mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
a219f9c121
commit
65a3306ad5
10
GNUmakefile
10
GNUmakefile
|
@ -7,8 +7,6 @@ gba := gba
|
||||||
|
|
||||||
profile := accuracy
|
profile := accuracy
|
||||||
target := tomoko
|
target := tomoko
|
||||||
|
|
||||||
# arch := x86
|
|
||||||
# console := true
|
# console := true
|
||||||
|
|
||||||
# compiler
|
# compiler
|
||||||
|
@ -28,16 +26,12 @@ endif
|
||||||
|
|
||||||
# platform
|
# platform
|
||||||
ifeq ($(platform),windows)
|
ifeq ($(platform),windows)
|
||||||
ifeq ($(arch),x86)
|
|
||||||
flags += -m32
|
|
||||||
link += -m32
|
|
||||||
endif
|
|
||||||
ifeq ($(console),true)
|
ifeq ($(console),true)
|
||||||
link += -mconsole
|
link += -mconsole
|
||||||
else
|
else
|
||||||
link += -mwindows
|
link += -mwindows
|
||||||
endif
|
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-auto-import
|
||||||
link += -Wl,-enable-runtime-pseudo-reloc
|
link += -Wl,-enable-runtime-pseudo-reloc
|
||||||
else ifeq ($(platform),macosx)
|
else ifeq ($(platform),macosx)
|
||||||
|
@ -46,7 +40,7 @@ else ifeq ($(platform),linux)
|
||||||
flags += -march=native -fopenmp
|
flags += -march=native -fopenmp
|
||||||
link += -fopenmp
|
link += -fopenmp
|
||||||
link += -Wl,-export-dynamic
|
link += -Wl,-export-dynamic
|
||||||
link += -lX11 -lXext -ldl
|
link += -lX11 -lXext
|
||||||
else ifeq ($(platform),bsd)
|
else ifeq ($(platform),bsd)
|
||||||
flags += -march=native -fopenmp
|
flags += -march=native -fopenmp
|
||||||
link += -fopenmp
|
link += -fopenmp
|
||||||
|
|
|
@ -7,7 +7,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
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 Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "http://byuu.org/";
|
static const string Website = "http://byuu.org/";
|
||||||
|
|
114
fc/apu/apu.cpp
114
fc/apu/apu.cpp
|
@ -11,38 +11,40 @@ namespace Famicom {
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
APU apu;
|
APU apu;
|
||||||
|
|
||||||
const uint8 APU::length_counter_table[32] = {
|
APU::APU() {
|
||||||
0x0a, 0xfe, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, 0xa0, 0x08, 0x3c, 0x0a, 0x0e, 0x0c, 0x1a, 0x0e,
|
for(uint amp : range(32)) {
|
||||||
0x0c, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, 0xc0, 0x18, 0x48, 0x1a, 0x10, 0x1c, 0x20, 0x1e,
|
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] = {
|
for(uint dmc_amp : range(128)) {
|
||||||
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068,
|
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] = {
|
auto APU::Main() -> void {
|
||||||
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() {
|
|
||||||
apu.main();
|
apu.main();
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::main() {
|
auto APU::main() -> void {
|
||||||
while(true) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
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[0].clock();
|
||||||
pulse_output += pulse[1].clock();
|
pulse_output += pulse[1].clock();
|
||||||
|
@ -52,7 +54,7 @@ void APU::main() {
|
||||||
|
|
||||||
clock_frame_counter_divider();
|
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 = filter.run_hipass_strong(output);
|
||||||
output += cartridge_sample;
|
output += cartridge_sample;
|
||||||
|
@ -66,20 +68,20 @@ void APU::main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::tick() {
|
auto APU::tick() -> void {
|
||||||
clock += 12;
|
clock += 12;
|
||||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
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);
|
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;
|
cartridge_sample = sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::power() {
|
auto APU::power() -> void {
|
||||||
filter.hipass_strong = 0;
|
filter.hipass_strong = 0;
|
||||||
filter.hipass_weak = 0;
|
filter.hipass_weak = 0;
|
||||||
filter.lopass = 0;
|
filter.lopass = 0;
|
||||||
|
@ -91,7 +93,7 @@ void APU::power() {
|
||||||
dmc.power();
|
dmc.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::reset() {
|
auto APU::reset() -> void {
|
||||||
create(APU::Main, 21477272);
|
create(APU::Main, 21477272);
|
||||||
|
|
||||||
pulse[0].reset();
|
pulse[0].reset();
|
||||||
|
@ -112,7 +114,7 @@ void APU::reset() {
|
||||||
set_irq_line();
|
set_irq_line();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 APU::read(uint16 addr) {
|
auto APU::read(uint16 addr) -> uint8 {
|
||||||
if(addr == 0x4015) {
|
if(addr == 0x4015) {
|
||||||
uint8 result = 0x00;
|
uint8 result = 0x00;
|
||||||
result |= pulse[0].length_counter ? 0x01 : 0;
|
result |= pulse[0].length_counter ? 0x01 : 0;
|
||||||
|
@ -132,8 +134,8 @@ uint8 APU::read(uint16 addr) {
|
||||||
return cpu.mdr();
|
return cpu.mdr();
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::write(uint16 addr, uint8 data) {
|
auto APU::write(uint16 addr, uint8 data) -> void {
|
||||||
const unsigned n = (addr >> 2) & 1; //pulse#
|
const uint n = (addr >> 2) & 1; //pulse#
|
||||||
|
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
case 0x4000: case 0x4004:
|
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;
|
hipass_strong += ((((int64)sample << 16) - (hipass_strong >> 16)) * HiPassStrong) >> 16;
|
||||||
return sample - (hipass_strong >> 32);
|
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;
|
hipass_weak += ((((int64)sample << 16) - (hipass_weak >> 16)) * HiPassWeak) >> 16;
|
||||||
return sample - (hipass_weak >> 32);
|
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;
|
lopass += ((((int64)sample << 16) - (lopass >> 16)) * LoPass) >> 16;
|
||||||
return (lopass >> 32);
|
return (lopass >> 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::clock_frame_counter() {
|
auto APU::clock_frame_counter() -> void {
|
||||||
frame.counter++;
|
frame.counter++;
|
||||||
|
|
||||||
if(frame.counter & 1) {
|
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;
|
frame.divider -= 2;
|
||||||
if(frame.divider <= 0) {
|
if(frame.divider <= 0) {
|
||||||
clock_frame_counter();
|
clock_frame_counter();
|
||||||
|
@ -303,27 +305,25 @@ void APU::clock_frame_counter_divider() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
APU::APU() {
|
const uint8 APU::length_counter_table[32] = {
|
||||||
for(unsigned amp = 0; amp < 32; amp++) {
|
0x0a, 0xfe, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, 0xa0, 0x08, 0x3c, 0x0a, 0x0e, 0x0c, 0x1a, 0x0e,
|
||||||
if(amp == 0) {
|
0x0c, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, 0xc0, 0x18, 0x48, 0x1a, 0x10, 0x1c, 0x20, 0x1e,
|
||||||
pulse_dac[amp] = 0;
|
};
|
||||||
} else {
|
|
||||||
pulse_dac[amp] = 16384.0 * 95.88 / (8128.0 / amp + 100.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(unsigned dmc_amp = 0; dmc_amp < 128; dmc_amp++) {
|
const uint16 APU::ntsc_noise_period_table[16] = {
|
||||||
for(unsigned triangle_amp = 0; triangle_amp < 16; triangle_amp++) {
|
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068,
|
||||||
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;
|
const uint16 APU::pal_noise_period_table[16] = {
|
||||||
} else {
|
4, 7, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778,
|
||||||
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_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,
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,33 @@
|
||||||
struct APU : Thread {
|
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();
|
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 {
|
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_strong;
|
||||||
int64 hipass_weak;
|
int64 hipass_weak;
|
||||||
int64 lopass;
|
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 "envelope.hpp"
|
||||||
#include "sweep.hpp"
|
#include "sweep.hpp"
|
||||||
|
@ -35,19 +37,22 @@ struct APU : Thread {
|
||||||
#include "dmc.hpp"
|
#include "dmc.hpp"
|
||||||
|
|
||||||
struct FrameCounter {
|
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;
|
bool irq_pending;
|
||||||
|
|
||||||
uint2 mode;
|
uint2 mode;
|
||||||
uint2 counter;
|
uint2 counter;
|
||||||
signed divider;
|
int divider;
|
||||||
|
};
|
||||||
|
|
||||||
void serialize(serializer&);
|
auto clock_frame_counter() -> void;
|
||||||
} frame;
|
auto clock_frame_counter_divider() -> void;
|
||||||
|
|
||||||
void clock_frame_counter();
|
Filter filter;
|
||||||
void clock_frame_counter_divider();
|
FrameCounter frame;
|
||||||
|
|
||||||
uint8 enabled_channels;
|
uint8 enabled_channels;
|
||||||
int16 cartridge_sample;
|
int16 cartridge_sample;
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
void APU::DMC::start() {
|
auto APU::DMC::start() -> void {
|
||||||
if(length_counter == 0) {
|
if(length_counter == 0) {
|
||||||
read_addr = 0x4000 + (addr_latch << 6);
|
read_addr = 0x4000 + (addr_latch << 6);
|
||||||
length_counter = (length_latch << 4) + 1;
|
length_counter = (length_latch << 4) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::DMC::stop() {
|
auto APU::DMC::stop() -> void {
|
||||||
length_counter = 0;
|
length_counter = 0;
|
||||||
dma_delay_counter = 0;
|
dma_delay_counter = 0;
|
||||||
cpu.set_rdy_line(1);
|
cpu.set_rdy_line(1);
|
||||||
cpu.set_rdy_addr(false);
|
cpu.set_rdy_addr(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 APU::DMC::clock() {
|
auto APU::DMC::clock() -> uint8 {
|
||||||
uint8 result = dac_latch;
|
uint8 result = dac_latch;
|
||||||
|
|
||||||
if(dma_delay_counter > 0) {
|
if(dma_delay_counter > 0) {
|
||||||
|
@ -42,8 +42,8 @@ uint8 APU::DMC::clock() {
|
||||||
|
|
||||||
if(--period_counter == 0) {
|
if(--period_counter == 0) {
|
||||||
if(have_sample) {
|
if(have_sample) {
|
||||||
signed delta = (((sample >> bit_counter) & 1) << 2) - 2;
|
int delta = (((sample >> bit_counter) & 1) << 2) - 2;
|
||||||
unsigned data = dac_latch + delta;
|
uint data = dac_latch + delta;
|
||||||
if((data & 0x80) == 0) dac_latch = data;
|
if((data & 0x80) == 0) dac_latch = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,10 +68,10 @@ uint8 APU::DMC::clock() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::DMC::power() {
|
auto APU::DMC::power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::DMC::reset() {
|
auto APU::DMC::reset() -> void {
|
||||||
length_counter = 0;
|
length_counter = 0;
|
||||||
irq_pending = 0;
|
irq_pending = 0;
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ void APU::DMC::reset() {
|
||||||
sample = 0;
|
sample = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::DMC::serialize(serializer& s) {
|
auto APU::DMC::serialize(serializer& s) -> void {
|
||||||
s.integer(length_counter);
|
s.integer(length_counter);
|
||||||
s.integer(irq_pending);
|
s.integer(irq_pending);
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
struct DMC {
|
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;
|
bool irq_pending;
|
||||||
|
|
||||||
uint4 period;
|
uint4 period;
|
||||||
unsigned period_counter;
|
uint period_counter;
|
||||||
|
|
||||||
bool irq_enable;
|
bool irq_enable;
|
||||||
bool loop_mode;
|
bool loop_mode;
|
||||||
|
@ -13,7 +22,7 @@ struct DMC {
|
||||||
uint8 length_latch;
|
uint8 length_latch;
|
||||||
|
|
||||||
uint15 read_addr;
|
uint15 read_addr;
|
||||||
unsigned dma_delay_counter;
|
uint dma_delay_counter;
|
||||||
|
|
||||||
uint3 bit_counter;
|
uint3 bit_counter;
|
||||||
bool have_dma_buffer;
|
bool have_dma_buffer;
|
||||||
|
@ -21,12 +30,4 @@ struct DMC {
|
||||||
|
|
||||||
bool have_sample;
|
bool have_sample;
|
||||||
uint8 sample;
|
uint8 sample;
|
||||||
|
|
||||||
void start();
|
|
||||||
void stop();
|
|
||||||
uint8 clock();
|
|
||||||
|
|
||||||
void power();
|
|
||||||
void reset();
|
|
||||||
void serialize(serializer&);
|
|
||||||
} dmc;
|
} dmc;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
unsigned APU::Envelope::volume() const {
|
auto APU::Envelope::volume() const -> uint {
|
||||||
return use_speed_as_volume ? speed : decay_volume;
|
return use_speed_as_volume ? speed : decay_volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Envelope::clock() {
|
auto APU::Envelope::clock() -> void {
|
||||||
if(reload_decay) {
|
if(reload_decay) {
|
||||||
reload_decay = false;
|
reload_decay = false;
|
||||||
decay_volume = 0x0f;
|
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;
|
speed = 0;
|
||||||
use_speed_as_volume = 0;
|
use_speed_as_volume = 0;
|
||||||
loop_mode = 0;
|
loop_mode = 0;
|
||||||
|
@ -28,7 +28,7 @@ void APU::Envelope::reset() {
|
||||||
decay_volume = 0;
|
decay_volume = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Envelope::serialize(serializer& s) {
|
auto APU::Envelope::serialize(serializer& s) -> void {
|
||||||
s.integer(speed);
|
s.integer(speed);
|
||||||
s.integer(use_speed_as_volume);
|
s.integer(use_speed_as_volume);
|
||||||
s.integer(loop_mode);
|
s.integer(loop_mode);
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
struct Envelope {
|
struct Envelope {
|
||||||
|
auto volume() const -> uint;
|
||||||
|
auto clock() -> void;
|
||||||
|
|
||||||
|
auto power() -> void;
|
||||||
|
auto reset() -> void;
|
||||||
|
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
uint4 speed;
|
uint4 speed;
|
||||||
bool use_speed_as_volume;
|
bool use_speed_as_volume;
|
||||||
bool loop_mode;
|
bool loop_mode;
|
||||||
|
@ -6,11 +14,4 @@ struct Envelope {
|
||||||
bool reload_decay;
|
bool reload_decay;
|
||||||
uint8 decay_counter;
|
uint8 decay_counter;
|
||||||
uint4 decay_volume;
|
uint4 decay_volume;
|
||||||
|
|
||||||
unsigned volume() const;
|
|
||||||
void clock();
|
|
||||||
|
|
||||||
void power();
|
|
||||||
void reset();
|
|
||||||
void serialize(serializer&);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
void APU::Noise::clock_length() {
|
auto APU::Noise::clock_length() -> void {
|
||||||
if(envelope.loop_mode == 0) {
|
if(envelope.loop_mode == 0) {
|
||||||
if(length_counter > 0) length_counter--;
|
if(length_counter > 0) length_counter--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 APU::Noise::clock() {
|
auto APU::Noise::clock() -> uint8 {
|
||||||
if(length_counter == 0) return 0;
|
if(length_counter == 0) return 0;
|
||||||
|
|
||||||
uint8 result = (lfsr & 1) ? envelope.volume() : 0;
|
uint8 result = (lfsr & 1) ? envelope.volume() : 0;
|
||||||
|
|
||||||
if(--period_counter == 0) {
|
if(--period_counter == 0) {
|
||||||
unsigned feedback;
|
uint feedback;
|
||||||
|
|
||||||
if(short_mode) {
|
if(short_mode) {
|
||||||
feedback = ((lfsr >> 0) & 1) ^ ((lfsr >> 6) & 1);
|
feedback = ((lfsr >> 0) & 1) ^ ((lfsr >> 6) & 1);
|
||||||
|
@ -25,10 +25,10 @@ uint8 APU::Noise::clock() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Noise::power() {
|
auto APU::Noise::power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Noise::reset() {
|
auto APU::Noise::reset() -> void {
|
||||||
length_counter = 0;
|
length_counter = 0;
|
||||||
|
|
||||||
envelope.speed = 0;
|
envelope.speed = 0;
|
||||||
|
@ -44,7 +44,7 @@ void APU::Noise::reset() {
|
||||||
lfsr = 1;
|
lfsr = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Noise::serialize(serializer& s) {
|
auto APU::Noise::serialize(serializer& s) -> void {
|
||||||
s.integer(length_counter);
|
s.integer(length_counter);
|
||||||
|
|
||||||
envelope.serialize(s);
|
envelope.serialize(s);
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
struct Noise {
|
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;
|
Envelope envelope;
|
||||||
|
|
||||||
uint4 period;
|
uint4 period;
|
||||||
unsigned period_counter;
|
uint period_counter;
|
||||||
|
|
||||||
bool short_mode;
|
bool short_mode;
|
||||||
uint15 lfsr;
|
uint15 lfsr;
|
||||||
|
|
||||||
void clock_length();
|
|
||||||
uint8 clock();
|
|
||||||
|
|
||||||
void power();
|
|
||||||
void reset();
|
|
||||||
void serialize(serializer&);
|
|
||||||
} noise;
|
} noise;
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
void APU::Pulse::clock_length() {
|
auto APU::Pulse::clock_length() -> void {
|
||||||
if(envelope.loop_mode == 0) {
|
if(envelope.loop_mode == 0) {
|
||||||
if(length_counter) length_counter--;
|
if(length_counter) length_counter--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 APU::Pulse::clock() {
|
auto APU::Pulse::clock() -> uint8 {
|
||||||
if(sweep.check_period() == false) return 0;
|
if(sweep.check_period() == false) return 0;
|
||||||
if(length_counter == 0) 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;
|
uint8 result = (duty_counter < duty_table[duty]) ? envelope.volume() : 0;
|
||||||
if(sweep.pulse_period < 0x008) result = 0;
|
if(sweep.pulse_period < 0x008) result = 0;
|
||||||
|
|
||||||
|
@ -20,12 +20,12 @@ uint8 APU::Pulse::clock() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Pulse::power() {
|
auto APU::Pulse::power() -> void {
|
||||||
envelope.power();
|
envelope.power();
|
||||||
sweep.power();
|
sweep.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Pulse::reset() {
|
auto APU::Pulse::reset() -> void {
|
||||||
envelope.reset();
|
envelope.reset();
|
||||||
sweep.reset();
|
sweep.reset();
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ void APU::Pulse::reset() {
|
||||||
period_counter = 1;
|
period_counter = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Pulse::serialize(serializer& s) {
|
auto APU::Pulse::serialize(serializer& s) -> void {
|
||||||
s.integer(length_counter);
|
s.integer(length_counter);
|
||||||
|
|
||||||
envelope.serialize(s);
|
envelope.serialize(s);
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
struct Pulse {
|
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;
|
Envelope envelope;
|
||||||
Sweep sweep;
|
Sweep sweep;
|
||||||
|
@ -8,13 +17,5 @@ struct Pulse {
|
||||||
uint3 duty_counter;
|
uint3 duty_counter;
|
||||||
|
|
||||||
uint11 period;
|
uint11 period;
|
||||||
unsigned period_counter;
|
uint period_counter;
|
||||||
|
|
||||||
void clock_length();
|
|
||||||
bool check_period();
|
|
||||||
uint8 clock();
|
|
||||||
|
|
||||||
void power();
|
|
||||||
void reset();
|
|
||||||
void serialize(serializer&);
|
|
||||||
} pulse[2];
|
} pulse[2];
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
void APU::serialize(serializer& s) {
|
auto APU::serialize(serializer& s) -> void {
|
||||||
Thread::serialize(s);
|
Thread::serialize(s);
|
||||||
|
|
||||||
filter.serialize(s);
|
filter.serialize(s);
|
||||||
|
@ -13,13 +13,13 @@ void APU::serialize(serializer& s) {
|
||||||
s.integer(cartridge_sample);
|
s.integer(cartridge_sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Filter::serialize(serializer& s) {
|
auto APU::Filter::serialize(serializer& s) -> void {
|
||||||
s.integer(hipass_strong);
|
s.integer(hipass_strong);
|
||||||
s.integer(hipass_weak);
|
s.integer(hipass_weak);
|
||||||
s.integer(lopass);
|
s.integer(lopass);
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::FrameCounter::serialize(serializer& s) {
|
auto APU::FrameCounter::serialize(serializer& s) -> void {
|
||||||
s.integer(irq_pending);
|
s.integer(irq_pending);
|
||||||
|
|
||||||
s.integer(mode);
|
s.integer(mode);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
bool APU::Sweep::check_period() {
|
auto APU::Sweep::check_period() -> bool {
|
||||||
if(pulse_period > 0x7ff) return false;
|
if(pulse_period > 0x7ff) return false;
|
||||||
|
|
||||||
if(decrement == 0) {
|
if(decrement == 0) {
|
||||||
|
@ -8,11 +8,11 @@ bool APU::Sweep::check_period() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Sweep::clock(unsigned channel) {
|
auto APU::Sweep::clock(uint channel) -> void {
|
||||||
if(--counter == 0) {
|
if(--counter == 0) {
|
||||||
counter = period + 1;
|
counter = period + 1;
|
||||||
if(enable && shift && pulse_period > 8) {
|
if(enable && shift && pulse_period > 8) {
|
||||||
signed delta = pulse_period >> shift;
|
int delta = pulse_period >> shift;
|
||||||
|
|
||||||
if(decrement) {
|
if(decrement) {
|
||||||
pulse_period -= delta;
|
pulse_period -= delta;
|
||||||
|
@ -29,7 +29,7 @@ void APU::Sweep::clock(unsigned channel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Sweep::power() {
|
auto APU::Sweep::power() -> void {
|
||||||
shift = 0;
|
shift = 0;
|
||||||
decrement = 0;
|
decrement = 0;
|
||||||
period = 0;
|
period = 0;
|
||||||
|
@ -39,10 +39,10 @@ void APU::Sweep::power() {
|
||||||
pulse_period = 0;
|
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(shift);
|
||||||
s.integer(decrement);
|
s.integer(decrement);
|
||||||
s.integer(period);
|
s.integer(period);
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
struct Sweep {
|
struct Sweep {
|
||||||
|
auto check_period() -> bool;
|
||||||
|
auto clock(uint channel) -> void;
|
||||||
|
|
||||||
|
auto power() -> void;
|
||||||
|
auto reset() -> void;
|
||||||
|
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
uint8 shift;
|
uint8 shift;
|
||||||
bool decrement;
|
bool decrement;
|
||||||
uint3 period;
|
uint3 period;
|
||||||
|
@ -6,11 +14,4 @@ struct Sweep {
|
||||||
bool enable;
|
bool enable;
|
||||||
bool reload;
|
bool reload;
|
||||||
uint11 pulse_period;
|
uint11 pulse_period;
|
||||||
|
|
||||||
bool check_period();
|
|
||||||
void clock(unsigned channel);
|
|
||||||
|
|
||||||
void power();
|
|
||||||
void reset();
|
|
||||||
void serialize(serializer&);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
void APU::Triangle::clock_length() {
|
auto APU::Triangle::clock_length() -> void {
|
||||||
if(halt_length_counter == 0) {
|
if(halt_length_counter == 0) {
|
||||||
if(length_counter > 0) length_counter--;
|
if(length_counter > 0) length_counter--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Triangle::clock_linear_length() {
|
auto APU::Triangle::clock_linear_length() -> void {
|
||||||
if(reload_linear) {
|
if(reload_linear) {
|
||||||
linear_length_counter = linear_length;
|
linear_length_counter = linear_length;
|
||||||
} else if(linear_length_counter) {
|
} else if(linear_length_counter) {
|
||||||
|
@ -14,7 +14,7 @@ void APU::Triangle::clock_linear_length() {
|
||||||
if(halt_length_counter == 0) reload_linear = false;
|
if(halt_length_counter == 0) reload_linear = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 APU::Triangle::clock() {
|
auto APU::Triangle::clock() -> uint8 {
|
||||||
uint8 result = step_counter & 0x0f;
|
uint8 result = step_counter & 0x0f;
|
||||||
if((step_counter & 0x10) == 0) result ^= 0x0f;
|
if((step_counter & 0x10) == 0) result ^= 0x0f;
|
||||||
if(length_counter == 0 || linear_length_counter == 0) return result;
|
if(length_counter == 0 || linear_length_counter == 0) return result;
|
||||||
|
@ -27,11 +27,11 @@ uint8 APU::Triangle::clock() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Triangle::power() {
|
auto APU::Triangle::power() -> void {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Triangle::reset() {
|
auto APU::Triangle::reset() -> void {
|
||||||
length_counter = 0;
|
length_counter = 0;
|
||||||
|
|
||||||
linear_length = 0;
|
linear_length = 0;
|
||||||
|
@ -43,7 +43,7 @@ void APU::Triangle::reset() {
|
||||||
reload_linear = 0;
|
reload_linear = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Triangle::serialize(serializer& s) {
|
auto APU::Triangle::serialize(serializer& s) -> void {
|
||||||
s.integer(length_counter);
|
s.integer(length_counter);
|
||||||
|
|
||||||
s.integer(linear_length);
|
s.integer(linear_length);
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
struct Triangle {
|
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;
|
uint8 linear_length;
|
||||||
bool halt_length_counter;
|
bool halt_length_counter;
|
||||||
|
|
||||||
uint11 period;
|
uint11 period;
|
||||||
unsigned period_counter;
|
uint period_counter;
|
||||||
|
|
||||||
uint5 step_counter;
|
uint5 step_counter;
|
||||||
uint8 linear_length_counter;
|
uint8 linear_length_counter;
|
||||||
bool reload_linear;
|
bool reload_linear;
|
||||||
|
|
||||||
void clock_length();
|
|
||||||
void clock_linear_length();
|
|
||||||
uint8 clock();
|
|
||||||
|
|
||||||
void power();
|
|
||||||
void reset();
|
|
||||||
void serialize(serializer&);
|
|
||||||
} triangle;
|
} triangle;
|
||||||
|
|
|
@ -1,15 +1,10 @@
|
||||||
//BANDAI-FCG
|
//BANDAI-FCG
|
||||||
|
|
||||||
struct BandaiFCG : Board {
|
struct BandaiFCG : Board {
|
||||||
|
BandaiFCG(Markup::Node& document) : Board(document) {
|
||||||
|
}
|
||||||
|
|
||||||
uint8 chr_bank[8];
|
auto main() -> void {
|
||||||
uint8 prg_bank;
|
|
||||||
uint2 mirror;
|
|
||||||
bool irq_counter_enable;
|
|
||||||
uint16 irq_counter;
|
|
||||||
uint16 irq_latch;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
while(true) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
|
@ -24,27 +19,27 @@ void main() {
|
||||||
|
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ciram_addr(unsigned addr) const {
|
auto ciram_addr(uint addr) const -> uint {
|
||||||
switch(mirror) {
|
switch(mirror) {
|
||||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
case 2: return 0x0000 | (addr & 0x03ff);
|
case 2: return 0x0000 | (addr & 0x03ff);
|
||||||
case 3: return 0x0400 | (addr & 0x03ff);
|
case 3: return 0x0400 | (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x8000) {
|
if(addr & 0x8000) {
|
||||||
bool region = addr & 0x4000;
|
bool region = addr & 0x4000;
|
||||||
unsigned bank = (region == 0 ? prg_bank : 0x0f);
|
uint bank = (region == 0 ? prg_bank : 0x0f);
|
||||||
return prgrom.read((bank << 14) | (addr & 0x3fff));
|
return prgrom.read((bank << 14) | (addr & 0x3fff));
|
||||||
}
|
}
|
||||||
return cpu.mdr();
|
return cpu.mdr();
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if(addr >= 0x6000) {
|
if(addr >= 0x6000) {
|
||||||
switch(addr & 15) {
|
switch(addr & 15) {
|
||||||
case 0x00: case 0x01: case 0x02: case 0x03:
|
case 0x00: case 0x01: case 0x02: case 0x03:
|
||||||
|
@ -73,34 +68,34 @@ void prg_write(unsigned addr, uint8 data) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
||||||
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
|
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
|
||||||
return Board::chr_read(addr);
|
return Board::chr_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
||||||
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
|
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
|
||||||
return Board::chr_write(addr, data);
|
return Board::chr_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
for(auto &n : chr_bank) n = 0;
|
for(auto &n : chr_bank) n = 0;
|
||||||
prg_bank = 0;
|
prg_bank = 0;
|
||||||
mirror = 0;
|
mirror = 0;
|
||||||
irq_counter_enable = 0;
|
irq_counter_enable = 0;
|
||||||
irq_counter = 0;
|
irq_counter = 0;
|
||||||
irq_latch = 0;
|
irq_latch = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
|
|
||||||
s.array(chr_bank);
|
s.array(chr_bank);
|
||||||
|
@ -109,9 +104,12 @@ void serialize(serializer& s) {
|
||||||
s.integer(irq_counter_enable);
|
s.integer(irq_counter_enable);
|
||||||
s.integer(irq_counter);
|
s.integer(irq_counter);
|
||||||
s.integer(irq_latch);
|
s.integer(irq_latch);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,69 +19,6 @@
|
||||||
#include "nes-uxrom.cpp"
|
#include "nes-uxrom.cpp"
|
||||||
#include "sunsoft-5b.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) {
|
Board::Board(Markup::Node& document) {
|
||||||
cartridge.board = this;
|
cartridge.board = this;
|
||||||
auto cartridge = document["cartridge"];
|
auto cartridge = document["cartridge"];
|
||||||
|
@ -116,10 +53,70 @@ Board::Board(Markup::Node& document) {
|
||||||
chrram.writable = true;
|
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);
|
auto document = BML::unserialize(manifest);
|
||||||
cartridge.information.title = document["information/title"].text();
|
cartridge.information.title = document["information/title"].text();
|
||||||
|
|
||||||
|
|
|
@ -1,38 +1,39 @@
|
||||||
struct Board {
|
struct Board {
|
||||||
struct Memory {
|
struct Memory {
|
||||||
uint8_t* data;
|
inline Memory(uint8_t* data, uint size) : data(data), size(size) {}
|
||||||
unsigned size;
|
|
||||||
bool writable;
|
|
||||||
|
|
||||||
inline uint8 read(unsigned addr) const;
|
|
||||||
inline void write(unsigned addr, uint8 data);
|
|
||||||
|
|
||||||
inline Memory(uint8_t* data, unsigned size) : data(data), size(size) {}
|
|
||||||
inline Memory() : data(nullptr), size(0u), writable(false) {}
|
inline Memory() : data(nullptr), size(0u), writable(false) {}
|
||||||
inline ~Memory() { if(data) delete[] data; }
|
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);
|
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 {
|
struct Information {
|
||||||
string type;
|
string type;
|
||||||
|
|
|
@ -1,40 +1,38 @@
|
||||||
struct KonamiVRC1 : Board {
|
struct KonamiVRC1 : Board {
|
||||||
|
KonamiVRC1(Markup::Node& document) : Board(document), vrc1(*this) {
|
||||||
|
}
|
||||||
|
|
||||||
VRC1 vrc1;
|
auto prg_read(uint addr) -> uint8 {
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
|
||||||
if(addr & 0x8000) return prgrom.read(vrc1.prg_addr(addr));
|
if(addr & 0x8000) return prgrom.read(vrc1.prg_addr(addr));
|
||||||
return cpu.mdr();
|
return cpu.mdr();
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x8000) return vrc1.reg_write(addr, data);
|
if(addr & 0x8000) return vrc1.reg_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) return ppu.ciram_read(vrc1.ciram_addr(addr));
|
if(addr & 0x2000) return ppu.ciram_read(vrc1.ciram_addr(addr));
|
||||||
return Board::chr_read(vrc1.chr_addr(addr));
|
return Board::chr_read(vrc1.chr_addr(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) return ppu.ciram_write(vrc1.ciram_addr(addr), data);
|
if(addr & 0x2000) return ppu.ciram_write(vrc1.ciram_addr(addr), data);
|
||||||
return Board::chr_write(vrc1.chr_addr(addr), data);
|
return Board::chr_write(vrc1.chr_addr(addr), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
vrc1.power();
|
vrc1.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
vrc1.reset();
|
vrc1.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
vrc1.serialize(s);
|
vrc1.serialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
KonamiVRC1(Markup::Node& document) : Board(document), vrc1(*this) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
VRC1 vrc1;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,21 +1,16 @@
|
||||||
struct KonamiVRC2 : Board {
|
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 {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
struct Pinout {
|
|
||||||
unsigned a0;
|
|
||||||
unsigned a1;
|
|
||||||
} pinout;
|
|
||||||
} settings;
|
|
||||||
|
|
||||||
VRC2 vrc2;
|
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
|
||||||
if(addr < 0x6000) return cpu.mdr();
|
if(addr < 0x6000) return cpu.mdr();
|
||||||
if(addr < 0x8000) return vrc2.ram_read(addr);
|
if(addr < 0x8000) return vrc2.ram_read(addr);
|
||||||
return prgrom.read(vrc2.prg_addr(addr));
|
return prgrom.read(vrc2.prg_addr(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if(addr < 0x6000) return;
|
if(addr < 0x6000) return;
|
||||||
if(addr < 0x8000) return vrc2.ram_write(addr, data);
|
if(addr < 0x8000) return vrc2.ram_write(addr, data);
|
||||||
|
|
||||||
|
@ -24,34 +19,37 @@ void prg_write(unsigned addr, uint8 data) {
|
||||||
addr &= 0xfff0;
|
addr &= 0xfff0;
|
||||||
addr |= (a0 << 0) | (a1 << 1);
|
addr |= (a0 << 0) | (a1 << 1);
|
||||||
return vrc2.reg_write(addr, data);
|
return vrc2.reg_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) return ppu.ciram_read(vrc2.ciram_addr(addr));
|
if(addr & 0x2000) return ppu.ciram_read(vrc2.ciram_addr(addr));
|
||||||
return Board::chr_read(vrc2.chr_addr(addr));
|
return Board::chr_read(vrc2.chr_addr(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) return ppu.ciram_write(vrc2.ciram_addr(addr), data);
|
if(addr & 0x2000) return ppu.ciram_write(vrc2.ciram_addr(addr), data);
|
||||||
return Board::chr_write(vrc2.chr_addr(addr), data);
|
return Board::chr_write(vrc2.chr_addr(addr), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
vrc2.power();
|
vrc2.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
vrc2.reset();
|
vrc2.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
vrc2.serialize(s);
|
vrc2.serialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
KonamiVRC2(Markup::Node& document) : Board(document), vrc2(*this) {
|
struct Settings {
|
||||||
settings.pinout.a0 = 1 << document["cartridge/chip/pinout/a0"].natural();
|
struct Pinout {
|
||||||
settings.pinout.a1 = 1 << document["cartridge/chip/pinout/a1"].natural();
|
uint a0;
|
||||||
}
|
uint a1;
|
||||||
|
} pinout;
|
||||||
|
} settings;
|
||||||
|
|
||||||
|
VRC2 vrc2;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,57 +1,55 @@
|
||||||
struct KonamiVRC3 : Board {
|
struct KonamiVRC3 : Board {
|
||||||
|
KonamiVRC3(Markup::Node& document) : Board(document), vrc3(*this) {
|
||||||
|
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct Settings {
|
auto main() -> void {
|
||||||
bool mirror; //0 = horizontal, 1 = vertical
|
|
||||||
} settings;
|
|
||||||
|
|
||||||
VRC3 vrc3;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vrc3.main();
|
vrc3.main();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
if((addr & 0xe000) == 0x6000) return prgram.read(addr & 0x1fff);
|
if((addr & 0xe000) == 0x6000) return prgram.read(addr & 0x1fff);
|
||||||
if(addr & 0x8000) return prgrom.read(vrc3.prg_addr(addr));
|
if(addr & 0x8000) return prgrom.read(vrc3.prg_addr(addr));
|
||||||
return cpu.mdr();
|
return cpu.mdr();
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if((addr & 0xe000) == 0x6000) return prgram.write(addr & 0x1fff, data);
|
if((addr & 0xe000) == 0x6000) return prgram.write(addr & 0x1fff, data);
|
||||||
if(addr & 0x8000) return vrc3.reg_write(addr, data);
|
if(addr & 0x8000) return vrc3.reg_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) {
|
if(addr & 0x2000) {
|
||||||
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
return ppu.ciram_read(addr & 0x07ff);
|
return ppu.ciram_read(addr & 0x07ff);
|
||||||
}
|
}
|
||||||
return chrram.read(addr);
|
return chrram.read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) {
|
if(addr & 0x2000) {
|
||||||
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
return ppu.ciram_write(addr & 0x07ff, data);
|
return ppu.ciram_write(addr & 0x07ff, data);
|
||||||
}
|
}
|
||||||
return chrram.write(addr, data);
|
return chrram.write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
vrc3.power();
|
vrc3.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
vrc3.reset();
|
vrc3.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
vrc3.serialize(s);
|
vrc3.serialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
KonamiVRC3(Markup::Node& document) : Board(document), vrc3(*this) {
|
struct Settings {
|
||||||
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
bool mirror; //0 = horizontal, 1 = vertical
|
||||||
}
|
} settings;
|
||||||
|
|
||||||
|
VRC3 vrc3;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,25 +1,20 @@
|
||||||
struct KonamiVRC4 : Board {
|
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 {
|
auto main() -> void {
|
||||||
struct Pinout {
|
|
||||||
unsigned a0;
|
|
||||||
unsigned a1;
|
|
||||||
} pinout;
|
|
||||||
} settings;
|
|
||||||
|
|
||||||
VRC4 vrc4;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
return vrc4.main();
|
return vrc4.main();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
if(addr < 0x6000) return cpu.mdr();
|
if(addr < 0x6000) return cpu.mdr();
|
||||||
if(addr < 0x8000) return prgram.read(addr);
|
if(addr < 0x8000) return prgram.read(addr);
|
||||||
return prgrom.read(vrc4.prg_addr(addr));
|
return prgrom.read(vrc4.prg_addr(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if(addr < 0x6000) return;
|
if(addr < 0x6000) return;
|
||||||
if(addr < 0x8000) return prgram.write(addr, data);
|
if(addr < 0x8000) return prgram.write(addr, data);
|
||||||
|
|
||||||
|
@ -28,34 +23,37 @@ void prg_write(unsigned addr, uint8 data) {
|
||||||
addr &= 0xfff0;
|
addr &= 0xfff0;
|
||||||
addr |= (a1 << 1) | (a0 << 0);
|
addr |= (a1 << 1) | (a0 << 0);
|
||||||
return vrc4.reg_write(addr, data);
|
return vrc4.reg_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) return ppu.ciram_read(vrc4.ciram_addr(addr));
|
if(addr & 0x2000) return ppu.ciram_read(vrc4.ciram_addr(addr));
|
||||||
return Board::chr_read(vrc4.chr_addr(addr));
|
return Board::chr_read(vrc4.chr_addr(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) return ppu.ciram_write(vrc4.ciram_addr(addr), data);
|
if(addr & 0x2000) return ppu.ciram_write(vrc4.ciram_addr(addr), data);
|
||||||
return Board::chr_write(vrc4.chr_addr(addr), data);
|
return Board::chr_write(vrc4.chr_addr(addr), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
vrc4.power();
|
vrc4.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
vrc4.reset();
|
vrc4.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
vrc4.serialize(s);
|
vrc4.serialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
KonamiVRC4(Markup::Node& document) : Board(document), vrc4(*this) {
|
struct Settings {
|
||||||
settings.pinout.a0 = 1 << document["cartridge/chip/pinout/a0"].natural();
|
struct Pinout {
|
||||||
settings.pinout.a1 = 1 << document["cartridge/chip/pinout/a1"].natural();
|
uint a0;
|
||||||
}
|
uint a1;
|
||||||
|
} pinout;
|
||||||
|
} settings;
|
||||||
|
|
||||||
|
VRC4 vrc4;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,42 +1,40 @@
|
||||||
struct KonamiVRC6 : Board {
|
struct KonamiVRC6 : Board {
|
||||||
|
KonamiVRC6(Markup::Node& document) : Board(document), vrc6(*this) {
|
||||||
|
}
|
||||||
|
|
||||||
VRC6 vrc6;
|
auto prg_read(uint addr) -> uint8{
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
|
||||||
if((addr & 0xe000) == 0x6000) return vrc6.ram_read(addr);
|
if((addr & 0xe000) == 0x6000) return vrc6.ram_read(addr);
|
||||||
if(addr & 0x8000) return prgrom.read(vrc6.prg_addr(addr));
|
if(addr & 0x8000) return prgrom.read(vrc6.prg_addr(addr));
|
||||||
return cpu.mdr();
|
return cpu.mdr();
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if((addr & 0xe000) == 0x6000) return vrc6.ram_write(addr, data);
|
if((addr & 0xe000) == 0x6000) return vrc6.ram_write(addr, data);
|
||||||
if(addr & 0x8000) {
|
if(addr & 0x8000) {
|
||||||
addr = (addr & 0xf003);
|
addr = (addr & 0xf003);
|
||||||
if(prgram.size) addr = (addr & ~3) | ((addr & 2) >> 1) | ((addr & 1) << 1);
|
if(prgram.size) addr = (addr & ~3) | ((addr & 2) >> 1) | ((addr & 1) << 1);
|
||||||
return vrc6.reg_write(addr, data);
|
return vrc6.reg_write(addr, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) return ppu.ciram_read(vrc6.ciram_addr(addr));
|
if(addr & 0x2000) return ppu.ciram_read(vrc6.ciram_addr(addr));
|
||||||
return Board::chr_read(vrc6.chr_addr(addr));
|
return Board::chr_read(vrc6.chr_addr(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) return ppu.ciram_write(vrc6.ciram_addr(addr), data);
|
if(addr & 0x2000) return ppu.ciram_write(vrc6.ciram_addr(addr), data);
|
||||||
return Board::chr_write(vrc6.chr_addr(addr), data);
|
return Board::chr_write(vrc6.chr_addr(addr), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
vrc6.serialize(s);
|
vrc6.serialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() { vrc6.main(); }
|
auto main() -> void { vrc6.main(); }
|
||||||
void power() { vrc6.power(); }
|
auto power() -> void { vrc6.power(); }
|
||||||
void reset() { vrc6.reset(); }
|
auto reset() -> void { vrc6.reset(); }
|
||||||
|
|
||||||
KonamiVRC6(Markup::Node& document) : Board(document), vrc6(*this) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
VRC6 vrc6;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,47 +1,45 @@
|
||||||
struct KonamiVRC7 : Board {
|
struct KonamiVRC7 : Board {
|
||||||
|
KonamiVRC7(Markup::Node& document) : Board(document), vrc7(*this) {
|
||||||
|
}
|
||||||
|
|
||||||
VRC7 vrc7;
|
auto main() -> void {
|
||||||
|
|
||||||
void main() {
|
|
||||||
return vrc7.main();
|
return vrc7.main();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
if(addr < 0x6000) return cpu.mdr();
|
if(addr < 0x6000) return cpu.mdr();
|
||||||
if(addr < 0x8000) return prgram.read(addr);
|
if(addr < 0x8000) return prgram.read(addr);
|
||||||
return prgrom.read(vrc7.prg_addr(addr));
|
return prgrom.read(vrc7.prg_addr(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if(addr < 0x6000) return;
|
if(addr < 0x6000) return;
|
||||||
if(addr < 0x8000) return prgram.write(addr, data);
|
if(addr < 0x8000) return prgram.write(addr, data);
|
||||||
return vrc7.reg_write(addr, data);
|
return vrc7.reg_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) return ppu.ciram_read(vrc7.ciram_addr(addr));
|
if(addr & 0x2000) return ppu.ciram_read(vrc7.ciram_addr(addr));
|
||||||
return chrram.read(vrc7.chr_addr(addr));
|
return chrram.read(vrc7.chr_addr(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) return ppu.ciram_write(vrc7.ciram_addr(addr), data);
|
if(addr & 0x2000) return ppu.ciram_write(vrc7.ciram_addr(addr), data);
|
||||||
return chrram.write(vrc7.chr_addr(addr), data);
|
return chrram.write(vrc7.chr_addr(addr), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
vrc7.power();
|
vrc7.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
vrc7.reset();
|
vrc7.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
vrc7.serialize(s);
|
vrc7.serialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
KonamiVRC7(Markup::Node& document) : Board(document), vrc7(*this) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
VRC7 vrc7;
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,48 +4,46 @@
|
||||||
//NES-AOROM
|
//NES-AOROM
|
||||||
|
|
||||||
struct NES_AxROM : Board {
|
struct NES_AxROM : Board {
|
||||||
|
NES_AxROM(Markup::Node& document) : Board(document) {
|
||||||
|
}
|
||||||
|
|
||||||
uint4 prg_bank;
|
auto prg_read(uint addr) -> uint8 {
|
||||||
bool mirror_select;
|
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
|
||||||
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
|
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
|
||||||
return cpu.mdr();
|
return cpu.mdr();
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x8000) {
|
if(addr & 0x8000) {
|
||||||
prg_bank = data & 0x0f;
|
prg_bank = data & 0x0f;
|
||||||
mirror_select = data & 0x10;
|
mirror_select = data & 0x10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) return ppu.ciram_read((mirror_select << 10) | (addr & 0x03ff));
|
if(addr & 0x2000) return ppu.ciram_read((mirror_select << 10) | (addr & 0x03ff));
|
||||||
return Board::chr_read(addr);
|
return Board::chr_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) return ppu.ciram_write((mirror_select << 10) | (addr & 0x03ff), data);
|
if(addr & 0x2000) return ppu.ciram_write((mirror_select << 10) | (addr & 0x03ff), data);
|
||||||
return Board::chr_write(addr, data);
|
return Board::chr_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
prg_bank = 0x0f;
|
prg_bank = 0x0f;
|
||||||
mirror_select = 0;
|
mirror_select = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
|
|
||||||
s.integer(prg_bank);
|
s.integer(prg_bank);
|
||||||
s.integer(mirror_select);
|
s.integer(mirror_select);
|
||||||
}
|
}
|
||||||
|
|
||||||
NES_AxROM(Markup::Node& document) : Board(document) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
uint4 prg_bank;
|
||||||
|
bool mirror_select;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,52 +1,50 @@
|
||||||
//NES-BN-ROM-01
|
//NES-BN-ROM-01
|
||||||
|
|
||||||
struct NES_BNROM : Board {
|
struct NES_BNROM : Board {
|
||||||
|
NES_BNROM(Markup::Node& document) : Board(document) {
|
||||||
|
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct Settings {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
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));
|
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
|
||||||
return cpu.mdr();
|
return cpu.mdr();
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x8000) prg_bank = data & 0x03;
|
if(addr & 0x8000) prg_bank = data & 0x03;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) {
|
if(addr & 0x2000) {
|
||||||
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
return ppu.ciram_read(addr);
|
return ppu.ciram_read(addr);
|
||||||
}
|
}
|
||||||
return Board::chr_read(addr);
|
return Board::chr_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) {
|
if(addr & 0x2000) {
|
||||||
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
return ppu.ciram_write(addr, data);
|
return ppu.ciram_write(addr, data);
|
||||||
}
|
}
|
||||||
return Board::chr_write(addr, data);
|
return Board::chr_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
prg_bank = 0;
|
prg_bank = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
s.integer(prg_bank);
|
s.integer(prg_bank);
|
||||||
}
|
}
|
||||||
|
|
||||||
NES_BNROM(Markup::Node& document) : Board(document) {
|
struct Settings {
|
||||||
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
bool mirror; //0 = horizontal, 1 = vertical
|
||||||
}
|
} settings;
|
||||||
|
|
||||||
|
uint2 prg_bank;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,54 +1,52 @@
|
||||||
//NES-CNROM
|
//NES-CNROM
|
||||||
|
|
||||||
struct NES_CNROM : Board {
|
struct NES_CNROM : Board {
|
||||||
|
NES_CNROM(Markup::Node& document) : Board(document) {
|
||||||
|
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct Settings {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
bool mirror; //0 = horizontal, 1 = vertical
|
|
||||||
} settings;
|
|
||||||
|
|
||||||
uint2 chr_bank;
|
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
|
||||||
if(addr & 0x8000) return prgrom.read(addr & 0x7fff);
|
if(addr & 0x8000) return prgrom.read(addr & 0x7fff);
|
||||||
return cpu.mdr();
|
return cpu.mdr();
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x8000) chr_bank = data & 0x03;
|
if(addr & 0x8000) chr_bank = data & 0x03;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) {
|
if(addr & 0x2000) {
|
||||||
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
return ppu.ciram_read(addr & 0x07ff);
|
return ppu.ciram_read(addr & 0x07ff);
|
||||||
}
|
}
|
||||||
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
|
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
|
||||||
return Board::chr_read(addr);
|
return Board::chr_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) {
|
if(addr & 0x2000) {
|
||||||
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
return ppu.ciram_write(addr & 0x07ff, data);
|
return ppu.ciram_write(addr & 0x07ff, data);
|
||||||
}
|
}
|
||||||
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
|
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
|
||||||
Board::chr_write(addr, data);
|
Board::chr_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
chr_bank = 0;
|
chr_bank = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
s.integer(chr_bank);
|
s.integer(chr_bank);
|
||||||
}
|
}
|
||||||
|
|
||||||
NES_CNROM(Markup::Node& document) : Board(document) {
|
struct Settings {
|
||||||
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
bool mirror; //0 = horizontal, 1 = vertical
|
||||||
}
|
} settings;
|
||||||
|
|
||||||
|
uint2 chr_bank;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,53 +1,51 @@
|
||||||
struct NES_ExROM : Board {
|
struct NES_ExROM : Board {
|
||||||
|
NES_ExROM(Markup::Node& document) : Board(document), mmc5(*this) {
|
||||||
|
revision = Revision::ELROM;
|
||||||
|
}
|
||||||
|
|
||||||
enum class Revision : unsigned {
|
auto main() -> void {
|
||||||
|
mmc5.main();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto prg_read(uint addr) -> uint8 {
|
||||||
|
return mmc5.prg_read(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
|
mmc5.prg_write(addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto chr_read(uint addr) -> uint8 {
|
||||||
|
return mmc5.chr_read(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
|
mmc5.chr_write(addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto scanline(uint y) -> void {
|
||||||
|
mmc5.scanline(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto power() -> void {
|
||||||
|
mmc5.power();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto reset() -> void {
|
||||||
|
mmc5.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto serialize(serializer& s) -> void {
|
||||||
|
Board::serialize(s);
|
||||||
|
mmc5.serialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Revision : uint {
|
||||||
EKROM,
|
EKROM,
|
||||||
ELROM,
|
ELROM,
|
||||||
ETROM,
|
ETROM,
|
||||||
EWROM,
|
EWROM,
|
||||||
} revision;
|
} revision;
|
||||||
|
|
||||||
MMC5 mmc5;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
mmc5.main();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
|
||||||
return mmc5.prg_read(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
|
||||||
mmc5.prg_write(addr, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
|
||||||
return mmc5.chr_read(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
|
||||||
mmc5.chr_write(addr, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scanline(unsigned y) {
|
|
||||||
mmc5.scanline(y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void power() {
|
|
||||||
mmc5.power();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
MMC5 mmc5;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,25 +1,18 @@
|
||||||
//MMC4
|
//MMC4
|
||||||
|
|
||||||
struct NES_FxROM : Board {
|
struct NES_FxROM : Board {
|
||||||
|
NES_FxROM(Markup::Node& document) : Board(document) {
|
||||||
|
revision = Revision::FKROM;
|
||||||
|
}
|
||||||
|
|
||||||
enum Revision : unsigned {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
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 < 0x6000) return cpu.mdr();
|
||||||
if(addr < 0x8000) return prgram.read(addr);
|
if(addr < 0x8000) return prgram.read(addr);
|
||||||
unsigned bank = addr < 0xc000 ? prg_bank : (uint4)0x0f;
|
uint bank = addr < 0xc000 ? prg_bank : (uint4)0x0f;
|
||||||
return prgrom.read((bank * 0x4000) | (addr & 0x3fff));
|
return prgrom.read((bank * 0x4000) | (addr & 0x3fff));
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if(addr < 0x6000) return;
|
if(addr < 0x6000) return;
|
||||||
if(addr < 0x8000) return prgram.write(addr, data);
|
if(addr < 0x8000) return prgram.write(addr, data);
|
||||||
|
|
||||||
|
@ -31,37 +24,37 @@ void prg_write(unsigned addr, uint8 data) {
|
||||||
case 0xe000: chr_bank[1][1] = data & 0x1f; break;
|
case 0xe000: chr_bank[1][1] = data & 0x1f; break;
|
||||||
case 0xf000: mirror = data & 0x01; break;
|
case 0xf000: mirror = data & 0x01; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ciram_addr(unsigned addr) const {
|
auto ciram_addr(uint addr) const -> uint {
|
||||||
switch(mirror) {
|
switch(mirror) {
|
||||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
||||||
bool region = addr & 0x1000;
|
bool region = addr & 0x1000;
|
||||||
unsigned bank = chr_bank[region][latch[region]];
|
uint bank = chr_bank[region][latch[region]];
|
||||||
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
|
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
|
||||||
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
|
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
|
||||||
return Board::chr_read((bank * 0x1000) | (addr & 0x0fff));
|
return Board::chr_read((bank * 0x1000) | (addr & 0x0fff));
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
||||||
bool region = addr & 0x1000;
|
bool region = addr & 0x1000;
|
||||||
unsigned bank = chr_bank[region][latch[region]];
|
uint bank = chr_bank[region][latch[region]];
|
||||||
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
|
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
|
||||||
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
|
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
|
||||||
return Board::chr_write((bank * 0x1000) | (addr & 0x0fff), data);
|
return Board::chr_write((bank * 0x1000) | (addr & 0x0fff), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
prg_bank = 0;
|
prg_bank = 0;
|
||||||
chr_bank[0][0] = 0;
|
chr_bank[0][0] = 0;
|
||||||
chr_bank[0][1] = 0;
|
chr_bank[0][1] = 0;
|
||||||
|
@ -70,9 +63,9 @@ void reset() {
|
||||||
mirror = 0;
|
mirror = 0;
|
||||||
latch[0] = 0;
|
latch[0] = 0;
|
||||||
latch[1] = 0;
|
latch[1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
|
|
||||||
s.integer(prg_bank);
|
s.integer(prg_bank);
|
||||||
|
@ -82,10 +75,15 @@ void serialize(serializer& s) {
|
||||||
s.integer(chr_bank[1][1]);
|
s.integer(chr_bank[1][1]);
|
||||||
s.integer(mirror);
|
s.integer(mirror);
|
||||||
s.array(latch);
|
s.array(latch);
|
||||||
}
|
}
|
||||||
|
|
||||||
NES_FxROM(Markup::Node& document) : Board(document) {
|
enum Revision : uint {
|
||||||
revision = Revision::FKROM;
|
FJROM,
|
||||||
}
|
FKROM,
|
||||||
|
} revision;
|
||||||
|
|
||||||
|
uint4 prg_bank;
|
||||||
|
uint5 chr_bank[2][2];
|
||||||
|
bool mirror;
|
||||||
|
bool latch[2];
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,60 +2,58 @@
|
||||||
//NES-MHROM
|
//NES-MHROM
|
||||||
|
|
||||||
struct NES_GxROM : Board {
|
struct NES_GxROM : Board {
|
||||||
|
NES_GxROM(Markup::Node& document) : Board(document) {
|
||||||
|
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct Settings {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
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));
|
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
|
||||||
return cpu.mdr();
|
return cpu.mdr();
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x8000) {
|
if(addr & 0x8000) {
|
||||||
prg_bank = (data & 0x30) >> 4;
|
prg_bank = (data & 0x30) >> 4;
|
||||||
chr_bank = (data & 0x03) >> 0;
|
chr_bank = (data & 0x03) >> 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) {
|
if(addr & 0x2000) {
|
||||||
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
return ppu.ciram_read(addr & 0x07ff);
|
return ppu.ciram_read(addr & 0x07ff);
|
||||||
}
|
}
|
||||||
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
|
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
|
||||||
return Board::chr_read(addr);
|
return Board::chr_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) {
|
if(addr & 0x2000) {
|
||||||
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
return ppu.ciram_write(addr & 0x07ff, data);
|
return ppu.ciram_write(addr & 0x07ff, data);
|
||||||
}
|
}
|
||||||
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
|
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
|
||||||
Board::chr_write(addr, data);
|
Board::chr_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
prg_bank = 0;
|
prg_bank = 0;
|
||||||
chr_bank = 0;
|
chr_bank = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
s.integer(prg_bank);
|
s.integer(prg_bank);
|
||||||
s.integer(chr_bank);
|
s.integer(chr_bank);
|
||||||
}
|
}
|
||||||
|
|
||||||
NES_GxROM(Markup::Node& document) : Board(document) {
|
struct Settings {
|
||||||
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
bool mirror; //0 = horizontal, 1 = vertical
|
||||||
}
|
} settings;
|
||||||
|
|
||||||
|
uint2 prg_bank;
|
||||||
|
uint2 chr_bank;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,48 +1,46 @@
|
||||||
struct NES_HKROM : Board {
|
struct NES_HKROM : Board {
|
||||||
|
NES_HKROM(Markup::Node& document) : Board(document), mmc6(*this) {
|
||||||
|
}
|
||||||
|
|
||||||
MMC6 mmc6;
|
auto main() -> void {
|
||||||
|
|
||||||
void main() {
|
|
||||||
mmc6.main();
|
mmc6.main();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
if((addr & 0xf000) == 0x7000) return mmc6.ram_read(addr);
|
if((addr & 0xf000) == 0x7000) return mmc6.ram_read(addr);
|
||||||
if(addr & 0x8000) return prgrom.read(mmc6.prg_addr(addr));
|
if(addr & 0x8000) return prgrom.read(mmc6.prg_addr(addr));
|
||||||
return cpu.mdr();
|
return cpu.mdr();
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if((addr & 0xf000) == 0x7000) return mmc6.ram_write(addr, data);
|
if((addr & 0xf000) == 0x7000) return mmc6.ram_write(addr, data);
|
||||||
if(addr & 0x8000) return mmc6.reg_write(addr, data);
|
if(addr & 0x8000) return mmc6.reg_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
mmc6.irq_test(addr);
|
mmc6.irq_test(addr);
|
||||||
if(addr & 0x2000) return ppu.ciram_read(mmc6.ciram_addr(addr));
|
if(addr & 0x2000) return ppu.ciram_read(mmc6.ciram_addr(addr));
|
||||||
return Board::chr_read(mmc6.chr_addr(addr));
|
return Board::chr_read(mmc6.chr_addr(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
mmc6.irq_test(addr);
|
mmc6.irq_test(addr);
|
||||||
if(addr & 0x2000) return ppu.ciram_write(mmc6.ciram_addr(addr), data);
|
if(addr & 0x2000) return ppu.ciram_write(mmc6.ciram_addr(addr), data);
|
||||||
return Board::chr_write(mmc6.chr_addr(addr), data);
|
return Board::chr_write(mmc6.chr_addr(addr), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
mmc6.power();
|
mmc6.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
mmc6.reset();
|
mmc6.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
mmc6.serialize(s);
|
mmc6.serialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
NES_HKROM(Markup::Node& document) : Board(document), mmc6(*this) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
MMC6 mmc6;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,42 +2,40 @@
|
||||||
//NES-NROM-256
|
//NES-NROM-256
|
||||||
|
|
||||||
struct NES_NROM : Board {
|
struct NES_NROM : Board {
|
||||||
|
NES_NROM(Markup::Node& document) : Board(document) {
|
||||||
|
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct Settings {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
bool mirror; //0 = horizontal, 1 = vertical
|
|
||||||
} settings;
|
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
|
||||||
if(addr & 0x8000) return prgrom.read(addr);
|
if(addr & 0x8000) return prgrom.read(addr);
|
||||||
return cpu.mdr();
|
return cpu.mdr();
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) {
|
if(addr & 0x2000) {
|
||||||
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
return ppu.ciram_read(addr & 0x07ff);
|
return ppu.ciram_read(addr & 0x07ff);
|
||||||
}
|
}
|
||||||
if(chrram.size) return chrram.read(addr);
|
if(chrram.size) return chrram.read(addr);
|
||||||
return chrrom.read(addr);
|
return chrrom.read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) {
|
if(addr & 0x2000) {
|
||||||
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
return ppu.ciram_write(addr & 0x07ff, data);
|
return ppu.ciram_write(addr & 0x07ff, data);
|
||||||
}
|
}
|
||||||
if(chrram.size) return chrram.write(addr, data);
|
if(chrram.size) return chrram.write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
NES_NROM(Markup::Node& document) : Board(document) {
|
|
||||||
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
struct Settings {
|
||||||
|
bool mirror; //0 = horizontal, 1 = vertical
|
||||||
|
} settings;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,21 +1,14 @@
|
||||||
//MMC2
|
//MMC2
|
||||||
|
|
||||||
struct NES_PxROM : Board {
|
struct NES_PxROM : Board {
|
||||||
|
NES_PxROM(Markup::Node& document) : Board(document) {
|
||||||
|
revision = Revision::PNROM;
|
||||||
|
}
|
||||||
|
|
||||||
enum Revision : unsigned {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
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 < 0x6000) return cpu.mdr();
|
||||||
if(addr < 0x8000) return prgram.read(addr);
|
if(addr < 0x8000) return prgram.read(addr);
|
||||||
unsigned bank = 0;
|
uint bank = 0;
|
||||||
switch((addr / 0x2000) & 3) {
|
switch((addr / 0x2000) & 3) {
|
||||||
case 0: bank = prg_bank; break;
|
case 0: bank = prg_bank; break;
|
||||||
case 1: bank = 0x0d; break;
|
case 1: bank = 0x0d; break;
|
||||||
|
@ -23,9 +16,9 @@ uint8 prg_read(unsigned addr) {
|
||||||
case 3: bank = 0x0f; break;
|
case 3: bank = 0x0f; break;
|
||||||
}
|
}
|
||||||
return prgrom.read((bank * 0x2000) | (addr & 0x1fff));
|
return prgrom.read((bank * 0x2000) | (addr & 0x1fff));
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if(addr < 0x6000) return;
|
if(addr < 0x6000) return;
|
||||||
if(addr < 0x8000) return prgram.write(addr, data);
|
if(addr < 0x8000) return prgram.write(addr, data);
|
||||||
|
|
||||||
|
@ -37,37 +30,37 @@ void prg_write(unsigned addr, uint8 data) {
|
||||||
case 0xe000: chr_bank[1][1] = data & 0x1f; break;
|
case 0xe000: chr_bank[1][1] = data & 0x1f; break;
|
||||||
case 0xf000: mirror = data & 0x01; break;
|
case 0xf000: mirror = data & 0x01; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ciram_addr(unsigned addr) const {
|
auto ciram_addr(uint addr) const -> uint {
|
||||||
switch(mirror) {
|
switch(mirror) {
|
||||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
||||||
bool region = addr & 0x1000;
|
bool region = addr & 0x1000;
|
||||||
unsigned bank = chr_bank[region][latch[region]];
|
uint bank = chr_bank[region][latch[region]];
|
||||||
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
|
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
|
||||||
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
|
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
|
||||||
return Board::chr_read((bank * 0x1000) | (addr & 0x0fff));
|
return Board::chr_read((bank * 0x1000) | (addr & 0x0fff));
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
||||||
bool region = addr & 0x1000;
|
bool region = addr & 0x1000;
|
||||||
unsigned bank = chr_bank[region][latch[region]];
|
uint bank = chr_bank[region][latch[region]];
|
||||||
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
|
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
|
||||||
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
|
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
|
||||||
return Board::chr_write((bank * 0x1000) | (addr & 0x0fff), data);
|
return Board::chr_write((bank * 0x1000) | (addr & 0x0fff), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
prg_bank = 0;
|
prg_bank = 0;
|
||||||
chr_bank[0][0] = 0;
|
chr_bank[0][0] = 0;
|
||||||
chr_bank[0][1] = 0;
|
chr_bank[0][1] = 0;
|
||||||
|
@ -76,9 +69,9 @@ void reset() {
|
||||||
mirror = 0;
|
mirror = 0;
|
||||||
latch[0] = 0;
|
latch[0] = 0;
|
||||||
latch[1] = 0;
|
latch[1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
|
|
||||||
s.integer(prg_bank);
|
s.integer(prg_bank);
|
||||||
|
@ -88,10 +81,15 @@ void serialize(serializer& s) {
|
||||||
s.integer(chr_bank[1][1]);
|
s.integer(chr_bank[1][1]);
|
||||||
s.integer(mirror);
|
s.integer(mirror);
|
||||||
s.array(latch);
|
s.array(latch);
|
||||||
}
|
}
|
||||||
|
|
||||||
NES_PxROM(Markup::Node& document) : Board(document) {
|
enum Revision : uint {
|
||||||
revision = Revision::PNROM;
|
PEEOROM,
|
||||||
}
|
PNROM,
|
||||||
|
} revision;
|
||||||
|
|
||||||
|
uint4 prg_bank;
|
||||||
|
uint5 chr_bank[2][2];
|
||||||
|
bool mirror;
|
||||||
|
bool latch[2];
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,76 @@
|
||||||
struct NES_SxROM : Board {
|
struct NES_SxROM : Board {
|
||||||
|
NES_SxROM(Markup::Node& document) : Board(document), mmc1(*this) {
|
||||||
|
revision = Revision::SXROM;
|
||||||
|
}
|
||||||
|
|
||||||
enum class Revision : unsigned {
|
auto main() -> void {
|
||||||
|
return mmc1.main();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr & 0x8000) {
|
||||||
|
addr = mmc1.prg_addr(addr);
|
||||||
|
if(revision == Revision::SXROM) {
|
||||||
|
addr |= ((mmc1.chr_bank[0] & 0x10) >> 4) << 18;
|
||||||
|
}
|
||||||
|
return prgrom.read(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpu.mdr();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr & 0x8000) return mmc1.mmio_write(addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
SAROM,
|
||||||
SBROM,
|
SBROM,
|
||||||
SCROM,
|
SCROM,
|
||||||
|
@ -23,79 +93,7 @@ enum class Revision : unsigned {
|
||||||
SOROM,
|
SOROM,
|
||||||
SUROM,
|
SUROM,
|
||||||
SXROM,
|
SXROM,
|
||||||
} revision;
|
} 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr & 0x8000) {
|
|
||||||
addr = mmc1.prg_addr(addr);
|
|
||||||
if(revision == Revision::SXROM) {
|
|
||||||
addr |= ((mmc1.chr_bank[0] & 0x10) >> 4) << 18;
|
|
||||||
}
|
|
||||||
return prgrom.read(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr & 0x8000) return mmc1.mmio_write(addr, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
|
||||||
if(addr & 0x2000) return ppu.ciram_read(mmc1.ciram_addr(addr));
|
|
||||||
return Board::chr_read(mmc1.chr_addr(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void power() {
|
|
||||||
mmc1.power();
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
mmc1.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
|
||||||
Board::serialize(s);
|
|
||||||
mmc1.serialize(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
NES_SxROM(Markup::Node& document) : Board(document), mmc1(*this) {
|
|
||||||
revision = Revision::SXROM;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
MMC1 mmc1;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,49 @@
|
||||||
struct NES_TxROM : Board {
|
struct NES_TxROM : Board {
|
||||||
|
NES_TxROM(Markup::Node& document) : Board(document), mmc3(*this) {
|
||||||
|
revision = Revision::TLROM;
|
||||||
|
}
|
||||||
|
|
||||||
enum class Revision : unsigned {
|
auto main() -> void {
|
||||||
|
mmc3.main();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto power() -> void {
|
||||||
|
mmc3.power();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto reset() -> void {
|
||||||
|
mmc3.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto serialize(serializer& s) -> void {
|
||||||
|
Board::serialize(s);
|
||||||
|
mmc3.serialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Revision : uint {
|
||||||
TBROM,
|
TBROM,
|
||||||
TEROM,
|
TEROM,
|
||||||
TFROM,
|
TFROM,
|
||||||
|
@ -16,52 +59,7 @@ enum class Revision : unsigned {
|
||||||
TR1ROM,
|
TR1ROM,
|
||||||
TSROM,
|
TSROM,
|
||||||
TVROM,
|
TVROM,
|
||||||
} revision;
|
} revision;
|
||||||
|
|
||||||
MMC3 mmc3;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
mmc3.main();
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void power() {
|
|
||||||
mmc3.power();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
MMC3 mmc3;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,54 +2,52 @@
|
||||||
//NES-UOROM
|
//NES-UOROM
|
||||||
|
|
||||||
struct NES_UxROM : Board {
|
struct NES_UxROM : Board {
|
||||||
|
NES_UxROM(Markup::Node& document) : Board(document) {
|
||||||
|
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct Settings {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
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) == 0x8000) return prgrom.read((prg_bank << 14) | (addr & 0x3fff));
|
||||||
if((addr & 0xc000) == 0xc000) return prgrom.read(( 0x0f << 14) | (addr & 0x3fff));
|
if((addr & 0xc000) == 0xc000) return prgrom.read(( 0x0f << 14) | (addr & 0x3fff));
|
||||||
return cpu.mdr();
|
return cpu.mdr();
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x8000) prg_bank = data & 0x0f;
|
if(addr & 0x8000) prg_bank = data & 0x0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) {
|
if(addr & 0x2000) {
|
||||||
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
return ppu.ciram_read(addr);
|
return ppu.ciram_read(addr);
|
||||||
}
|
}
|
||||||
return Board::chr_read(addr);
|
return Board::chr_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) {
|
if(addr & 0x2000) {
|
||||||
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
return ppu.ciram_write(addr, data);
|
return ppu.ciram_write(addr, data);
|
||||||
}
|
}
|
||||||
return Board::chr_write(addr, data);
|
return Board::chr_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
prg_bank = 0;
|
prg_bank = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
|
|
||||||
s.integer(prg_bank);
|
s.integer(prg_bank);
|
||||||
}
|
}
|
||||||
|
|
||||||
NES_UxROM(Markup::Node& document) : Board(document) {
|
struct Settings {
|
||||||
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
bool mirror; //0 = horizontal, 1 = vertical
|
||||||
}
|
} settings;
|
||||||
|
|
||||||
|
uint4 prg_bank;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,29 +1,11 @@
|
||||||
//SUNSOFT-5B
|
//SUNSOFT-5B
|
||||||
|
|
||||||
struct Sunsoft5B : Board {
|
struct Sunsoft5B : Board {
|
||||||
|
Sunsoft5B(Markup::Node& document) : Board(document) {
|
||||||
|
}
|
||||||
|
|
||||||
uint4 mmu_port;
|
struct Pulse {
|
||||||
uint4 apu_port;
|
auto clock() -> void {
|
||||||
|
|
||||||
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) {
|
if(--counter == 0) {
|
||||||
counter = frequency << 4;
|
counter = frequency << 4;
|
||||||
duty ^= 1;
|
duty ^= 1;
|
||||||
|
@ -32,7 +14,7 @@ struct Pulse {
|
||||||
if(disable) output = 0;
|
if(disable) output = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
disable = 1;
|
disable = 1;
|
||||||
frequency = 1;
|
frequency = 1;
|
||||||
volume = 0;
|
volume = 0;
|
||||||
|
@ -42,7 +24,7 @@ struct Pulse {
|
||||||
output = 0;
|
output = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
s.integer(disable);
|
s.integer(disable);
|
||||||
s.integer(frequency);
|
s.integer(frequency);
|
||||||
s.integer(volume);
|
s.integer(volume);
|
||||||
|
@ -51,9 +33,17 @@ struct Pulse {
|
||||||
s.integer(duty);
|
s.integer(duty);
|
||||||
s.integer(output);
|
s.integer(output);
|
||||||
}
|
}
|
||||||
} pulse[3];
|
|
||||||
|
|
||||||
void main() {
|
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) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
|
@ -73,9 +63,9 @@ void main() {
|
||||||
|
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
if(addr < 0x6000) return cpu.mdr();
|
if(addr < 0x6000) return cpu.mdr();
|
||||||
|
|
||||||
uint8 bank = 0x3f; //((addr & 0xe000) == 0xe000
|
uint8 bank = 0x3f; //((addr & 0xe000) == 0xe000
|
||||||
|
@ -95,9 +85,9 @@ uint8 prg_read(unsigned addr) {
|
||||||
|
|
||||||
addr = (bank << 13) | (addr & 0x1fff);
|
addr = (bank << 13) | (addr & 0x1fff);
|
||||||
return prgrom.read(addr);
|
return prgrom.read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if((addr & 0xe000) == 0x6000) {
|
if((addr & 0xe000) == 0x6000) {
|
||||||
prgram.data[addr & 0x1fff] = data;
|
prgram.data[addr & 0x1fff] = data;
|
||||||
}
|
}
|
||||||
|
@ -153,40 +143,40 @@ void prg_write(unsigned addr, uint8 data) {
|
||||||
case 10: pulse[2].volume = data & 0x0f; break;
|
case 10: pulse[2].volume = data & 0x0f; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned chr_addr(unsigned addr) {
|
auto chr_addr(uint addr) -> uint {
|
||||||
uint8 bank = (addr >> 10) & 7;
|
uint8 bank = (addr >> 10) & 7;
|
||||||
return (chr_bank[bank] << 10) | (addr & 0x03ff);
|
return (chr_bank[bank] << 10) | (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ciram_addr(unsigned addr) {
|
auto ciram_addr(uint addr) -> uint {
|
||||||
switch(mirror) {
|
switch(mirror) {
|
||||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical
|
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical
|
||||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal
|
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal
|
||||||
case 2: return 0x0000 | (addr & 0x03ff); //first
|
case 2: return 0x0000 | (addr & 0x03ff); //first
|
||||||
case 3: return 0x0400 | (addr & 0x03ff); //second
|
case 3: return 0x0400 | (addr & 0x03ff); //second
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
||||||
return Board::chr_read(chr_addr(addr));
|
return Board::chr_read(chr_addr(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
||||||
return Board::chr_write(chr_addr(addr), data);
|
return Board::chr_write(chr_addr(addr), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
for(signed n = 0; n < 16; n++) {
|
for(signed n : range(16)) {
|
||||||
double volume = 1.0 / pow(2, 1.0 / 2 * (15 - n));
|
double volume = 1.0 / pow(2, 1.0 / 2 * (15 - n));
|
||||||
dac[n] = volume * 8192.0;
|
dac[n] = volume * 8192.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
mmu_port = 0;
|
mmu_port = 0;
|
||||||
apu_port = 0;
|
apu_port = 0;
|
||||||
|
|
||||||
|
@ -200,9 +190,9 @@ void reset() {
|
||||||
pulse[0].reset();
|
pulse[0].reset();
|
||||||
pulse[1].reset();
|
pulse[1].reset();
|
||||||
pulse[2].reset();
|
pulse[2].reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
|
|
||||||
s.integer(mmu_port);
|
s.integer(mmu_port);
|
||||||
|
@ -218,9 +208,17 @@ void serialize(serializer& s) {
|
||||||
pulse[0].serialize(s);
|
pulse[0].serialize(s);
|
||||||
pulse[1].serialize(s);
|
pulse[1].serialize(s);
|
||||||
pulse[2].serialize(s);
|
pulse[2].serialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
Sunsoft5B(Markup::Node& document) : Board(document) {
|
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];
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,73 +6,77 @@ namespace Famicom {
|
||||||
#include "board/board.cpp"
|
#include "board/board.cpp"
|
||||||
Cartridge cartridge;
|
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;
|
return information.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::Main() {
|
auto Cartridge::Main() -> void {
|
||||||
cartridge.main();
|
cartridge.main();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::main() {
|
auto Cartridge::main() -> void {
|
||||||
board->main();
|
board->main();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::load() {
|
auto Cartridge::load() -> void {
|
||||||
interface->loadRequest(ID::Manifest, "manifest.bml", true);
|
interface->loadRequest(ID::Manifest, "manifest.bml", true);
|
||||||
|
|
||||||
Board::load(information.markup); //this call will set Cartridge::board if successful
|
Board::load(information.markup); //this call will set Cartridge::board if successful
|
||||||
if(board == nullptr) return;
|
if(!board) return;
|
||||||
|
|
||||||
Hash::SHA256 sha;
|
Hash::SHA256 sha;
|
||||||
sha.data(board->prgrom.data, board->prgrom.size);
|
sha.data(board->prgrom.data, board->prgrom.size);
|
||||||
sha.data(board->chrrom.data, board->chrrom.size);
|
sha.data(board->chrrom.data, board->chrrom.size);
|
||||||
sha256 = sha.digest();
|
_sha256 = sha.digest();
|
||||||
|
|
||||||
system.load();
|
system.load();
|
||||||
loaded = true;
|
_loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::unload() {
|
auto Cartridge::unload() -> void {
|
||||||
if(loaded == false) return;
|
if(!loaded()) return;
|
||||||
loaded = false;
|
_loaded = false;
|
||||||
memory.reset();
|
memory.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::power() {
|
auto Cartridge::power() -> void {
|
||||||
board->power();
|
board->power();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::reset() {
|
auto Cartridge::reset() -> void {
|
||||||
create(Cartridge::Main, 21477272);
|
create(Cartridge::Main, 21477272);
|
||||||
board->reset();
|
board->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
Cartridge::Cartridge() {
|
auto Cartridge::prg_read(uint addr) -> uint8 {
|
||||||
loaded = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8 Cartridge::prg_read(unsigned addr) {
|
|
||||||
return board->prg_read(addr);
|
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);
|
return board->prg_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 Cartridge::chr_read(unsigned addr) {
|
auto Cartridge::chr_read(uint addr) -> uint8 {
|
||||||
return board->chr_read(addr);
|
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);
|
return board->chr_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::scanline(unsigned y) {
|
auto Cartridge::scanline(uint y) -> void {
|
||||||
return board->scanline(y);
|
return board->scanline(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::serialize(serializer& s) {
|
auto Cartridge::serialize(serializer& s) -> void {
|
||||||
Thread::serialize(s);
|
Thread::serialize(s);
|
||||||
return board->serialize(s);
|
return board->serialize(s);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +1,47 @@
|
||||||
#include "chip/chip.hpp"
|
#include "chip/chip.hpp"
|
||||||
#include "board/board.hpp"
|
#include "board/board.hpp"
|
||||||
|
|
||||||
struct Cartridge : Thread, property<Cartridge> {
|
struct Cartridge : Thread {
|
||||||
static void Main();
|
static auto Main() -> void;
|
||||||
void main();
|
auto main() -> void;
|
||||||
|
|
||||||
void load();
|
auto loaded() const -> bool;
|
||||||
void unload();
|
auto sha256() const -> string;
|
||||||
|
auto title() const -> string;
|
||||||
|
|
||||||
void power();
|
auto load() -> void;
|
||||||
void reset();
|
auto unload() -> void;
|
||||||
|
|
||||||
readonly<bool> loaded;
|
auto power() -> void;
|
||||||
readonly<string> sha256;
|
auto reset() -> void;
|
||||||
|
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
struct Information {
|
struct Information {
|
||||||
string markup;
|
string markup;
|
||||||
string title;
|
string title;
|
||||||
} information;
|
} information;
|
||||||
|
|
||||||
string title();
|
|
||||||
|
|
||||||
struct Memory {
|
struct Memory {
|
||||||
unsigned id;
|
unsigned id;
|
||||||
string name;
|
string name;
|
||||||
};
|
};
|
||||||
vector<Memory> memory;
|
vector<Memory> memory;
|
||||||
|
|
||||||
void serialize(serializer&);
|
|
||||||
Cartridge();
|
|
||||||
|
|
||||||
//privileged:
|
//privileged:
|
||||||
Board *board;
|
Board* board = nullptr;
|
||||||
|
bool _loaded = false;
|
||||||
|
string _sha256;
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr);
|
auto prg_read(uint addr) -> uint8;
|
||||||
void prg_write(unsigned addr, uint8 data);
|
auto prg_write(uint addr, uint8 data) -> void;
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr);
|
auto chr_read(uint addr) -> uint8;
|
||||||
void chr_write(unsigned addr, uint8 data);
|
auto chr_write(uint addr, uint8 data) -> void;
|
||||||
|
|
||||||
//scanline() is for debugging purposes only:
|
//scanline() is for debugging purposes only:
|
||||||
//boards must detect scanline edges on their own
|
//boards must detect scanline edges on their own
|
||||||
void scanline(unsigned y);
|
auto scanline(uint y) -> void;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Cartridge cartridge;
|
extern Cartridge cartridge;
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
#include "vrc6.cpp"
|
#include "vrc6.cpp"
|
||||||
#include "vrc7.cpp"
|
#include "vrc7.cpp"
|
||||||
|
|
||||||
void Chip::tick() {
|
|
||||||
board.tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
Chip::Chip(Board& board) : board(board) {
|
Chip::Chip(Board& board) : board(board) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Chip::tick() -> void {
|
||||||
|
board.tick();
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
struct Board;
|
struct Board;
|
||||||
|
|
||||||
struct Chip {
|
struct Chip {
|
||||||
Board& board;
|
|
||||||
void tick();
|
|
||||||
Chip(Board& board);
|
Chip(Board& board);
|
||||||
|
auto tick() -> void;
|
||||||
|
|
||||||
|
Board& board;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,27 +1,9 @@
|
||||||
struct MMC1 : Chip {
|
struct MMC1 : Chip {
|
||||||
|
MMC1(Board& board) : Chip(board) {
|
||||||
|
revision = Revision::MMC1B2;
|
||||||
|
}
|
||||||
|
|
||||||
enum class Revision : unsigned {
|
auto main() -> void {
|
||||||
MMC1,
|
|
||||||
MMC1A,
|
|
||||||
MMC1B1,
|
|
||||||
MMC1B2,
|
|
||||||
MMC1B3,
|
|
||||||
MMC1C,
|
|
||||||
} revision;
|
|
||||||
|
|
||||||
unsigned writedelay;
|
|
||||||
unsigned shiftaddr;
|
|
||||||
unsigned 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;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
while(true) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
|
@ -30,11 +12,11 @@ void main() {
|
||||||
if(writedelay) writedelay--;
|
if(writedelay) writedelay--;
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned prg_addr(unsigned addr) {
|
auto prg_addr(uint addr) -> uint {
|
||||||
bool region = addr & 0x4000;
|
bool region = addr & 0x4000;
|
||||||
unsigned bank = (prg_bank & ~1) + region;
|
uint bank = (prg_bank & ~1) + region;
|
||||||
|
|
||||||
if(prg_size) {
|
if(prg_size) {
|
||||||
bank = (region == 0 ? 0x0 : 0xf);
|
bank = (region == 0 ? 0x0 : 0xf);
|
||||||
|
@ -42,25 +24,25 @@ unsigned prg_addr(unsigned addr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (bank << 14) | (addr & 0x3fff);
|
return (bank << 14) | (addr & 0x3fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned chr_addr(unsigned addr) {
|
auto chr_addr(uint addr) -> uint {
|
||||||
bool region = addr & 0x1000;
|
bool region = addr & 0x1000;
|
||||||
unsigned bank = chr_bank[region];
|
uint bank = chr_bank[region];
|
||||||
if(chr_mode == 0) bank = (chr_bank[0] & ~1) | region;
|
if(chr_mode == 0) bank = (chr_bank[0] & ~1) | region;
|
||||||
return (bank << 12) | (addr & 0x0fff);
|
return (bank << 12) | (addr & 0x0fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ciram_addr(unsigned addr) {
|
auto ciram_addr(uint addr) -> uint {
|
||||||
switch(mirror) {
|
switch(mirror) {
|
||||||
case 0: return 0x0000 | (addr & 0x03ff);
|
case 0: return 0x0000 | (addr & 0x03ff);
|
||||||
case 1: return 0x0400 | (addr & 0x03ff);
|
case 1: return 0x0400 | (addr & 0x03ff);
|
||||||
case 2: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
case 2: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||||
case 3: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
case 3: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mmio_write(unsigned addr, uint8 data) {
|
auto mmio_write(uint addr, uint8 data) -> void {
|
||||||
if(writedelay) return;
|
if(writedelay) return;
|
||||||
writedelay = 2;
|
writedelay = 2;
|
||||||
|
|
||||||
|
@ -95,12 +77,12 @@ void mmio_write(unsigned addr, uint8 data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
writedelay = 0;
|
writedelay = 0;
|
||||||
shiftaddr = 0;
|
shiftaddr = 0;
|
||||||
shiftdata = 0;
|
shiftdata = 0;
|
||||||
|
@ -113,9 +95,9 @@ void reset() {
|
||||||
chr_bank[1] = 1;
|
chr_bank[1] = 1;
|
||||||
ram_disable = 0;
|
ram_disable = 0;
|
||||||
prg_bank = 0;
|
prg_bank = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
s.integer(writedelay);
|
s.integer(writedelay);
|
||||||
s.integer(shiftaddr);
|
s.integer(shiftaddr);
|
||||||
s.integer(shiftdata);
|
s.integer(shiftdata);
|
||||||
|
@ -127,10 +109,26 @@ void serialize(serializer& s) {
|
||||||
s.array(chr_bank);
|
s.array(chr_bank);
|
||||||
s.integer(ram_disable);
|
s.integer(ram_disable);
|
||||||
s.integer(prg_bank);
|
s.integer(prg_bank);
|
||||||
}
|
}
|
||||||
|
|
||||||
MMC1(Board& board) : Chip(board) {
|
enum class Revision : uint {
|
||||||
revision = Revision::MMC1B2;
|
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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,22 +1,8 @@
|
||||||
struct MMC3 : Chip {
|
struct MMC3 : Chip {
|
||||||
|
MMC3(Board& board) : Chip(board) {
|
||||||
|
}
|
||||||
|
|
||||||
bool chr_mode;
|
auto main() -> void {
|
||||||
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) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
|
@ -26,9 +12,9 @@ void main() {
|
||||||
cpu.set_irq_line(irq_line);
|
cpu.set_irq_line(irq_line);
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void irq_test(unsigned addr) {
|
auto irq_test(uint addr) -> void {
|
||||||
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
|
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
|
||||||
if(irq_delay == 0) {
|
if(irq_delay == 0) {
|
||||||
if(irq_counter == 0) {
|
if(irq_counter == 0) {
|
||||||
|
@ -40,9 +26,9 @@ void irq_test(unsigned addr) {
|
||||||
irq_delay = 6;
|
irq_delay = 6;
|
||||||
}
|
}
|
||||||
chr_abus = addr;
|
chr_abus = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned prg_addr(unsigned addr) const {
|
auto prg_addr(uint addr) const -> uint {
|
||||||
switch((addr >> 13) & 3) {
|
switch((addr >> 13) & 3) {
|
||||||
case 0:
|
case 0:
|
||||||
if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff);
|
if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff);
|
||||||
|
@ -55,9 +41,9 @@ unsigned prg_addr(unsigned addr) const {
|
||||||
case 3:
|
case 3:
|
||||||
return (0x3f << 13) | (addr & 0x1fff);
|
return (0x3f << 13) | (addr & 0x1fff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned chr_addr(unsigned addr) const {
|
auto chr_addr(uint addr) const -> uint {
|
||||||
if(chr_mode == 0) {
|
if(chr_mode == 0) {
|
||||||
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
||||||
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
||||||
|
@ -73,23 +59,23 @@ unsigned chr_addr(unsigned addr) const {
|
||||||
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
||||||
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ciram_addr(unsigned addr) const {
|
auto ciram_addr(uint addr) const -> uint {
|
||||||
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||||
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 ram_read(unsigned addr) {
|
auto ram_read(uint addr) -> uint8 {
|
||||||
if(ram_enable) return board.prgram.data[addr & 0x1fff];
|
if(ram_enable) return board.prgram.data[addr & 0x1fff];
|
||||||
return 0x00;
|
return 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ram_write(unsigned addr, uint8 data) {
|
auto ram_write(uint addr, uint8 data) -> void {
|
||||||
if(ram_enable && !ram_write_protect) board.prgram.data[addr & 0x1fff] = data;
|
if(ram_enable && !ram_write_protect) board.prgram.data[addr & 0x1fff] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reg_write(unsigned addr, uint8 data) {
|
auto reg_write(uint addr, uint8 data) -> void {
|
||||||
switch(addr & 0xe001) {
|
switch(addr & 0xe001) {
|
||||||
case 0x8000:
|
case 0x8000:
|
||||||
chr_mode = data & 0x80;
|
chr_mode = data & 0x80;
|
||||||
|
@ -136,12 +122,12 @@ void reg_write(unsigned addr, uint8 data) {
|
||||||
irq_enable = true;
|
irq_enable = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
chr_mode = 0;
|
chr_mode = 0;
|
||||||
prg_mode = 0;
|
prg_mode = 0;
|
||||||
bank_select = 0;
|
bank_select = 0;
|
||||||
|
@ -163,9 +149,9 @@ void reset() {
|
||||||
irq_line = 0;
|
irq_line = 0;
|
||||||
|
|
||||||
chr_abus = 0;
|
chr_abus = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
s.integer(chr_mode);
|
s.integer(chr_mode);
|
||||||
s.integer(prg_mode);
|
s.integer(prg_mode);
|
||||||
s.integer(bank_select);
|
s.integer(bank_select);
|
||||||
|
@ -181,9 +167,21 @@ void serialize(serializer& s) {
|
||||||
s.integer(irq_line);
|
s.integer(irq_line);
|
||||||
|
|
||||||
s.integer(chr_abus);
|
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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,64 +1,9 @@
|
||||||
struct MMC5 : Chip {
|
struct MMC5 : Chip {
|
||||||
|
MMC5(Board& board) : Chip(board) {
|
||||||
|
revision = Revision::MMC5;
|
||||||
|
}
|
||||||
|
|
||||||
enum class Revision : unsigned {
|
auto main() -> void {
|
||||||
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) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
|
@ -70,15 +15,15 @@ void main() {
|
||||||
cpu.set_irq_line(irq_enable && irq_pending);
|
cpu.set_irq_line(irq_enable && irq_pending);
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void scanline(unsigned y) {
|
auto scanline(uint y) -> void {
|
||||||
//used for testing only, to verify MMC5 scanline detection is accurate:
|
//used for testing only, to verify MMC5 scanline detection is accurate:
|
||||||
//if(y != vcounter && y <= 240) print(y, " vs ", vcounter, "\n");
|
//if(y != vcounter && y <= 240) print(y, " vs ", vcounter, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 prg_access(bool write, unsigned addr, uint8 data = 0x00) {
|
auto prg_access(bool write, uint addr, uint8 data = 0x00) -> uint8 {
|
||||||
unsigned bank;
|
uint bank;
|
||||||
|
|
||||||
if((addr & 0xe000) == 0x6000) {
|
if((addr & 0xe000) == 0x6000) {
|
||||||
bank = (ram_select << 2) | ram_bank;
|
bank = (ram_select << 2) | ram_bank;
|
||||||
|
@ -123,9 +68,9 @@ uint8 prg_access(bool write, unsigned addr, uint8 data = 0x00) {
|
||||||
}
|
}
|
||||||
return 0x00;
|
return 0x00;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
auto prg_read(uint addr) -> uint8 {
|
||||||
if((addr & 0xfc00) == 0x5c00) {
|
if((addr & 0xfc00) == 0x5c00) {
|
||||||
if(exram_mode >= 2) return exram[addr & 0x03ff];
|
if(exram_mode >= 2) return exram[addr & 0x03ff];
|
||||||
return cpu.mdr();
|
return cpu.mdr();
|
||||||
|
@ -144,9 +89,9 @@ uint8 prg_read(unsigned addr) {
|
||||||
case 0x5205: return (multiplier * multiplicand) >> 0;
|
case 0x5205: return (multiplier * multiplicand) >> 0;
|
||||||
case 0x5206: return (multiplier * multiplicand) >> 8;
|
case 0x5206: return (multiplier * multiplicand) >> 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
auto prg_write(uint addr, uint8 data) -> void {
|
||||||
if((addr & 0xfc00) == 0x5c00) {
|
if((addr & 0xfc00) == 0x5c00) {
|
||||||
//writes 0x00 *during* Vblank (not during screen rendering ...)
|
//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 == 0 || exram_mode == 1) exram[addr & 0x03ff] = in_frame ? data : 0x00;
|
||||||
|
@ -254,9 +199,9 @@ void prg_write(unsigned addr, uint8 data) {
|
||||||
multiplier = data;
|
multiplier = data;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned chr_sprite_addr(unsigned addr) {
|
auto chr_sprite_addr(uint addr) -> uint {
|
||||||
if(chr_mode == 0) {
|
if(chr_mode == 0) {
|
||||||
auto bank = chr_sprite_bank[7];
|
auto bank = chr_sprite_bank[7];
|
||||||
return (bank * 0x2000) + (addr & 0x1fff);
|
return (bank * 0x2000) + (addr & 0x1fff);
|
||||||
|
@ -276,9 +221,9 @@ unsigned chr_sprite_addr(unsigned addr) {
|
||||||
auto bank = chr_sprite_bank[(addr / 0x0400)];
|
auto bank = chr_sprite_bank[(addr / 0x0400)];
|
||||||
return (bank * 0x0400) + (addr & 0x03ff);
|
return (bank * 0x0400) + (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned chr_bg_addr(unsigned addr) {
|
auto chr_bg_addr(uint addr) -> uint {
|
||||||
addr &= 0x0fff;
|
addr &= 0x0fff;
|
||||||
|
|
||||||
if(chr_mode == 0) {
|
if(chr_mode == 0) {
|
||||||
|
@ -300,17 +245,17 @@ unsigned chr_bg_addr(unsigned addr) {
|
||||||
auto bank = chr_bg_bank[(addr / 0x0400)];
|
auto bank = chr_bg_bank[(addr / 0x0400)];
|
||||||
return (bank * 0x0400) + (addr & 0x03ff);
|
return (bank * 0x0400) + (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned chr_vs_addr(unsigned addr) {
|
auto chr_vs_addr(uint addr) -> uint {
|
||||||
return (vs_bank * 0x1000) + (addr & 0x0ff8) + (vs_vpos & 7);
|
return (vs_bank * 0x1000) + (addr & 0x0ff8) + (vs_vpos & 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
void blank() {
|
auto blank() -> void {
|
||||||
in_frame = false;
|
in_frame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void scanline() {
|
auto scanline() -> void {
|
||||||
hcounter = 0;
|
hcounter = 0;
|
||||||
|
|
||||||
if(in_frame == false) {
|
if(in_frame == false) {
|
||||||
|
@ -323,9 +268,9 @@ void scanline() {
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu_cycle_counter = 0;
|
cpu_cycle_counter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 ciram_read(unsigned addr) {
|
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 / 8 * 32 + vs_hpos / 8];
|
||||||
if(vs_fetch && (hcounter & 2) != 0) return exram[vs_vpos / 32 * 8 + vs_hpos / 32 + 0x03c0];
|
if(vs_fetch && (hcounter & 2) != 0) return exram[vs_vpos / 32 * 8 + vs_hpos / 32 + 0x03c0];
|
||||||
|
|
||||||
|
@ -335,9 +280,9 @@ uint8 ciram_read(unsigned addr) {
|
||||||
case 2: return exram_mode < 2 ? exram[addr & 0x03ff] : 0x00;
|
case 2: return exram_mode < 2 ? exram[addr & 0x03ff] : 0x00;
|
||||||
case 3: return (hcounter & 2) == 0 ? fillmode_tile : fillmode_color;
|
case 3: return (hcounter & 2) == 0 ? fillmode_tile : fillmode_color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
auto chr_read(uint addr) -> uint8 {
|
||||||
chr_access[0] = chr_access[1];
|
chr_access[0] = chr_access[1];
|
||||||
chr_access[1] = chr_access[2];
|
chr_access[1] = chr_access[2];
|
||||||
chr_access[2] = chr_access[3];
|
chr_access[2] = chr_access[3];
|
||||||
|
@ -383,9 +328,9 @@ uint8 chr_read(unsigned addr) {
|
||||||
|
|
||||||
hcounter += 2;
|
hcounter += 2;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void chr_write(unsigned addr, uint8 data) {
|
auto chr_write(uint addr, uint8 data) -> void {
|
||||||
if(addr & 0x2000) {
|
if(addr & 0x2000) {
|
||||||
switch(nametable_mode[(addr >> 10) & 3]) {
|
switch(nametable_mode[(addr >> 10) & 3]) {
|
||||||
case 0: return ppu.ciram_write(0x0000 | (addr & 0x03ff), data);
|
case 0: return ppu.ciram_write(0x0000 | (addr & 0x03ff), data);
|
||||||
|
@ -393,12 +338,12 @@ void chr_write(unsigned addr, uint8 data) {
|
||||||
case 2: exram[addr & 0x03ff] = data; break;
|
case 2: exram[addr & 0x03ff] = data; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
for(auto& n : exram) n = 0xff;
|
for(auto& n : exram) n = 0xff;
|
||||||
|
|
||||||
prg_mode = 3;
|
prg_mode = 3;
|
||||||
|
@ -443,9 +388,9 @@ void reset() {
|
||||||
vs_fetch = 0;
|
vs_fetch = 0;
|
||||||
vs_vpos = 0;
|
vs_vpos = 0;
|
||||||
vs_hpos = 0;
|
vs_hpos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
s.array(exram);
|
s.array(exram);
|
||||||
|
|
||||||
s.integer(prg_mode);
|
s.integer(prg_mode);
|
||||||
|
@ -488,10 +433,63 @@ void serialize(serializer& s) {
|
||||||
s.integer(vs_fetch);
|
s.integer(vs_fetch);
|
||||||
s.integer(vs_vpos);
|
s.integer(vs_vpos);
|
||||||
s.integer(vs_hpos);
|
s.integer(vs_hpos);
|
||||||
}
|
}
|
||||||
|
|
||||||
MMC5(Board& board) : Chip(board) {
|
enum class Revision : uint {
|
||||||
revision = Revision::MMC5;
|
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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,23 +1,8 @@
|
||||||
struct MMC6 : Chip {
|
struct MMC6 : Chip {
|
||||||
|
MMC6(Board& board) : Chip(board) {
|
||||||
|
}
|
||||||
|
|
||||||
bool chr_mode;
|
auto main() -> void {
|
||||||
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) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
|
@ -27,9 +12,9 @@ void main() {
|
||||||
cpu.set_irq_line(irq_line);
|
cpu.set_irq_line(irq_line);
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void irq_test(unsigned addr) {
|
auto irq_test(uint addr) -> void {
|
||||||
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
|
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
|
||||||
if(irq_delay == 0) {
|
if(irq_delay == 0) {
|
||||||
if(irq_counter == 0) {
|
if(irq_counter == 0) {
|
||||||
|
@ -41,9 +26,9 @@ void irq_test(unsigned addr) {
|
||||||
irq_delay = 6;
|
irq_delay = 6;
|
||||||
}
|
}
|
||||||
chr_abus = addr;
|
chr_abus = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned prg_addr(unsigned addr) const {
|
auto prg_addr(uint addr) const -> uint {
|
||||||
switch((addr >> 13) & 3) {
|
switch((addr >> 13) & 3) {
|
||||||
case 0:
|
case 0:
|
||||||
if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff);
|
if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff);
|
||||||
|
@ -56,9 +41,9 @@ unsigned prg_addr(unsigned addr) const {
|
||||||
case 3:
|
case 3:
|
||||||
return (0x3f << 13) | (addr & 0x1fff);
|
return (0x3f << 13) | (addr & 0x1fff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned chr_addr(unsigned addr) const {
|
auto chr_addr(uint addr) const -> uint {
|
||||||
if(chr_mode == 0) {
|
if(chr_mode == 0) {
|
||||||
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
||||||
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
||||||
|
@ -74,29 +59,29 @@ unsigned chr_addr(unsigned addr) const {
|
||||||
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
||||||
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ciram_addr(unsigned addr) const {
|
auto ciram_addr(uint addr) const -> uint {
|
||||||
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||||
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 ram_read(unsigned addr) {
|
auto ram_read(uint addr) -> uint8 {
|
||||||
if(ram_enable == false) return cpu.mdr();
|
if(ram_enable == false) return cpu.mdr();
|
||||||
if(ram_readable[0] == false && ram_readable[1] == false) return cpu.mdr();
|
if(ram_readable[0] == false && ram_readable[1] == false) return cpu.mdr();
|
||||||
bool region = addr & 0x0200;
|
bool region = addr & 0x0200;
|
||||||
if(ram_readable[region] == false) return 0x00;
|
if(ram_readable[region] == false) return 0x00;
|
||||||
return board.prgram.read((region * 0x0200) + (addr & 0x01ff));
|
return board.prgram.read((region * 0x0200) + (addr & 0x01ff));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ram_write(unsigned addr, uint8 data) {
|
auto ram_write(uint addr, uint8 data) -> void {
|
||||||
if(ram_enable == false) return;
|
if(ram_enable == false) return;
|
||||||
bool region = addr & 0x0200;
|
bool region = addr & 0x0200;
|
||||||
if(ram_writable[region] == false) return;
|
if(ram_writable[region] == false) return;
|
||||||
return board.prgram.write((region * 0x0200) + (addr & 0x01ff), data);
|
return board.prgram.write((region * 0x0200) + (addr & 0x01ff), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reg_write(unsigned addr, uint8 data) {
|
auto reg_write(uint addr, uint8 data) -> void {
|
||||||
switch(addr & 0xe001) {
|
switch(addr & 0xe001) {
|
||||||
case 0x8000:
|
case 0x8000:
|
||||||
chr_mode = data & 0x80;
|
chr_mode = data & 0x80;
|
||||||
|
@ -151,12 +136,12 @@ void reg_write(unsigned addr, uint8 data) {
|
||||||
irq_enable = true;
|
irq_enable = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
chr_mode = 0;
|
chr_mode = 0;
|
||||||
prg_mode = 0;
|
prg_mode = 0;
|
||||||
ram_enable = 0;
|
ram_enable = 0;
|
||||||
|
@ -173,9 +158,9 @@ void reset() {
|
||||||
irq_line = 0;
|
irq_line = 0;
|
||||||
|
|
||||||
chr_abus = 0;
|
chr_abus = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
s.integer(chr_mode);
|
s.integer(chr_mode);
|
||||||
s.integer(prg_mode);
|
s.integer(prg_mode);
|
||||||
s.integer(ram_enable);
|
s.integer(ram_enable);
|
||||||
|
@ -192,9 +177,22 @@ void serialize(serializer& s) {
|
||||||
s.integer(irq_line);
|
s.integer(irq_line);
|
||||||
|
|
||||||
s.integer(chr_abus);
|
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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,33 +1,30 @@
|
||||||
struct VRC1 : Chip {
|
struct VRC1 : Chip {
|
||||||
|
VRC1(Board& board) : Chip(board) {
|
||||||
|
}
|
||||||
|
|
||||||
uint4 prg_bank[3];
|
auto prg_addr(uint addr) const -> uint {
|
||||||
uint4 chr_banklo[2];
|
uint bank = 0x0f;
|
||||||
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) == 0x8000) bank = prg_bank[0];
|
||||||
if((addr & 0xe000) == 0xa000) bank = prg_bank[1];
|
if((addr & 0xe000) == 0xa000) bank = prg_bank[1];
|
||||||
if((addr & 0xe000) == 0xc000) bank = prg_bank[2];
|
if((addr & 0xe000) == 0xc000) bank = prg_bank[2];
|
||||||
return (bank * 0x2000) + (addr & 0x1fff);
|
return (bank * 0x2000) + (addr & 0x1fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned chr_addr(unsigned addr) const {
|
auto chr_addr(uint addr) const -> uint {
|
||||||
unsigned bank = chr_banklo[(bool)(addr & 0x1000)];
|
uint bank = chr_banklo[(bool)(addr & 0x1000)];
|
||||||
bank |= chr_bankhi[(bool)(addr & 0x1000)] << 4;
|
bank |= chr_bankhi[(bool)(addr & 0x1000)] << 4;
|
||||||
return (bank * 0x1000) + (addr & 0x0fff);
|
return (bank * 0x1000) + (addr & 0x0fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ciram_addr(unsigned addr) const {
|
auto ciram_addr(uint addr) const -> uint {
|
||||||
switch(mirror) {
|
switch(mirror) {
|
||||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||||
}
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reg_write(unsigned addr, uint8 data) {
|
auto reg_write(uint addr, uint8 data) -> void {
|
||||||
switch(addr & 0xf000) {
|
switch(addr & 0xf000) {
|
||||||
case 0x8000:
|
case 0x8000:
|
||||||
prg_bank[0] = data & 0x0f;
|
prg_bank[0] = data & 0x0f;
|
||||||
|
@ -55,26 +52,27 @@ void reg_write(unsigned addr, uint8 data) {
|
||||||
chr_banklo[1] = data & 0x0f;
|
chr_banklo[1] = data & 0x0f;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
for(auto& n : prg_bank) n = 0;
|
for(auto& n : prg_bank) n = 0;
|
||||||
for(auto& n : chr_banklo) n = 0;
|
for(auto& n : chr_banklo) n = 0;
|
||||||
for(auto& n : chr_bankhi) n = 0;
|
for(auto& n : chr_bankhi) n = 0;
|
||||||
mirror = 0;
|
mirror = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
for(auto& n : prg_bank) s.integer(n);
|
for(auto& n : prg_bank) s.integer(n);
|
||||||
for(auto& n : chr_banklo) s.integer(n);
|
for(auto& n : chr_banklo) s.integer(n);
|
||||||
for(auto& n : chr_bankhi) s.integer(n);
|
for(auto& n : chr_bankhi) s.integer(n);
|
||||||
s.integer(mirror);
|
s.integer(mirror);
|
||||||
}
|
}
|
||||||
|
|
||||||
VRC1(Board& board) : Chip(board) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
uint4 prg_bank[3];
|
||||||
|
uint4 chr_banklo[2];
|
||||||
|
bool chr_bankhi[2];
|
||||||
|
bool mirror;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
struct VRC2 : Chip {
|
struct VRC2 : Chip {
|
||||||
|
VRC2(Board& board) : Chip(board) {
|
||||||
|
}
|
||||||
|
|
||||||
uint5 prg_bank[2];
|
auto prg_addr(uint addr) const -> uint {
|
||||||
uint8 chr_bank[8];
|
uint bank;
|
||||||
uint2 mirror;
|
|
||||||
bool latch;
|
|
||||||
|
|
||||||
unsigned prg_addr(unsigned addr) const {
|
|
||||||
unsigned bank;
|
|
||||||
switch(addr & 0xe000) {
|
switch(addr & 0xe000) {
|
||||||
case 0x8000: bank = prg_bank[0]; break;
|
case 0x8000: bank = prg_bank[0]; break;
|
||||||
case 0xa000: bank = prg_bank[1]; break;
|
case 0xa000: bank = prg_bank[1]; break;
|
||||||
|
@ -14,14 +11,14 @@ unsigned prg_addr(unsigned addr) const {
|
||||||
case 0xe000: bank = 0x1f; break;
|
case 0xe000: bank = 0x1f; break;
|
||||||
}
|
}
|
||||||
return (bank * 0x2000) + (addr & 0x1fff);
|
return (bank * 0x2000) + (addr & 0x1fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned chr_addr(unsigned addr) const {
|
auto chr_addr(uint addr) const -> uint {
|
||||||
unsigned bank = chr_bank[addr / 0x0400];
|
uint bank = chr_bank[addr / 0x0400];
|
||||||
return (bank * 0x0400) + (addr & 0x03ff);
|
return (bank * 0x0400) + (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ciram_addr(unsigned addr) const {
|
auto ciram_addr(uint addr) const -> uint {
|
||||||
switch(mirror) {
|
switch(mirror) {
|
||||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||||
|
@ -29,25 +26,25 @@ unsigned ciram_addr(unsigned addr) const {
|
||||||
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||||
}
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 ram_read(unsigned addr) {
|
auto ram_read(uint addr) -> uint8 {
|
||||||
if(board.prgram.size == 0) {
|
if(board.prgram.size == 0) {
|
||||||
if((addr & 0xf000) == 0x6000) return cpu.mdr() | latch;
|
if((addr & 0xf000) == 0x6000) return cpu.mdr() | latch;
|
||||||
return cpu.mdr();
|
return cpu.mdr();
|
||||||
}
|
}
|
||||||
return board.prgram.read(addr & 0x1fff);
|
return board.prgram.read(addr & 0x1fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ram_write(unsigned addr, uint8 data) {
|
auto ram_write(uint addr, uint8 data) -> void {
|
||||||
if(board.prgram.size == 0) {
|
if(board.prgram.size == 0) {
|
||||||
if((addr & 0xf000) == 0x6000) latch = data & 0x01;
|
if((addr & 0xf000) == 0x6000) latch = data & 0x01;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return board.prgram.write(addr & 0x1fff, data);
|
return board.prgram.write(addr & 0x1fff, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reg_write(unsigned addr, uint8 data) {
|
auto reg_write(uint addr, uint8 data) -> void {
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
||||||
prg_bank[0] = data & 0x1f;
|
prg_bank[0] = data & 0x1f;
|
||||||
|
@ -85,26 +82,27 @@ void reg_write(unsigned addr, uint8 data) {
|
||||||
case 0xe002: chr_bank[7] = (chr_bank[7] & 0xf0) | ((data & 0x0f) << 0); 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 0xe003: chr_bank[7] = (chr_bank[7] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
for(auto& n : prg_bank) n = 0;
|
for(auto& n : prg_bank) n = 0;
|
||||||
for(auto& n : chr_bank) n = 0;
|
for(auto& n : chr_bank) n = 0;
|
||||||
mirror = 0;
|
mirror = 0;
|
||||||
latch = 0;
|
latch = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
for(auto& n : prg_bank) s.integer(n);
|
for(auto& n : prg_bank) s.integer(n);
|
||||||
for(auto& n : chr_bank) s.integer(n);
|
for(auto& n : chr_bank) s.integer(n);
|
||||||
s.integer(mirror);
|
s.integer(mirror);
|
||||||
s.integer(latch);
|
s.integer(latch);
|
||||||
}
|
}
|
||||||
|
|
||||||
VRC2(Board& board) : Chip(board) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
uint5 prg_bank[2];
|
||||||
|
uint8 chr_bank[8];
|
||||||
|
uint2 mirror;
|
||||||
|
bool latch;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,19 +1,8 @@
|
||||||
struct VRC3 : Chip {
|
struct VRC3 : Chip {
|
||||||
|
VRC3(Board& board) : Chip(board) {
|
||||||
|
}
|
||||||
|
|
||||||
uint4 prg_bank;
|
auto main() -> void {
|
||||||
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) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
|
@ -39,14 +28,14 @@ void main() {
|
||||||
cpu.set_irq_line(irq_line);
|
cpu.set_irq_line(irq_line);
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned prg_addr(unsigned addr) const {
|
auto prg_addr(uint addr) const -> uint {
|
||||||
unsigned bank = (addr < 0xc000 ? (unsigned)prg_bank : 0x0f);
|
uint bank = (addr < 0xc000 ? (uint)prg_bank : 0x0f);
|
||||||
return (bank * 0x4000) + (addr & 0x3fff);
|
return (bank * 0x4000) + (addr & 0x3fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reg_write(unsigned addr, uint8 data) {
|
auto reg_write(uint addr, uint8 data) -> void {
|
||||||
switch(addr & 0xf000) {
|
switch(addr & 0xf000) {
|
||||||
case 0x8000: irq_latch = (irq_latch & 0xfff0) | ((data & 0x0f) << 0); break;
|
case 0x8000: irq_latch = (irq_latch & 0xfff0) | ((data & 0x0f) << 0); break;
|
||||||
case 0x9000: irq_latch = (irq_latch & 0xff0f) | ((data & 0x0f) << 4); break;
|
case 0x9000: irq_latch = (irq_latch & 0xff0f) | ((data & 0x0f) << 4); break;
|
||||||
|
@ -69,12 +58,12 @@ void reg_write(unsigned addr, uint8 data) {
|
||||||
prg_bank = data & 0x0f;
|
prg_bank = data & 0x0f;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
prg_bank = 0;
|
prg_bank = 0;
|
||||||
irq_mode = 0;
|
irq_mode = 0;
|
||||||
irq_enable = 0;
|
irq_enable = 0;
|
||||||
|
@ -82,9 +71,9 @@ void reset() {
|
||||||
irq_latch = 0;
|
irq_latch = 0;
|
||||||
irq_counter.w = 0;
|
irq_counter.w = 0;
|
||||||
irq_line = 0;
|
irq_line = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
s.integer(prg_bank);
|
s.integer(prg_bank);
|
||||||
s.integer(irq_mode);
|
s.integer(irq_mode);
|
||||||
s.integer(irq_enable);
|
s.integer(irq_enable);
|
||||||
|
@ -92,9 +81,18 @@ void serialize(serializer& s) {
|
||||||
s.integer(irq_latch);
|
s.integer(irq_latch);
|
||||||
s.integer(irq_counter.w);
|
s.integer(irq_counter.w);
|
||||||
s.integer(irq_line);
|
s.integer(irq_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
VRC3(Board& board) : Chip(board) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,20 +1,8 @@
|
||||||
struct VRC4 : Chip {
|
struct VRC4 : Chip {
|
||||||
|
VRC4(Board& board) : Chip(board) {
|
||||||
|
}
|
||||||
|
|
||||||
bool prg_mode;
|
auto main() -> void {
|
||||||
uint5 prg_bank[2];
|
|
||||||
uint2 mirror;
|
|
||||||
uint8 chr_bank[8];
|
|
||||||
|
|
||||||
uint8 irq_latch;
|
|
||||||
bool irq_mode;
|
|
||||||
bool irq_enable;
|
|
||||||
bool irq_acknowledge;
|
|
||||||
|
|
||||||
uint8 irq_counter;
|
|
||||||
signed irq_scalar;
|
|
||||||
bool irq_line;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
while(true) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
|
@ -47,10 +35,10 @@ void main() {
|
||||||
cpu.set_irq_line(irq_line);
|
cpu.set_irq_line(irq_line);
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned prg_addr(unsigned addr) const {
|
auto prg_addr(uint addr) const -> uint {
|
||||||
unsigned bank = 0, banks = board.prgrom.size / 0x2000;
|
uint bank = 0, banks = board.prgrom.size / 0x2000;
|
||||||
switch(addr & 0xe000) {
|
switch(addr & 0xe000) {
|
||||||
case 0x8000: bank = prg_mode == 0 ? (unsigned)prg_bank[0] : banks - 2; break;
|
case 0x8000: bank = prg_mode == 0 ? (unsigned)prg_bank[0] : banks - 2; break;
|
||||||
case 0xa000: bank = prg_bank[1]; break;
|
case 0xa000: bank = prg_bank[1]; break;
|
||||||
|
@ -58,14 +46,14 @@ unsigned prg_addr(unsigned addr) const {
|
||||||
case 0xe000: bank = banks - 1; break;
|
case 0xe000: bank = banks - 1; break;
|
||||||
}
|
}
|
||||||
return (bank * 0x2000) + (addr & 0x1fff);
|
return (bank * 0x2000) + (addr & 0x1fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned chr_addr(unsigned addr) const {
|
auto chr_addr(uint addr) const -> uint {
|
||||||
unsigned bank = chr_bank[addr / 0x0400];
|
uint bank = chr_bank[addr / 0x0400];
|
||||||
return (bank * 0x0400) + (addr & 0x03ff);
|
return (bank * 0x0400) + (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ciram_addr(unsigned addr) const {
|
auto ciram_addr(uint addr) const -> uint {
|
||||||
switch(mirror) {
|
switch(mirror) {
|
||||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||||
|
@ -73,9 +61,9 @@ unsigned ciram_addr(unsigned addr) const {
|
||||||
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||||
}
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reg_write(unsigned addr, uint8 data) {
|
auto reg_write(uint addr, uint8 data) -> void {
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
||||||
prg_bank[0] = data & 0x1f;
|
prg_bank[0] = data & 0x1f;
|
||||||
|
@ -141,12 +129,12 @@ void reg_write(unsigned addr, uint8 data) {
|
||||||
irq_line = 0;
|
irq_line = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
prg_mode = 0;
|
prg_mode = 0;
|
||||||
for(auto& n : prg_bank) n = 0;
|
for(auto& n : prg_bank) n = 0;
|
||||||
mirror = 0;
|
mirror = 0;
|
||||||
|
@ -160,9 +148,9 @@ void reset() {
|
||||||
irq_counter = 0;
|
irq_counter = 0;
|
||||||
irq_scalar = 0;
|
irq_scalar = 0;
|
||||||
irq_line = 0;
|
irq_line = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
s.integer(prg_mode);
|
s.integer(prg_mode);
|
||||||
for(auto& n : prg_bank) s.integer(n);
|
for(auto& n : prg_bank) s.integer(n);
|
||||||
s.integer(mirror);
|
s.integer(mirror);
|
||||||
|
@ -176,9 +164,19 @@ void serialize(serializer& s) {
|
||||||
s.integer(irq_counter);
|
s.integer(irq_counter);
|
||||||
s.integer(irq_scalar);
|
s.integer(irq_scalar);
|
||||||
s.integer(irq_line);
|
s.integer(irq_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
VRC4(Board& board) : Chip(board) {
|
bool prg_mode;
|
||||||
}
|
uint5 prg_bank[2];
|
||||||
|
uint2 mirror;
|
||||||
|
uint8 chr_bank[8];
|
||||||
|
|
||||||
|
uint8 irq_latch;
|
||||||
|
bool irq_mode;
|
||||||
|
bool irq_enable;
|
||||||
|
bool irq_acknowledge;
|
||||||
|
|
||||||
|
uint8 irq_counter;
|
||||||
|
int irq_scalar;
|
||||||
|
bool irq_line;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,29 +1,9 @@
|
||||||
struct VRC6 : Chip {
|
struct VRC6 : Chip {
|
||||||
|
VRC6(Board& board) : Chip(board) {
|
||||||
|
}
|
||||||
|
|
||||||
uint8 prg_bank[2];
|
struct Pulse {
|
||||||
uint8 chr_bank[8];
|
auto clock() -> void {
|
||||||
uint2 mirror;
|
|
||||||
uint8 irq_latch;
|
|
||||||
bool irq_mode;
|
|
||||||
bool irq_enable;
|
|
||||||
bool irq_acknowledge;
|
|
||||||
|
|
||||||
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) {
|
if(--divider == 0) {
|
||||||
divider = frequency + 1;
|
divider = frequency + 1;
|
||||||
cycle++;
|
cycle++;
|
||||||
|
@ -33,7 +13,7 @@ struct Pulse {
|
||||||
if(enable == false) output = 0;
|
if(enable == false) output = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
s.integer(mode);
|
s.integer(mode);
|
||||||
s.integer(duty);
|
s.integer(duty);
|
||||||
s.integer(volume);
|
s.integer(volume);
|
||||||
|
@ -44,20 +24,20 @@ struct Pulse {
|
||||||
s.integer(cycle);
|
s.integer(cycle);
|
||||||
s.integer(output);
|
s.integer(output);
|
||||||
}
|
}
|
||||||
} pulse1, pulse2;
|
|
||||||
|
|
||||||
struct Sawtooth {
|
bool mode;
|
||||||
uint6 rate;
|
uint3 duty;
|
||||||
|
uint4 volume;
|
||||||
bool enable;
|
bool enable;
|
||||||
uint12 frequency;
|
uint12 frequency;
|
||||||
|
|
||||||
uint12 divider;
|
uint12 divider;
|
||||||
uint1 phase;
|
uint4 cycle;
|
||||||
uint3 stage;
|
uint4 output;
|
||||||
uint8 accumulator;
|
} pulse1, pulse2;
|
||||||
uint5 output;
|
|
||||||
|
|
||||||
void clock() {
|
struct Sawtooth {
|
||||||
|
auto clock() -> void {
|
||||||
if(--divider == 0) {
|
if(--divider == 0) {
|
||||||
divider = frequency + 1;
|
divider = frequency + 1;
|
||||||
if(++phase == 0) {
|
if(++phase == 0) {
|
||||||
|
@ -73,7 +53,7 @@ struct Sawtooth {
|
||||||
if(enable == false) output = 0;
|
if(enable == false) output = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
s.integer(rate);
|
s.integer(rate);
|
||||||
s.integer(enable);
|
s.integer(enable);
|
||||||
s.integer(frequency);
|
s.integer(frequency);
|
||||||
|
@ -84,9 +64,19 @@ struct Sawtooth {
|
||||||
s.integer(accumulator);
|
s.integer(accumulator);
|
||||||
s.integer(output);
|
s.integer(output);
|
||||||
}
|
}
|
||||||
} sawtooth;
|
|
||||||
|
|
||||||
void main() {
|
uint6 rate;
|
||||||
|
bool enable;
|
||||||
|
uint12 frequency;
|
||||||
|
|
||||||
|
uint12 divider;
|
||||||
|
uint1 phase;
|
||||||
|
uint3 stage;
|
||||||
|
uint8 accumulator;
|
||||||
|
uint5 output;
|
||||||
|
} sawtooth;
|
||||||
|
|
||||||
|
auto main() -> void {
|
||||||
while(true) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
|
@ -120,42 +110,42 @@ void main() {
|
||||||
pulse1.clock();
|
pulse1.clock();
|
||||||
pulse2.clock();
|
pulse2.clock();
|
||||||
sawtooth.clock();
|
sawtooth.clock();
|
||||||
signed output = (pulse1.output + pulse2.output + sawtooth.output) << 7;
|
int output = (pulse1.output + pulse2.output + sawtooth.output) << 7;
|
||||||
apu.set_sample(-output);
|
apu.set_sample(-output);
|
||||||
|
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned prg_addr(unsigned addr) const {
|
auto prg_addr(uint addr) const -> uint {
|
||||||
if((addr & 0xc000) == 0x8000) return (prg_bank[0] << 14) | (addr & 0x3fff);
|
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) == 0xc000) return (prg_bank[1] << 13) | (addr & 0x1fff);
|
||||||
if((addr & 0xe000) == 0xe000) return ( 0xff << 13) | (addr & 0x1fff);
|
if((addr & 0xe000) == 0xe000) return ( 0xff << 13) | (addr & 0x1fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned chr_addr(unsigned addr) const {
|
auto chr_addr(uint addr) const -> uint {
|
||||||
unsigned bank = chr_bank[(addr >> 10) & 7];
|
uint bank = chr_bank[(addr >> 10) & 7];
|
||||||
return (bank << 10) | (addr & 0x03ff);
|
return (bank << 10) | (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ciram_addr(unsigned addr) const {
|
auto ciram_addr(uint addr) const -> uint {
|
||||||
switch(mirror) {
|
switch(mirror) {
|
||||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||||
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
||||||
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 ram_read(unsigned addr) {
|
auto ram_read(uint addr) -> uint8 {
|
||||||
return board.prgram.data[addr & 0x1fff];
|
return board.prgram.data[addr & 0x1fff];
|
||||||
}
|
}
|
||||||
|
|
||||||
void ram_write(unsigned addr, uint8 data) {
|
auto ram_write(uint addr, uint8 data) -> void {
|
||||||
board.prgram.data[addr & 0x1fff] = data;
|
board.prgram.data[addr & 0x1fff] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reg_write(unsigned addr, uint8 data) {
|
auto reg_write(uint addr, uint8 data) -> void {
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
||||||
prg_bank[0] = data;
|
prg_bank[0] = data;
|
||||||
|
@ -240,12 +230,12 @@ void reg_write(unsigned addr, uint8 data) {
|
||||||
irq_line = 0;
|
irq_line = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
prg_bank[0] = 0;
|
prg_bank[0] = 0;
|
||||||
prg_bank[1] = 0;
|
prg_bank[1] = 0;
|
||||||
chr_bank[0] = 0;
|
chr_bank[0] = 0;
|
||||||
|
@ -295,9 +285,9 @@ void reset() {
|
||||||
sawtooth.stage = 0;
|
sawtooth.stage = 0;
|
||||||
sawtooth.accumulator = 0;
|
sawtooth.accumulator = 0;
|
||||||
sawtooth.output = 0;
|
sawtooth.output = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
pulse1.serialize(s);
|
pulse1.serialize(s);
|
||||||
pulse2.serialize(s);
|
pulse2.serialize(s);
|
||||||
sawtooth.serialize(s);
|
sawtooth.serialize(s);
|
||||||
|
@ -313,9 +303,17 @@ void serialize(serializer& s) {
|
||||||
s.integer(irq_counter);
|
s.integer(irq_counter);
|
||||||
s.integer(irq_scalar);
|
s.integer(irq_scalar);
|
||||||
s.integer(irq_line);
|
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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,21 +2,10 @@
|
||||||
//Yamaha YM2413 OPLL audio - not emulated
|
//Yamaha YM2413 OPLL audio - not emulated
|
||||||
|
|
||||||
struct VRC7 : Chip {
|
struct VRC7 : Chip {
|
||||||
|
VRC7(Board& board) : Chip(board) {
|
||||||
|
}
|
||||||
|
|
||||||
uint8 prg_bank[3];
|
auto main() -> void {
|
||||||
uint8 chr_bank[8];
|
|
||||||
uint2 mirror;
|
|
||||||
|
|
||||||
uint8 irq_latch;
|
|
||||||
bool irq_mode;
|
|
||||||
bool irq_enable;
|
|
||||||
bool irq_acknowledge;
|
|
||||||
|
|
||||||
uint8 irq_counter;
|
|
||||||
signed irq_scalar;
|
|
||||||
bool irq_line;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
while(true) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
|
@ -49,9 +38,9 @@ void main() {
|
||||||
|
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reg_write(unsigned addr, uint8 data) {
|
auto reg_write(uint addr, uint8 data) -> void {
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
case 0x8000: prg_bank[0] = data; break;
|
case 0x8000: prg_bank[0] = data; break;
|
||||||
case 0x8010: prg_bank[1] = data; break;
|
case 0x8010: prg_bank[1] = data; break;
|
||||||
|
@ -88,10 +77,10 @@ void reg_write(unsigned addr, uint8 data) {
|
||||||
irq_line = 0;
|
irq_line = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned prg_addr(unsigned addr) const {
|
auto prg_addr(uint addr) const -> uint {
|
||||||
unsigned bank = 0;
|
uint bank = 0;
|
||||||
switch(addr & 0xe000) {
|
switch(addr & 0xe000) {
|
||||||
case 0x8000: bank = prg_bank[0]; break;
|
case 0x8000: bank = prg_bank[0]; break;
|
||||||
case 0xa000: bank = prg_bank[1]; break;
|
case 0xa000: bank = prg_bank[1]; break;
|
||||||
|
@ -99,26 +88,26 @@ unsigned prg_addr(unsigned addr) const {
|
||||||
case 0xe000: bank = 0xff; break;
|
case 0xe000: bank = 0xff; break;
|
||||||
}
|
}
|
||||||
return (bank * 0x2000) + (addr & 0x1fff);
|
return (bank * 0x2000) + (addr & 0x1fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned chr_addr(unsigned addr) const {
|
auto chr_addr(uint addr) const -> uint {
|
||||||
unsigned bank = chr_bank[addr / 0x0400];
|
uint bank = chr_bank[addr / 0x0400];
|
||||||
return (bank * 0x0400) + (addr & 0x03ff);
|
return (bank * 0x0400) + (addr & 0x03ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ciram_addr(unsigned addr) const {
|
auto ciram_addr(uint addr) const -> uint {
|
||||||
switch(mirror) {
|
switch(mirror) {
|
||||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||||
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
||||||
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void power() {
|
auto power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
auto reset() -> void {
|
||||||
for(auto& n : prg_bank) n = 0;
|
for(auto& n : prg_bank) n = 0;
|
||||||
for(auto& n : chr_bank) n = 0;
|
for(auto& n : chr_bank) n = 0;
|
||||||
mirror = 0;
|
mirror = 0;
|
||||||
|
@ -131,9 +120,9 @@ void reset() {
|
||||||
irq_counter = 0;
|
irq_counter = 0;
|
||||||
irq_scalar = 0;
|
irq_scalar = 0;
|
||||||
irq_line = 0;
|
irq_line = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer& s) {
|
auto serialize(serializer& s) -> void {
|
||||||
s.array(prg_bank);
|
s.array(prg_bank);
|
||||||
s.array(chr_bank);
|
s.array(chr_bank);
|
||||||
s.integer(mirror);
|
s.integer(mirror);
|
||||||
|
@ -146,9 +135,18 @@ void serialize(serializer& s) {
|
||||||
s.integer(irq_counter);
|
s.integer(irq_counter);
|
||||||
s.integer(irq_scalar);
|
s.integer(irq_scalar);
|
||||||
s.integer(irq_line);
|
s.integer(irq_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
VRC7(Board& board) : Chip(board) {
|
uint8 prg_bank[3];
|
||||||
}
|
uint8 chr_bank[8];
|
||||||
|
uint2 mirror;
|
||||||
|
|
||||||
|
uint8 irq_latch;
|
||||||
|
bool irq_mode;
|
||||||
|
bool irq_enable;
|
||||||
|
bool irq_acknowledge;
|
||||||
|
|
||||||
|
uint8 irq_counter;
|
||||||
|
int irq_scalar;
|
||||||
|
bool irq_line;
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,19 +4,19 @@ namespace Famicom {
|
||||||
|
|
||||||
Cheat cheat;
|
Cheat cheat;
|
||||||
|
|
||||||
void Cheat::reset() {
|
auto Cheat::reset() -> void {
|
||||||
codes.reset();
|
codes.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cheat::append(unsigned addr, unsigned data) {
|
auto Cheat::append(uint addr, uint data) -> void {
|
||||||
codes.append({addr, Unused, data});
|
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});
|
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) {
|
for(auto& code : codes) {
|
||||||
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
|
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
|
||||||
return code.data;
|
return code.data;
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
struct Cheat {
|
struct Cheat {
|
||||||
struct Code {
|
struct Code {
|
||||||
unsigned addr;
|
uint addr;
|
||||||
unsigned comp;
|
uint comp;
|
||||||
unsigned data;
|
uint data;
|
||||||
};
|
};
|
||||||
vector<Code> codes;
|
vector<Code> codes;
|
||||||
enum : unsigned { Unused = ~0u };
|
enum : uint { Unused = ~0u };
|
||||||
|
|
||||||
alwaysinline bool enable() const { return codes.size() > 0; }
|
alwaysinline auto enable() const -> bool { return codes.size() > 0; }
|
||||||
void reset();
|
auto reset() -> void;
|
||||||
void append(unsigned addr, unsigned data);
|
auto append(uint addr, uint data) -> void;
|
||||||
void append(unsigned addr, unsigned comp, unsigned data);
|
auto append(uint addr, uint comp, uint data) -> void;
|
||||||
maybe<unsigned> find(unsigned addr, unsigned comp);
|
auto find(uint addr, uint comp) -> maybe<uint>;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Cheat cheat;
|
extern Cheat cheat;
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace Famicom {
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
CPU cpu;
|
CPU cpu;
|
||||||
|
|
||||||
void CPU::Enter() {
|
auto CPU::Enter() -> void {
|
||||||
while(true) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
|
@ -16,7 +16,7 @@ void CPU::Enter() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::main() {
|
auto CPU::main() -> void {
|
||||||
if(status.interrupt_pending) {
|
if(status.interrupt_pending) {
|
||||||
interrupt();
|
interrupt();
|
||||||
return;
|
return;
|
||||||
|
@ -25,7 +25,7 @@ void CPU::main() {
|
||||||
exec();
|
exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::add_clocks(unsigned clocks) {
|
auto CPU::add_clocks(uint clocks) -> void {
|
||||||
apu.clock -= clocks;
|
apu.clock -= clocks;
|
||||||
if(apu.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(apu.thread);
|
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);
|
if(cartridge.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cartridge.thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::power() {
|
auto CPU::power() -> void {
|
||||||
R6502::power();
|
R6502::power();
|
||||||
|
|
||||||
for(unsigned addr = 0; addr < 0x0800; addr++) ram[addr] = 0xff;
|
for(auto addr : range(0x0800)) ram[addr] = 0xff;
|
||||||
ram[0x0008] = 0xf7;
|
ram[0x0008] = 0xf7;
|
||||||
ram[0x0009] = 0xef;
|
ram[0x0009] = 0xef;
|
||||||
ram[0x000a] = 0xdf;
|
ram[0x000a] = 0xdf;
|
||||||
ram[0x000f] = 0xbf;
|
ram[0x000f] = 0xbf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::reset() {
|
auto CPU::reset() -> void {
|
||||||
R6502::reset();
|
R6502::reset();
|
||||||
create(CPU::Enter, 21477272);
|
create(CPU::Enter, 21477272);
|
||||||
|
|
||||||
|
@ -71,19 +71,19 @@ void CPU::reset() {
|
||||||
status.controller_port1 = 0;
|
status.controller_port1 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 CPU::debugger_read(uint16 addr) {
|
auto CPU::debugger_read(uint16 addr) -> uint8 {
|
||||||
return bus.read(addr);
|
return bus.read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 CPU::ram_read(uint16 addr) {
|
auto CPU::ram_read(uint16 addr) -> uint8 {
|
||||||
return ram[addr & 0x07ff];
|
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;
|
ram[addr & 0x07ff] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 CPU::read(uint16 addr) {
|
auto CPU::read(uint16 addr) -> uint8 {
|
||||||
if(addr == 0x4016) {
|
if(addr == 0x4016) {
|
||||||
return (mdr() & 0xc0) | input.data(0);
|
return (mdr() & 0xc0) | input.data(0);
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ uint8 CPU::read(uint16 addr) {
|
||||||
return apu.read(addr);
|
return apu.read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::write(uint16 addr, uint8 data) {
|
auto CPU::write(uint16 addr, uint8 data) -> void {
|
||||||
if(addr == 0x4014) {
|
if(addr == 0x4014) {
|
||||||
status.oam_dma_page = data;
|
status.oam_dma_page = data;
|
||||||
status.oam_dma_pending = true;
|
status.oam_dma_pending = true;
|
||||||
|
|
|
@ -1,4 +1,37 @@
|
||||||
struct CPU : Processor::R6502, Thread {
|
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];
|
uint8 ram[0x0800];
|
||||||
|
|
||||||
struct Status {
|
struct Status {
|
||||||
|
@ -16,41 +49,9 @@ struct CPU : Processor::R6502, Thread {
|
||||||
uint8 oam_dma_page;
|
uint8 oam_dma_page;
|
||||||
|
|
||||||
bool controller_latch;
|
bool controller_latch;
|
||||||
unsigned controller_port0;
|
uint controller_port0;
|
||||||
unsigned controller_port1;
|
uint controller_port1;
|
||||||
} status;
|
} 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;
|
extern CPU cpu;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
void CPU::serialize(serializer& s) {
|
auto CPU::serialize(serializer& s) -> void {
|
||||||
R6502::serialize(s);
|
R6502::serialize(s);
|
||||||
Thread::serialize(s);
|
Thread::serialize(s);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
uint8 CPU::op_read(uint16 addr) {
|
auto CPU::op_read(uint16 addr) -> uint8 {
|
||||||
if(status.oam_dma_pending) {
|
if(status.oam_dma_pending) {
|
||||||
status.oam_dma_pending = false;
|
status.oam_dma_pending = false;
|
||||||
op_read(addr);
|
op_read(addr);
|
||||||
|
@ -15,50 +15,50 @@ uint8 CPU::op_read(uint16 addr) {
|
||||||
return regs.mdr;
|
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);
|
bus.write(addr, regs.mdr = data);
|
||||||
add_clocks(12);
|
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;
|
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) {
|
if(status.nmi_pending) {
|
||||||
status.nmi_pending = false;
|
status.nmi_pending = false;
|
||||||
vector = 0xfffa;
|
vector = 0xfffa;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::oam_dma() {
|
auto CPU::oam_dma() -> void {
|
||||||
for(unsigned n = 0; n < 256; n++) {
|
for(uint n : range(256)) {
|
||||||
uint8 data = op_read((status.oam_dma_page << 8) + n);
|
uint8 data = op_read((status.oam_dma_page << 8) + n);
|
||||||
op_write(0x2004, data);
|
op_write(0x2004, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::set_nmi_line(bool line) {
|
auto CPU::set_nmi_line(bool line) -> void {
|
||||||
//edge-sensitive (0->1)
|
//edge-sensitive (0->1)
|
||||||
if(!status.nmi_line && line) status.nmi_pending = true;
|
if(!status.nmi_line && line) status.nmi_pending = true;
|
||||||
status.nmi_line = line;
|
status.nmi_line = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::set_irq_line(bool line) {
|
auto CPU::set_irq_line(bool line) -> void {
|
||||||
//level-sensitive
|
//level-sensitive
|
||||||
status.irq_line = line;
|
status.irq_line = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::set_irq_apu_line(bool line) {
|
auto CPU::set_irq_apu_line(bool line) -> void {
|
||||||
//level-sensitive
|
//level-sensitive
|
||||||
status.irq_apu_line = line;
|
status.irq_apu_line = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::set_rdy_line(bool line) {
|
auto CPU::set_rdy_line(bool line) -> void {
|
||||||
status.rdy_line = line;
|
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_valid = valid;
|
||||||
status.rdy_addr_value = value;
|
status.rdy_addr_value = value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace Famicom {
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
Input input;
|
Input input;
|
||||||
|
|
||||||
void Input::latch(bool data) {
|
auto Input::latch(bool data) -> void {
|
||||||
latchdata = data;
|
latchdata = data;
|
||||||
|
|
||||||
if(latchdata == 1) {
|
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;
|
bool result = 0;
|
||||||
|
|
||||||
if(port == 0) {
|
if(port == 0) {
|
||||||
|
@ -36,15 +36,15 @@ bool Input::data(bool port) {
|
||||||
return result;
|
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 == 0) port1 = device, counter1 = 0;
|
||||||
if(port == 1) port2 = device, counter2 = 0;
|
if(port == 1) port2 = device, counter2 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Input::power() {
|
auto Input::power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Input::reset() {
|
auto Input::reset() -> void {
|
||||||
latchdata = 0;
|
latchdata = 0;
|
||||||
counter1 = 0;
|
counter1 = 0;
|
||||||
counter2 = 0;
|
counter2 = 0;
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
struct Input {
|
struct Input {
|
||||||
enum class Device : unsigned {
|
enum class Device : uint {
|
||||||
Joypad,
|
Joypad,
|
||||||
None,
|
None,
|
||||||
};
|
};
|
||||||
|
|
||||||
void latch(bool data);
|
auto latch(bool data) -> void;
|
||||||
bool data(bool port);
|
auto data(bool port) -> bool;
|
||||||
void connect(bool port, Device device);
|
auto connect(bool port, Device device) -> void;
|
||||||
|
|
||||||
void power();
|
auto power() -> void;
|
||||||
void reset();
|
auto reset() -> void;
|
||||||
|
|
||||||
void serialize(serializer&);
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Device port1;
|
Device port1;
|
||||||
Device port2;
|
Device port2;
|
||||||
|
|
||||||
bool latchdata;
|
bool latchdata;
|
||||||
unsigned counter1;
|
uint counter1;
|
||||||
unsigned counter2;
|
uint counter2;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Input input;
|
extern Input input;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
void Input::serialize(serializer& s) {
|
auto Input::serialize(serializer& s) -> void {
|
||||||
s.integer((unsigned&)port1);
|
s.integer((uint&)port1);
|
||||||
s.integer((unsigned&)port2);
|
s.integer((uint&)port2);
|
||||||
|
|
||||||
s.integer(latchdata);
|
s.integer(latchdata);
|
||||||
s.integer(counter1);
|
s.integer(counter1);
|
||||||
|
|
|
@ -4,27 +4,66 @@ namespace Famicom {
|
||||||
|
|
||||||
Interface* interface = nullptr;
|
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();
|
return cartridge.title();
|
||||||
}
|
}
|
||||||
|
|
||||||
double Interface::videoFrequency() {
|
auto Interface::videoFrequency() -> double {
|
||||||
return 21477272.0 / (262.0 * 1364.0 - 4.0);
|
return 21477272.0 / (262.0 * 1364.0 - 4.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
double Interface::audioFrequency() {
|
auto Interface::audioFrequency() -> double {
|
||||||
return 21477272.0 / 12.0;
|
return 21477272.0 / 12.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Interface::loaded() {
|
auto Interface::loaded() -> bool {
|
||||||
return cartridge.loaded();
|
return cartridge.loaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
string Interface::sha256() {
|
auto Interface::sha256() -> string {
|
||||||
return cartridge.sha256();
|
return cartridge.sha256();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned Interface::group(unsigned id) {
|
auto Interface::group(uint id) -> uint {
|
||||||
switch(id) {
|
switch(id) {
|
||||||
case ID::SystemManifest:
|
case ID::SystemManifest:
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -39,17 +78,17 @@ unsigned Interface::group(unsigned id) {
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::load(unsigned id) {
|
auto Interface::load(uint id) -> void {
|
||||||
cartridge.load();
|
cartridge.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::save() {
|
auto Interface::save() -> void {
|
||||||
for(auto& memory : cartridge.memory) {
|
for(auto& memory : cartridge.memory) {
|
||||||
saveRequest(memory.id, memory.name);
|
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) {
|
if(id == ID::SystemManifest) {
|
||||||
system.information.manifest = stream.text();
|
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) {
|
if(id == ID::ProgramRAM) {
|
||||||
stream.write(cartridge.board->prgram.data, cartridge.board->prgram.size);
|
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();
|
save();
|
||||||
cartridge.unload();
|
cartridge.unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::power() {
|
auto Interface::power() -> void {
|
||||||
system.power();
|
system.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::reset() {
|
auto Interface::reset() -> void {
|
||||||
system.reset();
|
system.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::run() {
|
auto Interface::run() -> void {
|
||||||
system.run();
|
system.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer Interface::serialize() {
|
auto Interface::serialize() -> serializer {
|
||||||
system.runtosave();
|
system.runtosave();
|
||||||
return system.serialize();
|
return system.serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Interface::unserialize(serializer& s) {
|
auto Interface::unserialize(serializer& s) -> bool {
|
||||||
return system.unserialize(s);
|
return system.unserialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::cheatSet(const lstring& list) {
|
auto Interface::cheatSet(const lstring& list) -> void {
|
||||||
cheat.reset();
|
cheat.reset();
|
||||||
for(auto& codeset : list) {
|
for(auto& codeset : list) {
|
||||||
lstring codes = codeset.split("+");
|
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);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,12 @@ namespace Famicom {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct ID {
|
struct ID {
|
||||||
enum : unsigned {
|
enum : uint {
|
||||||
System,
|
System,
|
||||||
Famicom,
|
Famicom,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum : unsigned {
|
enum : uint {
|
||||||
SystemManifest,
|
SystemManifest,
|
||||||
|
|
||||||
Manifest,
|
Manifest,
|
||||||
|
@ -18,39 +18,39 @@ struct ID {
|
||||||
CharacterRAM,
|
CharacterRAM,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum : unsigned {
|
enum : uint {
|
||||||
Port1 = 1,
|
Port1 = 1,
|
||||||
Port2 = 2,
|
Port2 = 2,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Interface : Emulator::Interface {
|
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();
|
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:
|
private:
|
||||||
vector<Device> device;
|
vector<Device> device;
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,7 @@ Bus bus;
|
||||||
//$4000-4017 = APU + I/O
|
//$4000-4017 = APU + I/O
|
||||||
//$4018-ffff = Cartridge
|
//$4018-ffff = Cartridge
|
||||||
|
|
||||||
uint8 Bus::read(uint16 addr) {
|
auto Bus::read(uint16 addr) -> uint8 {
|
||||||
uint8 data = cartridge.prg_read(addr);
|
uint8 data = cartridge.prg_read(addr);
|
||||||
if(addr <= 0x1fff) data = cpu.ram_read(addr);
|
if(addr <= 0x1fff) data = cpu.ram_read(addr);
|
||||||
else if(addr <= 0x3fff) data = ppu.read(addr);
|
else if(addr <= 0x3fff) data = ppu.read(addr);
|
||||||
|
@ -24,7 +24,7 @@ uint8 Bus::read(uint16 addr) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::write(uint16 addr, uint8 data) {
|
auto Bus::write(uint16 addr, uint8 data) -> void {
|
||||||
cartridge.prg_write(addr, data);
|
cartridge.prg_write(addr, data);
|
||||||
if(addr <= 0x1fff) return cpu.ram_write(addr, data);
|
if(addr <= 0x1fff) return cpu.ram_write(addr, data);
|
||||||
if(addr <= 0x3fff) return ppu.write(addr, data);
|
if(addr <= 0x3fff) return ppu.write(addr, data);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
struct Bus {
|
struct Bus {
|
||||||
uint8 read(uint16 addr);
|
auto read(uint16 addr) -> uint8;
|
||||||
void write(uint16 addr, uint8 data);
|
auto write(uint16 addr, uint8 data) -> void;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Bus bus;
|
extern Bus bus;
|
||||||
|
|
108
fc/ppu/ppu.cpp
108
fc/ppu/ppu.cpp
|
@ -5,11 +5,11 @@ namespace Famicom {
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
PPU ppu;
|
PPU ppu;
|
||||||
|
|
||||||
void PPU::Main() {
|
auto PPU::Main() -> void {
|
||||||
ppu.main();
|
ppu.main();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::main() {
|
auto PPU::main() -> void {
|
||||||
while(true) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::PPU) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::PPU) {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
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 == 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 == 0) status.nmi_flag = status.nmi_hold;
|
||||||
if(status.ly == 241 && status.lx == 2) cpu.set_nmi_line(status.nmi_enable && status.nmi_flag);
|
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++;
|
status.lx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::scanline() {
|
auto PPU::scanline() -> void {
|
||||||
status.lx = 0;
|
status.lx = 0;
|
||||||
if(++status.ly == 262) {
|
if(++status.ly == 262) {
|
||||||
status.ly = 0;
|
status.ly = 0;
|
||||||
|
@ -45,15 +45,15 @@ void PPU::scanline() {
|
||||||
cartridge.scanline(status.ly);
|
cartridge.scanline(status.ly);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::frame() {
|
auto PPU::frame() -> void {
|
||||||
status.field ^= 1;
|
status.field ^= 1;
|
||||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::power() {
|
auto PPU::power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::reset() {
|
auto PPU::reset() -> void {
|
||||||
create(PPU::Main, 21477272);
|
create(PPU::Main, 21477272);
|
||||||
|
|
||||||
status.mdr = 0x00;
|
status.mdr = 0x00;
|
||||||
|
@ -98,7 +98,7 @@ void PPU::reset() {
|
||||||
for(auto& n : oam ) n = 0;
|
for(auto& n : oam ) n = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 PPU::read(uint16 addr) {
|
auto PPU::read(uint16 addr) -> uint8 {
|
||||||
uint8 result = 0x00;
|
uint8 result = 0x00;
|
||||||
|
|
||||||
switch(addr & 7) {
|
switch(addr & 7) {
|
||||||
|
@ -136,7 +136,7 @@ uint8 PPU::read(uint16 addr) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::write(uint16 addr, uint8 data) {
|
auto PPU::write(uint16 addr, uint8 data) -> void {
|
||||||
status.mdr = data;
|
status.mdr = data;
|
||||||
|
|
||||||
switch(addr & 7) {
|
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];
|
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;
|
ciram[addr & 0x07ff] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 PPU::cgram_read(uint16 addr) {
|
auto PPU::cgram_read(uint16 addr) -> uint8 {
|
||||||
if((addr & 0x13) == 0x10) addr &= ~0x10;
|
if((addr & 0x13) == 0x10) addr &= ~0x10;
|
||||||
uint8 data = cgram[addr & 0x1f];
|
uint8 data = cgram[addr & 0x1f];
|
||||||
if(status.grayscale) data &= 0x30;
|
if(status.grayscale) data &= 0x30;
|
||||||
return data;
|
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;
|
if((addr & 0x13) == 0x10) addr &= ~0x10;
|
||||||
cgram[addr & 0x1f] = data;
|
cgram[addr & 0x1f] = data;
|
||||||
}
|
}
|
||||||
|
@ -229,36 +229,36 @@ void PPU::cgram_write(uint16 addr, uint8 data) {
|
||||||
//YYYYY = Y nametable (y:d3-d7)
|
//YYYYY = Y nametable (y:d3-d7)
|
||||||
//XXXXX = X nametable (x: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);
|
return (status.bg_enable || status.sprite_enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned PPU::nametable_addr() const {
|
auto PPU::nametable_addr() const -> uint {
|
||||||
return 0x2000 + (status.vaddr & 0x0c00);
|
return 0x2000 + (status.vaddr & 0x0c00);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned PPU::scrollx() const {
|
auto PPU::scrollx() const -> uint {
|
||||||
return ((status.vaddr & 0x1f) << 3) | status.xaddr;
|
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);
|
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;
|
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;
|
if(raster_enable() == false) return 0x00;
|
||||||
return cartridge.chr_read(addr);
|
return cartridge.chr_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
void PPU::scrollx_increment() {
|
auto PPU::scrollx_increment() -> void {
|
||||||
if(raster_enable() == false) return;
|
if(raster_enable() == false) return;
|
||||||
status.vaddr = (status.vaddr & 0x7fe0) | ((status.vaddr + 0x0001) & 0x001f);
|
status.vaddr = (status.vaddr & 0x7fe0) | ((status.vaddr + 0x0001) & 0x001f);
|
||||||
if((status.vaddr & 0x001f) == 0x0000) {
|
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;
|
if(raster_enable() == false) return;
|
||||||
status.vaddr = (status.vaddr & 0x0fff) | ((status.vaddr + 0x1000) & 0x7000);
|
status.vaddr = (status.vaddr & 0x0fff) | ((status.vaddr + 0x1000) & 0x7000);
|
||||||
if((status.vaddr & 0x7000) == 0x0000) {
|
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;
|
uint32* output = buffer + status.ly * 256;
|
||||||
|
|
||||||
unsigned mask = 0x8000 >> (status.xaddr + (status.lx & 7));
|
uint mask = 0x8000 >> (status.xaddr + (status.lx & 7));
|
||||||
unsigned palette = 0, object_palette = 0;
|
uint palette = 0, object_palette = 0;
|
||||||
bool object_priority = 0;
|
bool object_priority = 0;
|
||||||
palette |= (raster.tiledatalo & mask) ? 1 : 0;
|
palette |= (raster.tiledatalo & mask) ? 1 : 0;
|
||||||
palette |= (raster.tiledatahi & mask) ? 2 : 0;
|
palette |= (raster.tiledatahi & mask) ? 2 : 0;
|
||||||
if(palette) {
|
if(palette) {
|
||||||
unsigned attr = raster.attribute;
|
uint attr = raster.attribute;
|
||||||
if(mask >= 256) attr >>= 2;
|
if(mask >= 256) attr >>= 2;
|
||||||
palette |= (attr & 3) << 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.bg_edge_enable == false && status.lx < 8) palette = 0;
|
||||||
|
|
||||||
if(status.sprite_enable == true)
|
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(status.sprite_edge_enable == false && status.lx < 8) continue;
|
||||||
if(raster.oam[sprite].id == 64) 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(spritex >= 8) continue;
|
||||||
|
|
||||||
if(raster.oam[sprite].attr & 0x40) spritex ^= 7;
|
if(raster.oam[sprite].attr & 0x40) spritex ^= 7;
|
||||||
unsigned mask = 0x80 >> spritex;
|
uint mask = 0x80 >> spritex;
|
||||||
unsigned sprite_palette = 0;
|
uint sprite_palette = 0;
|
||||||
sprite_palette |= (raster.oam[sprite].tiledatalo & mask) ? 1 : 0;
|
sprite_palette |= (raster.oam[sprite].tiledatalo & mask) ? 1 : 0;
|
||||||
sprite_palette |= (raster.oam[sprite].tiledatahi & mask) ? 2 : 0;
|
sprite_palette |= (raster.oam[sprite].tiledatahi & mask) ? 2 : 0;
|
||||||
if(sprite_palette == 0) continue;
|
if(sprite_palette == 0) continue;
|
||||||
|
@ -327,12 +327,12 @@ void PPU::raster_pixel() {
|
||||||
output[status.lx] = (status.emphasis << 6) | cgram_read(palette);
|
output[status.lx] = (status.emphasis << 6) | cgram_read(palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::raster_sprite() {
|
auto PPU::raster_sprite() -> void {
|
||||||
if(raster_enable() == false) return;
|
if(raster_enable() == false) return;
|
||||||
|
|
||||||
unsigned n = raster.oam_iterator++;
|
uint n = raster.oam_iterator++;
|
||||||
signed ly = (status.ly == 261 ? -1 : status.ly);
|
int ly = (status.ly == 261 ? -1 : status.ly);
|
||||||
unsigned y = ly - oam[(n * 4) + 0];
|
uint y = ly - oam[(n * 4) + 0];
|
||||||
|
|
||||||
if(y >= sprite_height()) return;
|
if(y >= sprite_height()) return;
|
||||||
if(raster.oam_counter == 8) {
|
if(raster.oam_counter == 8) {
|
||||||
|
@ -348,16 +348,16 @@ void PPU::raster_sprite() {
|
||||||
raster.oam_counter++;
|
raster.oam_counter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::raster_scanline() {
|
auto PPU::raster_scanline() -> void {
|
||||||
if((status.ly >= 240 && status.ly <= 260)) {
|
if((status.ly >= 240 && status.ly <= 260)) {
|
||||||
for(unsigned x = 0; x < 341; x++) tick();
|
for(auto x : range(341)) tick();
|
||||||
return scanline();
|
return scanline();
|
||||||
}
|
}
|
||||||
|
|
||||||
raster.oam_iterator = 0;
|
raster.oam_iterator = 0;
|
||||||
raster.oam_counter = 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].id = 64;
|
||||||
raster.soam[n].y = 0xff;
|
raster.soam[n].y = 0xff;
|
||||||
raster.soam[n].tile = 0xff;
|
raster.soam[n].tile = 0xff;
|
||||||
|
@ -367,16 +367,16 @@ void PPU::raster_scanline() {
|
||||||
raster.soam[n].tiledatahi = 0;
|
raster.soam[n].tiledatahi = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(unsigned tile = 0; tile < 32; tile++) { // 0-255
|
for(uint tile : range(32)) { // 0-255
|
||||||
unsigned nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
|
uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
|
||||||
unsigned tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
|
uint tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
|
||||||
raster_pixel();
|
raster_pixel();
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
raster_pixel();
|
raster_pixel();
|
||||||
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(scrolly() & 16) attribute >>= 4;
|
||||||
if(scrollx() & 16) attribute >>= 2;
|
if(scrollx() & 16) attribute >>= 2;
|
||||||
raster_pixel();
|
raster_pixel();
|
||||||
|
@ -388,14 +388,14 @@ void PPU::raster_scanline() {
|
||||||
raster_sprite();
|
raster_sprite();
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
unsigned tiledatalo = chr_load(tileaddr + 0);
|
uint tiledatalo = chr_load(tileaddr + 0);
|
||||||
raster_pixel();
|
raster_pixel();
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
raster_pixel();
|
raster_pixel();
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
unsigned tiledatahi = chr_load(tileaddr + 8);
|
uint tiledatahi = chr_load(tileaddr + 8);
|
||||||
raster_pixel();
|
raster_pixel();
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
|
@ -409,23 +409,23 @@ void PPU::raster_scanline() {
|
||||||
raster.tiledatahi = (raster.tiledatahi << 8) | tiledatahi;
|
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
|
for(uint sprite : range(8)) { //256-319
|
||||||
unsigned nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
|
uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
if(raster_enable() && sprite == 0) status.vaddr = (status.vaddr & 0x7be0) | (status.taddr & 0x041f); //257
|
if(raster_enable() && sprite == 0) status.vaddr = (status.vaddr & 0x7be0) | (status.taddr & 0x041f); //257
|
||||||
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));
|
||||||
unsigned tileaddr = (sprite_height() == 8)
|
uint tileaddr = (sprite_height() == 8)
|
||||||
? status.sprite_addr + raster.oam[sprite].tile * 16
|
? status.sprite_addr + raster.oam[sprite].tile * 16
|
||||||
: ((raster.oam[sprite].tile & ~1) * 16) + ((raster.oam[sprite].tile & 1) * 0x1000);
|
: ((raster.oam[sprite].tile & ~1) * 16) + ((raster.oam[sprite].tile & 1) * 0x1000);
|
||||||
tick();
|
tick();
|
||||||
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);
|
if(raster.oam[sprite].attr & 0x80) spritey ^= (sprite_height() - 1);
|
||||||
tileaddr += spritey + (spritey & 8);
|
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
|
if(raster_enable() && sprite == 6 && status.ly == 261) status.vaddr = status.taddr; //304
|
||||||
}
|
}
|
||||||
|
|
||||||
for(unsigned tile = 0; tile < 2; tile++) { //320-335
|
for(uint tile : range(2)) { //320-335
|
||||||
unsigned nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
|
uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
|
||||||
unsigned tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
|
uint tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
|
||||||
tick();
|
tick();
|
||||||
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(scrolly() & 16) attribute >>= 4;
|
||||||
if(scrollx() & 16) attribute >>= 2;
|
if(scrollx() & 16) attribute >>= 2;
|
||||||
tick();
|
tick();
|
||||||
|
@ -454,11 +454,11 @@ void PPU::raster_scanline() {
|
||||||
scrollx_increment();
|
scrollx_increment();
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
unsigned tiledatalo = chr_load(tileaddr + 0);
|
uint tiledatalo = chr_load(tileaddr + 0);
|
||||||
tick();
|
tick();
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
unsigned tiledatahi = chr_load(tileaddr + 8);
|
uint tiledatahi = chr_load(tileaddr + 8);
|
||||||
tick();
|
tick();
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
|
|
|
@ -1,46 +1,46 @@
|
||||||
struct PPU : Thread {
|
struct PPU : Thread {
|
||||||
static void Main();
|
static auto Main() -> void;
|
||||||
void main();
|
auto main() -> void;
|
||||||
void tick();
|
auto tick() -> void;
|
||||||
|
|
||||||
void scanline();
|
auto scanline() -> void;
|
||||||
void frame();
|
auto frame() -> void;
|
||||||
|
|
||||||
void power();
|
auto power() -> void;
|
||||||
void reset();
|
auto reset() -> void;
|
||||||
|
|
||||||
uint8 read(uint16 addr);
|
auto read(uint16 addr) -> uint8;
|
||||||
void write(uint16 addr, uint8 data);
|
auto write(uint16 addr, uint8 data) -> void;
|
||||||
|
|
||||||
uint8 ciram_read(uint16 addr);
|
auto ciram_read(uint16 addr) -> uint8;
|
||||||
void ciram_write(uint16 addr, uint8 data);
|
auto ciram_write(uint16 addr, uint8 data) -> void;
|
||||||
|
|
||||||
uint8 cgram_read(uint16 addr);
|
auto cgram_read(uint16 addr) -> uint8;
|
||||||
void cgram_write(uint16 addr, uint8 data);
|
auto cgram_write(uint16 addr, uint8 data) -> void;
|
||||||
|
|
||||||
bool raster_enable() const;
|
auto raster_enable() const -> bool;
|
||||||
unsigned nametable_addr() const;
|
auto nametable_addr() const -> uint;
|
||||||
unsigned scrollx() const;
|
auto scrollx() const -> uint;
|
||||||
unsigned scrolly() const;
|
auto scrolly() const -> uint;
|
||||||
unsigned sprite_height() const;
|
auto sprite_height() const -> uint;
|
||||||
|
|
||||||
uint8 chr_load(uint16 addr);
|
auto chr_load(uint16 addr) -> uint8;
|
||||||
|
|
||||||
void scrollx_increment();
|
auto scrollx_increment() -> void;
|
||||||
void scrolly_increment();
|
auto scrolly_increment() -> void;
|
||||||
|
|
||||||
void raster_pixel();
|
auto raster_pixel() -> void;
|
||||||
void raster_sprite();
|
auto raster_sprite() -> void;
|
||||||
void raster_scanline();
|
auto raster_scanline() -> void;
|
||||||
|
|
||||||
void serialize(serializer&);
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
struct Status {
|
struct Status {
|
||||||
uint8 mdr;
|
uint8 mdr;
|
||||||
|
|
||||||
bool field;
|
bool field;
|
||||||
unsigned lx;
|
uint lx;
|
||||||
unsigned ly;
|
uint ly;
|
||||||
|
|
||||||
uint8 bus_data;
|
uint8 bus_data;
|
||||||
|
|
||||||
|
@ -57,9 +57,9 @@ struct PPU : Thread {
|
||||||
bool nmi_enable;
|
bool nmi_enable;
|
||||||
bool master_select;
|
bool master_select;
|
||||||
bool sprite_size;
|
bool sprite_size;
|
||||||
unsigned bg_addr;
|
uint bg_addr;
|
||||||
unsigned sprite_addr;
|
uint sprite_addr;
|
||||||
unsigned vram_increment;
|
uint vram_increment;
|
||||||
|
|
||||||
//$2001
|
//$2001
|
||||||
uint3 emphasis;
|
uint3 emphasis;
|
||||||
|
@ -83,8 +83,8 @@ struct PPU : Thread {
|
||||||
uint16 tiledatalo;
|
uint16 tiledatalo;
|
||||||
uint16 tiledatahi;
|
uint16 tiledatahi;
|
||||||
|
|
||||||
unsigned oam_iterator;
|
uint oam_iterator;
|
||||||
unsigned oam_counter;
|
uint oam_counter;
|
||||||
|
|
||||||
struct OAM {
|
struct OAM {
|
||||||
uint8 id;
|
uint8 id;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
void PPU::serialize(serializer& s) {
|
auto PPU::serialize(serializer& s) -> void {
|
||||||
Thread::serialize(s);
|
Thread::serialize(s);
|
||||||
|
|
||||||
s.integer(status.mdr);
|
s.integer(status.mdr);
|
||||||
|
@ -45,7 +45,7 @@ void PPU::serialize(serializer& s) {
|
||||||
s.integer(raster.oam_iterator);
|
s.integer(raster.oam_iterator);
|
||||||
s.integer(raster.oam_counter);
|
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].id);
|
||||||
s.integer(raster.oam[n].y);
|
s.integer(raster.oam[n].y);
|
||||||
s.integer(raster.oam[n].tile);
|
s.integer(raster.oam[n].tile);
|
||||||
|
@ -56,7 +56,7 @@ void PPU::serialize(serializer& s) {
|
||||||
s.integer(raster.oam[n].tiledatahi);
|
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].id);
|
||||||
s.integer(raster.soam[n].y);
|
s.integer(raster.soam[n].y);
|
||||||
s.integer(raster.soam[n].tile);
|
s.integer(raster.soam[n].tile);
|
||||||
|
|
|
@ -4,21 +4,21 @@ namespace Famicom {
|
||||||
|
|
||||||
Scheduler scheduler;
|
Scheduler scheduler;
|
||||||
|
|
||||||
void Scheduler::enter() {
|
auto Scheduler::enter() -> void {
|
||||||
host_thread = co_active();
|
host_thread = co_active();
|
||||||
co_switch(thread);
|
co_switch(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::exit(ExitReason reason) {
|
auto Scheduler::exit(ExitReason reason) -> void {
|
||||||
exit_reason = reason;
|
exit_reason = reason;
|
||||||
thread = co_active();
|
thread = co_active();
|
||||||
co_switch(host_thread);
|
co_switch(host_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::power() {
|
auto Scheduler::power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::reset() {
|
auto Scheduler::reset() -> void {
|
||||||
host_thread = co_active();
|
host_thread = co_active();
|
||||||
thread = cpu.thread;
|
thread = cpu.thread;
|
||||||
sync = SynchronizeMode::None;
|
sync = SynchronizeMode::None;
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
struct Scheduler : property<Scheduler> {
|
struct Scheduler : property<Scheduler> {
|
||||||
enum class SynchronizeMode : unsigned { None, PPU, All } sync;
|
enum class SynchronizeMode : uint { None, PPU, All } sync;
|
||||||
enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent };
|
enum class ExitReason : uint { UnknownEvent, FrameEvent, SynchronizeEvent };
|
||||||
readonly<ExitReason> exit_reason;
|
|
||||||
|
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 host_thread; //program thread (used to exit emulation)
|
||||||
cothread_t thread; //active emulation thread (used to enter emulation)
|
cothread_t thread; //active emulation thread (used to enter emulation)
|
||||||
|
readonly<ExitReason> exit_reason;
|
||||||
void enter();
|
|
||||||
void exit(ExitReason);
|
|
||||||
|
|
||||||
void power();
|
|
||||||
void reset();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Scheduler scheduler;
|
extern Scheduler scheduler;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
serializer System::serialize() {
|
auto System::serialize() -> serializer {
|
||||||
serializer s(serialize_size);
|
serializer s(serialize_size);
|
||||||
|
|
||||||
unsigned signature = 0x31545342, version = Info::SerializerVersion;
|
uint signature = 0x31545342, version = Info::SerializerVersion;
|
||||||
char hash[64], description[512];
|
char hash[64], description[512];
|
||||||
memcpy(&hash, (const char*)cartridge.sha256(), 64);
|
memcpy(&hash, (const char*)cartridge.sha256(), 64);
|
||||||
memset(&description, 0, sizeof description);
|
memset(&description, 0, sizeof description);
|
||||||
|
@ -15,8 +15,8 @@ serializer System::serialize() {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::unserialize(serializer& s) {
|
auto System::unserialize(serializer& s) -> bool {
|
||||||
unsigned signature, version;
|
uint signature, version;
|
||||||
char hash[64], description[512];
|
char hash[64], description[512];
|
||||||
|
|
||||||
s.integer(signature);
|
s.integer(signature);
|
||||||
|
@ -32,10 +32,10 @@ bool System::unserialize(serializer& s) {
|
||||||
return true;
|
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);
|
system.serialize(s);
|
||||||
input.serialize(s);
|
input.serialize(s);
|
||||||
cartridge.serialize(s);
|
cartridge.serialize(s);
|
||||||
|
@ -44,10 +44,10 @@ void System::serialize_all(serializer& s) {
|
||||||
ppu.serialize(s);
|
ppu.serialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::serialize_init() {
|
auto System::serialize_init() -> void {
|
||||||
serializer s;
|
serializer s;
|
||||||
|
|
||||||
unsigned signature = 0, version = 0;
|
uint signature = 0, version = 0;
|
||||||
char hash[64], description[512];
|
char hash[64], description[512];
|
||||||
|
|
||||||
s.integer(signature);
|
s.integer(signature);
|
||||||
|
|
|
@ -5,14 +5,14 @@ namespace Famicom {
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
System system;
|
System system;
|
||||||
|
|
||||||
void System::run() {
|
auto System::run() -> void {
|
||||||
scheduler.enter();
|
scheduler.enter();
|
||||||
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
|
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
|
||||||
interface->videoRefresh(video.palette, ppu.buffer, 4 * 256, 256, 240);
|
interface->videoRefresh(video.palette, ppu.buffer, 4 * 256, 256, 240);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::runtosave() {
|
auto System::runtosave() -> void {
|
||||||
scheduler.sync = Scheduler::SynchronizeMode::PPU;
|
scheduler.sync = Scheduler::SynchronizeMode::PPU;
|
||||||
runthreadtosave();
|
runthreadtosave();
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ void System::runtosave() {
|
||||||
scheduler.sync = Scheduler::SynchronizeMode::None;
|
scheduler.sync = Scheduler::SynchronizeMode::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::runthreadtosave() {
|
auto System::runthreadtosave() -> void {
|
||||||
while(true) {
|
while(true) {
|
||||||
scheduler.enter();
|
scheduler.enter();
|
||||||
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
|
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);
|
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
|
||||||
auto document = BML::unserialize(information.manifest);
|
auto document = BML::unserialize(information.manifest);
|
||||||
|
|
||||||
serialize_init();
|
serialize_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::power() {
|
auto System::power() -> void {
|
||||||
cartridge.power();
|
cartridge.power();
|
||||||
cpu.power();
|
cpu.power();
|
||||||
apu.power();
|
apu.power();
|
||||||
|
@ -58,7 +58,7 @@ void System::power() {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::reset() {
|
auto System::reset() -> void {
|
||||||
cartridge.reset();
|
cartridge.reset();
|
||||||
cpu.reset();
|
cpu.reset();
|
||||||
apu.reset();
|
apu.reset();
|
||||||
|
@ -67,13 +67,13 @@ void System::reset() {
|
||||||
scheduler.reset();
|
scheduler.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::init() {
|
auto System::init() -> void {
|
||||||
assert(interface != 0);
|
assert(interface != 0);
|
||||||
input.connect(0, Input::Device::Joypad);
|
input.connect(0, Input::Device::Joypad);
|
||||||
input.connect(1, Input::Device::None);
|
input.connect(1, Input::Device::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::term() {
|
auto System::term() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,27 @@
|
||||||
struct System {
|
struct System {
|
||||||
void run();
|
auto run() -> void;
|
||||||
void runtosave();
|
auto runtosave() -> void;
|
||||||
void runthreadtosave();
|
auto runthreadtosave() -> void;
|
||||||
|
|
||||||
void load();
|
auto load() -> void;
|
||||||
void power();
|
auto power() -> void;
|
||||||
void reset();
|
auto reset() -> void;
|
||||||
|
|
||||||
void init();
|
auto init() -> void;
|
||||||
void term();
|
auto term() -> void;
|
||||||
|
|
||||||
serializer serialize();
|
auto serialize() -> serializer;
|
||||||
bool unserialize(serializer&);
|
auto unserialize(serializer&) -> bool;
|
||||||
|
|
||||||
void serialize(serializer&);
|
auto serialize(serializer&) -> void;
|
||||||
void serialize_all(serializer&);
|
auto serialize_all(serializer&) -> void;
|
||||||
void serialize_init();
|
auto serialize_init() -> void;
|
||||||
unsigned serialize_size;
|
|
||||||
|
|
||||||
struct Information {
|
struct Information {
|
||||||
string manifest;
|
string manifest;
|
||||||
} information;
|
} information;
|
||||||
|
|
||||||
|
uint serialize_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern System system;
|
extern System system;
|
||||||
|
|
|
@ -6,14 +6,22 @@ namespace Famicom {
|
||||||
|
|
||||||
Video video;
|
Video video;
|
||||||
|
|
||||||
void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
|
Video::Video() {
|
||||||
for(unsigned color = 0; color < (1 << 9); color++) {
|
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) {
|
if(mode == Emulator::Interface::PaletteMode::Literal) {
|
||||||
palette[color] = color;
|
palette[color] = color;
|
||||||
} else if(mode == Emulator::Interface::PaletteMode::Channel) {
|
} else if(mode == Emulator::Interface::PaletteMode::Channel) {
|
||||||
unsigned emphasis = (color >> 6) & 7;
|
uint emphasis = (color >> 6) & 7;
|
||||||
unsigned luma = (color >> 4) & 3;
|
uint luma = (color >> 4) & 3;
|
||||||
unsigned chroma = (color >> 0) & 15;
|
uint chroma = (color >> 0) & 15;
|
||||||
emphasis = image::normalize(emphasis, 3, 16);
|
emphasis = image::normalize(emphasis, 3, 16);
|
||||||
luma = image::normalize(luma, 2, 16);
|
luma = image::normalize(luma, 2, 16);
|
||||||
chroma = image::normalize(chroma, 4, 16);
|
chroma = image::normalize(chroma, 4, 16);
|
||||||
|
@ -26,19 +34,11 @@ void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Video::Video() {
|
auto Video::generate_color(
|
||||||
palette = new uint32_t[1 << 9]();
|
uint n, double saturation, double hue,
|
||||||
}
|
|
||||||
|
|
||||||
Video::~Video() {
|
|
||||||
delete[] palette;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Video::generate_color(
|
|
||||||
unsigned n, double saturation, double hue,
|
|
||||||
double contrast, double brightness, double gamma
|
double contrast, double brightness, double gamma
|
||||||
) {
|
) -> uint32 {
|
||||||
signed color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1;
|
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 black = 0.518, white = 1.962, attenuation = 0.746;
|
||||||
static const double levels[8] = {
|
static const double levels[8] = {
|
||||||
|
@ -52,8 +52,8 @@ uint32_t Video::generate_color(
|
||||||
};
|
};
|
||||||
|
|
||||||
double y = 0.0, i = 0.0, q = 0.0;
|
double y = 0.0, i = 0.0, q = 0.0;
|
||||||
auto wave = [](signed p, signed color) { return (color + p + 8) % 12 < 6; };
|
auto wave = [](int p, int color) { return (color + p + 8) % 12 < 6; };
|
||||||
for(signed p = 0; p < 12; p++) {
|
for(int p : range(12)) {
|
||||||
double spot = lo_and_hi[wave(p, color)];
|
double spot = lo_and_hi[wave(p, color)];
|
||||||
|
|
||||||
if(((n & 0x040) && wave(p, 12))
|
if(((n & 0x040) && wave(p, 12))
|
||||||
|
@ -75,9 +75,9 @@ uint32_t Video::generate_color(
|
||||||
q *= saturation;
|
q *= saturation;
|
||||||
|
|
||||||
auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : std::pow(f, 2.2 / gamma); };
|
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);
|
uint r = 65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q);
|
||||||
unsigned g = 65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q);
|
uint g = 65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q);
|
||||||
unsigned b = 65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * 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));
|
return interface->videoColor(n, 0, uclamp<16>(r), uclamp<16>(g), uclamp<16>(b));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
struct Video {
|
struct Video {
|
||||||
uint32_t* palette = nullptr;
|
|
||||||
void generate_palette(Emulator::Interface::PaletteMode mode);
|
|
||||||
|
|
||||||
Video();
|
Video();
|
||||||
~Video();
|
~Video();
|
||||||
|
|
||||||
|
auto generate_palette(Emulator::Interface::PaletteMode mode) -> void;
|
||||||
|
|
||||||
|
uint32* palette = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t generate_color(unsigned, double, double, double, double, double);
|
auto generate_color(uint, double, double, double, double, double) -> uint32;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Video video;
|
extern Video video;
|
||||||
|
|
|
@ -69,7 +69,7 @@ endif
|
||||||
|
|
||||||
# windows settings
|
# windows settings
|
||||||
ifeq ($(platform),windows)
|
ifeq ($(platform),windows)
|
||||||
link := $(link) -lws2_32 -lole32
|
link += -lws2_32 -lole32
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# macosx settings
|
# macosx settings
|
||||||
|
@ -78,6 +78,11 @@ ifeq ($(platform),macosx)
|
||||||
link += -lc++ -lobjc
|
link += -lc++ -lobjc
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# linux settings
|
||||||
|
ifeq ($(platform),linux)
|
||||||
|
link += -ldl
|
||||||
|
endif
|
||||||
|
|
||||||
# bsd settings
|
# bsd settings
|
||||||
ifeq ($(platform),bsd)
|
ifeq ($(platform),bsd)
|
||||||
flags += -I/usr/local/include
|
flags += -I/usr/local/include
|
||||||
|
@ -93,12 +98,6 @@ ifeq ($(threaded),true)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# cross-compilation support
|
|
||||||
ifeq ($(arch),x86)
|
|
||||||
flags := -m32 $(flags)
|
|
||||||
link := -m32 $(link)
|
|
||||||
endif
|
|
||||||
|
|
||||||
# paths
|
# paths
|
||||||
prefix := $(HOME)/.local
|
prefix := $(HOME)/.local
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,12 @@ void CPU::step(unsigned clocks) {
|
||||||
auto& chip = *coprocessors[i];
|
auto& chip = *coprocessors[i];
|
||||||
chip.clock -= clocks * (uint64)chip.frequency;
|
chip.clock -= clocks * (uint64)chip.frequency;
|
||||||
}
|
}
|
||||||
input.port1->clock -= clocks * (uint64)input.port1->frequency;
|
device.controllerPort1->clock -= clocks * (uint64)device.controllerPort1->frequency;
|
||||||
input.port2->clock -= clocks * (uint64)input.port2->frequency;
|
device.controllerPort2->clock -= clocks * (uint64)device.controllerPort2->frequency;
|
||||||
synchronize_controllers();
|
synchronizeDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::synchronize_smp() {
|
void CPU::synchronizeSMP() {
|
||||||
if(SMP::Threaded == true) {
|
if(SMP::Threaded == true) {
|
||||||
if(smp.clock < 0) co_switch(smp.thread);
|
if(smp.clock < 0) co_switch(smp.thread);
|
||||||
} else {
|
} else {
|
||||||
|
@ -31,7 +31,7 @@ void CPU::synchronize_smp() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::synchronize_ppu() {
|
void CPU::synchronizePPU() {
|
||||||
if(PPU::Threaded == true) {
|
if(PPU::Threaded == true) {
|
||||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||||
} else {
|
} else {
|
||||||
|
@ -39,16 +39,16 @@ void CPU::synchronize_ppu() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::synchronize_coprocessors() {
|
void CPU::synchronizeCoprocessors() {
|
||||||
for(unsigned i = 0; i < coprocessors.size(); i++) {
|
for(unsigned i = 0; i < coprocessors.size(); i++) {
|
||||||
auto& chip = *coprocessors[i];
|
auto& chip = *coprocessors[i];
|
||||||
if(chip.clock < 0) co_switch(chip.thread);
|
if(chip.clock < 0) co_switch(chip.thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::synchronize_controllers() {
|
void CPU::synchronizeDevices() {
|
||||||
if(input.port1->clock < 0) co_switch(input.port1->thread);
|
if(device.controllerPort1->clock < 0) co_switch(device.controllerPort1->thread);
|
||||||
if(input.port2->clock < 0) co_switch(input.port2->thread);
|
if(device.controllerPort2->clock < 0) co_switch(device.controllerPort2->thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::Enter() { cpu.enter(); }
|
void CPU::Enter() { cpu.enter(); }
|
||||||
|
@ -110,7 +110,7 @@ void CPU::power() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::reset() {
|
void CPU::reset() {
|
||||||
create(Enter, system.cpu_frequency());
|
create(Enter, system.cpuFrequency());
|
||||||
coprocessors.reset();
|
coprocessors.reset();
|
||||||
PPUcounter::reset();
|
PPUcounter::reset();
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,10 @@ struct CPU : Processor::R65816, Thread, public PPUcounter {
|
||||||
enum : bool { Threaded = true };
|
enum : bool { Threaded = true };
|
||||||
vector<Thread*> coprocessors;
|
vector<Thread*> coprocessors;
|
||||||
alwaysinline void step(unsigned clocks);
|
alwaysinline void step(unsigned clocks);
|
||||||
alwaysinline void synchronize_smp();
|
alwaysinline void synchronizeSMP();
|
||||||
void synchronize_ppu();
|
void synchronizePPU();
|
||||||
void synchronize_coprocessors();
|
void synchronizeCoprocessors();
|
||||||
void synchronize_controllers();
|
void synchronizeDevices();
|
||||||
|
|
||||||
uint8 pio();
|
uint8 pio();
|
||||||
bool joylatch();
|
bool joylatch();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
uint8 CPU::mmio_read(unsigned addr) {
|
uint8 CPU::mmio_read(unsigned addr) {
|
||||||
if((addr & 0xffc0) == 0x2140) {
|
if((addr & 0xffc0) == 0x2140) {
|
||||||
synchronize_smp();
|
synchronizeSMP();
|
||||||
return smp.port_read(addr & 3);
|
return smp.port_read(addr & 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,13 +15,13 @@ uint8 CPU::mmio_read(unsigned addr) {
|
||||||
|
|
||||||
case 0x4016: {
|
case 0x4016: {
|
||||||
uint8 result = regs.mdr & 0xfc;
|
uint8 result = regs.mdr & 0xfc;
|
||||||
result |= input.port1->data() & 3;
|
result |= device.controllerPort1->data() & 3;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x4017: {
|
case 0x4017: {
|
||||||
uint8 result = (regs.mdr & 0xe0) | 0x1c;
|
uint8 result = (regs.mdr & 0xe0) | 0x1c;
|
||||||
result |= input.port2->data() & 3;
|
result |= device.controllerPort2->data() & 3;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ uint8 CPU::mmio_read(unsigned addr) {
|
||||||
|
|
||||||
void CPU::mmio_write(unsigned addr, uint8 data) {
|
void CPU::mmio_write(unsigned addr, uint8 data) {
|
||||||
if((addr & 0xffc0) == 0x2140) {
|
if((addr & 0xffc0) == 0x2140) {
|
||||||
synchronize_smp();
|
synchronizeSMP();
|
||||||
port_write(addr & 3, data);
|
port_write(addr & 3, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -127,8 +127,8 @@ void CPU::mmio_write(unsigned addr, uint8 data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x4016: {
|
case 0x4016: {
|
||||||
input.port1->latch(data & 1);
|
device.controllerPort1->latch(data & 1);
|
||||||
input.port2->latch(data & 1);
|
device.controllerPort2->latch(data & 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,9 +59,9 @@ void CPU::add_clocks(unsigned clocks) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::scanline() {
|
void CPU::scanline() {
|
||||||
synchronize_smp();
|
synchronizeSMP();
|
||||||
synchronize_ppu();
|
synchronizePPU();
|
||||||
synchronize_coprocessors();
|
synchronizeCoprocessors();
|
||||||
system.scanline();
|
system.scanline();
|
||||||
|
|
||||||
if(vcounter() == 0) hdma_init();
|
if(vcounter() == 0) hdma_init();
|
||||||
|
@ -87,15 +87,15 @@ void CPU::scanline() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::run_auto_joypad_poll() {
|
void CPU::run_auto_joypad_poll() {
|
||||||
input.port1->latch(1);
|
device.controllerPort1->latch(1);
|
||||||
input.port2->latch(1);
|
device.controllerPort2->latch(1);
|
||||||
input.port1->latch(0);
|
device.controllerPort1->latch(0);
|
||||||
input.port2->latch(0);
|
device.controllerPort2->latch(0);
|
||||||
|
|
||||||
uint16 joy1 = 0, joy2 = 0, joy3 = 0, joy4 = 0;
|
uint16 joy1 = 0, joy2 = 0, joy3 = 0, joy4 = 0;
|
||||||
for(unsigned i = 0; i < 16; i++) {
|
for(unsigned i = 0; i < 16; i++) {
|
||||||
uint8 port0 = input.port1->data();
|
uint8 port0 = device.controllerPort1->data();
|
||||||
uint8 port1 = input.port2->data();
|
uint8 port1 = device.controllerPort2->data();
|
||||||
|
|
||||||
joy1 |= (port0 & 1) ? (0x8000 >> i) : 0;
|
joy1 |= (port0 & 1) ? (0x8000 >> i) : 0;
|
||||||
joy2 |= (port1 & 1) ? (0x8000 >> i) : 0;
|
joy2 |= (port1 & 1) ? (0x8000 >> i) : 0;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#include <sfc/sfc.hpp>
|
#include <sfc/sfc.hpp>
|
||||||
|
|
||||||
#define DSP_CPP
|
|
||||||
namespace SuperFamicom {
|
namespace SuperFamicom {
|
||||||
|
|
||||||
DSP dsp;
|
DSP dsp;
|
||||||
|
@ -8,11 +7,15 @@ DSP dsp;
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
#include "SPC_DSP.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;
|
clock += clocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSP::synchronize_smp() {
|
auto DSP::synchronizeSMP() -> void {
|
||||||
if(SMP::Threaded == true) {
|
if(SMP::Threaded == true) {
|
||||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(smp.thread);
|
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(smp.thread);
|
||||||
} else {
|
} else {
|
||||||
|
@ -20,52 +23,48 @@ void DSP::synchronize_smp() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSP::enter() {
|
auto DSP::enter() -> void {
|
||||||
spc_dsp.run(1);
|
spc_dsp.run(1);
|
||||||
step(24);
|
step(24);
|
||||||
|
|
||||||
signed count = spc_dsp.sample_count();
|
int count = spc_dsp.sample_count();
|
||||||
if(count > 0) {
|
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);
|
spc_dsp.set_output(samplebuffer, 8192);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DSP::mute() {
|
auto DSP::mute() -> bool {
|
||||||
return spc_dsp.mute();
|
return spc_dsp.mute();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 DSP::read(uint8 addr) {
|
auto DSP::read(uint8 addr) -> uint8 {
|
||||||
return spc_dsp.read(addr);
|
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);
|
spc_dsp.write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSP::power() {
|
auto DSP::power() -> void {
|
||||||
spc_dsp.init(smp.apuram);
|
spc_dsp.init(smp.apuram);
|
||||||
spc_dsp.reset();
|
spc_dsp.reset();
|
||||||
spc_dsp.set_output(samplebuffer, 8192);
|
spc_dsp.set_output(samplebuffer, 8192);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSP::reset() {
|
auto DSP::reset() -> void {
|
||||||
Thread::clock = 0;
|
Thread::clock = 0;
|
||||||
spc_dsp.soft_reset();
|
spc_dsp.soft_reset();
|
||||||
spc_dsp.set_output(samplebuffer, 8192);
|
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;
|
channel_enabled[channel & 7] = enable;
|
||||||
unsigned mask = 0;
|
uint mask = 0;
|
||||||
for(unsigned i = 0; i < 8; i++) {
|
for(auto i : range(8)) {
|
||||||
if(channel_enabled[i] == false) mask |= 1 << i;
|
if(channel_enabled[i] == false) mask |= 1 << i;
|
||||||
}
|
}
|
||||||
spc_dsp.mute_voices(mask);
|
spc_dsp.mute_voices(mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
DSP::DSP() {
|
|
||||||
for(unsigned i = 0; i < 8; i++) channel_enabled[i] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,22 +2,24 @@
|
||||||
|
|
||||||
struct DSP : Thread {
|
struct DSP : Thread {
|
||||||
enum : bool { Threaded = false };
|
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();
|
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:
|
private:
|
||||||
SPC_DSP spc_dsp;
|
SPC_DSP spc_dsp;
|
||||||
int16 samplebuffer[8192];
|
int16 samplebuffer[8192];
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
#ifdef DSP_CPP
|
static auto dsp_state_save(unsigned char** out, void* in, size_t size) -> void {
|
||||||
|
|
||||||
static void dsp_state_save(unsigned char **out, void *in, size_t size) {
|
|
||||||
memcpy(*out, in, size);
|
memcpy(*out, in, size);
|
||||||
*out += 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);
|
memcpy(out, *in, size);
|
||||||
*in += size;
|
*in += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSP::serialize(serializer &s) {
|
auto DSP::serialize(serializer &s) -> void {
|
||||||
Thread::serialize(s);
|
Thread::serialize(s);
|
||||||
s.array(samplebuffer);
|
s.array(samplebuffer);
|
||||||
|
|
||||||
|
@ -27,5 +25,3 @@ void DSP::serialize(serializer &s) {
|
||||||
s.array(state);
|
s.array(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
#ifdef PPU_CPP
|
auto PPU::latch_counters() -> void {
|
||||||
|
|
||||||
void PPU::latch_counters() {
|
|
||||||
regs.hcounter = cpu.hdot();
|
regs.hcounter = cpu.hdot();
|
||||||
regs.vcounter = cpu.vcounter();
|
regs.vcounter = cpu.vcounter();
|
||||||
regs.counters_latched = true;
|
regs.counters_latched = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 PPU::get_vram_address() {
|
auto PPU::get_vram_address() -> uint16 {
|
||||||
uint16 addr = regs.vram_addr;
|
uint16 addr = regs.vram_addr;
|
||||||
switch(regs.vram_mapping) {
|
switch(regs.vram_mapping) {
|
||||||
case 0: break; //direct 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
|
//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.
|
//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;
|
uint8 data;
|
||||||
|
|
||||||
if(regs.display_disabled == true) {
|
if(regs.display_disabled == true) {
|
||||||
|
@ -51,7 +49,7 @@ uint8 PPU::vram_mmio_read(uint16 addr) {
|
||||||
return data;
|
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) {
|
if(regs.display_disabled == true) {
|
||||||
vram[addr] = data;
|
vram[addr] = data;
|
||||||
} else {
|
} 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;
|
addr &= 0x03ff;
|
||||||
if(addr & 0x0200) addr &= 0x021f;
|
if(addr & 0x0200) addr &= 0x021f;
|
||||||
uint8 data;
|
uint8 data;
|
||||||
|
@ -97,7 +95,7 @@ uint8 PPU::oam_mmio_read(uint16 addr) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::oam_mmio_write(uint16 addr, uint8 data) {
|
auto PPU::oam_mmio_write(uint16 addr, uint8 data) -> void {
|
||||||
addr &= 0x03ff;
|
addr &= 0x03ff;
|
||||||
if(addr & 0x0200) addr &= 0x021f;
|
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;
|
addr &= 0x01ff;
|
||||||
uint8 data;
|
uint8 data;
|
||||||
|
|
||||||
|
@ -137,7 +135,7 @@ uint8 PPU::cgram_mmio_read(uint16 addr) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::cgram_mmio_write(uint16 addr, uint8 data) {
|
auto PPU::cgram_mmio_write(uint16 addr, uint8 data) -> void {
|
||||||
addr &= 0x01ff;
|
addr &= 0x01ff;
|
||||||
if(addr & 1) data &= 0x7f;
|
if(addr & 1) data &= 0x7f;
|
||||||
|
|
||||||
|
@ -153,5 +151,3 @@ void PPU::cgram_mmio_write(uint16 addr, uint8 data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
uint16 get_vram_address();
|
auto get_vram_address() -> uint16;
|
||||||
|
|
||||||
uint8 vram_mmio_read(uint16 addr);
|
auto vram_mmio_read(uint16 addr) -> uint8;
|
||||||
void vram_mmio_write(uint16 addr, uint8 data);
|
auto vram_mmio_write(uint16 addr, uint8 data) -> void;
|
||||||
|
|
||||||
uint8 oam_mmio_read(uint16 addr);
|
auto oam_mmio_read(uint16 addr) -> uint8;
|
||||||
void oam_mmio_write(uint16 addr, uint8 data);
|
auto oam_mmio_write(uint16 addr, uint8 data) -> void;
|
||||||
|
|
||||||
uint8 cgram_mmio_read(uint16 addr);
|
auto cgram_mmio_read(uint16 addr) -> uint8;
|
||||||
void cgram_mmio_write(uint16 addr, uint8 data);
|
auto cgram_mmio_write(uint16 addr, uint8 data) -> void;
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#ifdef PPU_CPP
|
|
||||||
|
|
||||||
//INIDISP
|
//INIDISP
|
||||||
void PPU::mmio_w2100(uint8 value) {
|
auto PPU::mmio_w2100(uint8 value) -> void {
|
||||||
if(regs.display_disabled == true && cpu.vcounter() == (!overscan() ? 225 : 240)) {
|
if(regs.display_disabled == true && cpu.vcounter() == (!overscan() ? 225 : 240)) {
|
||||||
regs.oam_addr = regs.oam_baseaddr << 1;
|
regs.oam_addr = regs.oam_baseaddr << 1;
|
||||||
regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127;
|
regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127;
|
||||||
|
@ -12,14 +10,14 @@ void PPU::mmio_w2100(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//OBSEL
|
//OBSEL
|
||||||
void PPU::mmio_w2101(uint8 value) {
|
auto PPU::mmio_w2101(uint8 value) -> void {
|
||||||
regs.oam_basesize = (value >> 5) & 7;
|
regs.oam_basesize = (value >> 5) & 7;
|
||||||
regs.oam_nameselect = (value >> 3) & 3;
|
regs.oam_nameselect = (value >> 3) & 3;
|
||||||
regs.oam_tdaddr = (value & 3) << 14;
|
regs.oam_tdaddr = (value & 3) << 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
//OAMADDL
|
//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 = (regs.oam_baseaddr & ~0xff) | (data << 0);
|
||||||
regs.oam_baseaddr &= 0x01ff;
|
regs.oam_baseaddr &= 0x01ff;
|
||||||
regs.oam_addr = regs.oam_baseaddr << 1;
|
regs.oam_addr = regs.oam_baseaddr << 1;
|
||||||
|
@ -27,7 +25,7 @@ void PPU::mmio_w2102(uint8 data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//OAMADDH
|
//OAMADDH
|
||||||
void PPU::mmio_w2103(uint8 data) {
|
auto PPU::mmio_w2103(uint8 data) -> void {
|
||||||
regs.oam_priority = !!(data & 0x80);
|
regs.oam_priority = !!(data & 0x80);
|
||||||
regs.oam_baseaddr = (regs.oam_baseaddr & 0xff) | (data << 8);
|
regs.oam_baseaddr = (regs.oam_baseaddr & 0xff) | (data << 8);
|
||||||
regs.oam_baseaddr &= 0x01ff;
|
regs.oam_baseaddr &= 0x01ff;
|
||||||
|
@ -36,7 +34,7 @@ void PPU::mmio_w2103(uint8 data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//OAMDATA
|
//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 & 1) == 0) regs.oam_latchdata = data;
|
||||||
|
|
||||||
if(regs.oam_addr & 0x0200) {
|
if(regs.oam_addr & 0x0200) {
|
||||||
|
@ -52,7 +50,7 @@ void PPU::mmio_w2104(uint8 data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//BGMODE
|
//BGMODE
|
||||||
void PPU::mmio_w2105(uint8 value) {
|
auto PPU::mmio_w2105(uint8 value) -> void {
|
||||||
regs.bg_tilesize[BG4] = !!(value & 0x80);
|
regs.bg_tilesize[BG4] = !!(value & 0x80);
|
||||||
regs.bg_tilesize[BG3] = !!(value & 0x40);
|
regs.bg_tilesize[BG3] = !!(value & 0x40);
|
||||||
regs.bg_tilesize[BG2] = !!(value & 0x20);
|
regs.bg_tilesize[BG2] = !!(value & 0x20);
|
||||||
|
@ -62,7 +60,7 @@ void PPU::mmio_w2105(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//MOSAIC
|
//MOSAIC
|
||||||
void PPU::mmio_w2106(uint8 value) {
|
auto PPU::mmio_w2106(uint8 value) -> void {
|
||||||
regs.mosaic_size = (value >> 4) & 15;
|
regs.mosaic_size = (value >> 4) & 15;
|
||||||
regs.mosaic_enabled[BG4] = !!(value & 0x08);
|
regs.mosaic_enabled[BG4] = !!(value & 0x08);
|
||||||
regs.mosaic_enabled[BG3] = !!(value & 0x04);
|
regs.mosaic_enabled[BG3] = !!(value & 0x04);
|
||||||
|
@ -71,43 +69,43 @@ void PPU::mmio_w2106(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//BG1SC
|
//BG1SC
|
||||||
void PPU::mmio_w2107(uint8 value) {
|
auto PPU::mmio_w2107(uint8 value) -> void {
|
||||||
regs.bg_scaddr[BG1] = (value & 0x7c) << 9;
|
regs.bg_scaddr[BG1] = (value & 0x7c) << 9;
|
||||||
regs.bg_scsize[BG1] = value & 3;
|
regs.bg_scsize[BG1] = value & 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BG2SC
|
//BG2SC
|
||||||
void PPU::mmio_w2108(uint8 value) {
|
auto PPU::mmio_w2108(uint8 value) -> void {
|
||||||
regs.bg_scaddr[BG2] = (value & 0x7c) << 9;
|
regs.bg_scaddr[BG2] = (value & 0x7c) << 9;
|
||||||
regs.bg_scsize[BG2] = value & 3;
|
regs.bg_scsize[BG2] = value & 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BG3SC
|
//BG3SC
|
||||||
void PPU::mmio_w2109(uint8 value) {
|
auto PPU::mmio_w2109(uint8 value) -> void {
|
||||||
regs.bg_scaddr[BG3] = (value & 0x7c) << 9;
|
regs.bg_scaddr[BG3] = (value & 0x7c) << 9;
|
||||||
regs.bg_scsize[BG3] = value & 3;
|
regs.bg_scsize[BG3] = value & 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BG4SC
|
//BG4SC
|
||||||
void PPU::mmio_w210a(uint8 value) {
|
auto PPU::mmio_w210a(uint8 value) -> void {
|
||||||
regs.bg_scaddr[BG4] = (value & 0x7c) << 9;
|
regs.bg_scaddr[BG4] = (value & 0x7c) << 9;
|
||||||
regs.bg_scsize[BG4] = value & 3;
|
regs.bg_scsize[BG4] = value & 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BG12NBA
|
//BG12NBA
|
||||||
void PPU::mmio_w210b(uint8 value) {
|
auto PPU::mmio_w210b(uint8 value) -> void {
|
||||||
regs.bg_tdaddr[BG1] = (value & 0x07) << 13;
|
regs.bg_tdaddr[BG1] = (value & 0x07) << 13;
|
||||||
regs.bg_tdaddr[BG2] = (value & 0x70) << 9;
|
regs.bg_tdaddr[BG2] = (value & 0x70) << 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BG34NBA
|
//BG34NBA
|
||||||
void PPU::mmio_w210c(uint8 value) {
|
auto PPU::mmio_w210c(uint8 value) -> void {
|
||||||
regs.bg_tdaddr[BG3] = (value & 0x07) << 13;
|
regs.bg_tdaddr[BG3] = (value & 0x07) << 13;
|
||||||
regs.bg_tdaddr[BG4] = (value & 0x70) << 9;
|
regs.bg_tdaddr[BG4] = (value & 0x70) << 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BG1HOFS
|
//BG1HOFS
|
||||||
void PPU::mmio_w210d(uint8 value) {
|
auto PPU::mmio_w210d(uint8 value) -> void {
|
||||||
regs.m7_hofs = (value << 8) | regs.m7_latch;
|
regs.m7_hofs = (value << 8) | regs.m7_latch;
|
||||||
regs.m7_latch = value;
|
regs.m7_latch = value;
|
||||||
|
|
||||||
|
@ -116,7 +114,7 @@ void PPU::mmio_w210d(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//BG1VOFS
|
//BG1VOFS
|
||||||
void PPU::mmio_w210e(uint8 value) {
|
auto PPU::mmio_w210e(uint8 value) -> void {
|
||||||
regs.m7_vofs = (value << 8) | regs.m7_latch;
|
regs.m7_vofs = (value << 8) | regs.m7_latch;
|
||||||
regs.m7_latch = value;
|
regs.m7_latch = value;
|
||||||
|
|
||||||
|
@ -125,43 +123,43 @@ void PPU::mmio_w210e(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//BG2HOFS
|
//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_hofs[BG2] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG2] >> 8) & 7);
|
||||||
regs.bg_ofslatch = value;
|
regs.bg_ofslatch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BG2VOFS
|
//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_vofs[BG2] = (value << 8) | (regs.bg_ofslatch);
|
||||||
regs.bg_ofslatch = value;
|
regs.bg_ofslatch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BG3HOFS
|
//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_hofs[BG3] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG3] >> 8) & 7);
|
||||||
regs.bg_ofslatch = value;
|
regs.bg_ofslatch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BG3VOFS
|
//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_vofs[BG3] = (value << 8) | (regs.bg_ofslatch);
|
||||||
regs.bg_ofslatch = value;
|
regs.bg_ofslatch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BG4HOFS
|
//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_hofs[BG4] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG4] >> 8) & 7);
|
||||||
regs.bg_ofslatch = value;
|
regs.bg_ofslatch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BG4VOFS
|
//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_vofs[BG4] = (value << 8) | (regs.bg_ofslatch);
|
||||||
regs.bg_ofslatch = value;
|
regs.bg_ofslatch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//VMAIN
|
//VMAIN
|
||||||
void PPU::mmio_w2115(uint8 value) {
|
auto PPU::mmio_w2115(uint8 value) -> void {
|
||||||
regs.vram_incmode = !!(value & 0x80);
|
regs.vram_incmode = !!(value & 0x80);
|
||||||
regs.vram_mapping = (value >> 2) & 3;
|
regs.vram_mapping = (value >> 2) & 3;
|
||||||
switch(value & 3) {
|
switch(value & 3) {
|
||||||
|
@ -173,7 +171,7 @@ void PPU::mmio_w2115(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//VMADDL
|
//VMADDL
|
||||||
void PPU::mmio_w2116(uint8 value) {
|
auto PPU::mmio_w2116(uint8 value) -> void {
|
||||||
regs.vram_addr = (regs.vram_addr & 0xff00) | value;
|
regs.vram_addr = (regs.vram_addr & 0xff00) | value;
|
||||||
uint16 addr = get_vram_address();
|
uint16 addr = get_vram_address();
|
||||||
regs.vram_readbuffer = vram_mmio_read(addr + 0);
|
regs.vram_readbuffer = vram_mmio_read(addr + 0);
|
||||||
|
@ -181,7 +179,7 @@ void PPU::mmio_w2116(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//VMADDH
|
//VMADDH
|
||||||
void PPU::mmio_w2117(uint8 value) {
|
auto PPU::mmio_w2117(uint8 value) -> void {
|
||||||
regs.vram_addr = (value << 8) | (regs.vram_addr & 0x00ff);
|
regs.vram_addr = (value << 8) | (regs.vram_addr & 0x00ff);
|
||||||
uint16 addr = get_vram_address();
|
uint16 addr = get_vram_address();
|
||||||
regs.vram_readbuffer = vram_mmio_read(addr + 0);
|
regs.vram_readbuffer = vram_mmio_read(addr + 0);
|
||||||
|
@ -189,8 +187,8 @@ void PPU::mmio_w2117(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//VMDATAL
|
//VMDATAL
|
||||||
void PPU::mmio_w2118(uint8 value) {
|
auto PPU::mmio_w2118(uint8 value) -> void {
|
||||||
uint16 addr = get_vram_address();
|
uint16 addr = get_vram_address();
|
||||||
vram_mmio_write(addr, value);
|
vram_mmio_write(addr, value);
|
||||||
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
|
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
|
||||||
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
|
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
|
||||||
|
@ -202,8 +200,8 @@ uint16 addr = get_vram_address();
|
||||||
}
|
}
|
||||||
|
|
||||||
//VMDATAH
|
//VMDATAH
|
||||||
void PPU::mmio_w2119(uint8 value) {
|
auto PPU::mmio_w2119(uint8 value) -> void {
|
||||||
uint16 addr = get_vram_address() + 1;
|
uint16 addr = get_vram_address() + 1;
|
||||||
vram_mmio_write(addr, value);
|
vram_mmio_write(addr, value);
|
||||||
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
|
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
|
||||||
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
|
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
|
||||||
|
@ -215,50 +213,50 @@ uint16 addr = get_vram_address() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//M7SEL
|
//M7SEL
|
||||||
void PPU::mmio_w211a(uint8 value) {
|
auto PPU::mmio_w211a(uint8 value) -> void {
|
||||||
regs.mode7_repeat = (value >> 6) & 3;
|
regs.mode7_repeat = (value >> 6) & 3;
|
||||||
regs.mode7_vflip = !!(value & 0x02);
|
regs.mode7_vflip = !!(value & 0x02);
|
||||||
regs.mode7_hflip = !!(value & 0x01);
|
regs.mode7_hflip = !!(value & 0x01);
|
||||||
}
|
}
|
||||||
|
|
||||||
//M7A
|
//M7A
|
||||||
void PPU::mmio_w211b(uint8 value) {
|
auto PPU::mmio_w211b(uint8 value) -> void {
|
||||||
regs.m7a = (value << 8) | regs.m7_latch;
|
regs.m7a = (value << 8) | regs.m7_latch;
|
||||||
regs.m7_latch = value;
|
regs.m7_latch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//M7B
|
//M7B
|
||||||
void PPU::mmio_w211c(uint8 value) {
|
auto PPU::mmio_w211c(uint8 value) -> void {
|
||||||
regs.m7b = (value << 8) | regs.m7_latch;
|
regs.m7b = (value << 8) | regs.m7_latch;
|
||||||
regs.m7_latch = value;
|
regs.m7_latch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//M7C
|
//M7C
|
||||||
void PPU::mmio_w211d(uint8 value) {
|
auto PPU::mmio_w211d(uint8 value) -> void {
|
||||||
regs.m7c = (value << 8) | regs.m7_latch;
|
regs.m7c = (value << 8) | regs.m7_latch;
|
||||||
regs.m7_latch = value;
|
regs.m7_latch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//M7D
|
//M7D
|
||||||
void PPU::mmio_w211e(uint8 value) {
|
auto PPU::mmio_w211e(uint8 value) -> void {
|
||||||
regs.m7d = (value << 8) | regs.m7_latch;
|
regs.m7d = (value << 8) | regs.m7_latch;
|
||||||
regs.m7_latch = value;
|
regs.m7_latch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//M7X
|
//M7X
|
||||||
void PPU::mmio_w211f(uint8 value) {
|
auto PPU::mmio_w211f(uint8 value) -> void {
|
||||||
regs.m7x = (value << 8) | regs.m7_latch;
|
regs.m7x = (value << 8) | regs.m7_latch;
|
||||||
regs.m7_latch = value;
|
regs.m7_latch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//M7Y
|
//M7Y
|
||||||
void PPU::mmio_w2120(uint8 value) {
|
auto PPU::mmio_w2120(uint8 value) -> void {
|
||||||
regs.m7y = (value << 8) | regs.m7_latch;
|
regs.m7y = (value << 8) | regs.m7_latch;
|
||||||
regs.m7_latch = value;
|
regs.m7_latch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//CGADD
|
//CGADD
|
||||||
void PPU::mmio_w2121(uint8 value) {
|
auto PPU::mmio_w2121(uint8 value) -> void {
|
||||||
regs.cgram_addr = value << 1;
|
regs.cgram_addr = value << 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +268,7 @@ void PPU::mmio_w2121(uint8 value) {
|
||||||
//anomie indicates writes to CGDATA work the same
|
//anomie indicates writes to CGDATA work the same
|
||||||
//as writes to OAMDATA's low table. need to verify
|
//as writes to OAMDATA's low table. need to verify
|
||||||
//this on hardware.
|
//this on hardware.
|
||||||
void PPU::mmio_w2122(uint8 value) {
|
auto PPU::mmio_w2122(uint8 value) -> void {
|
||||||
if(!(regs.cgram_addr & 1)) {
|
if(!(regs.cgram_addr & 1)) {
|
||||||
regs.cgram_latchdata = value;
|
regs.cgram_latchdata = value;
|
||||||
} else {
|
} else {
|
||||||
|
@ -282,7 +280,7 @@ void PPU::mmio_w2122(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//W12SEL
|
//W12SEL
|
||||||
void PPU::mmio_w2123(uint8 value) {
|
auto PPU::mmio_w2123(uint8 value) -> void {
|
||||||
regs.window2_enabled[BG2] = !!(value & 0x80);
|
regs.window2_enabled[BG2] = !!(value & 0x80);
|
||||||
regs.window2_invert [BG2] = !!(value & 0x40);
|
regs.window2_invert [BG2] = !!(value & 0x40);
|
||||||
regs.window1_enabled[BG2] = !!(value & 0x20);
|
regs.window1_enabled[BG2] = !!(value & 0x20);
|
||||||
|
@ -294,7 +292,7 @@ void PPU::mmio_w2123(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//W34SEL
|
//W34SEL
|
||||||
void PPU::mmio_w2124(uint8 value) {
|
auto PPU::mmio_w2124(uint8 value) -> void {
|
||||||
regs.window2_enabled[BG4] = !!(value & 0x80);
|
regs.window2_enabled[BG4] = !!(value & 0x80);
|
||||||
regs.window2_invert [BG4] = !!(value & 0x40);
|
regs.window2_invert [BG4] = !!(value & 0x40);
|
||||||
regs.window1_enabled[BG4] = !!(value & 0x20);
|
regs.window1_enabled[BG4] = !!(value & 0x20);
|
||||||
|
@ -306,7 +304,7 @@ void PPU::mmio_w2124(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//WOBJSEL
|
//WOBJSEL
|
||||||
void PPU::mmio_w2125(uint8 value) {
|
auto PPU::mmio_w2125(uint8 value) -> void {
|
||||||
regs.window2_enabled[COL] = !!(value & 0x80);
|
regs.window2_enabled[COL] = !!(value & 0x80);
|
||||||
regs.window2_invert [COL] = !!(value & 0x40);
|
regs.window2_invert [COL] = !!(value & 0x40);
|
||||||
regs.window1_enabled[COL] = !!(value & 0x20);
|
regs.window1_enabled[COL] = !!(value & 0x20);
|
||||||
|
@ -318,27 +316,27 @@ void PPU::mmio_w2125(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//WH0
|
//WH0
|
||||||
void PPU::mmio_w2126(uint8 value) {
|
auto PPU::mmio_w2126(uint8 value) -> void {
|
||||||
regs.window1_left = value;
|
regs.window1_left = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//WH1
|
//WH1
|
||||||
void PPU::mmio_w2127(uint8 value) {
|
auto PPU::mmio_w2127(uint8 value) -> void {
|
||||||
regs.window1_right = value;
|
regs.window1_right = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//WH2
|
//WH2
|
||||||
void PPU::mmio_w2128(uint8 value) {
|
auto PPU::mmio_w2128(uint8 value) -> void {
|
||||||
regs.window2_left = value;
|
regs.window2_left = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//WH3
|
//WH3
|
||||||
void PPU::mmio_w2129(uint8 value) {
|
auto PPU::mmio_w2129(uint8 value) -> void {
|
||||||
regs.window2_right = value;
|
regs.window2_right = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//WBGLOG
|
//WBGLOG
|
||||||
void PPU::mmio_w212a(uint8 value) {
|
auto PPU::mmio_w212a(uint8 value) -> void {
|
||||||
regs.window_mask[BG4] = (value >> 6) & 3;
|
regs.window_mask[BG4] = (value >> 6) & 3;
|
||||||
regs.window_mask[BG3] = (value >> 4) & 3;
|
regs.window_mask[BG3] = (value >> 4) & 3;
|
||||||
regs.window_mask[BG2] = (value >> 2) & 3;
|
regs.window_mask[BG2] = (value >> 2) & 3;
|
||||||
|
@ -346,13 +344,13 @@ void PPU::mmio_w212a(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//WOBJLOG
|
//WOBJLOG
|
||||||
void PPU::mmio_w212b(uint8 value) {
|
auto PPU::mmio_w212b(uint8 value) -> void {
|
||||||
regs.window_mask[COL] = (value >> 2) & 3;
|
regs.window_mask[COL] = (value >> 2) & 3;
|
||||||
regs.window_mask[OAM] = (value ) & 3;
|
regs.window_mask[OAM] = (value ) & 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TM
|
//TM
|
||||||
void PPU::mmio_w212c(uint8 value) {
|
auto PPU::mmio_w212c(uint8 value) -> void {
|
||||||
regs.bg_enabled[OAM] = !!(value & 0x10);
|
regs.bg_enabled[OAM] = !!(value & 0x10);
|
||||||
regs.bg_enabled[BG4] = !!(value & 0x08);
|
regs.bg_enabled[BG4] = !!(value & 0x08);
|
||||||
regs.bg_enabled[BG3] = !!(value & 0x04);
|
regs.bg_enabled[BG3] = !!(value & 0x04);
|
||||||
|
@ -361,7 +359,7 @@ void PPU::mmio_w212c(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//TS
|
//TS
|
||||||
void PPU::mmio_w212d(uint8 value) {
|
auto PPU::mmio_w212d(uint8 value) -> void {
|
||||||
regs.bgsub_enabled[OAM] = !!(value & 0x10);
|
regs.bgsub_enabled[OAM] = !!(value & 0x10);
|
||||||
regs.bgsub_enabled[BG4] = !!(value & 0x08);
|
regs.bgsub_enabled[BG4] = !!(value & 0x08);
|
||||||
regs.bgsub_enabled[BG3] = !!(value & 0x04);
|
regs.bgsub_enabled[BG3] = !!(value & 0x04);
|
||||||
|
@ -370,7 +368,7 @@ void PPU::mmio_w212d(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//TMW
|
//TMW
|
||||||
void PPU::mmio_w212e(uint8 value) {
|
auto PPU::mmio_w212e(uint8 value) -> void {
|
||||||
regs.window_enabled[OAM] = !!(value & 0x10);
|
regs.window_enabled[OAM] = !!(value & 0x10);
|
||||||
regs.window_enabled[BG4] = !!(value & 0x08);
|
regs.window_enabled[BG4] = !!(value & 0x08);
|
||||||
regs.window_enabled[BG3] = !!(value & 0x04);
|
regs.window_enabled[BG3] = !!(value & 0x04);
|
||||||
|
@ -379,7 +377,7 @@ void PPU::mmio_w212e(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//TSW
|
//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[OAM] = !!(value & 0x10);
|
||||||
regs.sub_window_enabled[BG4] = !!(value & 0x08);
|
regs.sub_window_enabled[BG4] = !!(value & 0x08);
|
||||||
regs.sub_window_enabled[BG3] = !!(value & 0x04);
|
regs.sub_window_enabled[BG3] = !!(value & 0x04);
|
||||||
|
@ -388,7 +386,7 @@ void PPU::mmio_w212f(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//CGWSEL
|
//CGWSEL
|
||||||
void PPU::mmio_w2130(uint8 value) {
|
auto PPU::mmio_w2130(uint8 value) -> void {
|
||||||
regs.color_mask = (value >> 6) & 3;
|
regs.color_mask = (value >> 6) & 3;
|
||||||
regs.colorsub_mask = (value >> 4) & 3;
|
regs.colorsub_mask = (value >> 4) & 3;
|
||||||
regs.addsub_mode = !!(value & 0x02);
|
regs.addsub_mode = !!(value & 0x02);
|
||||||
|
@ -396,7 +394,7 @@ void PPU::mmio_w2130(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//CGADDSUB
|
//CGADDSUB
|
||||||
void PPU::mmio_w2131(uint8 value) {
|
auto PPU::mmio_w2131(uint8 value) -> void {
|
||||||
regs.color_mode = !!(value & 0x80);
|
regs.color_mode = !!(value & 0x80);
|
||||||
regs.color_halve = !!(value & 0x40);
|
regs.color_halve = !!(value & 0x40);
|
||||||
regs.color_enabled[BACK] = !!(value & 0x20);
|
regs.color_enabled[BACK] = !!(value & 0x20);
|
||||||
|
@ -408,7 +406,7 @@ void PPU::mmio_w2131(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//COLDATA
|
//COLDATA
|
||||||
void PPU::mmio_w2132(uint8 value) {
|
auto PPU::mmio_w2132(uint8 value) -> void {
|
||||||
if(value & 0x80) regs.color_b = value & 0x1f;
|
if(value & 0x80) regs.color_b = value & 0x1f;
|
||||||
if(value & 0x40) regs.color_g = value & 0x1f;
|
if(value & 0x40) regs.color_g = value & 0x1f;
|
||||||
if(value & 0x20) regs.color_r = value & 0x1f;
|
if(value & 0x20) regs.color_r = value & 0x1f;
|
||||||
|
@ -419,7 +417,7 @@ void PPU::mmio_w2132(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//SETINI
|
//SETINI
|
||||||
void PPU::mmio_w2133(uint8 value) {
|
auto PPU::mmio_w2133(uint8 value) -> void {
|
||||||
regs.mode7_extbg = !!(value & 0x40);
|
regs.mode7_extbg = !!(value & 0x40);
|
||||||
regs.pseudo_hires = !!(value & 0x08);
|
regs.pseudo_hires = !!(value & 0x08);
|
||||||
regs.overscan = !!(value & 0x04);
|
regs.overscan = !!(value & 0x04);
|
||||||
|
@ -431,31 +429,31 @@ void PPU::mmio_w2133(uint8 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//MPYL
|
//MPYL
|
||||||
uint8 PPU::mmio_r2134() {
|
auto PPU::mmio_r2134() -> uint8 {
|
||||||
uint32 r;
|
uint32 r;
|
||||||
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
|
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
|
||||||
regs.ppu1_mdr = r;
|
regs.ppu1_mdr = r;
|
||||||
return regs.ppu1_mdr;
|
return regs.ppu1_mdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
//MPYM
|
//MPYM
|
||||||
uint8 PPU::mmio_r2135() {
|
auto PPU::mmio_r2135() -> uint8 {
|
||||||
uint32 r;
|
uint32 r;
|
||||||
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
|
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
|
||||||
regs.ppu1_mdr = r >> 8;
|
regs.ppu1_mdr = r >> 8;
|
||||||
return regs.ppu1_mdr;
|
return regs.ppu1_mdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
//MPYH
|
//MPYH
|
||||||
uint8 PPU::mmio_r2136() {
|
auto PPU::mmio_r2136() -> uint8 {
|
||||||
uint32 r;
|
uint32 r;
|
||||||
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
|
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
|
||||||
regs.ppu1_mdr = r >> 16;
|
regs.ppu1_mdr = r >> 16;
|
||||||
return regs.ppu1_mdr;
|
return regs.ppu1_mdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
//SLHV
|
//SLHV
|
||||||
uint8 PPU::mmio_r2137() {
|
auto PPU::mmio_r2137() -> uint8 {
|
||||||
if(cpu.pio() & 0x80) {
|
if(cpu.pio() & 0x80) {
|
||||||
latch_counters();
|
latch_counters();
|
||||||
}
|
}
|
||||||
|
@ -463,7 +461,7 @@ uint8 PPU::mmio_r2137() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//OAMDATAREAD
|
//OAMDATAREAD
|
||||||
uint8 PPU::mmio_r2138() {
|
auto PPU::mmio_r2138() -> uint8 {
|
||||||
regs.ppu1_mdr = oam_mmio_read(regs.oam_addr);
|
regs.ppu1_mdr = oam_mmio_read(regs.oam_addr);
|
||||||
|
|
||||||
regs.oam_addr++;
|
regs.oam_addr++;
|
||||||
|
@ -474,8 +472,8 @@ uint8 PPU::mmio_r2138() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//VMDATALREAD
|
//VMDATALREAD
|
||||||
uint8 PPU::mmio_r2139() {
|
auto PPU::mmio_r2139() -> uint8 {
|
||||||
uint16 addr = get_vram_address();
|
uint16 addr = get_vram_address();
|
||||||
regs.ppu1_mdr = regs.vram_readbuffer;
|
regs.ppu1_mdr = regs.vram_readbuffer;
|
||||||
if(regs.vram_incmode == 0) {
|
if(regs.vram_incmode == 0) {
|
||||||
addr &= 0xfffe;
|
addr &= 0xfffe;
|
||||||
|
@ -487,8 +485,8 @@ uint16 addr = get_vram_address();
|
||||||
}
|
}
|
||||||
|
|
||||||
//VMDATAHREAD
|
//VMDATAHREAD
|
||||||
uint8 PPU::mmio_r213a() {
|
auto PPU::mmio_r213a() -> uint8 {
|
||||||
uint16 addr = get_vram_address() + 1;
|
uint16 addr = get_vram_address() + 1;
|
||||||
regs.ppu1_mdr = regs.vram_readbuffer >> 8;
|
regs.ppu1_mdr = regs.vram_readbuffer >> 8;
|
||||||
if(regs.vram_incmode == 1) {
|
if(regs.vram_incmode == 1) {
|
||||||
addr &= 0xfffe;
|
addr &= 0xfffe;
|
||||||
|
@ -503,7 +501,7 @@ uint16 addr = get_vram_address() + 1;
|
||||||
//note: CGRAM palette data is 15-bits (0,bbbbb,ggggg,rrrrr)
|
//note: CGRAM palette data is 15-bits (0,bbbbb,ggggg,rrrrr)
|
||||||
//therefore, the high byte read from each color does not
|
//therefore, the high byte read from each color does not
|
||||||
//update bit 7 of the PPU2 MDR.
|
//update bit 7 of the PPU2 MDR.
|
||||||
uint8 PPU::mmio_r213b() {
|
auto PPU::mmio_r213b() -> uint8 {
|
||||||
if(!(regs.cgram_addr & 1)) {
|
if(!(regs.cgram_addr & 1)) {
|
||||||
regs.ppu2_mdr = cgram_mmio_read(regs.cgram_addr) & 0xff;
|
regs.ppu2_mdr = cgram_mmio_read(regs.cgram_addr) & 0xff;
|
||||||
} else {
|
} else {
|
||||||
|
@ -516,7 +514,7 @@ uint8 PPU::mmio_r213b() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//OPHCT
|
//OPHCT
|
||||||
uint8 PPU::mmio_r213c() {
|
auto PPU::mmio_r213c() -> uint8 {
|
||||||
if(!regs.latch_hcounter) {
|
if(!regs.latch_hcounter) {
|
||||||
regs.ppu2_mdr = regs.hcounter & 0xff;
|
regs.ppu2_mdr = regs.hcounter & 0xff;
|
||||||
} else {
|
} else {
|
||||||
|
@ -528,7 +526,7 @@ uint8 PPU::mmio_r213c() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//OPVCT
|
//OPVCT
|
||||||
uint8 PPU::mmio_r213d() {
|
auto PPU::mmio_r213d() -> uint8 {
|
||||||
if(!regs.latch_vcounter) {
|
if(!regs.latch_vcounter) {
|
||||||
regs.ppu2_mdr = regs.vcounter & 0xff;
|
regs.ppu2_mdr = regs.vcounter & 0xff;
|
||||||
} else {
|
} else {
|
||||||
|
@ -540,8 +538,8 @@ uint8 PPU::mmio_r213d() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//STAT77
|
//STAT77
|
||||||
uint8 PPU::mmio_r213e() {
|
auto PPU::mmio_r213e() -> uint8 {
|
||||||
uint8 r = 0x00;
|
uint8 r = 0x00;
|
||||||
r |= (regs.time_over) ? 0x80 : 0x00;
|
r |= (regs.time_over) ? 0x80 : 0x00;
|
||||||
r |= (regs.range_over) ? 0x40 : 0x00;
|
r |= (regs.range_over) ? 0x40 : 0x00;
|
||||||
r |= (regs.ppu1_mdr & 0x10);
|
r |= (regs.ppu1_mdr & 0x10);
|
||||||
|
@ -551,8 +549,8 @@ uint8 r = 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
//STAT78
|
//STAT78
|
||||||
uint8 PPU::mmio_r213f() {
|
auto PPU::mmio_r213f() -> uint8 {
|
||||||
uint8 r = 0x00;
|
uint8 r = 0x00;
|
||||||
regs.latch_hcounter = 0;
|
regs.latch_hcounter = 0;
|
||||||
regs.latch_vcounter = 0;
|
regs.latch_vcounter = 0;
|
||||||
|
|
||||||
|
@ -570,8 +568,8 @@ uint8 r = 0x00;
|
||||||
return regs.ppu2_mdr;
|
return regs.ppu2_mdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 PPU::mmio_read(unsigned addr) {
|
auto PPU::mmio_read(uint addr) -> uint8 {
|
||||||
cpu.synchronize_ppu();
|
cpu.synchronizePPU();
|
||||||
|
|
||||||
switch(addr & 0xffff) {
|
switch(addr & 0xffff) {
|
||||||
case 0x2104:
|
case 0x2104:
|
||||||
|
@ -609,8 +607,8 @@ uint8 PPU::mmio_read(unsigned addr) {
|
||||||
return cpu.regs.mdr;
|
return cpu.regs.mdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::mmio_write(unsigned addr, uint8 data) {
|
auto PPU::mmio_write(uint addr, uint8 data) -> void {
|
||||||
cpu.synchronize_ppu();
|
cpu.synchronizePPU();
|
||||||
|
|
||||||
switch(addr & 0xffff) {
|
switch(addr & 0xffff) {
|
||||||
case 0x2100: return mmio_w2100(data); //INIDISP
|
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
|
case 0x2133: return mmio_w2133(data); //SETINI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -130,73 +130,73 @@ struct {
|
||||||
uint16 oam_itemcount, oam_tilecount;
|
uint16 oam_itemcount, oam_tilecount;
|
||||||
} regs;
|
} regs;
|
||||||
|
|
||||||
void mmio_w2100(uint8 value); //INIDISP
|
auto mmio_w2100(uint8 value) -> void; //INIDISP
|
||||||
void mmio_w2101(uint8 value); //OBSEL
|
auto mmio_w2101(uint8 value) -> void; //OBSEL
|
||||||
void mmio_w2102(uint8 value); //OAMADDL
|
auto mmio_w2102(uint8 value) -> void; //OAMADDL
|
||||||
void mmio_w2103(uint8 value); //OAMADDH
|
auto mmio_w2103(uint8 value) -> void; //OAMADDH
|
||||||
void mmio_w2104(uint8 value); //OAMDATA
|
auto mmio_w2104(uint8 value) -> void; //OAMDATA
|
||||||
void mmio_w2105(uint8 value); //BGMODE
|
auto mmio_w2105(uint8 value) -> void; //BGMODE
|
||||||
void mmio_w2106(uint8 value); //MOSAIC
|
auto mmio_w2106(uint8 value) -> void; //MOSAIC
|
||||||
void mmio_w2107(uint8 value); //BG1SC
|
auto mmio_w2107(uint8 value) -> void; //BG1SC
|
||||||
void mmio_w2108(uint8 value); //BG2SC
|
auto mmio_w2108(uint8 value) -> void; //BG2SC
|
||||||
void mmio_w2109(uint8 value); //BG3SC
|
auto mmio_w2109(uint8 value) -> void; //BG3SC
|
||||||
void mmio_w210a(uint8 value); //BG4SC
|
auto mmio_w210a(uint8 value) -> void; //BG4SC
|
||||||
void mmio_w210b(uint8 value); //BG12NBA
|
auto mmio_w210b(uint8 value) -> void; //BG12NBA
|
||||||
void mmio_w210c(uint8 value); //BG34NBA
|
auto mmio_w210c(uint8 value) -> void; //BG34NBA
|
||||||
void mmio_w210d(uint8 value); //BG1HOFS
|
auto mmio_w210d(uint8 value) -> void; //BG1HOFS
|
||||||
void mmio_w210e(uint8 value); //BG1VOFS
|
auto mmio_w210e(uint8 value) -> void; //BG1VOFS
|
||||||
void mmio_w210f(uint8 value); //BG2HOFS
|
auto mmio_w210f(uint8 value) -> void; //BG2HOFS
|
||||||
void mmio_w2110(uint8 value); //BG2VOFS
|
auto mmio_w2110(uint8 value) -> void; //BG2VOFS
|
||||||
void mmio_w2111(uint8 value); //BG3HOFS
|
auto mmio_w2111(uint8 value) -> void; //BG3HOFS
|
||||||
void mmio_w2112(uint8 value); //BG3VOFS
|
auto mmio_w2112(uint8 value) -> void; //BG3VOFS
|
||||||
void mmio_w2113(uint8 value); //BG4HOFS
|
auto mmio_w2113(uint8 value) -> void; //BG4HOFS
|
||||||
void mmio_w2114(uint8 value); //BG4VOFS
|
auto mmio_w2114(uint8 value) -> void; //BG4VOFS
|
||||||
void mmio_w2115(uint8 value); //VMAIN
|
auto mmio_w2115(uint8 value) -> void; //VMAIN
|
||||||
void mmio_w2116(uint8 value); //VMADDL
|
auto mmio_w2116(uint8 value) -> void; //VMADDL
|
||||||
void mmio_w2117(uint8 value); //VMADDH
|
auto mmio_w2117(uint8 value) -> void; //VMADDH
|
||||||
void mmio_w2118(uint8 value); //VMDATAL
|
auto mmio_w2118(uint8 value) -> void; //VMDATAL
|
||||||
void mmio_w2119(uint8 value); //VMDATAH
|
auto mmio_w2119(uint8 value) -> void; //VMDATAH
|
||||||
void mmio_w211a(uint8 value); //M7SEL
|
auto mmio_w211a(uint8 value) -> void; //M7SEL
|
||||||
void mmio_w211b(uint8 value); //M7A
|
auto mmio_w211b(uint8 value) -> void; //M7A
|
||||||
void mmio_w211c(uint8 value); //M7B
|
auto mmio_w211c(uint8 value) -> void; //M7B
|
||||||
void mmio_w211d(uint8 value); //M7C
|
auto mmio_w211d(uint8 value) -> void; //M7C
|
||||||
void mmio_w211e(uint8 value); //M7D
|
auto mmio_w211e(uint8 value) -> void; //M7D
|
||||||
void mmio_w211f(uint8 value); //M7X
|
auto mmio_w211f(uint8 value) -> void; //M7X
|
||||||
void mmio_w2120(uint8 value); //M7Y
|
auto mmio_w2120(uint8 value) -> void; //M7Y
|
||||||
void mmio_w2121(uint8 value); //CGADD
|
auto mmio_w2121(uint8 value) -> void; //CGADD
|
||||||
void mmio_w2122(uint8 value); //CGDATA
|
auto mmio_w2122(uint8 value) -> void; //CGDATA
|
||||||
void mmio_w2123(uint8 value); //W12SEL
|
auto mmio_w2123(uint8 value) -> void; //W12SEL
|
||||||
void mmio_w2124(uint8 value); //W34SEL
|
auto mmio_w2124(uint8 value) -> void; //W34SEL
|
||||||
void mmio_w2125(uint8 value); //WOBJSEL
|
auto mmio_w2125(uint8 value) -> void; //WOBJSEL
|
||||||
void mmio_w2126(uint8 value); //WH0
|
auto mmio_w2126(uint8 value) -> void; //WH0
|
||||||
void mmio_w2127(uint8 value); //WH1
|
auto mmio_w2127(uint8 value) -> void; //WH1
|
||||||
void mmio_w2128(uint8 value); //WH2
|
auto mmio_w2128(uint8 value) -> void; //WH2
|
||||||
void mmio_w2129(uint8 value); //WH3
|
auto mmio_w2129(uint8 value) -> void; //WH3
|
||||||
void mmio_w212a(uint8 value); //WBGLOG
|
auto mmio_w212a(uint8 value) -> void; //WBGLOG
|
||||||
void mmio_w212b(uint8 value); //WOBJLOG
|
auto mmio_w212b(uint8 value) -> void; //WOBJLOG
|
||||||
void mmio_w212c(uint8 value); //TM
|
auto mmio_w212c(uint8 value) -> void; //TM
|
||||||
void mmio_w212d(uint8 value); //TS
|
auto mmio_w212d(uint8 value) -> void; //TS
|
||||||
void mmio_w212e(uint8 value); //TMW
|
auto mmio_w212e(uint8 value) -> void; //TMW
|
||||||
void mmio_w212f(uint8 value); //TSW
|
auto mmio_w212f(uint8 value) -> void; //TSW
|
||||||
void mmio_w2130(uint8 value); //CGWSEL
|
auto mmio_w2130(uint8 value) -> void; //CGWSEL
|
||||||
void mmio_w2131(uint8 value); //CGADDSUB
|
auto mmio_w2131(uint8 value) -> void; //CGADDSUB
|
||||||
void mmio_w2132(uint8 value); //COLDATA
|
auto mmio_w2132(uint8 value) -> void; //COLDATA
|
||||||
void mmio_w2133(uint8 value); //SETINI
|
auto mmio_w2133(uint8 value) -> void; //SETINI
|
||||||
|
|
||||||
uint8 mmio_r2134(); //MPYL
|
auto mmio_r2134() -> uint8; //MPYL
|
||||||
uint8 mmio_r2135(); //MPYM
|
auto mmio_r2135() -> uint8; //MPYM
|
||||||
uint8 mmio_r2136(); //MPYH
|
auto mmio_r2136() -> uint8; //MPYH
|
||||||
uint8 mmio_r2137(); //SLHV
|
auto mmio_r2137() -> uint8; //SLHV
|
||||||
uint8 mmio_r2138(); //OAMDATAREAD
|
auto mmio_r2138() -> uint8; //OAMDATAREAD
|
||||||
uint8 mmio_r2139(); //VMDATALREAD
|
auto mmio_r2139() -> uint8; //VMDATALREAD
|
||||||
uint8 mmio_r213a(); //VMDATAHREAD
|
auto mmio_r213a() -> uint8; //VMDATAHREAD
|
||||||
uint8 mmio_r213b(); //CGDATAREAD
|
auto mmio_r213b() -> uint8; //CGDATAREAD
|
||||||
uint8 mmio_r213c(); //OPHCT
|
auto mmio_r213c() -> uint8; //OPHCT
|
||||||
uint8 mmio_r213d(); //OPVCT
|
auto mmio_r213d() -> uint8; //OPVCT
|
||||||
uint8 mmio_r213e(); //STAT77
|
auto mmio_r213e() -> uint8; //STAT77
|
||||||
uint8 mmio_r213f(); //STAT78
|
auto mmio_r213f() -> uint8; //STAT78
|
||||||
|
|
||||||
uint8 mmio_read(unsigned addr);
|
auto mmio_read(uint addr) -> uint8;
|
||||||
void mmio_write(unsigned addr, uint8 data);
|
auto mmio_write(uint addr, uint8 data) -> void;
|
||||||
|
|
||||||
void latch_counters();
|
auto latch_counters() -> void;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#include <sfc/sfc.hpp>
|
#include <sfc/sfc.hpp>
|
||||||
|
|
||||||
#define PPU_CPP
|
|
||||||
namespace SuperFamicom {
|
namespace SuperFamicom {
|
||||||
|
|
||||||
PPU ppu;
|
PPU ppu;
|
||||||
|
@ -10,11 +9,44 @@ PPU ppu;
|
||||||
#include "render/render.cpp"
|
#include "render/render.cpp"
|
||||||
#include "serialization.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;
|
clock += clocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::synchronize_cpu() {
|
auto PPU::synchronizeCPU() -> void {
|
||||||
if(CPU::Threaded == true) {
|
if(CPU::Threaded == true) {
|
||||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||||
} else {
|
} 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) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
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);
|
tick(clocks);
|
||||||
step(clocks);
|
step(clocks);
|
||||||
synchronize_cpu();
|
synchronizeCPU();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::scanline() {
|
auto PPU::scanline() -> void {
|
||||||
line = vcounter();
|
line = vcounter();
|
||||||
|
|
||||||
if(line == 0) {
|
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(line >= 1 && line < (!overscan() ? 225 : 240)) {
|
||||||
if(framecounter) return;
|
if(framecounter) return;
|
||||||
render_line_oam_rto();
|
render_line_oam_rto();
|
||||||
|
@ -106,7 +138,7 @@ void PPU::render_scanline() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::frame() {
|
auto PPU::frame() -> void {
|
||||||
system.frame();
|
system.frame();
|
||||||
|
|
||||||
if(field() == 0) {
|
if(field() == 0) {
|
||||||
|
@ -117,15 +149,15 @@ void PPU::frame() {
|
||||||
framecounter = (frameskip == 0 ? 0 : (framecounter + 1) % frameskip);
|
framecounter = (frameskip == 0 ? 0 : (framecounter + 1) % frameskip);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::enable() {
|
auto PPU::enable() -> void {
|
||||||
function<uint8 (unsigned)> reader = {&PPU::mmio_read, (PPU*)&ppu};
|
function<uint8 (uint)> reader = {&PPU::mmio_read, (PPU*)&ppu};
|
||||||
function<void (unsigned, uint8)> writer = {&PPU::mmio_write, (PPU*)&ppu};
|
function<void (uint, uint8)> writer = {&PPU::mmio_write, (PPU*)&ppu};
|
||||||
|
|
||||||
bus.map(reader, writer, 0x00, 0x3f, 0x2100, 0x213f);
|
bus.map(reader, writer, 0x00, 0x3f, 0x2100, 0x213f);
|
||||||
bus.map(reader, writer, 0x80, 0xbf, 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 : vram) n = 0x00;
|
||||||
for(auto& n : oam) n = 0x00;
|
for(auto& n : oam) n = 0x00;
|
||||||
for(auto& n : cgram) n = 0x00;
|
for(auto& n : cgram) n = 0x00;
|
||||||
|
@ -340,8 +372,8 @@ void PPU::power() {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::reset() {
|
auto PPU::reset() -> void {
|
||||||
create(Enter, system.cpu_frequency());
|
create(Enter, system.cpuFrequency());
|
||||||
PPUcounter::reset();
|
PPUcounter::reset();
|
||||||
memset(surface, 0, 512 * 512 * sizeof(uint32));
|
memset(surface, 0, 512 * 512 * sizeof(uint32));
|
||||||
|
|
||||||
|
@ -368,7 +400,7 @@ void PPU::reset() {
|
||||||
regs.bg_y[3] = 0;
|
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) {
|
switch(layer * 4 + priority) {
|
||||||
case 0: layer_enabled[BG1][0] = enable; break;
|
case 0: layer_enabled[BG1][0] = enable; break;
|
||||||
case 1: layer_enabled[BG1][1] = 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_;
|
frameskip = frameskip_;
|
||||||
framecounter = 0;
|
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,55 @@
|
||||||
struct PPU : Thread, public PPUcounter {
|
struct PPU : Thread, public PPUcounter {
|
||||||
uint8 vram[128 * 1024];
|
|
||||||
uint8 oam[544];
|
|
||||||
uint8 cgram[512];
|
|
||||||
|
|
||||||
enum : bool { Threaded = true };
|
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 "memory/memory.hpp"
|
||||||
#include "mmio/mmio.hpp"
|
#include "mmio/mmio.hpp"
|
||||||
#include "render/render.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* surface;
|
||||||
uint32* output;
|
uint32* output;
|
||||||
|
|
||||||
unsigned ppu1_version = 1;
|
uint ppu1_version = 1;
|
||||||
unsigned ppu2_version = 3;
|
uint ppu2_version = 3;
|
||||||
|
|
||||||
static void Enter();
|
|
||||||
void add_clocks(unsigned clocks);
|
|
||||||
|
|
||||||
uint8 region;
|
uint8 region;
|
||||||
unsigned line;
|
uint 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 };
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool interlace;
|
bool interlace;
|
||||||
|
@ -45,32 +69,10 @@ struct PPU : Thread, public PPUcounter {
|
||||||
uint16 m7a, m7b, m7c, m7d, m7x, m7y;
|
uint16 m7a, m7b, m7c, m7d, m7x, m7y;
|
||||||
} cache;
|
} 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];
|
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];
|
bool layer_enabled[5][4];
|
||||||
void layer_enable(unsigned layer, unsigned priority, bool enable);
|
uint frameskip;
|
||||||
unsigned frameskip;
|
uint framecounter;
|
||||||
unsigned framecounter;
|
|
||||||
void set_frameskip(unsigned frameskip);
|
|
||||||
|
|
||||||
void serialize(serializer&);
|
|
||||||
PPU();
|
|
||||||
~PPU();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern PPU ppu;
|
extern PPU ppu;
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
#ifdef PPU_CPP
|
|
||||||
|
|
||||||
//color addition / subtraction
|
//color addition / subtraction
|
||||||
//thanks go to blargg for the optimized algorithms
|
//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(!regs.color_mode) {
|
||||||
if(!halve) {
|
if(!halve) {
|
||||||
unsigned sum = x + y;
|
uint sum = x + y;
|
||||||
unsigned carry = (sum - ((x ^ y) & 0x0421)) & 0x8420;
|
uint carry = (sum - ((x ^ y) & 0x0421)) & 0x8420;
|
||||||
return (sum - carry) | (carry - (carry >> 5));
|
return (sum - carry) | (carry - (carry >> 5));
|
||||||
} else {
|
} else {
|
||||||
return (x + y - ((x ^ y) & 0x0421)) >> 1;
|
return (x + y - ((x ^ y) & 0x0421)) >> 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unsigned diff = x - y + 0x8420;
|
uint diff = x - y + 0x8420;
|
||||||
unsigned borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420;
|
uint borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420;
|
||||||
if(!halve) {
|
if(!halve) {
|
||||||
return (diff - borrow) & (borrow - (borrow >> 5));
|
return (diff - borrow) & (borrow - (borrow >> 5));
|
||||||
} else {
|
} else {
|
||||||
|
@ -21,5 +19,3 @@ inline uint16 PPU::addsub(uint32 x, uint32 y, bool halve) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#ifdef PPU_CPP
|
|
||||||
|
|
||||||
//called once at the start of every rendered scanline
|
//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 hires = (regs.bg_mode == 5 || regs.bg_mode == 6);
|
||||||
const unsigned width = (!hires ? 256 : 512);
|
const unsigned width = (!hires ? 256 : 512);
|
||||||
|
|
||||||
|
@ -22,8 +20,8 @@ void PPU::update_bg_info() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned bg>
|
template<uint bg>
|
||||||
uint16 PPU::bg_get_tile(uint16 x, uint16 y) {
|
auto PPU::bg_get_tile(uint16 x, uint16 y) -> uint16 {
|
||||||
x = (x & bg_info[bg].mx) >> bg_info[bg].tw;
|
x = (x & bg_info[bg].mx) >> bg_info[bg].tw;
|
||||||
y = (y & bg_info[bg].my) >> bg_info[bg].th;
|
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; \
|
pixel_cache[x].ce_sub = false; \
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned mode, unsigned bg, unsigned color_depth>
|
template<uint mode, uint bg, uint color_depth>
|
||||||
void PPU::render_line_bg(uint8 pri0_pos, uint8 pri1_pos) {
|
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][0] == false) pri0_pos = 0;
|
||||||
if(layer_enabled[bg][1] == false) pri1_pos = 0;
|
if(layer_enabled[bg][1] == false) pri1_pos = 0;
|
||||||
if(pri0_pos + pri1_pos == 0) return;
|
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_main
|
||||||
#undef setpixel_sub
|
#undef setpixel_sub
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#ifdef PPU_CPP
|
|
||||||
|
|
||||||
#define render_bg_tile_line_2bpp(mask) \
|
#define render_bg_tile_line_2bpp(mask) \
|
||||||
col = !!(d0 & mask) << 0; \
|
col = !!(d0 & mask) << 0; \
|
||||||
col += !!(d1 & mask) << 1; \
|
col += !!(d1 & mask) << 1; \
|
||||||
|
@ -23,8 +21,8 @@
|
||||||
col += !!(d7 & mask) << 7; \
|
col += !!(d7 & mask) << 7; \
|
||||||
*dest++ = col
|
*dest++ = col
|
||||||
|
|
||||||
template<unsigned color_depth>
|
template<uint color_depth>
|
||||||
void PPU::render_bg_tile(uint16 tile_num) {
|
auto PPU::render_bg_tile(uint16 tile_num) -> void {
|
||||||
uint8 col, d0, d1, d2, d3, d4, d5, d6, d7;
|
uint8 col, d0, d1, d2, d3, d4, d5, d6, d7;
|
||||||
|
|
||||||
if(color_depth == COLORDEPTH_4) {
|
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_4bpp
|
||||||
#undef render_bg_tile_line_8bpp
|
#undef render_bg_tile_line_8bpp
|
||||||
|
|
||||||
void PPU::flush_pixel_cache() {
|
auto PPU::flush_pixel_cache() -> void {
|
||||||
uint16 main = get_palette(0);
|
uint16 main = get_palette(0);
|
||||||
uint16 sub = (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6)
|
uint16 sub = (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6)
|
||||||
? main
|
? main
|
||||||
|
@ -119,7 +117,7 @@ void PPU::flush_pixel_cache() {
|
||||||
} while(i--);
|
} while(i--);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::alloc_tiledata_cache() {
|
auto PPU::alloc_tiledata_cache() -> void {
|
||||||
bg_tiledata[TILE_2BIT] = new uint8_t[262144]();
|
bg_tiledata[TILE_2BIT] = new uint8_t[262144]();
|
||||||
bg_tiledata[TILE_4BIT] = new uint8_t[131072]();
|
bg_tiledata[TILE_4BIT] = new uint8_t[131072]();
|
||||||
bg_tiledata[TILE_8BIT] = new uint8_t[ 65536]();
|
bg_tiledata[TILE_8BIT] = new uint8_t[ 65536]();
|
||||||
|
@ -129,13 +127,13 @@ void PPU::alloc_tiledata_cache() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//marks all tiledata cache entries as dirty
|
//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 < 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 < 2048; i++) bg_tiledata_state[TILE_4BIT][i] = 1;
|
||||||
for(unsigned i = 0; i < 1024; i++) bg_tiledata_state[TILE_8BIT][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_2BIT];
|
||||||
delete[] bg_tiledata[TILE_4BIT];
|
delete[] bg_tiledata[TILE_4BIT];
|
||||||
delete[] bg_tiledata[TILE_8BIT];
|
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_4BIT];
|
||||||
delete[] bg_tiledata_state[TILE_8BIT];
|
delete[] bg_tiledata_state[TILE_8BIT];
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
#ifdef PPU_CPP
|
inline auto PPU::get_palette(uint8 index) -> uint16 {
|
||||||
|
const uint addr = index << 1;
|
||||||
inline uint16 PPU::get_palette(uint8 index) {
|
|
||||||
const unsigned addr = index << 1;
|
|
||||||
return cgram[addr] + (cgram[addr + 1] << 8);
|
return cgram[addr] + (cgram[addr + 1] << 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
//p = 00000bgr <palette data>
|
//p = 00000bgr <palette data>
|
||||||
//t = BBGGGRRR <tilemap data>
|
//t = BBGGGRRR <tilemap data>
|
||||||
//r = 0BBb00GGGg0RRRr0 <return 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) |
|
return ((t & 7) << 2) | ((p & 1) << 1) |
|
||||||
(((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) |
|
(((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) |
|
||||||
((t >> 6) << 13) | ((p >> 2) << 12);
|
((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];
|
pixel_t& p = pixel_cache[x];
|
||||||
uint16 src_main, src_sub;
|
uint16 src_main, src_sub;
|
||||||
uint8 bg_sub;
|
uint8 bg_sub;
|
||||||
|
@ -50,7 +48,7 @@ inline uint16 PPU::get_pixel_normal(uint32 x) {
|
||||||
return src_main;
|
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];
|
pixel_t& p = pixel_cache[x];
|
||||||
uint16 src_main, src_sub;
|
uint16 src_main, src_sub;
|
||||||
uint8 bg_sub;
|
uint8 bg_sub;
|
||||||
|
@ -86,7 +84,7 @@ inline uint16 PPU::get_pixel_swap(uint32 x) {
|
||||||
return src_main;
|
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* ptr = (uint32*)output + (line * 1024) + ((interlace() && field()) ? 512 : 0);
|
||||||
uint32 curr, prev;
|
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);
|
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));
|
memset(ptr, 0, width * 2 * sizeof(uint32));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#ifdef PPU_CPP
|
|
||||||
|
|
||||||
//bsnes mode7 renderer
|
//bsnes mode7 renderer
|
||||||
//
|
//
|
||||||
//base algorithm written by anomie
|
//base algorithm written by anomie
|
||||||
|
@ -12,8 +10,8 @@
|
||||||
//--s---vvvvvvvvvv -> ssssssvvvvvvvvvv
|
//--s---vvvvvvvvvv -> ssssssvvvvvvvvvv
|
||||||
#define CLIP(x) ( ((x) & 0x2000) ? ( (x) | ~0x03ff) : ((x) & 0x03ff) )
|
#define CLIP(x) ( ((x) & 0x2000) ? ( (x) | ~0x03ff) : ((x) & 0x03ff) )
|
||||||
|
|
||||||
template<unsigned bg>
|
template<uint bg>
|
||||||
void PPU::render_line_mode7(uint8 pri0_pos, uint8 pri1_pos) {
|
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][0] == false) pri0_pos = 0;
|
||||||
if(layer_enabled[bg][1] == false) pri1_pos = 0;
|
if(layer_enabled[bg][1] == false) pri1_pos = 0;
|
||||||
if(pri0_pos + pri1_pos == 0) return;
|
if(pri0_pos + pri1_pos == 0) return;
|
||||||
|
@ -140,5 +138,3 @@ void PPU::render_line_mode7(uint8 pri0_pos, uint8 pri1_pos) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef CLIP
|
#undef CLIP
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
#ifdef PPU_CPP
|
auto PPU::update_sprite_list(uint addr, uint8 data) -> void {
|
||||||
|
|
||||||
void PPU::update_sprite_list(unsigned addr, uint8 data) {
|
|
||||||
if(addr < 0x0200) {
|
if(addr < 0x0200) {
|
||||||
unsigned i = addr >> 2;
|
unsigned i = addr >> 2;
|
||||||
switch(addr & 3) {
|
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;
|
if(sprite_list_valid == true) return;
|
||||||
sprite_list_valid = true;
|
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,
|
//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.
|
//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];
|
sprite_item* spr = &sprite_list[active_sprite];
|
||||||
|
@ -77,7 +75,7 @@ bool PPU::is_sprite_on_scanline() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::load_oam_tiles() {
|
auto PPU::load_oam_tiles() -> void {
|
||||||
sprite_item* spr = &sprite_list[active_sprite];
|
sprite_item* spr = &sprite_list[active_sprite];
|
||||||
uint16 tile_width = spr->width >> 3;
|
uint16 tile_width = spr->width >> 3;
|
||||||
int x = spr->x;
|
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];
|
oam_tileitem* t = &oam_tilelist[tile_num];
|
||||||
uint8* oam_td = (uint8*)bg_tiledata[COLORDEPTH_16];
|
uint8* oam_td = (uint8*)bg_tiledata[COLORDEPTH_16];
|
||||||
uint8* oam_td_state = (uint8*)bg_tiledata_state[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();
|
build_sprite_list();
|
||||||
|
|
||||||
regs.oam_itemcount = 0;
|
regs.oam_itemcount = 0;
|
||||||
|
@ -200,7 +198,7 @@ void PPU::render_line_oam_rto() {
|
||||||
pixel_cache[x].ce_sub = (oam_line_pal[x] < 192); \
|
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][0] == false) pri0_pos = 0;
|
||||||
if(layer_enabled[OAM][1] == false) pri1_pos = 0;
|
if(layer_enabled[OAM][1] == false) pri1_pos = 0;
|
||||||
if(layer_enabled[OAM][2] == false) pri2_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_main
|
||||||
#undef setpixel_sub
|
#undef setpixel_sub
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#ifdef PPU_CPP
|
|
||||||
|
|
||||||
#include "cache.cpp"
|
#include "cache.cpp"
|
||||||
#include "windows.cpp"
|
#include "windows.cpp"
|
||||||
#include "bg.cpp"
|
#include "bg.cpp"
|
||||||
|
@ -11,7 +9,7 @@
|
||||||
//Mode 0: ->
|
//Mode 0: ->
|
||||||
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
|
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
|
||||||
// BG4B, BG3B, OAM0, BG4A, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3
|
// 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, BG1, COLORDEPTH_4>(8, 11);
|
||||||
render_line_bg<0, BG2, COLORDEPTH_4>(7, 10);
|
render_line_bg<0, BG2, COLORDEPTH_4>(7, 10);
|
||||||
render_line_bg<0, BG3, COLORDEPTH_4>(2, 5);
|
render_line_bg<0, BG3, COLORDEPTH_4>(2, 5);
|
||||||
|
@ -26,7 +24,7 @@ void PPU::render_line_mode0() {
|
||||||
//Mode 1 (pri=0): ->
|
//Mode 1 (pri=0): ->
|
||||||
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
|
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
|
||||||
// BG3B, OAM0, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3
|
// 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) {
|
if(regs.bg3_priority) {
|
||||||
render_line_bg<1, BG1, COLORDEPTH_16>(5, 8);
|
render_line_bg<1, BG1, COLORDEPTH_16>(5, 8);
|
||||||
render_line_bg<1, BG2, COLORDEPTH_16>(4, 7);
|
render_line_bg<1, BG2, COLORDEPTH_16>(4, 7);
|
||||||
|
@ -43,7 +41,7 @@ void PPU::render_line_mode1() {
|
||||||
//Mode 2: ->
|
//Mode 2: ->
|
||||||
// 1, 2, 3, 4, 5, 6, 7, 8
|
// 1, 2, 3, 4, 5, 6, 7, 8
|
||||||
// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
// 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, BG1, COLORDEPTH_16>(3, 7);
|
||||||
render_line_bg<2, BG2, COLORDEPTH_16>(1, 5);
|
render_line_bg<2, BG2, COLORDEPTH_16>(1, 5);
|
||||||
render_line_oam(2, 4, 6, 8);
|
render_line_oam(2, 4, 6, 8);
|
||||||
|
@ -52,7 +50,7 @@ void PPU::render_line_mode2() {
|
||||||
//Mode 3: ->
|
//Mode 3: ->
|
||||||
// 1, 2, 3, 4, 5, 6, 7, 8
|
// 1, 2, 3, 4, 5, 6, 7, 8
|
||||||
// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
// 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, BG1, COLORDEPTH_256>(3, 7);
|
||||||
render_line_bg<3, BG2, COLORDEPTH_16 >(1, 5);
|
render_line_bg<3, BG2, COLORDEPTH_16 >(1, 5);
|
||||||
render_line_oam(2, 4, 6, 8);
|
render_line_oam(2, 4, 6, 8);
|
||||||
|
@ -61,7 +59,7 @@ void PPU::render_line_mode3() {
|
||||||
//Mode 4: ->
|
//Mode 4: ->
|
||||||
// 1, 2, 3, 4, 5, 6, 7, 8
|
// 1, 2, 3, 4, 5, 6, 7, 8
|
||||||
// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
// 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, BG1, COLORDEPTH_256>(3, 7);
|
||||||
render_line_bg<4, BG2, COLORDEPTH_4 >(1, 5);
|
render_line_bg<4, BG2, COLORDEPTH_4 >(1, 5);
|
||||||
render_line_oam(2, 4, 6, 8);
|
render_line_oam(2, 4, 6, 8);
|
||||||
|
@ -70,7 +68,7 @@ void PPU::render_line_mode4() {
|
||||||
//Mode 5: ->
|
//Mode 5: ->
|
||||||
// 1, 2, 3, 4, 5, 6, 7, 8
|
// 1, 2, 3, 4, 5, 6, 7, 8
|
||||||
// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
// 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, BG1, COLORDEPTH_16>(3, 7);
|
||||||
render_line_bg<5, BG2, COLORDEPTH_4 >(1, 5);
|
render_line_bg<5, BG2, COLORDEPTH_4 >(1, 5);
|
||||||
render_line_oam(2, 4, 6, 8);
|
render_line_oam(2, 4, 6, 8);
|
||||||
|
@ -79,7 +77,7 @@ void PPU::render_line_mode5() {
|
||||||
//Mode 6: ->
|
//Mode 6: ->
|
||||||
// 1, 2, 3, 4, 5, 6
|
// 1, 2, 3, 4, 5, 6
|
||||||
// OAM0, BG1B, OAM1, OAM2, BG1A, OAM3
|
// 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_bg<6, BG1, COLORDEPTH_16>(2, 5);
|
||||||
render_line_oam(1, 3, 4, 6);
|
render_line_oam(1, 3, 4, 6);
|
||||||
}
|
}
|
||||||
|
@ -91,7 +89,7 @@ void PPU::render_line_mode6() {
|
||||||
//Mode 7 EXTBG: ->
|
//Mode 7 EXTBG: ->
|
||||||
// 1, 2, 3, 4, 5, 6, 7
|
// 1, 2, 3, 4, 5, 6, 7
|
||||||
// BG2B, OAM0, BG1n, OAM1, BG2A, OAM2, OAM3
|
// BG2B, OAM0, BG1n, OAM1, BG2A, OAM2, OAM3
|
||||||
void PPU::render_line_mode7() {
|
auto PPU::render_line_mode7() -> void {
|
||||||
if(regs.mode7_extbg == false) {
|
if(regs.mode7_extbg == false) {
|
||||||
render_line_mode7<BG1>(2, 2);
|
render_line_mode7<BG1>(2, 2);
|
||||||
render_line_oam(1, 3, 4, 5);
|
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) {
|
if(regs.display_disabled == true) {
|
||||||
render_line_clear();
|
render_line_clear();
|
||||||
return;
|
return;
|
||||||
|
@ -125,5 +123,3 @@ void PPU::render_line() {
|
||||||
|
|
||||||
render_line_output();
|
render_line_output();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
//render.cpp
|
//render.cpp
|
||||||
inline void render_line_mode0();
|
inline auto render_line_mode0() -> void;
|
||||||
inline void render_line_mode1();
|
inline auto render_line_mode1() -> void;
|
||||||
inline void render_line_mode2();
|
inline auto render_line_mode2() -> void;
|
||||||
inline void render_line_mode3();
|
inline auto render_line_mode3() -> void;
|
||||||
inline void render_line_mode4();
|
inline auto render_line_mode4() -> void;
|
||||||
inline void render_line_mode5();
|
inline auto render_line_mode5() -> void;
|
||||||
inline void render_line_mode6();
|
inline auto render_line_mode6() -> void;
|
||||||
inline void render_line_mode7();
|
inline auto render_line_mode7() -> void;
|
||||||
|
|
||||||
//cache.cpp
|
//cache.cpp
|
||||||
enum { COLORDEPTH_4 = 0, COLORDEPTH_16 = 1, COLORDEPTH_256 = 2 };
|
enum : uint { COLORDEPTH_4 = 0, COLORDEPTH_16 = 1, COLORDEPTH_256 = 2 };
|
||||||
enum { TILE_2BIT = 0, TILE_4BIT = 1, TILE_8BIT = 2 };
|
enum : uint { TILE_2BIT = 0, TILE_4BIT = 1, TILE_8BIT = 2 };
|
||||||
|
|
||||||
struct pixel_t {
|
struct pixel_t {
|
||||||
//bgr555 color data for main/subscreen pixels: 0x0000 = transparent / use palette color # 0
|
//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[3];
|
||||||
uint8* bg_tiledata_state[3]; //0 = valid, 1 = dirty
|
uint8* bg_tiledata_state[3]; //0 = valid, 1 = dirty
|
||||||
|
|
||||||
template<unsigned color_depth> void render_bg_tile(uint16 tile_num);
|
template<uint color_depth> auto render_bg_tile(uint16 tile_num) -> void;
|
||||||
inline void flush_pixel_cache();
|
inline auto flush_pixel_cache() -> void;
|
||||||
void alloc_tiledata_cache();
|
auto alloc_tiledata_cache() -> void;
|
||||||
void flush_tiledata_cache();
|
auto flush_tiledata_cache() -> void;
|
||||||
void free_tiledata_cache();
|
auto free_tiledata_cache() -> void;
|
||||||
|
|
||||||
//windows.cpp
|
//windows.cpp
|
||||||
struct window_t {
|
struct window_t {
|
||||||
uint8 main[256], sub[256];
|
uint8 main[256], sub[256];
|
||||||
} window[6];
|
} window[6];
|
||||||
|
|
||||||
void build_window_table(uint8 bg, bool mainscreen);
|
auto build_window_table(uint8 bg, bool mainscreen) -> void;
|
||||||
void build_window_tables(uint8 bg);
|
auto build_window_tables(uint8 bg) -> void;
|
||||||
|
|
||||||
//bg.cpp
|
//bg.cpp
|
||||||
struct {
|
struct {
|
||||||
|
@ -48,10 +48,10 @@ struct {
|
||||||
uint16 mx, my; //screen mask x, y
|
uint16 mx, my; //screen mask x, y
|
||||||
uint16 scx, scy; //sc index offsets
|
uint16 scx, scy; //sc index offsets
|
||||||
} bg_info[4];
|
} bg_info[4];
|
||||||
void update_bg_info();
|
auto update_bg_info() -> void;
|
||||||
|
|
||||||
template<unsigned bg> uint16 bg_get_tile(uint16 x, uint16 y);
|
template<uint bg> auto bg_get_tile(uint16 x, uint16 y) -> uint16;
|
||||||
template<unsigned mode, unsigned bg, unsigned color_depth> void render_line_bg(uint8 pri0_pos, uint8 pri1_pos);
|
template<uint mode, uint bg, uint color_depth> auto render_line_bg(uint8 pri0_pos, uint8 pri1_pos) -> void;
|
||||||
|
|
||||||
//oam.cpp
|
//oam.cpp
|
||||||
struct sprite_item {
|
struct sprite_item {
|
||||||
|
@ -65,7 +65,7 @@ struct sprite_item {
|
||||||
bool size;
|
bool size;
|
||||||
} sprite_list[128];
|
} sprite_list[128];
|
||||||
bool sprite_list_valid;
|
bool sprite_list_valid;
|
||||||
unsigned active_sprite;
|
uint active_sprite;
|
||||||
|
|
||||||
uint8 oam_itemlist[32];
|
uint8 oam_itemlist[32];
|
||||||
struct oam_tileitem {
|
struct oam_tileitem {
|
||||||
|
@ -73,27 +73,27 @@ struct oam_tileitem {
|
||||||
bool hflip;
|
bool hflip;
|
||||||
} oam_tilelist[34];
|
} oam_tilelist[34];
|
||||||
|
|
||||||
enum { OAM_PRI_NONE = 4 };
|
enum : uint { OAM_PRI_NONE = 4 };
|
||||||
uint8 oam_line_pal[256], oam_line_pri[256];
|
uint8 oam_line_pal[256], oam_line_pri[256];
|
||||||
|
|
||||||
void update_sprite_list(unsigned addr, uint8 data);
|
auto update_sprite_list(unsigned addr, uint8 data) -> void;
|
||||||
void build_sprite_list();
|
auto build_sprite_list() -> void;
|
||||||
bool is_sprite_on_scanline();
|
auto is_sprite_on_scanline() -> bool;
|
||||||
void load_oam_tiles();
|
auto load_oam_tiles() -> void;
|
||||||
void render_oam_tile(int tile_num);
|
auto render_oam_tile(int tile_num) -> void;
|
||||||
void render_line_oam_rto();
|
auto render_line_oam_rto() -> void;
|
||||||
void render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos);
|
auto render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) -> void;
|
||||||
|
|
||||||
//mode7.cpp
|
//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
|
//addsub.cpp
|
||||||
inline uint16 addsub(uint32 x, uint32 y, bool halve);
|
inline auto addsub(uint32 x, uint32 y, bool halve) -> uint16;
|
||||||
|
|
||||||
//line.cpp
|
//line.cpp
|
||||||
inline uint16 get_palette(uint8 index);
|
inline auto get_palette(uint8 index) -> uint16;
|
||||||
inline uint16 get_direct_color(uint8 p, uint8 t);
|
inline auto get_direct_color(uint8 p, uint8 t) -> uint16;
|
||||||
inline uint16 get_pixel_normal(uint32 x);
|
inline auto get_pixel_normal(uint32 x) -> uint16;
|
||||||
inline uint16 get_pixel_swap(uint32 x);
|
inline auto get_pixel_swap(uint32 x) -> uint16;
|
||||||
void render_line_output();
|
auto render_line_output() -> void;
|
||||||
void render_line_clear();
|
auto render_line_clear() -> void;
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#ifdef PPU_CPP
|
|
||||||
|
|
||||||
//screen: 0 = main, 1 = sub
|
//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;
|
bool set = 1, clr = 0;
|
||||||
uint8* table = (screen == 0 ? window[bg].main : window[bg].sub);
|
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, 0);
|
||||||
build_window_table(bg, 1);
|
build_window_table(bg, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
#ifdef PPU_CPP
|
auto PPUcounter::serialize(serializer& s) -> void {
|
||||||
|
|
||||||
void PPUcounter::serialize(serializer& s) {
|
|
||||||
s.integer(status.interlace);
|
s.integer(status.interlace);
|
||||||
s.integer(status.field);
|
s.integer(status.field);
|
||||||
s.integer(status.vcounter);
|
s.integer(status.vcounter);
|
||||||
|
@ -12,7 +10,7 @@ void PPUcounter::serialize(serializer& s) {
|
||||||
s.integer(history.index);
|
s.integer(history.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::serialize(serializer& s) {
|
auto PPU::serialize(serializer& s) -> void {
|
||||||
Thread::serialize(s);
|
Thread::serialize(s);
|
||||||
PPUcounter::serialize(s);
|
PPUcounter::serialize(s);
|
||||||
|
|
||||||
|
@ -35,7 +33,7 @@ void PPU::serialize(serializer& s) {
|
||||||
|
|
||||||
s.integer(regs.ppu1_mdr);
|
s.integer(regs.ppu1_mdr);
|
||||||
s.integer(regs.ppu2_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.ioamaddr);
|
||||||
s.integer(regs.icgramaddr);
|
s.integer(regs.icgramaddr);
|
||||||
|
@ -54,24 +52,24 @@ void PPU::serialize(serializer& s) {
|
||||||
|
|
||||||
s.integer(regs.oam_latchdata);
|
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.bg3_priority);
|
||||||
s.integer(regs.bg_mode);
|
s.integer(regs.bg_mode);
|
||||||
|
|
||||||
s.integer(regs.mosaic_size);
|
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);
|
s.integer(regs.mosaic_countdown);
|
||||||
|
|
||||||
for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_scaddr[n]);
|
for(uint 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_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.bg_ofslatch);
|
||||||
s.integer(regs.m7_hofs);
|
s.integer(regs.m7_hofs);
|
||||||
s.integer(regs.m7_vofs);
|
s.integer(regs.m7_vofs);
|
||||||
for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_hofs[n]);
|
for(uint 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_vofs[n]);
|
||||||
|
|
||||||
s.integer(regs.vram_incmode);
|
s.integer(regs.vram_incmode);
|
||||||
s.integer(regs.vram_mapping);
|
s.integer(regs.vram_mapping);
|
||||||
|
@ -95,21 +93,21 @@ void PPU::serialize(serializer& s) {
|
||||||
|
|
||||||
s.integer(regs.cgram_latchdata);
|
s.integer(regs.cgram_latchdata);
|
||||||
|
|
||||||
for(unsigned n = 0; n < 6; n++) s.integer(regs.window1_enabled[n]);
|
for(uint 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(uint 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(uint 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.window2_invert [n]);
|
||||||
|
|
||||||
s.integer(regs.window1_left);
|
s.integer(regs.window1_left);
|
||||||
s.integer(regs.window1_right);
|
s.integer(regs.window1_right);
|
||||||
s.integer(regs.window2_left);
|
s.integer(regs.window2_left);
|
||||||
s.integer(regs.window2_right);
|
s.integer(regs.window2_right);
|
||||||
|
|
||||||
for(unsigned n = 0; n < 6; n++) s.integer(regs.window_mask[n]);
|
for(uint 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(uint 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(uint 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(uint 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 < 5; n++) s.integer(regs.sub_window_enabled[n]);
|
||||||
|
|
||||||
s.integer(regs.color_mask);
|
s.integer(regs.color_mask);
|
||||||
s.integer(regs.colorsub_mask);
|
s.integer(regs.colorsub_mask);
|
||||||
|
@ -118,7 +116,7 @@ void PPU::serialize(serializer& s) {
|
||||||
|
|
||||||
s.integer(regs.color_mode);
|
s.integer(regs.color_mode);
|
||||||
s.integer(regs.color_halve);
|
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_r);
|
||||||
s.integer(regs.color_g);
|
s.integer(regs.color_g);
|
||||||
|
@ -145,7 +143,7 @@ void PPU::serialize(serializer& s) {
|
||||||
s.integer(regs.oam_itemcount);
|
s.integer(regs.oam_itemcount);
|
||||||
s.integer(regs.oam_tilecount);
|
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_main);
|
||||||
s.integer(pixel_cache[n].src_sub);
|
s.integer(pixel_cache[n].src_sub);
|
||||||
s.integer(pixel_cache[n].bg_main);
|
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][] ...
|
//better to just take a small speed hit than store all of bg_tiledata[3][] ...
|
||||||
flush_tiledata_cache();
|
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].main, 256);
|
||||||
s.array(window[n].sub, 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].tw);
|
||||||
s.integer(bg_info[n].th);
|
s.integer(bg_info[n].th);
|
||||||
s.integer(bg_info[n].mx);
|
s.integer(bg_info[n].mx);
|
||||||
|
@ -173,7 +171,7 @@ void PPU::serialize(serializer& s) {
|
||||||
s.integer(bg_info[n].scy);
|
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].width);
|
||||||
s.integer(sprite_list[n].height);
|
s.integer(sprite_list[n].height);
|
||||||
s.integer(sprite_list[n].x);
|
s.integer(sprite_list[n].x);
|
||||||
|
@ -191,7 +189,7 @@ void PPU::serialize(serializer& s) {
|
||||||
|
|
||||||
s.array(oam_itemlist, 32);
|
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].x);
|
||||||
s.integer(oam_tilelist[n].y);
|
s.integer(oam_tilelist[n].y);
|
||||||
s.integer(oam_tilelist[n].pri);
|
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_pal, 256);
|
||||||
s.array(oam_line_pri, 256);
|
s.array(oam_line_pri, 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue