Update to v078r05 release.

byuu says:

This WIP adds Nintendo Super System emulation, at least of its DIP
switches.  This is done via XML mapping, like so:

    <?xml version="1.0" encoding="UTF-8"?>
    <cartridge region="NTSC">
      <name>ActRaiser</name>
      <rom>
	<map mode="linear" address="00-7f:8000-ffff"/>
	<map mode="linear" address="80-ff:8000-ffff"/>
      </rom>
      <nss>
	<setting name="Difficulty">
	  <option value="0000" name="Easy"/>
	  <option value="0001" name="Normal"/>
	  <option value="0002" name="Hard"/>
	  <option value="0003" name="Expert"/>
	</setting>
	<setting name="Lives">
	  <option value="0000" name="5 lives"/>
	  <option value="0004" name="4 lives"/>
	  <option value="0008" name="3 lives"/>
	  <option value="000c" name="2 lives"/>
	</setting>
      </nss>
    </cartridge>

The value field is a 16-bit value. All selected options are ORed
together to produce the final DIP switch values.  The number of options
per setting is unlimited, but there are only sixteen settings allowed
(you can't have more settings than you have switches, that's just
stupid.)

In the example above, d0-d1 controls difficulty, and d2-d3 controls # of
lives. d4-d15 appear to be unused, as far as I can tell.
This commit is contained in:
Tim Allen 2011-05-07 00:16:46 +10:00
parent 6694a1c986
commit 52443936e6
19 changed files with 225 additions and 4 deletions

View File

@ -1,7 +1,7 @@
include nall/Makefile
snes := snes
gameboy := gameboy
profile := accuracy
profile := compatibility
ui := ui
# debugger

View File

@ -69,6 +69,10 @@ Geometry VerticalLayout::minimumLayoutGeometry() {
return { 0, 0, maximumWidth ? MaximumSize : margin * 2 + width, maximumHeight ? MaximumSize : margin * 2 + height };
}
void VerticalLayout::reset() {
children.reset();
}
void VerticalLayout::setGeometry(const Geometry &containerGeometry) {
auto children = this->children;
foreach(child, children) {

View File

@ -5,6 +5,7 @@ struct VerticalLayout : public Layout {
void append(Widget &widget, unsigned width, unsigned height, unsigned spacing = 0);
Geometry minimumGeometry();
Geometry minimumLayoutGeometry();
void reset();
void setGeometry(const Geometry &geometry);
void setMargin(unsigned margin);
void setParent(Window &parent);

View File

@ -2,7 +2,7 @@ snes_objects := snes-system
snes_objects += snes-cartridge snes-cheat
snes_objects += snes-memory snes-cpucore snes-smpcore
snes_objects += snes-cpu snes-smp snes-dsp snes-ppu
snes_objects += snes-icd2 snes-superfx snes-sa1 snes-necdsp
snes_objects += snes-nss snes-icd2 snes-superfx snes-sa1 snes-necdsp
snes_objects += snes-bsx snes-srtc snes-sdd1 snes-spc7110 snes-cx4
snes_objects += snes-obc1 snes-st0018 snes-sufamiturbo
snes_objects += snes-msu1 snes-serial
@ -39,6 +39,7 @@ obj/snes-ppu.o : $(snesppu)/ppu.cpp $(call rwildcard,$(snesppu)/)
obj/snes-cartridge.o: $(snes)/cartridge/cartridge.cpp $(snes)/cartridge/*
obj/snes-cheat.o : $(snes)/cheat/cheat.cpp $(snes)/cheat/*
obj/snes-nss.o : $(snes)/chip/nss/nss.cpp $(call rwildcard,$(snes)/chip/nss/)
obj/snes-icd2.o : $(snes)/chip/icd2/icd2.cpp $(call rwildcard,$(snes)/chip/icd2/)
obj/snes-superfx.o : $(snes)/chip/superfx/superfx.cpp $(call rwildcard,$(snes)/chip/superfx/)
obj/snes-sa1.o : $(snes)/chip/sa1/sa1.cpp $(call rwildcard,$(snes)/chip/sa1/)

View File

@ -1,4 +1,4 @@
//#define CYCLE_ACCURATE
#define CYCLE_ACCURATE
#include <snes/snes.hpp>

View File

@ -17,6 +17,7 @@ void Cartridge::load(Mode cartridge_mode, const lstring &xml_list) {
ram_size = 0;
has_bsx_slot = false;
has_nss_dip = false;
has_superfx = false;
has_sa1 = false;
has_necdsp = false;

View File

@ -34,6 +34,7 @@ public:
readonly<unsigned> ram_size;
readonly<bool> has_bsx_slot;
readonly<bool> has_nss_dip;
readonly<bool> has_superfx;
readonly<bool> has_sa1;
readonly<bool> has_necdsp;
@ -75,6 +76,13 @@ public:
};
linear_vector<Mapping> mapping;
struct Information {
struct NSS {
lstring setting;
lstring option[16];
} nss;
} information;
void load(Mode, const lstring&);
void unload();
@ -91,6 +99,7 @@ private:
void xml_parse_rom(xml_element&);
void xml_parse_ram(xml_element&);
void xml_parse_nss(xml_element&);
void xml_parse_icd2(xml_element&);
void xml_parse_superfx(xml_element&);
void xml_parse_sa1(xml_element&);

View File

@ -17,6 +17,9 @@ void Cartridge::parse_xml(const lstring &list) {
}
void Cartridge::parse_xml_cartridge(const char *data) {
//reset cartridge information
information.nss.setting.reset();
xml_element document = xml_parse(data);
if(document.element.size() == 0) return;
@ -32,6 +35,7 @@ void Cartridge::parse_xml_cartridge(const char *data) {
foreach(node, head.element) {
if(node.name == "rom") xml_parse_rom(node);
if(node.name == "ram") xml_parse_ram(node);
if(node.name == "nss") xml_parse_nss(node);
if(node.name == "icd2") xml_parse_icd2(node);
if(node.name == "superfx") xml_parse_superfx(node);
if(node.name == "sa1") xml_parse_sa1(node);
@ -97,6 +101,33 @@ void Cartridge::xml_parse_ram(xml_element &root) {
}
}
void Cartridge::xml_parse_nss(xml_element &root) {
has_nss_dip = true;
foreach(node, root.element) {
if(node.name == "setting") {
unsigned number = information.nss.setting.size();
if(number >= 16) break; //more than 16 DIP switches is not possible
information.nss.option[number].reset();
foreach(attr, node.attribute) {
if(attr.name == "name") {
information.nss.setting[number] = attr.parse();
}
}
foreach(leaf, node.element) {
string name;
unsigned value = 0x0000;
foreach(attr, leaf.attribute) {
if(attr.name == "name") name = attr.parse();
if(attr.name == "value") value = (uint16)hex(attr.content);
}
information.nss.option[number].append({ hex<4>(value), ":", name });
}
}
}
}
void Cartridge::xml_parse_icd2(xml_element &root) {
if(mode != Mode::SuperGameBoy) return;
icd2.revision = 1;

View File

@ -3,6 +3,7 @@ struct Coprocessor : Processor {
alwaysinline void synchronize_cpu();
};
#include <snes/chip/nss/nss.hpp>
#include <snes/chip/icd2/icd2.hpp>
#include <snes/chip/superfx/superfx.hpp>
#include <snes/chip/sa1/sa1.hpp>

39
bsnes/snes/chip/nss/nss.cpp Executable file
View File

@ -0,0 +1,39 @@
#include <snes/snes.hpp>
#define NSS_CPP
namespace SNES {
NSS nss;
void NSS::init() {
}
void NSS::load() {
dip = 0x0000;
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4100, 0x4101, { &NSS::read, this }, { &NSS::write, this });
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4100, 0x4101, { &NSS::read, this }, { &NSS::write, this });
}
void NSS::unload() {
}
void NSS::power() {
}
void NSS::reset() {
}
void NSS::set_dip(uint16 dip) {
this->dip = dip;
}
uint8 NSS::read(unsigned addr) {
if((addr & 0x40ffff) == 0x004100) return dip >> 0;
if((addr & 0x40ffff) == 0x004101) return dip >> 8;
return cpu.regs.mdr;
}
void NSS::write(unsigned addr, uint8 data) {
}
}

17
bsnes/snes/chip/nss/nss.hpp Executable file
View File

@ -0,0 +1,17 @@
class NSS {
public:
void init();
void load();
void unload();
void power();
void reset();
void set_dip(uint16 dip);
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
private:
uint16 dip;
};
extern NSS nss;

View File

@ -1,7 +1,7 @@
namespace SNES {
namespace Info {
static const char Name[] = "bsnes";
static const char Version[] = "078.04";
static const char Version[] = "078.05";
static const unsigned SerializerVersion = 20;
}
}

View File

@ -70,6 +70,7 @@ void System::init(Interface *interface_) {
assert(interface != 0);
icd2.init();
nss.init();
superfx.init();
sa1.init();
necdsp.init();
@ -111,6 +112,7 @@ void System::load() {
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.load();
if(cartridge.has_bsx_slot()) bsxflash.load();
if(cartridge.has_nss_dip()) nss.load();
if(cartridge.has_superfx()) superfx.load();
if(cartridge.has_sa1()) sa1.load();
if(cartridge.has_necdsp()) necdsp.load();
@ -134,6 +136,7 @@ void System::unload() {
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.unload();
if(cartridge.has_bsx_slot()) bsxflash.unload();
if(cartridge.has_nss_dip()) nss.unload();
if(cartridge.has_superfx()) superfx.unload();
if(cartridge.has_sa1()) sa1.unload();
if(cartridge.has_necdsp()) necdsp.unload();
@ -169,6 +172,7 @@ void System::power() {
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.power();
if(cartridge.has_bsx_slot()) bsxflash.power();
if(cartridge.has_nss_dip()) nss.power();
if(cartridge.has_superfx()) superfx.power();
if(cartridge.has_sa1()) sa1.power();
if(cartridge.has_necdsp()) necdsp.power();
@ -204,6 +208,7 @@ void System::reset() {
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.reset();
if(cartridge.has_bsx_slot()) bsxflash.reset();
if(cartridge.has_nss_dip()) nss.reset();
if(cartridge.has_superfx()) superfx.reset();
if(cartridge.has_sa1()) sa1.reset();
if(cartridge.has_necdsp()) necdsp.reset();

View File

@ -2,3 +2,4 @@
#include "main-window.cpp"
#include "file-browser.cpp"
#include "slot-loader.cpp"
#include "nss-dip-window.cpp"

View File

@ -1,3 +1,4 @@
#include "main-window.hpp"
#include "file-browser.hpp"
#include "slot-loader.hpp"
#include "nss-dip-window.hpp"

View File

@ -0,0 +1,91 @@
NSSDipWindow nssDipWindow;
void NSSDipWindow::create() {
application.addWindow(this, "NSSDipWindow", "160,160");
setTitle("NSS DIP Settings");
for(unsigned n = 0; n < 16; n++) {
dipName[n].setText("Unused");
dipName[n].setVisible(false);
layout.append(dipName[n], { 0, 0, 16, 16 });
dipValue[n].setVisible(false);
layout.append(dipValue[n], { 0, 0, 16, 16 });
}
loadButton.setText("Load Cartridge");
layout.append(loadButton, { 0, 0, 16, 16 });
append(layout);
setResizable(false);
setGeometry({ 0, 0, 400, 240 });
onClose = loadButton.onTick = { &NSSDipWindow::assign, this };
}
void NSSDipWindow::select() {
setVisible(false);
for(unsigned n = 0; n < 16; n++) {
dipName[n].setText({ "DIP #", 1 + n, ":" });
dipName[n].setVisible(false);
dipValue[n].reset();
dipValue[n].setVisible(false);
}
unsigned dipCount = SNES::cartridge.information.nss.setting.size();
for(unsigned n = 0; n < dipCount; n++) {
dipName[n].setText({ "DIP #", 1 + n, ": ", SNES::cartridge.information.nss.setting[n] });
for(unsigned z = 0; z < SNES::cartridge.information.nss.option[n].size(); z++) {
lstring part;
part.split<1>(":", SNES::cartridge.information.nss.option[n][z]);
dipValue[n].append(part[1]);
}
}
unsigned maximumLabelWidth = 50;
unsigned maximumComboWidth = 100;
unsigned controlHeight = dipValue[0].minimumGeometry().height;
for(unsigned n = 0; n < dipCount; n++) {
maximumLabelWidth = max(maximumLabelWidth, dipName[n].minimumGeometry().width);
maximumComboWidth = max(maximumComboWidth, dipValue[n].minimumGeometry().width);
}
for(unsigned n = 0; n < dipCount; n++) {
dipName[n].setGeometry({ 5, 5 + (controlHeight + 5) * n, maximumLabelWidth, controlHeight });
dipName[n].setVisible(true);
dipValue[n].setGeometry({ 5 + maximumLabelWidth + 5, 5 + (controlHeight + 5) * n, maximumComboWidth, controlHeight });
dipValue[n].setVisible(true);
}
unsigned buttonWidth = loadButton.minimumGeometry().width;
unsigned buttonHeight = loadButton.minimumGeometry().height;
unsigned windowWidth = 5 + maximumLabelWidth + 5 + maximumComboWidth + 5;
unsigned windowHeight = 5 + (controlHeight + 5) * dipCount + buttonHeight + 5;
loadButton.setGeometry({ windowWidth - 5 - buttonWidth, windowHeight - 5 - buttonHeight, buttonWidth, buttonHeight });
setGeometry({ geometry().x, geometry().y, windowWidth, windowHeight });
setVisible(true);
loadButton.setFocused();
}
void NSSDipWindow::assign() {
unsigned dip = 0;
for(unsigned n = 0; n < SNES::cartridge.information.nss.setting.size(); n++) {
unsigned position = dipValue[n].selection();
lstring part;
part.split<1>(":", SNES::cartridge.information.nss.option[n][position]);
dip |= hex(part[0]);
}
SNES::nss.set_dip(dip);
setVisible(false);
application.pause = false;
}

View File

@ -0,0 +1,12 @@
struct NSSDipWindow : TopLevelWindow {
FixedLayout layout;
Label dipName[16];
ComboBox dipValue[16];
Button loadButton;
void create();
void select();
void assign();
};
extern NSSDipWindow nssDipWindow;

View File

@ -54,6 +54,7 @@ void Application::main(int argc, char **argv) {
fileBrowser.create();
singleSlotLoader.create();
doubleSlotLoader.create();
nssDipWindow.create();
videoSettings.create();
audioSettings.create();
inputSettings.create();

View File

@ -158,6 +158,12 @@ void Utility::cartridgeLoaded() {
"Loaded ", notdir(cartridge.baseName),
cartridge.patchApplied ? ", and applied UPS patch" : ""
});
//NSS
if(SNES::cartridge.has_nss_dip()) {
nssDipWindow.select();
application.pause = true;
}
}
void Utility::cartridgeUnloaded() {