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:
Tim Allen 2012-05-08 09:29:03 +10:00
parent cb97d98ad2
commit 689fc49047
42 changed files with 195 additions and 171 deletions

View File

@ -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"

View File

@ -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) {}
};
}

View File

@ -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);
}
}

View File

@ -36,6 +36,9 @@ struct Interface : Emulator::Interface {
void updatePalette();
Interface();
private:
vector<Device> device;
};
extern Interface *interface;

View File

@ -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

View File

@ -142,8 +142,8 @@ void Cartridge::power() {
Cartridge::Cartridge() {
loaded = false;
romdata = 0;
ramdata = 0;
romdata = nullptr;
ramdata = nullptr;
}
Cartridge::~Cartridge() {

View File

@ -127,7 +127,7 @@ Interface::Interface() {
this->device.append(device);
}
port.append({ID::Device, "Device", {device[0]}});
port.append({0, "Device", {device[0]}});
}
}

View File

@ -43,6 +43,9 @@ struct Interface : Emulator::Interface {
void updatePalette();
Interface();
private:
vector<Device> device;
};
extern Interface *interface;

View File

@ -114,7 +114,7 @@ Interface::Interface() {
this->device.append(device);
}
port.append({ID::Device, "Device", {device[0]}});
port.append({0, "Device", {device[0]}});
}
}

View File

@ -35,6 +35,9 @@ struct Interface : Emulator::Interface {
void updatePalette();
Interface();
private:
vector<Device> device;
};
extern Interface *interface;

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;

View File

@ -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>

View File

@ -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

View File

@ -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"
};

View File

@ -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);
}
}

View File

@ -56,6 +56,9 @@ struct Interface : Emulator::Interface {
void updatePalette();
Interface();
private:
vector<Device> device;
};
extern Interface *interface;

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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})};

View File

@ -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();

View File

@ -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};
}

View File

@ -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();

View File

@ -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);

View File

@ -54,7 +54,6 @@ struct Presentation : Window {
void bootstrap();
Presentation();
private:
Emulator *active;
};

View File

@ -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]);

View File

@ -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);
}

View File

@ -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);
};

View File

@ -42,6 +42,10 @@ Settings::Settings() {
append(*timingSettings);
append(*driverSettings);
onClose = [&] {
timingSettings->analysis.stop = true;
};
panelList.onChange = {&Settings::panelChanged, this};
panelList.setSelection(2);

View File

@ -23,7 +23,6 @@ struct TimingSettings : SettingsLayout {
TimingSettings();
private:
struct Analysis {
bool stop;
unsigned seconds;

View File

@ -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() {

View File

@ -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();

View File

@ -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

View File

@ -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/");

View File

@ -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();

View File

@ -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"}};

View File

@ -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;