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 {
|
||||
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 License[] = "GPLv3";
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace Emulator {
|
|||
#include <nall/varint.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
#include <nall/stream/memory.hpp>
|
||||
#include <nall/stream/vector.hpp>
|
||||
using namespace nall;
|
||||
|
||||
#include "interface.hpp"
|
||||
|
|
|
@ -44,7 +44,6 @@ struct Interface {
|
|||
vector<Input> input;
|
||||
vector<unsigned> order;
|
||||
};
|
||||
vector<Device> device;
|
||||
|
||||
struct Port {
|
||||
unsigned id;
|
||||
|
@ -53,48 +52,26 @@ struct Interface {
|
|||
};
|
||||
vector<Port> port;
|
||||
|
||||
struct Callback {
|
||||
function<uint32_t (unsigned, uint16_t, uint16_t, uint16_t)> videoColor;
|
||||
function<void (const uint32_t*, unsigned, unsigned, unsigned)> videoRefresh;
|
||||
function<void (int16_t, int16_t)> audioSample;
|
||||
function<int16_t (unsigned, unsigned, unsigned)> inputPoll;
|
||||
function<void (Media)> mediaRequest;
|
||||
function<unsigned (const XML::Node&)> dipSettings;
|
||||
function<string (unsigned)> path;
|
||||
} callback;
|
||||
struct Bind {
|
||||
virtual void loadRequest(unsigned, const string&) {}
|
||||
virtual void loadRequest(unsigned, const string&, const string&, const 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 XML::Node&) { return 0; }
|
||||
virtual string path(unsigned) { return ""; }
|
||||
} *bind;
|
||||
|
||||
//callback bindings (provided by user interface)
|
||||
virtual uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) {
|
||||
if(callback.videoColor) return callback.videoColor(source, red, green, blue);
|
||||
return (red >> 8) << 16 | (green >> 8) << 8 | (blue >> 8) << 0;
|
||||
}
|
||||
|
||||
virtual void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height) {
|
||||
if(callback.videoRefresh) return callback.videoRefresh(data, pitch, width, height);
|
||||
}
|
||||
|
||||
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 "";
|
||||
}
|
||||
void loadRequest(unsigned id, const string &path) { return bind->loadRequest(id, path); }
|
||||
void loadRequest(unsigned id, const string &name, const string &type, const string &path) { return bind->loadRequest(id, name, type, 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 XML::Node &node) { return bind->dipSettings(node); }
|
||||
string path(unsigned group) { return bind->path(group); }
|
||||
|
||||
//information
|
||||
virtual double videoFrequency() = 0;
|
||||
|
@ -123,6 +100,8 @@ struct Interface {
|
|||
|
||||
//utility functions
|
||||
virtual void updatePalette() {}
|
||||
|
||||
Interface() : bind(nullptr) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -107,12 +107,12 @@ Interface::Interface() {
|
|||
this->device.append(device);
|
||||
}
|
||||
|
||||
port.append({ID::Port1, "Port 1"});
|
||||
port.append({ID::Port2, "Port 2"});
|
||||
port.append({0, "Port 1"});
|
||||
port.append({1, "Port 2"});
|
||||
|
||||
for(auto &device : this->device) {
|
||||
for(auto &port : this->port) {
|
||||
if(device.portmask & port.id) {
|
||||
if(device.portmask & (1 << port.id)) {
|
||||
port.device.append(device);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,9 @@ struct Interface : Emulator::Interface {
|
|||
void updatePalette();
|
||||
|
||||
Interface();
|
||||
|
||||
private:
|
||||
vector<Device> device;
|
||||
};
|
||||
|
||||
extern Interface *interface;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
options += gameboy
|
||||
|
||||
gb_objects := gb-interface gb-system gb-scheduler
|
||||
gb_objects += gb-memory gb-cartridge
|
||||
gb_objects += gb-cpu gb-ppu gb-apu
|
||||
|
|
|
@ -142,8 +142,8 @@ void Cartridge::power() {
|
|||
|
||||
Cartridge::Cartridge() {
|
||||
loaded = false;
|
||||
romdata = 0;
|
||||
ramdata = 0;
|
||||
romdata = nullptr;
|
||||
ramdata = nullptr;
|
||||
}
|
||||
|
||||
Cartridge::~Cartridge() {
|
||||
|
|
|
@ -127,7 +127,7 @@ Interface::Interface() {
|
|||
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();
|
||||
|
||||
Interface();
|
||||
|
||||
private:
|
||||
vector<Device> device;
|
||||
};
|
||||
|
||||
extern Interface *interface;
|
||||
|
|
|
@ -114,7 +114,7 @@ Interface::Interface() {
|
|||
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();
|
||||
|
||||
Interface();
|
||||
|
||||
private:
|
||||
vector<Device> device;
|
||||
};
|
||||
|
||||
extern Interface *interface;
|
||||
|
|
|
@ -176,6 +176,7 @@ void pWindow::setTitle(const string &text) {
|
|||
|
||||
void pWindow::setVisible(bool visible) {
|
||||
ShowWindow(hwnd, visible ? SW_SHOWNORMAL : SW_HIDE);
|
||||
if(visible == false) setModal(false);
|
||||
}
|
||||
|
||||
void pWindow::setWidgetFont(const string &font) {
|
||||
|
|
|
@ -319,8 +319,14 @@ public:
|
|||
}
|
||||
|
||||
if(settings.synchronize) {
|
||||
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) {
|
||||
D3DRASTER_STATUS status;
|
||||
device->GetRasterStatus(0, &status);
|
||||
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) {
|
||||
#if defined(GAMEBOY)
|
||||
if(root.exists() == false) return;
|
||||
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));
|
||||
|
||||
|
@ -107,7 +113,6 @@ void Cartridge::parse_markup_icd2(XML::Node &root) {
|
|||
parse_markup_map(m, node);
|
||||
mapping.append(m);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if(necdsp.revision == NECDSP::Revision::uPD7725) {
|
||||
interface->mediaRequest({ID::Nec7725DSP, "", "", firmware});
|
||||
interface->loadRequest(ID::Nec7725DSP, firmware);
|
||||
}
|
||||
|
||||
if(necdsp.revision == NECDSP::Revision::uPD96050) {
|
||||
interface->mediaRequest({ID::Nec96050DSP, "", "", firmware});
|
||||
interface->loadRequest(ID::Nec96050DSP, firmware);
|
||||
}
|
||||
|
||||
for(auto &node : root) {
|
||||
|
@ -256,7 +261,7 @@ void Cartridge::parse_markup_hitachidsp(XML::Node &root) {
|
|||
string firmware = root["firmware"].data;
|
||||
string sha256 = root["sha256"].data;
|
||||
|
||||
interface->mediaRequest({ID::HitachiDSP, "", "", firmware});
|
||||
interface->loadRequest(ID::HitachiDSP, firmware);
|
||||
|
||||
for(auto &node : root) {
|
||||
if(node.name == "rom") {
|
||||
|
@ -284,7 +289,7 @@ void Cartridge::parse_markup_armdsp(XML::Node &root) {
|
|||
string firmware = root["firmware"].data;
|
||||
string sha256 = root["sha256"].data;
|
||||
|
||||
interface->mediaRequest({ID::ArmDSP, "", "", firmware});
|
||||
interface->loadRequest(ID::ArmDSP, firmware);
|
||||
|
||||
for(auto &node : root) {
|
||||
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_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"]) {
|
||||
if(node.name != "map") continue;
|
||||
|
@ -327,8 +332,8 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
|
|||
if(root.exists() == false) return;
|
||||
has_st_slots = true;
|
||||
|
||||
interface->mediaRequest({ID::SufamiTurboSlotAROM, "Sufami Turbo - Slot A", "", "program.rom", "st"});
|
||||
interface->mediaRequest({ID::SufamiTurboSlotBROM, "Sufami Turbo - Slot B", "", "program.rom", "st"});
|
||||
interface->loadRequest(ID::SufamiTurboSlotAROM, "Sufami Turbo - Slot A", "st", "program.rom");
|
||||
interface->loadRequest(ID::SufamiTurboSlotBROM, "Sufami Turbo - Slot B", "st", "program.rom");
|
||||
|
||||
for(auto &slot : root) {
|
||||
if(slot.name != "slot") continue;
|
||||
|
|
|
@ -3,10 +3,7 @@ struct Coprocessor : Thread {
|
|||
alwaysinline void synchronize_cpu();
|
||||
};
|
||||
|
||||
#if defined(GAMEBOY)
|
||||
#include <sfc/chip/icd2/icd2.hpp>
|
||||
#endif
|
||||
|
||||
#include <sfc/chip/nss/nss.hpp>
|
||||
#include <sfc/chip/superfx/superfx.hpp>
|
||||
#include <sfc/chip/sa1/sa1.hpp>
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#if defined(GAMEBOY)
|
||||
|
||||
#include <sfc/sfc.hpp>
|
||||
|
||||
#define ICD2_CPP
|
||||
|
@ -35,9 +33,12 @@ void ICD2::init() {
|
|||
}
|
||||
|
||||
void ICD2::load() {
|
||||
interface = GameBoy::interface->bind;
|
||||
GameBoy::interface->bind = this;
|
||||
}
|
||||
|
||||
void ICD2::unload() {
|
||||
GameBoy::interface->bind = interface;
|
||||
}
|
||||
|
||||
void ICD2::power() {
|
||||
|
@ -69,12 +70,9 @@ void ICD2::reset() {
|
|||
joyp14lock = 0;
|
||||
pulselock = true;
|
||||
|
||||
GameBoy::interface = this;
|
||||
GameBoy::video.generate_palette();
|
||||
GameBoy::system.init();
|
||||
GameBoy::system.power();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
class ICD2 : GameBoy::Interface, public Coprocessor {
|
||||
public:
|
||||
struct ICD2 : Emulator::Interface::Bind, Coprocessor {
|
||||
unsigned revision;
|
||||
|
||||
static void Enter();
|
||||
|
@ -17,6 +16,7 @@ public:
|
|||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
Emulator::Interface::Bind *interface;
|
||||
#include "interface/interface.hpp"
|
||||
#include "mmio/mmio.hpp"
|
||||
};
|
||||
|
|
|
@ -79,8 +79,6 @@ void Interface::load(unsigned id, const stream &stream, const string &markup) {
|
|||
if(id == ID::ROM) {
|
||||
cartridge.load(markup, stream);
|
||||
system.power();
|
||||
input.connect(0, Input::Device::Joypad);
|
||||
input.connect(1, Input::Device::Joypad);
|
||||
}
|
||||
|
||||
if(id == ID::SuperGameBoyROM) {
|
||||
|
@ -342,12 +340,12 @@ Interface::Interface() {
|
|||
this->device.append(device);
|
||||
}
|
||||
|
||||
port.append({ID::Port1, "Port 1"});
|
||||
port.append({ID::Port2, "Port 2"});
|
||||
port.append({0, "Port 1"});
|
||||
port.append({1, "Port 2"});
|
||||
|
||||
for(auto &device : this->device) {
|
||||
for(auto &port : this->port) {
|
||||
if(device.portmask & port.id) {
|
||||
if(device.portmask & (1 << port.id)) {
|
||||
port.device.append(device);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,9 @@ struct Interface : Emulator::Interface {
|
|||
void updatePalette();
|
||||
|
||||
Interface();
|
||||
|
||||
private:
|
||||
vector<Device> device;
|
||||
};
|
||||
|
||||
extern Interface *interface;
|
||||
|
|
|
@ -33,7 +33,7 @@ void PPU::enter() {
|
|||
}
|
||||
|
||||
scanline();
|
||||
add_clocks(68);
|
||||
add_clocks(28);
|
||||
bg1.begin();
|
||||
bg2.begin();
|
||||
bg3.begin();
|
||||
|
@ -65,7 +65,7 @@ void PPU::enter() {
|
|||
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>
|
||||
|
||||
#if defined(GAMEBOY)
|
||||
#include <gb/gb.hpp>
|
||||
#endif
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
namespace SuperFamicom {
|
||||
struct Thread {
|
||||
|
|
|
@ -57,9 +57,7 @@ void System::serialize_all(serializer &s) {
|
|||
ppu.serialize(s);
|
||||
dsp.serialize(s);
|
||||
|
||||
#if defined(GAMEBOY)
|
||||
if(cartridge.has_gb_slot()) icd2.serialize(s);
|
||||
#endif
|
||||
if(cartridge.has_st_slots()) sufamiturbo.serialize(s);
|
||||
if(cartridge.has_superfx()) superfx.serialize(s);
|
||||
if(cartridge.has_sa1()) sa1.serialize(s);
|
||||
|
|
|
@ -64,9 +64,7 @@ void System::runthreadtosave() {
|
|||
void System::init() {
|
||||
assert(interface != 0);
|
||||
|
||||
#if defined(GAMEBOY)
|
||||
icd2.init();
|
||||
#endif
|
||||
nss.init();
|
||||
superfx.init();
|
||||
sa1.init();
|
||||
|
@ -112,9 +110,7 @@ void System::load() {
|
|||
ppu.enable();
|
||||
|
||||
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.load();
|
||||
#if defined(GAMEBOY)
|
||||
if(cartridge.has_gb_slot()) icd2.load();
|
||||
#endif
|
||||
if(cartridge.has_bs_cart()) bsxcartridge.load();
|
||||
if(cartridge.has_bs_slot()) bsxflash.load();
|
||||
if(cartridge.has_st_slots()) sufamiturbo.load();
|
||||
|
@ -137,9 +133,7 @@ void System::load() {
|
|||
|
||||
void System::unload() {
|
||||
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.unload();
|
||||
#if defined(GAMEBOY)
|
||||
if(cartridge.has_gb_slot()) icd2.unload();
|
||||
#endif
|
||||
if(cartridge.has_bs_cart()) bsxcartridge.unload();
|
||||
if(cartridge.has_bs_slot()) bsxflash.unload();
|
||||
if(cartridge.has_st_slots()) sufamiturbo.unload();
|
||||
|
@ -166,9 +160,7 @@ void System::power() {
|
|||
ppu.power();
|
||||
|
||||
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.power();
|
||||
#if defined(GAMEBOY)
|
||||
if(cartridge.has_gb_slot()) icd2.power();
|
||||
#endif
|
||||
if(cartridge.has_bs_cart()) bsxcartridge.power();
|
||||
if(cartridge.has_bs_slot()) bsxflash.power();
|
||||
if(cartridge.has_nss_dip()) nss.power();
|
||||
|
@ -194,9 +186,7 @@ void System::reset() {
|
|||
ppu.reset();
|
||||
|
||||
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.reset();
|
||||
#if defined(GAMEBOY)
|
||||
if(cartridge.has_gb_slot()) icd2.reset();
|
||||
#endif
|
||||
if(cartridge.has_bs_cart()) bsxcartridge.reset();
|
||||
if(cartridge.has_bs_slot()) bsxflash.reset();
|
||||
if(cartridge.has_nss_dip()) nss.reset();
|
||||
|
@ -212,9 +202,7 @@ void System::reset() {
|
|||
if(cartridge.has_msu1()) msu1.reset();
|
||||
if(cartridge.has_link()) link.reset();
|
||||
|
||||
#if defined(GAMEBOY)
|
||||
if(cartridge.has_gb_slot()) cpu.coprocessors.append(&icd2);
|
||||
#endif
|
||||
if(cartridge.has_superfx()) cpu.coprocessors.append(&superfx);
|
||||
if(cartridge.has_sa1()) cpu.coprocessors.append(&sa1);
|
||||
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);
|
||||
|
||||
if(hires == false) {
|
||||
*((uint32_t*)data + vy * 1024 + vx) = pixelcolor;
|
||||
*((uint32_t*)data + vy * 1024 + vx) = palette[pixelcolor];
|
||||
} else {
|
||||
*((uint32_t*)data + vy * 1024 + vx * 2 + 0) = pixelcolor;
|
||||
*((uint32_t*)data + vy * 1024 + vx * 2 + 1) = pixelcolor;
|
||||
*((uint32_t*)data + vy * 1024 + vx * 2 + 0) = palette[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;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ include fc/Makefile
|
|||
include sfc/Makefile
|
||||
include gb/Makefile
|
||||
include gba/Makefile
|
||||
name := ethos
|
||||
name := bsnes
|
||||
|
||||
ui_objects := ui-ethos ui-configuration ui-interface ui-utility
|
||||
ui_objects += ui-input ui-window ui-general ui-settings ui-tools
|
||||
|
|
|
@ -12,13 +12,7 @@ void Application::bootstrap() {
|
|||
emulator.append(new GameBoyAdvance::Interface);
|
||||
|
||||
for(auto &system : emulator) {
|
||||
system->callback.videoColor = {&Interface::videoColor, 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};
|
||||
system->bind = interface;
|
||||
|
||||
for(auto &firmware : system->firmware) {
|
||||
filestream fs{application->path({firmware.name, ".", firmware.type, "/", firmware.path})};
|
||||
|
|
|
@ -41,11 +41,8 @@ Application::Application(int argc, char **argv) {
|
|||
basepath = dir(path);
|
||||
unused = ::userpath(path);
|
||||
userpath = path;
|
||||
if(Intrinsics::platform() == Intrinsics::Platform::Windows) {
|
||||
userpath.append("ethos/");
|
||||
} else {
|
||||
userpath.append(".config/ethos/");
|
||||
}
|
||||
if(Intrinsics::platform() != Intrinsics::Platform::Windows) userpath.append(".config/");
|
||||
userpath.append("bsnes/");
|
||||
directory::create(userpath);
|
||||
|
||||
bootstrap();
|
||||
|
|
|
@ -91,6 +91,24 @@ void Browser::bootstrap() {
|
|||
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) {
|
||||
this->extension = extension;
|
||||
|
||||
|
@ -110,15 +128,20 @@ string Browser::select(const string &title, const string &extension) {
|
|||
|
||||
audio.clear();
|
||||
setTitle(title);
|
||||
setModal();
|
||||
setVisible();
|
||||
setModal(true);
|
||||
setVisible(true);
|
||||
fileList.setFocused();
|
||||
dialogActive = true;
|
||||
outputFilename = "";
|
||||
|
||||
dialogActive = true;
|
||||
while(dialogActive == true) {
|
||||
OS::processEvents();
|
||||
inputManager->poll();
|
||||
usleep(20 * 1000);
|
||||
}
|
||||
|
||||
setModal(false);
|
||||
setVisible(false);
|
||||
return outputFilename;
|
||||
}
|
||||
|
||||
|
@ -169,7 +192,6 @@ void Browser::fileListActivate() {
|
|||
string suffix = {this->extension, "/"};
|
||||
if(filename.endswith(suffix) == false) return setPath({path, filename});
|
||||
|
||||
setVisible(false);
|
||||
dialogActive = false;
|
||||
outputFilename = {path, filename};
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ struct Browser : Window {
|
|||
Button openButton;
|
||||
|
||||
string select(const string &title, const string &extension);
|
||||
void inputEvent(unsigned scancode, int16_t value);
|
||||
void saveConfiguration();
|
||||
void synchronize();
|
||||
void bootstrap();
|
||||
|
|
|
@ -39,7 +39,7 @@ void Presentation::setSystemName(const string &name) {
|
|||
Presentation::Presentation() : active(nullptr) {
|
||||
bootstrap();
|
||||
loadShaders();
|
||||
setGeometry({1024, 600, 720, 480});
|
||||
setGeometry({256, 256, 720, 480});
|
||||
windowManager->append(this, "Presentation");
|
||||
|
||||
setTitle({::Emulator::Name, " v", ::Emulator::Version});
|
||||
|
@ -144,24 +144,20 @@ void Presentation::bootstrap() {
|
|||
iEmulator->reset.setText("Reset");
|
||||
iEmulator->unload.setText("Unload");
|
||||
|
||||
unsigned portNumber = 0;
|
||||
for(auto &port : emulator->port) {
|
||||
auto iPort = new Emulator::Port;
|
||||
iPort->menu.setText(port.name);
|
||||
iEmulator->port.append(iPort);
|
||||
|
||||
unsigned deviceNumber = 0;
|
||||
for(auto &device : port.device) {
|
||||
auto iDevice = new RadioItem;
|
||||
iDevice->setText(device.name);
|
||||
iDevice->onActivate = [=] { utility->connect(portNumber, device.id); };
|
||||
iDevice->onActivate = [=] { utility->connect(port.id, device.id); };
|
||||
iPort->group.append(*iDevice);
|
||||
iPort->device.append(iDevice);
|
||||
deviceNumber++;
|
||||
}
|
||||
|
||||
RadioItem::group(iPort->group);
|
||||
portNumber++;
|
||||
}
|
||||
|
||||
iEmulator->menu.append(iEmulator->power);
|
||||
|
|
|
@ -54,7 +54,6 @@ struct Presentation : Window {
|
|||
void bootstrap();
|
||||
Presentation();
|
||||
|
||||
private:
|
||||
Emulator *active;
|
||||
};
|
||||
|
||||
|
|
|
@ -157,11 +157,16 @@ void InputManager::bind() {
|
|||
}
|
||||
|
||||
void InputManager::poll() {
|
||||
using nall::Keyboard;
|
||||
|
||||
activeScancode = !activeScancode;
|
||||
input.poll(scancode[activeScancode]);
|
||||
|
||||
for(unsigned n = 0; n < Scancode::Limit; n++) {
|
||||
if(scancode[0][n] != scancode[1][n]) {
|
||||
if(browser->focused()) {
|
||||
browser->inputEvent(n, scancode[activeScancode][n]);
|
||||
}
|
||||
if(settings->focused()) {
|
||||
inputSettings->inputEvent(n, scancode[activeScancode][n]);
|
||||
hotkeySettings->inputEvent(n, scancode[activeScancode][n]);
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
#include "../ethos.hpp"
|
||||
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) {
|
||||
if(config->video.saturation != 100) {
|
||||
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();
|
||||
}
|
||||
|
||||
void Interface::mediaRequest(Emulator::Interface::Media media) {
|
||||
utility->loadMedia(media);
|
||||
}
|
||||
|
||||
unsigned Interface::dipSettings(const XML::Node &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);
|
||||
void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height);
|
||||
void audioSample(int16_t lsample, int16_t rsample);
|
||||
int16_t inputPoll(unsigned port, unsigned device, unsigned input);
|
||||
void mediaRequest(Emulator::Interface::Media media);
|
||||
unsigned dipSettings(const XML::Node &node);
|
||||
string path(unsigned group);
|
||||
};
|
||||
|
|
|
@ -42,6 +42,10 @@ Settings::Settings() {
|
|||
append(*timingSettings);
|
||||
append(*driverSettings);
|
||||
|
||||
onClose = [&] {
|
||||
timingSettings->analysis.stop = true;
|
||||
};
|
||||
|
||||
panelList.onChange = {&Settings::panelChanged, this};
|
||||
|
||||
panelList.setSelection(2);
|
||||
|
|
|
@ -23,7 +23,6 @@ struct TimingSettings : SettingsLayout {
|
|||
|
||||
TimingSettings();
|
||||
|
||||
private:
|
||||
struct Analysis {
|
||||
bool stop;
|
||||
unsigned seconds;
|
||||
|
|
|
@ -30,23 +30,24 @@ void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Medi
|
|||
load();
|
||||
}
|
||||
|
||||
void Utility::loadMedia(Emulator::Interface::Media &media) {
|
||||
string pathname = {path(system().group(media.id)), media.path};
|
||||
void Utility::loadMedia(unsigned id, const string &path) {
|
||||
string pathname = {this->path(system().group(id)), path};
|
||||
if(file::exists(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;
|
||||
path(system().group(media.id)) = pathname;
|
||||
this->path(system().group(id)) = pathname;
|
||||
this->pathname.append(pathname);
|
||||
|
||||
string markup;
|
||||
markup.readfile({pathname, "manifest.xml"});
|
||||
mmapstream stream({pathname, media.path});
|
||||
system().load(media.id, stream, markup);
|
||||
mmapstream stream({pathname, path});
|
||||
system().load(id, stream, markup);
|
||||
}
|
||||
|
||||
void Utility::loadMemory() {
|
||||
|
@ -170,6 +171,7 @@ void Utility::synchronizeRuby() {
|
|||
}
|
||||
dspaudio.setResamplerFrequency(config->audio.frequency);
|
||||
dspaudio.setVolume(config->audio.mute ? 0.0 : config->audio.volume * 0.01);
|
||||
synchronizeDSP();
|
||||
}
|
||||
|
||||
void Utility::updateShader() {
|
||||
|
|
|
@ -2,7 +2,8 @@ struct Utility {
|
|||
void setInterface(Emulator::Interface *emulator);
|
||||
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::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 saveMemory();
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
options += debugger
|
||||
|
||||
processors := arm gsu hg51b r65816 spc700 upd96050
|
||||
processors := arm gsu hg51b lr35902 r65816 spc700 upd96050
|
||||
include processor/Makefile
|
||||
|
||||
include $(sfc)/Makefile
|
||||
include $(gb)/Makefile
|
||||
name := laevateinn
|
||||
|
||||
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.setText("Laevateinn");
|
||||
version.setFont("Sans, 8, Bold");
|
||||
version.setText({"bsnes/debugger v", Version});
|
||||
version.setText({"bsnes/debugger v", Emulator::Version});
|
||||
website.setFont("Sans, 8, Bold");
|
||||
website.setText("http://byuu.org/");
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ ConsoleWindow *consoleWindow = nullptr;
|
|||
#include "about.cpp"
|
||||
|
||||
ConsoleWindow::ConsoleWindow() {
|
||||
setTitle({"Console - Laevateinn v", Version});
|
||||
setTitle({"Console - Laevateinn v", Emulator::Version});
|
||||
setGeometry({64, 640, 640, 400});
|
||||
setMenuVisible();
|
||||
|
||||
|
|
|
@ -12,14 +12,13 @@ bool Interface::loadCartridge(const string &foldername) {
|
|||
}
|
||||
|
||||
pathName = foldername;
|
||||
mkdir(string(pathName, "debug/"), 0755);
|
||||
directory::create({pathName, "debug/"});
|
||||
|
||||
string markup;
|
||||
markup.readfile({pathName, "manifest.xml"});
|
||||
if(markup.empty()) markup = SuperFamicomCartridge(memory.data(), memory.size()).markup;
|
||||
|
||||
SFC::cartridge.rom.copy(vectorstream{memory});
|
||||
SFC::cartridge.load(SFC::Cartridge::Mode::Normal, markup);
|
||||
SFC::cartridge.load(markup, vectorstream{memory});
|
||||
SFC::system.power();
|
||||
|
||||
string name = pathName;
|
||||
|
@ -36,25 +35,22 @@ bool Interface::loadCartridge(const string &foldername) {
|
|||
}
|
||||
|
||||
void Interface::loadMemory() {
|
||||
for(auto &memory : SFC::cartridge.nvram) {
|
||||
if(memory.size == 0) continue;
|
||||
string filename = {pathName, memory.id};
|
||||
if(auto content = file::read(filename)) {
|
||||
debugger->print("Loaded ", filename, "\n");
|
||||
memcpy(memory.data, content.data(), min(memory.size, content.size()));
|
||||
}
|
||||
for(auto &memory : SFC::interface->memory) {
|
||||
string filename{pathName, memory.name};
|
||||
filestream fs(filename, file::mode::read);
|
||||
instance->load(memory.id, fs);
|
||||
if(file::exists(filename)) debugger->print("Loaded ", filename, "\n");
|
||||
}
|
||||
|
||||
debugger->loadUsage();
|
||||
}
|
||||
|
||||
void Interface::saveMemory() {
|
||||
for(auto &memory : SFC::cartridge.nvram) {
|
||||
if(memory.size == 0) continue;
|
||||
string filename = { pathName, memory.id };
|
||||
if(file::write(filename, memory.data, memory.size)) {
|
||||
debugger->print("Saved ", filename, "\n");
|
||||
}
|
||||
for(auto &memory : SFC::interface->memory) {
|
||||
string filename{pathName, memory.name};
|
||||
filestream fs(filename, file::mode::write);
|
||||
instance->save(memory.id, fs);
|
||||
debugger->print("Saved ", filename, "\n");
|
||||
}
|
||||
|
||||
debugger->saveUsage();
|
||||
|
@ -79,19 +75,24 @@ bool Interface::saveState(unsigned slot) {
|
|||
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
|
||||
//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
|
||||
uint32_t *output = videoWindow->canvas.data();
|
||||
|
||||
bool interlace = height >= 448;
|
||||
if(interlace == false) {
|
||||
for(unsigned y = 0; y < 240; y++) {
|
||||
const uint32_t *sp = data + y * 1024;
|
||||
uint32_t *dp0 = output + y * 1024, *dp1 = dp0 + 512;
|
||||
for(unsigned x = 0; x < 512; x++) {
|
||||
*dp0++ = SFC::video.palette[*sp];
|
||||
*dp1++ = SFC::video.palette[*sp++];
|
||||
*dp0++ = *sp;
|
||||
*dp1++ = *sp++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -99,7 +100,7 @@ void Interface::videoRefresh(const uint32_t *data, bool hires, bool interlace, b
|
|||
const uint32_t *sp = data + y * 512;
|
||||
uint32_t *dp = output + y * 512;
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
auto keyboardState = phoenix::Keyboard::state();
|
||||
|
||||
if(port == 0) {
|
||||
if(device == SFC::Input::Device::Joypad) {
|
||||
switch((SFC::Input::JoypadID)id) {
|
||||
if(device == (unsigned)SFC::Input::Device::Joypad) {
|
||||
switch((SFC::Input::JoypadID)index) {
|
||||
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::Left: return keyboardState[(unsigned)phoenix::Keyboard::Scancode::Left];
|
||||
|
@ -147,7 +148,10 @@ void Interface::message(const string &text) {
|
|||
}
|
||||
|
||||
Interface::Interface() {
|
||||
SFC::interface = this;
|
||||
instance = new SFC::Interface;
|
||||
instance->bind = this;
|
||||
|
||||
SFC::video.generate_palette();
|
||||
SFC::system.init();
|
||||
|
||||
filestream fs{{application->userpath, "Super Famicom.sys/spc700.rom"}};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
struct Interface : SFC::Interface {
|
||||
struct Interface : Emulator::Interface::Bind {
|
||||
string pathName;
|
||||
|
||||
bool loadCartridge(const string &foldername);
|
||||
|
@ -7,14 +7,22 @@ struct Interface : SFC::Interface {
|
|||
bool loadState(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);
|
||||
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);
|
||||
void message(const string &text);
|
||||
|
||||
Interface();
|
||||
|
||||
private:
|
||||
SFC::Interface *instance;
|
||||
};
|
||||
|
||||
extern Interface *interface;
|
||||
|
|
Loading…
Reference in New Issue