Update sound
This commit is contained in:
parent
1dc9d0e123
commit
b99b5a05d6
|
@ -1266,37 +1266,37 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
|
|||
|
||||
state.spu.cycleCounter = 0; // spu.cycleCounter >> 12 & 7 represents the frame sequencer position.
|
||||
|
||||
state.spu.ch1.sweep.counter = SoundUnit::COUNTER_DISABLED;
|
||||
state.spu.ch1.sweep.counter = SoundUnit::counter_disabled;
|
||||
state.spu.ch1.sweep.shadow = 0;
|
||||
state.spu.ch1.sweep.nr0 = 0;
|
||||
state.spu.ch1.sweep.negging = false;
|
||||
state.spu.ch1.duty.nextPosUpdate = (state.spu.cycleCounter & ~1ul) + 37 * 2;
|
||||
state.spu.ch1.duty.nr3 = 0;
|
||||
state.spu.ch1.duty.nextPosUpdate = SoundUnit::counter_disabled;
|
||||
state.spu.ch1.duty.pos = 0;
|
||||
state.spu.ch1.env.counter = SoundUnit::COUNTER_DISABLED;
|
||||
state.spu.ch1.duty.high = false;
|
||||
state.spu.ch1.duty.nr3 = 0;
|
||||
state.spu.ch1.env.counter = SoundUnit::counter_disabled;
|
||||
state.spu.ch1.env.volume = 0;
|
||||
state.spu.ch1.lcounter.counter = SoundUnit::COUNTER_DISABLED;
|
||||
state.spu.ch1.lcounter.counter = SoundUnit::counter_disabled;
|
||||
state.spu.ch1.lcounter.lengthCounter = 0;
|
||||
state.spu.ch1.nr4 = 0;
|
||||
state.spu.ch1.master = false;
|
||||
|
||||
state.spu.ch2.duty.nextPosUpdate = SoundUnit::COUNTER_DISABLED;
|
||||
state.spu.ch2.duty.nextPosUpdate = SoundUnit::counter_disabled;
|
||||
state.spu.ch2.duty.nr3 = 0;
|
||||
state.spu.ch2.duty.pos = 0;
|
||||
state.spu.ch2.env.counter = SoundUnit::COUNTER_DISABLED;
|
||||
state.spu.ch2.duty.high = false;
|
||||
state.spu.ch2.env.counter = SoundUnit::counter_disabled;
|
||||
state.spu.ch2.env.volume = 0;
|
||||
state.spu.ch2.lcounter.counter = SoundUnit::COUNTER_DISABLED;
|
||||
state.spu.ch2.lcounter.counter = SoundUnit::counter_disabled;
|
||||
state.spu.ch2.lcounter.lengthCounter = 0;
|
||||
state.spu.ch2.nr4 = 0;
|
||||
state.spu.ch2.master = false;
|
||||
|
||||
for (unsigned i = 0; i < 0x10; ++i)
|
||||
state.spu.ch3.waveRam.ptr[i] = state.mem.ioamhram.get()[0x130 + i];
|
||||
|
||||
state.spu.ch3.lcounter.counter = SoundUnit::COUNTER_DISABLED;
|
||||
std::memcpy(state.spu.ch3.waveRam.ptr, state.mem.ioamhram.get() + 0x130, 0x10);
|
||||
state.spu.ch3.lcounter.counter = SoundUnit::counter_disabled;
|
||||
state.spu.ch3.lcounter.lengthCounter = 0x100;
|
||||
state.spu.ch3.waveCounter = SoundUnit::COUNTER_DISABLED;
|
||||
state.spu.ch3.lastReadTime = SoundUnit::COUNTER_DISABLED;
|
||||
state.spu.ch3.waveCounter = SoundUnit::counter_disabled;
|
||||
state.spu.ch3.lastReadTime = SoundUnit::counter_disabled;
|
||||
state.spu.ch3.nr3 = 0;
|
||||
state.spu.ch3.nr4 = 0;
|
||||
state.spu.ch3.wavePos = 0;
|
||||
|
@ -1305,9 +1305,9 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
|
|||
|
||||
state.spu.ch4.lfsr.counter = state.spu.cycleCounter + 4;
|
||||
state.spu.ch4.lfsr.reg = 0xFF;
|
||||
state.spu.ch4.env.counter = SoundUnit::COUNTER_DISABLED;
|
||||
state.spu.ch4.env.counter = SoundUnit::counter_disabled;
|
||||
state.spu.ch4.env.volume = 0;
|
||||
state.spu.ch4.lcounter.counter = SoundUnit::COUNTER_DISABLED;
|
||||
state.spu.ch4.lcounter.counter = SoundUnit::counter_disabled;
|
||||
state.spu.ch4.lcounter.lengthCounter = 0;
|
||||
state.spu.ch4.nr4 = 0;
|
||||
state.spu.ch4.master = false;
|
||||
|
|
|
@ -321,7 +321,7 @@ unsigned long Memory::stop(unsigned long cycleCounter) {
|
|||
cycleCounter += 4;
|
||||
|
||||
if (ioamhram[0x14D] & isCgb()) {
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
|
||||
display.speedChange(cycleCounter);
|
||||
ioamhram[0x14D] ^= 0x81;
|
||||
|
@ -496,7 +496,7 @@ unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleC
|
|||
break;
|
||||
case 0x26:
|
||||
if (ioamhram[0x126] & 0x80) {
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
ioamhram[0x126] = 0xF0 | sound.getStatus();
|
||||
} else
|
||||
ioamhram[0x126] = 0x70;
|
||||
|
@ -518,7 +518,7 @@ unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleC
|
|||
case 0x3D:
|
||||
case 0x3E:
|
||||
case 0x3F:
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
return sound.waveRamRead(P & 0xF);
|
||||
case 0x41:
|
||||
return ioamhram[0x141] | display.getStat(ioamhram[0x145], cycleCounter);
|
||||
|
@ -670,8 +670,8 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
|
|||
return;
|
||||
case 0x10:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr10(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr10(data);
|
||||
data |= 0x80;
|
||||
break;
|
||||
case 0x11:
|
||||
|
@ -682,24 +682,24 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
|
|||
data &= 0x3F;
|
||||
}
|
||||
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr11(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr11(data);
|
||||
data |= 0x3F;
|
||||
break;
|
||||
case 0x12:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr12(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr12(data);
|
||||
break;
|
||||
case 0x13:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr13(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr13(data);
|
||||
return;
|
||||
case 0x14:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr14(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr14(data);
|
||||
data |= 0xBF;
|
||||
break;
|
||||
case 0x16:
|
||||
|
@ -710,88 +710,88 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
|
|||
data &= 0x3F;
|
||||
}
|
||||
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr21(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr21(data);
|
||||
data |= 0x3F;
|
||||
break;
|
||||
case 0x17:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr22(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr22(data);
|
||||
break;
|
||||
case 0x18:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr23(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr23(data);
|
||||
return;
|
||||
case 0x19:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr24(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr24(data);
|
||||
data |= 0xBF;
|
||||
break;
|
||||
case 0x1A:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr30(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr30(data);
|
||||
data |= 0x7F;
|
||||
break;
|
||||
case 0x1B:
|
||||
if (!sound.isEnabled() && isCgb()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr31(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr31(data);
|
||||
return;
|
||||
case 0x1C:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr32(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr32(data);
|
||||
data |= 0x9F;
|
||||
break;
|
||||
case 0x1D:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr33(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr33(data);
|
||||
return;
|
||||
case 0x1E:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr34(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr34(data);
|
||||
data |= 0xBF;
|
||||
break;
|
||||
case 0x20:
|
||||
if (!sound.isEnabled() && isCgb()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr41(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr41(data);
|
||||
return;
|
||||
case 0x21:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr42(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr42(data);
|
||||
break;
|
||||
case 0x22:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr43(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr43(data);
|
||||
break;
|
||||
case 0x23:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_nr44(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setNr44(data);
|
||||
data |= 0xBF;
|
||||
break;
|
||||
case 0x24:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.set_so_volume(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.setSoVolume(data);
|
||||
break;
|
||||
case 0x25:
|
||||
if (!sound.isEnabled()) return;
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.map_so(data);
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.mapSo(data);
|
||||
break;
|
||||
case 0x26:
|
||||
if ((ioamhram[0x126] ^ data) & 0x80) {
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
|
||||
if (!(data & 0x80)) {
|
||||
for (unsigned i = 0xFF10; i < 0xFF26; ++i)
|
||||
|
@ -822,7 +822,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
|
|||
case 0x3D:
|
||||
case 0x3E:
|
||||
case 0x3F:
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
sound.waveRamWrite(P & 0xF, data);
|
||||
break;
|
||||
case 0x40:
|
||||
|
@ -1067,7 +1067,7 @@ LoadRes Memory::loadROM(const char *romfiledata, unsigned romfilelength, const b
|
|||
}
|
||||
|
||||
unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) {
|
||||
sound.generate_samples(cycleCounter, isDoubleSpeed());
|
||||
sound.generateSamples(cycleCounter, isDoubleSpeed());
|
||||
return sound.fillBuffer();
|
||||
}
|
||||
|
||||
|
|
|
@ -125,6 +125,7 @@ struct SaveState {
|
|||
unsigned long nextPosUpdate;
|
||||
unsigned char nr3;
|
||||
unsigned char pos;
|
||||
unsigned char /*bool*/ high;
|
||||
};
|
||||
|
||||
struct Env {
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include "sound.h"
|
||||
#include "savestate.h"
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
/*
|
||||
Frame Sequencer
|
||||
|
||||
Step Length Ctr Vol Env Sweep
|
||||
- - - - - - - - - - - - - - - - - - - -
|
||||
0 Clock - Clock
|
||||
S 1 - Clock -
|
||||
S0 0 Clock - Clock
|
||||
S1 1 - Clock -
|
||||
2 Clock - -
|
||||
3 - - -
|
||||
4 Clock - Clock
|
||||
|
@ -37,82 +37,82 @@ S 1 - Clock -
|
|||
- - - - - - - - - - - - - - - - - - - -
|
||||
Rate 256 Hz 64 Hz 128 Hz
|
||||
|
||||
S) start step on sound power on.
|
||||
S0) start step on sound power on.
|
||||
S1) step gets immediately incremented at power on if closer to previous edge than next.
|
||||
Clock) clock timer on transition to step.
|
||||
|
||||
*/
|
||||
|
||||
namespace gambatte {
|
||||
|
||||
PSG::PSG()
|
||||
: buffer(0),
|
||||
lastUpdate(0),
|
||||
soVol(0),
|
||||
rsum(0x8000), // initialize to 0x8000 to prevent borrows from high word, xor away later
|
||||
bufferPos(0),
|
||||
enabled(false)
|
||||
: buffer_(0)
|
||||
, bufferPos_(0)
|
||||
, lastUpdate_(0)
|
||||
, soVol_(0)
|
||||
, rsum_(0x8000) // initialize to 0x8000 to prevent borrows from high word, xor away later
|
||||
, enabled_(false)
|
||||
{
|
||||
}
|
||||
|
||||
void PSG::init(const bool cgb) {
|
||||
ch1.init(cgb);
|
||||
ch2.init(cgb);
|
||||
ch3.init(cgb);
|
||||
ch4.init(cgb);
|
||||
void PSG::init(bool cgb) {
|
||||
ch1_.init(cgb);
|
||||
ch3_.init(cgb);
|
||||
}
|
||||
|
||||
void PSG::reset() {
|
||||
ch1.reset();
|
||||
ch2.reset();
|
||||
ch3.reset();
|
||||
ch4.reset();
|
||||
ch1_.reset();
|
||||
ch2_.reset();
|
||||
ch3_.reset();
|
||||
ch4_.reset();
|
||||
}
|
||||
|
||||
void PSG::setStatePtrs(SaveState &state) {
|
||||
ch3.setStatePtrs(state);
|
||||
ch3_.setStatePtrs(state);
|
||||
}
|
||||
|
||||
void PSG::loadState(const SaveState &state) {
|
||||
ch1.loadState(state);
|
||||
ch2.loadState(state);
|
||||
ch3.loadState(state);
|
||||
ch4.loadState(state);
|
||||
void PSG::loadState(SaveState const &state) {
|
||||
ch1_.loadState(state);
|
||||
ch2_.loadState(state);
|
||||
ch3_.loadState(state);
|
||||
ch4_.loadState(state);
|
||||
|
||||
lastUpdate = state.cpu.cycleCounter;
|
||||
set_so_volume(state.mem.ioamhram.get()[0x124]);
|
||||
map_so(state.mem.ioamhram.get()[0x125]);
|
||||
enabled = state.mem.ioamhram.get()[0x126] >> 7 & 1;
|
||||
lastUpdate_ = state.cpu.cycleCounter;
|
||||
setSoVolume(state.mem.ioamhram.get()[0x124]);
|
||||
mapSo(state.mem.ioamhram.get()[0x125]);
|
||||
enabled_ = state.mem.ioamhram.get()[0x126] >> 7 & 1;
|
||||
}
|
||||
|
||||
void PSG::accumulate_channels(const unsigned long cycles) {
|
||||
uint_least32_t *const buf = buffer + bufferPos;
|
||||
|
||||
void PSG::accumulateChannels(unsigned long const cycles) {
|
||||
uint_least32_t *const buf = buffer_ + bufferPos_;
|
||||
std::memset(buf, 0, cycles * sizeof *buf);
|
||||
ch1.update(buf, soVol, cycles);
|
||||
ch2.update(buf, soVol, cycles);
|
||||
ch3.update(buf, soVol, cycles);
|
||||
ch4.update(buf, soVol, cycles);
|
||||
ch1_.update(buf, soVol_, cycles);
|
||||
ch2_.update(buf, soVol_, cycles);
|
||||
ch3_.update(buf, soVol_, cycles);
|
||||
ch4_.update(buf, soVol_, cycles);
|
||||
}
|
||||
|
||||
void PSG::generate_samples(const unsigned long cycleCounter, const unsigned doubleSpeed) {
|
||||
const unsigned long cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed);
|
||||
lastUpdate += cycles << (1 + doubleSpeed);
|
||||
void PSG::generateSamples(unsigned long const cycleCounter, bool const doubleSpeed) {
|
||||
unsigned long const cycles = (cycleCounter - lastUpdate_) >> (1 + doubleSpeed);
|
||||
lastUpdate_ += cycles << (1 + doubleSpeed);
|
||||
|
||||
if (cycles)
|
||||
accumulate_channels(cycles);
|
||||
accumulateChannels(cycles);
|
||||
|
||||
bufferPos += cycles;
|
||||
bufferPos_ += cycles;
|
||||
}
|
||||
|
||||
void PSG::resetCounter(const unsigned long newCc, const unsigned long oldCc, const unsigned doubleSpeed) {
|
||||
generate_samples(oldCc, doubleSpeed);
|
||||
lastUpdate = newCc - (oldCc - lastUpdate);
|
||||
void PSG::resetCounter(unsigned long newCc, unsigned long oldCc, bool doubleSpeed) {
|
||||
generateSamples(oldCc, doubleSpeed);
|
||||
lastUpdate_ = newCc - (oldCc - lastUpdate_);
|
||||
}
|
||||
|
||||
unsigned PSG::fillBuffer() {
|
||||
uint_least32_t sum = rsum;
|
||||
uint_least32_t *b = buffer;
|
||||
unsigned n = bufferPos;
|
||||
std::size_t PSG::fillBuffer() {
|
||||
uint_least32_t sum = rsum_;
|
||||
uint_least32_t *b = buffer_;
|
||||
std::size_t n = bufferPos_;
|
||||
|
||||
if (unsigned n2 = n >> 3) {
|
||||
if (std::size_t n2 = n >> 3) {
|
||||
n -= n2 << 3;
|
||||
|
||||
do {
|
||||
|
@ -139,50 +139,59 @@ unsigned PSG::fillBuffer() {
|
|||
|
||||
while (n--) {
|
||||
sum += *b;
|
||||
*b++ = sum ^ 0x8000; // xor away the initial rsum value of 0x8000 (which prevents borrows from the high word) from the low word
|
||||
// xor away the initial rsum value of 0x8000 (which prevents
|
||||
// borrows from the high word) from the low word
|
||||
*b++ = sum ^ 0x8000;
|
||||
}
|
||||
|
||||
rsum = sum;
|
||||
rsum_ = sum;
|
||||
|
||||
return bufferPos;
|
||||
return bufferPos_;
|
||||
}
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
static const unsigned long so1Mul = 0x00000001;
|
||||
static const unsigned long so2Mul = 0x00010000;
|
||||
#else
|
||||
static const unsigned long so1Mul = 0x00010000;
|
||||
static const unsigned long so2Mul = 0x00000001;
|
||||
#endif
|
||||
|
||||
void PSG::set_so_volume(const unsigned nr50) {
|
||||
soVol = (((nr50 & 0x7) + 1) * so1Mul + ((nr50 >> 4 & 0x7) + 1) * so2Mul) * 64;
|
||||
static bool isBigEndianSampleOrder() {
|
||||
union {
|
||||
uint_least32_t ul32;
|
||||
unsigned char uc[sizeof(uint_least32_t)];
|
||||
} u;
|
||||
u.ul32 = -0x10000;
|
||||
return u.uc[0];
|
||||
}
|
||||
|
||||
void PSG::map_so(const unsigned nr51) {
|
||||
const unsigned long tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul;
|
||||
static unsigned long so1Mul() { return isBigEndianSampleOrder() ? 0x00000001 : 0x00010000; }
|
||||
static unsigned long so2Mul() { return isBigEndianSampleOrder() ? 0x00010000 : 0x00000001; }
|
||||
|
||||
ch1.setSo((tmp & 0x00010001) * 0xFFFF);
|
||||
ch2.setSo((tmp >> 1 & 0x00010001) * 0xFFFF);
|
||||
ch3.setSo((tmp >> 2 & 0x00010001) * 0xFFFF);
|
||||
ch4.setSo((tmp >> 3 & 0x00010001) * 0xFFFF);
|
||||
void PSG::setSoVolume(unsigned nr50) {
|
||||
soVol_ = ((nr50 & 0x7) + 1) * so1Mul() * 64
|
||||
+ ((nr50 >> 4 & 0x7) + 1) * so2Mul() * 64;
|
||||
}
|
||||
|
||||
void PSG::mapSo(unsigned nr51) {
|
||||
unsigned long so = nr51 * so1Mul() + (nr51 >> 4) * so2Mul();
|
||||
ch1_.setSo((so & 0x00010001) * 0xFFFF);
|
||||
ch2_.setSo((so >> 1 & 0x00010001) * 0xFFFF);
|
||||
ch3_.setSo((so >> 2 & 0x00010001) * 0xFFFF);
|
||||
ch4_.setSo((so >> 3 & 0x00010001) * 0xFFFF);
|
||||
}
|
||||
|
||||
unsigned PSG::getStatus() const {
|
||||
return ch1.isActive() | ch2.isActive() << 1 | ch3.isActive() << 2 | ch4.isActive() << 3;
|
||||
return ch1_.isActive()
|
||||
| ch2_.isActive() << 1
|
||||
| ch3_.isActive() << 2
|
||||
| ch4_.isActive() << 3;
|
||||
}
|
||||
|
||||
// the buffer and position are not saved, as they're set and flushed on each runfor() call
|
||||
SYNCFUNC(PSG)
|
||||
{
|
||||
SSS(ch1);
|
||||
SSS(ch2);
|
||||
SSS(ch3);
|
||||
SSS(ch4);
|
||||
NSS(lastUpdate);
|
||||
NSS(soVol);
|
||||
NSS(rsum);
|
||||
NSS(enabled);
|
||||
SSS(ch1_);
|
||||
SSS(ch2_);
|
||||
SSS(ch3_);
|
||||
SSS(ch4_);
|
||||
NSS(lastUpdate_);
|
||||
NSS(soVol_);
|
||||
NSS(rsum_);
|
||||
NSS(enabled_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,67 +28,65 @@
|
|||
namespace gambatte {
|
||||
|
||||
class PSG {
|
||||
Channel1 ch1;
|
||||
Channel2 ch2;
|
||||
Channel3 ch3;
|
||||
Channel4 ch4;
|
||||
|
||||
uint_least32_t *buffer;
|
||||
|
||||
unsigned long lastUpdate;
|
||||
unsigned long soVol;
|
||||
|
||||
uint_least32_t rsum;
|
||||
|
||||
unsigned bufferPos;
|
||||
|
||||
bool enabled;
|
||||
|
||||
void accumulate_channels(unsigned long cycles);
|
||||
|
||||
public:
|
||||
PSG();
|
||||
void init(bool cgb);
|
||||
void reset();
|
||||
void setStatePtrs(SaveState &state);
|
||||
void loadState(const SaveState &state);
|
||||
void loadState(SaveState const &state);
|
||||
|
||||
void generate_samples(unsigned long cycleCounter, unsigned doubleSpeed);
|
||||
void resetCounter(unsigned long newCc, unsigned long oldCc, unsigned doubleSpeed);
|
||||
unsigned fillBuffer();
|
||||
void setBuffer(uint_least32_t *const buf) { buffer = buf; bufferPos = 0; }
|
||||
void generateSamples(unsigned long cycleCounter, bool doubleSpeed);
|
||||
void resetCounter(unsigned long newCc, unsigned long oldCc, bool doubleSpeed);
|
||||
std::size_t fillBuffer();
|
||||
void setBuffer(uint_least32_t *buf) { buffer_ = buf; bufferPos_ = 0; }
|
||||
|
||||
bool isEnabled() const { return enabled; }
|
||||
void setEnabled(bool value) { enabled = value; }
|
||||
bool isEnabled() const { return enabled_; }
|
||||
void setEnabled(bool value) { enabled_ = value; }
|
||||
|
||||
void set_nr10(unsigned data) { ch1.setNr0(data); }
|
||||
void set_nr11(unsigned data) { ch1.setNr1(data); }
|
||||
void set_nr12(unsigned data) { ch1.setNr2(data); }
|
||||
void set_nr13(unsigned data) { ch1.setNr3(data); }
|
||||
void set_nr14(unsigned data) { ch1.setNr4(data); }
|
||||
void setNr10(unsigned data) { ch1_.setNr0(data); }
|
||||
void setNr11(unsigned data) { ch1_.setNr1(data); }
|
||||
void setNr12(unsigned data) { ch1_.setNr2(data); }
|
||||
void setNr13(unsigned data) { ch1_.setNr3(data); }
|
||||
void setNr14(unsigned data) { ch1_.setNr4(data); }
|
||||
|
||||
void set_nr21(unsigned data) { ch2.setNr1(data); }
|
||||
void set_nr22(unsigned data) { ch2.setNr2(data); }
|
||||
void set_nr23(unsigned data) { ch2.setNr3(data); }
|
||||
void set_nr24(unsigned data) { ch2.setNr4(data); }
|
||||
void setNr21(unsigned data) { ch2_.setNr1(data); }
|
||||
void setNr22(unsigned data) { ch2_.setNr2(data); }
|
||||
void setNr23(unsigned data) { ch2_.setNr3(data); }
|
||||
void setNr24(unsigned data) { ch2_.setNr4(data); }
|
||||
|
||||
void set_nr30(unsigned data) { ch3.setNr0(data); }
|
||||
void set_nr31(unsigned data) { ch3.setNr1(data); }
|
||||
void set_nr32(unsigned data) { ch3.setNr2(data); }
|
||||
void set_nr33(unsigned data) { ch3.setNr3(data); }
|
||||
void set_nr34(unsigned data) { ch3.setNr4(data); }
|
||||
unsigned waveRamRead(unsigned index) const { return ch3.waveRamRead(index); }
|
||||
void waveRamWrite(unsigned index, unsigned data) { ch3.waveRamWrite(index, data); }
|
||||
void setNr30(unsigned data) { ch3_.setNr0(data); }
|
||||
void setNr31(unsigned data) { ch3_.setNr1(data); }
|
||||
void setNr32(unsigned data) { ch3_.setNr2(data); }
|
||||
void setNr33(unsigned data) { ch3_.setNr3(data); }
|
||||
void setNr34(unsigned data) { ch3_.setNr4(data); }
|
||||
unsigned waveRamRead(unsigned index) const { return ch3_.waveRamRead(index); }
|
||||
void waveRamWrite(unsigned index, unsigned data) { ch3_.waveRamWrite(index, data); }
|
||||
|
||||
void set_nr41(unsigned data) { ch4.setNr1(data); }
|
||||
void set_nr42(unsigned data) { ch4.setNr2(data); }
|
||||
void set_nr43(unsigned data) { ch4.setNr3(data); }
|
||||
void set_nr44(unsigned data) { ch4.setNr4(data); }
|
||||
void setNr41(unsigned data) { ch4_.setNr1(data); }
|
||||
void setNr42(unsigned data) { ch4_.setNr2(data); }
|
||||
void setNr43(unsigned data) { ch4_.setNr3(data); }
|
||||
void setNr44(unsigned data) { ch4_.setNr4(data); }
|
||||
|
||||
void set_so_volume(unsigned nr50);
|
||||
void map_so(unsigned nr51);
|
||||
void setSoVolume(unsigned nr50);
|
||||
void mapSo(unsigned nr51);
|
||||
unsigned getStatus() const;
|
||||
|
||||
private:
|
||||
Channel1 ch1_;
|
||||
Channel2 ch2_;
|
||||
Channel3 ch3_;
|
||||
Channel4 ch4_;
|
||||
uint_least32_t *buffer_;
|
||||
std::size_t bufferPos_;
|
||||
unsigned long lastUpdate_;
|
||||
unsigned long soVol_;
|
||||
uint_least32_t rsum_;
|
||||
bool enabled_;
|
||||
|
||||
void accumulateChannels(unsigned long cycles);
|
||||
|
||||
|
||||
public:
|
||||
template<bool isReader>void SyncState(NewState *ns);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,275 +1,276 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include "channel1.h"
|
||||
#include "../savestate.h"
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace gambatte {
|
||||
|
||||
Channel1::SweepUnit::SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit) :
|
||||
disableMaster(disabler),
|
||||
dutyUnit(dutyUnit),
|
||||
shadow(0),
|
||||
nr0(0),
|
||||
negging(false)
|
||||
{}
|
||||
Channel1::SweepUnit::SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit)
|
||||
: disableMaster_(disabler)
|
||||
, dutyUnit_(dutyUnit)
|
||||
, shadow_(0)
|
||||
, nr0_(0)
|
||||
, negging_(false)
|
||||
, cgb_(false)
|
||||
{
|
||||
}
|
||||
|
||||
unsigned Channel1::SweepUnit::calcFreq() {
|
||||
unsigned freq = shadow >> (nr0 & 0x07);
|
||||
unsigned freq = shadow_ >> (nr0_ & 0x07);
|
||||
|
||||
if (nr0 & 0x08) {
|
||||
freq = shadow - freq;
|
||||
negging = true;
|
||||
if (nr0_ & 0x08) {
|
||||
freq = shadow_ - freq;
|
||||
negging_ = true;
|
||||
} else
|
||||
freq = shadow + freq;
|
||||
freq = shadow_ + freq;
|
||||
|
||||
if (freq & 2048)
|
||||
disableMaster();
|
||||
disableMaster_();
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
void Channel1::SweepUnit::event() {
|
||||
const unsigned long period = nr0 >> 4 & 0x07;
|
||||
unsigned long const period = nr0_ >> 4 & 0x07;
|
||||
|
||||
if (period) {
|
||||
const unsigned freq = calcFreq();
|
||||
unsigned const freq = calcFreq();
|
||||
|
||||
if (!(freq & 2048) && (nr0 & 0x07)) {
|
||||
shadow = freq;
|
||||
dutyUnit.setFreq(freq, counter);
|
||||
if (!(freq & 2048) && (nr0_ & 0x07)) {
|
||||
shadow_ = freq;
|
||||
dutyUnit_.setFreq(freq, counter_);
|
||||
calcFreq();
|
||||
}
|
||||
|
||||
counter += period << 14;
|
||||
counter_ += period << 14;
|
||||
} else
|
||||
counter += 8ul << 14;
|
||||
counter_ += 8ul << 14;
|
||||
}
|
||||
|
||||
void Channel1::SweepUnit::nr0Change(const unsigned newNr0) {
|
||||
if (negging && !(newNr0 & 0x08))
|
||||
disableMaster();
|
||||
void Channel1::SweepUnit::nr0Change(unsigned newNr0) {
|
||||
if (negging_ && !(newNr0 & 0x08))
|
||||
disableMaster_();
|
||||
|
||||
nr0 = newNr0;
|
||||
nr0_ = newNr0;
|
||||
}
|
||||
|
||||
void Channel1::SweepUnit::nr4Init(const unsigned long cc) {
|
||||
negging = false;
|
||||
shadow = dutyUnit.getFreq();
|
||||
void Channel1::SweepUnit::nr4Init(unsigned long const cc) {
|
||||
negging_ = false;
|
||||
shadow_ = dutyUnit_.freq();
|
||||
|
||||
const unsigned period = nr0 >> 4 & 0x07;
|
||||
const unsigned shift = nr0 & 0x07;
|
||||
unsigned const period = nr0_ >> 4 & 0x07;
|
||||
unsigned const shift = nr0_ & 0x07;
|
||||
|
||||
if (period | shift)
|
||||
counter = ((cc >> 14) + (period ? period : 8)) << 14;
|
||||
counter_ = ((((cc + 2 + cgb_ * 2) >> 14) + (period ? period : 8)) << 14) + 2;
|
||||
else
|
||||
counter = COUNTER_DISABLED;
|
||||
counter_ = counter_disabled;
|
||||
|
||||
if (shift)
|
||||
calcFreq();
|
||||
}
|
||||
|
||||
void Channel1::SweepUnit::reset() {
|
||||
counter = COUNTER_DISABLED;
|
||||
counter_ = counter_disabled;
|
||||
}
|
||||
|
||||
void Channel1::SweepUnit::loadState(const SaveState &state) {
|
||||
counter = std::max(state.spu.ch1.sweep.counter, state.spu.cycleCounter);
|
||||
shadow = state.spu.ch1.sweep.shadow;
|
||||
nr0 = state.spu.ch1.sweep.nr0;
|
||||
negging = state.spu.ch1.sweep.negging;
|
||||
void Channel1::SweepUnit::loadState(SaveState const &state) {
|
||||
counter_ = std::max(state.spu.ch1.sweep.counter, state.spu.cycleCounter);
|
||||
shadow_ = state.spu.ch1.sweep.shadow;
|
||||
nr0_ = state.spu.ch1.sweep.nr0;
|
||||
negging_ = state.spu.ch1.sweep.negging;
|
||||
}
|
||||
|
||||
template<bool isReader>
|
||||
void Channel1::SweepUnit::SyncState(NewState *ns)
|
||||
{
|
||||
NSS(counter);
|
||||
NSS(shadow);
|
||||
NSS(nr0);
|
||||
NSS(negging);
|
||||
NSS(counter_);
|
||||
NSS(shadow_);
|
||||
NSS(nr0_);
|
||||
NSS(negging_);
|
||||
NSS(cgb_);
|
||||
}
|
||||
|
||||
Channel1::Channel1() :
|
||||
staticOutputTest(*this, dutyUnit),
|
||||
disableMaster(master, dutyUnit),
|
||||
lengthCounter(disableMaster, 0x3F),
|
||||
envelopeUnit(staticOutputTest),
|
||||
sweepUnit(disableMaster, dutyUnit),
|
||||
cycleCounter(0),
|
||||
soMask(0),
|
||||
prevOut(0),
|
||||
nr4(0),
|
||||
master(false)
|
||||
Channel1::Channel1()
|
||||
: staticOutputTest_(*this, dutyUnit_)
|
||||
, disableMaster_(master_, dutyUnit_)
|
||||
, lengthCounter_(disableMaster_, 0x3F)
|
||||
, envelopeUnit_(staticOutputTest_)
|
||||
, sweepUnit_(disableMaster_, dutyUnit_)
|
||||
, nextEventUnit_(0)
|
||||
, cycleCounter_(0)
|
||||
, soMask_(0)
|
||||
, prevOut_(0)
|
||||
, nr4_(0)
|
||||
, master_(false)
|
||||
{
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel1::setEvent() {
|
||||
// nextEventUnit = &dutyUnit;
|
||||
// if (sweepUnit.getCounter() < nextEventUnit->getCounter())
|
||||
nextEventUnit = &sweepUnit;
|
||||
if (envelopeUnit.getCounter() < nextEventUnit->getCounter())
|
||||
nextEventUnit = &envelopeUnit;
|
||||
if (lengthCounter.getCounter() < nextEventUnit->getCounter())
|
||||
nextEventUnit = &lengthCounter;
|
||||
nextEventUnit_ = &sweepUnit_;
|
||||
if (envelopeUnit_.counter() < nextEventUnit_->counter())
|
||||
nextEventUnit_ = &envelopeUnit_;
|
||||
if (lengthCounter_.counter() < nextEventUnit_->counter())
|
||||
nextEventUnit_ = &lengthCounter_;
|
||||
}
|
||||
|
||||
void Channel1::setNr0(const unsigned data) {
|
||||
sweepUnit.nr0Change(data);
|
||||
void Channel1::setNr0(unsigned data) {
|
||||
sweepUnit_.nr0Change(data);
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel1::setNr1(const unsigned data) {
|
||||
lengthCounter.nr1Change(data, nr4, cycleCounter);
|
||||
dutyUnit.nr1Change(data, cycleCounter);
|
||||
|
||||
void Channel1::setNr1(unsigned data) {
|
||||
lengthCounter_.nr1Change(data, nr4_, cycleCounter_);
|
||||
dutyUnit_.nr1Change(data, cycleCounter_);
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel1::setNr2(const unsigned data) {
|
||||
if (envelopeUnit.nr2Change(data))
|
||||
disableMaster();
|
||||
void Channel1::setNr2(unsigned data) {
|
||||
if (envelopeUnit_.nr2Change(data))
|
||||
disableMaster_();
|
||||
else
|
||||
staticOutputTest(cycleCounter);
|
||||
staticOutputTest_(cycleCounter_);
|
||||
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel1::setNr3(const unsigned data) {
|
||||
dutyUnit.nr3Change(data, cycleCounter);
|
||||
void Channel1::setNr3(unsigned data) {
|
||||
dutyUnit_.nr3Change(data, cycleCounter_);
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel1::setNr4(const unsigned data) {
|
||||
lengthCounter.nr4Change(nr4, data, cycleCounter);
|
||||
void Channel1::setNr4(unsigned const data) {
|
||||
lengthCounter_.nr4Change(nr4_, data, cycleCounter_);
|
||||
nr4_ = data;
|
||||
dutyUnit_.nr4Change(data, cycleCounter_, master_);
|
||||
|
||||
nr4 = data;
|
||||
|
||||
dutyUnit.nr4Change(data, cycleCounter);
|
||||
|
||||
if (data & 0x80) { //init-bit
|
||||
nr4 &= 0x7F;
|
||||
master = !envelopeUnit.nr4Init(cycleCounter);
|
||||
sweepUnit.nr4Init(cycleCounter);
|
||||
staticOutputTest(cycleCounter);
|
||||
if (data & 0x80) { // init-bit
|
||||
nr4_ &= 0x7F;
|
||||
master_ = !envelopeUnit_.nr4Init(cycleCounter_);
|
||||
sweepUnit_.nr4Init(cycleCounter_);
|
||||
staticOutputTest_(cycleCounter_);
|
||||
}
|
||||
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel1::setSo(const unsigned long soMask) {
|
||||
this->soMask = soMask;
|
||||
staticOutputTest(cycleCounter);
|
||||
void Channel1::setSo(unsigned long soMask) {
|
||||
soMask_ = soMask;
|
||||
staticOutputTest_(cycleCounter_);
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel1::reset() {
|
||||
cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position.
|
||||
|
||||
// lengthCounter.reset();
|
||||
dutyUnit.reset();
|
||||
envelopeUnit.reset();
|
||||
sweepUnit.reset();
|
||||
// cycleCounter >> 12 & 7 represents the frame sequencer position.
|
||||
cycleCounter_ &= 0xFFF;
|
||||
cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000;
|
||||
|
||||
dutyUnit_.reset();
|
||||
envelopeUnit_.reset();
|
||||
sweepUnit_.reset();
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel1::init(const bool cgb) {
|
||||
lengthCounter.init(cgb);
|
||||
void Channel1::init(bool cgb) {
|
||||
sweepUnit_.init(cgb);
|
||||
}
|
||||
|
||||
void Channel1::loadState(const SaveState &state) {
|
||||
sweepUnit.loadState(state);
|
||||
dutyUnit.loadState(state.spu.ch1.duty, state.mem.ioamhram.get()[0x111], state.spu.ch1.nr4, state.spu.cycleCounter);
|
||||
envelopeUnit.loadState(state.spu.ch1.env, state.mem.ioamhram.get()[0x112], state.spu.cycleCounter);
|
||||
lengthCounter.loadState(state.spu.ch1.lcounter, state.spu.cycleCounter);
|
||||
void Channel1::loadState(SaveState const &state) {
|
||||
sweepUnit_.loadState(state);
|
||||
dutyUnit_.loadState(state.spu.ch1.duty, state.mem.ioamhram.get()[0x111],
|
||||
state.spu.ch1.nr4, state.spu.cycleCounter);
|
||||
envelopeUnit_.loadState(state.spu.ch1.env, state.mem.ioamhram.get()[0x112],
|
||||
state.spu.cycleCounter);
|
||||
lengthCounter_.loadState(state.spu.ch1.lcounter, state.spu.cycleCounter);
|
||||
|
||||
cycleCounter = state.spu.cycleCounter;
|
||||
nr4 = state.spu.ch1.nr4;
|
||||
master = state.spu.ch1.master;
|
||||
cycleCounter_ = state.spu.cycleCounter;
|
||||
nr4_ = state.spu.ch1.nr4;
|
||||
master_ = state.spu.ch1.master;
|
||||
}
|
||||
|
||||
void Channel1::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
|
||||
const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
|
||||
const unsigned long outLow = outBase * (0 - 15ul);
|
||||
const unsigned long endCycles = cycleCounter + cycles;
|
||||
void Channel1::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) {
|
||||
unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0;
|
||||
unsigned long const outLow = outBase * (0 - 15ul);
|
||||
unsigned long const endCycles = cycleCounter_ + cycles;
|
||||
|
||||
for (;;) {
|
||||
const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
|
||||
const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
|
||||
unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
|
||||
unsigned long const outHigh = master_
|
||||
? outBase * (envelopeUnit_.getVolume() * 2 - 15ul)
|
||||
: outLow;
|
||||
unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), endCycles);
|
||||
unsigned long out = dutyUnit_.isHighState() ? outHigh : outLow;
|
||||
|
||||
while (dutyUnit.getCounter() <= nextMajorEvent) {
|
||||
*buf = out - prevOut;
|
||||
prevOut = out;
|
||||
buf += dutyUnit.getCounter() - cycleCounter;
|
||||
cycleCounter = dutyUnit.getCounter();
|
||||
while (dutyUnit_.counter() <= nextMajorEvent) {
|
||||
*buf = out - prevOut_;
|
||||
prevOut_ = out;
|
||||
buf += dutyUnit_.counter() - cycleCounter_;
|
||||
cycleCounter_ = dutyUnit_.counter();
|
||||
|
||||
dutyUnit.event();
|
||||
out = dutyUnit.isHighState() ? outHigh : outLow;
|
||||
dutyUnit_.event();
|
||||
out = dutyUnit_.isHighState() ? outHigh : outLow;
|
||||
}
|
||||
|
||||
if (cycleCounter < nextMajorEvent) {
|
||||
*buf = out - prevOut;
|
||||
prevOut = out;
|
||||
buf += nextMajorEvent - cycleCounter;
|
||||
cycleCounter = nextMajorEvent;
|
||||
if (cycleCounter_ < nextMajorEvent) {
|
||||
*buf = out - prevOut_;
|
||||
prevOut_ = out;
|
||||
buf += nextMajorEvent - cycleCounter_;
|
||||
cycleCounter_ = nextMajorEvent;
|
||||
}
|
||||
|
||||
if (nextEventUnit->getCounter() == nextMajorEvent) {
|
||||
nextEventUnit->event();
|
||||
if (nextEventUnit_->counter() == nextMajorEvent) {
|
||||
nextEventUnit_->event();
|
||||
setEvent();
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (cycleCounter & SoundUnit::COUNTER_MAX) {
|
||||
dutyUnit.resetCounters(cycleCounter);
|
||||
lengthCounter.resetCounters(cycleCounter);
|
||||
envelopeUnit.resetCounters(cycleCounter);
|
||||
sweepUnit.resetCounters(cycleCounter);
|
||||
|
||||
cycleCounter -= SoundUnit::COUNTER_MAX;
|
||||
if (cycleCounter_ >= SoundUnit::counter_max) {
|
||||
dutyUnit_.resetCounters(cycleCounter_);
|
||||
lengthCounter_.resetCounters(cycleCounter_);
|
||||
envelopeUnit_.resetCounters(cycleCounter_);
|
||||
sweepUnit_.resetCounters(cycleCounter_);
|
||||
cycleCounter_ -= SoundUnit::counter_max;
|
||||
}
|
||||
}
|
||||
|
||||
SYNCFUNC(Channel1)
|
||||
{
|
||||
SSS(lengthCounter);
|
||||
SSS(dutyUnit);
|
||||
SSS(envelopeUnit);
|
||||
SSS(sweepUnit);
|
||||
SSS(lengthCounter_);
|
||||
SSS(dutyUnit_);
|
||||
SSS(envelopeUnit_);
|
||||
SSS(sweepUnit_);
|
||||
|
||||
EBS(nextEventUnit, 0);
|
||||
EVS(nextEventUnit, &dutyUnit, 1);
|
||||
EVS(nextEventUnit, &sweepUnit, 2);
|
||||
EVS(nextEventUnit, &envelopeUnit, 3);
|
||||
EVS(nextEventUnit, &lengthCounter, 4);
|
||||
EES(nextEventUnit, NULL);
|
||||
EBS(nextEventUnit_, 0);
|
||||
EVS(nextEventUnit_, &dutyUnit_, 1);
|
||||
EVS(nextEventUnit_, &sweepUnit_, 2);
|
||||
EVS(nextEventUnit_, &envelopeUnit_, 3);
|
||||
EVS(nextEventUnit_, &lengthCounter_, 4);
|
||||
EES(nextEventUnit_, NULL);
|
||||
|
||||
NSS(cycleCounter);
|
||||
NSS(soMask);
|
||||
NSS(prevOut);
|
||||
NSS(cycleCounter_);
|
||||
NSS(soMask_);
|
||||
NSS(prevOut_);
|
||||
|
||||
NSS(nr4);
|
||||
NSS(master);
|
||||
NSS(nr4_);
|
||||
NSS(master_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef SOUND_CHANNEL1_H
|
||||
#define SOUND_CHANNEL1_H
|
||||
|
||||
#include "gbint.h"
|
||||
#include "master_disabler.h"
|
||||
#include "length_counter.h"
|
||||
#include "duty_unit.h"
|
||||
#include "envelope_unit.h"
|
||||
#include "gbint.h"
|
||||
#include "length_counter.h"
|
||||
#include "master_disabler.h"
|
||||
#include "static_output_tester.h"
|
||||
#include "newstate.h"
|
||||
|
||||
|
@ -32,46 +32,6 @@ namespace gambatte {
|
|||
struct SaveState;
|
||||
|
||||
class Channel1 {
|
||||
class SweepUnit : public SoundUnit {
|
||||
MasterDisabler &disableMaster;
|
||||
DutyUnit &dutyUnit;
|
||||
unsigned short shadow;
|
||||
unsigned char nr0;
|
||||
bool negging;
|
||||
|
||||
unsigned calcFreq();
|
||||
|
||||
public:
|
||||
SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit);
|
||||
void event();
|
||||
void nr0Change(unsigned newNr0);
|
||||
void nr4Init(unsigned long cycleCounter);
|
||||
void reset();
|
||||
void loadState(const SaveState &state);
|
||||
|
||||
template<bool isReader>void SyncState(NewState *ns);
|
||||
};
|
||||
|
||||
friend class StaticOutputTester<Channel1,DutyUnit>;
|
||||
|
||||
StaticOutputTester<Channel1,DutyUnit> staticOutputTest;
|
||||
DutyMasterDisabler disableMaster;
|
||||
LengthCounter lengthCounter;
|
||||
DutyUnit dutyUnit;
|
||||
EnvelopeUnit envelopeUnit;
|
||||
SweepUnit sweepUnit;
|
||||
|
||||
SoundUnit *nextEventUnit;
|
||||
|
||||
unsigned long cycleCounter;
|
||||
unsigned long soMask;
|
||||
unsigned long prevOut;
|
||||
|
||||
unsigned char nr4;
|
||||
bool master;
|
||||
|
||||
void setEvent();
|
||||
|
||||
public:
|
||||
Channel1();
|
||||
void setNr0(unsigned data);
|
||||
|
@ -79,16 +39,56 @@ public:
|
|||
void setNr2(unsigned data);
|
||||
void setNr3(unsigned data);
|
||||
void setNr4(unsigned data);
|
||||
|
||||
void setSo(unsigned long soMask);
|
||||
bool isActive() const { return master; }
|
||||
|
||||
bool isActive() const { return master_; }
|
||||
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
|
||||
|
||||
void reset();
|
||||
void init(bool cgb);
|
||||
void loadState(const SaveState &state);
|
||||
void loadState(SaveState const &state);
|
||||
|
||||
private:
|
||||
class SweepUnit : public SoundUnit {
|
||||
public:
|
||||
SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit);
|
||||
virtual void event();
|
||||
void nr0Change(unsigned newNr0);
|
||||
void nr4Init(unsigned long cycleCounter);
|
||||
void reset();
|
||||
void init(bool cgb) { cgb_ = cgb; }
|
||||
void loadState(SaveState const &state);
|
||||
|
||||
private:
|
||||
MasterDisabler &disableMaster_;
|
||||
DutyUnit &dutyUnit_;
|
||||
unsigned short shadow_;
|
||||
unsigned char nr0_;
|
||||
bool negging_;
|
||||
bool cgb_;
|
||||
|
||||
unsigned calcFreq();
|
||||
|
||||
public:
|
||||
template<bool isReader>void SyncState(NewState *ns);
|
||||
};
|
||||
|
||||
friend class StaticOutputTester<Channel1, DutyUnit>;
|
||||
|
||||
StaticOutputTester<Channel1, DutyUnit> staticOutputTest_;
|
||||
DutyMasterDisabler disableMaster_;
|
||||
LengthCounter lengthCounter_;
|
||||
DutyUnit dutyUnit_;
|
||||
EnvelopeUnit envelopeUnit_;
|
||||
SweepUnit sweepUnit_;
|
||||
SoundUnit *nextEventUnit_;
|
||||
unsigned long cycleCounter_;
|
||||
unsigned long soMask_;
|
||||
unsigned long prevOut_;
|
||||
unsigned char nr4_;
|
||||
bool master_;
|
||||
|
||||
void setEvent();
|
||||
|
||||
public:
|
||||
template<bool isReader>void SyncState(NewState *ns);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,176 +1,171 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include "channel2.h"
|
||||
#include "../savestate.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace gambatte {
|
||||
|
||||
Channel2::Channel2() :
|
||||
staticOutputTest(*this, dutyUnit),
|
||||
disableMaster(master, dutyUnit),
|
||||
lengthCounter(disableMaster, 0x3F),
|
||||
envelopeUnit(staticOutputTest),
|
||||
cycleCounter(0),
|
||||
soMask(0),
|
||||
prevOut(0),
|
||||
nr4(0),
|
||||
master(false)
|
||||
Channel2::Channel2()
|
||||
: staticOutputTest_(*this, dutyUnit_)
|
||||
, disableMaster_(master_, dutyUnit_)
|
||||
, lengthCounter_(disableMaster_, 0x3F)
|
||||
, envelopeUnit_(staticOutputTest_)
|
||||
, cycleCounter_(0)
|
||||
, soMask_(0)
|
||||
, prevOut_(0)
|
||||
, nr4_(0)
|
||||
, master_(false)
|
||||
{
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel2::setEvent() {
|
||||
// nextEventUnit = &dutyUnit;
|
||||
// if (envelopeUnit.getCounter() < nextEventUnit->getCounter())
|
||||
nextEventUnit = &envelopeUnit;
|
||||
if (lengthCounter.getCounter() < nextEventUnit->getCounter())
|
||||
nextEventUnit = &lengthCounter;
|
||||
nextEventUnit = &envelopeUnit_;
|
||||
if (lengthCounter_.counter() < nextEventUnit->counter())
|
||||
nextEventUnit = &lengthCounter_;
|
||||
}
|
||||
|
||||
void Channel2::setNr1(const unsigned data) {
|
||||
lengthCounter.nr1Change(data, nr4, cycleCounter);
|
||||
dutyUnit.nr1Change(data, cycleCounter);
|
||||
|
||||
void Channel2::setNr1(unsigned data) {
|
||||
lengthCounter_.nr1Change(data, nr4_, cycleCounter_);
|
||||
dutyUnit_.nr1Change(data, cycleCounter_);
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel2::setNr2(const unsigned data) {
|
||||
if (envelopeUnit.nr2Change(data))
|
||||
disableMaster();
|
||||
void Channel2::setNr2(unsigned data) {
|
||||
if (envelopeUnit_.nr2Change(data))
|
||||
disableMaster_();
|
||||
else
|
||||
staticOutputTest(cycleCounter);
|
||||
staticOutputTest_(cycleCounter_);
|
||||
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel2::setNr3(const unsigned data) {
|
||||
dutyUnit.nr3Change(data, cycleCounter);
|
||||
void Channel2::setNr3(unsigned data) {
|
||||
dutyUnit_.nr3Change(data, cycleCounter_);
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel2::setNr4(const unsigned data) {
|
||||
lengthCounter.nr4Change(nr4, data, cycleCounter);
|
||||
void Channel2::setNr4(unsigned const data) {
|
||||
lengthCounter_.nr4Change(nr4_, data, cycleCounter_);
|
||||
nr4_ = data;
|
||||
dutyUnit_.nr4Change(data, cycleCounter_, master_);
|
||||
|
||||
nr4 = data;
|
||||
|
||||
if (data & 0x80) { //init-bit
|
||||
nr4 &= 0x7F;
|
||||
master = !envelopeUnit.nr4Init(cycleCounter);
|
||||
staticOutputTest(cycleCounter);
|
||||
if (data & 0x80) { // init-bit
|
||||
nr4_ &= 0x7F;
|
||||
master_ = !envelopeUnit_.nr4Init(cycleCounter_);
|
||||
staticOutputTest_(cycleCounter_);
|
||||
}
|
||||
|
||||
dutyUnit.nr4Change(data, cycleCounter);
|
||||
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel2::setSo(const unsigned long soMask) {
|
||||
this->soMask = soMask;
|
||||
staticOutputTest(cycleCounter);
|
||||
void Channel2::setSo(unsigned long soMask) {
|
||||
soMask_ = soMask;
|
||||
staticOutputTest_(cycleCounter_);
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel2::reset() {
|
||||
cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position.
|
||||
|
||||
// lengthCounter.reset();
|
||||
dutyUnit.reset();
|
||||
envelopeUnit.reset();
|
||||
// cycleCounter >> 12 & 7 represents the frame sequencer position.
|
||||
cycleCounter_ &= 0xFFF;
|
||||
cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000;
|
||||
|
||||
dutyUnit_.reset();
|
||||
envelopeUnit_.reset();
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel2::init(const bool cgb) {
|
||||
lengthCounter.init(cgb);
|
||||
void Channel2::loadState(SaveState const &state) {
|
||||
dutyUnit_.loadState(state.spu.ch2.duty, state.mem.ioamhram.get()[0x116],
|
||||
state.spu.ch2.nr4, state.spu.cycleCounter);
|
||||
envelopeUnit_.loadState(state.spu.ch2.env, state.mem.ioamhram.get()[0x117],
|
||||
state.spu.cycleCounter);
|
||||
lengthCounter_.loadState(state.spu.ch2.lcounter, state.spu.cycleCounter);
|
||||
|
||||
cycleCounter_ = state.spu.cycleCounter;
|
||||
nr4_ = state.spu.ch2.nr4;
|
||||
master_ = state.spu.ch2.master;
|
||||
}
|
||||
|
||||
void Channel2::loadState(const SaveState &state) {
|
||||
dutyUnit.loadState(state.spu.ch2.duty, state.mem.ioamhram.get()[0x116], state.spu.ch2.nr4,state.spu.cycleCounter);
|
||||
envelopeUnit.loadState(state.spu.ch2.env, state.mem.ioamhram.get()[0x117], state.spu.cycleCounter);
|
||||
lengthCounter.loadState(state.spu.ch2.lcounter, state.spu.cycleCounter);
|
||||
|
||||
cycleCounter = state.spu.cycleCounter;
|
||||
nr4 = state.spu.ch2.nr4;
|
||||
master = state.spu.ch2.master;
|
||||
}
|
||||
|
||||
void Channel2::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
|
||||
const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
|
||||
const unsigned long outLow = outBase * (0 - 15ul);
|
||||
const unsigned long endCycles = cycleCounter + cycles;
|
||||
void Channel2::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) {
|
||||
unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0;
|
||||
unsigned long const outLow = outBase * (0 - 15ul);
|
||||
unsigned long const endCycles = cycleCounter_ + cycles;
|
||||
|
||||
for (;;) {
|
||||
const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
|
||||
const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
|
||||
unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
|
||||
unsigned long const outHigh = master_
|
||||
? outBase * (envelopeUnit_.getVolume() * 2 - 15ul)
|
||||
: outLow;
|
||||
unsigned long const nextMajorEvent = std::min(nextEventUnit->counter(), endCycles);
|
||||
unsigned long out = dutyUnit_.isHighState() ? outHigh : outLow;
|
||||
|
||||
while (dutyUnit.getCounter() <= nextMajorEvent) {
|
||||
*buf += out - prevOut;
|
||||
prevOut = out;
|
||||
buf += dutyUnit.getCounter() - cycleCounter;
|
||||
cycleCounter = dutyUnit.getCounter();
|
||||
while (dutyUnit_.counter() <= nextMajorEvent) {
|
||||
*buf += out - prevOut_;
|
||||
prevOut_ = out;
|
||||
buf += dutyUnit_.counter() - cycleCounter_;
|
||||
cycleCounter_ = dutyUnit_.counter();
|
||||
|
||||
dutyUnit.event();
|
||||
out = dutyUnit.isHighState() ? outHigh : outLow;
|
||||
dutyUnit_.event();
|
||||
out = dutyUnit_.isHighState() ? outHigh : outLow;
|
||||
}
|
||||
|
||||
if (cycleCounter < nextMajorEvent) {
|
||||
*buf += out - prevOut;
|
||||
prevOut = out;
|
||||
buf += nextMajorEvent - cycleCounter;
|
||||
cycleCounter = nextMajorEvent;
|
||||
if (cycleCounter_ < nextMajorEvent) {
|
||||
*buf += out - prevOut_;
|
||||
prevOut_ = out;
|
||||
buf += nextMajorEvent - cycleCounter_;
|
||||
cycleCounter_ = nextMajorEvent;
|
||||
}
|
||||
|
||||
if (nextEventUnit->getCounter() == nextMajorEvent) {
|
||||
if (nextEventUnit->counter() == nextMajorEvent) {
|
||||
nextEventUnit->event();
|
||||
setEvent();
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (cycleCounter & SoundUnit::COUNTER_MAX) {
|
||||
dutyUnit.resetCounters(cycleCounter);
|
||||
lengthCounter.resetCounters(cycleCounter);
|
||||
envelopeUnit.resetCounters(cycleCounter);
|
||||
|
||||
cycleCounter -= SoundUnit::COUNTER_MAX;
|
||||
if (cycleCounter_ >= SoundUnit::counter_max) {
|
||||
dutyUnit_.resetCounters(cycleCounter_);
|
||||
lengthCounter_.resetCounters(cycleCounter_);
|
||||
envelopeUnit_.resetCounters(cycleCounter_);
|
||||
cycleCounter_ -= SoundUnit::counter_max;
|
||||
}
|
||||
}
|
||||
|
||||
SYNCFUNC(Channel2)
|
||||
{
|
||||
SSS(lengthCounter);
|
||||
SSS(dutyUnit);
|
||||
SSS(envelopeUnit);
|
||||
SSS(lengthCounter_);
|
||||
SSS(dutyUnit_);
|
||||
SSS(envelopeUnit_);
|
||||
|
||||
EBS(nextEventUnit, 0);
|
||||
EVS(nextEventUnit, &dutyUnit, 1);
|
||||
EVS(nextEventUnit, &envelopeUnit, 2);
|
||||
EVS(nextEventUnit, &lengthCounter, 3);
|
||||
EVS(nextEventUnit, &dutyUnit_, 1);
|
||||
EVS(nextEventUnit, &envelopeUnit_, 2);
|
||||
EVS(nextEventUnit, &lengthCounter_, 3);
|
||||
EES(nextEventUnit, NULL);
|
||||
|
||||
NSS(cycleCounter);
|
||||
NSS(soMask);
|
||||
NSS(prevOut);
|
||||
NSS(cycleCounter_);
|
||||
NSS(soMask_);
|
||||
NSS(prevOut_);
|
||||
|
||||
NSS(nr4);
|
||||
NSS(master);
|
||||
NSS(nr4_);
|
||||
NSS(master_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef SOUND_CHANNEL2_H
|
||||
#define SOUND_CHANNEL2_H
|
||||
|
||||
#include "gbint.h"
|
||||
#include "length_counter.h"
|
||||
#include "duty_unit.h"
|
||||
#include "envelope_unit.h"
|
||||
#include "gbint.h"
|
||||
#include "length_counter.h"
|
||||
#include "static_output_tester.h"
|
||||
#include "newstate.h"
|
||||
|
||||
|
@ -31,42 +31,36 @@ namespace gambatte {
|
|||
struct SaveState;
|
||||
|
||||
class Channel2 {
|
||||
friend class StaticOutputTester<Channel2,DutyUnit>;
|
||||
|
||||
StaticOutputTester<Channel2,DutyUnit> staticOutputTest;
|
||||
DutyMasterDisabler disableMaster;
|
||||
LengthCounter lengthCounter;
|
||||
DutyUnit dutyUnit;
|
||||
EnvelopeUnit envelopeUnit;
|
||||
|
||||
SoundUnit *nextEventUnit;
|
||||
|
||||
unsigned long cycleCounter;
|
||||
unsigned long soMask;
|
||||
unsigned long prevOut;
|
||||
|
||||
unsigned char nr4;
|
||||
bool master;
|
||||
|
||||
void setEvent();
|
||||
|
||||
public:
|
||||
Channel2();
|
||||
void setNr1(unsigned data);
|
||||
void setNr2(unsigned data);
|
||||
void setNr3(unsigned data);
|
||||
void setNr4(unsigned data);
|
||||
|
||||
void setSo(unsigned long soMask);
|
||||
// void deactivate() { disableMaster(); setEvent(); }
|
||||
bool isActive() const { return master; }
|
||||
|
||||
bool isActive() const { return master_; }
|
||||
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
|
||||
|
||||
void reset();
|
||||
void init(bool cgb);
|
||||
void loadState(const SaveState &state);
|
||||
void loadState(SaveState const &state);
|
||||
|
||||
private:
|
||||
friend class StaticOutputTester<Channel2, DutyUnit>;
|
||||
|
||||
StaticOutputTester<Channel2, DutyUnit> staticOutputTest_;
|
||||
DutyMasterDisabler disableMaster_;
|
||||
LengthCounter lengthCounter_;
|
||||
DutyUnit dutyUnit_;
|
||||
EnvelopeUnit envelopeUnit_;
|
||||
SoundUnit *nextEventUnit;
|
||||
unsigned long cycleCounter_;
|
||||
unsigned long soMask_;
|
||||
unsigned long prevOut_;
|
||||
unsigned char nr4_;
|
||||
bool master_;
|
||||
|
||||
void setEvent();
|
||||
|
||||
public:
|
||||
template<bool isReader>void SyncState(NewState *ns);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include "channel3.h"
|
||||
#include "../savestate.h"
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
static inline unsigned toPeriod(const unsigned nr3, const unsigned nr4) {
|
||||
return 0x800 - ((nr4 << 8 & 0x700) | nr3);
|
||||
|
@ -27,193 +27,196 @@ static inline unsigned toPeriod(const unsigned nr3, const unsigned nr4) {
|
|||
|
||||
namespace gambatte {
|
||||
|
||||
Channel3::Channel3() :
|
||||
disableMaster(master, waveCounter),
|
||||
lengthCounter(disableMaster, 0xFF),
|
||||
cycleCounter(0),
|
||||
soMask(0),
|
||||
prevOut(0),
|
||||
waveCounter(SoundUnit::COUNTER_DISABLED),
|
||||
lastReadTime(0),
|
||||
nr0(0),
|
||||
nr3(0),
|
||||
nr4(0),
|
||||
wavePos(0),
|
||||
rShift(4),
|
||||
sampleBuf(0),
|
||||
master(false),
|
||||
cgb(false)
|
||||
{}
|
||||
Channel3::Channel3()
|
||||
: disableMaster_(master_, waveCounter_)
|
||||
, lengthCounter_(disableMaster_, 0xFF)
|
||||
, cycleCounter_(0)
|
||||
, soMask_(0)
|
||||
, prevOut_(0)
|
||||
, waveCounter_(SoundUnit::counter_disabled)
|
||||
, lastReadTime_(0)
|
||||
, nr0_(0)
|
||||
, nr3_(0)
|
||||
, nr4_(0)
|
||||
, wavePos_(0)
|
||||
, rshift_(4)
|
||||
, sampleBuf_(0)
|
||||
, master_(false)
|
||||
, cgb_(false)
|
||||
{
|
||||
}
|
||||
|
||||
void Channel3::setNr0(const unsigned data) {
|
||||
nr0 = data & 0x80;
|
||||
void Channel3::setNr0(unsigned data) {
|
||||
nr0_ = data & 0x80;
|
||||
|
||||
if (!(data & 0x80))
|
||||
disableMaster();
|
||||
disableMaster_();
|
||||
}
|
||||
|
||||
void Channel3::setNr2(const unsigned data) {
|
||||
rShift = (data >> 5 & 3U) - 1;
|
||||
|
||||
if (rShift > 3)
|
||||
rShift = 4;
|
||||
void Channel3::setNr2(unsigned data) {
|
||||
rshift_ = (data >> 5 & 3U) - 1;
|
||||
if (rshift_ > 3)
|
||||
rshift_ = 4;
|
||||
}
|
||||
|
||||
void Channel3::setNr4(const unsigned data) {
|
||||
lengthCounter.nr4Change(nr4, data, cycleCounter);
|
||||
void Channel3::setNr4(unsigned const data) {
|
||||
lengthCounter_.nr4Change(nr4_, data, cycleCounter_);
|
||||
nr4_ = data & 0x7F;
|
||||
|
||||
nr4 = data & 0x7F;
|
||||
|
||||
if (data & nr0/* & 0x80*/) {
|
||||
if (!cgb && waveCounter == cycleCounter + 1) {
|
||||
const unsigned pos = ((wavePos + 1) & 0x1F) >> 1;
|
||||
if (data & nr0_/* & 0x80*/) {
|
||||
if (!cgb_ && waveCounter_ == cycleCounter_ + 1) {
|
||||
unsigned const pos = ((wavePos_ + 1) & 0x1F) >> 1;
|
||||
|
||||
if (pos < 4)
|
||||
waveRam[0] = waveRam[pos];
|
||||
waveRam_[0] = waveRam_[pos];
|
||||
else
|
||||
std::memcpy(waveRam, waveRam + (pos & ~3), 4);
|
||||
std::memcpy(waveRam_, waveRam_ + (pos & ~3), 4);
|
||||
}
|
||||
|
||||
master = true;
|
||||
wavePos = 0;
|
||||
lastReadTime = waveCounter = cycleCounter + toPeriod(nr3, data) + 3;
|
||||
master_ = true;
|
||||
wavePos_ = 0;
|
||||
lastReadTime_ = waveCounter_ = cycleCounter_ + toPeriod(nr3_, data) + 3;
|
||||
}
|
||||
}
|
||||
|
||||
void Channel3::setSo(const unsigned long soMask) {
|
||||
this->soMask = soMask;
|
||||
void Channel3::setSo(unsigned long soMask) {
|
||||
soMask_ = soMask;
|
||||
}
|
||||
|
||||
void Channel3::reset() {
|
||||
cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position.
|
||||
// cycleCounter >> 12 & 7 represents the frame sequencer position.
|
||||
cycleCounter_ &= 0xFFF;
|
||||
cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000;
|
||||
|
||||
// lengthCounter.reset();
|
||||
sampleBuf = 0;
|
||||
sampleBuf_ = 0;
|
||||
}
|
||||
|
||||
void Channel3::init(const bool cgb) {
|
||||
this->cgb = cgb;
|
||||
lengthCounter.init(cgb);
|
||||
void Channel3::init(bool cgb) {
|
||||
cgb_ = cgb;
|
||||
}
|
||||
|
||||
void Channel3::setStatePtrs(SaveState &state) {
|
||||
state.spu.ch3.waveRam.set(waveRam, sizeof waveRam);
|
||||
state.spu.ch3.waveRam.set(waveRam_, sizeof waveRam_);
|
||||
}
|
||||
|
||||
void Channel3::loadState(const SaveState &state) {
|
||||
lengthCounter.loadState(state.spu.ch3.lcounter, state.spu.cycleCounter);
|
||||
void Channel3::loadState(SaveState const &state) {
|
||||
lengthCounter_.loadState(state.spu.ch3.lcounter, state.spu.cycleCounter);
|
||||
|
||||
cycleCounter = state.spu.cycleCounter;
|
||||
waveCounter = std::max(state.spu.ch3.waveCounter, state.spu.cycleCounter);
|
||||
lastReadTime = state.spu.ch3.lastReadTime;
|
||||
nr3 = state.spu.ch3.nr3;
|
||||
nr4 = state.spu.ch3.nr4;
|
||||
wavePos = state.spu.ch3.wavePos & 0x1F;
|
||||
sampleBuf = state.spu.ch3.sampleBuf;
|
||||
master = state.spu.ch3.master;
|
||||
cycleCounter_ = state.spu.cycleCounter;
|
||||
waveCounter_ = std::max(state.spu.ch3.waveCounter, state.spu.cycleCounter);
|
||||
lastReadTime_ = state.spu.ch3.lastReadTime;
|
||||
nr3_ = state.spu.ch3.nr3;
|
||||
nr4_ = state.spu.ch3.nr4;
|
||||
wavePos_ = state.spu.ch3.wavePos & 0x1F;
|
||||
sampleBuf_ = state.spu.ch3.sampleBuf;
|
||||
master_ = state.spu.ch3.master;
|
||||
|
||||
nr0 = state.mem.ioamhram.get()[0x11A] & 0x80;
|
||||
nr0_ = state.mem.ioamhram.get()[0x11A] & 0x80;
|
||||
setNr2(state.mem.ioamhram.get()[0x11C]);
|
||||
}
|
||||
|
||||
void Channel3::updateWaveCounter(const unsigned long cc) {
|
||||
if (cc >= waveCounter) {
|
||||
const unsigned period = toPeriod(nr3, nr4);
|
||||
const unsigned long periods = (cc - waveCounter) / period;
|
||||
void Channel3::updateWaveCounter(unsigned long const cc) {
|
||||
if (cc >= waveCounter_) {
|
||||
unsigned const period = toPeriod(nr3_, nr4_);
|
||||
unsigned long const periods = (cc - waveCounter_) / period;
|
||||
|
||||
lastReadTime = waveCounter + periods * period;
|
||||
waveCounter = lastReadTime + period;
|
||||
lastReadTime_ = waveCounter_ + periods * period;
|
||||
waveCounter_ = lastReadTime_ + period;
|
||||
|
||||
wavePos += periods + 1;
|
||||
wavePos &= 0x1F;
|
||||
wavePos_ += periods + 1;
|
||||
wavePos_ &= 0x1F;
|
||||
|
||||
sampleBuf = waveRam[wavePos >> 1];
|
||||
sampleBuf_ = waveRam_[wavePos_ >> 1];
|
||||
}
|
||||
}
|
||||
|
||||
void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
|
||||
const unsigned long outBase = (nr0/* & 0x80*/) ? soBaseVol & soMask : 0;
|
||||
void Channel3::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) {
|
||||
unsigned long const outBase = nr0_/* & 0x80*/ ? soBaseVol & soMask_ : 0;
|
||||
|
||||
if (outBase && rShift != 4) {
|
||||
const unsigned long endCycles = cycleCounter + cycles;
|
||||
if (outBase && rshift_ != 4) {
|
||||
unsigned long const endCycles = cycleCounter_ + cycles;
|
||||
|
||||
for (;;) {
|
||||
const unsigned long nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles;
|
||||
unsigned long out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul);
|
||||
unsigned long const nextMajorEvent =
|
||||
std::min(lengthCounter_.counter(), endCycles);
|
||||
unsigned long out = master_
|
||||
? ((sampleBuf_ >> (~wavePos_ << 2 & 4) & 0xF) >> rshift_) * 2 - 15ul
|
||||
: 0 - 15ul;
|
||||
out *= outBase;
|
||||
|
||||
while (waveCounter <= nextMajorEvent) {
|
||||
*buf += out - prevOut;
|
||||
prevOut = out;
|
||||
buf += waveCounter - cycleCounter;
|
||||
cycleCounter = waveCounter;
|
||||
while (waveCounter_ <= nextMajorEvent) {
|
||||
*buf += out - prevOut_;
|
||||
prevOut_ = out;
|
||||
buf += waveCounter_ - cycleCounter_;
|
||||
cycleCounter_ = waveCounter_;
|
||||
|
||||
lastReadTime = waveCounter;
|
||||
waveCounter += toPeriod(nr3, nr4);
|
||||
++wavePos;
|
||||
wavePos &= 0x1F;
|
||||
sampleBuf = waveRam[wavePos >> 1];
|
||||
out = outBase * (/*master ? */((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul/* : 0 - 15ul*/);
|
||||
lastReadTime_ = waveCounter_;
|
||||
waveCounter_ += toPeriod(nr3_, nr4_);
|
||||
++wavePos_;
|
||||
wavePos_ &= 0x1F;
|
||||
sampleBuf_ = waveRam_[wavePos_ >> 1];
|
||||
out = ((sampleBuf_ >> (~wavePos_ << 2 & 4) & 0xF) >> rshift_) * 2 - 15ul;
|
||||
out *= outBase;
|
||||
}
|
||||
|
||||
if (cycleCounter < nextMajorEvent) {
|
||||
*buf += out - prevOut;
|
||||
prevOut = out;
|
||||
buf += nextMajorEvent - cycleCounter;
|
||||
cycleCounter = nextMajorEvent;
|
||||
if (cycleCounter_ < nextMajorEvent) {
|
||||
*buf += out - prevOut_;
|
||||
prevOut_ = out;
|
||||
buf += nextMajorEvent - cycleCounter_;
|
||||
cycleCounter_ = nextMajorEvent;
|
||||
}
|
||||
|
||||
if (lengthCounter.getCounter() == nextMajorEvent) {
|
||||
lengthCounter.event();
|
||||
if (lengthCounter_.counter() == nextMajorEvent) {
|
||||
lengthCounter_.event();
|
||||
} else
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
const unsigned long out = outBase * (0 - 15ul);
|
||||
*buf += out - prevOut;
|
||||
prevOut = out;
|
||||
unsigned long const out = outBase * (0 - 15ul);
|
||||
*buf += out - prevOut_;
|
||||
prevOut_ = out;
|
||||
cycleCounter_ += cycles;
|
||||
|
||||
cycleCounter += cycles;
|
||||
|
||||
while (lengthCounter.getCounter() <= cycleCounter) {
|
||||
updateWaveCounter(lengthCounter.getCounter());
|
||||
lengthCounter.event();
|
||||
while (lengthCounter_.counter() <= cycleCounter_) {
|
||||
updateWaveCounter(lengthCounter_.counter());
|
||||
lengthCounter_.event();
|
||||
}
|
||||
|
||||
updateWaveCounter(cycleCounter);
|
||||
updateWaveCounter(cycleCounter_);
|
||||
}
|
||||
|
||||
if (cycleCounter & SoundUnit::COUNTER_MAX) {
|
||||
lengthCounter.resetCounters(cycleCounter);
|
||||
if (cycleCounter_ >= SoundUnit::counter_max) {
|
||||
lengthCounter_.resetCounters(cycleCounter_);
|
||||
|
||||
if (waveCounter != SoundUnit::COUNTER_DISABLED)
|
||||
waveCounter -= SoundUnit::COUNTER_MAX;
|
||||
if (waveCounter_ != SoundUnit::counter_disabled)
|
||||
waveCounter_ -= SoundUnit::counter_max;
|
||||
|
||||
lastReadTime -= SoundUnit::COUNTER_MAX;
|
||||
cycleCounter -= SoundUnit::COUNTER_MAX;
|
||||
lastReadTime_ -= SoundUnit::counter_max;
|
||||
cycleCounter_ -= SoundUnit::counter_max;
|
||||
}
|
||||
}
|
||||
|
||||
SYNCFUNC(Channel3)
|
||||
{
|
||||
NSS(waveRam);
|
||||
NSS(waveRam_);
|
||||
|
||||
SSS(lengthCounter);
|
||||
SSS(lengthCounter_);
|
||||
|
||||
NSS(cycleCounter);
|
||||
NSS(soMask);
|
||||
NSS(prevOut);
|
||||
NSS(waveCounter);
|
||||
NSS(lastReadTime);
|
||||
NSS(cycleCounter_);
|
||||
NSS(soMask_);
|
||||
NSS(prevOut_);
|
||||
NSS(waveCounter_);
|
||||
NSS(lastReadTime_);
|
||||
|
||||
NSS(nr0);
|
||||
NSS(nr3);
|
||||
NSS(nr4);
|
||||
NSS(wavePos);
|
||||
NSS(rShift);
|
||||
NSS(sampleBuf);
|
||||
NSS(nr0_);
|
||||
NSS(nr3_);
|
||||
NSS(nr4_);
|
||||
NSS(wavePos_);
|
||||
NSS(rshift_);
|
||||
NSS(sampleBuf_);
|
||||
|
||||
NSS(master);
|
||||
NSS(cgb);
|
||||
NSS(master_);
|
||||
NSS(cgb_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef SOUND_CHANNEL3_H
|
||||
#define SOUND_CHANNEL3_H
|
||||
|
||||
#include "gbint.h"
|
||||
#include "master_disabler.h"
|
||||
#include "length_counter.h"
|
||||
#include "master_disabler.h"
|
||||
#include "newstate.h"
|
||||
|
||||
namespace gambatte {
|
||||
|
@ -29,74 +29,78 @@ namespace gambatte {
|
|||
struct SaveState;
|
||||
|
||||
class Channel3 {
|
||||
class Ch3MasterDisabler : public MasterDisabler {
|
||||
unsigned long &waveCounter;
|
||||
|
||||
public:
|
||||
Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter(wC) {}
|
||||
void operator()() { MasterDisabler::operator()(); waveCounter = SoundUnit::COUNTER_DISABLED; }
|
||||
};
|
||||
|
||||
unsigned char waveRam[0x10];
|
||||
|
||||
Ch3MasterDisabler disableMaster;
|
||||
LengthCounter lengthCounter;
|
||||
|
||||
unsigned long cycleCounter;
|
||||
unsigned long soMask;
|
||||
unsigned long prevOut;
|
||||
unsigned long waveCounter;
|
||||
unsigned long lastReadTime;
|
||||
|
||||
unsigned char nr0;
|
||||
unsigned char nr3;
|
||||
unsigned char nr4;
|
||||
unsigned char wavePos;
|
||||
unsigned char rShift;
|
||||
unsigned char sampleBuf;
|
||||
|
||||
bool master;
|
||||
bool cgb;
|
||||
|
||||
void updateWaveCounter(unsigned long cc);
|
||||
|
||||
public:
|
||||
Channel3();
|
||||
bool isActive() const { return master; }
|
||||
bool isActive() const { return master_; }
|
||||
void reset();
|
||||
void init(bool cgb);
|
||||
void setStatePtrs(SaveState &state);
|
||||
void loadState(const SaveState &state);
|
||||
void setNr0(unsigned data);
|
||||
void setNr1(unsigned data) { lengthCounter.nr1Change(data, nr4, cycleCounter); }
|
||||
void setNr1(unsigned data) { lengthCounter_.nr1Change(data, nr4_, cycleCounter_); }
|
||||
void setNr2(unsigned data);
|
||||
void setNr3(unsigned data) { nr3 = data; }
|
||||
void setNr3(unsigned data) { nr3_ = data; }
|
||||
void setNr4(unsigned data);
|
||||
void setSo(unsigned long soMask);
|
||||
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
|
||||
|
||||
unsigned waveRamRead(unsigned index) const {
|
||||
if (master) {
|
||||
if (!cgb && cycleCounter != lastReadTime)
|
||||
if (master_) {
|
||||
if (!cgb_ && cycleCounter_ != lastReadTime_)
|
||||
return 0xFF;
|
||||
|
||||
index = wavePos >> 1;
|
||||
index = wavePos_ >> 1;
|
||||
}
|
||||
|
||||
return waveRam[index];
|
||||
return waveRam_[index];
|
||||
}
|
||||
|
||||
void waveRamWrite(unsigned index, unsigned data) {
|
||||
if (master) {
|
||||
if (!cgb && cycleCounter != lastReadTime)
|
||||
if (master_) {
|
||||
if (!cgb_ && cycleCounter_ != lastReadTime_)
|
||||
return;
|
||||
|
||||
index = wavePos >> 1;
|
||||
index = wavePos_ >> 1;
|
||||
}
|
||||
|
||||
waveRam[index] = data;
|
||||
waveRam_[index] = data;
|
||||
}
|
||||
|
||||
private:
|
||||
class Ch3MasterDisabler : public MasterDisabler {
|
||||
public:
|
||||
Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter_(wC) {}
|
||||
|
||||
virtual void operator()() {
|
||||
MasterDisabler::operator()();
|
||||
waveCounter_ = SoundUnit::counter_disabled;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned long &waveCounter_;
|
||||
};
|
||||
|
||||
unsigned char waveRam_[0x10];
|
||||
Ch3MasterDisabler disableMaster_;
|
||||
LengthCounter lengthCounter_;
|
||||
unsigned long cycleCounter_;
|
||||
unsigned long soMask_;
|
||||
unsigned long prevOut_;
|
||||
unsigned long waveCounter_;
|
||||
unsigned long lastReadTime_;
|
||||
unsigned char nr0_;
|
||||
unsigned char nr3_;
|
||||
unsigned char nr4_;
|
||||
unsigned char wavePos_;
|
||||
unsigned char rshift_;
|
||||
unsigned char sampleBuf_;
|
||||
bool master_;
|
||||
bool cgb_;
|
||||
|
||||
void updateWaveCounter(unsigned long cc);
|
||||
|
||||
|
||||
public:
|
||||
template<bool isReader>void SyncState(NewState *ns);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include "channel4.h"
|
||||
#include "../savestate.h"
|
||||
#include <algorithm>
|
||||
|
||||
static unsigned long toPeriod(const unsigned nr3) {
|
||||
static unsigned long toPeriod(unsigned const nr3) {
|
||||
unsigned s = (nr3 >> 4) + 3;
|
||||
unsigned r = nr3 & 7;
|
||||
|
||||
|
@ -34,286 +34,242 @@ static unsigned long toPeriod(const unsigned nr3) {
|
|||
|
||||
namespace gambatte {
|
||||
|
||||
Channel4::Lfsr::Lfsr() :
|
||||
backupCounter(COUNTER_DISABLED),
|
||||
reg(0x7FFF),
|
||||
nr3(0),
|
||||
master(false)
|
||||
{}
|
||||
Channel4::Lfsr::Lfsr()
|
||||
: backupCounter_(counter_disabled)
|
||||
, reg_(0x7FFF)
|
||||
, nr3_(0)
|
||||
, master_(false)
|
||||
{
|
||||
}
|
||||
|
||||
void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) {
|
||||
/*if (backupCounter <= cc) {
|
||||
const unsigned long period = toPeriod(nr3);
|
||||
backupCounter = cc - (cc - backupCounter) % period + period;
|
||||
}*/
|
||||
void Channel4::Lfsr::updateBackupCounter(unsigned long const cc) {
|
||||
if (backupCounter_ <= cc) {
|
||||
unsigned long const period = toPeriod(nr3_);
|
||||
unsigned long periods = (cc - backupCounter_) / period + 1;
|
||||
backupCounter_ += periods * period;
|
||||
|
||||
if (backupCounter <= cc) {
|
||||
const unsigned long period = toPeriod(nr3);
|
||||
unsigned long periods = (cc - backupCounter) / period + 1;
|
||||
|
||||
backupCounter += periods * period;
|
||||
|
||||
if (master && nr3 < 0xE0) {
|
||||
if (nr3 & 8) {
|
||||
if (master_ && nr3_ < 0xE0) {
|
||||
if (nr3_ & 8) {
|
||||
while (periods > 6) {
|
||||
const unsigned xored = (reg << 1 ^ reg) & 0x7E;
|
||||
reg = (reg >> 6 & ~0x7E) | xored | xored << 8;
|
||||
unsigned const xored = (reg_ << 1 ^ reg_) & 0x7E;
|
||||
reg_ = (reg_ >> 6 & ~0x7E) | xored | xored << 8;
|
||||
periods -= 6;
|
||||
}
|
||||
|
||||
const unsigned xored = ((reg ^ reg >> 1) << (7 - periods)) & 0x7F;
|
||||
reg = (reg >> periods & ~(0x80 - (0x80 >> periods))) | xored | xored << 8;
|
||||
unsigned const xored = ((reg_ ^ reg_ >> 1) << (7 - periods)) & 0x7F;
|
||||
reg_ = (reg_ >> periods & ~(0x80 - (0x80 >> periods))) | xored | xored << 8;
|
||||
} else {
|
||||
while (periods > 15) {
|
||||
reg = reg ^ reg >> 1;
|
||||
reg_ = reg_ ^ reg_ >> 1;
|
||||
periods -= 15;
|
||||
}
|
||||
|
||||
reg = reg >> periods | (((reg ^ reg >> 1) << (15 - periods)) & 0x7FFF);
|
||||
reg_ = reg_ >> periods | (((reg_ ^ reg_ >> 1) << (15 - periods)) & 0x7FFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Channel4::Lfsr::reviveCounter(const unsigned long cc) {
|
||||
void Channel4::Lfsr::reviveCounter(unsigned long cc) {
|
||||
updateBackupCounter(cc);
|
||||
counter = backupCounter;
|
||||
counter_ = backupCounter_;
|
||||
}
|
||||
|
||||
/*static const unsigned char nextStateDistance[0x40] = {
|
||||
6, 1, 1, 2, 2, 1, 1, 3,
|
||||
3, 1, 1, 2, 2, 1, 1, 4,
|
||||
4, 1, 1, 2, 2, 1, 1, 3,
|
||||
3, 1, 1, 2, 2, 1, 1, 5,
|
||||
5, 1, 1, 2, 2, 1, 1, 3,
|
||||
3, 1, 1, 2, 2, 1, 1, 4,
|
||||
4, 1, 1, 2, 2, 1, 1, 3,
|
||||
3, 1, 1, 2, 2, 1, 1, 6,
|
||||
};*/
|
||||
|
||||
inline void Channel4::Lfsr::event() {
|
||||
if (nr3 < 0xE0) {
|
||||
const unsigned shifted = reg >> 1;
|
||||
const unsigned xored = (reg ^ shifted) & 1;
|
||||
if (nr3_ < 0xE0) {
|
||||
unsigned const shifted = reg_ >> 1;
|
||||
unsigned const xored = (reg_ ^ shifted) & 1;
|
||||
reg_ = shifted | xored << 14;
|
||||
|
||||
reg = shifted | xored << 14;
|
||||
|
||||
if (nr3 & 8)
|
||||
reg = (reg & ~0x40) | xored << 6;
|
||||
if (nr3_ & 8)
|
||||
reg_ = (reg_ & ~0x40) | xored << 6;
|
||||
}
|
||||
|
||||
counter += toPeriod(nr3);
|
||||
backupCounter = counter;
|
||||
|
||||
|
||||
/*if (nr3 < 0xE0) {
|
||||
const unsigned periods = nextStateDistance[reg & 0x3F];
|
||||
const unsigned xored = ((reg ^ reg >> 1) << (7 - periods)) & 0x7F;
|
||||
|
||||
reg = reg >> periods | xored << 8;
|
||||
|
||||
if (nr3 & 8)
|
||||
reg = reg & ~(0x80 - (0x80 >> periods)) | xored;
|
||||
}
|
||||
|
||||
const unsigned long period = toPeriod(nr3);
|
||||
backupCounter = counter + period;
|
||||
counter += period * nextStateDistance[reg & 0x3F];*/
|
||||
counter_ += toPeriod(nr3_);
|
||||
backupCounter_ = counter_;
|
||||
}
|
||||
|
||||
void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) {
|
||||
void Channel4::Lfsr::nr3Change(unsigned newNr3, unsigned long cc) {
|
||||
updateBackupCounter(cc);
|
||||
nr3 = newNr3;
|
||||
|
||||
// if (counter != COUNTER_DISABLED)
|
||||
// counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1);
|
||||
nr3_ = newNr3;
|
||||
}
|
||||
|
||||
void Channel4::Lfsr::nr4Init(unsigned long cc) {
|
||||
disableMaster();
|
||||
updateBackupCounter(cc);
|
||||
master = true;
|
||||
backupCounter += 4;
|
||||
counter = backupCounter;
|
||||
// counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1);
|
||||
master_ = true;
|
||||
backupCounter_ += 4;
|
||||
counter_ = backupCounter_;
|
||||
}
|
||||
|
||||
void Channel4::Lfsr::reset(const unsigned long cc) {
|
||||
nr3 = 0;
|
||||
void Channel4::Lfsr::reset(unsigned long cc) {
|
||||
nr3_ = 0;
|
||||
disableMaster();
|
||||
backupCounter = cc + toPeriod(nr3);
|
||||
backupCounter_ = cc + toPeriod(nr3_);
|
||||
}
|
||||
|
||||
void Channel4::Lfsr::resetCounters(const unsigned long oldCc) {
|
||||
void Channel4::Lfsr::resetCounters(unsigned long oldCc) {
|
||||
updateBackupCounter(oldCc);
|
||||
backupCounter -= COUNTER_MAX;
|
||||
backupCounter_ -= counter_max;
|
||||
SoundUnit::resetCounters(oldCc);
|
||||
}
|
||||
|
||||
void Channel4::Lfsr::loadState(const SaveState &state) {
|
||||
counter = backupCounter = std::max(state.spu.ch4.lfsr.counter, state.spu.cycleCounter);
|
||||
reg = state.spu.ch4.lfsr.reg;
|
||||
master = state.spu.ch4.master;
|
||||
nr3 = state.mem.ioamhram.get()[0x122];
|
||||
void Channel4::Lfsr::loadState(SaveState const &state) {
|
||||
counter_ = backupCounter_ = std::max(state.spu.ch4.lfsr.counter, state.spu.cycleCounter);
|
||||
reg_ = state.spu.ch4.lfsr.reg;
|
||||
master_ = state.spu.ch4.master;
|
||||
nr3_ = state.mem.ioamhram.get()[0x122];
|
||||
}
|
||||
|
||||
template<bool isReader>
|
||||
void Channel4::Lfsr::SyncState(NewState *ns)
|
||||
{
|
||||
NSS(counter);
|
||||
NSS(backupCounter);
|
||||
NSS(reg);
|
||||
NSS(nr3);
|
||||
NSS(master);
|
||||
NSS(counter_);
|
||||
NSS(backupCounter_);
|
||||
NSS(reg_);
|
||||
NSS(nr3_);
|
||||
NSS(master_);
|
||||
}
|
||||
|
||||
Channel4::Channel4() :
|
||||
staticOutputTest(*this, lfsr),
|
||||
disableMaster(master, lfsr),
|
||||
lengthCounter(disableMaster, 0x3F),
|
||||
envelopeUnit(staticOutputTest),
|
||||
cycleCounter(0),
|
||||
soMask(0),
|
||||
prevOut(0),
|
||||
nr4(0),
|
||||
master(false)
|
||||
Channel4::Channel4()
|
||||
: staticOutputTest_(*this, lfsr_)
|
||||
, disableMaster_(master_, lfsr_)
|
||||
, lengthCounter_(disableMaster_, 0x3F)
|
||||
, envelopeUnit_(staticOutputTest_)
|
||||
, nextEventUnit_(0)
|
||||
, cycleCounter_(0)
|
||||
, soMask_(0)
|
||||
, prevOut_(0)
|
||||
, nr4_(0)
|
||||
, master_(false)
|
||||
{
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel4::setEvent() {
|
||||
// nextEventUnit = &lfsr;
|
||||
// if (envelopeUnit.getCounter() < nextEventUnit->getCounter())
|
||||
nextEventUnit = &envelopeUnit;
|
||||
if (lengthCounter.getCounter() < nextEventUnit->getCounter())
|
||||
nextEventUnit = &lengthCounter;
|
||||
nextEventUnit_ = &envelopeUnit_;
|
||||
if (lengthCounter_.counter() < nextEventUnit_->counter())
|
||||
nextEventUnit_ = &lengthCounter_;
|
||||
}
|
||||
|
||||
void Channel4::setNr1(const unsigned data) {
|
||||
lengthCounter.nr1Change(data, nr4, cycleCounter);
|
||||
|
||||
void Channel4::setNr1(unsigned data) {
|
||||
lengthCounter_.nr1Change(data, nr4_, cycleCounter_);
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel4::setNr2(const unsigned data) {
|
||||
if (envelopeUnit.nr2Change(data))
|
||||
disableMaster();
|
||||
void Channel4::setNr2(unsigned data) {
|
||||
if (envelopeUnit_.nr2Change(data))
|
||||
disableMaster_();
|
||||
else
|
||||
staticOutputTest(cycleCounter);
|
||||
staticOutputTest_(cycleCounter_);
|
||||
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel4::setNr4(const unsigned data) {
|
||||
lengthCounter.nr4Change(nr4, data, cycleCounter);
|
||||
void Channel4::setNr4(unsigned const data) {
|
||||
lengthCounter_.nr4Change(nr4_, data, cycleCounter_);
|
||||
nr4_ = data;
|
||||
|
||||
nr4 = data;
|
||||
if (data & 0x80) { // init-bit
|
||||
nr4_ &= 0x7F;
|
||||
master_ = !envelopeUnit_.nr4Init(cycleCounter_);
|
||||
|
||||
if (data & 0x80) { //init-bit
|
||||
nr4 &= 0x7F;
|
||||
if (master_)
|
||||
lfsr_.nr4Init(cycleCounter_);
|
||||
|
||||
master = !envelopeUnit.nr4Init(cycleCounter);
|
||||
|
||||
if (master)
|
||||
lfsr.nr4Init(cycleCounter);
|
||||
|
||||
staticOutputTest(cycleCounter);
|
||||
staticOutputTest_(cycleCounter_);
|
||||
}
|
||||
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel4::setSo(const unsigned long soMask) {
|
||||
this->soMask = soMask;
|
||||
staticOutputTest(cycleCounter);
|
||||
void Channel4::setSo(unsigned long soMask) {
|
||||
soMask_ = soMask;
|
||||
staticOutputTest_(cycleCounter_);
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel4::reset() {
|
||||
cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position.
|
||||
|
||||
// lengthCounter.reset();
|
||||
lfsr.reset(cycleCounter);
|
||||
envelopeUnit.reset();
|
||||
// cycleCounter >> 12 & 7 represents the frame sequencer position.
|
||||
cycleCounter_ &= 0xFFF;
|
||||
cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000;
|
||||
|
||||
lfsr_.reset(cycleCounter_);
|
||||
envelopeUnit_.reset();
|
||||
setEvent();
|
||||
}
|
||||
|
||||
void Channel4::init(const bool cgb) {
|
||||
lengthCounter.init(cgb);
|
||||
void Channel4::loadState(SaveState const &state) {
|
||||
lfsr_.loadState(state);
|
||||
envelopeUnit_.loadState(state.spu.ch4.env, state.mem.ioamhram.get()[0x121],
|
||||
state.spu.cycleCounter);
|
||||
lengthCounter_.loadState(state.spu.ch4.lcounter, state.spu.cycleCounter);
|
||||
|
||||
cycleCounter_ = state.spu.cycleCounter;
|
||||
nr4_ = state.spu.ch4.nr4;
|
||||
master_ = state.spu.ch4.master;
|
||||
}
|
||||
|
||||
void Channel4::loadState(const SaveState &state) {
|
||||
lfsr.loadState(state);
|
||||
envelopeUnit.loadState(state.spu.ch4.env, state.mem.ioamhram.get()[0x121], state.spu.cycleCounter);
|
||||
lengthCounter.loadState(state.spu.ch4.lcounter, state.spu.cycleCounter);
|
||||
|
||||
cycleCounter = state.spu.cycleCounter;
|
||||
nr4 = state.spu.ch4.nr4;
|
||||
master = state.spu.ch4.master;
|
||||
}
|
||||
|
||||
void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
|
||||
const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
|
||||
const unsigned long outLow = outBase * (0 - 15ul);
|
||||
const unsigned long endCycles = cycleCounter + cycles;
|
||||
void Channel4::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) {
|
||||
unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0;
|
||||
unsigned long const outLow = outBase * (0 - 15ul);
|
||||
unsigned long const endCycles = cycleCounter_ + cycles;
|
||||
|
||||
for (;;) {
|
||||
const unsigned long outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/;
|
||||
const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
|
||||
unsigned long out = lfsr.isHighState() ? outHigh : outLow;
|
||||
unsigned long const outHigh = outBase * (envelopeUnit_.getVolume() * 2 - 15ul);
|
||||
unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), endCycles);
|
||||
unsigned long out = lfsr_.isHighState() ? outHigh : outLow;
|
||||
|
||||
while (lfsr.getCounter() <= nextMajorEvent) {
|
||||
*buf += out - prevOut;
|
||||
prevOut = out;
|
||||
buf += lfsr.getCounter() - cycleCounter;
|
||||
cycleCounter = lfsr.getCounter();
|
||||
while (lfsr_.counter() <= nextMajorEvent) {
|
||||
*buf += out - prevOut_;
|
||||
prevOut_ = out;
|
||||
buf += lfsr_.counter() - cycleCounter_;
|
||||
cycleCounter_ = lfsr_.counter();
|
||||
|
||||
lfsr.event();
|
||||
out = lfsr.isHighState() ? outHigh : outLow;
|
||||
lfsr_.event();
|
||||
out = lfsr_.isHighState() ? outHigh : outLow;
|
||||
}
|
||||
|
||||
if (cycleCounter < nextMajorEvent) {
|
||||
*buf += out - prevOut;
|
||||
prevOut = out;
|
||||
buf += nextMajorEvent - cycleCounter;
|
||||
cycleCounter = nextMajorEvent;
|
||||
if (cycleCounter_ < nextMajorEvent) {
|
||||
*buf += out - prevOut_;
|
||||
prevOut_ = out;
|
||||
buf += nextMajorEvent - cycleCounter_;
|
||||
cycleCounter_ = nextMajorEvent;
|
||||
}
|
||||
|
||||
if (nextEventUnit->getCounter() == nextMajorEvent) {
|
||||
nextEventUnit->event();
|
||||
if (nextEventUnit_->counter() == nextMajorEvent) {
|
||||
nextEventUnit_->event();
|
||||
setEvent();
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (cycleCounter & SoundUnit::COUNTER_MAX) {
|
||||
lengthCounter.resetCounters(cycleCounter);
|
||||
lfsr.resetCounters(cycleCounter);
|
||||
envelopeUnit.resetCounters(cycleCounter);
|
||||
|
||||
cycleCounter -= SoundUnit::COUNTER_MAX;
|
||||
if (cycleCounter_ >= SoundUnit::counter_max) {
|
||||
lengthCounter_.resetCounters(cycleCounter_);
|
||||
lfsr_.resetCounters(cycleCounter_);
|
||||
envelopeUnit_.resetCounters(cycleCounter_);
|
||||
cycleCounter_ -= SoundUnit::counter_max;
|
||||
}
|
||||
}
|
||||
|
||||
SYNCFUNC(Channel4)
|
||||
{
|
||||
SSS(lengthCounter);
|
||||
SSS(envelopeUnit);
|
||||
SSS(lfsr);
|
||||
SSS(lengthCounter_);
|
||||
SSS(envelopeUnit_);
|
||||
SSS(lfsr_);
|
||||
|
||||
EBS(nextEventUnit, 0);
|
||||
EVS(nextEventUnit, &lfsr, 1);
|
||||
EVS(nextEventUnit, &envelopeUnit, 2);
|
||||
EVS(nextEventUnit, &lengthCounter, 3);
|
||||
EES(nextEventUnit, NULL);
|
||||
EBS(nextEventUnit_, 0);
|
||||
EVS(nextEventUnit_, &lfsr_, 1);
|
||||
EVS(nextEventUnit_, &envelopeUnit_, 2);
|
||||
EVS(nextEventUnit_, &lengthCounter_, 3);
|
||||
EES(nextEventUnit_, NULL);
|
||||
|
||||
NSS(cycleCounter);
|
||||
NSS(soMask);
|
||||
NSS(prevOut);
|
||||
NSS(cycleCounter_);
|
||||
NSS(soMask_);
|
||||
NSS(prevOut_);
|
||||
|
||||
NSS(nr4);
|
||||
NSS(master);
|
||||
NSS(nr4_);
|
||||
NSS(master_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef SOUND_CHANNEL4_H
|
||||
#define SOUND_CHANNEL4_H
|
||||
|
||||
#include "gbint.h"
|
||||
#include "master_disabler.h"
|
||||
#include "length_counter.h"
|
||||
#include "envelope_unit.h"
|
||||
#include "gbint.h"
|
||||
#include "length_counter.h"
|
||||
#include "master_disabler.h"
|
||||
#include "static_output_tester.h"
|
||||
#include "newstate.h"
|
||||
|
||||
|
@ -31,72 +31,71 @@ namespace gambatte {
|
|||
struct SaveState;
|
||||
|
||||
class Channel4 {
|
||||
class Lfsr : public SoundUnit {
|
||||
unsigned long backupCounter;
|
||||
unsigned short reg;
|
||||
unsigned char nr3;
|
||||
bool master;
|
||||
|
||||
void updateBackupCounter(unsigned long cc);
|
||||
|
||||
public:
|
||||
Lfsr();
|
||||
void event();
|
||||
bool isHighState() const { return ~reg & 1; }
|
||||
void nr3Change(unsigned newNr3, unsigned long cc);
|
||||
void nr4Init(unsigned long cc);
|
||||
void reset(unsigned long cc);
|
||||
void loadState(const SaveState &state);
|
||||
void resetCounters(unsigned long oldCc);
|
||||
void disableMaster() { killCounter(); master = false; reg = 0x7FFF; }
|
||||
void killCounter() { counter = COUNTER_DISABLED; }
|
||||
void reviveCounter(unsigned long cc);
|
||||
|
||||
template<bool isReader>void SyncState(NewState *ns);
|
||||
};
|
||||
|
||||
class Ch4MasterDisabler : public MasterDisabler {
|
||||
Lfsr &lfsr;
|
||||
public:
|
||||
Ch4MasterDisabler(bool &m, Lfsr &lfsr) : MasterDisabler(m), lfsr(lfsr) {}
|
||||
void operator()() { MasterDisabler::operator()(); lfsr.disableMaster(); }
|
||||
};
|
||||
|
||||
friend class StaticOutputTester<Channel4,Lfsr>;
|
||||
|
||||
StaticOutputTester<Channel4,Lfsr> staticOutputTest;
|
||||
Ch4MasterDisabler disableMaster;
|
||||
LengthCounter lengthCounter;
|
||||
EnvelopeUnit envelopeUnit;
|
||||
Lfsr lfsr;
|
||||
|
||||
SoundUnit *nextEventUnit;
|
||||
|
||||
unsigned long cycleCounter;
|
||||
unsigned long soMask;
|
||||
unsigned long prevOut;
|
||||
|
||||
unsigned char nr4;
|
||||
bool master;
|
||||
|
||||
void setEvent();
|
||||
|
||||
public:
|
||||
Channel4();
|
||||
void setNr1(unsigned data);
|
||||
void setNr2(unsigned data);
|
||||
void setNr3(unsigned data) { lfsr.nr3Change(data, cycleCounter); /*setEvent();*/ }
|
||||
void setNr3(unsigned data) { lfsr_.nr3Change(data, cycleCounter_); }
|
||||
void setNr4(unsigned data);
|
||||
|
||||
void setSo(unsigned long soMask);
|
||||
bool isActive() const { return master; }
|
||||
|
||||
bool isActive() const { return master_; }
|
||||
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
|
||||
|
||||
void reset();
|
||||
void init(bool cgb);
|
||||
void loadState(const SaveState &state);
|
||||
void loadState(SaveState const &state);
|
||||
|
||||
private:
|
||||
class Lfsr : public SoundUnit {
|
||||
public:
|
||||
Lfsr();
|
||||
virtual void event();
|
||||
virtual void resetCounters(unsigned long oldCc);
|
||||
bool isHighState() const { return ~reg_ & 1; }
|
||||
void nr3Change(unsigned newNr3, unsigned long cc);
|
||||
void nr4Init(unsigned long cc);
|
||||
void reset(unsigned long cc);
|
||||
void loadState(SaveState const &state);
|
||||
void disableMaster() { killCounter(); master_ = false; reg_ = 0x7FFF; }
|
||||
void killCounter() { counter_ = counter_disabled; }
|
||||
void reviveCounter(unsigned long cc);
|
||||
|
||||
private:
|
||||
unsigned long backupCounter_;
|
||||
unsigned short reg_;
|
||||
unsigned char nr3_;
|
||||
bool master_;
|
||||
|
||||
void updateBackupCounter(unsigned long cc);
|
||||
|
||||
public:
|
||||
template<bool isReader>void SyncState(NewState *ns);
|
||||
};
|
||||
|
||||
class Ch4MasterDisabler : public MasterDisabler {
|
||||
public:
|
||||
Ch4MasterDisabler(bool &m, Lfsr &lfsr) : MasterDisabler(m), lfsr_(lfsr) {}
|
||||
virtual void operator()() { MasterDisabler::operator()(); lfsr_.disableMaster(); }
|
||||
|
||||
private:
|
||||
Lfsr &lfsr_;
|
||||
};
|
||||
|
||||
friend class StaticOutputTester<Channel4, Lfsr>;
|
||||
|
||||
StaticOutputTester<Channel4, Lfsr> staticOutputTest_;
|
||||
Ch4MasterDisabler disableMaster_;
|
||||
LengthCounter lengthCounter_;
|
||||
EnvelopeUnit envelopeUnit_;
|
||||
Lfsr lfsr_;
|
||||
SoundUnit *nextEventUnit_;
|
||||
unsigned long cycleCounter_;
|
||||
unsigned long soMask_;
|
||||
unsigned long prevOut_;
|
||||
unsigned char nr4_;
|
||||
bool master_;
|
||||
|
||||
void setEvent();
|
||||
|
||||
public:
|
||||
template<bool isReader>void SyncState(NewState *ns);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,156 +1,162 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include "duty_unit.h"
|
||||
#include <algorithm>
|
||||
|
||||
static inline bool toOutState(const unsigned duty, const unsigned pos) {
|
||||
static const unsigned char duties[4] = { 0x80, 0x81, 0xE1, 0x7E };
|
||||
|
||||
return duties[duty] >> pos & 1;
|
||||
static inline bool toOutState(unsigned duty, unsigned pos) {
|
||||
return 0x7EE18180 >> (duty * 8 + pos) & 1;
|
||||
}
|
||||
|
||||
static inline unsigned toPeriod(const unsigned freq) {
|
||||
return (2048 - freq) << 1;
|
||||
static inline unsigned toPeriod(unsigned freq) {
|
||||
return (2048 - freq) * 2;
|
||||
}
|
||||
|
||||
namespace gambatte {
|
||||
|
||||
void DutyUnit::updatePos(const unsigned long cc) {
|
||||
if (cc >= nextPosUpdate) {
|
||||
const unsigned long inc = (cc - nextPosUpdate) / period + 1;
|
||||
nextPosUpdate += period * inc;
|
||||
pos += inc;
|
||||
pos &= 7;
|
||||
DutyUnit::DutyUnit()
|
||||
: nextPosUpdate_(counter_disabled)
|
||||
, period_(4096)
|
||||
, pos_(0)
|
||||
, duty_(0)
|
||||
, inc_(0)
|
||||
, high_(false)
|
||||
, enableEvents_(true)
|
||||
{
|
||||
}
|
||||
|
||||
void DutyUnit::updatePos(unsigned long const cc) {
|
||||
if (cc >= nextPosUpdate_) {
|
||||
unsigned long const inc = (cc - nextPosUpdate_) / period_ + 1;
|
||||
nextPosUpdate_ += period_ * inc;
|
||||
pos_ += inc;
|
||||
pos_ &= 7;
|
||||
high_ = toOutState(duty_, pos_);
|
||||
}
|
||||
}
|
||||
|
||||
void DutyUnit::setDuty(const unsigned nr1) {
|
||||
duty = nr1 >> 6;
|
||||
high = toOutState(duty, pos);
|
||||
}
|
||||
|
||||
void DutyUnit::setCounter() {
|
||||
static const unsigned char nextStateDistance[4 * 8] = {
|
||||
6, 5, 4, 3, 2, 1, 0, 0,
|
||||
0, 5, 4, 3, 2, 1, 0, 1,
|
||||
0, 3, 2, 1, 0, 3, 2, 1,
|
||||
0, 5, 4, 3, 2, 1, 0, 1
|
||||
static unsigned char const nextStateDistance[4 * 8] = {
|
||||
7, 6, 5, 4, 3, 2, 1, 1,
|
||||
1, 6, 5, 4, 3, 2, 1, 2,
|
||||
1, 4, 3, 2, 1, 4, 3, 2,
|
||||
1, 6, 5, 4, 3, 2, 1, 2
|
||||
};
|
||||
|
||||
if (enableEvents && nextPosUpdate != COUNTER_DISABLED)
|
||||
counter = nextPosUpdate + period * nextStateDistance[(duty * 8) | pos];
|
||||
else
|
||||
counter = COUNTER_DISABLED;
|
||||
if (enableEvents_ && nextPosUpdate_ != counter_disabled) {
|
||||
unsigned const npos = (pos_ + 1) & 7;
|
||||
counter_ = nextPosUpdate_;
|
||||
inc_ = nextStateDistance[duty_ * 8 + npos];
|
||||
if (toOutState(duty_, npos) == high_) {
|
||||
counter_ += period_ * inc_;
|
||||
inc_ = nextStateDistance[duty_ * 8 + ((npos + inc_) & 7)];
|
||||
}
|
||||
} else
|
||||
counter_ = counter_disabled;
|
||||
}
|
||||
|
||||
void DutyUnit::setFreq(const unsigned newFreq, const unsigned long cc) {
|
||||
void DutyUnit::setFreq(unsigned newFreq, unsigned long cc) {
|
||||
updatePos(cc);
|
||||
period = toPeriod(newFreq);
|
||||
period_ = toPeriod(newFreq);
|
||||
setCounter();
|
||||
}
|
||||
|
||||
void DutyUnit::event() {
|
||||
unsigned inc = period << duty;
|
||||
static unsigned char const inc[] = {
|
||||
1, 7,
|
||||
2, 6,
|
||||
4, 4,
|
||||
6, 2,
|
||||
};
|
||||
|
||||
if (duty == 3)
|
||||
inc -= period * 2;
|
||||
|
||||
if (!(high ^= true))
|
||||
inc = period * 8 - inc;
|
||||
|
||||
counter += inc;
|
||||
high_ ^= true;
|
||||
counter_ += inc_ * period_;
|
||||
inc_ = inc[duty_ * 2 + high_];
|
||||
}
|
||||
|
||||
void DutyUnit::nr1Change(const unsigned newNr1, const unsigned long cc) {
|
||||
void DutyUnit::nr1Change(unsigned newNr1, unsigned long cc) {
|
||||
updatePos(cc);
|
||||
setDuty(newNr1);
|
||||
duty_ = newNr1 >> 6;
|
||||
setCounter();
|
||||
}
|
||||
|
||||
void DutyUnit::nr3Change(const unsigned newNr3, const unsigned long cc) {
|
||||
setFreq((getFreq() & 0x700) | newNr3, cc);
|
||||
void DutyUnit::nr3Change(unsigned newNr3, unsigned long cc) {
|
||||
setFreq((freq() & 0x700) | newNr3, cc);
|
||||
}
|
||||
|
||||
void DutyUnit::nr4Change(const unsigned newNr4, const unsigned long cc) {
|
||||
setFreq((newNr4 << 8 & 0x700) | (getFreq() & 0xFF), cc);
|
||||
void DutyUnit::nr4Change(unsigned const newNr4, unsigned long const cc,
|
||||
bool const master) {
|
||||
setFreq((newNr4 << 8 & 0x700) | (freq() & 0xFF), cc);
|
||||
|
||||
if (newNr4 & 0x80) {
|
||||
nextPosUpdate = (cc & ~1) + period;
|
||||
nextPosUpdate_ = (cc & ~1ul) + period_ + 4 - (master << 1);
|
||||
setCounter();
|
||||
}
|
||||
}
|
||||
|
||||
DutyUnit::DutyUnit() :
|
||||
nextPosUpdate(COUNTER_DISABLED),
|
||||
period(4096),
|
||||
pos(0),
|
||||
duty(0),
|
||||
high(false),
|
||||
enableEvents(true)
|
||||
{}
|
||||
|
||||
void DutyUnit::reset() {
|
||||
pos = 0;
|
||||
high = toOutState(duty, pos);
|
||||
nextPosUpdate = COUNTER_DISABLED;
|
||||
pos_ = 0;
|
||||
high_ = false;
|
||||
nextPosUpdate_ = counter_disabled;
|
||||
setCounter();
|
||||
}
|
||||
|
||||
void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned long cc) {
|
||||
nextPosUpdate = std::max(dstate.nextPosUpdate, cc);
|
||||
pos = dstate.pos & 7;
|
||||
setDuty(nr1);
|
||||
period = toPeriod((nr4 << 8 & 0x700) | dstate.nr3);
|
||||
enableEvents = true;
|
||||
void DutyUnit::loadState(SaveState::SPU::Duty const &dstate,
|
||||
unsigned const nr1, unsigned const nr4, unsigned long const cc) {
|
||||
nextPosUpdate_ = std::max(dstate.nextPosUpdate, cc);
|
||||
pos_ = dstate.pos & 7;
|
||||
high_ = dstate.high;
|
||||
duty_ = nr1 >> 6;
|
||||
period_ = toPeriod((nr4 << 8 & 0x700) | dstate.nr3);
|
||||
enableEvents_ = true;
|
||||
setCounter();
|
||||
}
|
||||
|
||||
void DutyUnit::resetCounters(const unsigned long oldCc) {
|
||||
if (nextPosUpdate == COUNTER_DISABLED)
|
||||
void DutyUnit::resetCounters(unsigned long const oldCc) {
|
||||
if (nextPosUpdate_ == counter_disabled)
|
||||
return;
|
||||
|
||||
updatePos(oldCc);
|
||||
nextPosUpdate -= COUNTER_MAX;
|
||||
SoundUnit::resetCounters(oldCc);
|
||||
}
|
||||
|
||||
void DutyUnit::killCounter() {
|
||||
enableEvents = false;
|
||||
nextPosUpdate_ -= counter_max;
|
||||
setCounter();
|
||||
}
|
||||
|
||||
void DutyUnit::reviveCounter(const unsigned long cc) {
|
||||
void DutyUnit::killCounter() {
|
||||
enableEvents_ = false;
|
||||
setCounter();
|
||||
}
|
||||
|
||||
void DutyUnit::reviveCounter(unsigned long const cc) {
|
||||
updatePos(cc);
|
||||
high = toOutState(duty, pos);
|
||||
enableEvents = true;
|
||||
enableEvents_ = true;
|
||||
setCounter();
|
||||
}
|
||||
|
||||
SYNCFUNC(DutyUnit)
|
||||
{
|
||||
NSS(counter);
|
||||
NSS(nextPosUpdate);
|
||||
NSS(period);
|
||||
NSS(pos);
|
||||
NSS(duty);
|
||||
NSS(high);
|
||||
NSS(enableEvents);
|
||||
NSS(counter_);
|
||||
NSS(nextPosUpdate_);
|
||||
NSS(period_);
|
||||
NSS(pos_);
|
||||
NSS(duty_);
|
||||
NSS(inc_);
|
||||
NSS(high_);
|
||||
NSS(enableEvents_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef DUTY_UNIT_H
|
||||
#define DUTY_UNIT_H
|
||||
|
||||
|
@ -27,42 +27,47 @@
|
|||
namespace gambatte {
|
||||
|
||||
class DutyUnit : public SoundUnit {
|
||||
unsigned long nextPosUpdate;
|
||||
unsigned short period;
|
||||
unsigned char pos;
|
||||
unsigned char duty;
|
||||
bool high;
|
||||
bool enableEvents;
|
||||
public:
|
||||
DutyUnit();
|
||||
virtual void event();
|
||||
virtual void resetCounters(unsigned long oldCc);
|
||||
bool isHighState() const { return high_; }
|
||||
void nr1Change(unsigned newNr1, unsigned long cc);
|
||||
void nr3Change(unsigned newNr3, unsigned long cc);
|
||||
void nr4Change(unsigned newNr4, unsigned long cc, bool master);
|
||||
void reset();
|
||||
void loadState(SaveState::SPU::Duty const &dstate, unsigned nr1, unsigned nr4, unsigned long cc);
|
||||
void killCounter();
|
||||
void reviveCounter(unsigned long cc);
|
||||
|
||||
//intended for use by SweepUnit only.
|
||||
unsigned freq() const { return 2048 - (period_ >> 1); }
|
||||
void setFreq(unsigned newFreq, unsigned long cc);
|
||||
|
||||
private:
|
||||
unsigned long nextPosUpdate_;
|
||||
unsigned short period_;
|
||||
unsigned char pos_;
|
||||
unsigned char duty_;
|
||||
unsigned char inc_;
|
||||
bool high_;
|
||||
bool enableEvents_;
|
||||
|
||||
void setCounter();
|
||||
void setDuty(unsigned nr1);
|
||||
void updatePos(unsigned long cc);
|
||||
|
||||
public:
|
||||
DutyUnit();
|
||||
void event();
|
||||
bool isHighState() const { return high; }
|
||||
void nr1Change(unsigned newNr1, unsigned long cc);
|
||||
void nr3Change(unsigned newNr3, unsigned long cc);
|
||||
void nr4Change(unsigned newNr4, unsigned long cc);
|
||||
void reset();
|
||||
void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4, unsigned long cc);
|
||||
void resetCounters(unsigned long oldCc);
|
||||
void killCounter();
|
||||
void reviveCounter(unsigned long cc);
|
||||
|
||||
//intended for use by SweepUnit only.
|
||||
unsigned getFreq() const { return 2048 - (period >> 1); }
|
||||
void setFreq(unsigned newFreq, unsigned long cc);
|
||||
|
||||
template<bool isReader>void SyncState(NewState *ns);
|
||||
};
|
||||
|
||||
class DutyMasterDisabler : public MasterDisabler {
|
||||
DutyUnit &dutyUnit;
|
||||
public:
|
||||
DutyMasterDisabler(bool &m, DutyUnit &dutyUnit) : MasterDisabler(m), dutyUnit(dutyUnit) {}
|
||||
void operator()() { MasterDisabler::operator()(); dutyUnit.killCounter(); }
|
||||
DutyMasterDisabler(bool &m, DutyUnit &dutyUnit) : MasterDisabler(m), dutyUnit_(dutyUnit) {}
|
||||
virtual void operator()() { MasterDisabler::operator()(); dutyUnit_.killCounter(); }
|
||||
|
||||
private:
|
||||
DutyUnit &dutyUnit_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,108 +1,98 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include "envelope_unit.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace gambatte {
|
||||
|
||||
EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent;
|
||||
EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent_;
|
||||
|
||||
EnvelopeUnit::EnvelopeUnit(VolOnOffEvent &volOnOffEvent)
|
||||
: volOnOffEvent_(volOnOffEvent)
|
||||
, nr2_(0)
|
||||
, volume_(0)
|
||||
{
|
||||
}
|
||||
|
||||
void EnvelopeUnit::reset() {
|
||||
counter_ = counter_disabled;
|
||||
}
|
||||
|
||||
void EnvelopeUnit::loadState(SaveState::SPU::Env const &estate, unsigned nr2, unsigned long cc) {
|
||||
counter_ = std::max(estate.counter, cc);
|
||||
volume_ = estate.volume;
|
||||
nr2_ = nr2;
|
||||
}
|
||||
|
||||
void EnvelopeUnit::event() {
|
||||
const unsigned long period = nr2 & 7;
|
||||
unsigned long const period = nr2_ & 7;
|
||||
|
||||
if (period) {
|
||||
unsigned newVol = volume;
|
||||
|
||||
if (nr2 & 8)
|
||||
unsigned newVol = volume_;
|
||||
if (nr2_ & 8)
|
||||
++newVol;
|
||||
else
|
||||
--newVol;
|
||||
|
||||
if (newVol < 0x10U) {
|
||||
volume = newVol;
|
||||
volume_ = newVol;
|
||||
if (volume_ < 2)
|
||||
volOnOffEvent_(counter_);
|
||||
|
||||
if (volume < 2)
|
||||
volOnOffEvent(counter);
|
||||
|
||||
counter += period << 15;
|
||||
counter_ += period << 15;
|
||||
} else
|
||||
counter = COUNTER_DISABLED;
|
||||
counter_ = counter_disabled;
|
||||
} else
|
||||
counter += 8ul << 15;
|
||||
counter_ += 8ul << 15;
|
||||
}
|
||||
|
||||
bool EnvelopeUnit::nr2Change(const unsigned newNr2) {
|
||||
if (!(nr2 & 7) && counter != COUNTER_DISABLED)
|
||||
++volume;
|
||||
else if (!(nr2 & 8))
|
||||
volume += 2;
|
||||
bool EnvelopeUnit::nr2Change(unsigned const newNr2) {
|
||||
if (!(nr2_ & 7) && counter_ != counter_disabled)
|
||||
++volume_;
|
||||
else if (!(nr2_ & 8))
|
||||
volume_ += 2;
|
||||
|
||||
if ((nr2 ^ newNr2) & 8)
|
||||
volume = 0x10 - volume;
|
||||
|
||||
volume &= 0xF;
|
||||
|
||||
nr2 = newNr2;
|
||||
if ((nr2_ ^ newNr2) & 8)
|
||||
volume_ = 0x10 - volume_;
|
||||
|
||||
volume_ &= 0xF;
|
||||
nr2_ = newNr2;
|
||||
return !(newNr2 & 0xF8);
|
||||
}
|
||||
|
||||
bool EnvelopeUnit::nr4Init(const unsigned long cc) {
|
||||
{
|
||||
unsigned long period = nr2 & 7;
|
||||
bool EnvelopeUnit::nr4Init(unsigned long const cc) {
|
||||
unsigned long period = nr2_ & 7 ? nr2_ & 7 : 8;
|
||||
|
||||
if (!period)
|
||||
period = 8;
|
||||
if (((cc + 2) & 0x7000) == 0x0000)
|
||||
++period;
|
||||
|
||||
if (!(cc & 0x7000))
|
||||
++period;
|
||||
counter_ = cc - ((cc - 0x1000) & 0x7FFF) + period * 0x8000;
|
||||
|
||||
counter = cc - ((cc - 0x1000) & 0x7FFF) + period * 0x8000;
|
||||
}
|
||||
|
||||
volume = nr2 >> 4;
|
||||
|
||||
return !(nr2 & 0xF8);
|
||||
}
|
||||
|
||||
EnvelopeUnit::EnvelopeUnit(VolOnOffEvent &volOnOffEvent)
|
||||
: volOnOffEvent(volOnOffEvent),
|
||||
nr2(0),
|
||||
volume(0)
|
||||
{
|
||||
}
|
||||
|
||||
void EnvelopeUnit::reset() {
|
||||
counter = COUNTER_DISABLED;
|
||||
}
|
||||
|
||||
void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2, const unsigned long cc) {
|
||||
counter = std::max(estate.counter, cc);
|
||||
volume = estate.volume;
|
||||
this->nr2 = nr2;
|
||||
volume_ = nr2_ >> 4;
|
||||
return !(nr2_ & 0xF8);
|
||||
}
|
||||
|
||||
SYNCFUNC(EnvelopeUnit)
|
||||
{
|
||||
NSS(counter);
|
||||
NSS(nr2);
|
||||
NSS(volume);
|
||||
NSS(counter_);
|
||||
NSS(nr2_);
|
||||
NSS(volume_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef ENVELOPE_UNIT_H
|
||||
#define ENVELOPE_UNIT_H
|
||||
|
||||
|
@ -32,22 +32,22 @@ public:
|
|||
virtual void operator()(unsigned long /*cc*/) {}
|
||||
};
|
||||
|
||||
private:
|
||||
static VolOnOffEvent nullEvent;
|
||||
VolOnOffEvent &volOnOffEvent;
|
||||
unsigned char nr2;
|
||||
unsigned char volume;
|
||||
|
||||
public:
|
||||
explicit EnvelopeUnit(VolOnOffEvent &volOnOffEvent = nullEvent);
|
||||
explicit EnvelopeUnit(VolOnOffEvent &volOnOffEvent = nullEvent_);
|
||||
void event();
|
||||
bool dacIsOn() const { return nr2 & 0xF8; }
|
||||
unsigned getVolume() const { return volume; }
|
||||
bool dacIsOn() const { return nr2_ & 0xF8; }
|
||||
unsigned getVolume() const { return volume_; }
|
||||
bool nr2Change(unsigned newNr2);
|
||||
bool nr4Init(unsigned long cycleCounter);
|
||||
void reset();
|
||||
void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned long cc);
|
||||
void loadState(SaveState::SPU::Env const &estate, unsigned nr2, unsigned long cc);
|
||||
|
||||
private:
|
||||
static VolOnOffEvent nullEvent_;
|
||||
VolOnOffEvent &volOnOffEvent_;
|
||||
unsigned char nr2_;
|
||||
unsigned char volume_;
|
||||
|
||||
public:
|
||||
template<bool isReader>void SyncState(NewState *ns);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,93 +1,83 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include "length_counter.h"
|
||||
#include "master_disabler.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace gambatte {
|
||||
|
||||
LengthCounter::LengthCounter(MasterDisabler &disabler, const unsigned mask) :
|
||||
disableMaster(disabler),
|
||||
lengthMask(mask)
|
||||
LengthCounter::LengthCounter(MasterDisabler &disabler, unsigned const mask)
|
||||
: disableMaster_(disabler)
|
||||
, lengthCounter_(0)
|
||||
, lengthMask_(mask)
|
||||
{
|
||||
init(false);
|
||||
nr1Change(0, 0, 0);
|
||||
}
|
||||
|
||||
void LengthCounter::event() {
|
||||
counter = COUNTER_DISABLED;
|
||||
lengthCounter = 0;
|
||||
disableMaster();
|
||||
counter_ = counter_disabled;
|
||||
lengthCounter_ = 0;
|
||||
disableMaster_();
|
||||
}
|
||||
|
||||
void LengthCounter::nr1Change(const unsigned newNr1, const unsigned nr4, const unsigned long cycleCounter) {
|
||||
lengthCounter = (~newNr1 & lengthMask) + 1;
|
||||
counter = (nr4 & 0x40) ?( (cycleCounter >> 13) + lengthCounter) << 13 : static_cast<unsigned long>(COUNTER_DISABLED);
|
||||
void LengthCounter::nr1Change(unsigned const newNr1, unsigned const nr4, unsigned long const cc) {
|
||||
lengthCounter_ = (~newNr1 & lengthMask_) + 1;
|
||||
counter_ = nr4 & 0x40
|
||||
? ((cc >> 13) + lengthCounter_) << 13
|
||||
: static_cast<unsigned long>(counter_disabled);
|
||||
}
|
||||
|
||||
void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, const unsigned long cycleCounter) {
|
||||
if (counter != COUNTER_DISABLED)
|
||||
lengthCounter = (counter >> 13) - (cycleCounter >> 13);
|
||||
void LengthCounter::nr4Change(unsigned const oldNr4, unsigned const newNr4, unsigned long const cc) {
|
||||
if (counter_ != counter_disabled)
|
||||
lengthCounter_ = (counter_ >> 13) - (cc >> 13);
|
||||
|
||||
{
|
||||
unsigned dec = 0;
|
||||
|
||||
if (newNr4 & 0x40) {
|
||||
dec = ~cycleCounter >> 12 & 1;
|
||||
dec = ~cc >> 12 & 1;
|
||||
|
||||
if (!(oldNr4 & 0x40) && lengthCounter) {
|
||||
if (!(lengthCounter -= dec))
|
||||
disableMaster();
|
||||
if (!(oldNr4 & 0x40) && lengthCounter_) {
|
||||
if (!(lengthCounter_ -= dec))
|
||||
disableMaster_();
|
||||
}
|
||||
}
|
||||
|
||||
if ((newNr4 & 0x80) && !lengthCounter)
|
||||
lengthCounter = lengthMask + 1 - dec;
|
||||
if ((newNr4 & 0x80) && !lengthCounter_)
|
||||
lengthCounter_ = lengthMask_ + 1 - dec;
|
||||
}
|
||||
|
||||
if ((newNr4 & 0x40) && lengthCounter)
|
||||
counter = ((cycleCounter >> 13) + lengthCounter) << 13;
|
||||
if ((newNr4 & 0x40) && lengthCounter_)
|
||||
counter_ = ((cc >> 13) + lengthCounter_) << 13;
|
||||
else
|
||||
counter = COUNTER_DISABLED;
|
||||
counter_ = counter_disabled;
|
||||
}
|
||||
|
||||
/*void LengthCounter::reset() {
|
||||
counter = COUNTER_DISABLED;
|
||||
|
||||
if (cgb)
|
||||
lengthCounter = lengthMask + 1;
|
||||
}*/
|
||||
|
||||
void LengthCounter::init(const bool cgb) {
|
||||
this->cgb = cgb;
|
||||
}
|
||||
|
||||
void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsigned long cc) {
|
||||
counter = std::max(lstate.counter, cc);
|
||||
lengthCounter = lstate.lengthCounter;
|
||||
void LengthCounter::loadState(SaveState::SPU::LCounter const &lstate, unsigned long const cc) {
|
||||
counter_ = std::max(lstate.counter, cc);
|
||||
lengthCounter_ = lstate.lengthCounter;
|
||||
}
|
||||
|
||||
SYNCFUNC(LengthCounter)
|
||||
{
|
||||
NSS(counter);
|
||||
NSS(lengthCounter);
|
||||
NSS(cgb);
|
||||
NSS(counter_);
|
||||
NSS(lengthCounter_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef LENGTH_COUNTER_H
|
||||
#define LENGTH_COUNTER_H
|
||||
|
||||
|
@ -28,20 +28,19 @@ namespace gambatte {
|
|||
class MasterDisabler;
|
||||
|
||||
class LengthCounter : public SoundUnit {
|
||||
MasterDisabler &disableMaster;
|
||||
unsigned short lengthCounter;
|
||||
const unsigned char lengthMask;
|
||||
bool cgb;
|
||||
|
||||
public:
|
||||
LengthCounter(MasterDisabler &disabler, unsigned lengthMask);
|
||||
void event();
|
||||
virtual void event();
|
||||
void nr1Change(unsigned newNr1, unsigned nr4, unsigned long cc);
|
||||
void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned long cc);
|
||||
// void reset();
|
||||
void init(bool cgb);
|
||||
void loadState(const SaveState::SPU::LCounter &lstate, unsigned long cc);
|
||||
void loadState(SaveState::SPU::LCounter const &lstate, unsigned long cc);
|
||||
|
||||
private:
|
||||
MasterDisabler &disableMaster_;
|
||||
unsigned short lengthCounter_;
|
||||
unsigned char const lengthMask_;
|
||||
|
||||
public:
|
||||
template<bool isReader>void SyncState(NewState *ns);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,33 +1,36 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef MASTER_DISABLER_H
|
||||
#define MASTER_DISABLER_H
|
||||
|
||||
namespace gambatte {
|
||||
class MasterDisabler {
|
||||
bool &master;
|
||||
|
||||
class MasterDisabler {
|
||||
public:
|
||||
MasterDisabler(bool &m) : master(m) {}
|
||||
explicit MasterDisabler(bool &master) : master_(master) {}
|
||||
virtual ~MasterDisabler() {}
|
||||
virtual void operator()() { master = false; }
|
||||
virtual void operator()() { master_ = false; }
|
||||
|
||||
private:
|
||||
bool &master_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,37 +1,43 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef SOUND_UNIT_H
|
||||
#define SOUND_UNIT_H
|
||||
|
||||
namespace gambatte {
|
||||
|
||||
class SoundUnit {
|
||||
protected:
|
||||
unsigned long counter;
|
||||
public:
|
||||
enum { COUNTER_MAX = 0x80000000u, COUNTER_DISABLED = 0xFFFFFFFFu };
|
||||
enum { counter_max = 0x80000000u, counter_disabled = 0xFFFFFFFFu };
|
||||
|
||||
SoundUnit() : counter(COUNTER_DISABLED) {}
|
||||
virtual ~SoundUnit() {}
|
||||
virtual void event() = 0;
|
||||
unsigned long getCounter() const { return counter; }
|
||||
virtual void resetCounters(unsigned long /*oldCc*/) { if (counter != COUNTER_DISABLED) counter -= COUNTER_MAX; }
|
||||
|
||||
virtual void resetCounters(unsigned long /*oldCc*/) {
|
||||
if (counter_ != counter_disabled)
|
||||
counter_ -= counter_max;
|
||||
}
|
||||
|
||||
unsigned long counter() const { return counter_; }
|
||||
|
||||
protected:
|
||||
SoundUnit() : counter_(counter_disabled) {}
|
||||
unsigned long counter_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Sindre Aamås *
|
||||
* aamas@stud.ntnu.no *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License version 2 as *
|
||||
* published by the Free Software Foundation. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License version 2 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* version 2 along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
//
|
||||
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License version 2 for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// version 2 along with this program; if not, write to the
|
||||
// Free Software Foundation, Inc.,
|
||||
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef STATIC_OUTPUT_TESTER_H
|
||||
#define STATIC_OUTPUT_TESTER_H
|
||||
|
||||
|
@ -25,19 +25,21 @@ namespace gambatte {
|
|||
|
||||
template<class Channel, class Unit>
|
||||
class StaticOutputTester : public EnvelopeUnit::VolOnOffEvent {
|
||||
const Channel &ch;
|
||||
Unit &unit;
|
||||
public:
|
||||
StaticOutputTester(const Channel &ch, Unit &unit) : ch(ch), unit(unit) {}
|
||||
StaticOutputTester(Channel const &ch, Unit &unit) : ch_(ch), unit_(unit) {}
|
||||
void operator()(unsigned long cc);
|
||||
|
||||
private:
|
||||
Channel const &ch_;
|
||||
Unit &unit_;
|
||||
};
|
||||
|
||||
template<class Channel, class Unit>
|
||||
void StaticOutputTester<Channel, Unit>::operator()(const unsigned long cc) {
|
||||
if (ch.soMask && ch.master && ch.envelopeUnit.getVolume())
|
||||
unit.reviveCounter(cc);
|
||||
void StaticOutputTester<Channel, Unit>::operator()(unsigned long cc) {
|
||||
if (ch_.soMask_ && ch_.master_ && ch_.envelopeUnit_.getVolume())
|
||||
unit_.reviveCounter(cc);
|
||||
else
|
||||
unit.killCounter();
|
||||
unit_.killCounter();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue