mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
b8d607d16b
commit
ef85f7ccb0
|
@ -87,7 +87,7 @@ namespace GameBoy {
|
|||
clock = 0;
|
||||
}
|
||||
|
||||
inline Processor() : thread(0) {}
|
||||
inline Processor() : thread(nullptr) {}
|
||||
};
|
||||
|
||||
#include <gameboy/memory/memory.hpp>
|
||||
|
|
|
@ -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,8 +115,8 @@ 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 |= 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;
|
||||
|
@ -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,8 +228,8 @@ 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 & 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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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&);
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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];
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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&);
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
};
|
|
@ -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) {
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
||||
};
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
|
@ -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() {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -98,7 +98,6 @@ void mmio_write(unsigned addr, uint8 data) {
|
|||
}
|
||||
|
||||
void power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
|
|
|
@ -90,7 +90,6 @@ void ram_write(unsigned addr, uint8 data) {
|
|||
}
|
||||
|
||||
void power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
||||
};
|
|
@ -243,7 +243,6 @@ void reg_write(unsigned addr, uint8 data) {
|
|||
}
|
||||
|
||||
void power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
||||
};
|
|
@ -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");
|
||||
|
|
|
@ -58,8 +58,6 @@ void CPU::power() {
|
|||
ram[0x0009] = 0xef;
|
||||
ram[0x000a] = 0xdf;
|
||||
ram[0x000f] = 0xbf;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void CPU::reset() {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -47,6 +47,7 @@ void System::power() {
|
|||
ppu.power();
|
||||
input.reset();
|
||||
scheduler.power();
|
||||
reset();
|
||||
}
|
||||
|
||||
void System::reset() {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#define WINVER 0x0501
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#define _WIN32_IE 0x0600
|
||||
#define __MSVCRT_VERSION__ 0x0601
|
||||
#define NOMINMAX
|
||||
|
||||
#include <windows.h>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -118,7 +118,7 @@ namespace SNES {
|
|||
s.integer(clock);
|
||||
}
|
||||
|
||||
inline Processor() : thread(0) {}
|
||||
inline Processor() : thread(nullptr) {}
|
||||
};
|
||||
|
||||
struct ChipDebugger {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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" };
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -11,6 +11,8 @@ struct AdvancedSettings : SettingsLayout {
|
|||
Label focusPolicyLabel;
|
||||
HorizontalLayout focusPolicyLayout;
|
||||
RadioBox focusPolicy[3];
|
||||
Widget spacer;
|
||||
Label aboutLabel;
|
||||
|
||||
AdvancedSettings();
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue