]> Shamusworld >> Repos - apple2/blob - src/apple2.cpp
Fixed Apple power state to pause CPU thread immediately.
[apple2] / src / apple2.cpp
1 //
2 // Apple 2 SDL Portable Apple Emulator
3 //
4 // by James Hammons
5 // © 2014 Underground Software
6 //
7 // Loosely based on AppleWin by Tom Charlesworth which was based on AppleWin by
8 // Oliver Schmidt which was based on AppleWin by Michael O'Brien. :-) Parts are
9 // also derived from ApplePC. Too bad it was closed source--it could have been
10 // *the* premier Apple II emulator out there.
11 //
12 // JLH = James Hammons <jlhamm@acm.org>
13 //
14 // WHO  WHEN        WHAT
15 // ---  ----------  ------------------------------------------------------------
16 // JLH  11/12/2005  Initial port to SDL
17 // JLH  11/18/2005  Wired up graphic soft switches
18 // JLH  12/02/2005  Setup timer subsystem for more accurate time keeping
19 // JLH  12/12/2005  Added preliminary state saving support
20 // JLH  09/24/2013  Added //e support
21 //
22
23 // STILL TO DO:
24 //
25 // - Port to SDL [DONE]
26 // - GUI goodies
27 // - Weed out unneeded functions [DONE]
28 // - Disk I/O [DONE]
29 // - 128K IIe related stuff [DONE]
30 // - State loading/saving
31 //
32
33 #include "apple2.h"
34
35 #include <SDL2/SDL.h>
36 #include <fstream>
37 #include <string>
38 #include <iomanip>
39 #include <iostream>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <time.h>
43 #include "log.h"
44 #include "video.h"
45 #include "sound.h"
46 #include "settings.h"
47 #include "v65c02.h"
48 #include "applevideo.h"
49 #include "timing.h"
50 #include "floppy.h"
51 #include "firmware.h"
52 #include "mmu.h"
53
54 #include "gui/gui.h"
55
56 // Debug and misc. defines
57
58 #define THREADED_65C02
59 #define CPU_THREAD_OVERFLOW_COMPENSATION
60 #define DEBUG_LC
61 //#define CPU_CLOCK_CHECKING
62 //#define THREAD_DEBUGGING
63 #define SOFT_SWITCH_DEBUGGING
64
65 // Global variables
66
67 uint8_t ram[0x10000], rom[0x10000];                     // RAM & ROM spaces
68 uint8_t ram2[0x10000];                                          // Auxillary RAM
69 //uint8_t diskRom[0x100];                                               // Disk ROM space
70 V65C02REGS mainCPU;                                                     // v65C02 execution context
71 uint8_t appleType = APPLE_TYPE_IIE;
72 FloppyDrive floppyDrive;
73 //bool powerOnState = true;                                     // Virtual power switch
74 bool powerStateChangeRequested = false;
75
76 // Local variables
77
78 uint8_t lastKeyPressed = 0;
79 bool keyDown = false;
80 bool openAppleDown = false;
81 bool closedAppleDown = false;
82 bool store80Mode = false;
83 bool vbl = false;
84 bool slotCXROM = false;
85 bool slotC3ROM = false;
86 bool ramrd = false;
87 bool ramwrt = false;
88 bool altzp = false;
89 bool ioudis = true;
90 bool dhires = false;
91 // Language card state (ROM read, no write)
92 uint8_t lcState = 0x02;
93
94 static bool running = true;                                     // Machine running state flag...
95 static uint32_t startTicks;
96 static bool pauseMode = false;
97 static bool fullscreenDebounce = false;
98 static bool capsLock = false;
99 static bool capsLockDebounce = false;
100
101 // Local functions (technically, they're global...)
102
103 bool LoadImg(char * filename, uint8_t * ram, int size);
104 static void SaveApple2State(const char * filename);
105 static bool LoadApple2State(const char * filename);
106 static void ResetApple2State(void);
107
108 // Local timer callback functions
109
110 static void FrameCallback(void);
111 static void BlinkTimer(void);
112
113 #ifdef THREADED_65C02
114 // Test of threaded execution of 6502
115 static SDL_Thread * cpuThread = NULL;
116 //static SDL_mutex * cpuMutex = NULL;
117 static SDL_cond * cpuCond = NULL;
118 static SDL_sem * mainSem = NULL;
119 static bool cpuFinished = false;
120 static bool cpuSleep = false;
121
122 // NB: Apple //e Manual sez 6502 is running @ 1,022,727 Hz
123
124 // Let's try a thread...
125 /*
126 Here's how it works: Execute 1 frame's worth, then sleep.
127 Other stuff wakes it up
128 */
129 int CPUThreadFunc(void * data)
130 {
131         // Mutex must be locked for conditional to work...
132         // Also, must be created in the thread that uses it...
133         SDL_mutex * cpuMutex = SDL_CreateMutex();
134
135 // decrement mainSem...
136 //SDL_SemWait(mainSem);
137 #ifdef CPU_THREAD_OVERFLOW_COMPENSATION
138         float overflow = 0.0;
139 #endif
140
141         do
142         {
143 // This is never set to true anywhere...
144                 if (cpuSleep)
145                         SDL_CondWait(cpuCond, cpuMutex);
146
147 // decrement mainSem...
148 #ifdef THREAD_DEBUGGING
149 WriteLog("CPU: SDL_SemWait(mainSem);\n");
150 #endif
151 SDL_SemWait(mainSem);
152
153 // There are exactly 800 slices of 21.333 cycles per frame, so it works out
154 // evenly.
155 #ifdef THREAD_DEBUGGING
156 WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
157 #endif
158                 for(int i=0; i<800; i++)
159                 {
160                         uint32_t cycles = 21;
161                         overflow += 0.333333334;
162
163                         if (overflow > 1.0)
164                         {
165                                 cycles++;
166                                 overflow -= 1.0;
167                         }
168
169                         Execute65C02(&mainCPU, cycles);
170                         WriteSampleToBuffer();
171
172                         // Dunno if this is correct (seems to be close enough)...
173                         vbl = (i < 670 ? true : false);
174                 }
175
176 #ifdef THREAD_DEBUGGING
177 WriteLog("CPU: SDL_mutexP(cpuMutex);\n");
178 #endif
179                 SDL_mutexP(cpuMutex);
180 // increment mainSem...
181 #ifdef THREAD_DEBUGGING
182 WriteLog("CPU: SDL_SemPost(mainSem);\n");
183 #endif
184                 SDL_SemPost(mainSem);
185 #ifdef THREAD_DEBUGGING
186 WriteLog("CPU: SDL_CondWait(cpuCond, cpuMutex);\n");
187 #endif
188                 SDL_CondWait(cpuCond, cpuMutex);
189
190 #ifdef THREAD_DEBUGGING
191 WriteLog("CPU: SDL_mutexV(cpuMutex);\n");
192 #endif
193                 SDL_mutexV(cpuMutex);
194         }
195         while (!cpuFinished);
196
197         SDL_DestroyMutex(cpuMutex);
198
199         return 0;
200 }
201 #endif
202
203
204 #if 1
205 //
206 // Request a change in the power state of the emulated Apple
207 //
208 void SetPowerState(void)
209 {
210 //      powerOnState = state;
211 //      pauseMode = !state;
212
213 //      if (!pauseMode)
214 //      {
215 //printf("Turning on...\n");
216                 // Transitioning from OFF to ON
217 //              mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
218 //              SoundResume();
219 //      }
220 //      else
221 //      {
222 //printf("Turning off...\n");
223                 // Turn it off...
224 //              SoundPause();
225 //      }
226         powerStateChangeRequested = true;
227 }
228 #endif
229
230
231 //
232 // Load a file into RAM/ROM image space
233 //
234 bool LoadImg(char * filename, uint8_t * ram, int size)
235 {
236         FILE * fp = fopen(filename, "rb");
237
238         if (fp == NULL)
239                 return false;
240
241         fread(ram, 1, size, fp);
242         fclose(fp);
243
244         return true;
245 }
246
247
248 static void SaveApple2State(const char * filename)
249 {
250 }
251
252
253 static bool LoadApple2State(const char * filename)
254 {
255         return false;
256 }
257
258
259 static void ResetApple2State(void)
260 {
261         keyDown = false;
262         openAppleDown = false;
263         closedAppleDown = false;
264         store80Mode = false;
265         vbl = false;
266         slotCXROM = false;
267         slotC3ROM = false;
268         ramrd = false;
269         ramwrt = false;
270         altzp = false;
271         ioudis = true;
272         dhires = false;
273         lcState = 0x02;
274 //      SwitchLC();                     // Make sure MMU is in sane state
275 //NOPE, does nothing    SetupAddressMap();
276         ResetMMUPointers();
277
278         // Without this, you can wedge the system :-/
279         memset(ram, 0, 0x10000);
280 //      memset(ram2, 0, 0x10000);
281         mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
282 }
283
284
285 #ifdef CPU_CLOCK_CHECKING
286 uint8_t counter = 0;
287 uint32_t totalCPU = 0;
288 uint64_t lastClock = 0;
289 #endif
290 //
291 // Main loop
292 //
293 int main(int /*argc*/, char * /*argv*/[])
294 {
295         InitLog("./apple2.log");
296         LoadSettings();
297         srand(time(NULL));                                                                      // Initialize RNG
298
299         // Zero out memory
300         memset(ram, 0, 0x10000);
301         memset(rom, 0, 0x10000);
302         memset(ram2, 0, 0x10000);
303
304         // Set up MMU
305         SetupAddressMap();
306         ResetMMUPointers();
307
308         // Set up V65C02 execution context
309         memset(&mainCPU, 0, sizeof(V65C02REGS));
310         mainCPU.RdMem = AppleReadMem;
311         mainCPU.WrMem = AppleWriteMem;
312         mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
313
314 //      alternateCharset = true;
315 //      if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000))
316         if (!LoadImg(settings.BIOSPath, rom + 0xC000, 0x4000))
317         {
318                 WriteLog("Could not open file '%s'!\n", settings.BIOSPath);
319                 return -1;
320         }
321
322 //Load up disk image from config file (for now)...
323         floppyDrive.LoadImage(settings.diskImagePath1, 0);
324         floppyDrive.LoadImage(settings.diskImagePath2, 1);
325 //      floppyDrive.LoadImage("./disks/temp.nib", 1);   // Load temp .nib file into second drive...
326
327         WriteLog("About to initialize video...\n");
328
329         if (!InitVideo())
330         {
331                 std::cout << "Aborting!" << std::endl;
332                 return -1;
333         }
334
335         GUI::Init(sdlRenderer);
336
337         // Have to do this *after* video init but *before* sound init...!
338 //Shouldn't be necessary since we're not doing emulation in the ISR...
339         if (settings.autoStateSaving)
340         {
341                 // Load last state from file...
342                 if (!LoadApple2State(settings.autoStatePath))
343                         WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
344         }
345
346         WriteLog("About to initialize audio...\n");
347         SoundInit();
348         SetupBlurTable();                                                       // Set up the color TV emulation blur table
349         running = true;                                                         // Set running status...
350         InitializeEventList();                                          // Clear the event list before we use it...
351         SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
352         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 s intervals
353         startTicks = SDL_GetTicks();
354
355 #ifdef THREADED_65C02
356         cpuCond = SDL_CreateCond();
357         mainSem = SDL_CreateSemaphore(1);
358         cpuThread = SDL_CreateThread(CPUThreadFunc, NULL, NULL);
359 //Hmm... CPU does POST (+1), wait, then WAIT (-1)
360 //      SDL_sem * mainMutex = SDL_CreateMutex();
361 #endif
362
363         WriteLog("Entering main loop...\n");
364
365         while (running)
366         {
367                 double timeToNextEvent = GetTimeToNextEvent();
368 #ifndef THREADED_65C02
369                 Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent));
370 #endif
371
372 #ifdef CPU_CLOCK_CHECKING
373 #ifndef THREADED_65C02
374 totalCPU += USEC_TO_M6502_CYCLES(timeToNextEvent);
375 #endif
376 #endif
377                 HandleNextEvent();
378         }
379
380 #ifdef THREADED_65C02
381 WriteLog("Main: cpuFinished = true;\n");
382 cpuFinished = true;
383 //#warning "If sound thread is behind, CPU thread will never wake up... !!! FIX !!!" [DONE]
384 //What to do? How do you know when the CPU is sleeping???
385 //USE A CONDITIONAL!!! OF COURSE!!!!!!11!11!11!!!1!
386 //Nope, use a semaphore...
387 WriteLog("Main: SDL_SemWait(mainSem);\n");
388 SDL_SemWait(mainSem);//should lock until CPU thread is waiting...
389 #endif
390
391 WriteLog("Main: SDL_CondSignal(cpuCond);//thread is probably asleep, wake it up\n");
392 SDL_CondSignal(cpuCond);//thread is probably asleep, wake it up
393 WriteLog("Main: SDL_WaitThread(cpuThread, NULL);\n");
394 SDL_WaitThread(cpuThread, NULL);
395 //nowok:SDL_WaitThread(CPUThreadFunc, NULL);
396 WriteLog("Main: SDL_DestroyCond(cpuCond);\n");
397 SDL_DestroyCond(cpuCond);
398
399 SDL_DestroySemaphore(mainSem);
400
401         if (settings.autoStateSaving)
402         {
403                 // Save state here...
404                 SaveApple2State(settings.autoStatePath);
405         }
406 floppyDrive.SaveImage(0);
407 floppyDrive.SaveImage(1);
408
409         SoundDone();
410         VideoDone();
411         SaveSettings();
412         LogDone();
413
414         return 0;
415 }
416
417
418 /*
419 Apple II keycodes
420 -----------------
421
422 Key     Aln CTL SHF BTH
423 -----------------------
424 space   $A0     $A0     $A0 $A0         No xlation
425 RETURN  $8D     $8D     $8D     $8D             No xlation
426 0               $B0     $B0     $B0     $B0             Need to screen shift+0 (?)
427 1!              $B1 $B1 $A1 $A1         No xlation
428 2"              $B2     $B2     $A2     $A2             No xlation
429 3#              $B3     $B3     $A3     $A3             No xlation
430 4$              $B4     $B4     $A4     $A4             No xlation
431 5%              $B5     $B5     $A5     $A5             No xlation
432 6&              $B6     $B6     $A6     $A6             No xlation
433 7'              $B7     $B7     $A7     $A7             No xlation
434 8(              $B8     $B8     $A8     $A8             No xlation
435 9)              $B9     $B9     $A9     $A9             No xlation
436 :*              $BA     $BA     $AA     $AA             No xlation
437 ;+              $BB     $BB     $AB     $AB             No xlation
438 ,<              $AC     $AC     $BC     $BC             No xlation
439 -=              $AD     $AD     $BD     $BD             No xlation
440 .>              $AE     $AE     $BE     $BE             No xlation
441 /?              $AF     $AF     $BF     $BF             No xlation
442 A               $C1     $81     $C1     $81
443 B               $C2     $82     $C2     $82
444 C               $C3     $83     $C3     $83
445 D               $C4     $84     $C4     $84
446 E               $C5     $85     $C5     $85
447 F               $C6     $86     $C6     $86
448 G               $C7     $87     $C7     $87
449 H               $C8     $88     $C8     $88
450 I               $C9     $89     $C9     $89
451 J               $CA     $8A     $CA     $8A
452 K               $CB     $8B     $CB     $8B
453 L               $CC     $8C     $CC     $8C
454 M               $CD     $8D     $DD     $9D             -> ODD
455 N^              $CE     $8E     $DE     $9E             -> ODD
456 O               $CF     $8F     $CF     $8F
457 P@              $D0     $90     $C0     $80             Need to xlate CTL+SHFT+P & SHFT+P (?)
458 Q               $D1     $91     $D1     $91
459 R               $D2     $92     $D2     $92
460 S               $D3     $93     $D3     $93
461 T               $D4     $94     $D4     $94
462 U               $D5     $95     $D5     $95
463 V               $D6     $96     $D6     $96
464 W               $D7     $97     $D7     $97
465 X               $D8     $98     $D8     $98
466 Y               $D9     $99     $D9     $99
467 Z               $DA     $9A     $DA     $9A
468 <-              $88     $88     $88     $88
469 ->              $95     $95     $95     $95
470 ESC             $9B     $9B     $9B     $9B             No xlation
471
472 */
473 //static uint64_t lastCPUCycles = 0;
474 static uint32_t frameCount = 0;
475 static void FrameCallback(void)
476 {
477         SDL_Event event;
478
479         while (SDL_PollEvent(&event))
480         {
481                 switch (event.type)
482                 {
483 // Problem with using SDL_TEXTINPUT is that it causes key delay. :-/
484 #if 0
485                 case SDL_TEXTINPUT:
486 //Need to do some key translation here, and screen out non-apple keys as well...
487 //(really, could do it all in SDL_KEYDOWN, would just have to get symbols &
488 // everything else done separately. this is slightly easier. :-P)
489 //                      if (event.key.keysym.sym == SDLK_TAB)   // Prelim key screening...
490                         if (event.edit.text[0] == '\t') // Prelim key screening...
491                                 break;
492
493                         lastKeyPressed = event.edit.text[0];
494                         keyDown = true;
495
496                         //kludge: should have a caps lock thingy here...
497                         //or all uppercase for ][+...
498 //                      if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
499 //                              lastKeyPressed &= 0xDF;         // Convert to upper case...
500
501                         break;
502 #endif
503                 case SDL_KEYDOWN:
504                         // Use ALT+Q to exit, as well as the usual window decoration method
505                         if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
506                                 running = false;
507
508                         // CTRL+RESET key emulation (mapped to CTRL+`)
509 // This doesn't work...
510 //                      if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
511 //                      if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
512                         if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
513                         {
514 //NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
515 //ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
516                                 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
517                                 break;
518                         }
519
520                         if (event.key.keysym.sym == SDLK_RIGHT)
521                                 lastKeyPressed = 0x15, keyDown = true;
522                         else if (event.key.keysym.sym == SDLK_LEFT)
523                                 lastKeyPressed = 0x08, keyDown = true;
524                         else if (event.key.keysym.sym == SDLK_UP)
525                                 lastKeyPressed = 0x0B, keyDown = true;
526                         else if (event.key.keysym.sym == SDLK_DOWN)
527                                 lastKeyPressed = 0x0A, keyDown = true;
528                         else if (event.key.keysym.sym == SDLK_RETURN)
529                                 lastKeyPressed = 0x0D, keyDown = true;
530                         else if (event.key.keysym.sym == SDLK_ESCAPE)
531                                 lastKeyPressed = 0x1B, keyDown = true;
532                         else if (event.key.keysym.sym == SDLK_BACKSPACE)
533                                 lastKeyPressed = 0x7F, keyDown = true;
534
535                         // Fix CTRL+key combo...
536                         if (event.key.keysym.mod & KMOD_CTRL)
537                         {
538                                 if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
539                                 {
540                                         lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 1;
541                                         keyDown = true;
542 //printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40);
543                                         break;
544                                 }
545                         }
546
547 #if 1
548                         // Fix SHIFT+key combo...
549                         if (event.key.keysym.mod & KMOD_SHIFT)
550                         {
551                                 if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
552                                 {
553                                         lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 0x41;
554                                         keyDown = true;
555 //printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40);
556                                         break;
557                                 }
558                                 else if (event.key.keysym.sym == SDLK_1)
559                                 {
560                                         lastKeyPressed = '!';
561                                         keyDown = true;
562                                         break;
563                                 }
564                                 else if (event.key.keysym.sym == SDLK_2)
565                                 {
566                                         lastKeyPressed = '@';
567                                         keyDown = true;
568                                         break;
569                                 }
570                                 else if (event.key.keysym.sym == SDLK_3)
571                                 {
572                                         lastKeyPressed = '#';
573                                         keyDown = true;
574                                         break;
575                                 }
576                                 else if (event.key.keysym.sym == SDLK_3)
577                                 {
578                                         lastKeyPressed = '#';
579                                         keyDown = true;
580                                         break;
581                                 }
582                                 else if (event.key.keysym.sym == SDLK_4)
583                                 {
584                                         lastKeyPressed = '$';
585                                         keyDown = true;
586                                         break;
587                                 }
588                                 else if (event.key.keysym.sym == SDLK_5)
589                                 {
590                                         lastKeyPressed = '%';
591                                         keyDown = true;
592                                         break;
593                                 }
594                                 else if (event.key.keysym.sym == SDLK_6)
595                                 {
596                                         lastKeyPressed = '^';
597                                         keyDown = true;
598                                         break;
599                                 }
600                                 else if (event.key.keysym.sym == SDLK_7)
601                                 {
602                                         lastKeyPressed = '&';
603                                         keyDown = true;
604                                         break;
605                                 }
606                                 else if (event.key.keysym.sym == SDLK_8)
607                                 {
608                                         lastKeyPressed = '*';
609                                         keyDown = true;
610                                         break;
611                                 }
612                                 else if (event.key.keysym.sym == SDLK_9)
613                                 {
614                                         lastKeyPressed = '(';
615                                         keyDown = true;
616                                         break;
617                                 }
618                                 else if (event.key.keysym.sym == SDLK_0)
619                                 {
620                                         lastKeyPressed = ')';
621                                         keyDown = true;
622                                         break;
623                                 }
624                                 else if (event.key.keysym.sym == SDLK_MINUS)
625                                 {
626                                         lastKeyPressed = '_';
627                                         keyDown = true;
628                                         break;
629                                 }
630                                 else if (event.key.keysym.sym == SDLK_EQUALS)
631                                 {
632                                         lastKeyPressed = '+';
633                                         keyDown = true;
634                                         break;
635                                 }
636                                 else if (event.key.keysym.sym == SDLK_LEFTBRACKET)
637                                 {
638                                         lastKeyPressed = '{';
639                                         keyDown = true;
640                                         break;
641                                 }
642                                 else if (event.key.keysym.sym == SDLK_RIGHTBRACKET)
643                                 {
644                                         lastKeyPressed = '}';
645                                         keyDown = true;
646                                         break;
647                                 }
648                                 else if (event.key.keysym.sym == SDLK_BACKSLASH)
649                                 {
650                                         lastKeyPressed = '|';
651                                         keyDown = true;
652                                         break;
653                                 }
654                                 else if (event.key.keysym.sym == SDLK_SEMICOLON)
655                                 {
656                                         lastKeyPressed = ':';
657                                         keyDown = true;
658                                         break;
659                                 }
660                                 else if (event.key.keysym.sym == SDLK_QUOTE)
661                                 {
662                                         lastKeyPressed = '"';
663                                         keyDown = true;
664                                         break;
665                                 }
666                                 else if (event.key.keysym.sym == SDLK_COMMA)
667                                 {
668                                         lastKeyPressed = '<';
669                                         keyDown = true;
670                                         break;
671                                 }
672                                 else if (event.key.keysym.sym == SDLK_PERIOD)
673                                 {
674                                         lastKeyPressed = '>';
675                                         keyDown = true;
676                                         break;
677                                 }
678                                 else if (event.key.keysym.sym == SDLK_SLASH)
679                                 {
680                                         lastKeyPressed = '?';
681                                         keyDown = true;
682                                         break;
683                                 }
684                                 else if (event.key.keysym.sym == SDLK_BACKQUOTE)
685                                 {
686                                         lastKeyPressed = '~';
687                                         keyDown = true;
688                                         break;
689                                 }
690                         }
691 #endif
692
693                         // General keys...
694                         if (event.key.keysym.sym >= SDLK_SPACE && event.key.keysym.sym <= SDLK_z)
695                         {
696                                 lastKeyPressed = event.key.keysym.sym;
697                                 keyDown = true;
698
699                                 // Check for Caps Lock key...
700                                 if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z && capsLock)
701                                         lastKeyPressed -= 0x20;
702
703                                 break;
704                         }
705
706                         if (event.key.keysym.sym == SDLK_PAUSE)
707                         {
708                                 pauseMode = !pauseMode;
709
710                                 if (pauseMode)
711                                 {
712                                         SoundPause();
713                                         SpawnMessage("*** PAUSED ***");
714                                 }
715                                 else
716                                 {
717                                         SoundResume();
718                                         SpawnMessage("*** RESUME ***");
719                                 }
720                         }
721
722                         // Paddle buttons 0 & 1
723                         if (event.key.keysym.sym == SDLK_INSERT)
724                                 openAppleDown = true;
725                         if (event.key.keysym.sym == SDLK_PAGEUP)
726                                 closedAppleDown = true;
727
728                         if (event.key.keysym.sym == SDLK_F11)
729                                 dumpDis = !dumpDis;                             // Toggle the disassembly process
730
731 /*else if (event.key.keysym.sym == SDLK_F9)
732 {
733         floppyDrive.CreateBlankImage(0);
734 //      SpawnMessage("Image cleared...");
735 }//*/
736 /*else if (event.key.keysym.sym == SDLK_F10)
737 {
738         floppyDrive.SwapImages();
739 //      SpawnMessage("Image swapped...");
740 }//*/
741
742                         if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
743                                 TogglePalette();
744                         else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
745                                 CycleScreenTypes();
746                         else if (event.key.keysym.sym == SDLK_F5)
747                         {
748                                 VolumeDown();
749                                 char volStr[19] = "[****************]";
750 //                              volStr[GetVolume()] = 0;
751                                 for(int i=GetVolume(); i<16; i++)
752                                         volStr[1 + i] = '-';
753                                 SpawnMessage("Volume: %s", volStr);
754                         }
755                         else if (event.key.keysym.sym == SDLK_F6)
756                         {
757                                 VolumeUp();
758                                 char volStr[19] = "[****************]";
759 //                              volStr[GetVolume()] = 0;
760                                 for(int i=GetVolume(); i<16; i++)
761                                         volStr[1 + i] = '-';
762                                 SpawnMessage("Volume: %s", volStr);
763                         }
764                         else if (event.key.keysym.sym == SDLK_F12)
765                         {
766                                 if (!fullscreenDebounce)
767                                 {
768                                         ToggleFullScreen();
769                                         fullscreenDebounce = true;
770                                 }
771                         }
772
773                         if (event.key.keysym.sym == SDLK_CAPSLOCK)
774                         {
775                                 if (!capsLockDebounce)
776                                 {
777                                         capsLock = !capsLock;
778                                         capsLockDebounce = true;
779                                 }
780                         }
781
782                         break;
783                 case SDL_KEYUP:
784                         if (event.key.keysym.sym == SDLK_F12)
785                                 fullscreenDebounce = false;
786                         if (event.key.keysym.sym == SDLK_CAPSLOCK)
787                                 capsLockDebounce = false;
788
789                         // Paddle buttons 0 & 1
790                         if (event.key.keysym.sym == SDLK_INSERT)
791                                 openAppleDown = false;
792                         if (event.key.keysym.sym == SDLK_PAGEUP)
793                                 closedAppleDown = false;
794
795 //                      if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
796 //                              keyDown = false;
797
798                         break;
799                 case SDL_MOUSEBUTTONDOWN:
800                         GUI::MouseDown(event.motion.x, event.motion.y, event.motion.state);
801                         break;
802                 case SDL_MOUSEBUTTONUP:
803                         GUI::MouseUp(event.motion.x, event.motion.y, event.motion.state);
804                         break;
805                 case SDL_MOUSEMOTION:
806                         GUI::MouseMove(event.motion.x, event.motion.y, event.motion.state);
807                         break;
808                 case SDL_WINDOWEVENT:
809                         if (event.window.event == SDL_WINDOWEVENT_LEAVE)
810                                 GUI::MouseMove(0, 0, 0);
811
812                         break;
813                 case SDL_QUIT:
814                         running = false;
815                 }
816         }
817
818         // Handle power request from the GUI
819         if (powerStateChangeRequested)
820         {
821                 if (GUI::powerOnState)
822                 {
823                         pauseMode = false;
824                         SoundResume();
825                         // Unlock the CPU thread...
826                         SDL_SemPost(mainSem);
827                 }
828                 else
829                 {
830                         pauseMode = true;
831                         // Should lock until CPU thread is waiting...
832                         SDL_SemWait(mainSem);
833                         SoundPause();
834                         ResetApple2State();
835                 }
836
837                 powerStateChangeRequested = false;
838         }
839
840 //#warning "!!! Taking MAJOR time hit with the video frame rendering !!!"
841 //      if (!pauseMode)
842         {
843                 RenderVideoFrame();
844         }
845
846         RenderScreenBuffer();
847         GUI::Render(sdlRenderer);
848         SDL_RenderPresent(sdlRenderer);
849         SetCallbackTime(FrameCallback, 16666.66666667);
850
851 #ifdef CPU_CLOCK_CHECKING
852 //We know it's stopped, so we can get away with this...
853 counter++;
854 if (counter == 60)
855 {
856         uint64_t clock = GetCurrentV65C02Clock();
857 //totalCPU += (uint32_t)(clock - lastClock);
858
859         printf("Executed %u cycles...\n", (uint32_t)(clock - lastClock));
860         lastClock = clock;
861 //      totalCPU = 0;
862         counter = 0;
863 }
864 #endif
865
866 // This is the problem: If you set the interval to 16, it runs faster than
867 // 1/60s per frame. If you set it to 17, it runs slower. What we need is to
868 // have it do 16 for one frame, then 17 for two others. Then it should average
869 // out to 1/60s per frame every 3 frames.
870         frameCount = (frameCount + 1) % 3;
871         uint32_t waitFrameTime = 17 - (frameCount == 0 ? 1 : 0);
872
873         // Wait for next frame...
874         while (SDL_GetTicks() - startTicks < waitFrameTime)
875                 SDL_Delay(1);
876
877         startTicks = SDL_GetTicks();
878 #if 0
879         uint64_t cpuCycles = GetCurrentV65C02Clock();
880         uint32_t cyclesBurned = (uint32_t)(cpuCycles - lastCPUCycles);
881         WriteLog("FrameCallback: used %i cycles\n", cyclesBurned);
882         lastCPUCycles = cpuCycles
883 #endif
884
885 //let's wait, then signal...
886 //works longer, but then still falls behind...
887 #ifdef THREADED_65C02
888         if (!pauseMode)
889                 SDL_CondSignal(cpuCond);//OK, let the CPU go another frame...
890 #endif
891 }
892
893
894 static void BlinkTimer(void)
895 {
896         flash = !flash;
897         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 sec intervals
898 }
899
900
901 /*
902 Next problem is this: How to have events occur and synchronize with the rest
903 of the threads?
904
905   o Have the CPU thread manage the timer mechanism? (need to have a method of carrying
906     remainder CPU cycles over...)
907
908 One way would be to use a fractional accumulator, then subtract 1 every
909 time it overflows. Like so:
910
911 double overflow = 0;
912 uint32_t time = 20;
913 while (!done)
914 {
915         Execute6808(&soundCPU, time);
916         overflow += 0.289115646;
917         if (overflow > 1.0)
918         {
919                 overflow -= 1.0;
920                 time = 21;
921         }
922         else
923                 time = 20;
924 }
925 */