mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-16 21:12:26 +02:00
Compare commits
44 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e846c83d47 | ||
|
06d44b4878 | ||
|
25eaaa82f4 | ||
|
2d83300235 | ||
|
680d16561e | ||
|
379ab6991f | ||
|
d3413db04a | ||
|
a7f7985581 | ||
|
b586471562 | ||
|
c33065fbd1 | ||
|
79e7e6ab9e | ||
|
3d3ac8c1db | ||
|
b0d2f5033e | ||
|
570eb9c5f5 | ||
|
7dc62e3a69 | ||
|
fc7d5991ce | ||
|
29be18ce0c | ||
|
810cbdafb4 | ||
|
4b29f4bad7 | ||
|
ef65bb862a | ||
|
0d0af39b44 | ||
|
6c83329cae | ||
|
32a95a9761 | ||
|
a89a3da77a | ||
|
7a748e093e | ||
|
d158c8f293 | ||
|
71bda4144a | ||
|
ad51f1478e | ||
|
d0ddd87e9c | ||
|
605a8aa3e9 | ||
|
a8323d0d2b | ||
|
d7998b23ef | ||
|
344e63d928 | ||
|
f1ebef2ea8 | ||
|
1fdd0582fc | ||
|
12df278c5b | ||
|
cec33c1d0f | ||
|
3414c8c8df | ||
|
82ec876302 | ||
|
72b6a8b32e | ||
|
653bb378ee | ||
|
0b923489dd | ||
|
4d193d7d94 | ||
|
47d4bd4d81 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,2 +1,2 @@
|
||||
ananke/libananke.so
|
||||
icarus/icarus
|
||||
higan/profile/WonderSwan.sys/internal.ram
|
||||
higan/profile/WonderSwan Color.sys/internal.ram
|
||||
|
@@ -1,16 +0,0 @@
|
||||
fc_objects := fc-interface fc-system fc-scheduler fc-input
|
||||
fc_objects += fc-memory fc-cartridge fc-cpu fc-apu fc-ppu
|
||||
fc_objects += fc-cheat fc-video
|
||||
objects += $(fc_objects)
|
||||
|
||||
obj/fc-interface.o: $(fc)/interface/interface.cpp $(call rwildcard,$(fc)/interface/)
|
||||
obj/fc-system.o: $(fc)/system/system.cpp $(call rwildcard,$(fc)/system/)
|
||||
obj/fc-scheduler.o: $(fc)/scheduler/scheduler.cpp $(call rwildcard,$(fc)/scheduler/)
|
||||
obj/fc-input.o: $(fc)/input/input.cpp $(call rwildcard,$(fc)/input/)
|
||||
obj/fc-memory.o: $(fc)/memory/memory.cpp $(call rwildcard,$(fc)/memory/)
|
||||
obj/fc-cartridge.o: $(fc)/cartridge/cartridge.cpp $(call rwildcard,$(fc)/cartridge/)
|
||||
obj/fc-cpu.o: $(fc)/cpu/cpu.cpp $(call rwildcard,$(fc)/cpu/)
|
||||
obj/fc-apu.o: $(fc)/apu/apu.cpp $(call rwildcard,$(fc)/apu/)
|
||||
obj/fc-ppu.o: $(fc)/ppu/ppu.cpp $(call rwildcard,$(fc)/ppu/)
|
||||
obj/fc-cheat.o: $(fc)/cheat/cheat.cpp $(call rwildcard,$(fc)/cheat/)
|
||||
obj/fc-video.o: $(fc)/video/video.cpp $(call rwildcard,$(fc)/video/)
|
@@ -1,28 +0,0 @@
|
||||
#include <fc/fc.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
auto Scheduler::enter() -> void {
|
||||
host_thread = co_active();
|
||||
co_switch(thread);
|
||||
}
|
||||
|
||||
auto Scheduler::exit(ExitReason reason) -> void {
|
||||
exit_reason = reason;
|
||||
thread = co_active();
|
||||
co_switch(host_thread);
|
||||
}
|
||||
|
||||
auto Scheduler::power() -> void {
|
||||
}
|
||||
|
||||
auto Scheduler::reset() -> void {
|
||||
host_thread = co_active();
|
||||
thread = cpu.thread;
|
||||
sync = SynchronizeMode::None;
|
||||
exit_reason = ExitReason::UnknownEvent;
|
||||
}
|
||||
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
struct Scheduler : property<Scheduler> {
|
||||
enum class SynchronizeMode : uint { None, PPU, All } sync;
|
||||
enum class ExitReason : uint { UnknownEvent, FrameEvent, SynchronizeEvent };
|
||||
|
||||
auto enter() -> void;
|
||||
auto exit(ExitReason) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
cothread_t host_thread; //program thread (used to exit emulation)
|
||||
cothread_t thread; //active emulation thread (used to enter emulation)
|
||||
readonly<ExitReason> exit_reason;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
@@ -1,79 +0,0 @@
|
||||
#include <fc/fc.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
|
||||
#include "serialization.cpp"
|
||||
System system;
|
||||
|
||||
auto System::run() -> void {
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
|
||||
interface->videoRefresh(video.palette, ppu.buffer, 4 * 256, 256, 240);
|
||||
}
|
||||
}
|
||||
|
||||
auto System::runtosave() -> void {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::PPU;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.thread = cpu.thread;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.thread = apu.thread;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.thread = cartridge.thread;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::None;
|
||||
}
|
||||
|
||||
auto System::runthreadtosave() -> void {
|
||||
while(true) {
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
|
||||
interface->videoRefresh(video.palette, ppu.buffer, 4 * 256, 256, 240);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto System::load() -> void {
|
||||
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
|
||||
serialize_init();
|
||||
}
|
||||
|
||||
auto System::power() -> void {
|
||||
cartridge.power();
|
||||
cpu.power();
|
||||
apu.power();
|
||||
ppu.power();
|
||||
input.reset();
|
||||
scheduler.power();
|
||||
reset();
|
||||
}
|
||||
|
||||
auto System::reset() -> void {
|
||||
cartridge.reset();
|
||||
cpu.reset();
|
||||
apu.reset();
|
||||
ppu.reset();
|
||||
input.reset();
|
||||
scheduler.reset();
|
||||
}
|
||||
|
||||
auto System::init() -> void {
|
||||
assert(interface != 0);
|
||||
input.connect(0, Input::Device::Joypad);
|
||||
input.connect(1, Input::Device::None);
|
||||
}
|
||||
|
||||
auto System::term() -> void {
|
||||
}
|
||||
|
||||
}
|
@@ -1,85 +0,0 @@
|
||||
#include <fc/fc.hpp>
|
||||
#include <cmath>
|
||||
|
||||
#define VIDEO_CPP
|
||||
namespace Famicom {
|
||||
|
||||
Video video;
|
||||
|
||||
Video::Video() {
|
||||
palette = new uint32_t[1 << 9]();
|
||||
}
|
||||
|
||||
Video::~Video() {
|
||||
delete[] palette;
|
||||
}
|
||||
|
||||
auto Video::generate_palette(Emulator::Interface::PaletteMode mode) -> void {
|
||||
for(auto color : range(1 << 9)) {
|
||||
if(mode == Emulator::Interface::PaletteMode::Literal) {
|
||||
palette[color] = color;
|
||||
} else if(mode == Emulator::Interface::PaletteMode::Channel) {
|
||||
uint emphasis = (color >> 6) & 7;
|
||||
uint luma = (color >> 4) & 3;
|
||||
uint chroma = (color >> 0) & 15;
|
||||
emphasis = image::normalize(emphasis, 3, 16);
|
||||
luma = image::normalize(luma, 2, 16);
|
||||
chroma = image::normalize(chroma, 4, 16);
|
||||
palette[color] = interface->videoColor(color, 0, emphasis, luma, chroma);
|
||||
} else if(mode == Emulator::Interface::PaletteMode::Standard) {
|
||||
palette[color] = generate_color(color, 2.0, 0.0, 1.0, 1.0, 2.2);
|
||||
} else if(mode == Emulator::Interface::PaletteMode::Emulation) {
|
||||
palette[color] = generate_color(color, 2.0, 0.0, 1.0, 1.0, 1.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Video::generate_color(
|
||||
uint n, double saturation, double hue,
|
||||
double contrast, double brightness, double gamma
|
||||
) -> uint32 {
|
||||
int color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1;
|
||||
|
||||
static const double black = 0.518, white = 1.962, attenuation = 0.746;
|
||||
static const double levels[8] = {
|
||||
0.350, 0.518, 0.962, 1.550,
|
||||
1.094, 1.506, 1.962, 1.962,
|
||||
};
|
||||
|
||||
double lo_and_hi[2] = {
|
||||
levels[level + 4 * (color == 0x0)],
|
||||
levels[level + 4 * (color < 0xd)],
|
||||
};
|
||||
|
||||
double y = 0.0, i = 0.0, q = 0.0;
|
||||
auto wave = [](int p, int color) { return (color + p + 8) % 12 < 6; };
|
||||
for(int p : range(12)) {
|
||||
double spot = lo_and_hi[wave(p, color)];
|
||||
|
||||
if(((n & 0x040) && wave(p, 12))
|
||||
|| ((n & 0x080) && wave(p, 4))
|
||||
|| ((n & 0x100) && wave(p, 8))
|
||||
) spot *= attenuation;
|
||||
|
||||
double v = (spot - black) / (white - black);
|
||||
|
||||
v = (v - 0.5) * contrast + 0.5;
|
||||
v *= brightness / 12.0;
|
||||
|
||||
y += v;
|
||||
i += v * std::cos((3.141592653 / 6.0) * (p + hue));
|
||||
q += v * std::sin((3.141592653 / 6.0) * (p + hue));
|
||||
}
|
||||
|
||||
i *= saturation;
|
||||
q *= saturation;
|
||||
|
||||
auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : std::pow(f, 2.2 / gamma); };
|
||||
uint r = 65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q);
|
||||
uint g = 65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q);
|
||||
uint b = 65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q);
|
||||
|
||||
return interface->videoColor(n, 0, uclamp<16>(r), uclamp<16>(g), uclamp<16>(b));
|
||||
}
|
||||
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
struct Video {
|
||||
Video();
|
||||
~Video();
|
||||
|
||||
auto generate_palette(Emulator::Interface::PaletteMode mode) -> void;
|
||||
|
||||
uint32* palette = nullptr;
|
||||
|
||||
private:
|
||||
auto generate_color(uint, double, double, double, double, double) -> uint32;
|
||||
};
|
||||
|
||||
extern Video video;
|
@@ -1,16 +0,0 @@
|
||||
gb_objects := gb-interface gb-system gb-scheduler
|
||||
gb_objects += gb-memory gb-cartridge
|
||||
gb_objects += gb-cpu gb-ppu gb-apu
|
||||
gb_objects += gb-cheat gb-video
|
||||
objects += $(gb_objects)
|
||||
|
||||
obj/gb-interface.o: $(gb)/interface/interface.cpp $(call rwildcard,$(gb)/interface/)
|
||||
obj/gb-system.o: $(gb)/system/system.cpp $(call rwildcard,$(gb)/system/)
|
||||
obj/gb-scheduler.o: $(gb)/scheduler/scheduler.cpp $(call rwildcard,$(gb)/scheduler/)
|
||||
obj/gb-cartridge.o: $(gb)/cartridge/cartridge.cpp $(call rwildcard,$(gb)/cartridge/)
|
||||
obj/gb-memory.o: $(gb)/memory/memory.cpp $(call rwildcard,$(gb)/memory/)
|
||||
obj/gb-cpu.o: $(gb)/cpu/cpu.cpp $(call rwildcard,$(gb)/cpu/)
|
||||
obj/gb-ppu.o: $(gb)/ppu/ppu.cpp $(call rwildcard,$(gb)/ppu/)
|
||||
obj/gb-apu.o: $(gb)/apu/apu.cpp $(call rwildcard,$(gb)/apu/)
|
||||
obj/gb-cheat.o: $(gb)/cheat/cheat.cpp $(call rwildcard,$(gb)/cheat/)
|
||||
obj/gb-video.o: $(gb)/video/video.cpp $(call rwildcard,$(gb)/video/)
|
115
gb/apu/apu.cpp
115
gb/apu/apu.cpp
@@ -1,115 +0,0 @@
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
|
||||
#include "square1/square1.cpp"
|
||||
#include "square2/square2.cpp"
|
||||
#include "wave/wave.cpp"
|
||||
#include "noise/noise.cpp"
|
||||
#include "master/master.cpp"
|
||||
#include "serialization.cpp"
|
||||
APU apu;
|
||||
|
||||
auto APU::Main() -> void {
|
||||
apu.main();
|
||||
}
|
||||
|
||||
auto APU::main() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(sequencer_base == 0) { //512hz
|
||||
if(sequencer_step == 0 || sequencer_step == 2 || sequencer_step == 4 || sequencer_step == 6) { //256hz
|
||||
square1.clock_length();
|
||||
square2.clock_length();
|
||||
wave.clock_length();
|
||||
noise.clock_length();
|
||||
}
|
||||
if(sequencer_step == 2 || sequencer_step == 6) { //128hz
|
||||
square1.clock_sweep();
|
||||
}
|
||||
if(sequencer_step == 7) { //64hz
|
||||
square1.clock_envelope();
|
||||
square2.clock_envelope();
|
||||
noise.clock_envelope();
|
||||
}
|
||||
sequencer_step++;
|
||||
}
|
||||
sequencer_base++;
|
||||
|
||||
square1.run();
|
||||
square2.run();
|
||||
wave.run();
|
||||
noise.run();
|
||||
master.run();
|
||||
|
||||
hipass(master.center, master.center_bias);
|
||||
hipass(master.left, master.left_bias);
|
||||
hipass(master.right, master.right_bias);
|
||||
|
||||
interface->audioSample(master.left, master.right);
|
||||
|
||||
clock += cpu.frequency;
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(scheduler.active_thread = cpu.thread);
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::hipass(int16& sample, int64& bias) -> void {
|
||||
bias += ((((int64)sample << 16) - (bias >> 16)) * 57593) >> 16;
|
||||
sample = sclamp<16>(sample - (bias >> 32));
|
||||
}
|
||||
|
||||
auto APU::power() -> void {
|
||||
create(Main, 2 * 1024 * 1024);
|
||||
for(uint n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
|
||||
|
||||
for(auto& n : mmio_data) n = 0x00;
|
||||
sequencer_base = 0;
|
||||
sequencer_step = 0;
|
||||
|
||||
square1.power();
|
||||
square2.power();
|
||||
wave.power();
|
||||
noise.power();
|
||||
master.power();
|
||||
}
|
||||
|
||||
auto APU::mmio_read(uint16 addr) -> uint8 {
|
||||
static const uint8 table[48] = {
|
||||
0x80, 0x3f, 0x00, 0xff, 0xbf, //square1
|
||||
0xff, 0x3f, 0x00, 0xff, 0xbf, //square2
|
||||
0x7f, 0xff, 0x9f, 0xff, 0xbf, //wave
|
||||
0xff, 0xff, 0x00, 0x00, 0xbf, //noise
|
||||
0x00, 0x00, 0x70, //master
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //unmapped
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
|
||||
};
|
||||
|
||||
if(addr == 0xff26) {
|
||||
uint8 data = master.enable << 7;
|
||||
if(square1.enable) data |= 0x01;
|
||||
if(square2.enable) data |= 0x02;
|
||||
if( wave.enable) data |= 0x04;
|
||||
if( noise.enable) data |= 0x08;
|
||||
return data | table[addr - 0xff10];
|
||||
}
|
||||
|
||||
if(addr >= 0xff10 && addr <= 0xff3f) return mmio_data[addr - 0xff10] | table[addr - 0xff10];
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto APU::mmio_write(uint16 addr, uint8 data) -> void {
|
||||
if(addr >= 0xff10 && addr <= 0xff3f) mmio_data[addr - 0xff10] = data;
|
||||
|
||||
if(addr >= 0xff10 && addr <= 0xff14) return square1.write (addr - 0xff10, data);
|
||||
if(addr >= 0xff15 && addr <= 0xff19) return square2.write (addr - 0xff15, data);
|
||||
if(addr >= 0xff1a && addr <= 0xff1e) return wave.write (addr - 0xff1a, data);
|
||||
if(addr >= 0xff1f && addr <= 0xff23) return noise.write (addr - 0xff1f, data);
|
||||
if(addr >= 0xff24 && addr <= 0xff26) return master.write (addr - 0xff24, data);
|
||||
if(addr >= 0xff30 && addr <= 0xff3f) return wave.write_pattern(addr - 0xff30, data);
|
||||
}
|
||||
|
||||
}
|
@@ -1,112 +0,0 @@
|
||||
auto APU::Master::run() -> void {
|
||||
if(enable == false) {
|
||||
center = 0;
|
||||
left = 0;
|
||||
right = 0;
|
||||
|
||||
center_bias = left_bias = right_bias = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int sample = 0;
|
||||
sample += apu.square1.output;
|
||||
sample += apu.square2.output;
|
||||
sample += apu.wave.output;
|
||||
sample += apu.noise.output;
|
||||
center = (sample * 512) - 16384;
|
||||
|
||||
sample = 0;
|
||||
if(channel1_left_enable) sample += apu.square1.output;
|
||||
if(channel2_left_enable) sample += apu.square2.output;
|
||||
if(channel3_left_enable) sample += apu.wave.output;
|
||||
if(channel4_left_enable) sample += apu.noise.output;
|
||||
sample = (sample * 512) - 16384;
|
||||
sample = (sample * (left_volume + 1)) / 8;
|
||||
left = sample;
|
||||
|
||||
sample = 0;
|
||||
if(channel1_right_enable) sample += apu.square1.output;
|
||||
if(channel2_right_enable) sample += apu.square2.output;
|
||||
if(channel3_right_enable) sample += apu.wave.output;
|
||||
if(channel4_right_enable) sample += apu.noise.output;
|
||||
sample = (sample * 512) - 16384;
|
||||
sample = (sample * (right_volume + 1)) / 8;
|
||||
right = sample;
|
||||
|
||||
//reduce audio volume
|
||||
center >>= 1;
|
||||
left >>= 1;
|
||||
right >>= 1;
|
||||
}
|
||||
|
||||
auto APU::Master::write(uint r, uint8 data) -> void {
|
||||
if(r == 0) { //$ff24 NR50
|
||||
left_in_enable = data & 0x80;
|
||||
left_volume = (data >> 4) & 7;
|
||||
right_in_enable = data & 0x08;
|
||||
right_volume = (data >> 0) & 7;
|
||||
}
|
||||
|
||||
if(r == 1) { //$ff25 NR51
|
||||
channel4_left_enable = data & 0x80;
|
||||
channel3_left_enable = data & 0x40;
|
||||
channel2_left_enable = data & 0x20;
|
||||
channel1_left_enable = data & 0x10;
|
||||
channel4_right_enable = data & 0x08;
|
||||
channel3_right_enable = data & 0x04;
|
||||
channel2_right_enable = data & 0x02;
|
||||
channel1_right_enable = data & 0x01;
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff26 NR52
|
||||
enable = data & 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Master::power() -> void {
|
||||
left_in_enable = 0;
|
||||
left_volume = 0;
|
||||
right_in_enable = 0;
|
||||
right_volume = 0;
|
||||
channel4_left_enable = 0;
|
||||
channel3_left_enable = 0;
|
||||
channel2_left_enable = 0;
|
||||
channel1_left_enable = 0;
|
||||
channel4_right_enable = 0;
|
||||
channel3_right_enable = 0;
|
||||
channel2_right_enable = 0;
|
||||
channel1_right_enable = 0;
|
||||
enable = 0;
|
||||
|
||||
center = 0;
|
||||
left = 0;
|
||||
right = 0;
|
||||
|
||||
center_bias = 0;
|
||||
left_bias = 0;
|
||||
right_bias = 0;
|
||||
}
|
||||
|
||||
auto APU::Master::serialize(serializer& s) -> void {
|
||||
s.integer(left_in_enable);
|
||||
s.integer(left_volume);
|
||||
s.integer(right_in_enable);
|
||||
s.integer(right_volume);
|
||||
s.integer(channel4_left_enable);
|
||||
s.integer(channel3_left_enable);
|
||||
s.integer(channel2_left_enable);
|
||||
s.integer(channel1_left_enable);
|
||||
s.integer(channel4_right_enable);
|
||||
s.integer(channel3_right_enable);
|
||||
s.integer(channel2_right_enable);
|
||||
s.integer(channel1_right_enable);
|
||||
s.integer(enable);
|
||||
|
||||
s.integer(center);
|
||||
s.integer(left);
|
||||
s.integer(right);
|
||||
|
||||
s.integer(center_bias);
|
||||
s.integer(left_bias);
|
||||
s.integer(right_bias);
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
struct Master {
|
||||
auto run() -> void;
|
||||
auto write(uint r, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool left_in_enable;
|
||||
uint3 left_volume;
|
||||
bool right_in_enable;
|
||||
uint3 right_volume;
|
||||
bool channel4_left_enable;
|
||||
bool channel3_left_enable;
|
||||
bool channel2_left_enable;
|
||||
bool channel1_left_enable;
|
||||
bool channel4_right_enable;
|
||||
bool channel3_right_enable;
|
||||
bool channel2_right_enable;
|
||||
bool channel1_right_enable;
|
||||
bool enable;
|
||||
|
||||
int16 center;
|
||||
int16 left;
|
||||
int16 right;
|
||||
|
||||
int64 center_bias;
|
||||
int64 left_bias;
|
||||
int64 right_bias;
|
||||
};
|
@@ -1,103 +0,0 @@
|
||||
auto APU::Noise::dac_enable() const -> bool {
|
||||
return (envelope_volume || envelope_direction);
|
||||
}
|
||||
|
||||
auto APU::Noise::run() -> void {
|
||||
if(period && --period == 0) {
|
||||
period = divisor << frequency;
|
||||
if(frequency < 14) {
|
||||
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
|
||||
lfsr = (lfsr >> 1) ^ (bit << (narrow_lfsr ? 6 : 14));
|
||||
}
|
||||
}
|
||||
|
||||
uint4 sample = (lfsr & 1) ? (uint4)0 : volume;
|
||||
if(enable == false) sample = 0;
|
||||
|
||||
output = sample;
|
||||
}
|
||||
|
||||
auto APU::Noise::clock_length() -> void {
|
||||
if(enable && counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Noise::clock_envelope() -> void {
|
||||
if(enable && envelope_frequency && --envelope_period == 0) {
|
||||
envelope_period = envelope_frequency;
|
||||
if(envelope_direction == 0 && volume > 0) volume--;
|
||||
if(envelope_direction == 1 && volume < 15) volume++;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Noise::write(uint r, uint8 data) -> void {
|
||||
if(r == 1) { //$ff20 NR41
|
||||
length = data & 0x3f;
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff21 NR42
|
||||
envelope_volume = data >> 4;
|
||||
envelope_direction = data & 0x08;
|
||||
envelope_frequency = data & 0x07;
|
||||
if(dac_enable() == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 3) { //$ff22 NR43
|
||||
frequency = data >> 4;
|
||||
narrow_lfsr = data & 0x08;
|
||||
divisor = (data & 0x07) << 3;
|
||||
if(divisor == 0) divisor = 4;
|
||||
period = divisor << frequency;
|
||||
}
|
||||
|
||||
if(r == 4) { //$ff34 NR44
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
|
||||
if(initialize) {
|
||||
enable = dac_enable();
|
||||
lfsr = -1;
|
||||
envelope_period = envelope_frequency;
|
||||
volume = envelope_volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Noise::power() -> void {
|
||||
enable = 0;
|
||||
|
||||
envelope_volume = 0;
|
||||
envelope_direction = 0;
|
||||
envelope_frequency = 0;
|
||||
frequency = 0;
|
||||
narrow_lfsr = 0;
|
||||
divisor = 0;
|
||||
counter = 0;
|
||||
|
||||
output = 0;
|
||||
length = 0;
|
||||
envelope_period = 0;
|
||||
volume = 0;
|
||||
period = 0;
|
||||
lfsr = 0;
|
||||
}
|
||||
|
||||
auto APU::Noise::serialize(serializer& s) -> void {
|
||||
s.integer(enable);
|
||||
|
||||
s.integer(envelope_volume);
|
||||
s.integer(envelope_direction);
|
||||
s.integer(envelope_frequency);
|
||||
s.integer(frequency);
|
||||
s.integer(narrow_lfsr);
|
||||
s.integer(divisor);
|
||||
s.integer(counter);
|
||||
|
||||
s.integer(output);
|
||||
s.integer(length);
|
||||
s.integer(envelope_period);
|
||||
s.integer(volume);
|
||||
s.integer(period);
|
||||
s.integer(lfsr);
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
struct Noise {
|
||||
auto dac_enable() const -> bool;
|
||||
|
||||
auto run() -> void;
|
||||
auto clock_length() -> void;
|
||||
auto clock_envelope() -> void;
|
||||
auto write(uint r, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool enable;
|
||||
|
||||
uint4 envelope_volume;
|
||||
bool envelope_direction;
|
||||
uint3 envelope_frequency;
|
||||
uint4 frequency;
|
||||
bool narrow_lfsr;
|
||||
uint divisor;
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
uint6 length;
|
||||
uint3 envelope_period;
|
||||
uint4 volume;
|
||||
uint period;
|
||||
uint15 lfsr;
|
||||
};
|
@@ -1,154 +0,0 @@
|
||||
auto APU::Square1::dac_enable() const -> bool {
|
||||
return (envelope_volume || envelope_direction);
|
||||
}
|
||||
|
||||
auto APU::Square1::run() -> void {
|
||||
if(period && --period == 0) {
|
||||
period = 2 * (2048 - frequency);
|
||||
phase++;
|
||||
switch(duty) {
|
||||
case 0: duty_output = (phase == 6); break; //______-_
|
||||
case 1: duty_output = (phase >= 6); break; //______--
|
||||
case 2: duty_output = (phase >= 4); break; //____----
|
||||
case 3: duty_output = (phase <= 5); break; //------__
|
||||
}
|
||||
}
|
||||
|
||||
uint4 sample = (duty_output ? volume : (uint4)0);
|
||||
if(enable == false) sample = 0;
|
||||
|
||||
output = sample;
|
||||
}
|
||||
|
||||
auto APU::Square1::sweep(bool update) -> void {
|
||||
if(sweep_enable == false) return;
|
||||
|
||||
sweep_negate = sweep_direction;
|
||||
uint delta = frequency_shadow >> sweep_shift;
|
||||
int freq = frequency_shadow + (sweep_negate ? -delta : delta);
|
||||
|
||||
if(freq > 2047) {
|
||||
enable = false;
|
||||
} else if(sweep_shift && update) {
|
||||
frequency_shadow = freq;
|
||||
frequency = freq & 2047;
|
||||
period = 2 * (2048 - frequency);
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Square1::clock_length() -> void {
|
||||
if(counter && enable) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Square1::clock_sweep() -> void {
|
||||
if(enable && sweep_frequency && --sweep_period == 0) {
|
||||
sweep_period = sweep_frequency;
|
||||
sweep(1);
|
||||
sweep(0);
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Square1::clock_envelope() -> void {
|
||||
if(enable && envelope_frequency && --envelope_period == 0) {
|
||||
envelope_period = envelope_frequency;
|
||||
if(envelope_direction == 0 && volume > 0) volume--;
|
||||
if(envelope_direction == 1 && volume < 15) volume++;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Square1::write(uint r, uint8 data) -> void {
|
||||
if(r == 0) { //$ff10 NR10
|
||||
if(sweep_negate && sweep_direction && !(data & 0x08)) enable = false;
|
||||
sweep_frequency = (data >> 4) & 7;
|
||||
sweep_direction = data & 0x08;
|
||||
sweep_shift = data & 0x07;
|
||||
}
|
||||
|
||||
if(r == 1) { //$ff11 NR11
|
||||
duty = data >> 6;
|
||||
length = data & 0x3f;
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff12 NR12
|
||||
envelope_volume = data >> 4;
|
||||
envelope_direction = data & 0x08;
|
||||
envelope_frequency = data & 0x07;
|
||||
if(dac_enable() == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 3) { //$ff13 NR13
|
||||
frequency = (frequency & 0x0700) | data;
|
||||
}
|
||||
|
||||
if(r == 4) { //$ff14 NR14
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||
|
||||
if(initialize) {
|
||||
enable = dac_enable();
|
||||
period = 2 * (2048 - frequency);
|
||||
envelope_period = envelope_frequency;
|
||||
volume = envelope_volume;
|
||||
frequency_shadow = frequency;
|
||||
sweep_period = sweep_frequency;
|
||||
sweep_enable = sweep_period || sweep_shift;
|
||||
sweep_negate = false;
|
||||
if(sweep_shift) sweep(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Square1::power() -> void {
|
||||
enable = 0;
|
||||
|
||||
sweep_frequency = 0;
|
||||
sweep_direction = 0;
|
||||
sweep_shift = 0;
|
||||
sweep_negate = 0;
|
||||
duty = 0;
|
||||
length = 0;
|
||||
envelope_volume = 0;
|
||||
envelope_direction = 0;
|
||||
envelope_frequency = 0;
|
||||
frequency = 0;
|
||||
counter = 0;
|
||||
|
||||
output = 0;
|
||||
duty_output = 0;
|
||||
phase = 0;
|
||||
period = 0;
|
||||
envelope_period = 0;
|
||||
sweep_period = 0;
|
||||
frequency_shadow = 0;
|
||||
sweep_enable = 0;
|
||||
volume = 0;
|
||||
}
|
||||
|
||||
auto APU::Square1::serialize(serializer& s) -> void {
|
||||
s.integer(enable);
|
||||
|
||||
s.integer(sweep_frequency);
|
||||
s.integer(sweep_direction);
|
||||
s.integer(sweep_shift);
|
||||
s.integer(sweep_negate);
|
||||
s.integer(duty);
|
||||
s.integer(length);
|
||||
s.integer(envelope_volume);
|
||||
s.integer(envelope_direction);
|
||||
s.integer(envelope_frequency);
|
||||
s.integer(frequency);
|
||||
s.integer(counter);
|
||||
|
||||
s.integer(output);
|
||||
s.integer(duty_output);
|
||||
s.integer(phase);
|
||||
s.integer(period);
|
||||
s.integer(envelope_period);
|
||||
s.integer(sweep_period);
|
||||
s.integer(frequency_shadow);
|
||||
s.integer(sweep_enable);
|
||||
s.integer(volume);
|
||||
}
|
@@ -1,37 +0,0 @@
|
||||
struct Square1 {
|
||||
auto dac_enable() const -> bool;
|
||||
|
||||
auto run() -> void;
|
||||
auto sweep(bool update) -> void;
|
||||
auto clock_length() -> void;
|
||||
auto clock_sweep() -> void;
|
||||
auto clock_envelope() -> void;
|
||||
auto write(uint r, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool enable;
|
||||
|
||||
uint3 sweep_frequency;
|
||||
bool sweep_direction;
|
||||
uint3 sweep_shift;
|
||||
bool sweep_negate;
|
||||
uint2 duty;
|
||||
uint6 length;
|
||||
uint4 envelope_volume;
|
||||
bool envelope_direction;
|
||||
uint3 envelope_frequency;
|
||||
uint11 frequency;
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
bool duty_output;
|
||||
uint3 phase;
|
||||
uint period;
|
||||
uint3 envelope_period;
|
||||
uint3 sweep_period;
|
||||
int frequency_shadow;
|
||||
bool sweep_enable;
|
||||
uint4 volume;
|
||||
};
|
@@ -1,104 +0,0 @@
|
||||
auto APU::Square2::dac_enable() const -> bool {
|
||||
return (envelope_volume || envelope_direction);
|
||||
}
|
||||
|
||||
auto APU::Square2::run() -> void {
|
||||
if(period && --period == 0) {
|
||||
period = 2 * (2048 - frequency);
|
||||
phase++;
|
||||
switch(duty) {
|
||||
case 0: duty_output = (phase == 6); break; //______-_
|
||||
case 1: duty_output = (phase >= 6); break; //______--
|
||||
case 2: duty_output = (phase >= 4); break; //____----
|
||||
case 3: duty_output = (phase <= 5); break; //------__
|
||||
}
|
||||
}
|
||||
|
||||
uint4 sample = (duty_output ? volume : (uint4)0);
|
||||
if(enable == false) sample = 0;
|
||||
|
||||
output = sample;
|
||||
}
|
||||
|
||||
auto APU::Square2::clock_length() -> void {
|
||||
if(counter && enable) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Square2::clock_envelope() -> void {
|
||||
if(enable && envelope_frequency && --envelope_period == 0) {
|
||||
envelope_period = envelope_frequency;
|
||||
if(envelope_direction == 0 && volume > 0) volume--;
|
||||
if(envelope_direction == 1 && volume < 15) volume++;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Square2::write(uint r, uint8 data) -> void {
|
||||
if(r == 1) { //$ff16 NR21
|
||||
duty = data >> 6;
|
||||
length = (data & 0x3f);
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff17 NR22
|
||||
envelope_volume = data >> 4;
|
||||
envelope_direction = data & 0x08;
|
||||
envelope_frequency = data & 0x07;
|
||||
if(dac_enable() == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 3) { //$ff18 NR23
|
||||
frequency = (frequency & 0x0700) | data;
|
||||
}
|
||||
|
||||
if(r == 4) { //$ff19 NR24
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||
|
||||
if(initialize) {
|
||||
enable = dac_enable();
|
||||
period = 2 * (2048 - frequency);
|
||||
envelope_period = envelope_frequency;
|
||||
volume = envelope_volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Square2::power() -> void {
|
||||
enable = 0;
|
||||
|
||||
duty = 0;
|
||||
length = 0;
|
||||
envelope_volume = 0;
|
||||
envelope_direction = 0;
|
||||
envelope_frequency = 0;
|
||||
frequency = 0;
|
||||
counter = 0;
|
||||
|
||||
output = 0;
|
||||
duty_output = 0;
|
||||
phase = 0;
|
||||
period = 0;
|
||||
envelope_period = 0;
|
||||
volume = 0;
|
||||
}
|
||||
|
||||
auto APU::Square2::serialize(serializer& s) -> void {
|
||||
s.integer(enable);
|
||||
|
||||
s.integer(duty);
|
||||
s.integer(length);
|
||||
s.integer(envelope_volume);
|
||||
s.integer(envelope_direction);
|
||||
s.integer(envelope_frequency);
|
||||
s.integer(frequency);
|
||||
s.integer(counter);
|
||||
|
||||
s.integer(output);
|
||||
s.integer(duty_output);
|
||||
s.integer(phase);
|
||||
s.integer(period);
|
||||
s.integer(envelope_period);
|
||||
s.integer(volume);
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
struct Square2 {
|
||||
auto dac_enable() const -> bool;
|
||||
|
||||
auto run() -> void;
|
||||
auto clock_length() -> void;
|
||||
auto clock_envelope() -> void;
|
||||
auto write(uint r, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool enable;
|
||||
|
||||
uint2 duty;
|
||||
uint6 length;
|
||||
uint4 envelope_volume;
|
||||
bool envelope_direction;
|
||||
uint3 envelope_frequency;
|
||||
uint11 frequency;
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
bool duty_output;
|
||||
uint3 phase;
|
||||
uint period;
|
||||
uint3 envelope_period;
|
||||
uint4 volume;
|
||||
};
|
@@ -1,93 +0,0 @@
|
||||
auto APU::Wave::run() -> void {
|
||||
if(period && --period == 0) {
|
||||
period = 1 * (2048 - frequency);
|
||||
pattern_sample = pattern[++pattern_offset];
|
||||
}
|
||||
|
||||
uint4 sample = pattern_sample >> volume_shift;
|
||||
if(enable == false) sample = 0;
|
||||
|
||||
output = sample;
|
||||
}
|
||||
|
||||
auto APU::Wave::clock_length() -> void {
|
||||
if(enable && counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Wave::write(uint r, uint8 data) -> void {
|
||||
if(r == 0) { //$ff1a NR30
|
||||
dac_enable = data & 0x80;
|
||||
if(dac_enable == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 1) { //$ff1b NR31
|
||||
length = data;
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff1c NR32
|
||||
switch((data >> 5) & 3) {
|
||||
case 0: volume_shift = 4; break; // 0%
|
||||
case 1: volume_shift = 0; break; //100%
|
||||
case 2: volume_shift = 1; break; // 50%
|
||||
case 3: volume_shift = 2; break; // 25%
|
||||
}
|
||||
}
|
||||
|
||||
if(r == 3) { //$ff1d NR33
|
||||
frequency = (frequency & 0x0700) | data;
|
||||
}
|
||||
|
||||
if(r == 4) { //$ff1e NR34
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||
|
||||
if(initialize) {
|
||||
enable = dac_enable;
|
||||
period = 1 * (2048 - frequency);
|
||||
pattern_offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Wave::write_pattern(uint p, uint8 data) -> void {
|
||||
p <<= 1;
|
||||
pattern[p + 0] = (data >> 4) & 15;
|
||||
pattern[p + 1] = (data >> 0) & 15;
|
||||
}
|
||||
|
||||
auto APU::Wave::power() -> void {
|
||||
enable = 0;
|
||||
|
||||
dac_enable = 0;
|
||||
volume_shift = 0;
|
||||
frequency = 0;
|
||||
counter = 0;
|
||||
|
||||
LinearFeedbackShiftRegisterGenerator r;
|
||||
for(auto& n : pattern) n = r() & 15;
|
||||
|
||||
output = 0;
|
||||
length = 0;
|
||||
period = 0;
|
||||
pattern_offset = 0;
|
||||
pattern_sample = 0;
|
||||
}
|
||||
|
||||
auto APU::Wave::serialize(serializer& s) -> void {
|
||||
s.integer(enable);
|
||||
|
||||
s.integer(dac_enable);
|
||||
s.integer(volume_shift);
|
||||
s.integer(frequency);
|
||||
s.integer(counter);
|
||||
s.array(pattern);
|
||||
|
||||
s.integer(output);
|
||||
s.integer(length);
|
||||
s.integer(period);
|
||||
s.integer(pattern_offset);
|
||||
s.integer(pattern_sample);
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
struct Wave {
|
||||
auto run() -> void;
|
||||
auto clock_length() -> void;
|
||||
auto write(uint r, uint8 data) -> void;
|
||||
auto write_pattern(uint p, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool enable;
|
||||
|
||||
bool dac_enable;
|
||||
uint volume_shift;
|
||||
uint11 frequency;
|
||||
bool counter;
|
||||
uint8 pattern[32];
|
||||
|
||||
int16 output;
|
||||
uint8 length;
|
||||
uint period;
|
||||
uint5 pattern_offset;
|
||||
uint4 pattern_sample;
|
||||
};
|
@@ -1,23 +0,0 @@
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
auto Scheduler::init() -> void {
|
||||
host_thread = co_active();
|
||||
active_thread = cpu.thread;
|
||||
}
|
||||
|
||||
auto Scheduler::enter() -> void {
|
||||
host_thread = co_active();
|
||||
co_switch(active_thread);
|
||||
}
|
||||
|
||||
auto Scheduler::exit(ExitReason reason) -> void {
|
||||
exit_reason = reason;
|
||||
active_thread = co_active();
|
||||
co_switch(host_thread);
|
||||
}
|
||||
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
struct Scheduler {
|
||||
enum class SynchronizeMode : uint { None, CPU, All } sync;
|
||||
enum class ExitReason : uint { UnknownEvent, StepEvent, FrameEvent, SynchronizeEvent };
|
||||
|
||||
auto init() -> void;
|
||||
auto enter() -> void;
|
||||
auto exit(ExitReason) -> void;
|
||||
|
||||
cothread_t host_thread = nullptr;
|
||||
cothread_t active_thread = nullptr;
|
||||
ExitReason exit_reason = ExitReason::UnknownEvent;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
@@ -1,79 +0,0 @@
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
|
||||
#include "serialization.cpp"
|
||||
System system;
|
||||
|
||||
System::System() {
|
||||
for(auto& byte : bootROM.dmg) byte = 0;
|
||||
for(auto& byte : bootROM.sgb) byte = 0;
|
||||
for(auto& byte : bootROM.cgb) byte = 0;
|
||||
}
|
||||
|
||||
auto System::run() -> void {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::None;
|
||||
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) {
|
||||
interface->videoRefresh(video.palette, ppu.screen, 4 * 160, 160, 144);
|
||||
}
|
||||
}
|
||||
|
||||
auto System::runtosave() -> void {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::CPU;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.active_thread = ppu.thread;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.active_thread = apu.thread;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::None;
|
||||
}
|
||||
|
||||
auto System::runthreadtosave() -> void {
|
||||
while(true) {
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason == Scheduler::ExitReason::SynchronizeEvent) break;
|
||||
if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) {
|
||||
interface->videoRefresh(video.palette, ppu.screen, 4 * 160, 160, 144);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto System::init() -> void {
|
||||
assert(interface != nullptr);
|
||||
}
|
||||
|
||||
auto System::load(Revision revision) -> void {
|
||||
this->revision = revision;
|
||||
serialize_init();
|
||||
if(revision == Revision::SuperGameBoy) return; //Super Famicom core loads boot ROM for SGB
|
||||
|
||||
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
|
||||
if(auto bootROM = document["system/cpu/rom/name"].text()) {
|
||||
interface->loadRequest(
|
||||
revision == Revision::GameBoy ? ID::GameBoyBootROM : ID::GameBoyColorBootROM,
|
||||
bootROM, true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
auto System::power() -> void {
|
||||
bus.power();
|
||||
cartridge.power();
|
||||
cpu.power();
|
||||
ppu.power();
|
||||
apu.power();
|
||||
scheduler.init();
|
||||
|
||||
clocks_executed = 0;
|
||||
}
|
||||
|
||||
}
|
@@ -1,116 +0,0 @@
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
|
||||
Video video;
|
||||
|
||||
Video::Video() {
|
||||
palette = new uint32_t[1 << 15]();
|
||||
}
|
||||
|
||||
Video::~Video() {
|
||||
delete[] palette;
|
||||
}
|
||||
|
||||
auto Video::generate_palette(Emulator::Interface::PaletteMode mode) -> void {
|
||||
this->mode = mode;
|
||||
if(system.dmg()) for(auto n : range(4)) palette[n] = paletteDMG(n);
|
||||
if(system.sgb()) for(auto n : range(4)) palette[n] = paletteSGB(n);
|
||||
if(system.cgb()) for(auto n : range(1 << 15)) palette[n] = paletteCGB(n);
|
||||
}
|
||||
|
||||
auto Video::paletteDMG(uint color) const -> uint {
|
||||
if(mode == Emulator::Interface::PaletteMode::Literal) {
|
||||
return color;
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Channel) {
|
||||
uint L = image::normalize(color, 2, 16);
|
||||
return interface->videoColor(color, 0, 0, 0, L);
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Standard) {
|
||||
uint L = image::normalize(3 - color, 2, 16);
|
||||
return interface->videoColor(color, 0, L, L, L);
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Emulation) {
|
||||
uint R = monochrome[color][0];
|
||||
uint G = monochrome[color][1];
|
||||
uint B = monochrome[color][2];
|
||||
return interface->videoColor(color, 0, R, G, B);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto Video::paletteSGB(uint color) const -> uint {
|
||||
return color;
|
||||
}
|
||||
|
||||
auto Video::paletteCGB(uint color) const -> uint {
|
||||
if(mode == Emulator::Interface::PaletteMode::Literal) {
|
||||
return color;
|
||||
}
|
||||
|
||||
uint r = (color >> 0) & 31;
|
||||
uint g = (color >> 5) & 31;
|
||||
uint b = (color >> 10) & 31;
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Channel) {
|
||||
r = image::normalize(r, 5, 16);
|
||||
g = image::normalize(g, 5, 16);
|
||||
b = image::normalize(b, 5, 16);
|
||||
return interface->videoColor(color, 0, r, g, b);
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Standard) {
|
||||
r = image::normalize(r, 5, 16);
|
||||
g = image::normalize(g, 5, 16);
|
||||
b = image::normalize(b, 5, 16);
|
||||
return interface->videoColor(color, 0, r, g, b);
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Emulation) {
|
||||
uint R = (r * 26 + g * 4 + b * 2);
|
||||
uint G = ( g * 24 + b * 8);
|
||||
uint B = (r * 6 + g * 4 + b * 22);
|
||||
|
||||
R = min(960, R);
|
||||
G = min(960, G);
|
||||
B = min(960, B);
|
||||
|
||||
R = R << 6 | R >> 4;
|
||||
G = G << 6 | G >> 4;
|
||||
B = B << 6 | B >> 4;
|
||||
|
||||
return interface->videoColor(color, 0, R, G, B);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DMG_PALETTE_GREEN
|
||||
//#define DMG_PALETTE_YELLOW
|
||||
//#define DMG_PALETTE_WHITE
|
||||
|
||||
const uint16 Video::monochrome[4][3] = {
|
||||
#if defined(DMG_PALETTE_GREEN)
|
||||
{0xaeae, 0xd9d9, 0x2727},
|
||||
{0x5858, 0xa0a0, 0x2828},
|
||||
{0x2020, 0x6262, 0x2929},
|
||||
{0x1a1a, 0x4545, 0x2a2a},
|
||||
#elif defined(DMG_PALETTE_YELLOW)
|
||||
{0xffff, 0xf7f7, 0x7b7b},
|
||||
{0xb5b5, 0xaeae, 0x4a4a},
|
||||
{0x6b6b, 0x6969, 0x3131},
|
||||
{0x2121, 0x2020, 0x1010},
|
||||
#else //DMG_PALETTE_WHITE
|
||||
{0xffff, 0xffff, 0xffff},
|
||||
{0xaaaa, 0xaaaa, 0xaaaa},
|
||||
{0x5555, 0x5555, 0x5555},
|
||||
{0x0000, 0x0000, 0x0000},
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
struct Video {
|
||||
Video();
|
||||
~Video();
|
||||
|
||||
auto generate_palette(Emulator::Interface::PaletteMode mode) -> void;
|
||||
|
||||
uint32* palette = nullptr;
|
||||
|
||||
private:
|
||||
Emulator::Interface::PaletteMode mode;
|
||||
static const uint16 monochrome[4][3];
|
||||
auto paletteDMG(uint color) const -> uint;
|
||||
auto paletteSGB(uint color) const -> uint;
|
||||
auto paletteCGB(uint color) const -> uint;
|
||||
};
|
||||
|
||||
extern Video video;
|
@@ -1,15 +0,0 @@
|
||||
gba_objects := gba-memory gba-interface gba-scheduler gba-system
|
||||
gba_objects += gba-video gba-cartridge gba-player
|
||||
gba_objects += gba-cpu gba-ppu gba-apu
|
||||
objects += $(gba_objects)
|
||||
|
||||
obj/gba-memory.o: $(gba)/memory/memory.cpp $(call rwildcard,$(gba)/memory)
|
||||
obj/gba-interface.o: $(gba)/interface/interface.cpp $(call rwildcard,$(gba)/interface)
|
||||
obj/gba-scheduler.o: $(gba)/scheduler/scheduler.cpp $(call rwildcard,$(gba)/scheduler)
|
||||
obj/gba-system.o: $(gba)/system/system.cpp $(call rwildcard,$(gba)/system)
|
||||
obj/gba-video.o: $(gba)/video/video.cpp $(call rwildcard,$(gba)/video)
|
||||
obj/gba-cartridge.o: $(gba)/cartridge/cartridge.cpp $(call rwildcard,$(gba)/cartridge)
|
||||
obj/gba-player.o: $(gba)/player/player.cpp $(call rwildcard,$(gba)/player)
|
||||
obj/gba-cpu.o: $(gba)/cpu/cpu.cpp $(call rwildcard,$(gba)/cpu)
|
||||
obj/gba-ppu.o: $(gba)/ppu/ppu.cpp $(call rwildcard,$(gba)/ppu)
|
||||
obj/gba-apu.o: $(gba)/apu/apu.cpp $(call rwildcard,$(gba)/apu)
|
216
gba/apu/mmio.cpp
216
gba/apu/mmio.cpp
@@ -1,216 +0,0 @@
|
||||
auto APU::read(uint32 addr) -> uint8 {
|
||||
switch(addr) {
|
||||
|
||||
//NR10
|
||||
case 0x04000060: return square1.read(0);
|
||||
case 0x04000061: return 0u;
|
||||
|
||||
//NR11 + NR12
|
||||
case 0x04000062: return square1.read(1);
|
||||
case 0x04000063: return square1.read(2);
|
||||
|
||||
//NR13 + NR14
|
||||
case 0x04000064: return square1.read(3);
|
||||
case 0x04000065: return square1.read(4);
|
||||
|
||||
//NR21 + NR22
|
||||
case 0x04000068: return square2.read(1);
|
||||
case 0x04000069: return square2.read(2);
|
||||
|
||||
//NR23 + NR24
|
||||
case 0x0400006c: return square2.read(3);
|
||||
case 0x0400006d: return square2.read(4);
|
||||
|
||||
//NR30
|
||||
case 0x04000070: return wave.read(0);
|
||||
case 0x04000071: return 0u;
|
||||
|
||||
//NR31 + NR32
|
||||
case 0x04000072: return wave.read(1);
|
||||
case 0x04000073: return wave.read(2);
|
||||
|
||||
//NR33 + NR34
|
||||
case 0x04000074: return wave.read(3);
|
||||
case 0x04000075: return wave.read(4);
|
||||
|
||||
//NR41 + NR42
|
||||
case 0x04000078: return noise.read(1);
|
||||
case 0x04000079: return noise.read(2);
|
||||
|
||||
//NR43 + NR44
|
||||
case 0x0400007c: return noise.read(3);
|
||||
case 0x0400007d: return noise.read(4);
|
||||
|
||||
//NR50 + NR51
|
||||
case 0x04000080: return sequencer.read(0);
|
||||
case 0x04000081: return sequencer.read(1);
|
||||
|
||||
//SOUND_CNT_H
|
||||
case 0x04000082:
|
||||
return (fifo[1].volume << 3) | (fifo[0].volume << 2) | (sequencer.volume << 0);
|
||||
case 0x04000083:
|
||||
return (fifo[1].timer << 6) | (fifo[1].lenable << 5) | (fifo[1].renable << 4)
|
||||
| (fifo[0].timer << 2) | (fifo[0].lenable << 1) | (fifo[0].renable << 0);
|
||||
|
||||
//NR52
|
||||
case 0x04000084: return sequencer.read(2);
|
||||
case 0x04000085: return 0u;
|
||||
|
||||
//SOUNDBIAS
|
||||
case 0x04000088: return regs.bias >> 0;
|
||||
case 0x04000089: return regs.bias >> 8;
|
||||
|
||||
//WAVE_RAM0_L
|
||||
case 0x04000090: return wave.readram( 0);
|
||||
case 0x04000091: return wave.readram( 1);
|
||||
|
||||
//WAVE_RAM0_H
|
||||
case 0x04000092: return wave.readram( 2);
|
||||
case 0x04000093: return wave.readram( 3);
|
||||
|
||||
//WAVE_RAM1_L
|
||||
case 0x04000094: return wave.readram( 4);
|
||||
case 0x04000095: return wave.readram( 5);
|
||||
|
||||
//WAVE_RAM1_H
|
||||
case 0x04000096: return wave.readram( 6);
|
||||
case 0x04000097: return wave.readram( 7);
|
||||
|
||||
//WAVE_RAM2_L
|
||||
case 0x04000098: return wave.readram( 8);
|
||||
case 0x04000099: return wave.readram( 9);
|
||||
|
||||
//WAVE_RAM2_H
|
||||
case 0x0400009a: return wave.readram(10);
|
||||
case 0x0400009b: return wave.readram(11);
|
||||
|
||||
//WAVE_RAM3_L
|
||||
case 0x0400009c: return wave.readram(12);
|
||||
case 0x0400009d: return wave.readram(13);
|
||||
|
||||
//WAVE_RAM3_H
|
||||
case 0x0400009e: return wave.readram(14);
|
||||
case 0x0400009f: return wave.readram(15);
|
||||
|
||||
}
|
||||
|
||||
return 0u;
|
||||
}
|
||||
|
||||
auto APU::write(uint32 addr, uint8 byte) -> void {
|
||||
switch(addr) {
|
||||
|
||||
//NR10
|
||||
case 0x04000060: return square1.write(0, byte);
|
||||
case 0x04000061: return;
|
||||
|
||||
//NR11 + NR12
|
||||
case 0x04000062: return square1.write(1, byte);
|
||||
case 0x04000063: return square1.write(2, byte);
|
||||
|
||||
//NR13 + NR14
|
||||
case 0x04000064: return square1.write(3, byte);
|
||||
case 0x04000065: return square1.write(4, byte);
|
||||
|
||||
//NR21 + NR22
|
||||
case 0x04000068: return square2.write(1, byte);
|
||||
case 0x04000069: return square2.write(2, byte);
|
||||
|
||||
//NR23 + NR24
|
||||
case 0x0400006c: return square2.write(3, byte);
|
||||
case 0x0400006d: return square2.write(4, byte);
|
||||
|
||||
//NR30
|
||||
case 0x04000070: return wave.write(0, byte);
|
||||
case 0x04000071: return;
|
||||
|
||||
//NR31 + NR32
|
||||
case 0x04000072: return wave.write(1, byte);
|
||||
case 0x04000073: return wave.write(2, byte);
|
||||
|
||||
//NR33 + NR34
|
||||
case 0x04000074: return wave.write(3, byte);
|
||||
case 0x04000075: return wave.write(4, byte);
|
||||
|
||||
//NR41 + NR42
|
||||
case 0x04000078: return noise.write(1, byte);
|
||||
case 0x04000079: return noise.write(2, byte);
|
||||
|
||||
//NR43 + NR44
|
||||
case 0x0400007c: return noise.write(3, byte);
|
||||
case 0x0400007d: return noise.write(4, byte);
|
||||
|
||||
//NR50 + NR51
|
||||
case 0x04000080: return sequencer.write(0, byte);
|
||||
case 0x04000081: return sequencer.write(1, byte);
|
||||
|
||||
//SOUND_CNT_H
|
||||
case 0x04000082:
|
||||
sequencer.volume = byte >> 0;
|
||||
fifo[0].volume = byte >> 2;
|
||||
fifo[1].volume = byte >> 3;
|
||||
return;
|
||||
case 0x04000083:
|
||||
fifo[0].renable = byte >> 0;
|
||||
fifo[0].lenable = byte >> 1;
|
||||
fifo[0].timer = byte >> 2;
|
||||
if(byte & 1 << 3) fifo[0].reset();
|
||||
fifo[1].renable = byte >> 4;
|
||||
fifo[1].lenable = byte >> 5;
|
||||
fifo[1].timer = byte >> 6;
|
||||
if(byte & 1 << 7) fifo[1].reset();
|
||||
return;
|
||||
|
||||
//NR52
|
||||
case 0x04000084: return sequencer.write(2, byte);
|
||||
case 0x04000085: return;
|
||||
|
||||
//SOUNDBIAS
|
||||
case 0x04000088: regs.bias = (regs.bias & 0xff00) | (byte << 0); return;
|
||||
case 0x04000089: regs.bias = (regs.bias & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//WAVE_RAM0_L
|
||||
case 0x04000090: return wave.writeram( 0, byte);
|
||||
case 0x04000091: return wave.writeram( 1, byte);
|
||||
|
||||
//WAVE_RAM0_H
|
||||
case 0x04000092: return wave.writeram( 2, byte);
|
||||
case 0x04000093: return wave.writeram( 3, byte);
|
||||
|
||||
//WAVE_RAM1_L
|
||||
case 0x04000094: return wave.writeram( 4, byte);
|
||||
case 0x04000095: return wave.writeram( 5, byte);
|
||||
|
||||
//WAVE_RAM1_H
|
||||
case 0x04000096: return wave.writeram( 6, byte);
|
||||
case 0x04000097: return wave.writeram( 7, byte);
|
||||
|
||||
//WAVE_RAM2_L
|
||||
case 0x04000098: return wave.writeram( 8, byte);
|
||||
case 0x04000099: return wave.writeram( 9, byte);
|
||||
|
||||
//WAVE_RAM2_H
|
||||
case 0x0400009a: return wave.writeram(10, byte);
|
||||
case 0x0400009b: return wave.writeram(11, byte);
|
||||
|
||||
//WAVE_RAM3_L
|
||||
case 0x0400009c: return wave.writeram(12, byte);
|
||||
case 0x0400009d: return wave.writeram(13, byte);
|
||||
|
||||
//WAVE_RAM3_H
|
||||
case 0x0400009e: return wave.writeram(14, byte);
|
||||
case 0x0400009f: return wave.writeram(15, byte);
|
||||
|
||||
//FIFO_A_L
|
||||
//FIFO_A_H
|
||||
case 0x040000a0: case 0x040000a1:
|
||||
case 0x040000a2: case 0x040000a3:
|
||||
return fifo[0].write(byte);
|
||||
|
||||
//FIFO_B_L
|
||||
//FIFO_B_H
|
||||
case 0x040000a4: case 0x040000a5:
|
||||
case 0x040000a6: case 0x040000a7:
|
||||
return fifo[1].write(byte);
|
||||
}
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
APU::Registers::SoundBias::operator uint16() const {
|
||||
return (
|
||||
(level << 0)
|
||||
| (amplitude << 14)
|
||||
);
|
||||
}
|
||||
|
||||
auto APU::Registers::SoundBias::operator=(uint16 source) -> uint16 {
|
||||
level = (source >> 0) & 1023;
|
||||
amplitude = (source >> 14) & 3;
|
||||
return operator uint16();
|
||||
}
|
321
gba/cpu/mmio.cpp
321
gba/cpu/mmio.cpp
@@ -1,321 +0,0 @@
|
||||
auto CPU::read(uint32 addr) -> uint8 {
|
||||
uint8 result = 0;
|
||||
|
||||
switch(addr) {
|
||||
|
||||
//DMA0CNT_H
|
||||
//DMA1CNT_H
|
||||
//DMA2CNT_H
|
||||
//DMA3CNT_H
|
||||
case 0x040000ba: case 0x040000bb:
|
||||
case 0x040000c6: case 0x040000c7:
|
||||
case 0x040000d2: case 0x040000d3:
|
||||
case 0x040000de: case 0x040000df: {
|
||||
auto& dma = regs.dma[(addr - 0x040000ba) / 12];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
return dma.control >> shift;
|
||||
}
|
||||
|
||||
//TM0CNT_L
|
||||
//TM1CNT_L
|
||||
//TM2CNT_L
|
||||
//TM3CNT_L
|
||||
case 0x04000100: case 0x04000101:
|
||||
case 0x04000104: case 0x04000105:
|
||||
case 0x04000108: case 0x04000109:
|
||||
case 0x0400010c: case 0x0400010d: {
|
||||
auto& timer = regs.timer[(addr >> 2) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
return timer.period >> shift;
|
||||
}
|
||||
|
||||
//TIM0CNT_H
|
||||
case 0x04000102: case 0x04000103:
|
||||
case 0x04000106: case 0x04000107:
|
||||
case 0x0400010a: case 0x0400010b:
|
||||
case 0x0400010e: case 0x0400010f: {
|
||||
auto& timer = regs.timer[(addr >> 2) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
return timer.control >> shift;
|
||||
}
|
||||
|
||||
//SIOMULTI0 (SIODATA32_L)
|
||||
//SIOMULTI1 (SIODATA32_H)
|
||||
//SIOMULTI2
|
||||
//SIOMULTI3
|
||||
case 0x04000120: case 0x04000121:
|
||||
case 0x04000122: case 0x04000123:
|
||||
case 0x04000124: case 0x04000125:
|
||||
case 0x04000126: case 0x04000127: {
|
||||
if(auto data = player.read()) return data() >> ((addr & 3) << 3);
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
auto& data = regs.serial.data[(addr >> 1) & 3];
|
||||
return data >> shift;
|
||||
}
|
||||
|
||||
//SIOCNT
|
||||
case 0x04000128: return regs.serial.control >> 0;
|
||||
case 0x04000129: return regs.serial.control >> 8;
|
||||
|
||||
//SIOMLT_SEND (SIODATA8)
|
||||
case 0x0400012a: return regs.serial.data8;
|
||||
case 0x0400012b: return 0u;
|
||||
|
||||
//KEYINPUT
|
||||
case 0x04000130:
|
||||
if(auto result = player.keyinput()) return result() >> 0;
|
||||
for(unsigned n = 0; n < 8; n++) result |= interface->inputPoll(0, 0, n) << n;
|
||||
if((result & 0xc0) == 0xc0) result &= ~0xc0; //up+down cannot be pressed simultaneously
|
||||
if((result & 0x30) == 0x30) result &= ~0x30; //left+right cannot be pressed simultaneously
|
||||
return result ^ 0xff;
|
||||
case 0x04000131:
|
||||
if(auto result = player.keyinput()) return result() >> 8;
|
||||
result |= interface->inputPoll(0, 0, 8) << 0;
|
||||
result |= interface->inputPoll(0, 0, 9) << 1;
|
||||
return result ^ 0x03;
|
||||
|
||||
//KEYCNT
|
||||
case 0x04000132: return regs.keypad.control >> 0;
|
||||
case 0x04000133: return regs.keypad.control >> 8;
|
||||
|
||||
//RCNT
|
||||
case 0x04000134: return regs.joybus.settings >> 0;
|
||||
case 0x04000135: return regs.joybus.settings >> 8;
|
||||
|
||||
//JOYCNT
|
||||
case 0x04000140: return regs.joybus.control >> 0;
|
||||
case 0x04000141: return regs.joybus.control >> 8;
|
||||
|
||||
//JOY_RECV_L
|
||||
//JOY_RECV_H
|
||||
case 0x04000150: return regs.joybus.receive >> 0;
|
||||
case 0x04000151: return regs.joybus.receive >> 8;
|
||||
case 0x04000152: return regs.joybus.receive >> 16;
|
||||
case 0x04000153: return regs.joybus.receive >> 24;
|
||||
|
||||
//JOY_TRANS_L
|
||||
//JOY_TRANS_H
|
||||
case 0x04000154: return regs.joybus.transmit >> 0;
|
||||
case 0x04000155: return regs.joybus.transmit >> 8;
|
||||
case 0x04000156: return regs.joybus.transmit >> 16;
|
||||
case 0x04000157: return regs.joybus.transmit >> 24;
|
||||
|
||||
//JOYSTAT
|
||||
case 0x04000158: return regs.joybus.status >> 0;
|
||||
case 0x04000159: return regs.joybus.status >> 8;
|
||||
|
||||
//IE
|
||||
case 0x04000200: return regs.irq.enable >> 0;
|
||||
case 0x04000201: return regs.irq.enable >> 8;
|
||||
|
||||
//IF
|
||||
case 0x04000202: return regs.irq.flag >> 0;
|
||||
case 0x04000203: return regs.irq.flag >> 8;
|
||||
|
||||
//WAITCNT
|
||||
case 0x04000204: return regs.wait.control >> 0;
|
||||
case 0x04000205: return regs.wait.control >> 8;
|
||||
|
||||
//IME
|
||||
case 0x04000208: return regs.ime;
|
||||
case 0x04000209: return 0u;
|
||||
|
||||
//POSTFLG + HALTCNT
|
||||
case 0x04000300: return regs.postboot;
|
||||
case 0x04000301: return 0u;
|
||||
|
||||
//MEMCNT_L
|
||||
case 0x04000800: return regs.memory.control >> 0;
|
||||
case 0x04000801: return regs.memory.control >> 8;
|
||||
|
||||
//MEMCNT_H
|
||||
case 0x04000802: return regs.memory.control >> 16;
|
||||
case 0x04000803: return regs.memory.control >> 24;
|
||||
|
||||
}
|
||||
|
||||
return 0u;
|
||||
}
|
||||
|
||||
auto CPU::write(uint32 addr, uint8 byte) -> void {
|
||||
switch(addr) {
|
||||
|
||||
//DMA0SAD
|
||||
//DMA1SAD
|
||||
//DMA2SAD
|
||||
//DMA3SAD
|
||||
case 0x040000b0: case 0x040000b1: case 0x040000b2: case 0x040000b3:
|
||||
case 0x040000bc: case 0x040000bd: case 0x040000be: case 0x040000bf:
|
||||
case 0x040000c8: case 0x040000c9: case 0x040000ca: case 0x040000cb:
|
||||
case 0x040000d4: case 0x040000d5: case 0x040000d6: case 0x040000d7: {
|
||||
auto& dma = regs.dma[(addr - 0x040000b0) / 12];
|
||||
unsigned shift = (addr & 3) * 8;
|
||||
dma.source = (dma.source & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//DMA0DAD
|
||||
//DMA1DAD
|
||||
//DMA2DAD
|
||||
//DMA3DAD
|
||||
case 0x040000b4: case 0x040000b5: case 0x040000b6: case 0x040000b7:
|
||||
case 0x040000c0: case 0x040000c1: case 0x040000c2: case 0x040000c3:
|
||||
case 0x040000cc: case 0x040000cd: case 0x040000ce: case 0x040000cf:
|
||||
case 0x040000d8: case 0x040000d9: case 0x040000da: case 0x040000db: {
|
||||
auto& dma = regs.dma[(addr - 0x040000b4) / 12];
|
||||
unsigned shift = (addr & 3) * 8;
|
||||
dma.target = (dma.target & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//DMA0CNT_L
|
||||
//DMA1CNT_L
|
||||
//DMA2CNT_L
|
||||
//DMA3CNT_L
|
||||
case 0x040000b8: case 0x040000b9:
|
||||
case 0x040000c4: case 0x040000c5:
|
||||
case 0x040000d0: case 0x040000d1:
|
||||
case 0x040000dc: case 0x040000dd: {
|
||||
auto& dma = regs.dma[(addr - 0x040000b8) / 12];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
dma.length = (dma.length & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//DMA0CNT_H
|
||||
//DMA1CNT_H
|
||||
//DMA2CNT_H
|
||||
//DMA3CNT_H
|
||||
case 0x040000ba: case 0x040000bb:
|
||||
case 0x040000c6: case 0x040000c7:
|
||||
case 0x040000d2: case 0x040000d3:
|
||||
case 0x040000de: case 0x040000df: {
|
||||
if(addr == 0x040000bb || addr == 0x040000c7 || addr == 0x040000d3) byte &= 0xf7; //gamepak DRQ valid for DMA3 only
|
||||
auto& dma = regs.dma[(addr - 0x040000ba) / 12];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
bool enable = dma.control.enable;
|
||||
dma.control = (dma.control & ~(255 << shift)) | (byte << shift);
|
||||
if(enable == 0 && dma.control.enable) {
|
||||
if(dma.control.timingmode == 0) dma.pending = true; //immediate transfer mode
|
||||
dma.run.target = dma.target;
|
||||
dma.run.source = dma.source;
|
||||
dma.run.length = dma.length;
|
||||
} else if(dma.control.enable == 0) {
|
||||
dma.pending = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//TM0CNT_L
|
||||
//TM1CNT_L
|
||||
//TM2CNT_L
|
||||
//TM3CNT_L
|
||||
case 0x04000100: case 0x04000101:
|
||||
case 0x04000104: case 0x04000105:
|
||||
case 0x04000108: case 0x04000109:
|
||||
case 0x0400010c: case 0x0400010d: {
|
||||
auto& timer = regs.timer[(addr >> 2) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
timer.reload = (timer.reload & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//TM0CNT_H
|
||||
//TM1CNT_H
|
||||
//TM2CNT_H
|
||||
//TM3CNT_H
|
||||
case 0x04000102:
|
||||
case 0x04000106:
|
||||
case 0x0400010a:
|
||||
case 0x0400010e: {
|
||||
auto& timer = regs.timer[(addr >> 2) & 3];
|
||||
bool enable = timer.control.enable;
|
||||
timer.control = byte;
|
||||
if(enable == 0 && timer.control.enable == 1) {
|
||||
timer.pending = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//SIOMULTI0 (SIODATA32_L)
|
||||
//SIOMULTI1 (SIODATA32_H)
|
||||
//SIOMULTI2
|
||||
//SIOMULTI3
|
||||
case 0x04000120: case 0x04000121:
|
||||
case 0x04000122: case 0x04000123:
|
||||
case 0x04000124: case 0x04000125:
|
||||
case 0x04000126: case 0x04000127: {
|
||||
player.write(byte, addr & 3);
|
||||
auto& data = regs.serial.data[(addr >> 1) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
data = (data & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//SIOCNT
|
||||
case 0x04000128: regs.serial.control = (regs.serial.control & 0xff00) | (byte << 0); return;
|
||||
case 0x04000129: regs.serial.control = (regs.serial.control & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//SIOMLT_SEND (SIODATA8)
|
||||
case 0x0400012a: regs.serial.data8 = byte; return;
|
||||
case 0x0400012b: return;
|
||||
|
||||
//KEYCNT
|
||||
case 0x04000132: regs.keypad.control = (regs.keypad.control & 0xff00) | (byte << 0); return;
|
||||
case 0x04000133: regs.keypad.control = (regs.keypad.control & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//RCNT
|
||||
case 0x04000134: regs.joybus.settings = (regs.joybus.settings & 0xff00) | (byte << 0); return;
|
||||
case 0x04000135: regs.joybus.settings = (regs.joybus.settings & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//JOYCNT
|
||||
case 0x04000140: regs.joybus.control = (regs.joybus.control & 0xff00) | (byte << 0); return;
|
||||
case 0x04000141: regs.joybus.control = (regs.joybus.control & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//JOY_RECV_L
|
||||
//JOY_RECV_H
|
||||
case 0x04000150: regs.joybus.receive = (regs.joybus.receive & 0xffffff00) | (byte << 0); return;
|
||||
case 0x04000151: regs.joybus.receive = (regs.joybus.receive & 0xffff00ff) | (byte << 8); return;
|
||||
case 0x04000152: regs.joybus.receive = (regs.joybus.receive & 0xff00ffff) | (byte << 16); return;
|
||||
case 0x04000153: regs.joybus.receive = (regs.joybus.receive & 0x00ffffff) | (byte << 24); return;
|
||||
|
||||
//JOY_TRANS_L
|
||||
//JOY_TRANS_H
|
||||
case 0x04000154: regs.joybus.transmit = (regs.joybus.transmit & 0xffffff00) | (byte << 0); return;
|
||||
case 0x04000155: regs.joybus.transmit = (regs.joybus.transmit & 0xffff00ff) | (byte << 8); return;
|
||||
case 0x04000156: regs.joybus.transmit = (regs.joybus.transmit & 0xff00ffff) | (byte << 16); return;
|
||||
case 0x04000157: regs.joybus.transmit = (regs.joybus.transmit & 0x00ffffff) | (byte << 24); return;
|
||||
|
||||
//JOYSTAT
|
||||
case 0x04000158: regs.joybus.status = (regs.joybus.status & 0xff00) | (byte << 0); return;
|
||||
case 0x04000159: regs.joybus.status = (regs.joybus.status & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//IE
|
||||
case 0x04000200: regs.irq.enable = (regs.irq.enable & 0xff00) | (byte << 0); return;
|
||||
case 0x04000201: regs.irq.enable = (regs.irq.enable & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//IF
|
||||
case 0x04000202: regs.irq.flag = regs.irq.flag & ~(byte << 0); return;
|
||||
case 0x04000203: regs.irq.flag = regs.irq.flag & ~(byte << 8); return;
|
||||
|
||||
//WAITCNT
|
||||
case 0x04000204: regs.wait.control = (regs.wait.control & 0xff00) | ((byte & 0xff) << 0); return;
|
||||
case 0x04000205: regs.wait.control = (regs.wait.control & 0x00ff) | ((byte & 0x7f) << 8); return;
|
||||
|
||||
//IME
|
||||
case 0x04000208: regs.ime = byte >> 0; return;
|
||||
case 0x04000209: return;
|
||||
|
||||
//POSTFLG, HALTCNT
|
||||
case 0x04000300: regs.postboot |= byte >> 0; return;
|
||||
case 0x04000301: regs.mode = byte & 0x80 ? Registers::Mode::Stop : Registers::Mode::Halt; return;
|
||||
|
||||
//MEMCNT_L
|
||||
//MEMCNT_H
|
||||
case 0x04000800: regs.memory.control = (regs.memory.control & 0xffffff00) | (byte << 0); return;
|
||||
case 0x04000801: regs.memory.control = (regs.memory.control & 0xffff00ff) | (byte << 8); return;
|
||||
case 0x04000802: regs.memory.control = (regs.memory.control & 0xff00ffff) | (byte << 16); return;
|
||||
case 0x04000803: regs.memory.control = (regs.memory.control & 0x00ffffff) | (byte << 24); return;
|
||||
|
||||
}
|
||||
}
|
@@ -1,244 +0,0 @@
|
||||
CPU::Registers::DMAControl::operator uint16() const {
|
||||
return (
|
||||
(targetmode << 5)
|
||||
| (sourcemode << 7)
|
||||
| (repeat << 9)
|
||||
| (size << 10)
|
||||
| (drq << 11)
|
||||
| (timingmode << 12)
|
||||
| (irq << 14)
|
||||
| (enable << 15)
|
||||
);
|
||||
}
|
||||
|
||||
auto CPU::Registers::DMAControl::operator=(uint16 source) -> uint16 {
|
||||
targetmode = source >> 5;
|
||||
sourcemode = source >> 7;
|
||||
repeat = source >> 9;
|
||||
size = source >> 10;
|
||||
drq = source >> 11;
|
||||
timingmode = source >> 12;
|
||||
irq = source >> 14;
|
||||
enable = source >> 15;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::TimerControl::operator uint16() const {
|
||||
return (
|
||||
(frequency << 0)
|
||||
| (cascade << 2)
|
||||
| (irq << 6)
|
||||
| (enable << 7)
|
||||
);
|
||||
}
|
||||
|
||||
auto CPU::Registers::TimerControl::operator=(uint16 source) -> uint16 {
|
||||
frequency = source >> 0;
|
||||
cascade = source >> 2;
|
||||
irq = source >> 6;
|
||||
enable = source >> 7;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::SerialControl::operator uint16() const {
|
||||
return (
|
||||
(shiftclockselect << 0)
|
||||
| (shiftclockfrequency << 1)
|
||||
| (transferenablereceive << 2)
|
||||
| (transferenablesend << 3)
|
||||
| (startbit << 7)
|
||||
| (transferlength << 12)
|
||||
| (irqenable << 14)
|
||||
);
|
||||
}
|
||||
|
||||
auto CPU::Registers::SerialControl::operator=(uint16 source) -> uint16 {
|
||||
shiftclockselect = source >> 0;
|
||||
shiftclockfrequency = source >> 1;
|
||||
transferenablereceive = source >> 2;
|
||||
transferenablesend = source >> 3;
|
||||
startbit = source >> 7;
|
||||
transferlength = source >> 12;
|
||||
irqenable = source >> 14;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::KeypadControl::operator uint16() const {
|
||||
return (
|
||||
(flag[0] << 0)
|
||||
| (flag[1] << 1)
|
||||
| (flag[2] << 2)
|
||||
| (flag[3] << 3)
|
||||
| (flag[4] << 4)
|
||||
| (flag[5] << 5)
|
||||
| (flag[6] << 6)
|
||||
| (flag[7] << 7)
|
||||
| (flag[8] << 8)
|
||||
| (flag[9] << 9)
|
||||
| (enable << 14)
|
||||
| (condition << 15)
|
||||
);
|
||||
}
|
||||
|
||||
auto CPU::Registers::KeypadControl::operator=(uint16 source) -> uint16 {
|
||||
flag[0] = source >> 0;
|
||||
flag[1] = source >> 1;
|
||||
flag[2] = source >> 2;
|
||||
flag[3] = source >> 3;
|
||||
flag[4] = source >> 4;
|
||||
flag[5] = source >> 5;
|
||||
flag[6] = source >> 6;
|
||||
flag[7] = source >> 7;
|
||||
flag[8] = source >> 8;
|
||||
flag[9] = source >> 9;
|
||||
enable = source >> 14;
|
||||
condition = source >> 15;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::JoybusSettings::operator uint16() const {
|
||||
return (
|
||||
(sc << 0)
|
||||
| (sd << 1)
|
||||
| (si << 2)
|
||||
| (so << 3)
|
||||
| (scmode << 4)
|
||||
| (sdmode << 5)
|
||||
| (simode << 6)
|
||||
| (somode << 7)
|
||||
| (irqenable << 8)
|
||||
| (mode << 14)
|
||||
);
|
||||
}
|
||||
|
||||
auto CPU::Registers::JoybusSettings::operator=(uint16 source) -> uint16 {
|
||||
sc = source >> 0;
|
||||
sd = source >> 1;
|
||||
si = source >> 2;
|
||||
so = source >> 3;
|
||||
scmode = source >> 4;
|
||||
sdmode = source >> 5;
|
||||
simode = source >> 6;
|
||||
somode = source >> 7;
|
||||
irqenable = source >> 8;
|
||||
mode = source >> 14;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::JoybusControl::operator uint16() const {
|
||||
return (
|
||||
(resetsignal << 0)
|
||||
| (receivecomplete << 1)
|
||||
| (sendcomplete << 2)
|
||||
| (irqenable << 6)
|
||||
);
|
||||
}
|
||||
|
||||
auto CPU::Registers::JoybusControl::operator=(uint16 source) -> uint16 {
|
||||
resetsignal = source >> 0;
|
||||
receivecomplete = source >> 1;
|
||||
sendcomplete = source >> 2;
|
||||
irqenable = source >> 6;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::JoybusStatus::operator uint16() const {
|
||||
return (
|
||||
(receiveflag << 1)
|
||||
| (sendflag << 3)
|
||||
| (generalflag << 4)
|
||||
);
|
||||
}
|
||||
|
||||
auto CPU::Registers::JoybusStatus::operator=(uint16 source) -> uint16 {
|
||||
receiveflag = source >> 1;
|
||||
sendflag = source >> 3;
|
||||
generalflag = source >> 4;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::Interrupt::operator uint16() const {
|
||||
return (
|
||||
(vblank << 0)
|
||||
| (hblank << 1)
|
||||
| (vcoincidence << 2)
|
||||
| (timer[0] << 3)
|
||||
| (timer[1] << 4)
|
||||
| (timer[2] << 5)
|
||||
| (timer[3] << 6)
|
||||
| (serial << 7)
|
||||
| (dma[0] << 8)
|
||||
| (dma[1] << 9)
|
||||
| (dma[2] << 10)
|
||||
| (dma[3] << 11)
|
||||
| (keypad << 12)
|
||||
| (cartridge << 13)
|
||||
);
|
||||
}
|
||||
|
||||
auto CPU::Registers::Interrupt::operator=(uint16 source) -> uint16 {
|
||||
vblank = source >> 0;
|
||||
hblank = source >> 1;
|
||||
vcoincidence = source >> 2;
|
||||
timer[0] = source >> 3;
|
||||
timer[1] = source >> 4;
|
||||
timer[2] = source >> 5;
|
||||
timer[3] = source >> 6;
|
||||
serial = source >> 7;
|
||||
dma[0] = source >> 8;
|
||||
dma[1] = source >> 9;
|
||||
dma[2] = source >> 10;
|
||||
dma[3] = source >> 11;
|
||||
keypad = source >> 12;
|
||||
cartridge = source >> 13;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::WaitControl::operator uint16() const {
|
||||
return (
|
||||
(nwait[3] << 0)
|
||||
| (nwait[0] << 2)
|
||||
| (swait[0] << 4)
|
||||
| (nwait[1] << 5)
|
||||
| (swait[1] << 7)
|
||||
| (nwait[2] << 8)
|
||||
| (swait[2] << 10)
|
||||
| (phi << 11)
|
||||
| (prefetch << 14)
|
||||
| (gametype << 15)
|
||||
);
|
||||
}
|
||||
|
||||
auto CPU::Registers::WaitControl::operator=(uint16 source) -> uint16 {
|
||||
nwait[3] = (source >> 0) & 3;
|
||||
nwait[0] = (source >> 2) & 3;
|
||||
swait[0] = (source >> 4) & 1;
|
||||
nwait[1] = (source >> 5) & 3;
|
||||
swait[1] = (source >> 7) & 1;
|
||||
nwait[2] = (source >> 8) & 3;
|
||||
swait[2] = (source >> 10) & 1;
|
||||
phi = (source >> 11) & 3;
|
||||
prefetch = (source >> 14) & 1;
|
||||
gametype = (source >> 15) & 1;
|
||||
swait[3] = nwait[3];
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::MemoryControl::operator uint32() const {
|
||||
return (
|
||||
(disable << 0)
|
||||
| (unknown1 << 1)
|
||||
| (ewram << 5)
|
||||
| (ewramwait << 24)
|
||||
| (unknown2 << 28)
|
||||
);
|
||||
}
|
||||
|
||||
auto CPU::Registers::MemoryControl::operator=(uint32 source) -> uint32 {
|
||||
disable = source >> 0;
|
||||
unknown1 = source >> 1;
|
||||
ewram = source >> 5;
|
||||
ewramwait = source >> 24;
|
||||
unknown2 = source >> 28;
|
||||
return operator uint32();
|
||||
}
|
@@ -1,188 +0,0 @@
|
||||
struct Registers {
|
||||
struct DMAControl {
|
||||
uint2 targetmode;
|
||||
uint2 sourcemode;
|
||||
uint1 repeat;
|
||||
uint1 size;
|
||||
uint1 drq;
|
||||
uint2 timingmode;
|
||||
uint1 irq;
|
||||
uint1 enable;
|
||||
|
||||
operator uint16() const;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const DMAControl&) -> DMAControl& = delete;
|
||||
};
|
||||
|
||||
struct DMA {
|
||||
varuint source;
|
||||
varuint target;
|
||||
varuint length;
|
||||
uint32 data;
|
||||
DMAControl control;
|
||||
|
||||
//internal
|
||||
bool pending;
|
||||
struct Run {
|
||||
varuint target;
|
||||
varuint source;
|
||||
varuint length;
|
||||
} run;
|
||||
} dma[4];
|
||||
|
||||
struct TimerControl {
|
||||
uint2 frequency;
|
||||
uint1 cascade;
|
||||
uint1 irq;
|
||||
uint1 enable;
|
||||
|
||||
operator uint16() const;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const TimerControl&) -> TimerControl& = delete;
|
||||
};
|
||||
|
||||
struct Timer {
|
||||
uint16 period;
|
||||
uint16 reload;
|
||||
bool pending;
|
||||
TimerControl control;
|
||||
} timer[4];
|
||||
|
||||
struct SerialControl {
|
||||
uint1 shiftclockselect;
|
||||
uint1 shiftclockfrequency;
|
||||
uint1 transferenablereceive;
|
||||
uint1 transferenablesend;
|
||||
uint1 startbit;
|
||||
uint1 transferlength;
|
||||
uint1 irqenable;
|
||||
|
||||
operator uint16() const;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const SerialControl&) -> SerialControl& = delete;
|
||||
};
|
||||
|
||||
struct Serial {
|
||||
uint16 data[4];
|
||||
SerialControl control;
|
||||
uint8 data8;
|
||||
} serial;
|
||||
|
||||
struct KeypadControl {
|
||||
uint1 flag[10];
|
||||
uint1 enable;
|
||||
uint1 condition;
|
||||
|
||||
operator uint16() const;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const KeypadControl&) -> KeypadControl& = delete;
|
||||
};
|
||||
|
||||
struct Keypad {
|
||||
KeypadControl control;
|
||||
} keypad;
|
||||
|
||||
struct JoybusSettings {
|
||||
uint1 sc;
|
||||
uint1 sd;
|
||||
uint1 si;
|
||||
uint1 so;
|
||||
uint1 scmode;
|
||||
uint1 sdmode;
|
||||
uint1 simode;
|
||||
uint1 somode;
|
||||
uint1 irqenable;
|
||||
uint2 mode;
|
||||
|
||||
operator uint16() const;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const JoybusSettings&) -> JoybusSettings& = delete;
|
||||
};
|
||||
|
||||
struct JoybusControl {
|
||||
uint1 resetsignal;
|
||||
uint1 receivecomplete;
|
||||
uint1 sendcomplete;
|
||||
uint1 irqenable;
|
||||
|
||||
operator uint16() const;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const JoybusControl&) -> JoybusControl& = delete;
|
||||
};
|
||||
|
||||
struct JoybusStatus {
|
||||
uint1 receiveflag;
|
||||
uint1 sendflag;
|
||||
uint2 generalflag;
|
||||
|
||||
operator uint16() const;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const JoybusStatus&) -> JoybusStatus& = delete;
|
||||
};
|
||||
|
||||
struct Joybus {
|
||||
JoybusSettings settings;
|
||||
JoybusControl control;
|
||||
uint32 receive;
|
||||
uint32 transmit;
|
||||
JoybusStatus status;
|
||||
} joybus;
|
||||
|
||||
uint1 ime;
|
||||
|
||||
struct Interrupt {
|
||||
uint1 vblank;
|
||||
uint1 hblank;
|
||||
uint1 vcoincidence;
|
||||
uint1 timer[4];
|
||||
uint1 serial;
|
||||
uint1 dma[4];
|
||||
uint1 keypad;
|
||||
uint1 cartridge;
|
||||
|
||||
operator uint16() const;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const Interrupt&) -> Interrupt& = delete;
|
||||
};
|
||||
|
||||
struct IRQ {
|
||||
Interrupt enable;
|
||||
Interrupt flag;
|
||||
} irq;
|
||||
|
||||
struct WaitControl {
|
||||
uint2 nwait[4];
|
||||
uint1 swait[4];
|
||||
uint2 phi;
|
||||
uint1 prefetch;
|
||||
uint1 gametype;
|
||||
|
||||
operator uint16() const;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const WaitControl&) -> WaitControl& = delete;
|
||||
};
|
||||
|
||||
struct Wait {
|
||||
WaitControl control;
|
||||
} wait;
|
||||
|
||||
struct MemoryControl {
|
||||
uint1 disable;
|
||||
uint3 unknown1;
|
||||
uint1 ewram;
|
||||
uint4 ewramwait;
|
||||
uint4 unknown2;
|
||||
|
||||
operator uint32() const;
|
||||
auto operator=(uint32 source) -> uint32;
|
||||
auto operator=(const MemoryControl&) -> MemoryControl& = delete;
|
||||
};
|
||||
|
||||
struct Memory {
|
||||
MemoryControl control;
|
||||
} memory;
|
||||
|
||||
uint1 postboot;
|
||||
enum class Mode : uint { Normal, Halt, Stop } mode;
|
||||
uint clock;
|
||||
} regs;
|
207
gba/ppu/mmio.cpp
207
gba/ppu/mmio.cpp
@@ -1,207 +0,0 @@
|
||||
auto PPU::read(uint32 addr) -> uint8 {
|
||||
switch(addr) {
|
||||
|
||||
//DISPCNT
|
||||
case 0x04000000: return regs.control >> 0;
|
||||
case 0x04000001: return regs.control >> 8;
|
||||
|
||||
//GRSWP
|
||||
case 0x04000002: return regs.greenswap;
|
||||
case 0x04000003: return 0u;
|
||||
|
||||
//DISPSTAT
|
||||
case 0x04000004: return regs.status >> 0;
|
||||
case 0x04000005: return regs.status >> 8;
|
||||
|
||||
//VCOUNT
|
||||
case 0x04000006: return regs.vcounter >> 0;
|
||||
case 0x04000007: return regs.vcounter >> 8;
|
||||
|
||||
//BG0CNT,BG1CNT,BG2CNT,BG3CNT
|
||||
case 0x04000008: case 0x04000009:
|
||||
case 0x0400000a: case 0x0400000b:
|
||||
case 0x0400000c: case 0x0400000d:
|
||||
case 0x0400000e: case 0x0400000f: {
|
||||
auto& bg = regs.bg[(addr >> 1) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
return bg.control >> shift;
|
||||
}
|
||||
|
||||
//WININ
|
||||
case 0x04000048: return regs.windowflags[In0];
|
||||
case 0x04000049: return regs.windowflags[In1];
|
||||
case 0x0400004a: return regs.windowflags[Out];
|
||||
case 0x0400004b: return regs.windowflags[Obj];
|
||||
|
||||
//BLTCNT
|
||||
case 0x04000050: return regs.blend.control >> 0;
|
||||
case 0x04000051: return regs.blend.control >> 8;
|
||||
|
||||
//BLDALPHA
|
||||
case 0x04000052: return regs.blend.eva;
|
||||
case 0x04000053: return regs.blend.evb;
|
||||
|
||||
//BLDY is write-only
|
||||
|
||||
}
|
||||
|
||||
return 0u;
|
||||
}
|
||||
|
||||
auto PPU::write(uint32 addr, uint8 byte) -> void {
|
||||
switch(addr) {
|
||||
|
||||
//DISPCNT
|
||||
case 0x04000000: regs.control = (regs.control & 0xff00) | (byte << 0); return;
|
||||
case 0x04000001: regs.control = (regs.control & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//GRSWP
|
||||
case 0x04000002: regs.greenswap = byte >> 0; return;
|
||||
case 0x04000003: return;
|
||||
|
||||
//DISPSTAT
|
||||
case 0x04000004:
|
||||
regs.status.irqvblank = byte >> 3;
|
||||
regs.status.irqhblank = byte >> 4;
|
||||
regs.status.irqvcoincidence = byte >> 5;
|
||||
return;
|
||||
case 0x04000005:
|
||||
regs.status.vcompare = byte;
|
||||
return;
|
||||
|
||||
//BG0CNT,BG1CNT,BG2CNT,BG3CNT
|
||||
case 0x04000008: case 0x04000009:
|
||||
case 0x0400000a: case 0x0400000b:
|
||||
case 0x0400000c: case 0x0400000d:
|
||||
case 0x0400000e: case 0x0400000f: {
|
||||
auto& bg = regs.bg[(addr >> 1) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
if(addr == 0x04000009 || addr == 0x0400000b) byte &= 0xdf; //clear affine wrap for BG0,1
|
||||
bg.control = (bg.control & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//BG0HOFS,BG1HOFS,BG2BOFS,BG3HOFS
|
||||
case 0x04000010: case 0x04000011:
|
||||
case 0x04000014: case 0x04000015:
|
||||
case 0x04000018: case 0x04000019:
|
||||
case 0x0400001c: case 0x0400001d: {
|
||||
auto& bg = regs.bg[(addr >> 2) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
bg.hoffset = (bg.hoffset & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//BG0VOFS,BG1VOFS,BG2VOFS,BG3VOFS
|
||||
case 0x04000012: case 0x04000013:
|
||||
case 0x04000016: case 0x04000017:
|
||||
case 0x0400001a: case 0x0400001b:
|
||||
case 0x0400001e: case 0x0400001f: {
|
||||
auto& bg = regs.bg[(addr >> 2) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
bg.voffset = (bg.voffset & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//BG2PA,BG3PA
|
||||
case 0x04000020: case 0x04000021:
|
||||
case 0x04000030: case 0x04000031: {
|
||||
auto& bg = regs.bg[(addr >> 4) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
bg.pa = (bg.pa & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//BG2PB,BG3PB
|
||||
case 0x04000022: case 0x04000023:
|
||||
case 0x04000032: case 0x04000033: {
|
||||
auto& bg = regs.bg[(addr >> 4) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
bg.pb = (bg.pb & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//BG2PC,BG3PC
|
||||
case 0x04000024: case 0x04000025:
|
||||
case 0x04000034: case 0x04000035: {
|
||||
auto& bg = regs.bg[(addr >> 4) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
bg.pc = (bg.pc & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//BG2PD,BG3PD
|
||||
case 0x04000026: case 0x04000027:
|
||||
case 0x04000036: case 0x04000037: {
|
||||
auto& bg = regs.bg[(addr >> 4) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
bg.pd = (bg.pd & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//BG2X_L,BG2X_H,BG3X_L,BG3X_H
|
||||
case 0x04000028: case 0x04000029: case 0x0400002a: case 0x0400002b:
|
||||
case 0x04000038: case 0x04000039: case 0x0400003a: case 0x0400003b: {
|
||||
auto& bg = regs.bg[(addr >> 4) & 3];
|
||||
unsigned shift = (addr & 3) * 8;
|
||||
bg.lx = bg.x = (bg.x & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//BG2Y_L,BG2Y_H,BG3Y_L,BG3Y_H
|
||||
case 0x0400002c: case 0x0400002d: case 0x0400002e: case 0x0400002f:
|
||||
case 0x0400003c: case 0x0400003d: case 0x0400003e: case 0x0400003f: {
|
||||
auto& bg = regs.bg[(addr >> 4) & 3];
|
||||
unsigned shift = (addr & 3) * 8;
|
||||
bg.ly = bg.y = (bg.y & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//WIN0H
|
||||
case 0x04000040: regs.window[0].x2 = byte; return;
|
||||
case 0x04000041: regs.window[0].x1 = byte; return;
|
||||
|
||||
//WIN1H
|
||||
case 0x04000042: regs.window[1].x2 = byte; return;
|
||||
case 0x04000043: regs.window[1].x1 = byte; return;
|
||||
|
||||
//WIN0V
|
||||
case 0x04000044: regs.window[0].y2 = byte; return;
|
||||
case 0x04000045: regs.window[0].y1 = byte; return;
|
||||
|
||||
//WIN1V
|
||||
case 0x04000046: regs.window[1].y2 = byte; return;
|
||||
case 0x04000047: regs.window[1].y1 = byte; return;
|
||||
|
||||
//WININ
|
||||
case 0x04000048: regs.windowflags[In0] = byte; return;
|
||||
case 0x04000049: regs.windowflags[In1] = byte; return;
|
||||
|
||||
//WINOUT
|
||||
case 0x0400004a: regs.windowflags[Out] = byte; return;
|
||||
case 0x0400004b: regs.windowflags[Obj] = byte; return;
|
||||
|
||||
//MOSAIC
|
||||
case 0x0400004c:
|
||||
regs.mosaic.bghsize = byte >> 0;
|
||||
regs.mosaic.bgvsize = byte >> 4;
|
||||
return;
|
||||
case 0x0400004d:
|
||||
regs.mosaic.objhsize = byte >> 0;
|
||||
regs.mosaic.objvsize = byte >> 4;
|
||||
return;
|
||||
|
||||
//BLDCNT
|
||||
case 0x04000050: regs.blend.control = (regs.blend.control & 0xff00) | (byte << 0); return;
|
||||
case 0x04000051: regs.blend.control = (regs.blend.control & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//BLDALPHA
|
||||
case 0x04000052: regs.blend.eva = byte & 0x1f; return;
|
||||
case 0x04000053: regs.blend.evb = byte & 0x1f; return;
|
||||
|
||||
//BLDY
|
||||
case 0x04000054: regs.blend.evy = byte & 0x1f; return;
|
||||
case 0x04000055: return;
|
||||
|
||||
}
|
||||
}
|
@@ -1,140 +0,0 @@
|
||||
PPU::Registers::Control::operator uint16() const {
|
||||
return (
|
||||
(bgmode << 0)
|
||||
| (cgbmode << 3)
|
||||
| (frame << 4)
|
||||
| (hblank << 5)
|
||||
| (objmapping << 6)
|
||||
| (forceblank << 7)
|
||||
| (enable[BG0] << 8)
|
||||
| (enable[BG1] << 9)
|
||||
| (enable[BG2] << 10)
|
||||
| (enable[BG3] << 11)
|
||||
| (enable[OBJ] << 12)
|
||||
| (enablewindow[In0] << 13)
|
||||
| (enablewindow[In1] << 14)
|
||||
| (enablewindow[Obj] << 15)
|
||||
);
|
||||
}
|
||||
|
||||
auto PPU::Registers::Control::operator=(uint16 source) -> uint16 {
|
||||
bgmode = source >> 0;
|
||||
cgbmode = source >> 3;
|
||||
frame = source >> 4;
|
||||
hblank = source >> 5;
|
||||
objmapping = source >> 6;
|
||||
forceblank = source >> 7;
|
||||
enable[BG0] = source >> 8;
|
||||
enable[BG1] = source >> 9;
|
||||
enable[BG2] = source >> 10;
|
||||
enable[BG3] = source >> 11;
|
||||
enable[OBJ] = source >> 12;
|
||||
enablewindow[In0] = source >> 13;
|
||||
enablewindow[In1] = source >> 14;
|
||||
enablewindow[Obj] = source >> 15;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
PPU::Registers::Status::operator uint16() const {
|
||||
return (
|
||||
(vblank << 0)
|
||||
| (hblank << 1)
|
||||
| (vcoincidence << 2)
|
||||
| (irqvblank << 3)
|
||||
| (irqhblank << 4)
|
||||
| (irqvcoincidence << 5)
|
||||
| (vcompare << 8)
|
||||
);
|
||||
}
|
||||
|
||||
auto PPU::Registers::Status::operator=(uint16 source) -> uint16 {
|
||||
vblank = source >> 0;
|
||||
hblank = source >> 1;
|
||||
vcoincidence = source >> 2;
|
||||
irqvblank = source >> 3;
|
||||
irqhblank = source >> 4;
|
||||
irqvcoincidence = source >> 5;
|
||||
vcompare = source >> 8;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
PPU::Registers::BackgroundControl::operator uint16() const {
|
||||
return (
|
||||
(priority << 0)
|
||||
| (characterbaseblock << 2)
|
||||
| (unused << 4)
|
||||
| (mosaic << 6)
|
||||
| (colormode << 7)
|
||||
| (screenbaseblock << 8)
|
||||
| (affinewrap << 13)
|
||||
| (screensize << 14)
|
||||
);
|
||||
}
|
||||
|
||||
auto PPU::Registers::BackgroundControl::operator=(uint16 source) -> uint16 {
|
||||
priority = source >> 0;
|
||||
characterbaseblock = source >> 2;
|
||||
unused = source >> 4;
|
||||
mosaic = source >> 6;
|
||||
colormode = source >> 7;
|
||||
screenbaseblock = source >> 8;
|
||||
affinewrap = source >> 13;
|
||||
screensize = source >> 14;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
PPU::Registers::WindowFlags::operator uint8() const {
|
||||
return (
|
||||
(enable[BG0] << 0)
|
||||
| (enable[BG1] << 1)
|
||||
| (enable[BG2] << 2)
|
||||
| (enable[BG3] << 3)
|
||||
| (enable[OBJ] << 4)
|
||||
| (enable[SFX] << 5)
|
||||
);
|
||||
}
|
||||
|
||||
auto PPU::Registers::WindowFlags::operator=(uint8 source) -> uint8 {
|
||||
enable[BG0] = source >> 0;
|
||||
enable[BG1] = source >> 1;
|
||||
enable[BG2] = source >> 2;
|
||||
enable[BG3] = source >> 3;
|
||||
enable[OBJ] = source >> 4;
|
||||
enable[SFX] = source >> 5;
|
||||
return operator uint8();
|
||||
}
|
||||
|
||||
PPU::Registers::BlendControl::operator uint16() const {
|
||||
return (
|
||||
(above[BG0] << 0)
|
||||
| (above[BG1] << 1)
|
||||
| (above[BG2] << 2)
|
||||
| (above[BG3] << 3)
|
||||
| (above[OBJ] << 4)
|
||||
| (above[SFX] << 5)
|
||||
| (mode << 6)
|
||||
| (below[BG0] << 8)
|
||||
| (below[BG1] << 9)
|
||||
| (below[BG2] << 10)
|
||||
| (below[BG3] << 11)
|
||||
| (below[OBJ] << 12)
|
||||
| (below[SFX] << 13)
|
||||
);
|
||||
}
|
||||
|
||||
auto PPU::Registers::BlendControl::operator=(uint16 source) -> uint16 {
|
||||
above[BG0] = source >> 0;
|
||||
above[BG1] = source >> 1;
|
||||
above[BG2] = source >> 2;
|
||||
above[BG3] = source >> 3;
|
||||
above[OBJ] = source >> 4;
|
||||
above[SFX] = source >> 5;
|
||||
mode = source >> 6;
|
||||
below[BG0] = source >> 8;
|
||||
below[BG1] = source >> 9;
|
||||
below[BG2] = source >> 10;
|
||||
below[BG3] = source >> 11;
|
||||
below[OBJ] = source >> 12;
|
||||
below[SFX] = source >> 13;
|
||||
return operator uint16();
|
||||
}
|
@@ -1,107 +0,0 @@
|
||||
enum : uint { OBJ = 0, BG0 = 1, BG1 = 2, BG2 = 3, BG3 = 4, SFX = 5 };
|
||||
enum : uint { In0 = 0, In1 = 1, Obj = 2, Out = 3 };
|
||||
|
||||
struct Registers {
|
||||
struct Control {
|
||||
uint3 bgmode;
|
||||
uint1 cgbmode;
|
||||
uint1 frame;
|
||||
uint1 hblank;
|
||||
uint1 objmapping;
|
||||
uint1 forceblank;
|
||||
uint1 enable[5];
|
||||
uint1 enablewindow[3];
|
||||
|
||||
operator uint16() const;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const Control&) -> Control& = delete;
|
||||
} control;
|
||||
|
||||
uint1 greenswap;
|
||||
|
||||
struct Status {
|
||||
uint1 vblank;
|
||||
uint1 hblank;
|
||||
uint1 vcoincidence;
|
||||
uint1 irqvblank;
|
||||
uint1 irqhblank;
|
||||
uint1 irqvcoincidence;
|
||||
uint8 vcompare;
|
||||
|
||||
operator uint16() const;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const Status&) -> Status& = delete;
|
||||
} status;
|
||||
|
||||
uint16 vcounter;
|
||||
|
||||
struct BackgroundControl {
|
||||
uint2 priority;
|
||||
uint2 characterbaseblock;
|
||||
uint2 unused;
|
||||
uint1 mosaic;
|
||||
uint1 colormode;
|
||||
uint5 screenbaseblock;
|
||||
uint1 affinewrap; //BG2,3 only
|
||||
uint2 screensize;
|
||||
|
||||
operator uint16() const;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const BackgroundControl&) -> BackgroundControl& = delete;
|
||||
};
|
||||
|
||||
struct Background {
|
||||
BackgroundControl control;
|
||||
uint9 hoffset;
|
||||
uint9 voffset;
|
||||
|
||||
//BG2,3 only
|
||||
int16 pa, pb, pc, pd;
|
||||
int28 x, y;
|
||||
|
||||
//internal
|
||||
int28 lx, ly;
|
||||
uint vmosaic;
|
||||
uint hmosaic;
|
||||
uint id;
|
||||
} bg[4];
|
||||
|
||||
struct WindowFlags {
|
||||
uint1 enable[6];
|
||||
|
||||
operator uint8() const;
|
||||
auto operator=(uint8 source) -> uint8;
|
||||
auto operator=(const WindowFlags&) -> WindowFlags& = delete;
|
||||
};
|
||||
|
||||
struct Window {
|
||||
uint8 x1, x2;
|
||||
uint8 y1, y2;
|
||||
} window[2];
|
||||
|
||||
WindowFlags windowflags[4];
|
||||
|
||||
struct Mosaic {
|
||||
uint4 bghsize;
|
||||
uint4 bgvsize;
|
||||
uint4 objhsize;
|
||||
uint4 objvsize;
|
||||
} mosaic;
|
||||
|
||||
struct BlendControl {
|
||||
uint1 above[6];
|
||||
uint1 below[6];
|
||||
uint2 mode;
|
||||
|
||||
operator uint16() const;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const BlendControl&) -> BlendControl& = delete;
|
||||
};
|
||||
|
||||
struct Blend {
|
||||
BlendControl control;
|
||||
uint5 eva;
|
||||
uint5 evb;
|
||||
uint5 evy;
|
||||
} blend;
|
||||
} regs;
|
@@ -1,30 +0,0 @@
|
||||
#include <gba/gba.hpp>
|
||||
|
||||
namespace GameBoyAdvance {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
Scheduler::Scheduler() {
|
||||
sync = SynchronizeMode::None;
|
||||
exit_reason = ExitReason::UnknownEvent;
|
||||
host = nullptr;
|
||||
active = nullptr;
|
||||
}
|
||||
|
||||
auto Scheduler::enter() -> void {
|
||||
host = co_active();
|
||||
co_switch(active);
|
||||
}
|
||||
|
||||
auto Scheduler::exit(ExitReason reason) -> void {
|
||||
exit_reason = reason;
|
||||
active = co_active();
|
||||
co_switch(host);
|
||||
}
|
||||
|
||||
auto Scheduler::power() -> void {
|
||||
host = co_active();
|
||||
active = cpu.thread;
|
||||
}
|
||||
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
struct Scheduler : property<Scheduler> {
|
||||
enum class SynchronizeMode : uint { None, CPU, All } sync;
|
||||
enum class ExitReason : uint { UnknownEvent, FrameEvent, SynchronizeEvent };
|
||||
readonly<ExitReason> exit_reason;
|
||||
|
||||
cothread_t host;
|
||||
cothread_t active;
|
||||
|
||||
Scheduler();
|
||||
auto enter() -> void;
|
||||
auto exit(ExitReason) -> void;
|
||||
auto power() -> void;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
@@ -1,70 +0,0 @@
|
||||
#include <gba/gba.hpp>
|
||||
|
||||
namespace GameBoyAdvance {
|
||||
|
||||
#include "bios.cpp"
|
||||
#include "serialization.cpp"
|
||||
BIOS bios;
|
||||
System system;
|
||||
|
||||
auto System::init() -> void {
|
||||
}
|
||||
|
||||
auto System::term() -> void {
|
||||
}
|
||||
|
||||
auto System::power() -> void {
|
||||
bus.power();
|
||||
player.power();
|
||||
cpu.power();
|
||||
ppu.power();
|
||||
apu.power();
|
||||
cartridge.power();
|
||||
scheduler.power();
|
||||
}
|
||||
|
||||
auto System::load() -> void {
|
||||
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
|
||||
if(auto bios = document["system/cpu/rom/name"].text()) {
|
||||
interface->loadRequest(ID::BIOS, bios, true);
|
||||
}
|
||||
|
||||
serialize_init();
|
||||
}
|
||||
|
||||
auto System::run() -> void {
|
||||
while(true) {
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) break;
|
||||
}
|
||||
interface->videoRefresh(video.palette, ppu.output, 4 * 240, 240, 160);
|
||||
}
|
||||
|
||||
auto System::runtosave() -> void {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::CPU;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.active = ppu.thread;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.active = apu.thread;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::None;
|
||||
}
|
||||
|
||||
auto System::runthreadtosave() -> void {
|
||||
while(true) {
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
|
||||
interface->videoRefresh(video.palette, ppu.output, 4 * 240, 240, 160);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,66 +0,0 @@
|
||||
#include <gba/gba.hpp>
|
||||
|
||||
namespace GameBoyAdvance {
|
||||
|
||||
Video video;
|
||||
|
||||
Video::Video() {
|
||||
palette = new uint32[1 << 15]();
|
||||
}
|
||||
|
||||
Video::~Video() {
|
||||
delete[] palette;
|
||||
}
|
||||
|
||||
auto Video::generatePalette(Emulator::Interface::PaletteMode mode) -> void {
|
||||
for(auto color : range(1 << 15)) {
|
||||
if(mode == Emulator::Interface::PaletteMode::Literal) {
|
||||
palette[color] = color;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint B = (color >> 10) & 31;
|
||||
uint G = (color >> 5) & 31;
|
||||
uint R = (color >> 0) & 31;
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Channel) {
|
||||
R = image::normalize(R, 5, 16);
|
||||
G = image::normalize(G, 5, 16);
|
||||
B = image::normalize(B, 5, 16);
|
||||
palette[color] = interface->videoColor(color, 0, R, G, B);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Standard) {
|
||||
R = image::normalize(R, 5, 16);
|
||||
G = image::normalize(G, 5, 16);
|
||||
B = image::normalize(B, 5, 16);
|
||||
palette[color] = interface->videoColor(color, 0, R, G, B);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Emulation) {
|
||||
double lcdGamma = 4.0, outGamma = 2.2;
|
||||
double lb = pow(B / 31.0, lcdGamma);
|
||||
double lg = pow(G / 31.0, lcdGamma);
|
||||
double lr = pow(R / 31.0, lcdGamma);
|
||||
B = pow((220 * lb + 10 * lg + 50 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
G = pow(( 30 * lb + 230 * lg + 10 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
R = pow(( 0 * lb + 50 * lg + 255 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
|
||||
palette[color] = interface->videoColor(color, 0, R, G, B);
|
||||
continue;
|
||||
}
|
||||
|
||||
palette[color] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const uint8 Video::curve[32] = {
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x10, 0x12,
|
||||
0x14, 0x16, 0x18, 0x1c, 0x20, 0x28, 0x38, 0x38,
|
||||
0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x80,
|
||||
0x88, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,
|
||||
};
|
||||
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
struct Video {
|
||||
Video();
|
||||
~Video();
|
||||
|
||||
auto generatePalette(Emulator::Interface::PaletteMode mode) -> void;
|
||||
|
||||
uint32* palette = nullptr;
|
||||
|
||||
private:
|
||||
static const uint8 curve[32];
|
||||
};
|
||||
|
||||
extern Video video;
|
@@ -1,16 +1,11 @@
|
||||
include nall/GNUmakefile
|
||||
include ../nall/GNUmakefile
|
||||
|
||||
fc := fc
|
||||
sfc := sfc
|
||||
gb := gb
|
||||
gba := gba
|
||||
|
||||
profile := accuracy
|
||||
target := tomoko
|
||||
# target := loki
|
||||
profile := accuracy
|
||||
# console := true
|
||||
|
||||
# compiler
|
||||
flags += -I. -O3
|
||||
flags += -I. -I.. -O3
|
||||
objects := libco
|
||||
|
||||
# profile-guided optimization mode
|
||||
@@ -36,23 +31,15 @@ ifeq ($(platform),windows)
|
||||
link += -Wl,-enable-runtime-pseudo-reloc
|
||||
else ifeq ($(platform),macosx)
|
||||
flags += -march=native
|
||||
else ifeq ($(platform),linux)
|
||||
flags += -march=native -fopenmp
|
||||
link += -fopenmp
|
||||
link += -Wl,-export-dynamic
|
||||
link += -lX11 -lXext
|
||||
else ifeq ($(platform),bsd)
|
||||
else ifneq ($(filter $(platform),linux bsd),)
|
||||
flags += -march=native -fopenmp
|
||||
link += -fopenmp
|
||||
link += -Wl,-export-dynamic
|
||||
link += -lX11 -lXext
|
||||
else
|
||||
$(error unsupported platform.)
|
||||
$(error "unsupported platform")
|
||||
endif
|
||||
|
||||
ui := target-$(target)
|
||||
|
||||
# implicit rules
|
||||
compile = \
|
||||
$(strip \
|
||||
$(if $(filter %.c,$<), \
|
||||
@@ -67,39 +54,9 @@ compile = \
|
||||
|
||||
all: build;
|
||||
|
||||
obj/libco.o: libco/libco.c libco/*
|
||||
|
||||
ui := target-$(target)
|
||||
include $(ui)/GNUmakefile
|
||||
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
|
||||
|
||||
# targets
|
||||
clean:
|
||||
-@$(call delete,out/*)
|
||||
-@$(call delete,obj/*.o)
|
||||
-@$(call delete,obj/*.a)
|
||||
-@$(call delete,obj/*.so)
|
||||
-@$(call delete,obj/*.dylib)
|
||||
-@$(call delete,obj/*.dll)
|
||||
|
||||
archive:
|
||||
if [ -f higan.tar.xz ]; then rm higan.tar.xz; fi
|
||||
tar -cJf higan.tar.xz `ls`
|
||||
|
||||
sync:
|
||||
ifeq ($(shell id -un),byuu)
|
||||
if [ -d ./libco ]; then rm -r ./libco; fi
|
||||
if [ -d ./nall ]; then rm -r ./nall; fi
|
||||
if [ -d ./ruby ]; then rm -r ./ruby; fi
|
||||
if [ -d ./hiro ]; then rm -r ./hiro; fi
|
||||
cp -r ../libco ./libco
|
||||
cp -r ../nall ./nall
|
||||
cp -r ../ruby ./ruby
|
||||
cp -r ../hiro ./hiro
|
||||
rm -r libco/doc
|
||||
rm -r libco/-test
|
||||
rm -r nall/-test
|
||||
rm -r ruby/-test
|
||||
rm -r hiro/-test
|
||||
endif
|
||||
|
||||
help:;
|
||||
-@$(call delete,obj/*)
|
@@ -1,6 +1,6 @@
|
||||
[Desktop Entry]
|
||||
Name=higan
|
||||
Comment=Nintendo emulator
|
||||
Comment=Emulator
|
||||
Exec=higan
|
||||
Icon=higan
|
||||
Terminal=false
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
@@ -2,11 +2,17 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.byuu.higan</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>higan</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>higan</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>higan.icns</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<false/>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
3
higan/emulator/GNUmakefile
Normal file
3
higan/emulator/GNUmakefile
Normal file
@@ -0,0 +1,3 @@
|
||||
objects += emulator
|
||||
|
||||
obj/emulator.o: emulator/emulator.cpp $(call rwildcard,emulator/)
|
34
higan/emulator/emulator.cpp
Normal file
34
higan/emulator/emulator.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include <emulator/emulator.hpp>
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
auto Interface::videoColor(uint16 r, uint16 g, uint16 b) -> uint32 {
|
||||
double saturation = 1.0;
|
||||
double gamma = 1.0;
|
||||
double luminance = 1.0;
|
||||
|
||||
if(saturation != 1.0) {
|
||||
uint16 grayscale = uclamp<16>((r + g + b) / 3);
|
||||
double inverse = max(0.0, 1.0 - saturation);
|
||||
r = uclamp<16>(r * saturation + grayscale * inverse);
|
||||
g = uclamp<16>(g * saturation + grayscale * inverse);
|
||||
b = uclamp<16>(b * saturation + grayscale * inverse);
|
||||
}
|
||||
|
||||
if(gamma != 1.0) {
|
||||
double reciprocal = 1.0 / 32767.0;
|
||||
r = r > 32767 ? r : uint16(32767 * pow(r * reciprocal, gamma));
|
||||
g = g > 32767 ? g : uint16(32767 * pow(g * reciprocal, gamma));
|
||||
b = b > 32767 ? b : uint16(32767 * pow(b * reciprocal, gamma));
|
||||
}
|
||||
|
||||
if(luminance != 1.0) {
|
||||
r = uclamp<16>(r * luminance);
|
||||
g = uclamp<16>(g * luminance);
|
||||
b = uclamp<16>(b * luminance);
|
||||
}
|
||||
|
||||
return 255 << 24 | (r >> 8) << 16 | (g >> 8) << 8 | (b >> 8) << 0;
|
||||
}
|
||||
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
#ifndef EMULATOR_HPP
|
||||
#define EMULATOR_HPP
|
||||
#pragma once
|
||||
|
||||
#include <nall/nall.hpp>
|
||||
#include <nall/dsp.hpp>
|
||||
@@ -7,7 +6,7 @@ using namespace nall;
|
||||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "096";
|
||||
static const string Version = "098";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
@@ -53,7 +52,3 @@ template<typename R, typename... P> struct hook<auto (P...) -> R> {
|
||||
#else
|
||||
#define privileged private
|
||||
#endif
|
||||
|
||||
using varuint = varuint_t<uint>;
|
||||
|
||||
#endif
|
@@ -1,10 +1,10 @@
|
||||
#ifndef EMULATOR_INTERFACE_HPP
|
||||
#define EMULATOR_INTERFACE_HPP
|
||||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Interface {
|
||||
struct Information {
|
||||
string manufacturer;
|
||||
string name;
|
||||
uint width;
|
||||
uint height;
|
||||
@@ -50,8 +50,7 @@ struct Interface {
|
||||
virtual auto loadRequest(uint, string, string, bool) -> void {}
|
||||
virtual auto loadRequest(uint, string, bool) -> void {}
|
||||
virtual auto saveRequest(uint, string) -> void {}
|
||||
virtual auto videoColor(uint, uint16, uint16, uint16, uint16) -> uint32 { return 0u; }
|
||||
virtual auto videoRefresh(const uint32*, const uint32*, uint, uint, uint) -> void {}
|
||||
virtual auto videoRefresh(const uint32*, uint, uint, uint) -> void {}
|
||||
virtual auto audioSample(int16, int16) -> void {}
|
||||
virtual auto inputPoll(uint, uint, uint) -> int16 { return 0; }
|
||||
virtual auto inputRumble(uint, uint, uint, bool) -> void {}
|
||||
@@ -65,8 +64,7 @@ struct Interface {
|
||||
auto loadRequest(uint id, string name, string type, bool required) -> void { return bind->loadRequest(id, name, type, required); }
|
||||
auto loadRequest(uint id, string path, bool required) -> void { return bind->loadRequest(id, path, required); }
|
||||
auto saveRequest(uint id, string path) -> void { return bind->saveRequest(id, path); }
|
||||
auto videoColor(uint source, uint16 alpha, uint16 red, uint16 green, uint16 blue) -> uint32 { return bind->videoColor(source, alpha, red, green, blue); }
|
||||
auto videoRefresh(const uint32* palette, const uint32* data, uint pitch, uint width, uint height) -> void { return bind->videoRefresh(palette, data, pitch, width, height); }
|
||||
auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void { return bind->videoRefresh(data, pitch, width, height); }
|
||||
auto audioSample(int16 lsample, int16 rsample) -> void { return bind->audioSample(lsample, rsample); }
|
||||
auto inputPoll(uint port, uint device, uint input) -> int16 { return bind->inputPoll(port, device, input); }
|
||||
auto inputRumble(uint port, uint device, uint input, bool enable) -> void { return bind->inputRumble(port, device, input, enable); }
|
||||
@@ -107,11 +105,13 @@ struct Interface {
|
||||
//cheat functions
|
||||
virtual auto cheatSet(const lstring& = lstring{}) -> void {}
|
||||
|
||||
//utility functions
|
||||
enum class PaletteMode : uint { Literal, Channel, Standard, Emulation };
|
||||
virtual auto paletteUpdate(PaletteMode mode) -> void {}
|
||||
//settings
|
||||
virtual auto cap(const string& name) -> bool { return false; }
|
||||
virtual auto get(const string& name) -> any { return {}; }
|
||||
virtual auto set(const string& name, const any& value) -> bool { return false; }
|
||||
|
||||
//shared functions
|
||||
auto videoColor(uint16 r, uint16 g, uint16 b) -> uint32;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
16
higan/fc/GNUmakefile
Normal file
16
higan/fc/GNUmakefile
Normal file
@@ -0,0 +1,16 @@
|
||||
processors += r6502
|
||||
|
||||
objects += fc-interface fc-system fc-scheduler fc-input
|
||||
objects += fc-memory fc-cartridge fc-cpu fc-apu fc-ppu
|
||||
objects += fc-cheat
|
||||
|
||||
obj/fc-interface.o: fc/interface/interface.cpp $(call rwildcard,fc/interface/)
|
||||
obj/fc-system.o: fc/system/system.cpp $(call rwildcard,fc/system/)
|
||||
obj/fc-scheduler.o: fc/scheduler/scheduler.cpp $(call rwildcard,fc/scheduler/)
|
||||
obj/fc-input.o: fc/input/input.cpp $(call rwildcard,fc/input/)
|
||||
obj/fc-memory.o: fc/memory/memory.cpp $(call rwildcard,fc/memory/)
|
||||
obj/fc-cartridge.o: fc/cartridge/cartridge.cpp $(call rwildcard,fc/cartridge/)
|
||||
obj/fc-cpu.o: fc/cpu/cpu.cpp $(call rwildcard,fc/cpu/)
|
||||
obj/fc-apu.o: fc/apu/apu.cpp $(call rwildcard,fc/apu/)
|
||||
obj/fc-ppu.o: fc/ppu/ppu.cpp $(call rwildcard,fc/ppu/)
|
||||
obj/fc-cheat.o: fc/cheat/cheat.cpp $(call rwildcard,fc/cheat/)
|
@@ -34,43 +34,37 @@ APU::APU() {
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Main() -> void {
|
||||
apu.main();
|
||||
auto APU::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), apu.main();
|
||||
}
|
||||
|
||||
auto APU::main() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
uint pulse_output, triangle_output, noise_output, dmc_output;
|
||||
|
||||
uint pulse_output, triangle_output, noise_output, dmc_output;
|
||||
pulse_output = pulse[0].clock();
|
||||
pulse_output += pulse[1].clock();
|
||||
triangle_output = triangle.clock();
|
||||
noise_output = noise.clock();
|
||||
dmc_output = dmc.clock();
|
||||
|
||||
pulse_output = pulse[0].clock();
|
||||
pulse_output += pulse[1].clock();
|
||||
triangle_output = triangle.clock();
|
||||
noise_output = noise.clock();
|
||||
dmc_output = dmc.clock();
|
||||
clock_frame_counter_divider();
|
||||
|
||||
clock_frame_counter_divider();
|
||||
int output = pulse_dac[pulse_output] + dmc_triangle_noise_dac[dmc_output][triangle_output][noise_output];
|
||||
|
||||
int output = pulse_dac[pulse_output] + dmc_triangle_noise_dac[dmc_output][triangle_output][noise_output];
|
||||
output = filter.run_hipass_strong(output);
|
||||
output += cartridge_sample;
|
||||
output = filter.run_hipass_weak(output);
|
||||
//output = filter.run_lopass(output);
|
||||
output = sclamp<16>(output);
|
||||
|
||||
output = filter.run_hipass_strong(output);
|
||||
output += cartridge_sample;
|
||||
output = filter.run_hipass_weak(output);
|
||||
//output = filter.run_lopass(output);
|
||||
output = sclamp<16>(output);
|
||||
interface->audioSample(output, output);
|
||||
|
||||
interface->audioSample(output, output);
|
||||
|
||||
tick();
|
||||
}
|
||||
tick();
|
||||
}
|
||||
|
||||
auto APU::tick() -> void {
|
||||
clock += 12;
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
}
|
||||
|
||||
auto APU::set_irq_line() -> void {
|
||||
@@ -94,7 +88,7 @@ auto APU::power() -> void {
|
||||
}
|
||||
|
||||
auto APU::reset() -> void {
|
||||
create(APU::Main, 21477272);
|
||||
create(APU::Enter, 21'477'272);
|
||||
|
||||
pulse[0].reset();
|
||||
pulse[1].reset();
|
@@ -1,7 +1,7 @@
|
||||
struct APU : Thread {
|
||||
APU();
|
||||
|
||||
static auto Main() -> void;
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto tick() -> void;
|
||||
auto set_irq_line() -> void;
|
@@ -5,20 +5,14 @@ struct BandaiFCG : Board {
|
||||
}
|
||||
|
||||
auto main() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
if(irq_counter_enable) {
|
||||
if(--irq_counter == 0xffff) {
|
||||
cpu.set_irq_line(1);
|
||||
irq_counter_enable = false;
|
||||
}
|
||||
|
||||
if(irq_counter_enable) {
|
||||
if(--irq_counter == 0xffff) {
|
||||
cpu.set_irq_line(1);
|
||||
irq_counter_enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
tick();
|
||||
}
|
||||
|
||||
tick();
|
||||
}
|
||||
|
||||
auto ciram_addr(uint addr) const -> uint {
|
||||
@@ -33,7 +27,7 @@ struct BandaiFCG : Board {
|
||||
auto prg_read(uint addr) -> uint8 {
|
||||
if(addr & 0x8000) {
|
||||
bool region = addr & 0x4000;
|
||||
uint bank = (region == 0 ? prg_bank : 0x0f);
|
||||
uint bank = (region == 0 ? prg_bank : (uint8)0x0f);
|
||||
return prgrom.read((bank << 14) | (addr & 0x3fff));
|
||||
}
|
||||
return cpu.mdr();
|
@@ -36,10 +36,10 @@ Board::Board(Markup::Node& document) {
|
||||
chrrom.size = crom["size"].natural();
|
||||
chrram.size = cram["size"].natural();
|
||||
|
||||
if(prgrom.size) prgrom.data = new uint8[prgrom.size]();
|
||||
if(prgram.size) prgram.data = new uint8[prgram.size]();
|
||||
if(chrrom.size) chrrom.data = new uint8[chrrom.size]();
|
||||
if(chrram.size) chrram.data = new uint8[chrram.size]();
|
||||
if(prgrom.size) prgrom.data = new uint8_t[prgrom.size]();
|
||||
if(prgram.size) prgram.data = new uint8_t[prgram.size]();
|
||||
if(chrrom.size) chrrom.data = new uint8_t[chrrom.size]();
|
||||
if(chrram.size) chrram.data = new uint8_t[chrram.size]();
|
||||
|
||||
if(auto name = prom["name"].text()) interface->loadRequest(ID::ProgramROM, name, true);
|
||||
if(auto name = pram["name"].text()) interface->loadRequest(ID::ProgramRAM, name, false);
|
||||
@@ -80,19 +80,13 @@ auto Board::mirror(uint addr, uint size) -> uint {
|
||||
}
|
||||
|
||||
auto Board::main() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
cartridge.clock += 12 * 4095;
|
||||
tick();
|
||||
}
|
||||
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);
|
||||
if(cartridge.clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
}
|
||||
|
||||
auto Board::chr_read(uint addr) -> uint8 {
|
@@ -1,7 +1,7 @@
|
||||
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();
|
||||
settings.pinout.a0 = 1 << document["board/chip/pinout/a0"].natural();
|
||||
settings.pinout.a1 = 1 << document["board/chip/pinout/a1"].natural();
|
||||
}
|
||||
|
||||
auto prg_read(uint addr) -> uint8 {
|
@@ -1,6 +1,6 @@
|
||||
struct KonamiVRC3 : Board {
|
||||
KonamiVRC3(Markup::Node& document) : Board(document), vrc3(*this) {
|
||||
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||
settings.mirror = document["board/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||
}
|
||||
|
||||
auto main() -> void {
|
@@ -1,7 +1,7 @@
|
||||
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();
|
||||
settings.pinout.a0 = 1 << document["board/chip/pinout/a0"].natural();
|
||||
settings.pinout.a1 = 1 << document["board/chip/pinout/a1"].natural();
|
||||
}
|
||||
|
||||
auto main() -> void {
|
@@ -2,7 +2,7 @@
|
||||
|
||||
struct NES_BNROM : Board {
|
||||
NES_BNROM(Markup::Node& document) : Board(document) {
|
||||
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||
settings.mirror = document["board/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||
}
|
||||
|
||||
auto prg_read(uint addr) -> uint8 {
|
@@ -2,7 +2,7 @@
|
||||
|
||||
struct NES_CNROM : Board {
|
||||
NES_CNROM(Markup::Node& document) : Board(document) {
|
||||
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||
settings.mirror = document["board/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||
}
|
||||
|
||||
auto prg_read(uint addr) -> uint8 {
|
@@ -3,7 +3,7 @@
|
||||
|
||||
struct NES_GxROM : Board {
|
||||
NES_GxROM(Markup::Node& document) : Board(document) {
|
||||
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||
settings.mirror = document["board/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||
}
|
||||
|
||||
auto prg_read(uint addr) -> uint8 {
|
@@ -3,7 +3,7 @@
|
||||
|
||||
struct NES_NROM : Board {
|
||||
NES_NROM(Markup::Node& document) : Board(document) {
|
||||
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||
settings.mirror = document["board/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||
}
|
||||
|
||||
auto prg_read(uint addr) -> uint8 {
|
@@ -3,7 +3,7 @@
|
||||
|
||||
struct NES_UxROM : Board {
|
||||
NES_UxROM(Markup::Node& document) : Board(document) {
|
||||
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||
settings.mirror = document["board/mirror/mode"].text() == "vertical" ? 1 : 0;
|
||||
}
|
||||
|
||||
auto prg_read(uint addr) -> uint8 {
|
@@ -44,25 +44,19 @@ struct Sunsoft5B : Board {
|
||||
} pulse[3];
|
||||
|
||||
auto main() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
if(irq_counter_enable) {
|
||||
if(--irq_counter == 0xffff) {
|
||||
cpu.set_irq_line(irq_enable);
|
||||
}
|
||||
|
||||
if(irq_counter_enable) {
|
||||
if(--irq_counter == 0xffff) {
|
||||
cpu.set_irq_line(irq_enable);
|
||||
}
|
||||
}
|
||||
|
||||
pulse[0].clock();
|
||||
pulse[1].clock();
|
||||
pulse[2].clock();
|
||||
int16 output = dac[pulse[0].output] + dac[pulse[1].output] + dac[pulse[2].output];
|
||||
apu.set_sample(-output);
|
||||
|
||||
tick();
|
||||
}
|
||||
|
||||
pulse[0].clock();
|
||||
pulse[1].clock();
|
||||
pulse[2].clock();
|
||||
int16 output = dac[pulse[0].output] + dac[pulse[1].output] + dac[pulse[2].output];
|
||||
apu.set_sample(-output);
|
||||
|
||||
tick();
|
||||
}
|
||||
|
||||
auto prg_read(uint addr) -> uint8 {
|
@@ -6,10 +6,6 @@ namespace Famicom {
|
||||
#include "board/board.cpp"
|
||||
Cartridge cartridge;
|
||||
|
||||
auto Cartridge::loaded() const -> bool {
|
||||
return _loaded;
|
||||
}
|
||||
|
||||
auto Cartridge::sha256() const -> string {
|
||||
return _sha256;
|
||||
}
|
||||
@@ -22,8 +18,8 @@ auto Cartridge::title() const -> string {
|
||||
return information.title;
|
||||
}
|
||||
|
||||
auto Cartridge::Main() -> void {
|
||||
cartridge.main();
|
||||
auto Cartridge::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), cartridge.main();
|
||||
}
|
||||
|
||||
auto Cartridge::main() -> void {
|
||||
@@ -40,14 +36,9 @@ auto Cartridge::load() -> void {
|
||||
sha.data(board->prgrom.data, board->prgrom.size);
|
||||
sha.data(board->chrrom.data, board->chrrom.size);
|
||||
_sha256 = sha.digest();
|
||||
|
||||
system.load();
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
auto Cartridge::unload() -> void {
|
||||
if(!loaded()) return;
|
||||
_loaded = false;
|
||||
memory.reset();
|
||||
}
|
||||
|
||||
@@ -56,7 +47,7 @@ auto Cartridge::power() -> void {
|
||||
}
|
||||
|
||||
auto Cartridge::reset() -> void {
|
||||
create(Cartridge::Main, 21477272);
|
||||
create(Cartridge::Enter, 21'477'272);
|
||||
board->reset();
|
||||
}
|
||||
|
@@ -2,10 +2,9 @@
|
||||
#include "board/board.hpp"
|
||||
|
||||
struct Cartridge : Thread {
|
||||
static auto Main() -> void;
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
|
||||
auto loaded() const -> bool;
|
||||
auto sha256() const -> string;
|
||||
auto manifest() const -> string;
|
||||
auto title() const -> string;
|
||||
@@ -31,7 +30,6 @@ struct Cartridge : Thread {
|
||||
|
||||
//privileged:
|
||||
Board* board = nullptr;
|
||||
bool _loaded = false;
|
||||
string _sha256;
|
||||
|
||||
auto prg_read(uint addr) -> uint8;
|
@@ -4,14 +4,8 @@ struct MMC1 : Chip {
|
||||
}
|
||||
|
||||
auto main() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(writedelay) writedelay--;
|
||||
tick();
|
||||
}
|
||||
if(writedelay) writedelay--;
|
||||
tick();
|
||||
}
|
||||
|
||||
auto prg_addr(uint addr) -> uint {
|
@@ -3,15 +3,9 @@ struct MMC3 : Chip {
|
||||
}
|
||||
|
||||
auto main() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_delay) irq_delay--;
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
}
|
||||
if(irq_delay) irq_delay--;
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto irq_test(uint addr) -> void {
|
@@ -4,17 +4,11 @@ struct MMC5 : Chip {
|
||||
}
|
||||
|
||||
auto main() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
//scanline() resets this; if no scanlines detected, enter video blanking period
|
||||
if(++cpu_cycle_counter >= 200) blank(); //113-114 normal; ~2500 across Vblank period
|
||||
|
||||
//scanline() resets this; if no scanlines detected, enter video blanking period
|
||||
if(++cpu_cycle_counter >= 200) blank(); //113-114 normal; ~2500 across Vblank period
|
||||
|
||||
cpu.set_irq_line(irq_enable && irq_pending);
|
||||
tick();
|
||||
}
|
||||
cpu.set_irq_line(irq_enable && irq_pending);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto scanline(uint y) -> void {
|
||||
@@ -94,7 +88,7 @@ struct MMC5 : Chip {
|
||||
auto prg_write(uint addr, uint8 data) -> void {
|
||||
if((addr & 0xfc00) == 0x5c00) {
|
||||
//writes 0x00 *during* Vblank (not during screen rendering ...)
|
||||
if(exram_mode == 0 || exram_mode == 1) exram[addr & 0x03ff] = in_frame ? data : 0x00;
|
||||
if(exram_mode == 0 || exram_mode == 1) exram[addr & 0x03ff] = in_frame ? data : (uint8)0x00;
|
||||
if(exram_mode == 2) exram[addr & 0x03ff] = data;
|
||||
return;
|
||||
}
|
||||
@@ -277,7 +271,7 @@ struct MMC5 : Chip {
|
||||
switch(nametable_mode[(addr >> 10) & 3]) {
|
||||
case 0: return ppu.ciram_read(0x0000 | (addr & 0x03ff));
|
||||
case 1: return ppu.ciram_read(0x0400 | (addr & 0x03ff));
|
||||
case 2: return exram_mode < 2 ? exram[addr & 0x03ff] : 0x00;
|
||||
case 2: return exram_mode < 2 ? exram[addr & 0x03ff] : (uint8)0x00;
|
||||
case 3: return (hcounter & 2) == 0 ? fillmode_tile : fillmode_color;
|
||||
}
|
||||
}
|
@@ -3,15 +3,9 @@ struct MMC6 : Chip {
|
||||
}
|
||||
|
||||
auto main() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_delay) irq_delay--;
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
}
|
||||
if(irq_delay) irq_delay--;
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto irq_test(uint addr) -> void {
|
@@ -3,31 +3,25 @@ struct VRC3 : Chip {
|
||||
}
|
||||
|
||||
auto main() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_enable) {
|
||||
if(irq_mode == 0) { //16-bit
|
||||
if(++irq_counter.w == 0) {
|
||||
irq_line = 1;
|
||||
irq_enable = irq_acknowledge;
|
||||
irq_counter.w = irq_latch;
|
||||
}
|
||||
}
|
||||
if(irq_mode == 1) { //8-bit
|
||||
if(++irq_counter.l == 0) {
|
||||
irq_line = 1;
|
||||
irq_enable = irq_acknowledge;
|
||||
irq_counter.l = irq_latch;
|
||||
}
|
||||
if(irq_enable) {
|
||||
if(irq_mode == 0) { //16-bit
|
||||
if(++irq_counter.w == 0) {
|
||||
irq_line = 1;
|
||||
irq_enable = irq_acknowledge;
|
||||
irq_counter.w = irq_latch;
|
||||
}
|
||||
}
|
||||
if(irq_mode == 1) { //8-bit
|
||||
if(++irq_counter.l == 0) {
|
||||
irq_line = 1;
|
||||
irq_enable = irq_acknowledge;
|
||||
irq_counter.l = irq_latch;
|
||||
}
|
||||
}
|
||||
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
}
|
||||
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto prg_addr(uint addr) const -> uint {
|
||||
@@ -90,8 +84,8 @@ struct VRC3 : Chip {
|
||||
uint16 irq_latch;
|
||||
struct {
|
||||
union {
|
||||
uint16 w;
|
||||
struct { uint8 order_lsb2(l, h); };
|
||||
uint16_t w;
|
||||
struct { uint8_t order_lsb2(l, h); };
|
||||
};
|
||||
} irq_counter;
|
||||
bool irq_line;
|
@@ -3,26 +3,11 @@ struct VRC4 : Chip {
|
||||
}
|
||||
|
||||
auto main() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_enable) {
|
||||
if(irq_mode == 0) {
|
||||
irq_scalar -= 3;
|
||||
if(irq_scalar <= 0) {
|
||||
irq_scalar += 341;
|
||||
if(irq_counter == 0xff) {
|
||||
irq_counter = irq_latch;
|
||||
irq_line = 1;
|
||||
} else {
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(irq_mode == 1) {
|
||||
if(irq_enable) {
|
||||
if(irq_mode == 0) {
|
||||
irq_scalar -= 3;
|
||||
if(irq_scalar <= 0) {
|
||||
irq_scalar += 341;
|
||||
if(irq_counter == 0xff) {
|
||||
irq_counter = irq_latch;
|
||||
irq_line = 1;
|
||||
@@ -32,9 +17,18 @@ struct VRC4 : Chip {
|
||||
}
|
||||
}
|
||||
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
if(irq_mode == 1) {
|
||||
if(irq_counter == 0xff) {
|
||||
irq_counter = irq_latch;
|
||||
irq_line = 1;
|
||||
} else {
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto prg_addr(uint addr) const -> uint {
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user