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:
Aaron Giles 2020-08-19 19:33:13 -07:00 committed by GitHub
parent 92925532c3
commit f7b263de20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 7083 additions and 2431 deletions

View File

@ -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",

View File

@ -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",

View File

@ -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()

View File

@ -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,

View File

@ -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();
}

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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)

View File

@ -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;
};

View File

@ -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;

View File

@ -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__)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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")

View File

@ -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

File diff suppressed because it is too large Load Diff

View 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

View File

@ -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

File diff suppressed because it is too large Load Diff

11
src/mame/audio/nl_elim.h Normal file
View 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

View 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()

View 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

View 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()

View 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

File diff suppressed because it is too large Load Diff

View 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
View 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
View 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

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View 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
}

View 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
View 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
View 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

View File

@ -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

View File

@ -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"

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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);