mirror of https://github.com/bsnes-emu/bsnes.git
Update to v082r14 release.
byuu says: Emulates DMC channel (sound effect when Link gets hit in Zelda 1, for instance), fixes up bugs in rectangle/sweep and triangle channels, adds DMC/frame APU IRQs, adds proper stalling for DMC ROM reads (should even be cycle accurate, but has one extra cycle when triggered during OAM DMA, I think), but forgets the frame IRQ acknowledge clear on $4015 read (will fix next WIP.) All sound courtesy of Ryphecha. Made template configuration settings window (empty for now.) Simplified SNES cheat.cpp code. Some other stuff. Further developed RSI.
This commit is contained in:
parent
e3c7bbfb63
commit
7619805266
|
@ -1,35 +1,50 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct Buffer {
|
||||
double *sample[2];
|
||||
double **sample;
|
||||
uint16_t rdoffset;
|
||||
uint16_t wroffset;
|
||||
unsigned channels;
|
||||
|
||||
inline double& read(bool channel, signed offset = 0) {
|
||||
void setChannels(unsigned channels) {
|
||||
for(unsigned c = 0; c < this->channels; c++) {
|
||||
if(sample[c]) delete[] sample[c];
|
||||
}
|
||||
if(sample) delete[] sample;
|
||||
|
||||
this->channels = channels;
|
||||
if(channels == 0) return;
|
||||
|
||||
sample = new double*[channels];
|
||||
for(unsigned c = 0; c < channels; c++) {
|
||||
sample[c] = new double[65536]();
|
||||
}
|
||||
}
|
||||
|
||||
inline double& read(unsigned channel, signed offset = 0) {
|
||||
return sample[channel][(uint16_t)(rdoffset + offset)];
|
||||
}
|
||||
|
||||
inline double& write(bool channel, signed offset = 0) {
|
||||
inline double& write(unsigned channel, signed offset = 0) {
|
||||
return sample[channel][(uint16_t)(wroffset + offset)];
|
||||
}
|
||||
|
||||
inline void clear() {
|
||||
for(unsigned n = 0; n < 65536; n++) {
|
||||
sample[0][n] = 0;
|
||||
sample[1][n] = 0;
|
||||
for(unsigned c = 0; c < channels; c++) {
|
||||
for(unsigned n = 0; n < 65536; n++) {
|
||||
sample[c][n] = 0;
|
||||
}
|
||||
}
|
||||
rdoffset = 0;
|
||||
wroffset = 0;
|
||||
}
|
||||
|
||||
Buffer() {
|
||||
sample[0] = new double[65536];
|
||||
sample[1] = new double[65536];
|
||||
channels = 0;
|
||||
}
|
||||
|
||||
~Buffer() {
|
||||
delete[] sample[0];
|
||||
delete[] sample[1];
|
||||
setChannels(0);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ struct DSP {
|
|||
Average,
|
||||
};
|
||||
|
||||
inline void setChannels(unsigned channels);
|
||||
inline void setPrecision(unsigned precision);
|
||||
inline void setFrequency(double frequency); //inputFrequency
|
||||
inline void setVolume(double volume);
|
||||
|
@ -23,9 +24,9 @@ struct DSP {
|
|||
inline void setResampler(Resampler resampler);
|
||||
inline void setResamplerFrequency(double frequency); //outputFrequency
|
||||
|
||||
inline void sample(signed lchannel, signed rchannel);
|
||||
inline void sample(signed channel[]);
|
||||
inline bool pending();
|
||||
inline void read(signed &lchannel, signed &rchannel);
|
||||
inline void read(signed channel[]);
|
||||
|
||||
inline void clear();
|
||||
inline DSP();
|
||||
|
@ -33,6 +34,7 @@ struct DSP {
|
|||
|
||||
protected:
|
||||
struct Settings {
|
||||
unsigned channels;
|
||||
unsigned precision;
|
||||
double frequency;
|
||||
double volume;
|
||||
|
@ -50,7 +52,7 @@ protected:
|
|||
} resampler;
|
||||
|
||||
inline void resamplerRun();
|
||||
inline void resamplerWrite(double lchannel, double rchannel);
|
||||
inline void resamplerWrite(double channel[]);
|
||||
|
||||
inline void resamplePoint();
|
||||
inline void resampleLinear();
|
||||
|
@ -70,9 +72,10 @@ protected:
|
|||
|
||||
#include "settings.hpp"
|
||||
|
||||
void DSP::sample(signed lchannel, signed rchannel) {
|
||||
buffer.write(0) = (double)lchannel / settings.intensity;
|
||||
buffer.write(1) = (double)rchannel / settings.intensity;
|
||||
void DSP::sample(signed channel[]) {
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
buffer.write(c) = (double)channel[c] / settings.intensity;
|
||||
}
|
||||
buffer.wroffset++;
|
||||
resamplerRun();
|
||||
}
|
||||
|
@ -81,12 +84,13 @@ bool DSP::pending() {
|
|||
return output.rdoffset != output.wroffset;
|
||||
}
|
||||
|
||||
void DSP::read(signed &lchannel, signed &rchannel) {
|
||||
void DSP::read(signed channel[]) {
|
||||
adjustVolume();
|
||||
adjustBalance();
|
||||
|
||||
lchannel = clamp(settings.precision, output.read(0) * settings.intensity);
|
||||
rchannel = clamp(settings.precision, output.read(1) * settings.intensity);
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
channel[c] = clamp(settings.precision, output.read(0) * settings.intensity);
|
||||
}
|
||||
output.rdoffset++;
|
||||
}
|
||||
|
||||
|
@ -101,9 +105,10 @@ void DSP::resamplerRun() {
|
|||
}
|
||||
}
|
||||
|
||||
void DSP::resamplerWrite(double lchannel, double rchannel) {
|
||||
output.write(0) = lchannel;
|
||||
output.write(1) = rchannel;
|
||||
void DSP::resamplerWrite(double channel[]) {
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
output.write(c) = channel[c];
|
||||
}
|
||||
output.wroffset++;
|
||||
}
|
||||
|
||||
|
@ -115,11 +120,13 @@ void DSP::resamplerWrite(double lchannel, double rchannel) {
|
|||
#include "resample/average.hpp"
|
||||
|
||||
void DSP::adjustVolume() {
|
||||
output.read(0) *= settings.volume;
|
||||
output.read(1) *= settings.volume;
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
output.read(c) *= settings.volume;
|
||||
}
|
||||
}
|
||||
|
||||
void DSP::adjustBalance() {
|
||||
if(settings.channels != 2) return; //TODO: support > 2 channels
|
||||
if(settings.balance < 0.0) output.read(1) *= 1.0 + settings.balance;
|
||||
if(settings.balance > 0.0) output.read(0) *= 1.0 - settings.balance;
|
||||
}
|
||||
|
@ -137,6 +144,7 @@ void DSP::clear() {
|
|||
}
|
||||
|
||||
DSP::DSP() {
|
||||
setChannels(2);
|
||||
setPrecision(16);
|
||||
setFrequency(44100.0);
|
||||
setVolume(1.0);
|
||||
|
|
|
@ -9,17 +9,20 @@ void DSP::resampleAverage() {
|
|||
double scalar = 1.0;
|
||||
if(resampler.fraction > resampler.step) scalar = 1.0 - (resampler.fraction - resampler.step);
|
||||
|
||||
output.write(0) += buffer.read(0) * scalar;
|
||||
output.write(1) += buffer.read(1) * scalar;
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
output.write(c) += buffer.read(c) * scalar;
|
||||
}
|
||||
|
||||
if(resampler.fraction >= resampler.step) {
|
||||
output.write(0) /= resampler.step;
|
||||
output.write(1) /= resampler.step;
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
output.write(c) /= resampler.step;
|
||||
}
|
||||
output.wroffset++;
|
||||
|
||||
resampler.fraction -= resampler.step;
|
||||
output.write(0) = buffer.read(0) * resampler.fraction;
|
||||
output.write(1) = buffer.read(1) * resampler.fraction;
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
output.write(c) = buffer.read(c) * resampler.fraction;
|
||||
}
|
||||
}
|
||||
|
||||
buffer.rdoffset++;
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
void DSP::resampleCosine() {
|
||||
while(resampler.fraction <= 1.0) {
|
||||
double channel[2];
|
||||
double channel[settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < 2; n++) {
|
||||
for(unsigned n = 0; n < settings.channels; n++) {
|
||||
double a = buffer.read(n, -1);
|
||||
double b = buffer.read(n, -0);
|
||||
|
||||
|
@ -14,7 +14,7 @@ void DSP::resampleCosine() {
|
|||
channel[n] = a * (1.0 - mu) + b * mu;
|
||||
}
|
||||
|
||||
resamplerWrite(channel[0], channel[1]);
|
||||
resamplerWrite(channel);
|
||||
resampler.fraction += resampler.step;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
void DSP::resampleCubic() {
|
||||
while(resampler.fraction <= 1.0) {
|
||||
double channel[2];
|
||||
double channel[settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < 2; n++) {
|
||||
for(unsigned n = 0; n < settings.channels; n++) {
|
||||
double a = buffer.read(n, -3);
|
||||
double b = buffer.read(n, -2);
|
||||
double c = buffer.read(n, -1);
|
||||
|
@ -20,7 +20,7 @@ void DSP::resampleCubic() {
|
|||
channel[n] = A * (mu * 3) + B * (mu * 2) + C * mu + D;
|
||||
}
|
||||
|
||||
resamplerWrite(channel[0], channel[1]);
|
||||
resamplerWrite(channel);
|
||||
resampler.fraction += resampler.step;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
void DSP::resampleHermite() {
|
||||
while(resampler.fraction <= 1.0) {
|
||||
double channel[2];
|
||||
double channel[settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < 2; n++) {
|
||||
for(unsigned n = 0; n < settings.channels; n++) {
|
||||
double a = buffer.read(n, -3);
|
||||
double b = buffer.read(n, -2);
|
||||
double c = buffer.read(n, -1);
|
||||
|
@ -32,7 +32,7 @@ void DSP::resampleHermite() {
|
|||
channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||
}
|
||||
|
||||
resamplerWrite(channel[0], channel[1]);
|
||||
resamplerWrite(channel);
|
||||
resampler.fraction += resampler.step;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
void DSP::resampleLinear() {
|
||||
while(resampler.fraction <= 1.0) {
|
||||
double channel[2];
|
||||
double channel[settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < 2; n++) {
|
||||
for(unsigned n = 0; n < settings.channels; n++) {
|
||||
double a = buffer.read(n, -1);
|
||||
double b = buffer.read(n, -0);
|
||||
|
||||
|
@ -13,7 +13,7 @@ void DSP::resampleLinear() {
|
|||
channel[n] = a * (1.0 - mu) + b * mu;
|
||||
}
|
||||
|
||||
resamplerWrite(channel[0], channel[1]);
|
||||
resamplerWrite(channel);
|
||||
resampler.fraction += resampler.step;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
void DSP::resamplePoint() {
|
||||
while(resampler.fraction <= 1.0) {
|
||||
double channel[2];
|
||||
double channel[settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < 2; n++) {
|
||||
for(unsigned n = 0; n < settings.channels; n++) {
|
||||
double a = buffer.read(n, -1);
|
||||
double b = buffer.read(n, -0);
|
||||
|
||||
|
@ -13,7 +13,7 @@ void DSP::resamplePoint() {
|
|||
channel[n] = mu < 0.5 ? a : b;
|
||||
}
|
||||
|
||||
resamplerWrite(channel[0], channel[1]);
|
||||
resamplerWrite(channel);
|
||||
resampler.fraction += resampler.step;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
void DSP::setChannels(unsigned channels) {
|
||||
assert(channels > 0);
|
||||
buffer.setChannels(channels);
|
||||
output.setChannels(channels);
|
||||
settings.channels = channels;
|
||||
}
|
||||
|
||||
void DSP::setPrecision(unsigned precision) {
|
||||
settings.precision = precision;
|
||||
settings.intensity = 1 << (settings.precision - 1);
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace nall {
|
|||
inline operator bool() const { return valid; }
|
||||
inline const T& operator()() const { if(!valid) throw; return value; }
|
||||
inline optional<T>& operator=(const optional<T> &source) { valid = source.valid; value = source.value; return *this; }
|
||||
inline optional() : valid(false) {}
|
||||
inline optional(bool valid, const T &value) : valid(valid), value(value) {}
|
||||
};
|
||||
|
||||
|
|
|
@ -53,7 +53,21 @@ void APU::tick() {
|
|||
if(clock >= 0) co_switch(cpu.thread);
|
||||
}
|
||||
|
||||
void APU::set_irq_line() {
|
||||
cpu.set_irq_apu_line(frame.irq_pending || dmc.irq_pending);
|
||||
}
|
||||
|
||||
void APU::power() {
|
||||
for(unsigned n = 0; n < 2; n++) {
|
||||
rectangle[n].sweep.shift = 0;
|
||||
rectangle[n].sweep.decrement = 0;
|
||||
rectangle[n].sweep.period = 0;
|
||||
rectangle[n].sweep.counter = 1;
|
||||
rectangle[n].sweep.enable = 0;
|
||||
rectangle[n].sweep.reload = 0;
|
||||
rectangle[n].sweep.rectangle_period = 0;
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
|
@ -70,14 +84,6 @@ void APU::reset() {
|
|||
rectangle[n].envelope.decay_counter = 0;
|
||||
rectangle[n].envelope.decay_volume = 0;
|
||||
|
||||
rectangle[n].sweep.shift = 0;
|
||||
rectangle[n].sweep.decrement = 0;
|
||||
rectangle[n].sweep.period = 0;
|
||||
rectangle[n].sweep.counter = 0;
|
||||
rectangle[n].sweep.enable = 0;
|
||||
rectangle[n].sweep.reload = 0;
|
||||
rectangle[n].sweep.rectangle_period = 0;
|
||||
|
||||
rectangle[n].duty = 0;
|
||||
rectangle[n].duty_counter = 0;
|
||||
rectangle[n].period = 0;
|
||||
|
@ -108,27 +114,44 @@ void APU::reset() {
|
|||
noise.short_mode = 0;
|
||||
noise.lfsr = 1;
|
||||
|
||||
dmc.irq = 0;
|
||||
dmc.loop = 0;
|
||||
dmc.length_counter = 0;
|
||||
dmc.irq_pending = 0;
|
||||
|
||||
dmc.period = 0;
|
||||
dmc.counter = 0;
|
||||
dmc.addr = 0xc000;
|
||||
dmc.length = 1;
|
||||
dmc.period_counter = ntsc_dmc_period_table[0];
|
||||
dmc.irq_enable = 0;
|
||||
dmc.loop_mode = 0;
|
||||
dmc.dac_latch = 0;
|
||||
dmc.addr_latch = 0;
|
||||
dmc.length_latch = 0;
|
||||
dmc.read_addr = 0;
|
||||
dmc.dma_delay_counter = 0;
|
||||
dmc.bit_counter = 0;
|
||||
dmc.have_dma_buffer = 0;
|
||||
dmc.dma_buffer = 0;
|
||||
dmc.have_sample = 0;
|
||||
dmc.sample = 0;
|
||||
|
||||
frame.irq_pending = 0;
|
||||
|
||||
frame.mode = 0;
|
||||
frame.counter = 0;
|
||||
frame.divider = 1;
|
||||
|
||||
enabled_channels = 0;
|
||||
//cpu.set_alu_irq_line(frame.irq_pending || dmc.irq_pending);
|
||||
}
|
||||
|
||||
uint8 APU::read(uint16 addr) {
|
||||
if(addr == 0x4015) {
|
||||
uint8 result = 0x00;
|
||||
result |= rectangle[0].length_counter ? 1 : 0;
|
||||
result |= rectangle[1].length_counter ? 2 : 0;
|
||||
result |= triangle.length_counter ? 4 : 0;
|
||||
result |= noise.length_counter ? 8 : 0;
|
||||
result |= rectangle[0].length_counter ? 0x01 : 0;
|
||||
result |= rectangle[1].length_counter ? 0x02 : 0;
|
||||
result |= triangle.length_counter ? 0x04 : 0;
|
||||
result |= noise.length_counter ? 0x08 : 0;
|
||||
result |= dmc.length_counter ? 0x10 : 0;
|
||||
result |= frame.irq_pending ? 0x40 : 0;
|
||||
result |= dmc.irq_pending ? 0x80 : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -148,7 +171,7 @@ void APU::write(uint16 addr, uint8 data) {
|
|||
|
||||
case 0x4001: case 0x4005:
|
||||
rectangle[r].sweep.enable = data & 0x80;
|
||||
rectangle[r].sweep.period = (data >> 4) & 7;
|
||||
rectangle[r].sweep.period = (data & 0x70) >> 4;
|
||||
rectangle[r].sweep.decrement = data & 0x08;
|
||||
rectangle[r].sweep.shift = data & 0x07;
|
||||
rectangle[r].sweep.reload = true;
|
||||
|
@ -216,20 +239,24 @@ void APU::write(uint16 addr, uint8 data) {
|
|||
break;
|
||||
|
||||
case 0x4010:
|
||||
dmc.irq = data & 0x80;
|
||||
dmc.loop = data & 0x40;
|
||||
dmc.period = ntsc_dmc_period_table[data & 0x0f];
|
||||
dmc.irq_enable = data & 0x80;
|
||||
dmc.loop_mode = data & 0x40;
|
||||
dmc.period = data & 0x0f;
|
||||
|
||||
dmc.irq_pending = dmc.irq_pending && dmc.irq_enable && !dmc.loop_mode;
|
||||
set_irq_line();
|
||||
break;
|
||||
|
||||
case 0x4011:
|
||||
dmc.counter = data & 0x7f;
|
||||
dmc.dac_latch = data & 0x7f;
|
||||
break;
|
||||
|
||||
case 0x4012:
|
||||
dmc.addr = 0xc000 | (data << 6);
|
||||
dmc.addr_latch = data;
|
||||
break;
|
||||
|
||||
case 0x4013:
|
||||
dmc.length = (data << 4) | 1;
|
||||
dmc.length_latch = data;
|
||||
break;
|
||||
|
||||
case 0x4015:
|
||||
|
@ -237,6 +264,12 @@ void APU::write(uint16 addr, uint8 data) {
|
|||
if((data & 0x02) == 0) rectangle[1].length_counter = 0;
|
||||
if((data & 0x04) == 0) triangle.length_counter = 0;
|
||||
if((data & 0x08) == 0) noise.length_counter = 0;
|
||||
if((data & 0x10) == 0) dmc.length_counter = 0;
|
||||
|
||||
if((data & 0x10) && dmc.length_counter == 0) dmc.start();
|
||||
dmc.irq_pending = false;
|
||||
set_irq_line();
|
||||
|
||||
enabled_channels = data & 0x1f;
|
||||
break;
|
||||
|
||||
|
@ -245,20 +278,34 @@ void APU::write(uint16 addr, uint8 data) {
|
|||
|
||||
frame.counter = 0;
|
||||
if(frame.mode & 2) clock_frame_counter();
|
||||
frame.divider = 14915;
|
||||
if(frame.mode & 1) {
|
||||
frame.irq_pending = false;
|
||||
set_irq_line();
|
||||
}
|
||||
frame.divider = FrameCounter::NtscPeriod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Sweep::clock() {
|
||||
bool APU::Sweep::check_period() {
|
||||
if(rectangle_period > 0x7ff) return false;
|
||||
|
||||
if(decrement == 0) {
|
||||
if((rectangle_period + (rectangle_period >> shift)) & 0x800) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void APU::Sweep::clock(unsigned channel) {
|
||||
if(--counter == 0) {
|
||||
counter = period + 1;
|
||||
if(enable & shift && rectangle_period > 8) {
|
||||
if(enable && shift && rectangle_period > 8) {
|
||||
signed delta = rectangle_period >> shift;
|
||||
|
||||
if(decrement) {
|
||||
//first rectangle decrements by one extra
|
||||
rectangle_period -= delta + (channel == 0);
|
||||
rectangle_period -= delta;
|
||||
if(channel == 0) rectangle_period--;
|
||||
} else if((rectangle_period + delta) < 0x800) {
|
||||
rectangle_period += delta;
|
||||
}
|
||||
|
@ -272,7 +319,7 @@ void APU::Sweep::clock() {
|
|||
}
|
||||
|
||||
unsigned APU::Envelope::volume() const {
|
||||
return use_speed_as_volume ? speed :decay_volume;
|
||||
return use_speed_as_volume ? speed : decay_volume;
|
||||
}
|
||||
|
||||
void APU::Envelope::clock() {
|
||||
|
@ -295,24 +342,13 @@ void APU::Rectangle::clock_length() {
|
|||
}
|
||||
}
|
||||
|
||||
bool APU::Rectangle::check_period() {
|
||||
const unsigned raw_period = sweep.rectangle_period;
|
||||
|
||||
if(raw_period < 0x008 || raw_period > 0x7ff) return false;
|
||||
|
||||
if(sweep.decrement == 0) {
|
||||
if((raw_period + (raw_period >> sweep.shift)) & 0x800) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8 APU::Rectangle::clock() {
|
||||
if(check_period() == false) return 0;
|
||||
if(sweep.check_period() == false) return 0;
|
||||
if(length_counter == 0) return 0;
|
||||
|
||||
static const unsigned duty_table[] = { 1, 2, 4, 6 };
|
||||
uint8 result = (duty_counter < duty_table[duty]) ? envelope.volume() : 0;
|
||||
if(sweep.rectangle_period < 0x008) result = 0;
|
||||
|
||||
if(--period_counter == 0) {
|
||||
period_counter = (sweep.rectangle_period + 1) * 2;
|
||||
|
@ -340,11 +376,11 @@ void APU::Triangle::clock_linear_length() {
|
|||
|
||||
uint8 APU::Triangle::clock() {
|
||||
uint8 result = step_counter & 0x0f;
|
||||
if(step_counter & 0x10) result ^= 0x0f;
|
||||
if((step_counter & 0x10) == 0) result ^= 0x0f;
|
||||
if(length_counter == 0 || linear_length_counter == 0) return result;
|
||||
|
||||
if(--period_counter == 0) {
|
||||
step_counter = (step_counter + 1) & 0x1f;
|
||||
step_counter++;
|
||||
period_counter = period + 1;
|
||||
}
|
||||
|
||||
|
@ -378,8 +414,63 @@ uint8 APU::Noise::clock() {
|
|||
return result;
|
||||
}
|
||||
|
||||
void APU::DMC::start() {
|
||||
read_addr = 0x4000 + (addr_latch << 6);
|
||||
length_counter = (length_latch << 4) + 1;
|
||||
}
|
||||
|
||||
uint8 APU::DMC::clock() {
|
||||
uint8 result = counter;
|
||||
uint8 result = dac_latch;
|
||||
|
||||
if(dma_delay_counter > 0) {
|
||||
dma_delay_counter--;
|
||||
|
||||
if(dma_delay_counter == 1) {
|
||||
cpu.set_rdy_addr({ true, uint16(0x8000 | read_addr) });
|
||||
} else if(dma_delay_counter == 0) {
|
||||
cpu.set_rdy_line(1);
|
||||
cpu.set_rdy_addr({ false, 0u });
|
||||
|
||||
dma_buffer = cpu.mdr();
|
||||
have_dma_buffer = true;
|
||||
length_counter--;
|
||||
read_addr++;
|
||||
|
||||
if(length_counter == 0) {
|
||||
if(loop_mode) {
|
||||
start();
|
||||
} else if(irq_enable) {
|
||||
irq_pending = true;
|
||||
apu.set_irq_line();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(--period_counter == 0) {
|
||||
if(have_sample) {
|
||||
signed delta = (((sample >> bit_counter) & 1) << 2) - 2;
|
||||
unsigned data = dac_latch + delta;
|
||||
if((data & 0x80) == 0) dac_latch = data;
|
||||
}
|
||||
|
||||
if(++bit_counter == 0) {
|
||||
if(have_dma_buffer) {
|
||||
have_sample = true;
|
||||
sample = dma_buffer;
|
||||
have_dma_buffer = false;
|
||||
} else {
|
||||
have_sample = false;
|
||||
}
|
||||
}
|
||||
|
||||
period_counter = ntsc_dmc_period_table[period];
|
||||
}
|
||||
|
||||
if(length_counter > 0 && have_dma_buffer == false && dma_delay_counter == 0) {
|
||||
cpu.set_rdy_line(0);
|
||||
dma_delay_counter = 4;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -389,9 +480,9 @@ void APU::clock_frame_counter() {
|
|||
|
||||
if(frame.counter & 1) {
|
||||
rectangle[0].clock_length();
|
||||
rectangle[0].sweep.clock();
|
||||
rectangle[0].sweep.clock(0);
|
||||
rectangle[1].clock_length();
|
||||
rectangle[1].sweep.clock();
|
||||
rectangle[1].sweep.clock(1);
|
||||
triangle.clock_length();
|
||||
noise.clock_length();
|
||||
}
|
||||
|
@ -402,7 +493,11 @@ void APU::clock_frame_counter() {
|
|||
noise.envelope.clock();
|
||||
|
||||
if(frame.counter == 0) {
|
||||
if(frame.mode & 2) frame.divider += 14195;
|
||||
if(frame.mode & 2) frame.divider += FrameCounter::NtscPeriod;
|
||||
if(frame.mode == 0) {
|
||||
frame.irq_pending = true;
|
||||
set_irq_line();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -410,14 +505,11 @@ void APU::clock_frame_counter_divider() {
|
|||
frame.divider -= 2;
|
||||
if(frame.divider <= 0) {
|
||||
clock_frame_counter();
|
||||
frame.divider += 14195;
|
||||
frame.divider += FrameCounter::NtscPeriod;
|
||||
}
|
||||
}
|
||||
|
||||
APU::APU() {
|
||||
rectangle[0].sweep.channel = 0;
|
||||
rectangle[1].sweep.channel = 1;
|
||||
|
||||
for(unsigned amp = 0; amp < 32; amp++) {
|
||||
if(amp == 0) {
|
||||
rectangle_dac[amp] = 0;
|
||||
|
|
|
@ -2,6 +2,7 @@ struct APU : Processor {
|
|||
static void Main();
|
||||
void main();
|
||||
void tick();
|
||||
void set_irq_line();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
@ -25,8 +26,6 @@ struct APU : Processor {
|
|||
};
|
||||
|
||||
struct Sweep {
|
||||
bool channel;
|
||||
|
||||
uint8 shift;
|
||||
bool decrement;
|
||||
uint3 period;
|
||||
|
@ -35,7 +34,8 @@ struct APU : Processor {
|
|||
bool reload;
|
||||
unsigned rectangle_period;
|
||||
|
||||
void clock();
|
||||
bool check_period();
|
||||
void clock(unsigned channel);
|
||||
};
|
||||
|
||||
struct Rectangle {
|
||||
|
@ -64,7 +64,7 @@ struct APU : Processor {
|
|||
uint11 period;
|
||||
unsigned period_counter;
|
||||
|
||||
uint8 step_counter;
|
||||
uint5 step_counter;
|
||||
uint8 linear_length_counter;
|
||||
bool reload_linear;
|
||||
|
||||
|
@ -89,18 +89,38 @@ struct APU : Processor {
|
|||
} noise;
|
||||
|
||||
struct DMC {
|
||||
bool irq;
|
||||
bool loop;
|
||||
unsigned period;
|
||||
unsigned length_counter;
|
||||
bool irq_pending;
|
||||
|
||||
uint8 counter;
|
||||
uint16 addr;
|
||||
uint16 length;
|
||||
uint4 period;
|
||||
unsigned period_counter;
|
||||
|
||||
bool irq_enable;
|
||||
bool loop_mode;
|
||||
|
||||
uint8 dac_latch;
|
||||
uint8 addr_latch;
|
||||
uint8 length_latch;
|
||||
|
||||
uint15 read_addr;
|
||||
unsigned dma_delay_counter;
|
||||
|
||||
uint3 bit_counter;
|
||||
bool have_dma_buffer;
|
||||
uint8 dma_buffer;
|
||||
|
||||
bool have_sample;
|
||||
uint8 sample;
|
||||
|
||||
void start();
|
||||
uint8 clock();
|
||||
} dmc;
|
||||
|
||||
struct FrameCounter {
|
||||
enum : unsigned { NtscPeriod = 14915 }; //~(21.477MHz / 6 / 240hz)
|
||||
|
||||
bool irq_pending;
|
||||
|
||||
uint2 mode;
|
||||
uint2 counter;
|
||||
signed divider;
|
||||
|
|
|
@ -35,6 +35,7 @@ void interrupt();
|
|||
void interrupt_test();
|
||||
void set_nmi_line(bool);
|
||||
void set_irq_line(bool);
|
||||
void set_irq_apu_line(bool);
|
||||
|
||||
//opcodes.cpp
|
||||
void opf_asl();
|
||||
|
|
|
@ -17,7 +17,7 @@ L abs.h = op_read(vector++);
|
|||
}
|
||||
|
||||
void CPU::interrupt_test() {
|
||||
status.interrupt_pending = (status.irq_line & ~regs.p.i) | status.nmi_pending;
|
||||
status.interrupt_pending = ((status.irq_line | status.irq_apu_line) & ~regs.p.i) | status.nmi_pending;
|
||||
}
|
||||
|
||||
void CPU::set_nmi_line(bool line) {
|
||||
|
@ -30,3 +30,8 @@ void CPU::set_irq_line(bool line) {
|
|||
//level-sensitive
|
||||
status.irq_line = line;
|
||||
}
|
||||
|
||||
void CPU::set_irq_apu_line(bool line) {
|
||||
//level-sensitive
|
||||
status.irq_apu_line = line;
|
||||
}
|
||||
|
|
|
@ -70,6 +70,13 @@ void CPU::reset() {
|
|||
status.nmi_pending = false;
|
||||
status.nmi_line = 0;
|
||||
status.irq_line = 0;
|
||||
status.irq_apu_line = 0;
|
||||
|
||||
status.rdy_line = 1;
|
||||
status.rdy_addr = { false, 0x0000 };
|
||||
|
||||
status.oam_dma_pending = false;
|
||||
status.oam_dma_page = 0x00;
|
||||
|
||||
status.controller_latch = false;
|
||||
status.controller_port0 = 0;
|
||||
|
@ -80,6 +87,14 @@ uint8 CPU::mdr() const {
|
|||
return regs.mdr;
|
||||
}
|
||||
|
||||
void CPU::set_rdy_line(bool line) {
|
||||
status.rdy_line = line;
|
||||
}
|
||||
|
||||
void CPU::set_rdy_addr(optional<uint16> addr) {
|
||||
status.rdy_addr = addr;
|
||||
}
|
||||
|
||||
uint8 CPU::ram_read(uint16 addr) {
|
||||
return ram[addr & 0x07ff];
|
||||
}
|
||||
|
@ -104,7 +119,8 @@ uint8 CPU::read(uint16 addr) {
|
|||
|
||||
void CPU::write(uint16 addr, uint8 data) {
|
||||
if(addr == 0x4014) {
|
||||
return oam_dma(data << 8);
|
||||
status.oam_dma_page = data;
|
||||
status.oam_dma_pending = true;
|
||||
}
|
||||
|
||||
if(addr == 0x4016) {
|
||||
|
@ -118,10 +134,9 @@ void CPU::write(uint16 addr, uint8 data) {
|
|||
return apu.write(addr, data);
|
||||
}
|
||||
|
||||
void CPU::oam_dma(uint16 addr) {
|
||||
op_readpc();
|
||||
void CPU::oam_dma() {
|
||||
for(unsigned n = 0; n < 256; n++) {
|
||||
uint8 data = op_read(addr + n);
|
||||
uint8 data = op_read((status.oam_dma_page << 8) + n);
|
||||
op_write(0x2004, data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,13 @@ struct CPU : Processor {
|
|||
bool nmi_pending;
|
||||
bool nmi_line;
|
||||
bool irq_line;
|
||||
bool irq_apu_line;
|
||||
|
||||
bool rdy_line;
|
||||
optional<uint16> rdy_addr;
|
||||
|
||||
bool oam_dma_pending;
|
||||
uint8 oam_dma_page;
|
||||
|
||||
bool controller_latch;
|
||||
unsigned controller_port0;
|
||||
|
@ -22,6 +29,8 @@ struct CPU : Processor {
|
|||
void reset();
|
||||
|
||||
uint8 mdr() const;
|
||||
void set_rdy_line(bool);
|
||||
void set_rdy_addr(optional<uint16>);
|
||||
|
||||
uint8 ram_read(uint16 addr);
|
||||
void ram_write(uint16 addr, uint8 data);
|
||||
|
@ -29,7 +38,7 @@ struct CPU : Processor {
|
|||
uint8 read(uint16 addr);
|
||||
void write(uint16 addr, uint8 data);
|
||||
|
||||
void oam_dma(uint16 addr);
|
||||
void oam_dma();
|
||||
|
||||
bool trace;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,15 @@
|
|||
uint8 CPU::op_read(uint16 addr) {
|
||||
if(status.oam_dma_pending) {
|
||||
status.oam_dma_pending = false;
|
||||
op_read(addr);
|
||||
oam_dma();
|
||||
}
|
||||
|
||||
while(status.rdy_line == 0) {
|
||||
regs.mdr = bus.read(status.rdy_addr ? status.rdy_addr() : addr);
|
||||
add_clocks(12);
|
||||
}
|
||||
|
||||
regs.mdr = bus.read(addr);
|
||||
add_clocks(12);
|
||||
return regs.mdr;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
namespace NES {
|
||||
namespace Info {
|
||||
static const char Name[] = "bnes";
|
||||
static const char Version[] = "000.09";
|
||||
static const char Version[] = "000.10";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,13 +28,13 @@ void Audio::sample(int16 left, int16 right) {
|
|||
}
|
||||
}
|
||||
|
||||
void Audio::coprocessor_sample(int16 left, int16 right) {
|
||||
dspaudio.sample(left, right);
|
||||
void Audio::coprocessor_sample(int16 lsample, int16 rsample) {
|
||||
signed samples[] = { lsample, rsample };
|
||||
dspaudio.sample(samples);
|
||||
while(dspaudio.pending()) {
|
||||
signed left, right;
|
||||
dspaudio.read(left, right);
|
||||
dspaudio.read(samples);
|
||||
|
||||
cop_buffer[cop_wroffset] = ((uint16)left << 0) + ((uint16)right << 16);
|
||||
cop_buffer[cop_wroffset] = ((uint16)samples[0] << 0) + ((uint16)samples[1] << 16);
|
||||
cop_wroffset = (cop_wroffset + 1) & buffer_mask;
|
||||
cop_length = (cop_length + 1) & buffer_mask;
|
||||
flush();
|
||||
|
|
|
@ -65,51 +65,31 @@ Cheat::~Cheat() {
|
|||
delete[] override;
|
||||
}
|
||||
|
||||
//===============
|
||||
//encode / decode
|
||||
//===============
|
||||
|
||||
bool Cheat::decode(const char *s, unsigned &addr, unsigned &data, Type &type) {
|
||||
string t = s;
|
||||
bool Cheat::decode(const string &code, unsigned &addr, unsigned &data) {
|
||||
string t = code;
|
||||
t.lower();
|
||||
|
||||
#define ischr(n) ((n >= '0' && n <= '9') || (n >= 'a' && n <= 'f'))
|
||||
|
||||
if(strlen(t) == 8 || (strlen(t) == 9 && t[6] == ':')) {
|
||||
//strip ':'
|
||||
if(strlen(t) == 9 && t[6] == ':') t = { substr(t, 0, 6), substr(t, 7) };
|
||||
//validate input
|
||||
for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false;
|
||||
//Pro Action Replay
|
||||
if(strlen(t) == 9 && t[6] == ':') t = { substr(t, 0, 6), substr(t, 7) }; //strip ':'
|
||||
for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false; //validate input
|
||||
unsigned r = hex(t);
|
||||
|
||||
type = Type::ProActionReplay;
|
||||
unsigned r = hex((const char*)t);
|
||||
addr = r >> 8;
|
||||
data = r & 0xff;
|
||||
return true;
|
||||
} else if(strlen(t) == 9 && t[4] == '-') {
|
||||
//strip '-'
|
||||
t = { substr(t, 0, 4), substr(t, 5) };
|
||||
//validate input
|
||||
for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false;
|
||||
|
||||
type = Type::GameGenie;
|
||||
//Game Genie
|
||||
t = { substr(t, 0, 4), substr(t, 5) }; //strip '-'
|
||||
for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false; //validate input
|
||||
t.transform("df4709156bc8a23e", "0123456789abcdef");
|
||||
unsigned r = hex((const char*)t);
|
||||
//8421 8421 8421 8421 8421 8421
|
||||
//abcd efgh ijkl mnop qrst uvwx
|
||||
//ijkl qrst opab cduv wxef ghmn
|
||||
addr = (!!(r & 0x002000) << 23) | (!!(r & 0x001000) << 22)
|
||||
| (!!(r & 0x000800) << 21) | (!!(r & 0x000400) << 20)
|
||||
| (!!(r & 0x000020) << 19) | (!!(r & 0x000010) << 18)
|
||||
| (!!(r & 0x000008) << 17) | (!!(r & 0x000004) << 16)
|
||||
| (!!(r & 0x800000) << 15) | (!!(r & 0x400000) << 14)
|
||||
| (!!(r & 0x200000) << 13) | (!!(r & 0x100000) << 12)
|
||||
| (!!(r & 0x000002) << 11) | (!!(r & 0x000001) << 10)
|
||||
| (!!(r & 0x008000) << 9) | (!!(r & 0x004000) << 8)
|
||||
| (!!(r & 0x080000) << 7) | (!!(r & 0x040000) << 6)
|
||||
| (!!(r & 0x020000) << 5) | (!!(r & 0x010000) << 4)
|
||||
| (!!(r & 0x000200) << 3) | (!!(r & 0x000100) << 2)
|
||||
| (!!(r & 0x000080) << 1) | (!!(r & 0x000040) << 0);
|
||||
unsigned r = hex(t);
|
||||
static unsigned bits[] = { 13, 12, 11, 10, 5, 4, 3, 2, 23, 22, 21, 20, 1, 0, 15, 14, 19, 18, 17, 16, 9, 8, 7, 6 };
|
||||
|
||||
addr = 0;
|
||||
for(unsigned n = 0; n < 24; n++) addr |= r & (1 << bits[n]) ? 0x800000 >> n : 0;
|
||||
data = r >> 24;
|
||||
return true;
|
||||
} else {
|
||||
|
@ -119,38 +99,6 @@ bool Cheat::decode(const char *s, unsigned &addr, unsigned &data, Type &type) {
|
|||
#undef ischr
|
||||
}
|
||||
|
||||
bool Cheat::encode(string &s, unsigned addr, unsigned data, Type type) {
|
||||
char t[16];
|
||||
|
||||
if(type == Type::ProActionReplay) {
|
||||
s = string(hex<6>(addr), hex<2>(data));
|
||||
return true;
|
||||
} else if(type == Type::GameGenie) {
|
||||
unsigned r = addr;
|
||||
addr = (!!(r & 0x008000) << 23) | (!!(r & 0x004000) << 22)
|
||||
| (!!(r & 0x002000) << 21) | (!!(r & 0x001000) << 20)
|
||||
| (!!(r & 0x000080) << 19) | (!!(r & 0x000040) << 18)
|
||||
| (!!(r & 0x000020) << 17) | (!!(r & 0x000010) << 16)
|
||||
| (!!(r & 0x000200) << 15) | (!!(r & 0x000100) << 14)
|
||||
| (!!(r & 0x800000) << 13) | (!!(r & 0x400000) << 12)
|
||||
| (!!(r & 0x200000) << 11) | (!!(r & 0x100000) << 10)
|
||||
| (!!(r & 0x000008) << 9) | (!!(r & 0x000004) << 8)
|
||||
| (!!(r & 0x000002) << 7) | (!!(r & 0x000001) << 6)
|
||||
| (!!(r & 0x080000) << 5) | (!!(r & 0x040000) << 4)
|
||||
| (!!(r & 0x020000) << 3) | (!!(r & 0x010000) << 2)
|
||||
| (!!(r & 0x000800) << 1) | (!!(r & 0x000400) << 0);
|
||||
s = string(hex<2>(data), hex<2>(addr >> 16), "-", hex<4>(addr & 0xffff));
|
||||
s.transform("0123456789abcdef", "df4709156bc8a23e");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//========
|
||||
//internal
|
||||
//========
|
||||
|
||||
unsigned Cheat::mirror(unsigned addr) const {
|
||||
//$00-3f|80-bf:0000-1fff -> $7e:0000-1fff
|
||||
if((addr & 0x40e000) == 0x000000) return (0x7e0000 + (addr & 0x1fff));
|
||||
|
|
|
@ -5,7 +5,6 @@ struct CheatCode {
|
|||
|
||||
class Cheat : public linear_vector<CheatCode> {
|
||||
public:
|
||||
enum class Type : unsigned { ProActionReplay, GameGenie };
|
||||
uint8 *override;
|
||||
|
||||
bool enabled() const;
|
||||
|
@ -17,8 +16,7 @@ public:
|
|||
Cheat();
|
||||
~Cheat();
|
||||
|
||||
static bool decode(const char*, unsigned&, unsigned&, Type&);
|
||||
static bool encode(string&, unsigned, unsigned, Type);
|
||||
static bool decode(const string&, unsigned&, unsigned&);
|
||||
|
||||
private:
|
||||
bool system_enabled;
|
||||
|
|
|
@ -61,8 +61,7 @@ void Interface::setCheats(const lstring &list) {
|
|||
codelist.split("+", code);
|
||||
foreach(part, codelist) {
|
||||
unsigned addr, data;
|
||||
Cheat::Type type;
|
||||
if(Cheat::decode(part, addr, data, type)) {
|
||||
if(Cheat::decode(part, addr, data)) {
|
||||
cheat.append({ addr, data });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
namespace SNES {
|
||||
namespace Info {
|
||||
static const char Name[] = "bsnes";
|
||||
static const char Version[] = "082.13";
|
||||
static const char Version[] = "082.14";
|
||||
static const unsigned SerializerVersion = 22;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ include $(gameboy)/Makefile
|
|||
name := batch
|
||||
|
||||
ui_objects := ui-main ui-config ui-interface ui-utility
|
||||
ui_objects += ui-general ui-tools
|
||||
ui_objects += ui-general ui-settings ui-tools
|
||||
ui_objects += phoenix ruby
|
||||
ui_objects += $(if $(call streq,$(platform),win),resource)
|
||||
|
||||
|
@ -72,6 +72,7 @@ obj/ui-config.o: $(ui)/config/config.cpp $(call rwildcard,$(ui)/)
|
|||
obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-tools.o: $(ui)/tools/tools.cpp $(call rwildcard,$(ui)/)
|
||||
|
||||
obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/*)
|
||||
|
|
|
@ -23,6 +23,7 @@ using namespace ruby;
|
|||
#include "interface/interface.hpp"
|
||||
#include "utility/utility.hpp"
|
||||
#include "general/general.hpp"
|
||||
#include "settings/settings.hpp"
|
||||
#include "tools/tools.hpp"
|
||||
|
||||
struct Application {
|
||||
|
|
|
@ -30,6 +30,7 @@ MainWindow::MainWindow() {
|
|||
settingsSynchronizeAudio.setText("Synchronize Audio");
|
||||
settingsSynchronizeAudio.setChecked();
|
||||
settingsMuteAudio.setText("Mute Audio");
|
||||
settingsConfiguration.setText("Configuration ...");
|
||||
|
||||
toolsMenu.setText("Tools");
|
||||
toolsStateSave.setText("Save State");
|
||||
|
@ -78,6 +79,8 @@ MainWindow::MainWindow() {
|
|||
settingsMenu.append(settingsSynchronizeVideo);
|
||||
settingsMenu.append(settingsSynchronizeAudio);
|
||||
settingsMenu.append(settingsMuteAudio);
|
||||
settingsMenu.append(settingsSeparator);
|
||||
settingsMenu.append(settingsConfiguration);
|
||||
|
||||
append(toolsMenu);
|
||||
toolsMenu.append(toolsStateSave);
|
||||
|
@ -155,6 +158,8 @@ MainWindow::MainWindow() {
|
|||
dspaudio.setVolume(settingsMuteAudio.checked() ? 0.0 : 1.0);
|
||||
};
|
||||
|
||||
settingsConfiguration.onTick = [&] { settingsWindow->setVisible(); };
|
||||
|
||||
toolsStateSave1.onTick = [&] { interface->saveState({ interface->baseName, "-1.bst" }); };
|
||||
toolsStateSave2.onTick = [&] { interface->saveState({ interface->baseName, "-2.bst" }); };
|
||||
toolsStateSave3.onTick = [&] { interface->saveState({ interface->baseName, "-3.bst" }); };
|
||||
|
|
|
@ -28,6 +28,8 @@ struct MainWindow : Window {
|
|||
CheckItem settingsSynchronizeVideo;
|
||||
CheckItem settingsSynchronizeAudio;
|
||||
CheckItem settingsMuteAudio;
|
||||
Separator settingsSeparator;
|
||||
Item settingsConfiguration;
|
||||
|
||||
Menu toolsMenu;
|
||||
Menu toolsStateSave;
|
||||
|
|
|
@ -53,11 +53,11 @@ void InterfaceGameBoy::videoRefresh(const uint8_t *data) {
|
|||
}
|
||||
|
||||
void InterfaceGameBoy::audioSample(int16_t csample, int16_t lsample, int16_t rsample) {
|
||||
dspaudio.sample(lsample, rsample);
|
||||
signed samples[] = { lsample, rsample };
|
||||
dspaudio.sample(samples);
|
||||
while(dspaudio.pending()) {
|
||||
signed lsample, rsample;
|
||||
dspaudio.read(lsample, rsample);
|
||||
audio.sample(lsample, rsample);
|
||||
dspaudio.read(samples);
|
||||
audio.sample(samples[0], samples[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,11 +36,11 @@ void InterfaceNES::videoRefresh(const uint16_t *data) {
|
|||
}
|
||||
|
||||
void InterfaceNES::audioSample(int16_t sample) {
|
||||
dspaudio.sample(sample, sample);
|
||||
signed samples[] = { sample };
|
||||
dspaudio.sample(samples);
|
||||
while(dspaudio.pending()) {
|
||||
signed lsample, rsample;
|
||||
dspaudio.read(lsample, rsample);
|
||||
audio.sample(lsample, rsample);
|
||||
dspaudio.read(samples);
|
||||
audio.sample(samples[0], samples[0]); //NES audio output is monaural; ruby only takes stereo audio
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,11 +69,11 @@ void InterfaceSNES::videoRefresh(const uint16_t *data, bool hires, bool interlac
|
|||
}
|
||||
|
||||
void InterfaceSNES::audioSample(int16_t lsample, int16_t rsample) {
|
||||
dspaudio.sample(lsample, rsample);
|
||||
signed samples[] = { lsample, rsample };
|
||||
dspaudio.sample(samples);
|
||||
while(dspaudio.pending()) {
|
||||
signed lsample, rsample;
|
||||
dspaudio.read(lsample, rsample);
|
||||
audio.sample(lsample, rsample);
|
||||
dspaudio.read(samples);
|
||||
audio.sample(samples[0], samples[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ Application::Application(int argc, char **argv) : quit(false) {
|
|||
|
||||
mainWindow = new MainWindow;
|
||||
fileBrowser = new FileBrowser;
|
||||
settingsWindow = new SettingsWindow;
|
||||
cheatEditor = new CheatEditor;
|
||||
stateManager = new StateManager;
|
||||
utility->setMode(Interface::Mode::None);
|
||||
|
@ -82,6 +83,7 @@ Application::Application(int argc, char **argv) : quit(false) {
|
|||
Application::~Application() {
|
||||
delete stateManager;
|
||||
delete cheatEditor;
|
||||
delete settingsWindow;
|
||||
delete fileBrowser;
|
||||
delete mainWindow;
|
||||
delete utility;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#include "../base.hpp"
|
||||
SettingsWindow *settingsWindow = 0;
|
||||
|
||||
SettingsWindow::SettingsWindow() {
|
||||
setTitle("Configuration Settings");
|
||||
setGeometry({ 128, 128, 640, 360 });
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
struct SettingsWindow : Window {
|
||||
SettingsWindow();
|
||||
};
|
||||
|
||||
extern SettingsWindow *settingsWindow;
|
|
@ -18,18 +18,21 @@ void Utility::setMode(Interface::Mode mode) {
|
|||
else if(mode == Interface::Mode::NES) {
|
||||
mainWindow->setTitle({ notdir(interface->baseName), " - ", NES::Info::Name, " v", NES::Info::Version });
|
||||
mainWindow->nesMenu.setVisible(true);
|
||||
dspaudio.setChannels(1);
|
||||
dspaudio.setFrequency(315.0 / 88.8 * 6000000.0 / 12.0);
|
||||
}
|
||||
|
||||
else if(mode == Interface::Mode::SNES) {
|
||||
mainWindow->setTitle({ notdir(interface->baseName), " - ", SNES::Info::Name, " v", SNES::Info::Version });
|
||||
mainWindow->snesMenu.setVisible(true);
|
||||
dspaudio.setChannels(2);
|
||||
dspaudio.setFrequency(32040.0);
|
||||
}
|
||||
|
||||
else if(mode == Interface::Mode::GameBoy) {
|
||||
mainWindow->setTitle({ notdir(interface->baseName), " - ", GameBoy::Info::Name, " v", GameBoy::Info::Version });
|
||||
mainWindow->gameBoyMenu.setVisible(true);
|
||||
dspaudio.setChannels(2);
|
||||
dspaudio.setFrequency(4194304.0);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue