]> Shamusworld >> Repos - apple2/blob - src/apple2.cpp
Added application icon.
[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         // Only do this if NOT in power off/emulation paused mode!
389         if (!pauseMode)
390                 // Should lock until CPU thread is waiting...
391                 SDL_SemWait(mainSem);
392 #endif
393
394 WriteLog("Main: SDL_CondSignal(cpuCond);//thread is probably asleep, wake it up\n");
395         SDL_CondSignal(cpuCond);//thread is probably asleep, wake it up
396 WriteLog("Main: SDL_WaitThread(cpuThread, NULL);\n");
397         SDL_WaitThread(cpuThread, NULL);
398 //nowok:SDL_WaitThread(CPUThreadFunc, NULL);
399 WriteLog("Main: SDL_DestroyCond(cpuCond);\n");
400         SDL_DestroyCond(cpuCond);
401         SDL_DestroySemaphore(mainSem);
402
403         if (settings.autoStateSaving)
404         {
405                 // Save state here...
406                 SaveApple2State(settings.autoStatePath);
407         }
408
409         floppyDrive.SaveImage(0);
410         floppyDrive.SaveImage(1);
411
412         SoundDone();
413         VideoDone();
414         SaveSettings();
415         LogDone();
416
417         return 0;
418 }
419
420
421 /*
422 Apple II keycodes
423 -----------------
424
425 Key     Aln CTL SHF BTH
426 -----------------------
427 space   $A0     $A0     $A0 $A0         No xlation
428 RETURN  $8D     $8D     $8D     $8D             No xlation
429 0               $B0     $B0     $B0     $B0             Need to screen shift+0 (?)
430 1!              $B1 $B1 $A1 $A1         No xlation
431 2"              $B2     $B2     $A2     $A2             No xlation
432 3#              $B3     $B3     $A3     $A3             No xlation
433 4$              $B4     $B4     $A4     $A4             No xlation
434 5%              $B5     $B5     $A5     $A5             No xlation
435 6&              $B6     $B6     $A6     $A6             No xlation
436 7'              $B7     $B7     $A7     $A7             No xlation
437 8(              $B8     $B8     $A8     $A8             No xlation
438 9)              $B9     $B9     $A9     $A9             No xlation
439 :*              $BA     $BA     $AA     $AA             No xlation
440 ;+              $BB     $BB     $AB     $AB             No xlation
441 ,<              $AC     $AC     $BC     $BC             No xlation
442 -=              $AD     $AD     $BD     $BD             No xlation
443 .>              $AE     $AE     $BE     $BE             No xlation
444 /?              $AF     $AF     $BF     $BF             No xlation
445 A               $C1     $81     $C1     $81
446 B               $C2     $82     $C2     $82
447 C               $C3     $83     $C3     $83
448 D               $C4     $84     $C4     $84
449 E               $C5     $85     $C5     $85
450 F               $C6     $86     $C6     $86
451 G               $C7     $87     $C7     $87
452 H               $C8     $88     $C8     $88
453 I               $C9     $89     $C9     $89
454 J               $CA     $8A     $CA     $8A
455 K               $CB     $8B     $CB     $8B
456 L               $CC     $8C     $CC     $8C
457 M               $CD     $8D     $DD     $9D             -> ODD
458 N^              $CE     $8E     $DE     $9E             -> ODD
459 O               $CF     $8F     $CF     $8F
460 P@              $D0     $90     $C0     $80             Need to xlate CTL+SHFT+P & SHFT+P (?)
461 Q               $D1     $91     $D1     $91
462 R               $D2     $92     $D2     $92
463 S               $D3     $93     $D3     $93
464 T               $D4     $94     $D4     $94
465 U               $D5     $95     $D5     $95
466 V               $D6     $96     $D6     $96
467 W               $D7     $97     $D7     $97
468 X               $D8     $98     $D8     $98
469 Y               $D9     $99     $D9     $99
470 Z               $DA     $9A     $DA     $9A
471 <-              $88     $88     $88     $88
472 ->              $95     $95     $95     $95
473 ESC             $9B     $9B     $9B     $9B             No xlation
474
475 */
476 //static uint64_t lastCPUCycles = 0;
477 static uint32_t frameCount = 0;
478 static void FrameCallback(void)
479 {
480         SDL_Event event;
481
482         while (SDL_PollEvent(&event))
483         {
484                 switch (event.type)
485                 {
486 // Problem with using SDL_TEXTINPUT is that it causes key delay. :-/
487 #if 0
488                 case SDL_TEXTINPUT:
489 //Need to do some key translation here, and screen out non-apple keys as well...
490 //(really, could do it all in SDL_KEYDOWN, would just have to get symbols &
491 // everything else done separately. this is slightly easier. :-P)
492 //                      if (event.key.keysym.sym == SDLK_TAB)   // Prelim key screening...
493                         if (event.edit.text[0] == '\t') // Prelim key screening...
494                                 break;
495
496                         lastKeyPressed = event.edit.text[0];
497                         keyDown = true;
498
499                         //kludge: should have a caps lock thingy here...
500                         //or all uppercase for ][+...
501 //                      if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
502 //                              lastKeyPressed &= 0xDF;         // Convert to upper case...
503
504                         break;
505 #endif
506                 case SDL_KEYDOWN:
507                         // Use ALT+Q to exit, as well as the usual window decoration method
508                         if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
509                                 running = false;
510
511                         // CTRL+RESET key emulation (mapped to CTRL+`)
512 // This doesn't work...
513 //                      if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
514 //                      if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
515                         if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
516                         {
517 //NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
518 //ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
519                                 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
520                                 break;
521                         }
522
523                         if (event.key.keysym.sym == SDLK_RIGHT)
524                                 lastKeyPressed = 0x15, keyDown = true;
525                         else if (event.key.keysym.sym == SDLK_LEFT)
526                                 lastKeyPressed = 0x08, keyDown = true;
527                         else if (event.key.keysym.sym == SDLK_UP)
528                                 lastKeyPressed = 0x0B, keyDown = true;
529                         else if (event.key.keysym.sym == SDLK_DOWN)
530                                 lastKeyPressed = 0x0A, keyDown = true;
531                         else if (event.key.keysym.sym == SDLK_RETURN)
532                                 lastKeyPressed = 0x0D, keyDown = true;
533                         else if (event.key.keysym.sym == SDLK_ESCAPE)
534                                 lastKeyPressed = 0x1B, keyDown = true;
535                         else if (event.key.keysym.sym == SDLK_BACKSPACE)
536                                 lastKeyPressed = 0x7F, keyDown = true;
537
538                         // Fix CTRL+key combo...
539                         if (event.key.keysym.mod & KMOD_CTRL)
540                         {
541                                 if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
542                                 {
543                                         lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 1;
544                                         keyDown = true;
545 //printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40);
546                                         break;
547                                 }
548                         }
549
550 #if 1
551                         // Fix SHIFT+key combo...
552                         if (event.key.keysym.mod & KMOD_SHIFT)
553                         {
554                                 if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
555                                 {
556                                         lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 0x41;
557                                         keyDown = true;
558 //printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40);
559                                         break;
560                                 }
561                                 else if (event.key.keysym.sym == SDLK_1)
562                                 {
563                                         lastKeyPressed = '!';
564                                         keyDown = true;
565                                         break;
566                                 }
567                                 else if (event.key.keysym.sym == SDLK_2)
568                                 {
569                                         lastKeyPressed = '@';
570                                         keyDown = true;
571                                         break;
572                                 }
573                                 else if (event.key.keysym.sym == SDLK_3)
574                                 {
575                                         lastKeyPressed = '#';
576                                         keyDown = true;
577                                         break;
578                                 }
579                                 else if (event.key.keysym.sym == SDLK_3)
580                                 {
581                                         lastKeyPressed = '#';
582                                         keyDown = true;
583                                         break;
584                                 }
585                                 else if (event.key.keysym.sym == SDLK_4)
586                                 {
587                                         lastKeyPressed = '$';
588                                         keyDown = true;
589                                         break;
590                                 }
591                                 else if (event.key.keysym.sym == SDLK_5)
592                                 {
593                                         lastKeyPressed = '%';
594                                         keyDown = true;
595                                         break;
596                                 }
597                                 else if (event.key.keysym.sym == SDLK_6)
598                                 {
599                                         lastKeyPressed = '^';
600                                         keyDown = true;
601                                         break;
602                                 }
603                                 else if (event.key.keysym.sym == SDLK_7)
604                                 {
605                                         lastKeyPressed = '&';
606                                         keyDown = true;
607                                         break;
608                                 }
609                                 else if (event.key.keysym.sym == SDLK_8)
610                                 {
611                                         lastKeyPressed = '*';
612                                         keyDown = true;
613                                         break;
614                                 }
615                                 else if (event.key.keysym.sym == SDLK_9)
616                                 {
617                                         lastKeyPressed = '(';
618                                         keyDown = true;
619                                         break;
620                                 }
621                                 else if (event.key.keysym.sym == SDLK_0)
622                                 {
623                                         lastKeyPressed = ')';
624                                         keyDown = true;
625                                         break;
626                                 }
627                                 else if (event.key.keysym.sym == SDLK_MINUS)
628                                 {
629                                         lastKeyPressed = '_';
630                                         keyDown = true;
631                                         break;
632                                 }
633                                 else if (event.key.keysym.sym == SDLK_EQUALS)
634                                 {
635                                         lastKeyPressed = '+';
636                                         keyDown = true;
637                                         break;
638                                 }
639                                 else if (event.key.keysym.sym == SDLK_LEFTBRACKET)
640                                 {
641                                         lastKeyPressed = '{';
642                                         keyDown = true;
643                                         break;
644                                 }
645                                 else if (event.key.keysym.sym == SDLK_RIGHTBRACKET)
646                                 {
647                                         lastKeyPressed = '}';
648                                         keyDown = true;
649                                         break;
650                                 }
651                                 else if (event.key.keysym.sym == SDLK_BACKSLASH)
652                                 {
653                                         lastKeyPressed = '|';
654                                         keyDown = true;
655                                         break;
656                                 }
657                                 else if (event.key.keysym.sym == SDLK_SEMICOLON)
658                                 {
659                                         lastKeyPressed = ':';
660                                         keyDown = true;
661                                         break;
662                                 }
663                                 else if (event.key.keysym.sym == SDLK_QUOTE)
664                                 {
665                                         lastKeyPressed = '"';
666                                         keyDown = true;
667                                         break;
668                                 }
669                                 else if (event.key.keysym.sym == SDLK_COMMA)
670                                 {
671                                         lastKeyPressed = '<';
672                                         keyDown = true;
673                                         break;
674                                 }
675                                 else if (event.key.keysym.sym == SDLK_PERIOD)
676                                 {
677                                         lastKeyPressed = '>';
678                                         keyDown = true;
679                                         break;
680                                 }
681                                 else if (event.key.keysym.sym == SDLK_SLASH)
682                                 {
683                                         lastKeyPressed = '?';
684                                         keyDown = true;
685                                         break;
686                                 }
687                                 else if (event.key.keysym.sym == SDLK_BACKQUOTE)
688                                 {
689                                         lastKeyPressed = '~';
690                                         keyDown = true;
691                                         break;
692                                 }
693                         }
694 #endif
695
696                         // General keys...
697                         if (event.key.keysym.sym >= SDLK_SPACE && event.key.keysym.sym <= SDLK_z)
698                         {
699                                 lastKeyPressed = event.key.keysym.sym;
700                                 keyDown = true;
701
702                                 // Check for Caps Lock key...
703                                 if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z && capsLock)
704                                         lastKeyPressed -= 0x20;
705
706                                 break;
707                         }
708
709                         if (event.key.keysym.sym == SDLK_PAUSE)
710                         {
711                                 pauseMode = !pauseMode;
712
713                                 if (pauseMode)
714                                 {
715                                         SoundPause();
716                                         SpawnMessage("*** PAUSED ***");
717                                 }
718                                 else
719                                 {
720                                         SoundResume();
721                                         SpawnMessage("*** RESUME ***");
722                                 }
723                         }
724
725                         // Paddle buttons 0 & 1
726                         if (event.key.keysym.sym == SDLK_INSERT)
727                                 openAppleDown = true;
728                         if (event.key.keysym.sym == SDLK_PAGEUP)
729                                 closedAppleDown = true;
730
731                         if (event.key.keysym.sym == SDLK_F11)
732                                 dumpDis = !dumpDis;                             // Toggle the disassembly process
733
734 /*else if (event.key.keysym.sym == SDLK_F9)
735 {
736         floppyDrive.CreateBlankImage(0);
737 //      SpawnMessage("Image cleared...");
738 }//*/
739 /*else if (event.key.keysym.sym == SDLK_F10)
740 {
741         floppyDrive.SwapImages();
742 //      SpawnMessage("Image swapped...");
743 }//*/
744
745                         if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
746                                 TogglePalette();
747                         else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
748                                 CycleScreenTypes();
749                         else if (event.key.keysym.sym == SDLK_F5)
750                         {
751                                 VolumeDown();
752                                 char volStr[19] = "[****************]";
753 //                              volStr[GetVolume()] = 0;
754                                 for(int i=GetVolume(); i<16; i++)
755                                         volStr[1 + i] = '-';
756                                 SpawnMessage("Volume: %s", volStr);
757                         }
758                         else if (event.key.keysym.sym == SDLK_F6)
759                         {
760                                 VolumeUp();
761                                 char volStr[19] = "[****************]";
762 //                              volStr[GetVolume()] = 0;
763                                 for(int i=GetVolume(); i<16; i++)
764                                         volStr[1 + i] = '-';
765                                 SpawnMessage("Volume: %s", volStr);
766                         }
767                         else if (event.key.keysym.sym == SDLK_F12)
768                         {
769                                 if (!fullscreenDebounce)
770                                 {
771                                         ToggleFullScreen();
772                                         fullscreenDebounce = true;
773                                 }
774                         }
775
776                         if (event.key.keysym.sym == SDLK_CAPSLOCK)
777                         {
778                                 if (!capsLockDebounce)
779                                 {
780                                         capsLock = !capsLock;
781                                         capsLockDebounce = true;
782                                 }
783                         }
784
785                         break;
786                 case SDL_KEYUP:
787                         if (event.key.keysym.sym == SDLK_F12)
788                                 fullscreenDebounce = false;
789                         if (event.key.keysym.sym == SDLK_CAPSLOCK)
790                                 capsLockDebounce = false;
791
792                         // Paddle buttons 0 & 1
793                         if (event.key.keysym.sym == SDLK_INSERT)
794                                 openAppleDown = false;
795                         if (event.key.keysym.sym == SDLK_PAGEUP)
796                                 closedAppleDown = false;
797
798 //                      if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
799 //                              keyDown = false;
800
801                         break;
802                 case SDL_MOUSEBUTTONDOWN:
803                         GUI::MouseDown(event.motion.x, event.motion.y, event.motion.state);
804                         break;
805                 case SDL_MOUSEBUTTONUP:
806                         GUI::MouseUp(event.motion.x, event.motion.y, event.motion.state);
807                         break;
808                 case SDL_MOUSEMOTION:
809                         GUI::MouseMove(event.motion.x, event.motion.y, event.motion.state);
810                         break;
811                 case SDL_WINDOWEVENT:
812                         if (event.window.event == SDL_WINDOWEVENT_LEAVE)
813                                 GUI::MouseMove(0, 0, 0);
814
815                         break;
816                 case SDL_QUIT:
817                         running = false;
818                 }
819         }
820
821         // Handle power request from the GUI
822         if (powerStateChangeRequested)
823         {
824                 if (GUI::powerOnState)
825                 {
826                         pauseMode = false;
827 //                      SoundResume();
828                         // Unlock the CPU thread...
829                         SDL_SemPost(mainSem);
830                 }
831                 else
832                 {
833                         pauseMode = true;
834                         // Should lock until CPU thread is waiting...
835                         SDL_SemWait(mainSem);
836 //                      SoundPause();
837                         ResetApple2State();
838                 }
839
840                 powerStateChangeRequested = false;
841         }
842
843 //#warning "!!! Taking MAJOR time hit with the video frame rendering !!!"
844 //      if (!pauseMode)
845         {
846                 RenderVideoFrame();
847         }
848
849         RenderScreenBuffer();
850         GUI::Render(sdlRenderer);
851         SDL_RenderPresent(sdlRenderer);
852         SetCallbackTime(FrameCallback, 16666.66666667);
853
854 #ifdef CPU_CLOCK_CHECKING
855 //We know it's stopped, so we can get away with this...
856 counter++;
857 if (counter == 60)
858 {
859         uint64_t clock = GetCurrentV65C02Clock();
860 //totalCPU += (uint32_t)(clock - lastClock);
861
862         printf("Executed %u cycles...\n", (uint32_t)(clock - lastClock));
863         lastClock = clock;
864 //      totalCPU = 0;
865         counter = 0;
866 }
867 #endif
868
869 // This is the problem: If you set the interval to 16, it runs faster than
870 // 1/60s per frame. If you set it to 17, it runs slower. What we need is to
871 // have it do 16 for one frame, then 17 for two others. Then it should average
872 // out to 1/60s per frame every 3 frames.
873         frameCount = (frameCount + 1) % 3;
874         uint32_t waitFrameTime = 17 - (frameCount == 0 ? 1 : 0);
875
876         // Wait for next frame...
877         while (SDL_GetTicks() - startTicks < waitFrameTime)
878                 SDL_Delay(1);
879
880         startTicks = SDL_GetTicks();
881 #if 0
882         uint64_t cpuCycles = GetCurrentV65C02Clock();
883         uint32_t cyclesBurned = (uint32_t)(cpuCycles - lastCPUCycles);
884         WriteLog("FrameCallback: used %i cycles\n", cyclesBurned);
885         lastCPUCycles = cpuCycles
886 #endif
887
888 //let's wait, then signal...
889 //works longer, but then still falls behind...
890 #ifdef THREADED_65C02
891         if (!pauseMode)
892                 SDL_CondSignal(cpuCond);//OK, let the CPU go another frame...
893 #endif
894 }
895
896
897 static void BlinkTimer(void)
898 {
899         flash = !flash;
900         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 sec intervals
901 }
902
903
904 /*
905 Next problem is this: How to have events occur and synchronize with the rest
906 of the threads?
907
908   o Have the CPU thread manage the timer mechanism? (need to have a method of carrying
909     remainder CPU cycles over...)
910
911 One way would be to use a fractional accumulator, then subtract 1 every
912 time it overflows. Like so:
913
914 double overflow = 0;
915 uint32_t time = 20;
916 while (!done)
917 {
918         Execute6808(&soundCPU, time);
919         overflow += 0.289115646;
920         if (overflow > 1.0)
921         {
922                 overflow -= 1.0;
923                 time = 21;
924         }
925         else
926                 time = 20;
927 }
928 */