Update to v100r02 release.

byuu says:

Sigh ... I'm really not a good person. I'm inherently selfish.

My responsibility and obligation right now is to work on loki, and
then on the Tengai Makyou Zero translation, and then on improving the
Famicom emulation.

And yet ... it's not what I really want to do. That shouldn't matter;
I should work on my responsibilities first.

Instead, I'm going to be a greedy, self-centered asshole, and work on
what I really want to instead.

I'm really sorry, guys. I'm sure this will make a few people happy,
and probably upset even more people.

I'm also making zero guarantees that this ever gets finished. As always,
I wish I could keep these things secret, so if I fail / give up, I could
just drop it with no shame. But I would have to cut everyone out of the
WIP process completely to make it happen. So, here goes ...

This WIP adds the initial skeleton for Sega Mega Drive / Genesis
emulation. God help us.

(minor note: apparently the new extension for Mega Drive games is .md,
neat. That's what I chose for the folders too. I thought it was .smd,
so that'll be fixed in icarus for the next WIP.)

(aside: this is why I wanted to get v100 out. I didn't want this code in
a skeleton state in v100's source. Nor did I want really broken emulation,
which the first release is sure to be, tarring said release.)

...

So, basically, I've been ruminating on the legacy I want to leave behind
with higan. 3D systems are just plain out. I'm never going to support
them. They're too complex for my abilities, and they would run too slowly
with my design style. I'm not willing to compromise my design ideals. And
I would never want to play a 3D game system at native 240p/480i resolution
... but 1080p+ upscaling is not accurate, so that's a conflict I want
to avoid entirely. It's also never going to emulate computer systems
(X68K, PC-98, FM-Towns, etc) because holy shit that would completely
destroy me. It's also never going emulate arcade machines.

So I think of higan as a collection of 2D emulators for consoles
and handhelds. I've gone over every major 2D gaming system there is,
looking for ones with games I actually care about and enjoy. And I
basically have five of those systems supported already. Looking at the
remaining list, I see only three systems left that I have any interest
in whatsoever: PC-Engine, Master System, Mega Drive. Again, I'm not in
any way committing to emulating any of these, but ... if I had all of
those in higan, I think I'd be content to really, truly, finally stop
writing more emulators for the rest of my life.

And so I decided to tackle the most difficult system first. If I'm
successful, the Z80 core should cover a lot of the work on the SMS. And
the HuC6280 should land somewhere between the NES and SNES in terms of
difficulty ... closer to the NES.

The systems that just don't appeal to me at all, which I will never touch,
include, but are not limited to:
* Atari 2600/5200/7800
* Lynx
* Jaguar
* Vectrex
* Colecovision
* Commodore 64
* Neo-Geo
* Neo-Geo Pocket / Color
* Virtual Boy
* Super A'can
* 32X
* CD-i
* etc, etc, etc.

And really, even if something were mildly interesting in there ... we
have to stop. I can't scale infinitely. I'm already way past my limit,
but I'm doing this anyway. Too many cores bloats everything and kills
quality on everything. I don't want higan to become MESS v2.

I don't know what I'll do about the Famicom Disk System, PC-Engine CD,
and Mega CD. I don't think I'll be able to achieve 60fps emulating the
Mega CD, even if I tried to.

I don't know what's going to happen here with even the Mega Drive. Maybe
I'll get driven crazy with the documentation and quit. Maybe it'll end
up being too complicated and I'll quit. Maybe the emulation will end up
way too slow and I'll give up. Maybe it'll take me seven years to get
any games playable at all. Maybe Steve Snake, AamirM and Mike Pavone
will pool money to hire a hitman to come after me. Who knows.

But this is what I want to do, so ... here goes nothing.
This commit is contained in:
Tim Allen 2016-07-09 14:21:37 +10:00
parent 88c79e56a0
commit 3dd1aa9c1b
32 changed files with 526 additions and 2 deletions

View File

@ -11,7 +11,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "100.01";
static const string Version = "100.02";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

14
higan/md/GNUmakefile Normal file
View File

@ -0,0 +1,14 @@
processors += m68k z80
objects += md-interface
objects += md-cpu md-apu md-vdp md-ym2612
objects += md-system md-scheduler md-cartridge
obj/md-interface.o: md/interface/interface.cpp $(call rwildcard,md/interface)
obj/md-cpu.o: md/cpu/cpu.cpp $(call rwildcard,md/cpu)
obj/md-apu.o: md/apu/apu.cpp $(call rwildcard,md/apu)
obj/md-vdp.o: md/vdp/vdp.cpp $(call rwildcard,md/vdp)
obj/md-ym2612.o: md/ym2612/ym2612.cpp $(call rwildcard,md/ym2612)
obj/md-system.o: md/system/system.cpp $(call rwildcard,md/system)
obj/md-scheduler.o: md/scheduler/scheduler.cpp $(call rwildcard,md/scheduler)
obj/md-cartridge.o: md/cartridge/cartridge.cpp $(call rwildcard,md/cartridge)

7
higan/md/apu/apu.cpp Normal file
View File

@ -0,0 +1,7 @@
#include <md/md.hpp>
namespace MegaDrive {
APU apu;
}

4
higan/md/apu/apu.hpp Normal file
View File

@ -0,0 +1,4 @@
struct APU : Processor::Z80, Thread {
};
extern APU apu;

View File

@ -0,0 +1,36 @@
#include <md/md.hpp>
namespace MegaDrive {
Cartridge cartridge;
auto Cartridge::load() -> bool {
information = Information();
if(auto pathID = interface->load(ID::MegaDrive, "Mega Drive", "md")) {
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();
return false;
}
auto Cartridge::save() -> void {
}
auto Cartridge::unload() -> void {
}
auto Cartridge::power() -> void {
}
auto Cartridge::reset() -> void {
}
}

View File

@ -0,0 +1,21 @@
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;
auto reset() -> void;
struct Information {
uint pathID = 0;
string sha256;
string manifest;
string title;
} information;
};
extern Cartridge cartridge;

7
higan/md/cpu/cpu.cpp Normal file
View File

@ -0,0 +1,7 @@
#include <md/md.hpp>
namespace MegaDrive {
CPU cpu;
}

4
higan/md/cpu/cpu.hpp Normal file
View File

@ -0,0 +1,4 @@
struct CPU : Processor::M68K, Thread {
};
extern CPU cpu;

View File

@ -0,0 +1,128 @@
#include <md/md.hpp>
namespace MegaDrive {
Interface* interface = nullptr;
Settings settings;
Interface::Interface() {
interface = this;
information.manufacturer = "Sega";
information.name = "Mega Drive";
information.width = 1280;
information.height = 480;
information.overscan = true;
information.aspectRatio = 4.0 / 3.0;
information.resettable = true;
information.capability.states = false;
information.capability.cheats = false;
media.append({ID::MegaDrive, "Mega Drive", "md"});
Port controllerPort1{ID::Port::Controller1, "Controller Port 1"};
Port controllerPort2{ID::Port::Controller2, "Controller Port 2"};
{ 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, "A" });
device.inputs.append({0, "B" });
device.inputs.append({0, "C" });
device.inputs.append({0, "X" });
device.inputs.append({0, "Y" });
device.inputs.append({0, "Z" });
device.inputs.append({0, "Start"});
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
}
ports.append(move(controllerPort1));
ports.append(move(controllerPort2));
}
auto Interface::manifest() -> string {
return cartridge.manifest();
}
auto Interface::title() -> string {
return cartridge.title();
}
auto Interface::videoFrequency() -> double {
return 60.0;
}
auto Interface::videoColors() -> uint32 {
return 1 << 9;
}
auto Interface::videoColor(uint32 color) -> uint64 {
uint B = color.bits(0,2);
uint G = color.bits(3,5);
uint R = color.bits(6,8);
uint64 r = image::normalize(R, 3, 16);
uint64 g = image::normalize(G, 3, 16);
uint64 b = image::normalize(B, 3, 16);
return r << 32 | g << 16 | b << 0;
}
auto Interface::audioFrequency() -> double {
return 52'000.0;
}
auto Interface::loaded() -> bool {
return system.loaded();
}
auto Interface::load(uint id) -> bool {
system.load();
return false;
}
auto Interface::save() -> void {
system.save();
}
auto Interface::unload() -> void {
system.unload();
}
auto Interface::power() -> void {
system.power();
}
auto Interface::reset() -> void {
system.reset();
}
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,54 @@
namespace MegaDrive {
struct ID {
enum : uint {
System,
MegaDrive,
};
struct Port { enum : uint {
Controller1,
Controller2,
};};
struct Device { enum : uint {
Gamepad,
};};
};
struct Interface : Emulator::Interface {
using Emulator::Interface::load;
Interface();
auto manifest() -> string override;
auto title() -> string 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 power() -> void override;
auto reset() -> 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 {
};
extern Interface* interface;
extern Settings settings;
}

45
higan/md/md.hpp Normal file
View File

@ -0,0 +1,45 @@
#pragma once
//license: GPLv3
//started: 2016-07-08
#include <emulator/emulator.hpp>
#include <processor/m68k/m68k.hpp>
#include <processor/z80/z80.hpp>
namespace MegaDrive {
using File = Emulator::File;
struct Thread {
~Thread() {
if(thread) co_delete(thread);
}
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
if(thread) co_delete(thread);
thread = co_create(65'536 * sizeof(void*), entrypoint);
this->frequency = frequency;
clock = 0;
}
auto serialize(serializer& s) -> void {
s.integer(frequency);
s.integer(clock);
}
cothread_t thread = nullptr;
uint frequency = 0;
int64 clock = 0;
};
#include <md/cpu/cpu.hpp>
#include <md/apu/apu.hpp>
#include <md/vdp/vdp.hpp>
#include <md/ym2612/ym2612.hpp>
#include <md/system/system.hpp>
#include <md/scheduler/scheduler.hpp>
#include <md/cartridge/cartridge.hpp>
}
#include <md/interface/interface.hpp>

View File

@ -0,0 +1,7 @@
#include <md/md.hpp>
namespace MegaDrive {
Scheduler scheduler;
}

View File

@ -0,0 +1,4 @@
struct Scheduler {
};
extern Scheduler scheduler;

View File

@ -0,0 +1,37 @@
#include <md/md.hpp>
namespace MegaDrive {
System system;
auto System::run() -> void {
}
auto System::load() -> bool {
information = 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;
return information.loaded = true;
}
auto System::save() -> void {
cartridge.save();
}
auto System::unload() -> void {
cartridge.unload();
}
auto System::power() -> void {
cartridge.power();
reset();
}
auto System::reset() -> void {
cartridge.reset();
}
}

View File

@ -0,0 +1,18 @@
struct System {
auto loaded() const { return information.manifest; }
auto run() -> void;
auto load() -> bool;
auto save() -> void;
auto unload() -> void;
auto power() -> void;
auto reset() -> void;
struct Information {
bool loaded = false;
string manifest;
} information;
};
extern System system;

7
higan/md/vdp/vdp.cpp Normal file
View File

@ -0,0 +1,7 @@
#include <md/md.hpp>
namespace MegaDrive {
VDP vdp;
}

4
higan/md/vdp/vdp.hpp Normal file
View File

@ -0,0 +1,4 @@
struct VDP : Thread {
};
extern VDP vdp;

View File

@ -0,0 +1,7 @@
#include <md/md.hpp>
namespace MegaDrive {
YM2612 ym2612;
}

View File

@ -0,0 +1,6 @@
//Yamaha YM2612
struct YM2612 : Thread {
};
extern YM2612 ym2612;

View File

@ -4,18 +4,22 @@ objects += $(if $(findstring arm,$(processors)),processor-arm)
objects += $(if $(findstring gsu,$(processors)),processor-gsu)
objects += $(if $(findstring hg51b,$(processors)),processor-hg51b)
objects += $(if $(findstring lr35902,$(processors)),processor-lr35902)
objects += $(if $(findstring m68k,$(processors)),processor-m68k)
objects += $(if $(findstring r6502,$(processors)),processor-r6502)
objects += $(if $(findstring r65816,$(processors)),processor-r65816)
objects += $(if $(findstring spc700,$(processors)),processor-spc700)
objects += $(if $(findstring upd96050,$(processors)),processor-upd96050)
objects += $(if $(findstring v30mz,$(processors)),processor-v30mz)
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-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)
obj/processor-r65816.o: processor/r65816/r65816.cpp $(call rwildcard,processor/r65816)
obj/processor-spc700.o: processor/spc700/spc700.cpp $(call rwildcard,processor/spc700)
obj/processor-upd96050.o: processor/upd96050/upd96050.cpp $(call rwildcard,processor/upd96050)
obj/processor-v30mz.o: processor/v30mz/v30mz.cpp $(call rwildcard,processor/v30mz)
obj/processor-z80.o: processor/z80/z80.cpp $(call rwildcard,z80)

View File

@ -0,0 +1,6 @@
#include <processor/processor.hpp>
#include "m68k.hpp"
namespace Processor {
}

View File

@ -0,0 +1,10 @@
#pragma once
//Motorola M68000
namespace Processor {
struct M68K {
};
}

View File

@ -0,0 +1,5 @@
#include <processor/processor.hpp>
namespace Processor {
}

View File

@ -0,0 +1,10 @@
#pragma once
//Zilog Z80
namespace Processor {
struct Z80 {
};
}

View File

@ -0,0 +1 @@
system name:Mega Drive

View File

@ -3,6 +3,7 @@ flags += -DSFC_SUPERGAMEBOY
include fc/GNUmakefile
include sfc/GNUmakefile
include md/GNUmakefile
include gb/GNUmakefile
include gba/GNUmakefile
include ws/GNUmakefile

View File

@ -1,6 +1,7 @@
#include "../tomoko.hpp"
#include <fc/interface/interface.hpp>
#include <sfc/interface/interface.hpp>
#include <md/interface/interface.hpp>
#include <gb/interface/interface.hpp>
#include <gba/interface/interface.hpp>
#include <ws/interface/interface.hpp>
@ -16,6 +17,7 @@ Program::Program(string_vector args) {
emulators.append(new Famicom::Interface);
emulators.append(new SuperFamicom::Interface);
emulators.append(new MegaDrive::Interface);
emulators.append(new GameBoy::Interface);
emulators.append(new GameBoyAdvance::Interface);
emulators.append(new WonderSwan::Interface);

View File

@ -1,6 +1,7 @@
Icarus::Icarus() {
database.famicom = BML::unserialize(string::read(locate("Database/Famicom.bml")));
database.superFamicom = BML::unserialize(string::read(locate("Database/Super Famicom.bml")));
database.megaDrive = BML::unserialize(string::read(locate("Database/Mega Drive.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")));
@ -31,6 +32,7 @@ auto Icarus::manifest(string location) -> string {
auto type = Location::suffix(location).downcase();
if(type == ".fc") return famicomManifest(location);
if(type == ".sfc") return superFamicomManifest(location);
if(type == ".md") return megaDriveManifest(location);
if(type == ".gb") return gameBoyManifest(location);
if(type == ".gbc") return gameBoyColorManifest(location);
if(type == ".gba") return gameBoyAdvanceManifest(location);
@ -66,6 +68,7 @@ auto Icarus::import(string location) -> string {
if(type == ".fc" || type == ".nes") return famicomImport(buffer, location);
if(type == ".sfc" || type == ".smc") return superFamicomImport(buffer, location);
if(type == ".smd") return megaDriveImport(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

@ -22,6 +22,11 @@ struct Icarus {
auto superFamicomManifestScan(vector<Markup::Node>& roms, Markup::Node node) -> void;
auto superFamicomImport(vector<uint8_t>& buffer, string location) -> string;
//mega-drive.cpp
auto megaDriveManifest(string location) -> string;
auto megaDriveManifest(vector<uint8_t>& buffer, string location) -> string;
auto megaDriveImport(vector<uint8_t>& buffer, string location) -> string;
//game-boy.cpp
auto gameBoyManifest(string location) -> string;
auto gameBoyManifest(vector<uint8_t>& buffer, string location) -> string;
@ -63,6 +68,7 @@ private:
struct {
Markup::Node famicom;
Markup::Node superFamicom;
Markup::Node megaDrive;
Markup::Node gameBoy;
Markup::Node gameBoyColor;
Markup::Node gameBoyAdvance;

View File

@ -0,0 +1,45 @@
auto Icarus::megaDriveManifest(string location) -> string {
vector<uint8_t> buffer;
concatenate(buffer, {location, "program.rom"});
return megaDriveManifest(buffer, location);
}
auto Icarus::megaDriveManifest(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.megaDrive) {
if(node["sha256"].text() == digest) {
manifest.append(node.text(), "\n sha256: ", digest, "\n");
break;
}
}
}
if(settings["icarus/UseHeuristics"].boolean() && !manifest) {
MegaDriveCartridge cartridge{location, buffer.data(), buffer.size()};
manifest = cartridge.manifest;
}
return manifest;
}
auto Icarus::megaDriveImport(vector<uint8_t>& buffer, string location) -> string {
auto name = Location::prefix(location);
auto source = Location::path(location);
string target{settings["Library/Location"].text(), "Mega Drive/", name, ".md/"};
//if(directory::exists(target)) return failure("game already exists");
auto manifest = megaDriveManifest(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,19 @@
struct MegaDriveCartridge {
MegaDriveCartridge(string location, uint8_t* data, uint size);
string manifest;
//private:
struct Information {
} information;
};
MegaDriveCartridge::MegaDriveCartridge(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("\n");
manifest.append("note: heuristically generated by icarus\n");
}

View File

@ -20,6 +20,7 @@ Settings settings;
#include "heuristics/famicom.cpp"
#include "heuristics/super-famicom.cpp"
#include "heuristics/mega-drive.cpp"
#include "heuristics/game-boy.cpp"
#include "heuristics/game-boy-advance.cpp"
#include "heuristics/wonderswan.cpp"
@ -30,6 +31,7 @@ Settings settings;
#include "core/core.cpp"
#include "core/famicom.cpp"
#include "core/super-famicom.cpp"
#include "core/mega-drive.cpp"
#include "core/game-boy.cpp"
#include "core/game-boy-color.cpp"
#include "core/game-boy-advance.cpp"
@ -66,7 +68,7 @@ auto nall::main(string_vector args) -> void {
if(string source = BrowserDialog()
.setTitle("Load ROM Image")
.setPath(settings["icarus/Path"].text())
.setFilters("ROM Files|*.fc:*.nes:*.sfc:*.smc:*.gb:*.gbc:*.gba:*.ws:*.wsc:*.bs:*.st:*.zip")
.setFilters("ROM Files|*.fc:*.nes:*.sfc:*.smc:*.smd:*.gb:*.gbc:*.gba:*.ws:*.wsc:*.bs:*.st:*.zip")
.openFile()) {
if(string target = icarus.import(source)) {
settings["icarus/Path"].setValue(Location::path(source));