Update to v082r26 release.

byuu says:

.cht files now use BML-formatted data. I'm still going to request the
cheats.xml file as-is, and will write my own converter for embedding
during releases.
This is where parsing 2MB markup files in 10ms is really going to be
nice. Had to use an evil hack before for actually searching for games.

This has the start of the board/chip separation from mappers for NES,
and it has a barebones iNES->board markup converter.
You can specify your own board markup and bypass the need for an iNES
header, so in other words it will load No-Intro style games with
a proper board file.
Long-term, we'll have an internal database for commercial boards, and
probably folder.fc/prg.rom{,chr.rom} loading support.

Since they can't co-exist, the mappers are currently disabled, and I've
only ported the easy ones. So no MMC1/MMC3/VRC6 in this release. I need
to make them into chips first.
This commit is contained in:
Tim Allen 2011-09-29 22:44:49 +10:00
parent e8b1af0917
commit 7115047d85
23 changed files with 597 additions and 285 deletions

View File

@ -34,7 +34,7 @@ protected:
unsigned sourceSize; unsigned sourceSize;
public: public:
inline lzss() : sourceData(nullptr), sourceSize(nullptr) {} inline lzss() : sourceData(nullptr), sourceSize(0) {}
}; };
void lzss::source(const uint8_t *data, unsigned size) { void lzss::source(const uint8_t *data, unsigned size) {

View File

@ -20,7 +20,9 @@
#include <nall/windows/utf8.hpp> #include <nall/windows/utf8.hpp>
#define NALL_STRING_INTERNAL_HPP
#include <nall/string/base.hpp> #include <nall/string/base.hpp>
#include <nall/string/bml.hpp>
#include <nall/string/bsv.hpp> #include <nall/string/bsv.hpp>
#include <nall/string/core.hpp> #include <nall/string/core.hpp>
#include <nall/string/cast.hpp> #include <nall/string/cast.hpp>
@ -38,5 +40,6 @@
#include <nall/string/variadic.hpp> #include <nall/string/variadic.hpp>
#include <nall/string/wrapper.hpp> #include <nall/string/wrapper.hpp>
#include <nall/string/xml.hpp> #include <nall/string/xml.hpp>
#undef NALL_STRING_INTERNAL_HPP
#endif #endif

127
bsnes/nall/string/bml.hpp Executable file
View File

@ -0,0 +1,127 @@
#ifdef NALL_STRING_INTERNAL_HPP
//BML parser
//version 0.02
namespace nall {
namespace BML {
struct Node : linear_vector<Node> {
Node *parent;
const char *cname;
const char *cvalue;
inline string name() { return cname; }
inline string value() { return cvalue; }
private:
inline bool valid(char p) const {
if(p >= 'A' && p <= 'Z') return true;
if(p >= 'a' && p <= 'z') return true;
if(p >= '0' && p <= '9') return true;
if(p == '+' || p == '-') return true;
if(p == '.' || p == '_') return true;
if(p == ':') return true;
return false;
}
inline bool space(char p) const {
return p == ' ' || p == '\t';
}
inline unsigned parseDepth(char *p) {
unsigned depth = 0;
while(space(*p)) depth++, p++;
return depth;
}
inline void parseNode(char *&p) {
if(valid(*p)) {
cname = p;
while(valid(*p)) p++;
if(*p != '=') return;
*p++ = 0;
}
if(*p == '\n') throw "Missing node value";
if(valid(*p)) {
cvalue = p;
while(valid(*p)) p++;
} else {
char terminal = *p++;
cvalue = p;
while(*p && *p != terminal) p++;
if(*p == 0) throw "Unclosed terminal";
*p++ = 0;
}
}
inline void parseLine(char *&p) {
unsigned depth = parseDepth(p);
while(space(*p)) p++;
parseNode(p);
while(space(*p)) {
*p++ = 0;
Node node(this);
node.parseNode(p);
append(node);
}
if(*p == '\n') *p++ = 0;
while(parseDepth(p) > depth) {
Node node(this);
node.parseLine(p);
append(node);
}
}
inline void parse(char *&p, unsigned parentDepth = 0) {
while(*p) {
while(*p == '\n') *p++ = 0;
Node node(this);
node.parseLine(p);
append(node);
}
}
public:
inline Node& operator[](const string &name) {
for(auto &node : *this) {
if(!strcmp(node.cname, name)) return node;
}
static Node node;
return node;
}
inline bool exists() {
return parent != nullptr;
}
inline string content(const string &separator = "\n") const {
string result;
for(auto &node : *this) result.append(node.cvalue, separator);
result.rtrim<1>(separator);
return result;
}
inline Node(const string &document) : parent(nullptr), cname(nullptr), cvalue(nullptr) {
char *p = strdup(document);
cvalue = p;
try {
parse(p);
} catch(const char *error) {
reset();
}
}
inline Node(Node *parent) : parent(parent), cname(""), cvalue("") {}
inline Node() : parent(nullptr), cname(""), cvalue("") {}
inline ~Node() { if(cname == nullptr && cvalue) free((void*)cvalue); }
};
}
}
#endif

View File

@ -1,74 +1,75 @@
#ifndef NALL_STRING_BSV_HPP #ifdef NALL_STRING_INTERNAL_HPP
#define NALL_STRING_BSV_HPP
//BSV parser //BSV parser
//version 0.01 //version 0.02
namespace nall { namespace nall {
inline string bsv_decode(const char *input) { struct BSV {
string output; static inline string decode(const char *input) {
unsigned offset = 0; string output;
while(*input) { unsigned offset = 0;
//illegal characters while(*input) {
if(*input == '}' ) return ""; //illegal characters
if(*input == '\r') return ""; if(*input == '}' ) return "";
if(*input == '\n') return ""; if(*input == '\r') return "";
if(*input == '\n') return "";
//normal characters //normal characters
if(*input != '{') { output[offset++] = *input++; continue; } if(*input != '{') { output[offset++] = *input++; continue; }
//entities //entities
if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; } if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; }
if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; } if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; }
if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; } if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; }
//illegal entities //illegal entities
return ""; return "";
}
output[offset] = 0;
return output;
} }
output[offset] = 0;
return output;
}
inline string bsv_encode(const char *input) { static inline string encode(const char *input) {
string output; string output;
unsigned offset = 0; unsigned offset = 0;
while(*input) { while(*input) {
//illegal characters //illegal characters
if(*input == '\r') return ""; if(*input == '\r') return "";
if(*input == '\n') { if(*input == '\n') {
output[offset++] = '{'; output[offset++] = '{';
output[offset++] = 'l'; output[offset++] = 'l';
output[offset++] = 'f'; output[offset++] = 'f';
output[offset++] = '}'; output[offset++] = '}';
input++; input++;
continue; continue;
}
if(*input == '{') {
output[offset++] = '{';
output[offset++] = 'l';
output[offset++] = 'b';
output[offset++] = '}';
input++;
continue;
}
if(*input == '}') {
output[offset++] = '{';
output[offset++] = 'r';
output[offset++] = 'b';
output[offset++] = '}';
input++;
continue;
}
output[offset++] = *input++;
} }
output[offset] = 0;
if(*input == '{') { return output;
output[offset++] = '{';
output[offset++] = 'l';
output[offset++] = 'b';
output[offset++] = '}';
input++;
continue;
}
if(*input == '}') {
output[offset++] = '{';
output[offset++] = 'r';
output[offset++] = 'b';
output[offset++] = '}';
input++;
continue;
}
output[offset++] = *input++;
} }
output[offset] = 0; };
return output;
}
} }

View File

@ -1,6 +1,5 @@
nes_objects := nes-interface nes-system nes-scheduler nes-input nes_objects := nes-interface nes-system nes-scheduler nes-input
nes_objects += nes-mapper nes-cartridge nes-memory nes_objects += nes-memory nes-cartridge nes-cpu nes-apu nes-ppu
nes_objects += nes-cpu nes-apu nes-ppu
nes_objects += nes-cheat nes_objects += nes-cheat
objects += $(nes_objects) objects += $(nes_objects)
@ -8,9 +7,8 @@ obj/nes-interface.o: $(nes)/interface/interface.cpp $(call rwildcard,$(nes)/inte
obj/nes-system.o: $(nes)/system/system.cpp $(call rwildcard,$(nes)/system/) obj/nes-system.o: $(nes)/system/system.cpp $(call rwildcard,$(nes)/system/)
obj/nes-scheduler.o: $(nes)/scheduler/scheduler.cpp $(call rwildcard,$(nes)/scheduler/) obj/nes-scheduler.o: $(nes)/scheduler/scheduler.cpp $(call rwildcard,$(nes)/scheduler/)
obj/nes-input.o: $(nes)/input/input.cpp $(call rwildcard,$(nes)/input/) obj/nes-input.o: $(nes)/input/input.cpp $(call rwildcard,$(nes)/input/)
obj/nes-mapper.o: $(nes)/mapper/mapper.cpp $(call rwildcard,$(nes)/mapper/)
obj/nes-cartridge.o: $(nes)/cartridge/cartridge.cpp $(call rwildcard,$(nes)/cartridge/)
obj/nes-memory.o: $(nes)/memory/memory.cpp $(call rwildcard,$(nes)/memory/) obj/nes-memory.o: $(nes)/memory/memory.cpp $(call rwildcard,$(nes)/memory/)
obj/nes-cartridge.o: $(nes)/cartridge/cartridge.cpp $(call rwildcard,$(nes)/cartridge/)
obj/nes-cpu.o: $(nes)/cpu/cpu.cpp $(call rwildcard,$(nes)/cpu/) obj/nes-cpu.o: $(nes)/cpu/cpu.cpp $(call rwildcard,$(nes)/cpu/)
obj/nes-apu.o: $(nes)/apu/apu.cpp $(call rwildcard,$(nes)/apu/) obj/nes-apu.o: $(nes)/apu/apu.cpp $(call rwildcard,$(nes)/apu/)
obj/nes-ppu.o: $(nes)/ppu/ppu.cpp $(call rwildcard,$(nes)/ppu/) obj/nes-ppu.o: $(nes)/ppu/ppu.cpp $(call rwildcard,$(nes)/ppu/)

View File

@ -0,0 +1,50 @@
//NES-AMROM
//NES-ANROM
//NES-AN1ROM
//NES-AOROM
struct AxROM : Board {
uint4 prg_bank;
bool mirror_select;
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) return Board::prg_read((prg_bank << 15) | (addr & 0x7fff));
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if(addr & 0x8000) {
prg_bank = data & 0x0f;
mirror_select = data & 0x10;
}
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) return ppu.ciram_read((mirror_select << 10) | (addr & 0x03ff));
return Board::chr_read(addr);
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) return ppu.ciram_write((mirror_select << 10) | (addr & 0x03ff), data);
return Board::chr_write(addr, data);
}
void power() {
reset();
}
void reset() {
prg_bank = 0x0f;
mirror_select = 0;
}
void serialize(serializer &s) {
s.integer(prg_bank);
s.integer(mirror_select);
}
AxROM(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size) {
}
};

View File

@ -1,4 +1,8 @@
#include "axrom.cpp"
#include "cnrom.cpp"
#include "nrom.cpp" #include "nrom.cpp"
#include "sxrom.cpp"
#include "uxrom.cpp"
unsigned Board::mirror(unsigned addr, unsigned size) const { unsigned Board::mirror(unsigned addr, unsigned size) const {
unsigned base = 0; unsigned base = 0;
@ -18,73 +22,94 @@ unsigned Board::mirror(unsigned addr, unsigned size) const {
return base; return base;
} }
void Board::main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
cartridge.clock += 12 * 4095;
tick();
}
}
void Board::tick() {
cartridge.clock += 12;
if(cartridge.clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
}
uint8 Board::prg_read(unsigned addr) { uint8 Board::prg_read(unsigned addr) {
return prg.data[mirror(addr, prg.size)]; return prgrom.data[mirror(addr, prgrom.size)];
} }
void Board::prg_write(unsigned addr, uint8 data) { void Board::prg_write(unsigned addr, uint8 data) {
prg.data[mirror(addr, prg.size)] = data; prgrom.data[mirror(addr, prgrom.size)] = data;
} }
uint8 Board::chr_read(unsigned addr) { uint8 Board::chr_read(unsigned addr) {
return chr.data[mirror(addr, chr.size)]; if(chrrom.size) return chrrom.data[mirror(addr, chrrom.size)];
if(chrram.size) return chrram.data[mirror(addr, chrram.size)];
return 0u;
} }
void Board::chr_write(unsigned addr, uint8 data) { void Board::chr_write(unsigned addr, uint8 data) {
chr.data[mirror(addr, chr.size)] = data; if(chrram.size) chrram.data[mirror(addr, chrram.size)] = data;
} }
Board* Board::create(const string &xml, const uint8_t *data, unsigned size) { Board::Memory Board::memory() {
string type; return prgram;
string configuration; }
xml_element document = xml_parse(xml); void Board::power() {
for(auto &head : document.element) { }
if(head.name == "cartridge") {
for(auto &node : head.element) { void Board::reset() {
if(node.name == "board") { }
configuration = node.content;
for(auto &attr : node.attribute) { void Board::serialize(serializer &s) {
if(attr.name == "type") type = attr.parse(); if(prgram.size) s.array(prgram.data, prgram.size);
} if(chrram.size) s.array(chrram.data, chrram.size);
} }
}
} Board::Board(BML::Node &board, const uint8_t *data, unsigned size) {
} information.type = board["type"].value();
information.battery = board["prg"]["battery"].value();
Board *board = nullptr;
if(type == "NES-NROM-256") board = new NROM; prgrom.size = decimal(board["prg"]["rom"].value());
assert(board != nullptr); prgram.size = decimal(board["prg"]["ram"].value());
chrrom.size = decimal(board["chr"]["rom"].value());
for(auto &head : document.element) { chrram.size = decimal(board["chr"]["ram"].value());
if(head.name == "cartridge") {
for(auto &node : head.element) { if(prgrom.size) prgrom.data = new uint8[prgrom.size]();
if(node.name == "board") { if(prgram.size) prgram.data = new uint8[prgram.size]();
for(auto &leaf : node.element) { if(chrrom.size) chrrom.data = new uint8[chrrom.size]();
if(leaf.name == "prg") { if(chrram.size) chrram.data = new uint8[chrram.size]();
for(auto &attr : leaf.attribute) {
if(attr.name == "size") board->prg.size = decimal(attr.content); if(prgrom.size) memcpy(prgrom.data, data, prgrom.size);
} if(chrrom.size) memcpy(chrrom.data, data + prgrom.size, chrrom.size);
} }
if(leaf.name == "chr") { Board::~Board() {
for(auto &attr : leaf.attribute) { if(prgrom.size) delete[] prgrom.data;
if(attr.name == "size") board->chr.size = decimal(attr.content); if(prgram.size) delete[] prgram.data;
} if(chrrom.size) delete[] chrrom.data;
} if(chrram.size) delete[] chrram.data;
} }
}
} Board* Board::load(const string &markup, const uint8_t *data, unsigned size) {
} BML::Node document(markup);
} auto &board = document["cartridge"]["board"];
string type = board["type"].value();
board->prg.data = new uint8[board->prg.size];
memcpy(board->prg.data, data, board->prg.size); if(type == "NES-AMROM" ) return new AxROM(board, data, size);
if(type == "NES-ANROM" ) return new AxROM(board, data, size);
board->chr.data = new uint8[board->chr.size]; if(type == "NES-AN1ROM" ) return new AxROM(board, data, size);
memcpy(board->chr.data, data + board->prg.size, board->chr.size); if(type == "NES-AOROM" ) return new AxROM(board, data, size);
if(type == "NES-CNROM" ) return new CNROM(board, data, size);
board->configure({ "<board>\n", configuration, "</board>\n" }); if(type == "NES-NROM-256") return new NROM(board, data, size);
if(type == "NES-UNROM" ) return new UxROM(board, data, size);
return board; if(type == "NES-SXROM" ) return new SxROM(board, data, size);
if(type == "NES-UOROM" ) return new UxROM(board, data, size);
return nullptr;
} }

View File

@ -1,25 +1,40 @@
struct Board { struct Board {
struct Memory {
uint8_t *data;
unsigned size;
inline Memory() : data(nullptr), size(0u) {}
};
unsigned mirror(unsigned addr, unsigned size) const; unsigned mirror(unsigned addr, unsigned size) const;
virtual void main();
virtual void tick();
virtual uint8 prg_read(unsigned addr); virtual uint8 prg_read(unsigned addr);
virtual void prg_write(unsigned addr, uint8 data); virtual void prg_write(unsigned addr, uint8 data);
virtual uint8 chr_read(unsigned addr); virtual uint8 chr_read(unsigned addr);
virtual void chr_write(unsigned addr, uint8 data); virtual void chr_write(unsigned addr, uint8 data);
virtual void configure(const string &xml) = 0; virtual Memory memory();
static Board* create(const string &xml, const uint8_t *data, unsigned size); virtual void power();
virtual void reset();
virtual void serialize(serializer&);
Board(BML::Node &board, const uint8_t *data, unsigned size);
~Board();
static Board* load(const string &markup, const uint8_t *data, unsigned size);
struct Information { struct Information {
string type; string type;
bool battery;
} information; } information;
struct Memory { protected:
uint8_t *data; Memory prgrom;
unsigned size; Memory prgram;
}; Memory chrrom;
Memory chrram;
Memory prg;
Memory chr;
}; };

View File

@ -0,0 +1,54 @@
//NES-CNROM
struct CNROM : Board {
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint2 chr_bank;
uint8 prg_read(unsigned addr) {
if(addr & 0x8000) return Board::prg_read(addr & 0x7fff);
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if(addr & 0x8000) chr_bank = data & 0x03;
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
return Board::chr_read(addr);
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
Board::chr_write(addr, data);
}
void power() {
reset();
}
void reset() {
chr_bank = 0;
}
void serialize(serializer &s) {
s.integer(chr_bank);
}
CNROM(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size) {
settings.mirror = board["mirror"].value() == "vertical" ? 1 : 0;
}
};

View File

@ -1,7 +1,10 @@
//NES-NROM-128
//NES-NROM-256
struct NROM : Board { struct NROM : Board {
struct Settings { struct Settings {
enum class Mirror : unsigned { Horizontal, Vertical } mirror; bool mirror; //0 = horizontal, 1 = vertical
} settings; } settings;
uint8 prg_read(unsigned addr) { uint8 prg_read(unsigned addr) {
@ -14,7 +17,7 @@ void prg_write(unsigned addr, uint8 data) {
uint8 chr_read(unsigned addr) { uint8 chr_read(unsigned addr) {
if(addr & 0x2000) { if(addr & 0x2000) {
if(settings.mirror == Settings::Mirror::Horizontal) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff); return ppu.ciram_read(addr & 0x07ff);
} }
return Board::chr_read(addr); return Board::chr_read(addr);
@ -22,26 +25,14 @@ uint8 chr_read(unsigned addr) {
void chr_write(unsigned addr, uint8 data) { void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) { if(addr & 0x2000) {
if(settings.mirror == Settings::Mirror::Horizontal) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr, data); return ppu.ciram_write(addr & 0x07ff, data);
} }
return Board::chr_write(addr, data); return Board::chr_write(addr, data);
} }
void configure(const string &xml) { NROM(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size) {
xml_element document = xml_parse(xml); settings.mirror = board["mirror"].value() == "vertical" ? 1 : 0;
for(auto &node : document.element) {
if(node.name == "mirror") {
for(auto &attr : node.attribute) {
if(attr.name == "type") {
if(attr.content == "horizontal") settings.mirror = Settings::Mirror::Horizontal;
if(attr.content == "vertical" ) settings.mirror = Settings::Mirror::Vertical;
}
}
}
}
} }
}; };
NROM nrom;

View File

@ -0,0 +1,29 @@
//NES-SAROM
//NES-SBROM
//NES-SCROM
//NES-SC1ROM
//NES-SEROM
//NES-SFROM
//NES-SGROM
//NES-SHROM
//NES-SH1ROM
//NES-SIROM
//NES-SJROM
//NES-SKROM
//NES-SLROM
//NES-SL1ROM
//NES-SL2ROM
//NES-SL3ROM
//NES-SLRROM
//NES-SMROM
//NES-SNROM
//NES-SOROM
//NES-SUROM
//NES-SXROM
struct SxROM : Board {
SxROM(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size) {
}
};

View File

@ -0,0 +1,54 @@
//NES-UNROM
//NES-UOROM
struct UxROM : Board {
struct Settings {
bool mirror; //0 = horizontal, 1 = vertical
} settings;
uint4 prg_bank;
uint8 prg_read(unsigned addr) {
if((addr & 0xc000) == 0x8000) return Board::prg_read((prg_bank << 14) | (addr & 0x3fff));
if((addr & 0xc000) == 0xc000) return Board::prg_read(( 0x0f << 14) | (addr & 0x3fff));
return cpu.mdr();
}
void prg_write(unsigned addr, uint8 data) {
if(addr & 0x8000) prg_bank = data & 0x0f;
}
uint8 chr_read(unsigned addr) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr);
}
return Board::chr_read(addr);
}
void chr_write(unsigned addr, uint8 data) {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr, data);
}
return Board::chr_write(addr, data);
}
void power() {
reset();
}
void reset() {
prg_bank = 0;
}
void serialize(serializer &s) {
s.integer(prg_bank);
}
UxROM(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size) {
settings.mirror = board["mirror"].value() == "vertical" ? 1 : 0;
}
};

View File

@ -2,10 +2,9 @@
namespace NES { namespace NES {
#include "ines.cpp"
#include "chip/chip.cpp"
#include "board/board.cpp" #include "board/board.cpp"
//#define BOARD
Cartridge cartridge; Cartridge cartridge;
void Cartridge::Main() { void Cartridge::Main() {
@ -13,92 +12,42 @@ void Cartridge::Main() {
} }
void Cartridge::main() { void Cartridge::main() {
mapper->main(); board->main();
} }
void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) { void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
#ifdef BOARD if((size & 0xff) == 0) {
rom_size = size; sha256 = nall::sha256(data, size);
rom_data = new uint8[rom_size]; board = Board::load(markup, data, size);
memcpy(rom_data, data, size);
#else
rom_size = size - 16;
rom_data = new uint8[rom_size];
memcpy(rom_data, data + 16, size - 16);
#endif
#ifdef BOARD
prg_size = 32768;
chr_size = 8192;
#else
prg_size = data[4] * 0x4000;
chr_size = data[5] * 0x2000;
#endif
prg_data = new uint8[prg_size];
memcpy(prg_data, rom_data, prg_size);
if(chr_size) {
chr_ram = false;
chr_data = new uint8[chr_size];
memcpy(chr_data, rom_data + prg_size, chr_size);
} else { } else {
chr_ram = true; board = Board::load(markup != "" ? markup : iNES(data, size), data + 16, size - 16);
chr_size = 0x2000;
chr_data = new uint8[chr_size]();
} }
mirroring = ((data[6] & 0x08) >> 2) | (data[6] & 0x01);
uint8 mapperNumber = ((data[7] >> 4) << 4) | (data[6] >> 4);
switch(mapperNumber) {
default : mapper = &Mapper::none; break;
case 1: mapper = &Mapper::mmc1; break;
case 2: mapper = &Mapper::uorom; break;
case 3: mapper = &Mapper::cnrom; break;
case 4: mapper = &Mapper::mmc3; break;
case 7: mapper = &Mapper::aorom; break;
case 16: mapper = &Mapper::bandaiFCG; break;
case 24: mapper = &Mapper::vrc6; Mapper::vrc6.abus_swap = 0; break;
case 26: mapper = &Mapper::vrc6; Mapper::vrc6.abus_swap = 1; break;
}
sha256 = nall::sha256(rom_data, rom_size);
#ifdef BOARD
board = Board::create(xml, rom_data, rom_size);
#endif
system.load(); system.load();
loaded = true; loaded = true;
} }
void Cartridge::unload() { void Cartridge::unload() {
if(loaded == false) return; if(loaded == false) return;
delete[] rom_data;
delete[] prg_data;
delete[] chr_data;
loaded = false; loaded = false;
} }
unsigned Cartridge::ram_size() { unsigned Cartridge::ram_size() {
return mapper->ram_size(); return board->memory().size;
} }
uint8* Cartridge::ram_data() { uint8* Cartridge::ram_data() {
return mapper->ram_data(); return board->memory().data;
} }
void Cartridge::power() { void Cartridge::power() {
create(Cartridge::Main, 21477272); create(Cartridge::Main, 21477272);
mapper->power(); board->power();
} }
void Cartridge::reset() { void Cartridge::reset() {
create(Cartridge::Main, 21477272); create(Cartridge::Main, 21477272);
mapper->reset(); board->reset();
} }
Cartridge::Cartridge() { Cartridge::Cartridge() {
@ -106,37 +55,23 @@ Cartridge::Cartridge() {
} }
uint8 Cartridge::prg_read(unsigned addr) { uint8 Cartridge::prg_read(unsigned addr) {
#ifdef BOARD
return board->prg_read(addr); return board->prg_read(addr);
#endif
return mapper->prg_read(addr);
} }
void Cartridge::prg_write(unsigned addr, uint8 data) { void Cartridge::prg_write(unsigned addr, uint8 data) {
#ifdef BOARD
return board->prg_write(addr, data); return board->prg_write(addr, data);
#endif
return mapper->prg_write(addr, data);
} }
uint8 Cartridge::chr_read(unsigned addr) { uint8 Cartridge::chr_read(unsigned addr) {
#ifdef BOARD
return board->chr_read(addr); return board->chr_read(addr);
#endif
return mapper->chr_read(addr);
} }
void Cartridge::chr_write(unsigned addr, uint8 data) { void Cartridge::chr_write(unsigned addr, uint8 data) {
#ifdef BOARD
return board->chr_write(addr, data); return board->chr_write(addr, data);
#endif
return mapper->chr_write(addr, data);
} }
void Cartridge::serialize(serializer &s) { void Cartridge::serialize(serializer &s) {
if(chr_ram) s.array(chr_data, chr_size); return board->serialize(s);
return mapper->serialize(s);
} }
} }

View File

@ -1,10 +1,11 @@
#include "chip/chip.hpp"
#include "board/board.hpp" #include "board/board.hpp"
struct Cartridge : Processor, property<Cartridge> { struct Cartridge : Processor, property<Cartridge> {
static void Main(); static void Main();
void main(); void main();
void load(const string &xml, const uint8_t *data, unsigned size); void load(const string &markup, const uint8_t *data, unsigned size);
void unload(); void unload();
unsigned ram_size(); unsigned ram_size();
@ -21,25 +22,12 @@ struct Cartridge : Processor, property<Cartridge> {
//privileged: //privileged:
Board *board; Board *board;
Mapper::Mapper *mapper;
uint8 prg_read(unsigned addr); uint8 prg_read(unsigned addr);
void prg_write(unsigned addr, uint8 data); void prg_write(unsigned addr, uint8 data);
uint8 chr_read(unsigned addr); uint8 chr_read(unsigned addr);
void chr_write(unsigned addr, uint8 data); void chr_write(unsigned addr, uint8 data);
uint8 *rom_data;
unsigned rom_size;
uint8 *prg_data;
unsigned prg_size;
uint8 *chr_data;
unsigned chr_size;
bool chr_ram;
unsigned mirroring;
}; };
extern Cartridge cartridge; extern Cartridge cartridge;

View File

View File

@ -0,0 +1,2 @@
struct Chip {
};

66
bsnes/nes/cartridge/ines.cpp Executable file
View File

@ -0,0 +1,66 @@
static string iNES(const uint8_t *data, unsigned size) {
if(size < 16) return "";
if(data[0] != 'N') return "";
if(data[1] != 'E') return "";
if(data[2] != 'S') return "";
if(data[3] != 0x1a) return "";
string output;
unsigned mapper = ((data[7] >> 4) << 4) | (data[6] >> 4);
unsigned mirror = ((data[6] & 0x08) >> 2) | (data[6] & 0x01);
unsigned prgrom = data[4] * 0x4000;
unsigned chrrom = data[5] * 0x2000;
unsigned prgram = 0;
unsigned chrram = chrrom == 0 ? 8192 : 0;
print("iNES mapper: ", mapper, "\n");
output.append("cartridge\n");
switch(mapper) {
default:
output.append(" board type=NES-NROM-256\n");
output.append(" mirror=", mirror == 0 ? "horizontal" : "vertical", "\n");
break;
case 1:
output.append(" board type=NES-SXROM\n");
break;
case 2:
output.append(" board type=NES-UOROM\n");
output.append(" mirror=", mirror == 0 ? "horizontal" : "vertical", "\n");
break;
case 3:
output.append(" board type=NES-CNROM\n");
output.append(" mirror=", mirror == 0 ? "horizontal" : "vertical", "\n");
break;
case 7:
output.append(" board type=NES-AOROM\n");
break;
}
output.append(" prg rom=", prgrom, " ram=", prgram, "\n");
output.append(" chr rom=", chrrom, " ram=", chrram, "\n");
print(output, "\n");
return output;
}
/*
switch(mapperNumber) {
//default : mapper = &Mapper::none; break;
//case 1: mapper = &Mapper::mmc1; break;
//case 2: mapper = &Mapper::uorom; break;
//case 3: mapper = &Mapper::cnrom; break;
case 4: mapper = &Mapper::mmc3; break;
//case 7: mapper = &Mapper::aorom; break;
case 16: mapper = &Mapper::bandaiFCG; break;
case 24: mapper = &Mapper::vrc6; Mapper::vrc6.abus_swap = 0; break;
case 26: mapper = &Mapper::vrc6; Mapper::vrc6.abus_swap = 1; break;
}
*/

View File

@ -27,8 +27,8 @@ bool Interface::cartridgeLoaded() {
return cartridge.loaded(); return cartridge.loaded();
} }
void Interface::loadCartridge(const string &xml, const uint8_t *data, unsigned size) { void Interface::loadCartridge(const string &markup, const uint8_t *data, unsigned size) {
cartridge.load(xml, data, size); cartridge.load(markup, data, size);
system.power(); system.power();
} }

View File

@ -8,7 +8,7 @@ struct Interface {
virtual void initialize(Interface*); virtual void initialize(Interface*);
virtual bool cartridgeLoaded(); virtual bool cartridgeLoaded();
virtual void loadCartridge(const string &xml, const uint8_t *data, unsigned size); virtual void loadCartridge(const string &markup, const uint8_t *data, unsigned size);
virtual void unloadCartridge(); virtual void unloadCartridge();
enum class Memory : unsigned { enum class Memory : unsigned {

View File

@ -99,9 +99,8 @@ namespace NES {
#include <nes/system/system.hpp> #include <nes/system/system.hpp>
#include <nes/scheduler/scheduler.hpp> #include <nes/scheduler/scheduler.hpp>
#include <nes/input/input.hpp> #include <nes/input/input.hpp>
#include <nes/mapper/mapper.hpp>
#include <nes/cartridge/cartridge.hpp>
#include <nes/memory/memory.hpp> #include <nes/memory/memory.hpp>
#include <nes/cartridge/cartridge.hpp>
#include <nes/cpu/cpu.hpp> #include <nes/cpu/cpu.hpp>
#include <nes/apu/apu.hpp> #include <nes/apu/apu.hpp>
#include <nes/ppu/ppu.hpp> #include <nes/ppu/ppu.hpp>

View File

@ -20,10 +20,10 @@ bool InterfaceNES::loadCartridge(const string &filename) {
interface->unloadCartridge(); interface->unloadCartridge();
interface->baseName = nall::basename(filename); interface->baseName = nall::basename(filename);
string xml; string markup;
xml.readfile({ interface->baseName, ".xml" }); markup.readfile({ interface->baseName, ".bml" });
NES::Interface::loadCartridge(xml, fp.data(), fp.size()); NES::Interface::loadCartridge(markup, fp.data(), fp.size());
fp.close(); fp.close();
if(NES::Interface::memorySize(NES::Interface::Memory::RAM) > 0) { if(NES::Interface::memorySize(NES::Interface::Memory::RAM) > 0) {

View File

@ -49,7 +49,7 @@ Application::Application(int argc, char **argv) {
inputManager = new InputManager; inputManager = new InputManager;
utility = new Utility; utility = new Utility;
title = "bsnes v082.25"; title = "bsnes v082.26";
string fontFamily = Intrinsics::platform() == Intrinsics::Platform::Windows ? "Tahoma, " : "Sans, "; string fontFamily = Intrinsics::platform() == Intrinsics::Platform::Windows ? "Tahoma, " : "Sans, ";
normalFont = { fontFamily, "8" }; normalFont = { fontFamily, "8" };

View File

@ -129,31 +129,13 @@ bool CheatEditor::load(const string &filename) {
if(data.readfile(filename) == false) return false; if(data.readfile(filename) == false) return false;
unsigned n = 0; unsigned n = 0;
xml_element document = xml_parse(data); BML::Node document(data);
for(auto &head : document.element) { for(auto &cheat : document["cartridge"]) {
if(head.name == "cartridge") { if(cheat.name() != "cheat") continue;
for(auto &node : head.element) { cheatList.setChecked(n, cheat["enable"].exists());
if(node.name == "cheat") { cheatText[n][Code] = cheat["code"].value();
bool enable = false; cheatText[n][Desc] = cheat["description"].value();
string description; if(++n >= 128) break;
string code;
for(auto &attribute : node.attribute) {
if(attribute.name == "enabled") enable = (attribute.parse() == "true");
}
for(auto &element : node.element) {
if(element.name == "description") description = element.parse();
else if(element.name == "code") code.append(element.parse(), "+");
}
code.rtrim<1>("+");
cheatList.setChecked(n, enable);
cheatText[n][Code] = code;
cheatText[n][Desc] = description;
if(++n >= 128) break;
}
}
}
} }
updateUI(), updateInterface(); updateUI(), updateInterface();
@ -179,21 +161,14 @@ bool CheatEditor::save(const string &filename) {
file fp; file fp;
if(fp.open(filename, file::mode::write) == false) return false; if(fp.open(filename, file::mode::write) == false) return false;
fp.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); fp.print("cartridge sha256=", interface->sha256(), "\n");
fp.print("<cartridge>\n");
for(unsigned n = 0; n <= lastSave; n++) { for(unsigned n = 0; n <= lastSave; n++) {
fp.print(" <cheat enabled=\"", cheatList.checked(n) ? "true" : "false", "\">\n"); fp.print(" cheat", cheatList.checked(n) ? " enable" : "", "\n");
fp.print(" <description>", cheatText[n][Desc], "</description>\n"); fp.print(" description=|", cheatText[n][Desc], "|\n");
lstring list; fp.print(" code=|", cheatText[n][Code], "|\n");
list.split("+", cheatText[n][Code]);
for(auto &code : list) {
fp.print(" <code>", code, "</code>\n");
}
fp.print(" </cheat>\n");
} }
fp.print("</cartridge>\n");
fp.close();
fp.close();
return true; return true;
} }