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"