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 | |
74 | namespace { |
75 | constexpr uInt8 = 2; |
76 | } |
77 | |
78 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
79 | Console::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
219 | Console::~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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
231 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
259 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
274 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
302 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
316 | bool 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
339 | bool 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
362 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
376 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
454 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
472 | void Console::enableColorLoss(bool state) |
473 | { |
474 | myTIA->enableColorLoss(state); |
475 | } |
476 | |
477 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
478 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
521 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
553 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
570 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
607 | void Console::setProperties(const Properties& props) |
608 | { |
609 | myProperties = props; |
610 | } |
611 | |
612 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
613 | FBInitStatus 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
637 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
674 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
681 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
734 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
751 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
781 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
794 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
858 | unique_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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
952 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1007 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1043 | float Console::getFramerate() const |
1044 | { |
1045 | return |
1046 | (myConsoleTiming == ConsoleTiming::ntsc ? 262.f * 60.f : 312.f * 50.f) / |
1047 | myTIA->frameBufferScanlinesLastFrame(); |
1048 | } |
1049 | |
1050 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1051 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1059 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1067 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1075 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1083 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1092 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1100 | void Console::attachDebugger(Debugger& dbg) |
1101 | { |
1102 | #ifdef DEBUGGER_SUPPORT |
1103 | // myOSystem.createDebugger(*this); |
1104 | mySystem->m6502().attach(dbg); |
1105 | #endif |
1106 | } |
1107 | |
1108 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1109 | void Console::stateChanged(EventHandlerState state) |
1110 | { |
1111 | // only the CompuMate used to care about state changes |
1112 | } |
1113 | |
1114 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1115 | uInt32 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1151 | uInt32 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1187 | uInt32 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1223 | uInt32 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1259 | uInt32 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1295 | uInt32 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1331 | uInt32 Console::ourUserNTSCPalette[256] = { 0 }; // filled from external file |
1332 | |
1333 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1334 | uInt32 Console::ourUserPALPalette[256] = { 0 }; // filled from external file |
1335 | |
1336 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1337 | uInt32 Console::ourUserSECAMPalette[256] = { 0 }; // filled from external file |
1338 | |