mirror of https://github.com/bsnes-emu/bsnes.git
Update to v088r15 release.
byuu says: Changelog: - default placement of presentation window optimized for 1024x768 displays or larger (sorry if yours is smaller, move the window yourself.) - Direct3D waits until a previous Vblank ends before waiting for the next Vblank to begin (fixes video timing analysis, and ---really--- fast computers.) - Window::setVisible(false) clears modality, but also fixed in Browser code as well (fixes loading images on Windows hanging) - Browser won't consume full CPU resources (but timing analysis will, I don't want stalls to affect the results.) - closing settings window while analyzing stops analysis - you can load the SGB BIOS without a game (why the hell you would want to ...) - escape closes the Browser window (it won't close other dialogs, it has to be hooked up per-window) - just for fun, joypad hat up/down moves in Browser file list, any joypad button loads selected game [not very useful, lacks repeat, and there aren't GUI load file open buttons] - Super Scope and Justifier crosshairs render correctly (probably doesn't belong in the core, but it's not something I suspect people want to do themselves ...) - you can load GB, SGB, GB, SGB ... without problems (not happy with how I did this, but I don't want to add an Interface::setInterface() function yet) - PAL timing works as I want now (if you want 50fps on a 60hz monitor, you must not use sync video) [needed to update the DSP frequency when toggling video/audio sync] - not going to save input port selection for now (lot of work), but it will properly keep your port setting across cartridge loads at least [just goes to controller on emulator restart] - SFC overscan on and off both work as expected now (off centers image, on shows entire image) - laevateinn compiles properly now - ethos goes to ~/.config/bsnes now that target-ui is dead [honestly, I recommend deleting the old folder and starting over] - Emulator::Interface callbacks converted to virtual binding structure that GUI inherits from (simplifies binding callbacks) - this breaks Super Game Boy for a bit, I need to rethink system-specific bindings without direct inheritance Timing analysis works spectacularly well on Windows, too. You won't get your 100% perfect rate (unless maybe you leave the analysis running overnight?), but it'll get really freaking close this way.
This commit is contained in:
parent
cb97d98ad2
commit
689fc49047
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const char Name[] = "bsnes";
|
static const char Name[] = "bsnes";
|
||||||
static const char Version[] = "088.14";
|
static const char Version[] = "088.15";
|
||||||
static const char Author[] = "byuu";
|
static const char Author[] = "byuu";
|
||||||
static const char License[] = "GPLv3";
|
static const char License[] = "GPLv3";
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ namespace Emulator {
|
||||||
#include <nall/varint.hpp>
|
#include <nall/varint.hpp>
|
||||||
#include <nall/vector.hpp>
|
#include <nall/vector.hpp>
|
||||||
#include <nall/stream/memory.hpp>
|
#include <nall/stream/memory.hpp>
|
||||||
|
#include <nall/stream/vector.hpp>
|
||||||
using namespace nall;
|
using namespace nall;
|
||||||
|
|
||||||
#include "interface.hpp"
|
#include "interface.hpp"
|
||||||
|
|
|
@ -44,7 +44,6 @@ struct Interface {
|
||||||
vector<Input> input;
|
vector<Input> input;
|
||||||
vector<unsigned> order;
|
vector<unsigned> order;
|
||||||
};
|
};
|
||||||
vector<Device> device;
|
|
||||||
|
|
||||||
struct Port {
|
struct Port {
|
||||||
unsigned id;
|
unsigned id;
|
||||||
|
@ -53,48 +52,26 @@ struct Interface {
|
||||||
};
|
};
|
||||||
vector<Port> port;
|
vector<Port> port;
|
||||||
|
|
||||||
struct Callback {
|
struct Bind {
|
||||||
function<uint32_t (unsigned, uint16_t, uint16_t, uint16_t)> videoColor;
|
virtual void loadRequest(unsigned, const string&) {}
|
||||||
function<void (const uint32_t*, unsigned, unsigned, unsigned)> videoRefresh;
|
virtual void loadRequest(unsigned, const string&, const string&, const string&) {}
|
||||||
function<void (int16_t, int16_t)> audioSample;
|
virtual uint32_t videoColor(unsigned, uint16_t, uint16_t, uint16_t) { return 0u; }
|
||||||
function<int16_t (unsigned, unsigned, unsigned)> inputPoll;
|
virtual void videoRefresh(const uint32_t*, unsigned, unsigned, unsigned) {}
|
||||||
function<void (Media)> mediaRequest;
|
virtual void audioSample(int16_t, int16_t) {}
|
||||||
function<unsigned (const XML::Node&)> dipSettings;
|
virtual int16_t inputPoll(unsigned, unsigned, unsigned) { return 0; }
|
||||||
function<string (unsigned)> path;
|
virtual unsigned dipSettings(const XML::Node&) { return 0; }
|
||||||
} callback;
|
virtual string path(unsigned) { return ""; }
|
||||||
|
} *bind;
|
||||||
|
|
||||||
//callback bindings (provided by user interface)
|
//callback bindings (provided by user interface)
|
||||||
virtual uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) {
|
void loadRequest(unsigned id, const string &path) { return bind->loadRequest(id, path); }
|
||||||
if(callback.videoColor) return callback.videoColor(source, red, green, blue);
|
void loadRequest(unsigned id, const string &name, const string &type, const string &path) { return bind->loadRequest(id, name, type, path); }
|
||||||
return (red >> 8) << 16 | (green >> 8) << 8 | (blue >> 8) << 0;
|
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); }
|
||||||
virtual void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height) {
|
int16_t inputPoll(unsigned port, unsigned device, unsigned input) { return bind->inputPoll(port, device, input); }
|
||||||
if(callback.videoRefresh) return callback.videoRefresh(data, pitch, width, height);
|
unsigned dipSettings(const XML::Node &node) { return bind->dipSettings(node); }
|
||||||
}
|
string path(unsigned group) { return bind->path(group); }
|
||||||
|
|
||||||
virtual void audioSample(int16_t lsample, int16_t rsample) {
|
|
||||||
if(callback.audioSample) return callback.audioSample(lsample, rsample);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int16_t inputPoll(unsigned port, unsigned device, unsigned input) {
|
|
||||||
if(callback.inputPoll) return callback.inputPoll(port, device, input);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void mediaRequest(Media media) {
|
|
||||||
if(callback.mediaRequest) return callback.mediaRequest(media);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual unsigned dipSettings(const XML::Node &node) {
|
|
||||||
if(callback.dipSettings) return callback.dipSettings(node);
|
|
||||||
return 0u;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual string path(unsigned group) {
|
|
||||||
if(callback.path) return callback.path(group);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
//information
|
//information
|
||||||
virtual double videoFrequency() = 0;
|
virtual double videoFrequency() = 0;
|
||||||
|
@ -123,6 +100,8 @@ struct Interface {
|
||||||
|
|
||||||
//utility functions
|
//utility functions
|
||||||
virtual void updatePalette() {}
|
virtual void updatePalette() {}
|
||||||
|
|
||||||
|
Interface() : bind(nullptr) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,12 +107,12 @@ Interface::Interface() {
|
||||||
this->device.append(device);
|
this->device.append(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
port.append({ID::Port1, "Port 1"});
|
port.append({0, "Port 1"});
|
||||||
port.append({ID::Port2, "Port 2"});
|
port.append({1, "Port 2"});
|
||||||
|
|
||||||
for(auto &device : this->device) {
|
for(auto &device : this->device) {
|
||||||
for(auto &port : this->port) {
|
for(auto &port : this->port) {
|
||||||
if(device.portmask & port.id) {
|
if(device.portmask & (1 << port.id)) {
|
||||||
port.device.append(device);
|
port.device.append(device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,9 @@ struct Interface : Emulator::Interface {
|
||||||
void updatePalette();
|
void updatePalette();
|
||||||
|
|
||||||
Interface();
|
Interface();
|
||||||
|
|
||||||
|
private:
|
||||||
|
vector<Device> device;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Interface *interface;
|
extern Interface *interface;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
options += gameboy
|
|
||||||
|
|
||||||
gb_objects := gb-interface gb-system gb-scheduler
|
gb_objects := gb-interface gb-system gb-scheduler
|
||||||
gb_objects += gb-memory gb-cartridge
|
gb_objects += gb-memory gb-cartridge
|
||||||
gb_objects += gb-cpu gb-ppu gb-apu
|
gb_objects += gb-cpu gb-ppu gb-apu
|
||||||
|
|
|
@ -142,8 +142,8 @@ void Cartridge::power() {
|
||||||
|
|
||||||
Cartridge::Cartridge() {
|
Cartridge::Cartridge() {
|
||||||
loaded = false;
|
loaded = false;
|
||||||
romdata = 0;
|
romdata = nullptr;
|
||||||
ramdata = 0;
|
ramdata = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cartridge::~Cartridge() {
|
Cartridge::~Cartridge() {
|
||||||
|
|
|
@ -127,7 +127,7 @@ Interface::Interface() {
|
||||||
this->device.append(device);
|
this->device.append(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
port.append({ID::Device, "Device", {device[0]}});
|
port.append({0, "Device", {device[0]}});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,9 @@ struct Interface : Emulator::Interface {
|
||||||
void updatePalette();
|
void updatePalette();
|
||||||
|
|
||||||
Interface();
|
Interface();
|
||||||
|
|
||||||
|
private:
|
||||||
|
vector<Device> device;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Interface *interface;
|
extern Interface *interface;
|
||||||
|
|
|
@ -114,7 +114,7 @@ Interface::Interface() {
|
||||||
this->device.append(device);
|
this->device.append(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
port.append({ID::Device, "Device", {device[0]}});
|
port.append({0, "Device", {device[0]}});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,9 @@ struct Interface : Emulator::Interface {
|
||||||
void updatePalette();
|
void updatePalette();
|
||||||
|
|
||||||
Interface();
|
Interface();
|
||||||
|
|
||||||
|
private:
|
||||||
|
vector<Device> device;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Interface *interface;
|
extern Interface *interface;
|
||||||
|
|
|
@ -176,6 +176,7 @@ void pWindow::setTitle(const string &text) {
|
||||||
|
|
||||||
void pWindow::setVisible(bool visible) {
|
void pWindow::setVisible(bool visible) {
|
||||||
ShowWindow(hwnd, visible ? SW_SHOWNORMAL : SW_HIDE);
|
ShowWindow(hwnd, visible ? SW_SHOWNORMAL : SW_HIDE);
|
||||||
|
if(visible == false) setModal(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pWindow::setWidgetFont(const string &font) {
|
void pWindow::setWidgetFont(const string &font) {
|
||||||
|
|
|
@ -319,8 +319,14 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if(settings.synchronize) {
|
if(settings.synchronize) {
|
||||||
while(true) {
|
|
||||||
D3DRASTER_STATUS status;
|
D3DRASTER_STATUS status;
|
||||||
|
//wait for a previous vblank to finish, if necessary
|
||||||
|
while(true) {
|
||||||
|
device->GetRasterStatus(0, &status);
|
||||||
|
if(status.InVBlank == false) break;
|
||||||
|
}
|
||||||
|
//wait for next vblank to begin
|
||||||
|
while(true) {
|
||||||
device->GetRasterStatus(0, &status);
|
device->GetRasterStatus(0, &status);
|
||||||
if(status.InVBlank == true) break;
|
if(status.InVBlank == true) break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,11 +93,17 @@ void Cartridge::parse_markup_nss(XML::Node &root) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::parse_markup_icd2(XML::Node &root) {
|
void Cartridge::parse_markup_icd2(XML::Node &root) {
|
||||||
#if defined(GAMEBOY)
|
|
||||||
if(root.exists() == false) return;
|
if(root.exists() == false) return;
|
||||||
has_gb_slot = true;
|
has_gb_slot = true;
|
||||||
|
|
||||||
interface->mediaRequest({ID::SuperGameBoyROM, "Game Boy", "", "program.rom", "gb"});
|
//Game Boy requires a cartridge to be loaded ...
|
||||||
|
//load "empty" cartridge, in case loadRequest() does not load one
|
||||||
|
vector<uint8> stream;
|
||||||
|
stream.resize(32768);
|
||||||
|
for(auto &byte : stream) byte = 0xff;
|
||||||
|
interface->load(ID::SuperGameBoyROM, vectorstream{stream}, "");
|
||||||
|
|
||||||
|
interface->loadRequest(ID::SuperGameBoyROM, "Game Boy", "gb", "program.rom");
|
||||||
|
|
||||||
icd2.revision = max(1, numeral(root["revision"].data));
|
icd2.revision = max(1, numeral(root["revision"].data));
|
||||||
|
|
||||||
|
@ -107,7 +113,6 @@ void Cartridge::parse_markup_icd2(XML::Node &root) {
|
||||||
parse_markup_map(m, node);
|
parse_markup_map(m, node);
|
||||||
mapping.append(m);
|
mapping.append(m);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::parse_markup_superfx(XML::Node &root) {
|
void Cartridge::parse_markup_superfx(XML::Node &root) {
|
||||||
|
@ -213,11 +218,11 @@ void Cartridge::parse_markup_necdsp(XML::Node &root) {
|
||||||
string sha256 = root["sha256"].data;
|
string sha256 = root["sha256"].data;
|
||||||
|
|
||||||
if(necdsp.revision == NECDSP::Revision::uPD7725) {
|
if(necdsp.revision == NECDSP::Revision::uPD7725) {
|
||||||
interface->mediaRequest({ID::Nec7725DSP, "", "", firmware});
|
interface->loadRequest(ID::Nec7725DSP, firmware);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(necdsp.revision == NECDSP::Revision::uPD96050) {
|
if(necdsp.revision == NECDSP::Revision::uPD96050) {
|
||||||
interface->mediaRequest({ID::Nec96050DSP, "", "", firmware});
|
interface->loadRequest(ID::Nec96050DSP, firmware);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto &node : root) {
|
for(auto &node : root) {
|
||||||
|
@ -256,7 +261,7 @@ void Cartridge::parse_markup_hitachidsp(XML::Node &root) {
|
||||||
string firmware = root["firmware"].data;
|
string firmware = root["firmware"].data;
|
||||||
string sha256 = root["sha256"].data;
|
string sha256 = root["sha256"].data;
|
||||||
|
|
||||||
interface->mediaRequest({ID::HitachiDSP, "", "", firmware});
|
interface->loadRequest(ID::HitachiDSP, firmware);
|
||||||
|
|
||||||
for(auto &node : root) {
|
for(auto &node : root) {
|
||||||
if(node.name == "rom") {
|
if(node.name == "rom") {
|
||||||
|
@ -284,7 +289,7 @@ void Cartridge::parse_markup_armdsp(XML::Node &root) {
|
||||||
string firmware = root["firmware"].data;
|
string firmware = root["firmware"].data;
|
||||||
string sha256 = root["sha256"].data;
|
string sha256 = root["sha256"].data;
|
||||||
|
|
||||||
interface->mediaRequest({ID::ArmDSP, "", "", firmware});
|
interface->loadRequest(ID::ArmDSP, firmware);
|
||||||
|
|
||||||
for(auto &node : root) {
|
for(auto &node : root) {
|
||||||
if(node.name != "map") continue;
|
if(node.name != "map") continue;
|
||||||
|
@ -299,7 +304,7 @@ void Cartridge::parse_markup_bsx(XML::Node &root) {
|
||||||
has_bs_cart = root["mmio"].exists();
|
has_bs_cart = root["mmio"].exists();
|
||||||
has_bs_slot = true;
|
has_bs_slot = true;
|
||||||
|
|
||||||
interface->mediaRequest({ID::BsxFlashROM, "BS-X Satellaview", "", "program.rom", "bs"});
|
interface->loadRequest(ID::BsxFlashROM, "BS-X Satellaview", "bs", "program.rom");
|
||||||
|
|
||||||
for(auto &node : root["slot"]) {
|
for(auto &node : root["slot"]) {
|
||||||
if(node.name != "map") continue;
|
if(node.name != "map") continue;
|
||||||
|
@ -327,8 +332,8 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
|
||||||
if(root.exists() == false) return;
|
if(root.exists() == false) return;
|
||||||
has_st_slots = true;
|
has_st_slots = true;
|
||||||
|
|
||||||
interface->mediaRequest({ID::SufamiTurboSlotAROM, "Sufami Turbo - Slot A", "", "program.rom", "st"});
|
interface->loadRequest(ID::SufamiTurboSlotAROM, "Sufami Turbo - Slot A", "st", "program.rom");
|
||||||
interface->mediaRequest({ID::SufamiTurboSlotBROM, "Sufami Turbo - Slot B", "", "program.rom", "st"});
|
interface->loadRequest(ID::SufamiTurboSlotBROM, "Sufami Turbo - Slot B", "st", "program.rom");
|
||||||
|
|
||||||
for(auto &slot : root) {
|
for(auto &slot : root) {
|
||||||
if(slot.name != "slot") continue;
|
if(slot.name != "slot") continue;
|
||||||
|
|
|
@ -3,10 +3,7 @@ struct Coprocessor : Thread {
|
||||||
alwaysinline void synchronize_cpu();
|
alwaysinline void synchronize_cpu();
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(GAMEBOY)
|
|
||||||
#include <sfc/chip/icd2/icd2.hpp>
|
#include <sfc/chip/icd2/icd2.hpp>
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <sfc/chip/nss/nss.hpp>
|
#include <sfc/chip/nss/nss.hpp>
|
||||||
#include <sfc/chip/superfx/superfx.hpp>
|
#include <sfc/chip/superfx/superfx.hpp>
|
||||||
#include <sfc/chip/sa1/sa1.hpp>
|
#include <sfc/chip/sa1/sa1.hpp>
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#if defined(GAMEBOY)
|
|
||||||
|
|
||||||
#include <sfc/sfc.hpp>
|
#include <sfc/sfc.hpp>
|
||||||
|
|
||||||
#define ICD2_CPP
|
#define ICD2_CPP
|
||||||
|
@ -35,9 +33,12 @@ void ICD2::init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICD2::load() {
|
void ICD2::load() {
|
||||||
|
interface = GameBoy::interface->bind;
|
||||||
|
GameBoy::interface->bind = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICD2::unload() {
|
void ICD2::unload() {
|
||||||
|
GameBoy::interface->bind = interface;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICD2::power() {
|
void ICD2::power() {
|
||||||
|
@ -69,12 +70,9 @@ void ICD2::reset() {
|
||||||
joyp14lock = 0;
|
joyp14lock = 0;
|
||||||
pulselock = true;
|
pulselock = true;
|
||||||
|
|
||||||
GameBoy::interface = this;
|
|
||||||
GameBoy::video.generate_palette();
|
GameBoy::video.generate_palette();
|
||||||
GameBoy::system.init();
|
GameBoy::system.init();
|
||||||
GameBoy::system.power();
|
GameBoy::system.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
class ICD2 : GameBoy::Interface, public Coprocessor {
|
struct ICD2 : Emulator::Interface::Bind, Coprocessor {
|
||||||
public:
|
|
||||||
unsigned revision;
|
unsigned revision;
|
||||||
|
|
||||||
static void Enter();
|
static void Enter();
|
||||||
|
@ -17,6 +16,7 @@ public:
|
||||||
void serialize(serializer&);
|
void serialize(serializer&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Emulator::Interface::Bind *interface;
|
||||||
#include "interface/interface.hpp"
|
#include "interface/interface.hpp"
|
||||||
#include "mmio/mmio.hpp"
|
#include "mmio/mmio.hpp"
|
||||||
};
|
};
|
||||||
|
|
|
@ -79,8 +79,6 @@ void Interface::load(unsigned id, const stream &stream, const string &markup) {
|
||||||
if(id == ID::ROM) {
|
if(id == ID::ROM) {
|
||||||
cartridge.load(markup, stream);
|
cartridge.load(markup, stream);
|
||||||
system.power();
|
system.power();
|
||||||
input.connect(0, Input::Device::Joypad);
|
|
||||||
input.connect(1, Input::Device::Joypad);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(id == ID::SuperGameBoyROM) {
|
if(id == ID::SuperGameBoyROM) {
|
||||||
|
@ -342,12 +340,12 @@ Interface::Interface() {
|
||||||
this->device.append(device);
|
this->device.append(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
port.append({ID::Port1, "Port 1"});
|
port.append({0, "Port 1"});
|
||||||
port.append({ID::Port2, "Port 2"});
|
port.append({1, "Port 2"});
|
||||||
|
|
||||||
for(auto &device : this->device) {
|
for(auto &device : this->device) {
|
||||||
for(auto &port : this->port) {
|
for(auto &port : this->port) {
|
||||||
if(device.portmask & port.id) {
|
if(device.portmask & (1 << port.id)) {
|
||||||
port.device.append(device);
|
port.device.append(device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,9 @@ struct Interface : Emulator::Interface {
|
||||||
void updatePalette();
|
void updatePalette();
|
||||||
|
|
||||||
Interface();
|
Interface();
|
||||||
|
|
||||||
|
private:
|
||||||
|
vector<Device> device;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Interface *interface;
|
extern Interface *interface;
|
||||||
|
|
|
@ -33,7 +33,7 @@ void PPU::enter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
scanline();
|
scanline();
|
||||||
add_clocks(68);
|
add_clocks(28);
|
||||||
bg1.begin();
|
bg1.begin();
|
||||||
bg2.begin();
|
bg2.begin();
|
||||||
bg3.begin();
|
bg3.begin();
|
||||||
|
@ -65,7 +65,7 @@ void PPU::enter() {
|
||||||
add_clocks(1052 + 14 + 136);
|
add_clocks(1052 + 14 + 136);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_clocks(lineclocks() - 68 - 1052 - 14 - 136);
|
add_clocks(lineclocks() - 28 - 1052 - 14 - 136);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,10 +24,7 @@ namespace SuperFamicom {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libco/libco.h>
|
#include <libco/libco.h>
|
||||||
|
#include <gb/gb.hpp>
|
||||||
#if defined(GAMEBOY)
|
|
||||||
#include <gb/gb.hpp>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace SuperFamicom {
|
namespace SuperFamicom {
|
||||||
struct Thread {
|
struct Thread {
|
||||||
|
|
|
@ -57,9 +57,7 @@ void System::serialize_all(serializer &s) {
|
||||||
ppu.serialize(s);
|
ppu.serialize(s);
|
||||||
dsp.serialize(s);
|
dsp.serialize(s);
|
||||||
|
|
||||||
#if defined(GAMEBOY)
|
|
||||||
if(cartridge.has_gb_slot()) icd2.serialize(s);
|
if(cartridge.has_gb_slot()) icd2.serialize(s);
|
||||||
#endif
|
|
||||||
if(cartridge.has_st_slots()) sufamiturbo.serialize(s);
|
if(cartridge.has_st_slots()) sufamiturbo.serialize(s);
|
||||||
if(cartridge.has_superfx()) superfx.serialize(s);
|
if(cartridge.has_superfx()) superfx.serialize(s);
|
||||||
if(cartridge.has_sa1()) sa1.serialize(s);
|
if(cartridge.has_sa1()) sa1.serialize(s);
|
||||||
|
|
|
@ -64,9 +64,7 @@ void System::runthreadtosave() {
|
||||||
void System::init() {
|
void System::init() {
|
||||||
assert(interface != 0);
|
assert(interface != 0);
|
||||||
|
|
||||||
#if defined(GAMEBOY)
|
|
||||||
icd2.init();
|
icd2.init();
|
||||||
#endif
|
|
||||||
nss.init();
|
nss.init();
|
||||||
superfx.init();
|
superfx.init();
|
||||||
sa1.init();
|
sa1.init();
|
||||||
|
@ -112,9 +110,7 @@ void System::load() {
|
||||||
ppu.enable();
|
ppu.enable();
|
||||||
|
|
||||||
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.load();
|
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.load();
|
||||||
#if defined(GAMEBOY)
|
|
||||||
if(cartridge.has_gb_slot()) icd2.load();
|
if(cartridge.has_gb_slot()) icd2.load();
|
||||||
#endif
|
|
||||||
if(cartridge.has_bs_cart()) bsxcartridge.load();
|
if(cartridge.has_bs_cart()) bsxcartridge.load();
|
||||||
if(cartridge.has_bs_slot()) bsxflash.load();
|
if(cartridge.has_bs_slot()) bsxflash.load();
|
||||||
if(cartridge.has_st_slots()) sufamiturbo.load();
|
if(cartridge.has_st_slots()) sufamiturbo.load();
|
||||||
|
@ -137,9 +133,7 @@ void System::load() {
|
||||||
|
|
||||||
void System::unload() {
|
void System::unload() {
|
||||||
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.unload();
|
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.unload();
|
||||||
#if defined(GAMEBOY)
|
|
||||||
if(cartridge.has_gb_slot()) icd2.unload();
|
if(cartridge.has_gb_slot()) icd2.unload();
|
||||||
#endif
|
|
||||||
if(cartridge.has_bs_cart()) bsxcartridge.unload();
|
if(cartridge.has_bs_cart()) bsxcartridge.unload();
|
||||||
if(cartridge.has_bs_slot()) bsxflash.unload();
|
if(cartridge.has_bs_slot()) bsxflash.unload();
|
||||||
if(cartridge.has_st_slots()) sufamiturbo.unload();
|
if(cartridge.has_st_slots()) sufamiturbo.unload();
|
||||||
|
@ -166,9 +160,7 @@ void System::power() {
|
||||||
ppu.power();
|
ppu.power();
|
||||||
|
|
||||||
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.power();
|
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.power();
|
||||||
#if defined(GAMEBOY)
|
|
||||||
if(cartridge.has_gb_slot()) icd2.power();
|
if(cartridge.has_gb_slot()) icd2.power();
|
||||||
#endif
|
|
||||||
if(cartridge.has_bs_cart()) bsxcartridge.power();
|
if(cartridge.has_bs_cart()) bsxcartridge.power();
|
||||||
if(cartridge.has_bs_slot()) bsxflash.power();
|
if(cartridge.has_bs_slot()) bsxflash.power();
|
||||||
if(cartridge.has_nss_dip()) nss.power();
|
if(cartridge.has_nss_dip()) nss.power();
|
||||||
|
@ -194,9 +186,7 @@ void System::reset() {
|
||||||
ppu.reset();
|
ppu.reset();
|
||||||
|
|
||||||
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.reset();
|
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.reset();
|
||||||
#if defined(GAMEBOY)
|
|
||||||
if(cartridge.has_gb_slot()) icd2.reset();
|
if(cartridge.has_gb_slot()) icd2.reset();
|
||||||
#endif
|
|
||||||
if(cartridge.has_bs_cart()) bsxcartridge.reset();
|
if(cartridge.has_bs_cart()) bsxcartridge.reset();
|
||||||
if(cartridge.has_bs_slot()) bsxflash.reset();
|
if(cartridge.has_bs_slot()) bsxflash.reset();
|
||||||
if(cartridge.has_nss_dip()) nss.reset();
|
if(cartridge.has_nss_dip()) nss.reset();
|
||||||
|
@ -212,9 +202,7 @@ void System::reset() {
|
||||||
if(cartridge.has_msu1()) msu1.reset();
|
if(cartridge.has_msu1()) msu1.reset();
|
||||||
if(cartridge.has_link()) link.reset();
|
if(cartridge.has_link()) link.reset();
|
||||||
|
|
||||||
#if defined(GAMEBOY)
|
|
||||||
if(cartridge.has_gb_slot()) cpu.coprocessors.append(&icd2);
|
if(cartridge.has_gb_slot()) cpu.coprocessors.append(&icd2);
|
||||||
#endif
|
|
||||||
if(cartridge.has_superfx()) cpu.coprocessors.append(&superfx);
|
if(cartridge.has_superfx()) cpu.coprocessors.append(&superfx);
|
||||||
if(cartridge.has_sa1()) cpu.coprocessors.append(&sa1);
|
if(cartridge.has_sa1()) cpu.coprocessors.append(&sa1);
|
||||||
if(cartridge.has_necdsp()) cpu.coprocessors.append(&necdsp);
|
if(cartridge.has_necdsp()) cpu.coprocessors.append(&necdsp);
|
||||||
|
|
|
@ -64,10 +64,10 @@ void Video::draw_cursor(uint16_t color, int x, int y) {
|
||||||
uint32_t pixelcolor = (15 << 15) | ((pixel == 1) ? 0 : color);
|
uint32_t pixelcolor = (15 << 15) | ((pixel == 1) ? 0 : color);
|
||||||
|
|
||||||
if(hires == false) {
|
if(hires == false) {
|
||||||
*((uint32_t*)data + vy * 1024 + vx) = pixelcolor;
|
*((uint32_t*)data + vy * 1024 + vx) = palette[pixelcolor];
|
||||||
} else {
|
} else {
|
||||||
*((uint32_t*)data + vy * 1024 + vx * 2 + 0) = pixelcolor;
|
*((uint32_t*)data + vy * 1024 + vx * 2 + 0) = palette[pixelcolor];
|
||||||
*((uint32_t*)data + vy * 1024 + vx * 2 + 1) = pixelcolor;
|
*((uint32_t*)data + vy * 1024 + vx * 2 + 1) = palette[pixelcolor];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,14 @@ void Video::update() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface->videoRefresh(ppu.surface, 4 * (1024 >> ppu.interlace()), 256 << hires, 240 << ppu.interlace());
|
//overscan: when disabled, shift image down (by scrolling video buffer up) to center image onscreen
|
||||||
|
//(memory before ppu.output is filled with black scanlines)
|
||||||
|
interface->videoRefresh(
|
||||||
|
ppu.output - (ppu.overscan() ? 0 : 7 * 1024),
|
||||||
|
4 * (1024 >> ppu.interlace()),
|
||||||
|
256 << hires,
|
||||||
|
240 << ppu.interlace()
|
||||||
|
);
|
||||||
|
|
||||||
hires = false;
|
hires = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ include fc/Makefile
|
||||||
include sfc/Makefile
|
include sfc/Makefile
|
||||||
include gb/Makefile
|
include gb/Makefile
|
||||||
include gba/Makefile
|
include gba/Makefile
|
||||||
name := ethos
|
name := bsnes
|
||||||
|
|
||||||
ui_objects := ui-ethos ui-configuration ui-interface ui-utility
|
ui_objects := ui-ethos ui-configuration ui-interface ui-utility
|
||||||
ui_objects += ui-input ui-window ui-general ui-settings ui-tools
|
ui_objects += ui-input ui-window ui-general ui-settings ui-tools
|
||||||
|
|
|
@ -12,13 +12,7 @@ void Application::bootstrap() {
|
||||||
emulator.append(new GameBoyAdvance::Interface);
|
emulator.append(new GameBoyAdvance::Interface);
|
||||||
|
|
||||||
for(auto &system : emulator) {
|
for(auto &system : emulator) {
|
||||||
system->callback.videoColor = {&Interface::videoColor, interface};
|
system->bind = interface;
|
||||||
system->callback.videoRefresh = {&Interface::videoRefresh, interface};
|
|
||||||
system->callback.audioSample = {&Interface::audioSample, interface};
|
|
||||||
system->callback.inputPoll = {&Interface::inputPoll, interface};
|
|
||||||
system->callback.mediaRequest = {&Interface::mediaRequest, interface};
|
|
||||||
system->callback.dipSettings = {&Interface::dipSettings, interface};
|
|
||||||
system->callback.path = {&Interface::path, interface};
|
|
||||||
|
|
||||||
for(auto &firmware : system->firmware) {
|
for(auto &firmware : system->firmware) {
|
||||||
filestream fs{application->path({firmware.name, ".", firmware.type, "/", firmware.path})};
|
filestream fs{application->path({firmware.name, ".", firmware.type, "/", firmware.path})};
|
||||||
|
|
|
@ -41,11 +41,8 @@ Application::Application(int argc, char **argv) {
|
||||||
basepath = dir(path);
|
basepath = dir(path);
|
||||||
unused = ::userpath(path);
|
unused = ::userpath(path);
|
||||||
userpath = path;
|
userpath = path;
|
||||||
if(Intrinsics::platform() == Intrinsics::Platform::Windows) {
|
if(Intrinsics::platform() != Intrinsics::Platform::Windows) userpath.append(".config/");
|
||||||
userpath.append("ethos/");
|
userpath.append("bsnes/");
|
||||||
} else {
|
|
||||||
userpath.append(".config/ethos/");
|
|
||||||
}
|
|
||||||
directory::create(userpath);
|
directory::create(userpath);
|
||||||
|
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|
|
@ -91,6 +91,24 @@ void Browser::bootstrap() {
|
||||||
config.save(application->path("paths.cfg"));
|
config.save(application->path("paths.cfg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Browser::inputEvent(unsigned scancode, int16_t value) {
|
||||||
|
if(scancode == keyboard(0)[nall::Keyboard::Escape] && value == 0) onClose();
|
||||||
|
|
||||||
|
//proof of concept only, not very useful:
|
||||||
|
//joypad up/down moves around in the file list, any joypad button loads selected game
|
||||||
|
if(fileList.selected() == false) return;
|
||||||
|
unsigned selection = fileList.selection();
|
||||||
|
|
||||||
|
if(Joypad::isAnyHat(scancode)) {
|
||||||
|
if(value & Joypad::HatUp ) fileList.setSelection(selection - 1);
|
||||||
|
if(value & Joypad::HatDown) fileList.setSelection(selection + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Joypad::isAnyButton(scancode) && value == 1) {
|
||||||
|
fileList.onActivate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string Browser::select(const string &title, const string &extension) {
|
string Browser::select(const string &title, const string &extension) {
|
||||||
this->extension = extension;
|
this->extension = extension;
|
||||||
|
|
||||||
|
@ -110,15 +128,20 @@ string Browser::select(const string &title, const string &extension) {
|
||||||
|
|
||||||
audio.clear();
|
audio.clear();
|
||||||
setTitle(title);
|
setTitle(title);
|
||||||
setModal();
|
setModal(true);
|
||||||
setVisible();
|
setVisible(true);
|
||||||
fileList.setFocused();
|
fileList.setFocused();
|
||||||
dialogActive = true;
|
|
||||||
outputFilename = "";
|
outputFilename = "";
|
||||||
|
|
||||||
|
dialogActive = true;
|
||||||
while(dialogActive == true) {
|
while(dialogActive == true) {
|
||||||
OS::processEvents();
|
OS::processEvents();
|
||||||
|
inputManager->poll();
|
||||||
|
usleep(20 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setModal(false);
|
||||||
|
setVisible(false);
|
||||||
return outputFilename;
|
return outputFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +192,6 @@ void Browser::fileListActivate() {
|
||||||
string suffix = {this->extension, "/"};
|
string suffix = {this->extension, "/"};
|
||||||
if(filename.endswith(suffix) == false) return setPath({path, filename});
|
if(filename.endswith(suffix) == false) return setPath({path, filename});
|
||||||
|
|
||||||
setVisible(false);
|
|
||||||
dialogActive = false;
|
dialogActive = false;
|
||||||
outputFilename = {path, filename};
|
outputFilename = {path, filename};
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ struct Browser : Window {
|
||||||
Button openButton;
|
Button openButton;
|
||||||
|
|
||||||
string select(const string &title, const string &extension);
|
string select(const string &title, const string &extension);
|
||||||
|
void inputEvent(unsigned scancode, int16_t value);
|
||||||
void saveConfiguration();
|
void saveConfiguration();
|
||||||
void synchronize();
|
void synchronize();
|
||||||
void bootstrap();
|
void bootstrap();
|
||||||
|
|
|
@ -39,7 +39,7 @@ void Presentation::setSystemName(const string &name) {
|
||||||
Presentation::Presentation() : active(nullptr) {
|
Presentation::Presentation() : active(nullptr) {
|
||||||
bootstrap();
|
bootstrap();
|
||||||
loadShaders();
|
loadShaders();
|
||||||
setGeometry({1024, 600, 720, 480});
|
setGeometry({256, 256, 720, 480});
|
||||||
windowManager->append(this, "Presentation");
|
windowManager->append(this, "Presentation");
|
||||||
|
|
||||||
setTitle({::Emulator::Name, " v", ::Emulator::Version});
|
setTitle({::Emulator::Name, " v", ::Emulator::Version});
|
||||||
|
@ -144,24 +144,20 @@ void Presentation::bootstrap() {
|
||||||
iEmulator->reset.setText("Reset");
|
iEmulator->reset.setText("Reset");
|
||||||
iEmulator->unload.setText("Unload");
|
iEmulator->unload.setText("Unload");
|
||||||
|
|
||||||
unsigned portNumber = 0;
|
|
||||||
for(auto &port : emulator->port) {
|
for(auto &port : emulator->port) {
|
||||||
auto iPort = new Emulator::Port;
|
auto iPort = new Emulator::Port;
|
||||||
iPort->menu.setText(port.name);
|
iPort->menu.setText(port.name);
|
||||||
iEmulator->port.append(iPort);
|
iEmulator->port.append(iPort);
|
||||||
|
|
||||||
unsigned deviceNumber = 0;
|
|
||||||
for(auto &device : port.device) {
|
for(auto &device : port.device) {
|
||||||
auto iDevice = new RadioItem;
|
auto iDevice = new RadioItem;
|
||||||
iDevice->setText(device.name);
|
iDevice->setText(device.name);
|
||||||
iDevice->onActivate = [=] { utility->connect(portNumber, device.id); };
|
iDevice->onActivate = [=] { utility->connect(port.id, device.id); };
|
||||||
iPort->group.append(*iDevice);
|
iPort->group.append(*iDevice);
|
||||||
iPort->device.append(iDevice);
|
iPort->device.append(iDevice);
|
||||||
deviceNumber++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RadioItem::group(iPort->group);
|
RadioItem::group(iPort->group);
|
||||||
portNumber++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iEmulator->menu.append(iEmulator->power);
|
iEmulator->menu.append(iEmulator->power);
|
||||||
|
|
|
@ -54,7 +54,6 @@ struct Presentation : Window {
|
||||||
void bootstrap();
|
void bootstrap();
|
||||||
Presentation();
|
Presentation();
|
||||||
|
|
||||||
private:
|
|
||||||
Emulator *active;
|
Emulator *active;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -157,11 +157,16 @@ void InputManager::bind() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputManager::poll() {
|
void InputManager::poll() {
|
||||||
|
using nall::Keyboard;
|
||||||
|
|
||||||
activeScancode = !activeScancode;
|
activeScancode = !activeScancode;
|
||||||
input.poll(scancode[activeScancode]);
|
input.poll(scancode[activeScancode]);
|
||||||
|
|
||||||
for(unsigned n = 0; n < Scancode::Limit; n++) {
|
for(unsigned n = 0; n < Scancode::Limit; n++) {
|
||||||
if(scancode[0][n] != scancode[1][n]) {
|
if(scancode[0][n] != scancode[1][n]) {
|
||||||
|
if(browser->focused()) {
|
||||||
|
browser->inputEvent(n, scancode[activeScancode][n]);
|
||||||
|
}
|
||||||
if(settings->focused()) {
|
if(settings->focused()) {
|
||||||
inputSettings->inputEvent(n, scancode[activeScancode][n]);
|
inputSettings->inputEvent(n, scancode[activeScancode][n]);
|
||||||
hotkeySettings->inputEvent(n, scancode[activeScancode][n]);
|
hotkeySettings->inputEvent(n, scancode[activeScancode][n]);
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
#include "../ethos.hpp"
|
#include "../ethos.hpp"
|
||||||
Interface *interface = nullptr;
|
Interface *interface = nullptr;
|
||||||
|
|
||||||
|
void Interface::loadRequest(unsigned id, const string &path) {
|
||||||
|
return utility->loadMedia(id, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface::loadRequest(unsigned id, const string &name, const string &type, const string &path) {
|
||||||
|
if(name.empty() && type.empty()) return utility->loadMedia(id, path);
|
||||||
|
return utility->loadMedia(id, name, type, path);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t Interface::videoColor(unsigned source, uint16_t r, uint16_t g, uint16_t b) {
|
uint32_t Interface::videoColor(unsigned source, uint16_t r, uint16_t g, uint16_t b) {
|
||||||
if(config->video.saturation != 100) {
|
if(config->video.saturation != 100) {
|
||||||
uint16_t grayscale = uclamp<16>((r + g + b) / 3);
|
uint16_t grayscale = uclamp<16>((r + g + b) / 3);
|
||||||
|
@ -96,10 +105,6 @@ int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) {
|
||||||
return inputManager->inputMap[guid]->poll();
|
return inputManager->inputMap[guid]->poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::mediaRequest(Emulator::Interface::Media media) {
|
|
||||||
utility->loadMedia(media);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned Interface::dipSettings(const XML::Node &node) {
|
unsigned Interface::dipSettings(const XML::Node &node) {
|
||||||
return dipSwitches->run(node);
|
return dipSwitches->run(node);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
struct Interface {
|
struct Interface : Emulator::Interface::Bind {
|
||||||
|
void loadRequest(unsigned id, const string &path);
|
||||||
|
void loadRequest(unsigned id, const string &name, const string &type, const string &path);
|
||||||
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue);
|
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue);
|
||||||
void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height);
|
void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height);
|
||||||
void audioSample(int16_t lsample, int16_t rsample);
|
void audioSample(int16_t lsample, int16_t rsample);
|
||||||
int16_t inputPoll(unsigned port, unsigned device, unsigned input);
|
int16_t inputPoll(unsigned port, unsigned device, unsigned input);
|
||||||
void mediaRequest(Emulator::Interface::Media media);
|
|
||||||
unsigned dipSettings(const XML::Node &node);
|
unsigned dipSettings(const XML::Node &node);
|
||||||
string path(unsigned group);
|
string path(unsigned group);
|
||||||
};
|
};
|
||||||
|
|
|
@ -42,6 +42,10 @@ Settings::Settings() {
|
||||||
append(*timingSettings);
|
append(*timingSettings);
|
||||||
append(*driverSettings);
|
append(*driverSettings);
|
||||||
|
|
||||||
|
onClose = [&] {
|
||||||
|
timingSettings->analysis.stop = true;
|
||||||
|
};
|
||||||
|
|
||||||
panelList.onChange = {&Settings::panelChanged, this};
|
panelList.onChange = {&Settings::panelChanged, this};
|
||||||
|
|
||||||
panelList.setSelection(2);
|
panelList.setSelection(2);
|
||||||
|
|
|
@ -23,7 +23,6 @@ struct TimingSettings : SettingsLayout {
|
||||||
|
|
||||||
TimingSettings();
|
TimingSettings();
|
||||||
|
|
||||||
private:
|
|
||||||
struct Analysis {
|
struct Analysis {
|
||||||
bool stop;
|
bool stop;
|
||||||
unsigned seconds;
|
unsigned seconds;
|
||||||
|
|
|
@ -30,23 +30,24 @@ void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Medi
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utility::loadMedia(Emulator::Interface::Media &media) {
|
void Utility::loadMedia(unsigned id, const string &path) {
|
||||||
string pathname = {path(system().group(media.id)), media.path};
|
string pathname = {this->path(system().group(id)), path};
|
||||||
if(file::exists(pathname)) {
|
if(file::exists(pathname)) {
|
||||||
mmapstream stream(pathname);
|
mmapstream stream(pathname);
|
||||||
return system().load(media.id, stream);
|
return system().load(id, stream);
|
||||||
}
|
}
|
||||||
if(media.name.empty()) return;
|
}
|
||||||
|
|
||||||
pathname = browser->select({"Load ", media.name}, media.extension);
|
void Utility::loadMedia(unsigned id, const string &name, const string &type, const string &path) {
|
||||||
|
string pathname = browser->select({"Load ", name}, type);
|
||||||
if(pathname.empty()) return;
|
if(pathname.empty()) return;
|
||||||
path(system().group(media.id)) = pathname;
|
this->path(system().group(id)) = pathname;
|
||||||
this->pathname.append(pathname);
|
this->pathname.append(pathname);
|
||||||
|
|
||||||
string markup;
|
string markup;
|
||||||
markup.readfile({pathname, "manifest.xml"});
|
markup.readfile({pathname, "manifest.xml"});
|
||||||
mmapstream stream({pathname, media.path});
|
mmapstream stream({pathname, path});
|
||||||
system().load(media.id, stream, markup);
|
system().load(id, stream, markup);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utility::loadMemory() {
|
void Utility::loadMemory() {
|
||||||
|
@ -170,6 +171,7 @@ void Utility::synchronizeRuby() {
|
||||||
}
|
}
|
||||||
dspaudio.setResamplerFrequency(config->audio.frequency);
|
dspaudio.setResamplerFrequency(config->audio.frequency);
|
||||||
dspaudio.setVolume(config->audio.mute ? 0.0 : config->audio.volume * 0.01);
|
dspaudio.setVolume(config->audio.mute ? 0.0 : config->audio.volume * 0.01);
|
||||||
|
synchronizeDSP();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utility::updateShader() {
|
void Utility::updateShader() {
|
||||||
|
|
|
@ -2,7 +2,8 @@ struct Utility {
|
||||||
void setInterface(Emulator::Interface *emulator);
|
void setInterface(Emulator::Interface *emulator);
|
||||||
void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media);
|
void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media);
|
||||||
void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname);
|
void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname);
|
||||||
void loadMedia(Emulator::Interface::Media &media);
|
void loadMedia(unsigned id, const string &path);
|
||||||
|
void loadMedia(unsigned id, const string &name, const string &type, const string &path);
|
||||||
void loadMemory();
|
void loadMemory();
|
||||||
void saveMemory();
|
void saveMemory();
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
options += debugger
|
options += debugger
|
||||||
|
|
||||||
processors := arm gsu hg51b r65816 spc700 upd96050
|
processors := arm gsu hg51b lr35902 r65816 spc700 upd96050
|
||||||
include processor/Makefile
|
include processor/Makefile
|
||||||
|
|
||||||
include $(sfc)/Makefile
|
include $(sfc)/Makefile
|
||||||
|
include $(gb)/Makefile
|
||||||
name := laevateinn
|
name := laevateinn
|
||||||
|
|
||||||
ui_objects := ui-main ui-settings ui-interface ui-debugger ui-tracer ui-window
|
ui_objects := ui-main ui-settings ui-interface ui-debugger ui-tracer ui-window
|
||||||
|
|
|
@ -11,7 +11,7 @@ AboutWindow::AboutWindow() {
|
||||||
title.setFont("Sans, 16, Bold");
|
title.setFont("Sans, 16, Bold");
|
||||||
title.setText("Laevateinn");
|
title.setText("Laevateinn");
|
||||||
version.setFont("Sans, 8, Bold");
|
version.setFont("Sans, 8, Bold");
|
||||||
version.setText({"bsnes/debugger v", Version});
|
version.setText({"bsnes/debugger v", Emulator::Version});
|
||||||
website.setFont("Sans, 8, Bold");
|
website.setFont("Sans, 8, Bold");
|
||||||
website.setText("http://byuu.org/");
|
website.setText("http://byuu.org/");
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ ConsoleWindow *consoleWindow = nullptr;
|
||||||
#include "about.cpp"
|
#include "about.cpp"
|
||||||
|
|
||||||
ConsoleWindow::ConsoleWindow() {
|
ConsoleWindow::ConsoleWindow() {
|
||||||
setTitle({"Console - Laevateinn v", Version});
|
setTitle({"Console - Laevateinn v", Emulator::Version});
|
||||||
setGeometry({64, 640, 640, 400});
|
setGeometry({64, 640, 640, 400});
|
||||||
setMenuVisible();
|
setMenuVisible();
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,13 @@ bool Interface::loadCartridge(const string &foldername) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pathName = foldername;
|
pathName = foldername;
|
||||||
mkdir(string(pathName, "debug/"), 0755);
|
directory::create({pathName, "debug/"});
|
||||||
|
|
||||||
string markup;
|
string markup;
|
||||||
markup.readfile({pathName, "manifest.xml"});
|
markup.readfile({pathName, "manifest.xml"});
|
||||||
if(markup.empty()) markup = SuperFamicomCartridge(memory.data(), memory.size()).markup;
|
if(markup.empty()) markup = SuperFamicomCartridge(memory.data(), memory.size()).markup;
|
||||||
|
|
||||||
SFC::cartridge.rom.copy(vectorstream{memory});
|
SFC::cartridge.load(markup, vectorstream{memory});
|
||||||
SFC::cartridge.load(SFC::Cartridge::Mode::Normal, markup);
|
|
||||||
SFC::system.power();
|
SFC::system.power();
|
||||||
|
|
||||||
string name = pathName;
|
string name = pathName;
|
||||||
|
@ -36,26 +35,23 @@ bool Interface::loadCartridge(const string &foldername) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::loadMemory() {
|
void Interface::loadMemory() {
|
||||||
for(auto &memory : SFC::cartridge.nvram) {
|
for(auto &memory : SFC::interface->memory) {
|
||||||
if(memory.size == 0) continue;
|
string filename{pathName, memory.name};
|
||||||
string filename = {pathName, memory.id};
|
filestream fs(filename, file::mode::read);
|
||||||
if(auto content = file::read(filename)) {
|
instance->load(memory.id, fs);
|
||||||
debugger->print("Loaded ", filename, "\n");
|
if(file::exists(filename)) debugger->print("Loaded ", filename, "\n");
|
||||||
memcpy(memory.data, content.data(), min(memory.size, content.size()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debugger->loadUsage();
|
debugger->loadUsage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::saveMemory() {
|
void Interface::saveMemory() {
|
||||||
for(auto &memory : SFC::cartridge.nvram) {
|
for(auto &memory : SFC::interface->memory) {
|
||||||
if(memory.size == 0) continue;
|
string filename{pathName, memory.name};
|
||||||
string filename = { pathName, memory.id };
|
filestream fs(filename, file::mode::write);
|
||||||
if(file::write(filename, memory.data, memory.size)) {
|
instance->save(memory.id, fs);
|
||||||
debugger->print("Saved ", filename, "\n");
|
debugger->print("Saved ", filename, "\n");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
debugger->saveUsage();
|
debugger->saveUsage();
|
||||||
}
|
}
|
||||||
|
@ -79,19 +75,24 @@ bool Interface::saveState(unsigned slot) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t Interface::videoColor(unsigned source, uint16_t r, uint16_t g, uint16_t b) {
|
||||||
|
return (r >> 8) << 16 | (g >> 8) << 8 | (b >> 8) << 0;
|
||||||
|
}
|
||||||
|
|
||||||
//hires is always true for accuracy core
|
//hires is always true for accuracy core
|
||||||
//overscan is ignored for the debugger port
|
//overscan is ignored for the debugger port
|
||||||
void Interface::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) {
|
void Interface::videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height) {
|
||||||
data += 8 * 1024; //skip NTSC overscan compensation
|
data += 8 * 1024; //skip NTSC overscan compensation
|
||||||
uint32_t *output = videoWindow->canvas.data();
|
uint32_t *output = videoWindow->canvas.data();
|
||||||
|
|
||||||
|
bool interlace = height >= 448;
|
||||||
if(interlace == false) {
|
if(interlace == false) {
|
||||||
for(unsigned y = 0; y < 240; y++) {
|
for(unsigned y = 0; y < 240; y++) {
|
||||||
const uint32_t *sp = data + y * 1024;
|
const uint32_t *sp = data + y * 1024;
|
||||||
uint32_t *dp0 = output + y * 1024, *dp1 = dp0 + 512;
|
uint32_t *dp0 = output + y * 1024, *dp1 = dp0 + 512;
|
||||||
for(unsigned x = 0; x < 512; x++) {
|
for(unsigned x = 0; x < 512; x++) {
|
||||||
*dp0++ = SFC::video.palette[*sp];
|
*dp0++ = *sp;
|
||||||
*dp1++ = SFC::video.palette[*sp++];
|
*dp1++ = *sp++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -99,7 +100,7 @@ void Interface::videoRefresh(const uint32_t *data, bool hires, bool interlace, b
|
||||||
const uint32_t *sp = data + y * 512;
|
const uint32_t *sp = data + y * 512;
|
||||||
uint32_t *dp = output + y * 512;
|
uint32_t *dp = output + y * 512;
|
||||||
for(unsigned x = 0; x < 512; x++) {
|
for(unsigned x = 0; x < 512; x++) {
|
||||||
*dp++ = SFC::video.palette[*sp++];
|
*dp++ = *sp++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,13 +113,13 @@ void Interface::audioSample(int16_t lsample, int16_t rsample) {
|
||||||
audio.sample(lsample, rsample);
|
audio.sample(lsample, rsample);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t Interface::inputPoll(bool port, SFC::Input::Device device, unsigned index, unsigned id) {
|
int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned index) {
|
||||||
if(videoWindow->focused() == false) return 0;
|
if(videoWindow->focused() == false) return 0;
|
||||||
auto keyboardState = phoenix::Keyboard::state();
|
auto keyboardState = phoenix::Keyboard::state();
|
||||||
|
|
||||||
if(port == 0) {
|
if(port == 0) {
|
||||||
if(device == SFC::Input::Device::Joypad) {
|
if(device == (unsigned)SFC::Input::Device::Joypad) {
|
||||||
switch((SFC::Input::JoypadID)id) {
|
switch((SFC::Input::JoypadID)index) {
|
||||||
case SFC::Input::JoypadID::Up: return keyboardState[(unsigned)phoenix::Keyboard::Scancode::Up];
|
case SFC::Input::JoypadID::Up: return keyboardState[(unsigned)phoenix::Keyboard::Scancode::Up];
|
||||||
case SFC::Input::JoypadID::Down: return keyboardState[(unsigned)phoenix::Keyboard::Scancode::Down];
|
case SFC::Input::JoypadID::Down: return keyboardState[(unsigned)phoenix::Keyboard::Scancode::Down];
|
||||||
case SFC::Input::JoypadID::Left: return keyboardState[(unsigned)phoenix::Keyboard::Scancode::Left];
|
case SFC::Input::JoypadID::Left: return keyboardState[(unsigned)phoenix::Keyboard::Scancode::Left];
|
||||||
|
@ -147,7 +148,10 @@ void Interface::message(const string &text) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Interface::Interface() {
|
Interface::Interface() {
|
||||||
SFC::interface = this;
|
instance = new SFC::Interface;
|
||||||
|
instance->bind = this;
|
||||||
|
|
||||||
|
SFC::video.generate_palette();
|
||||||
SFC::system.init();
|
SFC::system.init();
|
||||||
|
|
||||||
filestream fs{{application->userpath, "Super Famicom.sys/spc700.rom"}};
|
filestream fs{{application->userpath, "Super Famicom.sys/spc700.rom"}};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
struct Interface : SFC::Interface {
|
struct Interface : Emulator::Interface::Bind {
|
||||||
string pathName;
|
string pathName;
|
||||||
|
|
||||||
bool loadCartridge(const string &foldername);
|
bool loadCartridge(const string &foldername);
|
||||||
|
@ -7,14 +7,22 @@ struct Interface : SFC::Interface {
|
||||||
bool loadState(unsigned slot);
|
bool loadState(unsigned slot);
|
||||||
bool saveState(unsigned slot);
|
bool saveState(unsigned slot);
|
||||||
|
|
||||||
void videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan);
|
void loadRequest(unsigned id, const string &path) {}
|
||||||
|
void loadRequest(unsigned id, const string &name, const string &type, const string &path) {}
|
||||||
|
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue);
|
||||||
|
void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height);
|
||||||
void audioSample(int16_t lsample, int16_t rsample);
|
void audioSample(int16_t lsample, int16_t rsample);
|
||||||
int16_t inputPoll(bool port, SFC::Input::Device device, unsigned index, unsigned id);
|
int16_t inputPoll(unsigned port, unsigned device, unsigned index);
|
||||||
|
unsigned dipSettings(const XML::Node &node) { return 0u; }
|
||||||
|
string path(unsigned group) { return ""; }
|
||||||
|
|
||||||
string path(SFC::Cartridge::Slot slot, const string &hint);
|
string path(SFC::Cartridge::Slot slot, const string &hint);
|
||||||
void message(const string &text);
|
void message(const string &text);
|
||||||
|
|
||||||
Interface();
|
Interface();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SFC::Interface *instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Interface *interface;
|
extern Interface *interface;
|
||||||
|
|
Loading…
Reference in New Issue