1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include "bspf.hxx"
19#include "OSystem.hxx"
20#include "Joystick.hxx"
21#include "Paddles.hxx"
22#include "PointingDevice.hxx"
23#include "SaveKey.hxx"
24#include "AtariVox.hxx"
25#include "Settings.hxx"
26#include "EditTextWidget.hxx"
27#include "PopUpWidget.hxx"
28#include "RadioButtonWidget.hxx"
29#include "ColorWidget.hxx"
30#include "TabWidget.hxx"
31#include "Widget.hxx"
32#include "Font.hxx"
33#include "Console.hxx"
34#include "TIA.hxx"
35#include "OSystem.hxx"
36#include "EventHandler.hxx"
37#include "StateManager.hxx"
38#include "RewindManager.hxx"
39#include "M6502.hxx"
40#ifdef DEBUGGER_SUPPORT
41 #include "Debugger.hxx"
42 #include "CartDebug.hxx"
43 #include "DebuggerDialog.hxx"
44#endif
45#include "DeveloperDialog.hxx"
46
47// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
48DeveloperDialog::DeveloperDialog(OSystem& osystem, DialogContainer& parent,
49 const GUI::Font& font, int max_w, int max_h)
50 : Dialog(osystem, parent, font, "Developer settings")
51{
52 const int VGAP = 4;
53 const int lineHeight = font.getLineHeight(),
54 fontWidth = font.getMaxCharWidth(),
55 buttonHeight = font.getLineHeight() + 4;
56 int xpos, ypos;
57
58 // Set real dimensions
59 setSize(54 * fontWidth + 10, 16 * (lineHeight + VGAP) + 14 + _th, max_w, max_h);
60
61 // The tab widget
62 xpos = 2; ypos = 4;
63 myTab = new TabWidget(this, font, xpos, ypos + _th, _w - 2 * xpos, _h - _th - buttonHeight - 16 - ypos);
64 addTabWidget(myTab);
65
66 addEmulationTab(font);
67 addTiaTab(font);
68 addVideoTab(font);
69 addTimeMachineTab(font);
70 addDebuggerTab(font);
71
72 WidgetArray wid;
73 addDefaultsOKCancelBGroup(wid, font);
74 addBGroupToFocusList(wid);
75
76 // Activate the first tab
77 myTab->setActiveTab(0);
78}
79
80// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
81void DeveloperDialog::addEmulationTab(const GUI::Font& font)
82{
83 const int HBORDER = 10;
84 const int INDENT = 16+4;
85 const int VBORDER = 8;
86 const int VGAP = 4;
87 int ypos = VBORDER;
88 int lineHeight = font.getLineHeight();
89 WidgetArray wid;
90 VariantList items;
91 int tabID = myTab->addTab(" Emulation ", TabWidget::AUTO_WIDTH);
92
93 // settings set
94 mySettingsGroupEmulation = new RadioButtonGroup();
95 RadioButtonWidget* r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1,
96 "Player settings", mySettingsGroupEmulation, kPlrSettings);
97 wid.push_back(r);
98 ypos += lineHeight + VGAP;
99 r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1,
100 "Developer settings", mySettingsGroupEmulation, kDevSettings);
101 wid.push_back(r);
102 ypos += lineHeight + VGAP * 1;
103
104 myFrameStatsWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1, "Console info overlay");
105 wid.push_back(myFrameStatsWidget);
106 ypos += lineHeight + VGAP;
107
108 // 2600/7800 mode
109 items.clear();
110 VarList::push_back(items, "Atari 2600", "2600");
111 VarList::push_back(items, "Atari 7800", "7800");
112 int lwidth = font.getStringWidth("Console ");
113 int pwidth = font.getStringWidth("Atari 2600");
114
115 myConsoleWidget = new PopUpWidget(myTab, font, HBORDER + INDENT * 1, ypos, pwidth, lineHeight, items,
116 "Console ", lwidth, kConsole);
117 wid.push_back(myConsoleWidget);
118 ypos += lineHeight + VGAP;
119
120 // Randomize items
121 myLoadingROMLabel = new StaticTextWidget(myTab, font, HBORDER + INDENT*1, ypos + 1, "When loading a ROM:");
122 wid.push_back(myLoadingROMLabel);
123 ypos += lineHeight + VGAP;
124
125 myRandomBankWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 2, ypos + 1,
126 "Random startup bank");
127 wid.push_back(myRandomBankWidget);
128 ypos += lineHeight + VGAP;
129
130 // Randomize RAM
131 myRandomizeRAMWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 2, ypos + 1,
132 "Randomize zero-page and extended RAM", kRandRAMID);
133 wid.push_back(myRandomizeRAMWidget);
134 ypos += lineHeight + VGAP;
135
136 // Randomize CPU
137 myRandomizeCPULabel = new StaticTextWidget(myTab, font, HBORDER + INDENT * 2, ypos + 1, "Randomize CPU ");
138 wid.push_back(myRandomizeCPULabel);
139
140 int xpos = myRandomizeCPULabel->getRight() + 10;
141 const char* const cpuregs[] = { "SP", "A", "X", "Y", "PS" };
142 for(int i = 0; i < 5; ++i)
143 {
144 myRandomizeCPUWidget[i] = new CheckboxWidget(myTab, font, xpos, ypos + 1,
145 cpuregs[i], kRandCPUID);
146 wid.push_back(myRandomizeCPUWidget[i]);
147 xpos += CheckboxWidget::boxSize() + font.getStringWidth("XX") + 20;
148 }
149 ypos += lineHeight + VGAP;
150
151 // How to handle undriven TIA pins
152 myUndrivenPinsWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
153 "Drive unused TIA pins randomly on a read/peek");
154 wid.push_back(myUndrivenPinsWidget);
155 ypos += lineHeight + VGAP;
156
157#ifdef DEBUGGER_SUPPORT
158 myRWPortBreakWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
159 "Break on reads from write ports");
160 wid.push_back(myRWPortBreakWidget);
161 ypos += lineHeight + VGAP;
162
163 myWRPortBreakWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
164 "Break on writes to read ports");
165 wid.push_back(myWRPortBreakWidget);
166 ypos += lineHeight + VGAP;
167#endif
168
169 // Thumb ARM emulation exception
170 myThumbExceptionWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
171 "Fatal ARM emulation error throws exception");
172 wid.push_back(myThumbExceptionWidget);
173 ypos += lineHeight + VGAP;
174
175 // AtariVox/SaveKey EEPROM access
176 myEEPROMAccessWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
177 "Display AtariVox/SaveKey EEPROM R/W access");
178 wid.push_back(myEEPROMAccessWidget);
179
180 // Add items for tab 0
181 addToFocusList(wid, myTab, tabID);
182}
183
184// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
185void DeveloperDialog::addTiaTab(const GUI::Font& font)
186{
187 const int HBORDER = 10;
188 const int INDENT = 16 + 4;
189 const int VBORDER = 8;
190 const int VGAP = 4;
191 int ypos = VBORDER;
192 int lineHeight = font.getLineHeight();
193 int pwidth = font.getStringWidth("Faulty Cosmic Ark stars");
194 WidgetArray wid;
195 VariantList items;
196 int tabID = myTab->addTab(" TIA ", TabWidget::AUTO_WIDTH);
197
198 wid.clear();
199
200 // settings set
201 mySettingsGroupTia = new RadioButtonGroup();
202 RadioButtonWidget* r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1,
203 "Player settings", mySettingsGroupTia, kPlrSettings);
204 wid.push_back(r);
205 ypos += lineHeight + VGAP;
206 r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1,
207 "Developer settings", mySettingsGroupTia, kDevSettings);
208 wid.push_back(r);
209 ypos += lineHeight + VGAP * 1;
210
211 items.clear();
212 VarList::push_back(items, "Standard", "standard");
213 VarList::push_back(items, "Faulty Kool-Aid Man", "koolaidman");
214 VarList::push_back(items, "Faulty Cosmic Ark stars", "cosmicark");
215 VarList::push_back(items, "Glitched Pesco", "pesco");
216 VarList::push_back(items, "Glitched Quick Step!", "quickstep");
217 VarList::push_back(items, "Glitched He-Man title", "heman");
218 VarList::push_back(items, "Custom", "custom");
219 myTIATypeWidget = new PopUpWidget(myTab, font, HBORDER + INDENT, ypos - 1,
220 pwidth, lineHeight, items, "Chip type ", 0, kTIAType);
221 wid.push_back(myTIATypeWidget);
222 ypos += lineHeight + VGAP * 1;
223
224 myInvPhaseLabel = new StaticTextWidget(myTab, font, HBORDER + INDENT * 2, ypos + 1,
225 "Inverted HMOVE clock phase for");
226 wid.push_back(myInvPhaseLabel);
227 ypos += lineHeight + VGAP * 1;
228
229 myPlInvPhaseWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 3, ypos + 1,
230 "Players");
231 wid.push_back(myPlInvPhaseWidget);
232
233 myMsInvPhaseWidget = new CheckboxWidget(myTab, font, myPlInvPhaseWidget->getRight() + 20, ypos + 1,
234 "Missiles");
235 wid.push_back(myMsInvPhaseWidget);
236
237 myBlInvPhaseWidget = new CheckboxWidget(myTab, font, myMsInvPhaseWidget->getRight() + 20, ypos + 1,
238 "Ball");
239 wid.push_back(myBlInvPhaseWidget);
240 ypos += lineHeight + VGAP * 1;
241
242 myPlayfieldLabel = new StaticTextWidget(myTab, font, HBORDER + INDENT * 2, ypos + 1,
243 "Delayed playfield");
244 wid.push_back(myPlayfieldLabel);
245 ypos += lineHeight + VGAP * 1;
246
247 myPFBitsWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 3, ypos + 1, "Bits");
248 wid.push_back(myPFBitsWidget);
249 //ypos += lineHeight + VGAP * 1;
250
251 myPFColorWidget = new CheckboxWidget(myTab, font, myPFBitsWidget->getRight() + 20, ypos + 1, "Color");
252 wid.push_back(myPFColorWidget);
253 ypos += lineHeight + VGAP * 1;
254
255 mySwapLabel = new StaticTextWidget(myTab, font, HBORDER + INDENT * 2, ypos + 1,
256 "Delayed VDEL" + ELLIPSIS + " swap for");
257 wid.push_back(mySwapLabel);
258 ypos += lineHeight + VGAP * 1;
259
260 myPlSwapWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 3, ypos + 1, "Players");
261 wid.push_back(myPlSwapWidget);
262
263 myBlSwapWidget = new CheckboxWidget(myTab, font, myPlSwapWidget->getRight() + 20, ypos + 1, "Ball");
264 wid.push_back(myBlSwapWidget);
265
266 // Add items for tab 2
267 addToFocusList(wid, myTab, tabID);
268}
269
270// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
271void DeveloperDialog::addVideoTab(const GUI::Font& font)
272{
273 const int HBORDER = 10;
274 const int INDENT = 16 + 4;
275 const int VBORDER = 8;
276 const int VGAP = 4;
277 int ypos = VBORDER;
278 int lineHeight = font.getLineHeight();
279 int fontWidth = font.getMaxCharWidth(), fontHeight = font.getFontHeight();
280 int lwidth = font.getStringWidth("Intensity ");
281 int pwidth = font.getMaxCharWidth() * 6;
282 WidgetArray wid;
283 VariantList items;
284 int tabID = myTab->addTab(" Video ", TabWidget::AUTO_WIDTH);
285
286 wid.clear();
287
288 // settings set
289 mySettingsGroupVideo = new RadioButtonGroup();
290 RadioButtonWidget* r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1,
291 "Player settings", mySettingsGroupVideo, kPlrSettings);
292 wid.push_back(r);
293 ypos += lineHeight + VGAP;
294 r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1,
295 "Developer settings", mySettingsGroupVideo, kDevSettings);
296 wid.push_back(r);
297 ypos += lineHeight + VGAP * 1;
298
299 // TV jitter effect
300 myTVJitterWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
301 "Jitter/roll effect", kTVJitter);
302 wid.push_back(myTVJitterWidget);
303 myTVJitterRecWidget = new SliderWidget(myTab, font,
304 myTVJitterWidget->getRight() + fontWidth * 3, ypos - 1,
305 "Recovery ", 0, kTVJitterChanged);
306 myTVJitterRecWidget->setMinValue(1); myTVJitterRecWidget->setMaxValue(20);
307 myTVJitterRecWidget->setTickmarkIntervals(5);
308 wid.push_back(myTVJitterRecWidget);
309 myTVJitterRecLabelWidget = new StaticTextWidget(myTab, font,
310 myTVJitterRecWidget->getRight() + 4,
311 myTVJitterRecWidget->getTop() + 2,
312 5 * fontWidth, fontHeight, "");
313 ypos += lineHeight + VGAP;
314
315 myColorLossWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
316 "PAL color-loss");
317 wid.push_back(myColorLossWidget);
318 ypos += lineHeight + VGAP;
319
320 // debug colors
321 myDebugColorsWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
322 "Debug colors (*)");
323 wid.push_back(myDebugColorsWidget);
324 ypos += lineHeight + VGAP + 2;
325
326 items.clear();
327 VarList::push_back(items, "Red", "r");
328 VarList::push_back(items, "Orange", "o");
329 VarList::push_back(items, "Yellow", "y");
330 VarList::push_back(items, "Green", "g");
331 VarList::push_back(items, "Purple", "p");
332 VarList::push_back(items, "Blue", "b");
333
334 static constexpr int dbg_cmds[DEBUG_COLORS] = {
335 kP0ColourChangedCmd, kM0ColourChangedCmd, kP1ColourChangedCmd,
336 kM1ColourChangedCmd, kPFColourChangedCmd, kBLColourChangedCmd
337 };
338
339 auto createDebugColourWidgets = [&](int idx, const string& desc)
340 {
341 int x = HBORDER + INDENT * 1;
342 myDbgColour[idx] = new PopUpWidget(myTab, font, x, ypos - 1,
343 pwidth, lineHeight, items, desc, lwidth, dbg_cmds[idx]);
344 wid.push_back(myDbgColour[idx]);
345 x += myDbgColour[idx]->getWidth() + 10;
346 myDbgColourSwatch[idx] = new ColorWidget(myTab, font, x, ypos - 1,
347 uInt32(2 * lineHeight), lineHeight);
348 ypos += lineHeight + VGAP * 1;
349 };
350
351 createDebugColourWidgets(0, "Player 0 ");
352 createDebugColourWidgets(1, "Missile 0 ");
353 createDebugColourWidgets(2, "Player 1 ");
354 createDebugColourWidgets(3, "Missile 1 ");
355 createDebugColourWidgets(4, "Playfield ");
356 createDebugColourWidgets(5, "Ball ");
357
358 // Add message concerning usage
359 const GUI::Font& infofont = instance().frameBuffer().infoFont();
360 ypos = myTab->getHeight() - 5 - fontHeight - infofont.getFontHeight() - 10;
361 new StaticTextWidget(myTab, infofont, HBORDER, ypos, "(*) Colors identical for player and developer settings");
362
363 // Add items for tab 2
364 addToFocusList(wid, myTab, tabID);
365}
366
367// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
368void DeveloperDialog::addTimeMachineTab(const GUI::Font& font)
369{
370 const string INTERVALS[NUM_INTERVALS] = {
371 " 1 frame",
372 " 3 frames",
373 "10 frames",
374 "30 frames",
375 " 1 second",
376 " 3 seconds",
377 "10 seconds"
378 };
379 const string INT_SETTINGS[NUM_INTERVALS] = {
380 "1f",
381 "3f",
382 "10f",
383 "30f",
384 "1s",
385 "3s",
386 "10s"
387 };
388 const string HORIZONS[NUM_HORIZONS] = {
389 " 3 seconds",
390 "10 seconds",
391 "30 seconds",
392 " 1 minute",
393 " 3 minutes",
394 "10 minutes",
395 "30 minutes",
396 "60 minutes"
397 };
398 const string HOR_SETTINGS[NUM_HORIZONS] = {
399 "3s",
400 "10s",
401 "30s",
402 "1m",
403 "3m",
404 "10m",
405 "30m",
406 "60m"
407 };
408 const int HBORDER = 10;
409 const int INDENT = 16+4;
410 const int VBORDER = 8;
411 const int VGAP = 4;
412 int ypos = VBORDER;
413 int lineHeight = font.getLineHeight(),
414 fontHeight = font.getFontHeight(),
415 fontWidth = font.getMaxCharWidth(),
416 lwidth = fontWidth * 11;
417 WidgetArray wid;
418 VariantList items;
419 int tabID = myTab->addTab(" Time Machine ", TabWidget::AUTO_WIDTH);
420
421 // settings set
422 mySettingsGroupTM = new RadioButtonGroup();
423 RadioButtonWidget* r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1,
424 "Player settings", mySettingsGroupTM, kPlrSettings);
425 wid.push_back(r);
426 ypos += lineHeight + VGAP;
427 r = new RadioButtonWidget(myTab, font, HBORDER, ypos + 1,
428 "Developer settings", mySettingsGroupTM, kDevSettings);
429 wid.push_back(r);
430 ypos += lineHeight + VGAP * 1;
431
432 myTimeMachineWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT, ypos + 1,
433 "Time Machine", kTimeMachine);
434 wid.push_back(myTimeMachineWidget);
435 ypos += lineHeight + VGAP;
436
437 int swidth = fontWidth * 12 + 5; // width of PopUpWidgets below
438 myStateSizeWidget = new SliderWidget(myTab, font, HBORDER + INDENT * 2, ypos - 1, swidth, lineHeight,
439 "Buffer size (*) ", 0, kSizeChanged, lwidth, " states");
440 myStateSizeWidget->setMinValue(20);
441#ifdef RETRON77
442 myStateSizeWidget->setMaxValue(100);
443#else
444 myStateSizeWidget->setMaxValue(1000);
445#endif
446 myStateSizeWidget->setStepValue(20);
447 myStateSizeWidget->setTickmarkIntervals(5);
448 wid.push_back(myStateSizeWidget);
449 ypos += lineHeight + VGAP;
450
451 myUncompressedWidget = new SliderWidget(myTab, font, HBORDER + INDENT * 2, ypos - 1, swidth, lineHeight,
452 "Uncompressed size ", 0, kUncompressedChanged, lwidth, " states");
453 myUncompressedWidget->setMinValue(0);
454#ifdef RETRON77
455 myUncompressedWidget->setMaxValue(100);
456#else
457 myUncompressedWidget->setMaxValue(1000);
458#endif
459 myUncompressedWidget->setStepValue(20);
460 myUncompressedWidget->setTickmarkIntervals(5);
461 wid.push_back(myUncompressedWidget);
462 ypos += lineHeight + VGAP;
463
464 items.clear();
465 for(int i = 0; i < NUM_INTERVALS; ++i)
466 VarList::push_back(items, INTERVALS[i], INT_SETTINGS[i]);
467 int pwidth = font.getStringWidth("10 seconds");
468 myStateIntervalWidget = new PopUpWidget(myTab, font, HBORDER + INDENT * 2, ypos, pwidth,
469 lineHeight, items, "Interval ", 0, kIntervalChanged);
470 wid.push_back(myStateIntervalWidget);
471 ypos += lineHeight + VGAP;
472
473 items.clear();
474 for(int i = 0; i < NUM_HORIZONS; ++i)
475 VarList::push_back(items, HORIZONS[i], HOR_SETTINGS[i]);
476 myStateHorizonWidget = new PopUpWidget(myTab, font, HBORDER + INDENT * 2, ypos, pwidth,
477 lineHeight, items, "Horizon ~ ", 0, kHorizonChanged);
478 wid.push_back(myStateHorizonWidget);
479
480 ypos += lineHeight + VGAP * 2;
481 new StaticTextWidget(myTab, font, HBORDER, ypos + 1,
482 "When exiting emulation:");
483 ypos += lineHeight + VGAP;
484 mySaveOnExitGroup = new RadioButtonGroup();
485 r = new RadioButtonWidget(myTab, font, HBORDER + INDENT, ypos + 1,
486 "Save nothing", mySaveOnExitGroup);
487 wid.push_back(r);
488 ypos += lineHeight + VGAP;
489 r = new RadioButtonWidget(myTab, font, HBORDER + INDENT, ypos + 1,
490 "Save current state in current slot", mySaveOnExitGroup);
491 wid.push_back(r);
492 ypos += lineHeight + VGAP;
493 r = new RadioButtonWidget(myTab, font, HBORDER + INDENT, ypos + 1,
494 "Save all states", mySaveOnExitGroup);
495 wid.push_back(r);
496 ypos += lineHeight + VGAP;
497
498 // Add message concerning usage
499 const GUI::Font& infofont = instance().frameBuffer().infoFont();
500 ypos = myTab->getHeight() - 5 - fontHeight - infofont.getFontHeight() - 10;
501 new StaticTextWidget(myTab, infofont, HBORDER, ypos, "(*) Any size change clears the buffer");
502
503 addToFocusList(wid, myTab, tabID);
504}
505
506// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
507void DeveloperDialog::addDebuggerTab(const GUI::Font& font)
508{
509 int tabID = myTab->addTab(" Debugger ", TabWidget::AUTO_WIDTH);
510 WidgetArray wid;
511
512#ifdef DEBUGGER_SUPPORT
513 const int HBORDER = 10;
514 const int VBORDER = 8;
515 const int VGAP = 4;
516
517 VariantList items;
518 int fontWidth = font.getMaxCharWidth(),
519 fontHeight = font.getFontHeight(),
520 lineHeight = font.getLineHeight();
521 int xpos, ypos, pwidth;
522 const Common::Size& ds = instance().frameBuffer().desktopSize();
523
524 xpos = HBORDER;
525 ypos = VBORDER;
526
527 // font size
528 items.clear();
529 VarList::push_back(items, "Small", "small");
530 VarList::push_back(items, "Medium", "medium");
531 VarList::push_back(items, "Large", "large");
532 pwidth = font.getStringWidth("Medium");
533 myDebuggerFontSize =
534 new PopUpWidget(myTab, font, HBORDER, ypos + 1, pwidth, lineHeight, items,
535 "Font size (*) ", 0, kDFontSizeChanged);
536 wid.push_back(myDebuggerFontSize);
537 ypos += lineHeight + 4;
538
539 // Font style (bold label vs. text, etc)
540 items.clear();
541 VarList::push_back(items, "All normal font", "0");
542 VarList::push_back(items, "Bold labels only", "1");
543 VarList::push_back(items, "Bold non-labels only", "2");
544 VarList::push_back(items, "All bold font", "3");
545 pwidth = font.getStringWidth("Bold non-labels only");
546 myDebuggerFontStyle =
547 new PopUpWidget(myTab, font, HBORDER, ypos + 1, pwidth, lineHeight, items,
548 "Font style (*) ", 0);
549 wid.push_back(myDebuggerFontStyle);
550
551 ypos += lineHeight + VGAP * 4;
552
553 // Debugger width and height
554 myDebuggerWidthSlider = new SliderWidget(myTab, font, xpos, ypos-1, "Debugger width (*) ",
555 0, 0, 6 * fontWidth, "px");
556 myDebuggerWidthSlider->setMinValue(DebuggerDialog::kSmallFontMinW);
557 myDebuggerWidthSlider->setMaxValue(ds.w);
558 myDebuggerWidthSlider->setStepValue(10);
559 // one tickmark every ~100 pixel
560 myDebuggerWidthSlider->setTickmarkIntervals((ds.w - DebuggerDialog::kSmallFontMinW + 50) / 100);
561 wid.push_back(myDebuggerWidthSlider);
562 ypos += lineHeight + VGAP;
563
564 myDebuggerHeightSlider = new SliderWidget(myTab, font, xpos, ypos-1, "Debugger height (*) ",
565 0, 0, 6 * fontWidth, "px");
566 myDebuggerHeightSlider->setMinValue(DebuggerDialog::kSmallFontMinH);
567 myDebuggerHeightSlider->setMaxValue(ds.h);
568 myDebuggerHeightSlider->setStepValue(10);
569 // one tickmark every ~100 pixel
570 myDebuggerHeightSlider->setTickmarkIntervals((ds.h - DebuggerDialog::kSmallFontMinH + 50) / 100);
571 wid.push_back(myDebuggerHeightSlider);
572 ypos += lineHeight + VGAP * 4;
573
574 myGhostReadsTrapWidget = new CheckboxWidget(myTab, font, HBORDER, ypos + 1,
575 "Trap on 'ghost' reads");
576 wid.push_back(myGhostReadsTrapWidget);
577
578 // Add message concerning usage
579 const GUI::Font& infofont = instance().frameBuffer().infoFont();
580 ypos = myTab->getHeight() - 5 - fontHeight - infofont.getFontHeight() - 10;
581 new StaticTextWidget(myTab, infofont, HBORDER, ypos, "(*) Changes require a ROM reload");
582
583 // Debugger is only realistically available in windowed modes 800x600 or greater
584 // (and when it's actually been compiled into the app)
585 bool debuggerAvailable =
586#if defined(DEBUGGER_SUPPORT) && defined(WINDOWED_SUPPORT)
587 (ds.w >= 800 && ds.h >= 600); // TODO - maybe this logic can disappear?
588#else
589 false;
590#endif
591 if(!debuggerAvailable)
592 {
593 myDebuggerWidthSlider->clearFlags(Widget::FLAG_ENABLED);
594 myDebuggerHeightSlider->clearFlags(Widget::FLAG_ENABLED);
595 }
596#else
597 new StaticTextWidget(myTab, font, 0, 20, _w - 20, font.getFontHeight(),
598 "Debugger support not included", TextAlign::Center);
599#endif
600
601 addToFocusList(wid, myTab, tabID);
602}
603
604// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
605void DeveloperDialog::loadSettings(SettingsSet set)
606{
607 bool devSettings = set == SettingsSet::developer;
608 const string& prefix = devSettings ? "dev." : "plr.";
609
610 myFrameStats[set] = instance().settings().getBool(prefix + "stats");
611 myConsole[set] = instance().settings().getString(prefix + "console") == "7800" ? 1 : 0;
612 // Randomization
613 myRandomBank[set] = instance().settings().getBool(prefix + "bankrandom");
614 myRandomizeRAM[set] = instance().settings().getBool(prefix + "ramrandom");
615 myRandomizeCPU[set] = instance().settings().getString(prefix + "cpurandom");
616 // Undriven TIA pins
617 myUndrivenPins[set] = devSettings ? instance().settings().getBool("dev.tiadriven") : false;
618#ifdef DEBUGGER_SUPPORT
619 // Read from write ports break
620 myRWPortBreak[set] = devSettings ? instance().settings().getBool("dev.rwportbreak") : false;
621 // Write to read ports break
622 myWRPortBreak[set] = devSettings ? instance().settings().getBool("dev.wrportbreak") : false;
623#endif
624 // Thumb ARM emulation exception
625 myThumbException[set] = devSettings ? instance().settings().getBool("dev.thumb.trapfatal") : false;
626 // AtariVox/SaveKey EEPROM access
627 myEEPROMAccess[set] = instance().settings().getBool(prefix + "eepromaccess");
628
629 // TIA tab
630 myTIAType[set] = devSettings ? instance().settings().getString("dev.tia.type") : "standard";
631 myPlInvPhase[set] = devSettings ? instance().settings().getBool("dev.tia.plinvphase") : false;
632 myMsInvPhase[set] = devSettings ? instance().settings().getBool("dev.tia.msinvphase") : false;
633 myBlInvPhase[set] = devSettings ? instance().settings().getBool("dev.tia.blinvphase") : false;
634 myPFBits[set] = devSettings ? instance().settings().getBool("dev.tia.delaypfbits") : false;
635 myPFColor[set] = devSettings ? instance().settings().getBool("dev.tia.delaypfcolor") : false;
636 myPlSwap[set] = devSettings ? instance().settings().getBool("dev.tia.delayplswap") : false;
637 myBlSwap[set] = devSettings ? instance().settings().getBool("dev.tia.delayblswap") : false;
638
639 // Debug colors
640 myDebugColors[set] = instance().settings().getBool(prefix + "debugcolors");
641 // PAL color-loss effect
642 myColorLoss[set] = instance().settings().getBool(prefix + "colorloss");
643 // Jitter
644 myTVJitter[set] = instance().settings().getBool(prefix + "tv.jitter");
645 myTVJitterRec[set] = instance().settings().getInt(prefix + "tv.jitter_recovery");
646
647 // States
648 myTimeMachine[set] = instance().settings().getBool(prefix + "timemachine");
649 myStateSize[set] = instance().settings().getInt(prefix + "tm.size");
650 myUncompressed[set] = instance().settings().getInt(prefix + "tm.uncompressed");
651 myStateInterval[set] = instance().settings().getString(prefix + "tm.interval");
652 myStateHorizon[set] = instance().settings().getString(prefix + "tm.horizon");
653
654
655}
656
657// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
658void DeveloperDialog::saveSettings(SettingsSet set)
659{
660 bool devSettings = set == SettingsSet::developer;
661 const string& prefix = devSettings ? "dev." : "plr.";
662
663 instance().settings().setValue(prefix + "stats", myFrameStats[set]);
664 instance().settings().setValue(prefix + "console", myConsole[set] == 1 ? "7800" : "2600");
665 if(instance().hasConsole())
666 instance().eventHandler().set7800Mode();
667
668 // Randomization
669 instance().settings().setValue(prefix + "bankrandom", myRandomBank[set]);
670 instance().settings().setValue(prefix + "ramrandom", myRandomizeRAM[set]);
671 instance().settings().setValue(prefix + "cpurandom", myRandomizeCPU[set]);
672 // Undriven TIA pins
673 if(devSettings)
674 instance().settings().setValue("dev.tiadriven", myUndrivenPins[set]);
675#ifdef DEBUGGER_SUPPORT
676 if (devSettings)
677 {
678 // Read from write ports break
679 instance().settings().setValue("dev.rwportbreak", myRWPortBreak[set]);
680 // Write to read ports break
681 instance().settings().setValue("dev.wrportbreak", myWRPortBreak[set]);
682 }
683#endif
684 if(devSettings)
685 // Thumb ARM emulation exception
686 instance().settings().setValue("dev.thumb.trapfatal", myThumbException[set]);
687 // AtariVox/SaveKey EEPROM access
688 instance().settings().setValue(prefix + "eepromaccess", myEEPROMAccess[set]);
689
690 // TIA tab
691 if (devSettings)
692 {
693 instance().settings().setValue("dev.tia.type", myTIAType[set]);
694 if (BSPF::equalsIgnoreCase("custom", myTIAType[set]))
695 {
696 instance().settings().setValue("dev.tia.plinvphase", myPlInvPhase[set]);
697 instance().settings().setValue("dev.tia.msinvphase", myMsInvPhase[set]);
698 instance().settings().setValue("dev.tia.blinvphase", myBlInvPhase[set]);
699 instance().settings().setValue("dev.tia.delaypfbits", myPFBits[set]);
700 instance().settings().setValue("dev.tia.delaypfcolor", myPFColor[set]);
701 instance().settings().setValue("dev.tia.delayplswap", myPlSwap[set]);
702 instance().settings().setValue("dev.tia.delayblswap", myBlSwap[set]);
703 }
704 }
705
706 // Debug colors
707 instance().settings().setValue(prefix + "debugcolors", myDebugColors[set]);
708 // PAL color loss
709 instance().settings().setValue(prefix + "colorloss", myColorLoss[set]);
710 // Jitter
711 instance().settings().setValue(prefix + "tv.jitter", myTVJitter[set]);
712 instance().settings().setValue(prefix + "tv.jitter_recovery", myTVJitterRec[set]);
713
714 // States
715 instance().settings().setValue(prefix + "timemachine", myTimeMachine[set]);
716 instance().settings().setValue(prefix + "tm.size", myStateSize[set]);
717 instance().settings().setValue(prefix + "tm.uncompressed", myUncompressed[set]);
718 instance().settings().setValue(prefix + "tm.interval", myStateInterval[set]);
719 instance().settings().setValue(prefix + "tm.horizon", myStateHorizon[set]);
720}
721
722// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
723void DeveloperDialog::getWidgetStates(SettingsSet set)
724{
725 myFrameStats[set] = myFrameStatsWidget->getState();
726 myConsole[set] = myConsoleWidget->getSelected() == 1;
727 // Randomization
728 myRandomBank[set] = myRandomBankWidget->getState();
729 myRandomizeRAM[set] = myRandomizeRAMWidget->getState();
730 string cpurandom;
731 const char* const cpuregs[] = { "S", "A", "X", "Y", "P" };
732 for(int i = 0; i < 5; ++i)
733 if(myRandomizeCPUWidget[i]->getState())
734 cpurandom += cpuregs[i];
735 myRandomizeCPU[set] = cpurandom;
736 // Undriven TIA pins
737 myUndrivenPins[set] = myUndrivenPinsWidget->getState();
738#ifdef DEBUGGER_SUPPORT
739 // Read from write ports break
740 myRWPortBreak[set] = myRWPortBreakWidget->getState();
741 myWRPortBreak[set] = myWRPortBreakWidget->getState();
742#endif
743 // Thumb ARM emulation exception
744 myThumbException[set] = myThumbExceptionWidget->getState();
745 // AtariVox/SaveKey EEPROM access
746 myEEPROMAccess[set] = myEEPROMAccessWidget->getState();
747
748 // TIA tab
749 myTIAType[set] = myTIATypeWidget->getSelectedTag().toString();
750 myPlInvPhase[set] = myPlInvPhaseWidget->getState();
751 myMsInvPhase[set] = myMsInvPhaseWidget->getState();
752 myBlInvPhase[set] = myBlInvPhaseWidget->getState();
753 myPFBits[set] = myPFBitsWidget->getState();
754 myPFColor[set] = myPFColorWidget->getState();
755 myPlSwap[set] = myPlSwapWidget->getState();
756 myBlSwap[set] = myBlSwapWidget->getState();
757
758 // Debug colors
759 myDebugColors[set] = myDebugColorsWidget->getState();
760 // PAL color-loss effect
761 myColorLoss[set] = myColorLossWidget->getState();
762 // Jitter
763 myTVJitter[set] = myTVJitterWidget->getState();
764 myTVJitterRec[set] = myTVJitterRecWidget->getValue();
765
766 // States
767 myTimeMachine[set] = myTimeMachineWidget->getState();
768 myStateSize[set] = myStateSizeWidget->getValue();
769 myUncompressed[set] = myUncompressedWidget->getValue();
770 myStateInterval[set] = myStateIntervalWidget->getSelected();
771 myStateInterval[set] = myStateIntervalWidget->getSelectedTag().toString();
772 myStateHorizon[set] = myStateHorizonWidget->getSelectedTag().toString();
773}
774
775// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
776void DeveloperDialog::setWidgetStates(SettingsSet set)
777{
778 myFrameStatsWidget->setState(myFrameStats[set]);
779 myConsoleWidget->setSelectedIndex(myConsole[set]);
780 // Randomization
781 myRandomBankWidget->setState(myRandomBank[set]);
782 myRandomizeRAMWidget->setState(myRandomizeRAM[set]);
783
784 const string& cpurandom = myRandomizeCPU[set];
785 const char* const cpuregs[] = { "S", "A", "X", "Y", "P" };
786 for(int i = 0; i < 5; ++i)
787 myRandomizeCPUWidget[i]->setState(BSPF::containsIgnoreCase(cpurandom, cpuregs[i]));
788 // Undriven TIA pins
789 myUndrivenPinsWidget->setState(myUndrivenPins[set]);
790#ifdef DEBUGGER_SUPPORT
791 // Read from write ports break
792 myRWPortBreakWidget->setState(myRWPortBreak[set]);
793 myWRPortBreakWidget->setState(myWRPortBreak[set]);
794#endif
795 // Thumb ARM emulation exception
796 myThumbExceptionWidget->setState(myThumbException[set]);
797 // AtariVox/SaveKey EEPROM access
798 myEEPROMAccessWidget->setState(myEEPROMAccess[set]);
799 handleConsole();
800
801 // TIA tab
802 myTIATypeWidget->setSelected(myTIAType[set], "standard");
803 handleTia();
804
805 // Debug colors
806 myDebugColorsWidget->setState(myDebugColors[set]);
807 // PAL color-loss effect
808 myColorLossWidget->setState(myColorLoss[set]);
809 // Jitter
810 myTVJitterWidget->setState(myTVJitter[set]);
811 myTVJitterRecWidget->setValue(myTVJitterRec[set]);
812
813 handleTVJitterChange(myTVJitterWidget->getState());
814
815 // States
816 myTimeMachineWidget->setState(myTimeMachine[set]);
817 myStateSizeWidget->setValue(myStateSize[set]);
818 myUncompressedWidget->setValue(myUncompressed[set]);
819 myStateIntervalWidget->setSelected(myStateInterval[set]);
820 myStateHorizonWidget->setSelected(myStateHorizon[set]);
821
822 handleTimeMachine();
823 handleSize();
824 handleUncompressed();
825 handleInterval();
826 handleHorizon();
827}
828
829// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
830void DeveloperDialog::loadConfig()
831{
832 bool devSettings = instance().settings().getBool("dev.settings");
833 handleSettings(devSettings);
834 mySettings = devSettings;
835 mySettingsGroupEmulation->setSelected(devSettings ? 1 : 0);
836 mySettingsGroupTia->setSelected(devSettings ? 1 : 0);
837 mySettingsGroupVideo->setSelected(devSettings ? 1 : 0);
838 mySettingsGroupTM->setSelected(devSettings ? 1 : 0);
839
840 // load both setting sets...
841 loadSettings(SettingsSet::player);
842 loadSettings(SettingsSet::developer);
843 // ...and select the current one
844 setWidgetStates(SettingsSet(mySettingsGroupEmulation->getSelected()));
845
846 // Debug colours
847 handleDebugColours(instance().settings().getString("tia.dbgcolors"));
848
849 // Save on exit
850 string saveOnExit = instance().settings().getString("saveonexit");
851 mySaveOnExitGroup->setSelected(saveOnExit == "all" ? 2 : saveOnExit == "current" ? 1 : 0);
852
853#ifdef DEBUGGER_SUPPORT
854 uInt32 w, h;
855
856 // Debugger size
857 const Common::Size& ds = instance().settings().getSize("dbg.res");
858 w = ds.w; h = ds.h;
859
860 myDebuggerWidthSlider->setValue(w);
861 myDebuggerHeightSlider->setValue(h);
862
863 // Debugger font size
864 string size = instance().settings().getString("dbg.fontsize");
865 myDebuggerFontSize->setSelected(size, "medium");
866
867 // Debugger font style
868 int style = instance().settings().getInt("dbg.fontstyle");
869 myDebuggerFontStyle->setSelected(style, "0");
870
871 // Ghost reads trap
872 myGhostReadsTrapWidget->setState(instance().settings().getBool("dbg.ghostreadstrap"));
873
874 handleFontSize();
875#endif
876
877 myTab->loadConfig();
878}
879
880// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
881void DeveloperDialog::saveConfig()
882{
883 instance().settings().setValue("dev.settings", mySettingsGroupEmulation->getSelected() == SettingsSet::developer);
884 // copy current widget status into set...
885 getWidgetStates(SettingsSet(mySettingsGroupEmulation->getSelected()));
886 // ...and save both sets
887 saveSettings(SettingsSet::player);
888 saveSettings(SettingsSet::developer);
889
890 // activate the current settings
891 instance().frameBuffer().showFrameStats(myFrameStatsWidget->getState());
892 // jitter
893 if(instance().hasConsole())
894 {
895 instance().console().tia().toggleJitter(myTVJitterWidget->getState() ? 1 : 0);
896 instance().console().tia().setJitterRecoveryFactor(myTVJitterRecWidget->getValue());
897 }
898
899 // TIA tab
900 if(instance().hasConsole())
901 {
902 instance().console().tia().setPlInvertedPhaseClock(myPlInvPhaseWidget->getState());
903 instance().console().tia().setMsInvertedPhaseClock(myMsInvPhaseWidget->getState());
904 instance().console().tia().setBlInvertedPhaseClock(myBlInvPhaseWidget->getState());
905 instance().console().tia().setPFBitsDelay(myPFBitsWidget->getState());
906 instance().console().tia().setPFColorDelay(myPFColorWidget->getState());
907 instance().console().tia().setPlSwapDelay(myPlSwapWidget->getState());
908 instance().console().tia().setBlSwapDelay(myBlSwapWidget->getState());
909 }
910
911 handleEnableDebugColors();
912 // PAL color loss
913 if(instance().hasConsole())
914 instance().console().enableColorLoss(myColorLossWidget->getState());
915
916 // Debug colours
917 string dbgcolors;
918 for(int i = 0; i < DEBUG_COLORS; ++i)
919 dbgcolors += myDbgColour[i]->getSelectedTag().toString();
920 if(instance().hasConsole() &&
921 instance().console().tia().setFixedColorPalette(dbgcolors))
922 instance().settings().setValue("tia.dbgcolors", dbgcolors);
923
924 // update RewindManager
925 instance().state().rewindManager().setup();
926 instance().state().setRewindMode(myTimeMachineWidget->getState() ?
927 StateManager::Mode::TimeMachine : StateManager::Mode::Off);
928
929 // Save on exit
930 int saveOnExit = mySaveOnExitGroup->getSelected();
931 instance().settings().setValue("saveonexit",
932 saveOnExit == 0 ? "none" : saveOnExit == 1 ? "current" : "all");
933
934#ifdef DEBUGGER_SUPPORT
935 // Debugger font style
936 instance().settings().setValue("dbg.fontstyle",
937 myDebuggerFontStyle->getSelectedTag().toString());
938 // Debugger size
939 instance().settings().setValue("dbg.res",
940 Common::Size(myDebuggerWidthSlider->getValue(),
941 myDebuggerHeightSlider->getValue()));
942 // Debugger font size
943 instance().settings().setValue("dbg.fontsize", myDebuggerFontSize->getSelectedTag().toString());
944
945 // Ghost reads trap
946 instance().settings().setValue("dbg.ghostreadstrap", myGhostReadsTrapWidget->getState());
947 if(instance().hasConsole())
948 instance().console().system().m6502().setGhostReadsTrap(myGhostReadsTrapWidget->getState());
949
950 // Read from write ports and write to read ports breaks
951 if (instance().hasConsole())
952 {
953 instance().console().system().m6502().setReadFromWritePortBreak(myRWPortBreakWidget->getState());
954 instance().console().system().m6502().setWriteToReadPortBreak(myWRPortBreakWidget->getState());
955 }
956#endif
957}
958
959// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
960void DeveloperDialog::setDefaults()
961{
962 bool devSettings = mySettings;
963 SettingsSet set = SettingsSet(mySettingsGroupEmulation->getSelected());
964
965 switch(myTab->getActiveTab())
966 {
967 case 0: // Emulation
968 myFrameStats[set] = devSettings ? true : false;
969 myConsole[set] = 0;
970 // Randomization
971 myRandomBank[set] = devSettings ? true : false;
972 myRandomizeRAM[set] = true;
973 myRandomizeCPU[set] = devSettings ? "SAXYP" : "AXYP";
974 // Undriven TIA pins
975 myUndrivenPins[set] = devSettings ? true : false;
976 #ifdef DEBUGGER_SUPPORT
977 // Reads from write ports
978 myRWPortBreak[set] = devSettings ? true : false;
979 myWRPortBreak[set] = devSettings ? true : false;
980 #endif
981 // Thumb ARM emulation exception
982 myThumbException[set] = devSettings ? true : false;
983 // AtariVox/SaveKey EEPROM access
984 myEEPROMAccess[set] = devSettings ? true : false;
985
986 setWidgetStates(set);
987 break;
988
989 case 1: // TIA
990 myTIAType[set] = "standard";
991 // reset "custom" mode
992 myPlInvPhase[set] = devSettings ? true : false;
993 myMsInvPhase[set] = devSettings ? true : false;
994 myBlInvPhase[set] = devSettings ? true : false;
995 myPFBits[set] = devSettings ? true : false;
996 myPFColor[set] = devSettings ? true : false;
997 myPlSwap[set] = devSettings ? true : false;
998 myBlSwap[set] = devSettings ? true : false;
999
1000 setWidgetStates(set);
1001 break;
1002
1003 case 2: // Video
1004 // Jitter
1005 myTVJitter[set] = true;
1006 myTVJitterRec[set] = devSettings ? 2 : 10;
1007 // PAL color-loss effect
1008 myColorLoss[set] = devSettings ? true : false;
1009 // Debug colors
1010 myDebugColors[set] = false;
1011 handleDebugColours("roygpb");
1012
1013 setWidgetStates(set);
1014 break;
1015
1016 case 3: // States
1017 myTimeMachine[set] = true;
1018 myStateSize[set] = devSettings ? 1000 : 200;
1019 myUncompressed[set] = devSettings ? 600 : 60;
1020 myStateInterval[set] = devSettings ? "1f" : "30f";
1021 myStateHorizon[set] = devSettings ? "30s" : "10m";
1022
1023 setWidgetStates(set);
1024 mySaveOnExitGroup->setSelected(0);
1025 break;
1026
1027 case 4: // Debugger options
1028 {
1029#ifdef DEBUGGER_SUPPORT
1030 uInt32 w = std::min(instance().frameBuffer().desktopSize().w, uInt32(DebuggerDialog::kMediumFontMinW));
1031 uInt32 h = std::min(instance().frameBuffer().desktopSize().h, uInt32(DebuggerDialog::kMediumFontMinH));
1032 myDebuggerWidthSlider->setValue(w);
1033 myDebuggerHeightSlider->setValue(h);
1034 myDebuggerFontSize->setSelected("medium");
1035 myDebuggerFontStyle->setSelected("0");
1036
1037 myGhostReadsTrapWidget->setState(true);
1038
1039 handleFontSize();
1040#endif
1041 break;
1042 }
1043
1044 default:
1045 break;
1046 }
1047}
1048
1049// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1050void DeveloperDialog::handleCommand(CommandSender* sender, int cmd, int data, int id)
1051{
1052 switch(cmd)
1053 {
1054 case kPlrSettings:
1055 handleSettings(false);
1056 break;
1057
1058 case kDevSettings:
1059 handleSettings(true);
1060 break;
1061
1062 case kTIAType:
1063 handleTia();
1064 break;
1065
1066 case kConsole:
1067 handleConsole();
1068 break;
1069
1070 case kTVJitter:
1071 handleTVJitterChange(myTVJitterWidget->getState());
1072 break;
1073
1074 case kTVJitterChanged:
1075 myTVJitterRecLabelWidget->setValue(myTVJitterRecWidget->getValue());
1076 break;
1077
1078 case kPPinCmd:
1079 instance().console().tia().driveUnusedPinsRandom(myUndrivenPinsWidget->getState());
1080 break;
1081
1082 case kTimeMachine:
1083 handleTimeMachine();
1084 break;
1085
1086 case kSizeChanged:
1087 handleSize();
1088 break;
1089
1090 case kUncompressedChanged:
1091 handleUncompressed();
1092 break;
1093
1094 case kIntervalChanged:
1095 handleInterval();
1096 break;
1097
1098 case kHorizonChanged:
1099 handleHorizon();
1100 break;
1101
1102 case kP0ColourChangedCmd:
1103 handleDebugColours(0, myDbgColour[0]->getSelected());
1104 break;
1105
1106 case kM0ColourChangedCmd:
1107 handleDebugColours(1, myDbgColour[1]->getSelected());
1108 break;
1109
1110 case kP1ColourChangedCmd:
1111 handleDebugColours(2, myDbgColour[2]->getSelected());
1112 break;
1113
1114 case kM1ColourChangedCmd:
1115 handleDebugColours(3, myDbgColour[3]->getSelected());
1116 break;
1117
1118 case kPFColourChangedCmd:
1119 handleDebugColours(4, myDbgColour[4]->getSelected());
1120 break;
1121
1122 case kBLColourChangedCmd:
1123 handleDebugColours(5, myDbgColour[5]->getSelected());
1124 break;
1125
1126#ifdef DEBUGGER_SUPPORT
1127 case kDFontSizeChanged:
1128 handleFontSize();
1129 break;
1130#endif
1131
1132 case GuiObject::kOKCmd:
1133 saveConfig();
1134 close();
1135 break;
1136
1137 case GuiObject::kCloseCmd:
1138 // Revert changes made to event mapping
1139 close();
1140 break;
1141
1142 case GuiObject::kDefaultsCmd:
1143 setDefaults();
1144 break;
1145
1146 default:
1147 Dialog::handleCommand(sender, cmd, data, 0);
1148 }
1149}
1150
1151// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1152void DeveloperDialog::handleSettings(bool devSettings)
1153{
1154 myUndrivenPinsWidget->setEnabled(devSettings);
1155#ifdef DEBUGGER_SUPPORT
1156 myRWPortBreakWidget->setEnabled(devSettings);
1157 myWRPortBreakWidget->setEnabled(devSettings);
1158#endif
1159 myThumbExceptionWidget->setEnabled(devSettings);
1160
1161 if (mySettings != devSettings)
1162 {
1163 mySettings = devSettings; // block redundant events first!
1164 SettingsSet set = devSettings ? SettingsSet::developer : SettingsSet::player;
1165 mySettingsGroupEmulation->setSelected(set);
1166 mySettingsGroupTia->setSelected(set);
1167 mySettingsGroupVideo->setSelected(set);
1168 mySettingsGroupTM->setSelected(set);
1169 getWidgetStates(devSettings ? SettingsSet::player : SettingsSet::developer);
1170 setWidgetStates(set);
1171 }
1172}
1173
1174// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1175void DeveloperDialog::handleTVJitterChange(bool enable)
1176{
1177 myTVJitterRecWidget->setEnabled(enable);
1178 myTVJitterRecLabelWidget->setEnabled(enable);
1179}
1180
1181// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1182void DeveloperDialog::handleEnableDebugColors()
1183{
1184 if(instance().hasConsole())
1185 {
1186 bool fixed = instance().console().tia().usingFixedColors();
1187 if(fixed != myDebugColorsWidget->getState())
1188 instance().console().tia().toggleFixedColors();
1189 }
1190}
1191
1192// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1193void DeveloperDialog::handleConsole()
1194{
1195 bool is7800 = myConsoleWidget->getSelected() == 1;
1196
1197 myRandomizeRAMWidget->setEnabled(!is7800);
1198 if(is7800)
1199 myRandomizeRAMWidget->setState(false);
1200}
1201
1202// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1203void DeveloperDialog::handleTia()
1204{
1205 bool enable = BSPF::equalsIgnoreCase("custom", myTIATypeWidget->getSelectedTag().toString());
1206
1207 myTIATypeWidget->setEnabled(mySettings);
1208 myInvPhaseLabel->setEnabled(enable);
1209 myPlInvPhaseWidget->setEnabled(enable);
1210 myMsInvPhaseWidget->setEnabled(enable);
1211 myBlInvPhaseWidget->setEnabled(enable);
1212 myPlayfieldLabel->setEnabled(enable);
1213 myPFBitsWidget->setEnabled(enable);
1214 myPFColorWidget->setEnabled(enable);
1215 mySwapLabel->setEnabled(enable);
1216 myPlSwapWidget->setEnabled(enable);
1217 myBlSwapWidget->setEnabled(enable);
1218
1219 if(BSPF::equalsIgnoreCase("custom", myTIATypeWidget->getSelectedTag().toString()))
1220 {
1221 myPlInvPhaseWidget->setState(myPlInvPhase[SettingsSet::developer]);
1222 myMsInvPhaseWidget->setState(myMsInvPhase[SettingsSet::developer]);
1223 myBlInvPhaseWidget->setState(myBlInvPhase[SettingsSet::developer]);
1224 myPFBitsWidget->setState(myPFBits[SettingsSet::developer]);
1225 myPFColorWidget->setState(myPFColor[SettingsSet::developer]);
1226 myPlSwapWidget->setState(myPlSwap[SettingsSet::developer]);
1227 myBlSwapWidget->setState(myBlSwap[SettingsSet::developer]);
1228 }
1229 else
1230 {
1231 myPlInvPhaseWidget->setState(BSPF::equalsIgnoreCase("koolaidman", myTIATypeWidget->getSelectedTag().toString()));
1232 myMsInvPhaseWidget->setState(BSPF::equalsIgnoreCase("cosmicark", myTIATypeWidget->getSelectedTag().toString()));
1233 myBlInvPhaseWidget->setState(false);
1234 myPFBitsWidget->setState(BSPF::equalsIgnoreCase("pesco", myTIATypeWidget->getSelectedTag().toString()));
1235 myPFColorWidget->setState(BSPF::equalsIgnoreCase("quickstep", myTIATypeWidget->getSelectedTag().toString()));
1236 myPlSwapWidget->setState(BSPF::equalsIgnoreCase("heman", myTIATypeWidget->getSelectedTag().toString()));
1237 myBlSwapWidget->setState(false);
1238 }
1239}
1240
1241// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1242void DeveloperDialog::handleTimeMachine()
1243{
1244 bool enable = myTimeMachineWidget->getState();
1245
1246 myStateSizeWidget->setEnabled(enable);
1247 myUncompressedWidget->setEnabled(enable);
1248 myStateIntervalWidget->setEnabled(enable);
1249
1250 uInt32 size = myStateSizeWidget->getValue();
1251 uInt32 uncompressed = myUncompressedWidget->getValue();
1252
1253 myStateHorizonWidget->setEnabled(enable && size > uncompressed);
1254}
1255
1256// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1257void DeveloperDialog::handleSize()
1258{
1259 uInt32 size = myStateSizeWidget->getValue();
1260 uInt32 uncompressed = myUncompressedWidget->getValue();
1261 Int32 interval = myStateIntervalWidget->getSelected();
1262 Int32 horizon = myStateHorizonWidget->getSelected();
1263 bool found = false;
1264 Int32 i;
1265
1266 // handle illegal values
1267 if(interval == -1)
1268 interval = 0;
1269 if(horizon == -1)
1270 horizon = 0;
1271
1272 // adapt horizon and interval
1273 do
1274 {
1275 for(i = horizon; i < NUM_HORIZONS; ++i)
1276 {
1277 if(uInt64(size) * instance().state().rewindManager().INTERVAL_CYCLES[interval]
1278 <= instance().state().rewindManager().HORIZON_CYCLES[i])
1279 {
1280 found = true;
1281 break;
1282 }
1283 }
1284 if(!found)
1285 --interval;
1286 } while(!found);
1287
1288 if(size < uncompressed)
1289 myUncompressedWidget->setValue(size);
1290 myStateIntervalWidget->setSelectedIndex(interval);
1291 myStateHorizonWidget->setSelectedIndex(i);
1292 myStateHorizonWidget->setEnabled(myTimeMachineWidget->getState() && size > uncompressed);
1293}
1294
1295// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1296void DeveloperDialog::handleUncompressed()
1297{
1298 uInt32 size = myStateSizeWidget->getValue();
1299 uInt32 uncompressed = myUncompressedWidget->getValue();
1300
1301 if(size < uncompressed)
1302 myStateSizeWidget->setValue(uncompressed);
1303 myStateHorizonWidget->setEnabled(myTimeMachineWidget->getState() && size > uncompressed);
1304}
1305
1306// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1307void DeveloperDialog::handleInterval()
1308{
1309 uInt32 size = myStateSizeWidget->getValue();
1310 uInt32 uncompressed = myUncompressedWidget->getValue();
1311 Int32 interval = myStateIntervalWidget->getSelected();
1312 Int32 horizon = myStateHorizonWidget->getSelected();
1313 bool found = false;
1314 Int32 i;
1315
1316 // handle illegal values
1317 if(interval == -1)
1318 interval = 0;
1319 if(horizon == -1)
1320 horizon = 0;
1321
1322 // adapt horizon and size
1323 do
1324 {
1325 for(i = horizon; i < NUM_HORIZONS; ++i)
1326 {
1327 if(uInt64(size) * instance().state().rewindManager().INTERVAL_CYCLES[interval]
1328 <= instance().state().rewindManager().HORIZON_CYCLES[i])
1329 {
1330 found = true;
1331 break;
1332 }
1333 }
1334 if(!found)
1335 size -= myStateSizeWidget->getStepValue();
1336 } while(!found);
1337
1338 myStateHorizonWidget->setSelectedIndex(i);
1339 myStateSizeWidget->setValue(size);
1340 if(size < uncompressed)
1341 myUncompressedWidget->setValue(size);
1342}
1343
1344// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1345void DeveloperDialog::handleHorizon()
1346{
1347 uInt32 size = myStateSizeWidget->getValue();
1348 uInt32 uncompressed = myUncompressedWidget->getValue();
1349 Int32 interval = myStateIntervalWidget->getSelected();
1350 Int32 horizon = myStateHorizonWidget->getSelected();
1351 bool found = false;
1352 Int32 i;
1353
1354 // handle illegal values
1355 if(interval == -1)
1356 interval = 0;
1357 if(horizon == -1)
1358 horizon = 0;
1359
1360 // adapt interval and size
1361 do
1362 {
1363 for(i = interval; i >= 0; --i)
1364 {
1365 if(uInt64(size) * instance().state().rewindManager().INTERVAL_CYCLES[i]
1366 <= instance().state().rewindManager().HORIZON_CYCLES[horizon])
1367 {
1368 found = true;
1369 break;
1370 }
1371 }
1372 if(!found)
1373 size -= myStateSizeWidget->getStepValue();
1374 } while(!found);
1375
1376 myStateIntervalWidget->setSelectedIndex(i);
1377 myStateSizeWidget->setValue(size);
1378 if(size < uncompressed)
1379 myUncompressedWidget->setValue(size);
1380}
1381
1382// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1383void DeveloperDialog::handleDebugColours(int idx, int color)
1384{
1385 if(idx < 0 || idx >= DEBUG_COLORS)
1386 return;
1387
1388 if(!instance().hasConsole())
1389 {
1390 myDbgColour[idx]->clearFlags(Widget::FLAG_ENABLED);
1391 myDbgColourSwatch[idx]->clearFlags(Widget::FLAG_ENABLED);
1392 return;
1393 }
1394
1395 static constexpr ColorId dbg_color[3][DEBUG_COLORS] = {
1396 {
1397 TIA::FixedColor::NTSC_RED,
1398 TIA::FixedColor::NTSC_ORANGE,
1399 TIA::FixedColor::NTSC_YELLOW,
1400 TIA::FixedColor::NTSC_GREEN,
1401 TIA::FixedColor::NTSC_PURPLE,
1402 TIA::FixedColor::NTSC_BLUE
1403 },
1404 {
1405 TIA::FixedColor::PAL_RED,
1406 TIA::FixedColor::PAL_ORANGE,
1407 TIA::FixedColor::PAL_YELLOW,
1408 TIA::FixedColor::PAL_GREEN,
1409 TIA::FixedColor::PAL_PURPLE,
1410 TIA::FixedColor::PAL_BLUE
1411 },
1412 {
1413 TIA::FixedColor::SECAM_RED,
1414 TIA::FixedColor::SECAM_ORANGE,
1415 TIA::FixedColor::SECAM_YELLOW,
1416 TIA::FixedColor::SECAM_GREEN,
1417 TIA::FixedColor::SECAM_PURPLE,
1418 TIA::FixedColor::SECAM_BLUE
1419 }
1420 };
1421
1422 int timing = instance().console().timing() == ConsoleTiming::ntsc ? 0
1423 : instance().console().timing() == ConsoleTiming::pal ? 1 : 2;
1424
1425 myDbgColourSwatch[idx]->setColor(dbg_color[timing][color]);
1426 myDbgColour[idx]->setSelectedIndex(color);
1427
1428 // make sure the selected debug colors are all different
1429 bool usedCol[DEBUG_COLORS];
1430
1431 // identify used colors
1432 for(int i = 0; i < DEBUG_COLORS; ++i)
1433 {
1434 usedCol[i] = false;
1435 for(int j = 0; j < DEBUG_COLORS; ++j)
1436 {
1437 if(myDbgColourSwatch[j]->getColor() == dbg_color[timing][i])
1438 {
1439 usedCol[i] = true;
1440 break;
1441 }
1442 }
1443 }
1444 // check if currently changed color was used somewhere else
1445 for(int i = 0; i < DEBUG_COLORS; ++i)
1446 {
1447 if (i != idx && myDbgColourSwatch[i]->getColor() == dbg_color[timing][color])
1448 {
1449 // if already used, change the other color to an unused one
1450 for(int j = 0; j < DEBUG_COLORS; ++j)
1451 {
1452 if(!usedCol[j])
1453 {
1454 myDbgColourSwatch[i]->setColor(dbg_color[timing][j]);
1455 myDbgColour[i]->setSelectedIndex(j);
1456 break;
1457 }
1458 }
1459 }
1460 }
1461}
1462
1463// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1464void DeveloperDialog::handleDebugColours(const string& colors)
1465{
1466 for(int i = 0; i < DEBUG_COLORS; ++i)
1467 {
1468 switch(colors[i])
1469 {
1470 case 'r': handleDebugColours(i, 0); break;
1471 case 'o': handleDebugColours(i, 1); break;
1472 case 'y': handleDebugColours(i, 2); break;
1473 case 'g': handleDebugColours(i, 3); break;
1474 case 'p': handleDebugColours(i, 4); break;
1475 case 'b': handleDebugColours(i, 5); break;
1476 default: break;
1477 }
1478 }
1479}
1480
1481// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1482void DeveloperDialog::handleFontSize()
1483{
1484#ifdef DEBUGGER_SUPPORT
1485 uInt32 minW, minH;
1486 int fontSize = myDebuggerFontSize->getSelected();
1487
1488 if(fontSize == 0)
1489 {
1490 minW = DebuggerDialog::kSmallFontMinW;
1491 minH = DebuggerDialog::kSmallFontMinH;
1492 }
1493 else if(fontSize == 1)
1494 {
1495 minW = DebuggerDialog::kMediumFontMinW;
1496 minH = DebuggerDialog::kMediumFontMinH;
1497 }
1498 else // large
1499 {
1500 minW = DebuggerDialog::kLargeFontMinW;
1501 minH = DebuggerDialog::kLargeFontMinH;
1502 }
1503 minW = std::min(instance().frameBuffer().desktopSize().w, minW);
1504 minH = std::min(instance().frameBuffer().desktopSize().h, minH);
1505
1506 myDebuggerWidthSlider->setMinValue(minW);
1507 if(minW > uInt32(myDebuggerWidthSlider->getValue()))
1508 myDebuggerWidthSlider->setValue(minW);
1509
1510 myDebuggerHeightSlider->setMinValue(minH);
1511 if(minH > uInt32(myDebuggerHeightSlider->getValue()))
1512 myDebuggerHeightSlider->setValue(minH);
1513#endif
1514}
1515