diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 1f6ef54f..8896d8e7 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 = "101.29"; + static const string Version = "101.30"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/ms/cartridge/cartridge.cpp b/higan/ms/cartridge/cartridge.cpp index 3616d15f..fe7c1158 100644 --- a/higan/ms/cartridge/cartridge.cpp +++ b/higan/ms/cartridge/cartridge.cpp @@ -84,13 +84,29 @@ auto Cartridge::reset() -> void { mapper.romPage2 = 2; } +auto Cartridge::Memory::mirror(uint addr, uint size) -> uint { + uint base = 0; + uint mask = 1 << 21; + while(addr >= size) { + while(!(addr & mask)) mask >>= 1; + addr -= mask; + if(size > mask) { + size -= mask; + base += mask; + } + mask >>= 1; + } + return base + addr; +} + auto Cartridge::Memory::read(uint addr) -> uint8 { - if(addr < this->size) return this->data[addr]; - return 0x00; + if(!size) return 0x00; + return this->data[mirror(addr, size)]; } auto Cartridge::Memory::write(uint addr, uint8 data) -> void { - if(addr < this->size) this->data[addr] = data; + if(!size) return; + this->data[mirror(addr, size)] = data; } } diff --git a/higan/ms/cartridge/cartridge.hpp b/higan/ms/cartridge/cartridge.hpp index 560a574c..57ee103b 100644 --- a/higan/ms/cartridge/cartridge.hpp +++ b/higan/ms/cartridge/cartridge.hpp @@ -28,6 +28,7 @@ private: uint size = 0; uint mask = 0; + static auto mirror(uint addr, uint size) -> uint; auto read(uint addr) -> uint8; auto write(uint addr, uint8 data) -> void; }; diff --git a/higan/ms/cpu/cpu.cpp b/higan/ms/cpu/cpu.cpp index 32938d09..5f488751 100644 --- a/higan/ms/cpu/cpu.cpp +++ b/higan/ms/cpu/cpu.cpp @@ -9,13 +9,6 @@ auto CPU::Enter() -> void { } auto CPU::main() -> void { - #if 0 - static uint64 instructionsExecuted = 0; - if(instructionsExecuted < 20) - print(disassemble(r.pc), "\n"); - instructionsExecuted++; - #endif - //note: SMS1 extbus value is random; SMS2+ is pulled high ($ff) if(state.nmiLine) { diff --git a/higan/ms/vdp/sprite.cpp b/higan/ms/vdp/sprite.cpp index 7cfa681d..e273203a 100644 --- a/higan/ms/vdp/sprite.cpp +++ b/higan/ms/vdp/sprite.cpp @@ -3,7 +3,7 @@ auto VDP::Sprite::scanline() -> void { state.y = vdp.io.vcounter; objects.reset(); - bool large = vdp.io.extendedHeight; + uint limit = vdp.io.spriteTile ? 15 : 7; uint14 attributeAddress = vdp.io.spriteAttributeTableAddress << 8; for(uint index : range(64)) { uint8 y = vdp.vram[attributeAddress + index]; @@ -14,9 +14,9 @@ auto VDP::Sprite::scanline() -> void { if(vdp.io.spriteShift) x -= 8; y += 1; if(state.y < y) continue; - if(state.y > y + (large ? 15 : 7)) continue; + if(state.y > y + limit) continue; - if(large) pattern.bit(0) = 0; + if(limit == 15) pattern.bit(0) = 0; objects.append({x, y, pattern}); if(objects.size() == 8) { @@ -31,8 +31,7 @@ auto VDP::Sprite::run() -> void { if(state.y >= vdp.vlines()) return; - bool large = vdp.io.extendedHeight; - uint mask = vdp.io.extendedHeight ? 15 : 7; + uint limit = vdp.io.spriteTile ? 15 : 7; for(auto& o : objects) { if(state.x < o.x) continue; if(state.x > o.x + 7) continue; @@ -42,7 +41,7 @@ auto VDP::Sprite::run() -> void { uint14 address = vdp.io.spritePatternTableAddress << 13; address += o.pattern << 5; - address += (y & mask) << 2; + address += (y & limit) << 2; auto index = 7 - (x & 7); uint4 color; diff --git a/higan/pce/GNUmakefile b/higan/pce/GNUmakefile new file mode 100644 index 00000000..2684e0f7 --- /dev/null +++ b/higan/pce/GNUmakefile @@ -0,0 +1,14 @@ +processors += huc6280 + +objects += pce-interface +objects += pce-cpu 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-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) +obj/pce-cartridge.o: pce/cartridge/cartridge.cpp $(call rwildcard,pce/cartridge) +obj/pce-controller.o: pce/controller/controller.cpp $(call rwildcard,pce/controller) diff --git a/higan/pce/cartridge/cartridge.cpp b/higan/pce/cartridge/cartridge.cpp new file mode 100644 index 00000000..9d3edfc7 --- /dev/null +++ b/higan/pce/cartridge/cartridge.cpp @@ -0,0 +1,93 @@ +#include + +namespace PCEngine { + +Cartridge cartridge; + +auto Cartridge::load() -> bool { + information = {}; + + if(auto pathID = interface->load(ID::PCEngine, "PC Engine", "pce")) { + information.pathID = pathID(); + } else return false; + + if(auto fp = interface->open(pathID(), "manifest.bml", File::Read, File::Required)) { + information.manifest = fp->reads(); + } else return false; + + auto document = BML::unserialize(information.manifest); + information.title = document["information/title"].text(); + + if(auto node = document["board/rom"]) { + rom.size = node["size"].natural(); + if(rom.size) { + rom.data = new uint8[rom.size](); + if(auto name = node["name"].text()) { + if(auto fp = interface->open(pathID(), name, File::Read, File::Required)) { + fp->read(rom.data, rom.size); + } + } + } + } + + if(auto node = document["board/ram"]) { + ram.size = node["size"].natural(); + if(ram.size) { + ram.data = new uint8[ram.size](); + if(auto name = node["name"].text()) { + if(auto fp = interface->open(pathID(), name, File::Read)) { + fp->read(ram.data, ram.size); + } + } + } + } + + return true; +} + +auto Cartridge::save() -> void { + auto document = BML::unserialize(information.manifest); + + if(auto name = document["board/ram/name"].text()) { + if(auto fp = interface->open(pathID(), name, File::Write)) { + fp->write(ram.data, ram.size); + } + } +} + +auto Cartridge::unload() -> void { + delete[] rom.data; + delete[] ram.data; + rom = {}; + ram = {}; +} + +auto Cartridge::power() -> void { +} + +auto Cartridge::Memory::mirror(uint addr, uint size) -> uint { + uint base = 0; + uint mask = 1 << 23; + while(addr >= size) { + while(!(addr & mask)) mask >>= 1; + addr -= mask; + if(size > mask) { + size -= mask; + base += mask; + } + mask >>= 1; + } + return base + addr; +} + +auto Cartridge::Memory::read(uint addr) -> uint8 { + if(!size) return 0x00; + return this->data[mirror(addr, size)]; +} + +auto Cartridge::Memory::write(uint addr, uint8 data) -> void { + if(!size) return; + this->data[mirror(addr, size)] = data; +} + +} diff --git a/higan/pce/cartridge/cartridge.hpp b/higan/pce/cartridge/cartridge.hpp new file mode 100644 index 00000000..318a6e8a --- /dev/null +++ b/higan/pce/cartridge/cartridge.hpp @@ -0,0 +1,34 @@ +struct Cartridge { + auto pathID() const -> uint { return information.pathID; } + auto sha256() const -> string { return information.sha256; } + auto manifest() const -> string { return information.manifest; } + auto title() const -> string { return information.title; } + + auto load() -> bool; + auto save() -> void; + auto unload() -> void; + + auto power() -> void; + +private: + struct Information { + uint pathID = 0; + string sha256; + string manifest; + string title; + } information; + + struct Memory { + uint8* data = nullptr; + uint size = 0; + + static auto mirror(uint addr, uint size) -> uint; + auto read(uint addr) -> uint8; + auto write(uint addr, uint8 data) -> void; + }; + + Memory rom; + Memory ram; +}; + +extern Cartridge cartridge; diff --git a/higan/pce/controller/controller.cpp b/higan/pce/controller/controller.cpp new file mode 100644 index 00000000..9adb25ec --- /dev/null +++ b/higan/pce/controller/controller.cpp @@ -0,0 +1,26 @@ +#include + +namespace PCEngine { + +#include "gamepad/gamepad.cpp" + +Controller::Controller() { + if(!handle()) create(Controller::Enter, 100); +} + +Controller::~Controller() { +} + +auto Controller::Enter() -> void { + while(true) { + scheduler.synchronize(); + if(peripherals.controllerPort->active()) peripherals.controllerPort->main(); + } +} + +auto Controller::main() -> void { + step(1); + synchronize(cpu); +} + +} diff --git a/higan/pce/controller/controller.hpp b/higan/pce/controller/controller.hpp new file mode 100644 index 00000000..6701fdc0 --- /dev/null +++ b/higan/pce/controller/controller.hpp @@ -0,0 +1,11 @@ +struct Controller : Thread { + Controller(); + virtual ~Controller(); + + static auto Enter() -> void; + auto main() -> void; + + virtual auto readData() -> uint8 { return 0x00; } +}; + +#include "gamepad/gamepad.hpp" diff --git a/higan/pce/controller/gamepad/gamepad.cpp b/higan/pce/controller/gamepad/gamepad.cpp new file mode 100644 index 00000000..ca1d7204 --- /dev/null +++ b/higan/pce/controller/gamepad/gamepad.cpp @@ -0,0 +1,6 @@ +Gamepad::Gamepad() { +} + +auto Gamepad::readData() -> uint8 { + return 0x00; +} diff --git a/higan/pce/controller/gamepad/gamepad.hpp b/higan/pce/controller/gamepad/gamepad.hpp new file mode 100644 index 00000000..faae0e12 --- /dev/null +++ b/higan/pce/controller/gamepad/gamepad.hpp @@ -0,0 +1,9 @@ +struct Gamepad : Controller { + enum : uint { + Up, Down, Left, Right, Two, One, Select, Run, + }; + + Gamepad(); + + auto readData() -> uint8 override; +}; diff --git a/higan/pce/cpu/cpu.cpp b/higan/pce/cpu/cpu.cpp new file mode 100644 index 00000000..829993c1 --- /dev/null +++ b/higan/pce/cpu/cpu.cpp @@ -0,0 +1,27 @@ +#include + +namespace PCEngine { + +CPU cpu; + +auto CPU::Enter() -> void { + while(true) scheduler.synchronize(), cpu.main(); +} + +auto CPU::main() -> void { + instruction(); +} + +auto CPU::step(uint clocks) -> void { + Thread::step(clocks); + synchronize(vdc); + synchronize(psg); + for(auto peripheral : peripherals) synchronize(*peripheral); +} + +auto CPU::power() -> void { + HuC6280::power(); + create(CPU::Enter, system.colorburst() * 6.0); +} + +} diff --git a/higan/pce/cpu/cpu.hpp b/higan/pce/cpu/cpu.hpp new file mode 100644 index 00000000..a216bcbb --- /dev/null +++ b/higan/pce/cpu/cpu.hpp @@ -0,0 +1,13 @@ +//Hudson Soft HuC6280 + +struct CPU : Processor::HuC6280, Thread { + static auto Enter() -> void; + auto main() -> void; + auto step(uint clocks) -> void; + + auto power() -> void; + + vector peripherals; +}; + +extern CPU cpu; diff --git a/higan/pce/interface/interface.cpp b/higan/pce/interface/interface.cpp new file mode 100644 index 00000000..8bc52005 --- /dev/null +++ b/higan/pce/interface/interface.cpp @@ -0,0 +1,127 @@ +#include + +namespace PCEngine { + +Interface* interface = nullptr; +Settings settings; + +Interface::Interface() { + interface = this; + + information.manufacturer = "NEC"; + information.name = "PC Engine"; + information.overscan = true; + information.resettable = false; + + 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"}; + controllerPort.devices.append(device); + } + + { Device device{ID::Device::Gamepad, "Gamepad"}; + device.inputs.append({0, "Up"}); + device.inputs.append({0, "Down"}); + device.inputs.append({0, "Left"}); + device.inputs.append({0, "Right"}); + device.inputs.append({0, "II"}); + device.inputs.append({0, "I"}); + device.inputs.append({0, "Select"}); + device.inputs.append({0, "Run"}); + controllerPort.devices.append(device); + } + + ports.append(move(controllerPort)); +} + +auto Interface::manifest() -> string { + return cartridge.manifest(); +} + +auto Interface::title() -> string { + return cartridge.title(); +} + +auto Interface::videoSize() -> VideoSize { + return {512, 484}; +} + +auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize { + auto a = arc ? 8.0 / 7.0 : 1.0; + uint w = 256; + uint h = 242; + uint m = min(width / (w * a), height / h); + return {uint(w * a * m), uint(h * m)}; +} + +auto Interface::videoFrequency() -> double { + return 60.0; +} + +auto Interface::videoColors() -> uint32 { + return 1 << 9; +} + +auto Interface::videoColor(uint32 color) -> uint64 { + return 0; +} + +auto Interface::audioFrequency() -> double { + return 44'100.0; //todo: not accurate +} + +auto Interface::loaded() -> bool { + return system.loaded(); +} + +auto Interface::load(uint id) -> bool { + if(id == ID::PCEngine) return system.load(); + return false; +} + +auto Interface::save() -> void { + system.save(); +} + +auto Interface::unload() -> void { + system.unload(); +} + +auto Interface::connect(uint port, uint device) -> void { + PCEngine::peripherals.connect(port, device); +} + +auto Interface::power() -> void { + system.power(); +} + +auto Interface::run() -> void { + system.run(); +} + +auto Interface::serialize() -> serializer { + return {}; +} + +auto Interface::unserialize(serializer& s) -> bool { + return false; +} + +auto Interface::cap(const string& name) -> bool { + return false; +} + +auto Interface::get(const string& name) -> any { + return {}; +} + +auto Interface::set(const string& name, const any& value) -> bool { + return false; +} + +} diff --git a/higan/pce/interface/interface.hpp b/higan/pce/interface/interface.hpp new file mode 100644 index 00000000..3f0dfdf4 --- /dev/null +++ b/higan/pce/interface/interface.hpp @@ -0,0 +1,59 @@ +namespace PCEngine { + +struct ID { + enum : uint { + System, + PCEngine, + }; + + struct Port { enum : uint { + Controller, + };}; + + struct Device { enum : uint { + None, + Gamepad, + };}; +}; + +struct Interface : Emulator::Interface { + using Emulator::Interface::load; + + Interface(); + + auto manifest() -> string override; + auto title() -> string override; + + auto videoSize() -> VideoSize override; + auto videoSize(uint width, uint height, bool arc) -> VideoSize override; + auto videoFrequency() -> double override; + auto videoColors() -> uint32 override; + auto videoColor(uint32 color) -> uint64 override; + + auto audioFrequency() -> double override; + + auto loaded() -> bool override; + auto load(uint id) -> bool override; + auto save() -> void override; + auto unload() -> void override; + + auto connect(uint port, uint device) -> void override; + auto power() -> void override; + auto run() -> void override; + + auto serialize() -> serializer override; + auto unserialize(serializer&) -> bool override; + + auto cap(const string& name) -> bool override; + auto get(const string& name) -> any override; + auto set(const string& name, const any& value) -> bool override; +}; + +struct Settings { + uint controllerPort = 0; +}; + +extern Interface* interface; +extern Settings settings; + +} diff --git a/higan/pce/pce.hpp b/higan/pce/pce.hpp new file mode 100644 index 00000000..0cb35ec8 --- /dev/null +++ b/higan/pce/pce.hpp @@ -0,0 +1,38 @@ +#pragma once + +//license: GPLv3 +//started: 2017-01-11 + +#include +#include +#include + +#include + +namespace PCEngine { + using File = Emulator::File; + using Scheduler = Emulator::Scheduler; + extern Scheduler scheduler; + + struct Thread : Emulator::Thread { + auto create(auto (*entrypoint)() -> void, double frequency) -> void { + Emulator::Thread::create(entrypoint, frequency); + scheduler.append(*this); + } + + inline auto synchronize(Thread& thread) -> void { + if(clock() >= thread.clock()) scheduler.resume(thread); + } + }; + + #include + + #include + #include + #include + + #include + #include +} + +#include diff --git a/higan/pce/psg/psg.cpp b/higan/pce/psg/psg.cpp new file mode 100644 index 00000000..7922e163 --- /dev/null +++ b/higan/pce/psg/psg.cpp @@ -0,0 +1,26 @@ +#include + +namespace PCEngine { + +PSG psg; + +auto PSG::Enter() -> void { + while(true) scheduler.synchronize(), psg.main(); +} + +auto PSG::main() -> void { + step(1); + stream->sample(0.0, 0.0); +} + +auto PSG::step(uint clocks) -> void { + Thread::step(clocks); + synchronize(cpu); +} + +auto PSG::power() -> void { + create(PSG::Enter, 44'100.0); + stream = Emulator::audio.createStream(2, 44'100.0); +} + +} diff --git a/higan/pce/psg/psg.hpp b/higan/pce/psg/psg.hpp new file mode 100644 index 00000000..234bb61f --- /dev/null +++ b/higan/pce/psg/psg.hpp @@ -0,0 +1,13 @@ +//Programmable Sound Generator + +struct PSG : Thread { + shared_pointer stream; + + static auto Enter() -> void; + auto main() -> void; + auto step(uint clocks) -> void; + + auto power() -> void; +}; + +extern PSG psg; diff --git a/higan/pce/system/peripherals.cpp b/higan/pce/system/peripherals.cpp new file mode 100644 index 00000000..e4595ccf --- /dev/null +++ b/higan/pce/system/peripherals.cpp @@ -0,0 +1,26 @@ +Peripherals peripherals; + +auto Peripherals::unload() -> void { + delete controllerPort; + controllerPort = nullptr; +} + +auto Peripherals::reset() -> void { + connect(ID::Port::Controller, settings.controllerPort); +} + +auto Peripherals::connect(uint port, uint device) -> void { + if(port == ID::Port::Controller) { + settings.controllerPort = device; + if(!system.loaded()) return; + + delete controllerPort; + switch(device) { default: + case ID::Device::None: controllerPort = new Controller; break; + case ID::Device::Gamepad: controllerPort = new Gamepad; break; + } + } + + cpu.peripherals.reset(); + cpu.peripherals.append(controllerPort); +} diff --git a/higan/pce/system/system.cpp b/higan/pce/system/system.cpp new file mode 100644 index 00000000..d4cb17c3 --- /dev/null +++ b/higan/pce/system/system.cpp @@ -0,0 +1,54 @@ +#include + +namespace PCEngine { + +System system; +Scheduler scheduler; +#include "peripherals.cpp" + +auto System::run() -> void { + if(scheduler.enter() == Scheduler::Event::Frame) vdc.refresh(); +} + +auto System::load() -> bool { + information = {}; + + if(auto fp = interface->open(ID::System, "manifest.bml", File::Read, File::Required)) { + information.manifest = fp->reads(); + } else return false; + + auto document = BML::unserialize(information.manifest); + if(!cartridge.load()) return false; + + information.colorburst = Emulator::Constants::Colorburst::NTSC; + return information.loaded = true; +} + +auto System::save() -> void { + cartridge.save(); +} + +auto System::unload() -> void { + peripherals.unload(); + cartridge.unload(); +} + +auto System::power() -> void { + Emulator::video.reset(); + Emulator::video.setInterface(interface); + Emulator::video.setPalette(); + + Emulator::audio.reset(); + Emulator::audio.setInterface(interface); + + scheduler.reset(); + cartridge.power(); + cpu.power(); + vdc.power(); + psg.power(); + scheduler.primary(cpu); + + peripherals.reset(); +} + +} diff --git a/higan/pce/system/system.hpp b/higan/pce/system/system.hpp new file mode 100644 index 00000000..e1aad5fe --- /dev/null +++ b/higan/pce/system/system.hpp @@ -0,0 +1,30 @@ +struct System { + auto loaded() const -> bool { return information.loaded; } + auto colorburst() const -> double { return information.colorburst; } + + auto run() -> void; + + auto load() -> bool; + auto save() -> void; + auto unload() -> void; + + auto power() -> void; + +private: + struct Information { + bool loaded = false; + string manifest; + double colorburst = 0.0; + } information; +}; + +struct Peripherals { + auto unload() -> void; + auto reset() -> void; + auto connect(uint port, uint device) -> void; + + Controller* controllerPort = nullptr; +}; + +extern System system; +extern Peripherals peripherals; diff --git a/higan/pce/vdc/vdc.cpp b/higan/pce/vdc/vdc.cpp new file mode 100644 index 00000000..af155642 --- /dev/null +++ b/higan/pce/vdc/vdc.cpp @@ -0,0 +1,30 @@ +#include + +namespace PCEngine { + +VDC vdc; + +auto VDC::Enter() -> void { + while(true) scheduler.synchronize(), vdc.main(); +} + +auto VDC::main() -> void { + step(system.colorburst() * 6.0 / 60.0); + scheduler.exit(Scheduler::Event::Frame); +} + +auto VDC::step(uint clocks) -> void { + Thread::step(clocks); + synchronize(cpu); +} + +auto VDC::refresh() -> void { + Emulator::video.refresh(buffer, 512 * sizeof(uint32), 512, 484); +} + +auto VDC::power() -> void { + create(VDC::Enter, system.colorburst() * 6.0); + for(auto& pixel : buffer) pixel = 0; +} + +} diff --git a/higan/pce/vdc/vdc.hpp b/higan/pce/vdc/vdc.hpp new file mode 100644 index 00000000..60b767fa --- /dev/null +++ b/higan/pce/vdc/vdc.hpp @@ -0,0 +1,15 @@ +//Video Display Controller + +struct VDC : Thread { + static auto Enter() -> void; + auto main() -> void; + auto step(uint clocks) -> void; + auto refresh() -> void; + + auto power() -> void; + +private: + uint32 buffer[512 * 484]; +}; + +extern VDC vdc; diff --git a/higan/processor/GNUmakefile b/higan/processor/GNUmakefile index 2aaa74f7..a6fafc01 100644 --- a/higan/processor/GNUmakefile +++ b/higan/processor/GNUmakefile @@ -3,6 +3,7 @@ processors := $(call unique,$(processors)) objects += $(if $(findstring arm,$(processors)),processor-arm) objects += $(if $(findstring gsu,$(processors)),processor-gsu) objects += $(if $(findstring hg51b,$(processors)),processor-hg51b) +objects += $(if $(findstring huc6280,$(processors)),processor-huc6280) objects += $(if $(findstring lr35902,$(processors)),processor-lr35902) objects += $(if $(findstring m68k,$(processors)),processor-m68k) objects += $(if $(findstring r6502,$(processors)),processor-r6502) @@ -15,6 +16,7 @@ objects += $(if $(findstring z80,$(processors)),processor-z80) obj/processor-arm.o: processor/arm/arm.cpp $(call rwildcard,processor/arm) obj/processor-gsu.o: processor/gsu/gsu.cpp $(call rwildcard,processor/gsu) obj/processor-hg51b.o: processor/hg51b/hg51b.cpp $(call rwildcard,processor/hg51b) +obj/processor-huc6280.o: processor/huc6280/huc6280.cpp $(call rwildcard,processor/huc6280) obj/processor-lr35902.o: processor/lr35902/lr35902.cpp $(call rwildcard,processor/lr35902) obj/processor-m68k.o: processor/m68k/m68k.cpp $(call rwildcard,processor/m68k) obj/processor-r6502.o: processor/r6502/r6502.cpp $(call rwildcard,processor/r6502) diff --git a/higan/processor/huc6280/huc6280.cpp b/higan/processor/huc6280/huc6280.cpp new file mode 100644 index 00000000..f80cd77e --- /dev/null +++ b/higan/processor/huc6280/huc6280.cpp @@ -0,0 +1,13 @@ +#include +#include "huc6280.hpp" + +namespace Processor { + +auto HuC6280::power() -> void { +} + +auto HuC6280::instruction() -> void { + step(1); +} + +} diff --git a/higan/processor/huc6280/huc6280.hpp b/higan/processor/huc6280/huc6280.hpp new file mode 100644 index 00000000..a47051bc --- /dev/null +++ b/higan/processor/huc6280/huc6280.hpp @@ -0,0 +1,14 @@ +//Hudson Soft HuC6280A + +#pragma once + +namespace Processor { + +struct HuC6280 { + virtual auto step(uint clocks) -> void = 0; + + auto power() -> void; + auto instruction() -> void; +}; + +} diff --git a/higan/processor/z80/disassembler.cpp b/higan/processor/z80/disassembler.cpp index 08217269..29fac128 100644 --- a/higan/processor/z80/disassembler.cpp +++ b/higan/processor/z80/disassembler.cpp @@ -339,6 +339,7 @@ auto Z80::disassemble(uint16 pc, uint8 prefix, uint8 code) -> string { op(0xe0, "ret ", "po") op(0xe1, "pop ", HL) op(0xe2, "jp ", "po", NN) + op(0xe3, "ex ", ISP, HL) op(0xe4, "call", "po", NN) op(0xe5, "push", HL) op(0xe6, "and ", A, N) diff --git a/higan/processor/z80/instruction.cpp b/higan/processor/z80/instruction.cpp index 6b93a3e4..365610bd 100644 --- a/higan/processor/z80/instruction.cpp +++ b/higan/processor/z80/instruction.cpp @@ -257,6 +257,7 @@ auto Z80::instruction(uint8 code) -> void { op(0xe0, RET_c, PF == 0) op(0xe1, POP_rr, HL) op(0xe2, JP_c_nn, PF == 0) + op(0xe3, EX_irr_rr, SP, HL) op(0xe4, CALL_c_nn, PF == 0) op(0xe5, PUSH_rr, HL) op(0xe6, AND_a_n) diff --git a/higan/processor/z80/instructions.cpp b/higan/processor/z80/instructions.cpp index ed7eb19a..8f834fde 100644 --- a/higan/processor/z80/instructions.cpp +++ b/higan/processor/z80/instructions.cpp @@ -376,7 +376,7 @@ auto Z80::instructionCPD() -> void { auto Z80::instructionCPDR() -> void { instructionCPD(); - if(!VF || ZF) return; + if(!BC) return; wait(5); PC -= 2; } @@ -392,7 +392,7 @@ auto Z80::instructionCPI() -> void { auto Z80::instructionCPIR() -> void { instructionCPI(); - if(!VF || ZF) return; + if(!BC) return; wait(5); PC -= 2; } @@ -452,6 +452,15 @@ auto Z80::instructionEI() -> void { r.ei = 1; //raise IFF1, IFF2 after the next instruction } +auto Z80::instructionEX_irr_rr(uint16& x, uint16& y) -> void { + uint16 z; + z.byte(0) = read(x + 0); + z.byte(1) = read(x + 1); + write(x + 0, y.byte(0)); + write(x + 1, y.byte(1)); + y = z; +} + auto Z80::instructionEX_rr_rr(uint16& x, uint16& y) -> void { auto z = x; x = y; @@ -501,13 +510,13 @@ auto Z80::instructionIND() -> void { wait(1); auto data = in(C); write(_HL--, data); - NF = 0; + NF = 1; ZF = --B == 0; } auto Z80::instructionINDR() -> void { instructionIND(); - if(ZF) return; + if(!B) return; wait(5); PC -= 2; } @@ -516,13 +525,13 @@ auto Z80::instructionINI() -> void { wait(1); auto data = in(C); write(_HL++, data); - NF = 0; + NF = 1; ZF = --B == 0; } auto Z80::instructionINIR() -> void { instructionINI(); - if(ZF) return; + if(!B) return; wait(5); PC -= 2; } @@ -616,7 +625,7 @@ auto Z80::instructionLDD() -> void { auto Z80::instructionLDDR() -> void { instructionLDD(); - if(!VF) return; + if(!BC) return; wait(5); PC -= 2; } @@ -632,7 +641,7 @@ auto Z80::instructionLDI() -> void { auto Z80::instructionLDIR() -> void { instructionLDI(); - if(!VF) return; + if(!BC) return; wait(5); PC -= 2; } @@ -658,14 +667,14 @@ auto Z80::instructionOR_a_r(uint8& x) -> void { auto Z80::instructionOTDR() -> void { instructionOUTD(); - if(ZF) return; + if(!B) return; wait(5); PC -= 2; } auto Z80::instructionOTIR() -> void { instructionOUTI(); - if(ZF) return; + if(!B) return; wait(5); PC -= 2; } diff --git a/higan/processor/z80/z80.hpp b/higan/processor/z80/z80.hpp index 74cc384f..c896aba4 100644 --- a/higan/processor/z80/z80.hpp +++ b/higan/processor/z80/z80.hpp @@ -93,6 +93,7 @@ struct Z80 { auto instructionDI() -> void; auto instructionDJNZ_e() -> void; auto instructionEI() -> void; + auto instructionEX_irr_rr(uint16&, uint16&) -> void; auto instructionEX_rr_rr(uint16&, uint16&) -> void; auto instructionEXX() -> void; auto instructionHALT() -> void; diff --git a/higan/sfc/debugger.hpp b/higan/sfc/debugger.hpp deleted file mode 100644 index 0524715a..00000000 --- a/higan/sfc/debugger.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -namespace SuperFamicom { - -struct Debugger { - struct CPU { - function read; - function write; - function execute; - function nmi; - function irq; - } cpu; - - struct SMP { - function read; - function write; - function execute; - } smp; - - struct PPU { - struct VRAM { - function read; - function write; - } vram; - - struct OAM { - function read; - function write; - } oam; - - struct CGRAM { - function read; - function write; - } cgram; - } ppu; -}; - -extern Debugger debugger; - -} diff --git a/higan/sfc/interface/interface.cpp b/higan/sfc/interface/interface.cpp index 211b16a5..7c77bd2a 100644 --- a/higan/sfc/interface/interface.cpp +++ b/higan/sfc/interface/interface.cpp @@ -4,7 +4,6 @@ namespace SuperFamicom { Interface* interface = nullptr; Settings settings; -Debugger debugger; Interface::Interface() { interface = this; diff --git a/higan/sfc/interface/interface.hpp b/higan/sfc/interface/interface.hpp index 4ff0eaa9..f0445e35 100644 --- a/higan/sfc/interface/interface.hpp +++ b/higan/sfc/interface/interface.hpp @@ -1,5 +1,3 @@ -#include - namespace SuperFamicom { struct ID { diff --git a/higan/systems/PC Engine.sys/manifest.bml b/higan/systems/PC Engine.sys/manifest.bml new file mode 100644 index 00000000..fa4735f6 --- /dev/null +++ b/higan/systems/PC Engine.sys/manifest.bml @@ -0,0 +1 @@ +system name:PC Engine diff --git a/higan/target-tomoko/GNUmakefile b/higan/target-tomoko/GNUmakefile index d518f062..478d36f1 100644 --- a/higan/target-tomoko/GNUmakefile +++ b/higan/target-tomoko/GNUmakefile @@ -5,6 +5,7 @@ include fc/GNUmakefile include sfc/GNUmakefile include ms/GNUmakefile include md/GNUmakefile +include pce/GNUmakefile include gb/GNUmakefile include gba/GNUmakefile include ws/GNUmakefile diff --git a/higan/target-tomoko/program/program.cpp b/higan/target-tomoko/program/program.cpp index 06aa549f..c47b02e3 100644 --- a/higan/target-tomoko/program/program.cpp +++ b/higan/target-tomoko/program/program.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ Program::Program(string_vector args) { emulators.append(new SuperFamicom::Interface); emulators.append(new MasterSystem::Interface); emulators.append(new MegaDrive::Interface); + emulators.append(new PCEngine::Interface); emulators.append(new GameBoy::Interface); emulators.append(new GameBoyAdvance::Interface); emulators.append(new WonderSwan::Interface); diff --git a/icarus/core/core.cpp b/icarus/core/core.cpp index d63fe827..5af215fb 100644 --- a/icarus/core/core.cpp +++ b/icarus/core/core.cpp @@ -3,6 +3,7 @@ Icarus::Icarus() { database.superFamicom = BML::unserialize(string::read(locate("Database/Super Famicom.bml"))); 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.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"))); @@ -36,6 +37,7 @@ auto Icarus::manifest(string location) -> string { if(type == ".sfc") return superFamicomManifest(location); if(type == ".ms") return masterSystemManifest(location); if(type == ".md") return megaDriveManifest(location); + if(type == ".pce") return pcEngineManifest(location); if(type == ".gb") return gameBoyManifest(location); if(type == ".gbc") return gameBoyColorManifest(location); if(type == ".gba") return gameBoyAdvanceManifest(location); @@ -74,6 +76,7 @@ auto Icarus::import(string location) -> string { if(type == ".sfc" || type == ".smc") return superFamicomImport(buffer, location); 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 == ".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 8e663c1c..35a0fb8c 100644 --- a/icarus/core/core.hpp +++ b/icarus/core/core.hpp @@ -32,6 +32,11 @@ struct Icarus { auto megaDriveManifest(vector& buffer, string location) -> string; auto megaDriveImport(vector& buffer, string location) -> string; + //pc-engine.cpp + auto pcEngineManifest(string location) -> string; + auto pcEngineManifest(vector& buffer, string location) -> string; + auto pcEngineImport(vector& buffer, string location) -> string; + //game-boy.cpp auto gameBoyManifest(string location) -> string; auto gameBoyManifest(vector& buffer, string location) -> string; @@ -80,6 +85,7 @@ private: Markup::Node superFamicom; Markup::Node masterSystem; Markup::Node megaDrive; + Markup::Node pcEngine; Markup::Node gameBoy; Markup::Node gameBoyColor; Markup::Node gameBoyAdvance; diff --git a/icarus/core/pc-engine.cpp b/icarus/core/pc-engine.cpp new file mode 100644 index 00000000..679cfb4d --- /dev/null +++ b/icarus/core/pc-engine.cpp @@ -0,0 +1,45 @@ +auto Icarus::pcEngineManifest(string location) -> string { + vector buffer; + concatenate(buffer, {location, "program.rom"}); + return pcEngineManifest(buffer, location); +} + +auto Icarus::pcEngineManifest(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.pcEngine) { + if(node["sha256"].text() == digest) { + manifest.append(node.text(), "\n sha256: ", digest, "\n"); + break; + } + } + } + + if(settings["icarus/UseHeuristics"].boolean() && !manifest) { + PCEngineCartridge cartridge{location, buffer.data(), buffer.size()}; + manifest = cartridge.manifest; + } + + return manifest; +} + +auto Icarus::pcEngineImport(vector& buffer, string location) -> string { + auto name = Location::prefix(location); + auto source = Location::path(location); + string target{settings["Library/Location"].text(), "PC Engine/", name, ".pce/"}; +//if(directory::exists(target)) return failure("game already exists"); + + auto manifest = pcEngineManifest(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/pc-engine.cpp b/icarus/heuristics/pc-engine.cpp new file mode 100644 index 00000000..316194c2 --- /dev/null +++ b/icarus/heuristics/pc-engine.cpp @@ -0,0 +1,21 @@ +struct PCEngineCartridge { + PCEngineCartridge(string location, uint8_t* data, uint size); + + string manifest; + +//private: + struct Information { + } information; +}; + +PCEngineCartridge::PCEngineCartridge(string location, uint8_t* data, uint size) { + manifest.append("board\n"); + manifest.append(" rom name=program.rom size=0x", hex(size), "\n"); + manifest.append(" ram name=save.ram size=0x8000\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 79fdab65..4ca97c19 100644 --- a/icarus/icarus.cpp +++ b/icarus/icarus.cpp @@ -22,6 +22,7 @@ Settings settings; #include "heuristics/super-famicom.cpp" #include "heuristics/master-system.cpp" #include "heuristics/mega-drive.cpp" +#include "heuristics/pc-engine.cpp" #include "heuristics/game-boy.cpp" #include "heuristics/game-boy-advance.cpp" #include "heuristics/game-gear.cpp" @@ -35,6 +36,7 @@ Settings settings; #include "core/super-famicom.cpp" #include "core/master-system.cpp" #include "core/mega-drive.cpp" +#include "core/pc-engine.cpp" #include "core/game-boy.cpp" #include "core/game-boy-color.cpp" #include "core/game-boy-advance.cpp" @@ -77,6 +79,7 @@ auto nall::main(string_vector args) -> void { "*.sfc:*.smc:" "*.ms:*.sms:" "*.md:*.smd:*.gen:" + "*.pce:" "*.gb:" "*.gbc:" "*.gba:" diff --git a/icarus/ui/scan-dialog.cpp b/icarus/ui/scan-dialog.cpp index 3015194f..934065f6 100644 --- a/icarus/ui/scan-dialog.cpp +++ b/icarus/ui/scan-dialog.cpp @@ -102,9 +102,13 @@ auto ScanDialog::gamePakType(const string& type) -> bool { return type == ".sys" || type == ".fc" || type == ".sfc" + || type == ".ms" + || type == ".md" + || type == ".pce" || type == ".gb" || type == ".gbc" || type == ".gba" + || type == ".gg" || type == ".bs" || type == ".st"; } @@ -113,9 +117,13 @@ auto ScanDialog::gameRomType(const string& type) -> bool { return type == ".zip" || type == ".fc" || type == ".nes" || type == ".sfc" || type == ".smc" + || type == ".ms" || type == ".sms" + || type == ".md" || type == ".smd" || type == ".gen" + || type == ".pce" || type == ".gb" || type == ".gbc" || type == ".gba" + || type == ".gg" || type == ".bs" || type == ".st"; }