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