Update to bsnes v058 release.

We've tested the latest release on at least a dozen computers now, all seems to be in order for a release.
Changelog:
    - added 21fx support (more on this later)
    - added movie recording and playback support
    - added rewind support (enable under Settings->Configuration->Advanced, use backspace key to rewind)
    - added speedup (fast forward) and slowdown key bindings
    - audio no longer stutters on Windows when moving or resizing the main window
    - co-processors can now specify their own clock rates instead of sharing the S-CPU clock rate
    - Super Game Boy 2 now runs at the correct hardware speed, and not 2.4% faster like the Super Game Boy 1 does
    - added Vsync support to the Windows OpenGL driver (Intel graphics drivers do not support this option, because their engineers are lazy)
    - OpenGL driver no longer re-initializes when changing video synchronization, helps pixel shaders
    - refactored user interface compilation; now split into several object files, auto-generated MOC files placed under src/obj/
    - worked around a bug in the PulseAudio sound server that was causing the ALSA output driver to lock up [BearOso]
    - rewrote and simplified the save state manager, it is no longer a part of the core
    - S-DD1 and SPC7110 can now access up to 256MB via their MMCs
    - re-added background and OAM layer toggling under the tools dialog
    - added config file options to adjust emulation speed levels (config.system.speed*)
    - added snesreader, snesfilter and supergameboy support to the OS X port
    - added a really neat pixel shader that can perform point scaling to non-even multiples, eg it looks great even with aspect correction [Fes]
    - upgraded to Qt 4.6.0 official
Debugger changelog:
    - added memory export and import to the memory editor
    - added bus usage analyzer: logs opcodes, memory reads, memory writes and M/X states to usage.bin file
    - added disassembler that can trace both forward and backward from the current execution address
    - extended read/write breakpoints to the S-SMP
    - re-added trace masking option
Errata: there is one known bug in Qt 4.6.0 that affects the Windows port: menus attached to buttons show up as invisible on Windows Vista and above. I only use this on the file load dialog options button, and only to toggle the information pane on and off. Given that this is less severe than the bugs in the beta versions, I've upgraded anyway. I'll submit a bug report to the Qt team for this shortly. Also, my sincerest thanks to Bradley Hughes from the Qt development team for quickly fixing this show-stopper bug that greatly affected performance in bsnes v056.
This commit is contained in:
byuu 2009-12-09 13:34:03 +00:00
parent 54c7b4692d
commit 6ec765f2c4
132 changed files with 2332 additions and 1163 deletions

View File

@ -81,6 +81,7 @@ objects += system cartridge cheat
objects += memory smemory cpu cpucore scpu smp smpcore ssmp sdsp ppu bppu
objects += supergameboy superfx sa1
objects += bsx srtc sdd1 spc7110 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010 st011 st018
objects += 21fx
######################
### implicit rules ###
@ -182,6 +183,7 @@ obj/obc1.o : chip/obc1/obc1.cpp chip/obc1/*
obj/st010.o : chip/st010/st010.cpp chip/st010/*
obj/st011.o : chip/st011/st011.cpp chip/st011/*
obj/st018.o : chip/st018/st018.cpp chip/st018/*
obj/21fx.o : chip/21fx/21fx.cpp chip/21fx/*
###############
### targets ###

View File

@ -1,6 +1,6 @@
static const char bsnesVersion[] = "0.057";
static const char bsnesVersion[] = "0.058";
static const char bsnesTitle[] = "bsnes";
static const unsigned bsnesSaveStateVersion = 3;
static const unsigned bsnesSerializerVersion = 4;
//S-DSP can be encapsulated into a state machine using #define magic
//this avoids ~2.048m co_switch() calls per second (~5% speedup)

View File

@ -125,6 +125,8 @@ Cartridge::Type Cartridge::detect_image_type(uint8_t *data, unsigned size) const
return info.type;
}
bool Cartridge::has_21fx() const { return s21fx.exists(); }
void Cartridge::serialize(serializer &s) {
if(memory::cartram.size() != 0 && memory::cartram.size() != ~0) {
s.array(memory::cartram.data(), memory::cartram.size());

View File

@ -67,6 +67,7 @@ public:
property_t<bool> has_dsp1, has_dsp2, has_dsp3, has_dsp4;
property_t<bool> has_obc1;
property_t<bool> has_st010, has_st011, has_st018;
bool has_21fx() const;
//main interface
void load(Mode);

165
src/chip/21fx/21fx.cpp Normal file
View File

@ -0,0 +1,165 @@
#include <../base.hpp>
//$21f0 command port (r/w)
//-------------------
//$00 set data port address (sr[3-0] = address)
//$01 set audio track number (sr[1-0] = track number)
//$02 set volume (sr[1] = left, sr[0] = right)
//$03 set audio state (sr[0].d1 = pause, sr[0].d0 = repeat)
//
//d7 = data port busy
//d6 = audio port busy
//d5 = audio playing
//d4 = reserved (0)
//d3-d0 = version (1)
//
//
//$21f1 parameter port (w)
//---------------------
//(shift register)
//
//
//$21f2 data port (r)
//----------------
//(auto-increment read port)
#define S21FX_CPP
namespace SNES {
S21fx s21fx;
#include "serialization.cpp"
void S21fx::enter() {
scheduler.clock.cop_freq = 44100;
while(true) {
int16 left = 0, right = 0;
if((mmio.status & AudioPlaying) && !mmio.audio_pause) {
if(audiofile.open()) {
if(audiofile.end()) {
if(!mmio.audio_repeat) mmio.status &= ~AudioPlaying;
audiofile.seek(mmio.audio_offset = 58);
} else {
mmio.audio_offset += 4;
left = audiofile.readl(2);
right = audiofile.readl(2);
}
} else {
mmio.status &= ~AudioPlaying;
}
}
left = sclamp<16>((double)left * (double)mmio.audio_volume_left / 255.0);
right = sclamp<16>((double)right * (double)mmio.audio_volume_right / 255.0);
audio.coprocessor_sample(left, right);
scheduler.addclocks_cop(1);
scheduler.sync_copcpu();
}
}
void S21fx::init() {
}
void S21fx::enable() {
audio.coprocessor_enable(true);
audio.coprocessor_frequency(44100.0);
for(unsigned i = 0x21f0; i <= 0x21f7; i++) {
memory::mmio.map(i, *this);
}
if(datafile.open()) datafile.close();
datafile.open(string() << basepath << "21fx.bin", file::mode_read);
}
void S21fx::power() {
reset();
}
void S21fx::reset() {
mmio.status = DataPortBusy | AudioBusy;
mmio.shift_register = 0;
mmio.data_offset = 0;
mmio.audio_offset = 0;
mmio.audio_track = 0;
mmio.audio_volume_left = 255;
mmio.audio_volume_right = 255;
mmio.audio_repeat = false;
mmio.audio_pause = false;
}
uint8 S21fx::mmio_read(unsigned addr) {
addr &= 0xffff;
if(addr == 0x21f0) {
return mmio.status | 0x01;
}
if(addr == 0x21f2) {
if(mmio.status & DataPortBusy) return 0x00;
mmio.data_offset++;
if(datafile.open()) return datafile.read();
return 0x00;
}
return 0x00;
}
void S21fx::mmio_write(unsigned addr, uint8 data) {
addr &= 0xffff;
if(addr == 0x21f0) {
if(data == 0x00) {
mmio.data_offset = mmio.shift_register & 0xffffffff;
if(datafile.open()) {
datafile.seek(mmio.data_offset);
}
mmio.status &= ~DataPortBusy;
}
if(data == 0x01) {
mmio.audio_track = mmio.shift_register & 0xffff;
if(audiofile.open()) audiofile.close();
char track[16];
sprintf(track, "%.5u", mmio.audio_track);
if(audiofile.open(string() << basepath << "audio" << track << ".wav", file::mode_read)) {
audiofile.seek(mmio.audio_offset = 58); //skip WAV header
}
mmio.status &= ~(AudioBusy | AudioPlaying);
}
if(data == 0x02) {
mmio.audio_volume_left = mmio.shift_register >> 8;
mmio.audio_volume_right = mmio.shift_register >> 0;
}
if(data == 0x03) {
mmio.status |= AudioPlaying;
mmio.audio_repeat = mmio.shift_register & 1;
mmio.audio_pause = mmio.shift_register & 2;
}
mmio.shift_register = 0;
}
if(addr == 0x21f1) {
mmio.shift_register = (mmio.shift_register << 8) | data;
}
}
void S21fx::base(const string& path) {
basepath = path;
}
bool S21fx::exists() {
return file::exists(string() << basepath << "21fx.bin");
}
S21fx::S21fx() {
}
}

44
src/chip/21fx/21fx.hpp Normal file
View File

@ -0,0 +1,44 @@
class S21fx : public MMIO {
public:
void enter();
void init();
void enable();
void power();
void reset();
uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data);
void base(const string &path);
bool exists();
void serialize(serializer&);
S21fx();
private:
string basepath;
file datafile;
file audiofile;
enum Flag {
DataPortBusy = 0x80,
AudioBusy = 0x40,
AudioPlaying = 0x20,
};
struct MMIO {
uint8 status;
uint64 shift_register;
uint32 data_offset;
uint32 audio_offset;
uint16 audio_track;
uint8 audio_volume_left;
uint8 audio_volume_right;
bool audio_repeat;
bool audio_pause;
} mmio;
};
extern S21fx s21fx;

View File

@ -0,0 +1,31 @@
#ifdef S21FX_CPP
void S21fx::serialize(serializer &s) {
s.integer(mmio.status);
s.integer(mmio.shift_register);
s.integer(mmio.data_offset);
s.integer(mmio.audio_offset);
s.integer(mmio.audio_track);
s.integer(mmio.audio_volume_left);
s.integer(mmio.audio_volume_right);
s.integer(mmio.audio_repeat);
s.integer(mmio.audio_pause);
//flush file handles and indices, as a different track may be playing,
//or the file offsets may be at the wrong location ...
if(datafile.open()) datafile.close();
if(datafile.open(string() << basepath << "21fx.bin", file::mode_read)) {
datafile.seek(mmio.data_offset);
}
if(audiofile.open()) audiofile.close();
char track[16];
sprintf(track, "%.5u", mmio.audio_track);
if(audiofile.open(string() << basepath << "audio" << track << ".wav", file::mode_read)) {
audiofile.seek(mmio.audio_offset);
}
}
#endif

View File

@ -14,3 +14,4 @@
#include "st010/st010.hpp"
#include "st011/st011.hpp"
#include "st018/st018.hpp"
#include "21fx/21fx.hpp"

View File

@ -55,10 +55,10 @@ uint8 SDD1::mmio_read(unsigned addr) {
}
switch(addr) {
case 0x4804: return (mmc[0] >> 20) & 7;
case 0x4805: return (mmc[1] >> 20) & 7;
case 0x4806: return (mmc[2] >> 20) & 7;
case 0x4807: return (mmc[3] >> 20) & 7;
case 0x4804: return mmc[0] >> 20;
case 0x4805: return mmc[1] >> 20;
case 0x4806: return mmc[2] >> 20;
case 0x4807: return mmc[3] >> 20;
}
return cpu.regs.mdr;
@ -84,10 +84,10 @@ void SDD1::mmio_write(unsigned addr, uint8 data) {
case 0x4800: sdd1_enable = data; break;
case 0x4801: xfer_enable = data; break;
case 0x4804: mmc[0] = (data & 7) << 20; break;
case 0x4805: mmc[1] = (data & 7) << 20; break;
case 0x4806: mmc[2] = (data & 7) << 20; break;
case 0x4807: mmc[3] = (data & 7) << 20; break;
case 0x4804: mmc[0] = data << 20; break;
case 0x4805: mmc[1] = data << 20; break;
case 0x4806: mmc[2] = data << 20; break;
case 0x4807: mmc[3] = data << 20; break;
}
}

View File

@ -535,17 +535,17 @@ void SPC7110::mmio_write(unsigned addr, uint8 data) {
case 0x4831: {
r4831 = data;
dx_offset = datarom_addr((data & 7) * 0x100000);
dx_offset = datarom_addr(data * 0x100000);
} break;
case 0x4832: {
r4832 = data;
ex_offset = datarom_addr((data & 7) * 0x100000);
ex_offset = datarom_addr(data * 0x100000);
} break;
case 0x4833: {
r4833 = data;
fx_offset = datarom_addr((data & 7) * 0x100000);
fx_offset = datarom_addr(data * 0x100000);
} break;
case 0x4834: r4834 = data; break;

View File

@ -6,9 +6,11 @@ namespace SNES {
SuperGameBoy supergameboy;
void SuperGameBoy::enter() {
scheduler.clock.cop_freq = (version == SuperGameBoy1 ? 2147727 : 2097152);
if(!sgb_run) while(true) {
audio.coprocessor_sample(0, 0);
scheduler.addclocks_cop(10);
scheduler.addclocks_cop(1);
scheduler.sync_copcpu();
}
@ -21,7 +23,7 @@ void SuperGameBoy::enter() {
//SNES audio is notoriously quiet; lower Game Boy samples to match SGB sound effects
audio.coprocessor_sample(left / 3, right / 3);
}
scheduler.addclocks_cop(samples * 10);
scheduler.addclocks_cop(samples);
scheduler.sync_copcpu();
}
}
@ -96,8 +98,11 @@ void SuperGameBoy::enable() {
}
void SuperGameBoy::power() {
//determine whether to use SGB1 or SGB2 mode based on the cartridge title (look for the '2')
version = memory::cartrom[0x7fcd] != 0x32 ? SuperGameBoy1 : SuperGameBoy2;
audio.coprocessor_enable(true);
audio.coprocessor_frequency(2147727.2);
audio.coprocessor_frequency(version == SuperGameBoy1 ? 2147727.0 : 2097152.0);
bus.map(Bus::MapDirect, 0x00, 0x3f, 0x6000, 0x7fff, *this);
bus.map(Bus::MapDirect, 0x80, 0xbf, 0x6000, 0x7fff, *this);
@ -107,7 +112,7 @@ void SuperGameBoy::power() {
sgb_rtc(memory::gbrtc.data(), memory::gbrtc.size() == -1U ? 0 : memory::gbrtc.size());
//determine whether to use SGB1 or SGB2 mode based on the cartridge title (look for the '2')
if(sgb_init) sgb_init(memory::cartrom[0x7fcd] != 0x32 ? SuperGameBoy1 : SuperGameBoy2);
if(sgb_init) sgb_init(version);
if(sgb_power) sgb_power();
}
@ -121,6 +126,7 @@ void SuperGameBoy::unload() {
void SuperGameBoy::serialize(serializer &s) {
s.integer(row);
s.integer(version);
if(sgb_serialize) sgb_serialize(s);
}

View File

@ -20,6 +20,7 @@ public:
private:
uint32_t samplebuffer[4096];
unsigned row;
bool version;
enum { SuperGameBoy1 = 0, SuperGameBoy2 = 1 };
function<void (uint8_t*, unsigned)> sgb_rom;

View File

@ -102,7 +102,7 @@ uint32 CPUcore::decode(uint8 offset_type, uint32 addr) {
return(r & 0xffffff);
}
void CPUcore::disassemble_opcode(char *output) {
void CPUcore::disassemble_opcode(char *output, uint32 addr) {
static reg24_t pc;
char t[256];
char *s = output;
@ -112,7 +112,7 @@ void CPUcore::disassemble_opcode(char *output) {
return;
}
pc.d = regs.pc.d;
pc.d = addr;
sprintf(s, "%.6x ", (uint32)pc.d);
uint8 op = dreadb(pc.d); pc.w++;

View File

@ -22,7 +22,7 @@
OPTYPE_RELW, //relw
};
void disassemble_opcode(char *output);
void disassemble_opcode(char *output, uint32 addr);
uint8 dreadb(uint32 addr);
uint16 dreadw(uint32 addr);
uint32 dreadl(uint32 addr);

View File

@ -10,25 +10,36 @@ void sCPUDebug::op_step() {
debugger.breakpoint_test(Debugger::Breakpoint::CPUBus, Debugger::Breakpoint::Exec, regs.pc, 0x00);
}
if(debugger.trace_cpu) {
char t[256];
disassemble_opcode(t);
debugger.tracefile.print(string() << t << "\n");
}
usage[regs.pc] &= ~(UsageFlagM | UsageFlagX);
usage[regs.pc] |= UsageExec | (regs.p.m << 1) | (regs.p.x << 0);
opcode_pc = regs.pc;
if(step_event) step_event();
sCPU::op_step();
scheduler.sync_cpusmp();
}
uint8 sCPUDebug::op_read(uint32 addr) {
uint8 data = sCPU::op_read(addr);
usage[addr] |= UsageRead;
debugger.breakpoint_test(Debugger::Breakpoint::CPUBus, Debugger::Breakpoint::Read, addr, data);
return data;
}
void sCPUDebug::op_write(uint32 addr, uint8 data) {
sCPU::op_write(addr, data);
usage[addr] |= UsageWrite;
usage[addr] &= ~UsageExec;
debugger.breakpoint_test(Debugger::Breakpoint::CPUBus, Debugger::Breakpoint::Write, addr, data);
}
sCPUDebug::sCPUDebug() {
usage = new uint8[1 << 24]();
opcode_pc = 0x8000;
}
sCPUDebug::~sCPUDebug() {
delete[] usage;
}
#endif

View File

@ -1,6 +1,21 @@
class sCPUDebug : public sCPU {
public:
function<void ()> step_event;
enum Usage {
UsageRead = 0x80,
UsageWrite = 0x40,
UsageExec = 0x20,
UsageFlagM = 0x02,
UsageFlagX = 0x01,
};
uint8 *usage;
uint32 opcode_pc; //points to the current opcode, used to backtrace on read/write breakpoints
void op_step();
uint8 op_read(uint32 addr);
void op_write(uint32 addr, uint8 data);
sCPUDebug();
~sCPUDebug();
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 935 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 900 B

View File

@ -8,7 +8,7 @@
#include <nall/stdint.hpp>
#include <nall/utility.hpp>
#if defined(PLATFORM_X)
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
#include <dlfcn.h>
#elif defined(PLATFORM_WIN)
#include <windows.h>
@ -52,6 +52,34 @@ namespace nall {
return dlsym((void*)handle, name);
}
inline void library::close() {
if(!handle) return;
dlclose((void*)handle);
handle = 0;
}
#elif defined(PLATFORM_OSX)
inline bool library::open(const char *name) {
if(handle) close();
char *t = new char[strlen(name) + 256];
strcpy(t, "lib");
strcat(t, name);
strcat(t, ".dylib");
handle = (uintptr_t)dlopen(t, RTLD_LAZY);
if(!handle) {
strcpy(t, "/usr/local/lib/lib");
strcat(t, name);
strcat(t, ".dylib");
handle = (uintptr_t)dlopen(t, RTLD_LAZY);
}
delete[] t;
return handle;
}
inline void* library::sym(const char *name) {
if(!handle) return 0;
return dlsym((void*)handle, name);
}
inline void library::close() {
if(!handle) return;
dlclose((void*)handle);

View File

@ -136,7 +136,7 @@ namespace nall {
function() { data.fn_call = 0; }
function(void *fn) {
data.fn_call = &fn_call_global;
data.fn_call = fn ? &fn_call_global : 0;
data.fn_global = (R (*)(PL))fn;
}

View File

@ -1,7 +1,5 @@
/*
audio.alsa (2008-08-12)
authors: Nach, RedDwarf
*/
//audio.alsa (2009-11-30)
//authors: BearOso, byuu, Nach, RedDwarf
#include <alsa/asoundlib.h>
@ -77,6 +75,22 @@ public:
buffer.data[buffer.length++] = left + (right << 16);
if(buffer.length < device.period_size) return;
snd_pcm_sframes_t avail;
do {
avail = snd_pcm_avail_update(device.handle);
if(avail < 0) snd_pcm_recover(device.handle, avail, 1);
if(avail < buffer.length) {
if(settings.synchronize == false) {
buffer.length = 0;
return;
}
int error = snd_pcm_wait(device.handle, -1);
if(error < 0) snd_pcm_recover(device.handle, error, 1);
}
} while(avail < buffer.length);
//below code has issues with PulseAudio sound server
#if 0
if(settings.synchronize == false) {
snd_pcm_sframes_t avail = snd_pcm_avail_update(device.handle);
if(avail < device.period_size) {
@ -84,6 +98,7 @@ public:
return;
}
}
#endif
uint32_t *buffer_ptr = buffer.data;
int i = 4;
@ -114,12 +129,13 @@ public:
bool init() {
term();
if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) {
term();
return false;
}
/* //below code will not work with 24khz frequency rate (ALSA library bug)
//below code will not work with 24khz frequency rate (ALSA library bug)
#if 0
if(snd_pcm_set_params(device.handle, device.format, SND_PCM_ACCESS_RW_INTERLEAVED,
device.channels, settings.frequency, 1, settings.latency * 1000) < 0) {
//failed to set device parameters
@ -129,7 +145,8 @@ public:
if(snd_pcm_get_params(device.handle, &device.buffer_size, &device.period_size) < 0) {
device.period_size = settings.latency * 1000 * 1e-6 * settings.frequency / 4;
}*/
}
#endif
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;

View File

@ -14,7 +14,6 @@ InputInterface input;
const char *Video::Handle = "Handle";
const char *Video::Synchronize = "Synchronize";
const char *Video::Filter = "Filter";
const char *Video::GLSL = "GLSL";
const char *Video::FragmentShader = "FragmentShader";
const char *Video::VertexShader = "VertexShader";

View File

@ -3,7 +3,6 @@ public:
static const char *Handle;
static const char *Synchronize;
static const char *Filter;
static const char *GLSL;
static const char *FragmentShader;
static const char *VertexShader;

View File

@ -2,7 +2,7 @@
video.glx
author: byuu
license: public domain
last updated: 2008-08-20
last updated: 2009-12-08
Design notes:
SGI's GLX is the X11/Xlib interface to OpenGL.
@ -34,6 +34,8 @@ static Bool glx_wait_for_map_notify(Display *d, XEvent *e, char *arg) {
class pVideoGLX : public OpenGL {
public:
int (*glSwapInterval)(int);
Display *display;
int screen;
Window xwindow;
@ -60,7 +62,6 @@ public:
if(name == Video::Handle) return true;
if(name == Video::Synchronize) return true;
if(name == Video::Filter) return true;
if(name == Video::GLSL) return true;
if(name == Video::FragmentShader) return true;
if(name == Video::VertexShader) return true;
return false;
@ -82,7 +83,7 @@ public:
if(name == Video::Synchronize) {
if(settings.synchronize != any_cast<bool>(value)) {
settings.synchronize = any_cast<bool>(value);
if(glxcontext) init();
if(glSwapInterval) glSwapInterval(settings.synchronize);
return true;
}
}
@ -151,11 +152,8 @@ public:
//let GLX determine the best Visual to use for GL output; provide a few hints
//note: some video drivers will override double buffering attribute
int elements = 0;
int attributelist[] = { GLX_RGBA, None };
int attributelist_sync[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
XVisualInfo *vi = glXChooseVisual(display, screen,
settings.synchronize ? attributelist_sync : attributelist);
int attributelist[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
XVisualInfo *vi = glXChooseVisual(display, screen, attributelist);
//Window settings.handle has already been realized, most likely with DefaultVisual.
//GLX requires that the GL output window has the same Visual as the GLX context.
@ -190,13 +188,10 @@ public:
settings.height = 256;
//vertical synchronization
int (*glSwapInterval)(int);
glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalEXT");
if(glSwapInterval) glSwapInterval(settings.synchronize);
glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalSGI");
if(glSwapInterval) glSwapInterval(settings.synchronize);
glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalMESA");
if(glSwapInterval) glSwapInterval(settings.synchronize);
if(!glSwapInterval) glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalEXT");
if(!glSwapInterval) glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalSGI");
if(!glSwapInterval) glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalMESA");
if( glSwapInterval) glSwapInterval(settings.synchronize);
return true;
}
@ -220,7 +215,7 @@ public:
}
}
pVideoGLX() {
pVideoGLX() : glSwapInterval(0) {
settings.handle = 0;
settings.synchronize = false;
xwindow = 0;

View File

@ -9,6 +9,8 @@ namespace ruby {
class pVideoWGL : public OpenGL {
public:
BOOL (APIENTRY *glSwapInterval)(int);
HDC display;
HGLRC wglcontext;
HWND window;
@ -27,7 +29,6 @@ public:
if(name == Video::Handle) return true;
if(name == Video::Synchronize) return true;
if(name == Video::Filter) return true;
if(name == Video::GLSL) return true;
if(name == Video::FragmentShader) return true;
if(name == Video::VertexShader) return true;
return false;
@ -105,7 +106,7 @@ public:
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | (settings.synchronize ? PFD_DOUBLEBUFFER : 0);
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
display = GetDC(settings.handle);
@ -120,9 +121,8 @@ public:
settings.height = 256;
//vertical synchronization
BOOL (APIENTRY *glSwapInterval)(int);
glSwapInterval = (BOOL (APIENTRY*)(int))glGetProcAddress("wglSwapIntervalEXT");
if(glSwapInterval) glSwapInterval(settings.synchronize);
if(!glSwapInterval) glSwapInterval = (BOOL (APIENTRY*)(int))glGetProcAddress("wglSwapIntervalEXT");
if( glSwapInterval) glSwapInterval(settings.synchronize);
return true;
}
@ -136,7 +136,7 @@ public:
}
}
pVideoWGL() {
pVideoWGL() : glSwapInterval(0) {
settings.handle = 0;
settings.synchronize = false;
settings.filter = 0;

View File

@ -5,5 +5,3 @@ mkdir nall
mkdir ruby
xcopy /E ..\..\..\nall nall
xcopy /E ..\..\..\ruby ruby
del ruby\test*
del ruby\cc.*

View File

@ -8,5 +8,3 @@ cp -r ../../../ruby ./ruby
rm -r libco/doc
rm -r libco/test
rm ruby/test*
rm ruby/cc.*

View File

@ -55,6 +55,10 @@ template<unsigned mode, unsigned bg, unsigned color_depth>
void bPPU::render_line_bg(uint8 pri0_pos, uint8 pri1_pos) {
if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) return;
if(render_enabled(bg, 0) == false) pri0_pos = 0;
if(render_enabled(bg, 1) == false) pri1_pos = 0;
if(pri0_pos == 0 && pri1_pos == 0) return;
const bool bg_enabled = regs.bg_enabled[bg];
const bool bgsub_enabled = regs.bgsub_enabled[bg];

View File

@ -16,6 +16,10 @@ template<unsigned bg>
void bPPU::render_line_mode7(uint8 pri0_pos, uint8 pri1_pos) {
if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) return;
if(render_enabled(bg, 0) == false) pri0_pos = 0;
if(render_enabled(bg, 1) == false) pri1_pos = 1;
if(pri0_pos == 0 && pri1_pos == 0) return;
int32 px, py;
int32 tx, ty, tile, palette;

View File

@ -189,6 +189,12 @@ void bPPU::render_line_oam_rto() {
void bPPU::render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) {
if(regs.bg_enabled[OAM] == false && regs.bgsub_enabled[OAM] == false) return;
if(render_enabled(OAM, 0) == false) pri0_pos = 0;
if(render_enabled(OAM, 1) == false) pri1_pos = 0;
if(render_enabled(OAM, 2) == false) pri2_pos = 0;
if(render_enabled(OAM, 3) == false) pri3_pos = 0;
if(pri0_pos == 0 && pri1_pos == 0 && pri2_pos == 0 && pri3_pos == 0) return;
for(unsigned s = 0; s < 34; s++) {
if(oam_tilelist[s].tile == 0xffff) continue;
render_oam_tile(s);

View File

@ -8,6 +8,17 @@
#include "addsub.cpp"
#include "line.cpp"
bool bPPU::render_enabled(unsigned bg, unsigned pri) const {
switch(bg) {
case BG1: return config.ppu.bg1_enabled[pri];
case BG2: return config.ppu.bg2_enabled[pri];
case BG3: return config.ppu.bg3_enabled[pri];
case BG4: return config.ppu.bg4_enabled[pri];
case OAM: return config.ppu.oam_enabled[pri];
}
return true;
}
//Mode 0: ->
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
// BG4B, BG3B, OAM0, BG4A, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3

View File

@ -1,4 +1,6 @@
//render.cpp
bool render_enabled(unsigned bg, unsigned pri) const;
inline void render_line_mode0();
inline void render_line_mode1();
inline void render_line_mode2();

View File

@ -5,18 +5,18 @@ uint16 SMPcore::__relb(int8 offset, int op_len) {
return pc + offset;
}
void SMPcore::disassemble_opcode(char *output) {
void SMPcore::disassemble_opcode(char *output, uint16 addr) {
char *s, t[512];
uint8 op, op0, op1;
uint16 opw, opdp0, opdp1;
s = output;
sprintf(s, "..%.4x ", regs.pc);
sprintf(s, "..%.4x ", addr);
//TODO: read from IPLROM when applicable
op = memory::apuram[(uint16)(regs.pc + 0)];
op0 = memory::apuram[(uint16)(regs.pc + 1)];
op1 = memory::apuram[(uint16)(regs.pc + 2)];
op = memory::apuram[(uint16)(addr + 0)];
op0 = memory::apuram[(uint16)(addr + 1)];
op1 = memory::apuram[(uint16)(addr + 2)];
opw = (op0) | (op1 << 8);
opdp0 = ((unsigned)regs.p.p << 8) + op0;
opdp1 = ((unsigned)regs.p.p << 8) + op1;

View File

@ -1,2 +1,2 @@
void disassemble_opcode(char *output);
void disassemble_opcode(char *output, uint16 addr);
inline uint16 __relb(int8 offset, int op_len);

View File

@ -10,14 +10,35 @@ void sSMPDebug::op_step() {
debugger.breakpoint_test(Debugger::Breakpoint::APURAM, Debugger::Breakpoint::Exec, regs.pc, 0x00);
}
if(debugger.trace_smp) {
char t[256];
disassemble_opcode(t);
debugger.tracefile.print(string() << t << "\n");
}
usage[regs.pc] |= UsageExec;
opcode_pc = regs.pc;
if(step_event) step_event();
sSMP::op_step();
scheduler.sync_smpcpu();
}
uint8 sSMPDebug::op_read(uint16 addr) {
uint8 data = sSMP::op_read(addr);
usage[addr] |= UsageRead;
debugger.breakpoint_test(Debugger::Breakpoint::APURAM, Debugger::Breakpoint::Read, addr, data);
return data;
}
void sSMPDebug::op_write(uint16 addr, uint8 data) {
sSMP::op_write(addr, data);
usage[addr] |= UsageWrite;
usage[addr] &= ~UsageExec;
debugger.breakpoint_test(Debugger::Breakpoint::APURAM, Debugger::Breakpoint::Write, addr, data);
}
sSMPDebug::sSMPDebug() {
usage = new uint8[1 << 16]();
opcode_pc = 0xffc0;
}
sSMPDebug::~sSMPDebug() {
delete[] usage;
}
#endif

View File

@ -1,4 +1,19 @@
class sSMPDebug : public sSMP {
public:
function<void ()> step_event;
enum Usage {
UsageRead = 0x80,
UsageWrite = 0x40,
UsageExec = 0x20,
};
uint8 *usage;
uint16 opcode_pc;
void op_step();
uint8 op_read(uint16 addr);
void op_write(uint16 addr, uint8 data);
sSMPDebug();
~sSMPDebug();
};

View File

@ -1,12 +1,12 @@
uint8 ram_read(uint16 addr);
void ram_write(uint16 addr, uint8 data);
uint8 ram_read(uint16 addr);
void ram_write(uint16 addr, uint8 data);
uint8 port_read(uint8 port);
void port_write(uint8 port, uint8 data);
uint8 port_read(uint8 port);
void port_write(uint8 port, uint8 data);
uint8 op_busread(uint16 addr);
void op_buswrite(uint16 addr, uint8 data);
uint8 op_busread(uint16 addr);
void op_buswrite(uint16 addr, uint8 data);
void op_io();
uint8 op_read(uint16 addr);
void op_write(uint16 addr, uint8 data);
void op_io();
debugvirtual uint8 op_read(uint16 addr);
debugvirtual void op_write(uint16 addr, uint8 data);

View File

@ -18,6 +18,12 @@ Configuration::Configuration() {
smp.ntsc_clock_rate = 24607104; //32040.5 * 768
smp.pal_clock_rate = 24607104;
ppu.bg1_enabled[0] = ppu.bg1_enabled[1] = true;
ppu.bg2_enabled[0] = ppu.bg2_enabled[1] = true;
ppu.bg3_enabled[0] = ppu.bg3_enabled[1] = true;
ppu.bg4_enabled[0] = ppu.bg4_enabled[1] = true;
ppu.oam_enabled[0] = ppu.oam_enabled[1] = ppu.oam_enabled[2] = ppu.oam_enabled[3] = true;
ppu1.version = 1;
ppu2.version = 3;

View File

@ -18,6 +18,14 @@ struct Configuration {
unsigned pal_clock_rate;
} smp;
struct PPU {
bool bg1_enabled[2];
bool bg2_enabled[2];
bool bg3_enabled[2];
bool bg4_enabled[2];
bool oam_enabled[4];
} ppu;
struct PPU1 {
unsigned version;
} ppu1;

View File

@ -22,10 +22,6 @@ public:
bool step_cpu;
bool step_smp;
file tracefile;
bool trace_cpu;
bool trace_smp;
enum MemorySource { CPUBus, APURAM, VRAM, OAM, CGRAM };
uint8 read(MemorySource, unsigned addr);
void write(MemorySource, unsigned addr, uint8 data);

View File

@ -23,6 +23,7 @@ void Scheduler::init() {
clock.smp_freq = system.region() == System::NTSC
? config.smp.ntsc_clock_rate
: config.smp.pal_clock_rate;
clock.cop_freq = clock.cpu_freq;
clock.cpucop = 0;
clock.cpuppu = 0;

View File

@ -15,6 +15,7 @@ public:
struct {
uint32 cpu_freq;
uint32 cop_freq;
uint32 smp_freq;
int64 cpucop;
@ -102,13 +103,13 @@ public:
//==========
alwaysinline void addclocks_cpu(unsigned clocks) {
clock.cpucop -= clocks;
clock.cpucop -= clocks * (uint64)clock.cop_freq;
clock.cpuppu -= clocks;
clock.cpusmp -= clocks * (uint64)clock.smp_freq;
}
alwaysinline void addclocks_cop(unsigned clocks) {
clock.cpucop += clocks;
clock.cpucop += clocks * (uint64)clock.cpu_freq;
}
alwaysinline void addclocks_ppu(unsigned clocks) {

View File

@ -1,12 +1,20 @@
#ifdef SYSTEM_CPP
serializer System::serialize() {
serializer s(serialize_size);
unsigned System::serialize_size() const {
return serializer_size;
}
serializer System::serialize() {
serializer s(serializer_size);
unsigned signature = 0x31545342, version = bsnesSerializerVersion, crc32 = cartridge.crc32();
char description[512];
memset(&description, 0, sizeof description);
unsigned signature = 0x31545342, version = bsnesSaveStateVersion, crc32 = cartridge.crc32();
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(description);
serialize_all(s);
return s;
@ -14,12 +22,15 @@ serializer System::serialize() {
bool System::unserialize(serializer &s) {
unsigned signature, version, crc32;
char description[512];
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(description);
if(signature != 0x31545342) return false;
if(version != bsnesSaveStateVersion) return false;
if(version != bsnesSerializerVersion) return false;
//if(crc32 != cartridge.crc32()) return false;
scheduler.init();
@ -27,7 +38,9 @@ bool System::unserialize(serializer &s) {
return true;
}
//========
//internal
//========
void System::serialize(serializer &s) {
s.integer(snes_region);
@ -61,6 +74,7 @@ void System::serialize_all(serializer &s) {
if(cartridge.has_dsp2()) dsp2.serialize(s);
if(cartridge.has_obc1()) obc1.serialize(s);
if(cartridge.has_st010()) st010.serialize(s);
if(cartridge.has_21fx()) s21fx.serialize(s);
}
//called once upon cartridge load event: perform dry-run state save.
@ -70,12 +84,15 @@ void System::serialize_init() {
serializer s;
unsigned signature = 0, version = 0, crc32 = 0;
char description[512];
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(description);
serialize_all(s);
serialize_size = s.size();
serializer_size = s.size();
}
#endif

View File

@ -1,191 +0,0 @@
StateManager statemanager;
void StateManager::list(lstring &l) const {
l.reset();
for(unsigned x = 0; x < info.slotcount; x++) {
for(unsigned y = 0; y < info.slotcount; y++) {
if(info.slot[y] == x) {
unsigned n = l.size();
char slot[8];
sprintf(slot, "%3u", x + 1);
l[n] << slot << "\t";
char datetime[DateTimeSize + 1];
memcpy(datetime, &info.datetime[y * DateTimeSize], DateTimeSize);
datetime[DateTimeSize] = 0;
l[n] << datetime << "\t";
char desc[DescriptionSize + 1];
memcpy(desc, &info.description[y * DescriptionSize], DescriptionSize);
desc[DescriptionSize] = 0;
l[n] << desc;
}
}
}
}
bool StateManager::set_description(const char *filename, uint8 slot, const char *description) {
if(load(filename) == false) return false;
if(info.slotcount <= slot) return false;
file fp;
if(fp.open(filename, file::mode_readwrite) == false) return false;
uint8 index = findslot(slot);
if(index == SlotInvalid) { fp.close(); return false; }
char desc[DescriptionSize];
memset(&desc, 0, DescriptionSize);
strlcpy(desc, description, DescriptionSize);
fp.seek(DescIndex + index * DescriptionSize);
fp.write((uint8*)&desc[0], DescriptionSize);
fp.close();
return true;
}
serializer StateManager::load(const char *filename, uint8 slot) {
if(load(filename) == false) throw;
if(info.slotcount <= slot) throw;
uint8 index = findslot(slot);
if(index == SlotInvalid) throw;
file fp;
if(fp.open(filename, file::mode_read) == false) throw;
fp.seek(HeaderSize + system.serialize_size * index);
uint8 *data = new uint8[system.serialize_size];
fp.read(data, system.serialize_size);
serializer s(data, system.serialize_size);
delete[] data;
fp.close();
return s;
}
bool StateManager::save(const char *filename, uint8 slot, serializer &s, const char *description) {
//if no state archive exists ...
if(file::exists(filename) == false) {
//try and create one
if(create(filename) == false) return false;
}
//if we cannot load the existing state archive ...
if(load(filename) == false) {
//it's probably an older version, try and create a new one
if(create(filename) == false) return false;
//it still needs to be loaded before we can write to it
if(load(filename) == false) return false;
}
uint8 index = findslot(slot);
if(index == SlotInvalid) {
//create new slot instead of over-writing existing slot
if(info.slotcount >= 255) return false;
index = info.slotcount;
slot = info.slotcount;
}
file fp;
if(fp.open(filename, file::mode_readwrite) == false) return false;
fp.seek(SlotIndex + index);
fp.write(slot);
time_t current = time(0);
tm *ts = localtime(&current);
char timestamp[32];
sprintf(timestamp, "%.4u-%.2u-%.2u %.2u:%.2u:%.2u",
1900 + ts->tm_year, ts->tm_mon + 1, ts->tm_mday,
ts->tm_hour, ts->tm_min, ts->tm_sec,
(ts->tm_hour < 12 ? "AM" : "PM")
);
fp.seek(DateTimeIndex + index * DateTimeSize);
fp.write((uint8*)&timestamp[0], DateTimeSize);
char desc[DescriptionSize];
memset(&desc, 0, DescriptionSize);
strlcpy(desc, description, DescriptionSize);
fp.seek(DescIndex + index * DescriptionSize);
fp.write((uint8*)&desc[0], DescriptionSize);
fp.seek(HeaderSize + index * system.serialize_size);
fp.write(s.data(), s.size());
for(unsigned n = 0; n < system.serialize_size - s.size(); n++) fp.write(0x00);
fp.close();
return true;
}
bool StateManager::erase(const char *filename, uint8 slot) {
if(load(filename) == false) return false;
uint8 index = findslot(slot);
if(index == SlotInvalid) return false;
file fp;
if(fp.open(filename, file::mode_readwrite) == false) return false;
if(info.slotcount <= slot) return false;
//copy the very last state to the slot that is to be erased
uint8 lastslot = info.slotcount - 1;
info.slot[index] = info.slot[lastslot];
info.slot[lastslot] = SlotInvalid;
fp.seek(DateTimeIndex + index * DateTimeSize);
fp.write((uint8*)&info.datetime[lastslot * DateTimeSize], DateTimeSize);
fp.seek(DescIndex + index * DescriptionSize);
fp.write((uint8*)&info.description[lastslot * DescriptionSize], DescriptionSize);
fp.seek(HeaderSize + system.serialize_size * lastslot);
uint8 *data = new uint8[system.serialize_size];
fp.read(data, system.serialize_size);
fp.seek(HeaderSize + system.serialize_size * index);
fp.write(data, system.serialize_size);
delete[] data;
//decrement all IDs after the deleted one (removes empty slot ID from deletion)
for(unsigned n = 0; n < lastslot; n++) {
if(info.slot[n] > slot) info.slot[n]--;
}
fp.seek(SlotIndex);
fp.write(info.slot, 256);
unsigned size = fp.size();
fp.truncate(size - system.serialize_size);
return true;
}
bool StateManager::load(const char *filename) {
file fp;
if(fp.open(filename, file::mode_read) == false) return false;
unsigned filesize = fp.size();
if(filesize < HeaderSize) return false;
fp.seek(0);
if(fp.readl(4) != 0x31415342) return false;
if(fp.readl(4) != bsnesSaveStateVersion) return false;
fp.read((uint8*)&info.slot[0], 256);
fp.read((uint8*)&info.datetime[0], 256 * DateTimeSize);
fp.read((uint8*)&info.description[0], 256 * DescriptionSize);
info.slotcount = (filesize - HeaderSize) / system.serialize_size;
return true;
}
bool StateManager::create(const char *filename) const {
file fp;
if(fp.open(filename, file::mode_write) == false) return false;
fp.writel(0x31415342, 4); //signature ('BSA1')
fp.writel(bsnesSaveStateVersion, 4); //version
for(unsigned i = 0; i < 256 * SlotSize; i++) fp.write(SlotInvalid); //slot index
for(unsigned i = 0; i < 256 * DateTimeSize; i++) fp.write(0x20); //date / time
for(unsigned i = 0; i < 256 * DescriptionSize; i++) fp.write(0x00); //description
fp.close();
return true;
}
uint8 StateManager::findslot(uint8 slot) const {
if(slot == SlotInvalid) return SlotInvalid;
for(unsigned n = 0; n < info.slotcount; n++) {
if(info.slot[n] == slot) return n;
}
return SlotInvalid;
}

View File

@ -1,35 +0,0 @@
class StateManager : noncopyable {
public:
enum {
SlotInvalid = 0xff,
SlotSize = 1,
DateTimeSize = 19,
DescriptionSize = 512,
HeaderSize = 8 + (256 * SlotSize) + (256 * DateTimeSize) + (256 * DescriptionSize),
SlotIndex = 8,
DateTimeIndex = SlotIndex + (256 * SlotSize),
DescIndex = DateTimeIndex + (256 * DateTimeSize),
};
void list(lstring&) const;
bool load(const char *filename);
bool set_description(const char *filename, uint8 slot, const char *description);
serializer load(const char *filename, uint8 slot);
bool save(const char *filename, uint8 slot, serializer&, const char *description);
bool erase(const char *filename, uint8 slot);
private:
struct Info {
unsigned slotcount;
uint8 slot[256 * SlotSize];
char datetime[256 * DateTimeSize];
char description[256 * DescriptionSize];
} info;
bool create(const char *filename) const;
uint8 findslot(uint8 slot) const;
};
extern StateManager statemanager;

View File

@ -9,7 +9,6 @@ System system;
#include "debugger/debugger.cpp"
#include "serialization.cpp"
#include "scheduler/scheduler.cpp"
#include "statemanager/statemanager.cpp"
#include "video/video.cpp"
#include "audio/audio.cpp"
@ -19,6 +18,7 @@ void System::coprocessor_enter() {
if(cartridge.mode() == Cartridge::ModeSuperGameBoy) supergameboy.enter();
if(cartridge.has_superfx()) superfx.enter();
if(cartridge.has_sa1()) sa1.enter();
if(cartridge.has_21fx()) s21fx.enter();
while(true) {
scheduler.addclocks_cop(64 * 1024 * 1024);
@ -78,6 +78,7 @@ void System::init(Interface *interface_) {
st010.init();
st011.init();
st018.init();
s21fx.init();
video.init();
audio.init();
@ -126,6 +127,7 @@ void System::power() {
if(cartridge.has_st010()) st010.enable();
if(cartridge.has_st011()) st011.enable();
if(cartridge.has_st018()) st018.enable();
if(cartridge.has_21fx()) s21fx.enable();
if(expansion() == ExpansionBSX) bsxbase.power();
if(memory::bsxflash.data()) bsxflash.power();
@ -146,8 +148,8 @@ void System::power() {
if(cartridge.has_st010()) st010.power();
if(cartridge.has_st011()) st011.power();
if(cartridge.has_st018()) st018.power();
if(cartridge.has_21fx()) s21fx.power();
//ppu.PPUcounter::reset();
cpu.power();
smp.power();
dsp.power();
@ -162,7 +164,6 @@ void System::power() {
void System::reset() {
scheduler.init();
//ppu.PPUcounter::reset();
cpu.reset();
smp.reset();
dsp.reset();
@ -188,6 +189,7 @@ void System::reset() {
if(cartridge.has_st010()) st010.reset();
if(cartridge.has_st011()) st011.reset();
if(cartridge.has_st018()) st018.reset();
if(cartridge.has_21fx()) s21fx.reset();
input.port_set_device(0, config.controller_port1);
input.port_set_device(1, config.controller_port2);

View File

@ -2,7 +2,6 @@
#include "debugger/debugger.hpp"
#include "interface/interface.hpp"
#include "scheduler/scheduler.hpp"
#include "statemanager/statemanager.hpp"
#include "video/video.hpp"
#include "audio/audio.hpp"
@ -17,23 +16,24 @@ public:
enum ExpansionPortDevice { ExpansionNone = 0, ExpansionBSX = 1 };
//system functions
virtual void run();
virtual void runtosave();
void run();
void runtosave();
virtual void init(Interface*);
virtual void term();
virtual void power();
virtual void reset();
virtual void unload();
void init(Interface*);
void term();
void power();
void reset();
void unload();
virtual void frame();
virtual void scanline();
void frame();
void scanline();
//return *active* region / expansion port device information
//settings cached upon power-on
Region region() const;
ExpansionPortDevice expansion() const;
unsigned serialize_size() const;
serializer serialize();
bool unserialize(serializer&);
@ -41,7 +41,7 @@ public:
virtual ~System() {}
private:
unsigned serialize_size;
unsigned serializer_size;
void serialize(serializer&);
void serialize_all(serializer&);
void serialize_init();

View File

@ -1,23 +1,38 @@
objects := ui-main $(objects)
objects := ui-main ui-base ui-debugger ui-input ui-movie ui-settings ui-state ui-tools $(objects)
objects += $(if $(call streq,$(platform),win),resource)
link += $(qtlib)
headers := $(call rwildcard,$(ui)/,%.hpp)
moc_headers := $(call rwildcard,$(ui)/,%.moc.hpp)
moc_objects := $(patsubst %.moc.hpp,%.moc,$(moc_headers))
moc_objects := $(foreach f,$(moc_headers),obj/$(notdir $(patsubst %.moc.hpp,%.moc,$f)))
qt_compile = $(call compile,-Iobj $(qtinc))
#############
### rules ###
#############
# automatically run moc on all .moc.hpp (MOC header) files
%.moc: $<; $(moc) -f $< -o $@
$(foreach f,$(moc_objects),$(eval $f: $(patsubst %.moc,%.moc.hpp,$f)))
%.moc: $<; $(moc) -i $< -o $@
obj/ui-main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/)
$(call compile,$(qtinc))
# automatically generate %.moc build rules
__list = $(moc_headers)
$(foreach f,$(moc_objects), \
$(eval __file = $(word 1,$(__list))) \
$(eval __list = $(wordlist 2,$(words $(__list)),$(__list))) \
$(eval $f: $(__file)) \
)
$(ui)/resource/resource.rcc: $(ui)/resource/resource.qrc data/*
$(rcc) $(ui)/resource/resource.qrc -o $(ui)/resource/resource.rcc
obj/ui-main.o: $(ui)/main.cpp $(headers) $(wildcard $(ui)/*.cpp) $(wildcard $(ui)/platform/*.cpp) $(wildcard $(ui)/utility/*.cpp); $(qt_compile)
obj/ui-base.o: $(ui)/base/base.cpp $(headers) $(wildcard $(ui)/base/*.cpp); $(qt_compile)
obj/ui-debugger.o: $(ui)/debugger/debugger.cpp $(headers) $(wildcard $(ui)/debugger/*.cpp); $(qt_compile)
obj/ui-input.o: $(ui)/input/input.cpp $(headers) $(wildcard $(ui)/input/*.cpp); $(qt_compile)
obj/ui-movie.o: $(ui)/movie/movie.cpp $(headers) $(wildcard $(ui)/movie/*.cpp); $(qt_compile)
obj/ui-settings.o: $(ui)/settings/settings.cpp $(headers) $(wildcard $(ui)/settings/*.cpp); $(qt_compile)
obj/ui-state.o: $(ui)/state/state.cpp $(headers) $(wildcard $(ui)/state/*.cpp); $(qt_compile)
obj/ui-tools.o: $(ui)/tools/tools.cpp $(headers) $(wildcard $(ui)/tools/*.cpp); $(qt_compile)
obj/resource.rcc: $(ui)/resource/resource.qrc data/*
$(rcc) $(ui)/resource/resource.qrc -o obj/resource.rcc
obj/resource.o: $(ui)/resource/resource.rc
windres $(ui)/resource/resource.rc obj/resource.o
@ -26,7 +41,7 @@ obj/resource.o: $(ui)/resource/resource.rc
### targets ###
###############
ui_build: $(ui)/resource/resource.rcc $(moc_objects);
ui_build: obj/resource.rcc $(moc_objects);
ui_clean:
-$(foreach f,$(moc_objects),@$(call delete,$f))
-@$(call delete,$(ui)/resource/resource.rcc)
-@$(call delete,obj/*.rcc)
-@$(call delete,obj/*.moc)

View File

@ -1,3 +1,6 @@
#include "application.moc"
Application application;
#include "init.cpp"
#include "qb.cpp"
@ -117,6 +120,7 @@ void Application::run() {
if(SNES::cartridge.loaded() && !pause && !autopause && (!debug || debugrun)) {
SNES::system.run();
#if defined(DEBUGGER)
if(SNES::debugger.break_event != SNES::Debugger::None) {
debug = true;
debugrun = false;
@ -124,6 +128,7 @@ void Application::run() {
debugger->event();
SNES::debugger.break_event = SNES::Debugger::None;
}
#endif
} else {
usleep(20 * 1000);
}

View File

@ -41,4 +41,6 @@ public:
public slots:
void run();
} application;
};
extern Application application;

View File

@ -1,21 +1,3 @@
#include "../base/main.moc"
#include "../base/diskbrowser.moc"
#include "../base/loader.moc"
#include "../base/htmlviewer.moc"
#include "../base/about.moc"
#include "../settings/settings.moc"
#include "../tools/tools.moc"
#include "../base/main.cpp"
#include "../base/diskbrowser.cpp"
#include "../base/loader.cpp"
#include "../base/htmlviewer.cpp"
#include "../base/about.cpp"
#include "../settings/settings.cpp"
#include "../tools/tools.cpp"
void Application::init() {
if(config().system.crashedOnLastRun == true) {
//emulator crashed on last run, disable all drivers
@ -53,7 +35,9 @@ void Application::init() {
utility.updateFullscreenState();
QApplication::processEvents();
#if defined(DEBUGGER)
debugger = new Debugger;
#endif
settingsWindow = new SettingsWindow;
toolsWindow = new ToolsWindow;

View File

@ -1,3 +1,6 @@
#include "about.moc"
AboutWindow *aboutWindow;
AboutWindow::AboutWindow() : QbWindow(config().geometry.aboutWindow) {
setObjectName("about-window");
setWindowTitle("About bsnes ...");

View File

@ -9,4 +9,6 @@ public:
QLabel *info;
AboutWindow();
} *aboutWindow;
};
extern AboutWindow *aboutWindow;

7
src/ui_qt/base/base.cpp Normal file
View File

@ -0,0 +1,7 @@
#include "../ui-base.hpp"
#include "about.cpp"
#include "diskbrowser.cpp"
#include "htmlviewer.cpp"
#include "loader.cpp"
#include "main.cpp"

View File

@ -1,3 +1,7 @@
#include "diskbrowser.moc"
FolderCreator *folderCreator;
DiskBrowser *diskBrowser;
//=============
//FolderCreator
//=============

View File

@ -14,7 +14,9 @@ public:
public slots:
void show();
void createFolder();
} *folderCreator;
};
extern FolderCreator *folderCreator;
class DiskBrowserView : public QTreeView {
Q_OBJECT
@ -109,4 +111,6 @@ private:
} browseMode;
bool currentFilename(string&);
} *diskBrowser;
};
extern DiskBrowser *diskBrowser;

View File

@ -1,3 +1,6 @@
#include "htmlviewer.moc"
HtmlViewerWindow *htmlViewerWindow;
HtmlViewerWindow::HtmlViewerWindow() : QbWindow(config().geometry.htmlViewerWindow) {
setObjectName("html-window");
resize(560, 480);

View File

@ -9,4 +9,6 @@ public:
HtmlViewerWindow();
public slots:
} *htmlViewerWindow;
};
extern HtmlViewerWindow *htmlViewerWindow;

View File

@ -1,3 +1,6 @@
#include "loader.moc"
LoaderWindow *loaderWindow;
LoaderWindow::LoaderWindow() : QbWindow(config().geometry.loaderWindow) {
setObjectName("loader-window");
setMinimumWidth(520);

View File

@ -42,4 +42,6 @@ public slots:
private:
SNES::Cartridge::Mode mode;
void showWindow(const char *title);
} *loaderWindow;
};
extern LoaderWindow *loaderWindow;

View File

@ -1,3 +1,6 @@
#include "main.moc"
MainWindow *mainWindow;
MainWindow::MainWindow() : QbWindow(config().geometry.mainWindow) {
setObjectName("main-window");
setWindowTitle(string() << bsnesTitle << " v" << bsnesVersion);
@ -84,8 +87,6 @@ MainWindow::MainWindow() : QbWindow(config().geometry.mainWindow) {
settings_videoMode->addAction(settings_videoMode_correctAspectRatio = new QbCheckAction("Correct &Aspect Ratio", 0));
settings_videoMode->addAction(settings_videoMode_fullscreen = new QbCheckAction("&Fullscreen", 0));
settings_videoMode->addSeparator();
settings_videoMode->addAction(settings_videoMode_ntsc = new QbRadioAction("&NTSC", 0));
@ -122,15 +123,15 @@ MainWindow::MainWindow() : QbWindow(config().geometry.mainWindow) {
settings_emulationSpeed = settings->addMenu("Emulation &Speed");
settings_emulationSpeed->setIcon(QIcon(":/16x16/appointment-new.png"));
settings_emulationSpeed->addAction(settings_emulationSpeed_slowest = new QbRadioAction("50%", 0));
settings_emulationSpeed->addAction(settings_emulationSpeed_slowest = new QbRadioAction("Slowest", 0));
settings_emulationSpeed->addAction(settings_emulationSpeed_slow = new QbRadioAction("75%", 0));
settings_emulationSpeed->addAction(settings_emulationSpeed_slow = new QbRadioAction("Slow", 0));
settings_emulationSpeed->addAction(settings_emulationSpeed_normal = new QbRadioAction("100%", 0));
settings_emulationSpeed->addAction(settings_emulationSpeed_normal = new QbRadioAction("Normal", 0));
settings_emulationSpeed->addAction(settings_emulationSpeed_fast = new QbRadioAction("150%", 0));
settings_emulationSpeed->addAction(settings_emulationSpeed_fast = new QbRadioAction("Fast", 0));
settings_emulationSpeed->addAction(settings_emulationSpeed_fastest = new QbRadioAction("200%", 0));
settings_emulationSpeed->addAction(settings_emulationSpeed_fastest = new QbRadioAction("Fastest", 0));
settings_emulationSpeed->addSeparator();
@ -144,26 +145,35 @@ MainWindow::MainWindow() : QbWindow(config().geometry.mainWindow) {
tools = menuBar->addMenu("&Tools");
tools_cheatEditor = tools->addAction("Cheat &Editor ...");
tools_cheatEditor->setIcon(QIcon(":/16x16/accessories-text-editor.png"));
tools_movies = tools->addMenu("&Movies");
tools_movies->setIcon(QIcon(":/16x16/applications-multimedia.png"));
tools_cheatFinder = tools->addAction("Cheat &Finder ...");
tools_cheatFinder->setIcon(QIcon(":/16x16/system-search.png"));
tools_movies_play = tools_movies->addAction("Play Movie ...");
tools_movies_play->setIcon(QIcon(":/16x16/media-playback-start.png"));
tools_stateManager = tools->addAction("&State Manager ...");
tools_stateManager->setIcon(QIcon(":/16x16/system-file-manager.png"));
tools_movies_stop = tools_movies->addAction("Stop");
tools_movies_stop->setIcon(QIcon(":/16x16/media-playback-stop.png"));
tools->addSeparator();
tools_movies_recordFromPowerOn = tools_movies->addAction("Record Movie (and restart system)");
tools_movies_recordFromPowerOn->setIcon(QIcon(":/16x16/media-record.png"));
tools_movies_recordFromHere = tools_movies->addAction("Record Movie (starting from here)");
tools_movies_recordFromHere->setIcon(QIcon(":/16x16/media-record.png"));
tools_captureScreenshot = tools->addAction("&Capture Screenshot");
tools_captureScreenshot->setIcon(QIcon(":/16x16/image-x-generic.png"));
tools->addSeparator();
tools_debugger = tools->addAction("&Debugger ...");
tools_debugger->setIcon(QIcon(":/16x16/utilities-terminal.png"));
#if !defined(DEBUGGER)
tools_debugger->setVisible(false);
#endif
tools_dialog = tools->addAction("&Tools Dialog ...");
tools_dialog->setIcon(QIcon(":/16x16/preferences-desktop.png"));
help = menuBar->addMenu("&Help");
help_documentation = help->addAction("&Documentation ...");
@ -249,7 +259,6 @@ MainWindow::MainWindow() : QbWindow(config().geometry.mainWindow) {
connect(settings_videoMode_4x, SIGNAL(triggered()), this, SLOT(setVideoMode4x()));
connect(settings_videoMode_5x, SIGNAL(triggered()), this, SLOT(setVideoMode5x()));
connect(settings_videoMode_correctAspectRatio, SIGNAL(triggered()), this, SLOT(toggleAspectCorrection()));
connect(settings_videoMode_fullscreen, SIGNAL(triggered()), this, SLOT(toggleFullscreen()));
connect(settings_videoMode_ntsc, SIGNAL(triggered()), this, SLOT(setVideoNtsc()));
connect(settings_videoMode_pal, SIGNAL(triggered()), this, SLOT(setVideoPal()));
if(filter.opened()) {
@ -268,11 +277,13 @@ MainWindow::MainWindow() : QbWindow(config().geometry.mainWindow) {
connect(settings_emulationSpeed_syncVideo, SIGNAL(triggered()), this, SLOT(syncVideo()));
connect(settings_emulationSpeed_syncAudio, SIGNAL(triggered()), this, SLOT(syncAudio()));
connect(settings_configuration, SIGNAL(triggered()), this, SLOT(showConfigWindow()));
connect(tools_cheatEditor, SIGNAL(triggered()), this, SLOT(showCheatEditor()));
connect(tools_cheatFinder, SIGNAL(triggered()), this, SLOT(showCheatFinder()));
connect(tools_stateManager, SIGNAL(triggered()), this, SLOT(showStateManager()));
connect(tools_movies_play, SIGNAL(triggered()), this, SLOT(playMovie()));
connect(tools_movies_stop, SIGNAL(triggered()), this, SLOT(stopMovie()));
connect(tools_movies_recordFromPowerOn, SIGNAL(triggered()), this, SLOT(recordMovieFromPowerOn()));
connect(tools_movies_recordFromHere, SIGNAL(triggered()), this, SLOT(recordMovieFromHere()));
connect(tools_captureScreenshot, SIGNAL(triggered()), this, SLOT(saveScreenshot()));
connect(tools_debugger, SIGNAL(triggered()), this, SLOT(showDebugger()));
connect(tools_dialog, SIGNAL(triggered()), this, SLOT(showToolsDialog()));
connect(help_documentation, SIGNAL(triggered()), this, SLOT(showDocumentation()));
connect(help_license, SIGNAL(triggered()), this, SLOT(showLicense()));
connect(help_about, SIGNAL(triggered()), this, SLOT(showAbout()));
@ -308,7 +319,6 @@ void MainWindow::syncUi() {
settings_videoMode_5x->setChecked(config().video.context->multiplier == 5);
settings_videoMode_correctAspectRatio->setChecked(config().video.context->correctAspectRatio);
settings_videoMode_fullscreen->setChecked(config().video.isFullscreen);
settings_videoMode_ntsc->setChecked(config().video.context->region == 0);
settings_videoMode_pal->setChecked (config().video.context->region == 1);
@ -332,6 +342,15 @@ void MainWindow::syncUi() {
settings_emulationSpeed_syncVideo->setChecked(config().video.synchronize);
settings_emulationSpeed_syncAudio->setChecked(config().audio.synchronize);
//movies contian save states to synchronize playback to recorded input
tools_movies->setEnabled(SNES::cartridge.loaded() && utility.saveStatesSupported());
if(tools_movies->isEnabled()) {
tools_movies_play->setEnabled(movie.state == Movie::Inactive);
tools_movies_stop->setEnabled(movie.state != Movie::Inactive);
tools_movies_recordFromPowerOn->setEnabled(movie.state == Movie::Inactive);
tools_movies_recordFromHere->setEnabled(movie.state == Movie::Inactive);
}
}
bool MainWindow::isActive() {
@ -461,7 +480,6 @@ void MainWindow::setVideoMode4x() { utility.setScale(4); }
void MainWindow::setVideoMode5x() { utility.setScale(5); }
void MainWindow::toggleAspectCorrection() { utility.toggleAspectCorrection(); }
void MainWindow::toggleFullscreen() { utility.toggleFullscreen(); }
void MainWindow::setVideoNtsc() { utility.setNtscMode(); }
void MainWindow::setVideoPal() { utility.setPalMode(); }
@ -504,16 +522,39 @@ void MainWindow::syncAudio() { utility.toggleSynchronizeAudio(); }
void MainWindow::showConfigWindow() { settingsWindow->show(); }
void MainWindow::showCheatEditor() { toolsWindow->showCheatEditor(); }
void MainWindow::showCheatFinder() { toolsWindow->showCheatFinder(); }
void MainWindow::showStateManager() { toolsWindow->showStateManager(); }
void MainWindow::playMovie() {
movie.chooseFile();
syncUi();
}
void MainWindow::stopMovie() {
movie.stop();
syncUi();
}
void MainWindow::recordMovieFromPowerOn() {
utility.modifySystemState(Utility::PowerCycle);
movie.record();
syncUi();
}
void MainWindow::recordMovieFromHere() {
movie.record();
syncUi();
}
void MainWindow::saveScreenshot() {
//tell SNES::Interface to save a screenshot at the next video_refresh() event
interface.saveScreenshot = true;
}
void MainWindow::showDebugger() { debugger->show(); }
void MainWindow::showDebugger() {
#if defined(DEBUGGER)
debugger->show();
#endif
}
void MainWindow::showToolsDialog() { toolsWindow->show(); }
void MainWindow::showDocumentation() {
QFile file(":/documentation.html");

View File

@ -53,7 +53,6 @@ public:
QbRadioAction *settings_videoMode_4x;
QbRadioAction *settings_videoMode_5x;
QbCheckAction *settings_videoMode_correctAspectRatio;
QbCheckAction *settings_videoMode_fullscreen;
QbRadioAction *settings_videoMode_ntsc;
QbRadioAction *settings_videoMode_pal;
QMenu *settings_videoFilter;
@ -72,11 +71,14 @@ public:
QbCheckAction *settings_emulationSpeed_syncAudio;
QAction *settings_configuration;
QMenu *tools;
QAction *tools_cheatEditor;
QAction *tools_cheatFinder;
QAction *tools_stateManager;
QMenu *tools_movies;
QAction *tools_movies_play;
QAction *tools_movies_stop;
QAction *tools_movies_recordFromPowerOn;
QAction *tools_movies_recordFromHere;
QAction *tools_captureScreenshot;
QAction *tools_debugger;
QAction *tools_dialog;
QMenu *help;
QAction *help_documentation;
QAction *help_license;
@ -120,7 +122,6 @@ public slots:
void setVideoMode4x();
void setVideoMode5x();
void toggleAspectCorrection();
void toggleFullscreen();
void setVideoNtsc();
void setVideoPal();
void configureFilter();
@ -135,12 +136,16 @@ public slots:
void syncVideo();
void syncAudio();
void showConfigWindow();
void showCheatEditor();
void showCheatFinder();
void showStateManager();
void playMovie();
void stopMovie();
void recordMovieFromPowerOn();
void recordMovieFromHere();
void saveScreenshot();
void showDebugger();
void showToolsDialog();
void showDocumentation();
void showLicense();
void showAbout();
} *mainWindow;
};
extern MainWindow *mainWindow;

View File

@ -44,8 +44,13 @@ Configuration::Configuration() {
attach(system.input = "", "system.input");
attach(system.crashedOnLastRun = false, "system.crashedOnLastRun");
attach(system.speed = 2, "system.speed");
attach(system.speedSlowest = 50, "system.speedSlowest");
attach(system.speedSlow = 75, "system.speedSlow");
attach(system.speedNormal = 100, "system.speedNormal");
attach(system.speedFast = 150, "system.speedFast");
attach(system.speedFastest = 200, "system.speedFastest");
attach(system.autoSaveMemory = false, "system.autoSaveMemory", "Automatically save cartridge back-up RAM once every minute");
attach(system.autoHideMenus = false, "system.autoHideMenus", "Automatically hide the menu and status bars when entering fullscreen mode");
attach(system.rewindEnabled = false, "system.rewindEnabled", "Automatically save states periodically to allow auto-rewind support");
attach(diskBrowser.showPanel = true, "diskBrowser.showPanel");
@ -65,6 +70,7 @@ Configuration::Configuration() {
attach(path.vertexShader = "", "path.vertexShader");
attach(path.current.folder = "", "path.current.folder");
attach(path.current.movie = "", "path.current.movie");
attach(path.current.shader = "", "path.current.shader");
attach(path.current.cartridge = "", "path.current.cartridge");
attach(path.current.bsx = "", "path.current.bsx");
@ -121,6 +127,7 @@ Configuration::Configuration() {
attach(geometry.settingsWindow = "", "geometry.settingsWindow");
attach(geometry.toolsWindow = "", "geometry.toolsWindow");
attach(geometry.debugger = "", "geometry.debugger");
attach(geometry.disassembler = "", "geometry.disassembler");
attach(geometry.breakpointEditor = "", "geometry.breakpointEditor");
attach(geometry.memoryEditor = "", "geometry.memoryEditor");
attach(geometry.vramViewer = "", "geometry.vramViewer");

View File

@ -4,8 +4,13 @@ public:
string video, audio, input;
bool crashedOnLastRun;
unsigned speed;
unsigned speedSlowest;
unsigned speedSlow;
unsigned speedNormal;
unsigned speedFast;
unsigned speedFastest;
bool autoSaveMemory;
bool autoHideMenus;
bool rewindEnabled;
} system;
struct File {
@ -26,7 +31,7 @@ public:
string fragmentShader, vertexShader;
struct Current {
string folder, shader, cartridge, bsx, st, sgb;
string folder, movie, shader, cartridge, bsx, st, sgb;
unsigned filter; //current active filter for "Load Cartridge"
} current;
} path;
@ -69,6 +74,7 @@ public:
string settingsWindow;
string toolsWindow;
string debugger;
string disassembler;
string breakpointEditor;
string memoryEditor;
string vramViewer;

View File

@ -1,3 +1,6 @@
#include "breakpoint.moc"
BreakpointEditor *breakpointEditor;
BreakpointItem::BreakpointItem(unsigned id_) : id(id_) {
layout = new QHBoxLayout;
layout->setMargin(0);

View File

@ -25,4 +25,6 @@ public:
BreakpointItem *breakpoint[SNES::Debugger::Breakpoints];
BreakpointEditor();
} *breakpointEditor;
};
extern BreakpointEditor *breakpointEditor;

View File

@ -1,7 +1,16 @@
#include "../ui-base.hpp"
#if defined(DEBUGGER)
#include "debugger.moc"
Debugger *debugger;
#include "hexeditor.cpp"
#include "disassembler.cpp"
#include "breakpoint.cpp"
#include "memory.cpp"
#include "vramviewer.cpp"
#include "tracer.cpp"
Debugger::Debugger() : QbWindow(config().geometry.debugger) {
setObjectName("debugger");
@ -16,16 +25,13 @@ Debugger::Debugger() : QbWindow(config().geometry.debugger) {
layout->setMenuBar(menu);
tools = menu->addMenu("Tools");
tools_disassembler = tools->addAction("Disassembler ...");
tools_breakpoint = tools->addAction("Breakpoint Editor ...");
tools_breakpoint->setIcon(QIcon(":/16x16/process-stop.png"));
tools_memory = tools->addAction("Memory Editor ...");
tools_memory->setIcon(QIcon(":/16x16/text-x-generic.png"));
tools_vramViewer = tools->addAction("Video RAM Viewer ...");
tools_vramViewer->setIcon(QIcon(":/16x16/image-x-generic.png"));
miscOptions = menu->addMenu("Misc");
miscOptions_clear = miscOptions->addAction("Clear Console");
miscOptions_clear->setIcon(QIcon(":/16x16/document-new.png"));
console = new QTextEdit;
console->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
@ -52,22 +58,32 @@ Debugger::Debugger() : QbWindow(config().geometry.debugger) {
controlLayout->addSpacing(Style::WidgetSpacing);
stepCPU = new QCheckBox("Step CPU");
stepCPU = new QCheckBox("Step S-CPU");
controlLayout->addWidget(stepCPU);
stepSMP = new QCheckBox("Step SMP");
stepSMP = new QCheckBox("Step S-SMP");
controlLayout->addWidget(stepSMP);
traceCPU = new QCheckBox("Trace CPU opcodes");
traceCPU = new QCheckBox("Trace S-CPU opcodes");
controlLayout->addWidget(traceCPU);
traceSMP = new QCheckBox("Trace SMP opcodes");
traceSMP = new QCheckBox("Trace S-SMP opcodes");
controlLayout->addWidget(traceSMP);
traceMask = new QCheckBox("Enable trace mask");
controlLayout->addWidget(traceMask);
spacer = new QWidget;
spacer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
controlLayout->addWidget(spacer);
disassembler = new Disassembler;
breakpointEditor = new BreakpointEditor;
memoryEditor = new MemoryEditor;
vramViewer = new VramViewer;
tracer = new Tracer;
connect(tools_disassembler, SIGNAL(triggered()), this, SLOT(showDisassembler()));
connect(tools_breakpoint, SIGNAL(triggered()), this, SLOT(showBreakpointEditor()));
connect(tools_memory, SIGNAL(triggered()), this, SLOT(showMemoryEditor()));
connect(tools_vramViewer, SIGNAL(triggered()), this, SLOT(showVramViewer()));
@ -77,18 +93,39 @@ Debugger::Debugger() : QbWindow(config().geometry.debugger) {
connect(stepInstruction, SIGNAL(released()), this, SLOT(stepAction()));
connect(stepCPU, SIGNAL(released()), this, SLOT(synchronize()));
connect(stepSMP, SIGNAL(released()), this, SLOT(synchronize()));
connect(traceCPU, SIGNAL(released()), this, SLOT(toggleTraceCPU()));
connect(traceSMP, SIGNAL(released()), this, SLOT(toggleTraceSMP()));
breakpointEditor = new BreakpointEditor;
memoryEditor = new MemoryEditor;
vramViewer = new VramViewer;
connect(traceCPU, SIGNAL(stateChanged(int)), tracer, SLOT(setCpuTraceState(int)));
connect(traceSMP, SIGNAL(stateChanged(int)), tracer, SLOT(setSmpTraceState(int)));
connect(traceMask, SIGNAL(stateChanged(int)), tracer, SLOT(setTraceMaskState(int)));
frameCounter = 0;
synchronize();
resize(855, 425);
}
void Debugger::modifySystemState(unsigned state) {
string usagefile = string() << dir(utility.cartridge.baseName) << "usage.bin";
file fp;
if(state == Utility::LoadCartridge) {
if(fp.open(usagefile, file::mode_read)) {
fp.read(SNES::cpu.usage, 1 << 24);
fp.read(SNES::smp.usage, 1 << 16);
fp.close();
} else {
memset(SNES::cpu.usage, 0x00, 1 << 24);
memset(SNES::smp.usage, 0x00, 1 << 16);
}
}
if(state == Utility::UnloadCartridge) {
if(fp.open(usagefile, file::mode_write)) {
fp.write(SNES::cpu.usage, 1 << 24);
fp.write(SNES::smp.usage, 1 << 16);
fp.close();
}
}
}
void Debugger::synchronize() {
runBreak->setText(application.debug ? "Run" : "Break");
stepInstruction->setEnabled(SNES::cartridge.loaded() && application.debug && (stepCPU->isChecked() || stepSMP->isChecked()));
@ -107,6 +144,10 @@ void Debugger::clear() {
console->setHtml("");
}
void Debugger::showDisassembler() {
disassembler->show();
}
void Debugger::showBreakpointEditor() {
breakpointEditor->show();
}
@ -130,28 +171,6 @@ void Debugger::stepAction() {
application.debugrun = true;
}
void Debugger::tracerUpdate() {
if(SNES::debugger.trace_cpu || SNES::debugger.trace_smp) {
if(SNES::debugger.tracefile.open() == false) {
SNES::debugger.tracefile.open(string() << config().path.data << "trace.log", file::mode_write);
}
} else if(!SNES::debugger.trace_cpu && !SNES::debugger.trace_smp) {
if(SNES::debugger.tracefile.open() == true) {
SNES::debugger.tracefile.close();
}
}
}
void Debugger::toggleTraceCPU() {
SNES::debugger.trace_cpu = traceCPU->isChecked();
tracerUpdate();
}
void Debugger::toggleTraceSMP() {
SNES::debugger.trace_smp = traceSMP->isChecked();
tracerUpdate();
}
void Debugger::event() {
char t[256];
@ -160,33 +179,39 @@ void Debugger::event() {
unsigned n = SNES::debugger.breakpoint_hit;
echo(string() << "Breakpoint " << n << " hit (" << SNES::debugger.breakpoint[n].counter << ").<br>");
if(SNES::debugger.breakpoint[n].mode == SNES::Debugger::Breakpoint::Exec) {
if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::CPUBus) {
SNES::debugger.step_cpu = true;
SNES::cpu.disassemble_opcode(t);
echo(string() << t << "<br>");
}
if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::CPUBus) {
SNES::debugger.step_cpu = true;
SNES::cpu.disassemble_opcode(t, SNES::cpu.opcode_pc);
string s = t;
s.replace(" ", "&nbsp;");
echo(string() << "<font color='#a00000'>" << s << "</font><br>");
disassembler->refresh(Disassembler::CPU, SNES::cpu.opcode_pc);
}
if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::APURAM) {
SNES::debugger.step_smp = true;
SNES::smp.disassemble_opcode(t);
echo(string() << t << "<br>");
}
if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::APURAM) {
SNES::debugger.step_smp = true;
SNES::smp.disassemble_opcode(t, SNES::smp.opcode_pc);
string s = t;
s.replace(" ", "&nbsp;");
echo(string() << "<font color='#a00000'>" << t << "</font><br>");
disassembler->refresh(Disassembler::SMP, SNES::smp.opcode_pc);
}
} break;
case SNES::Debugger::CPUStep: {
SNES::cpu.disassemble_opcode(t);
SNES::cpu.disassemble_opcode(t, SNES::cpu.regs.pc);
string s = t;
s.replace(" ", "&nbsp;");
echo(string() << "<font color='#0000a0'>" << s << "</font><br>");
disassembler->refresh(Disassembler::CPU, SNES::cpu.regs.pc);
} break;
case SNES::Debugger::SMPStep: {
SNES::smp.disassemble_opcode(t);
SNES::smp.disassemble_opcode(t, SNES::smp.regs.pc);
string s = t;
s.replace(" ", "&nbsp;");
echo(string() << "<font color='#a00000'>" << s << "</font><br>");
disassembler->refresh(Disassembler::SMP, SNES::smp.regs.pc);
} break;
}
}
@ -199,3 +224,5 @@ void Debugger::frameTick() {
vramViewer->autoUpdate();
}
}
#endif

View File

@ -1,14 +1,10 @@
#include "hexeditor.moc"
#include "breakpoint.moc"
#include "memory.moc"
#include "vramviewer.moc"
class Debugger : public QbWindow {
Q_OBJECT
public:
QMenuBar *menu;
QMenu *tools;
QAction *tools_disassembler;
QAction *tools_breakpoint;
QAction *tools_memory;
QAction *tools_vramViewer;
@ -25,15 +21,17 @@ public:
QCheckBox *stepSMP;
QCheckBox *traceCPU;
QCheckBox *traceSMP;
QCheckBox *traceMask;
QWidget *spacer;
void modifySystemState(unsigned);
void echo(const char *message);
void tracerUpdate();
void event();
void frameTick();
Debugger();
public slots:
void showDisassembler();
void showBreakpointEditor();
void showMemoryEditor();
void showVramViewer();
@ -42,9 +40,9 @@ public slots:
void toggleRunStatus();
void stepAction();
void toggleTraceCPU();
void toggleTraceSMP();
private:
unsigned frameCounter;
} *debugger;
};
extern Debugger *debugger;

View File

@ -0,0 +1,96 @@
#include "disassembler.moc"
Disassembler *disassembler;
Disassembler::Disassembler() : QbWindow(config().geometry.disassembler) {
setObjectName("disassembler");
setWindowTitle("Disassembler");
layout = new QHBoxLayout;
layout->setMargin(Style::WindowMargin);
layout->setSpacing(Style::WidgetSpacing);
setLayout(layout);
view = new QTextEdit;
view->setReadOnly(true);
view->setFont(QFont(Style::Monospace));
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view->setMinimumWidth((23 + 2) * view->fontMetrics().width(' '));
view->setMinimumHeight((25 + 1) * view->fontMetrics().height());
layout->addWidget(view);
controlLayout = new QVBoxLayout;
controlLayout->setAlignment(Qt::AlignTop);
controlLayout->setSpacing(0);
layout->addLayout(controlLayout);
sourceLabel = new QLabel("Source:");
controlLayout->addWidget(sourceLabel);
sourceCPU = new QRadioButton("S-CPU");
sourceCPU->setChecked(true);
controlLayout->addWidget(sourceCPU);
sourceSMP = new QRadioButton("S-SMP");
controlLayout->addWidget(sourceSMP);
}
void Disassembler::refresh(Source source, unsigned addr) {
if(source == CPU && !sourceCPU->isChecked()) return;
if(source == SMP && !sourceSMP->isChecked()) return;
uint8 *usage;
unsigned mask;
if(source == CPU) { usage = SNES::cpu.usage; mask = (1 << 24) - 1; }
if(source == SMP) { usage = SNES::smp.usage; mask = (1 << 16) - 1; }
int line[25];
for(unsigned i = 0; i < 25; i++) line[i] = -1;
line[12] = addr;
for(signed index = 11; index >= 0; index--) {
int base = line[index + 1];
if(base == -1) break;
for(unsigned i = 1; i <= 4; i++) {
if(usage[(base - i) & mask] & 0x20) {
line[index] = base - i;
break;
}
}
}
for(signed index = 13; index <= 24; index++) {
int base = line[index - 1];
if(base == -1) break;
for(unsigned i = 1; i <= 4; i++) {
if(usage[(base + i) & mask] & 0x20) {
line[index] = base + i;
break;
}
}
}
string output;
for(unsigned i = 0; i < 25; i++) {
if(i < 12) output << "<font color='#0000a0'>";
else if(i == 12) output << "<font color='#00a000'>";
else output << "<font color='#a00000'>";
if(line[i] == -1) {
output << "...";
} else {
char t[256];
if(source == CPU) { SNES::cpu.disassemble_opcode(t, line[i]); t[20] = 0; }
if(source == SMP) { SNES::smp.disassemble_opcode(t, line[i]); t[23] = 0; }
string text = rtrim(t);
text.replace(" ", "&nbsp;");
output << text;
}
output << "</font>";
if(i != 24) output << "<br>";
}
view->setHtml(output);
}

View File

@ -0,0 +1,18 @@
class Disassembler : public QbWindow {
Q_OBJECT
public:
enum Source { CPU, SMP };
QHBoxLayout *layout;
QTextEdit *view;
QVBoxLayout *controlLayout;
QLabel *sourceLabel;
QRadioButton *sourceCPU;
QRadioButton *sourceSMP;
void refresh(Source, unsigned);
Disassembler();
};
extern Disassembler *disassembler;

View File

@ -1,3 +1,5 @@
#include "hexeditor.moc"
void HexEditor::keyPressEvent(QKeyEvent *event) {
QTextCursor cursor = textCursor();
unsigned x = cursor.position() % 56;

View File

@ -1,38 +1,15 @@
#include "memory.moc"
MemoryEditor *memoryEditor;
MemoryEditor::MemoryEditor() : QbWindow(config().geometry.memoryEditor) {
setObjectName("memory-editor");
setWindowTitle("Memory Editor");
layout = new QVBoxLayout;
layout = new QHBoxLayout;
layout->setMargin(Style::WindowMargin);
layout->setSpacing(Style::WidgetSpacing);
setLayout(layout);
controlLayout = new QHBoxLayout;
controlLayout->setSpacing(Style::WidgetSpacing);
layout->addLayout(controlLayout);
spacer = new QWidget;
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
controlLayout->addWidget(spacer);
addr = new QLineEdit;
addr->setFixedWidth(80);
controlLayout->addWidget(addr);
source = new QComboBox;
source->addItem("S-CPU bus");
source->addItem("S-APU bus");
source->addItem("S-PPU VRAM");
source->addItem("S-PPU OAM");
source->addItem("S-PPU CGRAM");
controlLayout->addWidget(source);
autoUpdateBox = new QCheckBox("Auto update");
controlLayout->addWidget(autoUpdateBox);
refreshButton = new QPushButton("Refresh");
controlLayout->addWidget(refreshButton);
editor = new HexEditor;
editor->reader = bind(&MemoryEditor::reader, this);
editor->writer = bind(&MemoryEditor::writer, this);
@ -40,9 +17,43 @@ MemoryEditor::MemoryEditor() : QbWindow(config().geometry.memoryEditor) {
memorySource = SNES::Debugger::CPUBus;
layout->addWidget(editor);
connect(addr, SIGNAL(textEdited(const QString&)), this, SLOT(refresh()));
controlLayout = new QVBoxLayout;
controlLayout->setSpacing(0);
layout->addLayout(controlLayout);
source = new QComboBox;
source->addItem("S-CPU bus");
source->addItem("S-APU bus");
source->addItem("S-PPU VRAM");
source->addItem("S-PPU OAM");
source->addItem("S-PPU CGRAM");
controlLayout->addWidget(source);
controlLayout->addSpacing(2);
addr = new QLineEdit;
controlLayout->addWidget(addr);
autoUpdateBox = new QCheckBox("Auto update");
controlLayout->addWidget(autoUpdateBox);
refreshButton = new QPushButton("Refresh");
controlLayout->addWidget(refreshButton);
spacer = new QWidget;
spacer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
controlLayout->addWidget(spacer);
exportButton = new QPushButton("Export");
controlLayout->addWidget(exportButton);
importButton = new QPushButton("Import");
controlLayout->addWidget(importButton);
connect(source, SIGNAL(currentIndexChanged(int)), this, SLOT(sourceChanged(int)));
connect(addr, SIGNAL(textEdited(const QString&)), this, SLOT(refresh()));
connect(refreshButton, SIGNAL(released()), this, SLOT(refresh()));
connect(exportButton, SIGNAL(released()), this, SLOT(exportMemory()));
connect(importButton, SIGNAL(released()), this, SLOT(importMemory()));
}
void MemoryEditor::autoUpdate() {
@ -51,31 +62,25 @@ void MemoryEditor::autoUpdate() {
void MemoryEditor::synchronize() {
if(SNES::cartridge.loaded() == false) {
addr->setEnabled(false);
source->setEnabled(false);
autoUpdateBox->setEnabled(false);
refreshButton->setEnabled(false);
editor->setHtml("");
editor->scrollbar->setEnabled(false);
source->setEnabled(false);
addr->setEnabled(false);
autoUpdateBox->setEnabled(false);
refreshButton->setEnabled(false);
exportButton->setEnabled(false);
importButton->setEnabled(false);
} else {
addr->setEnabled(true);
editor->scrollbar->setEnabled(true);
source->setEnabled(true);
addr->setEnabled(true);
autoUpdateBox->setEnabled(true);
refreshButton->setEnabled(true);
editor->scrollbar->setEnabled(true);
exportButton->setEnabled(true);
importButton->setEnabled(true);
}
}
void MemoryEditor::refresh() {
if(SNES::cartridge.loaded() == false) {
editor->setHtml("");
return;
}
editor->setOffset(strhex(addr->text().toUtf8().data()));
editor->update();
}
void MemoryEditor::sourceChanged(int index) {
switch(index) { default:
case 0: memorySource = SNES::Debugger::CPUBus; editor->setSize(16 * 1024 * 1024); break;
@ -89,6 +94,58 @@ void MemoryEditor::sourceChanged(int index) {
editor->update();
}
void MemoryEditor::refresh() {
if(SNES::cartridge.loaded() == false) {
editor->setHtml("");
return;
}
editor->setOffset(strhex(addr->text().toUtf8().data()));
editor->update();
}
void MemoryEditor::exportMemory() {
string basename = config().path.data;
if(basename == "") basename = dir(utility.cartridge.fileName);
exportMemory(SNES::memory::cartram, string() << basename << "sram.bin");
exportMemory(SNES::memory::wram, string() << basename << "wram.bin");
exportMemory(SNES::memory::apuram, string() << basename << "apuram.bin");
exportMemory(SNES::memory::vram, string() << basename << "vram.bin");
exportMemory(SNES::memory::oam, string() << basename << "oam.bin");
exportMemory(SNES::memory::cgram, string() << basename << "cgram.bin");
}
void MemoryEditor::importMemory() {
string basename = config().path.data;
if(basename == "") basename = dir(utility.cartridge.fileName);
importMemory(SNES::memory::cartram, string() << basename << "sram.bin");
importMemory(SNES::memory::wram, string() << basename << "wram.bin");
importMemory(SNES::memory::apuram, string() << basename << "apuram.bin");
importMemory(SNES::memory::vram, string() << basename << "vram.bin");
importMemory(SNES::memory::oam, string() << basename << "oam.bin");
importMemory(SNES::memory::cgram, string() << basename << "cgram.bin");
refresh(); //in case import changed values that are currently being displayed ...
}
void MemoryEditor::exportMemory(SNES::Memory &memory, const string &filename) const {
file fp;
if(fp.open(filename, file::mode_write)) {
for(unsigned i = 0; i < memory.size(); i++) fp.write(memory.read(i));
fp.close();
}
}
void MemoryEditor::importMemory(SNES::Memory &memory, const string &filename) const {
file fp;
if(fp.open(filename, file::mode_read)) {
unsigned filesize = fp.size();
for(unsigned i = 0; i < memory.size() && i < filesize; i++) memory.write(i, fp.read());
fp.close();
}
}
uint8 MemoryEditor::reader(unsigned addr) {
return SNES::debugger.read(memorySource, addr);
}

View File

@ -2,14 +2,16 @@ class MemoryEditor : public QbWindow {
Q_OBJECT
public:
QVBoxLayout *layout;
QHBoxLayout *controlLayout;
QWidget *spacer;
QLineEdit *addr;
QHBoxLayout *layout;
HexEditor *editor;
QVBoxLayout *controlLayout;
QComboBox *source;
QLineEdit *addr;
QCheckBox *autoUpdateBox;
QPushButton *refreshButton;
HexEditor *editor;
QPushButton *exportButton;
QWidget *spacer;
QPushButton *importButton;
void autoUpdate();
void synchronize();
@ -21,6 +23,12 @@ public:
MemoryEditor();
public slots:
void refresh();
void sourceChanged(int);
} *memoryEditor;
void refresh();
void exportMemory();
void importMemory();
void exportMemory(SNES::Memory&, const string&) const;
void importMemory(SNES::Memory&, const string&) const;
};
extern MemoryEditor *memoryEditor;

View File

@ -0,0 +1,74 @@
#include "tracer.moc"
Tracer *tracer;
void Tracer::stepCpu() {
if(traceCpu) {
unsigned addr = SNES::cpu.regs.pc;
if(!traceMask || !(traceMaskCPU[addr >> 3] & (0x80 >> (addr & 7)))) {
char text[256];
SNES::cpu.disassemble_opcode(text, addr);
tracefile.print(string() << text << "\n");
}
traceMaskCPU[addr >> 3] |= 0x80 >> (addr & 7);
}
}
void Tracer::stepSmp() {
if(traceSmp) {
unsigned addr = SNES::smp.regs.pc;
if(!traceMask || !(traceMaskSMP[addr >> 3] & (0x80 >> (addr & 7)))) {
char text[256];
SNES::smp.disassemble_opcode(text, addr);
tracefile.print(string() << text << "\n");
}
traceMaskSMP[addr >> 3] |= 0x80 >> (addr & 7);
}
}
void Tracer::setCpuTraceState(int state) {
traceCpu = (state == Qt::Checked);
if(traceCpu && !tracefile.open()) {
tracefile.open(string() << config().path.data << "trace.log", file::mode_write);
} else if(!traceCpu && !traceSmp && tracefile.open()) {
tracefile.close();
}
}
void Tracer::setSmpTraceState(int state) {
traceSmp = (state == Qt::Checked);
if(traceSmp && !tracefile.open()) {
tracefile.open(string() << config().path.data << "trace.log", file::mode_write);
} else if(!traceCpu && !traceSmp && tracefile.open()) {
tracefile.close();
}
}
void Tracer::setTraceMaskState(int state) {
traceMask = (state == Qt::Checked);
if(traceMask) {
//flush all bitmasks once enabled
memset(traceMaskCPU, 0x00, (1 << 24) >> 3);
memset(traceMaskSMP, 0x00, (1 << 16) >> 3);
}
}
Tracer::Tracer() {
traceCpu = false;
traceSmp = false;
traceMask = false;
traceMaskCPU = new uint8[(1 << 24) >> 3]();
traceMaskSMP = new uint8[(1 << 16) >> 3]();
SNES::cpu.step_event = bind(&Tracer::stepCpu, this);
SNES::smp.step_event = bind(&Tracer::stepSmp, this);
}
Tracer::~Tracer() {
delete[] traceMaskCPU;
delete[] traceMaskSMP;
if(tracefile.open()) tracefile.close();
}

View File

@ -0,0 +1,26 @@
class Tracer : public QObject {
Q_OBJECT
public:
void stepCpu();
void stepSmp();
Tracer();
~Tracer();
public slots:
void setCpuTraceState(int);
void setSmpTraceState(int);
void setTraceMaskState(int);
private:
file tracefile;
bool traceCpu;
bool traceSmp;
bool traceMask;
uint8 *traceMaskCPU;
uint8 *traceMaskSMP;
};
extern Tracer *tracer;

View File

@ -1,3 +1,6 @@
#include "vramviewer.moc"
VramViewer *vramViewer;
VramViewer::VramViewer() : QbWindow(config().geometry.vramViewer) {
setObjectName("vram-viewer");
setWindowTitle("Video RAM Viewer");

View File

@ -32,4 +32,6 @@ private:
void refresh4bpp(const uint8_t*, uint32_t*);
void refresh8bpp(const uint8_t*, uint32_t*);
void refreshMode7(const uint8_t*, uint32_t*);
} *vramViewer;
};
extern VramViewer *vramViewer;

View File

@ -1,3 +1,5 @@
#include "../ui-base.hpp"
#include "controller.cpp"
#include "userinterface-general.cpp"
#include "userinterface-system.cpp"
@ -112,7 +114,9 @@ AnalogInput::AnalogInput(const char *label, const char *configName) : MappedInpu
void HotkeyInput::poll() {
DigitalInput::poll();
if(state && state != previousState && mainWindow->isActive()) pressed();
if(mainWindow->isActive() && state != previousState) {
state ? pressed() : released();
}
}
HotkeyInput::HotkeyInput(const char *label, const char *configName) : DigitalInput(label, configName) {
@ -227,9 +231,18 @@ void InputMapper::cache() {
}
int16_t InputMapper::status(bool port, unsigned device, unsigned index, unsigned id) {
if(port == InputCategory::Port1 && port1) return port1->status(index, id);
if(port == InputCategory::Port2 && port2) return port2->status(index, id);
return 0;
int16_t result = 0;
if(port == InputCategory::Port1 && port1) result = port1->status(index, id);
if(port == InputCategory::Port2 && port2) result = port2->status(index, id);
if(movie.state == Movie::Playback) {
result = movie.read();
} else if(movie.state == Movie::Record) {
movie.write(result);
}
return result;
}
string InputMapper::modifierString() const {

View File

@ -40,6 +40,7 @@ struct AnalogInput : MappedInput {
struct HotkeyInput : DigitalInput {
void poll();
virtual void pressed() {}
virtual void released() {}
HotkeyInput(const char*, const char*);
};

View File

@ -2,6 +2,69 @@ InputGroup userInterfaceEmulationSpeed(InputCategory::UserInterface, "Emulation
namespace UserInterfaceEmulationSpeed {
//slowdown and speedup do not work well with Vsync enabled, as it locks the
//speed to the monitor refresh rate. thus, when one is pressed, it is disabled
//until the key is released.
struct Slowdown : HotkeyInput {
bool syncVideo;
void pressed() {
config().system.speed = 0;
utility.updateEmulationSpeed();
syncVideo = config().video.synchronize;
if(syncVideo) {
config().video.synchronize = false;
utility.updateAvSync();
}
mainWindow->syncUi();
}
void released() {
config().system.speed = 2;
utility.updateEmulationSpeed();
if(syncVideo) {
config().video.synchronize = true;
utility.updateAvSync();
}
mainWindow->syncUi();
}
Slowdown() : HotkeyInput("Slowdown", "config.userInterface.emulationSpeed.slowdown") {
userInterfaceEmulationSpeed.attach(this);
}
} slowdown;
struct Speedup : HotkeyInput {
bool syncVideo;
void pressed() {
config().system.speed = 4;
utility.updateEmulationSpeed();
syncVideo = config().video.synchronize;
if(syncVideo) {
config().video.synchronize = false;
utility.updateAvSync();
}
mainWindow->syncUi();
}
void released() {
config().system.speed = 2;
utility.updateEmulationSpeed();
if(syncVideo) {
config().video.synchronize = true;
utility.updateAvSync();
}
mainWindow->syncUi();
}
Speedup() : HotkeyInput("Speedup", "config.userInterface.emulationSpeed.speedup") {
name = "KB0::Tilde";
userInterfaceEmulationSpeed.attach(this);
}
} speedup;
struct Decrease : HotkeyInput {
void pressed() {
if(config().system.speed > 0) config().system.speed--;

View File

@ -8,7 +8,7 @@ struct ToggleMenubar : HotkeyInput {
}
ToggleMenubar() : HotkeyInput("Toggle Menubar", "input.userInterface.general.toggleMenubar") {
name = "KB0::F12";
name = "KB0::Tab";
userInterfaceGeneral.attach(this);
}
} toggleMenubar;
@ -19,7 +19,7 @@ struct ToggleStatusbar : HotkeyInput {
}
ToggleStatusbar() : HotkeyInput("Toggle Statusbar", "input.userInterface.general.toggleStatusbar") {
name = "KB0::F12";
name = "KB0::Tab";
userInterfaceGeneral.attach(this);
}
} toggleStatusbar;

View File

@ -6,9 +6,20 @@ namespace UserInterfaceStates {
//for the sake of users, this is displayed as 1-10 in the GUI
unsigned activeState = 0;
struct Rewind : HotkeyInput {
void pressed() {
::state.rewind();
}
Rewind() : HotkeyInput("Rewind", "input.userInterface.states.rewind") {
name = "KB0::Backspace";
userInterfaceStates.attach(this);
}
} rewind;
struct LoadActiveState : HotkeyInput {
void pressed() {
utility.quickLoad(activeState);
::state.load(activeState);
}
LoadActiveState() : HotkeyInput("Load Active Quick State", "input.userInterface.states.loadActiveQuickState") {
@ -19,7 +30,7 @@ struct LoadActiveState : HotkeyInput {
struct SaveActiveState : HotkeyInput {
void pressed() {
utility.quickSave(activeState);
::state.save(activeState);
}
SaveActiveState() : HotkeyInput("Save Active Quick State", "input.userInterface.states.saveActiveQuickState") {
@ -28,6 +39,28 @@ struct SaveActiveState : HotkeyInput {
}
} saveActiveState;
struct DecrementAndLoadState : HotkeyInput {
void pressed() {
activeState = (activeState + 10 - 1) % 10;
::state.load(activeState);
}
DecrementAndLoadState() : HotkeyInput("Decrement and Load State", "input.userInterface.states.decrementAndLoadState") {
userInterfaceStates.attach(this);
}
} decrementAndLoadState;
struct SaveAndIncrementState : HotkeyInput {
void pressed() {
::state.save(activeState);
activeState = (activeState + 10 + 1) % 10;
}
SaveAndIncrementState() : HotkeyInput("Save and Increment State", "input.userInterface.states.saveAndIncrementState") {
userInterfaceStates.attach(this);
}
} saveAndIncrementState;
struct DecrementActiveState : HotkeyInput {
void pressed() {
activeState = (activeState + 10 - 1) % 10;
@ -54,7 +87,7 @@ struct IncrementActiveState : HotkeyInput {
struct LoadState1 : HotkeyInput {
void pressed() {
utility.quickLoad(0);
::state.load(0);
}
LoadState1() : HotkeyInput("Load Quick State 1", "input.userInterface.states.loadQuickState1") {
@ -64,7 +97,7 @@ struct LoadState1 : HotkeyInput {
struct LoadState2 : HotkeyInput {
void pressed() {
utility.quickLoad(1);
::state.load(1);
}
LoadState2() : HotkeyInput("Load Quick State 2", "input.userInterface.states.loadQuickState2") {
@ -74,7 +107,7 @@ struct LoadState2 : HotkeyInput {
struct LoadState3 : HotkeyInput {
void pressed() {
utility.quickLoad(2);
::state.load(2);
}
LoadState3() : HotkeyInput("Load Quick State 3", "input.userInterface.states.loadQuickState3") {
@ -84,7 +117,7 @@ struct LoadState3 : HotkeyInput {
struct SaveState1 : HotkeyInput {
void pressed() {
utility.quickSave(0);
::state.save(0);
}
SaveState1() : HotkeyInput("Save Quick State 1", "input.userInterface.states.saveQuickState1") {
@ -94,7 +127,7 @@ struct SaveState1 : HotkeyInput {
struct SaveState2 : HotkeyInput {
void pressed() {
utility.quickSave(1);
::state.save(1);
}
SaveState2() : HotkeyInput("Save Quick State 2", "input.userInterface.states.saveQuickState2") {
@ -104,7 +137,7 @@ struct SaveState2 : HotkeyInput {
struct SaveState3 : HotkeyInput {
void pressed() {
utility.quickSave(2);
::state.save(2);
}
SaveState3() : HotkeyInput("Save Quick State 3", "input.userInterface.states.saveQuickState3") {

View File

@ -8,7 +8,7 @@ struct ToggleFullscreen : HotkeyInput {
}
ToggleFullscreen() : HotkeyInput("Toggle Fullscreen Mode", "input.userInterface.videoSettings.toggleFullscreen") {
name = "KB0::F11";
name = "Alt+KB0::Return";
userInterfaceVideoSettings.attach(this);
}
} toggleFullscreen;

View File

@ -1,3 +1,5 @@
Interface interface;
void Interface::video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height) {
uint32_t *output;
unsigned outwidth, outheight, outpitch;
@ -10,7 +12,10 @@ void Interface::video_refresh(uint16_t *data, unsigned pitch, unsigned *line, un
if(saveScreenshot == true) captureScreenshot(output, outpitch, outwidth, outheight);
}
state.frame();
#if defined(DEBUGGER)
debugger->frameTick();
#endif
}
void Interface::audio_sample(uint16_t left, uint16_t right) {
@ -30,7 +35,7 @@ void Interface::captureScreenshot(uint32_t *data, unsigned pitch, unsigned width
saveScreenshot = false;
QImage image((const unsigned char*)data, width, height, pitch, QImage::Format_RGB32);
string filename = "screenshot-";
string filename = nall::basename(notdir(utility.cartridge.fileName));
time_t systemTime = time(0);
tm *currentTime = localtime(&systemTime);
char t[512];
@ -38,7 +43,7 @@ void Interface::captureScreenshot(uint32_t *data, unsigned pitch, unsigned width
1900 + currentTime->tm_year, 1 + currentTime->tm_mon, currentTime->tm_mday,
currentTime->tm_hour, currentTime->tm_min, currentTime->tm_sec
);
filename << t << ".png";
filename << "-" << t << ".png";
string path = config().path.data;
if(path == "") path = dir(utility.cartridge.baseName);

View File

@ -8,4 +8,6 @@ public:
Interface();
void captureScreenshot(uint32_t*, unsigned, unsigned, unsigned);
bool saveScreenshot;
} interface;
};
extern Interface interface;

View File

@ -1,12 +1,15 @@
#include "main.hpp"
#include "resource/resource.rcc"
#include "ui-base.hpp"
#include "resource.rcc"
#if defined(PLATFORM_X)
#include "platform/platform_x.cpp"
const char Style::Monospace[64] = "Monospace";
#elif defined(PLATFORM_OSX)
#include "platform/platform_osx.cpp"
const char Style::Monospace[64] = "Courier New";
#elif defined(PLATFORM_WIN)
#include "platform/platform_win.cpp"
const char Style::Monospace[64] = "Lucida Console";
#else
#error "unsupported platform"
#endif
@ -17,9 +20,6 @@
const char defaultStylesheet[] =
"QLabel.title {"
" font: bold 18px \"Georgia\";"
" margin-bottom: 5px;"
" margin-left: -5px;"
" margin-top: 5px;"
"}\n"
"#backdrop {"
@ -27,21 +27,10 @@ const char defaultStylesheet[] =
"}\n";
#include "application/application.cpp"
#include "debugger/debugger.cpp"
#include "input/input.cpp"
#include "link/filter.cpp"
#include "link/reader.cpp"
#include "utility/utility.cpp"
int main(int argc, char **argv) {
application.main(argc, argv);
#if defined(PLATFORM_WIN)
//Qt/Windows has a few bugs that cause the process to hang and/or crash when
//unloading DLLs. 4.5.x always hangs, and 4.6.x crashes under certain
//circumstances. However, all DLLs must unload for profiling to work.
//The below code bypasses Qt DLL unloading, but only when the binary is named
//as such to indicate that profile generation is taking place.
if(strpos(argv[0], "bsnes-profile") < 0) TerminateProcess(GetCurrentProcess(), 0);
#endif
return 0;
return application.main(argc, argv);
}

View File

@ -1,48 +0,0 @@
#define UNICODE
#define QT_NO_DEBUG
#define QT_CORE_LIB
#define QT_GUI_LIB
#define QT_THREAD_SUPPORT
#include <QApplication>
#include <QtGui>
//Q_IMPORT_PLUGIN(QJpegPlugin)
//Q_IMPORT_PLUGIN(QMngPlugin)
#include <../base.hpp>
#include <nall/base64.hpp>
#include <nall/config.hpp>
#include <nall/input.hpp>
#include <nall/ups.hpp>
using namespace nall;
#include <ruby/ruby.hpp>
using namespace ruby;
#include "config.hpp"
#include "interface.hpp"
#include "application/application.moc"
#include "debugger/debugger.moc"
#include "input/input.hpp"
#include "link/filter.hpp"
#include "link/reader.hpp"
#include "utility/utility.hpp"
struct Style {
static const char Monospace[];
enum {
WindowMargin = 5,
WidgetSpacing = 5,
SeparatorSpacing = 5,
};
};
#if defined(PLATFORM_X)
const char Style::Monospace[] = "Monospace";
#elif defined(PLATFORM_WIN)
const char Style::Monospace[] = "Lucida Console";
#else
const char Style::Monospace[] = "Courier New";
#endif

114
src/ui_qt/movie/movie.cpp Normal file
View File

@ -0,0 +1,114 @@
#include "../ui-base.hpp"
Movie movie;
void Movie::chooseFile() {
diskBrowser->chooseFile(
bind(&Movie::play, this),
config().path.current.movie,
"Select Movie"
);
}
void Movie::play(string filename) {
if(Movie::state != Inactive) stop();
if(fp.open(filename, file::mode_read)) {
if(fp.size() < 32) goto corrupt;
unsigned signature = fp.readm(4);
if(signature != 0x42535631) goto corrupt;
unsigned version = fp.readl(4);
if(version != bsnesSerializerVersion) goto corrupt;
unsigned crc32 = fp.readl(4);
if(crc32 != SNES::cartridge.crc32()) goto corrupt;
unsigned size = fp.readl(4);
uint8_t *data = new uint8_t[size];
fp.read(data, size);
serializer state(data, size);
SNES::system.unserialize(state);
Movie::state = Playback;
mainWindow->syncUi();
utility.showMessage("Playback started.");
return;
}
corrupt:
fp.close();
utility.showMessage("Movie file is invalid, playback cancelled.");
}
void Movie::record() {
if(Movie::state != Inactive) {
utility.showMessage("Movie mode already active, recording cancelled.");
} else {
SNES::system.runtosave();
serializer state = SNES::system.serialize();
utility.showMessage("Recording started.");
Movie::state = Record;
mainWindow->syncUi();
fp.open(makeFilename(), file::mode_write);
fp.writem(0x42535631, 4);
fp.writel(bsnesSerializerVersion, 4);
fp.writel(SNES::cartridge.crc32(), 4);
fp.writel(state.size(), 4);
fp.write(state.data(), state.size());
}
}
void Movie::stop() {
if(Movie::state != Inactive) {
Movie::state = Inactive;
mainWindow->syncUi();
fp.close();
utility.showMessage("Recording / playback stopped.");
}
}
string Movie::makeFilename() const {
string filename;
filename << utility.cartridge.name << "-";
time_t systemTime = time(0);
tm *currentTime = localtime(&systemTime);
char t[512];
sprintf(t, "%.4u%.2u%.2u-%.2u%.2u%.2u",
1900 + currentTime->tm_year, 1 + currentTime->tm_mon, currentTime->tm_mday,
currentTime->tm_hour, currentTime->tm_min, currentTime->tm_sec
);
filename << t << ".bsv";
string filepath = config().path.data;
if(filepath == "") filepath = dir(utility.cartridge.fileName);
filepath << filename;
return filepath;
}
int16_t Movie::read() {
int16_t result = fp.readl(2);
if(fp.end()) {
Movie::state = Inactive;
mainWindow->syncUi();
fp.close();
utility.showMessage("Playback finished.");
}
return result;
}
void Movie::write(int16_t value) {
fp.writel(value, 2);
}
Movie::Movie() {
state = Inactive;
}

20
src/ui_qt/movie/movie.hpp Normal file
View File

@ -0,0 +1,20 @@
class Movie {
public:
enum State { Inactive, Playback, Record } state;
void chooseFile();
void play(string filename);
void record();
void stop();
Movie();
//private:
file fp;
string makeFilename() const;
int16_t read();
void write(int16_t value);
};
extern Movie movie;

View File

@ -9,8 +9,6 @@ char *getcwd(char *path) {
return getcwd(path, PATH_MAX);
}
#define mkdir(path) (mkdir)(path, 0755)
void initargs(int &argc, char **&argv) {
}

View File

@ -35,12 +35,19 @@ void initargs(int &argc, char **&argv) {
}
bool Application::App::winEventFilter(MSG *msg, long *result) {
//supress screen saver from activating during gameplay
if(msg->message == WM_SYSCOMMAND) {
if(msg->wParam == SC_SCREENSAVE || msg->wParam == SC_MONITORPOWER) {
*result = 0;
return true;
}
}
//prevent DirectSound audio buffer from looping during Windows modal events
if(msg->message == WM_ENTERMENULOOP || msg->message == WM_ENTERSIZEMOVE) {
audio.clear();
}
return false;
}

View File

@ -25,8 +25,6 @@ char *getcwd(char *path) {
return getcwd(path, PATH_MAX);
}
#define mkdir(path) (mkdir)(path, 0755)
void initargs(int &argc, char **&argv) {
}

View File

@ -7,31 +7,32 @@
<file alias="documentation.html">../../data/documentation.html</file>
<file alias="license.html">../../data/license.html</file>
<file alias="16x16/accessories-text-editor.png">../../data/icons-16x16/accessories-text-editor.png</file>
<file alias="16x16/appointment-new.png">../../data/icons-16x16/appointment-new.png</file>
<file alias="16x16/document-new.png">../../data/icons-16x16/document-new.png</file>
<file alias="16x16/document-open.png">../../data/icons-16x16/document-open.png</file>
<file alias="16x16/folder-new.png">../../data/icons-16x16/folder-new.png</file>
<file alias="16x16/help-browser.png">../../data/icons-16x16/help-browser.png</file>
<file alias="16x16/image-x-generic.png">../../data/icons-16x16/image-x-generic.png</file>
<file alias="16x16/input-gaming.png">../../data/icons-16x16/input-gaming.png</file>
<file alias="16x16/preferences-desktop.png">../../data/icons-16x16/preferences-desktop.png</file>
<file alias="16x16/process-stop.png">../../data/icons-16x16/process-stop.png</file>
<file alias="16x16/system-file-manager.png">../../data/icons-16x16/system-file-manager.png</file>
<file alias="16x16/system-search.png">../../data/icons-16x16/system-search.png</file>
<file alias="16x16/text-x-generic.png">../../data/icons-16x16/text-x-generic.png</file>
<file alias="16x16/utilities-terminal.png">../../data/icons-16x16/utilities-terminal.png</file>
<file alias="16x16/video-display.png">../../data/icons-16x16/video-display.png</file>
<file alias="16x16/view-refresh.png">../../data/icons-16x16/view-refresh.png</file>
<file alias="16x16/item-check-on.png">../../data/icons-16x16/item-check-on.png</file>
<file alias="16x16/item-check-off.png">../../data/icons-16x16/item-check-off.png</file>
<file alias="16x16/item-radio-on.png">../../data/icons-16x16/item-radio-on.png</file>
<file alias="16x16/item-radio-off.png">../../data/icons-16x16/item-radio-off.png</file>
<file alias="16x16/applications-multimedia.png">../../data/icons-16x16/applications-multimedia.png</file>
<file alias="16x16/appointment-new.png">../../data/icons-16x16/appointment-new.png</file>
<file alias="16x16/document-open.png">../../data/icons-16x16/document-open.png</file>
<file alias="16x16/folder-new.png">../../data/icons-16x16/folder-new.png</file>
<file alias="16x16/help-browser.png">../../data/icons-16x16/help-browser.png</file>
<file alias="16x16/image-x-generic.png">../../data/icons-16x16/image-x-generic.png</file>
<file alias="16x16/input-gaming.png">../../data/icons-16x16/input-gaming.png</file>
<file alias="16x16/media-playback-start.png">../../data/icons-16x16/media-playback-start.png</file>
<file alias="16x16/media-playback-stop.png">../../data/icons-16x16/media-playback-stop.png</file>
<file alias="16x16/media-record.png">../../data/icons-16x16/media-record.png</file>
<file alias="16x16/preferences-desktop.png">../../data/icons-16x16/preferences-desktop.png</file>
<file alias="16x16/process-stop.png">../../data/icons-16x16/process-stop.png</file>
<file alias="16x16/text-x-generic.png">../../data/icons-16x16/text-x-generic.png</file>
<file alias="16x16/utilities-terminal.png">../../data/icons-16x16/utilities-terminal.png</file>
<file alias="16x16/video-display.png">../../data/icons-16x16/video-display.png</file>
<file alias="16x16/view-refresh.png">../../data/icons-16x16/view-refresh.png</file>
<file alias="22x22/accessories-text-editor.png">../../data/icons-22x22/accessories-text-editor.png</file>
<file alias="22x22/audio-volume-high.png">../../data/icons-22x22/audio-volume-high.png</file>
<file alias="22x22/folder.png">../../data/icons-22x22/folder.png</file>
<file alias="22x22/image-x-generic.png">../../data/icons-22x22/image-x-generic.png</file>
<file alias="22x22/input-gaming.png">../../data/icons-22x22/input-gaming.png</file>
<file alias="22x22/preferences-system.png">../../data/icons-22x22/preferences-system.png</file>
<file alias="22x22/system-file-manager.png">../../data/icons-22x22/system-file-manager.png</file>

Some files were not shown because too many files have changed in this diff Show More