Update to v088r08 release.

byuu says:

From this WIP, I'm starting on the impossible task of
a declarative-based GUI, which I'm calling Ethos.
base/ becomes emulator/, and we add emulator/interface.hpp, which is
a base API that all emulation cores must implement in full.
(Right now, it's kind of a hybrid to work with the old GUI and the new
GUI at the same time, of course.)
Unlike the old interfaces, the new base class also provides all general
usability hooks: loading and saving files and states, cheat codes, etc.
The new interface also contains information and vector structs to
describe all possible loading methods, controller bindings, etc; and
gives names for them all.
The actual GUI in fact should not include eg <gba/gba.hpp> anymore.
Should speed up GUI compilation.

So the idea going forward is that ethos will build a list of emulators
right when the application starts up.
Once you've appended an emulator to that list, you're done. No more GUI
changes are needed to support that system.
The GUI will have code to parse the emulator interfaces list, and build
all the requisite GUI options dynamically, declarative style.

Ultimately, once the project is finished, the new GUI should look ~99%
identical to the current GUI. But it'll probably be a whole lot smaller.
This commit is contained in:
Tim Allen 2012-04-29 16:29:54 +10:00
parent bb4db22a7d
commit 4fd20f0ae0
57 changed files with 593 additions and 214 deletions

View File

@ -1,7 +1,7 @@
#ifndef BASE_HPP
#define BASE_HPP
#ifndef EMULATOR_HPP
#define EMULATOR_HPP
static const char Version[] = "088.07";
static const char Version[] = "088.08";
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>
@ -23,6 +23,8 @@ static const char Version[] = "088.07";
#include <nall/stream/memory.hpp>
using namespace nall;
#include "interface.hpp"
//debugging function hook:
//no overhead (and no debugger invocation) if not compiled with -DDEBUGGER
//wraps testing of function to allow invocation without a defined callback

76
bsnes/emulator/interface.hpp Executable file
View File

@ -0,0 +1,76 @@
#ifndef EMULATOR_INTERFACE_HPP
#define EMULATOR_INTERFACE_HPP
namespace Emulator {
struct Interface {
struct Information {
string name;
unsigned width;
unsigned height;
unsigned frequency;
unsigned ports;
} information;
struct Media {
string name;
string filter;
unsigned id;
};
vector<Media> media;
struct Controller {
string name;
string port;
string device;
struct Input {
string name;
unsigned id;
};
vector<Input> inputs;
};
vector<Controller> controllers;
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;
} callback;
//audio/visual 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 id) {
if(callback.inputPoll) return callback.inputPoll(port, device, id);
return 0;
}
//cartridge interface
virtual bool loaded() { return false; }
virtual void load(unsigned id, const stream &memory, const string &markup = "") {}
virtual void unload() {}
//system interface
virtual void power() {}
virtual void reset() {}
virtual void run() {}
//utility functions
virtual void updatePalette() {}
};
}
#endif

View File

@ -60,7 +60,7 @@ void APU::main() {
//output = filter.run_lopass(output);
output = sclamp<16>(output);
interface->audioSample(output);
interface->audioSample(output, output);
tick();
}

View File

@ -1,7 +1,7 @@
#ifndef FC_HPP
#define FC_HPP
#include <base/base.hpp>
#include <emulator/emulator.hpp>
#include <processor/r6502/r6502.hpp>
namespace Famicom {

View File

@ -4,23 +4,4 @@ namespace Famicom {
Interface *interface = nullptr;
uint32_t Interface::videoColor(uint9_t source, uint16_t red, uint16_t green, uint16_t blue) {
red >>= 8, green >>= 8, blue >>= 8;
return red << 16 | green << 8 | blue << 0;
}
void Interface::videoRefresh(const uint32_t *data) {
}
void Interface::audioSample(const int16_t sample) {
}
int16_t Interface::inputPoll(bool port, unsigned device, unsigned id) {
return 0;
}
void Interface::message(const string &text) {
print(text, "\n");
}
}

View File

@ -1,10 +1,4 @@
struct Interface {
virtual uint32_t videoColor(uint9_t source, uint16_t red, uint16_t green, uint16_t blue);
virtual void videoRefresh(const uint32_t *data);
virtual void audioSample(int16_t sample);
virtual int16_t inputPoll(bool port, unsigned device, unsigned id);
virtual void message(const string &text);
struct Interface : Emulator::Interface {
};
extern Interface *interface;

View File

@ -47,7 +47,6 @@ void PPU::scanline() {
void PPU::frame() {
status.field ^= 1;
interface->videoRefresh(buffer);
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}

View File

@ -7,6 +7,9 @@ System system;
void System::run() {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(ppu.buffer, 4 * 256, 256, 240);
}
}
void System::runtosave() {
@ -32,7 +35,9 @@ void System::runthreadtosave() {
while(true) {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent);
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(ppu.buffer, 4 * 256, 256, 240);
}
}
}

View File

@ -46,9 +46,9 @@ void APU::main() {
noise.run();
master.run();
interface->audioSample(master.center, master.left, master.right);
interface->audioSample(master.left, master.right);
clock++;
clock += cpu.frequency;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(scheduler.active_thread = cpu.thread);
}
}

View File

@ -91,14 +91,15 @@ void CPU::interrupt_exec(uint16 pc) {
op_io();
}
void CPU::stop() {
bool CPU::stop() {
if(status.speed_switch) {
r.stop = false;
status.speed_switch = 0;
status.speed_double ^= 1;
if(status.speed_double == 0) frequency = 4 * 1024 * 1024;
if(status.speed_double == 1) frequency = 8 * 1024 * 1024;
return true;
}
return false;
}
void CPU::power() {

View File

@ -86,7 +86,7 @@ struct CPU : Processor::LR35902, Thread, MMIO {
void interrupt_raise(Interrupt id);
void interrupt_test();
void interrupt_exec(uint16 pc);
void stop();
bool stop();
void power();
void serialize(serializer&);

View File

@ -10,15 +10,15 @@ unsigned CPU::wram_addr(uint16 addr) const {
void CPU::mmio_joyp_poll() {
unsigned button = 0, dpad = 0;
button |= interface->inputPoll((unsigned)Input::Start) << 3;
button |= interface->inputPoll((unsigned)Input::Select) << 2;
button |= interface->inputPoll((unsigned)Input::B) << 1;
button |= interface->inputPoll((unsigned)Input::A) << 0;
button |= interface->inputPoll(0, 0, (unsigned)Input::Start) << 3;
button |= interface->inputPoll(0, 0, (unsigned)Input::Select) << 2;
button |= interface->inputPoll(0, 0, (unsigned)Input::B) << 1;
button |= interface->inputPoll(0, 0, (unsigned)Input::A) << 0;
dpad |= interface->inputPoll((unsigned)Input::Down) << 3;
dpad |= interface->inputPoll((unsigned)Input::Up) << 2;
dpad |= interface->inputPoll((unsigned)Input::Left) << 1;
dpad |= interface->inputPoll((unsigned)Input::Right) << 0;
dpad |= interface->inputPoll(0, 0, (unsigned)Input::Down) << 3;
dpad |= interface->inputPoll(0, 0, (unsigned)Input::Up) << 2;
dpad |= interface->inputPoll(0, 0, (unsigned)Input::Left) << 1;
dpad |= interface->inputPoll(0, 0, (unsigned)Input::Right) << 0;
status.joyp = 0x0f;
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;

View File

@ -21,10 +21,10 @@ void CPU::add_clocks(unsigned clocks) {
if((status.clock & 511) == 0) timer_8192hz();
if((status.clock & 1023) == 0) timer_4096hz();
ppu.clock -= clocks;
ppu.clock -= clocks * ppu.frequency;
if(ppu.clock < 0) co_switch(scheduler.active_thread = ppu.thread);
apu.clock -= clocks;
apu.clock -= clocks * apu.frequency;
if(apu.clock < 0) co_switch(scheduler.active_thread = apu.thread);
}

View File

@ -1,7 +1,7 @@
#ifndef GAMEBOY_HPP
#define GAMEBOY_HPP
#include <base/base.hpp>
#include <emulator/emulator.hpp>
#include <processor/lr35902/lr35902.hpp>
namespace GameBoy {
@ -24,7 +24,7 @@ namespace GameBoy {
struct Thread {
cothread_t thread;
unsigned frequency;
signed clock;
int64_t clock;
inline void create(void (*entrypoint)(), unsigned frequency) {
if(thread) co_delete(thread);

View File

@ -4,29 +4,4 @@ namespace GameBoy {
Interface *interface = nullptr;
void Interface::lcdScanline() {
}
void Interface::joypWrite(bool p15, bool p14) {
}
uint32_t Interface::videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue) {
red >>= 8, green >>= 8, blue >>= 8;
return red << 16 | green << 8 | blue << 0;
}
void Interface::videoRefresh(const uint32_t *data) {
}
void Interface::audioSample(int16_t center, int16_t left, int16_t right) {
}
bool Interface::inputPoll(unsigned id) {
return false;
}
void Interface::message(const string &text) {
print(text, "\n");
}
}

View File

@ -1,13 +1,6 @@
struct Interface {
virtual void lcdScanline();
virtual void joypWrite(bool p15, bool p14);
virtual uint32_t videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue);
virtual void videoRefresh(const uint32_t *data);
virtual void audioSample(int16_t center, int16_t left, int16_t right);
virtual bool inputPoll(unsigned id);
virtual void message(const string &text);
struct Interface : Emulator::Interface {
virtual void lcdScanline() {}
virtual void joypWrite(bool p15, bool p14) {}
};
extern Interface *interface;

View File

@ -41,7 +41,7 @@ void PPU::main() {
}
void PPU::add_clocks(unsigned clocks) {
clock += clocks;
clock += clocks * cpu.frequency;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
co_switch(scheduler.active_thread = cpu.thread);
}

View File

@ -11,7 +11,7 @@ void System::run() {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(ppu.screen);
interface->videoRefresh(ppu.screen, 4 * 160, 160, 144);
}
}
@ -35,7 +35,7 @@ void System::runthreadtosave() {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(ppu.screen);
interface->videoRefresh(ppu.screen, 4 * 160, 160, 144);
}
}
}

View File

@ -91,7 +91,7 @@ void CPU::keypad_run() {
bool test = regs.keypad.control.condition; //0 = OR, 1 = AND
for(unsigned n = 0; n < 10; n++) {
if(regs.keypad.control.flag[n] == false) continue;
bool input = interface->inputPoll(n);
bool input = interface->inputPoll(0, 0, n);
if(regs.keypad.control.condition == 0) test |= input;
if(regs.keypad.control.condition == 1) test &= input;
}

View File

@ -62,13 +62,13 @@ uint8 CPU::read(uint32 addr) {
//KEYINPUT
case 0x04000130:
for(unsigned n = 0; n < 8; n++) result |= interface->inputPoll(n) << n;
for(unsigned n = 0; n < 8; n++) result |= interface->inputPoll(0, 0, n) << n;
if((result & 0xc0) == 0xc0) result &= ~0xc0; //up+down cannot be pressed simultaneously
if((result & 0x30) == 0x30) result &= ~0x30; //left+right cannot be pressed simultaneously
return result ^ 0xff;
case 0x04000131:
result |= interface->inputPoll(8) << 0;
result |= interface->inputPoll(9) << 1;
result |= interface->inputPoll(0, 0, 8) << 0;
result |= interface->inputPoll(0, 0, 9) << 1;
return result ^ 0x03;
//KEYCNT

View File

@ -1,7 +1,7 @@
#ifndef GBA_HPP
#define GBA_HPP
#include <base/base.hpp>
#include <emulator/emulator.hpp>
#include <processor/arm/arm.hpp>
namespace GameBoyAdvance {

View File

@ -4,23 +4,69 @@ namespace GameBoyAdvance {
Interface *interface = nullptr;
uint32_t Interface::videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue) {
red >>= 8, green >>= 8, blue >>= 8;
return red << 16 | green << 8 | blue << 0;
bool Interface::loaded() {
return cartridge.loaded();
}
void Interface::videoRefresh(const uint32_t *data) {
void Interface::load(unsigned id, const stream &memory, const string &markup) {
if(id == 0) cartridge.load(markup, memory);
if(id == 1) memory.read(bios.data, min(bios.size, memory.size()));
}
void Interface::audioSample(int16_t lsample, int16_t rsample) {
void Interface::unload() {
cartridge.unload();
}
bool Interface::inputPoll(unsigned id) {
return false;
void Interface::power() {
system.power();
}
void Interface::message(const string &text) {
print(text, "\n");
void Interface::reset() {
system.power();
}
void Interface::run() {
system.run();
}
void Interface::updatePalette() {
video.generate_palette();
}
Interface::Interface() {
interface = this;
information.name = "Game Boy Advance";
information.width = 240;
information.height = 160;
information.frequency = 32768;
information.ports = 1;
{
Media medium;
medium.name = "Game Boy Advance";
medium.filter = "*.sfc";
medium.id = 0;
media.append(medium);
}
{
Controller controller;
controller.name = "Controller";
controller.port = 0;
controller.device = 0;
controller.inputs.append({"Up", 6});
controller.inputs.append({"Down", 7});
controller.inputs.append({"Left", 5});
controller.inputs.append({"Right", 4});
controller.inputs.append({"B", 1});
controller.inputs.append({"A", 0});
controller.inputs.append({"L", 9});
controller.inputs.append({"R", 8});
controller.inputs.append({"Select", 2});
controller.inputs.append({"Start", 3});
controllers.append(controller);
}
}
}

View File

@ -1,10 +1,23 @@
struct Interface {
virtual uint32_t videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue);
virtual void videoRefresh(const uint32_t *data);
virtual void audioSample(int16_t lsample, int16_t rsample);
virtual bool inputPoll(unsigned id);
#ifndef GBA_HPP
namespace GameBoyAdvance {
#endif
virtual void message(const string &text);
struct Interface : Emulator::Interface {
bool loaded();
void load(unsigned id, const stream &memory, const string &markup = "");
void unload();
void power();
void reset();
void run();
void updatePalette();
Interface();
};
extern Interface *interface;
#ifndef GBA_HPP
}
#endif

View File

@ -31,7 +31,7 @@ void System::run() {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) break;
}
interface->videoRefresh(ppu.output);
interface->videoRefresh(ppu.output, 4 * 240, 240, 160);
}
void System::runtosave() {
@ -53,7 +53,9 @@ void System::runthreadtosave() {
while(true) {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) interface->videoRefresh(ppu.output);
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(ppu.output, 4 * 240, 240, 160);
}
}
}

View File

@ -565,8 +565,8 @@ void LR35902::op_halt() {
}
void LR35902::op_stop() {
if(stop()) return;
r.stop = true;
stop();
while(r.stop == true) op_io();
}

View File

@ -11,7 +11,7 @@ struct LR35902 {
virtual void op_io() = 0;
virtual uint8 op_read(uint16 addr) = 0;
virtual void op_write(uint16 addr, uint8 data) = 0;
virtual void stop() = 0;
virtual bool stop() = 0;
virtual uint8 debugger_read(uint16 addr) { return 0u; }
void power();

View File

@ -1,6 +1,6 @@
#ifndef PROCESSOR_HPP
#define PROCESSOR_HPP
#include <base/base.hpp>
#include <emulator/emulator.hpp>
#endif

View File

@ -80,18 +80,18 @@ void ICD2::joypWrite(bool p15, bool p14) {
packetlock = true;
}
uint32_t ICD2::videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue) {
uint32_t ICD2::videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) {
return source;
}
void ICD2::videoRefresh(const uint32_t *data) {
void ICD2::videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height) {
}
void ICD2::audioSample(int16_t center, int16_t left, int16_t right) {
void ICD2::audioSample(int16_t left, int16_t right) {
audio.coprocessor_sample(left, right);
}
bool ICD2::inputPoll(unsigned id) {
int16_t ICD2::inputPoll(unsigned port, unsigned device, unsigned id) {
GameBoy::cpu.status.mlt_req = joyp_id & mlt_req;
unsigned data = 0x00;

View File

@ -1,9 +1,10 @@
void lcdScanline();
void joypWrite(bool p15, bool p14);
uint32_t videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t *data);
void audioSample(int16_t center, int16_t left, int16_t right);
bool inputPoll(unsigned id);
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 id);
struct Packet {
uint8 data[16];

View File

@ -2,7 +2,7 @@
uint2 Gamepad::data() {
if(counter >= 16) return 1;
uint2 result = interface->inputPoll(port, Input::Device::Joypad, 0, counter);
uint2 result = interface->inputPoll(port, (unsigned)Input::Device::Joypad, counter);
if(latched == 0) counter++;
return result;
}

View File

@ -18,8 +18,8 @@ void Justifier::enter() {
}
if(next < prev) {
int nx1 = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::X);
int ny1 = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Y);
int nx1 = interface->inputPoll(port, (unsigned)Input::Device::Justifier, 0 << 16 | (unsigned)Input::JustifierID::X);
int ny1 = interface->inputPoll(port, (unsigned)Input::Device::Justifier, 0 << 16 | (unsigned)Input::JustifierID::Y);
nx1 += player1.x;
ny1 += player1.y;
player1.x = max(-16, min(256 + 16, nx1));
@ -27,8 +27,8 @@ void Justifier::enter() {
}
if(next < prev && chained) {
int nx2 = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::X);
int ny2 = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Y);
int nx2 = interface->inputPoll(port, (unsigned)Input::Device::Justifiers, 1 << 16 | (unsigned)Input::JustifierID::X);
int ny2 = interface->inputPoll(port, (unsigned)Input::Device::Justifiers, 1 << 16 | (unsigned)Input::JustifierID::Y);
nx2 += player2.x;
ny2 += player2.y;
player2.x = max(-16, min(256 + 16, nx2));
@ -44,13 +44,13 @@ uint2 Justifier::data() {
if(counter >= 32) return 1;
if(counter == 0) {
player1.trigger = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Trigger);
player1.start = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Start);
player1.trigger = interface->inputPoll(port, (unsigned)Input::Device::Justifier, 0 << 16 | (unsigned)Input::JustifierID::Trigger);
player1.start = interface->inputPoll(port, (unsigned)Input::Device::Justifier, 0 << 16 | (unsigned)Input::JustifierID::Start);
}
if(counter == 0 && chained) {
player2.trigger = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Trigger);
player2.start = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Start);
player2.trigger = interface->inputPoll(port, (unsigned)Input::Device::Justifiers, 1 << 16 | (unsigned)Input::JustifierID::Trigger);
player2.start = interface->inputPoll(port, (unsigned)Input::Device::Justifiers, 1 << 16 | (unsigned)Input::JustifierID::Start);
}
switch(counter++) {

View File

@ -3,8 +3,8 @@
uint2 Mouse::data() {
if(counter >= 32) return 1;
int position_x = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
int position_y = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
int position_x = interface->inputPoll(port, (unsigned)Input::Device::Mouse, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
int position_y = interface->inputPoll(port, (unsigned)Input::Device::Mouse, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
bool direction_x = position_x < 0; //0 = right, 1 = left
bool direction_y = position_y < 0; //0 = down, 1 = up
@ -25,8 +25,8 @@ uint2 Mouse::data() {
case 6: return 0;
case 7: return 0;
case 8: return interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Right);
case 9: return interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Left);
case 8: return interface->inputPoll(port, (unsigned)Input::Device::Mouse, (unsigned)Input::MouseID::Right);
case 9: return interface->inputPoll(port, (unsigned)Input::Device::Mouse, (unsigned)Input::MouseID::Left);
case 10: return 0; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused)
case 11: return 0; // ||

View File

@ -18,8 +18,8 @@ uint2 Multitap::data() {
port2 = 3; //controller 4
}
bool data1 = interface->inputPoll(port, Input::Device::Multitap, port1, index);
bool data2 = interface->inputPoll(port, Input::Device::Multitap, port2, index);
bool data1 = interface->inputPoll(port, (unsigned)Input::Device::Multitap, port1 << 16 | index);
bool data2 = interface->inputPoll(port, (unsigned)Input::Device::Multitap, port2 << 16 | index);
return (data2 << 1) | (data1 << 0);
}

View File

@ -28,8 +28,8 @@ void SuperScope::enter() {
if(next < prev) {
//Vcounter wrapped back to zero; update cursor coordinates for start of new frame
int nx = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::X);
int ny = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Y);
int nx = interface->inputPoll(port, (unsigned)Input::Device::SuperScope, (unsigned)Input::SuperScopeID::X);
int ny = interface->inputPoll(port, (unsigned)Input::Device::SuperScope, (unsigned)Input::SuperScopeID::Y);
nx += x;
ny += y;
x = max(-16, min(256 + 16, nx));
@ -47,7 +47,7 @@ uint2 SuperScope::data() {
if(counter == 0) {
//turbo is a switch; toggle is edge sensitive
bool newturbo = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Turbo);
bool newturbo = interface->inputPoll(port, (unsigned)Input::Device::SuperScope, (unsigned)Input::SuperScopeID::Turbo);
if(newturbo && !turbo) {
turbo = !turbo; //toggle state
turbolock = true;
@ -58,7 +58,7 @@ uint2 SuperScope::data() {
//trigger is a button
//if turbo is active, trigger is level sensitive; otherwise, it is edge sensitive
trigger = false;
bool newtrigger = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Trigger);
bool newtrigger = interface->inputPoll(port, (unsigned)Input::Device::SuperScope, (unsigned)Input::SuperScopeID::Trigger);
if(newtrigger && (turbo || !triggerlock)) {
trigger = true;
triggerlock = true;
@ -67,11 +67,11 @@ uint2 SuperScope::data() {
}
//cursor is a button; it is always level sensitive
cursor = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Cursor);
cursor = interface->inputPoll(port, (unsigned)Input::Device::SuperScope, (unsigned)Input::SuperScopeID::Cursor);
//pause is a button; it is always edge sensitive
pause = false;
bool newpause = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Pause);
bool newpause = interface->inputPoll(port, (unsigned)Input::Device::SuperScope, (unsigned)Input::SuperScopeID::Pause);
if(newpause && !pauselock) {
pause = true;
pauselock = true;

View File

@ -68,7 +68,7 @@ uint2 USART::data() {
//Joypad
if(iobit()) {
if(counter >= 16) return 1;
uint2 result = interface->inputPoll(port, Input::Device::Joypad, 0, counter);
uint2 result = interface->inputPoll(port, (unsigned)Input::Device::Joypad, counter);
if(latched == 0) counter++;
return result;
}

View File

@ -4,21 +4,6 @@ namespace SuperFamicom {
Interface *interface = nullptr;
uint32_t Interface::videoColor(uint19_t source, uint16_t red, uint16_t green, uint16_t blue) {
red >>= 8, green >>= 8, blue >>= 8;
return red << 16 | green << 8 | blue << 0;
}
void Interface::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) {
}
void Interface::audioSample(int16_t l_sample, int16_t r_sample) {
}
int16_t Interface::inputPoll(bool port, Input::Device device, unsigned index, unsigned id) {
return 0;
}
void Interface::message(const string &text) {
print(text, "\n");
}

View File

@ -1,9 +1,4 @@
struct Interface {
virtual uint32_t videoColor(uint19_t source, uint16_t red, uint16_t green, uint16_t blue);
virtual void videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan);
virtual void audioSample(int16_t lsample, int16_t rsample);
virtual int16_t inputPoll(bool port, Input::Device device, unsigned index, unsigned id);
struct Interface : Emulator::Interface {
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
virtual void message(const string &text);
};

View File

@ -1,7 +1,7 @@
#ifndef SFC_HPP
#define SFC_HPP
#include <base/base.hpp>
#include <emulator/emulator.hpp>
#include <processor/arm/arm.hpp>
#include <processor/gsu/gsu.hpp>
#include <processor/hg51b/hg51b.hpp>

View File

@ -106,7 +106,7 @@ void Video::update() {
}
}
interface->videoRefresh(ppu.surface, hires, ppu.interlace(), ppu.overscan());
interface->videoRefresh(ppu.surface, 4 * (1024 >> ppu.interlace()), 256 << hires, 240 << ppu.interlace());
hires = false;
}

80
bsnes/target-ethos/Makefile Executable file
View File

@ -0,0 +1,80 @@
processors := arm gsu hg51b lr35902 r6502 r65816 spc700 upd96050
include processor/Makefile
include fc/Makefile
include sfc/Makefile
include gb/Makefile
include gba/Makefile
name := ethos
ui_objects := ui-ethos ui-interface ui-general
ui_objects += phoenix ruby
ui_objects += $(if $(call streq,$(platform),win),resource)
# platform
ifeq ($(platform),x)
ruby := video.glx video.xv video.sdl
ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao
ruby += input.sdl input.x
else ifeq ($(platform),osx)
ruby :=
ruby += audio.openal
ruby += input.carbon
else ifeq ($(platform),win)
ruby := video.direct3d video.wgl video.directdraw video.gdi
ruby += audio.directsound audio.xaudio2
ruby += input.rawinput input.directinput
endif
# phoenix
include phoenix/Makefile
link += $(phoenixlink)
# ruby
include ruby/Makefile
link += $(rubylink)
# rules
objects := $(ui_objects) $(objects)
objects := $(patsubst %,obj/%.o,$(objects))
obj/ui-ethos.o: $(ui)/ethos.cpp $(call rwildcard,$(ui)/)
obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/)
obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/)
obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/*)
$(call compile,$(rubyflags))
obj/phoenix.o: phoenix/phoenix.cpp $(call rwildcard,phoenix/*)
$(call compile,$(phoenixflags))
obj/resource.o: $(ui)/resource.rc
# windres --target=pe-i386 $(ui)/resource.rc obj/resource.o
windres $(ui)/resource.rc obj/resource.o
# targets
build: $(objects)
ifeq ($(platform),osx)
test -d ../$(name).app || mkdir -p ../$(name).app/Contents/MacOS
$(strip $(cpp) -o ../$(name).app/Contents/MacOS/$(name) $(objects) $(link))
else
$(strip $(cpp) -o out/$(name) $(objects) $(link))
endif
install:
ifeq ($(USER),root)
@echo Please do not run make install as root.
@echo The installer needs to know your home directory to install important files.
else ifeq ($(platform),x)
sudo install -D -m 755 out/$(name) $(DESTDIR)$(prefix)/bin/$(name)
mkdir -p ~/.config/$(name)
cp -R profile/* ~/.config/$(name)
cp data/cheats.xml ~/.config/$(name)/cheats.xml
chmod -R 777 ~/.config/$(name)
endif
uninstall:
ifeq ($(platform),x)
sudo rm $(DESTDIR)$(prefix)/bin/$(name)
endif

100
bsnes/target-ethos/ethos.cpp Executable file
View File

@ -0,0 +1,100 @@
#include "ethos.hpp"
Application *application = nullptr;
string Application::path(const string &filename) {
string path = {basepath, filename};
if(file::exists(path)) return path;
return {userpath, filename};
}
void Application::run() {
}
Application::Application(int argc, char **argv) {
application = this;
quit = false;
pause = false;
autopause = false;
char path[PATH_MAX];
auto unused = ::realpath(argv[0], path);
basepath = dir(path);
unused = ::userpath(path);
userpath = path;
if(Intrinsics::platform() == Intrinsics::Platform::Windows) {
userpath.append("bsnes/");
} else {
userpath.append(".config/bsnes/");
}
mkdir(userpath, 0755);
interface = new Interface;
auto gba = new GameBoyAdvance::Interface;
gba->callback.videoColor = {&Interface::videoColor, interface};
gba->callback.videoRefresh = {&Interface::videoRefresh, interface};
gba->callback.audioSample = {&Interface::audioSample, interface};
gba->callback.inputPoll = {&Interface::inputPoll, interface};
gba->updatePalette();
emulators.append(gba);
if(Intrinsics::platform() == Intrinsics::Platform::Windows) {
normalFont = "Tahoma, 8";
boldFont = "Tahoma, 8, Bold";
titleFont = "Tahoma, 16, Bold";
monospaceFont = "Lucida Console, 8";
} else {
normalFont = "Sans, 8";
boldFont = "Sans, 8, Bold";
titleFont = "Sans, 16, Bold";
monospaceFont = "Liberation Mono, 8";
}
filestream bios{"/home/byuu/.config/bsnes/Game Boy Advance.sys/bios.rom"};
gba->load(1, bios);
string manifest;
manifest.readfile("/media/sdb1/root/cartridges/Game Boy Advance/Super Mario Advance (US).gba/manifest.xml");
filestream fs{"/media/sdb1/root/cartridges/Game Boy Advance/Super Mario Advance (US).gba/program.rom"};
gba->load(0, fs, manifest);
gba->power();
videoWindow = new VideoWindow;
videoWindow->setVisible();
video.driver("OpenGL");
video.set(Video::Handle, videoWindow->viewport.handle());
video.set(Video::Synchronize, false);
video.set(Video::Depth, 24u);
video.init();
audio.driver("ALSA");
audio.set(Audio::Handle, videoWindow->viewport.handle());
audio.set(Audio::Synchronize, true);
audio.set(Audio::Latency, 80u);
audio.set(Audio::Frequency, 32768u);
audio.init();
input.driver("SDL");
input.set(Input::Handle, videoWindow->viewport.handle());
input.init();
while(quit == false) {
OS::processEvents();
gba->run();
}
}
Application::~Application() {
}
int main(int argc, char **argv) {
#if defined(PLATFORM_WINDOWS)
utf8_args(argc, argv);
#endif
new Application(argc, argv);
delete application;
return 0;
}

41
bsnes/target-ethos/ethos.hpp Executable file
View File

@ -0,0 +1,41 @@
#include <emulator/emulator.hpp>
#include <gba/interface/interface.hpp>
#include <nall/platform.hpp>
#include <nall/config.hpp>
#include <nall/stream/file.hpp>
#include <nall/stream/memory.hpp>
#include <nall/stream/vector.hpp>
using namespace nall;
#include <phoenix/phoenix.hpp>
using namespace phoenix;
#include <ruby/ruby.hpp>
using namespace ruby;
#include "interface/interface.hpp"
#include "general/general.hpp"
struct Application {
vector<Emulator::Interface*> emulators;
bool quit;
bool pause;
bool autopause;
unsigned depth;
string basepath;
string userpath;
string normalFont;
string boldFont;
string titleFont;
string monospaceFont;
string path(const string &filename);
void run();
Application(int argc, char **argv);
~Application();
};
extern Application *application;

View File

@ -0,0 +1,2 @@
#include "../ethos.hpp"
#include "video-window.cpp"

View File

@ -0,0 +1 @@
#include "video-window.hpp"

View File

@ -0,0 +1,12 @@
VideoWindow *videoWindow = nullptr;
VideoWindow::VideoWindow() {
setTitle("ethos");
setGeometry({1024, 600, 720, 480});
setBackgroundColor({0, 0, 0});
append(layout);
layout.append(viewport, {0, 0, 720, 480});
onClose = [&] { application->quit = true; };
}

View File

@ -0,0 +1,8 @@
struct VideoWindow : Window {
FixedLayout layout;
Viewport viewport;
VideoWindow();
};
extern VideoWindow *videoWindow;

View File

@ -0,0 +1,48 @@
#include "../ethos.hpp"
Interface *interface = nullptr;
uint32_t Interface::videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) {
red >>= 8, green >>= 8, blue >>= 8;
return red << 16 | green << 8 | blue << 0;
}
void Interface::videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height) {
uint32_t *output;
unsigned outputPitch;
if(video.lock(output, outputPitch, width, height)) {
pitch >>= 2, outputPitch >>= 2;
for(unsigned y = 0; y < height; y++) {
memcpy(output + y * outputPitch, data + y * pitch, 4 * width);
}
video.unlock();
video.refresh();
}
}
void Interface::audioSample(int16_t lsample, int16_t rsample) {
audio.sample(lsample, rsample);
}
int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned id) {
using nall::Keyboard;
static int16_t table[Scancode::Limit];
if(id == 0) input.poll(table);
switch(id) {
case 0: return table[keyboard(0)[Keyboard::X]]; //A
case 1: return table[keyboard(0)[Keyboard::Z]]; //B
case 2: return table[keyboard(0)[Keyboard::Apostrophe]]; //Select
case 3: return table[keyboard(0)[Keyboard::Return]]; //Start
case 4: return table[keyboard(0)[Keyboard::Right]]; //Right
case 5: return table[keyboard(0)[Keyboard::Left]]; //Left
case 6: return table[keyboard(0)[Keyboard::Up]]; //Up
case 7: return table[keyboard(0)[Keyboard::Down]]; //Down
case 8: return table[keyboard(0)[Keyboard::R]]; //R
case 9: return table[keyboard(0)[Keyboard::L]]; //L
}
return 0;
}

View File

@ -0,0 +1,8 @@
struct Interface {
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 id);
};
extern Interface *interface;

2
bsnes/target-ethos/resource.rc Executable file
View File

@ -0,0 +1,2 @@
1 24 "../data/bsnes.Manifest"
2 ICON DISCARDABLE "../data/bsnes.ico"

View File

@ -99,16 +99,16 @@ void InterfaceGB::setCheats(const lstring &list) {
//
uint32_t InterfaceGB::videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue) {
uint32_t InterfaceGB::videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) {
return color(red, green, blue);
}
void InterfaceGB::videoRefresh(const uint32_t *data) {
interface->videoRefresh(data, 160 * sizeof(uint32_t), 160, 144);
void InterfaceGB::videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height) {
interface->videoRefresh(data, pitch, width, height);
}
void InterfaceGB::audioSample(int16_t csample, int16_t lsample, int16_t rsample) {
signed samples[] = { lsample, rsample };
void InterfaceGB::audioSample(int16_t lsample, int16_t rsample) {
signed samples[] = {lsample, rsample};
dspaudio.sample(samples);
while(dspaudio.pending()) {
dspaudio.read(samples);
@ -116,6 +116,6 @@ void InterfaceGB::audioSample(int16_t csample, int16_t lsample, int16_t rsample)
}
}
bool InterfaceGB::inputPoll(unsigned id) {
int16_t InterfaceGB::inputPoll(unsigned port, unsigned device, unsigned id) {
return inputManager->gb.device.controller.poll(id);
}

View File

@ -16,8 +16,8 @@ struct InterfaceGB : InterfaceCore, GB::Interface {
void setCheats(const lstring &list = lstring{});
uint32_t videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t *data);
void audioSample(int16_t csample, int16_t lsample, int16_t rsample);
bool inputPoll(unsigned id);
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 id);
};

View File

@ -82,12 +82,12 @@ void InterfaceGBA::setCheats(const lstring &list) {
//
uint32_t InterfaceGBA::videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue) {
uint32_t InterfaceGBA::videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) {
return color(red, green, blue);
}
void InterfaceGBA::videoRefresh(const uint32_t *data) {
interface->videoRefresh(data, 240 * sizeof(uint32_t), 240, 160);
void InterfaceGBA::videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height) {
interface->videoRefresh(data, pitch, width, height);
}
void InterfaceGBA::audioSample(int16_t lsample, int16_t rsample) {
@ -99,6 +99,6 @@ void InterfaceGBA::audioSample(int16_t lsample, int16_t rsample) {
}
}
bool InterfaceGBA::inputPoll(unsigned id) {
int16_t InterfaceGBA::inputPoll(unsigned port, unsigned device, unsigned id) {
return inputManager->gba.device.controller.poll(id);
}

View File

@ -16,8 +16,8 @@ struct InterfaceGBA : InterfaceCore, GBA::Interface {
void setCheats(const lstring &list = lstring{});
uint32_t videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t *data);
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);
bool inputPoll(unsigned id);
int16_t inputPoll(unsigned port, unsigned device, unsigned id);
};

View File

@ -112,24 +112,25 @@ void InterfaceNES::setCheats(const lstring &list) {
//
uint32_t InterfaceNES::videoColor(uint9_t source, uint16_t red, uint16_t green, uint16_t blue) {
uint32_t InterfaceNES::videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) {
return color(red, green, blue);
}
void InterfaceNES::videoRefresh(const uint32_t *data) {
interface->videoRefresh(data, 256 * sizeof(uint32_t), 256, 240);
void InterfaceNES::videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height) {
interface->videoRefresh(data, pitch, width, height);
}
void InterfaceNES::audioSample(int16_t sample) {
signed samples[] = { sample };
void InterfaceNES::audioSample(int16_t lsample, int16_t rsample) {
//NES audio output is monaural; ruby only takes stereo audio
signed samples[] = {lsample};
dspaudio.sample(samples);
while(dspaudio.pending()) {
dspaudio.read(samples);
audio.sample(samples[0], samples[0]); //NES audio output is monaural; ruby only takes stereo audio
audio.sample(samples[0], samples[0]);
}
}
int16_t InterfaceNES::inputPoll(bool port, unsigned device, unsigned id) {
int16_t InterfaceNES::inputPoll(unsigned port, unsigned device, unsigned id) {
if(port == 0 && device == 0) return inputManager->nes.port1.gamepad.poll(id);
return 0;
}

View File

@ -18,8 +18,8 @@ struct InterfaceNES : InterfaceCore, FC::Interface {
void setCheats(const lstring &list = lstring{});
uint32_t videoColor(uint9_t source, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t *data);
void audioSample(int16_t sample);
int16_t inputPoll(bool port, unsigned device, unsigned id);
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 id);
};

View File

@ -305,24 +305,26 @@ void InterfaceSNES::setCheats(const lstring &list) {
//
uint32_t InterfaceSNES::videoColor(uint19_t source, uint16_t red, uint16_t green, uint16_t blue) {
uint32_t InterfaceSNES::videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) {
return color(red, green, blue);
}
void InterfaceSNES::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) {
unsigned width = 256 << hires;
unsigned height = 240 << interlace;
unsigned pitch = 1024 >> interlace;
void InterfaceSNES::videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height) {
//unsigned width = 256 << hires;
//unsigned height = 240 << interlace;
//unsigned pitch = 1024 >> interlace;
bool overscan = false;
//skip first line; as it is always blank (by SNES design)
if(overscan == false) data += 1 * 1024; // 8 + 224 + 8
if(overscan == true ) data += 9 * 1024; // 0 + 240 + 0
interface->videoRefresh(data, pitch * sizeof(uint32_t), width, height);
interface->videoRefresh(data, pitch, width, height);
}
void InterfaceSNES::audioSample(int16_t lsample, int16_t rsample) {
signed samples[] = { lsample, rsample };
signed samples[] = {lsample, rsample};
dspaudio.sample(samples);
while(dspaudio.pending()) {
dspaudio.read(samples);
@ -330,30 +332,36 @@ void InterfaceSNES::audioSample(int16_t lsample, int16_t rsample) {
}
}
int16_t InterfaceSNES::inputPoll(bool port, SFC::Input::Device device, unsigned index, unsigned id) {
int16_t InterfaceSNES::inputPoll(unsigned port, unsigned device, unsigned id) {
if(port == 0) {
if(device == SFC::Input::Device::Joypad) return inputManager->snes.port1.gamepad.poll(id);
if(device == SFC::Input::Device::Multitap) {
if(device == (unsigned)SFC::Input::Device::Joypad) return inputManager->snes.port1.gamepad.poll(id);
if(device == (unsigned)SFC::Input::Device::Multitap) {
unsigned index = id >> 16;
id &= 65535;
if(index == 0) return inputManager->snes.port1.multitap1.poll(id);
if(index == 1) return inputManager->snes.port1.multitap2.poll(id);
if(index == 2) return inputManager->snes.port1.multitap3.poll(id);
if(index == 3) return inputManager->snes.port1.multitap4.poll(id);
}
if(device == SFC::Input::Device::Mouse) return inputManager->snes.port1.mouse.poll(id);
if(device == (unsigned)SFC::Input::Device::Mouse) return inputManager->snes.port1.mouse.poll(id);
}
if(port == 1) {
if(device == SFC::Input::Device::Joypad) return inputManager->snes.port2.gamepad.poll(id);
if(device == SFC::Input::Device::Multitap) {
if(device == (unsigned)SFC::Input::Device::Joypad) return inputManager->snes.port2.gamepad.poll(id);
if(device == (unsigned)SFC::Input::Device::Multitap) {
unsigned index = id >> 16;
id &= 65535;
if(index == 0) return inputManager->snes.port2.multitap1.poll(id);
if(index == 1) return inputManager->snes.port2.multitap2.poll(id);
if(index == 2) return inputManager->snes.port2.multitap3.poll(id);
if(index == 3) return inputManager->snes.port2.multitap4.poll(id);
}
if(device == SFC::Input::Device::Mouse) return inputManager->snes.port2.mouse.poll(id);
if(device == SFC::Input::Device::SuperScope) return inputManager->snes.port2.superScope.poll(id);
if(device == SFC::Input::Device::Justifier) return inputManager->snes.port2.justifier1.poll(id);
if(device == SFC::Input::Device::Justifiers) {
if(device == (unsigned)SFC::Input::Device::Mouse) return inputManager->snes.port2.mouse.poll(id);
if(device == (unsigned)SFC::Input::Device::SuperScope) return inputManager->snes.port2.superScope.poll(id);
if(device == (unsigned)SFC::Input::Device::Justifier) return inputManager->snes.port2.justifier1.poll(id);
if(device == (unsigned)SFC::Input::Device::Justifiers) {
unsigned index = id >> 16;
id &= 65535;
if(index == 0) return inputManager->snes.port2.justifier1.poll(id);
if(index == 1) return inputManager->snes.port2.justifier2.poll(id);
}

View File

@ -27,10 +27,10 @@ struct InterfaceSNES : InterfaceCore, SFC::Interface {
void setCheats(const lstring &list = lstring{});
uint32_t videoColor(uint19_t source, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan);
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 id);
string path(SFC::Cartridge::Slot slot, const string &hint);
void message(const string &text);