mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-17 00:32:11 +02:00
Compare commits
164 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
07995c05a5 | ||
|
13ad9644a2 | ||
|
8d5cc0c35e | ||
|
82293c95ae | ||
|
67457fade4 | ||
|
7a68059f78 | ||
|
3e807946b8 | ||
|
a816998122 | ||
|
3a9c7c6843 | ||
|
f48b332c83 | ||
|
ccd8878d75 | ||
|
875f031182 | ||
|
f04d9d58f5 | ||
|
40abcfc4a5 | ||
|
44a8c5a2b4 | ||
|
f1a80075fa | ||
|
ae5b4c3bb3 | ||
|
c074c6e064 | ||
|
50420e3dd2 | ||
|
b08449215a | ||
|
9b452c9f5f | ||
|
3681961ca5 | ||
|
20ac95ee49 | ||
|
fdc41611cf | ||
|
839813d0f1 | ||
|
7f3cfa17b9 | ||
|
ae5d380d06 | ||
|
3ebc77c148 | ||
|
6ae0abe3d3 | ||
|
0955295475 | ||
|
7cdae5195a | ||
|
e2ee6689a0 | ||
|
55e507d5df | ||
|
a2d3b8ba15 | ||
|
1929ad47d2 | ||
|
7403e69307 | ||
|
19e1d89f00 | ||
|
aff00506c5 | ||
|
e846c83d47 | ||
|
06d44b4878 | ||
|
25eaaa82f4 | ||
|
2d83300235 | ||
|
680d16561e | ||
|
379ab6991f | ||
|
d3413db04a | ||
|
a7f7985581 | ||
|
b586471562 | ||
|
c33065fbd1 | ||
|
79e7e6ab9e | ||
|
3d3ac8c1db | ||
|
b0d2f5033e | ||
|
570eb9c5f5 | ||
|
7dc62e3a69 | ||
|
fc7d5991ce | ||
|
29be18ce0c | ||
|
810cbdafb4 | ||
|
4b29f4bad7 | ||
|
ef65bb862a | ||
|
0d0af39b44 | ||
|
6c83329cae | ||
|
32a95a9761 | ||
|
a89a3da77a | ||
|
7a748e093e | ||
|
d158c8f293 | ||
|
71bda4144a | ||
|
ad51f1478e | ||
|
d0ddd87e9c | ||
|
605a8aa3e9 | ||
|
a8323d0d2b | ||
|
d7998b23ef | ||
|
344e63d928 | ||
|
f1ebef2ea8 | ||
|
1fdd0582fc | ||
|
12df278c5b | ||
|
cec33c1d0f | ||
|
3414c8c8df | ||
|
82ec876302 | ||
|
72b6a8b32e | ||
|
653bb378ee | ||
|
0b923489dd | ||
|
4d193d7d94 | ||
|
47d4bd4d81 | ||
|
27660505c8 | ||
|
702b657e75 | ||
|
0253db8685 | ||
|
2a4eb1cfc8 | ||
|
bd628de3cf | ||
|
2c53d5fbc0 | ||
|
f2a416aea9 | ||
|
78d49d3873 | ||
|
65a3306ad5 | ||
|
a219f9c121 | ||
|
6adfe71836 | ||
|
41c478ac4a | ||
|
40f4b91000 | ||
|
6d9f43a37b | ||
|
d1ffd59c29 | ||
|
0fe55e3f5b | ||
|
b42ab2fcb3 | ||
|
8476a12deb | ||
|
b0e862613b | ||
|
b113ecb5a3 | ||
|
bc5ad4a1cd | ||
|
1a90e206e0 | ||
|
483fc81356 | ||
|
0c87bdabed | ||
|
c45633550e | ||
|
7081f46e45 | ||
|
213879771e | ||
|
4344b916b6 | ||
|
0271d6a12b | ||
|
1b0b54a690 | ||
|
092cac9073 | ||
|
ecb35cac33 | ||
|
28a14198cb | ||
|
7ff7f64482 | ||
|
4c9266d18f | ||
|
169e400437 | ||
|
ea02f1e36a | ||
|
310ff4fa3b | ||
|
83f684c66c | ||
|
e0815b55b9 | ||
|
20cc6148cb | ||
|
a21ff570ee | ||
|
bb3c69a30d | ||
|
f0c17ffc0d | ||
|
314aee8c5c | ||
|
7bf4cff946 | ||
|
99b2b4b57c | ||
|
4e0223d590 | ||
|
458775a481 | ||
|
fc8eba133d | ||
|
39ca8a2fab | ||
|
c335ee9d80 | ||
|
2eb50fd70b | ||
|
89d578bc7f | ||
|
b4ba95242f | ||
|
a1b2fb0124 | ||
|
4a069761f9 | ||
|
80c1c9c2ef | ||
|
a512d14628 | ||
|
1a7bc6bb87 | ||
|
ecc651c88b | ||
|
3016e595f0 | ||
|
423a6c6bf8 | ||
|
10e2a6d497 | ||
|
187ba0eec6 | ||
|
c54be74832 | ||
|
04986d2bf7 | ||
|
10464b8c54 | ||
|
fe85679321 | ||
|
2b81b630cb | ||
|
3ce1d19f7a | ||
|
73be2e729c | ||
|
84fab07756 | ||
|
926a39d701 | ||
|
1361820dd8 | ||
|
0f78acffd7 | ||
|
35f1605829 | ||
|
ed4e87f65e | ||
|
b4f18c3b47 | ||
|
68eaf53691 | ||
|
8c0b0fa4ad | ||
|
66f136718e |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
ananke/libananke.so
|
||||
higan/profile/WonderSwan.sys/internal.ram
|
||||
higan/profile/WonderSwan Color.sys/internal.ram
|
||||
|
106
Makefile
106
Makefile
@@ -1,106 +0,0 @@
|
||||
include nall/Makefile
|
||||
|
||||
fc := fc
|
||||
sfc := sfc
|
||||
gb := gb
|
||||
gba := gba
|
||||
|
||||
profile := accuracy
|
||||
target := ethos
|
||||
|
||||
# options += debugger
|
||||
# arch := x86
|
||||
# console := true
|
||||
|
||||
# compiler
|
||||
flags += -I. -O3 -fomit-frame-pointer
|
||||
link +=
|
||||
objects := libco
|
||||
|
||||
# profile-guided optimization mode
|
||||
# pgo := instrument
|
||||
# pgo := optimize
|
||||
|
||||
ifeq ($(pgo),instrument)
|
||||
flags += -fprofile-generate
|
||||
link += -lgcov
|
||||
else ifeq ($(pgo),optimize)
|
||||
flags += -fprofile-use
|
||||
endif
|
||||
|
||||
# platform
|
||||
ifeq ($(platform),x)
|
||||
flags += -march=native
|
||||
link += -s -Wl,-export-dynamic -ldl -lX11 -lXext
|
||||
else ifeq ($(platform),osx)
|
||||
flags += -march=native
|
||||
else ifeq ($(platform),win)
|
||||
ifeq ($(arch),win32)
|
||||
flags += -m32
|
||||
link += -m32
|
||||
endif
|
||||
ifeq ($(console),true)
|
||||
link += -mconsole
|
||||
else
|
||||
link += -mwindows
|
||||
endif
|
||||
link += -s -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 -lws2_32
|
||||
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
||||
else
|
||||
$(error unsupported platform.)
|
||||
endif
|
||||
|
||||
ui := target-$(target)
|
||||
|
||||
# implicit rules
|
||||
compile = \
|
||||
$(strip \
|
||||
$(if $(filter %.c,$<), \
|
||||
$(compiler) $(cflags) $(flags) $1 -c $< -o $@, \
|
||||
$(if $(filter %.cpp,$<), \
|
||||
$(compiler) $(cppflags) $(flags) $1 -c $< -o $@ \
|
||||
) \
|
||||
) \
|
||||
)
|
||||
|
||||
%.o: $<; $(call compile)
|
||||
|
||||
all: build;
|
||||
|
||||
obj/libco.o: libco/libco.c libco/*
|
||||
|
||||
include $(ui)/Makefile
|
||||
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
|
||||
|
||||
# targets
|
||||
clean:
|
||||
-@$(call delete,obj/*.o)
|
||||
-@$(call delete,obj/*.a)
|
||||
-@$(call delete,obj/*.so)
|
||||
-@$(call delete,obj/*.dylib)
|
||||
-@$(call delete,obj/*.dll)
|
||||
-@$(call delete,*.res)
|
||||
-@$(call delete,*.manifest)
|
||||
|
||||
archive:
|
||||
if [ -f higan.tar.xz ]; then rm higan.tar.xz; fi
|
||||
tar -cJf higan.tar.xz `ls`
|
||||
|
||||
sync:
|
||||
ifeq ($(shell id -un),byuu)
|
||||
if [ -d ./libco ]; then rm -r ./libco; fi
|
||||
if [ -d ./nall ]; then rm -r ./nall; fi
|
||||
if [ -d ./ruby ]; then rm -r ./ruby; fi
|
||||
if [ -d ./phoenix ]; then rm -r ./phoenix; fi
|
||||
cp -r ../libco ./libco
|
||||
cp -r ../nall ./nall
|
||||
cp -r ../ruby ./ruby
|
||||
cp -r ../phoenix ./phoenix
|
||||
rm -r libco/doc
|
||||
rm -r libco/.test
|
||||
rm -r nall/.test
|
||||
rm -r ruby/.test
|
||||
rm -r phoenix/.test
|
||||
endif
|
||||
|
||||
help:;
|
@@ -1,46 +0,0 @@
|
||||
include ../nall/Makefile
|
||||
include ../phoenix/Makefile
|
||||
|
||||
path := /usr/local/lib
|
||||
flags := $(flags) -O3 -fomit-frame-pointer -I..
|
||||
|
||||
all:
|
||||
$(compiler) $(cppflags) $(flags) -fPIC -o obj/ananke.o -c ananke.cpp
|
||||
ifeq ($(platform),x)
|
||||
$(compiler) $(link) -shared -Wl,-soname,libananke.so.1 -o libananke.so obj/ananke.o
|
||||
else ifeq ($(platform),osx)
|
||||
$(compiler) $(link) -shared -dynamiclib -undefined suppress -flat_namespace -o libananke.dylib obj/ananke.o
|
||||
else ifeq ($(platform),win)
|
||||
$(compiler) $(phoenixflags) -fPIC -o obj/phoenix.o -c ../phoenix/phoenix.cpp
|
||||
$(compiler) $(link) -shared -o phoenix.dll obj/phoenix.o $(phoenixlink)
|
||||
$(compiler) $(link) -shared -o ananke.dll obj/ananke.o -L. -lphoenix
|
||||
endif
|
||||
|
||||
resource: force
|
||||
sourcery resource/resource.bml resource/resource.cpp resource/resource.hpp
|
||||
|
||||
clean:
|
||||
-@$(call delete,obj/*.o)
|
||||
-@$(call delete,*.so)
|
||||
|
||||
install: uninstall
|
||||
ifeq ($(platform),x)
|
||||
if [ ! -d ~/.config/ananke ]; then mkdir ~/.config/ananke; fi
|
||||
sudo cp libananke.so $(path)/libananke.so.1
|
||||
sudo ln -s $(path)/libananke.so.1 $(path)/libananke.so
|
||||
else ifeq ($(platform),osx)
|
||||
if [ ! -d ~/Library/Application\ Support/ananke ]; then mkdir ~/Library/Application\ Support/ananke; fi
|
||||
sudo cp libananke.dylib $(path)/libananke.1.dylib
|
||||
sudo ln -s $(path)/libananke.1.dylib $(path)/libananke.dylib
|
||||
endif
|
||||
|
||||
uninstall:
|
||||
ifeq ($(platform),x)
|
||||
if [ -f $(path)/libananke.so ]; then sudo rm $(path)/libananke.so; fi
|
||||
if [ -f $(path)/libananke.so.1 ]; then sudo rm $(path)/libananke.so.1; fi
|
||||
else ifeq ($(platform),osx)
|
||||
if [ -f $(path)/libananke.dylib ]; then sudo rm $(path)/libananke.dylib; fi
|
||||
if [ -f $(path)/libananke.1.dylib ]; then sudo rm $(path)/libananke.1.dylib; fi
|
||||
endif
|
||||
|
||||
force:
|
@@ -1,179 +0,0 @@
|
||||
#include <nall/nall.hpp>
|
||||
#include <nall/beat/patch.hpp>
|
||||
#include "heuristics/famicom.hpp"
|
||||
#include "heuristics/super-famicom.hpp"
|
||||
#include "heuristics/game-boy.hpp"
|
||||
#include "heuristics/game-boy-advance.hpp"
|
||||
using namespace nall;
|
||||
|
||||
#include <phoenix/phoenix.hpp>
|
||||
using namespace phoenix;
|
||||
|
||||
namespace Database {
|
||||
#include "database/super-famicom.hpp"
|
||||
#include "database/sufami-turbo.hpp"
|
||||
#include "database/bsx-satellaview.hpp"
|
||||
};
|
||||
|
||||
struct Ananke {
|
||||
#include "configuration.cpp"
|
||||
string libraryPath;
|
||||
|
||||
Ananke();
|
||||
|
||||
struct Information {
|
||||
string path; //path to selected file
|
||||
string name; //name of selected file (inside of archive if .zip)
|
||||
string archive; //pathname of archive
|
||||
string manifest; //manifest from successfully applied patch
|
||||
} information;
|
||||
|
||||
//archive.cpp
|
||||
vector<uint8_t> extractROM();
|
||||
vector<uint8_t> extractFile(const string &filename);
|
||||
|
||||
//patch.cpp
|
||||
void applyBeatPatch(vector<uint8_t> &buffer);
|
||||
|
||||
//famicom.cpp
|
||||
void copyFamicomSaves(const string &pathname);
|
||||
string createFamicomHeuristic(vector<uint8_t> &buffer);
|
||||
string openFamicom(vector<uint8_t> &buffer);
|
||||
string syncFamicom(const string &pathname);
|
||||
|
||||
//super-famicom.cpp
|
||||
void copySuperFamicomSaves(const string &pathname);
|
||||
string createSuperFamicomDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest);
|
||||
string createSuperFamicomHeuristic(vector<uint8_t> &buffer);
|
||||
void createSuperFamicomHeuristicFirmware(vector<uint8_t> &buffer, const string &pathname, bool firmware_appended);
|
||||
string openSuperFamicom(vector<uint8_t> &buffer);
|
||||
string syncSuperFamicom(const string &pathname);
|
||||
|
||||
//sufami-turbo.cpp
|
||||
void copySufamiTurboSaves(const string &pathname);
|
||||
string createSufamiTurboDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest);
|
||||
string createSufamiTurboHeuristic(vector<uint8_t> &buffer);
|
||||
string openSufamiTurbo(vector<uint8_t> &buffer);
|
||||
string syncSufamiTurbo(const string &pathname);
|
||||
|
||||
//bsx-satellaview.cpp
|
||||
string createBsxSatellaviewDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest);
|
||||
string createBsxSatellaviewHeuristic(vector<uint8_t> &buffer);
|
||||
string openBsxSatellaview(vector<uint8_t> &buffer);
|
||||
string syncBsxSatellaview(const string &pathname);
|
||||
|
||||
//game-boy.cpp
|
||||
void copyGameBoySaves(const string &pathname);
|
||||
string createGameBoyHeuristic(vector<uint8_t> &buffer);
|
||||
string openGameBoy(vector<uint8_t> &buffer);
|
||||
string syncGameBoy(const string &pathname);
|
||||
|
||||
//game-boy-advance.cpp
|
||||
void copyGameBoyAdvanceSaves(const string &pathname);
|
||||
string createGameBoyAdvanceHeuristic(vector<uint8_t> &buffer);
|
||||
string openGameBoyAdvance(vector<uint8_t> &buffer);
|
||||
string syncGameBoyAdvance(const string &pathname);
|
||||
|
||||
static bool supported(const string &filename);
|
||||
string open(string filename = "");
|
||||
string sync(string pathname);
|
||||
};
|
||||
|
||||
#include "resource/resource.cpp"
|
||||
#include "file-dialog.cpp"
|
||||
#include "archive.cpp"
|
||||
#include "patch.cpp"
|
||||
#include "famicom.cpp"
|
||||
#include "super-famicom.cpp"
|
||||
#include "sufami-turbo.cpp"
|
||||
#include "bsx-satellaview.cpp"
|
||||
#include "game-boy.cpp"
|
||||
#include "game-boy-advance.cpp"
|
||||
|
||||
FileDialog *fileDialog = nullptr;
|
||||
|
||||
Ananke::Ananke() {
|
||||
libraryPath = string::read({configpath(), "higan/library.bml"}).strip().ltrim<1>("Path: ").replace("\\", "/");
|
||||
if(libraryPath.empty()) libraryPath = {userpath(), "Emulation/"};
|
||||
if(libraryPath.endswith("/") == false) libraryPath.append("/");
|
||||
}
|
||||
|
||||
bool Ananke::supported(const string &filename) {
|
||||
string extension = nall::extension(filename);
|
||||
|
||||
if(extension == "fc" ) return true;
|
||||
if(extension == "nes") return true;
|
||||
if(extension == "sfc") return true;
|
||||
if(extension == "smc") return true;
|
||||
if(extension == "st" ) return true;
|
||||
if(extension == "bs" ) return true;
|
||||
if(extension == "gb" ) return true;
|
||||
if(extension == "gbc") return true;
|
||||
if(extension == "gba") return true;
|
||||
if(extension == "zip") return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
string Ananke::open(string filename) {
|
||||
if(filename.empty()) {
|
||||
if(!fileDialog) {
|
||||
fileDialog = new FileDialog;
|
||||
fileDialog->setGeometry(config.geometry);
|
||||
}
|
||||
fileDialog->setPath(config.path);
|
||||
filename = fileDialog->open();
|
||||
config.geometry = fileDialog->geometry().text();
|
||||
}
|
||||
|
||||
if(filename.empty()) return "";
|
||||
|
||||
information.path = dir(filename);
|
||||
information.name = notdir(filename);
|
||||
config.path = information.path; //remember last used directory
|
||||
|
||||
vector<uint8_t> buffer;
|
||||
if(filename.endswith(".zip")) {
|
||||
information.archive = filename;
|
||||
buffer = extractROM();
|
||||
} else {
|
||||
buffer = file::read(filename);
|
||||
}
|
||||
if(buffer.size() == 0) return ""; //failed to read file
|
||||
|
||||
applyBeatPatch(buffer);
|
||||
|
||||
if(information.name.endswith(".fc") || information.name.endswith(".nes")) return openFamicom(buffer);
|
||||
if(information.name.endswith(".sfc") || information.name.endswith(".smc")) return openSuperFamicom(buffer);
|
||||
if(information.name.endswith(".st")) return openSufamiTurbo(buffer);
|
||||
if(information.name.endswith(".bs")) return openBsxSatellaview(buffer);
|
||||
if(information.name.endswith(".gb") || information.name.endswith(".gbc")) return openGameBoy(buffer);
|
||||
if(information.name.endswith(".gba")) return openGameBoyAdvance(buffer);
|
||||
return "";
|
||||
}
|
||||
|
||||
string Ananke::sync(string pathname) {
|
||||
if(pathname.endswith(".fc/")) return syncFamicom(pathname);
|
||||
if(pathname.endswith(".sfc/")) return syncSuperFamicom(pathname);
|
||||
if(pathname.endswith(".st/")) return syncSufamiTurbo(pathname);
|
||||
if(pathname.endswith(".bs/")) return syncBsxSatellaview(pathname);
|
||||
if(pathname.endswith(".gb/")) return syncGameBoy(pathname);
|
||||
if(pathname.endswith(".gbc/")) return syncGameBoy(pathname);
|
||||
if(pathname.endswith(".gba/")) return syncGameBoyAdvance(pathname);
|
||||
return "";
|
||||
}
|
||||
|
||||
extern "C" string ananke_browse(const string &filename) {
|
||||
Ananke ananke;
|
||||
return ananke.open();
|
||||
}
|
||||
|
||||
extern "C" string ananke_open(const string &filename) {
|
||||
Ananke ananke;
|
||||
return ananke.open(filename);
|
||||
}
|
||||
|
||||
extern "C" string ananke_sync(const string &pathname) {
|
||||
Ananke ananke;
|
||||
return ananke.sync(pathname);
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
vector<uint8_t> Ananke::extractROM() {
|
||||
unzip archive;
|
||||
if(archive.open(information.archive)) {
|
||||
for(auto &file : archive.file) {
|
||||
if(file.name.endswith(".fc") || file.name.endswith(".nes")
|
||||
|| file.name.endswith(".sfc") || file.name.endswith(".smc")
|
||||
|| file.name.endswith(".st") || file.name.endswith(".bs")
|
||||
|| file.name.endswith(".gb") || file.name.endswith(".gbc")
|
||||
|| file.name.endswith(".gba")
|
||||
) {
|
||||
information.name = notdir(file.name);
|
||||
return archive.extract(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return vector<uint8_t>();
|
||||
}
|
||||
|
||||
vector<uint8_t> Ananke::extractFile(const string &filename) {
|
||||
unzip archive;
|
||||
if(archive.open(information.archive)) {
|
||||
for(auto &file : archive.file) {
|
||||
if(notdir(file.name) == filename) {
|
||||
return archive.extract(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return vector<uint8_t>();
|
||||
}
|
@@ -1,72 +0,0 @@
|
||||
string Ananke::createBsxSatellaviewDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest) {
|
||||
string pathname = {
|
||||
libraryPath, "BS-X Satellaview/",
|
||||
document["release/information/name"].text(),
|
||||
" (", document["release/information/region"].text(), ")",
|
||||
" (", document["release/information/revision"].text(), ")",
|
||||
".bs/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
//strip "release" root node from database entry (since a single game manifest isn't part of a database)
|
||||
string markup = manifest;
|
||||
markup.replace("\n ", "\n");
|
||||
markup.replace("information", "\ninformation");
|
||||
markup.ltrim<1>("release\n");
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::createBsxSatellaviewHeuristic(vector<uint8_t> &buffer) {
|
||||
string pathname = {
|
||||
libraryPath, "BS-X Satellaview/",
|
||||
nall::basename(information.name),
|
||||
".bs/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
file::write({pathname, "manifest.bml"}, {
|
||||
"unverified\n",
|
||||
"\n",
|
||||
"cartridge\n",
|
||||
" rom name=program.rom size=0x", hex(buffer.size()), " type=FlashROM\n",
|
||||
"\n",
|
||||
"information\n",
|
||||
" title: ", nall::basename(information.name), "\n"
|
||||
});
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::openBsxSatellaview(vector<uint8_t> &buffer) {
|
||||
string sha256 = nall::sha256(buffer.data(), buffer.size());
|
||||
|
||||
string databaseText = string::read({configpath(), "ananke/database/BS-X Satellaview.bml"}).strip();
|
||||
if(databaseText.empty()) databaseText = string{Database::BsxSatellaview}.strip();
|
||||
lstring databaseItem = databaseText.split("\n\n");
|
||||
|
||||
for(auto &item : databaseItem) {
|
||||
item.append("\n");
|
||||
auto document = Markup::Document(item);
|
||||
|
||||
if(document["release/information/sha256"].text() == sha256) {
|
||||
return createBsxSatellaviewDatabase(buffer, document, item);
|
||||
}
|
||||
}
|
||||
|
||||
return createBsxSatellaviewHeuristic(buffer);
|
||||
}
|
||||
|
||||
string Ananke::syncBsxSatellaview(const string &pathname) {
|
||||
auto buffer = file::read({pathname, "program.rom"});
|
||||
if(buffer.size() == 0) return "";
|
||||
|
||||
directory::remove(pathname);
|
||||
information.path = pathname;
|
||||
information.name = notdir(string{pathname}.rtrim<1>("/"));
|
||||
return openBsxSatellaview(buffer);
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
struct Settings : Configuration::Document {
|
||||
string path;
|
||||
string geometry;
|
||||
|
||||
Settings() {
|
||||
Configuration::Node node;
|
||||
node.append(path = userpath(), "Path");
|
||||
node.append(geometry = "64,64,480,600", "Geometry");
|
||||
append(node, "Settings");
|
||||
directory::create({configpath(), "ananke/"});
|
||||
load({configpath(), "ananke/settings.bml"});
|
||||
}
|
||||
|
||||
~Settings() {
|
||||
save({configpath(), "ananke/settings.bml"});
|
||||
}
|
||||
} config;
|
@@ -1,19 +0,0 @@
|
||||
string BsxSatellaview = R"(
|
||||
|
||||
database revision=2013-01-22
|
||||
|
||||
release
|
||||
cartridge
|
||||
rom name=program.rom size=0x80000 type=MaskROM
|
||||
information
|
||||
title: 鮫亀 キャラカセット
|
||||
name: Same Game - Character Cassette
|
||||
region: JP
|
||||
revision: 1.0
|
||||
board: BSMC-CR-01
|
||||
serial: BSMC-ZS5J-JPN
|
||||
sha256: 80c34b50817d58820bc8c88d2d9fa462550b4a76372e19c6467cbfbc8cf5d9ef
|
||||
configuration
|
||||
rom name=program.rom size=0x80000 type=MaskROM
|
||||
|
||||
)";
|
@@ -1,162 +0,0 @@
|
||||
string SufamiTurbo = R"(
|
||||
|
||||
database revision=2013-01-22
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: SDウルトラバトル ウルトラマン伝説
|
||||
name: SD Ultra Battle - Ultraman Densetsu
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0101-JPN
|
||||
sha256: 2bb55214fb668ca603d7b944b14f105dfb10b987a8902d420fe4ae1cb69c1d4a
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: SDウルトラバトル セブン伝説
|
||||
name: SD Ultra Battle - Seven Densetsu
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0102-JPN
|
||||
sha256: 2fec5f2bc7dee010af10569a3d2bc18715a79a126940800c3eade5abbd625e3f
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: ポイポイ忍者ワールド
|
||||
name: Poi Poi Ninja World
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0103-JPN
|
||||
sha256: 602b20b788640f5743487108a10f3f77bca5ce2d24208b25b1ca498a96eb0d69
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション 一年戦争記
|
||||
name: SD Gundam Generation - Ichinen Sensouki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0104-JPN
|
||||
sha256: 3e82215bed08274874b30d461fc4a965c6bca932229da5d46d56e36f484d65eb
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション グリプス戦記
|
||||
name: SD Gundam Generation - Grips Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0105-JPN
|
||||
sha256: 8547a08ed11fe408eac282a90ac46654bd2e5f49bda3aec8e5edf166a0a4b9af
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge
|
||||
rom name=program.rom size=0x80000
|
||||
information
|
||||
title: ゲゲゲの鬼太郎 妖怪ドンジャラ
|
||||
name: Gegege no Kitarou - Youkai Donjara
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0106-JPN
|
||||
sha256: d93b3a570e7cf343f680ab0768a50b77e3577f9c555007e2de3decd6bc4765c8
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション アクシズ戦記
|
||||
name: SD Gundam Generation - Axis Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0107-JPN
|
||||
sha256: 2a9d7c9a61318861028a73ca03e32a48cff162d76cba36fbaab8690b212efe9b
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション バビロニア建国戦記
|
||||
name: SD Gundam Generation - Babylonia Kenkoku Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0108-JPN
|
||||
sha256: 60ac017c18f534e8cf24ca7f38e22ce92db95ea6c30b2d59d76f13c4f1c8a6e4
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション ザンスカール戦記
|
||||
name: SD Gundam Generation - Zanscar Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0110-JPN
|
||||
sha256: 5951a58a91d8e397d0a237ccc2b1248e17c7312cb9cc11cbc350200a97b4e021
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: SDガンダムジェネレーション コロニー格闘記
|
||||
name: SD Gundam Generation - Colony Kakutouki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0111-JPN
|
||||
sha256: e639b5d5d722432b6809ccc6801dc584e1a3016379f34b335ed2dfa73b1ebf69
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
)";
|
File diff suppressed because it is too large
Load Diff
@@ -1,39 +0,0 @@
|
||||
void Ananke::copyFamicomSaves(const string &pathname) {
|
||||
if(!file::exists({pathname, "save.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".sav"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".srm"}, {pathname, "save.ram"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string Ananke::createFamicomHeuristic(vector<uint8_t> &buffer) {
|
||||
string pathname = {
|
||||
libraryPath, "Famicom/",
|
||||
nall::basename(information.name),
|
||||
".fc/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
FamicomCartridge info(buffer.data(), buffer.size());
|
||||
string markup = {"unverified\n\n", info.markup};
|
||||
markup.append("\ninformation\n title: ", nall::basename(information.name), "\n");
|
||||
if(!information.manifest.empty()) markup = information.manifest; //override with embedded beat manifest, if one exists
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
file::write({pathname, "program.rom"}, buffer.data() + 16, info.prgrom);
|
||||
if(info.chrrom > 0) file::write({pathname, "character.rom"}, buffer.data() + 16 + info.prgrom, info.chrrom);
|
||||
|
||||
copyFamicomSaves(pathname);
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::openFamicom(vector<uint8_t> &buffer) {
|
||||
return createFamicomHeuristic(buffer);
|
||||
}
|
||||
|
||||
//this currently cannot work:
|
||||
//game folders discard iNES header required for heuristic detection
|
||||
//a games database of all commercial Famicom software will be required
|
||||
string Ananke::syncFamicom(const string &pathname) {
|
||||
return "";
|
||||
}
|
@@ -1,106 +0,0 @@
|
||||
struct FileDialog : Window {
|
||||
VerticalLayout layout;
|
||||
HorizontalLayout pathLayout;
|
||||
LineEdit pathEdit;
|
||||
Button homeButton;
|
||||
Button upButton;
|
||||
ListView fileList;
|
||||
HorizontalLayout controlLayout;
|
||||
Label filterLabel;
|
||||
Button openButton;
|
||||
|
||||
string open() {
|
||||
setVisible();
|
||||
fileList.setFocused();
|
||||
filename = "";
|
||||
|
||||
setModal();
|
||||
return filename;
|
||||
}
|
||||
|
||||
void setPath(const string &path) {
|
||||
pathname = string{path}.transform("\\", "/");
|
||||
if(pathname.empty()) pathname = userpath();
|
||||
if(pathname.endswith("/") == false) pathname.append("/");
|
||||
pathEdit.setText(pathname);
|
||||
|
||||
fileList.reset();
|
||||
filenameList.reset();
|
||||
|
||||
lstring folders = directory::ifolders(pathname);
|
||||
for(auto &folder : folders) {
|
||||
fileList.append(string{folder}.rtrim<1>("/"));
|
||||
fileList.setImage(filenameList.size(), 0, {resource::folder, sizeof resource::folder});
|
||||
filenameList.append({pathname, folder});
|
||||
}
|
||||
|
||||
lstring files = directory::ifiles(pathname);
|
||||
for(auto &file : files) {
|
||||
if(Ananke::supported(file) == false) continue; //ignore unsupported extensions
|
||||
fileList.append(file);
|
||||
if(extension(file) == "zip") {
|
||||
fileList.setImage(filenameList.size(), 0, {resource::archive, sizeof resource::archive});
|
||||
} else {
|
||||
fileList.setImage(filenameList.size(), 0, {resource::file, sizeof resource::file});
|
||||
}
|
||||
filenameList.append({pathname, file});
|
||||
}
|
||||
|
||||
fileList.setSelection(0);
|
||||
fileList.setSelected();
|
||||
fileList.setFocused();
|
||||
}
|
||||
|
||||
FileDialog() {
|
||||
setTitle("Load Image");
|
||||
|
||||
layout.setMargin(5);
|
||||
homeButton.setImage({resource::home, sizeof resource::home});
|
||||
upButton.setImage({resource::up, sizeof resource::up});
|
||||
filterLabel.setText("Filter: *.fc, *.sfc, *.st, *.bs, *.gb, *.gbc, *.gba, *.nes, *.smc, *.zip");
|
||||
openButton.setText("Open");
|
||||
|
||||
append(layout);
|
||||
layout.append(pathLayout, {~0, 0}, 5);
|
||||
pathLayout.append(pathEdit, {~0, 0}, 5);
|
||||
pathLayout.append(homeButton, {28, 28}, 5);
|
||||
pathLayout.append(upButton, {28, 28});
|
||||
layout.append(fileList, {~0, ~0}, 5);
|
||||
layout.append(controlLayout, {~0, 0});
|
||||
controlLayout.append(filterLabel, {~0, 0}, 5);
|
||||
controlLayout.append(openButton, {80, 0});
|
||||
|
||||
pathEdit.onActivate = [&] {
|
||||
string path = pathEdit.text();
|
||||
setPath(path);
|
||||
};
|
||||
|
||||
homeButton.onActivate = [&] {
|
||||
setPath(userpath());
|
||||
};
|
||||
|
||||
upButton.onActivate = [&] {
|
||||
setPath(parentdir(pathname));
|
||||
};
|
||||
|
||||
fileList.onActivate = openButton.onActivate = [&] {
|
||||
if(fileList.selected() == false) return;
|
||||
string name = filenameList(fileList.selection());
|
||||
if(name.empty()) return;
|
||||
|
||||
if(name.endswith("/")) return setPath(name);
|
||||
filename = name;
|
||||
onClose();
|
||||
};
|
||||
|
||||
onClose = [&] {
|
||||
setModal(false);
|
||||
setVisible(false);
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
string pathname;
|
||||
string filename;
|
||||
lstring filenameList;
|
||||
};
|
@@ -1,58 +0,0 @@
|
||||
void Ananke::copyGameBoyAdvanceSaves(const string &pathname) {
|
||||
if(!file::exists({pathname, "save.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".sav"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".sav"}, {pathname, "save.ram"});
|
||||
}
|
||||
}
|
||||
|
||||
if(!file::exists({pathname, "rtc.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".rtc"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".rtc"}, {pathname, "rtc.ram"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string Ananke::createGameBoyAdvanceHeuristic(vector<uint8_t> &buffer) {
|
||||
string pathname = {
|
||||
libraryPath, "Game Boy Advance/",
|
||||
nall::basename(information.name),
|
||||
".gba/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
GameBoyAdvanceCartridge info(buffer.data(), buffer.size());
|
||||
string markup = {"unverified\n\n", info.markup};
|
||||
markup.append("\ninformation\n title: ", nall::basename(information.name), "\n");
|
||||
if(!information.manifest.empty()) markup = information.manifest; //override with embedded beat manifest, if one exists
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
|
||||
copyGameBoyAdvanceSaves(pathname);
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::openGameBoyAdvance(vector<uint8_t> &buffer) {
|
||||
return createGameBoyAdvanceHeuristic(buffer);
|
||||
}
|
||||
|
||||
string Ananke::syncGameBoyAdvance(const string &pathname) {
|
||||
auto buffer = file::read({pathname, "program.rom"});
|
||||
if(buffer.size() == 0) return "";
|
||||
|
||||
auto save = file::read({pathname, "save.ram"});
|
||||
if(save.size() == 0) save = file::read({pathname, "save.rwm"});
|
||||
|
||||
auto rtc = file::read({pathname, "rtc.ram"});
|
||||
if(rtc.size() == 0) rtc = file::read({pathname, "rtc.rwm"});
|
||||
|
||||
directory::remove(pathname);
|
||||
information.path = pathname;
|
||||
information.name = notdir(string{pathname}.rtrim<1>("/"));
|
||||
string outputPath = openGameBoyAdvance(buffer);
|
||||
|
||||
if(save.size()) file::write({outputPath, "save.ram"}, save);
|
||||
if(rtc.size()) file::write({outputPath, "rtc.ram"}, rtc);
|
||||
|
||||
return outputPath;
|
||||
}
|
@@ -1,59 +0,0 @@
|
||||
void Ananke::copyGameBoySaves(const string &pathname) {
|
||||
if(!file::exists({pathname, "save.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".sav"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".sav"}, {pathname, "save.ram"});
|
||||
}
|
||||
}
|
||||
|
||||
if(!file::exists({pathname, "rtc.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".rtc"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".rtc"}, {pathname, "rtc.ram"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string Ananke::createGameBoyHeuristic(vector<uint8_t> &buffer) {
|
||||
GameBoyCartridge info(buffer.data(), buffer.size());
|
||||
|
||||
string pathname = {
|
||||
libraryPath, "Game Boy", (info.info.cgb ? " Color" : ""), "/",
|
||||
nall::basename(information.name),
|
||||
".", (info.info.cgb ? "gbc" : "gb"), "/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
string markup = {"unverified\n\n", info.markup};
|
||||
markup.append("\ninformation\n title: ", nall::basename(information.name), "\n");
|
||||
if(!information.manifest.empty()) markup = information.manifest; //override with embedded beat manifest, if one exists
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
|
||||
copyGameBoySaves(pathname);
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::openGameBoy(vector<uint8_t> &buffer) {
|
||||
return createGameBoyHeuristic(buffer);
|
||||
}
|
||||
|
||||
string Ananke::syncGameBoy(const string &pathname) {
|
||||
auto buffer = file::read({pathname, "program.rom"});
|
||||
if(buffer.size() == 0) return "";
|
||||
|
||||
auto save = file::read({pathname, "save.ram"});
|
||||
if(save.size() == 0) save = file::read({pathname, "save.rwm"});
|
||||
|
||||
auto rtc = file::read({pathname, "rtc.ram"});
|
||||
if(rtc.size() == 0) rtc = file::read({pathname, "rtc.rwm"});
|
||||
|
||||
directory::remove(pathname);
|
||||
information.path = pathname;
|
||||
information.name = notdir(string{pathname}.rtrim<1>("/"));
|
||||
string outputPath = openGameBoy(buffer);
|
||||
|
||||
if(save.size()) file::write({outputPath, "save.ram"}, save);
|
||||
if(rtc.size()) file::write({outputPath, "rtc.ram"}, rtc);
|
||||
|
||||
return outputPath;
|
||||
}
|
@@ -1,173 +0,0 @@
|
||||
#ifndef NALL_EMULATION_FAMICOM_HPP
|
||||
#define NALL_EMULATION_FAMICOM_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct FamicomCartridge {
|
||||
string markup;
|
||||
inline FamicomCartridge(const uint8_t *data, unsigned size);
|
||||
|
||||
//private:
|
||||
unsigned mapper;
|
||||
unsigned mirror;
|
||||
unsigned prgrom;
|
||||
unsigned prgram;
|
||||
unsigned chrrom;
|
||||
unsigned chrram;
|
||||
};
|
||||
|
||||
FamicomCartridge::FamicomCartridge(const uint8_t *data, unsigned size) {
|
||||
markup = "";
|
||||
if(size < 16) return;
|
||||
if(data[0] != 'N') return;
|
||||
if(data[1] != 'E') return;
|
||||
if(data[2] != 'S') return;
|
||||
if(data[3] != 26) return;
|
||||
|
||||
mapper = ((data[7] >> 4) << 4) | (data[6] >> 4);
|
||||
mirror = ((data[6] & 0x08) >> 2) | (data[6] & 0x01);
|
||||
prgrom = data[4] * 0x4000;
|
||||
chrrom = data[5] * 0x2000;
|
||||
prgram = 0u;
|
||||
chrram = chrrom == 0u ? 8192u : 0u;
|
||||
|
||||
markup.append("cartridge\n");
|
||||
|
||||
switch(mapper) {
|
||||
default:
|
||||
markup.append(" board type=NES-NROM-256\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
markup.append(" board type=NES-SXROM\n");
|
||||
markup.append(" chip type=MMC1B2\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
markup.append(" board type=NES-UOROM\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
markup.append(" board type=NES-CNROM\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
//MMC3
|
||||
markup.append(" board type=NES-TLROM\n");
|
||||
markup.append(" chip type=MMC3B\n");
|
||||
prgram = 8192;
|
||||
//MMC6
|
||||
//markup.append(" board type=NES-HKROM\n");
|
||||
//markup.append(" chip type=MMC6n");
|
||||
//prgram = 1024;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
markup.append(" board type=NES-ELROM\n");
|
||||
markup.append(" chip type=MMC5\n");
|
||||
prgram = 65536;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
markup.append(" board type=NES-AOROM\n");
|
||||
break;
|
||||
|
||||
case 9:
|
||||
markup.append(" board type=NES-PNROM\n");
|
||||
markup.append(" chip type=MMC2\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 10:
|
||||
markup.append(" board type=NES-FKROM\n");
|
||||
markup.append(" chip type=MMC4\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
markup.append(" board type=BANDAI-FCG\n");
|
||||
markup.append(" chip type=LZ93D50\n");
|
||||
break;
|
||||
|
||||
case 21:
|
||||
case 23:
|
||||
case 25:
|
||||
//VRC4
|
||||
markup.append(" board type=KONAMI-VRC-4\n");
|
||||
markup.append(" chip type=VRC4\n");
|
||||
markup.append(" pinout a0=1 a1=0\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 22:
|
||||
//VRC2
|
||||
markup.append(" board type=KONAMI-VRC-2\n");
|
||||
markup.append(" chip type=VRC2\n");
|
||||
markup.append(" pinout a0=0 a1=1\n");
|
||||
break;
|
||||
|
||||
case 24:
|
||||
markup.append(" board type=KONAMI-VRC-6\n");
|
||||
markup.append(" chip type=VRC6\n");
|
||||
break;
|
||||
|
||||
case 26:
|
||||
markup.append(" board type=KONAMI-VRC-6\n");
|
||||
markup.append(" chip type=VRC6\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 34:
|
||||
markup.append(" board type=NES-BNROM\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 66:
|
||||
markup.append(" board type=NES-GNROM\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 69:
|
||||
markup.append(" board type=SUNSOFT-5B\n");
|
||||
markup.append(" chip type=5B\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 73:
|
||||
markup.append(" board type=KONAMI-VRC-3\n");
|
||||
markup.append(" chip type=VRC3\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 75:
|
||||
markup.append(" board type=KONAMI-VRC-1\n");
|
||||
markup.append(" chip type=VRC1\n");
|
||||
break;
|
||||
|
||||
case 85:
|
||||
markup.append(" board type=KONAMI-VRC-7\n");
|
||||
markup.append(" chip type=VRC7\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
}
|
||||
|
||||
markup.append(" prg\n");
|
||||
if(prgrom) markup.append(" rom name=program.rom size=0x", hex(prgrom), "\n");
|
||||
if(prgram) markup.append(" ram name=save.ram size=0x", hex(prgram), "\n");
|
||||
|
||||
markup.append(" chr\n");
|
||||
if(chrrom) markup.append(" rom name=character.rom size=0x", hex(chrrom), "\n");
|
||||
if(chrram) markup.append(" ram size=0x", hex(chrram), "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,63 +0,0 @@
|
||||
#ifndef NALL_EMULATION_GAME_BOY_ADVANCE_HPP
|
||||
#define NALL_EMULATION_GAME_BOY_ADVANCE_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct GameBoyAdvanceCartridge {
|
||||
string markup;
|
||||
string identifiers;
|
||||
inline GameBoyAdvanceCartridge(const uint8_t *data, unsigned size);
|
||||
};
|
||||
|
||||
GameBoyAdvanceCartridge::GameBoyAdvanceCartridge(const uint8_t *data, unsigned size) {
|
||||
struct Identifier {
|
||||
string name;
|
||||
unsigned size;
|
||||
};
|
||||
vector<Identifier> idlist;
|
||||
idlist.append({"SRAM_V", 6});
|
||||
idlist.append({"SRAM_F_V", 8});
|
||||
idlist.append({"EEPROM_V", 8});
|
||||
idlist.append({"FLASH_V", 7});
|
||||
idlist.append({"FLASH512_V", 10});
|
||||
idlist.append({"FLASH1M_V", 9});
|
||||
|
||||
lstring list;
|
||||
for(auto &id : idlist) {
|
||||
for(signed n = 0; n < size - 16; n++) {
|
||||
if(!memcmp(data + n, (const char*)id.name, id.size)) {
|
||||
const char *p = (const char*)data + n + id.size;
|
||||
if(p[0] >= '0' && p[0] <= '9'
|
||||
&& p[1] >= '0' && p[1] <= '9'
|
||||
&& p[2] >= '0' && p[2] <= '9'
|
||||
) {
|
||||
char text[16];
|
||||
memcpy(text, data + n, id.size + 3);
|
||||
text[id.size + 3] = 0;
|
||||
list.appendonce(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
identifiers = list.concatenate(",");
|
||||
|
||||
markup = "";
|
||||
markup.append("cartridge\n");
|
||||
markup.append(" rom name=program.rom size=0x", hex(size), "\n");
|
||||
if(0);
|
||||
else if(identifiers.beginswith("SRAM_V" )) markup.append(" ram name=save.ram type=SRAM size=0x8000\n");
|
||||
else if(identifiers.beginswith("SRAM_F_V" )) markup.append(" ram name=save.ram type=FRAM size=0x8000\n");
|
||||
else if(identifiers.beginswith("EEPROM_V" )) markup.append(" ram name=save.ram type=EEPROM size=0x0\n");
|
||||
else if(identifiers.beginswith("FLASH_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
|
||||
else if(identifiers.beginswith("FLASH512_V")) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
|
||||
else if(identifiers.beginswith("FLASH1M_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x20000\n");
|
||||
//if(identifiers.empty() == false) markup.append(" #detected: ", identifiers, "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,120 +0,0 @@
|
||||
#ifndef NALL_EMULATION_GAME_BOY_HPP
|
||||
#define NALL_EMULATION_GAME_BOY_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct GameBoyCartridge {
|
||||
string markup;
|
||||
inline GameBoyCartridge(uint8_t *data, unsigned size);
|
||||
|
||||
//private:
|
||||
struct Information {
|
||||
string mapper;
|
||||
bool ram;
|
||||
bool battery;
|
||||
bool rtc;
|
||||
bool rumble;
|
||||
|
||||
unsigned romsize;
|
||||
unsigned ramsize;
|
||||
|
||||
bool cgb;
|
||||
bool cgbonly;
|
||||
} info;
|
||||
};
|
||||
|
||||
GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
|
||||
markup = "";
|
||||
if(romsize < 0x4000) return;
|
||||
|
||||
info.mapper = "unknown";
|
||||
info.ram = false;
|
||||
info.battery = false;
|
||||
info.rtc = false;
|
||||
info.rumble = false;
|
||||
|
||||
info.romsize = 0;
|
||||
info.ramsize = 0;
|
||||
|
||||
unsigned base = romsize - 0x8000;
|
||||
if(romdata[base + 0x0104] == 0xce && romdata[base + 0x0105] == 0xed
|
||||
&& romdata[base + 0x0106] == 0x66 && romdata[base + 0x0107] == 0x66
|
||||
&& romdata[base + 0x0108] == 0xcc && romdata[base + 0x0109] == 0x0d
|
||||
&& romdata[base + 0x0147] >= 0x0b && romdata[base + 0x0147] <= 0x0d
|
||||
) {
|
||||
//MMM01 stores header at bottom of image
|
||||
//flip this around for consistency with all other mappers
|
||||
uint8_t header[0x8000];
|
||||
memcpy(header, romdata + base, 0x8000);
|
||||
memmove(romdata + 0x8000, romdata, romsize - 0x8000);
|
||||
memcpy(romdata, header, 0x8000);
|
||||
}
|
||||
|
||||
info.cgb = (romdata[0x0143] & 0x80) == 0x80;
|
||||
info.cgbonly = (romdata[0x0143] & 0xc0) == 0xc0;
|
||||
|
||||
switch(romdata[0x0147]) {
|
||||
case 0x00: info.mapper = "none"; break;
|
||||
case 0x01: info.mapper = "MBC1"; break;
|
||||
case 0x02: info.mapper = "MBC1"; info.ram = true; break;
|
||||
case 0x03: info.mapper = "MBC1"; info.ram = true; info.battery = true; break;
|
||||
case 0x05: info.mapper = "MBC2"; info.ram = true; break;
|
||||
case 0x06: info.mapper = "MBC2"; info.ram = true; info.battery = true; break;
|
||||
case 0x08: info.mapper = "none"; info.ram = true; break;
|
||||
case 0x09: info.mapper = "MBC0"; info.ram = true; info.battery = true; break;
|
||||
case 0x0b: info.mapper = "MMM01"; break;
|
||||
case 0x0c: info.mapper = "MMM01"; info.ram = true; break;
|
||||
case 0x0d: info.mapper = "MMM01"; info.ram = true; info.battery = true; break;
|
||||
case 0x0f: info.mapper = "MBC3"; info.rtc = true; info.battery = true; break;
|
||||
case 0x10: info.mapper = "MBC3"; info.rtc = true; info.ram = true; info.battery = true; break;
|
||||
case 0x11: info.mapper = "MBC3"; break;
|
||||
case 0x12: info.mapper = "MBC3"; info.ram = true; break;
|
||||
case 0x13: info.mapper = "MBC3"; info.ram = true; info.battery = true; break;
|
||||
case 0x19: info.mapper = "MBC5"; break;
|
||||
case 0x1a: info.mapper = "MBC5"; info.ram = true; break;
|
||||
case 0x1b: info.mapper = "MBC5"; info.ram = true; info.battery = true; break;
|
||||
case 0x1c: info.mapper = "MBC5"; info.rumble = true; break;
|
||||
case 0x1d: info.mapper = "MBC5"; info.rumble = true; info.ram = true; break;
|
||||
case 0x1e: info.mapper = "MBC5"; info.rumble = true; info.ram = true; info.battery = true; break;
|
||||
case 0xfc: break; //Pocket Camera
|
||||
case 0xfd: break; //Bandai TAMA5
|
||||
case 0xfe: info.mapper = "HuC3"; break;
|
||||
case 0xff: info.mapper = "HuC1"; info.ram = true; info.battery = true; break;
|
||||
}
|
||||
|
||||
switch(romdata[0x0148]) { default:
|
||||
case 0x00: info.romsize = 2 * 16 * 1024; break;
|
||||
case 0x01: info.romsize = 4 * 16 * 1024; break;
|
||||
case 0x02: info.romsize = 8 * 16 * 1024; break;
|
||||
case 0x03: info.romsize = 16 * 16 * 1024; break;
|
||||
case 0x04: info.romsize = 32 * 16 * 1024; break;
|
||||
case 0x05: info.romsize = 64 * 16 * 1024; break;
|
||||
case 0x06: info.romsize = 128 * 16 * 1024; break;
|
||||
case 0x07: info.romsize = 256 * 16 * 1024; break;
|
||||
case 0x52: info.romsize = 72 * 16 * 1024; break;
|
||||
case 0x53: info.romsize = 80 * 16 * 1024; break;
|
||||
case 0x54: info.romsize = 96 * 16 * 1024; break;
|
||||
}
|
||||
|
||||
switch(romdata[0x0149]) { default:
|
||||
case 0x00: info.ramsize = 0 * 1024; break;
|
||||
case 0x01: info.ramsize = 2 * 1024; break;
|
||||
case 0x02: info.ramsize = 8 * 1024; break;
|
||||
case 0x03: info.ramsize = 32 * 1024; break;
|
||||
}
|
||||
|
||||
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
|
||||
|
||||
markup = "";
|
||||
markup.append("cartridge\n");
|
||||
markup.append(" board type=", info.mapper, "\n");
|
||||
markup.append(" rom name=program.rom size=0x", hex(romsize), "\n");
|
||||
if(info.ramsize > 0) markup.append(" ram name=save.ram size=0x", hex(info.ramsize), "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,23 +0,0 @@
|
||||
#ifndef NALL_EMULATION_SATELLAVIEW_HPP
|
||||
#define NALL_EMULATION_SATELLAVIEW_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct SatellaviewCartridge {
|
||||
string markup;
|
||||
inline SatellaviewCartridge(const uint8_t *data, unsigned size);
|
||||
};
|
||||
|
||||
SatellaviewCartridge::SatellaviewCartridge(const uint8_t *data, unsigned size) {
|
||||
markup = "";
|
||||
|
||||
markup.append("cartridge\n");
|
||||
markup.append(" rom name=program.rom size=0x", hex(size), " type=FlashROM\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,31 +0,0 @@
|
||||
#ifndef NALL_EMULATION_SUFAMI_TURBO_HPP
|
||||
#define NALL_EMULATION_SUFAMI_TURBO_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct SufamiTurboCartridge {
|
||||
string markup;
|
||||
inline SufamiTurboCartridge(const uint8_t *data, unsigned size);
|
||||
};
|
||||
|
||||
SufamiTurboCartridge::SufamiTurboCartridge(const uint8_t *data, unsigned size) {
|
||||
markup = "";
|
||||
|
||||
if(size < 0x20000) return; //too small to be a valid game?
|
||||
if(memcmp(data, "BANDAI SFC-ADX", 14)) return; //missing required header?
|
||||
unsigned romsize = data[0x36] * 0x20000; //128KB
|
||||
unsigned ramsize = data[0x37] * 0x800; //2KB
|
||||
bool linkable = data[0x35] != 0x00; //TODO: unconfirmed
|
||||
|
||||
markup.append("cartridge", linkable ? " linkable" : "", "\n");
|
||||
markup.append(" rom name=program.rom size=0x", hex(romsize), "\n");
|
||||
if(ramsize)
|
||||
markup.append(" ram name=save.ram size=0x", hex(ramsize), "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,814 +0,0 @@
|
||||
#ifndef NALL_EMULATION_SUPER_FAMICOM_HPP
|
||||
#define NALL_EMULATION_SUPER_FAMICOM_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct SuperFamicomCartridge {
|
||||
string markup;
|
||||
inline SuperFamicomCartridge(const uint8_t *data, unsigned size);
|
||||
|
||||
//private:
|
||||
inline void read_header(const uint8_t *data, unsigned size);
|
||||
inline unsigned find_header(const uint8_t *data, unsigned size);
|
||||
inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr);
|
||||
|
||||
enum HeaderField {
|
||||
CartName = 0x00,
|
||||
Mapper = 0x15,
|
||||
RomType = 0x16,
|
||||
RomSize = 0x17,
|
||||
RamSize = 0x18,
|
||||
CartRegion = 0x19,
|
||||
Company = 0x1a,
|
||||
Version = 0x1b,
|
||||
Complement = 0x1c, //inverse checksum
|
||||
Checksum = 0x1e,
|
||||
ResetVector = 0x3c,
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
ModeNormal,
|
||||
ModeBsxSlotted,
|
||||
ModeBsx,
|
||||
ModeSufamiTurbo,
|
||||
ModeSuperGameBoy,
|
||||
};
|
||||
|
||||
enum Type {
|
||||
TypeNormal,
|
||||
TypeBsxSlotted,
|
||||
TypeBsxBios,
|
||||
TypeBsx,
|
||||
TypeSufamiTurboBios,
|
||||
TypeSufamiTurbo,
|
||||
TypeSuperGameBoy1Bios,
|
||||
TypeSuperGameBoy2Bios,
|
||||
TypeGameBoy,
|
||||
TypeUnknown,
|
||||
};
|
||||
|
||||
enum Region {
|
||||
NTSC,
|
||||
PAL,
|
||||
};
|
||||
|
||||
enum MemoryMapper {
|
||||
LoROM,
|
||||
HiROM,
|
||||
ExLoROM,
|
||||
ExHiROM,
|
||||
SuperFXROM,
|
||||
SA1ROM,
|
||||
SPC7110ROM,
|
||||
BSCLoROM,
|
||||
BSCHiROM,
|
||||
BSXROM,
|
||||
STROM,
|
||||
};
|
||||
|
||||
enum DSP1MemoryMapper {
|
||||
DSP1Unmapped,
|
||||
DSP1LoROM1MB,
|
||||
DSP1LoROM2MB,
|
||||
DSP1HiROM,
|
||||
};
|
||||
|
||||
bool loaded; //is a base cartridge inserted?
|
||||
unsigned crc32; //crc32 of all cartridges (base+slot(s))
|
||||
unsigned rom_size;
|
||||
unsigned ram_size;
|
||||
bool firmware_appended; //true if firmware is appended to end of ROM data
|
||||
|
||||
Mode mode;
|
||||
Type type;
|
||||
Region region;
|
||||
MemoryMapper mapper;
|
||||
DSP1MemoryMapper dsp1_mapper;
|
||||
|
||||
bool has_bsx_slot;
|
||||
bool has_superfx;
|
||||
bool has_sa1;
|
||||
bool has_sharprtc;
|
||||
bool has_epsonrtc;
|
||||
bool has_sdd1;
|
||||
bool has_spc7110;
|
||||
bool has_cx4;
|
||||
bool has_dsp1;
|
||||
bool has_dsp2;
|
||||
bool has_dsp3;
|
||||
bool has_dsp4;
|
||||
bool has_obc1;
|
||||
bool has_st010;
|
||||
bool has_st011;
|
||||
bool has_st018;
|
||||
};
|
||||
|
||||
SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size) {
|
||||
firmware_appended = false;
|
||||
|
||||
//skip copier header
|
||||
if((size & 0x7fff) == 512) data += 512, size -= 512;
|
||||
|
||||
markup = "";
|
||||
if(size < 0x8000) return;
|
||||
|
||||
read_header(data, size);
|
||||
|
||||
markup = "";
|
||||
if(type == TypeGameBoy) return;
|
||||
if(type == TypeBsx) return;
|
||||
if(type == TypeSufamiTurbo) return;
|
||||
|
||||
const char *range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? "0000-7fff" : "0000-ffff";
|
||||
markup.append("cartridge region=", region == NTSC ? "NTSC" : "PAL", "\n");
|
||||
|
||||
if(type == TypeSuperGameBoy1Bios || type == TypeSuperGameBoy2Bios) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" map id=rom address=00-7f,80-ff:8000-ffff mask=0x8000\n"
|
||||
" icd2 revision=1\n"
|
||||
" rom name=sgb.boot.rom size=0x100\n"
|
||||
" map id=io address=00-3f,80-bf:6000-7fff\n"
|
||||
);
|
||||
if((rom_size & 0x7fff) == 0x100) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x100;
|
||||
}
|
||||
}
|
||||
|
||||
else if(has_cx4) {
|
||||
markup.append(
|
||||
" hitachidsp model=HG51B169 frequency=20000000\n"
|
||||
" rom id=program name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" rom id=data name=cx4.data.rom size=0xc00\n"
|
||||
" ram id=data size=0xc00\n"
|
||||
" map id=io address=00-3f,80-bf:6000-7fff\n"
|
||||
" map id=rom address=00-7f,80-ff:8000-ffff mask=0x8000\n"
|
||||
" map id=ram address=70-77:0000-7fff\n"
|
||||
);
|
||||
if((rom_size & 0x7fff) == 0xc00) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0xc00;
|
||||
}
|
||||
}
|
||||
|
||||
else if(has_spc7110) {
|
||||
markup.append(
|
||||
" spc7110\n"
|
||||
" rom id=program name=program.rom size=0x100000\n"
|
||||
" rom id=data name=data.rom size=0x", hex(rom_size - 0x100000), "\n"
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
" map id=io address=00-3f,80-bf:4800-483f\n"
|
||||
" map id=io address=50:0000-ffff\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff\n"
|
||||
" map id=rom address=c0-ff:0000-ffff\n"
|
||||
" map id=ram address=00-3f,80-bf:6000-7fff mask=0xe000\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(has_sdd1) {
|
||||
markup.append(
|
||||
" sdd1\n"
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=io address=00-3f,80-bf:4800-4807\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff mask=0x8000\n"
|
||||
" map id=rom address=c0-ff:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=20-3f,a0-bf:6000-7fff mask=0xe000\n"
|
||||
" map id=ram address=70-7f:0000-7fff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == LoROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=rom address=00-7f,80-ff:8000-ffff mask=0x8000\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=70-7f,f0-ff:", range, "\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == HiROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff\n"
|
||||
" map id=rom address=40-7f,c0-ff:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=10-3f,90-bf:6000-7fff mask=0xe000\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == ExLoROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff mask=0x8000\n"
|
||||
" map id=rom address=40-7f:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=20-3f,a0-bf:6000-7fff\n"
|
||||
" map id=ram address=70-7f:0000-7fff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == ExHiROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=rom address=00-3f:8000-ffff base=0x400000\n"
|
||||
" map id=rom address=40-7f:0000-ffff base=0x400000\n"
|
||||
" map id=rom address=80-bf:8000-ffff mask=0xc00000\n"
|
||||
" map id=rom address=c0-ff:0000-ffff mask=0xc00000\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=20-3f,a0-bf:6000-7fff mask=0xe000\n"
|
||||
" map id=ram address=70-7f:", range, "\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == SuperFXROM) {
|
||||
markup.append(
|
||||
" superfx revision=3\n"
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=io address=00-3f,80-bf:3000-32ff\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff mask=0x8000\n"
|
||||
" map id=rom address=40-5f,c0-df:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=00-3f,80-bf:6000-7fff size=0x2000\n"
|
||||
" map id=ram address=70-71,f0-f1:0000-ffff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == SA1ROM) {
|
||||
markup.append(
|
||||
" sa1\n"
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram id=bitmap name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" ram id=internal size=0x800\n"
|
||||
" map id=io address=00-3f,80-bf:2200-23ff\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff\n"
|
||||
" map id=rom address=c0-ff:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=bwram address=00-3f,80-bf:6000-7fff\n"
|
||||
" map id=bwram address=40-4f:0000-ffff\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=iram address=00-3f,80-bf:3000-37ff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == BSCLoROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
" map id=rom address=00-1f:8000-ffff base=0x000000 mask=0x8000\n"
|
||||
" map id=rom address=20-3f:8000-ffff base=0x100000 mask=0x8000\n"
|
||||
" map id=rom address=80-9f:8000-ffff base=0x200000 mask=0x8000\n"
|
||||
" map id=rom address=a0-bf:8000-ffff base=0x100000 mask=0x8000\n"
|
||||
" map id=ram address=70-7f,f0-ff:0000-7fff\n"
|
||||
" bsxslot\n"
|
||||
" map id=rom address=c0-ef:0000-ffff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == BSCHiROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
" map id=rom address=00-1f,80-9f:8000-ffff\n"
|
||||
" map id=rom address=40-5f,c0-df:0000-ffff\n"
|
||||
" map id=ram address=20-3f,a0-bf:6000-7fff\n"
|
||||
" bsxslot\n"
|
||||
" map id=rom address=20-3f,a0-bf:8000-ffff\n"
|
||||
" map id=rom address=60-7f,e0-ff:0000-ffff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == BSXROM) {
|
||||
markup.append(
|
||||
" bsx\n"
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" ram id=save name=save.ram size=0x", hex(ram_size), "\n"
|
||||
" ram id=download name=bsx.ram size=0x40000\n"
|
||||
" map id=io address=00-3f,80-bf:5000-5fff\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff\n"
|
||||
" map id=rom address=40-7f,c0-ff:0000-ffff\n"
|
||||
" map id=ram address=20-3f:6000-7fff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == STROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" map id=rom address='00-1f,80-9f:8000-ffff mask=0x8000\n"
|
||||
" sufamiturbo\n"
|
||||
" slot id=A\n"
|
||||
" map id=rom address=20-3f,a0-bf:8000-ffff mask=0x8000\n"
|
||||
" map id=ram address=60-63,e0-e3:8000-ffff\n"
|
||||
" slot id=B\n"
|
||||
" map id=rom address=40-5f,c0-df:8000-ffff mask=0x8000\n"
|
||||
" map id=ram address=70-73,f0-f3:8000-ffff\n"
|
||||
);
|
||||
}
|
||||
|
||||
if(has_sharprtc) {
|
||||
markup.append(
|
||||
" sharprtc\n"
|
||||
" ram name=rtc.ram size=0x10\n"
|
||||
" map id=io address=00-3f,80-bf:2800-2801\n"
|
||||
);
|
||||
}
|
||||
|
||||
if(has_epsonrtc) {
|
||||
markup.append(
|
||||
" epsonrtc\n"
|
||||
" ram name=rtc.ram size=0x10\n"
|
||||
" map id=io address=00-3f,80-bf:4840-4842\n"
|
||||
);
|
||||
}
|
||||
|
||||
if(has_obc1) {
|
||||
markup.append(
|
||||
" obc1\n"
|
||||
" ram name=save.ram size=0x2000\n"
|
||||
" map id=io address=00-3f,80-bf:6000-7fff\n"
|
||||
);
|
||||
}
|
||||
|
||||
if(has_dsp1) {
|
||||
markup.append(
|
||||
" necdsp model=uPD7725 frequency=8000000\n"
|
||||
" rom id=program name=dsp1b.program.rom size=0x1800\n"
|
||||
" rom id=data name=dsp1b.data.rom size=0x800\n"
|
||||
" ram id=data size=0x200\n"
|
||||
);
|
||||
if(dsp1_mapper == DSP1LoROM1MB) markup.append(
|
||||
" map id=io address=20-3f,a0-bf:8000-ffff select=0x4000\n"
|
||||
);
|
||||
if(dsp1_mapper == DSP1LoROM2MB) markup.append(
|
||||
" map id=io address=60-6f,e0-ef:0000-7fff select=0x4000\n"
|
||||
);
|
||||
if(dsp1_mapper == DSP1HiROM) markup.append(
|
||||
" map id=io address=00-1f,80-9f:6000-7fff select=0x1000\n"
|
||||
);
|
||||
if((size & 0x7fff) == 0x2000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_dsp2) {
|
||||
markup.append(
|
||||
" necdsp model=uPD7725 frequency=8000000\n"
|
||||
" rom id=program name=dsp2.program.rom size=0x1800\n"
|
||||
" rom id=data name=dsp2.data.rom size=0x800\n"
|
||||
" ram id=data size=0x200\n"
|
||||
" map id=io address=20-3f,a0-bf:8000-ffff select=0x4000\n"
|
||||
);
|
||||
if((size & 0x7fff) == 0x2000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_dsp3) {
|
||||
markup.append(
|
||||
" necdsp model=uPD7725 frequency=8000000\n"
|
||||
" rom id=program name=dsp3.program.rom size=0x1800\n"
|
||||
" rom id=data name=dsp3.data.rom size=0x800\n"
|
||||
" ram id=data size=0x200\n"
|
||||
" map id=io address=20-3f,a0-bf:8000-ffff select=0x4000\n"
|
||||
);
|
||||
if((size & 0x7fff) == 0x2000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_dsp4) {
|
||||
markup.append(
|
||||
" necdsp model=uPD7725 frequency=8000000\n"
|
||||
" rom id=program name=dsp4.program.rom size=0x1800\n"
|
||||
" rom id=data name=dsp4.data.rom size=0x800\n"
|
||||
" ram id=data size=0x200\n"
|
||||
" map id=io address=30-3f,b0-bf:8000-ffff select=0x4000\n"
|
||||
);
|
||||
if((size & 0x7fff) == 0x2000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_st010) {
|
||||
markup.append(
|
||||
" necdsp model=uPD96050 frequency=11000000\n"
|
||||
" rom id=program name=st010.program.rom size=0xc000\n"
|
||||
" rom id=data name=st010.data.rom size=0x1000\n"
|
||||
" ram id=data name=save.ram size=0x1000\n"
|
||||
" map id=io address=60-67,e0-e7:0000-3fff select=0x0001\n"
|
||||
" map id=ram address=68-6f,e8-ef:0000-7fff\n"
|
||||
);
|
||||
if((size & 0xffff) == 0xd000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0xd000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_st011) {
|
||||
markup.append(
|
||||
" necdsp model=uPD96050 frequency=15000000\n"
|
||||
" rom id=program name=st011.program.rom size=0xc000\n"
|
||||
" rom id=data name=st011.data.rom size=0x1000\n"
|
||||
" ram id=data name=save.ram size=0x1000\n"
|
||||
" map id=io address=60-67,e0-e7:0000-3fff select=0x0001\n"
|
||||
" map id=ram address=68-6f,e8-ef:0000-7fff\n"
|
||||
);
|
||||
if((size & 0xffff) == 0xd000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0xd000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_st018) {
|
||||
markup.append(
|
||||
" armdsp frequency=21477272\n"
|
||||
" rom id=program name=st018.program.rom size=0x20000\n"
|
||||
" rom id=data name=st018.data.rom size=0x8000\n"
|
||||
" ram name=save.ram size=0x4000\n"
|
||||
" map id=io address=00-3f,80-bf:3800-38ff\n"
|
||||
);
|
||||
if((size & 0x3ffff) == 0x28000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x28000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SuperFamicomCartridge::read_header(const uint8_t *data, unsigned size) {
|
||||
type = TypeUnknown;
|
||||
mapper = LoROM;
|
||||
dsp1_mapper = DSP1Unmapped;
|
||||
region = NTSC;
|
||||
rom_size = size;
|
||||
ram_size = 0;
|
||||
|
||||
has_bsx_slot = false;
|
||||
has_superfx = false;
|
||||
has_sa1 = false;
|
||||
has_sharprtc = false;
|
||||
has_epsonrtc = false;
|
||||
has_sdd1 = false;
|
||||
has_spc7110 = false;
|
||||
has_cx4 = false;
|
||||
has_dsp1 = false;
|
||||
has_dsp2 = false;
|
||||
has_dsp3 = false;
|
||||
has_dsp4 = false;
|
||||
has_obc1 = false;
|
||||
has_st010 = false;
|
||||
has_st011 = false;
|
||||
has_st018 = false;
|
||||
|
||||
//=====================
|
||||
//detect Game Boy carts
|
||||
//=====================
|
||||
|
||||
if(size >= 0x0140) {
|
||||
if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66
|
||||
&& data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) {
|
||||
type = TypeGameBoy;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(size < 32768) {
|
||||
type = TypeUnknown;
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned index = find_header(data, size);
|
||||
const uint8_t mapperid = data[index + Mapper];
|
||||
const uint8_t rom_type = data[index + RomType];
|
||||
const uint8_t rom_size = data[index + RomSize];
|
||||
const uint8_t company = data[index + Company];
|
||||
const uint8_t regionid = data[index + CartRegion] & 0x7f;
|
||||
|
||||
ram_size = 1024 << (data[index + RamSize] & 7);
|
||||
if(ram_size == 1024) ram_size = 0; //no RAM present
|
||||
if(rom_size == 0 && ram_size) ram_size = 0; //fix for Bazooka Blitzkrieg's malformed header (swapped ROM and RAM sizes)
|
||||
|
||||
//0, 1, 13 = NTSC; 2 - 12 = PAL
|
||||
region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL;
|
||||
|
||||
//=======================
|
||||
//detect BS-X flash carts
|
||||
//=======================
|
||||
|
||||
if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) {
|
||||
if(data[index + 0x14] == 0x00) {
|
||||
const uint8_t n15 = data[index + 0x15];
|
||||
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
|
||||
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
|
||||
type = TypeBsx;
|
||||
mapper = BSXROM;
|
||||
region = NTSC; //BS-X only released in Japan
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================
|
||||
//detect Sufami Turbo carts
|
||||
//=========================
|
||||
|
||||
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
|
||||
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
|
||||
type = TypeSufamiTurboBios;
|
||||
} else {
|
||||
type = TypeSufamiTurbo;
|
||||
}
|
||||
mapper = STROM;
|
||||
region = NTSC; //Sufami Turbo only released in Japan
|
||||
return; //RAM size handled outside this routine
|
||||
}
|
||||
|
||||
//==========================
|
||||
//detect Super Game Boy BIOS
|
||||
//==========================
|
||||
|
||||
if(!memcmp(data + index, "Super GAMEBOY2", 14)) {
|
||||
type = TypeSuperGameBoy2Bios;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!memcmp(data + index, "Super GAMEBOY", 13)) {
|
||||
type = TypeSuperGameBoy1Bios;
|
||||
return;
|
||||
}
|
||||
|
||||
//=====================
|
||||
//detect standard carts
|
||||
//=====================
|
||||
|
||||
//detect presence of BS-X flash cartridge connector (reads extended header information)
|
||||
if(data[index - 14] == 'Z') {
|
||||
if(data[index - 11] == 'J') {
|
||||
uint8_t n13 = data[index - 13];
|
||||
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
|
||||
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
|
||||
has_bsx_slot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(has_bsx_slot) {
|
||||
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
|
||||
//BS-X base cart
|
||||
type = TypeBsxBios;
|
||||
mapper = BSXROM;
|
||||
region = NTSC; //BS-X only released in Japan
|
||||
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
|
||||
} else {
|
||||
type = TypeBsxSlotted;
|
||||
mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
|
||||
region = NTSC; //BS-X slotted cartridges only released in Japan
|
||||
}
|
||||
} else {
|
||||
//standard cart
|
||||
type = TypeNormal;
|
||||
|
||||
if(index == 0x7fc0 && size >= 0x401000) {
|
||||
mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0 && mapperid == 0x32) {
|
||||
mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0) {
|
||||
mapper = LoROM;
|
||||
} else if(index == 0xffc0) {
|
||||
mapper = HiROM;
|
||||
} else { //index == 0x40ffc0
|
||||
mapper = ExHiROM;
|
||||
}
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
|
||||
has_superfx = true;
|
||||
mapper = SuperFXROM;
|
||||
ram_size = 1024 << (data[index - 3] & 7);
|
||||
if(ram_size == 1024) ram_size = 0;
|
||||
}
|
||||
|
||||
if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) {
|
||||
has_sa1 = true;
|
||||
mapper = SA1ROM;
|
||||
}
|
||||
|
||||
if(mapperid == 0x35 && rom_type == 0x55) {
|
||||
has_sharprtc = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
|
||||
has_sdd1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
|
||||
has_spc7110 = true;
|
||||
has_epsonrtc = (rom_type == 0xf9);
|
||||
mapper = SPC7110ROM;
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && rom_type == 0xf3) {
|
||||
has_cx4 = true;
|
||||
}
|
||||
|
||||
if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(has_dsp1 == true) {
|
||||
if((mapperid & 0x2f) == 0x20 && size <= 0x100000) {
|
||||
dsp1_mapper = DSP1LoROM1MB;
|
||||
} else if((mapperid & 0x2f) == 0x20) {
|
||||
dsp1_mapper = DSP1LoROM2MB;
|
||||
} else if((mapperid & 0x2f) == 0x21) {
|
||||
dsp1_mapper = DSP1HiROM;
|
||||
}
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && rom_type == 0x05) {
|
||||
has_dsp2 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) {
|
||||
has_dsp3 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x03) {
|
||||
has_dsp4 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x25) {
|
||||
has_obc1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
|
||||
has_st010 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) {
|
||||
has_st011 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf5) {
|
||||
has_st018 = true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SuperFamicomCartridge::find_header(const uint8_t *data, unsigned size) {
|
||||
unsigned score_lo = score_header(data, size, 0x007fc0);
|
||||
unsigned score_hi = score_header(data, size, 0x00ffc0);
|
||||
unsigned score_ex = score_header(data, size, 0x40ffc0);
|
||||
if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
|
||||
|
||||
if(score_lo >= score_hi && score_lo >= score_ex) {
|
||||
return 0x007fc0;
|
||||
} else if(score_hi >= score_ex) {
|
||||
return 0x00ffc0;
|
||||
} else {
|
||||
return 0x40ffc0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SuperFamicomCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
|
||||
if(size < addr + 64) return 0; //image too small to contain header at this location?
|
||||
int score = 0;
|
||||
|
||||
uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8);
|
||||
uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8);
|
||||
uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8);
|
||||
|
||||
uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
|
||||
uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit
|
||||
|
||||
//$00:[000-7fff] contains uninitialized RAM and MMIO.
|
||||
//reset vector must point to ROM at $00:[8000-ffff] to be considered valid.
|
||||
if(resetvector < 0x8000) return 0;
|
||||
|
||||
//some images duplicate the header in multiple locations, and others have completely
|
||||
//invalid header information that cannot be relied upon.
|
||||
//below code will analyze the first opcode executed at the specified reset vector to
|
||||
//determine the probability that this is the correct header.
|
||||
|
||||
//most likely opcodes
|
||||
if(resetop == 0x78 //sei
|
||||
|| resetop == 0x18 //clc (clc; xce)
|
||||
|| resetop == 0x38 //sec (sec; xce)
|
||||
|| resetop == 0x9c //stz $nnnn (stz $4200)
|
||||
|| resetop == 0x4c //jmp $nnnn
|
||||
|| resetop == 0x5c //jml $nnnnnn
|
||||
) score += 8;
|
||||
|
||||
//plausible opcodes
|
||||
if(resetop == 0xc2 //rep #$nn
|
||||
|| resetop == 0xe2 //sep #$nn
|
||||
|| resetop == 0xad //lda $nnnn
|
||||
|| resetop == 0xae //ldx $nnnn
|
||||
|| resetop == 0xac //ldy $nnnn
|
||||
|| resetop == 0xaf //lda $nnnnnn
|
||||
|| resetop == 0xa9 //lda #$nn
|
||||
|| resetop == 0xa2 //ldx #$nn
|
||||
|| resetop == 0xa0 //ldy #$nn
|
||||
|| resetop == 0x20 //jsr $nnnn
|
||||
|| resetop == 0x22 //jsl $nnnnnn
|
||||
) score += 4;
|
||||
|
||||
//implausible opcodes
|
||||
if(resetop == 0x40 //rti
|
||||
|| resetop == 0x60 //rts
|
||||
|| resetop == 0x6b //rtl
|
||||
|| resetop == 0xcd //cmp $nnnn
|
||||
|| resetop == 0xec //cpx $nnnn
|
||||
|| resetop == 0xcc //cpy $nnnn
|
||||
) score -= 4;
|
||||
|
||||
//least likely opcodes
|
||||
if(resetop == 0x00 //brk #$nn
|
||||
|| resetop == 0x02 //cop #$nn
|
||||
|| resetop == 0xdb //stp
|
||||
|| resetop == 0x42 //wdm
|
||||
|| resetop == 0xff //sbc $nnnnnn,x
|
||||
) score -= 8;
|
||||
|
||||
//at times, both the header and reset vector's first opcode will match ...
|
||||
//fallback and rely on info validity in these cases to determine more likely header.
|
||||
|
||||
//a valid checksum is the biggest indicator of a valid header.
|
||||
if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4;
|
||||
|
||||
if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM
|
||||
if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM
|
||||
if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM
|
||||
if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
|
||||
|
||||
if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header
|
||||
if(data[addr + RomType] < 0x08) score++;
|
||||
if(data[addr + RomSize] < 0x10) score++;
|
||||
if(data[addr + RamSize] < 0x08) score++;
|
||||
if(data[addr + CartRegion] < 14) score++;
|
||||
|
||||
if(score < 0) score = 0;
|
||||
return score;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,15 +0,0 @@
|
||||
void Ananke::applyBeatPatch(vector<uint8_t> &buffer) {
|
||||
string name = {information.path, nall::basename(information.name), ".bps"};
|
||||
if(!file::exists(name)) return;
|
||||
|
||||
bpspatch patch;
|
||||
if(patch.modify(name) == false) return;
|
||||
patch.source(buffer.data(), buffer.size());
|
||||
vector<uint8_t> output;
|
||||
output.resize(patch.size());
|
||||
patch.target(output.data(), output.size());
|
||||
if(patch.apply() == bpspatch::result::success) {
|
||||
buffer = output;
|
||||
information.manifest = patch.metadata();
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 844 B |
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,6 +0,0 @@
|
||||
resource name=resource
|
||||
binary id=home name=home.png
|
||||
binary id=up name=up.png
|
||||
binary id=folder name=folder.png
|
||||
binary id=file name=file.png
|
||||
binary id=archive name=archive.png
|
@@ -1,156 +0,0 @@
|
||||
namespace resource {
|
||||
|
||||
const uint8_t home[606] = {
|
||||
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
|
||||
97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0,
|
||||
0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,10,14,20,37,19,83,42,210,59,0,0,1,235,73,
|
||||
68,65,84,56,203,149,147,191,107,83,81,20,128,191,123,251,222,75,211,64,242,36,160,85,135,100,81,123,19,104,85,172,
|
||||
17,92,28,140,66,19,167,135,212,74,39,145,162,163,24,92,58,180,110,193,169,254,24,234,212,37,139,245,199,96,19,240,
|
||||
15,240,63,16,121,91,92,196,90,219,240,98,81,137,33,121,215,33,246,217,151,80,33,103,186,92,206,247,221,115,14,231,
|
||||
194,1,225,41,85,172,128,174,128,246,148,122,200,48,177,7,123,74,105,79,169,61,73,113,104,120,177,56,163,203,55,111,
|
||||
252,87,34,250,225,154,235,110,20,148,98,253,220,89,182,39,78,113,228,240,56,39,188,38,83,107,107,212,92,151,130,82,
|
||||
215,14,185,110,117,64,176,31,126,83,156,97,247,248,49,148,202,50,22,141,210,106,181,24,219,252,74,182,92,30,144,136,
|
||||
126,184,122,251,22,157,100,146,116,42,77,42,149,2,64,107,104,183,219,236,214,235,156,44,149,66,18,177,31,126,247,160,
|
||||
68,199,48,201,229,46,96,219,118,208,154,214,26,0,223,247,249,185,181,69,114,110,46,144,136,10,232,130,82,188,127,84,
|
||||
198,107,54,57,63,157,35,30,143,35,132,8,9,124,223,15,206,134,16,140,230,243,212,92,23,3,184,3,172,78,157,62,
|
||||
195,253,210,61,54,170,111,3,240,201,202,51,86,30,63,69,139,17,116,167,133,48,70,209,221,223,44,47,45,209,238,165,
|
||||
204,202,121,120,190,48,153,33,17,79,0,160,212,4,153,140,34,155,205,244,94,20,146,145,244,149,222,196,83,121,144,22,
|
||||
90,107,22,38,51,204,195,186,236,239,211,178,44,76,211,196,48,140,224,254,75,227,7,0,31,234,59,116,187,126,144,11,
|
||||
48,32,144,82,34,165,196,146,159,1,232,96,241,241,83,3,128,111,222,47,58,34,18,90,36,99,96,179,254,14,207,231,
|
||||
40,0,151,46,78,115,53,26,67,234,113,46,75,147,237,77,66,21,24,253,21,252,155,126,239,165,239,222,14,162,217,56,
|
||||
112,245,3,65,36,18,193,182,109,18,137,4,2,137,16,16,139,197,112,28,103,0,178,44,43,252,23,174,207,58,175,0,
|
||||
135,225,98,245,229,139,215,119,255,0,86,248,213,163,133,187,128,26,0,0,0,0,73,69,78,68,174,66,96,130,
|
||||
};
|
||||
|
||||
const uint8_t up[652] = {
|
||||
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
|
||||
97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
|
||||
101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,30,73,68,65,84,56,141,
|
||||
149,147,79,104,19,65,20,198,191,55,187,51,217,141,133,122,104,76,255,209,130,4,237,193,64,42,168,208,213,138,104,68,
|
||||
98,69,45,20,114,107,74,201,73,144,98,22,193,171,199,82,145,160,23,15,69,79,30,165,66,241,226,77,42,94,68,80,
|
||||
208,67,41,180,42,149,148,52,137,166,154,38,217,217,25,15,81,172,33,169,246,29,223,251,230,199,124,223,155,33,173,53,
|
||||
218,213,201,155,124,30,0,150,102,189,233,118,26,214,110,224,184,60,221,223,21,73,246,117,29,76,58,46,79,239,9,224,
|
||||
184,60,26,16,251,178,87,206,76,6,199,70,147,193,128,8,102,29,151,71,255,11,224,184,188,3,132,197,137,115,211,214,
|
||||
150,151,199,150,183,137,248,200,37,11,132,69,199,229,29,255,4,16,225,209,232,112,162,167,55,52,72,159,75,31,240,177,
|
||||
244,30,157,157,157,20,27,58,209,13,194,195,93,1,142,203,211,253,7,34,137,179,71,199,249,74,254,13,12,198,97,50,
|
||||
142,87,107,11,136,13,29,23,161,253,61,137,230,60,216,142,195,81,193,237,108,42,225,218,235,229,101,104,242,32,132,137,
|
||||
58,125,199,54,21,241,46,255,28,23,78,95,13,154,166,248,43,15,182,211,247,212,88,198,82,228,161,34,75,16,1,1,
|
||||
30,224,40,203,117,8,219,64,65,174,162,168,86,113,254,212,69,139,216,159,60,216,111,223,241,99,227,221,135,250,98,148,
|
||||
175,172,193,228,6,56,55,80,81,5,84,141,175,16,54,131,176,25,150,191,189,64,184,55,68,71,14,15,135,137,53,242,
|
||||
160,145,140,153,30,232,142,100,111,76,220,177,5,15,64,65,194,135,135,215,185,5,188,45,60,131,100,21,16,35,36,6,
|
||||
50,208,90,67,41,160,94,175,225,241,211,249,74,177,180,57,99,18,67,234,83,110,197,158,185,119,185,225,137,163,122,247,
|
||||
250,19,171,44,115,240,141,109,112,193,192,76,130,193,9,115,15,110,87,101,77,91,191,236,7,137,33,101,46,205,122,78,
|
||||
211,38,180,214,10,165,250,23,48,131,96,112,6,30,104,64,100,77,91,47,231,60,218,245,29,0,128,210,62,126,212,139,
|
||||
141,43,251,26,178,174,160,85,235,63,99,182,106,250,74,162,90,219,134,50,53,160,21,124,73,240,189,61,0,164,47,17,
|
||||
31,188,6,98,0,49,2,17,246,0,32,108,220,186,63,25,110,37,38,194,70,115,239,39,48,247,197,219,182,208,154,34,
|
||||
0,0,0,0,73,69,78,68,174,66,96,130,
|
||||
};
|
||||
|
||||
const uint8_t folder[1176] = {
|
||||
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
|
||||
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
|
||||
215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
|
||||
97,112,101,46,111,114,103,155,238,60,26,0,0,4,21,73,68,65,84,88,133,237,151,61,111,28,69,24,199,127,51,187,
|
||||
119,123,175,246,57,145,48,9,5,86,148,194,31,0,9,5,33,83,110,149,130,26,137,2,90,58,62,64,36,62,64,74,
|
||||
132,68,133,104,161,67,72,167,64,23,201,138,2,138,210,69,194,198,196,40,22,198,247,186,119,222,151,153,157,25,138,123,
|
||||
201,58,119,142,19,114,82,40,248,75,143,118,118,118,247,121,126,243,60,51,179,187,34,12,67,94,167,228,107,141,254,95,
|
||||
0,240,1,110,220,184,225,43,165,126,54,198,188,119,209,3,66,136,220,90,251,249,195,135,15,191,92,25,64,154,166,91,
|
||||
149,74,229,157,155,55,111,250,73,146,224,156,3,192,90,11,48,63,159,182,253,59,119,238,220,222,222,222,254,254,209,163,
|
||||
71,199,43,1,80,74,101,213,106,213,29,29,29,209,233,116,112,206,45,53,0,33,4,155,155,155,249,193,193,193,71,192,
|
||||
237,149,0,104,173,211,44,203,196,165,75,27,24,99,176,214,158,9,92,204,132,16,130,106,181,90,63,60,60,252,98,123,
|
||||
123,251,214,172,127,153,156,115,218,90,251,241,222,222,222,143,207,5,0,178,44,203,188,78,167,75,167,211,153,3,204,142,
|
||||
179,32,197,64,91,91,91,245,70,163,65,179,217,68,8,129,16,2,0,41,229,188,29,69,17,247,238,221,187,5,60,31,
|
||||
224,242,229,203,105,191,223,247,54,54,90,104,173,23,0,150,149,98,166,56,142,1,230,16,69,139,227,24,99,76,237,188,
|
||||
224,115,128,221,221,93,117,237,218,53,217,235,245,230,25,0,150,130,204,178,81,212,108,196,197,224,82,202,25,64,229,66,
|
||||
0,0,99,140,109,54,215,100,154,102,220,63,180,220,221,63,27,228,188,58,63,171,25,204,228,153,22,206,93,185,206,219,
|
||||
31,232,226,61,158,39,190,93,255,253,155,79,206,0,56,231,116,183,219,245,187,221,46,143,59,13,62,253,240,93,174,191,
|
||||
181,81,112,54,61,62,47,250,18,200,105,207,60,206,147,206,152,175,190,187,255,254,236,188,152,1,221,104,212,171,90,107,
|
||||
212,31,146,102,181,204,254,223,99,162,56,159,140,108,233,104,207,35,17,11,77,1,52,42,62,42,211,8,65,103,118,121,
|
||||
190,21,27,99,116,191,63,160,219,237,18,103,150,122,181,132,53,147,27,228,212,193,179,134,59,207,220,83,179,19,179,214,
|
||||
145,100,134,52,203,193,137,249,6,86,44,65,86,169,4,180,90,45,50,35,168,5,37,172,83,120,114,53,175,139,113,154,
|
||||
83,111,250,140,134,57,214,186,39,11,0,121,158,171,40,26,209,239,15,208,230,13,170,129,143,115,14,79,158,155,231,23,
|
||||
147,131,81,170,209,185,37,240,37,113,170,173,49,249,209,2,128,49,38,43,151,75,84,155,27,172,213,202,56,64,10,177,
|
||||
188,248,47,40,227,28,163,84,147,91,135,231,9,130,178,199,105,146,41,33,196,201,2,128,181,54,27,141,198,28,15,20,
|
||||
235,245,75,232,220,190,210,232,51,109,137,51,3,78,224,79,253,148,61,201,232,84,229,130,167,147,176,152,129,212,247,125,
|
||||
188,32,160,73,153,76,217,11,131,76,230,152,195,186,201,62,97,236,196,84,110,151,173,72,156,131,193,40,181,185,115,139,
|
||||
0,214,218,100,60,30,211,25,248,84,90,101,162,68,147,233,233,75,8,200,141,69,27,135,206,45,185,153,110,203,47,153,
|
||||
149,68,25,134,227,196,19,210,91,90,130,84,8,129,12,26,148,74,62,163,68,19,37,154,84,25,148,182,47,29,108,153,
|
||||
226,44,231,52,209,62,231,148,32,142,227,83,122,81,133,74,13,30,159,196,12,99,189,212,209,191,145,0,134,177,66,25,
|
||||
83,250,245,234,94,47,124,252,12,128,181,54,6,129,245,107,100,185,37,138,53,74,95,60,15,94,84,82,10,78,134,9,
|
||||
82,200,56,220,221,157,59,62,51,7,146,36,33,74,12,87,175,4,84,3,143,160,188,186,111,86,41,4,105,170,144,82,
|
||||
12,139,253,197,18,36,0,154,18,235,85,143,122,105,21,85,127,42,231,28,253,36,65,88,59,88,0,104,183,219,98,103,
|
||||
103,199,68,177,98,112,234,248,243,100,136,236,190,226,14,184,68,199,199,67,155,36,195,7,187,237,118,45,12,195,120,14,
|
||||
0,120,81,20,253,244,203,161,248,76,173,227,255,182,255,202,31,187,103,228,192,2,88,157,30,28,63,248,225,235,226,53,
|
||||
49,251,53,107,183,219,205,96,109,237,205,90,208,218,116,56,41,133,92,201,18,200,141,49,42,75,107,185,16,121,62,58,
|
||||
233,1,127,133,97,216,91,0,152,66,72,32,96,146,153,124,21,0,83,5,64,10,168,48,12,207,44,45,241,255,207,233,
|
||||
235,6,248,7,188,50,165,151,203,8,55,43,0,0,0,0,73,69,78,68,174,66,96,130,
|
||||
};
|
||||
|
||||
const uint8_t file[844] = {
|
||||
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
|
||||
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,3,3,73,68,65,84,88,133,229,151,79,110,212,
|
||||
48,24,197,127,182,227,56,81,39,116,36,132,0,169,167,226,2,101,209,93,239,192,150,37,171,46,43,245,4,92,164,183,
|
||||
40,82,89,21,36,134,84,76,18,59,44,90,91,142,243,103,50,21,59,62,201,138,227,120,236,247,189,247,252,37,3,255,
|
||||
123,136,116,224,234,234,234,67,150,101,55,64,165,148,66,74,137,115,14,107,109,104,93,215,133,107,220,95,26,3,118,93,
|
||||
215,125,188,185,185,249,26,239,151,141,16,9,113,125,113,113,81,61,247,195,120,223,247,131,121,241,253,202,126,117,121,121,
|
||||
121,13,44,3,176,214,110,1,110,111,111,17,66,32,165,12,87,41,37,74,41,148,82,8,33,80,74,177,221,110,209,90,
|
||||
15,158,167,125,33,4,251,253,158,166,105,182,233,126,50,29,232,251,30,33,68,104,64,88,208,55,207,78,215,117,24,99,
|
||||
70,191,247,109,77,140,24,112,206,133,13,98,6,210,38,165,164,40,138,48,127,42,60,16,159,200,20,168,17,3,206,185,
|
||||
17,11,177,4,49,40,231,28,77,211,28,204,114,137,141,17,0,107,109,232,207,177,224,159,41,165,208,90,31,100,97,9,
|
||||
200,36,3,233,230,233,189,181,150,60,207,41,203,146,186,174,233,186,46,204,91,202,124,21,0,207,192,156,9,1,140,49,
|
||||
20,69,65,93,215,100,89,70,158,231,3,173,231,50,127,17,128,88,123,127,239,105,55,198,96,140,161,109,219,85,174,95,
|
||||
13,96,202,132,41,35,62,218,182,5,240,213,110,113,211,163,25,72,179,23,66,208,52,13,90,235,80,92,252,243,199,199,
|
||||
199,229,244,97,210,172,7,37,136,93,47,165,164,170,42,178,44,67,8,193,201,201,73,232,151,101,57,152,63,197,194,139,
|
||||
37,136,77,168,181,198,90,27,42,97,215,117,97,222,210,113,92,205,128,215,50,205,222,103,16,75,226,156,11,12,120,64,
|
||||
62,203,41,38,142,146,32,213,190,239,123,172,181,225,69,228,105,143,193,214,117,141,115,110,86,134,163,0,196,11,11,33,
|
||||
200,178,140,170,170,6,115,235,186,14,114,120,79,40,165,70,155,28,205,64,74,181,7,32,165,28,248,195,24,67,158,231,
|
||||
1,172,82,106,117,89,158,5,144,158,231,212,7,113,40,165,6,12,120,169,210,170,184,20,171,142,161,47,54,177,254,254,
|
||||
236,123,205,227,58,177,219,237,86,3,152,250,34,26,80,37,132,96,179,217,80,150,229,200,96,155,205,6,173,245,96,190,
|
||||
49,38,48,177,6,196,162,9,189,15,178,44,27,140,123,96,109,219,142,36,136,95,90,107,98,214,3,177,9,211,240,99,
|
||||
190,8,77,197,161,162,52,11,32,149,96,238,37,3,79,18,164,0,60,232,135,135,135,17,107,171,1,132,135,82,6,157,
|
||||
227,240,0,119,187,221,8,160,63,5,167,167,167,65,138,163,62,201,226,5,189,235,167,88,232,251,30,173,245,36,56,95,
|
||||
71,252,247,229,82,164,167,160,146,82,254,234,251,254,213,217,217,89,24,156,211,185,40,138,197,197,211,242,155,231,249,14,
|
||||
120,7,252,4,246,41,128,10,120,115,127,127,255,249,252,252,252,19,80,46,174,190,50,242,60,167,105,26,132,16,127,238,
|
||||
238,238,190,0,239,1,5,124,7,92,156,90,14,188,5,94,3,91,192,60,79,252,23,241,135,167,100,107,224,7,240,13,
|
||||
248,13,19,127,78,159,55,46,158,1,173,43,103,135,195,1,45,79,180,239,129,96,140,191,182,58,238,12,241,249,173,246,
|
||||
0,0,0,0,73,69,78,68,174,66,96,130,
|
||||
};
|
||||
|
||||
const uint8_t archive[1067] = {
|
||||
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
|
||||
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,3,226,73,68,65,84,88,133,237,150,95,136,212,
|
||||
85,20,199,63,231,222,251,251,253,102,102,103,255,142,166,27,150,150,88,144,102,127,31,218,40,49,200,36,136,68,200,30,
|
||||
20,218,212,13,89,84,8,138,30,138,222,10,122,232,205,212,44,89,55,145,192,146,32,168,232,37,42,208,212,84,212,165,
|
||||
32,162,168,101,21,203,221,213,253,51,237,56,51,191,223,189,61,252,134,217,217,117,86,155,26,240,161,253,194,225,222,115,
|
||||
239,253,157,251,189,231,158,223,57,23,102,49,139,255,59,228,90,147,189,47,164,151,26,63,120,51,12,243,107,156,184,60,
|
||||
14,59,229,75,135,2,130,234,70,5,45,114,196,89,121,233,185,247,198,142,215,68,96,223,150,166,37,126,144,58,168,148,
|
||||
190,115,249,163,107,82,205,109,237,68,81,136,32,24,63,0,229,163,76,2,209,1,202,76,138,232,4,202,11,80,58,32,
|
||||
180,206,253,240,117,143,244,159,250,232,47,27,113,209,138,125,165,115,87,246,208,117,9,236,239,106,190,77,18,254,137,101,
|
||||
43,58,91,231,45,186,67,141,12,156,117,103,15,127,38,78,0,231,112,81,30,163,4,163,193,211,130,209,130,167,193,76,
|
||||
235,39,146,45,100,30,123,195,93,56,190,215,133,131,167,213,207,231,70,115,69,203,182,141,239,142,247,84,238,167,43,149,
|
||||
3,93,201,5,36,147,199,239,93,221,221,182,248,129,213,122,252,66,31,167,191,249,88,10,133,43,216,40,196,218,8,37,
|
||||
160,68,80,10,180,18,148,18,180,2,53,173,47,46,79,225,242,79,114,83,199,139,146,255,227,20,173,137,208,27,30,203,
|
||||
175,122,250,65,51,240,233,201,98,223,85,30,120,191,171,97,94,50,149,60,185,108,229,166,246,246,37,29,218,141,253,138,
|
||||
23,52,80,140,38,157,100,157,5,107,65,25,68,123,177,168,88,40,181,162,13,136,46,27,78,54,207,167,144,27,167,175,
|
||||
119,45,162,52,191,93,24,155,112,214,110,234,220,147,61,88,38,208,179,49,61,55,104,76,29,107,91,176,116,161,56,114,
|
||||
67,3,125,233,71,158,121,141,230,185,183,146,27,57,55,121,95,162,64,52,136,138,5,13,74,226,13,69,202,115,18,7,
|
||||
40,0,65,67,134,200,193,151,59,214,227,108,33,151,12,252,68,54,151,15,113,172,239,220,147,61,36,123,55,55,181,25,
|
||||
99,135,1,252,32,117,62,44,230,191,187,253,190,39,215,221,179,122,27,209,200,47,224,44,206,89,112,46,182,234,92,172,
|
||||
151,250,255,100,204,207,220,69,118,228,34,95,125,240,242,69,92,120,38,178,246,9,0,177,108,85,198,68,111,3,160,100,
|
||||
205,149,98,126,69,208,152,121,106,249,170,173,68,163,191,99,139,19,216,98,14,23,94,41,139,13,167,233,197,220,117,199,
|
||||
242,127,158,165,113,206,34,238,94,185,161,69,180,30,182,74,47,22,56,234,20,59,167,252,5,7,182,207,57,252,248,230,
|
||||
221,15,167,51,11,175,153,31,254,11,190,221,191,61,55,116,254,199,141,157,187,42,98,0,96,95,119,211,67,98,237,81,
|
||||
165,117,81,68,121,162,52,206,70,53,152,118,51,206,136,196,182,28,46,111,163,40,0,120,126,79,86,166,16,112,32,31,
|
||||
118,55,183,0,20,109,116,169,99,237,171,12,28,235,169,110,177,70,220,124,255,58,190,255,124,7,158,210,109,0,158,248,
|
||||
197,103,119,13,102,1,76,153,37,56,118,143,94,6,232,221,146,38,145,74,147,76,165,235,66,32,72,54,2,176,161,100,
|
||||
191,18,230,170,213,37,40,109,240,147,13,117,33,160,141,55,227,220,140,4,180,241,240,19,245,241,128,54,126,237,4,140,
|
||||
246,8,18,245,241,128,249,247,30,184,145,4,60,143,160,78,49,96,252,242,21,248,64,225,90,4,2,160,21,226,138,103,
|
||||
252,20,184,90,114,65,21,136,66,169,114,209,189,5,24,3,46,1,81,53,2,205,64,198,41,111,112,184,255,204,220,204,
|
||||
194,142,210,112,69,146,113,149,9,199,149,117,87,117,77,220,14,245,247,97,197,27,2,230,151,246,140,74,36,166,16,208,
|
||||
37,221,251,226,244,196,91,216,157,175,11,97,75,141,231,173,10,135,25,249,228,196,196,59,64,2,240,168,120,135,76,207,
|
||||
249,173,64,166,212,6,196,119,86,15,20,0,5,100,137,79,126,30,40,86,35,0,144,4,90,74,155,207,24,164,53,34,
|
||||
36,190,143,81,96,28,38,31,183,215,171,122,245,170,138,51,87,170,89,220,104,252,13,162,179,143,166,193,167,182,66,0,
|
||||
0,0,0,73,69,78,68,174,66,96,130,
|
||||
};
|
||||
|
||||
};
|
@@ -1,7 +0,0 @@
|
||||
namespace resource {
|
||||
extern const uint8_t home[606];
|
||||
extern const uint8_t up[652];
|
||||
extern const uint8_t folder[1176];
|
||||
extern const uint8_t file[844];
|
||||
extern const uint8_t archive[1067];
|
||||
};
|
@@ -1,90 +0,0 @@
|
||||
void Ananke::copySufamiTurboSaves(const string &pathname) {
|
||||
if(!file::exists({pathname, "save.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".srm"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".srm"}, {pathname, "save.ram"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string Ananke::createSufamiTurboDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest) {
|
||||
string pathname = {
|
||||
libraryPath, "Sufami Turbo/",
|
||||
document["release/information/name"].text(),
|
||||
" (", document["release/information/region"].text(), ")",
|
||||
" (", document["release/information/revision"].text(), ")",
|
||||
".st/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
//strip "release" root node from database entry (since a single game manifest isn't part of a database)
|
||||
string markup = manifest;
|
||||
markup.replace("\n ", "\n");
|
||||
markup.replace("information", "\ninformation");
|
||||
markup.ltrim<1>("release\n");
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
copySufamiTurboSaves(pathname);
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::createSufamiTurboHeuristic(vector<uint8_t> &buffer) {
|
||||
string pathname = {
|
||||
libraryPath, "Sufami Turbo/",
|
||||
nall::basename(information.name),
|
||||
".st/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
file::write({pathname, "manifest.bml"}, {
|
||||
"unverified\n",
|
||||
"\n",
|
||||
"cartridge\n",
|
||||
" rom name=program.rom size=0x", hex(buffer.size()), "\n",
|
||||
" ram name=save.ram size=0x2000\n",
|
||||
"\n",
|
||||
"information\n",
|
||||
" title: ", nall::basename(information.name), "\n"
|
||||
});
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
copySufamiTurboSaves(pathname);
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::openSufamiTurbo(vector<uint8_t> &buffer) {
|
||||
string sha256 = nall::sha256(buffer.data(), buffer.size());
|
||||
|
||||
string databaseText = string::read({configpath(), "ananke/database/Sufami Turbo.bml"}).strip();
|
||||
if(databaseText.empty()) databaseText = string{Database::SufamiTurbo}.strip();
|
||||
lstring databaseItem = databaseText.split("\n\n");
|
||||
|
||||
for(auto &item : databaseItem) {
|
||||
item.append("\n");
|
||||
auto document = Markup::Document(item);
|
||||
|
||||
if(document["release/information/sha256"].text() == sha256) {
|
||||
return createSufamiTurboDatabase(buffer, document, item);
|
||||
}
|
||||
}
|
||||
|
||||
return createSufamiTurboHeuristic(buffer);
|
||||
}
|
||||
|
||||
string Ananke::syncSufamiTurbo(const string &pathname) {
|
||||
auto buffer = file::read({pathname, "program.rom"});
|
||||
if(buffer.size() == 0) return "";
|
||||
|
||||
auto save = file::read({pathname, "save.ram"});
|
||||
if(save.size() == 0) save = file::read({pathname, "save.rwm"});
|
||||
|
||||
directory::remove(pathname);
|
||||
information.path = pathname;
|
||||
information.name = notdir(string{pathname}.rtrim<1>("/"));
|
||||
string outputPath = openSufamiTurbo(buffer);
|
||||
|
||||
if(save.size()) file::write({outputPath, "save.ram"}, save);
|
||||
|
||||
return outputPath;
|
||||
}
|
@@ -1,215 +0,0 @@
|
||||
void Ananke::copySuperFamicomSaves(const string &pathname) {
|
||||
if(!file::exists({pathname, "save.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".srm"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".srm"}, {pathname, "save.ram"});
|
||||
}
|
||||
}
|
||||
|
||||
if(!file::exists({pathname, "rtc.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".rtc"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".rtc"}, {pathname, "rtc.ram"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string Ananke::createSuperFamicomDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest) {
|
||||
string pathname = {
|
||||
libraryPath, "Super Famicom/",
|
||||
document["release/information/name"].text(),
|
||||
" (", document["release/information/region"].text(), ")",
|
||||
" (", document["release/information/revision"].text(), ")",
|
||||
".sfc/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
//strip "release" root node from database entry (since a single game manifest isn't part of a database)
|
||||
string markup = manifest;
|
||||
markup.replace("\n ", "\n");
|
||||
markup.replace("information", "\ninformation");
|
||||
markup.ltrim<1>("release\n");
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
|
||||
unsigned offset = 0;
|
||||
for(auto &node : document["release/information/configuration"]) {
|
||||
if(node.name != "rom") continue;
|
||||
string name = node["name"].text();
|
||||
unsigned size = node["size"].decimal();
|
||||
file::write({pathname, name}, buffer.data() + offset, size);
|
||||
offset += size;
|
||||
}
|
||||
|
||||
copySuperFamicomSaves(pathname);
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::createSuperFamicomHeuristic(vector<uint8_t> &buffer) {
|
||||
string pathname = {
|
||||
libraryPath, "Super Famicom/",
|
||||
nall::basename(information.name),
|
||||
".sfc/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
if((buffer.size() & 0x7fff) == 512) buffer.remove(0, 512); //strip copier header, if present
|
||||
|
||||
SuperFamicomCartridge info(buffer.data(), buffer.size());
|
||||
string markup = {"unverified\n\n", info.markup};
|
||||
markup.append("\ninformation\n title: ", nall::basename(information.name), "\n");
|
||||
if(!information.manifest.empty()) markup = information.manifest; //override with embedded beat manifest, if one exists
|
||||
information.manifest = markup; //save for use with firmware routine below
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
|
||||
if(!markup.find("spc7110")) {
|
||||
file::write({pathname, "program.rom"}, buffer.data(), info.rom_size);
|
||||
} else {
|
||||
file::write({pathname, "program.rom"}, buffer.data(), 0x100000);
|
||||
file::write({pathname, "data.rom"}, buffer.data() + 0x100000, info.rom_size - 0x100000);
|
||||
}
|
||||
|
||||
createSuperFamicomHeuristicFirmware(buffer, pathname, info.firmware_appended);
|
||||
copySuperFamicomSaves(pathname);
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
void Ananke::createSuperFamicomHeuristicFirmware(vector<uint8_t> &buffer, const string &pathname, bool firmware_appended) {
|
||||
auto copyFirmwareInternal = [&](const string &name, unsigned programSize, unsigned dataSize, unsigned bootSize) {
|
||||
//firmware appended directly onto .sfc file
|
||||
string basename = nall::basename(name);
|
||||
if(programSize) file::write({pathname, basename, ".program.rom"}, buffer.data() + buffer.size() - programSize - dataSize - bootSize, programSize);
|
||||
if(dataSize) file::write({pathname, basename, ".data.rom"}, buffer.data() + buffer.size() - dataSize - bootSize, dataSize);
|
||||
if(bootSize) file::write({pathname, basename, ".boot.rom"}, buffer.data() + buffer.size() - bootSize, bootSize);
|
||||
};
|
||||
|
||||
auto copyFirmwareExternal = [&](const string &name, unsigned programSize, unsigned dataSize, unsigned bootSize) {
|
||||
//firmware stored in external file
|
||||
auto buffer = file::read({information.path, name}); //try and read from the containing directory
|
||||
if(buffer.size() == 0) buffer = extractFile(name); //try and read from the containing archive, if one exists
|
||||
if(buffer.size() == 0) {
|
||||
if(thread::primary()) MessageWindow().setText({
|
||||
"Error: ", information.name, "\n\n",
|
||||
"Required firmware ", name, " not found. Game will not be playable!\n\n",
|
||||
"You must obtain this file, and place it in the same folder as this game."
|
||||
}).error();
|
||||
return;
|
||||
}
|
||||
|
||||
string basename = nall::basename(name);
|
||||
if(programSize) file::write({pathname, basename, ".program.rom"}, buffer.data(), programSize);
|
||||
if(dataSize) file::write({pathname, basename, ".data.rom"}, buffer.data() + programSize, dataSize);
|
||||
if(bootSize) file::write({pathname, basename, ".boot.rom"}, buffer.data() + programSize + dataSize, bootSize);
|
||||
};
|
||||
|
||||
auto copyFirmware = [&](const string &name, unsigned programSize, unsigned dataSize, unsigned bootSize = 0) {
|
||||
if(firmware_appended == 1) copyFirmwareInternal(name, programSize, dataSize, bootSize);
|
||||
if(firmware_appended == 0) copyFirmwareExternal(name, programSize, dataSize, bootSize);
|
||||
};
|
||||
|
||||
string markup = information.manifest;
|
||||
if(markup.find("dsp1.program.rom" )) copyFirmware("dsp1.rom", 0x001800, 0x000800);
|
||||
if(markup.find("dsp1b.program.rom")) copyFirmware("dsp1b.rom", 0x001800, 0x000800);
|
||||
if(markup.find("dsp2.program.rom" )) copyFirmware("dsp2.rom", 0x001800, 0x000800);
|
||||
if(markup.find("dsp3.program.rom" )) copyFirmware("dsp3.rom", 0x001800, 0x000800);
|
||||
if(markup.find("dsp4.program.rom" )) copyFirmware("dsp4.rom", 0x001800, 0x000800);
|
||||
if(markup.find("st010.program.rom")) copyFirmware("st010.rom", 0x00c000, 0x001000);
|
||||
if(markup.find("st011.program.rom")) copyFirmware("st011.rom", 0x00c000, 0x001000);
|
||||
if(markup.find("st018.program.rom")) copyFirmware("st018.rom", 0x020000, 0x008000);
|
||||
if(markup.find("cx4.data.rom" )) copyFirmware("cx4.rom", 0x000000, 0x000c00);
|
||||
if(markup.find("sgb.boot.rom" )) copyFirmware("sgb.rom", 0x000000, 0x000000, 0x000100);
|
||||
}
|
||||
|
||||
string Ananke::openSuperFamicom(vector<uint8_t> &buffer) {
|
||||
string sha256 = nall::sha256(buffer.data(), buffer.size());
|
||||
|
||||
string databaseText = string::read({configpath(), "ananke/database/Super Famicom.bml"}).strip();
|
||||
if(databaseText.empty()) databaseText = string{Database::SuperFamicom}.strip();
|
||||
lstring databaseItem = databaseText.split("\n\n");
|
||||
|
||||
for(auto &item : databaseItem) {
|
||||
item.append("\n");
|
||||
auto document = Markup::Document(item);
|
||||
|
||||
if(document["release/information/sha256"].text() == sha256) {
|
||||
return createSuperFamicomDatabase(buffer, document, item);
|
||||
}
|
||||
}
|
||||
|
||||
return createSuperFamicomHeuristic(buffer);
|
||||
}
|
||||
|
||||
string Ananke::syncSuperFamicom(const string &pathname) {
|
||||
if(file::exists({pathname, "msu1.rom"})) return ""; //cannot update MSU1 games
|
||||
|
||||
vector<uint8_t> buffer;
|
||||
|
||||
auto append = [&](string filename) {
|
||||
filename = {pathname, filename};
|
||||
auto data = file::read(filename);
|
||||
if(data.size() == 0) return; //file does not exist
|
||||
|
||||
unsigned position = buffer.size();
|
||||
buffer.resize(buffer.size() + data.size());
|
||||
memcpy(buffer.data() + position, data.data(), data.size());
|
||||
};
|
||||
|
||||
append("program.rom");
|
||||
append("data.rom");
|
||||
|
||||
append("dsp1.rom");
|
||||
append("dsp1.program.rom");
|
||||
append("dsp1.data.rom");
|
||||
|
||||
append("dsp1b.rom");
|
||||
append("dsp1b.program.rom");
|
||||
append("dsp1b.data.rom");
|
||||
|
||||
append("dsp2.rom");
|
||||
append("dsp2.program.rom");
|
||||
append("dsp2.data.rom");
|
||||
|
||||
append("dsp3.rom");
|
||||
append("dsp3.program.rom");
|
||||
append("dsp3.data.rom");
|
||||
|
||||
append("dsp4.rom");
|
||||
append("dsp4.program.rom");
|
||||
append("dsp4.data.rom");
|
||||
|
||||
append("st010.rom");
|
||||
append("st010.program.rom");
|
||||
append("st010.data.rom");
|
||||
|
||||
append("st011.rom");
|
||||
append("st011.program.rom");
|
||||
append("st011.data.rom");
|
||||
|
||||
append("st018.rom");
|
||||
append("st018.program.rom");
|
||||
append("st018.data.rom");
|
||||
|
||||
append("cx4.rom");
|
||||
append("cx4.data.rom");
|
||||
|
||||
append("sgb.rom");
|
||||
append("sgb.boot.rom");
|
||||
|
||||
if(buffer.size() == 0) return "";
|
||||
|
||||
auto save = file::read({pathname, "save.ram"});
|
||||
if(save.size() == 0) save = file::read({pathname, "save.rwm"});
|
||||
|
||||
auto rtc = file::read({pathname, "rtc.ram"});
|
||||
if(rtc.size() == 0) rtc= file::read({pathname, "rtc.rwm"});
|
||||
|
||||
directory::remove(pathname);
|
||||
information.path = pathname;
|
||||
information.name = notdir(string{pathname}.rtrim<1>("/"));
|
||||
string outputPath = openSuperFamicom(buffer);
|
||||
|
||||
if(save.size()) file::write({outputPath, "save.ram"}, save);
|
||||
if(rtc.size()) file::write({outputPath, "rtc.ram"}, save);
|
||||
|
||||
return outputPath;
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>higan</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>higan</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>higan.icns</string>
|
||||
</dict>
|
||||
</plist>
|
84600
data/cheats.bml
84600
data/cheats.bml
File diff suppressed because it is too large
Load Diff
BIN
data/higan.png
BIN
data/higan.png
Binary file not shown.
Before Width: | Height: | Size: 2.2 KiB |
@@ -1,149 +0,0 @@
|
||||
#ifndef EMULATOR_HPP
|
||||
#define EMULATOR_HPP
|
||||
|
||||
namespace Emulator {
|
||||
static const char Name[] = "higan";
|
||||
static const char Version[] = "093";
|
||||
static const char Author[] = "byuu";
|
||||
static const char License[] = "GPLv3";
|
||||
static const char Website[] = "http://byuu.org/";
|
||||
|
||||
#if defined(PROFILE_ACCURACY)
|
||||
static const char Profile[] = "Accuracy";
|
||||
#elif defined(PROFILE_BALANCED)
|
||||
static const char Profile[] = "Balanced";
|
||||
#elif defined(PROFILE_PERFORMANCE)
|
||||
static const char Profile[] = "Performance";
|
||||
#endif
|
||||
}
|
||||
|
||||
#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>
|
||||
#include <nall/serializer.hpp>
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
#include <nall/varint.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
#include <nall/stream/memory.hpp>
|
||||
#include <nall/stream/vector.hpp>
|
||||
using namespace nall;
|
||||
|
||||
#include "interface.hpp"
|
||||
|
||||
//debugging function hook:
|
||||
//no overhead (and no debugger invocation) if not compiled with -DDEBUGGER
|
||||
//wraps testing of function to allow invocation without a defined callback
|
||||
template<typename T> struct hook;
|
||||
template<typename R, typename... P> struct hook<R (P...)> {
|
||||
function<R (P...)> callback;
|
||||
|
||||
R operator()(P... p) const {
|
||||
#if defined(DEBUGGER)
|
||||
if(callback) return callback(std::forward<P>(p)...);
|
||||
#endif
|
||||
return R();
|
||||
}
|
||||
|
||||
hook() {}
|
||||
hook(const hook& hook) { callback = hook.callback; }
|
||||
hook(void* function) { callback = function; }
|
||||
hook(R (*function)(P...)) { callback = function; }
|
||||
template<typename C> hook(R (C::*function)(P...), C* object) { callback = {function, object}; }
|
||||
template<typename C> hook(R (C::*function)(P...) const, C* object) { callback = {function, object}; }
|
||||
template<typename L> hook(const L& function) { callback = function; }
|
||||
|
||||
hook& operator=(const hook& hook) { callback = hook.callback; return *this; }
|
||||
};
|
||||
|
||||
#if defined(DEBUGGER)
|
||||
#define privileged public
|
||||
#else
|
||||
#define privileged private
|
||||
#endif
|
||||
|
||||
typedef int1_t int1;
|
||||
typedef int2_t int2;
|
||||
typedef int3_t int3;
|
||||
typedef int4_t int4;
|
||||
typedef int5_t int5;
|
||||
typedef int6_t int6;
|
||||
typedef int7_t int7;
|
||||
typedef int8_t int8;
|
||||
typedef int9_t int9;
|
||||
typedef int10_t int10;
|
||||
typedef int11_t int11;
|
||||
typedef int12_t int12;
|
||||
typedef int13_t int13;
|
||||
typedef int14_t int14;
|
||||
typedef int15_t int15;
|
||||
typedef int16_t int16;
|
||||
typedef int17_t int17;
|
||||
typedef int18_t int18;
|
||||
typedef int19_t int19;
|
||||
typedef int20_t int20;
|
||||
typedef int21_t int21;
|
||||
typedef int22_t int22;
|
||||
typedef int23_t int23;
|
||||
typedef int24_t int24;
|
||||
typedef int25_t int25;
|
||||
typedef int26_t int26;
|
||||
typedef int27_t int27;
|
||||
typedef int28_t int28;
|
||||
typedef int29_t int29;
|
||||
typedef int30_t int30;
|
||||
typedef int31_t int31;
|
||||
typedef int32_t int32;
|
||||
typedef int64_t int64;
|
||||
|
||||
typedef uint1_t uint1;
|
||||
typedef uint2_t uint2;
|
||||
typedef uint3_t uint3;
|
||||
typedef uint4_t uint4;
|
||||
typedef uint5_t uint5;
|
||||
typedef uint6_t uint6;
|
||||
typedef uint7_t uint7;
|
||||
typedef uint8_t uint8;
|
||||
typedef uint9_t uint9;
|
||||
typedef uint10_t uint10;
|
||||
typedef uint11_t uint11;
|
||||
typedef uint12_t uint12;
|
||||
typedef uint13_t uint13;
|
||||
typedef uint14_t uint14;
|
||||
typedef uint15_t uint15;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint17_t uint17;
|
||||
typedef uint18_t uint18;
|
||||
typedef uint19_t uint19;
|
||||
typedef uint20_t uint20;
|
||||
typedef uint21_t uint21;
|
||||
typedef uint22_t uint22;
|
||||
typedef uint23_t uint23;
|
||||
typedef uint24_t uint24;
|
||||
typedef uint25_t uint25;
|
||||
typedef uint26_t uint26;
|
||||
typedef uint27_t uint27;
|
||||
typedef uint28_t uint28;
|
||||
typedef uint29_t uint29;
|
||||
typedef uint30_t uint30;
|
||||
typedef uint31_t uint31;
|
||||
typedef uint32_t uint32;
|
||||
typedef uint_t<33> uint33;
|
||||
typedef uint64_t uint64;
|
||||
|
||||
typedef varuint_t<unsigned> varuint;
|
||||
|
||||
#endif
|
@@ -1,119 +0,0 @@
|
||||
#ifndef EMULATOR_INTERFACE_HPP
|
||||
#define EMULATOR_INTERFACE_HPP
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Interface {
|
||||
struct Information {
|
||||
string name;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
bool overscan;
|
||||
double aspectRatio;
|
||||
bool resettable;
|
||||
struct Capability {
|
||||
bool states;
|
||||
bool cheats;
|
||||
} capability;
|
||||
} information;
|
||||
|
||||
struct Media {
|
||||
unsigned id;
|
||||
string name;
|
||||
string type;
|
||||
bool bootable; //false for cartridge slots (eg Sufami Turbo cartridges)
|
||||
};
|
||||
vector<Media> media;
|
||||
|
||||
struct Device {
|
||||
unsigned id;
|
||||
unsigned portmask;
|
||||
string name;
|
||||
struct Input {
|
||||
unsigned id;
|
||||
unsigned type; //0 = digital, 1 = analog (relative), 2 = analog (absolute)
|
||||
string name;
|
||||
unsigned guid;
|
||||
};
|
||||
vector<Input> input;
|
||||
vector<unsigned> order;
|
||||
};
|
||||
|
||||
struct Port {
|
||||
unsigned id;
|
||||
string name;
|
||||
vector<Device> device;
|
||||
};
|
||||
vector<Port> port;
|
||||
|
||||
struct Bind {
|
||||
virtual void loadRequest(unsigned, string, string) {}
|
||||
virtual void loadRequest(unsigned, string) {}
|
||||
virtual void saveRequest(unsigned, string) {}
|
||||
virtual uint32_t videoColor(unsigned, uint16_t, uint16_t, uint16_t) { return 0u; }
|
||||
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 Markup::Node&) { return 0; }
|
||||
virtual string path(unsigned) { return ""; }
|
||||
virtual string server() { return ""; }
|
||||
virtual void notify(string text) { print(text, "\n"); }
|
||||
};
|
||||
Bind* bind = nullptr;
|
||||
|
||||
//callback bindings (provided by user interface)
|
||||
void loadRequest(unsigned id, string name, string type) { return bind->loadRequest(id, name, type); }
|
||||
void loadRequest(unsigned id, string path) { return bind->loadRequest(id, path); }
|
||||
void saveRequest(unsigned id, string path) { return bind->saveRequest(id, path); }
|
||||
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) { return bind->videoColor(source, red, green, blue); }
|
||||
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 Markup::Node& node) { return bind->dipSettings(node); }
|
||||
string path(unsigned group) { return bind->path(group); }
|
||||
string server() { return bind->server(); }
|
||||
template<typename... Args> void notify(Args&&... args) { return bind->notify({std::forward<Args>(args)...}); }
|
||||
|
||||
//information
|
||||
virtual string title() = 0;
|
||||
virtual double videoFrequency() = 0;
|
||||
virtual double audioFrequency() = 0;
|
||||
|
||||
//media interface
|
||||
virtual bool loaded() { return false; }
|
||||
virtual string sha256() { return ""; }
|
||||
virtual unsigned group(unsigned id) = 0;
|
||||
virtual void load(unsigned id) {}
|
||||
virtual void save() {}
|
||||
virtual void load(unsigned id, const stream& memory) {}
|
||||
virtual void save(unsigned id, const stream& memory) {}
|
||||
virtual void unload() {}
|
||||
|
||||
//system interface
|
||||
virtual void connect(unsigned port, unsigned device) {}
|
||||
virtual void power() {}
|
||||
virtual void reset() {}
|
||||
virtual void run() {}
|
||||
|
||||
//time functions
|
||||
virtual bool rtc() { return false; }
|
||||
virtual void rtcsync() {}
|
||||
|
||||
//state functions
|
||||
virtual serializer serialize() = 0;
|
||||
virtual bool unserialize(serializer&) = 0;
|
||||
|
||||
//cheat functions
|
||||
virtual void cheatSet(const lstring& = lstring{}) {}
|
||||
|
||||
//utility functions
|
||||
virtual void paletteUpdate() {}
|
||||
|
||||
//debugger functions
|
||||
virtual bool tracerEnable(bool) { return false; }
|
||||
virtual void exportMemory() {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
16
fc/Makefile
16
fc/Makefile
@@ -1,16 +0,0 @@
|
||||
fc_objects := fc-interface fc-system fc-scheduler fc-input
|
||||
fc_objects += fc-memory fc-cartridge fc-cpu fc-apu fc-ppu
|
||||
fc_objects += fc-cheat fc-video
|
||||
objects += $(fc_objects)
|
||||
|
||||
obj/fc-interface.o: $(fc)/interface/interface.cpp $(call rwildcard,$(fc)/interface/)
|
||||
obj/fc-system.o: $(fc)/system/system.cpp $(call rwildcard,$(fc)/system/)
|
||||
obj/fc-scheduler.o: $(fc)/scheduler/scheduler.cpp $(call rwildcard,$(fc)/scheduler/)
|
||||
obj/fc-input.o: $(fc)/input/input.cpp $(call rwildcard,$(fc)/input/)
|
||||
obj/fc-memory.o: $(fc)/memory/memory.cpp $(call rwildcard,$(fc)/memory/)
|
||||
obj/fc-cartridge.o: $(fc)/cartridge/cartridge.cpp $(call rwildcard,$(fc)/cartridge/)
|
||||
obj/fc-cpu.o: $(fc)/cpu/cpu.cpp $(call rwildcard,$(fc)/cpu/)
|
||||
obj/fc-apu.o: $(fc)/apu/apu.cpp $(call rwildcard,$(fc)/apu/)
|
||||
obj/fc-ppu.o: $(fc)/ppu/ppu.cpp $(call rwildcard,$(fc)/ppu/)
|
||||
obj/fc-cheat.o: $(fc)/cheat/cheat.cpp $(call rwildcard,$(fc)/cheat/)
|
||||
obj/fc-video.o: $(fc)/video/video.cpp $(call rwildcard,$(fc)/video/)
|
329
fc/apu/apu.cpp
329
fc/apu/apu.cpp
@@ -1,329 +0,0 @@
|
||||
#include <fc/fc.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
|
||||
#include "envelope.cpp"
|
||||
#include "sweep.cpp"
|
||||
#include "pulse.cpp"
|
||||
#include "triangle.cpp"
|
||||
#include "noise.cpp"
|
||||
#include "dmc.cpp"
|
||||
#include "serialization.cpp"
|
||||
APU apu;
|
||||
|
||||
const uint8 APU::length_counter_table[32] = {
|
||||
0x0a, 0xfe, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, 0xa0, 0x08, 0x3c, 0x0a, 0x0e, 0x0c, 0x1a, 0x0e,
|
||||
0x0c, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, 0xc0, 0x18, 0x48, 0x1a, 0x10, 0x1c, 0x20, 0x1e,
|
||||
};
|
||||
|
||||
const uint16 APU::ntsc_noise_period_table[16] = {
|
||||
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068,
|
||||
};
|
||||
|
||||
const uint16 APU::pal_noise_period_table[16] = {
|
||||
4, 7, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778,
|
||||
};
|
||||
|
||||
const uint16 APU::ntsc_dmc_period_table[16] = {
|
||||
428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54,
|
||||
};
|
||||
|
||||
const uint16 APU::pal_dmc_period_table[16] = {
|
||||
398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50,
|
||||
};
|
||||
|
||||
void APU::Main() {
|
||||
apu.main();
|
||||
}
|
||||
|
||||
void APU::main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
unsigned pulse_output, triangle_output, noise_output, dmc_output;
|
||||
|
||||
pulse_output = pulse[0].clock();
|
||||
pulse_output += pulse[1].clock();
|
||||
triangle_output = triangle.clock();
|
||||
noise_output = noise.clock();
|
||||
dmc_output = dmc.clock();
|
||||
|
||||
clock_frame_counter_divider();
|
||||
|
||||
signed output = pulse_dac[pulse_output] + dmc_triangle_noise_dac[dmc_output][triangle_output][noise_output];
|
||||
|
||||
output = filter.run_hipass_strong(output);
|
||||
output += cartridge_sample;
|
||||
output = filter.run_hipass_weak(output);
|
||||
//output = filter.run_lopass(output);
|
||||
output = sclamp<16>(output);
|
||||
|
||||
interface->audioSample(output, output);
|
||||
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
void APU::tick() {
|
||||
clock += 12;
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
}
|
||||
|
||||
void APU::set_irq_line() {
|
||||
cpu.set_irq_apu_line(frame.irq_pending || dmc.irq_pending);
|
||||
}
|
||||
|
||||
void APU::set_sample(int16 sample) {
|
||||
cartridge_sample = sample;
|
||||
}
|
||||
|
||||
void APU::power() {
|
||||
filter.hipass_strong = 0;
|
||||
filter.hipass_weak = 0;
|
||||
filter.lopass = 0;
|
||||
|
||||
pulse[0].power();
|
||||
pulse[1].power();
|
||||
triangle.power();
|
||||
noise.power();
|
||||
dmc.power();
|
||||
}
|
||||
|
||||
void APU::reset() {
|
||||
create(APU::Main, 21477272);
|
||||
|
||||
pulse[0].reset();
|
||||
pulse[1].reset();
|
||||
triangle.reset();
|
||||
noise.reset();
|
||||
dmc.reset();
|
||||
|
||||
frame.irq_pending = 0;
|
||||
|
||||
frame.mode = 0;
|
||||
frame.counter = 0;
|
||||
frame.divider = 1;
|
||||
|
||||
enabled_channels = 0;
|
||||
cartridge_sample = 0;
|
||||
|
||||
set_irq_line();
|
||||
}
|
||||
|
||||
uint8 APU::read(uint16 addr) {
|
||||
if(addr == 0x4015) {
|
||||
uint8 result = 0x00;
|
||||
result |= pulse[0].length_counter ? 0x01 : 0;
|
||||
result |= pulse[1].length_counter ? 0x02 : 0;
|
||||
result |= triangle.length_counter ? 0x04 : 0;
|
||||
result |= noise.length_counter ? 0x08 : 0;
|
||||
result |= dmc.length_counter ? 0x10 : 0;
|
||||
result |= frame.irq_pending ? 0x40 : 0;
|
||||
result |= dmc.irq_pending ? 0x80 : 0;
|
||||
|
||||
frame.irq_pending = false;
|
||||
set_irq_line();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void APU::write(uint16 addr, uint8 data) {
|
||||
const unsigned n = (addr >> 2) & 1; //pulse#
|
||||
|
||||
switch(addr) {
|
||||
case 0x4000: case 0x4004:
|
||||
pulse[n].duty = data >> 6;
|
||||
pulse[n].envelope.loop_mode = data & 0x20;
|
||||
pulse[n].envelope.use_speed_as_volume = data & 0x10;
|
||||
pulse[n].envelope.speed = data & 0x0f;
|
||||
break;
|
||||
|
||||
case 0x4001: case 0x4005:
|
||||
pulse[n].sweep.enable = data & 0x80;
|
||||
pulse[n].sweep.period = (data & 0x70) >> 4;
|
||||
pulse[n].sweep.decrement = data & 0x08;
|
||||
pulse[n].sweep.shift = data & 0x07;
|
||||
pulse[n].sweep.reload = true;
|
||||
break;
|
||||
|
||||
case 0x4002: case 0x4006:
|
||||
pulse[n].period = (pulse[n].period & 0x0700) | (data << 0);
|
||||
pulse[n].sweep.pulse_period = (pulse[n].sweep.pulse_period & 0x0700) | (data << 0);
|
||||
break;
|
||||
|
||||
case 0x4003: case 0x4007:
|
||||
pulse[n].period = (pulse[n].period & 0x00ff) | (data << 8);
|
||||
pulse[n].sweep.pulse_period = (pulse[n].sweep.pulse_period & 0x00ff) | (data << 8);
|
||||
|
||||
pulse[n].duty_counter = 7;
|
||||
pulse[n].envelope.reload_decay = true;
|
||||
|
||||
if(enabled_channels & (1 << n)) {
|
||||
pulse[n].length_counter = length_counter_table[(data >> 3) & 0x1f];
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x4008:
|
||||
triangle.halt_length_counter = data & 0x80;
|
||||
triangle.linear_length = data & 0x7f;
|
||||
break;
|
||||
|
||||
case 0x400a:
|
||||
triangle.period = (triangle.period & 0x0700) | (data << 0);
|
||||
break;
|
||||
|
||||
case 0x400b:
|
||||
triangle.period = (triangle.period & 0x00ff) | (data << 8);
|
||||
|
||||
triangle.reload_linear = true;
|
||||
|
||||
if(enabled_channels & (1 << 2)) {
|
||||
triangle.length_counter = length_counter_table[(data >> 3) & 0x1f];
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x400c:
|
||||
noise.envelope.loop_mode = data & 0x20;
|
||||
noise.envelope.use_speed_as_volume = data & 0x10;
|
||||
noise.envelope.speed = data & 0x0f;
|
||||
break;
|
||||
|
||||
case 0x400e:
|
||||
noise.short_mode = data & 0x80;
|
||||
noise.period = data & 0x0f;
|
||||
break;
|
||||
|
||||
case 0x400f:
|
||||
noise.envelope.reload_decay = true;
|
||||
|
||||
if(enabled_channels & (1 << 3)) {
|
||||
noise.length_counter = length_counter_table[(data >> 3) & 0x1f];
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x4010:
|
||||
dmc.irq_enable = data & 0x80;
|
||||
dmc.loop_mode = data & 0x40;
|
||||
dmc.period = data & 0x0f;
|
||||
|
||||
dmc.irq_pending = dmc.irq_pending && dmc.irq_enable && !dmc.loop_mode;
|
||||
set_irq_line();
|
||||
break;
|
||||
|
||||
case 0x4011:
|
||||
dmc.dac_latch = data & 0x7f;
|
||||
break;
|
||||
|
||||
case 0x4012:
|
||||
dmc.addr_latch = data;
|
||||
break;
|
||||
|
||||
case 0x4013:
|
||||
dmc.length_latch = data;
|
||||
break;
|
||||
|
||||
case 0x4015:
|
||||
if((data & 0x01) == 0) pulse[0].length_counter = 0;
|
||||
if((data & 0x02) == 0) pulse[1].length_counter = 0;
|
||||
if((data & 0x04) == 0) triangle.length_counter = 0;
|
||||
if((data & 0x08) == 0) noise.length_counter = 0;
|
||||
|
||||
(data & 0x10) ? dmc.start() : dmc.stop();
|
||||
dmc.irq_pending = false;
|
||||
|
||||
set_irq_line();
|
||||
enabled_channels = data & 0x1f;
|
||||
break;
|
||||
|
||||
case 0x4017:
|
||||
frame.mode = data >> 6;
|
||||
|
||||
frame.counter = 0;
|
||||
if(frame.mode & 2) clock_frame_counter();
|
||||
if(frame.mode & 1) {
|
||||
frame.irq_pending = false;
|
||||
set_irq_line();
|
||||
}
|
||||
frame.divider = FrameCounter::NtscPeriod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
signed APU::Filter::run_hipass_strong(signed sample) {
|
||||
hipass_strong += ((((int64)sample << 16) - (hipass_strong >> 16)) * HiPassStrong) >> 16;
|
||||
return sample - (hipass_strong >> 32);
|
||||
}
|
||||
|
||||
signed APU::Filter::run_hipass_weak(signed sample) {
|
||||
hipass_weak += ((((int64)sample << 16) - (hipass_weak >> 16)) * HiPassWeak) >> 16;
|
||||
return sample - (hipass_weak >> 32);
|
||||
}
|
||||
|
||||
signed APU::Filter::run_lopass(signed sample) {
|
||||
lopass += ((((int64)sample << 16) - (lopass >> 16)) * LoPass) >> 16;
|
||||
return (lopass >> 32);
|
||||
}
|
||||
|
||||
void APU::clock_frame_counter() {
|
||||
frame.counter++;
|
||||
|
||||
if(frame.counter & 1) {
|
||||
pulse[0].clock_length();
|
||||
pulse[0].sweep.clock(0);
|
||||
pulse[1].clock_length();
|
||||
pulse[1].sweep.clock(1);
|
||||
triangle.clock_length();
|
||||
noise.clock_length();
|
||||
}
|
||||
|
||||
pulse[0].envelope.clock();
|
||||
pulse[1].envelope.clock();
|
||||
triangle.clock_linear_length();
|
||||
noise.envelope.clock();
|
||||
|
||||
if(frame.counter == 0) {
|
||||
if(frame.mode & 2) frame.divider += FrameCounter::NtscPeriod;
|
||||
if(frame.mode == 0) {
|
||||
frame.irq_pending = true;
|
||||
set_irq_line();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APU::clock_frame_counter_divider() {
|
||||
frame.divider -= 2;
|
||||
if(frame.divider <= 0) {
|
||||
clock_frame_counter();
|
||||
frame.divider += FrameCounter::NtscPeriod;
|
||||
}
|
||||
}
|
||||
|
||||
APU::APU() {
|
||||
for(unsigned amp = 0; amp < 32; amp++) {
|
||||
if(amp == 0) {
|
||||
pulse_dac[amp] = 0;
|
||||
} else {
|
||||
pulse_dac[amp] = 16384.0 * 95.88 / (8128.0 / amp + 100.0);
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned dmc_amp = 0; dmc_amp < 128; dmc_amp++) {
|
||||
for(unsigned triangle_amp = 0; triangle_amp < 16; triangle_amp++) {
|
||||
for(unsigned noise_amp = 0; noise_amp < 16; noise_amp++) {
|
||||
if(dmc_amp == 0 && triangle_amp == 0 && noise_amp == 0) {
|
||||
dmc_triangle_noise_dac[dmc_amp][triangle_amp][noise_amp] = 0;
|
||||
} else {
|
||||
dmc_triangle_noise_dac[dmc_amp][triangle_amp][noise_amp]
|
||||
= 16384.0 * 159.79 / (100.0 + 1.0 / (triangle_amp / 8227.0 + noise_amp / 12241.0 + dmc_amp / 22638.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,65 +0,0 @@
|
||||
struct APU : Thread {
|
||||
static void Main();
|
||||
void main();
|
||||
void tick();
|
||||
void set_irq_line();
|
||||
void set_sample(int16 sample);
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
uint8 read(uint16 addr);
|
||||
void write(uint16 addr, uint8 data);
|
||||
|
||||
void serialize(serializer&);
|
||||
APU();
|
||||
|
||||
struct Filter {
|
||||
enum : signed { HiPassStrong = 225574, HiPassWeak = 57593, LoPass = 86322413 };
|
||||
|
||||
int64 hipass_strong;
|
||||
int64 hipass_weak;
|
||||
int64 lopass;
|
||||
|
||||
signed run_hipass_strong(signed sample);
|
||||
signed run_hipass_weak(signed sample);
|
||||
signed run_lopass(signed sample);
|
||||
void serialize(serializer&);
|
||||
} filter;
|
||||
|
||||
#include "envelope.hpp"
|
||||
#include "sweep.hpp"
|
||||
#include "pulse.hpp"
|
||||
#include "triangle.hpp"
|
||||
#include "noise.hpp"
|
||||
#include "dmc.hpp"
|
||||
|
||||
struct FrameCounter {
|
||||
enum : unsigned { NtscPeriod = 14915 }; //~(21.477MHz / 6 / 240hz)
|
||||
|
||||
bool irq_pending;
|
||||
|
||||
uint2 mode;
|
||||
uint2 counter;
|
||||
signed divider;
|
||||
|
||||
void serialize(serializer&);
|
||||
} frame;
|
||||
|
||||
void clock_frame_counter();
|
||||
void clock_frame_counter_divider();
|
||||
|
||||
uint8 enabled_channels;
|
||||
int16 cartridge_sample;
|
||||
|
||||
int16 pulse_dac[32];
|
||||
int16 dmc_triangle_noise_dac[128][16][16];
|
||||
|
||||
static const uint8 length_counter_table[32];
|
||||
static const uint16 ntsc_dmc_period_table[16];
|
||||
static const uint16 pal_dmc_period_table[16];
|
||||
static const uint16 ntsc_noise_period_table[16];
|
||||
static const uint16 pal_noise_period_table[16];
|
||||
};
|
||||
|
||||
extern APU apu;
|
117
fc/apu/dmc.cpp
117
fc/apu/dmc.cpp
@@ -1,117 +0,0 @@
|
||||
void APU::DMC::start() {
|
||||
if(length_counter == 0) {
|
||||
read_addr = 0x4000 + (addr_latch << 6);
|
||||
length_counter = (length_latch << 4) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::DMC::stop() {
|
||||
length_counter = 0;
|
||||
dma_delay_counter = 0;
|
||||
cpu.set_rdy_line(1);
|
||||
cpu.set_rdy_addr(false);
|
||||
}
|
||||
|
||||
uint8 APU::DMC::clock() {
|
||||
uint8 result = dac_latch;
|
||||
|
||||
if(dma_delay_counter > 0) {
|
||||
dma_delay_counter--;
|
||||
|
||||
if(dma_delay_counter == 1) {
|
||||
cpu.set_rdy_addr(true, 0x8000 | read_addr);
|
||||
} else if(dma_delay_counter == 0) {
|
||||
cpu.set_rdy_line(1);
|
||||
cpu.set_rdy_addr(false);
|
||||
|
||||
dma_buffer = cpu.mdr();
|
||||
have_dma_buffer = true;
|
||||
length_counter--;
|
||||
read_addr++;
|
||||
|
||||
if(length_counter == 0) {
|
||||
if(loop_mode) {
|
||||
start();
|
||||
} else if(irq_enable) {
|
||||
irq_pending = true;
|
||||
apu.set_irq_line();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(--period_counter == 0) {
|
||||
if(have_sample) {
|
||||
signed delta = (((sample >> bit_counter) & 1) << 2) - 2;
|
||||
unsigned data = dac_latch + delta;
|
||||
if((data & 0x80) == 0) dac_latch = data;
|
||||
}
|
||||
|
||||
if(++bit_counter == 0) {
|
||||
if(have_dma_buffer) {
|
||||
have_sample = true;
|
||||
sample = dma_buffer;
|
||||
have_dma_buffer = false;
|
||||
} else {
|
||||
have_sample = false;
|
||||
}
|
||||
}
|
||||
|
||||
period_counter = ntsc_dmc_period_table[period];
|
||||
}
|
||||
|
||||
if(length_counter > 0 && have_dma_buffer == false && dma_delay_counter == 0) {
|
||||
cpu.set_rdy_line(0);
|
||||
dma_delay_counter = 4;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void APU::DMC::power() {
|
||||
}
|
||||
|
||||
void APU::DMC::reset() {
|
||||
length_counter = 0;
|
||||
irq_pending = 0;
|
||||
|
||||
period = 0;
|
||||
period_counter = ntsc_dmc_period_table[0];
|
||||
irq_enable = 0;
|
||||
loop_mode = 0;
|
||||
dac_latch = 0;
|
||||
addr_latch = 0;
|
||||
length_latch = 0;
|
||||
read_addr = 0;
|
||||
dma_delay_counter = 0;
|
||||
bit_counter = 0;
|
||||
have_dma_buffer = 0;
|
||||
dma_buffer = 0;
|
||||
have_sample = 0;
|
||||
sample = 0;
|
||||
}
|
||||
|
||||
void APU::DMC::serialize(serializer& s) {
|
||||
s.integer(length_counter);
|
||||
s.integer(irq_pending);
|
||||
|
||||
s.integer(period);
|
||||
s.integer(period_counter);
|
||||
|
||||
s.integer(irq_enable);
|
||||
s.integer(loop_mode);
|
||||
|
||||
s.integer(dac_latch);
|
||||
s.integer(addr_latch);
|
||||
s.integer(length_latch);
|
||||
|
||||
s.integer(read_addr);
|
||||
s.integer(dma_delay_counter);
|
||||
|
||||
s.integer(bit_counter);
|
||||
s.integer(have_dma_buffer);
|
||||
s.integer(dma_buffer);
|
||||
|
||||
s.integer(have_sample);
|
||||
s.integer(sample);
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
struct DMC {
|
||||
unsigned length_counter;
|
||||
bool irq_pending;
|
||||
|
||||
uint4 period;
|
||||
unsigned period_counter;
|
||||
|
||||
bool irq_enable;
|
||||
bool loop_mode;
|
||||
|
||||
uint8 dac_latch;
|
||||
uint8 addr_latch;
|
||||
uint8 length_latch;
|
||||
|
||||
uint15 read_addr;
|
||||
unsigned dma_delay_counter;
|
||||
|
||||
uint3 bit_counter;
|
||||
bool have_dma_buffer;
|
||||
uint8 dma_buffer;
|
||||
|
||||
bool have_sample;
|
||||
uint8 sample;
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
uint8 clock();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
void serialize(serializer&);
|
||||
} dmc;
|
@@ -1,39 +0,0 @@
|
||||
unsigned APU::Envelope::volume() const {
|
||||
return use_speed_as_volume ? speed : decay_volume;
|
||||
}
|
||||
|
||||
void APU::Envelope::clock() {
|
||||
if(reload_decay) {
|
||||
reload_decay = false;
|
||||
decay_volume = 0x0f;
|
||||
decay_counter = speed + 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if(--decay_counter == 0) {
|
||||
decay_counter = speed + 1;
|
||||
if(decay_volume || loop_mode) decay_volume--;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Envelope::power() {
|
||||
}
|
||||
|
||||
void APU::Envelope::reset() {
|
||||
speed = 0;
|
||||
use_speed_as_volume = 0;
|
||||
loop_mode = 0;
|
||||
reload_decay = 0;
|
||||
decay_counter = 0;
|
||||
decay_volume = 0;
|
||||
}
|
||||
|
||||
void APU::Envelope::serialize(serializer& s) {
|
||||
s.integer(speed);
|
||||
s.integer(use_speed_as_volume);
|
||||
s.integer(loop_mode);
|
||||
|
||||
s.integer(reload_decay);
|
||||
s.integer(decay_counter);
|
||||
s.integer(decay_volume);
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
struct Envelope {
|
||||
uint4 speed;
|
||||
bool use_speed_as_volume;
|
||||
bool loop_mode;
|
||||
|
||||
bool reload_decay;
|
||||
uint8 decay_counter;
|
||||
uint4 decay_volume;
|
||||
|
||||
unsigned volume() const;
|
||||
void clock();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
void serialize(serializer&);
|
||||
};
|
@@ -1,57 +0,0 @@
|
||||
void APU::Noise::clock_length() {
|
||||
if(envelope.loop_mode == 0) {
|
||||
if(length_counter > 0) length_counter--;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 APU::Noise::clock() {
|
||||
if(length_counter == 0) return 0;
|
||||
|
||||
uint8 result = (lfsr & 1) ? envelope.volume() : 0;
|
||||
|
||||
if(--period_counter == 0) {
|
||||
unsigned feedback;
|
||||
|
||||
if(short_mode) {
|
||||
feedback = ((lfsr >> 0) & 1) ^ ((lfsr >> 6) & 1);
|
||||
} else {
|
||||
feedback = ((lfsr >> 0) & 1) ^ ((lfsr >> 1) & 1);
|
||||
}
|
||||
|
||||
lfsr = (lfsr >> 1) | (feedback << 14);
|
||||
period_counter = apu.ntsc_noise_period_table[period];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void APU::Noise::power() {
|
||||
}
|
||||
|
||||
void APU::Noise::reset() {
|
||||
length_counter = 0;
|
||||
|
||||
envelope.speed = 0;
|
||||
envelope.use_speed_as_volume = 0;
|
||||
envelope.loop_mode = 0;
|
||||
envelope.reload_decay = 0;
|
||||
envelope.decay_counter = 0;
|
||||
envelope.decay_volume = 0;
|
||||
|
||||
period = 0;
|
||||
period_counter = 1;
|
||||
short_mode = 0;
|
||||
lfsr = 1;
|
||||
}
|
||||
|
||||
void APU::Noise::serialize(serializer& s) {
|
||||
s.integer(length_counter);
|
||||
|
||||
envelope.serialize(s);
|
||||
|
||||
s.integer(period);
|
||||
s.integer(period_counter);
|
||||
|
||||
s.integer(short_mode);
|
||||
s.integer(lfsr);
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
struct Noise {
|
||||
unsigned length_counter;
|
||||
|
||||
Envelope envelope;
|
||||
|
||||
uint4 period;
|
||||
unsigned period_counter;
|
||||
|
||||
bool short_mode;
|
||||
uint15 lfsr;
|
||||
|
||||
void clock_length();
|
||||
uint8 clock();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
void serialize(serializer&);
|
||||
} noise;
|
@@ -1,51 +0,0 @@
|
||||
void APU::Pulse::clock_length() {
|
||||
if(envelope.loop_mode == 0) {
|
||||
if(length_counter) length_counter--;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 APU::Pulse::clock() {
|
||||
if(sweep.check_period() == false) return 0;
|
||||
if(length_counter == 0) return 0;
|
||||
|
||||
static const unsigned duty_table[] = {1, 2, 4, 6};
|
||||
uint8 result = (duty_counter < duty_table[duty]) ? envelope.volume() : 0;
|
||||
if(sweep.pulse_period < 0x008) result = 0;
|
||||
|
||||
if(--period_counter == 0) {
|
||||
period_counter = (sweep.pulse_period + 1) * 2;
|
||||
duty_counter++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void APU::Pulse::power() {
|
||||
envelope.power();
|
||||
sweep.power();
|
||||
}
|
||||
|
||||
void APU::Pulse::reset() {
|
||||
envelope.reset();
|
||||
sweep.reset();
|
||||
|
||||
length_counter = 0;
|
||||
|
||||
duty = 0;
|
||||
duty_counter = 0;
|
||||
period = 0;
|
||||
period_counter = 1;
|
||||
}
|
||||
|
||||
void APU::Pulse::serialize(serializer& s) {
|
||||
s.integer(length_counter);
|
||||
|
||||
envelope.serialize(s);
|
||||
sweep.serialize(s);
|
||||
|
||||
s.integer(duty);
|
||||
s.integer(duty_counter);
|
||||
|
||||
s.integer(period);
|
||||
s.integer(period_counter);
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
struct Pulse {
|
||||
unsigned length_counter;
|
||||
|
||||
Envelope envelope;
|
||||
Sweep sweep;
|
||||
|
||||
uint2 duty;
|
||||
uint3 duty_counter;
|
||||
|
||||
uint11 period;
|
||||
unsigned period_counter;
|
||||
|
||||
void clock_length();
|
||||
bool check_period();
|
||||
uint8 clock();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
void serialize(serializer&);
|
||||
} pulse[2];
|
@@ -1,28 +0,0 @@
|
||||
void APU::serialize(serializer& s) {
|
||||
Thread::serialize(s);
|
||||
|
||||
filter.serialize(s);
|
||||
|
||||
pulse[0].serialize(s);
|
||||
pulse[1].serialize(s);
|
||||
triangle.serialize(s);
|
||||
dmc.serialize(s);
|
||||
frame.serialize(s);
|
||||
|
||||
s.integer(enabled_channels);
|
||||
s.integer(cartridge_sample);
|
||||
}
|
||||
|
||||
void APU::Filter::serialize(serializer& s) {
|
||||
s.integer(hipass_strong);
|
||||
s.integer(hipass_weak);
|
||||
s.integer(lopass);
|
||||
}
|
||||
|
||||
void APU::FrameCounter::serialize(serializer& s) {
|
||||
s.integer(irq_pending);
|
||||
|
||||
s.integer(mode);
|
||||
s.integer(counter);
|
||||
s.integer(divider);
|
||||
}
|
@@ -1,53 +0,0 @@
|
||||
bool APU::Sweep::check_period() {
|
||||
if(pulse_period > 0x7ff) return false;
|
||||
|
||||
if(decrement == 0) {
|
||||
if((pulse_period + (pulse_period >> shift)) & 0x800) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void APU::Sweep::clock(unsigned channel) {
|
||||
if(--counter == 0) {
|
||||
counter = period + 1;
|
||||
if(enable && shift && pulse_period > 8) {
|
||||
signed delta = pulse_period >> shift;
|
||||
|
||||
if(decrement) {
|
||||
pulse_period -= delta;
|
||||
if(channel == 0) pulse_period--;
|
||||
} else if((pulse_period + delta) < 0x800) {
|
||||
pulse_period += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(reload) {
|
||||
reload = false;
|
||||
counter = period + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Sweep::power() {
|
||||
shift = 0;
|
||||
decrement = 0;
|
||||
period = 0;
|
||||
counter = 1;
|
||||
enable = 0;
|
||||
reload = 0;
|
||||
pulse_period = 0;
|
||||
}
|
||||
|
||||
void APU::Sweep::reset() {
|
||||
}
|
||||
|
||||
void APU::Sweep::serialize(serializer& s) {
|
||||
s.integer(shift);
|
||||
s.integer(decrement);
|
||||
s.integer(period);
|
||||
s.integer(counter);
|
||||
s.integer(enable);
|
||||
s.integer(reload);
|
||||
s.integer(pulse_period);
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
struct Sweep {
|
||||
uint8 shift;
|
||||
bool decrement;
|
||||
uint3 period;
|
||||
uint8 counter;
|
||||
bool enable;
|
||||
bool reload;
|
||||
uint11 pulse_period;
|
||||
|
||||
bool check_period();
|
||||
void clock(unsigned channel);
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
void serialize(serializer&);
|
||||
};
|
@@ -1,58 +0,0 @@
|
||||
void APU::Triangle::clock_length() {
|
||||
if(halt_length_counter == 0) {
|
||||
if(length_counter > 0) length_counter--;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Triangle::clock_linear_length() {
|
||||
if(reload_linear) {
|
||||
linear_length_counter = linear_length;
|
||||
} else if(linear_length_counter) {
|
||||
linear_length_counter--;
|
||||
}
|
||||
|
||||
if(halt_length_counter == 0) reload_linear = false;
|
||||
}
|
||||
|
||||
uint8 APU::Triangle::clock() {
|
||||
uint8 result = step_counter & 0x0f;
|
||||
if((step_counter & 0x10) == 0) result ^= 0x0f;
|
||||
if(length_counter == 0 || linear_length_counter == 0) return result;
|
||||
|
||||
if(--period_counter == 0) {
|
||||
step_counter++;
|
||||
period_counter = period + 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void APU::Triangle::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void APU::Triangle::reset() {
|
||||
length_counter = 0;
|
||||
|
||||
linear_length = 0;
|
||||
halt_length_counter = 0;
|
||||
period = 0;
|
||||
period_counter = 1;
|
||||
step_counter = 0;
|
||||
linear_length_counter = 0;
|
||||
reload_linear = 0;
|
||||
}
|
||||
|
||||
void APU::Triangle::serialize(serializer& s) {
|
||||
s.integer(length_counter);
|
||||
|
||||
s.integer(linear_length);
|
||||
s.integer(halt_length_counter);
|
||||
|
||||
s.integer(period);
|
||||
s.integer(period_counter);
|
||||
|
||||
s.integer(step_counter);
|
||||
s.integer(linear_length_counter);
|
||||
s.integer(reload_linear);
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
struct Triangle {
|
||||
unsigned length_counter;
|
||||
|
||||
uint8 linear_length;
|
||||
bool halt_length_counter;
|
||||
|
||||
uint11 period;
|
||||
unsigned period_counter;
|
||||
|
||||
uint5 step_counter;
|
||||
uint8 linear_length_counter;
|
||||
bool reload_linear;
|
||||
|
||||
void clock_length();
|
||||
void clock_linear_length();
|
||||
uint8 clock();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
void serialize(serializer&);
|
||||
} triangle;
|
@@ -1,117 +0,0 @@
|
||||
//BANDAI-FCG
|
||||
|
||||
struct BandaiFCG : Board {
|
||||
|
||||
uint8 chr_bank[8];
|
||||
uint8 prg_bank;
|
||||
uint2 mirror;
|
||||
bool irq_counter_enable;
|
||||
uint16 irq_counter;
|
||||
uint16 irq_latch;
|
||||
|
||||
void main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_counter_enable) {
|
||||
if(--irq_counter == 0xffff) {
|
||||
cpu.set_irq_line(1);
|
||||
irq_counter_enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const {
|
||||
switch(mirror) {
|
||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
case 2: return 0x0000 | (addr & 0x03ff);
|
||||
case 3: return 0x0400 | (addr & 0x03ff);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if(addr & 0x8000) {
|
||||
bool region = addr & 0x4000;
|
||||
unsigned bank = (region == 0 ? prg_bank : 0x0f);
|
||||
return prgrom.read((bank << 14) | (addr & 0x3fff));
|
||||
}
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if(addr >= 0x6000) {
|
||||
switch(addr & 15) {
|
||||
case 0x00: case 0x01: case 0x02: case 0x03:
|
||||
case 0x04: case 0x05: case 0x06: case 0x07:
|
||||
chr_bank[addr & 7] = data;
|
||||
break;
|
||||
case 0x08:
|
||||
prg_bank = data & 0x0f;
|
||||
break;
|
||||
case 0x09:
|
||||
mirror = data & 0x03;
|
||||
break;
|
||||
case 0x0a:
|
||||
cpu.set_irq_line(0);
|
||||
irq_counter_enable = data & 0x01;
|
||||
irq_counter = irq_latch;
|
||||
break;
|
||||
case 0x0b:
|
||||
irq_latch = (irq_latch & 0xff00) | (data << 0);
|
||||
break;
|
||||
case 0x0c:
|
||||
irq_latch = (irq_latch & 0x00ff) | (data << 8);
|
||||
break;
|
||||
case 0x0d:
|
||||
//TODO: serial EEPROM support
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
||||
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
|
||||
return Board::chr_read(addr);
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
||||
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
|
||||
return Board::chr_write(addr, data);
|
||||
}
|
||||
|
||||
void power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
for(auto &n : chr_bank) n = 0;
|
||||
prg_bank = 0;
|
||||
mirror = 0;
|
||||
irq_counter_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
|
||||
s.array(chr_bank);
|
||||
s.integer(prg_bank);
|
||||
s.integer(mirror);
|
||||
s.integer(irq_counter_enable);
|
||||
s.integer(irq_counter);
|
||||
s.integer(irq_latch);
|
||||
}
|
||||
|
||||
BandaiFCG(Markup::Node& document) : Board(document) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,212 +0,0 @@
|
||||
#include "bandai-fcg.cpp"
|
||||
#include "konami-vrc1.cpp"
|
||||
#include "konami-vrc2.cpp"
|
||||
#include "konami-vrc3.cpp"
|
||||
#include "konami-vrc4.cpp"
|
||||
#include "konami-vrc6.cpp"
|
||||
#include "konami-vrc7.cpp"
|
||||
#include "nes-axrom.cpp"
|
||||
#include "nes-bnrom.cpp"
|
||||
#include "nes-cnrom.cpp"
|
||||
#include "nes-exrom.cpp"
|
||||
#include "nes-fxrom.cpp"
|
||||
#include "nes-gxrom.cpp"
|
||||
#include "nes-hkrom.cpp"
|
||||
#include "nes-nrom.cpp"
|
||||
#include "nes-pxrom.cpp"
|
||||
#include "nes-sxrom.cpp"
|
||||
#include "nes-txrom.cpp"
|
||||
#include "nes-uxrom.cpp"
|
||||
#include "sunsoft-5b.cpp"
|
||||
|
||||
uint8 Board::Memory::read(unsigned addr) const {
|
||||
return data[mirror(addr, size)];
|
||||
}
|
||||
|
||||
void Board::Memory::write(unsigned addr, uint8 byte) {
|
||||
if(writable) data[mirror(addr, size)] = byte;
|
||||
}
|
||||
|
||||
unsigned Board::mirror(unsigned addr, unsigned size) {
|
||||
unsigned base = 0;
|
||||
if(size) {
|
||||
unsigned mask = 1 << 23;
|
||||
while(addr >= size) {
|
||||
while(!(addr & mask)) mask >>= 1;
|
||||
addr -= mask;
|
||||
if(size > mask) {
|
||||
size -= mask;
|
||||
base += mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
}
|
||||
base += addr;
|
||||
}
|
||||
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::chr_read(unsigned addr) {
|
||||
if(chrram.size) return chrram.data[mirror(addr, chrram.size)];
|
||||
if(chrrom.size) return chrrom.data[mirror(addr, chrrom.size)];
|
||||
return 0u;
|
||||
}
|
||||
|
||||
void Board::chr_write(unsigned addr, uint8 data) {
|
||||
if(chrram.size) chrram.data[mirror(addr, chrram.size)] = data;
|
||||
}
|
||||
|
||||
void Board::power() {
|
||||
}
|
||||
|
||||
void Board::reset() {
|
||||
}
|
||||
|
||||
void Board::serialize(serializer& s) {
|
||||
if(prgram.size) s.array(prgram.data, prgram.size);
|
||||
if(chrram.size) s.array(chrram.data, chrram.size);
|
||||
}
|
||||
|
||||
Board::Board(Markup::Node& document) {
|
||||
cartridge.board = this;
|
||||
auto cartridge = document["cartridge"];
|
||||
|
||||
information.type = cartridge["board/type"].data;
|
||||
information.battery = cartridge["prg/ram/name"].exists();
|
||||
|
||||
auto prom = cartridge["prg/rom"];
|
||||
auto pram = cartridge["prg/ram"];
|
||||
auto crom = cartridge["chr/rom"];
|
||||
auto cram = cartridge["chr/ram"];
|
||||
|
||||
prgrom.size = numeral(prom["size"].data);
|
||||
prgram.size = numeral(pram["size"].data);
|
||||
chrrom.size = numeral(crom["size"].data);
|
||||
chrram.size = numeral(cram["size"].data);
|
||||
|
||||
if(prgrom.size) prgrom.data = new uint8[prgrom.size]();
|
||||
if(prgram.size) prgram.data = new uint8[prgram.size]();
|
||||
if(chrrom.size) chrrom.data = new uint8[chrrom.size]();
|
||||
if(chrram.size) chrram.data = new uint8[chrram.size]();
|
||||
|
||||
if(prom["name"].data) interface->loadRequest(ID::ProgramROM, prom["name"].data);
|
||||
if(pram["name"].data) interface->loadRequest(ID::ProgramRAM, pram["name"].data);
|
||||
if(crom["name"].data) interface->loadRequest(ID::CharacterROM, crom["name"].data);
|
||||
if(cram["name"].data) interface->loadRequest(ID::CharacterRAM, cram["name"].data);
|
||||
|
||||
if(pram["name"].data) Famicom::cartridge.memory.append({ID::ProgramRAM, pram["name"].data});
|
||||
if(cram["name"].data) Famicom::cartridge.memory.append({ID::CharacterRAM, cram["name"].data});
|
||||
|
||||
prgram.writable = true;
|
||||
chrram.writable = true;
|
||||
}
|
||||
|
||||
Board::~Board() {
|
||||
}
|
||||
|
||||
Board* Board::load(string manifest) {
|
||||
auto document = Markup::Document(manifest);
|
||||
cartridge.information.title = document["information/title"].text();
|
||||
|
||||
string type = document["cartridge/board/type"].text();
|
||||
|
||||
if(type == "BANDAI-FCG" ) return new BandaiFCG(document);
|
||||
|
||||
if(type == "KONAMI-VRC-1") return new KonamiVRC1(document);
|
||||
if(type == "KONAMI-VRC-2") return new KonamiVRC2(document);
|
||||
if(type == "KONAMI-VRC-3") return new KonamiVRC3(document);
|
||||
if(type == "KONAMI-VRC-4") return new KonamiVRC4(document);
|
||||
if(type == "KONAMI-VRC-6") return new KonamiVRC6(document);
|
||||
if(type == "KONAMI-VRC-7") return new KonamiVRC7(document);
|
||||
|
||||
if(type == "NES-AMROM" ) return new NES_AxROM(document);
|
||||
if(type == "NES-ANROM" ) return new NES_AxROM(document);
|
||||
if(type == "NES-AN1ROM" ) return new NES_AxROM(document);
|
||||
if(type == "NES-AOROM" ) return new NES_AxROM(document);
|
||||
|
||||
if(type == "NES-BNROM" ) return new NES_BNROM(document);
|
||||
|
||||
if(type == "NES-CNROM" ) return new NES_CNROM(document);
|
||||
|
||||
if(type == "NES-EKROM" ) return new NES_ExROM(document);
|
||||
if(type == "NES-ELROM" ) return new NES_ExROM(document);
|
||||
if(type == "NES-ETROM" ) return new NES_ExROM(document);
|
||||
if(type == "NES-EWROM" ) return new NES_ExROM(document);
|
||||
|
||||
if(type == "NES-FJROM" ) return new NES_FxROM(document);
|
||||
if(type == "NES-FKROM" ) return new NES_FxROM(document);
|
||||
|
||||
if(type == "NES-GNROM" ) return new NES_GxROM(document);
|
||||
if(type == "NES-MHROM" ) return new NES_GxROM(document);
|
||||
|
||||
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);
|
||||
|
||||
if(type == "SUNSOFT-5B" ) return new Sunsoft5B(document);
|
||||
|
||||
return nullptr;
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
struct Board {
|
||||
struct Memory {
|
||||
uint8_t* data;
|
||||
unsigned size;
|
||||
bool writable;
|
||||
|
||||
inline uint8 read(unsigned addr) const;
|
||||
inline void write(unsigned addr, uint8 data);
|
||||
|
||||
inline Memory(uint8_t* data, unsigned size) : data(data), size(size) {}
|
||||
inline Memory() : data(nullptr), size(0u), writable(false) {}
|
||||
inline ~Memory() { if(data) delete[] data; }
|
||||
};
|
||||
|
||||
static unsigned mirror(unsigned addr, unsigned size);
|
||||
|
||||
virtual void main();
|
||||
virtual void tick();
|
||||
|
||||
virtual uint8 prg_read(unsigned addr) = 0;
|
||||
virtual void prg_write(unsigned addr, uint8 data) = 0;
|
||||
|
||||
virtual uint8 chr_read(unsigned addr);
|
||||
virtual void chr_write(unsigned addr, uint8 data);
|
||||
|
||||
virtual inline void scanline(unsigned y) {}
|
||||
|
||||
virtual void power();
|
||||
virtual void reset();
|
||||
|
||||
virtual void serialize(serializer&);
|
||||
Board(Markup::Node& document);
|
||||
virtual ~Board();
|
||||
|
||||
static Board* load(string manifest);
|
||||
|
||||
struct Information {
|
||||
string type;
|
||||
bool battery;
|
||||
} information;
|
||||
|
||||
Memory prgrom;
|
||||
Memory prgram;
|
||||
Memory chrrom;
|
||||
Memory chrram;
|
||||
};
|
@@ -1,40 +0,0 @@
|
||||
struct KonamiVRC1 : Board {
|
||||
|
||||
VRC1 vrc1;
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if(addr & 0x8000) return prgrom.read(vrc1.prg_addr(addr));
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x8000) return vrc1.reg_write(addr, data);
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(vrc1.ciram_addr(addr));
|
||||
return Board::chr_read(vrc1.chr_addr(addr));
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(vrc1.ciram_addr(addr), data);
|
||||
return Board::chr_write(vrc1.chr_addr(addr), data);
|
||||
}
|
||||
|
||||
void power() {
|
||||
vrc1.power();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
vrc1.reset();
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
vrc1.serialize(s);
|
||||
}
|
||||
|
||||
KonamiVRC1(Markup::Node& document) : Board(document), vrc1(*this) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,57 +0,0 @@
|
||||
struct KonamiVRC2 : Board {
|
||||
|
||||
struct Settings {
|
||||
struct Pinout {
|
||||
unsigned a0;
|
||||
unsigned a1;
|
||||
} pinout;
|
||||
} settings;
|
||||
|
||||
VRC2 vrc2;
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if(addr < 0x6000) return cpu.mdr();
|
||||
if(addr < 0x8000) return vrc2.ram_read(addr);
|
||||
return prgrom.read(vrc2.prg_addr(addr));
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if(addr < 0x6000) return;
|
||||
if(addr < 0x8000) return vrc2.ram_write(addr, data);
|
||||
|
||||
bool a0 = (addr & settings.pinout.a0);
|
||||
bool a1 = (addr & settings.pinout.a1);
|
||||
addr &= 0xfff0;
|
||||
addr |= (a0 << 0) | (a1 << 1);
|
||||
return vrc2.reg_write(addr, data);
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(vrc2.ciram_addr(addr));
|
||||
return Board::chr_read(vrc2.chr_addr(addr));
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(vrc2.ciram_addr(addr), data);
|
||||
return Board::chr_write(vrc2.chr_addr(addr), data);
|
||||
}
|
||||
|
||||
void power() {
|
||||
vrc2.power();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
vrc2.reset();
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
vrc2.serialize(s);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
};
|
@@ -1,57 +0,0 @@
|
||||
struct KonamiVRC3 : Board {
|
||||
|
||||
struct Settings {
|
||||
bool mirror; //0 = horizontal, 1 = vertical
|
||||
} settings;
|
||||
|
||||
VRC3 vrc3;
|
||||
|
||||
void main() {
|
||||
vrc3.main();
|
||||
}
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if((addr & 0xe000) == 0x6000) return prgram.read(addr & 0x1fff);
|
||||
if(addr & 0x8000) return prgrom.read(vrc3.prg_addr(addr));
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if((addr & 0xe000) == 0x6000) return prgram.write(addr & 0x1fff, data);
|
||||
if(addr & 0x8000) return vrc3.reg_write(addr, data);
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) {
|
||||
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
return ppu.ciram_read(addr & 0x07ff);
|
||||
}
|
||||
return chrram.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);
|
||||
}
|
||||
return chrram.write(addr, data);
|
||||
}
|
||||
|
||||
void power() {
|
||||
vrc3.power();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
vrc3.reset();
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
vrc3.serialize(s);
|
||||
}
|
||||
|
||||
KonamiVRC3(Markup::Node& document) : Board(document), vrc3(*this) {
|
||||
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
|
||||
}
|
||||
|
||||
};
|
@@ -1,61 +0,0 @@
|
||||
struct KonamiVRC4 : Board {
|
||||
|
||||
struct Settings {
|
||||
struct Pinout {
|
||||
unsigned a0;
|
||||
unsigned a1;
|
||||
} pinout;
|
||||
} settings;
|
||||
|
||||
VRC4 vrc4;
|
||||
|
||||
void main() {
|
||||
return vrc4.main();
|
||||
}
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if(addr < 0x6000) return cpu.mdr();
|
||||
if(addr < 0x8000) return prgram.read(addr);
|
||||
return prgrom.read(vrc4.prg_addr(addr));
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if(addr < 0x6000) return;
|
||||
if(addr < 0x8000) return prgram.write(addr, data);
|
||||
|
||||
bool a0 = (addr & settings.pinout.a0);
|
||||
bool a1 = (addr & settings.pinout.a1);
|
||||
addr &= 0xfff0;
|
||||
addr |= (a1 << 1) | (a0 << 0);
|
||||
return vrc4.reg_write(addr, data);
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(vrc4.ciram_addr(addr));
|
||||
return Board::chr_read(vrc4.chr_addr(addr));
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(vrc4.ciram_addr(addr), data);
|
||||
return Board::chr_write(vrc4.chr_addr(addr), data);
|
||||
}
|
||||
|
||||
void power() {
|
||||
vrc4.power();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
vrc4.reset();
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
vrc4.serialize(s);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
};
|
@@ -1,42 +0,0 @@
|
||||
struct KonamiVRC6 : Board {
|
||||
|
||||
VRC6 vrc6;
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if((addr & 0xe000) == 0x6000) return vrc6.ram_read(addr);
|
||||
if(addr & 0x8000) return prgrom.read(vrc6.prg_addr(addr));
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if((addr & 0xe000) == 0x6000) return vrc6.ram_write(addr, data);
|
||||
if(addr & 0x8000) {
|
||||
addr = (addr & 0xf003);
|
||||
if(prgram.size) addr = (addr & ~3) | ((addr & 2) >> 1) | ((addr & 1) << 1);
|
||||
return vrc6.reg_write(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(vrc6.ciram_addr(addr));
|
||||
return Board::chr_read(vrc6.chr_addr(addr));
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(vrc6.ciram_addr(addr), data);
|
||||
return Board::chr_write(vrc6.chr_addr(addr), data);
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
vrc6.serialize(s);
|
||||
}
|
||||
|
||||
void main() { vrc6.main(); }
|
||||
void power() { vrc6.power(); }
|
||||
void reset() { vrc6.reset(); }
|
||||
|
||||
KonamiVRC6(Markup::Node& document) : Board(document), vrc6(*this) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,47 +0,0 @@
|
||||
struct KonamiVRC7 : Board {
|
||||
|
||||
VRC7 vrc7;
|
||||
|
||||
void main() {
|
||||
return vrc7.main();
|
||||
}
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if(addr < 0x6000) return cpu.mdr();
|
||||
if(addr < 0x8000) return prgram.read(addr);
|
||||
return prgrom.read(vrc7.prg_addr(addr));
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if(addr < 0x6000) return;
|
||||
if(addr < 0x8000) return prgram.write(addr, data);
|
||||
return vrc7.reg_write(addr, data);
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(vrc7.ciram_addr(addr));
|
||||
return chrram.read(vrc7.chr_addr(addr));
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(vrc7.ciram_addr(addr), data);
|
||||
return chrram.write(vrc7.chr_addr(addr), data);
|
||||
}
|
||||
|
||||
void power() {
|
||||
vrc7.power();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
vrc7.reset();
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
vrc7.serialize(s);
|
||||
}
|
||||
|
||||
KonamiVRC7(Markup::Node& document) : Board(document), vrc7(*this) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,51 +0,0 @@
|
||||
//NES-AMROM
|
||||
//NES-ANROM
|
||||
//NES-AN1ROM
|
||||
//NES-AOROM
|
||||
|
||||
struct NES_AxROM : Board {
|
||||
|
||||
uint4 prg_bank;
|
||||
bool mirror_select;
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if(addr & 0x8000) return prgrom.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() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
prg_bank = 0x0f;
|
||||
mirror_select = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
|
||||
s.integer(prg_bank);
|
||||
s.integer(mirror_select);
|
||||
}
|
||||
|
||||
NES_AxROM(Markup::Node& document) : Board(document) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,52 +0,0 @@
|
||||
//NES-BN-ROM-01
|
||||
|
||||
struct NES_BNROM : Board {
|
||||
|
||||
struct Settings {
|
||||
bool mirror; //0 = horizontal, 1 = vertical
|
||||
} settings;
|
||||
|
||||
uint2 prg_bank;
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x8000) prg_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);
|
||||
}
|
||||
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() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
prg_bank = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
s.integer(prg_bank);
|
||||
}
|
||||
|
||||
NES_BNROM(Markup::Node& document) : Board(document) {
|
||||
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
|
||||
}
|
||||
|
||||
};
|
@@ -1,54 +0,0 @@
|
||||
//NES-CNROM
|
||||
|
||||
struct NES_CNROM : Board {
|
||||
|
||||
struct Settings {
|
||||
bool mirror; //0 = horizontal, 1 = vertical
|
||||
} settings;
|
||||
|
||||
uint2 chr_bank;
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if(addr & 0x8000) return prgrom.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() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
chr_bank = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
s.integer(chr_bank);
|
||||
}
|
||||
|
||||
NES_CNROM(Markup::Node& document) : Board(document) {
|
||||
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
|
||||
}
|
||||
|
||||
};
|
@@ -1,53 +0,0 @@
|
||||
struct NES_ExROM : Board {
|
||||
|
||||
enum class Revision : unsigned {
|
||||
EKROM,
|
||||
ELROM,
|
||||
ETROM,
|
||||
EWROM,
|
||||
} revision;
|
||||
|
||||
MMC5 mmc5;
|
||||
|
||||
void main() {
|
||||
mmc5.main();
|
||||
}
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
return mmc5.prg_read(addr);
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
mmc5.prg_write(addr, data);
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
return mmc5.chr_read(addr);
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
mmc5.chr_write(addr, data);
|
||||
}
|
||||
|
||||
void scanline(unsigned y) {
|
||||
mmc5.scanline(y);
|
||||
}
|
||||
|
||||
void power() {
|
||||
mmc5.power();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
mmc5.reset();
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
mmc5.serialize(s);
|
||||
}
|
||||
|
||||
NES_ExROM(Markup::Node& document) : Board(document), mmc5(*this) {
|
||||
revision = Revision::ELROM;
|
||||
}
|
||||
|
||||
};
|
@@ -1,91 +0,0 @@
|
||||
//MMC4
|
||||
|
||||
struct NES_FxROM : Board {
|
||||
|
||||
enum Revision : unsigned {
|
||||
FJROM,
|
||||
FKROM,
|
||||
} revision;
|
||||
|
||||
uint4 prg_bank;
|
||||
uint5 chr_bank[2][2];
|
||||
bool mirror;
|
||||
bool latch[2];
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if(addr < 0x6000) return cpu.mdr();
|
||||
if(addr < 0x8000) return prgram.read(addr);
|
||||
unsigned bank = addr < 0xc000 ? prg_bank : (uint4)0x0f;
|
||||
return prgrom.read((bank * 0x4000) | (addr & 0x3fff));
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if(addr < 0x6000) return;
|
||||
if(addr < 0x8000) return prgram.write(addr, data);
|
||||
|
||||
switch(addr & 0xf000) {
|
||||
case 0xa000: prg_bank = data & 0x0f; break;
|
||||
case 0xb000: chr_bank[0][0] = data & 0x1f; break;
|
||||
case 0xc000: chr_bank[0][1] = data & 0x1f; break;
|
||||
case 0xd000: chr_bank[1][0] = data & 0x1f; break;
|
||||
case 0xe000: chr_bank[1][1] = data & 0x1f; break;
|
||||
case 0xf000: mirror = data & 0x01; break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const {
|
||||
switch(mirror) {
|
||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||
}
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
||||
bool region = addr & 0x1000;
|
||||
unsigned bank = chr_bank[region][latch[region]];
|
||||
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
|
||||
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
|
||||
return Board::chr_read((bank * 0x1000) | (addr & 0x0fff));
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
||||
bool region = addr & 0x1000;
|
||||
unsigned bank = chr_bank[region][latch[region]];
|
||||
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
|
||||
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
|
||||
return Board::chr_write((bank * 0x1000) | (addr & 0x0fff), data);
|
||||
}
|
||||
|
||||
void power() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
prg_bank = 0;
|
||||
chr_bank[0][0] = 0;
|
||||
chr_bank[0][1] = 0;
|
||||
chr_bank[1][0] = 0;
|
||||
chr_bank[1][1] = 0;
|
||||
mirror = 0;
|
||||
latch[0] = 0;
|
||||
latch[1] = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
|
||||
s.integer(prg_bank);
|
||||
s.integer(chr_bank[0][0]);
|
||||
s.integer(chr_bank[0][1]);
|
||||
s.integer(chr_bank[1][0]);
|
||||
s.integer(chr_bank[1][1]);
|
||||
s.integer(mirror);
|
||||
s.array(latch);
|
||||
}
|
||||
|
||||
NES_FxROM(Markup::Node& document) : Board(document) {
|
||||
revision = Revision::FKROM;
|
||||
}
|
||||
|
||||
};
|
@@ -1,61 +0,0 @@
|
||||
//NES-GNROM
|
||||
//NES-MHROM
|
||||
|
||||
struct NES_GxROM : Board {
|
||||
|
||||
struct Settings {
|
||||
bool mirror; //0 = horizontal, 1 = vertical
|
||||
} settings;
|
||||
|
||||
uint2 prg_bank;
|
||||
uint2 chr_bank;
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if(addr & 0x8000) return prgrom.read((prg_bank << 15) | (addr & 0x7fff));
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x8000) {
|
||||
prg_bank = (data & 0x30) >> 4;
|
||||
chr_bank = (data & 0x03) >> 0;
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
prg_bank = 0;
|
||||
chr_bank = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
s.integer(prg_bank);
|
||||
s.integer(chr_bank);
|
||||
}
|
||||
|
||||
NES_GxROM(Markup::Node& document) : Board(document) {
|
||||
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
|
||||
}
|
||||
|
||||
};
|
@@ -1,48 +0,0 @@
|
||||
struct NES_HKROM : Board {
|
||||
|
||||
MMC6 mmc6;
|
||||
|
||||
void main() {
|
||||
mmc6.main();
|
||||
}
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if((addr & 0xf000) == 0x7000) return mmc6.ram_read(addr);
|
||||
if(addr & 0x8000) return prgrom.read(mmc6.prg_addr(addr));
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if((addr & 0xf000) == 0x7000) return mmc6.ram_write(addr, data);
|
||||
if(addr & 0x8000) return mmc6.reg_write(addr, data);
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
mmc6.irq_test(addr);
|
||||
if(addr & 0x2000) return ppu.ciram_read(mmc6.ciram_addr(addr));
|
||||
return Board::chr_read(mmc6.chr_addr(addr));
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
mmc6.irq_test(addr);
|
||||
if(addr & 0x2000) return ppu.ciram_write(mmc6.ciram_addr(addr), data);
|
||||
return Board::chr_write(mmc6.chr_addr(addr), data);
|
||||
}
|
||||
|
||||
void power() {
|
||||
mmc6.power();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
mmc6.reset();
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
mmc6.serialize(s);
|
||||
}
|
||||
|
||||
NES_HKROM(Markup::Node& document) : Board(document), mmc6(*this) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,43 +0,0 @@
|
||||
//NES-NROM-128
|
||||
//NES-NROM-256
|
||||
|
||||
struct NES_NROM : Board {
|
||||
|
||||
struct Settings {
|
||||
bool mirror; //0 = horizontal, 1 = vertical
|
||||
} settings;
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if(addr & 0x8000) return prgrom.read(addr);
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) {
|
||||
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
return ppu.ciram_read(addr & 0x07ff);
|
||||
}
|
||||
if(chrram.size) return chrram.read(addr);
|
||||
return chrrom.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);
|
||||
}
|
||||
if(chrram.size) return chrram.write(addr, data);
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
}
|
||||
|
||||
NES_NROM(Markup::Node& document) : Board(document) {
|
||||
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
|
||||
}
|
||||
|
||||
};
|
@@ -1,97 +0,0 @@
|
||||
//MMC2
|
||||
|
||||
struct NES_PxROM : Board {
|
||||
|
||||
enum Revision : unsigned {
|
||||
PEEOROM,
|
||||
PNROM,
|
||||
} revision;
|
||||
|
||||
uint4 prg_bank;
|
||||
uint5 chr_bank[2][2];
|
||||
bool mirror;
|
||||
bool latch[2];
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if(addr < 0x6000) return cpu.mdr();
|
||||
if(addr < 0x8000) return prgram.read(addr);
|
||||
unsigned bank = 0;
|
||||
switch((addr / 0x2000) & 3) {
|
||||
case 0: bank = prg_bank; break;
|
||||
case 1: bank = 0x0d; break;
|
||||
case 2: bank = 0x0e; break;
|
||||
case 3: bank = 0x0f; break;
|
||||
}
|
||||
return prgrom.read((bank * 0x2000) | (addr & 0x1fff));
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if(addr < 0x6000) return;
|
||||
if(addr < 0x8000) return prgram.write(addr, data);
|
||||
|
||||
switch(addr & 0xf000) {
|
||||
case 0xa000: prg_bank = data & 0x0f; break;
|
||||
case 0xb000: chr_bank[0][0] = data & 0x1f; break;
|
||||
case 0xc000: chr_bank[0][1] = data & 0x1f; break;
|
||||
case 0xd000: chr_bank[1][0] = data & 0x1f; break;
|
||||
case 0xe000: chr_bank[1][1] = data & 0x1f; break;
|
||||
case 0xf000: mirror = data & 0x01; break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const {
|
||||
switch(mirror) {
|
||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||
}
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
||||
bool region = addr & 0x1000;
|
||||
unsigned bank = chr_bank[region][latch[region]];
|
||||
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
|
||||
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
|
||||
return Board::chr_read((bank * 0x1000) | (addr & 0x0fff));
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
||||
bool region = addr & 0x1000;
|
||||
unsigned bank = chr_bank[region][latch[region]];
|
||||
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
|
||||
if((addr & 0x0ff8) == 0x0fe8) latch[region] = 1;
|
||||
return Board::chr_write((bank * 0x1000) | (addr & 0x0fff), data);
|
||||
}
|
||||
|
||||
void power() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
prg_bank = 0;
|
||||
chr_bank[0][0] = 0;
|
||||
chr_bank[0][1] = 0;
|
||||
chr_bank[1][0] = 0;
|
||||
chr_bank[1][1] = 0;
|
||||
mirror = 0;
|
||||
latch[0] = 0;
|
||||
latch[1] = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
|
||||
s.integer(prg_bank);
|
||||
s.integer(chr_bank[0][0]);
|
||||
s.integer(chr_bank[0][1]);
|
||||
s.integer(chr_bank[1][0]);
|
||||
s.integer(chr_bank[1][1]);
|
||||
s.integer(mirror);
|
||||
s.array(latch);
|
||||
}
|
||||
|
||||
NES_PxROM(Markup::Node& document) : Board(document) {
|
||||
revision = Revision::PNROM;
|
||||
}
|
||||
|
||||
};
|
@@ -1,101 +0,0 @@
|
||||
struct NES_SxROM : Board {
|
||||
|
||||
enum class Revision : unsigned {
|
||||
SAROM,
|
||||
SBROM,
|
||||
SCROM,
|
||||
SC1ROM,
|
||||
SEROM,
|
||||
SFROM,
|
||||
SGROM,
|
||||
SHROM,
|
||||
SH1ROM,
|
||||
SIROM,
|
||||
SJROM,
|
||||
SKROM,
|
||||
SLROM,
|
||||
SL1ROM,
|
||||
SL2ROM,
|
||||
SL3ROM,
|
||||
SLRROM,
|
||||
SMROM,
|
||||
SNROM,
|
||||
SOROM,
|
||||
SUROM,
|
||||
SXROM,
|
||||
} revision;
|
||||
|
||||
MMC1 mmc1;
|
||||
|
||||
void main() {
|
||||
return mmc1.main();
|
||||
}
|
||||
|
||||
unsigned ram_addr(unsigned addr) {
|
||||
unsigned bank = 0;
|
||||
if(revision == Revision::SOROM) bank = (mmc1.chr_bank[0] & 0x08) >> 3;
|
||||
if(revision == Revision::SUROM) bank = (mmc1.chr_bank[0] & 0x0c) >> 2;
|
||||
if(revision == Revision::SXROM) bank = (mmc1.chr_bank[0] & 0x0c) >> 2;
|
||||
return (bank << 13) | (addr & 0x1fff);
|
||||
}
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if((addr & 0xe000) == 0x6000) {
|
||||
if(revision == Revision::SNROM) {
|
||||
if(mmc1.chr_bank[0] & 0x10) return cpu.mdr();
|
||||
}
|
||||
if(mmc1.ram_disable) return 0x00;
|
||||
return prgram.read(ram_addr(addr));
|
||||
}
|
||||
|
||||
if(addr & 0x8000) {
|
||||
addr = mmc1.prg_addr(addr);
|
||||
if(revision == Revision::SXROM) {
|
||||
addr |= ((mmc1.chr_bank[0] & 0x10) >> 4) << 18;
|
||||
}
|
||||
return prgrom.read(addr);
|
||||
}
|
||||
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if((addr & 0xe000) == 0x6000) {
|
||||
if(revision == Revision::SNROM) {
|
||||
if(mmc1.chr_bank[0] & 0x10) return;
|
||||
}
|
||||
if(mmc1.ram_disable) return;
|
||||
return prgram.write(ram_addr(addr), data);
|
||||
}
|
||||
|
||||
if(addr & 0x8000) return mmc1.mmio_write(addr, data);
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(mmc1.ciram_addr(addr));
|
||||
return Board::chr_read(mmc1.chr_addr(addr));
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(mmc1.ciram_addr(addr), data);
|
||||
return Board::chr_write(mmc1.chr_addr(addr), data);
|
||||
}
|
||||
|
||||
void power() {
|
||||
mmc1.power();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
mmc1.reset();
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
mmc1.serialize(s);
|
||||
}
|
||||
|
||||
NES_SxROM(Markup::Node& document) : Board(document), mmc1(*this) {
|
||||
revision = Revision::SXROM;
|
||||
}
|
||||
|
||||
};
|
@@ -1,67 +0,0 @@
|
||||
struct NES_TxROM : Board {
|
||||
|
||||
enum class Revision : unsigned {
|
||||
TBROM,
|
||||
TEROM,
|
||||
TFROM,
|
||||
TGROM,
|
||||
TKROM,
|
||||
TKSROM,
|
||||
TLROM,
|
||||
TL1ROM,
|
||||
TL2ROM,
|
||||
TLSROM,
|
||||
TNROM,
|
||||
TQROM,
|
||||
TR1ROM,
|
||||
TSROM,
|
||||
TVROM,
|
||||
} revision;
|
||||
|
||||
MMC3 mmc3;
|
||||
|
||||
void main() {
|
||||
mmc3.main();
|
||||
}
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if((addr & 0xe000) == 0x6000) return mmc3.ram_read(addr);
|
||||
if(addr & 0x8000) return prgrom.read(mmc3.prg_addr(addr));
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if((addr & 0xe000) == 0x6000) return mmc3.ram_write(addr, data);
|
||||
if(addr & 0x8000) return mmc3.reg_write(addr, data);
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
mmc3.irq_test(addr);
|
||||
if(addr & 0x2000) return ppu.ciram_read(mmc3.ciram_addr(addr));
|
||||
return Board::chr_read(mmc3.chr_addr(addr));
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
mmc3.irq_test(addr);
|
||||
if(addr & 0x2000) return ppu.ciram_write(mmc3.ciram_addr(addr), data);
|
||||
return Board::chr_write(mmc3.chr_addr(addr), data);
|
||||
}
|
||||
|
||||
void power() {
|
||||
mmc3.power();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
mmc3.reset();
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
mmc3.serialize(s);
|
||||
}
|
||||
|
||||
NES_TxROM(Markup::Node& document) : Board(document), mmc3(*this) {
|
||||
revision = Revision::TLROM;
|
||||
}
|
||||
|
||||
};
|
@@ -1,55 +0,0 @@
|
||||
//NES-UNROM
|
||||
//NES-UOROM
|
||||
|
||||
struct NES_UxROM : Board {
|
||||
|
||||
struct Settings {
|
||||
bool mirror; //0 = horizontal, 1 = vertical
|
||||
} settings;
|
||||
|
||||
uint4 prg_bank;
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if((addr & 0xc000) == 0x8000) return prgrom.read((prg_bank << 14) | (addr & 0x3fff));
|
||||
if((addr & 0xc000) == 0xc000) return prgrom.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() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
prg_bank = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
|
||||
s.integer(prg_bank);
|
||||
}
|
||||
|
||||
NES_UxROM(Markup::Node& document) : Board(document) {
|
||||
settings.mirror = document["cartridge"]["mirror"]["mode"].data == "vertical" ? 1 : 0;
|
||||
}
|
||||
|
||||
};
|
@@ -1,226 +0,0 @@
|
||||
//SUNSOFT-5B
|
||||
|
||||
struct Sunsoft5B : Board {
|
||||
|
||||
uint4 mmu_port;
|
||||
uint4 apu_port;
|
||||
|
||||
uint8 prg_bank[4];
|
||||
uint8 chr_bank[8];
|
||||
uint2 mirror;
|
||||
bool irq_enable;
|
||||
bool irq_counter_enable;
|
||||
uint16 irq_counter;
|
||||
|
||||
int16 dac[16];
|
||||
|
||||
struct Pulse {
|
||||
bool disable;
|
||||
uint12 frequency;
|
||||
uint4 volume;
|
||||
|
||||
uint16 counter; //12-bit countdown + 4-bit phase
|
||||
uint1 duty;
|
||||
uint4 output;
|
||||
|
||||
void clock() {
|
||||
if(--counter == 0) {
|
||||
counter = frequency << 4;
|
||||
duty ^= 1;
|
||||
}
|
||||
output = duty ? volume : (uint4)0;
|
||||
if(disable) output = 0;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
disable = 1;
|
||||
frequency = 1;
|
||||
volume = 0;
|
||||
|
||||
counter = 0;
|
||||
duty = 0;
|
||||
output = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
s.integer(disable);
|
||||
s.integer(frequency);
|
||||
s.integer(volume);
|
||||
|
||||
s.integer(counter);
|
||||
s.integer(duty);
|
||||
s.integer(output);
|
||||
}
|
||||
} pulse[3];
|
||||
|
||||
void main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_counter_enable) {
|
||||
if(--irq_counter == 0xffff) {
|
||||
cpu.set_irq_line(irq_enable);
|
||||
}
|
||||
}
|
||||
|
||||
pulse[0].clock();
|
||||
pulse[1].clock();
|
||||
pulse[2].clock();
|
||||
int16 output = dac[pulse[0].output] + dac[pulse[1].output] + dac[pulse[2].output];
|
||||
apu.set_sample(-output);
|
||||
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if(addr < 0x6000) return cpu.mdr();
|
||||
|
||||
uint8 bank = 0x3f; //((addr & 0xe000) == 0xe000
|
||||
if((addr & 0xe000) == 0x6000) bank = prg_bank[0];
|
||||
if((addr & 0xe000) == 0x8000) bank = prg_bank[1];
|
||||
if((addr & 0xe000) == 0xa000) bank = prg_bank[2];
|
||||
if((addr & 0xe000) == 0xc000) bank = prg_bank[3];
|
||||
|
||||
bool ram_enable = bank & 0x80;
|
||||
bool ram_select = bank & 0x40;
|
||||
bank &= 0x3f;
|
||||
|
||||
if(ram_select) {
|
||||
if(ram_enable == false) return cpu.mdr();
|
||||
return prgram.data[addr & 0x1fff];
|
||||
}
|
||||
|
||||
addr = (bank << 13) | (addr & 0x1fff);
|
||||
return prgrom.read(addr);
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if((addr & 0xe000) == 0x6000) {
|
||||
prgram.data[addr & 0x1fff] = data;
|
||||
}
|
||||
|
||||
if(addr == 0x8000) {
|
||||
mmu_port = data & 0x0f;
|
||||
}
|
||||
|
||||
if(addr == 0xa000) {
|
||||
switch(mmu_port) {
|
||||
case 0: chr_bank[0] = data; break;
|
||||
case 1: chr_bank[1] = data; break;
|
||||
case 2: chr_bank[2] = data; break;
|
||||
case 3: chr_bank[3] = data; break;
|
||||
case 4: chr_bank[4] = data; break;
|
||||
case 5: chr_bank[5] = data; break;
|
||||
case 6: chr_bank[6] = data; break;
|
||||
case 7: chr_bank[7] = data; break;
|
||||
case 8: prg_bank[0] = data; break;
|
||||
case 9: prg_bank[1] = data; break;
|
||||
case 10: prg_bank[2] = data; break;
|
||||
case 11: prg_bank[3] = data; break;
|
||||
case 12: mirror = data & 3; break;
|
||||
case 13:
|
||||
irq_enable = data & 0x80;
|
||||
irq_counter_enable = data & 0x01;
|
||||
if(irq_enable == 0) cpu.set_irq_line(0);
|
||||
break;
|
||||
case 14: irq_counter = (irq_counter & 0xff00) | (data << 0); break;
|
||||
case 15: irq_counter = (irq_counter & 0x00ff) | (data << 8); break;
|
||||
}
|
||||
}
|
||||
|
||||
if(addr == 0xc000) {
|
||||
apu_port = data & 0x0f;
|
||||
}
|
||||
|
||||
if(addr == 0xe000) {
|
||||
switch(apu_port) {
|
||||
case 0: pulse[0].frequency = (pulse[0].frequency & 0xff00) | (data << 0); break;
|
||||
case 1: pulse[0].frequency = (pulse[0].frequency & 0x00ff) | (data << 8); break;
|
||||
case 2: pulse[1].frequency = (pulse[1].frequency & 0xff00) | (data << 0); break;
|
||||
case 3: pulse[1].frequency = (pulse[1].frequency & 0x00ff) | (data << 8); break;
|
||||
case 4: pulse[2].frequency = (pulse[2].frequency & 0xff00) | (data << 0); break;
|
||||
case 5: pulse[2].frequency = (pulse[2].frequency & 0x00ff) | (data << 8); break;
|
||||
case 7:
|
||||
pulse[0].disable = data & 0x01;
|
||||
pulse[1].disable = data & 0x02;
|
||||
pulse[2].disable = data & 0x04;
|
||||
break;
|
||||
case 8: pulse[0].volume = data & 0x0f; break;
|
||||
case 9: pulse[1].volume = data & 0x0f; break;
|
||||
case 10: pulse[2].volume = data & 0x0f; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned chr_addr(unsigned addr) {
|
||||
uint8 bank = (addr >> 10) & 7;
|
||||
return (chr_bank[bank] << 10) | (addr & 0x03ff);
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) {
|
||||
switch(mirror) {
|
||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical
|
||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal
|
||||
case 2: return 0x0000 | (addr & 0x03ff); //first
|
||||
case 3: return 0x0400 | (addr & 0x03ff); //second
|
||||
}
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
||||
return Board::chr_read(chr_addr(addr));
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
||||
return Board::chr_write(chr_addr(addr), data);
|
||||
}
|
||||
|
||||
void power() {
|
||||
for(signed n = 0; n < 16; n++) {
|
||||
double volume = 1.0 / pow(2, 1.0 / 2 * (15 - n));
|
||||
dac[n] = volume * 8192.0;
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
mmu_port = 0;
|
||||
apu_port = 0;
|
||||
|
||||
for(auto& n : prg_bank) n = 0;
|
||||
for(auto& n : chr_bank) n = 0;
|
||||
mirror = 0;
|
||||
irq_enable = 0;
|
||||
irq_counter_enable = 0;
|
||||
irq_counter = 0;
|
||||
|
||||
pulse[0].reset();
|
||||
pulse[1].reset();
|
||||
pulse[2].reset();
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
Board::serialize(s);
|
||||
|
||||
s.integer(mmu_port);
|
||||
s.integer(apu_port);
|
||||
|
||||
s.array(prg_bank);
|
||||
s.array(chr_bank);
|
||||
s.integer(mirror);
|
||||
s.integer(irq_enable);
|
||||
s.integer(irq_counter_enable);
|
||||
s.integer(irq_counter);
|
||||
|
||||
pulse[0].serialize(s);
|
||||
pulse[1].serialize(s);
|
||||
pulse[2].serialize(s);
|
||||
}
|
||||
|
||||
Sunsoft5B(Markup::Node& document) : Board(document) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,86 +0,0 @@
|
||||
#include <fc/fc.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
|
||||
#include "chip/chip.cpp"
|
||||
#include "board/board.cpp"
|
||||
Cartridge cartridge;
|
||||
|
||||
string Cartridge::title() {
|
||||
return information.title;
|
||||
}
|
||||
|
||||
void Cartridge::Main() {
|
||||
cartridge.main();
|
||||
}
|
||||
|
||||
void Cartridge::main() {
|
||||
board->main();
|
||||
}
|
||||
|
||||
void Cartridge::load() {
|
||||
interface->loadRequest(ID::Manifest, "manifest.bml");
|
||||
|
||||
Board::load(information.markup); //this call will set Cartridge::board if successful
|
||||
if(board == nullptr) return;
|
||||
|
||||
sha256_ctx sha;
|
||||
uint8 hash[32];
|
||||
sha256_init(&sha);
|
||||
sha256_chunk(&sha, board->prgrom.data, board->prgrom.size);
|
||||
sha256_chunk(&sha, board->chrrom.data, board->chrrom.size);
|
||||
sha256_final(&sha);
|
||||
sha256_hash(&sha, hash);
|
||||
string result;
|
||||
for(auto& byte : hash) result.append(hex<2>(byte));
|
||||
sha256 = result;
|
||||
|
||||
system.load();
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
void Cartridge::unload() {
|
||||
if(loaded == false) return;
|
||||
loaded = false;
|
||||
memory.reset();
|
||||
}
|
||||
|
||||
void Cartridge::power() {
|
||||
board->power();
|
||||
}
|
||||
|
||||
void Cartridge::reset() {
|
||||
create(Cartridge::Main, 21477272);
|
||||
board->reset();
|
||||
}
|
||||
|
||||
Cartridge::Cartridge() {
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
uint8 Cartridge::prg_read(unsigned addr) {
|
||||
return board->prg_read(addr);
|
||||
}
|
||||
|
||||
void Cartridge::prg_write(unsigned addr, uint8 data) {
|
||||
return board->prg_write(addr, data);
|
||||
}
|
||||
|
||||
uint8 Cartridge::chr_read(unsigned addr) {
|
||||
return board->chr_read(addr);
|
||||
}
|
||||
|
||||
void Cartridge::chr_write(unsigned addr, uint8 data) {
|
||||
return board->chr_write(addr, data);
|
||||
}
|
||||
|
||||
void Cartridge::scanline(unsigned y) {
|
||||
return board->scanline(y);
|
||||
}
|
||||
|
||||
void Cartridge::serialize(serializer& s) {
|
||||
Thread::serialize(s);
|
||||
return board->serialize(s);
|
||||
}
|
||||
|
||||
}
|
@@ -1,47 +0,0 @@
|
||||
#include "chip/chip.hpp"
|
||||
#include "board/board.hpp"
|
||||
|
||||
struct Cartridge : Thread, property<Cartridge> {
|
||||
static void Main();
|
||||
void main();
|
||||
|
||||
void load();
|
||||
void unload();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
readonly<bool> loaded;
|
||||
readonly<string> sha256;
|
||||
|
||||
struct Information {
|
||||
string markup;
|
||||
string title;
|
||||
} information;
|
||||
|
||||
string title();
|
||||
|
||||
struct Memory {
|
||||
unsigned id;
|
||||
string name;
|
||||
};
|
||||
vector<Memory> memory;
|
||||
|
||||
void serialize(serializer&);
|
||||
Cartridge();
|
||||
|
||||
//privileged:
|
||||
Board *board;
|
||||
|
||||
uint8 prg_read(unsigned addr);
|
||||
void prg_write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 chr_read(unsigned addr);
|
||||
void chr_write(unsigned addr, uint8 data);
|
||||
|
||||
//scanline() is for debugging purposes only:
|
||||
//boards must detect scanline edges on their own
|
||||
void scanline(unsigned y);
|
||||
};
|
||||
|
||||
extern Cartridge cartridge;
|
@@ -1,7 +0,0 @@
|
||||
struct Board;
|
||||
|
||||
struct Chip {
|
||||
Board& board;
|
||||
void tick();
|
||||
Chip(Board& board);
|
||||
};
|
@@ -1,136 +0,0 @@
|
||||
struct MMC1 : Chip {
|
||||
|
||||
enum class Revision : unsigned {
|
||||
MMC1,
|
||||
MMC1A,
|
||||
MMC1B1,
|
||||
MMC1B2,
|
||||
MMC1B3,
|
||||
MMC1C,
|
||||
} revision;
|
||||
|
||||
unsigned writedelay;
|
||||
unsigned shiftaddr;
|
||||
unsigned shiftdata;
|
||||
|
||||
bool chr_mode;
|
||||
bool prg_size; //0 = 32K, 1 = 16K
|
||||
bool prg_mode;
|
||||
uint2 mirror; //0 = first, 1 = second, 2 = vertical, 3 = horizontal
|
||||
uint5 chr_bank[2];
|
||||
bool ram_disable;
|
||||
uint4 prg_bank;
|
||||
|
||||
void main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(writedelay) writedelay--;
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned prg_addr(unsigned addr) {
|
||||
bool region = addr & 0x4000;
|
||||
unsigned bank = (prg_bank & ~1) + region;
|
||||
|
||||
if(prg_size) {
|
||||
bank = (region == 0 ? 0x0 : 0xf);
|
||||
if(region != prg_mode) bank = prg_bank;
|
||||
}
|
||||
|
||||
return (bank << 14) | (addr & 0x3fff);
|
||||
}
|
||||
|
||||
unsigned chr_addr(unsigned addr) {
|
||||
bool region = addr & 0x1000;
|
||||
unsigned bank = chr_bank[region];
|
||||
if(chr_mode == 0) bank = (chr_bank[0] & ~1) | region;
|
||||
return (bank << 12) | (addr & 0x0fff);
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) {
|
||||
switch(mirror) {
|
||||
case 0: return 0x0000 | (addr & 0x03ff);
|
||||
case 1: return 0x0400 | (addr & 0x03ff);
|
||||
case 2: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||
case 3: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
}
|
||||
}
|
||||
|
||||
void mmio_write(unsigned addr, uint8 data) {
|
||||
if(writedelay) return;
|
||||
writedelay = 2;
|
||||
|
||||
if(data & 0x80) {
|
||||
shiftaddr = 0;
|
||||
prg_size = 1;
|
||||
prg_mode = 1;
|
||||
} else {
|
||||
shiftdata = ((data & 1) << 4) | (shiftdata >> 1);
|
||||
if(++shiftaddr == 5) {
|
||||
shiftaddr = 0;
|
||||
switch((addr >> 13) & 3) {
|
||||
case 0:
|
||||
chr_mode = (shiftdata & 0x10);
|
||||
prg_size = (shiftdata & 0x08);
|
||||
prg_mode = (shiftdata & 0x04);
|
||||
mirror = (shiftdata & 0x03);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
chr_bank[0] = (shiftdata & 0x1f);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
chr_bank[1] = (shiftdata & 0x1f);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
ram_disable = (shiftdata & 0x10);
|
||||
prg_bank = (shiftdata & 0x0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void power() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
writedelay = 0;
|
||||
shiftaddr = 0;
|
||||
shiftdata = 0;
|
||||
|
||||
chr_mode = 0;
|
||||
prg_size = 1;
|
||||
prg_mode = 1;
|
||||
mirror = 0;
|
||||
chr_bank[0] = 0;
|
||||
chr_bank[1] = 1;
|
||||
ram_disable = 0;
|
||||
prg_bank = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
s.integer(writedelay);
|
||||
s.integer(shiftaddr);
|
||||
s.integer(shiftdata);
|
||||
|
||||
s.integer(chr_mode);
|
||||
s.integer(prg_size);
|
||||
s.integer(prg_mode);
|
||||
s.integer(mirror);
|
||||
s.array(chr_bank);
|
||||
s.integer(ram_disable);
|
||||
s.integer(prg_bank);
|
||||
}
|
||||
|
||||
MMC1(Board& board) : Chip(board) {
|
||||
revision = Revision::MMC1B2;
|
||||
}
|
||||
|
||||
};
|
@@ -1,189 +0,0 @@
|
||||
struct MMC3 : Chip {
|
||||
|
||||
bool chr_mode;
|
||||
bool prg_mode;
|
||||
uint3 bank_select;
|
||||
uint8 prg_bank[2];
|
||||
uint8 chr_bank[6];
|
||||
bool mirror;
|
||||
bool ram_enable;
|
||||
bool ram_write_protect;
|
||||
uint8 irq_latch;
|
||||
uint8 irq_counter;
|
||||
bool irq_enable;
|
||||
unsigned irq_delay;
|
||||
bool irq_line;
|
||||
|
||||
uint16 chr_abus;
|
||||
|
||||
void main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_delay) irq_delay--;
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
void irq_test(unsigned addr) {
|
||||
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
|
||||
if(irq_delay == 0) {
|
||||
if(irq_counter == 0) {
|
||||
irq_counter = irq_latch;
|
||||
} else if(--irq_counter == 0) {
|
||||
if(irq_enable) irq_line = 1;
|
||||
}
|
||||
}
|
||||
irq_delay = 6;
|
||||
}
|
||||
chr_abus = addr;
|
||||
}
|
||||
|
||||
unsigned prg_addr(unsigned addr) const {
|
||||
switch((addr >> 13) & 3) {
|
||||
case 0:
|
||||
if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff);
|
||||
return (prg_bank[0] << 13) | (addr & 0x1fff);
|
||||
case 1:
|
||||
return (prg_bank[1] << 13) | (addr & 0x1fff);
|
||||
case 2:
|
||||
if(prg_mode == 0) return (0x3e << 13) | (addr & 0x1fff);
|
||||
return (prg_bank[0] << 13) | (addr & 0x1fff);
|
||||
case 3:
|
||||
return (0x3f << 13) | (addr & 0x1fff);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned chr_addr(unsigned addr) const {
|
||||
if(chr_mode == 0) {
|
||||
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
||||
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
||||
if(addr <= 0x13ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x17ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x1bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x1fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
|
||||
} else {
|
||||
if(addr <= 0x03ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x07ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x0bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x0fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
||||
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const {
|
||||
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
}
|
||||
|
||||
uint8 ram_read(unsigned addr) {
|
||||
if(ram_enable) return board.prgram.data[addr & 0x1fff];
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void ram_write(unsigned addr, uint8 data) {
|
||||
if(ram_enable && !ram_write_protect) board.prgram.data[addr & 0x1fff] = data;
|
||||
}
|
||||
|
||||
void reg_write(unsigned addr, uint8 data) {
|
||||
switch(addr & 0xe001) {
|
||||
case 0x8000:
|
||||
chr_mode = data & 0x80;
|
||||
prg_mode = data & 0x40;
|
||||
bank_select = data & 0x07;
|
||||
break;
|
||||
|
||||
case 0x8001:
|
||||
switch(bank_select) {
|
||||
case 0: chr_bank[0] = data & ~1; break;
|
||||
case 1: chr_bank[1] = data & ~1; break;
|
||||
case 2: chr_bank[2] = data; break;
|
||||
case 3: chr_bank[3] = data; break;
|
||||
case 4: chr_bank[4] = data; break;
|
||||
case 5: chr_bank[5] = data; break;
|
||||
case 6: prg_bank[0] = data & 0x3f; break;
|
||||
case 7: prg_bank[1] = data & 0x3f; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xa000:
|
||||
mirror = data & 0x01;
|
||||
break;
|
||||
|
||||
case 0xa001:
|
||||
ram_enable = data & 0x80;
|
||||
ram_write_protect = data & 0x40;
|
||||
break;
|
||||
|
||||
case 0xc000:
|
||||
irq_latch = data;
|
||||
break;
|
||||
|
||||
case 0xc001:
|
||||
irq_counter = 0;
|
||||
break;
|
||||
|
||||
case 0xe000:
|
||||
irq_enable = false;
|
||||
irq_line = 0;
|
||||
break;
|
||||
|
||||
case 0xe001:
|
||||
irq_enable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void power() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
chr_mode = 0;
|
||||
prg_mode = 0;
|
||||
bank_select = 0;
|
||||
prg_bank[0] = 0;
|
||||
prg_bank[1] = 0;
|
||||
chr_bank[0] = 0;
|
||||
chr_bank[1] = 0;
|
||||
chr_bank[2] = 0;
|
||||
chr_bank[3] = 0;
|
||||
chr_bank[4] = 0;
|
||||
chr_bank[5] = 0;
|
||||
mirror = 0;
|
||||
ram_enable = 1;
|
||||
ram_write_protect = 0;
|
||||
irq_latch = 0;
|
||||
irq_counter = 0;
|
||||
irq_enable = false;
|
||||
irq_delay = 0;
|
||||
irq_line = 0;
|
||||
|
||||
chr_abus = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
s.integer(chr_mode);
|
||||
s.integer(prg_mode);
|
||||
s.integer(bank_select);
|
||||
s.array(prg_bank);
|
||||
s.array(chr_bank);
|
||||
s.integer(mirror);
|
||||
s.integer(ram_enable);
|
||||
s.integer(ram_write_protect);
|
||||
s.integer(irq_latch);
|
||||
s.integer(irq_counter);
|
||||
s.integer(irq_enable);
|
||||
s.integer(irq_delay);
|
||||
s.integer(irq_line);
|
||||
|
||||
s.integer(chr_abus);
|
||||
}
|
||||
|
||||
MMC3(Board& board) : Chip(board) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,497 +0,0 @@
|
||||
struct MMC5 : Chip {
|
||||
|
||||
enum class Revision : unsigned {
|
||||
MMC5,
|
||||
MMC5B,
|
||||
} revision;
|
||||
|
||||
uint8 exram[1024];
|
||||
|
||||
//programmable registers
|
||||
|
||||
uint2 prg_mode; //$5100
|
||||
uint2 chr_mode; //$5101
|
||||
|
||||
uint2 prgram_write_protect[2]; //$5102,$5103
|
||||
|
||||
uint2 exram_mode; //$5104
|
||||
uint2 nametable_mode[4]; //$5105
|
||||
uint8 fillmode_tile; //$5106
|
||||
uint8 fillmode_color; //$5107
|
||||
|
||||
bool ram_select; //$5113
|
||||
uint2 ram_bank; //$5113
|
||||
uint8 prg_bank[4]; //$5114-5117
|
||||
uint10 chr_sprite_bank[8]; //$5120-5127
|
||||
uint10 chr_bg_bank[4]; //$5128-512b
|
||||
uint2 chr_bank_hi; //$5130
|
||||
|
||||
bool vs_enable; //$5200
|
||||
bool vs_side; //$5200
|
||||
uint5 vs_tile; //$5200
|
||||
uint8 vs_scroll; //$5201
|
||||
uint8 vs_bank; //$5202
|
||||
|
||||
uint8 irq_line; //$5203
|
||||
bool irq_enable; //$5204
|
||||
|
||||
uint8 multiplicand; //$5205
|
||||
uint8 multiplier; //$5206
|
||||
|
||||
//status registers
|
||||
|
||||
unsigned cpu_cycle_counter;
|
||||
unsigned irq_counter;
|
||||
bool irq_pending;
|
||||
bool in_frame;
|
||||
|
||||
unsigned vcounter;
|
||||
unsigned hcounter;
|
||||
uint16 chr_access[4];
|
||||
bool chr_active;
|
||||
bool sprite_8x16;
|
||||
|
||||
uint8 exbank;
|
||||
uint8 exattr;
|
||||
|
||||
bool vs_fetch;
|
||||
uint8 vs_vpos;
|
||||
uint8 vs_hpos;
|
||||
|
||||
void main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
//scanline() resets this; if no scanlines detected, enter video blanking period
|
||||
if(++cpu_cycle_counter >= 200) blank(); //113-114 normal; ~2500 across Vblank period
|
||||
|
||||
cpu.set_irq_line(irq_enable && irq_pending);
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
void scanline(unsigned y) {
|
||||
//used for testing only, to verify MMC5 scanline detection is accurate:
|
||||
//if(y != vcounter && y <= 240) print(y, " vs ", vcounter, "\n");
|
||||
}
|
||||
|
||||
uint8 prg_access(bool write, unsigned addr, uint8 data = 0x00) {
|
||||
unsigned bank;
|
||||
|
||||
if((addr & 0xe000) == 0x6000) {
|
||||
bank = (ram_select << 2) | ram_bank;
|
||||
addr &= 0x1fff;
|
||||
} else if(prg_mode == 0) {
|
||||
bank = prg_bank[3] & ~3;
|
||||
addr &= 0x7fff;
|
||||
} else if(prg_mode == 1) {
|
||||
if((addr & 0xc000) == 0x8000) bank = (prg_bank[1] & ~1);
|
||||
if((addr & 0xe000) == 0xc000) bank = (prg_bank[3] & ~1);
|
||||
addr &= 0x3fff;
|
||||
} else if(prg_mode == 2) {
|
||||
if((addr & 0xe000) == 0x8000) bank = (prg_bank[1] & ~1) | 0;
|
||||
if((addr & 0xe000) == 0xa000) bank = (prg_bank[1] & ~1) | 1;
|
||||
if((addr & 0xe000) == 0xc000) bank = (prg_bank[2]);
|
||||
if((addr & 0xe000) == 0xe000) bank = (prg_bank[3]);
|
||||
addr &= 0x1fff;
|
||||
} else if(prg_mode == 3) {
|
||||
if((addr & 0xe000) == 0x8000) bank = prg_bank[0];
|
||||
if((addr & 0xe000) == 0xa000) bank = prg_bank[1];
|
||||
if((addr & 0xe000) == 0xc000) bank = prg_bank[2];
|
||||
if((addr & 0xe000) == 0xe000) bank = prg_bank[3];
|
||||
addr &= 0x1fff;
|
||||
}
|
||||
|
||||
bool rom = bank & 0x80;
|
||||
bank &= 0x7f;
|
||||
|
||||
if(write == false) {
|
||||
if(rom) {
|
||||
return board.prgrom.read((bank << 13) | addr);
|
||||
} else {
|
||||
return board.prgram.read((bank << 13) | addr);
|
||||
}
|
||||
} else {
|
||||
if(rom) {
|
||||
board.prgrom.write((bank << 13) | addr, data);
|
||||
} else {
|
||||
if(prgram_write_protect[0] == 2 && prgram_write_protect[1] == 1) {
|
||||
board.prgram.write((bank << 13) | addr, data);
|
||||
}
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if((addr & 0xfc00) == 0x5c00) {
|
||||
if(exram_mode >= 2) return exram[addr & 0x03ff];
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
if(addr >= 0x6000) {
|
||||
return prg_access(0, addr);
|
||||
}
|
||||
|
||||
switch(addr) {
|
||||
case 0x5204: {
|
||||
uint8 result = (irq_pending << 7) | (in_frame << 6);
|
||||
irq_pending = false;
|
||||
return result;
|
||||
}
|
||||
case 0x5205: return (multiplier * multiplicand) >> 0;
|
||||
case 0x5206: return (multiplier * multiplicand) >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if((addr & 0xfc00) == 0x5c00) {
|
||||
//writes 0x00 *during* Vblank (not during screen rendering ...)
|
||||
if(exram_mode == 0 || exram_mode == 1) exram[addr & 0x03ff] = in_frame ? data : 0x00;
|
||||
if(exram_mode == 2) exram[addr & 0x03ff] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr >= 0x6000) {
|
||||
prg_access(1, addr, data);
|
||||
return;
|
||||
}
|
||||
|
||||
switch(addr) {
|
||||
case 0x2000:
|
||||
sprite_8x16 = data & 0x20;
|
||||
break;
|
||||
|
||||
case 0x2001:
|
||||
//if BG+sprites are disabled; enter video blanking period
|
||||
if((data & 0x18) == 0) blank();
|
||||
break;
|
||||
|
||||
case 0x5100: prg_mode = data & 3; break;
|
||||
case 0x5101: chr_mode = data & 3; break;
|
||||
|
||||
case 0x5102: prgram_write_protect[0] = data & 3; break;
|
||||
case 0x5103: prgram_write_protect[1] = data & 3; break;
|
||||
|
||||
case 0x5104:
|
||||
exram_mode = data & 3;
|
||||
break;
|
||||
|
||||
case 0x5105:
|
||||
nametable_mode[0] = (data & 0x03) >> 0;
|
||||
nametable_mode[1] = (data & 0x0c) >> 2;
|
||||
nametable_mode[2] = (data & 0x30) >> 4;
|
||||
nametable_mode[3] = (data & 0xc0) >> 6;
|
||||
break;
|
||||
|
||||
case 0x5106:
|
||||
fillmode_tile = data;
|
||||
break;
|
||||
|
||||
case 0x5107:
|
||||
fillmode_color = data & 3;
|
||||
fillmode_color |= fillmode_color << 2;
|
||||
fillmode_color |= fillmode_color << 4;
|
||||
break;
|
||||
|
||||
case 0x5113:
|
||||
ram_select = data & 0x04;
|
||||
ram_bank = data & 0x03;
|
||||
break;
|
||||
|
||||
case 0x5114: prg_bank[0] = data; break;
|
||||
case 0x5115: prg_bank[1] = data; break;
|
||||
case 0x5116: prg_bank[2] = data; break;
|
||||
case 0x5117: prg_bank[3] = data | 0x80; break;
|
||||
|
||||
case 0x5120: chr_sprite_bank[0] = (chr_bank_hi << 8) | data; chr_active = 0; break;
|
||||
case 0x5121: chr_sprite_bank[1] = (chr_bank_hi << 8) | data; chr_active = 0; break;
|
||||
case 0x5122: chr_sprite_bank[2] = (chr_bank_hi << 8) | data; chr_active = 0; break;
|
||||
case 0x5123: chr_sprite_bank[3] = (chr_bank_hi << 8) | data; chr_active = 0; break;
|
||||
case 0x5124: chr_sprite_bank[4] = (chr_bank_hi << 8) | data; chr_active = 0; break;
|
||||
case 0x5125: chr_sprite_bank[5] = (chr_bank_hi << 8) | data; chr_active = 0; break;
|
||||
case 0x5126: chr_sprite_bank[6] = (chr_bank_hi << 8) | data; chr_active = 0; break;
|
||||
case 0x5127: chr_sprite_bank[7] = (chr_bank_hi << 8) | data; chr_active = 0; break;
|
||||
|
||||
case 0x5128: chr_bg_bank[0] = (chr_bank_hi << 8) | data; chr_active = 1; break;
|
||||
case 0x5129: chr_bg_bank[1] = (chr_bank_hi << 8) | data; chr_active = 1; break;
|
||||
case 0x512a: chr_bg_bank[2] = (chr_bank_hi << 8) | data; chr_active = 1; break;
|
||||
case 0x512b: chr_bg_bank[3] = (chr_bank_hi << 8) | data; chr_active = 1; break;
|
||||
|
||||
case 0x5130:
|
||||
chr_bank_hi = data & 3;
|
||||
break;
|
||||
|
||||
case 0x5200:
|
||||
vs_enable = data & 0x80;
|
||||
vs_side = data & 0x40;
|
||||
vs_tile = data & 0x1f;
|
||||
break;
|
||||
|
||||
case 0x5201:
|
||||
vs_scroll = data;
|
||||
break;
|
||||
|
||||
case 0x5202:
|
||||
vs_bank = data;
|
||||
break;
|
||||
|
||||
case 0x5203:
|
||||
irq_line = data;
|
||||
break;
|
||||
|
||||
case 0x5204:
|
||||
irq_enable = data & 0x80;
|
||||
break;
|
||||
|
||||
case 0x5205:
|
||||
multiplicand = data;
|
||||
break;
|
||||
|
||||
case 0x5206:
|
||||
multiplier = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned chr_sprite_addr(unsigned addr) {
|
||||
if(chr_mode == 0) {
|
||||
auto bank = chr_sprite_bank[7];
|
||||
return (bank * 0x2000) + (addr & 0x1fff);
|
||||
}
|
||||
|
||||
if(chr_mode == 1) {
|
||||
auto bank = chr_sprite_bank[(addr / 0x1000) * 4 + 3];
|
||||
return (bank * 0x1000) + (addr & 0x0fff);
|
||||
}
|
||||
|
||||
if(chr_mode == 2) {
|
||||
auto bank = chr_sprite_bank[(addr / 0x0800) * 2 + 1];
|
||||
return (bank * 0x0800) + (addr & 0x07ff);
|
||||
}
|
||||
|
||||
if(chr_mode == 3) {
|
||||
auto bank = chr_sprite_bank[(addr / 0x0400)];
|
||||
return (bank * 0x0400) + (addr & 0x03ff);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned chr_bg_addr(unsigned addr) {
|
||||
addr &= 0x0fff;
|
||||
|
||||
if(chr_mode == 0) {
|
||||
auto bank = chr_bg_bank[3];
|
||||
return (bank * 0x2000) + (addr & 0x0fff);
|
||||
}
|
||||
|
||||
if(chr_mode == 1) {
|
||||
auto bank = chr_bg_bank[3];
|
||||
return (bank * 0x1000) + (addr & 0x0fff);
|
||||
}
|
||||
|
||||
if(chr_mode == 2) {
|
||||
auto bank = chr_bg_bank[(addr / 0x0800) * 2 + 1];
|
||||
return (bank * 0x0800) + (addr & 0x07ff);
|
||||
}
|
||||
|
||||
if(chr_mode == 3) {
|
||||
auto bank = chr_bg_bank[(addr / 0x0400)];
|
||||
return (bank * 0x0400) + (addr & 0x03ff);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned chr_vs_addr(unsigned addr) {
|
||||
return (vs_bank * 0x1000) + (addr & 0x0ff8) + (vs_vpos & 7);
|
||||
}
|
||||
|
||||
void blank() {
|
||||
in_frame = false;
|
||||
}
|
||||
|
||||
void scanline() {
|
||||
hcounter = 0;
|
||||
|
||||
if(in_frame == false) {
|
||||
in_frame = true;
|
||||
irq_pending = false;
|
||||
vcounter = 0;
|
||||
} else {
|
||||
if(vcounter == irq_line) irq_pending = true;
|
||||
vcounter++;
|
||||
}
|
||||
|
||||
cpu_cycle_counter = 0;
|
||||
}
|
||||
|
||||
uint8 ciram_read(unsigned addr) {
|
||||
if(vs_fetch && (hcounter & 2) == 0) return exram[vs_vpos / 8 * 32 + vs_hpos / 8];
|
||||
if(vs_fetch && (hcounter & 2) != 0) return exram[vs_vpos / 32 * 8 + vs_hpos / 32 + 0x03c0];
|
||||
|
||||
switch(nametable_mode[(addr >> 10) & 3]) {
|
||||
case 0: return ppu.ciram_read(0x0000 | (addr & 0x03ff));
|
||||
case 1: return ppu.ciram_read(0x0400 | (addr & 0x03ff));
|
||||
case 2: return exram_mode < 2 ? exram[addr & 0x03ff] : 0x00;
|
||||
case 3: return (hcounter & 2) == 0 ? fillmode_tile : fillmode_color;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
chr_access[0] = chr_access[1];
|
||||
chr_access[1] = chr_access[2];
|
||||
chr_access[2] = chr_access[3];
|
||||
chr_access[3] = addr;
|
||||
|
||||
//detect two unused nametable fetches at end of each scanline
|
||||
if((chr_access[0] & 0x2000) == 0
|
||||
&& (chr_access[1] & 0x2000)
|
||||
&& (chr_access[2] & 0x2000)
|
||||
&& (chr_access[3] & 0x2000)) scanline();
|
||||
|
||||
if(in_frame == false) {
|
||||
vs_fetch = false;
|
||||
if(addr & 0x2000) return ciram_read(addr);
|
||||
return board.chrrom.read(chr_active ? chr_bg_addr(addr) : chr_sprite_addr(addr));
|
||||
}
|
||||
|
||||
bool bg_fetch = (hcounter < 256 || hcounter >= 320);
|
||||
uint8 result = 0x00;
|
||||
|
||||
if((hcounter & 7) == 0) {
|
||||
vs_hpos = hcounter >= 320 ? hcounter - 320 : hcounter + 16;
|
||||
vs_vpos = vcounter + vs_scroll;
|
||||
vs_fetch = vs_enable && bg_fetch && exram_mode < 2
|
||||
&& (vs_side ? vs_hpos / 8 >= vs_tile : vs_hpos / 8 < vs_tile);
|
||||
if(vs_vpos >= 240) vs_vpos -= 240;
|
||||
|
||||
result = ciram_read(addr);
|
||||
|
||||
exbank = (chr_bank_hi << 6) | (exram[addr & 0x03ff] & 0x3f);
|
||||
exattr = exram[addr & 0x03ff] >> 6;
|
||||
exattr |= exattr << 2;
|
||||
exattr |= exattr << 4;
|
||||
} else if((hcounter & 7) == 2) {
|
||||
result = ciram_read(addr);
|
||||
if(bg_fetch && exram_mode == 1) result = exattr;
|
||||
} else {
|
||||
if(vs_fetch) result = board.chrrom.read(chr_vs_addr(addr));
|
||||
else if(sprite_8x16 ? bg_fetch : chr_active) result = board.chrrom.read(chr_bg_addr(addr));
|
||||
else result = board.chrrom.read(chr_sprite_addr(addr));
|
||||
if(bg_fetch && exram_mode == 1) result = board.chrrom.read(exbank * 0x1000 + (addr & 0x0fff));
|
||||
}
|
||||
|
||||
hcounter += 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) {
|
||||
switch(nametable_mode[(addr >> 10) & 3]) {
|
||||
case 0: return ppu.ciram_write(0x0000 | (addr & 0x03ff), data);
|
||||
case 1: return ppu.ciram_write(0x0400 | (addr & 0x03ff), data);
|
||||
case 2: exram[addr & 0x03ff] = data; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void power() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
for(auto& n : exram) n = 0xff;
|
||||
|
||||
prg_mode = 3;
|
||||
chr_mode = 0;
|
||||
for(auto& n : prgram_write_protect) n = 0;
|
||||
exram_mode = 0;
|
||||
for(auto& n : nametable_mode) n = 0;
|
||||
fillmode_tile = 0;
|
||||
fillmode_color = 0;
|
||||
ram_select = 0;
|
||||
ram_bank = 0;
|
||||
prg_bank[0] = 0x00;
|
||||
prg_bank[1] = 0x00;
|
||||
prg_bank[2] = 0x00;
|
||||
prg_bank[3] = 0xff;
|
||||
for(auto& n : chr_sprite_bank) n = 0;
|
||||
for(auto& n : chr_bg_bank) n = 0;
|
||||
chr_bank_hi = 0;
|
||||
vs_enable = 0;
|
||||
vs_side = 0;
|
||||
vs_tile = 0;
|
||||
vs_scroll = 0;
|
||||
vs_bank = 0;
|
||||
irq_line = 0;
|
||||
irq_enable = 0;
|
||||
multiplicand = 0;
|
||||
multiplier = 0;
|
||||
|
||||
cpu_cycle_counter = 0;
|
||||
irq_counter = 0;
|
||||
irq_pending = 0;
|
||||
in_frame = 0;
|
||||
vcounter = 0;
|
||||
hcounter = 0;
|
||||
for(auto& n : chr_access) n = 0;
|
||||
chr_active = 0;
|
||||
sprite_8x16 = 0;
|
||||
|
||||
exbank = 0;
|
||||
exattr = 0;
|
||||
|
||||
vs_fetch = 0;
|
||||
vs_vpos = 0;
|
||||
vs_hpos = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
s.array(exram);
|
||||
|
||||
s.integer(prg_mode);
|
||||
s.integer(chr_mode);
|
||||
for(auto& n : prgram_write_protect) s.integer(n);
|
||||
s.integer(exram_mode);
|
||||
for(auto& n : nametable_mode) s.integer(n);
|
||||
s.integer(fillmode_tile);
|
||||
s.integer(fillmode_color);
|
||||
s.integer(ram_select);
|
||||
s.integer(ram_bank);
|
||||
for(auto& n : prg_bank) s.integer(n);
|
||||
for(auto& n : chr_sprite_bank) s.integer(n);
|
||||
for(auto& n : chr_bg_bank) s.integer(n);
|
||||
s.integer(chr_bank_hi);
|
||||
s.integer(vs_enable);
|
||||
s.integer(vs_side);
|
||||
s.integer(vs_tile);
|
||||
s.integer(vs_scroll);
|
||||
s.integer(vs_bank);
|
||||
s.integer(irq_line);
|
||||
s.integer(irq_enable);
|
||||
s.integer(multiplicand);
|
||||
s.integer(multiplier);
|
||||
|
||||
s.integer(cpu_cycle_counter);
|
||||
s.integer(irq_counter);
|
||||
s.integer(irq_pending);
|
||||
s.integer(in_frame);
|
||||
|
||||
s.integer(vcounter);
|
||||
s.integer(hcounter);
|
||||
for(auto& n : chr_access) s.integer(n);
|
||||
s.integer(chr_active);
|
||||
s.integer(sprite_8x16);
|
||||
|
||||
s.integer(exbank);
|
||||
s.integer(exattr);
|
||||
|
||||
s.integer(vs_fetch);
|
||||
s.integer(vs_vpos);
|
||||
s.integer(vs_hpos);
|
||||
}
|
||||
|
||||
MMC5(Board& board) : Chip(board) {
|
||||
revision = Revision::MMC5;
|
||||
}
|
||||
|
||||
};
|
@@ -1,200 +0,0 @@
|
||||
struct MMC6 : Chip {
|
||||
|
||||
bool chr_mode;
|
||||
bool prg_mode;
|
||||
bool ram_enable;
|
||||
uint3 bank_select;
|
||||
uint8 prg_bank[2];
|
||||
uint8 chr_bank[6];
|
||||
bool mirror;
|
||||
bool ram_readable[2];
|
||||
bool ram_writable[2];
|
||||
uint8 irq_latch;
|
||||
uint8 irq_counter;
|
||||
bool irq_enable;
|
||||
unsigned irq_delay;
|
||||
bool irq_line;
|
||||
|
||||
uint16 chr_abus;
|
||||
|
||||
void main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_delay) irq_delay--;
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
void irq_test(unsigned addr) {
|
||||
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
|
||||
if(irq_delay == 0) {
|
||||
if(irq_counter == 0) {
|
||||
irq_counter = irq_latch;
|
||||
} else if(--irq_counter == 0) {
|
||||
if(irq_enable) irq_line = 1;
|
||||
}
|
||||
}
|
||||
irq_delay = 6;
|
||||
}
|
||||
chr_abus = addr;
|
||||
}
|
||||
|
||||
unsigned prg_addr(unsigned addr) const {
|
||||
switch((addr >> 13) & 3) {
|
||||
case 0:
|
||||
if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff);
|
||||
return (prg_bank[0] << 13) | (addr & 0x1fff);
|
||||
case 1:
|
||||
return (prg_bank[1] << 13) | (addr & 0x1fff);
|
||||
case 2:
|
||||
if(prg_mode == 0) return (0x3e << 13) | (addr & 0x1fff);
|
||||
return (prg_bank[0] << 13) | (addr & 0x1fff);
|
||||
case 3:
|
||||
return (0x3f << 13) | (addr & 0x1fff);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned chr_addr(unsigned addr) const {
|
||||
if(chr_mode == 0) {
|
||||
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
||||
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
||||
if(addr <= 0x13ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x17ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x1bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x1fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
|
||||
} else {
|
||||
if(addr <= 0x03ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x07ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x0bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x0fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
||||
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const {
|
||||
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
}
|
||||
|
||||
uint8 ram_read(unsigned addr) {
|
||||
if(ram_enable == false) return cpu.mdr();
|
||||
if(ram_readable[0] == false && ram_readable[1] == false) return cpu.mdr();
|
||||
bool region = addr & 0x0200;
|
||||
if(ram_readable[region] == false) return 0x00;
|
||||
return board.prgram.read((region * 0x0200) + (addr & 0x01ff));
|
||||
}
|
||||
|
||||
void ram_write(unsigned addr, uint8 data) {
|
||||
if(ram_enable == false) return;
|
||||
bool region = addr & 0x0200;
|
||||
if(ram_writable[region] == false) return;
|
||||
return board.prgram.write((region * 0x0200) + (addr & 0x01ff), data);
|
||||
}
|
||||
|
||||
void reg_write(unsigned addr, uint8 data) {
|
||||
switch(addr & 0xe001) {
|
||||
case 0x8000:
|
||||
chr_mode = data & 0x80;
|
||||
prg_mode = data & 0x40;
|
||||
ram_enable = data & 0x20;
|
||||
bank_select = data & 0x07;
|
||||
if(ram_enable == false) {
|
||||
for(auto &n : ram_readable) n = false;
|
||||
for(auto &n : ram_writable) n = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x8001:
|
||||
switch(bank_select) {
|
||||
case 0: chr_bank[0] = data & ~1; break;
|
||||
case 1: chr_bank[1] = data & ~1; break;
|
||||
case 2: chr_bank[2] = data; break;
|
||||
case 3: chr_bank[3] = data; break;
|
||||
case 4: chr_bank[4] = data; break;
|
||||
case 5: chr_bank[5] = data; break;
|
||||
case 6: prg_bank[0] = data & 0x3f; break;
|
||||
case 7: prg_bank[1] = data & 0x3f; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xa000:
|
||||
mirror = data & 0x01;
|
||||
break;
|
||||
|
||||
case 0xa001:
|
||||
if(ram_enable == false) break;
|
||||
ram_readable[1] = data & 0x80;
|
||||
ram_writable[1] = data & 0x40;
|
||||
ram_readable[0] = data & 0x20;
|
||||
ram_writable[0] = data & 0x10;
|
||||
break;
|
||||
|
||||
case 0xc000:
|
||||
irq_latch = data;
|
||||
break;
|
||||
|
||||
case 0xc001:
|
||||
irq_counter = 0;
|
||||
break;
|
||||
|
||||
case 0xe000:
|
||||
irq_enable = false;
|
||||
irq_line = 0;
|
||||
break;
|
||||
|
||||
case 0xe001:
|
||||
irq_enable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void power() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
chr_mode = 0;
|
||||
prg_mode = 0;
|
||||
ram_enable = 0;
|
||||
bank_select = 0;
|
||||
for(auto& n : prg_bank) n = 0;
|
||||
for(auto& n : chr_bank) n = 0;
|
||||
mirror = 0;
|
||||
for(auto& n : ram_readable) n = 0;
|
||||
for(auto& n : ram_writable) n = 0;
|
||||
irq_latch = 0;
|
||||
irq_counter = 0;
|
||||
irq_enable = 0;
|
||||
irq_delay = 0;
|
||||
irq_line = 0;
|
||||
|
||||
chr_abus = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
s.integer(chr_mode);
|
||||
s.integer(prg_mode);
|
||||
s.integer(ram_enable);
|
||||
s.integer(bank_select);
|
||||
for(auto& n : prg_bank) s.integer(n);
|
||||
for(auto& n : chr_bank) s.integer(n);
|
||||
s.integer(mirror);
|
||||
for(auto& n : ram_readable) s.integer(n);
|
||||
for(auto& n : ram_writable) s.integer(n);
|
||||
s.integer(irq_latch);
|
||||
s.integer(irq_counter);
|
||||
s.integer(irq_enable);
|
||||
s.integer(irq_delay);
|
||||
s.integer(irq_line);
|
||||
|
||||
s.integer(chr_abus);
|
||||
}
|
||||
|
||||
MMC6(Board& board) : Chip(board) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,80 +0,0 @@
|
||||
struct VRC1 : Chip {
|
||||
|
||||
uint4 prg_bank[3];
|
||||
uint4 chr_banklo[2];
|
||||
bool chr_bankhi[2];
|
||||
bool mirror;
|
||||
|
||||
unsigned prg_addr(unsigned addr) const {
|
||||
unsigned bank = 0x0f;
|
||||
if((addr & 0xe000) == 0x8000) bank = prg_bank[0];
|
||||
if((addr & 0xe000) == 0xa000) bank = prg_bank[1];
|
||||
if((addr & 0xe000) == 0xc000) bank = prg_bank[2];
|
||||
return (bank * 0x2000) + (addr & 0x1fff);
|
||||
}
|
||||
|
||||
unsigned chr_addr(unsigned addr) const {
|
||||
unsigned bank = chr_banklo[(bool)(addr & 0x1000)];
|
||||
bank |= chr_bankhi[(bool)(addr & 0x1000)] << 4;
|
||||
return (bank * 0x1000) + (addr & 0x0fff);
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const {
|
||||
switch(mirror) {
|
||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
void reg_write(unsigned addr, uint8 data) {
|
||||
switch(addr & 0xf000) {
|
||||
case 0x8000:
|
||||
prg_bank[0] = data & 0x0f;
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
chr_bankhi[1] = data & 0x04;
|
||||
chr_bankhi[0] = data & 0x02;
|
||||
mirror = data & 0x01;
|
||||
break;
|
||||
|
||||
case 0xa000:
|
||||
prg_bank[1] = data & 0x0f;
|
||||
break;
|
||||
|
||||
case 0xc000:
|
||||
prg_bank[2] = data & 0x0f;
|
||||
break;
|
||||
|
||||
case 0xe000:
|
||||
chr_banklo[0] = data & 0x0f;
|
||||
break;
|
||||
|
||||
case 0xf000:
|
||||
chr_banklo[1] = data & 0x0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void power() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
for(auto& n : prg_bank) n = 0;
|
||||
for(auto& n : chr_banklo) n = 0;
|
||||
for(auto& n : chr_bankhi) n = 0;
|
||||
mirror = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
for(auto& n : prg_bank) s.integer(n);
|
||||
for(auto& n : chr_banklo) s.integer(n);
|
||||
for(auto& n : chr_bankhi) s.integer(n);
|
||||
s.integer(mirror);
|
||||
}
|
||||
|
||||
VRC1(Board& board) : Chip(board) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,110 +0,0 @@
|
||||
struct VRC2 : Chip {
|
||||
|
||||
uint5 prg_bank[2];
|
||||
uint8 chr_bank[8];
|
||||
uint2 mirror;
|
||||
bool latch;
|
||||
|
||||
unsigned prg_addr(unsigned addr) const {
|
||||
unsigned bank;
|
||||
switch(addr & 0xe000) {
|
||||
case 0x8000: bank = prg_bank[0]; break;
|
||||
case 0xa000: bank = prg_bank[1]; break;
|
||||
case 0xc000: bank = 0x1e; break;
|
||||
case 0xe000: bank = 0x1f; break;
|
||||
}
|
||||
return (bank * 0x2000) + (addr & 0x1fff);
|
||||
}
|
||||
|
||||
unsigned chr_addr(unsigned addr) const {
|
||||
unsigned bank = chr_bank[addr / 0x0400];
|
||||
return (bank * 0x0400) + (addr & 0x03ff);
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const {
|
||||
switch(mirror) {
|
||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
||||
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
uint8 ram_read(unsigned addr) {
|
||||
if(board.prgram.size == 0) {
|
||||
if((addr & 0xf000) == 0x6000) return cpu.mdr() | latch;
|
||||
return cpu.mdr();
|
||||
}
|
||||
return board.prgram.read(addr & 0x1fff);
|
||||
}
|
||||
|
||||
void ram_write(unsigned addr, uint8 data) {
|
||||
if(board.prgram.size == 0) {
|
||||
if((addr & 0xf000) == 0x6000) latch = data & 0x01;
|
||||
return;
|
||||
}
|
||||
return board.prgram.write(addr & 0x1fff, data);
|
||||
}
|
||||
|
||||
void reg_write(unsigned addr, uint8 data) {
|
||||
switch(addr) {
|
||||
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
||||
prg_bank[0] = data & 0x1f;
|
||||
break;
|
||||
|
||||
case 0x9000: case 0x9001: case 0x9002: case 0x9003:
|
||||
mirror = data & 0x03;
|
||||
break;
|
||||
|
||||
case 0xa000: case 0xa001: case 0xa002: case 0xa003:
|
||||
prg_bank[1] = data & 0x1f;
|
||||
break;
|
||||
|
||||
case 0xb000: chr_bank[0] = (chr_bank[0] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xb001: chr_bank[0] = (chr_bank[0] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xb002: chr_bank[1] = (chr_bank[1] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xb003: chr_bank[1] = (chr_bank[1] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xc000: chr_bank[2] = (chr_bank[2] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xc001: chr_bank[2] = (chr_bank[2] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xc002: chr_bank[3] = (chr_bank[3] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xc003: chr_bank[3] = (chr_bank[3] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xd000: chr_bank[4] = (chr_bank[4] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xd001: chr_bank[4] = (chr_bank[4] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xd002: chr_bank[5] = (chr_bank[5] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xd003: chr_bank[5] = (chr_bank[5] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xe000: chr_bank[6] = (chr_bank[6] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xe001: chr_bank[6] = (chr_bank[6] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xe002: chr_bank[7] = (chr_bank[7] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xe003: chr_bank[7] = (chr_bank[7] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
}
|
||||
}
|
||||
|
||||
void power() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
for(auto& n : prg_bank) n = 0;
|
||||
for(auto& n : chr_bank) n = 0;
|
||||
mirror = 0;
|
||||
latch = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
for(auto& n : prg_bank) s.integer(n);
|
||||
for(auto& n : chr_bank) s.integer(n);
|
||||
s.integer(mirror);
|
||||
s.integer(latch);
|
||||
}
|
||||
|
||||
VRC2(Board& board) : Chip(board) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,100 +0,0 @@
|
||||
struct VRC3 : Chip {
|
||||
|
||||
uint4 prg_bank;
|
||||
bool irq_mode;
|
||||
bool irq_enable;
|
||||
bool irq_acknowledge;
|
||||
uint16 irq_latch;
|
||||
struct {
|
||||
union {
|
||||
uint16 w;
|
||||
struct { uint8 order_lsb2(l, h); };
|
||||
};
|
||||
} irq_counter;
|
||||
bool irq_line;
|
||||
|
||||
void main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_enable) {
|
||||
if(irq_mode == 0) { //16-bit
|
||||
if(++irq_counter.w == 0) {
|
||||
irq_line = 1;
|
||||
irq_enable = irq_acknowledge;
|
||||
irq_counter.w = irq_latch;
|
||||
}
|
||||
}
|
||||
if(irq_mode == 1) { //8-bit
|
||||
if(++irq_counter.l == 0) {
|
||||
irq_line = 1;
|
||||
irq_enable = irq_acknowledge;
|
||||
irq_counter.l = irq_latch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned prg_addr(unsigned addr) const {
|
||||
unsigned bank = (addr < 0xc000 ? (unsigned)prg_bank : 0x0f);
|
||||
return (bank * 0x4000) + (addr & 0x3fff);
|
||||
}
|
||||
|
||||
void reg_write(unsigned addr, uint8 data) {
|
||||
switch(addr & 0xf000) {
|
||||
case 0x8000: irq_latch = (irq_latch & 0xfff0) | ((data & 0x0f) << 0); break;
|
||||
case 0x9000: irq_latch = (irq_latch & 0xff0f) | ((data & 0x0f) << 4); break;
|
||||
case 0xa000: irq_latch = (irq_latch & 0xf0ff) | ((data & 0x0f) << 8); break;
|
||||
case 0xb000: irq_latch = (irq_latch & 0x0fff) | ((data & 0x0f) << 12); break;
|
||||
|
||||
case 0xc000:
|
||||
irq_mode = data & 0x04;
|
||||
irq_enable = data & 0x02;
|
||||
irq_acknowledge = data & 0x01;
|
||||
if(irq_enable) irq_counter.w = irq_latch;
|
||||
break;
|
||||
|
||||
case 0xd000:
|
||||
irq_line = 0;
|
||||
irq_enable = irq_acknowledge;
|
||||
break;
|
||||
|
||||
case 0xf000:
|
||||
prg_bank = data & 0x0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void power() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
prg_bank = 0;
|
||||
irq_mode = 0;
|
||||
irq_enable = 0;
|
||||
irq_acknowledge = 0;
|
||||
irq_latch = 0;
|
||||
irq_counter.w = 0;
|
||||
irq_line = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
s.integer(prg_bank);
|
||||
s.integer(irq_mode);
|
||||
s.integer(irq_enable);
|
||||
s.integer(irq_acknowledge);
|
||||
s.integer(irq_latch);
|
||||
s.integer(irq_counter.w);
|
||||
s.integer(irq_line);
|
||||
}
|
||||
|
||||
VRC3(Board& board) : Chip(board) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,184 +0,0 @@
|
||||
struct VRC4 : Chip {
|
||||
|
||||
bool prg_mode;
|
||||
uint5 prg_bank[2];
|
||||
uint2 mirror;
|
||||
uint8 chr_bank[8];
|
||||
|
||||
uint8 irq_latch;
|
||||
bool irq_mode;
|
||||
bool irq_enable;
|
||||
bool irq_acknowledge;
|
||||
|
||||
uint8 irq_counter;
|
||||
signed irq_scalar;
|
||||
bool irq_line;
|
||||
|
||||
void main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_enable) {
|
||||
if(irq_mode == 0) {
|
||||
irq_scalar -= 3;
|
||||
if(irq_scalar <= 0) {
|
||||
irq_scalar += 341;
|
||||
if(irq_counter == 0xff) {
|
||||
irq_counter = irq_latch;
|
||||
irq_line = 1;
|
||||
} else {
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(irq_mode == 1) {
|
||||
if(irq_counter == 0xff) {
|
||||
irq_counter = irq_latch;
|
||||
irq_line = 1;
|
||||
} else {
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned prg_addr(unsigned addr) const {
|
||||
unsigned bank = 0, banks = board.prgrom.size / 0x2000;
|
||||
switch(addr & 0xe000) {
|
||||
case 0x8000: bank = prg_mode == 0 ? (unsigned)prg_bank[0] : banks - 2; break;
|
||||
case 0xa000: bank = prg_bank[1]; break;
|
||||
case 0xc000: bank = prg_mode == 0 ? banks - 2 : (unsigned)prg_bank[0]; break;
|
||||
case 0xe000: bank = banks - 1; break;
|
||||
}
|
||||
return (bank * 0x2000) + (addr & 0x1fff);
|
||||
}
|
||||
|
||||
unsigned chr_addr(unsigned addr) const {
|
||||
unsigned bank = chr_bank[addr / 0x0400];
|
||||
return (bank * 0x0400) + (addr & 0x03ff);
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const {
|
||||
switch(mirror) {
|
||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
||||
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
void reg_write(unsigned addr, uint8 data) {
|
||||
switch(addr) {
|
||||
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
||||
prg_bank[0] = data & 0x1f;
|
||||
break;
|
||||
|
||||
case 0x9000: case 0x9001:
|
||||
mirror = data & 0x03;
|
||||
break;
|
||||
|
||||
case 0x9002: case 0x9003:
|
||||
prg_mode = data & 0x02;
|
||||
break;
|
||||
|
||||
case 0xa000: case 0xa001: case 0xa002: case 0xa003:
|
||||
prg_bank[1] = data & 0x1f;
|
||||
break;
|
||||
|
||||
case 0xb000: chr_bank[0] = (chr_bank[0] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xb001: chr_bank[0] = (chr_bank[0] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xb002: chr_bank[1] = (chr_bank[1] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xb003: chr_bank[1] = (chr_bank[1] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xc000: chr_bank[2] = (chr_bank[2] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xc001: chr_bank[2] = (chr_bank[2] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xc002: chr_bank[3] = (chr_bank[3] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xc003: chr_bank[3] = (chr_bank[3] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xd000: chr_bank[4] = (chr_bank[4] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xd001: chr_bank[4] = (chr_bank[4] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xd002: chr_bank[5] = (chr_bank[5] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xd003: chr_bank[5] = (chr_bank[5] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xe000: chr_bank[6] = (chr_bank[6] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xe001: chr_bank[6] = (chr_bank[6] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xe002: chr_bank[7] = (chr_bank[7] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||
case 0xe003: chr_bank[7] = (chr_bank[7] & 0x0f) | ((data & 0x0f) << 4); break;
|
||||
|
||||
case 0xf000:
|
||||
irq_latch = (irq_latch & 0xf0) | ((data & 0x0f) << 0);
|
||||
break;
|
||||
|
||||
case 0xf001:
|
||||
irq_latch = (irq_latch & 0x0f) | ((data & 0x0f) << 4);
|
||||
break;
|
||||
|
||||
case 0xf002:
|
||||
irq_mode = data & 0x04;
|
||||
irq_enable = data & 0x02;
|
||||
irq_acknowledge = data & 0x01;
|
||||
if(irq_enable) {
|
||||
irq_counter = irq_latch;
|
||||
irq_scalar = 341;
|
||||
}
|
||||
irq_line = 0;
|
||||
break;
|
||||
|
||||
case 0xf003:
|
||||
irq_enable = irq_acknowledge;
|
||||
irq_line = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void power() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
prg_mode = 0;
|
||||
for(auto& n : prg_bank) n = 0;
|
||||
mirror = 0;
|
||||
for(auto& n : chr_bank) n = 0;
|
||||
|
||||
irq_latch = 0;
|
||||
irq_mode = 0;
|
||||
irq_enable = 0;
|
||||
irq_acknowledge = 0;
|
||||
|
||||
irq_counter = 0;
|
||||
irq_scalar = 0;
|
||||
irq_line = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
s.integer(prg_mode);
|
||||
for(auto& n : prg_bank) s.integer(n);
|
||||
s.integer(mirror);
|
||||
for(auto& n : chr_bank) s.integer(n);
|
||||
|
||||
s.integer(irq_latch);
|
||||
s.integer(irq_mode);
|
||||
s.integer(irq_enable);
|
||||
s.integer(irq_acknowledge);
|
||||
|
||||
s.integer(irq_counter);
|
||||
s.integer(irq_scalar);
|
||||
s.integer(irq_line);
|
||||
}
|
||||
|
||||
VRC4(Board& board) : Chip(board) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,321 +0,0 @@
|
||||
struct VRC6 : Chip {
|
||||
|
||||
uint8 prg_bank[2];
|
||||
uint8 chr_bank[8];
|
||||
uint2 mirror;
|
||||
uint8 irq_latch;
|
||||
bool irq_mode;
|
||||
bool irq_enable;
|
||||
bool irq_acknowledge;
|
||||
|
||||
uint8 irq_counter;
|
||||
signed irq_scalar;
|
||||
bool irq_line;
|
||||
|
||||
struct Pulse {
|
||||
bool mode;
|
||||
uint3 duty;
|
||||
uint4 volume;
|
||||
bool enable;
|
||||
uint12 frequency;
|
||||
|
||||
uint12 divider;
|
||||
uint4 cycle;
|
||||
uint4 output;
|
||||
|
||||
void clock() {
|
||||
if(--divider == 0) {
|
||||
divider = frequency + 1;
|
||||
cycle++;
|
||||
output = (mode == 1 || cycle > duty) ? volume : (uint4)0;
|
||||
}
|
||||
|
||||
if(enable == false) output = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
s.integer(mode);
|
||||
s.integer(duty);
|
||||
s.integer(volume);
|
||||
s.integer(enable);
|
||||
s.integer(frequency);
|
||||
|
||||
s.integer(divider);
|
||||
s.integer(cycle);
|
||||
s.integer(output);
|
||||
}
|
||||
} pulse1, pulse2;
|
||||
|
||||
struct Sawtooth {
|
||||
uint6 rate;
|
||||
bool enable;
|
||||
uint12 frequency;
|
||||
|
||||
uint12 divider;
|
||||
uint1 phase;
|
||||
uint3 stage;
|
||||
uint8 accumulator;
|
||||
uint5 output;
|
||||
|
||||
void clock() {
|
||||
if(--divider == 0) {
|
||||
divider = frequency + 1;
|
||||
if(++phase == 0) {
|
||||
accumulator += rate;
|
||||
if(++stage == 7) {
|
||||
stage = 0;
|
||||
accumulator = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output = accumulator >> 3;
|
||||
if(enable == false) output = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
s.integer(rate);
|
||||
s.integer(enable);
|
||||
s.integer(frequency);
|
||||
|
||||
s.integer(divider);
|
||||
s.integer(phase);
|
||||
s.integer(stage);
|
||||
s.integer(accumulator);
|
||||
s.integer(output);
|
||||
}
|
||||
} sawtooth;
|
||||
|
||||
void main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_enable) {
|
||||
if(irq_mode == 0) {
|
||||
irq_scalar -= 3;
|
||||
if(irq_scalar <= 0) {
|
||||
irq_scalar += 341;
|
||||
if(irq_counter == 0xff) {
|
||||
irq_counter = irq_latch;
|
||||
irq_line = 1;
|
||||
} else {
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(irq_mode == 1) {
|
||||
if(irq_counter == 0xff) {
|
||||
irq_counter = irq_latch;
|
||||
irq_line = 1;
|
||||
} else {
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
cpu.set_irq_line(irq_line);
|
||||
|
||||
pulse1.clock();
|
||||
pulse2.clock();
|
||||
sawtooth.clock();
|
||||
signed output = (pulse1.output + pulse2.output + sawtooth.output) << 7;
|
||||
apu.set_sample(-output);
|
||||
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned prg_addr(unsigned addr) const {
|
||||
if((addr & 0xc000) == 0x8000) return (prg_bank[0] << 14) | (addr & 0x3fff);
|
||||
if((addr & 0xe000) == 0xc000) return (prg_bank[1] << 13) | (addr & 0x1fff);
|
||||
if((addr & 0xe000) == 0xe000) return ( 0xff << 13) | (addr & 0x1fff);
|
||||
}
|
||||
|
||||
unsigned chr_addr(unsigned addr) const {
|
||||
unsigned bank = chr_bank[(addr >> 10) & 7];
|
||||
return (bank << 10) | (addr & 0x03ff);
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const {
|
||||
switch(mirror) {
|
||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
||||
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||
}
|
||||
}
|
||||
|
||||
uint8 ram_read(unsigned addr) {
|
||||
return board.prgram.data[addr & 0x1fff];
|
||||
}
|
||||
|
||||
void ram_write(unsigned addr, uint8 data) {
|
||||
board.prgram.data[addr & 0x1fff] = data;
|
||||
}
|
||||
|
||||
void reg_write(unsigned addr, uint8 data) {
|
||||
switch(addr) {
|
||||
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
||||
prg_bank[0] = data;
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
pulse1.mode = data & 0x80;
|
||||
pulse1.duty = (data & 0x70) >> 4;
|
||||
pulse1.volume = data & 0x0f;
|
||||
break;
|
||||
|
||||
case 0x9001:
|
||||
pulse1.frequency = (pulse1.frequency & 0x0f00) | ((data & 0xff) << 0);
|
||||
break;
|
||||
|
||||
case 0x9002:
|
||||
pulse1.frequency = (pulse1.frequency & 0x00ff) | ((data & 0x0f) << 8);
|
||||
pulse1.enable = data & 0x80;
|
||||
break;
|
||||
|
||||
case 0xa000:
|
||||
pulse2.mode = data & 0x80;
|
||||
pulse2.duty = (data & 0x70) >> 4;
|
||||
pulse2.volume = data & 0x0f;
|
||||
break;
|
||||
|
||||
case 0xa001:
|
||||
pulse2.frequency = (pulse2.frequency & 0x0f00) | ((data & 0xff) << 0);
|
||||
break;
|
||||
|
||||
case 0xa002:
|
||||
pulse2.frequency = (pulse2.frequency & 0x00ff) | ((data & 0x0f) << 8);
|
||||
pulse2.enable = data & 0x80;
|
||||
break;
|
||||
|
||||
case 0xb000:
|
||||
sawtooth.rate = data & 0x3f;
|
||||
break;
|
||||
|
||||
case 0xb001:
|
||||
sawtooth.frequency = (sawtooth.frequency & 0x0f00) | ((data & 0xff) << 0);
|
||||
break;
|
||||
|
||||
case 0xb002:
|
||||
sawtooth.frequency = (sawtooth.frequency & 0x00ff) | ((data & 0x0f) << 8);
|
||||
sawtooth.enable = data & 0x80;
|
||||
break;
|
||||
|
||||
case 0xb003:
|
||||
mirror = (data >> 2) & 3;
|
||||
break;
|
||||
|
||||
case 0xc000: case 0xc001: case 0xc002: case 0xc003:
|
||||
prg_bank[1] = data;
|
||||
break;
|
||||
|
||||
case 0xd000: case 0xd001: case 0xd002: case 0xd003:
|
||||
chr_bank[0 + (addr & 3)] = data;
|
||||
break;
|
||||
|
||||
case 0xe000: case 0xe001: case 0xe002: case 0xe003:
|
||||
chr_bank[4 + (addr & 3)] = data;
|
||||
break;
|
||||
|
||||
case 0xf000:
|
||||
irq_latch = data;
|
||||
break;
|
||||
|
||||
case 0xf001:
|
||||
irq_mode = data & 0x04;
|
||||
irq_enable = data & 0x02;
|
||||
irq_acknowledge = data & 0x01;
|
||||
if(irq_enable) {
|
||||
irq_counter = irq_latch;
|
||||
irq_scalar = 341;
|
||||
}
|
||||
irq_line = 0;
|
||||
break;
|
||||
|
||||
case 0xf002:
|
||||
irq_enable = irq_acknowledge;
|
||||
irq_line = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void power() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
prg_bank[0] = 0;
|
||||
prg_bank[1] = 0;
|
||||
chr_bank[0] = 0;
|
||||
chr_bank[1] = 0;
|
||||
chr_bank[2] = 0;
|
||||
chr_bank[3] = 0;
|
||||
chr_bank[4] = 0;
|
||||
chr_bank[5] = 0;
|
||||
chr_bank[6] = 0;
|
||||
chr_bank[7] = 0;
|
||||
mirror = 0;
|
||||
irq_latch = 0;
|
||||
irq_mode = 0;
|
||||
irq_enable = 0;
|
||||
irq_acknowledge = 0;
|
||||
|
||||
irq_counter = 0;
|
||||
irq_scalar = 0;
|
||||
irq_line = 0;
|
||||
|
||||
pulse1.mode = 0;
|
||||
pulse1.duty = 0;
|
||||
pulse1.volume = 0;
|
||||
pulse1.enable = 0;
|
||||
pulse1.frequency = 0;
|
||||
|
||||
pulse1.divider = 1;
|
||||
pulse1.cycle = 0;
|
||||
pulse1.output = 0;
|
||||
|
||||
pulse2.mode = 0;
|
||||
pulse2.duty = 0;
|
||||
pulse2.volume = 0;
|
||||
pulse2.enable = 0;
|
||||
pulse2.frequency = 0;
|
||||
|
||||
pulse2.divider = 1;
|
||||
pulse2.cycle = 0;
|
||||
pulse2.output = 0;
|
||||
|
||||
sawtooth.rate = 0;
|
||||
sawtooth.enable = 0;
|
||||
sawtooth.frequency = 0;
|
||||
|
||||
sawtooth.divider = 1;
|
||||
sawtooth.phase = 0;
|
||||
sawtooth.stage = 0;
|
||||
sawtooth.accumulator = 0;
|
||||
sawtooth.output = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
pulse1.serialize(s);
|
||||
pulse2.serialize(s);
|
||||
sawtooth.serialize(s);
|
||||
|
||||
s.array(prg_bank);
|
||||
s.array(chr_bank);
|
||||
s.integer(mirror);
|
||||
s.integer(irq_latch);
|
||||
s.integer(irq_mode);
|
||||
s.integer(irq_enable);
|
||||
s.integer(irq_acknowledge);
|
||||
|
||||
s.integer(irq_counter);
|
||||
s.integer(irq_scalar);
|
||||
s.integer(irq_line);
|
||||
}
|
||||
|
||||
VRC6(Board& board) : Chip(board) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,154 +0,0 @@
|
||||
//Konami VRC7
|
||||
//Yamaha YM2413 OPLL audio - not emulated
|
||||
|
||||
struct VRC7 : Chip {
|
||||
|
||||
uint8 prg_bank[3];
|
||||
uint8 chr_bank[8];
|
||||
uint2 mirror;
|
||||
|
||||
uint8 irq_latch;
|
||||
bool irq_mode;
|
||||
bool irq_enable;
|
||||
bool irq_acknowledge;
|
||||
|
||||
uint8 irq_counter;
|
||||
signed irq_scalar;
|
||||
bool irq_line;
|
||||
|
||||
void main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_enable) {
|
||||
if(irq_mode == 0) {
|
||||
irq_scalar -= 3;
|
||||
if(irq_scalar <= 0) {
|
||||
irq_scalar += 341;
|
||||
if(irq_counter == 0xff) {
|
||||
irq_counter = irq_latch;
|
||||
irq_line = 1;
|
||||
} else {
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(irq_mode == 1) {
|
||||
if(irq_counter == 0xff) {
|
||||
irq_counter = irq_latch;
|
||||
irq_line = 1;
|
||||
} else {
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
cpu.set_irq_line(irq_line);
|
||||
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
void reg_write(unsigned addr, uint8 data) {
|
||||
switch(addr) {
|
||||
case 0x8000: prg_bank[0] = data; break;
|
||||
case 0x8010: prg_bank[1] = data; break;
|
||||
case 0x9000: prg_bank[2] = data; break;
|
||||
case 0x9010: break; //APU addr port
|
||||
case 0x9030: break; //APU data port
|
||||
case 0xa000: chr_bank[0] = data; break;
|
||||
case 0xa010: chr_bank[1] = data; break;
|
||||
case 0xb000: chr_bank[2] = data; break;
|
||||
case 0xb010: chr_bank[3] = data; break;
|
||||
case 0xc000: chr_bank[4] = data; break;
|
||||
case 0xc010: chr_bank[5] = data; break;
|
||||
case 0xd000: chr_bank[6] = data; break;
|
||||
case 0xd010: chr_bank[7] = data; break;
|
||||
case 0xe000: mirror = data & 0x03; break;
|
||||
|
||||
case 0xe010:
|
||||
irq_latch = data;
|
||||
break;
|
||||
|
||||
case 0xf000:
|
||||
irq_mode = data & 0x04;
|
||||
irq_enable = data & 0x02;
|
||||
irq_acknowledge = data & 0x01;
|
||||
if(irq_enable) {
|
||||
irq_counter = irq_latch;
|
||||
irq_scalar = 341;
|
||||
}
|
||||
irq_line = 0;
|
||||
break;
|
||||
|
||||
case 0xf010:
|
||||
irq_enable = irq_acknowledge;
|
||||
irq_line = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned prg_addr(unsigned addr) const {
|
||||
unsigned bank = 0;
|
||||
switch(addr & 0xe000) {
|
||||
case 0x8000: bank = prg_bank[0]; break;
|
||||
case 0xa000: bank = prg_bank[1]; break;
|
||||
case 0xc000: bank = prg_bank[2]; break;
|
||||
case 0xe000: bank = 0xff; break;
|
||||
}
|
||||
return (bank * 0x2000) + (addr & 0x1fff);
|
||||
}
|
||||
|
||||
unsigned chr_addr(unsigned addr) const {
|
||||
unsigned bank = chr_bank[addr / 0x0400];
|
||||
return (bank * 0x0400) + (addr & 0x03ff);
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const {
|
||||
switch(mirror) {
|
||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
||||
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||
}
|
||||
}
|
||||
|
||||
void power() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
for(auto& n : prg_bank) n = 0;
|
||||
for(auto& n : chr_bank) n = 0;
|
||||
mirror = 0;
|
||||
|
||||
irq_latch = 0;
|
||||
irq_mode = 0;
|
||||
irq_enable = 0;
|
||||
irq_acknowledge = 0;
|
||||
|
||||
irq_counter = 0;
|
||||
irq_scalar = 0;
|
||||
irq_line = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer& s) {
|
||||
s.array(prg_bank);
|
||||
s.array(chr_bank);
|
||||
s.integer(mirror);
|
||||
|
||||
s.integer(irq_latch);
|
||||
s.integer(irq_mode);
|
||||
s.integer(irq_enable);
|
||||
s.integer(irq_acknowledge);
|
||||
|
||||
s.integer(irq_counter);
|
||||
s.integer(irq_scalar);
|
||||
s.integer(irq_line);
|
||||
}
|
||||
|
||||
VRC7(Board& board) : Chip(board) {
|
||||
}
|
||||
|
||||
};
|
@@ -1,88 +0,0 @@
|
||||
#include <fc/fc.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
|
||||
Cheat cheat;
|
||||
|
||||
bool Cheat::decode(string code_, unsigned& addr, unsigned& data, unsigned& comp) {
|
||||
static bool initialize = false;
|
||||
static uint8 mapProActionReplay[256], mapGameGenie[256];
|
||||
|
||||
if(initialize == false) {
|
||||
initialize = true;
|
||||
|
||||
for(auto& n : mapProActionReplay) n = ~0;
|
||||
mapProActionReplay['0'] = 0; mapProActionReplay['1'] = 1; mapProActionReplay['2'] = 2; mapProActionReplay['3'] = 3;
|
||||
mapProActionReplay['4'] = 4; mapProActionReplay['5'] = 5; mapProActionReplay['6'] = 6; mapProActionReplay['7'] = 7;
|
||||
mapProActionReplay['8'] = 8; mapProActionReplay['9'] = 9; mapProActionReplay['A'] = 10; mapProActionReplay['B'] = 11;
|
||||
mapProActionReplay['C'] = 12; mapProActionReplay['D'] = 13; mapProActionReplay['E'] = 14; mapProActionReplay['F'] = 15;
|
||||
|
||||
for(auto& n : mapGameGenie) n = ~0;
|
||||
mapGameGenie['A'] = 0; mapGameGenie['P'] = 1; mapGameGenie['Z'] = 2; mapGameGenie['L'] = 3;
|
||||
mapGameGenie['G'] = 4; mapGameGenie['I'] = 5; mapGameGenie['T'] = 6; mapGameGenie['Y'] = 7;
|
||||
mapGameGenie['E'] = 8; mapGameGenie['O'] = 9; mapGameGenie['X'] = 10; mapGameGenie['U'] = 11;
|
||||
mapGameGenie['K'] = 12; mapGameGenie['S'] = 13; mapGameGenie['V'] = 14; mapGameGenie['N'] = 15;
|
||||
}
|
||||
|
||||
string code = code_;
|
||||
code.upper();
|
||||
unsigned length = code.length(), bits = 0;
|
||||
|
||||
if(code.match("????:??")) {
|
||||
code = {substr(code, 0, 4), substr(code, 5, 2)};
|
||||
for(unsigned n = 0; n < 6; n++) if(mapProActionReplay[code[n]] > 15) return false;
|
||||
bits = hex(code);
|
||||
addr = (bits >> 8) & 0xffff;
|
||||
data = (bits >> 0) & 0xff;
|
||||
comp = ~0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(code.match("????:??:??")) {
|
||||
code = {substr(code, 0, 4), substr(code, 5, 2), substr(code, 8, 2)};
|
||||
for(unsigned n = 0; n < 8; n++) if(mapProActionReplay[code[n]] > 15) return false;
|
||||
bits = hex(code);
|
||||
addr = (bits >> 16) & 0xffff;
|
||||
data = (bits >> 8) & 0xff;
|
||||
comp = (bits >> 0) & 0xff;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(length == 6) {
|
||||
for(unsigned n = 0; n < 6; n++) if(mapGameGenie[code[n]] > 15) return false;
|
||||
for(unsigned n = 0; n < 6; n++) bits |= mapGameGenie[code[n]] << (20 - n * 4);
|
||||
unsigned addrTable[] = {10, 9, 8, 7, 2, 1, 0, 19, 14, 13, 12, 11, 6, 5, 4};
|
||||
unsigned dataTable[] = {23, 18, 17, 16, 3, 22, 21, 20};
|
||||
|
||||
addr = 0x8000, data = 0x00, comp = ~0;
|
||||
for(unsigned n = 0; n < 15; n++) addr |= bits & (1 << addrTable[n]) ? 0x4000 >> n : 0;
|
||||
for(unsigned n = 0; n < 8; n++) data |= bits & (1 << dataTable[n]) ? 0x80 >> n : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(length == 8) {
|
||||
for(unsigned n = 0; n < 8; n++) if(mapGameGenie[code[n]] > 15) return false;
|
||||
for(unsigned n = 0; n < 8; n++) bits |= mapGameGenie[code[n]] << (28 - n * 4);
|
||||
unsigned addrTable[] = {18, 17, 16, 15, 10, 9, 8, 27, 22, 21, 20, 19, 14, 13, 12};
|
||||
unsigned dataTable[] = {31, 26, 25, 24, 3, 30, 29, 28};
|
||||
unsigned compTable[] = {7, 2, 1, 0, 11, 6, 5, 4};
|
||||
|
||||
addr = 0x8000, data = 0x00, comp = 0x00;
|
||||
for(unsigned n = 0; n < 15; n++) addr |= bits & (1 << addrTable[n]) ? 0x4000 >> n : 0;
|
||||
for(unsigned n = 0; n < 8; n++) data |= bits & (1 << dataTable[n]) ? 0x80 >> n : 0;
|
||||
for(unsigned n = 0; n < 8; n++) comp |= bits & (1 << compTable[n]) ? 0x80 >> n : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Cheat::synchronize() {
|
||||
for(auto& n : override) n = false;
|
||||
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
override[operator[](n).addr] = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
struct CheatCode {
|
||||
unsigned addr;
|
||||
unsigned data;
|
||||
unsigned comp;
|
||||
};
|
||||
|
||||
struct Cheat : public vector<CheatCode> {
|
||||
static bool decode(string code, unsigned& addr, unsigned& data, unsigned& comp);
|
||||
|
||||
void synchronize();
|
||||
bool override[65536];
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
111
fc/cpu/cpu.cpp
111
fc/cpu/cpu.cpp
@@ -1,111 +0,0 @@
|
||||
#include <fc/fc.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
|
||||
#include "timing.cpp"
|
||||
#include "serialization.cpp"
|
||||
CPU cpu;
|
||||
|
||||
void CPU::Enter() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
cpu.main();
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::main() {
|
||||
if(status.interrupt_pending) {
|
||||
interrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
exec();
|
||||
}
|
||||
|
||||
void CPU::add_clocks(unsigned clocks) {
|
||||
apu.clock -= clocks;
|
||||
if(apu.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(apu.thread);
|
||||
|
||||
ppu.clock -= clocks;
|
||||
if(ppu.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(ppu.thread);
|
||||
|
||||
cartridge.clock -= clocks;
|
||||
if(cartridge.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cartridge.thread);
|
||||
}
|
||||
|
||||
void CPU::power() {
|
||||
R6502::power();
|
||||
|
||||
for(unsigned addr = 0; addr < 0x0800; addr++) ram[addr] = 0xff;
|
||||
ram[0x0008] = 0xf7;
|
||||
ram[0x0009] = 0xef;
|
||||
ram[0x000a] = 0xdf;
|
||||
ram[0x000f] = 0xbf;
|
||||
}
|
||||
|
||||
void CPU::reset() {
|
||||
R6502::reset();
|
||||
create(CPU::Enter, 21477272);
|
||||
|
||||
regs.pc = bus.read(0xfffc) << 0;
|
||||
regs.pc |= bus.read(0xfffd) << 8;
|
||||
|
||||
status.interrupt_pending = false;
|
||||
status.nmi_pending = false;
|
||||
status.nmi_line = 0;
|
||||
status.irq_line = 0;
|
||||
status.irq_apu_line = 0;
|
||||
|
||||
status.rdy_line = 1;
|
||||
status.rdy_addr_valid = false;
|
||||
status.rdy_addr_value = 0x0000;
|
||||
|
||||
status.oam_dma_pending = false;
|
||||
status.oam_dma_page = 0x00;
|
||||
|
||||
status.controller_latch = false;
|
||||
status.controller_port0 = 0;
|
||||
status.controller_port1 = 0;
|
||||
}
|
||||
|
||||
uint8 CPU::debugger_read(uint16 addr) {
|
||||
return bus.read(addr);
|
||||
}
|
||||
|
||||
uint8 CPU::ram_read(uint16 addr) {
|
||||
return ram[addr & 0x07ff];
|
||||
}
|
||||
|
||||
void CPU::ram_write(uint16 addr, uint8 data) {
|
||||
ram[addr & 0x07ff] = data;
|
||||
}
|
||||
|
||||
uint8 CPU::read(uint16 addr) {
|
||||
if(addr == 0x4016) {
|
||||
return (mdr() & 0xc0) | input.data(0);
|
||||
}
|
||||
|
||||
if(addr == 0x4017) {
|
||||
return (mdr() & 0xc0) | input.data(1);
|
||||
}
|
||||
|
||||
return apu.read(addr);
|
||||
}
|
||||
|
||||
void CPU::write(uint16 addr, uint8 data) {
|
||||
if(addr == 0x4014) {
|
||||
status.oam_dma_page = data;
|
||||
status.oam_dma_pending = true;
|
||||
}
|
||||
|
||||
if(addr == 0x4016) {
|
||||
input.latch(data & 0x01);
|
||||
}
|
||||
|
||||
return apu.write(addr, data);
|
||||
}
|
||||
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
struct CPU : Processor::R6502, Thread {
|
||||
uint8 ram[0x0800];
|
||||
|
||||
struct Status {
|
||||
bool interrupt_pending;
|
||||
bool nmi_pending;
|
||||
bool nmi_line;
|
||||
bool irq_line;
|
||||
bool irq_apu_line;
|
||||
|
||||
bool rdy_line;
|
||||
bool rdy_addr_valid;
|
||||
uint16 rdy_addr_value;
|
||||
|
||||
bool oam_dma_pending;
|
||||
uint8 oam_dma_page;
|
||||
|
||||
bool controller_latch;
|
||||
unsigned controller_port0;
|
||||
unsigned controller_port1;
|
||||
} status;
|
||||
|
||||
static void Enter();
|
||||
void main();
|
||||
void add_clocks(unsigned clocks);
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
uint8 debugger_read(uint16 addr);
|
||||
|
||||
uint8 ram_read(uint16 addr);
|
||||
void ram_write(uint16 addr, uint8 data);
|
||||
|
||||
uint8 read(uint16 addr);
|
||||
void write(uint16 addr, uint8 data);
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
//timing.cpp
|
||||
uint8 op_read(uint16 addr);
|
||||
void op_write(uint16 addr, uint8 data);
|
||||
void last_cycle();
|
||||
void nmi(uint16 &vector);
|
||||
|
||||
void oam_dma();
|
||||
|
||||
void set_nmi_line(bool);
|
||||
void set_irq_line(bool);
|
||||
void set_irq_apu_line(bool);
|
||||
|
||||
void set_rdy_line(bool);
|
||||
void set_rdy_addr(bool valid, uint16 value = 0);
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
@@ -1,23 +0,0 @@
|
||||
void CPU::serialize(serializer& s) {
|
||||
R6502::serialize(s);
|
||||
Thread::serialize(s);
|
||||
|
||||
s.array(ram);
|
||||
|
||||
s.integer(status.interrupt_pending);
|
||||
s.integer(status.nmi_pending);
|
||||
s.integer(status.nmi_line);
|
||||
s.integer(status.irq_line);
|
||||
s.integer(status.irq_apu_line);
|
||||
|
||||
s.integer(status.rdy_line);
|
||||
s.integer(status.rdy_addr_valid);
|
||||
s.integer(status.rdy_addr_value);
|
||||
|
||||
s.integer(status.oam_dma_pending);
|
||||
s.integer(status.oam_dma_page);
|
||||
|
||||
s.integer(status.controller_latch);
|
||||
s.integer(status.controller_port0);
|
||||
s.integer(status.controller_port1);
|
||||
}
|
@@ -1,64 +0,0 @@
|
||||
uint8 CPU::op_read(uint16 addr) {
|
||||
if(status.oam_dma_pending) {
|
||||
status.oam_dma_pending = false;
|
||||
op_read(addr);
|
||||
oam_dma();
|
||||
}
|
||||
|
||||
while(status.rdy_line == 0) {
|
||||
regs.mdr = bus.read(status.rdy_addr_valid ? status.rdy_addr_value : addr);
|
||||
add_clocks(12);
|
||||
}
|
||||
|
||||
regs.mdr = bus.read(addr);
|
||||
add_clocks(12);
|
||||
return regs.mdr;
|
||||
}
|
||||
|
||||
void CPU::op_write(uint16 addr, uint8 data) {
|
||||
bus.write(addr, regs.mdr = data);
|
||||
add_clocks(12);
|
||||
}
|
||||
|
||||
void CPU::last_cycle() {
|
||||
status.interrupt_pending = ((status.irq_line | status.irq_apu_line) & ~regs.p.i) | status.nmi_pending;
|
||||
}
|
||||
|
||||
void CPU::nmi(uint16 &vector) {
|
||||
if(status.nmi_pending) {
|
||||
status.nmi_pending = false;
|
||||
vector = 0xfffa;
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::oam_dma() {
|
||||
for(unsigned n = 0; n < 256; n++) {
|
||||
uint8 data = op_read((status.oam_dma_page << 8) + n);
|
||||
op_write(0x2004, data);
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::set_nmi_line(bool line) {
|
||||
//edge-sensitive (0->1)
|
||||
if(!status.nmi_line && line) status.nmi_pending = true;
|
||||
status.nmi_line = line;
|
||||
}
|
||||
|
||||
void CPU::set_irq_line(bool line) {
|
||||
//level-sensitive
|
||||
status.irq_line = line;
|
||||
}
|
||||
|
||||
void CPU::set_irq_apu_line(bool line) {
|
||||
//level-sensitive
|
||||
status.irq_apu_line = line;
|
||||
}
|
||||
|
||||
void CPU::set_rdy_line(bool line) {
|
||||
status.rdy_line = line;
|
||||
}
|
||||
|
||||
void CPU::set_rdy_addr(bool valid, uint16 value) {
|
||||
status.rdy_addr_valid = valid;
|
||||
status.rdy_addr_value = value;
|
||||
}
|
62
fc/fc.hpp
62
fc/fc.hpp
@@ -1,62 +0,0 @@
|
||||
#ifndef FC_HPP
|
||||
#define FC_HPP
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <processor/r6502/r6502.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
namespace Info {
|
||||
static const char Name[] = "bnes";
|
||||
static const unsigned SerializerVersion = 2;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
bnes - Famicom emulator
|
||||
authors: byuu, Ryphecha
|
||||
license: GPLv3
|
||||
project started: 2011-09-05
|
||||
*/
|
||||
|
||||
#include <libco/libco.h>
|
||||
|
||||
namespace Famicom {
|
||||
struct Thread {
|
||||
cothread_t thread;
|
||||
unsigned frequency;
|
||||
int64 clock;
|
||||
|
||||
inline void create(void (*entrypoint)(), unsigned frequency) {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
inline void serialize(serializer& s) {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
}
|
||||
|
||||
inline Thread() : thread(nullptr) {
|
||||
}
|
||||
|
||||
inline ~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
}
|
||||
};
|
||||
|
||||
#include <fc/system/system.hpp>
|
||||
#include <fc/scheduler/scheduler.hpp>
|
||||
#include <fc/input/input.hpp>
|
||||
#include <fc/memory/memory.hpp>
|
||||
#include <fc/cartridge/cartridge.hpp>
|
||||
#include <fc/cpu/cpu.hpp>
|
||||
#include <fc/apu/apu.hpp>
|
||||
#include <fc/ppu/ppu.hpp>
|
||||
#include <fc/cheat/cheat.hpp>
|
||||
#include <fc/video/video.hpp>
|
||||
#include <fc/interface/interface.hpp>
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,53 +0,0 @@
|
||||
#include <fc/fc.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
|
||||
#include "serialization.cpp"
|
||||
Input input;
|
||||
|
||||
void Input::latch(bool data) {
|
||||
latchdata = data;
|
||||
|
||||
if(latchdata == 1) {
|
||||
counter1 = 0;
|
||||
counter2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Input::data(bool port) {
|
||||
bool result = 0;
|
||||
|
||||
if(port == 0) {
|
||||
if(port1 == Device::Joypad) {
|
||||
if(counter1 >= 8) return 1;
|
||||
result = interface->inputPoll(0, 0u, counter1);
|
||||
if(latchdata == 0) counter1++;
|
||||
}
|
||||
}
|
||||
|
||||
if(port == 1) {
|
||||
if(port2 == Device::Joypad) {
|
||||
if(counter2 >= 8) return 1;
|
||||
result = interface->inputPoll(1, 0u, counter2);
|
||||
if(latchdata == 0) counter2++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Input::connect(bool port, Device device) {
|
||||
if(port == 0) port1 = device, counter1 = 0;
|
||||
if(port == 1) port2 = device, counter2 = 0;
|
||||
}
|
||||
|
||||
void Input::power() {
|
||||
}
|
||||
|
||||
void Input::reset() {
|
||||
latchdata = 0;
|
||||
counter1 = 0;
|
||||
counter2 = 0;
|
||||
}
|
||||
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
struct Input {
|
||||
enum class Device : unsigned {
|
||||
Joypad,
|
||||
None,
|
||||
};
|
||||
|
||||
void latch(bool data);
|
||||
bool data(bool port);
|
||||
void connect(bool port, Device device);
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
Device port1;
|
||||
Device port2;
|
||||
|
||||
bool latchdata;
|
||||
unsigned counter1;
|
||||
unsigned counter2;
|
||||
};
|
||||
|
||||
extern Input input;
|
@@ -1,8 +0,0 @@
|
||||
void Input::serialize(serializer& s) {
|
||||
s.integer((unsigned&)port1);
|
||||
s.integer((unsigned&)port2);
|
||||
|
||||
s.integer(latchdata);
|
||||
s.integer(counter1);
|
||||
s.integer(counter2);
|
||||
}
|
@@ -1,162 +0,0 @@
|
||||
#include <fc/fc.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
|
||||
Interface* interface = nullptr;
|
||||
|
||||
string Interface::title() {
|
||||
return cartridge.title();
|
||||
}
|
||||
|
||||
double Interface::videoFrequency() {
|
||||
return 21477272.0 / (262.0 * 1364.0 - 4.0);
|
||||
}
|
||||
|
||||
double Interface::audioFrequency() {
|
||||
return 21477272.0 / 12.0;
|
||||
}
|
||||
|
||||
bool Interface::loaded() {
|
||||
return cartridge.loaded();
|
||||
}
|
||||
|
||||
string Interface::sha256() {
|
||||
return cartridge.sha256();
|
||||
}
|
||||
|
||||
unsigned Interface::group(unsigned id) {
|
||||
switch(id) {
|
||||
case ID::Manifest:
|
||||
case ID::ProgramROM:
|
||||
case ID::ProgramRAM:
|
||||
case ID::CharacterROM:
|
||||
case ID::CharacterRAM:
|
||||
return 1;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
void Interface::load(unsigned id) {
|
||||
cartridge.load();
|
||||
}
|
||||
|
||||
void Interface::save() {
|
||||
for(auto& memory : cartridge.memory) {
|
||||
saveRequest(memory.id, memory.name);
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::load(unsigned id, const stream& stream) {
|
||||
if(id == ID::Manifest) cartridge.information.markup = stream.text();
|
||||
|
||||
if(id == ID::ProgramROM) {
|
||||
stream.read(cartridge.board->prgrom.data, min(cartridge.board->prgrom.size, stream.size()));
|
||||
}
|
||||
|
||||
if(id == ID::ProgramRAM) {
|
||||
stream.read(cartridge.board->prgram.data, min(cartridge.board->prgram.size, stream.size()));
|
||||
}
|
||||
|
||||
if(id == ID::CharacterROM) {
|
||||
stream.read(cartridge.board->chrrom.data, min(cartridge.board->chrrom.size, stream.size()));
|
||||
}
|
||||
|
||||
if(id == ID::CharacterRAM) {
|
||||
stream.read(cartridge.board->chrram.data, min(cartridge.board->chrram.size, stream.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::save(unsigned id, const stream& stream) {
|
||||
if(id == ID::ProgramRAM) {
|
||||
stream.write(cartridge.board->prgram.data, cartridge.board->prgram.size);
|
||||
}
|
||||
|
||||
if(id == ID::CharacterRAM) {
|
||||
stream.write(cartridge.board->chrram.data, cartridge.board->chrram.size);
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::unload() {
|
||||
save();
|
||||
cartridge.unload();
|
||||
}
|
||||
|
||||
void Interface::power() {
|
||||
system.power();
|
||||
}
|
||||
|
||||
void Interface::reset() {
|
||||
system.reset();
|
||||
}
|
||||
|
||||
void Interface::run() {
|
||||
system.run();
|
||||
}
|
||||
|
||||
serializer Interface::serialize() {
|
||||
system.runtosave();
|
||||
return system.serialize();
|
||||
}
|
||||
|
||||
bool Interface::unserialize(serializer& s) {
|
||||
return system.unserialize(s);
|
||||
}
|
||||
|
||||
void Interface::cheatSet(const lstring& list) {
|
||||
cheat.reset();
|
||||
for(auto& code : list) {
|
||||
lstring codelist = code.split("+");
|
||||
for(auto& part : codelist) {
|
||||
unsigned addr, data, comp;
|
||||
if(Cheat::decode(part, addr, data, comp)) cheat.append({addr, data, comp});
|
||||
}
|
||||
}
|
||||
cheat.synchronize();
|
||||
}
|
||||
|
||||
void Interface::paletteUpdate() {
|
||||
video.generate_palette();
|
||||
}
|
||||
|
||||
Interface::Interface() {
|
||||
interface = this;
|
||||
|
||||
information.name = "Famicom";
|
||||
information.width = 256;
|
||||
information.height = 240;
|
||||
information.overscan = true;
|
||||
information.aspectRatio = 8.0 / 7.0;
|
||||
information.resettable = true;
|
||||
information.capability.states = true;
|
||||
information.capability.cheats = true;
|
||||
|
||||
media.append({ID::Famicom, "Famicom", "fc", true});
|
||||
|
||||
{
|
||||
Device device{0, ID::Port1 | ID::Port2, "Controller"};
|
||||
device.input.append({0, 0, "A" });
|
||||
device.input.append({1, 0, "B" });
|
||||
device.input.append({2, 0, "Select"});
|
||||
device.input.append({3, 0, "Start" });
|
||||
device.input.append({4, 0, "Up" });
|
||||
device.input.append({5, 0, "Down" });
|
||||
device.input.append({6, 0, "Left" });
|
||||
device.input.append({7, 0, "Right" });
|
||||
device.order = {4, 5, 6, 7, 1, 0, 2, 3};
|
||||
this->device.append(device);
|
||||
}
|
||||
|
||||
port.append({0, "Port 1"});
|
||||
port.append({1, "Port 2"});
|
||||
|
||||
for(auto& device : this->device) {
|
||||
for(auto& port : this->port) {
|
||||
if(device.portmask & (1 << port.id)) {
|
||||
port.device.append(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,60 +0,0 @@
|
||||
#ifndef FC_HPP
|
||||
namespace Famicom {
|
||||
#endif
|
||||
|
||||
struct ID {
|
||||
enum : unsigned {
|
||||
System,
|
||||
Famicom,
|
||||
};
|
||||
|
||||
enum : unsigned {
|
||||
Manifest,
|
||||
ProgramROM,
|
||||
ProgramRAM,
|
||||
CharacterROM,
|
||||
CharacterRAM,
|
||||
};
|
||||
|
||||
enum : unsigned {
|
||||
Port1 = 1,
|
||||
Port2 = 2,
|
||||
};
|
||||
};
|
||||
|
||||
struct Interface : Emulator::Interface {
|
||||
string title();
|
||||
double videoFrequency();
|
||||
double audioFrequency();
|
||||
|
||||
bool loaded();
|
||||
string sha256();
|
||||
unsigned group(unsigned id);
|
||||
void load(unsigned id);
|
||||
void save();
|
||||
void load(unsigned id, const stream& stream);
|
||||
void save(unsigned id, const stream& stream);
|
||||
void unload();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
void run();
|
||||
|
||||
serializer serialize();
|
||||
bool unserialize(serializer&);
|
||||
|
||||
void cheatSet(const lstring&);
|
||||
|
||||
void paletteUpdate();
|
||||
|
||||
Interface();
|
||||
|
||||
private:
|
||||
vector<Device> device;
|
||||
};
|
||||
|
||||
extern Interface* interface;
|
||||
|
||||
#ifndef FC_HPP
|
||||
}
|
||||
#endif
|
@@ -1,41 +0,0 @@
|
||||
#include <fc/fc.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
|
||||
Bus bus;
|
||||
|
||||
//$0000-07ff = RAM (2KB)
|
||||
//$0800-1fff = RAM (mirror)
|
||||
//$2000-2007 = PPU
|
||||
//$2008-3fff = PPU (mirror)
|
||||
//$4000-4017 = APU + I/O
|
||||
//$4018-ffff = Cartridge
|
||||
|
||||
uint8 Bus::read(uint16 addr) {
|
||||
uint8 data = cartridge.prg_read(addr);
|
||||
if(addr <= 0x1fff) data = cpu.ram_read(addr);
|
||||
else if(addr <= 0x3fff) data = ppu.read(addr);
|
||||
else if(addr <= 0x4017) data = cpu.read(addr);
|
||||
|
||||
if(cheat.override[addr]) {
|
||||
for(unsigned n = 0; n < cheat.size(); n++) {
|
||||
if(cheat[n].addr == addr) {
|
||||
if(cheat[n].comp > 255 || cheat[n].comp == data) {
|
||||
data = cheat[n].data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void Bus::write(uint16 addr, uint8 data) {
|
||||
cartridge.prg_write(addr, data);
|
||||
if(addr <= 0x1fff) return cpu.ram_write(addr, data);
|
||||
if(addr <= 0x3fff) return ppu.write(addr, data);
|
||||
if(addr <= 0x4017) return cpu.write(addr, data);
|
||||
}
|
||||
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
struct Bus {
|
||||
uint8 read(uint16 addr);
|
||||
void write(uint16 addr, uint8 data);
|
||||
};
|
||||
|
||||
extern Bus bus;
|
487
fc/ppu/ppu.cpp
487
fc/ppu/ppu.cpp
@@ -1,487 +0,0 @@
|
||||
#include <fc/fc.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
|
||||
#include "serialization.cpp"
|
||||
PPU ppu;
|
||||
|
||||
void PPU::Main() {
|
||||
ppu.main();
|
||||
}
|
||||
|
||||
void PPU::main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::PPU) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
raster_scanline();
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::tick() {
|
||||
if(status.ly == 240 && status.lx == 340) status.nmi_hold = 1;
|
||||
if(status.ly == 241 && status.lx == 0) status.nmi_flag = status.nmi_hold;
|
||||
if(status.ly == 241 && status.lx == 2) cpu.set_nmi_line(status.nmi_enable && status.nmi_flag);
|
||||
|
||||
if(status.ly == 260 && status.lx == 340) status.sprite_zero_hit = 0, status.sprite_overflow = 0;
|
||||
|
||||
if(status.ly == 260 && status.lx == 340) status.nmi_hold = 0;
|
||||
if(status.ly == 261 && status.lx == 0) status.nmi_flag = status.nmi_hold;
|
||||
if(status.ly == 261 && status.lx == 2) cpu.set_nmi_line(status.nmi_enable && status.nmi_flag);
|
||||
|
||||
clock += 4;
|
||||
if(clock >= 0) co_switch(cpu.thread);
|
||||
|
||||
status.lx++;
|
||||
}
|
||||
|
||||
void PPU::scanline() {
|
||||
status.lx = 0;
|
||||
if(++status.ly == 262) {
|
||||
status.ly = 0;
|
||||
frame();
|
||||
}
|
||||
cartridge.scanline(status.ly);
|
||||
}
|
||||
|
||||
void PPU::frame() {
|
||||
status.field ^= 1;
|
||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||
}
|
||||
|
||||
void PPU::power() {
|
||||
}
|
||||
|
||||
void PPU::reset() {
|
||||
create(PPU::Main, 21477272);
|
||||
|
||||
status.mdr = 0x00;
|
||||
status.field = 0;
|
||||
status.ly = 0;
|
||||
status.bus_data = 0x00;
|
||||
status.address_latch = 0;
|
||||
|
||||
status.vaddr = 0x0000;
|
||||
status.taddr = 0x0000;
|
||||
status.xaddr = 0x00;
|
||||
|
||||
status.nmi_hold = 0;
|
||||
status.nmi_flag = 0;
|
||||
|
||||
//$2000
|
||||
status.nmi_enable = false;
|
||||
status.master_select = 0;
|
||||
status.sprite_size = 0;
|
||||
status.bg_addr = 0x0000;
|
||||
status.sprite_addr = 0x0000;
|
||||
status.vram_increment = 1;
|
||||
|
||||
//$2001
|
||||
status.emphasis = 0;
|
||||
status.sprite_enable = false;
|
||||
status.bg_enable = false;
|
||||
status.sprite_edge_enable = false;
|
||||
status.bg_edge_enable = false;
|
||||
status.grayscale = false;
|
||||
|
||||
//$2002
|
||||
status.sprite_zero_hit = false;
|
||||
status.sprite_overflow = false;
|
||||
|
||||
//$2003
|
||||
status.oam_addr = 0x00;
|
||||
|
||||
for(auto& n : buffer) n = 0;
|
||||
for(auto& n : ciram ) n = 0;
|
||||
for(auto& n : cgram ) n = 0;
|
||||
for(auto& n : oam ) n = 0;
|
||||
}
|
||||
|
||||
uint8 PPU::read(uint16 addr) {
|
||||
uint8 result = 0x00;
|
||||
|
||||
switch(addr & 7) {
|
||||
case 2: //PPUSTATUS
|
||||
result |= status.nmi_flag << 7;
|
||||
result |= status.sprite_zero_hit << 6;
|
||||
result |= status.sprite_overflow << 5;
|
||||
result |= status.mdr & 0x1f;
|
||||
status.address_latch = 0;
|
||||
status.nmi_hold = 0;
|
||||
cpu.set_nmi_line(status.nmi_flag = 0);
|
||||
break;
|
||||
case 4: //OAMDATA
|
||||
result = oam[status.oam_addr];
|
||||
if((status.oam_addr & 3) == 3) result &= 0xe3;
|
||||
break;
|
||||
case 7: //PPUDATA
|
||||
if(raster_enable() && (status.ly <= 240 || status.ly == 261)) return 0x00;
|
||||
|
||||
addr = status.vaddr & 0x3fff;
|
||||
if(addr <= 0x1fff) {
|
||||
result = status.bus_data;
|
||||
status.bus_data = cartridge.chr_read(addr);
|
||||
} else if(addr <= 0x3eff) {
|
||||
result = status.bus_data;
|
||||
status.bus_data = cartridge.chr_read(addr);
|
||||
} else if(addr <= 0x3fff) {
|
||||
result = cgram_read(addr);
|
||||
status.bus_data = cartridge.chr_read(addr);
|
||||
}
|
||||
status.vaddr += status.vram_increment;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void PPU::write(uint16 addr, uint8 data) {
|
||||
status.mdr = data;
|
||||
|
||||
switch(addr & 7) {
|
||||
case 0: //PPUCTRL
|
||||
status.nmi_enable = data & 0x80;
|
||||
status.master_select = data & 0x40;
|
||||
status.sprite_size = data & 0x20;
|
||||
status.bg_addr = (data & 0x10) ? 0x1000 : 0x0000;
|
||||
status.sprite_addr = (data & 0x08) ? 0x1000 : 0x0000;
|
||||
status.vram_increment = (data & 0x04) ? 32 : 1;
|
||||
status.taddr = (status.taddr & 0x73ff) | ((data & 0x03) << 10);
|
||||
cpu.set_nmi_line(status.nmi_enable && status.nmi_hold && status.nmi_flag);
|
||||
return;
|
||||
case 1: //PPUMASK
|
||||
status.emphasis = data >> 5;
|
||||
status.sprite_enable = data & 0x10;
|
||||
status.bg_enable = data & 0x08;
|
||||
status.sprite_edge_enable = data & 0x04;
|
||||
status.bg_edge_enable = data & 0x02;
|
||||
status.grayscale = data & 0x01;
|
||||
return;
|
||||
case 2: //PPUSTATUS
|
||||
return;
|
||||
case 3: //OAMADDR
|
||||
status.oam_addr = data;
|
||||
return;
|
||||
case 4: //OAMDATA
|
||||
oam[status.oam_addr++] = data;
|
||||
return;
|
||||
case 5: //PPUSCROLL
|
||||
if(status.address_latch == 0) {
|
||||
status.xaddr = data & 0x07;
|
||||
status.taddr = (status.taddr & 0x7fe0) | (data >> 3);
|
||||
} else {
|
||||
status.taddr = (status.taddr & 0x0c1f) | ((data & 0x07) << 12) | ((data >> 3) << 5);
|
||||
}
|
||||
status.address_latch ^= 1;
|
||||
return;
|
||||
case 6: //PPUADDR
|
||||
if(status.address_latch == 0) {
|
||||
status.taddr = (status.taddr & 0x00ff) | ((data & 0x3f) << 8);
|
||||
} else {
|
||||
status.taddr = (status.taddr & 0x7f00) | data;
|
||||
status.vaddr = status.taddr;
|
||||
}
|
||||
status.address_latch ^= 1;
|
||||
return;
|
||||
case 7: //PPUDATA
|
||||
if(raster_enable() && (status.ly <= 240 || status.ly == 261)) return;
|
||||
|
||||
addr = status.vaddr & 0x3fff;
|
||||
if(addr <= 0x1fff) {
|
||||
cartridge.chr_write(addr, data);
|
||||
} else if(addr <= 0x3eff) {
|
||||
cartridge.chr_write(addr, data);
|
||||
} else if(addr <= 0x3fff) {
|
||||
cgram_write(addr, data);
|
||||
}
|
||||
status.vaddr += status.vram_increment;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 PPU::ciram_read(uint16 addr) {
|
||||
return ciram[addr & 0x07ff];
|
||||
}
|
||||
|
||||
void PPU::ciram_write(uint16 addr, uint8 data) {
|
||||
ciram[addr & 0x07ff] = data;
|
||||
}
|
||||
|
||||
uint8 PPU::cgram_read(uint16 addr) {
|
||||
if((addr & 0x13) == 0x10) addr &= ~0x10;
|
||||
uint8 data = cgram[addr & 0x1f];
|
||||
if(status.grayscale) data &= 0x30;
|
||||
return data;
|
||||
}
|
||||
|
||||
void PPU::cgram_write(uint16 addr, uint8 data) {
|
||||
if((addr & 0x13) == 0x10) addr &= ~0x10;
|
||||
cgram[addr & 0x1f] = data;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
//vaddr = 0yyy VHYY YYYX XXXX
|
||||
//yyy = fine Yscroll (y:d0-d2)
|
||||
//V = V nametable (y:d8)
|
||||
//H = H nametable (x:d8)
|
||||
//YYYYY = Y nametable (y:d3-d7)
|
||||
//XXXXX = X nametable (x:d3-d7)
|
||||
|
||||
bool PPU::raster_enable() const {
|
||||
return (status.bg_enable || status.sprite_enable);
|
||||
}
|
||||
|
||||
unsigned PPU::nametable_addr() const {
|
||||
return 0x2000 + (status.vaddr & 0x0c00);
|
||||
}
|
||||
|
||||
unsigned PPU::scrollx() const {
|
||||
return ((status.vaddr & 0x1f) << 3) | status.xaddr;
|
||||
}
|
||||
|
||||
unsigned PPU::scrolly() const {
|
||||
return (((status.vaddr >> 5) & 0x1f) << 3) | ((status.vaddr >> 12) & 7);
|
||||
}
|
||||
|
||||
unsigned PPU::sprite_height() const {
|
||||
return status.sprite_size == 0 ? 8 : 16;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
uint8 PPU::chr_load(uint16 addr) {
|
||||
if(raster_enable() == false) return 0x00;
|
||||
return cartridge.chr_read(addr);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void PPU::scrollx_increment() {
|
||||
if(raster_enable() == false) return;
|
||||
status.vaddr = (status.vaddr & 0x7fe0) | ((status.vaddr + 0x0001) & 0x001f);
|
||||
if((status.vaddr & 0x001f) == 0x0000) {
|
||||
status.vaddr ^= 0x0400;
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::scrolly_increment() {
|
||||
if(raster_enable() == false) return;
|
||||
status.vaddr = (status.vaddr & 0x0fff) | ((status.vaddr + 0x1000) & 0x7000);
|
||||
if((status.vaddr & 0x7000) == 0x0000) {
|
||||
status.vaddr = (status.vaddr & 0x7c1f) | ((status.vaddr + 0x0020) & 0x03e0);
|
||||
if((status.vaddr & 0x03e0) == 0x03c0) { //0x03c0 == 30 << 5; 30 * 8 = 240
|
||||
status.vaddr &= 0x7c1f;
|
||||
status.vaddr ^= 0x0800;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void PPU::raster_pixel() {
|
||||
uint32* output = buffer + status.ly * 256;
|
||||
|
||||
unsigned mask = 0x8000 >> (status.xaddr + (status.lx & 7));
|
||||
unsigned palette = 0, object_palette = 0;
|
||||
bool object_priority = 0;
|
||||
palette |= (raster.tiledatalo & mask) ? 1 : 0;
|
||||
palette |= (raster.tiledatahi & mask) ? 2 : 0;
|
||||
if(palette) {
|
||||
unsigned attr = raster.attribute;
|
||||
if(mask >= 256) attr >>= 2;
|
||||
palette |= (attr & 3) << 2;
|
||||
}
|
||||
|
||||
if(status.bg_enable == false) palette = 0;
|
||||
if(status.bg_edge_enable == false && status.lx < 8) palette = 0;
|
||||
|
||||
if(status.sprite_enable == true)
|
||||
for(signed sprite = 7; sprite >= 0; sprite--) {
|
||||
if(status.sprite_edge_enable == false && status.lx < 8) continue;
|
||||
if(raster.oam[sprite].id == 64) continue;
|
||||
|
||||
unsigned spritex = status.lx - raster.oam[sprite].x;
|
||||
if(spritex >= 8) continue;
|
||||
|
||||
if(raster.oam[sprite].attr & 0x40) spritex ^= 7;
|
||||
unsigned mask = 0x80 >> spritex;
|
||||
unsigned sprite_palette = 0;
|
||||
sprite_palette |= (raster.oam[sprite].tiledatalo & mask) ? 1 : 0;
|
||||
sprite_palette |= (raster.oam[sprite].tiledatahi & mask) ? 2 : 0;
|
||||
if(sprite_palette == 0) continue;
|
||||
|
||||
if(raster.oam[sprite].id == 0 && palette && status.lx != 255) status.sprite_zero_hit = 1;
|
||||
sprite_palette |= (raster.oam[sprite].attr & 3) << 2;
|
||||
|
||||
object_priority = raster.oam[sprite].attr & 0x20;
|
||||
object_palette = 16 + sprite_palette;
|
||||
}
|
||||
|
||||
if(object_palette) {
|
||||
if(palette == 0 || object_priority == 0) palette = object_palette;
|
||||
}
|
||||
|
||||
if(raster_enable() == false) palette = 0;
|
||||
output[status.lx] = video.palette[(status.emphasis << 6) | cgram_read(palette)];
|
||||
}
|
||||
|
||||
void PPU::raster_sprite() {
|
||||
if(raster_enable() == false) return;
|
||||
|
||||
unsigned n = raster.oam_iterator++;
|
||||
signed ly = (status.ly == 261 ? -1 : status.ly);
|
||||
unsigned y = ly - oam[(n * 4) + 0];
|
||||
|
||||
if(y >= sprite_height()) return;
|
||||
if(raster.oam_counter == 8) {
|
||||
status.sprite_overflow = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
raster.soam[raster.oam_counter].id = n;
|
||||
raster.soam[raster.oam_counter].y = oam[(n * 4) + 0];
|
||||
raster.soam[raster.oam_counter].tile = oam[(n * 4) + 1];
|
||||
raster.soam[raster.oam_counter].attr = oam[(n * 4) + 2];
|
||||
raster.soam[raster.oam_counter].x = oam[(n * 4) + 3];
|
||||
raster.oam_counter++;
|
||||
}
|
||||
|
||||
void PPU::raster_scanline() {
|
||||
if((status.ly >= 240 && status.ly <= 260)) {
|
||||
for(unsigned x = 0; x < 341; x++) tick();
|
||||
return scanline();
|
||||
}
|
||||
|
||||
raster.oam_iterator = 0;
|
||||
raster.oam_counter = 0;
|
||||
|
||||
for(unsigned n = 0; n < 8; n++) {
|
||||
raster.soam[n].id = 64;
|
||||
raster.soam[n].y = 0xff;
|
||||
raster.soam[n].tile = 0xff;
|
||||
raster.soam[n].attr = 0xff;
|
||||
raster.soam[n].x = 0xff;
|
||||
raster.soam[n].tiledatalo = 0;
|
||||
raster.soam[n].tiledatahi = 0;
|
||||
}
|
||||
|
||||
for(unsigned tile = 0; tile < 32; tile++) { // 0-255
|
||||
unsigned nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
|
||||
unsigned tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
|
||||
raster_pixel();
|
||||
tick();
|
||||
|
||||
raster_pixel();
|
||||
tick();
|
||||
|
||||
unsigned attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
|
||||
if(scrolly() & 16) attribute >>= 4;
|
||||
if(scrollx() & 16) attribute >>= 2;
|
||||
raster_pixel();
|
||||
tick();
|
||||
|
||||
scrollx_increment();
|
||||
if(tile == 31) scrolly_increment();
|
||||
raster_pixel();
|
||||
raster_sprite();
|
||||
tick();
|
||||
|
||||
unsigned tiledatalo = chr_load(tileaddr + 0);
|
||||
raster_pixel();
|
||||
tick();
|
||||
|
||||
raster_pixel();
|
||||
tick();
|
||||
|
||||
unsigned tiledatahi = chr_load(tileaddr + 8);
|
||||
raster_pixel();
|
||||
tick();
|
||||
|
||||
raster_pixel();
|
||||
raster_sprite();
|
||||
tick();
|
||||
|
||||
raster.nametable = (raster.nametable << 8) | nametable;
|
||||
raster.attribute = (raster.attribute << 2) | (attribute & 3);
|
||||
raster.tiledatalo = (raster.tiledatalo << 8) | tiledatalo;
|
||||
raster.tiledatahi = (raster.tiledatahi << 8) | tiledatahi;
|
||||
}
|
||||
|
||||
for(unsigned n = 0; n < 8; n++) raster.oam[n] = raster.soam[n];
|
||||
|
||||
for(unsigned sprite = 0; sprite < 8; sprite++) { //256-319
|
||||
unsigned nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
|
||||
tick();
|
||||
|
||||
if(raster_enable() && sprite == 0) status.vaddr = (status.vaddr & 0x7be0) | (status.taddr & 0x041f); //257
|
||||
tick();
|
||||
|
||||
unsigned attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
|
||||
unsigned tileaddr = (sprite_height() == 8)
|
||||
? status.sprite_addr + raster.oam[sprite].tile * 16
|
||||
: ((raster.oam[sprite].tile & ~1) * 16) + ((raster.oam[sprite].tile & 1) * 0x1000);
|
||||
tick();
|
||||
tick();
|
||||
|
||||
unsigned spritey = (status.ly - raster.oam[sprite].y) & (sprite_height() - 1);
|
||||
if(raster.oam[sprite].attr & 0x80) spritey ^= (sprite_height() - 1);
|
||||
tileaddr += spritey + (spritey & 8);
|
||||
|
||||
raster.oam[sprite].tiledatalo = chr_load(tileaddr + 0);
|
||||
tick();
|
||||
tick();
|
||||
|
||||
raster.oam[sprite].tiledatahi = chr_load(tileaddr + 8);
|
||||
tick();
|
||||
tick();
|
||||
|
||||
if(raster_enable() && sprite == 6 && status.ly == 261) status.vaddr = status.taddr; //304
|
||||
}
|
||||
|
||||
for(unsigned tile = 0; tile < 2; tile++) { //320-335
|
||||
unsigned nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
|
||||
unsigned tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
|
||||
tick();
|
||||
tick();
|
||||
|
||||
unsigned attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
|
||||
if(scrolly() & 16) attribute >>= 4;
|
||||
if(scrollx() & 16) attribute >>= 2;
|
||||
tick();
|
||||
|
||||
scrollx_increment();
|
||||
tick();
|
||||
|
||||
unsigned tiledatalo = chr_load(tileaddr + 0);
|
||||
tick();
|
||||
tick();
|
||||
|
||||
unsigned tiledatahi = chr_load(tileaddr + 8);
|
||||
tick();
|
||||
tick();
|
||||
|
||||
raster.nametable = (raster.nametable << 8) | nametable;
|
||||
raster.attribute = (raster.attribute << 2) | (attribute & 3);
|
||||
raster.tiledatalo = (raster.tiledatalo << 8) | tiledatalo;
|
||||
raster.tiledatahi = (raster.tiledatahi << 8) | tiledatahi;
|
||||
}
|
||||
|
||||
//336-339
|
||||
chr_load(0x2000 | (status.vaddr & 0x0fff));
|
||||
tick();
|
||||
bool skip = (raster_enable() && status.field == 1 && status.ly == 261);
|
||||
tick();
|
||||
|
||||
chr_load(0x2000 | (status.vaddr & 0x0fff));
|
||||
tick();
|
||||
tick();
|
||||
|
||||
//340
|
||||
if(skip == false) tick();
|
||||
|
||||
return scanline();
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user