Update sound

This commit is contained in:
MrWint 2019-05-25 20:27:44 +02:00
parent 1dc9d0e123
commit b99b5a05d6
23 changed files with 1424 additions and 1463 deletions

View File

@ -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;

View File

@ -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();
}

View File

@ -125,6 +125,7 @@ struct SaveState {
unsigned long nextPosUpdate;
unsigned char nr3;
unsigned char pos;
unsigned char /*bool*/ high;
};
struct Env {

View File

@ -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_);
}
}

View File

@ -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);
};

View File

@ -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_);
}
}

View File

@ -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);
};

View File

@ -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_);
}
}

View File

@ -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);
};

View File

@ -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_);
}
}

View File

@ -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);
};

View File

@ -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_);
}
}

View File

@ -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);
};

View File

@ -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_);
}
}

View File

@ -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_;
};
}

View File

@ -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_);
}
}

View File

@ -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);
};

View File

@ -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_);
}
}

View File

@ -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);
};

View File

@ -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

View File

@ -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_;
};
}

View File

@ -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.