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:
Tim Allen 2017-01-24 08:18:54 +11:00
parent bdc100e123
commit 186f008574
39 changed files with 929 additions and 484 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

54
higan/pce/vce/io.cpp Normal file
View File

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

11
higan/pce/vce/memory.cpp Normal file
View File

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

69
higan/pce/vce/vce.cpp Normal file
View File

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

42
higan/pce/vce/vce.hpp Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

174
higan/pce/vpc/vpc.cpp Normal file
View File

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

22
higan/pce/vpc/vpc.hpp Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
system name:SuperGrafx

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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