mirror of
https://github.com/holub/mame
synced 2025-06-05 12:26:35 +03:00
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.
This commit is contained in:
parent
92925532c3
commit
f7b263de20
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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()
|
||||
|
@ -12,9 +12,14 @@
|
||||
#define MAME_MACHINE_NETLIST_H
|
||||
|
||||
#include <functional>
|
||||
#include <deque>
|
||||
|
||||
#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<buffer_entry> m_buffer;
|
||||
FILE* m_csv_file = nullptr;
|
||||
#endif
|
||||
};
|
||||
|
||||
class netlist_mame_cpu_device : public netlist_mame_device,
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
//
|
||||
// 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;
|
||||
|
||||
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.
|
||||
|
||||
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_amp(0),
|
||||
m_pitch(0),
|
||||
m_repeat(0),
|
||||
m_pcount(0),
|
||||
m_rcount(0),
|
||||
m_playing(0),
|
||||
m_RNG(0),
|
||||
m_stream(nullptr),
|
||||
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_pcount(0),
|
||||
m_repeat(0),
|
||||
m_rcount(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));
|
||||
|
||||
save_item(NAME(m_amp));
|
||||
save_item(NAME(m_pitch));
|
||||
save_item(NAME(m_repeat));
|
||||
save_item(NAME(m_pcount));
|
||||
save_item(NAME(m_rcount));
|
||||
save_item(NAME(m_playing));
|
||||
save_item(NAME(m_RNG));
|
||||
// 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_pcount));
|
||||
save_item(NAME(m_repeat));
|
||||
save_item(NAME(m_rcount));
|
||||
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;
|
||||
m_pwm_index = 0;
|
||||
if (m_pwm_counts == 0)
|
||||
next();
|
||||
m_pwm_count = m_pwm_counts & 0xff;
|
||||
m_pwm_counts >>= 8;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
value = 32767;
|
||||
remaining = m_pwm_count - m_pwm_index;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Borrowing the ay noise generation LFSR
|
||||
if(m_RNG & 1)
|
||||
{
|
||||
z0 = m_amp;
|
||||
m_RNG ^= 0x24000;
|
||||
}
|
||||
else
|
||||
z0 = -m_amp;
|
||||
|
||||
m_RNG >>= 1;
|
||||
value = 0;
|
||||
remaining = PWM_CLOCKS - m_pwm_index;
|
||||
}
|
||||
|
||||
for (int f = 0; f < 6; f++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
// clamp to the number of samples requested and advance the counters
|
||||
if (remaining > samples)
|
||||
remaining = samples;
|
||||
m_pwm_index += remaining;
|
||||
samples -= remaining;
|
||||
|
||||
// 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)
|
||||
{
|
||||
m_pcount = 0;
|
||||
m_rcount++;
|
||||
if (m_rcount >= m_repeat)
|
||||
m_playing = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
output[i] = 0;
|
||||
|
||||
if (!m_playing)
|
||||
{
|
||||
if(m_fifo_pos == 15)
|
||||
load_values();
|
||||
// fill the output
|
||||
while (remaining-- != 0)
|
||||
*output++ = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -46,17 +46,15 @@ namespace netlist
|
||||
namespace devices
|
||||
{
|
||||
|
||||
template <unsigned _LiveBitmask>
|
||||
template <unsigned _TotalBits, unsigned _LiveBitmask>
|
||||
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<logic_output_t, MAX_BITS> m_Q;
|
||||
object_array_t<logic_output_t, _TotalBits> m_Q;
|
||||
|
||||
state_var<unsigned> 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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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__)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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<analog::NETLIB_NAME(twoterm)>())
|
||||
{
|
||||
@ -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)
|
||||
|
1161
src/mame/audio/nl_astrob.cpp
Normal file
1161
src/mame/audio/nl_astrob.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10
src/mame/audio/nl_astrob.h
Normal file
10
src/mame/audio/nl_astrob.h
Normal file
@ -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
|
@ -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)")
|
||||
|
||||
|
1225
src/mame/audio/nl_elim.cpp
Normal file
1225
src/mame/audio/nl_elim.cpp
Normal file
File diff suppressed because it is too large
Load Diff
11
src/mame/audio/nl_elim.h
Normal file
11
src/mame/audio/nl_elim.h
Normal file
@ -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
|
119
src/mame/audio/nl_segaspeech.cpp
Normal file
119
src/mame/audio/nl_segaspeech.cpp
Normal file
@ -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()
|
10
src/mame/audio/nl_segaspeech.h
Normal file
10
src/mame/audio/nl_segaspeech.h
Normal file
@ -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
|
544
src/mame/audio/nl_segausb.cpp
Normal file
544
src/mame/audio/nl_segausb.cpp
Normal file
@ -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()
|
10
src/mame/audio/nl_segausb.h
Normal file
10
src/mame/audio/nl_segausb.h
Normal file
@ -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
|
1105
src/mame/audio/nl_spacfury.cpp
Normal file
1105
src/mame/audio/nl_spacfury.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10
src/mame/audio/nl_spacfury.h
Normal file
10
src/mame/audio/nl_spacfury.h
Normal file
@ -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
|
185
src/mame/audio/segag80.cpp
Normal file
185
src/mame/audio/segag80.cpp
Normal file
@ -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)
|
||||
{
|
||||
}
|
74
src/mame/audio/segag80.h
Normal file
74
src/mame/audio/segag80.h
Normal file
@ -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<netlist_mame_logic_input_device, 8> m_lo_input;
|
||||
optional_device_array<netlist_mame_logic_input_device, 8> m_hi_input;
|
||||
optional_device<ay8912_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
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
}
|
@ -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 <cmath>
|
||||
|
||||
|
||||
#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);
|
||||
}
|
@ -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<cpu_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 <typename T> 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<i8035_device> m_ourcpu;
|
||||
required_device<cpu_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<u8> m_program_ram; // pointer to program RAM
|
||||
required_shared_ptr<u8> 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 <typename T> 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
|
217
src/mame/audio/segaspeech.cpp
Normal file
217
src/mame/audio/segaspeech.cpp
Normal file
@ -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
|
||||
}
|
61
src/mame/audio/segaspeech.h
Normal file
61
src/mame/audio/segaspeech.h
Normal file
@ -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<cpu_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
|
859
src/mame/audio/segausb.cpp
Normal file
859
src/mame/audio/segausb.cpp
Normal file
@ -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 <cmath>
|
||||
|
||||
|
||||
#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);
|
||||
}
|
192
src/mame/audio/segausb.h
Normal file
192
src/mame/audio/segausb.h
Normal file
@ -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 <typename T> 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<u8> m_program_ram; // pointer to program RAM
|
||||
required_shared_ptr<u8> 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<i8035_device> m_ourcpu;
|
||||
required_device<cpu_device> m_maincpu;
|
||||
|
||||
private:
|
||||
#if (ENABLE_SEGAUSB_NETLIST)
|
||||
// PIT devices
|
||||
required_device_array<pit8253_device, 3> m_pit;
|
||||
|
||||
// channel 0
|
||||
required_device_array<netlist_mame_analog_input_device, 3> m_nl_dac0;
|
||||
required_device<netlist_mame_logic_input_device> m_nl_sel0;
|
||||
required_device_array<netlist_mame_logic_input_device, 3> m_nl_pit0_out;
|
||||
|
||||
// channel 1
|
||||
required_device_array<netlist_mame_analog_input_device, 3> m_nl_dac1;
|
||||
required_device<netlist_mame_logic_input_device> m_nl_sel1;
|
||||
required_device_array<netlist_mame_logic_input_device, 3> m_nl_pit1_out;
|
||||
|
||||
// channel 2
|
||||
required_device_array<netlist_mame_analog_input_device, 3> m_nl_dac2;
|
||||
required_device<netlist_mame_logic_input_device> m_nl_sel2;
|
||||
required_device_array<netlist_mame_logic_input_device, 3> 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 <typename T> 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
|
@ -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 )
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
|
@ -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<cpu_device> m_audiocpu;
|
||||
optional_device<monsterb_sound_device> m_soundbrd;
|
||||
optional_device<samples_device> m_samples;
|
||||
optional_device<speech_sound_device> m_speech;
|
||||
optional_device<sega_speech_device> m_speech;
|
||||
optional_device<segag80_audio_device> m_g80_audio;
|
||||
optional_device<usb_sound_device> m_usbsnd;
|
||||
optional_device<sega005_sound_device> m_005snd;
|
||||
required_device<gfxdecode_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);
|
||||
|
@ -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<int _Index>
|
||||
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<uint8_t> m_mainram;
|
||||
required_shared_ptr<uint8_t> m_vectorram;
|
||||
required_memory_region m_mainrom;
|
||||
required_shared_ptr<u8> m_mainram;
|
||||
required_shared_ptr<u8> m_vectorram;
|
||||
|
||||
required_device<cpu_device> m_maincpu;
|
||||
required_device<z80_device> m_maincpu;
|
||||
optional_device<samples_device> m_samples;
|
||||
optional_device<speech_sound_device> m_speech;
|
||||
optional_device<sega_speech_device> m_speech;
|
||||
optional_device<usb_sound_device> m_usb;
|
||||
optional_device<segag80_audio_device> m_g80_audio;
|
||||
optional_device<ay8912_device> m_aysnd;
|
||||
required_device<vector_device> m_vector;
|
||||
required_device<screen_device> m_screen;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
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);
|
||||
attotime m_draw_end_time;
|
||||
|
||||
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);
|
||||
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, uint8_t data);
|
||||
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);
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
u8 draw = vectorram[symaddr++ & 0xfff];
|
||||
|
||||
/* The "draw" flag is clocked at the end of phase 0. */
|
||||
draw = 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 byte of the X coordinate is latched into the */
|
||||
/* up/down counters at U15/U16 during phase 1. */
|
||||
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.
|
||||
u8 attrib = vectorram[vecaddr++ & 0xfff];
|
||||
|
||||
/* 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.
|
||||
u16 length = (vectorram[vecaddr++ & 0xfff] * scale) >> 7;
|
||||
|
||||
/* 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.
|
||||
u16 vecangle = vectorram[vecaddr++ & 0xfff];
|
||||
|
||||
/* 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 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 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. */
|
||||
deltay = sintable[((vecangle + symangle + 0x100) & 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. */
|
||||
total_time -= 4.0 / (double)U51_CLOCK;
|
||||
// Account for the 4 phases for data fetching.
|
||||
time_remaining -= 4 * U51_ATTOS;
|
||||
|
||||
/* Compute color/intensity values from the attributes */
|
||||
color = vector_device::color222((attrib >> 1) & 0x3f);
|
||||
// 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);
|
||||
|
Loading…
Reference in New Issue
Block a user