Update to v082r33 release.

byuu says:

Added MMC2, MMC4, VRC4, VRC7 (no audio.)
Split NES audio code up into individual modules.
Fixed libsnes to compile: Themaister, can you please test to make sure
it works? I don't have a libsnes client on my work PC to test it.
Added about / license information to bottom of advanced settings screen
for now (better than nothing, I guess.)
Blocked PPU reads/writes while rendering for now, easier than coming up
with a bus address locking thing :/

I can't seem to fix MMC5 graphics during the intro to Uchuu Keibitai.
Without that, trying to implement vertical-split screen mode doesn't
make sense.

So as far as special audio chips go ...
* VRC6 is completed
* Sunsoft 5B has everything the only game to use it uses, but there are
  more unused channels I'd like to support anyway (they aren't
  documented, though.)
* MMC5 audio unsupported for now
* VRC7 audio unsupported, probably for a long time (hardest audio driver
  of all. More complex than core NES APU.)
* audio PCM games (Moero Pro Yakyuu!) I probably won't ever support
  (they require external WAV packs.)
This commit is contained in:
Tim Allen 2011-10-12 23:03:58 +11:00
parent b8d607d16b
commit ef85f7ccb0
58 changed files with 1378 additions and 596 deletions

View File

@ -87,7 +87,7 @@ namespace GameBoy {
clock = 0;
}
inline Processor() : thread(0) {}
inline Processor() : thread(nullptr) {}
};
#include <gameboy/memory/memory.hpp>

View File

@ -2,6 +2,12 @@
namespace NES {
#include "envelope.cpp"
#include "sweep.cpp"
#include "pulse.cpp"
#include "triangle.cpp"
#include "noise.cpp"
#include "dmc.cpp"
#include "serialization.cpp"
APU apu;
@ -36,17 +42,17 @@ void APU::main() {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
unsigned rectangle_output, triangle_output, noise_output, dmc_output;
unsigned pulse_output, triangle_output, noise_output, dmc_output;
rectangle_output = rectangle[0].clock();
rectangle_output += rectangle[1].clock();
pulse_output = pulse[0].clock();
pulse_output += pulse[1].clock();
triangle_output = triangle.clock();
noise_output = noise.clock();
dmc_output = dmc.clock();
clock_frame_counter_divider();
signed output = rectangle_dac[rectangle_output] + dmc_triangle_noise_dac[dmc_output][triangle_output][noise_output];
signed output = pulse_dac[pulse_output] + dmc_triangle_noise_dac[dmc_output][triangle_output][noise_output];
output = filter.run_hipass_strong(output);
output += cartridge_sample;
@ -78,79 +84,21 @@ void APU::power() {
filter.hipass_weak = 0;
filter.lopass = 0;
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();
pulse[0].power();
pulse[1].power();
triangle.power();
noise.power();
dmc.power();
}
void APU::reset() {
Processor::create(APU::Main, 21477272);
for(unsigned n = 0; n < 2; n++) {
rectangle[n].length_counter = 0;
rectangle[n].envelope.speed = 0;
rectangle[n].envelope.use_speed_as_volume = 0;
rectangle[n].envelope.loop_mode = 0;
rectangle[n].envelope.reload_decay = 0;
rectangle[n].envelope.decay_counter = 0;
rectangle[n].envelope.decay_volume = 0;
rectangle[n].duty = 0;
rectangle[n].duty_counter = 0;
rectangle[n].period = 0;
rectangle[n].period_counter = 1;
}
triangle.length_counter = 0;
triangle.linear_length = 0;
triangle.halt_length_counter = 0;
triangle.period = 0;
triangle.period_counter = 1;
triangle.step_counter = 0;
triangle.linear_length_counter = 0;
triangle.reload_linear = 0;
noise.length_counter = 0;
noise.envelope.speed = 0;
noise.envelope.use_speed_as_volume = 0;
noise.envelope.loop_mode = 0;
noise.envelope.reload_decay = 0;
noise.envelope.decay_counter = 0;
noise.envelope.decay_volume = 0;
noise.period = 0;
noise.period_counter = 1;
noise.short_mode = 0;
noise.lfsr = 1;
dmc.length_counter = 0;
dmc.irq_pending = 0;
dmc.period = 0;
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;
pulse[0].reset();
pulse[1].reset();
triangle.reset();
noise.reset();
dmc.reset();
frame.irq_pending = 0;
@ -167,13 +115,13 @@ void APU::reset() {
uint8 APU::read(uint16 addr) {
if(addr == 0x4015) {
uint8 result = 0x00;
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;
result |= pulse[0].length_counter ? 0x01 : 0;
result |= pulse[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;
frame.irq_pending = false;
set_irq_line();
@ -185,42 +133,38 @@ uint8 APU::read(uint16 addr) {
}
void APU::write(uint16 addr, uint8 data) {
const unsigned r = (addr >> 2) & 1; //rectangle#
const unsigned n = (addr >> 2) & 1; //pulse#
switch(addr) {
case 0x4000: case 0x4004:
rectangle[r].duty = data >> 6;
rectangle[r].envelope.loop_mode = data & 0x20;
rectangle[r].envelope.use_speed_as_volume = data & 0x10;
rectangle[r].envelope.speed = data & 0x0f;
pulse[n].duty = data >> 6;
pulse[n].envelope.loop_mode = data & 0x20;
pulse[n].envelope.use_speed_as_volume = data & 0x10;
pulse[n].envelope.speed = data & 0x0f;
break;
case 0x4001: case 0x4005:
rectangle[r].sweep.enable = data & 0x80;
rectangle[r].sweep.period = (data & 0x70) >> 4;
rectangle[r].sweep.decrement = data & 0x08;
rectangle[r].sweep.shift = data & 0x07;
rectangle[r].sweep.reload = true;
pulse[n].sweep.enable = data & 0x80;
pulse[n].sweep.period = (data & 0x70) >> 4;
pulse[n].sweep.decrement = data & 0x08;
pulse[n].sweep.shift = data & 0x07;
pulse[n].sweep.reload = true;
break;
case 0x4002: case 0x4006:
rectangle[r].period &= 0x700;
rectangle[r].period |= data;
rectangle[r].sweep.rectangle_period &= 0x700;
rectangle[r].sweep.rectangle_period |= data;
pulse[n].period = (pulse[n].period & 0x0700) | (data << 0);
pulse[n].sweep.pulse_period = (pulse[n].sweep.pulse_period & 0x0700) | (data << 0);
break;
case 0x4003: case 0x4007:
rectangle[r].period &= 0xff;
rectangle[r].period |= (data & 7) << 8;
rectangle[r].sweep.rectangle_period &= 0xff;
rectangle[r].sweep.rectangle_period |= (data & 7) << 8;
pulse[n].period = (pulse[n].period & 0x00ff) | (data << 8);
pulse[n].sweep.pulse_period = (pulse[n].sweep.pulse_period & 0x00ff) | (data << 8);
rectangle[r].duty_counter = 7;
rectangle[r].envelope.reload_decay = true;
pulse[n].duty_counter = 7;
pulse[n].envelope.reload_decay = true;
if(enabled_channels & (1 << r)) {
rectangle[r].length_counter = length_counter_table[(data >> 3) & 0x1f];
if(enabled_channels & (1 << n)) {
pulse[n].length_counter = length_counter_table[(data >> 3) & 0x1f];
}
break;
@ -230,13 +174,11 @@ void APU::write(uint16 addr, uint8 data) {
break;
case 0x400a:
triangle.period &= 0x700;
triangle.period |= data;
triangle.period = (triangle.period & 0x0700) | (data << 0);
break;
case 0x400b:
triangle.period &= 0xff;
triangle.period |= (data & 7) << 8;
triangle.period = (triangle.period & 0x00ff) | (data << 8);
triangle.reload_linear = true;
@ -286,10 +228,10 @@ void APU::write(uint16 addr, uint8 data) {
break;
case 0x4015:
if((data & 0x01) == 0) rectangle[0].length_counter = 0;
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 & 0x01) == 0) pulse[0].length_counter = 0;
if((data & 0x02) == 0) pulse[1].length_counter = 0;
if((data & 0x04) == 0) triangle.length_counter = 0;
if((data & 0x08) == 0) noise.length_counter = 0;
(data & 0x10) ? dmc.start() : dmc.stop();
dmc.irq_pending = false;
@ -327,217 +269,20 @@ signed APU::Filter::run_lopass(signed sample) {
return (lopass >> 32);
}
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) {
signed delta = rectangle_period >> shift;
if(decrement) {
rectangle_period -= delta;
if(channel == 0) rectangle_period--;
} else if((rectangle_period + delta) < 0x800) {
rectangle_period += delta;
}
}
}
if(reload) {
reload = false;
counter = period + 1;
}
}
unsigned APU::Envelope::volume() const {
return use_speed_as_volume ? speed : decay_volume;
}
void APU::Envelope::clock() {
if(reload_decay) {
reload_decay = false;
decay_volume = 0x0f;
decay_counter = speed + 1;
return;
}
if(--decay_counter == 0) {
decay_counter = speed + 1;
if(decay_volume || loop_mode) decay_volume--;
}
}
void APU::Rectangle::clock_length() {
if(envelope.loop_mode == 0) {
if(length_counter) length_counter--;
}
}
uint8 APU::Rectangle::clock() {
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;
duty_counter++;
}
return result;
}
void APU::Triangle::clock_length() {
if(halt_length_counter == 0) {
if(length_counter > 0) length_counter--;
}
}
void APU::Triangle::clock_linear_length() {
if(reload_linear) {
linear_length_counter = linear_length;
} else if(linear_length_counter) {
linear_length_counter--;
}
if(halt_length_counter == 0) reload_linear = false;
}
uint8 APU::Triangle::clock() {
uint8 result = step_counter & 0x0f;
if((step_counter & 0x10) == 0) result ^= 0x0f;
if(length_counter == 0 || linear_length_counter == 0) return result;
if(--period_counter == 0) {
step_counter++;
period_counter = period + 1;
}
return result;
}
void APU::Noise::clock_length() {
if(envelope.loop_mode == 0) {
if(length_counter > 0) length_counter--;
}
}
uint8 APU::Noise::clock() {
if(length_counter == 0) return 0;
uint8 result = (lfsr & 1) ? envelope.volume() : 0;
if(--period_counter == 0) {
unsigned feedback;
if(short_mode) {
feedback = ((lfsr >> 0) & 1) ^ ((lfsr >> 6) & 1);
} else {
feedback = ((lfsr >> 0) & 1) ^ ((lfsr >> 1) & 1);
}
lfsr = (lfsr >> 1) | (feedback << 14);
period_counter = apu.ntsc_noise_period_table[period];
}
return result;
}
void APU::DMC::start() {
if(length_counter == 0) {
read_addr = 0x4000 + (addr_latch << 6);
length_counter = (length_latch << 4) + 1;
}
}
void APU::DMC::stop() {
length_counter = 0;
dma_delay_counter = 0;
cpu.set_rdy_line(1);
cpu.set_rdy_addr({ false, 0u });
}
uint8 APU::DMC::clock() {
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;
}
void APU::clock_frame_counter() {
frame.counter++;
if(frame.counter & 1) {
rectangle[0].clock_length();
rectangle[0].sweep.clock(0);
rectangle[1].clock_length();
rectangle[1].sweep.clock(1);
pulse[0].clock_length();
pulse[0].sweep.clock(0);
pulse[1].clock_length();
pulse[1].sweep.clock(1);
triangle.clock_length();
noise.clock_length();
}
rectangle[0].envelope.clock();
rectangle[1].envelope.clock();
pulse[0].envelope.clock();
pulse[1].envelope.clock();
triangle.clock_linear_length();
noise.envelope.clock();
@ -561,9 +306,9 @@ void APU::clock_frame_counter_divider() {
APU::APU() {
for(unsigned amp = 0; amp < 32; amp++) {
if(amp == 0) {
rectangle_dac[amp] = 0;
pulse_dac[amp] = 0;
} else {
rectangle_dac[amp] = 16384.0 * 95.88 / (8128.0 / amp + 100.0);
pulse_dac[amp] = 16384.0 * 95.88 / (8128.0 / amp + 100.0);
}
}

View File

@ -27,116 +27,12 @@ struct APU : Processor {
void serialize(serializer&);
} filter;
struct Envelope {
uint4 speed;
bool use_speed_as_volume;
bool loop_mode;
bool reload_decay;
uint8 decay_counter;
uint4 decay_volume;
unsigned volume() const;
void clock();
void serialize(serializer&);
};
struct Sweep {
uint8 shift;
bool decrement;
uint3 period;
uint8 counter;
bool enable;
bool reload;
unsigned rectangle_period;
bool check_period();
void clock(unsigned channel);
void serialize(serializer&);
};
struct Rectangle {
unsigned length_counter;
Envelope envelope;
Sweep sweep;
uint2 duty;
uint3 duty_counter;
uint11 period;
unsigned period_counter;
void clock_length();
bool check_period();
uint8 clock();
void serialize(serializer&);
} rectangle[2];
struct Triangle {
unsigned length_counter;
uint8 linear_length;
bool halt_length_counter;
uint11 period;
unsigned period_counter;
uint5 step_counter;
uint8 linear_length_counter;
bool reload_linear;
void clock_length();
void clock_linear_length();
uint8 clock();
void serialize(serializer&);
} triangle;
struct Noise {
unsigned length_counter;
Envelope envelope;
uint4 period;
unsigned period_counter;
bool short_mode;
uint15 lfsr;
void clock_length();
uint8 clock();
void serialize(serializer&);
} noise;
struct DMC {
unsigned length_counter;
bool irq_pending;
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();
void stop();
uint8 clock();
void serialize(serializer&);
} dmc;
#include "envelope.hpp"
#include "sweep.hpp"
#include "pulse.hpp"
#include "triangle.hpp"
#include "noise.hpp"
#include "dmc.hpp"
struct FrameCounter {
enum : unsigned { NtscPeriod = 14915 }; //~(21.477MHz / 6 / 240hz)
@ -156,7 +52,7 @@ struct APU : Processor {
uint8 enabled_channels;
int16 cartridge_sample;
int16 rectangle_dac[32];
int16 pulse_dac[32];
int16 dmc_triangle_noise_dac[128][16][16];
static const uint8 length_counter_table[32];

117
bsnes/nes/apu/dmc.cpp Executable file
View File

@ -0,0 +1,117 @@
void APU::DMC::start() {
if(length_counter == 0) {
read_addr = 0x4000 + (addr_latch << 6);
length_counter = (length_latch << 4) + 1;
}
}
void APU::DMC::stop() {
length_counter = 0;
dma_delay_counter = 0;
cpu.set_rdy_line(1);
cpu.set_rdy_addr({ false, 0u });
}
uint8 APU::DMC::clock() {
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;
}
void APU::DMC::power() {
}
void APU::DMC::reset() {
length_counter = 0;
irq_pending = 0;
period = 0;
period_counter = ntsc_dmc_period_table[0];
irq_enable = 0;
loop_mode = 0;
dac_latch = 0;
addr_latch = 0;
length_latch = 0;
read_addr = 0;
dma_delay_counter = 0;
bit_counter = 0;
have_dma_buffer = 0;
dma_buffer = 0;
have_sample = 0;
sample = 0;
}
void APU::DMC::serialize(serializer &s) {
s.integer(length_counter);
s.integer(irq_pending);
s.integer(period);
s.integer(period_counter);
s.integer(irq_enable);
s.integer(loop_mode);
s.integer(dac_latch);
s.integer(addr_latch);
s.integer(length_latch);
s.integer(read_addr);
s.integer(dma_delay_counter);
s.integer(bit_counter);
s.integer(have_dma_buffer);
s.integer(dma_buffer);
s.integer(have_sample);
s.integer(sample);
}

32
bsnes/nes/apu/dmc.hpp Executable file
View File

@ -0,0 +1,32 @@
struct DMC {
unsigned length_counter;
bool irq_pending;
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();
void stop();
uint8 clock();
void power();
void reset();
void serialize(serializer&);
} dmc;

39
bsnes/nes/apu/envelope.cpp Executable file
View File

@ -0,0 +1,39 @@
unsigned APU::Envelope::volume() const {
return use_speed_as_volume ? speed : decay_volume;
}
void APU::Envelope::clock() {
if(reload_decay) {
reload_decay = false;
decay_volume = 0x0f;
decay_counter = speed + 1;
return;
}
if(--decay_counter == 0) {
decay_counter = speed + 1;
if(decay_volume || loop_mode) decay_volume--;
}
}
void APU::Envelope::power() {
}
void APU::Envelope::reset() {
speed = 0;
use_speed_as_volume = 0;
loop_mode = 0;
reload_decay = 0;
decay_counter = 0;
decay_volume = 0;
}
void APU::Envelope::serialize(serializer &s) {
s.integer(speed);
s.integer(use_speed_as_volume);
s.integer(loop_mode);
s.integer(reload_decay);
s.integer(decay_counter);
s.integer(decay_volume);
}

16
bsnes/nes/apu/envelope.hpp Executable file
View File

@ -0,0 +1,16 @@
struct Envelope {
uint4 speed;
bool use_speed_as_volume;
bool loop_mode;
bool reload_decay;
uint8 decay_counter;
uint4 decay_volume;
unsigned volume() const;
void clock();
void power();
void reset();
void serialize(serializer&);
};

57
bsnes/nes/apu/noise.cpp Executable file
View File

@ -0,0 +1,57 @@
void APU::Noise::clock_length() {
if(envelope.loop_mode == 0) {
if(length_counter > 0) length_counter--;
}
}
uint8 APU::Noise::clock() {
if(length_counter == 0) return 0;
uint8 result = (lfsr & 1) ? envelope.volume() : 0;
if(--period_counter == 0) {
unsigned feedback;
if(short_mode) {
feedback = ((lfsr >> 0) & 1) ^ ((lfsr >> 6) & 1);
} else {
feedback = ((lfsr >> 0) & 1) ^ ((lfsr >> 1) & 1);
}
lfsr = (lfsr >> 1) | (feedback << 14);
period_counter = apu.ntsc_noise_period_table[period];
}
return result;
}
void APU::Noise::power() {
}
void APU::Noise::reset() {
length_counter = 0;
envelope.speed = 0;
envelope.use_speed_as_volume = 0;
envelope.loop_mode = 0;
envelope.reload_decay = 0;
envelope.decay_counter = 0;
envelope.decay_volume = 0;
period = 0;
period_counter = 1;
short_mode = 0;
lfsr = 1;
}
void APU::Noise::serialize(serializer &s) {
s.integer(length_counter);
envelope.serialize(s);
s.integer(period);
s.integer(period_counter);
s.integer(short_mode);
s.integer(lfsr);
}

18
bsnes/nes/apu/noise.hpp Executable file
View File

@ -0,0 +1,18 @@
struct Noise {
unsigned length_counter;
Envelope envelope;
uint4 period;
unsigned period_counter;
bool short_mode;
uint15 lfsr;
void clock_length();
uint8 clock();
void power();
void reset();
void serialize(serializer&);
} noise;

51
bsnes/nes/apu/pulse.cpp Executable file
View File

@ -0,0 +1,51 @@
void APU::Pulse::clock_length() {
if(envelope.loop_mode == 0) {
if(length_counter) length_counter--;
}
}
uint8 APU::Pulse::clock() {
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.pulse_period < 0x008) result = 0;
if(--period_counter == 0) {
period_counter = (sweep.pulse_period + 1) * 2;
duty_counter++;
}
return result;
}
void APU::Pulse::power() {
envelope.power();
sweep.power();
}
void APU::Pulse::reset() {
envelope.reset();
sweep.reset();
length_counter = 0;
duty = 0;
duty_counter = 0;
period = 0;
period_counter = 1;
}
void APU::Pulse::serialize(serializer &s) {
s.integer(length_counter);
envelope.serialize(s);
sweep.serialize(s);
s.integer(duty);
s.integer(duty_counter);
s.integer(period);
s.integer(period_counter);
}

20
bsnes/nes/apu/pulse.hpp Executable file
View File

@ -0,0 +1,20 @@
struct Pulse {
unsigned length_counter;
Envelope envelope;
Sweep sweep;
uint2 duty;
uint3 duty_counter;
uint11 period;
unsigned period_counter;
void clock_length();
bool check_period();
uint8 clock();
void power();
void reset();
void serialize(serializer&);
} pulse[2];

View File

@ -3,8 +3,8 @@ void APU::serialize(serializer &s) {
filter.serialize(s);
rectangle[0].serialize(s);
rectangle[1].serialize(s);
pulse[0].serialize(s);
pulse[1].serialize(s);
triangle.serialize(s);
dmc.serialize(s);
frame.serialize(s);
@ -19,90 +19,6 @@ void APU::Filter::serialize(serializer &s) {
s.integer(lopass);
}
void APU::Envelope::serialize(serializer &s) {
s.integer(speed);
s.integer(use_speed_as_volume);
s.integer(loop_mode);
s.integer(reload_decay);
s.integer(decay_counter);
s.integer(decay_volume);
}
void APU::Sweep::serialize(serializer &s) {
s.integer(shift);
s.integer(decrement);
s.integer(period);
s.integer(counter);
s.integer(enable);
s.integer(reload);
s.integer(rectangle_period);
}
void APU::Rectangle::serialize(serializer &s) {
s.integer(length_counter);
envelope.serialize(s);
sweep.serialize(s);
s.integer(duty);
s.integer(duty_counter);
s.integer(period);
s.integer(period_counter);
}
void APU::Triangle::serialize(serializer &s) {
s.integer(length_counter);
s.integer(linear_length);
s.integer(halt_length_counter);
s.integer(period);
s.integer(period_counter);
s.integer(step_counter);
s.integer(linear_length_counter);
s.integer(reload_linear);
}
void APU::Noise::serialize(serializer &s) {
s.integer(length_counter);
envelope.serialize(s);
s.integer(period);
s.integer(period_counter);
s.integer(short_mode);
s.integer(lfsr);
}
void APU::DMC::serialize(serializer &s) {
s.integer(length_counter);
s.integer(irq_pending);
s.integer(period);
s.integer(period_counter);
s.integer(irq_enable);
s.integer(loop_mode);
s.integer(dac_latch);
s.integer(addr_latch);
s.integer(length_latch);
s.integer(read_addr);
s.integer(dma_delay_counter);
s.integer(bit_counter);
s.integer(have_dma_buffer);
s.integer(dma_buffer);
s.integer(have_sample);
s.integer(sample);
}
void APU::FrameCounter::serialize(serializer &s) {
s.integer(irq_pending);

53
bsnes/nes/apu/sweep.cpp Executable file
View File

@ -0,0 +1,53 @@
bool APU::Sweep::check_period() {
if(pulse_period > 0x7ff) return false;
if(decrement == 0) {
if((pulse_period + (pulse_period >> shift)) & 0x800) return false;
}
return true;
}
void APU::Sweep::clock(unsigned channel) {
if(--counter == 0) {
counter = period + 1;
if(enable && shift && pulse_period > 8) {
signed delta = pulse_period >> shift;
if(decrement) {
pulse_period -= delta;
if(channel == 0) pulse_period--;
} else if((pulse_period + delta) < 0x800) {
pulse_period += delta;
}
}
}
if(reload) {
reload = false;
counter = period + 1;
}
}
void APU::Sweep::power() {
shift = 0;
decrement = 0;
period = 0;
counter = 1;
enable = 0;
reload = 0;
pulse_period = 0;
}
void APU::Sweep::reset() {
}
void APU::Sweep::serialize(serializer &s) {
s.integer(shift);
s.integer(decrement);
s.integer(period);
s.integer(counter);
s.integer(enable);
s.integer(reload);
s.integer(pulse_period);
}

16
bsnes/nes/apu/sweep.hpp Executable file
View File

@ -0,0 +1,16 @@
struct Sweep {
uint8 shift;
bool decrement;
uint3 period;
uint8 counter;
bool enable;
bool reload;
uint11 pulse_period;
bool check_period();
void clock(unsigned channel);
void power();
void reset();
void serialize(serializer&);
};

58
bsnes/nes/apu/triangle.cpp Executable file
View File

@ -0,0 +1,58 @@
void APU::Triangle::clock_length() {
if(halt_length_counter == 0) {
if(length_counter > 0) length_counter--;
}
}
void APU::Triangle::clock_linear_length() {
if(reload_linear) {
linear_length_counter = linear_length;
} else if(linear_length_counter) {
linear_length_counter--;
}
if(halt_length_counter == 0) reload_linear = false;
}
uint8 APU::Triangle::clock() {
uint8 result = step_counter & 0x0f;
if((step_counter & 0x10) == 0) result ^= 0x0f;
if(length_counter == 0 || linear_length_counter == 0) return result;
if(--period_counter == 0) {
step_counter++;
period_counter = period + 1;
}
return result;
}
void APU::Triangle::power() {
reset();
}
void APU::Triangle::reset() {
length_counter = 0;
linear_length = 0;
halt_length_counter = 0;
period = 0;
period_counter = 1;
step_counter = 0;
linear_length_counter = 0;
reload_linear = 0;
}
void APU::Triangle::serialize(serializer &s) {
s.integer(length_counter);
s.integer(linear_length);
s.integer(halt_length_counter);
s.integer(period);
s.integer(period_counter);
s.integer(step_counter);
s.integer(linear_length_counter);
s.integer(reload_linear);
}

21
bsnes/nes/apu/triangle.hpp Executable file
View File

@ -0,0 +1,21 @@
struct Triangle {
unsigned length_counter;
uint8 linear_length;
bool halt_length_counter;
uint11 period;
unsigned period_counter;
uint5 step_counter;
uint8 linear_length_counter;
bool reload_linear;
void clock_length();
void clock_linear_length();
uint8 clock();
void power();
void reset();
void serialize(serializer&);
} triangle;

View File

@ -39,7 +39,7 @@ uint8 prg_read(unsigned addr) {
if(addr & 0x8000) {
bool region = addr & 0x4000;
unsigned bank = (region == 0 ? prg_bank : 0x0f);
return Board::prg_read((bank << 14) | (addr & 0x3fff));
return prgrom.read((bank << 14) | (addr & 0x3fff));
}
return cpu.mdr();
}

View File

@ -1,11 +1,15 @@
#include "bandai-fcg.cpp"
#include "konami-vrc4.cpp"
#include "konami-vrc6.cpp"
#include "konami-vrc7.cpp"
#include "nes-axrom.cpp"
#include "nes-bnrom.cpp"
#include "nes-cnrom.cpp"
#include "nes-exrom.cpp"
#include "nes-fxrom.cpp"
#include "nes-gxrom.cpp"
#include "nes-nrom.cpp"
#include "nes-pxrom.cpp"
#include "nes-sxrom.cpp"
#include "nes-txrom.cpp"
#include "nes-uxrom.cpp"
@ -16,7 +20,7 @@ uint8 Board::Memory::read(unsigned addr) const {
}
void Board::Memory::write(unsigned addr, uint8 byte) {
data[mirror(addr, size)] = byte;
if(writable) data[mirror(addr, size)] = byte;
}
unsigned Board::mirror(unsigned addr, unsigned size) {
@ -53,17 +57,9 @@ void Board::tick() {
if(cartridge.clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
}
uint8 Board::prg_read(unsigned addr) {
return prgrom.data[mirror(addr, prgrom.size)];
}
void Board::prg_write(unsigned addr, uint8 data) {
prgrom.data[mirror(addr, prgrom.size)] = data;
}
uint8 Board::chr_read(unsigned addr) {
if(chrrom.size) return chrrom.data[mirror(addr, chrrom.size)];
if(chrram.size) return chrram.data[mirror(addr, chrram.size)];
if(chrrom.size) return chrrom.data[mirror(addr, chrrom.size)];
return 0u;
}
@ -71,7 +67,7 @@ void Board::chr_write(unsigned addr, uint8 data) {
if(chrram.size) chrram.data[mirror(addr, chrram.size)] = data;
}
Board::Memory Board::memory() {
Board::Memory& Board::memory() {
return prgram;
}
@ -102,13 +98,12 @@ Board::Board(BML::Node &board, const uint8_t *data, unsigned size) {
if(prgrom.size) memcpy(prgrom.data, data, prgrom.size);
if(chrrom.size) memcpy(chrrom.data, data + prgrom.size, chrrom.size);
prgram.writable = true;
chrram.writable = true;
}
Board::~Board() {
if(prgrom.size) delete[] prgrom.data;
if(prgram.size) delete[] prgram.data;
if(chrrom.size) delete[] chrrom.data;
if(chrram.size) delete[] chrram.data;
}
Board* Board::load(const string &markup, const uint8_t *data, unsigned size) {
@ -118,7 +113,9 @@ Board* Board::load(const string &markup, const uint8_t *data, unsigned size) {
if(type == "BANDAI-FCG") return new BandaiFCG(board, data, size);
if(type == "KONAMI-VRC-4") return new KonamiVRC4(board, data, size);
if(type == "KONAMI-VRC-6") return new KonamiVRC6(board, data, size);
if(type == "KONAMI-VRC-7") return new KonamiVRC7(board, data, size);
if(type == "NES-AMROM" ) return new NES_AxROM(board, data, size);
if(type == "NES-ANROM" ) return new NES_AxROM(board, data, size);
@ -134,12 +131,18 @@ Board* Board::load(const string &markup, const uint8_t *data, unsigned size) {
if(type == "NES-ETROM" ) return new NES_ExROM(board, data, size);
if(type == "NES-EWROM" ) return new NES_ExROM(board, data, size);
if(type == "NES-FJROM" ) return new NES_FxROM(board, data, size);
if(type == "NES-FKROM" ) return new NES_FxROM(board, data, size);
if(type == "NES-GNROM" ) return new NES_GxROM(board, data, size);
if(type == "NES-MHROM" ) return new NES_GxROM(board, data, size);
if(type == "NES-NROM-128") return new NES_NROM(board, data, size);
if(type == "NES-NROM-256") return new NES_NROM(board, data, size);
if(type == "NES-PEEOROM" ) return new NES_PxROM(board, data, size);
if(type == "NES-PNROM" ) return new NES_PxROM(board, data, size);
if(type == "NES-SNROM" ) return new NES_SxROM(board, data, size);
if(type == "NES-SXROM" ) return new NES_SxROM(board, data, size);

View File

@ -2,10 +2,14 @@ struct Board {
struct Memory {
uint8_t *data;
unsigned size;
bool writable;
inline uint8 read(unsigned addr) const;
inline void write(unsigned addr, uint8 data);
inline Memory(uint8_t *data, unsigned size) : data(data), size(size) {}
inline Memory() : data(nullptr), size(0u) {}
inline Memory() : data(nullptr), size(0u), writable(false) {}
inline ~Memory() { if(data) delete[] data; }
};
static unsigned mirror(unsigned addr, unsigned size);
@ -13,20 +17,22 @@ struct Board {
virtual void main();
virtual void tick();
virtual uint8 prg_read(unsigned addr);
virtual void prg_write(unsigned addr, uint8 data);
virtual uint8 prg_read(unsigned addr) = 0;
virtual void prg_write(unsigned addr, uint8 data) = 0;
virtual uint8 chr_read(unsigned addr);
virtual void chr_write(unsigned addr, uint8 data);
virtual Memory memory();
virtual inline void scanline(unsigned y) {}
virtual Memory& memory();
virtual void power();
virtual void reset();
virtual void serialize(serializer&);
Board(BML::Node &board, const uint8_t *data, unsigned size);
~Board();
virtual ~Board();
static Board* load(const string &markup, const uint8_t *data, unsigned size);

View File

@ -0,0 +1,61 @@
struct KonamiVRC4 : Board {
struct Settings {
struct Pinout {
unsigned a0;
unsigned a1;
} pinout;
} settings;
VRC4 vrc4;
void main() {
return vrc4.main();
}
uint8 prg_read(unsigned addr) {
if(addr < 0x6000) return cpu.mdr();
if(addr < 0x8000) return prgram.read(addr);
return prgrom.read(vrc4.prg_addr(addr));
}
void prg_write(unsigned addr, uint8 data) {
if(addr < 0x6000) return;
if(addr < 0x8000) return prgram.write(addr, data);
bool a0 = (addr & settings.pinout.a0);
bool a1 = (addr & settings.pinout.a1);
addr &= 0xfff0;
addr |= (a1 << 1) | (a0 << 0);
return vrc4.reg_write(addr, data);
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read(vrc4.ciram_addr(addr));
return Board::chr_read(vrc4.chr_addr(addr));
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) return ppu.ciram_write(vrc4.ciram_addr(addr), data);
return Board::chr_write(vrc4.chr_addr(addr), data);
}
void power() {
vrc4.power();
}
void reset() {
vrc4.reset();
}
void serialize(serializer &s) {
Board::serialize(s);
vrc4.serialize(s);
}
KonamiVRC4(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size), vrc4(*this) {
settings.pinout.a0 = 1 << decimal(board["chip"]["pinout"]["a0"].value);
settings.pinout.a1 = 1 << decimal(board["chip"]["pinout"]["a1"].value);
}
};

View File

@ -4,7 +4,7 @@ VRC6 vrc6;
uint8 prg_read(unsigned addr) {
if((addr & 0xe000) == 0x6000) return vrc6.ram_read(addr);
if(addr & 0x8000) return Board::prg_read(vrc6.prg_addr(addr));
if(addr & 0x8000) return prgrom.read(vrc6.prg_addr(addr));
return cpu.mdr();
}
@ -35,7 +35,6 @@ void serialize(serializer &s) {
void main() { vrc6.main(); }
void power() { vrc6.power(); }
void reset() { vrc6.reset(); }
Memory memory() { return prgram; }
KonamiVRC6(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size), vrc6(*this) {
}

View File

@ -0,0 +1,47 @@
struct KonamiVRC7 : Board {
VRC7 vrc7;
void main() {
return vrc7.main();
}
uint8 prg_read(unsigned addr) {
if(addr < 0x6000) return cpu.mdr();
if(addr < 0x8000) return prgram.read(addr);
return prgrom.read(vrc7.prg_addr(addr));
}
void prg_write(unsigned addr, uint8 data) {
if(addr < 0x6000) return;
if(addr < 0x8000) return prgram.write(addr, data);
return vrc7.reg_write(addr, data);
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read(vrc7.ciram_addr(addr));
return chrram.read(vrc7.chr_addr(addr));
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) return ppu.ciram_write(vrc7.ciram_addr(addr), data);
return chrram.write(vrc7.chr_addr(addr), data);
}
void power() {
vrc7.power();
}
void reset() {
vrc7.reset();
}
void serialize(serializer &s) {
Board::serialize(s);
vrc7.serialize(s);
}
KonamiVRC7(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size), vrc7(*this) {
}
};

View File

@ -9,7 +9,7 @@ uint4 prg_bank;
bool mirror_select;
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) return Board::prg_read((prg_bank << 15) | (addr & 0x7fff));
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
return cpu.mdr();
}
@ -31,7 +31,6 @@ void chr_write(unsigned addr, uint8 data) {
}
void power() {
reset();
}
void reset() {

View File

@ -9,7 +9,7 @@ struct Settings {
uint2 prg_bank;
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) return Board::prg_read((prg_bank << 15) | (addr & 0x7fff));
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
return cpu.mdr();
}
@ -34,7 +34,6 @@ void chr_write(unsigned addr, uint8 data) {
}
void power() {
reset();
}
void reset() {

View File

@ -9,7 +9,7 @@ struct Settings {
uint2 chr_bank;
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) return Board::prg_read(addr & 0x7fff);
if(addr & 0x8000) return prgrom.read(addr & 0x7fff);
return cpu.mdr();
}
@ -36,7 +36,6 @@ void chr_write(unsigned addr, uint8 data) {
}
void power() {
reset();
}
void reset() {

View File

@ -29,6 +29,10 @@ void chr_write(unsigned addr, uint8 data) {
mmc5.chr_write(addr, data);
}
void scanline(unsigned y) {
mmc5.scanline(y);
}
void power() {
mmc5.power();
}

View File

@ -0,0 +1,91 @@
//MMC4
struct NES_FxROM : Board {
enum Revision : unsigned {
FJROM,
FKROM,
} revision;
uint4 prg_bank;
uint5 chr_bank[2][2];
bool mirror;
bool latch[2];
uint8 prg_read(unsigned addr) {
if(addr < 0x6000) return cpu.mdr();
if(addr < 0x8000) return prgram.read(addr);
unsigned bank = addr < 0xc000 ? prg_bank : (uint4)0x0f;
return prgrom.read((bank * 0x4000) | (addr & 0x3fff));
}
void prg_write(unsigned addr, uint8 data) {
if(addr < 0x6000) return;
if(addr < 0x8000) return prgram.write(addr, data);
switch(addr & 0xf000) {
case 0xa000: prg_bank = data & 0x0f; break;
case 0xb000: chr_bank[0][0] = data & 0x1f; break;
case 0xc000: chr_bank[0][1] = data & 0x1f; break;
case 0xd000: chr_bank[1][0] = data & 0x1f; break;
case 0xe000: chr_bank[1][1] = data & 0x1f; break;
case 0xf000: mirror = data & 0x01; break;
}
}
unsigned ciram_addr(unsigned addr) const {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
}
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
bool region = addr & 0x1000;
unsigned bank = chr_bank[region][latch[region]];
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
return Board::chr_read((bank * 0x1000) | (addr & 0x0fff));
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
bool region = addr & 0x1000;
unsigned bank = chr_bank[region][latch[region]];
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
return Board::chr_write((bank * 0x1000) | (addr & 0x0fff), data);
}
void power() {
}
void reset() {
prg_bank = 0;
chr_bank[0][0] = 0;
chr_bank[0][1] = 0;
chr_bank[1][0] = 0;
chr_bank[1][1] = 0;
mirror = 0;
latch[0] = 0;
latch[1] = 0;
}
void serialize(serializer &s) {
Board::serialize(s);
s.integer(prg_bank);
s.integer(chr_bank[0][0]);
s.integer(chr_bank[0][1]);
s.integer(chr_bank[1][0]);
s.integer(chr_bank[1][1]);
s.integer(mirror);
s.array(latch);
}
NES_FxROM(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size) {
revision = Revision::FKROM;
}
};

View File

@ -11,7 +11,7 @@ uint2 prg_bank;
uint2 chr_bank;
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) return Board::prg_read((prg_bank << 15) | (addr & 0x7fff));
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
return cpu.mdr();
}
@ -41,7 +41,6 @@ void chr_write(unsigned addr, uint8 data) {
}
void power() {
reset();
}
void reset() {

View File

@ -8,7 +8,7 @@ struct Settings {
} settings;
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) return Board::prg_read(addr);
if(addr & 0x8000) return prgrom.read(addr);
return cpu.mdr();
}
@ -20,7 +20,8 @@ uint8 chr_read(unsigned addr) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
}
return Board::chr_read(addr);
if(chrram.size) return chrram.read(addr);
return chrrom.read(addr);
}
void chr_write(unsigned addr, uint8 data) {
@ -28,7 +29,7 @@ void chr_write(unsigned addr, uint8 data) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
}
return Board::chr_write(addr, data);
if(chrram.size) return chrram.write(addr, data);
}
void serialize(serializer &s) {

View File

@ -0,0 +1,97 @@
//MMC2
struct NES_PxROM : Board {
enum Revision : unsigned {
PEEOROM,
PNROM,
} revision;
uint4 prg_bank;
uint5 chr_bank[2][2];
bool mirror;
bool latch[2];
uint8 prg_read(unsigned addr) {
if(addr < 0x6000) return cpu.mdr();
if(addr < 0x8000) return prgram.read(addr);
unsigned bank = 0;
switch((addr / 0x2000) & 3) {
case 0: bank = prg_bank; break;
case 1: bank = 0x0d; break;
case 2: bank = 0x0e; break;
case 3: bank = 0x0f; break;
}
return prgrom.read((bank * 0x2000) | (addr & 0x1fff));
}
void prg_write(unsigned addr, uint8 data) {
if(addr < 0x6000) return;
if(addr < 0x8000) return prgram.write(addr, data);
switch(addr & 0xf000) {
case 0xa000: prg_bank = data & 0x0f; break;
case 0xb000: chr_bank[0][0] = data & 0x1f; break;
case 0xc000: chr_bank[0][1] = data & 0x1f; break;
case 0xd000: chr_bank[1][0] = data & 0x1f; break;
case 0xe000: chr_bank[1][1] = data & 0x1f; break;
case 0xf000: mirror = data & 0x01; break;
}
}
unsigned ciram_addr(unsigned addr) const {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
}
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
bool region = addr & 0x1000;
unsigned bank = chr_bank[region][latch[region]];
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
return Board::chr_read((bank * 0x1000) | (addr & 0x0fff));
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
bool region = addr & 0x1000;
unsigned bank = chr_bank[region][latch[region]];
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
return Board::chr_write((bank * 0x1000) | (addr & 0x0fff), data);
}
void power() {
}
void reset() {
prg_bank = 0;
chr_bank[0][0] = 0;
chr_bank[0][1] = 0;
chr_bank[1][0] = 0;
chr_bank[1][1] = 0;
mirror = 0;
latch[0] = 0;
latch[1] = 0;
}
void serialize(serializer &s) {
Board::serialize(s);
s.integer(prg_bank);
s.integer(chr_bank[0][0]);
s.integer(chr_bank[0][1]);
s.integer(chr_bank[1][0]);
s.integer(chr_bank[1][1]);
s.integer(mirror);
s.array(latch);
}
NES_PxROM(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size) {
revision = Revision::PNROM;
}
};

View File

@ -81,10 +81,6 @@ void chr_write(unsigned addr, uint8 data) {
return Board::chr_write(mmc1.chr_addr(addr), data);
}
Memory memory() {
return prgram;
}
void power() {
mmc1.power();
}

View File

@ -26,7 +26,7 @@ void main() {
uint8 prg_read(unsigned addr) {
if((addr & 0xe000) == 0x6000) return mmc3.ram_read(addr);
if(addr & 0x8000) return Board::prg_read(mmc3.prg_addr(addr));
if(addr & 0x8000) return prgrom.read(mmc3.prg_addr(addr));
return cpu.mdr();
}
@ -93,10 +93,6 @@ void chr_write(unsigned addr, uint8 data) {
return Board::chr_write(mmc3.chr_addr(addr), data);
}
Memory memory() {
return prgram;
}
void power() {
mmc3.power();
}

View File

@ -10,8 +10,8 @@ struct Settings {
uint4 prg_bank;
uint8 prg_read(unsigned addr) {
if((addr & 0xc000) == 0x8000) return Board::prg_read((prg_bank << 14) | (addr & 0x3fff));
if((addr & 0xc000) == 0xc000) return Board::prg_read(( 0x0f << 14) | (addr & 0x3fff));
if((addr & 0xc000) == 0x8000) return prgrom.read((prg_bank << 14) | (addr & 0x3fff));
if((addr & 0xc000) == 0xc000) return prgrom.read(( 0x0f << 14) | (addr & 0x3fff));
return cpu.mdr();
}
@ -36,7 +36,6 @@ void chr_write(unsigned addr, uint8 data) {
}
void power() {
reset();
}
void reset() {

View File

@ -94,7 +94,7 @@ uint8 prg_read(unsigned addr) {
}
addr = (bank << 13) | (addr & 0x1fff);
return Board::prg_read(addr);
return prgrom.read(addr);
}
void prg_write(unsigned addr, uint8 data) {
@ -184,8 +184,6 @@ void power() {
double volume = 1.0 / pow(2, 1.0 / 2 * (15 - n));
dac[n] = volume * 8192.0;
}
reset();
}
void reset() {

View File

@ -20,6 +20,8 @@ void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
sha256 = nall::sha256(data, size);
board = Board::load(markup, data, size);
} else {
//unsigned crc32 = crc32_calculate(data + 16, size - 16);
//print(hex<8>(crc32), "\n");
sha256 = nall::sha256(data + 16, size - 16);
board = Board::load(markup != "" ? markup : iNES(data, size), data + 16, size - 16);
}
@ -43,7 +45,6 @@ uint8* Cartridge::ram_data() {
}
void Cartridge::power() {
create(Cartridge::Main, 21477272);
board->power();
}
@ -72,7 +73,12 @@ void Cartridge::chr_write(unsigned addr, uint8 data) {
return board->chr_write(addr, data);
}
void Cartridge::scanline(unsigned y) {
return board->scanline(y);
}
void Cartridge::serialize(serializer &s) {
Processor::serialize(s);
return board->serialize(s);
}

View File

@ -28,6 +28,10 @@ struct Cartridge : Processor, property<Cartridge> {
uint8 chr_read(unsigned addr);
void chr_write(unsigned addr, uint8 data);
//scanline() is for debugging purposes only:
//boards must detect scanline edges on their own
void scanline(unsigned y);
};
extern Cartridge cartridge;

View File

@ -1,7 +1,9 @@
#include "mmc1.cpp"
#include "mmc3.cpp"
#include "mmc5.cpp"
#include "vrc4.cpp"
#include "vrc6.cpp"
#include "vrc7.cpp"
void Chip::tick() {
board.tick();

View File

@ -98,7 +98,6 @@ void mmio_write(unsigned addr, uint8 data) {
}
void power() {
reset();
}
void reset() {

View File

@ -90,7 +90,6 @@ void ram_write(unsigned addr, uint8 data) {
}
void power() {
reset();
}
void reset() {

View File

@ -30,7 +30,7 @@ bool vs_enable; //$5200
bool vs_side; //$5200
uint5 vs_tile; //$5200
uint8 vs_scroll; //$5201
uint8 vs_bank; //5202
uint8 vs_bank; //$5202
uint8 irq_line; //$5203
bool irq_enable; //$5204
@ -68,6 +68,11 @@ void main() {
}
}
void scanline(unsigned y) {
//used for testing only, to verify MMC5 scanline detection is accurate:
//if(y != vcounter && y <= 240) print(y, " vs ", vcounter, "\n");
}
uint8 prg_access(bool write, unsigned addr, uint8 data = 0x00) {
unsigned bank;
@ -396,7 +401,6 @@ void chr_write(unsigned addr, uint8 data) {
}
void power() {
reset();
}
void reset() {

183
bsnes/nes/cartridge/chip/vrc4.cpp Executable file
View File

@ -0,0 +1,183 @@
struct VRC4 : Chip {
bool prg_mode;
uint5 prg_bank[2];
uint2 mirror;
uint8 chr_bank[8];
uint8 irq_latch;
bool irq_mode;
bool irq_enable;
bool irq_acknowledge;
uint8 irq_counter;
signed irq_scalar;
bool irq_line;
void main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_enable) {
if(irq_mode == 0) {
irq_scalar -= 3;
if(irq_scalar <= 0) {
irq_scalar += 341;
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
}
}
if(irq_mode == 1) {
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
}
}
cpu.set_irq_line(irq_line);
tick();
}
}
void reg_write(unsigned addr, uint8 data) {
switch(addr) {
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
prg_bank[0] = data & 0x1f;
break;
case 0x9000: case 0x9001:
mirror = data & 0x03;
break;
case 0x9002: case 0x9003:
prg_mode = data & 0x02;
break;
case 0xa000: case 0xa001: case 0xa002: case 0xa003:
prg_bank[1] = data & 0x1f;
break;
case 0xb000: chr_bank[0] = (chr_bank[0] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xb001: chr_bank[0] = (chr_bank[0] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xb002: chr_bank[1] = (chr_bank[1] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xb003: chr_bank[1] = (chr_bank[1] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xc000: chr_bank[2] = (chr_bank[2] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xc001: chr_bank[2] = (chr_bank[2] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xc002: chr_bank[3] = (chr_bank[3] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xc003: chr_bank[3] = (chr_bank[3] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xd000: chr_bank[4] = (chr_bank[4] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xd001: chr_bank[4] = (chr_bank[4] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xd002: chr_bank[5] = (chr_bank[5] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xd003: chr_bank[5] = (chr_bank[5] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xe000: chr_bank[6] = (chr_bank[6] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xe001: chr_bank[6] = (chr_bank[6] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xe002: chr_bank[7] = (chr_bank[7] & 0xf0) | ((data & 0x0f) << 0); break;
case 0xe003: chr_bank[7] = (chr_bank[7] & 0x0f) | ((data & 0x0f) << 4); break;
case 0xf000:
irq_latch = (irq_latch & 0xf0) | ((data & 0x0f) << 0);
break;
case 0xf001:
irq_latch = (irq_latch & 0x0f) | ((data & 0x0f) << 4);
break;
case 0xf002:
irq_mode = data & 0x04;
irq_enable = data & 0x02;
irq_acknowledge = data & 0x01;
if(irq_enable) {
irq_counter = irq_latch;
irq_scalar = 341;
}
irq_line = 0;
break;
case 0xf003:
irq_enable = irq_acknowledge;
irq_line = 0;
break;
}
}
unsigned prg_addr(unsigned addr) const {
unsigned bank = 0, banks = board.prgrom.size / 0x2000;
switch((addr / 0x2000) & 3) {
case 0: bank = prg_mode == 0 ? (unsigned)prg_bank[0] : banks - 2; break;
case 1: bank = prg_bank[1]; break;
case 2: bank = prg_mode == 0 ? banks - 2 : (unsigned)prg_bank[0]; break;
case 3: bank = banks - 1; break;
}
return (bank * 0x2000) + (addr & 0x1fff);
}
unsigned chr_addr(unsigned addr) const {
unsigned bank = chr_bank[addr / 0x0400];
return (bank * 0x0400) + (addr & 0x03ff);
}
unsigned ciram_addr(unsigned addr) const {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
}
}
void power() {
}
void reset() {
prg_mode = 0;
for(auto &n : prg_bank) n = 0;
mirror = 0;
for(auto &n : chr_bank) n = 0;
irq_latch = 0;
irq_mode = 0;
irq_enable = 0;
irq_acknowledge = 0;
irq_counter = 0;
irq_scalar = 0;
irq_line = 0;
}
void serialize(serializer &s) {
s.integer(prg_mode);
for(auto &n : prg_bank) s.integer(n);
s.integer(mirror);
for(auto &n : chr_bank) s.integer(n);
s.integer(irq_latch);
s.integer(irq_mode);
s.integer(irq_enable);
s.integer(irq_acknowledge);
s.integer(irq_counter);
s.integer(irq_scalar);
s.integer(irq_line);
}
VRC4(Board &board) : Chip(board) {
}
};

View File

@ -243,7 +243,6 @@ void reg_write(unsigned addr, uint8 data) {
}
void power() {
reset();
}
void reset() {

154
bsnes/nes/cartridge/chip/vrc7.cpp Executable file
View File

@ -0,0 +1,154 @@
//Konami VRC7
//Yamaha YM2413 OPLL audio - not emulated
struct VRC7 : Chip {
uint8 prg_bank[3];
uint8 chr_bank[8];
uint2 mirror;
uint8 irq_latch;
bool irq_mode;
bool irq_enable;
bool irq_acknowledge;
uint8 irq_counter;
signed irq_scalar;
bool irq_line;
void main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_enable) {
if(irq_mode == 0) {
irq_scalar -= 3;
if(irq_scalar <= 0) {
irq_scalar += 341;
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
}
}
if(irq_mode == 1) {
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
}
}
cpu.set_irq_line(irq_line);
tick();
}
}
void reg_write(unsigned addr, uint8 data) {
switch(addr) {
case 0x8000: prg_bank[0] = data; break;
case 0x8010: prg_bank[1] = data; break;
case 0x9000: prg_bank[2] = data; break;
case 0x9010: break; //APU addr port
case 0x9030: break; //APU data port
case 0xa000: chr_bank[0] = data; break;
case 0xa010: chr_bank[1] = data; break;
case 0xb000: chr_bank[2] = data; break;
case 0xb010: chr_bank[3] = data; break;
case 0xc000: chr_bank[4] = data; break;
case 0xc010: chr_bank[5] = data; break;
case 0xd000: chr_bank[6] = data; break;
case 0xd010: chr_bank[7] = data; break;
case 0xe000: mirror = data & 0x03; break;
case 0xe010:
irq_latch = data;
break;
case 0xf000:
irq_mode = data & 0x04;
irq_enable = data & 0x02;
irq_acknowledge = data & 0x01;
if(irq_enable) {
irq_counter = irq_latch;
irq_scalar = 341;
}
irq_line = 0;
break;
case 0xf010:
irq_enable = irq_acknowledge;
irq_line = 0;
break;
}
}
unsigned prg_addr(unsigned addr) const {
unsigned bank = 0;
switch(addr & 0xe000) {
case 0x8000: bank = prg_bank[0]; break;
case 0xa000: bank = prg_bank[1]; break;
case 0xc000: bank = prg_bank[2]; break;
case 0xe000: bank = 0xff; break;
}
return (bank * 0x2000) + (addr & 0x1fff);
}
unsigned chr_addr(unsigned addr) const {
unsigned bank = chr_bank[addr / 0x0400];
return (bank * 0x0400) + (addr & 0x03ff);
}
unsigned ciram_addr(unsigned addr) const {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
}
}
void power() {
}
void reset() {
for(auto &n : prg_bank) n = 0;
for(auto &n : chr_bank) n = 0;
mirror = 0;
irq_latch = 0;
irq_mode = 0;
irq_enable = 0;
irq_acknowledge = 0;
irq_counter = 0;
irq_scalar = 0;
irq_line = 0;
}
void serialize(serializer &s) {
s.array(prg_bank);
s.array(chr_bank);
s.integer(mirror);
s.integer(irq_latch);
s.integer(irq_mode);
s.integer(irq_enable);
s.integer(irq_acknowledge);
s.integer(irq_counter);
s.integer(irq_scalar);
s.integer(irq_line);
}
VRC7(Board &board) : Chip(board) {
}
};

View File

@ -56,11 +56,32 @@ static string iNES(const uint8_t *data, unsigned size) {
output.append("\tboard type:NES-AOROM\n");
break;
case 9:
output.append("\tboard type:NES-PNROM\n");
output.append("\t\tchip type:MMC2\n");
prgram = 8192;
break;
case 10:
output.append("\tboard type:NES-FKROM\n");
output.append("\t\tchip type:MMC4\n");
prgram = 8192;
break;
case 16:
output.append("\tboard type:BANDAI-FCG\n");
output.append("\t\tchip type:LZ93D50\n");
break;
case 21:
case 23:
case 25:
output.append("\tboard type:KONAMI-VRC-4\n");
output.append("\t\tchip type:VRC4\n");
output.append("\t\t\tpinout a0=1 a1=0\n");
prgram = 8192;
break;
case 24:
output.append("\tboard type:KONAMI-VRC-6\n");
output.append("\t\tchip type:VRC6\n");
@ -87,6 +108,12 @@ static string iNES(const uint8_t *data, unsigned size) {
output.append("\t\tchip type:5B\n");
prgram = 8192;
break;
case 85:
output.append("\tboard type:KONAMI-VRC-7\n");
output.append("\t\tchip type:VRC7\n");
prgram = 8192;
break;
}
output.append("\t\tprg rom=", prgrom, " ram=", prgram, "\n");

View File

@ -58,8 +58,6 @@ void CPU::power() {
ram[0x0009] = 0xef;
ram[0x000a] = 0xdf;
ram[0x000f] = 0xbf;
reset();
}
void CPU::reset() {

View File

@ -19,6 +19,7 @@ namespace NES {
#include <nall/algorithm.hpp>
#include <nall/array.hpp>
#include <nall/crc32.hpp>
#include <nall/dl.hpp>
#include <nall/endian.hpp>
#include <nall/file.hpp>
@ -93,7 +94,7 @@ namespace NES {
s.integer(clock);
}
inline Processor() : thread(0) {}
inline Processor() : thread(nullptr) {}
};
#include <nes/system/system.hpp>

View File

@ -43,7 +43,6 @@ void PPU::frame_edge() {
}
void PPU::power() {
reset();
}
void PPU::reset() {
@ -108,6 +107,8 @@ uint8 PPU::read(uint16 addr) {
if((status.oam_addr & 3) == 3) result &= 0xe3;
break;
case 7: //PPUDATA
if(raster_enable() && (status.ly <= 240 || status.ly == 261)) return 0x00;
addr = status.vaddr & 0x3fff;
if(addr <= 0x1fff) {
result = status.bus_data;
@ -175,6 +176,8 @@ void PPU::write(uint16 addr, uint8 data) {
status.address_latch ^= 1;
return;
case 7: //PPUDATA
if(raster_enable() && (status.ly <= 240 || status.ly == 261)) return;
addr = status.vaddr & 0x3fff;
if(addr <= 0x1fff) {
cartridge.chr_write(addr, data);
@ -252,6 +255,7 @@ void PPU::ly_increment() {
frame_edge();
}
scanline_edge();
cartridge.scanline(status.ly);
}
void PPU::scrollx_increment() {

View File

@ -47,6 +47,7 @@ void System::power() {
ppu.power();
input.reset();
scheduler.power();
reset();
}
void System::reset() {

View File

@ -6,6 +6,7 @@
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_IE 0x0600
#define __MSVCRT_VERSION__ 0x0601
#define NOMINMAX
#include <windows.h>

View File

@ -110,9 +110,9 @@ inline void PPU::render_line_output() {
}
inline void PPU::render_line_clear() {
uint16 *ptr = (uint16*)output + (line * 1024) + ((interlace() && field()) ? 512 : 0);
uint16 width = (!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) ? 256 : 512;
memset(ptr, 0, width * 2 * sizeof(uint16));
uint32 *ptr = (uint32*)output + (line * 1024) + ((interlace() && field()) ? 512 : 0);
unsigned width = (!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) ? 256 : 512;
memset(ptr, 0, width * 2 * sizeof(uint32));
}
#endif

View File

@ -33,8 +33,8 @@ struct Interface {
void setCheats(const lstring &list = lstring{});
virtual void message(const string &text);
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
virtual void message(const string &text);
};
extern Interface *interface;

View File

@ -118,7 +118,7 @@ namespace SNES {
s.integer(clock);
}
inline Processor() : thread(0) {}
inline Processor() : thread(nullptr) {}
};
struct ChipDebugger {

View File

@ -11,13 +11,25 @@ struct Interface : public SNES::Interface {
snes_input_poll_t pinput_poll;
snes_input_state_t pinput_state;
string basename;
uint16_t *buffer;
uint32_t *palette;
void videoRefresh(const uint16_t *data, bool hires, bool interlace, bool overscan) {
void videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) {
unsigned width = hires ? 512 : 256;
unsigned height = overscan ? 239 : 224;
unsigned pitch = 1024 >> interlace;
if(interlace) height <<= 1;
data += 9 * 1024; //skip front porch
if(pvideo_refresh) pvideo_refresh(data, width, height);
for(unsigned y = 0; y < height; y++) {
const uint32_t *sp = data + y * pitch;
uint16_t *dp = buffer + y * pitch;
for(unsigned x = 0; x < width; x++) {
*dp++ = palette[*sp++];
}
}
if(pvideo_refresh) pvideo_refresh(buffer, width, height);
if(pinput_poll) pinput_poll();
}
@ -39,14 +51,35 @@ struct Interface : public SNES::Interface {
}
Interface() : pvideo_refresh(0), paudio_sample(0), pinput_poll(0), pinput_state(0) {
buffer = new uint16_t[512 * 480];
palette = new uint32_t[16 * 32768];
//{llll bbbbb ggggg rrrrr} -> { rrrrr ggggg bbbbb }
for(unsigned l = 0; l < 16; l++) {
for(unsigned r = 0; r < 32; r++) {
for(unsigned g = 0; g < 32; g++) {
for(unsigned b = 0; b < 32; b++) {
double luma = (double)l / 15.0;
unsigned ar = (luma * r + 0.5);
unsigned ag = (luma * g + 0.5);
unsigned ab = (luma * b + 0.5);
palette[(l << 15) + (r << 10) + (g << 5) + (b << 0)] = (ab << 10) + (ag << 5) + (ar << 0);
}
}
}
}
}
~Interface() {
delete[] buffer;
delete[] palette;
}
};
static Interface interface;
const char* snes_library_id(void) {
static string id = { SNES::Info::Name, " v", SNES::Info::Version };
return (const char*)id;
return "bsnes v083";
}
unsigned snes_library_revision_major(void) {
@ -148,7 +181,7 @@ bool snes_load_cartridge_normal(
) {
snes_cheat_reset();
if(rom_data) SNES::cartridge.rom.copy(rom_data, rom_size);
string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SNESCartridge(rom_data, rom_size).xmlMemoryMap;
string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SnesCartridge(rom_data, rom_size).markup;
SNES::cartridge.load(SNES::Cartridge::Mode::Normal, { xmlrom });
SNES::system.power();
return true;
@ -160,10 +193,10 @@ bool snes_load_cartridge_bsx_slotted(
) {
snes_cheat_reset();
if(rom_data) SNES::cartridge.rom.copy(rom_data, rom_size);
string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SNESCartridge(rom_data, rom_size).xmlMemoryMap;
string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SnesCartridge(rom_data, rom_size).markup;
if(bsx_data) SNES::bsxflash.memory.copy(bsx_data, bsx_size);
string xmlbsx = (bsx_xml && *bsx_xml) ? string(bsx_xml) : SNESCartridge(bsx_data, bsx_size).xmlMemoryMap;
SNES::cartridge.load(SNES::Cartridge::Mode::BsxSlotted, { xmlrom, xmlbsx });
string xmlbsx = (bsx_xml && *bsx_xml) ? string(bsx_xml) : SnesCartridge(bsx_data, bsx_size).markup;
SNES::cartridge.load(SNES::Cartridge::Mode::BsxSlotted, xmlrom);
SNES::system.power();
return true;
}
@ -174,10 +207,10 @@ bool snes_load_cartridge_bsx(
) {
snes_cheat_reset();
if(rom_data) SNES::cartridge.rom.copy(rom_data, rom_size);
string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SNESCartridge(rom_data, rom_size).xmlMemoryMap;
string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SnesCartridge(rom_data, rom_size).markup;
if(bsx_data) SNES::bsxflash.memory.copy(bsx_data, bsx_size);
string xmlbsx = (bsx_xml && *bsx_xml) ? string(bsx_xml) : SNESCartridge(bsx_data, bsx_size).xmlMemoryMap;
SNES::cartridge.load(SNES::Cartridge::Mode::Bsx, { xmlrom, xmlbsx });
string xmlbsx = (bsx_xml && *bsx_xml) ? string(bsx_xml) : SnesCartridge(bsx_data, bsx_size).markup;
SNES::cartridge.load(SNES::Cartridge::Mode::Bsx, xmlrom);
SNES::system.power();
return true;
}
@ -189,12 +222,12 @@ bool snes_load_cartridge_sufami_turbo(
) {
snes_cheat_reset();
if(rom_data) SNES::cartridge.rom.copy(rom_data, rom_size);
string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SNESCartridge(rom_data, rom_size).xmlMemoryMap;
string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SnesCartridge(rom_data, rom_size).markup;
if(sta_data) SNES::sufamiturbo.slotA.rom.copy(sta_data, sta_size);
string xmlsta = (sta_xml && *sta_xml) ? string(sta_xml) : SNESCartridge(sta_data, sta_size).xmlMemoryMap;
string xmlsta = (sta_xml && *sta_xml) ? string(sta_xml) : SnesCartridge(sta_data, sta_size).markup;
if(stb_data) SNES::sufamiturbo.slotB.rom.copy(stb_data, stb_size);
string xmlstb = (stb_xml && *stb_xml) ? string(stb_xml) : SNESCartridge(stb_data, stb_size).xmlMemoryMap;
SNES::cartridge.load(SNES::Cartridge::Mode::SufamiTurbo, { xmlrom, xmlsta, xmlstb });
string xmlstb = (stb_xml && *stb_xml) ? string(stb_xml) : SnesCartridge(stb_data, stb_size).markup;
SNES::cartridge.load(SNES::Cartridge::Mode::SufamiTurbo, xmlrom);
SNES::system.power();
return true;
}
@ -205,16 +238,16 @@ bool snes_load_cartridge_super_game_boy(
) {
snes_cheat_reset();
if(rom_data) SNES::cartridge.rom.copy(rom_data, rom_size);
string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SNESCartridge(rom_data, rom_size).xmlMemoryMap;
string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SnesCartridge(rom_data, rom_size).markup;
if(dmg_data) {
//GameBoyCartridge needs to modify dmg_data (for MMM01 emulation); so copy data
uint8_t *data = new uint8_t[dmg_size];
memcpy(data, dmg_data, dmg_size);
string xmldmg = (dmg_xml && *dmg_xml) ? string(dmg_xml) : GameBoyCartridge(data, dmg_size).xml;
string xmldmg = (dmg_xml && *dmg_xml) ? string(dmg_xml) : GameBoyCartridge(data, dmg_size).markup;
GameBoy::cartridge.load(xmldmg, data, dmg_size);
delete[] data;
}
SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, { xmlrom, "" });
SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, xmlrom);
SNES::system.power();
return true;
}

View File

@ -200,6 +200,7 @@ void InterfaceSNES::videoRefresh(const uint32_t *data, bool hires, bool interlac
unsigned height = 240 << interlace;
unsigned pitch = 1024 >> interlace;
//skip first line; as it is always blank (by SNES design)
if(overscan == false) data += 1 * 1024; // 8 + 224 + 8
if(overscan == true ) data += 9 * 1024; // 0 + 240 + 0
@ -275,8 +276,13 @@ string InterfaceSNES::path(SNES::Cartridge::Slot slot, const string &hint) {
return { interface->slotName[index[(unsigned)slot]], hint };
}
void InterfaceSNES::message(const string &text) {
MessageWindow::information(*mainWindow, text);
}
InterfaceSNES::InterfaceSNES() {
//{llll bbbbb ggggg rrrrr} -> { rrrrr ggggg bbbbb }
palette = new uint32_t[16 * 32 * 32 * 32];
for(unsigned l = 0; l < 16; l++) {
for(unsigned r = 0; r < 32; r++) {
for(unsigned g = 0; g < 32; g++) {
@ -291,3 +297,7 @@ InterfaceSNES::InterfaceSNES() {
}
}
}
InterfaceSNES::~InterfaceSNES() {
delete[] palette;
}

View File

@ -19,9 +19,11 @@ struct InterfaceSNES : SNES::Interface {
int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id);
string path(SNES::Cartridge::Slot slot, const string &hint);
void message(const string &text);
InterfaceSNES();
~InterfaceSNES();
private:
unsigned palette[16 * 32768];
unsigned *palette;
};

View File

@ -49,7 +49,7 @@ Application::Application(int argc, char **argv) {
inputManager = new InputManager;
utility = new Utility;
title = "bsnes v082.32";
title = "bsnes v082.34";
string fontFamily = Intrinsics::platform() == Intrinsics::Platform::Windows ? "Tahoma, " : "Sans, ";
normalFont = { fontFamily, "8" };

View File

@ -15,6 +15,8 @@ AdvancedSettings::AdvancedSettings() {
focusPolicy[2].setText("Pause emulation");
RadioBox::group(focusPolicy[0], focusPolicy[1], focusPolicy[2]);
focusPolicy[config->input.focusPolicy].setChecked();
aboutLabel.setFont(application->boldFont);
aboutLabel.setText("bsnes author: byuu license: GPLv3 website: http://byuu.org/");
lstring list;
@ -53,6 +55,8 @@ AdvancedSettings::AdvancedSettings() {
focusPolicyLayout.append(focusPolicy[0], ~0, 0, 5);
focusPolicyLayout.append(focusPolicy[1], ~0, 0, 5);
focusPolicyLayout.append(focusPolicy[2], ~0, 0);
append(spacer, ~0, ~0);
append(aboutLabel, ~0, 0);
videoDriver.onChange = [&] {
lstring list;

View File

@ -11,6 +11,8 @@ struct AdvancedSettings : SettingsLayout {
Label focusPolicyLabel;
HorizontalLayout focusPolicyLayout;
RadioBox focusPolicy[3];
Widget spacer;
Label aboutLabel;
AdvancedSettings();
};