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.
|
@ -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 ###
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -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
|
|
@ -14,3 +14,4 @@
|
|||
#include "st010/st010.hpp"
|
||||
#include "st011/st011.hpp"
|
||||
#include "st018/st018.hpp"
|
||||
#include "21fx/21fx.hpp"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
Before Width: | Height: | Size: 574 B |
After Width: | Height: | Size: 592 B |
Before Width: | Height: | Size: 477 B |
After Width: | Height: | Size: 660 B |
After Width: | Height: | Size: 429 B |
After Width: | Height: | Size: 653 B |
Before Width: | Height: | Size: 378 B |
Before Width: | Height: | Size: 935 B |
After Width: | Height: | Size: 900 B |
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -5,5 +5,3 @@ mkdir nall
|
|||
mkdir ruby
|
||||
xcopy /E ..\..\..\nall nall
|
||||
xcopy /E ..\..\..\ruby ruby
|
||||
del ruby\test*
|
||||
del ruby\cc.*
|
||||
|
|
|
@ -8,5 +8,3 @@ cp -r ../../../ruby ./ruby
|
|||
|
||||
rm -r libco/doc
|
||||
rm -r libco/test
|
||||
rm ruby/test*
|
||||
rm ruby/cc.*
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
void disassemble_opcode(char *output);
|
||||
void disassemble_opcode(char *output, uint16 addr);
|
||||
inline uint16 __relb(int8 offset, int op_len);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(¤t);
|
||||
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*)×tamp[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;
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -41,4 +41,6 @@ public:
|
|||
|
||||
public slots:
|
||||
void run();
|
||||
} application;
|
||||
};
|
||||
|
||||
extern Application application;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#include "about.moc"
|
||||
AboutWindow *aboutWindow;
|
||||
|
||||
AboutWindow::AboutWindow() : QbWindow(config().geometry.aboutWindow) {
|
||||
setObjectName("about-window");
|
||||
setWindowTitle("About bsnes ...");
|
||||
|
|
|
@ -9,4 +9,6 @@ public:
|
|||
QLabel *info;
|
||||
|
||||
AboutWindow();
|
||||
} *aboutWindow;
|
||||
};
|
||||
|
||||
extern AboutWindow *aboutWindow;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#include "../ui-base.hpp"
|
||||
|
||||
#include "about.cpp"
|
||||
#include "diskbrowser.cpp"
|
||||
#include "htmlviewer.cpp"
|
||||
#include "loader.cpp"
|
||||
#include "main.cpp"
|
|
@ -1,3 +1,7 @@
|
|||
#include "diskbrowser.moc"
|
||||
FolderCreator *folderCreator;
|
||||
DiskBrowser *diskBrowser;
|
||||
|
||||
//=============
|
||||
//FolderCreator
|
||||
//=============
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#include "htmlviewer.moc"
|
||||
HtmlViewerWindow *htmlViewerWindow;
|
||||
|
||||
HtmlViewerWindow::HtmlViewerWindow() : QbWindow(config().geometry.htmlViewerWindow) {
|
||||
setObjectName("html-window");
|
||||
resize(560, 480);
|
||||
|
|
|
@ -9,4 +9,6 @@ public:
|
|||
HtmlViewerWindow();
|
||||
|
||||
public slots:
|
||||
} *htmlViewerWindow;
|
||||
};
|
||||
|
||||
extern HtmlViewerWindow *htmlViewerWindow;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#include "loader.moc"
|
||||
LoaderWindow *loaderWindow;
|
||||
|
||||
LoaderWindow::LoaderWindow() : QbWindow(config().geometry.loaderWindow) {
|
||||
setObjectName("loader-window");
|
||||
setMinimumWidth(520);
|
||||
|
|
|
@ -42,4 +42,6 @@ public slots:
|
|||
private:
|
||||
SNES::Cartridge::Mode mode;
|
||||
void showWindow(const char *title);
|
||||
} *loaderWindow;
|
||||
};
|
||||
|
||||
extern LoaderWindow *loaderWindow;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#include "breakpoint.moc"
|
||||
BreakpointEditor *breakpointEditor;
|
||||
|
||||
BreakpointItem::BreakpointItem(unsigned id_) : id(id_) {
|
||||
layout = new QHBoxLayout;
|
||||
layout->setMargin(0);
|
||||
|
|
|
@ -25,4 +25,6 @@ public:
|
|||
BreakpointItem *breakpoint[SNES::Debugger::Breakpoints];
|
||||
|
||||
BreakpointEditor();
|
||||
} *breakpointEditor;
|
||||
};
|
||||
|
||||
extern BreakpointEditor *breakpointEditor;
|
||||
|
|
|
@ -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(" ", " ");
|
||||
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(" ", " ");
|
||||
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(" ", " ");
|
||||
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(" ", " ");
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(" ", " ");
|
||||
output << text;
|
||||
}
|
||||
|
||||
output << "</font>";
|
||||
if(i != 24) output << "<br>";
|
||||
}
|
||||
view->setHtml(output);
|
||||
}
|
|
@ -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;
|
|
@ -1,3 +1,5 @@
|
|||
#include "hexeditor.moc"
|
||||
|
||||
void HexEditor::keyPressEvent(QKeyEvent *event) {
|
||||
QTextCursor cursor = textCursor();
|
||||
unsigned x = cursor.position() % 56;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
|
@ -1,3 +1,6 @@
|
|||
#include "vramviewer.moc"
|
||||
VramViewer *vramViewer;
|
||||
|
||||
VramViewer::VramViewer() : QbWindow(config().geometry.vramViewer) {
|
||||
setObjectName("vram-viewer");
|
||||
setWindowTitle("Video RAM Viewer");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -40,6 +40,7 @@ struct AnalogInput : MappedInput {
|
|||
struct HotkeyInput : DigitalInput {
|
||||
void poll();
|
||||
virtual void pressed() {}
|
||||
virtual void released() {}
|
||||
|
||||
HotkeyInput(const char*, const char*);
|
||||
};
|
||||
|
|
|
@ -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--;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -8,4 +8,6 @@ public:
|
|||
Interface();
|
||||
void captureScreenshot(uint32_t*, unsigned, unsigned, unsigned);
|
||||
bool saveScreenshot;
|
||||
} interface;
|
||||
};
|
||||
|
||||
extern Interface interface;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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) {
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|