mirror of https://github.com/bsnes-emu/bsnes.git
Update to v097r03 release.
byuu says: So, this WIP starts work on something new for higan. Obviously, I can't keep it a secret until it's ready, because I want to continue daily WIP releases, and of course, solicit feedback as I go along.
This commit is contained in:
parent
344e63d928
commit
d7998b23ef
|
@ -4,6 +4,7 @@ fc := fc
|
|||
sfc := sfc
|
||||
gb := gb
|
||||
gba := gba
|
||||
ws := ws
|
||||
|
||||
profile := accuracy
|
||||
target := tomoko
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[Desktop Entry]
|
||||
Name=higan
|
||||
Comment=Nintendo emulator
|
||||
Comment=Emulator
|
||||
Exec=higan
|
||||
Icon=higan
|
||||
Terminal=false
|
||||
|
|
|
@ -6,7 +6,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "097.02";
|
||||
static const string Version = "097.03";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -7,6 +7,7 @@ processor_objects += $(if $(findstring r6502,$(processors)),processor-r6502)
|
|||
processor_objects += $(if $(findstring r65816,$(processors)),processor-r65816)
|
||||
processor_objects += $(if $(findstring spc700,$(processors)),processor-spc700)
|
||||
processor_objects += $(if $(findstring upd96050,$(processors)),processor-upd96050)
|
||||
processor_objects += $(if $(findstring v30mz,$(processors)),processor-v30mz)
|
||||
objects += $(processor_objects)
|
||||
|
||||
processor := processor
|
||||
|
@ -18,3 +19,4 @@ obj/processor-r6502.o: $(processor)/r6502/r6502.cpp $(call rwildcard,$(proces
|
|||
obj/processor-r65816.o: $(processor)/r65816/r65816.cpp $(call rwildcard,$(processor)/r65816)
|
||||
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)
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
#include <processor/processor.hpp>
|
||||
#include "v30mz.hpp"
|
||||
|
||||
namespace Processor {
|
||||
|
||||
auto V30MZ::exec() -> void {
|
||||
step(1);
|
||||
}
|
||||
|
||||
auto V30MZ::power() -> void {
|
||||
r.ax = 0x0000;
|
||||
r.cx = 0x0000;
|
||||
r.dx = 0x0000;
|
||||
r.bx = 0x0000;
|
||||
r.sp = 0x0000;
|
||||
r.bp = 0x0000;
|
||||
r.si = 0x0000;
|
||||
r.di = 0x0000;
|
||||
r.es = 0x0000;
|
||||
r.cs = 0xffff;
|
||||
r.ss = 0x0000;
|
||||
r.ds = 0x0000;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
//NEC V30MZ
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Processor {
|
||||
|
||||
struct V30MZ {
|
||||
virtual auto step(uint clocks) -> void = 0;
|
||||
virtual auto read(uint32 addr) -> uint8 = 0;
|
||||
virtual auto write(uint32 addr, uint8 data) -> void = 0;
|
||||
|
||||
auto exec() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
struct Registers {
|
||||
struct { uint16 ax; uint8 order_lsb2(al, ah); };
|
||||
struct { uint16 cx; uint8 order_lsb2(cl, ch); };
|
||||
struct { uint16 dx; uint8 order_lsb2(dl, dh); };
|
||||
struct { uint16 bx; uint8 order_lsb2(bl, bh); };
|
||||
uint16 sp;
|
||||
uint16 bp;
|
||||
uint16 si;
|
||||
uint16 di;
|
||||
uint16 es;
|
||||
uint16 cs;
|
||||
uint16 ss;
|
||||
uint16 ds;
|
||||
} r;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
system name:WonderSwan Color
|
|
@ -0,0 +1 @@
|
|||
system name:WonderSwan
|
|
@ -1,6 +1,6 @@
|
|||
name := higan
|
||||
|
||||
processors := arm gsu hg51b lr35902 r6502 r65816 spc700 upd96050
|
||||
processors := arm gsu hg51b lr35902 r6502 r65816 spc700 upd96050 v30mz
|
||||
include processor/GNUmakefile
|
||||
|
||||
include emulator/GNUmakefile
|
||||
|
@ -8,6 +8,7 @@ include fc/GNUmakefile
|
|||
include sfc/GNUmakefile
|
||||
include gb/GNUmakefile
|
||||
include gba/GNUmakefile
|
||||
include ws/GNUmakefile
|
||||
|
||||
ui_objects := ui-tomoko ui-program ui-configuration ui-input
|
||||
ui_objects += ui-settings ui-tools ui-presentation
|
||||
|
@ -90,12 +91,12 @@ else ifeq ($(platform),macosx)
|
|||
else ifneq ($(filter $(platform),linux bsd),)
|
||||
mkdir -p $(prefix)/bin/
|
||||
mkdir -p $(prefix)/share/icons/
|
||||
mkdir -p $(prefix)/$(name)/
|
||||
mkdir -p ~/Emulation/System/
|
||||
mkdir -p $(prefix)/share/$(name)/
|
||||
cp out/$(name) $(prefix)/bin/$(name)
|
||||
cp data/$(name).desktop $(prefix)/share/applications/$(name).desktop
|
||||
cp data/$(name).png $(prefix)/share/icons/$(name).png
|
||||
cp data/cheats.bml $(prefix)/$(name)/cheats.bml
|
||||
cp -R profile/* $(prefix)/$(name)/
|
||||
cp data/cheats.bml $(prefix)/share/$(name)/cheats.bml
|
||||
cp -R profile/* $(prefix)/share/$(name)/
|
||||
endif
|
||||
|
||||
uninstall:
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <sfc/interface/interface.hpp>
|
||||
#include <gb/interface/interface.hpp>
|
||||
#include <gba/interface/interface.hpp>
|
||||
#include <ws/interface/interface.hpp>
|
||||
#include "interface.cpp"
|
||||
#include "media.cpp"
|
||||
#include "state.cpp"
|
||||
|
@ -17,6 +18,7 @@ Program::Program(lstring args) {
|
|||
emulators.append(new SuperFamicom::Interface);
|
||||
emulators.append(new GameBoy::Interface);
|
||||
emulators.append(new GameBoyAdvance::Interface);
|
||||
emulators.append(new WonderSwan::Interface);
|
||||
for(auto& emulator : emulators) emulator->bind = this;
|
||||
|
||||
new InputManager;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
unique_pointer<Video> video;
|
||||
unique_pointer<Audio> audio;
|
||||
unique_pointer<Input> input;
|
||||
unique_pointer<Emulator::Interface> emulator;
|
||||
Emulator::Interface* emulator = nullptr;
|
||||
|
||||
auto locate(string name) -> string {
|
||||
string location = {programpath(), name};
|
||||
|
|
|
@ -9,7 +9,7 @@ extern unique_pointer<Audio> audio;
|
|||
extern unique_pointer<Input> input;
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
extern unique_pointer<Emulator::Interface> emulator;
|
||||
extern Emulator::Interface* emulator;
|
||||
|
||||
#include "program/program.hpp"
|
||||
#include "configuration/configuration.hpp"
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
ws_objects := ws-interface ws-system ws-scheduler
|
||||
ws_objects += ws-memory ws-cartridge
|
||||
ws_objects += ws-cpu ws-ppu ws-apu
|
||||
objects += $(ws_objects)
|
||||
|
||||
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-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/)
|
|
@ -0,0 +1,31 @@
|
|||
#include <ws/ws.hpp>
|
||||
|
||||
namespace WonderSwan {
|
||||
|
||||
APU apu;
|
||||
|
||||
auto APU::Enter() -> void {
|
||||
apu.main();
|
||||
}
|
||||
|
||||
auto APU::main() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
step(128);
|
||||
interface->audioSample(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::step(uint clocks) -> void {
|
||||
clock += clocks;
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
}
|
||||
|
||||
auto APU::power() -> void {
|
||||
create(APU::Enter, 3072000);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
struct APU : Thread {
|
||||
static auto Enter() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto power() -> void;
|
||||
};
|
||||
|
||||
extern APU apu;
|
|
@ -0,0 +1,60 @@
|
|||
#include <ws/ws.hpp>
|
||||
|
||||
namespace WonderSwan {
|
||||
|
||||
Cartridge cartridge;
|
||||
#include "memory.cpp"
|
||||
|
||||
auto Cartridge::loaded() const -> bool {
|
||||
return _loaded;
|
||||
}
|
||||
|
||||
auto Cartridge::load() -> void {
|
||||
information.manifest = "";
|
||||
information.title = "";
|
||||
information.sha256 = "";
|
||||
|
||||
interface->loadRequest(ID::Manifest, "manifest.bml", true);
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
|
||||
if(auto node = document["board/rom"]) {
|
||||
rom.name = node["name"].text();
|
||||
rom.size = node["size"].natural();
|
||||
if(rom.size) rom.data = new uint8[rom.size]();
|
||||
if(rom.name) interface->loadRequest(ID::ROM, rom.name, true);
|
||||
}
|
||||
|
||||
if(auto node = document["board/ram"]) {
|
||||
ram.name = node["name"].text();
|
||||
ram.size = node["size"].natural();
|
||||
if(ram.size) ram.data = new uint8[ram.size]();
|
||||
if(ram.name) interface->loadRequest(ID::RAM, ram.name, true);
|
||||
}
|
||||
|
||||
information.title = document["information/title"].text();
|
||||
information.sha256 = Hash::SHA256(rom.data, rom.size).digest();
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
auto Cartridge::unload() -> void {
|
||||
_loaded = false;
|
||||
|
||||
delete[] rom.data;
|
||||
rom.data = nullptr;
|
||||
rom.size = 0;
|
||||
rom.name = "";
|
||||
|
||||
delete[] ram.data;
|
||||
ram.data = nullptr;
|
||||
ram.size = 0;
|
||||
ram.name = "";
|
||||
}
|
||||
|
||||
auto Cartridge::power() -> void {
|
||||
r.bank_rom0 = 0x00;
|
||||
r.bank_rom1 = 0x00;
|
||||
r.bank_rom2 = 0x00;
|
||||
r.bank_sram = 0x00;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
struct Cartridge {
|
||||
auto loaded() const -> bool;
|
||||
|
||||
auto load() -> void;
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto romRead(uint addr) -> uint8;
|
||||
auto romWrite(uint addr, uint8 data) -> void;
|
||||
|
||||
auto ramRead(uint addr) -> uint8;
|
||||
auto ramWrite(uint addr, uint8 data) -> void;
|
||||
|
||||
struct Registers {
|
||||
uint8 bank_rom0;
|
||||
uint8 bank_rom1;
|
||||
uint8 bank_rom2;
|
||||
uint8 bank_sram;
|
||||
} r;
|
||||
|
||||
struct Memory {
|
||||
uint8* data = nullptr;
|
||||
uint size = 0;
|
||||
string name;
|
||||
} rom, ram;
|
||||
|
||||
struct Information {
|
||||
string manifest;
|
||||
string title;
|
||||
string sha256;
|
||||
} information;
|
||||
|
||||
privileged:
|
||||
bool _loaded = false;
|
||||
};
|
||||
|
||||
extern Cartridge cartridge;
|
|
@ -0,0 +1,26 @@
|
|||
//20000-fffff
|
||||
auto Cartridge::romRead(uint addr) -> uint8 {
|
||||
switch(addr >> 16) {
|
||||
case 2: addr = r.bank_rom0 << 16 | (uint16)addr; break; //20000-2ffff
|
||||
case 3: addr = r.bank_rom1 << 16 | (uint16)addr; break; //30000-3ffff
|
||||
default: addr = r.bank_rom2 << 16 | (uint16)addr; break; //40000-fffff
|
||||
}
|
||||
if(!rom.data || addr >= rom.size) return 0x00;
|
||||
return rom.data[addr];
|
||||
}
|
||||
|
||||
auto Cartridge::romWrite(uint addr, uint8 data) -> void {
|
||||
}
|
||||
|
||||
//10000-1ffff
|
||||
auto Cartridge::ramRead(uint addr) -> uint8 {
|
||||
addr = r.bank_sram << 16 | (uint16)addr;
|
||||
if(!ram.data || addr >= ram.size) return 0x00;
|
||||
return ram.data[addr];
|
||||
}
|
||||
|
||||
auto Cartridge::ramWrite(uint addr, uint8 data) -> void {
|
||||
addr = r.bank_sram << 16 | (uint16)addr;
|
||||
if(!ram.data || addr >= ram.size) return;
|
||||
ram.data[addr] = data;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
#include <ws/ws.hpp>
|
||||
|
||||
namespace WonderSwan {
|
||||
|
||||
CPU cpu;
|
||||
#include "memory.cpp"
|
||||
|
||||
auto CPU::Enter() -> void {
|
||||
cpu.main();
|
||||
}
|
||||
|
||||
auto CPU::main() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
exec();
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
ppu.clock -= clocks;
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
|
||||
apu.clock -= clocks;
|
||||
if(apu.clock < 0) co_switch(apu.thread);
|
||||
}
|
||||
|
||||
auto CPU::read(uint32 addr) -> uint8 {
|
||||
return bus.read(addr);
|
||||
}
|
||||
|
||||
auto CPU::write(uint32 addr, uint8 data) -> void {
|
||||
return bus.write(addr, data);
|
||||
}
|
||||
|
||||
auto CPU::power() -> void {
|
||||
V30MZ::power();
|
||||
create(CPU::Enter, 3072000);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
struct CPU : Processor::V30MZ, Thread {
|
||||
static auto Enter() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void override;
|
||||
auto read(uint32 addr) -> uint8 override;
|
||||
auto write(uint32 addr, uint8 data) -> void override;
|
||||
auto power() -> void;
|
||||
|
||||
auto ramRead(uint addr) -> uint8;
|
||||
auto ramWrite(uint addr, uint8 data) -> void;
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
|
@ -0,0 +1,9 @@
|
|||
auto CPU::ramRead(uint addr) -> uint8 {
|
||||
uint mask = system.monochrome() ? 0x3fff : 0xffff;
|
||||
return iram[addr & mask];
|
||||
}
|
||||
|
||||
auto CPU::ramWrite(uint addr, uint8 data) -> void {
|
||||
uint mask = system.monochrome() ? 0x3fff : 0xffff;
|
||||
iram[addr & mask] = data;
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
#include <ws/ws.hpp>
|
||||
|
||||
namespace WonderSwan {
|
||||
|
||||
Interface* interface = nullptr;
|
||||
Settings settings;
|
||||
|
||||
Interface::Interface() {
|
||||
interface = this;
|
||||
|
||||
information.name = "WonderSwan";
|
||||
information.width = 224; //note: technically 224x144; but screen can be rotated
|
||||
information.height = 224; //by using a square size; this can be done in the core
|
||||
information.overscan = false;
|
||||
information.aspectRatio = 1.0;
|
||||
information.resettable = false;
|
||||
|
||||
information.capability.states = false;
|
||||
information.capability.cheats = false;
|
||||
|
||||
media.append({ID::WonderSwan, "WonderSwan", "ws", true});
|
||||
media.append({ID::WonderSwanColor, "WonderSwan Color", "wsc", true});
|
||||
|
||||
{ Device device{0, ID::Device, "Controller"};
|
||||
device.input.append({ 0, 0, "X1" });
|
||||
device.input.append({ 1, 0, "X2" });
|
||||
device.input.append({ 2, 0, "X3" });
|
||||
device.input.append({ 3, 0, "X4" });
|
||||
device.input.append({ 4, 0, "Y1" });
|
||||
device.input.append({ 5, 0, "Y2" });
|
||||
device.input.append({ 6, 0, "Y3" });
|
||||
device.input.append({ 7, 0, "Y4" });
|
||||
device.input.append({ 8, 0, "B" });
|
||||
device.input.append({ 9, 0, "A" });
|
||||
device.input.append({10, 0, "Sound"});
|
||||
device.input.append({11, 0, "Start"});
|
||||
device.order = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
||||
this->device.append(device);
|
||||
}
|
||||
|
||||
port.append({0, "Device", {device[0]}});
|
||||
}
|
||||
|
||||
auto Interface::manifest() -> string {
|
||||
return cartridge.information.manifest;
|
||||
}
|
||||
|
||||
auto Interface::title() -> string {
|
||||
return cartridge.information.title;
|
||||
}
|
||||
|
||||
auto Interface::videoFrequency() -> double {
|
||||
return 3072000.0 / (159.0 * 256.0); //~75.47hz
|
||||
}
|
||||
|
||||
auto Interface::audioFrequency() -> double {
|
||||
return 3072000.0 / 128.0; //24Khz
|
||||
}
|
||||
|
||||
auto Interface::loaded() -> bool {
|
||||
return cartridge.loaded();
|
||||
}
|
||||
|
||||
auto Interface::sha256() -> string {
|
||||
return cartridge.information.sha256;
|
||||
}
|
||||
|
||||
auto Interface::group(uint id) -> uint {
|
||||
switch(id) {
|
||||
case ID::SystemManifest:
|
||||
return 0;
|
||||
case ID::Manifest:
|
||||
case ID::ROM:
|
||||
case ID::RAM:
|
||||
switch(system.revision()) {
|
||||
case System::Revision::WonderSwan:
|
||||
return ID::WonderSwan;
|
||||
case System::Revision::WonderSwanColor:
|
||||
case System::Revision::SwanCrystal:
|
||||
return ID::WonderSwanColor;
|
||||
}
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
auto Interface::load(uint id) -> void {
|
||||
if(id == ID::WonderSwan) system.load(System::Revision::WonderSwan);
|
||||
if(id == ID::WonderSwanColor) system.load(System::Revision::WonderSwanColor);
|
||||
}
|
||||
|
||||
auto Interface::save() -> void {
|
||||
if(cartridge.ram.name) interface->saveRequest(ID::RAM, cartridge.ram.name);
|
||||
}
|
||||
|
||||
auto Interface::load(uint id, const stream& stream) -> void {
|
||||
if(id == ID::SystemManifest) {
|
||||
system.information.manifest = stream.text();
|
||||
}
|
||||
|
||||
if(id == ID::Manifest) {
|
||||
cartridge.information.manifest = stream.text();
|
||||
}
|
||||
|
||||
if(id == ID::ROM) {
|
||||
stream.read(cartridge.rom.data, min(cartridge.rom.size, stream.size()));
|
||||
}
|
||||
|
||||
if(id == ID::RAM) {
|
||||
stream.read(cartridge.ram.data, min(cartridge.ram.size, stream.size()));
|
||||
}
|
||||
}
|
||||
|
||||
auto Interface::save(uint id, const stream& stream) -> void {
|
||||
if(id == ID::RAM) {
|
||||
stream.write(cartridge.ram.data, cartridge.ram.size);
|
||||
}
|
||||
}
|
||||
|
||||
auto Interface::unload() -> void {
|
||||
save();
|
||||
}
|
||||
|
||||
auto Interface::power() -> void {
|
||||
system.power();
|
||||
}
|
||||
|
||||
auto Interface::run() -> void {
|
||||
system.run();
|
||||
}
|
||||
|
||||
auto Interface::serialize() -> serializer {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Interface::unserialize(serializer& s) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::cap(const string& name) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::get(const string& name) -> any {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Interface::set(const string& name, const any& value) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
namespace WonderSwan {
|
||||
|
||||
struct ID {
|
||||
enum : uint {
|
||||
System,
|
||||
WonderSwan,
|
||||
WonderSwanColor,
|
||||
};
|
||||
|
||||
enum : uint {
|
||||
SystemManifest,
|
||||
|
||||
Manifest,
|
||||
ROM,
|
||||
RAM,
|
||||
};
|
||||
|
||||
enum : uint {
|
||||
Device = 1,
|
||||
};
|
||||
};
|
||||
|
||||
struct Interface : Emulator::Interface {
|
||||
Interface();
|
||||
|
||||
auto manifest() -> string override;
|
||||
auto title() -> string override;
|
||||
auto videoFrequency() -> double override;
|
||||
auto audioFrequency() -> double override;
|
||||
|
||||
auto loaded() -> bool override;
|
||||
auto sha256() -> string override;
|
||||
auto group(uint id) -> uint override;
|
||||
auto load(uint id) -> void override;
|
||||
auto save() -> void override;
|
||||
auto load(uint id, const stream& stream) -> void override;
|
||||
auto save(uint id, const stream& stream) -> void override;
|
||||
auto unload() -> void override;
|
||||
|
||||
auto power() -> void override;
|
||||
auto run() -> void override;
|
||||
|
||||
auto serialize() -> serializer override;
|
||||
auto unserialize(serializer&) -> bool override;
|
||||
|
||||
auto cap(const string& name) -> bool override;
|
||||
auto get(const string& name) -> any override;
|
||||
auto set(const string& name, const any& value) -> bool override;
|
||||
|
||||
private:
|
||||
vector<Device> device;
|
||||
};
|
||||
|
||||
struct Settings {
|
||||
};
|
||||
|
||||
extern Interface* interface;
|
||||
extern Settings settings;
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#include <ws/ws.hpp>
|
||||
|
||||
namespace WonderSwan {
|
||||
|
||||
uint8 iram[64 * 1024] = {0};
|
||||
IO* io[256];
|
||||
Bus bus;
|
||||
|
||||
auto IO::power() -> void {
|
||||
static IO unmapped;
|
||||
for(auto& n : io) n = &unmapped;
|
||||
}
|
||||
|
||||
auto Bus::read(uint20 addr) -> uint8 {
|
||||
if(addr < 0x10000) return cpu.ramRead(addr);
|
||||
if(addr < 0x20000) return cartridge.ramRead(addr);
|
||||
return cartridge.romRead(addr);
|
||||
}
|
||||
|
||||
auto Bus::write(uint20 addr, uint8 data) -> void {
|
||||
if(addr < 0x10000) return cpu.ramWrite(addr, data);
|
||||
if(addr < 0x20000) return cartridge.ramWrite(addr, data);
|
||||
return cartridge.romWrite(addr, data);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
struct IO {
|
||||
static auto power() -> void;
|
||||
|
||||
virtual auto in(uint8 addr) -> uint8 { return 0x00; }
|
||||
virtual auto out(uint8 addr, uint8 data) -> void {}
|
||||
};
|
||||
|
||||
struct Bus {
|
||||
auto read(uint20 addr) -> uint8;
|
||||
auto write(uint20 addr, uint8 data) -> void;
|
||||
};
|
||||
|
||||
extern uint8 iram[64 * 1024];
|
||||
extern IO* io[256];
|
||||
extern Bus bus;
|
|
@ -0,0 +1,44 @@
|
|||
#include <ws/ws.hpp>
|
||||
|
||||
namespace WonderSwan {
|
||||
|
||||
PPU ppu;
|
||||
#include "video.cpp"
|
||||
|
||||
auto PPU::Enter() -> void {
|
||||
ppu.main();
|
||||
}
|
||||
|
||||
auto PPU::main() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
step(256);
|
||||
|
||||
status.hclk = 0;
|
||||
if(++status.vclk == 159) {
|
||||
status.vclk = 0;
|
||||
video.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::step(uint clocks) -> void {
|
||||
status.hclk += clocks;
|
||||
|
||||
clock += clocks;
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
}
|
||||
|
||||
auto PPU::power() -> void {
|
||||
create(PPU::Enter, 3072000);
|
||||
|
||||
for(auto& n : output) n = 0;
|
||||
|
||||
status.vclk = 0;
|
||||
status.hclk = 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#include "video.hpp"
|
||||
|
||||
struct PPU : Thread {
|
||||
static auto Enter() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
uint16 output[224 * 144] = {0};
|
||||
|
||||
struct Status {
|
||||
uint vclk;
|
||||
uint hclk;
|
||||
} status;
|
||||
};
|
||||
|
||||
extern PPU ppu;
|
|
@ -0,0 +1,49 @@
|
|||
Video video;
|
||||
|
||||
Video::Video() {
|
||||
output = new uint32[224 * 224];
|
||||
paletteLiteral = new uint32[1 << 12];
|
||||
paletteStandard = new uint32[1 << 12];
|
||||
}
|
||||
|
||||
auto Video::power() -> void {
|
||||
memory::fill(output(), 224 * 224 * sizeof(uint32));
|
||||
|
||||
if(system.monochrome()) {
|
||||
for(auto color : range(16)) {
|
||||
paletteLiteral[color] = color;
|
||||
|
||||
uint L = image::normalize(15 - color, 4, 16);
|
||||
paletteStandard[color] = interface->videoColor(L, L, L);
|
||||
}
|
||||
}
|
||||
|
||||
if(system.color()) {
|
||||
for(auto color : range(1 << 12)) {
|
||||
paletteLiteral[color] = color;
|
||||
|
||||
uint R = (uint4)(color >> 8);
|
||||
uint G = (uint4)(color >> 4);
|
||||
uint B = (uint4)(color >> 0);
|
||||
|
||||
R = image::normalize(R, 4, 16);
|
||||
G = image::normalize(G, 4, 16);
|
||||
B = image::normalize(B, 4, 16);
|
||||
paletteStandard[color] = interface->videoColor(R, G, B);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Video::refresh() -> void {
|
||||
for(uint y = 0; y < 144; y++) {
|
||||
auto source = ppu.output + y * 224;
|
||||
auto target = output() + y * 224;
|
||||
for(uint x = 0; x < 224; x++) {
|
||||
auto color = paletteStandard[*source++];
|
||||
*target++ = color;
|
||||
}
|
||||
}
|
||||
|
||||
interface->videoRefresh(output(), 224 * sizeof(uint32), 224, 224);
|
||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
struct Video {
|
||||
Video();
|
||||
|
||||
auto power() -> void;
|
||||
auto refresh() -> void;
|
||||
|
||||
private:
|
||||
unique_pointer<uint32[]> output;
|
||||
unique_pointer<uint32[]> paletteLiteral;
|
||||
unique_pointer<uint32[]> paletteStandard;
|
||||
};
|
||||
|
||||
extern Video video;
|
|
@ -0,0 +1,23 @@
|
|||
#include <ws/ws.hpp>
|
||||
|
||||
namespace WonderSwan {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
auto Scheduler::enter() -> void {
|
||||
host = co_active();
|
||||
co_switch(active);
|
||||
}
|
||||
|
||||
auto Scheduler::exit(ExitReason reason) -> void {
|
||||
exitReason = reason;
|
||||
active = co_active();
|
||||
co_switch(host);
|
||||
}
|
||||
|
||||
auto Scheduler::power() -> void {
|
||||
host = co_active();
|
||||
active = cpu.thread;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
struct Scheduler {
|
||||
enum class SynchronizeMode : uint { None, CPU, All };
|
||||
enum class ExitReason : uint { UnknownEvent, FrameEvent, SynchronizeEvent };
|
||||
|
||||
auto enter() -> void;
|
||||
auto exit(ExitReason reason) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
cothread_t host = nullptr;
|
||||
cothread_t active = nullptr;
|
||||
SynchronizeMode sync = SynchronizeMode::None;
|
||||
ExitReason exitReason = ExitReason::UnknownEvent;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
|
@ -0,0 +1,50 @@
|
|||
#include <ws/ws.hpp>
|
||||
|
||||
namespace WonderSwan {
|
||||
|
||||
System system;
|
||||
|
||||
auto System::revision() const -> Revision {
|
||||
return _revision;
|
||||
}
|
||||
|
||||
auto System::monochrome() const -> bool {
|
||||
return revision() == Revision::WonderSwan;
|
||||
}
|
||||
|
||||
auto System::color() const -> bool {
|
||||
return revision() != Revision::WonderSwan;
|
||||
}
|
||||
|
||||
auto System::init() -> void {
|
||||
}
|
||||
|
||||
auto System::term() -> void {
|
||||
}
|
||||
|
||||
auto System::load(Revision revision) -> void {
|
||||
_revision = revision;
|
||||
|
||||
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
|
||||
cartridge.load();
|
||||
}
|
||||
|
||||
auto System::power() -> void {
|
||||
IO::power();
|
||||
cpu.power();
|
||||
ppu.power();
|
||||
apu.power();
|
||||
cartridge.power();
|
||||
scheduler.power();
|
||||
}
|
||||
|
||||
auto System::run() -> void {
|
||||
while(true) {
|
||||
scheduler.enter();
|
||||
if(scheduler.exitReason == Scheduler::ExitReason::FrameEvent) break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
struct System {
|
||||
enum class Revision : uint { WonderSwan, WonderSwanColor, SwanCrystal };
|
||||
|
||||
auto revision() const -> Revision;
|
||||
auto monochrome() const -> bool;
|
||||
auto color() const -> bool;
|
||||
|
||||
auto init() -> void;
|
||||
auto term() -> void;
|
||||
auto load(Revision) -> void;
|
||||
auto power() -> void;
|
||||
auto run() -> void;
|
||||
|
||||
struct Information {
|
||||
string manifest;
|
||||
} information;
|
||||
|
||||
privileged:
|
||||
Revision _revision;
|
||||
};
|
||||
|
||||
extern System system;
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <processor/v30mz/v30mz.hpp>
|
||||
|
||||
namespace WonderSwan {
|
||||
namespace Info {
|
||||
static const string Name = "bws";
|
||||
static const uint SerializerVersion = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
bws - WonderSwan, WonderSwan Color, and SwanCrystal emulator
|
||||
author: byuu
|
||||
license: GPLv3
|
||||
project started: 2016-01-26
|
||||
*/
|
||||
|
||||
#include <libco/libco.h>
|
||||
|
||||
namespace WonderSwan {
|
||||
struct Thread {
|
||||
~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
}
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65536 * 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/system/system.hpp>
|
||||
#include <ws/scheduler/scheduler.hpp>
|
||||
#include <ws/memory/memory.hpp>
|
||||
#include <ws/cartridge/cartridge.hpp>
|
||||
#include <ws/cpu/cpu.hpp>
|
||||
#include <ws/ppu/ppu.hpp>
|
||||
#include <ws/apu/apu.hpp>
|
||||
}
|
||||
|
||||
#include <ws/interface/interface.hpp>
|
Loading…
Reference in New Issue