summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/ymadpcm.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/ymadpcm.h')
-rw-r--r--src/devices/sound/ymadpcm.h338
1 files changed, 338 insertions, 0 deletions
diff --git a/src/devices/sound/ymadpcm.h b/src/devices/sound/ymadpcm.h
new file mode 100644
index 00000000000..53f6db1e4b5
--- /dev/null
+++ b/src/devices/sound/ymadpcm.h
@@ -0,0 +1,338 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+
+#ifndef MAME_SOUND_YMADPCM_H
+#define MAME_SOUND_YMADPCM_H
+
+#pragma once
+
+#include "dirom.h"
+
+
+// ======================> ymadpcm_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 ymadpcm_a_registers
+{
+ // private constructor to directly specify channel base
+ ymadpcm_a_registers(ymadpcm_a_registers const &src, u8 chbase) :
+ m_chbase(chbase),
+ m_regdata(src.m_regdata)
+ {
+ }
+
+public:
+ // constructor
+ ymadpcm_a_registers(std::vector<u8> &regdata) :
+ m_chbase(0),
+ m_regdata(regdata)
+ {
+ }
+
+ u8 chbase() const { return m_chbase; }
+
+ // direct read/write access
+ u8 read(u8 index) { return m_regdata[index]; }
+ void write(u8 index, u8 data) { m_regdata[index] = data; }
+
+ // create a new version of ourself with a different channel/operator base
+ ymadpcm_a_registers channel_registers(u8 chnum) { return ymadpcm_a_registers(*this, chnum); }
+
+ // system-wide registers
+ u8 dump() const /* 1 bit */ { return BIT(m_regdata[0x00], 7); }
+ u8 dump_mask() const /* 6 bits */ { return BIT(m_regdata[0x00], 0, 6); }
+ u8 total_level() const /* 6 bits */ { return BIT(m_regdata[0x01], 0, 6); }
+ u8 test() const /* 8 bits */ { return m_regdata[0x02]; }
+
+ // per-channel registers
+ u8 pan_left() const /* 1 bit */ { return BIT(m_regdata[m_chbase + 0x08], 7); }
+ u8 pan_right() const /* 1 bit */ { return BIT(m_regdata[m_chbase + 0x08], 6); }
+ u8 instrument_level() const /* 5 bits */ { return BIT(m_regdata[m_chbase + 0x08], 0, 5); }
+ u16 start() const /* 16 bits */ { return m_regdata[m_chbase + 0x10] | (m_regdata[m_chbase + 0x18] << 8); }
+ u16 end() const /* 16 bits */ { return m_regdata[m_chbase + 0x20] | (m_regdata[m_chbase + 0x28] << 8); }
+
+ // per-channel writes
+ void write_start(u16 address) { write(m_chbase + 0x10, address); write(m_chbase + 0x18, address >> 8); }
+ void write_end(u16 address) { write(m_chbase + 0x20, address); write(m_chbase + 0x28, address >> 8); }
+
+private:
+ // internal state
+ u8 m_chbase; // base offset for channel-specific data
+ std::vector<u8> &m_regdata; // reference to the raw data
+};
+
+
+// ======================> ymadpcm_a_channel
+
+class ymadpcm_a_channel
+{
+public:
+ // constructor
+ ymadpcm_a_channel(ymadpcm_a_registers regs, read8sm_delegate reader, u8 addrshift);
+
+ // register for save states
+ void save(device_t &device, u8 index);
+
+ // reset the channel state
+ void reset();
+
+ // signal key on/off
+ void keyonoff(bool on);
+
+ // master clockingfunction
+ bool clock();
+
+ // return the computed output value, with panning applied
+ void output(s32 &lsum, s32 &rsum) const;
+
+ // direct parameter setting for YM2608 ROM-based samples
+ void set_start_end(u16 start, u16 end) { m_regs.write_start(start); m_regs.write_end(end); }
+
+private:
+ // internal state
+ u8 const m_address_shift; // address bits shift-left
+ read8sm_delegate const m_reader; // read delegate
+ u8 m_playing; // currently playing?
+ u8 m_curnibble; // index of the current nibble
+ u8 m_curbyte; // current byte of data
+ u32 m_curaddress; // current address
+ s16 m_accumulator; // accumulator
+ s8 m_step_index; // index in the stepping table
+ ymadpcm_a_registers m_regs; // register accessor
+};
+
+
+// ======================> ymadpcm_a_engine
+
+class ymadpcm_a_engine
+{
+ static constexpr int CHANNELS = 6;
+
+public:
+ // constructor
+ ymadpcm_a_engine(device_t &device, read8sm_delegate reader, u8 addrshift);
+
+ // save state handling
+ void save(device_t &device);
+
+ // reset our status
+ void reset();
+
+ // master clocking function
+ u8 clock(u8 chanmask);
+
+ // compute sum of channel outputs
+ void output(s32 &lsum, s32 &rsum, u8 chanmask);
+
+ // write to the ADPCM-A registers
+ void write(u8 regnum, u8 data);
+
+ // set the start/end address for a channel (for hardcoded YM2608 percussion)
+ void set_start_end(u8 chnum, u16 start, u16 end) { m_channel[chnum]->set_start_end(start, end); }
+
+private:
+ // internal state
+ std::unique_ptr<ymadpcm_a_channel> m_channel[CHANNELS]; // array of channels
+ std::vector<u8> m_regdata; // raw register data
+ ymadpcm_a_registers m_regs; // register accessor
+};
+
+
+// ======================> ymadpcm_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
+// 0f xxxxxxxx PCM data
+//
+class ymadpcm_b_registers
+{
+public:
+ // constructor
+ ymadpcm_b_registers(std::vector<u8> &regdata) :
+ m_regdata(regdata)
+ {
+ }
+
+ // direct read/write access
+ u8 read(u8 index) { return m_regdata[index]; }
+ void write(u8 index, u8 data) { m_regdata[index] = data; }
+
+ // system-wide registers
+ u8 execute() const /* 1 bit */ { return BIT(m_regdata[0x00], 7); }
+ u8 record() const /* 1 bit */ { return BIT(m_regdata[0x00], 6); }
+ u8 external() const /* 1 bit */ { return BIT(m_regdata[0x00], 5); }
+ u8 repeat() const /* 1 bit */ { return BIT(m_regdata[0x00], 4); }
+ u8 speaker() const /* 1 bit */ { return BIT(m_regdata[0x00], 3); }
+ u8 reset() const /* 1 bit */ { return BIT(m_regdata[0x00], 0); }
+ u8 pan_left() const /* 1 bit */ { return BIT(m_regdata[0x01], 7); }
+ u8 pan_right() const /* 1 bit */ { return BIT(m_regdata[0x01], 6); }
+ u8 start_conversion() const /* 1 bit */ { return BIT(m_regdata[0x01], 3); }
+ u8 dac_enable() const /* 1 bit */ { return BIT(m_regdata[0x01], 2); }
+ u8 dram_8bit() const /* 1 bit */ { return BIT(m_regdata[0x01], 1); }
+ u8 rom_ram() const /* 1 bit */ { return BIT(m_regdata[0x01], 0); }
+ u16 start() const /* 16 bits */ { return m_regdata[0x02] | (m_regdata[0x03] << 8); }
+ u16 end() const /* 16 bits */ { return m_regdata[0x04] | (m_regdata[0x05] << 8); }
+ u16 prescale() const /* 11 bits */ { return m_regdata[0x06] | (BIT(m_regdata[0x07], 0, 3) << 8); }
+ u8 cpudata() const /* 8 bits */ { return m_regdata[0x08]; }
+ u16 delta_n() const /* 16 bits */ { return m_regdata[0x09] | (m_regdata[0x0a] << 8); }
+ u8 level() const /* 8 bits */ { return m_regdata[0x0b]; }
+ u16 limit() const /* 16 bits */ { return m_regdata[0x0c] | (m_regdata[0x0d] << 8); }
+ u8 dac() const /* 8 bits */ { return m_regdata[0x0e]; }
+ u8 pcm() const /* 8 bits */ { return m_regdata[0x0f]; }
+
+private:
+ // internal state
+ std::vector<u8> &m_regdata; // reference to the raw data
+};
+
+
+// ======================> ymadpcm_b_channel
+
+class ymadpcm_b_channel
+{
+ static constexpr s32 STEP_MIN = 127;
+ static constexpr s32 STEP_MAX = 24576;
+
+public:
+ static constexpr u8 STATUS_EOS = 0x01;
+ static constexpr u8 STATUS_BRDY = 0x02;
+ static constexpr u8 STATUS_PLAYING = 0x04;
+
+ // constructor
+ ymadpcm_b_channel(ymadpcm_b_registers regs, read8sm_delegate reader, write8sm_delegate writer, u8 addrshift);
+
+ // register for save states
+ void save(device_t &device, u8 index);
+
+ // reset the channel state
+ void reset();
+
+ // signal key on/off
+ void keyonoff(bool on);
+
+ // master clocking function
+ void clock();
+
+ // return the computed output value, with panning applied
+ void output(s32 &lsum, s32 &rsum, u8 rshift) const;
+
+ // return the status register
+ u8 status() const { return m_status; }
+
+ // handle special register reads
+ u8 read(u8 regnum);
+
+ // handle special register writes
+ void write(u8 regnum, u8 value);
+
+private:
+ // helper - return the current address shift
+ u8 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
+ read8sm_delegate const m_reader; // read delegate
+ write8sm_delegate const m_writer;// write delegate
+ u8 const m_address_shift; // address bits shift-left
+ u8 m_status; // currently playing?
+ u8 m_curnibble; // index of the current nibble
+ u8 m_curbyte; // current byte of data
+ u8 m_dummy_read; // dummy read tracker
+ u16 m_position; // current fractional position
+ u32 m_curaddress; // current address
+ s32 m_accumulator; // accumulator
+ s32 m_prev_accum; // previous accumulator (for linear interp)
+ s32 m_adpcm_step; // next forecast
+ ymadpcm_b_registers m_regs; // register accessor
+};
+
+
+// ======================> ymadpcm_b_engine
+
+class ymadpcm_b_engine
+{
+ static constexpr int CHANNELS = 1;
+
+public:
+ // constructor
+ ymadpcm_b_engine(device_t &device, read8sm_delegate reader, write8sm_delegate writer, u8 addrshift = 0);
+
+ // save state handling
+ void save(device_t &device);
+
+ // reset our status
+ void reset();
+
+ // master clocking function
+ void clock(u8 chanmask);
+
+ // compute sum of channel outputs
+ void output(s32 &lsum, s32 &rsum, u8 rshift, u8 chanmask);
+
+ // read from the ADPCM-B registers
+ u8 read(u8 regnum) { return m_channel[0]->read(regnum); }
+
+ // write to the ADPCM-B registers
+ void write(u8 regnum, u8 data);
+
+ // status
+ u8 status(u8 chnum = 0) const { return m_channel[chnum]->status(); }
+
+private:
+ // internal state
+ std::unique_ptr<ymadpcm_b_channel> m_channel[CHANNELS]; // array of channels
+ std::vector<u8> m_regdata; // raw register data
+ ymadpcm_b_registers m_regs; // register accessor
+};
+
+#endif // MAME_SOUND_YMADPCM_H