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 {
|
||||
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 License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
processors += huc6280
|
||||
|
||||
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-controller
|
||||
|
||||
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-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-psg.o: pce/psg/psg.cpp $(call rwildcard,pce/psg)
|
||||
obj/pce-system.o: pce/system/system.cpp $(call rwildcard,pce/system)
|
||||
|
|
|
@ -7,9 +7,17 @@ Cartridge cartridge;
|
|||
auto Cartridge::load() -> bool {
|
||||
information = {};
|
||||
|
||||
if(auto pathID = platform->load(ID::PCEngine, "PC Engine", "pce")) {
|
||||
information.pathID = pathID();
|
||||
} else return false;
|
||||
if(Model::PCEngine()) {
|
||||
if(auto pathID = platform->load(ID::PCEngine, "PC Engine", "pce")) {
|
||||
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)) {
|
||||
information.manifest = fp->reads();
|
||||
|
|
|
@ -19,7 +19,9 @@ auto CPU::main() -> void {
|
|||
auto CPU::step(uint clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
timer.step(clocks);
|
||||
synchronize(vdc);
|
||||
synchronize(vdc0);
|
||||
synchronize(vdc1);
|
||||
synchronize(vce);
|
||||
synchronize(psg);
|
||||
for(auto peripheral : peripherals) synchronize(*peripheral);
|
||||
}
|
||||
|
@ -28,9 +30,10 @@ auto CPU::power() -> void {
|
|||
HuC6280::power();
|
||||
create(CPU::Enter, system.colorburst() * 2.0);
|
||||
|
||||
r.pc.byte(0) = read(0x1ffe);
|
||||
r.pc.byte(1) = read(0x1fff);
|
||||
r.pc.byte(0) = read(0x00, 0x1ffe);
|
||||
r.pc.byte(1) = read(0x00, 0x1fff);
|
||||
|
||||
for(auto& byte : ram) byte = 0x00;
|
||||
memory::fill(&irq, sizeof(IRQ));
|
||||
memory::fill(&timer, sizeof(Timer));
|
||||
memory::fill(&io, sizeof(IO));
|
||||
|
|
|
@ -9,8 +9,9 @@ struct CPU : Processor::HuC6280, Thread {
|
|||
auto lastCycle() -> void override;
|
||||
|
||||
//io.cpp
|
||||
auto read(uint21 addr) -> uint8 override;
|
||||
auto write(uint21 addr, uint8 data) -> void override;
|
||||
auto read(uint8 bank, uint13 addr) -> uint8 override;
|
||||
auto write(uint8 bank, uint13 addr, uint8 data) -> void override;
|
||||
auto store(uint2 addr, uint8 data) -> void override;
|
||||
|
||||
//timer.cpp
|
||||
auto timerStep(uint clocks) -> void;
|
||||
|
@ -60,7 +61,7 @@ struct CPU : Processor::HuC6280, Thread {
|
|||
} io;
|
||||
|
||||
private:
|
||||
uint8 ram[0x2000];
|
||||
uint8 ram[0x8000]; //PC Engine = 8KB, SuperGrafx = 32KB
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
||||
|
|
|
@ -1,23 +1,26 @@
|
|||
auto CPU::read(uint21 addr) -> uint8 {
|
||||
//$000000-0fffff HuCard
|
||||
if(!addr.bit(20)) {
|
||||
return cartridge.read(addr);
|
||||
auto CPU::read(uint8 bank, uint13 addr) -> uint8 {
|
||||
//$00-7f HuCard
|
||||
if(!bank.bit(7)) {
|
||||
return cartridge.read(bank << 13 | addr);
|
||||
}
|
||||
|
||||
uint8 bank = addr.bits(13,20);
|
||||
addr = addr.bits(0,12);
|
||||
|
||||
//$1f8000-1fbfff RAM
|
||||
//$f8-fb RAM
|
||||
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) {
|
||||
//$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
|
||||
if((addr & 0x1800) == 0x0000) {
|
||||
return vdc.read(addr);
|
||||
if((addr & 0x1c00) == 0x0400) {
|
||||
return vce.read(addr);
|
||||
}
|
||||
|
||||
//$0800-0bff PSG
|
||||
|
@ -84,27 +87,30 @@ auto CPU::read(uint21 addr) -> uint8 {
|
|||
return 0xff;
|
||||
}
|
||||
|
||||
auto CPU::write(uint21 addr, uint8 data) -> void {
|
||||
//$000000-0fffff HuCard
|
||||
if(!addr.bit(20)) {
|
||||
return cartridge.write(addr, data);
|
||||
auto CPU::write(uint8 bank, uint13 addr, uint8 data) -> void {
|
||||
//$00-7f HuCard
|
||||
if(!bank.bit(7)) {
|
||||
return cartridge.write(bank << 13 | addr, data);
|
||||
}
|
||||
|
||||
uint8 bank = addr.bits(13,20);
|
||||
addr = addr.bits(0,12);
|
||||
|
||||
//$1f8000-1fbfff RAM
|
||||
//$f8-fb RAM
|
||||
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;
|
||||
}
|
||||
|
||||
//$1fe000-1fffff Hardware
|
||||
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
|
||||
if((addr & 0x1800) == 0x0000) {
|
||||
return vdc.write(addr, data);
|
||||
if((addr & 0x1c00) == 0x0400) {
|
||||
return vce.write(addr, data);
|
||||
}
|
||||
|
||||
//$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 {
|
||||
|
||||
Model model;
|
||||
Settings settings;
|
||||
#include "pc-engine.cpp"
|
||||
#include "supergrafx.cpp"
|
||||
|
||||
Interface::Interface() {
|
||||
information.manufacturer = "NEC";
|
||||
information.name = "PC Engine";
|
||||
information.overscan = true;
|
||||
information.overscan = true;
|
||||
|
||||
information.capability.states = false;
|
||||
information.capability.cheats = false;
|
||||
|
||||
media.append({ID::PCEngine, "PC Engine", "pce"});
|
||||
|
||||
Port controllerPort{ID::Port::Controller, "Controller Port"};
|
||||
|
||||
{ Device device{ID::Device::None, "None"};
|
||||
|
@ -83,11 +82,6 @@ auto Interface::loaded() -> bool {
|
|||
return system.loaded();
|
||||
}
|
||||
|
||||
auto Interface::load(uint id) -> bool {
|
||||
if(id == ID::PCEngine) return system.load(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::save() -> void {
|
||||
system.save();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ struct ID {
|
|||
enum : uint {
|
||||
System,
|
||||
PCEngine,
|
||||
SuperGrafx,
|
||||
};
|
||||
|
||||
struct Port { enum : uint {
|
||||
|
@ -33,7 +34,6 @@ struct Interface : Emulator::Interface {
|
|||
auto audioFrequency() -> double override;
|
||||
|
||||
auto loaded() -> bool override;
|
||||
auto load(uint id) -> bool override;
|
||||
auto save() -> void override;
|
||||
auto unload() -> void override;
|
||||
|
||||
|
@ -49,6 +49,18 @@ struct Interface : Emulator::Interface {
|
|||
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 {
|
||||
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/cpu/cpu.hpp>
|
||||
#include <pce/vpc/vpc.hpp>
|
||||
#include <pce/vce/vce.hpp>
|
||||
#include <pce/vdc/vdc.hpp>
|
||||
#include <pce/psg/psg.hpp>
|
||||
|
||||
|
|
|
@ -2,15 +2,17 @@
|
|||
|
||||
namespace PCEngine {
|
||||
|
||||
uint Model::id;
|
||||
System system;
|
||||
Scheduler scheduler;
|
||||
#include "peripherals.cpp"
|
||||
|
||||
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 = {};
|
||||
|
||||
if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) {
|
||||
|
@ -45,7 +47,10 @@ auto System::power() -> void {
|
|||
scheduler.reset();
|
||||
cartridge.power();
|
||||
cpu.power();
|
||||
vdc.power();
|
||||
vpc.power();
|
||||
vce.power();
|
||||
vdc0.power();
|
||||
vdc1.power();
|
||||
psg.power();
|
||||
scheduler.primary(cpu);
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ struct System {
|
|||
|
||||
auto run() -> void;
|
||||
|
||||
auto load(Emulator::Interface*) -> bool;
|
||||
auto load(Emulator::Interface*, uint) -> bool;
|
||||
auto save() -> 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 {
|
||||
color = nothing;
|
||||
color = 0;
|
||||
palette = 0;
|
||||
|
||||
uint16 batAddress;
|
||||
batAddress = (voffset >> 3) & (height - 1);
|
||||
batAddress *= width;
|
||||
batAddress += (hoffset >> 3) & (width - 1);
|
||||
|
||||
uint16 tiledata = vdc.vram.read(batAddress);
|
||||
uint16 tiledata = vdc->vram.read(batAddress);
|
||||
uint16 patternAddress = tiledata.bits(0,11) << 4;
|
||||
patternAddress += (voffset & 7);
|
||||
uint4 palette = tiledata.bits(12,15);
|
||||
|
||||
uint16 d0 = vdc.vram.read(patternAddress + 0);
|
||||
uint16 d1 = vdc.vram.read(patternAddress + 8);
|
||||
uint16 d0 = vdc->vram.read(patternAddress + 0);
|
||||
uint16 d1 = vdc->vram.read(patternAddress + 8);
|
||||
|
||||
uint3 index = 7 - (hoffset & 7);
|
||||
uint4 output;
|
||||
output.bit(0) = d0.bit(0 + index);
|
||||
output.bit(1) = d0.bit(8 + index);
|
||||
output.bit(2) = d1.bit(0 + index);
|
||||
output.bit(3) = d1.bit(8 + index);
|
||||
uint4 color;
|
||||
color.bit(0) = d0.bit(0 + index);
|
||||
color.bit(1) = d0.bit(8 + index);
|
||||
color.bit(2) = d1.bit(0 + 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++;
|
||||
}
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
auto VDC::DMA::step(uint clocks) -> void {
|
||||
while(clocks--) {
|
||||
if(vramActive) {
|
||||
uint16 data = vdc.vram.read(source);
|
||||
vdc.vram.write(target, data);
|
||||
uint16 data = vdc->vram.read(source);
|
||||
vdc->vram.write(target, data);
|
||||
sourceIncrementMode == 0 ? source++ : source--;
|
||||
targetIncrementMode == 0 ? target++ : target--;
|
||||
if(!--length) {
|
||||
vramActive = false;
|
||||
vdc.irq.raise(VDC::IRQ::Line::TransferVRAM);
|
||||
vdc->irq.raise(VDC::IRQ::Line::TransferVRAM);
|
||||
}
|
||||
}
|
||||
|
||||
if(satbActive) {
|
||||
uint16 data = vdc.vram.read(satbSource + satbOffset);
|
||||
vdc.satb.write(satbOffset, data);
|
||||
uint16 data = vdc->vram.read(satbSource + satbOffset);
|
||||
vdc->satb.write(satbOffset, data);
|
||||
if(++satbOffset == 256) {
|
||||
satbActive = false;
|
||||
satbOffset = 0;
|
||||
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);
|
||||
if(!addr.bit(10)) {
|
||||
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;
|
||||
}
|
||||
bool a1 = addr.bit(1);
|
||||
|
||||
if(addr.bit(1) == 1) {
|
||||
if(io.address == 0x02) {
|
||||
//VRR
|
||||
uint8 data = vram.dataRead.byte(a0);
|
||||
if(a0) {
|
||||
vram.addressRead += vram.addressIncrement;
|
||||
vram.dataRead = vram.read(vram.addressRead);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
if(a1 == 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;
|
||||
} else {
|
||||
if(addr.bits(0,2) == 0x04) {
|
||||
//CTR
|
||||
uint8 data = cram.read(cram.address).bits(0,7);
|
||||
return data;
|
||||
}
|
||||
|
||||
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;
|
||||
if(io.address == 0x02) {
|
||||
//VRR
|
||||
uint8 data = vram.dataRead.byte(a0);
|
||||
if(a0) {
|
||||
vram.addressRead += vram.addressIncrement;
|
||||
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 {
|
||||
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
|
||||
if(data.bits(0,1) == 0) vce.clock = 4;
|
||||
if(data.bits(0,1) == 1) vce.clock = 3;
|
||||
if(data.bits(0,1) == 2) vce.clock = 2;
|
||||
if(data.bits(0,1) == 3) vce.clock = 2;
|
||||
io.colorBlur = data.bit(2);
|
||||
io.grayscale = data.bit(7);
|
||||
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(addr.bits(0,2) == 0x02) {
|
||||
//CTA
|
||||
cram.address.bits(0,7) = data.bits(0,7);
|
||||
if(io.address == 0x06) {
|
||||
//RCR
|
||||
io.lineCoincidence.byte(a0) = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr.bits(0,2) == 0x03) {
|
||||
//CTA
|
||||
cram.address.bit(8) = data.bit(0);
|
||||
if(io.address == 0x07) {
|
||||
//BXR
|
||||
background.hscroll.byte(a0) = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr.bits(0,2) == 0x04) {
|
||||
//CTW
|
||||
cram.write(cram.address, 0, data.bits(0,7));
|
||||
if(io.address == 0x08) {
|
||||
//BYR
|
||||
background.vscroll.byte(a0) = data;
|
||||
background.vcounter = background.vscroll;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr.bits(0,2) == 0x05) {
|
||||
//CTW
|
||||
cram.write(cram.address, 1, data.bit(0));
|
||||
cram.address++;
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,15 +15,3 @@ auto VDC::SATB::read(uint8 addr) -> uint16 {
|
|||
auto VDC::SATB::write(uint8 addr, uint16 value) -> void {
|
||||
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;
|
||||
for(uint index : range(64)) {
|
||||
uint16 d0 = vdc.satb.read(index << 2 | 0);
|
||||
uint16 d1 = vdc.satb.read(index << 2 | 1);
|
||||
uint16 d2 = vdc.satb.read(index << 2 | 2);
|
||||
uint16 d3 = vdc.satb.read(index << 2 | 3);
|
||||
uint16 d0 = vdc->satb.read(index << 2 | 0);
|
||||
uint16 d1 = vdc->satb.read(index << 2 | 1);
|
||||
uint16 d2 = vdc->satb.read(index << 2 | 2);
|
||||
uint16 d3 = vdc->satb.read(index << 2 | 3);
|
||||
|
||||
Object object;
|
||||
object.y = d0.bits(0,9);
|
||||
|
@ -34,18 +34,18 @@ auto VDC::Sprite::scanline(uint y) -> void {
|
|||
|
||||
if(object.width == 15) {
|
||||
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 {
|
||||
//32-width sprites count as two 16-width sprite slots
|
||||
object.pattern ^= object.hflip;
|
||||
object.width = 15;
|
||||
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.pattern ^= 1;
|
||||
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;
|
||||
y += 64;
|
||||
|
||||
color = nothing;
|
||||
color = 0;
|
||||
palette = 0;
|
||||
priority = 0;
|
||||
if(!enable) return;
|
||||
|
||||
bool first = false;
|
||||
|
@ -73,26 +75,28 @@ auto VDC::Sprite::run(uint x, uint y) -> void {
|
|||
patternAddress <<= 6;
|
||||
patternAddress += (voffset & 15);
|
||||
|
||||
uint16 d0 = vdc.vram.read(patternAddress + 0);
|
||||
uint16 d1 = vdc.vram.read(patternAddress + 16);
|
||||
uint16 d2 = vdc.vram.read(patternAddress + 32);
|
||||
uint16 d3 = vdc.vram.read(patternAddress + 48);
|
||||
uint16 d0 = vdc->vram.read(patternAddress + 0);
|
||||
uint16 d1 = vdc->vram.read(patternAddress + 16);
|
||||
uint16 d2 = vdc->vram.read(patternAddress + 32);
|
||||
uint16 d3 = vdc->vram.read(patternAddress + 48);
|
||||
|
||||
uint4 index = 15 - (hoffset & 15);
|
||||
uint4 output;
|
||||
output.bit(0) = d0.bit(index);
|
||||
output.bit(1) = d1.bit(index);
|
||||
output.bit(2) = d2.bit(index);
|
||||
output.bit(3) = d3.bit(index);
|
||||
if(output == 0) continue;
|
||||
uint4 color;
|
||||
color.bit(0) = d0.bit(index);
|
||||
color.bit(1) = d1.bit(index);
|
||||
color.bit(2) = d2.bit(index);
|
||||
color.bit(3) = d3.bit(index);
|
||||
if(color == 0) continue;
|
||||
|
||||
if(color) {
|
||||
if(first) return vdc.irq.raise(VDC::IRQ::Line::Collision);
|
||||
if(this->color) {
|
||||
if(first) return vdc->irq.raise(VDC::IRQ::Line::Collision);
|
||||
return;
|
||||
}
|
||||
|
||||
color = vdc.cram.read(1 << 8 | object.palette << 4 | output);
|
||||
priority = object.priority;
|
||||
this->color = color;
|
||||
this->palette = object.palette;
|
||||
this->priority = object.priority;
|
||||
|
||||
if(object.first) first = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
namespace PCEngine {
|
||||
|
||||
VDC vdc;
|
||||
VDC vdc0;
|
||||
VDC vdc1;
|
||||
#include "memory.cpp"
|
||||
#include "io.cpp"
|
||||
#include "irq.cpp"
|
||||
|
@ -11,112 +12,105 @@ VDC vdc;
|
|||
#include "sprite.cpp"
|
||||
|
||||
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 output = buffer + 1140 * vce.vclock;
|
||||
if(Model::PCEngine() && vdc1.active()) return step(frequency());
|
||||
|
||||
//todo: if block breaks TV Sports Basketball
|
||||
//if(vce.vclock >= vce.vstart && vce.voffset < vce.vlength) {
|
||||
background.scanline(vce.voffset);
|
||||
sprite.scanline(vce.voffset);
|
||||
//}
|
||||
timing.vpulse = false;
|
||||
timing.vclock = 0;
|
||||
timing.voffset = 0;
|
||||
timing.vstart = max((uint8)2, timing.verticalDisplayStart) - 2;
|
||||
timing.vlength = min(242, timing.verticalDisplayLength + 1);
|
||||
|
||||
while(vce.hclock < 1140) {
|
||||
uint9 color = 0;
|
||||
while(!timing.vpulse) {
|
||||
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
|
||||
&& vce.hclock >= vce.hstart && vce.hoffset < vce.hlength
|
||||
) {
|
||||
background.run(vce.hoffset, vce.voffset);
|
||||
sprite.run(vce.hoffset, vce.voffset);
|
||||
vce.hoffset++;
|
||||
if(timing.vclock >= timing.vstart && timing.voffset < timing.vlength) {
|
||||
background.scanline(timing.voffset);
|
||||
sprite.scanline(timing.voffset);
|
||||
|
||||
if(sprite.color && sprite.priority) {
|
||||
color = sprite.color();
|
||||
} else if(background.color) {
|
||||
color = background.color();
|
||||
} else if(sprite.color) {
|
||||
color = sprite.color();
|
||||
} else {
|
||||
color = cram.read(0);
|
||||
step(timing.hstart);
|
||||
|
||||
while(timing.hoffset < timing.hlength) {
|
||||
data = 0;
|
||||
|
||||
background.run(timing.hoffset, timing.voffset);
|
||||
sprite.run(timing.hoffset, timing.voffset);
|
||||
|
||||
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(vce.clock >= 2) *output++ = color;
|
||||
if(vce.clock >= 3) *output++ = color;
|
||||
if(vce.clock >= 4) *output++ = color;
|
||||
|
||||
step(vce.clock);
|
||||
if(timing.vclock == timing.vstart + timing.vlength) {
|
||||
irq.raise(IRQ::Line::Vblank);
|
||||
dma.satbStart();
|
||||
}
|
||||
}
|
||||
|
||||
if(vce.vclock == io.lineCoincidence - (65 - vce.vstart)) {
|
||||
irq.raise(IRQ::Line::LineCoincidence);
|
||||
}
|
||||
|
||||
step(1365 - vce.hclock);
|
||||
scanline();
|
||||
}
|
||||
|
||||
auto VDC::scanline() -> void {
|
||||
vce.hclock = 0;
|
||||
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();
|
||||
}
|
||||
timing.hpulse = true;
|
||||
}
|
||||
|
||||
auto VDC::frame() -> void {
|
||||
scheduler.exit(Scheduler::Event::Frame);
|
||||
|
||||
vce.vclock = 0;
|
||||
vce.voffset = 0;
|
||||
vce.vstart = max((uint8)2, vce.verticalDisplayStart) - 2;
|
||||
vce.vlength = min(242, vce.verticalDisplayLength + 1);
|
||||
timing.vpulse = true;
|
||||
}
|
||||
|
||||
auto VDC::step(uint clocks) -> void {
|
||||
vce.hclock += clocks;
|
||||
Thread::step(clocks);
|
||||
dma.step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto VDC::refresh() -> void {
|
||||
Emulator::video.refresh(buffer + 1140 * vce.vstart, 1140 * sizeof(uint32), 1140, 242);
|
||||
timing.hclock += clocks;
|
||||
dma.step(clocks);
|
||||
}
|
||||
|
||||
auto VDC::power() -> void {
|
||||
create(VDC::Enter, system.colorburst() * 6.0);
|
||||
|
||||
for(auto& pixel : buffer) pixel = 0;
|
||||
memory::fill(&vram, sizeof(VRAM));
|
||||
memory::fill(&satb, sizeof(SATB));
|
||||
memory::fill(&cram, sizeof(CRAM));
|
||||
memory::fill(&timing, sizeof(Timing));
|
||||
memory::fill(&irq, sizeof(IRQ));
|
||||
memory::fill(&dma, sizeof(DMA));
|
||||
memory::fill(&vce, sizeof(VCE));
|
||||
memory::fill(&io, sizeof(IO));
|
||||
memory::fill(&background, sizeof(Background));
|
||||
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
|
||||
|
||||
struct VDC : Thread {
|
||||
inline auto bus() const -> uint9 { return data; }
|
||||
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto scanline() -> void;
|
||||
auto frame() -> void;
|
||||
auto refresh() -> void;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto read(uint11 addr) -> uint8;
|
||||
auto write(uint11 addr, uint8 data) -> void;
|
||||
auto read(uint2 addr) -> uint8;
|
||||
auto write(uint2 addr, uint8 data) -> void;
|
||||
|
||||
private:
|
||||
uint32 buffer[1140 * 512];
|
||||
uint9 data;
|
||||
|
||||
struct VRAM {
|
||||
//memory.cpp
|
||||
|
@ -43,16 +43,32 @@ private:
|
|||
uint16 data[0x100];
|
||||
} satb;
|
||||
|
||||
struct CRAM {
|
||||
//memory.cpp
|
||||
auto read(uint9 addr) -> uint9;
|
||||
auto write(uint9 addr, bool a0, uint8 data) -> void;
|
||||
struct Timing {
|
||||
uint5 horizontalSyncWidth;
|
||||
uint7 horizontalDisplayStart;
|
||||
uint7 horizontalDisplayLength;
|
||||
uint7 horizontalDisplayEnd;
|
||||
|
||||
uint9 address;
|
||||
uint5 verticalSyncWidth;
|
||||
uint8 verticalDisplayStart;
|
||||
uint9 verticalDisplayLength;
|
||||
uint8 verticalDisplayEnd;
|
||||
|
||||
private:
|
||||
uint9 data[0x200];
|
||||
} cram;
|
||||
bool vpulse;
|
||||
bool hpulse;
|
||||
|
||||
uint hclock;
|
||||
uint vclock;
|
||||
|
||||
uint hoffset;
|
||||
uint voffset;
|
||||
|
||||
uint hstart;
|
||||
uint vstart;
|
||||
|
||||
uint hlength;
|
||||
uint vlength;
|
||||
} timing;
|
||||
|
||||
struct IRQ {
|
||||
enum class Line : uint {
|
||||
|
@ -85,6 +101,8 @@ private:
|
|||
} irq;
|
||||
|
||||
struct DMA {
|
||||
VDC* vdc = nullptr;
|
||||
|
||||
//dma.cpp
|
||||
auto step(uint clocks) -> void;
|
||||
auto vramStart() -> void;
|
||||
|
@ -105,33 +123,9 @@ private:
|
|||
uint16 satbOffset;
|
||||
} 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 {
|
||||
VDC* vdc = nullptr;
|
||||
|
||||
//background.cpp
|
||||
auto scanline(uint y) -> void;
|
||||
auto run(uint x, uint y) -> void;
|
||||
|
@ -146,10 +140,13 @@ private:
|
|||
uint10 hoffset;
|
||||
uint9 voffset;
|
||||
|
||||
maybe<uint9> color;
|
||||
uint4 color;
|
||||
uint4 palette;
|
||||
} background;
|
||||
|
||||
struct Sprite {
|
||||
VDC* vdc = nullptr;
|
||||
|
||||
//sprite.cpp
|
||||
auto scanline(uint y) -> void;
|
||||
auto run(uint x, uint y) -> void;
|
||||
|
@ -171,7 +168,8 @@ private:
|
|||
};
|
||||
array<Object, 64> objects;
|
||||
|
||||
maybe<uint9> color;
|
||||
uint4 color;
|
||||
uint4 palette;
|
||||
bool priority;
|
||||
} sprite;
|
||||
|
||||
|
@ -197,4 +195,5 @@ private:
|
|||
} 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 {
|
||||
uint21 pc = mmu(pc_);
|
||||
string s{hex((uint)pc.bits(13,20), 2L), ":", hex((uint)pc.bits(0,12), 4L), " "};
|
||||
auto HuC6280::disassemble(uint16 pc) -> string {
|
||||
uint8 bank = r.mpr[pc.bits(13,15)];
|
||||
uint13 addr = pc.bits(0,12);
|
||||
|
||||
string s{hex(bank, 2L), ":", hex(addr, 4L), " "};
|
||||
|
||||
auto readByte = [&]() -> uint8 {
|
||||
return read(pc++);
|
||||
return read(bank, addr++);
|
||||
};
|
||||
|
||||
auto readWord = [&]() -> uint16 {
|
||||
uint16 data = read(pc++);
|
||||
return data | read(pc++) << 8;
|
||||
uint16 data = read(bank, addr++) << 0;
|
||||
return data | read(bank, addr++) << 8;
|
||||
};
|
||||
|
||||
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(" Y:", hex(r.y, 2L));
|
||||
s.append(" S:", hex(r.s, 2L));
|
||||
s.append(" PC:", hex(pc_, 4L));
|
||||
s.append(" PC:", hex(pc, 4L));
|
||||
s.append(" ");
|
||||
s.append(r.p.n ? "N" : "n");
|
||||
s.append(r.p.v ? "V" : "v");
|
||||
|
|
|
@ -6,19 +6,18 @@ namespace Processor {
|
|||
|
||||
struct HuC6280 {
|
||||
virtual auto step(uint clocks) -> void = 0;
|
||||
virtual auto read(uint21 addr) -> uint8 = 0;
|
||||
virtual auto write(uint21 addr, uint8 data) -> void = 0;
|
||||
virtual auto read(uint8 bank, uint13 addr) -> uint8 = 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;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
//memory.cpp
|
||||
inline auto mmu(uint16) const -> uint21;
|
||||
inline auto load8(uint8) -> uint8;
|
||||
inline auto load16(uint16) -> uint8;
|
||||
inline auto store8(uint8, uint8) -> void;
|
||||
inline auto store16(uint16, uint8) -> void;
|
||||
inline auto store21(uint21, uint8) -> void;
|
||||
|
||||
auto io() -> uint8;
|
||||
auto opcode() -> uint8;
|
||||
|
|
|
@ -47,7 +47,7 @@ U op(0x0b, NOP)
|
|||
op(0x10, branch, N == 0)
|
||||
op(0x11, indirectYLoad, fp(ORA), A)
|
||||
op(0x12, indirectLoad, fp(ORA), A)
|
||||
op(0x13, ST, 2)
|
||||
op(0x13, ST, 1)
|
||||
op(0x14, zeropageModify, fp(TRB))
|
||||
op(0x15, zeropageLoad, fp(ORA), A, X)
|
||||
op(0x16, zeropageModify, fp(ASL), X)
|
||||
|
@ -63,7 +63,7 @@ U op(0x1b, NOP)
|
|||
op(0x20, JSR)
|
||||
op(0x21, indirectLoad, fp(AND), A, X)
|
||||
op(0x22, swap, A, X)
|
||||
op(0x23, ST, 3)
|
||||
op(0x23, ST, 2)
|
||||
op(0x24, zeropageLoad, fp(BIT), A)
|
||||
op(0x25, zeropageLoad, fp(AND), A)
|
||||
op(0x26, zeropageModify, fp(ROL))
|
||||
|
|
|
@ -495,7 +495,8 @@ L store8(zeropage, data);
|
|||
auto HuC6280::instruction_ST(uint2 index) -> void {
|
||||
auto data = operand();
|
||||
io();
|
||||
L store21(0x1fe000 + index, data);
|
||||
L io();
|
||||
store(index, data);
|
||||
}
|
||||
|
||||
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 {
|
||||
step(r.cs);
|
||||
return read(mmu(0x2000 + addr));
|
||||
return read(r.mpr[1], addr);
|
||||
}
|
||||
|
||||
auto HuC6280::load16(uint16 addr) -> uint8 {
|
||||
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 {
|
||||
step(r.cs);
|
||||
return write(mmu(0x2000 + addr), data);
|
||||
return write(r.mpr[1], addr, data);
|
||||
}
|
||||
|
||||
auto HuC6280::store16(uint16 addr, uint8 data) -> void {
|
||||
step(r.cs);
|
||||
return write(mmu(addr), data);
|
||||
}
|
||||
|
||||
auto HuC6280::store21(uint21 addr, uint8 data) -> void {
|
||||
step(r.cs);
|
||||
return write(addr, data);
|
||||
return write(r.mpr[addr.bits(13,15)], addr.bits(0,12), data);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
auto HuC6280::io() -> uint8 {
|
||||
step(r.cs);
|
||||
return read(mmu(PC));
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto HuC6280::opcode() -> uint8 {
|
||||
step(r.cs);
|
||||
return read(mmu(PC++));
|
||||
return load16(PC++);
|
||||
}
|
||||
|
||||
auto HuC6280::operand() -> uint8 {
|
||||
step(r.cs);
|
||||
return read(mmu(PC++));
|
||||
return load16(PC++);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
auto HuC6280::push(uint8 data) -> void {
|
||||
step(r.cs);
|
||||
write(mmu(0x2100 + S), data);
|
||||
write(r.mpr[1], 0x0100 | S, data);
|
||||
S--;
|
||||
}
|
||||
|
||||
auto HuC6280::pull() -> uint8 {
|
||||
step(r.cs);
|
||||
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 MasterSystem::MasterSystemInterface);
|
||||
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::GameBoyColorInterface);
|
||||
emulators.append(new GameBoyAdvance::Interface);
|
||||
|
|
|
@ -4,6 +4,7 @@ Icarus::Icarus() {
|
|||
database.masterSystem = BML::unserialize(string::read(locate("Database/Master System.bml")));
|
||||
database.megaDrive = BML::unserialize(string::read(locate("Database/Mega Drive.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.gameBoyColor = BML::unserialize(string::read(locate("Database/Game Boy Color.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 == ".md") return megaDriveManifest(location);
|
||||
if(type == ".pce") return pcEngineManifest(location);
|
||||
if(type == ".sg") return superGrafxManifest(location);
|
||||
if(type == ".gb") return gameBoyManifest(location);
|
||||
if(type == ".gbc") return gameBoyColorManifest(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 == ".md" || type == ".smd" || type == ".gen") return megaDriveImport(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 == ".gbc") return gameBoyColorImport(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 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
|
||||
auto gameBoyManifest(string location) -> string;
|
||||
auto gameBoyManifest(vector<uint8_t>& buffer, string location) -> string;
|
||||
|
@ -86,6 +91,7 @@ private:
|
|||
Markup::Node masterSystem;
|
||||
Markup::Node megaDrive;
|
||||
Markup::Node pcEngine;
|
||||
Markup::Node superGrafx;
|
||||
Markup::Node gameBoy;
|
||||
Markup::Node gameBoyColor;
|
||||
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/mega-drive.cpp"
|
||||
#include "heuristics/pc-engine.cpp"
|
||||
#include "heuristics/supergrafx.cpp"
|
||||
#include "heuristics/game-boy.cpp"
|
||||
#include "heuristics/game-boy-advance.cpp"
|
||||
#include "heuristics/game-gear.cpp"
|
||||
|
@ -37,6 +38,7 @@ Settings settings;
|
|||
#include "core/master-system.cpp"
|
||||
#include "core/mega-drive.cpp"
|
||||
#include "core/pc-engine.cpp"
|
||||
#include "core/supergrafx.cpp"
|
||||
#include "core/game-boy.cpp"
|
||||
#include "core/game-boy-color.cpp"
|
||||
#include "core/game-boy-advance.cpp"
|
||||
|
@ -80,6 +82,7 @@ auto nall::main(string_vector args) -> void {
|
|||
"*.ms:*.sms:"
|
||||
"*.md:*.smd:*.gen:"
|
||||
"*.pce:"
|
||||
"*.sg:*.sgx:"
|
||||
"*.gb:"
|
||||
"*.gbc:"
|
||||
"*.gba:"
|
||||
|
|
|
@ -105,6 +105,7 @@ auto ScanDialog::gamePakType(const string& type) -> bool {
|
|||
|| type == ".ms"
|
||||
|| type == ".md"
|
||||
|| type == ".pce"
|
||||
|| type == ".sg"
|
||||
|| type == ".gb"
|
||||
|| type == ".gbc"
|
||||
|| type == ".gba"
|
||||
|
@ -120,6 +121,7 @@ auto ScanDialog::gameRomType(const string& type) -> bool {
|
|||
|| type == ".ms" || type == ".sms"
|
||||
|| type == ".md" || type == ".smd" || type == ".gen"
|
||||
|| type == ".pce"
|
||||
|| type == ".sg" || type == ".sgx"
|
||||
|| type == ".gb"
|
||||
|| type == ".gbc"
|
||||
|| type == ".gba"
|
||||
|
|
Loading…
Reference in New Issue