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.cpp272
1 files changed, 109 insertions, 163 deletions
diff --git a/src/devices/sound/upd7759.cpp b/src/devices/sound/upd7759.cpp
index fc07c0f95ab..1579fd8ee39 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)
@@ -168,6 +173,7 @@ upd775x_device::upd775x_device(const machine_config &mconfig, device_type type,
, 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)
@@ -209,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 = 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));
@@ -243,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()
@@ -252,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();
}
@@ -261,26 +266,12 @@ void upd7759_device::device_start()
{
upd775x_device::device_start();
- // chip configuration
m_sample_offset_shift = 1;
- m_timer = timer_alloc(TID_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
//-------------------------------------------------
@@ -288,7 +279,6 @@ void upd7756_device::device_start()
void upd775x_device::device_reset()
{
m_pos = 0;
- //m_fifo_in = 0; // this seems keeping state when /RESET line asserted (test case: konmedal.cpp games)
m_state = STATE_IDLE;
m_clocks_left = 0;
m_nibbles_left = 0;
@@ -305,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)
@@ -365,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)
@@ -388,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;
@@ -401,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)
@@ -413,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 + m_start_delay; /* 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;
@@ -431,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;
@@ -454,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;
@@ -466,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;
@@ -480,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;
@@ -540,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;
@@ -564,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;
@@ -577,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;
@@ -587,52 +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;
-
- switch (id)
- {
- case TID_PORT_WRITE:
- m_fifo_in = param;
- break;
-
- case TID_RESET_WRITE:
- internal_reset_w(param);
- break;
+ m_channel->update();
- case TID_START_WRITE:
- internal_start_w(param);
- break;
+ uint8_t olddrq = m_drq;
+ int old_state = m_state;
- case TID_SLAVE_UPDATE:
- /* 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");
- }
-}
/************************************************************
@@ -640,87 +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)
{
- synchronize(TID_RESET_WRITE, state);
+ machine().scheduler().synchronize(timer_expired_delegate(FUNC(upd775x_device::internal_reset_w), this), state);
}
-void upd775x_device::internal_reset_w(int state)
+TIMER_CALLBACK_MEMBER(upd775x_device::internal_reset_w)
{
- /* update the reset value */
- uint8_t oldreset = m_reset;
- m_reset = (state != 0);
-
- /* update the stream first */
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( upd775x_device::start_w )
+
+void upd775x_device::start_w(int state)
{
- synchronize(TID_START_WRITE, state);
+ machine().scheduler().synchronize(timer_expired_delegate(FUNC(upd775x_device::internal_start_w), this), state);
}
-void upd7759_device::internal_start_w(int state)
+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);
-
- /* update the stream first */
- m_channel->update();
+ LOG_STATE("upd7759_start_w: %d->%d\n", oldstart, m_start);
- /* 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);
}
}
-void upd7756_device::internal_start_w(int state)
-{
-
- /* 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 */
- synchronize(TID_PORT_WRITE, 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()
{
- /* update the stream first */
m_channel->update();
- /* return /BUSY */
+ // return /BUSY
return (m_state == STATE_IDLE);
}
@@ -737,48 +692,39 @@ void upd775x_device::sound_stream_update(sound_stream &stream, std::vector<read_
uint32_t step = m_step;
uint32_t pos = m_pos;
- /* loop until done */
u32 index = 0;
if (m_state != STATE_IDLE)
for ( ; index < outputs[0].samples(); index++)
{
- /* store the current sample */
outputs[0].put(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 = stream_buffer::sample_t(m_sample) * sample_scale;
}
}
}
- /* if we got out early, just zap the rest of the buffer */
+ // if we got out early, just zap the rest of the buffer
for (; index < outputs[0].samples(); index++)
outputs[0].put(index, 0);
- /* flush the state back */
m_clocks_left = clocks_left;
m_pos = pos;
}