summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/ymfm/src/ymfm_adpcm.h
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/ymfm/src/ymfm_adpcm.h')
-rw-r--r--3rdparty/ymfm/src/ymfm_adpcm.h411
1 files changed, 411 insertions, 0 deletions
diff --git a/3rdparty/ymfm/src/ymfm_adpcm.h b/3rdparty/ymfm/src/ymfm_adpcm.h
new file mode 100644
index 00000000000..4b4af0fddd6
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_adpcm.h
@@ -0,0 +1,411 @@
+// BSD 3-Clause License
+//
+// Copyright (c) 2021, Aaron Giles
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef YMFM_ADPCM_H
+#define YMFM_ADPCM_H
+
+#pragma once
+
+#include "ymfm.h"
+
+namespace ymfm
+{
+
+//*********************************************************
+// INTERFACE CLASSES
+//*********************************************************
+
+// forward declarations
+class adpcm_a_engine;
+class adpcm_b_engine;
+
+
+// ======================> adpcm_a_registers
+
+//
+// ADPCM-A register map:
+//
+// System-wide registers:
+// 00 x------- Dump (disable=1) or keyon (0) control
+// --xxxxxx Mask of channels to dump or keyon
+// 01 --xxxxxx Total level
+// 02 xxxxxxxx Test register
+// 08-0D x------- Pan left
+// -x------ Pan right
+// ---xxxxx Instrument level
+// 10-15 xxxxxxxx Start address (low)
+// 18-1D xxxxxxxx Start address (high)
+// 20-25 xxxxxxxx End address (low)
+// 28-2D xxxxxxxx End address (high)
+//
+class adpcm_a_registers
+{
+public:
+ // constants
+ static constexpr uint32_t OUTPUTS = 2;
+ static constexpr uint32_t CHANNELS = 6;
+ static constexpr uint32_t REGISTERS = 0x30;
+ static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
+
+ // constructor
+ adpcm_a_registers() { }
+
+ // reset to initial state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // map channel number to register offset
+ static constexpr uint32_t channel_offset(uint32_t chnum)
+ {
+ assert(chnum < CHANNELS);
+ return chnum;
+ }
+
+ // direct read/write access
+ void write(uint32_t index, uint8_t data) { m_regdata[index] = data; }
+
+ // system-wide registers
+ uint32_t dump() const { return bitfield(m_regdata[0x00], 7); }
+ uint32_t dump_mask() const { return bitfield(m_regdata[0x00], 0, 6); }
+ uint32_t total_level() const { return bitfield(m_regdata[0x01], 0, 6); }
+ uint32_t test() const { return m_regdata[0x02]; }
+
+ // per-channel registers
+ uint32_t ch_pan_left(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x08], 7); }
+ uint32_t ch_pan_right(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x08], 6); }
+ uint32_t ch_instrument_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x08], 0, 5); }
+ uint32_t ch_start(uint32_t choffs) const { return m_regdata[choffs + 0x10] | (m_regdata[choffs + 0x18] << 8); }
+ uint32_t ch_end(uint32_t choffs) const { return m_regdata[choffs + 0x20] | (m_regdata[choffs + 0x28] << 8); }
+
+ // per-channel writes
+ void write_start(uint32_t choffs, uint32_t address)
+ {
+ write(choffs + 0x10, address);
+ write(choffs + 0x18, address >> 8);
+ }
+ void write_end(uint32_t choffs, uint32_t address)
+ {
+ write(choffs + 0x20, address);
+ write(choffs + 0x28, address >> 8);
+ }
+
+private:
+ // internal state
+ uint8_t m_regdata[REGISTERS]; // register data
+};
+
+
+// ======================> adpcm_a_channel
+
+class adpcm_a_channel
+{
+public:
+ // constructor
+ adpcm_a_channel(adpcm_a_engine &owner, uint32_t choffs, uint32_t addrshift);
+
+ // reset the channel state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // signal key on/off
+ void keyonoff(bool on);
+
+ // master clockingfunction
+ bool clock();
+
+ // return the computed output value, with panning applied
+ template<int NumOutputs>
+ void output(ymfm_output<NumOutputs> &output) const;
+
+private:
+ // internal state
+ uint32_t const m_choffs; // channel offset
+ uint32_t const m_address_shift; // address bits shift-left
+ uint32_t m_playing; // currently playing?
+ uint32_t m_curnibble; // index of the current nibble
+ uint32_t m_curbyte; // current byte of data
+ uint32_t m_curaddress; // current address
+ int32_t m_accumulator; // accumulator
+ int32_t m_step_index; // index in the stepping table
+ adpcm_a_registers &m_regs; // reference to registers
+ adpcm_a_engine &m_owner; // reference to our owner
+};
+
+
+// ======================> adpcm_a_engine
+
+class adpcm_a_engine
+{
+public:
+ static constexpr int CHANNELS = adpcm_a_registers::CHANNELS;
+
+ // constructor
+ adpcm_a_engine(ymfm_interface &intf, uint32_t addrshift);
+
+ // reset our status
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // master clocking function
+ uint32_t clock(uint32_t chanmask);
+
+ // compute sum of channel outputs
+ template<int NumOutputs>
+ void output(ymfm_output<NumOutputs> &output, uint32_t chanmask);
+
+ // write to the ADPCM-A registers
+ void write(uint32_t regnum, uint8_t data);
+
+ // set the start/end address for a channel (for hardcoded YM2608 percussion)
+ void set_start_end(uint8_t chnum, uint16_t start, uint16_t end)
+ {
+ uint32_t choffs = adpcm_a_registers::channel_offset(chnum);
+ m_regs.write_start(choffs, start);
+ m_regs.write_end(choffs, end);
+ }
+
+ // return a reference to our interface
+ ymfm_interface &intf() { return m_intf; }
+
+ // return a reference to our registers
+ adpcm_a_registers &regs() { return m_regs; }
+
+private:
+ // internal state
+ ymfm_interface &m_intf; // reference to the interface
+ std::unique_ptr<adpcm_a_channel> m_channel[CHANNELS]; // array of channels
+ adpcm_a_registers m_regs; // registers
+};
+
+
+// ======================> adpcm_b_registers
+
+//
+// ADPCM-B register map:
+//
+// System-wide registers:
+// 00 x------- Start of synthesis/analysis
+// -x------ Record
+// --x----- External/manual driving
+// ---x---- Repeat playback
+// ----x--- Speaker off
+// -------x Reset
+// 01 x------- Pan left
+// -x------ Pan right
+// ----x--- Start conversion
+// -----x-- DAC enable
+// ------x- DRAM access (1=8-bit granularity; 0=1-bit)
+// -------x RAM/ROM (1=ROM, 0=RAM)
+// 02 xxxxxxxx Start address (low)
+// 03 xxxxxxxx Start address (high)
+// 04 xxxxxxxx End address (low)
+// 05 xxxxxxxx End address (high)
+// 06 xxxxxxxx Prescale value (low)
+// 07 -----xxx Prescale value (high)
+// 08 xxxxxxxx CPU data/buffer
+// 09 xxxxxxxx Delta-N frequency scale (low)
+// 0a xxxxxxxx Delta-N frequency scale (high)
+// 0b xxxxxxxx Level control
+// 0c xxxxxxxx Limit address (low)
+// 0d xxxxxxxx Limit address (high)
+// 0e xxxxxxxx DAC data [YM2608/10]
+// 0f xxxxxxxx PCM data [YM2608/10]
+// 0e xxxxxxxx DAC data high [Y8950]
+// 0f xx------ DAC data low [Y8950]
+// 10 -----xxx DAC data exponent [Y8950]
+//
+class adpcm_b_registers
+{
+public:
+ // constants
+ static constexpr uint32_t REGISTERS = 0x11;
+
+ // constructor
+ adpcm_b_registers() { }
+
+ // reset to initial state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // direct read/write access
+ void write(uint32_t index, uint8_t data) { m_regdata[index] = data; }
+
+ // system-wide registers
+ uint32_t execute() const { return bitfield(m_regdata[0x00], 7); }
+ uint32_t record() const { return bitfield(m_regdata[0x00], 6); }
+ uint32_t external() const { return bitfield(m_regdata[0x00], 5); }
+ uint32_t repeat() const { return bitfield(m_regdata[0x00], 4); }
+ uint32_t speaker() const { return bitfield(m_regdata[0x00], 3); }
+ uint32_t resetflag() const { return bitfield(m_regdata[0x00], 0); }
+ uint32_t pan_left() const { return bitfield(m_regdata[0x01], 7); }
+ uint32_t pan_right() const { return bitfield(m_regdata[0x01], 6); }
+ uint32_t start_conversion() const { return bitfield(m_regdata[0x01], 3); }
+ uint32_t dac_enable() const { return bitfield(m_regdata[0x01], 2); }
+ uint32_t dram_8bit() const { return bitfield(m_regdata[0x01], 1); }
+ uint32_t rom_ram() const { return bitfield(m_regdata[0x01], 0); }
+ uint32_t start() const { return m_regdata[0x02] | (m_regdata[0x03] << 8); }
+ uint32_t end() const { return m_regdata[0x04] | (m_regdata[0x05] << 8); }
+ uint32_t prescale() const { return m_regdata[0x06] | (bitfield(m_regdata[0x07], 0, 3) << 8); }
+ uint32_t cpudata() const { return m_regdata[0x08]; }
+ uint32_t delta_n() const { return m_regdata[0x09] | (m_regdata[0x0a] << 8); }
+ uint32_t level() const { return m_regdata[0x0b]; }
+ uint32_t limit() const { return m_regdata[0x0c] | (m_regdata[0x0d] << 8); }
+ uint32_t dac() const { return m_regdata[0x0e]; }
+ uint32_t pcm() const { return m_regdata[0x0f]; }
+
+private:
+ // internal state
+ uint8_t m_regdata[REGISTERS]; // register data
+};
+
+
+// ======================> adpcm_b_channel
+
+class adpcm_b_channel
+{
+ static constexpr int32_t STEP_MIN = 127;
+ static constexpr int32_t STEP_MAX = 24576;
+
+public:
+ static constexpr uint8_t STATUS_EOS = 0x01;
+ static constexpr uint8_t STATUS_BRDY = 0x02;
+ static constexpr uint8_t STATUS_PLAYING = 0x04;
+
+ // constructor
+ adpcm_b_channel(adpcm_b_engine &owner, uint32_t addrshift);
+
+ // reset the channel state
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // signal key on/off
+ void keyonoff(bool on);
+
+ // master clocking function
+ void clock();
+
+ // return the computed output value, with panning applied
+ template<int NumOutputs>
+ void output(ymfm_output<NumOutputs> &output, uint32_t rshift) const;
+
+ // return the status register
+ uint8_t status() const { return m_status; }
+
+ // handle special register reads
+ uint8_t read(uint32_t regnum);
+
+ // handle special register writes
+ void write(uint32_t regnum, uint8_t value);
+
+private:
+ // helper - return the current address shift
+ uint32_t address_shift() const;
+
+ // load the start address
+ void load_start();
+
+ // limit checker
+ bool at_limit() const { return (m_curaddress >> address_shift()) >= m_regs.limit(); }
+
+ // end checker
+ bool at_end() const { return (m_curaddress >> address_shift()) > m_regs.end(); }
+
+ // 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
+};
+
+
+// ======================> adpcm_b_engine
+
+class adpcm_b_engine
+{
+public:
+ // constructor
+ adpcm_b_engine(ymfm_interface &intf, uint32_t addrshift = 0);
+
+ // reset our status
+ void reset();
+
+ // save/restore
+ void save_restore(ymfm_saved_state &state);
+
+ // master clocking function
+ void clock();
+
+ // compute sum of channel outputs
+ template<int NumOutputs>
+ void output(ymfm_output<NumOutputs> &output, uint32_t rshift);
+
+ // read from the ADPCM-B registers
+ uint32_t read(uint32_t regnum) { return m_channel->read(regnum); }
+
+ // write to the ADPCM-B registers
+ void write(uint32_t regnum, uint8_t data);
+
+ // status
+ uint8_t status() const { return m_channel->status(); }
+
+ // return a reference to our interface
+ ymfm_interface &intf() { return m_intf; }
+
+ // return a reference to our registers
+ adpcm_b_registers &regs() { return m_regs; }
+
+private:
+ // internal state
+ ymfm_interface &m_intf; // reference to our interface
+ std::unique_ptr<adpcm_b_channel> m_channel; // channel pointer
+ adpcm_b_registers m_regs; // registers
+};
+
+}
+
+#endif // YMFM_ADPCM_H