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:
Tim Allen 2016-07-10 15:28:26 +10:00
parent 3dd1aa9c1b
commit 76a8ecd32a
83 changed files with 630 additions and 819 deletions

48
higan/emulator/cheat.hpp Normal file
View File

@ -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;
};
}

View File

@ -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"

View File

@ -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;
};
}

27
higan/emulator/thread.hpp Normal file
View File

@ -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;
};
}

View File

@ -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/)

View File

@ -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();

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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();
}

View File

@ -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));

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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/)

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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>

View File

@ -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 {

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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)

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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 {

View File

@ -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)

View File

@ -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());
}
}

View File

@ -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;

View File

@ -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 {
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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 {

View File

@ -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>
}

25
higan/md/psg/psg.cpp Normal file
View File

@ -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());
}
}

12
higan/md/psg/psg.hpp Normal file
View File

@ -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;

View File

@ -1,7 +0,0 @@
#include <md/md.hpp>
namespace MegaDrive {
Scheduler scheduler;
}

View File

@ -1,4 +0,0 @@
struct Scheduler {
};
extern Scheduler scheduler;

View File

@ -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);
}
}

View File

@ -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;
};

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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;
};
}

View File

@ -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;
}

View File

@ -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/)

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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())) {

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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));

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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>

View File

@ -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];

View File

@ -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();
}

View File

@ -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;

View File

@ -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/)

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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>

View File

@ -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);

View File

@ -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));