Update to v101r30 release.

byuu says:

Changelog:

  - SMS: added cartridge ROM/RAM mirroring (fixes Alex Kidd)
  - SMS: fixed 8x16 sprite mode (fixes Wonder Boy, Ys graphics)
  - Z80: emulated "ex (sp),hl" instruction
  - Z80: fixed INx NF (should be set instead of cleared)
  - Z80: fixed loop condition check for CPxR, INxR, LDxR, OTxR (fixes
    walking in Wonder Boy)
  - SFC: removed Debugger and sfc/debugger.hpp
  - icarus: connected MS, GG, MD importing to the scan dialog
  - PCE: added emulation skeleton to higan and icarus

At this point, Master System games are fairly highly compatible, sans
audio. Game Gear games are running, but I need to crop the resolution
and support the higher color palette that they can utilize. It's really
something else the way they handled the resolution shrink on that thing.

The last change is obviously going to be the biggest news.

I'm very well aware it's not an ideal time to start on a new emulation
core, with the MS and MD cores only just now coming to life with no
audio support.

But, for whatever reason, my heart's really set on working on the PC
Engine. I wanted to write the final higan skeleton core, and get things
ready so that whenever I'm in the mood to work on the PCE, I can do so.

The skeleton is far and away the most tedious and obnoxious part of the
emulator development, because it's basically all just lots of
boilerplate templated code, lots of new files to create, etc.

I really don't know how things are going to proceed ... but I can say
with 99.9% certainty that this will be the final brand new core ever
added to higan -- at least one written by me, that is. This was
basically the last system from my childhood that I ever cared about.
It's the last 2D system with games that I really enjoy playing. No other
system is worth dividing my efforts and reducing the quality and amount
of time to work on the systems I have.

In the future, there will be potential for FDS, Mega CD and PCE-CD
support. But those will all be add-ons, and they'll all be really
difficult and challenge the entire design of higan's UI (it's entirely
cartridge-driven at this time.) None of them will be entirely new cores
like this one.
This commit is contained in:
Tim Allen 2017-01-12 07:27:30 +11:00
parent 79c83ade70
commit 0ad70a30f8
43 changed files with 818 additions and 70 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

14
higan/pce/GNUmakefile Normal file
View File

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

View File

@ -0,0 +1,93 @@
#include <pce/pce.hpp>
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;
}
}

View File

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

View File

@ -0,0 +1,26 @@
#include <pce/pce.hpp>
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);
}
}

View File

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

View File

@ -0,0 +1,6 @@
Gamepad::Gamepad() {
}
auto Gamepad::readData() -> uint8 {
return 0x00;
}

View File

@ -0,0 +1,9 @@
struct Gamepad : Controller {
enum : uint {
Up, Down, Left, Right, Two, One, Select, Run,
};
Gamepad();
auto readData() -> uint8 override;
};

27
higan/pce/cpu/cpu.cpp Normal file
View File

@ -0,0 +1,27 @@
#include <pce/pce.hpp>
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);
}
}

13
higan/pce/cpu/cpu.hpp Normal file
View File

@ -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<Thread*> peripherals;
};
extern CPU cpu;

View File

@ -0,0 +1,127 @@
#include <pce/pce.hpp>
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;
}
}

View File

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

38
higan/pce/pce.hpp Normal file
View File

@ -0,0 +1,38 @@
#pragma once
//license: GPLv3
//started: 2017-01-11
#include <emulator/emulator.hpp>
#include <emulator/thread.hpp>
#include <emulator/scheduler.hpp>
#include <processor/huc6280/huc6280.hpp>
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 <pce/controller/controller.hpp>
#include <pce/cpu/cpu.hpp>
#include <pce/vdc/vdc.hpp>
#include <pce/psg/psg.hpp>
#include <pce/system/system.hpp>
#include <pce/cartridge/cartridge.hpp>
}
#include <pce/interface/interface.hpp>

26
higan/pce/psg/psg.cpp Normal file
View File

@ -0,0 +1,26 @@
#include <pce/pce.hpp>
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);
}
}

13
higan/pce/psg/psg.hpp Normal file
View File

@ -0,0 +1,13 @@
//Programmable Sound Generator
struct PSG : Thread {
shared_pointer<Emulator::Stream> stream;
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto power() -> void;
};
extern PSG psg;

View File

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

View File

@ -0,0 +1,54 @@
#include <pce/pce.hpp>
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();
}
}

View File

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

30
higan/pce/vdc/vdc.cpp Normal file
View File

@ -0,0 +1,30 @@
#include <pce/pce.hpp>
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;
}
}

15
higan/pce/vdc/vdc.hpp Normal file
View File

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

View File

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

View File

@ -0,0 +1,13 @@
#include <processor/processor.hpp>
#include "huc6280.hpp"
namespace Processor {
auto HuC6280::power() -> void {
}
auto HuC6280::instruction() -> void {
step(1);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,40 +0,0 @@
#pragma once
namespace SuperFamicom {
struct Debugger {
struct CPU {
function<void (uint24, uint8)> read;
function<void (uint24, uint8)> write;
function<void (uint24)> execute;
function<void ()> nmi;
function<void ()> irq;
} cpu;
struct SMP {
function<void (uint16, uint8)> read;
function<void (uint16, uint8)> write;
function<void (uint16)> execute;
} smp;
struct PPU {
struct VRAM {
function<void (uint17, uint8)> read;
function<void (uint17, uint8)> write;
} vram;
struct OAM {
function<void (uint10, uint8)> read;
function<void (uint10, uint8)> write;
} oam;
struct CGRAM {
function<void (uint9, uint8)> read;
function<void (uint9, uint8)> write;
} cgram;
} ppu;
};
extern Debugger debugger;
}

View File

@ -4,7 +4,6 @@ namespace SuperFamicom {
Interface* interface = nullptr;
Settings settings;
Debugger debugger;
Interface::Interface() {
interface = this;

View File

@ -1,5 +1,3 @@
#include <sfc/debugger.hpp>
namespace SuperFamicom {
struct ID {

View File

@ -0,0 +1 @@
system name:PC Engine

View File

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

View File

@ -3,6 +3,7 @@
#include <sfc/interface/interface.hpp>
#include <ms/interface/interface.hpp>
#include <md/interface/interface.hpp>
#include <pce/interface/interface.hpp>
#include <gb/interface/interface.hpp>
#include <gba/interface/interface.hpp>
#include <ws/interface/interface.hpp>
@ -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);

View File

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

View File

@ -32,6 +32,11 @@ struct Icarus {
auto megaDriveManifest(vector<uint8_t>& buffer, string location) -> string;
auto megaDriveImport(vector<uint8_t>& buffer, string location) -> string;
//pc-engine.cpp
auto pcEngineManifest(string location) -> string;
auto pcEngineManifest(vector<uint8_t>& buffer, string location) -> string;
auto pcEngineImport(vector<uint8_t>& buffer, string location) -> string;
//game-boy.cpp
auto gameBoyManifest(string location) -> string;
auto gameBoyManifest(vector<uint8_t>& 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;

45
icarus/core/pc-engine.cpp Normal file
View File

@ -0,0 +1,45 @@
auto Icarus::pcEngineManifest(string location) -> string {
vector<uint8_t> buffer;
concatenate(buffer, {location, "program.rom"});
return pcEngineManifest(buffer, location);
}
auto Icarus::pcEngineManifest(vector<uint8_t>& buffer, string location) -> string {
string manifest;
if(settings["icarus/UseDatabase"].boolean() && !manifest) {
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
for(auto node : database.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<uint8_t>& 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);
}

View File

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

View File

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

View File

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