bsnes/higan/gb/apu/wave.cpp

157 lines
3.4 KiB
C++

auto APU::Wave::getPattern(uint5 offset) const -> uint4 {
return pattern[offset >> 1] >> (offset & 1 ? 0 : 4);
}
auto APU::Wave::run() -> void {
if(patternHold) patternHold--;
if(period && --period == 0) {
period = 1 * (2048 - frequency);
patternSample = getPattern(++patternOffset);
patternHold = 1;
}
static const uint shift[] = {4, 0, 1, 2}; //0%, 100%, 50%, 25%
uint4 sample = patternSample >> shift[volume];
if(!enable) sample = 0;
output = sample;
}
auto APU::Wave::clockLength() -> void {
if(counter) {
if(length && --length == 0) enable = false;
}
}
auto APU::Wave::read(uint16 addr) -> uint8 {
if(addr == 0xff1a) { //NR30
return dacEnable << 7 | 0x7f;
}
if(addr == 0xff1b) { //NR31
return 0xff;
}
if(addr == 0xff1c) { //NR32
return 0x80 | volume << 5 | 0x1f;
}
if(addr == 0xff1d) { //NR33
return 0xff;
}
if(addr == 0xff1e) { //NR34
return 0x80 | counter << 6 | 0x3f;
}
if(addr >= 0xff30 && addr <= 0xff3f) {
if(enable) {
if(!Model::GameBoyColor() && !patternHold) return 0xff;
return pattern[patternOffset >> 1];
} else {
return pattern[addr & 15];
}
}
return 0xff;
}
auto APU::Wave::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff1a) { //NR30
dacEnable = data.bit(7);
if(!dacEnable) enable = false;
}
if(addr == 0xff1b) { //NR31
length = 256 - data;
}
if(addr == 0xff1c) { //NR32
volume = data.bits(6,5);
}
if(addr == 0xff1d) { //NR33
frequency.bits(7,0) = data;
}
if(addr == 0xff1e) { //NR34
if(apu.phase.bit(0) && !counter && data.bit(6)) {
if(length && --length == 0) enable = false;
}
counter = data.bit(6);
frequency.bits(10,8) = data.bits(2,0);
if(data.bit(7)) {
if(!Model::GameBoyColor() && patternHold) {
//DMG,SGB trigger while channel is being read corrupts wave RAM
if((patternOffset >> 1) <= 3) {
//if current pattern is with 0-3; only byte 0 is corrupted
pattern[0] = pattern[patternOffset >> 1];
} else {
//if current pattern is within 4-15; pattern&~3 is copied to pattern[0-3]
pattern[0] = pattern[((patternOffset >> 1) & ~3) + 0];
pattern[1] = pattern[((patternOffset >> 1) & ~3) + 1];
pattern[2] = pattern[((patternOffset >> 1) & ~3) + 2];
pattern[3] = pattern[((patternOffset >> 1) & ~3) + 3];
}
}
enable = dacEnable;
period = 1 * (2048 - frequency);
patternOffset = 0;
patternSample = 0;
patternHold = 0;
if(!length) {
length = 256;
if(apu.phase.bit(0) && counter) length--;
}
}
}
if(addr >= 0xff30 && addr <= 0xff3f) {
if(enable) {
if(!Model::GameBoyColor() && !patternHold) return;
pattern[patternOffset >> 1] = data;
} else {
pattern[addr & 15] = data;
}
}
}
auto APU::Wave::power(bool initializeLength) -> void {
enable = 0;
dacEnable = 0;
volume = 0;
frequency = 0;
counter = 0;
output = 0;
period = 0;
patternOffset = 0;
patternSample = 0;
patternHold = 0;
if(initializeLength) length = 256;
}
auto APU::Wave::serialize(serializer& s) -> void {
s.integer(enable);
s.integer(dacEnable);
s.integer(volume);
s.integer(frequency);
s.integer(counter);
s.array(pattern);
s.integer(output);
s.integer(length);
s.integer(period);
s.integer(patternOffset);
s.integer(patternSample);
s.integer(patternHold);
}