Update to v091r05 release.

[No prior releases were posted to the WIP thread. -Ed.]

byuu says:

Super Famicom mapping system has been reworked as discussed with the
mask= changes. offset becomes base, mode is gone. Also added support for
comma-separated fields in the address fields, to reduce the number of
map lines needed.

    <?xml version="1.0" encoding="UTF-8"?>
    <cartridge region="NTSC">
      <superfx revision="2">
	<rom name="program.rom" size="0x200000"/>
	<ram name="save.rwm" size="0x8000"/>
	<map id="io" address="00-3f,80-bf:3000-32ff"/>
	<map id="rom" address="00-3f:8000-ffff" mask="0x8000"/>
	<map id="rom" address="40-5f:0000-ffff"/>
	<map id="ram" address="00-3f,80-bf:6000-7fff" size="0x2000"/>
	<map id="ram" address="70-71:0000-ffff"/>
      </superfx>
    </cartridge>

Or in BML:

    cartridge region=NTSC
      superfx revision=2
	rom name=program.rom size=0x200000
	ram name=save.rwm size=0x8000
	map id=io address=00-3f,80-bf:3000-32ff
	map id=rom address=00-3f:8000-ffff mask=0x8000
	map id=rom address=40-5f:0000-ffff
	map id=ram address=00-3f,80-bf:6000-7fff size=0x2000
	map id=ram address=70-71:0000-ffff

As a result of the changes, old mappings will no longer work. The above
XML example will run Super Mario World 2: Yoshi's Island. Otherwise,
you'll have to write your own.

All that's left now is to work some sort of database mapping system in,
so I can start dumping carts en masse.

The NES changes that FitzRoy asked for are mostly in as well.

Also, part of the reason I haven't released a WIP ... but fuck it, I'm
not going to wait forever to post a new WIP.

I've added a skeleton driver to emulate Campus Challenge '92 and
Powerfest '94. There's no actual emulation, except for the stuff I can
glean from looking at the pictures of the board. It has a DSP-1 (so
SR/DR registers), four ROMs that map in and out, RAM, etc.

I've also added preliminary mapping to upload high scores to a website,
but obviously I need the ROMs first.
This commit is contained in:
Tim Allen 2012-10-09 19:25:32 +11:00
parent 94b2538af5
commit ef746bbda4
67 changed files with 791 additions and 735 deletions

View File

@ -46,7 +46,7 @@ else ifeq ($(platform),win)
else
link += -mwindows
endif
link += -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
link += -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 -lws2_32
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
else
unknown_platform: help;

View File

@ -3,19 +3,22 @@
namespace Emulator {
static const char Name[] = "higan";
static const char Version[] = "091";
static const char Version[] = "091.05";
static const char Author[] = "byuu";
static const char License[] = "GPLv3";
}
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>
#include <nall/base64.hpp>
#include <nall/directory.hpp>
#include <nall/dl.hpp>
#include <nall/dsp.hpp>
#include <nall/endian.hpp>
#include <nall/file.hpp>
#include <nall/function.hpp>
#include <nall/http.hpp>
#include <nall/invoke.hpp>
#include <nall/priority-queue.hpp>
#include <nall/property.hpp>
#include <nall/random.hpp>

View File

@ -55,7 +55,7 @@ struct Interface {
virtual void videoRefresh(const uint32_t*, unsigned, unsigned, unsigned) {}
virtual void audioSample(int16_t, int16_t) {}
virtual int16_t inputPoll(unsigned, unsigned, unsigned) { return 0; }
virtual unsigned dipSettings(const XML::Node&) { return 0; }
virtual unsigned dipSettings(const Markup::Node&) { return 0; }
virtual string path(unsigned) { return ""; }
virtual void notify(const string &text) { print(text, "\n"); }
} *bind;
@ -68,7 +68,7 @@ struct Interface {
void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height) { return bind->videoRefresh(data, pitch, width, height); }
void audioSample(int16_t lsample, int16_t rsample) { return bind->audioSample(lsample, rsample); }
int16_t inputPoll(unsigned port, unsigned device, unsigned input) { return bind->inputPoll(port, device, input); }
unsigned dipSettings(const XML::Node &node) { return bind->dipSettings(node); }
unsigned dipSettings(const Markup::Node &node) { return bind->dipSettings(node); }
string path(unsigned group) { return bind->path(group); }
template<typename... Args> void notify(Args&... args) { return bind->notify({std::forward<Args>(args)...}); }

View File

@ -111,7 +111,7 @@ void serialize(serializer &s) {
s.integer(irq_latch);
}
BandaiFCG(XML::Document &document) : Board(document) {
BandaiFCG(Markup::Node &document) : Board(document) {
}
};

View File

@ -82,7 +82,7 @@ void Board::serialize(serializer &s) {
if(chrram.size) s.array(chrram.data, chrram.size);
}
Board::Board(XML::Document &document) {
Board::Board(Markup::Node &document) {
cartridge.board = this;
auto &cartridge = document["cartridge"];
@ -120,7 +120,7 @@ Board::~Board() {
}
Board* Board::load(const string &manifest) {
XML::Document document(manifest);
auto document = Markup::Document(manifest);
string type = document["cartridge"]["board"]["type"].data;
if(type == "BANDAI-FCG" ) return new BandaiFCG(document);
@ -154,16 +154,52 @@ Board* Board::load(const string &manifest) {
if(type == "NES-HKROM" ) return new NES_HKROM(document);
if(type == "NES-NROM" ) return new NES_NROM(document);
if(type == "NES-NROM-128") return new NES_NROM(document);
if(type == "NES-NROM-256") return new NES_NROM(document);
if(type == "NES-PEEOROM" ) return new NES_PxROM(document);
if(type == "NES-PNROM" ) return new NES_PxROM(document);
if(type == "NES-SAROM" ) return new NES_SxROM(document);
if(type == "NES-SBROM" ) return new NES_SxROM(document);
if(type == "NES-SCROM" ) return new NES_SxROM(document);
if(type == "NES-SC1ROM" ) return new NES_SxROM(document);
if(type == "NES-SEROM" ) return new NES_SxROM(document);
if(type == "NES-SFROM" ) return new NES_SxROM(document);
if(type == "NES-SFEXPROM") return new NES_SxROM(document);
if(type == "NES-SGROM" ) return new NES_SxROM(document);
if(type == "NES-SHROM" ) return new NES_SxROM(document);
if(type == "NES-SH1ROM" ) return new NES_SxROM(document);
if(type == "NES-SIROM" ) return new NES_SxROM(document);
if(type == "NES-SJROM" ) return new NES_SxROM(document);
if(type == "NES-SKROM" ) return new NES_SxROM(document);
if(type == "NES-SLROM" ) return new NES_SxROM(document);
if(type == "NES-SL1ROM" ) return new NES_SxROM(document);
if(type == "NES-SL2ROM" ) return new NES_SxROM(document);
if(type == "NES-SL3ROM" ) return new NES_SxROM(document);
if(type == "NES-SLRROM" ) return new NES_SxROM(document);
if(type == "NES-SMROM" ) return new NES_SxROM(document);
if(type == "NES-SNROM" ) return new NES_SxROM(document);
if(type == "NES-SOROM" ) return new NES_SxROM(document);
if(type == "NES-SUROM" ) return new NES_SxROM(document);
if(type == "NES-SXROM" ) return new NES_SxROM(document);
if(type == "NES-TBROM" ) return new NES_TxROM(document);
if(type == "NES-TEROM" ) return new NES_TxROM(document);
if(type == "NES-TFROM" ) return new NES_TxROM(document);
if(type == "NES-TGROM" ) return new NES_TxROM(document);
if(type == "NES-TKROM" ) return new NES_TxROM(document);
if(type == "NES-TKSROM" ) return new NES_TxROM(document);
if(type == "NES-TLROM" ) return new NES_TxROM(document);
if(type == "NES-TL1ROM" ) return new NES_TxROM(document);
if(type == "NES-TL2ROM" ) return new NES_TxROM(document);
if(type == "NES-TLSROM" ) return new NES_TxROM(document);
if(type == "NES-TNROM" ) return new NES_TxROM(document);
if(type == "NES-TQROM" ) return new NES_TxROM(document);
if(type == "NES-TR1ROM" ) return new NES_TxROM(document);
if(type == "NES-TSROM" ) return new NES_TxROM(document);
if(type == "NES-TVROM" ) return new NES_TxROM(document);
if(type == "NES-UNROM" ) return new NES_UxROM(document);
if(type == "NES-UOROM" ) return new NES_UxROM(document);

View File

@ -29,7 +29,7 @@ struct Board {
virtual void reset();
virtual void serialize(serializer&);
Board(XML::Document &document);
Board(Markup::Node &document);
virtual ~Board();
static Board* load(const string &manifest);

View File

@ -34,7 +34,7 @@ void serialize(serializer &s) {
vrc1.serialize(s);
}
KonamiVRC1(XML::Document &document) : Board(document), vrc1(*this) {
KonamiVRC1(Markup::Node &document) : Board(document), vrc1(*this) {
}
};

View File

@ -49,7 +49,7 @@ void serialize(serializer &s) {
vrc2.serialize(s);
}
KonamiVRC2(XML::Document &document) : Board(document), vrc2(*this) {
KonamiVRC2(Markup::Node &document) : Board(document), vrc2(*this) {
settings.pinout.a0 = 1 << decimal(document["cartridge"]["chip"]["pinout"]["a0"].data);
settings.pinout.a1 = 1 << decimal(document["cartridge"]["chip"]["pinout"]["a1"].data);
}

View File

@ -50,7 +50,7 @@ void serialize(serializer &s) {
vrc3.serialize(s);
}
KonamiVRC3(XML::Document &document) : Board(document), vrc3(*this) {
KonamiVRC3(Markup::Node &document) : Board(document), vrc3(*this) {
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
}

View File

@ -53,7 +53,7 @@ void serialize(serializer &s) {
vrc4.serialize(s);
}
KonamiVRC4(XML::Document &document) : Board(document), vrc4(*this) {
KonamiVRC4(Markup::Node &document) : Board(document), vrc4(*this) {
settings.pinout.a0 = 1 << decimal(document["cartridge"]["chip"]["pinout"]["a0"].data);
settings.pinout.a1 = 1 << decimal(document["cartridge"]["chip"]["pinout"]["a1"].data);
}

View File

@ -36,7 +36,7 @@ void main() { vrc6.main(); }
void power() { vrc6.power(); }
void reset() { vrc6.reset(); }
KonamiVRC6(XML::Document &document) : Board(document), vrc6(*this) {
KonamiVRC6(Markup::Node &document) : Board(document), vrc6(*this) {
}
};

View File

@ -41,7 +41,7 @@ void serialize(serializer &s) {
vrc7.serialize(s);
}
KonamiVRC7(XML::Document &document) : Board(document), vrc7(*this) {
KonamiVRC7(Markup::Node &document) : Board(document), vrc7(*this) {
}
};

View File

@ -45,7 +45,7 @@ void serialize(serializer &s) {
s.integer(mirror_select);
}
NES_AxROM(XML::Document &document) : Board(document) {
NES_AxROM(Markup::Node &document) : Board(document) {
}
};

View File

@ -45,7 +45,7 @@ void serialize(serializer &s) {
s.integer(prg_bank);
}
NES_BNROM(XML::Document &document) : Board(document) {
NES_BNROM(Markup::Node &document) : Board(document) {
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
}

View File

@ -47,7 +47,7 @@ void serialize(serializer &s) {
s.integer(chr_bank);
}
NES_CNROM(XML::Document &document) : Board(document) {
NES_CNROM(Markup::Node &document) : Board(document) {
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
}

View File

@ -46,7 +46,7 @@ void serialize(serializer &s) {
mmc5.serialize(s);
}
NES_ExROM(XML::Document &document) : Board(document), mmc5(*this) {
NES_ExROM(Markup::Node &document) : Board(document), mmc5(*this) {
revision = Revision::ELROM;
}

View File

@ -84,7 +84,7 @@ void serialize(serializer &s) {
s.array(latch);
}
NES_FxROM(XML::Document &document) : Board(document) {
NES_FxROM(Markup::Node &document) : Board(document) {
revision = Revision::FKROM;
}

View File

@ -54,7 +54,7 @@ void serialize(serializer &s) {
s.integer(chr_bank);
}
NES_GxROM(XML::Document &document) : Board(document) {
NES_GxROM(Markup::Node &document) : Board(document) {
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
}

View File

@ -42,7 +42,7 @@ void serialize(serializer &s) {
mmc6.serialize(s);
}
NES_HKROM(XML::Document &document) : Board(document), mmc6(*this) {
NES_HKROM(Markup::Node &document) : Board(document), mmc6(*this) {
}
};

View File

@ -36,7 +36,7 @@ void serialize(serializer &s) {
Board::serialize(s);
}
NES_NROM(XML::Document &document) : Board(document) {
NES_NROM(Markup::Node &document) : Board(document) {
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
}

View File

@ -90,7 +90,7 @@ void serialize(serializer &s) {
s.array(latch);
}
NES_PxROM(XML::Document &document) : Board(document) {
NES_PxROM(Markup::Node &document) : Board(document) {
revision = Revision::PNROM;
}

View File

@ -94,7 +94,7 @@ void serialize(serializer &s) {
mmc1.serialize(s);
}
NES_SxROM(XML::Document &document) : Board(document), mmc1(*this) {
NES_SxROM(Markup::Node &document) : Board(document), mmc1(*this) {
revision = Revision::SXROM;
}

View File

@ -60,7 +60,7 @@ void serialize(serializer &s) {
mmc3.serialize(s);
}
NES_TxROM(XML::Document &document) : Board(document), mmc3(*this) {
NES_TxROM(Markup::Node &document) : Board(document), mmc3(*this) {
revision = Revision::TLROM;
}

View File

@ -48,7 +48,7 @@ void serialize(serializer &s) {
s.integer(prg_bank);
}
NES_UxROM(XML::Document &document) : Board(document) {
NES_UxROM(Markup::Node &document) : Board(document) {
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
}

View File

@ -220,7 +220,7 @@ void serialize(serializer &s) {
pulse[2].serialize(s);
}
Sunsoft5B(XML::Document &document) : Board(document) {
Sunsoft5B(Markup::Node &document) : Board(document) {
}
};

View File

@ -25,7 +25,7 @@ void Cartridge::load(System::Revision revision, const string &manifest) {
information.romsize = 0;
information.ramsize = 0;
XML::Document document(manifest);
auto document = Markup::Document(manifest);
auto &mapperid = document["cartridge"]["board"]["type"].data;
if(mapperid == "none" ) information.mapper = Mapper::MBC0;

View File

@ -9,7 +9,7 @@ Cartridge cartridge;
void Cartridge::load(const string &manifest) {
information.markup = manifest;
XML::Document document(manifest);
auto document = Markup::Document(manifest);
unsigned rom_size = 0;
if(document["cartridge"]["rom"].exists()) {

View File

@ -1,33 +1,38 @@
#ifndef NALL_BASE64_HPP
#define NALL_BASE64_HPP
#include <string.h>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
namespace nall {
struct base64 {
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
output = new char[inlength * 8 / 6 + 6]();
output = new char[inlength * 8 / 6 + 8]();
unsigned i = 0, o = 0;
while(i < inlength) {
switch(i % 3) {
case 0: {
output[o++] = enc(input[i] >> 2);
output[o] = enc((input[i] & 3) << 4);
} break;
case 1: {
uint8_t prev = dec(output[o]);
output[o++] = enc(prev + (input[i] >> 4));
output[o] = enc((input[i] & 15) << 2);
} break;
case 0: {
output[o++] = enc(input[i] >> 2);
output[o] = enc((input[i] & 3) << 4);
break;
}
case 1: {
uint8_t prev = dec(output[o]);
output[o++] = enc(prev + (input[i] >> 4));
output[o] = enc((input[i] & 15) << 2);
break;
}
case 2: {
uint8_t prev = dec(output[o]);
output[o++] = enc(prev + (input[i] >> 6));
output[o++] = enc(input[i] & 63);
break;
}
case 2: {
uint8_t prev = dec(output[o]);
output[o++] = enc(prev + (input[i] >> 6));
output[o++] = enc(input[i] & 63);
} break;
}
i++;
@ -36,32 +41,46 @@ namespace nall {
return true;
}
static string encode(const string &data) {
char *buffer = nullptr;
encode(buffer, (const uint8_t*)(const char*)data, data.length());
string result = buffer;
delete[] buffer;
return result;
}
static bool decode(uint8_t *&output, unsigned &outlength, const char *input) {
unsigned inlength = strlen(input), infix = 0;
output = new uint8_t[inlength]();
output = new uint8_t[inlength + 1]();
unsigned i = 0, o = 0;
while(i < inlength) {
uint8_t x = dec(input[i]);
switch(i++ & 3) {
case 0: {
output[o] = x << 2;
} break;
case 1: {
output[o++] |= x >> 4;
output[o] = (x & 15) << 4;
} break;
case 0: {
output[o] = x << 2;
break;
}
case 2: {
output[o++] |= x >> 2;
output[o] = (x & 3) << 6;
} break;
case 1: {
output[o++] |= x >> 4;
output[o] = (x & 15) << 4;
break;
}
case 2: {
output[o++] |= x >> 2;
output[o] = (x & 3) << 6;
break;
}
case 3: {
output[o++] |= x;
break;
}
case 3: {
output[o++] |= x;
} break;
}
}
@ -69,9 +88,18 @@ namespace nall {
return true;
}
static string decode(const string &data) {
uint8_t *buffer = nullptr;
unsigned size = 0;
decode(buffer, size, (const char*)data);
string result = (const char*)buffer;
delete[] buffer;
return result;
}
private:
static char enc(uint8_t n) {
//base64 for URL encodings
//base64 for URL encodings (URL = -_, MIME = +/)
static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
return lookup_table[n & 63];
}

View File

@ -15,6 +15,11 @@ struct bpsmulti {
MirrorFile = 3,
};
enum : unsigned {
OriginSource = 0,
OriginTarget = 1,
};
bool create(const string &patchName, const string &sourcePath, const string &targetPath, bool delta = false, const string &metadata = "") {
if(fp.open()) fp.close();
fp.open(patchName, file::mode::write);
@ -30,9 +35,10 @@ struct bpsmulti {
for(auto &targetName : targetList) {
if(targetName.endswith("/")) {
targetName.rtrim<1>("/");
writeNumber(CreatePath | ((targetName.length() - 1) << 2));
writeString(targetName);
} else if(auto position = sourceList.find(targetName)) { //note: sourceName == targetName
} else if(auto position = sourceList.find(targetName)) { //if sourceName == targetName
file sp, dp;
sp.open({sourcePath, targetName}, file::mode::read);
dp.open({targetPath, targetName}, file::mode::read);
@ -46,30 +52,32 @@ struct bpsmulti {
cksum = crc32_adjust(cksum, byte);
}
writeNumber((identical ? MirrorFile : ModifyFile) | ((targetName.length() - 1) << 2));
writeString(targetName);
writeNumber(targetName.length() - 1);
writeString(targetName);
if(identical) {
writeNumber(MirrorFile | ((targetName.length() - 1) << 2));
writeString(targetName);
writeNumber(OriginSource);
writeChecksum(~cksum);
continue;
}
if(delta == false) {
bpslinear patch;
patch.source({sourcePath, targetName});
patch.target({targetPath, targetName});
patch.create({temppath(), "temp.bps"});
} else {
bpsdelta patch;
patch.source({sourcePath, targetName});
patch.target({targetPath, targetName});
patch.create({temppath(), "temp.bps"});
}
writeNumber(ModifyFile | ((targetName.length() - 1) << 2));
writeString(targetName);
writeNumber(OriginSource);
auto buffer = file::read({temppath(), "temp.bps"});
writeNumber(buffer.size());
for(auto &byte : buffer) write(byte);
if(delta == false) {
bpslinear patch;
patch.source({sourcePath, targetName});
patch.target({targetPath, targetName});
patch.create({temppath(), "temp.bps"});
} else {
bpsdelta patch;
patch.source({sourcePath, targetName});
patch.target({targetPath, targetName});
patch.create({temppath(), "temp.bps"});
}
auto buffer = file::read({temppath(), "temp.bps"});
writeNumber(buffer.size());
for(auto &byte : buffer) write(byte);
}
} else {
writeNumber(CreateFile | ((targetName.length() - 1) << 2));
writeString(targetName);
@ -105,7 +113,7 @@ struct bpsmulti {
string targetName = readString(targetLength);
if(action == CreatePath) {
directory::create({targetPath, targetName});
directory::create({targetPath, targetName, "/"});
} else if(action == CreateFile) {
file fp;
fp.open({targetPath, targetName}, file::mode::write);
@ -113,21 +121,23 @@ struct bpsmulti {
while(fileSize--) fp.write(read());
uint32_t cksum = readChecksum();
} else if(action == ModifyFile) {
auto sourceLength = readNumber() + 1;
string sourceName = readString(sourceLength);
auto encoding = readNumber();
string originPath = encoding & 1 ? targetPath : sourcePath;
string sourceName = (encoding >> 1) == 0 ? targetName : readString(encoding >> 1);
auto patchSize = readNumber();
vector<uint8_t> buffer;
buffer.resize(patchSize);
for(unsigned n = 0; n < patchSize; n++) buffer[n] = read();
bpspatch patch;
patch.modify(buffer.data(), buffer.size());
patch.source({sourcePath, sourceName});
patch.source({originPath, sourceName});
patch.target({targetPath, targetName});
if(patch.apply() != bpspatch::result::success) return false;
} else if(action == MirrorFile) {
auto sourceLength = readNumber() + 1;
string sourceName = readString(sourceLength);
file::copy({sourcePath, sourceName}, {targetPath, targetName});
auto encoding = readNumber();
string originPath = encoding & 1 ? targetPath : sourcePath;
string sourceName = (encoding >> 1) == 0 ? targetName : readString(encoding >> 1);
file::copy({originPath, sourceName}, {targetPath, targetName});
uint32_t cksum = readChecksum();
}
}

View File

@ -74,6 +74,14 @@ namespace nall {
return true;
}
static bool write(const string &filename, const vector<uint8_t> &buffer) {
file fp;
if(fp.open(filename, mode::write) == false) return false;
fp.write(buffer.data(), buffer.size());
fp.close();
return true;
}
static bool write(const string &filename, const uint8_t *data, unsigned size) {
file fp;
if(fp.open(filename, mode::write) == false) return false;

View File

@ -22,7 +22,6 @@
#define NALL_STRING_INTERNAL_HPP
#include <nall/string/base.hpp>
#include <nall/string/bml.hpp>
#include <nall/string/bsv.hpp>
#include <nall/string/cast.hpp>
#include <nall/string/compare.hpp>
@ -44,7 +43,10 @@
#include <nall/string/variadic.hpp>
#include <nall/string/wildcard.hpp>
#include <nall/string/wrapper.hpp>
#include <nall/string/xml.hpp>
#include <nall/string/markup/node.hpp>
#include <nall/string/markup/bml.hpp>
#include <nall/string/markup/xml.hpp>
#include <nall/string/markup/document.hpp>
#undef NALL_STRING_INTERNAL_HPP
#endif

View File

@ -1,151 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
//BML v1.0 parser
//revision 0.05
namespace nall {
namespace BML {
inline static string indent(const char *s, unsigned depth) {
vector<char> output;
do {
for(unsigned n = 0; n < depth; n++) output.append('\t');
do output.append(*s); while(*s && *s++ != '\n');
} while(*s);
return output.data();
}
struct Node {
cstring name;
cstring value;
private:
vector<Node> children;
inline bool valid(char p) const { //A-Za-z0-9-.
return p - 'A' < 26u | p - 'a' < 26u | p - '0' < 10u | p - '-' < 2u;
}
inline unsigned parseDepth(char *&p) {
while(*p == '\n' || *p == '#') {
while(*p != '\n') *p++ = 0;
*p++ = 0; //'\n'
}
unsigned depth = 0;
while(p[depth] == '\t') depth++;
return depth;
}
inline void parseName(char *&p) {
if(valid(*p) == false) throw "Missing node name";
name = p;
while(valid(*p)) p++;
}
inline void parseValue(char *&p) {
char terminal = *p == ':' ? '\n' : ' '; //':' or '='
*p++ = 0;
value = p;
while(*p && *p != terminal && *p != '\n') p++;
}
inline void parseBlock(char *&p, unsigned depth) {
value = p;
char *w = p;
while(parseDepth(p) > depth) {
p += depth + 1;
while(*p && *p != '\n') *w++ = *p++;
if(*p && *p != '\n') throw "Multi-line value missing line feed";
*w++ = *p;
}
*(w - 1) = 0; //'\n'
}
inline void parseLine(char *&p) {
unsigned depth = parseDepth(p);
while(*p == '\t') p++;
parseName(p);
bool multiLine = *p == '~';
if(multiLine) *p++ = 0;
else if(*p == ':' || *p == '=') parseValue(p);
if(*p && *p != ' ' && *p != '\n') throw "Invalid character encountered";
while(*p == ' ') {
*p++ = 0;
Node node;
node.parseName(p);
if(*p == ':' || *p == '=') node.parseValue(p);
if(*p && *p != ' ' && *p != '\n') throw "Invalid character after node";
if(*p == '\n') *p++ = 0;
children.append(node);
}
if(multiLine) return parseBlock(p, depth);
while(parseDepth(p) > depth) {
Node node;
node.parseLine(p);
children.append(node);
}
}
inline void parse(char *&p) {
while(*p) {
Node node;
node.parseLine(p);
children.append(node);
}
}
public:
inline Node& operator[](const char *name) {
for(auto &node : children) {
if(node.name == name) return node;
}
static Node node;
node.name = nullptr;
return node;
}
inline bool exists() const { return name; }
unsigned size() const { return children.size(); }
Node* begin() { return children.begin(); }
Node* end() { return children.end(); }
const Node* begin() const { return children.begin(); }
const Node* end() const { return children.end(); }
inline Node() : name(""), value("") {}
friend class Document;
};
struct Document : Node {
cstring error;
inline bool load(const char *document) {
if(document == nullptr) return false;
this->document = strdup(document);
char *p = this->document;
try {
this->error = nullptr;
parse(p);
} catch(const char *error) {
this->error = error;
free(this->document);
this->document = nullptr;
children.reset();
return false;
}
return true;
}
inline Document(const char *document = "") : document(nullptr), error(nullptr) { if(*document) load(document); }
inline ~Document() { if(document) free(document); }
private:
char *document;
};
}
}
#endif

View File

@ -0,0 +1,147 @@
#ifdef NALL_STRING_INTERNAL_HPP
//BML v1.0 parser
//revision 0.02
namespace nall {
namespace BML {
struct Node : Markup::Node {
protected:
//test to verify if a valid character for a node name
bool valid(char p) const { //A-Z, a-z, 0-9, -./
return p - 'A' < 26u || p - 'a' < 26u || p - '0' < 10u || p - '-' < 3u;
}
//determine indentation level, without incrementing pointer
unsigned readDepth(const char *p) {
unsigned depth = 0;
while(p[depth] == '\t' || p[depth] == ' ') depth++;
return depth;
}
//determine indentation level
unsigned parseDepth(const char *&p) {
unsigned depth = readDepth(p);
p += depth;
return depth;
}
//read name
void parseName(const char *&p) {
unsigned length = 0;
while(valid(p[length])) length++;
if(length == 0) throw "Invalid node name";
name = substr(p, 0, length);
p += length;
}
void parseData(const char *&p) {
if(*p == '=' && *(p + 1) == '\"') {
unsigned length = 2;
while(p[length] && p[length] != '\n' && p[length] != '\"') length++;
if(p[length] != '\"') throw "Unescaped value";
data = substr(p, 2, length - 2);
p += length + 1;
} else if(*p == '=') {
unsigned length = 1;
while(p[length] && p[length] != '\n' && p[length] != '\"' && p[length] != ' ') length++;
if(p[length] == '\"') throw "Illegal character in value";
data = substr(p, 1, length - 1);
p += length;
} else if(*p == ':') {
unsigned length = 1;
while(p[length] && p[length] != '\n') length++;
data = {substr(p, 1, length - 1), "\n"};
p += length;
}
}
//read all attributes for a node
void parseAttributes(const char *&p) {
while(*p && *p != '\n') {
if(*p != ' ') throw "Invalid node name";
while(*p == ' ') p++; //skip excess spaces
Node node;
node.attribute = true;
unsigned length = 0;
while(valid(p[length])) length++;
if(length == 0) throw "Invalid attribute name";
node.name = substr(p, 0, length);
node.parseData(p += length);
children.append(node);
}
}
//read a node and all of its children nodes
void parseNode(const char *&p) {
level = parseDepth(p);
parseName(p);
parseData(p);
parseAttributes(p);
if(*p++ != '\n') throw "Missing line feed";
while(*p) {
if(*p == '\n') { p++; continue; }
unsigned depth = readDepth(p);
if(depth <= level) break;
if(p[depth] == ':') {
p += depth;
unsigned length = 0;
while(p[length] && p[length] != '\n') length++;
data.append(substr(p, 1, length - 1), "\n");
p += length;
continue;
}
Node node;
node.parseNode(p);
children.append(node);
}
data.rtrim<1>("\n");
}
//read top-level nodes
void parse(const char *p) {
while(*p) {
Node node;
node.parseNode(p);
if(node.level > 0) throw "Root nodes cannot be indented";
children.append(node);
}
}
friend class Document;
};
struct Document : Node {
string error;
bool load(string document) {
name = "{root}", data = "";
try {
document.replace("\r", "");
while(document.position("\n\n")) document.replace("\n\n", "\n");
parse(document);
} catch(const char *perror) {
error = perror;
children.reset();
return false;
}
return true;
}
Document(const string &document = "") {
load(document);
}
};
}
}
#endif

View File

@ -0,0 +1,14 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
namespace Markup {
inline Node Document(const string &markup) {
if(markup.beginswith("<")) return XML::Document(markup);
return BML::Document(markup);
}
}
}
#endif

View File

@ -0,0 +1,62 @@
#ifdef NALL_STRING_INTERNAL_HPP
//note: specific markups inherit from Markup::Node
//vector<Node> will slice any data; so derived nodes must not contain data nor virtual functions
//vector<Node*> would incur a large performance penalty and greatly increased complexity
namespace nall {
namespace Markup {
struct Node {
string name;
string data;
bool attribute;
bool exists() const {
return !name.empty();
}
string content() const {
return string{data}.trim(" ");
}
void reset() {
children.reset();
}
Node& operator[](const string &name) {
for(auto &node : *this) {
if(node.name == name) return node;
}
static Node node;
return node;
}
const Node& operator[](const string &name) const {
return operator[](name);
}
vector<Node> operator()(const string &pattern) const {
vector<Node> result;
for(auto &node : *this) {
if(node.name.wildcard(pattern)) result.append(node);
}
return result;
}
Node* begin() { return children.begin(); }
Node* end() { return children.end(); }
const Node* begin() const { return children.begin(); }
const Node* end() const { return children.end(); }
Node() : attribute(false), level(0) {}
protected:
unsigned level;
vector<Node> children;
};
}
}
#endif

View File

@ -1,19 +1,22 @@
#ifdef NALL_STRING_INTERNAL_HPP
//XML v1.0 subset parser
//revision 0.01
//revision 0.03
namespace nall {
namespace XML {
struct Node {
string name;
string data;
bool attribute;
vector<Node*> children;
inline bool exists() const {
return !name.empty();
struct Node : Markup::Node {
protected:
inline string escape() const {
string result = data;
result.replace("&", "&amp;");
result.replace("<", "&lt;");
result.replace(">", "&gt;");
if(attribute == false) return result;
result.replace("\'", "&apos;");
result.replace("\"", "&quot;");
return result;
}
inline bool isName(char c) const {
@ -124,15 +127,14 @@ struct Node {
if(*p == '?' || *p == '/' || *p == '>') break;
//parse attribute name
Node *attribute = new Node;
children.append(attribute);
attribute->attribute = true;
Node attribute;
attribute.attribute = true;
const char *nameStart = p;
while(isName(*p)) p++;
const char *nameEnd = p;
copy(attribute->name, nameStart, nameEnd - nameStart);
if(attribute->name.empty()) throw "missing attribute name";
copy(attribute.name, nameStart, nameEnd - nameStart);
if(attribute.name.empty()) throw "missing attribute name";
//parse attribute data
if(*p++ != '=') throw "missing attribute value";
@ -143,7 +145,8 @@ struct Node {
if(!*p) throw "missing attribute data terminal";
const char *dataEnd = p++; //skip closing terminal
copy(attribute->data, dataStart, dataEnd - dataStart);
copy(attribute.data, dataStart, dataEnd - dataStart);
children.append(attribute);
}
//parse closure
@ -155,10 +158,9 @@ struct Node {
//parse element and all of its child elements
inline void parseElement(const char *&p) {
Node *node = new Node;
Node node;
if(node.parseHead(p) == false) node.parse(p);
children.append(node);
if(node->parseHead(p) == true) return;
node->parse(p);
}
//return true if </tag> matches this node's name
@ -188,40 +190,6 @@ struct Node {
copy(data, dataStart, dataEnd - dataStart);
}
inline void reset() {
for(auto &child : children) delete child;
children.reset();
}
struct iterator {
inline bool operator!=(const iterator &source) const { return index != source.index; }
inline Node& operator*() { return *node.children[index]; }
inline iterator& operator++() { index++; return *this; }
inline iterator(const Node &node, unsigned index) : node(node), index(index) {}
private:
const Node &node;
unsigned index;
};
inline iterator begin() { return iterator(*this, 0); }
inline iterator end() { return iterator(*this, children.size()); }
inline const iterator begin() const { return iterator(*this, 0); }
inline const iterator end() const { return iterator(*this, children.size()); }
inline Node& operator[](const char *name) {
for(auto &node : *this) {
if(node.name == name) return node;
}
static Node node;
return node;
}
inline Node() : attribute(false) {}
inline ~Node() { reset(); }
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
};
struct Document : Node {

View File

@ -63,7 +63,7 @@ string temppath() {
#ifdef _WIN32
wchar_t path[PATH_MAX] = L"";
GetTempPathW(PATH_MAX, path);
path.transform("\\", "/");
//path.transform("\\", "/");
return (const char*)utf8_t(path);
#else
return "/tmp/";

View File

@ -1,265 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
//XML v1.0 subset parser
//revision 0.05
namespace nall {
struct xml_attribute {
string name;
string content;
virtual string parse() const;
};
struct xml_element : xml_attribute {
string parse() const;
linear_vector<xml_attribute> attribute;
linear_vector<xml_element> element;
protected:
void parse_doctype(const char *&data);
bool parse_head(string data);
bool parse_body(const char *&data);
friend xml_element xml_parse(const char *data);
};
inline string xml_attribute::parse() const {
string data;
unsigned offset = 0;
const char *source = content;
while(*source) {
if(*source == '&') {
if(strbegin(source, "&lt;")) { data[offset++] = '<'; source += 4; continue; }
if(strbegin(source, "&gt;")) { data[offset++] = '>'; source += 4; continue; }
if(strbegin(source, "&amp;")) { data[offset++] = '&'; source += 5; continue; }
if(strbegin(source, "&apos;")) { data[offset++] = '\''; source += 6; continue; }
if(strbegin(source, "&quot;")) { data[offset++] = '"'; source += 6; continue; }
}
//reject illegal characters
if(*source == '&') return "";
if(*source == '<') return "";
if(*source == '>') return "";
data[offset++] = *source++;
}
data[offset] = 0;
return data;
}
inline string xml_element::parse() const {
string data;
unsigned offset = 0;
const char *source = content;
while(*source) {
if(*source == '&') {
if(strbegin(source, "&lt;")) { data[offset++] = '<'; source += 4; continue; }
if(strbegin(source, "&gt;")) { data[offset++] = '>'; source += 4; continue; }
if(strbegin(source, "&amp;")) { data[offset++] = '&'; source += 5; continue; }
if(strbegin(source, "&apos;")) { data[offset++] = '\''; source += 6; continue; }
if(strbegin(source, "&quot;")) { data[offset++] = '"'; source += 6; continue; }
}
if(strbegin(source, "<!--")) {
if(auto pos = strpos(source, "-->")) {
source += pos() + 3;
continue;
} else {
return "";
}
}
if(strbegin(source, "<![CDATA[")) {
if(auto pos = strpos(source, "]]>")) {
if(pos() - 9 > 0) {
string cdata = substr(source, 9, pos() - 9);
data.append(cdata);
offset += strlen(cdata);
}
source += 9 + offset + 3;
continue;
} else {
return "";
}
}
//reject illegal characters
if(*source == '&') return "";
if(*source == '<') return "";
if(*source == '>') return "";
data[offset++] = *source++;
}
data[offset] = 0;
return data;
}
inline void xml_element::parse_doctype(const char *&data) {
name = "!DOCTYPE";
const char *content_begin = data;
signed counter = 0;
while(*data) {
char value = *data++;
if(value == '<') counter++;
if(value == '>') counter--;
if(counter < 0) {
content = substr(content_begin, 0, data - content_begin - 1);
return;
}
}
throw "...";
}
inline bool xml_element::parse_head(string data) {
data.qreplace("\t", " ");
data.qreplace("\r", " ");
data.qreplace("\n", " ");
while(qstrpos(data, " ")) data.qreplace(" ", " ");
data.qreplace(" =", "=");
data.qreplace("= ", "=");
data.rtrim();
lstring part;
part.qsplit(" ", data);
name = part[0];
if(name == "") throw "...";
for(unsigned i = 1; i < part.size(); i++) {
lstring side;
side.qsplit("=", part[i]);
if(side.size() != 2) throw "...";
xml_attribute attr;
attr.name = side[0];
attr.content = side[1];
if(strbegin(attr.content, "\"") && strend(attr.content, "\"")) attr.content.trim<1>("\"");
else if(strbegin(attr.content, "'") && strend(attr.content, "'")) attr.content.trim<1>("'");
else throw "...";
attribute.append(attr);
}
}
inline bool xml_element::parse_body(const char *&data) {
while(true) {
if(!*data) return false;
if(*data++ != '<') continue;
if(*data == '/') return false;
if(strbegin(data, "!DOCTYPE") == true) {
parse_doctype(data);
return true;
}
if(strbegin(data, "!--")) {
if(auto offset = strpos(data, "-->")) {
data += offset() + 3;
continue;
} else {
throw "...";
}
}
if(strbegin(data, "![CDATA[")) {
if(auto offset = strpos(data, "]]>")) {
data += offset() + 3;
continue;
} else {
throw "...";
}
}
auto offset = strpos(data, ">");
if(!offset) throw "...";
string tag = substr(data, 0, offset());
data += offset() + 1;
const char *content_begin = data;
bool self_terminating = false;
if(strend(tag, "?") == true) {
self_terminating = true;
tag.rtrim<1>("?");
} else if(strend(tag, "/") == true) {
self_terminating = true;
tag.rtrim<1>("/");
}
parse_head(tag);
if(self_terminating) return true;
while(*data) {
unsigned index = element.size();
xml_element node;
if(node.parse_body(data) == false) {
if(*data == '/') {
signed length = data - content_begin - 1;
if(length > 0) content = substr(content_begin, 0, length);
data++;
auto offset = strpos(data, ">");
if(!offset) throw "...";
tag = substr(data, 0, offset());
data += offset() + 1;
tag.replace("\t", " ");
tag.replace("\r", " ");
tag.replace("\n", " ");
while(strpos(tag, " ")) tag.replace(" ", " ");
tag.rtrim();
if(name != tag) throw "...";
return true;
}
} else {
element.append(node);
}
}
}
}
//ensure there is only one root element
inline bool xml_validate(xml_element &document) {
unsigned root_counter = 0;
for(unsigned i = 0; i < document.element.size(); i++) {
string &name = document.element[i].name;
if(strbegin(name, "?")) continue;
if(strbegin(name, "!")) continue;
if(++root_counter > 1) return false;
}
return true;
}
inline xml_element xml_parse(const char *data) {
xml_element self;
try {
while(*data) {
xml_element node;
if(node.parse_body(data) == false) {
break;
} else {
self.element.append(node);
}
}
if(xml_validate(self) == false) throw "...";
return self;
} catch(const char*) {
xml_element empty;
return empty;
}
}
}
#endif

View File

@ -23,6 +23,7 @@ namespace nall {
public:
operator bool() const { return pool; }
T* data() { return pool; }
const T* data() const { return pool; }
bool empty() const { return objectsize == 0; }
unsigned size() const { return objectsize; }
@ -47,14 +48,15 @@ namespace nall {
}
void reserve(unsigned size) {
unsigned outputsize = min(size, objectsize);
size = bit::round(size); //amortize growth
T *copy = (T*)calloc(size, sizeof(T));
for(unsigned n = 0; n < min(size, objectsize); n++) new(copy + n) T(pool[n]);
for(unsigned n = 0; n < outputsize; n++) new(copy + n) T(pool[n]);
for(unsigned n = 0; n < objectsize; n++) pool[n].~T();
free(pool);
pool = copy;
poolsize = size;
objectsize = min(size, objectsize);
objectsize = outputsize;
}
//requires trivial constructor
@ -121,8 +123,8 @@ namespace nall {
}
optional<unsigned> find(const T& data) {
for(unsigned n = 0; n < size(); n++) if(pool[n] == data) return { true, n };
return { false, 0u };
for(unsigned n = 0; n < size(); n++) if(pool[n] == data) return {true, n};
return {false, 0u};
}
T& first() {

View File

@ -339,6 +339,14 @@ void Window::setResizable(bool resizable) {
return p.setResizable(resizable);
}
void Window::setSmartGeometry(const Geometry &geometry) {
Geometry margin = p.frameMargin();
return setGeometry({
geometry.x + margin.x, geometry.y + margin.y,
geometry.width, geometry.height
});
}
void Window::setStatusFont(const string &font) {
state.statusFont = font;
return p.setStatusFont(font);

View File

@ -202,6 +202,7 @@ struct Window : private nall::base_from_member<pWindow&>, Object {
void setMenuVisible(bool visible = true);
void setModal(bool modal = true);
void setResizable(bool resizable = true);
void setSmartGeometry(const Geometry &geometry);
void setStatusFont(const nall::string &font);
void setStatusText(const nall::string &text);
void setStatusVisible(bool visible = true);

View File

@ -1,7 +1,7 @@
sfc_objects := sfc-interface sfc-system sfc-controller
sfc_objects += sfc-cartridge sfc-cheat
sfc_objects += sfc-memory sfc-cpu sfc-smp sfc-dsp sfc-ppu
sfc_objects += sfc-icd2 sfc-bsx sfc-sufamiturbo sfc-nss
sfc_objects += sfc-icd2 sfc-bsx sfc-sufamiturbo sfc-nss sfc-event
sfc_objects += sfc-sa1 sfc-superfx
sfc_objects += sfc-armdsp sfc-hitachidsp sfc-necdsp
sfc_objects += sfc-epsonrtc sfc-sharprtc
@ -44,6 +44,7 @@ obj/sfc-icd2.o : $(sfc)/chip/icd2/icd2.cpp $(call rwildcard,$(sfc)/chip/ic
obj/sfc-bsx.o : $(sfc)/chip/bsx/bsx.cpp $(call rwildcard,$(sfc)/chip/bsx/)
obj/sfc-sufamiturbo.o: $(sfc)/chip/sufamiturbo/sufamiturbo.cpp $(sfc)/chip/sufamiturbo/*
obj/sfc-nss.o : $(sfc)/chip/nss/nss.cpp $(call rwildcard,$(sfc)/chip/nss/)
obj/sfc-event.o : $(sfc)/chip/event/event.cpp $(call rwildcard,$(sfc)/chip/event/)
obj/sfc-sa1.o : $(sfc)/chip/sa1/sa1.cpp $(call rwildcard,$(sfc)/chip/sa1/)
obj/sfc-superfx.o : $(sfc)/chip/superfx/superfx.cpp $(call rwildcard,$(sfc)/chip/superfx/)

View File

@ -81,27 +81,27 @@ alwaysinline void CPU::op_step() {
}
void CPU::enable() {
function<uint8 (unsigned)> read = { &CPU::mmio_read, (CPU*)&cpu };
function<void (unsigned, uint8)> write = { &CPU::mmio_write, (CPU*)&cpu };
function<uint8 (unsigned)> reader = { &CPU::mmio_read, (CPU*)&cpu };
function<void (unsigned, uint8)> writer = { &CPU::mmio_write, (CPU*)&cpu };
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2140, 0x2183, read, write);
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2140, 0x2183, read, write);
bus.map(reader, writer, 0x00, 0x3f, 0x2140, 0x2183);
bus.map(reader, writer, 0x80, 0xbf, 0x2140, 0x2183);
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4016, 0x4017, read, write);
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4016, 0x4017, read, write);
bus.map(reader, writer, 0x00, 0x3f, 0x4016, 0x4017);
bus.map(reader, writer, 0x80, 0xbf, 0x4016, 0x4017);
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4200, 0x421f, read, write);
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4200, 0x421f, read, write);
bus.map(reader, writer, 0x00, 0x3f, 0x4200, 0x421f);
bus.map(reader, writer, 0x80, 0xbf, 0x4200, 0x421f);
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4300, 0x437f, read, write);
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, read, write);
bus.map(reader, writer, 0x00, 0x3f, 0x4300, 0x437f);
bus.map(reader, writer, 0x80, 0xbf, 0x4300, 0x437f);
read = [](unsigned addr) { return cpu.wram[addr]; };
write = [](unsigned addr, uint8 data) { cpu.wram[addr] = data; };
reader = [](unsigned addr) { return cpu.wram[addr]; };
writer = [](unsigned addr, uint8 data) { cpu.wram[addr] = data; };
bus.map(Bus::MapMode::Linear, 0x00, 0x3f, 0x0000, 0x1fff, read, write, 0x000000, 0x002000);
bus.map(Bus::MapMode::Linear, 0x80, 0xbf, 0x0000, 0x1fff, read, write, 0x000000, 0x002000);
bus.map(Bus::MapMode::Linear, 0x7e, 0x7f, 0x0000, 0xffff, read, write);
bus.map(reader, writer, 0x00, 0x3f, 0x0000, 0x1fff, 0x002000);
bus.map(reader, writer, 0x80, 0xbf, 0x0000, 0x1fff, 0x002000);
bus.map(reader, writer, 0x7e, 0x7f, 0x0000, 0xffff, 0x020000);
}
void CPU::power() {

View File

@ -118,11 +118,11 @@ void PPU::frame() {
}
void PPU::enable() {
function<uint8 (unsigned)> read = { &PPU::mmio_read, (PPU*)&ppu };
function<void (unsigned, uint8)> write = { &PPU::mmio_write, (PPU*)&ppu };
function<uint8 (unsigned)> reader = { &PPU::mmio_read, (PPU*)&ppu };
function<void (unsigned, uint8)> writer = { &PPU::mmio_write, (PPU*)&ppu };
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, read, write);
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, read, write);
bus.map(reader, writer, 0x00, 0x3f, 0x2100, 0x213f);
bus.map(reader, writer, 0x80, 0xbf, 0x2100, 0x213f);
}
void PPU::power() {

View File

@ -82,11 +82,11 @@ void PPU::frame() {
}
void PPU::enable() {
function<uint8 (unsigned)> read = { &PPU::mmio_read, (PPU*)&ppu };
function<void (unsigned, uint8)> write = { &PPU::mmio_write, (PPU*)&ppu };
function<uint8 (unsigned)> reader = { &PPU::mmio_read, (PPU*)&ppu };
function<void (unsigned, uint8)> writer = { &PPU::mmio_write, (PPU*)&ppu };
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, read, write);
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, read, write);
bus.map(reader, writer, 0x00, 0x3f, 0x2100, 0x213f);
bus.map(reader, writer, 0x80, 0xbf, 0x2100, 0x213f);
}
void PPU::power() {

View File

@ -15,6 +15,7 @@ void Cartridge::load(const string &manifest) {
has_bs_slot = false;
has_st_slots = false;
has_nss_dip = false;
has_event = false;
has_sa1 = false;
has_superfx = false;
has_armdsp = false;

View File

@ -27,6 +27,7 @@ struct Cartridge : property<Cartridge> {
readonly<bool> has_bs_slot;
readonly<bool> has_st_slots;
readonly<bool> has_nss_dip;
readonly<bool> has_event;
readonly<bool> has_sa1;
readonly<bool> has_superfx;
readonly<bool> has_armdsp;
@ -40,15 +41,12 @@ struct Cartridge : property<Cartridge> {
readonly<bool> has_msu1;
struct Mapping {
function<uint8 (unsigned)> read;
function<void (unsigned, uint8)> write;
Bus::MapMode mode;
unsigned banklo;
unsigned bankhi;
unsigned addrlo;
unsigned addrhi;
unsigned offset;
function<uint8 (unsigned)> reader;
function<void (unsigned, uint8)> writer;
string addr;
unsigned size;
unsigned base;
unsigned mask;
Mapping();
Mapping(const function<uint8 (unsigned)>&, const function<void (unsigned, uint8)>&);
@ -75,26 +73,27 @@ struct Cartridge : property<Cartridge> {
private:
void parse_markup(const char*);
void parse_markup_map(Mapping&, XML::Node&);
void parse_markup_memory(MappedRAM&, XML::Node&, unsigned id, bool writable);
void parse_markup_map(Mapping&, Markup::Node&);
void parse_markup_memory(MappedRAM&, Markup::Node&, unsigned id, bool writable);
void parse_markup_cartridge(XML::Node&);
void parse_markup_icd2(XML::Node&);
void parse_markup_bsx(XML::Node&);
void parse_markup_bsxslot(XML::Node&);
void parse_markup_sufamiturbo(XML::Node&);
void parse_markup_nss(XML::Node&);
void parse_markup_sa1(XML::Node&);
void parse_markup_superfx(XML::Node&);
void parse_markup_armdsp(XML::Node&);
void parse_markup_hitachidsp(XML::Node&);
void parse_markup_necdsp(XML::Node&);
void parse_markup_epsonrtc(XML::Node&);
void parse_markup_sharprtc(XML::Node&);
void parse_markup_spc7110(XML::Node&);
void parse_markup_sdd1(XML::Node&);
void parse_markup_obc1(XML::Node&);
void parse_markup_msu1(XML::Node&);
void parse_markup_cartridge(Markup::Node&);
void parse_markup_icd2(Markup::Node&);
void parse_markup_bsx(Markup::Node&);
void parse_markup_bsxslot(Markup::Node&);
void parse_markup_sufamiturbo(Markup::Node&);
void parse_markup_nss(Markup::Node&);
void parse_markup_event(Markup::Node&);
void parse_markup_sa1(Markup::Node&);
void parse_markup_superfx(Markup::Node&);
void parse_markup_armdsp(Markup::Node&);
void parse_markup_hitachidsp(Markup::Node&);
void parse_markup_necdsp(Markup::Node&);
void parse_markup_epsonrtc(Markup::Node&);
void parse_markup_sharprtc(Markup::Node&);
void parse_markup_spc7110(Markup::Node&);
void parse_markup_sdd1(Markup::Node&);
void parse_markup_obc1(Markup::Node&);
void parse_markup_msu1(Markup::Node&);
};
extern Cartridge cartridge;

View File

@ -3,7 +3,7 @@
void Cartridge::parse_markup(const char *markup) {
mapping.reset();
XML::Document document(markup);
auto document = Markup::Document(markup);
auto &cartridge = document["cartridge"];
region = cartridge["region"].data != "PAL" ? Region::NTSC : Region::PAL;
@ -13,6 +13,7 @@ void Cartridge::parse_markup(const char *markup) {
parse_markup_bsxslot(cartridge["bsxslot"]);
parse_markup_sufamiturbo(cartridge["sufamiturbo"]);
parse_markup_nss(cartridge["nss"]);
parse_markup_event(cartridge["event"]);
parse_markup_sa1(cartridge["sa1"]);
parse_markup_superfx(cartridge["superfx"]);
parse_markup_armdsp(cartridge["armdsp"]);
@ -28,40 +29,14 @@ void Cartridge::parse_markup(const char *markup) {
//
void Cartridge::parse_markup_map(Mapping &m, XML::Node &map) {
m.offset = numeral(map["offset"].data);
void Cartridge::parse_markup_map(Mapping &m, Markup::Node &map) {
m.addr = map["address"].data;
m.size = numeral(map["size"].data);
string data = map["mode"].data;
if(data == "direct") m.mode = Bus::MapMode::Direct;
if(data == "linear") m.mode = Bus::MapMode::Linear;
if(data == "shadow") m.mode = Bus::MapMode::Shadow;
lstring part;
part.split(":", map["address"].data);
if(part.size() != 2) return;
lstring subpart;
subpart.split("-", part[0]);
if(subpart.size() == 1) {
m.banklo = hex(subpart[0]);
m.bankhi = m.banklo;
} else if(subpart.size() == 2) {
m.banklo = hex(subpart[0]);
m.bankhi = hex(subpart[1]);
}
subpart.split("-", part[1]);
if(subpart.size() == 1) {
m.addrlo = hex(subpart[0]);
m.addrhi = m.addrlo;
} else if(subpart.size() == 2) {
m.addrlo = hex(subpart[0]);
m.addrhi = hex(subpart[1]);
}
m.base = numeral(map["base"].data);
m.mask = numeral(map["mask"].data);
}
void Cartridge::parse_markup_memory(MappedRAM &ram, XML::Node &node, unsigned id, bool writable) {
void Cartridge::parse_markup_memory(MappedRAM &ram, Markup::Node &node, unsigned id, bool writable) {
string name = node["name"].data;
unsigned size = numeral(node["size"].data);
ram.map(allocate<uint8>(size, 0xff), size);
@ -73,7 +48,7 @@ void Cartridge::parse_markup_memory(MappedRAM &ram, XML::Node &node, unsigned id
//
void Cartridge::parse_markup_cartridge(XML::Node &root) {
void Cartridge::parse_markup_cartridge(Markup::Node &root) {
if(root.exists() == false) return;
parse_markup_memory(rom, root["rom"], ID::ROM, false);
@ -98,7 +73,7 @@ void Cartridge::parse_markup_cartridge(XML::Node &root) {
}
}
void Cartridge::parse_markup_icd2(XML::Node &root) {
void Cartridge::parse_markup_icd2(Markup::Node &root) {
if(root.exists() == false) return;
has_gb_slot = true;
icd2.revision = max(1, numeral(root["revision"].data));
@ -122,7 +97,7 @@ void Cartridge::parse_markup_icd2(XML::Node &root) {
}
}
void Cartridge::parse_markup_bsx(XML::Node &root) {
void Cartridge::parse_markup_bsx(Markup::Node &root) {
if(root.exists() == false) return;
has_bs_cart = true;
has_bs_slot = true;
@ -136,7 +111,8 @@ void Cartridge::parse_markup_bsx(XML::Node &root) {
for(auto &node : root) {
if(node.name != "map") continue;
if(node["id"].data == "rom" || node["id"].data == "ram") {
if(node["id"].data == "rom"
|| node["id"].data == "ram") {
Mapping m({&BSXCartridge::mcu_read, &bsxcartridge}, {&BSXCartridge::mcu_write, &bsxcartridge});
parse_markup_map(m, node);
mapping.append(m);
@ -150,7 +126,7 @@ void Cartridge::parse_markup_bsx(XML::Node &root) {
}
}
void Cartridge::parse_markup_bsxslot(XML::Node &root) {
void Cartridge::parse_markup_bsxslot(Markup::Node &root) {
if(root.exists() == false) return;
has_bs_slot = true;
@ -169,7 +145,7 @@ void Cartridge::parse_markup_bsxslot(XML::Node &root) {
}
}
void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
void Cartridge::parse_markup_sufamiturbo(Markup::Node &root) {
if(root.exists() == false) return;
has_st_slots = true;
@ -206,7 +182,7 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
}
}
void Cartridge::parse_markup_nss(XML::Node &root) {
void Cartridge::parse_markup_nss(Markup::Node &root) {
if(root.exists() == false) return;
has_nss_dip = true;
nss.dip = interface->dipSettings(root);
@ -222,7 +198,65 @@ void Cartridge::parse_markup_nss(XML::Node &root) {
}
}
void Cartridge::parse_markup_sa1(XML::Node &root) {
void Cartridge::parse_markup_event(Markup::Node &root) {
if(root.exists() == false) return;
has_event = true;
for(auto &node : root) {
if(node.name != "rom") continue;
unsigned id = numeral(node["id"].data);
if(id > 3) continue;
parse_markup_memory(event.rom[id], node, ID::EventROM0 + id, false);
}
parse_markup_memory(event.ram, root["ram"], ID::EventRAM, true);
event.board = Event::Board::CampusChallenge92;
if(root["name"].data == "Campus Challenge '92") event.board = Event::Board::CampusChallenge92;
if(root["name"].data == "Powerfest '94") event.board = Event::Board::Powerfest94;
event.revision = root["revision"].data == "B" ? 2 : 1;
lstring part = root["timer"].data.split<1>(":");
if(part.size() == 1) event.timer = decimal(part(0));
if(part.size() == 2) event.timer = decimal(part(0)) * 60 + decimal(part(1));
part = string{root["server"]["address"].data}.ltrim<1>("http://").split<1>("/");
event.path = {"/", part(1)};
part = part(0).split<1>(":");
event.host = part(0);
event.port = decimal(part(1)) ? decimal(part(1)) : 80;
event.username = root["server"]["username"].data;
event.password = root["server"]["password"].data;
for(auto &node : root) {
if(node.name != "map") continue;
if(node["id"].data == "rom") {
Mapping m({&Event::rom_read, &event}, [](unsigned, uint8) {});
parse_markup_map(m, node);
mapping.append(m);
}
if(node["id"].data == "ram") {
Mapping m({&Event::ram_read, &event}, {&Event::ram_write, &event});
parse_markup_map(m, node);
mapping.append(m);
}
if(node["id"].data == "dr") {
Mapping m([](unsigned) -> uint8 { return cpu.regs.mdr; }, {&Event::dr, &event});
parse_markup_map(m, node);
mapping.append(m);
}
if(node["id"].data == "sr") {
Mapping m({&Event::sr, &event}, [](unsigned, uint8) {});
parse_markup_map(m, node);
mapping.append(m);
}
}
}
void Cartridge::parse_markup_sa1(Markup::Node &root) {
if(root.exists() == false) return;
has_sa1 = true;
@ -260,7 +294,7 @@ void Cartridge::parse_markup_sa1(XML::Node &root) {
}
}
void Cartridge::parse_markup_superfx(XML::Node &root) {
void Cartridge::parse_markup_superfx(Markup::Node &root) {
if(root.exists() == false) return;
has_superfx = true;
@ -279,6 +313,7 @@ void Cartridge::parse_markup_superfx(XML::Node &root) {
if(node["id"].data == "rom") {
Mapping m(superfx.cpurom);
parse_markup_map(m, node);
if(m.size == 0) m.size = superfx.rom.size();
mapping.append(m);
}
@ -291,7 +326,7 @@ void Cartridge::parse_markup_superfx(XML::Node &root) {
}
}
void Cartridge::parse_markup_armdsp(XML::Node &root) {
void Cartridge::parse_markup_armdsp(Markup::Node &root) {
if(root.exists() == false) return;
has_armdsp = true;
@ -311,7 +346,7 @@ void Cartridge::parse_markup_armdsp(XML::Node &root) {
}
}
void Cartridge::parse_markup_hitachidsp(XML::Node &root) {
void Cartridge::parse_markup_hitachidsp(Markup::Node &root) {
if(root.exists() == false) return;
has_hitachidsp = true;
@ -338,12 +373,13 @@ void Cartridge::parse_markup_hitachidsp(XML::Node &root) {
if(node["id"].data == "rom") {
Mapping m({&HitachiDSP::rom_read, &hitachidsp}, {&HitachiDSP::rom_write, &hitachidsp});
parse_markup_map(m, node);
if(m.size == 0) m.size = hitachidsp.rom.size();
mapping.append(m);
}
}
}
void Cartridge::parse_markup_necdsp(XML::Node &root) {
void Cartridge::parse_markup_necdsp(Markup::Node &root) {
if(root.exists() == false) return;
has_necdsp = true;
@ -393,7 +429,7 @@ void Cartridge::parse_markup_necdsp(XML::Node &root) {
}
}
void Cartridge::parse_markup_epsonrtc(XML::Node &root) {
void Cartridge::parse_markup_epsonrtc(Markup::Node &root) {
if(root.exists() == false) return;
has_epsonrtc = true;
@ -412,7 +448,7 @@ void Cartridge::parse_markup_epsonrtc(XML::Node &root) {
}
}
void Cartridge::parse_markup_sharprtc(XML::Node &root) {
void Cartridge::parse_markup_sharprtc(Markup::Node &root) {
if(root.exists() == false) return;
has_sharprtc = true;
@ -431,7 +467,7 @@ void Cartridge::parse_markup_sharprtc(XML::Node &root) {
}
}
void Cartridge::parse_markup_spc7110(XML::Node &root) {
void Cartridge::parse_markup_spc7110(Markup::Node &root) {
if(root.exists() == false) return;
has_spc7110 = true;
@ -462,7 +498,7 @@ void Cartridge::parse_markup_spc7110(XML::Node &root) {
}
}
void Cartridge::parse_markup_sdd1(XML::Node &root) {
void Cartridge::parse_markup_sdd1(Markup::Node &root) {
if(root.exists() == false) return;
has_sdd1 = true;
@ -492,7 +528,7 @@ void Cartridge::parse_markup_sdd1(XML::Node &root) {
}
}
void Cartridge::parse_markup_obc1(XML::Node &root) {
void Cartridge::parse_markup_obc1(Markup::Node &root) {
if(root.exists() == false) return;
has_obc1 = true;
@ -509,7 +545,7 @@ void Cartridge::parse_markup_obc1(XML::Node &root) {
}
}
void Cartridge::parse_markup_msu1(XML::Node &root) {
void Cartridge::parse_markup_msu1(Markup::Node &root) {
if(root.exists() == false) return;
has_msu1 = true;
@ -525,22 +561,19 @@ void Cartridge::parse_markup_msu1(XML::Node &root) {
}
Cartridge::Mapping::Mapping() {
mode = Bus::MapMode::Direct;
banklo = bankhi = addrlo = addrhi = offset = size = 0;
size = base = mask = 0;
}
Cartridge::Mapping::Mapping(SuperFamicom::Memory &memory) {
read = {&SuperFamicom::Memory::read, &memory};
write = {&SuperFamicom::Memory::write, &memory};
mode = Bus::MapMode::Direct;
banklo = bankhi = addrlo = addrhi = offset = size = 0;
reader = {&SuperFamicom::Memory::read, &memory};
writer = {&SuperFamicom::Memory::write, &memory};
size = base = mask = 0;
}
Cartridge::Mapping::Mapping(const function<uint8 (unsigned)> &read_, const function<void (unsigned, uint8)> &write_) {
read = read_;
write = write_;
mode = Bus::MapMode::Direct;
banklo = bankhi = addrlo = addrhi = offset = size = 0;
Cartridge::Mapping::Mapping(const function<uint8 (unsigned)> &reader, const function<void (unsigned, uint8)> &writer) {
this->reader = reader;
this->writer = writer;
size = base = mask = 0;
}
#endif

View File

@ -6,8 +6,8 @@ void BSXSatellaview::init() {
}
void BSXSatellaview::load() {
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2188, 0x219f, {&BSXSatellaview::mmio_read, &bsxsatellaview}, {&BSXSatellaview::mmio_write, &bsxsatellaview});
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2188, 0x219f, {&BSXSatellaview::mmio_read, &bsxsatellaview}, {&BSXSatellaview::mmio_write, &bsxsatellaview});
bus.map({&BSXSatellaview::mmio_read, &bsxsatellaview}, {&BSXSatellaview::mmio_write, &bsxsatellaview}, 0x00, 0x3f, 0x2188, 0x219f);
bus.map({&BSXSatellaview::mmio_read, &bsxsatellaview}, {&BSXSatellaview::mmio_write, &bsxsatellaview}, 0x80, 0xbf, 0x2188, 0x219f);
}
void BSXSatellaview::unload() {

View File

@ -7,6 +7,7 @@ struct Coprocessor : Thread {
#include <sfc/chip/bsx/bsx.hpp>
#include <sfc/chip/sufamiturbo/sufamiturbo.hpp>
#include <sfc/chip/nss/nss.hpp>
#include <sfc/chip/event/event.hpp>
#include <sfc/chip/sa1/sa1.hpp>
#include <sfc/chip/superfx/superfx.hpp>

View File

@ -0,0 +1,70 @@
#include <sfc/sfc.hpp>
#define EVENT_CPP
namespace SuperFamicom {
Event event;
void Event::Enter() { event.enter(); }
void Event::enter() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
}
step(1);
synchronize_cpu();
}
void Event::init() {
}
void Event::load() {
}
void Event::unload() {
rom[0].reset();
rom[1].reset();
rom[2].reset();
rom[3].reset();
ram.reset();
}
void Event::power() {
}
void Event::reset() {
create(Event::Enter, 1);
}
//DSP-1
uint8 Event::sr(unsigned addr) {
return 0;
}
//DSP-1
void Event::dr(unsigned addr, uint8 data) {
}
//is there bank-switching?
uint8 Event::rom_read(unsigned addr) {
return cpu.regs.mdr;
}
//is there read-protection?
uint8 Event::ram_read(unsigned addr) {
return cpu.regs.mdr;
}
//is there write-protection?
void Event::ram_write(unsigned addr, uint8 data) {
}
void Event::serialize(serializer &s) {
Thread::serialize(s);
s.array(ram.data(), ram.size());
}
}

View File

@ -0,0 +1,37 @@
//SNES-EVENT board emulation (skeleton):
//* Campus Challenge '92
//* Powerfest '94
struct Event : Coprocessor {
MappedRAM rom[4];
MappedRAM ram;
static void Enter();
void enter();
void init();
void load();
void unload();
void power();
void reset();
uint8 sr(unsigned);
void dr(unsigned, uint8 data);
uint8 rom_read(unsigned addr);
uint8 ram_read(unsigned addr);
void ram_write(unsigned addr, uint8 data);
void serialize(serializer&);
//private:
enum class Board : unsigned { CampusChallenge92, Powerfest94 } board;
unsigned revision;
unsigned timer;
string host;
unsigned port;
string path;
string username;
string password;
};
extern Event event;

View File

@ -14,8 +14,8 @@ void SDD1::init() {
void SDD1::load() {
//hook S-CPU DMA MMIO registers to gather information for struct dma[];
//buffer address and transfer size information for use in SDD1::mcu_read()
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4300, 0x437f, {&SDD1::read, &sdd1}, {&SDD1::write, &sdd1});
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, {&SDD1::read, &sdd1}, {&SDD1::write, &sdd1});
bus.map({&SDD1::read, &sdd1}, {&SDD1::write, &sdd1}, 0x00, 0x3f, 0x4300, 0x437f);
bus.map({&SDD1::read, &sdd1}, {&SDD1::write, &sdd1}, 0x80, 0xbf, 0x4300, 0x437f);
}
void SDD1::unload() {

View File

@ -96,27 +96,27 @@ void CPU::op_step() {
}
void CPU::enable() {
function<uint8 (unsigned)> read = {&CPU::mmio_read, (CPU*)&cpu};
function<void (unsigned, uint8)> write = {&CPU::mmio_write, (CPU*)&cpu};
function<uint8 (unsigned)> reader = {&CPU::mmio_read, (CPU*)&cpu};
function<void (unsigned, uint8)> writer = {&CPU::mmio_write, (CPU*)&cpu};
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2140, 0x2183, read, write);
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2140, 0x2183, read, write);
bus.map(reader, writer, 0x00, 0x3f, 0x2140, 0x2183);
bus.map(reader, writer, 0x80, 0xbf, 0x2140, 0x2183);
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4016, 0x4017, read, write);
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4016, 0x4017, read, write);
bus.map(reader, writer, 0x00, 0x3f, 0x4016, 0x4017);
bus.map(reader, writer, 0x80, 0xbf, 0x4016, 0x4017);
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4200, 0x421f, read, write);
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4200, 0x421f, read, write);
bus.map(reader, writer, 0x00, 0x3f, 0x4200, 0x421f);
bus.map(reader, writer, 0x80, 0xbf, 0x4200, 0x421f);
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4300, 0x437f, read, write);
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, read, write);
bus.map(reader, writer, 0x00, 0x3f, 0x4300, 0x437f);
bus.map(reader, writer, 0x80, 0xbf, 0x4300, 0x437f);
read = [](unsigned addr) { return cpu.wram[addr]; };
write = [](unsigned addr, uint8 data) { cpu.wram[addr] = data; };
reader = [](unsigned addr) { return cpu.wram[addr]; };
writer = [](unsigned addr, uint8 data) { cpu.wram[addr] = data; };
bus.map(Bus::MapMode::Linear, 0x00, 0x3f, 0x0000, 0x1fff, read, write, 0x000000, 0x002000);
bus.map(Bus::MapMode::Linear, 0x80, 0xbf, 0x0000, 0x1fff, read, write, 0x000000, 0x002000);
bus.map(Bus::MapMode::Linear, 0x7e, 0x7f, 0x0000, 0xffff, read, write);
bus.map(reader, writer, 0x00, 0x3f, 0x0000, 0x1fff, 0x002000);
bus.map(reader, writer, 0x80, 0xbf, 0x0000, 0x1fff, 0x002000);
bus.map(reader, writer, 0x7e, 0x7f, 0x0000, 0xffff, 0x020000);
}
void CPU::power() {

View File

@ -29,6 +29,11 @@ unsigned Interface::group(unsigned id) {
return 0;
case ID::ROM:
case ID::RAM:
case ID::EventROM0:
case ID::EventROM1:
case ID::EventROM2:
case ID::EventROM3:
case ID::EventRAM:
case ID::SA1ROM:
case ID::SA1IRAM:
case ID::SA1BWRAM:
@ -97,6 +102,12 @@ void Interface::load(unsigned id, const stream &stream, const string &manifest)
if(id == ID::ROM) cartridge.rom.read(stream);
if(id == ID::RAM) cartridge.ram.read(stream);
if(id == ID::EventROM0) event.rom[0].read(stream);
if(id == ID::EventROM1) event.rom[1].read(stream);
if(id == ID::EventROM2) event.rom[2].read(stream);
if(id == ID::EventROM3) event.rom[3].read(stream);
if(id == ID::EventRAM) event.ram.read(stream);
if(id == ID::SA1ROM) sa1.rom.read(stream);
if(id == ID::SA1IRAM) sa1.iram.read(stream);
if(id == ID::SA1BWRAM) sa1.bwram.read(stream);
@ -175,6 +186,7 @@ void Interface::load(unsigned id, const stream &stream, const string &manifest)
void Interface::save(unsigned id, const stream &stream) {
if(id == ID::RAM) stream.write(cartridge.ram.data(), cartridge.ram.size());
if(id == ID::EventRAM) stream.write(event.ram.data(), event.ram.size());
if(id == ID::SA1IRAM) stream.write(sa1.iram.data(), sa1.iram.size());
if(id == ID::SA1BWRAM) stream.write(sa1.bwram.data(), sa1.bwram.size());
if(id == ID::SuperFXRAM) stream.write(superfx.ram.data(), superfx.ram.size());

View File

@ -18,6 +18,12 @@ struct ID {
ROM,
RAM,
EventROM0,
EventROM1,
EventROM2,
EventROM3,
EventRAM,
SA1ROM,
SA1IRAM,
SA1BWRAM,

View File

@ -73,6 +73,14 @@ unsigned Bus::mirror(unsigned addr, unsigned size) {
return base;
}
unsigned Bus::recode(unsigned addr, unsigned mask) {
for(unsigned n = 0; n < 24; n++) {
unsigned bit = 1 << n;
if(mask & bit) addr = ((addr >> (n + 1)) << n) | (addr & (bit - 1));
}
return addr;
}
uint8 Bus::read(unsigned addr) {
if(cheat.override[addr]) return cheat.read(addr);
return reader[lookup[addr]](target[addr]);

View File

@ -6,30 +6,26 @@ namespace SuperFamicom {
Bus bus;
void Bus::map(
MapMode mode,
unsigned bank_lo, unsigned bank_hi,
unsigned addr_lo, unsigned addr_hi,
const function<uint8 (unsigned)> &rd,
const function<void (unsigned, uint8)> &wr,
unsigned base, unsigned length
const function<uint8 (unsigned)> &reader,
const function<void (unsigned, uint8)> &writer,
unsigned banklo, unsigned bankhi,
unsigned addrlo, unsigned addrhi,
unsigned size, unsigned base, unsigned mask
) {
assert(bank_lo <= bank_hi && bank_lo <= 0xff);
assert(addr_lo <= addr_hi && addr_lo <= 0xffff);
assert(banklo <= bankhi && banklo <= 0xff);
assert(addrlo <= addrhi && addrlo <= 0xffff);
assert(idcount < 255);
unsigned id = idcount++;
assert(id < 255);
reader[id] = rd;
writer[id] = wr;
this->reader[id] = reader;
this->writer[id] = writer;
if(length == 0) length = (bank_hi - bank_lo + 1) * (addr_hi - addr_lo + 1);
unsigned offset = 0;
for(unsigned bank = bank_lo; bank <= bank_hi; bank++) {
for(unsigned addr = addr_lo; addr <= addr_hi; addr++) {
unsigned destaddr = (bank << 16) | addr;
if(mode == MapMode::Linear) destaddr = mirror(base + offset++, length);
if(mode == MapMode::Shadow) destaddr = mirror(base + destaddr, length);
lookup[(bank << 16) | addr] = id;
target[(bank << 16) | addr] = destaddr;
for(unsigned bank = banklo; bank <= bankhi; bank++) {
for(unsigned addr = addrlo; addr <= addrhi; addr++) {
unsigned offset = recode(bank << 16 | addr, mask);
if(size) offset = base + mirror(offset, size - base);
lookup[bank << 16 | addr] = id;
target[bank << 16 | addr] = offset;
}
}
}
@ -39,12 +35,25 @@ void Bus::map_reset() {
function<void (unsigned, uint8)> writer = [](unsigned, uint8) {};
idcount = 0;
map(MapMode::Direct, 0x00, 0xff, 0x0000, 0xffff, reader, writer);
map(reader, writer, 0x00, 0xff, 0x0000, 0xffff);
}
void Bus::map_xml() {
for(auto &m : cartridge.mapping) {
map(m.mode, m.banklo, m.bankhi, m.addrlo, m.addrhi, m.read, m.write, m.offset, m.size);
lstring part = m.addr.split<1>(":");
lstring banks = part(0).split(",");
lstring addrs = part(1).split(",");
for(auto &bank : banks) {
for(auto &addr : addrs) {
lstring bankpart = bank.split<1>("-");
lstring addrpart = addr.split<1>("-");
unsigned banklo = hex(bankpart(0));
unsigned bankhi = hex(bankpart(1, bankpart(0)));
unsigned addrlo = hex(addrpart(0));
unsigned addrhi = hex(addrpart(1, addrpart(0)));
map(m.reader, m.writer, banklo, bankhi, addrlo, addrhi, m.size, m.base, m.mask);
}
}
}
}

View File

@ -44,6 +44,7 @@ private:
struct Bus {
alwaysinline static unsigned mirror(unsigned addr, unsigned size);
alwaysinline static unsigned recode(unsigned addr, unsigned mask);
alwaysinline uint8 read(unsigned addr);
alwaysinline void write(unsigned addr, uint8 data);
@ -55,14 +56,12 @@ struct Bus {
function<uint8 (unsigned)> reader[256];
function<void (unsigned, uint8)> writer[256];
enum class MapMode : unsigned { Direct, Linear, Shadow };
void map(
MapMode mode,
unsigned bank_lo, unsigned bank_hi,
unsigned addr_lo, unsigned addr_hi,
const function<uint8 (unsigned)> &read,
const function<void (unsigned, uint8)> &write,
unsigned base = 0, unsigned length = 0
const function<uint8 (unsigned)> &reader,
const function<void (unsigned, uint8)> &writer,
unsigned banklo, unsigned bankhi,
unsigned addrlo, unsigned addrhi,
unsigned size = 0, unsigned base = 0, unsigned mask = 0
);
void map_reset();

View File

@ -79,11 +79,11 @@ void PPU::add_clocks(unsigned clocks) {
}
void PPU::enable() {
function<uint8 (unsigned)> read = { &PPU::mmio_read, (PPU*)&ppu };
function<void (unsigned, uint8)> write = { &PPU::mmio_write, (PPU*)&ppu };
function<uint8 (unsigned)> reader = { &PPU::mmio_read, (PPU*)&ppu };
function<void (unsigned, uint8)> writer = { &PPU::mmio_write, (PPU*)&ppu };
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, read, write);
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, read, write);
bus.map(reader, writer, 0x00, 0x3f, 0x2100, 0x213f);
bus.map(reader, writer, 0x80, 0xbf, 0x2100, 0x213f);
}
void PPU::power() {

View File

@ -60,6 +60,7 @@ void System::serialize_all(serializer &s) {
if(cartridge.has_gb_slot()) icd2.serialize(s);
if(cartridge.has_bs_cart()) bsxcartridge.serialize(s);
if(cartridge.has_st_slots()) sufamiturbo.serialize(s);
if(cartridge.has_event()) event.serialize(s);
if(cartridge.has_sa1()) sa1.serialize(s);
if(cartridge.has_superfx()) superfx.serialize(s);
if(cartridge.has_armdsp()) armdsp.serialize(s);

View File

@ -69,6 +69,7 @@ void System::init() {
bsxcartridge.init();
bsxflash.init();
nss.init();
event.init();
sa1.init();
superfx.init();
armdsp.init();
@ -124,6 +125,7 @@ void System::load() {
if(cartridge.has_bs_slot()) bsxflash.load();
if(cartridge.has_st_slots()) sufamiturbo.load();
if(cartridge.has_nss_dip()) nss.load();
if(cartridge.has_event()) event.load();
if(cartridge.has_sa1()) sa1.load();
if(cartridge.has_superfx()) superfx.load();
if(cartridge.has_armdsp()) armdsp.load();
@ -147,6 +149,7 @@ void System::unload() {
if(cartridge.has_bs_slot()) bsxflash.unload();
if(cartridge.has_st_slots()) sufamiturbo.unload();
if(cartridge.has_nss_dip()) nss.unload();
if(cartridge.has_event()) event.unload();
if(cartridge.has_sa1()) sa1.unload();
if(cartridge.has_superfx()) superfx.unload();
if(cartridge.has_armdsp()) armdsp.unload();
@ -173,6 +176,7 @@ void System::power() {
if(cartridge.has_bs_cart()) bsxcartridge.power();
if(cartridge.has_bs_slot()) bsxflash.power();
if(cartridge.has_nss_dip()) nss.power();
if(cartridge.has_event()) event.power();
if(cartridge.has_sa1()) sa1.power();
if(cartridge.has_superfx()) superfx.power();
if(cartridge.has_armdsp()) armdsp.power();
@ -199,6 +203,7 @@ void System::reset() {
if(cartridge.has_bs_cart()) bsxcartridge.reset();
if(cartridge.has_bs_slot()) bsxflash.reset();
if(cartridge.has_nss_dip()) nss.reset();
if(cartridge.has_event()) event.reset();
if(cartridge.has_sa1()) sa1.reset();
if(cartridge.has_superfx()) superfx.reset();
if(cartridge.has_armdsp()) armdsp.reset();
@ -212,6 +217,7 @@ void System::reset() {
if(cartridge.has_msu1()) msu1.reset();
if(cartridge.has_gb_slot()) cpu.coprocessors.append(&icd2);
if(cartridge.has_event()) cpu.coprocessors.append(&event);
if(cartridge.has_sa1()) cpu.coprocessors.append(&sa1);
if(cartridge.has_superfx()) cpu.coprocessors.append(&superfx);
if(cartridge.has_armdsp()) cpu.coprocessors.append(&armdsp);

View File

@ -21,7 +21,7 @@ DipSwitches::DipSwitches() {
onClose = accept.onActivate = [&] { quit = true; };
}
unsigned DipSwitches::run(const XML::Node &node) {
unsigned DipSwitches::run(const Markup::Node &node) {
audio.clear();
setModal(true);
quit = false;

View File

@ -15,7 +15,7 @@ struct DipSwitches : Window {
Widget spacer;
Button accept;
unsigned run(const XML::Node &node);
unsigned run(const Markup::Node &node);
DipSwitches();
private:

View File

@ -107,7 +107,7 @@ int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) {
return inputManager->inputMap[guid]->poll();
}
unsigned Interface::dipSettings(const XML::Node &node) {
unsigned Interface::dipSettings(const Markup::Node &node) {
return dipSwitches->run(node);
}

View File

@ -6,7 +6,7 @@ struct Interface : Emulator::Interface::Bind {
void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height);
void audioSample(int16_t lsample, int16_t rsample);
int16_t inputPoll(unsigned port, unsigned device, unsigned input);
unsigned dipSettings(const XML::Node &node);
unsigned dipSettings(const Markup::Node &node);
string path(unsigned group);
void notify(const string &text);
};

View File

@ -1,2 +1,2 @@
1 24 "../data/bsnes.Manifest"
2 ICON DISCARDABLE "../data/bsnes.ico"
1 24 "../data/higan.Manifest"
2 ICON DISCARDABLE "../data/higan.ico"