From 186f0085749d20f6da61e90946f6de20b20f063c Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Tue, 24 Jan 2017 08:18:54 +1100 Subject: [PATCH] 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. --- higan/emulator/emulator.hpp | 2 +- higan/pce/GNUmakefile | 4 +- higan/pce/cartridge/cartridge.cpp | 14 +- higan/pce/cpu/cpu.cpp | 9 +- higan/pce/cpu/cpu.hpp | 7 +- higan/pce/cpu/io.cpp | 63 ++-- higan/pce/interface/interface.cpp | 14 +- higan/pce/interface/interface.hpp | 14 +- higan/pce/interface/pc-engine.cpp | 11 + higan/pce/interface/supergrafx.cpp | 11 + higan/pce/pce.hpp | 8 + higan/pce/system/system.cpp | 11 +- higan/pce/system/system.hpp | 2 +- higan/pce/vce/io.cpp | 54 +++ higan/pce/vce/memory.cpp | 11 + higan/pce/vce/vce.cpp | 69 ++++ higan/pce/vce/vce.hpp | 42 +++ higan/pce/vdc/background.cpp | 25 +- higan/pce/vdc/dma.cpp | 12 +- higan/pce/vdc/io.cpp | 411 ++++++++++------------ higan/pce/vdc/memory.cpp | 12 - higan/pce/vdc/sprite.cpp | 48 +-- higan/pce/vdc/vdc.cpp | 136 ++++--- higan/pce/vdc/vdc.hpp | 83 +++-- higan/pce/vpc/vpc.cpp | 174 +++++++++ higan/pce/vpc/vpc.hpp | 22 ++ higan/processor/huc6280/disassembler.cpp | 16 +- higan/processor/huc6280/huc6280.hpp | 7 +- higan/processor/huc6280/instruction.cpp | 4 +- higan/processor/huc6280/instructions.cpp | 3 +- higan/processor/huc6280/memory.cpp | 31 +- higan/systems/SuperGrafx.sys/manifest.bml | 1 + higan/target-tomoko/program/program.cpp | 3 +- icarus/core/core.cpp | 3 + icarus/core/core.hpp | 6 + icarus/core/supergrafx.cpp | 45 +++ icarus/heuristics/supergrafx.cpp | 20 ++ icarus/icarus.cpp | 3 + icarus/ui/scan-dialog.cpp | 2 + 39 files changed, 929 insertions(+), 484 deletions(-) create mode 100644 higan/pce/interface/pc-engine.cpp create mode 100644 higan/pce/interface/supergrafx.cpp create mode 100644 higan/pce/vce/io.cpp create mode 100644 higan/pce/vce/memory.cpp create mode 100644 higan/pce/vce/vce.cpp create mode 100644 higan/pce/vce/vce.hpp create mode 100644 higan/pce/vpc/vpc.cpp create mode 100644 higan/pce/vpc/vpc.hpp create mode 100644 higan/systems/SuperGrafx.sys/manifest.bml create mode 100644 icarus/core/supergrafx.cpp create mode 100644 icarus/heuristics/supergrafx.cpp diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 5da2eb1c..72bd606d 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -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/"; diff --git a/higan/pce/GNUmakefile b/higan/pce/GNUmakefile index 2684e0f7..eae20a01 100644 --- a/higan/pce/GNUmakefile +++ b/higan/pce/GNUmakefile @@ -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) diff --git a/higan/pce/cartridge/cartridge.cpp b/higan/pce/cartridge/cartridge.cpp index 9638cf02..1f2833ab 100644 --- a/higan/pce/cartridge/cartridge.cpp +++ b/higan/pce/cartridge/cartridge.cpp @@ -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(); diff --git a/higan/pce/cpu/cpu.cpp b/higan/pce/cpu/cpu.cpp index e1ec3d6b..a79835b5 100644 --- a/higan/pce/cpu/cpu.cpp +++ b/higan/pce/cpu/cpu.cpp @@ -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)); diff --git a/higan/pce/cpu/cpu.hpp b/higan/pce/cpu/cpu.hpp index 6b62dacd..56aa0d32 100644 --- a/higan/pce/cpu/cpu.hpp +++ b/higan/pce/cpu/cpu.hpp @@ -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; diff --git a/higan/pce/cpu/io.cpp b/higan/pce/cpu/io.cpp index 8873d031..fa2ce999 100644 --- a/higan/pce/cpu/io.cpp +++ b/higan/pce/cpu/io.cpp @@ -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); +} diff --git a/higan/pce/interface/interface.cpp b/higan/pce/interface/interface.cpp index a7479903..4f184aed 100644 --- a/higan/pce/interface/interface.cpp +++ b/higan/pce/interface/interface.cpp @@ -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(); } diff --git a/higan/pce/interface/interface.hpp b/higan/pce/interface/interface.hpp index eefdc40b..249abc2c 100644 --- a/higan/pce/interface/interface.hpp +++ b/higan/pce/interface/interface.hpp @@ -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; }; diff --git a/higan/pce/interface/pc-engine.cpp b/higan/pce/interface/pc-engine.cpp new file mode 100644 index 00000000..b653a88d --- /dev/null +++ b/higan/pce/interface/pc-engine.cpp @@ -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; +} diff --git a/higan/pce/interface/supergrafx.cpp b/higan/pce/interface/supergrafx.cpp new file mode 100644 index 00000000..8e6dc87a --- /dev/null +++ b/higan/pce/interface/supergrafx.cpp @@ -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; +} diff --git a/higan/pce/pce.hpp b/higan/pce/pce.hpp index 7cf8dfe4..625fb764 100644 --- a/higan/pce/pce.hpp +++ b/higan/pce/pce.hpp @@ -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 #include + #include + #include #include #include diff --git a/higan/pce/system/system.cpp b/higan/pce/system/system.cpp index 268e0d1e..9727c5d8 100644 --- a/higan/pce/system/system.cpp +++ b/higan/pce/system/system.cpp @@ -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); diff --git a/higan/pce/system/system.hpp b/higan/pce/system/system.hpp index 92df0e16..dbd7988a 100644 --- a/higan/pce/system/system.hpp +++ b/higan/pce/system/system.hpp @@ -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; diff --git a/higan/pce/vce/io.cpp b/higan/pce/vce/io.cpp new file mode 100644 index 00000000..1530c6f2 --- /dev/null +++ b/higan/pce/vce/io.cpp @@ -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; + } +} diff --git a/higan/pce/vce/memory.cpp b/higan/pce/vce/memory.cpp new file mode 100644 index 00000000..02ef2858 --- /dev/null +++ b/higan/pce/vce/memory.cpp @@ -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); + } +} diff --git a/higan/pce/vce/vce.cpp b/higan/pce/vce/vce.cpp new file mode 100644 index 00000000..d128610f --- /dev/null +++ b/higan/pce/vce/vce.cpp @@ -0,0 +1,69 @@ +#include + +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; +} + +} diff --git a/higan/pce/vce/vce.hpp b/higan/pce/vce/vce.hpp new file mode 100644 index 00000000..625e7702 --- /dev/null +++ b/higan/pce/vce/vce.hpp @@ -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; diff --git a/higan/pce/vdc/background.cpp b/higan/pce/vdc/background.cpp index 36fe7409..a143c416 100644 --- a/higan/pce/vdc/background.cpp +++ b/higan/pce/vdc/background.cpp @@ -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++; } diff --git a/higan/pce/vdc/dma.cpp b/higan/pce/vdc/dma.cpp index 82b36b8e..68844eb3 100644 --- a/higan/pce/vdc/dma.cpp +++ b/higan/pce/vdc/dma.cpp @@ -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); } } } diff --git a/higan/pce/vdc/io.cpp b/higan/pce/vdc/io.cpp index 548cec8e..90152cc6 100644 --- a/higan/pce/vdc/io.cpp +++ b/higan/pce/vdc/io.cpp @@ -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; } } diff --git a/higan/pce/vdc/memory.cpp b/higan/pce/vdc/memory.cpp index 7d9b464c..0084e771 100644 --- a/higan/pce/vdc/memory.cpp +++ b/higan/pce/vdc/memory.cpp @@ -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); - } -} diff --git a/higan/pce/vdc/sprite.cpp b/higan/pce/vdc/sprite.cpp index eb9d670d..fa9d5240 100644 --- a/higan/pce/vdc/sprite.cpp +++ b/higan/pce/vdc/sprite.cpp @@ -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; } } diff --git a/higan/pce/vdc/vdc.cpp b/higan/pce/vdc/vdc.cpp index d763827b..c0f9193b 100644 --- a/higan/pce/vdc/vdc.cpp +++ b/higan/pce/vdc/vdc.cpp @@ -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; } } diff --git a/higan/pce/vdc/vdc.hpp b/higan/pce/vdc/vdc.hpp index 979a3715..5c20cc14 100644 --- a/higan/pce/vdc/vdc.hpp +++ b/higan/pce/vdc/vdc.hpp @@ -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 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 objects; - maybe color; + uint4 color; + uint4 palette; bool priority; } sprite; @@ -197,4 +195,5 @@ private: } io; }; -extern VDC vdc; +extern VDC vdc0; +extern VDC vdc1; diff --git a/higan/pce/vpc/vpc.cpp b/higan/pce/vpc/vpc.cpp new file mode 100644 index 00000000..9bf483a8 --- /dev/null +++ b/higan/pce/vpc/vpc.cpp @@ -0,0 +1,174 @@ +#include + +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); +} + +} diff --git a/higan/pce/vpc/vpc.hpp b/higan/pce/vpc/vpc.hpp new file mode 100644 index 00000000..80c0ac2c --- /dev/null +++ b/higan/pce/vpc/vpc.hpp @@ -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; diff --git a/higan/processor/huc6280/disassembler.cpp b/higan/processor/huc6280/disassembler.cpp index 1a4cf369..d5d468b0 100644 --- a/higan/processor/huc6280/disassembler.cpp +++ b/higan/processor/huc6280/disassembler.cpp @@ -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"); diff --git a/higan/processor/huc6280/huc6280.hpp b/higan/processor/huc6280/huc6280.hpp index eb5d1121..75064d52 100644 --- a/higan/processor/huc6280/huc6280.hpp +++ b/higan/processor/huc6280/huc6280.hpp @@ -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; diff --git a/higan/processor/huc6280/instruction.cpp b/higan/processor/huc6280/instruction.cpp index a288d85a..80cdc792 100644 --- a/higan/processor/huc6280/instruction.cpp +++ b/higan/processor/huc6280/instruction.cpp @@ -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)) diff --git a/higan/processor/huc6280/instructions.cpp b/higan/processor/huc6280/instructions.cpp index 03751557..fcc2a592 100644 --- a/higan/processor/huc6280/instructions.cpp +++ b/higan/processor/huc6280/instructions.cpp @@ -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 { diff --git a/higan/processor/huc6280/memory.cpp b/higan/processor/huc6280/memory.cpp index cabb0d76..9f3b21e6 100644 --- a/higan/processor/huc6280/memory.cpp +++ b/higan/processor/huc6280/memory.cpp @@ -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); } diff --git a/higan/systems/SuperGrafx.sys/manifest.bml b/higan/systems/SuperGrafx.sys/manifest.bml new file mode 100644 index 00000000..5bbc7066 --- /dev/null +++ b/higan/systems/SuperGrafx.sys/manifest.bml @@ -0,0 +1 @@ +system name:SuperGrafx diff --git a/higan/target-tomoko/program/program.cpp b/higan/target-tomoko/program/program.cpp index b0d9fb2b..3d295154 100644 --- a/higan/target-tomoko/program/program.cpp +++ b/higan/target-tomoko/program/program.cpp @@ -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); diff --git a/icarus/core/core.cpp b/icarus/core/core.cpp index 5af215fb..ed3f1717 100644 --- a/icarus/core/core.cpp +++ b/icarus/core/core.cpp @@ -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); diff --git a/icarus/core/core.hpp b/icarus/core/core.hpp index 35a0fb8c..ec2c2292 100644 --- a/icarus/core/core.hpp +++ b/icarus/core/core.hpp @@ -37,6 +37,11 @@ struct Icarus { auto pcEngineManifest(vector& buffer, string location) -> string; auto pcEngineImport(vector& buffer, string location) -> string; + //supergrafx.cpp + auto superGrafxManifest(string location) -> string; + auto superGrafxManifest(vector& buffer, string location) -> string; + auto superGrafxImport(vector& buffer, string location) -> string; + //game-boy.cpp auto gameBoyManifest(string location) -> string; auto gameBoyManifest(vector& 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; diff --git a/icarus/core/supergrafx.cpp b/icarus/core/supergrafx.cpp new file mode 100644 index 00000000..7042cb5f --- /dev/null +++ b/icarus/core/supergrafx.cpp @@ -0,0 +1,45 @@ +auto Icarus::superGrafxManifest(string location) -> string { + vector buffer; + concatenate(buffer, {location, "program.rom"}); + return superGrafxManifest(buffer, location); +} + +auto Icarus::superGrafxManifest(vector& 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& 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); +} diff --git a/icarus/heuristics/supergrafx.cpp b/icarus/heuristics/supergrafx.cpp new file mode 100644 index 00000000..1b6f81ae --- /dev/null +++ b/icarus/heuristics/supergrafx.cpp @@ -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"); +} diff --git a/icarus/icarus.cpp b/icarus/icarus.cpp index 4ca97c19..30795194 100644 --- a/icarus/icarus.cpp +++ b/icarus/icarus.cpp @@ -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:" diff --git a/icarus/ui/scan-dialog.cpp b/icarus/ui/scan-dialog.cpp index 934065f6..aa0d4f43 100644 --- a/icarus/ui/scan-dialog.cpp +++ b/icarus/ui/scan-dialog.cpp @@ -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"