Commit fd3546b7 authored by Tim Allen's avatar Tim Allen

Update to v106r129 release.

byuu says:

fixed the pitch/speed of the VRC7, and added a 2280hz lowpass filter to match
hardware a bit more (it's kind of hacked in, but it works.) forgot to set the
echo buffer disable flag on the SNES DSP, which was breaking SNES games, so
that's fixed. started emulating the famicom disk system. so far just the icarus
BIOS + FDS disk imports (won't handle multi-disk merged ROMs yet), the basic PCB
mapping of PRG-ROM+RAM and CHR-RAM, some MMIO registers, FDS timer IRQs, and the
GUI connections to connect a disk (marked hot swappable.) I need more info on
the low-level details of how the ram adaper talks to the disk drive to proceed
further. But it's a start.
parent dcf1381e
Pipeline #55112925 passed with stage
in 23 minutes and 14 seconds
......@@ -37,7 +37,7 @@ using namespace nall;
namespace higan {
static const string Name = "higan";
static const string Version = "106.128";
static const string Version = "106.129";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org/";
......
......@@ -2,6 +2,7 @@ components += mos6502 ym2149 ym2413
objects += fc-interface fc-system fc-controller
objects += fc-memory fc-cartridge fc-cpu fc-apu fc-ppu
objects += fc-fds
obj/fc-interface.o: fc/interface/interface.cpp
obj/fc-system.o: fc/system/system.cpp
......@@ -11,3 +12,4 @@ obj/fc-cartridge.o: fc/cartridge/cartridge.cpp
obj/fc-cpu.o: fc/cpu/cpu.cpp
obj/fc-apu.o: fc/apu/apu.cpp
obj/fc-ppu.o: fc/ppu/ppu.cpp
obj/fc-fds.o: fc/fds/fds.cpp
#include "bandai-fcg.cpp"
#include "hvc-fmr.cpp"
#include "konami-vrc1.cpp"
#include "konami-vrc2.cpp"
#include "konami-vrc3.cpp"
......@@ -137,6 +138,8 @@ auto Board::load(string metadata) -> Board* {
if(type == "BANDAI-FCG" ) return new BandaiFCG(document);
if(type == "HVC-FMR" ) return new HVC_FMR(document);
if(type == "KONAMI-VRC-1") return new KonamiVRC1(document);
if(type == "KONAMI-VRC-2") return new KonamiVRC2(document);
if(type == "KONAMI-VRC-3") return new KonamiVRC3(document);
......
//HVC-FMR-03
//HVC-FMR-04
struct HVC_FMR : Board {
HVC_FMR(Markup::Node& document) : Board(document) {
fds.present = 1;
}
auto readPRG(uint address) -> uint8 {
if(address >= 0x6000 && address <= 0xdfff) return prgram.read(address);
if(address >= 0xe000 && address <= 0xffff) return prgrom.read(address);
return cpu.mdr();
}
auto writePRG(uint address, uint8 data) -> void {
if(address >= 0x6000 && address <= 0xdfff) return prgram.write(address, data);
}
auto readCHR(uint address) -> uint8 {
if(address & 0x2000) {
if(fds.mirroring() == 0) address = ((address & 0x8000) >> 1) | (address & 0x03ff);
return ppu.readCIRAM(address & 0x07ff);
}
return chrram.read(address);
}
auto writeCHR(uint address, uint8 data) -> void {
if(address & 0x2000) {
if(fds.mirroring() == 0) address = ((address & 0x8000) >> 1) | (address & 0x03ff);
return ppu.writeCIRAM(address & 0x07ff, data);
}
return chrram.write(address, data);
}
auto power() -> void {
}
auto serialize(serializer& s) -> void {
Board::serialize(s);
}
};
......@@ -18,6 +18,14 @@ auto Cartridge::load(Node::Object parent, Node::Object from) -> void {
parent->append(port);
}
auto Cartridge::unload() -> void {
if(fds.present) {
fds.unload();
fds.present = 0;
}
port = {};
}
auto Cartridge::connect(Node::Peripheral with) -> void {
node = Node::Peripheral::create(interface->name());
node->load(with);
......@@ -29,6 +37,9 @@ auto Cartridge::connect(Node::Peripheral with) -> void {
Board::load(information.metadata); //this call will set Cartridge::board if successful
power();
if(fds.present) {
fds.load(node, with);
}
port->prepend(node);
}
......
......@@ -11,6 +11,7 @@ struct Cartridge : Thread {
//cartridge.cpp
auto load(Node::Object, Node::Object) -> void;
auto unload() -> void;
auto connect(Node::Peripheral) -> void;
auto disconnect() -> void;
......
//Konami VRC7
struct VRC7 : YM2413, Chip {
DSP::IIR::OnePole onePole;
VRC7(Board& board) : Chip(board) {
}
......@@ -30,9 +32,11 @@ struct VRC7 : YM2413, Chip {
}
cpu.irqLine(irqLine);
if(!++divider) {
if(++divider == 36) {
divider = 0;
double sample = 0.0;
if(!disableFM) sample = YM2413::clock();
sample = onePole.process(sample);
apu.setSample(sample * 32767.0);
}
......@@ -55,10 +59,14 @@ struct VRC7 : YM2413, Chip {
case 0xd000: chrBank[6] = data; break;
case 0xd010: chrBank[7] = data; break;
case 0xe000:
if(disableFM && !data.bit(6)) {
YM2413::power(1);
double inputFrequency = system.frequency() / apu.rate() / 36.0;
onePole.reset(DSP::IIR::OnePole::Type::LowPass, 2280.0, inputFrequency);
}
mirror = data.bits(0,1);
disableFM = data.bit(6);
ramWritable = data.bit(7);
if(disableFM) YM2413::power(1);
break;
case 0xe010:
......@@ -110,11 +118,11 @@ struct VRC7 : YM2413, Chip {
}
auto power() -> void {
YM2413::power(1);
for(auto& n : prgBank) n = 0;
for(auto& n : chrBank) n = 0;
mirror = 0;
disableFM = 1;
ramWritable = 1;
irqLatch = 0;
irqMode = 0;
......@@ -124,6 +132,8 @@ struct VRC7 : YM2413, Chip {
irqCounter = 0;
irqScalar = 0;
irqLine = 0;
divider = 0;
}
auto serialize(serializer& s) -> void {
......@@ -162,5 +172,5 @@ struct VRC7 : YM2413, Chip {
int irqScalar;
bool irqLine;
uint5 divider;
uint6 divider;
};
......@@ -13,6 +13,7 @@ Controller::Controller() {
}
Controller::~Controller() {
cpu.peripherals.removeWhere() == this;
Thread::destroy();
}
......
......@@ -45,6 +45,7 @@ namespace higan::Famicom {
#include <fc/cpu/cpu.hpp>
#include <fc/apu/apu.hpp>
#include <fc/ppu/ppu.hpp>
#include <fc/fds/fds.hpp>
}
#include <fc/interface/interface.hpp>
#include <fc/fc.hpp>
namespace higan::Famicom {
FDS fds;
#include "io.cpp"
#include "serialization.cpp"
auto FDS::load(Node::Object parent, Node::Object from) -> void {
port = Node::Port::create("Disk Slot", "Disk");
port->hotSwappable = true;
port->allocate = [&] { return Node::Peripheral::create("Famicom Disk"); };
port->attach = [&](auto node) { connect(node); };
port->detach = [&](auto node) { disconnect(); };
if(from = Node::load(port, from)) {
if(auto node = from->find<Node::Peripheral>(0)) port->connect(node);
}
parent->append(port);
Thread::create(system.frequency(), [&] {
while(true) scheduler.synchronize(), main();
});
cpu.peripherals.append(this);
power();
}
auto FDS::unload() -> void {
cpu.peripherals.removeWhere() == this;
Thread::destroy();
port = {};
sideA.reset();
sideB.reset();
}
auto FDS::connect(Node::Peripheral with) -> void {
node = Node::Peripheral::create("Famicom Disk");
node->load(with);
information = {};
if(auto fp = platform->open(node, "metadata.bml", File::Read, File::Required)) {
information.metadata = fp->reads();
} else return;
auto document = BML::unserialize(information.metadata);
if(auto fp = platform->open(node, "program.disk", File::Read, File::Required)) {
if(fp->size() >= 65500 * 1) {
sideA.allocate(65500);
fp->read(sideA.data(), sideA.size());
}
if(fp->size() >= 65500 * 2) {
sideB.allocate(65500);
fp->read(sideB.data(), sideB.size());
}
}
port->prepend(node);
}
auto FDS::disconnect() -> void {
if(!node) return;
node = {};
}
auto FDS::main() -> void {
if(irq.onTimer && !--irq.period) {
if(irq.repeat) irq.period = irq.counter;
irq.onTimer = irq.repeat;
irq.triggered = 1;
cpu.irqLine(1);
}
step(1);
}
auto FDS::step(uint clocks) -> void {
Thread::step(clocks);
synchronize(cpu);
}
auto FDS::power() -> void {
irq = {};
io = {};
}
}
//Famicom Disk System
struct FDS : Thread {
Node::Port port;
Node::Peripheral node;
uint1 present;
Memory::Writable<uint8> sideA;
Memory::Writable<uint8> sideB;
inline auto mirroring() const -> bool { return io.mirroring; }
//fds.cpp
auto load(Node::Object, Node::Object) -> void;
auto unload() -> void;
auto connect(Node::Peripheral) -> void;
auto disconnect() -> void;
auto save() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto power() -> void;
//io.cpp
auto read(uint16 address) -> uint8;
auto write(uint16 address, uint8 data) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
private:
struct Information {
string metadata;
} information;
struct IRQ {
uint16 counter;
uint16 period;
uint1 repeat;
uint1 onTimer;
uint1 onTransfer;
uint1 triggered;
} irq;
struct IO {
uint1 enableDisk;
uint1 enableAudio;
uint1 enableMotor;
uint1 accessMode; //0 = write, 1 = read
uint1 mirroring = 1; //0 = vertical, 1 = horizontal
uint1 crcControl;
} io;
};
extern FDS fds;
auto FDS::read(uint16 address) -> uint8 {
uint8 data = 0x00;
switch(0x4030 | (uint4)address) {
case 0x4030:
data.bit(0) = irq.triggered;
irq.triggered = 0;
cpu.irqLine(0);
return data;
case 0x4032:
data.bit(0) = !(bool)sideA; //0 = disk inserted, 1 = disk not inserted
data.bit(1) = !(bool)sideA; //0 = disk ready, 1 = disk not ready
data.bit(2) = 0; //0 = not write protected, 1 = write protected
return data;
case 0x4033:
data.bit(7) = 0; //0 = battery good, 1 = battery voltage low
return data;
}
return data;
}
auto FDS::write(uint16 address, uint8 data) -> void {
switch(0x4020 | (uint4)address) {
case 0x4020:
irq.period.byte(0) = data;
return;
case 0x4021:
irq.period.byte(1) = data;
return;
case 0x4022:
if(!io.enableDisk) return;
irq.repeat = data.bit(0);
irq.onTimer = data.bit(1);
if(irq.onTimer) {
irq.counter = irq.period;
} else {
irq.triggered = 0;
cpu.irqLine(0);
}
return;
case 0x4023:
io.enableDisk = data.bit(0);
io.enableAudio = data.bit(1);
if(!io.enableDisk) {
irq.triggered = 0;
cpu.irqLine(0);
}
return;
case 0x4025:
io.enableMotor = data.bit(0);
bool reset = data.bit(1);
io.accessMode = data.bit(2);
io.mirroring = data.bit(3);
io.crcControl = data.bit(5);
irq.onTransfer = data.bit(7);
return;
}
}
auto FDS::serialize(serializer& s) -> void {
Thread::serialize(s);
}
......@@ -9,6 +9,7 @@ Bus bus;
//$2000-2007 = PPU
//$2008-3fff = PPU (mirror)
//$4000-4017 = APU + I/O
//$4020-403f = FDS
//$4018-ffff = Cartridge
auto Bus::read(uint16 addr) -> uint8 {
......@@ -16,6 +17,7 @@ auto Bus::read(uint16 addr) -> uint8 {
if(addr <= 0x1fff) data = cpu.readRAM(addr);
else if(addr <= 0x3fff) data = ppu.readIO(addr);
else if(addr <= 0x4017) data = cpu.readIO(addr);
else if(fds.present && (addr & 0xfff0) == 0x4030) return fds.read(addr);
if(cheat) {
if(auto result = cheat.find(addr, data)) return result();
......@@ -29,6 +31,7 @@ auto Bus::write(uint16 addr, uint8 data) -> void {
if(addr <= 0x1fff) return cpu.writeRAM(addr, data);
if(addr <= 0x3fff) return ppu.writeIO(addr, data);
if(addr <= 0x4017) return cpu.writeIO(addr, data);
if(fds.present && (addr & 0xfff0) == 0x4020) return fds.write(addr, data);
}
}
......@@ -40,6 +40,7 @@ auto System::serializeAll(serializer& s) -> void {
cpu.serialize(s);
apu.serialize(s);
ppu.serialize(s);
if(fds.present) fds.serialize(s);
controllerPort1.serialize(s);
controllerPort2.serialize(s);
}
......
......@@ -62,7 +62,7 @@ auto System::load(Node::Object from) -> void {
auto System::unload() -> void {
if(!node) return;
save();
cartridge.port = {};
cartridge.unload();
controllerPort1.port = {};
controllerPort2.port = {};
node = {};
......@@ -85,6 +85,7 @@ auto System::power(bool reset) -> void {
cpu.power(reset);
apu.power(reset);
ppu.power(reset);
for(auto peripheral : cpu.peripherals) scheduler.append(*peripheral);
scheduler.primary(cpu);
serializeInit();
......
......@@ -5,8 +5,8 @@ Gamepad::Gamepad(Node::Port parent, Node::Peripheral with) {
down = Node::append<Node::Button>(node, with, "Down");
left = Node::append<Node::Button>(node, with, "Left");
right = Node::append<Node::Button>(node, with, "Right");
b = Node::append<Node::Button>(node, with, "B");
a = Node::append<Node::Button>(node, with, "A");
b = Node::append<Node::Button>(node, with, "B");
parent->prepend(node);
}
......@@ -15,8 +15,8 @@ auto Gamepad::read() -> uint6 {
platform->input(down);
platform->input(left);
platform->input(right);
platform->input(b);
platform->input(a);
platform->input(b);
if(!(up->value & down->value)) {
yHold = 0, upLatch = up->value, downLatch = down->value;
......@@ -35,7 +35,7 @@ auto Gamepad::read() -> uint6 {
data.bit(1) = !downLatch;
data.bit(2) = !leftLatch;
data.bit(3) = !rightLatch;
data.bit(4) = !b->value;
data.bit(5) = !a->value;
data.bit(4) = !a->value;
data.bit(5) = !b->value;
return data;
}
......@@ -3,8 +3,8 @@ struct Gamepad : Controller {
Node::Button down;
Node::Button left;
Node::Button right;
Node::Button b;
Node::Button a;
Node::Button b;
Gamepad(Node::Port, Node::Peripheral);
......
......@@ -45,7 +45,7 @@ private:
int16 history[2][8];
uint8 bank;
uint4 delay;
uint1 readonly;
uint1 readonly = 1;
int17 input[2];
int17 output[2];
......
......@@ -2,6 +2,7 @@ Icarus::Icarus() {
Database::BSMemory = BML::unserialize(string::read(locate("Database/BS Memory.bml")));
Database::ColecoVision = BML::unserialize(string::read(locate("Database/ColecoVision.bml")));
Database::Famicom = BML::unserialize(string::read(locate("Database/Famicom.bml")));
Database::FamicomDisk = BML::unserialize(string::read(locate("Database/Famicom Disk.bml")));
Database::GameBoy = BML::unserialize(string::read(locate("Database/Game Boy.bml")));
Database::GameBoyAdvance = BML::unserialize(string::read(locate("Database/Game Boy Advance.bml")));
Database::GameBoyColor = BML::unserialize(string::read(locate("Database/Game Boy Color.bml")));
......@@ -47,6 +48,7 @@ auto Icarus::manifest(string system, string location) -> string {
if(system == "BS Memory" ) return bsMemoryManifest(location);
if(system == "ColecoVision" ) return colecoVisionManifest(location);
if(system == "Famicom" ) return famicomManifest(location);
if(system == "Famicom Disk" ) return famicomDiskManifest(location);
if(system == "Game Boy" ) return gameBoyManifest(location);
if(system == "Game Boy Advance" ) return gameBoyAdvanceManifest(location);
if(system == "Game Boy Color" ) return gameBoyColorManifest(location);
......@@ -97,6 +99,7 @@ auto Icarus::import(string system, string location) -> string {
if(system == "BS Memory" ) return bsMemoryImport(buffer, location);
if(system == "ColecoVision" ) return colecoVisionImport(buffer, location);
if(system == "Famicom" ) return famicomImport(buffer, location);
if(system == "Famicom Disk" ) return famicomDiskImport(buffer, location);
if(system == "Game Boy" ) return gameBoyImport(buffer, location);
if(system == "Game Boy Advance" ) return gameBoyAdvanceImport(buffer, location);
if(system == "Game Boy Color" ) return gameBoyColorImport(buffer, location);
......
......@@ -51,6 +51,11 @@ struct Icarus {
auto famicomManifest(vector<uint8_t>& buffer, string location) -> string;
auto famicomImport(vector<uint8_t>& buffer, string location) -> string;
//famicom-disk.cpp
auto famicomDiskManifest(string location) -> string;
auto famicomDiskManifest(vector<uint8_t>& buffer, string location) -> string;
auto famicomDiskImport(vector<uint8_t>& buffer, string location) -> string;
//game-boy.cpp
auto gameBoyManifest(string location) -> string;
auto gameBoyManifest(vector<uint8_t>& buffer, string location) -> string;
......@@ -150,6 +155,7 @@ namespace Database {
Markup::Node BSMemory;
Markup::Node ColecoVision;
Markup::Node Famicom;
Markup::Node FamicomDisk;
Markup::Node GameBoy;
Markup::Node GameBoyAdvance;
Markup::Node GameBoyColor;
......
auto Icarus::famicomDiskManifest(string location) -> string {
vector<uint8_t> buffer;
concatenate(buffer, {location, "program.disk"});
return famicomDiskManifest(buffer, location);
}
auto Icarus::famicomDiskManifest(vector<uint8_t>& buffer, string location) -> string {
if(settings.useDatabase) {
auto digest = Hash::SHA256(buffer).digest();
for(auto game : Database::FamicomDisk.find("game")) {
if(game["sha256"].text() == digest) return BML::serialize(game);
}
}
if(settings.useHeuristics) {
Heuristics::FamicomDisk game{buffer, location};
if(auto manifest = game.manifest()) return manifest;
}
return {};
}
auto Icarus::famicomDiskImport(vector<uint8_t>& buffer, string location) -> string {
auto name = Location::prefix(location);
auto source = Location::path(location);
string target{settings.famicomDisk, name, "/"};
auto manifest = famicomDiskManifest(buffer, location);
if(!manifest) return failure("failed to parse ROM image");
if(!create(target)) return failure("library path unwritable");
if(settings.createManifests) write({target, "manifest.bml"}, manifest);
write({target, "program.disk"}, buffer);
return success(target);
}
......@@ -38,7 +38,7 @@ auto Icarus::famicomImport(vector<uint8_t>& buffer, string location) -> string {
if(settings.createManifests) write({target, "manifest.bml"}, manifest);