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