From f7b263de20594fd720a8ff49b7528c48a64ddb9d Mon Sep 17 00:00:00 2001 From: Aaron Giles Date: Wed, 19 Aug 2020 19:33:13 -0700 Subject: Sound and other improvements to Sega G-80 games. (#7103) Sound and other improvements to Sega G-80 games: [Aaron Giles, couriersud] * Added netlist-based sound to Eliminator, Zektor, Space Fury, and Astro Blaster. * Split the Sega Universal Sound Board and Speech Boards into their own separate files. * Improved Universal Sound Board implementation for better accuracy in Star Trek and Tac/Scan. * Wrote netlist-based backend for Universal Sound Board; currently disabled due to limitations in the system. * Wrote netlist-based backend for Speech Board; currently disabled pending future sound system changes. * Implemented wait states and the vector DRAW flag to help improve timing. SP0250 Improvements: [Aaron Giles] * Matched clock divider to real chip measurements. * Fixed behavior when not fed enough data; addresses "gapping" in speech in Sega games. * Implemented accurate LFR noise generator according to real chip measurements. * Added pulse-width modulation DAC output mode for future consumption by netlist. Netlist additions: [Aaron Giles] * Added compile-time option to record nltool-compatible CSV files. * Improved CD4020 implementation. * Fixed CD4053 behavior. * Added 74139 device. * Added TL082 device. 8253 PIT changes: [Aaron Giles] * Added explicit synchronization to all writes. * Cleaned up some timing calculations to avoid double<->attotime conversions. --- scripts/target/mame/arcade.lua | 19 +- scripts/target/mame/nl.lua | 37 + src/devices/machine/netlist.cpp | 83 +- src/devices/machine/netlist.h | 24 + src/devices/machine/pit8253.cpp | 78 +- src/devices/machine/pit8253.h | 20 +- src/devices/sound/sp0250.cpp | 234 ++++-- src/devices/sound/sp0250.h | 58 +- src/lib/netlist/devices/nld_4020.cpp | 20 +- src/lib/netlist/devices/nld_4053.cpp | 70 +- src/lib/netlist/devices/nld_devinc.h | 12 + src/lib/netlist/macro/nlm_opamp_lib.cpp | 9 + src/lib/netlist/macro/nlm_opamp_lib.h | 3 + src/lib/netlist/macro/nlm_ttl74xx_lib.cpp | 57 ++ src/lib/netlist/macro/nlm_ttl74xx_lib.h | 3 + src/lib/netlist/nl_errstr.h | 4 +- src/lib/netlist/nl_setup.cpp | 9 +- src/mame/audio/nl_astrob.cpp | 1161 +++++++++++++++++++++++++++ src/mame/audio/nl_astrob.h | 10 + src/mame/audio/nl_cinemat_common.h | 2 +- src/mame/audio/nl_elim.cpp | 1225 +++++++++++++++++++++++++++++ src/mame/audio/nl_elim.h | 11 + src/mame/audio/nl_segaspeech.cpp | 119 +++ src/mame/audio/nl_segaspeech.h | 10 + src/mame/audio/nl_segausb.cpp | 544 +++++++++++++ src/mame/audio/nl_segausb.h | 10 + src/mame/audio/nl_spacfury.cpp | 1105 ++++++++++++++++++++++++++ src/mame/audio/nl_spacfury.h | 10 + src/mame/audio/segag80.cpp | 185 +++++ src/mame/audio/segag80.h | 74 ++ src/mame/audio/segag80r.cpp | 254 ------ src/mame/audio/segag80v.cpp | 327 -------- src/mame/audio/segasnd.cpp | 909 --------------------- src/mame/audio/segasnd.h | 214 ----- src/mame/audio/segaspeech.cpp | 217 +++++ src/mame/audio/segaspeech.h | 61 ++ src/mame/audio/segausb.cpp | 859 ++++++++++++++++++++ src/mame/audio/segausb.h | 192 +++++ src/mame/drivers/segag80r.cpp | 130 ++- src/mame/drivers/segag80v.cpp | 636 ++++++++------- src/mame/drivers/zaxxon.cpp | 2 +- src/mame/includes/segag80r.h | 17 +- src/mame/includes/segag80v.h | 137 +++- src/mame/nl.lst | 43 + src/mame/video/segag80v.cpp | 316 ++++---- 45 files changed, 7086 insertions(+), 2434 deletions(-) create mode 100644 src/mame/audio/nl_astrob.cpp create mode 100644 src/mame/audio/nl_astrob.h create mode 100644 src/mame/audio/nl_elim.cpp create mode 100644 src/mame/audio/nl_elim.h create mode 100644 src/mame/audio/nl_segaspeech.cpp create mode 100644 src/mame/audio/nl_segaspeech.h create mode 100644 src/mame/audio/nl_segausb.cpp create mode 100644 src/mame/audio/nl_segausb.h create mode 100644 src/mame/audio/nl_spacfury.cpp create mode 100644 src/mame/audio/nl_spacfury.h create mode 100644 src/mame/audio/segag80.cpp create mode 100644 src/mame/audio/segag80.h delete mode 100644 src/mame/audio/segag80v.cpp delete mode 100644 src/mame/audio/segasnd.cpp delete mode 100644 src/mame/audio/segasnd.h create mode 100644 src/mame/audio/segaspeech.cpp create mode 100644 src/mame/audio/segaspeech.h create mode 100644 src/mame/audio/segausb.cpp create mode 100644 src/mame/audio/segausb.h diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index c4f075f74d3..9355fb09dd6 100644 --- a/scripts/target/mame/arcade.lua +++ b/scripts/target/mame/arcade.lua @@ -3528,7 +3528,14 @@ files { MAME_DIR .. "src/mame/video/segag80r.cpp", MAME_DIR .. "src/mame/drivers/segag80v.cpp", MAME_DIR .. "src/mame/includes/segag80v.h", - MAME_DIR .. "src/mame/audio/segag80v.cpp", + MAME_DIR .. "src/mame/audio/segag80.cpp", + MAME_DIR .. "src/mame/audio/segag80.h", + MAME_DIR .. "src/mame/audio/nl_astrob.cpp", + MAME_DIR .. "src/mame/audio/nl_astrob.h", + MAME_DIR .. "src/mame/audio/nl_elim.cpp", + MAME_DIR .. "src/mame/audio/nl_elim.h", + MAME_DIR .. "src/mame/audio/nl_spacfury.cpp", + MAME_DIR .. "src/mame/audio/nl_spacfury.h", MAME_DIR .. "src/mame/video/segag80v.cpp", MAME_DIR .. "src/mame/drivers/segahang.cpp", MAME_DIR .. "src/mame/includes/segahang.h", @@ -3644,8 +3651,14 @@ files { MAME_DIR .. "src/mame/machine/segaic16.h", MAME_DIR .. "src/mame/machine/segabill.cpp", MAME_DIR .. "src/mame/machine/segabill.h", - MAME_DIR .. "src/mame/audio/segasnd.cpp", - MAME_DIR .. "src/mame/audio/segasnd.h", + MAME_DIR .. "src/mame/audio/segaspeech.cpp", + MAME_DIR .. "src/mame/audio/segaspeech.h", + MAME_DIR .. "src/mame/audio/segausb.cpp", + MAME_DIR .. "src/mame/audio/segausb.h", + MAME_DIR .. "src/mame/audio/nl_segausb.cpp", + MAME_DIR .. "src/mame/audio/nl_segausb.h", + MAME_DIR .. "src/mame/audio/nl_segaspeech.cpp", + MAME_DIR .. "src/mame/audio/nl_segaspeech.h", MAME_DIR .. "src/mame/video/segaic16.cpp", MAME_DIR .. "src/mame/video/segaic16.h", MAME_DIR .. "src/mame/video/segaic16_road.cpp", diff --git a/scripts/target/mame/nl.lua b/scripts/target/mame/nl.lua index 842f23541ca..dae23216d51 100644 --- a/scripts/target/mame/nl.lua +++ b/scripts/target/mame/nl.lua @@ -58,6 +58,9 @@ SOUNDS["VOLT_REG"] = true SOUNDS["SPEAKER"] = true SOUNDS["DIGITALKER"] = true SOUNDS["SN76477"] = true +SOUNDS["SN76496"] = true +SOUNDS["SP0250"] = true +SOUNDS["TMS36XX"] = true -------------------------------------------------- -- specify available video cores @@ -89,6 +92,7 @@ MACHINES["6522VIA"] = true MACHINES["6821PIA"] = true MACHINES["I8255"] = true +MACHINES["I8243"] = true MACHINES["WATCHDOG"] = true MACHINES["EEPROMDEV"] = true MACHINES["UPD4701"] = true @@ -285,11 +289,44 @@ files{ MAME_DIR .. "src/mame/machine/nl_palestra.cpp", MAME_DIR .. "src/mame/machine/nl_palestra.h", + MAME_DIR .. "src/mame/machine/segacrpt_device.cpp", + MAME_DIR .. "src/mame/machine/segacrpt_device.h", + MAME_DIR .. "src/mame/drivers/segag80r.cpp", + MAME_DIR .. "src/mame/includes/segag80r.h", + MAME_DIR .. "src/mame/machine/segag80.cpp", + MAME_DIR .. "src/mame/machine/segag80.h", + MAME_DIR .. "src/mame/audio/segag80r.cpp", + MAME_DIR .. "src/mame/audio/segag80r.h", + MAME_DIR .. "src/mame/video/segag80r.cpp", + MAME_DIR .. "src/mame/drivers/segag80v.cpp", + MAME_DIR .. "src/mame/includes/segag80v.h", + MAME_DIR .. "src/mame/audio/segag80.cpp", + MAME_DIR .. "src/mame/audio/segag80.h", + MAME_DIR .. "src/mame/audio/segaspeech.cpp", + MAME_DIR .. "src/mame/audio/segaspeech.h", + MAME_DIR .. "src/mame/audio/segausb.cpp", + MAME_DIR .. "src/mame/audio/segausb.h", + MAME_DIR .. "src/mame/audio/nl_astrob.cpp", + MAME_DIR .. "src/mame/audio/nl_astrob.h", + MAME_DIR .. "src/mame/audio/nl_elim.cpp", + MAME_DIR .. "src/mame/audio/nl_elim.h", + MAME_DIR .. "src/mame/audio/nl_spacfury.cpp", + MAME_DIR .. "src/mame/audio/nl_spacfury.h", + MAME_DIR .. "src/mame/video/segag80v.cpp", + MAME_DIR .. "src/mame/drivers/zaxxon.cpp", + MAME_DIR .. "src/mame/includes/zaxxon.h", + MAME_DIR .. "src/mame/audio/zaxxon.cpp", + MAME_DIR .. "src/mame/video/zaxxon.cpp", + MAME_DIR .. "src/mame/drivers/segas16b.cpp", MAME_DIR .. "src/mame/includes/segas16b.h", MAME_DIR .. "src/mame/video/segas16b.cpp", MAME_DIR .. "src/mame/audio/nl_segas16b.cpp", MAME_DIR .. "src/mame/audio/nl_segas16b.h", + MAME_DIR .. "src/mame/audio/nl_segausb.cpp", + MAME_DIR .. "src/mame/audio/nl_segausb.h", + MAME_DIR .. "src/mame/audio/nl_segaspeech.cpp", + MAME_DIR .. "src/mame/audio/nl_segaspeech.h", MAME_DIR .. "src/mame/machine/315_5195.cpp", MAME_DIR .. "src/mame/machine/315_5195.h", MAME_DIR .. "src/mame/machine/fd1089.cpp", diff --git a/src/devices/machine/netlist.cpp b/src/devices/machine/netlist.cpp index 8dd06070960..3af1570edb5 100644 --- a/src/devices/machine/netlist.cpp +++ b/src/devices/machine/netlist.cpp @@ -366,6 +366,9 @@ void netlist_mame_analog_input_device::write(const double val) void netlist_mame_analog_input_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { update_to_current_time(); +#if NETLIST_CREATE_CSV + nl_owner().log_add(m_param_name, *((double *) ptr), true); +#endif m_param->set(*((double *) ptr)); } @@ -392,12 +395,18 @@ void netlist_mame_logic_input_device::write(const uint32_t val) void netlist_mame_int_input_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { update_to_current_time(); +#if NETLIST_CREATE_CSV + nl_owner().log_add(m_param_name, param, false); +#endif m_param->set(param); } void netlist_mame_logic_input_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { update_to_current_time(); +#if NETLIST_CREATE_CSV + nl_owner().log_add(m_param_name, param, false); +#endif m_param->set(param); } @@ -435,6 +444,57 @@ void netlist_mame_sub_interface::set_mult_offset(const double mult, const double m_offset = offset; } +#if NETLIST_CREATE_CSV +void netlist_mame_device::log_add(char const* param, double value, bool isfloat) +{ + // skip if no file + if (m_csv_file == nullptr) + return; + + // make a new entry + buffer_entry entry = { machine().scheduler().time(), isfloat, value, param }; + + // flush out half of the old entries if we hit the buffer limit + if (m_buffer.size() >= MAX_BUFFER_ENTRIES) + log_flush(MAX_BUFFER_ENTRIES / 2); + + // fast common case: if we go at the end, just push_back + if (m_buffer.size() == 0 || entry.time >= m_buffer.back().time) + { + m_buffer.push_back(entry); + return; + } + + // find our place in the queue + for (auto cur = m_buffer.rbegin(); cur != m_buffer.rend(); cur++) + if (entry.time >= cur->time) + { + m_buffer.insert(cur.base(), entry); + return; + } + + // if we're too early, drop this entry rather than risk putting an out-of-order + // entry after the last one we flushed +} + +void netlist_mame_device::log_flush(int count) +{ + if (m_csv_file == nullptr) + return; + if (count > m_buffer.size()) + count = m_buffer.size(); + while (count--) + { + auto &entry = m_buffer.front(); + if (entry.isfloat) + fprintf(m_csv_file, "%s,%s,%f\n", entry.time.as_string(), entry.string, entry.value); + else + fprintf(m_csv_file, "%s,%s,%d\n", entry.time.as_string(), entry.string, int(entry.value)); + m_buffer.pop_front(); + } +} +#endif + netlist_mame_analog_input_device::netlist_mame_analog_input_device(const machine_config &mconfig, const char *tag, device_t *owner, const char *param_name) : device_t(mconfig, NETLIST_ANALOG_INPUT, tag, owner, 0) , netlist_mame_sub_interface(*owner) @@ -833,7 +893,7 @@ void netlist_mame_stream_output_device::device_reset() void netlist_mame_stream_output_device::sound_update_fill(std::size_t samples, stream_sample_t *target) { if (samples < m_buffer.size()) - throw emu_fatalerror("sound %s: samples %d less bufsize %d\n", name(), samples, m_buffer.size()); + osd_printf_warning("sound %s: samples %d less bufsize %d\n", name(), samples, m_buffer.size()); std::copy(m_buffer.begin(), m_buffer.end(), target); std::size_t pos = m_buffer.size(); @@ -1036,6 +1096,18 @@ void netlist_mame_device::device_start_common() m_device_reset_called = false; + +#if NETLIST_CREATE_CSV + std::string name = machine().system().name; + name += tag(); + for (int index = 0; index < name.size(); index++) + if (name[index] == ':') + name[index] = '_'; + name += ".csv"; + m_csv_file = fopen(name.c_str(), "wb"); +#endif + + LOGDEVCALLS("device_start exit\n"); } @@ -1070,7 +1142,14 @@ void netlist_mame_device::device_stop() { LOGDEVCALLS("device_stop\n"); if (m_netlist) - m_netlist->exec().stop(); + netlist().exec().stop(); +#if NETLIST_CREATE_CSV + if (m_csv_file != nullptr) + { + log_flush(); + fclose(m_csv_file); + } +#endif } void netlist_mame_device::device_post_load() diff --git a/src/devices/machine/netlist.h b/src/devices/machine/netlist.h index b7bd668cc32..8b35b13bacf 100644 --- a/src/devices/machine/netlist.h +++ b/src/devices/machine/netlist.h @@ -12,9 +12,14 @@ #define MAME_MACHINE_NETLIST_H #include +#include #include "../../lib/netlist/nltypes.h" +#ifndef NETLIST_CREATE_CSV +#define NETLIST_CREATE_CSV (0) +#endif + class netlist_mame_stream_output_device; class nld_sound_in; @@ -106,6 +111,25 @@ private: func_type m_setup_func; bool m_device_reset_called; + +#if NETLIST_CREATE_CSV + static constexpr int MAX_BUFFER_ENTRIES = 1000; + +public: + void log_add(char const* param, double value, bool isfloat); + void log_flush(int count = MAX_BUFFER_ENTRIES); + +private: + struct buffer_entry + { + attotime time; + bool isfloat; + double value; + char const *string; + }; + std::deque m_buffer; + FILE* m_csv_file = nullptr; +#endif }; class netlist_mame_cpu_device : public netlist_mame_device, diff --git a/src/devices/machine/pit8253.cpp b/src/devices/machine/pit8253.cpp index eecffb58b9e..2e73aecbcb9 100644 --- a/src/devices/machine/pit8253.cpp +++ b/src/devices/machine/pit8253.cpp @@ -101,6 +101,7 @@ void pit8253_device::device_resolve_objects() m_out_handler[timer].resolve_safe(); m_counter[timer]->m_index = timer; m_counter[timer]->m_clockin = m_clk[timer]; + m_counter[timer]->m_clock_period = (m_clk[timer] != 0) ? attotime::from_hz(m_clk[timer]) : attotime::never; } } @@ -112,11 +113,12 @@ void pit8253_device::device_resolve_objects() void pit_counter_device::device_start() { /* initialize timer */ - m_updatetimer = timer_alloc(); - m_updatetimer->adjust(attotime::never); + m_updatetimer = timer_alloc(TID_UPDATE); + adjust_timer(attotime::never); /* set up state save values */ save_item(NAME(m_clockin)); + save_item(NAME(m_clock_period)); save_item(NAME(m_control)); save_item(NAME(m_status)); save_item(NAME(m_lowcount)); @@ -197,6 +199,14 @@ void pit_counter_device::device_reset() #define CTRL_MODE(control) (((control) >> 1) & (((control) & 0x04) ? 0x03 : 0x07)) #define CTRL_BCD(control) (((control) >> 0) & 0x01) +inline void pit_counter_device::adjust_timer(attotime target) +{ +// if (target != m_next_update) + { + m_next_update = target; + m_updatetimer->adjust(target - machine().time()); + } +} inline uint32_t pit_counter_device::adjusted_count() const { @@ -684,15 +694,9 @@ void pit_counter_device::simulate(int64_t elapsed_cycles) } if (cycles_to_output == CYCLES_NEVER || m_clockin == 0) - { - m_updatetimer->adjust(attotime::never); - } + adjust_timer(attotime::never); else - { - attotime next_fire_time = m_last_updated + cycles_to_output * attotime::from_hz(m_clockin); - - m_updatetimer->adjust(next_fire_time - machine().time()); - } + adjust_timer(m_last_updated + cycles_to_output * m_clock_period); LOG2("simulate(): simulating %d cycles in mode %d, bcd = %d, phase = %d, gate = %d, output %d, value = 0x%04x, cycles_to_output = %04x\n", (int)elapsed_cycles, mode, bcd, m_phase, m_gate, m_output, m_value, cycles_to_output); @@ -704,13 +708,19 @@ void pit_counter_device::update() /* With the 82C54's maximum clockin of 10MHz, 64 bits is nearly 60,000 years of time. Should be enough for now. */ attotime now = machine().time(); - attotime elapsed_time = now - m_last_updated; - int64_t elapsed_cycles = elapsed_time.as_double() * m_clockin; + int64_t elapsed_cycles = 0; + if (m_clockin != 0) + { + if (now > m_last_updated) + { + attotime elapsed_time = now - m_last_updated; + elapsed_cycles = elapsed_time.as_double() * m_clockin; - LOG2("update(): %d elapsed_cycles\n", elapsed_cycles); + LOG2("update(): %d elapsed_cycles\n", elapsed_cycles); - if (m_clockin) - m_last_updated += elapsed_cycles * attotime::from_hz(m_clockin); + m_last_updated += elapsed_cycles * m_clock_period; + } + } else m_last_updated = now; @@ -718,8 +728,8 @@ void pit_counter_device::update() sections punctuated by callbacks. */ if (elapsed_cycles > 0) simulate(elapsed_cycles); - else if (m_clockin) - m_updatetimer->adjust(attotime::from_hz(m_clockin)); + else if (m_clockin != 0) + adjust_timer(m_last_updated + m_clock_period); } @@ -928,12 +938,27 @@ void pit8254_device::readback_command(uint8_t data) void pit_counter_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { update(); + switch (id) + { + case TID_UPDATE: + break; + + case TID_CONTROL: + control_w_deferred(param); + break; + + case TID_COUNT: + count_w_deferred(param); + break; + + case TID_GATE: + gate_w_deferred(param); + break; + } } -void pit_counter_device::control_w(uint8_t data) +void pit_counter_device::control_w_deferred(uint8_t data) { - update(); - if (CTRL_ACCESS(data) == 0) { LOG1("write(): readback\n"); @@ -955,10 +980,8 @@ void pit_counter_device::control_w(uint8_t data) } } -void pit_counter_device::count_w(uint8_t data) +void pit_counter_device::count_w_deferred(uint8_t data) { - update(); - bool middle_of_a_cycle = (machine().time() > m_last_updated && m_clockin != 0); switch (CTRL_ACCESS(m_control)) @@ -972,7 +995,7 @@ void pit_counter_device::count_w(uint8_t data) /* check if we should compensate for not being on a cycle boundary */ if (middle_of_a_cycle) - m_last_updated += attotime::from_hz(m_clockin); + m_last_updated += m_clock_period; load_count(data); simulate(0); @@ -986,7 +1009,7 @@ void pit_counter_device::count_w(uint8_t data) /* check if we should compensate for not being on a cycle boundary */ if (middle_of_a_cycle) - m_last_updated += attotime::from_hz(m_clockin); + m_last_updated += m_clock_period; load_count(data << 8); simulate(0); @@ -1001,7 +1024,7 @@ void pit_counter_device::count_w(uint8_t data) { /* check if we should compensate for not being on a cycle boundary */ if (middle_of_a_cycle) - m_last_updated += attotime::from_hz(m_clockin); + m_last_updated += m_clock_period; load_count(m_lowcount | (data << 8)); simulate(0); @@ -1042,7 +1065,7 @@ void pit8253_device::write(offs_t offset, uint8_t data) m_counter[offset]->count_w(data); } -void pit_counter_device::gate_w(int state) +void pit_counter_device::gate_w_deferred(int state) { LOG2("gate_w(): state=%d\n", state); @@ -1070,6 +1093,7 @@ void pit_counter_device::set_clockin(double new_clockin) if (started()) update(); m_clockin = new_clockin; + m_clock_period = (new_clockin != 0) ? attotime::from_hz(new_clockin) : attotime::never; if (started()) update(); } diff --git a/src/devices/machine/pit8253.h b/src/devices/machine/pit8253.h index 13b222478f5..a862dee8aad 100644 --- a/src/devices/machine/pit8253.h +++ b/src/devices/machine/pit8253.h @@ -42,6 +42,14 @@ class pit_counter_device : public device_t friend class pit8253_device; friend class pit8254_device; + enum + { + TID_UPDATE = 1, + TID_CONTROL, + TID_COUNT, + TID_GATE + }; + public: // construction/destruction pit_counter_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); @@ -54,6 +62,7 @@ protected: private: inline uint32_t adjusted_count() const; + inline void adjust_timer(attotime target); void decrease_counter_value(int64_t cycles); void load_counter_value(); void set_output(int output); @@ -63,18 +72,23 @@ private: uint8_t read(); void load_count(uint16_t newcount); void readback(int command); - void control_w(uint8_t data); - void count_w(uint8_t data); - void gate_w(int state); + void control_w(uint8_t data) { synchronize(TID_CONTROL, data); } + void control_w_deferred(uint8_t data); + void count_w(uint8_t data) { synchronize(TID_COUNT, data); } + void count_w_deferred(uint8_t data); + void gate_w(int state) { synchronize(TID_GATE, state); } + void gate_w_deferred(int state); void set_clock_signal(int state); void set_clockin(double new_clockin); // internal state int m_index; // index number of the timer double m_clockin; // input clock frequency in Hz + attotime m_clock_period; // precomputed input clock period int m_clock_signal; // clock signal when clockin is 0 attotime m_last_updated; // time when last updated + attotime m_next_update; // time of next update emu_timer *m_updatetimer; // MAME timer to process updates diff --git a/src/devices/sound/sp0250.cpp b/src/devices/sound/sp0250.cpp index 898bd778dad..03d5004f695 100644 --- a/src/devices/sound/sp0250.cpp +++ b/src/devices/sound/sp0250.cpp @@ -21,34 +21,32 @@ #include "emu.h" #include "sp0250.h" -/* -standard external clock is 3.12MHz -the chip provides a 445.7kHz output clock, which is = 3.12MHz / 7 -therefore I expect the clock divider to be a multiple of 7 -Also there are 6 cascading filter stages so I expect the divider to be a multiple of 6. - -The SP0250 manual states that the original speech is sampled at 10kHz, so the divider -should be 312, but 312 = 39*8 so it doesn't look right because a divider by 39 is unlikely. +// +// Input clock is divided by 2 to make ROMCLOCK. +// Output is via pulse-width modulation (PWM) over the course of 39 ROMCLOCKs. +// 4 PWM periods per frame. +// +static constexpr int PWM_CLOCKS = 39; -7*6*8 = 336 gives a 9.286kHz sample rate and matches the samples from the Sega boards. -*/ -#define CLOCK_DIVIDER (7*6*8) DEFINE_DEVICE_TYPE(SP0250, sp0250_device, "sp0250", "GI SP0250 LPC") sp0250_device::sp0250_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, SP0250, tag, owner, clock), device_sound_interface(mconfig, *this), + m_pwm_mode(false), + m_pwm_index(PWM_CLOCKS), + m_pwm_count(0), + m_pwm_counts(0), + m_voiced(0), m_amp(0), + m_lfsr(0x7fff), m_pitch(0), - m_repeat(0), m_pcount(0), + m_repeat(0), m_rcount(0), - m_playing(0), - m_RNG(0), - m_stream(nullptr), - m_voiced(0), m_fifo_pos(0), + m_stream(nullptr), m_drq(*this) { for (auto & elem : m_fifo) @@ -71,29 +69,55 @@ sp0250_device::sp0250_device(const machine_config &mconfig, const char *tag, dev void sp0250_device::device_start() { - m_RNG = 1; + // output PWM data at the ROMCLOCK frequency + int sample_rate = clock() / 2; + int frame_rate = sample_rate / (4 * PWM_CLOCKS); + if (!m_pwm_mode) + m_stream = machine().sound().stream_alloc(*this, 0, 1, frame_rate); + else + m_stream = machine().sound().stream_alloc(*this, 0, 1, sample_rate); + + // if a DRQ callback is offered, run a timer at the frame rate + // to ensure the DRQ gets picked up in a timely manner m_drq.resolve_safe(); if (!m_drq.isnull()) { m_drq(ASSERT_LINE); - m_tick_timer= machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(sp0250_device::timer_tick), this)); - m_tick_timer->adjust(attotime::from_hz(clock()) * CLOCK_DIVIDER, 0, attotime::from_hz(clock()) * CLOCK_DIVIDER); + attotime period = attotime::from_hz(frame_rate); + timer_alloc()->adjust(period, 0, period); } - m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / CLOCK_DIVIDER); + // PWM state + save_item(NAME(m_pwm_index)); + save_item(NAME(m_pwm_count)); + save_item(NAME(m_pwm_counts)); + // LPC state + save_item(NAME(m_voiced)); save_item(NAME(m_amp)); + save_item(NAME(m_lfsr)); save_item(NAME(m_pitch)); - save_item(NAME(m_repeat)); save_item(NAME(m_pcount)); + save_item(NAME(m_repeat)); save_item(NAME(m_rcount)); - save_item(NAME(m_playing)); - save_item(NAME(m_RNG)); - save_item(NAME(m_voiced)); + for (int index = 0; index < 6; index++) + { + save_item(NAME(m_filter[index].F), index); + save_item(NAME(m_filter[index].B), index); + save_item(NAME(m_filter[index].z1), index); + save_item(NAME(m_filter[index].z2), index); + } + + // FIFO state save_item(NAME(m_fifo)); save_item(NAME(m_fifo_pos)); } +void sp0250_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + m_stream->update(); +} + static uint16_t sp0250_ga(uint8_t v) { return (v & 0x1f) << (v>>5); @@ -140,19 +164,11 @@ void sp0250_device::load_values() m_filter[5].F = sp0250_gc(m_fifo[14]); m_fifo_pos = 0; m_drq(ASSERT_LINE); - m_pcount = 0; m_rcount = 0; for (int f = 0; f < 6; f++) - m_filter[f].z1 = m_filter[f].z2 = 0; - - m_playing = 1; -} - -TIMER_CALLBACK_MEMBER( sp0250_device::timer_tick ) -{ - m_stream->update(); + m_filter[f].reset(); } void sp0250_device::write(uint8_t data) @@ -175,6 +191,79 @@ uint8_t sp0250_device::drq_r() return (m_fifo_pos == 15) ? CLEAR_LINE : ASSERT_LINE; } +int8_t sp0250_device::next() +{ + if (m_rcount >= m_repeat) + { + if (m_fifo_pos == 15) + load_values(); + else + { + // According to http://www.cpcwiki.eu/index.php/SP0256_Measured_Timings + // the SP0250 executes "NOPs" with a repeat count of 1 and unchanged + // pitch while waiting for input + m_repeat = 1; + m_pcount = 0; + m_rcount = 0; + } + } + + int16_t z0; + if (m_voiced) + z0 = (m_pcount == 0) ? m_amp : 0; + else + { + z0 = (m_lfsr & 1) ? m_amp : -m_amp; + + // 15-bit LFSR algorithm verified by dump from actual hardware + m_lfsr ^= (m_lfsr ^ (m_lfsr >> 1)) << 15; + m_lfsr >>= 1; + } + + for (int f = 0; f < 6; f++) + z0 = m_filter[f].apply(z0); + + // maximum amp value is effectively 13 bits + // reduce to 7 bits; due to filter effects it + // may occasionally clip + int dac = z0 >> 6; + if (dac < -64) + dac = -64; + if (dac > 63) + dac = 63; + + // PWM is divided into 4x 5-bit sections; the lower + // bits of the original 7-bit value are added to only + // some of the pulses in the following pattern: + // + // DAC -64 -> 1,1,1,1 + // DAC -63 -> 2,1,1,1 + // DAC -62 -> 2,1,2,1 + // DAC -61 -> 2,2,2,1 + // DAC -60 -> 2,2,2,2 + // ... + // DAC -1 -> 17,17,17,16 + // DAC 0 -> 17,17,17,17 + // DAC 1 -> 18,17,17,17 + // ... + // DAC 60 -> 32,32,32,32 + // DAC 61 -> 33,32,32,32 + // DAC 62 -> 33,32,33,32 + // DAC 63 -> 33,33,33,32 + m_pwm_counts = (((dac + 68 + 3) >> 2) << 0) + + (((dac + 68 + 1) >> 2) << 8) + + (((dac + 68 + 2) >> 2) << 16) + + (((dac + 68 + 0) >> 2) << 24); + + m_pcount++; + if (m_pcount >= m_pitch) + { + m_pcount = 0; + m_rcount++; + } + return dac; +} + //------------------------------------------------- // sound_stream_update - handle a stream update //------------------------------------------------- @@ -182,62 +271,49 @@ uint8_t sp0250_device::drq_r() void sp0250_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { stream_sample_t *output = outputs[0]; - for (int i = 0; i < samples; i++) + if (!m_pwm_mode) { - if (m_playing) + while (samples-- != 0) + *output++ = next() << 8; + } + else + { + while (samples != 0) { - int16_t z0; - - if (m_voiced) + // see where we're at in the current PWM cycle + if (m_pwm_index >= PWM_CLOCKS) { - if(!m_pcount) - z0 = m_amp; - else - z0 = 0; - } - else - { - // Borrowing the ay noise generation LFSR - if(m_RNG & 1) - { - z0 = m_amp; - m_RNG ^= 0x24000; - } - else - z0 = -m_amp; - - m_RNG >>= 1; + m_pwm_index = 0; + if (m_pwm_counts == 0) + next(); + m_pwm_count = m_pwm_counts & 0xff; + m_pwm_counts >>= 8; } - for (int f = 0; f < 6; f++) + // determine the value to fill and the number of samples remaining + // until it changes + stream_sample_t value; + int remaining; + if (m_pwm_index < m_pwm_count) { - z0 += ((m_filter[f].z1 * m_filter[f].F) >> 8) - + ((m_filter[f].z2 * m_filter[f].B) >> 9); - m_filter[f].z2 = m_filter[f].z1; - m_filter[f].z1 = z0; + value = 32767; + remaining = m_pwm_count - m_pwm_index; } - - // Physical resolution is only 7 bits, but heh - - // max amplitude is 0x0f80 so we have margin to push up the output - output[i] = z0 << 3; - - m_pcount++; - if (m_pcount >= m_pitch) + else { - m_pcount = 0; - m_rcount++; - if (m_rcount >= m_repeat) - m_playing = 0; + value = 0; + remaining = PWM_CLOCKS - m_pwm_index; } - } - else - output[i] = 0; - if (!m_playing) - { - if(m_fifo_pos == 15) - load_values(); + // clamp to the number of samples requested and advance the counters + if (remaining > samples) + remaining = samples; + m_pwm_index += remaining; + samples -= remaining; + + // fill the output + while (remaining-- != 0) + *output++ = value; } } } diff --git a/src/devices/sound/sp0250.h b/src/devices/sound/sp0250.h index 9685fa4fea2..48f77a69d01 100644 --- a/src/devices/sound/sp0250.h +++ b/src/devices/sound/sp0250.h @@ -11,6 +11,7 @@ public: sp0250_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); auto drq() { return m_drq.bind(); } + void set_pwm_mode() { m_pwm_mode = true; } void write(uint8_t data); uint8_t drq_r(); @@ -18,33 +19,56 @@ public: protected: // device-level overrides virtual void device_start() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; // sound stream update overrides virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; private: - // internal state - int16_t m_amp; - uint8_t m_pitch; - uint8_t m_repeat; - int m_pcount, m_rcount; - int m_playing; - uint32_t m_RNG; - sound_stream * m_stream; - int m_voiced; - uint8_t m_fifo[15]; - int m_fifo_pos; - devcb_write_line m_drq; + // internal helpers + int8_t next(); + void load_values(); - struct + // state for each filter + class filter { + public: + filter() : z1(0), z2(0) { } + void reset() { z1 = z2 = 0; } + int16_t apply(int16_t in) + { + int16_t z0 = in + ((z1 * F) >> 8) + ((z2 * B) >> 9); + z2 = z1; + z1 = z0; + return z0; + } int16_t F, B; int16_t z1, z2; - } m_filter[6]; + }; - void load_values(); - TIMER_CALLBACK_MEMBER( timer_tick ); - emu_timer * m_tick_timer; + // PWM state + bool m_pwm_mode; + uint8_t m_pwm_index; + uint8_t m_pwm_count; + uint32_t m_pwm_counts; + + // LPC state + bool m_voiced; + int16_t m_amp; + uint16_t m_lfsr; + uint8_t m_pitch; + uint8_t m_pcount; + uint8_t m_repeat; + uint8_t m_rcount; + filter m_filter[6]; + + // FIFO state + uint8_t m_fifo[15]; + uint8_t m_fifo_pos; + + // external interfacing + sound_stream *m_stream; + devcb_write_line m_drq; }; DECLARE_DEVICE_TYPE(SP0250, sp0250_device) diff --git a/src/lib/netlist/devices/nld_4020.cpp b/src/lib/netlist/devices/nld_4020.cpp index 4143f66da8d..9e2dd386654 100644 --- a/src/lib/netlist/devices/nld_4020.cpp +++ b/src/lib/netlist/devices/nld_4020.cpp @@ -46,17 +46,15 @@ namespace netlist namespace devices { - template + template NETLIB_OBJECT(CD4020_sub) { - static constexpr unsigned MAX_BITS = 14; - static constexpr unsigned MAX_BITMASK = (1 << MAX_BITS) - 1; + static_assert((_LiveBitmask >> _TotalBits) == 0, "Live bitmask too large"); NETLIB_CONSTRUCTOR_MODEL(CD4020_sub, "CD4XXX") , m_IP(*this, "IP", NETLIB_DELEGATE(ip)) , m_RESET(*this, "RESET", NETLIB_DELEGATE(reseti)) - , m_Q(*this, {"Q1", "_Q2", "_Q3", "Q4", "Q5", "Q6", "Q7", "Q8", "Q9", - "Q10", "Q11", "Q12", "Q13", "Q14"}) + , m_Q(*this, 1, "Q{}") , m_cnt(*this, "m_cnt", 0) , m_supply(*this) { @@ -81,7 +79,7 @@ namespace netlist m_cnt = 0; m_IP.inactivate(); /* static */ const netlist_time reset_time = netlist_time::from_nsec(140); - for (unsigned i = 0; i < MAX_BITS; i++) + for (int i = 0; i < _TotalBits; i++) if (((_LiveBitmask >> i) & 1) != 0) m_Q[i].push(0, reset_time); } @@ -102,13 +100,13 @@ namespace netlist NLTIME_FROM_NS(1380), NLTIME_FROM_NS(1480), }; - for (unsigned i = 0; i < MAX_BITS; i++) + for (int i = 0; i < _TotalBits; i++) if (((_LiveBitmask >> i) & 1) != 0) - m_Q[i].push(cnt & 1, out_delayQn[i]); + m_Q[i].push((cnt >> i) & 1, out_delayQn[i]); } logic_input_t m_IP; logic_input_t m_RESET; - object_array_t m_Q; + object_array_t m_Q; state_var m_cnt; nld_power_pins m_supply; @@ -140,7 +138,7 @@ namespace netlist //NETLIB_RESETI() {} private: - NETLIB_SUB(CD4020_sub)<0x3ff9> m_sub; + NETLIB_SUB(CD4020_sub)<14, 0x3ff9> m_sub; }; NETLIB_OBJECT(CD4024) @@ -164,7 +162,7 @@ namespace netlist //NETLIB_RESETI() {} private: - NETLIB_SUB(CD4020_sub)<0x7f> m_sub; + NETLIB_SUB(CD4020_sub)<7, 0x7f> m_sub; }; diff --git a/src/lib/netlist/devices/nld_4053.cpp b/src/lib/netlist/devices/nld_4053.cpp index 62db781fbfa..5056b453622 100644 --- a/src/lib/netlist/devices/nld_4053.cpp +++ b/src/lib/netlist/devices/nld_4053.cpp @@ -48,19 +48,14 @@ namespace netlist , m_supply(*this) { connect(m_RX.N(), m_RY.N()); - register_subalias("X", m_RX.P()); register_subalias("Y", m_RY.P()); - register_subalias("XY", m_RX.N()); } NETLIB_RESETI() { - // Start in off condition - // FIXME: is ROFF correct? - m_RX.set_R(plib::reciprocal(exec().gmin())); - m_RY.set_R(plib::reciprocal(exec().gmin())); + update_state(true, false); } private: @@ -80,40 +75,16 @@ namespace netlist } if (newx != m_lastx || newy != m_lasty) - { - const nl_fptype sup = (m_supply.VCC().Q_Analog() - m_supply.GND().Q_Analog()); - const nl_fptype Ron = m_base_r() * nlconst::magic(5.0) / sup; - const nl_fptype Roff = plib::reciprocal(exec().gmin()); - const nl_fptype RX = (newx != 0) ? Ron : Roff; - const nl_fptype RY = (newy != 0) ? Ron : Roff; - if (m_RX.solver() == m_RY.solver()) - { - m_RX.change_state([this, &RX, &RY]() - { - m_RX.set_R(RX); - m_RY.set_R(RY); - }); - } - else - { - m_RX.change_state([this, &RX]() - { - m_RX.set_R(RX); - }); - m_RY.change_state([this, &RY]() - { - m_RY.set_R(RY); - }); - } - } + update_state(newx, newy); } bool on(analog_input_t &input, bool &state) { + // digital inputs are based on VDD nl_fptype sup = (m_supply.VCC().Q_Analog() - m_supply.GND().Q_Analog()); nl_fptype in = input() - m_supply.GND().Q_Analog(); - nl_fptype low = nlconst::magic(0.45) * sup; - nl_fptype high = nlconst::magic(0.55) * sup; + nl_fptype low = nlconst::magic(0.3) * sup; + nl_fptype high = nlconst::magic(0.7) * sup; if (in < low) { state = false; @@ -125,6 +96,37 @@ namespace netlist return state; } + void update_state(bool newx, bool newy) + { + // analog output is based on VEE + const nl_fptype sup = (m_VEE() - m_supply.GND().Q_Analog()); + const nl_fptype Ron = m_base_r() * nlconst::magic(5.0) / sup; + const nl_fptype Roff = plib::reciprocal(exec().gmin()); + const nl_fptype RX = newx ? Ron : Roff; + const nl_fptype RY = newy ? Ron : Roff; + if (m_RX.solver() == m_RY.solver()) + { + m_RX.change_state([this, &RX, &RY]() + { + m_RX.set_R(RX); + m_RY.set_R(RY); + }); + } + else + { + m_RX.change_state([this, &RX]() + { + m_RX.set_R(RX); + }); + m_RY.change_state([this, &RY]() + { + m_RY.set_R(RY); + }); + } + m_lastx = newx; + m_lasty = newy; + } + analog::NETLIB_SUB(R_base) m_RX; analog::NETLIB_SUB(R_base) m_RY; analog_input_t m_select; diff --git a/src/lib/netlist/devices/nld_devinc.h b/src/lib/netlist/devices/nld_devinc.h index 1a1e7d2865f..5780106c96b 100644 --- a/src/lib/netlist/devices/nld_devinc.h +++ b/src/lib/netlist/devices/nld_devinc.h @@ -1021,6 +1021,10 @@ #define TTL_74113A_DIP(...) \ NET_REGISTER_DEVEXT(TTL_74113A_DIP, __VA_ARGS__) +// usage : TTL_74139_GATE(name) +#define TTL_74139_GATE(...) \ + NET_REGISTER_DEVEXT(TTL_74139_GATE, __VA_ARGS__) + // usage : TTL_74155A_GATE(name) #define TTL_74155A_GATE(...) \ NET_REGISTER_DEVEXT(TTL_74155A_GATE, __VA_ARGS__) @@ -1202,6 +1206,10 @@ #define TTL_74126_DIP(...) \ NET_REGISTER_DEVEXT(TTL_74126_DIP, __VA_ARGS__) +// usage : TTL_74139_DIP(name) +#define TTL_74139_DIP(...) \ + NET_REGISTER_DEVEXT(TTL_74139_DIP, __VA_ARGS__) + // usage : TTL_74153_DIP(name) #define TTL_74153_DIP(...) \ NET_REGISTER_DEVEXT(TTL_74153_DIP, __VA_ARGS__) @@ -1387,6 +1395,10 @@ #define TL081_DIP(...) \ NET_REGISTER_DEVEXT(TL081_DIP, __VA_ARGS__) +// usage : TL082_DIP(name) +#define TL082_DIP(...) \ + NET_REGISTER_DEVEXT(TL082_DIP, __VA_ARGS__) + // usage : TL084_DIP(name) #define TL084_DIP(...) \ NET_REGISTER_DEVEXT(TL084_DIP, __VA_ARGS__) diff --git a/src/lib/netlist/macro/nlm_opamp_lib.cpp b/src/lib/netlist/macro/nlm_opamp_lib.cpp index a8fae82e8c8..94c5da23686 100644 --- a/src/lib/netlist/macro/nlm_opamp_lib.cpp +++ b/src/lib/netlist/macro/nlm_opamp_lib.cpp @@ -314,6 +314,14 @@ static NETLIST_START(TL081_DIP) NETLIST_END() +static NETLIST_START(TL082_DIP) + OPAMP(A, "TL084") + OPAMP(B, "TL084") + + INCLUDE(opamp_layout_2_8_4) + +NETLIST_END() + static NETLIST_START(TL084_DIP) OPAMP(A, "TL084") OPAMP(B, "TL084") @@ -594,6 +602,7 @@ NETLIST_START(opamp_lib) LOCAL_LIB_ENTRY(MB3614_DIP) LOCAL_LIB_ENTRY(MC3340_DIP) LOCAL_LIB_ENTRY(TL081_DIP) + LOCAL_LIB_ENTRY(TL082_DIP) LOCAL_LIB_ENTRY(TL084_DIP) LOCAL_LIB_ENTRY(LM324_DIP) LOCAL_LIB_ENTRY(LM358_DIP) diff --git a/src/lib/netlist/macro/nlm_opamp_lib.h b/src/lib/netlist/macro/nlm_opamp_lib.h index 7c9b31a29bd..362e5c7baa1 100644 --- a/src/lib/netlist/macro/nlm_opamp_lib.h +++ b/src/lib/netlist/macro/nlm_opamp_lib.h @@ -27,6 +27,9 @@ #define TL081_DIP(name) \ NET_REGISTER_DEV(TL081_DIP, name) +#define TL082_DIP(name) \ + NET_REGISTER_DEV(TL082_DIP, name) + #define TL084_DIP(name) \ NET_REGISTER_DEV(TL084_DIP, name) diff --git a/src/lib/netlist/macro/nlm_ttl74xx_lib.cpp b/src/lib/netlist/macro/nlm_ttl74xx_lib.cpp index 92f486aac5a..0d3754e94d7 100644 --- a/src/lib/netlist/macro/nlm_ttl74xx_lib.cpp +++ b/src/lib/netlist/macro/nlm_ttl74xx_lib.cpp @@ -1822,6 +1822,52 @@ static NETLIST_START(TTL_74126_DIP) ) NETLIST_END() +//- Identifier: TTL_74139_DIP +//- Title: 54LS139/DM54LS139/DM74LS139 Decoders/Demultiplexers +//- Description: These Schottky-clamped circuits are designed to be used in high-performance memory-decoding or data-routing applications, requiring very short propagation delay times. +//- In high-performance memory systems these decoders can be used to minimize the effects of system decoding. +//- When used with high-speed memories, the delay times of these decoders are usually less than the typical access time of the memory. +//- This means that the effective system delay introduced by the decoder is negligible. +//- The LS139 comprises two separate two-line-to-four-line decoders in a single package. +//- The active-low enable input can be used as a data line in demultiplexing applications. +//- All of these decoders/demultiplexers feature fully buffered inputs, presenting only one normalized load to its driving circuit. +// All inputs are clamped with high-performance Schottky diodes to suppress line-ringing and simplify system design. +//- Pinalias: G1,A1,B1,1Y0,1Y1,1Y2,1Y3,GND,2Y3,2Y2,2Y1,2Y0,B2,A2,G2,VCC +//- Package: DIP +//- NamingConvention: Naming conventions follow National Semiconductor datasheet +//- FunctionTable: +//- pdf.datasheetcatalog.com/datasheets/166/375388_DS.pdf +//- +//- +---+-------+-------------+ +//- | E | A0 A1 | O0 O1 O2 O3 | +//- +===+=======+=============+ +//- | 1 | X X | 1 1 1 1 | +//- | 0 | 0 0 | 0 1 1 1 | +//- | 0 | 1 0 | 1 0 1 1 | +//- | 0 | 0 1 | 1 1 0 1 | +//- | 0 | 1 1 | 1 1 1 0 | +//- +---+-------+-------------+ +//- +static NETLIST_START(TTL_74139_DIP) + NET_REGISTER_DEV(TTL_74139_GATE, A) + NET_REGISTER_DEV(TTL_74139_GATE, B) + + NET_C(A.VCC, B.VCC) + NET_C(A.GND, B.GND) + + DIPPINS( /* +--------------+ */ + A.E, /* /Ea |1 ++ 16| VCC */ A.VCC, + A.A, /* A0a |2 15| /Eb */ B.E, + A.B, /* A1a |3 14| A0b */ B.A, + A.0, /* /O0a |4 74139 13| A1b */ B.B, + A.1, /* /O1a |5 12| /O0b */ B.0, + A.2, /* /O2a |6 11| /O1b */ B.1, + A.3, /* /O3a |7 10| /O2b */ B.2, + A.GND,/* GND |8 9| /O3b */ B.3 + /* +--------------+ */ + ) +NETLIST_END() + //- Identifier: TTL_74153_DIP //- Title: 54153/DM54153/DM74153 Dual 4-Line to 1-LineData Selectors/Multiplexers //- Description: Each of these data selectors/multiplexers contains inverters and drivers to supply fully complementary, on-chip, binary decoding data selection to the AND-OR-invert gates. @@ -3205,6 +3251,16 @@ NETLIST_START(ttl74xx_lib) TRUTHTABLE_END() + TRUTHTABLE_START(TTL_74139_GATE, 3, 4, "") + TT_HEAD("E,A,B|0,1,2,3") + TT_LINE("1,X,X|1,1,1,1|14") + TT_LINE("0,0,0|0,1,1,1|14") + TT_LINE("0,0,1|1,0,1,1|14") + TT_LINE("0,1,0|1,1,0,1|14") + TT_LINE("0,1,1|1,1,1,0|14") + TT_FAMILY("74XX") + TRUTHTABLE_END() + TRUTHTABLE_START(TTL_74155A_GATE, 4, 4, "") TT_HEAD("B,A,G,C|0,1,2,3") TT_LINE("X,X,1,X|1,1,1,1|13,13,13,13") @@ -3367,6 +3423,7 @@ NETLIST_START(ttl74xx_lib) LOCAL_LIB_ENTRY(TTL_9602_DIP) LOCAL_LIB_ENTRY(TTL_74125_DIP) LOCAL_LIB_ENTRY(TTL_74126_DIP) + LOCAL_LIB_ENTRY(TTL_74139_DIP) LOCAL_LIB_ENTRY(TTL_74153_DIP) LOCAL_LIB_ENTRY(TTL_74155_DIP) LOCAL_LIB_ENTRY(TTL_74156_DIP) diff --git a/src/lib/netlist/macro/nlm_ttl74xx_lib.h b/src/lib/netlist/macro/nlm_ttl74xx_lib.h index d864f148c7e..ce91782b5e6 100644 --- a/src/lib/netlist/macro/nlm_ttl74xx_lib.h +++ b/src/lib/netlist/macro/nlm_ttl74xx_lib.h @@ -324,6 +324,9 @@ #define TTL_74125_DIP(name) \ NET_REGISTER_DEV(TTL_74125_DIP, name) +#define TTL_74139_DIP(name) \ + NET_REGISTER_DEV(TTL_74139_DIP, name) + #define TTL_74153_DIP(name) \ NET_REGISTER_DEV(TTL_74153_DIP, name) diff --git a/src/lib/netlist/nl_errstr.h b/src/lib/netlist/nl_errstr.h index e5844bbb4ab..553387301d9 100644 --- a/src/lib/netlist/nl_errstr.h +++ b/src/lib/netlist/nl_errstr.h @@ -55,7 +55,9 @@ namespace netlist // nl_setup.cpp PERRMSGV(MF_UNABLE_TO_PARSE_MODEL_1, 1, "Unable to parse model: {1}") - PERRMSGV(MF_MODEL_ALREADY_EXISTS_1, 1, "Model already exists: {1}") + // FIXME: Add an directive MODEL_OVERWRITE to netlist language + //PERRMSGV(MF_MODEL_ALREADY_EXISTS_1, 1, "Model already exists: {1}") + PERRMSGV(MI_MODEL_OVERWRITE_1, 2, "Model already exists, overwriting {1} with {2}") PERRMSGV(MF_DEVICE_ALREADY_EXISTS_1, 1, "Device already exists: {1}") PERRMSGV(MF_UNUSED_HINT_1, 1, "Error hint {1} is not used") PERRMSGV(MF_ADDING_HINT_1, 1, "Error adding hint {1} to hint list") diff --git a/src/lib/netlist/nl_setup.cpp b/src/lib/netlist/nl_setup.cpp index 394d5008f00..70860747f93 100644 --- a/src/lib/netlist/nl_setup.cpp +++ b/src/lib/netlist/nl_setup.cpp @@ -448,7 +448,12 @@ namespace netlist pstring model = plib::ucase(plib::trim(plib::left(model_in, pos))); pstring def = plib::trim(model_in.substr(pos + 1)); if (!m_abstract.m_models.insert({model, def}).second) - throw nl_exception(MF_MODEL_ALREADY_EXISTS_1(model_in)); + { + // FIXME: Add an directive MODEL_OVERWRITE to netlist language + //throw nl_exception(MF_MODEL_ALREADY_EXISTS_1(model_in)); + log().info(MI_MODEL_OVERWRITE_1(model, model_in)); + m_abstract.m_models[model] = def; + } } @@ -1623,6 +1628,7 @@ void setup_t::prepare_to_run() // resolve inputs resolve_inputs(); +#if 0 log().verbose("looking for two terms connected to rail nets ..."); for (auto & t : m_nlstate.get_device_list()) { @@ -1638,6 +1644,7 @@ void setup_t::prepare_to_run() #endif } } +#endif log().verbose("looking for unused hints ..."); for (auto &h : m_abstract.m_hints) diff --git a/src/mame/audio/nl_astrob.cpp b/src/mame/audio/nl_astrob.cpp new file mode 100644 index 00000000000..90d3d6c98ed --- /dev/null +++ b/src/mame/audio/nl_astrob.cpp @@ -0,0 +1,1161 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles + +// +// Netlist for Astro Blaster +// +// Derived from the schematics in the Astro Blaster manual. +// +// Known problems/issues: +// +// * All sounds work under the right conditions. +// +// * SONAR and BONUS sounds fail to trigger after the first +// few seconds. For some reason the triggering via CD4011 +// never actually fires the one-shot that controls the +// duration of the sound. +// +// * Performance is poor, primarily due to the SONAR sound +// and its connection to the mixing net. All attempts at +// separating the two have ended in failure so far. +// + +#include "netlist/devices/net_lib.h" +#include "nl_astrob.h" + + +// +// Optimizations +// + +#define HLE_LASER_1_VCO (1) +#define HLE_LASER_2_VCO (1) +#define SIMPLIFY_SONAR (1) +#define ENABLE_FRONTIERS (1) +#define UNDERCLOCK_NOISE_GEN (1) + + + +// +// Hacks +// + +#define ADD_CLIPPING_DIODES (1) + + + +// not really found anywhere, just took the 5239 and changed the +// breakdown voltage to 5.1 according to the datasheet +#define D_1N5231(name) ZDIODE(name, "D(BV=5.1 IBV=0.020 NBV=1)") +#define D_1N914(name) DIODE(name, "1N914") + +// SPICE model taken from https://www.onsemi.com/support/design-resources/models?rpn=2N4403 +#define Q_2N4403(name) QBJT_EB(name, "PNP(Is=650.6E-18 Xti=3 Eg=1.11 Vaf=115.7 Bf=216.2 Ne=1.829 Ise=58.72f Ikf=1.079 Xtb=1.5 Br=3.578 Nc=2 Isc=0 Ikr=0 Rc=.715 Cjc=14.76p Mjc=.5383 Vjc=.75 Fc=.5 Cje=19.82p Mje=.3357 Vje=.75 Tr=111.6n Tf=603.7p Itf=.65 Vtf=5 Xtf=1.7 Rb=10)") + +// JFET transistors not supported, but this should do the trick +#define Q_2N4093(name) MOSFET(name, "NMOS(VTO=-1 CAPMOD=0)") + +#define LM555_DIP NE555_DIP +#define LM566_DIP NE566_DIP + +#define TTL_74LS00_DIP TTL_7400_DIP +#define TTL_74LS04_DIP TTL_7404_DIP + + + +// +// Main netlist +// + +NETLIST_START(astrob) + +#if 1 + SOLVER(Solver, 1000) + PARAM(Solver.DYNAMIC_TS, 1) + PARAM(Solver.DYNAMIC_MIN_TIMESTEP, 4e-5) +#if (SIMPLIFY_SONAR) + PARAM(Solver.Solver_54.DYNAMIC_MIN_TIMESTEP, 7e-6) // gets rid of NR loops failure +#else + PARAM(Solver.Solver_40.DYNAMIC_MIN_TIMESTEP, 7e-6) // gets rid of NR loops failure +#endif +#else + SOLVER(Solver, 48000) +#endif + + // Overwrite model - the default model uses minimum datasheet + // specifications for 5V. These are for 10V and thus closer to the + // 12V used in this circuit. + NET_MODEL("CD4XXX FAMILY(TYPE=CMOS IVL=0.3 IVH=0.7 OVL=0.05 OVH=0.05 ORL=384 ORH=384)") + + TTL_INPUT(I_LO_D0, 0) + NET_C(I_LO_D0, U31.1) + ALIAS(I_INVADER_1, U31.2) + TTL_INPUT(I_LO_D1, 0) + NET_C(I_LO_D1, U31.3) + ALIAS(I_INVADER_2, U31.4) + TTL_INPUT(I_LO_D2, 0) + NET_C(I_LO_D2, U31.11) + ALIAS(I_INVADER_3, U31.10) + TTL_INPUT(I_LO_D3, 0) + NET_C(I_LO_D3, U31.9) + ALIAS(I_INVADER_4, U31.8) + TTL_INPUT(I_LO_D4, 0) + NET_C(I_LO_D4, U30.13) + ALIAS(I_ASTROIDS, U30.12) + TTL_INPUT(I_LO_D5, 0) + ALIAS(I_MUTE, I_LO_D5) + TTL_INPUT(I_LO_D6, 0) + NET_C(I_LO_D6, U30.3, U29.11) + ALIAS(I_REFILL, U29.10) + ALIAS(I_REFILL_Q, U30.4) + TTL_INPUT(I_LO_D7, 0) + ALIAS(W, I_LO_D7) + NET_C(I_LO_D7, U31.5) + ALIAS(I_WARP, U31.6) + + NET_C(GND, I_LO_D0.GND, I_LO_D1.GND, I_LO_D2.GND, I_LO_D3.GND, I_LO_D4.GND, I_LO_D5.GND, I_LO_D6.GND, I_LO_D7.GND) + NET_C(I_V5, I_LO_D0.VCC, I_LO_D1.VCC, I_LO_D2.VCC, I_LO_D3.VCC, I_LO_D4.VCC, I_LO_D5.VCC, I_LO_D6.VCC, I_LO_D7.VCC) + + TTL_INPUT(I_HI_D0, 0) + NET_C(I_HI_D0, U26.5) + ALIAS(I_LASER_1, U26.6) + TTL_INPUT(I_HI_D1, 0) + NET_C(I_HI_D1, U26.3) + ALIAS(I_LASER_2, U26.4) + TTL_INPUT(I_HI_D2, 0) + NET_C(I_HI_D2, U26.11) + ALIAS(I_SHORT_EXPL, U26.10) + TTL_INPUT(I_HI_D3, 0) + NET_C(I_HI_D3, U26.9) + ALIAS(I_LONG_EXPL, U26.8) + TTL_INPUT(I_HI_D4, 0) + NET_C(I_HI_D4, U26.1) + ALIAS(I_ATTACK_RATE, U26.2) + TTL_INPUT(I_HI_D5, 0) + NET_C(I_HI_D5, U31.13) + ALIAS(I_RATE_RESET, U31.12) + TTL_INPUT(I_HI_D6, 0) + NET_C(I_HI_D6, U29.9) + ALIAS(I_BONUS, U29.8) + TTL_INPUT(I_HI_D7, 0) + NET_C(I_HI_D7, U29.5) + ALIAS(I_SONAR, U29.6) + + NET_C(GND, I_HI_D0.GND, I_HI_D1.GND, I_HI_D2.GND, I_HI_D3.GND, I_HI_D4.GND, I_HI_D5.GND, I_HI_D6.GND, I_HI_D7.GND) + NET_C(I_V5, I_HI_D0.VCC, I_HI_D1.VCC, I_HI_D2.VCC, I_HI_D3.VCC, I_HI_D4.VCC, I_HI_D5.VCC, I_HI_D6.VCC, I_HI_D7.VCC) + + ANALOG_INPUT(I_V5, 5) + ANALOG_INPUT(I_V12, 12) + ANALOG_INPUT(I_VM12, -12) + + RES(R1, RES_K(100)) + RES(R2, RES_K(1.5)) + RES(R3, RES_K(330)) + RES(R4, RES_K(10)) +#if (SIMPLIFY_SONAR) + // use less resistance to account for only emulating 1/4 identical circuits + RES(R5, RES_K(17)) +#else + RES(R5, RES_K(68)) +#endif + RES(R6, RES_K(68)) + RES(R7, RES_K(22)) + RES(R8, RES_K(18)) + RES(R9, RES_K(33)) + RES(R10, RES_K(100)) + RES(R11, RES_K(150)) + RES(R12, RES_K(39)) + RES(R13, RES_K(330)) + RES(R14, RES_K(47)) + RES(R15, RES_K(220)) + RES(R16, RES_K(10)) + RES(R17, RES_K(10)) + RES(R18, RES_M(2.7)) +// RES(R19, RES_K(1)) + RES(R20, RES_M(1)) + RES(R21, RES_K(100)) + RES(R22, RES_K(470)) + RES(R23, RES_K(100)) + RES(R24, RES_M(2.2)) + RES(R25, RES_K(10)) + RES(R26, RES_K(820)) + RES(R27, RES_K(47)) + RES(R28, RES_K(22)) + RES(R29, RES_K(10)) + RES(R30, RES_K(82)) + RES(R31, RES_K(10)) + RES(R32, RES_K(22)) + RES(R33, RES_K(39)) + RES(R34, RES_K(4.7)) + RES(R35, RES_K(4.7)) + RES(R36, RES_K(100.1)) // part of SONAR circuit that relies on subtle part differences + RES(R37, RES_K(1.51)) // part of SONAR circuit that relies on subtle part differences + RES(R38, RES_K(330.1)) // part of SONAR circuit that relies on subtle part differences + RES(R39, RES_K(10.1)) // part of SONAR circuit that relies on subtle part differences + RES(R40, RES_K(68.1)) // part of SONAR circuit that relies on subtle part differences + RES(R41, RES_K(10)) + RES(R42, RES_K(100)) + RES(R43, RES_K(470)) + RES(R44, RES_M(1)) + RES(R45, RES_K(100)) + RES(R46, RES_K(470)) + RES(R47, RES_K(10)) + RES(R48, RES_K(82)) + RES(R49, RES_K(39)) + RES(R50, RES_K(22)) + RES(R51, RES_K(10)) + RES(R52, RES_K(68)) + RES(R53, RES_K(10)) + RES(R54, RES_K(22)) + RES(R55, RES_M(2.2)) + RES(R56, RES_K(33)) + RES(R57, RES_K(33)) + RES(R58, RES_K(820)) + RES(R59, RES_K(100)) + RES(R60, RES_K(10)) + RES(R61, RES_K(100)) + RES(R62, RES_K(99.9)) // part of SONAR circuit that relies on subtle part differences + RES(R63, RES_K(1.49)) // part of SONAR circuit that relies on subtle part differences + RES(R64, RES_K(329.9)) // part of SONAR circuit that relies on subtle part differences + RES(R65, RES_K(9.9)) // part of SONAR circuit that relies on subtle part differences + RES(R66, RES_K(67.9)) // part of SONAR circuit that relies on subtle part differences + RES(R67, RES_K(10)) + RES(R68, RES_K(82)) + RES(R69, RES_K(470)) + RES(R70, RES_K(100)) + RES(R71, RES_K(10)) + RES(R72, RES_K(10)) + RES(R73, RES_K(100)) + RES(R74, RES_K(4.7)) + RES(R75, RES_K(100)) + RES(R76, RES_K(22)) + RES(R77, RES_K(330)) + RES(R78, RES_K(1)) + RES(R79, RES_K(150)) + RES(R80, RES_M(1)) + RES(R81, RES_K(10)) + RES(R82, RES_K(100)) + RES(R83, RES_K(1)) + RES(R84, RES_K(5.6)) + RES(R85, RES_K(150)) + RES(R86, RES_K(10)) + RES(R87, RES_K(10)) + RES(R88, RES_K(100)) + RES(R89, RES_K(10)) + RES(R90, RES_K(100)) + RES(R91, RES_K(100.2)) // part of SONAR circuit that relies on subtle part differences + RES(R92, RES_K(1.52)) // part of SONAR circuit that relies on subtle part differences + RES(R93, RES_K(330.2)) // part of SONAR circuit that relies on subtle part differences + RES(R94, RES_K(10.2)) // part of SONAR circuit that relies on subtle part differences + RES(R95, RES_K(68.2)) // part of SONAR circuit that relies on subtle part differences + RES(R96, RES_K(10)) + RES(R97, RES_K(4.7)) + RES(R98, RES_M(1)) + RES(R99, RES_M(1)) + RES(R100, RES_M(1)) + RES(R101, RES_M(1)) + RES(R102, RES_K(470)) + RES(R103, RES_M(1)) + RES(R104, RES_K(10)) + RES(R105, RES_K(10)) + RES(R106, RES_M(2.7)) + RES(R107, RES_K(100)) + RES(R108, RES_K(100)) + RES(R109, RES_K(10)) + RES(R110, RES_K(10)) + RES(R111, RES_K(10)) + RES(R112, RES_K(10)) + RES(R113, RES_K(10)) + RES(R114, 100) +// RES(R115, RES_K(100)) -- part of final amp (not emulated) +// RES(R116, RES_K(470)) -- part of final amp (not emulated) + RES(R117, RES_K(1)) + RES(R118, RES_K(470)) + RES(R119, RES_K(470)) + RES(R120, RES_K(220)) + RES(R121, RES_K(10)) + RES(R122, RES_K(4.7)) +// RES(R123, RES_K(1)) -- part of final amp (not emulated) +// RES(R124, RES_K(100)) -- part of final amp (not emulated) + RES(R125, RES_K(82)) + RES(R126, RES_K(39)) + RES(R127, RES_K(10)) + RES(R128, RES_K(22)) + RES(R129, RES_K(10)) + RES(R130, RES_K(22)) + RES(R131, RES_K(39)) + RES(R132, RES_K(82)) + RES(R133, RES_K(100)) + RES(R134, RES_M(2.7)) +// RES(R135, RES_K(22)) -- part of final amp (not emulated) + RES(R136, RES_K(1)) + RES(R137, RES_K(4.7)) + RES(R138, RES_M(2.2)) + RES(R139, RES_K(4.7)) + RES(R140, RES_K(4.7)) +// RES(R141, RES_K(22)) +// RES(R142, RES_M(1)) -- part of final amp (not emulated) +// RES(R143, RES_K(22)) -- part of final amp (not emulated) +// RES(R144, RES_K(22)) -- part of final amp (not emulated) + RES(R145, RES_K(4.7)) + RES(R146, RES_K(100)) + RES(R147, RES_M(2.2)) + RES(R148, RES_K(100)) + RES(R149, RES_K(100)) + RES(R150, RES_K(33)) + RES(R151, RES_M(1)) + RES(R152, RES_K(22)) + RES(R153, RES_K(47)) + RES(R154, RES_K(24)) + RES(R155, RES_K(56)) + RES(R156, RES_K(39)) + RES(R157, RES_K(27)) + RES(R158, RES_K(33)) + RES(R159, RES_K(62)) + RES(R160, RES_K(82)) + RES(R161, RES_K(120)) + RES(R162, RES_K(4.7)) + RES(R163, RES_K(10)) + RES(R164, RES_K(10)) + RES(R165, RES_M(1)) + RES(R166, RES_M(1)) + RES(R167, RES_K(1)) + RES(R168, RES_M(1)) + RES(R169, RES_K(100)) + RES(R170, RES_M(2.2)) + RES(R171, RES_K(100)) + RES(R172, RES_K(33)) + RES(R173, RES_K(47)) + RES(R174, RES_K(47)) + RES(R175, RES_K(47)) + RES(R176, RES_K(47)) + RES(R177, RES_K(47)) + RES(R178, RES_K(22)) + RES(R179, RES_K(47)) + RES(R180, RES_K(47)) + RES(R181, RES_K(47)) + RES(R182, RES_K(10)) + RES(R183, RES_K(10)) + RES(R184, RES_K(4.7)) + RES(R185, RES_M(1)) + RES(R186, RES_K(1)) + + CAP(C1, CAP_U(0.01)) + CAP(C2, CAP_U(0.01)) + CAP(C3, CAP_U(0.1)) + CAP(C4, CAP_U(0.05)) + CAP(C5, CAP_U(0.1)) + CAP(C6, CAP_U(10)) + CAP(C7, CAP_U(0.1)) + CAP(C8, CAP_U(0.1)) + CAP(C9, CAP_U(0.1)) +// CAP(C10, CAP_U(0.05)) +// CAP(C11, CAP_U(0.05)) + CAP(C12, CAP_U(10)) +// CAP(C13, CAP_U(0.05)) +// CAP(C14, CAP_U(0.05)) + CAP(C15, CAP_U(10)) + CAP(C16, CAP_U(0.01)) + CAP(C17, CAP_U(0.01)) + CAP(C18, CAP_U(0.022)) + CAP(C19, CAP_U(0.05)) + CAP(C20, CAP_U(0.1)) + CAP(C21, CAP_U(0.33)) + CAP(C22, CAP_U(0.33)) + CAP(C23, CAP_U(0.01)) + CAP(C24, CAP_U(0.01)) + CAP(C25, CAP_U(0.01)) + CAP(C26, CAP_U(0.022)) + CAP(C27, CAP_U(10)) + CAP(C28, CAP_U(0.05)) +// CAP(C29, CAP_U(0.05)) +// CAP(C30, CAP_U(0.05)) + CAP(C31, CAP_U(0.0047)) + CAP(C32, CAP_U(0.05)) + CAP(C33, CAP_U(10)) + CAP(C34, CAP_U(10)) + CAP(C35, CAP_U(0.1)) + CAP(C36, CAP_U(0.0022)) + CAP(C37, CAP_U(0.1)) + CAP(C38, CAP_U(0.1)) + CAP(C39, CAP_U(0.1)) + CAP(C40, CAP_U(0.01)) + CAP(C41, CAP_U(0.01)) +// CAP(C42, CAP_U(0.001)) +// CAP(C43, CAP_U(10)) +// CAP(C44, CAP_U(0.001)) +// CAP(C45, CAP_U(0.001)) +// CAP(C46, CAP_U(10)) + CAP(C47, CAP_U(4.7)) +// CAP(C48, CAP_U(22)) -- part of final amp (not emulated) + CAP(C49, CAP_U(0.05)) + CAP(C50, CAP_U(0.05)) + CAP(C51, CAP_U(0.05)) + CAP(C52, CAP_U(0.05)) + CAP(C53, CAP_U(0.1)) +// CAP(C54, CAP_U(0.05)) + CAP(C55, CAP_U(0.05)) + CAP(C56, CAP_U(10)) + CAP(C57, CAP_U(0.1)) +// CAP(C58, CAP_U(4.7)) -- part of final amp (not emulated) + CAP(C59, CAP_U(0.33)) + CAP(C60, CAP_U(4.7)) + CAP(C61, CAP_U(4.7)) + CAP(C62, CAP_U(10)) + CAP(C63, CAP_U(0.1)) + CAP(C64, CAP_U(0.1)) + CAP(C65, CAP_U(0.01)) +// CAP(C66, CAP_U(0.05)) + CAP(C67, CAP_U(0.05)) + CAP(C68, CAP_U(0.0047)) + CAP(C69, CAP_U(4.7)) + CAP(C70, CAP_U(0.1)) + CAP(C71, CAP_U(0.022)) + CAP(C72, CAP_U(0.05)) +// CAP(C73, CAP_U(0.05)) + CAP(C74, CAP_U(0.05)) + CAP(C75, CAP_U(0.05)) + CAP(C76, CAP_U(0.022)) + CAP(C77, CAP_U(0.0047)) + CAP(C78, CAP_U(0.1)) +// CAP(C79, CAP_U(0.05)) + CAP(C80, CAP_U(4.7)) + CAP(C81, CAP_U(0.05)) + CAP(C82, CAP_U(0.1)) + + D_1N5231(D1) + D_1N914(D2) + D_1N5231(D3) + D_1N5231(D4) + D_1N5231(D5) + D_1N5231(D6) + D_1N914(D7) + D_1N914(D8) + D_1N914(D9) + D_1N5231(D10) + D_1N914(D11) + D_1N914(D12) +// D_1N914(D13) -- part of final amp (not emulated) + D_1N914(D14) + D_1N914(D15) + D_1N914(D16) + D_1N914(D17) + D_1N914(D18) + D_1N914(D19) + D_1N914(D20) + D_1N914(D21) + D_1N914(D22) + D_1N914(D23) + D_1N914(D24) + D_1N914(D25) + D_1N914(D26) + D_1N914(D27) + D_1N914(D28) + D_1N914(D29) + D_1N914(D30) + D_1N914(D31) + + Q_2N4403(Q1) + Q_2N4403(Q2) + Q_2N4403(Q3) + Q_2N4093(Q4) + Q_2N4093(Q5) + Q_2N4093(Q6) + Q_2N4403(Q7) + Q_2N4403(Q8) + Q_2N4403(Q9) + Q_2N4403(Q10) + + TL084_DIP(U1) // Op. Amp. + NET_C(U1.4, I_V12) + NET_C(U1.11, I_VM12) + + TL084_DIP(U2) // Op. Amp. + NET_C(U2.4, I_V12) + NET_C(U2.11, I_VM12) + + CD4017_DIP(U3) // Decade Counter/Divider + NET_C(U3.8, GND) + NET_C(U3.16, I_V12) + + NE555_DIP(U4) // Timer + + NE555_DIP(U5) // Timer + + NE555_DIP(U6) // Timer + + TL084_DIP(U7) // Op. Amp. + NET_C(U7.4, I_V12) + NET_C(U7.11, I_VM12) + + MM5837_DIP(U8) // Noise Generator +#if (UNDERCLOCK_NOISE_GEN) + // officially runs at 48-112kHz, but little noticeable difference + // in exchange for a big performance boost + PARAM(U8.FREQ, 12000) +#endif + + CD4011_DIP(U9) // Quad 2-Input NAND Gates + NET_C(U9.7, GND) + NET_C(U9.14, I_V12) + + CD4011_DIP(U10) // Quad 2-Input NAND Gates + NET_C(U10.7, GND) + NET_C(U10.14, I_V12) + + CD4011_DIP(U11) // Quad 2-Input NAND Gates + NET_C(U11.7, GND) + NET_C(U11.14, I_V12) + + CD4024_DIP(U12) // 7-Stage Ripple Binary Counter + NET_C(U12.7, GND) + NET_C(U12.14, I_V12) + + NE555_DIP(U13) // Timer + + CD4024_DIP(U14) // 7-Stage Ripple Binary Counter + NET_C(U14.7, GND) + NET_C(U14.14, I_V12) + + CD4017_DIP(U15) // Decade Counter/Divider + NET_C(U15.8, GND) + NET_C(U15.16, I_V12) + + TL084_DIP(U16) // Op. Amp. + NET_C(U16.4, I_V12) + NET_C(U16.11, I_VM12) + + NE555_DIP(U17) // Timer + + NE555_DIP(U18) // Timer + + CD4024_DIP(U19) // 7-Stage Ripple Binary Counter + NET_C(U19.7, GND) + NET_C(U19.14, I_V12) + + NE555_DIP(U20) // Timer + + CD4011_DIP(U21) // Quad 2-Input NAND Gates + NET_C(U21.7, GND) + NET_C(U21.14, I_V12) + + TL084_DIP(U22) // Op. Amp. + NET_C(U22.4, I_V12) + NET_C(U22.11, I_VM12) + + NE555_DIP(U23) // Timer + + NE555_DIP(U24) // Timer + + CD4011_DIP(U25) // Quad 2-Input NAND Gates + NET_C(U25.7, GND) + NET_C(U25.14, I_V12) + + TTL_7407_DIP(U26) // Hex Buffers with High Votage Open-Collector Outputs + NET_C(U26.7, GND) + NET_C(U26.14, I_V5) + + CD4011_DIP(U27) // Quad 2-Input NAND Gates + NET_C(U27.7, GND) + NET_C(U27.14, I_V12) + + CD4024_DIP(U28) // 7-Stage Ripple Binary Counter + NET_C(U28.7, GND) + NET_C(U28.14, I_V12) + + TTL_7407_DIP(U29) // Hex Buffers with High Votage Open-Collector Outputs + NET_C(U29.7, GND) + NET_C(U29.14, I_V5) + + TTL_7406_DIP(U30) // Hex inverter -- currently using a clone of 7416, no open collector behavior + NET_C(U30.7, GND) + NET_C(U30.14, I_V5) + + TTL_7406_DIP(U31) // Hex inverter -- currently using a clone of 7416, no open collector behavior + NET_C(U31.7, GND) + NET_C(U31.14, I_V5) + +// TTL_74LS374_DIP(U32) // Octal D-Type Transparent Latches And Edge-Triggered Flip-Flop +// NET_C(U32.10, GND) +// NET_C(U32.20, I_V5) + +// TTL_74LS374_DIP(U33) // Octal D-Type Transparent Latches And Edge-Triggered Flip-Flop +// NET_C(U33.10, GND) +// NET_C(U33.20, I_V5) + +// TTL_74LS00_DIP(U34) // Quad 4-Input NAND Gate +// NET_C(U34.7, GND) +// NET_C(U34.14, I_V5) + +// TTL_74LS30_DIP(U35) // 8-Input NAND Gate +// NET_C(U35.7, GND) +// NET_C(U35.14, I_V5) + +// TTL_74LS04_DIP(U36) // Hex Inverting Gates +// NET_C(U36.7, GND) +// NET_C(U36.14, I_V5) + + NE555_DIP(U37) // Timer + + NE555_DIP(U38) // Timer + + // + // Sheet 7, top-left (SONAR) + // + + NET_C(U1.3, GND) + NET_C(U1.2, D1.A, R4.1) + NET_C(U1.1, D1.K, R1.1) + NET_C(R1.2, R2.2, C1.1, C2.1) + NET_C(R2.1, GND) + NET_C(R4.2, R3.2, U1.7, C2.2, R5.2) + NET_C(C1.2, R3.1, U1.6) + NET_C(U1.5, GND) + +#if (SIMPLIFY_SONAR) + // sonar has 4 identical circuits; reduce the net size + // by only emulating one and multiplying it by 4 + NET_C(U1.9, U1.10, U1.12, U1.13, GND) + NET_C(R36.1, R36.2, R37.1, R37.2, R38.1, R38.2, R39.1, R39.2, R40.1, R40.2, GND) + NET_C(C16.1, C16.2, C17.1, C17.2, GND) + NET_C(D4.A, D4.K, GND) + + NET_C(U2.2, U2.3, U2.5, U2.6, GND) + NET_C(R62.1, R62.2, R63.1, R63.2, R64.1, R64.2, R65.1, R65.2, R66.1, R66.2, GND) + NET_C(C24.1, C24.2, C25.1, C25.2, GND) + NET_C(D6.A, D6.K, GND) + + NET_C(U2.9, U2.10, U2.12, U2.13, GND) + NET_C(R91.1, R91.2, R92.1, R92.2, R93.1, R93.2, R94.1, R94.2, R95.1, R95.2, GND) + NET_C(C40.1, C40.2, C41.1, C41.2, GND) + NET_C(D10.A, D10.K, GND) + + NET_C(R5.1, R114.1, Q3.B) +#else + NET_C(U1.12, GND) + NET_C(U1.13, D4.A, R39.1) + NET_C(U1.14, D4.K, R36.1) + NET_C(R36.2, R37.2, C16.1, C17.1) + NET_C(R37.1, GND) + NET_C(R39.2, R38.2, U1.8, C17.2, R40.2) + NET_C(C16.2, R38.1, U1.9) + NET_C(U1.10, GND) + + NET_C(U2.3, GND) + NET_C(U2.2, D6.A, R65.1) + NET_C(U2.14, D6.K, R62.1) + NET_C(R62.2, R63.2, C24.1, C25.1) + NET_C(R63.1, GND) + NET_C(R65.2, R64.2, U2.7, C25.2, R66.2) + NET_C(C24.2, R64.1, U2.6) + NET_C(U2.5, GND) + + NET_C(U2.12, GND) + NET_C(U2.13, D10.A, R94.1) + NET_C(U2.14, D10.K, R91.1) + NET_C(R91.2, R92.2, C40.1, C41.1) + NET_C(R92.1, GND) + NET_C(R94.2, R93.2, U2.8, C41.2, R95.2) + NET_C(C40.2, R93.1, U2.9) + NET_C(U2.10, GND) + + NET_C(R5.1, R40.1, R114.1, Q3.B, R95.1, R66.1) +#endif + + NET_C(R114.2, GND) + NET_C(Q3.E, Q2.E, Q1.C) + NET_C(Q1.B, R108.2, C47.2, R117.2) + NET_C(C47.1, R108.1, I_V12) + NET_C(I_V12, R109.1) + NET_C(R109.2, Q1.E) + NET_C(R117.1, D2.A) + NET_C(D2.K, U11.4, C4.2) + NET_C(C4.1, U11.2, R20.1) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_SONAR) + NET_C(D_SONAR.A, U11.2) + NET_C(D_SONAR.K, I_V12) +#endif + NET_C(U11.5, U11.6, U11.3) + NET_C(U11.1, R21.1) + NET_C(I_SONAR, R21.1) + NET_C(R21.2, R20.2, I_V12) + + NET_C(Q2.B, GND) + NET_C(Q2.C, R110.2, R111.2, U7.3) + NET_C(R110.1, GND) + NET_C(R111.1, R112.1, I_VM12) + NET_C(Q3.C, U7.2, R112.2, R113.1) + NET_C(R113.2, U7.1) + ALIAS(SONAR, U7.1) + + // + // Sheet 7, middle-left (LASER-1) + // + + NET_C(I_LASER_1, R146.1, U25.9) + NET_C(U25.8, C64.2, R138.1) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_LASER_1) + NET_C(D_LASER_1.A, U25.8) + NET_C(D_LASER_1.K, I_V12) +#endif + NET_C(R146.2, R138.2, R134.2, R139.2, U24.8, C56.1, I_V12) + NET_C(U25.10, U25.5, U25.6, D11.K, U24.4) + NET_C(C64.1, U25.4, U19.2) + NET_C(D11.A, R107.1, C57.2) + NET_C(C57.1, Q10.C, C65.1, U24.1, GND) + NET_C(R107.2, Q10.B, R134.1) + NET_C(Q10.E, U24.5) +#if (HLE_LASER_1_VCO) + // + // R2 = 0.98461: HP = (0.00000524285*A0) + 0.00000563193 + // R2 = 0.99441: HP = (0.000000368659*A0*A0) + (0.00000116694*A0) + 0.0000155514 + // R2 = 0.99797: HP = (0.000000154808*A0*A0*A0) - (0.00000213809*A0*A0) + (0.0000138122*A0) - 0.00000398935 + // R2 = 0.99877: HP = (-0.0000000527853*A0*A0*A0*A0) + (0.00000128033*A0*A0*A0) - (0.0000107258*A0*A0) + (0.0000413916*A0) - 0.0000352437 + // R2 = 0.99943: HP = (0.0000000343262*A0*A0*A0*A0*A0) - (0.00000096054*A0*A0*A0*A0) + (0.0000105481*A0*A0*A0) - (0.0000561978*A0*A0) + (0.000148191*A0) - 0.000131018 + // + VARCLOCK(LASER1CLK, 1, "max(0.000001,min(0.1,(0.0000000343262*A0*A0*A0*A0*A0) - (0.00000096054*A0*A0*A0*A0) + (0.0000105481*A0*A0*A0) - (0.0000561978*A0*A0) + (0.000148191*A0) - 0.000131018))") + NET_C(LASER1CLK.GND, GND) + NET_C(LASER1CLK.VCC, I_V12) + NET_C(LASER1CLK.A0, Q10.E) + NET_C(LASER1CLK.Q, LASER1ENV.A1) + AFUNC(LASER1ENV, 2, "if(A0>6,A1,0)") + NET_C(LASER1ENV.A0, U25.10) + NET_C(LASER1ENV.Q, U19.1) + NET_C(U24.3, GND) + NET_C(U24.2, U24.6, R140.1, C65.2, GND) +#else + NET_C(U24.3, U19.1) + NET_C(U24.2, U24.6, R140.1, C65.2) +#endif + NET_C(R140.2, U24.7, R139.1) + NET_C(C56.2, GND) + NET_C(U19.12, R132.1) + NET_C(U19.11, R131.1) + NET_C(U19.9, R130.1) + NET_C(U19.6, R105.1) + NET_C(U19.4, R104.1) + NET_C(R104.2, R105.2, R130.2, R131.2, R132.2, C52.1) + ALIAS(LASER_1, C52.2) + + // + // Sheet 7, bottom-left (LASER-2) + // + + NET_C(I_LASER_2, R149.1, U25.12) + NET_C(U25.13, C63.2, R147.1) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_LASER_2) + NET_C(D_LASER_2.A, U25.13) + NET_C(D_LASER_2.K, I_V12) +#endif + NET_C(R149.2, R147.2, R106.2, R145.2, U20.8, C62.1, I_V12) + NET_C(U25.11, U25.1, U25.2, D12.K, U20.4) + NET_C(C63.1, U25.3, U14.2) + NET_C(D12.A, R133.1, C53.2) + NET_C(C53.1, Q9.C, C68.1, U20.1, GND) + NET_C(R133.2, Q9.B, R106.1) + NET_C(Q9.E, U20.5) +#if (HLE_LASER_2_VCO) + // + // R2 = 0.98942: HP = (0.00000251528*A0) + 0.00000244265 + // R2 = 0.99596: HP = (0.000000160852*A0*A0) + (0.000000694298*A0) + 0.00000690896 + // R2 = 0.99821: HP = (0.000000068284*A0*A0*A0) - (0.000000949931*A0*A0) + (0.00000630369*A0) - 0.00000175548 + // R2 = 0.99896: HP = (-0.0000000291178*A0*A0*A0*A0) + (0.000000689145*A0*A0*A0) - (0.00000568073*A0*A0) + (0.0000214603*A0) - 0.0000188875 + // R2 = 0.99937: HP = (0.0000000153499*A0*A0*A0*A0*A0) - (0.000000433800*A0*A0*A0*A0) + (0.00000480504*A0*A0*A0) - (0.0000257871*A0*A0) + (0.000068467*A0) - 0.0000608574 + // + VARCLOCK(LASER2CLK, 1, "max(0.000001,min(0.1,(0.0000000153499*A0*A0*A0*A0*A0) - (0.000000433800*A0*A0*A0*A0) + (0.00000480504*A0*A0*A0) - (0.0000257871*A0*A0) + (0.000068467*A0) - 0.0000608574))") + NET_C(LASER2CLK.GND, GND) + NET_C(LASER2CLK.VCC, I_V12) + NET_C(LASER2CLK.A0, Q9.E) + NET_C(LASER2CLK.Q, LASER2ENV.A1) + AFUNC(LASER2ENV, 2, "if(A0>6,A1,0)") + NET_C(LASER2ENV.A0, U25.11) + NET_C(LASER2ENV.Q, U14.1) + NET_C(U20.3, GND) + NET_C(U20.2, U20.6, R137.1, C68.2, GND) +#else + NET_C(U20.3, U14.1) + NET_C(U20.2, U20.6, R137.1, C68.2) +#endif + NET_C(R137.2, U20.7, R145.1) + NET_C(C62.2, GND) + NET_C(U14.12, R125.1) + NET_C(U14.11, R126.1) + NET_C(U14.9, R128.1) + NET_C(U14.6, R129.1) + NET_C(U14.4, R127.1) + NET_C(R127.2, R129.2, R128.2, R126.2, R125.2, C51.1) + ALIAS(LASER_2, C51.2) + + // + // Sheet 7, middle-top (REFILL) + // + + NET_C(I_REFILL, U3.15, R59.1) + NET_C(R59.2, I_V12, R17.2) + + // CD4011 oscillator: R18=2.7M, C3=0.1u, freq=2.664 + CLOCK(OSC1, 2.664) + NET_C(OSC1.GND, GND) + NET_C(OSC1.VCC, I_V12) + NET_C(OSC1.Q, U3.14) + NET_C(R18.1, R18.2, C3.1, C3.2, U9.1, U9.2, U9.5, U9.6, GND) + + NET_C(U3.3, R8.1) + NET_C(U3.2, R7.1) + NET_C(U3.4, R9.1) + NET_C(U3.7, R12.1) + NET_C(U3.10, R14.1) + NET_C(U3.1, R6.1) + NET_C(U3.5, R10.1) + NET_C(U3.6, R11.1) + NET_C(U3.9, R15.1) + NET_C(U3.11, R13.1) + NET_C(U3.13, GND) + NET_C(R17.1, R8.2, R7.2, R9.2, R12.2, R14.2, R6.2, R10.2, R11.2, R15.2, R13.2, R16.2) + NET_C(R16.1, Q7.B) + + // CD4011 oscillator: R46=470k, C20=0.1u, freq=15.307 + CLOCK(OSC2, 15.307) + NET_C(OSC2.GND, GND) + NET_C(OSC2.VCC, I_V12) + NET_C(OSC2.Q, U4.4, U9.12, U9.13) + NET_C(U9.11, U5.4) + NET_C(R46.1, R46.2, C20.1, C20.2, U9.8, U9.9, GND) + + NET_C(U5.7, R67.1, R68.2) + NET_C(R68.1, U5.2, U5.6, C26.2) + NET_C(C26.1, GND, U5.1, C18.1, U4.1, Q7.C) + NET_C(U5.5, U4.5, Q7.E) + NET_C(R67.2, U5.8, R41.2, U4.8, I_V12) + NET_C(R41.1, R42.2, U4.7) + NET_C(R42.1, U4.2, U4.6, C18.2) + NET_C(U5.3, R69.2) + NET_C(U4.3, R43.2) + NET_C(R43.1, C49.1, R69.1, I_REFILL_Q) + ALIAS(REFILL, C49.2) + + // + // Sheet 7, middle-bottom (ATTACK, RATE RESET) + // + + NET_C(I_RATE_RESET, U15.15, R96.1) + NET_C(R96.2, I_V12) + NET_C(I_ATTACK_RATE, R148.1, U21.5) + NET_C(R148.2, R166.2, I_V12) + NET_C(U21.6, R166.1, C67.2) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_ATTACK_RATE) + NET_C(D_ATTACK_RATE.A, U21.6) + NET_C(D_ATTACK_RATE.K, I_V12) +#endif + NET_C(C67.1, U21.3) + NET_C(U21.4, U21.1, U21.2, U15.14) + + NET_C(U15.3, D23.A) + NET_C(D23.K, R161.1) + NET_C(U15.2, D22.A) + NET_C(D22.K, R160.1) + NET_C(U15.4, D21.A) + NET_C(D21.K, R159.1) + NET_C(U15.7, D17.A) + NET_C(D17.K, R155.1) + NET_C(U15.10, D15.A) + NET_C(D15.K, R153.1) + NET_C(U15.1, D18.A) + NET_C(D18.K, R156.1) + NET_C(U15.5, D20.A) + NET_C(D20.K, R158.1) + NET_C(U15.6, D19.A) + NET_C(D19.K, R157.1) + NET_C(U15.9, D16.A) + NET_C(D16.K, R154.1) + NET_C(U15.11, D14.A) + NET_C(D14.K, R152.1) + NET_C(U15.13, GND) + NET_C(R161.2, R160.2, R159.2, R155.2, R153.2, R156.2, R158.2, R157.2, R154.2, R152.2, R178.2, U16.6) + + NET_C(I_WARP, R164.1) + NET_C(R164.2, R162.1, R163.2, U16.5) + NET_C(R162.2, I_V12) + NET_C(R163.1, GND) + NET_C(U16.7, R178.1) + ALIAS(V, U16.7) + + // + // Sheet 7, top-right (EXPLOSIONS) + // + + NET_C(I_SHORT_EXPL, R169.1, U27.2) + NET_C(R169.2, R185.2, I_V12) + NET_C(U27.1, R185.1, C81.2) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_SHORT_EXPL) + NET_C(D_SHORT_EXPL.A, U27.1) + NET_C(D_SHORT_EXPL.K, I_V12) +#endif + NET_C(C81.1, U27.11) + NET_C(U27.13, U27.12, U27.3) + NET_C(U27.3, R184.1) + NET_C(R184.2, D31.A) + NET_C(D31.K, D30.A, R182.2, R183.2) + NET_C(R182.1, GND) + NET_C(D30.K, R180.1, R181.1, C80.1, D29.K) + NET_C(C80.2, GND) + NET_C(R180.2, U16.9, R179.1) + NET_C(R179.2, U16.8, R177.1) + NET_C(R181.2, U16.10, Q6.D, D26.K) // D and S swapped on schematics??? + NET_C(Q6.G, C72.2, R151.2, Q5.G) + NET_C(R151.1, GND) + NET_C(C72.1, U8.3) + NET_C(U8.4, I_V12) + NET_C(U8.2, I_VM12) + NET_C(U8.1, GND) + NET_C(Q6.S, D26.A, GND) + NET_C(D29.A, D24.K, C69.1) + NET_C(C69.2, GND) + NET_C(R183.1, R186.2, U27.4, U27.8, U27.9, D28.K) + NET_C(R186.1, C75.1) + NET_C(C75.2, U21.8, U21.9, R168.2) + NET_C(R168.1, U21.11) + NET_C(I_LONG_EXPL, R171.1, U27.6) + NET_C(R171.2, R170.2, I_V12) + NET_C(R170.1, U27.5, C82.2) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_LONG_EXPL) + NET_C(D_LONG_EXPL.A, U27.5) + NET_C(D_LONG_EXPL.K, I_V12) +#endif + NET_C(C82.1, U27.10) + NET_C(U21.12, U21.13, C74.1, R165.2) + NET_C(U21.10, C74.2, R167.1) + NET_C(R165.1, GND) + NET_C(R167.2, D24.A) + NET_C(R177.2, R176.1, C78.1) + NET_C(C78.2, U16.2, U16.1, C61.1) + NET_C(R176.2, U16.3, C77.2, C76.2) + ALIAS(EXPLOSIONS, C61.2) + NET_C(C77.1, GND) + NET_C(C76.1, D27.K, D28.A) + NET_C(D27.A, GND) + + // + // Sheet 7, middle-right (ASTROIDS) + // + + NET_C(I_ASTROIDS, R97.1, R174.1, R175.1) + NET_C(R97.2, I_V12) + NET_C(R174.2, U16.13, R173.1) + NET_C(R175.2, U16.12, Q5.D, D25.K) // D and S swapped on schematics??? + NET_C(D25.A, Q5.S, GND) + NET_C(R173.2, U16.14, R172.1) + NET_C(R172.2, R150.1, C70.1) + NET_C(C70.2, U7.9, U7.8, R136.1) + NET_C(R150.2, C71.2, U7.10) + NET_C(C71.1, GND) + NET_C(R136.2, C59.2, C60.1) + NET_C(C59.1, GND) + ALIAS(ASTROIDS, C60.2) + + // + // Sheet 7, bottom-right (mixer) + // + + ALIAS(INVADER_1, R98.1) + ALIAS(INVADER_2, R99.1) + ALIAS(INVADER_3, R100.1) + ALIAS(INVADER_4, R101.1) + NET_C(R98.2, R99.2, R100.2, R101.2, C55.1) + NET_C(ASTROIDS, R121.1) + NET_C(EXPLOSIONS, R122.1) + ALIAS(BONUS, R118.1) + NET_C(REFILL, R119.1) + NET_C(LASER_1, R102.1) + NET_C(LASER_2, R103.1) + NET_C(SONAR, R120.1) + NET_C(C55.2, R121.2, R122.2, R118.2, R119.2, R102.2, R103.2, R120.2) + AFUNC(MUTEFUNC, 2, "if(A0>2.5,0,A1)") + NET_C(MUTEFUNC.A0, I_MUTE) + NET_C(MUTEFUNC.A1, R121.2) + ALIAS(OUTPUT, MUTEFUNC.Q) +// ALIAS(OUTPUT, C55.2) + + // + // Sheet 8, middle-top (INVADER_1) + // + + NET_C(U22.10, GND) + NET_C(U22.9, D5.A, R29.1) + NET_C(R29.2, R55.2, U22.14, C21.2, R72.1) + NET_C(D5.K, U22.8, R58.1) + NET_C(R58.2, C22.1, R57.2, R56.2, C21.1) + NET_C(W, U30.5) + NET_C(U30.6, R57.1) + NET_C(R56.1, GND) + NET_C(U22.13, C22.2, R55.1) + NET_C(U22.12, GND) + + NET_C(R85.1, U23.7, R86.2) + NET_C(R85.2, U23.8, R83.2, R84.2, R73.2, U18.8, I_V12) + NET_C(R86.1, U23.2, U23.6, C38.2) + NET_C(C38.1, U23.1, C37.1, C28.1, U18.1, GND) + NET_C(I_INVADER_1, U23.4, R73.1, U18.4) + NET_C(C28.2, U18.5) + NET_C(U23.3, U18.2) + NET_C(U23.5, R87.2) + NET_C(R87.1, V) + NET_C(R83.1, D9.A) + NET_C(D9.K, Q8.B, R72.2) + NET_C(Q8.E, R84.1) + NET_C(Q8.C, C37.2, U18.6, U18.7) + NET_C(U18.3, INVADER_1) + + // + // Sheet 8, middle (INVADER_2) + // + + NET_C(R81.2, R70.2, U13.8, C27.1, I_V12) + NET_C(C27.2, GND) + NET_C(R81.1, R82.2, U13.7) + NET_C(R82.1, U13.2, U13.6, C36.2) + NET_C(C36.1, U13.1, GND) + NET_C(U13.5, R71.2) + NET_C(R71.1, V) + NET_C(I_INVADER_2, U13.4, R70.1) + NET_C(U13.3, U12.1) + // CD4011 oscillator: R22=470k, C5=0.1u, freq=15.3Hz + CLOCK(OSC3, 15.3) + NET_C(OSC3.GND, GND) + NET_C(OSC3.VCC, I_V12) + NET_C(OSC3.Q, U12.2) + NET_C(R22.1, R22.2, C5.1, C5.2, U11.8, U11.9, U11.12, U11.13, GND) + NET_C(U12.12, R48.1) + NET_C(U12.11, R49.1) + NET_C(U12.9, R50.1) + NET_C(U12.6, R47.1) + NET_C(R47.2, R50.2, R49.2, R48.2) + NET_C(R48.2, INVADER_2) + + // + // Sheet 8, middle-bottom (INVADER_4) + // + + NET_C(R89.2, U37.4, U37.8, C15.1, R60.2, R88.2, U38.8, I_V12) + NET_C(R89.1, U37.7, R90.2) + NET_C(R90.1, U37.2, U37.6, C39.2) + NET_C(C39.1, U37.1, C23.1, U38.1, GND) + NET_C(U37.5, R35.1) + NET_C(R35.2, V, R34.1) + NET_C(C15.2, GND) + NET_C(U37.3, U28.1) + NET_C(U28.2, U28.5) + NET_C(U28.12, R31.1) + NET_C(U28.11, R32.1) + NET_C(U28.9, R33.1) + NET_C(U28.6, R30.1) + NET_C(R31.2, R32.2, R33.2, R30.2, R34.2, U38.5) + NET_C(U38.2, U38.6, C23.2, R61.1) + NET_C(R61.2, U38.7, R60.1) + NET_C(U38.4, R88.1, I_INVADER_4) + NET_C(INVADER_4, U38.3) + + // + // Sheet 8, middle-bottom (BONUS) + // + + NET_C(I_BONUS, R45.1, U10.6) + NET_C(C34.1, R77.2, I_V12) + NET_C(R45.2, R44.2, R74.2, U6.4, U6.8, C33.1, I_V12) + + NET_C(R44.1, U10.5, C19.1) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_BONUS) + NET_C(D_BONUS.A, U10.5) + NET_C(D_BONUS.K, I_V12) +#endif + NET_C(U10.4, U10.1, U10.2) + NET_C(C19.2, U10.3, D7.K) + NET_C(D7.A, R78.1) + NET_C(R78.2, R77.1, C34.2, R76.1) + // CD4011 oscillator: R80=1M, C35=0.1, freq=7.19Hz + CLOCK(OSC4, 7.19) + NET_C(OSC4.GND, GND) + NET_C(OSC4.VCC, I_V12) + NET_C(OSC4.Q, R79.1) + NET_C(R80.1, R80.2, C35.1, C35.2, U10.8, U10.9, U10.12, U10.13, GND) + NET_C(R79.2, R74.1, R75.2, U6.7) + NET_C(R75.1, U6.2, U6.6, C31.2) + NET_C(C31.1, C32.1, U6.1, GND) + NET_C(C32.2, U6.5) + NET_C(R76.2, D8.K, C50.1) + NET_C(U6.3, D8.A) + NET_C(C33.2, GND) + NET_C(BONUS, C50.2) + + // + // Sheet 8, top-right (INVADER_3) + // + + NET_C(U22.5, GND) + NET_C(U22.6, D3.A, R25.1) + NET_C(R25.2, C12.2, R24.2, U22.1, C8.2) + NET_C(D3.K, U22.7, R26.1) + NET_C(W, U30.9) + NET_C(U30.8, R28.1) + NET_C(R28.2, R26.2, C9.1, R27.2, C8.1) + NET_C(R27.1, GND) + NET_C(C9.2, U22.2, R24.1) + NET_C(U22.3, GND) + NET_C(C12.1, R54.1) + NET_C(R54.2, R53.1, U17.5) + NET_C(R53.2, V) + NET_C(U17.1, C7.1, GND) + NET_C(C7.2, U17.2, U17.6, R52.1) + NET_C(R52.2, U17.7, R51.1) + NET_C(R51.2, R23.2, U17.8, C6.1, I_V12) + NET_C(R23.1, U17.4, I_INVADER_3) + NET_C(C6.2, GND) + NET_C(INVADER_3, U17.3) + + // + // Unconnected inputs + // + + NET_C(GND, U7.5, U7.6, U7.12, U7.13) + NET_C(GND, U26.13) + NET_C(GND, U29.1, U29.3, U29.13) + NET_C(GND, U30.1, U30.11) + + // + // Unconnected outputs + // + +#if (ENABLE_FRONTIERS) +#define RXX 192 + OPTIMIZE_FRONTIER(INVADER_1, RES_M(1), RXX) + OPTIMIZE_FRONTIER(INVADER_2, RES_M(1), RXX) + OPTIMIZE_FRONTIER(INVADER_3, RES_M(1), RXX) + OPTIMIZE_FRONTIER(INVADER_4, RES_M(1), RXX) + + OPTIMIZE_FRONTIER(C50.1, RES_M(10), RXX) + OPTIMIZE_FRONTIER(C60.1, RES_M(10), RXX) // this is a big one + OPTIMIZE_FRONTIER(C61.1, RES_M(10), RXX) +#endif + +NETLIST_END() diff --git a/src/mame/audio/nl_astrob.h b/src/mame/audio/nl_astrob.h new file mode 100644 index 00000000000..952dad78bfa --- /dev/null +++ b/src/mame/audio/nl_astrob.h @@ -0,0 +1,10 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +#ifndef MAME_AUDIO_NL_ASTROB_H +#define MAME_AUDIO_NL_ASTROB_H + +#pragma once + +NETLIST_EXTERNAL(astrob) + +#endif // MAME_AUDIO_NL_ASTROB_H diff --git a/src/mame/audio/nl_cinemat_common.h b/src/mame/audio/nl_cinemat_common.h index 1b2079b79b1..7533acd56da 100644 --- a/src/mame/audio/nl_cinemat_common.h +++ b/src/mame/audio/nl_cinemat_common.h @@ -71,7 +71,7 @@ // models copied from https://www.diodes.com/assets/Spice-Models/Discrete-Prodcut-Groups/Zener-Diodes.txt #define D_1N914(name) DIODE(name, "1N914") #define D_1N914B(name) DIODE(name, "1N914") -#define D_1N5236B(name) DIODE(name, "D(BV=7.5 IS=27.5p RS=33.8 N=1.10 CJO=58.2p VJ=0.750 M=0.330 TT=50.1n)") +#define D_1N5236B(name) ZDIODE(name, "D(BV=7.5 IS=27.5p RS=33.8 N=1.10 CJO=58.2p VJ=0.750 M=0.330 TT=50.1n)") #define D_1N5240(name) ZDIODE(name, "D(BV=10 IS=14.4p RS=32.0 N=1.10 CJO=24.1p VJ=0.750 M=0.330 TT=50.1n)") #define D_1N5240B(name) ZDIODE(name, "D(BV=10 IS=14.4p RS=32.0 N=1.10 CJO=24.1p VJ=0.750 M=0.330 TT=50.1n)") diff --git a/src/mame/audio/nl_elim.cpp b/src/mame/audio/nl_elim.cpp new file mode 100644 index 00000000000..1727de5c46f --- /dev/null +++ b/src/mame/audio/nl_elim.cpp @@ -0,0 +1,1225 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles + +//NL_CONTAINS elim zektor + +// +// Netlist for Eliminator/Zektor +// +// Derived from the schematics in the Eliminator and Zektor manuals. +// +// Known problems/issues: +// +// * The noise signal (WBN) is connected into the TORPEDO_2 sound +// but seems to make no audible difference, either in the +// simulation or in recordings. Disabling it gives a nice speedup. +// +// * Torpedo sounds don't retrigger until they're complete. This means +// you can fire several times before the next firing sound triggers. +// In recordings of games this doesn't appear to be the case. +// + +#include "netlist/devices/net_lib.h" +#include "nl_elim.h" + + +// +// Optimizations +// + +#define UNDERCLOCK_NOISE_GEN (1) +#define HLE_BACKGROUND_VCO (1) +#define HLE_TORPEDO1_VCO (1) +#define HLE_TORPEDO2_VCO (1) +#define HLE_SKITTER_VCO (1) +#define ENABLE_FRONTIERS (1) + + + +// +// Hacks +// + +#define ADD_CLIPPING_DIODES (1) +#define DISABLE_TORPEDO2_NOISE (1) + + + +// +// Initial compilation includes this section. +// + +#ifndef SOUND_VARIANT + + +#define D_1N914(name) DIODE(name, "1N914") +#define D_1N4002(name) DIODE(name, "D(IS=65.4p RS=42.2m BV=100 IBV=5.00u CJO=14.8p M=0.333 N=1.36 TT=2.88u)") + +// SPICE model taken from https://www.onsemi.com/support/design-resources/models?rpn=2N4403 +#define Q_2N4403(name) QBJT_EB(name, "PNP(Is=650.6E-18 Xti=3 Eg=1.11 Vaf=115.7 Bf=216.2 Ne=1.829 Ise=58.72f Ikf=1.079 Xtb=1.5 Br=3.578 Nc=2 Isc=0 Ikr=0 Rc=.715 Cjc=14.76p Mjc=.5383 Vjc=.75 Fc=.5 Cje=19.82p Mje=.3357 Vje=.75 Tr=111.6n Tf=603.7p Itf=.65 Vtf=5 Xtf=1.7 Rb=10)") + +#define LM555_DIP NE555_DIP +#define LM566_DIP NE566_DIP + +#define TTL_74LS00_DIP TTL_7400_DIP + +// +// DIP mappings use the submodels below for CA3080 +// +#define CA3080_DIP(name) SUBMODEL(_CA3080_FAST_DIP, name) +static NETLIST_START(_CA3080_FAST_DIP) + ALIAS(2, F.A0) // - + ALIAS(3, F.A1) // + + ALIAS(4, F.A2) // V- + ALIAS(5, RIABC.1) // IB + ALIAS(6, VO.OP) // FIXME + ALIAS(7, F.A4) // V+ + + RES(RI, 26000) + NET_C(RI.1, F.A0) + NET_C(RI.2, F.A1) + // Delivers I0 + AFUNC(F, 5, "max(-0.5e-3, min(0.5e-3, 19.2 * (A3 - A2) * A0))") + RES(RIABC, 1) + NET_C(RIABC.2, F.A2) + NET_C(RIABC.1, F.A3) // IB + VCCS(VO, 1) + ANALOG_INPUT(XGND, 0) + NET_C(XGND, VO.IN, VO.ON) // FIXME: assume symmetric supply + NET_C(F.Q, VO.IP) +NETLIST_END() + + + +// +// Now include ourselves twice, once for Eliminator and +// once for Zektor +// + +#define VARIANT_ELIMINATOR 0 +#define VARIANT_ZEKTOR 1 + +#define SOUND_VARIANT (VARIANT_ELIMINATOR) +#include "nl_elim.cpp" + +#undef SOUND_VARIANT +#define SOUND_VARIANT (VARIANT_ZEKTOR) +#include "nl_elim.cpp" + + +#else + + +// +// Main netlist +// + +#if (SOUND_VARIANT == VARIANT_ELIMINATOR) +NETLIST_START(elim) +#else // (SOUND_VARIANT == VARIANT_ZEKTOR) +NETLIST_START(zektor) +#endif + + SOLVER(Solver, 1000) + PARAM(Solver.DYNAMIC_TS, 1) + PARAM(Solver.DYNAMIC_MIN_TIMESTEP, 2e-5) + + // Overwrite model - the default model uses minimum datasheet + // specifications for 5V. These are for 10V and thus closer to the + // 12V used in this circuit. + NET_MODEL("CD4XXX FAMILY(TYPE=CMOS IVL=0.3 IVH=0.7 OVL=0.05 OVH=0.05 ORL=384 ORH=384)") + + LOCAL_SOURCE(_CA3080_FAST_DIP) + +// TTL_INPUT(I_LO_D0, 0) + TTL_INPUT(I_LO_D1, 0) + ALIAS(I_FIREBALL, I_LO_D1) + TTL_INPUT(I_LO_D2, 0) + ALIAS(I_EXPLOSION_1, I_LO_D2) + TTL_INPUT(I_LO_D3, 0) + ALIAS(I_EXPLOSION_2, I_LO_D3) + TTL_INPUT(I_LO_D4, 0) + ALIAS(I_EXPLOSION_3, I_LO_D4) + TTL_INPUT(I_LO_D5, 0) + ALIAS(I_BOUNCE, I_LO_D5) + TTL_INPUT(I_LO_D6, 0) + ALIAS(I_TORPEDO_1, I_LO_D6) + TTL_INPUT(I_LO_D7, 0) + ALIAS(I_TORPEDO_2, I_LO_D7) + + NET_C(GND, I_LO_D1.GND, I_LO_D2.GND, I_LO_D3.GND, I_LO_D4.GND, I_LO_D5.GND, I_LO_D6.GND, I_LO_D7.GND) + NET_C(I_V5, I_LO_D1.VCC, I_LO_D2.VCC, I_LO_D3.VCC, I_LO_D4.VCC, I_LO_D5.VCC, I_LO_D6.VCC, I_LO_D7.VCC) + + TTL_INPUT(I_HI_D0, 0) + ALIAS(I_THRUST_LOW, I_HI_D0) + TTL_INPUT(I_HI_D1, 0) + ALIAS(I_THRUST_HI, I_HI_D1) + TTL_INPUT(I_HI_D2, 0) + ALIAS(I_THRUST_LSB, I_HI_D2) + TTL_INPUT(I_HI_D3, 0) + ALIAS(I_THRUST_MSB, I_HI_D3) + TTL_INPUT(I_HI_D4, 0) + ALIAS(I_SKITTER, I_HI_D4) + TTL_INPUT(I_HI_D5, 0) + ALIAS(I_ENEMY_SHIP, I_HI_D5) + TTL_INPUT(I_HI_D6, 0) + ALIAS(I_BACKGROUND_LSB, I_HI_D6) + TTL_INPUT(I_HI_D7, 0) + ALIAS(I_BACKGROUND_MSB, I_HI_D7) + + NET_C(GND, I_HI_D0.GND, I_HI_D1.GND, I_HI_D2.GND, I_HI_D3.GND, I_HI_D4.GND, I_HI_D5.GND, I_HI_D6.GND, I_HI_D7.GND) + NET_C(I_V5, I_HI_D0.VCC, I_HI_D1.VCC, I_HI_D2.VCC, I_HI_D3.VCC, I_HI_D4.VCC, I_HI_D5.VCC, I_HI_D6.VCC, I_HI_D7.VCC) + + RES(R_PSG_1, 1000) + RES(R_PSG_2, 1000) + RES(R_PSG_3, 1000) + NET_C(I_V5, R_PSG_1.1, R_PSG_2.1, R_PSG_3.1) + + ANALOG_INPUT(I_V5, 5) + ANALOG_INPUT(I_V12, 12) + ANALOG_INPUT(I_VM12, -12) + + // + // Part differences between Eliminator and Zektor + // + // Ref Des ELIMINATOR ZEKTOR + // R5 10K 4.7K + // R9 33K 12K + // R71 270K 100K + // R79 2 MEG unused + // R122 220K 390K + // R132 220K 100K + // C9 0.01uF 0.0047uF + // C46 0.022uF 0.047uF + // + +// RES(R1, RES_K(100)) -- part of final amp (not emulated) +// RES(R2, RES_M(1)) -- part of final amp (not emulated) +// RES(R3, RES_K(22)) -- part of final amp (not emulated) +// RES(R4, RES_K(2.2)) -- part of final amp (not emulated) +#if (SOUND_VARIANT == VARIANT_ELIMINATOR) +// RES(R5, RES_K(10)) -- part of final amp (not emulated) +#else // (SOUND_VARIANT == VARIANT_ZEKTOR) +// RES(R5, RES_K(4.7)) -- part of final amp (not emulated) +#endif + RES(R6, RES_K(220)) + RES(R7, RES_K(220)) + RES(R8, RES_K(10)) +#if (SOUND_VARIANT == VARIANT_ELIMINATOR) + RES(R9, RES_K(33)) +#else // (SOUND_VARIANT == VARIANT_ZEKTOR) + RES(R9, RES_K(12)) +#endif + RES(R10, RES_K(10)) +// RES(R11, RES_K(2.2)) -- part of final amp (not emulated) +// RES(R12, RES_M(1)) -- part of final amp (not emulated) +// RES(R13, RES_K(330)) -- part of final amp (not emulated) + RES(R14, RES_K(470)) + RES(R15, RES_K(100)) + RES(R16, RES_K(100)) + RES(R17, RES_K(10)) + RES(R18, RES_K(10)) + RES(R19, RES_K(1)) + RES(R20, RES_K(10)) + RES(R21, RES_K(100)) + RES(R22, RES_K(10)) + RES(R23, RES_K(4.7)) +// R24?? + RES(R25, RES_K(220)) + RES(R26, RES_K(470)) + RES(R27, RES_K(470)) + RES(R28, RES_K(33)) + RES(R29, RES_K(68)) + RES(R30, RES_K(22)) + RES(R31, RES_K(1)) + RES(R32, RES_K(47)) + RES(R33, RES_K(82)) + RES(R34, RES_K(22)) + RES(R35, RES_K(1)) + RES(R36, RES_M(1.5)) + RES(R37, RES_K(6.8)) + RES(R38, RES_K(33)) + RES(R39, RES_K(22)) + RES(R40, RES_K(1)) + RES(R41, RES_M(2.2)) + RES(R42, RES_M(2.2)) + RES(R43, RES_K(1)) + RES(R44, RES_K(10)) + RES(R45, RES_K(47)) + RES(R46, RES_K(22)) + RES(R47, RES_K(2)) + RES(R48, RES_K(6.8)) + RES(R49, RES_K(33)) + RES(R50, RES_K(22)) + RES(R51, RES_K(1)) + RES(R52, RES_K(1)) + RES(R53, RES_K(1)) + RES(R54, RES_K(2)) + RES(R55, RES_K(1)) + RES(R56, RES_K(1)) + RES(R57, RES_K(1)) + RES(R58, RES_K(33)) + RES(R59, RES_K(2)) + RES(R60, RES_K(10)) + RES(R61, RES_K(47)) + RES(R62, RES_K(220)) + RES(R63, RES_K(1)) + RES(R64, RES_K(150)) + RES(R65, RES_K(390)) + RES(R66, RES_K(33)) + RES(R67, RES_K(100)) + RES(R68, RES_K(100)) + RES(R69, RES_K(1)) + RES(R70, RES_K(470)) +#if (SOUND_VARIANT == VARIANT_ELIMINATOR) + RES(R71, RES_K(270)) +#else // (SOUND_VARIANT == VARIANT_ZEKTOR) + RES(R71, RES_K(100)) +#endif + RES(R72, RES_K(1)) + RES(R73, RES_K(680)) + RES(R74, RES_K(390)) + RES(R75, RES_K(150)) + RES(R76, RES_K(10)) + RES(R77, RES_K(10)) + RES(R78, RES_K(10)) +#if (SOUND_VARIANT == VARIANT_ELIMINATOR) + RES(R79, RES_M(2)) +#endif + RES(R80, RES_K(1)) + RES(R81, RES_M(1.5)) + RES(R82, 470) + RES(R83, 470) + RES(R84, RES_K(2.2)) + RES(R85, RES_K(2.2)) + RES(R86, RES_K(2.2)) + RES(R87, RES_K(2.2)) + RES(R88, RES_K(2.2)) + RES(R89, RES_K(2.2)) + RES(R90, RES_K(15)) + RES(R91, RES_M(1)) + RES(R92, RES_K(22)) + RES(R93, RES_M(1)) + RES(R94, RES_K(10)) + RES(R95, RES_K(10)) + RES(R96, RES_K(22)) + RES(R97, RES_K(39)) + RES(R98, RES_K(82)) + RES(R99, RES_K(10)) + RES(R100, RES_K(10)) + RES(R101, 910) + RES(R102, RES_K(6.8)) + RES(R103, RES_K(9.1)) // RES_K(2.2)) + RES(R104, RES_K(15)) + RES(R105, RES_K(10)) + RES(R106, RES_K(680)) + RES(R107, RES_K(4.7)) + RES(R108, RES_K(4.7)) + RES(R109, RES_K(2.2)) + RES(R110, RES_K(2.2)) + RES(R111, RES_K(100)) + RES(R112, RES_M(2.2)) + RES(R113, RES_M(1)) + RES(R114, RES_K(100)) + RES(R115, RES_M(1)) + RES(R116, RES_M(1)) + RES(R117, RES_M(1)) + RES(R118, RES_K(100)) + RES(R119, RES_K(100)) + RES(R120, RES_K(100)) + RES(R121, RES_M(2.2)) +#if (SOUND_VARIANT == VARIANT_ELIMINATOR) + RES(R122, RES_K(220)) +#else // (SOUND_VARIANT == VARIANT_ZEKTOR) + RES(R122, RES_K(390)) +#endif + RES(R123, RES_K(100)) + RES(R124, RES_M(2.2)) + RES(R125, RES_K(100)) + RES(R126, RES_K(10)) + RES(R127, RES_K(22)) + RES(R128, RES_K(39)) + RES(R129, RES_K(82)) + RES(R130, RES_K(22)) + RES(R131, RES_K(1)) +#if (SOUND_VARIANT == VARIANT_ELIMINATOR) + RES(R132, RES_K(220)) +#else // (SOUND_VARIANT == VARIANT_ZEKTOR) + RES(R132, RES_K(100)) +#endif + RES(R133, 680) + RES(R134, RES_K(10)) + RES(R135, RES_K(10)) + RES(R136, RES_K(680)) + RES(R137, RES_K(10)) + RES(R138, RES_K(10)) + RES(R139, RES_K(330)) + RES(R140, RES_K(270)) + RES(R141, RES_K(220)) + RES(R142, RES_K(330)) + RES(R143, RES_K(680)) + RES(R144, RES_K(100)) + RES(R145, RES_K(68)) + RES(R146, RES_K(22)) + RES(R147, RES_K(2.2)) + +// CAP(C1, CAP_U(4.7)) -- part of final amp (not emulated) +// CAP(C2, CAP_U(0.1)) +// CAP(C3, CAP_U(0.1)) +// CAP(C4, CAP_U(10)) -- part of final amp (not emulated) + CAP(C5, CAP_U(0.1)) + CAP(C6, CAP_U(0.1)) + CAP(C7, CAP_U(0.001)) + CAP(C8, CAP_U(10)) +#if (SOUND_VARIANT == VARIANT_ELIMINATOR) + CAP(C9, CAP_U(0.01)) +#else // (SOUND_VARIANT == VARIANT_ZEKTOR) + CAP(C9, CAP_U(0.0047)) +#endif + CAP(C10, CAP_U(0.068)) + CAP(C11, CAP_U(0.068)) + CAP(C12, CAP_U(0.1)) + CAP(C13, CAP_U(0.1)) + CAP(C14, CAP_U(0.1)) + CAP(C15, CAP_U(0.068)) + CAP(C16, CAP_U(0.068)) + CAP(C17, CAP_U(0.068)) + CAP(C18, CAP_U(0.068)) + CAP(C19, CAP_U(0.068)) + CAP(C20, CAP_U(0.068)) + CAP(C21, CAP_U(2.2)) +// CAP(C22, CAP_U(0.1)) +// CAP(C23, CAP_U(0.1)) +// CAP(C24, CAP_U(0.1)) +// CAP(C25, CAP_U(0.1)) +// CAP(C26, CAP_U(0.1)) +// CAP(C27, CAP_U(0.1)) + CAP(C28, CAP_U(0.1)) + CAP(C29, CAP_U(0.1)) + CAP(C30, CAP_U(0.022)) +// CAP(C31, CAP_U(0.1)) +// CAP(C32, CAP_U(0.1)) + CAP(C33, CAP_U(0.1)) + CAP(C34, CAP_U(0.1)) + CAP(C35, CAP_U(1)) + CAP(C36, CAP_U(2.2)) + CAP(C37, CAP_U(10)) + CAP(C38, CAP_U(4.7)) + CAP(C39, CAP_U(0.1)) +// CAP(C40, CAP_U(0.1)) +// CAP(C41, CAP_U(0.1)) +// C42?? + CAP(C43, CAP_U(0.033)) + CAP(C44, CAP_U(0.1)) + CAP(C45, CAP_U(0.1)) +#if (SOUND_VARIANT == VARIANT_ELIMINATOR) + CAP(C46, CAP_U(0.022)) +#else // (SOUND_VARIANT == VARIANT_ZEKTOR) + CAP(C46, CAP_U(0.047)) +#endif + CAP(C47, CAP_U(0.047)) + CAP(C48, CAP_U(0.05)) + CAP(C49, CAP_U(0.1)) + CAP(C50, CAP_U(0.05)) + CAP(C51, CAP_U(0.05)) + CAP(C52, CAP_U(0.05)) + CAP(C53, CAP_U(0.1)) + CAP(C54, CAP_U(0.1)) + CAP(C55, CAP_U(0.1)) + CAP(C56, CAP_U(33)) + CAP(C57, CAP_U(0.1)) + CAP(C58, CAP_U(0.1)) + CAP(C59, CAP_U(0.1)) + CAP(C60, CAP_U(0.001)) + CAP(C61, CAP_U(0.068)) +// CAP(C62, CAP_U(0.1)) + CAP(C63, CAP_P(100)) + CAP(C64, CAP_U(0.1)) + CAP(C65, CAP_U(0.01)) + CAP(C66, CAP_U(0.022)) + CAP(C67, CAP_U(0.047)) + CAP(C68, CAP_U(0.01)) + CAP(C69, CAP_U(0.1)) +// CAP(C70, CAP_U(0.1)) +// CAP(C71, CAP_U(0.1)) +// CAP(C72, CAP_U(0.1)) +// CAP(C73, CAP_U(10)) +// CAP(C74, CAP_U(0.1)) + CAP(C75, CAP_U(0.1)) + CAP(C76, CAP_U(0.1)) + CAP(C77, CAP_U(0.1)) + + D_1N914(D1) + D_1N914(D2) + D_1N914(D3) + D_1N914(D4) + D_1N914(D5) + D_1N914(D6) + D_1N914(D7) + D_1N4002(D8) + +// Q_2N4093(Q1) -- part of final amp (not emulated) + Q_2N4403(Q2) + Q_2N4403(Q3) + Q_2N4403(Q4) + Q_2N4403(Q5) + Q_2N4403(Q6) + Q_2N4403(Q7) + Q_2N4403(Q8) + Q_2N4403(Q9) + Q_2N4403(Q10) + Q_2N4403(Q11) + + TL081_DIP(U1) // Op. Amp. + NET_C(U1.7, I_V12) + NET_C(U1.4, I_VM12) + +// TL082_DIP(U2) // Op. Amp. -- part of final amp (not emulated) +// NET_C(U2.8, I_V12) +// NET_C(U2.4, I_VM12) + + TL081_DIP(U3) // Op. Amp. + NET_C(U3.7, I_V12) + NET_C(U3.4, I_VM12) + + MM5837_DIP(U4) // Noise Generator +#if (UNDERCLOCK_NOISE_GEN) + PARAM(U4.FREQ, 24000) +#endif + + LM555_DIP(U5) // Timer + + CA3080_DIP(U6) // Op. Amp. + NET_C(U6.4, I_VM12) + NET_C(U6.7, I_V12) + + CA3080_DIP(U7) // Op. Amp. + NET_C(U7.4, I_VM12) + NET_C(U7.7, I_V12) + + CA3080_DIP(U8) // Op. Amp. + NET_C(U8.4, I_VM12) + NET_C(U8.7, I_V12) + + CA3080_DIP(U9) // Op. Amp. + NET_C(U9.4, I_VM12) + NET_C(U9.7, I_V12) + + CA3080_DIP(U10) // Op. Amp. + NET_C(U10.4, I_VM12) + NET_C(U10.7, I_V12) + + CA3080_DIP(U11) // Op. Amp. + NET_C(U11.4, I_VM12) + NET_C(U11.7, I_V12) + + TL081_DIP(U12) // Op. Amp. + NET_C(U12.7, I_V12) + NET_C(U12.4, I_VM12) + + LM555_DIP(U13) // Timer + + CA3080_DIP(U14) // Op. Amp. + NET_C(U14.4, I_VM12) + NET_C(U14.7, I_V12) + + CA3080_DIP(U15) // Op. Amp. + NET_C(U15.4, I_VM12) + NET_C(U15.7, I_V12) + + TL082_DIP(U16) // Op. Amp. + NET_C(U16.8, I_V12) + NET_C(U16.4, I_VM12) + + CD4011_DIP(U17) // Quad 2-Input NAND Gates + NET_C(U17.7, GND) + NET_C(U17.14, I_V12) + + CD4011_DIP(U18) // Quad 2-Input NAND Gates + NET_C(U18.7, GND) + NET_C(U18.14, I_V12) + + CD4011_DIP(U19) // Quad 2-Input NAND Gates + NET_C(U19.7, GND) + NET_C(U19.14, I_V12) + + TL082_DIP(U20) // Op. Amp. + NET_C(U20.8, I_V12) + NET_C(U20.4, I_VM12) + + CD4024_DIP(U21) // 7-Stage Ripple Binary Counter + NET_C(U21.7, GND) + NET_C(U21.14, I_V12) + + CD4011_DIP(U22) // Quad 2-Input NAND Gates + NET_C(U22.7, GND) + NET_C(U22.14, I_V12) + + CD4024_DIP(U23) // 7-Stage Ripple Binary Counter + NET_C(U23.7, GND) + NET_C(U23.14, I_V12) + + TTL_7406_DIP(U24) // Hex inverter -- currently using a clone of 7416, no open collector behavior + NET_C(U24.7, GND) + NET_C(U24.14, I_V5) + +#if (!HLE_BACKGROUND_VCO) + LM566_DIP(U25) // Voltage-Controlled Oscillator +#endif + + TTL_74LS00_DIP(U26) // Quad 4-Input NAND Gate + NET_C(U26.7, GND) + NET_C(U26.14, I_V5) + + TTL_7407_DIP(U27) // Hex Buffers with High Votage Open-Collector Outputs + NET_C(U27.7, GND) + NET_C(U27.14, I_V5) + + TTL_7407_DIP(U28) // Hex Buffers with High Votage Open-Collector Outputs + NET_C(U28.7, GND) + NET_C(U28.14, I_V5) + + TL081_DIP(U29) // Op. Amp. + NET_C(U29.7, I_V12) + NET_C(U29.4, I_VM12) + +// AY_3_8912_DIP(U30) // PSG -- emulated by MAME + + CD4069_DIP(U31) // Hex Inverter + NET_C(U31.7, GND) + NET_C(U31.14, I_V12) + + CD4069_DIP(U32) // Hex Inverter + NET_C(U32.7, GND) + NET_C(U32.14, I_V12) + +// TTL_74LS125_DIP(U33) // Quad 3-state buffer +// NET_C(U33.7, GND) +// NET_C(U33.14, I_V5) + +// TTL_74LS374_DIP(U34) // Octal D-Type Transparent Latches And Edge-Triggered Flip-Flop +// NET_C(U34.10, GND) +// NET_C(U34.20, I_V5) +// +// TTL_74LS374_DIP(U35) // Octal D-Type Transparent Latches And Edge-Triggered Flip-Flop +// NET_C(U34.10, GND) +// NET_C(U34.20, I_V5) + +// TTL_74LS74_DIP(U36) // Dual D Flip Flop +// NET_C(U36.7, GND) +// NET_C(U36.14, I_V5) + +// TTL_74LS10_DIP(U37) // Triple 3-Input NAND Gate +// NET_C(U37.7, GND) +// NET_C(U37.14, I_V5) + +// TTL_74LS14_DIP(U38) +// NET_C(U38.7, GND) +// NET_C(U38.14, I_V5) + +// TTL_74LS08_DIP(U39) // Quad 2-Input AND Gates +// NET_C(U39.7, GND) +// NET_C(U39.14, I_V5) + +// TTL_74LS30_DIP(U40) // 8-Input NAND Gate +// NET_C(U40.7, GND) +// NET_C(U40.14, I_V5) + +// TTL_74LS14_DIP(U41) +// NET_C(U41.7, GND) +// NET_C(U41.14, I_V5) + + // + // Sheet 7, top-left/middle (Thrust) + // + + NET_C(R58.1, GND) + NET_C(R58.2, U12.3, U10.6, U11.6, U15.6) + ALIAS(SUM_VCA, R58.2) + NET_C(U12.2, R75.1, R76.2) + NET_C(R76.1, GND) + NET_C(R75.2, U12.6) + ALIAS(BUFFER, U12.6) + + NET_C(I_V12, U4.4, C75.2) + NET_C(C75.1, GND) + NET_C(U4.1, GND) + NET_C(U4.2, I_VM12, C76.2) + NET_C(C76.1, GND) + NET_C(U4.3, C77.1) + NET_C(C77.2, R27.2, U3.3) + NET_C(R27.1, GND) + NET_C(U3.2, U3.6, R28.1, R32.1) + ALIAS(WBN, U3.6) + + NET_C(R28.2, C17.2, R29.1) + NET_C(C17.1, GND) + NET_C(R29.2, C18.2, R30.1) + NET_C(C18.1, GND) + NET_C(R30.2, R31.2, U10.2) + NET_C(R31.1, GND) + NET_C(U10.3, R56.2) + NET_C(R56.1, GND) + + NET_C(I_THRUST_LOW, U24.5) + NET_C(U24.6, R99.1, R70.1) + NET_C(R99.2, I_V12) + NET_C(R70.2, R71.2, Q4.E) + NET_C(I_THRUST_HI, U24.3) + NET_C(U24.4, R100.1, R71.1) + NET_C(R100.2, I_V12) + NET_C(Q4.B, GND) + NET_C(Q4.C, R69.1) + NET_C(R69.2, U10.5) + + NET_C(R32.2, C19.2, R33.1) + NET_C(C19.1, GND) + NET_C(R33.2, C20.2, R34.1) + NET_C(C20.1, GND) + NET_C(R34.2, R35.2, U11.2) + NET_C(R35.1, GND) + NET_C(U11.3, R57.2) + NET_C(R57.1, GND) + + NET_C(I_THRUST_LSB, U24.1) + NET_C(U24.2, R77.1, R73.1) + NET_C(R77.2, I_V12) + NET_C(R73.2, R74.2, Q5.E) + NET_C(I_THRUST_MSB, U24.13) + NET_C(U24.12, R78.1, R74.1) + NET_C(R78.2, I_V12) + NET_C(Q5.B, GND) + NET_C(Q5.C, R72.1) + NET_C(R72.2, U11.5) + + // + // Sheet 7, top-right (Skitter) + // + + NET_C(I_SKITTER, U24.9) + NET_C(U24.8, R25.2, D1.K, R17.1) + NET_C(R17.2, I_V12) + NET_C(D1.A, C21.1, R26.2, R25.1, Q3.B) + NET_C(C21.2, GND) + NET_C(R26.1, GND) + NET_C(Q3.C, GND, Q2.C) + NET_C(Q3.E, R23.1) + NET_C(R23.2, R22.2) + NET_C(R22.1, Q2.E) + +#if (HLE_SKITTER_VCO) + // + // U1 is basically a standalone oscillator. Simulation at high + // frequency puts it at around 45Hz. Samples from real machines + // show it to be about 35Hz. + // + CLOCK(SKITTERCLK, 45) + NET_C(SKITTERCLK.GND, GND) + NET_C(SKITTERCLK.VCC, I_V12) + AFUNC(SKITTERENV, 1, "if(A0<6,2,18)") + NET_C(SKITTERENV.A0, SKITTERCLK.Q) + NET_C(SKITTERENV.Q, Q2.B) + NET_C(GND, C5.1, C5.2, C47.1, C47.2, R14.1, R14.2, R15.1, R15.2, R16.1, R16.2, R19.1, R19.2, U1.2, U1.3) +#else + NET_C(Q2.B, R19.1) + NET_C(R19.2, C5.1) + NET_C(C5.2, C47.2, U1.2, R16.1) + NET_C(C47.1, GND) + NET_C(U1.3, R15.1, R14.2) + NET_C(R14.1, GND) + NET_C(R15.2, U1.6, R16.2) +#endif + + NET_C(C9.2, R21.1) + NET_C(C9.1, GND) + NET_C(R21.2, R18.1) + NET_C(R18.2, I_V12) + NET_C(I_V12, C8.1) + NET_C(C8.2, GND) + NET_C(U5.7, R18.1) + NET_C(U5.5, R23.2) + NET_C(U5.4, U24.8) + NET_C(U5.2, U5.6, R21.1) + NET_C(U5.8, I_V12) + NET_C(U5.1, GND) + NET_C(U5.3, R20.1) + NET_C(R20.2, C7.2, C6.1) + NET_C(C7.1, GND) + ALIAS(SKITTER, C6.2) + + // + // Sheet 7, middle-right (Enemy Ship) + // + + NET_C(I_ENEMY_SHIP, U24.11) + NET_C(U24.10, R60.1, U13.4) + NET_C(R60.2, I_V12) + NET_C(U13.7, R59.1, R61.2) + NET_C(R59.2, I_V12) + NET_C(R61.1, U13.2, U13.6, C30.2) + NET_C(C30.1, GND) + NET_C(U13.5, R145.1, R144.1) + ALIAS(_10HZ, R144.2) + NET_C(U13.1, GND) + NET_C(U13.8, I_V12, C29.2) + NET_C(C29.1, GND) + NET_C(U13.3, C28.1) + ALIAS(ENEMY_SHIP, C28.2) + + ALIAS(_100HZ, R135.1) + NET_C(R145.2, R135.1) + ALIAS(_50HZ, R134.1) + ALIAS(_200HZA, R137.1) + ALIAS(_200HZB, R138.1) + NET_C(R138.2, R137.2, R134.2, R135.2, C61.2, R136.1) + NET_C(C61.1, GND) + NET_C(R136.2, Q11.E, U27.6) + NET_C(U27.5, U26.8) + NET_C(Q11.B, GND) + NET_C(Q11.C, R131.1) + NET_C(R131.2, U15.5) + NET_C(U15.2, R106.2, R83.2) + NET_C(R83.1, GND) + NET_C(U15.3, R82.2) + NET_C(R82.1, GND) + + // + // Sheet 7, bottom-right (Background) + // + + NET_C(I_BACKGROUND_LSB, U27.1, U26.10) + NET_C(U27.2, R107.1, R108.1) + NET_C(R107.2, I_V12) + NET_C(R108.2, R101.1, U16.6, R109.2) + NET_C(I_BACKGROUND_MSB, U27.3, U26.9) + NET_C(U27.4, R110.1, R109.1) + NET_C(R110.2, I_V12) + NET_C(U16.5, GND) + NET_C(U16.7, R101.2, R104.1) + NET_C(R104.2, U16.2, R105.1, C56.2) + NET_C(U16.3, R102.2, R103.1) + NET_C(R103.2, I_V12) + NET_C(R102.1, GND) + NET_C(U16.1, R105.2, C56.1, C60.1) + NET_C(C60.2, R130.1) + NET_C(R130.2, D8.K, C59.1) + NET_C(D8.A, I_V12) + NET_C(C59.2, GND) + +#if (HLE_BACKGROUND_VCO) + // + // R2 = 0.96310: HP = (0.000237231*A0) - 0.000561399 + // R2 = 0.99617: HP = (0.0000527091*A0*A0) - (0.000501828*A0) + 0.00197538 + // R2 = 0.99867: HP = (0.0000174335*A0*A0*A0) - (0.000309878*A0*A0) + (0.00198163*A0) - 0.00362162 + // R2 = 0.99898: HP = (0.00000748879*A0*A0*A0*A0) - (0.000189174*A0*A0*A0) + (0.00180942*A0*A0) - (0.00759439*A0) + 0.0124560 + // R2 = 0.99899: HP = (0.00000135301*A0*A0*A0*A0*A0) - (0.0000390321*A0*A0*A0*A0) + (0.000446417*A0*A0*A0) - (0.00250299*A0*A0) + (0.00693360*A0) - 0.00698302 + // + VARCLOCK(BGCLK, 1, "max(0.000001,min(0.1,(0.00000748879*A0*A0*A0*A0) - (0.000189174*A0*A0*A0) + (0.00180942*A0*A0) - (0.00759439*A0) + 0.0124560))") + NET_C(BGCLK.GND, GND) + NET_C(BGCLK.VCC, I_V5) + NET_C(BGCLK.A0, U16.1) + NET_C(BGCLK.Q, BGENV.A0) + NET_C(GND, C57.1, C57.2, C58.1, C58.2) + AFUNC(BGENV, 1, "if(A0>2.5,2.5,-2.5)") + NET_C(BGENV.Q, R106.1) +#else + NET_C(U16.1, U25.5) + NET_C(C60.2, U25.6) + NET_C(R130.2, U25.8) + NET_C(U25.7, C58.2) + NET_C(C58.1, GND) + NET_C(U25.1, GND) + NET_C(U25.3, C57.1) + NET_C(C57.2, R106.1) +#endif + + // + // Sheet 7, PSG input + // + + NET_C(R_PSG_1.2, R_PSG_2.2, R_PSG_3.2, R133.2, C64.1) + NET_C(R133.1, GND) + NET_C(C64.2, R132.1) + NET_C(R132.2, U29.2, R122.1, C63.1) + NET_C(U29.3, GND) + NET_C(C63.2, R122.2, U29.6) + ALIAS(PSG, U29.6) + + // + // Sheet 8, oscillators + // + // Pairs of CD4069 are used together with resistors and capacitors + // to create oscillators. The formula for the frequency is + // 1/(1.39*R*C). The following are simulated here: + // + // _200HZA: R142=330K, C68=0.01uF, Freq=218.01Hz + // _200HZB: R139=330K, C65=0.01uF, Freq=218.01Hz + // _10HZ: R143=680K, C69=0.1uF, Freq=10.58Hz + // _50HZ: R141=220K, C67=0.047uF, Freq=69.58Hz + // _100HZ: R140=270K, C66=0.022uF, Freq=121.12Hz + // + + CLOCK(_200HZACLK, 218.01) + NET_C(_200HZACLK.VCC, I_V12) + NET_C(_200HZACLK.GND, GND) + NET_C(_200HZA, _200HZACLK.Q) + NET_C(R142.1, R142.2, C68.1, C68.2, U32.5, U32.9, GND) + HINT(U32.6, NC) + HINT(U32.8, NC) + + CLOCK(_200HZBCLK, 217.98) // tweak frequency so this is out of phase + NET_C(_200HZBCLK.VCC, I_V12) + NET_C(_200HZBCLK.GND, GND) + NET_C(_200HZB, _200HZBCLK.Q) + NET_C(R139.1, R139.2, C65.1, C65.2, U32.13, U32.11, GND) + HINT(U32.12, NC) + HINT(U32.10, NC) + + CLOCK(_10HZCLK, 10.58) + NET_C(_10HZCLK.VCC, I_V12) + NET_C(_10HZCLK.GND, GND) + NET_C(_10HZ, _10HZCLK.Q) + NET_C(R143.1, R143.2, C69.1, C69.2, U32.1, U32.3, GND) + HINT(U32.2, NC) + HINT(U32.4, NC) + + CLOCK(_50HZCLK, 69.58) + NET_C(_50HZCLK.VCC, I_V12) + NET_C(_50HZCLK.GND, GND) + NET_C(_50HZ, _50HZCLK.Q) + NET_C(R141.1, R141.2, C67.1, C67.2, U31.1, U31.3, GND) + HINT(U31.2, NC) + HINT(U31.4, NC) + + // Note this oscillator is on Sheet 7, in the right-middle section + CLOCK(_100HZCLK, 121.12) + NET_C(_100HZCLK.VCC, I_V12) + NET_C(_100HZCLK.GND, GND) + NET_C(_100HZ, _100HZCLK.Q) + NET_C(R140.1, R140.2, C66.1, C66.2, U31.5, U31.9, GND) + HINT(U31.6, NC) + HINT(U31.8, NC) + + // + // Sheet 8, top middle (Explosion 1) + // + + NET_C(WBN, R44.1) + NET_C(R44.2, C13.2, R45.1) + NET_C(C13.1, GND) + NET_C(R45.2, C14.2, R46.1) + NET_C(C14.1, GND) + NET_C(R46.2, R47.2, U8.2) + NET_C(R47.1, GND) + NET_C(U8.3, R54.2) + NET_C(R54.1, GND) + NET_C(U8.6, SUM_VCA) + + NET_C(I_EXPLOSION_1, U28.11) + NET_C(U28.10, R120.1, U19.6) + NET_C(R120.2, I_V12) + NET_C(U19.5, R116.1, C51.1) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_EXPLOSION_1) + NET_C(D_EXPLOSION_1.A, U19.5) + NET_C(D_EXPLOSION_1.K, I_V12) +#endif + NET_C(R116.2, I_V12) + NET_C(C51.2, U19.3) + NET_C(U19.4, U19.1, U19.2, R87.1) + NET_C(R87.2, Q9.E) + NET_C(Q9.B, GND) + NET_C(Q9.C, C37.1, R66.1) + NET_C(C37.2, I_VM12) + NET_C(R66.2, U8.5) + + // + // Sheet 8, top right (Explosion 2) + // + + NET_C(WBN, R48.1) + NET_C(R48.2, C15.2, R49.1) + NET_C(C15.1, GND) + NET_C(R49.2, C16.2, R50.1) + NET_C(C16.1, GND) + NET_C(R50.2, R51.2, U9.2) + NET_C(R51.1, GND) + NET_C(U9.3, R55.2) + NET_C(R55.1, GND) + NET_C(U9.6, SUM_VCA) + + NET_C(I_EXPLOSION_2, U28.9) + NET_C(U28.8, R119.1, U19.8) + NET_C(R119.2, I_V12) + NET_C(U19.9, R117.1, C52.1) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_EXPLOSION_2) + NET_C(D_EXPLOSION_2.A, U19.9) + NET_C(D_EXPLOSION_2.K, I_V12) +#endif + NET_C(R117.2, I_V12) + NET_C(C52.2, U19.11) + NET_C(U19.10, U19.12, U19.13, R88.1) + NET_C(R88.2, Q10.E) + NET_C(Q10.B, GND) + NET_C(Q10.C, C38.1, R68.1) + NET_C(C38.2, I_VM12) + NET_C(R68.2, U9.5) + + // + // Sheet 8, bottom left (Explosion 3) + + NET_C(WBN, R37.1) + NET_C(R37.2, C10.2, R38.1) + NET_C(C10.1, GND) + NET_C(R38.2, C11.2, R39.1) + NET_C(C11.1, GND) + NET_C(R39.2, R40.2, U6.2) + NET_C(R40.1, GND) + NET_C(U6.3, R52.2) + NET_C(R52.1, GND) + NET_C(U6.6, SUM_VCA) + + NET_C(I_EXPLOSION_3, U28.5) + NET_C(U28.6, R118.1, U18.8) + NET_C(R118.2, I_V12) + NET_C(U18.9, R115.1, C50.1) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_EXPLOSION_3) + NET_C(D_EXPLOSION_3.A, U18.9) + NET_C(D_EXPLOSION_3.K, I_V12) +#endif + NET_C(R115.2, I_V12) + NET_C(C50.2, U18.11) + NET_C(U18.10, U18.12, U18.13, R86.1) + NET_C(R86.2, Q8.E) + NET_C(Q8.B, GND) + NET_C(Q8.C, C36.1, R62.1) + NET_C(C36.2, I_VM12) + NET_C(R62.2, U6.5) + + // + // Sheet 8, bottom left (Fireball) + // + + NET_C(WBN, R41.1) + NET_C(R41.2, R43.2, U7.2, R42.2) + NET_C(R43.1, GND) + NET_C(R42.1, C12.2) + NET_C(C12.1, _50HZ) + NET_C(U7.3, R53.2) + NET_C(R53.1, GND) + NET_C(U7.6, SUM_VCA) + + NET_C(I_FIREBALL, U28.13) + NET_C(U28.12, R111.1, U18.6) + NET_C(R111.2, I_V12) + NET_C(U18.5, R112.1, C49.1) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_FIREBALL) + NET_C(D_FIREBALL.A, U18.5) + NET_C(D_FIREBALL.K, I_V12) +#endif + NET_C(R112.2, I_V12) + NET_C(C49.2, U18.3) + NET_C(U18.4, U18.1, U18.2, R85.1) + NET_C(R85.2, Q7.E) + NET_C(Q7.B, GND) + NET_C(Q7.C, R67.1) + NET_C(R67.2, C35.1, R64.1) + NET_C(C35.2, I_VM12) + NET_C(R64.2, U7.5) + + // + // Sheet 8, bottom-ish middle (Bounce) + // + + NET_C(_50HZ, C45.1) + NET_C(C45.2, R81.1) + NET_C(R81.2, R80.2, U14.2) +#if (SOUND_VARIANT == VARIANT_ELIMINATOR) + NET_C(WBN, R79.1) + NET_C(R79.2, R80.2) +#endif + NET_C(R80.1, GND) + NET_C(U14.3, R63.2) + NET_C(R63.1, GND) + NET_C(U14.6, SUM_VCA) + + NET_C(I_BOUNCE, U28.3) + NET_C(U28.4, R114.1, U17.8) + NET_C(R114.2, I_V12) + NET_C(U17.9, R113.1, C48.1) + NET_C(R113.2, I_V12) + NET_C(C48.2, U17.11) + NET_C(U17.10, U17.13, U17.12, R84.1) + NET_C(R84.2, Q6.E) + NET_C(Q6.B, GND) + NET_C(Q6.C, C34.1, C33.1, R65.1) + NET_C(C33.2, C34.2, I_VM12) + NET_C(R65.2, U14.5) + + // + // Sheet 8, bottom middle (Torpedo 1) + // + + NET_C(I_TORPEDO_1, U27.9) + NET_C(U27.8, R125.1, U22.8) + NET_C(R125.2, I_V12) + NET_C(U22.9, R124.1, C55.1) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_TORPEDO_1) + NET_C(D_TORPEDO_1.A, U22.9) + NET_C(D_TORPEDO_1.K, I_V12) +#endif + NET_C(R124.2, I_V12) + NET_C(C55.2, U22.11, U23.2) + NET_C(U22.10, U22.12, U22.13, D4.K) + NET_C(D4.A, R89.1) + NET_C(R89.2, C39.2, D2.K) + NET_C(C39.1, GND) + +#if (HLE_TORPEDO1_VCO) + // + // R2 = 0.99044: HP = (0.000104804*A0) - 0.00000213808 + // R2 = 0.99913: HP = (0.0000155103*A0*A0) + (0.0000595259*A0) + 0.0000223086 + // R2 = 0.99930: HP = (0.00000342725*A0*A0*A0) + (0.000000183693*A0*A0) + (0.000079091*A0) + 0.0000157375 + // R2 = 0.99930: HP = (-0.000000204983*A0*A0*A0*A0) + (0.00000466123*A0*A0*A0) - (0.00000232484*A0*A0) + (0.000081060*A0) + 0.0000152562 + // R2 = 0.99931: HP = (-0.00000210641*A0*A0*A0*A0*A0) + (0.0000157326*A0*A0*A0*A0) - (0.0000399041*A0*A0*A0) + (0.0000541020*A0*A0) + (0.0000495068*A0) + 0.0000213321 + // + VARCLOCK(TORP1CLK, 1, "max(0.000001,min(0.1,(0.00000342725*A0*A0*A0) + (0.000000183693*A0*A0) + (0.000079091*A0) + 0.0000157375))") + NET_C(TORP1CLK.GND, GND) + NET_C(TORP1CLK.VCC, I_V5) + NET_C(TORP1CLK.A0, D2.K) + NET_C(TORP1CLK.Q, T1ENV.A0) + AFUNC(T1ENV, 1, "if(A0>2.5,11.1,-11.1)") + NET_C(T1ENV.Q, U23.1) + NET_C(D2.A, D3.K, R91.1) + NET_C(C46.2, R90.1) + NET_C(R90.2, R91.2, R146.1) + NET_C(GND, U20.3, U20.2) +#else + NET_C(D2.A, D3.K, R91.1, U20.3) + NET_C(U20.2, C46.2, R90.1) + NET_C(R90.2, U20.1, R91.2, R146.1) +#endif + NET_C(D3.A, GND) + NET_C(C46.1, GND) + NET_C(R146.2, U23.1) + + NET_C(U23.12, R129.1) + NET_C(U23.11, R128.1) + NET_C(U23.9, R126.1) + NET_C(U23.6, R127.1) + NET_C(R127.2, R126.2, R128.2, R129.2, C44.1) + + // + // Sheet 8, middle (Torpedo 2) + // + + NET_C(WBN, R36.1) +#if (DISABLE_TORPEDO2_NOISE) + NET_C(R36.2, GND) // noise source +#else + NET_C(R36.2, D6.A) // noise source +#endif + +#if (HLE_TORPEDO2_VCO) + // + // R2 = 0.98868: HP = (0.000142472*A0) - 0.0000121481 + // R2 = 0.99851: HP = (0.0000304790*A0*A0) + (0.000072730*A0) + 0.0000185913 + // R2 = 0.99875: HP = (0.0000104419*A0*A0*A0) - (0.00000603211*A0*A0) + (0.000109865*A0) + 0.0000082421 + // R2 = 0.99875: HP = (0.000000506238*A0*A0*A0*A0) + (0.0000080617*A0*A0*A0) - (0.00000220640*A0*A0) + (0.000107441*A0) + 0.0000087378 + // R2 = 0.99876: HP = (-0.0000095532*A0*A0*A0*A0*A0) + (0.0000569271*A0*A0*A0*A0) - (0.000116200*A0*A0*A0) + (0.000123414*A0*A0) + (0.0000501789*A0) + 0.0000180007 + // + VARCLOCK(TORP2CLK, 1, "max(0.000001,min(0.1,(0.0000104419*A0*A0*A0) - (0.00000603211*A0*A0) + (0.000109865*A0) + 0.0000082421))") + NET_C(TORP2CLK.GND, GND) + NET_C(TORP2CLK.VCC, I_V5) + NET_C(TORP2CLK.A0, D6.K) + NET_C(TORP2CLK.Q, T2ENV.A0) + AFUNC(T2ENV, 1, "if(A0>2.5,11.1,-11.1)") + NET_C(T2ENV.Q, U21.1) + NET_C(D6.A, D7.K, R93.1) + NET_C(C43.2, R94.1) + NET_C(R94.2, R93.2, R92.2) + NET_C(GND, U20.6, U20.5) +#else + NET_C(D6.A, U20.5, D7.K, R93.1) + NET_C(U20.6, C43.2, R94.1) + NET_C(R94.2, U20.7, R93.2, R92.2) +#endif + + NET_C(D7.A, GND) + NET_C(D6.K, C53.2, R147.2) + NET_C(C53.1, GND) + NET_C(R147.1, D5.A) + NET_C(U22.4, U22.1, U22.2, D5.K) + NET_C(C43.1, GND) + NET_C(R92.1, U21.1) + + NET_C(I_TORPEDO_2, U27.11) + NET_C(U27.10, R123.1, U22.6) + NET_C(R123.2, I_V12) + NET_C(U22.5, R121.1, C54.1) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_TORPEDO_2) + NET_C(D_TORPEDO_2.A, U22.5) + NET_C(D_TORPEDO_2.K, I_V12) +#endif + NET_C(R121.2, I_V12) + NET_C(C54.2, U22.3, U21.2) + NET_C(U21.12, R98.1) + NET_C(U21.11, R97.1) + NET_C(U21.9, R95.1) + NET_C(U21.6, R96.1) + NET_C(R98.2, R97.2, R95.2, R96.2, C44.1) + + // + // Sheet 8, middle right (Final sum) + // + + NET_C(PSG, R10.1) + NET_C(ENEMY_SHIP, R7.1) + NET_C(SKITTER, R6.1) + NET_C(BUFFER, R8.1) + NET_C(C44.2, R9.1) + NET_C(R10.2, R7.2, R6.2, R8.2, R9.2) + ALIAS(OUTPUT, R9.2) + + // + // Unconnected inputs + // + + NET_C(GND, U17.1, U17.2, U17.5, U17.6) + NET_C(GND, U26.1, U26.2, U26.4, U26.5, U26.12, U26.13) + NET_C(GND, U27.13, U28.1) + NET_C(GND, U31.11, U31.13) + + // + // Unconnected outputs + // + + +#if (ENABLE_FRONTIERS) +#define RXX 384 + OPTIMIZE_FRONTIER(R10.1, RES_K(10), RXX) + OPTIMIZE_FRONTIER(R7.1, RES_K(220), RXX) + OPTIMIZE_FRONTIER(R6.1, RES_K(220), RXX) + OPTIMIZE_FRONTIER(R8.1, RES_K(10), RXX) +#endif + +NETLIST_END() + +#endif diff --git a/src/mame/audio/nl_elim.h b/src/mame/audio/nl_elim.h new file mode 100644 index 00000000000..a9cd5a2ea4b --- /dev/null +++ b/src/mame/audio/nl_elim.h @@ -0,0 +1,11 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +#ifndef MAME_AUDIO_NL_ELIM_H +#define MAME_AUDIO_NL_ELIM_H + +#pragma once + +NETLIST_EXTERNAL(elim) +NETLIST_EXTERNAL(zektor) + +#endif // MAME_AUDIO_NL_ELIM_H diff --git a/src/mame/audio/nl_segaspeech.cpp b/src/mame/audio/nl_segaspeech.cpp new file mode 100644 index 00000000000..6b52f886df5 --- /dev/null +++ b/src/mame/audio/nl_segaspeech.cpp @@ -0,0 +1,119 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles + +// +// Netlist for Sega Speech board +// +// Derived from the schematics in the Sega G-80 manual. +// +// Known problems/issues: +// +// * Mostly works. Not 100% sure about using a current source +// for input. +// + +#include "netlist/devices/net_lib.h" +#include "nl_segaspeech.h" + +// +// Optimizations +// + + + + +// +// Main netlist +// + +NETLIST_START(segaspeech) + + SOLVER(Solver, 1000) + PARAM(Solver.DYNAMIC_TS, 1) + PARAM(Solver.DYNAMIC_MIN_TIMESTEP, 2e-5) + + TTL_INPUT(I_SP0250, 0) + PARAM(I_SP0250.MODEL, "74XXOC") + NET_C(I_SP0250.GND, GND) + NET_C(I_SP0250.VCC, I_V5) + ALIAS(I_SPEECH, I_SP0250.Q) + + ANALOG_INPUT(I_V5, 5) + ANALOG_INPUT(I_VM5, -5) + + // + // There are two schematic drawings of the speech board + // that show fairly different outputs from the SP0250. + // Both have their problems. + // + // The simpler one is included in the Astro Blaster and + // Space Fury manuals. This is largely correct, except + // that it is believed (not verified) that R20 should be + // 4.7K instead of 470 Ohms. This schematic does not show + // the CD4053 mixer. + // + // The more complex schematic is included in the G-80 + // schematics package, and in the Star Trek and Zektor + // manuals. It has several significant errors (all verified + // from a working PCB): + // + // 1. U8 pins 2 and 3 are swapped + // 2. The connection from R20 to GND should be removed + // (this also disconnected C50 and R21 from GND) + // 3. R13 should be 220k, not 22k + // + // With the fixes above, the output sections of the two + // schematics line up, minus the mixing section, which is + // only shown on the more complex schematic. + // + // The mixing section is trivial, with 3 bits from a control + // port controlling three switches in the CD4053. Bit 3 + // enables/disables speech. Bit 4 enables/disables an + // unconnected source. And bit 5 enables/disables incoming + // external sound. The incoming sound is also routed through + // a pot to enable control over the relative volume. + // + // For purposes of this netlist, and since it runs at high + // speeds, we tap the speech output before it hits the + // CD4053 and manually manage the speech output. + // + // Since we use MAME to manage the mixing, the control bits + // are managed directly there, rather than in this netlist. + // + + // + // This represents the schematic drawing from Astro Blaster + // and Space Fury; there is no control register and it + // works. + // + RES(R17, RES_K(10)) + RES(R18, RES_K(22)) + RES(R19, RES_K(250)) + RES(R20, RES_K(4.7)) // schematic shows 470Ohm, but a real PCB had 4.7k here + RES(R21, RES_K(10)) + + CAP(C9, CAP_U(0.1)) + CAP(C10, CAP_U(0.047)) + CAP(C50, CAP_U(0.003)) + + TL081_DIP(U8) // Op. Amp. + NET_C(U8.7, I_V5) + NET_C(U8.4, I_VM5) + + NET_C(I_SPEECH, R17.1, C9.1) + NET_C(R17.2, I_V5) + NET_C(C9.2, R18.1) + NET_C(R18.2, C10.1, R19.2, U8.3) + NET_C(R19.1, C10.2, GND) + NET_C(R20.1, GND) + NET_C(R20.2, U8.2, R21.2, C50.2) + NET_C(U8.6, R21.1, C50.1) + ALIAS(OUTPUT, U8.6) + + // + // In the more complex case, this would feed into a + // CD4053 for switching. We rely on this being managed + // at the driver level. + // + +NETLIST_END() diff --git a/src/mame/audio/nl_segaspeech.h b/src/mame/audio/nl_segaspeech.h new file mode 100644 index 00000000000..e9779772eba --- /dev/null +++ b/src/mame/audio/nl_segaspeech.h @@ -0,0 +1,10 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +#ifndef MAME_AUDIO_NL_SEGASPEECH_H +#define MAME_AUDIO_NL_SEGASPEECH_H + +#pragma once + +NETLIST_EXTERNAL(segaspeech) + +#endif // MAME_AUDIO_NL_SEGASPEECH_H diff --git a/src/mame/audio/nl_segausb.cpp b/src/mame/audio/nl_segausb.cpp new file mode 100644 index 00000000000..179f9b4a211 --- /dev/null +++ b/src/mame/audio/nl_segausb.cpp @@ -0,0 +1,544 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles + +// +// Netlist for Sega Universal Sound Board +// +// The Sega Universal Sound Board was used by several early G80 +// vector and raster games, notably Star Trek and Tac/Scan, +// among others. It is largely implemented as a MAME device, but +// the analog audio section is implemented here. +// +// Known problems/issues: +// +// * WIP. +// + +#include "netlist/devices/net_lib.h" +#include "nl_segausb.h" + + + +// +// Optimizations +// + +#define USE_AFUNC_MIXING (1) +#define ENABLE_NOISE_FRONTIERS (1) +#define UNDERCLOCK_NOISE_GEN (1) +#define ENABLE_FRONTIERS (0) + + + +// +// Hacks +// + + + +NETLIST_START(segausb) + + SOLVER(Solver, 1000) + PARAM(Solver.DYNAMIC_TS, 1) + PARAM(Solver.DYNAMIC_MIN_TIMESTEP, 2e-5) + + ANALOG_INPUT(I_U12_DAC, 0) // AD7524 + ANALOG_INPUT(I_U13_DAC, 0) // AD7524 + ANALOG_INPUT(I_U14_DAC, 0) // AD7524 + TTL_INPUT(I_U2B_SEL, 0) // 74LS74 + + ANALOG_INPUT(I_U24_DAC, 0) // AD7524 + ANALOG_INPUT(I_U25_DAC, 0) // AD7524 + ANALOG_INPUT(I_U26_DAC, 0) // AD7524 + TTL_INPUT(I_U38B_SEL, 0) // 74LS74 + + ANALOG_INPUT(I_U27_DAC, 0) // AD7524 + ANALOG_INPUT(I_U28_DAC, 0) // AD7524 + ANALOG_INPUT(I_U29_DAC, 0) // AD7524 + TTL_INPUT(I_U2A_SEL, 0) // 74LS74 + + NET_C(GND, I_U2B_SEL.GND, I_U38B_SEL.GND, I_U2A_SEL.GND) + NET_C(I_V5, I_U2B_SEL.VCC, I_U38B_SEL.VCC, I_U2A_SEL.VCC) + + TTL_INPUT(I_U41_OUT0, 0) // 8253 PIT U41 + TTL_INPUT(I_U41_OUT1, 0) + TTL_INPUT(I_U41_OUT2, 0) + + TTL_INPUT(I_U42_OUT0, 0) // 8253 PIT U42 + TTL_INPUT(I_U42_OUT1, 0) + TTL_INPUT(I_U42_OUT2, 0) + + TTL_INPUT(I_U43_OUT0, 0) // 8253 PIT U43 + TTL_INPUT(I_U43_OUT1, 0) + TTL_INPUT(I_U43_OUT2, 0) + + NET_C(GND, I_U41_OUT0.GND, I_U41_OUT1.GND, I_U41_OUT2.GND) + NET_C(I_V5, I_U41_OUT0.VCC, I_U41_OUT1.VCC, I_U41_OUT2.VCC) + + NET_C(GND, I_U42_OUT0.GND, I_U42_OUT1.GND, I_U42_OUT2.GND) + NET_C(I_V5, I_U42_OUT0.VCC, I_U42_OUT1.VCC, I_U42_OUT2.VCC) + + NET_C(GND, I_U43_OUT0.GND, I_U43_OUT1.GND, I_U43_OUT2.GND) + NET_C(I_V5, I_U43_OUT0.VCC, I_U43_OUT1.VCC, I_U43_OUT2.VCC) + + ANALOG_INPUT(I_V5, 5) + ANALOG_INPUT(I_V12, 12) + ANALOG_INPUT(I_VM12, -12) + + RES(R7, RES_K(100)) + RES(R8, RES_K(100)) + RES(R9, RES_K(100)) + RES(R10, RES_K(100)) + RES(R12, RES_K(100)) + RES(R13, RES_K(100)) + RES(R14, RES_K(33)) + RES(R15, RES_K(100)) + RES(R16, RES_K(100)) + RES(R17, RES_K(5.6)) + RES(R18, RES_K(100)) + RES(R19, RES_K(100)) + RES(R20, RES_K(10)) + RES(R21, RES_K(10)) + RES(R22, RES_K(10)) + RES(R23, RES_K(1)) + RES(R24, RES_K(1)) + RES(R25, RES_K(100)) + RES(R26, RES_K(5.6)) + RES(R27, RES_K(10)) + RES(R28, RES_K(100)) + RES(R29, RES_K(100)) + RES(R30, RES_K(1)) + RES(R31, RES_K(100)) + RES(R32, RES_K(100)) + RES(R33, RES_K(1)) + RES(R34, RES_K(100)) + RES(R35, RES_K(100)) + RES(R36, RES_K(100)) + RES(R37, RES_K(33)) + RES(R38, RES_K(100)) + RES(R39, RES_K(100)) + RES(R40, RES_K(10)) + RES(R41, RES_K(10)) + RES(R42, RES_K(100)) + RES(R43, RES_K(100)) + RES(R44, RES_K(33)) + RES(R45, RES_K(100)) + RES(R46, RES_K(100)) + RES(R47, RES_K(5.6)) + RES(R48, RES_K(100)) + RES(R49, RES_K(100)) + RES(R50, RES_K(10)) + RES(R51, RES_K(10)) + RES(R52, RES_K(10)) + RES(R53, RES_K(1)) + RES(R54, RES_K(100)) + RES(R55, RES_K(100)) + RES(R56, RES_K(1)) + RES(R59, RES_K(1)) + RES(R60, RES_K(2.2)) + RES(R61, RES_K(33)) + RES(R62, 270) + RES(R63, RES_K(1)) + RES(R64, RES_K(2.7)) + RES(R65, RES_K(2.7)) + +// CAP(C6, CAP_P(100)) +// CAP(C7, CAP_P(100)) +// CAP(C8, CAP_P(100)) + CAP(C9, CAP_U(0.01)) + CAP(C13, CAP_U(0.01)) + CAP(C14, CAP_U(1)) + CAP(C15, CAP_U(1)) + CAP(C16, CAP_U(0.01)) + CAP(C17, CAP_U(0.01)) +// CAP(C19, CAP_P(100)) +// CAP(C20, CAP_P(100)) +// CAP(C21, CAP_P(100)) +// CAP(C22, CAP_P(100)) +// CAP(C23, CAP_P(100)) +// CAP(C24, CAP_P(100)) + CAP(C25, CAP_U(0.01)) + CAP(C32, CAP_U(0.01)) + CAP(C33, CAP_U(1)) + CAP(C34, CAP_U(1)) + CAP(C35, CAP_U(1)) + CAP(C36, CAP_U(1)) + CAP(C51, CAP_U(0.15)) + CAP(C52, CAP_U(0.1)) + CAP(C53, CAP_U(0.082)) + CAP(C54, CAP_U(1)) + CAP(C55, CAP_U(0.15)) + CAP(C56, CAP_U(0.15)) + +// TL082_DIP(U1) // Op. Amp. +// NET_C(U1.7, I_V12) +// NET_C(U1.4, I_VM12) + +// TTL_74LS74(U2) // Dual D-Type Positive Edge-Triggered Flip-Flop -- not emulated +// NET_C(U2.7, GND) +// NET_C(U2.14, I_V5) + + TL082_DIP(U3) // Op. Amp. + NET_C(U3.8, I_V12) + NET_C(U3.4, I_VM12) + + TL082_DIP(U4) // Op. Amp. + NET_C(U4.8, I_V12) + NET_C(U4.4, I_VM12) + + TL082_DIP(U5) // Op. Amp. + NET_C(U5.8, I_V12) + NET_C(U5.4, I_VM12) + + TL082_DIP(U6) // Op. Amp. + NET_C(U6.8, I_V12) + NET_C(U6.4, I_VM12) + + CD4053_DIP(U7) // 3x analog demuxer + NET_C(U7.16, I_V5) + NET_C(U7.6, GND) // INH + NET_C(U7.7, I_V12) // VEE + NET_C(U7.8, GND) + + CD4053_DIP(U8) // 3x analog demuxer + NET_C(U8.16, I_V5) + NET_C(U8.6, GND) // INH + NET_C(U8.7, I_V12) // VEE + NET_C(U8.8, GND) + + TL082_DIP(U9) // Op. Amp. + NET_C(U9.8, I_V12) + NET_C(U9.4, I_VM12) + +// TTL_74LS139_DIP(U10) // Dual 1-of-4 Decoder -- not emulated + +// TTL_74LS139_DIP(U11) // Dual 1-of-4 Decoder -- not emulated + +// AD7524_DIP(U12) // DAC -- not emulated +// NET_C(U12.3, GND) +// NET_C(U12.14, I_V5) + +// AD7524_DIP(U13) // DAC -- not emulated +// NET_C(U12.3, GND) +// NET_C(U12.14, I_V5) + +// AD7524_DIP(U14) // DAC -- not emulated +// NET_C(U12.3, GND) +// NET_C(U12.14, I_V5) + + CD4053_DIP(U15) // 3x analog demuxer + NET_C(U15.16, I_V5) + NET_C(U15.6, GND) // INH + NET_C(U15.7, I_V12) // VEE + NET_C(U15.8, GND) + + CD4053_DIP(U16) // 3x analog demuxer + NET_C(U16.16, I_V5) + NET_C(U16.6, GND) // INH + NET_C(U16.7, I_V12) // VEE + NET_C(U16.8, GND) + + TL082_DIP(U17) // Op. Amp. + NET_C(U17.8, I_V12) + NET_C(U17.4, I_VM12) + + TL082_DIP(U18) // Op. Amp. + NET_C(U18.8, I_V12) + NET_C(U18.4, I_VM12) + + TL082_DIP(U19) // Op. Amp. + NET_C(U19.8, I_V12) + NET_C(U19.4, I_VM12) + + TL082_DIP(U20) // Op. Amp. + NET_C(U20.8, I_V12) + NET_C(U20.4, I_VM12) + + TL082_DIP(U21) // Op. Amp. + NET_C(U21.8, I_V12) + NET_C(U21.4, I_VM12) + + TL082_DIP(U22) // Op. Amp. + NET_C(U22.8, I_V12) + NET_C(U22.4, I_VM12) + + TL082_DIP(U23) // Op. Amp. + NET_C(U23.8, I_V12) + NET_C(U23.4, I_VM12) + +// AD7524_DIP(U24) // DAC -- not emulated +// NET_C(U24.3, GND) +// NET_C(U24.14, I_V5) + +// AD7524_DIP(U25) // DAC -- not emulated +// NET_C(U25.3, GND) +// NET_C(U25.14, I_V5) + +// AD7524_DIP(U26) // DAC -- not emulated +// NET_C(U26.3, GND) +// NET_C(U26.14, I_V5) + +// AD7524_DIP(U27) // DAC -- not emulated +// NET_C(U27.3, GND) +// NET_C(U27.14, I_V5) + +// AD7524_DIP(U28) // DAC -- not emulated +// NET_C(U28.3, GND) +// NET_C(U28.14, I_V5) + +// AD7524_DIP(U29) // DAC -- not emulated +// NET_C(U29.3, GND) +// NET_C(U29.14, I_V5) + + CD4053_DIP(U30) // 3x analog demuxer + NET_C(U30.16, I_V5) + NET_C(U30.6, GND) // INH + NET_C(U30.7, I_V12) // VEE + NET_C(U30.8, GND) + + CD4053_DIP(U31) // 3x analog demuxer + NET_C(U31.16, I_V5) + NET_C(U31.6, GND) // INH + NET_C(U31.7, I_V12) // VEE + NET_C(U31.8, GND) + +// TTL_74LS74(U38) // Dual D-Type Positive Edge-Triggered Flip-Flop -- not emulated +// NET_C(U38.7, GND) +// NET_C(U38.14, I_V5) + + TL081_DIP(U49) // Op. Amp. + NET_C(U49.7, I_V12) + NET_C(U49.4, I_VM12) + + MM5837_DIP(U60) + NET_C(U60.2, I_VM12) + NET_C(U60.4, I_V12) +#if (UNDERCLOCK_NOISE_GEN) + // officially runs at 48-112kHz, but little noticeable difference + // in exchange for a big performance boost + PARAM(U60.FREQ, 24000) +#endif + + // + // Sheet 6, noise source + // + + NET_C(U60.1, GND) + NET_C(U60.3, R65.1) + NET_C(R65.2, R64.2, R63.2, R62.2, C53.2, C52.1) + NET_C(R64.1, C54.1) + NET_C(C54.2, GND) + NET_C(R63.1, C55.2, C56.2) + NET_C(C55.1, GND) + NET_C(C56.1, GND) + NET_C(R62.1, C51.2) + NET_C(C51.1, GND) + NET_C(C53.1, GND) + NET_C(C52.2, R61.2, U49.3) + NET_C(R61.1, GND) + NET_C(U49.2, R60.1, R59.2) + NET_C(R60.2, U49.6) + NET_C(R59.1, GND) +#if (ENABLE_NOISE_FRONTIERS) + AFUNC(NOISEFUNC, 1, "A0") + NET_C(R60.2, NOISEFUNC.A0) + ALIAS(NOISE, NOISEFUNC.Q) +#else + ALIAS(NOISE, R60.2) +#endif + + // + // Sheet 7, top-left + // + + NET_C(I_U42_OUT0, C14.1) + NET_C(C14.2, R20.2, U3.5) + NET_C(R20.1, GND) + NET_C(U3.6, U3.7) + AFUNC(DAC_U12, 2, "A0*A1") + NET_C(DAC_U12.A0, U3.7) + NET_C(DAC_U12.A1, I_U12_DAC) + NET_C(DAC_U12.Q, R12.2) + + NET_C(I_U42_OUT1, C15.1) + NET_C(C15.2, R21.2, U4.5) + NET_C(R21.1, GND) + NET_C(U4.6, U4.7) + AFUNC(DAC_U13, 2, "A0*A1") + NET_C(DAC_U13.A0, U4.7) + NET_C(DAC_U13.A1, I_U13_DAC) + NET_C(DAC_U13.Q, R13.2) + + NET_C(I_U42_OUT2, U15.10, U15.9) + NET_C(U15.15, U15.14, R32.1) + NET_C(U15.1, R33.1) + NET_C(U15.12, NOISE) + NET_C(U15.13, U16.3) + NET_C(U15.11, U16.11, U16.10, U16.9, I_U2B_SEL) + NET_C(R33.2, C13.2, R32.2, R31.1, U15.4) + NET_C(C13.1, GND) + NET_C(U15.3, R30.1) + NET_C(R31.2, R30.2, C9.2, U6.3) + NET_C(C9.1, GND) + NET_C(U6.2, R22.2, R17.1) + NET_C(R22.1, GND) + NET_C(R17.2, U6.1, U16.14) + + NET_C(U16.13, U16.5, R9.1) + NET_C(U16.12, R19.2) + NET_C(U16.15, NOISE) + NET_C(U16.1, R18.2) + NET_C(R18.1, R19.1, U5.6, R15.2) + NET_C(U5.5, GND) + NET_C(R15.1, U5.7) + AFUNC(DAC_U14, 2, "A0*A1") + PARAM(DAC_U14.THRESH, 1e-5) + NET_C(DAC_U14.A0, U5.7) + NET_C(DAC_U14.A1, I_U14_DAC) + NET_C(DAC_U14.Q, R14.2) + NET_C(R14.1, R13.1, R12.1, U6.6, R16.1) + NET_C(U6.5, GND) + NET_C(U6.7, R16.2, U16.4) + + // + // Sheet 7, bottom-left + // + + NET_C(I_U41_OUT0, C34.1) + NET_C(C34.2, R41.2, U19.5) + NET_C(R41.1, GND) + NET_C(U19.6, U19.7) + AFUNC(DAC_U26, 2, "A0*A1") + NET_C(DAC_U26.A0, U19.7) + NET_C(DAC_U26.A1, I_U26_DAC) + NET_C(DAC_U26.Q, R39.2) + + NET_C(I_U41_OUT1, C33.1) + NET_C(C33.2, R40.2, U18.5) + NET_C(R40.1, GND) + NET_C(U18.6, U18.7) + AFUNC(DAC_U25, 2, "A0*A1") + NET_C(DAC_U25.A0, U18.7) + NET_C(DAC_U25.A1, I_U25_DAC) + NET_C(DAC_U25.Q, R38.2) + + NET_C(I_U41_OUT2, U8.10, U8.9) + NET_C(U8.15, U8.14, R25.1) + NET_C(U8.1, R24.1) + NET_C(U8.12, NOISE) + NET_C(U8.13, U7.3) + NET_C(U8.11, U7.11, U7.10, U7.9, I_U38B_SEL) + NET_C(R24.2, C16.2, R25.2, R29.1, U8.4) + NET_C(C16.1, GND) + NET_C(U8.3, R23.1) + NET_C(R23.2, R29.2, C17.2, U9.3) + NET_C(C17.1, GND) + NET_C(U9.2, R27.2, R26.1) + NET_C(R27.1, GND) + NET_C(R26.2, U9.1, U7.14) + + NET_C(U7.13, U7.5, R8.1) + NET_C(U7.12, R35.2) + NET_C(U7.15, NOISE) + NET_C(U7.1, R34.2) + NET_C(R34.1, R35.1, U17.6, R36.2) + NET_C(U17.5, GND) + NET_C(R36.1, U17.7) + AFUNC(DAC_U24, 2, "A0*A1") + PARAM(DAC_U24.THRESH, 1e-5) + NET_C(DAC_U24.A0, U17.7) + NET_C(DAC_U24.A1, I_U24_DAC) + NET_C(DAC_U24.Q, R37.2) + NET_C(R37.1, R38.1, R39.1, U9.6, R28.1) + NET_C(U9.5, GND) + NET_C(U9.7, R28.2, U7.4) + + // + // Sheet 7, top-right + // + + NET_C(I_U43_OUT0, C35.1) + NET_C(C35.2, R50.2, U20.5) + NET_C(R50.1, GND) + NET_C(U20.6, U20.7) + AFUNC(DAC_U27, 2, "A0*A1") + NET_C(DAC_U27.A0, U20.7) + NET_C(DAC_U27.A1, I_U27_DAC) + NET_C(DAC_U27.Q, R42.2) + + NET_C(I_U43_OUT1, C36.1) + NET_C(C36.2, R51.2, U21.5) + NET_C(R51.1, GND) + NET_C(U21.6, U21.7) + AFUNC(DAC_U28, 2, "A0*A1") + NET_C(DAC_U28.A0, U21.7) + NET_C(DAC_U28.A1, I_U28_DAC) + NET_C(DAC_U28.Q, R43.2) + + NET_C(I_U43_OUT2, U30.10, U30.9) + NET_C(U30.15, U30.14, R55.1) + NET_C(U30.1, R56.1) + NET_C(U30.12, NOISE) + NET_C(U30.13, U31.3) + NET_C(U30.11, U31.11, U31.10, U31.9, I_U2A_SEL) + NET_C(R54.2, C32.2, R55.2, R56.2, U30.4) + NET_C(C32.1, GND) + NET_C(U30.3, R53.1) + NET_C(R53.2, R54.1, C25.2, U23.3) + NET_C(C25.1, GND) + NET_C(U23.2, R52.2, R47.1) + NET_C(R52.1, GND) + NET_C(R47.2, U23.1, U31.14) + + NET_C(U31.13, U31.5, R10.1) + NET_C(U31.12, R49.2) + NET_C(U31.15, NOISE) + NET_C(U31.1, R48.2) + NET_C(R48.1, R49.1, U22.6, R45.2) + NET_C(U22.5, GND) + NET_C(R45.1, U22.7) + AFUNC(DAC_U29, 2, "A0*A1") + PARAM(DAC_U29.THRESH, 1e-5) + NET_C(DAC_U29.A0, U22.7) + NET_C(DAC_U29.A1, I_U29_DAC) + NET_C(DAC_U29.Q, R44.2) + NET_C(R42.1, R43.1, R44.1, U23.6, R46.1) + NET_C(U23.5, GND) + NET_C(U23.7, R46.2, U31.4) + +#if (USE_AFUNC_MIXING) + // This prevents the individual sound nets from combining + // at the mixing resistors. + AFUNC(MIXFUNC, 4, "(A0+A1+A2+A3)/4") + NET_C(MIXFUNC.A0, R7.1) + NET_C(MIXFUNC.A1, R10.2) + NET_C(MIXFUNC.A2, R9.2) + NET_C(MIXFUNC.A3, R8.2) + ALIAS(OUTPUT, MIXFUNC.Q) +#else + NET_C(R7.1, R10.2, R9.2, R8.2) + ALIAS(OUTPUT, R8.2) +#endif + + // + // Unconnected inputs + // + NET_C(GND, R7.2) + NET_C(GND, U3.2, U3.3, U4.2, U4.3, U5.2, U5.3) + NET_C(GND, U17.2, U17.3, U18.2, U18.3, U19.2, U19.3) + NET_C(GND, U20.2, U20.3, U21.2, U21.3, U22.2, U22.3) + + // unconnected inputs to analog switch -- what's the right approach? + NET_C(GND, U8.2, U8.5, U7.2) + NET_C(GND, U15.2, U15.5, U16.2) + NET_C(GND, U30.2, U30.5, U31.2) +/* + OPTIMIZE_FRONTIER(R48.1, RES_M(1), 50) + OPTIMIZE_FRONTIER(R49.1, RES_M(1), 50) + OPTIMIZE_FRONTIER(R18.1, RES_M(1), 50) + OPTIMIZE_FRONTIER(R19.1, RES_M(1), 50) + OPTIMIZE_FRONTIER(R34.1, RES_M(1), 50) + OPTIMIZE_FRONTIER(R35.1, RES_M(1), 50) +*/ + +NETLIST_END() diff --git a/src/mame/audio/nl_segausb.h b/src/mame/audio/nl_segausb.h new file mode 100644 index 00000000000..ec217540801 --- /dev/null +++ b/src/mame/audio/nl_segausb.h @@ -0,0 +1,10 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +#ifndef MAME_AUDIO_NL_SEGAUSB_H +#define MAME_AUDIO_NL_SEGAUSB_H + +#pragma once + +NETLIST_EXTERNAL(segausb) + +#endif // MAME_AUDIO_NL_SEGAUSB_H diff --git a/src/mame/audio/nl_spacfury.cpp b/src/mame/audio/nl_spacfury.cpp new file mode 100644 index 00000000000..797185e9dd0 --- /dev/null +++ b/src/mame/audio/nl_spacfury.cpp @@ -0,0 +1,1105 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles + +// +// Netlist for Space Fury +// +// Derived from the schematics in the Space Fury manual. +// +// Known problems/issues: +// +// * Works pretty well. All sounds trigger. +// +// * Very challenging to get running at speed. We have to use +// the "slow" CA3080 model for the star spin sound, which +// creates a giant net that dominates time and is sensitive +// to cheats to reduce timesteps, etc +// + +#include "netlist/devices/net_lib.h" +#include "nl_spacfury.h" + + +// +// Optimizations +// + +#define HLE_SHOOT_VCO (1) +#define SPLIT_SHARED_OSCILLATORS (1) +#define ENABLE_SPIN_VCO_FRONTIER (1) +#define ENABLE_NOISE_FRONTIERS (1) +#define ENABLE_FRONTIERS (1) +#define UNDERCLOCK_NOISE_GEN (1 && ENABLE_NOISE_FRONTIERS) + + + + +// +// Hacks +// + +#define ADD_CLIPPING_DIODES (1) + + + +// +// Local models +// + +#define D_1N914(name) DIODE(name, "1N914") +#define D_1N4002(name) DIODE(name, "D(IS=65.4p RS=42.2m BV=100 IBV=5.00u CJO=14.8p M=0.333 N=1.36 TT=2.88u)") + +// SPICE model taken from https://www.onsemi.com/support/design-resources/models?rpn=2N4401 +#define Q_2N4401(name) QBJT_EB(name, "NPN(Is=26.03f Xti=3 Eg=1.11 Vaf=90.7 Bf=4.292K Ne=1.244 Ise=26.03f Ikf=.2061 Xtb=1.5 Br=1.01 Nc=2 Isc=0 Ikr=0 Rc=.5 Cjc=11.01p Mjc=.3763 Vjc=.75 Fc=.5 Cje=24.07p Mje=.3641 Vje=.75 Tr=233.7n Tf=466.5p Itf=0 Vtf=0 Xtf=0 Rb=10)") + +// SPICE model taken from https://www.onsemi.com/support/design-resources/models?rpn=2N4403 +#define Q_2N4403(name) QBJT_EB(name, "PNP(Is=650.6E-18 Xti=3 Eg=1.11 Vaf=115.7 Bf=216.2 Ne=1.829 Ise=58.72f Ikf=1.079 Xtb=1.5 Br=3.578 Nc=2 Isc=0 Ikr=0 Rc=.715 Cjc=14.76p Mjc=.5383 Vjc=.75 Fc=.5 Cje=19.82p Mje=.3357 Vje=.75 Tr=111.6n Tf=603.7p Itf=.65 Vtf=5 Xtf=1.7 Rb=10)") + +// JFET transistors not supported, but this should do the trick +//#define Q_2N4093(name) MOSFET(name, "NMOS(VTO=-1.0)") +#define Q_2N4093(name) MOSFET(name, "NMOS(VTO=-1 CAPMOD=0)") + +#define LM555_DIP NE555_DIP +#define LM566_DIP NE566_DIP + +#define TTL_74LS00_DIP TTL_7400_DIP + + + +// +// DIP mappings use the submodels below for CA3080 +// +#define CA3080_FAST_DIP(name) SUBMODEL(_CA3080_FAST_DIP, name) +#define CA3080_SLOW_DIP(name) SUBMODEL(_CA3080_SLOW_DIP, name) +static NETLIST_START(_CA3080_FAST_DIP) + ALIAS(2, F.A0) // - + ALIAS(3, F.A1) // + + ALIAS(4, F.A2) // V- + ALIAS(5, RIABC.1) // IB + ALIAS(6, VO.OP) // FIXME + ALIAS(7, F.A4) // V+ + + RES(RI, 26000) + NET_C(RI.1, F.A0) + NET_C(RI.2, F.A1) + // Delivers I0 + AFUNC(F, 5, "max(-0.5e-3, min(0.5e-3, 19.2 * (A3 - A2) * A0))") + RES(RIABC, 1) + NET_C(RIABC.2, F.A2) + NET_C(RIABC.1, F.A3) // IB + VCCS(VO, 1) + ANALOG_INPUT(XGND, 0) + NET_C(XGND, VO.IN, VO.ON) // FIXME: assume symmetric supply + NET_C(F.Q, VO.IP) +NETLIST_END() + +static NETLIST_START(_CA3080_SLOW_DIP) +// +// These items are common to several models +// +#define CA3080_D(name) DIODE(name, "D(IS=2p RS=5 BV=40 CJO=3p TT=6n)") +#define CA3080_NPN(name) QBJT_EB(name, "NPN(IS=21.48f XTI=3 EG=1.11 VAF=80 BF=550 ISE=50f NE=1.5 IKF=10m XTB=1.5 BR=.1 ISC=10f NC=2 IKR=3m RC=10 CJC=800f MJC=.3333 VJC=.75 FC=.5 CJE=1.3p MJE=.3333 VJE=.75 TR=30n TF=400P ITF=30m XTF=1 VTF=10 CJS=5.8P MJS=.3333 VJS=.75)") +#define CA3080_PNP(name) QBJT_EB(name, "PNP(IS=50f XTI=3 EG=1.11 VAF=80 BF=100 ISE=130f NE=1.5 IKF=1m XTB=1.5 BR=1 ISC=0 NC=2 IKR=0 RC=0 CJC=4p MJC=.3333 VJC=.75 FC=.5 CJE=1.4p MJE=.3333 VJE=.75 TR=500n TF=23n ITF=.1 XTF=1 VTF=10 CJS=5.5P MJS=.3333 VJS=.75)") + + CA3080_D(D1) + CA3080_D(D2) + CA3080_NPN(Q1) + CA3080_PNP(Q2) + CA3080_PNP(Q3) + CA3080_NPN(Q4) + CA3080_NPN(Q5) + CA3080_PNP(Q6) + CA3080_PNP(Q7) + CA3080_PNP(Q8) + CA3080_PNP(Q9) + CA3080_NPN(Q10) + CA3080_NPN(Q11) + CA3080_NPN(Q12) + CA3080_NPN(Q13) + CA3080_PNP(Q14) + CA3080_PNP(Q15) + + ALIAS(2, Q10.B) // N1 + ALIAS(3, Q5.B) // N28 + ALIAS(4, Q1.E) // N13 + ALIAS(5, Q1.B) // N11 + ALIAS(6, Q6.C) // N30 + ALIAS(7, Q8.E) // N8 + NET_C(Q8.E, Q9.E, Q14.E, Q15.E) // N8 + NET_C(Q1.B, Q1.C, Q4.B) // N11 + NET_C(Q1.E, Q4.E, Q11.E, Q12.E) // N13 + NET_C(Q6.C, Q7.C, Q13.C) // N30 + NET_C(Q3.B, Q10.C, Q14.C, D1.A) // N1N13 + NET_C(Q2.E, Q14.B, Q15.C, Q15.B) // N1N15 + NET_C(Q2.B, Q3.E, D1.K) // N1N17 + NET_C(Q2.C, Q3.C, Q11.C, Q13.B) // N1N22 + NET_C(Q5.C, Q6.B, Q9.C, D2.A) // N1N32 + NET_C(Q6.E, Q7.B, D2.K) // N1N34 + NET_C(Q7.E, Q8.C, Q8.B, Q9.B) // N1N36 + NET_C(Q4.C, Q5.E, Q10.E) // N1N52 + NET_C(Q11.B, Q12.C, Q12.B, Q13.E) // N1N44 +NETLIST_END() + + + +// +// Main netlist +// + +NETLIST_START(spacfury) + + SOLVER(Solver, 1000) + PARAM(Solver.DYNAMIC_TS, 1) + PARAM(Solver.DYNAMIC_MIN_TIMESTEP, 4e-5) + // subtle but noticeable effect where the spin "whoosh" is missing + // some upper harmonics at 4e-5; however, this is the most demanding + // solver in the system and it keeps jumping around as I tweak things +// PARAM(Solver.Solver_21.DYNAMIC_MIN_TIMESTEP, 2e-5) + + // Overwrite model - the default model uses minimum datasheet + // specifications for 5V. These are for 10V and thus closer to the + // 12V used in this circuit. + NET_MODEL("CD4XXX FAMILY(TYPE=CMOS IVL=0.3 IVH=0.7 OVL=0.05 OVH=0.05 ORL=384 ORH=384)") + + LOCAL_SOURCE(_CA3080_FAST_DIP) + LOCAL_SOURCE(_CA3080_SLOW_DIP) + + TTL_INPUT(I_LO_D0, 0) + ALIAS(I_CRAFTS_SCALE, I_LO_D0) + TTL_INPUT(I_LO_D1, 0) + ALIAS(I_MOVING, I_LO_D1) + TTL_INPUT(I_LO_D2, 0) + ALIAS(I_THRUST, I_LO_D2) + TTL_INPUT(I_LO_D6, 0) + ALIAS(I_STAR_SPIN, I_LO_D6) + TTL_INPUT(I_LO_D7, 0) + ALIAS(I_PARTIAL_WARSHIP, I_LO_D7) + + NET_C(GND, I_LO_D0.GND, I_LO_D1.GND, I_LO_D2.GND, I_LO_D6.GND, I_LO_D7.GND) + NET_C(I_V5, I_LO_D0.VCC, I_LO_D1.VCC, I_LO_D2.VCC, I_LO_D6.VCC, I_LO_D7.VCC) + + TTL_INPUT(I_HI_D0, 0) + ALIAS(I_CRAFTS_JOINING, I_HI_D0) + TTL_INPUT(I_HI_D1, 0) + ALIAS(I_SHOOT, I_HI_D1) + TTL_INPUT(I_HI_D2, 0) + ALIAS(I_FIREBALL, I_HI_D2) + TTL_INPUT(I_HI_D3, 0) + ALIAS(I_SMALL_EXPL, I_HI_D3) + TTL_INPUT(I_HI_D4, 0) + ALIAS(I_LARGE_EXPL, I_HI_D4) + TTL_INPUT(I_HI_D5, 0) + ALIAS(I_DOCKING_BANG, I_HI_D5) + + NET_C(GND, I_HI_D0.GND, I_HI_D1.GND, I_HI_D2.GND, I_HI_D3.GND, I_HI_D4.GND, I_HI_D5.GND) + NET_C(I_V5, I_HI_D0.VCC, I_HI_D1.VCC, I_HI_D2.VCC, I_HI_D3.VCC, I_HI_D4.VCC, I_HI_D5.VCC) + + RES(R_PSG_1, 1000) + RES(R_PSG_2, 1000) + RES(R_PSG_3, 1000) + NET_C(I_V5, R_PSG_1.1, R_PSG_2.1, R_PSG_3.1) + + ANALOG_INPUT(I_V5, 5) + ANALOG_INPUT(I_V12, 12) + ANALOG_INPUT(I_VM12, -12) + +// RES(R1, RES_K(2.2)) -- part of final amp (not emulated) +// RES(R2, RES_K(10)) -- part of final amp (not emulated) +// RES(R4, RES_K(330)) -- part of final amp (not emulated) +// RES(R5, RES_M(1)) -- part of final amp (not emulated) + RES(R6, RES_K(680)) + RES(R7, RES_K(680)) + RES(R8, RES_K(10)) + RES(R9, RES_K(100)) + RES(R10, 680) + RES(R11, RES_K(47)) + RES(R12, RES_K(47)) + RES(R13, RES_K(47)) + RES(R14, RES_M(1)) +// RES(R15, RES_K(100)) -- part of final amp (not emulated) + RES(R18, RES_K(10)) + RES(R19, RES_K(470)) + RES(R20, RES_K(22)) + RES(R21, RES_K(47)) + RES(R22, RES_K(3.9)) + RES(R23, RES_K(82)) + RES(R24, RES_K(4.7)) + RES(R25, RES_K(47)) + RES(R26, 100) + RES(R27, RES_K(100)) + RES(R28, RES_K(100)) + RES(R29, RES_K(2.2)) + RES(R30, RES_K(4.7)) + RES(R31, RES_K(100)) + RES(R32, RES_K(100)) + RES(R33, RES_M(1)) + RES(R34, RES_K(10)) + RES(R35, RES_K(680)) + RES(R36, 680) + RES(R37, RES_K(47)) + RES(R38, RES_K(47)) + RES(R39, RES_K(2.2)) + RES(R40, RES_K(47)) + RES(R41, RES_K(47)) + RES(R42, RES_K(100)) + RES(R43, RES_K(2.2)) + RES(R44, RES_K(100)) + RES(R45, RES_M(2.2)) + RES(R46, RES_K(100)) + RES(R47, RES_K(9.1)) + RES(R48, 100) + RES(R49, RES_K(4.7)) + RES(R50, RES_K(47)) + RES(R51, RES_K(10)) + RES(R52, RES_K(47)) + RES(R53, RES_K(47)) + RES(R54, RES_K(100)) + RES(R55, RES_K(47)) + RES(R56, RES_K(100)) + RES(R57, RES_K(47)) + RES(R58, RES_K(100)) + RES(R59, RES_K(10)) + RES(R60, 100) + RES(R61, RES_K(9.1)) + RES(R62, RES_K(100)) + RES(R63, RES_K(47)) + RES(R64, RES_K(10)) + RES(R65, RES_K(470)) + RES(R66, RES_K(10)) + RES(R67, 100) + RES(R68, RES_K(10)) + RES(R69, RES_K(22)) + RES(R70, RES_K(220)) + RES(R71, RES_K(2.2)) + RES(R72, RES_K(22)) + RES(R73, RES_K(330)) + RES(R74, RES_K(330)) + RES(R75, RES_K(100)) + RES(R76, RES_K(10)) + RES(R77, RES_K(10)) + RES(R78, RES_K(22)) + RES(R79, RES_K(10)) + RES(R80, RES_K(100)) + RES(R81, RES_K(82)) + RES(R82, RES_K(39)) + RES(R83, RES_K(10)) + RES(R84, RES_K(22)) + RES(R85, RES_K(47)) + RES(R86, RES_K(47)) + RES(R87, RES_K(47)) + RES(R88, RES_K(100)) + RES(R89, RES_K(100)) + RES(R90, RES_K(10)) + RES(R91, RES_M(1)) + RES(R92, RES_K(470)) + RES(R93, RES_K(470)) + RES(R94, RES_K(220)) + RES(R95, RES_K(22)) + RES(R96, RES_K(100)) + RES(R97, RES_K(100)) + RES(R98, RES_K(10)) + RES(R99, RES_K(3.9)) + RES(R100, RES_K(47)) + RES(R101, RES_K(2.2)) + RES(R102, RES_M(1)) + RES(R103, RES_K(10)) + RES(R104, RES_K(10)) + RES(R105, RES_K(100)) + RES(R106, RES_K(10)) + RES(R107, RES_K(220)) + RES(R108, RES_K(220)) + RES(R109, RES_K(10)) + RES(R110, 100) + RES(R111, RES_K(10)) + RES(R112, RES_K(470)) + RES(R113, RES_K(100)) + RES(R114, RES_K(100)) + RES(R115, RES_K(100)) + RES(R116, RES_K(100)) + RES(R117, RES_K(100)) + RES(R118, RES_K(100)) + RES(R119, RES_M(2.2)) + RES(R120, RES_K(100)) + RES(R121, RES_K(100)) + RES(R122, RES_M(2.2)) + RES(R123, RES_K(22)) + RES(R124, RES_M(3.9)) + RES(R125, RES_K(100)) + RES(R126, RES_K(100)) + RES(R127, RES_M(2.2)) + RES(R128, RES_K(22)) + RES(R129, RES_K(22)) + RES(R130, RES_K(22)) + RES(R131, RES_K(10)) + RES(R132, RES_K(2.2)) + RES(R133, RES_K(2.2)) + RES(R135, RES_K(10)) + RES(R136, RES_K(10)) + RES(R134, RES_K(10)) + RES(R137, RES_K(2.2)) + RES(R138, RES_K(150)) + RES(R139, RES_K(150)) + RES(R140, RES_K(2.2)) + RES(R141, RES_K(220)) + RES(R142, RES_K(100)) + RES(R143, RES_K(10)) + RES(R144, RES_K(220)) + RES(R146, RES_K(220)) + RES(R147, 680) + RES(R148, RES_K(10)) + RES(R149, RES_K(220)) + +// CAP(C1, CAP_U(4.7)) -- part of final amp (not emulated) +// CAP(C2, CAP_U(0.05)) +// CAP(C3, CAP_U(0.05)) +// CAP(C4, CAP_U(10)) -- part of final amp (not emulated) + CAP(C5, CAP_U(0.01)) + CAP(C6, CAP_U(0.01)) + CAP(C7, CAP_U(4.7)) + CAP(C8, CAP_U(0.047)) +// CAP(C9, CAP_U(0.05)) +// CAP(C10, CAP_U(0.05)) + CAP(C11, CAP_U(0.01)) + CAP(C12, CAP_U(0.01)) + CAP(C13, CAP_U(0.01)) + CAP(C14, CAP_U(0.01)) + CAP(C15, CAP_U(0.01)) +// CAP(C16, CAP_U(0.05)) +// CAP(C17, CAP_U(0.05)) + CAP(C18, CAP_U(0.01)) + CAP(C19, CAP_U(0.047)) + CAP(C20, CAP_U(0.01)) + CAP(C21, CAP_U(4.7)) + CAP(C22, CAP_U(0.05)) + CAP(C23, CAP_U(0.05)) + CAP(C24, CAP_U(10)) +// CAP(C25, CAP_U(0.05)) + CAP(C26, CAP_U(0.0033)) +// CAP(C27, CAP_U(0.05)) + CAP(C28, CAP_U(0.1)) + CAP(C29, CAP_U(0.001)) + CAP(C30, CAP_U(0.0022)) + CAP(C31, CAP_U(0.022)) + CAP(C32, CAP_U(10)) + CAP(C33, CAP_U(0.1)) + CAP(C34, CAP_U(0.1)) + CAP(C35, CAP_U(0.0033)) + CAP(C36, CAP_U(4.7)) + CAP(C37, CAP_U(0.0022)) +// CAP(C38, CAP_U(0.05)) +// CAP(C39, CAP_U(0.05)) + CAP(C40, CAP_U(0.047)) + CAP(C41, CAP_U(0.0047)) + CAP(C42, CAP_U(0.01)) + CAP(C43, CAP_U(0.01)) + CAP(C44, CAP_U(4.7)) +// CAP(C45, CAP_U(0.05)) +// CAP(C46, CAP_U(0.05)) + CAP(C47, CAP_U(0.047)) + CAP(C48, CAP_U(4.7)) + CAP(C49, CAP_U(0.1)) + CAP(C50, CAP_U(4.7)) + CAP(C51, CAP_U(0.1)) + CAP(C52, CAP_U(0.022)) + CAP(C53, CAP_U(10)) + CAP(C54, CAP_U(0.1)) + CAP(C55, CAP_U(100)) +// CAP(C56, CAP_U(0.05)) + CAP(C57, CAP_U(4.7)) +// CAP(C58, CAP_U(0.05)) + CAP(C59, CAP_U(0.01)) + CAP(C60, CAP_U(0.01)) + CAP(C61, CAP_U(0.047)) + CAP(C62, CAP_U(0.047)) + CAP(C63, CAP_U(0.05)) + CAP(C64, CAP_U(0.05)) +// CAP(C65, CAP_U(0.05)) + CAP(C66, CAP_U(0.033)) +// CAP(C67, CAP_U(0.05)) + CAP(C68, CAP_U(0.1)) + CAP(C69, CAP_U(0.1)) + CAP(C70, CAP_U(0.1)) + CAP(C71, CAP_U(0.047)) + CAP(C72, CAP_P(100)) + CAP(C73, CAP_U(0.1)) +// CAP(C75, CAP_U(10)) +// CAP(C76, CAP_U(10)) +// CAP(C77, CAP_U(0.1)) +// CAP(C78, CAP_U(0.1)) +// CAP(C79, CAP_U(0.1)) +// CAP(C80, CAP_U(0.1)) + + D_1N914(D1) + D_1N914(D2) + D_1N914(D3) + D_1N914(D4) + D_1N914(D5) + D_1N914(D6) + D_1N914(D7) + D_1N914(D8) + D_1N914(D9) + D_1N914(D10) + D_1N914(D11) + D_1N914(D12) + D_1N914(D13) + D_1N914(D14) + D_1N914(D15) + D_1N914(D16) + +// Q_2N4093(Q1) -- part of final amp (not emulated) + Q_2N4093(Q2) + Q_2N4093(Q3) + Q_2N4403(Q4) + Q_2N4403(Q5) + Q_2N4403(Q6) + Q_2N4093(Q7) + Q_2N4093(Q8) + Q_2N4403(Q9) + Q_2N4403(Q10) + Q_2N4401(Q11) + Q_2N4401(Q12) + Q_2N4403(Q13) + +// TL082_DIP(U1) // Op. Amp. -- part of final amp (not emulated) +// NET_C(U1.8, I_V12) +// NET_C(U1.4, I_VM12) + + TL082_DIP(U2) // Op. Amp. + NET_C(U2.8, I_V12) + NET_C(U2.4, I_VM12) + + CD4069_DIP(U3) // Hex Inverter + NET_C(U3.7, GND) + NET_C(U3.14, I_V12) + + TL082_DIP(U4) // Op. Amp. + NET_C(U4.8, I_V12) + NET_C(U4.4, I_VM12) + + TL082_DIP(U5) // Op. Amp. + NET_C(U5.8, I_V12) + NET_C(U5.4, I_VM12) + + CD4011_DIP(U6) // Quad 2-Input NAND Gates + NET_C(U6.7, GND) + NET_C(U6.14, I_V12) + + CA3080_SLOW_DIP(U7) // Op. Amp. + NET_C(U7.4, I_VM12) + NET_C(U7.7, I_V12) + + TL082_DIP(U8) // Op. Amp. + NET_C(U8.8, I_V12) + NET_C(U8.4, I_VM12) + + MM5837_DIP(U9) // Noise Generator +#if (UNDERCLOCK_NOISE_GEN) + // officially runs at 48-112kHz, but little noticeable difference + // in exchange for a big performance boost + PARAM(U9.FREQ, 24000) + + // the NOISE_B signal can run even lower, so use a second + // MM5837 instance at a lower frequency to drive it; this + // second instance doesn't really exist, it just allows us + // a bit more performance + MM5837_DIP(U9B) // Noise Generator + PARAM(U9B.FREQ, 12000) +#endif + + TL082_DIP(U10) // Op. Amp. + NET_C(U10.8, I_V12) + NET_C(U10.4, I_VM12) + + CD4024_DIP(U11) // 7-Stage Ripple Binary Counter + NET_C(U11.7, GND) + NET_C(U11.14, I_V12) + + CA3080_SLOW_DIP(U12) // Op. Amp. + NET_C(U12.4, I_VM12) + NET_C(U12.7, I_V12) + + TL082_DIP(U13) // Op. Amp. + NET_C(U13.8, I_V12) + NET_C(U13.4, I_VM12) + + TL082_DIP(U14) // Op. Amp. + NET_C(U14.8, I_V12) + NET_C(U14.4, I_VM12) + + TL082_DIP(U15) // Op. Amp. + NET_C(U15.8, I_V12) + NET_C(U15.4, I_VM12) + + TL082_DIP(U16) // Op. Amp. + NET_C(U16.8, I_V12) + NET_C(U16.4, I_VM12) + + LM555_DIP(U17) + + TL082_DIP(U18) // Op. Amp. + NET_C(U18.8, I_V12) + NET_C(U18.4, I_VM12) + + CD4069_DIP(U19) // Hex Inverter + NET_C(U19.7, GND) + NET_C(U19.14, I_V12) + + CD4011_DIP(U20) // Quad 2-Input NAND Gates + NET_C(U20.7, GND) + NET_C(U20.14, I_V12) + + TL082_DIP(U21) // Op. Amp. + NET_C(U21.8, I_V12) + NET_C(U21.4, I_VM12) + + CD4011_DIP(U22) // Quad 2-Input NAND Gates + NET_C(U22.7, GND) + NET_C(U22.14, I_V12) + + TL082_DIP(U23) // Op. Amp. + NET_C(U23.8, I_V12) + NET_C(U23.4, I_VM12) + +// TTL_74LS08_DIP(U25) // Quad 2-Input AND Gates +// NET_C(U25.7, GND) +// NET_C(U25.14, I_V5) + + TTL_7407_DIP(U26) // Hex Buffers with High Votage Open-Collector Outputs + NET_C(U26.7, GND) + NET_C(U26.14, I_V5) + +// TTL_74LS10_DIP(U28) // Triple 3-Input NAND Gate +// NET_C(U28.7, GND) +// NET_C(U28.14, I_V5) + +// TTL_74LS14_DIP(U30) +// NET_C(U30.7, GND) +// NET_C(U30.14, I_V5) + + TTL_7406_DIP(U31) // Hex inverter -- currently using a clone of 7416, no open collector behavior + NET_C(U31.7, GND) + NET_C(U31.14, I_V5) + +// TTL_74LS374_DIP(U32) // Octal D-Type Transparent Latches And Edge-Triggered Flip-Flop +// NET_C(U32.10, GND) +// NET_C(U32.20, I_V5) +// +// TTL_74LS374_DIP(U33) // Octal D-Type Transparent Latches And Edge-Triggered Flip-Flop +// NET_C(U33.10, GND) +// NET_C(U33.20, I_V5) + +// TTL_74LS30_DIP(U34) // 8-Input NAND Gate +// NET_C(U34.7, GND) +// NET_C(U34.14, I_V5) + +// TTL_74LS14_DIP(U35) +// NET_C(U35.7, GND) +// NET_C(U35.14, I_V5) + + // + // Sheet 7, top (Moving) + // + + // Pairs of CD4069 are used together with resistors and capacitors + // to create oscillators. The formula for the frequency is + // 1/1.39*R*C. The following are simulated here: + // + // #1: R35=680K, C12=0.01uF, Freq=105.80Hz + // #2: R6=680K, C11=0.01uF, Freq=105.80Hz + // #3: R7=680K, C13=0.01uF, Freq=105.80Hz + // + // Since these are all the same and components have tolerances, + // we use tweaked frequencies for #2 and #3 + + CLOCK(O1CLK, 105.8) + NET_C(O1CLK.GND, GND) + NET_C(O1CLK.VCC, I_V12) + NET_C(O1CLK.Q, R56.1) + NET_C(GND, R35.1, R35.2, C12.1, C12.2, U3.5, U3.9) + + CLOCK(O2CLK, 105.7) + NET_C(O2CLK.GND, GND) + NET_C(O2CLK.VCC, I_V12) + NET_C(O2CLK.Q, R54.1) + NET_C(GND, R6.1, R6.2, C11.1, C11.2, U3.1, U3.3) + + CLOCK(O3CLK, 105.9) + NET_C(O3CLK.GND, GND) + NET_C(O3CLK.VCC, I_V12) + NET_C(O3CLK.Q, R58.1) + NET_C(GND, R7.1, R7.2, C13.1, C13.2, U3.13, U3.11) + + NET_C(I_MOVING, U31.13) + NET_C(U31.12, R56.2, R54.2, R58.2, R59.1) + NET_C(R59.2, C15.2, R36.2, C14.1) + NET_C(C15.1, U4.2, R28.2) + NET_C(R28.1, U4.1, C14.2, R8.1) + NET_C(U4.3, GND, R36.1) + + NET_C(R8.2, C5.2, R10.2, C6.1) + NET_C(C5.1, U4.6, R9.2) + NET_C(R9.1, U4.7, C6.2) + ALIAS(MOVING_SUM, C6.2) + NET_C(U4.5, GND, R10.1) + + // + // Sheet 7, 2nd from top (Star Spin/Partial Warship) + // + +#if (SPLIT_SHARED_OSCILLATORS) + + // In the schematics, the below oscillators are shared; however, + // connecting them also brings the two nets together, so we just + // duplicate the clocks here to help keep the very expensive net + // below more isolated. + + CLOCK(O1CLKA, 105.8) + NET_C(O1CLKA.GND, GND) + NET_C(O1CLKA.VCC, I_V12) + NET_C(O1CLKA.Q, R55.2) + + CLOCK(O2CLKB, 105.7) + NET_C(O2CLKB.GND, GND) + NET_C(O2CLKB.VCC, I_V12) + NET_C(O2CLKB.Q, R53.2) + + CLOCK(O3CLKC, 105.9) + NET_C(O3CLKC.GND, GND) + NET_C(O3CLKC.VCC, I_V12) + NET_C(O3CLKC.Q, R57.2) + +#else + + NET_C(O1CLK.Q, R55.2) + NET_C(O2CLK.Q, R53.2) + NET_C(O3CLK.Q, R57.2) + +#endif + + NET_C(I_STAR_SPIN, U31.3, U31.1) + NET_C(U31.2, R148.1) + NET_C(C32.1, I_V12, R62.1, R72.1) + + NET_C(R148.2, C32.2, R62.2, U13.5) + NET_C(R72.2, R90.2, R91.1, D6.A) + NET_C(R90.1, GND) + NET_C(U13.6, U13.7, D5.A) + NET_C(D5.K, D6.K, R94.1, R92.1) + NET_C(R94.2, R108.2, U16.5) + NET_C(R108.1, GND) + NET_C(R92.2, U16.6, R107.1, C47.1) + +#if (ENABLE_SPIN_VCO_FRONTIER) + // split the envelope VCO from the rest of the circuit + // to break up a giant net into 2 smaller ones + NET_C(C47.2, U16.7, U16.2, TESTFUNC.A0) + AFUNC(TESTFUNC, 1, "A0") + NET_C(TESTFUNC.Q, R89.1, R88.1) +#else + NET_C(C47.2, U16.7, U16.2, R89.1, R88.1) +#endif + + NET_C(U16.3, R93.1, R91.2) + NET_C(R93.2, U16.1, D10.A) + NET_C(D10.K, R106.2) + NET_C(R106.1, Q11.B, R133.2) + NET_C(R133.1, GND) + NET_C(Q11.E, GND) + NET_C(Q11.C, R107.2) + + NET_C(I_PARTIAL_WARSHIP, U31.5) + NET_C(U31.6, R57.1, R53.1, R55.1, C33.1) + NET_C(C33.2, R63.1) + NET_C(R63.2, R34.1, R51.2, R64.2, U8.6) + NET_C(R34.2, U8.1, C26.2) + ALIAS(STAR_SPIN_PARTIAL_WARSHIP_SUM, U8.1) + NET_C(C26.1, U8.2, U7.6) + NET_C(U8.3, GND) + NET_C(U31.4, C34.1) + NET_C(NOISE_A, C34.1) + NET_C(C34.2, R64.1) + NET_C(U8.5, R49.2, R50.1) + NET_C(R49.1, GND) + NET_C(U8.7, R61.1, R51.1) + NET_C(R61.2, R60.2, U12.3) + NET_C(R60.1, GND) + NET_C(U12.2, GND) + NET_C(U12.5, R88.2) + NET_C(R89.2, U7.5) + NET_C(U7.3, R48.2, R47.2) + NET_C(U7.2, GND) + NET_C(R48.1, GND) + NET_C(R47.1, C35.2, U13.1, R50.2) + NET_C(U13.3, GND) + NET_C(U13.2, C35.1, U12.6) + + // + // Sheet 7, middle (Fireball) + // + + NET_C(I_FIREBALL, U26.11) + NET_C(U26.10, U22.12, R125.2) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_FIREBALL) + NET_C(D_FIREBALL.A, U22.12) + NET_C(D_FIREBALL.K, I_V12) +#endif + NET_C(R125.1, I_V12, R124.1) + NET_C(R124.2, U22.13, C70.1) + NET_C(C70.2, U22.10) + NET_C(U22.8, U22.9, U22.11) + NET_C(U22.11, R123.1) + NET_C(R123.2, D12.A) + NET_C(D12.K, R86.1, R87.1, C50.1) + NET_C(C50.2, GND) + NET_C(R86.2, U14.6, R85.1) + NET_C(R87.2, U14.5, D8.K, Q8.D) + NET_C(NOISE_B, Q8.G) + NET_C(Q8.S, GND) + NET_C(D8.A, GND) + NET_C(R85.2, R71.1, U14.7) + NET_C(R71.2, R70.1, C31.2) + NET_C(C31.1, GND) + NET_C(R70.2, C30.1, R149.1) + NET_C(C30.2, U14.2, U14.1, C44.1) + NET_C(R149.2, C29.2, U14.3) + NET_C(C29.1, GND) + ALIAS(FIREBALL_SUM, C44.2) + + // + // Sheet 7, 2nd from bottom (Explosions) + // + + NET_C(U9.1, GND) + NET_C(U9.4, I_V12) + NET_C(U9.2, I_VM12) + NET_C(C28.1, R14.2) + +#if (UNDERCLOCK_NOISE_GEN) + + NET_C(U9B.1, GND) + NET_C(U9B.4, I_V12) + NET_C(U9B.2, I_VM12) + NET_C(U9B.3, C28.2) + // Yes - AFuncs are frontiers as well + AFUNC(NOISE_B_FUNC, 1, "A0") + NET_C(R14.2, NOISE_B_FUNC.A0) + ALIAS(NOISE_B, NOISE_B_FUNC.Q) + NET_C(R14.1, GND) + // Yes - AFuncs are frontiers as well + AFUNC(NOISE_A_FUNC, 1, "A0") + NET_C(U9.3, NOISE_A_FUNC.A0) + NET_C(R52.1, NOISE_A_FUNC.Q) + ALIAS(NOISE_A, R52.2) + +#else + +#if (ENABLE_NOISE_FRONTIERS) + // Yes - AFuncs are frontiers as well + AFUNC(NOISE_B_FUNC, 1, "A0") + NET_C(R14.2, NOISE_B_FUNC.A0) + ALIAS(NOISE_B, NOISE_B_FUNC.Q) +#else + ALIAS(NOISE_B, R14.2) +#endif + NET_C(R14.1, GND) +#if (ENABLE_NOISE_FRONTIERS) + // Yes - AFuncs are frontiers as well + AFUNC(NOISE_A_FUNC, 1, "A0") + NET_C(U9.3, NOISE_A_FUNC.A0, C28.2) + NET_C(R52.1, NOISE_A_FUNC.Q) +#else + NET_C(U9.3, R52.1, C28.2) +#endif + ALIAS(NOISE_A, R52.2) + +#endif + + NET_C(I_SMALL_EXPL, U26.13) + NET_C(U26.12, R32.2, U6.2) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_SMALL_EXPL) + NET_C(D_SMALL_EXPL.A, U6.2) + NET_C(D_SMALL_EXPL.K, I_V12) +#endif + NET_C(U6.1, C22.1, R33.2) + NET_C(R33.1, I_V12, R45.2, R46.2, R32.1) + NET_C(R45.1, C23.2, U6.13) + NET_C(R46.1, U6.12, U26.6) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(I_LARGE_EXPL) + NET_C(I_LARGE_EXPL.A, U6.12) + NET_C(I_LARGE_EXPL.K, I_V12) +#endif + NET_C(I_LARGE_EXPL, U26.5) + NET_C(C23.1, U6.10) + NET_C(U6.8, U6.9, U6.11) + NET_C(U6.11, D3.A) + NET_C(D3.K, R43.1) + NET_C(R43.2, R42.1, R44.1, C24.1) + NET_C(C24.2, C21.2, Q3.S, Q2.S, D4.A, D1.A, C18.2, C20.1, GND) + NET_C(R42.2, U5.6, R31.1) + NET_C(R31.2, R38.1, U5.7) + NET_C(U5.5, R44.2, Q3.D, D4.K) + NET_C(Q3.G, NOISE_B) + NET_C(R38.2, C19.1, R37.1) + NET_C(C19.2, U5.2, U5.1, R29.1) + NET_C(R37.2, U5.3, C18.1) + NET_C(R29.2, C7.1, R30.1) + ALIAS(EXPL_SUM, C7.2) + + NET_C(C22.2, U6.4) + NET_C(U6.5, U6.6, D2.A, U6.3) + NET_C(D2.K, R39.1) + NET_C(R39.2, R41.1, R40.1, C21.1) + NET_C(R41.2, U2.6, R13.1) + NET_C(R13.2, U2.7, R11.1) + NET_C(U2.5, R40.2, Q2.D, D1.K) + NET_C(Q2.G, NOISE_B) + NET_C(R11.2, C8.1, R12.1) + NET_C(R12.2, C20.2, U2.3) + NET_C(C8.2, U2.2, U2.1, R30.2) + + // + // Sheet 7, bottom-left (PSG) + // + + NET_C(R_PSG_1.2, R_PSG_2.2, R_PSG_3.2, R147.2, C73.1) + NET_C(R147.1, GND) + NET_C(C73.2, R146.1) + NET_C(R146.2, U23.2, R141.1, C72.1) + NET_C(U23.3, GND) + NET_C(C72.2, R141.2, U23.1) + ALIAS(PSG_SUM, U23.1) + + // + // Sheet 7, bottom (Thrust) + // + + NET_C(I_THRUST, U31.11) + NET_C(U31.10, R96.1, R97.1, R98.2) + NET_C(R98.1, I_V12) + NET_C(R96.2, U15.2, R75.1) + NET_C(U15.3, R97.2, Q7.D, D7.K) + NET_C(Q7.G, NOISE_B) + NET_C(Q7.S, GND) + NET_C(D7.A, GND) + NET_C(U15.1, R75.2, R95.1) + NET_C(R95.2, C40.2, R73.1) + NET_C(C40.1, GND) + NET_C(R73.2, R74.1, C41.1) + NET_C(R74.2, C37.2, U15.5) + NET_C(C37.1, GND) + NET_C(C41.2, U15.6, U15.7, C36.1) + ALIAS(THRUST_SUM, C36.2) + + // + // Sheet 8, top (Crafts Joining) + // + + NET_C(I_CRAFTS_JOINING, U26.1) + NET_C(U26.2, R120.1, U20.12) + NET_C(R120.2, I_V12, R119.2, C48.1, R80.2, R79.2) + NET_C(R119.1, C64.1, U20.13) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_CRAFTS_JOINING) + NET_C(D_CRAFTS_JOINING.A, U20.13) + NET_C(D_CRAFTS_JOINING.K, I_V12) +#endif + NET_C(U20.11, U20.8, U20.9) + NET_C(U20.10, C64.2, D9.K) + NET_C(D9.A, R101.1) + NET_C(R101.2, C48.2, R80.1, Q6.B) + NET_C(Q6.E, R79.1) + NET_C(Q6.C, Q5.E, Q4.E) + + // Another set of CD4069 oscillators: + // + // #4: R138=150K, C61=0.047uF, Freq=102.05Hz + // #5: R144=220K, C71=0.047uF, Freq=69.58Hz + // #6: R139=150K, C62=0.047uF, Freq=102.05Hz + + CLOCK(O4CLK, 102.05) + NET_C(O4CLK.GND, GND) + NET_C(O4CLK.VCC, I_V12) + NET_C(O4CLK.Q, R115.1, R116.2) + NET_C(GND, R138.1, R138.2, C61.1, C61.2, U19.1, U19.3) + + CLOCK(O5CLK, 69.58) + NET_C(O5CLK.GND, GND) + NET_C(O5CLK.VCC, I_V12) + NET_C(O5CLK.Q, R114.1, R117.2) + NET_C(GND, R144.1, R144.2, C71.1, C71.2, U19.5, U19.9) + + CLOCK(O6CLK, 102.15) + NET_C(O6CLK.GND, GND) + NET_C(O6CLK.VCC, I_V12) + NET_C(O6CLK.Q, R113.1, R118.2) + NET_C(GND, R139.1, R139.2, C62.1, C62.2, U19.13, U19.11) + + NET_C(R115.2, R114.2, R113.2, C43.2, C42.1, R100.2) + NET_C(C43.1, R65.1, U10.2) + NET_C(U10.3, R100.1, R99.2, U18.5, R67.1, R110.2, R68.1, Q5.B, R134.2, Q9.B, GND) + NET_C(R65.2, U10.1, C42.2, R78.1) + NET_C(R78.2, Q4.B, R67.2) + NET_C(Q4.C, R68.2, R76.1, U10.5) + NET_C(Q5.C, R77.1, R66.1, U10.6) + NET_C(R76.2, R77.2, I_VM12) + NET_C(R66.2, U10.7) + ALIAS(JOINING_SUM, U10.7) + + // + // Sheet 8, middle (Docking Bang) + // + + NET_C(R116.1, R117.1, R118.1, R99.1, C60.1, C59.1) + NET_C(C60.2, R112.1, U18.6) + NET_C(C59.2, R112.2, U18.7, R111.1) + NET_C(R111.2, R110.1, Q10.B) + NET_C(Q10.C, R134.1, R135.2, U18.3) + NET_C(Q10.E, Q9.E, Q13.C) + NET_C(Q9.C, U18.2, R109.1, R136.2) + NET_C(R135.1, R136.1, I_VM12) + NET_C(R109.2, U18.1) + ALIAS(BANG_SUM, U18.1) + + NET_C(I_DOCKING_BANG, U26.3) + NET_C(U26.4, R121.2, U20.2) + NET_C(R121.1, I_V12, R127.2, R126.2, R122.1, C57.1, R142.1, R143.1) + NET_C(R122.2, C63.1, U20.1) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_DOCKING_BANG) + NET_C(D_DOCKING_BANG.A, U20.1) + NET_C(D_DOCKING_BANG.K, I_V12) +#endif + NET_C(U20.3, U20.5, U20.6) + NET_C(U20.4, C63.2, D16.K) + NET_C(D16.A, R137.1) + NET_C(R137.2, C57.2, R142.2, Q13.B) + NET_C(Q13.E, R143.2) + + // + // Sheet 8, 2nd from bottom (Shoot) + // + + NET_C(I_SHOOT, U26.9) + NET_C(U26.8, R126.1, U22.2) +#if (ADD_CLIPPING_DIODES) + // fast retriggering relies on clipping diodes which + // aren't implemented by default for speed + D_1N914(D_SHOOT) + NET_C(D_SHOOT.A, U22.2) + NET_C(D_SHOOT.K, I_V12) +#endif + NET_C(U22.1, R127.1, C69.2) + NET_C(C69.1, U22.4, U11.2) + NET_C(U22.5, U22.6, U22.3) + NET_C(U22.3, D14.K) + NET_C(D14.A, R140.1) + NET_C(R140.2, D15.K, C68.2) + NET_C(C68.1, C66.1, D13.A, GND) + NET_C(D15.A, R102.1, D13.K) // and U21.3 + NET_C(C66.2, R103.1) // and U21.2 + NET_C(R103.2, R102.2, R84.1)// and U21.1 + +#if (HLE_SHOOT_VCO) + // + // R2 = 0.96721: HP = (0.000138452*A0) - 0.0000076731 + // R2 = 0.97638: HP = (0.0000289361*A0*A0) + (0.000074399*A0) + 0.0000125173 + // R2 = 0.97705: HP = (0.0000152345*A0*A0*A0) - (0.0000278973*A0*A0) + (0.000132881*A0) - 0.00000173354 + // R2 = 0.97706: HP = (-0.00000384290*A0*A0*A0*A0) + (0.0000353504*A0*A0*A0) - (0.000063189*A0*A0) + (0.000156402*A0) - 0.00000642730 + // R2 = 0.97712: HP = (0.0000167037*A0*A0*A0*A0*A0) - (0.000115736*A0*A0*A0*A0) + (0.000312133*A0*A0*A0) - (0.000372722*A0*A0) + (0.000308685*A0) - 0.0000319600 + // + VARCLOCK(SHOOTCLK, 1, "max(0.000001,min(0.1,(0.0000152345*A0*A0*A0) - (0.0000278973*A0*A0) + (0.000132881*A0) - 0.00000173354))") + NET_C(SHOOTCLK.GND, GND) + NET_C(SHOOTCLK.VCC, I_V5) + NET_C(SHOOTCLK.A0, D15.K) + NET_C(SHOOTCLK.Q, SHOOTENV.A0) + AFUNC(SHOOTENV, 1, "if(A0>2.5,11.1,-11.1)") + NET_C(SHOOTENV.Q, U11.1) + NET_C(GND, U21.3, U21.2) +#else + NET_C(D15.A, U21.3) + NET_C(U21.2, C66.2) + NET_C(R103.2, U21.1) +#endif + + NET_C(R84.2, U11.1) + NET_C(U11.12, R81.1) + NET_C(U11.11, R82.1) + NET_C(U11.9, R83.1) + NET_C(U11.6, R69.1) + NET_C(R81.2, R82.2, R83.2, R69.2, C49.1) + ALIAS(SHOOT_SUM, C49.2) + + // + // Sheet 8, bottom (Crafts Scale) + // + + NET_C(I_CRAFTS_SCALE, U31.9) + NET_C(U31.8, R130.1, R129.1, U17.4) + NET_C(R130.2, R132.2, C55.1, R104.2, U17.8, C53.1, I_V12) + NET_C(R129.2, R128.2, Q12.B) + NET_C(Q12.C, D11.A, R131.1, R132.1) + NET_C(R128.1, Q12.E, C52.1, U17.1, C54.1, C53.2, GND) + NET_C(D11.K, R131.2, C55.2, C54.2, U17.5) + NET_C(R104.1, R105.2, U17.7) + NET_C(C52.2, U17.6, U17.2, R105.1) + NET_C(U17.3, C51.1) + ALIAS(SCALE_SUM, C51.2) + + // + // Sheet 8, right (summing) + // + + NET_C(JOINING_SUM, R25.1) + NET_C(BANG_SUM, R21.1) + NET_C(SHOOT_SUM, R23.1) + NET_C(SCALE_SUM, R19.1) + NET_C(EXPL_SUM, R26.1) + NET_C(THRUST_SUM, R22.1) + NET_C(MOVING_SUM, R27.1) + NET_C(FIREBALL_SUM, R24.1) + NET_C(STAR_SPIN_PARTIAL_WARSHIP_SUM, R20.1) + NET_C(PSG_SUM, R18.1) + + NET_C(R25.2, R21.2, R23.2, R19.2, R26.2, R22.2, R27.2, R24.2, R20.2, R18.2) + ALIAS(OUTPUT, R18.2) + + // + // Unconnected inputs + // + + NET_C(GND, U21.5, U21.6) + NET_C(GND, U23.5, U23.6) + + // + // Unconnected outputs + // + +#if (ENABLE_FRONTIERS) +#define RXX 384 + OPTIMIZE_FRONTIER(C36.1, RES_K(3.9), RXX) + OPTIMIZE_FRONTIER(C44.1, RES_K(4.7), RXX) + OPTIMIZE_FRONTIER(R25.1, RES_K(47), RXX) + OPTIMIZE_FRONTIER(R21.1, RES_K(47), RXX) + OPTIMIZE_FRONTIER(R23.1, RES_K(82), RXX) + OPTIMIZE_FRONTIER(R19.1, RES_K(470), RXX) + OPTIMIZE_FRONTIER(R26.1, 100, RXX) + OPTIMIZE_FRONTIER(R22.1, RES_K(3.9), RXX) + OPTIMIZE_FRONTIER(R27.1, RES_K(100), RXX) + OPTIMIZE_FRONTIER(R24.1, RES_K(4.7), RXX) + OPTIMIZE_FRONTIER(R20.1, RES_K(22), RXX) +#endif + +NETLIST_END() diff --git a/src/mame/audio/nl_spacfury.h b/src/mame/audio/nl_spacfury.h new file mode 100644 index 00000000000..f53e47d9674 --- /dev/null +++ b/src/mame/audio/nl_spacfury.h @@ -0,0 +1,10 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +#ifndef MAME_AUDIO_NL_SPACFURY_H +#define MAME_AUDIO_NL_SPACFURY_H + +#pragma once + +NETLIST_EXTERNAL(spacfury) + +#endif // MAME_AUDIO_NL_SPACFURY_H diff --git a/src/mame/audio/segag80.cpp b/src/mame/audio/segag80.cpp new file mode 100644 index 00000000000..f3d2d9b9179 --- /dev/null +++ b/src/mame/audio/segag80.cpp @@ -0,0 +1,185 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/************************************************************************* + + Sega vector hardware + +*************************************************************************/ + +#include "emu.h" + +#include "audio/segag80.h" +#include "audio/nl_astrob.h" +#include "audio/nl_elim.h" +#include "audio/nl_spacfury.h" +#include "includes/segag80v.h" +#include "sound/samples.h" + + + +/************************************* + * + * Base class + * + *************************************/ + +segag80_audio_device::segag80_audio_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u8 lomask, u8 himask, bool haspsg, void (*netlist)(netlist::nlparse_t &), double output_scale) + : device_t(mconfig, type, tag, owner, clock) + , device_mixer_interface(mconfig, *this) + , m_lo_input(*this, "sound_nl:lo_%u", 0) + , m_hi_input(*this, "sound_nl:hi_%u", 0) + , m_psg(*this, "psg") + , m_lo_vals(0xff) + , m_hi_vals(0xff) + , m_lo_mask(lomask) + , m_hi_mask(himask) + , m_has_psg(haspsg) + , m_netlist(netlist) + , m_output_scale(output_scale) +{ +} + +void segag80_audio_device::device_add_mconfig(machine_config &config) +{ + NETLIST_SOUND(config, "sound_nl", 48000) + .set_source(m_netlist) + .add_route(ALL_OUTPUTS, *this, 1.0); + + if (BIT(m_lo_mask, 0)) + NETLIST_LOGIC_INPUT(config, m_lo_input[0], "I_LO_D0.IN", 0); + if (BIT(m_lo_mask, 1)) + NETLIST_LOGIC_INPUT(config, m_lo_input[1], "I_LO_D1.IN", 0); + if (BIT(m_lo_mask, 2)) + NETLIST_LOGIC_INPUT(config, m_lo_input[2], "I_LO_D2.IN", 0); + if (BIT(m_lo_mask, 3)) + NETLIST_LOGIC_INPUT(config, m_lo_input[3], "I_LO_D3.IN", 0); + if (BIT(m_lo_mask, 4)) + NETLIST_LOGIC_INPUT(config, m_lo_input[4], "I_LO_D4.IN", 0); + if (BIT(m_lo_mask, 5)) + NETLIST_LOGIC_INPUT(config, m_lo_input[5], "I_LO_D5.IN", 0); + if (BIT(m_lo_mask, 6)) + NETLIST_LOGIC_INPUT(config, m_lo_input[6], "I_LO_D6.IN", 0); + if (BIT(m_lo_mask, 7)) + NETLIST_LOGIC_INPUT(config, m_lo_input[7], "I_LO_D7.IN", 0); + + if (BIT(m_hi_mask, 0)) + NETLIST_LOGIC_INPUT(config, m_hi_input[0], "I_HI_D0.IN", 0); + if (BIT(m_hi_mask, 1)) + NETLIST_LOGIC_INPUT(config, m_hi_input[1], "I_HI_D1.IN", 0); + if (BIT(m_hi_mask, 2)) + NETLIST_LOGIC_INPUT(config, m_hi_input[2], "I_HI_D2.IN", 0); + if (BIT(m_hi_mask, 3)) + NETLIST_LOGIC_INPUT(config, m_hi_input[3], "I_HI_D3.IN", 0); + if (BIT(m_hi_mask, 4)) + NETLIST_LOGIC_INPUT(config, m_hi_input[4], "I_HI_D4.IN", 0); + if (BIT(m_hi_mask, 5)) + NETLIST_LOGIC_INPUT(config, m_hi_input[5], "I_HI_D5.IN", 0); + if (BIT(m_hi_mask, 6)) + NETLIST_LOGIC_INPUT(config, m_hi_input[6], "I_HI_D6.IN", 0); + if (BIT(m_hi_mask, 7)) + NETLIST_LOGIC_INPUT(config, m_hi_input[7], "I_HI_D7.IN", 0); + + if (m_has_psg) + { + AY8912(config, m_psg, VIDEO_CLOCK/4/2); + m_psg->set_flags(AY8910_RESISTOR_OUTPUT); + m_psg->set_resistors_load(10000.0, 10000.0, 10000.0); + m_psg->add_route(0, "sound_nl", 1.0, 0); + m_psg->add_route(1, "sound_nl", 1.0, 1); + m_psg->add_route(2, "sound_nl", 1.0, 2); + + NETLIST_STREAM_INPUT(config, "sound_nl:cin0", 0, "R_PSG_1.R"); + NETLIST_STREAM_INPUT(config, "sound_nl:cin1", 1, "R_PSG_2.R"); + NETLIST_STREAM_INPUT(config, "sound_nl:cin2", 2, "R_PSG_3.R"); + } + + NETLIST_STREAM_OUTPUT(config, "sound_nl:cout0", 0, "OUTPUT").set_mult_offset(m_output_scale, 0.0); +} + +void segag80_audio_device::device_start() +{ +} + +void segag80_audio_device::device_stop() +{ +} + +void segag80_audio_device::write(offs_t addr, uint8_t data) +{ + addr &= 1; + + auto &inputs = (addr == 0) ? m_lo_input : m_hi_input; + auto &oldvals = (addr == 0) ? m_lo_vals : m_hi_vals; + auto &mask = (addr == 0) ? m_lo_mask : m_hi_mask; + + for (int bit = 0; bit < 8; bit++) + if (BIT(mask, bit)) + inputs[bit]->write_line(BIT(data, bit)); + oldvals = data; +} + +void segag80_audio_device::write_ay(offs_t addr, uint8_t data) +{ + assert(m_has_psg); + m_psg->address_data_w(addr, data); +} + + + +/************************************* + * + * Eliminator + * + *************************************/ + +DEFINE_DEVICE_TYPE(ELIMINATOR_AUDIO, elim_audio_device, "elim_audio", "Eliminator Sound Board") + +elim_audio_device::elim_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : segag80_audio_device(mconfig, ELIMINATOR_AUDIO, tag, owner, clock, 0xfe, 0xff, false, NETLIST_NAME(elim), 5000.0) +{ +} + + + +/************************************* + * + * Zektor + * + *************************************/ + +DEFINE_DEVICE_TYPE(ZEKTOR_AUDIO, zektor_audio_device, "zektor_audio", "Zektor Sound Board") + +zektor_audio_device::zektor_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : segag80_audio_device(mconfig, ZEKTOR_AUDIO, tag, owner, clock, 0xfe, 0xff, true, NETLIST_NAME(zektor), 5000.0) +{ +} + + + +/************************************* + * + * Space Fury + * + *************************************/ + +DEFINE_DEVICE_TYPE(SPACE_FURY_AUDIO, spacfury_audio_device, "spcfury_audio", "Space Fury Sound Board") + +spacfury_audio_device::spacfury_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : segag80_audio_device(mconfig, SPACE_FURY_AUDIO, tag, owner, clock, 0xc7, 0x3f, false, NETLIST_NAME(spacfury), 60000.0) +{ +} + + + +/************************************* + * + * Astro Blaster + * + *************************************/ + +DEFINE_DEVICE_TYPE(ASTRO_BLASTER_AUDIO, astrob_audio_device, "astrob_audio", "Astro Blaster Sound Board") + +astrob_audio_device::astrob_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : segag80_audio_device(mconfig, ASTRO_BLASTER_AUDIO, tag, owner, clock, 0xff, 0xff, false, NETLIST_NAME(astrob), 25000.0) +{ +} diff --git a/src/mame/audio/segag80.h b/src/mame/audio/segag80.h new file mode 100644 index 00000000000..4f6c29b324a --- /dev/null +++ b/src/mame/audio/segag80.h @@ -0,0 +1,74 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +#ifndef MAME_AUDIO_SEGAG80V_H +#define MAME_AUDIO_SEGAG80V_H + +#pragma once + +#include "machine/netlist.h" +#include "netlist/nl_setup.h" +#include "sound/ay8910.h" + + +class segag80_audio_device : public device_t, public device_mixer_interface +{ +public: + segag80_audio_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u8 lomask, u8 himask, bool haspsg, void (*netlist)(netlist::nlparse_t &), double output_scale); + + void write(offs_t addr, uint8_t data); + void write_ay(offs_t addr, uint8_t data); + +protected: + virtual void device_add_mconfig(machine_config &config) override; + virtual void device_start() override; + virtual void device_stop() override; + + optional_device_array m_lo_input; + optional_device_array m_hi_input; + optional_device m_psg; + +private: + u8 m_lo_vals; + u8 m_hi_vals; + u8 m_lo_mask; + u8 m_hi_mask; + bool m_has_psg; + void (*m_netlist)(netlist::nlparse_t &) = nullptr; + double m_output_scale = 0; +}; + + +class elim_audio_device : public segag80_audio_device +{ +public: + elim_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); +}; + + +class zektor_audio_device : public segag80_audio_device +{ +public: + zektor_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); +}; + + +class spacfury_audio_device : public segag80_audio_device +{ +public: + spacfury_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); +}; + + +class astrob_audio_device : public segag80_audio_device +{ +public: + astrob_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); +}; + + +DECLARE_DEVICE_TYPE(ELIMINATOR_AUDIO, elim_audio_device) +DECLARE_DEVICE_TYPE(ZEKTOR_AUDIO, zektor_audio_device) +DECLARE_DEVICE_TYPE(SPACE_FURY_AUDIO, spacfury_audio_device) +DECLARE_DEVICE_TYPE(ASTRO_BLASTER_AUDIO, astrob_audio_device) + +#endif // MAME_AUDIO_SEGAG80V_H diff --git a/src/mame/audio/segag80r.cpp b/src/mame/audio/segag80r.cpp index d71ec7edda9..1662c2f2258 100644 --- a/src/mame/audio/segag80r.cpp +++ b/src/mame/audio/segag80r.cpp @@ -86,260 +86,6 @@ void sega005_sound_device::sound_stream_update(sound_stream &stream, stream_samp -/************************************* - * - * Astro Blaster sound hardware - * - *************************************/ - -/* - Description of Astro Blaster sounds (in the hope of future discrete goodness): - - CD4017 = decade counter with one output per decoded stage (10 outputs altogether) - CD4024 = 7-bit counter with 7 outputs - - - "V" signal - ---------- - CD4017 @ U15: - reset by RATE RESET signal = 1 - clocked by falling edge of ATTACK signal - +12V output from here goes through a diode and one of 10 resistors: - 0 = 120k - 1 = 82k - 2 = 62k - 3 = 56k - 4 = 47k - 5 = 39k - 6 = 35k - 7 = 27k - 8 = 24k - 9 = 22k - and then in series through a 22k resistor - - Op-amp @ U6 takes the WARP signal and the output of CD4017 @ U15 - and forms the signal "V" which is used to control the invader - sounds - - - How to calculate the output voltage at U16 labeled (V). - (Derrick Renaud) - - First you have an inverting amp. To get the gain you - use G=-Rf/Ri, where Rf=R178=22k. Ri is the selected - resistor on the output of U15. - - The input voltage to the amp (pin 6) will always be - about 12V - 0.5V (diode drop in low current circuit) = - 11.5V. - - Now you need to calculate the reference voltage on the - + input (pin 5). Depending on the state of WARP... - - If the warp data is 0, then U31 inverts it to an Open - Collector high, meaning WARP is out of circuit. So: - Vref = 12V * (R163)/(R162+R163) - = 12V * 10k/(10K+4.7k) - = 8.163V - - When warp data is 1, then U31 inverts it to low, - grounding R164 putting it in parallel with R163, - giving: - Vref = 12V * (R163||R164)/(R163||R164 +R162) - = 12V * 5k/(5k+4.7k) - = 6.186V - - Now to get the control voltage V: - V = (Vi - Vref) * G + Vref - = (11.5V - Vref) * G + Vref - - That gives you the control voltage at V. From there I - would have to millman the voltage with the internal - voltage/resistors of the 555 to get the actual used - control voltage. - - But it seems you just want a range, so just use the - above info to get the highest and lowest voltages - generated, and create the frequency shift you desire. - Remember as the control voltage (V) lowers, the - frequency increases. - - - - INVADER-1 output - ---------------- - - - - - INVADER-2 output - ---------------- - 555 timer @ U13 in astable mode with the following parameters: - R1 = 10k - R2 = 100k - C = 0.0022u - CV = "V" signal - Reset = (PORT076 & 0x02) - Output goes to CD4024 @ U12 - - CD4024 @ U12: - reset through some unknown means - clocked by 555 timer @ U13 - +12 output from here goes through a resistor ladder: - Q1 -> 82k - Q2 -> 39k - Q3 -> 22k - Q4 -> 10k - Summed output from here is INVADER-2 - - - INVADER-3 output - ---------------- - 555 timer at U17 in astable mode with the following parameters: - R1 = 10k - R2 = 68k - C = 0.1u - CV = some combination of "V" and "W" signals - Reset = (PORT076 & 0x04) - Output from here is INVADER-3 - -*/ - -static const char *const astrob_sample_names[] = -{ - "*astrob", - "invadr1", /* 0 */ - "winvadr1", /* 1 */ - "invadr2", /* 2 */ - "winvadr2", /* 3 */ - "invadr3", /* 4 */ - "winvadr3", /* 5 */ - "invadr4", /* 6 */ - "winvadr4", /* 7 */ - "asteroid", /* 8 */ - "refuel", /* 9 */ - "pbullet", /* 10 */ - "ebullet", /* 11 */ - "eexplode", /* 12 */ - "pexplode", /* 13 */ - "deedle", /* 14 */ - "sonar", /* 15 */ - nullptr -}; - - -void segag80r_state::astrob_sound_board(machine_config &config) -{ - /* sound hardware */ - SAMPLES(config, m_samples); - m_samples->set_channels(11); - m_samples->set_samples_names(astrob_sample_names); - m_samples->add_route(ALL_OUTPUTS, "speaker", 0.25); -} - - -/************************************* - * - * Astro Blaster sound triggers - * - *************************************/ - -void segag80r_state::astrob_sound_w(offs_t offset, uint8_t data) -{ - static const float attack_resistor[10] = - { - 120.0f, 82.0f, 62.0f, 56.0f, 47.0f, 39.0f, 33.0f, 27.0f, 24.0f, 22.0f - }; - float freq_factor; - - uint8_t diff = data ^ m_sound_state[offset]; - m_sound_state[offset] = data; - - switch (offset) - { - case 0: - /* INVADER-1: channel 0 */ - if ((diff & 0x01) && !(data & 0x01)) m_samples->start(0, (data & 0x80) ? 0 : 1, true); - if ((data & 0x01) && m_samples->playing(0)) m_samples->stop(0); - - /* INVADER-2: channel 1 */ - if ((diff & 0x02) && !(data & 0x02)) m_samples->start(1, (data & 0x80) ? 2 : 3, true); - if ((data & 0x02) && m_samples->playing(1)) m_samples->stop(1); - - /* INVADER-3: channel 2 */ - if ((diff & 0x04) && !(data & 0x04)) m_samples->start(2, (data & 0x80) ? 4 : 5, true); - if ((data & 0x04) && m_samples->playing(2)) m_samples->stop(2); - - /* INVADER-4: channel 3 */ - if ((diff & 0x08) && !(data & 0x08)) m_samples->start(3, (data & 0x80) ? 6 : 7, true); - if ((data & 0x08) && m_samples->playing(3)) m_samples->stop(3); - - /* ASTROIDS: channel 4 */ - if ((diff & 0x10) && !(data & 0x10)) m_samples->start(4, 8, true); - if ((data & 0x10) && m_samples->playing(4)) m_samples->stop(4); - - /* MUTE */ - machine().sound().system_mute(data & 0x20); - - /* REFILL: channel 5 */ - if (!(data & 0x40) && !m_samples->playing(5)) m_samples->start(5, 9); - if ( (data & 0x40) && m_samples->playing(5)) m_samples->stop(5); - - /* WARP: changes which sample is played for the INVADER samples above */ - if (diff & 0x80) - { - if (m_samples->playing(0)) m_samples->start(0, (data & 0x80) ? 0 : 1, true); - if (m_samples->playing(1)) m_samples->start(1, (data & 0x80) ? 2 : 3, true); - if (m_samples->playing(2)) m_samples->start(2, (data & 0x80) ? 4 : 5, true); - if (m_samples->playing(3)) m_samples->start(3, (data & 0x80) ? 6 : 7, true); - } - break; - - case 1: - /* LASER #1: channel 6 */ - if ((diff & 0x01) && !(data & 0x01)) m_samples->start(6, 10); - - /* LASER #2: channel 7 */ - if ((diff & 0x02) && !(data & 0x02)) m_samples->start(7, 11); - - /* SHORT EXPL: channel 8 */ - if ((diff & 0x04) && !(data & 0x04)) m_samples->start(8, 12); - - /* LONG EXPL: channel 8 */ - if ((diff & 0x08) && !(data & 0x08)) m_samples->start(8, 13); - - /* ATTACK RATE */ - if ((diff & 0x10) && !(data & 0x10)) m_sound_rate = (m_sound_rate + 1) % 10; - - /* RATE RESET */ - if (!(data & 0x20)) m_sound_rate = 0; - - /* BONUS: channel 9 */ - if ((diff & 0x40) && !(data & 0x40)) m_samples->start(9, 14); - - /* SONAR: channel 10 */ - if ((diff & 0x80) && !(data & 0x80)) m_samples->start(10, 15); - break; - } - - /* the samples were recorded with sound_rate = 0, so we need to scale */ - /* the frequency as a fraction of that; these equations come from */ - /* Derrick's analysis above; we compute the inverted scale factor to */ - /* account for the fact that frequency goes up as CV goes down */ - /* WARP is already taken into account by the differing samples above */ - freq_factor = (11.5f - 8.163f) * (-22.0f / attack_resistor[0]) + 8.163f; - freq_factor /= (11.5f - 8.163f) * (-22.0f / attack_resistor[m_sound_rate]) + 8.163f; - - /* adjust the sample rate of invader sounds based the sound_rate */ - /* this is an approximation */ - if (m_samples->playing(0)) m_samples->set_frequency(0, m_samples->base_frequency(0) * freq_factor); - if (m_samples->playing(1)) m_samples->set_frequency(1, m_samples->base_frequency(1) * freq_factor); - if (m_samples->playing(2)) m_samples->set_frequency(2, m_samples->base_frequency(2) * freq_factor); - if (m_samples->playing(3)) m_samples->set_frequency(3, m_samples->base_frequency(3) * freq_factor); -} - - - /************************************* * * 005 sound hardware diff --git a/src/mame/audio/segag80v.cpp b/src/mame/audio/segag80v.cpp deleted file mode 100644 index a6944b39d3b..00000000000 --- a/src/mame/audio/segag80v.cpp +++ /dev/null @@ -1,327 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Aaron Giles -/************************************************************************* - - Sega vector hardware - -*************************************************************************/ - -#include "emu.h" -#include "includes/segag80v.h" -#include "sound/samples.h" - -/* History: - - - - * 4/25/99 Tac-Scan now makes Credit Noises with $2c (Jim Hernandez) - * 4/9/99 Zektor Discrete Sound Support mixed with voice samples. (Jim Hernandez) - Zektor uses some Eliminator sounds. - - * 2/5/99 Extra Life sound constant found $1C after fixing main driver. (Jim Hernandez) - * 1/29/99 Supports Tac Scan new 44.1 kHz sample set. (Jim Hernandez) - - * -Stuff to do - - * Find hex bit for warp.wav sound calls. - * - * 2/05/98 now using the new sample_*() functions. BW - * - */ - -/* - Tac/Scan sound constants - - There are some sounds that are unknown: - $09 Tunnel Warp Sound? - $0a - $0b Formation Change - $0c - $0e - $0f - $1c 1up (Extra Life) - $2c Credit - $30 - $3f Hex numbers for ship position flight sounds - $41 - - Some sound samples are missing: - - I use the one bullet and one explosion sound for all 3 for example. - -Star Trk Sounds (USB Loaded from 5400 in main EPROMs) - -8 PHASER -a PHOTON -e TARGETING -10 DENY -12 SHEILD HIT -14 ENTERPRISE HIT -16 ENT EXPLOSION -1a KLINGON EXPLOSION -1c DOCK -1e STARBASE HIT -11 STARBASE RED -22 STARBASE EXPLOSION -24 SMALL BONUS -25 LARGE BONUS -26 STARBASE INTRO -27 KLINGON INTRO -28 ENTERPRISE INTRO -29 PLAYER CHANGE -2e KLINGON FIRE -4,5 IMPULSE -6,7 WARP -c,d RED ALERT -18,2f WARP SUCK -19,2f SAUCER EXIT -2c,21 NOMAD MOTION -2d,21 NOMAD STOPPED -2b COIN DROP MUSIC -2a HIGH SCORE MUSIC - - -Eliminator Sound Board (800-3174) ---------------------------------- - -inputs -0x3c-0x3f - -d0 speech ready - -outputs ( 0 = ON) - -0x3e (076) - -d7 torpedo 2 -d6 torpedo 1 -d5 bounce -d4 explosion 3 -d3 explosion 2 -d2 explosion 1 -d1 fireball -d0 - - -0x3f (077) - -d7 background msb -d6 background lsb -d5 enemy ship -d4 skitter -d3 thrust msb -d2 thrust lsb -d1 thrust hi -d0 thrust lo - -Space Fury Sound Board (800-0241) ---------------------------------- - -0x3e (076) (0 = ON) - -d7 partial warship, low frequency oscillation -d6 star spin -d5 - -d4 - -d3 - -d2 thrust, low frequency noise -d1 fire, metalic buzz -d0 craft scale, rising tone - -0x3f (077) - -d7 - -d6 - -d5 docking bang -d4 large explosion -d3 small explosion, low frequency noise -d2 fireball -d1 shot -d0 crafts joining - -*/ - -void segag80v_state::elim1_sh_w(uint8_t data) -{ - data ^= 0xff; - - /* Play fireball sample */ - if (data & 0x02) - m_samples->start(0, 0); - - /* Play explosion samples */ - if (data & 0x04) - m_samples->start(1, 10); - if (data & 0x08) - m_samples->start(1, 9); - if (data & 0x10) - m_samples->start(1, 8); - - /* Play bounce sample */ - if (data & 0x20) - { - if (m_samples->playing(2)) - m_samples->stop(2); - m_samples->start(2, 1); - } - - /* Play lazer sample */ - if (data & 0xc0) - { - if (m_samples->playing(3)) - m_samples->stop(3); - m_samples->start(3, 5); - } -} - -void segag80v_state::elim2_sh_w(uint8_t data) -{ - data ^= 0xff; - - /* Play thrust sample */ - if (data & 0x0f) - m_samples->start(4, 6); - else - m_samples->stop(4); - - /* Play skitter sample */ - if (data & 0x10) - m_samples->start(5, 2); - - /* Play eliminator sample */ - if (data & 0x20) - m_samples->start(6, 3); - - /* Play electron samples */ - if (data & 0x40) - m_samples->start(7, 7); - if (data & 0x80) - m_samples->start(7, 4); -} - - -void segag80v_state::zektor1_sh_w(uint8_t data) -{ - data ^= 0xff; - - /* Play fireball sample */ - if (data & 0x02) - m_samples->start(0, 0); - - /* Play explosion samples */ - if (data & 0x04) - m_samples->start(1, 10); - if (data & 0x08) - m_samples->start(1, 9); - if (data & 0x10) - m_samples->start(1, 8); - - /* Play bounce sample */ - if (data & 0x20) - { - if (m_samples->playing(2)) - m_samples->stop(2); - m_samples->start(2, 1); - } - - /* Play lazer sample */ - if (data & 0xc0) - { - if (m_samples->playing(3)) - m_samples->stop(3); - m_samples->start(3, 5); - } -} - -void segag80v_state::zektor2_sh_w(uint8_t data) -{ - data ^= 0xff; - - /* Play thrust sample */ - if (data & 0x0f) - m_samples->start(4, 6); - else - m_samples->stop(4); - - /* Play skitter sample */ - if (data & 0x10) - m_samples->start(5, 2); - - /* Play eliminator sample */ - if (data & 0x20) - m_samples->start(6, 3); - - /* Play electron samples */ - if (data & 0x40) - m_samples->start(7, 40); - if (data & 0x80) - m_samples->start(7, 41); -} - - - -void segag80v_state::spacfury1_sh_w(uint8_t data) -{ - data ^= 0xff; - - /* craft growing */ - if (data & 0x01) - m_samples->start(1, 0); - - /* craft moving */ - if (data & 0x02) - { - if (!m_samples->playing(2)) - m_samples->start(2, 1, true); - } - else - m_samples->stop(2); - - /* Thrust */ - if (data & 0x04) - { - if (!m_samples->playing(3)) - m_samples->start(3, 4, true); - } - else - m_samples->stop(3); - - /* star spin */ - if (data & 0x40) - m_samples->start(4, 8); - - /* partial warship? */ - if (data & 0x80) - m_samples->start(4, 9); - -} - -void segag80v_state::spacfury2_sh_w(uint8_t data) -{ - data ^= 0xff; - - /* craft joining */ - if (data & 0x01) - m_samples->start(5, 2); - - /* ship firing */ - if (data & 0x02) - { - if (m_samples->playing(6)) - m_samples->stop(6); - m_samples->start(6, 3); - - } - - /* fireball */ - if (data & 0x04) - m_samples->start(7, 6); - - /* small explosion */ - if (data & 0x08) - m_samples->start(7, 6); - /* large explosion */ - if (data & 0x10) - m_samples->start(7, 5); - - /* docking bang */ - if (data & 0x20) - m_samples->start(0, 7); - -} diff --git a/src/mame/audio/segasnd.cpp b/src/mame/audio/segasnd.cpp deleted file mode 100644 index af247149188..00000000000 --- a/src/mame/audio/segasnd.cpp +++ /dev/null @@ -1,909 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Aaron Giles -/*************************************************************************** - - segasnd.cpp - - Sound boards for early Sega G-80 based games. - -***************************************************************************/ - -#include "emu.h" -#include "segasnd.h" - -#include "sound/sp0250.h" -#include "includes/segag80r.h" -#include "includes/segag80v.h" - -#include - - -#define VERBOSE 0 -#include "logmacro.h" - - -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -#define SPEECH_MASTER_CLOCK 3120000 - -#define USB_MASTER_CLOCK 6000000 -#define USB_2MHZ_CLOCK (USB_MASTER_CLOCK/3) -#define USB_PCS_CLOCK (USB_2MHZ_CLOCK/2) -#define USB_GOS_CLOCK (USB_2MHZ_CLOCK/16/4) -#define MM5837_CLOCK 100000 - -#define SAMPLE_RATE (USB_2MHZ_CLOCK/8) - - - -/*************************************************************************** - INLINE FUNCTIONS -***************************************************************************/ - -inline void usb_sound_device::g80_filter_state::configure(double r, double c) -{ - capval = 0.0; - exponent = 1.0 - std::exp(-1.0 / (r * c * SAMPLE_RATE)); -} - - -inline double usb_sound_device::g80_filter_state::step_rc(double input) -{ - return capval += (input - capval) * exponent; -} - - -inline double usb_sound_device::g80_filter_state::step_cr(double input) -{ - double const result = input - capval; - capval += result * exponent; - return result; -} - - - -/*************************************************************************** - SPEECH BOARD -***************************************************************************/ - -DEFINE_DEVICE_TYPE(SEGASPEECH, speech_sound_device, "sega_speech_sound", "Sega Speech Sound Board") - -#define SEGASPEECH_REGION "speech" - -speech_sound_device::speech_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : device_t(mconfig, SEGASPEECH, tag, owner, clock), - device_sound_interface(mconfig, *this), - m_int_cb(*this), - m_speech(*this, SEGASPEECH_REGION), - m_drq(0), - m_latch(0), - m_t0(0), - m_p2(0) -{ -} - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void speech_sound_device::device_start() -{ - m_int_cb.resolve(); - - save_item(NAME(m_latch)); - save_item(NAME(m_t0)); - save_item(NAME(m_p2)); - save_item(NAME(m_drq)); -} - - -/************************************* - * - * i8035 port accessors - * - *************************************/ - - - -READ_LINE_MEMBER( speech_sound_device::t0_r ) -{ - return m_t0; -} - -READ_LINE_MEMBER( speech_sound_device::t1_r ) -{ - return m_drq; -} - -uint8_t speech_sound_device::p1_r() -{ - return m_latch & 0x7f; -} - -uint8_t speech_sound_device::rom_r(offs_t offset) -{ - return m_speech->base()[0x100 * (m_p2 & 0x3f) + offset]; -} - -void speech_sound_device::p1_w(uint8_t data) -{ - if (!(data & 0x80)) - m_t0 = 0; -} - -void speech_sound_device::p2_w(uint8_t data) -{ - m_p2 = data; -} - - - -/************************************* - * - * i8035 port accessors - * - *************************************/ - -WRITE_LINE_MEMBER(speech_sound_device::drq_w) -{ - m_drq = (state == ASSERT_LINE); -} - - - -/************************************* - * - * External access - * - *************************************/ - -TIMER_CALLBACK_MEMBER( speech_sound_device::delayed_speech_w ) -{ - int data = param; - u8 old = m_latch; - - /* all 8 bits are latched */ - m_latch = data; - - /* the high bit goes directly to the INT line */ - m_int_cb((data & 0x80) ? CLEAR_LINE : ASSERT_LINE); - - /* a clock on the high bit clocks a 1 into T0 */ - if (!(old & 0x80) && (data & 0x80)) - m_t0 = 1; -} - - -void speech_sound_device::data_w(uint8_t data) -{ - machine().scheduler().synchronize(timer_expired_delegate(FUNC(speech_sound_device::delayed_speech_w), this), data); -} - - -void speech_sound_device::control_w(uint8_t data) -{ - LOG("Speech control = %X\n", data); -} - - -//------------------------------------------------- -// sound_stream_update - handle a stream update -//------------------------------------------------- - -void speech_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) -{ -} - -/************************************* - * - * Speech board functions - * - *************************************/ - -WRITE_LINE_MEMBER(segag80snd_common::segaspeech_int_w) -{ - m_audiocpu->set_input_line(0, state); -} - -/************************************* - * - * Speech board address maps - * - *************************************/ - -void segag80snd_common::speech_map(address_map &map) -{ - map(0x0000, 0x07ff).mirror(0x0800).rom(); -} - - -void segag80snd_common::speech_portmap(address_map &map) -{ - map(0x00, 0xff).r("segaspeech", FUNC(speech_sound_device::rom_r)); - map(0x00, 0xff).w("speech", FUNC(sp0250_device::write)); -} - - -/************************************* - * - * Speech board machine drivers - * - *************************************/ - -void segag80snd_common::sega_speech_board(machine_config &config) -{ - /* CPU for the speech board */ - i8035_device &audiocpu(I8035(config, m_audiocpu, SPEECH_MASTER_CLOCK)); /* divide by 15 in CPU */ - audiocpu.set_addrmap(AS_PROGRAM, &segag80snd_common::speech_map); - audiocpu.set_addrmap(AS_IO, &segag80snd_common::speech_portmap); - audiocpu.p1_in_cb().set("segaspeech", FUNC(speech_sound_device::p1_r)); - audiocpu.p1_out_cb().set("segaspeech", FUNC(speech_sound_device::p1_w)); - audiocpu.p2_out_cb().set("segaspeech", FUNC(speech_sound_device::p2_w)); - audiocpu.t0_in_cb().set("segaspeech", FUNC(speech_sound_device::t0_r)); - audiocpu.t1_in_cb().set("segaspeech", FUNC(speech_sound_device::t1_r)); - - /* sound hardware */ - SEGASPEECH(config, "segaspeech", 0).int_cb().set(FUNC(segag80snd_common::segaspeech_int_w)); - sp0250_device &speech(SP0250(config, "speech", SPEECH_MASTER_CLOCK)); - speech.drq().set("segaspeech", FUNC(speech_sound_device::drq_w)); - speech.add_route(ALL_OUTPUTS, "speaker", 1.0); -} - - - -/*************************************************************************** - UNIVERSAL SOUND BOARD -***************************************************************************/ - -DEFINE_DEVICE_TYPE(SEGAUSB, usb_sound_device, "segausb", "Sega Universal Sound Board") - -usb_sound_device::usb_sound_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) - : device_t(mconfig, type, tag, owner, clock), - device_sound_interface(mconfig, *this), - m_ourcpu(*this, "ourcpu"), - m_maincpu(*this, finder_base::DUMMY_TAG), - m_stream(nullptr), - m_in_latch(0), - m_out_latch(0), - m_last_p2_value(0), - m_program_ram(*this, "pgmram"), - m_work_ram(*this, "workram"), - m_work_ram_bank(0), - m_t1_clock(0), - m_t1_clock_mask(0), - m_noise_shift(0), - m_noise_state(0), - m_noise_subcount(0) -{ -} - -usb_sound_device::usb_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : usb_sound_device(mconfig, SEGAUSB, tag, owner, clock) -{ -} - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void usb_sound_device::device_start() -{ - /* create a sound stream */ - m_stream = machine().sound().stream_alloc(*this, 0, 1, SAMPLE_RATE); - - /* initialize state */ - m_noise_shift = 0x15555; - - for (timer8253 &g : m_timer_group) - { - g.chan_filter[0].configure(10e3, 1e-6); - g.chan_filter[1].configure(10e3, 1e-6); - g.gate1.configure(100e3, 0.01e-6); - g.gate2.configure(2 * 100e3, 0.01e-6); - } - - g80_filter_state temp; - temp.configure(100e3, 0.01e-6); - m_gate_rc1_exp[0] = temp.exponent; - temp.configure(1e3, 0.01e-6); - m_gate_rc1_exp[1] = temp.exponent; - temp.configure(2 * 100e3, 0.01e-6); - m_gate_rc2_exp[0] = temp.exponent; - temp.configure(2 * 1e3, 0.01e-6); - m_gate_rc2_exp[1] = temp.exponent; - - m_noise_filters[0].configure(2.7e3 + 2.7e3, 1.0e-6); - m_noise_filters[1].configure(2.7e3 + 1e3, 0.30e-6); - m_noise_filters[2].configure(2.7e3 + 270, 0.15e-6); - m_noise_filters[3].configure(2.7e3 + 0, 0.082e-6); - m_noise_filters[4].configure(33e3, 0.1e-6); - - m_final_filter.configure(100e3, 4.7e-6); - - /* register for save states */ - save_item(NAME(m_in_latch)); - save_item(NAME(m_out_latch)); - save_item(NAME(m_last_p2_value)); - save_item(NAME(m_work_ram_bank)); - save_item(NAME(m_t1_clock)); - - for (int tgroup = 0; tgroup < 3; tgroup++) - { - timer8253 *group = &m_timer_group[tgroup]; - for (int tchan = 0; tchan < 3; tchan++) - { - timer8253::channel *channel = &group->chan[tchan]; - save_item(NAME(channel->holding), tgroup * 3 + tchan); - save_item(NAME(channel->latchmode), tgroup * 3 + tchan); - save_item(NAME(channel->latchtoggle), tgroup * 3 + tchan); - save_item(NAME(channel->clockmode), tgroup * 3 + tchan); - save_item(NAME(channel->bcdmode), tgroup * 3 + tchan); - save_item(NAME(channel->output), tgroup * 3 + tchan); - save_item(NAME(channel->lastgate), tgroup * 3 + tchan); - save_item(NAME(channel->gate), tgroup * 3 + tchan); - save_item(NAME(channel->subcount), tgroup * 3 + tchan); - save_item(NAME(channel->count), tgroup * 3 + tchan); - save_item(NAME(channel->remain), tgroup * 3 + tchan); - } - save_item(NAME(group->env), tgroup); - save_item(NAME(group->chan_filter[0].capval), tgroup); - save_item(NAME(group->chan_filter[1].capval), tgroup); - save_item(NAME(group->gate1.capval), tgroup); - save_item(NAME(group->gate2.capval), tgroup); - save_item(NAME(group->config), tgroup); - } - - save_item(NAME(m_timer_mode)); - save_item(NAME(m_noise_shift)); - save_item(NAME(m_noise_state)); - save_item(NAME(m_noise_subcount)); - save_item(NAME(m_final_filter.capval)); - save_item(NAME(m_noise_filters[0].capval)); - save_item(NAME(m_noise_filters[1].capval)); - save_item(NAME(m_noise_filters[2].capval)); - save_item(NAME(m_noise_filters[3].capval)); - save_item(NAME(m_noise_filters[4].capval)); -} - -//------------------------------------------------- -// device_reset - device-specific reset -//------------------------------------------------- - -void usb_sound_device::device_reset() -{ - /* halt the USB CPU at reset time */ - m_ourcpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); - - /* start the clock timer */ - m_t1_clock_mask = 0x10; -} - -/************************************* - * - * Initialization/reset - * - *************************************/ - -TIMER_DEVICE_CALLBACK_MEMBER( usb_sound_device::increment_t1_clock_timer_cb ) -{ - /* only increment if it is not being forced clear */ - if (!(m_last_p2_value & 0x80)) - m_t1_clock++; -} - -/************************************* - * - * External access - * - *************************************/ - -uint8_t usb_sound_device::status_r() -{ - LOG("%s:usb_data_r = %02X\n", machine().describe_context(), (m_out_latch & 0x81) | (m_in_latch & 0x7e)); - - m_maincpu->adjust_icount(-200); - - /* only bits 0 and 7 are controlled by the I8035; the remaining */ - /* bits 1-6 reflect the current input latch values */ - return (m_out_latch & 0x81) | (m_in_latch & 0x7e); -} - - -TIMER_CALLBACK_MEMBER( usb_sound_device::delayed_usb_data_w ) -{ - int data = param; - - /* look for rising/falling edges of bit 7 to control the RESET line */ - m_ourcpu->set_input_line(INPUT_LINE_RESET, (data & 0x80) ? ASSERT_LINE : CLEAR_LINE); - - /* if the CLEAR line is set, the low 7 bits of the input are ignored */ - if ((m_last_p2_value & 0x40) == 0) - data &= ~0x7f; - - /* update the effective input latch */ - m_in_latch = data; -} - - -void usb_sound_device::data_w(uint8_t data) -{ - LOG("%s:usb_data_w = %02X\n", machine().describe_context(), data); - machine().scheduler().synchronize(timer_expired_delegate(FUNC(usb_sound_device::delayed_usb_data_w), this), data); - - /* boost the interleave so that sequences can be sent */ - machine().scheduler().boost_interleave(attotime::zero, attotime::from_usec(250)); -} - - -uint8_t usb_sound_device::ram_r(offs_t offset) -{ - return m_program_ram[offset]; -} - - -void usb_sound_device::ram_w(offs_t offset, uint8_t data) -{ - if (m_in_latch & 0x80) - m_program_ram[offset] = data; - else - LOG("%s:sega_usb_ram_w(%03X) = %02X while /LOAD disabled\n", machine().describe_context(), offset, data); -} - - - -/************************************* - * - * I8035 port accesses - * - *************************************/ - -uint8_t usb_sound_device::p1_r() -{ - /* bits 0-6 are inputs and map to bits 0-6 of the input latch */ - if ((m_in_latch & 0x7f) != 0) - LOG("%s: P1 read = %02X\n", machine().describe_context(), m_in_latch & 0x7f); - return m_in_latch & 0x7f; -} - - -void usb_sound_device::p1_w(uint8_t data) -{ - /* bit 7 maps to bit 0 on the output latch */ - m_out_latch = (m_out_latch & 0xfe) | (data >> 7); - LOG("%s: P1 write = %02X\n", machine().describe_context(), data); -} - - -void usb_sound_device::p2_w(uint8_t data) -{ - u8 old = m_last_p2_value; - m_last_p2_value = data; - - /* low 2 bits control the bank of work RAM we are addressing */ - m_work_ram_bank = data & 3; - - /* bit 6 controls the "ready" bit output to the host */ - /* it also clears the input latch from the host (active low) */ - m_out_latch = ((data & 0x40) << 1) | (m_out_latch & 0x7f); - if ((data & 0x40) == 0) - m_in_latch = 0; - - /* bit 7 controls the reset on the upper counter at U33 */ - if ((old & 0x80) && !(data & 0x80)) - m_t1_clock = 0; - - LOG("%s: P2 write -> bank=%d ready=%d clock=%d\n", machine().describe_context(), data & 3, (data >> 6) & 1, (data >> 7) & 1); -} - - -READ_LINE_MEMBER( usb_sound_device::t1_r ) -{ - /* T1 returns 1 based on the value of the T1 clock; the exact */ - /* pattern is determined by one or more jumpers on the board. */ - return (m_t1_clock & m_t1_clock_mask) != 0; -} - - - -/************************************* - * - * Sound generation - * - *************************************/ - -inline void usb_sound_device::timer8253::channel::clock() -{ - u8 const old_lastgate = lastgate; - - /* update the gate */ - lastgate = gate; - - /* if we're holding, skip */ - if (holding) - return; - - /* switch off the clock mode */ - switch (clockmode) - { - /* oneshot; waits for trigger to restart */ - case 1: - if (!old_lastgate && gate) - { - output = 0; - remain = count; - } - else - { - if (--remain == 0) - output = 1; - } - break; - - /* square wave: counts down by 2 and toggles output */ - case 3: - remain = (remain - 1) & ~1; - if (remain == 0) - { - output ^= 1; - remain = count; - } - break; - } -} - - -/************************************* - * - * USB timer and envelope controls - * - *************************************/ - -void usb_sound_device::timer_w(int which, u8 offset, u8 data) -{ - timer8253 *g = &m_timer_group[which]; - timer8253::channel *ch; - int was_holding; - - m_stream->update(); - - /* switch off the offset */ - switch (offset) - { - case 0: - case 1: - case 2: - ch = &g->chan[offset]; - was_holding = ch->holding; - - /* based on the latching mode */ - switch (ch->latchmode) - { - case 1: /* low word only */ - ch->count = data; - ch->holding = false; - break; - - case 2: /* high word only */ - ch->count = data << 8; - ch->holding = false; - break; - - case 3: /* low word followed by high word */ - if (ch->latchtoggle == 0) - { - ch->count = (ch->count & 0xff00) | (data & 0x00ff); - ch->latchtoggle = 1; - } - else - { - ch->count = (ch->count & 0x00ff) | (data << 8); - ch->holding = false; - ch->latchtoggle = 0; - } - break; - } - - /* if we're not holding, load the initial count for some modes */ - if (was_holding && !ch->holding) - ch->remain = 1; - break; - - case 3: - /* break out the components */ - if (((data & 0xc0) >> 6) < 3) - { - ch = &g->chan[(data & 0xc0) >> 6]; - - /* extract the bits */ - ch->holding = true; - ch->latchmode = (data >> 4) & 3; - ch->clockmode = (data >> 1) & 7; - ch->bcdmode = (data >> 0) & 1; - ch->latchtoggle = 0; - ch->output = (ch->clockmode == 1); - } - break; - } -} - - -void usb_sound_device::env_w(int which, u8 offset, u8 data) -{ - timer8253 *g = &m_timer_group[which]; - - m_stream->update(); - - if (offset < 3) - g->env[offset] = (double)data; - else - g->config = data & 1; -} - - - -/************************************* - * - * USB work RAM access - * - *************************************/ - -uint8_t usb_sound_device::workram_r(offs_t offset) -{ - offset += 256 * m_work_ram_bank; - return m_work_ram[offset]; -} - - -void usb_sound_device::workram_w(offs_t offset, uint8_t data) -{ - offset += 256 * m_work_ram_bank; - m_work_ram[offset] = data; - - /* writes to the low 32 bytes go to various controls */ - switch (offset & ~3) - { - case 0x00: /* CTC0 */ - timer_w(0, offset & 3, data); - break; - - case 0x04: /* ENV0 */ - env_w(0, offset & 3, data); - break; - - case 0x08: /* CTC1 */ - timer_w(1, offset & 3, data); - break; - - case 0x0c: /* ENV1 */ - env_w(1, offset & 3, data); - break; - - case 0x10: /* CTC2 */ - timer_w(2, offset & 3, data); - break; - - case 0x14: /* ENV2 */ - env_w(2, offset & 3, data); - break; - } -} - -//------------------------------------------------- -// sound_stream_update - handle a stream update -//------------------------------------------------- - -void usb_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) -{ - stream_sample_t *dest = outputs[0]; - - /* iterate over samples */ - while (samples--) - { - double noiseval; - double sample = 0; - int group; - int step; - - - /*---------------- - Noise Source - ---------------- - - RC - MM5837 ---> FILTER ---> CR FILTER ---> 3.2x AMP ---> NOISE - LADDER - */ - - /* update the noise source */ - for (step = USB_2MHZ_CLOCK / SAMPLE_RATE; step >= m_noise_subcount; step -= m_noise_subcount) - { - m_noise_shift = (m_noise_shift << 1) | (((m_noise_shift >> 13) ^ (m_noise_shift >> 16)) & 1); - m_noise_state = (m_noise_shift >> 16) & 1; - m_noise_subcount = USB_2MHZ_CLOCK / MM5837_CLOCK; - } - m_noise_subcount -= step; - - /* update the filtered noise value -- this is just an approximation to the pink noise filter */ - /* being applied on the PCB, but it sounds pretty close */ - m_noise_filters[0].capval = 0.99765 * m_noise_filters[0].capval + m_noise_state * 0.0990460; - m_noise_filters[1].capval = 0.96300 * m_noise_filters[1].capval + m_noise_state * 0.2965164; - m_noise_filters[2].capval = 0.57000 * m_noise_filters[2].capval + m_noise_state * 1.0526913; - noiseval = m_noise_filters[0].capval + m_noise_filters[1].capval + m_noise_filters[2].capval + m_noise_state * 0.1848; - - /* final output goes through a CR filter; the scaling factor is arbitrary to get the noise to the */ - /* correct relative volume */ - noiseval = m_noise_filters[4].step_cr(noiseval); - noiseval *= 0.075; - - /* there are 3 identical groups of circuits, each with its own 8253 */ - for (group = 0; group < 3; group++) - { - timer8253 *g = &m_timer_group[group]; - double chan0, chan1, chan2, mix; - - - /*------------- - Channel 0 - ------------- - - 8253 CR AD7524 - OUT0 ---> FILTER ---> BUFFER---> VRef ---> 100k ---> mix - */ - - /* channel 0 clocks with the PCS clock */ - for (step = USB_2MHZ_CLOCK / SAMPLE_RATE; step >= g->chan[0].subcount; step -= g->chan[0].subcount) - { - g->chan[0].subcount = USB_2MHZ_CLOCK / USB_PCS_CLOCK; - g->chan[0].gate = 1; - g->chan[0].clock(); - } - g->chan[0].subcount -= step; - - /* channel 0 is mixed in with a resistance of 100k */ - chan0 = g->chan_filter[0].step_cr(g->chan[0].output) * g->env[0] * (1.0/100.0); - - - /*------------- - Channel 1 - ------------- - - 8253 CR AD7524 - OUT1 ---> FILTER ---> BUFFER---> VRef ---> 100k ---> mix - */ - - /* channel 1 clocks with the PCS clock */ - for (step = USB_2MHZ_CLOCK / SAMPLE_RATE; step >= g->chan[1].subcount; step -= g->chan[1].subcount) - { - g->chan[1].subcount = USB_2MHZ_CLOCK / USB_PCS_CLOCK; - g->chan[1].gate = 1; - g->chan[1].clock(); - } - g->chan[1].subcount -= step; - - /* channel 1 is mixed in with a resistance of 100k */ - chan1 = g->chan_filter[1].step_cr(g->chan[1].output) * g->env[1] * (1.0/100.0); - - - /*------------- - Channel 2 - ------------- - - If timer_mode == 0: - - SWITCHED AD7524 - NOISE ---> RC ---> 1.56x AMP ---> INVERTER ---> VRef ---> 33k ---> mix - FILTERS - - If timer mode == 1: - - AD7524 SWITCHED - NOISE ---> INVERTER ---> VRef ---> 33k ---> mix ---> INVERTER ---> RC ---> 1.56x AMP ---> finalmix - FILTERS - */ - - /* channel 2 clocks with the 2MHZ clock and triggers with the GOS clock */ - for (step = 0; step < USB_2MHZ_CLOCK / SAMPLE_RATE; step++) - { - if (g->chan[2].subcount-- == 0) - { - g->chan[2].subcount = USB_2MHZ_CLOCK / USB_GOS_CLOCK / 2 - 1; - g->chan[2].gate = !g->chan[2].gate; - } - g->chan[2].clock(); - } - - /* the exponents for the gate filters are determined by channel 2's output */ - g->gate1.exponent = m_gate_rc1_exp[g->chan[2].output]; - g->gate2.exponent = m_gate_rc2_exp[g->chan[2].output]; - - /* based on the envelope mode, we do one of two things with source 2 */ - if (g->config == 0) - { - chan2 = g->gate2.step_rc(g->gate1.step_rc(noiseval)) * -1.56 * g->env[2] * (1.0/33.0); - mix = chan0 + chan1 + chan2; - } - else - { - chan2 = -noiseval * g->env[2] * (1.0/33.0); - mix = chan0 + chan1 + chan2; - mix = g->gate2.step_rc(g->gate1.step_rc(-mix)) * 1.56; - } - - /* accumulate the sample */ - sample += mix; - } - - - /*------------- - Final mix - ------------- - - INPUTS - EQUAL ---> 1.2x INVERTER ---> CR FILTER ---> out - WEIGHT - - */ - *dest++ = 4000 * m_final_filter.step_cr(sample); - } -} - - -/************************************* - * - * USB address maps - * - *************************************/ - -void usb_sound_device::usb_map(address_map &map) -{ - map(0x0000, 0x0fff).ram().share("pgmram"); -} - -void usb_sound_device::usb_portmap(address_map &map) -{ - map(0x00, 0xff).rw(FUNC(usb_sound_device::workram_r), FUNC(usb_sound_device::workram_w)).share("workram"); -} - - -//------------------------------------------------- -// device_add_mconfig - add device configuration -//------------------------------------------------- - -void usb_sound_device::device_add_mconfig(machine_config &config) -{ - /* CPU for the usb board */ - I8035(config, m_ourcpu, USB_MASTER_CLOCK); /* divide by 15 in CPU */ - m_ourcpu->set_addrmap(AS_PROGRAM, &usb_sound_device::usb_map); - m_ourcpu->set_addrmap(AS_IO, &usb_sound_device::usb_portmap); - m_ourcpu->p1_in_cb().set(FUNC(usb_sound_device::p1_r)); - m_ourcpu->p1_out_cb().set(FUNC(usb_sound_device::p1_w)); - m_ourcpu->p2_out_cb().set(FUNC(usb_sound_device::p2_w)); - m_ourcpu->t1_in_cb().set(FUNC(usb_sound_device::t1_r)); - - TIMER(config, "usb_timer", 0).configure_periodic( - FUNC(usb_sound_device::increment_t1_clock_timer_cb), - attotime::from_hz(USB_2MHZ_CLOCK / 256)); -} - - -DEFINE_DEVICE_TYPE(SEGAUSBROM, usb_rom_sound_device, "segausbrom", "Sega Universal Sound Board with ROM") - -usb_rom_sound_device::usb_rom_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : usb_sound_device(mconfig, SEGAUSBROM, tag, owner, clock) -{ -} - -void usb_sound_device::usb_map_rom(address_map &map) -{ - map(0x0000, 0x0fff).rom().region(":usbcpu", 0); -} - -void usb_rom_sound_device::device_add_mconfig(machine_config &config) -{ - usb_sound_device::device_add_mconfig(config); - - /* CPU for the usb board */ - m_ourcpu->set_addrmap(AS_PROGRAM, &usb_rom_sound_device::usb_map_rom); -} diff --git a/src/mame/audio/segasnd.h b/src/mame/audio/segasnd.h deleted file mode 100644 index 75fa94252bb..00000000000 --- a/src/mame/audio/segasnd.h +++ /dev/null @@ -1,214 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Aaron Giles -/************************************************************************* - - Sega g80 common sound hardware - -*************************************************************************/ -#ifndef MAME_AUDIO_SEGASND_H -#define MAME_AUDIO_SEGASND_H - -#pragma once - -#include "cpu/mcs48/mcs48.h" -#include "machine/timer.h" - -class segag80snd_common : public driver_device { -public: - segag80snd_common(const machine_config &mconfig, device_type type, const char *tag) : - driver_device(mconfig, type, tag), - m_audiocpu(*this, "audiocpu") - { } - - virtual ~segag80snd_common() = default; - - DECLARE_WRITE_LINE_MEMBER(segaspeech_int_w); - - void sega_speech_board(machine_config &config); - -protected: - void speech_map(address_map &map); - void speech_portmap(address_map &map); - - optional_device m_audiocpu; -}; - -#define SEGASND_SEGASPEECH_REGION "segaspeech:speech" - -class speech_sound_device : public device_t, public device_sound_interface -{ -public: - speech_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); - - auto int_cb() { return m_int_cb.bind(); } - - void data_w(uint8_t data); - void control_w(uint8_t data); - - DECLARE_READ_LINE_MEMBER( t0_r ); - DECLARE_READ_LINE_MEMBER( t1_r ); - uint8_t p1_r(); - uint8_t rom_r(offs_t offset); - void p1_w(uint8_t data); - void p2_w(uint8_t data); - - DECLARE_WRITE_LINE_MEMBER(drq_w); - -protected: - // device-level overrides - virtual void device_start() override; - - // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; - -private: - devcb_write_line m_int_cb; - required_memory_region m_speech; - - // internal state - u8 m_drq; - u8 m_latch; - u8 m_t0; - u8 m_p2; - - TIMER_CALLBACK_MEMBER( delayed_speech_w ); -}; - -DECLARE_DEVICE_TYPE(SEGASPEECH, speech_sound_device) - -class usb_sound_device : public device_t, public device_sound_interface -{ -public: - template usb_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, T &&maincpu_tag) - : usb_sound_device(mconfig, tag, owner, clock) - { - m_maincpu.set_tag(maincpu_tag); - } - - usb_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); - - uint8_t status_r(); - void data_w(uint8_t data); - uint8_t ram_r(offs_t offset); - void ram_w(offs_t offset, uint8_t data); - - uint8_t workram_r(offs_t offset); - void workram_w(offs_t offset, uint8_t data); - - TIMER_DEVICE_CALLBACK_MEMBER( increment_t1_clock_timer_cb ); - - void usb_map(address_map &map); - void usb_map_rom(address_map &map); - void usb_portmap(address_map &map); - -protected: - usb_sound_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock); - - // device-level overrides - virtual void device_add_mconfig(machine_config &config) override; - virtual void device_start() override; - virtual void device_reset() override; - - // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; - - required_device m_ourcpu; - required_device m_maincpu; - -private: - struct g80_filter_state - { - g80_filter_state() { } - - void configure(double r, double c); - double step_rc(double input); - double step_cr(double input); - - double capval = 0.0; // current capacitor value - double exponent = 0.0; // constant exponent - }; - - - struct timer8253 - { - struct channel - { - channel() { } - - void clock(); - - u8 holding = 0; // holding until counts written? */ - u8 latchmode = 0; // latching mode */ - u8 latchtoggle = 0; // latching state */ - u8 clockmode = 0; // clocking mode */ - u8 bcdmode = 0; // BCD mode? */ - u8 output = 0; // current output value */ - u8 lastgate = 0; // previous gate value */ - u8 gate = 0; // current gate value */ - u8 subcount = 0; // subcount (2MHz clocks per input clock) */ - u16 count = 0; // initial count - u16 remain = 0; // current down counter value - }; - - timer8253() : env{ 0.0, 0.0, 0.0 } { } - - channel chan[3]; // three channels' worth of information - double env[3]; // envelope value for each channel - g80_filter_state chan_filter[2]; // filter states for the first two channels - g80_filter_state gate1; // first RC filter state - g80_filter_state gate2; // second RC filter state - u8 config = 0; // configuration for this timer - }; - - // internal state - sound_stream *m_stream; // output stream - u8 m_in_latch; // input latch - u8 m_out_latch; // output latch - u8 m_last_p2_value; // current P2 output value - optional_shared_ptr m_program_ram; // pointer to program RAM - required_shared_ptr m_work_ram; // pointer to work RAM - u8 m_work_ram_bank; // currently selected work RAM bank - u8 m_t1_clock; // T1 clock value - u8 m_t1_clock_mask; // T1 clock mask (configured via jumpers) - timer8253 m_timer_group[3]; // 3 groups of timers - u8 m_timer_mode[3]; // mode control for each group - u32 m_noise_shift; - u8 m_noise_state; - u8 m_noise_subcount; - double m_gate_rc1_exp[2]; - double m_gate_rc2_exp[2]; - g80_filter_state m_final_filter; - g80_filter_state m_noise_filters[5]; - - TIMER_CALLBACK_MEMBER( delayed_usb_data_w ); - void timer_w(int which, u8 offset, u8 data); - void env_w(int which, u8 offset, u8 data); - - uint8_t p1_r(); - void p1_w(uint8_t data); - void p2_w(uint8_t data); - DECLARE_READ_LINE_MEMBER( t1_r ); -}; - -DECLARE_DEVICE_TYPE(SEGAUSB, usb_sound_device) - - -class usb_rom_sound_device : public usb_sound_device -{ -public: - template usb_rom_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, T &&maincpu_tag) - : usb_rom_sound_device(mconfig, tag, owner, clock) - { - m_maincpu.set_tag(maincpu_tag); - } - - usb_rom_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); - -protected: - // device-level overrides - virtual void device_add_mconfig(machine_config &config) override; -}; - -DECLARE_DEVICE_TYPE(SEGAUSBROM, usb_rom_sound_device) - -#endif // MAME_AUDIO_SEGASND_H diff --git a/src/mame/audio/segaspeech.cpp b/src/mame/audio/segaspeech.cpp new file mode 100644 index 00000000000..d158b0b7d2e --- /dev/null +++ b/src/mame/audio/segaspeech.cpp @@ -0,0 +1,217 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + segaspeech.cpp + + Sega Speech board for early G-80 games. + +***************************************************************************/ + +#include "emu.h" +#include "segaspeech.h" + +#include "sound/sp0250.h" +#include "includes/segag80r.h" +#include "includes/segag80v.h" +#include "audio/nl_segaspeech.h" + +#define VERBOSE 0 +#include "logmacro.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define SPEECH_MASTER_CLOCK 3120000 + + + +/*************************************************************************** + SPEECH BOARD +***************************************************************************/ + +DEFINE_DEVICE_TYPE(SEGA_SPEECH_BOARD, sega_speech_device, "sega_speech_device", "Sega Speech Sound Board") + +sega_speech_device::sega_speech_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + device_t(mconfig, SEGA_SPEECH_BOARD, tag, owner, clock), + device_mixer_interface(mconfig, *this), + m_speech(*this, "data"), + m_cpu(*this, "cpu"), + m_drq(0), + m_latch(0), + m_t0(0), + m_p2(0) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sega_speech_device::device_start() +{ + save_item(NAME(m_latch)); + save_item(NAME(m_t0)); + save_item(NAME(m_p2)); + save_item(NAME(m_drq)); +} + + +/************************************* + * + * i8035 port accessors + * + *************************************/ + +READ_LINE_MEMBER( sega_speech_device::t0_r ) +{ + return m_t0; +} + +READ_LINE_MEMBER( sega_speech_device::t1_r ) +{ +//printf("%s: t1_r=%d\n", machine().scheduler().time().as_string(), m_drq); + return m_drq; +} + +uint8_t sega_speech_device::p1_r() +{ + return m_latch & 0x7f; +} + +uint8_t sega_speech_device::rom_r(offs_t offset) +{ + return m_speech->base()[0x100 * (m_p2 & 0x3f) + offset]; +} + +void sega_speech_device::p1_w(uint8_t data) +{ + if (!(data & 0x80)) + m_t0 = 0; +} + +void sega_speech_device::p2_w(uint8_t data) +{ + m_p2 = data; +} + + + +/************************************* + * + * i8035 port accessors + * + *************************************/ + +WRITE_LINE_MEMBER(sega_speech_device::drq_w) +{ +//printf("%s: DRQ=%d\n", machine().scheduler().time().as_string(), state); + m_drq = (state == ASSERT_LINE); +} + + + +/************************************* + * + * External access + * + *************************************/ + +TIMER_CALLBACK_MEMBER( sega_speech_device::delayed_speech_w ) +{ + int data = param; + u8 old = m_latch; + + /* all 8 bits are latched */ + m_latch = data; + + /* the high bit goes directly to the INT line */ + m_cpu->set_input_line(INPUT_LINE_IRQ0, (data & 0x80) ? CLEAR_LINE : ASSERT_LINE); + + /* a clock on the high bit clocks a 1 into T0 */ + if (!(old & 0x80) && (data & 0x80)) + m_t0 = 1; +} + + +void sega_speech_device::data_w(uint8_t data) +{ + machine().scheduler().synchronize(timer_expired_delegate(FUNC(sega_speech_device::delayed_speech_w), this), data); +} + + +void sega_speech_device::control_w(uint8_t data) +{ + // bit 3 controls channel #1 of a CD4053 to enable/disable + // speech output + this->set_input_gain(0, BIT(data, 3) ? 1.0 : 0.0); + + // bit 4 controls channel #2, but the source is unconnected + + // bit 5 controls channel #3, which comes from off-board + // and has a pot to control the incoming volume; at least on + // Star Trek, this is the audio from the Universal Sound Board + this->set_input_gain(1, BIT(data, 5) ? 1.0 : 0.0); + + LOG("Speech control = %X\n", data); +} + + +/************************************* + * + * Speech board address maps + * + *************************************/ + +void sega_speech_device::speech_map(address_map &map) +{ + map(0x0000, 0x07ff).mirror(0x0800).rom(); +} + + +void sega_speech_device::speech_portmap(address_map &map) +{ + map(0x00, 0xff).r(FUNC(sega_speech_device::rom_r)); + map(0x00, 0xff).w("sp0250", FUNC(sp0250_device::write)); +} + + +/************************************* + * + * Speech board machine drivers + * + *************************************/ + +void sega_speech_device::device_add_mconfig(machine_config &config) +{ + // CPU for the speech board + i8035_device &audiocpu(I8035(config, "cpu", SPEECH_MASTER_CLOCK)); // divide by 15 in CPU + audiocpu.set_addrmap(AS_PROGRAM, &sega_speech_device::speech_map); + audiocpu.set_addrmap(AS_IO, &sega_speech_device::speech_portmap); + audiocpu.p1_in_cb().set(FUNC(sega_speech_device::p1_r)); + audiocpu.p1_out_cb().set(FUNC(sega_speech_device::p1_w)); + audiocpu.p2_out_cb().set(FUNC(sega_speech_device::p2_w)); + audiocpu.t0_in_cb().set(FUNC(sega_speech_device::t0_r)); + audiocpu.t1_in_cb().set(FUNC(sega_speech_device::t1_r)); + + // speech chip + sp0250_device &speech(SP0250(config, "sp0250", SPEECH_MASTER_CLOCK)); + speech.drq().set(FUNC(sega_speech_device::drq_w)); +#if (ENABLE_NETLIST_FILTERING) + speech.set_pwm_mode(); + speech.add_route(ALL_OUTPUTS, "sound_nl", 1.0, 0); + + // netlist filtering + NETLIST_SOUND(config, "sound_nl", SPEECH_MASTER_CLOCK/2) + .set_source(NETLIST_NAME(segaspeech)) + .add_route(ALL_OUTPUTS, *this, 1.0); + + NETLIST_STREAM_INPUT(config, "sound_nl:cin0", 0, "I_SP0250.IN").set_mult_offset(1.0 / 32767.0, 0); + + NETLIST_STREAM_OUTPUT(config, "sound_nl:cout0", 0, "OUTPUT").set_mult_offset(75000.0, 0.0); +#else + speech.add_route(ALL_OUTPUTS, *this, 1.0); +#endif +} diff --git a/src/mame/audio/segaspeech.h b/src/mame/audio/segaspeech.h new file mode 100644 index 00000000000..a9905ce05f5 --- /dev/null +++ b/src/mame/audio/segaspeech.h @@ -0,0 +1,61 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/************************************************************************* + + Sega speech board + +*************************************************************************/ +#ifndef MAME_AUDIO_SEGASPEECH_H +#define MAME_AUDIO_SEGASPEECH_H + +#pragma once + +#define ENABLE_NETLIST_FILTERING (0) + +#include "cpu/mcs48/mcs48.h" +#include "machine/netlist.h" +#include "machine/timer.h" + +class sega_speech_device : public device_t, public device_mixer_interface +{ +public: + sega_speech_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + +// auto int_cb() { return m_int_cb.bind(); } + + void data_w(uint8_t data); + void control_w(uint8_t data); + +protected: + // device-level overrides + virtual void device_add_mconfig(machine_config &config) override; + virtual void device_start() override; + + DECLARE_READ_LINE_MEMBER( t0_r ); + DECLARE_READ_LINE_MEMBER( t1_r ); + uint8_t p1_r(); + uint8_t rom_r(offs_t offset); + void p1_w(uint8_t data); + void p2_w(uint8_t data); + + DECLARE_WRITE_LINE_MEMBER(drq_w); + +private: + void speech_map(address_map &map); + void speech_portmap(address_map &map); + + required_memory_region m_speech; + required_device m_cpu; + + // internal state + u8 m_drq; + u8 m_latch; + u8 m_t0; + u8 m_p2; + + TIMER_CALLBACK_MEMBER( delayed_speech_w ); +}; + +DECLARE_DEVICE_TYPE(SEGA_SPEECH_BOARD, sega_speech_device) + +#endif // MAME_AUDIO_SEGASPEECH_H diff --git a/src/mame/audio/segausb.cpp b/src/mame/audio/segausb.cpp new file mode 100644 index 00000000000..59e1c226854 --- /dev/null +++ b/src/mame/audio/segausb.cpp @@ -0,0 +1,859 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + segausb.cpp + + Sega Universal Sound Board. + +***************************************************************************/ + +#include "emu.h" +#include "segausb.h" +#include "nl_segausb.h" + +#include "includes/segag80r.h" +#include "includes/segag80v.h" + +#include + + +#define VERBOSE 0 +#include "logmacro.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define USB_MASTER_CLOCK 6000000 +#define USB_2MHZ_CLOCK (USB_MASTER_CLOCK/3) +#define USB_PCS_CLOCK (USB_2MHZ_CLOCK/2) +#define USB_GOS_CLOCK (USB_2MHZ_CLOCK/16/4) +#define MM5837_CLOCK 100000 + + +/*************************************************************************** + UNIVERSAL SOUND BOARD +***************************************************************************/ + +DEFINE_DEVICE_TYPE(SEGAUSB, usb_sound_device, "segausb", "Sega Universal Sound Board") + +usb_sound_device::usb_sound_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, type, tag, owner, clock), + device_mixer_interface(mconfig, *this), + m_in_latch(0), + m_out_latch(0), + m_last_p2_value(0), + m_program_ram(*this, "pgmram"), + m_work_ram(*this, "workram"), + m_work_ram_bank(0), + m_t1_clock(0), + m_t1_clock_mask(0), + m_ourcpu(*this, "ourcpu"), + m_maincpu(*this, finder_base::DUMMY_TAG), +#if (ENABLE_SEGAUSB_NETLIST) + m_pit(*this, "pit_%u", 0), + m_nl_dac0(*this, "sound_nl:dac0_%u", 0), + m_nl_sel0(*this, "sound_nl:sel0"), + m_nl_pit0_out(*this, "sound_nl:pit0_out%u", 0), + m_nl_dac1(*this, "sound_nl:dac1_%u", 0), + m_nl_sel1(*this, "sound_nl:sel1"), + m_nl_pit1_out(*this, "sound_nl:pit1_out%u", 0), + m_nl_dac2(*this, "sound_nl:dac2_%u", 0), + m_nl_sel2(*this, "sound_nl:sel2"), + m_nl_pit2_out(*this, "sound_nl:pit2_out%u", 0), + m_gos_clock(0) +#else + m_stream(nullptr), + m_noise_shift(0), + m_noise_state(0), + m_noise_subcount(0) +#endif +{ +} + +usb_sound_device::usb_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : usb_sound_device(mconfig, SEGAUSB, tag, owner, clock) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void usb_sound_device::device_start() +{ + // register for save states + save_item(NAME(m_in_latch)); + save_item(NAME(m_out_latch)); + save_item(NAME(m_last_p2_value)); + save_item(NAME(m_work_ram_bank)); + save_item(NAME(m_t1_clock)); + +#if (ENABLE_SEGAUSB_NETLIST) + + for (int index = 0; index < 3; index++) + { + m_pit[index]->write_gate0(1); + m_pit[index]->write_gate1(1); + m_pit[index]->write_gate2(1); + } + save_item(NAME(m_gos_clock)); + +#else + + m_stream = stream_alloc(0, 1, USB_2MHZ_CLOCK); + + m_noise_shift = 0x15555; + + for (timer8253 &group : m_timer_group) + { + group.chan_filter[0].configure(10e3, 1e-6); + group.chan_filter[1].configure(10e3, 1e-6); + group.gate1.configure(100e3, 0.01e-6); + group.gate2.configure(2 * 100e3, 0.01e-6); + } + + g80_filter_state temp; + temp.configure(100e3, 0.01e-6); + m_gate_rc1_exp[0] = temp.exponent; + temp.configure(1e3, 0.01e-6); + m_gate_rc1_exp[1] = temp.exponent; + temp.configure(2 * 100e3, 0.01e-6); + m_gate_rc2_exp[0] = temp.exponent; + temp.configure(2 * 1e3, 0.01e-6); + m_gate_rc2_exp[1] = temp.exponent; + + m_noise_filters[0].configure(2.7e3 + 2.7e3, 1.0e-6); + m_noise_filters[1].configure(2.7e3 + 1e3, 0.30e-6); + m_noise_filters[2].configure(2.7e3 + 270, 0.15e-6); + m_noise_filters[3].configure(2.7e3 + 0, 0.082e-6); + m_noise_filters[4].configure(33e3, 0.1e-6); + + m_final_filter.configure(100e3, 4.7e-6); + + for (int tgroup = 0; tgroup < 3; tgroup++) + { + timer8253 &group = m_timer_group[tgroup]; + for (int tchan = 0; tchan < 3; tchan++) + { + timer8253::channel &channel = group.chan[tchan]; + save_item(NAME(channel.holding), tgroup * 3 + tchan); + save_item(NAME(channel.latchmode), tgroup * 3 + tchan); + save_item(NAME(channel.latchtoggle), tgroup * 3 + tchan); + save_item(NAME(channel.clockmode), tgroup * 3 + tchan); + save_item(NAME(channel.bcdmode), tgroup * 3 + tchan); + save_item(NAME(channel.output), tgroup * 3 + tchan); + save_item(NAME(channel.lastgate), tgroup * 3 + tchan); + save_item(NAME(channel.gate), tgroup * 3 + tchan); + save_item(NAME(channel.subcount), tgroup * 3 + tchan); + save_item(NAME(channel.count), tgroup * 3 + tchan); + save_item(NAME(channel.remain), tgroup * 3 + tchan); + } + save_item(NAME(group.env), tgroup); + save_item(NAME(group.chan_filter[0].capval), tgroup); + save_item(NAME(group.chan_filter[1].capval), tgroup); + save_item(NAME(group.gate1.capval), tgroup); + save_item(NAME(group.gate2.capval), tgroup); + save_item(NAME(group.config), tgroup); + } + + save_item(NAME(m_timer_mode)); + save_item(NAME(m_noise_shift)); + save_item(NAME(m_noise_state)); + save_item(NAME(m_noise_subcount)); + save_item(NAME(m_final_filter.capval)); + save_item(NAME(m_noise_filters[0].capval)); + save_item(NAME(m_noise_filters[1].capval)); + save_item(NAME(m_noise_filters[2].capval)); + save_item(NAME(m_noise_filters[3].capval)); + save_item(NAME(m_noise_filters[4].capval)); + +#endif +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void usb_sound_device::device_reset() +{ + // halt the USB CPU at reset time + m_ourcpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); + + // start the clock timer + m_t1_clock_mask = 0x10; +} + + +/************************************* + * + * Initialization/reset + * + *************************************/ + +TIMER_DEVICE_CALLBACK_MEMBER( usb_sound_device::increment_t1_clock_timer_cb ) +{ + // only increment if it is not being forced clear + if (!(m_last_p2_value & 0x80)) + m_t1_clock++; +} + + +/************************************* + * + * External access + * + *************************************/ + +u8 usb_sound_device::status_r() +{ + LOG("%s:usb_data_r = %02X\n", machine().describe_context(), (m_out_latch & 0x81) | (m_in_latch & 0x7e)); + + m_maincpu->adjust_icount(-200); + + // only bits 0 and 7 are controlled by the I8035; the remaining + // bits 1-6 reflect the current input latch values + return (m_out_latch & 0x81) | (m_in_latch & 0x7e); +} + + +TIMER_CALLBACK_MEMBER( usb_sound_device::delayed_usb_data_w ) +{ + // look for rising/falling edges of bit 7 to control the RESET line + int data = param; + m_ourcpu->set_input_line(INPUT_LINE_RESET, (data & 0x80) ? ASSERT_LINE : CLEAR_LINE); + + // if the CLEAR line is set, the low 7 bits of the input are ignored + if ((m_last_p2_value & 0x40) == 0) + data &= ~0x7f; + + // update the effective input latch + m_in_latch = data; +} + + +void usb_sound_device::data_w(u8 data) +{ + LOG("%s:usb_data_w = %02X\n", machine().describe_context(), data); + machine().scheduler().synchronize(timer_expired_delegate(FUNC(usb_sound_device::delayed_usb_data_w), this), data); + + // boost the interleave so that sequences can be sent + machine().scheduler().boost_interleave(attotime::zero, attotime::from_usec(250)); +} + + +u8 usb_sound_device::ram_r(offs_t offset) +{ + return m_program_ram[offset]; +} + + +void usb_sound_device::ram_w(offs_t offset, u8 data) +{ + if (m_in_latch & 0x80) + m_program_ram[offset] = data; + else + LOG("%s:sega_usb_ram_w(%03X) = %02X while /LOAD disabled\n", machine().describe_context(), offset, data); +} + + + +/************************************* + * + * I8035 port accesses + * + *************************************/ + +u8 usb_sound_device::p1_r() +{ + // bits 0-6 are inputs and map to bits 0-6 of the input latch + if ((m_in_latch & 0x7f) != 0) + LOG("%s: P1 read = %02X\n", machine().describe_context(), m_in_latch & 0x7f); + return m_in_latch & 0x7f; +} + + +void usb_sound_device::p1_w(u8 data) +{ + // bit 7 maps to bit 0 on the output latch + m_out_latch = (m_out_latch & 0xfe) | (data >> 7); + LOG("%s: P1 write = %02X\n", machine().describe_context(), data); +} + + +void usb_sound_device::p2_w(u8 data) +{ + u8 old = m_last_p2_value; + m_last_p2_value = data; + + // low 2 bits control the bank of work RAM we are addressing + m_work_ram_bank = data & 3; + + // bit 6 controls the "ready" bit output to the host + // it also clears the input latch from the host (active low) + m_out_latch = ((data & 0x40) << 1) | (m_out_latch & 0x7f); + if ((data & 0x40) == 0) + m_in_latch = 0; + + // bit 7 controls the reset on the upper counter at U33 + if ((old & 0x80) && !(data & 0x80)) + m_t1_clock = 0; + + LOG("%s: P2 write -> bank=%d ready=%d clock=%d\n", machine().describe_context(), data & 3, (data >> 6) & 1, (data >> 7) & 1); +} + + +READ_LINE_MEMBER( usb_sound_device::t1_r ) +{ + // T1 returns 1 based on the value of the T1 clock; the exact + // pattern is determined by one or more jumpers on the board. + return (m_t1_clock & m_t1_clock_mask) != 0; +} + + + +/************************************* + * + * Sound generation + * + *************************************/ + +#if (!ENABLE_SEGAUSB_NETLIST) + +inline void usb_sound_device::g80_filter_state::configure(double r, double c) +{ + capval = 0.0; + exponent = 1.0 - std::exp(-1.0 / (r * c * USB_2MHZ_CLOCK)); +} + +inline void usb_sound_device::timer8253::channel::clock() +{ + u8 const old_lastgate = lastgate; + + // update the gate + lastgate = gate; + + // if we're holding, skip + if (holding) + return; + + // switch off the clock mode + switch (clockmode) + { + // oneshot; waits for trigger to restart + case 1: + if (!old_lastgate && gate) + { + output = 0; + remain = count; + } + else + { + if (--remain == 0) + output = 1; + } + break; + + // square wave: counts down by 2 and toggles output + case 3: + remain = (remain - 1) & ~1; + if (remain == 0) + { + output ^= 1; + remain = count; + } + break; + } +} + + +/************************************* + * + * USB timer and envelope controls + * + *************************************/ + +void usb_sound_device::timer_w(int which, u8 offset, u8 data) +{ + timer8253 &group = m_timer_group[which]; + + m_stream->update(); + + // switch off the offset + switch (offset) + { + case 0: + case 1: + case 2: + { + timer8253::channel &ch = group.chan[offset]; + bool was_holding = ch.holding; + + // based on the latching mode + switch (ch.latchmode) + { + case 1: // low word only + ch.count = data; + ch.holding = false; + break; + + case 2: // high word only + ch.count = data << 8; + ch.holding = false; + break; + + case 3: // low word followed by high word + if (ch.latchtoggle == 0) + { + ch.count = (ch.count & 0xff00) | (data & 0x00ff); + ch.latchtoggle = 1; + } + else + { + ch.count = (ch.count & 0x00ff) | (data << 8); + ch.holding = false; + ch.latchtoggle = 0; + } + break; + } + + // if we're not holding, load the initial count for some modes + if (was_holding && !ch.holding) + ch.remain = 1; + break; + } + + case 3: + // break out the components + if (((data & 0xc0) >> 6) < 3) + { + timer8253::channel &ch = group.chan[(data & 0xc0) >> 6]; + + // extract the bits + ch.holding = true; + ch.latchmode = (data >> 4) & 3; + ch.clockmode = (data >> 1) & 7; + ch.bcdmode = (data >> 0) & 1; + ch.latchtoggle = 0; + ch.output = (ch.clockmode == 1); + } + break; + } +} + + +void usb_sound_device::env_w(int which, u8 offset, u8 data) +{ + timer8253 &group = m_timer_group[which]; + + m_stream->update(); + + if (offset < 3) + group.env[offset] = double(data); + else + group.config = data & 1; +} + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void usb_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + stream_sample_t *dest = outputs[0]; + + // iterate over samples + while (samples--) + { + /*---------------- + Noise Source + ---------------- + + RC + MM5837 ---> FILTER ---> CR FILTER ---> 3.2x AMP ---> NOISE + LADDER + */ + + // update the noise source + if (m_noise_subcount-- == 0) + { + m_noise_shift = (m_noise_shift << 1) | (((m_noise_shift >> 13) ^ (m_noise_shift >> 16)) & 1); + m_noise_state = (m_noise_shift >> 16) & 1; + m_noise_subcount += USB_2MHZ_CLOCK / MM5837_CLOCK; + } + + // update the filtered noise value -- this is just an approximation to the pink noise filter + // being applied on the PCB, but it sounds pretty close + m_noise_filters[0].capval = 0.99765 * m_noise_filters[0].capval + m_noise_state * 0.0990460; + m_noise_filters[1].capval = 0.96300 * m_noise_filters[1].capval + m_noise_state * 0.2965164; + m_noise_filters[2].capval = 0.57000 * m_noise_filters[2].capval + m_noise_state * 1.0526913; + double noiseval = m_noise_filters[0].capval + m_noise_filters[1].capval + m_noise_filters[2].capval + m_noise_state * 0.1848; + + // final output goes through a CR filter; the scaling factor is arbitrary to get the noise to the + // correct relative volume + noiseval = m_noise_filters[4].step_cr(noiseval); + noiseval *= 0.075; + + // there are 3 identical groups of circuits, each with its own 8253 + double sample = 0; + for (int groupnum = 0; groupnum < 3; groupnum++) + { + timer8253 &group = m_timer_group[groupnum]; + + /*------------- + Channel 0 + ------------- + + 8253 CR AD7524 + OUT0 ---> FILTER ---> BUFFER---> VRef ---> 100k ---> mix + */ + + // channel 0 clocks with the PCS clock + if (group.chan[0].subcount-- == 0) + { + group.chan[0].subcount += USB_2MHZ_CLOCK / USB_PCS_CLOCK; + group.chan[0].gate = 1; + group.chan[0].clock(); + } + + // channel 0 is mixed in with a resistance of 100k + double chan0 = group.chan_filter[0].step_cr(group.chan[0].output) * group.env[0] * (1.0/100.0); + + /*------------- + Channel 1 + ------------- + + 8253 CR AD7524 + OUT1 ---> FILTER ---> BUFFER---> VRef ---> 100k ---> mix + */ + + // channel 1 clocks with the PCS clock + if (group.chan[1].subcount-- == 0) + { + group.chan[1].subcount += USB_2MHZ_CLOCK / USB_PCS_CLOCK; + group.chan[1].gate = 1; + group.chan[1].clock(); + } + + // channel 1 is mixed in with a resistance of 100k + double chan1 = group.chan_filter[1].step_cr(group.chan[1].output) * group.env[1] * (1.0/100.0); + + /*------------- + Channel 2 + ------------- + + If timer_mode == 0: + + SWITCHED AD7524 + NOISE ---> RC ---> 1.56x AMP ---> INVERTER ---> VRef ---> 33k ---> mix + FILTERS + + If timer mode == 1: + + AD7524 SWITCHED + NOISE ---> INVERTER ---> VRef ---> 33k ---> mix ---> INVERTER ---> RC ---> 1.56x AMP ---> finalmix + FILTERS + */ + + // channel 2 clocks with the 2MHZ clock and triggers with the GOS clock + if (group.chan[2].subcount-- == 0) + { + group.chan[2].subcount += USB_2MHZ_CLOCK / USB_GOS_CLOCK / 2; + group.chan[2].gate = !group.chan[2].gate; + } + group.chan[2].clock(); + + // the exponents for the gate filters are determined by channel 2's output + group.gate1.exponent = m_gate_rc1_exp[group.chan[2].output]; + group.gate2.exponent = m_gate_rc2_exp[group.chan[2].output]; + + // based on the envelope mode, we do one of two things with source 2 + double chan2, mix; + if (group.config == 0) + { + chan2 = group.gate2.step_rc(group.gate1.step_rc(noiseval)) * -1.56 * group.env[2] * (1.0/33.0); + mix = chan0 + chan1 + chan2; + } + else + { + chan2 = -noiseval * group.env[2] * (1.0/33.0); + mix = chan0 + chan1 + chan2; + mix = group.gate2.step_rc(group.gate1.step_rc(-mix)) * 1.56; + } + + // accumulate the sample + sample += mix; + } + + /*------------- + Final mix + ------------- + + INPUTS + EQUAL ---> 1.2x INVERTER ---> CR FILTER ---> out + WEIGHT + + */ + *dest++ = 3000 * m_final_filter.step_cr(sample); + } +} + +#endif + + + +/************************************* + * + * USB work RAM access + * + *************************************/ + +u8 usb_sound_device::workram_r(offs_t offset) +{ + offset += 256 * m_work_ram_bank; + return m_work_ram[offset]; +} + + +void usb_sound_device::workram_w(offs_t offset, u8 data) +{ + offset += 256 * m_work_ram_bank; + m_work_ram[offset] = data; + + // writes to the low 32 bytes go to various controls +#if (ENABLE_SEGAUSB_NETLIST) + + switch (offset) + { + case 0x00: // 8253 U41 + case 0x01: // 8253 U41 + case 0x02: // 8253 U41 + case 0x03: // 8253 U41 + if ((offset & 3) != 3) + printf("%s: 2.%d count=%d\n", machine().scheduler().time().as_string(), offset & 3, data); + m_pit[0]->write(offset & 3, data); + break; + + case 0x04: // ENV0 U26 + case 0x05: // ENV0 U25 + case 0x06: // ENV0 U24 + m_nl_dac0[offset & 3]->write(double(data) / 255.0); + break; + + case 0x07: // ENV0 U38B + m_nl_sel0->write(data & 1); + break; + + case 0x08: // 8253 U42 + case 0x09: // 8253 U42 + case 0x0a: // 8253 U42 + case 0x0b: // 8253 U42 + if ((offset & 3) != 3) + printf("%s: 2.%d count=%d\n", machine().scheduler().time().as_string(), offset & 3, data); + m_pit[1]->write(offset & 3, data); + break; + + case 0x0c: // ENV1 U12 + case 0x0d: // ENV1 U13 + case 0x0e: // ENV1 U14 + m_nl_dac1[offset & 3]->write(double(data) / 255.0); + break; + + case 0x0f: // ENV1 U2B + m_nl_sel1->write(data & 1); + break; + + case 0x10: // 8253 U43 + case 0x11: // 8253 U43 + case 0x12: // 8253 U43 + case 0x13: // 8253 U43 + if ((offset & 3) != 3) + printf("%s: 2.%d count=%d\n", machine().scheduler().time().as_string(), offset & 3, data); + m_pit[2]->write(offset & 3, data); + break; + + case 0x14: // ENV2 U27 + case 0x15: // ENV2 U28 + case 0x16: // ENV2 U29 + m_nl_dac2[offset & 3]->write(double(data) / 255.0); + break; + + case 0x17: // ENV2 U38B + m_nl_sel2->write(data & 1); + break; + } + +#else + + switch (offset & ~3) + { + case 0x00: // CTC0 + timer_w(0, offset & 3, data); + break; + + case 0x04: // ENV0 + env_w(0, offset & 3, data); + break; + + case 0x08: // CTC1 + timer_w(1, offset & 3, data); + break; + + case 0x0c: // ENV1 + env_w(1, offset & 3, data); + break; + + case 0x10: // CTC2 + timer_w(2, offset & 3, data); + break; + + case 0x14: // ENV2 + env_w(2, offset & 3, data); + break; + } + +#endif +} + + + +/************************************* + * + * USB address maps + * + *************************************/ + +void usb_sound_device::usb_map(address_map &map) +{ + map(0x0000, 0x0fff).ram().share("pgmram"); +} + +void usb_sound_device::usb_portmap(address_map &map) +{ + map(0x00, 0xff).rw(FUNC(usb_sound_device::workram_r), FUNC(usb_sound_device::workram_w)).share("workram"); +} + + +//------------------------------------------------- +// device_add_mconfig - add device configuration +//------------------------------------------------- + +#if (ENABLE_SEGAUSB_NETLIST) + +TIMER_DEVICE_CALLBACK_MEMBER( usb_sound_device::gos_timer ) +{ + // technically we should clock at 2x and toggle between states + // however, in practice this is just used to trigger a oneshot + // so we can halve the high frequency rate and just toggle both + // ways to initiate the countdown + m_pit[0]->write_gate2(0); + m_pit[1]->write_gate2(0); + m_pit[2]->write_gate2(0); + m_pit[0]->write_gate2(1); + m_pit[1]->write_gate2(1); + m_pit[2]->write_gate2(1); +} + +#endif + + +void usb_sound_device::device_add_mconfig(machine_config &config) +{ + // CPU for the usb board + I8035(config, m_ourcpu, USB_MASTER_CLOCK); // divide by 15 in CPU + m_ourcpu->set_addrmap(AS_PROGRAM, &usb_sound_device::usb_map); + m_ourcpu->set_addrmap(AS_IO, &usb_sound_device::usb_portmap); + m_ourcpu->p1_in_cb().set(FUNC(usb_sound_device::p1_r)); + m_ourcpu->p1_out_cb().set(FUNC(usb_sound_device::p1_w)); + m_ourcpu->p2_out_cb().set(FUNC(usb_sound_device::p2_w)); + m_ourcpu->t1_in_cb().set(FUNC(usb_sound_device::t1_r)); + + TIMER(config, "usb_timer", 0).configure_periodic( + FUNC(usb_sound_device::increment_t1_clock_timer_cb), + attotime::from_hz(USB_2MHZ_CLOCK / 256)); + +#if (ENABLE_SEGAUSB_NETLIST) + + TIMER(config, "gos_timer", 0).configure_periodic( + FUNC(usb_sound_device::gos_timer), attotime::from_hz(USB_GOS_CLOCK)); + + NETLIST_SOUND(config, "sound_nl", 48000) + .set_source(NETLIST_NAME(segausb)) + .add_route(ALL_OUTPUTS, *this, 1.0); + + // channel 0 inputs + NETLIST_ANALOG_INPUT(config, m_nl_dac0[0], "I_U26_DAC.IN"); + NETLIST_ANALOG_INPUT(config, m_nl_dac0[1], "I_U25_DAC.IN"); + NETLIST_ANALOG_INPUT(config, m_nl_dac0[2], "I_U24_DAC.IN"); + NETLIST_LOGIC_INPUT(config, m_nl_sel0, "I_U38B_SEL.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit0_out[0], "I_U41_OUT0.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit0_out[1], "I_U41_OUT1.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit0_out[2], "I_U41_OUT2.IN", 0); + + // channel 1 inputs + NETLIST_ANALOG_INPUT(config, m_nl_dac1[0], "I_U12_DAC.IN"); + NETLIST_ANALOG_INPUT(config, m_nl_dac1[1], "I_U13_DAC.IN"); + NETLIST_ANALOG_INPUT(config, m_nl_dac1[2], "I_U14_DAC.IN"); + NETLIST_LOGIC_INPUT(config, m_nl_sel1, "I_U2B_SEL.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit1_out[0], "I_U42_OUT0.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit1_out[1], "I_U42_OUT1.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit1_out[2], "I_U42_OUT2.IN", 0); + + // channel 2 inputs + NETLIST_ANALOG_INPUT(config, m_nl_dac2[0], "I_U27_DAC.IN"); + NETLIST_ANALOG_INPUT(config, m_nl_dac2[1], "I_U28_DAC.IN"); + NETLIST_ANALOG_INPUT(config, m_nl_dac2[2], "I_U29_DAC.IN"); + NETLIST_LOGIC_INPUT(config, m_nl_sel2, "I_U2A_SEL.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit2_out[0], "I_U43_OUT0.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit2_out[1], "I_U43_OUT1.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit2_out[2], "I_U43_OUT2.IN", 0); + + // final output + NETLIST_STREAM_OUTPUT(config, "sound_nl:cout0", 0, "OUTPUT").set_mult_offset(5000.0, 0.0); + + // configure the PIT clocks and gates + for (int index = 0; index < 3; index++) + { + PIT8253(config, m_pit[index], 0); + m_pit[index]->set_clk<0>(USB_PCS_CLOCK); + m_pit[index]->set_clk<1>(USB_PCS_CLOCK); + m_pit[index]->set_clk<2>(USB_2MHZ_CLOCK); + } + + // connect the PIT outputs to the netlist + m_pit[0]->out_handler<0>().set(m_nl_pit0_out[0], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[0]->out_handler<1>().set(m_nl_pit0_out[1], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[0]->out_handler<2>().set(m_nl_pit0_out[2], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[1]->out_handler<0>().set(m_nl_pit1_out[0], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[1]->out_handler<1>().set(m_nl_pit1_out[1], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[1]->out_handler<2>().set(m_nl_pit1_out[2], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[2]->out_handler<0>().set(m_nl_pit2_out[0], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[2]->out_handler<1>().set(m_nl_pit2_out[1], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[2]->out_handler<2>().set(m_nl_pit2_out[2], FUNC(netlist_mame_logic_input_device::write_line)); + +#endif +} + + +DEFINE_DEVICE_TYPE(SEGAUSBROM, usb_rom_sound_device, "segausbrom", "Sega Universal Sound Board with ROM") + +usb_rom_sound_device::usb_rom_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : usb_sound_device(mconfig, SEGAUSBROM, tag, owner, clock) +{ +} + +void usb_sound_device::usb_map_rom(address_map &map) +{ + map(0x0000, 0x0fff).rom().region(":usbcpu", 0); +} + +void usb_rom_sound_device::device_add_mconfig(machine_config &config) +{ + usb_sound_device::device_add_mconfig(config); + + // CPU for the usb board + m_ourcpu->set_addrmap(AS_PROGRAM, &usb_rom_sound_device::usb_map_rom); +} diff --git a/src/mame/audio/segausb.h b/src/mame/audio/segausb.h new file mode 100644 index 00000000000..afed3b688af --- /dev/null +++ b/src/mame/audio/segausb.h @@ -0,0 +1,192 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/************************************************************************* + + Sega g80 common sound hardware + +*************************************************************************/ +#ifndef MAME_AUDIO_SEGAUSB_H +#define MAME_AUDIO_SEGAUSB_H + +#pragma once + +#define ENABLE_SEGAUSB_NETLIST (0) + +#include "cpu/mcs48/mcs48.h" +#include "machine/netlist.h" +#include "machine/timer.h" +#include "netlist/nl_setup.h" +#include "nl_segausb.h" + +#if (ENABLE_SEGAUSB_NETLIST) +#include "machine/pit8253.h" +#endif + +class usb_sound_device : public device_t, public device_mixer_interface +{ +public: + template usb_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, T &&maincpu_tag) + : usb_sound_device(mconfig, tag, owner, clock) + { + m_maincpu.set_tag(maincpu_tag); + } + + usb_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + + uint8_t status_r(); + void data_w(uint8_t data); + uint8_t ram_r(offs_t offset); + void ram_w(offs_t offset, uint8_t data); + + uint8_t workram_r(offs_t offset); + void workram_w(offs_t offset, uint8_t data); + + TIMER_DEVICE_CALLBACK_MEMBER( increment_t1_clock_timer_cb ); + + void usb_map(address_map &map); + void usb_map_rom(address_map &map); + void usb_portmap(address_map &map); + +#if (ENABLE_SEGAUSB_NETLIST) + TIMER_DEVICE_CALLBACK_MEMBER( gos_timer ); +#endif + +protected: + + usb_sound_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock); + + // device-level overrides + virtual void device_add_mconfig(machine_config &config) override; + virtual void device_start() override; + virtual void device_reset() override; + +#if (!ENABLE_SEGAUSB_NETLIST) + // sound stream update overrides + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; +#endif + +private: + // internal state + u8 m_in_latch; // input latch + u8 m_out_latch; // output latch + u8 m_last_p2_value; // current P2 output value + optional_shared_ptr m_program_ram; // pointer to program RAM + required_shared_ptr m_work_ram; // pointer to work RAM + u8 m_work_ram_bank; // currently selected work RAM bank + u8 m_t1_clock; // T1 clock value + u8 m_t1_clock_mask; // T1 clock mask (configured via jumpers) + +protected: + // devices + required_device m_ourcpu; + required_device m_maincpu; + +private: +#if (ENABLE_SEGAUSB_NETLIST) + // PIT devices + required_device_array m_pit; + + // channel 0 + required_device_array m_nl_dac0; + required_device m_nl_sel0; + required_device_array m_nl_pit0_out; + + // channel 1 + required_device_array m_nl_dac1; + required_device m_nl_sel1; + required_device_array m_nl_pit1_out; + + // channel 2 + required_device_array m_nl_dac2; + required_device m_nl_sel2; + required_device_array m_nl_pit2_out; + + u8 m_gos_clock; // state of the GOD clock +#else + struct g80_filter_state + { + g80_filter_state() { } + + void configure(double r, double c); + double step_rc(double input) { return capval += (input - capval) * exponent; } + double step_cr(double input) { double const result = input - capval; capval += result * exponent; return result; } + + double capval = 0.0; // current capacitor value + double exponent = 0.0; // constant exponent + }; + + struct timer8253 + { + struct channel + { + channel() { } + + void clock(); + + u8 holding = 0; // holding until counts written? + u8 latchmode = 0; // latching mode + u8 latchtoggle = 0; // latching state + u8 clockmode = 0; // clocking mode + u8 bcdmode = 0; // BCD mode? + u8 output = 0; // current output value + u8 lastgate = 0; // previous gate value + u8 gate = 0; // current gate value + u16 count = 0; // initial count + u16 remain = 0; // current down counter value + s32 subcount = 0; // subcount (2MHz clocks per input clock) + }; + + timer8253() : env{ 0.0, 0.0, 0.0 } { } + + channel chan[3]; // three channels' worth of information + double env[3]; // envelope value for each channel + g80_filter_state chan_filter[2]; // filter states for the first two channels + g80_filter_state gate1; // first RC filter state + g80_filter_state gate2; // second RC filter state + u8 config = 0; // configuration for this timer + }; + + sound_stream *m_stream; // output stream + timer8253 m_timer_group[3]; // 3 groups of timers + u8 m_timer_mode[3]; // mode control for each group + u32 m_noise_shift; + u8 m_noise_state; + s32 m_noise_subcount; + double m_gate_rc1_exp[2]; + double m_gate_rc2_exp[2]; + g80_filter_state m_final_filter; + g80_filter_state m_noise_filters[5]; +#endif + + TIMER_CALLBACK_MEMBER( delayed_usb_data_w ); + void timer_w(int which, u8 offset, u8 data); + void env_w(int which, u8 offset, u8 data); + + uint8_t p1_r(); + void p1_w(uint8_t data); + void p2_w(uint8_t data); + DECLARE_READ_LINE_MEMBER( t1_r ); +}; + +DECLARE_DEVICE_TYPE(SEGAUSB, usb_sound_device) + + +class usb_rom_sound_device : public usb_sound_device +{ +public: + template usb_rom_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, T &&maincpu_tag) + : usb_rom_sound_device(mconfig, tag, owner, clock) + { + m_maincpu.set_tag(maincpu_tag); + } + + usb_rom_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + +protected: + // device-level overrides + virtual void device_add_mconfig(machine_config &config) override; +}; + +DECLARE_DEVICE_TYPE(SEGAUSBROM, usb_rom_sound_device) + +#endif // MAME_AUDIO_SEGAUSB_H diff --git a/src/mame/drivers/segag80r.cpp b/src/mame/drivers/segag80r.cpp index 2a4092247ee..db74169b8e9 100644 --- a/src/mame/drivers/segag80r.cpp +++ b/src/mame/drivers/segag80r.cpp @@ -136,6 +136,11 @@ #define VBEND (0) #define VBSTART (224) +// Unsure whether this should be 2 or 3. It depends on how many rising clock +// edges MEMRQ is held for, plus 1 additional cycle. Going to 3 creates +// noticeable slowdowns in Space Fury. +static constexpr int WAIT_STATES = 2; + /************************************* @@ -856,8 +861,8 @@ void segag80r_state::astrob(machine_config &config) SPEAKER(config, "speaker").front_center(); /* sound boards */ - astrob_sound_board(config); - sega_speech_board(config); + ASTRO_BLASTER_AUDIO(config, m_g80_audio, 0).add_route(ALL_OUTPUTS, "speech", 1.0); + SEGA_SPEECH_BOARD(config, "speech", 0).add_route(ALL_OUTPUTS, "speaker", 0.5); } @@ -1007,19 +1012,21 @@ ROM_START( astrob ) ROM_LOAD( "924a.prom-u18", 0x9000, 0x0800, CRC(120a39c7) SHA1(d8fdf97290725cf9ebddab9eeb34d7adba097394) ) ROM_LOAD( "925a.prom-u19", 0x9800, 0x0800, CRC(790a7f4e) SHA1(16b7b8e864a8f5f59da6bf2ad17f1e4791f34abe) ) - ROM_REGION( 0x0800, "audiocpu", 0 ) + ROM_REGION( 0x0800, "speech:cpu", 0 ) ROM_LOAD( "808b.speech-u7", 0x0000, 0x0800, CRC(5988c767) SHA1(3b91a8cd46aa7e714028cc40f700fea32287afb1) ) - ROM_REGION( 0x4000, SEGASND_SEGASPEECH_REGION, 0 ) + ROM_REGION( 0x0020, "speech:proms", 0 ) + ROM_LOAD( "pr84.speech-u30", 0x0000, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* 7051, speech board addressing */ + + ROM_REGION( 0x4000, "speech:data", 0 ) ROM_LOAD( "809a.speech-u6", 0x0000, 0x0800, CRC(893f228d) SHA1(41c08210d322105f5446cfaa1258c194dd078a34) ) ROM_LOAD( "810.speech-u5", 0x0800, 0x0800, CRC(ff0163c5) SHA1(158a12f9bf01d25c7e98f34fce56df51d49e5a85) ) ROM_LOAD( "811.speech-u4", 0x1000, 0x0800, CRC(219f3978) SHA1(728edb9251f7cde237fa3b005971366a099c6342) ) ROM_LOAD( "812a.speech-u3", 0x1800, 0x0800, CRC(410ad0d2) SHA1(9b5f05bb64a6ecfe3543025a10c6ec67de797333) ) - ROM_REGION( 0x0440, "proms", 0 ) // not dumped for this set, but believed identical + ROM_REGION( 0x0420, "proms", 0 ) // not dumped for this set, but believed identical ROM_LOAD( "316-0806.video1-u52", 0x0000, 0x0020, CRC(358128b6) SHA1(b6b4b9ecfdcc69b45e69e7a8614153d83be4c62b) ) /* 6331 */ ROM_LOAD( "316-0764.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* 6331, CPU board addressing */ - ROM_LOAD( "pr84.speech-u30", 0x0420, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* 7051, speech board addressing */ ROM_END ROM_START( astrob2 ) @@ -1045,19 +1052,21 @@ ROM_START( astrob2 ) ROM_LOAD( "905.prom-u18", 0x9000, 0x0800, CRC(4f08f9f4) SHA1(755a825b18ed50caa7bf274a0a5c3a1b00b1c070) ) ROM_LOAD( "906.prom-u19", 0x9800, 0x0800, CRC(58149df1) SHA1(2bba56576a225ca47ce31a5b6dcc491546dfffec) ) - ROM_REGION( 0x0800, "audiocpu", 0 ) + ROM_REGION( 0x0800, "speech:cpu", 0 ) ROM_LOAD( "808b.speech-u7", 0x0000, 0x0800, CRC(5988c767) SHA1(3b91a8cd46aa7e714028cc40f700fea32287afb1) ) - ROM_REGION( 0x4000, SEGASND_SEGASPEECH_REGION, 0 ) + ROM_REGION( 0x0020, "speech:proms", 0 ) + ROM_LOAD( "pr84.speech-u30", 0x0000, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* 7051, speech board addressing */ + + ROM_REGION( 0x4000, "speech:data", 0 ) ROM_LOAD( "809a.speech-u6", 0x0000, 0x0800, CRC(893f228d) SHA1(41c08210d322105f5446cfaa1258c194dd078a34) ) ROM_LOAD( "810.speech-u5", 0x0800, 0x0800, CRC(ff0163c5) SHA1(158a12f9bf01d25c7e98f34fce56df51d49e5a85) ) ROM_LOAD( "811.speech-u4", 0x1000, 0x0800, CRC(219f3978) SHA1(728edb9251f7cde237fa3b005971366a099c6342) ) ROM_LOAD( "812a.speech-u3", 0x1800, 0x0800, CRC(410ad0d2) SHA1(9b5f05bb64a6ecfe3543025a10c6ec67de797333) ) - ROM_REGION( 0x0440, "proms", 0 ) // not dumped for this set, but believed identical + ROM_REGION( 0x0420, "proms", 0 ) // not dumped for this set, but believed identical ROM_LOAD( "316-0806.video1-u52", 0x0000, 0x0020, CRC(358128b6) SHA1(b6b4b9ecfdcc69b45e69e7a8614153d83be4c62b) ) /* 6331 */ ROM_LOAD( "316-0764.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* 6331, CPU board addressing */ - ROM_LOAD( "pr84.speech-u30", 0x0420, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* 7051, speech board addressing */ ROM_END ROM_START( astrob2a ) @@ -1083,19 +1092,21 @@ ROM_START( astrob2a ) ROM_LOAD( "905.prom-u18", 0x9000, 0x0800, CRC(4f08f9f4) SHA1(755a825b18ed50caa7bf274a0a5c3a1b00b1c070) ) ROM_LOAD( "906.prom-u19", 0x9800, 0x0800, CRC(58149df1) SHA1(2bba56576a225ca47ce31a5b6dcc491546dfffec) ) - ROM_REGION( 0x0800, "audiocpu", 0 ) + ROM_REGION( 0x0800, "speech:cpu", 0 ) ROM_LOAD( "808b.speech-u7", 0x0000, 0x0800, CRC(5988c767) SHA1(3b91a8cd46aa7e714028cc40f700fea32287afb1) ) - ROM_REGION( 0x4000, SEGASND_SEGASPEECH_REGION, 0 ) + ROM_REGION( 0x0020, "speech:proms", 0 ) + ROM_LOAD( "pr84.speech-u30", 0x0000, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* 7051, speech board addressing */ + + ROM_REGION( 0x4000, "speech:data", 0 ) ROM_LOAD( "809a.speech-u6", 0x0000, 0x0800, CRC(893f228d) SHA1(41c08210d322105f5446cfaa1258c194dd078a34) ) ROM_LOAD( "810.speech-u5", 0x0800, 0x0800, CRC(ff0163c5) SHA1(158a12f9bf01d25c7e98f34fce56df51d49e5a85) ) ROM_LOAD( "811.speech-u4", 0x1000, 0x0800, CRC(219f3978) SHA1(728edb9251f7cde237fa3b005971366a099c6342) ) ROM_LOAD( "812a.speech-u3", 0x1800, 0x0800, CRC(410ad0d2) SHA1(9b5f05bb64a6ecfe3543025a10c6ec67de797333) ) - ROM_REGION( 0x0440, "proms", 0 ) + ROM_REGION( 0x0420, "proms", 0 ) ROM_LOAD( "316-0806.video1-u52", 0x0000, 0x0020, CRC(358128b6) SHA1(b6b4b9ecfdcc69b45e69e7a8614153d83be4c62b) ) /* 6331 */ ROM_LOAD( "316-0764.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* 6331, CPU board addressing */ - ROM_LOAD( "pr84.speech-u30", 0x0420, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* 7051, speech board addressing */ ROM_END ROM_START( astrob2b ) // This was dumped from 2 different PCB sets, with matching reads. Only different ROM from astrob2a is 829d. @@ -1121,19 +1132,21 @@ ROM_START( astrob2b ) // This was dumped from 2 different PCB sets, with matchin ROM_LOAD( "905.prom-u18", 0x9000, 0x0800, CRC(4f08f9f4) SHA1(755a825b18ed50caa7bf274a0a5c3a1b00b1c070) ) ROM_LOAD( "906.prom-u19", 0x9800, 0x0800, CRC(58149df1) SHA1(2bba56576a225ca47ce31a5b6dcc491546dfffec) ) - ROM_REGION( 0x0800, "audiocpu", 0 ) + ROM_REGION( 0x0800, "speech:cpu", 0 ) ROM_LOAD( "808b.speech-u7", 0x0000, 0x0800, CRC(5988c767) SHA1(3b91a8cd46aa7e714028cc40f700fea32287afb1) ) - ROM_REGION( 0x4000, SEGASND_SEGASPEECH_REGION, 0 ) + ROM_REGION( 0x0020, "speech:proms", 0 ) + ROM_LOAD( "pr84.speech-u30", 0x0000, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* 7051, speech board addressing */ + + ROM_REGION( 0x4000, "speech:data", 0 ) ROM_LOAD( "809a.speech-u6", 0x0000, 0x0800, CRC(893f228d) SHA1(41c08210d322105f5446cfaa1258c194dd078a34) ) ROM_LOAD( "810.speech-u5", 0x0800, 0x0800, CRC(ff0163c5) SHA1(158a12f9bf01d25c7e98f34fce56df51d49e5a85) ) ROM_LOAD( "811.speech-u4", 0x1000, 0x0800, CRC(219f3978) SHA1(728edb9251f7cde237fa3b005971366a099c6342) ) ROM_LOAD( "812a.speech-u3", 0x1800, 0x0800, CRC(410ad0d2) SHA1(9b5f05bb64a6ecfe3543025a10c6ec67de797333) ) - ROM_REGION( 0x0440, "proms", 0 ) + ROM_REGION( 0x0420, "proms", 0 ) ROM_LOAD( "316-0806.video1-u52", 0x0000, 0x0020, CRC(358128b6) SHA1(b6b4b9ecfdcc69b45e69e7a8614153d83be4c62b) ) /* 6331 */ ROM_LOAD( "316-0764.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* 6331, CPU board addressing */ - ROM_LOAD( "pr84.speech-u30", 0x0420, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* 7051, speech board addressing */ ROM_END ROM_START( astrob1 ) @@ -1156,19 +1169,21 @@ ROM_START( astrob1 ) ROM_LOAD( "851.prom-u15", 0x7800, 0x0800, CRC(3d4cf9f0) SHA1(11e996f33f3a104e50d0a54a0814ea3e07735683) ) ROM_LOAD( "852.prom-u16", 0x8000, 0x0800, CRC(af88a97e) SHA1(fe7993101c629b296b5da05519b0990cc2b78286) ) - ROM_REGION( 0x0800, "audiocpu", 0 ) + ROM_REGION( 0x0800, "speech:cpu", 0 ) ROM_LOAD( "808b.speech-u7", 0x0000, 0x0800, CRC(5988c767) SHA1(3b91a8cd46aa7e714028cc40f700fea32287afb1) ) - ROM_REGION( 0x4000, SEGASND_SEGASPEECH_REGION, 0 ) + ROM_REGION( 0x0020, "speech:proms", 0 ) + ROM_LOAD( "pr84.speech-u30", 0x0000, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* 7051, speech board addressing */ + + ROM_REGION( 0x4000, "speech:data", 0 ) ROM_LOAD( "809a.speech-u6", 0x0000, 0x0800, CRC(893f228d) SHA1(41c08210d322105f5446cfaa1258c194dd078a34) ) ROM_LOAD( "810.speech-u5", 0x0800, 0x0800, CRC(ff0163c5) SHA1(158a12f9bf01d25c7e98f34fce56df51d49e5a85) ) ROM_LOAD( "811.speech-u4", 0x1000, 0x0800, CRC(219f3978) SHA1(728edb9251f7cde237fa3b005971366a099c6342) ) ROM_LOAD( "812a.speech-u3", 0x1800, 0x0800, CRC(410ad0d2) SHA1(9b5f05bb64a6ecfe3543025a10c6ec67de797333) ) - ROM_REGION( 0x0440, "proms", 0 ) // not dumped for this set, but believed identical + ROM_REGION( 0x0420, "proms", 0 ) // not dumped for this set, but believed identical ROM_LOAD( "316-0806.video1-u52", 0x0000, 0x0020, CRC(358128b6) SHA1(b6b4b9ecfdcc69b45e69e7a8614153d83be4c62b) ) /* 6331 */ ROM_LOAD( "316-0764.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* 6331, CPU board addressing */ - ROM_LOAD( "pr84.speech-u30", 0x0420, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* 7051, speech board addressing */ ROM_END ROM_START( astrobg ) @@ -1191,19 +1206,21 @@ ROM_START( astrobg ) ROM_LOAD( "835.u15", 0x7800, 0x0800, CRC(6eeeb409) SHA1(1caf9b7ac08a4adcbf8c17f9e4b398373db706e1) ) ROM_LOAD( "836.u16", 0x8000, 0x0800, CRC(07ffe6dc) SHA1(70673e8266139034afa64bf980b1b9ddbf294e0f) ) - ROM_REGION( 0x0800, "audiocpu", 0 ) + ROM_REGION( 0x0800, "speech:cpu", 0 ) ROM_LOAD( "808b_speech_de.u07", 0x0000, 0x0800, CRC(5988c767) SHA1(3b91a8cd46aa7e714028cc40f700fea32287afb1) ) - ROM_REGION( 0x4000, SEGASND_SEGASPEECH_REGION, 0 ) + ROM_REGION( 0x0020, "speech:proms", 0 ) + ROM_LOAD( "pr84.speech-u30", 0x0000, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* 7051, speech board addressing */ + + ROM_REGION( 0x4000, "speech:data", 0 ) ROM_LOAD( "830_speech_de.u06", 0x0000, 0x0800, CRC(2d840552) SHA1(7a2a7b54378b6cc85b8ab5c26e42266aa747c635) ) ROM_LOAD( "831_speech_de.u05", 0x0800, 0x0800, CRC(46b30ee4) SHA1(c9e19a9b9ebc9b3b853e79f93ad74e4ec5dfd1ae) ) ROM_LOAD( "832_speech_de.u04", 0x1000, 0x0800, CRC(d05280b8) SHA1(8d30b23b83b32465a8a2decd2ce9bfed24394e7e) ) ROM_LOAD( "833_speech_de.u03", 0x1800, 0x0800, CRC(08f11459) SHA1(da6dc2bf30b95882f95c21739ec02fc89d286a66) ) - ROM_REGION( 0x0440, "proms", 0 ) // not dumped for this set, but believed identical + ROM_REGION( 0x0420, "proms", 0 ) // not dumped for this set, but believed identical ROM_LOAD( "316-0806.video1-u52", 0x0000, 0x0020, CRC(358128b6) SHA1(b6b4b9ecfdcc69b45e69e7a8614153d83be4c62b) ) /* 6331 */ ROM_LOAD( "316-0764.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* 6331, CPU board addressing */ - ROM_LOAD( "pr84.speech-u30", 0x0420, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* 7051, speech board addressing */ ROM_END ROM_START( 005 ) @@ -1526,8 +1543,36 @@ void segag80r_state::monsterb_expand_gfx(const char *region) * *************************************/ +void segag80r_state::init_waitstates() +{ + address_space &pgmspace = m_maincpu->space(AS_PROGRAM); + address_space &opspace = m_maincpu->space(AS_OPCODES); + + pgmspace.install_read_tap(0x0000, 0xffff, "program_waitstate_r",[this](offs_t offset, u8 &data, u8 mem_mask) + { + if (!machine().side_effects_disabled()) + m_maincpu->adjust_icount(-WAIT_STATES); + return data; + }); + pgmspace.install_write_tap(0x0000, 0xffff, "program_waitstate_w",[this](offs_t offset, u8 &data, u8 mem_mask) + { + if (!machine().side_effects_disabled()) + m_maincpu->adjust_icount(-WAIT_STATES); + return data; + }); + + opspace.install_read_tap(0x0000, 0xffff, "opcodes_waitstate_r",[this](offs_t offset, u8 &data, u8 mem_mask) + { + if (!machine().side_effects_disabled()) + m_maincpu->adjust_icount(-WAIT_STATES); + return data; + }); +} + void segag80r_state::init_astrob() { + init_waitstates(); + address_space &iospace = m_maincpu->space(AS_IO); /* configure the 315-0062 security chip */ @@ -1537,19 +1582,18 @@ void segag80r_state::init_astrob() m_background_pcb = G80_BACKGROUND_NONE; /* install speech board */ - iospace.install_write_handler(0x38, 0x38, write8smo_delegate(*m_speech, FUNC(speech_sound_device::data_w))); - iospace.install_write_handler(0x3b, 0x3b, write8smo_delegate(*m_speech, FUNC(speech_sound_device::control_w))); + iospace.install_write_handler(0x38, 0x38, write8smo_delegate(*m_speech, FUNC(sega_speech_device::data_w))); + iospace.install_write_handler(0x3b, 0x3b, write8smo_delegate(*m_speech, FUNC(sega_speech_device::control_w))); /* install Astro Blaster sound board */ - iospace.install_write_handler(0x3e, 0x3f, write8sm_delegate(*this, FUNC(segag80r_state::astrob_sound_w))); - - save_item(NAME(m_sound_state)); - save_item(NAME(m_sound_rate)); + iospace.install_write_handler(0x3e, 0x3f, write8sm_delegate(*m_g80_audio, FUNC(segag80_audio_device::write))); } void segag80r_state::init_005() { + init_waitstates(); + /* configure the 315-0070 security chip */ m_decrypt = segag80_security(70); @@ -1566,6 +1610,8 @@ void segag80r_state::init_005() void segag80r_state::init_spaceod() { + init_waitstates(); + address_space &iospace = m_maincpu->space(AS_IO); /* configure the 315-0063 security chip */ @@ -1590,6 +1636,8 @@ void segag80r_state::init_spaceod() void segag80r_state::init_monsterb() { + init_waitstates(); + address_space &iospace = m_maincpu->space(AS_IO); address_space &pgmspace = m_maincpu->space(AS_PROGRAM); @@ -1611,6 +1659,8 @@ void segag80r_state::init_monsterb() void segag80r_state::init_monster2() { + init_waitstates(); + address_space &iospace = m_maincpu->space(AS_IO); address_space &pgmspace = m_maincpu->space(AS_PROGRAM); @@ -1633,6 +1683,8 @@ void segag80r_state::init_monster2() void segag80r_state::init_pignewt() { + init_waitstates(); + address_space &iospace = m_maincpu->space(AS_IO); address_space &pgmspace = m_maincpu->space(AS_PROGRAM); @@ -1657,6 +1709,8 @@ void segag80r_state::init_pignewt() void segag80r_state::init_sindbadm() { + init_waitstates(); + address_space &iospace = m_maincpu->space(AS_IO); address_space &pgmspace = m_maincpu->space(AS_PROGRAM); @@ -1681,12 +1735,12 @@ void segag80r_state::init_sindbadm() // YEAR, NAME, PARENT, MACHINE, INPUT, CLASS, INIT, MONITOR,COMPANY,FULLNAME,FLAGS /* basic G-80 system with: CPU board, PROM board, Video I board, custom sound boards */ -GAME( 1981, astrob, 0, astrob, astrob, segag80r_state, init_astrob, ROT270, "Sega", "Astro Blaster (version 3)", MACHINE_IMPERFECT_SOUND ) -GAME( 1981, astrob2, astrob, astrob, astrob2, segag80r_state, init_astrob, ROT270, "Sega", "Astro Blaster (version 2)", MACHINE_IMPERFECT_SOUND ) -GAME( 1981, astrob2a, astrob, astrob, astrob2, segag80r_state, init_astrob, ROT270, "Sega", "Astro Blaster (version 2a)", MACHINE_IMPERFECT_SOUND ) -GAME( 1981, astrob2b, astrob, astrob, astrob2, segag80r_state, init_astrob, ROT270, "Sega", "Astro Blaster (version 2b)", MACHINE_IMPERFECT_SOUND ) -GAME( 1981, astrob1, astrob, astrob, astrob, segag80r_state, init_astrob, ROT270, "Sega", "Astro Blaster (version 1)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) // instant death if you start game with 1 credit, protection?, bad dump? -GAME( 1981, astrobg, astrob, astrob, astrob, segag80r_state, init_astrob, ROT270, "Sega", "Astro Blaster (German)", MACHINE_IMPERFECT_SOUND ) +GAME( 1981, astrob, 0, astrob, astrob, segag80r_state, init_astrob, ROT270, "Sega", "Astro Blaster (version 3)", 0 ) +GAME( 1981, astrob2, astrob, astrob, astrob2, segag80r_state, init_astrob, ROT270, "Sega", "Astro Blaster (version 2)", 0 ) +GAME( 1981, astrob2a, astrob, astrob, astrob2, segag80r_state, init_astrob, ROT270, "Sega", "Astro Blaster (version 2a)", 0 ) +GAME( 1981, astrob2b, astrob, astrob, astrob2, segag80r_state, init_astrob, ROT270, "Sega", "Astro Blaster (version 2b)", 0 ) +GAME( 1981, astrob1, astrob, astrob, astrob, segag80r_state, init_astrob, ROT270, "Sega", "Astro Blaster (version 1)", 0 | MACHINE_NOT_WORKING ) // instant death if you start game with 1 credit, protection?, bad dump? +GAME( 1981, astrobg, astrob, astrob, astrob, segag80r_state, init_astrob, ROT270, "Sega", "Astro Blaster (German)", 0 ) GAME( 1981, 005, 0, sega005, 005, segag80r_state, init_005, ROT270, "Sega", "005", MACHINE_IMPERFECT_SOUND ) diff --git a/src/mame/drivers/segag80v.cpp b/src/mame/drivers/segag80v.cpp index 0019926832d..b8da53afa46 100644 --- a/src/mame/drivers/segag80v.cpp +++ b/src/mame/drivers/segag80v.cpp @@ -135,6 +135,7 @@ ***************************************************************************/ #include "emu.h" +#include "audio/segag80.h" #include "includes/segag80v.h" #include "machine/segag80.h" @@ -143,15 +144,10 @@ #include "speaker.h" -/************************************* - * - * Constants - * - *************************************/ - -#define CPU_CLOCK 8000000 /* not used when video boards are connected */ -#define VIDEO_CLOCK 15468480 - +// Unsure whether this should be 2 or 3. It depends on how many rising clock +// edges MEMRQ is held for, plus 1 additional cycle. Going to 3 creates +// noticeable slowdowns in Space Fury. +static constexpr int WAIT_STATES = 2; /************************************* @@ -160,10 +156,10 @@ * *************************************/ -INPUT_CHANGED_MEMBER(segag80v_state::service_switch) +WRITE_LINE_MEMBER(segag80v_state::service_switch_w) { - /* pressing the service switch sends an NMI */ - if (newval) + // pressing the service switch sends an NMI + if (state) m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero); } @@ -172,13 +168,15 @@ void segag80v_state::machine_start() { m_scrambled_write_pc = 0xffff; - /* register for save states */ save_item(NAME(m_mult_data)); save_item(NAME(m_mult_result)); save_item(NAME(m_spinner_select)); save_item(NAME(m_spinner_sign)); save_item(NAME(m_spinner_count)); save_item(NAME(m_scrambled_write_pc)); + save_item(NAME(m_coin_ff_state)); + save_item(NAME(m_coin_last_state)); + save_item(NAME(m_edgint_ff_state)); } @@ -189,10 +187,10 @@ void segag80v_state::machine_start() * *************************************/ -uint8_t segag80v_state::g80v_opcode_r(offs_t offset) +u8 segag80v_state::opcode_r(offs_t offset) { // opcodes themselves are not scrambled - uint8_t op = m_maincpu->space(AS_PROGRAM).read_byte(offset); + u8 op = m_maincpu->space(AS_PROGRAM).read_byte(offset); // writes via opcode $32 (LD $(XXYY),A) get scrambled if (!machine().side_effects_disabled()) @@ -209,17 +207,21 @@ offs_t segag80v_state::decrypt_offset(offs_t offset) offs_t pc = m_scrambled_write_pc; m_scrambled_write_pc = 0xffff; - /* munge the low byte of the address */ + // munge the low byte of the address return (offset & 0xff00) | (*m_decrypt)(pc, offset & 0xff); } -void segag80v_state::mainram_w(offs_t offset, uint8_t data) +void segag80v_state::mainram_w(offs_t offset, u8 data) { m_mainram[decrypt_offset(offset)] = data; } -void segag80v_state::usb_ram_w(offs_t offset, uint8_t data){ m_usb->ram_w(decrypt_offset(offset), data); } -void segag80v_state::vectorram_w(offs_t offset, uint8_t data) +void segag80v_state::usb_ram_w(offs_t offset, u8 data) +{ + m_usb->ram_w(decrypt_offset(offset), data); +} + +void segag80v_state::vectorram_w(offs_t offset, u8 data) { m_vectorram[decrypt_offset(offset)] = data; } @@ -232,28 +234,50 @@ void segag80v_state::vectorram_w(offs_t offset, uint8_t data) * *************************************/ -inline uint8_t segag80v_state::demangle(uint8_t d7d6, uint8_t d5d4, uint8_t d3d2, uint8_t d1d0) +// +// I/O port mapping +// +// +--------------- Offset-----------------+ +// | 0 | 1 | 2 | 3 | +// +----+---------+---------+---------+---------+ +// | D7 | COINA | n/c | n/c | n/c | +// | D6 | COINB | P1.13 | P1.14 | n/c | +// | D5 | SERVICE | P1.15 | P1.16 | P1.17 | +// | D4 | P1.18 | P1.19 | P1.20 | P1.21 | +// | D3 | SW1.8 | SW1.7 | SW1.6 | SW1.5 | +// | D2 | SW1.4 | SW1.3 | SW1.2 | SW1.1 | +// | D1 | SW2.8 | SW2.7 | SW2.6 | SW2.5 | +// | D0 | SW2.4 | SW2.3 | SW2.2 | SW2.1 | +// +----+---------+---------+---------+---------+ +// +// Notes: +// COINA, COINB are gated to be impulses, and signal an INT +// SERVICE is gated to be an impulse, but does not signal +// P1.13 = DRAW signal from XY board +// +// The input ports are mapped to how the schematics show them, so +// to achieve the matrix above, they must be demangled. +// + +u8 segag80v_state::mangled_ports_r(offs_t offset) { - return ((d7d6 << 7) & 0x80) | ((d7d6 << 2) & 0x40) | - ((d5d4 << 5) & 0x20) | ((d5d4 << 0) & 0x10) | - ((d3d2 << 3) & 0x08) | ((d3d2 >> 2) & 0x04) | - ((d1d0 << 1) & 0x02) | ((d1d0 >> 4) & 0x01); -} + u8 d7d6 = m_d7d6->read(); + u8 d5d4 = m_d5d4->read(); + u8 d3d2 = m_d3d2->read(); + u8 d1d0 = m_d1d0->read(); + + offset &= 3; + u8 result = + (BIT(d7d6, offset + 0) << 7) | + (BIT(d7d6, offset + 4) << 6) | + (BIT(d5d4, offset + 0) << 5) | + (BIT(d5d4, offset + 4) << 4) | + (BIT(d3d2, offset + 0) << 3) | + (BIT(d3d2, offset + 4) << 2) | + (BIT(d1d0, offset + 0) << 1) | + (BIT(d1d0, offset + 4) << 0); - -uint8_t segag80v_state::mangled_ports_r(offs_t offset) -{ - /* The input ports are odd. Neighboring lines are read via a mux chip */ - /* one bit at a time. This means that one bank of DIP switches will be */ - /* read as two bits from each of 4 ports. For this reason, the input */ - /* ports have been organized logically, and are demangled at runtime. */ - /* 4 input ports each provide 8 bits of information. */ - uint8_t d7d6 = ioport("D7D6")->read(); - uint8_t d5d4 = ioport("D5D4")->read(); - uint8_t d3d2 = ioport("D3D2")->read(); - uint8_t d1d0 = ioport("D1D0")->read(); - int shift = offset & 3; - return demangle(d7d6 >> shift, d5d4 >> shift, d3d2 >> shift, d1d0 >> shift); + return result; } @@ -264,28 +288,24 @@ uint8_t segag80v_state::mangled_ports_r(offs_t offset) * *************************************/ -void segag80v_state::spinner_select_w(uint8_t data) +void segag80v_state::spinner_select_w(u8 data) { m_spinner_select = data; } -uint8_t segag80v_state::spinner_input_r() +u8 segag80v_state::spinner_input_r() { - int8_t delta; - if (m_spinner_select & 1) - return ioport("FC")->read(); - -/* - * The values returned are always increasing. That is, regardless of whether - * you turn the spinner left or right, the self-test should always show the - * number as increasing. The direction is only reflected in the least - * significant bit. - */ - - /* I'm sure this can be further simplified ;-) BW */ - delta = ioport("SPINNER")->read(); + return m_fc->read(); + + // + // The values returned are always increasing. That is, regardless of whether + // you turn the spinner left or right, the self-test should always show the + // number as increasing. The direction is only reflected in the least + // significant bit. + // + s8 delta = m_spinner->read(); if (delta != 0) { m_spinner_sign = (delta >> 7) & 1; @@ -304,32 +324,33 @@ uint8_t segag80v_state::spinner_input_r() READ_LINE_MEMBER(segag80v_state::elim4_joint_coin_r) { - return (ioport("COINS")->read() & 0xf) != 0xf; + return (m_coins->read() & 0xf) != 0xf; } -uint8_t segag80v_state::elim4_input_r() +u8 segag80v_state::elim4_input_r() { - uint8_t result = 0; + u8 result = 0; - /* bit 3 enables demux */ - if (m_spinner_select & 8) + // bit 3 enables demux + if (BIT(m_spinner_select, 3) != 0) { - /* Demux bit 0-2. Only 6 and 7 are connected */ + // Demux bit 0-2. Only 6 and 7 are connected switch (m_spinner_select & 7) { case 6: - /* player 3 & 4 controls */ - result = ioport("FC")->read(); + // player 3 & 4 controls + result = m_fc->read(); break; + case 7: - /* the 4 coin inputs */ - result = ioport("COINS")->read(); + // the 4 coin inputs + result = m_coins->read(); break; } } - /* LS240 has inverting outputs */ + // LS240 has inverting outputs return (result ^ 0xff); } @@ -341,7 +362,7 @@ uint8_t segag80v_state::elim4_input_r() * *************************************/ -void segag80v_state::multiply_w(offs_t offset, uint8_t data) +void segag80v_state::multiply_w(offs_t offset, u8 data) { m_mult_data[offset] = data; if (offset == 1) @@ -349,9 +370,9 @@ void segag80v_state::multiply_w(offs_t offset, uint8_t data) } -uint8_t segag80v_state::multiply_r() +u8 segag80v_state::multiply_r() { - uint8_t result = m_mult_result; + u8 result = m_mult_result; m_mult_result >>= 8; return result; } @@ -364,21 +385,76 @@ uint8_t segag80v_state::multiply_r() * *************************************/ -void segag80v_state::coin_count_w(uint8_t data) +void segag80v_state::coin_count_w(u8 data) { machine().bookkeeping().coin_counter_w(0, (data >> 7) & 1); machine().bookkeeping().coin_counter_w(1, (data >> 6) & 1); } -void segag80v_state::unknown_w(uint8_t data) +void segag80v_state::unknown_w(u8 data) { - /* writing an 0x04 here enables interrupts */ - /* some games write 0x00/0x01 here as well */ + // writing an 0x04 here enables interrupts + // some games write 0x00/0x01 here as well if (data != 0x00 && data != 0x01 && data != 0x04) osd_printf_debug("%04X:unknown_w = %02X\n", m_maincpu->pc(), data); } +READ_LINE_MEMBER(segag80v_state::draw_r) +{ + return (machine().scheduler().time() < m_draw_end_time); +} + + + +/************************************* + * + * Main CPU interrupts + * + *************************************/ + +void segag80v_state::vblank_callback(screen_device &screen, bool state) +{ + if (!state) + { + m_edgint_ff_state = 1; + update_int(); + } +} + +void segag80v_state::update_int() +{ + // + // IRQ signal comes from multiple sources: + // * XINT signal, which is a combination of: + // - COINA impulse, clocks an LS74, cleared by INTCL signal + // - COINB impulse, clocks an LS74, cleared by INTCL signal + // - SERVICE impulse + // * /EDGINT signal from vector board, clocks an LS74, cleared by INTCL signal + // - signal comes from 15468480 crystal, divided by 3, and then by 0x1f788 + // * /INT signal from ??? + // + + if (m_coin_ff_state != 0 || m_edgint_ff_state != 0) + m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE); + else + m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE); + + // SERVICE coin doesn't have a flip-flop; it's instantaneous + m_coin_ff_state &= ~0x04; +} + +WRITE_LINE_MEMBER(segag80v_state::irq_ack_w) +{ + if (state) + { + // INTCL signal clears both coin flip-flops and EDGINT flip-flop + m_coin_ff_state = 0; + m_edgint_ff_state = 0; + update_int(); + } +} + /************************************* @@ -387,26 +463,26 @@ void segag80v_state::unknown_w(uint8_t data) * *************************************/ -/* complete memory map derived from schematics */ +// complete memory map derived from schematics void segag80v_state::main_map(address_map &map) { - map(0x0000, 0x07ff).rom(); /* CPU board ROM */ - map(0x0800, 0xbfff).rom(); /* PROM board ROM area */ + map(0x0000, 0x07ff).rom(); // CPU board ROM + map(0x0800, 0xbfff).rom(); // PROM board ROM area map(0xc800, 0xcfff).ram().w(FUNC(segag80v_state::mainram_w)).share("mainram"); map(0xe000, 0xefff).ram().w(FUNC(segag80v_state::vectorram_w)).share("vectorram"); } void segag80v_state::opcodes_map(address_map &map) { - map(0x0000, 0xffff).r(FUNC(segag80v_state::g80v_opcode_r)); + map(0x0000, 0xffff).r(FUNC(segag80v_state::opcode_r)); } -/* complete memory map derived from schematics */ +// complete memory map derived from schematics void segag80v_state::main_portmap(address_map &map) { map.global_mask(0xff); - map(0xbc, 0xbc); /* .r(FUNC(segag80v_state::)); ??? */ + map(0xbc, 0xbc); // .r(FUNC(segag80v_state::)); ??? map(0xbd, 0xbe).w(FUNC(segag80v_state::multiply_w)); map(0xbe, 0xbe).r(FUNC(segag80v_state::multiply_r)); map(0xbf, 0xbf).w(FUNC(segag80v_state::unknown_w)); @@ -426,24 +502,24 @@ void segag80v_state::main_portmap(address_map &map) static INPUT_PORTS_START( g80v_generic ) PORT_START("D7D6") - PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_IMPULSE(3) /* P1.5 */ - PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED ) /* n/c */ - PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED ) /* n/c */ - PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) /* n/c */ - PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_COIN2 ) PORT_IMPULSE(3) /* P1.8 */ - PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) /* P1.13 */ - PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) /* P1.14 */ - PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) /* n/c */ + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_WRITE_LINE_MEMBER(segag80v_state, coin_w<0>) // P1.5 + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED ) // n/c + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED ) // n/c + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) // n/c + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_COIN2 ) PORT_WRITE_LINE_MEMBER(segag80v_state, coin_w<1>) // P1.8 + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(segag80v_state, draw_r) // P1.13 + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) // P1.14 + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) // n/c PORT_START("D5D4") - PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SERVICE1 ) /* P1.10 */ - PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_START1 ) /* P1.15 */ - PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED ) /* P1.16 */ - PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) /* P1.17 */ - PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) /* P1.18 */ - PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START2 ) /* P1.19 */ - PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) /* P1.20 */ - PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) /* P1.21 */ + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_WRITE_LINE_MEMBER(segag80v_state, coin_w<2>) // P1.10 + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_START1 ) // P1.15 + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED ) // P1.16 + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) // P1.17 + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) // P1.18 + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START2 ) // P1.19 + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) // P1.20 + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) // P1.21 PORT_START("D3D2") PORT_DIPUNUSED_DIPLOC( 0x01, 0x01, "SW1:8" ) @@ -492,17 +568,17 @@ static INPUT_PORTS_START( g80v_generic ) PORT_DIPSETTING( 0x80, DEF_STR( 1C_6C )) PORT_START("FC") - PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) /* P1.23 */ - PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) /* P1.24 */ - PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) /* P1.25 */ - PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) /* P1.26 */ - PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED ) /* P1.27 */ - PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED ) /* P1.28 */ - PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED ) /* P1.29 */ - PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) /* P1.30 */ + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) // P1.23 + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) // P1.24 + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // P1.25 + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) // P1.26 + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED ) // P1.27 + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED ) // P1.28 + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED ) // P1.29 + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) // P1.30 PORT_START("SERVICESW") - PORT_SERVICE_NO_TOGGLE( 0x01, IP_ACTIVE_HIGH ) PORT_CHANGED_MEMBER(DEVICE_SELF, segag80v_state,service_switch, 0) + PORT_SERVICE_NO_TOGGLE( 0x01, IP_ACTIVE_HIGH ) PORT_WRITE_LINE_MEMBER(segag80v_state, service_switch_w) INPUT_PORTS_END @@ -714,8 +790,8 @@ static INPUT_PORTS_START( zektor ) PORT_DIPSETTING( 0x40, "30000" ) PORT_MODIFY("D5D4") - PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED ) /* P1.15 */ - PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) /* P1.19 */ + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED ) // P1.15 + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) // P1.19 PORT_MODIFY("FC") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 ) @@ -755,8 +831,8 @@ static INPUT_PORTS_START( tacscan ) PORT_DIPSETTING( 0x40, "30000" ) PORT_MODIFY("D5D4") - PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED ) /* P1.15 */ - PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) /* P1.19 */ + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED ) // P1.15 + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) // P1.19 PORT_MODIFY("FC") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 ) @@ -796,8 +872,8 @@ static INPUT_PORTS_START( startrek ) PORT_DIPSETTING( 0xc0, "40000" ) PORT_MODIFY("D5D4") - PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED ) /* P1.15 */ - PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) /* P1.19 */ + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED ) // P1.15 + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) // P1.19 PORT_MODIFY("FC") @@ -814,78 +890,6 @@ INPUT_PORTS_END -/************************************* - * - * Eliminator sound interfaces - * - *************************************/ - -static const char *const elim_sample_names[] = -{ - "*elim2", - "elim1", - "elim2", - "elim3", - "elim4", - "elim5", - "elim6", - "elim7", - "elim8", - "elim9", - "elim10", - "elim11", - "elim12", - nullptr /* end of array */ -}; - - -/************************************* - * - * Space Fury sound interfaces - * - *************************************/ - -static const char *const spacfury_sample_names[] = -{ - "*spacfury", - /* Sound samples */ - "sfury1", - "sfury2", - "sfury3", - "sfury4", - "sfury5", - "sfury6", - "sfury7", - "sfury8", - "sfury9", - "sfury10", - nullptr /* end of array */ -}; - -/************************************* - * - * Zektor sound interfaces - * - *************************************/ - -static const char *const zektor_sample_names[] = -{ - "*zektor", - "elim1", /* 0 fireball */ - "elim2", /* 1 bounce */ - "elim3", /* 2 Skitter */ - "elim4", /* 3 Eliminator */ - "elim5", /* 4 Electron */ - "elim6", /* 5 fire */ - "elim7", /* 6 thrust */ - "elim8", /* 7 Electron */ - "elim9", /* 8 small explosion */ - "elim10", /* 9 med explosion */ - "elim11", /* 10 big explosion */ - nullptr -}; - - /************************************* * * Machine drivers @@ -894,83 +898,59 @@ static const char *const zektor_sample_names[] = void segag80v_state::g80v_base(machine_config &config) { - /* basic machine hardware */ + // basic machine hardware Z80(config, m_maincpu, VIDEO_CLOCK/4); m_maincpu->set_addrmap(AS_PROGRAM, &segag80v_state::main_map); m_maincpu->set_addrmap(AS_OPCODES, &segag80v_state::opcodes_map); m_maincpu->set_addrmap(AS_IO, &segag80v_state::main_portmap); - m_maincpu->set_vblank_int("screen", FUNC(segag80v_state::irq0_line_hold)); + m_maincpu->irqack_cb().set(FUNC(segag80v_state::irq_ack_w)); +// m_maincpu->set_vblank_int("screen", FUNC(segag80v_state::irq0_line_hold)); - /* video hardware */ + // video hardware SCREEN(config, m_screen, SCREEN_TYPE_VECTOR); m_screen->set_refresh_hz(40); m_screen->set_size(400, 300); m_screen->set_visarea(512, 1536, 640-32, 1408+32); m_screen->set_screen_update(FUNC(segag80v_state::screen_update_segag80v)); + m_screen->register_vblank_callback(vblank_state_delegate(&segag80v_state::vblank_callback, this)); VECTOR(config, m_vector); - /* sound hardware */ + // sound hardware SPEAKER(config, "speaker").front_center(); } void segag80v_state::elim2(machine_config &config) { g80v_base(config); - - /* custom sound board */ - SAMPLES(config, m_samples); - m_samples->set_channels(8); - m_samples->set_samples_names(elim_sample_names); - m_samples->add_route(ALL_OUTPUTS, "speaker", 1.0); + ELIMINATOR_AUDIO(config, m_g80_audio, 0).add_route(ALL_OUTPUTS, "speaker", 1.0); } void segag80v_state::spacfury(machine_config &config) { g80v_base(config); - - /* custom sound board */ - SAMPLES(config, m_samples); - m_samples->set_channels(8); - m_samples->set_samples_names(spacfury_sample_names); - m_samples->add_route(ALL_OUTPUTS, "speaker", 0.1); - - /* speech board */ - sega_speech_board(config); + SPACE_FURY_AUDIO(config, m_g80_audio, 0).add_route(ALL_OUTPUTS, "speech", 1.0); + SEGA_SPEECH_BOARD(config, "speech", 0).add_route(ALL_OUTPUTS, "speaker", 1.0); } void segag80v_state::zektor(machine_config &config) { g80v_base(config); - - /* custom sound board */ - SAMPLES(config, m_samples); - m_samples->set_channels(8); - m_samples->set_samples_names(zektor_sample_names); - m_samples->add_route(ALL_OUTPUTS, "speaker", 0.1); - - AY8912(config, m_aysnd, VIDEO_CLOCK/4/2).add_route(ALL_OUTPUTS, "speaker", 0.33); - - /* speech board */ - sega_speech_board(config); + ZEKTOR_AUDIO(config, m_g80_audio, 0).add_route(ALL_OUTPUTS, "speech", 0.7); + SEGA_SPEECH_BOARD(config, "speech", 0).add_route(ALL_OUTPUTS, "speaker", 1.0); } - void segag80v_state::tacscan(machine_config &config) { g80v_base(config); - - /* universal sound board */ SEGAUSB(config, m_usb, 0, m_maincpu).add_route(ALL_OUTPUTS, "speaker", 1.0); } - void segag80v_state::startrek(machine_config &config) { - tacscan(config); - - /* speech board */ - sega_speech_board(config); + g80v_base(config); + SEGAUSB(config, m_usb, 0, m_maincpu).add_route(ALL_OUTPUTS, "speech", 1.0); + SEGA_SPEECH_BOARD(config, "speech", 0).add_route(ALL_OUTPUTS, "speaker", 1.0); } @@ -999,8 +979,8 @@ ROM_START( elim2 ) ROM_LOAD( "1345.prom-u13", 0x6800, 0x0800, CRC(40597a92) SHA1(ee1ae2b424c38b40d2cbeda4aba3328e6d3f9c81) ) ROM_REGION( 0x0420, "proms", 0 ) - ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) /* sine table */ - ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* CPU board addressing */ + ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) // sine table + ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) // CPU board addressing ROM_END ROM_START( elim2a ) @@ -1021,8 +1001,8 @@ ROM_START( elim2a ) ROM_LOAD( "1170a.prom-u13", 0x6800, 0x0800, CRC(8cdacd35) SHA1(f24f8a74cb4b8452ddbd42e61d3b0366bbee7f98) ) ROM_REGION( 0x0420, "proms", 0 ) - ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) /* sine table */ - ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* CPU board addressing */ + ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) // sine table + ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) // CPU board addressing ROM_END ROM_START( elim2c ) @@ -1043,8 +1023,8 @@ ROM_START( elim2c ) ROM_LOAD( "1212.prom-u13", 0x6800, 0x0800, CRC(152cf376) SHA1(56c3141598b8bac81e85b1fc7052fdd19cd95609) ) ROM_REGION( 0x0420, "proms", 0 ) - ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) /* sine table */ - ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* CPU board addressing */ + ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) // sine table + ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) // CPU board addressing ROM_END ROM_START( elim4 ) @@ -1066,8 +1046,8 @@ ROM_START( elim4 ) ROM_LOAD( "1360.prom-u14", 0x7000, 0x0800, CRC(96d48238) SHA1(76a7b49081cd2d0dd1976077aa66b6d5ae5b2b43) ) ROM_REGION( 0x0420, "proms", 0 ) - ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) /* sine table */ - ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* CPU board addressing */ + ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) // sine table + ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) // CPU board addressing ROM_END ROM_START( elim4p ) @@ -1089,12 +1069,12 @@ ROM_START( elim4p ) ROM_LOAD( "swe.prom-u14", 0x7000, 0x0800, CRC(ec4cc343) SHA1(00e107eaf530ce6bec2afffd7d7bedd7763cfb17) ) ROM_REGION( 0x0420, "proms", 0 ) - ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) /* sine table */ - ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* CPU board addressing */ + ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) // sine table + ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) // CPU board addressing ROM_END -ROM_START( spacfury ) /* Revision C */ +ROM_START( spacfury ) // Revision C ROM_REGION( 0xc000, "maincpu", 0 ) ROM_LOAD( "969c.cpu-u25", 0x0000, 0x0800, CRC(411207f2) SHA1(2a082be4052b5d8f365abd0a51ea805d270d1189) ) ROM_LOAD( "960c.prom-u1", 0x0800, 0x0800, CRC(d071ab7e) SHA1(c7d2429e4fa77988d7ac62bc68f876ffb7467838) ) @@ -1107,21 +1087,23 @@ ROM_START( spacfury ) /* Revision C */ ROM_LOAD( "967c.prom-u8", 0x4000, 0x0800, CRC(330f0751) SHA1(07ae52fdbfa2cc326f88dc76c3dc8e145b592863) ) ROM_LOAD( "968c.prom-u9", 0x4800, 0x0800, CRC(8366eadb) SHA1(8e4cb30a730237da2e933370faf5eaa1a41cacbf) ) - ROM_REGION( 0x0800, "audiocpu", 0 ) + ROM_REGION( 0x0800, "speech:cpu", 0 ) ROM_LOAD( "808c.speech-u7", 0x0000, 0x0800, CRC(b779884b) SHA1(ac07e99717a1f51b79f3e43a5d873ebfa0559320) ) - ROM_REGION( 0x4000, SEGASND_SEGASPEECH_REGION, 0 ) + ROM_REGION( 0x0020, "speech:proms", 0 ) + ROM_LOAD( "6331.speech-u30", 0x0000, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) // speech board addressing + + ROM_REGION( 0x4000, "speech:data", 0 ) ROM_LOAD( "970c.speech-u6", 0x0000, 0x1000, CRC(979d8535) SHA1(1ed097e563319ca6d2b7df9875ce7ee921eae468) ) ROM_LOAD( "971c.speech-u5", 0x1000, 0x1000, CRC(022dbd32) SHA1(4e0504b5ccc28094078912673c49571cf83804ab) ) ROM_LOAD( "972c.speech-u4", 0x2000, 0x1000, CRC(fad9346d) SHA1(784e5ab0fb00235cfd733c502baf23960923504f) ) - ROM_REGION( 0x0440, "proms", 0 ) - ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) /* sine table */ - ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* CPU board addressing */ - ROM_LOAD( "6331.speech-u30", 0x0420, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* speech board addressing */ + ROM_REGION( 0x0420, "proms", 0 ) + ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) // sine table + ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) // CPU board addressing ROM_END -ROM_START( spacfurya ) /* Revision A */ +ROM_START( spacfurya ) // Revision A ROM_REGION( 0xc000, "maincpu", 0 ) ROM_LOAD( "969a.cpu-u25", 0x0000, 0x0800, CRC(896a615c) SHA1(542386196eca9fd822e36508e173201ee8a962ed) ) ROM_LOAD( "960a.prom-u1", 0x0800, 0x0800, CRC(e1ea7964) SHA1(9c84c525973fcf1437b062d98195272723249d02) ) @@ -1134,21 +1116,23 @@ ROM_START( spacfurya ) /* Revision A */ ROM_LOAD( "967a.prom-u8", 0x4000, 0x0800, CRC(d60f667d) SHA1(821271ec1918e22ed29a5b1f4b0182765ef5ba10) ) ROM_LOAD( "968a.prom-u9", 0x4800, 0x0800, CRC(aea85b6a) SHA1(8778ff0be34cd4fd5b8f6f76c64bfca68d4d240e) ) - ROM_REGION( 0x0800, "audiocpu", 0 ) + ROM_REGION( 0x0800, "speech:cpu", 0 ) ROM_LOAD( "808a.speech-u7", 0x0000, 0x0800, CRC(5988c767) SHA1(3b91a8cd46aa7e714028cc40f700fea32287afb1) ) - ROM_REGION( 0x4000, SEGASND_SEGASPEECH_REGION, 0 ) + ROM_REGION( 0x0020, "speech:proms", 0 ) + ROM_LOAD( "6331.speech-u30", 0x0000, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) // speech board addressing + + ROM_REGION( 0x4000, "speech:data", 0 ) ROM_LOAD( "970.speech-u6", 0x0000, 0x1000, CRC(f3b47b36) SHA1(6ae0b627349664140a7f70799645b368e452d69c) ) ROM_LOAD( "971.speech-u5", 0x1000, 0x1000, CRC(e72bbe88) SHA1(efadf8aa448c289cf4d0cf1831255b9ac60820f2) ) ROM_LOAD( "972.speech-u4", 0x2000, 0x1000, CRC(8b3da539) SHA1(3a0c4af96a2116fc668a340534582776b2018663) ) - ROM_REGION( 0x0440, "proms", 0 ) - ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) /* sine table */ - ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* CPU board addressing */ - ROM_LOAD( "6331.speech-u30", 0x0420, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* speech board addressing */ + ROM_REGION( 0x0420, "proms", 0 ) + ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) // sine table + ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) // CPU board addressing ROM_END -ROM_START( spacfuryb ) /* Revision B */ +ROM_START( spacfuryb ) // Revision B ROM_REGION( 0xc000, "maincpu", 0 ) ROM_LOAD( "969a.cpu-u25", 0x0000, 0x0800, CRC(896a615c) SHA1(542386196eca9fd822e36508e173201ee8a962ed) ) ROM_LOAD( "960b.prom-u1", 0x0800, 0x0800, CRC(8a99b63f) SHA1(4b9ec152e0fad50afeea11f5d61331f3211da606) ) @@ -1161,18 +1145,20 @@ ROM_START( spacfuryb ) /* Revision B */ ROM_LOAD( "967b.prom-u8", 0x4000, 0x0800, CRC(82b5768d) SHA1(823d8c0a537bad62e8186f88f8d02a0f3dc6da0f) ) ROM_LOAD( "968b.prom-u9", 0x4800, 0x0800, CRC(fea68f02) SHA1(83bef40dfaac014b7929239d81075335ff8fd506) ) - ROM_REGION( 0x0800, "audiocpu", 0 ) + ROM_REGION( 0x0800, "speech:cpu", 0 ) ROM_LOAD( "808a.speech-u7", 0x0000, 0x0800, CRC(5988c767) SHA1(3b91a8cd46aa7e714028cc40f700fea32287afb1) ) - ROM_REGION( 0x4000, SEGASND_SEGASPEECH_REGION, 0 ) + ROM_REGION( 0x0020, "speech:proms", 0 ) + ROM_LOAD( "6331.speech-u30", 0x0000, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) // speech board addressing + + ROM_REGION( 0x4000, "speech:data", 0 ) ROM_LOAD( "970.speech-u6", 0x0000, 0x1000, CRC(f3b47b36) SHA1(6ae0b627349664140a7f70799645b368e452d69c) ) ROM_LOAD( "971.speech-u5", 0x1000, 0x1000, CRC(e72bbe88) SHA1(efadf8aa448c289cf4d0cf1831255b9ac60820f2) ) ROM_LOAD( "972.speech-u4", 0x2000, 0x1000, CRC(8b3da539) SHA1(3a0c4af96a2116fc668a340534582776b2018663) ) - ROM_REGION( 0x0440, "proms", 0 ) - ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) /* sine table */ - ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* CPU board addressing */ - ROM_LOAD( "6331.speech-u30", 0x0420, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* speech board addressing */ + ROM_REGION( 0x0420, "proms", 0 ) + ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) // sine table + ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) // CPU board addressing ROM_END @@ -1201,18 +1187,20 @@ ROM_START( zektor ) ROM_LOAD( "1605.prom-u20", 0xa000, 0x0800, CRC(e27d7144) SHA1(5b82fda797d86e11882d1f9738a59092c5e3e7d8) ) ROM_LOAD( "1606.prom-u21", 0xa800, 0x0800, CRC(7965f636) SHA1(5c8720beedab4979a813ce7f0e8961c863973ff7) ) - ROM_REGION( 0x0800, "audiocpu", 0 ) + ROM_REGION( 0x0800, "speech:cpu", 0 ) ROM_LOAD( "1607.speech-u7", 0x0000, 0x0800, CRC(b779884b) SHA1(ac07e99717a1f51b79f3e43a5d873ebfa0559320) ) - ROM_REGION( 0x4000, SEGASND_SEGASPEECH_REGION, 0 ) + ROM_REGION( 0x0020, "speech:proms", 0 ) + ROM_LOAD( "6331.speech-u30", 0x0000, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) // speech board addressing + + ROM_REGION( 0x4000, "speech:data", 0 ) ROM_LOAD( "1608.speech-u6", 0x0000, 0x1000, CRC(637e2b13) SHA1(8a470f9a8a722f7ced340c4d32b4cf6f05b3e848) ) ROM_LOAD( "1609.speech-u5", 0x1000, 0x1000, CRC(675ee8e5) SHA1(e314482028b8925ad02e833a1d22224533d0a683) ) ROM_LOAD( "1610.speech-u4", 0x2000, 0x1000, CRC(2915c7bd) SHA1(3ed98747b5237aa1b3bab6866292370dc2c7655a) ) - ROM_REGION( 0x0440, "proms", 0 ) - ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) /* sine table */ - ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* CPU board addressing */ - ROM_LOAD( "6331.speech-u30", 0x0420, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* speech board addressing */ + ROM_REGION( 0x0420, "proms", 0 ) + ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) // sine table + ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) // CPU board addressing ROM_END @@ -1242,8 +1230,8 @@ ROM_START( tacscan ) ROM_LOAD( "1710a.prom-u21", 0xa800, 0x0800, CRC(6203be22) SHA1(89731c7c88d0125a11368d707f566eb53c783266) ) ROM_REGION( 0x0420, "proms", 0 ) - ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) /* sine table */ - ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* CPU board addressing */ + ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) // sine table + ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) // CPU board addressing ROM_END @@ -1274,17 +1262,19 @@ ROM_START( startrek ) ROM_LOAD( "1869.prom-u22", 0xb000, 0x0800, CRC(e5663070) SHA1(735944c2b924964f72f3bb3d251a35ea2aef3d15) ) ROM_LOAD( "1870.prom-u23", 0xb800, 0x0800, CRC(4340616d) SHA1(e93686a29377933332523425532d102e30211111) ) - ROM_REGION( 0x0800, "audiocpu", 0 ) + ROM_REGION( 0x0800, "speech:cpu", 0 ) ROM_LOAD( "1670.speech-u7", 0x0000, 0x0800, CRC(b779884b) SHA1(ac07e99717a1f51b79f3e43a5d873ebfa0559320) ) - ROM_REGION( 0x4000, SEGASND_SEGASPEECH_REGION, 0 ) + ROM_REGION( 0x0020, "speech:proms", 0 ) + ROM_LOAD( "6331.speech-u30", 0x0000, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) // speech board addressing + + ROM_REGION( 0x4000, "speech:data", 0 ) ROM_LOAD( "1871.speech-u6", 0x0000, 0x1000, CRC(03713920) SHA1(25a0158cab9983248e91133f96d1849c9e9bcbd2) ) ROM_LOAD( "1872.speech-u5", 0x1000, 0x1000, CRC(ebb5c3a9) SHA1(533b6f0499b311f561cf7aba14a7f48ca7c47321) ) - ROM_REGION( 0x0440, "proms", 0 ) - ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) /* sine table */ - ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) /* CPU board addressing */ - ROM_LOAD( "6331.speech-u30", 0x0420, 0x0020, CRC(adcb81d0) SHA1(74b0efc7e8362b0c98e54a6107981cff656d87e1) ) /* speech board addressing */ + ROM_REGION( 0x0420, "proms", 0 ) + ROM_LOAD( "s-c.xyt-u39", 0x0000, 0x0400, CRC(56484d19) SHA1(61f43126fdcfc230638ed47085ae037a098e6781) ) // sine table + ROM_LOAD( "pr-82.cpu-u15", 0x0400, 0x0020, CRC(c609b79e) SHA1(49dbcbb607079a182d7eb396c0da097166ea91c9) ) // CPU board addressing ROM_END @@ -1295,31 +1285,59 @@ ROM_END * *************************************/ +void segag80v_state::init_waitstates() +{ + address_space &pgmspace = m_maincpu->space(AS_PROGRAM); + address_space &opspace = m_maincpu->space(AS_OPCODES); + + pgmspace.install_read_tap(0x0000, 0xffff, "program_waitstate_r",[this](offs_t offset, u8 &data, u8 mem_mask) + { + if (!machine().side_effects_disabled()) + m_maincpu->adjust_icount(-WAIT_STATES); + return data; + }); + pgmspace.install_write_tap(0x0000, 0xffff, "program_waitstate_w",[this](offs_t offset, u8 &data, u8 mem_mask) + { + if (!machine().side_effects_disabled()) + m_maincpu->adjust_icount(-WAIT_STATES); + return data; + }); + + opspace.install_read_tap(0x0000, 0xffff, "opcodes_waitstate_r",[this](offs_t offset, u8 &data, u8 mem_mask) + { + if (!machine().side_effects_disabled()) + m_maincpu->adjust_icount(-WAIT_STATES); + return data; + }); +} + void segag80v_state::init_elim2() { + init_waitstates(); + address_space &iospace = m_maincpu->space(AS_IO); - /* configure security */ + // configure security m_decrypt = segag80_security(70); - /* configure sound */ - iospace.install_write_handler(0x3e, 0x3e, write8smo_delegate(*this, FUNC(segag80v_state::elim1_sh_w))); - iospace.install_write_handler(0x3f, 0x3f, write8smo_delegate(*this, FUNC(segag80v_state::elim2_sh_w))); + // configure sound + iospace.install_write_handler(0x3e, 0x3f, write8sm_delegate(*m_g80_audio, FUNC(segag80_audio_device::write))); } void segag80v_state::init_elim4() { + init_waitstates(); + address_space &iospace = m_maincpu->space(AS_IO); - /* configure security */ + // configure security m_decrypt = segag80_security(76); - /* configure sound */ - iospace.install_write_handler(0x3e, 0x3e, write8smo_delegate(*this, FUNC(segag80v_state::elim1_sh_w))); - iospace.install_write_handler(0x3f, 0x3f, write8smo_delegate(*this, FUNC(segag80v_state::elim2_sh_w))); + // configure sound + iospace.install_write_handler(0x3e, 0x3f, write8sm_delegate(*m_g80_audio, FUNC(segag80_audio_device::write))); - /* configure inputs */ + // configure inputs iospace.install_write_handler(0xf8, 0xf8, write8smo_delegate(*this, FUNC(segag80v_state::spinner_select_w))); iospace.install_read_handler(0xfc, 0xfc, read8smo_delegate(*this, FUNC(segag80v_state::elim4_input_r))); } @@ -1327,34 +1345,36 @@ void segag80v_state::init_elim4() void segag80v_state::init_spacfury() { + init_waitstates(); + address_space &iospace = m_maincpu->space(AS_IO); - /* configure security */ + // configure security m_decrypt = segag80_security(64); - /* configure sound */ - iospace.install_write_handler(0x38, 0x38, write8smo_delegate(*m_speech, FUNC(speech_sound_device::data_w))); - iospace.install_write_handler(0x3b, 0x3b, write8smo_delegate(*m_speech, FUNC(speech_sound_device::control_w))); - iospace.install_write_handler(0x3e, 0x3e, write8smo_delegate(*this, FUNC(segag80v_state::spacfury1_sh_w))); - iospace.install_write_handler(0x3f, 0x3f, write8smo_delegate(*this, FUNC(segag80v_state::spacfury2_sh_w))); + // configure sound + iospace.install_write_handler(0x38, 0x38, write8smo_delegate(*m_speech, FUNC(sega_speech_device::data_w))); + iospace.install_write_handler(0x3b, 0x3b, write8smo_delegate(*m_speech, FUNC(sega_speech_device::control_w))); + iospace.install_write_handler(0x3e, 0x3f, write8sm_delegate(*m_g80_audio, FUNC(segag80_audio_device::write))); } void segag80v_state::init_zektor() { + init_waitstates(); + address_space &iospace = m_maincpu->space(AS_IO); - /* configure security */ + // configure security m_decrypt = segag80_security(82); - /* configure sound */ - iospace.install_write_handler(0x38, 0x38, write8smo_delegate(*m_speech, FUNC(speech_sound_device::data_w))); - iospace.install_write_handler(0x3b, 0x3b, write8smo_delegate(*m_speech, FUNC(speech_sound_device::control_w))); - iospace.install_write_handler(0x3c, 0x3d, write8sm_delegate(*m_aysnd, FUNC(ay8912_device::address_data_w))); - iospace.install_write_handler(0x3e, 0x3e, write8smo_delegate(*this, FUNC(segag80v_state::zektor1_sh_w))); - iospace.install_write_handler(0x3f, 0x3f, write8smo_delegate(*this, FUNC(segag80v_state::zektor2_sh_w))); + // configure sound + iospace.install_write_handler(0x38, 0x38, write8smo_delegate(*m_speech, FUNC(sega_speech_device::data_w))); + iospace.install_write_handler(0x3b, 0x3b, write8smo_delegate(*m_speech, FUNC(sega_speech_device::control_w))); + iospace.install_write_handler(0x3c, 0x3d, write8sm_delegate(*m_g80_audio, FUNC(segag80_audio_device::write_ay))); + iospace.install_write_handler(0x3e, 0x3f, write8sm_delegate(*m_g80_audio, FUNC(segag80_audio_device::write))); - /* configure inputs */ + // configure inputs iospace.install_write_handler(0xf8, 0xf8, write8smo_delegate(*this, FUNC(segag80v_state::spinner_select_w))); iospace.install_read_handler(0xfc, 0xfc, read8smo_delegate(*this, FUNC(segag80v_state::spinner_input_r))); } @@ -1362,18 +1382,20 @@ void segag80v_state::init_zektor() void segag80v_state::init_tacscan() { + init_waitstates(); + address_space &pgmspace = m_maincpu->space(AS_PROGRAM); address_space &iospace = m_maincpu->space(AS_IO); - /* configure security */ + // configure security m_decrypt = segag80_security(76); - /* configure sound */ + // configure sound iospace.install_readwrite_handler(0x3f, 0x3f, read8smo_delegate(*m_usb, FUNC(usb_sound_device::status_r)), write8smo_delegate(*m_usb, FUNC(usb_sound_device::data_w))); pgmspace.install_read_handler(0xd000, 0xdfff, read8sm_delegate(*m_usb, FUNC(usb_sound_device::ram_r))); pgmspace.install_write_handler(0xd000, 0xdfff, write8sm_delegate(*this, FUNC(segag80v_state::usb_ram_w))); - /* configure inputs */ + // configure inputs iospace.install_write_handler(0xf8, 0xf8, write8smo_delegate(*this, FUNC(segag80v_state::spinner_select_w))); iospace.install_read_handler(0xfc, 0xfc, read8smo_delegate(*this, FUNC(segag80v_state::spinner_input_r))); } @@ -1381,21 +1403,23 @@ void segag80v_state::init_tacscan() void segag80v_state::init_startrek() { + init_waitstates(); + address_space &pgmspace = m_maincpu->space(AS_PROGRAM); address_space &iospace = m_maincpu->space(AS_IO); - /* configure security */ + // configure security m_decrypt = segag80_security(64); - /* configure sound */ - iospace.install_write_handler(0x38, 0x38, write8smo_delegate(*m_speech, FUNC(speech_sound_device::data_w))); - iospace.install_write_handler(0x3b, 0x3b, write8smo_delegate(*m_speech, FUNC(speech_sound_device::control_w))); + // configure sound + iospace.install_write_handler(0x38, 0x38, write8smo_delegate(*m_speech, FUNC(sega_speech_device::data_w))); + iospace.install_write_handler(0x3b, 0x3b, write8smo_delegate(*m_speech, FUNC(sega_speech_device::control_w))); iospace.install_readwrite_handler(0x3f, 0x3f, read8smo_delegate(*m_usb, FUNC(usb_sound_device::status_r)), write8smo_delegate(*m_usb, FUNC(usb_sound_device::data_w))); pgmspace.install_read_handler(0xd000, 0xdfff, read8sm_delegate(*m_usb, FUNC(usb_sound_device::ram_r))); pgmspace.install_write_handler(0xd000, 0xdfff, write8sm_delegate(*this, FUNC(segag80v_state::usb_ram_w))); - /* configure inputs */ + // configure inputs iospace.install_write_handler(0xf8, 0xf8, write8smo_delegate(*this, FUNC(segag80v_state::spinner_select_w))); iospace.install_read_handler(0xfc, 0xfc, read8smo_delegate(*this, FUNC(segag80v_state::spinner_input_r))); } @@ -1409,14 +1433,14 @@ void segag80v_state::init_startrek() *************************************/ // YEAR, NAME, PARENT, MACHINE, INPUT, STATE INIT, MONITOR, COMPANY, FULLNAME,FLAGS -GAME( 1981, elim2, 0, elim2, elim2, segag80v_state, init_elim2, ORIENTATION_FLIP_Y, "Gremlin", "Eliminator (2 Players, set 1)", MACHINE_IMPERFECT_SOUND ) -GAME( 1981, elim2a, elim2, elim2, elim2, segag80v_state, init_elim2, ORIENTATION_FLIP_Y, "Gremlin", "Eliminator (2 Players, set 2)", MACHINE_IMPERFECT_SOUND ) -GAME( 1981, elim2c, elim2, elim2, elim2c, segag80v_state, init_elim2, ORIENTATION_FLIP_Y, "Gremlin", "Eliminator (2 Players, cocktail)", MACHINE_IMPERFECT_SOUND ) -GAME( 1981, elim4, elim2, elim2, elim4, segag80v_state, init_elim4, ORIENTATION_FLIP_Y, "Gremlin", "Eliminator (4 Players)", MACHINE_IMPERFECT_SOUND ) -GAME( 1981, elim4p, elim2, elim2, elim4, segag80v_state, init_elim4, ORIENTATION_FLIP_Y, "Gremlin", "Eliminator (4 Players, prototype)", MACHINE_IMPERFECT_SOUND ) -GAME( 1981, spacfury, 0, spacfury, spacfury, segag80v_state, init_spacfury, ORIENTATION_FLIP_Y, "Sega", "Space Fury (revision C)", MACHINE_IMPERFECT_SOUND ) -GAME( 1981, spacfurya, spacfury, spacfury, spacfury, segag80v_state, init_spacfury, ORIENTATION_FLIP_Y, "Sega", "Space Fury (revision A)", MACHINE_IMPERFECT_SOUND ) -GAME( 1981, spacfuryb, spacfury, spacfury, spacfury, segag80v_state, init_spacfury, ORIENTATION_FLIP_Y, "Sega", "Space Fury (revision B)", MACHINE_IMPERFECT_SOUND ) -GAME( 1982, zektor, 0, zektor, zektor, segag80v_state, init_zektor, ORIENTATION_FLIP_Y, "Sega", "Zektor (revision B)", MACHINE_IMPERFECT_SOUND ) -GAME( 1982, tacscan, 0, tacscan, tacscan, segag80v_state, init_tacscan, ORIENTATION_FLIP_X ^ ROT270, "Sega", "Tac/Scan", MACHINE_IMPERFECT_SOUND ) -GAME( 1982, startrek, 0, startrek, startrek, segag80v_state, init_startrek, ORIENTATION_FLIP_Y, "Sega", "Star Trek", MACHINE_IMPERFECT_SOUND ) +GAME( 1981, elim2, 0, elim2, elim2, segag80v_state, init_elim2, ORIENTATION_FLIP_Y, "Gremlin", "Eliminator (2 Players, set 1)", 0 ) +GAME( 1981, elim2a, elim2, elim2, elim2, segag80v_state, init_elim2, ORIENTATION_FLIP_Y, "Gremlin", "Eliminator (2 Players, set 2)", 0 ) +GAME( 1981, elim2c, elim2, elim2, elim2c, segag80v_state, init_elim2, ORIENTATION_FLIP_Y, "Gremlin", "Eliminator (2 Players, cocktail)", 0 ) +GAME( 1981, elim4, elim2, elim2, elim4, segag80v_state, init_elim4, ORIENTATION_FLIP_Y, "Gremlin", "Eliminator (4 Players)", 0 ) +GAME( 1981, elim4p, elim2, elim2, elim4, segag80v_state, init_elim4, ORIENTATION_FLIP_Y, "Gremlin", "Eliminator (4 Players, prototype)", 0 ) +GAME( 1981, spacfury, 0, spacfury, spacfury, segag80v_state, init_spacfury, ORIENTATION_FLIP_Y, "Sega", "Space Fury (revision C)", 0 ) +GAME( 1981, spacfurya, spacfury, spacfury, spacfury, segag80v_state, init_spacfury, ORIENTATION_FLIP_Y, "Sega", "Space Fury (revision A)", 0 ) +GAME( 1981, spacfuryb, spacfury, spacfury, spacfury, segag80v_state, init_spacfury, ORIENTATION_FLIP_Y, "Sega", "Space Fury (revision B)", 0 ) +GAME( 1982, zektor, 0, zektor, zektor, segag80v_state, init_zektor, ORIENTATION_FLIP_Y, "Sega", "Zektor (revision B)", 0 ) +GAME( 1982, tacscan, 0, tacscan, tacscan, segag80v_state, init_tacscan, ORIENTATION_FLIP_X ^ ROT270, "Sega", "Tac/Scan", 0 ) +GAME( 1982, startrek, 0, startrek, startrek, segag80v_state, init_startrek, ORIENTATION_FLIP_Y, "Sega", "Star Trek", 0 ) diff --git a/src/mame/drivers/zaxxon.cpp b/src/mame/drivers/zaxxon.cpp index a6edd92174b..43436590c13 100644 --- a/src/mame/drivers/zaxxon.cpp +++ b/src/mame/drivers/zaxxon.cpp @@ -255,7 +255,7 @@ #include "emu.h" #include "includes/zaxxon.h" -#include "audio/segasnd.h" +#include "audio/segausb.h" #include "cpu/z80/z80.h" #include "machine/gen_latch.h" diff --git a/src/mame/includes/segag80r.h b/src/mame/includes/segag80r.h index 9e54a676882..a4c996149fd 100644 --- a/src/mame/includes/segag80r.h +++ b/src/mame/includes/segag80r.h @@ -10,8 +10,10 @@ #pragma once +#include "audio/segag80.h" #include "audio/segag80r.h" -#include "audio/segasnd.h" +#include "audio/segaspeech.h" +#include "audio/segausb.h" #include "machine/i8255.h" #include "machine/segag80.h" #include "sound/samples.h" @@ -23,11 +25,11 @@ class sega005_sound_device; -class segag80r_state : public segag80snd_common +class segag80r_state : public driver_device { public: segag80r_state(const machine_config &mconfig, device_type type, const char *tag) : - segag80snd_common(mconfig, type, tag), + driver_device(mconfig, type, tag), m_mainram(*this, "mainram"), m_videoram(*this, "videoram"), m_sn1(*this, "sn1"), @@ -36,7 +38,8 @@ public: m_audiocpu(*this, "audiocpu"), m_soundbrd(*this, "soundbrd"), m_samples(*this, "samples"), - m_speech(*this, "segaspeech"), + m_speech(*this, "speech"), + m_g80_audio(*this, "g80sound"), m_usbsnd(*this, "usbsnd"), m_005snd(*this, "005"), m_gfxdecode(*this, "gfxdecode"), @@ -53,11 +56,11 @@ public: void monster2(machine_config &config); void sega005(machine_config &config); void spaceod(machine_config &config); - void astrob_sound_board(machine_config &config); void sega005_sound_board(machine_config &config); void spaceod_sound_board(machine_config &config); void monsterb_sound_board(machine_config &config); + void init_waitstates(); void init_spaceod(); void init_sindbadm(); void init_pignewt(); @@ -91,7 +94,8 @@ private: optional_device m_audiocpu; optional_device m_soundbrd; optional_device m_samples; - optional_device m_speech; + optional_device m_speech; + optional_device m_g80_audio; optional_device m_usbsnd; optional_device m_005snd; required_device m_gfxdecode; @@ -147,7 +151,6 @@ private: void pignewt_back_port_w(offs_t offset, uint8_t data); void sindbadm_videoram_w(offs_t offset, uint8_t data); void sindbadm_back_port_w(offs_t offset, uint8_t data); - void astrob_sound_w(offs_t offset, uint8_t data); void spaceod_sound_w(offs_t offset, uint8_t data); void usb_ram_w(offs_t offset, uint8_t data); diff --git a/src/mame/includes/segag80v.h b/src/mame/includes/segag80v.h index dd247ac859c..7f5fc5d007f 100644 --- a/src/mame/includes/segag80v.h +++ b/src/mame/includes/segag80v.h @@ -6,7 +6,10 @@ *************************************************************************/ -#include "audio/segasnd.h" +#include "audio/segaspeech.h" +#include "audio/segausb.h" +#include "audio/segag80.h" +#include "cpu/z80/z80.h" #include "machine/segag80.h" #include "sound/ay8910.h" #include "sound/samples.h" @@ -14,20 +17,45 @@ #include "screen.h" -class segag80v_state : public segag80snd_common +#define CPU_CLOCK 8000000 /* not used when video boards are connected */ +#define VIDEO_CLOCK 15468480 + +class segag80v_state : public driver_device { public: - segag80v_state(const machine_config &mconfig, device_type type, const char *tag) - : segag80snd_common(mconfig, type, tag), + segag80v_state(const machine_config &mconfig, device_type type, const char *tag) : + driver_device(mconfig, type, tag), + m_mainrom(*this, "maincpu"), m_mainram(*this, "mainram"), m_vectorram(*this, "vectorram"), m_maincpu(*this, "maincpu"), m_samples(*this, "samples"), - m_speech(*this, "segaspeech"), + m_speech(*this, "speech"), m_usb(*this, "usbsnd"), + m_g80_audio(*this, "g80sound"), m_aysnd(*this, "aysnd"), m_vector(*this, "vector"), - m_screen(*this, "screen") + m_screen(*this, "screen"), + m_d7d6(*this, "D7D6"), + m_d5d4(*this, "D5D4"), + m_d3d2(*this, "D3D2"), + m_d1d0(*this, "D1D0"), + m_fc(*this, "FC"), + m_coins(*this, "COINS"), + m_spinner(*this, "SPINNER"), + m_mult_data{0,0}, + m_mult_result(0), + m_spinner_select(0), + m_spinner_sign(0), + m_spinner_count(0), + m_coin_ff_state(0), + m_coin_last_state(0), + m_edgint_ff_state(0), + m_scrambled_write_pc(0), + m_decrypt(nullptr), + m_min_x(0), + m_min_y(0), + m_draw_end_time(attotime::zero) { } void g80v_base(machine_config &config); @@ -37,6 +65,7 @@ public: void zektor(machine_config &config); void spacfury(machine_config &config); + void init_waitstates(); void init_zektor(); void init_startrek(); void init_elim4(); @@ -44,59 +73,89 @@ public: void init_tacscan(); void init_spacfury(); - DECLARE_INPUT_CHANGED_MEMBER(service_switch); DECLARE_READ_LINE_MEMBER(elim4_joint_coin_r); + DECLARE_READ_LINE_MEMBER(draw_r); + DECLARE_WRITE_LINE_MEMBER(service_switch_w); + DECLARE_WRITE_LINE_MEMBER(irq_ack_w); + + template + DECLARE_WRITE_LINE_MEMBER( coin_w ) + { + const u8 mask = 1 << _Index; + + if (state == 0 && (m_coin_last_state & mask) != 0) + m_coin_ff_state |= mask; + else + m_coin_ff_state &= ~mask; + + if (state) + m_coin_last_state |= mask; + else + m_coin_last_state &= ~mask; + + update_int(); + } private: - required_shared_ptr m_mainram; - required_shared_ptr m_vectorram; + required_memory_region m_mainrom; + required_shared_ptr m_mainram; + required_shared_ptr m_vectorram; - required_device m_maincpu; + required_device m_maincpu; optional_device m_samples; - optional_device m_speech; + optional_device m_speech; optional_device m_usb; + optional_device m_g80_audio; optional_device m_aysnd; required_device m_vector; required_device m_screen; - - uint8_t m_mult_data[2]; - uint16_t m_mult_result; - uint8_t m_spinner_select; - uint8_t m_spinner_sign; - uint8_t m_spinner_count; + required_ioport m_d7d6; + required_ioport m_d5d4; + required_ioport m_d3d2; + required_ioport m_d1d0; + required_ioport m_fc; + optional_ioport m_coins; + optional_ioport m_spinner; + + u8 m_mult_data[2]; + u16 m_mult_result; + u8 m_spinner_select; + u8 m_spinner_sign; + u8 m_spinner_count; + u8 m_coin_ff_state; + u8 m_coin_last_state; + u8 m_edgint_ff_state; offs_t m_scrambled_write_pc; segag80_decrypt_func m_decrypt; int m_min_x; int m_min_y; - uint8_t g80v_opcode_r(offs_t offset); - void mainram_w(offs_t offset, uint8_t data); - void vectorram_w(offs_t offset, uint8_t data); - uint8_t mangled_ports_r(offs_t offset); - void spinner_select_w(uint8_t data); - uint8_t spinner_input_r(); - uint8_t elim4_input_r(); - void multiply_w(offs_t offset, uint8_t data); - uint8_t multiply_r(); - void coin_count_w(uint8_t data); - void unknown_w(uint8_t data); - - void elim1_sh_w(uint8_t data); - void elim2_sh_w(uint8_t data); - void zektor1_sh_w(uint8_t data); - void zektor2_sh_w(uint8_t data); - void spacfury1_sh_w(uint8_t data); - void spacfury2_sh_w(uint8_t data); - - void usb_ram_w(offs_t offset, uint8_t data); + attotime m_draw_end_time; + + u8 opcode_r(offs_t offset); + u8 mainrom_r(offs_t offset); + void mainram_w(offs_t offset, u8 data); + void vectorram_w(offs_t offset, u8 data); + u8 mangled_ports_r(offs_t offset); + void spinner_select_w(u8 data); + u8 spinner_input_r(); + u8 elim4_input_r(); + void multiply_w(offs_t offset, u8 data); + u8 multiply_r(); + void coin_count_w(u8 data); + void unknown_w(u8 data); + void update_int(); + void vblank_callback(screen_device &screen, bool state); + + void usb_ram_w(offs_t offset, u8 data); virtual void machine_start() override; virtual void video_start() override; uint32_t screen_update_segag80v(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); - inline bool adjust_xy(int rawx, int rawy, int *outx, int *outy); + inline bool adjust_xy(int rawx, int rawy, int &outx, int &outy); void sega_generate_vector_list(); offs_t decrypt_offset(offs_t offset); - inline uint8_t demangle(uint8_t d7d6, uint8_t d5d4, uint8_t d3d2, uint8_t d1d0); + inline u8 demangle(u8 d7d6, u8 d5d4, u8 d3d2, u8 d1d0); void main_map(address_map &map); void opcodes_map(address_map &map); diff --git a/src/mame/nl.lst b/src/mame/nl.lst index a55bc5f1fcb..ce1ddf052ff 100644 --- a/src/mame/nl.lst +++ b/src/mame/nl.lst @@ -335,6 +335,49 @@ popeyej popeyejo popeyehs +@source:segag80r.cpp +005 // (c) 1981 +astrob // (c) 1981 +astrob1 // (c) 1981 +astrob2 // (c) 1981 +astrob2a // (c) 1981 +astrob2b // (c) 1981 +astrobg // (c) 1981 +monsterb // (c) 1982 +monsterb2 // (c) 1982 +pignewt // (c) 1983 +pignewta // (c) 1983 +sindbadm // 834-5244 (c) 1983 Sega +spaceod // (c) 1981 +spaceod2 // (c) 1981 + +@source:segag80v.cpp +elim2 // (c) 1981 Gremlin +elim2a // (c) 1981 Gremlin +elim2c // (c) 1981 Gremlin +elim4 // (c) 1981 Gremlin +elim4p // (c) 1981 Gremlin +spacfury // (c) 1981 +spacfurya // no copyright notice +spacfuryb // no copyright notice +startrek // (c) 1982 +tacscan // (c) 1982 +zektor // (c) 1982 + +@source:zaxxon.cpp +congo // 605-5167 (c) 1983 (2 board stack) +congoa // 605-5167 (c) 1983 (3 board stack) +futspy // (c) 1984 +ixion // (c) 1983 +razmataz // modified 834-0213, 834-0214 (c) 1983 +szaxxon // (c) 1982 +tiptop // 605-5167 (c) 1983 (3 board stack) +zaxxon // (c) 1982 +zaxxon2 // (c) 1982 +zaxxon3 // (c) 1982 +zaxxonb // bootleg +zaxxonj // (c) 1982 + @source:segas16b.cpp aceattac // (c) 1988 (FD1094) //afightere diff --git a/src/mame/video/segag80v.cpp b/src/mame/video/segag80v.cpp index 110930e5973..bc126f72fe4 100644 --- a/src/mame/video/segag80v.cpp +++ b/src/mame/video/segag80v.cpp @@ -9,13 +9,17 @@ #include "emu.h" #include "includes/segag80v.h" -#define VECTOR_CLOCK 15468480 /* master clock */ -#define U34_CLOCK (VECTOR_CLOCK/3) /* clock for interrupt chain */ -#define VCL_CLOCK (U34_CLOCK/2) /* clock for vector generator */ -#define U51_CLOCK (VCL_CLOCK/16) /* clock for phase generator */ -#define IRQ_CLOCK (U34_CLOCK/0x1f788) /* 40Hz interrupt */ +#define VECTOR_CLOCK 15468480 // master clock +#define U34_CLOCK (VECTOR_CLOCK/3) // clock for interrupt chain +#define VCL_CLOCK (U34_CLOCK/2) // clock for vector generator +#define U51_CLOCK (VCL_CLOCK/16) // clock for phase generator +#define IRQ_CLOCK (U34_CLOCK/0x1f788) // 40Hz interrupt +static constexpr attoseconds_t VCL_ATTOS = HZ_TO_ATTOSECONDS(VCL_CLOCK); +static constexpr attoseconds_t U51_ATTOS = HZ_TO_ATTOSECONDS(U51_CLOCK); +static constexpr attoseconds_t IRQ_ATTOS = HZ_TO_ATTOSECONDS(IRQ_CLOCK); + /* @@ -73,247 +77,239 @@ A1-A8 = sum of VEC angle and SYM angle (low 8 bits) A9 = sum of bit 8 of VEC angle and SYM angle, plus 1 for phase 13 - */ -inline bool segag80v_state::adjust_xy(int rawx, int rawy, int *outx, int *outy) + +inline bool segag80v_state::adjust_xy(int rawx, int rawy, int &outx, int &outy) { bool clipped = false; - /* first apply the XOR at 0x200 */ - *outx = (rawx & 0x7ff) ^ 0x200; - *outy = (rawy & 0x7ff) ^ 0x200; + // first apply the XOR at 0x200 + outx = (rawx & 0x7ff) ^ 0x200; + outy = (rawy & 0x7ff) ^ 0x200; - /* apply clipping logic to X */ - if ((*outx & 0x600) == 0x200) - *outx = 0x000, clipped = true; - else if ((*outx & 0x600) == 0x400) - *outx = 0x3ff, clipped = true; + // apply clipping logic to X + if ((outx & 0x600) == 0x200) + outx = 0x000, clipped = true; + else if ((outx & 0x600) == 0x400) + outx = 0x3ff, clipped = true; else - *outx &= 0x3ff; + outx &= 0x3ff; - /* apply clipping logic to Y */ - if ((*outy & 0x600) == 0x200) - *outy = 0x000, clipped = true; - else if ((*outy & 0x600) == 0x400) - *outy = 0x3ff, clipped = true; + // apply clipping logic to Y + if ((outy & 0x600) == 0x200) + outy = 0x000, clipped = true; + else if ((outy & 0x600) == 0x400) + outy = 0x3ff, clipped = true; else - *outy &= 0x3ff; + outy &= 0x3ff; - /* convert into .16 values */ - *outx = (*outx - (m_min_x - 512)) << 16; - *outy = (*outy - (m_min_y - 512)) << 16; + // convert into .16 values + outx = (outx - (m_min_x - 512)) << 16; + outy = (outy - (m_min_y - 512)) << 16; return clipped; } void segag80v_state::sega_generate_vector_list() { - uint8_t *sintable = memregion("proms")->base(); - double total_time = 1.0 / (double)IRQ_CLOCK; - uint16_t symaddr = 0; - uint8_t *vectorram = m_vectorram; + attoseconds_t time_remaining = IRQ_ATTOS; + u8 *sintable = memregion("proms")->base(); + u8 *vectorram = m_vectorram; + u16 symaddr = 0; m_vector->clear_list(); - /* Loop until we run out of time. */ - while (total_time > 0) + // Loop until we run out of time. + while (time_remaining > 0) { - uint16_t curx, cury, xaccum, yaccum; - uint16_t vecaddr, symangle; - uint8_t scale, draw; - - /* The "draw" flag is clocked at the end of phase 0. */ - draw = vectorram[symaddr++ & 0xfff]; + // The "draw" flag is clocked at the end of phase 0. + u8 draw = vectorram[symaddr++ & 0xfff]; - /* The low byte of the X coordinate is latched into the */ - /* up/down counters at U15/U16 during phase 1. */ - curx = vectorram[symaddr++ & 0xfff]; + // The low byte of the X coordinate is latched into the + // up/down counters at U15/U16 during phase 1. + u16 curx = vectorram[symaddr++ & 0xfff]; - /* The low 3 bits of the high byte of the X coordinate are */ - /* latched into the up/down counter at U17 during phase 2. */ - /* Bit 2 of the input is latched as both bit 2 and 3. */ + // The low 3 bits of the high byte of the X coordinate are + // latched into the up/down counter at U17 during phase 2. + // Bit 2 of the input is latched as both bit 2 and 3. curx |= (vectorram[symaddr++ & 0xfff] & 7) << 8; curx |= (curx << 1) & 0x800; - /* The low byte of the Y coordinate is latched into the */ - /* up/down counters at U18/U19 during phase 3. */ - cury = vectorram[symaddr++ & 0xfff]; + // The low byte of the Y coordinate is latched into the + // up/down counters at U18/U19 during phase 3. + u16 cury = vectorram[symaddr++ & 0xfff]; - /* The low 3 bits of the high byte of the X coordinate are */ - /* latched into the up/down counter at U17 during phase 4. */ - /* Bit 2 of the input is latched as both bit 2 and 3. */ + // The low 3 bits of the high byte of the X coordinate are + // latched into the up/down counter at U17 during phase 4. + // Bit 2 of the input is latched as both bit 2 and 3. cury |= (vectorram[symaddr++ & 0xfff] & 7) << 8; cury |= (cury << 1) & 0x800; - /* The low byte of the vector address is latched into the */ - /* counters at U10/U11 during phase 5. */ - vecaddr = vectorram[symaddr++ & 0xfff]; + // The low byte of the vector address is latched into the + // counters at U10/U11 during phase 5. + u16 vecaddr = vectorram[symaddr++ & 0xfff]; - /* The low 4 bits of the high byte of the vector address is */ - /* latched into the counter at U12 during phase 6. */ + // The low 4 bits of the high byte of the vector address is + // latched into the counter at U12 during phase 6. vecaddr |= (vectorram[symaddr++ & 0xfff] & 0xf) << 8; - /* The low byte of the symbol angle is latched into the tri- */ - /* state flip flop at U55 at the end of phase 7. */ - symangle = vectorram[symaddr++ & 0xfff]; + // The low byte of the symbol angle is latched into the tri- + // state flip flop at U55 at the end of phase 7. + u16 symangle = vectorram[symaddr++ & 0xfff]; - /* The low 2 bits of the high byte of the symbol angle are */ - /* latched into flip flops at U26 at the end of phase 8. */ + // The low 2 bits of the high byte of the symbol angle are + // latched into flip flops at U26 at the end of phase 8. symangle |= (vectorram[symaddr++ & 0xfff] & 3) << 8; - /* The scale is latched in phase 9 as the X input to the */ - /* 25LS14 multiplier at U8. */ - scale = vectorram[symaddr++ & 0xfff]; + // The scale is latched in phase 9 as the X input to the + // 25LS14 multiplier at U8. + u8 scale = vectorram[symaddr++ & 0xfff]; - /* Account for the 10 phases so far. */ - total_time -= 10.0 / (double)U51_CLOCK; + // Account for the 10 phases so far. + time_remaining -= 10 * U51_ATTOS; - /* Skip the rest if we're not drawing this symbol. */ + // Skip the rest if we're not drawing this symbol. if (draw & 1) { - int adjx, adjy, clipped; - - /* Add a starting point to the vector list. */ - clipped = adjust_xy(curx, cury, &adjx, &adjy); + // Add a starting point to the vector list. + int adjx, adjy; + bool clipped = adjust_xy(curx, cury, adjx, adjy); if (!clipped) m_vector->add_point(adjx, adjy, 0, 0); - /* Loop until we run out of time. */ - while (total_time > 0) + // Loop until we run out of time. + while (time_remaining > 0) { - uint16_t vecangle, length, deltax, deltay; - uint8_t attrib, intensity; - uint32_t color; - - /* The 'attribute' byte is latched at the end of phase 10 into */ - /* the tri-state flip flop at U2. The low bit controls whether */ - /* or not the beam is enabled. Bits 1-6 control the RGB color */ - /* (2 bits per component). In addition, bit 7 of this value is */ - /* latched into U52, which controls the pre-load value for the */ - /* phase generator. If bit 7 is high, then the phase generator */ - /* will reset back to 0 and draw a new symbol; if bit 7 is low */ - /* the phase generator will reset back to 10 and draw another */ - /* vector. */ - attrib = vectorram[vecaddr++ & 0xfff]; - - /* The length of the vector is loaded into the shift registers */ - /* at U6/U7 during phase 11. During phase 12, the 25LS14 */ - /* multiplier at U8 is used to multiply the length by the */ - /* scale that was loaded during phase 9. The length is clocked */ - /* bit by bit out of U6/U7 and the result is clocked into the */ - /* other side. After the multiply, the 9 MSBs are loaded into */ - /* the counter chain at U15/16/17 and are used to count how */ - /* long to draw the vector. */ - length = (vectorram[vecaddr++ & 0xfff] * scale) >> 7; - - /* The vector angle low byte is latched at the end of phase 12 */ - /* into the tri-state flip flop at U56. */ - vecangle = vectorram[vecaddr++ & 0xfff]; - - /* The vector angle high byte is preset on the CD bus during */ - /* phases 13 and 14, and is used as inputs to the adder at */ - /* U46. */ + // The 'attribute' byte is latched at the end of phase 10 into + // the tri-state flip flop at U2. The low bit controls whether + // or not the beam is enabled. Bits 1-6 control the RGB color + // (2 bits per component). In addition, bit 7 of this value is + // latched into U52, which controls the pre-load value for the + // phase generator. If bit 7 is high, then the phase generator + // will reset back to 0 and draw a new symbol; if bit 7 is low + // the phase generator will reset back to 10 and draw another + // vector. + u8 attrib = vectorram[vecaddr++ & 0xfff]; + + // The length of the vector is loaded into the shift registers + // at U6/U7 during phase 11. During phase 12, the 25LS14 + // multiplier at U8 is used to multiply the length by the + // scale that was loaded during phase 9. The length is clocked + // bit by bit out of U6/U7 and the result is clocked into the + // other side. After the multiply, the 9 MSBs are loaded into + // the counter chain at U15/16/17 and are used to count how + // long to draw the vector. + u16 length = (vectorram[vecaddr++ & 0xfff] * scale) >> 7; + + // The vector angle low byte is latched at the end of phase 12 + // into the tri-state flip flop at U56. + u16 vecangle = vectorram[vecaddr++ & 0xfff]; + + // The vector angle high byte is preset on the CD bus during + // phases 13 and 14, and is used as inputs to the adder at + // U46. vecangle |= (vectorram[vecaddr++ & 0xfff] & 3) << 8; - /* The X increment value is looked up first (phase 13). The */ - /* sum of the latched symbol angle and the vector angle is */ - /* used as input to the PROM at U39. A0 is tied to ground. */ - /* A1-A9 map to bits 0-8 of the summed angles. The output from */ - /* the PROM is latched into U48. */ - deltax = sintable[((vecangle + symangle) & 0x1ff) << 1]; - - /* The Y increment value is looked up second (phase 14). The */ - /* angle sum is used once again as the input to the PROM, but */ - /* this time an additional 0x100 is effectively added to it */ - /* before it is used; this separates sin from cos. The output */ - /* from the PROM is latched into U49. */ - deltay = sintable[((vecangle + symangle + 0x100) & 0x1ff) << 1]; - - /* Account for the 4 phases for data fetching. */ - total_time -= 4.0 / (double)U51_CLOCK; - - /* Compute color/intensity values from the attributes */ - color = vector_device::color222((attrib >> 1) & 0x3f); + // The X increment value is looked up first (phase 13). The + // sum of the latched symbol angle and the vector angle is + // used as input to the PROM at U39. A0 is tied to ground. + // A1-A9 map to bits 0-8 of the summed angles. The output from + // the PROM is latched into U48. + u16 deltax = sintable[((vecangle + symangle) & 0x1ff) << 1]; + + // The Y increment value is looked up second (phase 14). The + // angle sum is used once again as the input to the PROM, but + // this time an additional 0x100 is effectively added to it + // before it is used; this separates sin from cos. The output + // from the PROM is latched into U49. + u16 deltay = sintable[((vecangle + symangle + 0x100) & 0x1ff) << 1]; + + // Account for the 4 phases for data fetching. + time_remaining -= 4 * U51_ATTOS; + + // Compute color/intensity values from the attributes + u32 color = vector_device::color222((attrib >> 1) & 0x3f); + u8 intensity = 0; if ((attrib & 1) && color) intensity = 0xff; - else - intensity = 0; - /* Loop over the length of the vector. */ - clipped = adjust_xy(curx, cury, &adjx, &adjy); - xaccum = yaccum = 0; - while (length-- != 0 && total_time > 0) + // Loop over the length of the vector. + clipped = adjust_xy(curx, cury, adjx, adjy); + u16 xaccum = 0; + u16 yaccum = 0; + while (length-- != 0 && time_remaining > 0) { - int newclip; - - /* The adders at U44/U45 are used as X accumulators. The value */ - /* from U48 is repeatedly added to itself here. The carry out */ - /* of bit 8 clocks the up/down counters at U15/U16/U17. Bit 7 */ - /* of the input value from U48 is used as a carry in to round */ - /* small values downward and larger values upward. */ + // The adders at U44/U45 are used as X accumulators. The value + // from U48 is repeatedly added to itself here. The carry out + // of bit 8 clocks the up/down counters at U15/U16/U17. Bit 7 + // of the input value from U48 is used as a carry in to round + // small values downward and larger values upward. xaccum += deltax + (deltax >> 7); - /* Bit 9 of the summed angles controls the direction the up/ */ - /* down counters at U15/U16/U17. */ + // Bit 9 of the summed angles controls the direction the up/ + // down counters at U15/U16/U17. if (((vecangle + symangle) & 0x200) == 0) curx += xaccum >> 8; else curx -= xaccum >> 8; xaccum &= 0xff; - /* The adders at U46/U47 are used as Y accumulators. The value */ - /* from U49 is repeatedly added to itself here. The carry out */ - /* of bit 8 clocks the up/down counters at U18/U19/U20. Bit 7 */ - /* of the input value from U49 is used as a carry in to round */ - /* small values downward and larger values upward. */ + // The adders at U46/U47 are used as Y accumulators. The value + // from U49 is repeatedly added to itself here. The carry out + // of bit 8 clocks the up/down counters at U18/U19/U20. Bit 7 + // of the input value from U49 is used as a carry in to round + // small values downward and larger values upward. yaccum += deltay + (deltay >> 7); - /* Bit 9 of the summed angles controls the direction the up/ */ - /* down counters at U18/U19/U20. */ + // Bit 9 of the summed angles controls the direction the up/ + // down counters at U18/U19/U20. if (((vecangle + symangle + 0x100) & 0x200) == 0) cury += yaccum >> 8; else cury -= yaccum >> 8; yaccum &= 0xff; - /* Apply the clipping from the DAC circuit. If the values clip */ - /* the beam is turned off, but the computations continue right */ - /* on going. */ - newclip = adjust_xy(curx, cury, &adjx, &adjy); + // Apply the clipping from the DAC circuit. If the values clip + // the beam is turned off, but the computations continue right + // on going. + bool newclip = adjust_xy(curx, cury, adjx, adjy); if (newclip != clipped) { - /* if we're just becoming unclipped, add an empty point */ + // if we're just becoming unclipped, add an empty point if (!newclip) m_vector->add_point(adjx, adjy, 0, 0); - /* otherwise, add a colored point */ + // otherwise, add a colored point else m_vector->add_point(adjx, adjy, color, intensity); } clipped = newclip; - /* account for vector drawing time */ - total_time -= 1.0 / (double)VCL_CLOCK; + // account for vector drawing time + time_remaining -= VCL_ATTOS; } - /* We're done; if we are not clipped, add a final point. */ + // We're done; if we are not clipped, add a final point. if (!clipped) m_vector->add_point(adjx, adjy, color, intensity); - /* if the high bit of the attribute is set, we break out of */ - /* this loop and fetch another symbol */ + // if the high bit of the attribute is set, we break out of + // this loop and fetch another symbol if (attrib & 0x80) break; } } - /* if the high bit of the draw flag is set, we break out of this loop */ - /* and stop the rendering altogether for this frame. */ + // if the high bit of the draw flag is set, we break out of this loop + // and stop the rendering altogether for this frame. if (draw & 0x80) break; } + + // set the drawing end time for this frame + m_draw_end_time = machine().scheduler().time() + attotime(0, IRQ_ATTOS - time_remaining); } @@ -328,12 +324,12 @@ void segag80v_state::video_start() if (!m_vectorram.bytes()) throw emu_fatalerror("segag80v_state::video_start: !vectorram.bytes()"); - m_min_x =m_screen->visible_area().min_x; - m_min_y =m_screen->visible_area().min_y; + m_min_x = m_screen->visible_area().min_x; + m_min_y = m_screen->visible_area().min_y; } -uint32_t segag80v_state::screen_update_segag80v(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +u32 segag80v_state::screen_update_segag80v(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { sega_generate_vector_list(); m_vector->screen_update(screen, bitmap, cliprect); -- cgit v1.2.3