Update to v095r09 release.

byuu says:

Changelog:

- all of fc/ ported to "auto function() -> return;" syntax
  - (includes all of cartridge/board and cartridge/chip as well; even
    though they're all deprecated)
- sfc balanced profile ported to "auto function() -> return;" syntax
- sfc balanced and performance profiles compile again
- Linux always gets -ldl
- removed arch=x86 logic from nall/GNUmakefile, as TDM/GCC64 can't
  produce bug-free 32-bit binaries anyway

The only code that continues to use the old function syntax is the SFC
performance core, obscure parts of nall that higan doesn't use, and the
pieces of code that weren't written by me (blargg's SFC-DSP, Ryphecha's
sinc resampler, and OV2's xaudio2 header file.)

I was too burned out to finish it tonight. The above was about four
hours straight of non-stop typing. Really can't wait to be done with
this once and for all.
This commit is contained in:
Tim Allen 2015-12-05 16:44:49 +11:00
parent a219f9c121
commit 65a3306ad5
105 changed files with 3917 additions and 4011 deletions

View File

@ -7,8 +7,6 @@ gba := gba
profile := accuracy
target := tomoko
# arch := x86
# console := true
# compiler
@ -28,16 +26,12 @@ endif
# platform
ifeq ($(platform),windows)
ifeq ($(arch),x86)
flags += -m32
link += -m32
endif
ifeq ($(console),true)
link += -mconsole
else
link += -mwindows
endif
link += -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 -lws2_32
link += -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32
link += -Wl,-enable-auto-import
link += -Wl,-enable-runtime-pseudo-reloc
else ifeq ($(platform),macosx)
@ -46,7 +40,7 @@ else ifeq ($(platform),linux)
flags += -march=native -fopenmp
link += -fopenmp
link += -Wl,-export-dynamic
link += -lX11 -lXext -ldl
link += -lX11 -lXext
else ifeq ($(platform),bsd)
flags += -march=native -fopenmp
link += -fopenmp

View File

@ -7,7 +7,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "095.08";
static const string Version = "095.09";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -11,38 +11,40 @@ namespace Famicom {
#include "serialization.cpp"
APU apu;
const uint8 APU::length_counter_table[32] = {
0x0a, 0xfe, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, 0xa0, 0x08, 0x3c, 0x0a, 0x0e, 0x0c, 0x1a, 0x0e,
0x0c, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, 0xc0, 0x18, 0x48, 0x1a, 0x10, 0x1c, 0x20, 0x1e,
};
APU::APU() {
for(uint amp : range(32)) {
if(amp == 0) {
pulse_dac[amp] = 0;
} else {
pulse_dac[amp] = 16384.0 * 95.88 / (8128.0 / amp + 100.0);
}
}
const uint16 APU::ntsc_noise_period_table[16] = {
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068,
};
for(uint dmc_amp : range(128)) {
for(uint triangle_amp : range(16)) {
for(uint noise_amp : range(16)) {
if(dmc_amp == 0 && triangle_amp == 0 && noise_amp == 0) {
dmc_triangle_noise_dac[dmc_amp][triangle_amp][noise_amp] = 0;
} else {
dmc_triangle_noise_dac[dmc_amp][triangle_amp][noise_amp]
= 16384.0 * 159.79 / (100.0 + 1.0 / (triangle_amp / 8227.0 + noise_amp / 12241.0 + dmc_amp / 22638.0));
}
}
}
}
}
const uint16 APU::pal_noise_period_table[16] = {
4, 7, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778,
};
const uint16 APU::ntsc_dmc_period_table[16] = {
428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54,
};
const uint16 APU::pal_dmc_period_table[16] = {
398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50,
};
void APU::Main() {
auto APU::Main() -> void {
apu.main();
}
void APU::main() {
auto APU::main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
unsigned pulse_output, triangle_output, noise_output, dmc_output;
uint pulse_output, triangle_output, noise_output, dmc_output;
pulse_output = pulse[0].clock();
pulse_output += pulse[1].clock();
@ -52,7 +54,7 @@ void APU::main() {
clock_frame_counter_divider();
signed output = pulse_dac[pulse_output] + dmc_triangle_noise_dac[dmc_output][triangle_output][noise_output];
int output = pulse_dac[pulse_output] + dmc_triangle_noise_dac[dmc_output][triangle_output][noise_output];
output = filter.run_hipass_strong(output);
output += cartridge_sample;
@ -66,20 +68,20 @@ void APU::main() {
}
}
void APU::tick() {
auto APU::tick() -> void {
clock += 12;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
}
void APU::set_irq_line() {
auto APU::set_irq_line() -> void {
cpu.set_irq_apu_line(frame.irq_pending || dmc.irq_pending);
}
void APU::set_sample(int16 sample) {
auto APU::set_sample(int16 sample) -> void {
cartridge_sample = sample;
}
void APU::power() {
auto APU::power() -> void {
filter.hipass_strong = 0;
filter.hipass_weak = 0;
filter.lopass = 0;
@ -91,7 +93,7 @@ void APU::power() {
dmc.power();
}
void APU::reset() {
auto APU::reset() -> void {
create(APU::Main, 21477272);
pulse[0].reset();
@ -112,7 +114,7 @@ void APU::reset() {
set_irq_line();
}
uint8 APU::read(uint16 addr) {
auto APU::read(uint16 addr) -> uint8 {
if(addr == 0x4015) {
uint8 result = 0x00;
result |= pulse[0].length_counter ? 0x01 : 0;
@ -132,8 +134,8 @@ uint8 APU::read(uint16 addr) {
return cpu.mdr();
}
void APU::write(uint16 addr, uint8 data) {
const unsigned n = (addr >> 2) & 1; //pulse#
auto APU::write(uint16 addr, uint8 data) -> void {
const uint n = (addr >> 2) & 1; //pulse#
switch(addr) {
case 0x4000: case 0x4004:
@ -254,22 +256,22 @@ void APU::write(uint16 addr, uint8 data) {
}
}
signed APU::Filter::run_hipass_strong(signed sample) {
auto APU::Filter::run_hipass_strong(int sample) -> int {
hipass_strong += ((((int64)sample << 16) - (hipass_strong >> 16)) * HiPassStrong) >> 16;
return sample - (hipass_strong >> 32);
}
signed APU::Filter::run_hipass_weak(signed sample) {
auto APU::Filter::run_hipass_weak(int sample) -> int {
hipass_weak += ((((int64)sample << 16) - (hipass_weak >> 16)) * HiPassWeak) >> 16;
return sample - (hipass_weak >> 32);
}
signed APU::Filter::run_lopass(signed sample) {
auto APU::Filter::run_lopass(int sample) -> int {
lopass += ((((int64)sample << 16) - (lopass >> 16)) * LoPass) >> 16;
return (lopass >> 32);
}
void APU::clock_frame_counter() {
auto APU::clock_frame_counter() -> void {
frame.counter++;
if(frame.counter & 1) {
@ -295,7 +297,7 @@ void APU::clock_frame_counter() {
}
}
void APU::clock_frame_counter_divider() {
auto APU::clock_frame_counter_divider() -> void {
frame.divider -= 2;
if(frame.divider <= 0) {
clock_frame_counter();
@ -303,27 +305,25 @@ void APU::clock_frame_counter_divider() {
}
}
APU::APU() {
for(unsigned amp = 0; amp < 32; amp++) {
if(amp == 0) {
pulse_dac[amp] = 0;
} else {
pulse_dac[amp] = 16384.0 * 95.88 / (8128.0 / amp + 100.0);
}
}
const uint8 APU::length_counter_table[32] = {
0x0a, 0xfe, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, 0xa0, 0x08, 0x3c, 0x0a, 0x0e, 0x0c, 0x1a, 0x0e,
0x0c, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, 0xc0, 0x18, 0x48, 0x1a, 0x10, 0x1c, 0x20, 0x1e,
};
for(unsigned dmc_amp = 0; dmc_amp < 128; dmc_amp++) {
for(unsigned triangle_amp = 0; triangle_amp < 16; triangle_amp++) {
for(unsigned noise_amp = 0; noise_amp < 16; noise_amp++) {
if(dmc_amp == 0 && triangle_amp == 0 && noise_amp == 0) {
dmc_triangle_noise_dac[dmc_amp][triangle_amp][noise_amp] = 0;
} else {
dmc_triangle_noise_dac[dmc_amp][triangle_amp][noise_amp]
= 16384.0 * 159.79 / (100.0 + 1.0 / (triangle_amp / 8227.0 + noise_amp / 12241.0 + dmc_amp / 22638.0));
}
}
}
}
}
const uint16 APU::ntsc_noise_period_table[16] = {
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068,
};
const uint16 APU::pal_noise_period_table[16] = {
4, 7, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778,
};
const uint16 APU::ntsc_dmc_period_table[16] = {
428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54,
};
const uint16 APU::pal_dmc_period_table[16] = {
398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50,
};
}

View File

@ -1,31 +1,33 @@
struct APU : Thread {
static void Main();
void main();
void tick();
void set_irq_line();
void set_sample(int16 sample);
void power();
void reset();
uint8 read(uint16 addr);
void write(uint16 addr, uint8 data);
void serialize(serializer&);
APU();
static auto Main() -> void;
auto main() -> void;
auto tick() -> void;
auto set_irq_line() -> void;
auto set_sample(int16 sample) -> void;
auto power() -> void;
auto reset() -> void;
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
auto serialize(serializer&) -> void;
struct Filter {
enum : signed { HiPassStrong = 225574, HiPassWeak = 57593, LoPass = 86322413 };
auto run_hipass_strong(int sample) -> int;
auto run_hipass_weak(int sample) -> int;
auto run_lopass(int sample) -> int;
auto serialize(serializer&) -> void;
enum : int { HiPassStrong = 225574, HiPassWeak = 57593, LoPass = 86322413 };
int64 hipass_strong;
int64 hipass_weak;
int64 lopass;
signed run_hipass_strong(signed sample);
signed run_hipass_weak(signed sample);
signed run_lopass(signed sample);
void serialize(serializer&);
} filter;
};
#include "envelope.hpp"
#include "sweep.hpp"
@ -35,19 +37,22 @@ struct APU : Thread {
#include "dmc.hpp"
struct FrameCounter {
enum : unsigned { NtscPeriod = 14915 }; //~(21.477MHz / 6 / 240hz)
auto serialize(serializer&) -> void;
enum : uint { NtscPeriod = 14915 }; //~(21.477MHz / 6 / 240hz)
bool irq_pending;
uint2 mode;
uint2 counter;
signed divider;
int divider;
};
void serialize(serializer&);
} frame;
auto clock_frame_counter() -> void;
auto clock_frame_counter_divider() -> void;
void clock_frame_counter();
void clock_frame_counter_divider();
Filter filter;
FrameCounter frame;
uint8 enabled_channels;
int16 cartridge_sample;

View File

@ -1,18 +1,18 @@
void APU::DMC::start() {
auto APU::DMC::start() -> void {
if(length_counter == 0) {
read_addr = 0x4000 + (addr_latch << 6);
length_counter = (length_latch << 4) + 1;
}
}
void APU::DMC::stop() {
auto APU::DMC::stop() -> void {
length_counter = 0;
dma_delay_counter = 0;
cpu.set_rdy_line(1);
cpu.set_rdy_addr(false);
}
uint8 APU::DMC::clock() {
auto APU::DMC::clock() -> uint8 {
uint8 result = dac_latch;
if(dma_delay_counter > 0) {
@ -42,8 +42,8 @@ uint8 APU::DMC::clock() {
if(--period_counter == 0) {
if(have_sample) {
signed delta = (((sample >> bit_counter) & 1) << 2) - 2;
unsigned data = dac_latch + delta;
int delta = (((sample >> bit_counter) & 1) << 2) - 2;
uint data = dac_latch + delta;
if((data & 0x80) == 0) dac_latch = data;
}
@ -68,10 +68,10 @@ uint8 APU::DMC::clock() {
return result;
}
void APU::DMC::power() {
auto APU::DMC::power() -> void {
}
void APU::DMC::reset() {
auto APU::DMC::reset() -> void {
length_counter = 0;
irq_pending = 0;
@ -91,7 +91,7 @@ void APU::DMC::reset() {
sample = 0;
}
void APU::DMC::serialize(serializer& s) {
auto APU::DMC::serialize(serializer& s) -> void {
s.integer(length_counter);
s.integer(irq_pending);

View File

@ -1,9 +1,18 @@
struct DMC {
unsigned length_counter;
auto start() -> void;
auto stop() -> void;
auto clock() -> uint8;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
uint length_counter;
bool irq_pending;
uint4 period;
unsigned period_counter;
uint period_counter;
bool irq_enable;
bool loop_mode;
@ -13,7 +22,7 @@ struct DMC {
uint8 length_latch;
uint15 read_addr;
unsigned dma_delay_counter;
uint dma_delay_counter;
uint3 bit_counter;
bool have_dma_buffer;
@ -21,12 +30,4 @@ struct DMC {
bool have_sample;
uint8 sample;
void start();
void stop();
uint8 clock();
void power();
void reset();
void serialize(serializer&);
} dmc;

View File

@ -1,8 +1,8 @@
unsigned APU::Envelope::volume() const {
auto APU::Envelope::volume() const -> uint {
return use_speed_as_volume ? speed : decay_volume;
}
void APU::Envelope::clock() {
auto APU::Envelope::clock() -> void {
if(reload_decay) {
reload_decay = false;
decay_volume = 0x0f;
@ -16,10 +16,10 @@ void APU::Envelope::clock() {
}
}
void APU::Envelope::power() {
auto APU::Envelope::power() -> void {
}
void APU::Envelope::reset() {
auto APU::Envelope::reset() -> void {
speed = 0;
use_speed_as_volume = 0;
loop_mode = 0;
@ -28,7 +28,7 @@ void APU::Envelope::reset() {
decay_volume = 0;
}
void APU::Envelope::serialize(serializer& s) {
auto APU::Envelope::serialize(serializer& s) -> void {
s.integer(speed);
s.integer(use_speed_as_volume);
s.integer(loop_mode);

View File

@ -1,4 +1,12 @@
struct Envelope {
auto volume() const -> uint;
auto clock() -> void;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
uint4 speed;
bool use_speed_as_volume;
bool loop_mode;
@ -6,11 +14,4 @@ struct Envelope {
bool reload_decay;
uint8 decay_counter;
uint4 decay_volume;
unsigned volume() const;
void clock();
void power();
void reset();
void serialize(serializer&);
};

View File

@ -1,16 +1,16 @@
void APU::Noise::clock_length() {
auto APU::Noise::clock_length() -> void {
if(envelope.loop_mode == 0) {
if(length_counter > 0) length_counter--;
}
}
uint8 APU::Noise::clock() {
auto APU::Noise::clock() -> uint8 {
if(length_counter == 0) return 0;
uint8 result = (lfsr & 1) ? envelope.volume() : 0;
if(--period_counter == 0) {
unsigned feedback;
uint feedback;
if(short_mode) {
feedback = ((lfsr >> 0) & 1) ^ ((lfsr >> 6) & 1);
@ -25,10 +25,10 @@ uint8 APU::Noise::clock() {
return result;
}
void APU::Noise::power() {
auto APU::Noise::power() -> void {
}
void APU::Noise::reset() {
auto APU::Noise::reset() -> void {
length_counter = 0;
envelope.speed = 0;
@ -44,7 +44,7 @@ void APU::Noise::reset() {
lfsr = 1;
}
void APU::Noise::serialize(serializer& s) {
auto APU::Noise::serialize(serializer& s) -> void {
s.integer(length_counter);
envelope.serialize(s);

View File

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

View File

@ -1,14 +1,14 @@
void APU::Pulse::clock_length() {
auto APU::Pulse::clock_length() -> void {
if(envelope.loop_mode == 0) {
if(length_counter) length_counter--;
}
}
uint8 APU::Pulse::clock() {
auto APU::Pulse::clock() -> uint8 {
if(sweep.check_period() == false) return 0;
if(length_counter == 0) return 0;
static const unsigned duty_table[] = {1, 2, 4, 6};
static const uint duty_table[] = {1, 2, 4, 6};
uint8 result = (duty_counter < duty_table[duty]) ? envelope.volume() : 0;
if(sweep.pulse_period < 0x008) result = 0;
@ -20,12 +20,12 @@ uint8 APU::Pulse::clock() {
return result;
}
void APU::Pulse::power() {
auto APU::Pulse::power() -> void {
envelope.power();
sweep.power();
}
void APU::Pulse::reset() {
auto APU::Pulse::reset() -> void {
envelope.reset();
sweep.reset();
@ -37,7 +37,7 @@ void APU::Pulse::reset() {
period_counter = 1;
}
void APU::Pulse::serialize(serializer& s) {
auto APU::Pulse::serialize(serializer& s) -> void {
s.integer(length_counter);
envelope.serialize(s);

View File

@ -1,5 +1,14 @@
struct Pulse {
unsigned length_counter;
auto clock_length() -> void;
auto check_period() -> bool;
auto clock() -> uint8;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
uint length_counter;
Envelope envelope;
Sweep sweep;
@ -8,13 +17,5 @@ struct Pulse {
uint3 duty_counter;
uint11 period;
unsigned period_counter;
void clock_length();
bool check_period();
uint8 clock();
void power();
void reset();
void serialize(serializer&);
uint period_counter;
} pulse[2];

View File

@ -1,4 +1,4 @@
void APU::serialize(serializer& s) {
auto APU::serialize(serializer& s) -> void {
Thread::serialize(s);
filter.serialize(s);
@ -13,13 +13,13 @@ void APU::serialize(serializer& s) {
s.integer(cartridge_sample);
}
void APU::Filter::serialize(serializer& s) {
auto APU::Filter::serialize(serializer& s) -> void {
s.integer(hipass_strong);
s.integer(hipass_weak);
s.integer(lopass);
}
void APU::FrameCounter::serialize(serializer& s) {
auto APU::FrameCounter::serialize(serializer& s) -> void {
s.integer(irq_pending);
s.integer(mode);

View File

@ -1,4 +1,4 @@
bool APU::Sweep::check_period() {
auto APU::Sweep::check_period() -> bool {
if(pulse_period > 0x7ff) return false;
if(decrement == 0) {
@ -8,11 +8,11 @@ bool APU::Sweep::check_period() {
return true;
}
void APU::Sweep::clock(unsigned channel) {
auto APU::Sweep::clock(uint channel) -> void {
if(--counter == 0) {
counter = period + 1;
if(enable && shift && pulse_period > 8) {
signed delta = pulse_period >> shift;
int delta = pulse_period >> shift;
if(decrement) {
pulse_period -= delta;
@ -29,7 +29,7 @@ void APU::Sweep::clock(unsigned channel) {
}
}
void APU::Sweep::power() {
auto APU::Sweep::power() -> void {
shift = 0;
decrement = 0;
period = 0;
@ -39,10 +39,10 @@ void APU::Sweep::power() {
pulse_period = 0;
}
void APU::Sweep::reset() {
auto APU::Sweep::reset() -> void {
}
void APU::Sweep::serialize(serializer& s) {
auto APU::Sweep::serialize(serializer& s) -> void {
s.integer(shift);
s.integer(decrement);
s.integer(period);

View File

@ -1,4 +1,12 @@
struct Sweep {
auto check_period() -> bool;
auto clock(uint channel) -> void;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
uint8 shift;
bool decrement;
uint3 period;
@ -6,11 +14,4 @@ struct Sweep {
bool enable;
bool reload;
uint11 pulse_period;
bool check_period();
void clock(unsigned channel);
void power();
void reset();
void serialize(serializer&);
};

View File

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

View File

@ -1,21 +1,22 @@
struct Triangle {
unsigned length_counter;
auto clock_length() -> void;
auto clock_linear_length() -> void;
auto clock() -> uint8;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
uint length_counter;
uint8 linear_length;
bool halt_length_counter;
uint11 period;
unsigned period_counter;
uint 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

@ -1,117 +1,115 @@
//BANDAI-FCG
struct BandaiFCG : Board {
BandaiFCG(Markup::Node& document) : Board(document) {
}
uint8 chr_bank[8];
uint8 prg_bank;
uint2 mirror;
bool irq_counter_enable;
uint16 irq_counter;
uint16 irq_latch;
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
void main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
if(irq_counter_enable) {
if(--irq_counter == 0xffff) {
cpu.set_irq_line(1);
irq_counter_enable = false;
}
}
tick();
}
}
if(irq_counter_enable) {
if(--irq_counter == 0xffff) {
cpu.set_irq_line(1);
irq_counter_enable = false;
auto ciram_addr(uint addr) const -> uint {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
case 2: return 0x0000 | (addr & 0x03ff);
case 3: return 0x0400 | (addr & 0x03ff);
}
}
auto prg_read(uint addr) -> uint8 {
if(addr & 0x8000) {
bool region = addr & 0x4000;
uint bank = (region == 0 ? prg_bank : 0x0f);
return prgrom.read((bank << 14) | (addr & 0x3fff));
}
return cpu.mdr();
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr >= 0x6000) {
switch(addr & 15) {
case 0x00: case 0x01: case 0x02: case 0x03:
case 0x04: case 0x05: case 0x06: case 0x07:
chr_bank[addr & 7] = data;
break;
case 0x08:
prg_bank = data & 0x0f;
break;
case 0x09:
mirror = data & 0x03;
break;
case 0x0a:
cpu.set_irq_line(0);
irq_counter_enable = data & 0x01;
irq_counter = irq_latch;
break;
case 0x0b:
irq_latch = (irq_latch & 0xff00) | (data << 0);
break;
case 0x0c:
irq_latch = (irq_latch & 0x00ff) | (data << 8);
break;
case 0x0d:
//TODO: serial EEPROM support
break;
}
}
tick();
}
}
unsigned ciram_addr(unsigned addr) const {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
case 2: return 0x0000 | (addr & 0x03ff);
case 3: return 0x0400 | (addr & 0x03ff);
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
return Board::chr_read(addr);
}
}
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) {
bool region = addr & 0x4000;
unsigned bank = (region == 0 ? prg_bank : 0x0f);
return prgrom.read((bank << 14) | (addr & 0x3fff));
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
return Board::chr_write(addr, data);
}
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if(addr >= 0x6000) {
switch(addr & 15) {
case 0x00: case 0x01: case 0x02: case 0x03:
case 0x04: case 0x05: case 0x06: case 0x07:
chr_bank[addr & 7] = data;
break;
case 0x08:
prg_bank = data & 0x0f;
break;
case 0x09:
mirror = data & 0x03;
break;
case 0x0a:
cpu.set_irq_line(0);
irq_counter_enable = data & 0x01;
irq_counter = irq_latch;
break;
case 0x0b:
irq_latch = (irq_latch & 0xff00) | (data << 0);
break;
case 0x0c:
irq_latch = (irq_latch & 0x00ff) | (data << 8);
break;
case 0x0d:
//TODO: serial EEPROM support
break;
}
auto power() -> void {
reset();
}
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
return Board::chr_read(addr);
}
auto reset() -> void {
for(auto &n : chr_bank) n = 0;
prg_bank = 0;
mirror = 0;
irq_counter_enable = 0;
irq_counter = 0;
irq_latch = 0;
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
return Board::chr_write(addr, data);
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
void power() {
reset();
}
void reset() {
for(auto &n : chr_bank) n = 0;
prg_bank = 0;
mirror = 0;
irq_counter_enable = 0;
irq_counter = 0;
irq_latch = 0;
}
void serialize(serializer& s) {
Board::serialize(s);
s.array(chr_bank);
s.integer(prg_bank);
s.integer(mirror);
s.integer(irq_counter_enable);
s.integer(irq_counter);
s.integer(irq_latch);
}
BandaiFCG(Markup::Node& document) : Board(document) {
}
s.array(chr_bank);
s.integer(prg_bank);
s.integer(mirror);
s.integer(irq_counter_enable);
s.integer(irq_counter);
s.integer(irq_latch);
}
uint8 chr_bank[8];
uint8 prg_bank;
uint2 mirror;
bool irq_counter_enable;
uint16 irq_counter;
uint16 irq_latch;
};

View File

@ -19,69 +19,6 @@
#include "nes-uxrom.cpp"
#include "sunsoft-5b.cpp"
uint8 Board::Memory::read(unsigned addr) const {
return data[mirror(addr, size)];
}
void Board::Memory::write(unsigned addr, uint8 byte) {
if(writable) data[mirror(addr, size)] = byte;
}
unsigned Board::mirror(unsigned addr, unsigned size) {
unsigned base = 0;
if(size) {
unsigned mask = 1 << 23;
while(addr >= size) {
while(!(addr & mask)) mask >>= 1;
addr -= mask;
if(size > mask) {
size -= mask;
base += mask;
}
mask >>= 1;
}
base += addr;
}
return base;
}
void Board::main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
cartridge.clock += 12 * 4095;
tick();
}
}
void Board::tick() {
cartridge.clock += 12;
if(cartridge.clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
}
uint8 Board::chr_read(unsigned addr) {
if(chrram.size) return chrram.data[mirror(addr, chrram.size)];
if(chrrom.size) return chrrom.data[mirror(addr, chrrom.size)];
return 0u;
}
void Board::chr_write(unsigned addr, uint8 data) {
if(chrram.size) chrram.data[mirror(addr, chrram.size)] = data;
}
void Board::power() {
}
void Board::reset() {
}
void Board::serialize(serializer& s) {
if(prgram.size) s.array(prgram.data, prgram.size);
if(chrram.size) s.array(chrram.data, chrram.size);
}
Board::Board(Markup::Node& document) {
cartridge.board = this;
auto cartridge = document["cartridge"];
@ -116,10 +53,70 @@ Board::Board(Markup::Node& document) {
chrram.writable = true;
}
Board::~Board() {
auto Board::Memory::read(uint addr) const -> uint8 {
return data[mirror(addr, size)];
}
Board* Board::load(string manifest) {
auto Board::Memory::write(uint addr, uint8 byte) -> void {
if(writable) data[mirror(addr, size)] = byte;
}
auto Board::mirror(uint addr, uint size) -> uint {
uint base = 0;
if(size) {
uint mask = 1 << 23;
while(addr >= size) {
while(!(addr & mask)) mask >>= 1;
addr -= mask;
if(size > mask) {
size -= mask;
base += mask;
}
mask >>= 1;
}
base += addr;
}
return base;
}
auto Board::main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
cartridge.clock += 12 * 4095;
tick();
}
}
auto Board::tick() -> void {
cartridge.clock += 12;
if(cartridge.clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
}
auto Board::chr_read(uint addr) -> uint8 {
if(chrram.size) return chrram.data[mirror(addr, chrram.size)];
if(chrrom.size) return chrrom.data[mirror(addr, chrrom.size)];
return 0u;
}
auto Board::chr_write(uint addr, uint8 data) -> void {
if(chrram.size) chrram.data[mirror(addr, chrram.size)] = data;
}
auto Board::power() -> void {
}
auto Board::reset() -> void {
}
auto Board::serialize(serializer& s) -> void {
if(prgram.size) s.array(prgram.data, prgram.size);
if(chrram.size) s.array(chrram.data, chrram.size);
}
auto Board::load(string manifest) -> Board* {
auto document = BML::unserialize(manifest);
cartridge.information.title = document["information/title"].text();

View File

@ -1,38 +1,39 @@
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(uint8_t* data, uint size) : data(data), size(size) {}
inline Memory() : data(nullptr), size(0u), writable(false) {}
inline ~Memory() { if(data) delete[] data; }
inline auto read(uint addr) const -> uint8;
inline auto write(uint addr, uint8 data) -> void;
uint8_t* data;
uint size;
bool writable;
};
static unsigned mirror(unsigned addr, unsigned size);
virtual void main();
virtual void tick();
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 inline void scanline(unsigned y) {}
virtual void power();
virtual void reset();
virtual void serialize(serializer&);
Board(Markup::Node& document);
virtual ~Board();
virtual ~Board() = default;
static Board* load(string manifest);
static auto mirror(uint addr, uint size) -> uint;
virtual auto main() -> void;
virtual auto tick() -> void;
virtual auto prg_read(uint addr) -> uint8 = 0;
virtual auto prg_write(uint addr, uint8 data) -> void = 0;
virtual auto chr_read(uint addr) -> uint8;
virtual auto chr_write(uint addr, uint8 data) -> void;
virtual inline auto scanline(uint y) -> void {}
virtual auto power() -> void;
virtual auto reset() -> void;
virtual auto serialize(serializer&) -> void;
static auto load(string manifest) -> Board*;
struct Information {
string type;

View File

@ -1,40 +1,38 @@
struct KonamiVRC1 : Board {
KonamiVRC1(Markup::Node& document) : Board(document), vrc1(*this) {
}
VRC1 vrc1;
auto prg_read(uint addr) -> uint8 {
if(addr & 0x8000) return prgrom.read(vrc1.prg_addr(addr));
return cpu.mdr();
}
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) return prgrom.read(vrc1.prg_addr(addr));
return cpu.mdr();
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr & 0x8000) return vrc1.reg_write(addr, data);
}
void prg_write(unsigned addr, uint8 data) {
if(addr & 0x8000) return vrc1.reg_write(addr, data);
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(vrc1.ciram_addr(addr));
return Board::chr_read(vrc1.chr_addr(addr));
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read(vrc1.ciram_addr(addr));
return Board::chr_read(vrc1.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(vrc1.ciram_addr(addr), data);
return Board::chr_write(vrc1.chr_addr(addr), data);
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) return ppu.ciram_write(vrc1.ciram_addr(addr), data);
return Board::chr_write(vrc1.chr_addr(addr), data);
}
auto power() -> void {
vrc1.power();
}
void power() {
vrc1.power();
}
auto reset() -> void {
vrc1.reset();
}
void reset() {
vrc1.reset();
}
void serialize(serializer& s) {
Board::serialize(s);
vrc1.serialize(s);
}
KonamiVRC1(Markup::Node& document) : Board(document), vrc1(*this) {
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
vrc1.serialize(s);
}
VRC1 vrc1;
};

View File

@ -1,57 +1,55 @@
struct KonamiVRC2 : Board {
KonamiVRC2(Markup::Node& document) : Board(document), vrc2(*this) {
settings.pinout.a0 = 1 << document["cartridge/chip/pinout/a0"].natural();
settings.pinout.a1 = 1 << document["cartridge/chip/pinout/a1"].natural();
}
struct Settings {
struct Pinout {
unsigned a0;
unsigned a1;
} pinout;
} settings;
auto prg_read(uint addr) -> uint8 {
if(addr < 0x6000) return cpu.mdr();
if(addr < 0x8000) return vrc2.ram_read(addr);
return prgrom.read(vrc2.prg_addr(addr));
}
VRC2 vrc2;
auto prg_write(uint addr, uint8 data) -> void {
if(addr < 0x6000) return;
if(addr < 0x8000) return vrc2.ram_write(addr, data);
uint8 prg_read(unsigned addr) {
if(addr < 0x6000) return cpu.mdr();
if(addr < 0x8000) return vrc2.ram_read(addr);
return prgrom.read(vrc2.prg_addr(addr));
}
bool a0 = (addr & settings.pinout.a0);
bool a1 = (addr & settings.pinout.a1);
addr &= 0xfff0;
addr |= (a0 << 0) | (a1 << 1);
return vrc2.reg_write(addr, data);
}
void prg_write(unsigned addr, uint8 data) {
if(addr < 0x6000) return;
if(addr < 0x8000) return vrc2.ram_write(addr, data);
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(vrc2.ciram_addr(addr));
return Board::chr_read(vrc2.chr_addr(addr));
}
bool a0 = (addr & settings.pinout.a0);
bool a1 = (addr & settings.pinout.a1);
addr &= 0xfff0;
addr |= (a0 << 0) | (a1 << 1);
return vrc2.reg_write(addr, data);
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(vrc2.ciram_addr(addr), data);
return Board::chr_write(vrc2.chr_addr(addr), data);
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read(vrc2.ciram_addr(addr));
return Board::chr_read(vrc2.chr_addr(addr));
}
auto power() -> void {
vrc2.power();
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) return ppu.ciram_write(vrc2.ciram_addr(addr), data);
return Board::chr_write(vrc2.chr_addr(addr), data);
}
auto reset() -> void {
vrc2.reset();
}
void power() {
vrc2.power();
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
vrc2.serialize(s);
}
void reset() {
vrc2.reset();
}
void serialize(serializer& s) {
Board::serialize(s);
vrc2.serialize(s);
}
KonamiVRC2(Markup::Node& document) : Board(document), vrc2(*this) {
settings.pinout.a0 = 1 << document["cartridge/chip/pinout/a0"].natural();
settings.pinout.a1 = 1 << document["cartridge/chip/pinout/a1"].natural();
}
struct Settings {
struct Pinout {
uint a0;
uint a1;
} pinout;
} settings;
VRC2 vrc2;
};

View File

@ -1,57 +1,55 @@
struct KonamiVRC3 : Board {
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
VRC3 vrc3;
void main() {
vrc3.main();
}
uint8 prg_read(unsigned addr) {
if((addr & 0xe000) == 0x6000) return prgram.read(addr & 0x1fff);
if(addr & 0x8000) return prgrom.read(vrc3.prg_addr(addr));
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if((addr & 0xe000) == 0x6000) return prgram.write(addr & 0x1fff, data);
if(addr & 0x8000) return vrc3.reg_write(addr, data);
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
KonamiVRC3(Markup::Node& document) : Board(document), vrc3(*this) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
return chrram.read(addr);
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
auto main() -> void {
vrc3.main();
}
return chrram.write(addr, data);
}
void power() {
vrc3.power();
}
auto prg_read(uint addr) -> uint8 {
if((addr & 0xe000) == 0x6000) return prgram.read(addr & 0x1fff);
if(addr & 0x8000) return prgrom.read(vrc3.prg_addr(addr));
return cpu.mdr();
}
void reset() {
vrc3.reset();
}
auto prg_write(uint addr, uint8 data) -> void {
if((addr & 0xe000) == 0x6000) return prgram.write(addr & 0x1fff, data);
if(addr & 0x8000) return vrc3.reg_write(addr, data);
}
void serialize(serializer& s) {
Board::serialize(s);
vrc3.serialize(s);
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
}
return chrram.read(addr);
}
KonamiVRC3(Markup::Node& document) : Board(document), vrc3(*this) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
}
return chrram.write(addr, data);
}
auto power() -> void {
vrc3.power();
}
auto reset() -> void {
vrc3.reset();
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
vrc3.serialize(s);
}
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
VRC3 vrc3;
};

View File

@ -1,61 +1,59 @@
struct KonamiVRC4 : Board {
KonamiVRC4(Markup::Node& document) : Board(document), vrc4(*this) {
settings.pinout.a0 = 1 << document["cartridge/chip/pinout/a0"].natural();
settings.pinout.a1 = 1 << document["cartridge/chip/pinout/a1"].natural();
}
struct Settings {
struct Pinout {
unsigned a0;
unsigned a1;
} pinout;
} settings;
auto main() -> void {
return vrc4.main();
}
VRC4 vrc4;
auto prg_read(uint addr) -> uint8 {
if(addr < 0x6000) return cpu.mdr();
if(addr < 0x8000) return prgram.read(addr);
return prgrom.read(vrc4.prg_addr(addr));
}
void main() {
return vrc4.main();
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr < 0x6000) return;
if(addr < 0x8000) return prgram.write(addr, data);
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));
}
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);
}
void prg_write(unsigned addr, uint8 data) {
if(addr < 0x6000) return;
if(addr < 0x8000) return prgram.write(addr, data);
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(vrc4.ciram_addr(addr));
return Board::chr_read(vrc4.chr_addr(addr));
}
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);
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(vrc4.ciram_addr(addr), data);
return Board::chr_write(vrc4.chr_addr(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));
}
auto power() -> void {
vrc4.power();
}
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);
}
auto reset() -> void {
vrc4.reset();
}
void power() {
vrc4.power();
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
vrc4.serialize(s);
}
void reset() {
vrc4.reset();
}
void serialize(serializer& s) {
Board::serialize(s);
vrc4.serialize(s);
}
KonamiVRC4(Markup::Node& document) : Board(document), vrc4(*this) {
settings.pinout.a0 = 1 << document["cartridge/chip/pinout/a0"].natural();
settings.pinout.a1 = 1 << document["cartridge/chip/pinout/a1"].natural();
}
struct Settings {
struct Pinout {
uint a0;
uint a1;
} pinout;
} settings;
VRC4 vrc4;
};

View File

@ -1,42 +1,40 @@
struct KonamiVRC6 : Board {
VRC6 vrc6;
uint8 prg_read(unsigned addr) {
if((addr & 0xe000) == 0x6000) return vrc6.ram_read(addr);
if(addr & 0x8000) return prgrom.read(vrc6.prg_addr(addr));
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if((addr & 0xe000) == 0x6000) return vrc6.ram_write(addr, data);
if(addr & 0x8000) {
addr = (addr & 0xf003);
if(prgram.size) addr = (addr & ~3) | ((addr & 2) >> 1) | ((addr & 1) << 1);
return vrc6.reg_write(addr, data);
KonamiVRC6(Markup::Node& document) : Board(document), vrc6(*this) {
}
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read(vrc6.ciram_addr(addr));
return Board::chr_read(vrc6.chr_addr(addr));
}
auto prg_read(uint addr) -> uint8{
if((addr & 0xe000) == 0x6000) return vrc6.ram_read(addr);
if(addr & 0x8000) return prgrom.read(vrc6.prg_addr(addr));
return cpu.mdr();
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) return ppu.ciram_write(vrc6.ciram_addr(addr), data);
return Board::chr_write(vrc6.chr_addr(addr), data);
}
auto prg_write(uint addr, uint8 data) -> void {
if((addr & 0xe000) == 0x6000) return vrc6.ram_write(addr, data);
if(addr & 0x8000) {
addr = (addr & 0xf003);
if(prgram.size) addr = (addr & ~3) | ((addr & 2) >> 1) | ((addr & 1) << 1);
return vrc6.reg_write(addr, data);
}
}
void serialize(serializer& s) {
Board::serialize(s);
vrc6.serialize(s);
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(vrc6.ciram_addr(addr));
return Board::chr_read(vrc6.chr_addr(addr));
}
void main() { vrc6.main(); }
void power() { vrc6.power(); }
void reset() { vrc6.reset(); }
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(vrc6.ciram_addr(addr), data);
return Board::chr_write(vrc6.chr_addr(addr), data);
}
KonamiVRC6(Markup::Node& document) : Board(document), vrc6(*this) {
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
vrc6.serialize(s);
}
auto main() -> void { vrc6.main(); }
auto power() -> void { vrc6.power(); }
auto reset() -> void { vrc6.reset(); }
VRC6 vrc6;
};

View File

@ -1,47 +1,45 @@
struct KonamiVRC7 : Board {
KonamiVRC7(Markup::Node& document) : Board(document), vrc7(*this) {
}
VRC7 vrc7;
auto main() -> void {
return vrc7.main();
}
void main() {
return vrc7.main();
}
auto prg_read(uint addr) -> uint8 {
if(addr < 0x6000) return cpu.mdr();
if(addr < 0x8000) return prgram.read(addr);
return prgrom.read(vrc7.prg_addr(addr));
}
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));
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr < 0x6000) return;
if(addr < 0x8000) return prgram.write(addr, data);
return vrc7.reg_write(addr, data);
}
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);
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(vrc7.ciram_addr(addr));
return chrram.read(vrc7.chr_addr(addr));
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read(vrc7.ciram_addr(addr));
return chrram.read(vrc7.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(vrc7.ciram_addr(addr), data);
return chrram.write(vrc7.chr_addr(addr), data);
}
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);
}
auto power() -> void {
vrc7.power();
}
void power() {
vrc7.power();
}
auto reset() -> void {
vrc7.reset();
}
void reset() {
vrc7.reset();
}
void serialize(serializer& s) {
Board::serialize(s);
vrc7.serialize(s);
}
KonamiVRC7(Markup::Node& document) : Board(document), vrc7(*this) {
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
vrc7.serialize(s);
}
VRC7 vrc7;
};

View File

@ -4,48 +4,46 @@
//NES-AOROM
struct NES_AxROM : Board {
uint4 prg_bank;
bool mirror_select;
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if(addr & 0x8000) {
prg_bank = data & 0x0f;
mirror_select = data & 0x10;
NES_AxROM(Markup::Node& document) : Board(document) {
}
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read((mirror_select << 10) | (addr & 0x03ff));
return Board::chr_read(addr);
}
auto prg_read(uint addr) -> uint8 {
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
return cpu.mdr();
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) return ppu.ciram_write((mirror_select << 10) | (addr & 0x03ff), data);
return Board::chr_write(addr, data);
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr & 0x8000) {
prg_bank = data & 0x0f;
mirror_select = data & 0x10;
}
}
void power() {
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read((mirror_select << 10) | (addr & 0x03ff));
return Board::chr_read(addr);
}
void reset() {
prg_bank = 0x0f;
mirror_select = 0;
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write((mirror_select << 10) | (addr & 0x03ff), data);
return Board::chr_write(addr, data);
}
void serialize(serializer& s) {
Board::serialize(s);
auto power() -> void {
}
s.integer(prg_bank);
s.integer(mirror_select);
}
auto reset() -> void {
prg_bank = 0x0f;
mirror_select = 0;
}
NES_AxROM(Markup::Node& document) : Board(document) {
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
s.integer(prg_bank);
s.integer(mirror_select);
}
uint4 prg_bank;
bool mirror_select;
};

View File

@ -1,52 +1,50 @@
//NES-BN-ROM-01
struct NES_BNROM : Board {
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint2 prg_bank;
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if(addr & 0x8000) prg_bank = data & 0x03;
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr);
NES_BNROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
return Board::chr_read(addr);
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr, data);
auto prg_read(uint addr) -> uint8 {
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
return cpu.mdr();
}
return Board::chr_write(addr, data);
}
void power() {
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr & 0x8000) prg_bank = data & 0x03;
}
void reset() {
prg_bank = 0;
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr);
}
return Board::chr_read(addr);
}
void serialize(serializer& s) {
Board::serialize(s);
s.integer(prg_bank);
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr, data);
}
return Board::chr_write(addr, data);
}
NES_BNROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
auto power() -> void {
}
auto reset() -> void {
prg_bank = 0;
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
s.integer(prg_bank);
}
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint2 prg_bank;
};

View File

@ -1,54 +1,52 @@
//NES-CNROM
struct NES_CNROM : Board {
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint2 chr_bank;
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) return prgrom.read(addr & 0x7fff);
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if(addr & 0x8000) chr_bank = data & 0x03;
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
NES_CNROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
return Board::chr_read(addr);
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
auto prg_read(uint addr) -> uint8 {
if(addr & 0x8000) return prgrom.read(addr & 0x7fff);
return cpu.mdr();
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
Board::chr_write(addr, data);
}
void power() {
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr & 0x8000) chr_bank = data & 0x03;
}
void reset() {
chr_bank = 0;
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
return Board::chr_read(addr);
}
void serialize(serializer& s) {
Board::serialize(s);
s.integer(chr_bank);
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
Board::chr_write(addr, data);
}
NES_CNROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
auto power() -> void {
}
auto reset() -> void {
chr_bank = 0;
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
s.integer(chr_bank);
}
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint2 chr_bank;
};

View File

@ -1,53 +1,51 @@
struct NES_ExROM : Board {
NES_ExROM(Markup::Node& document) : Board(document), mmc5(*this) {
revision = Revision::ELROM;
}
enum class Revision : unsigned {
EKROM,
ELROM,
ETROM,
EWROM,
} revision;
auto main() -> void {
mmc5.main();
}
MMC5 mmc5;
auto prg_read(uint addr) -> uint8 {
return mmc5.prg_read(addr);
}
void main() {
mmc5.main();
}
auto prg_write(uint addr, uint8 data) -> void {
mmc5.prg_write(addr, data);
}
uint8 prg_read(unsigned addr) {
return mmc5.prg_read(addr);
}
auto chr_read(uint addr) -> uint8 {
return mmc5.chr_read(addr);
}
void prg_write(unsigned addr, uint8 data) {
mmc5.prg_write(addr, data);
}
auto chr_write(uint addr, uint8 data) -> void {
mmc5.chr_write(addr, data);
}
uint8 chr_read(unsigned addr) {
return mmc5.chr_read(addr);
}
auto scanline(uint y) -> void {
mmc5.scanline(y);
}
void chr_write(unsigned addr, uint8 data) {
mmc5.chr_write(addr, data);
}
auto power() -> void {
mmc5.power();
}
void scanline(unsigned y) {
mmc5.scanline(y);
}
auto reset() -> void {
mmc5.reset();
}
void power() {
mmc5.power();
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
mmc5.serialize(s);
}
void reset() {
mmc5.reset();
}
void serialize(serializer& s) {
Board::serialize(s);
mmc5.serialize(s);
}
NES_ExROM(Markup::Node& document) : Board(document), mmc5(*this) {
revision = Revision::ELROM;
}
enum class Revision : uint {
EKROM,
ELROM,
ETROM,
EWROM,
} revision;
MMC5 mmc5;
};

View File

@ -1,91 +1,89 @@
//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;
NES_FxROM(Markup::Node& document) : Board(document) {
revision = Revision::FKROM;
}
}
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
auto prg_read(uint addr) -> uint8 {
if(addr < 0x6000) return cpu.mdr();
if(addr < 0x8000) return prgram.read(addr);
uint bank = addr < 0xc000 ? prg_bank : (uint4)0x0f;
return prgrom.read((bank * 0x4000) | (addr & 0x3fff));
}
}
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));
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr < 0x6000) return;
if(addr < 0x8000) return prgram.write(addr, data);
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);
}
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;
}
}
void power() {
}
auto ciram_addr(uint addr) const -> uint {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
}
}
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;
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
bool region = addr & 0x1000;
uint 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 serialize(serializer& s) {
Board::serialize(s);
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
bool region = addr & 0x1000;
uint 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);
}
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);
}
auto power() -> void {
}
NES_FxROM(Markup::Node& document) : Board(document) {
revision = Revision::FKROM;
}
auto reset() -> void {
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;
}
auto serialize(serializer& s) -> void {
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);
}
enum Revision : uint {
FJROM,
FKROM,
} revision;
uint4 prg_bank;
uint5 chr_bank[2][2];
bool mirror;
bool latch[2];
};

View File

@ -2,60 +2,58 @@
//NES-MHROM
struct NES_GxROM : Board {
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint2 prg_bank;
uint2 chr_bank;
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if(addr & 0x8000) {
prg_bank = (data & 0x30) >> 4;
chr_bank = (data & 0x03) >> 0;
NES_GxROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
auto prg_read(uint addr) -> uint8 {
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
return cpu.mdr();
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
return Board::chr_read(addr);
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
auto prg_write(uint addr, uint8 data) -> void {
if(addr & 0x8000) {
prg_bank = (data & 0x30) >> 4;
chr_bank = (data & 0x03) >> 0;
}
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
Board::chr_write(addr, data);
}
void power() {
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
return Board::chr_read(addr);
}
void reset() {
prg_bank = 0;
chr_bank = 0;
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
Board::chr_write(addr, data);
}
void serialize(serializer& s) {
Board::serialize(s);
s.integer(prg_bank);
s.integer(chr_bank);
}
auto power() -> void {
}
NES_GxROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
auto reset() -> void {
prg_bank = 0;
chr_bank = 0;
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
s.integer(prg_bank);
s.integer(chr_bank);
}
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint2 prg_bank;
uint2 chr_bank;
};

View File

@ -1,48 +1,46 @@
struct NES_HKROM : Board {
NES_HKROM(Markup::Node& document) : Board(document), mmc6(*this) {
}
MMC6 mmc6;
auto main() -> void {
mmc6.main();
}
void main() {
mmc6.main();
}
auto prg_read(uint addr) -> uint8 {
if((addr & 0xf000) == 0x7000) return mmc6.ram_read(addr);
if(addr & 0x8000) return prgrom.read(mmc6.prg_addr(addr));
return cpu.mdr();
}
uint8 prg_read(unsigned addr) {
if((addr & 0xf000) == 0x7000) return mmc6.ram_read(addr);
if(addr & 0x8000) return prgrom.read(mmc6.prg_addr(addr));
return cpu.mdr();
}
auto prg_write(uint addr, uint8 data) -> void {
if((addr & 0xf000) == 0x7000) return mmc6.ram_write(addr, data);
if(addr & 0x8000) return mmc6.reg_write(addr, data);
}
void prg_write(unsigned addr, uint8 data) {
if((addr & 0xf000) == 0x7000) return mmc6.ram_write(addr, data);
if(addr & 0x8000) return mmc6.reg_write(addr, data);
}
auto chr_read(uint addr) -> uint8 {
mmc6.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_read(mmc6.ciram_addr(addr));
return Board::chr_read(mmc6.chr_addr(addr));
}
uint8 chr_read(unsigned addr) {
mmc6.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_read(mmc6.ciram_addr(addr));
return Board::chr_read(mmc6.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
mmc6.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_write(mmc6.ciram_addr(addr), data);
return Board::chr_write(mmc6.chr_addr(addr), data);
}
void chr_write(unsigned addr, uint8 data) {
mmc6.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_write(mmc6.ciram_addr(addr), data);
return Board::chr_write(mmc6.chr_addr(addr), data);
}
auto power() -> void {
mmc6.power();
}
void power() {
mmc6.power();
}
auto reset() -> void {
mmc6.reset();
}
void reset() {
mmc6.reset();
}
void serialize(serializer& s) {
Board::serialize(s);
mmc6.serialize(s);
}
NES_HKROM(Markup::Node& document) : Board(document), mmc6(*this) {
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
mmc6.serialize(s);
}
MMC6 mmc6;
};

View File

@ -2,42 +2,40 @@
//NES-NROM-256
struct NES_NROM : Board {
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) return prgrom.read(addr);
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
NES_NROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
if(chrram.size) return chrram.read(addr);
return chrrom.read(addr);
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
auto prg_read(uint addr) -> uint8 {
if(addr & 0x8000) return prgrom.read(addr);
return cpu.mdr();
}
if(chrram.size) return chrram.write(addr, data);
}
void serialize(serializer& s) {
Board::serialize(s);
}
auto prg_write(uint addr, uint8 data) -> void {
}
NES_NROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
}
if(chrram.size) return chrram.read(addr);
return chrrom.read(addr);
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
}
if(chrram.size) return chrram.write(addr, data);
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
}
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
};

View File

@ -1,97 +1,95 @@
//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;
NES_PxROM(Markup::Node& document) : Board(document) {
revision = Revision::PNROM;
}
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;
auto prg_read(uint addr) -> uint8 {
if(addr < 0x6000) return cpu.mdr();
if(addr < 0x8000) return prgram.read(addr);
uint 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));
}
}
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
auto prg_write(uint addr, uint8 data) -> void {
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;
}
}
}
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));
}
auto ciram_addr(uint addr) const -> uint {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
}
}
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);
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
bool region = addr & 0x1000;
uint 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 power() {
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
bool region = addr & 0x1000;
uint 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 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;
}
auto power() -> void {
}
void serialize(serializer& s) {
Board::serialize(s);
auto reset() -> void {
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;
}
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);
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
NES_PxROM(Markup::Node& document) : Board(document) {
revision = Revision::PNROM;
}
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);
}
enum Revision : uint {
PEEOROM,
PNROM,
} revision;
uint4 prg_bank;
uint5 chr_bank[2][2];
bool mirror;
bool latch[2];
};

View File

@ -1,101 +1,99 @@
struct NES_SxROM : Board {
enum class Revision : unsigned {
SAROM,
SBROM,
SCROM,
SC1ROM,
SEROM,
SFROM,
SGROM,
SHROM,
SH1ROM,
SIROM,
SJROM,
SKROM,
SLROM,
SL1ROM,
SL2ROM,
SL3ROM,
SLRROM,
SMROM,
SNROM,
SOROM,
SUROM,
SXROM,
} revision;
MMC1 mmc1;
void main() {
return mmc1.main();
}
unsigned ram_addr(unsigned addr) {
unsigned bank = 0;
if(revision == Revision::SOROM) bank = (mmc1.chr_bank[0] & 0x08) >> 3;
if(revision == Revision::SUROM) bank = (mmc1.chr_bank[0] & 0x0c) >> 2;
if(revision == Revision::SXROM) bank = (mmc1.chr_bank[0] & 0x0c) >> 2;
return (bank << 13) | (addr & 0x1fff);
}
uint8 prg_read(unsigned addr) {
if((addr & 0xe000) == 0x6000) {
if(revision == Revision::SNROM) {
if(mmc1.chr_bank[0] & 0x10) return cpu.mdr();
}
if(mmc1.ram_disable) return 0x00;
return prgram.read(ram_addr(addr));
NES_SxROM(Markup::Node& document) : Board(document), mmc1(*this) {
revision = Revision::SXROM;
}
if(addr & 0x8000) {
addr = mmc1.prg_addr(addr);
if(revision == Revision::SXROM) {
addr |= ((mmc1.chr_bank[0] & 0x10) >> 4) << 18;
}
return prgrom.read(addr);
auto main() -> void {
return mmc1.main();
}
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if((addr & 0xe000) == 0x6000) {
if(revision == Revision::SNROM) {
if(mmc1.chr_bank[0] & 0x10) return;
}
if(mmc1.ram_disable) return;
return prgram.write(ram_addr(addr), data);
auto ram_addr(uint addr) -> uint {
uint bank = 0;
if(revision == Revision::SOROM) bank = (mmc1.chr_bank[0] & 0x08) >> 3;
if(revision == Revision::SUROM) bank = (mmc1.chr_bank[0] & 0x0c) >> 2;
if(revision == Revision::SXROM) bank = (mmc1.chr_bank[0] & 0x0c) >> 2;
return (bank << 13) | (addr & 0x1fff);
}
if(addr & 0x8000) return mmc1.mmio_write(addr, data);
}
auto prg_read(uint addr) -> uint8 {
if((addr & 0xe000) == 0x6000) {
if(revision == Revision::SNROM) {
if(mmc1.chr_bank[0] & 0x10) return cpu.mdr();
}
if(mmc1.ram_disable) return 0x00;
return prgram.read(ram_addr(addr));
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read(mmc1.ciram_addr(addr));
return Board::chr_read(mmc1.chr_addr(addr));
}
if(addr & 0x8000) {
addr = mmc1.prg_addr(addr);
if(revision == Revision::SXROM) {
addr |= ((mmc1.chr_bank[0] & 0x10) >> 4) << 18;
}
return prgrom.read(addr);
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) return ppu.ciram_write(mmc1.ciram_addr(addr), data);
return Board::chr_write(mmc1.chr_addr(addr), data);
}
return cpu.mdr();
}
void power() {
mmc1.power();
}
auto prg_write(uint addr, uint8 data) -> void {
if((addr & 0xe000) == 0x6000) {
if(revision == Revision::SNROM) {
if(mmc1.chr_bank[0] & 0x10) return;
}
if(mmc1.ram_disable) return;
return prgram.write(ram_addr(addr), data);
}
void reset() {
mmc1.reset();
}
if(addr & 0x8000) return mmc1.mmio_write(addr, data);
}
void serialize(serializer& s) {
Board::serialize(s);
mmc1.serialize(s);
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(mmc1.ciram_addr(addr));
return Board::chr_read(mmc1.chr_addr(addr));
}
NES_SxROM(Markup::Node& document) : Board(document), mmc1(*this) {
revision = Revision::SXROM;
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(mmc1.ciram_addr(addr), data);
return Board::chr_write(mmc1.chr_addr(addr), data);
}
auto power() -> void {
mmc1.power();
}
auto reset() -> void {
mmc1.reset();
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
mmc1.serialize(s);
}
enum class Revision : uint {
SAROM,
SBROM,
SCROM,
SC1ROM,
SEROM,
SFROM,
SGROM,
SHROM,
SH1ROM,
SIROM,
SJROM,
SKROM,
SLROM,
SL1ROM,
SL2ROM,
SL3ROM,
SLRROM,
SMROM,
SNROM,
SOROM,
SUROM,
SXROM,
} revision;
MMC1 mmc1;
};

View File

@ -1,67 +1,65 @@
struct NES_TxROM : Board {
NES_TxROM(Markup::Node& document) : Board(document), mmc3(*this) {
revision = Revision::TLROM;
}
enum class Revision : unsigned {
TBROM,
TEROM,
TFROM,
TGROM,
TKROM,
TKSROM,
TLROM,
TL1ROM,
TL2ROM,
TLSROM,
TNROM,
TQROM,
TR1ROM,
TSROM,
TVROM,
} revision;
auto main() -> void {
mmc3.main();
}
MMC3 mmc3;
auto prg_read(uint addr) -> uint8 {
if((addr & 0xe000) == 0x6000) return mmc3.ram_read(addr);
if(addr & 0x8000) return prgrom.read(mmc3.prg_addr(addr));
return cpu.mdr();
}
void main() {
mmc3.main();
}
auto prg_write(uint addr, uint8 data) -> void {
if((addr & 0xe000) == 0x6000) return mmc3.ram_write(addr, data);
if(addr & 0x8000) return mmc3.reg_write(addr, data);
}
uint8 prg_read(unsigned addr) {
if((addr & 0xe000) == 0x6000) return mmc3.ram_read(addr);
if(addr & 0x8000) return prgrom.read(mmc3.prg_addr(addr));
return cpu.mdr();
}
auto chr_read(uint addr) -> uint8 {
mmc3.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_read(mmc3.ciram_addr(addr));
return Board::chr_read(mmc3.chr_addr(addr));
}
void prg_write(unsigned addr, uint8 data) {
if((addr & 0xe000) == 0x6000) return mmc3.ram_write(addr, data);
if(addr & 0x8000) return mmc3.reg_write(addr, data);
}
auto chr_write(uint addr, uint8 data) -> void {
mmc3.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_write(mmc3.ciram_addr(addr), data);
return Board::chr_write(mmc3.chr_addr(addr), data);
}
uint8 chr_read(unsigned addr) {
mmc3.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_read(mmc3.ciram_addr(addr));
return Board::chr_read(mmc3.chr_addr(addr));
}
auto power() -> void {
mmc3.power();
}
void chr_write(unsigned addr, uint8 data) {
mmc3.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_write(mmc3.ciram_addr(addr), data);
return Board::chr_write(mmc3.chr_addr(addr), data);
}
auto reset() -> void {
mmc3.reset();
}
void power() {
mmc3.power();
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
mmc3.serialize(s);
}
void reset() {
mmc3.reset();
}
void serialize(serializer& s) {
Board::serialize(s);
mmc3.serialize(s);
}
NES_TxROM(Markup::Node& document) : Board(document), mmc3(*this) {
revision = Revision::TLROM;
}
enum class Revision : uint {
TBROM,
TEROM,
TFROM,
TGROM,
TKROM,
TKSROM,
TLROM,
TL1ROM,
TL2ROM,
TLSROM,
TNROM,
TQROM,
TR1ROM,
TSROM,
TVROM,
} revision;
MMC3 mmc3;
};

View File

@ -2,54 +2,52 @@
//NES-UOROM
struct NES_UxROM : Board {
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint4 prg_bank;
uint8 prg_read(unsigned addr) {
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();
}
void prg_write(unsigned addr, uint8 data) {
if(addr & 0x8000) prg_bank = data & 0x0f;
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr);
NES_UxROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
return Board::chr_read(addr);
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr, data);
auto prg_read(uint addr) -> uint8 {
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();
}
return Board::chr_write(addr, data);
}
void power() {
}
auto prg_write(uint addr, uint8 data) -> void {
if(addr & 0x8000) prg_bank = data & 0x0f;
}
void reset() {
prg_bank = 0;
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr);
}
return Board::chr_read(addr);
}
void serialize(serializer& s) {
Board::serialize(s);
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr, data);
}
return Board::chr_write(addr, data);
}
s.integer(prg_bank);
}
auto power() -> void {
}
NES_UxROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
}
auto reset() -> void {
prg_bank = 0;
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
s.integer(prg_bank);
}
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint4 prg_bank;
};

View File

@ -1,226 +1,224 @@
//SUNSOFT-5B
struct Sunsoft5B : Board {
uint4 mmu_port;
uint4 apu_port;
uint8 prg_bank[4];
uint8 chr_bank[8];
uint2 mirror;
bool irq_enable;
bool irq_counter_enable;
uint16 irq_counter;
int16 dac[16];
struct Pulse {
bool disable;
uint12 frequency;
uint4 volume;
uint16 counter; //12-bit countdown + 4-bit phase
uint1 duty;
uint4 output;
void clock() {
if(--counter == 0) {
counter = frequency << 4;
duty ^= 1;
}
output = duty ? volume : (uint4)0;
if(disable) output = 0;
Sunsoft5B(Markup::Node& document) : Board(document) {
}
void reset() {
disable = 1;
frequency = 1;
volume = 0;
counter = 0;
duty = 0;
output = 0;
}
void serialize(serializer& s) {
s.integer(disable);
s.integer(frequency);
s.integer(volume);
s.integer(counter);
s.integer(duty);
s.integer(output);
}
} pulse[3];
void main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
struct Pulse {
auto clock() -> void {
if(--counter == 0) {
counter = frequency << 4;
duty ^= 1;
}
output = duty ? volume : (uint4)0;
if(disable) output = 0;
}
if(irq_counter_enable) {
if(--irq_counter == 0xffff) {
cpu.set_irq_line(irq_enable);
auto reset() -> void {
disable = 1;
frequency = 1;
volume = 0;
counter = 0;
duty = 0;
output = 0;
}
auto serialize(serializer& s) -> void {
s.integer(disable);
s.integer(frequency);
s.integer(volume);
s.integer(counter);
s.integer(duty);
s.integer(output);
}
bool disable;
uint12 frequency;
uint4 volume;
uint16 counter; //12-bit countdown + 4-bit phase
uint1 duty;
uint4 output;
} pulse[3];
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_counter_enable) {
if(--irq_counter == 0xffff) {
cpu.set_irq_line(irq_enable);
}
}
pulse[0].clock();
pulse[1].clock();
pulse[2].clock();
int16 output = dac[pulse[0].output] + dac[pulse[1].output] + dac[pulse[2].output];
apu.set_sample(-output);
tick();
}
}
auto prg_read(uint addr) -> uint8 {
if(addr < 0x6000) return cpu.mdr();
uint8 bank = 0x3f; //((addr & 0xe000) == 0xe000
if((addr & 0xe000) == 0x6000) bank = prg_bank[0];
if((addr & 0xe000) == 0x8000) bank = prg_bank[1];
if((addr & 0xe000) == 0xa000) bank = prg_bank[2];
if((addr & 0xe000) == 0xc000) bank = prg_bank[3];
bool ram_enable = bank & 0x80;
bool ram_select = bank & 0x40;
bank &= 0x3f;
if(ram_select) {
if(ram_enable == false) return cpu.mdr();
return prgram.data[addr & 0x1fff];
}
addr = (bank << 13) | (addr & 0x1fff);
return prgrom.read(addr);
}
auto prg_write(uint addr, uint8 data) -> void {
if((addr & 0xe000) == 0x6000) {
prgram.data[addr & 0x1fff] = data;
}
if(addr == 0x8000) {
mmu_port = data & 0x0f;
}
if(addr == 0xa000) {
switch(mmu_port) {
case 0: chr_bank[0] = data; break;
case 1: chr_bank[1] = data; break;
case 2: chr_bank[2] = data; break;
case 3: chr_bank[3] = data; break;
case 4: chr_bank[4] = data; break;
case 5: chr_bank[5] = data; break;
case 6: chr_bank[6] = data; break;
case 7: chr_bank[7] = data; break;
case 8: prg_bank[0] = data; break;
case 9: prg_bank[1] = data; break;
case 10: prg_bank[2] = data; break;
case 11: prg_bank[3] = data; break;
case 12: mirror = data & 3; break;
case 13:
irq_enable = data & 0x80;
irq_counter_enable = data & 0x01;
if(irq_enable == 0) cpu.set_irq_line(0);
break;
case 14: irq_counter = (irq_counter & 0xff00) | (data << 0); break;
case 15: irq_counter = (irq_counter & 0x00ff) | (data << 8); break;
}
}
pulse[0].clock();
pulse[1].clock();
pulse[2].clock();
int16 output = dac[pulse[0].output] + dac[pulse[1].output] + dac[pulse[2].output];
apu.set_sample(-output);
if(addr == 0xc000) {
apu_port = data & 0x0f;
}
tick();
}
}
uint8 prg_read(unsigned addr) {
if(addr < 0x6000) return cpu.mdr();
uint8 bank = 0x3f; //((addr & 0xe000) == 0xe000
if((addr & 0xe000) == 0x6000) bank = prg_bank[0];
if((addr & 0xe000) == 0x8000) bank = prg_bank[1];
if((addr & 0xe000) == 0xa000) bank = prg_bank[2];
if((addr & 0xe000) == 0xc000) bank = prg_bank[3];
bool ram_enable = bank & 0x80;
bool ram_select = bank & 0x40;
bank &= 0x3f;
if(ram_select) {
if(ram_enable == false) return cpu.mdr();
return prgram.data[addr & 0x1fff];
}
addr = (bank << 13) | (addr & 0x1fff);
return prgrom.read(addr);
}
void prg_write(unsigned addr, uint8 data) {
if((addr & 0xe000) == 0x6000) {
prgram.data[addr & 0x1fff] = data;
}
if(addr == 0x8000) {
mmu_port = data & 0x0f;
}
if(addr == 0xa000) {
switch(mmu_port) {
case 0: chr_bank[0] = data; break;
case 1: chr_bank[1] = data; break;
case 2: chr_bank[2] = data; break;
case 3: chr_bank[3] = data; break;
case 4: chr_bank[4] = data; break;
case 5: chr_bank[5] = data; break;
case 6: chr_bank[6] = data; break;
case 7: chr_bank[7] = data; break;
case 8: prg_bank[0] = data; break;
case 9: prg_bank[1] = data; break;
case 10: prg_bank[2] = data; break;
case 11: prg_bank[3] = data; break;
case 12: mirror = data & 3; break;
case 13:
irq_enable = data & 0x80;
irq_counter_enable = data & 0x01;
if(irq_enable == 0) cpu.set_irq_line(0);
break;
case 14: irq_counter = (irq_counter & 0xff00) | (data << 0); break;
case 15: irq_counter = (irq_counter & 0x00ff) | (data << 8); break;
if(addr == 0xe000) {
switch(apu_port) {
case 0: pulse[0].frequency = (pulse[0].frequency & 0xff00) | (data << 0); break;
case 1: pulse[0].frequency = (pulse[0].frequency & 0x00ff) | (data << 8); break;
case 2: pulse[1].frequency = (pulse[1].frequency & 0xff00) | (data << 0); break;
case 3: pulse[1].frequency = (pulse[1].frequency & 0x00ff) | (data << 8); break;
case 4: pulse[2].frequency = (pulse[2].frequency & 0xff00) | (data << 0); break;
case 5: pulse[2].frequency = (pulse[2].frequency & 0x00ff) | (data << 8); break;
case 7:
pulse[0].disable = data & 0x01;
pulse[1].disable = data & 0x02;
pulse[2].disable = data & 0x04;
break;
case 8: pulse[0].volume = data & 0x0f; break;
case 9: pulse[1].volume = data & 0x0f; break;
case 10: pulse[2].volume = data & 0x0f; break;
}
}
}
if(addr == 0xc000) {
apu_port = data & 0x0f;
auto chr_addr(uint addr) -> uint {
uint8 bank = (addr >> 10) & 7;
return (chr_bank[bank] << 10) | (addr & 0x03ff);
}
if(addr == 0xe000) {
switch(apu_port) {
case 0: pulse[0].frequency = (pulse[0].frequency & 0xff00) | (data << 0); break;
case 1: pulse[0].frequency = (pulse[0].frequency & 0x00ff) | (data << 8); break;
case 2: pulse[1].frequency = (pulse[1].frequency & 0xff00) | (data << 0); break;
case 3: pulse[1].frequency = (pulse[1].frequency & 0x00ff) | (data << 8); break;
case 4: pulse[2].frequency = (pulse[2].frequency & 0xff00) | (data << 0); break;
case 5: pulse[2].frequency = (pulse[2].frequency & 0x00ff) | (data << 8); break;
case 7:
pulse[0].disable = data & 0x01;
pulse[1].disable = data & 0x02;
pulse[2].disable = data & 0x04;
break;
case 8: pulse[0].volume = data & 0x0f; break;
case 9: pulse[1].volume = data & 0x0f; break;
case 10: pulse[2].volume = data & 0x0f; break;
auto ciram_addr(uint addr) -> uint {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal
case 2: return 0x0000 | (addr & 0x03ff); //first
case 3: return 0x0400 | (addr & 0x03ff); //second
}
}
}
unsigned chr_addr(unsigned addr) {
uint8 bank = (addr >> 10) & 7;
return (chr_bank[bank] << 10) | (addr & 0x03ff);
}
unsigned ciram_addr(unsigned addr) {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal
case 2: return 0x0000 | (addr & 0x03ff); //first
case 3: return 0x0400 | (addr & 0x03ff); //second
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
return Board::chr_read(chr_addr(addr));
}
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
return Board::chr_read(chr_addr(addr));
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
return Board::chr_write(chr_addr(addr), data);
}
void power() {
for(signed n = 0; n < 16; n++) {
double volume = 1.0 / pow(2, 1.0 / 2 * (15 - n));
dac[n] = volume * 8192.0;
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
return Board::chr_write(chr_addr(addr), data);
}
}
void reset() {
mmu_port = 0;
apu_port = 0;
auto power() -> void {
for(signed n : range(16)) {
double volume = 1.0 / pow(2, 1.0 / 2 * (15 - n));
dac[n] = volume * 8192.0;
}
}
for(auto& n : prg_bank) n = 0;
for(auto& n : chr_bank) n = 0;
mirror = 0;
irq_enable = 0;
irq_counter_enable = 0;
irq_counter = 0;
auto reset() -> void {
mmu_port = 0;
apu_port = 0;
pulse[0].reset();
pulse[1].reset();
pulse[2].reset();
}
for(auto& n : prg_bank) n = 0;
for(auto& n : chr_bank) n = 0;
mirror = 0;
irq_enable = 0;
irq_counter_enable = 0;
irq_counter = 0;
void serialize(serializer& s) {
Board::serialize(s);
pulse[0].reset();
pulse[1].reset();
pulse[2].reset();
}
s.integer(mmu_port);
s.integer(apu_port);
auto serialize(serializer& s) -> void {
Board::serialize(s);
s.array(prg_bank);
s.array(chr_bank);
s.integer(mirror);
s.integer(irq_enable);
s.integer(irq_counter_enable);
s.integer(irq_counter);
s.integer(mmu_port);
s.integer(apu_port);
pulse[0].serialize(s);
pulse[1].serialize(s);
pulse[2].serialize(s);
}
s.array(prg_bank);
s.array(chr_bank);
s.integer(mirror);
s.integer(irq_enable);
s.integer(irq_counter_enable);
s.integer(irq_counter);
Sunsoft5B(Markup::Node& document) : Board(document) {
}
pulse[0].serialize(s);
pulse[1].serialize(s);
pulse[2].serialize(s);
}
uint4 mmu_port;
uint4 apu_port;
uint8 prg_bank[4];
uint8 chr_bank[8];
uint2 mirror;
bool irq_enable;
bool irq_counter_enable;
uint16 irq_counter;
int16 dac[16];
};

View File

@ -6,73 +6,77 @@ namespace Famicom {
#include "board/board.cpp"
Cartridge cartridge;
string Cartridge::title() {
auto Cartridge::loaded() const -> bool {
return _loaded;
}
auto Cartridge::sha256() const -> string {
return _sha256;
}
auto Cartridge::title() const -> string {
return information.title;
}
void Cartridge::Main() {
auto Cartridge::Main() -> void {
cartridge.main();
}
void Cartridge::main() {
auto Cartridge::main() -> void {
board->main();
}
void Cartridge::load() {
auto Cartridge::load() -> void {
interface->loadRequest(ID::Manifest, "manifest.bml", true);
Board::load(information.markup); //this call will set Cartridge::board if successful
if(board == nullptr) return;
if(!board) return;
Hash::SHA256 sha;
sha.data(board->prgrom.data, board->prgrom.size);
sha.data(board->chrrom.data, board->chrrom.size);
sha256 = sha.digest();
_sha256 = sha.digest();
system.load();
loaded = true;
_loaded = true;
}
void Cartridge::unload() {
if(loaded == false) return;
loaded = false;
auto Cartridge::unload() -> void {
if(!loaded()) return;
_loaded = false;
memory.reset();
}
void Cartridge::power() {
auto Cartridge::power() -> void {
board->power();
}
void Cartridge::reset() {
auto Cartridge::reset() -> void {
create(Cartridge::Main, 21477272);
board->reset();
}
Cartridge::Cartridge() {
loaded = false;
}
uint8 Cartridge::prg_read(unsigned addr) {
auto Cartridge::prg_read(uint addr) -> uint8 {
return board->prg_read(addr);
}
void Cartridge::prg_write(unsigned addr, uint8 data) {
auto Cartridge::prg_write(uint addr, uint8 data) -> void {
return board->prg_write(addr, data);
}
uint8 Cartridge::chr_read(unsigned addr) {
auto Cartridge::chr_read(uint addr) -> uint8 {
return board->chr_read(addr);
}
void Cartridge::chr_write(unsigned addr, uint8 data) {
auto Cartridge::chr_write(uint addr, uint8 data) -> void {
return board->chr_write(addr, data);
}
void Cartridge::scanline(unsigned y) {
auto Cartridge::scanline(uint y) -> void {
return board->scanline(y);
}
void Cartridge::serialize(serializer& s) {
auto Cartridge::serialize(serializer& s) -> void {
Thread::serialize(s);
return board->serialize(s);
}

View File

@ -1,47 +1,47 @@
#include "chip/chip.hpp"
#include "board/board.hpp"
struct Cartridge : Thread, property<Cartridge> {
static void Main();
void main();
struct Cartridge : Thread {
static auto Main() -> void;
auto main() -> void;
void load();
void unload();
auto loaded() const -> bool;
auto sha256() const -> string;
auto title() const -> string;
void power();
void reset();
auto load() -> void;
auto unload() -> void;
readonly<bool> loaded;
readonly<string> sha256;
auto power() -> void;
auto reset() -> void;
auto serialize(serializer&) -> void;
struct Information {
string markup;
string title;
} information;
string title();
struct Memory {
unsigned id;
string name;
};
vector<Memory> memory;
void serialize(serializer&);
Cartridge();
//privileged:
Board *board;
Board* board = nullptr;
bool _loaded = false;
string _sha256;
uint8 prg_read(unsigned addr);
void prg_write(unsigned addr, uint8 data);
auto prg_read(uint addr) -> uint8;
auto prg_write(uint addr, uint8 data) -> void;
uint8 chr_read(unsigned addr);
void chr_write(unsigned addr, uint8 data);
auto chr_read(uint addr) -> uint8;
auto chr_write(uint addr, uint8 data) -> void;
//scanline() is for debugging purposes only:
//boards must detect scanline edges on their own
void scanline(unsigned y);
auto scanline(uint y) -> void;
};
extern Cartridge cartridge;

View File

@ -9,9 +9,9 @@
#include "vrc6.cpp"
#include "vrc7.cpp"
void Chip::tick() {
board.tick();
}
Chip::Chip(Board& board) : board(board) {
}
auto Chip::tick() -> void {
board.tick();
}

View File

@ -1,7 +1,8 @@
struct Board;
struct Chip {
Board& board;
void tick();
Chip(Board& board);
auto tick() -> void;
Board& board;
};

View File

@ -1,136 +1,134 @@
struct MMC1 : Chip {
MMC1(Board& board) : Chip(board) {
revision = Revision::MMC1B2;
}
enum class Revision : unsigned {
MMC1,
MMC1A,
MMC1B1,
MMC1B2,
MMC1B3,
MMC1C,
} revision;
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
unsigned writedelay;
unsigned shiftaddr;
unsigned shiftdata;
if(writedelay) writedelay--;
tick();
}
}
bool chr_mode;
bool prg_size; //0 = 32K, 1 = 16K
bool prg_mode;
uint2 mirror; //0 = first, 1 = second, 2 = vertical, 3 = horizontal
uint5 chr_bank[2];
bool ram_disable;
uint4 prg_bank;
auto prg_addr(uint addr) -> uint {
bool region = addr & 0x4000;
uint bank = (prg_bank & ~1) + region;
void main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
if(prg_size) {
bank = (region == 0 ? 0x0 : 0xf);
if(region != prg_mode) bank = prg_bank;
}
if(writedelay) writedelay--;
tick();
}
}
unsigned prg_addr(unsigned addr) {
bool region = addr & 0x4000;
unsigned bank = (prg_bank & ~1) + region;
if(prg_size) {
bank = (region == 0 ? 0x0 : 0xf);
if(region != prg_mode) bank = prg_bank;
return (bank << 14) | (addr & 0x3fff);
}
return (bank << 14) | (addr & 0x3fff);
}
unsigned chr_addr(unsigned addr) {
bool region = addr & 0x1000;
unsigned bank = chr_bank[region];
if(chr_mode == 0) bank = (chr_bank[0] & ~1) | region;
return (bank << 12) | (addr & 0x0fff);
}
unsigned ciram_addr(unsigned addr) {
switch(mirror) {
case 0: return 0x0000 | (addr & 0x03ff);
case 1: return 0x0400 | (addr & 0x03ff);
case 2: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
case 3: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
auto chr_addr(uint addr) -> uint {
bool region = addr & 0x1000;
uint bank = chr_bank[region];
if(chr_mode == 0) bank = (chr_bank[0] & ~1) | region;
return (bank << 12) | (addr & 0x0fff);
}
}
void mmio_write(unsigned addr, uint8 data) {
if(writedelay) return;
writedelay = 2;
auto ciram_addr(uint addr) -> uint {
switch(mirror) {
case 0: return 0x0000 | (addr & 0x03ff);
case 1: return 0x0400 | (addr & 0x03ff);
case 2: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
case 3: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
}
}
if(data & 0x80) {
shiftaddr = 0;
prg_size = 1;
prg_mode = 1;
} else {
shiftdata = ((data & 1) << 4) | (shiftdata >> 1);
if(++shiftaddr == 5) {
auto mmio_write(uint addr, uint8 data) -> void {
if(writedelay) return;
writedelay = 2;
if(data & 0x80) {
shiftaddr = 0;
switch((addr >> 13) & 3) {
case 0:
chr_mode = (shiftdata & 0x10);
prg_size = (shiftdata & 0x08);
prg_mode = (shiftdata & 0x04);
mirror = (shiftdata & 0x03);
break;
prg_size = 1;
prg_mode = 1;
} else {
shiftdata = ((data & 1) << 4) | (shiftdata >> 1);
if(++shiftaddr == 5) {
shiftaddr = 0;
switch((addr >> 13) & 3) {
case 0:
chr_mode = (shiftdata & 0x10);
prg_size = (shiftdata & 0x08);
prg_mode = (shiftdata & 0x04);
mirror = (shiftdata & 0x03);
break;
case 1:
chr_bank[0] = (shiftdata & 0x1f);
break;
case 1:
chr_bank[0] = (shiftdata & 0x1f);
break;
case 2:
chr_bank[1] = (shiftdata & 0x1f);
break;
case 2:
chr_bank[1] = (shiftdata & 0x1f);
break;
case 3:
ram_disable = (shiftdata & 0x10);
prg_bank = (shiftdata & 0x0f);
break;
case 3:
ram_disable = (shiftdata & 0x10);
prg_bank = (shiftdata & 0x0f);
break;
}
}
}
}
}
void power() {
}
auto power() -> void {
}
void reset() {
writedelay = 0;
shiftaddr = 0;
shiftdata = 0;
auto reset() -> void {
writedelay = 0;
shiftaddr = 0;
shiftdata = 0;
chr_mode = 0;
prg_size = 1;
prg_mode = 1;
mirror = 0;
chr_bank[0] = 0;
chr_bank[1] = 1;
ram_disable = 0;
prg_bank = 0;
}
chr_mode = 0;
prg_size = 1;
prg_mode = 1;
mirror = 0;
chr_bank[0] = 0;
chr_bank[1] = 1;
ram_disable = 0;
prg_bank = 0;
}
void serialize(serializer& s) {
s.integer(writedelay);
s.integer(shiftaddr);
s.integer(shiftdata);
auto serialize(serializer& s) -> void {
s.integer(writedelay);
s.integer(shiftaddr);
s.integer(shiftdata);
s.integer(chr_mode);
s.integer(prg_size);
s.integer(prg_mode);
s.integer(mirror);
s.array(chr_bank);
s.integer(ram_disable);
s.integer(prg_bank);
}
s.integer(chr_mode);
s.integer(prg_size);
s.integer(prg_mode);
s.integer(mirror);
s.array(chr_bank);
s.integer(ram_disable);
s.integer(prg_bank);
}
MMC1(Board& board) : Chip(board) {
revision = Revision::MMC1B2;
}
enum class Revision : uint {
MMC1,
MMC1A,
MMC1B1,
MMC1B2,
MMC1B3,
MMC1C,
} revision;
uint writedelay;
uint shiftaddr;
uint shiftdata;
bool chr_mode;
bool prg_size; //0 = 32K, 1 = 16K
bool prg_mode;
uint2 mirror; //0 = first, 1 = second, 2 = vertical, 3 = horizontal
uint5 chr_bank[2];
bool ram_disable;
uint4 prg_bank;
};

View File

@ -1,189 +1,187 @@
struct MMC3 : Chip {
bool chr_mode;
bool prg_mode;
uint3 bank_select;
uint8 prg_bank[2];
uint8 chr_bank[6];
bool mirror;
bool ram_enable;
bool ram_write_protect;
uint8 irq_latch;
uint8 irq_counter;
bool irq_enable;
unsigned irq_delay;
bool irq_line;
uint16 chr_abus;
void main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_delay) irq_delay--;
cpu.set_irq_line(irq_line);
tick();
MMC3(Board& board) : Chip(board) {
}
}
void irq_test(unsigned addr) {
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
if(irq_delay == 0) {
if(irq_counter == 0) {
irq_counter = irq_latch;
} else if(--irq_counter == 0) {
if(irq_enable) irq_line = 1;
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_delay) irq_delay--;
cpu.set_irq_line(irq_line);
tick();
}
irq_delay = 6;
}
chr_abus = addr;
}
unsigned prg_addr(unsigned addr) const {
switch((addr >> 13) & 3) {
case 0:
if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff);
return (prg_bank[0] << 13) | (addr & 0x1fff);
case 1:
return (prg_bank[1] << 13) | (addr & 0x1fff);
case 2:
if(prg_mode == 0) return (0x3e << 13) | (addr & 0x1fff);
return (prg_bank[0] << 13) | (addr & 0x1fff);
case 3:
return (0x3f << 13) | (addr & 0x1fff);
}
}
unsigned chr_addr(unsigned addr) const {
if(chr_mode == 0) {
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
if(addr <= 0x13ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
if(addr <= 0x17ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
if(addr <= 0x1bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
if(addr <= 0x1fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
} else {
if(addr <= 0x03ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
if(addr <= 0x07ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
if(addr <= 0x0bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
if(addr <= 0x0fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
}
}
unsigned ciram_addr(unsigned addr) const {
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
}
uint8 ram_read(unsigned addr) {
if(ram_enable) return board.prgram.data[addr & 0x1fff];
return 0x00;
}
void ram_write(unsigned addr, uint8 data) {
if(ram_enable && !ram_write_protect) board.prgram.data[addr & 0x1fff] = data;
}
void reg_write(unsigned addr, uint8 data) {
switch(addr & 0xe001) {
case 0x8000:
chr_mode = data & 0x80;
prg_mode = data & 0x40;
bank_select = data & 0x07;
break;
case 0x8001:
switch(bank_select) {
case 0: chr_bank[0] = data & ~1; break;
case 1: chr_bank[1] = data & ~1; break;
case 2: chr_bank[2] = data; break;
case 3: chr_bank[3] = data; break;
case 4: chr_bank[4] = data; break;
case 5: chr_bank[5] = data; break;
case 6: prg_bank[0] = data & 0x3f; break;
case 7: prg_bank[1] = data & 0x3f; break;
auto irq_test(uint addr) -> void {
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
if(irq_delay == 0) {
if(irq_counter == 0) {
irq_counter = irq_latch;
} else if(--irq_counter == 0) {
if(irq_enable) irq_line = 1;
}
}
irq_delay = 6;
}
break;
chr_abus = addr;
}
case 0xa000:
mirror = data & 0x01;
break;
auto prg_addr(uint addr) const -> uint {
switch((addr >> 13) & 3) {
case 0:
if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff);
return (prg_bank[0] << 13) | (addr & 0x1fff);
case 1:
return (prg_bank[1] << 13) | (addr & 0x1fff);
case 2:
if(prg_mode == 0) return (0x3e << 13) | (addr & 0x1fff);
return (prg_bank[0] << 13) | (addr & 0x1fff);
case 3:
return (0x3f << 13) | (addr & 0x1fff);
}
}
case 0xa001:
ram_enable = data & 0x80;
ram_write_protect = data & 0x40;
break;
auto chr_addr(uint addr) const -> uint {
if(chr_mode == 0) {
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
if(addr <= 0x13ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
if(addr <= 0x17ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
if(addr <= 0x1bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
if(addr <= 0x1fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
} else {
if(addr <= 0x03ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
if(addr <= 0x07ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
if(addr <= 0x0bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
if(addr <= 0x0fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
}
}
case 0xc000:
irq_latch = data;
break;
auto ciram_addr(uint addr) const -> uint {
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
}
case 0xc001:
auto ram_read(uint addr) -> uint8 {
if(ram_enable) return board.prgram.data[addr & 0x1fff];
return 0x00;
}
auto ram_write(uint addr, uint8 data) -> void {
if(ram_enable && !ram_write_protect) board.prgram.data[addr & 0x1fff] = data;
}
auto reg_write(uint addr, uint8 data) -> void {
switch(addr & 0xe001) {
case 0x8000:
chr_mode = data & 0x80;
prg_mode = data & 0x40;
bank_select = data & 0x07;
break;
case 0x8001:
switch(bank_select) {
case 0: chr_bank[0] = data & ~1; break;
case 1: chr_bank[1] = data & ~1; break;
case 2: chr_bank[2] = data; break;
case 3: chr_bank[3] = data; break;
case 4: chr_bank[4] = data; break;
case 5: chr_bank[5] = data; break;
case 6: prg_bank[0] = data & 0x3f; break;
case 7: prg_bank[1] = data & 0x3f; break;
}
break;
case 0xa000:
mirror = data & 0x01;
break;
case 0xa001:
ram_enable = data & 0x80;
ram_write_protect = data & 0x40;
break;
case 0xc000:
irq_latch = data;
break;
case 0xc001:
irq_counter = 0;
break;
case 0xe000:
irq_enable = false;
irq_line = 0;
break;
case 0xe001:
irq_enable = true;
break;
}
}
auto power() -> void {
}
auto reset() -> void {
chr_mode = 0;
prg_mode = 0;
bank_select = 0;
prg_bank[0] = 0;
prg_bank[1] = 0;
chr_bank[0] = 0;
chr_bank[1] = 0;
chr_bank[2] = 0;
chr_bank[3] = 0;
chr_bank[4] = 0;
chr_bank[5] = 0;
mirror = 0;
ram_enable = 1;
ram_write_protect = 0;
irq_latch = 0;
irq_counter = 0;
break;
case 0xe000:
irq_enable = false;
irq_delay = 0;
irq_line = 0;
break;
case 0xe001:
irq_enable = true;
break;
chr_abus = 0;
}
}
void power() {
}
auto serialize(serializer& s) -> void {
s.integer(chr_mode);
s.integer(prg_mode);
s.integer(bank_select);
s.array(prg_bank);
s.array(chr_bank);
s.integer(mirror);
s.integer(ram_enable);
s.integer(ram_write_protect);
s.integer(irq_latch);
s.integer(irq_counter);
s.integer(irq_enable);
s.integer(irq_delay);
s.integer(irq_line);
void reset() {
chr_mode = 0;
prg_mode = 0;
bank_select = 0;
prg_bank[0] = 0;
prg_bank[1] = 0;
chr_bank[0] = 0;
chr_bank[1] = 0;
chr_bank[2] = 0;
chr_bank[3] = 0;
chr_bank[4] = 0;
chr_bank[5] = 0;
mirror = 0;
ram_enable = 1;
ram_write_protect = 0;
irq_latch = 0;
irq_counter = 0;
irq_enable = false;
irq_delay = 0;
irq_line = 0;
s.integer(chr_abus);
}
chr_abus = 0;
}
void serialize(serializer& s) {
s.integer(chr_mode);
s.integer(prg_mode);
s.integer(bank_select);
s.array(prg_bank);
s.array(chr_bank);
s.integer(mirror);
s.integer(ram_enable);
s.integer(ram_write_protect);
s.integer(irq_latch);
s.integer(irq_counter);
s.integer(irq_enable);
s.integer(irq_delay);
s.integer(irq_line);
s.integer(chr_abus);
}
MMC3(Board& board) : Chip(board) {
}
bool chr_mode;
bool prg_mode;
uint3 bank_select;
uint8 prg_bank[2];
uint8 chr_bank[6];
bool mirror;
bool ram_enable;
bool ram_write_protect;
uint8 irq_latch;
uint8 irq_counter;
bool irq_enable;
uint irq_delay;
bool irq_line;
uint16 chr_abus;
};

View File

@ -1,497 +1,495 @@
struct MMC5 : Chip {
enum class Revision : unsigned {
MMC5,
MMC5B,
} revision;
uint8 exram[1024];
//programmable registers
uint2 prg_mode; //$5100
uint2 chr_mode; //$5101
uint2 prgram_write_protect[2]; //$5102,$5103
uint2 exram_mode; //$5104
uint2 nametable_mode[4]; //$5105
uint8 fillmode_tile; //$5106
uint8 fillmode_color; //$5107
bool ram_select; //$5113
uint2 ram_bank; //$5113
uint8 prg_bank[4]; //$5114-5117
uint10 chr_sprite_bank[8]; //$5120-5127
uint10 chr_bg_bank[4]; //$5128-512b
uint2 chr_bank_hi; //$5130
bool vs_enable; //$5200
bool vs_side; //$5200
uint5 vs_tile; //$5200
uint8 vs_scroll; //$5201
uint8 vs_bank; //$5202
uint8 irq_line; //$5203
bool irq_enable; //$5204
uint8 multiplicand; //$5205
uint8 multiplier; //$5206
//status registers
unsigned cpu_cycle_counter;
unsigned irq_counter;
bool irq_pending;
bool in_frame;
unsigned vcounter;
unsigned hcounter;
uint16 chr_access[4];
bool chr_active;
bool sprite_8x16;
uint8 exbank;
uint8 exattr;
bool vs_fetch;
uint8 vs_vpos;
uint8 vs_hpos;
void main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
//scanline() resets this; if no scanlines detected, enter video blanking period
if(++cpu_cycle_counter >= 200) blank(); //113-114 normal; ~2500 across Vblank period
cpu.set_irq_line(irq_enable && irq_pending);
tick();
}
}
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;
if((addr & 0xe000) == 0x6000) {
bank = (ram_select << 2) | ram_bank;
addr &= 0x1fff;
} else if(prg_mode == 0) {
bank = prg_bank[3] & ~3;
addr &= 0x7fff;
} else if(prg_mode == 1) {
if((addr & 0xc000) == 0x8000) bank = (prg_bank[1] & ~1);
if((addr & 0xe000) == 0xc000) bank = (prg_bank[3] & ~1);
addr &= 0x3fff;
} else if(prg_mode == 2) {
if((addr & 0xe000) == 0x8000) bank = (prg_bank[1] & ~1) | 0;
if((addr & 0xe000) == 0xa000) bank = (prg_bank[1] & ~1) | 1;
if((addr & 0xe000) == 0xc000) bank = (prg_bank[2]);
if((addr & 0xe000) == 0xe000) bank = (prg_bank[3]);
addr &= 0x1fff;
} else if(prg_mode == 3) {
if((addr & 0xe000) == 0x8000) bank = prg_bank[0];
if((addr & 0xe000) == 0xa000) bank = prg_bank[1];
if((addr & 0xe000) == 0xc000) bank = prg_bank[2];
if((addr & 0xe000) == 0xe000) bank = prg_bank[3];
addr &= 0x1fff;
MMC5(Board& board) : Chip(board) {
revision = Revision::MMC5;
}
bool rom = bank & 0x80;
bank &= 0x7f;
if(write == false) {
if(rom) {
return board.prgrom.read((bank << 13) | addr);
} else {
return board.prgram.read((bank << 13) | addr);
}
} else {
if(rom) {
board.prgrom.write((bank << 13) | addr, data);
} else {
if(prgram_write_protect[0] == 2 && prgram_write_protect[1] == 1) {
board.prgram.write((bank << 13) | addr, data);
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
//scanline() resets this; if no scanlines detected, enter video blanking period
if(++cpu_cycle_counter >= 200) blank(); //113-114 normal; ~2500 across Vblank period
cpu.set_irq_line(irq_enable && irq_pending);
tick();
}
return 0x00;
}
}
uint8 prg_read(unsigned addr) {
if((addr & 0xfc00) == 0x5c00) {
if(exram_mode >= 2) return exram[addr & 0x03ff];
return cpu.mdr();
}
if(addr >= 0x6000) {
return prg_access(0, addr);
auto scanline(uint y) -> void {
//used for testing only, to verify MMC5 scanline detection is accurate:
//if(y != vcounter && y <= 240) print(y, " vs ", vcounter, "\n");
}
switch(addr) {
case 0x5204: {
uint8 result = (irq_pending << 7) | (in_frame << 6);
irq_pending = false;
auto prg_access(bool write, uint addr, uint8 data = 0x00) -> uint8 {
uint bank;
if((addr & 0xe000) == 0x6000) {
bank = (ram_select << 2) | ram_bank;
addr &= 0x1fff;
} else if(prg_mode == 0) {
bank = prg_bank[3] & ~3;
addr &= 0x7fff;
} else if(prg_mode == 1) {
if((addr & 0xc000) == 0x8000) bank = (prg_bank[1] & ~1);
if((addr & 0xe000) == 0xc000) bank = (prg_bank[3] & ~1);
addr &= 0x3fff;
} else if(prg_mode == 2) {
if((addr & 0xe000) == 0x8000) bank = (prg_bank[1] & ~1) | 0;
if((addr & 0xe000) == 0xa000) bank = (prg_bank[1] & ~1) | 1;
if((addr & 0xe000) == 0xc000) bank = (prg_bank[2]);
if((addr & 0xe000) == 0xe000) bank = (prg_bank[3]);
addr &= 0x1fff;
} else if(prg_mode == 3) {
if((addr & 0xe000) == 0x8000) bank = prg_bank[0];
if((addr & 0xe000) == 0xa000) bank = prg_bank[1];
if((addr & 0xe000) == 0xc000) bank = prg_bank[2];
if((addr & 0xe000) == 0xe000) bank = prg_bank[3];
addr &= 0x1fff;
}
bool rom = bank & 0x80;
bank &= 0x7f;
if(write == false) {
if(rom) {
return board.prgrom.read((bank << 13) | addr);
} else {
return board.prgram.read((bank << 13) | addr);
}
} else {
if(rom) {
board.prgrom.write((bank << 13) | addr, data);
} else {
if(prgram_write_protect[0] == 2 && prgram_write_protect[1] == 1) {
board.prgram.write((bank << 13) | addr, data);
}
}
return 0x00;
}
}
auto prg_read(uint addr) -> uint8 {
if((addr & 0xfc00) == 0x5c00) {
if(exram_mode >= 2) return exram[addr & 0x03ff];
return cpu.mdr();
}
if(addr >= 0x6000) {
return prg_access(0, addr);
}
switch(addr) {
case 0x5204: {
uint8 result = (irq_pending << 7) | (in_frame << 6);
irq_pending = false;
return result;
}
case 0x5205: return (multiplier * multiplicand) >> 0;
case 0x5206: return (multiplier * multiplicand) >> 8;
}
}
auto prg_write(uint addr, uint8 data) -> void {
if((addr & 0xfc00) == 0x5c00) {
//writes 0x00 *during* Vblank (not during screen rendering ...)
if(exram_mode == 0 || exram_mode == 1) exram[addr & 0x03ff] = in_frame ? data : 0x00;
if(exram_mode == 2) exram[addr & 0x03ff] = data;
return;
}
if(addr >= 0x6000) {
prg_access(1, addr, data);
return;
}
switch(addr) {
case 0x2000:
sprite_8x16 = data & 0x20;
break;
case 0x2001:
//if BG+sprites are disabled; enter video blanking period
if((data & 0x18) == 0) blank();
break;
case 0x5100: prg_mode = data & 3; break;
case 0x5101: chr_mode = data & 3; break;
case 0x5102: prgram_write_protect[0] = data & 3; break;
case 0x5103: prgram_write_protect[1] = data & 3; break;
case 0x5104:
exram_mode = data & 3;
break;
case 0x5105:
nametable_mode[0] = (data & 0x03) >> 0;
nametable_mode[1] = (data & 0x0c) >> 2;
nametable_mode[2] = (data & 0x30) >> 4;
nametable_mode[3] = (data & 0xc0) >> 6;
break;
case 0x5106:
fillmode_tile = data;
break;
case 0x5107:
fillmode_color = data & 3;
fillmode_color |= fillmode_color << 2;
fillmode_color |= fillmode_color << 4;
break;
case 0x5113:
ram_select = data & 0x04;
ram_bank = data & 0x03;
break;
case 0x5114: prg_bank[0] = data; break;
case 0x5115: prg_bank[1] = data; break;
case 0x5116: prg_bank[2] = data; break;
case 0x5117: prg_bank[3] = data | 0x80; break;
case 0x5120: chr_sprite_bank[0] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5121: chr_sprite_bank[1] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5122: chr_sprite_bank[2] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5123: chr_sprite_bank[3] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5124: chr_sprite_bank[4] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5125: chr_sprite_bank[5] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5126: chr_sprite_bank[6] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5127: chr_sprite_bank[7] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5128: chr_bg_bank[0] = (chr_bank_hi << 8) | data; chr_active = 1; break;
case 0x5129: chr_bg_bank[1] = (chr_bank_hi << 8) | data; chr_active = 1; break;
case 0x512a: chr_bg_bank[2] = (chr_bank_hi << 8) | data; chr_active = 1; break;
case 0x512b: chr_bg_bank[3] = (chr_bank_hi << 8) | data; chr_active = 1; break;
case 0x5130:
chr_bank_hi = data & 3;
break;
case 0x5200:
vs_enable = data & 0x80;
vs_side = data & 0x40;
vs_tile = data & 0x1f;
break;
case 0x5201:
vs_scroll = data;
break;
case 0x5202:
vs_bank = data;
break;
case 0x5203:
irq_line = data;
break;
case 0x5204:
irq_enable = data & 0x80;
break;
case 0x5205:
multiplicand = data;
break;
case 0x5206:
multiplier = data;
break;
}
}
auto chr_sprite_addr(uint addr) -> uint {
if(chr_mode == 0) {
auto bank = chr_sprite_bank[7];
return (bank * 0x2000) + (addr & 0x1fff);
}
if(chr_mode == 1) {
auto bank = chr_sprite_bank[(addr / 0x1000) * 4 + 3];
return (bank * 0x1000) + (addr & 0x0fff);
}
if(chr_mode == 2) {
auto bank = chr_sprite_bank[(addr / 0x0800) * 2 + 1];
return (bank * 0x0800) + (addr & 0x07ff);
}
if(chr_mode == 3) {
auto bank = chr_sprite_bank[(addr / 0x0400)];
return (bank * 0x0400) + (addr & 0x03ff);
}
}
auto chr_bg_addr(uint addr) -> uint {
addr &= 0x0fff;
if(chr_mode == 0) {
auto bank = chr_bg_bank[3];
return (bank * 0x2000) + (addr & 0x0fff);
}
if(chr_mode == 1) {
auto bank = chr_bg_bank[3];
return (bank * 0x1000) + (addr & 0x0fff);
}
if(chr_mode == 2) {
auto bank = chr_bg_bank[(addr / 0x0800) * 2 + 1];
return (bank * 0x0800) + (addr & 0x07ff);
}
if(chr_mode == 3) {
auto bank = chr_bg_bank[(addr / 0x0400)];
return (bank * 0x0400) + (addr & 0x03ff);
}
}
auto chr_vs_addr(uint addr) -> uint {
return (vs_bank * 0x1000) + (addr & 0x0ff8) + (vs_vpos & 7);
}
auto blank() -> void {
in_frame = false;
}
auto scanline() -> void {
hcounter = 0;
if(in_frame == false) {
in_frame = true;
irq_pending = false;
vcounter = 0;
} else {
if(vcounter == irq_line) irq_pending = true;
vcounter++;
}
cpu_cycle_counter = 0;
}
auto ciram_read(uint addr) -> uint8 {
if(vs_fetch && (hcounter & 2) == 0) return exram[vs_vpos / 8 * 32 + vs_hpos / 8];
if(vs_fetch && (hcounter & 2) != 0) return exram[vs_vpos / 32 * 8 + vs_hpos / 32 + 0x03c0];
switch(nametable_mode[(addr >> 10) & 3]) {
case 0: return ppu.ciram_read(0x0000 | (addr & 0x03ff));
case 1: return ppu.ciram_read(0x0400 | (addr & 0x03ff));
case 2: return exram_mode < 2 ? exram[addr & 0x03ff] : 0x00;
case 3: return (hcounter & 2) == 0 ? fillmode_tile : fillmode_color;
}
}
auto chr_read(uint addr) -> uint8 {
chr_access[0] = chr_access[1];
chr_access[1] = chr_access[2];
chr_access[2] = chr_access[3];
chr_access[3] = addr;
//detect two unused nametable fetches at end of each scanline
if((chr_access[0] & 0x2000) == 0
&& (chr_access[1] & 0x2000)
&& (chr_access[2] & 0x2000)
&& (chr_access[3] & 0x2000)) scanline();
if(in_frame == false) {
vs_fetch = false;
if(addr & 0x2000) return ciram_read(addr);
return board.chrrom.read(chr_active ? chr_bg_addr(addr) : chr_sprite_addr(addr));
}
bool bg_fetch = (hcounter < 256 || hcounter >= 320);
uint8 result = 0x00;
if((hcounter & 7) == 0) {
vs_hpos = hcounter >= 320 ? hcounter - 320 : hcounter + 16;
vs_vpos = vcounter + vs_scroll;
vs_fetch = vs_enable && bg_fetch && exram_mode < 2
&& (vs_side ? vs_hpos / 8 >= vs_tile : vs_hpos / 8 < vs_tile);
if(vs_vpos >= 240) vs_vpos -= 240;
result = ciram_read(addr);
exbank = (chr_bank_hi << 6) | (exram[addr & 0x03ff] & 0x3f);
exattr = exram[addr & 0x03ff] >> 6;
exattr |= exattr << 2;
exattr |= exattr << 4;
} else if((hcounter & 7) == 2) {
result = ciram_read(addr);
if(bg_fetch && exram_mode == 1) result = exattr;
} else {
if(vs_fetch) result = board.chrrom.read(chr_vs_addr(addr));
else if(sprite_8x16 ? bg_fetch : chr_active) result = board.chrrom.read(chr_bg_addr(addr));
else result = board.chrrom.read(chr_sprite_addr(addr));
if(bg_fetch && exram_mode == 1) result = board.chrrom.read(exbank * 0x1000 + (addr & 0x0fff));
}
hcounter += 2;
return result;
}
case 0x5205: return (multiplier * multiplicand) >> 0;
case 0x5206: return (multiplier * multiplicand) >> 8;
}
}
void prg_write(unsigned addr, uint8 data) {
if((addr & 0xfc00) == 0x5c00) {
//writes 0x00 *during* Vblank (not during screen rendering ...)
if(exram_mode == 0 || exram_mode == 1) exram[addr & 0x03ff] = in_frame ? data : 0x00;
if(exram_mode == 2) exram[addr & 0x03ff] = data;
return;
}
if(addr >= 0x6000) {
prg_access(1, addr, data);
return;
}
switch(addr) {
case 0x2000:
sprite_8x16 = data & 0x20;
break;
case 0x2001:
//if BG+sprites are disabled; enter video blanking period
if((data & 0x18) == 0) blank();
break;
case 0x5100: prg_mode = data & 3; break;
case 0x5101: chr_mode = data & 3; break;
case 0x5102: prgram_write_protect[0] = data & 3; break;
case 0x5103: prgram_write_protect[1] = data & 3; break;
case 0x5104:
exram_mode = data & 3;
break;
case 0x5105:
nametable_mode[0] = (data & 0x03) >> 0;
nametable_mode[1] = (data & 0x0c) >> 2;
nametable_mode[2] = (data & 0x30) >> 4;
nametable_mode[3] = (data & 0xc0) >> 6;
break;
case 0x5106:
fillmode_tile = data;
break;
case 0x5107:
fillmode_color = data & 3;
fillmode_color |= fillmode_color << 2;
fillmode_color |= fillmode_color << 4;
break;
case 0x5113:
ram_select = data & 0x04;
ram_bank = data & 0x03;
break;
case 0x5114: prg_bank[0] = data; break;
case 0x5115: prg_bank[1] = data; break;
case 0x5116: prg_bank[2] = data; break;
case 0x5117: prg_bank[3] = data | 0x80; break;
case 0x5120: chr_sprite_bank[0] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5121: chr_sprite_bank[1] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5122: chr_sprite_bank[2] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5123: chr_sprite_bank[3] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5124: chr_sprite_bank[4] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5125: chr_sprite_bank[5] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5126: chr_sprite_bank[6] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5127: chr_sprite_bank[7] = (chr_bank_hi << 8) | data; chr_active = 0; break;
case 0x5128: chr_bg_bank[0] = (chr_bank_hi << 8) | data; chr_active = 1; break;
case 0x5129: chr_bg_bank[1] = (chr_bank_hi << 8) | data; chr_active = 1; break;
case 0x512a: chr_bg_bank[2] = (chr_bank_hi << 8) | data; chr_active = 1; break;
case 0x512b: chr_bg_bank[3] = (chr_bank_hi << 8) | data; chr_active = 1; break;
case 0x5130:
chr_bank_hi = data & 3;
break;
case 0x5200:
vs_enable = data & 0x80;
vs_side = data & 0x40;
vs_tile = data & 0x1f;
break;
case 0x5201:
vs_scroll = data;
break;
case 0x5202:
vs_bank = data;
break;
case 0x5203:
irq_line = data;
break;
case 0x5204:
irq_enable = data & 0x80;
break;
case 0x5205:
multiplicand = data;
break;
case 0x5206:
multiplier = data;
break;
}
}
unsigned chr_sprite_addr(unsigned addr) {
if(chr_mode == 0) {
auto bank = chr_sprite_bank[7];
return (bank * 0x2000) + (addr & 0x1fff);
}
if(chr_mode == 1) {
auto bank = chr_sprite_bank[(addr / 0x1000) * 4 + 3];
return (bank * 0x1000) + (addr & 0x0fff);
}
if(chr_mode == 2) {
auto bank = chr_sprite_bank[(addr / 0x0800) * 2 + 1];
return (bank * 0x0800) + (addr & 0x07ff);
}
if(chr_mode == 3) {
auto bank = chr_sprite_bank[(addr / 0x0400)];
return (bank * 0x0400) + (addr & 0x03ff);
}
}
unsigned chr_bg_addr(unsigned addr) {
addr &= 0x0fff;
if(chr_mode == 0) {
auto bank = chr_bg_bank[3];
return (bank * 0x2000) + (addr & 0x0fff);
}
if(chr_mode == 1) {
auto bank = chr_bg_bank[3];
return (bank * 0x1000) + (addr & 0x0fff);
}
if(chr_mode == 2) {
auto bank = chr_bg_bank[(addr / 0x0800) * 2 + 1];
return (bank * 0x0800) + (addr & 0x07ff);
}
if(chr_mode == 3) {
auto bank = chr_bg_bank[(addr / 0x0400)];
return (bank * 0x0400) + (addr & 0x03ff);
}
}
unsigned chr_vs_addr(unsigned addr) {
return (vs_bank * 0x1000) + (addr & 0x0ff8) + (vs_vpos & 7);
}
void blank() {
in_frame = false;
}
void scanline() {
hcounter = 0;
if(in_frame == false) {
in_frame = true;
irq_pending = false;
vcounter = 0;
} else {
if(vcounter == irq_line) irq_pending = true;
vcounter++;
}
cpu_cycle_counter = 0;
}
uint8 ciram_read(unsigned addr) {
if(vs_fetch && (hcounter & 2) == 0) return exram[vs_vpos / 8 * 32 + vs_hpos / 8];
if(vs_fetch && (hcounter & 2) != 0) return exram[vs_vpos / 32 * 8 + vs_hpos / 32 + 0x03c0];
switch(nametable_mode[(addr >> 10) & 3]) {
case 0: return ppu.ciram_read(0x0000 | (addr & 0x03ff));
case 1: return ppu.ciram_read(0x0400 | (addr & 0x03ff));
case 2: return exram_mode < 2 ? exram[addr & 0x03ff] : 0x00;
case 3: return (hcounter & 2) == 0 ? fillmode_tile : fillmode_color;
}
}
uint8 chr_read(unsigned addr) {
chr_access[0] = chr_access[1];
chr_access[1] = chr_access[2];
chr_access[2] = chr_access[3];
chr_access[3] = addr;
//detect two unused nametable fetches at end of each scanline
if((chr_access[0] & 0x2000) == 0
&& (chr_access[1] & 0x2000)
&& (chr_access[2] & 0x2000)
&& (chr_access[3] & 0x2000)) scanline();
if(in_frame == false) {
vs_fetch = false;
if(addr & 0x2000) return ciram_read(addr);
return board.chrrom.read(chr_active ? chr_bg_addr(addr) : chr_sprite_addr(addr));
}
bool bg_fetch = (hcounter < 256 || hcounter >= 320);
uint8 result = 0x00;
if((hcounter & 7) == 0) {
vs_hpos = hcounter >= 320 ? hcounter - 320 : hcounter + 16;
vs_vpos = vcounter + vs_scroll;
vs_fetch = vs_enable && bg_fetch && exram_mode < 2
&& (vs_side ? vs_hpos / 8 >= vs_tile : vs_hpos / 8 < vs_tile);
if(vs_vpos >= 240) vs_vpos -= 240;
result = ciram_read(addr);
exbank = (chr_bank_hi << 6) | (exram[addr & 0x03ff] & 0x3f);
exattr = exram[addr & 0x03ff] >> 6;
exattr |= exattr << 2;
exattr |= exattr << 4;
} else if((hcounter & 7) == 2) {
result = ciram_read(addr);
if(bg_fetch && exram_mode == 1) result = exattr;
} else {
if(vs_fetch) result = board.chrrom.read(chr_vs_addr(addr));
else if(sprite_8x16 ? bg_fetch : chr_active) result = board.chrrom.read(chr_bg_addr(addr));
else result = board.chrrom.read(chr_sprite_addr(addr));
if(bg_fetch && exram_mode == 1) result = board.chrrom.read(exbank * 0x1000 + (addr & 0x0fff));
}
hcounter += 2;
return result;
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) {
switch(nametable_mode[(addr >> 10) & 3]) {
case 0: return ppu.ciram_write(0x0000 | (addr & 0x03ff), data);
case 1: return ppu.ciram_write(0x0400 | (addr & 0x03ff), data);
case 2: exram[addr & 0x03ff] = data; break;
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
switch(nametable_mode[(addr >> 10) & 3]) {
case 0: return ppu.ciram_write(0x0000 | (addr & 0x03ff), data);
case 1: return ppu.ciram_write(0x0400 | (addr & 0x03ff), data);
case 2: exram[addr & 0x03ff] = data; break;
}
}
}
}
void power() {
}
auto power() -> void {
}
void reset() {
for(auto& n : exram) n = 0xff;
auto reset() -> void {
for(auto& n : exram) n = 0xff;
prg_mode = 3;
chr_mode = 0;
for(auto& n : prgram_write_protect) n = 0;
exram_mode = 0;
for(auto& n : nametable_mode) n = 0;
fillmode_tile = 0;
fillmode_color = 0;
ram_select = 0;
ram_bank = 0;
prg_bank[0] = 0x00;
prg_bank[1] = 0x00;
prg_bank[2] = 0x00;
prg_bank[3] = 0xff;
for(auto& n : chr_sprite_bank) n = 0;
for(auto& n : chr_bg_bank) n = 0;
chr_bank_hi = 0;
vs_enable = 0;
vs_side = 0;
vs_tile = 0;
vs_scroll = 0;
vs_bank = 0;
irq_line = 0;
irq_enable = 0;
multiplicand = 0;
multiplier = 0;
prg_mode = 3;
chr_mode = 0;
for(auto& n : prgram_write_protect) n = 0;
exram_mode = 0;
for(auto& n : nametable_mode) n = 0;
fillmode_tile = 0;
fillmode_color = 0;
ram_select = 0;
ram_bank = 0;
prg_bank[0] = 0x00;
prg_bank[1] = 0x00;
prg_bank[2] = 0x00;
prg_bank[3] = 0xff;
for(auto& n : chr_sprite_bank) n = 0;
for(auto& n : chr_bg_bank) n = 0;
chr_bank_hi = 0;
vs_enable = 0;
vs_side = 0;
vs_tile = 0;
vs_scroll = 0;
vs_bank = 0;
irq_line = 0;
irq_enable = 0;
multiplicand = 0;
multiplier = 0;
cpu_cycle_counter = 0;
irq_counter = 0;
irq_pending = 0;
in_frame = 0;
vcounter = 0;
hcounter = 0;
for(auto& n : chr_access) n = 0;
chr_active = 0;
sprite_8x16 = 0;
cpu_cycle_counter = 0;
irq_counter = 0;
irq_pending = 0;
in_frame = 0;
vcounter = 0;
hcounter = 0;
for(auto& n : chr_access) n = 0;
chr_active = 0;
sprite_8x16 = 0;
exbank = 0;
exattr = 0;
exbank = 0;
exattr = 0;
vs_fetch = 0;
vs_vpos = 0;
vs_hpos = 0;
}
vs_fetch = 0;
vs_vpos = 0;
vs_hpos = 0;
}
void serialize(serializer& s) {
s.array(exram);
auto serialize(serializer& s) -> void {
s.array(exram);
s.integer(prg_mode);
s.integer(chr_mode);
for(auto& n : prgram_write_protect) s.integer(n);
s.integer(exram_mode);
for(auto& n : nametable_mode) s.integer(n);
s.integer(fillmode_tile);
s.integer(fillmode_color);
s.integer(ram_select);
s.integer(ram_bank);
for(auto& n : prg_bank) s.integer(n);
for(auto& n : chr_sprite_bank) s.integer(n);
for(auto& n : chr_bg_bank) s.integer(n);
s.integer(chr_bank_hi);
s.integer(vs_enable);
s.integer(vs_side);
s.integer(vs_tile);
s.integer(vs_scroll);
s.integer(vs_bank);
s.integer(irq_line);
s.integer(irq_enable);
s.integer(multiplicand);
s.integer(multiplier);
s.integer(prg_mode);
s.integer(chr_mode);
for(auto& n : prgram_write_protect) s.integer(n);
s.integer(exram_mode);
for(auto& n : nametable_mode) s.integer(n);
s.integer(fillmode_tile);
s.integer(fillmode_color);
s.integer(ram_select);
s.integer(ram_bank);
for(auto& n : prg_bank) s.integer(n);
for(auto& n : chr_sprite_bank) s.integer(n);
for(auto& n : chr_bg_bank) s.integer(n);
s.integer(chr_bank_hi);
s.integer(vs_enable);
s.integer(vs_side);
s.integer(vs_tile);
s.integer(vs_scroll);
s.integer(vs_bank);
s.integer(irq_line);
s.integer(irq_enable);
s.integer(multiplicand);
s.integer(multiplier);
s.integer(cpu_cycle_counter);
s.integer(irq_counter);
s.integer(irq_pending);
s.integer(in_frame);
s.integer(cpu_cycle_counter);
s.integer(irq_counter);
s.integer(irq_pending);
s.integer(in_frame);
s.integer(vcounter);
s.integer(hcounter);
for(auto& n : chr_access) s.integer(n);
s.integer(chr_active);
s.integer(sprite_8x16);
s.integer(vcounter);
s.integer(hcounter);
for(auto& n : chr_access) s.integer(n);
s.integer(chr_active);
s.integer(sprite_8x16);
s.integer(exbank);
s.integer(exattr);
s.integer(exbank);
s.integer(exattr);
s.integer(vs_fetch);
s.integer(vs_vpos);
s.integer(vs_hpos);
}
s.integer(vs_fetch);
s.integer(vs_vpos);
s.integer(vs_hpos);
}
MMC5(Board& board) : Chip(board) {
revision = Revision::MMC5;
}
enum class Revision : uint {
MMC5,
MMC5B,
} revision;
uint8 exram[1024];
//programmable registers
uint2 prg_mode; //$5100
uint2 chr_mode; //$5101
uint2 prgram_write_protect[2]; //$5102,$5103
uint2 exram_mode; //$5104
uint2 nametable_mode[4]; //$5105
uint8 fillmode_tile; //$5106
uint8 fillmode_color; //$5107
bool ram_select; //$5113
uint2 ram_bank; //$5113
uint8 prg_bank[4]; //$5114-5117
uint10 chr_sprite_bank[8]; //$5120-5127
uint10 chr_bg_bank[4]; //$5128-512b
uint2 chr_bank_hi; //$5130
bool vs_enable; //$5200
bool vs_side; //$5200
uint5 vs_tile; //$5200
uint8 vs_scroll; //$5201
uint8 vs_bank; //$5202
uint8 irq_line; //$5203
bool irq_enable; //$5204
uint8 multiplicand; //$5205
uint8 multiplier; //$5206
//status registers
uint cpu_cycle_counter;
uint irq_counter;
bool irq_pending;
bool in_frame;
uint vcounter;
uint hcounter;
uint16 chr_access[4];
bool chr_active;
bool sprite_8x16;
uint8 exbank;
uint8 exattr;
bool vs_fetch;
uint8 vs_vpos;
uint8 vs_hpos;
};

View File

@ -1,200 +1,198 @@
struct MMC6 : Chip {
bool chr_mode;
bool prg_mode;
bool ram_enable;
uint3 bank_select;
uint8 prg_bank[2];
uint8 chr_bank[6];
bool mirror;
bool ram_readable[2];
bool ram_writable[2];
uint8 irq_latch;
uint8 irq_counter;
bool irq_enable;
unsigned irq_delay;
bool irq_line;
uint16 chr_abus;
void main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_delay) irq_delay--;
cpu.set_irq_line(irq_line);
tick();
MMC6(Board& board) : Chip(board) {
}
}
void irq_test(unsigned addr) {
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
if(irq_delay == 0) {
if(irq_counter == 0) {
irq_counter = irq_latch;
} else if(--irq_counter == 0) {
if(irq_enable) irq_line = 1;
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_delay) irq_delay--;
cpu.set_irq_line(irq_line);
tick();
}
irq_delay = 6;
}
chr_abus = addr;
}
unsigned prg_addr(unsigned addr) const {
switch((addr >> 13) & 3) {
case 0:
if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff);
return (prg_bank[0] << 13) | (addr & 0x1fff);
case 1:
return (prg_bank[1] << 13) | (addr & 0x1fff);
case 2:
if(prg_mode == 0) return (0x3e << 13) | (addr & 0x1fff);
return (prg_bank[0] << 13) | (addr & 0x1fff);
case 3:
return (0x3f << 13) | (addr & 0x1fff);
}
}
unsigned chr_addr(unsigned addr) const {
if(chr_mode == 0) {
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
if(addr <= 0x13ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
if(addr <= 0x17ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
if(addr <= 0x1bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
if(addr <= 0x1fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
} else {
if(addr <= 0x03ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
if(addr <= 0x07ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
if(addr <= 0x0bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
if(addr <= 0x0fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
}
}
unsigned ciram_addr(unsigned addr) const {
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
}
uint8 ram_read(unsigned addr) {
if(ram_enable == false) return cpu.mdr();
if(ram_readable[0] == false && ram_readable[1] == false) return cpu.mdr();
bool region = addr & 0x0200;
if(ram_readable[region] == false) return 0x00;
return board.prgram.read((region * 0x0200) + (addr & 0x01ff));
}
void ram_write(unsigned addr, uint8 data) {
if(ram_enable == false) return;
bool region = addr & 0x0200;
if(ram_writable[region] == false) return;
return board.prgram.write((region * 0x0200) + (addr & 0x01ff), data);
}
void reg_write(unsigned addr, uint8 data) {
switch(addr & 0xe001) {
case 0x8000:
chr_mode = data & 0x80;
prg_mode = data & 0x40;
ram_enable = data & 0x20;
bank_select = data & 0x07;
if(ram_enable == false) {
for(auto &n : ram_readable) n = false;
for(auto &n : ram_writable) n = false;
auto irq_test(uint addr) -> void {
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
if(irq_delay == 0) {
if(irq_counter == 0) {
irq_counter = irq_latch;
} else if(--irq_counter == 0) {
if(irq_enable) irq_line = 1;
}
}
irq_delay = 6;
}
break;
chr_abus = addr;
}
case 0x8001:
switch(bank_select) {
case 0: chr_bank[0] = data & ~1; break;
case 1: chr_bank[1] = data & ~1; break;
case 2: chr_bank[2] = data; break;
case 3: chr_bank[3] = data; break;
case 4: chr_bank[4] = data; break;
case 5: chr_bank[5] = data; break;
case 6: prg_bank[0] = data & 0x3f; break;
case 7: prg_bank[1] = data & 0x3f; break;
auto prg_addr(uint addr) const -> uint {
switch((addr >> 13) & 3) {
case 0:
if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff);
return (prg_bank[0] << 13) | (addr & 0x1fff);
case 1:
return (prg_bank[1] << 13) | (addr & 0x1fff);
case 2:
if(prg_mode == 0) return (0x3e << 13) | (addr & 0x1fff);
return (prg_bank[0] << 13) | (addr & 0x1fff);
case 3:
return (0x3f << 13) | (addr & 0x1fff);
}
break;
}
case 0xa000:
mirror = data & 0x01;
break;
auto chr_addr(uint addr) const -> uint {
if(chr_mode == 0) {
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
if(addr <= 0x13ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
if(addr <= 0x17ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
if(addr <= 0x1bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
if(addr <= 0x1fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
} else {
if(addr <= 0x03ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
if(addr <= 0x07ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
if(addr <= 0x0bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
if(addr <= 0x0fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
}
}
case 0xa001:
if(ram_enable == false) break;
ram_readable[1] = data & 0x80;
ram_writable[1] = data & 0x40;
ram_readable[0] = data & 0x20;
ram_writable[0] = data & 0x10;
break;
auto ciram_addr(uint addr) const -> uint {
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
}
case 0xc000:
irq_latch = data;
break;
auto ram_read(uint addr) -> uint8 {
if(ram_enable == false) return cpu.mdr();
if(ram_readable[0] == false && ram_readable[1] == false) return cpu.mdr();
bool region = addr & 0x0200;
if(ram_readable[region] == false) return 0x00;
return board.prgram.read((region * 0x0200) + (addr & 0x01ff));
}
case 0xc001:
auto ram_write(uint addr, uint8 data) -> void {
if(ram_enable == false) return;
bool region = addr & 0x0200;
if(ram_writable[region] == false) return;
return board.prgram.write((region * 0x0200) + (addr & 0x01ff), data);
}
auto reg_write(uint addr, uint8 data) -> void {
switch(addr & 0xe001) {
case 0x8000:
chr_mode = data & 0x80;
prg_mode = data & 0x40;
ram_enable = data & 0x20;
bank_select = data & 0x07;
if(ram_enable == false) {
for(auto &n : ram_readable) n = false;
for(auto &n : ram_writable) n = false;
}
break;
case 0x8001:
switch(bank_select) {
case 0: chr_bank[0] = data & ~1; break;
case 1: chr_bank[1] = data & ~1; break;
case 2: chr_bank[2] = data; break;
case 3: chr_bank[3] = data; break;
case 4: chr_bank[4] = data; break;
case 5: chr_bank[5] = data; break;
case 6: prg_bank[0] = data & 0x3f; break;
case 7: prg_bank[1] = data & 0x3f; break;
}
break;
case 0xa000:
mirror = data & 0x01;
break;
case 0xa001:
if(ram_enable == false) break;
ram_readable[1] = data & 0x80;
ram_writable[1] = data & 0x40;
ram_readable[0] = data & 0x20;
ram_writable[0] = data & 0x10;
break;
case 0xc000:
irq_latch = data;
break;
case 0xc001:
irq_counter = 0;
break;
case 0xe000:
irq_enable = false;
irq_line = 0;
break;
case 0xe001:
irq_enable = true;
break;
}
}
auto power() -> void {
}
auto reset() -> void {
chr_mode = 0;
prg_mode = 0;
ram_enable = 0;
bank_select = 0;
for(auto& n : prg_bank) n = 0;
for(auto& n : chr_bank) n = 0;
mirror = 0;
for(auto& n : ram_readable) n = 0;
for(auto& n : ram_writable) n = 0;
irq_latch = 0;
irq_counter = 0;
break;
case 0xe000:
irq_enable = false;
irq_enable = 0;
irq_delay = 0;
irq_line = 0;
break;
case 0xe001:
irq_enable = true;
break;
chr_abus = 0;
}
}
void power() {
}
auto serialize(serializer& s) -> void {
s.integer(chr_mode);
s.integer(prg_mode);
s.integer(ram_enable);
s.integer(bank_select);
for(auto& n : prg_bank) s.integer(n);
for(auto& n : chr_bank) s.integer(n);
s.integer(mirror);
for(auto& n : ram_readable) s.integer(n);
for(auto& n : ram_writable) s.integer(n);
s.integer(irq_latch);
s.integer(irq_counter);
s.integer(irq_enable);
s.integer(irq_delay);
s.integer(irq_line);
void reset() {
chr_mode = 0;
prg_mode = 0;
ram_enable = 0;
bank_select = 0;
for(auto& n : prg_bank) n = 0;
for(auto& n : chr_bank) n = 0;
mirror = 0;
for(auto& n : ram_readable) n = 0;
for(auto& n : ram_writable) n = 0;
irq_latch = 0;
irq_counter = 0;
irq_enable = 0;
irq_delay = 0;
irq_line = 0;
s.integer(chr_abus);
}
chr_abus = 0;
}
void serialize(serializer& s) {
s.integer(chr_mode);
s.integer(prg_mode);
s.integer(ram_enable);
s.integer(bank_select);
for(auto& n : prg_bank) s.integer(n);
for(auto& n : chr_bank) s.integer(n);
s.integer(mirror);
for(auto& n : ram_readable) s.integer(n);
for(auto& n : ram_writable) s.integer(n);
s.integer(irq_latch);
s.integer(irq_counter);
s.integer(irq_enable);
s.integer(irq_delay);
s.integer(irq_line);
s.integer(chr_abus);
}
MMC6(Board& board) : Chip(board) {
}
bool chr_mode;
bool prg_mode;
bool ram_enable;
uint3 bank_select;
uint8 prg_bank[2];
uint8 chr_bank[6];
bool mirror;
bool ram_readable[2];
bool ram_writable[2];
uint8 irq_latch;
uint8 irq_counter;
bool irq_enable;
uint irq_delay;
bool irq_line;
uint16 chr_abus;
};

View File

@ -1,80 +1,78 @@
struct VRC1 : Chip {
uint4 prg_bank[3];
uint4 chr_banklo[2];
bool chr_bankhi[2];
bool mirror;
unsigned prg_addr(unsigned addr) const {
unsigned bank = 0x0f;
if((addr & 0xe000) == 0x8000) bank = prg_bank[0];
if((addr & 0xe000) == 0xa000) bank = prg_bank[1];
if((addr & 0xe000) == 0xc000) bank = prg_bank[2];
return (bank * 0x2000) + (addr & 0x1fff);
}
unsigned chr_addr(unsigned addr) const {
unsigned bank = chr_banklo[(bool)(addr & 0x1000)];
bank |= chr_bankhi[(bool)(addr & 0x1000)] << 4;
return (bank * 0x1000) + (addr & 0x0fff);
}
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
VRC1(Board& board) : Chip(board) {
}
throw;
}
void reg_write(unsigned addr, uint8 data) {
switch(addr & 0xf000) {
case 0x8000:
prg_bank[0] = data & 0x0f;
break;
case 0x9000:
chr_bankhi[1] = data & 0x04;
chr_bankhi[0] = data & 0x02;
mirror = data & 0x01;
break;
case 0xa000:
prg_bank[1] = data & 0x0f;
break;
case 0xc000:
prg_bank[2] = data & 0x0f;
break;
case 0xe000:
chr_banklo[0] = data & 0x0f;
break;
case 0xf000:
chr_banklo[1] = data & 0x0f;
break;
auto prg_addr(uint addr) const -> uint {
uint bank = 0x0f;
if((addr & 0xe000) == 0x8000) bank = prg_bank[0];
if((addr & 0xe000) == 0xa000) bank = prg_bank[1];
if((addr & 0xe000) == 0xc000) bank = prg_bank[2];
return (bank * 0x2000) + (addr & 0x1fff);
}
}
void power() {
}
auto chr_addr(uint addr) const -> uint {
uint bank = chr_banklo[(bool)(addr & 0x1000)];
bank |= chr_bankhi[(bool)(addr & 0x1000)] << 4;
return (bank * 0x1000) + (addr & 0x0fff);
}
void reset() {
for(auto& n : prg_bank) n = 0;
for(auto& n : chr_banklo) n = 0;
for(auto& n : chr_bankhi) n = 0;
mirror = 0;
}
auto ciram_addr(uint addr) const -> uint {
switch(mirror) {
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
}
throw;
}
void serialize(serializer& s) {
for(auto& n : prg_bank) s.integer(n);
for(auto& n : chr_banklo) s.integer(n);
for(auto& n : chr_bankhi) s.integer(n);
s.integer(mirror);
}
auto reg_write(uint addr, uint8 data) -> void {
switch(addr & 0xf000) {
case 0x8000:
prg_bank[0] = data & 0x0f;
break;
VRC1(Board& board) : Chip(board) {
}
case 0x9000:
chr_bankhi[1] = data & 0x04;
chr_bankhi[0] = data & 0x02;
mirror = data & 0x01;
break;
case 0xa000:
prg_bank[1] = data & 0x0f;
break;
case 0xc000:
prg_bank[2] = data & 0x0f;
break;
case 0xe000:
chr_banklo[0] = data & 0x0f;
break;
case 0xf000:
chr_banklo[1] = data & 0x0f;
break;
}
}
auto power() -> void {
}
auto reset() -> void {
for(auto& n : prg_bank) n = 0;
for(auto& n : chr_banklo) n = 0;
for(auto& n : chr_bankhi) n = 0;
mirror = 0;
}
auto serialize(serializer& s) -> void {
for(auto& n : prg_bank) s.integer(n);
for(auto& n : chr_banklo) s.integer(n);
for(auto& n : chr_bankhi) s.integer(n);
s.integer(mirror);
}
uint4 prg_bank[3];
uint4 chr_banklo[2];
bool chr_bankhi[2];
bool mirror;
};

View File

@ -1,110 +1,108 @@
struct VRC2 : Chip {
uint5 prg_bank[2];
uint8 chr_bank[8];
uint2 mirror;
bool latch;
unsigned prg_addr(unsigned addr) const {
unsigned bank;
switch(addr & 0xe000) {
case 0x8000: bank = prg_bank[0]; break;
case 0xa000: bank = prg_bank[1]; break;
case 0xc000: bank = 0x1e; break;
case 0xe000: bank = 0x1f; break;
VRC2(Board& board) : Chip(board) {
}
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)
auto prg_addr(uint addr) const -> uint {
uint bank;
switch(addr & 0xe000) {
case 0x8000: bank = prg_bank[0]; break;
case 0xa000: bank = prg_bank[1]; break;
case 0xc000: bank = 0x1e; break;
case 0xe000: bank = 0x1f; break;
}
return (bank * 0x2000) + (addr & 0x1fff);
}
throw;
}
uint8 ram_read(unsigned addr) {
if(board.prgram.size == 0) {
if((addr & 0xf000) == 0x6000) return cpu.mdr() | latch;
return cpu.mdr();
auto chr_addr(uint addr) const -> uint {
uint bank = chr_bank[addr / 0x0400];
return (bank * 0x0400) + (addr & 0x03ff);
}
return board.prgram.read(addr & 0x1fff);
}
void ram_write(unsigned addr, uint8 data) {
if(board.prgram.size == 0) {
if((addr & 0xf000) == 0x6000) latch = data & 0x01;
return;
auto ciram_addr(uint addr) const -> uint {
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)
}
throw;
}
return board.prgram.write(addr & 0x1fff, data);
}
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: case 0x9002: case 0x9003:
mirror = data & 0x03;
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;
auto ram_read(uint addr) -> uint8 {
if(board.prgram.size == 0) {
if((addr & 0xf000) == 0x6000) return cpu.mdr() | latch;
return cpu.mdr();
}
return board.prgram.read(addr & 0x1fff);
}
}
void power() {
}
auto ram_write(uint addr, uint8 data) -> void {
if(board.prgram.size == 0) {
if((addr & 0xf000) == 0x6000) latch = data & 0x01;
return;
}
return board.prgram.write(addr & 0x1fff, data);
}
void reset() {
for(auto& n : prg_bank) n = 0;
for(auto& n : chr_bank) n = 0;
mirror = 0;
latch = 0;
}
auto reg_write(uint addr, uint8 data) -> void {
switch(addr) {
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
prg_bank[0] = data & 0x1f;
break;
void serialize(serializer& s) {
for(auto& n : prg_bank) s.integer(n);
for(auto& n : chr_bank) s.integer(n);
s.integer(mirror);
s.integer(latch);
}
case 0x9000: case 0x9001: case 0x9002: case 0x9003:
mirror = data & 0x03;
break;
VRC2(Board& board) : Chip(board) {
}
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;
}
}
auto power() -> void {
}
auto reset() -> void {
for(auto& n : prg_bank) n = 0;
for(auto& n : chr_bank) n = 0;
mirror = 0;
latch = 0;
}
auto serialize(serializer& s) -> void {
for(auto& n : prg_bank) s.integer(n);
for(auto& n : chr_bank) s.integer(n);
s.integer(mirror);
s.integer(latch);
}
uint5 prg_bank[2];
uint8 chr_bank[8];
uint2 mirror;
bool latch;
};

View File

@ -1,100 +1,98 @@
struct VRC3 : Chip {
uint4 prg_bank;
bool irq_mode;
bool irq_enable;
bool irq_acknowledge;
uint16 irq_latch;
struct {
union {
uint16 w;
struct { uint8 order_lsb2(l, h); };
};
} irq_counter;
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) { //16-bit
if(++irq_counter.w == 0) {
irq_line = 1;
irq_enable = irq_acknowledge;
irq_counter.w = irq_latch;
}
}
if(irq_mode == 1) { //8-bit
if(++irq_counter.l == 0) {
irq_line = 1;
irq_enable = irq_acknowledge;
irq_counter.l = irq_latch;
}
}
}
cpu.set_irq_line(irq_line);
tick();
VRC3(Board& board) : Chip(board) {
}
}
unsigned prg_addr(unsigned addr) const {
unsigned bank = (addr < 0xc000 ? (unsigned)prg_bank : 0x0f);
return (bank * 0x4000) + (addr & 0x3fff);
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
void reg_write(unsigned addr, uint8 data) {
switch(addr & 0xf000) {
case 0x8000: irq_latch = (irq_latch & 0xfff0) | ((data & 0x0f) << 0); break;
case 0x9000: irq_latch = (irq_latch & 0xff0f) | ((data & 0x0f) << 4); break;
case 0xa000: irq_latch = (irq_latch & 0xf0ff) | ((data & 0x0f) << 8); break;
case 0xb000: irq_latch = (irq_latch & 0x0fff) | ((data & 0x0f) << 12); break;
if(irq_enable) {
if(irq_mode == 0) { //16-bit
if(++irq_counter.w == 0) {
irq_line = 1;
irq_enable = irq_acknowledge;
irq_counter.w = irq_latch;
}
}
if(irq_mode == 1) { //8-bit
if(++irq_counter.l == 0) {
irq_line = 1;
irq_enable = irq_acknowledge;
irq_counter.l = irq_latch;
}
}
}
case 0xc000:
irq_mode = data & 0x04;
irq_enable = data & 0x02;
irq_acknowledge = data & 0x01;
if(irq_enable) irq_counter.w = irq_latch;
break;
cpu.set_irq_line(irq_line);
tick();
}
}
case 0xd000:
auto prg_addr(uint addr) const -> uint {
uint bank = (addr < 0xc000 ? (uint)prg_bank : 0x0f);
return (bank * 0x4000) + (addr & 0x3fff);
}
auto reg_write(uint addr, uint8 data) -> void {
switch(addr & 0xf000) {
case 0x8000: irq_latch = (irq_latch & 0xfff0) | ((data & 0x0f) << 0); break;
case 0x9000: irq_latch = (irq_latch & 0xff0f) | ((data & 0x0f) << 4); break;
case 0xa000: irq_latch = (irq_latch & 0xf0ff) | ((data & 0x0f) << 8); break;
case 0xb000: irq_latch = (irq_latch & 0x0fff) | ((data & 0x0f) << 12); break;
case 0xc000:
irq_mode = data & 0x04;
irq_enable = data & 0x02;
irq_acknowledge = data & 0x01;
if(irq_enable) irq_counter.w = irq_latch;
break;
case 0xd000:
irq_line = 0;
irq_enable = irq_acknowledge;
break;
case 0xf000:
prg_bank = data & 0x0f;
break;
}
}
auto power() -> void {
}
auto reset() -> void {
prg_bank = 0;
irq_mode = 0;
irq_enable = 0;
irq_acknowledge = 0;
irq_latch = 0;
irq_counter.w = 0;
irq_line = 0;
irq_enable = irq_acknowledge;
break;
case 0xf000:
prg_bank = data & 0x0f;
break;
}
}
void power() {
}
void reset() {
prg_bank = 0;
irq_mode = 0;
irq_enable = 0;
irq_acknowledge = 0;
irq_latch = 0;
irq_counter.w = 0;
irq_line = 0;
}
void serialize(serializer& s) {
s.integer(prg_bank);
s.integer(irq_mode);
s.integer(irq_enable);
s.integer(irq_acknowledge);
s.integer(irq_latch);
s.integer(irq_counter.w);
s.integer(irq_line);
}
VRC3(Board& board) : Chip(board) {
}
auto serialize(serializer& s) -> void {
s.integer(prg_bank);
s.integer(irq_mode);
s.integer(irq_enable);
s.integer(irq_acknowledge);
s.integer(irq_latch);
s.integer(irq_counter.w);
s.integer(irq_line);
}
uint4 prg_bank;
bool irq_mode;
bool irq_enable;
bool irq_acknowledge;
uint16 irq_latch;
struct {
union {
uint16 w;
struct { uint8 order_lsb2(l, h); };
};
} irq_counter;
bool irq_line;
};

View File

@ -1,30 +1,28 @@
struct VRC4 : Chip {
VRC4(Board& board) : Chip(board) {
}
bool prg_mode;
uint5 prg_bank[2];
uint2 mirror;
uint8 chr_bank[8];
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
uint8 irq_latch;
bool irq_mode;
bool irq_enable;
bool irq_acknowledge;
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++;
}
}
}
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_mode == 1) {
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
@ -34,151 +32,151 @@ void main() {
}
}
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();
}
}
auto prg_addr(uint addr) const -> uint {
uint bank = 0, banks = board.prgrom.size / 0x2000;
switch(addr & 0xe000) {
case 0x8000: bank = prg_mode == 0 ? (unsigned)prg_bank[0] : banks - 2; break;
case 0xa000: bank = prg_bank[1]; break;
case 0xc000: bank = prg_mode == 0 ? banks - 2 : (unsigned)prg_bank[0]; break;
case 0xe000: bank = banks - 1; break;
}
return (bank * 0x2000) + (addr & 0x1fff);
}
auto chr_addr(uint addr) const -> uint {
uint bank = chr_bank[addr / 0x0400];
return (bank * 0x0400) + (addr & 0x03ff);
}
auto ciram_addr(uint addr) const -> uint {
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)
}
throw;
}
auto reg_write(uint addr, uint8 data) -> void {
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;
}
cpu.set_irq_line(irq_line);
tick();
}
}
unsigned prg_addr(unsigned addr) const {
unsigned bank = 0, banks = board.prgrom.size / 0x2000;
switch(addr & 0xe000) {
case 0x8000: bank = prg_mode == 0 ? (unsigned)prg_bank[0] : banks - 2; break;
case 0xa000: bank = prg_bank[1]; break;
case 0xc000: bank = prg_mode == 0 ? banks - 2 : (unsigned)prg_bank[0]; break;
case 0xe000: bank = banks - 1; break;
auto power() -> void {
}
return (bank * 0x2000) + (addr & 0x1fff);
}
unsigned chr_addr(unsigned addr) const {
unsigned bank = chr_bank[addr / 0x0400];
return (bank * 0x0400) + (addr & 0x03ff);
}
auto reset() -> void {
prg_mode = 0;
for(auto& n : prg_bank) n = 0;
mirror = 0;
for(auto& n : chr_bank) n = 0;
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)
}
throw;
}
irq_latch = 0;
irq_mode = 0;
irq_enable = 0;
irq_acknowledge = 0;
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_counter = 0;
irq_scalar = 0;
irq_line = 0;
break;
case 0xf003:
irq_enable = irq_acknowledge;
irq_line = 0;
break;
}
}
void power() {
}
auto serialize(serializer& s) -> void {
s.integer(prg_mode);
for(auto& n : prg_bank) s.integer(n);
s.integer(mirror);
for(auto& n : chr_bank) s.integer(n);
void reset() {
prg_mode = 0;
for(auto& n : prg_bank) n = 0;
mirror = 0;
for(auto& n : chr_bank) n = 0;
s.integer(irq_latch);
s.integer(irq_mode);
s.integer(irq_enable);
s.integer(irq_acknowledge);
irq_latch = 0;
irq_mode = 0;
irq_enable = 0;
irq_acknowledge = 0;
s.integer(irq_counter);
s.integer(irq_scalar);
s.integer(irq_line);
}
irq_counter = 0;
irq_scalar = 0;
irq_line = 0;
}
bool prg_mode;
uint5 prg_bank[2];
uint2 mirror;
uint8 chr_bank[8];
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) {
}
uint8 irq_latch;
bool irq_mode;
bool irq_enable;
bool irq_acknowledge;
uint8 irq_counter;
int irq_scalar;
bool irq_line;
};

View File

@ -1,102 +1,102 @@
struct VRC6 : Chip {
VRC6(Board& board) : Chip(board) {
}
uint8 prg_bank[2];
uint8 chr_bank[8];
uint2 mirror;
uint8 irq_latch;
bool irq_mode;
bool irq_enable;
bool irq_acknowledge;
struct Pulse {
auto clock() -> void {
if(--divider == 0) {
divider = frequency + 1;
cycle++;
output = (mode == 1 || cycle > duty) ? volume : (uint4)0;
}
uint8 irq_counter;
signed irq_scalar;
bool irq_line;
struct Pulse {
bool mode;
uint3 duty;
uint4 volume;
bool enable;
uint12 frequency;
uint12 divider;
uint4 cycle;
uint4 output;
void clock() {
if(--divider == 0) {
divider = frequency + 1;
cycle++;
output = (mode == 1 || cycle > duty) ? volume : (uint4)0;
if(enable == false) output = 0;
}
if(enable == false) output = 0;
}
auto serialize(serializer& s) -> void {
s.integer(mode);
s.integer(duty);
s.integer(volume);
s.integer(enable);
s.integer(frequency);
void serialize(serializer& s) {
s.integer(mode);
s.integer(duty);
s.integer(volume);
s.integer(enable);
s.integer(frequency);
s.integer(divider);
s.integer(cycle);
s.integer(output);
}
s.integer(divider);
s.integer(cycle);
s.integer(output);
}
} pulse1, pulse2;
bool mode;
uint3 duty;
uint4 volume;
bool enable;
uint12 frequency;
struct Sawtooth {
uint6 rate;
bool enable;
uint12 frequency;
uint12 divider;
uint4 cycle;
uint4 output;
} pulse1, pulse2;
uint12 divider;
uint1 phase;
uint3 stage;
uint8 accumulator;
uint5 output;
void clock() {
if(--divider == 0) {
divider = frequency + 1;
if(++phase == 0) {
accumulator += rate;
if(++stage == 7) {
stage = 0;
accumulator = 0;
struct Sawtooth {
auto clock() -> void {
if(--divider == 0) {
divider = frequency + 1;
if(++phase == 0) {
accumulator += rate;
if(++stage == 7) {
stage = 0;
accumulator = 0;
}
}
}
output = accumulator >> 3;
if(enable == false) output = 0;
}
output = accumulator >> 3;
if(enable == false) output = 0;
}
auto serialize(serializer& s) -> void {
s.integer(rate);
s.integer(enable);
s.integer(frequency);
void serialize(serializer& s) {
s.integer(rate);
s.integer(enable);
s.integer(frequency);
s.integer(divider);
s.integer(phase);
s.integer(stage);
s.integer(accumulator);
s.integer(output);
}
} sawtooth;
void main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
s.integer(divider);
s.integer(phase);
s.integer(stage);
s.integer(accumulator);
s.integer(output);
}
if(irq_enable) {
if(irq_mode == 0) {
irq_scalar -= 3;
if(irq_scalar <= 0) {
irq_scalar += 341;
uint6 rate;
bool enable;
uint12 frequency;
uint12 divider;
uint1 phase;
uint3 stage;
uint8 accumulator;
uint5 output;
} sawtooth;
auto main() -> void {
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;
@ -105,217 +105,215 @@ void main() {
}
}
}
cpu.set_irq_line(irq_line);
if(irq_mode == 1) {
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
pulse1.clock();
pulse2.clock();
sawtooth.clock();
int output = (pulse1.output + pulse2.output + sawtooth.output) << 7;
apu.set_sample(-output);
tick();
}
}
auto prg_addr(uint addr) const -> uint {
if((addr & 0xc000) == 0x8000) return (prg_bank[0] << 14) | (addr & 0x3fff);
if((addr & 0xe000) == 0xc000) return (prg_bank[1] << 13) | (addr & 0x1fff);
if((addr & 0xe000) == 0xe000) return ( 0xff << 13) | (addr & 0x1fff);
}
auto chr_addr(uint addr) const -> uint {
uint bank = chr_bank[(addr >> 10) & 7];
return (bank << 10) | (addr & 0x03ff);
}
auto ciram_addr(uint addr) const -> uint {
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)
}
}
auto ram_read(uint addr) -> uint8 {
return board.prgram.data[addr & 0x1fff];
}
auto ram_write(uint addr, uint8 data) -> void {
board.prgram.data[addr & 0x1fff] = data;
}
auto reg_write(uint addr, uint8 data) -> void {
switch(addr) {
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
prg_bank[0] = data;
break;
case 0x9000:
pulse1.mode = data & 0x80;
pulse1.duty = (data & 0x70) >> 4;
pulse1.volume = data & 0x0f;
break;
case 0x9001:
pulse1.frequency = (pulse1.frequency & 0x0f00) | ((data & 0xff) << 0);
break;
case 0x9002:
pulse1.frequency = (pulse1.frequency & 0x00ff) | ((data & 0x0f) << 8);
pulse1.enable = data & 0x80;
break;
case 0xa000:
pulse2.mode = data & 0x80;
pulse2.duty = (data & 0x70) >> 4;
pulse2.volume = data & 0x0f;
break;
case 0xa001:
pulse2.frequency = (pulse2.frequency & 0x0f00) | ((data & 0xff) << 0);
break;
case 0xa002:
pulse2.frequency = (pulse2.frequency & 0x00ff) | ((data & 0x0f) << 8);
pulse2.enable = data & 0x80;
break;
case 0xb000:
sawtooth.rate = data & 0x3f;
break;
case 0xb001:
sawtooth.frequency = (sawtooth.frequency & 0x0f00) | ((data & 0xff) << 0);
break;
case 0xb002:
sawtooth.frequency = (sawtooth.frequency & 0x00ff) | ((data & 0x0f) << 8);
sawtooth.enable = data & 0x80;
break;
case 0xb003:
mirror = (data >> 2) & 3;
break;
case 0xc000: case 0xc001: case 0xc002: case 0xc003:
prg_bank[1] = data;
break;
case 0xd000: case 0xd001: case 0xd002: case 0xd003:
chr_bank[0 + (addr & 3)] = data;
break;
case 0xe000: case 0xe001: case 0xe002: case 0xe003:
chr_bank[4 + (addr & 3)] = data;
break;
case 0xf000:
irq_latch = data;
break;
case 0xf001:
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 0xf002:
irq_enable = irq_acknowledge;
irq_line = 0;
break;
}
cpu.set_irq_line(irq_line);
pulse1.clock();
pulse2.clock();
sawtooth.clock();
signed output = (pulse1.output + pulse2.output + sawtooth.output) << 7;
apu.set_sample(-output);
tick();
}
}
unsigned prg_addr(unsigned addr) const {
if((addr & 0xc000) == 0x8000) return (prg_bank[0] << 14) | (addr & 0x3fff);
if((addr & 0xe000) == 0xc000) return (prg_bank[1] << 13) | (addr & 0x1fff);
if((addr & 0xe000) == 0xe000) return ( 0xff << 13) | (addr & 0x1fff);
}
unsigned chr_addr(unsigned addr) const {
unsigned bank = chr_bank[(addr >> 10) & 7];
return (bank << 10) | (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)
auto power() -> void {
}
}
uint8 ram_read(unsigned addr) {
return board.prgram.data[addr & 0x1fff];
}
auto reset() -> void {
prg_bank[0] = 0;
prg_bank[1] = 0;
chr_bank[0] = 0;
chr_bank[1] = 0;
chr_bank[2] = 0;
chr_bank[3] = 0;
chr_bank[4] = 0;
chr_bank[5] = 0;
chr_bank[6] = 0;
chr_bank[7] = 0;
mirror = 0;
irq_latch = 0;
irq_mode = 0;
irq_enable = 0;
irq_acknowledge = 0;
void ram_write(unsigned addr, uint8 data) {
board.prgram.data[addr & 0x1fff] = data;
}
void reg_write(unsigned addr, uint8 data) {
switch(addr) {
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
prg_bank[0] = data;
break;
case 0x9000:
pulse1.mode = data & 0x80;
pulse1.duty = (data & 0x70) >> 4;
pulse1.volume = data & 0x0f;
break;
case 0x9001:
pulse1.frequency = (pulse1.frequency & 0x0f00) | ((data & 0xff) << 0);
break;
case 0x9002:
pulse1.frequency = (pulse1.frequency & 0x00ff) | ((data & 0x0f) << 8);
pulse1.enable = data & 0x80;
break;
case 0xa000:
pulse2.mode = data & 0x80;
pulse2.duty = (data & 0x70) >> 4;
pulse2.volume = data & 0x0f;
break;
case 0xa001:
pulse2.frequency = (pulse2.frequency & 0x0f00) | ((data & 0xff) << 0);
break;
case 0xa002:
pulse2.frequency = (pulse2.frequency & 0x00ff) | ((data & 0x0f) << 8);
pulse2.enable = data & 0x80;
break;
case 0xb000:
sawtooth.rate = data & 0x3f;
break;
case 0xb001:
sawtooth.frequency = (sawtooth.frequency & 0x0f00) | ((data & 0xff) << 0);
break;
case 0xb002:
sawtooth.frequency = (sawtooth.frequency & 0x00ff) | ((data & 0x0f) << 8);
sawtooth.enable = data & 0x80;
break;
case 0xb003:
mirror = (data >> 2) & 3;
break;
case 0xc000: case 0xc001: case 0xc002: case 0xc003:
prg_bank[1] = data;
break;
case 0xd000: case 0xd001: case 0xd002: case 0xd003:
chr_bank[0 + (addr & 3)] = data;
break;
case 0xe000: case 0xe001: case 0xe002: case 0xe003:
chr_bank[4 + (addr & 3)] = data;
break;
case 0xf000:
irq_latch = data;
break;
case 0xf001:
irq_mode = data & 0x04;
irq_enable = data & 0x02;
irq_acknowledge = data & 0x01;
if(irq_enable) {
irq_counter = irq_latch;
irq_scalar = 341;
}
irq_counter = 0;
irq_scalar = 0;
irq_line = 0;
break;
case 0xf002:
irq_enable = irq_acknowledge;
irq_line = 0;
break;
pulse1.mode = 0;
pulse1.duty = 0;
pulse1.volume = 0;
pulse1.enable = 0;
pulse1.frequency = 0;
pulse1.divider = 1;
pulse1.cycle = 0;
pulse1.output = 0;
pulse2.mode = 0;
pulse2.duty = 0;
pulse2.volume = 0;
pulse2.enable = 0;
pulse2.frequency = 0;
pulse2.divider = 1;
pulse2.cycle = 0;
pulse2.output = 0;
sawtooth.rate = 0;
sawtooth.enable = 0;
sawtooth.frequency = 0;
sawtooth.divider = 1;
sawtooth.phase = 0;
sawtooth.stage = 0;
sawtooth.accumulator = 0;
sawtooth.output = 0;
}
}
void power() {
}
auto serialize(serializer& s) -> void {
pulse1.serialize(s);
pulse2.serialize(s);
sawtooth.serialize(s);
void reset() {
prg_bank[0] = 0;
prg_bank[1] = 0;
chr_bank[0] = 0;
chr_bank[1] = 0;
chr_bank[2] = 0;
chr_bank[3] = 0;
chr_bank[4] = 0;
chr_bank[5] = 0;
chr_bank[6] = 0;
chr_bank[7] = 0;
mirror = 0;
irq_latch = 0;
irq_mode = 0;
irq_enable = 0;
irq_acknowledge = 0;
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);
irq_counter = 0;
irq_scalar = 0;
irq_line = 0;
s.integer(irq_counter);
s.integer(irq_scalar);
s.integer(irq_line);
}
pulse1.mode = 0;
pulse1.duty = 0;
pulse1.volume = 0;
pulse1.enable = 0;
pulse1.frequency = 0;
pulse1.divider = 1;
pulse1.cycle = 0;
pulse1.output = 0;
pulse2.mode = 0;
pulse2.duty = 0;
pulse2.volume = 0;
pulse2.enable = 0;
pulse2.frequency = 0;
pulse2.divider = 1;
pulse2.cycle = 0;
pulse2.output = 0;
sawtooth.rate = 0;
sawtooth.enable = 0;
sawtooth.frequency = 0;
sawtooth.divider = 1;
sawtooth.phase = 0;
sawtooth.stage = 0;
sawtooth.accumulator = 0;
sawtooth.output = 0;
}
void serialize(serializer& s) {
pulse1.serialize(s);
pulse2.serialize(s);
sawtooth.serialize(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);
}
VRC6(Board& board) : Chip(board) {
}
uint8 prg_bank[2];
uint8 chr_bank[8];
uint2 mirror;
uint8 irq_latch;
bool irq_mode;
bool irq_enable;
bool irq_acknowledge;
uint8 irq_counter;
int irq_scalar;
bool irq_line;
};

View File

@ -2,31 +2,30 @@
//Yamaha YM2413 OPLL audio - not emulated
struct VRC7 : Chip {
VRC7(Board& board) : Chip(board) {
}
uint8 prg_bank[3];
uint8 chr_bank[8];
uint2 mirror;
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
uint8 irq_latch;
bool irq_mode;
bool irq_enable;
bool irq_acknowledge;
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++;
}
}
}
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_mode == 1) {
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
@ -35,120 +34,119 @@ void main() {
}
}
}
cpu.set_irq_line(irq_line);
if(irq_mode == 1) {
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
tick();
}
}
auto reg_write(uint addr, uint8 data) -> void {
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;
}
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;
auto prg_addr(uint addr) const -> uint {
uint 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);
}
auto chr_addr(uint addr) const -> uint {
uint bank = chr_bank[addr / 0x0400];
return (bank * 0x0400) + (addr & 0x03ff);
}
auto ciram_addr(uint addr) const -> uint {
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)
}
}
auto power() -> void {
}
auto reset() -> void {
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;
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;
auto serialize(serializer& s) -> void {
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);
}
return (bank * 0x2000) + (addr & 0x1fff);
}
unsigned chr_addr(unsigned addr) const {
unsigned bank = chr_bank[addr / 0x0400];
return (bank * 0x0400) + (addr & 0x03ff);
}
uint8 prg_bank[3];
uint8 chr_bank[8];
uint2 mirror;
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) {
}
uint8 irq_latch;
bool irq_mode;
bool irq_enable;
bool irq_acknowledge;
uint8 irq_counter;
int irq_scalar;
bool irq_line;
};

View File

@ -4,19 +4,19 @@ namespace Famicom {
Cheat cheat;
void Cheat::reset() {
auto Cheat::reset() -> void {
codes.reset();
}
void Cheat::append(unsigned addr, unsigned data) {
auto Cheat::append(uint addr, uint data) -> void {
codes.append({addr, Unused, data});
}
void Cheat::append(unsigned addr, unsigned comp, unsigned data) {
auto Cheat::append(uint addr, uint comp, uint data) -> void {
codes.append({addr, comp, data});
}
maybe<unsigned> Cheat::find(unsigned addr, unsigned comp) {
auto Cheat::find(uint addr, uint comp) -> maybe<uint> {
for(auto& code : codes) {
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
return code.data;

View File

@ -1,17 +1,17 @@
struct Cheat {
struct Code {
unsigned addr;
unsigned comp;
unsigned data;
uint addr;
uint comp;
uint data;
};
vector<Code> codes;
enum : unsigned { Unused = ~0u };
enum : uint { Unused = ~0u };
alwaysinline bool enable() const { return codes.size() > 0; }
void reset();
void append(unsigned addr, unsigned data);
void append(unsigned addr, unsigned comp, unsigned data);
maybe<unsigned> find(unsigned addr, unsigned comp);
alwaysinline auto enable() const -> bool { return codes.size() > 0; }
auto reset() -> void;
auto append(uint addr, uint data) -> void;
auto append(uint addr, uint comp, uint data) -> void;
auto find(uint addr, uint comp) -> maybe<uint>;
};
extern Cheat cheat;

View File

@ -6,7 +6,7 @@ namespace Famicom {
#include "serialization.cpp"
CPU cpu;
void CPU::Enter() {
auto CPU::Enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
@ -16,7 +16,7 @@ void CPU::Enter() {
}
}
void CPU::main() {
auto CPU::main() -> void {
if(status.interrupt_pending) {
interrupt();
return;
@ -25,7 +25,7 @@ void CPU::main() {
exec();
}
void CPU::add_clocks(unsigned clocks) {
auto CPU::add_clocks(uint clocks) -> void {
apu.clock -= clocks;
if(apu.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(apu.thread);
@ -36,17 +36,17 @@ void CPU::add_clocks(unsigned clocks) {
if(cartridge.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cartridge.thread);
}
void CPU::power() {
auto CPU::power() -> void {
R6502::power();
for(unsigned addr = 0; addr < 0x0800; addr++) ram[addr] = 0xff;
for(auto addr : range(0x0800)) ram[addr] = 0xff;
ram[0x0008] = 0xf7;
ram[0x0009] = 0xef;
ram[0x000a] = 0xdf;
ram[0x000f] = 0xbf;
}
void CPU::reset() {
auto CPU::reset() -> void {
R6502::reset();
create(CPU::Enter, 21477272);
@ -71,19 +71,19 @@ void CPU::reset() {
status.controller_port1 = 0;
}
uint8 CPU::debugger_read(uint16 addr) {
auto CPU::debugger_read(uint16 addr) -> uint8 {
return bus.read(addr);
}
uint8 CPU::ram_read(uint16 addr) {
auto CPU::ram_read(uint16 addr) -> uint8 {
return ram[addr & 0x07ff];
}
void CPU::ram_write(uint16 addr, uint8 data) {
auto CPU::ram_write(uint16 addr, uint8 data) -> void {
ram[addr & 0x07ff] = data;
}
uint8 CPU::read(uint16 addr) {
auto CPU::read(uint16 addr) -> uint8 {
if(addr == 0x4016) {
return (mdr() & 0xc0) | input.data(0);
}
@ -95,7 +95,7 @@ uint8 CPU::read(uint16 addr) {
return apu.read(addr);
}
void CPU::write(uint16 addr, uint8 data) {
auto CPU::write(uint16 addr, uint8 data) -> void {
if(addr == 0x4014) {
status.oam_dma_page = data;
status.oam_dma_pending = true;

View File

@ -1,4 +1,37 @@
struct CPU : Processor::R6502, Thread {
static auto Enter() -> void;
auto main() -> void;
auto add_clocks(uint clocks) -> void;
auto power() -> void;
auto reset() -> void;
auto debugger_read(uint16 addr) -> uint8;
auto ram_read(uint16 addr) -> uint8;
auto ram_write(uint16 addr, uint8 data) -> void;
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
auto serialize(serializer&) -> void;
//timing.cpp
auto op_read(uint16 addr) -> uint8;
auto op_write(uint16 addr, uint8 data) -> void;
auto last_cycle() -> void;
auto nmi(uint16& vector) -> void;
auto oam_dma() -> void;
auto set_nmi_line(bool) -> void;
auto set_irq_line(bool) -> void;
auto set_irq_apu_line(bool) -> void;
auto set_rdy_line(bool) -> void;
auto set_rdy_addr(bool valid, uint16 value = 0) -> void;
//protected:
uint8 ram[0x0800];
struct Status {
@ -16,41 +49,9 @@ struct CPU : Processor::R6502, Thread {
uint8 oam_dma_page;
bool controller_latch;
unsigned controller_port0;
unsigned controller_port1;
uint controller_port0;
uint controller_port1;
} status;
static void Enter();
void main();
void add_clocks(unsigned clocks);
void power();
void reset();
uint8 debugger_read(uint16 addr);
uint8 ram_read(uint16 addr);
void ram_write(uint16 addr, uint8 data);
uint8 read(uint16 addr);
void write(uint16 addr, uint8 data);
void serialize(serializer&);
//timing.cpp
uint8 op_read(uint16 addr);
void op_write(uint16 addr, uint8 data);
void last_cycle();
void nmi(uint16 &vector);
void oam_dma();
void set_nmi_line(bool);
void set_irq_line(bool);
void set_irq_apu_line(bool);
void set_rdy_line(bool);
void set_rdy_addr(bool valid, uint16 value = 0);
};
extern CPU cpu;

View File

@ -1,4 +1,4 @@
void CPU::serialize(serializer& s) {
auto CPU::serialize(serializer& s) -> void {
R6502::serialize(s);
Thread::serialize(s);

View File

@ -1,4 +1,4 @@
uint8 CPU::op_read(uint16 addr) {
auto CPU::op_read(uint16 addr) -> uint8 {
if(status.oam_dma_pending) {
status.oam_dma_pending = false;
op_read(addr);
@ -15,50 +15,50 @@ uint8 CPU::op_read(uint16 addr) {
return regs.mdr;
}
void CPU::op_write(uint16 addr, uint8 data) {
auto CPU::op_write(uint16 addr, uint8 data) -> void {
bus.write(addr, regs.mdr = data);
add_clocks(12);
}
void CPU::last_cycle() {
auto CPU::last_cycle() -> void {
status.interrupt_pending = ((status.irq_line | status.irq_apu_line) & ~regs.p.i) | status.nmi_pending;
}
void CPU::nmi(uint16 &vector) {
auto CPU::nmi(uint16& vector) -> void {
if(status.nmi_pending) {
status.nmi_pending = false;
vector = 0xfffa;
}
}
void CPU::oam_dma() {
for(unsigned n = 0; n < 256; n++) {
auto CPU::oam_dma() -> void {
for(uint n : range(256)) {
uint8 data = op_read((status.oam_dma_page << 8) + n);
op_write(0x2004, data);
}
}
void CPU::set_nmi_line(bool line) {
auto CPU::set_nmi_line(bool line) -> void {
//edge-sensitive (0->1)
if(!status.nmi_line && line) status.nmi_pending = true;
status.nmi_line = line;
}
void CPU::set_irq_line(bool line) {
auto CPU::set_irq_line(bool line) -> void {
//level-sensitive
status.irq_line = line;
}
void CPU::set_irq_apu_line(bool line) {
auto CPU::set_irq_apu_line(bool line) -> void {
//level-sensitive
status.irq_apu_line = line;
}
void CPU::set_rdy_line(bool line) {
auto CPU::set_rdy_line(bool line) -> void {
status.rdy_line = line;
}
void CPU::set_rdy_addr(bool valid, uint16 value) {
auto CPU::set_rdy_addr(bool valid, uint16 value) -> void {
status.rdy_addr_valid = valid;
status.rdy_addr_value = value;
}

View File

@ -5,7 +5,7 @@ namespace Famicom {
#include "serialization.cpp"
Input input;
void Input::latch(bool data) {
auto Input::latch(bool data) -> void {
latchdata = data;
if(latchdata == 1) {
@ -14,7 +14,7 @@ void Input::latch(bool data) {
}
}
bool Input::data(bool port) {
auto Input::data(bool port) -> bool {
bool result = 0;
if(port == 0) {
@ -36,15 +36,15 @@ bool Input::data(bool port) {
return result;
}
void Input::connect(bool port, Device device) {
auto Input::connect(bool port, Device device) -> void {
if(port == 0) port1 = device, counter1 = 0;
if(port == 1) port2 = device, counter2 = 0;
}
void Input::power() {
auto Input::power() -> void {
}
void Input::reset() {
auto Input::reset() -> void {
latchdata = 0;
counter1 = 0;
counter2 = 0;

View File

@ -1,25 +1,25 @@
struct Input {
enum class Device : unsigned {
enum class Device : uint {
Joypad,
None,
};
void latch(bool data);
bool data(bool port);
void connect(bool port, Device device);
auto latch(bool data) -> void;
auto data(bool port) -> bool;
auto connect(bool port, Device device) -> void;
void power();
void reset();
auto power() -> void;
auto reset() -> void;
void serialize(serializer&);
auto serialize(serializer&) -> void;
private:
Device port1;
Device port2;
bool latchdata;
unsigned counter1;
unsigned counter2;
uint counter1;
uint counter2;
};
extern Input input;

View File

@ -1,6 +1,6 @@
void Input::serialize(serializer& s) {
s.integer((unsigned&)port1);
s.integer((unsigned&)port2);
auto Input::serialize(serializer& s) -> void {
s.integer((uint&)port1);
s.integer((uint&)port2);
s.integer(latchdata);
s.integer(counter1);

View File

@ -4,27 +4,66 @@ namespace Famicom {
Interface* interface = nullptr;
string Interface::title() {
Interface::Interface() {
interface = this;
information.name = "Famicom";
information.width = 256;
information.height = 240;
information.overscan = true;
information.aspectRatio = 8.0 / 7.0;
information.resettable = true;
information.capability.states = true;
information.capability.cheats = true;
media.append({ID::Famicom, "Famicom", "fc", true});
{ Device device{0, ID::Port1 | ID::Port2, "Controller"};
device.input.append({0, 0, "A" });
device.input.append({1, 0, "B" });
device.input.append({2, 0, "Select"});
device.input.append({3, 0, "Start" });
device.input.append({4, 0, "Up" });
device.input.append({5, 0, "Down" });
device.input.append({6, 0, "Left" });
device.input.append({7, 0, "Right" });
device.order = {4, 5, 6, 7, 1, 0, 2, 3};
this->device.append(device);
}
port.append({0, "Port 1"});
port.append({1, "Port 2"});
for(auto& device : this->device) {
for(auto& port : this->port) {
if(device.portmask & (1 << port.id)) {
port.device.append(device);
}
}
}
}
auto Interface::title() -> string {
return cartridge.title();
}
double Interface::videoFrequency() {
auto Interface::videoFrequency() -> double {
return 21477272.0 / (262.0 * 1364.0 - 4.0);
}
double Interface::audioFrequency() {
auto Interface::audioFrequency() -> double {
return 21477272.0 / 12.0;
}
bool Interface::loaded() {
auto Interface::loaded() -> bool {
return cartridge.loaded();
}
string Interface::sha256() {
auto Interface::sha256() -> string {
return cartridge.sha256();
}
unsigned Interface::group(unsigned id) {
auto Interface::group(uint id) -> uint {
switch(id) {
case ID::SystemManifest:
return 0;
@ -39,17 +78,17 @@ unsigned Interface::group(unsigned id) {
throw;
}
void Interface::load(unsigned id) {
auto Interface::load(uint id) -> void {
cartridge.load();
}
void Interface::save() {
auto Interface::save() -> void {
for(auto& memory : cartridge.memory) {
saveRequest(memory.id, memory.name);
}
}
void Interface::load(unsigned id, const stream& stream) {
auto Interface::load(uint id, const stream& stream) -> void {
if(id == ID::SystemManifest) {
system.information.manifest = stream.text();
}
@ -75,7 +114,7 @@ void Interface::load(unsigned id, const stream& stream) {
}
}
void Interface::save(unsigned id, const stream& stream) {
auto Interface::save(uint id, const stream& stream) -> void {
if(id == ID::ProgramRAM) {
stream.write(cartridge.board->prgram.data, cartridge.board->prgram.size);
}
@ -85,33 +124,33 @@ void Interface::save(unsigned id, const stream& stream) {
}
}
void Interface::unload() {
auto Interface::unload() -> void {
save();
cartridge.unload();
}
void Interface::power() {
auto Interface::power() -> void {
system.power();
}
void Interface::reset() {
auto Interface::reset() -> void {
system.reset();
}
void Interface::run() {
auto Interface::run() -> void {
system.run();
}
serializer Interface::serialize() {
auto Interface::serialize() -> serializer {
system.runtosave();
return system.serialize();
}
bool Interface::unserialize(serializer& s) {
auto Interface::unserialize(serializer& s) -> bool {
return system.unserialize(s);
}
void Interface::cheatSet(const lstring& list) {
auto Interface::cheatSet(const lstring& list) -> void {
cheat.reset();
for(auto& codeset : list) {
lstring codes = codeset.split("+");
@ -123,48 +162,8 @@ void Interface::cheatSet(const lstring& list) {
}
}
void Interface::paletteUpdate(PaletteMode mode) {
auto Interface::paletteUpdate(PaletteMode mode) -> void {
video.generate_palette(mode);
}
Interface::Interface() {
interface = this;
information.name = "Famicom";
information.width = 256;
information.height = 240;
information.overscan = true;
information.aspectRatio = 8.0 / 7.0;
information.resettable = true;
information.capability.states = true;
information.capability.cheats = true;
media.append({ID::Famicom, "Famicom", "fc", true});
{
Device device{0, ID::Port1 | ID::Port2, "Controller"};
device.input.append({0, 0, "A" });
device.input.append({1, 0, "B" });
device.input.append({2, 0, "Select"});
device.input.append({3, 0, "Start" });
device.input.append({4, 0, "Up" });
device.input.append({5, 0, "Down" });
device.input.append({6, 0, "Left" });
device.input.append({7, 0, "Right" });
device.order = {4, 5, 6, 7, 1, 0, 2, 3};
this->device.append(device);
}
port.append({0, "Port 1"});
port.append({1, "Port 2"});
for(auto& device : this->device) {
for(auto& port : this->port) {
if(device.portmask & (1 << port.id)) {
port.device.append(device);
}
}
}
}
}

View File

@ -3,12 +3,12 @@ namespace Famicom {
#endif
struct ID {
enum : unsigned {
enum : uint {
System,
Famicom,
};
enum : unsigned {
enum : uint {
SystemManifest,
Manifest,
@ -18,39 +18,39 @@ struct ID {
CharacterRAM,
};
enum : unsigned {
enum : uint {
Port1 = 1,
Port2 = 2,
};
};
struct Interface : Emulator::Interface {
string title();
double videoFrequency();
double audioFrequency();
bool loaded();
string sha256();
unsigned group(unsigned id);
void load(unsigned id);
void save();
void load(unsigned id, const stream& stream);
void save(unsigned id, const stream& stream);
void unload();
void power();
void reset();
void run();
serializer serialize();
bool unserialize(serializer&);
void cheatSet(const lstring&);
void paletteUpdate(PaletteMode mode);
Interface();
auto title() -> string;
auto videoFrequency() -> double;
auto audioFrequency() -> double;
auto loaded() -> bool;
auto sha256() -> string;
auto group(uint id) -> uint;
auto load(uint id) -> void;
auto save() -> void;
auto load(uint id, const stream& stream) -> void;
auto save(uint id, const stream& stream) -> void;
auto unload() -> void;
auto power() -> void;
auto reset() -> void;
auto run() -> void;
auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;
auto cheatSet(const lstring&) -> void;
auto paletteUpdate(PaletteMode mode) -> void;
private:
vector<Device> device;
};

View File

@ -11,7 +11,7 @@ Bus bus;
//$4000-4017 = APU + I/O
//$4018-ffff = Cartridge
uint8 Bus::read(uint16 addr) {
auto Bus::read(uint16 addr) -> uint8 {
uint8 data = cartridge.prg_read(addr);
if(addr <= 0x1fff) data = cpu.ram_read(addr);
else if(addr <= 0x3fff) data = ppu.read(addr);
@ -24,7 +24,7 @@ uint8 Bus::read(uint16 addr) {
return data;
}
void Bus::write(uint16 addr, uint8 data) {
auto Bus::write(uint16 addr, uint8 data) -> void {
cartridge.prg_write(addr, data);
if(addr <= 0x1fff) return cpu.ram_write(addr, data);
if(addr <= 0x3fff) return ppu.write(addr, data);

View File

@ -1,6 +1,6 @@
struct Bus {
uint8 read(uint16 addr);
void write(uint16 addr, uint8 data);
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
};
extern Bus bus;

View File

@ -5,11 +5,11 @@ namespace Famicom {
#include "serialization.cpp"
PPU ppu;
void PPU::Main() {
auto PPU::Main() -> void {
ppu.main();
}
void PPU::main() {
auto PPU::main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::PPU) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
@ -19,7 +19,7 @@ void PPU::main() {
}
}
void PPU::tick() {
auto PPU::tick() -> void {
if(status.ly == 240 && status.lx == 340) status.nmi_hold = 1;
if(status.ly == 241 && status.lx == 0) status.nmi_flag = status.nmi_hold;
if(status.ly == 241 && status.lx == 2) cpu.set_nmi_line(status.nmi_enable && status.nmi_flag);
@ -36,7 +36,7 @@ void PPU::tick() {
status.lx++;
}
void PPU::scanline() {
auto PPU::scanline() -> void {
status.lx = 0;
if(++status.ly == 262) {
status.ly = 0;
@ -45,15 +45,15 @@ void PPU::scanline() {
cartridge.scanline(status.ly);
}
void PPU::frame() {
auto PPU::frame() -> void {
status.field ^= 1;
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
void PPU::power() {
auto PPU::power() -> void {
}
void PPU::reset() {
auto PPU::reset() -> void {
create(PPU::Main, 21477272);
status.mdr = 0x00;
@ -98,7 +98,7 @@ void PPU::reset() {
for(auto& n : oam ) n = 0;
}
uint8 PPU::read(uint16 addr) {
auto PPU::read(uint16 addr) -> uint8 {
uint8 result = 0x00;
switch(addr & 7) {
@ -136,7 +136,7 @@ uint8 PPU::read(uint16 addr) {
return result;
}
void PPU::write(uint16 addr, uint8 data) {
auto PPU::write(uint16 addr, uint8 data) -> void {
status.mdr = data;
switch(addr & 7) {
@ -200,22 +200,22 @@ void PPU::write(uint16 addr, uint8 data) {
}
}
uint8 PPU::ciram_read(uint16 addr) {
auto PPU::ciram_read(uint16 addr) -> uint8 {
return ciram[addr & 0x07ff];
}
void PPU::ciram_write(uint16 addr, uint8 data) {
auto PPU::ciram_write(uint16 addr, uint8 data) -> void {
ciram[addr & 0x07ff] = data;
}
uint8 PPU::cgram_read(uint16 addr) {
auto PPU::cgram_read(uint16 addr) -> uint8 {
if((addr & 0x13) == 0x10) addr &= ~0x10;
uint8 data = cgram[addr & 0x1f];
if(status.grayscale) data &= 0x30;
return data;
}
void PPU::cgram_write(uint16 addr, uint8 data) {
auto PPU::cgram_write(uint16 addr, uint8 data) -> void {
if((addr & 0x13) == 0x10) addr &= ~0x10;
cgram[addr & 0x1f] = data;
}
@ -229,36 +229,36 @@ void PPU::cgram_write(uint16 addr, uint8 data) {
//YYYYY = Y nametable (y:d3-d7)
//XXXXX = X nametable (x:d3-d7)
bool PPU::raster_enable() const {
auto PPU::raster_enable() const -> bool {
return (status.bg_enable || status.sprite_enable);
}
unsigned PPU::nametable_addr() const {
auto PPU::nametable_addr() const -> uint {
return 0x2000 + (status.vaddr & 0x0c00);
}
unsigned PPU::scrollx() const {
auto PPU::scrollx() const -> uint {
return ((status.vaddr & 0x1f) << 3) | status.xaddr;
}
unsigned PPU::scrolly() const {
auto PPU::scrolly() const -> uint {
return (((status.vaddr >> 5) & 0x1f) << 3) | ((status.vaddr >> 12) & 7);
}
unsigned PPU::sprite_height() const {
auto PPU::sprite_height() const -> uint {
return status.sprite_size == 0 ? 8 : 16;
}
//
uint8 PPU::chr_load(uint16 addr) {
auto PPU::chr_load(uint16 addr) -> uint8 {
if(raster_enable() == false) return 0x00;
return cartridge.chr_read(addr);
}
//
void PPU::scrollx_increment() {
auto PPU::scrollx_increment() -> void {
if(raster_enable() == false) return;
status.vaddr = (status.vaddr & 0x7fe0) | ((status.vaddr + 0x0001) & 0x001f);
if((status.vaddr & 0x001f) == 0x0000) {
@ -266,7 +266,7 @@ void PPU::scrollx_increment() {
}
}
void PPU::scrolly_increment() {
auto PPU::scrolly_increment() -> void {
if(raster_enable() == false) return;
status.vaddr = (status.vaddr & 0x0fff) | ((status.vaddr + 0x1000) & 0x7000);
if((status.vaddr & 0x7000) == 0x0000) {
@ -280,16 +280,16 @@ void PPU::scrolly_increment() {
//
void PPU::raster_pixel() {
auto PPU::raster_pixel() -> void {
uint32* output = buffer + status.ly * 256;
unsigned mask = 0x8000 >> (status.xaddr + (status.lx & 7));
unsigned palette = 0, object_palette = 0;
uint mask = 0x8000 >> (status.xaddr + (status.lx & 7));
uint palette = 0, object_palette = 0;
bool object_priority = 0;
palette |= (raster.tiledatalo & mask) ? 1 : 0;
palette |= (raster.tiledatahi & mask) ? 2 : 0;
if(palette) {
unsigned attr = raster.attribute;
uint attr = raster.attribute;
if(mask >= 256) attr >>= 2;
palette |= (attr & 3) << 2;
}
@ -298,16 +298,16 @@ void PPU::raster_pixel() {
if(status.bg_edge_enable == false && status.lx < 8) palette = 0;
if(status.sprite_enable == true)
for(signed sprite = 7; sprite >= 0; sprite--) {
for(int sprite = 7; sprite >= 0; sprite--) {
if(status.sprite_edge_enable == false && status.lx < 8) continue;
if(raster.oam[sprite].id == 64) continue;
unsigned spritex = status.lx - raster.oam[sprite].x;
uint spritex = status.lx - raster.oam[sprite].x;
if(spritex >= 8) continue;
if(raster.oam[sprite].attr & 0x40) spritex ^= 7;
unsigned mask = 0x80 >> spritex;
unsigned sprite_palette = 0;
uint mask = 0x80 >> spritex;
uint sprite_palette = 0;
sprite_palette |= (raster.oam[sprite].tiledatalo & mask) ? 1 : 0;
sprite_palette |= (raster.oam[sprite].tiledatahi & mask) ? 2 : 0;
if(sprite_palette == 0) continue;
@ -327,12 +327,12 @@ void PPU::raster_pixel() {
output[status.lx] = (status.emphasis << 6) | cgram_read(palette);
}
void PPU::raster_sprite() {
auto PPU::raster_sprite() -> void {
if(raster_enable() == false) return;
unsigned n = raster.oam_iterator++;
signed ly = (status.ly == 261 ? -1 : status.ly);
unsigned y = ly - oam[(n * 4) + 0];
uint n = raster.oam_iterator++;
int ly = (status.ly == 261 ? -1 : status.ly);
uint y = ly - oam[(n * 4) + 0];
if(y >= sprite_height()) return;
if(raster.oam_counter == 8) {
@ -348,16 +348,16 @@ void PPU::raster_sprite() {
raster.oam_counter++;
}
void PPU::raster_scanline() {
auto PPU::raster_scanline() -> void {
if((status.ly >= 240 && status.ly <= 260)) {
for(unsigned x = 0; x < 341; x++) tick();
for(auto x : range(341)) tick();
return scanline();
}
raster.oam_iterator = 0;
raster.oam_counter = 0;
for(unsigned n = 0; n < 8; n++) {
for(auto n : range(8)) {
raster.soam[n].id = 64;
raster.soam[n].y = 0xff;
raster.soam[n].tile = 0xff;
@ -367,16 +367,16 @@ void PPU::raster_scanline() {
raster.soam[n].tiledatahi = 0;
}
for(unsigned tile = 0; tile < 32; tile++) { // 0-255
unsigned nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
unsigned tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
for(uint tile : range(32)) { // 0-255
uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
uint tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
raster_pixel();
tick();
raster_pixel();
tick();
unsigned attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
uint attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
if(scrolly() & 16) attribute >>= 4;
if(scrollx() & 16) attribute >>= 2;
raster_pixel();
@ -388,14 +388,14 @@ void PPU::raster_scanline() {
raster_sprite();
tick();
unsigned tiledatalo = chr_load(tileaddr + 0);
uint tiledatalo = chr_load(tileaddr + 0);
raster_pixel();
tick();
raster_pixel();
tick();
unsigned tiledatahi = chr_load(tileaddr + 8);
uint tiledatahi = chr_load(tileaddr + 8);
raster_pixel();
tick();
@ -409,23 +409,23 @@ void PPU::raster_scanline() {
raster.tiledatahi = (raster.tiledatahi << 8) | tiledatahi;
}
for(unsigned n = 0; n < 8; n++) raster.oam[n] = raster.soam[n];
for(auto n : range(8)) raster.oam[n] = raster.soam[n];
for(unsigned sprite = 0; sprite < 8; sprite++) { //256-319
unsigned nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
for(uint sprite : range(8)) { //256-319
uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
tick();
if(raster_enable() && sprite == 0) status.vaddr = (status.vaddr & 0x7be0) | (status.taddr & 0x041f); //257
tick();
unsigned attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
unsigned tileaddr = (sprite_height() == 8)
uint attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
uint tileaddr = (sprite_height() == 8)
? status.sprite_addr + raster.oam[sprite].tile * 16
: ((raster.oam[sprite].tile & ~1) * 16) + ((raster.oam[sprite].tile & 1) * 0x1000);
tick();
tick();
unsigned spritey = (status.ly - raster.oam[sprite].y) & (sprite_height() - 1);
uint spritey = (status.ly - raster.oam[sprite].y) & (sprite_height() - 1);
if(raster.oam[sprite].attr & 0x80) spritey ^= (sprite_height() - 1);
tileaddr += spritey + (spritey & 8);
@ -440,13 +440,13 @@ void PPU::raster_scanline() {
if(raster_enable() && sprite == 6 && status.ly == 261) status.vaddr = status.taddr; //304
}
for(unsigned tile = 0; tile < 2; tile++) { //320-335
unsigned nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
unsigned tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
for(uint tile : range(2)) { //320-335
uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
uint tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
tick();
tick();
unsigned attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
uint attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
if(scrolly() & 16) attribute >>= 4;
if(scrollx() & 16) attribute >>= 2;
tick();
@ -454,11 +454,11 @@ void PPU::raster_scanline() {
scrollx_increment();
tick();
unsigned tiledatalo = chr_load(tileaddr + 0);
uint tiledatalo = chr_load(tileaddr + 0);
tick();
tick();
unsigned tiledatahi = chr_load(tileaddr + 8);
uint tiledatahi = chr_load(tileaddr + 8);
tick();
tick();

View File

@ -1,46 +1,46 @@
struct PPU : Thread {
static void Main();
void main();
void tick();
static auto Main() -> void;
auto main() -> void;
auto tick() -> void;
void scanline();
void frame();
auto scanline() -> void;
auto frame() -> void;
void power();
void reset();
auto power() -> void;
auto reset() -> void;
uint8 read(uint16 addr);
void write(uint16 addr, uint8 data);
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
uint8 ciram_read(uint16 addr);
void ciram_write(uint16 addr, uint8 data);
auto ciram_read(uint16 addr) -> uint8;
auto ciram_write(uint16 addr, uint8 data) -> void;
uint8 cgram_read(uint16 addr);
void cgram_write(uint16 addr, uint8 data);
auto cgram_read(uint16 addr) -> uint8;
auto cgram_write(uint16 addr, uint8 data) -> void;
bool raster_enable() const;
unsigned nametable_addr() const;
unsigned scrollx() const;
unsigned scrolly() const;
unsigned sprite_height() const;
auto raster_enable() const -> bool;
auto nametable_addr() const -> uint;
auto scrollx() const -> uint;
auto scrolly() const -> uint;
auto sprite_height() const -> uint;
uint8 chr_load(uint16 addr);
auto chr_load(uint16 addr) -> uint8;
void scrollx_increment();
void scrolly_increment();
auto scrollx_increment() -> void;
auto scrolly_increment() -> void;
void raster_pixel();
void raster_sprite();
void raster_scanline();
auto raster_pixel() -> void;
auto raster_sprite() -> void;
auto raster_scanline() -> void;
void serialize(serializer&);
auto serialize(serializer&) -> void;
struct Status {
uint8 mdr;
bool field;
unsigned lx;
unsigned ly;
uint lx;
uint ly;
uint8 bus_data;
@ -57,9 +57,9 @@ struct PPU : Thread {
bool nmi_enable;
bool master_select;
bool sprite_size;
unsigned bg_addr;
unsigned sprite_addr;
unsigned vram_increment;
uint bg_addr;
uint sprite_addr;
uint vram_increment;
//$2001
uint3 emphasis;
@ -83,8 +83,8 @@ struct PPU : Thread {
uint16 tiledatalo;
uint16 tiledatahi;
unsigned oam_iterator;
unsigned oam_counter;
uint oam_iterator;
uint oam_counter;
struct OAM {
uint8 id;

View File

@ -1,4 +1,4 @@
void PPU::serialize(serializer& s) {
auto PPU::serialize(serializer& s) -> void {
Thread::serialize(s);
s.integer(status.mdr);
@ -45,7 +45,7 @@ void PPU::serialize(serializer& s) {
s.integer(raster.oam_iterator);
s.integer(raster.oam_counter);
for(unsigned n = 0; n < 8; n++) {
for(auto n : range(8)) {
s.integer(raster.oam[n].id);
s.integer(raster.oam[n].y);
s.integer(raster.oam[n].tile);
@ -56,7 +56,7 @@ void PPU::serialize(serializer& s) {
s.integer(raster.oam[n].tiledatahi);
}
for(unsigned n = 0; n < 8; n++) {
for(auto n : range(8)) {
s.integer(raster.soam[n].id);
s.integer(raster.soam[n].y);
s.integer(raster.soam[n].tile);

View File

@ -4,21 +4,21 @@ namespace Famicom {
Scheduler scheduler;
void Scheduler::enter() {
auto Scheduler::enter() -> void {
host_thread = co_active();
co_switch(thread);
}
void Scheduler::exit(ExitReason reason) {
auto Scheduler::exit(ExitReason reason) -> void {
exit_reason = reason;
thread = co_active();
co_switch(host_thread);
}
void Scheduler::power() {
auto Scheduler::power() -> void {
}
void Scheduler::reset() {
auto Scheduler::reset() -> void {
host_thread = co_active();
thread = cpu.thread;
sync = SynchronizeMode::None;

View File

@ -1,16 +1,16 @@
struct Scheduler : property<Scheduler> {
enum class SynchronizeMode : unsigned { None, PPU, All } sync;
enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent };
readonly<ExitReason> exit_reason;
enum class SynchronizeMode : uint { None, PPU, All } sync;
enum class ExitReason : uint { UnknownEvent, FrameEvent, SynchronizeEvent };
auto enter() -> void;
auto exit(ExitReason) -> void;
auto power() -> void;
auto reset() -> void;
cothread_t host_thread; //program thread (used to exit emulation)
cothread_t thread; //active emulation thread (used to enter emulation)
void enter();
void exit(ExitReason);
void power();
void reset();
readonly<ExitReason> exit_reason;
};
extern Scheduler scheduler;

View File

@ -1,7 +1,7 @@
serializer System::serialize() {
auto System::serialize() -> serializer {
serializer s(serialize_size);
unsigned signature = 0x31545342, version = Info::SerializerVersion;
uint signature = 0x31545342, version = Info::SerializerVersion;
char hash[64], description[512];
memcpy(&hash, (const char*)cartridge.sha256(), 64);
memset(&description, 0, sizeof description);
@ -15,8 +15,8 @@ serializer System::serialize() {
return s;
}
bool System::unserialize(serializer& s) {
unsigned signature, version;
auto System::unserialize(serializer& s) -> bool {
uint signature, version;
char hash[64], description[512];
s.integer(signature);
@ -32,10 +32,10 @@ bool System::unserialize(serializer& s) {
return true;
}
void System::serialize(serializer& s) {
auto System::serialize(serializer& s) -> void {
}
void System::serialize_all(serializer& s) {
auto System::serialize_all(serializer& s) -> void {
system.serialize(s);
input.serialize(s);
cartridge.serialize(s);
@ -44,10 +44,10 @@ void System::serialize_all(serializer& s) {
ppu.serialize(s);
}
void System::serialize_init() {
auto System::serialize_init() -> void {
serializer s;
unsigned signature = 0, version = 0;
uint signature = 0, version = 0;
char hash[64], description[512];
s.integer(signature);

View File

@ -5,14 +5,14 @@ namespace Famicom {
#include "serialization.cpp"
System system;
void System::run() {
auto System::run() -> void {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(video.palette, ppu.buffer, 4 * 256, 256, 240);
}
}
void System::runtosave() {
auto System::runtosave() -> void {
scheduler.sync = Scheduler::SynchronizeMode::PPU;
runthreadtosave();
@ -31,7 +31,7 @@ void System::runtosave() {
scheduler.sync = Scheduler::SynchronizeMode::None;
}
void System::runthreadtosave() {
auto System::runthreadtosave() -> void {
while(true) {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
@ -41,14 +41,14 @@ void System::runthreadtosave() {
}
}
void System::load() {
auto System::load() -> void {
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
auto document = BML::unserialize(information.manifest);
serialize_init();
}
void System::power() {
auto System::power() -> void {
cartridge.power();
cpu.power();
apu.power();
@ -58,7 +58,7 @@ void System::power() {
reset();
}
void System::reset() {
auto System::reset() -> void {
cartridge.reset();
cpu.reset();
apu.reset();
@ -67,13 +67,13 @@ void System::reset() {
scheduler.reset();
}
void System::init() {
auto System::init() -> void {
assert(interface != 0);
input.connect(0, Input::Device::Joypad);
input.connect(1, Input::Device::None);
}
void System::term() {
auto System::term() -> void {
}
}

View File

@ -1,26 +1,27 @@
struct System {
void run();
void runtosave();
void runthreadtosave();
auto run() -> void;
auto runtosave() -> void;
auto runthreadtosave() -> void;
void load();
void power();
void reset();
auto load() -> void;
auto power() -> void;
auto reset() -> void;
void init();
void term();
auto init() -> void;
auto term() -> void;
serializer serialize();
bool unserialize(serializer&);
auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;
void serialize(serializer&);
void serialize_all(serializer&);
void serialize_init();
unsigned serialize_size;
auto serialize(serializer&) -> void;
auto serialize_all(serializer&) -> void;
auto serialize_init() -> void;
struct Information {
string manifest;
} information;
uint serialize_size;
};
extern System system;

View File

@ -6,14 +6,22 @@ namespace Famicom {
Video video;
void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
for(unsigned color = 0; color < (1 << 9); color++) {
Video::Video() {
palette = new uint32_t[1 << 9]();
}
Video::~Video() {
delete[] palette;
}
auto Video::generate_palette(Emulator::Interface::PaletteMode mode) -> void {
for(auto color : range(1 << 9)) {
if(mode == Emulator::Interface::PaletteMode::Literal) {
palette[color] = color;
} else if(mode == Emulator::Interface::PaletteMode::Channel) {
unsigned emphasis = (color >> 6) & 7;
unsigned luma = (color >> 4) & 3;
unsigned chroma = (color >> 0) & 15;
uint emphasis = (color >> 6) & 7;
uint luma = (color >> 4) & 3;
uint chroma = (color >> 0) & 15;
emphasis = image::normalize(emphasis, 3, 16);
luma = image::normalize(luma, 2, 16);
chroma = image::normalize(chroma, 4, 16);
@ -26,19 +34,11 @@ void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
}
}
Video::Video() {
palette = new uint32_t[1 << 9]();
}
Video::~Video() {
delete[] palette;
}
uint32_t Video::generate_color(
unsigned n, double saturation, double hue,
auto Video::generate_color(
uint n, double saturation, double hue,
double contrast, double brightness, double gamma
) {
signed color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1;
) -> uint32 {
int color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1;
static const double black = 0.518, white = 1.962, attenuation = 0.746;
static const double levels[8] = {
@ -52,8 +52,8 @@ uint32_t Video::generate_color(
};
double y = 0.0, i = 0.0, q = 0.0;
auto wave = [](signed p, signed color) { return (color + p + 8) % 12 < 6; };
for(signed p = 0; p < 12; p++) {
auto wave = [](int p, int color) { return (color + p + 8) % 12 < 6; };
for(int p : range(12)) {
double spot = lo_and_hi[wave(p, color)];
if(((n & 0x040) && wave(p, 12))
@ -75,9 +75,9 @@ uint32_t Video::generate_color(
q *= saturation;
auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : std::pow(f, 2.2 / gamma); };
unsigned r = 65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q);
unsigned g = 65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q);
unsigned b = 65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q);
uint r = 65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q);
uint g = 65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q);
uint b = 65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q);
return interface->videoColor(n, 0, uclamp<16>(r), uclamp<16>(g), uclamp<16>(b));
}

View File

@ -1,12 +1,13 @@
struct Video {
uint32_t* palette = nullptr;
void generate_palette(Emulator::Interface::PaletteMode mode);
Video();
~Video();
auto generate_palette(Emulator::Interface::PaletteMode mode) -> void;
uint32* palette = nullptr;
private:
uint32_t generate_color(unsigned, double, double, double, double, double);
auto generate_color(uint, double, double, double, double, double) -> uint32;
};
extern Video video;

View File

@ -69,7 +69,7 @@ endif
# windows settings
ifeq ($(platform),windows)
link := $(link) -lws2_32 -lole32
link += -lws2_32 -lole32
endif
# macosx settings
@ -78,6 +78,11 @@ ifeq ($(platform),macosx)
link += -lc++ -lobjc
endif
# linux settings
ifeq ($(platform),linux)
link += -ldl
endif
# bsd settings
ifeq ($(platform),bsd)
flags += -I/usr/local/include
@ -93,12 +98,6 @@ ifeq ($(threaded),true)
endif
endif
# cross-compilation support
ifeq ($(arch),x86)
flags := -m32 $(flags)
link := -m32 $(link)
endif
# paths
prefix := $(HOME)/.local

View File

@ -18,12 +18,12 @@ void CPU::step(unsigned clocks) {
auto& chip = *coprocessors[i];
chip.clock -= clocks * (uint64)chip.frequency;
}
input.port1->clock -= clocks * (uint64)input.port1->frequency;
input.port2->clock -= clocks * (uint64)input.port2->frequency;
synchronize_controllers();
device.controllerPort1->clock -= clocks * (uint64)device.controllerPort1->frequency;
device.controllerPort2->clock -= clocks * (uint64)device.controllerPort2->frequency;
synchronizeDevices();
}
void CPU::synchronize_smp() {
void CPU::synchronizeSMP() {
if(SMP::Threaded == true) {
if(smp.clock < 0) co_switch(smp.thread);
} else {
@ -31,7 +31,7 @@ void CPU::synchronize_smp() {
}
}
void CPU::synchronize_ppu() {
void CPU::synchronizePPU() {
if(PPU::Threaded == true) {
if(ppu.clock < 0) co_switch(ppu.thread);
} else {
@ -39,16 +39,16 @@ void CPU::synchronize_ppu() {
}
}
void CPU::synchronize_coprocessors() {
void CPU::synchronizeCoprocessors() {
for(unsigned i = 0; i < coprocessors.size(); i++) {
auto& chip = *coprocessors[i];
if(chip.clock < 0) co_switch(chip.thread);
}
}
void CPU::synchronize_controllers() {
if(input.port1->clock < 0) co_switch(input.port1->thread);
if(input.port2->clock < 0) co_switch(input.port2->thread);
void CPU::synchronizeDevices() {
if(device.controllerPort1->clock < 0) co_switch(device.controllerPort1->thread);
if(device.controllerPort2->clock < 0) co_switch(device.controllerPort2->thread);
}
void CPU::Enter() { cpu.enter(); }
@ -110,7 +110,7 @@ void CPU::power() {
}
void CPU::reset() {
create(Enter, system.cpu_frequency());
create(Enter, system.cpuFrequency());
coprocessors.reset();
PPUcounter::reset();

View File

@ -4,10 +4,10 @@ struct CPU : Processor::R65816, Thread, public PPUcounter {
enum : bool { Threaded = true };
vector<Thread*> coprocessors;
alwaysinline void step(unsigned clocks);
alwaysinline void synchronize_smp();
void synchronize_ppu();
void synchronize_coprocessors();
void synchronize_controllers();
alwaysinline void synchronizeSMP();
void synchronizePPU();
void synchronizeCoprocessors();
void synchronizeDevices();
uint8 pio();
bool joylatch();

View File

@ -2,7 +2,7 @@
uint8 CPU::mmio_read(unsigned addr) {
if((addr & 0xffc0) == 0x2140) {
synchronize_smp();
synchronizeSMP();
return smp.port_read(addr & 3);
}
@ -15,13 +15,13 @@ uint8 CPU::mmio_read(unsigned addr) {
case 0x4016: {
uint8 result = regs.mdr & 0xfc;
result |= input.port1->data() & 3;
result |= device.controllerPort1->data() & 3;
return result;
}
case 0x4017: {
uint8 result = (regs.mdr & 0xe0) | 0x1c;
result |= input.port2->data() & 3;
result |= device.controllerPort2->data() & 3;
return result;
}
@ -99,7 +99,7 @@ uint8 CPU::mmio_read(unsigned addr) {
void CPU::mmio_write(unsigned addr, uint8 data) {
if((addr & 0xffc0) == 0x2140) {
synchronize_smp();
synchronizeSMP();
port_write(addr & 3, data);
return;
}
@ -127,8 +127,8 @@ void CPU::mmio_write(unsigned addr, uint8 data) {
}
case 0x4016: {
input.port1->latch(data & 1);
input.port2->latch(data & 1);
device.controllerPort1->latch(data & 1);
device.controllerPort2->latch(data & 1);
return;
}

View File

@ -59,9 +59,9 @@ void CPU::add_clocks(unsigned clocks) {
}
void CPU::scanline() {
synchronize_smp();
synchronize_ppu();
synchronize_coprocessors();
synchronizeSMP();
synchronizePPU();
synchronizeCoprocessors();
system.scanline();
if(vcounter() == 0) hdma_init();
@ -87,15 +87,15 @@ void CPU::scanline() {
}
void CPU::run_auto_joypad_poll() {
input.port1->latch(1);
input.port2->latch(1);
input.port1->latch(0);
input.port2->latch(0);
device.controllerPort1->latch(1);
device.controllerPort2->latch(1);
device.controllerPort1->latch(0);
device.controllerPort2->latch(0);
uint16 joy1 = 0, joy2 = 0, joy3 = 0, joy4 = 0;
for(unsigned i = 0; i < 16; i++) {
uint8 port0 = input.port1->data();
uint8 port1 = input.port2->data();
uint8 port0 = device.controllerPort1->data();
uint8 port1 = device.controllerPort2->data();
joy1 |= (port0 & 1) ? (0x8000 >> i) : 0;
joy2 |= (port1 & 1) ? (0x8000 >> i) : 0;

View File

@ -1,6 +1,5 @@
#include <sfc/sfc.hpp>
#define DSP_CPP
namespace SuperFamicom {
DSP dsp;
@ -8,11 +7,15 @@ DSP dsp;
#include "serialization.cpp"
#include "SPC_DSP.cpp"
void DSP::step(unsigned clocks) {
DSP::DSP() {
for(auto i : range(8)) channel_enabled[i] = true;
}
auto DSP::step(uint clocks) -> void {
clock += clocks;
}
void DSP::synchronize_smp() {
auto DSP::synchronizeSMP() -> void {
if(SMP::Threaded == true) {
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(smp.thread);
} else {
@ -20,52 +23,48 @@ void DSP::synchronize_smp() {
}
}
void DSP::enter() {
auto DSP::enter() -> void {
spc_dsp.run(1);
step(24);
signed count = spc_dsp.sample_count();
int count = spc_dsp.sample_count();
if(count > 0) {
for(unsigned n = 0; n < count; n += 2) audio.sample(samplebuffer[n + 0], samplebuffer[n + 1]);
for(uint n = 0; n < count; n += 2) audio.sample(samplebuffer[n + 0], samplebuffer[n + 1]);
spc_dsp.set_output(samplebuffer, 8192);
}
}
bool DSP::mute() {
auto DSP::mute() -> bool {
return spc_dsp.mute();
}
uint8 DSP::read(uint8 addr) {
auto DSP::read(uint8 addr) -> uint8 {
return spc_dsp.read(addr);
}
void DSP::write(uint8 addr, uint8 data) {
auto DSP::write(uint8 addr, uint8 data) -> void {
spc_dsp.write(addr, data);
}
void DSP::power() {
auto DSP::power() -> void {
spc_dsp.init(smp.apuram);
spc_dsp.reset();
spc_dsp.set_output(samplebuffer, 8192);
}
void DSP::reset() {
auto DSP::reset() -> void {
Thread::clock = 0;
spc_dsp.soft_reset();
spc_dsp.set_output(samplebuffer, 8192);
}
void DSP::channel_enable(unsigned channel, bool enable) {
auto DSP::channel_enable(uint channel, bool enable) -> void {
channel_enabled[channel & 7] = enable;
unsigned mask = 0;
for(unsigned i = 0; i < 8; i++) {
uint mask = 0;
for(auto i : range(8)) {
if(channel_enabled[i] == false) mask |= 1 << i;
}
spc_dsp.mute_voices(mask);
}
DSP::DSP() {
for(unsigned i = 0; i < 8; i++) channel_enabled[i] = true;
}
}

View File

@ -2,22 +2,24 @@
struct DSP : Thread {
enum : bool { Threaded = false };
alwaysinline void step(unsigned clocks);
alwaysinline void synchronize_smp();
bool mute();
uint8 read(uint8 addr);
void write(uint8 addr, uint8 data);
void enter();
void power();
void reset();
void channel_enable(unsigned channel, bool enable);
void serialize(serializer&);
DSP();
alwaysinline auto step(uint clocks) -> void;
alwaysinline auto synchronizeSMP() -> void;
auto mute() -> bool;
auto read(uint8 addr) -> uint8;
auto write(uint8 addr, uint8 data) -> void;
auto enter() -> void;
auto power() -> void;
auto reset() -> void;
auto channel_enable(uint channel, bool enable) -> void;
auto serialize(serializer&) -> void;
private:
SPC_DSP spc_dsp;
int16 samplebuffer[8192];

View File

@ -1,16 +1,14 @@
#ifdef DSP_CPP
static void dsp_state_save(unsigned char **out, void *in, size_t size) {
static auto dsp_state_save(unsigned char** out, void* in, size_t size) -> void {
memcpy(*out, in, size);
*out += size;
}
static void dsp_state_load(unsigned char **in, void *out, size_t size) {
static auto dsp_state_load(unsigned char** in, void* out, size_t size) -> void {
memcpy(out, *in, size);
*in += size;
}
void DSP::serialize(serializer &s) {
auto DSP::serialize(serializer &s) -> void {
Thread::serialize(s);
s.array(samplebuffer);
@ -27,5 +25,3 @@ void DSP::serialize(serializer &s) {
s.array(state);
}
}
#endif

View File

@ -1,12 +1,10 @@
#ifdef PPU_CPP
void PPU::latch_counters() {
auto PPU::latch_counters() -> void {
regs.hcounter = cpu.hdot();
regs.vcounter = cpu.vcounter();
regs.counters_latched = true;
}
uint16 PPU::get_vram_address() {
auto PPU::get_vram_address() -> uint16 {
uint16 addr = regs.vram_addr;
switch(regs.vram_mapping) {
case 0: break; //direct mapping
@ -22,7 +20,7 @@ uint16 PPU::get_vram_address() {
//been validated on hardware, as has the edge case where the S-CPU MDR can be written if the
//write occurs during the very last clock cycle of vblank.
uint8 PPU::vram_mmio_read(uint16 addr) {
auto PPU::vram_mmio_read(uint16 addr) -> uint8 {
uint8 data;
if(regs.display_disabled == true) {
@ -51,7 +49,7 @@ uint8 PPU::vram_mmio_read(uint16 addr) {
return data;
}
void PPU::vram_mmio_write(uint16 addr, uint8 data) {
auto PPU::vram_mmio_write(uint16 addr, uint8 data) -> void {
if(regs.display_disabled == true) {
vram[addr] = data;
} else {
@ -79,7 +77,7 @@ void PPU::vram_mmio_write(uint16 addr, uint8 data) {
}
}
uint8 PPU::oam_mmio_read(uint16 addr) {
auto PPU::oam_mmio_read(uint16 addr) -> uint8 {
addr &= 0x03ff;
if(addr & 0x0200) addr &= 0x021f;
uint8 data;
@ -97,7 +95,7 @@ uint8 PPU::oam_mmio_read(uint16 addr) {
return data;
}
void PPU::oam_mmio_write(uint16 addr, uint8 data) {
auto PPU::oam_mmio_write(uint16 addr, uint8 data) -> void {
addr &= 0x03ff;
if(addr & 0x0200) addr &= 0x021f;
@ -117,7 +115,7 @@ void PPU::oam_mmio_write(uint16 addr, uint8 data) {
}
}
uint8 PPU::cgram_mmio_read(uint16 addr) {
auto PPU::cgram_mmio_read(uint16 addr) -> uint8 {
addr &= 0x01ff;
uint8 data;
@ -137,7 +135,7 @@ uint8 PPU::cgram_mmio_read(uint16 addr) {
return data;
}
void PPU::cgram_mmio_write(uint16 addr, uint8 data) {
auto PPU::cgram_mmio_write(uint16 addr, uint8 data) -> void {
addr &= 0x01ff;
if(addr & 1) data &= 0x7f;
@ -153,5 +151,3 @@ void PPU::cgram_mmio_write(uint16 addr, uint8 data) {
}
}
}
#endif

View File

@ -1,10 +1,10 @@
uint16 get_vram_address();
auto get_vram_address() -> uint16;
uint8 vram_mmio_read(uint16 addr);
void vram_mmio_write(uint16 addr, uint8 data);
auto vram_mmio_read(uint16 addr) -> uint8;
auto vram_mmio_write(uint16 addr, uint8 data) -> void;
uint8 oam_mmio_read(uint16 addr);
void oam_mmio_write(uint16 addr, uint8 data);
auto oam_mmio_read(uint16 addr) -> uint8;
auto oam_mmio_write(uint16 addr, uint8 data) -> void;
uint8 cgram_mmio_read(uint16 addr);
void cgram_mmio_write(uint16 addr, uint8 data);
auto cgram_mmio_read(uint16 addr) -> uint8;
auto cgram_mmio_write(uint16 addr, uint8 data) -> void;

View File

@ -1,7 +1,5 @@
#ifdef PPU_CPP
//INIDISP
void PPU::mmio_w2100(uint8 value) {
auto PPU::mmio_w2100(uint8 value) -> void {
if(regs.display_disabled == true && cpu.vcounter() == (!overscan() ? 225 : 240)) {
regs.oam_addr = regs.oam_baseaddr << 1;
regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127;
@ -12,14 +10,14 @@ void PPU::mmio_w2100(uint8 value) {
}
//OBSEL
void PPU::mmio_w2101(uint8 value) {
auto PPU::mmio_w2101(uint8 value) -> void {
regs.oam_basesize = (value >> 5) & 7;
regs.oam_nameselect = (value >> 3) & 3;
regs.oam_tdaddr = (value & 3) << 14;
}
//OAMADDL
void PPU::mmio_w2102(uint8 data) {
auto PPU::mmio_w2102(uint8 data) -> void {
regs.oam_baseaddr = (regs.oam_baseaddr & ~0xff) | (data << 0);
regs.oam_baseaddr &= 0x01ff;
regs.oam_addr = regs.oam_baseaddr << 1;
@ -27,7 +25,7 @@ void PPU::mmio_w2102(uint8 data) {
}
//OAMADDH
void PPU::mmio_w2103(uint8 data) {
auto PPU::mmio_w2103(uint8 data) -> void {
regs.oam_priority = !!(data & 0x80);
regs.oam_baseaddr = (regs.oam_baseaddr & 0xff) | (data << 8);
regs.oam_baseaddr &= 0x01ff;
@ -36,7 +34,7 @@ void PPU::mmio_w2103(uint8 data) {
}
//OAMDATA
void PPU::mmio_w2104(uint8 data) {
auto PPU::mmio_w2104(uint8 data) -> void {
if((regs.oam_addr & 1) == 0) regs.oam_latchdata = data;
if(regs.oam_addr & 0x0200) {
@ -52,7 +50,7 @@ void PPU::mmio_w2104(uint8 data) {
}
//BGMODE
void PPU::mmio_w2105(uint8 value) {
auto PPU::mmio_w2105(uint8 value) -> void {
regs.bg_tilesize[BG4] = !!(value & 0x80);
regs.bg_tilesize[BG3] = !!(value & 0x40);
regs.bg_tilesize[BG2] = !!(value & 0x20);
@ -62,7 +60,7 @@ void PPU::mmio_w2105(uint8 value) {
}
//MOSAIC
void PPU::mmio_w2106(uint8 value) {
auto PPU::mmio_w2106(uint8 value) -> void {
regs.mosaic_size = (value >> 4) & 15;
regs.mosaic_enabled[BG4] = !!(value & 0x08);
regs.mosaic_enabled[BG3] = !!(value & 0x04);
@ -71,43 +69,43 @@ void PPU::mmio_w2106(uint8 value) {
}
//BG1SC
void PPU::mmio_w2107(uint8 value) {
auto PPU::mmio_w2107(uint8 value) -> void {
regs.bg_scaddr[BG1] = (value & 0x7c) << 9;
regs.bg_scsize[BG1] = value & 3;
}
//BG2SC
void PPU::mmio_w2108(uint8 value) {
auto PPU::mmio_w2108(uint8 value) -> void {
regs.bg_scaddr[BG2] = (value & 0x7c) << 9;
regs.bg_scsize[BG2] = value & 3;
}
//BG3SC
void PPU::mmio_w2109(uint8 value) {
auto PPU::mmio_w2109(uint8 value) -> void {
regs.bg_scaddr[BG3] = (value & 0x7c) << 9;
regs.bg_scsize[BG3] = value & 3;
}
//BG4SC
void PPU::mmio_w210a(uint8 value) {
auto PPU::mmio_w210a(uint8 value) -> void {
regs.bg_scaddr[BG4] = (value & 0x7c) << 9;
regs.bg_scsize[BG4] = value & 3;
}
//BG12NBA
void PPU::mmio_w210b(uint8 value) {
auto PPU::mmio_w210b(uint8 value) -> void {
regs.bg_tdaddr[BG1] = (value & 0x07) << 13;
regs.bg_tdaddr[BG2] = (value & 0x70) << 9;
}
//BG34NBA
void PPU::mmio_w210c(uint8 value) {
auto PPU::mmio_w210c(uint8 value) -> void {
regs.bg_tdaddr[BG3] = (value & 0x07) << 13;
regs.bg_tdaddr[BG4] = (value & 0x70) << 9;
}
//BG1HOFS
void PPU::mmio_w210d(uint8 value) {
auto PPU::mmio_w210d(uint8 value) -> void {
regs.m7_hofs = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
@ -116,7 +114,7 @@ void PPU::mmio_w210d(uint8 value) {
}
//BG1VOFS
void PPU::mmio_w210e(uint8 value) {
auto PPU::mmio_w210e(uint8 value) -> void {
regs.m7_vofs = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
@ -125,43 +123,43 @@ void PPU::mmio_w210e(uint8 value) {
}
//BG2HOFS
void PPU::mmio_w210f(uint8 value) {
auto PPU::mmio_w210f(uint8 value) -> void {
regs.bg_hofs[BG2] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG2] >> 8) & 7);
regs.bg_ofslatch = value;
}
//BG2VOFS
void PPU::mmio_w2110(uint8 value) {
auto PPU::mmio_w2110(uint8 value) -> void {
regs.bg_vofs[BG2] = (value << 8) | (regs.bg_ofslatch);
regs.bg_ofslatch = value;
}
//BG3HOFS
void PPU::mmio_w2111(uint8 value) {
auto PPU::mmio_w2111(uint8 value) -> void {
regs.bg_hofs[BG3] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG3] >> 8) & 7);
regs.bg_ofslatch = value;
}
//BG3VOFS
void PPU::mmio_w2112(uint8 value) {
auto PPU::mmio_w2112(uint8 value) -> void {
regs.bg_vofs[BG3] = (value << 8) | (regs.bg_ofslatch);
regs.bg_ofslatch = value;
}
//BG4HOFS
void PPU::mmio_w2113(uint8 value) {
auto PPU::mmio_w2113(uint8 value) -> void {
regs.bg_hofs[BG4] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG4] >> 8) & 7);
regs.bg_ofslatch = value;
}
//BG4VOFS
void PPU::mmio_w2114(uint8 value) {
auto PPU::mmio_w2114(uint8 value) -> void {
regs.bg_vofs[BG4] = (value << 8) | (regs.bg_ofslatch);
regs.bg_ofslatch = value;
}
//VMAIN
void PPU::mmio_w2115(uint8 value) {
auto PPU::mmio_w2115(uint8 value) -> void {
regs.vram_incmode = !!(value & 0x80);
regs.vram_mapping = (value >> 2) & 3;
switch(value & 3) {
@ -173,7 +171,7 @@ void PPU::mmio_w2115(uint8 value) {
}
//VMADDL
void PPU::mmio_w2116(uint8 value) {
auto PPU::mmio_w2116(uint8 value) -> void {
regs.vram_addr = (regs.vram_addr & 0xff00) | value;
uint16 addr = get_vram_address();
regs.vram_readbuffer = vram_mmio_read(addr + 0);
@ -181,7 +179,7 @@ void PPU::mmio_w2116(uint8 value) {
}
//VMADDH
void PPU::mmio_w2117(uint8 value) {
auto PPU::mmio_w2117(uint8 value) -> void {
regs.vram_addr = (value << 8) | (regs.vram_addr & 0x00ff);
uint16 addr = get_vram_address();
regs.vram_readbuffer = vram_mmio_read(addr + 0);
@ -189,8 +187,8 @@ void PPU::mmio_w2117(uint8 value) {
}
//VMDATAL
void PPU::mmio_w2118(uint8 value) {
uint16 addr = get_vram_address();
auto PPU::mmio_w2118(uint8 value) -> void {
uint16 addr = get_vram_address();
vram_mmio_write(addr, value);
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
@ -202,8 +200,8 @@ uint16 addr = get_vram_address();
}
//VMDATAH
void PPU::mmio_w2119(uint8 value) {
uint16 addr = get_vram_address() + 1;
auto PPU::mmio_w2119(uint8 value) -> void {
uint16 addr = get_vram_address() + 1;
vram_mmio_write(addr, value);
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
@ -215,50 +213,50 @@ uint16 addr = get_vram_address() + 1;
}
//M7SEL
void PPU::mmio_w211a(uint8 value) {
auto PPU::mmio_w211a(uint8 value) -> void {
regs.mode7_repeat = (value >> 6) & 3;
regs.mode7_vflip = !!(value & 0x02);
regs.mode7_hflip = !!(value & 0x01);
}
//M7A
void PPU::mmio_w211b(uint8 value) {
auto PPU::mmio_w211b(uint8 value) -> void {
regs.m7a = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
}
//M7B
void PPU::mmio_w211c(uint8 value) {
auto PPU::mmio_w211c(uint8 value) -> void {
regs.m7b = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
}
//M7C
void PPU::mmio_w211d(uint8 value) {
auto PPU::mmio_w211d(uint8 value) -> void {
regs.m7c = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
}
//M7D
void PPU::mmio_w211e(uint8 value) {
auto PPU::mmio_w211e(uint8 value) -> void {
regs.m7d = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
}
//M7X
void PPU::mmio_w211f(uint8 value) {
auto PPU::mmio_w211f(uint8 value) -> void {
regs.m7x = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
}
//M7Y
void PPU::mmio_w2120(uint8 value) {
auto PPU::mmio_w2120(uint8 value) -> void {
regs.m7y = (value << 8) | regs.m7_latch;
regs.m7_latch = value;
}
//CGADD
void PPU::mmio_w2121(uint8 value) {
auto PPU::mmio_w2121(uint8 value) -> void {
regs.cgram_addr = value << 1;
}
@ -270,7 +268,7 @@ void PPU::mmio_w2121(uint8 value) {
//anomie indicates writes to CGDATA work the same
//as writes to OAMDATA's low table. need to verify
//this on hardware.
void PPU::mmio_w2122(uint8 value) {
auto PPU::mmio_w2122(uint8 value) -> void {
if(!(regs.cgram_addr & 1)) {
regs.cgram_latchdata = value;
} else {
@ -282,7 +280,7 @@ void PPU::mmio_w2122(uint8 value) {
}
//W12SEL
void PPU::mmio_w2123(uint8 value) {
auto PPU::mmio_w2123(uint8 value) -> void {
regs.window2_enabled[BG2] = !!(value & 0x80);
regs.window2_invert [BG2] = !!(value & 0x40);
regs.window1_enabled[BG2] = !!(value & 0x20);
@ -294,7 +292,7 @@ void PPU::mmio_w2123(uint8 value) {
}
//W34SEL
void PPU::mmio_w2124(uint8 value) {
auto PPU::mmio_w2124(uint8 value) -> void {
regs.window2_enabled[BG4] = !!(value & 0x80);
regs.window2_invert [BG4] = !!(value & 0x40);
regs.window1_enabled[BG4] = !!(value & 0x20);
@ -306,7 +304,7 @@ void PPU::mmio_w2124(uint8 value) {
}
//WOBJSEL
void PPU::mmio_w2125(uint8 value) {
auto PPU::mmio_w2125(uint8 value) -> void {
regs.window2_enabled[COL] = !!(value & 0x80);
regs.window2_invert [COL] = !!(value & 0x40);
regs.window1_enabled[COL] = !!(value & 0x20);
@ -318,27 +316,27 @@ void PPU::mmio_w2125(uint8 value) {
}
//WH0
void PPU::mmio_w2126(uint8 value) {
auto PPU::mmio_w2126(uint8 value) -> void {
regs.window1_left = value;
}
//WH1
void PPU::mmio_w2127(uint8 value) {
auto PPU::mmio_w2127(uint8 value) -> void {
regs.window1_right = value;
}
//WH2
void PPU::mmio_w2128(uint8 value) {
auto PPU::mmio_w2128(uint8 value) -> void {
regs.window2_left = value;
}
//WH3
void PPU::mmio_w2129(uint8 value) {
auto PPU::mmio_w2129(uint8 value) -> void {
regs.window2_right = value;
}
//WBGLOG
void PPU::mmio_w212a(uint8 value) {
auto PPU::mmio_w212a(uint8 value) -> void {
regs.window_mask[BG4] = (value >> 6) & 3;
regs.window_mask[BG3] = (value >> 4) & 3;
regs.window_mask[BG2] = (value >> 2) & 3;
@ -346,13 +344,13 @@ void PPU::mmio_w212a(uint8 value) {
}
//WOBJLOG
void PPU::mmio_w212b(uint8 value) {
auto PPU::mmio_w212b(uint8 value) -> void {
regs.window_mask[COL] = (value >> 2) & 3;
regs.window_mask[OAM] = (value ) & 3;
}
//TM
void PPU::mmio_w212c(uint8 value) {
auto PPU::mmio_w212c(uint8 value) -> void {
regs.bg_enabled[OAM] = !!(value & 0x10);
regs.bg_enabled[BG4] = !!(value & 0x08);
regs.bg_enabled[BG3] = !!(value & 0x04);
@ -361,7 +359,7 @@ void PPU::mmio_w212c(uint8 value) {
}
//TS
void PPU::mmio_w212d(uint8 value) {
auto PPU::mmio_w212d(uint8 value) -> void {
regs.bgsub_enabled[OAM] = !!(value & 0x10);
regs.bgsub_enabled[BG4] = !!(value & 0x08);
regs.bgsub_enabled[BG3] = !!(value & 0x04);
@ -370,7 +368,7 @@ void PPU::mmio_w212d(uint8 value) {
}
//TMW
void PPU::mmio_w212e(uint8 value) {
auto PPU::mmio_w212e(uint8 value) -> void {
regs.window_enabled[OAM] = !!(value & 0x10);
regs.window_enabled[BG4] = !!(value & 0x08);
regs.window_enabled[BG3] = !!(value & 0x04);
@ -379,7 +377,7 @@ void PPU::mmio_w212e(uint8 value) {
}
//TSW
void PPU::mmio_w212f(uint8 value) {
auto PPU::mmio_w212f(uint8 value) -> void {
regs.sub_window_enabled[OAM] = !!(value & 0x10);
regs.sub_window_enabled[BG4] = !!(value & 0x08);
regs.sub_window_enabled[BG3] = !!(value & 0x04);
@ -388,7 +386,7 @@ void PPU::mmio_w212f(uint8 value) {
}
//CGWSEL
void PPU::mmio_w2130(uint8 value) {
auto PPU::mmio_w2130(uint8 value) -> void {
regs.color_mask = (value >> 6) & 3;
regs.colorsub_mask = (value >> 4) & 3;
regs.addsub_mode = !!(value & 0x02);
@ -396,7 +394,7 @@ void PPU::mmio_w2130(uint8 value) {
}
//CGADDSUB
void PPU::mmio_w2131(uint8 value) {
auto PPU::mmio_w2131(uint8 value) -> void {
regs.color_mode = !!(value & 0x80);
regs.color_halve = !!(value & 0x40);
regs.color_enabled[BACK] = !!(value & 0x20);
@ -408,7 +406,7 @@ void PPU::mmio_w2131(uint8 value) {
}
//COLDATA
void PPU::mmio_w2132(uint8 value) {
auto PPU::mmio_w2132(uint8 value) -> void {
if(value & 0x80) regs.color_b = value & 0x1f;
if(value & 0x40) regs.color_g = value & 0x1f;
if(value & 0x20) regs.color_r = value & 0x1f;
@ -419,7 +417,7 @@ void PPU::mmio_w2132(uint8 value) {
}
//SETINI
void PPU::mmio_w2133(uint8 value) {
auto PPU::mmio_w2133(uint8 value) -> void {
regs.mode7_extbg = !!(value & 0x40);
regs.pseudo_hires = !!(value & 0x08);
regs.overscan = !!(value & 0x04);
@ -431,31 +429,31 @@ void PPU::mmio_w2133(uint8 value) {
}
//MPYL
uint8 PPU::mmio_r2134() {
uint32 r;
auto PPU::mmio_r2134() -> uint8 {
uint32 r;
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
regs.ppu1_mdr = r;
return regs.ppu1_mdr;
}
//MPYM
uint8 PPU::mmio_r2135() {
uint32 r;
auto PPU::mmio_r2135() -> uint8 {
uint32 r;
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
regs.ppu1_mdr = r >> 8;
return regs.ppu1_mdr;
}
//MPYH
uint8 PPU::mmio_r2136() {
uint32 r;
auto PPU::mmio_r2136() -> uint8 {
uint32 r;
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
regs.ppu1_mdr = r >> 16;
return regs.ppu1_mdr;
}
//SLHV
uint8 PPU::mmio_r2137() {
auto PPU::mmio_r2137() -> uint8 {
if(cpu.pio() & 0x80) {
latch_counters();
}
@ -463,7 +461,7 @@ uint8 PPU::mmio_r2137() {
}
//OAMDATAREAD
uint8 PPU::mmio_r2138() {
auto PPU::mmio_r2138() -> uint8 {
regs.ppu1_mdr = oam_mmio_read(regs.oam_addr);
regs.oam_addr++;
@ -474,8 +472,8 @@ uint8 PPU::mmio_r2138() {
}
//VMDATALREAD
uint8 PPU::mmio_r2139() {
uint16 addr = get_vram_address();
auto PPU::mmio_r2139() -> uint8 {
uint16 addr = get_vram_address();
regs.ppu1_mdr = regs.vram_readbuffer;
if(regs.vram_incmode == 0) {
addr &= 0xfffe;
@ -487,8 +485,8 @@ uint16 addr = get_vram_address();
}
//VMDATAHREAD
uint8 PPU::mmio_r213a() {
uint16 addr = get_vram_address() + 1;
auto PPU::mmio_r213a() -> uint8 {
uint16 addr = get_vram_address() + 1;
regs.ppu1_mdr = regs.vram_readbuffer >> 8;
if(regs.vram_incmode == 1) {
addr &= 0xfffe;
@ -503,7 +501,7 @@ uint16 addr = get_vram_address() + 1;
//note: CGRAM palette data is 15-bits (0,bbbbb,ggggg,rrrrr)
//therefore, the high byte read from each color does not
//update bit 7 of the PPU2 MDR.
uint8 PPU::mmio_r213b() {
auto PPU::mmio_r213b() -> uint8 {
if(!(regs.cgram_addr & 1)) {
regs.ppu2_mdr = cgram_mmio_read(regs.cgram_addr) & 0xff;
} else {
@ -516,7 +514,7 @@ uint8 PPU::mmio_r213b() {
}
//OPHCT
uint8 PPU::mmio_r213c() {
auto PPU::mmio_r213c() -> uint8 {
if(!regs.latch_hcounter) {
regs.ppu2_mdr = regs.hcounter & 0xff;
} else {
@ -528,7 +526,7 @@ uint8 PPU::mmio_r213c() {
}
//OPVCT
uint8 PPU::mmio_r213d() {
auto PPU::mmio_r213d() -> uint8 {
if(!regs.latch_vcounter) {
regs.ppu2_mdr = regs.vcounter & 0xff;
} else {
@ -540,8 +538,8 @@ uint8 PPU::mmio_r213d() {
}
//STAT77
uint8 PPU::mmio_r213e() {
uint8 r = 0x00;
auto PPU::mmio_r213e() -> uint8 {
uint8 r = 0x00;
r |= (regs.time_over) ? 0x80 : 0x00;
r |= (regs.range_over) ? 0x40 : 0x00;
r |= (regs.ppu1_mdr & 0x10);
@ -551,8 +549,8 @@ uint8 r = 0x00;
}
//STAT78
uint8 PPU::mmio_r213f() {
uint8 r = 0x00;
auto PPU::mmio_r213f() -> uint8 {
uint8 r = 0x00;
regs.latch_hcounter = 0;
regs.latch_vcounter = 0;
@ -570,8 +568,8 @@ uint8 r = 0x00;
return regs.ppu2_mdr;
}
uint8 PPU::mmio_read(unsigned addr) {
cpu.synchronize_ppu();
auto PPU::mmio_read(uint addr) -> uint8 {
cpu.synchronizePPU();
switch(addr & 0xffff) {
case 0x2104:
@ -609,8 +607,8 @@ uint8 PPU::mmio_read(unsigned addr) {
return cpu.regs.mdr;
}
void PPU::mmio_write(unsigned addr, uint8 data) {
cpu.synchronize_ppu();
auto PPU::mmio_write(uint addr, uint8 data) -> void {
cpu.synchronizePPU();
switch(addr & 0xffff) {
case 0x2100: return mmio_w2100(data); //INIDISP
@ -667,5 +665,3 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
case 0x2133: return mmio_w2133(data); //SETINI
}
}
#endif

View File

@ -130,73 +130,73 @@ struct {
uint16 oam_itemcount, oam_tilecount;
} regs;
void mmio_w2100(uint8 value); //INIDISP
void mmio_w2101(uint8 value); //OBSEL
void mmio_w2102(uint8 value); //OAMADDL
void mmio_w2103(uint8 value); //OAMADDH
void mmio_w2104(uint8 value); //OAMDATA
void mmio_w2105(uint8 value); //BGMODE
void mmio_w2106(uint8 value); //MOSAIC
void mmio_w2107(uint8 value); //BG1SC
void mmio_w2108(uint8 value); //BG2SC
void mmio_w2109(uint8 value); //BG3SC
void mmio_w210a(uint8 value); //BG4SC
void mmio_w210b(uint8 value); //BG12NBA
void mmio_w210c(uint8 value); //BG34NBA
void mmio_w210d(uint8 value); //BG1HOFS
void mmio_w210e(uint8 value); //BG1VOFS
void mmio_w210f(uint8 value); //BG2HOFS
void mmio_w2110(uint8 value); //BG2VOFS
void mmio_w2111(uint8 value); //BG3HOFS
void mmio_w2112(uint8 value); //BG3VOFS
void mmio_w2113(uint8 value); //BG4HOFS
void mmio_w2114(uint8 value); //BG4VOFS
void mmio_w2115(uint8 value); //VMAIN
void mmio_w2116(uint8 value); //VMADDL
void mmio_w2117(uint8 value); //VMADDH
void mmio_w2118(uint8 value); //VMDATAL
void mmio_w2119(uint8 value); //VMDATAH
void mmio_w211a(uint8 value); //M7SEL
void mmio_w211b(uint8 value); //M7A
void mmio_w211c(uint8 value); //M7B
void mmio_w211d(uint8 value); //M7C
void mmio_w211e(uint8 value); //M7D
void mmio_w211f(uint8 value); //M7X
void mmio_w2120(uint8 value); //M7Y
void mmio_w2121(uint8 value); //CGADD
void mmio_w2122(uint8 value); //CGDATA
void mmio_w2123(uint8 value); //W12SEL
void mmio_w2124(uint8 value); //W34SEL
void mmio_w2125(uint8 value); //WOBJSEL
void mmio_w2126(uint8 value); //WH0
void mmio_w2127(uint8 value); //WH1
void mmio_w2128(uint8 value); //WH2
void mmio_w2129(uint8 value); //WH3
void mmio_w212a(uint8 value); //WBGLOG
void mmio_w212b(uint8 value); //WOBJLOG
void mmio_w212c(uint8 value); //TM
void mmio_w212d(uint8 value); //TS
void mmio_w212e(uint8 value); //TMW
void mmio_w212f(uint8 value); //TSW
void mmio_w2130(uint8 value); //CGWSEL
void mmio_w2131(uint8 value); //CGADDSUB
void mmio_w2132(uint8 value); //COLDATA
void mmio_w2133(uint8 value); //SETINI
auto mmio_w2100(uint8 value) -> void; //INIDISP
auto mmio_w2101(uint8 value) -> void; //OBSEL
auto mmio_w2102(uint8 value) -> void; //OAMADDL
auto mmio_w2103(uint8 value) -> void; //OAMADDH
auto mmio_w2104(uint8 value) -> void; //OAMDATA
auto mmio_w2105(uint8 value) -> void; //BGMODE
auto mmio_w2106(uint8 value) -> void; //MOSAIC
auto mmio_w2107(uint8 value) -> void; //BG1SC
auto mmio_w2108(uint8 value) -> void; //BG2SC
auto mmio_w2109(uint8 value) -> void; //BG3SC
auto mmio_w210a(uint8 value) -> void; //BG4SC
auto mmio_w210b(uint8 value) -> void; //BG12NBA
auto mmio_w210c(uint8 value) -> void; //BG34NBA
auto mmio_w210d(uint8 value) -> void; //BG1HOFS
auto mmio_w210e(uint8 value) -> void; //BG1VOFS
auto mmio_w210f(uint8 value) -> void; //BG2HOFS
auto mmio_w2110(uint8 value) -> void; //BG2VOFS
auto mmio_w2111(uint8 value) -> void; //BG3HOFS
auto mmio_w2112(uint8 value) -> void; //BG3VOFS
auto mmio_w2113(uint8 value) -> void; //BG4HOFS
auto mmio_w2114(uint8 value) -> void; //BG4VOFS
auto mmio_w2115(uint8 value) -> void; //VMAIN
auto mmio_w2116(uint8 value) -> void; //VMADDL
auto mmio_w2117(uint8 value) -> void; //VMADDH
auto mmio_w2118(uint8 value) -> void; //VMDATAL
auto mmio_w2119(uint8 value) -> void; //VMDATAH
auto mmio_w211a(uint8 value) -> void; //M7SEL
auto mmio_w211b(uint8 value) -> void; //M7A
auto mmio_w211c(uint8 value) -> void; //M7B
auto mmio_w211d(uint8 value) -> void; //M7C
auto mmio_w211e(uint8 value) -> void; //M7D
auto mmio_w211f(uint8 value) -> void; //M7X
auto mmio_w2120(uint8 value) -> void; //M7Y
auto mmio_w2121(uint8 value) -> void; //CGADD
auto mmio_w2122(uint8 value) -> void; //CGDATA
auto mmio_w2123(uint8 value) -> void; //W12SEL
auto mmio_w2124(uint8 value) -> void; //W34SEL
auto mmio_w2125(uint8 value) -> void; //WOBJSEL
auto mmio_w2126(uint8 value) -> void; //WH0
auto mmio_w2127(uint8 value) -> void; //WH1
auto mmio_w2128(uint8 value) -> void; //WH2
auto mmio_w2129(uint8 value) -> void; //WH3
auto mmio_w212a(uint8 value) -> void; //WBGLOG
auto mmio_w212b(uint8 value) -> void; //WOBJLOG
auto mmio_w212c(uint8 value) -> void; //TM
auto mmio_w212d(uint8 value) -> void; //TS
auto mmio_w212e(uint8 value) -> void; //TMW
auto mmio_w212f(uint8 value) -> void; //TSW
auto mmio_w2130(uint8 value) -> void; //CGWSEL
auto mmio_w2131(uint8 value) -> void; //CGADDSUB
auto mmio_w2132(uint8 value) -> void; //COLDATA
auto mmio_w2133(uint8 value) -> void; //SETINI
uint8 mmio_r2134(); //MPYL
uint8 mmio_r2135(); //MPYM
uint8 mmio_r2136(); //MPYH
uint8 mmio_r2137(); //SLHV
uint8 mmio_r2138(); //OAMDATAREAD
uint8 mmio_r2139(); //VMDATALREAD
uint8 mmio_r213a(); //VMDATAHREAD
uint8 mmio_r213b(); //CGDATAREAD
uint8 mmio_r213c(); //OPHCT
uint8 mmio_r213d(); //OPVCT
uint8 mmio_r213e(); //STAT77
uint8 mmio_r213f(); //STAT78
auto mmio_r2134() -> uint8; //MPYL
auto mmio_r2135() -> uint8; //MPYM
auto mmio_r2136() -> uint8; //MPYH
auto mmio_r2137() -> uint8; //SLHV
auto mmio_r2138() -> uint8; //OAMDATAREAD
auto mmio_r2139() -> uint8; //VMDATALREAD
auto mmio_r213a() -> uint8; //VMDATAHREAD
auto mmio_r213b() -> uint8; //CGDATAREAD
auto mmio_r213c() -> uint8; //OPHCT
auto mmio_r213d() -> uint8; //OPVCT
auto mmio_r213e() -> uint8; //STAT77
auto mmio_r213f() -> uint8; //STAT78
uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data);
auto mmio_read(uint addr) -> uint8;
auto mmio_write(uint addr, uint8 data) -> void;
void latch_counters();
auto latch_counters() -> void;

View File

@ -1,6 +1,5 @@
#include <sfc/sfc.hpp>
#define PPU_CPP
namespace SuperFamicom {
PPU ppu;
@ -10,11 +9,44 @@ PPU ppu;
#include "render/render.cpp"
#include "serialization.cpp"
void PPU::step(unsigned clocks) {
PPU::PPU() {
surface = new uint32[512 * 512];
output = surface + 16 * 512;
alloc_tiledata_cache();
for(uint l : range(16)) {
for(uint i : range(4096)) {
mosaic_table[l][i] = (i / (l + 1)) * (l + 1);
}
}
layer_enabled[BG1][0] = true;
layer_enabled[BG1][1] = true;
layer_enabled[BG2][0] = true;
layer_enabled[BG2][1] = true;
layer_enabled[BG3][0] = true;
layer_enabled[BG3][1] = true;
layer_enabled[BG4][0] = true;
layer_enabled[BG4][1] = true;
layer_enabled[OAM][0] = true;
layer_enabled[OAM][1] = true;
layer_enabled[OAM][2] = true;
layer_enabled[OAM][3] = true;
frameskip = 0;
framecounter = 0;
}
PPU::~PPU() {
delete[] surface;
free_tiledata_cache();
}
auto PPU::step(uint clocks) -> void {
clock += clocks;
}
void PPU::synchronize_cpu() {
auto PPU::synchronizeCPU() -> void {
if(CPU::Threaded == true) {
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
} else {
@ -22,9 +54,9 @@ void PPU::synchronize_cpu() {
}
}
void PPU::Enter() { ppu.enter(); }
auto PPU::Enter() -> void { ppu.enter(); }
void PPU::enter() {
auto PPU::enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
@ -67,13 +99,13 @@ void PPU::enter() {
}
}
void PPU::add_clocks(unsigned clocks) {
auto PPU::add_clocks(uint clocks) -> void {
tick(clocks);
step(clocks);
synchronize_cpu();
synchronizeCPU();
}
void PPU::scanline() {
auto PPU::scanline() -> void {
line = vcounter();
if(line == 0) {
@ -98,7 +130,7 @@ void PPU::scanline() {
}
}
void PPU::render_scanline() {
auto PPU::render_scanline() -> void {
if(line >= 1 && line < (!overscan() ? 225 : 240)) {
if(framecounter) return;
render_line_oam_rto();
@ -106,7 +138,7 @@ void PPU::render_scanline() {
}
}
void PPU::frame() {
auto PPU::frame() -> void {
system.frame();
if(field() == 0) {
@ -117,15 +149,15 @@ void PPU::frame() {
framecounter = (frameskip == 0 ? 0 : (framecounter + 1) % frameskip);
}
void PPU::enable() {
function<uint8 (unsigned)> reader = {&PPU::mmio_read, (PPU*)&ppu};
function<void (unsigned, uint8)> writer = {&PPU::mmio_write, (PPU*)&ppu};
auto PPU::enable() -> void {
function<uint8 (uint)> reader = {&PPU::mmio_read, (PPU*)&ppu};
function<void (uint, uint8)> writer = {&PPU::mmio_write, (PPU*)&ppu};
bus.map(reader, writer, 0x00, 0x3f, 0x2100, 0x213f);
bus.map(reader, writer, 0x80, 0xbf, 0x2100, 0x213f);
}
void PPU::power() {
auto PPU::power() -> void {
for(auto& n : vram) n = 0x00;
for(auto& n : oam) n = 0x00;
for(auto& n : cgram) n = 0x00;
@ -340,8 +372,8 @@ void PPU::power() {
reset();
}
void PPU::reset() {
create(Enter, system.cpu_frequency());
auto PPU::reset() -> void {
create(Enter, system.cpuFrequency());
PPUcounter::reset();
memset(surface, 0, 512 * 512 * sizeof(uint32));
@ -368,7 +400,7 @@ void PPU::reset() {
regs.bg_y[3] = 0;
}
void PPU::layer_enable(unsigned layer, unsigned priority, bool enable) {
auto PPU::layer_enable(uint layer, uint priority, bool enable) -> void {
switch(layer * 4 + priority) {
case 0: layer_enabled[BG1][0] = enable; break;
case 1: layer_enabled[BG1][1] = enable; break;
@ -385,42 +417,9 @@ void PPU::layer_enable(unsigned layer, unsigned priority, bool enable) {
}
}
void PPU::set_frameskip(unsigned frameskip_) {
auto PPU::set_frameskip(uint frameskip_) -> void {
frameskip = frameskip_;
framecounter = 0;
}
PPU::PPU() {
surface = new uint32[512 * 512];
output = surface + 16 * 512;
alloc_tiledata_cache();
for(unsigned l = 0; l < 16; l++) {
for(unsigned i = 0; i < 4096; i++) {
mosaic_table[l][i] = (i / (l + 1)) * (l + 1);
}
}
layer_enabled[BG1][0] = true;
layer_enabled[BG1][1] = true;
layer_enabled[BG2][0] = true;
layer_enabled[BG2][1] = true;
layer_enabled[BG3][0] = true;
layer_enabled[BG3][1] = true;
layer_enabled[BG4][0] = true;
layer_enabled[BG4][1] = true;
layer_enabled[OAM][0] = true;
layer_enabled[OAM][1] = true;
layer_enabled[OAM][2] = true;
layer_enabled[OAM][3] = true;
frameskip = 0;
framecounter = 0;
}
PPU::~PPU() {
delete[] surface;
free_tiledata_cache();
}
}

View File

@ -1,31 +1,55 @@
struct PPU : Thread, public PPUcounter {
uint8 vram[128 * 1024];
uint8 oam[544];
uint8 cgram[512];
enum : bool { Threaded = true };
alwaysinline void step(unsigned clocks);
alwaysinline void synchronize_cpu();
PPU();
~PPU();
alwaysinline auto step(uint clocks) -> void;
alwaysinline auto synchronizeCPU() -> void;
#include "memory/memory.hpp"
#include "mmio/mmio.hpp"
#include "render/render.hpp"
static auto Enter() -> void;
auto add_clocks(uint clocks) -> void;
alwaysinline auto interlace() const -> bool { return display.interlace; }
alwaysinline auto overscan() const -> bool { return display.overscan; }
alwaysinline auto hires() const -> bool { return (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6); }
auto render_line() -> void;
auto update_oam_status() -> void;
auto scanline() -> void;
auto render_scanline() -> void;
auto frame() -> void;
auto enter() -> void;
auto enable() -> void;
auto power() -> void;
auto reset() -> void;
auto layer_enable(uint layer, uint priority, bool enable) -> void;
auto set_frameskip(uint frameskip) -> void;
auto serialize(serializer&) -> void;
enum : uint { NTSC = 0, PAL = 1 };
enum : uint { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 };
enum : uint { SC_32x32 = 0, SC_64x32 = 1, SC_32x64 = 2, SC_64x64 = 3 };
uint8 vram[128 * 1024];
uint8 oam[544];
uint8 cgram[512];
uint32* surface;
uint32* output;
unsigned ppu1_version = 1;
unsigned ppu2_version = 3;
static void Enter();
void add_clocks(unsigned clocks);
uint ppu1_version = 1;
uint ppu2_version = 3;
uint8 region;
unsigned line;
enum { NTSC = 0, PAL = 1 };
enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 };
enum { SC_32x32 = 0, SC_64x32 = 1, SC_32x64 = 2, SC_64x64 = 3 };
uint line;
struct {
bool interlace;
@ -45,32 +69,10 @@ struct PPU : Thread, public PPUcounter {
uint16 m7a, m7b, m7c, m7d, m7x, m7y;
} cache;
alwaysinline bool interlace() const { return display.interlace; }
alwaysinline bool overscan() const { return display.overscan; }
alwaysinline bool hires() const { return (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6); }
uint16 mosaic_table[16][4096];
void render_line();
void update_oam_status();
//required functions
void scanline();
void render_scanline();
void frame();
void enter();
void enable();
void power();
void reset();
bool layer_enabled[5][4];
void layer_enable(unsigned layer, unsigned priority, bool enable);
unsigned frameskip;
unsigned framecounter;
void set_frameskip(unsigned frameskip);
void serialize(serializer&);
PPU();
~PPU();
uint frameskip;
uint framecounter;
};
extern PPU ppu;

View File

@ -1,19 +1,17 @@
#ifdef PPU_CPP
//color addition / subtraction
//thanks go to blargg for the optimized algorithms
inline uint16 PPU::addsub(uint32 x, uint32 y, bool halve) {
inline auto PPU::addsub(uint32 x, uint32 y, bool halve) -> uint16 {
if(!regs.color_mode) {
if(!halve) {
unsigned sum = x + y;
unsigned carry = (sum - ((x ^ y) & 0x0421)) & 0x8420;
uint sum = x + y;
uint carry = (sum - ((x ^ y) & 0x0421)) & 0x8420;
return (sum - carry) | (carry - (carry >> 5));
} else {
return (x + y - ((x ^ y) & 0x0421)) >> 1;
}
} else {
unsigned diff = x - y + 0x8420;
unsigned borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420;
uint diff = x - y + 0x8420;
uint borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420;
if(!halve) {
return (diff - borrow) & (borrow - (borrow >> 5));
} else {
@ -21,5 +19,3 @@ inline uint16 PPU::addsub(uint32 x, uint32 y, bool halve) {
}
}
}
#endif

View File

@ -1,7 +1,5 @@
#ifdef PPU_CPP
//called once at the start of every rendered scanline
void PPU::update_bg_info() {
auto PPU::update_bg_info() -> void {
const unsigned hires = (regs.bg_mode == 5 || regs.bg_mode == 6);
const unsigned width = (!hires ? 256 : 512);
@ -22,8 +20,8 @@ void PPU::update_bg_info() {
}
}
template<unsigned bg>
uint16 PPU::bg_get_tile(uint16 x, uint16 y) {
template<uint bg>
auto PPU::bg_get_tile(uint16 x, uint16 y) -> uint16 {
x = (x & bg_info[bg].mx) >> bg_info[bg].tw;
y = (y & bg_info[bg].my) >> bg_info[bg].th;
@ -51,8 +49,8 @@ uint16 PPU::bg_get_tile(uint16 x, uint16 y) {
pixel_cache[x].ce_sub = false; \
}
template<unsigned mode, unsigned bg, unsigned color_depth>
void PPU::render_line_bg(uint8 pri0_pos, uint8 pri1_pos) {
template<uint mode, uint bg, uint color_depth>
auto PPU::render_line_bg(uint8 pri0_pos, uint8 pri1_pos) -> void {
if(layer_enabled[bg][0] == false) pri0_pos = 0;
if(layer_enabled[bg][1] == false) pri1_pos = 0;
if(pri0_pos + pri1_pos == 0) return;
@ -205,5 +203,3 @@ void PPU::render_line_bg(uint8 pri0_pos, uint8 pri1_pos) {
#undef setpixel_main
#undef setpixel_sub
#endif

View File

@ -1,5 +1,3 @@
#ifdef PPU_CPP
#define render_bg_tile_line_2bpp(mask) \
col = !!(d0 & mask) << 0; \
col += !!(d1 & mask) << 1; \
@ -23,8 +21,8 @@
col += !!(d7 & mask) << 7; \
*dest++ = col
template<unsigned color_depth>
void PPU::render_bg_tile(uint16 tile_num) {
template<uint color_depth>
auto PPU::render_bg_tile(uint16 tile_num) -> void {
uint8 col, d0, d1, d2, d3, d4, d5, d6, d7;
if(color_depth == COLORDEPTH_4) {
@ -100,7 +98,7 @@ void PPU::render_bg_tile(uint16 tile_num) {
#undef render_bg_tile_line_4bpp
#undef render_bg_tile_line_8bpp
void PPU::flush_pixel_cache() {
auto PPU::flush_pixel_cache() -> void {
uint16 main = get_palette(0);
uint16 sub = (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6)
? main
@ -119,7 +117,7 @@ void PPU::flush_pixel_cache() {
} while(i--);
}
void PPU::alloc_tiledata_cache() {
auto PPU::alloc_tiledata_cache() -> void {
bg_tiledata[TILE_2BIT] = new uint8_t[262144]();
bg_tiledata[TILE_4BIT] = new uint8_t[131072]();
bg_tiledata[TILE_8BIT] = new uint8_t[ 65536]();
@ -129,13 +127,13 @@ void PPU::alloc_tiledata_cache() {
}
//marks all tiledata cache entries as dirty
void PPU::flush_tiledata_cache() {
auto PPU::flush_tiledata_cache() -> void {
for(unsigned i = 0; i < 4096; i++) bg_tiledata_state[TILE_2BIT][i] = 1;
for(unsigned i = 0; i < 2048; i++) bg_tiledata_state[TILE_4BIT][i] = 1;
for(unsigned i = 0; i < 1024; i++) bg_tiledata_state[TILE_8BIT][i] = 1;
}
void PPU::free_tiledata_cache() {
auto PPU::free_tiledata_cache() -> void {
delete[] bg_tiledata[TILE_2BIT];
delete[] bg_tiledata[TILE_4BIT];
delete[] bg_tiledata[TILE_8BIT];
@ -143,5 +141,3 @@ void PPU::free_tiledata_cache() {
delete[] bg_tiledata_state[TILE_4BIT];
delete[] bg_tiledata_state[TILE_8BIT];
}
#endif

View File

@ -1,20 +1,18 @@
#ifdef PPU_CPP
inline uint16 PPU::get_palette(uint8 index) {
const unsigned addr = index << 1;
inline auto PPU::get_palette(uint8 index) -> uint16 {
const uint addr = index << 1;
return cgram[addr] + (cgram[addr + 1] << 8);
}
//p = 00000bgr <palette data>
//t = BBGGGRRR <tilemap data>
//r = 0BBb00GGGg0RRRr0 <return data>
inline uint16 PPU::get_direct_color(uint8 p, uint8 t) {
inline auto PPU::get_direct_color(uint8 p, uint8 t) -> uint16 {
return ((t & 7) << 2) | ((p & 1) << 1) |
(((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) |
((t >> 6) << 13) | ((p >> 2) << 12);
}
inline uint16 PPU::get_pixel_normal(uint32 x) {
inline auto PPU::get_pixel_normal(uint32 x) -> uint16 {
pixel_t& p = pixel_cache[x];
uint16 src_main, src_sub;
uint8 bg_sub;
@ -50,7 +48,7 @@ inline uint16 PPU::get_pixel_normal(uint32 x) {
return src_main;
}
inline uint16 PPU::get_pixel_swap(uint32 x) {
inline auto PPU::get_pixel_swap(uint32 x) -> uint16 {
pixel_t& p = pixel_cache[x];
uint16 src_main, src_sub;
uint8 bg_sub;
@ -86,7 +84,7 @@ inline uint16 PPU::get_pixel_swap(uint32 x) {
return src_main;
}
inline void PPU::render_line_output() {
inline auto PPU::render_line_output() -> void {
uint32* ptr = (uint32*)output + (line * 1024) + ((interlace() && field()) ? 512 : 0);
uint32 curr, prev;
@ -111,10 +109,8 @@ inline void PPU::render_line_output() {
}
}
inline void PPU::render_line_clear() {
inline auto PPU::render_line_clear() -> void {
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;
uint width = (!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) ? 256 : 512;
memset(ptr, 0, width * 2 * sizeof(uint32));
}
#endif

View File

@ -1,5 +1,3 @@
#ifdef PPU_CPP
//bsnes mode7 renderer
//
//base algorithm written by anomie
@ -12,8 +10,8 @@
//--s---vvvvvvvvvv -> ssssssvvvvvvvvvv
#define CLIP(x) ( ((x) & 0x2000) ? ( (x) | ~0x03ff) : ((x) & 0x03ff) )
template<unsigned bg>
void PPU::render_line_mode7(uint8 pri0_pos, uint8 pri1_pos) {
template<uint bg>
auto PPU::render_line_mode7(uint8 pri0_pos, uint8 pri1_pos) -> void {
if(layer_enabled[bg][0] == false) pri0_pos = 0;
if(layer_enabled[bg][1] == false) pri1_pos = 0;
if(pri0_pos + pri1_pos == 0) return;
@ -140,5 +138,3 @@ void PPU::render_line_mode7(uint8 pri0_pos, uint8 pri1_pos) {
}
#undef CLIP
#endif

View File

@ -1,6 +1,4 @@
#ifdef PPU_CPP
void PPU::update_sprite_list(unsigned addr, uint8 data) {
auto PPU::update_sprite_list(uint addr, uint8 data) -> void {
if(addr < 0x0200) {
unsigned i = addr >> 2;
switch(addr & 3) {
@ -26,7 +24,7 @@ void PPU::update_sprite_list(unsigned addr, uint8 data) {
}
}
void PPU::build_sprite_list() {
auto PPU::build_sprite_list() -> void {
if(sprite_list_valid == true) return;
sprite_list_valid = true;
@ -65,7 +63,7 @@ void PPU::build_sprite_list() {
}
}
bool PPU::is_sprite_on_scanline() {
auto PPU::is_sprite_on_scanline() -> bool {
//if sprite is entirely offscreen and doesn't wrap around to the left side of the screen,
//then it is not counted. this *should* be 256, and not 255, even though dot 256 is offscreen.
sprite_item* spr = &sprite_list[active_sprite];
@ -77,7 +75,7 @@ bool PPU::is_sprite_on_scanline() {
return false;
}
void PPU::load_oam_tiles() {
auto PPU::load_oam_tiles() -> void {
sprite_item* spr = &sprite_list[active_sprite];
uint16 tile_width = spr->width >> 3;
int x = spr->x;
@ -130,7 +128,7 @@ void PPU::load_oam_tiles() {
}
}
void PPU::render_oam_tile(int tile_num) {
auto PPU::render_oam_tile(int tile_num) -> void {
oam_tileitem* t = &oam_tilelist[tile_num];
uint8* oam_td = (uint8*)bg_tiledata[COLORDEPTH_16];
uint8* oam_td_state = (uint8*)bg_tiledata_state[COLORDEPTH_16];
@ -155,7 +153,7 @@ void PPU::render_oam_tile(int tile_num) {
}
}
void PPU::render_line_oam_rto() {
auto PPU::render_line_oam_rto() -> void {
build_sprite_list();
regs.oam_itemcount = 0;
@ -200,7 +198,7 @@ void PPU::render_line_oam_rto() {
pixel_cache[x].ce_sub = (oam_line_pal[x] < 192); \
}
void PPU::render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) {
auto PPU::render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) -> void {
if(layer_enabled[OAM][0] == false) pri0_pos = 0;
if(layer_enabled[OAM][1] == false) pri1_pos = 0;
if(layer_enabled[OAM][2] == false) pri2_pos = 0;
@ -233,5 +231,3 @@ void PPU::render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8
#undef setpixel_main
#undef setpixel_sub
#endif

View File

@ -1,5 +1,3 @@
#ifdef PPU_CPP
#include "cache.cpp"
#include "windows.cpp"
#include "bg.cpp"
@ -11,7 +9,7 @@
//Mode 0: ->
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
// BG4B, BG3B, OAM0, BG4A, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3
void PPU::render_line_mode0() {
auto PPU::render_line_mode0() -> void {
render_line_bg<0, BG1, COLORDEPTH_4>(8, 11);
render_line_bg<0, BG2, COLORDEPTH_4>(7, 10);
render_line_bg<0, BG3, COLORDEPTH_4>(2, 5);
@ -26,7 +24,7 @@ void PPU::render_line_mode0() {
//Mode 1 (pri=0): ->
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
// BG3B, OAM0, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3
void PPU::render_line_mode1() {
auto PPU::render_line_mode1() -> void {
if(regs.bg3_priority) {
render_line_bg<1, BG1, COLORDEPTH_16>(5, 8);
render_line_bg<1, BG2, COLORDEPTH_16>(4, 7);
@ -43,7 +41,7 @@ void PPU::render_line_mode1() {
//Mode 2: ->
// 1, 2, 3, 4, 5, 6, 7, 8
// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
void PPU::render_line_mode2() {
auto PPU::render_line_mode2() -> void {
render_line_bg<2, BG1, COLORDEPTH_16>(3, 7);
render_line_bg<2, BG2, COLORDEPTH_16>(1, 5);
render_line_oam(2, 4, 6, 8);
@ -52,7 +50,7 @@ void PPU::render_line_mode2() {
//Mode 3: ->
// 1, 2, 3, 4, 5, 6, 7, 8
// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
void PPU::render_line_mode3() {
auto PPU::render_line_mode3() -> void {
render_line_bg<3, BG1, COLORDEPTH_256>(3, 7);
render_line_bg<3, BG2, COLORDEPTH_16 >(1, 5);
render_line_oam(2, 4, 6, 8);
@ -61,7 +59,7 @@ void PPU::render_line_mode3() {
//Mode 4: ->
// 1, 2, 3, 4, 5, 6, 7, 8
// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
void PPU::render_line_mode4() {
auto PPU::render_line_mode4() -> void {
render_line_bg<4, BG1, COLORDEPTH_256>(3, 7);
render_line_bg<4, BG2, COLORDEPTH_4 >(1, 5);
render_line_oam(2, 4, 6, 8);
@ -70,7 +68,7 @@ void PPU::render_line_mode4() {
//Mode 5: ->
// 1, 2, 3, 4, 5, 6, 7, 8
// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
void PPU::render_line_mode5() {
auto PPU::render_line_mode5() -> void {
render_line_bg<5, BG1, COLORDEPTH_16>(3, 7);
render_line_bg<5, BG2, COLORDEPTH_4 >(1, 5);
render_line_oam(2, 4, 6, 8);
@ -79,7 +77,7 @@ void PPU::render_line_mode5() {
//Mode 6: ->
// 1, 2, 3, 4, 5, 6
// OAM0, BG1B, OAM1, OAM2, BG1A, OAM3
void PPU::render_line_mode6() {
auto PPU::render_line_mode6() -> void {
render_line_bg<6, BG1, COLORDEPTH_16>(2, 5);
render_line_oam(1, 3, 4, 6);
}
@ -91,7 +89,7 @@ void PPU::render_line_mode6() {
//Mode 7 EXTBG: ->
// 1, 2, 3, 4, 5, 6, 7
// BG2B, OAM0, BG1n, OAM1, BG2A, OAM2, OAM3
void PPU::render_line_mode7() {
auto PPU::render_line_mode7() -> void {
if(regs.mode7_extbg == false) {
render_line_mode7<BG1>(2, 2);
render_line_oam(1, 3, 4, 5);
@ -102,7 +100,7 @@ void PPU::render_line_mode7() {
}
}
void PPU::render_line() {
auto PPU::render_line() -> void {
if(regs.display_disabled == true) {
render_line_clear();
return;
@ -125,5 +123,3 @@ void PPU::render_line() {
render_line_output();
}
#endif

View File

@ -1,16 +1,16 @@
//render.cpp
inline void render_line_mode0();
inline void render_line_mode1();
inline void render_line_mode2();
inline void render_line_mode3();
inline void render_line_mode4();
inline void render_line_mode5();
inline void render_line_mode6();
inline void render_line_mode7();
inline auto render_line_mode0() -> void;
inline auto render_line_mode1() -> void;
inline auto render_line_mode2() -> void;
inline auto render_line_mode3() -> void;
inline auto render_line_mode4() -> void;
inline auto render_line_mode5() -> void;
inline auto render_line_mode6() -> void;
inline auto render_line_mode7() -> void;
//cache.cpp
enum { COLORDEPTH_4 = 0, COLORDEPTH_16 = 1, COLORDEPTH_256 = 2 };
enum { TILE_2BIT = 0, TILE_4BIT = 1, TILE_8BIT = 2 };
enum : uint { COLORDEPTH_4 = 0, COLORDEPTH_16 = 1, COLORDEPTH_256 = 2 };
enum : uint { TILE_2BIT = 0, TILE_4BIT = 1, TILE_8BIT = 2 };
struct pixel_t {
//bgr555 color data for main/subscreen pixels: 0x0000 = transparent / use palette color # 0
@ -28,19 +28,19 @@ struct pixel_t {
uint8* bg_tiledata[3];
uint8* bg_tiledata_state[3]; //0 = valid, 1 = dirty
template<unsigned color_depth> void render_bg_tile(uint16 tile_num);
inline void flush_pixel_cache();
void alloc_tiledata_cache();
void flush_tiledata_cache();
void free_tiledata_cache();
template<uint color_depth> auto render_bg_tile(uint16 tile_num) -> void;
inline auto flush_pixel_cache() -> void;
auto alloc_tiledata_cache() -> void;
auto flush_tiledata_cache() -> void;
auto free_tiledata_cache() -> void;
//windows.cpp
struct window_t {
uint8 main[256], sub[256];
} window[6];
void build_window_table(uint8 bg, bool mainscreen);
void build_window_tables(uint8 bg);
auto build_window_table(uint8 bg, bool mainscreen) -> void;
auto build_window_tables(uint8 bg) -> void;
//bg.cpp
struct {
@ -48,10 +48,10 @@ struct {
uint16 mx, my; //screen mask x, y
uint16 scx, scy; //sc index offsets
} bg_info[4];
void update_bg_info();
auto update_bg_info() -> void;
template<unsigned bg> uint16 bg_get_tile(uint16 x, uint16 y);
template<unsigned mode, unsigned bg, unsigned color_depth> void render_line_bg(uint8 pri0_pos, uint8 pri1_pos);
template<uint bg> auto bg_get_tile(uint16 x, uint16 y) -> uint16;
template<uint mode, uint bg, uint color_depth> auto render_line_bg(uint8 pri0_pos, uint8 pri1_pos) -> void;
//oam.cpp
struct sprite_item {
@ -65,7 +65,7 @@ struct sprite_item {
bool size;
} sprite_list[128];
bool sprite_list_valid;
unsigned active_sprite;
uint active_sprite;
uint8 oam_itemlist[32];
struct oam_tileitem {
@ -73,27 +73,27 @@ struct oam_tileitem {
bool hflip;
} oam_tilelist[34];
enum { OAM_PRI_NONE = 4 };
enum : uint { OAM_PRI_NONE = 4 };
uint8 oam_line_pal[256], oam_line_pri[256];
void update_sprite_list(unsigned addr, uint8 data);
void build_sprite_list();
bool is_sprite_on_scanline();
void load_oam_tiles();
void render_oam_tile(int tile_num);
void render_line_oam_rto();
void render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos);
auto update_sprite_list(unsigned addr, uint8 data) -> void;
auto build_sprite_list() -> void;
auto is_sprite_on_scanline() -> bool;
auto load_oam_tiles() -> void;
auto render_oam_tile(int tile_num) -> void;
auto render_line_oam_rto() -> void;
auto render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) -> void;
//mode7.cpp
template<unsigned bg> void render_line_mode7(uint8 pri0_pos, uint8 pri1_pos);
template<uint bg> auto render_line_mode7(uint8 pri0_pos, uint8 pri1_pos) -> void;
//addsub.cpp
inline uint16 addsub(uint32 x, uint32 y, bool halve);
inline auto addsub(uint32 x, uint32 y, bool halve) -> uint16;
//line.cpp
inline uint16 get_palette(uint8 index);
inline uint16 get_direct_color(uint8 p, uint8 t);
inline uint16 get_pixel_normal(uint32 x);
inline uint16 get_pixel_swap(uint32 x);
void render_line_output();
void render_line_clear();
inline auto get_palette(uint8 index) -> uint16;
inline auto get_direct_color(uint8 p, uint8 t) -> uint16;
inline auto get_pixel_normal(uint32 x) -> uint16;
inline auto get_pixel_swap(uint32 x) -> uint16;
auto render_line_output() -> void;
auto render_line_clear() -> void;

View File

@ -1,7 +1,5 @@
#ifdef PPU_CPP
//screen: 0 = main, 1 = sub
void PPU::build_window_table(uint8 bg, bool screen) {
auto PPU::build_window_table(uint8 bg, bool screen) -> void {
bool set = 1, clr = 0;
uint8* table = (screen == 0 ? window[bg].main : window[bg].sub);
@ -62,9 +60,7 @@ void PPU::build_window_table(uint8 bg, bool screen) {
}
}
void PPU::build_window_tables(uint8 bg) {
auto PPU::build_window_tables(uint8 bg) -> void {
build_window_table(bg, 0);
build_window_table(bg, 1);
}
#endif

View File

@ -1,6 +1,4 @@
#ifdef PPU_CPP
void PPUcounter::serialize(serializer& s) {
auto PPUcounter::serialize(serializer& s) -> void {
s.integer(status.interlace);
s.integer(status.field);
s.integer(status.vcounter);
@ -12,7 +10,7 @@ void PPUcounter::serialize(serializer& s) {
s.integer(history.index);
}
void PPU::serialize(serializer& s) {
auto PPU::serialize(serializer& s) -> void {
Thread::serialize(s);
PPUcounter::serialize(s);
@ -35,7 +33,7 @@ void PPU::serialize(serializer& s) {
s.integer(regs.ppu1_mdr);
s.integer(regs.ppu2_mdr);
for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_y[n]);
for(uint n = 0; n < 4; n++) s.integer(regs.bg_y[n]);
s.integer(regs.ioamaddr);
s.integer(regs.icgramaddr);
@ -54,24 +52,24 @@ void PPU::serialize(serializer& s) {
s.integer(regs.oam_latchdata);
for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_tilesize[n]);
for(uint n = 0; n < 4; n++) s.integer(regs.bg_tilesize[n]);
s.integer(regs.bg3_priority);
s.integer(regs.bg_mode);
s.integer(regs.mosaic_size);
for(unsigned n = 0; n < 4; n++) s.integer(regs.mosaic_enabled[n]);
for(uint n = 0; n < 4; n++) s.integer(regs.mosaic_enabled[n]);
s.integer(regs.mosaic_countdown);
for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_scaddr[n]);
for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_scsize[n]);
for(uint n = 0; n < 4; n++) s.integer(regs.bg_scaddr[n]);
for(uint n = 0; n < 4; n++) s.integer(regs.bg_scsize[n]);
for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_tdaddr[n]);
for(uint n = 0; n < 4; n++) s.integer(regs.bg_tdaddr[n]);
s.integer(regs.bg_ofslatch);
s.integer(regs.m7_hofs);
s.integer(regs.m7_vofs);
for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_hofs[n]);
for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_vofs[n]);
for(uint n = 0; n < 4; n++) s.integer(regs.bg_hofs[n]);
for(uint n = 0; n < 4; n++) s.integer(regs.bg_vofs[n]);
s.integer(regs.vram_incmode);
s.integer(regs.vram_mapping);
@ -95,21 +93,21 @@ void PPU::serialize(serializer& s) {
s.integer(regs.cgram_latchdata);
for(unsigned n = 0; n < 6; n++) s.integer(regs.window1_enabled[n]);
for(unsigned n = 0; n < 6; n++) s.integer(regs.window1_invert [n]);
for(unsigned n = 0; n < 6; n++) s.integer(regs.window2_enabled[n]);
for(unsigned n = 0; n < 6; n++) s.integer(regs.window2_invert [n]);
for(uint n = 0; n < 6; n++) s.integer(regs.window1_enabled[n]);
for(uint n = 0; n < 6; n++) s.integer(regs.window1_invert [n]);
for(uint n = 0; n < 6; n++) s.integer(regs.window2_enabled[n]);
for(uint n = 0; n < 6; n++) s.integer(regs.window2_invert [n]);
s.integer(regs.window1_left);
s.integer(regs.window1_right);
s.integer(regs.window2_left);
s.integer(regs.window2_right);
for(unsigned n = 0; n < 6; n++) s.integer(regs.window_mask[n]);
for(unsigned n = 0; n < 5; n++) s.integer(regs.bg_enabled[n]);
for(unsigned n = 0; n < 5; n++) s.integer(regs.bgsub_enabled[n]);
for(unsigned n = 0; n < 5; n++) s.integer(regs.window_enabled[n]);
for(unsigned n = 0; n < 5; n++) s.integer(regs.sub_window_enabled[n]);
for(uint n = 0; n < 6; n++) s.integer(regs.window_mask[n]);
for(uint n = 0; n < 5; n++) s.integer(regs.bg_enabled[n]);
for(uint n = 0; n < 5; n++) s.integer(regs.bgsub_enabled[n]);
for(uint n = 0; n < 5; n++) s.integer(regs.window_enabled[n]);
for(uint n = 0; n < 5; n++) s.integer(regs.sub_window_enabled[n]);
s.integer(regs.color_mask);
s.integer(regs.colorsub_mask);
@ -118,7 +116,7 @@ void PPU::serialize(serializer& s) {
s.integer(regs.color_mode);
s.integer(regs.color_halve);
for(unsigned n = 0; n < 6; n++) s.integer(regs.color_enabled[n]);
for(uint n = 0; n < 6; n++) s.integer(regs.color_enabled[n]);
s.integer(regs.color_r);
s.integer(regs.color_g);
@ -145,7 +143,7 @@ void PPU::serialize(serializer& s) {
s.integer(regs.oam_itemcount);
s.integer(regs.oam_tilecount);
for(unsigned n = 0; n < 256; n++) {
for(uint n = 0; n < 256; n++) {
s.integer(pixel_cache[n].src_main);
s.integer(pixel_cache[n].src_sub);
s.integer(pixel_cache[n].bg_main);
@ -159,12 +157,12 @@ void PPU::serialize(serializer& s) {
//better to just take a small speed hit than store all of bg_tiledata[3][] ...
flush_tiledata_cache();
for(unsigned n = 0; n < 6; n++) {
for(uint n = 0; n < 6; n++) {
s.array(window[n].main, 256);
s.array(window[n].sub, 256);
}
for(unsigned n = 0; n < 4; n++) {
for(uint n = 0; n < 4; n++) {
s.integer(bg_info[n].tw);
s.integer(bg_info[n].th);
s.integer(bg_info[n].mx);
@ -173,7 +171,7 @@ void PPU::serialize(serializer& s) {
s.integer(bg_info[n].scy);
}
for(unsigned n = 0; n < 128; n++) {
for(uint n = 0; n < 128; n++) {
s.integer(sprite_list[n].width);
s.integer(sprite_list[n].height);
s.integer(sprite_list[n].x);
@ -191,7 +189,7 @@ void PPU::serialize(serializer& s) {
s.array(oam_itemlist, 32);
for(unsigned n = 0; n < 34; n++) {
for(uint n = 0; n < 34; n++) {
s.integer(oam_tilelist[n].x);
s.integer(oam_tilelist[n].y);
s.integer(oam_tilelist[n].pri);
@ -203,5 +201,3 @@ void PPU::serialize(serializer& s) {
s.array(oam_line_pal, 256);
s.array(oam_line_pri, 256);
}
#endif

Some files were not shown because too many files have changed in this diff Show More