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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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/cpu/cpu.hpp>
#include <pce/vpc/vpc.hpp>
#include <pce/vce/vce.hpp>
#include <pce/vdc/vdc.hpp>
#include <pce/psg/psg.hpp>

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

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

View File

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