summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/upd7759.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/upd7759.cpp')
-rw-r--r--src/devices/sound/upd7759.cpp283
1 files changed, 125 insertions, 158 deletions
diff --git a/src/devices/sound/upd7759.cpp b/src/devices/sound/upd7759.cpp
index b800f6a7b0e..ff23e9dd6ec 100644
--- a/src/devices/sound/upd7759.cpp
+++ b/src/devices/sound/upd7759.cpp
@@ -126,9 +126,14 @@
#include "upd7759.h"
-#define DEBUG_STATES (0)
+#define MASK_LOG_STATE (1U << 1)
+#define MASK_LOG_DRQ (1U << 2)
+//#define VERBOSE (MASK_LOG_STATE|MASK_LOG_DRQ)
+#include "logmacro.h"
+#define LOG_STATE(...) LOGMASKED(MASK_LOG_STATE, __VA_ARGS__)
+#define LOG_DRQ(...) LOGMASKED(MASK_LOG_DRQ, __VA_ARGS__)
/************************************************************
@@ -136,7 +141,7 @@
*************************************************************/
-/* step value fractional bits */
+// step value fractional bits
#define FRAC_BITS 20
#define FRAC_ONE (1 << FRAC_BITS)
#define FRAC_MASK (FRAC_ONE - 1)
@@ -145,7 +150,7 @@
upd775x_device::upd775x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, type, tag, owner, clock)
, device_sound_interface(mconfig, *this)
- , device_rom_interface(mconfig, *this, 17)
+ , device_rom_interface(mconfig, *this)
, m_channel(nullptr)
, m_sample_offset_shift(0)
, m_pos(0)
@@ -167,6 +172,8 @@ upd775x_device::upd775x_device(const machine_config &mconfig, device_type type,
, m_first_valid_header(0)
, m_offset(0)
, m_repeat_offset(0)
+ , m_start_delay(0)
+ , m_mode(MODE_STAND_ALONE)
, m_adpcm_state(0)
, m_adpcm_data(0)
, m_sample(0)
@@ -208,13 +215,10 @@ upd7756_device::upd7756_device(const machine_config &mconfig, device_type type,
void upd775x_device::device_start()
{
- // allocate a stream channel
- m_channel = machine().sound().stream_alloc(*this, 0, 1, clock()/4);
+ m_channel = stream_alloc(0, 1, clock()/4);
- // compute the stepping rate based on the chip's clock speed
m_step = 4 * FRAC_ONE;
- // compute the clock period
m_clock_period = clock() ? attotime::from_hz(clock()) : attotime::zero;
save_item(NAME(m_pos));
@@ -242,6 +246,8 @@ void upd775x_device::device_start()
save_item(NAME(m_adpcm_state));
save_item(NAME(m_adpcm_data));
save_item(NAME(m_sample));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_md));
}
void upd775x_device::device_clock_changed()
@@ -251,7 +257,7 @@ void upd775x_device::device_clock_changed()
m_channel->set_sample_rate(clock() / 4);
}
-void upd775x_device::rom_bank_updated()
+void upd775x_device::rom_bank_pre_change()
{
m_channel->update();
}
@@ -260,26 +266,12 @@ void upd7759_device::device_start()
{
upd775x_device::device_start();
- // chip configuration
m_sample_offset_shift = 1;
- m_timer = timer_alloc(TIMER_SLAVE_UPDATE);
-
- m_drqcallback.resolve_safe();
-
- // toggle the reset line to finish the reset
- device_reset();
+ m_timer = timer_alloc(FUNC(upd7759_device::drq_update), this);
}
-void upd7756_device::device_start()
-{
- upd775x_device::device_start();
-
- // toggle the reset line to finish the reset
- device_reset();
-}
-
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
@@ -287,7 +279,6 @@ void upd7756_device::device_start()
void upd775x_device::device_reset()
{
m_pos = 0;
- m_fifo_in = 0;
m_state = STATE_IDLE;
m_clocks_left = 0;
m_nibbles_left = 0;
@@ -304,13 +295,13 @@ void upd775x_device::device_reset()
m_adpcm_state = 0;
m_adpcm_data = 0;
m_sample = 0;
+ m_mode = MODE_STAND_ALONE;
}
void upd7759_device::device_reset()
{
upd775x_device::device_reset();
- // turn off any timer
m_timer->adjust(attotime::never);
if (m_drq)
@@ -364,11 +355,9 @@ static const int upd775x_state_table[16] = { -1, -1, 0, 0, 1, 2, 2, 3, -1, -1, 0
void upd775x_device::update_adpcm(int data)
{
- /* update the sample and the state */
m_sample += upd775x_step[m_adpcm_state][data];
m_adpcm_state += upd775x_state_table[data];
- /* clamp the state to 0..15 */
if (m_adpcm_state < 0)
m_adpcm_state = 0;
else if (m_adpcm_state > 15)
@@ -387,12 +376,12 @@ void upd775x_device::advance_state()
{
switch (m_state)
{
- /* Idle state: we stick around here while there's nothing to do */
+ // Idle state: we stick around here while there's nothing to do
case STATE_IDLE:
m_clocks_left = 4;
break;
- /* drop DRQ state: update to the intended state */
+ // drop DRQ state: update to the intended state
case STATE_DROP_DRQ:
m_drq = 0;
@@ -400,10 +389,10 @@ void upd775x_device::advance_state()
m_state = m_post_drq_state;
break;
- /* Start state: we begin here as soon as a sample is triggered */
+ // Start state: we begin here as soon as a sample is triggered
case STATE_START:
- m_req_sample = m_md ? m_fifo_in : 0x10;
- if (DEBUG_STATES) logerror("req_sample = %02X\n", m_req_sample);
+ m_req_sample = (m_mode == MODE_STAND_ALONE) ? m_fifo_in : 0x10;
+ LOG_STATE("req_sample = %02X\n", m_req_sample);
/* 35+ cycles after we get here, the /DRQ goes low
* (first byte (number of samples in ROM) should be sent in response)
@@ -412,17 +401,16 @@ void upd775x_device::advance_state()
* Depending on the state the chip was in just before the /MD was set to 0 (reset, standby
* or just-finished-playing-previous-sample) this number can range from 35 up to ~24000).
* It also varies slightly from test to test, but not much - a few cycles at most.) */
- m_clocks_left = 70; /* 35 - breaks cotton */
+ m_clocks_left = 70 + m_start_delay;
m_state = STATE_FIRST_REQ;
break;
/* First request state: issue a request for the first byte */
/* The expected response will be the index of the last sample */
case STATE_FIRST_REQ:
- if (DEBUG_STATES) logerror("first data request\n");
+ LOG_STATE("first data request\n");
m_drq = 1;
- /* 44 cycles later, we will latch this value and request another byte */
m_clocks_left = 44;
m_state = STATE_LAST_SAMPLE;
break;
@@ -430,22 +418,20 @@ void upd775x_device::advance_state()
/* Last sample state: latch the last sample value and issue a request for the second byte */
/* The second byte read will be just a dummy */
case STATE_LAST_SAMPLE:
- m_last_sample = m_md ? read_byte(0) : m_fifo_in;
- if (DEBUG_STATES) logerror("last_sample = %02X, requesting dummy 1\n", m_last_sample);
+ m_last_sample = (m_mode == MODE_STAND_ALONE) ? read_byte(0) : m_fifo_in;
+ LOG_STATE("last_sample = %02X, requesting dummy 1\n", m_last_sample);
m_drq = 1;
- /* 28 cycles later, we will latch this value and request another byte */
- m_clocks_left = 28; /* 28 - breaks cotton */
+ m_clocks_left = 28;
m_state = (m_req_sample > m_last_sample) ? STATE_IDLE : STATE_DUMMY1;
break;
/* First dummy state: ignore any data here and issue a request for the third byte */
/* The expected response will be the MSB of the sample address */
case STATE_DUMMY1:
- if (DEBUG_STATES) logerror("dummy1, requesting offset_hi\n");
+ LOG_STATE("dummy1, requesting offset_hi\n");
m_drq = 1;
- /* 32 cycles later, we will latch this value and request another byte */
m_clocks_left = 32;
m_state = STATE_ADDR_MSB;
break;
@@ -453,11 +439,10 @@ void upd775x_device::advance_state()
/* Address MSB state: latch the MSB of the sample address and issue a request for the fourth byte */
/* The expected response will be the LSB of the sample address */
case STATE_ADDR_MSB:
- m_offset = (m_md ? read_byte(m_req_sample * 2 + 5) : m_fifo_in) << (8 + m_sample_offset_shift);
- if (DEBUG_STATES) logerror("offset_hi = %02X, requesting offset_lo\n", m_offset >> (8 + m_sample_offset_shift));
+ m_offset = ((m_mode == MODE_STAND_ALONE) ? read_byte(m_req_sample * 2 + 5) : m_fifo_in) << (8 + m_sample_offset_shift);
+ LOG_STATE("offset_hi = %02X, requesting offset_lo\n", m_offset >> (8 + m_sample_offset_shift));
m_drq = 1;
- /* 44 cycles later, we will latch this value and request another byte */
m_clocks_left = 44;
m_state = STATE_ADDR_LSB;
break;
@@ -465,11 +450,10 @@ void upd775x_device::advance_state()
/* Address LSB state: latch the LSB of the sample address and issue a request for the fifth byte */
/* The expected response will be just a dummy */
case STATE_ADDR_LSB:
- m_offset |= (m_md ? read_byte(m_req_sample * 2 + 6) : m_fifo_in) << m_sample_offset_shift;
- if (DEBUG_STATES) logerror("offset_lo = %02X, requesting dummy 2\n", (m_offset >> m_sample_offset_shift) & 0xff);
+ m_offset |= ((m_mode == MODE_STAND_ALONE) ? read_byte(m_req_sample * 2 + 6) : m_fifo_in) << m_sample_offset_shift;
+ LOG_STATE("offset_lo = %02X, requesting dummy 2\n", (m_offset >> m_sample_offset_shift) & 0xff);
m_drq = 1;
- /* 36 cycles later, we will latch this value and request another byte */
m_clocks_left = 36;
m_state = STATE_DUMMY2;
break;
@@ -479,59 +463,55 @@ void upd775x_device::advance_state()
case STATE_DUMMY2:
m_offset++;
m_first_valid_header = 0;
- if (DEBUG_STATES) logerror("dummy2, requesting block header\n");
+ LOG_STATE("dummy2, requesting block header\n");
m_drq = 1;
- /* 36?? cycles later, we will latch this value and request another byte */
m_clocks_left = 36;
m_state = STATE_BLOCK_HEADER;
break;
- /* Block header state: latch the header and issue a request for the first byte afterwards */
+ // Block header state: latch the header and issue a request for the first byte afterwards
case STATE_BLOCK_HEADER:
- /* if we're in a repeat loop, reset the offset to the repeat point and decrement the count */
if (m_repeat_count)
{
m_repeat_count--;
m_offset = m_repeat_offset;
}
- m_block_header = m_md ? read_byte(m_offset++) : m_fifo_in;
- if (DEBUG_STATES) logerror("header (@%05X) = %02X, requesting next byte\n", m_offset, m_block_header);
+ m_block_header = (m_mode == MODE_STAND_ALONE) ? read_byte(m_offset++) : m_fifo_in;
+ LOG_STATE("header (@%05X) = %02X, requesting next byte\n", m_offset, m_block_header);
m_drq = 1;
- /* our next step depends on the top two bits */
switch (m_block_header & 0xc0)
{
- case 0x00: /* silence */
+ case 0x00: // silence
m_clocks_left = 1024 * ((m_block_header & 0x3f) + 1);
m_state = (m_block_header == 0 && m_first_valid_header) ? STATE_IDLE : STATE_BLOCK_HEADER;
m_sample = 0;
m_adpcm_state = 0;
break;
- case 0x40: /* 256 nibbles */
+ case 0x40: // 256 nibbles
m_sample_rate = (m_block_header & 0x3f) + 1;
m_nibbles_left = 256;
- m_clocks_left = 36; /* just a guess */
+ m_clocks_left = 36; // just a guess
m_state = STATE_NIBBLE_MSN;
break;
- case 0x80: /* n nibbles */
+ case 0x80: // n nibbles
m_sample_rate = (m_block_header & 0x3f) + 1;
- m_clocks_left = 36; /* just a guess */
+ m_clocks_left = 36; // just a guess
m_state = STATE_NIBBLE_COUNT;
break;
- case 0xc0: /* repeat loop */
+ case 0xc0: // repeat loop
m_repeat_count = (m_block_header & 7) + 1;
m_repeat_offset = m_offset;
- m_clocks_left = 36; /* just a guess */
+ m_clocks_left = 36; // just a guess
m_state = STATE_BLOCK_HEADER;
break;
}
- /* set a flag when we get the first non-zero header */
if (m_block_header != 0)
m_first_valid_header = 1;
break;
@@ -539,23 +519,21 @@ void upd775x_device::advance_state()
/* Nibble count state: latch the number of nibbles to play and request another byte */
/* The expected response will be the first data byte */
case STATE_NIBBLE_COUNT:
- m_nibbles_left = (m_md ? read_byte(m_offset++) : m_fifo_in) + 1;
- if (DEBUG_STATES) logerror("nibble_count = %u, requesting next byte\n", (unsigned)m_nibbles_left);
+ m_nibbles_left = ((m_mode == MODE_STAND_ALONE) ? read_byte(m_offset++) : m_fifo_in) + 1;
+ LOG_STATE("nibble_count = %u, requesting next byte\n", (unsigned)m_nibbles_left);
m_drq = 1;
- /* 36?? cycles later, we will latch this value and request another byte */
- m_clocks_left = 36; /* just a guess */
+ m_clocks_left = 36; // just a guess
m_state = STATE_NIBBLE_MSN;
break;
/* MSN state: latch the data for this pair of samples and request another byte */
/* The expected response will be the next sample data or another header */
case STATE_NIBBLE_MSN:
- m_adpcm_data = m_md ? read_byte(m_offset++) : m_fifo_in;
+ m_adpcm_data = (m_mode == MODE_STAND_ALONE) ? read_byte(m_offset++) : m_fifo_in;
update_adpcm(m_adpcm_data >> 4);
m_drq = 1;
- /* we stay in this state until the time for this sample is complete */
m_clocks_left = m_sample_rate * 4;
if (--m_nibbles_left == 0)
m_state = STATE_BLOCK_HEADER;
@@ -563,11 +541,10 @@ void upd775x_device::advance_state()
m_state = STATE_NIBBLE_LSN;
break;
- /* LSN state: process the lower nibble */
+ // LSN state: process the lower nibble
case STATE_NIBBLE_LSN:
update_adpcm(m_adpcm_data & 15);
- /* we stay in this state until the time for this sample is complete */
m_clocks_left = m_sample_rate * 4;
if (--m_nibbles_left == 0)
m_state = STATE_BLOCK_HEADER;
@@ -576,7 +553,6 @@ void upd775x_device::advance_state()
break;
}
- /* if there's a DRQ, fudge the state */
if (m_drq)
{
m_post_drq_state = m_state;
@@ -586,41 +562,27 @@ void upd775x_device::advance_state()
}
}
-/************************************************************
-
- DRQ callback
-
-*************************************************************/
-
-void upd7759_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+TIMER_CALLBACK_MEMBER(upd7759_device::drq_update)
{
- uint8_t olddrq = m_drq;
+ m_channel->update();
- switch (id)
- {
- case TIMER_SLAVE_UPDATE:
+ uint8_t olddrq = m_drq;
+ int old_state = m_state;
- /* update the stream */
- m_channel->update();
+ advance_state();
- /* advance the state */
- advance_state();
+ LOG_STATE("upd7759_slave_update: DRQ %d->%d\n", olddrq, m_drq);
+ if (olddrq != m_drq)
+ {
+ LOG_DRQ("DRQ changed %d->%d\n", olddrq, m_drq);
+ m_drqcallback(m_drq);
+ }
- /* if the DRQ changed, update it */
- if (DEBUG_STATES)
- logerror("upd7759_slave_update: DRQ %d->%d\n", olddrq, m_drq);
- if (olddrq != m_drq)
- m_drqcallback(m_drq);
+ if (m_state != STATE_IDLE || old_state != STATE_IDLE)
+ m_timer->adjust(m_clock_period * m_clocks_left);
+}
- /* set a timer to go off when that is done */
- if (m_state != STATE_IDLE)
- m_timer->adjust(m_clock_period * m_clocks_left);
- break;
- default:
- throw emu_fatalerror("Unknown id in upd7759_device::device_timer");
- }
-}
/************************************************************
@@ -628,78 +590,92 @@ void upd7759_device::device_timer(emu_timer &timer, device_timer_id id, int para
*************************************************************/
-WRITE_LINE_MEMBER( upd775x_device::reset_w )
+void upd775x_device::reset_w(int state)
{
- /* update the reset value */
- uint8_t oldreset = m_reset;
- m_reset = (state != 0);
+ machine().scheduler().synchronize(timer_expired_delegate(FUNC(upd775x_device::internal_reset_w), this), state);
+}
- /* update the stream first */
+TIMER_CALLBACK_MEMBER(upd775x_device::internal_reset_w)
+{
m_channel->update();
- /* on the falling edge, reset everything */
+ uint8_t oldreset = m_reset;
+ m_reset = (param != 0);
+
if (oldreset && !m_reset)
device_reset();
}
-WRITE_LINE_MEMBER(upd7759_device::md_w)
+
+void upd775x_device::start_w(int state)
{
- m_md = state;
+ machine().scheduler().synchronize(timer_expired_delegate(FUNC(upd775x_device::internal_start_w), this), state);
}
-WRITE_LINE_MEMBER( upd7759_device::start_w )
+void upd775x_device::internal_start_w(int state)
{
- /* update the start value */
+ m_channel->update();
+
uint8_t oldstart = m_start;
m_start = (state != 0);
- if (DEBUG_STATES)
- logerror("upd7759_start_w: %d->%d\n", oldstart, m_start);
+ LOG_STATE("upd7759_start_w: %d->%d\n", oldstart, m_start);
- /* update the stream first */
- m_channel->update();
-
- /* on the rising edge, if we're idle, start going, but not if we're held in reset */
- if (m_state == STATE_IDLE && !oldstart && m_start && m_reset)
+ if (m_state == STATE_IDLE && m_mode == MODE_STAND_ALONE && oldstart && !m_start && m_reset)
{
m_state = STATE_START;
-
- /* for slave mode, start the timer going */
- if (!m_md)
- m_timer->adjust(attotime::zero);
}
}
-WRITE_LINE_MEMBER( upd7756_device::start_w )
-{
- /* update the start value */
- uint8_t oldstart = m_start;
- m_start = (state != 0);
- if (DEBUG_STATES)
- logerror("upd7759_start_w: %d->%d\n", oldstart, m_start);
+void upd775x_device::port_w(u8 data)
+{
+ machine().scheduler().synchronize(timer_expired_delegate(FUNC(upd775x_device::internal_port_w), this), data);
+}
- /* update the stream first */
+TIMER_CALLBACK_MEMBER(upd775x_device::internal_port_w)
+{
m_channel->update();
- /* on the rising edge, if we're idle, start going, but not if we're held in reset */
- if (m_state == STATE_IDLE && !oldstart && m_start && m_reset)
- {
- m_state = STATE_START;
- }
+ m_fifo_in = param;
}
-void upd775x_device::port_w(u8 data)
+void upd7759_device::md_w(int state)
+{
+ machine().scheduler().synchronize(timer_expired_delegate(FUNC(upd7759_device::internal_md_w), this), state);
+}
+
+TIMER_CALLBACK_MEMBER(upd7759_device::internal_md_w)
{
- /* update the FIFO value */
- m_fifo_in = data;
+ m_channel->update();
+
+ uint8_t old_md = m_md;
+ m_md = (param != 0);
+
+ LOG_STATE("upd7759_md_w: %d->%d\n", old_md, m_md);
+
+ if (m_state == STATE_IDLE && m_reset)
+ {
+ if (old_md && !m_md)
+ {
+ m_mode = MODE_SLAVE;
+ m_state = STATE_START;
+ m_timer->adjust(attotime::zero);
+ }
+ else if (!old_md && m_md)
+ {
+ m_mode = MODE_STAND_ALONE;
+ }
+ }
}
-READ_LINE_MEMBER( upd775x_device::busy_r )
+int upd775x_device::busy_r()
{
- /* return /BUSY */
+ m_channel->update();
+
+ // return /BUSY
return (m_state == STATE_IDLE);
}
@@ -708,56 +684,47 @@ READ_LINE_MEMBER( upd775x_device::busy_r )
// sound_stream_update - handle a stream update
//-------------------------------------------------
-void upd775x_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+void upd775x_device::sound_stream_update(sound_stream &stream)
{
+ constexpr sound_stream::sample_t sample_scale = 128.0 / 32768.0;
+ sound_stream::sample_t sample = sound_stream::sample_t(m_sample) * sample_scale;
int32_t clocks_left = m_clocks_left;
- int16_t sample = m_sample;
uint32_t step = m_step;
uint32_t pos = m_pos;
- stream_sample_t *buffer = outputs[0];
- /* loop until done */
+ u32 index = 0;
if (m_state != STATE_IDLE)
- while (samples != 0)
+ for ( ; index < stream.samples(); index++)
{
- /* store the current sample */
- *buffer++ = sample << 7;
- samples--;
+ stream.put(0, index, sample);
- /* advance by the number of clocks/output sample */
pos += step;
- /* handle clocks, but only in standalone mode */
- while (m_md && pos >= FRAC_ONE)
+ while ((m_mode == MODE_STAND_ALONE) && pos >= FRAC_ONE)
{
int clocks_this_time = pos >> FRAC_BITS;
if (clocks_this_time > clocks_left)
clocks_this_time = clocks_left;
- /* clock once */
pos -= clocks_this_time * FRAC_ONE;
clocks_left -= clocks_this_time;
- /* if we're out of clocks, time to handle the next state */
if (clocks_left == 0)
{
- /* advance one state; if we hit idle, bail */
advance_state();
if (m_state == STATE_IDLE)
break;
- /* reimport the variables that we cached */
clocks_left = m_clocks_left;
- sample = m_sample;
+ sample = sound_stream::sample_t(m_sample) * sample_scale;
}
}
}
- /* if we got out early, just zap the rest of the buffer */
- if (samples != 0)
- memset(buffer, 0, samples * sizeof(*buffer));
+ // if we got out early, just zap the rest of the buffer
+ for (; index < stream.samples(); index++)
+ stream.put(0, index, 0);
- /* flush the state back */
m_clocks_left = clocks_left;
m_pos = pos;
}