From ec5a3cdec580864503e1566f6b4aad958cef716a Mon Sep 17 00:00:00 2001 From: thrust26 Date: Wed, 6 Dec 2017 12:15:16 +0100 Subject: [PATCH] thumb.trapfatal added to developer settings dialog restructured developer settings dialog duplicated debug colors are changed automatically updated doc and screenshots accordingly --- docs/graphics/options_developer.png | Bin 6943 -> 6552 bytes docs/graphics/options_developer_dbgcolors.png | Bin 5169 -> 6698 bytes docs/graphics/options_developer_debugger.png | Bin 4795 -> 4732 bytes docs/graphics/options_developer_emulation.png | Bin 7011 -> 6623 bytes docs/graphics/options_developer_states.png | Bin 0 -> 5368 bytes docs/index.html | 188 ++++---- src/emucore/Settings.cxx | 30 +- src/gui/DeveloperDialog.cxx | 430 ++++++++++++------ src/gui/DeveloperDialog.hxx | 69 +-- 9 files changed, 457 insertions(+), 260 deletions(-) create mode 100644 docs/graphics/options_developer_states.png diff --git a/docs/graphics/options_developer.png b/docs/graphics/options_developer.png index bed6c7229de8a46e3e7c55da8bd7e4be8a9e6b05..b8ad043ad35eea82283edc65badd36424ba682c6 100644 GIT binary patch literal 6552 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 Ay8r+H literal 6943 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 QU&r+N=S_6q=v)u|4_`#a!~g&Q literal 5169 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 delta 3422 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 diff --git a/docs/graphics/options_developer_emulation.png b/docs/graphics/options_developer_emulation.png index 9ca4eef674c940899ee31a393315a33bc9c91579..483583dc15682deb3d6cf8dd717a10b11eb1ff74 100644 GIT binary patch literal 6623 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 diff --git a/docs/graphics/options_developer_states.png b/docs/graphics/options_developer_states.png new file mode 100644 index 0000000000000000000000000000000000000000..3304e8c0cb335f1e174547a40da50f6fba8881ab GIT binary patch literal 5368 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 hn)$H literal 0 HcmV?d00001 diff --git a/docs/index.html b/docs/index.html index e553353b7..8bba4f505 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2421,15 +2421,6 @@ Set "Display.PPBlend" property, used for phosphor effect (0-100). Default is whatever is specified for tv.phosblend. - - -
-thumb.trapfatal <1|0>
- The default of true allows the Thumb ARM emulation to - throw an exception and enter the debugger on fatal errors. When disabled, such - fatal errors are simply logged, and emulation continues. Do not use this - unless you know exactly what you're doing, as it changes the behaviour as compared - to real hardware. -

The following are available in two sets, one for players (prefixed by "plr.") and one @@ -2440,64 +2431,67 @@ Argument Description - - - +

-dev.settings <1|0>
Select developer (1) or player (0) set. - - - +
-<plr.|dev.>stats <1|0>
Overlay console info on the TIA image during emulation. - - - +
-<plr.|dev.>console <2600|7800>
Select console for B/W and Pause key handling and RAM initialization. - - - +
-<plr.|dev.>bankrandom <1|0>
On reset, randomize the startup bank (only for selected bankswitch types). - - - +
-<plr.|dev.>ramrandom <1|0>
On reset, either randomize all RAM content, or initialize with zero (console = 2600)/startup values (console = 7800) instead. - - - +
-<plr.|dev.>cpurandom <S,A,X,Y,P>
On reset, randomize the content of the specified CPU registers. - - - -
-<plr.|dev.>debugcolors <1|0>
- Enable/disable the fixed debug colors. - - - -
-<plr.|dev.>colorloss <1|0>
- Enable/disable the PAL color-loss effect. - - - -
-<plr.|dev.>tv.jitter <1|0>
- Enable TV jitter/roll effect, when there are too many or too few scanlines - per frame. - - - -
-<plr.|dev.>tv.jitter_recovery <1 - 20>
- When TV jitter/roll effect is enabled, determines how long to delay recovery - time (recovery spread over multiple frames). - - - +
-<plr.|dev.>tiadriven <1|0>
Set unused TIA pins to be randomly driven high or low on a read/peek. If disabled, use the last databus value for those pins instead. + +
-<plr.|dev.>thumb.trapfatal <1|0>
+ The default of true allows the Thumb ARM emulation to + throw an exception and enter the debugger on fatal errors. When disabled, such + fatal errors are simply logged, and emulation continues. Do not use this + unless you know exactly what you're doing, as it changes the behaviour as compared + to real hardware. + +
-<plr.|dev.>tv.jitter <1|0>
+ Enable TV jitter/roll effect, when there are too many or too few scanlines + per frame. + +
-<plr.|dev.>tv.jitter_recovery <1 - 20>
+ When TV jitter/roll effect is enabled, determines how long to delay recovery + time (recovery spread over multiple frames). + +
-<plr.|dev.>colorloss <1|0>
+ Enable/disable the PAL color-loss effect. + +
-<plr.|dev.>debugcolors <1|0>
+ Enable/disable the fixed debug colors. + +
-<plr.|dev.>rewind <1|0>
+ Enables continuous rewind + +
-<plr.|dev.>rewind.size <100 - 1000>
+ Defines the rewind buffer size. + + +
-<plr.|dev.>rewind.uncompressed <100 - 1000>
+ Defines the uncompressed rewind buffer size. Must be <= rewind buffer size. + + +
-<plr.|dev.>rewind.interval <0 - 5>
+ Defines the interval between two save states. + + +
-<plr.|dev.>rewind.horizon <0 - 6>
+ Defines the horizon of the rewind buffer. @@ -3023,11 +3017,28 @@ Random startup bankRandomize the startup bank (only for selected bankswitch types)-plr.bankrandom
-dev.bankrandom Randomize zero-page ...When loading a ROM, randomize all RAM content instead of initializing with all zeroes (for 'Console' = 'Atari 2600' only)-plr.ramrandom
-dev.ramrandom Randomize CPUWhen loading a ROM, randomize the content of the specified CPU registers-plr.cpurandom
-dev.cpurandom + Drive unused TIA pins ...Unused TIA pins are read random instead of the last databus values-plr.tiadriven
-dev.tiadriven + Fatal ARM emulation ...Thumb ARM emulation throws an exception and enters the debugger on fatal errors.-plr.thumb.trapfatal
-dev.thumb.trapfatal
+ + + + + +

Developer Settings dialog (Video):

+ + + + + @@ -3036,39 +3047,56 @@

Developer Settings dialog (States) TODO

     + + + + - - - + +
ItemBrief descriptionFor more information,
see CommandLine
Jitter/roll effectEmulate screen roll with inconsistent scanline count-plr.tv.jitter
-dev.tv.jitter
(Jitter/roll) RecoveryDetermines recovery time for screen rolling-plr.tv.jitter_recovery
-dev.tv.jitter_recovery
PAL color-lossUse PAL color-loss effect-plr.colorloss
-dev.colorloss
Debug colorsUse fixed debug colors-plr.debugcolors
-dev.debugcolors
Jitter/roll effectEmulate screen roll with inconsistent scanline count-plr.tv.jitter
-dev.tv.jitter
(Jitter/roll) RecoveryDetermines recovery time for screen rolling-plr.tv.jitter_recovery
-dev.tv.jitter_recovery
Drive unused TIA pins ...Unused TIA pins are read random instead of the last databus values-plr.tiadriven
-dev.tiadriven
Player 0
Missile 0
Player 1
+ Missile 1
Playfield
Ball
Set color for specific object in debug colors mode
(PF0, PF1 and PF2 are have slightly different luminance,
disabled in ROM launcher mode)
-tia.dbgcolors
- +
     - - - - - - + + + + + + + + + + + + + + + + + + + + + + +
ItemBrief descriptionFor more information,
see CommandLine
-.plr
-.dev
-.plr
-.dev
-.plr
-.dev
-.plr
-.dev
-.plr
-.dev
ItemBrief descriptionFor more information,
see CommandLine
Continuous rewind + When continuous rewind is enabled, Stella will automatically + create new save states in the interval described below.
+ Note: This buffer is identical with the one described in + Debugger/Global Buttons. + It is independent from the save states manually created + with F9. +
-plr.rewind
-dev.rewind
Buffer size + Defines the rewind buffer size. The larger the buffer, the less + save states have to be compressed to reach the horizon. + -plr.rewind.size
-dev.rewind.size
Uncompressed size + Defines the uncompressed rewind buffer size. States within this + area will not be compressed and keep their initial interval.-plr.rewind.uncompressed
-dev.rewind.uncompressed
IntervalDefines the interval between two save states when they are created.-plr.rewind.interval
-dev.rewind.interval
Horizon + Defines the horizon of the rewind buffer. A large horizon allows + going back further in time. To reach the horizon, save states + will be compressed. This means that more and more intermediate + states will be removed and the interval between compressed states + becomes larger the further they are back in time. + -plr.rewind.horizon
-dev.rewind.horizon
-

Developer Settings dialog (Debug Colors):

- - - - - - -
     - - - - -
ItemBrief descriptionFor more information,
see CommandLine
Player 0
Missile 0
Player 1
- Missile 1
Playfield
Ball
Set color for specific object in debug colors mode
(PF0, PF1 and PF2 are have slightly different luminance,
disabled in ROM launcher mode)
-tia.dbgcolors
-
-
- -

Developer Settings dialog (Debugger)

diff --git a/src/emucore/Settings.cxx b/src/emucore/Settings.cxx index 51a5a237f..c2dbd6e83 100644 --- a/src/emucore/Settings.cxx +++ b/src/emucore/Settings.cxx @@ -157,8 +157,13 @@ Settings::Settings(OSystem& osystem) setInternal("plr.console", "2600"); // 7800 setInternal("plr.rewind", false); setInternal("plr.rewind.size", 100); + setInternal("plr.rewind.uncompressed", 30); setInternal("plr.rewind.interval", 4); // = 1 second setInternal("plr.rewind.horizon", 5); // = ~10 minutes +#ifdef DTHUMB_SUPPORT + // Thumb ARM emulation options + setInternal("plr.thumb.trapfatal", "false"); +#endif // developer settings setInternal("dev.settings", "false"); @@ -174,12 +179,13 @@ Settings::Settings(OSystem& osystem) setInternal("dev.console", "2600"); // 7800 setInternal("dev.rewind", true); setInternal("dev.rewind.size", 100); + setInternal("dev.rewind.uncompressed", 60); setInternal("dev.rewind.interval", 2); // = 1 frame setInternal("dev.rewind.horizon", 3); // = ~10 seconds #ifdef DTHUMB_SUPPORT // Thumb ARM emulation options - setInternal("thumb.trapfatal", "true"); + setInternal("dev.thumb.trapfatal", "true"); #endif } @@ -318,8 +324,15 @@ void Settings::validate() i = getInt("dev.tv.jitter_recovery"); if(i < 1 || i > 20) setInternal("dev.tv.jitter_recovery", "2"); - i = getInt("dev.rewind.size"); - if (i < 100 ||i > 1000) setInternal("dev.rewind.size", 100); + int size = getInt("dev.rewind.size"); + if(size < 100 || size > 1000) + { + setInternal("dev.rewind.size", 100); + size = 100; + } + + i = getInt("dev.rewind.uncompressed"); + if(i < 0 || i > size) setInternal("dev.rewind.uncompressed", size); i = getInt("dev.rewind.interval"); if(i < 0 || i > 5) setInternal("dev.rewind.interval", 2); @@ -330,8 +343,15 @@ void Settings::validate() i = getInt("plr.tv.jitter_recovery"); if(i < 1 || i > 20) setInternal("plr.tv.jitter_recovery", "10"); - i = getInt("plr.rewind.size"); - if(i < 100 || i > 1000) setInternal("plr.rewind.size", 100); + size = getInt("plr.rewind.size"); + if(size < 100 || size > 1000) + { + setInternal("plr.rewind.size", 100); + size = 100; + } + + i = getInt("plr.rewind.uncompressed"); + if(i < 0 || i > size) setInternal("plr.rewind.uncompressed", size); i = getInt("plr.rewind.interval"); if(i < 0 || i > 5) setInternal("plr.rewind.interval", 4); diff --git a/src/gui/DeveloperDialog.cxx b/src/gui/DeveloperDialog.cxx index 231bfdc3c..1f2498465 100644 --- a/src/gui/DeveloperDialog.cxx +++ b/src/gui/DeveloperDialog.cxx @@ -47,6 +47,7 @@ DeveloperDialog::DeveloperDialog(OSystem& osystem, DialogContainer& parent, myMaxWidth(max_w), myMaxHeight(max_h) { + const int VGAP = 4; const int lineHeight = font.getLineHeight(), fontWidth = font.getMaxCharWidth(), buttonHeight = font.getLineHeight() + 4; @@ -54,7 +55,7 @@ DeveloperDialog::DeveloperDialog(OSystem& osystem, DialogContainer& parent, // Set real dimensions _w = std::min(53 * fontWidth + 10, max_w); - _h = std::min(15 * (lineHeight + 4) + 14, max_h); + _h = std::min(15 * (lineHeight + VGAP) + 14, max_h); // The tab widget xpos = 2; ypos = 4; @@ -62,8 +63,8 @@ DeveloperDialog::DeveloperDialog(OSystem& osystem, DialogContainer& parent, addTabWidget(myTab); addEmulationTab(font); + addVideoTab(font); addStatesTab(font); - addDebugColorsTab(font); addDebuggerTab(font); addDefaultOKCancelButtons(font); @@ -83,7 +84,7 @@ void DeveloperDialog::addEmulationTab(const GUI::Font& font) int fontWidth = font.getMaxCharWidth(), fontHeight = font.getFontHeight(); WidgetArray wid; VariantList items; - int tabID = myTab->addTab("Emulation"); + int tabID = myTab->addTab(" Emulation "); // settings set mySettingsGroup0 = new RadioButtonGroup(); @@ -92,7 +93,7 @@ void DeveloperDialog::addEmulationTab(const GUI::Font& font) ypos += lineHeight + VGAP; r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1, "Developer settings", mySettingsGroup0, kDevSettings); wid.push_back(r); - ypos += lineHeight + VGAP * 2; + ypos += lineHeight + VGAP * 1; myFrameStatsWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1, "Frame statistics"); wid.push_back(myFrameStatsWidget); @@ -105,22 +106,24 @@ void DeveloperDialog::addEmulationTab(const GUI::Font& font) int lwidth = font.getStringWidth("Console "); int pwidth = font.getStringWidth("Atari 2600"); - myConsoleWidget = new PopUpWidget(myTab, font, HBORDER + INDENT * 1, ypos, pwidth, lineHeight, items, "Console ", lwidth, kConsole); + myConsoleWidget = new PopUpWidget(myTab, font, HBORDER + INDENT * 1, ypos, pwidth, lineHeight, items, + "Console ", lwidth, kConsole); wid.push_back(myConsoleWidget); ypos += lineHeight + VGAP; // Randomize items - myLoadingROMLabel = new StaticTextWidget(myTab, font, HBORDER + INDENT*1, ypos + 1, "When loading a ROM:", kTextAlignLeft); + myLoadingROMLabel = new StaticTextWidget(myTab, font, HBORDER + INDENT*1, ypos + 1, "When loading a ROM:"); wid.push_back(myLoadingROMLabel); ypos += lineHeight + VGAP; - myRandomBankWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 2, ypos + 1, "Random startup bank"); + myRandomBankWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 2, ypos + 1, + "Random startup bank"); wid.push_back(myRandomBankWidget); ypos += lineHeight + VGAP; // Randomize RAM myRandomizeRAMWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 2, ypos + 1, - "Randomize zero-page and extended RAM", kRandRAMID); + "Randomize zero-page and extended RAM", kRandRAMID); wid.push_back(myRandomizeRAMWidget); ypos += lineHeight + VGAP; @@ -140,37 +143,119 @@ void DeveloperDialog::addEmulationTab(const GUI::Font& font) } ypos += lineHeight + VGAP; - // debug colors - myDebugColorsWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1, "Debug colors"); - wid.push_back(myDebugColorsWidget); + + // How to handle undriven TIA pins + myUndrivenPinsWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1, + "Drive unused TIA pins randomly on a read/peek"); + wid.push_back(myUndrivenPinsWidget); + ypos += lineHeight + VGAP; + +#ifdef DTHUMB_SUPPORT + // Thumb ARM emulation exception + myThumbExceptionWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1, + "Fatal ARM emulation error throws exception"); + wid.push_back(myThumbExceptionWidget); +#endif + + // Add items for tab 0 + addToFocusList(wid, myTab, tabID); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void DeveloperDialog::addVideoTab(const GUI::Font& font) +{ + const int HBORDER = 10; + const int INDENT = 16 + 4; + const int VBORDER = 8; + const int VGAP = 4; + int ypos = VBORDER; + int lineHeight = font.getLineHeight(); + int fontWidth = font.getMaxCharWidth(), fontHeight = font.getFontHeight(); + int lwidth = font.getStringWidth("Intensity "); + int pwidth = font.getMaxCharWidth() * 6; + WidgetArray wid; + VariantList items; + int tabID = myTab->addTab("Video"); + + wid.clear(); + ypos = VBORDER; + + // settings set + mySettingsGroup1 = new RadioButtonGroup(); + RadioButtonWidget* r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1, "Player settings", mySettingsGroup1, kPlrSettings); + wid.push_back(r); + ypos += lineHeight + VGAP; + r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1, "Developer settings", mySettingsGroup1, kDevSettings); + wid.push_back(r); + ypos += lineHeight + VGAP * 1; + + // TV jitter effect + myTVJitterWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1, "Jitter/roll effect", kTVJitter); + wid.push_back(myTVJitterWidget); + myTVJitterRecWidget = new SliderWidget(myTab, font, + myTVJitterWidget->getRight() + fontWidth * 3, ypos - 1, + 8 * fontWidth, lineHeight, "Recovery ", + font.getStringWidth("Recovery "), kTVJitterChanged); + myTVJitterRecWidget->setMinValue(1); myTVJitterRecWidget->setMaxValue(20); + wid.push_back(myTVJitterRecWidget); + myTVJitterRecLabelWidget = new StaticTextWidget(myTab, font, + myTVJitterRecWidget->getRight() + 4, myTVJitterRecWidget->getTop() + 2, + 5 * fontWidth, fontHeight, "", kTextAlignLeft); + myTVJitterRecLabelWidget->setFlags(WIDGET_CLEARBG); + wid.push_back(myTVJitterRecLabelWidget); ypos += lineHeight + VGAP; myColorLossWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1, "PAL color-loss"); wid.push_back(myColorLossWidget); ypos += lineHeight + VGAP; - // TV jitter effect - myTVJitterWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1, "Jitter/roll effect", kTVJitter); - wid.push_back(myTVJitterWidget); - myTVJitterRecWidget = new SliderWidget(myTab, font, - myTVJitterWidget->getRight() + fontWidth * 3, ypos - 1, - 8 * fontWidth, lineHeight, "Recovery ", - font.getStringWidth("Recovery "), kTVJitterChanged); - myTVJitterRecWidget->setMinValue(1); myTVJitterRecWidget->setMaxValue(20); - wid.push_back(myTVJitterRecWidget); - myTVJitterRecLabelWidget = new StaticTextWidget(myTab, font, - myTVJitterRecWidget->getRight() + 4, myTVJitterRecWidget->getTop() + 2, - 5 * fontWidth, fontHeight, "", kTextAlignLeft); - myTVJitterRecLabelWidget->setFlags(WIDGET_CLEARBG); - wid.push_back(myTVJitterRecLabelWidget); - ypos += lineHeight + VGAP; + // debug colors + myDebugColorsWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1, "Debug colors (*)"); + wid.push_back(myDebugColorsWidget); + ypos += lineHeight + VGAP + 2; - // How to handle undriven TIA pins - myUndrivenPinsWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1, - "Drive unused TIA pins randomly on a read/peek"); - wid.push_back(myUndrivenPinsWidget); + //StaticTextWidget* s = new StaticTextWidget(myTab, font, HBORDER, ypos, "Debug Colors "); + //ypos += lineHeight + VGAP; - // Add items for tab 0 + items.clear(); + VarList::push_back(items, "Red", "r"); + VarList::push_back(items, "Orange", "o"); + VarList::push_back(items, "Yellow", "y"); + VarList::push_back(items, "Green", "g"); + VarList::push_back(items, "Purple", "p"); + VarList::push_back(items, "Blue", "b"); + + static constexpr int dbg_cmds[DEBUG_COLORS] = { + kP0ColourChangedCmd, kM0ColourChangedCmd, kP1ColourChangedCmd, + kM1ColourChangedCmd, kPFColourChangedCmd, kBLColourChangedCmd + }; + + auto createDebugColourWidgets = [&](int idx, const string& desc) + { + int x = HBORDER + INDENT * 1; + myDbgColour[idx] = new PopUpWidget(myTab, font, x, ypos - 1, + pwidth, lineHeight, items, desc, lwidth, dbg_cmds[idx]); + wid.push_back(myDbgColour[idx]); + x += myDbgColour[idx]->getWidth() + 10; + myDbgColourSwatch[idx] = new ColorWidget(myTab, font, x, ypos - 1, + uInt32(2 * lineHeight), lineHeight); + ypos += lineHeight + VGAP * 1; + }; + + createDebugColourWidgets(0, "Player 0 "); + createDebugColourWidgets(1, "Missile 0 "); + createDebugColourWidgets(2, "Player 1 "); + createDebugColourWidgets(3, "Missile 1 "); + createDebugColourWidgets(4, "Playfield "); + createDebugColourWidgets(5, "Ball "); + + // Add message concerning usage + const GUI::Font& infofont = instance().frameBuffer().infoFont(); + ypos = myTab->getHeight() - 5 - fontHeight - infofont.getFontHeight() - 10; + //new StaticTextWidget(myTab, infofont, 10, ypos, "(*) Colors must be different for each object"); + new StaticTextWidget(myTab, infofont, HBORDER, ypos, "(*) colors identical for player and developer settings"); + + // Add items for tab 2 addToFocusList(wid, myTab, tabID); } @@ -188,13 +273,13 @@ void DeveloperDialog::addStatesTab(const GUI::Font& font) int tabID = myTab->addTab("States"); // settings set - mySettingsGroup1 = new RadioButtonGroup(); - RadioButtonWidget* r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1, "Player settings", mySettingsGroup1, kPlrSettings); + mySettingsGroup2 = new RadioButtonGroup(); + RadioButtonWidget* r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1, "Player settings", mySettingsGroup2, kPlrSettings); wid.push_back(r); ypos += lineHeight + VGAP; - r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1, "Developer settings", mySettingsGroup1, kDevSettings); + r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1, "Developer settings", mySettingsGroup2, kDevSettings); wid.push_back(r); - ypos += lineHeight + VGAP * 2; + ypos += lineHeight + VGAP * 1; myContinuousRewindWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT, ypos + 1, "Continuous rewind", kRewind); wid.push_back(myContinuousRewindWidget); @@ -202,29 +287,42 @@ void DeveloperDialog::addStatesTab(const GUI::Font& font) int sWidth = font.getMaxCharWidth() * 8; myStateSizeWidget = new SliderWidget(myTab, font, HBORDER + INDENT * 2, ypos - 1, sWidth, lineHeight, - "Buffer size (*) ", 0, kSizeChanged); + "Buffer size (*) ", 0, kSizeChanged); myStateSizeWidget->setMinValue(100); myStateSizeWidget->setMaxValue(1000); - myStateSizeWidget->setStepValue(100); + myStateSizeWidget->setStepValue(20); wid.push_back(myStateSizeWidget); - myStateSizeLabelWidget = new StaticTextWidget(myTab, font, myStateSizeWidget->getRight() + 4, myStateSizeWidget->getTop() + 2, "100 "); - + myStateSizeLabelWidget = new StaticTextWidget(myTab, font, myStateSizeWidget->getRight() + 4, + myStateSizeWidget->getTop() + 2, "100 "); ypos += lineHeight + VGAP; + + myUncompressedWidget = new SliderWidget(myTab, font, HBORDER + INDENT * 2, ypos - 1, sWidth, lineHeight, + "Uncompressed size (*) ", 0, kUncompressedChanged); + myUncompressedWidget->setMinValue(0); + myUncompressedWidget->setMaxValue(1000); + myUncompressedWidget->setStepValue(20); + wid.push_back(myUncompressedWidget); + myUncompressedLabelWidget = new StaticTextWidget(myTab, font, myUncompressedWidget->getRight() + 4, + myUncompressedWidget->getTop() + 2, "50 "); + ypos += lineHeight + VGAP; + myStateIntervalWidget = new SliderWidget(myTab, font, HBORDER + INDENT * 2, ypos - 1, sWidth, lineHeight, - "Interval ", 0, kIntervalChanged); + "Interval ", 0, kIntervalChanged); myStateIntervalWidget->setMinValue(0); myStateIntervalWidget->setMaxValue(NUM_INTERVALS - 1); wid.push_back(myStateIntervalWidget); - myStateIntervalLabelWidget = new StaticTextWidget(myTab, font, myStateIntervalWidget->getRight() + 4, myStateIntervalWidget->getTop() + 2, "50 scanlines"); - + myStateIntervalLabelWidget = new StaticTextWidget(myTab, font, myStateIntervalWidget->getRight() + 4, + myStateIntervalWidget->getTop() + 2, "50 scanlines"); ypos += lineHeight + VGAP; + myStateHorizonWidget = new SliderWidget(myTab, font, HBORDER + INDENT * 2, ypos - 1, sWidth, lineHeight, - "Horizon ", 0, kHorizonChanged); + "Horizon ", 0, kHorizonChanged); myStateHorizonWidget->setMinValue(0); myStateHorizonWidget->setMaxValue(NUM_HORIZONS - 1); wid.push_back(myStateHorizonWidget); - myStateHorizonLabelWidget = new StaticTextWidget(myTab, font, myStateHorizonWidget->getRight() + 4, myStateHorizonWidget->getTop() + 2, "~60 minutes"); + myStateHorizonLabelWidget = new StaticTextWidget(myTab, font, myStateHorizonWidget->getRight() + 4, + myStateHorizonWidget->getTop() + 2, "~60 minutes"); // Add message concerning usage const GUI::Font& infofont = instance().frameBuffer().infoFont(); @@ -234,67 +332,6 @@ void DeveloperDialog::addStatesTab(const GUI::Font& font) addToFocusList(wid, myTab, tabID); } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void DeveloperDialog::addDebugColorsTab(const GUI::Font& font) -{ - const int HBORDER = 10; - const int INDENT = 16 + 4; - const int VBORDER = 8; - const int VGAP = 4; - int ypos = VBORDER; - int lineHeight = font.getLineHeight(); - int fontWidth = font.getMaxCharWidth(), fontHeight = font.getFontHeight(); - int lwidth = font.getStringWidth("Intensity "); - int pwidth = font.getMaxCharWidth() * 6; - WidgetArray wid; - VariantList items; - int tabID = myTab->addTab("Debug Colors"); - - wid.clear(); - ypos = VBORDER; - - items.clear(); - VarList::push_back(items, "Red", "r"); - VarList::push_back(items, "Orange", "o"); - VarList::push_back(items, "Yellow", "y"); - VarList::push_back(items, "Green", "g"); - VarList::push_back(items, "Blue", "b"); - VarList::push_back(items, "Purple", "p"); - - static constexpr int dbg_cmds[6] = { - kP0ColourChangedCmd, kM0ColourChangedCmd, kP1ColourChangedCmd, - kM1ColourChangedCmd, kPFColourChangedCmd, kBLColourChangedCmd - }; - - auto createDebugColourWidgets = [&](int idx, const string& desc) - { - int x = HBORDER; - myDbgColour[idx] = new PopUpWidget(myTab, font, x, ypos, - pwidth, lineHeight, items, desc, lwidth, dbg_cmds[idx]); - wid.push_back(myDbgColour[idx]); - x += myDbgColour[idx]->getWidth() + 10; - myDbgColourSwatch[idx] = new ColorWidget(myTab, font, x, ypos, - uInt32(2 * lineHeight), lineHeight); - ypos += lineHeight + VGAP * 1; - }; - - createDebugColourWidgets(0, "Player 0 "); - createDebugColourWidgets(1, "Missile 0 "); - createDebugColourWidgets(2, "Player 1 "); - createDebugColourWidgets(3, "Missile 1 "); - createDebugColourWidgets(4, "Playfield "); - createDebugColourWidgets(5, "Ball "); - - // Add message concerning usage - const GUI::Font& infofont = instance().frameBuffer().infoFont(); - ypos = myTab->getHeight() - 5 - fontHeight - infofont.getFontHeight() - 10; - new StaticTextWidget(myTab, infofont, 10, ypos, "(*) Colors must be different for each object"); - - // Add items for tab 2 - addToFocusList(wid, myTab, tabID); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void DeveloperDialog::addDebuggerTab(const GUI::Font& font) { @@ -429,6 +466,11 @@ void DeveloperDialog::loadSettings(SettingsSet set) myRandomBank[set] = instance().settings().getBool(prefix + "bankrandom"); myRandomizeRAM[set] = instance().settings().getBool(prefix + "ramrandom"); myRandomizeCPU[set] = instance().settings().getString(prefix + "cpurandom"); + // Undriven TIA pins + myUndrivenPins[set] = instance().settings().getBool(prefix + "tiadriven"); + // Thumb ARM emulation exception + myThumbException[set] = instance().settings().getBool(prefix + "thumb.trapfatal"); + // Debug colors myDebugColors[set] = instance().settings().getBool(prefix + "debugcolors"); // PAL color-loss effect @@ -436,12 +478,11 @@ void DeveloperDialog::loadSettings(SettingsSet set) // Jitter myTVJitter[set] = instance().settings().getBool(prefix + "tv.jitter"); myTVJitterRec[set] = instance().settings().getInt(prefix + "tv.jitter_recovery"); - // Undriven TIA pins - myUndrivenPins[set] = instance().settings().getBool(prefix + "tiadriven"); // States myContinuousRewind[set] = instance().settings().getBool(prefix + "rewind"); myStateSize[set] = instance().settings().getInt(prefix + "rewind.size"); + myUncompressed[set] = instance().settings().getInt(prefix + "rewind.uncompressed"); myStateInterval[set] = instance().settings().getInt(prefix + "rewind.interval"); myStateHorizon[set] = instance().settings().getInt(prefix + "rewind.horizon"); } @@ -457,6 +498,11 @@ void DeveloperDialog::saveSettings(SettingsSet set) instance().settings().setValue(prefix + "bankrandom", myRandomBank[set]); instance().settings().setValue(prefix + "ramrandom", myRandomizeRAM[set]); instance().settings().setValue(prefix + "cpurandom", myRandomizeCPU[set]); + // Undriven TIA pins + instance().settings().setValue(prefix + "tiadriven", myUndrivenPins[set]); + // Thumb ARM emulation exception + instance().settings().setValue(prefix + "thumb.trapfatal", myThumbException[set]); + // Debug colors instance().settings().setValue(prefix + "debugcolors", myDebugColors[set]); // PAL color loss @@ -464,12 +510,11 @@ void DeveloperDialog::saveSettings(SettingsSet set) // Jitter instance().settings().setValue(prefix + "tv.jitter", myTVJitter[set]); instance().settings().setValue(prefix + "tv.jitter_recovery", myTVJitterRec[set]); - // Undriven TIA pins - instance().settings().setValue(prefix + "tiadriven", myUndrivenPins[set]); // States instance().settings().setValue(prefix + "rewind", myContinuousRewind[set]); instance().settings().setValue(prefix + "rewind.size", myStateSize[set]); + instance().settings().setValue(prefix + "rewind.uncompressed", myUncompressed[set]); instance().settings().setValue(prefix + "rewind.interval", myStateInterval[set]); instance().settings().setValue(prefix + "rewind.horizon", myStateHorizon[set]); } @@ -488,6 +533,11 @@ void DeveloperDialog::getWidgetStates(SettingsSet set) if(myRandomizeCPUWidget[i]->getState()) cpurandom += cpuregs[i]; myRandomizeCPU[set] = cpurandom; + // Undriven TIA pins + myUndrivenPins[set] = myUndrivenPinsWidget->getState(); + // Thumb ARM emulation exception + myThumbException[set] = myThumbExceptionWidget->getState(); + // Debug colors myDebugColors[set] = myDebugColorsWidget->getState(); // PAL color-loss effect @@ -495,12 +545,11 @@ void DeveloperDialog::getWidgetStates(SettingsSet set) // Jitter myTVJitter[set] = myTVJitterWidget->getState(); myTVJitterRec[set] = myTVJitterRecWidget->getValue(); - // Undriven TIA pins - myUndrivenPins[set] = myUndrivenPinsWidget->getState(); // States myContinuousRewind[set] = myContinuousRewindWidget->getState(); myStateSize[set] = myStateSizeWidget->getValue(); + myUncompressed[set] = myUncompressedWidget->getValue(); myStateInterval[set] = myStateIntervalWidget->getValue(); myStateHorizon[set] = myStateHorizonWidget->getValue(); } @@ -518,6 +567,13 @@ void DeveloperDialog::setWidgetStates(SettingsSet set) const char* const cpuregs[] = { "S", "A", "X", "Y", "P" }; for(int i = 0; i < 5; ++i) myRandomizeCPUWidget[i]->setState(BSPF::containsIgnoreCase(cpurandom, cpuregs[i])); + // Undriven TIA pins + myUndrivenPinsWidget->setState(myUndrivenPins[set]); + // Thumb ARM emulation exception + myThumbExceptionWidget->setState(myThumbException[set]); + + handleConsole(); + // Debug colors myDebugColorsWidget->setState(myDebugColors[set]); // PAL color-loss effect @@ -525,21 +581,20 @@ void DeveloperDialog::setWidgetStates(SettingsSet set) // Jitter myTVJitterWidget->setState(myTVJitter[set]); myTVJitterRecWidget->setValue(myTVJitterRec[set]); - // Undriven TIA pins - myUndrivenPinsWidget->setState(myUndrivenPins[set]); - handleConsole(); handleTVJitterChange(myTVJitterWidget->getState()); handleEnableDebugColors(); // States myContinuousRewindWidget->setState(myContinuousRewind[set]); myStateSizeWidget->setValue(myStateSize[set]); + myUncompressedWidget->setValue(myUncompressed[set]); myStateIntervalWidget->setValue(myStateInterval[set]); myStateHorizonWidget->setValue(myStateHorizon[set]); handleRewind(); handleSize(); + handleUncompressed(); handleInterval(); handleHorizon(); } @@ -551,6 +606,7 @@ void DeveloperDialog::loadConfig() mySettings = devSettings; mySettingsGroup0->setSelected(devSettings ? 1 : 0); mySettingsGroup1->setSelected(devSettings ? 1 : 0); + mySettingsGroup2->setSelected(devSettings ? 1 : 0); // load both setting sets... loadSettings(SettingsSet::player); @@ -612,7 +668,7 @@ void DeveloperDialog::saveConfig() // Debug colours string dbgcolors; - for(int i = 0; i < 6; ++i) + for(int i = 0; i < DEBUG_COLORS; ++i) dbgcolors += myDbgColour[i]->getSelectedTag().toString(); if(instance().hasConsole() && instance().console().tia().setFixedColorPalette(dbgcolors)) @@ -627,17 +683,18 @@ void DeveloperDialog::saveConfig() // define interval growth factor uInt32 size = myStateSizeWidget->getValue(); + uInt32 uncompressed = myUncompressedWidget->getValue(); uInt64 horizon = HORIZON_CYCLES[myStateHorizonWidget->getValue()]; double factor, minFactor = 1, maxFactor = 2; while(true) { double interval = INTERVAL_CYCLES[myStateIntervalWidget->getValue()]; - double cycleSum = 0.0; + double cycleSum = interval * uncompressed; // calculate next factor factor = (minFactor + maxFactor) / 2; // sum up interval cycles - for(uInt32 i = 0; i < size; ++i) + for(uInt32 i = uncompressed; i < size; ++i) { cycleSum += interval; interval *= factor; @@ -686,32 +743,37 @@ void DeveloperDialog::setDefaults() myRandomBank[set] = devSettings ? true : false; myRandomizeRAM[set] = devSettings ? true : false; myRandomizeCPU[set] = devSettings ? "SAXYP" : ""; - // Debug colors - myDebugColors[set] = false; - // PAL color-loss effect - myColorLoss[set] = devSettings ? true : false; - // Jitter - myTVJitter[set] = true; - myTVJitterRec[set] = devSettings ? 2 : 10; // Undriven TIA pins myUndrivenPins[set] = devSettings ? true : false; + // Thumb ARM emulation exception + myThumbException[set] = devSettings ? true : false; setWidgetStates(set); break; - case 1: // States + case 1: // Video + // Jitter + myTVJitter[set] = true; + myTVJitterRec[set] = devSettings ? 2 : 10; + // PAL color-loss effect + myColorLoss[set] = devSettings ? true : false; + // Debug colors + myDebugColors[set] = false; + handleDebugColours("roygpb"); + + setWidgetStates(set); + break; + + case 2: // States myContinuousRewind[set] = devSettings ? true : false; myStateSize[set] = 100; + myUncompressed[set] = devSettings ? 60 : 30; myStateInterval[set] = devSettings ? 2 : 4; myStateHorizon[set] = devSettings ? 3 : 5; setWidgetStates(set); break; - case 2: // Debug colours - handleDebugColours("roygpb"); - break; - case 3: // Debugger options { #ifdef DEBUGGER_SUPPORT @@ -770,6 +832,10 @@ void DeveloperDialog::handleCommand(CommandSender* sender, int cmd, int data, in handleSize(); break; + case kUncompressedChanged: + handleUncompressed(); + break; + case kIntervalChanged: handleInterval(); break; @@ -843,6 +909,7 @@ void DeveloperDialog::handleSettings(bool devSettings) mySettings = devSettings; // block redundant events first! mySettingsGroup0->setSelected(devSettings ? 1 : 0); mySettingsGroup1->setSelected(devSettings ? 1 : 0); + mySettingsGroup2->setSelected(devSettings ? 1 : 0); getWidgetStates(devSettings ? SettingsSet::player : SettingsSet::developer); setWidgetStates(devSettings ? SettingsSet::developer : SettingsSet::player); } @@ -884,6 +951,9 @@ void DeveloperDialog::handleRewind() myStateSizeWidget->setEnabled(enable); myStateSizeLabelWidget->setEnabled(enable); + myUncompressedWidget->setEnabled(enable); + myUncompressedLabelWidget->setEnabled(enable); + myStateIntervalWidget->setEnabled(enable); myStateIntervalLabelWidget->setEnabled(enable); @@ -896,6 +966,7 @@ void DeveloperDialog::handleSize() { bool found = false; uInt64 size = myStateSizeWidget->getValue(); + uInt64 uncompressed = myUncompressedWidget->getValue(); uInt64 interval = myStateIntervalWidget->getValue(); uInt64 horizon = myStateHorizonWidget->getValue(); Int32 i; @@ -916,17 +987,38 @@ void DeveloperDialog::handleSize() interval--; } while(!found); + if(size < uncompressed) + { + myUncompressedWidget->setValue(size); + myUncompressedLabelWidget->setValue(myStateSizeWidget->getValue()); + } + myStateHorizonWidget->setValue(i); myStateHorizonLabelWidget->setLabel(HORIZONS[i]); myStateIntervalWidget->setValue(interval); myStateIntervalLabelWidget->setLabel(INTERVALS[interval]); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void DeveloperDialog::handleUncompressed() +{ + uInt64 size = myStateSizeWidget->getValue(); + uInt64 uncompressed = myUncompressedWidget->getValue(); + + myUncompressedLabelWidget->setValue(myUncompressedWidget->getValue()); + if(uncompressed > size) + { + myStateSizeWidget->setValue(uncompressed); + myStateSizeLabelWidget->setValue(myUncompressedWidget->getValue()); + } +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void DeveloperDialog::handleInterval() { bool found = false; uInt64 size = myStateSizeWidget->getValue(); + uInt64 uncompressed = myUncompressedWidget->getValue(); uInt64 interval = myStateIntervalWidget->getValue(); uInt64 horizon = myStateHorizonWidget->getValue(); Int32 i; @@ -951,6 +1043,12 @@ void DeveloperDialog::handleInterval() myStateHorizonLabelWidget->setLabel(HORIZONS[i]); myStateSizeWidget->setValue(size); myStateSizeLabelWidget->setValue(myStateSizeWidget->getValue()); + + if(size < uncompressed) + { + myUncompressedWidget->setValue(size); + myUncompressedLabelWidget->setValue(myStateSizeWidget->getValue()); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -958,6 +1056,7 @@ void DeveloperDialog::handleHorizon() { bool found = false; uInt64 size = myStateSizeWidget->getValue(); + uInt64 uncompressed = myUncompressedWidget->getValue(); uInt64 interval = myStateIntervalWidget->getValue(); uInt64 horizon = myStateHorizonWidget->getValue(); Int32 i; @@ -981,13 +1080,15 @@ void DeveloperDialog::handleHorizon() myStateIntervalWidget->setValue(i); myStateIntervalLabelWidget->setLabel(INTERVALS[i]); myStateSizeWidget->setValue(size); + if(size < uncompressed) + myUncompressedWidget->setValue(size); myStateSizeLabelWidget->setValue(myStateSizeWidget->getValue()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void DeveloperDialog::handleDebugColours(int idx, int color) { - if(idx < 0 || idx > 5) + if(idx < 0 || idx >= DEBUG_COLORS) return; if(!instance().hasConsole()) @@ -997,31 +1098,68 @@ void DeveloperDialog::handleDebugColours(int idx, int color) return; } - static constexpr int dbg_color[2][6] = { - { TIA::FixedColor::NTSC_RED, - TIA::FixedColor::NTSC_ORANGE, - TIA::FixedColor::NTSC_YELLOW, - TIA::FixedColor::NTSC_GREEN, - TIA::FixedColor::NTSC_BLUE, - TIA::FixedColor::NTSC_PURPLE + static constexpr int dbg_color[2][DEBUG_COLORS] = { + { + TIA::FixedColor::NTSC_RED, + TIA::FixedColor::NTSC_ORANGE, + TIA::FixedColor::NTSC_YELLOW, + TIA::FixedColor::NTSC_GREEN, + TIA::FixedColor::NTSC_PURPLE, + TIA::FixedColor::NTSC_BLUE }, - { TIA::FixedColor::PAL_RED, - TIA::FixedColor::PAL_ORANGE, - TIA::FixedColor::PAL_YELLOW, - TIA::FixedColor::PAL_GREEN, - TIA::FixedColor::PAL_BLUE, - TIA::FixedColor::PAL_PURPLE + { + TIA::FixedColor::PAL_RED, + TIA::FixedColor::PAL_ORANGE, + TIA::FixedColor::PAL_YELLOW, + TIA::FixedColor::PAL_GREEN, + TIA::FixedColor::PAL_PURPLE, + TIA::FixedColor::PAL_BLUE } }; + int mode = instance().console().tia().frameLayout() == FrameLayout::ntsc ? 0 : 1; myDbgColourSwatch[idx]->setColor(dbg_color[mode][color]); myDbgColour[idx]->setSelectedIndex(color); + + // make sure the selected debug colors are all different + bool usedCol[DEBUG_COLORS]; + + // identify used colors + for(int i = 0; i < DEBUG_COLORS; ++i) + { + usedCol[i] = false; + for(int j = 0; j < DEBUG_COLORS; ++j) + { + if(myDbgColourSwatch[j]->getColor() == dbg_color[mode][i]) + { + usedCol[i] = true; + break; + } + } + } + // check if currently changed color was used somewhere else + for(int i = 0; i < DEBUG_COLORS; ++i) + { + if (i != idx && myDbgColourSwatch[i]->getColor() == dbg_color[mode][color]) + { + // if already used, change the other color to an unused one + for(int j = 0; j < DEBUG_COLORS; ++j) + { + if(!usedCol[j]) + { + myDbgColourSwatch[i]->setColor(dbg_color[mode][j]); + myDbgColour[i]->setSelectedIndex(j); + break; + } + } + } + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void DeveloperDialog::handleDebugColours(const string& colors) { - for(int i = 0; i < 6; ++i) + for(int i = 0; i < DEBUG_COLORS; ++i) { switch(colors[i]) { @@ -1029,8 +1167,8 @@ void DeveloperDialog::handleDebugColours(const string& colors) case 'o': handleDebugColours(i, 1); break; case 'y': handleDebugColours(i, 2); break; case 'g': handleDebugColours(i, 3); break; - case 'b': handleDebugColours(i, 4); break; - case 'p': handleDebugColours(i, 5); break; + case 'p': handleDebugColours(i, 4); break; + case 'b': handleDebugColours(i, 5); break; default: break; } } diff --git a/src/gui/DeveloperDialog.hxx b/src/gui/DeveloperDialog.hxx index e816e35d8..c347dc9aa 100644 --- a/src/gui/DeveloperDialog.hxx +++ b/src/gui/DeveloperDialog.hxx @@ -55,28 +55,29 @@ class DeveloperDialog : public Dialog private: enum { - kPlrSettings = 'DVpl', - kDevSettings = 'DVdv', - kConsole = 'DVco', - kRandRAMID = 'DVrm', - kRandCPUID = 'DVcp', - kTVJitter = 'DVjt', - kTVJitterChanged = 'DVjr', - kPPinCmd = 'DVpn', - kRewind = 'DSrw', - kSizeChanged = 'DSsz', - kIntervalChanged = 'DSin', - kHorizonChanged = 'DShz', - kP0ColourChangedCmd = 'GOp0', - kM0ColourChangedCmd = 'GOm0', - kP1ColourChangedCmd = 'GOp1', - kM1ColourChangedCmd = 'GOm1', - kPFColourChangedCmd = 'GOpf', - kBLColourChangedCmd = 'GObl', + kPlrSettings = 'DVpl', + kDevSettings = 'DVdv', + kConsole = 'DVco', + kRandRAMID = 'DVrm', + kRandCPUID = 'DVcp', + kTVJitter = 'DVjt', + kTVJitterChanged = 'DVjr', + kPPinCmd = 'DVpn', + kRewind = 'DSrw', + kSizeChanged = 'DSsz', + kUncompressedChanged = 'DSuc', + kIntervalChanged = 'DSin', + kHorizonChanged = 'DShz', + kP0ColourChangedCmd = 'GOp0', + kM0ColourChangedCmd = 'GOm0', + kP1ColourChangedCmd = 'GOp1', + kM1ColourChangedCmd = 'GOm1', + kPFColourChangedCmd = 'GOpf', + kBLColourChangedCmd = 'GObl', #ifdef DEBUGGER_SUPPORT - kDWidthChanged = 'UIdw', - kDHeightChanged = 'UIdh', - kDFontSizeChanged = 'UIfs', + kDWidthChanged = 'UIdw', + kDHeightChanged = 'UIdh', + kDFontSizeChanged = 'UIfs', #endif }; enum SettingsSet @@ -97,6 +98,8 @@ class DeveloperDialog : public Dialog const uInt64 HORIZON_CYCLES[NUM_HORIZONS] = { 76 * 262, 76 * 262 * 10, 76 * 262 * 60, 76 * 262 * 60 * 10, 76 * 262 * 60 * 60, 76 * 262 * 60 * 60 * 10, (uInt64)76 * 262 * 60 * 60 * 60 }; + static const int DEBUG_COLORS = 6; + TabWidget* myTab; // Emulator widgets RadioButtonGroup* mySettingsGroup0; @@ -107,26 +110,31 @@ class DeveloperDialog : public Dialog CheckboxWidget* myRandomizeRAMWidget; StaticTextWidget* myRandomizeCPULabel; CheckboxWidget* myRandomizeCPUWidget[5]; - CheckboxWidget* myColorLossWidget; + CheckboxWidget* myUndrivenPinsWidget; + CheckboxWidget* myThumbExceptionWidget; + + // Video widgets + RadioButtonGroup* mySettingsGroup1; CheckboxWidget* myTVJitterWidget; SliderWidget* myTVJitterRecWidget; StaticTextWidget* myTVJitterRecLabelWidget; + CheckboxWidget* myColorLossWidget; CheckboxWidget* myDebugColorsWidget; - CheckboxWidget* myUndrivenPinsWidget; + PopUpWidget* myDbgColour[DEBUG_COLORS]; + ColorWidget* myDbgColourSwatch[DEBUG_COLORS]; + // States widgets - RadioButtonGroup* mySettingsGroup1; + RadioButtonGroup* mySettingsGroup2; CheckboxWidget* myContinuousRewindWidget; SliderWidget* myStateSizeWidget; StaticTextWidget* myStateSizeLabelWidget; + SliderWidget* myUncompressedWidget; + StaticTextWidget* myUncompressedLabelWidget; SliderWidget* myStateIntervalWidget; StaticTextWidget* myStateIntervalLabelWidget; SliderWidget* myStateHorizonWidget; StaticTextWidget* myStateHorizonLabelWidget; - // Debug colours selection - PopUpWidget* myDbgColour[6]; - ColorWidget* myDbgColourSwatch[6]; - #ifdef DEBUGGER_SUPPORT // Debugger UI widgets SliderWidget* myDebuggerWidthSlider; @@ -151,16 +159,18 @@ class DeveloperDialog : public Dialog int myTVJitterRec[2]; bool myDebugColors[2]; bool myUndrivenPins[2]; + bool myThumbException[2]; // States sets bool myContinuousRewind[2]; int myStateSize[2]; + int myUncompressed[2]; int myStateInterval[2]; int myStateHorizon[2]; private: void addEmulationTab(const GUI::Font& font); void addStatesTab(const GUI::Font& font); - void addDebugColorsTab(const GUI::Font& font); + void addVideoTab(const GUI::Font& font); void addDebuggerTab(const GUI::Font& font); // Add Defaults, OK and Cancel buttons void addDefaultOKCancelButtons(const GUI::Font& font); @@ -180,6 +190,7 @@ class DeveloperDialog : public Dialog void handleRewind(); void handleSize(); + void handleUncompressed(); void handleInterval(); void handleHorizon(); void handleFontSize();