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 <cassert>
19#include <stdexcept>
20
21#include "AtariVox.hxx"
22#include "Booster.hxx"
23#include "Cart.hxx"
24#include "Control.hxx"
25#include "Cart.hxx"
26#include "Driving.hxx"
27#include "Event.hxx"
28#include "EventHandler.hxx"
29#include "ControllerDetector.hxx"
30#include "Joystick.hxx"
31#include "Keyboard.hxx"
32#include "KidVid.hxx"
33#include "Genesis.hxx"
34#include "MindLink.hxx"
35#include "CompuMate.hxx"
36#include "M6502.hxx"
37#include "M6532.hxx"
38#include "TIA.hxx"
39#include "Paddles.hxx"
40#include "Props.hxx"
41#include "PropsSet.hxx"
42#include "SaveKey.hxx"
43#include "Settings.hxx"
44#include "Sound.hxx"
45#include "Switches.hxx"
46#include "System.hxx"
47#include "AmigaMouse.hxx"
48#include "AtariMouse.hxx"
49#include "TrakBall.hxx"
50#include "FrameBuffer.hxx"
51#include "TIASurface.hxx"
52#include "OSystem.hxx"
53#include "Serializable.hxx"
54#include "Serializer.hxx"
55#include "TimerManager.hxx"
56#include "Version.hxx"
57#include "TIAConstants.hxx"
58#include "FrameLayout.hxx"
59#include "AudioQueue.hxx"
60#include "AudioSettings.hxx"
61#include "frame-manager/FrameManager.hxx"
62#include "frame-manager/FrameLayoutDetector.hxx"
63#include "frame-manager/YStartDetector.hxx"
64
65#ifdef CHEATCODE_SUPPORT
66 #include "CheatManager.hxx"
67#endif
68#ifdef DEBUGGER_SUPPORT
69 #include "Debugger.hxx"
70#endif
71
72#include "Console.hxx"
73
74namespace {
75 constexpr uInt8 YSTART_EXTRA = 2;
76}
77
78// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
79Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
80 const Properties& props, AudioSettings& audioSettings)
81 : myOSystem(osystem),
82 myEvent(osystem.eventHandler().event()),
83 myProperties(props),
84 myCart(std::move(cart)),
85 myDisplayFormat(""), // Unknown TV format @ start
86 myCurrentFormat(0), // Unknown format @ start,
87 myAutodetectedYstart(0),
88 myYStartAutodetected(false),
89 myFormatAutodetected(false),
90 myUserPaletteDefined(false),
91 myConsoleTiming(ConsoleTiming::ntsc),
92 myAudioSettings(audioSettings)
93{
94 // Load user-defined palette for this ROM
95 loadUserPalette();
96
97 // Create subsystems for the console
98 my6502 = make_unique<M6502>(myOSystem.settings());
99 myRiot = make_unique<M6532>(*this, myOSystem.settings());
100 myTIA = make_unique<TIA>(*this, [this]() { return timing(); }, myOSystem.settings());
101 myFrameManager = make_unique<FrameManager>();
102 mySwitches = make_unique<Switches>(myEvent, myProperties, myOSystem.settings());
103
104 myTIA->setFrameManager(myFrameManager.get());
105
106 // Reinitialize the RNG
107 myOSystem.random().initSeed(static_cast<uInt32>(TimerManager::getTicks()));
108
109 // Construct the system and components
110 mySystem = make_unique<System>(myOSystem.random(), *my6502, *myRiot, *myTIA, *myCart);
111
112 // The real controllers for this console will be added later
113 // For now, we just add dummy joystick controllers, since autodetection
114 // runs the emulation for a while, and this may interfere with 'smart'
115 // controllers such as the AVox and SaveKey
116 myLeftControl = make_unique<Joystick>(Controller::Jack::Left, myEvent, *mySystem);
117 myRightControl = make_unique<Joystick>(Controller::Jack::Right, myEvent, *mySystem);
118
119 // Let the cart know how to query for the 'Cartridge.StartBank' property
120 myCart->setStartBankFromPropsFunc([this]() {
121 const string& startbank = myProperties.get(PropType::Cart_StartBank);
122 return startbank == EmptyString ? -1 : atoi(startbank.c_str());
123 });
124
125 // We can only initialize after all the devices/components have been created
126 mySystem->initialize();
127
128 // Auto-detect NTSC/PAL mode if it's requested
129 string autodetected = "";
130 myDisplayFormat = myProperties.get(PropType::Display_Format);
131
132 // Add the real controllers for this system
133 // This must be done before the debugger is initialized
134 const string& md5 = myProperties.get(PropType::Cart_MD5);
135 setControllers(md5);
136
137 // Mute audio and clear framebuffer while autodetection runs
138 myOSystem.sound().mute(1);
139 myOSystem.frameBuffer().clear();
140
141 if(myDisplayFormat == "AUTO" || myOSystem.settings().getBool("rominfo"))
142 {
143 autodetectFrameLayout();
144
145 if(myProperties.get(PropType::Display_Format) == "AUTO")
146 {
147 autodetected = "*";
148 myCurrentFormat = 0;
149 myFormatAutodetected = true;
150 }
151 }
152
153 if (atoi(myProperties.get(PropType::Display_YStart).c_str()) == 0) {
154 autodetectYStart();
155 }
156
157 myConsoleInfo.DisplayFormat = myDisplayFormat + autodetected;
158
159 // Set up the correct properties used when toggling format
160 // Note that this can be overridden if a format is forced
161 // For example, if a PAL ROM is forced to be NTSC, it will use NTSC-like
162 // properties (60Hz, 262 scanlines, etc), but likely result in flicker
163 if(myDisplayFormat == "NTSC")
164 {
165 myCurrentFormat = 1;
166 myConsoleTiming = ConsoleTiming::ntsc;
167 }
168 else if(myDisplayFormat == "PAL")
169 {
170 myCurrentFormat = 2;
171 myConsoleTiming = ConsoleTiming::pal;
172 }
173 else if(myDisplayFormat == "SECAM")
174 {
175 myCurrentFormat = 3;
176 myConsoleTiming = ConsoleTiming::secam;
177 }
178 else if(myDisplayFormat == "NTSC50")
179 {
180 myCurrentFormat = 4;
181 myConsoleTiming = ConsoleTiming::ntsc;
182 }
183 else if(myDisplayFormat == "PAL60")
184 {
185 myCurrentFormat = 5;
186 myConsoleTiming = ConsoleTiming::pal;
187 }
188 else if(myDisplayFormat == "SECAM60")
189 {
190 myCurrentFormat = 6;
191 myConsoleTiming = ConsoleTiming::secam;
192 }
193
194 setTIAProperties();
195
196 bool joyallow4 = myOSystem.settings().getBool("joyallow4");
197 myOSystem.eventHandler().allowAllDirections(joyallow4);
198
199 // Reset the system to its power-on state
200 mySystem->reset();
201 myRiot->update();
202
203 // Finally, add remaining info about the console
204 myConsoleInfo.CartName = myProperties.get(PropType::Cart_Name);
205 myConsoleInfo.CartMD5 = myProperties.get(PropType::Cart_MD5);
206 bool swappedPorts = properties().get(PropType::Console_SwapPorts) == "YES";
207 myConsoleInfo.Control0 = myLeftControl->about(swappedPorts);
208 myConsoleInfo.Control1 = myRightControl->about(swappedPorts);
209 myConsoleInfo.BankSwitch = myCart->about();
210
211 // Some carts have an associated nvram file
212 myCart->setNVRamFile(myOSystem.nvramDir(), myConsoleInfo.CartName);
213
214 // Let the other devices know about the new console
215 mySystem->consoleChanged(myConsoleTiming);
216}
217
218// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
219Console::~Console()
220{
221 // Some smart controllers need to be informed that the console is going away
222 myLeftControl->close();
223 myRightControl->close();
224
225 // Close audio to prevent invalid access to myConsoleTiming from the audio
226 // callback
227 myOSystem.sound().close();
228}
229
230// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
231void Console::autodetectFrameLayout(bool reset)
232{
233 // Run the TIA, looking for PAL scanline patterns
234 // We turn off the SuperCharger progress bars, otherwise the SC BIOS
235 // will take over 250 frames!
236 // The 'fastscbios' option must be changed before the system is reset
237 bool fastscbios = myOSystem.settings().getBool("fastscbios");
238 myOSystem.settings().setValue("fastscbios", true);
239
240 FrameLayoutDetector frameLayoutDetector;
241 myTIA->setFrameManager(&frameLayoutDetector);
242
243 if (reset) {
244 mySystem->reset(true);
245 myRiot->update();
246 }
247
248 for(int i = 0; i < 60; ++i) myTIA->update();
249
250 myTIA->setFrameManager(myFrameManager.get());
251
252 myDisplayFormat = frameLayoutDetector.detectedLayout() == FrameLayout::pal ? "PAL" : "NTSC";
253
254 // Don't forget to reset the SC progress bars again
255 myOSystem.settings().setValue("fastscbios", fastscbios);
256}
257
258// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
259void Console::redetectFrameLayout()
260{
261 Serializer s;
262
263 myOSystem.sound().close();
264 save(s);
265
266 autodetectFrameLayout(false);
267 if (myYStartAutodetected) autodetectYStart();
268
269 load(s);
270 initializeAudio();
271}
272
273// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
274void Console::autodetectYStart(bool reset)
275{
276 // We turn off the SuperCharger progress bars, otherwise the SC BIOS
277 // will take over 250 frames!
278 // The 'fastscbios' option must be changed before the system is reset
279 bool fastscbios = myOSystem.settings().getBool("fastscbios");
280 myOSystem.settings().setValue("fastscbios", true);
281
282 YStartDetector ystartDetector;
283 ystartDetector.setLayout(myDisplayFormat == "PAL" ? FrameLayout::pal : FrameLayout::ntsc);
284 myTIA->setFrameManager(&ystartDetector);
285
286 if (reset) {
287 mySystem->reset(true);
288 myRiot->update();
289 }
290
291 for (int i = 0; i < 80; i++) myTIA->update();
292
293 myTIA->setFrameManager(myFrameManager.get());
294
295 myAutodetectedYstart = ystartDetector.detectedYStart() - YSTART_EXTRA;
296
297 // Don't forget to reset the SC progress bars again
298 myOSystem.settings().setValue("fastscbios", fastscbios);
299}
300
301// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
302void Console::redetectYStart()
303{
304 Serializer s;
305
306 myOSystem.sound().close();
307 save(s);
308
309 autodetectYStart(false);
310
311 load(s);
312 initializeAudio();
313}
314
315// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
316bool Console::save(Serializer& out) const
317{
318 try
319 {
320 // First save state for the system
321 if(!mySystem->save(out))
322 return false;
323
324 // Now save the console controllers and switches
325 if(!(myLeftControl->save(out) && myRightControl->save(out) &&
326 mySwitches->save(out)))
327 return false;
328 }
329 catch(...)
330 {
331 cerr << "ERROR: Console::save" << endl;
332 return false;
333 }
334
335 return true; // success
336}
337
338// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
339bool Console::load(Serializer& in)
340{
341 try
342 {
343 // First load state for the system
344 if(!mySystem->load(in))
345 return false;
346
347 // Then load the console controllers and switches
348 if(!(myLeftControl->load(in) && myRightControl->load(in) &&
349 mySwitches->load(in)))
350 return false;
351 }
352 catch(...)
353 {
354 cerr << "ERROR: Console::load" << endl;
355 return false;
356 }
357
358 return true; // success
359}
360
361// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
362void Console::toggleFormat(int direction)
363{
364 string saveformat, message;
365 uInt32 format = myCurrentFormat;
366
367 if(direction == 1)
368 format = (myCurrentFormat + 1) % 7;
369 else if(direction == -1)
370 format = myCurrentFormat > 0 ? (myCurrentFormat - 1) : 6;
371
372 setFormat(format);
373}
374
375// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
376void Console::setFormat(uInt32 format)
377{
378 if(myCurrentFormat == format)
379 return;
380
381 string saveformat, message;
382 string autodetected = "";
383
384 myCurrentFormat = format;
385 switch(myCurrentFormat)
386 {
387 case 0: // auto-detect
388 {
389 if (myFormatAutodetected) return;
390
391 string oldDisplayFormat = myDisplayFormat;
392 redetectFrameLayout();
393 myFormatAutodetected = true;
394 saveformat = "AUTO";
395 autodetected = "*";
396 myConsoleTiming = myDisplayFormat == "PAL" ? ConsoleTiming::pal : ConsoleTiming::ntsc;
397 message = "Auto-detect mode: " + myDisplayFormat;
398 break;
399 }
400 case 1:
401 saveformat = myDisplayFormat = "NTSC";
402 myConsoleTiming = ConsoleTiming::ntsc;
403 message = "NTSC mode";
404 myFormatAutodetected = false;
405 break;
406 case 2:
407 saveformat = myDisplayFormat = "PAL";
408 myConsoleTiming = ConsoleTiming::pal;
409 message = "PAL mode";
410 myFormatAutodetected = false;
411 break;
412 case 3:
413 saveformat = myDisplayFormat = "SECAM";
414 myConsoleTiming = ConsoleTiming::secam;
415 message = "SECAM mode";
416 myFormatAutodetected = false;
417 break;
418 case 4:
419 saveformat = myDisplayFormat = "NTSC50";
420 myConsoleTiming = ConsoleTiming::ntsc;
421 message = "NTSC50 mode";
422 myFormatAutodetected = false;
423 break;
424 case 5:
425 saveformat = myDisplayFormat = "PAL60";
426 myConsoleTiming = ConsoleTiming::pal;
427 message = "PAL60 mode";
428 myFormatAutodetected = false;
429 break;
430 case 6:
431 saveformat = myDisplayFormat = "SECAM60";
432 myConsoleTiming = ConsoleTiming::secam;
433 message = "SECAM60 mode";
434 myFormatAutodetected = false;
435 break;
436 }
437 myProperties.set(PropType::Display_Format, saveformat);
438
439 myConsoleInfo.DisplayFormat = myDisplayFormat + autodetected;
440
441 setPalette(myOSystem.settings().getString("palette"));
442 setTIAProperties();
443 initializeVideo(); // takes care of refreshing the screen
444 initializeAudio(); // ensure that audio synthesis is set up to match emulation speed
445 myOSystem.resetFps(); // Reset FPS measurement
446
447 myOSystem.frameBuffer().showMessage(message);
448
449 // Let the other devices know about the console change
450 mySystem->consoleChanged(myConsoleTiming);
451}
452
453// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
454void Console::toggleColorLoss()
455{
456 bool colorloss = !myTIA->colorLossEnabled();
457 if(myTIA->enableColorLoss(colorloss))
458 {
459 myOSystem.settings().setValue(
460 myOSystem.settings().getBool("dev.settings") ? "dev.colorloss" : "plr.colorloss", colorloss);
461
462 string message = string("PAL color-loss ") +
463 (colorloss ? "enabled" : "disabled");
464 myOSystem.frameBuffer().showMessage(message);
465 }
466 else
467 myOSystem.frameBuffer().showMessage(
468 "PAL color-loss not available in non PAL modes");
469}
470
471// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
472void Console::enableColorLoss(bool state)
473{
474 myTIA->enableColorLoss(state);
475}
476
477// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
478void Console::togglePalette()
479{
480 string palette, message;
481 palette = myOSystem.settings().getString("palette");
482
483 if(palette == "standard") // switch to z26
484 {
485 palette = "z26";
486 message = "Z26 palette";
487 }
488 else if(palette == "z26") // switch to user or standard
489 {
490 // If we have a user-defined palette, it will come next in
491 // the sequence; otherwise loop back to the standard one
492 if(myUserPaletteDefined)
493 {
494 palette = "user";
495 message = "User-defined palette";
496 }
497 else
498 {
499 palette = "standard";
500 message = "Standard Stella palette";
501 }
502 }
503 else if(palette == "user") // switch to standard
504 {
505 palette = "standard";
506 message = "Standard Stella palette";
507 }
508 else // switch to standard mode if we get this far
509 {
510 palette = "standard";
511 message = "Standard Stella palette";
512 }
513
514 myOSystem.settings().setValue("palette", palette);
515 myOSystem.frameBuffer().showMessage(message);
516
517 setPalette(palette);
518}
519
520// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
521void Console::setPalette(const string& type)
522{
523 // Look at all the palettes, since we don't know which one is
524 // currently active
525 static uInt32* palettes[3][3] = {
526 { &ourNTSCPalette[0], &ourPALPalette[0], &ourSECAMPalette[0] },
527 { &ourNTSCPaletteZ26[0], &ourPALPaletteZ26[0], &ourSECAMPaletteZ26[0] },
528 { &ourUserNTSCPalette[0], &ourUserPALPalette[0], &ourUserSECAMPalette[0] }
529 };
530
531 // See which format we should be using
532 int paletteNum = 0;
533 if(type == "standard")
534 paletteNum = 0;
535 else if(type == "z26")
536 paletteNum = 1;
537 else if(type == "user" && myUserPaletteDefined)
538 paletteNum = 2;
539
540 // Now consider the current display format
541 const uInt32* palette =
542 (myDisplayFormat.compare(0, 3, "PAL") == 0) ? palettes[paletteNum][1] :
543 (myDisplayFormat.compare(0, 5, "SECAM") == 0) ? palettes[paletteNum][2] :
544 palettes[paletteNum][0];
545
546 myOSystem.frameBuffer().setPalette(palette);
547
548 if(myTIA->usingFixedColors())
549 myTIA->enableFixedColors(true);
550}
551
552// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
553void Console::togglePhosphor()
554{
555 if(myOSystem.frameBuffer().tiaSurface().phosphorEnabled())
556 {
557 myProperties.set(PropType::Display_Phosphor, "NO");
558 myOSystem.frameBuffer().tiaSurface().enablePhosphor(false);
559 myOSystem.frameBuffer().showMessage("Phosphor effect disabled");
560 }
561 else
562 {
563 myProperties.set(PropType::Display_Phosphor, "YES");
564 myOSystem.frameBuffer().tiaSurface().enablePhosphor(true);
565 myOSystem.frameBuffer().showMessage("Phosphor effect enabled");
566 }
567}
568
569// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
570void Console::changePhosphor(int direction)
571{
572 int blend = atoi(myProperties.get(PropType::Display_PPBlend).c_str());
573
574 if(direction == +1) // increase blend
575 {
576 if(blend >= 100)
577 {
578 myOSystem.frameBuffer().showMessage("Phosphor blend at maximum");
579 myOSystem.frameBuffer().tiaSurface().enablePhosphor(true, 100);
580 return;
581 }
582 else
583 blend = std::min(blend+2, 100);
584 }
585 else if(direction == -1) // decrease blend
586 {
587 if(blend <= 2)
588 {
589 myOSystem.frameBuffer().showMessage("Phosphor blend at minimum");
590 myOSystem.frameBuffer().tiaSurface().enablePhosphor(true, 0);
591 return;
592 }
593 else
594 blend = std::max(blend-2, 0);
595 }
596 else
597 return;
598
599 ostringstream val;
600 val << blend;
601 myProperties.set(PropType::Display_PPBlend, val.str());
602 myOSystem.frameBuffer().showMessage("Phosphor blend " + val.str());
603 myOSystem.frameBuffer().tiaSurface().enablePhosphor(true, blend);
604}
605
606// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
607void Console::setProperties(const Properties& props)
608{
609 myProperties = props;
610}
611
612// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
613FBInitStatus Console::initializeVideo(bool full)
614{
615 FBInitStatus fbstatus = FBInitStatus::Success;
616
617 if(full)
618 {
619 bool devSettings = myOSystem.settings().getBool("dev.settings");
620 const string& title = string("Stella ") + STELLA_VERSION +
621 ": \"" + myProperties.get(PropType::Cart_Name) + "\"";
622 fbstatus = myOSystem.frameBuffer().createDisplay(title,
623 TIAConstants::viewableWidth, TIAConstants::viewableHeight, false);
624 if(fbstatus != FBInitStatus::Success)
625 return fbstatus;
626
627 myOSystem.frameBuffer().showFrameStats(
628 myOSystem.settings().getBool(devSettings ? "dev.stats" : "plr.stats"));
629 generateColorLossPalette();
630 }
631 setPalette(myOSystem.settings().getString("palette"));
632
633 return fbstatus;
634}
635
636// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
637void Console::initializeAudio()
638{
639 myOSystem.sound().close();
640
641 myEmulationTiming
642 .updatePlaybackRate(myAudioSettings.sampleRate())
643 .updatePlaybackPeriod(myAudioSettings.fragmentSize())
644 .updateAudioQueueExtraFragments(myAudioSettings.bufferSize())
645 .updateAudioQueueHeadroom(myAudioSettings.headroom())
646 .updateSpeedFactor(myOSystem.settings().getFloat("speed"));
647
648 createAudioQueue();
649 myTIA->setAudioQueue(myAudioQueue);
650
651 myOSystem.sound().open(myAudioQueue, &myEmulationTiming);
652}
653
654/* Original frying research and code by Fred Quimby.
655 I've tried the following variations on this code:
656 - Both OR and Exclusive OR instead of AND. This generally crashes the game
657 without ever giving us realistic "fried" effects.
658 - Loop only over the RIOT RAM. This still gave us frying-like effects, but
659 it seemed harder to duplicate most effects. I have no idea why, but
660 munging the TIA regs seems to have some effect (I'd think it wouldn't).
661
662 Fred says he also tried mangling the PC and registers, but usually it'd just
663 crash the game (e.g. black screen, no way out of it).
664
665 It's definitely easier to get some effects (e.g. 255 lives in Battlezone)
666 with this code than it is on a real console. My guess is that most "good"
667 frying effects come from a RIOT location getting cleared to 0. Fred's
668 code is more likely to accomplish this than frying a real console is...
669
670 Until someone comes up with a more accurate way to emulate frying, I'm
671 leaving this as Fred posted it. -- B.
672*/
673// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
674void Console::fry() const
675{
676 for(int i = 0; i < 0x100; i += mySystem->randGenerator().next() % 4)
677 mySystem->poke(i, mySystem->peek(i) & mySystem->randGenerator().next());
678}
679
680// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
681void Console::changeYStart(int direction)
682{
683 uInt32 ystart = myTIA->ystart();
684
685 if(direction == +1) // increase YStart
686 {
687 if(ystart >= TIAConstants::maxYStart)
688 {
689 myOSystem.frameBuffer().showMessage("YStart at maximum");
690 return;
691 }
692
693 ++ystart;
694 myYStartAutodetected = false;
695 }
696 else if(direction == -1) // decrease YStart
697 {
698 if(ystart == 0)
699 {
700 throw runtime_error("cannot happen");
701 }
702
703 --ystart;
704 myYStartAutodetected = false;
705 }
706 else
707 return;
708
709 if(ystart == 0) {
710 redetectYStart();
711 ystart = myAutodetectedYstart;
712 myYStartAutodetected = true;
713
714 myProperties.set(PropType::Display_YStart, "0");
715 }
716 else {
717 ostringstream ss;
718 ss << ystart;
719
720 myProperties.set(PropType::Display_YStart, ss.str());
721 }
722
723 if (ystart != myTIA->ystart()) myTIA->setYStart(ystart);
724
725 ostringstream ss;
726
727 if(myAutodetectedYstart == ystart) ss << "YStart " << ystart << " (Auto)";
728 else ss << "YStart " << ystart;
729
730 myOSystem.frameBuffer().showMessage(ss.str());
731}
732
733// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
734void Console::updateYStart(uInt32 ystart)
735{
736 if (ystart > TIAConstants::maxYStart) return;
737
738 if (ystart == 0) {
739 if (myYStartAutodetected) return;
740
741 redetectYStart();
742 myYStartAutodetected = true;
743 ystart = myAutodetectedYstart;
744 } else
745 myYStartAutodetected = false;
746
747 if (ystart != myTIA->ystart()) myTIA->setYStart(ystart);
748}
749
750// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
751void Console::setTIAProperties()
752{
753 // FIXME - ystart is probably disappearing soon, or at least autodetection is
754 uInt32 ystart = atoi(myProperties.get(PropType::Display_YStart).c_str());
755 if(ystart != 0)
756 ystart = BSPF::clamp(ystart, 0u, TIAConstants::maxYStart);
757 else {
758 ystart = myAutodetectedYstart;
759 myYStartAutodetected = true;
760 }
761
762 if(myDisplayFormat == "NTSC" || myDisplayFormat == "PAL60" ||
763 myDisplayFormat == "SECAM60")
764 {
765 // Assume we've got ~262 scanlines (NTSC-like format)
766 myTIA->setLayout(FrameLayout::ntsc);
767 }
768 else
769 {
770 // Assume we've got ~312 scanlines (PAL-like format)
771 myTIA->setLayout(FrameLayout::pal);
772 }
773
774 myTIA->setYStart(ystart);
775
776 myEmulationTiming.updateFrameLayout(myTIA->frameLayout());
777 myEmulationTiming.updateConsoleTiming(myConsoleTiming);
778}
779
780// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
781void Console::createAudioQueue()
782{
783 bool useStereo = myOSystem.settings().getBool(AudioSettings::SETTING_STEREO)
784 || myProperties.get(PropType::Cart_Sound) == "STEREO";
785
786 myAudioQueue = make_shared<AudioQueue>(
787 myEmulationTiming.audioFragmentSize(),
788 myEmulationTiming.audioQueueCapacity(),
789 useStereo
790 );
791}
792
793// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
794void Console::setControllers(const string& romMd5)
795{
796 // Check for CompuMate scheme; it is special in that a handler creates both
797 // controllers for us, and associates them with the bankswitching class
798 if(myCart->detectedType() == "CM")
799 {
800 myCMHandler = make_shared<CompuMate>(*this, myEvent, *mySystem);
801
802 // A somewhat ugly bit of code that casts to CartridgeCM to
803 // add the CompuMate, and then back again for the actual
804 // Cartridge
805 unique_ptr<CartridgeCM> cartcm(static_cast<CartridgeCM*>(myCart.release()));
806 cartcm->setCompuMate(myCMHandler);
807 myCart = std::move(cartcm);
808
809 myLeftControl = std::move(myCMHandler->leftController());
810 myRightControl = std::move(myCMHandler->rightController());
811 myOSystem.eventHandler().defineKeyControllerMappings(Controller::Type::CompuMate, Controller::Jack::Left);
812 myOSystem.eventHandler().defineJoyControllerMappings(Controller::Type::CompuMate, Controller::Jack::Left);
813 }
814 else
815 {
816 // Setup the controllers based on properties
817 Controller::Type leftType = Controller::getType(myProperties.get(PropType::Controller_Left));
818 Controller::Type rightType = Controller::getType(myProperties.get(PropType::Controller_Right));
819 size_t size = 0;
820 const uInt8* image = myCart->getImage(size);
821 const bool swappedPorts = myProperties.get(PropType::Console_SwapPorts) == "YES";
822
823 // Try to detect controllers
824 if(image != nullptr || size != 0)
825 {
826 leftType = ControllerDetector::detectType(image, size, leftType,
827 !swappedPorts ? Controller::Jack::Left : Controller::Jack::Right, myOSystem.settings());
828 rightType = ControllerDetector::detectType(image, size, rightType,
829 !swappedPorts ? Controller::Jack::Right : Controller::Jack::Left, myOSystem.settings());
830 }
831
832 unique_ptr<Controller> leftC = getControllerPort(leftType, Controller::Jack::Left, romMd5),
833 rightC = getControllerPort(rightType, Controller::Jack::Right, romMd5);
834
835 // Swap the ports if necessary
836 if(!swappedPorts)
837 {
838 myLeftControl = std::move(leftC);
839 myRightControl = std::move(rightC);
840 }
841 else
842 {
843 myLeftControl = std::move(rightC);
844 myRightControl = std::move(leftC);
845 }
846 }
847
848 myTIA->bindToControllers();
849
850 // now that we know the controllers, enable the event mappings
851 myOSystem.eventHandler().enableEmulationKeyMappings();
852 myOSystem.eventHandler().enableEmulationJoyMappings();
853
854 myOSystem.eventHandler().setMouseControllerMode(myOSystem.settings().getString("usemouse"));
855}
856
857// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
858unique_ptr<Controller> Console::getControllerPort(const Controller::Type type,
859 const Controller::Jack port, const string& romMd5)
860{
861 unique_ptr<Controller> controller;
862
863 myOSystem.eventHandler().defineKeyControllerMappings(type, port);
864 myOSystem.eventHandler().defineJoyControllerMappings(type, port);
865
866 switch(type)
867 {
868 case Controller::Type::BoosterGrip:
869 controller = make_unique<BoosterGrip>(port, myEvent, *mySystem);
870 break;
871
872 case Controller::Type::Driving:
873 controller = make_unique<Driving>(port, myEvent, *mySystem);
874 break;
875
876 case Controller::Type::Keyboard:
877 controller = make_unique<Keyboard>(port, myEvent, *mySystem);
878 break;
879
880 case Controller::Type::Paddles:
881 case Controller::Type::PaddlesIAxis:
882 case Controller::Type::PaddlesIAxDr:
883 {
884 // Also check if we should swap the paddles plugged into a jack
885 bool swapPaddles = myProperties.get(PropType::Controller_SwapPaddles) == "YES";
886 bool swapAxis = false, swapDir = false;
887 if(type == Controller::Type::PaddlesIAxis)
888 swapAxis = true;
889 else if(type == Controller::Type::PaddlesIAxDr)
890 swapAxis = swapDir = true;
891 controller = make_unique<Paddles>(port, myEvent, *mySystem,
892 swapPaddles, swapAxis, swapDir);
893 break;
894 }
895 case Controller::Type::AmigaMouse:
896 controller = make_unique<AmigaMouse>(port, myEvent, *mySystem);
897 break;
898
899 case Controller::Type::AtariMouse:
900 controller = make_unique<AtariMouse>(port, myEvent, *mySystem);
901 break;
902
903 case Controller::Type::TrakBall:
904 controller = make_unique<TrakBall>(port, myEvent, *mySystem);
905 break;
906
907 case Controller::Type::AtariVox:
908 {
909 const string& nvramfile = myOSystem.nvramDir() + "atarivox_eeprom.dat";
910 Controller::onMessageCallback callback = [&os = myOSystem](const string& msg) {
911 bool devSettings = os.settings().getBool("dev.settings");
912 if(os.settings().getBool(devSettings ? "dev.eepromaccess" : "plr.eepromaccess"))
913 os.frameBuffer().showMessage(msg);
914 };
915 controller = make_unique<AtariVox>(port, myEvent, *mySystem,
916 myOSystem.settings().getString("avoxport"), nvramfile, callback);
917 break;
918 }
919 case Controller::Type::SaveKey:
920 {
921 const string& nvramfile = myOSystem.nvramDir() + "savekey_eeprom.dat";
922 Controller::onMessageCallback callback = [&os = myOSystem](const string& msg) {
923 bool devSettings = os.settings().getBool("dev.settings");
924 if(os.settings().getBool(devSettings ? "dev.eepromaccess" : "plr.eepromaccess"))
925 os.frameBuffer().showMessage(msg);
926 };
927 controller = make_unique<SaveKey>(port, myEvent, *mySystem, nvramfile, callback);
928 break;
929 }
930 case Controller::Type::Genesis:
931 controller = make_unique<Genesis>(port, myEvent, *mySystem);
932 break;
933
934 case Controller::Type::KidVid:
935 controller = make_unique<KidVid>(port, myEvent, *mySystem, romMd5);
936 break;
937
938 case Controller::Type::MindLink:
939 controller = make_unique<MindLink>(port, myEvent, *mySystem);
940 break;
941
942 default:
943 // What else can we do?
944 // always create because it may have been changed by user dialog
945 controller = make_unique<Joystick>(port, myEvent, *mySystem);
946 }
947
948 return controller;
949}
950
951// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
952void Console::loadUserPalette()
953{
954 const string& palette = myOSystem.paletteFile();
955 ifstream in(palette, std::ios::binary);
956 if(!in)
957 return;
958
959 // Make sure the contains enough data for the NTSC, PAL and SECAM palettes
960 // This means 128 colours each for NTSC and PAL, at 3 bytes per pixel
961 // and 8 colours for SECAM at 3 bytes per pixel
962 in.seekg(0, std::ios::end);
963 std::streampos length = in.tellg();
964 in.seekg(0, std::ios::beg);
965 if(length < 128 * 3 * 2 + 8 * 3)
966 {
967 cerr << "ERROR: invalid palette file " << palette << endl;
968 return;
969 }
970
971 // Now that we have valid data, create the user-defined palettes
972 uInt8 pixbuf[3]; // Temporary buffer for one 24-bit pixel
973
974 for(int i = 0; i < 128; i++) // NTSC palette
975 {
976 in.read(reinterpret_cast<char*>(pixbuf), 3);
977 uInt32 pixel = (int(pixbuf[0]) << 16) + (int(pixbuf[1]) << 8) + int(pixbuf[2]);
978 ourUserNTSCPalette[(i<<1)] = pixel;
979 }
980 for(int i = 0; i < 128; i++) // PAL palette
981 {
982 in.read(reinterpret_cast<char*>(pixbuf), 3);
983 uInt32 pixel = (int(pixbuf[0]) << 16) + (int(pixbuf[1]) << 8) + int(pixbuf[2]);
984 ourUserPALPalette[(i<<1)] = pixel;
985 }
986
987 uInt32 secam[16]; // All 8 24-bit pixels, plus 8 colorloss pixels
988 for(int i = 0; i < 8; i++) // SECAM palette
989 {
990 in.read(reinterpret_cast<char*>(pixbuf), 3);
991 uInt32 pixel = (int(pixbuf[0]) << 16) + (int(pixbuf[1]) << 8) + int(pixbuf[2]);
992 secam[(i<<1)] = pixel;
993 secam[(i<<1)+1] = 0;
994 }
995 uInt32* ptr = ourUserSECAMPalette;
996 for(int i = 0; i < 16; ++i)
997 {
998 uInt32* s = secam;
999 for(int j = 0; j < 16; ++j)
1000 *ptr++ = *s++;
1001 }
1002
1003 myUserPaletteDefined = true;
1004}
1005
1006// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1007void Console::generateColorLossPalette()
1008{
1009 // Look at all the palettes, since we don't know which one is
1010 // currently active
1011 uInt32* palette[9] = {
1012 &ourNTSCPalette[0], &ourPALPalette[0], &ourSECAMPalette[0],
1013 &ourNTSCPaletteZ26[0], &ourPALPaletteZ26[0], &ourSECAMPaletteZ26[0],
1014 nullptr, nullptr, nullptr
1015 };
1016 if(myUserPaletteDefined)
1017 {
1018 palette[6] = &ourUserNTSCPalette[0];
1019 palette[7] = &ourUserPALPalette[0];
1020 palette[8] = &ourUserSECAMPalette[0];
1021 }
1022
1023 for(int i = 0; i < 9; ++i)
1024 {
1025 if(palette[i] == nullptr)
1026 continue;
1027
1028 // Fill the odd numbered palette entries with gray values (calculated
1029 // using the standard RGB -> grayscale conversion formula)
1030 for(int j = 0; j < 128; ++j)
1031 {
1032 uInt32 pixel = palette[i][(j<<1)];
1033 uInt8 r = (pixel >> 16) & 0xff;
1034 uInt8 g = (pixel >> 8) & 0xff;
1035 uInt8 b = (pixel >> 0) & 0xff;
1036 uInt8 sum = uInt8((r * 0.2989) + (g * 0.5870) + (b * 0.1140));
1037 palette[i][(j<<1)+1] = (sum << 16) + (sum << 8) + sum;
1038 }
1039 }
1040}
1041
1042// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1043float Console::getFramerate() const
1044{
1045 return
1046 (myConsoleTiming == ConsoleTiming::ntsc ? 262.f * 60.f : 312.f * 50.f) /
1047 myTIA->frameBufferScanlinesLastFrame();
1048}
1049
1050// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1051void Console::toggleTIABit(TIABit bit, const string& bitname, bool show) const
1052{
1053 bool result = myTIA->toggleBit(bit);
1054 string message = bitname + (result ? " enabled" : " disabled");
1055 myOSystem.frameBuffer().showMessage(message);
1056}
1057
1058// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1059void Console::toggleBits() const
1060{
1061 bool enabled = myTIA->toggleBits();
1062 string message = string("TIA bits") + (enabled ? " enabled" : " disabled");
1063 myOSystem.frameBuffer().showMessage(message);
1064}
1065
1066// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1067void Console::toggleTIACollision(TIABit bit, const string& bitname, bool show) const
1068{
1069 bool result = myTIA->toggleCollision(bit);
1070 string message = bitname + (result ? " collision enabled" : " collision disabled");
1071 myOSystem.frameBuffer().showMessage(message);
1072}
1073
1074// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1075void Console::toggleCollisions() const
1076{
1077 bool enabled = myTIA->toggleCollisions();
1078 string message = string("TIA collisions") + (enabled ? " enabled" : " disabled");
1079 myOSystem.frameBuffer().showMessage(message);
1080}
1081
1082// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1083void Console::toggleFixedColors() const
1084{
1085 if(myTIA->toggleFixedColors())
1086 myOSystem.frameBuffer().showMessage("Fixed debug colors enabled");
1087 else
1088 myOSystem.frameBuffer().showMessage("Fixed debug colors disabled");
1089}
1090
1091// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1092void Console::toggleJitter() const
1093{
1094 bool enabled = myTIA->toggleJitter();
1095 string message = string("TV scanline jitter") + (enabled ? " enabled" : " disabled");
1096 myOSystem.frameBuffer().showMessage(message);
1097}
1098
1099// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1100void Console::attachDebugger(Debugger& dbg)
1101{
1102#ifdef DEBUGGER_SUPPORT
1103// myOSystem.createDebugger(*this);
1104 mySystem->m6502().attach(dbg);
1105#endif
1106}
1107
1108// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1109void Console::stateChanged(EventHandlerState state)
1110{
1111 // only the CompuMate used to care about state changes
1112}
1113
1114// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1115uInt32 Console::ourNTSCPalette[256] = {
1116 0x000000, 0, 0x4a4a4a, 0, 0x6f6f6f, 0, 0x8e8e8e, 0,
1117 0xaaaaaa, 0, 0xc0c0c0, 0, 0xd6d6d6, 0, 0xececec, 0,
1118 0x484800, 0, 0x69690f, 0, 0x86861d, 0, 0xa2a22a, 0,
1119 0xbbbb35, 0, 0xd2d240, 0, 0xe8e84a, 0, 0xfcfc54, 0,
1120 0x7c2c00, 0, 0x904811, 0, 0xa26221, 0, 0xb47a30, 0,
1121 0xc3903d, 0, 0xd2a44a, 0, 0xdfb755, 0, 0xecc860, 0,
1122 0x901c00, 0, 0xa33915, 0, 0xb55328, 0, 0xc66c3a, 0,
1123 0xd5824a, 0, 0xe39759, 0, 0xf0aa67, 0, 0xfcbc74, 0,
1124 0x940000, 0, 0xa71a1a, 0, 0xb83232, 0, 0xc84848, 0,
1125 0xd65c5c, 0, 0xe46f6f, 0, 0xf08080, 0, 0xfc9090, 0,
1126 0x840064, 0, 0x97197a, 0, 0xa8308f, 0, 0xb846a2, 0,
1127 0xc659b3, 0, 0xd46cc3, 0, 0xe07cd2, 0, 0xec8ce0, 0,
1128 0x500084, 0, 0x68199a, 0, 0x7d30ad, 0, 0x9246c0, 0,
1129 0xa459d0, 0, 0xb56ce0, 0, 0xc57cee, 0, 0xd48cfc, 0,
1130 0x140090, 0, 0x331aa3, 0, 0x4e32b5, 0, 0x6848c6, 0,
1131 0x7f5cd5, 0, 0x956fe3, 0, 0xa980f0, 0, 0xbc90fc, 0,
1132 0x000094, 0, 0x181aa7, 0, 0x2d32b8, 0, 0x4248c8, 0,
1133 0x545cd6, 0, 0x656fe4, 0, 0x7580f0, 0, 0x8490fc, 0,
1134 0x001c88, 0, 0x183b9d, 0, 0x2d57b0, 0, 0x4272c2, 0,
1135 0x548ad2, 0, 0x65a0e1, 0, 0x75b5ef, 0, 0x84c8fc, 0,
1136 0x003064, 0, 0x185080, 0, 0x2d6d98, 0, 0x4288b0, 0,
1137 0x54a0c5, 0, 0x65b7d9, 0, 0x75cceb, 0, 0x84e0fc, 0,
1138 0x004030, 0, 0x18624e, 0, 0x2d8169, 0, 0x429e82, 0,
1139 0x54b899, 0, 0x65d1ae, 0, 0x75e7c2, 0, 0x84fcd4, 0,
1140 0x004400, 0, 0x1a661a, 0, 0x328432, 0, 0x48a048, 0,
1141 0x5cba5c, 0, 0x6fd26f, 0, 0x80e880, 0, 0x90fc90, 0,
1142 0x143c00, 0, 0x355f18, 0, 0x527e2d, 0, 0x6e9c42, 0,
1143 0x87b754, 0, 0x9ed065, 0, 0xb4e775, 0, 0xc8fc84, 0,
1144 0x303800, 0, 0x505916, 0, 0x6d762b, 0, 0x88923e, 0,
1145 0xa0ab4f, 0, 0xb7c25f, 0, 0xccd86e, 0, 0xe0ec7c, 0,
1146 0x482c00, 0, 0x694d14, 0, 0x866a26, 0, 0xa28638, 0,
1147 0xbb9f47, 0, 0xd2b656, 0, 0xe8cc63, 0, 0xfce070, 0
1148};
1149
1150// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1151uInt32 Console::ourPALPalette[256] = {
1152 0x000000, 0, 0x121212, 0, 0x242424, 0, 0x484848, 0, // 180 0
1153 0x6c6c6c, 0, 0x909090, 0, 0xb4b4b4, 0, 0xd8d8d8, 0, // was 0x111111..0xcccccc
1154 0x000000, 0, 0x121212, 0, 0x242424, 0, 0x484848, 0, // 198 1
1155 0x6c6c6c, 0, 0x909090, 0, 0xb4b4b4, 0, 0xd8d8d8, 0,
1156 0x1d0f00, 0, 0x3f2700, 0, 0x614900, 0, 0x836b01, 0, // 1b0 2
1157 0xa58d23, 0, 0xc7af45, 0, 0xe9d167, 0, 0xffe789, 0, // was ..0xfff389
1158 0x002400, 0, 0x004600, 0, 0x216800, 0, 0x438a07, 0, // 1c8 3
1159 0x65ac29, 0, 0x87ce4b, 0, 0xa9f06d, 0, 0xcbff8f, 0,
1160 0x340000, 0, 0x561400, 0, 0x783602, 0, 0x9a5824, 0, // 1e0 4
1161 0xbc7a46, 0, 0xde9c68, 0, 0xffbe8a, 0, 0xffd0ad, 0, // was ..0xffe0ac
1162 0x002700, 0, 0x004900, 0, 0x0c6b0c, 0, 0x2e8d2e, 0, // 1f8 5
1163 0x50af50, 0, 0x72d172, 0, 0x94f394, 0, 0xb6ffb6, 0,
1164 0x3d0008, 0, 0x610511, 0, 0x832733, 0, 0xa54955, 0, // 210 6
1165 0xc76b77, 0, 0xe98d99, 0, 0xffafbb, 0, 0xffd1d7, 0, // was 0x3f0000..0xffd1dd
1166 0x001e12, 0, 0x004228, 0, 0x046540, 0, 0x268762, 0, // 228 7
1167 0x48a984, 0, 0x6acba6, 0, 0x8cedc8, 0, 0xafffe0, 0, // was 0x002100, 0x00431e..0xaeffff
1168 0x300025, 0, 0x5f0047, 0, 0x811e69, 0, 0xa3408b, 0, // 240 8
1169 0xc562ad, 0, 0xe784cf, 0, 0xffa8ea, 0, 0xffc9f2, 0, // was ..0xffa6f1, 0xffc8ff
1170 0x001431, 0, 0x003653, 0, 0x0a5875, 0, 0x2c7a97, 0, // 258 9
1171 0x4e9cb9, 0, 0x70bedb, 0, 0x92e0fd, 0, 0xb4ffff, 0,
1172 0x2c0052, 0, 0x4e0074, 0, 0x701d96, 0, 0x923fb8, 0, // 270 a
1173 0xb461da, 0, 0xd683fc, 0, 0xe2a5ff, 0, 0xeec9ff, 0, // was ..0xf8a5ff, 0xffc7ff
1174 0x001759, 0, 0x00247c, 0, 0x1d469e, 0, 0x3f68c0, 0, // 288 b
1175 0x618ae2, 0, 0x83acff, 0, 0xa5ceff, 0, 0xc7f0ff, 0,
1176 0x12006d, 0, 0x34038f, 0, 0x5625b1, 0, 0x7847d3, 0, // 2a0 c
1177 0x9a69f5, 0, 0xb48cff, 0, 0xc9adff, 0, 0xe1d1ff, 0, // was ..0xbc8bff, 0xdeadff, 0xffcfff,
1178 0x000070, 0, 0x161292, 0, 0x3834b4, 0, 0x5a56d6, 0, // 2b8 d
1179 0x7c78f8, 0, 0x9e9aff, 0, 0xc0bcff, 0, 0xe2deff, 0,
1180 0x000000, 0, 0x121212, 0, 0x242424, 0, 0x484848, 0, // 2d0 e
1181 0x6c6c6c, 0, 0x909090, 0, 0xb4b4b4, 0, 0xd8d8d8, 0,
1182 0x000000, 0, 0x121212, 0, 0x242424, 0, 0x484848, 0, // 2e8 f
1183 0x6c6c6c, 0, 0x909090, 0, 0xb4b4b4, 0, 0xd8d8d8, 0,
1184};
1185
1186// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1187uInt32 Console::ourSECAMPalette[256] = {
1188 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1189 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1190 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1191 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1192 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1193 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1194 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1195 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1196 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1197 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1198 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1199 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1200 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1201 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1202 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1203 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1204 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1205 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1206 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1207 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1208 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1209 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1210 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1211 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1212 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1213 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1214 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1215 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1216 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1217 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1218 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
1219 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0
1220};
1221
1222// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1223uInt32 Console::ourNTSCPaletteZ26[256] = {
1224 0x000000, 0, 0x505050, 0, 0x646464, 0, 0x787878, 0,
1225 0x8c8c8c, 0, 0xa0a0a0, 0, 0xb4b4b4, 0, 0xc8c8c8, 0,
1226 0x445400, 0, 0x586800, 0, 0x6c7c00, 0, 0x809000, 0,
1227 0x94a414, 0, 0xa8b828, 0, 0xbccc3c, 0, 0xd0e050, 0,
1228 0x673900, 0, 0x7b4d00, 0, 0x8f6100, 0, 0xa37513, 0,
1229 0xb78927, 0, 0xcb9d3b, 0, 0xdfb14f, 0, 0xf3c563, 0,
1230 0x7b2504, 0, 0x8f3918, 0, 0xa34d2c, 0, 0xb76140, 0,
1231 0xcb7554, 0, 0xdf8968, 0, 0xf39d7c, 0, 0xffb190, 0,
1232 0x7d122c, 0, 0x912640, 0, 0xa53a54, 0, 0xb94e68, 0,
1233 0xcd627c, 0, 0xe17690, 0, 0xf58aa4, 0, 0xff9eb8, 0,
1234 0x730871, 0, 0x871c85, 0, 0x9b3099, 0, 0xaf44ad, 0,
1235 0xc358c1, 0, 0xd76cd5, 0, 0xeb80e9, 0, 0xff94fd, 0,
1236 0x5d0b92, 0, 0x711fa6, 0, 0x8533ba, 0, 0x9947ce, 0,
1237 0xad5be2, 0, 0xc16ff6, 0, 0xd583ff, 0, 0xe997ff, 0,
1238 0x401599, 0, 0x5429ad, 0, 0x683dc1, 0, 0x7c51d5, 0,
1239 0x9065e9, 0, 0xa479fd, 0, 0xb88dff, 0, 0xcca1ff, 0,
1240 0x252593, 0, 0x3939a7, 0, 0x4d4dbb, 0, 0x6161cf, 0,
1241 0x7575e3, 0, 0x8989f7, 0, 0x9d9dff, 0, 0xb1b1ff, 0,
1242 0x0f3480, 0, 0x234894, 0, 0x375ca8, 0, 0x4b70bc, 0,
1243 0x5f84d0, 0, 0x7398e4, 0, 0x87acf8, 0, 0x9bc0ff, 0,
1244 0x04425a, 0, 0x18566e, 0, 0x2c6a82, 0, 0x407e96, 0,
1245 0x5492aa, 0, 0x68a6be, 0, 0x7cbad2, 0, 0x90cee6, 0,
1246 0x044f30, 0, 0x186344, 0, 0x2c7758, 0, 0x408b6c, 0,
1247 0x549f80, 0, 0x68b394, 0, 0x7cc7a8, 0, 0x90dbbc, 0,
1248 0x0f550a, 0, 0x23691e, 0, 0x377d32, 0, 0x4b9146, 0,
1249 0x5fa55a, 0, 0x73b96e, 0, 0x87cd82, 0, 0x9be196, 0,
1250 0x1f5100, 0, 0x336505, 0, 0x477919, 0, 0x5b8d2d, 0,
1251 0x6fa141, 0, 0x83b555, 0, 0x97c969, 0, 0xabdd7d, 0,
1252 0x344600, 0, 0x485a00, 0, 0x5c6e14, 0, 0x708228, 0,
1253 0x84963c, 0, 0x98aa50, 0, 0xacbe64, 0, 0xc0d278, 0,
1254 0x463e00, 0, 0x5a5205, 0, 0x6e6619, 0, 0x827a2d, 0,
1255 0x968e41, 0, 0xaaa255, 0, 0xbeb669, 0, 0xd2ca7d, 0
1256};
1257
1258// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1259uInt32 Console::ourPALPaletteZ26[256] = {
1260 0x000000, 0, 0x4c4c4c, 0, 0x606060, 0, 0x747474, 0,
1261 0x888888, 0, 0x9c9c9c, 0, 0xb0b0b0, 0, 0xc4c4c4, 0,
1262 0x000000, 0, 0x4c4c4c, 0, 0x606060, 0, 0x747474, 0,
1263 0x888888, 0, 0x9c9c9c, 0, 0xb0b0b0, 0, 0xc4c4c4, 0,
1264 0x533a00, 0, 0x674e00, 0, 0x7b6203, 0, 0x8f7617, 0,
1265 0xa38a2b, 0, 0xb79e3f, 0, 0xcbb253, 0, 0xdfc667, 0,
1266 0x1b5800, 0, 0x2f6c00, 0, 0x438001, 0, 0x579415, 0,
1267 0x6ba829, 0, 0x7fbc3d, 0, 0x93d051, 0, 0xa7e465, 0,
1268 0x6a2900, 0, 0x7e3d12, 0, 0x925126, 0, 0xa6653a, 0,
1269 0xba794e, 0, 0xce8d62, 0, 0xe2a176, 0, 0xf6b58a, 0,
1270 0x075b00, 0, 0x1b6f11, 0, 0x2f8325, 0, 0x439739, 0,
1271 0x57ab4d, 0, 0x6bbf61, 0, 0x7fd375, 0, 0x93e789, 0,
1272 0x741b2f, 0, 0x882f43, 0, 0x9c4357, 0, 0xb0576b, 0,
1273 0xc46b7f, 0, 0xd87f93, 0, 0xec93a7, 0, 0xffa7bb, 0,
1274 0x00572e, 0, 0x106b42, 0, 0x247f56, 0, 0x38936a, 0,
1275 0x4ca77e, 0, 0x60bb92, 0, 0x74cfa6, 0, 0x88e3ba, 0,
1276 0x6d165f, 0, 0x812a73, 0, 0x953e87, 0, 0xa9529b, 0,
1277 0xbd66af, 0, 0xd17ac3, 0, 0xe58ed7, 0, 0xf9a2eb, 0,
1278 0x014c5e, 0, 0x156072, 0, 0x297486, 0, 0x3d889a, 0,
1279 0x519cae, 0, 0x65b0c2, 0, 0x79c4d6, 0, 0x8dd8ea, 0,
1280 0x5f1588, 0, 0x73299c, 0, 0x873db0, 0, 0x9b51c4, 0,
1281 0xaf65d8, 0, 0xc379ec, 0, 0xd78dff, 0, 0xeba1ff, 0,
1282 0x123b87, 0, 0x264f9b, 0, 0x3a63af, 0, 0x4e77c3, 0,
1283 0x628bd7, 0, 0x769feb, 0, 0x8ab3ff, 0, 0x9ec7ff, 0,
1284 0x451e9d, 0, 0x5932b1, 0, 0x6d46c5, 0, 0x815ad9, 0,
1285 0x956eed, 0, 0xa982ff, 0, 0xbd96ff, 0, 0xd1aaff, 0,
1286 0x2a2b9e, 0, 0x3e3fb2, 0, 0x5253c6, 0, 0x6667da, 0,
1287 0x7a7bee, 0, 0x8e8fff, 0, 0xa2a3ff, 0, 0xb6b7ff, 0,
1288 0x000000, 0, 0x4c4c4c, 0, 0x606060, 0, 0x747474, 0,
1289 0x888888, 0, 0x9c9c9c, 0, 0xb0b0b0, 0, 0xc4c4c4, 0,
1290 0x000000, 0, 0x4c4c4c, 0, 0x606060, 0, 0x747474, 0,
1291 0x888888, 0, 0x9c9c9c, 0, 0xb0b0b0, 0, 0xc4c4c4, 0
1292};
1293
1294// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1295uInt32 Console::ourSECAMPaletteZ26[256] = {
1296 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1297 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1298 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1299 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1300 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1301 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1302 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1303 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1304 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1305 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1306 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1307 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1308 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1309 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1310 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1311 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1312 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1313 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1314 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1315 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1316 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1317 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1318 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1319 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1320 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1321 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1322 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1323 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1324 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1325 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
1326 0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
1327 0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0
1328};
1329
1330// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1331uInt32 Console::ourUserNTSCPalette[256] = { 0 }; // filled from external file
1332
1333// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1334uInt32 Console::ourUserPALPalette[256] = { 0 }; // filled from external file
1335
1336// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1337uInt32 Console::ourUserSECAMPalette[256] = { 0 }; // filled from external file
1338