mirror of https://github.com/bsnes-emu/bsnes.git
Update to v102r03 release.
byuu says: Changelog: - PCE: split VCE from VDC - HuC6280: changed bus from (uint21 addr) to (uint8 bank, uint13 addr) - added SuperGrafx emulation (adds secondary VDC, plus new VPC) The VDC now has no concept of the actual display raster timing, and instead is driven by Vpulse (start of frame) and Hpulse (start of scanline) signals from the VCE. One still can't render the start of the next scanline onto the current scanline through overly aggressive timings, but it shouldn't be too much more difficult to allow that to occur now. This process incurs quite a major speed hit, so low-end systems with Atom CPUs can't run things at 60fps anymore. The timing needs a lot of work. The pixels end up very jagged if the VCE doesn't output batches of 2-4 pixels at a time. But this should not be a requirement at all, so I'm not sure what's going wrong there. Yo, Bro and the 512-width mode of TV Sports Basketball is now broken as a result of these changes, and I'm not sure why. To load SuperGrafx games, you're going to have to change the .pce extensions to .sg or .sgx. Or you can manually move the games from the PC Engine folder to the SuperGrafx folder and change the game folder extensions. I have no way to tell the games apart. Mednafen uses CRC32 comparisons, and I may consider that since there's only five games, but I'm not sure yet. The only SuperGrafx game that's playable right now is Aldynes. And the priorities are all screwed up. I don't understand how the windows or the priorities work at all from sgxtech.txt, so ... yeah. It's pretty broken, but it's a start. I could really use some help with this, as I'm very lost right now with rendering :/ ----- Note that the SuperGrafx is technically its own system, it's not an add-on. As such, I'm giving it a separate .sys folder, and a separate library. There's debate over how to name this thing. "SuperGrafx" appears more popular than "Super Grafx". And you might also call it the "PC Engine SuperGrafx", but I decided to leave off the prefix so it appears more distinct.
This commit is contained in:
parent
bdc100e123
commit
186f008574
|
@ -12,7 +12,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "102.02";
|
static const string Version = "102.03";
|
||||||
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/";
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
processors += huc6280
|
processors += huc6280
|
||||||
|
|
||||||
objects += pce-interface
|
objects += pce-interface
|
||||||
objects += pce-cpu pce-vdc pce-psg
|
objects += pce-cpu pce-vpc pce-vce pce-vdc pce-psg
|
||||||
objects += pce-system pce-cartridge
|
objects += pce-system pce-cartridge
|
||||||
objects += pce-controller
|
objects += pce-controller
|
||||||
|
|
||||||
obj/pce-interface.o: pce/interface/interface.cpp $(call rwildcard,pce/interface)
|
obj/pce-interface.o: pce/interface/interface.cpp $(call rwildcard,pce/interface)
|
||||||
obj/pce-cpu.o: pce/cpu/cpu.cpp $(call rwildcard,pce/cpu)
|
obj/pce-cpu.o: pce/cpu/cpu.cpp $(call rwildcard,pce/cpu)
|
||||||
|
obj/pce-vpc.o: pce/vpc/vpc.cpp $(call rwildcard,pce/vpc)
|
||||||
|
obj/pce-vce.o: pce/vce/vce.cpp $(call rwildcard,pce/vce)
|
||||||
obj/pce-vdc.o: pce/vdc/vdc.cpp $(call rwildcard,pce/vdc)
|
obj/pce-vdc.o: pce/vdc/vdc.cpp $(call rwildcard,pce/vdc)
|
||||||
obj/pce-psg.o: pce/psg/psg.cpp $(call rwildcard,pce/psg)
|
obj/pce-psg.o: pce/psg/psg.cpp $(call rwildcard,pce/psg)
|
||||||
obj/pce-system.o: pce/system/system.cpp $(call rwildcard,pce/system)
|
obj/pce-system.o: pce/system/system.cpp $(call rwildcard,pce/system)
|
||||||
|
|
|
@ -7,9 +7,17 @@ Cartridge cartridge;
|
||||||
auto Cartridge::load() -> bool {
|
auto Cartridge::load() -> bool {
|
||||||
information = {};
|
information = {};
|
||||||
|
|
||||||
if(auto pathID = platform->load(ID::PCEngine, "PC Engine", "pce")) {
|
if(Model::PCEngine()) {
|
||||||
information.pathID = pathID();
|
if(auto pathID = platform->load(ID::PCEngine, "PC Engine", "pce")) {
|
||||||
} else return false;
|
information.pathID = pathID();
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Model::SuperGrafx()) {
|
||||||
|
if(auto pathID = platform->load(ID::SuperGrafx, "SuperGrafx", "sg")) {
|
||||||
|
information.pathID = pathID();
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) {
|
if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) {
|
||||||
information.manifest = fp->reads();
|
information.manifest = fp->reads();
|
||||||
|
|
|
@ -19,7 +19,9 @@ auto CPU::main() -> void {
|
||||||
auto CPU::step(uint clocks) -> void {
|
auto CPU::step(uint clocks) -> void {
|
||||||
Thread::step(clocks);
|
Thread::step(clocks);
|
||||||
timer.step(clocks);
|
timer.step(clocks);
|
||||||
synchronize(vdc);
|
synchronize(vdc0);
|
||||||
|
synchronize(vdc1);
|
||||||
|
synchronize(vce);
|
||||||
synchronize(psg);
|
synchronize(psg);
|
||||||
for(auto peripheral : peripherals) synchronize(*peripheral);
|
for(auto peripheral : peripherals) synchronize(*peripheral);
|
||||||
}
|
}
|
||||||
|
@ -28,9 +30,10 @@ auto CPU::power() -> void {
|
||||||
HuC6280::power();
|
HuC6280::power();
|
||||||
create(CPU::Enter, system.colorburst() * 2.0);
|
create(CPU::Enter, system.colorburst() * 2.0);
|
||||||
|
|
||||||
r.pc.byte(0) = read(0x1ffe);
|
r.pc.byte(0) = read(0x00, 0x1ffe);
|
||||||
r.pc.byte(1) = read(0x1fff);
|
r.pc.byte(1) = read(0x00, 0x1fff);
|
||||||
|
|
||||||
|
for(auto& byte : ram) byte = 0x00;
|
||||||
memory::fill(&irq, sizeof(IRQ));
|
memory::fill(&irq, sizeof(IRQ));
|
||||||
memory::fill(&timer, sizeof(Timer));
|
memory::fill(&timer, sizeof(Timer));
|
||||||
memory::fill(&io, sizeof(IO));
|
memory::fill(&io, sizeof(IO));
|
||||||
|
|
|
@ -9,8 +9,9 @@ struct CPU : Processor::HuC6280, Thread {
|
||||||
auto lastCycle() -> void override;
|
auto lastCycle() -> void override;
|
||||||
|
|
||||||
//io.cpp
|
//io.cpp
|
||||||
auto read(uint21 addr) -> uint8 override;
|
auto read(uint8 bank, uint13 addr) -> uint8 override;
|
||||||
auto write(uint21 addr, uint8 data) -> void override;
|
auto write(uint8 bank, uint13 addr, uint8 data) -> void override;
|
||||||
|
auto store(uint2 addr, uint8 data) -> void override;
|
||||||
|
|
||||||
//timer.cpp
|
//timer.cpp
|
||||||
auto timerStep(uint clocks) -> void;
|
auto timerStep(uint clocks) -> void;
|
||||||
|
@ -60,7 +61,7 @@ struct CPU : Processor::HuC6280, Thread {
|
||||||
} io;
|
} io;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8 ram[0x2000];
|
uint8 ram[0x8000]; //PC Engine = 8KB, SuperGrafx = 32KB
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CPU cpu;
|
extern CPU cpu;
|
||||||
|
|
|
@ -1,23 +1,26 @@
|
||||||
auto CPU::read(uint21 addr) -> uint8 {
|
auto CPU::read(uint8 bank, uint13 addr) -> uint8 {
|
||||||
//$000000-0fffff HuCard
|
//$00-7f HuCard
|
||||||
if(!addr.bit(20)) {
|
if(!bank.bit(7)) {
|
||||||
return cartridge.read(addr);
|
return cartridge.read(bank << 13 | addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 bank = addr.bits(13,20);
|
//$f8-fb RAM
|
||||||
addr = addr.bits(0,12);
|
|
||||||
|
|
||||||
//$1f8000-1fbfff RAM
|
|
||||||
if(bank >= 0xf8 && bank <= 0xfb) {
|
if(bank >= 0xf8 && bank <= 0xfb) {
|
||||||
return ram[addr];
|
if(Model::PCEngine()) return ram[addr];
|
||||||
|
if(Model::SuperGrafx()) return ram[bank.bits(0,1) << 13 | addr];
|
||||||
}
|
}
|
||||||
|
|
||||||
//$1fe000-$1fffff Hardware
|
//$ff Hardware
|
||||||
if(bank == 0xff) {
|
if(bank == 0xff) {
|
||||||
//$0000-03ff VDC
|
//$0000-03ff VDC or VPC
|
||||||
|
if((addr & 0x1c00) == 0x0000) {
|
||||||
|
if(Model::PCEngine()) return vdc0.read(addr);
|
||||||
|
if(Model::SuperGrafx()) return vpc.read(addr);
|
||||||
|
}
|
||||||
|
|
||||||
//$0400-07ff VCE
|
//$0400-07ff VCE
|
||||||
if((addr & 0x1800) == 0x0000) {
|
if((addr & 0x1c00) == 0x0400) {
|
||||||
return vdc.read(addr);
|
return vce.read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
//$0800-0bff PSG
|
//$0800-0bff PSG
|
||||||
|
@ -84,27 +87,30 @@ auto CPU::read(uint21 addr) -> uint8 {
|
||||||
return 0xff;
|
return 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::write(uint21 addr, uint8 data) -> void {
|
auto CPU::write(uint8 bank, uint13 addr, uint8 data) -> void {
|
||||||
//$000000-0fffff HuCard
|
//$00-7f HuCard
|
||||||
if(!addr.bit(20)) {
|
if(!bank.bit(7)) {
|
||||||
return cartridge.write(addr, data);
|
return cartridge.write(bank << 13 | addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 bank = addr.bits(13,20);
|
//$f8-fb RAM
|
||||||
addr = addr.bits(0,12);
|
|
||||||
|
|
||||||
//$1f8000-1fbfff RAM
|
|
||||||
if(bank >= 0xf8 && bank <= 0xfb) {
|
if(bank >= 0xf8 && bank <= 0xfb) {
|
||||||
ram[addr] = data;
|
if(Model::PCEngine()) ram[addr] = data;
|
||||||
|
if(Model::SuperGrafx()) ram[bank.bits(0,1) << 13 | addr] = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//$1fe000-1fffff Hardware
|
//$1fe000-1fffff Hardware
|
||||||
if(bank == 0xff) {
|
if(bank == 0xff) {
|
||||||
//$0000-03ff VDC
|
//$0000-03ff VDC or VPC
|
||||||
|
if((addr & 0x1c00) == 0x0000) {
|
||||||
|
if(Model::PCEngine()) return vdc0.write(addr, data);
|
||||||
|
if(Model::SuperGrafx()) return vpc.write(addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
//$0400-07ff VCE
|
//$0400-07ff VCE
|
||||||
if((addr & 0x1800) == 0x0000) {
|
if((addr & 0x1c00) == 0x0400) {
|
||||||
return vdc.write(addr, data);
|
return vce.write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
//$0800-0bff PSG
|
//$0800-0bff PSG
|
||||||
|
@ -159,3 +165,10 @@ auto CPU::write(uint21 addr, uint8 data) -> void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ST0, ST1, ST2
|
||||||
|
auto CPU::store(uint2 addr, uint8 data) -> void {
|
||||||
|
if(addr) addr++; //0,1,2 => 0,2,3
|
||||||
|
if(Model::PCEngine()) vdc0.write(addr, data);
|
||||||
|
if(Model::SuperGrafx()) vpc.store(addr, data);
|
||||||
|
}
|
||||||
|
|
|
@ -2,18 +2,17 @@
|
||||||
|
|
||||||
namespace PCEngine {
|
namespace PCEngine {
|
||||||
|
|
||||||
|
Model model;
|
||||||
Settings settings;
|
Settings settings;
|
||||||
|
#include "pc-engine.cpp"
|
||||||
|
#include "supergrafx.cpp"
|
||||||
|
|
||||||
Interface::Interface() {
|
Interface::Interface() {
|
||||||
information.manufacturer = "NEC";
|
information.overscan = true;
|
||||||
information.name = "PC Engine";
|
|
||||||
information.overscan = true;
|
|
||||||
|
|
||||||
information.capability.states = false;
|
information.capability.states = false;
|
||||||
information.capability.cheats = false;
|
information.capability.cheats = false;
|
||||||
|
|
||||||
media.append({ID::PCEngine, "PC Engine", "pce"});
|
|
||||||
|
|
||||||
Port controllerPort{ID::Port::Controller, "Controller Port"};
|
Port controllerPort{ID::Port::Controller, "Controller Port"};
|
||||||
|
|
||||||
{ Device device{ID::Device::None, "None"};
|
{ Device device{ID::Device::None, "None"};
|
||||||
|
@ -83,11 +82,6 @@ auto Interface::loaded() -> bool {
|
||||||
return system.loaded();
|
return system.loaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::load(uint id) -> bool {
|
|
||||||
if(id == ID::PCEngine) return system.load(this);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::save() -> void {
|
auto Interface::save() -> void {
|
||||||
system.save();
|
system.save();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ struct ID {
|
||||||
enum : uint {
|
enum : uint {
|
||||||
System,
|
System,
|
||||||
PCEngine,
|
PCEngine,
|
||||||
|
SuperGrafx,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Port { enum : uint {
|
struct Port { enum : uint {
|
||||||
|
@ -33,7 +34,6 @@ struct Interface : Emulator::Interface {
|
||||||
auto audioFrequency() -> double override;
|
auto audioFrequency() -> double override;
|
||||||
|
|
||||||
auto loaded() -> bool override;
|
auto loaded() -> bool override;
|
||||||
auto load(uint id) -> bool override;
|
|
||||||
auto save() -> void override;
|
auto save() -> void override;
|
||||||
auto unload() -> void override;
|
auto unload() -> void override;
|
||||||
|
|
||||||
|
@ -49,6 +49,18 @@ struct Interface : Emulator::Interface {
|
||||||
auto set(const string& name, const any& value) -> bool override;
|
auto set(const string& name, const any& value) -> bool override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PCEngineInterface : Interface {
|
||||||
|
PCEngineInterface();
|
||||||
|
|
||||||
|
auto load(uint id) -> bool override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SuperGrafxInterface : Interface {
|
||||||
|
SuperGrafxInterface();
|
||||||
|
|
||||||
|
auto load(uint id) -> bool override;
|
||||||
|
};
|
||||||
|
|
||||||
struct Settings {
|
struct Settings {
|
||||||
uint controllerPort = 0;
|
uint controllerPort = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
PCEngineInterface::PCEngineInterface() {
|
||||||
|
information.manufacturer = "NEC";
|
||||||
|
information.name = "PC Engine";
|
||||||
|
|
||||||
|
media.append({ID::PCEngine, "PC Engine", "pce"});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PCEngineInterface::load(uint id) -> bool {
|
||||||
|
if(id == ID::PCEngine) return system.load(this, id);
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
SuperGrafxInterface::SuperGrafxInterface() {
|
||||||
|
information.manufacturer = "NEC";
|
||||||
|
information.name = "SuperGrafx";
|
||||||
|
|
||||||
|
media.append({ID::SuperGrafx, "SuperGrafx", "sg"});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SuperGrafxInterface::load(uint id) -> bool {
|
||||||
|
if(id == ID::SuperGrafx) return system.load(this, id);
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -26,9 +26,17 @@ namespace PCEngine {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Model {
|
||||||
|
inline static auto PCEngine() -> bool { return id == 1; }
|
||||||
|
inline static auto SuperGrafx() -> bool { return id == 2; }
|
||||||
|
static uint id;
|
||||||
|
};
|
||||||
|
|
||||||
#include <pce/controller/controller.hpp>
|
#include <pce/controller/controller.hpp>
|
||||||
|
|
||||||
#include <pce/cpu/cpu.hpp>
|
#include <pce/cpu/cpu.hpp>
|
||||||
|
#include <pce/vpc/vpc.hpp>
|
||||||
|
#include <pce/vce/vce.hpp>
|
||||||
#include <pce/vdc/vdc.hpp>
|
#include <pce/vdc/vdc.hpp>
|
||||||
#include <pce/psg/psg.hpp>
|
#include <pce/psg/psg.hpp>
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,17 @@
|
||||||
|
|
||||||
namespace PCEngine {
|
namespace PCEngine {
|
||||||
|
|
||||||
|
uint Model::id;
|
||||||
System system;
|
System system;
|
||||||
Scheduler scheduler;
|
Scheduler scheduler;
|
||||||
#include "peripherals.cpp"
|
#include "peripherals.cpp"
|
||||||
|
|
||||||
auto System::run() -> void {
|
auto System::run() -> void {
|
||||||
if(scheduler.enter() == Scheduler::Event::Frame) vdc.refresh();
|
if(scheduler.enter() == Scheduler::Event::Frame) vce.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::load(Emulator::Interface* interface) -> bool {
|
auto System::load(Emulator::Interface* interface, uint id) -> bool {
|
||||||
|
Model::id = id;
|
||||||
information = {};
|
information = {};
|
||||||
|
|
||||||
if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) {
|
if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) {
|
||||||
|
@ -45,7 +47,10 @@ auto System::power() -> void {
|
||||||
scheduler.reset();
|
scheduler.reset();
|
||||||
cartridge.power();
|
cartridge.power();
|
||||||
cpu.power();
|
cpu.power();
|
||||||
vdc.power();
|
vpc.power();
|
||||||
|
vce.power();
|
||||||
|
vdc0.power();
|
||||||
|
vdc1.power();
|
||||||
psg.power();
|
psg.power();
|
||||||
scheduler.primary(cpu);
|
scheduler.primary(cpu);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ struct System {
|
||||||
|
|
||||||
auto run() -> void;
|
auto run() -> void;
|
||||||
|
|
||||||
auto load(Emulator::Interface*) -> bool;
|
auto load(Emulator::Interface*, uint) -> bool;
|
||||||
auto save() -> void;
|
auto save() -> void;
|
||||||
auto unload() -> void;
|
auto unload() -> void;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
auto VCE::read(uint3 addr) -> uint8 {
|
||||||
|
if(addr == 0x04) {
|
||||||
|
//CTR
|
||||||
|
uint8 data = cram.read(cram.address).bits(0,7);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0x05) {
|
||||||
|
//CTR
|
||||||
|
uint1 data = cram.read(cram.address).bit(8);
|
||||||
|
cram.address++;
|
||||||
|
return 0xfe | data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VCE::write(uint3 addr, uint8 data) -> void {
|
||||||
|
if(addr == 0x00) {
|
||||||
|
//CR
|
||||||
|
if(data.bits(0,1) == 0) io.clock = 4;
|
||||||
|
if(data.bits(0,1) == 1) io.clock = 3;
|
||||||
|
if(data.bits(0,1) == 2) io.clock = 2;
|
||||||
|
if(data.bits(0,1) == 3) io.clock = 2;
|
||||||
|
io.extraLine = data.bit(2);
|
||||||
|
io.grayscale = data.bit(7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0x02) {
|
||||||
|
//CTA
|
||||||
|
cram.address.bits(0,7) = data.bits(0,7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0x03) {
|
||||||
|
//CTA
|
||||||
|
cram.address.bit(8) = data.bit(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0x04) {
|
||||||
|
//CTW
|
||||||
|
cram.write(cram.address, 0, data.bits(0,7));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0x05) {
|
||||||
|
//CTW
|
||||||
|
cram.write(cram.address, 1, data.bit(0));
|
||||||
|
cram.address++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
auto VCE::CRAM::read(uint9 addr) -> uint9 {
|
||||||
|
return data[addr];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VCE::CRAM::write(uint9 addr, bool a0, uint8 value) -> void {
|
||||||
|
if(!a0) {
|
||||||
|
data[addr].bits(0,7) = value.bits(0,7);
|
||||||
|
} else {
|
||||||
|
data[addr].bit(8) = value.bit(0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
#include <pce/pce.hpp>
|
||||||
|
|
||||||
|
namespace PCEngine {
|
||||||
|
|
||||||
|
VCE vce;
|
||||||
|
#include "memory.cpp"
|
||||||
|
#include "io.cpp"
|
||||||
|
|
||||||
|
auto VCE::Enter() -> void {
|
||||||
|
while(true) scheduler.synchronize(), vce.main();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VCE::main() -> void {
|
||||||
|
vdc0.frame();
|
||||||
|
vdc1.frame();
|
||||||
|
timing.vclock = 0;
|
||||||
|
|
||||||
|
while(timing.vclock < 262) {
|
||||||
|
vdc0.scanline();
|
||||||
|
vdc1.scanline();
|
||||||
|
timing.hclock = 0;
|
||||||
|
|
||||||
|
auto output = buffer + 1365 * timing.vclock;
|
||||||
|
|
||||||
|
while(timing.hclock < 1360) {
|
||||||
|
uint9 color;
|
||||||
|
if(Model::PCEngine()) color = vdc0.bus();
|
||||||
|
if(Model::SuperGrafx()) color = vpc.bus(timing.hclock);
|
||||||
|
color = cram.read(color);
|
||||||
|
|
||||||
|
//*output++ = color;
|
||||||
|
//step(1);
|
||||||
|
|
||||||
|
if(clock() >= 2) *output++ = color;
|
||||||
|
if(clock() >= 2) *output++ = color;
|
||||||
|
if(clock() >= 3) *output++ = color;
|
||||||
|
if(clock() >= 4) *output++ = color;
|
||||||
|
step(clock());
|
||||||
|
}
|
||||||
|
|
||||||
|
step(1365 - timing.hclock);
|
||||||
|
timing.vclock++;
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduler.exit(Scheduler::Event::Frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VCE::step(uint clocks) -> void {
|
||||||
|
Thread::step(clocks);
|
||||||
|
synchronize(cpu);
|
||||||
|
|
||||||
|
timing.hclock += clocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VCE::refresh() -> void {
|
||||||
|
Emulator::video.refresh(buffer + 1365 * 13, 1365 * sizeof(uint32), 1140, 242);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VCE::power() -> void {
|
||||||
|
create(VCE::Enter, system.colorburst() * 6.0);
|
||||||
|
|
||||||
|
for(auto& pixel : buffer) pixel = 0;
|
||||||
|
memory::fill(&cram, sizeof(CRAM));
|
||||||
|
memory::fill(&timing, sizeof(Timing));
|
||||||
|
memory::fill(&io, sizeof(IO));
|
||||||
|
io.clock = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
//HuC6260 -- Video Color Encoder
|
||||||
|
|
||||||
|
struct VCE : Thread {
|
||||||
|
inline auto clock() const -> uint { return io.clock; }
|
||||||
|
|
||||||
|
static auto Enter() -> void;
|
||||||
|
auto main() -> void;
|
||||||
|
auto step(uint clocks) -> void;
|
||||||
|
auto refresh() -> void;
|
||||||
|
auto power() -> void;
|
||||||
|
|
||||||
|
//io.cpp
|
||||||
|
auto read(uint3 addr) -> uint8;
|
||||||
|
auto write(uint3 addr, uint8 data) -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32 buffer[1365 * 263];
|
||||||
|
|
||||||
|
struct CRAM {
|
||||||
|
//memory.cpp
|
||||||
|
auto read(uint9 addr) -> uint9;
|
||||||
|
auto write(uint9 addr, bool a0, uint8 data) -> void;
|
||||||
|
|
||||||
|
uint9 address;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint9 data[0x200];
|
||||||
|
} cram;
|
||||||
|
|
||||||
|
struct Timing {
|
||||||
|
uint hclock;
|
||||||
|
uint vclock;
|
||||||
|
} timing;
|
||||||
|
|
||||||
|
struct IO {
|
||||||
|
uint clock;
|
||||||
|
bool extraLine;
|
||||||
|
bool grayscale;
|
||||||
|
} io;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern VCE vce;
|
|
@ -9,28 +9,33 @@ auto VDC::Background::scanline(uint y) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VDC::Background::run(uint x, uint y) -> void {
|
auto VDC::Background::run(uint x, uint y) -> void {
|
||||||
color = nothing;
|
color = 0;
|
||||||
|
palette = 0;
|
||||||
|
|
||||||
uint16 batAddress;
|
uint16 batAddress;
|
||||||
batAddress = (voffset >> 3) & (height - 1);
|
batAddress = (voffset >> 3) & (height - 1);
|
||||||
batAddress *= width;
|
batAddress *= width;
|
||||||
batAddress += (hoffset >> 3) & (width - 1);
|
batAddress += (hoffset >> 3) & (width - 1);
|
||||||
|
|
||||||
uint16 tiledata = vdc.vram.read(batAddress);
|
uint16 tiledata = vdc->vram.read(batAddress);
|
||||||
uint16 patternAddress = tiledata.bits(0,11) << 4;
|
uint16 patternAddress = tiledata.bits(0,11) << 4;
|
||||||
patternAddress += (voffset & 7);
|
patternAddress += (voffset & 7);
|
||||||
uint4 palette = tiledata.bits(12,15);
|
uint4 palette = tiledata.bits(12,15);
|
||||||
|
|
||||||
uint16 d0 = vdc.vram.read(patternAddress + 0);
|
uint16 d0 = vdc->vram.read(patternAddress + 0);
|
||||||
uint16 d1 = vdc.vram.read(patternAddress + 8);
|
uint16 d1 = vdc->vram.read(patternAddress + 8);
|
||||||
|
|
||||||
uint3 index = 7 - (hoffset & 7);
|
uint3 index = 7 - (hoffset & 7);
|
||||||
uint4 output;
|
uint4 color;
|
||||||
output.bit(0) = d0.bit(0 + index);
|
color.bit(0) = d0.bit(0 + index);
|
||||||
output.bit(1) = d0.bit(8 + index);
|
color.bit(1) = d0.bit(8 + index);
|
||||||
output.bit(2) = d1.bit(0 + index);
|
color.bit(2) = d1.bit(0 + index);
|
||||||
output.bit(3) = d1.bit(8 + index);
|
color.bit(3) = d1.bit(8 + index);
|
||||||
|
|
||||||
|
if(enable && color) {
|
||||||
|
this->color = color;
|
||||||
|
this->palette = palette;
|
||||||
|
}
|
||||||
|
|
||||||
if(enable && output) color = vdc.cram.read(palette << 4 | output);
|
|
||||||
hoffset++;
|
hoffset++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
auto VDC::DMA::step(uint clocks) -> void {
|
auto VDC::DMA::step(uint clocks) -> void {
|
||||||
while(clocks--) {
|
while(clocks--) {
|
||||||
if(vramActive) {
|
if(vramActive) {
|
||||||
uint16 data = vdc.vram.read(source);
|
uint16 data = vdc->vram.read(source);
|
||||||
vdc.vram.write(target, data);
|
vdc->vram.write(target, data);
|
||||||
sourceIncrementMode == 0 ? source++ : source--;
|
sourceIncrementMode == 0 ? source++ : source--;
|
||||||
targetIncrementMode == 0 ? target++ : target--;
|
targetIncrementMode == 0 ? target++ : target--;
|
||||||
if(!--length) {
|
if(!--length) {
|
||||||
vramActive = false;
|
vramActive = false;
|
||||||
vdc.irq.raise(VDC::IRQ::Line::TransferVRAM);
|
vdc->irq.raise(VDC::IRQ::Line::TransferVRAM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(satbActive) {
|
if(satbActive) {
|
||||||
uint16 data = vdc.vram.read(satbSource + satbOffset);
|
uint16 data = vdc->vram.read(satbSource + satbOffset);
|
||||||
vdc.satb.write(satbOffset, data);
|
vdc->satb.write(satbOffset, data);
|
||||||
if(++satbOffset == 256) {
|
if(++satbOffset == 256) {
|
||||||
satbActive = false;
|
satbActive = false;
|
||||||
satbOffset = 0;
|
satbOffset = 0;
|
||||||
satbPending = satbRepeat;
|
satbPending = satbRepeat;
|
||||||
vdc.irq.raise(VDC::IRQ::Line::TransferSATB);
|
vdc->irq.raise(VDC::IRQ::Line::TransferSATB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,256 +1,201 @@
|
||||||
auto VDC::read(uint11 addr) -> uint8 {
|
auto VDC::read(uint2 addr) -> uint8 {
|
||||||
bool a0 = addr.bit(0);
|
bool a0 = addr.bit(0);
|
||||||
if(!addr.bit(10)) {
|
bool a1 = addr.bit(1);
|
||||||
if(addr.bit(1) == 0) {
|
|
||||||
//SR
|
|
||||||
if(a0) return 0x00;
|
|
||||||
uint8 data;
|
|
||||||
data.bit(0) = irq.pendingCollision;
|
|
||||||
data.bit(1) = irq.pendingOverflow;
|
|
||||||
data.bit(2) = irq.pendingLineCoincidence;
|
|
||||||
data.bit(3) = irq.pendingTransferSATB;
|
|
||||||
data.bit(4) = irq.pendingTransferVRAM;
|
|
||||||
data.bit(5) = irq.pendingVblank;
|
|
||||||
irq.lower();
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr.bit(1) == 1) {
|
if(a1 == 0) {
|
||||||
if(io.address == 0x02) {
|
//SR
|
||||||
//VRR
|
if(a0) return 0x00;
|
||||||
uint8 data = vram.dataRead.byte(a0);
|
uint8 data;
|
||||||
if(a0) {
|
data.bit(0) = irq.pendingCollision;
|
||||||
vram.addressRead += vram.addressIncrement;
|
data.bit(1) = irq.pendingOverflow;
|
||||||
vram.dataRead = vram.read(vram.addressRead);
|
data.bit(2) = irq.pendingLineCoincidence;
|
||||||
}
|
data.bit(3) = irq.pendingTransferSATB;
|
||||||
return data;
|
data.bit(4) = irq.pendingTransferVRAM;
|
||||||
}
|
data.bit(5) = irq.pendingVblank;
|
||||||
}
|
irq.lower();
|
||||||
|
return data;
|
||||||
} else {
|
} else {
|
||||||
if(addr.bits(0,2) == 0x04) {
|
if(io.address == 0x02) {
|
||||||
//CTR
|
//VRR
|
||||||
uint8 data = cram.read(cram.address).bits(0,7);
|
uint8 data = vram.dataRead.byte(a0);
|
||||||
return data;
|
if(a0) {
|
||||||
}
|
vram.addressRead += vram.addressIncrement;
|
||||||
|
|
||||||
if(addr.bits(0,2) == 0x05) {
|
|
||||||
//CTR
|
|
||||||
uint8 data = 0xfe | cram.read(cram.address).bit(0);
|
|
||||||
cram.address++;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0x00;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto VDC::write(uint11 addr, uint8 data) -> void {
|
|
||||||
bool a0 = addr.bit(0);
|
|
||||||
if(!addr.bit(10)) {
|
|
||||||
if(addr.bit(1) == 0) {
|
|
||||||
//AR
|
|
||||||
if(a0) return;
|
|
||||||
io.address = data.bits(0,4);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr.bit(1) == 1) {
|
|
||||||
if(io.address == 0x00) {
|
|
||||||
//MAWR
|
|
||||||
vram.addressWrite.byte(a0) = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x01) {
|
|
||||||
//MARR
|
|
||||||
vram.addressRead.byte(a0) = data;
|
|
||||||
vram.dataRead = vram.read(vram.addressRead);
|
vram.dataRead = vram.read(vram.addressRead);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x02) {
|
|
||||||
//VWR
|
|
||||||
vram.dataWrite.byte(a0) = data;
|
|
||||||
if(a0) {
|
|
||||||
vram.write(vram.addressWrite, vram.dataWrite);
|
|
||||||
vram.addressWrite += vram.addressIncrement;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x05) {
|
|
||||||
//CR
|
|
||||||
if(!a0) {
|
|
||||||
irq.enableCollision = data.bit(0);
|
|
||||||
irq.enableOverflow = data.bit(1);
|
|
||||||
irq.enableLineCoincidence = data.bit(2);
|
|
||||||
irq.enableVblank = data.bit(3);
|
|
||||||
io.externalSync = data.bits(4,5);
|
|
||||||
sprite.enable = data.bit(6);
|
|
||||||
background.enable = data.bit(7);
|
|
||||||
} else {
|
|
||||||
io.displayOutput = data.bits(0,1);
|
|
||||||
io.dramRefresh = data.bit(2);
|
|
||||||
if(data.bits(3,4) == 0) vram.addressIncrement = 0x01;
|
|
||||||
if(data.bits(3,4) == 1) vram.addressIncrement = 0x20;
|
|
||||||
if(data.bits(3,4) == 2) vram.addressIncrement = 0x40;
|
|
||||||
if(data.bits(3,4) == 3) vram.addressIncrement = 0x80;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x06) {
|
|
||||||
//RCR
|
|
||||||
io.lineCoincidence.byte(a0) = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x07) {
|
|
||||||
//BXR
|
|
||||||
background.hscroll.byte(a0) = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x08) {
|
|
||||||
//BYR
|
|
||||||
background.vscroll.byte(a0) = data;
|
|
||||||
background.vcounter = background.vscroll;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x09) {
|
|
||||||
//MWR
|
|
||||||
if(a0) return;
|
|
||||||
io.vramAccess = data.bits(0,1);
|
|
||||||
io.spriteAccess = data.bits(2,3);
|
|
||||||
if(data.bits(4,5) == 0) background.width = 32;
|
|
||||||
if(data.bits(4,5) == 1) background.width = 64;
|
|
||||||
if(data.bits(4,5) == 2) background.width = 128;
|
|
||||||
if(data.bits(4,5) == 3) background.width = 128;
|
|
||||||
if(data.bit(6) == 0) background.height = 32;
|
|
||||||
if(data.bit(6) == 1) background.height = 64;
|
|
||||||
io.cgMode = data.bit(7);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x0a) {
|
|
||||||
//HSR
|
|
||||||
if(!a0) {
|
|
||||||
vce.horizontalSyncWidth = data.bits(0,4);
|
|
||||||
} else {
|
|
||||||
vce.horizontalDisplayStart = data.bits(0,6);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x0b) {
|
|
||||||
//HDR
|
|
||||||
if(!a0) {
|
|
||||||
vce.horizontalDisplayLength = data.bits(0,6);
|
|
||||||
} else {
|
|
||||||
vce.horizontalDisplayEnd = data.bits(0,6);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x0c) {
|
|
||||||
//VPR
|
|
||||||
if(!a0) {
|
|
||||||
vce.verticalSyncWidth = data.bits(0,4);
|
|
||||||
} else {
|
|
||||||
vce.verticalDisplayStart = data.bits(0,7);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x0d) {
|
|
||||||
//VDR
|
|
||||||
if(!a0) {
|
|
||||||
vce.verticalDisplayLength.bits(0,7) = data.bits(0,7);
|
|
||||||
} else {
|
|
||||||
vce.verticalDisplayLength.bit(8) = data.bit(0);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x0e) {
|
|
||||||
//VCR
|
|
||||||
if(a0) return;
|
|
||||||
vce.verticalDisplayEnd = data.bits(0,7);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x0f) {
|
|
||||||
//DCR
|
|
||||||
if(a0) return;
|
|
||||||
irq.enableTransferVRAM = data.bit(0);
|
|
||||||
irq.enableTransferSATB = data.bit(1);
|
|
||||||
dma.sourceIncrementMode = data.bit(2);
|
|
||||||
dma.targetIncrementMode = data.bit(3);
|
|
||||||
dma.satbRepeat = data.bit(4);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x10) {
|
|
||||||
//SOUR
|
|
||||||
dma.source.byte(a0) = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x11) {
|
|
||||||
//DESR
|
|
||||||
dma.target.byte(a0) = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x12) {
|
|
||||||
//LENR
|
|
||||||
dma.length.byte(a0) = data;
|
|
||||||
if(a0) dma.vramStart();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.address == 0x13) {
|
|
||||||
//DVSSR
|
|
||||||
dma.satbSource.byte(a0) = data;
|
|
||||||
if(a0) dma.satbQueue();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VDC::write(uint2 addr, uint8 data) -> void {
|
||||||
|
bool a0 = addr.bit(0);
|
||||||
|
bool a1 = addr.bit(1);
|
||||||
|
|
||||||
|
if(a1 == 0) {
|
||||||
|
//AR
|
||||||
|
if(a0) return;
|
||||||
|
io.address = data.bits(0,4);
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
if(addr.bits(0,2) == 0x00) {
|
if(io.address == 0x00) {
|
||||||
|
//MAWR
|
||||||
|
vram.addressWrite.byte(a0) = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.address == 0x01) {
|
||||||
|
//MARR
|
||||||
|
vram.addressRead.byte(a0) = data;
|
||||||
|
vram.dataRead = vram.read(vram.addressRead);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.address == 0x02) {
|
||||||
|
//VWR
|
||||||
|
vram.dataWrite.byte(a0) = data;
|
||||||
|
if(a0) {
|
||||||
|
vram.write(vram.addressWrite, vram.dataWrite);
|
||||||
|
vram.addressWrite += vram.addressIncrement;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.address == 0x05) {
|
||||||
//CR
|
//CR
|
||||||
if(data.bits(0,1) == 0) vce.clock = 4;
|
if(!a0) {
|
||||||
if(data.bits(0,1) == 1) vce.clock = 3;
|
irq.enableCollision = data.bit(0);
|
||||||
if(data.bits(0,1) == 2) vce.clock = 2;
|
irq.enableOverflow = data.bit(1);
|
||||||
if(data.bits(0,1) == 3) vce.clock = 2;
|
irq.enableLineCoincidence = data.bit(2);
|
||||||
io.colorBlur = data.bit(2);
|
irq.enableVblank = data.bit(3);
|
||||||
io.grayscale = data.bit(7);
|
io.externalSync = data.bits(4,5);
|
||||||
|
sprite.enable = data.bit(6);
|
||||||
|
background.enable = data.bit(7);
|
||||||
|
} else {
|
||||||
|
io.displayOutput = data.bits(0,1);
|
||||||
|
io.dramRefresh = data.bit(2);
|
||||||
|
if(data.bits(3,4) == 0) vram.addressIncrement = 0x01;
|
||||||
|
if(data.bits(3,4) == 1) vram.addressIncrement = 0x20;
|
||||||
|
if(data.bits(3,4) == 2) vram.addressIncrement = 0x40;
|
||||||
|
if(data.bits(3,4) == 3) vram.addressIncrement = 0x80;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(addr.bits(0,2) == 0x02) {
|
if(io.address == 0x06) {
|
||||||
//CTA
|
//RCR
|
||||||
cram.address.bits(0,7) = data.bits(0,7);
|
io.lineCoincidence.byte(a0) = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(addr.bits(0,2) == 0x03) {
|
if(io.address == 0x07) {
|
||||||
//CTA
|
//BXR
|
||||||
cram.address.bit(8) = data.bit(0);
|
background.hscroll.byte(a0) = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(addr.bits(0,2) == 0x04) {
|
if(io.address == 0x08) {
|
||||||
//CTW
|
//BYR
|
||||||
cram.write(cram.address, 0, data.bits(0,7));
|
background.vscroll.byte(a0) = data;
|
||||||
|
background.vcounter = background.vscroll;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(addr.bits(0,2) == 0x05) {
|
if(io.address == 0x09) {
|
||||||
//CTW
|
//MWR
|
||||||
cram.write(cram.address, 1, data.bit(0));
|
if(a0) return;
|
||||||
cram.address++;
|
io.vramAccess = data.bits(0,1);
|
||||||
|
io.spriteAccess = data.bits(2,3);
|
||||||
|
if(data.bits(4,5) == 0) background.width = 32;
|
||||||
|
if(data.bits(4,5) == 1) background.width = 64;
|
||||||
|
if(data.bits(4,5) == 2) background.width = 128;
|
||||||
|
if(data.bits(4,5) == 3) background.width = 128;
|
||||||
|
if(data.bit(6) == 0) background.height = 32;
|
||||||
|
if(data.bit(6) == 1) background.height = 64;
|
||||||
|
io.cgMode = data.bit(7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.address == 0x0a) {
|
||||||
|
//HSR
|
||||||
|
if(!a0) {
|
||||||
|
timing.horizontalSyncWidth = data.bits(0,4);
|
||||||
|
} else {
|
||||||
|
timing.horizontalDisplayStart = data.bits(0,6);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.address == 0x0b) {
|
||||||
|
//HDR
|
||||||
|
if(!a0) {
|
||||||
|
timing.horizontalDisplayLength = data.bits(0,6);
|
||||||
|
} else {
|
||||||
|
timing.horizontalDisplayEnd = data.bits(0,6);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.address == 0x0c) {
|
||||||
|
//VPR
|
||||||
|
if(!a0) {
|
||||||
|
timing.verticalSyncWidth = data.bits(0,4);
|
||||||
|
} else {
|
||||||
|
timing.verticalDisplayStart = data.bits(0,7);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.address == 0x0d) {
|
||||||
|
//VDR
|
||||||
|
if(!a0) {
|
||||||
|
timing.verticalDisplayLength.bits(0,7) = data.bits(0,7);
|
||||||
|
} else {
|
||||||
|
timing.verticalDisplayLength.bit(8) = data.bit(0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.address == 0x0e) {
|
||||||
|
//VCR
|
||||||
|
if(a0) return;
|
||||||
|
timing.verticalDisplayEnd = data.bits(0,7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.address == 0x0f) {
|
||||||
|
//DCR
|
||||||
|
if(a0) return;
|
||||||
|
irq.enableTransferVRAM = data.bit(0);
|
||||||
|
irq.enableTransferSATB = data.bit(1);
|
||||||
|
dma.sourceIncrementMode = data.bit(2);
|
||||||
|
dma.targetIncrementMode = data.bit(3);
|
||||||
|
dma.satbRepeat = data.bit(4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.address == 0x10) {
|
||||||
|
//SOUR
|
||||||
|
dma.source.byte(a0) = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.address == 0x11) {
|
||||||
|
//DESR
|
||||||
|
dma.target.byte(a0) = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.address == 0x12) {
|
||||||
|
//LENR
|
||||||
|
dma.length.byte(a0) = data;
|
||||||
|
if(a0) dma.vramStart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io.address == 0x13) {
|
||||||
|
//DVSSR
|
||||||
|
dma.satbSource.byte(a0) = data;
|
||||||
|
if(a0) dma.satbQueue();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,3 @@ auto VDC::SATB::read(uint8 addr) -> uint16 {
|
||||||
auto VDC::SATB::write(uint8 addr, uint16 value) -> void {
|
auto VDC::SATB::write(uint8 addr, uint16 value) -> void {
|
||||||
data[addr] = value;
|
data[addr] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VDC::CRAM::read(uint9 addr) -> uint9 {
|
|
||||||
return data[addr];
|
|
||||||
}
|
|
||||||
|
|
||||||
auto VDC::CRAM::write(uint9 addr, bool a0, uint8 value) -> void {
|
|
||||||
if(!a0) {
|
|
||||||
data[addr].bits(0,7) = value.bits(0,7);
|
|
||||||
} else {
|
|
||||||
data[addr].bit(8) = value.bit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,10 +7,10 @@ auto VDC::Sprite::scanline(uint y) -> void {
|
||||||
|
|
||||||
uint count = 0;
|
uint count = 0;
|
||||||
for(uint index : range(64)) {
|
for(uint index : range(64)) {
|
||||||
uint16 d0 = vdc.satb.read(index << 2 | 0);
|
uint16 d0 = vdc->satb.read(index << 2 | 0);
|
||||||
uint16 d1 = vdc.satb.read(index << 2 | 1);
|
uint16 d1 = vdc->satb.read(index << 2 | 1);
|
||||||
uint16 d2 = vdc.satb.read(index << 2 | 2);
|
uint16 d2 = vdc->satb.read(index << 2 | 2);
|
||||||
uint16 d3 = vdc.satb.read(index << 2 | 3);
|
uint16 d3 = vdc->satb.read(index << 2 | 3);
|
||||||
|
|
||||||
Object object;
|
Object object;
|
||||||
object.y = d0.bits(0,9);
|
object.y = d0.bits(0,9);
|
||||||
|
@ -34,18 +34,18 @@ auto VDC::Sprite::scanline(uint y) -> void {
|
||||||
|
|
||||||
if(object.width == 15) {
|
if(object.width == 15) {
|
||||||
objects.append(object);
|
objects.append(object);
|
||||||
if(++count >= 16) return vdc.irq.raise(VDC::IRQ::Line::Overflow);
|
if(++count >= 16) return vdc->irq.raise(VDC::IRQ::Line::Overflow);
|
||||||
} else {
|
} else {
|
||||||
//32-width sprites count as two 16-width sprite slots
|
//32-width sprites count as two 16-width sprite slots
|
||||||
object.pattern ^= object.hflip;
|
object.pattern ^= object.hflip;
|
||||||
object.width = 15;
|
object.width = 15;
|
||||||
objects.append(object);
|
objects.append(object);
|
||||||
if(++count >= 16) return vdc.irq.raise(VDC::IRQ::Line::Overflow);
|
if(++count >= 16) return vdc->irq.raise(VDC::IRQ::Line::Overflow);
|
||||||
|
|
||||||
object.x += 16;
|
object.x += 16;
|
||||||
object.pattern ^= 1;
|
object.pattern ^= 1;
|
||||||
objects.append(object);
|
objects.append(object);
|
||||||
if(++count >= 16) return vdc.irq.raise(VDC::IRQ::Line::Overflow);
|
if(++count >= 16) return vdc->irq.raise(VDC::IRQ::Line::Overflow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,9 @@ auto VDC::Sprite::run(uint x, uint y) -> void {
|
||||||
x += 32;
|
x += 32;
|
||||||
y += 64;
|
y += 64;
|
||||||
|
|
||||||
color = nothing;
|
color = 0;
|
||||||
|
palette = 0;
|
||||||
|
priority = 0;
|
||||||
if(!enable) return;
|
if(!enable) return;
|
||||||
|
|
||||||
bool first = false;
|
bool first = false;
|
||||||
|
@ -73,26 +75,28 @@ auto VDC::Sprite::run(uint x, uint y) -> void {
|
||||||
patternAddress <<= 6;
|
patternAddress <<= 6;
|
||||||
patternAddress += (voffset & 15);
|
patternAddress += (voffset & 15);
|
||||||
|
|
||||||
uint16 d0 = vdc.vram.read(patternAddress + 0);
|
uint16 d0 = vdc->vram.read(patternAddress + 0);
|
||||||
uint16 d1 = vdc.vram.read(patternAddress + 16);
|
uint16 d1 = vdc->vram.read(patternAddress + 16);
|
||||||
uint16 d2 = vdc.vram.read(patternAddress + 32);
|
uint16 d2 = vdc->vram.read(patternAddress + 32);
|
||||||
uint16 d3 = vdc.vram.read(patternAddress + 48);
|
uint16 d3 = vdc->vram.read(patternAddress + 48);
|
||||||
|
|
||||||
uint4 index = 15 - (hoffset & 15);
|
uint4 index = 15 - (hoffset & 15);
|
||||||
uint4 output;
|
uint4 color;
|
||||||
output.bit(0) = d0.bit(index);
|
color.bit(0) = d0.bit(index);
|
||||||
output.bit(1) = d1.bit(index);
|
color.bit(1) = d1.bit(index);
|
||||||
output.bit(2) = d2.bit(index);
|
color.bit(2) = d2.bit(index);
|
||||||
output.bit(3) = d3.bit(index);
|
color.bit(3) = d3.bit(index);
|
||||||
if(output == 0) continue;
|
if(color == 0) continue;
|
||||||
|
|
||||||
if(color) {
|
if(this->color) {
|
||||||
if(first) return vdc.irq.raise(VDC::IRQ::Line::Collision);
|
if(first) return vdc->irq.raise(VDC::IRQ::Line::Collision);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
color = vdc.cram.read(1 << 8 | object.palette << 4 | output);
|
this->color = color;
|
||||||
priority = object.priority;
|
this->palette = object.palette;
|
||||||
|
this->priority = object.priority;
|
||||||
|
|
||||||
if(object.first) first = true;
|
if(object.first) first = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
namespace PCEngine {
|
namespace PCEngine {
|
||||||
|
|
||||||
VDC vdc;
|
VDC vdc0;
|
||||||
|
VDC vdc1;
|
||||||
#include "memory.cpp"
|
#include "memory.cpp"
|
||||||
#include "io.cpp"
|
#include "io.cpp"
|
||||||
#include "irq.cpp"
|
#include "irq.cpp"
|
||||||
|
@ -11,112 +12,105 @@ VDC vdc;
|
||||||
#include "sprite.cpp"
|
#include "sprite.cpp"
|
||||||
|
|
||||||
auto VDC::Enter() -> void {
|
auto VDC::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), vdc.main();
|
while(true) {
|
||||||
|
scheduler.synchronize();
|
||||||
|
if(vdc0.active()) vdc0.main();
|
||||||
|
if(vdc1.active()) vdc1.main();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VDC::main() -> void {
|
auto VDC::main() -> void {
|
||||||
auto output = buffer + 1140 * vce.vclock;
|
if(Model::PCEngine() && vdc1.active()) return step(frequency());
|
||||||
|
|
||||||
//todo: if block breaks TV Sports Basketball
|
timing.vpulse = false;
|
||||||
//if(vce.vclock >= vce.vstart && vce.voffset < vce.vlength) {
|
timing.vclock = 0;
|
||||||
background.scanline(vce.voffset);
|
timing.voffset = 0;
|
||||||
sprite.scanline(vce.voffset);
|
timing.vstart = max((uint8)2, timing.verticalDisplayStart) - 2;
|
||||||
//}
|
timing.vlength = min(242, timing.verticalDisplayLength + 1);
|
||||||
|
|
||||||
while(vce.hclock < 1140) {
|
while(!timing.vpulse) {
|
||||||
uint9 color = 0;
|
timing.hpulse = false;
|
||||||
|
timing.hclock = 0;
|
||||||
|
timing.hoffset = 0;
|
||||||
|
timing.hstart = timing.horizontalDisplayStart;
|
||||||
|
timing.hlength = (timing.horizontalDisplayLength + 1) << 3;
|
||||||
|
|
||||||
if(vce.vclock >= vce.vstart && vce.voffset < vce.vlength
|
if(timing.vclock >= timing.vstart && timing.voffset < timing.vlength) {
|
||||||
&& vce.hclock >= vce.hstart && vce.hoffset < vce.hlength
|
background.scanline(timing.voffset);
|
||||||
) {
|
sprite.scanline(timing.voffset);
|
||||||
background.run(vce.hoffset, vce.voffset);
|
|
||||||
sprite.run(vce.hoffset, vce.voffset);
|
|
||||||
vce.hoffset++;
|
|
||||||
|
|
||||||
if(sprite.color && sprite.priority) {
|
step(timing.hstart);
|
||||||
color = sprite.color();
|
|
||||||
} else if(background.color) {
|
while(timing.hoffset < timing.hlength) {
|
||||||
color = background.color();
|
data = 0;
|
||||||
} else if(sprite.color) {
|
|
||||||
color = sprite.color();
|
background.run(timing.hoffset, timing.voffset);
|
||||||
} else {
|
sprite.run(timing.hoffset, timing.voffset);
|
||||||
color = cram.read(0);
|
|
||||||
|
if(sprite.color && sprite.priority) {
|
||||||
|
data = 1 << 8 | sprite.palette << 4 | sprite.color << 0;
|
||||||
|
} else if(background.color) {
|
||||||
|
data = 0 << 8 | background.palette << 4 | background.color << 0;
|
||||||
|
} else if(sprite.color) {
|
||||||
|
data = 1 << 8 | sprite.palette << 4 | sprite.color << 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
step(vce.clock());
|
||||||
|
timing.hoffset++;
|
||||||
}
|
}
|
||||||
|
data = 0;
|
||||||
|
|
||||||
|
if(timing.voffset == io.lineCoincidence - 64) {
|
||||||
|
irq.raise(IRQ::Line::LineCoincidence);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(!timing.hpulse) step(1);
|
||||||
|
timing.vclock++;
|
||||||
|
timing.voffset++;
|
||||||
|
} else {
|
||||||
|
data = 0;
|
||||||
|
while(!timing.hpulse) step(1);
|
||||||
|
timing.vclock++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(vce.clock >= 2) *output++ = color;
|
if(timing.vclock == timing.vstart + timing.vlength) {
|
||||||
if(vce.clock >= 2) *output++ = color;
|
irq.raise(IRQ::Line::Vblank);
|
||||||
if(vce.clock >= 3) *output++ = color;
|
dma.satbStart();
|
||||||
if(vce.clock >= 4) *output++ = color;
|
}
|
||||||
|
|
||||||
step(vce.clock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(vce.vclock == io.lineCoincidence - (65 - vce.vstart)) {
|
|
||||||
irq.raise(IRQ::Line::LineCoincidence);
|
|
||||||
}
|
|
||||||
|
|
||||||
step(1365 - vce.hclock);
|
|
||||||
scanline();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VDC::scanline() -> void {
|
auto VDC::scanline() -> void {
|
||||||
vce.hclock = 0;
|
timing.hpulse = true;
|
||||||
vce.hoffset = 0;
|
|
||||||
vce.hstart = vce.horizontalDisplayStart * vce.clock;
|
|
||||||
vce.hlength = (vce.horizontalDisplayLength + 1) << 3;
|
|
||||||
if(vce.clock == 2) vce.hstart += 0;
|
|
||||||
if(vce.clock == 3) vce.hstart += 0;
|
|
||||||
if(vce.clock == 4) vce.hstart += 0;
|
|
||||||
|
|
||||||
vce.vclock++;
|
|
||||||
if(vce.vclock > vce.vstart) vce.voffset++;
|
|
||||||
|
|
||||||
if(vce.vclock == 262) {
|
|
||||||
frame();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(vce.vclock == vce.vstart + vce.vlength) {
|
|
||||||
irq.raise(IRQ::Line::Vblank);
|
|
||||||
dma.satbStart();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VDC::frame() -> void {
|
auto VDC::frame() -> void {
|
||||||
scheduler.exit(Scheduler::Event::Frame);
|
timing.vpulse = true;
|
||||||
|
|
||||||
vce.vclock = 0;
|
|
||||||
vce.voffset = 0;
|
|
||||||
vce.vstart = max((uint8)2, vce.verticalDisplayStart) - 2;
|
|
||||||
vce.vlength = min(242, vce.verticalDisplayLength + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VDC::step(uint clocks) -> void {
|
auto VDC::step(uint clocks) -> void {
|
||||||
vce.hclock += clocks;
|
|
||||||
Thread::step(clocks);
|
Thread::step(clocks);
|
||||||
dma.step(clocks);
|
|
||||||
synchronize(cpu);
|
synchronize(cpu);
|
||||||
}
|
|
||||||
|
|
||||||
auto VDC::refresh() -> void {
|
timing.hclock += clocks;
|
||||||
Emulator::video.refresh(buffer + 1140 * vce.vstart, 1140 * sizeof(uint32), 1140, 242);
|
dma.step(clocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VDC::power() -> void {
|
auto VDC::power() -> void {
|
||||||
create(VDC::Enter, system.colorburst() * 6.0);
|
create(VDC::Enter, system.colorburst() * 6.0);
|
||||||
|
|
||||||
for(auto& pixel : buffer) pixel = 0;
|
|
||||||
memory::fill(&vram, sizeof(VRAM));
|
memory::fill(&vram, sizeof(VRAM));
|
||||||
memory::fill(&satb, sizeof(SATB));
|
memory::fill(&satb, sizeof(SATB));
|
||||||
memory::fill(&cram, sizeof(CRAM));
|
memory::fill(&timing, sizeof(Timing));
|
||||||
memory::fill(&irq, sizeof(IRQ));
|
memory::fill(&irq, sizeof(IRQ));
|
||||||
memory::fill(&dma, sizeof(DMA));
|
memory::fill(&dma, sizeof(DMA));
|
||||||
memory::fill(&vce, sizeof(VCE));
|
|
||||||
memory::fill(&io, sizeof(IO));
|
memory::fill(&io, sizeof(IO));
|
||||||
memory::fill(&background, sizeof(Background));
|
memory::fill(&background, sizeof(Background));
|
||||||
memory::fill(&sprite, sizeof(Sprite));
|
memory::fill(&sprite, sizeof(Sprite));
|
||||||
|
|
||||||
vce.clock = 4;
|
dma.vdc = this;
|
||||||
|
background.vdc = this;
|
||||||
|
sprite.vdc = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
//Hudson Soft HuC6260 -- Video Color Encoder
|
|
||||||
//Hudson Soft HuC6270 -- Video Display Controller
|
//Hudson Soft HuC6270 -- Video Display Controller
|
||||||
|
|
||||||
struct VDC : Thread {
|
struct VDC : Thread {
|
||||||
|
inline auto bus() const -> uint9 { return data; }
|
||||||
|
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
auto step(uint clocks) -> void;
|
auto step(uint clocks) -> void;
|
||||||
auto scanline() -> void;
|
auto scanline() -> void;
|
||||||
auto frame() -> void;
|
auto frame() -> void;
|
||||||
auto refresh() -> void;
|
|
||||||
|
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
|
|
||||||
//io.cpp
|
//io.cpp
|
||||||
auto read(uint11 addr) -> uint8;
|
auto read(uint2 addr) -> uint8;
|
||||||
auto write(uint11 addr, uint8 data) -> void;
|
auto write(uint2 addr, uint8 data) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32 buffer[1140 * 512];
|
uint9 data;
|
||||||
|
|
||||||
struct VRAM {
|
struct VRAM {
|
||||||
//memory.cpp
|
//memory.cpp
|
||||||
|
@ -43,16 +43,32 @@ private:
|
||||||
uint16 data[0x100];
|
uint16 data[0x100];
|
||||||
} satb;
|
} satb;
|
||||||
|
|
||||||
struct CRAM {
|
struct Timing {
|
||||||
//memory.cpp
|
uint5 horizontalSyncWidth;
|
||||||
auto read(uint9 addr) -> uint9;
|
uint7 horizontalDisplayStart;
|
||||||
auto write(uint9 addr, bool a0, uint8 data) -> void;
|
uint7 horizontalDisplayLength;
|
||||||
|
uint7 horizontalDisplayEnd;
|
||||||
|
|
||||||
uint9 address;
|
uint5 verticalSyncWidth;
|
||||||
|
uint8 verticalDisplayStart;
|
||||||
|
uint9 verticalDisplayLength;
|
||||||
|
uint8 verticalDisplayEnd;
|
||||||
|
|
||||||
private:
|
bool vpulse;
|
||||||
uint9 data[0x200];
|
bool hpulse;
|
||||||
} cram;
|
|
||||||
|
uint hclock;
|
||||||
|
uint vclock;
|
||||||
|
|
||||||
|
uint hoffset;
|
||||||
|
uint voffset;
|
||||||
|
|
||||||
|
uint hstart;
|
||||||
|
uint vstart;
|
||||||
|
|
||||||
|
uint hlength;
|
||||||
|
uint vlength;
|
||||||
|
} timing;
|
||||||
|
|
||||||
struct IRQ {
|
struct IRQ {
|
||||||
enum class Line : uint {
|
enum class Line : uint {
|
||||||
|
@ -85,6 +101,8 @@ private:
|
||||||
} irq;
|
} irq;
|
||||||
|
|
||||||
struct DMA {
|
struct DMA {
|
||||||
|
VDC* vdc = nullptr;
|
||||||
|
|
||||||
//dma.cpp
|
//dma.cpp
|
||||||
auto step(uint clocks) -> void;
|
auto step(uint clocks) -> void;
|
||||||
auto vramStart() -> void;
|
auto vramStart() -> void;
|
||||||
|
@ -105,33 +123,9 @@ private:
|
||||||
uint16 satbOffset;
|
uint16 satbOffset;
|
||||||
} dma;
|
} dma;
|
||||||
|
|
||||||
struct VCE {
|
|
||||||
uint5 horizontalSyncWidth;
|
|
||||||
uint7 horizontalDisplayStart;
|
|
||||||
uint7 horizontalDisplayLength;
|
|
||||||
uint7 horizontalDisplayEnd;
|
|
||||||
|
|
||||||
uint5 verticalSyncWidth;
|
|
||||||
uint8 verticalDisplayStart;
|
|
||||||
uint9 verticalDisplayLength;
|
|
||||||
uint8 verticalDisplayEnd;
|
|
||||||
|
|
||||||
uint clock;
|
|
||||||
|
|
||||||
uint hclock;
|
|
||||||
uint vclock;
|
|
||||||
|
|
||||||
uint hoffset;
|
|
||||||
uint voffset;
|
|
||||||
|
|
||||||
uint hstart;
|
|
||||||
uint vstart;
|
|
||||||
|
|
||||||
uint hlength;
|
|
||||||
uint vlength;
|
|
||||||
} vce;
|
|
||||||
|
|
||||||
struct Background {
|
struct Background {
|
||||||
|
VDC* vdc = nullptr;
|
||||||
|
|
||||||
//background.cpp
|
//background.cpp
|
||||||
auto scanline(uint y) -> void;
|
auto scanline(uint y) -> void;
|
||||||
auto run(uint x, uint y) -> void;
|
auto run(uint x, uint y) -> void;
|
||||||
|
@ -146,10 +140,13 @@ private:
|
||||||
uint10 hoffset;
|
uint10 hoffset;
|
||||||
uint9 voffset;
|
uint9 voffset;
|
||||||
|
|
||||||
maybe<uint9> color;
|
uint4 color;
|
||||||
|
uint4 palette;
|
||||||
} background;
|
} background;
|
||||||
|
|
||||||
struct Sprite {
|
struct Sprite {
|
||||||
|
VDC* vdc = nullptr;
|
||||||
|
|
||||||
//sprite.cpp
|
//sprite.cpp
|
||||||
auto scanline(uint y) -> void;
|
auto scanline(uint y) -> void;
|
||||||
auto run(uint x, uint y) -> void;
|
auto run(uint x, uint y) -> void;
|
||||||
|
@ -171,7 +168,8 @@ private:
|
||||||
};
|
};
|
||||||
array<Object, 64> objects;
|
array<Object, 64> objects;
|
||||||
|
|
||||||
maybe<uint9> color;
|
uint4 color;
|
||||||
|
uint4 palette;
|
||||||
bool priority;
|
bool priority;
|
||||||
} sprite;
|
} sprite;
|
||||||
|
|
||||||
|
@ -197,4 +195,5 @@ private:
|
||||||
} io;
|
} io;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern VDC vdc;
|
extern VDC vdc0;
|
||||||
|
extern VDC vdc1;
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
#include <pce/pce.hpp>
|
||||||
|
|
||||||
|
namespace PCEngine {
|
||||||
|
|
||||||
|
VPC vpc;
|
||||||
|
|
||||||
|
auto VPC::bus(uint hclock) -> uint9 {
|
||||||
|
auto bus0 = vdc0.bus();
|
||||||
|
auto bus1 = vdc1.bus();
|
||||||
|
|
||||||
|
auto color0 = bus0.bits(0,3);
|
||||||
|
auto color1 = bus1.bits(0,3);
|
||||||
|
|
||||||
|
auto palette0 = bus0.bits(4,7);
|
||||||
|
auto palette1 = bus1.bits(4,7);
|
||||||
|
|
||||||
|
auto mode0 = bus0.bit(8);
|
||||||
|
auto mode1 = bus1.bit(8);
|
||||||
|
|
||||||
|
//todo: I am unsure how the window coordinates relate to raw screen pixels ...
|
||||||
|
bool window0 = window[0] >= 64 && (window[0] - 64) >= hclock;
|
||||||
|
bool window1 = window[1] >= 64 && (window[1] - 64) >= hclock;
|
||||||
|
|
||||||
|
uint2 mode;
|
||||||
|
if(!window0 && !window1) mode = 1;
|
||||||
|
if( window0 && !window1) mode = 0;
|
||||||
|
if(!window0 && window1) mode = 3;
|
||||||
|
if( window0 && window1) mode = 2;
|
||||||
|
|
||||||
|
auto enableVDC0 = settings[mode].enableVDC0;
|
||||||
|
auto enableVDC1 = settings[mode].enableVDC1;
|
||||||
|
auto priority = settings[mode].priority;
|
||||||
|
|
||||||
|
//todo: I am unsure how this should work ...
|
||||||
|
if(priority == 0 || priority == 3) {
|
||||||
|
if(color1) return bus1;
|
||||||
|
return bus0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(priority == 1) {
|
||||||
|
if(color1) return bus1;
|
||||||
|
return bus0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(priority == 2) {
|
||||||
|
if(color1) return bus1;
|
||||||
|
return bus0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VPC::power() -> void {
|
||||||
|
settings[0].enableVDC0 = true;
|
||||||
|
settings[0].enableVDC1 = false;
|
||||||
|
settings[0].priority = 0;
|
||||||
|
|
||||||
|
settings[1].enableVDC0 = true;
|
||||||
|
settings[1].enableVDC1 = false;
|
||||||
|
settings[1].priority = 0;
|
||||||
|
|
||||||
|
settings[2].enableVDC0 = true;
|
||||||
|
settings[2].enableVDC1 = false;
|
||||||
|
settings[2].priority = 0;
|
||||||
|
|
||||||
|
settings[3].enableVDC0 = true;
|
||||||
|
settings[3].enableVDC1 = false;
|
||||||
|
settings[3].priority = 0;
|
||||||
|
|
||||||
|
window[0] = 0;
|
||||||
|
window[1] = 1;
|
||||||
|
|
||||||
|
select = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VPC::read(uint5 addr) -> uint8 {
|
||||||
|
if(addr >= 0x00 && addr <= 0x07) return vdc0.read(addr);
|
||||||
|
if(addr >= 0x10 && addr <= 0x17) return vdc1.read(addr);
|
||||||
|
if(addr >= 0x18 && addr <= 0x1f) return 0xff;
|
||||||
|
|
||||||
|
if(addr == 0x08) {
|
||||||
|
return (
|
||||||
|
settings[0].enableVDC0 << 0
|
||||||
|
| settings[0].enableVDC1 << 1
|
||||||
|
| settings[0].priority << 2
|
||||||
|
| settings[1].enableVDC0 << 4
|
||||||
|
| settings[1].enableVDC1 << 5
|
||||||
|
| settings[1].priority << 6
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0x09) {
|
||||||
|
return (
|
||||||
|
settings[2].enableVDC0 << 0
|
||||||
|
| settings[2].enableVDC1 << 1
|
||||||
|
| settings[2].priority << 2
|
||||||
|
| settings[3].enableVDC0 << 4
|
||||||
|
| settings[3].enableVDC1 << 5
|
||||||
|
| settings[3].priority << 6
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0x0a) return window[0].bits(0,7);
|
||||||
|
if(addr == 0x0b) return window[0].bits(8,9);
|
||||||
|
if(addr == 0x0c) return window[1].bits(0,7);
|
||||||
|
if(addr == 0x0d) return window[1].bits(8,9);
|
||||||
|
if(addr == 0x0e) return 0x00; //select is not readable
|
||||||
|
if(addr == 0x0f) return 0x00; //unused
|
||||||
|
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VPC::write(uint5 addr, uint8 data) -> void {
|
||||||
|
if(addr >= 0x00 && addr <= 0x07) return vdc0.write(addr, data);
|
||||||
|
if(addr >= 0x10 && addr <= 0x17) return vdc1.write(addr, data);
|
||||||
|
if(addr >= 0x18 && addr <= 0x1f) return;
|
||||||
|
|
||||||
|
if(addr == 0x08) {
|
||||||
|
settings[0].enableVDC0 = data.bit(0);
|
||||||
|
settings[0].enableVDC1 = data.bit(1);
|
||||||
|
settings[0].priority = data.bits(2,3);
|
||||||
|
settings[1].enableVDC0 = data.bit(4);
|
||||||
|
settings[1].enableVDC1 = data.bit(5);
|
||||||
|
settings[1].priority = data.bits(6,7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0x09) {
|
||||||
|
settings[2].enableVDC0 = data.bit(0);
|
||||||
|
settings[2].enableVDC1 = data.bit(1);
|
||||||
|
settings[2].priority = data.bits(2,3);
|
||||||
|
settings[3].enableVDC0 = data.bit(4);
|
||||||
|
settings[3].enableVDC1 = data.bit(5);
|
||||||
|
settings[3].priority = data.bits(6,7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0x0a) {
|
||||||
|
window[0].bits(0,7) = data.bits(0,7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0x0b) {
|
||||||
|
window[0].bits(8,9) = data.bits(0,1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0x0c) {
|
||||||
|
window[1].bits(0,7) = data.bits(0,7);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0x0d) {
|
||||||
|
window[1].bits(8,9) = data.bits(0,1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0x0e) {
|
||||||
|
select = data.bit(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0x0f) {
|
||||||
|
//unused
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VPC::store(uint2 addr, uint8 data) -> void {
|
||||||
|
if(select == 0) return vdc0.write(addr, data);
|
||||||
|
if(select == 1) return vdc1.write(addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
//Hudson Soft HuC6202 -- Video Priority Controller
|
||||||
|
|
||||||
|
struct VPC {
|
||||||
|
auto bus(uint hclock) -> uint9;
|
||||||
|
|
||||||
|
auto power() -> void;
|
||||||
|
auto read(uint5 addr) -> uint8;
|
||||||
|
auto write(uint5 addr, uint8 data) -> void;
|
||||||
|
auto store(uint2 addr, uint8 data) -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Settings {
|
||||||
|
bool enableVDC0;
|
||||||
|
bool enableVDC1;
|
||||||
|
uint2 priority;
|
||||||
|
} settings[4];
|
||||||
|
|
||||||
|
uint10 window[2];
|
||||||
|
bool select;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern VPC vpc;
|
|
@ -1,14 +1,16 @@
|
||||||
auto HuC6280::disassemble(uint16 pc_) -> string {
|
auto HuC6280::disassemble(uint16 pc) -> string {
|
||||||
uint21 pc = mmu(pc_);
|
uint8 bank = r.mpr[pc.bits(13,15)];
|
||||||
string s{hex((uint)pc.bits(13,20), 2L), ":", hex((uint)pc.bits(0,12), 4L), " "};
|
uint13 addr = pc.bits(0,12);
|
||||||
|
|
||||||
|
string s{hex(bank, 2L), ":", hex(addr, 4L), " "};
|
||||||
|
|
||||||
auto readByte = [&]() -> uint8 {
|
auto readByte = [&]() -> uint8 {
|
||||||
return read(pc++);
|
return read(bank, addr++);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto readWord = [&]() -> uint16 {
|
auto readWord = [&]() -> uint16 {
|
||||||
uint16 data = read(pc++);
|
uint16 data = read(bank, addr++) << 0;
|
||||||
return data | read(pc++) << 8;
|
return data | read(bank, addr++) << 8;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto absolute = [&]() -> string { return {"$", hex(readWord(), 4L)}; };
|
auto absolute = [&]() -> string { return {"$", hex(readWord(), 4L)}; };
|
||||||
|
@ -338,7 +340,7 @@ U op(0xfc, "nop", "$fc")
|
||||||
s.append(" X:", hex(r.x, 2L));
|
s.append(" X:", hex(r.x, 2L));
|
||||||
s.append(" Y:", hex(r.y, 2L));
|
s.append(" Y:", hex(r.y, 2L));
|
||||||
s.append(" S:", hex(r.s, 2L));
|
s.append(" S:", hex(r.s, 2L));
|
||||||
s.append(" PC:", hex(pc_, 4L));
|
s.append(" PC:", hex(pc, 4L));
|
||||||
s.append(" ");
|
s.append(" ");
|
||||||
s.append(r.p.n ? "N" : "n");
|
s.append(r.p.n ? "N" : "n");
|
||||||
s.append(r.p.v ? "V" : "v");
|
s.append(r.p.v ? "V" : "v");
|
||||||
|
|
|
@ -6,19 +6,18 @@ namespace Processor {
|
||||||
|
|
||||||
struct HuC6280 {
|
struct HuC6280 {
|
||||||
virtual auto step(uint clocks) -> void = 0;
|
virtual auto step(uint clocks) -> void = 0;
|
||||||
virtual auto read(uint21 addr) -> uint8 = 0;
|
virtual auto read(uint8 bank, uint13 addr) -> uint8 = 0;
|
||||||
virtual auto write(uint21 addr, uint8 data) -> void = 0;
|
virtual auto write(uint8 bank, uint13 addr, uint8 data) -> void = 0;
|
||||||
|
virtual auto store(uint2 addr, uint8 data) -> void = 0;
|
||||||
virtual auto lastCycle() -> void = 0;
|
virtual auto lastCycle() -> void = 0;
|
||||||
|
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
|
|
||||||
//memory.cpp
|
//memory.cpp
|
||||||
inline auto mmu(uint16) const -> uint21;
|
|
||||||
inline auto load8(uint8) -> uint8;
|
inline auto load8(uint8) -> uint8;
|
||||||
inline auto load16(uint16) -> uint8;
|
inline auto load16(uint16) -> uint8;
|
||||||
inline auto store8(uint8, uint8) -> void;
|
inline auto store8(uint8, uint8) -> void;
|
||||||
inline auto store16(uint16, uint8) -> void;
|
inline auto store16(uint16, uint8) -> void;
|
||||||
inline auto store21(uint21, uint8) -> void;
|
|
||||||
|
|
||||||
auto io() -> uint8;
|
auto io() -> uint8;
|
||||||
auto opcode() -> uint8;
|
auto opcode() -> uint8;
|
||||||
|
|
|
@ -47,7 +47,7 @@ U op(0x0b, NOP)
|
||||||
op(0x10, branch, N == 0)
|
op(0x10, branch, N == 0)
|
||||||
op(0x11, indirectYLoad, fp(ORA), A)
|
op(0x11, indirectYLoad, fp(ORA), A)
|
||||||
op(0x12, indirectLoad, fp(ORA), A)
|
op(0x12, indirectLoad, fp(ORA), A)
|
||||||
op(0x13, ST, 2)
|
op(0x13, ST, 1)
|
||||||
op(0x14, zeropageModify, fp(TRB))
|
op(0x14, zeropageModify, fp(TRB))
|
||||||
op(0x15, zeropageLoad, fp(ORA), A, X)
|
op(0x15, zeropageLoad, fp(ORA), A, X)
|
||||||
op(0x16, zeropageModify, fp(ASL), X)
|
op(0x16, zeropageModify, fp(ASL), X)
|
||||||
|
@ -63,7 +63,7 @@ U op(0x1b, NOP)
|
||||||
op(0x20, JSR)
|
op(0x20, JSR)
|
||||||
op(0x21, indirectLoad, fp(AND), A, X)
|
op(0x21, indirectLoad, fp(AND), A, X)
|
||||||
op(0x22, swap, A, X)
|
op(0x22, swap, A, X)
|
||||||
op(0x23, ST, 3)
|
op(0x23, ST, 2)
|
||||||
op(0x24, zeropageLoad, fp(BIT), A)
|
op(0x24, zeropageLoad, fp(BIT), A)
|
||||||
op(0x25, zeropageLoad, fp(AND), A)
|
op(0x25, zeropageLoad, fp(AND), A)
|
||||||
op(0x26, zeropageModify, fp(ROL))
|
op(0x26, zeropageModify, fp(ROL))
|
||||||
|
|
|
@ -495,7 +495,8 @@ L store8(zeropage, data);
|
||||||
auto HuC6280::instruction_ST(uint2 index) -> void {
|
auto HuC6280::instruction_ST(uint2 index) -> void {
|
||||||
auto data = operand();
|
auto data = operand();
|
||||||
io();
|
io();
|
||||||
L store21(0x1fe000 + index, data);
|
L io();
|
||||||
|
store(index, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HuC6280::instruction_TAM() -> void {
|
auto HuC6280::instruction_TAM() -> void {
|
||||||
|
|
|
@ -1,61 +1,48 @@
|
||||||
auto HuC6280::mmu(uint16 addr) const -> uint21 {
|
|
||||||
return r.mpr[addr.bits(13,15)] << 13 | addr.bits(0,12);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
auto HuC6280::load8(uint8 addr) -> uint8 {
|
auto HuC6280::load8(uint8 addr) -> uint8 {
|
||||||
step(r.cs);
|
step(r.cs);
|
||||||
return read(mmu(0x2000 + addr));
|
return read(r.mpr[1], addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HuC6280::load16(uint16 addr) -> uint8 {
|
auto HuC6280::load16(uint16 addr) -> uint8 {
|
||||||
step(r.cs);
|
step(r.cs);
|
||||||
return read(mmu(addr));
|
return read(r.mpr[addr.bits(13,15)], addr.bits(0,12));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HuC6280::store8(uint8 addr, uint8 data) -> void {
|
auto HuC6280::store8(uint8 addr, uint8 data) -> void {
|
||||||
step(r.cs);
|
step(r.cs);
|
||||||
return write(mmu(0x2000 + addr), data);
|
return write(r.mpr[1], addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HuC6280::store16(uint16 addr, uint8 data) -> void {
|
auto HuC6280::store16(uint16 addr, uint8 data) -> void {
|
||||||
step(r.cs);
|
step(r.cs);
|
||||||
return write(mmu(addr), data);
|
return write(r.mpr[addr.bits(13,15)], addr.bits(0,12), data);
|
||||||
}
|
|
||||||
|
|
||||||
auto HuC6280::store21(uint21 addr, uint8 data) -> void {
|
|
||||||
step(r.cs);
|
|
||||||
return write(addr, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
auto HuC6280::io() -> uint8 {
|
auto HuC6280::io() -> uint8 {
|
||||||
step(r.cs);
|
step(r.cs);
|
||||||
return read(mmu(PC));
|
return 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HuC6280::opcode() -> uint8 {
|
auto HuC6280::opcode() -> uint8 {
|
||||||
step(r.cs);
|
return load16(PC++);
|
||||||
return read(mmu(PC++));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HuC6280::operand() -> uint8 {
|
auto HuC6280::operand() -> uint8 {
|
||||||
step(r.cs);
|
return load16(PC++);
|
||||||
return read(mmu(PC++));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
auto HuC6280::push(uint8 data) -> void {
|
auto HuC6280::push(uint8 data) -> void {
|
||||||
step(r.cs);
|
step(r.cs);
|
||||||
write(mmu(0x2100 + S), data);
|
write(r.mpr[1], 0x0100 | S, data);
|
||||||
S--;
|
S--;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HuC6280::pull() -> uint8 {
|
auto HuC6280::pull() -> uint8 {
|
||||||
step(r.cs);
|
step(r.cs);
|
||||||
S++;
|
S++;
|
||||||
return read(mmu(0x2100 + S));
|
return read(r.mpr[1], 0x0100 | S);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
system name:SuperGrafx
|
|
@ -22,7 +22,8 @@ Program::Program(string_vector args) {
|
||||||
emulators.append(new SuperFamicom::Interface);
|
emulators.append(new SuperFamicom::Interface);
|
||||||
emulators.append(new MasterSystem::MasterSystemInterface);
|
emulators.append(new MasterSystem::MasterSystemInterface);
|
||||||
emulators.append(new MegaDrive::Interface);
|
emulators.append(new MegaDrive::Interface);
|
||||||
emulators.append(new PCEngine::Interface);
|
emulators.append(new PCEngine::PCEngineInterface);
|
||||||
|
emulators.append(new PCEngine::SuperGrafxInterface);
|
||||||
emulators.append(new GameBoy::GameBoyInterface);
|
emulators.append(new GameBoy::GameBoyInterface);
|
||||||
emulators.append(new GameBoy::GameBoyColorInterface);
|
emulators.append(new GameBoy::GameBoyColorInterface);
|
||||||
emulators.append(new GameBoyAdvance::Interface);
|
emulators.append(new GameBoyAdvance::Interface);
|
||||||
|
|
|
@ -4,6 +4,7 @@ Icarus::Icarus() {
|
||||||
database.masterSystem = BML::unserialize(string::read(locate("Database/Master System.bml")));
|
database.masterSystem = BML::unserialize(string::read(locate("Database/Master System.bml")));
|
||||||
database.megaDrive = BML::unserialize(string::read(locate("Database/Mega Drive.bml")));
|
database.megaDrive = BML::unserialize(string::read(locate("Database/Mega Drive.bml")));
|
||||||
database.pcEngine = BML::unserialize(string::read(locate("Database/PC Engine.bml")));
|
database.pcEngine = BML::unserialize(string::read(locate("Database/PC Engine.bml")));
|
||||||
|
database.superGrafx = BML::unserialize(string::read(locate("Database/SuperGrafx.bml")));
|
||||||
database.gameBoy = BML::unserialize(string::read(locate("Database/Game Boy.bml")));
|
database.gameBoy = BML::unserialize(string::read(locate("Database/Game Boy.bml")));
|
||||||
database.gameBoyColor = BML::unserialize(string::read(locate("Database/Game Boy Color.bml")));
|
database.gameBoyColor = BML::unserialize(string::read(locate("Database/Game Boy Color.bml")));
|
||||||
database.gameBoyAdvance = BML::unserialize(string::read(locate("Database/Game Boy Advance.bml")));
|
database.gameBoyAdvance = BML::unserialize(string::read(locate("Database/Game Boy Advance.bml")));
|
||||||
|
@ -38,6 +39,7 @@ auto Icarus::manifest(string location) -> string {
|
||||||
if(type == ".ms") return masterSystemManifest(location);
|
if(type == ".ms") return masterSystemManifest(location);
|
||||||
if(type == ".md") return megaDriveManifest(location);
|
if(type == ".md") return megaDriveManifest(location);
|
||||||
if(type == ".pce") return pcEngineManifest(location);
|
if(type == ".pce") return pcEngineManifest(location);
|
||||||
|
if(type == ".sg") return superGrafxManifest(location);
|
||||||
if(type == ".gb") return gameBoyManifest(location);
|
if(type == ".gb") return gameBoyManifest(location);
|
||||||
if(type == ".gbc") return gameBoyColorManifest(location);
|
if(type == ".gbc") return gameBoyColorManifest(location);
|
||||||
if(type == ".gba") return gameBoyAdvanceManifest(location);
|
if(type == ".gba") return gameBoyAdvanceManifest(location);
|
||||||
|
@ -77,6 +79,7 @@ auto Icarus::import(string location) -> string {
|
||||||
if(type == ".ms" || type == ".sms") return masterSystemImport(buffer, location);
|
if(type == ".ms" || type == ".sms") return masterSystemImport(buffer, location);
|
||||||
if(type == ".md" || type == ".smd" || type == ".gen") return megaDriveImport(buffer, location);
|
if(type == ".md" || type == ".smd" || type == ".gen") return megaDriveImport(buffer, location);
|
||||||
if(type == ".pce") return pcEngineImport(buffer, location);
|
if(type == ".pce") return pcEngineImport(buffer, location);
|
||||||
|
if(type == ".sg" || type == ".sgx") return superGrafxImport(buffer, location);
|
||||||
if(type == ".gb") return gameBoyImport(buffer, location);
|
if(type == ".gb") return gameBoyImport(buffer, location);
|
||||||
if(type == ".gbc") return gameBoyColorImport(buffer, location);
|
if(type == ".gbc") return gameBoyColorImport(buffer, location);
|
||||||
if(type == ".gba") return gameBoyAdvanceImport(buffer, location);
|
if(type == ".gba") return gameBoyAdvanceImport(buffer, location);
|
||||||
|
|
|
@ -37,6 +37,11 @@ struct Icarus {
|
||||||
auto pcEngineManifest(vector<uint8_t>& buffer, string location) -> string;
|
auto pcEngineManifest(vector<uint8_t>& buffer, string location) -> string;
|
||||||
auto pcEngineImport(vector<uint8_t>& buffer, string location) -> string;
|
auto pcEngineImport(vector<uint8_t>& buffer, string location) -> string;
|
||||||
|
|
||||||
|
//supergrafx.cpp
|
||||||
|
auto superGrafxManifest(string location) -> string;
|
||||||
|
auto superGrafxManifest(vector<uint8_t>& buffer, string location) -> string;
|
||||||
|
auto superGrafxImport(vector<uint8_t>& buffer, string location) -> string;
|
||||||
|
|
||||||
//game-boy.cpp
|
//game-boy.cpp
|
||||||
auto gameBoyManifest(string location) -> string;
|
auto gameBoyManifest(string location) -> string;
|
||||||
auto gameBoyManifest(vector<uint8_t>& buffer, string location) -> string;
|
auto gameBoyManifest(vector<uint8_t>& buffer, string location) -> string;
|
||||||
|
@ -86,6 +91,7 @@ private:
|
||||||
Markup::Node masterSystem;
|
Markup::Node masterSystem;
|
||||||
Markup::Node megaDrive;
|
Markup::Node megaDrive;
|
||||||
Markup::Node pcEngine;
|
Markup::Node pcEngine;
|
||||||
|
Markup::Node superGrafx;
|
||||||
Markup::Node gameBoy;
|
Markup::Node gameBoy;
|
||||||
Markup::Node gameBoyColor;
|
Markup::Node gameBoyColor;
|
||||||
Markup::Node gameBoyAdvance;
|
Markup::Node gameBoyAdvance;
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
auto Icarus::superGrafxManifest(string location) -> string {
|
||||||
|
vector<uint8_t> buffer;
|
||||||
|
concatenate(buffer, {location, "program.rom"});
|
||||||
|
return superGrafxManifest(buffer, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Icarus::superGrafxManifest(vector<uint8_t>& buffer, string location) -> string {
|
||||||
|
string manifest;
|
||||||
|
|
||||||
|
if(settings["icarus/UseDatabase"].boolean() && !manifest) {
|
||||||
|
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
|
||||||
|
for(auto node : database.superGrafx) {
|
||||||
|
if(node["sha256"].text() == digest) {
|
||||||
|
manifest.append(node.text(), "\n sha256: ", digest, "\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(settings["icarus/UseHeuristics"].boolean() && !manifest) {
|
||||||
|
SuperGrafxCartridge cartridge{location, buffer.data(), buffer.size()};
|
||||||
|
manifest = cartridge.manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Icarus::superGrafxImport(vector<uint8_t>& buffer, string location) -> string {
|
||||||
|
auto name = Location::prefix(location);
|
||||||
|
auto source = Location::path(location);
|
||||||
|
string target{settings["Library/Location"].text(), "SuperGrafx/", name, ".sg/"};
|
||||||
|
//if(directory::exists(target)) return failure("game already exists");
|
||||||
|
|
||||||
|
auto manifest = superGrafxManifest(buffer, location);
|
||||||
|
if(!manifest) return failure("failed to parse ROM image");
|
||||||
|
|
||||||
|
if(!directory::create(target)) return failure("library path unwritable");
|
||||||
|
if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) {
|
||||||
|
file::copy({source, name, ".sav"}, {target, "save.ram"});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, manifest);
|
||||||
|
file::write({target, "program.rom"}, buffer);
|
||||||
|
return success(target);
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
struct SuperGrafxCartridge {
|
||||||
|
SuperGrafxCartridge(string location, uint8_t* data, uint size);
|
||||||
|
|
||||||
|
string manifest;
|
||||||
|
|
||||||
|
//private:
|
||||||
|
struct Information {
|
||||||
|
} information;
|
||||||
|
};
|
||||||
|
|
||||||
|
SuperGrafxCartridge::SuperGrafxCartridge(string location, uint8_t* data, uint size) {
|
||||||
|
manifest.append("board\n");
|
||||||
|
manifest.append(" rom name=program.rom size=0x", hex(size), "\n");
|
||||||
|
manifest.append("\n");
|
||||||
|
manifest.append("information\n");
|
||||||
|
manifest.append(" title: ", Location::prefix(location), "\n");
|
||||||
|
manifest.append(" sha256: ", Hash::SHA256(data, size).digest(), "\n");
|
||||||
|
manifest.append("\n");
|
||||||
|
manifest.append("note: heuristically generated by icarus\n");
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ Settings settings;
|
||||||
#include "heuristics/master-system.cpp"
|
#include "heuristics/master-system.cpp"
|
||||||
#include "heuristics/mega-drive.cpp"
|
#include "heuristics/mega-drive.cpp"
|
||||||
#include "heuristics/pc-engine.cpp"
|
#include "heuristics/pc-engine.cpp"
|
||||||
|
#include "heuristics/supergrafx.cpp"
|
||||||
#include "heuristics/game-boy.cpp"
|
#include "heuristics/game-boy.cpp"
|
||||||
#include "heuristics/game-boy-advance.cpp"
|
#include "heuristics/game-boy-advance.cpp"
|
||||||
#include "heuristics/game-gear.cpp"
|
#include "heuristics/game-gear.cpp"
|
||||||
|
@ -37,6 +38,7 @@ Settings settings;
|
||||||
#include "core/master-system.cpp"
|
#include "core/master-system.cpp"
|
||||||
#include "core/mega-drive.cpp"
|
#include "core/mega-drive.cpp"
|
||||||
#include "core/pc-engine.cpp"
|
#include "core/pc-engine.cpp"
|
||||||
|
#include "core/supergrafx.cpp"
|
||||||
#include "core/game-boy.cpp"
|
#include "core/game-boy.cpp"
|
||||||
#include "core/game-boy-color.cpp"
|
#include "core/game-boy-color.cpp"
|
||||||
#include "core/game-boy-advance.cpp"
|
#include "core/game-boy-advance.cpp"
|
||||||
|
@ -80,6 +82,7 @@ auto nall::main(string_vector args) -> void {
|
||||||
"*.ms:*.sms:"
|
"*.ms:*.sms:"
|
||||||
"*.md:*.smd:*.gen:"
|
"*.md:*.smd:*.gen:"
|
||||||
"*.pce:"
|
"*.pce:"
|
||||||
|
"*.sg:*.sgx:"
|
||||||
"*.gb:"
|
"*.gb:"
|
||||||
"*.gbc:"
|
"*.gbc:"
|
||||||
"*.gba:"
|
"*.gba:"
|
||||||
|
|
|
@ -105,6 +105,7 @@ auto ScanDialog::gamePakType(const string& type) -> bool {
|
||||||
|| type == ".ms"
|
|| type == ".ms"
|
||||||
|| type == ".md"
|
|| type == ".md"
|
||||||
|| type == ".pce"
|
|| type == ".pce"
|
||||||
|
|| type == ".sg"
|
||||||
|| type == ".gb"
|
|| type == ".gb"
|
||||||
|| type == ".gbc"
|
|| type == ".gbc"
|
||||||
|| type == ".gba"
|
|| type == ".gba"
|
||||||
|
@ -120,6 +121,7 @@ auto ScanDialog::gameRomType(const string& type) -> bool {
|
||||||
|| type == ".ms" || type == ".sms"
|
|| type == ".ms" || type == ".sms"
|
||||||
|| type == ".md" || type == ".smd" || type == ".gen"
|
|| type == ".md" || type == ".smd" || type == ".gen"
|
||||||
|| type == ".pce"
|
|| type == ".pce"
|
||||||
|
|| type == ".sg" || type == ".sgx"
|
||||||
|| type == ".gb"
|
|| type == ".gb"
|
||||||
|| type == ".gbc"
|
|| type == ".gbc"
|
||||||
|| type == ".gba"
|
|| type == ".gba"
|
||||||
|
|
Loading…
Reference in New Issue