mirror of https://github.com/bsnes-emu/bsnes.git
Update to v097r28 release.
byuu says: Changelog: (all WSC unless otherwise noted) - fixed LINECMP=0 interrupt case (fixes FF4 world map during airship sequence) - improved CPU timing (fixes Magical Drop flickering and FF1 battle music) - added per-frame OAM caching (fixes sprite glitchiness in Magical Drop, Riviera, etc.) - added RTC emulation (fixes Dicing Knight and Judgement Silversword) - added save state support - added cheat code support (untested because I don't know of any cheat codes that exist for this system) - icarus: can now detect games with RTC chips - SFC: bugfix to SharpRTC emulation (Dai Kaijuu Monogatari II) - ( I was adding the extra leap year day to all 12 months instead of just February ... >_< ) Note that the RTC emulation is very incomplete. It's not really documented at all, and the two games I've tried that use it never even ask you to set the date/time (so they're probably just using it to count seconds.) I'm not even sure if I've implement the level-sensitive behavior correctly (actually, now that I think about it, I need to mask the clear bit in INT_ACK for the level-sensitive interrupts ...) A bit worried about the RTC alarm, because it seems like it'll fire continuously for a full minute. Or even if you turn it off after it fires, then that doesn't seem to be lowering the line until the next second ticks on the RTC, so that likely needs to happen when changing the alarm flag. Also not sure on this RTC's weekday byte. On the SharpRTC, it actually computes this for you. Because it's not at all an easy thing to calculate yourself in 65816 or V30MZ assembler. About 40 lines of code to do it in C. For now, I'm requiring the program to calculate the value itself. Also note that there's some gibberish tiles in Judgement Silversword, sadly. Not sure what's up there, but the game's still fully playable at least. Finally, no surprise: Beat-Mania doesn't run :P
This commit is contained in:
parent
d3413db04a
commit
379ab6991f
|
@ -6,7 +6,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "097.27";
|
static const string Version = "097.28";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "http://byuu.org/";
|
static const string Website = "http://byuu.org/";
|
||||||
|
|
|
@ -47,7 +47,7 @@ auto System::serializeAll(serializer& s) -> void {
|
||||||
auto System::serializeInit() -> void {
|
auto System::serializeInit() -> void {
|
||||||
serializer s;
|
serializer s;
|
||||||
|
|
||||||
uint signature = 0, version = 0, crc32 = 0;
|
uint signature = 0, version = 0;
|
||||||
char hash[64], description[512];
|
char hash[64], description[512];
|
||||||
|
|
||||||
s.integer(signature);
|
s.integer(signature);
|
||||||
|
|
|
@ -1,32 +1,35 @@
|
||||||
auto V30MZ::read(Size size, uint16 segment, uint16 address) -> uint32 {
|
auto V30MZ::read(Size size, uint16 segment, uint16 address) -> uint32 {
|
||||||
uint32 data = read(segment * 16 + address);
|
uint32 data;
|
||||||
if(size == Word) data |= read(segment * 16 + ++address) << 8;
|
if(size >= Byte) data.byte(0) = read(segment * 16 + address++);
|
||||||
if(size == Long) data |= read(segment * 16 + ++address) << 16;
|
if(size >= Word) data.byte(1) = read(segment * 16 + address++);
|
||||||
if(size == Long) data |= read(segment * 16 + ++address) << 24;
|
if(size >= Long) data.byte(2) = read(segment * 16 + address++);
|
||||||
|
if(size >= Long) data.byte(3) = read(segment * 16 + address++);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto V30MZ::write(Size size, uint16 segment, uint16 address, uint16 data) -> void {
|
auto V30MZ::write(Size size, uint16 segment, uint16 address, uint16 data) -> void {
|
||||||
write(segment * 16 + address, data);
|
if(size >= Byte) write(segment * 16 + address++, data.byte(0));
|
||||||
if(size == Word) write(segment * 16 + ++address, data >> 8);
|
if(size >= Word) write(segment * 16 + address++, data.byte(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
auto V30MZ::in(Size size, uint16 address) -> uint16 {
|
auto V30MZ::in(Size size, uint16 address) -> uint16 {
|
||||||
uint16 data = in(address);
|
uint16 data;
|
||||||
if(size == Word) data |= in(++address) << 8;
|
if(size >= Byte) data.byte(0) = in(address++);
|
||||||
|
if(size >= Word) data.byte(1) = in(address++);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto V30MZ::out(Size size, uint16 address, uint16 data) -> void {
|
auto V30MZ::out(Size size, uint16 address, uint16 data) -> void {
|
||||||
out(address, data);
|
if(size >= Byte) out(address++, data.byte(0));
|
||||||
if(size == Word) out(++address, data >> 8);
|
if(size >= Word) out(address++, data.byte(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
auto V30MZ::fetch(Size size) -> uint16 {
|
auto V30MZ::fetch(Size size) -> uint16 {
|
||||||
|
wait(size);
|
||||||
uint16 data = read(size, r.cs, r.ip);
|
uint16 data = read(size, r.cs, r.ip);
|
||||||
return r.ip += size, data;
|
return r.ip += size, data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
auto V30MZ::serialize(serializer& s) -> void {
|
||||||
|
s.integer(state.halt);
|
||||||
|
s.integer(state.poll);
|
||||||
|
s.integer(state.prefix);
|
||||||
|
|
||||||
|
s.integer(opcode);
|
||||||
|
if(s.mode() == serializer::Save) {
|
||||||
|
uint8 _prefixes[7] = {0};
|
||||||
|
uint8 _prefixCount = prefixes.size();
|
||||||
|
for(auto n : range(prefixes)) _prefixes[n] = prefixes[n];
|
||||||
|
s.integer(_prefixCount);
|
||||||
|
s.array(_prefixes);
|
||||||
|
} else {
|
||||||
|
uint8 _prefixes[7] = {0};
|
||||||
|
uint8 _prefixCount = 0;
|
||||||
|
s.integer(_prefixCount);
|
||||||
|
s.array(_prefixes);
|
||||||
|
prefixes.resize(_prefixCount);
|
||||||
|
for(auto n : range(prefixes)) prefixes[n] = _prefixes[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
s.integer(modrm.mod);
|
||||||
|
s.integer(modrm.reg);
|
||||||
|
s.integer(modrm.mem);
|
||||||
|
s.integer(modrm.segment);
|
||||||
|
s.integer(modrm.address);
|
||||||
|
|
||||||
|
s.integer(r.ax);
|
||||||
|
s.integer(r.cx);
|
||||||
|
s.integer(r.dx);
|
||||||
|
s.integer(r.bx);
|
||||||
|
s.integer(r.sp);
|
||||||
|
s.integer(r.bp);
|
||||||
|
s.integer(r.si);
|
||||||
|
s.integer(r.di);
|
||||||
|
s.integer(r.es);
|
||||||
|
s.integer(r.cs);
|
||||||
|
s.integer(r.ss);
|
||||||
|
s.integer(r.ds);
|
||||||
|
s.integer(r.ip);
|
||||||
|
s.integer(r.f.m);
|
||||||
|
s.integer(r.f.v);
|
||||||
|
s.integer(r.f.d);
|
||||||
|
s.integer(r.f.i);
|
||||||
|
s.integer(r.f.b);
|
||||||
|
s.integer(r.f.s);
|
||||||
|
s.integer(r.f.z);
|
||||||
|
s.integer(r.f.h);
|
||||||
|
s.integer(r.f.p);
|
||||||
|
s.integer(r.f.c);
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ namespace Processor {
|
||||||
#include "instructions-misc.cpp"
|
#include "instructions-misc.cpp"
|
||||||
#include "instructions-move.cpp"
|
#include "instructions-move.cpp"
|
||||||
#include "instructions-string.cpp"
|
#include "instructions-string.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
#include "disassembler.cpp"
|
#include "disassembler.cpp"
|
||||||
|
|
||||||
auto V30MZ::debug(string text) -> void {
|
auto V30MZ::debug(string text) -> void {
|
||||||
|
@ -53,6 +54,8 @@ auto V30MZ::exec() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto V30MZ::interrupt(uint8 vector) -> void {
|
auto V30MZ::interrupt(uint8 vector) -> void {
|
||||||
|
wait(32);
|
||||||
|
|
||||||
state.halt = false;
|
state.halt = false;
|
||||||
state.poll = true;
|
state.poll = true;
|
||||||
state.prefix = false;
|
state.prefix = false;
|
||||||
|
@ -81,10 +84,7 @@ auto V30MZ::interrupt(uint8 vector) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto V30MZ::instruction() -> void {
|
auto V30MZ::instruction() -> void {
|
||||||
opcode = fetch();
|
switch(opcode = fetch()) {
|
||||||
wait(1);
|
|
||||||
|
|
||||||
switch(opcode) {
|
|
||||||
case 0x00: return opAddMemReg(Byte);
|
case 0x00: return opAddMemReg(Byte);
|
||||||
case 0x01: return opAddMemReg(Word);
|
case 0x01: return opAddMemReg(Word);
|
||||||
case 0x02: return opAddRegMem(Byte);
|
case 0x02: return opAddRegMem(Byte);
|
||||||
|
|
|
@ -203,6 +203,9 @@ struct V30MZ {
|
||||||
auto opLoadString(Size);
|
auto opLoadString(Size);
|
||||||
auto opScanString(Size);
|
auto opScanString(Size);
|
||||||
|
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
//disassembler.cpp
|
//disassembler.cpp
|
||||||
auto disassemble(uint16 cs, uint16 ip, bool registers = true, bool bytes = true) -> string;
|
auto disassemble(uint16 cs, uint16 ip, bool registers = true, bool bytes = true) -> string;
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,12 @@ auto SharpRTC::tick_hour() -> void {
|
||||||
auto SharpRTC::tick_day() -> void {
|
auto SharpRTC::tick_day() -> void {
|
||||||
uint days = daysinmonth[month % 12];
|
uint days = daysinmonth[month % 12];
|
||||||
|
|
||||||
//add one day for leap years
|
//add one day in February for leap years
|
||||||
if(year % 400 == 0) days++;
|
if(month == 1) {
|
||||||
else if(year % 100 == 0);
|
if(year % 400 == 0) days++;
|
||||||
else if(year % 4 == 0) days++;
|
else if(year % 100 == 0);
|
||||||
|
else if(year % 4 == 0) days++;
|
||||||
|
}
|
||||||
|
|
||||||
if(day++ < days) return;
|
if(day++ < days) return;
|
||||||
day = 1;
|
day = 1;
|
||||||
|
|
|
@ -3,6 +3,7 @@ processors += v30mz
|
||||||
objects += ws-interface ws-system ws-scheduler
|
objects += ws-interface ws-system ws-scheduler
|
||||||
objects += ws-memory ws-eeprom ws-cartridge
|
objects += ws-memory ws-eeprom ws-cartridge
|
||||||
objects += ws-cpu ws-ppu ws-apu
|
objects += ws-cpu ws-ppu ws-apu
|
||||||
|
objects += ws-cheat
|
||||||
|
|
||||||
obj/ws-interface.o: ws/interface/interface.cpp $(call rwildcard,ws/interface/)
|
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-system.o: ws/system/system.cpp $(call rwildcard,ws/system/)
|
||||||
|
@ -13,3 +14,4 @@ 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-cpu.o: ws/cpu/cpu.cpp $(call rwildcard,ws/cpu/)
|
||||||
obj/ws-ppu.o: ws/ppu/ppu.cpp $(call rwildcard,ws/ppu/)
|
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-apu.o: ws/apu/apu.cpp $(call rwildcard,ws/apu/)
|
||||||
|
obj/ws-cheat.o: ws/cheat/cheat.cpp $(call rwildcard,ws/cheat/)
|
||||||
|
|
|
@ -10,6 +10,7 @@ APU apu;
|
||||||
#include "channel3.cpp"
|
#include "channel3.cpp"
|
||||||
#include "channel4.cpp"
|
#include "channel4.cpp"
|
||||||
#include "channel5.cpp"
|
#include "channel5.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
|
|
||||||
auto APU::Enter() -> void {
|
auto APU::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), apu.main();
|
while(true) scheduler.synchronize(), apu.main();
|
||||||
|
|
|
@ -10,10 +10,23 @@ struct APU : Thread, IO {
|
||||||
auto portRead(uint16 addr) -> uint8;
|
auto portRead(uint16 addr) -> uint8;
|
||||||
auto portWrite(uint16 addr, uint8 data) -> void;
|
auto portWrite(uint16 addr, uint8 data) -> void;
|
||||||
|
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
uint13 sweepClock;
|
uint13 sweepClock;
|
||||||
} s;
|
} s;
|
||||||
|
|
||||||
|
struct Registers {
|
||||||
|
//$008f SND_WAVE_BASE
|
||||||
|
uint8 waveBase;
|
||||||
|
|
||||||
|
//$0091 SND_OUTPUT
|
||||||
|
uint1 speakerEnable;
|
||||||
|
uint2 speakerShift;
|
||||||
|
uint1 headphoneEnable;
|
||||||
|
} r;
|
||||||
|
|
||||||
struct DMA {
|
struct DMA {
|
||||||
auto run() -> void;
|
auto run() -> void;
|
||||||
|
|
||||||
|
@ -40,30 +53,6 @@ struct APU : Thread, IO {
|
||||||
} r;
|
} r;
|
||||||
} dma;
|
} dma;
|
||||||
|
|
||||||
struct Registers {
|
|
||||||
//$004a-$004c SDMA_SRC
|
|
||||||
uint20 dmaSource;
|
|
||||||
|
|
||||||
//$004e-$0050 SDMA_LEN
|
|
||||||
uint20 dmaLength;
|
|
||||||
|
|
||||||
//$0052 SDMA_CTRL
|
|
||||||
uint2 dmaRate;
|
|
||||||
uint1 dmaUnknown;
|
|
||||||
uint1 dmaLoop;
|
|
||||||
uint1 dmaTarget;
|
|
||||||
uint1 dmaDirection;
|
|
||||||
uint1 dmaEnable;
|
|
||||||
|
|
||||||
//$008f SND_WAVE_BASE
|
|
||||||
uint8 waveBase;
|
|
||||||
|
|
||||||
//$0091 SND_OUTPUT
|
|
||||||
uint1 speakerEnable;
|
|
||||||
uint2 speakerShift;
|
|
||||||
uint1 headphoneEnable;
|
|
||||||
} r;
|
|
||||||
|
|
||||||
struct Channel1 {
|
struct Channel1 {
|
||||||
auto run() -> void;
|
auto run() -> void;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
auto APU::serialize(serializer& s) -> void {
|
||||||
|
s.integer(this->s.sweepClock);
|
||||||
|
s.integer(r.waveBase);
|
||||||
|
s.integer(r.speakerEnable);
|
||||||
|
s.integer(r.speakerShift);
|
||||||
|
s.integer(r.headphoneEnable);
|
||||||
|
|
||||||
|
s.integer(dma.s.clock);
|
||||||
|
s.integer(dma.s.source);
|
||||||
|
s.integer(dma.s.length);
|
||||||
|
s.integer(dma.r.source);
|
||||||
|
s.integer(dma.r.length);
|
||||||
|
s.integer(dma.r.rate);
|
||||||
|
s.integer(dma.r.unknown);
|
||||||
|
s.integer(dma.r.loop);
|
||||||
|
s.integer(dma.r.target);
|
||||||
|
s.integer(dma.r.direction);
|
||||||
|
s.integer(dma.r.enable);
|
||||||
|
|
||||||
|
s.integer(channel1.o.left);
|
||||||
|
s.integer(channel1.o.right);
|
||||||
|
s.integer(channel1.s.period);
|
||||||
|
s.integer(channel1.s.sampleOffset);
|
||||||
|
s.integer(channel1.r.pitch);
|
||||||
|
s.integer(channel1.r.volumeLeft);
|
||||||
|
s.integer(channel1.r.volumeRight);
|
||||||
|
s.integer(channel1.r.enable);
|
||||||
|
|
||||||
|
s.integer(channel2.o.left);
|
||||||
|
s.integer(channel2.o.right);
|
||||||
|
s.integer(channel2.s.period);
|
||||||
|
s.integer(channel2.s.sampleOffset);
|
||||||
|
s.integer(channel2.r.pitch);
|
||||||
|
s.integer(channel2.r.volumeLeft);
|
||||||
|
s.integer(channel2.r.volumeRight);
|
||||||
|
s.integer(channel2.r.enable);
|
||||||
|
s.integer(channel2.r.voice);
|
||||||
|
s.integer(channel2.r.voiceEnableLeft);
|
||||||
|
s.integer(channel2.r.voiceEnableRight);
|
||||||
|
|
||||||
|
s.integer(channel3.o.left);
|
||||||
|
s.integer(channel3.o.right);
|
||||||
|
s.integer(channel3.s.period);
|
||||||
|
s.integer(channel3.s.sampleOffset);
|
||||||
|
s.integer(channel3.s.sweepCounter);
|
||||||
|
s.integer(channel3.r.pitch);
|
||||||
|
s.integer(channel3.r.volumeLeft);
|
||||||
|
s.integer(channel3.r.volumeRight);
|
||||||
|
s.integer(channel3.r.sweepValue);
|
||||||
|
s.integer(channel3.r.sweepTime);
|
||||||
|
s.integer(channel3.r.enable);
|
||||||
|
s.integer(channel3.r.sweep);
|
||||||
|
|
||||||
|
s.integer(channel4.o.left);
|
||||||
|
s.integer(channel4.o.right);
|
||||||
|
s.integer(channel4.s.period);
|
||||||
|
s.integer(channel4.s.sampleOffset);
|
||||||
|
s.integer(channel4.s.noiseOutput);
|
||||||
|
s.integer(channel4.s.noiseLFSR);
|
||||||
|
s.integer(channel4.r.pitch);
|
||||||
|
s.integer(channel4.r.volumeLeft);
|
||||||
|
s.integer(channel4.r.volumeRight);
|
||||||
|
s.integer(channel4.r.noiseMode);
|
||||||
|
s.integer(channel4.r.noiseReset);
|
||||||
|
s.integer(channel4.r.noiseUpdate);
|
||||||
|
s.integer(channel4.r.enable);
|
||||||
|
s.integer(channel4.r.noise);
|
||||||
|
|
||||||
|
s.integer(channel5.o.left);
|
||||||
|
s.integer(channel5.o.right);
|
||||||
|
s.integer(channel5.s.clock);
|
||||||
|
s.integer(channel5.s.data);
|
||||||
|
s.integer(channel5.r.volume);
|
||||||
|
s.integer(channel5.r.scale);
|
||||||
|
s.integer(channel5.r.speed);
|
||||||
|
s.integer(channel5.r.enable);
|
||||||
|
s.integer(channel5.r.unknown);
|
||||||
|
s.integer(channel5.r.leftEnable);
|
||||||
|
s.integer(channel5.r.rightEnable);
|
||||||
|
}
|
|
@ -4,7 +4,39 @@ namespace WonderSwan {
|
||||||
|
|
||||||
Cartridge cartridge;
|
Cartridge cartridge;
|
||||||
#include "memory.cpp"
|
#include "memory.cpp"
|
||||||
|
#include "rtc.cpp"
|
||||||
#include "io.cpp"
|
#include "io.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
|
|
||||||
|
auto Cartridge::Enter() -> void {
|
||||||
|
while(true) scheduler.synchronize(), cartridge.main();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::main() -> void {
|
||||||
|
if(rtc.data) {
|
||||||
|
rtcTickSecond();
|
||||||
|
rtcCheckAlarm();
|
||||||
|
}
|
||||||
|
step(3'072'000);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::step(uint clocks) -> void {
|
||||||
|
clock += clocks;
|
||||||
|
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::power() -> void {
|
||||||
|
create(Cartridge::Enter, 3'072'000);
|
||||||
|
eeprom.power();
|
||||||
|
|
||||||
|
bus.map(this, 0x00c0, 0x00c8);
|
||||||
|
if(rtc.data) bus.map(this, 0x00ca, 0x00cb);
|
||||||
|
|
||||||
|
r.romBank0 = 0xff;
|
||||||
|
r.romBank1 = 0xff;
|
||||||
|
r.romBank2 = 0xff;
|
||||||
|
r.sramBank = 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
auto Cartridge::load() -> void {
|
auto Cartridge::load() -> void {
|
||||||
information.manifest = "";
|
information.manifest = "";
|
||||||
|
@ -39,6 +71,14 @@ auto Cartridge::load() -> void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(auto node = document["board/rtc"]) {
|
||||||
|
rtc.name = node["name"].text();
|
||||||
|
rtc.size = node["size"].natural();
|
||||||
|
rtc.mask = bit::round(rtc.size) - 1;
|
||||||
|
if(rtc.size) rtc.data = new uint8[rtc.mask + 1]();
|
||||||
|
if(rtc.name) interface->loadRequest(ID::RTC, rtc.name, false);
|
||||||
|
}
|
||||||
|
|
||||||
information.title = document["information/title"].text();
|
information.title = document["information/title"].text();
|
||||||
information.orientation = document["information/orientation"].text() == "vertical";
|
information.orientation = document["information/orientation"].text() == "vertical";
|
||||||
information.sha256 = Hash::SHA256(rom.data, rom.size).digest();
|
information.sha256 = Hash::SHA256(rom.data, rom.size).digest();
|
||||||
|
@ -56,17 +96,12 @@ auto Cartridge::unload() -> void {
|
||||||
ram.size = 0;
|
ram.size = 0;
|
||||||
ram.mask = 0;
|
ram.mask = 0;
|
||||||
ram.name = "";
|
ram.name = "";
|
||||||
}
|
|
||||||
|
|
||||||
auto Cartridge::power() -> void {
|
delete[] rtc.data;
|
||||||
eeprom.power();
|
rtc.data = nullptr;
|
||||||
|
rtc.size = 0;
|
||||||
bus.map(this, 0x00c0, 0x00c8);
|
rtc.mask = 0;
|
||||||
|
rtc.name = "";
|
||||||
r.bank_rom0 = 0xff;
|
|
||||||
r.bank_rom1 = 0xff;
|
|
||||||
r.bank_rom2 = 0xff;
|
|
||||||
r.bank_sram = 0xff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,41 @@
|
||||||
struct Cartridge : IO {
|
struct Cartridge : Thread, IO {
|
||||||
auto load() -> void;
|
static auto Enter() -> void;
|
||||||
auto unload() -> void;
|
auto main() -> void;
|
||||||
|
auto step(uint clocks) -> void;
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
|
|
||||||
|
auto load() -> void;
|
||||||
|
auto unload() -> void;
|
||||||
|
|
||||||
|
//memory.cpp
|
||||||
auto romRead(uint20 addr) -> uint8;
|
auto romRead(uint20 addr) -> uint8;
|
||||||
auto romWrite(uint20 addr, uint8 data) -> void;
|
auto romWrite(uint20 addr, uint8 data) -> void;
|
||||||
|
|
||||||
auto ramRead(uint20 addr) -> uint8;
|
auto ramRead(uint20 addr) -> uint8;
|
||||||
auto ramWrite(uint20 addr, uint8 data) -> void;
|
auto ramWrite(uint20 addr, uint8 data) -> void;
|
||||||
|
|
||||||
|
//rtc.cpp
|
||||||
|
auto rtcLoad() -> void;
|
||||||
|
auto rtcSave() -> void;
|
||||||
|
auto rtcTickSecond() -> void;
|
||||||
|
auto rtcCheckAlarm() -> void;
|
||||||
|
auto rtcStatus() -> uint8;
|
||||||
|
auto rtcCommand(uint8 data) -> void;
|
||||||
|
auto rtcRead() -> uint8;
|
||||||
|
auto rtcWrite(uint8 data) -> void;
|
||||||
|
|
||||||
|
//io.cpp
|
||||||
auto portRead(uint16 addr) -> uint8 override;
|
auto portRead(uint16 addr) -> uint8 override;
|
||||||
auto portWrite(uint16 addr, uint8 data) -> void override;
|
auto portWrite(uint16 addr, uint8 data) -> void override;
|
||||||
|
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
struct Registers {
|
struct Registers {
|
||||||
uint8 bank_rom0;
|
uint8 romBank0;
|
||||||
uint8 bank_rom1;
|
uint8 romBank1;
|
||||||
uint8 bank_rom2;
|
uint8 romBank2;
|
||||||
uint8 bank_sram;
|
uint8 sramBank;
|
||||||
} r;
|
} r;
|
||||||
|
|
||||||
struct Memory {
|
struct Memory {
|
||||||
|
@ -24,9 +43,29 @@ struct Cartridge : IO {
|
||||||
uint size = 0;
|
uint size = 0;
|
||||||
uint mask = 0;
|
uint mask = 0;
|
||||||
string name;
|
string name;
|
||||||
} rom, ram;
|
};
|
||||||
|
|
||||||
|
struct RTC : Memory {
|
||||||
|
uint8 command;
|
||||||
|
uint4 index;
|
||||||
|
|
||||||
|
uint8 alarm;
|
||||||
|
uint8 alarmHour;
|
||||||
|
uint8 alarmMinute;
|
||||||
|
|
||||||
|
auto year() -> uint8& { return data[0]; }
|
||||||
|
auto month() -> uint8& { return data[1]; }
|
||||||
|
auto day() -> uint8& { return data[2]; }
|
||||||
|
auto weekday() -> uint8& { return data[3]; }
|
||||||
|
auto hour() -> uint8& { return data[4]; }
|
||||||
|
auto minute() -> uint8& { return data[5]; }
|
||||||
|
auto second() -> uint8& { return data[6]; }
|
||||||
|
};
|
||||||
|
|
||||||
|
Memory rom;
|
||||||
|
Memory ram;
|
||||||
EEPROM eeprom;
|
EEPROM eeprom;
|
||||||
|
RTC rtc;
|
||||||
|
|
||||||
struct Information {
|
struct Information {
|
||||||
string manifest;
|
string manifest;
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
auto Cartridge::portRead(uint16 addr) -> uint8 {
|
auto Cartridge::portRead(uint16 addr) -> uint8 {
|
||||||
//BANK_ROM2
|
//BANK_ROM2
|
||||||
if(addr == 0x00c0) return r.bank_rom2;
|
if(addr == 0x00c0) return r.romBank2;
|
||||||
|
|
||||||
//BANK_SRAM
|
//BANK_SRAM
|
||||||
if(addr == 0x00c1) return r.bank_sram;
|
if(addr == 0x00c1) return r.sramBank;
|
||||||
|
|
||||||
//BANK_ROM0
|
//BANK_ROM0
|
||||||
if(addr == 0x00c2) return r.bank_rom0;
|
if(addr == 0x00c2) return r.romBank0;
|
||||||
|
|
||||||
//BANK_ROM1
|
//BANK_ROM1
|
||||||
if(addr == 0x00c3) return r.bank_rom1;
|
if(addr == 0x00c3) return r.romBank1;
|
||||||
|
|
||||||
//EEP_DATA
|
//EEP_DATA
|
||||||
if(addr == 0x00c4) return eeprom.read(EEPROM::DataLo);
|
if(addr == 0x00c4) return eeprom.read(EEPROM::DataLo);
|
||||||
|
@ -22,42 +22,42 @@ auto Cartridge::portRead(uint16 addr) -> uint8 {
|
||||||
//EEP_STATUS
|
//EEP_STATUS
|
||||||
if(addr == 0x00c8) return eeprom.read(EEPROM::Status);
|
if(addr == 0x00c8) return eeprom.read(EEPROM::Status);
|
||||||
|
|
||||||
|
//RTC_STATUS
|
||||||
|
if(addr == 0x00ca) return rtcStatus();
|
||||||
|
|
||||||
|
//RTC_DATA
|
||||||
|
if(addr == 0x00cb) return rtcRead();
|
||||||
|
|
||||||
return 0x00;
|
return 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cartridge::portWrite(uint16 addr, uint8 data) -> void {
|
auto Cartridge::portWrite(uint16 addr, uint8 data) -> void {
|
||||||
//BANK_ROM2
|
//BANK_ROM2
|
||||||
if(addr == 0x00c0) {
|
if(addr == 0x00c0) r.romBank2 = data;
|
||||||
r.bank_rom2 = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//BANK_SRAM
|
//BANK_SRAM
|
||||||
if(addr == 0x00c1) {
|
if(addr == 0x00c1) r.sramBank = data;
|
||||||
r.bank_sram = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//BANK_ROM0
|
//BANK_ROM0
|
||||||
if(addr == 0x00c2) {
|
if(addr == 0x00c2) r.romBank0 = data;
|
||||||
r.bank_rom0 = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//BANK_ROM1
|
//BANK_ROM1
|
||||||
if(addr == 0x00c3) {
|
if(addr == 0x00c3) r.romBank1 = data;
|
||||||
r.bank_rom1 = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//EEP_DATA
|
//EEP_DATA
|
||||||
if(addr == 0x00c4) return eeprom.write(EEPROM::DataLo, data);
|
if(addr == 0x00c4) eeprom.write(EEPROM::DataLo, data);
|
||||||
if(addr == 0x00c5) return eeprom.write(EEPROM::DataHi, data);
|
if(addr == 0x00c5) eeprom.write(EEPROM::DataHi, data);
|
||||||
|
|
||||||
//EEP_ADDR
|
//EEP_ADDR
|
||||||
if(addr == 0x00c6) return eeprom.write(EEPROM::AddressLo, data);
|
if(addr == 0x00c6) eeprom.write(EEPROM::AddressLo, data);
|
||||||
if(addr == 0x00c7) return eeprom.write(EEPROM::AddressHi, data);
|
if(addr == 0x00c7) eeprom.write(EEPROM::AddressHi, data);
|
||||||
|
|
||||||
//EEP_CMD
|
//EEP_CMD
|
||||||
if(addr == 0x00c8) return eeprom.write(EEPROM::Command, data);
|
if(addr == 0x00c8) eeprom.write(EEPROM::Command, data);
|
||||||
|
|
||||||
|
//RTC_CMD
|
||||||
|
if(addr == 0x00ca) rtcCommand(data);
|
||||||
|
|
||||||
|
//RTC_DATA
|
||||||
|
if(addr == 0x00cb) rtcWrite(data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@ auto Cartridge::romRead(uint20 addr) -> uint8 {
|
||||||
if(!rom.data) return 0x00;
|
if(!rom.data) return 0x00;
|
||||||
uint28 offset;
|
uint28 offset;
|
||||||
switch(addr.byte(2)) {
|
switch(addr.byte(2)) {
|
||||||
case 2: offset = r.bank_rom0 << 16 | addr.bits(0,15); break; //20000-2ffff
|
case 2: offset = r.romBank0 << 16 | addr.bits(0,15); break; //20000-2ffff
|
||||||
case 3: offset = r.bank_rom1 << 16 | addr.bits(0,15); break; //30000-3ffff
|
case 3: offset = r.romBank1 << 16 | addr.bits(0,15); break; //30000-3ffff
|
||||||
default: offset = r.bank_rom2 << 20 | addr.bits(0,19); break; //40000-fffff
|
default: offset = r.romBank2 << 20 | addr.bits(0,19); break; //40000-fffff
|
||||||
}
|
}
|
||||||
return rom.data[offset & rom.mask];
|
return rom.data[offset & rom.mask];
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,12 @@ auto Cartridge::romWrite(uint20 addr, uint8 data) -> void {
|
||||||
//10000-1ffff
|
//10000-1ffff
|
||||||
auto Cartridge::ramRead(uint20 addr) -> uint8 {
|
auto Cartridge::ramRead(uint20 addr) -> uint8 {
|
||||||
if(!ram.data) return 0x00;
|
if(!ram.data) return 0x00;
|
||||||
uint24 offset = r.bank_sram << 16 | addr.bits(0,15);
|
uint24 offset = r.sramBank << 16 | addr.bits(0,15);
|
||||||
return ram.data[offset & ram.mask];
|
return ram.data[offset & ram.mask];
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cartridge::ramWrite(uint20 addr, uint8 data) -> void {
|
auto Cartridge::ramWrite(uint20 addr, uint8 data) -> void {
|
||||||
if(!ram.data) return;
|
if(!ram.data) return;
|
||||||
uint24 offset = r.bank_sram << 16 | addr.bits(0,15);
|
uint24 offset = r.sramBank << 16 | addr.bits(0,15);
|
||||||
ram.data[offset & ram.mask] = data;
|
ram.data[offset & ram.mask] = data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
//calculate time between last play of game and current time;
|
||||||
|
//increment RTC by said amount of seconds
|
||||||
|
auto Cartridge::rtcLoad() -> void {
|
||||||
|
uint64 timestamp = 0;
|
||||||
|
for(auto n : range(8)) timestamp.byte(n) = rtc.data[8 + n];
|
||||||
|
if(!timestamp) return; //new save file
|
||||||
|
|
||||||
|
timestamp = time(0) - timestamp;
|
||||||
|
while(timestamp--) rtcTickSecond();
|
||||||
|
}
|
||||||
|
|
||||||
|
//save time when game is unloaded
|
||||||
|
auto Cartridge::rtcSave() -> void {
|
||||||
|
uint64 timestamp = time(0);
|
||||||
|
for(auto n : range(8)) rtc.data[8 + n] = timestamp.byte(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::rtcTickSecond() -> void {
|
||||||
|
if(++rtc.second() < 60) return;
|
||||||
|
rtc.second() = 0;
|
||||||
|
|
||||||
|
if(++rtc.minute() < 60) return;
|
||||||
|
rtc.minute() = 0;
|
||||||
|
|
||||||
|
if(++rtc.hour() < 60) return;
|
||||||
|
rtc.hour() = 0;
|
||||||
|
|
||||||
|
rtc.weekday() += 1;
|
||||||
|
rtc.weekday() %= 7;
|
||||||
|
|
||||||
|
uint daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||||
|
if(rtc.year() && (rtc.year() % 100) && !(rtc.year() % 4)) daysInMonth[1]++;
|
||||||
|
|
||||||
|
if(++rtc.day() < daysInMonth[rtc.month()]) return;
|
||||||
|
rtc.day() = 0;
|
||||||
|
|
||||||
|
if(++rtc.month() < 12) return;
|
||||||
|
rtc.month() = 0;
|
||||||
|
|
||||||
|
++rtc.year();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::rtcCheckAlarm() -> void {
|
||||||
|
if(!rtc.alarm.bit(5)) return;
|
||||||
|
|
||||||
|
if(rtc.hour() == rtc.alarmHour && rtc.minute() == rtc.alarmMinute) {
|
||||||
|
cpu.raise(CPU::Interrupt::Cartridge);
|
||||||
|
} else {
|
||||||
|
cpu.lower(CPU::Interrupt::Cartridge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::rtcStatus() -> uint8 {
|
||||||
|
return 0x80;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::rtcCommand(uint8 data) -> void {
|
||||||
|
rtc.command = data;
|
||||||
|
|
||||||
|
//RESET
|
||||||
|
if(rtc.command == 0x10) {
|
||||||
|
rtc.year() = 0;
|
||||||
|
rtc.month() = 0;
|
||||||
|
rtc.day() = 0;
|
||||||
|
rtc.weekday() = 0;
|
||||||
|
rtc.hour() = 0;
|
||||||
|
rtc.minute() = 0;
|
||||||
|
rtc.second() = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//ALARM_FLAG
|
||||||
|
if(rtc.command == 0x12) {
|
||||||
|
rtc.index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//SET_DATETIME
|
||||||
|
if(rtc.command == 0x14) {
|
||||||
|
rtc.index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//GET_DATETIME
|
||||||
|
if(rtc.command == 0x15) {
|
||||||
|
rtc.index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//SET_ALARM
|
||||||
|
if(rtc.command == 0x18) {
|
||||||
|
rtc.index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::rtcRead() -> uint8 {
|
||||||
|
uint8 data = 0;
|
||||||
|
|
||||||
|
static auto encode = [](uint8 data) -> uint8 {
|
||||||
|
return ((data / 10) << 4) + (data % 10);
|
||||||
|
};
|
||||||
|
|
||||||
|
//GET_DATETIME
|
||||||
|
if(rtc.command == 0x15) {
|
||||||
|
switch(rtc.index) {
|
||||||
|
case 0: data = encode(rtc.year()); break;
|
||||||
|
case 1: data = encode(rtc.month() + 1); break;
|
||||||
|
case 2: data = encode(rtc.day() + 1); break;
|
||||||
|
case 3: data = encode(rtc.weekday()); break;
|
||||||
|
case 4: data = encode(rtc.hour()); break;
|
||||||
|
case 5: data = encode(rtc.minute()); break;
|
||||||
|
case 6: data = encode(rtc.second()); break;
|
||||||
|
}
|
||||||
|
if(++rtc.index >= 7) rtc.command = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Cartridge::rtcWrite(uint8 data) -> void {
|
||||||
|
static auto decode = [](uint8 data) -> uint8 {
|
||||||
|
return (data >> 4) * 10 + (data & 0x0f);
|
||||||
|
};
|
||||||
|
|
||||||
|
//ALARM_FLAG
|
||||||
|
if(rtc.command == 0x12) {
|
||||||
|
if(data.bit(6)) rtc.alarm = data; //todo: is bit6 really required to be set?
|
||||||
|
rtc.command = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//SET_DATETIME
|
||||||
|
if(rtc.command == 0x14) {
|
||||||
|
switch(rtc.index) {
|
||||||
|
case 0: rtc.year() = decode(data); break;
|
||||||
|
case 1: rtc.month() = decode(data) - 1; break;
|
||||||
|
case 2: rtc.day() = decode(data) - 1; break;
|
||||||
|
case 3: rtc.weekday() = decode(data); break;
|
||||||
|
case 4: rtc.hour() = decode(data); break;
|
||||||
|
case 5: rtc.minute() = decode(data); break;
|
||||||
|
case 6: rtc.second() = decode(data); break;
|
||||||
|
}
|
||||||
|
if(++rtc.index >= 7) rtc.command = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//SET_ALRM
|
||||||
|
if(rtc.command == 0x18) {
|
||||||
|
switch(rtc.index) {
|
||||||
|
case 0: rtc.alarmHour = decode(data.bits(0,6)); break;
|
||||||
|
case 1: rtc.alarmMinute = decode(data); break;
|
||||||
|
}
|
||||||
|
if(++rtc.index >= 2) rtc.command = 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
auto Cartridge::serialize(serializer& s) -> void {
|
||||||
|
if(ram.size) s.array(ram.data, ram.size);
|
||||||
|
if(eeprom.size()) eeprom.serialize(s);
|
||||||
|
if(rtc.size) s.array(rtc.data, rtc.size);
|
||||||
|
|
||||||
|
if(rtc.size) {
|
||||||
|
s.integer(rtc.command);
|
||||||
|
s.integer(rtc.index);
|
||||||
|
s.integer(rtc.alarm);
|
||||||
|
s.integer(rtc.alarmHour);
|
||||||
|
s.integer(rtc.alarmMinute);
|
||||||
|
}
|
||||||
|
|
||||||
|
s.integer(r.romBank0);
|
||||||
|
s.integer(r.romBank1);
|
||||||
|
s.integer(r.romBank2);
|
||||||
|
s.integer(r.sramBank);
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
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;
|
|
@ -6,6 +6,7 @@ CPU cpu;
|
||||||
#include "io.cpp"
|
#include "io.cpp"
|
||||||
#include "interrupt.cpp"
|
#include "interrupt.cpp"
|
||||||
#include "dma.cpp"
|
#include "dma.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
|
|
||||||
auto CPU::Enter() -> void {
|
auto CPU::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), cpu.main();
|
while(true) scheduler.synchronize(), cpu.main();
|
||||||
|
@ -22,6 +23,9 @@ auto CPU::step(uint clocks) -> void {
|
||||||
|
|
||||||
apu.clock -= clocks;
|
apu.clock -= clocks;
|
||||||
if(apu.clock < 0) co_switch(apu.thread);
|
if(apu.clock < 0) co_switch(apu.thread);
|
||||||
|
|
||||||
|
cartridge.clock -= clocks;
|
||||||
|
if(cartridge.clock < 0) co_switch(cartridge.thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::wait(uint clocks) -> void {
|
auto CPU::wait(uint clocks) -> void {
|
||||||
|
@ -51,9 +55,7 @@ auto CPU::power() -> void {
|
||||||
bus.map(this, 0x00a0);
|
bus.map(this, 0x00a0);
|
||||||
bus.map(this, 0x00b0);
|
bus.map(this, 0x00b0);
|
||||||
bus.map(this, 0x00b2);
|
bus.map(this, 0x00b2);
|
||||||
bus.map(this, 0x00b4);
|
bus.map(this, 0x00b4, 0x00b6);
|
||||||
bus.map(this, 0x00b5);
|
|
||||||
bus.map(this, 0x00b6);
|
|
||||||
|
|
||||||
if(system.model() != Model::WonderSwan) {
|
if(system.model() != Model::WonderSwan) {
|
||||||
bus.map(this, 0x0040, 0x0049);
|
bus.map(this, 0x0040, 0x0049);
|
||||||
|
|
|
@ -35,6 +35,9 @@ struct CPU : Processor::V30MZ, Thread, IO {
|
||||||
//dma.cpp
|
//dma.cpp
|
||||||
auto dmaTransfer() -> void;
|
auto dmaTransfer() -> void;
|
||||||
|
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
struct Registers {
|
struct Registers {
|
||||||
//$0040-0042 DMA_SRC
|
//$0040-0042 DMA_SRC
|
||||||
uint20 dmaSource;
|
uint20 dmaSource;
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
auto CPU::serialize(serializer& s) -> void {
|
||||||
|
V30MZ::serialize(s);
|
||||||
|
|
||||||
|
s.integer(r.dmaSource);
|
||||||
|
s.integer(r.dmaTarget);
|
||||||
|
s.integer(r.dmaLength);
|
||||||
|
s.integer(r.dmaEnable);
|
||||||
|
s.integer(r.dmaMode);
|
||||||
|
s.integer(r.interruptBase);
|
||||||
|
s.integer(r.interruptEnable);
|
||||||
|
s.integer(r.interruptStatus);
|
||||||
|
s.integer(r.ypadEnable);
|
||||||
|
s.integer(r.xpadEnable);
|
||||||
|
s.integer(r.buttonEnable);
|
||||||
|
}
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace WonderSwan {
|
namespace WonderSwan {
|
||||||
|
|
||||||
|
#include "serialization.cpp"
|
||||||
|
|
||||||
auto EEPROM::name() const -> string { return _name; }
|
auto EEPROM::name() const -> string { return _name; }
|
||||||
auto EEPROM::data() -> uint16* { return _data; }
|
auto EEPROM::data() -> uint16* { return _data; }
|
||||||
auto EEPROM::size() const -> uint { return _size; }
|
auto EEPROM::size() const -> uint { return _size; }
|
||||||
|
|
|
@ -26,6 +26,9 @@ struct EEPROM {
|
||||||
auto read(uint) -> uint8;
|
auto read(uint) -> uint8;
|
||||||
auto write(uint, uint8) -> void;
|
auto write(uint, uint8) -> void;
|
||||||
|
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto execute() -> void;
|
auto execute() -> void;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
auto EEPROM::serialize(serializer& s) -> void {
|
||||||
|
s.array(_data);
|
||||||
|
|
||||||
|
s.integer(r.latch);
|
||||||
|
s.integer(r.address);
|
||||||
|
s.integer(r.unknown);
|
||||||
|
s.integer(r.writeRequested);
|
||||||
|
s.integer(r.readRequested);
|
||||||
|
s.integer(r.writeCompleted);
|
||||||
|
s.integer(r.readCompleted);
|
||||||
|
s.integer(r.writeProtect);
|
||||||
|
}
|
|
@ -16,8 +16,8 @@ Interface::Interface() {
|
||||||
information.aspectRatio = 1.0;
|
information.aspectRatio = 1.0;
|
||||||
information.resettable = false;
|
information.resettable = false;
|
||||||
|
|
||||||
information.capability.states = false;
|
information.capability.states = true;
|
||||||
information.capability.cheats = false;
|
information.capability.cheats = true;
|
||||||
|
|
||||||
media.append({ID::WonderSwan, "WonderSwan", "ws", true});
|
media.append({ID::WonderSwan, "WonderSwan", "ws", true});
|
||||||
media.append({ID::WonderSwanColor, "WonderSwan Color", "wsc", true});
|
media.append({ID::WonderSwanColor, "WonderSwan Color", "wsc", true});
|
||||||
|
@ -94,6 +94,7 @@ auto Interface::group(uint id) -> uint {
|
||||||
case ID::ROM:
|
case ID::ROM:
|
||||||
case ID::RAM:
|
case ID::RAM:
|
||||||
case ID::EEPROM:
|
case ID::EEPROM:
|
||||||
|
case ID::RTC:
|
||||||
switch(system.model()) {
|
switch(system.model()) {
|
||||||
case Model::WonderSwan:
|
case Model::WonderSwan:
|
||||||
return ID::WonderSwan;
|
return ID::WonderSwan;
|
||||||
|
@ -114,6 +115,7 @@ auto Interface::save() -> void {
|
||||||
if(auto name = system.eeprom.name()) interface->saveRequest(ID::SystemEEPROM, name);
|
if(auto name = system.eeprom.name()) interface->saveRequest(ID::SystemEEPROM, name);
|
||||||
if(auto name = cartridge.ram.name) interface->saveRequest(ID::RAM, name);
|
if(auto name = cartridge.ram.name) interface->saveRequest(ID::RAM, name);
|
||||||
if(auto name = cartridge.eeprom.name()) interface->saveRequest(ID::EEPROM, name);
|
if(auto name = cartridge.eeprom.name()) interface->saveRequest(ID::EEPROM, name);
|
||||||
|
if(auto name = cartridge.rtc.name) interface->saveRequest(ID::RTC, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::load(uint id, const stream& stream) -> void {
|
auto Interface::load(uint id, const stream& stream) -> void {
|
||||||
|
@ -140,6 +142,11 @@ auto Interface::load(uint id, const stream& stream) -> void {
|
||||||
if(id == ID::EEPROM) {
|
if(id == ID::EEPROM) {
|
||||||
stream.read((uint8_t*)cartridge.eeprom.data(), min(cartridge.eeprom.size() * sizeof(uint16), stream.size()));
|
stream.read((uint8_t*)cartridge.eeprom.data(), min(cartridge.eeprom.size() * sizeof(uint16), stream.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(id == ID::RTC) {
|
||||||
|
stream.read((uint8_t*)cartridge.rtc.data, min(cartridge.rtc.size, stream.size()));
|
||||||
|
cartridge.rtcLoad();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::save(uint id, const stream& stream) -> void {
|
auto Interface::save(uint id, const stream& stream) -> void {
|
||||||
|
@ -154,6 +161,11 @@ auto Interface::save(uint id, const stream& stream) -> void {
|
||||||
if(id == ID::EEPROM) {
|
if(id == ID::EEPROM) {
|
||||||
stream.write((uint8_t*)cartridge.eeprom.data(), cartridge.eeprom.size() * sizeof(uint16));
|
stream.write((uint8_t*)cartridge.eeprom.data(), cartridge.eeprom.size() * sizeof(uint16));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(id == ID::RTC) {
|
||||||
|
cartridge.rtcSave();
|
||||||
|
stream.write((uint8_t*)cartridge.rtc.data, cartridge.rtc.size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::unload() -> void {
|
auto Interface::unload() -> void {
|
||||||
|
@ -169,11 +181,24 @@ auto Interface::run() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::serialize() -> serializer {
|
auto Interface::serialize() -> serializer {
|
||||||
return {};
|
system.runToSave();
|
||||||
|
return system.serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::unserialize(serializer& s) -> bool {
|
auto Interface::unserialize(serializer& s) -> bool {
|
||||||
return false;
|
return system.unserialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interface::cheatSet(const lstring& list) -> void {
|
||||||
|
cheat.reset();
|
||||||
|
for(auto& codeset : list) {
|
||||||
|
lstring codes = codeset.split("+");
|
||||||
|
for(auto& code : codes) {
|
||||||
|
lstring part = code.split("/");
|
||||||
|
if(part.size() == 2) cheat.append(hex(part[0]), hex(part[1]));
|
||||||
|
if(part.size() == 3) cheat.append(hex(part[0]), hex(part[1]), hex(part[2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::cap(const string& name) -> bool {
|
auto Interface::cap(const string& name) -> bool {
|
||||||
|
|
|
@ -16,6 +16,7 @@ struct ID {
|
||||||
ROM,
|
ROM,
|
||||||
RAM,
|
RAM,
|
||||||
EEPROM,
|
EEPROM,
|
||||||
|
RTC,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum : uint {
|
enum : uint {
|
||||||
|
@ -47,6 +48,8 @@ struct Interface : Emulator::Interface {
|
||||||
auto serialize() -> serializer override;
|
auto serialize() -> serializer override;
|
||||||
auto unserialize(serializer&) -> bool override;
|
auto unserialize(serializer&) -> bool override;
|
||||||
|
|
||||||
|
auto cheatSet(const lstring&) -> void;
|
||||||
|
|
||||||
auto cap(const string& name) -> bool override;
|
auto cap(const string& name) -> bool override;
|
||||||
auto get(const string& name) -> any override;
|
auto get(const string& name) -> any override;
|
||||||
auto set(const string& name, const any& value) -> bool override;
|
auto set(const string& name, const any& value) -> bool override;
|
||||||
|
|
|
@ -9,6 +9,10 @@ auto InternalRAM::power() -> void {
|
||||||
for(auto& byte : memory) byte = 0x00;
|
for(auto& byte : memory) byte = 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto InternalRAM::serialize(serializer& s) -> void {
|
||||||
|
s.array(memory, system.model() == Model::WonderSwan ? 0x4000 : 0x10000);
|
||||||
|
}
|
||||||
|
|
||||||
auto InternalRAM::read(uint16 addr, uint size) -> uint32 {
|
auto InternalRAM::read(uint16 addr, uint size) -> uint32 {
|
||||||
if(size == Long) return read(addr + 0, Word) << 0 | read(addr + 2, Word) << 16;
|
if(size == Long) return read(addr + 0, Word) << 0 | read(addr + 2, Word) << 16;
|
||||||
if(size == Word) return read(addr + 0, Byte) << 0 | read(addr + 1, Byte) << 8;
|
if(size == Word) return read(addr + 0, Byte) << 0 | read(addr + 1, Byte) << 8;
|
||||||
|
@ -27,16 +31,20 @@ auto Bus::power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Bus::read(uint20 addr) -> uint8 {
|
auto Bus::read(uint20 addr) -> uint8 {
|
||||||
if(addr.bits(16,19) == 0) return iram.read(addr);
|
uint8 data = 0;
|
||||||
if(addr.bits(16,19) == 1) return cartridge.ramRead(addr);
|
if(addr.bits(16,19) == 0) data = iram.read(addr);
|
||||||
if(addr.bits(16,19) >= 2) return cartridge.romRead(addr);
|
if(addr.bits(16,19) == 1) data = cartridge.ramRead(addr);
|
||||||
unreachable;
|
if(addr.bits(16,19) >= 2) data = cartridge.romRead(addr);
|
||||||
|
if(cheat.enable()) {
|
||||||
|
if(auto result = cheat.find(addr, data)) data = result();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Bus::write(uint20 addr, uint8 data) -> void {
|
auto Bus::write(uint20 addr, uint8 data) -> void {
|
||||||
if(addr.bits(16,19) == 0) return iram.write(addr, data);
|
if(addr.bits(16,19) == 0) iram.write(addr, data);
|
||||||
if(addr.bits(16,19) == 1) return cartridge.ramWrite(addr, data);
|
if(addr.bits(16,19) == 1) cartridge.ramWrite(addr, data);
|
||||||
if(addr.bits(16,19) >= 2) return cartridge.romWrite(addr, data);
|
if(addr.bits(16,19) >= 2) cartridge.romWrite(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Bus::map(IO* io, uint16_t lo, maybe<uint16_t> hi) -> void {
|
auto Bus::map(IO* io, uint16_t lo, maybe<uint16_t> hi) -> void {
|
||||||
|
|
|
@ -5,6 +5,7 @@ struct IO {
|
||||||
|
|
||||||
struct InternalRAM {
|
struct InternalRAM {
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
auto read(uint16 addr, uint size = Byte) -> uint32;
|
auto read(uint16 addr, uint size = Byte) -> uint32;
|
||||||
auto write(uint16 addr, uint8 data) -> void;
|
auto write(uint16 addr, uint8 data) -> void;
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
auto PPU::latchRegisters() -> void {
|
||||||
|
l.backColor = r.backColor;
|
||||||
|
|
||||||
|
l.screenOneEnable = r.screenOneEnable;
|
||||||
|
l.screenOneMapBase = r.screenOneMapBase;
|
||||||
|
l.scrollOneX = r.scrollOneX;
|
||||||
|
l.scrollOneY = r.scrollOneY;
|
||||||
|
|
||||||
|
l.screenTwoEnable = r.screenTwoEnable;
|
||||||
|
l.screenTwoMapBase = r.screenTwoMapBase;
|
||||||
|
l.scrollTwoX = r.scrollTwoX;
|
||||||
|
l.scrollTwoY = r.scrollTwoY;
|
||||||
|
l.screenTwoWindowEnable = r.screenTwoWindowEnable;
|
||||||
|
l.screenTwoWindowInvert = r.screenTwoWindowInvert;
|
||||||
|
l.screenTwoWindowX0 = r.screenTwoWindowX0;
|
||||||
|
l.screenTwoWindowY0 = r.screenTwoWindowY0;
|
||||||
|
l.screenTwoWindowX1 = r.screenTwoWindowX1;
|
||||||
|
l.screenTwoWindowY1 = r.screenTwoWindowY1;
|
||||||
|
|
||||||
|
l.spriteEnable = r.spriteEnable;
|
||||||
|
l.spriteWindowEnable = r.spriteWindowEnable;
|
||||||
|
l.spriteWindowX0 = r.spriteWindowX0;
|
||||||
|
l.spriteWindowY0 = r.spriteWindowY0;
|
||||||
|
l.spriteWindowX1 = r.spriteWindowX1;
|
||||||
|
l.spriteWindowY1 = r.spriteWindowY1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PPU::latchSprites() -> void {
|
||||||
|
l.spriteCount = 0;
|
||||||
|
if(!l.spriteEnable) return;
|
||||||
|
|
||||||
|
uint offset = 0;
|
||||||
|
bool windowInside = s.vclk >= l.spriteWindowY0 && s.vclk <= l.spriteWindowY1;
|
||||||
|
for(auto index : range(l.oamCount)) {
|
||||||
|
uint32 attributes = l.oam[!s.field][index];
|
||||||
|
|
||||||
|
auto& sprite = l.sprite[l.spriteCount];
|
||||||
|
sprite.x = attributes.bits(24,31);
|
||||||
|
if(sprite.x > 224 && sprite.x < 249) continue;
|
||||||
|
sprite.y = attributes.bits(16,23);
|
||||||
|
if((uint8)(s.vclk - sprite.y) > 7) continue;
|
||||||
|
sprite.vflip = attributes.bit(15);
|
||||||
|
sprite.hflip = attributes.bit(14);
|
||||||
|
sprite.priority = attributes.bit(13);
|
||||||
|
sprite.window = attributes.bit(12);
|
||||||
|
if(l.spriteWindowEnable && sprite.window == windowInside) continue;
|
||||||
|
sprite.palette = 8 + attributes.bits(9,11);
|
||||||
|
sprite.tile = attributes.bits(0,8);
|
||||||
|
|
||||||
|
if(++l.spriteCount >= 32) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//note: this implicitly latches spriteBase, spriteFirst, spriteCount
|
||||||
|
auto PPU::latchOAM() -> void {
|
||||||
|
uint7 spriteIndex = r.spriteFirst;
|
||||||
|
uint8 spriteCount = min(128, (uint)r.spriteCount);
|
||||||
|
uint16 spriteBase = r.spriteBase.bits(0, 4 + system.depth()) << 9;
|
||||||
|
l.oamCount = spriteCount;
|
||||||
|
for(auto index : range(spriteCount)) {
|
||||||
|
l.oam[s.field][index] = iram.read(spriteBase + (spriteIndex++ << 2), Long);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,23 +4,27 @@ namespace WonderSwan {
|
||||||
|
|
||||||
PPU ppu;
|
PPU ppu;
|
||||||
#include "io.cpp"
|
#include "io.cpp"
|
||||||
#include "render-sprite.cpp"
|
#include "latch.cpp"
|
||||||
#include "render-mono.cpp"
|
#include "render-mono.cpp"
|
||||||
#include "render-color.cpp"
|
#include "render-color.cpp"
|
||||||
#include "video.cpp"
|
#include "video.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
|
|
||||||
auto PPU::Enter() -> void {
|
auto PPU::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), ppu.main();
|
while(true) scheduler.synchronize(), ppu.main();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::main() -> void {
|
auto PPU::main() -> void {
|
||||||
|
if(s.vclk == 142) {
|
||||||
|
latchOAM();
|
||||||
|
}
|
||||||
|
|
||||||
if(s.vclk < 144) {
|
if(s.vclk < 144) {
|
||||||
latchRegisters();
|
latchRegisters();
|
||||||
renderSpriteFetch();
|
latchSprites();
|
||||||
renderSpriteDecode();
|
|
||||||
for(auto x : range(224)) {
|
for(auto x : range(224)) {
|
||||||
if(!r.lcdEnable) {
|
if(!r.lcdEnable) {
|
||||||
pixel = {Pixel::Source::Back, 0x000};
|
s.pixel = {Pixel::Source::Back, 0x000};
|
||||||
} else if(!system.color()) {
|
} else if(!system.color()) {
|
||||||
renderMonoBack();
|
renderMonoBack();
|
||||||
renderMonoScreenOne();
|
renderMonoScreenOne();
|
||||||
|
@ -32,7 +36,7 @@ auto PPU::main() -> void {
|
||||||
renderColorScreenTwo();
|
renderColorScreenTwo();
|
||||||
renderColorSprite();
|
renderColorSprite();
|
||||||
}
|
}
|
||||||
output[s.vclk * 224 + s.hclk] = pixel.color;
|
output[s.vclk * 224 + s.hclk] = s.pixel.color;
|
||||||
step(1);
|
step(1);
|
||||||
}
|
}
|
||||||
step(32);
|
step(32);
|
||||||
|
@ -54,7 +58,7 @@ auto PPU::main() -> void {
|
||||||
|
|
||||||
auto PPU::scanline() -> void {
|
auto PPU::scanline() -> void {
|
||||||
s.hclk = 0;
|
s.hclk = 0;
|
||||||
s.vclk++;
|
if(++s.vclk == 159) frame();
|
||||||
if(s.vclk == r.lineCompare) {
|
if(s.vclk == r.lineCompare) {
|
||||||
cpu.raise(CPU::Interrupt::LineCompare);
|
cpu.raise(CPU::Interrupt::LineCompare);
|
||||||
}
|
}
|
||||||
|
@ -71,7 +75,6 @@ auto PPU::scanline() -> void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(s.vclk == 159) frame();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::frame() -> void {
|
auto PPU::frame() -> void {
|
||||||
|
@ -88,36 +91,6 @@ auto PPU::step(uint clocks) -> void {
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::latchRegisters() -> void {
|
|
||||||
l.backColor = r.backColor;
|
|
||||||
|
|
||||||
l.screenOneEnable = r.screenOneEnable;
|
|
||||||
l.screenOneMapBase = r.screenOneMapBase;
|
|
||||||
l.scrollOneX = r.scrollOneX;
|
|
||||||
l.scrollOneY = r.scrollOneY;
|
|
||||||
|
|
||||||
l.screenTwoEnable = r.screenTwoEnable;
|
|
||||||
l.screenTwoMapBase = r.screenTwoMapBase;
|
|
||||||
l.scrollTwoX = r.scrollTwoX;
|
|
||||||
l.scrollTwoY = r.scrollTwoY;
|
|
||||||
l.screenTwoWindowEnable = r.screenTwoWindowEnable;
|
|
||||||
l.screenTwoWindowInvert = r.screenTwoWindowInvert;
|
|
||||||
l.screenTwoWindowX0 = r.screenTwoWindowX0;
|
|
||||||
l.screenTwoWindowY0 = r.screenTwoWindowY0;
|
|
||||||
l.screenTwoWindowX1 = r.screenTwoWindowX1;
|
|
||||||
l.screenTwoWindowY1 = r.screenTwoWindowY1;
|
|
||||||
|
|
||||||
l.spriteEnable = r.spriteEnable;
|
|
||||||
l.spriteBase = r.spriteBase;
|
|
||||||
l.spriteFirst = r.spriteFirst;
|
|
||||||
l.spriteCount = r.spriteCount;
|
|
||||||
l.spriteWindowEnable = r.spriteWindowEnable;
|
|
||||||
l.spriteWindowX0 = r.spriteWindowX0;
|
|
||||||
l.spriteWindowY0 = r.spriteWindowY0;
|
|
||||||
l.spriteWindowX1 = r.spriteWindowX1;
|
|
||||||
l.spriteWindowY1 = r.spriteWindowY1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto PPU::power() -> void {
|
auto PPU::power() -> void {
|
||||||
create(PPU::Enter, 3'072'000);
|
create(PPU::Enter, 3'072'000);
|
||||||
|
|
||||||
|
@ -127,58 +100,13 @@ auto PPU::power() -> void {
|
||||||
bus.map(this, 0x00a4, 0x00ab);
|
bus.map(this, 0x00a4, 0x00ab);
|
||||||
|
|
||||||
for(auto& n : output) n = 0;
|
for(auto& n : output) n = 0;
|
||||||
for(auto& n : oam[0]) n = 0;
|
memory::fill(&s, sizeof(State));
|
||||||
for(auto& n : oam[1]) n = 0;
|
memory::fill(&l, sizeof(Latches));
|
||||||
|
memory::fill(&r, sizeof(Registers));
|
||||||
|
|
||||||
s.vclk = 0;
|
|
||||||
s.hclk = 0;
|
|
||||||
|
|
||||||
r.screenOneEnable = 0;
|
|
||||||
r.screenTwoEnable = 0;
|
|
||||||
r.spriteEnable = 0;
|
|
||||||
r.spriteWindowEnable = 0;
|
|
||||||
r.screenTwoWindowInvert = 0;
|
|
||||||
r.screenTwoWindowEnable = 0;
|
|
||||||
r.backColor = 0;
|
|
||||||
r.lineCompare = 0xff;
|
|
||||||
r.spriteBase = 0;
|
|
||||||
r.spriteFirst = 0;
|
|
||||||
r.spriteCount = 0;
|
|
||||||
r.screenOneMapBase = 0;
|
|
||||||
r.screenTwoMapBase = 0;
|
|
||||||
r.screenTwoWindowX0 = 0;
|
|
||||||
r.screenTwoWindowY0 = 0;
|
|
||||||
r.screenTwoWindowX1 = 0;
|
|
||||||
r.screenTwoWindowY1 = 0;
|
|
||||||
r.spriteWindowX0 = 0;
|
|
||||||
r.spriteWindowY0 = 0;
|
|
||||||
r.spriteWindowX1 = 0;
|
|
||||||
r.spriteWindowY1 = 0;
|
|
||||||
r.scrollOneX = 0;
|
|
||||||
r.scrollOneY = 0;
|
|
||||||
r.scrollTwoX = 0;
|
|
||||||
r.scrollTwoY = 0;
|
|
||||||
r.lcdEnable = 1;
|
r.lcdEnable = 1;
|
||||||
r.lcdContrast = 0;
|
|
||||||
r.lcdUnknown = 0;
|
|
||||||
r.iconSleep = 0;
|
|
||||||
r.iconVertical = 0;
|
|
||||||
r.iconHorizontal = 0;
|
|
||||||
r.iconAux1 = 0;
|
|
||||||
r.iconAux2 = 0;
|
|
||||||
r.iconAux3 = 0;
|
|
||||||
r.vtotal = 158;
|
r.vtotal = 158;
|
||||||
r.vblank = 155;
|
r.vblank = 155;
|
||||||
for(auto& color : r.pool) color = 0;
|
|
||||||
for(auto& p : r.palette) for(auto& color : p.color) color = 0;
|
|
||||||
r.htimerEnable = 0;
|
|
||||||
r.htimerRepeat = 0;
|
|
||||||
r.vtimerEnable = 0;
|
|
||||||
r.vtimerRepeat = 0;
|
|
||||||
r.htimerFrequency = 0;
|
|
||||||
r.vtimerFrequency = 0;
|
|
||||||
r.htimerCounter = 0;
|
|
||||||
r.vtimerCounter = 0;
|
|
||||||
|
|
||||||
video.power();
|
video.power();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,16 @@ struct PPU : Thread, IO {
|
||||||
auto scanline() -> void;
|
auto scanline() -> void;
|
||||||
auto frame() -> void;
|
auto frame() -> void;
|
||||||
auto step(uint clocks) -> void;
|
auto step(uint clocks) -> void;
|
||||||
auto latchRegisters() -> void;
|
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
|
|
||||||
//io.cpp
|
//io.cpp
|
||||||
auto portRead(uint16 addr) -> uint8 override;
|
auto portRead(uint16 addr) -> uint8 override;
|
||||||
auto portWrite(uint16 addr, uint8 data) -> void override;
|
auto portWrite(uint16 addr, uint8 data) -> void override;
|
||||||
|
|
||||||
//render-sprite.cpp
|
//latch.cpp
|
||||||
auto renderSpriteFetch() -> void;
|
auto latchRegisters() -> void;
|
||||||
auto renderSpriteDecode() -> void;
|
auto latchSprites() -> void;
|
||||||
|
auto latchOAM() -> void;
|
||||||
|
|
||||||
//render-mono.cpp
|
//render-mono.cpp
|
||||||
auto renderMonoFetch(uint14 offset, uint3 y, uint3 x) -> uint2;
|
auto renderMonoFetch(uint14 offset, uint3 y, uint3 x) -> uint2;
|
||||||
|
@ -33,9 +33,15 @@ struct PPU : Thread, IO {
|
||||||
auto renderColorScreenTwo() -> void;
|
auto renderColorScreenTwo() -> void;
|
||||||
auto renderColorSprite() -> void;
|
auto renderColorSprite() -> void;
|
||||||
|
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
//state
|
//state
|
||||||
uint12 output[224 * 144];
|
struct Pixel {
|
||||||
uint32 oam[2][128];
|
enum class Source : uint { Back, ScreenOne, ScreenTwo, Sprite };
|
||||||
|
Source source;
|
||||||
|
uint12 color;
|
||||||
|
};
|
||||||
|
|
||||||
struct Sprite {
|
struct Sprite {
|
||||||
uint8 x;
|
uint8 x;
|
||||||
|
@ -44,24 +50,21 @@ struct PPU : Thread, IO {
|
||||||
uint1 hflip;
|
uint1 hflip;
|
||||||
uint1 priority;
|
uint1 priority;
|
||||||
uint1 window;
|
uint1 window;
|
||||||
uint4 palette; //renderSpriteDecode() always sets bit3
|
uint4 palette; //latchSprites() always sets bit3
|
||||||
uint9 tile;
|
uint9 tile;
|
||||||
};
|
};
|
||||||
vector<Sprite> sprites;
|
|
||||||
|
|
||||||
struct Pixel {
|
uint12 output[224 * 144];
|
||||||
enum class Source : uint { Back, ScreenOne, ScreenTwo, Sprite };
|
|
||||||
Source source;
|
|
||||||
uint12 color;
|
|
||||||
} pixel;
|
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
bool field;
|
bool field;
|
||||||
uint vclk;
|
uint vclk;
|
||||||
uint hclk;
|
uint hclk;
|
||||||
|
Pixel pixel;
|
||||||
} s;
|
} s;
|
||||||
|
|
||||||
struct Latches {
|
struct Latches {
|
||||||
|
//latchRegisters()
|
||||||
uint8 backColor;
|
uint8 backColor;
|
||||||
|
|
||||||
uint1 screenOneEnable;
|
uint1 screenOneEnable;
|
||||||
|
@ -81,14 +84,19 @@ struct PPU : Thread, IO {
|
||||||
uint8 screenTwoWindowY1;
|
uint8 screenTwoWindowY1;
|
||||||
|
|
||||||
uint1 spriteEnable;
|
uint1 spriteEnable;
|
||||||
uint6 spriteBase;
|
|
||||||
uint7 spriteFirst;
|
|
||||||
uint8 spriteCount;
|
|
||||||
uint1 spriteWindowEnable;
|
uint1 spriteWindowEnable;
|
||||||
uint8 spriteWindowX0;
|
uint8 spriteWindowX0;
|
||||||
uint8 spriteWindowY0;
|
uint8 spriteWindowY0;
|
||||||
uint8 spriteWindowX1;
|
uint8 spriteWindowX1;
|
||||||
uint8 spriteWindowY1;
|
uint8 spriteWindowY1;
|
||||||
|
|
||||||
|
//latchSprites()
|
||||||
|
Sprite sprite[32];
|
||||||
|
uint spriteCount;
|
||||||
|
|
||||||
|
//latchOAM()
|
||||||
|
uint32 oam[2][128];
|
||||||
|
uint oamCount;
|
||||||
} l;
|
} l;
|
||||||
|
|
||||||
struct Registers {
|
struct Registers {
|
||||||
|
|
|
@ -23,7 +23,7 @@ auto PPU::renderColorPalette(uint4 palette, uint4 index) -> uint12 {
|
||||||
|
|
||||||
auto PPU::renderColorBack() -> void {
|
auto PPU::renderColorBack() -> void {
|
||||||
uint12 color = iram.read(0xfe00 + (l.backColor << 1), Word);
|
uint12 color = iram.read(0xfe00 + (l.backColor << 1), Word);
|
||||||
pixel = {Pixel::Source::Back, color};
|
s.pixel = {Pixel::Source::Back, color};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::renderColorScreenOne() -> void {
|
auto PPU::renderColorScreenOne() -> void {
|
||||||
|
@ -43,7 +43,7 @@ auto PPU::renderColorScreenOne() -> void {
|
||||||
uint4 tileColor = renderColorFetch(tileOffset, tileY, tileX);
|
uint4 tileColor = renderColorFetch(tileOffset, tileY, tileX);
|
||||||
if(tileColor == 0) return;
|
if(tileColor == 0) return;
|
||||||
|
|
||||||
pixel = {Pixel::Source::ScreenOne, renderColorPalette(tile.bits(9, 12), tileColor)};
|
s.pixel = {Pixel::Source::ScreenOne, renderColorPalette(tile.bits(9, 12), tileColor)};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::renderColorScreenTwo() -> void {
|
auto PPU::renderColorScreenTwo() -> void {
|
||||||
|
@ -68,14 +68,15 @@ auto PPU::renderColorScreenTwo() -> void {
|
||||||
uint4 tileColor = renderColorFetch(tileOffset, tileY, tileX);
|
uint4 tileColor = renderColorFetch(tileOffset, tileY, tileX);
|
||||||
if(tileColor == 0) return;
|
if(tileColor == 0) return;
|
||||||
|
|
||||||
pixel = {Pixel::Source::ScreenTwo, renderColorPalette(tile.bits(9, 12), tileColor)};
|
s.pixel = {Pixel::Source::ScreenTwo, renderColorPalette(tile.bits(9, 12), tileColor)};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::renderColorSprite() -> void {
|
auto PPU::renderColorSprite() -> void {
|
||||||
if(!l.spriteEnable) return;
|
if(!l.spriteEnable) return;
|
||||||
|
|
||||||
bool windowInside = s.hclk >= l.spriteWindowX0 && s.hclk <= l.spriteWindowX1;
|
bool windowInside = s.hclk >= l.spriteWindowX0 && s.hclk <= l.spriteWindowX1;
|
||||||
for(auto& sprite : sprites) {
|
for(auto index : range(l.spriteCount)) {
|
||||||
|
auto& sprite = l.sprite[index];
|
||||||
if(l.spriteWindowEnable && sprite.window == windowInside) continue;
|
if(l.spriteWindowEnable && sprite.window == windowInside) continue;
|
||||||
if((uint8)(s.hclk - sprite.x) > 7) continue;
|
if((uint8)(s.hclk - sprite.x) > 7) continue;
|
||||||
|
|
||||||
|
@ -84,9 +85,9 @@ auto PPU::renderColorSprite() -> void {
|
||||||
uint3 tileX = (uint8)(s.hclk - sprite.x) ^ (sprite.hflip ? 7 : 0);
|
uint3 tileX = (uint8)(s.hclk - sprite.x) ^ (sprite.hflip ? 7 : 0);
|
||||||
uint4 tileColor = renderColorFetch(tileOffset, tileY, tileX);
|
uint4 tileColor = renderColorFetch(tileOffset, tileY, tileX);
|
||||||
if(tileColor == 0) continue;
|
if(tileColor == 0) continue;
|
||||||
if(!sprite.priority && pixel.source == Pixel::Source::ScreenTwo) continue;
|
if(!sprite.priority && s.pixel.source == Pixel::Source::ScreenTwo) continue;
|
||||||
|
|
||||||
pixel = {Pixel::Source::Sprite, renderColorPalette(sprite.palette, tileColor)};
|
s.pixel = {Pixel::Source::Sprite, renderColorPalette(sprite.palette, tileColor)};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ auto PPU::renderMonoPalette(uint4 palette, uint2 index) -> uint12 {
|
||||||
|
|
||||||
auto PPU::renderMonoBack() -> void {
|
auto PPU::renderMonoBack() -> void {
|
||||||
uint4 poolColor = 15 - r.pool[l.backColor.bits(0,2)];
|
uint4 poolColor = 15 - r.pool[l.backColor.bits(0,2)];
|
||||||
pixel = {Pixel::Source::Back, poolColor << 0 | poolColor << 4 | poolColor << 8};
|
s.pixel = {Pixel::Source::Back, poolColor << 0 | poolColor << 4 | poolColor << 8};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::renderMonoScreenOne() -> void {
|
auto PPU::renderMonoScreenOne() -> void {
|
||||||
|
@ -43,7 +43,7 @@ auto PPU::renderMonoScreenOne() -> void {
|
||||||
uint2 tileColor = renderMonoFetch(tileOffset, tileY, tileX);
|
uint2 tileColor = renderMonoFetch(tileOffset, tileY, tileX);
|
||||||
if(tile.bit(11) && tileColor == 0) return;
|
if(tile.bit(11) && tileColor == 0) return;
|
||||||
|
|
||||||
pixel = {Pixel::Source::ScreenOne, renderMonoPalette(tile.bits(9,12), tileColor)};
|
s.pixel = {Pixel::Source::ScreenOne, renderMonoPalette(tile.bits(9,12), tileColor)};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::renderMonoScreenTwo() -> void {
|
auto PPU::renderMonoScreenTwo() -> void {
|
||||||
|
@ -68,14 +68,15 @@ auto PPU::renderMonoScreenTwo() -> void {
|
||||||
uint2 tileColor = renderMonoFetch(tileOffset, tileY, tileX);
|
uint2 tileColor = renderMonoFetch(tileOffset, tileY, tileX);
|
||||||
if(tile.bit(11) && tileColor == 0) return;
|
if(tile.bit(11) && tileColor == 0) return;
|
||||||
|
|
||||||
pixel = {Pixel::Source::ScreenTwo, renderMonoPalette(tile.bits(9,12), tileColor)};
|
s.pixel = {Pixel::Source::ScreenTwo, renderMonoPalette(tile.bits(9,12), tileColor)};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::renderMonoSprite() -> void {
|
auto PPU::renderMonoSprite() -> void {
|
||||||
if(!l.spriteEnable) return;
|
if(!l.spriteEnable) return;
|
||||||
|
|
||||||
bool windowInside = s.hclk >= l.spriteWindowX0 && s.hclk <= l.spriteWindowX1;
|
bool windowInside = s.hclk >= l.spriteWindowX0 && s.hclk <= l.spriteWindowX1;
|
||||||
for(auto& sprite : sprites) {
|
for(auto index : range(l.spriteCount)) {
|
||||||
|
auto& sprite = l.sprite[index];
|
||||||
if(l.spriteWindowEnable && sprite.window == windowInside) continue;
|
if(l.spriteWindowEnable && sprite.window == windowInside) continue;
|
||||||
if((uint8)(s.hclk - sprite.x) > 7) continue;
|
if((uint8)(s.hclk - sprite.x) > 7) continue;
|
||||||
|
|
||||||
|
@ -84,9 +85,9 @@ auto PPU::renderMonoSprite() -> void {
|
||||||
uint3 tileX = (uint8)(s.hclk - sprite.x) ^ (sprite.hflip ? 7 : 0);
|
uint3 tileX = (uint8)(s.hclk - sprite.x) ^ (sprite.hflip ? 7 : 0);
|
||||||
uint2 tileColor = renderMonoFetch(tileOffset, tileY, tileX);
|
uint2 tileColor = renderMonoFetch(tileOffset, tileY, tileX);
|
||||||
if(sprite.palette.bit(2) && tileColor == 0) continue;
|
if(sprite.palette.bit(2) && tileColor == 0) continue;
|
||||||
if(!sprite.priority && pixel.source == Pixel::Source::ScreenTwo) continue;
|
if(!sprite.priority && s.pixel.source == Pixel::Source::ScreenTwo) continue;
|
||||||
|
|
||||||
pixel = {Pixel::Source::Sprite, renderMonoPalette(sprite.palette, tileColor)};
|
s.pixel = {Pixel::Source::Sprite, renderMonoPalette(sprite.palette, tileColor)};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
auto PPU::renderSpriteFetch() -> void {
|
|
||||||
uint16 spriteBase = l.spriteBase.bits(0, 4 + system.depth()) << 9;
|
|
||||||
for(auto spriteIndex : range(128)) {
|
|
||||||
oam[s.field][spriteIndex] = iram.read(spriteBase + (spriteIndex << 2), Long);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto PPU::renderSpriteDecode() -> void {
|
|
||||||
sprites.reset();
|
|
||||||
sprites.reserve(32);
|
|
||||||
if(!l.spriteEnable) return;
|
|
||||||
|
|
||||||
uint offset = 0;
|
|
||||||
bool windowInside = s.vclk >= l.spriteWindowY0 && s.vclk <= l.spriteWindowY1;
|
|
||||||
uint7 spriteIndex = l.spriteFirst;
|
|
||||||
uint8 spriteCount = min(128, (uint)l.spriteCount);
|
|
||||||
while(spriteCount--) {
|
|
||||||
uint32 attributes = oam[s.field][spriteIndex++];
|
|
||||||
|
|
||||||
Sprite sprite;
|
|
||||||
sprite.x = attributes.bits(24,31);
|
|
||||||
if(sprite.x > 224 && sprite.x < 249) continue;
|
|
||||||
sprite.y = attributes.bits(16,23);
|
|
||||||
if((uint8)(s.vclk - sprite.y) > 7) continue;
|
|
||||||
sprite.vflip = attributes.bit(15);
|
|
||||||
sprite.hflip = attributes.bit(14);
|
|
||||||
sprite.priority = attributes.bit(13);
|
|
||||||
sprite.window = attributes.bit(12);
|
|
||||||
if(l.spriteWindowEnable && sprite.window == windowInside) continue;
|
|
||||||
sprite.palette = 8 + attributes.bits(9,11);
|
|
||||||
sprite.tile = attributes.bits(0,8);
|
|
||||||
|
|
||||||
sprites.append(sprite);
|
|
||||||
if(sprites.size() >= 32) break;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
auto PPU::serialize(serializer& s) -> void {
|
||||||
|
s.integer(this->s.field);
|
||||||
|
s.integer(this->s.vclk);
|
||||||
|
s.integer(this->s.hclk);
|
||||||
|
s.integer((uint&)this->s.pixel.source);
|
||||||
|
s.integer(this->s.pixel.color);
|
||||||
|
|
||||||
|
s.integer(l.backColor);
|
||||||
|
s.integer(l.screenOneEnable);
|
||||||
|
s.integer(l.screenOneMapBase);
|
||||||
|
s.integer(l.scrollOneX);
|
||||||
|
s.integer(l.scrollOneY);
|
||||||
|
s.integer(l.screenTwoEnable);
|
||||||
|
s.integer(l.screenTwoMapBase);
|
||||||
|
s.integer(l.scrollTwoX);
|
||||||
|
s.integer(l.scrollTwoY);
|
||||||
|
s.integer(l.screenTwoWindowEnable);
|
||||||
|
s.integer(l.screenTwoWindowInvert);
|
||||||
|
s.integer(l.screenTwoWindowX0);
|
||||||
|
s.integer(l.screenTwoWindowY0);
|
||||||
|
s.integer(l.screenTwoWindowX1);
|
||||||
|
s.integer(l.screenTwoWindowY1);
|
||||||
|
s.integer(l.spriteEnable);
|
||||||
|
s.integer(l.spriteWindowEnable);
|
||||||
|
s.integer(l.spriteWindowX0);
|
||||||
|
s.integer(l.spriteWindowY0);
|
||||||
|
s.integer(l.spriteWindowX1);
|
||||||
|
s.integer(l.spriteWindowY1);
|
||||||
|
|
||||||
|
for(uint n : range(32)) {
|
||||||
|
s.integer(l.sprite[n].x);
|
||||||
|
s.integer(l.sprite[n].y);
|
||||||
|
s.integer(l.sprite[n].vflip);
|
||||||
|
s.integer(l.sprite[n].hflip);
|
||||||
|
s.integer(l.sprite[n].priority);
|
||||||
|
s.integer(l.sprite[n].window);
|
||||||
|
s.integer(l.sprite[n].palette);
|
||||||
|
s.integer(l.sprite[n].tile);
|
||||||
|
}
|
||||||
|
s.integer(l.spriteCount);
|
||||||
|
|
||||||
|
for(uint n : range(2)) {
|
||||||
|
s.array(l.oam[n]);
|
||||||
|
}
|
||||||
|
s.integer(l.oamCount);
|
||||||
|
|
||||||
|
s.integer(r.screenOneEnable);
|
||||||
|
s.integer(r.screenTwoEnable);
|
||||||
|
s.integer(r.spriteEnable);
|
||||||
|
s.integer(r.spriteWindowEnable);
|
||||||
|
s.integer(r.screenTwoWindowInvert);
|
||||||
|
s.integer(r.screenTwoWindowEnable);
|
||||||
|
s.integer(r.backColor);
|
||||||
|
s.integer(r.lineCompare);
|
||||||
|
s.integer(r.spriteBase);
|
||||||
|
s.integer(r.spriteFirst);
|
||||||
|
s.integer(r.spriteCount);
|
||||||
|
s.integer(r.screenOneMapBase);
|
||||||
|
s.integer(r.screenTwoMapBase);
|
||||||
|
s.integer(r.screenTwoWindowX0);
|
||||||
|
s.integer(r.screenTwoWindowY0);
|
||||||
|
s.integer(r.screenTwoWindowX1);
|
||||||
|
s.integer(r.screenTwoWindowY1);
|
||||||
|
s.integer(r.spriteWindowX0);
|
||||||
|
s.integer(r.spriteWindowY0);
|
||||||
|
s.integer(r.spriteWindowX1);
|
||||||
|
s.integer(r.spriteWindowY1);
|
||||||
|
s.integer(r.scrollOneX);
|
||||||
|
s.integer(r.scrollOneY);
|
||||||
|
s.integer(r.scrollTwoX);
|
||||||
|
s.integer(r.scrollTwoY);
|
||||||
|
s.integer(r.lcdEnable);
|
||||||
|
s.integer(r.lcdContrast);
|
||||||
|
s.integer(r.lcdUnknown);
|
||||||
|
s.integer(r.iconSleep);
|
||||||
|
s.integer(r.iconVertical);
|
||||||
|
s.integer(r.iconHorizontal);
|
||||||
|
s.integer(r.iconAux1);
|
||||||
|
s.integer(r.iconAux2);
|
||||||
|
s.integer(r.iconAux3);
|
||||||
|
s.integer(r.vtotal);
|
||||||
|
s.integer(r.vblank);
|
||||||
|
s.array(r.pool);
|
||||||
|
for(uint n : range(16)) {
|
||||||
|
s.array(r.palette[n].color);
|
||||||
|
}
|
||||||
|
s.integer(r.htimerEnable);
|
||||||
|
s.integer(r.htimerRepeat);
|
||||||
|
s.integer(r.vtimerEnable);
|
||||||
|
s.integer(r.vtimerRepeat);
|
||||||
|
s.integer(r.htimerFrequency);
|
||||||
|
s.integer(r.vtimerFrequency);
|
||||||
|
s.integer(r.htimerCounter);
|
||||||
|
s.integer(r.vtimerCounter);
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
auto System::serializeInit() -> void {
|
||||||
|
serializer s;
|
||||||
|
|
||||||
|
uint signature = 0, version = 0;
|
||||||
|
char hash[64], description[512];
|
||||||
|
|
||||||
|
s.integer(signature);
|
||||||
|
s.integer(version);
|
||||||
|
s.array(hash);
|
||||||
|
s.array(description);
|
||||||
|
|
||||||
|
serializeAll(s);
|
||||||
|
_serializeSize = s.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto System::serialize() -> serializer {
|
||||||
|
serializer s(_serializeSize);
|
||||||
|
|
||||||
|
uint signature = 0x31545342, version = Info::SerializerVersion;
|
||||||
|
char hash[64], description[512];
|
||||||
|
memory::copy(&hash, (const char*)cartridge.information.sha256, 64);
|
||||||
|
memory::fill(&description, 512);
|
||||||
|
|
||||||
|
s.integer(signature);
|
||||||
|
s.integer(version);
|
||||||
|
s.array(hash);
|
||||||
|
s.array(description);
|
||||||
|
|
||||||
|
serializeAll(s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto System::unserialize(serializer& s) -> bool {
|
||||||
|
uint signature, version;
|
||||||
|
char hash[64], description[512];
|
||||||
|
|
||||||
|
s.integer(signature);
|
||||||
|
s.integer(version);
|
||||||
|
s.array(hash);
|
||||||
|
s.array(description);
|
||||||
|
|
||||||
|
if(signature != 0x31545342) return false;
|
||||||
|
if(version != Info::SerializerVersion) return false;
|
||||||
|
|
||||||
|
power();
|
||||||
|
serializeAll(s);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto System::serializeAll(serializer& s) -> void {
|
||||||
|
system.serialize(s);
|
||||||
|
cpu.serialize(s);
|
||||||
|
ppu.serialize(s);
|
||||||
|
apu.serialize(s);
|
||||||
|
cartridge.serialize(s);
|
||||||
|
iram.serialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto System::serialize(serializer& s) -> void {
|
||||||
|
eeprom.serialize(s);
|
||||||
|
|
||||||
|
s.integer(r.depth);
|
||||||
|
s.integer(r.color);
|
||||||
|
s.integer(r.format);
|
||||||
|
s.integer(r.unknown);
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ namespace WonderSwan {
|
||||||
|
|
||||||
System system;
|
System system;
|
||||||
#include "io.cpp"
|
#include "io.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
|
|
||||||
auto System::loaded() const -> bool { return _loaded; }
|
auto System::loaded() const -> bool { return _loaded; }
|
||||||
auto System::model() const -> Model { return _model; }
|
auto System::model() const -> Model { return _model; }
|
||||||
|
@ -14,6 +15,7 @@ auto System::packed() const -> bool { return r.format == 1; }
|
||||||
auto System::depth() const -> bool { return r.depth == 1; }
|
auto System::depth() const -> bool { return r.depth == 1; }
|
||||||
|
|
||||||
auto System::init() -> void {
|
auto System::init() -> void {
|
||||||
|
assert(interface != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::term() -> void {
|
auto System::term() -> void {
|
||||||
|
@ -37,6 +39,7 @@ auto System::load(Model model) -> void {
|
||||||
cartridge.load();
|
cartridge.load();
|
||||||
_loaded = true;
|
_loaded = true;
|
||||||
_orientation = cartridge.information.orientation;
|
_orientation = cartridge.information.orientation;
|
||||||
|
serializeInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::unload() -> void {
|
auto System::unload() -> void {
|
||||||
|
@ -69,8 +72,18 @@ auto System::power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::run() -> void {
|
auto System::run() -> void {
|
||||||
while(scheduler.enter() != Scheduler::Event::Frame);
|
scheduler.enter();
|
||||||
|
pollKeypad();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto System::runToSave() -> void {
|
||||||
|
scheduler.synchronize(cpu.thread);
|
||||||
|
scheduler.synchronize(ppu.thread);
|
||||||
|
scheduler.synchronize(apu.thread);
|
||||||
|
scheduler.synchronize(cartridge.thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto System::pollKeypad() -> void {
|
||||||
bool rotate = keypad.rotate;
|
bool rotate = keypad.rotate;
|
||||||
keypad.y1 = interface->inputPoll(_orientation, 0, 0);
|
keypad.y1 = interface->inputPoll(_orientation, 0, 0);
|
||||||
keypad.y2 = interface->inputPoll(_orientation, 0, 1);
|
keypad.y2 = interface->inputPoll(_orientation, 0, 1);
|
||||||
|
|
|
@ -13,10 +13,20 @@ struct System : IO {
|
||||||
auto unload() -> void;
|
auto unload() -> void;
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
auto run() -> void;
|
auto run() -> void;
|
||||||
|
auto runToSave() -> void;
|
||||||
|
auto pollKeypad() -> void;
|
||||||
|
|
||||||
|
//io.cpp
|
||||||
auto portRead(uint16 addr) -> uint8 override;
|
auto portRead(uint16 addr) -> uint8 override;
|
||||||
auto portWrite(uint16 addr, uint8 data) -> void override;
|
auto portWrite(uint16 addr, uint8 data) -> void override;
|
||||||
|
|
||||||
|
//serialization.cpp
|
||||||
|
auto serializeInit() -> void;
|
||||||
|
auto serialize() -> serializer;
|
||||||
|
auto unserialize(serializer&) -> bool;
|
||||||
|
auto serializeAll(serializer&) -> void;
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
struct Information {
|
struct Information {
|
||||||
string manifest;
|
string manifest;
|
||||||
} information;
|
} information;
|
||||||
|
@ -42,6 +52,7 @@ privileged:
|
||||||
bool _loaded = false;
|
bool _loaded = false;
|
||||||
Model _model = Model::WonderSwan;
|
Model _model = Model::WonderSwan;
|
||||||
bool _orientation = 0; //0 = horizontal, 1 = vertical
|
bool _orientation = 0; //0 = horizontal, 1 = vertical
|
||||||
|
uint _serializeSize = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern System system;
|
extern System system;
|
||||||
|
|
|
@ -58,6 +58,7 @@ namespace WonderSwan {
|
||||||
#include <ws/cpu/cpu.hpp>
|
#include <ws/cpu/cpu.hpp>
|
||||||
#include <ws/ppu/ppu.hpp>
|
#include <ws/ppu/ppu.hpp>
|
||||||
#include <ws/apu/apu.hpp>
|
#include <ws/apu/apu.hpp>
|
||||||
|
#include <ws/cheat/cheat.hpp>
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <ws/interface/interface.hpp>
|
#include <ws/interface/interface.hpp>
|
||||||
|
|
|
@ -10,6 +10,7 @@ struct WonderSwanCartridge {
|
||||||
string ramType;
|
string ramType;
|
||||||
uint ramSize;
|
uint ramSize;
|
||||||
bool orientation; //0 = horizontal; 1 = vertical
|
bool orientation; //0 = horizontal; 1 = vertical
|
||||||
|
bool hasRTC;
|
||||||
} information;
|
} information;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,10 +35,14 @@ WonderSwanCartridge::WonderSwanCartridge(string location, uint8_t* data, uint si
|
||||||
|
|
||||||
information.orientation = metadata[12] & 1;
|
information.orientation = metadata[12] & 1;
|
||||||
|
|
||||||
|
information.hasRTC = metadata[13] & 1;
|
||||||
|
|
||||||
manifest.append("board\n");
|
manifest.append("board\n");
|
||||||
manifest.append(" rom name=program.rom size=0x", hex(size), "\n");
|
manifest.append(" rom name=program.rom size=0x", hex(size), "\n");
|
||||||
if(information.ramType && information.ramSize)
|
if(information.ramType && information.ramSize)
|
||||||
manifest.append(" ram name=save.ram type=", information.ramType, " size=0x", hex(information.ramSize), "\n");
|
manifest.append(" ram name=save.ram type=", information.ramType, " size=0x", hex(information.ramSize), "\n");
|
||||||
|
if(information.hasRTC)
|
||||||
|
manifest.append(" rtc name=rtc.ram size=16\n");
|
||||||
manifest.append("\n");
|
manifest.append("\n");
|
||||||
manifest.append("information\n");
|
manifest.append("information\n");
|
||||||
manifest.append(" title: ", prefixname(location), "\n");
|
manifest.append(" title: ", prefixname(location), "\n");
|
||||||
|
|
Loading…
Reference in New Issue