2 // Apple 2 SDL Portable Apple Emulator
5 // © 2017 Underground Software
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.
12 // JLH = James Hammons <jlhamm@acm.org>
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
25 // - Port to SDL [DONE]
27 // - Weed out unneeded functions [DONE]
29 // - 128K IIe related stuff [DONE]
30 // - State loading/saving
54 // Debug and misc. defines
56 #define THREADED_65C02
57 #define CPU_THREAD_OVERFLOW_COMPENSATION
59 //#define CPU_CLOCK_CHECKING
60 //#define THREAD_DEBUGGING
61 #define SOFT_SWITCH_DEBUGGING
65 uint8_t ram[0x10000], rom[0x10000]; // RAM & ROM spaces
66 uint8_t ram2[0x10000]; // Auxillary RAM
67 V65C02REGS mainCPU; // v65C02 execution context
68 uint8_t appleType = APPLE_TYPE_IIE;
69 FloppyDrive floppyDrive;
70 bool powerStateChangeRequested = false;
72 // Local variables (actually, they're global since they're not static)
74 uint8_t lastKeyPressed = 0;
76 bool openAppleDown = false;
77 bool closedAppleDown = false;
78 bool store80Mode = false;
80 bool slotCXROM = false;
81 bool slotC3ROM = false;
87 // Language card state (ROM read, no write)
88 uint8_t lcState = 0x02;
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 bool resetKeyDown = false;
97 static int8_t hideMouseTimeout = 60;
99 // Vars to handle the //e's 2-key rollover
100 static SDL_Keycode keysHeld[2];
101 static uint8_t keysHeldAppleCode[2];
102 static uint8_t keyDownCount = 0;
103 static uint8_t keyDelay = 0;
107 static void SaveApple2State(const char * filename);
108 static bool LoadApple2State(const char * filename);
109 static void ResetApple2State(void);
111 // Local timer callback functions
113 static void FrameCallback(void);
114 static void BlinkTimer(void);
116 #ifdef THREADED_65C02
117 // Test of threaded execution of 6502
118 static SDL_Thread * cpuThread = NULL;
119 static SDL_cond * cpuCond = NULL;
120 static SDL_sem * mainSem = NULL;
121 static bool cpuFinished = false;
123 // NB: Apple //e Manual sez 6502 is running @ 1,022,727 Hz
125 // Let's try a thread...
127 // Here's how it works: Execute 1 frame's worth, then sleep. Other stuff wakes
130 int CPUThreadFunc(void * data)
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();
136 #ifdef CPU_THREAD_OVERFLOW_COMPENSATION
137 float overflow = 0.0;
142 // decrement mainSem...
143 #ifdef THREAD_DEBUGGING
144 WriteLog("CPU: SDL_SemWait(mainSem);\n");
146 SDL_SemWait(mainSem);
148 // There are exactly 800 slices of 21.333 cycles per frame, so it works
150 #ifdef THREAD_DEBUGGING
151 WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
153 for(int i=0; i<800; i++)
155 uint32_t cycles = 21;
156 overflow += 0.333333334;
164 // If the CTRL+Reset key combo is being held, make sure the RESET
165 // line stays asserted:
167 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
169 Execute65C02(&mainCPU, cycles);
170 WriteSampleToBuffer();
172 // Dunno if this is correct (seems to be close enough)...
173 vbl = (i < 670 ? true : false);
176 #ifdef THREAD_DEBUGGING
177 WriteLog("CPU: SDL_mutexP(cpuMutex);\n");
179 SDL_mutexP(cpuMutex);
180 // increment mainSem...
181 #ifdef THREAD_DEBUGGING
182 WriteLog("CPU: SDL_SemPost(mainSem);\n");
184 SDL_SemPost(mainSem);
185 #ifdef THREAD_DEBUGGING
186 WriteLog("CPU: SDL_CondWait(cpuCond, cpuMutex);\n");
188 SDL_CondWait(cpuCond, cpuMutex);
190 #ifdef THREAD_DEBUGGING
191 WriteLog("CPU: SDL_mutexV(cpuMutex);\n");
193 SDL_mutexV(cpuMutex);
195 while (!cpuFinished);
197 SDL_DestroyMutex(cpuMutex);
205 // Request a change in the power state of the emulated Apple
207 void SetPowerState(void)
209 powerStateChangeRequested = true;
214 // Load a file into RAM/ROM image space
216 bool LoadImg(char * filename, uint8_t * ram, int size)
218 FILE * fp = fopen(filename, "rb");
223 fread(ram, 1, size, fp);
230 const uint8_t stateHeader[19] = "APPLE2SAVESTATE1.0";
231 static void SaveApple2State(const char * filename)
233 WriteLog("Main: Saving Apple2 state...\n");
234 FILE * file = fopen(filename, "wb");
238 WriteLog("Could not open file \"%s\" for writing!\n", filename);
243 fwrite(stateHeader, 1, 18, file);
245 // Write out CPU state
246 fwrite(&mainCPU, 1, sizeof(mainCPU), file);
248 // Write out main memory
249 fwrite(ram, 1, 0x10000, file);
250 fwrite(ram2, 1, 0x10000, file);
252 // Write out state variables
253 fputc((uint8_t)keyDown, file);
254 fputc((uint8_t)openAppleDown, file);
255 fputc((uint8_t)closedAppleDown, file);
256 fputc((uint8_t)store80Mode, file);
257 fputc((uint8_t)vbl, file);
258 fputc((uint8_t)slotCXROM, file);
259 fputc((uint8_t)slotC3ROM, file);
260 fputc((uint8_t)ramrd, file);
261 fputc((uint8_t)ramwrt, file);
262 fputc((uint8_t)altzp, file);
263 fputc((uint8_t)ioudis, file);
264 fputc((uint8_t)dhires, file);
265 fputc((uint8_t)flash, file);
266 fputc((uint8_t)textMode, file);
267 fputc((uint8_t)mixedMode, file);
268 fputc((uint8_t)displayPage2, file);
269 fputc((uint8_t)hiRes, file);
270 fputc((uint8_t)alternateCharset, file);
271 fputc((uint8_t)col80Mode, file);
272 fputc(lcState, file);
274 // Write out floppy state
275 floppyDrive.SaveState(file);
280 static bool LoadApple2State(const char * filename)
282 WriteLog("Main: Loading Apple2 state...\n");
283 FILE * file = fopen(filename, "rb");
287 WriteLog("Could not open file \"%s\" for reading!\n", filename);
292 fread(buffer, 1, 18, file);
295 if (memcmp(buffer, stateHeader, 18) != 0)
298 WriteLog("File \"%s\" is not a valid Apple2 save state file!\n", filename);
303 fread(&mainCPU, 1, sizeof(mainCPU), file);
306 fread(ram, 1, 0x10000, file);
307 fread(ram2, 1, 0x10000, file);
309 // Read in state variables
310 keyDown = (bool)fgetc(file);
311 openAppleDown = (bool)fgetc(file);
312 closedAppleDown = (bool)fgetc(file);
313 store80Mode = (bool)fgetc(file);
314 vbl = (bool)fgetc(file);
315 slotCXROM = (bool)fgetc(file);
316 slotC3ROM = (bool)fgetc(file);
317 ramrd = (bool)fgetc(file);
318 ramwrt = (bool)fgetc(file);
319 altzp = (bool)fgetc(file);
320 ioudis = (bool)fgetc(file);
321 dhires = (bool)fgetc(file);
322 flash = (bool)fgetc(file);
323 textMode = (bool)fgetc(file);
324 mixedMode = (bool)fgetc(file);
325 displayPage2 = (bool)fgetc(file);
326 hiRes = (bool)fgetc(file);
327 alternateCharset = (bool)fgetc(file);
328 col80Mode = (bool)fgetc(file);
329 lcState = fgetc(file);
331 // Read in floppy state
332 floppyDrive.LoadState(file);
336 // Make sure things are in a sane state before execution :-P
337 mainCPU.RdMem = AppleReadMem;
338 mainCPU.WrMem = AppleWriteMem;
345 static void ResetApple2State(void)
348 openAppleDown = false;
349 closedAppleDown = false;
362 // Without this, you can wedge the system :-/
363 memset(ram, 0, 0x10000);
364 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
368 #ifdef CPU_CLOCK_CHECKING
370 uint32_t totalCPU = 0;
371 uint64_t lastClock = 0;
376 int main(int /*argc*/, char * /*argv*/[])
378 InitLog("./apple2.log");
380 srand(time(NULL)); // Initialize RNG
383 memset(ram, 0, 0x10000);
384 memset(rom, 0, 0x10000);
385 memset(ram2, 0, 0x10000);
391 // Set up V65C02 execution context
392 memset(&mainCPU, 0, sizeof(V65C02REGS));
393 mainCPU.RdMem = AppleReadMem;
394 mainCPU.WrMem = AppleWriteMem;
395 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
397 if (!LoadImg(settings.BIOSPath, rom + 0xC000, 0x4000))
399 WriteLog("Could not open file '%s'!\n", settings.BIOSPath);
403 WriteLog("About to initialize video...\n");
407 printf("Could not init screen: aborting!\n");
411 GUI::Init(sdlRenderer);
412 WriteLog("About to initialize audio...\n");
415 if (settings.autoStateSaving)
417 // Load last state from file...
418 if (!LoadApple2State(settings.autoStatePath))
419 WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
423 InitializeEventList();
424 // Set frame to fire at 1/60 s interval
425 SetCallbackTime(FrameCallback, 16666.66666667);
426 // Set up blinking at 1/4 s intervals
427 SetCallbackTime(BlinkTimer, 250000);
428 startTicks = SDL_GetTicks();
430 #ifdef THREADED_65C02
431 // Kick off the CPU...
432 cpuCond = SDL_CreateCond();
433 mainSem = SDL_CreateSemaphore(1);
434 cpuThread = SDL_CreateThread(CPUThreadFunc, NULL, NULL);
435 //Hmm... CPU does POST (+1), wait, then WAIT (-1)
436 // SDL_sem * mainMutex = SDL_CreateMutex();
439 WriteLog("Entering main loop...\n");
443 double timeToNextEvent = GetTimeToNextEvent();
444 #ifndef THREADED_65C02
445 Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent));
447 #ifdef CPU_CLOCK_CHECKING
448 totalCPU += USEC_TO_M6502_CYCLES(timeToNextEvent);
454 #ifdef THREADED_65C02
455 WriteLog("Main: cpuFinished = true;\n");
458 WriteLog("Main: SDL_SemWait(mainSem);\n");
459 // Only do this if NOT in power off/emulation paused mode!
461 // Should lock until CPU thread is waiting...
462 SDL_SemWait(mainSem);
465 WriteLog("Main: SDL_CondSignal(cpuCond);\n");
466 SDL_CondSignal(cpuCond);//thread is probably asleep, so wake it up
467 WriteLog("Main: SDL_WaitThread(cpuThread, NULL);\n");
468 SDL_WaitThread(cpuThread, NULL);
469 WriteLog("Main: SDL_DestroyCond(cpuCond);\n");
470 SDL_DestroyCond(cpuCond);
471 SDL_DestroySemaphore(mainSem);
473 // Autosave state here, if requested...
474 if (settings.autoStateSaving)
475 SaveApple2State(settings.autoStatePath);
477 floppyDrive.SaveImage(0);
478 floppyDrive.SaveImage(1);
490 // Apple //e scancodes. Tables are normal (0), +CTRL (1), +SHIFT (2),
491 // +CTRL+SHIFT (3). Order of keys is:
492 // Delete, left, tab, down, up, return, right, escape
493 // Space, single quote, comma, minus, period, slash
495 // Semicolon, equals, left bracket, backslash, right bracket, backquote
496 // Letters a-z (lowercase)
498 // N.B.: The Apple //e keyboard maps its shift characters like most modern US
499 // keyboards, so this table should suffice for the shifted keys just fine.
501 uint8_t apple2e_keycode[4][56] = {
503 0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B,
504 0x20, 0x27, 0x2C, 0x2D, 0x2E, 0x2F,
505 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
506 0x3B, 0x3D, 0x5B, 0x5C, 0x5D, 0x60,
507 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A,
508 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74,
509 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A
512 0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B,
513 0x20, 0x27, 0x2C, 0x1F, 0x2E, 0x2F,
514 0x30, 0x31, 0x00, 0x33, 0x34, 0x35, 0x1E, 0x37, 0x38, 0x39,
515 0x3B, 0x3D, 0x1B, 0x1C, 0x1D, 0x60,
516 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
517 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
518 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A
521 0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B,
522 0x20, 0x22, 0x3C, 0x5F, 0x3E, 0x3F,
523 0x29, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28,
524 0x3A, 0x2B, 0x7B, 0x7C, 0x7D, 0x7E,
525 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
526 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54,
527 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A
529 { // With CTRL+Shift held
530 0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B,
531 0x20, 0x22, 0x3C, 0x1F, 0x3E, 0x3F,
532 0x29, 0x21, 0x00, 0x23, 0x24, 0x25, 0x1E, 0x26, 0x2A, 0x28,
533 0x3A, 0x2B, 0x1B, 0x1C, 0x1D, 0x7E,
534 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
535 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
536 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A
540 static uint32_t frameCount = 0;
541 static void FrameCallback(void)
546 while (SDL_PollEvent(&event))
551 // We do our own repeat handling thank you very much! :-)
552 if (event.key.repeat != 0)
555 // Use CTRL+SHIFT+Q to exit, as well as the usual window decoration
557 if ((event.key.keysym.mod & KMOD_CTRL)
558 && (event.key.keysym.mod & KMOD_SHIFT)
559 && (event.key.keysym.sym == SDLK_q))
562 // We return here, because we don't want to pick up any
563 // spurious keypresses with our exit sequence.
567 // CTRL+RESET key emulation (mapped to CTRL+HOME)
568 if ((event.key.keysym.mod & KMOD_CTRL)
569 && (event.key.keysym.sym == SDLK_HOME))
571 //seems to leave the machine in an inconsistent state vis-a-vis the language card... [does it anymore?]
576 // There has GOT to be a better way off mapping SDLKs to our
577 // keyindex. But for now, this should suffice.
580 switch (event.key.keysym.sym)
582 case SDLK_BACKSPACE: keyIndex = 0; break;
583 case SDLK_LEFT: keyIndex = 1; break;
584 case SDLK_TAB: keyIndex = 2; break;
585 case SDLK_DOWN: keyIndex = 3; break;
586 case SDLK_UP: keyIndex = 4; break;
587 case SDLK_RETURN: keyIndex = 5; break;
588 case SDLK_RIGHT: keyIndex = 6; break;
589 case SDLK_ESCAPE: keyIndex = 7; break;
590 case SDLK_SPACE: keyIndex = 8; break;
591 case SDLK_QUOTE: keyIndex = 9; break;
592 case SDLK_COMMA: keyIndex = 10; break;
593 case SDLK_MINUS: keyIndex = 11; break;
594 case SDLK_PERIOD: keyIndex = 12; break;
595 case SDLK_SLASH: keyIndex = 13; break;
596 case SDLK_0: keyIndex = 14; break;
597 case SDLK_1: keyIndex = 15; break;
598 case SDLK_2: keyIndex = 16; break;
599 case SDLK_3: keyIndex = 17; break;
600 case SDLK_4: keyIndex = 18; break;
601 case SDLK_5: keyIndex = 19; break;
602 case SDLK_6: keyIndex = 20; break;
603 case SDLK_7: keyIndex = 21; break;
604 case SDLK_8: keyIndex = 22; break;
605 case SDLK_9: keyIndex = 23; break;
606 case SDLK_SEMICOLON: keyIndex = 24; break;
607 case SDLK_EQUALS: keyIndex = 25; break;
608 case SDLK_LEFTBRACKET: keyIndex = 26; break;
609 case SDLK_BACKSLASH: keyIndex = 27; break;
610 case SDLK_RIGHTBRACKET: keyIndex = 28; break;
611 case SDLK_BACKQUOTE: keyIndex = 29; break;
612 case SDLK_a: keyIndex = 30; break;
613 case SDLK_b: keyIndex = 31; break;
614 case SDLK_c: keyIndex = 32; break;
615 case SDLK_d: keyIndex = 33; break;
616 case SDLK_e: keyIndex = 34; break;
617 case SDLK_f: keyIndex = 35; break;
618 case SDLK_g: keyIndex = 36; break;
619 case SDLK_h: keyIndex = 37; break;
620 case SDLK_i: keyIndex = 38; break;
621 case SDLK_j: keyIndex = 39; break;
622 case SDLK_k: keyIndex = 40; break;
623 case SDLK_l: keyIndex = 41; break;
624 case SDLK_m: keyIndex = 42; break;
625 case SDLK_n: keyIndex = 43; break;
626 case SDLK_o: keyIndex = 44; break;
627 case SDLK_p: keyIndex = 45; break;
628 case SDLK_q: keyIndex = 46; break;
629 case SDLK_r: keyIndex = 47; break;
630 case SDLK_s: keyIndex = 48; break;
631 case SDLK_t: keyIndex = 49; break;
632 case SDLK_u: keyIndex = 50; break;
633 case SDLK_v: keyIndex = 51; break;
634 case SDLK_w: keyIndex = 52; break;
635 case SDLK_x: keyIndex = 53; break;
636 case SDLK_y: keyIndex = 54; break;
637 case SDLK_z: keyIndex = 55; break;
640 // Stuff the key in if we have a valid one...
641 if (keyIndex != 0xFF)
643 // Handle Shift, CTRL, & Shift+CTRL combos
646 if (event.key.keysym.mod & KMOD_CTRL)
649 if (event.key.keysym.mod & KMOD_SHIFT)
652 lastKeyPressed = apple2e_keycode[table][keyIndex];
657 && (lastKeyPressed >= 0x61) && (lastKeyPressed <= 0x7A))
658 lastKeyPressed -= 0x20;
660 // Handle key repeat if the key hasn't been held
661 // if (keyDelay == 0)
666 // Buffer the key held. Note that the last key is always
667 // stuffed into keysHeld[0].
668 if (keyDownCount >= 2)
670 keysHeld[1] = keysHeld[0];
671 keysHeldAppleCode[1] = keysHeldAppleCode[0];
673 if (keyDownCount > 2)
677 keysHeld[0] = event.key.keysym.sym;
678 keysHeldAppleCode[0] = lastKeyPressed;
682 if (event.key.keysym.sym == SDLK_PAUSE)
684 pauseMode = !pauseMode;
689 SpawnMessage("*** PAUSED ***");
694 SpawnMessage("*** RESUME ***");
698 else if (event.key.keysym.sym == SDLK_LALT)
699 openAppleDown = true;
700 else if (event.key.keysym.sym == SDLK_RALT)
701 closedAppleDown = true;
702 // Toggle the disassembly process
703 else if (event.key.keysym.sym == SDLK_F11)
706 /*else if (event.key.keysym.sym == SDLK_F9)
708 floppyDrive.CreateBlankImage(0);
709 // SpawnMessage("Image cleared...");
711 /*else if (event.key.keysym.sym == SDLK_F10)
713 floppyDrive.SwapImages();
714 // SpawnMessage("Image swapped...");
717 else if (event.key.keysym.sym == SDLK_F2)
719 else if (event.key.keysym.sym == SDLK_F3)
721 else if (event.key.keysym.sym == SDLK_F5)
724 char volStr[19] = "[****************]";
726 for(int i=GetVolume(); i<16; i++)
729 SpawnMessage("Volume: %s", volStr);
731 else if (event.key.keysym.sym == SDLK_F6)
734 char volStr[19] = "[****************]";
736 for(int i=GetVolume(); i<16; i++)
739 SpawnMessage("Volume: %s", volStr);
741 else if (event.key.keysym.sym == SDLK_F12)
743 if (!fullscreenDebounce)
746 fullscreenDebounce = true;
749 else if (event.key.keysym.sym == SDLK_CAPSLOCK)
751 if (!capsLockDebounce)
753 capsLock = !capsLock;
754 capsLockDebounce = true;
761 if (event.key.keysym.sym == SDLK_F12)
762 fullscreenDebounce = false;
763 else if (event.key.keysym.sym == SDLK_CAPSLOCK)
764 capsLockDebounce = false;
765 // Paddle buttons 0 & 1
766 else if (event.key.keysym.sym == SDLK_LALT)
767 openAppleDown = false;
768 else if (event.key.keysym.sym == SDLK_RALT)
769 closedAppleDown = false;
770 else if ((event.key.keysym.mod & KMOD_CTRL)
771 && (event.key.keysym.sym == SDLK_HOME))
772 resetKeyDown = false;
775 // Handle key buffering 'key up' event (2 key rollover)
776 if ((keyDownCount == 1) && (event.key.keysym.sym == keysHeld[0]))
779 keyDelay = 0; // Reset key delay
781 else if (keyDownCount == 2)
783 if (event.key.keysym.sym == keysHeld[0])
786 keysHeld[0] = keysHeld[1];
787 keysHeldAppleCode[0] = keysHeldAppleCode[1];
789 else if (event.key.keysym.sym == keysHeld[1])
798 case SDL_MOUSEBUTTONDOWN:
799 GUI::MouseDown(event.motion.x, event.motion.y, event.motion.state);
802 case SDL_MOUSEBUTTONUP:
803 GUI::MouseUp(event.motion.x, event.motion.y, event.motion.state);
806 case SDL_MOUSEMOTION:
807 GUI::MouseMove(event.motion.x, event.motion.y, event.motion.state);
809 // Handle mouse showing when the mouse is hidden...
810 if (hideMouseTimeout == -1)
813 hideMouseTimeout = 60;
816 case SDL_WINDOWEVENT:
817 if (event.window.event == SDL_WINDOWEVENT_LEAVE)
818 GUI::MouseMove(0, 0, 0);
827 // Hide the mouse if it's been 1s since the last time it was moved
828 // N.B.: Should disable mouse hiding if it's over the GUI...
829 if (hideMouseTimeout > 0)
831 else if (hideMouseTimeout == 0)
837 // Stuff the Apple keyboard buffer, if any keys are pending
838 // N.B.: May have to simulate the key repeat delay too [yup, sure do]
839 if (keyDownCount > 0)
846 lastKeyPressed = keysHeldAppleCode[0];
851 // Handle power request from the GUI
852 if (powerStateChangeRequested)
854 if (GUI::powerOnState)
857 // Unlock the CPU thread...
858 SDL_SemPost(mainSem);
863 // Should lock until CPU thread is waiting...
864 SDL_SemWait(mainSem);
868 powerStateChangeRequested = false;
871 // Render the Apple screen + GUI overlay
872 RenderAppleScreen(sdlRenderer);
873 GUI::Render(sdlRenderer);
874 SDL_RenderPresent(sdlRenderer);
875 SetCallbackTime(FrameCallback, 16666.66666667);
877 #ifdef CPU_CLOCK_CHECKING
878 //We know it's stopped, so we can get away with this...
882 uint64_t clock = GetCurrentV65C02Clock();
883 //totalCPU += (uint32_t)(clock - lastClock);
885 printf("Executed %u cycles...\n", (uint32_t)(clock - lastClock));
892 // This is the problem: If you set the interval to 16, it runs faster than
893 // 1/60s per frame. If you set it to 17, it runs slower. What we need is to
894 // have it do 16 for one frame, then 17 for two others. Then it should average
895 // out to 1/60s per frame every 3 frames. [And now it does!]
896 frameCount = (frameCount + 1) % 3;
897 uint32_t waitFrameTime = 17 - (frameCount == 0 ? 1 : 0);
899 // Wait for next frame...
900 while (SDL_GetTicks() - startTicks < waitFrameTime)
903 startTicks = SDL_GetTicks();
905 uint64_t cpuCycles = GetCurrentV65C02Clock();
906 uint32_t cyclesBurned = (uint32_t)(cpuCycles - lastCPUCycles);
907 WriteLog("FrameCallback: used %i cycles\n", cyclesBurned);
908 lastCPUCycles = cpuCycles
911 //let's wait, then signal...
912 //works longer, but then still falls behind... [FIXED, see above]
913 #ifdef THREADED_65C02
915 SDL_CondSignal(cpuCond);//OK, let the CPU go another frame...
920 static void BlinkTimer(void)
922 // Set up blinking at 1/4 sec intervals
924 SetCallbackTime(BlinkTimer, 250000);
929 Next problem is this: How to have events occur and synchronize with the rest
932 o Have the CPU thread manage the timer mechanism? (need to have a method of carrying
933 remainder CPU cycles over...)
935 One way would be to use a fractional accumulator, then subtract 1 every
936 time it overflows. Like so:
942 Execute6808(&soundCPU, time);
943 overflow += 0.289115646;