mirror of https://github.com/bsnes-emu/bsnes.git
Update to v100r03 release.
byuu says: Changelog: - moved Thread, Scheduler, Cheat functionality into emulator/ for all cores - start of actual Mega Drive emulation (two 68K instructions) I'm going to be rather terse on MD emulation, as it's too early for any meaningful dialogue here.
This commit is contained in:
parent
3dd1aa9c1b
commit
76a8ecd32a
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Cheat {
|
||||
struct Code {
|
||||
uint addr;
|
||||
uint data;
|
||||
maybe<uint> comp;
|
||||
};
|
||||
|
||||
explicit operator bool() const {
|
||||
return codes.size() > 0;
|
||||
}
|
||||
|
||||
auto reset() -> void {
|
||||
codes.reset();
|
||||
}
|
||||
|
||||
auto append(uint addr, uint data, maybe<uint> comp = nothing) -> void {
|
||||
codes.append({addr, data, comp});
|
||||
}
|
||||
|
||||
auto assign(const string_vector& list) -> void {
|
||||
reset();
|
||||
for(auto& entry : list) {
|
||||
for(auto code : entry.split("+")) {
|
||||
auto part = code.split("/");
|
||||
if(part.size() == 2) append(part[0].hex(), part[1].hex());
|
||||
if(part.size() == 3) append(part[0].hex(), part[2].hex(), part[1].hex());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto find(uint addr, uint comp) -> maybe<uint> {
|
||||
for(auto& code : codes) {
|
||||
if(code.addr == addr && (!code.comp || code.comp() == comp)) {
|
||||
return code.data;
|
||||
}
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
private:
|
||||
vector<Code> codes;
|
||||
};
|
||||
|
||||
}
|
|
@ -11,13 +11,20 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "100.02";
|
||||
static const string Version = "100.03";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
||||
//incremented only when serialization format changes
|
||||
static const string SerializerVersion = "100";
|
||||
|
||||
namespace Constants {
|
||||
namespace Colorburst {
|
||||
static constexpr double NTSC = 315.0 / 88.0 * 1'000'000.0;
|
||||
static constexpr double PAL = 283.75 * 15'625.0 + 25.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "interface.hpp"
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Scheduler {
|
||||
enum class Mode : uint {
|
||||
Run,
|
||||
SynchronizeMaster,
|
||||
SynchronizeSlave,
|
||||
};
|
||||
|
||||
enum class Event : uint {
|
||||
Step,
|
||||
Frame,
|
||||
Synchronize,
|
||||
};
|
||||
|
||||
auto reset(cothread_t master_) -> void {
|
||||
master = resume = master_;
|
||||
host = co_active();
|
||||
}
|
||||
|
||||
auto enter(Mode mode_ = Mode::Run) -> Event {
|
||||
mode = mode_;
|
||||
host = co_active();
|
||||
co_switch(resume);
|
||||
return event;
|
||||
}
|
||||
|
||||
auto exit(Event event_) -> void {
|
||||
event = event_;
|
||||
resume = co_active();
|
||||
co_switch(host);
|
||||
}
|
||||
|
||||
auto synchronize(cothread_t thread) -> void {
|
||||
if(thread == master) {
|
||||
while(enter(Mode::SynchronizeMaster) != Event::Synchronize);
|
||||
} else {
|
||||
resume = thread;
|
||||
while(enter(Mode::SynchronizeSlave) != Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
||||
auto synchronize() -> void {
|
||||
if(co_active() == master) {
|
||||
if(mode == Mode::SynchronizeMaster) return exit(Event::Synchronize);
|
||||
} else {
|
||||
if(mode == Mode::SynchronizeSlave) return exit(Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
||||
auto synchronizing() const -> bool {
|
||||
return mode == Mode::SynchronizeSlave;
|
||||
}
|
||||
|
||||
private:
|
||||
cothread_t host = nullptr; //program thread (used to exit scheduler)
|
||||
cothread_t resume = nullptr; //resume thread (used to enter scheduler)
|
||||
cothread_t master = nullptr; //primary thread (used to synchronize components)
|
||||
Mode mode = Mode::Run;
|
||||
Event event = Event::Step;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Thread {
|
||||
virtual ~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
}
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, double frequency_) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(64 * 1024 * sizeof(void*), entrypoint);
|
||||
frequency = frequency_ + 0.5; //round to nearest whole number
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
}
|
||||
|
||||
cothread_t thread = nullptr;
|
||||
uint frequency = 0;
|
||||
int64 clock = 0;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,16 +1,13 @@
|
|||
processors += r6502
|
||||
|
||||
objects += fc-interface fc-system fc-scheduler fc-controller
|
||||
objects += fc-interface fc-system fc-controller
|
||||
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-controller.o: fc/controller/controller.cpp $(call rwildcard,fc/controller/)
|
||||
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/)
|
||||
|
|
|
@ -88,8 +88,8 @@ auto APU::power() -> void {
|
|||
}
|
||||
|
||||
auto APU::reset() -> void {
|
||||
create(APU::Enter, 21'477'272);
|
||||
stream = Emulator::audio.createStream(1, 21'477'272.0 / 12.0);
|
||||
create(APU::Enter, system.colorburst() * 6.0);
|
||||
stream = Emulator::audio.createStream(1, system.colorburst() / 2.0);
|
||||
|
||||
pulse[0].reset();
|
||||
pulse[1].reset();
|
||||
|
|
|
@ -49,7 +49,7 @@ auto Cartridge::power() -> void {
|
|||
}
|
||||
|
||||
auto Cartridge::reset() -> void {
|
||||
create(Cartridge::Enter, 21'477'272);
|
||||
create(Cartridge::Enter, system.colorburst() * 6.0);
|
||||
board->reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
#include <fc/fc.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
|
||||
Cheat cheat;
|
||||
|
||||
auto Cheat::reset() -> void {
|
||||
codes.reset();
|
||||
}
|
||||
|
||||
auto Cheat::append(uint addr, uint data) -> void {
|
||||
codes.append({addr, Unused, data});
|
||||
}
|
||||
|
||||
auto Cheat::append(uint addr, uint comp, uint data) -> void {
|
||||
codes.append({addr, comp, data});
|
||||
}
|
||||
|
||||
auto Cheat::find(uint addr, uint comp) -> maybe<uint> {
|
||||
for(auto& code : codes) {
|
||||
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
|
||||
return code.data;
|
||||
}
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
struct Cheat {
|
||||
struct Code {
|
||||
uint addr;
|
||||
uint comp;
|
||||
uint data;
|
||||
};
|
||||
vector<Code> codes;
|
||||
enum : uint { Unused = ~0u };
|
||||
|
||||
alwaysinline auto enable() const -> bool { return codes.size() > 0; }
|
||||
auto reset() -> void;
|
||||
auto append(uint addr, uint data) -> void;
|
||||
auto append(uint addr, uint comp, uint data) -> void;
|
||||
auto find(uint addr, uint comp) -> maybe<uint>;
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
|
@ -44,7 +44,7 @@ auto CPU::power() -> void {
|
|||
|
||||
auto CPU::reset() -> void {
|
||||
R6502::reset();
|
||||
create(CPU::Enter, 21'477'272);
|
||||
create(CPU::Enter, system.colorburst() * 6.0);
|
||||
|
||||
regs.pc = bus.read(0xfffc) << 0;
|
||||
regs.pc |= bus.read(0xfffd) << 8;
|
||||
|
|
|
@ -4,39 +4,25 @@
|
|||
//started: 2011-09-05
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
#include <emulator/scheduler.hpp>
|
||||
#include <emulator/cheat.hpp>
|
||||
|
||||
#include <processor/r6502/r6502.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
using File = Emulator::File;
|
||||
|
||||
struct Thread {
|
||||
~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
}
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65'536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
}
|
||||
|
||||
cothread_t thread = nullptr;
|
||||
uint frequency = 0;
|
||||
int64 clock = 0;
|
||||
};
|
||||
using Thread = Emulator::Thread;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
using Cheat = Emulator::Cheat;
|
||||
extern Scheduler scheduler;
|
||||
extern Cheat cheat;
|
||||
|
||||
struct Cothread : Thread {
|
||||
auto step(uint clocks) -> void;
|
||||
auto synchronizeCPU() -> void;
|
||||
};
|
||||
|
||||
#include <fc/scheduler/scheduler.hpp>
|
||||
#include <fc/controller/controller.hpp>
|
||||
#include <fc/system/system.hpp>
|
||||
#include <fc/memory/memory.hpp>
|
||||
|
@ -44,7 +30,6 @@ namespace Famicom {
|
|||
#include <fc/cpu/cpu.hpp>
|
||||
#include <fc/apu/apu.hpp>
|
||||
#include <fc/ppu/ppu.hpp>
|
||||
#include <fc/cheat/cheat.hpp>
|
||||
|
||||
inline auto Cothread::step(uint clocks) -> void {
|
||||
clock += clocks * (uint64)cpu.frequency;
|
||||
|
|
|
@ -164,15 +164,7 @@ auto Interface::unserialize(serializer& s) -> bool {
|
|||
}
|
||||
|
||||
auto Interface::cheatSet(const string_vector& list) -> void {
|
||||
cheat.reset();
|
||||
for(auto& codeset : list) {
|
||||
auto codes = codeset.split("+");
|
||||
for(auto& code : codes) {
|
||||
auto part = code.split("/");
|
||||
if(part.size() == 2) cheat.append(part[0].hex(), part[1].hex());
|
||||
if(part.size() == 3) cheat.append(part[0].hex(), part[1].hex(), part[2].hex());
|
||||
}
|
||||
}
|
||||
cheat.assign(list);
|
||||
}
|
||||
|
||||
auto Interface::cap(const string& name) -> bool {
|
||||
|
|
|
@ -17,7 +17,7 @@ auto Bus::read(uint16 addr) -> uint8 {
|
|||
else if(addr <= 0x3fff) data = ppu.readIO(addr);
|
||||
else if(addr <= 0x4017) data = cpu.readIO(addr);
|
||||
|
||||
if(cheat.enable()) {
|
||||
if(cheat) {
|
||||
if(auto result = cheat.find(addr, data)) return result();
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ auto PPU::power() -> void {
|
|||
}
|
||||
|
||||
auto PPU::reset() -> void {
|
||||
create(PPU::Enter, 21'477'272);
|
||||
create(PPU::Enter, system.colorburst() * 6.0);
|
||||
|
||||
memory::fill(&io, sizeof(IO));
|
||||
memory::fill(&latch, sizeof(Latches));
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
#include <fc/fc.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
auto Scheduler::reset() -> void {
|
||||
host = co_active();
|
||||
resume = cpu.thread;
|
||||
}
|
||||
|
||||
auto Scheduler::enter(Mode mode_) -> Event {
|
||||
mode = mode_;
|
||||
host = co_active();
|
||||
co_switch(resume);
|
||||
if(event == Event::Frame) ppu.refresh();
|
||||
return event;
|
||||
}
|
||||
|
||||
auto Scheduler::exit(Event event_) -> void {
|
||||
event = event_;
|
||||
resume = co_active();
|
||||
co_switch(host);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize(cothread_t thread) -> void {
|
||||
if(thread == cpu.thread) {
|
||||
while(enter(Mode::SynchronizeCPU) != Event::Synchronize);
|
||||
} else {
|
||||
resume = thread;
|
||||
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize() -> void {
|
||||
if(co_active() == cpu.thread && mode == Mode::SynchronizeCPU) return exit(Event::Synchronize);
|
||||
if(co_active() != cpu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronizing() const -> bool {
|
||||
return mode == Mode::SynchronizeAll;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
struct Scheduler {
|
||||
enum class Mode : uint {
|
||||
Run,
|
||||
SynchronizeCPU,
|
||||
SynchronizeAll,
|
||||
};
|
||||
|
||||
enum class Event : uint {
|
||||
Unknown,
|
||||
Frame,
|
||||
Synchronize,
|
||||
};
|
||||
|
||||
auto reset() -> void;
|
||||
auto enter(Mode = Mode::Run) -> Event;
|
||||
auto exit(Event) -> void;
|
||||
auto synchronize(cothread_t) -> void;
|
||||
auto synchronize() -> void;
|
||||
auto synchronizing() const -> bool;
|
||||
|
||||
private:
|
||||
cothread_t host = nullptr;
|
||||
cothread_t resume = nullptr;
|
||||
Mode mode = Mode::Run;
|
||||
Event event = Event::Unknown;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
|
@ -6,9 +6,11 @@ namespace Famicom {
|
|||
#include "video.cpp"
|
||||
#include "serialization.cpp"
|
||||
System system;
|
||||
Scheduler scheduler;
|
||||
Cheat cheat;
|
||||
|
||||
auto System::run() -> void {
|
||||
scheduler.enter();
|
||||
if(scheduler.enter() == Scheduler::Event::Frame) ppu.refresh();
|
||||
}
|
||||
|
||||
auto System::runToSave() -> void {
|
||||
|
@ -30,6 +32,7 @@ auto System::load() -> bool {
|
|||
}
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
if(!cartridge.load()) return false;
|
||||
information.colorburst = Emulator::Constants::Colorburst::NTSC;
|
||||
serializeInit();
|
||||
return information.loaded = true;
|
||||
}
|
||||
|
@ -66,7 +69,7 @@ auto System::reset() -> void {
|
|||
cpu.reset();
|
||||
apu.reset();
|
||||
ppu.reset();
|
||||
scheduler.reset();
|
||||
scheduler.reset(cpu.thread);
|
||||
peripherals.reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
struct System {
|
||||
auto loaded() const -> bool { return information.loaded; }
|
||||
auto colorburst() const -> double { return information.colorburst; }
|
||||
|
||||
auto run() -> void;
|
||||
auto runToSave() -> void;
|
||||
|
@ -27,6 +28,7 @@ struct System {
|
|||
|
||||
struct Information {
|
||||
bool loaded = false;
|
||||
double colorburst = 0.0;
|
||||
string manifest;
|
||||
} information;
|
||||
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
processors += lr35902
|
||||
|
||||
objects += gb-interface gb-system gb-scheduler
|
||||
objects += gb-interface gb-system
|
||||
objects += gb-memory gb-cartridge
|
||||
objects += gb-cpu gb-ppu gb-apu
|
||||
objects += gb-cheat
|
||||
|
||||
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/)
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
#include <gb/gb.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
|
||||
Cheat cheat;
|
||||
|
||||
auto Cheat::reset() -> void {
|
||||
codes.reset();
|
||||
}
|
||||
|
||||
auto Cheat::append(uint addr, uint data) -> void {
|
||||
codes.append({addr, Unused, data});
|
||||
}
|
||||
|
||||
auto Cheat::append(uint addr, uint comp, uint data) -> void {
|
||||
codes.append({addr, comp, data});
|
||||
}
|
||||
|
||||
auto Cheat::find(uint addr, uint comp) -> maybe<uint> {
|
||||
for(auto& code : codes) {
|
||||
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
|
||||
return code.data;
|
||||
}
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
struct Cheat {
|
||||
struct Code {
|
||||
uint addr;
|
||||
uint comp;
|
||||
uint data;
|
||||
};
|
||||
vector<Code> codes;
|
||||
enum : uint { Unused = ~0u };
|
||||
|
||||
alwaysinline auto enable() const -> bool { return codes.size() > 0; }
|
||||
|
||||
auto reset() -> void;
|
||||
auto append(uint addr, uint data) -> void;
|
||||
auto append(uint addr, uint comp, uint data) -> void;
|
||||
auto find(uint addr, uint comp) -> maybe<uint>;
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
|
@ -4,41 +4,26 @@
|
|||
//started: 2010-12-27
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
#include <emulator/scheduler.hpp>
|
||||
#include <emulator/cheat.hpp>
|
||||
|
||||
#include <processor/lr35902/lr35902.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
using File = Emulator::File;
|
||||
|
||||
struct Thread {
|
||||
~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
}
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65'536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
}
|
||||
|
||||
cothread_t thread = nullptr;
|
||||
uint frequency = 0;
|
||||
int64 clock = 0;
|
||||
};
|
||||
using Thread = Emulator::Thread;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
using Cheat = Emulator::Cheat;
|
||||
extern Scheduler scheduler;
|
||||
extern Cheat cheat;
|
||||
|
||||
#include <gb/memory/memory.hpp>
|
||||
#include <gb/system/system.hpp>
|
||||
#include <gb/scheduler/scheduler.hpp>
|
||||
#include <gb/cartridge/cartridge.hpp>
|
||||
#include <gb/cpu/cpu.hpp>
|
||||
#include <gb/ppu/ppu.hpp>
|
||||
#include <gb/apu/apu.hpp>
|
||||
#include <gb/cheat/cheat.hpp>
|
||||
}
|
||||
|
||||
#include <gb/interface/interface.hpp>
|
||||
|
|
|
@ -163,15 +163,7 @@ auto Interface::unserialize(serializer& s) -> bool {
|
|||
}
|
||||
|
||||
auto Interface::cheatSet(const string_vector& list) -> void {
|
||||
cheat.reset();
|
||||
for(auto& codeset : list) {
|
||||
auto codes = codeset.split("+");
|
||||
for(auto& code : codes) {
|
||||
auto part = code.split("/");
|
||||
if(part.size() == 2) cheat.append(part[0].hex(), part[1].hex());
|
||||
if(part.size() == 3) cheat.append(part[0].hex(), part[1].hex(), part[2].hex());
|
||||
}
|
||||
}
|
||||
cheat.assign(list);
|
||||
}
|
||||
|
||||
auto Interface::lcdScanline() -> void {
|
||||
|
|
|
@ -38,7 +38,7 @@ auto Memory::free() -> void {
|
|||
auto Bus::read(uint16 addr) -> uint8 {
|
||||
uint8 data = mmio[addr]->readIO(addr);
|
||||
|
||||
if(cheat.enable()) {
|
||||
if(cheat) {
|
||||
if(auto result = cheat.find(addr, data)) return result();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
#include <gb/gb.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
auto Scheduler::power() -> void {
|
||||
host = co_active();
|
||||
resume = cpu.thread;
|
||||
}
|
||||
|
||||
auto Scheduler::enter(Mode mode_) -> Event {
|
||||
mode = mode_;
|
||||
host = co_active();
|
||||
co_switch(resume);
|
||||
if(event == Event::Frame) ppu.refresh();
|
||||
return event;
|
||||
}
|
||||
|
||||
auto Scheduler::exit(Event event_) -> void {
|
||||
event = event_;
|
||||
resume = co_active();
|
||||
co_switch(host);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize(cothread_t thread) -> void {
|
||||
if(thread == cpu.thread) {
|
||||
while(enter(Mode::SynchronizeCPU) != Event::Synchronize);
|
||||
} else {
|
||||
resume = thread;
|
||||
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize() -> void {
|
||||
if(co_active() == cpu.thread && mode == Mode::SynchronizeCPU) return exit(Event::Synchronize);
|
||||
if(co_active() != cpu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronizing() const -> bool {
|
||||
return mode == Mode::SynchronizeAll;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
struct Scheduler {
|
||||
enum class Mode : uint {
|
||||
Run,
|
||||
SynchronizeCPU,
|
||||
SynchronizeAll,
|
||||
};
|
||||
|
||||
enum class Event : uint {
|
||||
Unknown,
|
||||
Step,
|
||||
Frame,
|
||||
Synchronize,
|
||||
};
|
||||
|
||||
auto power() -> void;
|
||||
auto enter(Mode = Mode::Run) -> Event;
|
||||
auto exit(Event) -> void;
|
||||
auto synchronize(cothread_t) -> void;
|
||||
auto synchronize() -> void;
|
||||
auto synchronizing() const -> bool;
|
||||
|
||||
private:
|
||||
cothread_t host = nullptr;
|
||||
cothread_t resume = nullptr;
|
||||
Mode mode = Mode::Run;
|
||||
Event event = Event::Unknown;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
|
@ -5,9 +5,11 @@ namespace GameBoy {
|
|||
#include "video.cpp"
|
||||
#include "serialization.cpp"
|
||||
System system;
|
||||
Scheduler scheduler;
|
||||
Cheat cheat;
|
||||
|
||||
auto System::run() -> void {
|
||||
scheduler.enter();
|
||||
if(scheduler.enter() == Scheduler::Event::Frame) ppu.refresh();
|
||||
}
|
||||
|
||||
auto System::runToSave() -> void {
|
||||
|
@ -71,7 +73,7 @@ auto System::power() -> void {
|
|||
cpu.power();
|
||||
ppu.power();
|
||||
apu.power();
|
||||
scheduler.power();
|
||||
scheduler.reset(cpu.thread);
|
||||
|
||||
_clocksExecuted = 0;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
processors += arm
|
||||
|
||||
objects += gba-memory gba-interface gba-scheduler gba-system
|
||||
objects += gba-memory gba-interface gba-system
|
||||
objects += gba-cartridge gba-player
|
||||
objects += gba-cpu gba-ppu gba-apu
|
||||
|
||||
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-cartridge.o: gba/cartridge/cartridge.cpp $(call rwildcard,gba/cartridge)
|
||||
obj/gba-player.o: gba/player/player.cpp $(call rwildcard,gba/player)
|
||||
|
|
|
@ -4,10 +4,16 @@
|
|||
//started: 2012-03-19
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
#include <emulator/scheduler.hpp>
|
||||
|
||||
#include <processor/arm/arm.hpp>
|
||||
|
||||
namespace GameBoyAdvance {
|
||||
using File = Emulator::File;
|
||||
using Thread = Emulator::Thread;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
extern Scheduler scheduler;
|
||||
|
||||
enum : uint { //mode flags for bus read, write:
|
||||
Nonsequential = 1, //N cycle
|
||||
|
@ -21,30 +27,7 @@ namespace GameBoyAdvance {
|
|||
Signed = 256, //sign extended
|
||||
};
|
||||
|
||||
struct Thread {
|
||||
~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
}
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65'536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
}
|
||||
|
||||
cothread_t thread = nullptr;
|
||||
uint frequency = 0;
|
||||
int clock = 0;
|
||||
};
|
||||
|
||||
#include <gba/memory/memory.hpp>
|
||||
#include <gba/scheduler/scheduler.hpp>
|
||||
#include <gba/system/system.hpp>
|
||||
#include <gba/cartridge/cartridge.hpp>
|
||||
#include <gba/player/player.hpp>
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
#include <gba/gba.hpp>
|
||||
|
||||
namespace GameBoyAdvance {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
auto Scheduler::power() -> void {
|
||||
host = co_active();
|
||||
resume = cpu.thread;
|
||||
}
|
||||
|
||||
auto Scheduler::enter(Mode mode_) -> Event {
|
||||
mode = mode_;
|
||||
host = co_active();
|
||||
co_switch(resume);
|
||||
if(event == Event::Frame) ppu.refresh();
|
||||
return event;
|
||||
}
|
||||
|
||||
auto Scheduler::exit(Event event_) -> void {
|
||||
event = event_;
|
||||
resume = co_active();
|
||||
co_switch(host);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize(cothread_t thread) -> void {
|
||||
if(thread == cpu.thread) {
|
||||
while(enter(Mode::SynchronizeCPU) != Event::Synchronize);
|
||||
} else {
|
||||
resume = thread;
|
||||
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize() -> void {
|
||||
if(co_active() == cpu.thread && mode == Mode::SynchronizeCPU) return exit(Event::Synchronize);
|
||||
if(co_active() != cpu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronizing() const -> bool {
|
||||
return mode == Mode::SynchronizeAll;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
struct Scheduler {
|
||||
enum class Mode : uint {
|
||||
Run,
|
||||
SynchronizeCPU,
|
||||
SynchronizeAll,
|
||||
};
|
||||
|
||||
enum class Event : uint {
|
||||
Unknown,
|
||||
Frame,
|
||||
Synchronize,
|
||||
};
|
||||
|
||||
auto power() -> void;
|
||||
auto enter(Mode = Mode::Run) -> Event;
|
||||
auto exit(Event) -> void;
|
||||
auto synchronize(cothread_t) -> void;
|
||||
auto synchronize() -> void;
|
||||
auto synchronizing() const -> bool;
|
||||
|
||||
private:
|
||||
cothread_t host = nullptr;
|
||||
cothread_t resume = nullptr;
|
||||
Mode mode = Mode::Run;
|
||||
Event event = Event::Unknown;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
|
@ -7,6 +7,7 @@ namespace GameBoyAdvance {
|
|||
#include "serialization.cpp"
|
||||
BIOS bios;
|
||||
System system;
|
||||
Scheduler scheduler;
|
||||
|
||||
auto System::init() -> void {
|
||||
}
|
||||
|
@ -29,7 +30,7 @@ auto System::power() -> void {
|
|||
ppu.power();
|
||||
apu.power();
|
||||
cartridge.power();
|
||||
scheduler.power();
|
||||
scheduler.reset(cpu.thread);
|
||||
}
|
||||
|
||||
auto System::load() -> bool {
|
||||
|
@ -61,7 +62,7 @@ auto System::unload() -> void {
|
|||
}
|
||||
|
||||
auto System::run() -> void {
|
||||
while(scheduler.enter() != Scheduler::Event::Frame);
|
||||
if(scheduler.enter() == Scheduler::Event::Frame) ppu.refresh();
|
||||
}
|
||||
|
||||
auto System::runToSave() -> void {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
processors += m68k z80
|
||||
|
||||
objects += md-interface
|
||||
objects += md-cpu md-apu md-vdp md-ym2612
|
||||
objects += md-system md-scheduler md-cartridge
|
||||
objects += md-cpu md-apu md-vdp md-psg md-ym2612
|
||||
objects += md-system md-cartridge
|
||||
|
||||
obj/md-interface.o: md/interface/interface.cpp $(call rwildcard,md/interface)
|
||||
obj/md-cpu.o: md/cpu/cpu.cpp $(call rwildcard,md/cpu)
|
||||
obj/md-apu.o: md/apu/apu.cpp $(call rwildcard,md/apu)
|
||||
obj/md-vdp.o: md/vdp/vdp.cpp $(call rwildcard,md/vdp)
|
||||
obj/md-psg.o: md/psg/psg.cpp $(call rwildcard,md/psg)
|
||||
obj/md-ym2612.o: md/ym2612/ym2612.cpp $(call rwildcard,md/ym2612)
|
||||
obj/md-system.o: md/system/system.cpp $(call rwildcard,md/system)
|
||||
obj/md-scheduler.o: md/scheduler/scheduler.cpp $(call rwildcard,md/scheduler)
|
||||
obj/md-cartridge.o: md/cartridge/cartridge.cpp $(call rwildcard,md/cartridge)
|
||||
|
|
|
@ -4,4 +4,22 @@ namespace MegaDrive {
|
|||
|
||||
APU apu;
|
||||
|
||||
auto APU::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), apu.main();
|
||||
}
|
||||
|
||||
auto APU::main() -> void {
|
||||
step(frequency);
|
||||
}
|
||||
|
||||
auto APU::step(uint clocks) -> void {
|
||||
}
|
||||
|
||||
auto APU::power() -> void {
|
||||
}
|
||||
|
||||
auto APU::reset() -> void {
|
||||
create(APU::Enter, system.colorburst());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
//Zilog Z80
|
||||
|
||||
struct APU : Processor::Z80, Thread {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
};
|
||||
|
||||
extern APU apu;
|
||||
|
|
|
@ -18,13 +18,50 @@ auto Cartridge::load() -> bool {
|
|||
auto document = BML::unserialize(information.manifest);
|
||||
information.title = document["information/title"].text();
|
||||
|
||||
return false;
|
||||
if(auto node = document["board/rom"]) {
|
||||
rom.size = node["size"].natural();
|
||||
rom.mask = bit::round(rom.size) - 1;
|
||||
if(rom.size) {
|
||||
rom.data = new uint8[rom.mask + 1];
|
||||
if(auto name = node["name"].text()) {
|
||||
if(auto fp = interface->open(pathID(), name, File::Read, File::Required)) {
|
||||
fp->read(rom.data, rom.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(auto node = document["board/ram"]) {
|
||||
ram.size = node["size"].natural();
|
||||
ram.mask = bit::round(ram.size) - 1;
|
||||
if(ram.size) {
|
||||
ram.data = new uint8[ram.mask + 1];
|
||||
if(auto name = node["name"].text()) {
|
||||
if(auto fp = interface->open(pathID(), name, File::Read)) {
|
||||
fp->read(ram.data, ram.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Cartridge::save() -> void {
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
|
||||
if(auto name = document["board/ram/name"].text()) {
|
||||
if(auto fp = interface->open(pathID(), name, File::Write)) {
|
||||
fp->write(ram.data, ram.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::unload() -> void {
|
||||
delete[] rom.data;
|
||||
delete[] ram.data;
|
||||
rom = Memory();
|
||||
ram = Memory();
|
||||
}
|
||||
|
||||
auto Cartridge::power() -> void {
|
||||
|
@ -33,4 +70,11 @@ auto Cartridge::power() -> void {
|
|||
auto Cartridge::reset() -> void {
|
||||
}
|
||||
|
||||
auto Cartridge::read(uint24 addr) -> uint8 {
|
||||
return rom.data[addr & rom.mask];
|
||||
}
|
||||
|
||||
auto Cartridge::write(uint24 addr, uint8 data) -> void {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,12 +10,24 @@ struct Cartridge {
|
|||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
auto read(uint24 addr) -> uint8;
|
||||
auto write(uint24 addr, uint8 data) -> void;
|
||||
|
||||
struct Information {
|
||||
uint pathID = 0;
|
||||
string sha256;
|
||||
string manifest;
|
||||
string title;
|
||||
} information;
|
||||
|
||||
struct Memory {
|
||||
uint8* data = nullptr;
|
||||
uint size = 0;
|
||||
uint mask = 0;
|
||||
};
|
||||
|
||||
Memory rom;
|
||||
Memory ram;
|
||||
};
|
||||
|
||||
extern Cartridge cartridge;
|
||||
|
|
|
@ -4,4 +4,48 @@ namespace MegaDrive {
|
|||
|
||||
CPU cpu;
|
||||
|
||||
auto CPU::Enter() -> void {
|
||||
cpu.boot();
|
||||
while(true) scheduler.synchronize(), cpu.main();
|
||||
}
|
||||
|
||||
auto CPU::boot() -> void {
|
||||
r.sp = readLong(0);
|
||||
r.pc = readLong(4);
|
||||
}
|
||||
|
||||
auto CPU::main() -> void {
|
||||
instruction();
|
||||
}
|
||||
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
clock += clocks;
|
||||
while(clock >= frequency) {
|
||||
clock -= frequency;
|
||||
scheduler.exit(Scheduler::Event::Frame);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::power() -> void {
|
||||
M68K::power();
|
||||
}
|
||||
|
||||
auto CPU::reset() -> void {
|
||||
M68K::reset();
|
||||
create(CPU::Enter, system.colorburst() * 15.0 / 7.0);
|
||||
}
|
||||
|
||||
auto CPU::read(uint32 addr) -> uint8 {
|
||||
addr = (uint24)addr;
|
||||
|
||||
if(addr < 0x400000) return cartridge.read(addr);
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
auto CPU::write(uint32 addr, uint8 data) -> void {
|
||||
addr = (uint24)addr;
|
||||
|
||||
if(addr < 0x400000) return cartridge.write(addr, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,16 @@
|
|||
//Motorola 68000
|
||||
|
||||
struct CPU : Processor::M68K, Thread {
|
||||
static auto Enter() -> void;
|
||||
auto boot() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void override;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
auto read(uint32 addr) -> uint8 override;
|
||||
auto write(uint32 addr, uint8 data) -> void override;
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
||||
|
|
|
@ -81,8 +81,7 @@ auto Interface::loaded() -> bool {
|
|||
}
|
||||
|
||||
auto Interface::load(uint id) -> bool {
|
||||
system.load();
|
||||
return false;
|
||||
return system.load();
|
||||
}
|
||||
|
||||
auto Interface::save() -> void {
|
||||
|
|
|
@ -4,41 +4,25 @@
|
|||
//started: 2016-07-08
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
#include <emulator/scheduler.hpp>
|
||||
|
||||
#include <processor/m68k/m68k.hpp>
|
||||
#include <processor/z80/z80.hpp>
|
||||
|
||||
namespace MegaDrive {
|
||||
using File = Emulator::File;
|
||||
|
||||
struct Thread {
|
||||
~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
}
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65'536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
}
|
||||
|
||||
cothread_t thread = nullptr;
|
||||
uint frequency = 0;
|
||||
int64 clock = 0;
|
||||
};
|
||||
using Thread = Emulator::Thread;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
extern Scheduler scheduler;
|
||||
|
||||
#include <md/cpu/cpu.hpp>
|
||||
#include <md/apu/apu.hpp>
|
||||
#include <md/vdp/vdp.hpp>
|
||||
#include <md/psg/psg.hpp>
|
||||
#include <md/ym2612/ym2612.hpp>
|
||||
|
||||
#include <md/system/system.hpp>
|
||||
#include <md/scheduler/scheduler.hpp>
|
||||
#include <md/cartridge/cartridge.hpp>
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
#include <md/md.hpp>
|
||||
|
||||
namespace MegaDrive {
|
||||
|
||||
PSG psg;
|
||||
|
||||
auto PSG::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), psg.main();
|
||||
}
|
||||
|
||||
auto PSG::main() -> void {
|
||||
step(frequency);
|
||||
}
|
||||
|
||||
auto PSG::step(uint clocks) -> void {
|
||||
}
|
||||
|
||||
auto PSG::power() -> void {
|
||||
}
|
||||
|
||||
auto PSG::reset() -> void {
|
||||
create(PSG::Enter, system.colorburst());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
//TI SN76489
|
||||
|
||||
struct PSG : Thread {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
};
|
||||
|
||||
extern PSG psg;
|
|
@ -1,7 +0,0 @@
|
|||
#include <md/md.hpp>
|
||||
|
||||
namespace MegaDrive {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
struct Scheduler {
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
|
@ -3,8 +3,13 @@
|
|||
namespace MegaDrive {
|
||||
|
||||
System system;
|
||||
Scheduler scheduler;
|
||||
|
||||
auto System::run() -> void {
|
||||
if(scheduler.enter() == Scheduler::Event::Frame) {
|
||||
static uint32 output[1280 * 480] = {0};
|
||||
Emulator::video.refresh(output, 1280 * sizeof(uint32), 1280, 480);
|
||||
}
|
||||
}
|
||||
|
||||
auto System::load() -> bool {
|
||||
|
@ -14,6 +19,7 @@ auto System::load() -> bool {
|
|||
} else return false;
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
if(!cartridge.load()) return false;
|
||||
information.colorburst = Emulator::Constants::Colorburst::NTSC;
|
||||
return information.loaded = true;
|
||||
}
|
||||
|
||||
|
@ -27,11 +33,29 @@ auto System::unload() -> void {
|
|||
|
||||
auto System::power() -> void {
|
||||
cartridge.power();
|
||||
cpu.power();
|
||||
apu.power();
|
||||
vdp.power();
|
||||
psg.power();
|
||||
ym2612.power();
|
||||
reset();
|
||||
}
|
||||
|
||||
auto System::reset() -> void {
|
||||
Emulator::video.reset();
|
||||
Emulator::video.setInterface(interface);
|
||||
Emulator::video.setPalette();
|
||||
|
||||
Emulator::audio.reset();
|
||||
Emulator::audio.setInterface(interface);
|
||||
|
||||
cartridge.reset();
|
||||
cpu.reset();
|
||||
apu.reset();
|
||||
vdp.reset();
|
||||
psg.reset();
|
||||
ym2612.reset();
|
||||
scheduler.reset(cpu.thread);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
struct System {
|
||||
auto loaded() const { return information.manifest; }
|
||||
auto loaded() const -> bool { return information.loaded; }
|
||||
auto colorburst() const -> double { return information.colorburst; }
|
||||
|
||||
auto run() -> void;
|
||||
|
||||
|
@ -12,6 +13,7 @@ struct System {
|
|||
struct Information {
|
||||
bool loaded = false;
|
||||
string manifest;
|
||||
double colorburst = 0.0;
|
||||
} information;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,28 @@
|
|||
#include <md/md.hpp>
|
||||
|
||||
//256-width = colorburst * 15 / 10
|
||||
//320-width = colorburst * 15 / 8
|
||||
|
||||
namespace MegaDrive {
|
||||
|
||||
VDP vdp;
|
||||
|
||||
auto VDP::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), vdp.main();
|
||||
}
|
||||
|
||||
auto VDP::main() -> void {
|
||||
step(frequency);
|
||||
}
|
||||
|
||||
auto VDP::step(uint clocks) -> void {
|
||||
}
|
||||
|
||||
auto VDP::power() -> void {
|
||||
}
|
||||
|
||||
auto VDP::reset() -> void {
|
||||
create(VDP::Enter, system.colorburst() * 15.0 / 10.0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
//Yamaha YM7101
|
||||
|
||||
struct VDP : Thread {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
};
|
||||
|
||||
extern VDP vdp;
|
||||
|
|
|
@ -4,4 +4,22 @@ namespace MegaDrive {
|
|||
|
||||
YM2612 ym2612;
|
||||
|
||||
auto YM2612::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), ym2612.main();
|
||||
}
|
||||
|
||||
auto YM2612::main() -> void {
|
||||
step(frequency);
|
||||
}
|
||||
|
||||
auto YM2612::step(uint clocks) -> void {
|
||||
}
|
||||
|
||||
auto YM2612::power() -> void {
|
||||
}
|
||||
|
||||
auto YM2612::reset() -> void {
|
||||
create(YM2612::Enter, system.colorburst() * 15.0 / 7.0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
//Yamaha YM2612
|
||||
|
||||
struct YM2612 : Thread {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
};
|
||||
|
||||
extern YM2612 ym2612;
|
||||
|
|
|
@ -22,4 +22,4 @@ obj/processor-r65816.o: processor/r65816/r65816.cpp $(call rwildcard,processor
|
|||
obj/processor-spc700.o: processor/spc700/spc700.cpp $(call rwildcard,processor/spc700)
|
||||
obj/processor-upd96050.o: processor/upd96050/upd96050.cpp $(call rwildcard,processor/upd96050)
|
||||
obj/processor-v30mz.o: processor/v30mz/v30mz.cpp $(call rwildcard,processor/v30mz)
|
||||
obj/processor-z80.o: processor/z80/z80.cpp $(call rwildcard,z80)
|
||||
obj/processor-z80.o: processor/z80/z80.cpp $(call rwildcard,processor/z80)
|
||||
|
|
|
@ -3,4 +3,46 @@
|
|||
|
||||
namespace Processor {
|
||||
|
||||
#include "memory.cpp"
|
||||
|
||||
auto M68K::instruction() -> void {
|
||||
instructionsExecuted++;
|
||||
|
||||
auto opcode = readWord(r.pc);
|
||||
r.pc += 2;
|
||||
|
||||
//nop
|
||||
if(opcode == 0x4e71) {
|
||||
step(5);
|
||||
return;
|
||||
}
|
||||
|
||||
//bra disp
|
||||
if((opcode & 0xff00) == 0x6000) {
|
||||
int displacement = (int8)opcode;
|
||||
if(!displacement) displacement = (int16)readWord(r.pc);
|
||||
r.pc += displacement;
|
||||
step(12);
|
||||
return;
|
||||
}
|
||||
|
||||
instructionsExecuted--;
|
||||
r.pc -= 2;
|
||||
print("[M68K] unimplemented instruction: ", hex(r.pc, 6L), " = ", hex(opcode, 4L), "\n");
|
||||
print("[M68K] executed ", instructionsExecuted, " instructions\n");
|
||||
while(true) step(5);
|
||||
}
|
||||
|
||||
auto M68K::power() -> void {
|
||||
}
|
||||
|
||||
auto M68K::reset() -> void {
|
||||
instructionsExecuted = 0;
|
||||
|
||||
for(auto& n : r.d) n = 0;
|
||||
for(auto& n : r.a) n = 0;
|
||||
r.pc = 0x000200;
|
||||
r.ccr.value = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,42 @@
|
|||
namespace Processor {
|
||||
|
||||
struct M68K {
|
||||
virtual auto step(uint clocks) -> void = 0;
|
||||
virtual auto read(uint32 addr) -> uint8 = 0;
|
||||
virtual auto write(uint32 addr, uint8 data) -> void = 0;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
auto instruction() -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto readByte(uint32 addr) -> uint8;
|
||||
auto readWord(uint32 addr) -> uint16;
|
||||
auto readLong(uint32 addr) -> uint32;
|
||||
auto readQuad(uint32 addr) -> uint64;
|
||||
|
||||
struct Registers {
|
||||
//todo: this is almost certainly UB or IB due to alignment rules ...
|
||||
union {
|
||||
uint32_t d[8];
|
||||
struct { uint32_t d0, d1, d2, d3, d4, d5, d6, d7; };
|
||||
};
|
||||
union {
|
||||
uint32_t a[8];
|
||||
struct { uint32_t a0, a1, a2, a3, a4, a5, a6; union { uint32_t a7, sp; }; };
|
||||
};
|
||||
uint32 pc;
|
||||
union CCR {
|
||||
uint8_t value = 0;
|
||||
BooleanBitField<uint8_t, 0> c; //carry
|
||||
BooleanBitField<uint8_t, 1> v; //overflow
|
||||
BooleanBitField<uint8_t, 2> z; //zero
|
||||
BooleanBitField<uint8_t, 3> n; //negative
|
||||
BooleanBitField<uint8_t, 4> x; //extend
|
||||
} ccr;
|
||||
} r;
|
||||
|
||||
uint instructionsExecuted = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
auto M68K::readByte(uint32 addr) -> uint8 {
|
||||
return read(addr);
|
||||
}
|
||||
|
||||
auto M68K::readWord(uint32 addr) -> uint16 {
|
||||
uint16 data;
|
||||
data |= read(addr + 0) << 8;
|
||||
data |= read(addr + 1) << 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
auto M68K::readLong(uint32 addr) -> uint32 {
|
||||
uint32 data;
|
||||
data |= read(addr + 0) << 24;
|
||||
data |= read(addr + 1) << 16;
|
||||
data |= read(addr + 2) << 8;
|
||||
data |= read(addr + 3) << 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
auto M68K::readQuad(uint32 addr) -> uint64 {
|
||||
uint64 data;
|
||||
data |= (uint64)read(addr + 0) << 56;
|
||||
data |= (uint64)read(addr + 1) << 48;
|
||||
data |= (uint64)read(addr + 2) << 40;
|
||||
data |= (uint64)read(addr + 3) << 32;
|
||||
data |= (uint64)read(addr + 4) << 24;
|
||||
data |= (uint64)read(addr + 5) << 16;
|
||||
data |= (uint64)read(addr + 6) << 8;
|
||||
data |= (uint64)read(addr + 7) << 0;
|
||||
return data;
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
processors += r65816 spc700 arm gsu hg51b upd96050
|
||||
|
||||
objects += sfc-interface sfc-system sfc-scheduler sfc-controller
|
||||
objects += sfc-cartridge sfc-cheat
|
||||
objects += sfc-memory sfc-cpu sfc-smp sfc-dsp sfc-ppu
|
||||
objects += sfc-interface sfc-system sfc-controller
|
||||
objects += sfc-cartridge sfc-memory
|
||||
objects += sfc-cpu sfc-smp sfc-dsp sfc-ppu
|
||||
objects += sfc-expansion sfc-satellaview sfc-superdisc
|
||||
objects += sfc-21fx
|
||||
objects += sfc-icd2 sfc-mcc sfc-nss sfc-event
|
||||
|
@ -15,10 +15,8 @@ objects += sfc-bsmemory sfc-sufamiturbo
|
|||
|
||||
obj/sfc-interface.o: sfc/interface/interface.cpp $(call rwildcard,sfc/interface)
|
||||
obj/sfc-system.o: sfc/system/system.cpp $(call rwildcard,sfc/system/)
|
||||
obj/sfc-scheduler.o: sfc/scheduler/scheduler.cpp $(call rwildcard,sfc/scheduler/)
|
||||
obj/sfc-controller.o: sfc/controller/controller.cpp $(call rwildcard,sfc/controller/)
|
||||
obj/sfc-cartridge.o: sfc/cartridge/cartridge.cpp $(call rwildcard,sfc/cartridge/)
|
||||
obj/sfc-cheat.o: sfc/cheat/cheat.cpp $(call rwildcard,sfc/cheat/)
|
||||
obj/sfc-memory.o: sfc/memory/memory.cpp $(call rwildcard,sfc/memory/)
|
||||
|
||||
obj/sfc-cpu.o: sfc/cpu/cpu.cpp $(call rwildcard,sfc/cpu/)
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
#include <sfc/sfc.hpp>
|
||||
|
||||
namespace SuperFamicom {
|
||||
|
||||
Cheat cheat;
|
||||
|
||||
auto Cheat::reset() -> void {
|
||||
codes.reset();
|
||||
}
|
||||
|
||||
auto Cheat::append(uint addr, uint data) -> void {
|
||||
codes.append({addr, Unused, data});
|
||||
}
|
||||
|
||||
auto Cheat::append(uint addr, uint comp, uint data) -> void {
|
||||
codes.append({addr, comp, data});
|
||||
}
|
||||
|
||||
auto Cheat::find(uint addr, uint comp) -> maybe<unsigned> {
|
||||
//WRAM mirroring: $00-3f,80-bf:0000-1fff -> $7e:0000-1fff
|
||||
if((addr & 0x40e000) == 0x000000) addr = 0x7e0000 | (addr & 0x1fff);
|
||||
|
||||
for(auto& code : codes) {
|
||||
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
|
||||
return code.data;
|
||||
}
|
||||
}
|
||||
|
||||
return nothing;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
struct Cheat {
|
||||
enum : uint { Unused = ~0u };
|
||||
|
||||
alwaysinline auto enable() const -> bool { return codes.size() > 0; }
|
||||
|
||||
auto reset() -> void;
|
||||
auto append(uint addr, uint data) -> void;
|
||||
auto append(uint addr, uint comp, uint data) -> void;
|
||||
auto find(uint addr, uint comp) -> maybe<uint>;
|
||||
|
||||
struct Code {
|
||||
uint addr;
|
||||
uint comp;
|
||||
uint data;
|
||||
};
|
||||
vector<Code> codes;
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
|
@ -117,7 +117,7 @@ auto SA1::power() -> void {
|
|||
}
|
||||
|
||||
auto SA1::reset() -> void {
|
||||
create(SA1::Enter, system.cpuFrequency());
|
||||
create(SA1::Enter, system.colorburst() * 6.0);
|
||||
|
||||
cpubwram.dma = false;
|
||||
for(auto addr : range(iram.size())) {
|
||||
|
|
|
@ -48,7 +48,7 @@ auto SuperFX::power() -> void {
|
|||
|
||||
auto SuperFX::reset() -> void {
|
||||
GSU::reset();
|
||||
create(SuperFX::Enter, system.cpuFrequency());
|
||||
create(SuperFX::Enter, system.colorburst() * 6.0);
|
||||
|
||||
romMask = rom.size() - 1;
|
||||
ramMask = ram.size() - 1;
|
||||
|
|
|
@ -114,7 +114,7 @@ auto CPU::power() -> void {
|
|||
}
|
||||
|
||||
auto CPU::reset() -> void {
|
||||
create(Enter, system.cpuFrequency());
|
||||
create(Enter, system.colorburst() * 6.0);
|
||||
coprocessors.reset();
|
||||
PPUcounter::reset();
|
||||
|
||||
|
|
|
@ -244,8 +244,8 @@ auto DSP::power() -> void {
|
|||
}
|
||||
|
||||
auto DSP::reset() -> void {
|
||||
create(Enter, system.apuFrequency());
|
||||
stream = Emulator::audio.createStream(2, system.apuFrequency() / 768.0);
|
||||
create(Enter, 32040.0 * 768.0);
|
||||
stream = Emulator::audio.createStream(2, 32040.0);
|
||||
|
||||
REG(FLG) = 0xe0;
|
||||
state.noise = 0x4000;
|
||||
|
|
|
@ -134,8 +134,8 @@ auto Interface::title() -> string {
|
|||
|
||||
auto Interface::videoFrequency() -> double {
|
||||
switch(system.region()) { default:
|
||||
case System::Region::NTSC: return system.cpuFrequency() / (262.0 * 1364.0 - 4.0);
|
||||
case System::Region::PAL: return system.cpuFrequency() / (312.0 * 1364.0);
|
||||
case System::Region::NTSC: return (system.colorburst() * 6.0) / (262.0 * 1364.0 - 4.0);
|
||||
case System::Region::PAL: return (system.colorburst() * 6.0) / (312.0 * 1364.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,7 +170,7 @@ auto Interface::videoColor(uint32 color) -> uint64 {
|
|||
}
|
||||
|
||||
auto Interface::audioFrequency() -> double {
|
||||
return system.apuFrequency() / 768.0;
|
||||
return 32040.0;
|
||||
}
|
||||
|
||||
auto Interface::loaded() -> bool {
|
||||
|
@ -236,30 +236,10 @@ auto Interface::unserialize(serializer& s) -> bool {
|
|||
|
||||
auto Interface::cheatSet(const string_vector& list) -> void {
|
||||
cheat.reset();
|
||||
|
||||
#if defined(SFC_SUPERGAMEBOY)
|
||||
if(cartridge.has.ICD2) {
|
||||
GameBoy::cheat.reset();
|
||||
for(auto& codeset : list) {
|
||||
auto codes = codeset.split("+");
|
||||
for(auto& code : codes) {
|
||||
auto part = code.split("/");
|
||||
if(part.size() == 2) GameBoy::cheat.append(part[0].hex(), part[1].hex());
|
||||
if(part.size() == 3) GameBoy::cheat.append(part[0].hex(), part[1].hex(), part[2].hex());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(cartridge.has.ICD2) return GameBoy::cheat.assign(list);
|
||||
#endif
|
||||
|
||||
for(auto& codeset : list) {
|
||||
auto codes = codeset.split("+");
|
||||
for(auto& code : codes) {
|
||||
auto part = code.split("/");
|
||||
if(part.size() == 2) cheat.append(part[0].hex(), part[1].hex());
|
||||
if(part.size() == 3) cheat.append(part[0].hex(), part[1].hex(), part[2].hex());
|
||||
}
|
||||
}
|
||||
cheat.assign(list);
|
||||
}
|
||||
|
||||
auto Interface::cap(const string& name) -> bool {
|
||||
|
|
|
@ -67,7 +67,8 @@ auto Bus::reduce(uint addr, uint mask) -> uint {
|
|||
|
||||
auto Bus::read(uint24 addr, uint8 data) -> uint8 {
|
||||
data = reader[lookup[addr]](target[addr], data);
|
||||
if(cheat.enable()) {
|
||||
if(cheat) {
|
||||
if(!(addr & 0x40e000)) addr = 0x7e0000 | (addr & 0x1fff); //de-mirror WRAM
|
||||
if(auto result = cheat.find(addr, data)) return result();
|
||||
}
|
||||
return data;
|
||||
|
|
|
@ -95,7 +95,7 @@ auto PPU::power() -> void {
|
|||
}
|
||||
|
||||
auto PPU::reset() -> void {
|
||||
create(Enter, system.cpuFrequency());
|
||||
create(Enter, system.colorburst() * 6.0);
|
||||
PPUcounter::reset();
|
||||
memory::fill(output, 512 * 480 * sizeof(uint32));
|
||||
|
||||
|
|
|
@ -154,7 +154,7 @@ privileged:
|
|||
friend class PPU::Object;
|
||||
friend class PPU::Window;
|
||||
friend class PPU::Screen;
|
||||
friend class Scheduler;
|
||||
friend class System;
|
||||
};
|
||||
|
||||
extern PPU ppu;
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
#include <sfc/sfc.hpp>
|
||||
|
||||
namespace SuperFamicom {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
auto Scheduler::reset() -> void {
|
||||
host = co_active();
|
||||
resume = cpu.thread;
|
||||
}
|
||||
|
||||
auto Scheduler::enter(Mode mode_) -> Event {
|
||||
mode = mode_;
|
||||
host = co_active();
|
||||
co_switch(resume);
|
||||
if(event == Event::Frame) ppu.refresh();
|
||||
return event;
|
||||
}
|
||||
|
||||
auto Scheduler::exit(Event event_) -> void {
|
||||
event = event_;
|
||||
resume = co_active();
|
||||
co_switch(host);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize(cothread_t thread) -> void {
|
||||
if(thread == cpu.thread) {
|
||||
while(enter(Mode::SynchronizeCPU) != Event::Synchronize);
|
||||
} else {
|
||||
resume = thread;
|
||||
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize() -> void {
|
||||
if(co_active() == cpu.thread && mode == Mode::SynchronizeCPU) return exit(Event::Synchronize);
|
||||
if(co_active() != cpu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronizing() const -> bool {
|
||||
return mode == Mode::SynchronizeAll;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
struct Scheduler {
|
||||
enum class Mode : uint {
|
||||
Run,
|
||||
SynchronizeCPU,
|
||||
SynchronizeAll,
|
||||
};
|
||||
|
||||
enum class Event : uint {
|
||||
Unknown,
|
||||
Frame,
|
||||
Synchronize,
|
||||
Debugger,
|
||||
};
|
||||
|
||||
auto reset() -> void;
|
||||
auto enter(Mode = Mode::Run) -> Event;
|
||||
auto exit(Event) -> void;
|
||||
auto synchronize(cothread_t) -> void;
|
||||
auto synchronize() -> void;
|
||||
auto synchronizing() const -> bool;
|
||||
|
||||
private:
|
||||
cothread_t host = nullptr; //program thread (used to exit emulation)
|
||||
cothread_t resume = nullptr; //resume thread (used to re-enter emulation)
|
||||
Mode mode = Mode::Run; //determines when to exit emulation thread
|
||||
Event event = Event::Unknown; //set by exit(), returned by enter()
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
|
@ -4,6 +4,10 @@
|
|||
//started: 2004-10-14
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
#include <emulator/scheduler.hpp>
|
||||
#include <emulator/cheat.hpp>
|
||||
|
||||
#include <processor/arm/arm.hpp>
|
||||
#include <processor/gsu/gsu.hpp>
|
||||
#include <processor/hg51b/hg51b.hpp>
|
||||
|
@ -17,28 +21,11 @@
|
|||
|
||||
namespace SuperFamicom {
|
||||
using File = Emulator::File;
|
||||
|
||||
struct Thread {
|
||||
virtual ~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
}
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65'536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
}
|
||||
|
||||
cothread_t thread = nullptr;
|
||||
uint frequency = 0;
|
||||
int64 clock = 0;
|
||||
};
|
||||
using Thread = Emulator::Thread;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
using Cheat = Emulator::Cheat;
|
||||
extern Scheduler scheduler;
|
||||
extern Cheat cheat;
|
||||
|
||||
//dynamic thread bound to CPU (coprocessors and peripherals)
|
||||
struct Cothread : Thread {
|
||||
|
@ -57,11 +44,9 @@ namespace SuperFamicom {
|
|||
#include <sfc/controller/controller.hpp>
|
||||
#include <sfc/expansion/expansion.hpp>
|
||||
#include <sfc/system/system.hpp>
|
||||
#include <sfc/scheduler/scheduler.hpp>
|
||||
#include <sfc/coprocessor/coprocessor.hpp>
|
||||
#include <sfc/slot/slot.hpp>
|
||||
#include <sfc/cartridge/cartridge.hpp>
|
||||
#include <sfc/cheat/cheat.hpp>
|
||||
|
||||
#include <sfc/memory/memory-inline.hpp>
|
||||
#include <sfc/ppu/counter/counter-inline.hpp>
|
||||
|
|
|
@ -43,7 +43,7 @@ auto SMP::power() -> void {
|
|||
}
|
||||
|
||||
auto SMP::reset() -> void {
|
||||
create(Enter, system.apuFrequency());
|
||||
create(Enter, 32040.0 * 768.0);
|
||||
|
||||
regs.pc.l = iplrom[62];
|
||||
regs.pc.h = iplrom[63];
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
namespace SuperFamicom {
|
||||
|
||||
System system;
|
||||
|
||||
Scheduler scheduler;
|
||||
Cheat cheat;
|
||||
#include "video.cpp"
|
||||
#include "peripherals.cpp"
|
||||
#include "random.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto System::run() -> void {
|
||||
scheduler.enter();
|
||||
if(scheduler.enter() == Scheduler::Event::Frame) ppu.refresh();
|
||||
}
|
||||
|
||||
auto System::runToSave() -> void {
|
||||
|
@ -72,8 +73,9 @@ auto System::load() -> bool {
|
|||
if(system["region"].text() == "NTSC") information.region = Region::NTSC;
|
||||
if(system["region"].text() == "PAL" ) information.region = Region::PAL;
|
||||
|
||||
information.cpuFrequency = region() == Region::NTSC ? 21'477'272 : 21'281'370;
|
||||
information.apuFrequency = 24'606'720;
|
||||
information.colorburst = region() == Region::NTSC
|
||||
? Emulator::Constants::Colorburst::NTSC
|
||||
: Emulator::Constants::Colorburst::PAL * 4.0 / 5.0;
|
||||
|
||||
if(cartridge.has.ICD2) icd2.load();
|
||||
if(cartridge.has.MCC) mcc.load();
|
||||
|
@ -203,7 +205,7 @@ auto System::reset() -> void {
|
|||
if(cartridge.has.SPC7110) cpu.coprocessors.append(&spc7110);
|
||||
if(cartridge.has.MSU1) cpu.coprocessors.append(&msu1);
|
||||
|
||||
scheduler.reset();
|
||||
scheduler.reset(cpu.thread);
|
||||
peripherals.reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,8 +5,7 @@ struct System {
|
|||
|
||||
inline auto loaded() const -> bool { return information.loaded; }
|
||||
inline auto region() const -> Region { return information.region; }
|
||||
inline auto cpuFrequency() const -> uint { return information.cpuFrequency; }
|
||||
inline auto apuFrequency() const -> uint { return information.apuFrequency; }
|
||||
inline auto colorburst() const -> double { return information.colorburst; }
|
||||
|
||||
auto run() -> void;
|
||||
auto runToSave() -> void;
|
||||
|
@ -32,8 +31,7 @@ private:
|
|||
string manifest;
|
||||
bool loaded = false;
|
||||
Region region = Region::NTSC;
|
||||
uint cpuFrequency = 0;
|
||||
uint apuFrequency = 0;
|
||||
double colorburst = 0.0;
|
||||
} information;
|
||||
|
||||
uint serializeSize = 0;
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
processors += v30mz
|
||||
|
||||
objects += ws-interface ws-system ws-scheduler
|
||||
objects += ws-interface ws-system
|
||||
objects += ws-memory ws-eeprom ws-cartridge
|
||||
objects += ws-cpu ws-ppu ws-apu
|
||||
objects += ws-cheat
|
||||
|
||||
obj/ws-interface.o: ws/interface/interface.cpp $(call rwildcard,ws/interface/)
|
||||
obj/ws-system.o: ws/system/system.cpp $(call rwildcard,ws/system/)
|
||||
obj/ws-scheduler.o: ws/scheduler/scheduler.cpp $(call rwildcard,ws/scheduler/)
|
||||
obj/ws-memory.o: ws/memory/memory.cpp $(call rwildcard,ws/memory/)
|
||||
obj/ws-eeprom.o: ws/eeprom/eeprom.cpp $(call rwildcard,ws/eeprom/)
|
||||
obj/ws-cartridge.o: ws/cartridge/cartridge.cpp $(call rwildcard,ws/cartridge/)
|
||||
obj/ws-cpu.o: ws/cpu/cpu.cpp $(call rwildcard,ws/cpu/)
|
||||
obj/ws-ppu.o: ws/ppu/ppu.cpp $(call rwildcard,ws/ppu/)
|
||||
obj/ws-apu.o: ws/apu/apu.cpp $(call rwildcard,ws/apu/)
|
||||
obj/ws-cheat.o: ws/cheat/cheat.cpp $(call rwildcard,ws/cheat/)
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
#include <ws/ws.hpp>
|
||||
|
||||
namespace WonderSwan {
|
||||
|
||||
Cheat cheat;
|
||||
|
||||
auto Cheat::reset() -> void {
|
||||
codes.reset();
|
||||
}
|
||||
|
||||
auto Cheat::append(uint addr, uint data) -> void {
|
||||
codes.append({addr, Unused, data});
|
||||
}
|
||||
|
||||
auto Cheat::append(uint addr, uint comp, uint data) -> void {
|
||||
codes.append({addr, comp, data});
|
||||
}
|
||||
|
||||
auto Cheat::find(uint addr, uint comp) -> maybe<uint> {
|
||||
for(auto& code : codes) {
|
||||
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
|
||||
return code.data;
|
||||
}
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
struct Cheat {
|
||||
struct Code {
|
||||
uint addr;
|
||||
uint comp;
|
||||
uint data;
|
||||
};
|
||||
vector<Code> codes;
|
||||
enum : uint { Unused = ~0u };
|
||||
|
||||
alwaysinline auto enable() const -> bool { return codes.size() > 0; }
|
||||
|
||||
auto reset() -> void;
|
||||
auto append(uint addr, uint data) -> void;
|
||||
auto append(uint addr, uint comp, uint data) -> void;
|
||||
auto find(uint addr, uint comp) -> maybe<uint>;
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
|
@ -128,15 +128,7 @@ auto Interface::unserialize(serializer& s) -> bool {
|
|||
}
|
||||
|
||||
auto Interface::cheatSet(const string_vector& list) -> void {
|
||||
cheat.reset();
|
||||
for(auto& codeset : list) {
|
||||
auto codes = codeset.split("+");
|
||||
for(auto& code : codes) {
|
||||
auto part = code.split("/");
|
||||
if(part.size() == 2) cheat.append(part[0].hex(), part[1].hex());
|
||||
if(part.size() == 3) cheat.append(part[0].hex(), part[1].hex(), part[2].hex());
|
||||
}
|
||||
}
|
||||
cheat.assign(list);
|
||||
}
|
||||
|
||||
auto Interface::cap(const string& name) -> bool {
|
||||
|
|
|
@ -35,7 +35,7 @@ auto Bus::read(uint20 addr) -> uint8 {
|
|||
if(addr.bits(16,19) == 0) data = iram.read(addr);
|
||||
if(addr.bits(16,19) == 1) data = cartridge.ramRead(addr);
|
||||
if(addr.bits(16,19) >= 2) data = cartridge.romRead(addr);
|
||||
if(cheat.enable()) {
|
||||
if(cheat) {
|
||||
if(auto result = cheat.find(addr, data)) data = result();
|
||||
}
|
||||
return data;
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
#include <ws/ws.hpp>
|
||||
|
||||
namespace WonderSwan {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
auto Scheduler::power() -> void {
|
||||
host = co_active();
|
||||
resume = cpu.thread;
|
||||
}
|
||||
|
||||
auto Scheduler::enter(Mode mode_) -> Event {
|
||||
mode = mode_;
|
||||
host = co_active();
|
||||
co_switch(resume);
|
||||
if(event == Event::Frame) ppu.refresh();
|
||||
return event;
|
||||
}
|
||||
|
||||
auto Scheduler::exit(Event event_) -> void {
|
||||
event = event_;
|
||||
resume = co_active();
|
||||
co_switch(host);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize(cothread_t thread) -> void {
|
||||
if(thread == cpu.thread) {
|
||||
while(enter(Mode::SynchronizeCPU) != Event::Synchronize);
|
||||
} else {
|
||||
resume = thread;
|
||||
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize() -> void {
|
||||
if(co_active() == cpu.thread && mode == Mode::SynchronizeCPU) return exit(Event::Synchronize);
|
||||
if(co_active() != cpu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronizing() const -> bool {
|
||||
return mode == Mode::SynchronizeAll;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
struct Scheduler {
|
||||
enum class Mode : uint {
|
||||
Run,
|
||||
SynchronizeCPU,
|
||||
SynchronizeAll,
|
||||
};
|
||||
|
||||
enum class Event : uint {
|
||||
Unknown,
|
||||
Frame,
|
||||
Synchronize,
|
||||
};
|
||||
|
||||
auto power() -> void;
|
||||
auto enter(Mode = Mode::Run) -> Event;
|
||||
auto exit(Event) -> void;
|
||||
auto synchronize(cothread_t) -> void;
|
||||
auto synchronize() -> void;
|
||||
auto synchronizing() const -> bool;
|
||||
|
||||
private:
|
||||
cothread_t host = nullptr;
|
||||
cothread_t resume = nullptr;
|
||||
Mode mode = Mode::Run;
|
||||
Event event = Event::Unknown;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
|
@ -3,6 +3,8 @@
|
|||
namespace WonderSwan {
|
||||
|
||||
System system;
|
||||
Scheduler scheduler;
|
||||
Cheat cheat;
|
||||
#include "io.cpp"
|
||||
#include "video.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
@ -74,7 +76,7 @@ auto System::power() -> void {
|
|||
ppu.power();
|
||||
apu.power();
|
||||
cartridge.power();
|
||||
scheduler.power();
|
||||
scheduler.reset(cpu.thread);
|
||||
|
||||
bus.map(this, 0x0060);
|
||||
bus.map(this, 0x00ba, 0x00be);
|
||||
|
@ -86,7 +88,7 @@ auto System::power() -> void {
|
|||
}
|
||||
|
||||
auto System::run() -> void {
|
||||
scheduler.enter();
|
||||
if(scheduler.enter() == Scheduler::Event::Frame) ppu.refresh();
|
||||
pollKeypad();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,10 +4,19 @@
|
|||
//started: 2016-01-26
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
#include <emulator/scheduler.hpp>
|
||||
#include <emulator/cheat.hpp>
|
||||
|
||||
#include <processor/v30mz/v30mz.hpp>
|
||||
|
||||
namespace WonderSwan {
|
||||
using File = Emulator::File;
|
||||
using Thread = Emulator::Thread;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
using Cheat = Emulator::Cheat;
|
||||
extern Scheduler scheduler;
|
||||
extern Cheat cheat;
|
||||
|
||||
enum class Model : uint {
|
||||
WonderSwan, //SW-001 (ASWAN)
|
||||
|
@ -17,37 +26,13 @@ namespace WonderSwan {
|
|||
|
||||
enum : uint { Byte = 1, Word = 2, Long = 4 };
|
||||
|
||||
struct Thread {
|
||||
~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
}
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65'536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
}
|
||||
|
||||
cothread_t thread = nullptr;
|
||||
uint frequency = 0;
|
||||
int64 clock = 0;
|
||||
};
|
||||
|
||||
#include <ws/memory/memory.hpp>
|
||||
#include <ws/eeprom/eeprom.hpp>
|
||||
#include <ws/system/system.hpp>
|
||||
#include <ws/scheduler/scheduler.hpp>
|
||||
#include <ws/cartridge/cartridge.hpp>
|
||||
#include <ws/cpu/cpu.hpp>
|
||||
#include <ws/ppu/ppu.hpp>
|
||||
#include <ws/apu/apu.hpp>
|
||||
#include <ws/cheat/cheat.hpp>
|
||||
}
|
||||
|
||||
#include <ws/interface/interface.hpp>
|
||||
|
|
|
@ -68,7 +68,7 @@ auto Icarus::import(string location) -> string {
|
|||
|
||||
if(type == ".fc" || type == ".nes") return famicomImport(buffer, location);
|
||||
if(type == ".sfc" || type == ".smc") return superFamicomImport(buffer, location);
|
||||
if(type == ".smd") return megaDriveImport(buffer, location);
|
||||
if(type == ".md") return megaDriveImport(buffer, location);
|
||||
if(type == ".gb") return gameBoyImport(buffer, location);
|
||||
if(type == ".gbc") return gameBoyColorImport(buffer, location);
|
||||
if(type == ".gba") return gameBoyAdvanceImport(buffer, location);
|
||||
|
|
|
@ -68,7 +68,7 @@ auto nall::main(string_vector args) -> void {
|
|||
if(string source = BrowserDialog()
|
||||
.setTitle("Load ROM Image")
|
||||
.setPath(settings["icarus/Path"].text())
|
||||
.setFilters("ROM Files|*.fc:*.nes:*.sfc:*.smc:*.smd:*.gb:*.gbc:*.gba:*.ws:*.wsc:*.bs:*.st:*.zip")
|
||||
.setFilters("ROM Files|*.fc:*.nes:*.sfc:*.smc:*.md:*.gb:*.gbc:*.gba:*.ws:*.wsc:*.bs:*.st:*.zip")
|
||||
.openFile()) {
|
||||
if(string target = icarus.import(source)) {
|
||||
settings["icarus/Path"].setValue(Location::path(source));
|
||||
|
|
Loading…
Reference in New Issue