diff options
Diffstat (limited to 'src/devices/sound/upd7759.cpp')
-rw-r--r-- | src/devices/sound/upd7759.cpp | 283 |
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; } |