Unverified Commit 16dd5d40 authored by Rafael Kitover's avatar Rafael Kitover
Browse files

make speedup/turbo configurable + misc #339



Add Speedup / Turbo configuration panel which allows setting the
throttle or number of frames to skip for when the speed key is pressed
or turbo is enabled (which just presses the speed key.)

Throttle and frame-skip are mutually exclusive, throttle must be 0 (no
throttle) when number of frames to skip is non-zero. The dialog controls
handle this.

This is implemented in the core in GBA.cpp, GB.cpp and ConfigManager.

Two new options are added both in ConfigManager and in the wx options,
speedup_throttle and speedup_frame_skip, the defaults are:

```
speedup_throttle   = 0 (no throttle)
speedup_frame_skip = 9
```

this was the original behavior.

Add support for unsigned ints to wx/opts.cpp for these and for throttle,
this requires a new validator wxUIntValidator to use them in spin
controls.

Clean up appearance of the throttle spin control in the General dialog.

Maximum throttle and speedup_throttle is 600, values much over 500 will
not behave differently from 0 on modern hardware.

Maximum frame skip is 30 at the moment.
Signed-off-by: default avatarRafael Kitover <rkitover@gmail.com>
parent 5379708f
......@@ -104,8 +104,9 @@ enum named_opts
OPT_THREAD_PRIORITY,
OPT_VIDEO_OPTION,
OPT_WINDOW_POSITION_X,
OPT_WINDOW_POSITION_Y
OPT_WINDOW_POSITION_Y,
OPT_SPEEDUP_THROTTLE,
OPT_SPEEDUP_FRAME_SKIP
};
#define SOUND_MAX_VOLUME 2.0
......@@ -242,7 +243,9 @@ int* rewindSerials = NULL;
uint32_t autoFrameSkipLastTime;
uint32_t movieLastJoypad;
uint32_t movieNextJoypad;
int throttle;
uint32_t throttle = 100;
uint32_t speedup_throttle = 0;
uint32_t speedup_frame_skip = 9;
const char* preparedCheatCodes[MAX_CHEATS];
......@@ -374,6 +377,8 @@ struct option argOptions[] = {
{ "synchronize", required_argument, 0, OPT_SYNCHRONIZE },
{ "thread-priority", required_argument, 0, OPT_THREAD_PRIORITY },
{ "throttle", required_argument, 0, 'T' },
{ "speedup_throttle", required_argument, 0, OPT_SPEEDUP_THROTTLE },
{ "speedup_frame_skip", required_argument, 0, OPT_SPEEDUP_FRAME_SKIP },
{ "triple-buffering", no_argument, &tripleBuffering, 1 },
{ "use-bios", no_argument, &useBios, 1 },
{ "use-bios-file-gb", no_argument, &useBiosFileGB, 1 },
......@@ -527,6 +532,8 @@ void LoadConfig()
soundRecordDir = ReadPrefString("soundRecordDir");
threadPriority = ReadPref("priority", 2);
throttle = ReadPref("throttle", 100);
speedup_throttle = ReadPref("speedup_throttle", 0);
speedup_frame_skip = ReadPref("speedup_frame_skip", 9);
tripleBuffering = ReadPref("tripleBuffering", 0);
useBios = ReadPrefHex("useBiosGBA");
useBiosFileGB = ReadPref("useBiosGB", 0);
......@@ -971,6 +978,10 @@ int ReadOpts(int argc, char ** argv)
filter = kStretch2x;
}
break;
case 'T':
if (optarg)
throttle = atoi(optarg);
break;
case 'I':
if (optarg) {
ifbType = (IFBFilter)atoi(optarg);
......@@ -1339,6 +1350,14 @@ int ReadOpts(int argc, char ** argv)
// --dotcode-file-name-save
saveDotCodeFile = optarg;
break;
case OPT_SPEEDUP_THROTTLE:
if (optarg)
speedup_throttle = atoi(optarg);
break;
case OPT_SPEEDUP_FRAME_SKIP:
if (optarg)
speedup_frame_skip = atoi(optarg);
break;
}
}
return op;
......
......@@ -141,7 +141,9 @@ extern int winPauseNextFrame;
extern uint32_t autoFrameSkipLastTime;
extern uint32_t movieLastJoypad;
extern uint32_t movieNextJoypad;
extern int throttle;
extern uint32_t throttle;
extern uint32_t speedup_throttle;
extern uint32_t speedup_frame_skip;
extern int preparedCheats;
extern const char *preparedCheatCodes[MAX_CHEATS];
......
......@@ -10,6 +10,7 @@
#include "../Util.h"
#include "../common/ConfigManager.h"
#include "../gba/GBALink.h"
#include "../gba/Sound.h"
#include "gb.h"
#include "gbCheats.h"
#include "gbGlobals.h"
......@@ -4842,8 +4843,32 @@ void gbEmulate(int ticksToStop)
if ((gbLcdTicksDelayed <= 0) && (gbLCDChangeHappened)) {
int framesToSkip = systemFrameSkip;
if (speedup)
framesToSkip = 9; // try 6 FPS during speedup
static bool speedup_throttle_set = false;
static uint32_t last_throttle;
if ((gbJoymask[0] >> 10) & 1) {
if (speedup_throttle != 0) {
if (!speedup_throttle_set && throttle != speedup_throttle) {
last_throttle = throttle;
throttle = speedup_throttle;
soundSetThrottle(speedup_throttle);
speedup_throttle_set = true;
}
}
else {
if (speedup_frame_skip)
framesToSkip = speedup_frame_skip;
speedup_throttle_set = false;
}
}
else if (speedup_throttle_set) {
throttle = last_throttle;
soundSetThrottle(last_throttle);
speedup_throttle_set = false;
}
//gbLcdTicksDelayed = gbLcdTicks+1;
gbLCDChangeHappened = false;
switch (gbLcdModeDelayed) {
......@@ -4919,7 +4944,11 @@ void gbEmulate(int ticksToStop)
newmask = (gbJoymask[0] >> 10);
speedup = (newmask & 1) ? true : false;
speedup = false;
if (newmask & 1 && speedup_throttle == 0)
speedup = true;
gbCapture = (newmask & 2) ? true : false;
if (gbCapture && !gbCapturePrevious) {
......
......@@ -3756,8 +3756,31 @@ void CPULoop(int ticks)
}
} else {
int framesToSkip = systemFrameSkip;
if (speedup)
framesToSkip = 9; // try 6 FPS during speedup
static bool speedup_throttle_set = false;
static uint32_t last_throttle;
if ((joy >> 10) & 1) {
if (speedup_throttle != 0) {
if (!speedup_throttle_set && throttle != speedup_throttle) {
last_throttle = throttle;
throttle = speedup_throttle;
soundSetThrottle(speedup_throttle);
speedup_throttle_set = true;
}
}
else {
if (speedup_frame_skip)
framesToSkip = speedup_frame_skip;
speedup_throttle_set = false;
}
}
else if (speedup_throttle_set) {
throttle = last_throttle;
soundSetThrottle(last_throttle);
speedup_throttle_set = false;
}
if (DISPSTAT & 2) {
// if in H-Blank, leave it and move to drawing mode
......@@ -3788,7 +3811,12 @@ void CPULoop(int ticks)
// If no (m) code is enabled, apply the cheats at each LCDline
if ((cheatsEnabled) && (mastercode == 0))
remainingTicks += cheatsCheckKeys(P1 ^ 0x3FF, ext);
speedup = (ext & 1) ? true : false;
speedup = false;
if (ext & 1 && speedup_throttle == 0)
speedup = true;
capture = (ext & 2) ? true : false;
if (capture && !capturePrevious) {
......
......@@ -432,6 +432,7 @@ SET(XRC_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/xrc/PaletteViewer.xrc
${CMAKE_CURRENT_SOURCE_DIR}/xrc/SoundConfig.xrc
${CMAKE_CURRENT_SOURCE_DIR}/xrc/TileViewer.xrc
${CMAKE_CURRENT_SOURCE_DIR}/xrc/SpeedupConfig.xrc
)
# wxrc does not support xrs files in -c output (> 10x compression)
......
......@@ -2041,6 +2041,14 @@ EVT_HANDLER(GeneralConfigure, "General options...")
}
}
EVT_HANDLER(SpeedupConfigure, "Speedup / Turbo options...")
{
wxDialog* dlg = GetXRCDialog("SpeedupConfig");
if (ShowModal(dlg) == wxID_OK)
update_opts();
}
EVT_HANDLER(GameBoyConfigure, "Game Boy options...")
{
wxDialog* dlg = GetXRCDialog("GameBoyConfig");
......
......@@ -17,6 +17,7 @@
#include <wx/filepicker.h>
#include <wx/progdlg.h>
#include <wx/spinctrl.h>
#include <wx/valnum.h>
#include <wx/stockitem.h>
#include <wx/tokenzr.h>
#include <wx/txtstrm.h>
......@@ -2221,70 +2222,23 @@ public:
DoSetThrottleSel(thr->GetValue());
}
void DoSetThrottleSel(int val)
void DoSetThrottleSel(uint32_t val)
{
switch (val) {
case 0:
thrsel->SetSelection(1);
break;
case 25:
thrsel->SetSelection(2);
break;
case 50:
thrsel->SetSelection(3);
break;
case 100:
thrsel->SetSelection(4);
break;
case 150:
thrsel->SetSelection(5);
break;
case 200:
thrsel->SetSelection(6);
break;
default:
thrsel->SetSelection(0);
break;
}
if (val <= 600)
thrsel->SetSelection(std::round((double)val / 25));
else
thrsel->SetSelection(100 / 25);
}
// set thr from thrsel
void SetThrottle(wxCommandEvent& evt)
{
switch (thrsel->GetSelection()) {
case 0: // blank; leave it alone
break;
case 1:
thr->SetValue(0);
break;
case 2:
thr->SetValue(25);
break;
case 3:
thr->SetValue(50);
break;
uint32_t val = thrsel->GetSelection() * 25;
case 4:
if (val <= 600)
thr->SetValue(val);
else
thr->SetValue(100);
break;
case 5:
thr->SetValue(150);
break;
case 6:
thr->SetValue(200);
break;
}
}
// since this is not the actual dialog, derived from wxDialog, which is
......@@ -2298,6 +2252,102 @@ public:
}
} throttle_ctrl;
// manage speedup key frame skip spinctrl/canned setting choice interaction
static class SpeedupFrameSkipCtrl_t : public wxEvtHandler {
public:
wxSpinCtrl* speedup_frame_skip_spin;
wxChoice* speedup_frame_skip_sel;
void SetSpeedupFrameSkipSel(wxSpinEvent& evt);
void DoSetSpeedupFrameSkipSel(uint32_t val);
void SetSpeedupFrameSkip(wxCommandEvent& evt);
void Init(wxShowEvent& ev);
} speedup_frame_skip_ctrl;
// manage speedup key throttle spinctrl/canned setting choice interaction
static class SpeedupThrottleCtrl_t : public wxEvtHandler {
public:
wxSpinCtrl* speedup_throttle_spin;
wxChoice* speedup_throttle_sel;
// set speedup_throttle_sel from speedup_throttle
void SetSpeedupThrottleSel(wxSpinEvent& evt)
{
DoSetSpeedupThrottleSel(speedup_throttle_spin->GetValue());
}
void DoSetSpeedupThrottleSel(uint32_t val)
{
if (val > 0 && val <= 600) {
speedup_throttle_sel->SetSelection(std::round((double)val / 25));
speedup_frame_skip_ctrl.DoSetSpeedupFrameSkipSel(0);
wxCommandEvent nil;
speedup_frame_skip_ctrl.SetSpeedupFrameSkip(nil);
}
else
speedup_throttle_sel->SetSelection(0);
}
// set speedup_throttle from speedup_throttle_sel
void SetSpeedupThrottle(wxCommandEvent& evt)
{
uint32_t val = speedup_throttle_sel->GetSelection() * 25;
if (val > 0 && val <= 600) {
speedup_throttle_spin->SetValue(val);
speedup_frame_skip_ctrl.DoSetSpeedupFrameSkipSel(0);
wxCommandEvent nil;
speedup_frame_skip_ctrl.SetSpeedupFrameSkip(nil);
}
else
speedup_throttle_spin->SetValue(0);
}
void Init(wxShowEvent& ev)
{
ev.Skip();
DoSetSpeedupThrottleSel(speedup_throttle);
}
} speedup_throttle_ctrl;
// set speedup_frame_skip_sel from speedup_frame_skip
void SpeedupFrameSkipCtrl_t::SetSpeedupFrameSkipSel(wxSpinEvent& evt)
{
DoSetSpeedupFrameSkipSel(speedup_frame_skip_spin->GetValue());
}
void SpeedupFrameSkipCtrl_t::DoSetSpeedupFrameSkipSel(uint32_t val)
{
if (val > 0 && val <= 30) {
speedup_frame_skip_sel->SetSelection(val);
speedup_throttle_ctrl.DoSetSpeedupThrottleSel(0);
wxCommandEvent nil;
speedup_throttle_ctrl.SetSpeedupThrottle(nil);
}
else
speedup_frame_skip_sel->SetSelection(0);
}
// set speedup_frame_skip from speedup_frame_skip_sel
void SpeedupFrameSkipCtrl_t::SetSpeedupFrameSkip(wxCommandEvent& evt)
{
uint32_t val = speedup_frame_skip_sel->GetSelection();
if (val > 0 && val <= 30) {
speedup_frame_skip_spin->SetValue(val);
speedup_throttle_ctrl.DoSetSpeedupThrottleSel(0);
wxCommandEvent nil;
speedup_throttle_ctrl.SetSpeedupThrottle(nil);
}
else
speedup_frame_skip_spin->SetValue(0);
}
void SpeedupFrameSkipCtrl_t::Init(wxShowEvent& ev)
{
ev.Skip();
DoSetSpeedupFrameSkipSel(speedup_frame_skip);
}
/////////////////////////////
//Check if a pointer from the XRC file is valid. If it's not, throw an error telling the user.
template <typename T>
......@@ -3207,6 +3257,11 @@ bool MainFrame::BindControls()
sc = SafeXRCCTRL<wxSpinCtrl>(d, n); \
sc->SetValidator(wxGenericValidator(&o)); \
} while (0)
#define getsc_uint(n, o) \
do { \
sc = SafeXRCCTRL<wxSpinCtrl>(d, n); \
sc->SetValidator(wxUIntValidator(&o)); \
} while (0)
{
// Online Auto Update check frequency
getrbi("UpdateNever", gopts.onlineupdates, 0);
......@@ -3215,7 +3270,7 @@ bool MainFrame::BindControls()
getrbi("PNG", captureFormat, 0);
getrbi("BMP", captureFormat, 1);
getsc("RewindInterval", gopts.rewind_interval);
getsc("Throttle", throttle);
getsc_uint("Throttle", throttle);
throttle_ctrl.thr = sc;
throttle_ctrl.thrsel = SafeXRCCTRL<wxChoice>(d, "ThrottleSel");
throttle_ctrl.thr->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,
......@@ -3228,6 +3283,35 @@ bool MainFrame::BindControls()
NULL, &throttle_ctrl);
d->Fit();
}
// SpeedUp Key Config
d = LoadXRCDialog("SpeedupConfig");
{
getsc_uint("SpeedupThrottle", speedup_throttle);
speedup_throttle_ctrl.speedup_throttle_spin = sc;
speedup_throttle_ctrl.speedup_throttle_sel = SafeXRCCTRL<wxChoice>(d, "SpeedupThrottleSel");
speedup_throttle_ctrl.speedup_throttle_spin->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,
wxSpinEventHandler(SpeedupThrottleCtrl_t::SetSpeedupThrottleSel),
NULL, &speedup_throttle_ctrl);
speedup_throttle_ctrl.speedup_throttle_sel->Connect(wxEVT_COMMAND_CHOICE_SELECTED,
wxCommandEventHandler(SpeedupThrottleCtrl_t::SetSpeedupThrottle),
NULL, &speedup_throttle_ctrl);
d->Connect(wxEVT_SHOW, wxShowEventHandler(SpeedupThrottleCtrl_t::Init),
NULL, &speedup_throttle_ctrl);
d->Fit();
getsc_uint("SpeedupFrameSkip", speedup_frame_skip);
speedup_frame_skip_ctrl.speedup_frame_skip_spin = sc;
speedup_frame_skip_ctrl.speedup_frame_skip_sel = SafeXRCCTRL<wxChoice>(d, "SpeedupFrameSkipSel");
speedup_frame_skip_ctrl.speedup_frame_skip_spin->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,
wxSpinEventHandler(SpeedupFrameSkipCtrl_t::SetSpeedupFrameSkipSel),
NULL, &speedup_frame_skip_ctrl);
speedup_frame_skip_ctrl.speedup_frame_skip_sel->Connect(wxEVT_COMMAND_CHOICE_SELECTED,
wxCommandEventHandler(SpeedupFrameSkipCtrl_t::SetSpeedupFrameSkip),
NULL, &speedup_frame_skip_ctrl);
d->Connect(wxEVT_SHOW, wxShowEventHandler(SpeedupFrameSkipCtrl_t::Init),
NULL, &speedup_frame_skip_ctrl);
d->Fit();
}
#define getcbbe(n, o) getbe(n, o, cb, wxCheckBox, CB)
wxBoolIntEnValidator* bienval;
#define getbie(n, o, v, cv, t, wt) \
......
......@@ -26,6 +26,10 @@
{ \
wxT(c), (n), d, NULL, NULL, wxT(""), min, max, NULL, &v \
}
#define UINTOPT(c, n, d, v, min, max) \
{ \
wxT(c), (n), d, NULL, NULL, wxT(""), min, max, NULL, NULL, &v \
}
#define BOOLOPT(c, n, d, v) \
{ \
wxT(c), (n), d, NULL, NULL, wxT(""), 0, 0, &v \
......@@ -252,7 +256,9 @@ opt_desc opts[] = {
INTOPT("preferences/skipBios", "SkipIntro", wxTRANSLATE("Skip BIOS initialization"), skipBios, 0, 1),
INTOPT("preferences/skipSaveGameCheats", "", wxTRANSLATE("Do not overwrite cheat list when loading state"), skipSaveGameCheats, 0, 1),
INTOPT("preferences/skipSaveGameBattery", "", wxTRANSLATE("Do not overwrite native (battery) save when loading state"), skipSaveGameBattery, 0, 1),
INTOPT("preferences/throttle", "", wxTRANSLATE("Throttle game speed, even when accelerated (0-1000%, 0 = disabled)"), throttle, 0, 1000),
UINTOPT("preferences/throttle", "", wxTRANSLATE("Throttle game speed, even when accelerated (0-500%, 0 = no throttle)"), throttle, 0, 600),
UINTOPT("preferences/speedupThrottle", "", wxTRANSLATE("Set throttle for speedup key (0-600%, 0 = no throttle)"), speedup_throttle, 0, 600),
UINTOPT("preferences/speedupFrameSkip", "", wxTRANSLATE("Set frame skip for speedup key (0-30)"), speedup_frame_skip, 0, 30),
INTOPT("preferences/useBiosGB", "BootRomGB", wxTRANSLATE("Use the specified BIOS file for GB"), useBiosFileGB, 0, 1),
INTOPT("preferences/useBiosGBA", "BootRomEn", wxTRANSLATE("Use the specified BIOS file"), useBiosFileGBA, 0, 1),
INTOPT("preferences/useBiosGBC", "BootRomGBC", wxTRANSLATE("Use the specified BIOS file for GBC"), useBiosFileGBC, 0, 1),
......@@ -525,6 +531,15 @@ void load_opts()
wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), opt.curdouble, opt.opt, opt.min, opt.max);
} else
*opt.doubleopt = opt.curdouble;
} else if (opt.uintopt) {
int val;
cfg->Read(opt.opt, &val, *opt.uintopt);
opt.curuint = val;
if (opt.curuint < opt.min || opt.curuint > opt.max) {
wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), opt.curuint, opt.opt, opt.min, opt.max);
} else
*opt.uintopt = opt.curuint;
} else if (opt.boolopt) {
cfg->Read(opt.opt, opt.boolopt, *opt.boolopt);
opt.curbool = *opt.boolopt;
......@@ -648,6 +663,9 @@ void update_opts()
} else if (opt.doubleopt) {
if (*opt.doubleopt != opt.curdouble)
cfg->Write(opt.opt, (opt.curdouble = *opt.doubleopt));
} else if (opt.uintopt) {
if (*opt.uintopt != opt.curuint)
cfg->Write(opt.opt, (opt.curuint = *opt.uintopt));
} else if (opt.boolopt) {
if (*opt.boolopt != opt.curbool)
cfg->Write(opt.opt, (opt.curbool = *opt.boolopt));
......@@ -802,6 +820,14 @@ bool opt_set(const wxString& name, const wxString& val)
wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), dval, name, opt->min, opt->max);
else
*opt->doubleopt = dval;
} else if (opt->uintopt) {
const wxString s(val);
unsigned long uival;
if (!s.ToULong(&uival) || uival < opt->min || uival > opt->max)
wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), uival, name, opt->min, opt->max);
else
*opt->uintopt = (uint32_t)uival;
} else {
// GB/Palette[0-2] is virtual
for (int i = 0; i < 3; i++) {
......
......@@ -93,10 +93,12 @@ extern struct opt_desc {
double min, max;
bool* boolopt;
double* doubleopt;
uint32_t* uintopt;
// current configured value
wxString curstr;
int curint;
double curdouble;
uint32_t curuint;
#define curbool curint
} opts[];
extern const int num_opts;
......
......@@ -72,6 +72,17 @@ protected:
wxString str_val;
};
class wxUIntValidator : public wxValidator {
public:
wxUIntValidator(uint32_t* _val);
bool TransferToWindow();
bool TransferFromWindow();
bool Validate(wxWindow* parent);
wxObject* Clone() const;
protected:
uint32_t* uint_val;
};
// boolean copy-only validator with reversed value
// may be attached to radio button or checkbox
class wxBoolRevValidator : public wxValidator {
......
// utility widgets
#include "wx/wxmisc.h"
#include <wx/wx.h>
#include <wx/spinctrl.h>
wxFarRadio::wxFarRadio()
: wxCheckBox()
......@@ -429,3 +430,50 @@ wxObject* wxPositiveDoubleValidator::Clone() const
{
return new wxPositiveDoubleValidator(double_val);
}
wxUIntValidator::wxUIntValidator(uint32_t* _val)
: uint_val(_val)
{
if (uint_val)
TransferToWindow();
}
bool wxUIntValidator::TransferToWindow()
{
wxSpinCtrl* ctrl = wxDynamicCast(GetWindow(), wxSpinCtrl);
if (ctrl && uint_val) {
ctrl->SetValue(*uint_val);
return true;
}
return false;
}
bool wxUIntValidator::TransferFromWindow()
{
wxSpinCtrl* ctrl = wxDynamicCast(GetWindow(), wxSpinCtrl);
if (ctrl && uint_val) {
*uint_val = ctrl->GetValue();
return true;
}
return false;
}
bool wxUIntValidator::Validate(wxWindow* parent)
{
wxSpinCtrl* ctrl = wxDynamicCast(GetWindow(), wxSpinCtrl);
if (ctrl) {
if (ctrl->GetValue() >= 0) {
return true;
}
return false;
}
return false;
}
wxObject* wxUIntValidator::Clone() const
{
return new wxUIntValidator(uint_val);
}
......@@ -149,14 +149,33 @@
<object class="sizeritem">
<object class="wxChoice" name="ThrottleSel">
<content>
<item/>
<item>No throttle</item>
<item>None</item>
<item>25%</item>
<item>50%</item>
<item>75%</item>
<item>100%</item>
<item>125%</item>
<item>150%</item>
<item>175%</item>
<