25static constexpr int R07_RESET = 0x01;
26static constexpr int R07_SP_OFF = 0x08;
27static constexpr int R07_REPEAT = 0x10;
28static constexpr int R07_MEMORY_DATA = 0x20;
29static constexpr int R07_REC = 0x40;
30static constexpr int R07_START = 0x80;
31static constexpr int R07_MODE = 0xE0;
34static constexpr int R08_ROM = 0x01;
35static constexpr int R08_64K = 0x02;
36static constexpr int R08_DA_AD = 0x04;
37static constexpr int R08_SAMPL = 0x08;
38static constexpr int R08_NOTE_SET = 0x40;
39static constexpr int R08_CSM = 0x80;
41static constexpr int DIFF_MAX = 0x6000;
42static constexpr int DIFF_MIN = 0x7F;
43static constexpr int DIFF_DEFAULT = 0x7F;
45static constexpr int STEP_BITS = 16;
46static constexpr int STEP_MASK = (1 << STEP_BITS) -1;
50 const std::string& name,
unsigned sampleRam)
53 , ram(config, name +
" RAM",
"Y8950 sample RAM", sampleRam)
54 , clock(config.getMotherBoard().getCurrentTime())
73 addrMask = (1 << 18) - 1;
86bool Y8950Adpcm::isPlaying()
const
88 return (reg7 & 0xC0) == 0x80;
92 return !isPlaying() || (reg7 & R07_SP_OFF);
95void Y8950Adpcm::restart(PlayData& pd)
97 pd.memPtr = startAddr;
98 pd.nowStep = (1 << STEP_BITS) - delta;
101 pd.diff = DIFF_DEFAULT;
111 for (
unsigned i = 0; isPlaying() && (i < ticks); ++i) {
118void Y8950Adpcm::schedule()
121 if ((stopAddr > startAddr) && (delta != 0)) {
125 if (reg7 & R07_MEMORY_DATA) {
128 uint64_t samples = stopAddr - emu.memPtr + 1;
129 uint64_t
length = (samples << STEP_BITS) +
130 ((1 << STEP_BITS) - emu.nowStep) +
132 stop += unsigned(
length / delta);
142void Y8950Adpcm::executeUntil(EmuTime::param time)
147 if (isPlaying() && (reg7 & R07_REPEAT)) {
158 if (reg7 & R07_START) {
163 if (reg7 & R07_RESET) {
166 if (reg7 & R07_START) {
171 if (reg7 & R07_MEMORY_DATA) {
173 emu.memPtr = startAddr;
174 aud.memPtr = startAddr;
176 if ((reg7 & 0xA0) == 0x20) {
192 romBank = data & R08_ROM;
193 addrMask = data & R08_64K ? (1 << 16) - 1 : (1 << 18) - 1;
197 startAddr = (startAddr & 0x7F807) | (data << 3);
200 startAddr = (startAddr & 0x007FF) | (data << 11);
204 stopAddr = (stopAddr & 0x7F807) | (data << 3);
211 stopAddr = (stopAddr & 0x007FF) | (data << 11);
223 delta = (delta & 0xFF00) | data;
224 volumeWStep = (volume * delta) >> STEP_BITS;
231 delta = (delta & 0x00FF) | (data << 8);
232 volumeWStep = (volume * delta) >> STEP_BITS;
241 volumeWStep = (volume * delta) >> STEP_BITS;
255void Y8950Adpcm::writeData(
byte data)
258 if ((reg7 & R07_MODE) == 0x60) {
260 assert(!isPlaying());
262 emu.memPtr = startAddr;
265 if (emu.memPtr <= stopAddr) {
266 writeMemory(emu.memPtr, data);
282 if (emu.memPtr > stopAddr) {
289 emu.memPtr = startAddr;
293 }
else if ((reg7 & R07_MODE) == 0x80) {
305 byte result = (rg == 0x0F)
326 return narrow_cast<byte>((emu.output >> 8) & 0xFF);
328 return narrow_cast<byte>((emu.output >> 16) & 0xFF);
352 if (((reg7 & R07_MODE & ~R07_REC) == R07_MEMORY_DATA) ||
353 ((reg7 & R07_MODE) == 0)){
359byte Y8950Adpcm::readData()
361 if ((reg7 & R07_MODE) == R07_MEMORY_DATA) {
363 assert(!isPlaying());
365 emu.memPtr = startAddr;
368 byte result = peekData();
369 if ((reg7 & R07_MODE) == R07_MEMORY_DATA) {
370 assert(!isPlaying());
375 }
else if (emu.memPtr > stopAddr) {
398byte Y8950Adpcm::peekData()
const
400 if ((reg7 & R07_MODE) == R07_MEMORY_DATA) {
402 assert(!isPlaying());
405 }
else if (emu.memPtr > stopAddr) {
408 return readMemory(emu.memPtr);
415void Y8950Adpcm::writeMemory(
unsigned memPtr,
byte value)
417 unsigned addr = (memPtr / 2) & addrMask;
418 if ((addr < ram.
size()) && !romBank) {
419 ram.
write(addr, value);
422byte Y8950Adpcm::readMemory(
unsigned memPtr)
const
424 unsigned addr = (memPtr / 2) & addrMask;
425 if (romBank || (addr >= ram.
size())) {
435 if (!isPlaying())
return 0;
437 return (reg7 & R07_SP_OFF) ? 0 : output;
443 static constexpr std::array<int, 16> F1 = {
444 1, 3, 5, 7, 9, 11, 13, 15,
445 -1, -3, -5, -7, -9, -11, -13, -15
447 static constexpr std::array<int, 16> F2 = {
448 57, 57, 57, 57, 77, 102, 128, 153,
449 57, 57, 57, 57, 77, 102, 128, 153
454 PlayData& pd = doEmu ? emu : aud;
456 if (pd.nowStep & ~STEP_MASK) {
457 pd.nowStep &= STEP_MASK;
459 if (!(pd.memPtr & 1)) {
461 if (reg7 & R07_MEMORY_DATA) {
462 pd.adpcm_data = readMemory(pd.memPtr);
464 pd.adpcm_data = reg15;
470 return byte(pd.adpcm_data >> 4);
473 return byte(pd.adpcm_data & 0x0F);
476 int prevOut = pd.out;
478 pd.diff =
std::clamp((pd.diff * F2[val]) / 64, DIFF_MIN, DIFF_MAX);
480 int prevLeveling = pd.nextLeveling;
481 pd.nextLeveling = (prevOut + pd.out) / 2;
482 int deltaLeveling = pd.nextLeveling - prevLeveling;
483 pd.sampleStep = deltaLeveling * volumeWStep;
484 int tmp = deltaLeveling * ((volume * narrow<int>(pd.nowStep)) >> STEP_BITS);
485 pd.output = prevLeveling * volume + tmp;
488 if ((reg7 & R07_MEMORY_DATA) &&
489 (pd.memPtr > stopAddr)) {
499 if (reg7 & R07_REPEAT) {
509 pd.output += pd.sampleStep;
511 return pd.output >> 12;
521template<
typename Archive>
524 ar.template serializeBase<Schedulable>(*
this);
525 ar.serialize(
"ram", ram,
526 "startAddr", startAddr,
527 "stopAddr", stopAddr,
528 "addrMask", addrMask,
530 "volumeWStep", volumeWStep,
531 "readDelay", readDelay,
537 "memPntr", emu.memPtr,
538 "nowStep", emu.nowStep,
540 "output", emu.output,
542 "nextLeveling", emu.nextLeveling,
543 "sampleStep", emu.sampleStep,
544 "adpcm_data", emu.adpcm_data);
545 if constexpr (Archive::IS_LOADER) {
551 if (ar.versionBelow(version, 2)) {
561 ar.serialize(
"clock", clock);
Represents a clock with a fixed frequency.
constexpr void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
constexpr void advance(EmuTime::param e)
Advance this clock in time until the last tick which is not past the given time.
constexpr unsigned getTicksTill(EmuTime::param e) const
Calculate the number of ticks for this clock until the given time.
Every class that wants to get scheduled at some point must inherit from this class.
void setSyncPoint(EmuTime::param timestamp)
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
void write(size_t addr, byte value)
void sync(EmuTime::param time)
byte readReg(byte rg, EmuTime::param time)
byte peekReg(byte rg, EmuTime::param time) const
Y8950Adpcm(Y8950 &y8950, const DeviceConfig &config, const std::string &name, unsigned sampleRam)
void writeReg(byte rg, byte data, EmuTime::param time)
void serialize(Archive &ar, unsigned version)
void reset(EmuTime::param time)
static constexpr int STATUS_BUF_RDY
void setStatus(uint8_t flags)
static constexpr int STATUS_PCM_BSY
void resetStatus(uint8_t flags)
static constexpr int STATUS_EOS
uint8_t peekRawStatus() const
int16_t clipToInt16(T x)
Clip x to range [-32768,32767].
T length(const vecN< N, T > &x)
constexpr vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
This file implemented 3 utility functions:
uint8_t byte
8 bit unsigned integer
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)