summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author hap <happppp@users.noreply.github.com>2025-11-30 20:40:01 +0100
committer hap <happppp@users.noreply.github.com>2025-11-30 20:40:14 +0100
commit66deabe1768df8e0ee3767688a8bfec32df27f72 (patch)
tree4a3b44f98e32aba2f69a26364ed13f3f7c0f841b
parent3c94fd6829d59e5f1cc17d854e277f3ef6cbf9b1 (diff)
ymfm: adpcm updates from https://github.com/aaronsgiles/ymfm/pull/40 [Aaron Giles]
-rw-r--r--3rdparty/ymfm/src/ymfm_adpcm.cpp444
-rw-r--r--3rdparty/ymfm/src/ymfm_adpcm.h89
-rw-r--r--3rdparty/ymfm/src/ymfm_opl.cpp2
-rw-r--r--3rdparty/ymfm/src/ymfm_opn.cpp5
4 files changed, 351 insertions, 189 deletions
diff --git a/3rdparty/ymfm/src/ymfm_adpcm.cpp b/3rdparty/ymfm/src/ymfm_adpcm.cpp
index 4bc22beb2b0..0917e7c361a 100644
--- a/3rdparty/ymfm/src/ymfm_adpcm.cpp
+++ b/3rdparty/ymfm/src/ymfm_adpcm.cpp
@@ -390,13 +390,13 @@ void adpcm_b_registers::save_restore(ymfm_saved_state &state)
adpcm_b_channel::adpcm_b_channel(adpcm_b_engine &owner, uint32_t addrshift) :
m_address_shift(addrshift),
m_status(STATUS_BRDY),
- m_curnibble(0),
- m_curbyte(0),
- m_dummy_read(0),
+ m_buffer(0),
+ m_nibbles(0),
m_position(0),
m_curaddress(0),
m_accumulator(0),
- m_prev_accum(0),
+ m_output(0),
+ m_prev_output(0),
m_adpcm_step(STEP_MIN),
m_regs(owner.regs()),
m_owner(owner)
@@ -411,13 +411,13 @@ adpcm_b_channel::adpcm_b_channel(adpcm_b_engine &owner, uint32_t addrshift) :
void adpcm_b_channel::reset()
{
m_status = STATUS_BRDY;
- m_curnibble = 0;
- m_curbyte = 0;
- m_dummy_read = 0;
+ m_buffer = 0;
+ m_nibbles = 0;
m_position = 0;
m_curaddress = 0;
m_accumulator = 0;
- m_prev_accum = 0;
+ m_output = 0;
+ m_prev_output = 0;
m_adpcm_step = STEP_MIN;
}
@@ -429,13 +429,13 @@ void adpcm_b_channel::reset()
void adpcm_b_channel::save_restore(ymfm_saved_state &state)
{
state.save_restore(m_status);
- state.save_restore(m_curnibble);
- state.save_restore(m_curbyte);
- state.save_restore(m_dummy_read);
+ state.save_restore(m_buffer);
+ state.save_restore(m_nibbles);
state.save_restore(m_position);
state.save_restore(m_curaddress);
state.save_restore(m_accumulator);
- state.save_restore(m_prev_accum);
+ state.save_restore(m_output);
+ state.save_restore(m_prev_output);
state.save_restore(m_adpcm_step);
}
@@ -447,9 +447,11 @@ void adpcm_b_channel::save_restore(ymfm_saved_state &state)
void adpcm_b_channel::clock()
{
// only process if active and not recording (which we don't support)
- if (!m_regs.execute() || m_regs.record() || (m_status & STATUS_PLAYING) == 0)
+ if (!m_regs.execute() || m_regs.record() || (m_status & STATUS_INTERNAL_PLAYING) == 0)
{
- m_status &= ~STATUS_PLAYING;
+ m_prev_output = m_output;
+ m_position = 0;
+ set_reset_status(0, STATUS_INTERNAL_PLAYING);
return;
}
@@ -459,76 +461,63 @@ void adpcm_b_channel::clock()
if (position < 0x10000)
return;
- // if we're about to process nibble 0, fetch sample
- if (m_curnibble == 0)
+ // if we have nibbles available, process them
+ if (m_nibbles != 0)
{
- // playing from RAM/ROM
- if (m_regs.external())
- m_curbyte = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress);
- }
+ // fetch the next nibble
+ uint8_t data = consume_nibbles(1);
- // extract the nibble from our current byte
- uint8_t data = uint8_t(m_curbyte << (4 * m_curnibble)) >> 4;
- m_curnibble ^= 1;
+ // forecast to next forecast: 1/8, 3/8, 5/8, 7/8, 9/8, 11/8, 13/8, 15/8
+ int32_t delta = (2 * bitfield(data, 0, 3) + 1) * m_adpcm_step / 8;
+ if (bitfield(data, 3))
+ delta = -delta;
- // we just processed the last nibble
- if (m_curnibble == 0)
- {
- // if playing from RAM/ROM, check the end/limit address or advance
- if (m_regs.external())
- {
- // handle the sample end, either repeating or stopping
- if (at_end())
- {
- // if repeating, go back to the start
- if (m_regs.repeat())
- load_start();
-
- // otherwise, done; set the EOS bit
- else
- {
- m_accumulator = 0;
- m_prev_accum = 0;
- m_status = (m_status & ~STATUS_PLAYING) | STATUS_EOS;
- debug::log_keyon("%s\n", "ADPCM EOS");
- return;
- }
- }
+ // add and clamp to 16 bits
+ m_accumulator = clamp(m_accumulator + delta, -32768, 32767);
- // wrap at the limit address
- else if (at_limit())
- m_curaddress = 0;
+ // scale the ADPCM step: 0.9, 0.9, 0.9, 0.9, 1.2, 1.6, 2.0, 2.4
+ static uint8_t const s_step_scale[8] = { 57, 57, 57, 57, 77, 102, 128, 153 };
+ m_adpcm_step = clamp((m_adpcm_step * s_step_scale[bitfield(data, 0, 3)]) / 64, STEP_MIN, STEP_MAX);
- // otherwise, advance the current address
- else
- {
- m_curaddress++;
- m_curaddress &= 0xffffff;
- }
- }
+ // make the new output equal to the accumulator
+ m_prev_output = m_output;
+ m_output = m_accumulator;
- // if CPU-driven, copy the next byte and request more
- else
+ // if we've drained all the nibbles, that means we're at a repeat point or end of sample
+ if (m_nibbles == 0)
{
- m_curbyte = m_regs.cpudata();
- m_status |= STATUS_BRDY;
+ // reset the ADPCM state (but leave output alone)
+ m_accumulator = 0;
+ m_adpcm_step = STEP_MIN;
+
+ // always set EOS bit, even if repeating
+ set_reset_status(STATUS_EOS);
+ debug::log_keyon("%s\n", "ADPCM EOS");
+
+ // clear playing flag if we're not repeating
+ if (!m_regs.repeat())
+ set_reset_status(0, STATUS_INTERNAL_PLAYING);
}
}
- // remember previous value for interpolation
- m_prev_accum = m_accumulator;
-
- // forecast to next forecast: 1/8, 3/8, 5/8, 7/8, 9/8, 11/8, 13/8, 15/8
- int32_t delta = (2 * bitfield(data, 0, 3) + 1) * m_adpcm_step / 8;
- if (bitfield(data, 3))
- delta = -delta;
+ // if we don't have at least 3 nibbles in the buffer, request more data
+ if ((m_status & STATUS_INTERNAL_PLAYING) != 0 && m_nibbles < 3)
+ {
+ // if we hit the end address after this fetch, handle looping/ending
+ if (request_data())
+ {
+ // the final 3 samples are not played; chop them from the stream
+ consume_nibbles(3);
- // add and clamp to 16 bits
- m_accumulator = clamp(m_accumulator + delta, -32768, 32767);
+ // this should always end up as 1; the logic above assumes we will hit
+ // 0 nibbles after processing the next one
+ assert(m_nibbles == 1);
- // scale the ADPCM step: 0.9, 0.9, 0.9, 0.9, 1.2, 1.6, 2.0, 2.4
- static uint8_t const s_step_scale[8] = { 57, 57, 57, 57, 77, 102, 128, 153 };
- m_adpcm_step = clamp((m_adpcm_step * s_step_scale[bitfield(data, 0, 3)]) / 64, STEP_MIN, STEP_MAX);
+ // if repeating, set the current address back to start for next fetch
+ if (m_regs.repeat())
+ latch_addresses();
+ }
+ }
}
@@ -545,7 +534,7 @@ void adpcm_b_channel::output(ymfm_output<NumOutputs> &output, uint32_t rshift) c
return;
// do a linear interpolation between samples
- int32_t result = (m_prev_accum * int32_t((m_position ^ 0xffff) + 1) + m_accumulator * int32_t(m_position)) >> 16;
+ int32_t result = (m_prev_output * int32_t((m_position ^ 0xffff) + 1) + m_output * int32_t(m_position)) >> 16;
// apply volume (level) in a linear fashion and reduce
result = (result * int32_t(m_regs.level())) >> (8 + rshift);
@@ -568,37 +557,8 @@ uint8_t adpcm_b_channel::read(uint32_t regnum)
// register 8 reads over the bus under some conditions
if (regnum == 0x08 && !m_regs.execute() && !m_regs.record() && m_regs.external())
- {
- // two dummy reads are consumed first
- if (m_dummy_read != 0)
- {
- load_start();
- m_dummy_read--;
- }
-
- // read the data
- else
- {
- // read from outside of the chip
- result = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress++);
+ result = read_ram();
- // did we hit the end? if so, signal EOS
- if (at_end())
- {
- m_status = STATUS_EOS | STATUS_BRDY;
- debug::log_keyon("%s\n", "ADPCM EOS");
- }
- else
- {
- // signal ready
- m_status = STATUS_BRDY;
- }
-
- // wrap at the limit address
- if (at_limit())
- m_curaddress = 0;
- }
- }
return result;
}
@@ -613,68 +573,76 @@ void adpcm_b_channel::write(uint32_t regnum, uint8_t value)
// dummy read counter
if (regnum == 0x00)
{
- if (m_regs.execute())
+ // reset flag stops playback and holds output, but does not clear the
+ // externally-visible playing flag
+ if (m_regs.resetflag())
+ set_reset_status(STATUS_BRDY | (((m_status & STATUS_INTERNAL_PLAYING) != 0) ? STATUS_EOS : 0), STATUS_INTERNAL_PLAYING);
+
+ // all other modes set up for an operation
+ else
{
- load_start();
-
- // don't log masked channels
- if ((debug::GLOBAL_ADPCM_B_CHANNEL_MASK & 1) != 0)
- debug::log_keyon("KeyOn ADPCM-B: rep=%d spk=%d pan=%d%d dac=%d 8b=%d rom=%d ext=%d rec=%d start=%04X end=%04X pre=%04X dn=%04X lvl=%02X lim=%04X\n",
- m_regs.repeat(),
- m_regs.speaker(),
- m_regs.pan_left(),
- m_regs.pan_right(),
- m_regs.dac_enable(),
- m_regs.dram_8bit(),
- m_regs.rom_ram(),
- m_regs.external(),
- m_regs.record(),
- m_regs.start(),
- m_regs.end(),
- m_regs.prescale(),
- m_regs.delta_n(),
- m_regs.level(),
- m_regs.limit());
+ // initialize the core state; appears to leave EOS flag alone until next execute
+ set_reset_status(STATUS_BRDY, STATUS_PLAYING | STATUS_INTERNAL_DRAIN | STATUS_INTERNAL_PLAYING | STATUS_INTERNAL_SUPPRESS_WRITE);
+
+ // flag the address to be latched at the next access; this is necessary
+ // because it is allowed to program the start/stop addresses after this
+ // command byte is written
+ m_curaddress = LATCH_ADDRESS;
+
+ // if playing, set the playing status
+ if (m_regs.execute())
+ {
+ m_buffer = 0;
+ m_nibbles = 0;
+ m_position = 0;
+ m_accumulator = 0;
+ m_adpcm_step = STEP_MIN;
+ m_output = 0;
+
+ set_reset_status(STATUS_PLAYING | STATUS_INTERNAL_PLAYING, STATUS_EOS);
+
+ // don't log masked channels
+ if ((debug::GLOBAL_ADPCM_B_CHANNEL_MASK & 1) != 0)
+ debug::log_keyon("KeyOn ADPCM-B: rep=%d spk=%d pan=%d%d dac=%d 8b=%d rom=%d ext=%d rec=%d start=%04X end=%04X pre=%04X dn=%04X lvl=%02X lim=%04X\n",
+ m_regs.repeat(),
+ m_regs.speaker(),
+ m_regs.pan_left(),
+ m_regs.pan_right(),
+ m_regs.dac_enable(),
+ m_regs.dram_8bit(),
+ m_regs.rom_ram(),
+ m_regs.external(),
+ m_regs.record(),
+ m_regs.start(),
+ m_regs.end(),
+ m_regs.prescale(),
+ m_regs.delta_n(),
+ m_regs.level(),
+ m_regs.limit());
+ }
}
- else
- m_status &= ~STATUS_EOS;
- if (m_regs.resetflag())
- reset();
- if (m_regs.external())
- m_dummy_read = 2;
}
// register 8 writes over the bus under some conditions
else if (regnum == 0x08)
{
- // if writing from the CPU during execute, clear the ready flag
- if (m_regs.execute() && !m_regs.record() && !m_regs.external())
- m_status &= ~STATUS_BRDY;
-
- // if writing during "record", pass through as data
- else if (!m_regs.execute() && m_regs.record() && m_regs.external())
+ // writing during execute
+ if (m_regs.execute())
{
- // clear out dummy reads and set start address
- if (m_dummy_read != 0)
- {
- load_start();
- m_dummy_read = 0;
- }
+ // if writing from the CPU during execute, clear the ready flag; data will be picked
+ // up on next fetch
+ if (!m_regs.record() && !m_regs.external())
+ set_reset_status(0, STATUS_BRDY);
+ }
- // did we hit the end? if so, signal EOS
- if (at_end())
- {
- debug::log_keyon("%s\n", "ADPCM EOS");
- m_status = STATUS_EOS | STATUS_BRDY;
- }
+ // if writing to external data, process record mode, which writes data to RAM
+ else if (m_regs.external() && m_regs.record())
+ write_ram(value);
- // otherwise, write the data and signal ready
- else
- {
- m_owner.intf().ymfm_external_write(ACCESS_ADPCM_B, m_curaddress++, value);
- m_status = STATUS_BRDY;
- }
- }
+ // writes in external non-record mode appear to behave like a read in that it will advance
+ // the address and consume a nibble, but the last written value will still be present
+ else
+ read_ram();
}
}
@@ -702,20 +670,164 @@ uint32_t adpcm_b_channel::address_shift() const
//-------------------------------------------------
-// load_start - load the start address and
-// initialize the state
+// advance_address - advance the address, checking
+// for end/limit values at programmed boundaries;
+// returns true if the end is hit
//-------------------------------------------------
-void adpcm_b_channel::load_start()
+bool adpcm_b_channel::advance_address()
{
- m_status = (m_status & ~STATUS_EOS) | STATUS_PLAYING;
- m_curaddress = m_regs.external() ? (m_regs.start() << address_shift()) : 0;
- m_curnibble = 0;
- m_curbyte = 0;
- m_position = 0;
- m_accumulator = 0;
- m_prev_accum = 0;
- m_adpcm_step = STEP_MIN;
+ // if we're fetching the last byte of a unit, check ending conditions
+ auto shift = address_shift();
+ auto mask = (1 << shift) - 1;
+
+ // should never get here with an uninitialized current address
+ assert(m_curaddress != LATCH_ADDRESS);
+
+ // if at the end of a unit, check for end/limit
+ if ((m_curaddress & mask) == mask)
+ {
+ // shift off the low address bits and check against the end
+ uint32_t unitaddr = m_curaddress >> shift;
+ if (unitaddr == m_regs.end())
+ return true;
+
+ // wrap at the limit address; this does not report any status
+ else if (unitaddr == m_regs.limit())
+ {
+ m_curaddress = 0;
+ return false;
+ }
+ }
+
+ // advance the address
+ m_curaddress = (m_curaddress + 1) & 0xffffff;
+ return false;
+}
+
+
+//-------------------------------------------------
+// request_data - request another byte of data
+// for the buffer; used by both playback and
+// data reading code
+//-------------------------------------------------
+
+bool adpcm_b_channel::request_data()
+{
+ // pick up the current address if this is the first read
+ if (m_curaddress == LATCH_ADDRESS)
+ latch_addresses();
+
+ // if CPU-driven, just set the flag and return true
+ if (!m_regs.external())
+ {
+ // if data was written, consume it
+ if ((m_status & STATUS_BRDY) == 0)
+ append_buffer_byte(m_regs.cpudata());
+ set_reset_status(STATUS_BRDY);
+ return false;
+ }
+
+ // append the new byte to our buffer; also write to the cpudata register to match
+ // behavior of dummy reads from real chip
+ uint8_t data = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress);
+ append_buffer_byte(data);
+ m_regs.write(0x08, data);
+
+ // advance the address, returning true if we hit the end
+ return advance_address();
+}
+
+
+//-------------------------------------------------
+// read_ram - perform a read cycle from RAM/ROM
+//-------------------------------------------------
+
+uint8_t adpcm_b_channel::read_ram()
+{
+ // if this is the first read, ensure there is at least 2 bytes of data available,
+ // padding with the cpudata register if needed
+ if (m_curaddress == LATCH_ADDRESS)
+ {
+ set_reset_status(0, STATUS_INTERNAL_DRAIN);
+ while (m_nibbles < 4)
+ append_buffer_byte(m_regs.cpudata());
+ }
+
+ // if we have the nibbles, return them
+ uint8_t result = consume_nibbles(2);
+ set_reset_status(STATUS_BRDY);
+
+ // if we previously hit the end and we're draining, see if we're out
+ if ((m_status & STATUS_INTERNAL_DRAIN) != 0)
+ {
+ // if we run out of nibbles, mark end of sample and reset the address
+ if (m_nibbles == 0)
+ {
+ set_reset_status(STATUS_EOS, STATUS_INTERNAL_DRAIN);
+
+ // if repeating, add one dummy sample and issue a fetch of the first byte
+ if (m_regs.repeat())
+ {
+ append_buffer_byte(m_regs.cpudata());
+ m_curaddress = m_regs.start() << address_shift();
+ request_data();
+ }
+
+ // otherwise, reset the address
+ else
+ m_curaddress = LATCH_ADDRESS;
+ }
+ }
+
+ // if not draining, then request more data and start draining if we hit the end
+ else if (request_data())
+ set_reset_status(STATUS_INTERNAL_DRAIN);
+
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_ram - perform a write cycle to RAM
+//-------------------------------------------------
+
+void adpcm_b_channel::write_ram(uint8_t value)
+{
+ // normal write case, unsuppressed
+ if ((m_status & STATUS_INTERNAL_SUPPRESS_WRITE) == 0)
+ {
+ // latch the current address if this is the first write
+ if (m_curaddress == LATCH_ADDRESS)
+ latch_addresses();
+
+ // write the data
+ m_owner.intf().ymfm_external_write(ACCESS_ADPCM_B, m_curaddress, value);
+ set_reset_status(STATUS_BRDY);
+
+ // advance; if we hit the end, signal EOS and put ourselves back in the latching state
+ if (advance_address())
+ {
+ set_reset_status(STATUS_EOS);
+ m_curaddress = LATCH_ADDRESS;
+
+ // in the repeat case, suppress further writes
+ if (m_regs.repeat())
+ set_reset_status(STATUS_INTERNAL_SUPPRESS_WRITE);
+ }
+ }
+
+ // suppressed writes after reaching stop address in repeat mode; note that this runs
+ // immediately after the EOS condition above, as well as on subsequent writes
+ if ((m_status & STATUS_INTERNAL_SUPPRESS_WRITE) != 0)
+ {
+ // reset the buffer to 4 nibbles with 0 and the value written, then trigger a read
+ // cycle which will consume the 0 and clock in the next byte, leaving the value
+ // written as the next byte to consume
+ m_buffer = value << 16;
+ m_nibbles = 4;
+ read_ram();
+ }
}
diff --git a/3rdparty/ymfm/src/ymfm_adpcm.h b/3rdparty/ymfm/src/ymfm_adpcm.h
index 98f57121e5c..ef27b36427b 100644
--- a/3rdparty/ymfm/src/ymfm_adpcm.h
+++ b/3rdparty/ymfm/src/ymfm_adpcm.h
@@ -214,6 +214,8 @@ private:
// ======================> adpcm_b_registers
//
+// See https://github.com/hyano/opna-analyze/blob/main/doc/OPNA.md for details on ADPCM timing
+//
// ADPCM-B register map:
//
// System-wide registers:
@@ -263,6 +265,7 @@ public:
void save_restore(ymfm_saved_state &state);
// direct read/write access
+ uint8_t read(uint32_t index) const { return m_regdata[index]; }
void write(uint32_t index, uint8_t data) { m_regdata[index] = data; }
// system-wide registers
@@ -301,11 +304,22 @@ class adpcm_b_channel
static constexpr int32_t STEP_MIN = 127;
static constexpr int32_t STEP_MAX = 24576;
+ static constexpr uint32_t LATCH_ADDRESS = 0xffffffff;
+
public:
- static constexpr uint8_t STATUS_EOS = 0x01;
- static constexpr uint8_t STATUS_BRDY = 0x02;
- static constexpr uint8_t STATUS_PLAYING = 0x04;
+ // publicly visible status bits
+ static constexpr uint32_t STATUS_EOS = 0x01;
+ static constexpr uint32_t STATUS_BRDY = 0x02;
+ static constexpr uint32_t STATUS_PLAYING = 0x04;
+
+private:
+ // internal status bits
+ static constexpr uint32_t STATUS_EXTERNAL = STATUS_EOS | STATUS_BRDY | STATUS_PLAYING;
+ static constexpr uint32_t STATUS_INTERNAL_DRAIN = 0x08;
+ static constexpr uint32_t STATUS_INTERNAL_PLAYING = 0x10;
+ static constexpr uint32_t STATUS_INTERNAL_SUPPRESS_WRITE = 0x20;
+public:
// constructor
adpcm_b_channel(adpcm_b_engine &owner, uint32_t addrshift);
@@ -326,7 +340,10 @@ public:
void output(ymfm_output<NumOutputs> &output, uint32_t rshift) const;
// return the status register
- uint8_t status() const { return m_status; }
+ uint8_t status() const { return m_status & STATUS_EXTERNAL; }
+
+ // clear bits in the status register
+ void clear_status(uint8_t status) { m_status &= ~(status & STATUS_EXTERNAL); }
// handle special register reads
uint8_t read(uint32_t regnum);
@@ -335,31 +352,56 @@ public:
void write(uint32_t regnum, uint8_t value);
private:
- // helper - return the current address shift
+ // update the status register
+ void set_reset_status(uint32_t set, uint32_t reset = 0) { m_status = (m_status & ~reset) | set; }
+
+ // return the current address shift
uint32_t address_shift() const;
- // load the start address
- void load_start();
+ // advance the address by one byte, return true if the end address was hit
+ bool advance_address();
+
+ // request the next byte of data
+ bool request_data();
- // limit checker; stops at the last byte of the chunk described by address_shift()
- bool at_limit() const { return (m_curaddress == (((m_regs.limit() + 1) << address_shift()) - 1)); }
+ // perform a read cycle from RAM/ROM
+ uint8_t read_ram();
- // end checker; stops at the last byte of the chunk described by address_shift()
- bool at_end() const { return (m_curaddress == (((m_regs.end() + 1) << address_shift()))); }
+ // perform a write cycle to RAM
+ void write_ram(uint8_t value);
+
+ // latch the current address
+ void latch_addresses() { m_curaddress = m_regs.external() ? (m_regs.start() << address_shift()) : 0; }
+
+ // append a byte to our internal buffer
+ void append_buffer_byte(uint8_t data)
+ {
+ m_buffer |= data << (24 - 4 * m_nibbles);
+ m_nibbles += 2;
+ }
+
+ // consume the requested number of nibbles from the buffer
+ uint32_t consume_nibbles(uint8_t count)
+ {
+ uint32_t result = m_buffer >> (32 - 4 * count);
+ m_buffer <<= 4 * count;
+ m_nibbles = (m_nibbles > count) ? (m_nibbles - count) : 0;
+ return result;
+ }
// internal state
- uint32_t const m_address_shift; // address bits shift-left
- uint32_t m_status; // currently playing?
- uint32_t m_curnibble; // index of the current nibble
- uint32_t m_curbyte; // current byte of data
- uint32_t m_dummy_read; // dummy read tracker
- uint32_t m_position; // current fractional position
- uint32_t m_curaddress; // current address
- int32_t m_accumulator; // accumulator
- int32_t m_prev_accum; // previous accumulator (for linear interp)
- int32_t m_adpcm_step; // next forecast
- adpcm_b_registers &m_regs; // reference to registers
- adpcm_b_engine &m_owner; // reference to our owner
+ uint32_t const m_address_shift; // address bits shift-left
+ uint32_t m_status; // currently playing?
+ uint32_t m_buffer; // buffer of bytes read, left-justified
+ uint32_t m_nibbles; // number of nibbles in m_bytebuffer
+ uint32_t m_position; // current fractional position
+ uint32_t m_curaddress; // current address
+ int32_t m_accumulator; // accumulator
+ int32_t m_output; // current output value
+ int32_t m_prev_output; // previous output value (for linear interp)
+ int32_t m_adpcm_step; // next forecast
+ adpcm_b_registers &m_regs; // reference to registers
+ adpcm_b_engine &m_owner; // reference to our owner
};
@@ -392,6 +434,7 @@ public:
// status
uint8_t status() const { return m_channel->status(); }
+ void clear_status(uint8_t status) { m_channel->clear_status(status); }
// return a reference to our interface
ymfm_interface &intf() { return m_intf; }
diff --git a/3rdparty/ymfm/src/ymfm_opl.cpp b/3rdparty/ymfm/src/ymfm_opl.cpp
index 8e8025fd9cc..46f93c708df 100644
--- a/3rdparty/ymfm/src/ymfm_opl.cpp
+++ b/3rdparty/ymfm/src/ymfm_opl.cpp
@@ -1012,6 +1012,8 @@ void y8950::write_data(uint8_t data)
{
case 0x04: // IRQ control
m_fm.write(m_address, data);
+ if ((data & STATUS_ADPCM_B_EOS) != 0)
+ m_adpcm_b.clear_status(adpcm_b_channel::STATUS_EOS);
read_status();
break;
diff --git a/3rdparty/ymfm/src/ymfm_opn.cpp b/3rdparty/ymfm/src/ymfm_opn.cpp
index 9a8f5e99feb..5f418636a87 100644
--- a/3rdparty/ymfm/src/ymfm_opn.cpp
+++ b/3rdparty/ymfm/src/ymfm_opn.cpp
@@ -1237,7 +1237,10 @@ void ym2608::write_data_hi(uint8_t data)
{
// 110: IRQ flag control
if (bitfield(data, 7))
+ {
m_fm.set_reset_status(0, 0xff);
+ m_adpcm_b.clear_status(adpcm_b_channel::STATUS_EOS | adpcm_b_channel::STATUS_PLAYING);
+ }
else
{
m_flag_control = data;
@@ -2024,6 +2027,8 @@ void ym2610::write_data(uint8_t data)
// 1C: EOS flag reset
m_flag_mask = ~data & EOS_FLAGS_MASK;
m_eos_status &= ~(data & EOS_FLAGS_MASK);
+ if (bitfield(data, 7))
+ m_adpcm_b.clear_status(adpcm_b_channel::STATUS_EOS);
}
else
{