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-2021 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 "Settings.hxx"
19 #include "DataGridWidget.hxx"
20 #include "EditTextWidget.hxx"
21 #include "FrameBuffer.hxx"
22 #include "GuiObject.hxx"
23 #include "OSystem.hxx"
24 #include "Debugger.hxx"
25 #include "RiotDebug.hxx"
26 #include "PopUpWidget.hxx"
27 #include "ToggleBitWidget.hxx"
28 #include "Widget.hxx"
29 
30 #include "NullControlWidget.hxx"
31 #include "JoystickWidget.hxx"
32 #include "PaddleWidget.hxx"
33 #include "BoosterWidget.hxx"
34 #include "DrivingWidget.hxx"
35 #include "GenesisWidget.hxx"
36 #include "KeyboardWidget.hxx"
37 #include "AtariVoxWidget.hxx"
38 #include "SaveKeyWidget.hxx"
39 #include "AmigaMouseWidget.hxx"
40 #include "AtariMouseWidget.hxx"
41 #include "TrakBallWidget.hxx"
42 #include "QuadTariWidget.hxx"
43 
44 #include "RiotWidget.hxx"
45 
46 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RiotWidget(GuiObject * boss,const GUI::Font & lfont,const GUI::Font & nfont,int x,int y,int w,int h)47 RiotWidget::RiotWidget(GuiObject* boss, const GUI::Font& lfont,
48                        const GUI::Font& nfont,
49                        int x, int y, int w, int h)
50   : Widget(boss, lfont, x, y, w, h),
51     CommandSender(boss)
52 {
53   const int fontWidth  = lfont.getMaxCharWidth(),
54             fontHeight = lfont.getFontHeight(),
55             lineHeight = lfont.getLineHeight();
56   int xpos = 10, ypos = 25, lwidth = 8 * fontWidth, col = 0;
57   StaticTextWidget* t;
58   VariantList items;
59 
60   // Set the strings to be used in the various bit registers
61   // We only do this once because it's the state that changes, not the strings
62   StringList off, on;
63   for(int i = 0; i < 8; ++i)
64   {
65     off.push_back("0");
66     on.push_back("1");
67   }
68 
69   StringList labels;
70 
71 #define CREATE_IO_REGS(desc, bits, bitsID, editable)                     \
72   t = new StaticTextWidget(boss, lfont, xpos, ypos+2, lwidth, fontHeight,\
73                            desc);                                        \
74   xpos += t->getWidth() + 5;                                             \
75   bits = new ToggleBitWidget(boss, nfont, xpos, ypos, 8, 1, 1, labels);  \
76   bits->setTarget(this);                                                 \
77   bits->setID(bitsID);                                                   \
78   if(editable) addFocusWidget(bits); else bits->setEditable(false);      \
79   bits->setList(off, on);
80 
81   // SWCHA bits in 'poke' mode
82   labels.clear();
83   CREATE_IO_REGS("SWCHA(W)", mySWCHAWriteBits, kSWCHABitsID, true)
84   col = xpos + mySWCHAWriteBits->getWidth() + 25;  // remember this for adding widgets to the second column
85 
86   // SWACNT bits
87   xpos = 10;  ypos += lineHeight + 5;
88   CREATE_IO_REGS("SWACNT", mySWACNTBits, kSWACNTBitsID, true)
89 
90   // SWCHA bits in 'peek' mode
91   xpos = 10;  ypos += lineHeight + 5;
92   labels.clear();
93   labels.push_back("Left right");
94   labels.push_back("Left left");
95   labels.push_back("Left down");
96   labels.push_back("Left up");
97   labels.push_back("Right right");
98   labels.push_back("Right left");
99   labels.push_back("Right down");
100   labels.push_back("Right up");
101   CREATE_IO_REGS("SWCHA(R)", mySWCHAReadBits, kSWCHARBitsID, true)
102 
103   // SWCHB bits in 'poke' mode
104   xpos = 10;  ypos += 2 * lineHeight;
105   labels.clear();
106   CREATE_IO_REGS("SWCHB(W)", mySWCHBWriteBits, kSWCHBBitsID, true)
107 
108   // SWBCNT bits
109   xpos = 10;  ypos += lineHeight + 5;
110   CREATE_IO_REGS("SWBCNT", mySWBCNTBits, kSWBCNTBitsID, true)
111 
112   // SWCHB bits in 'peek' mode
113   xpos = 10;  ypos += lineHeight + 5;
114   labels.clear();
115   labels.push_back("Right difficulty");
116   labels.push_back("Left difficulty");
117   labels.push_back("");
118   labels.push_back("");
119   labels.push_back("Color/B+W");
120   labels.push_back("");
121   labels.push_back("Select");
122   labels.push_back("Reset");
123   CREATE_IO_REGS("SWCHB(R)", mySWCHBReadBits, kSWCHBRBitsID, true)
124 
125   // Timer registers (R/W)
126   static constexpr std::array<const char*, 4> writeNames = {
127     "TIM1T", "TIM8T", "TIM64T", "T1024T"
128   };
129   xpos = 10;  ypos += 2*lineHeight;
130   for(int row = 0; row < 4; ++row)
131   {
132     t = new StaticTextWidget(boss, lfont, xpos, ypos + row*lineHeight + 2,
133                              lwidth, fontHeight, writeNames[row], TextAlign::Left);
134   }
135   xpos += t->getWidth() + 5;
136   myTimWrite = new DataGridWidget(boss, nfont, xpos, ypos, 1, 4, 2, 8, Common::Base::Fmt::_16);
137   myTimWrite->setTarget(this);
138   myTimWrite->setID(kTimWriteID);
139   addFocusWidget(myTimWrite);
140 
141   t = new StaticTextWidget(boss, lfont, myTimWrite->getRight() + _fontWidth, ypos + 2 , "#");
142   myTimClocks = new DataGridWidget(boss, nfont, t->getRight() + _fontWidth / 2, ypos,
143                                    1, 1, 6, 30, Common::Base::Fmt::_10_6);
144   myTimClocks->setToolTip("Number of CPU cycles available for current timer interval.\n");
145   myTimClocks->setTarget(this);
146   myTimClocks->setEditable(false);
147 
148   // Timer registers (RO)
149   static constexpr std::array<const char*, 5> readNames = {
150     "INTIM", "TIMINT", "Total Clks", "INTIM Clks", "Divider  #"
151   };
152   xpos = 10;  ypos += myTimWrite->getHeight() + lineHeight / 2;
153   for(int row = 0; row < 5; ++row)
154   {
155     t = new StaticTextWidget(boss, lfont, xpos, ypos + row * lineHeight + 2,
156                              readNames[row]);
157   }
158   xpos += t->getWidth() + _fontWidth / 2;
159   myTimRead = new DataGridWidget(boss, nfont, xpos, ypos, 1, 4, 4, 30, Common::Base::Fmt::_16);
160   myTimRead->setToolTip(0, 1, "Timer interrupt flag in bit 7.\n");
161   myTimRead->setToolTip(0, 2, "Number of CPU cycles since last TIMxxT write.\n");
162   myTimRead->setTarget(this);
163   myTimRead->setEditable(false);
164 
165   ypos += myTimRead->getHeight() - 1;
166   myTimDivider = new DataGridWidget(boss, nfont, xpos, ypos, 1, 1, 4, 12, Common::Base::Fmt::_10_4);
167   myTimDivider->setTarget(this);
168   myTimDivider->setEditable(false);
169 
170   // Controller ports
171   xpos = col;  ypos = 10;
172   myLeftControl = addControlWidget(boss, lfont, xpos, ypos,
173       instance().console().leftController());
174   addToFocusList(myLeftControl->getFocusList());
175   xpos += myLeftControl->getWidth() + 15;
176   myRightControl = addControlWidget(boss, lfont, xpos, ypos,
177       instance().console().rightController());
178   addToFocusList(myRightControl->getFocusList());
179 
180   // TIA INPTx registers (R), left port
181   static constexpr std::array<const char*, 3> contLeftReadNames = {
182     "INPT0", "INPT1", "INPT4"
183   };
184   xpos = col;  ypos += myLeftControl->getHeight() + 2 * lineHeight;
185   for(int row = 0; row < 3; ++row)
186   {
187     new StaticTextWidget(boss, lfont, xpos, ypos + row*lineHeight + 2,
188                          5*fontWidth, fontHeight, contLeftReadNames[row], TextAlign::Left);
189   }
190   xpos += 5*fontWidth + 5;
191   myLeftINPT = new DataGridWidget(boss, nfont, xpos, ypos, 1, 3, 2, 8, Common::Base::Fmt::_16);
192   myLeftINPT->setTarget(this);
193   myLeftINPT->setEditable(false);
194 
195   // TIA INPTx registers (R), right port
196   static constexpr std::array<const char*, 3> contRightReadNames = {
197     "INPT2", "INPT3", "INPT5"
198   };
199   xpos = col + myLeftControl->getWidth() + 15;
200   for(int row = 0; row < 3; ++row)
201   {
202     new StaticTextWidget(boss, lfont, xpos, ypos + row*lineHeight + 2,
203                          5*fontWidth, fontHeight, contRightReadNames[row], TextAlign::Left);
204   }
205   xpos += 5*fontWidth + 5;
206   myRightINPT = new DataGridWidget(boss, nfont, xpos, ypos, 1, 3, 2, 8, Common::Base::Fmt::_16);
207   myRightINPT->setTarget(this);
208   myRightINPT->setEditable(false);
209 
210   // TIA INPTx VBLANK bits (D6-latch, D7-dump) (R)
211   xpos = col + 20;  ypos += myLeftINPT->getHeight() + lineHeight;
212   myINPTLatch = new CheckboxWidget(boss, lfont, xpos, ypos, "INPT latch (VBlank D6)");
213   myINPTLatch->setTarget(this);
214   myINPTLatch->setEditable(false);
215   ypos += lineHeight + 5;
216   myINPTDump = new CheckboxWidget(boss, lfont, xpos, ypos, "INPT dump to gnd (VBlank D7)");
217   myINPTDump->setTarget(this);
218   myINPTDump->setEditable(false);
219 
220   // PO & P1 difficulty switches
221   int pwidth = lfont.getStringWidth("B/easy");
222   lwidth = lfont.getStringWidth("Right Diff ");
223   xpos = col;  ypos += 2 * lineHeight;
224   int col2_ypos = ypos;
225   items.clear();
226   VarList::push_back(items, "B/easy", "b");
227   VarList::push_back(items, "A/hard", "a");
228   myP0Diff = new PopUpWidget(boss, lfont, xpos, ypos, pwidth, lineHeight, items,
229                              "Left Diff ", lwidth, kP0DiffChanged);
230   myP0Diff->setTarget(this);
231   addFocusWidget(myP0Diff);
232   ypos += myP0Diff->getHeight() + 5;
233   myP1Diff = new PopUpWidget(boss, lfont, xpos, ypos, pwidth, lineHeight, items,
234                              "Right Diff ", lwidth, kP1DiffChanged);
235   myP1Diff->setTarget(this);
236   addFocusWidget(myP1Diff);
237 
238   // TV Type
239   ypos += myP1Diff->getHeight() + 5;
240   items.clear();
241   VarList::push_back(items, "B&W", "bw");
242   VarList::push_back(items, "Color", "color");
243   myTVType = new PopUpWidget(boss, lfont, xpos, ypos, pwidth, lineHeight, items,
244                              "TV Type ", lwidth, kTVTypeChanged);
245   myTVType->setTarget(this);
246   addFocusWidget(myTVType);
247 
248   // 2600/7800 mode
249   lwidth = lfont.getStringWidth("Console") + 29;
250   pwidth = lfont.getStringWidth("Atari 2600") + 6;
251   new StaticTextWidget(boss, lfont, 10, ypos+1, "Console");
252   myConsole = new EditTextWidget(boss, lfont, 10 + lwidth, ypos - 1, pwidth, lineHeight);
253   myConsole->setEditable(false, true);
254   addFocusWidget(myConsole);
255 
256   // Select and Reset
257   xpos += myP0Diff->getWidth() + 20;  ypos = col2_ypos + 1;
258   mySelect = new CheckboxWidget(boss, lfont, xpos, ypos, "Select",
259                                 CheckboxWidget::kCheckActionCmd);
260   mySelect->setID(kSelectID);
261   mySelect->setTarget(this);
262   addFocusWidget(mySelect);
263 
264   ypos += myP0Diff->getHeight() + 5;
265   myReset = new CheckboxWidget(boss, lfont, xpos, ypos, "Reset",
266                                CheckboxWidget::kCheckActionCmd);
267   myReset->setID(kResetID);
268   myReset->setTarget(this);
269   addFocusWidget(myReset);
270 
271   ypos += myP0Diff->getHeight() + 5;
272   myPause = new CheckboxWidget(boss, lfont, xpos, ypos, "Pause",
273                                CheckboxWidget::kCheckActionCmd);
274   myPause->setID(kPauseID);
275   myPause->setTarget(this);
276   addFocusWidget(myPause);
277 
278   setHelpAnchor("IOTab", true);
279 }
280 
281 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
loadConfig()282 void RiotWidget::loadConfig()
283 {
284 #define IO_REGS_UPDATE(bits, s_bits)                          \
285   changed.clear();                                            \
286   for(uInt32 i = 0; i < state.s_bits.size(); ++i)             \
287     changed.push_back(state.s_bits[i] != oldstate.s_bits[i]); \
288   bits->setState(state.s_bits, changed);
289 
290   IntArray alist;
291   IntArray vlist;
292   BoolArray changed;
293 
294   // We push the enumerated items as addresses, and deal with the real
295   // address in the callback (handleCommand)
296   Debugger& dbg   = instance().debugger();
297   RiotDebug& riot = dbg.riotDebug();
298   const RiotState& state    = static_cast<const RiotState&>(riot.getState());
299   const RiotState& oldstate = static_cast<const RiotState&>(riot.getOldState());
300 
301   // Update the SWCHA register booleans (poke mode)
302   IO_REGS_UPDATE(mySWCHAWriteBits, swchaWriteBits)
303 
304   // Update the SWACNT register booleans
305   IO_REGS_UPDATE(mySWACNTBits, swacntBits)
306 
307   // Update the SWCHA register booleans (peek mode)
308   IO_REGS_UPDATE(mySWCHAReadBits, swchaReadBits)
309 
310   // Update the SWCHB register booleans (poke mode)
311   IO_REGS_UPDATE(mySWCHBWriteBits, swchbWriteBits)
312 
313   // Update the SWBCNT register booleans
314   IO_REGS_UPDATE(mySWBCNTBits, swbcntBits)
315 
316   // Update the SWCHB register booleans (peek mode)
317   IO_REGS_UPDATE(mySWCHBReadBits, swchbReadBits)
318 
319   // Update TIA INPTx registers
320   alist.clear();  vlist.clear();  changed.clear();
321   alist.push_back(0);  vlist.push_back(state.INPT0);
322     changed.push_back(state.INPT0 != oldstate.INPT0);
323   alist.push_back(1);  vlist.push_back(state.INPT1);
324     changed.push_back(state.INPT1 != oldstate.INPT1);
325   alist.push_back(4);  vlist.push_back(state.INPT4);
326     changed.push_back(state.INPT4 != oldstate.INPT4);
327   myLeftINPT->setList(alist, vlist, changed);
328   alist.clear();  vlist.clear();  changed.clear();
329   alist.push_back(2);  vlist.push_back(state.INPT2);
330     changed.push_back(state.INPT2 != oldstate.INPT2);
331   alist.push_back(3);  vlist.push_back(state.INPT3);
332     changed.push_back(state.INPT3 != oldstate.INPT3);
333   alist.push_back(5);  vlist.push_back(state.INPT5);
334     changed.push_back(state.INPT5 != oldstate.INPT5);
335   myRightINPT->setList(alist, vlist, changed);
336 
337   // Update TIA VBLANK bits
338   myINPTLatch->setState(riot.vblank(6), state.INPTLatch != oldstate.INPTLatch);
339   myINPTDump->setState(riot.vblank(7), state.INPTDump != oldstate.INPTDump);
340 
341   // Update timer write registers
342   alist.clear();  vlist.clear();  changed.clear();
343   alist.push_back(kTim1TID);  vlist.push_back(state.TIM1T);
344     changed.push_back(state.TIM1T != oldstate.TIM1T);
345   alist.push_back(kTim8TID);  vlist.push_back(state.TIM8T);
346     changed.push_back(state.TIM8T != oldstate.TIM8T);
347   alist.push_back(kTim64TID);  vlist.push_back(state.TIM64T);
348     changed.push_back(state.TIM64T != oldstate.TIM64T);
349   alist.push_back(kTim1024TID);  vlist.push_back(state.T1024T);
350     changed.push_back(state.T1024T != oldstate.T1024T);
351   myTimWrite->setList(alist, vlist, changed);
352 
353   alist.clear();  vlist.clear();  changed.clear();
354   alist.push_back(0);
355   if(state.TIM1T)
356     vlist.push_back((state.TIM1T  - 1) * 1);
357   else if(state.TIM8T)
358     vlist.push_back((state.TIM8T  - 1) * 8);
359   else if(state.TIM64T)
360     vlist.push_back((state.TIM64T - 1) * 64);
361   else if(state.T1024T)
362     vlist.push_back((state.T1024T - 1) * 1024);
363   else
364     vlist.push_back(0);
365   changed.push_back(state.TIM1T != oldstate.TIM1T ||
366                     state.TIM8T != oldstate.TIM8T ||
367                     state.TIM64T != oldstate.TIM64T ||
368                     state.T1024T != oldstate.T1024T);
369   myTimClocks->setList(alist, vlist, changed);
370 
371   // Update timer read registers
372   alist.clear();  vlist.clear();  changed.clear();
373   alist.push_back(0);  vlist.push_back(state.INTIM);
374     changed.push_back(state.INTIM != oldstate.INTIM);
375   alist.push_back(0);  vlist.push_back(state.TIMINT);
376     changed.push_back(state.TIMINT != oldstate.TIMINT);
377   alist.push_back(0);  vlist.push_back(state.TIMCLKS);
378     changed.push_back(state.TIMCLKS != oldstate.TIMCLKS);
379   alist.push_back(0);  vlist.push_back(state.INTIMCLKS);
380     changed.push_back(state.INTIMCLKS != oldstate.INTIMCLKS);
381   myTimRead->setList(alist, vlist, changed);
382 
383   alist.clear();  vlist.clear();  changed.clear();
384   alist.push_back(0);  vlist.push_back(state.TIMDIV);
385     changed.push_back(state.TIMDIV != oldstate.TIMDIV);
386   myTimDivider->setList(alist, vlist, changed);
387 
388   // Console switches (inverted, since 'selected' in the UI
389   // means 'grounded' in the system)
390   myP0Diff->setSelectedIndex(riot.diffP0(), state.swchbReadBits[1] != oldstate.swchbReadBits[1]);
391   myP1Diff->setSelectedIndex(riot.diffP1(), state.swchbReadBits[0] != oldstate.swchbReadBits[0]);
392 
393   bool devSettings = instance().settings().getBool("dev.settings");
394   myConsole->setText(instance().settings().getString(devSettings ? "dev.console" : "plr.console") == "7800" ? "Atari 7800" : "Atari 2600");
395   myConsole->setEditable(false, true);
396 
397   myTVType->setSelectedIndex(riot.tvType(), state.swchbReadBits[4] != oldstate.swchbReadBits[4]);
398   myPause->setState(!riot.tvType(), state.swchbReadBits[4] != oldstate.swchbReadBits[4]);
399   mySelect->setState(!riot.select(), state.swchbReadBits[6] != oldstate.swchbReadBits[6]);
400   myReset->setState(!riot.reset(), state.swchbReadBits[7] != oldstate.swchbReadBits[7]);
401 
402   myLeftControl->loadConfig();
403   myRightControl->loadConfig();
404 
405   handleConsole();
406 }
407 
408 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
handleCommand(CommandSender * sender,int cmd,int data,int id)409 void RiotWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
410 {
411   int value = -1;
412   RiotDebug& riot = instance().debugger().riotDebug();
413 
414   switch(cmd)
415   {
416     case DataGridWidget::kItemDataChangedCmd:
417       if(id == kTimWriteID)
418       {
419         switch(myTimWrite->getSelectedAddr())
420         {
421           case kTim1TID:
422             riot.tim1T(myTimWrite->getSelectedValue());
423             break;
424           case kTim8TID:
425             riot.tim8T(myTimWrite->getSelectedValue());
426             break;
427           case kTim64TID:
428             riot.tim64T(myTimWrite->getSelectedValue());
429             break;
430           case kTim1024TID:
431             riot.tim1024T(myTimWrite->getSelectedValue());
432             break;
433           default:
434             break;
435         }
436       }
437       break;
438 
439     case ToggleWidget::kItemDataChangedCmd:
440       switch(id)
441       {
442         case kSWCHABitsID:
443           value = Debugger::get_bits(mySWCHAWriteBits->getState());
444           riot.swcha(value & 0xff);
445           break;
446         case kSWACNTBitsID:
447           value = Debugger::get_bits(mySWACNTBits->getState());
448           riot.swacnt(value & 0xff);
449           break;
450         case kSWCHBBitsID:
451           value = Debugger::get_bits(mySWCHBWriteBits->getState());
452           riot.swchb(value & 0xff);
453           break;
454         case kSWBCNTBitsID:
455           value = Debugger::get_bits(mySWBCNTBits->getState());
456           riot.swbcnt(value & 0xff);
457           break;
458         case kSWCHARBitsID:
459         {
460           value = Debugger::get_bits(mySWCHAReadBits->getState());
461           ControllerLowLevel lport(instance().console().leftController());
462           ControllerLowLevel rport(instance().console().rightController());
463           lport.setPin(Controller::DigitalPin::One,   value & 0b00010000);
464           lport.setPin(Controller::DigitalPin::Two,   value & 0b00100000);
465           lport.setPin(Controller::DigitalPin::Three, value & 0b01000000);
466           lport.setPin(Controller::DigitalPin::Four,  value & 0b10000000);
467           rport.setPin(Controller::DigitalPin::One,   value & 0b00000001);
468           rport.setPin(Controller::DigitalPin::Two,   value & 0b00000010);
469           rport.setPin(Controller::DigitalPin::Three, value & 0b00000100);
470           rport.setPin(Controller::DigitalPin::Four,  value & 0b00001000);
471           break;
472         }
473         case kSWCHBRBitsID:
474         {
475           value = Debugger::get_bits(mySWCHBReadBits->getState());
476 
477           riot.reset( value & 0b00000001);
478           riot.select(value & 0b00000010);
479           riot.tvType(value & 0b00001000);
480           riot.diffP0(value & 0b01000000);
481           riot.diffP1(value & 0b10000000);
482           break;
483         }
484         default:
485           break;
486       }
487       break;
488 
489     case CheckboxWidget::kCheckActionCmd:
490       switch(id)
491       {
492         case kSelectID:
493           riot.select(!mySelect->getState());
494           break;
495         case kResetID:
496           riot.reset(!myReset->getState());
497           break;
498         case kPauseID:
499           handleConsole();
500           break;
501         default:
502           break;
503       }
504       break;
505 
506     case kP0DiffChanged:
507       riot.diffP0(myP0Diff->getSelectedTag().toString() != "b");
508       break;
509 
510     case kP1DiffChanged:
511       riot.diffP1(myP1Diff->getSelectedTag().toString() != "b");
512       break;
513 
514     case kTVTypeChanged:
515       handleConsole();
516       break;
517 
518     default:
519       break;
520   }
521 }
522 
523 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
addControlWidget(GuiObject * boss,const GUI::Font & font,int x,int y,Controller & controller)524 ControllerWidget* RiotWidget::addControlWidget(GuiObject* boss, const GUI::Font& font,
525         int x, int y, Controller& controller)
526 {
527   switch(controller.type())
528   {
529     case Controller::Type::AmigaMouse:
530       return new AmigaMouseWidget(boss, font, x, y, controller);
531     case Controller::Type::AtariMouse:
532       return new AtariMouseWidget(boss, font, x, y, controller);
533     case Controller::Type::AtariVox:
534       return new AtariVoxWidget(boss, font, x, y, controller);
535     case Controller::Type::BoosterGrip:
536       return new BoosterWidget(boss, font, x, y, controller);
537     case Controller::Type::Driving:
538       return new DrivingWidget(boss, font, x, y, controller);
539     case Controller::Type::Genesis:
540       return new GenesisWidget(boss, font, x, y, controller);
541     case Controller::Type::Joystick:
542       return new JoystickWidget(boss, font, x, y, controller);
543     case Controller::Type::Keyboard:
544       return new KeyboardWidget(boss, font, x, y, controller);
545 //    case Controller::Type::KidVid:      // TODO - implement this
546 //    case Controller::Type::MindLink:    // TODO - implement this
547 //    case Controller::Type::Lightgun:    // TODO - implement this
548     case Controller::Type::Paddles:
549       return new PaddleWidget(boss, font, x, y, controller);
550     case Controller::Type::SaveKey:
551       return new SaveKeyWidget(boss, font, x, y, controller);
552     case Controller::Type::TrakBall:
553       return new TrakBallWidget(boss, font, x, y, controller);
554     case Controller::Type::QuadTari:
555       return new QuadTariWidget(boss, font, x, y, controller);
556     default:
557       return new NullControlWidget(boss, font, x, y, controller);
558   }
559 }
560 
561 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
handleConsole()562 void RiotWidget::handleConsole()
563 {
564   RiotDebug& riot = instance().debugger().riotDebug();
565   bool devSettings = instance().settings().getBool("dev.settings");
566   bool is7800 = instance().settings().getString(devSettings ? "dev.console" : "plr.console") == "7800";
567 
568   myTVType->setEnabled(!is7800);
569   myPause->setEnabled(is7800);
570   if(is7800)
571     myTVType->setSelectedIndex(myPause->getState() ? 0 : 1);
572   else
573     myPause->setState(myTVType->getSelected() == 0);
574   riot.tvType(myTVType->getSelected());
575 }
576