]> Shamusworld >> Repos - apple2/blobdiff - src/apple2.cpp
Added support for the Apple High Speed SCSI card.
[apple2] / src / apple2.cpp
old mode 100755 (executable)
new mode 100644 (file)
index 12eb8c9..ce38ed1
@@ -1,58 +1,62 @@
 //
 // Apple 2 SDL Portable Apple Emulator
 //
-// by James L. Hammons
-// (C) 2005 Underground Software
+// by James Hammons
+// © 2018 Underground Software
 //
-// Loosely based on AppleWin by Tom Charlesworth which was based on AppleWin by
-// Oliver Schmidt which was based on AppleWin by Michael O'Brien. :-) Parts are
-// also derived from ApplePC. Too bad it was closed source--it could have been
-// *the* premier Apple II emulator out there.
+// Parts loosely inspired by AppleWin by Tom Charlesworth which was based on
+// AppleWin by Oliver Schmidt which was based on AppleWin by Michael O'Brien.
+// :-)  Some parts (mainly TV rendering) are derived from ApplePC.  Too bad it
+// was closed source--it could have been *the* premier Apple II emulator out
+// there.
 //
-// JLH = James L. Hammons <jlhamm@acm.org>
+// JLH = James Hammons <jlhamm@acm.org>
 //
 // WHO  WHEN        WHAT
-// ---  ----------  ------------------------------------------------------------
+// ---  ----------  -----------------------------------------------------------
 // JLH  11/12/2005  Initial port to SDL
 // JLH  11/18/2005  Wired up graphic soft switches
 // JLH  12/02/2005  Setup timer subsystem for more accurate time keeping
 // JLH  12/12/2005  Added preliminary state saving support
+// JLH  09/24/2013  Added //e support
 //
 
 // STILL TO DO:
 //
 // - Port to SDL [DONE]
-// - GUI goodies
 // - Weed out unneeded functions [DONE]
 // - Disk I/O [DONE]
-// - 128K IIe related stuff
-// - State loading/saving
+// - 128K IIe related stuff [DONE]
+// - State loading/saving [DONE]
+// - GUI goodies
+//
+// BUGS:
+//
+// - Having a directory in the ${disks} directory causes a segfault in floppy
 //
 
 #include "apple2.h"
 
 #include <SDL2/SDL.h>
 #include <fstream>
-#include <string>
 #include <iomanip>
 #include <iostream>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string>
 #include <time.h>
+#include "firmware.h"
+#include "floppydrive.h"
+#include "harddrive.h"
 #include "log.h"
-#include "video.h"
-#include "sound.h"
+#include "mmu.h"
+#include "mockingboard.h"
 #include "settings.h"
-#include "v65c02.h"
-#include "applevideo.h"
+#include "sound.h"
 #include "timing.h"
-#include "floppy.h"
-#include "firmware.h"
-
+#include "video.h"
 #include "gui/gui.h"
-#include "gui/window.h"
-#include "gui/draggablewindow2.h"
-#include "gui/textedit.h"
+#include "gui/diskselector.h"
 
 // Debug and misc. defines
 
 
 // Global variables
 
-uint8_t ram[0x10000], rom[0x10000];                            // RAM & ROM spaces
-uint8_t ram2[0x10000];                                                 // Auxillary RAM
-uint8_t diskRom[0x100];                                                        // Disk ROM space
-V65C02REGS mainCPU;                                                            // v65C02 execution context
+uint8_t ram[0x10000], rom[0x10000];                    // RAM & ROM spaces
+uint8_t ram2[0x10000];                                         // Auxillary RAM
+V65C02REGS mainCPU;                                                    // v65C02 execution context
 uint8_t appleType = APPLE_TYPE_IIE;
-FloppyDrive floppyDrive;
-
-// Local variables
-
-static uint8_t lastKeyPressed = 0;
-static bool keyDown = false;
-static bool openAppleDown = false;
-static bool closedAppleDown = false;
-static bool store80Mode = false;
-static bool vbl = false;
-static bool slotCXROM = false;
-static bool slotC3ROM = false;
-static bool ramrd = false;
-static bool ramwrt = false;
-static bool altzp = false;
-static bool ioudis = true;
+bool powerStateChangeRequested = false;
+uint64_t frameCycleStart;
+
+uint64_t frameTicks = 0;
+uint64_t frameTime[60];
+uint32_t frameTimePtr = 0;
+
+// Exported variables
+
+uint8_t lastKeyPressed = 0;
+bool keyDown = false;
+bool openAppleDown = false;
+bool closedAppleDown = false;
+bool store80Mode = false;
+bool vbl = false;
+bool intCXROM = false;
+bool slotC3ROM = false;
+bool intC8ROM = false;
+bool ramrd = false;
+bool ramwrt = false;
+bool altzp = false;
+bool ioudis = true;
 bool dhires = false;
+// Language card state (ROM read, no write)
+uint8_t lcState = 0x02;
+uint8_t blinkTimer = 0;
 
-//static FloppyDrive floppyDrive;
-
-enum { LC_BANK_1, LC_BANK_2 };
-
-static uint8_t visibleBank = LC_BANK_1;
-static bool readRAM = false;
-static bool writeRAM = false;
+static bool running = true;                                    // Machine running state flag...
+static uint64_t startTicks;
+static bool pauseMode = false;
+static bool fullscreenDebounce = false;
+static bool resetKeyDown = false;
+static int8_t hideMouseTimeout = 60;
 
-static bool running = true;                                            // Machine running state flag...
-static uint32_t startTicks;
+// Vars to handle the //e's 2-key rollover
+static SDL_Keycode keysHeld[2];
+static uint8_t keysHeldAppleCode[2];
+static uint8_t keyDownCount = 0;
+static uint8_t keyDelay = 0;
 
-static GUI * gui = NULL;
+// Local functions
 
-// Local functions (technically, they're global...)
-
-bool LoadImg(char * filename, uint8_t * ram, int size);
-uint8_t RdMem(uint16_t addr);
-void WrMem(uint16_t addr, uint8_t b);
 static void SaveApple2State(const char * filename);
 static bool LoadApple2State(const char * filename);
+static void ResetApple2State(void);
+static void AppleTimer(uint16_t);
 
 // Local timer callback functions
 
@@ -117,1206 +128,160 @@ static void BlinkTimer(void);
 #ifdef THREADED_65C02
 // Test of threaded execution of 6502
 static SDL_Thread * cpuThread = NULL;
-//static SDL_mutex * cpuMutex = NULL;
 static SDL_cond * cpuCond = NULL;
 static SDL_sem * mainSem = NULL;
 static bool cpuFinished = false;
-static bool cpuSleep = false;
-
 
 // NB: Apple //e Manual sez 6502 is running @ 1,022,727 Hz
+//     This is a lie. At the end of each 65 cycle line, there is an elongated
+//     cycle (the so-called 'long' cycle) that throws the calcs out of whack.
+//     So actually, it's supposed to be 1,020,484.32 Hz
 
 // Let's try a thread...
-/*
-Here's how it works: Execute 1 frame's worth, then sleep.
-Other stuff wakes it up
-*/
+//
+// Here's how it works: Execute 1 frame's worth, then sleep. Other stuff wakes
+// it up.
+//
+static uint32_t sampleCount;
+static uint64_t sampleClock, lastSampleClock;
 int CPUThreadFunc(void * data)
 {
        // Mutex must be locked for conditional to work...
        // Also, must be created in the thread that uses it...
        SDL_mutex * cpuMutex = SDL_CreateMutex();
 
-// decrement mainSem...
-//SDL_SemWait(mainSem);
 #ifdef CPU_THREAD_OVERFLOW_COMPENSATION
-       float overflow = 0.0;
+//     float overflow = 0.0;
 #endif
 
        do
        {
-               if (cpuSleep)
-                       SDL_CondWait(cpuCond, cpuMutex);
-
+uint64_t cpuFrameTickStart = SDL_GetPerformanceCounter();
+uint64_t oldClock = mainCPU.clock;
+sampleCount = 0;
+sampleClock = lastSampleClock = mainCPU.clock;
 // decrement mainSem...
 #ifdef THREAD_DEBUGGING
 WriteLog("CPU: SDL_SemWait(mainSem);\n");
 #endif
-SDL_SemWait(mainSem);
+               SDL_SemWait(mainSem);
 
-// There are exactly 800 slices of 21.333 cycles per frame, so it works out
-// evenly.
-#if 0
-               uint32_t cycles = 17066;
-#ifdef CPU_THREAD_OVERFLOW_COMPENSATION
-// ODD! It's closer *without* this overflow compensation. ??? WHY ???
-               overflow += 0.666666667;
-
-               if (overflow > 1.0)
-               {
-                       overflow -= 1.0;
-                       cycles++;
-               }
-#endif
+               // There are exactly 800 slices of 21.333 cycles per frame, so it works
+               // out evenly.
+               // [Actually, seems it's 786 slices of 21.666 cycles per frame]
 
+               // Set our frame cycle counter to the correct # of cycles at the start
+               // of this frame
+               frameCycleStart = mainCPU.clock - mainCPU.overflow;
 #ifdef THREAD_DEBUGGING
 WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
 #endif
-               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!
-
-               // Adjust the sound routine's last cycle toggled time base
-               // Also, since we're finished executing, .clock is now valid
-#ifdef THREAD_DEBUGGING
-WriteLog("CPU: AdjustLastToggleCycles(mainCPU.clock);\n");
-#endif
-               AdjustLastToggleCycles(mainCPU.clock);
-#else
-#ifdef THREAD_DEBUGGING
-WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
-#endif
-               for(int i=0; i<800; i++)
+               for(int i=0; i<262; i++)
                {
-                       uint32_t cycles = 21;
-                       overflow += 0.333333334;
-
-                       if (overflow > 1.0)
-                       {
-                               cycles++;
-                               overflow -= 1.0;
-                       }
-
-                       Execute65C02(&mainCPU, cycles);
-                       WriteSampleToBuffer();
-               }
-#endif
-
-//WriteLog("CPUThread: Supposedly end of frame...\n");
-
-#ifdef THREAD_DEBUGGING
-WriteLog("CPU: SDL_mutexP(cpuMutex);\n");
-#endif
-               SDL_mutexP(cpuMutex);
-#if 0
-               if (SDL_CondWait(cpuCond, cpuMutex) != 0)
-               {
-                       printf("SDL_CondWait != 0! (Error: '%s')\n", SDL_GetError());
-                       exit(-1);
-               }
-#else
-// increment mainSem...
-#ifdef THREAD_DEBUGGING
-WriteLog("CPU: SDL_SemPost(mainSem);\n");
-#endif
-SDL_SemPost(mainSem);
-//             SDL_CondSignal(mainCond);       // In case something is waiting on us...
-
-#ifdef THREAD_DEBUGGING
-WriteLog("CPU: SDL_CondWait(cpuCond, cpuMutex);\n");
-#endif
-               SDL_CondWait(cpuCond, cpuMutex);
-
-#endif
-#ifdef THREAD_DEBUGGING
-WriteLog("CPU: SDL_mutexV(cpuMutex);\n");
-#endif
-               SDL_mutexV(cpuMutex);
-       }
-       while (!cpuFinished);
-
-       SDL_DestroyMutex(cpuMutex);
-
-       return 0;
-}
-#endif
-
-
-// Test GUI function
-
-Element * TestWindow(void)
-{
-       Element * win = new DraggableWindow2(10, 10, 128, 128);
-//     ((DraggableWindow *)win)->AddElement(new TextEdit(4, 16, 92, 0, "u2prog.dsk", win));
-
-       return win;
-}
-
-
-Element * QuitEmulator(void)
-{
-       gui->Stop();
-       running = false;
-
-       return NULL;
-}
-
-
-/*
- Small Apple II memory map:
-
-$C010 - Clear bit 7 of keyboard data ($C000)
-$C030 - Toggle speaker diaphragm
-$C051 - Display text
-$C054 - Select page 1
-$C056 - Select lo-res
-$C058 - Set annuciator-0 output to 0
-$C05A - Set annuciator-0 output to 0
-$C05D - Set annuciator-0 output to 1
-$C05F - Set annuciator-0 output to 1
-$C0E0 - Disk control stepper ($C0E0-7)
-$C0E9 - Disk control motor (on)
-$C0EA - Disk enable (drive 1)
-$C0EC - Disk R/W
-$C0EE - Disk set read mode
-*/
-
-//
-// V65C02 read/write memory functions
-//
-
-uint8_t RdMem(uint16_t addr)
-{
-       uint8_t b;
-
-#if 0
-if (addr >= 0xC000 && addr <= 0xC0FF)
-       WriteLog("\n*** Read at I/O address %04X\n", addr);
-#endif
-#if 0
-if (addr >= 0xC080 && addr <= 0xC08F)
-       WriteLog("\n*** Read at I/O address %04X\n", addr);
-#endif
-
-       if ((addr & 0xFFF0) == 0xC000)                  // Read $C000-$C00F
-       {
-               return lastKeyPressed | (keyDown ? 0x80 : 0x00);
-       }
-//     else if ((addr & 0xFFF8) == 0xC010)             // Read $C010-$C01F
-       else if (addr == 0xC010)
-       {
-//This is bogus: keyDown is set to false, so return val NEVER is set...
-//Fixed...
-//Also, this is IIe/IIc only...!
-               uint8_t retVal = lastKeyPressed | (keyDown ? 0x80 : 0x00);
-               keyDown = false;
-               return retVal;
-       }
-       // These are //e locations
-       else if (addr == 0xC011)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("RDBANK2 (read)\n");
-#endif
-               return (visibleBank == LC_BANK_2 ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC012)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("RDLCRAM (read)\n");
-#endif
-               return (readRAM ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC013)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("RAMRD (read)\n");
-#endif
-               return (ramrd ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC014)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("RAMWRT (read)\n");
-#endif
-               return (ramwrt ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC015)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("SLOTCXROM (read)\n");
-#endif
-               return (slotCXROM ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC016)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("ALTZP (read)\n");
-#endif
-               return (altzp ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC017)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("SLOTC3ROM (read)\n");
-#endif
-               return (slotC3ROM ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC018)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("80STORE (read)\n");
-#endif
-               return (store80Mode ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC019)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("VBL (read)\n");
-#endif
-               return (vbl ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC01A)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("TEXT (read)\n");
-#endif
-               return (textMode ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC01B)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("MIXED (read)\n");
-#endif
-               return (mixedMode ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC01C)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("PAGE2 (read)\n");
-#endif
-               return (displayPage2 ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC01D)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("HIRES (read)\n");
-#endif
-               return (hiRes ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC01E)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("ALTCHARSET (read)\n");
-#endif
-               return (alternateCharset ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC01F)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("80COL (read)\n");
-#endif
-               return (col80Mode ? 0x80 : 0x00);
-       }
-       else if ((addr & 0xFFF0) == 0xC030)             // Read $C030-$C03F
-       {
-/*
-This is problematic, mainly because the v65C02 removes actual cycles run after each call.
-Therefore, we don't really have a reliable method of sending a timestamp to the sound routine.
-How to fix?
-
-What we need to send is a delta T value but related to the IRQ buffer routine. E.g., if the buffer
-hasn't had any changes in it then we just fill it with the last sample value and are done. Then
-we need to adjust our delta T accordingly. What we could do is keep a running total of time since the
-last change and adjust it accordingly, i.e., whenever a sound IRQ happens.
-How to keep track?
-
-Have deltaT somewhere. Then, whenever there's a toggle, backfill buffer with last spkr state and reset
-deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer size from deltaT. (?)
-
-
-
-*/
-               ToggleSpeaker(GetCurrentV65C02Clock());
-//should it return something else here???
-               return 0x00;
-       }
-       else if (addr == 0xC050)                                // Read $C050
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("TEXT off (read)\n");
-#endif
-               textMode = false;
-       }
-       else if (addr == 0xC051)                                // Read $C051
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("TEXT on (read)\n");
-#endif
-               textMode = true;
-       }
-       else if (addr == 0xC052)                                // Read $C052
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("MIXED off (read)\n");
-#endif
-               mixedMode = false;
-       }
-       else if (addr == 0xC053)                                // Read $C053
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("MIXED on (read)\n");
-#endif
-               mixedMode = true;
-       }
-       else if (addr == 0xC054)                                // Read $C054
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("PAGE2 off (read)\n");
-#endif
-               displayPage2 = false;
-       }
-       else if (addr == 0xC055)                                // Read $C055
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("PAGE2 on (read)\n");
-#endif
-               displayPage2 = true;
-       }
-       else if (addr == 0xC056)                                // Read $C056
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("HIRES off (read)\n");
-#endif
-               hiRes = false;
-       }
-       else if (addr == 0xC057)                                // Read $C057
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("HIRES on (read)\n");
-#endif
-               hiRes = true;
-       }
-       else if (addr == 0xC05E)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("DHIRES on (read)\n");
-#endif
-               if (ioudis)
-                       dhires = true;
-       }
-       else if (addr == 0xC05F)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("DHIRES off (read)\n");
-#endif
-               if (ioudis)
-                       dhires = false;
-       }
-       else if (addr == 0xC061)                                // Read $C061
-       {
-               // Open Apple key (or push button 0)
-               return (openAppleDown ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC062)                                // Read $C062
-       {
-               // Open Apple key (or push button 0)
-               return (closedAppleDown ? 0x80 : 0x00);
-       }
-       // The way the paddles work is that a strobe is written (or read) to $C070,
-       // then software counts down the time that it takes for the paddle outputs
-       // to have bit 7 return to 0. If there are no paddles connected, bit 7
-       // stays at 1.
-       else if (addr == 0xC064)        // Paddles 0-3
-       {
-               return 0xFF;
-       }
-       else if (addr == 0xC065)
-       {
-               return 0xFF;
-       }
-       else if (addr == 0xC066)
-       {
-               return 0xFF;
-       }
-       else if (addr == 0xC067)
-       {
-               return 0xFF;
-       }
-       else if (addr == 0xC07E)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("IOUDIS (read)\n");
-#endif
-               return (ioudis ? 0x80 : 0x00);
-       }
-       else if (addr == 0xC07F)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("DHIRES (read)\n");
-#endif
-               return (dhires ? 0x80 : 0x00);
-       }
-
-//Note that this is a kludge: The $D000-$DFFF 4K space is shared (since $C000-$CFFF is
-//memory mapped) between TWO banks, and that that $E000-$FFFF RAM space is a single bank.
-//[SHOULD BE FIXED NOW]
-//OK! This switch selects bank 2 of the 4K bank at $D000-$DFFF. One access makes it
-//visible, two makes it R/W.
-
-/*
-301  LDA $E000
-304  PHA
-305  LDA $C081
-308  PLA
-309  PHA
-30A  CMP $E000
-30D  BNE $332
-30F  LDA $C083
-312  LDA $C083
-315  LDA #$A5
-317  STA $D000
-31A  CMP $D000
-31D  BNE $332
-31F  LSR A
-320  STA $D000
-323  CMP $D000
-326  BNE $332
-328  LDA $C081
-32B  LDA $C081
-32E  LDA #$01
-330  BNE $334
-332  LDA #$00
-334  STA $300
-337  PLA
-338  CMP $E000
-33B  BEQ $340
-33D  LDA $C080
-340  RTS
-
-A = PEEK($C082)
-*/
-
-       else if ((addr & 0xFFFB) == 0xC080)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
-#endif
-//$C080 49280              OECG  R   Read RAM bank 2; no write
-               visibleBank = LC_BANK_2;
-               readRAM = true;
-               writeRAM = false;
-       }
-       else if ((addr & 0xFFFB) == 0xC081)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
-#endif
-//$C081 49281 ROMIN        OECG  RR  Read ROM; write RAM bank 2
-               visibleBank = LC_BANK_2;
-               readRAM = false;
-               writeRAM = true;
-       }
-       else if ((addr & 0xFFFB) == 0xC082)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
-#endif
-//$C082 49282              OECG  R   Read ROM; no write
-               visibleBank = LC_BANK_2;
-               readRAM = false;
-               writeRAM = false;
-       }
-       else if ((addr & 0xFFFB) == 0xC083)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
-#endif
-//$C083 49283 LCBANK2      OECG  RR  Read/write RAM bank 2
-               visibleBank = LC_BANK_2;
-               readRAM = true;
-               writeRAM = true;
-       }
-       else if ((addr & 0xFFFB) == 0xC088)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $%04X 49288 OECG R Read RAM bank 1; no write\n", addr);
-#endif
-//$C088 49288              OECG  R   Read RAM bank 1; no write
-               visibleBank = LC_BANK_1;
-               readRAM = true;
-               writeRAM = false;
-//Hm. Some stuff seems to want this.
-//nope, was looking at $C0E8... return 0xFF;
-       }
-       else if ((addr & 0xFFFB) == 0xC089)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
-#endif
-//$C089 49289              OECG  RR  Read ROM; write RAM bank 1
-               visibleBank = LC_BANK_1;
-               readRAM = false;
-               writeRAM = true;
-       }
-       else if ((addr & 0xFFFB) == 0xC08A)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
-#endif
-//$C08A 49290              OECG  R   Read ROM; no write
-               visibleBank = LC_BANK_1;
-               readRAM = false;
-               writeRAM = false;
-       }
-       else if ((addr & 0xFFFB) == 0xC08B)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
-#endif
-//$C08B 49291              OECG  RR  Read/write RAM bank 1
-               visibleBank = LC_BANK_1;
-               readRAM = true;
-               writeRAM = true;
-       }
-       else if ((addr & 0xFFF8) == 0xC0E0)
-       {
-               floppyDrive.ControlStepper(addr & 0x07);
-       }
-       else if ((addr & 0xFFFE) == 0xC0E8)
-       {
-               floppyDrive.ControlMotor(addr & 0x01);
-       }
-       else if ((addr & 0xFFFE) == 0xC0EA)
-       {
-               floppyDrive.DriveEnable(addr & 0x01);
-       }
-       else if (addr == 0xC0EC)
-       {
-               return floppyDrive.ReadWrite();
-       }
-       else if (addr == 0xC0ED)
-       {
-               return floppyDrive.GetLatchValue();
-       }
-       else if (addr == 0xC0EE)
-       {
-               floppyDrive.SetReadMode();
-       }
-       else if (addr == 0xC0EF)
-       {
-               floppyDrive.SetWriteMode();
-       }
+                       // If the CTRL+Reset key combo is being held, make sure the RESET
+                       // line stays asserted:
+                       if (resetKeyDown)
+                               mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
 
-//#define LC_DEBUGGING
-#ifdef LC_DEBUGGING
-bool showpath = false;
-if (addr >= 0xD000 && addr <= 0xD00F)
-       showpath = true;
-#endif
-//This sux...
-       if (addr >= 0xC100 && addr <= 0xC7FF)   // The $C000-$CFFF block is *never* RAM
-       {
-// Looks like the ][e ref manual got this one wrong: slotCXROM set should mean
-// use internal ROM, NOT slot ROM. :-/
-// (fixed now, by setting the switch correctly in the write mem section :-P)
-               if (!slotCXROM)
-//             if (slotCXROM)
-                       b = rom[addr];
-               else
-               {
-                       if (addr >= 0xC100 && addr <= 0xC1FF)
-                               b = parallelROM[addr & 0xFF];
-                       else if (addr >= 0xC600 && addr <= 0xC6FF)
-                               b = diskROM[addr & 0xFF];
-                       else if (addr >= 0xC300 && addr <= 0xC3FF && !slotC3ROM)
-                               b = rom[addr];
-                       else
-                               b = 0xFF;
-//                             b = rom[addr];
-               }
-#ifdef LC_DEBUGGING
-if (showpath)
-       WriteLog("b is from $C100-$CFFF block...\n");
-#endif
-       }
-       else if (addr >= 0xC800 && addr <= 0xCFFF)      // 2K peripheral or OS ROM
-       {
-               b = rom[addr];
-       }
-       else if (addr >= 0xD000)
-       {
-               if (readRAM)
-               {
-                       if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
-#ifdef LC_DEBUGGING
-                       {
-#endif
-//                             b = ram[addr - 0x1000];
-                               b = (altzp ? ram2[addr - 0x1000] : ram[addr - 0x1000]);
-#ifdef LC_DEBUGGING
-if (showpath)
-       WriteLog("b is from LC bank #1 (ram[addr - 0x1000])...\n");
-                       }
-#endif
-                       else
-#ifdef LC_DEBUGGING
-                       {
-#endif
-//                             b = ram[addr];
-                               b = (altzp ? ram2[addr] : ram[addr]);
-#ifdef LC_DEBUGGING
-if (showpath)
-       WriteLog("b is from LC bank #2 (ram[addr])...\n");
-                       }
-#endif
-               }
-               else
-#ifdef LC_DEBUGGING
-               {
-#endif
-                       b = rom[addr];
-#ifdef LC_DEBUGGING
-if (showpath)
-       WriteLog("b is from LC ROM (rom[addr])...\n");
-               }
-#endif
-       }
-       else
-       {
-               // Check for 80STORE mode (STORE80 takes precedence over RAMRD/WRT)...
-               if ((((addr >= 0x0400) && (addr <= 0x07FF)) || ((addr >= 0x2000) && (addr <= 0x3FFF))) && store80Mode)
-               {
-                       if (displayPage2)
-                               b = ram2[addr];
-                       else
-                               b = ram[addr];
+                       Execute65C02(&mainCPU, 65);
 
-                       return b;
+                       // According to "Understanding The Apple IIe", VBL asserted after
+                       // the last byte of the screen is read and let go on the first read
+                       // of the first byte of the screen. We now know that the screen
+                       // starts on line #6 and ends on line #197 (of the vertical
+                       // counter--actual VBLANK proper happens on lines 230 thru 233).
+                       vbl = ((i >= 6) && (i <= 197) ? true : false);
                }
 
-               // Finally, check for auxillary/altzp write switches
-               if (addr < 0x0200)
-                       b = (altzp ? ram2[addr] : ram[addr]);
-               else
-                       b = (ramrd ? ram2[addr] : ram[addr]);
-#ifdef LC_DEBUGGING
-if (showpath)
-       WriteLog("b is from ram[addr]...\n");
-#endif
-       }
-
-#ifdef LC_DEBUGGING
-if (addr >= 0xD000 && addr <= 0xD00F)
-{
-       WriteLog("*** Read from $%04X: $%02X (readRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (readRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
-}
-#endif
-
-       return b;
-}
-
-/*
-A-9 (Mockingboard)
-APPENDIX F Assembly Language Program Listings
-
-       1       *PRIMARY ROUTINES
-       2       *FOR SLOT 4
-       3       *
-       4                       ORG     $9000
-       5       *                               ;ADDRESSES FOR FIRST 6522
-       6       ORB             EQU     $C400           ;PORT B
-       7       ORA             EQU     $C401           ;PORT A
-       8       DDRB            EQU     $C402           ;DATA DIRECTION REGISTER (A)
-       9       DDRA            EQU     $C403           ;DATA DIRECTION REGISTER (B)
-       10      *                                       ;ADDRESSES FOR SECOND 6522
-       11      ORB2            EQU     $C480           ;PORT B
-       12      ORA2            EQU     $C481           ;PORT A
-       13      DDRB2   EQU     $C482           ;DATA DIRECTION REGISTER (B)
-       14      DDRA2   EQU     $C483           ;DATA DIRECTION REGISTER (A)
-*/
-void WrMem(uint16_t addr, uint8_t b)
-{
-//temp...
-//extern V6809REGS regs;
-//if (addr >= 0xC800 && addr <= 0xCBFE)
-//if (addr == 0xC80F || addr == 0xC80D)
-//     WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
-
-#if 0
-if (addr >= 0xC000 && addr <= 0xC0FF)
-       WriteLog("\n*** Write at I/O address %04X\n", addr);
-#endif
+//WriteLog("*** Frame ran for %d cycles (%.3lf µs, %d samples).\n", mainCPU.clock - oldClock, ((double)(SDL_GetPerformanceCounter() - cpuFrameTickStart) * 1000000.0) / (double)SDL_GetPerformanceFrequency(), sampleCount);
+//     frameTicks = ((SDL_GetPerformanceCounter() - startTicks) * 1000) / SDL_GetPerformanceFrequency();
 /*
-Check the BIKO version on Asimov to see if it's been cracked or not...
-
-7F3D: 29 07          AND   #$07       [PC=7F3F, SP=01EA, CC=---B-I--, A=01, X=4B, Y=00]
-7F3F: C9 06          CMP   #$06       [PC=7F41, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
-7F41: 90 03          BCC   $7F46      [PC=7F46, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
-[7F43: 4C 83 7E      JMP   $7E83] <- Skipped over... (Prints "THANK YOU VERY MUCH!")
-7F46: AA             TAX              [PC=7F47, SP=01EA, CC=---B-I--, A=01, X=01, Y=00]
-
-; INX here *ensures* 1 - 6!!! BUG!!!
-; Or is it? Could this be part of a braindead copy protection scheme? It's
-; awfully close to NOP ($EA)...
-; Nothing else touches it once it's been written... Hmm...
-
-7F47: E8             INX              [PC=7F48, SP=01EA, CC=---B-I--, A=01, X=02, Y=00]
-7F48: F8             SED              [PC=7F49, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
-7F49: 18             CLC              [PC=7F4A, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
-7F4A: BD 15 4E       LDA   $4E15,X    [PC=7F4D, SP=01EA, CC=---BDI--, A=15, X=02, Y=00]
-
-; 4E13: 03 00
-; 4E15: 25 25 15 15 10 20
-; 4E1B: 03 41 99 99 01 00 12
-; 4E22: 99 70
-
-7F4D: 65 FC          ADC   $FC        [PC=7F4F, SP=01EA, CC=---BDI--, A=16, X=02, Y=00]
-7F4F: 65 FC          ADC   $FC        [PC=7F51, SP=01EA, CC=---BDI--, A=17, X=02, Y=00]
-7F51: 65 FC          ADC   $FC        [PC=7F53, SP=01EA, CC=---BDI--, A=18, X=02, Y=00]
-7F53: 65 FC          ADC   $FC        [PC=7F55, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
-
-; NO checking is done on the raised stat! Aarrrgggghhhhh!
-
-7F55: 9D 15 4E       STA   $4E15,X    [PC=7F58, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
-7F58: D8             CLD              [PC=7F59, SP=01EA, CC=---B-I--, A=19, X=02, Y=00]
-
-; Print "ALAKAZAM!" and so on...
-
-7F59: 20 2C 40       JSR   $402C      [PC=402C, SP=01E8, CC=---B-I--, A=19, X=02, Y=00]
-*/
-#if 0
-if (addr == 0x7F47)
-       WriteLog("\n*** Byte %02X written at address %04X\n", b, addr);
-#endif
-/*
-I think this is IIc/IIe only...
-
-CLR80STORE=$C000 ;80STORE Off- disable 80-column memory mapping (Write)
-SET80STORE=$C001 ;80STORE On- enable 80-column memory mapping (WR-only)
-
-CLRAUXRD = $C002 ;read from main 48K (WR-only)
-SETAUXRD = $C003 ;read from aux/alt 48K (WR-only)
-
-CLRAUXWR = $C004 ;write to main 48K (WR-only)
-SETAUXWR = $C005 ;write to aux/alt 48K (WR-only)
-
-CLRCXROM = $C006 ;use ROM on cards (WR-only)
-SETCXROM = $C007 ;use internal ROM (WR-only)
-
-CLRAUXZP = $C008 ;use main zero page, stack, & LC (WR-only)
-SETAUXZP = $C009 ;use alt zero page, stack, & LC (WR-only)
-
-CLRC3ROM = $C00A ;use internal Slot 3 ROM (WR-only)
-SETC3ROM = $C00B ;use external Slot 3 ROM (WR-only)
-
-CLR80VID = $C00C ;disable 80-column display mode (WR-only)
-SET80VID = $C00D ;enable 80-column display mode (WR-only)
-
-CLRALTCH = $C00E ;use main char set- norm LC, Flash UC (WR-only)
-SETALTCH = $C00F ;use alt char set- norm inverse, LC; no Flash (WR-only)
-*/
-       if (addr == 0xC000)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("80STORE off (write)\n");
-#endif
-               store80Mode = false;
-       }
-       else if (addr == 0xC001)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("80STORE on (write)\n");
-#endif
-               store80Mode = true;
-       }
-       else if (addr == 0xC002)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("RAMRD off (write)\n");
-#endif
-               ramrd = false;
-       }
-       else if (addr == 0xC003)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("RAMRD on (write)\n");
-#endif
-               ramrd = true;
-       }
-       else if (addr == 0xC004)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("RAMWRT off (write)\n");
-#endif
-               ramwrt = false;
-       }
-       else if (addr == 0xC005)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("RAMWRT on (write)\n");
-#endif
-               ramwrt = true;
-       }
-       else if (addr == 0xC006)
-       {
-               // This is the only soft switch that breaks the usual convention.
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("SLOTCXROM on (write)\n");
-#endif
-               slotCXROM = true;
-       }
-       else if (addr == 0xC007)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("SLOTCXROM off (write)\n");
-#endif
-               slotCXROM = false;
-       }
-       else if (addr == 0xC008)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("ALTZP off (write)\n");
-#endif
-               altzp = false;
-       }
-       else if (addr == 0xC009)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("ALTZP on (write)\n");
-#endif
-               altzp = true;
-       }
-       else if (addr == 0xC00A)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("SLOTC3ROM off (write)\n");
-#endif
-               slotC3ROM = false;
-       }
-       else if (addr == 0xC00B)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("SLOTC3ROM on (write)\n");
-#endif
-               slotC3ROM = true;
-       }
-       else if (addr == 0xC00C)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("80COL off (write)\n");
-#endif
-               col80Mode = false;
-       }
-       else if (addr == 0xC00D)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("80COL on (write)\n");
-#endif
-               col80Mode = true;
-       }
-       else if (addr == 0xC00E)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("ALTCHARSET off (write)\n");
-#endif
-               alternateCharset = false;
-       }
-       else if (addr == 0xC00F)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("ALTCHARSET on (write)\n");
-#endif
-               alternateCharset = true;
-       }
-       else if ((addr & 0xFFF0) == 0xC010)             // Keyboard strobe
-       {
-//Actually, according to the A2 ref, this should do nothing since a write
-//is immediately preceded by a read leaving it in the same state it was...
-//But leaving this out seems to fuck up the key handling of some games...
-               keyDown = false;
-       }
-       else if (addr == 0xC050)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("TEXT off (write)\n");
-#endif
-               textMode = false;
-       }
-       else if (addr == 0xC051)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("TEXT on (write)\n");
-#endif
-               textMode = true;
-       }
-       else if (addr == 0xC052)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("MIXED off (write)\n");
-#endif
-               mixedMode = false;
-       }
-       else if (addr == 0xC053)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("MIXED on (write)\n");
-#endif
-               mixedMode = true;
-       }
-       else if (addr == 0xC054)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("PAGE2 off (write)\n");
-#endif
-               displayPage2 = false;
-       }
-       else if (addr == 0xC055)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("PAGE2 on (write)\n");
-#endif
-               displayPage2 = true;
-       }
-       else if (addr == 0xC056)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("HIRES off (write)\n");
-#endif
-               hiRes = false;
-       }
-       else if (addr == 0xC057)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("HIRES on (write)\n");
-#endif
-               hiRes = true;
-       }
-       else if (addr == 0xC05E)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("DHIRES on (write)\n");
-#endif
-               if (ioudis)
-                       dhires = true;
-
-//static int goDumpDis = 0;
-//goDumpDis++;
-//if (goDumpDis == 2)
-//     dumpDis = true;
-       }
-       else if (addr == 0xC05F)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("DHIRES off (write)\n");
-#endif
-               if (ioudis)
-                       dhires = false;
-       }
-       else if (addr == 0xC07E)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("IOUDIS on (write)\n");
-#endif
-               ioudis = true;
-       }
-       else if (addr == 0xC07F)
-       {
-#ifdef SOFT_SWITCH_DEBUGGING
-WriteLog("IOUDIS off (write)\n");
-#endif
-               ioudis = false;
-       }
-       else if ((addr & 0xFFFB) == 0xC080)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
-#endif
-//$C080 49280              OECG  R   Read RAM bank 2; no write
-               visibleBank = LC_BANK_2;
-               readRAM = true;
-               writeRAM = false;
-       }
-       else if ((addr & 0xFFFB) == 0xC081)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
-#endif
-//$C081 49281 ROMIN        OECG  RR  Read ROM; write RAM bank 2
-               visibleBank = LC_BANK_2;
-               readRAM = false;
-               writeRAM = true;
-       }
-       else if ((addr & 0xFFFB) == 0xC082)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
-#endif
-//$C082 49282              OECG  R   Read ROM; no write
-               visibleBank = LC_BANK_2;
-               readRAM = false;
-               writeRAM = false;
-       }
-       else if ((addr & 0xFFFB) == 0xC083)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
-#endif
-//$C083 49283 LCBANK2      OECG  RR  Read/write RAM bank 2
-               visibleBank = LC_BANK_2;
-               readRAM = true;
-               writeRAM = true;
-       }
-       else if ((addr & 0xFFFB) == 0xC088)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C088 49288 OECG R Read RAM bank 1; no write\n");
-#endif
-//$C088 49288              OECG  R   Read RAM bank 1; no write
-               visibleBank = LC_BANK_1;
-               readRAM = true;
-               writeRAM = false;
-       }
-       else if ((addr & 0xFFFB) == 0xC089)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
-#endif
-//$C089 49289              OECG  RR  Read ROM; write RAM bank 1
-               visibleBank = LC_BANK_1;
-               readRAM = false;
-               writeRAM = true;
-       }
-       else if ((addr & 0xFFFB) == 0xC08A)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
-#endif
-//$C08A 49290              OECG  R   Read ROM; no write
-               visibleBank = LC_BANK_1;
-               readRAM = false;
-               writeRAM = false;
-       }
-       else if ((addr & 0xFFFB) == 0xC08B)
-       {
-#ifdef DEBUG_LC
-WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
-#endif
-//$C08B 49291              OECG  RR  Read/write RAM bank 1
-               visibleBank = LC_BANK_1;
-               readRAM = true;
-               writeRAM = true;
-       }
-//This is determined by which slot it is in--this assumes slot 6. !!! FIX !!!
-       else if ((addr & 0xFFF8) == 0xC0E0)
-       {
-               floppyDrive.ControlStepper(addr & 0x07);
-       }
-       else if ((addr & 0xFFFE) == 0xC0E8)
-       {
-               floppyDrive.ControlMotor(addr & 0x01);
-       }
-       else if ((addr & 0xFFFE) == 0xC0EA)
-       {
-               floppyDrive.DriveEnable(addr & 0x01);
-       }
-       else if (addr == 0xC0EC)
-       {
-//change this to Write()? (and the other to Read()?) Dunno. Seems to work OK, but still...
-//or DoIO
-               floppyDrive.ReadWrite();
-       }
-       else if (addr == 0xC0ED)
-       {
-               floppyDrive.SetLatchValue(b);
-       }
-       else if (addr == 0xC0EE)
-       {
-               floppyDrive.SetReadMode();
-       }
-       else if (addr == 0xC0EF)
-       {
-               floppyDrive.SetWriteMode();
-       }
-//Still need to add missing I/O switches here...
-
-//DEEE: BD 10 BF       LDA   $BF10,X    [PC=DEF1, SP=01F4, CC=--.B-IZ-, A=00, X=0C, Y=07]
-#if 0
-if (addr >= 0xD000 && addr <= 0xD00F)
-{
-       WriteLog("*** Write to $%04X: $%02X (writeRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (writeRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
-}
-#endif
-       if (addr >= 0xC000 && addr <= 0xCFFF)
-               return; // Protect LC bank #1 from being written to!
-
-       if (addr >= 0xD000)
-       {
-               if (writeRAM)
-               {
-#if 0
-                       if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
-                               ram[addr - 0x1000] = b;
-                       else
-                               ram[addr] = b;
-#else
-                       if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
-                       {
-                               if (altzp)
-                                       ram2[addr - 0x1000] = b;
-                               else
-                                       ram[addr - 0x1000] = b;
-                       }
-                       else
-                       {
-                               if (altzp)
-                                       ram2[addr] = b;
-                               else
-                                       ram[addr] = b;
-                       }
-#endif
-               }
+Other timings from UTA2E:
+Power-up reset                         32.6 msec / 512 horizontal scans
+Flash cycle                                    1.87 Hz / Vertical freq./32
+Delay before auto repeat       534-801 msec / 32-48 vertical scans
+Auto repeat frequency          15 Hz / Vertical freq./4
+Vertical frequency                     59.94 Hz (actually, 59.92 Hz [59.92274339401])
+Horizontal frequency           15,734 Hz (actually, 15700 Hz)
+1 NTSC frame = 17030 cycles (N.B.: this works out to 1021800 cycles per sec.)
+NTSC clock frequency ("composite" freq.) is 1.02048432 MHz, which is 14.31818 x (65 / (65 x 14 + 2)) MHz.
+1 line = 65 cycles
+70 blank lines for top margin, 192 lines for screen, (35 & 35?)
+VA-C,V0-5 is upcounter starting at 011111010 ($FA) to 111111111 ($1FF)
+Horizontal counter is upcounter resets to 0000000, then jumps to 1000000 &
+counts up to 1111111 (bit 7 is Horizontal Preset Enable, which resets the counter when it goes low, the rest are H0-5)
+
+pg. 3-24 says one cycle before VBL the counters will be at
+010111111/1111111 (that doesn't look right to me...)
+
+Video address bits:
+
+A0 <- H0
+A1 <- H1
+A2 <- H2
+A3 <- SUM-A3
+A4 <- SUM-A4
+A5 <- SUM-A5
+A6 <- SUM-A6
+A7 <- V0
+A8 <- V1
+A9 <- V2
+
+SUMS are calculated like so:
+
+  1        1       0       1
+          H5      H4      H3
+  V4      V3      V4      V3
+------------------------------
+SUM-A6  SUM-A5  SUM-A4  SUM-A3
+
+In TEXT mode, A10 == (80STORE' * PAGE2)', A11 == 80STORE' * PAGE2
+A12-A15 == 0
+
+In HIRES mode, A13 == (PAGE2 * 80STORE')', A14 == PAGE2 * 80STORE'
+A10 == VA, A11 == VB, A12 == VC, A15 == 0
+
+N.B.: VA-C are lower bits than V5-0
+
+HC, from 00, 0 to 23 is the HBL interval, with horizontal retrace occuring between cycles 8 and 11.
+VC, from line 0-5 and 198-261 is the VBL interval, with vertical retrace occuring between lines 230-233
 
-               return;
-       }
+*/
 
-       // Check for 80STORE mode (STORE80 takes precedence over RAMRD/WRT)...
-       if ((((addr >= 0x0400) && (addr <= 0x07FF)) || ((addr >= 0x2000) && (addr <= 0x3FFF))) && store80Mode)
-       {
-               if (displayPage2)
-                       ram2[addr] = b;
-               else
-                       ram[addr] = b;
+#ifdef THREAD_DEBUGGING
+WriteLog("CPU: SDL_mutexP(cpuMutex);\n");
+#endif
+               SDL_mutexP(cpuMutex);
+               // increment mainSem...
+#ifdef THREAD_DEBUGGING
+WriteLog("CPU: SDL_SemPost(mainSem);\n");
+#endif
+               SDL_SemPost(mainSem);
+#ifdef THREAD_DEBUGGING
+WriteLog("CPU: SDL_CondWait(cpuCond, cpuMutex);\n");
+#endif
+               SDL_CondWait(cpuCond, cpuMutex);
 
-               return;
+#ifdef THREAD_DEBUGGING
+WriteLog("CPU: SDL_mutexV(cpuMutex);\n");
+#endif
+               SDL_mutexV(cpuMutex);
        }
+       while (!cpuFinished);
 
-       // Finally, check for auxillary/altzp write switches
-#if 0
-       if (ramwrt)
-               ram2[addr] = b;
-       else
-       {
-               if (altzp)
-                       ram2[addr] = b;
-               else
-                       ram[addr] = b;
-       }
-#else
-       if (addr < 0x0200)
-//     if (addr < 0x0200 || addr >= 0xD000)
-       {
-               if (altzp)
-                       ram2[addr] = b;
-               else
-                       ram[addr] = b;
-       }
-       else
-       {
-               if (ramwrt)
-                       ram2[addr] = b;
-               else
-                       ram[addr] = b;
-       }
+       SDL_DestroyMutex(cpuMutex);
+
+       return 0;
+}
 #endif
+
+
+//
+// Request a change in the power state of the emulated Apple
+//
+void SetPowerState(void)
+{
+       powerStateChangeRequested = true;
 }
 
 
@@ -1337,14 +302,180 @@ bool LoadImg(char * filename, uint8_t * ram, int size)
 }
 
 
+const uint8_t stateHeader[19] = "APPLE2SAVESTATE1.2";
 static void SaveApple2State(const char * filename)
 {
+       WriteLog("Main: Saving Apple2 state...\n");
+       FILE * file = fopen(filename, "wb");
+
+       if (!file)
+       {
+               WriteLog("Could not open file \"%s\" for writing!\n", filename);
+               return;
+       }
+
+       // Write out header
+       fwrite(stateHeader, 1, 18, file);
+
+       // Write out CPU state
+       fwrite(&mainCPU, 1, sizeof(mainCPU), file);
+
+       // Write out main memory
+       fwrite(ram, 1, 0x10000, file);
+       fwrite(ram2, 1, 0x10000, file);
+
+       // Write out state variables
+       fputc((uint8_t)keyDown, file);
+       fputc((uint8_t)openAppleDown, file);
+       fputc((uint8_t)closedAppleDown, file);
+       fputc((uint8_t)store80Mode, file);
+       fputc((uint8_t)vbl, file);
+       fputc((uint8_t)intCXROM, file);
+       fputc((uint8_t)slotC3ROM, file);
+       fputc((uint8_t)intC8ROM, file);
+       fputc((uint8_t)ramrd, file);
+       fputc((uint8_t)ramwrt, file);
+       fputc((uint8_t)altzp, file);
+       fputc((uint8_t)ioudis, file);
+       fputc((uint8_t)dhires, file);
+       fputc((uint8_t)flash, file);
+       fputc((uint8_t)textMode, file);
+       fputc((uint8_t)mixedMode, file);
+       fputc((uint8_t)displayPage2, file);
+       fputc((uint8_t)hiRes, file);
+       fputc((uint8_t)alternateCharset, file);
+       fputc((uint8_t)col80Mode, file);
+       fputc(lcState, file);
+
+       // Write out floppy state
+       floppyDrive[0].SaveState(file);
+
+       // Write out Mockingboard state
+       MBSaveState(file);
+       fclose(file);
 }
 
 
 static bool LoadApple2State(const char * filename)
 {
-       return false;
+       WriteLog("Main: Loading Apple2 state...\n");
+       FILE * file = fopen(filename, "rb");
+
+       if (!file)
+       {
+               WriteLog("Could not open file \"%s\" for reading!\n", filename);
+               return false;
+       }
+
+       uint8_t buffer[18];
+       fread(buffer, 1, 18, file);
+
+       // Sanity check...
+       if (memcmp(buffer, stateHeader, 18) != 0)
+       {
+               fclose(file);
+               WriteLog("File \"%s\" is not a valid Apple2 save state file!\n", filename);
+               return false;
+       }
+
+       // Read CPU state
+       fread(&mainCPU, 1, sizeof(mainCPU), file);
+
+       // Read main memory
+       fread(ram, 1, 0x10000, file);
+       fread(ram2, 1, 0x10000, file);
+
+       // Read in state variables
+       keyDown = (bool)fgetc(file);
+       openAppleDown = (bool)fgetc(file);
+       closedAppleDown = (bool)fgetc(file);
+       store80Mode = (bool)fgetc(file);
+       vbl = (bool)fgetc(file);
+       intCXROM = (bool)fgetc(file);
+       slotC3ROM = (bool)fgetc(file);
+       intC8ROM = (bool)fgetc(file);
+       ramrd = (bool)fgetc(file);
+       ramwrt = (bool)fgetc(file);
+       altzp = (bool)fgetc(file);
+       ioudis = (bool)fgetc(file);
+       dhires = (bool)fgetc(file);
+       flash = (bool)fgetc(file);
+       textMode = (bool)fgetc(file);
+       mixedMode = (bool)fgetc(file);
+       displayPage2 = (bool)fgetc(file);
+       hiRes = (bool)fgetc(file);
+       alternateCharset = (bool)fgetc(file);
+       col80Mode = (bool)fgetc(file);
+       lcState = fgetc(file);
+
+       // Read in floppy state
+       floppyDrive[0].LoadState(file);
+
+       // Read in Mockingboard state
+       MBLoadState(file);
+       fclose(file);
+
+       // Make sure things are in a sane state before execution :-P
+       mainCPU.RdMem = AppleReadMem;
+       mainCPU.WrMem = AppleWriteMem;
+       mainCPU.Timer = AppleTimer;
+       ResetMMUPointers();
+
+       return true;
+}
+
+
+static void ResetApple2State(void)
+{
+       keyDown = false;
+       openAppleDown = false;
+       closedAppleDown = false;
+       store80Mode = false;
+       vbl = false;
+       intCXROM = false;
+       slotC3ROM = false;
+       intC8ROM = false;
+       ramrd = false;
+       ramwrt = false;
+       altzp = false;
+       ioudis = true;
+       dhires = false;
+       lcState = 0x02;
+       ResetMMUPointers();
+       MBReset();
+
+       // Without this, you can wedge the system :-/
+       memset(ram, 0, 0x10000);
+       memset(ram2, 0, 0x10000);
+       mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
+}
+
+
+static double cyclesForSample = 0;
+static void AppleTimer(uint16_t cycles)
+{
+       // Handle PHI2 clocked stuff here...
+       MBRun(cycles);
+       floppyDrive[0].RunSequencer(cycles);
+
+#if 1
+       // Handle sound
+       // 21.26009 cycles per sample @ 48000 (running @ 1,020,484.32 Hz)
+       // 16.688154500083 ms = 1 frame
+       cyclesForSample += (double)cycles;
+
+       if (cyclesForSample >= 21.26009)
+       {
+#if 0
+sampleClock = mainCPU.clock;
+WriteLog("    cyclesForSample = %lf (%d samples, cycles=%d)\n", cyclesForSample, sampleClock - lastSampleClock, cycles);
+sampleCount++;
+lastSampleClock = sampleClock;
+#endif
+               WriteSampleToBuffer();
+               cyclesForSample -= 21.26009;
+       }
+#endif
 }
 
 
@@ -1360,55 +491,90 @@ int main(int /*argc*/, char * /*argv*/[])
 {
        InitLog("./apple2.log");
        LoadSettings();
-       srand(time(NULL));                                                                      // Initialize RNG
+       srand(time(NULL));                      // Initialize RNG
+
+#if 0
+// Make some timing/address tables
+
+       for(uint32_t line=0; line<262; line++)
+       {
+               WriteLog("LINE %03i: ", line);
+
+               for(uint32_t col=0; col<65; col++)
+               {
+                       // Convert these to H/V counters
+                       uint32_t hcount = col - 1;
+
+                       // HC sees zero twice:
+                       if (hcount == 0xFFFFFFFF)
+                               hcount = 0;
+
+                       uint32_t vcount = line + 0xFA;
+
+                       // Now do the address calculations
+                       uint32_t sum = 0xD + ((hcount & 0x38) >> 3)
+                               + (((vcount & 0xC0) >> 6) | ((vcount & 0xC0) >> 4));
+                       uint32_t address = ((vcount & 0x38) << 4) | ((sum & 0x0F) << 3) | (hcount & 0x07);
+
+                       // Add in particulars for the gfx mode we're in...
+                       if (false)
+                               // non hires
+                               address |= (!(!store80Mode && displayPage2) ? 0x400 : 0)
+                                       | (!store80Mode && displayPage2 ? 0x800 : 0);
+                       else
+                               // hires
+                               address |= (!(!store80Mode && displayPage2) ? 0x2000: 0)
+                                       | (!store80Mode && displayPage2 ? 0x4000 : 0)
+                                       | ((vcount & 0x07) << 10);
+
+                       WriteLog("$%04X ", address);
+               }
+
+               WriteLog("\n");
+       }
+
+#endif
 
        // Zero out memory
-//Need to bankify this stuff for the IIe emulation...
        memset(ram, 0, 0x10000);
        memset(rom, 0, 0x10000);
        memset(ram2, 0, 0x10000);
 
+       // Set up MMU
+       SetupAddressMap();
+       ResetMMUPointers();
+
+       // Install devices in slots
+       InstallFloppy(SLOT6);
+       InstallMockingboard(SLOT4);
+       InstallHardDrive(SLOT7);
+
        // Set up V65C02 execution context
        memset(&mainCPU, 0, sizeof(V65C02REGS));
-       mainCPU.RdMem = RdMem;
-       mainCPU.WrMem = WrMem;
+       mainCPU.RdMem = AppleReadMem;
+       mainCPU.WrMem = AppleWriteMem;
+       mainCPU.Timer = AppleTimer;
        mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
 
-//     alternateCharset = true;
-//     if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000))
        if (!LoadImg(settings.BIOSPath, rom + 0xC000, 0x4000))
        {
                WriteLog("Could not open file '%s'!\n", settings.BIOSPath);
                return -1;
        }
 
-//This is now included...
-/*     if (!LoadImg(settings.diskPath, diskRom, 0x100))
-       {
-               WriteLog("Could not open file '%s'!\nDisk II will be unavailable!\n", settings.diskPath);
-//             return -1;
-       }//*/
-
-//Load up disk image from config file (for now)...
-       floppyDrive.LoadImage(settings.diskImagePath1, 0);
-       floppyDrive.LoadImage(settings.diskImagePath2, 1);
-//     floppyDrive.LoadImage("./disks/temp.nib", 1);   // Load temp .nib file into second drive...
-
-//Kill the DOS ROM in slot 6 for now...
-//not
-//     memcpy(rom + 0xC600, diskROM, 0x100);
-//     memcpy(rom + 0xC700, diskROM, 0x100);   // Slot 7???
-
        WriteLog("About to initialize video...\n");
 
        if (!InitVideo())
        {
-               std::cout << "Aborting!" << std::endl;
+               printf("Could not init screen: aborting!\n");
                return -1;
        }
 
-       // Have to do this *after* video init but *before* sound init...!
-//Shouldn't be necessary since we're not doing emulation in the ISR...
+       GUI::Init(sdlRenderer);
+
+       WriteLog("About to initialize audio...\n");
+       SoundInit();
+
        if (settings.autoStateSaving)
        {
                // Load last state from file...
@@ -1416,65 +582,30 @@ int main(int /*argc*/, char * /*argv*/[])
                        WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
        }
 
+/*
+So, how to run this then?  Right now, we have two separate threads, one for the CPU and one for audio.  The screen refresh is tied to the CPU thread.
 
-#if 0
-// State loading!
-if (!LoadImg("./BT1_6502_RAM_SPACE.bin", ram, 0x10000))
-{
-       cout << "Couldn't load state file!" << endl;
-       cout << "Aborting!!" << endl;
-       return -1;
-}
+To do this properly, however, we need to execute approximately 1,020,484.32 cycles per second, and we need to tie the AY/regular sound to this rate.  Video should happen still approximately 60 times a second, even though the real thing has a frame rate of 59.92 Hz.
 
-//A  P  Y  X  S     PC
-//-- -- -- -- ----- -----
-//00 75 3B 53 FD 01 41 44
-
-mainCPU.cpuFlags = 0;
-mainCPU.a = 0x00;
-mainCPU.x = 0x53;
-mainCPU.y = 0x3B;
-mainCPU.cc = 0x75;
-mainCPU.sp = 0xFD;
-mainCPU.pc = 0x4441;
-
-textMode = false;
-mixedMode = false;
-displayPage2 = false;
-hiRes = true;
-
-//kludge...
-readHiRam = true;
-//dumpDis=true;
-//kludge II...
-memcpy(ram + 0xD000, ram + 0xC000, 0x1000);
-#endif
+Right now, the speed of the CPU is tied to the host system's refresh rate (which seems to be somewhere around 59.9 Hz).  Under this regime, the sound thread is starved much of the time, which means there's a timing mismatch somewhere between the sound thread and the CPU thread and the video (main) thread.
 
-       WriteLog("About to initialize audio...\n");
-       SoundInit();
-//nope SDL_EnableUNICODE(1);                                           // Needed to do key translation shit
+Other considerations: Even though we know the exact amount of cycles for one frame (17030 cycles to be exact), because the video frame rate is slightly under 60 (~59.92) the amount of time those cycles take can't be tied to the host system refresh rate, as they won't be the same (it will have about 8,000 or so more cycles in one second than it should have using 60 frames per second as the base frequency).  However, we do know that the system clock is 14.318180 MHz, and we do know that 1 out of every 65 cycles will take 2 extra ticks of the system clock (cycles are normally 14 ticks of the system clock).  So, by virtue of this, we know how long one frame is in seconds exactly (which would be (((65 * 14) + 2) * 262) / 14318180 = 16.688154500083 milliseconds).
+
+So we need to decouple the CPU thread from the host video thread, and have the CPU frame run at its rate so that it will complete its running in its alloted time.  We also need to have a little bit of cushion for the sound thread, so that its buffer doesn't starve.  Assuming we get the timing correct, it will pull ahead and fall behind and all average out in the end.
 
-//     gui = new GUI(surface);                                         // Set up the GUI system object...
-//     gui = new GUI(mainSurface);                                     // Set up the GUI system object...
-// SDL 2... this will likely cause Apple 2 to crash
-//     gui = new GUI(NULL);                                    // Set up the GUI system object...
-#if 0
-       gui->AddMenuTitle("Apple2");
-       gui->AddMenuItem("Test!", TestWindow/*, hotkey*/);
-       gui->AddMenuItem("");
-       gui->AddMenuItem("Quit", QuitEmulator, SDLK_q);
-       gui->CommitItemsToMenu();
-#endif
 
-       SetupBlurTable();                                                       // Set up the color TV emulation blur table
-       running = true;                                                         // Set running status...
+*/
 
-       InitializeEventList();                                          // Clear the event list before we use it...
-       SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
-       SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 s intervals
+       running = true;
+       InitializeEventList();
+       // Set frame to fire at 1/60 s interval
+       SetCallbackTime(FrameCallback, 16666.66666667);
+       // Set up blinking at 1/4 s intervals
+//     SetCallbackTime(BlinkTimer, 250000);
        startTicks = SDL_GetTicks();
 
 #ifdef THREADED_65C02
+       // Kick off the CPU...
        cpuCond = SDL_CreateCond();
        mainSem = SDL_CreateSemaphore(1);
        cpuThread = SDL_CreateThread(CPUThreadFunc, NULL, NULL);
@@ -1483,62 +614,58 @@ memcpy(ram + 0xD000, ram + 0xC000, 0x1000);
 #endif
 
        WriteLog("Entering main loop...\n");
+
        while (running)
        {
+#ifdef CPU_CLOCK_CHECKING
                double timeToNextEvent = GetTimeToNextEvent();
+#endif
 #ifndef THREADED_65C02
                Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent));
-#endif
-//We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
-//(Fix so that this is not a requirement!)
-//Fixed, but mainCPU.clock is destroyed in the bargain. Oh well.
-//             mainCPU.clock -= USEC_TO_M6502_CYCLES(timeToNextEvent);
 
-#ifdef CPU_CLOCK_CHECKING
-#ifndef THREADED_65C02
+       #ifdef CPU_CLOCK_CHECKING
 totalCPU += USEC_TO_M6502_CYCLES(timeToNextEvent);
+       #endif
 #endif
-#endif
-               // Handle CPU time delta for sound...
-//Don't need this anymore now that we use absolute time...
-//             AddToSoundTimeBase(USEC_TO_M6502_CYCLES(timeToNextEvent));
                HandleNextEvent();
        }
 
 #ifdef THREADED_65C02
 WriteLog("Main: cpuFinished = true;\n");
-cpuFinished = true;
-//#warning "If sound thread is behind, CPU thread will never wake up... !!! FIX !!!" [DONE]
-//What to do? How do you know when the CPU is sleeping???
-//USE A CONDITIONAL!!! OF COURSE!!!!!!11!11!11!!!1!
-#if 0
-SDL_mutexP(mainMutex);
-SDL_CondWait(mainCond, mainMutex);     // Wait for CPU thread to get to signal point...
-SDL_mutexV(mainMutex);
-#else
-//Nope, use a semaphore...
+       cpuFinished = true;
+
 WriteLog("Main: SDL_SemWait(mainSem);\n");
-SDL_SemWait(mainSem);//should lock until CPU thread is waiting...
+       // Only do this if NOT in power off/emulation paused mode!
+       if (!pauseMode)
+               // Should lock until CPU thread is waiting...
+               SDL_SemWait(mainSem);
 #endif
 
-WriteLog("Main: SDL_CondSignal(cpuCond);//thread is probably asleep, wake it up\n");
-SDL_CondSignal(cpuCond);//thread is probably asleep, wake it up
+WriteLog("Main: SDL_CondSignal(cpuCond);\n");
+       SDL_CondSignal(cpuCond);//thread is probably asleep, so wake it up
 WriteLog("Main: SDL_WaitThread(cpuThread, NULL);\n");
-SDL_WaitThread(cpuThread, NULL);
-//nowok:SDL_WaitThread(CPUThreadFunc, NULL);
+       SDL_WaitThread(cpuThread, NULL);
 WriteLog("Main: SDL_DestroyCond(cpuCond);\n");
-SDL_DestroyCond(cpuCond);
-
-//SDL_DestroyMutex(mainMutex);
-SDL_DestroySemaphore(mainSem);
-#endif
+       SDL_DestroyCond(cpuCond);
+       SDL_DestroySemaphore(mainSem);
 
+       // Autosave state here, if requested...
        if (settings.autoStateSaving)
-       {
-               // Save state here...
                SaveApple2State(settings.autoStatePath);
-       }
-floppyDrive.SaveImage();
+
+       floppyDrive[0].SaveImage(0);
+       floppyDrive[0].SaveImage(1);
+
+#if 0
+#include "dis65c02.h"
+static char disbuf[80];
+uint16_t pc=0x801;
+while (pc < 0x9FF)
+{
+       pc += Decode65C02(&mainCPU, disbuf, pc);
+       WriteLog("%s\n", disbuf);
+}
+#endif
 
        SoundDone();
        VideoDone();
@@ -1549,182 +676,280 @@ floppyDrive.SaveImage();
 }
 
 
-/*
-Apple II keycodes
------------------
-
-Key     Aln CTL SHF BTH
------------------------
-space  $A0     $A0     $A0 $A0         No xlation
-RETURN $8D     $8D     $8D     $8D             No xlation
-0              $B0     $B0     $B0     $B0             Need to screen shift+0 (?)
-1!             $B1 $B1 $A1 $A1         No xlation
-2"             $B2     $B2     $A2     $A2             No xlation
-3#             $B3     $B3     $A3     $A3             No xlation
-4$             $B4     $B4     $A4     $A4             No xlation
-5%             $B5     $B5     $A5     $A5             No xlation
-6&             $B6     $B6     $A6     $A6             No xlation
-7'             $B7     $B7     $A7     $A7             No xlation
-8(             $B8     $B8     $A8     $A8             No xlation
-9)             $B9     $B9     $A9     $A9             No xlation
-:*             $BA     $BA     $AA     $AA             No xlation
-;+             $BB     $BB     $AB     $AB             No xlation
-,<             $AC     $AC     $BC     $BC             No xlation
--=             $AD     $AD     $BD     $BD             No xlation
-.>             $AE     $AE     $BE     $BE             No xlation
-/?             $AF     $AF     $BF     $BF             No xlation
-A              $C1     $81     $C1     $81
-B              $C2     $82     $C2     $82
-C              $C3     $83     $C3     $83
-D              $C4     $84     $C4     $84
-E              $C5     $85     $C5     $85
-F              $C6     $86     $C6     $86
-G              $C7     $87     $C7     $87
-H              $C8     $88     $C8     $88
-I              $C9     $89     $C9     $89
-J              $CA     $8A     $CA     $8A
-K              $CB     $8B     $CB     $8B
-L              $CC     $8C     $CC     $8C
-M              $CD     $8D     $DD     $9D             -> ODD
-N^             $CE     $8E     $DE     $9E             -> ODD
-O              $CF     $8F     $CF     $8F
-P@             $D0     $90     $C0     $80             Need to xlate CTL+SHFT+P & SHFT+P (?)
-Q              $D1     $91     $D1     $91
-R              $D2     $92     $D2     $92
-S              $D3     $93     $D3     $93
-T              $D4     $94     $D4     $94
-U              $D5     $95     $D5     $95
-V              $D6     $96     $D6     $96
-W              $D7     $97     $D7     $97
-X              $D8     $98     $D8     $98
-Y              $D9     $99     $D9     $99
-Z              $DA     $9A     $DA     $9A
-<-             $88     $88     $88     $88
-->             $95     $95     $95     $95
-ESC            $9B     $9B     $9B     $9B             No xlation
+//
+// Apple //e scancodes. Tables are normal (0), +CTRL (1), +SHIFT (2),
+// +CTRL+SHIFT (3). Order of keys is:
+// Delete, left, tab, down, up, return, right, escape
+// Space, single quote, comma, minus, period, slash
+// Numbers 0-9
+// Semicolon, equals, left bracket, backslash, right bracket, backquote
+// Letters a-z (lowercase)
+//
+// N.B.: The Apple //e keyboard maps its shift characters like most modern US
+//       keyboards, so this table should suffice for the shifted keys just
+//       fine.
+//
+uint8_t apple2e_keycode[4][56] = {
+       {       // Normal
+               0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B,
+               0x20, 0x27, 0x2C, 0x2D, 0x2E, 0x2F,
+               0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+               0x3B, 0x3D, 0x5B, 0x5C, 0x5D, 0x60,
+               0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A,
+               0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74,
+               0x75, 0x76, 0x77, 0x78, 0x79, 0x7A
+       },
+       {       // With CTRL held
+               0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B,
+               0x20, 0x27, 0x2C, 0x1F, 0x2E, 0x2F,
+               0x30, 0x31, 0x00, 0x33, 0x34, 0x35, 0x1E, 0x37, 0x38, 0x39,
+               0x3B, 0x3D, 0x1B, 0x1C, 0x1D, 0x60,
+               0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+               0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
+               0x15, 0x16, 0x17, 0x18, 0x19, 0x1A
+       },
+       {       // With Shift held
+               0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B,
+               0x20, 0x22, 0x3C, 0x5F, 0x3E, 0x3F,
+               0x29, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28,
+               0x3A, 0x2B, 0x7B, 0x7C, 0x7D, 0x7E,
+               0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
+               0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54,
+               0x55, 0x56, 0x57, 0x58, 0x59, 0x5A
+       },
+       {       // With CTRL+Shift held
+               0x7F, 0x08, 0x09, 0x0A, 0x0B, 0x0D, 0x15, 0x1B,
+               0x20, 0x22, 0x3C, 0x1F, 0x3E, 0x3F,
+               0x29, 0x21, 0x00, 0x23, 0x24, 0x25, 0x1E, 0x26, 0x2A, 0x28,
+               0x3A, 0x2B, 0x1B, 0x1C, 0x1D, 0x7E,
+               0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+               0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
+               0x15, 0x16, 0x17, 0x18, 0x19, 0x1A
+       }
+};
 
-*/
-//static uint64_t lastCPUCycles = 0;
 static uint32_t frameCount = 0;
 static void FrameCallback(void)
 {
        SDL_Event event;
+       uint8_t keyIndex;
+
+       frameTimePtr = (frameTimePtr + 1) % 60;
+       frameTime[frameTimePtr] = startTicks;
 
        while (SDL_PollEvent(&event))
        {
                switch (event.type)
                {
-               case SDL_TEXTINPUT:
-//Need to do some key translation here, and screen out non-apple keys as well...
-//(really, could do it all in SDL_KEYDOWN, would just have to get symbols &
-// everything else done separately. this is slightly easier. :-P)
-//                     if (event.key.keysym.sym == SDLK_TAB)   // Prelim key screening...
-                       if (event.edit.text[0] == '\t') // Prelim key screening...
+               case SDL_KEYDOWN:
+                       // We do our own repeat handling thank you very much! :-)
+                       if (event.key.repeat != 0)
                                break;
 
-                       lastKeyPressed = event.edit.text[0];
-                       keyDown = true;
+                       // Use CTRL+SHIFT+Q to exit, as well as the usual window decoration
+                       // method
+                       if ((event.key.keysym.mod & KMOD_CTRL)
+                               && (event.key.keysym.mod & KMOD_SHIFT)
+                               && (event.key.keysym.sym == SDLK_q))
+                       {
+                               running = false;
+                               // We return here, because we don't want to pick up any
+                               // spurious keypresses with our exit sequence.
+                               return;
+                       }
+
+                       // CTRL+RESET key emulation (mapped to CTRL+HOME)
+                       if ((event.key.keysym.mod & KMOD_CTRL)
+                               && (event.key.keysym.sym == SDLK_HOME))
+                       {
+//seems to leave the machine in an inconsistent state vis-a-vis the language card... [does it anymore?]
+                               resetKeyDown = true;
+                               // Need to reset the MMU switches as well on RESET
+                               intCXROM = false;
+                               slotC3ROM = false;
+                               intC8ROM = false;
+                               break;
+                       }
 
-                       //kludge: should have a caps lock thingy here...
-                       //or all uppercase for ][+...
-//                     if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
-//                             lastKeyPressed &= 0xDF;         // Convert to upper case...
+                       // There has GOT to be a better way off mapping SDLKs to our
+                       // keyindex. But for now, this should suffice.
+                       keyIndex = 0xFF;
 
-                       break;
-               case SDL_KEYDOWN:
-                       // CTRL+RESET key emulation (mapped to CTRL+`)
-// This doesn't work...
-//                     if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
-//                     if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
-                       if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
-//NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
-//ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
-                               mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
+                       switch (event.key.keysym.sym)
+                       {
+                       case SDLK_BACKSPACE:    keyIndex =  0; break;
+                       case SDLK_LEFT:         keyIndex =  1; break;
+                       case SDLK_TAB:          keyIndex =  2; break;
+                       case SDLK_DOWN:         keyIndex =  3; break;
+                       case SDLK_UP:           keyIndex =  4; break;
+                       case SDLK_RETURN:       keyIndex =  5; break;
+                       case SDLK_RIGHT:        keyIndex =  6; break;
+                       case SDLK_ESCAPE:       keyIndex =  7; break;
+                       case SDLK_SPACE:        keyIndex =  8; break;
+                       case SDLK_QUOTE:        keyIndex =  9; break;
+                       case SDLK_COMMA:        keyIndex = 10; break;
+                       case SDLK_MINUS:        keyIndex = 11; break;
+                       case SDLK_PERIOD:       keyIndex = 12; break;
+                       case SDLK_SLASH:        keyIndex = 13; break;
+                       case SDLK_0:            keyIndex = 14; break;
+                       case SDLK_1:            keyIndex = 15; break;
+                       case SDLK_2:            keyIndex = 16; break;
+                       case SDLK_3:            keyIndex = 17; break;
+                       case SDLK_4:            keyIndex = 18; break;
+                       case SDLK_5:            keyIndex = 19; break;
+                       case SDLK_6:            keyIndex = 20; break;
+                       case SDLK_7:            keyIndex = 21; break;
+                       case SDLK_8:            keyIndex = 22; break;
+                       case SDLK_9:            keyIndex = 23; break;
+                       case SDLK_SEMICOLON:    keyIndex = 24; break;
+                       case SDLK_EQUALS:       keyIndex = 25; break;
+                       case SDLK_LEFTBRACKET:  keyIndex = 26; break;
+                       case SDLK_BACKSLASH:    keyIndex = 27; break;
+                       case SDLK_RIGHTBRACKET: keyIndex = 28; break;
+                       case SDLK_BACKQUOTE:    keyIndex = 29; break;
+                       case SDLK_a:            keyIndex = 30; break;
+                       case SDLK_b:            keyIndex = 31; break;
+                       case SDLK_c:            keyIndex = 32; break;
+                       case SDLK_d:            keyIndex = 33; break;
+                       case SDLK_e:            keyIndex = 34; break;
+                       case SDLK_f:            keyIndex = 35; break;
+                       case SDLK_g:            keyIndex = 36; break;
+                       case SDLK_h:            keyIndex = 37; break;
+                       case SDLK_i:            keyIndex = 38; break;
+                       case SDLK_j:            keyIndex = 39; break;
+                       case SDLK_k:            keyIndex = 40; break;
+                       case SDLK_l:            keyIndex = 41; break;
+                       case SDLK_m:            keyIndex = 42; break;
+                       case SDLK_n:            keyIndex = 43; break;
+                       case SDLK_o:            keyIndex = 44; break;
+                       case SDLK_p:            keyIndex = 45; break;
+                       case SDLK_q:            keyIndex = 46; break;
+                       case SDLK_r:            keyIndex = 47; break;
+                       case SDLK_s:            keyIndex = 48; break;
+                       case SDLK_t:            keyIndex = 49; break;
+                       case SDLK_u:            keyIndex = 50; break;
+                       case SDLK_v:            keyIndex = 51; break;
+                       case SDLK_w:            keyIndex = 52; break;
+                       case SDLK_x:            keyIndex = 53; break;
+                       case SDLK_y:            keyIndex = 54; break;
+                       case SDLK_z:            keyIndex = 55; break;
+                       }
 
-                       if (event.key.keysym.sym == SDLK_RIGHT)
-                               lastKeyPressed = 0x15, keyDown = true;
-                       else if (event.key.keysym.sym == SDLK_LEFT)
-                               lastKeyPressed = 0x08, keyDown = true;
-                       else if (event.key.keysym.sym == SDLK_UP)
-                               lastKeyPressed = 0x0B, keyDown = true;
-                       else if (event.key.keysym.sym == SDLK_DOWN)
-                               lastKeyPressed = 0x0A, keyDown = true;
-                       else if (event.key.keysym.sym == SDLK_RETURN)
-                               lastKeyPressed = 0x0D, keyDown = true;
-                       else if (event.key.keysym.sym == SDLK_ESCAPE)
-                               lastKeyPressed = 0x1B, keyDown = true;
-                       else if (event.key.keysym.sym == SDLK_BACKSPACE)
-                               lastKeyPressed = 0x7F, keyDown = true;
-
-                       // Fix CTRL+key combo...
-                       if (event.key.keysym.mod & KMOD_CTRL)
+                       // Stuff the key in if we have a valid one...
+                       if (keyIndex != 0xFF)
                        {
-                               if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
+                               // Handle Shift, CTRL, & Shift+CTRL combos
+                               uint8_t table = 0;
+
+                               if (event.key.keysym.mod & KMOD_CTRL)
+                                       table |= 1;
+
+                               if (event.key.keysym.mod & KMOD_SHIFT)
+                                       table |= 2;
+
+                               lastKeyPressed = apple2e_keycode[table][keyIndex];
+                               keyDown = true;
+
+                               // Handle Caps Lock
+                               if ((SDL_GetModState() & KMOD_CAPS)
+                                       && (lastKeyPressed >= 0x61) && (lastKeyPressed <= 0x7A))
+                                       lastKeyPressed -= 0x20;
+
+                               // Handle key repeat if the key hasn't been held
+                               keyDelay = 15;
+                               keyDownCount++;
+
+                               // Buffer the key held. Note that the last key is always
+                               // stuffed into keysHeld[0].
+                               if (keyDownCount >= 2)
                                {
-                                       lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 1;
-                                       keyDown = true;
-//printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40);
+                                       keysHeld[1] = keysHeld[0];
+                                       keysHeldAppleCode[1] = keysHeldAppleCode[0];
+
+                                       if (keyDownCount > 2)
+                                               keyDownCount = 2;
                                }
+
+                               keysHeld[0] = event.key.keysym.sym;
+                               keysHeldAppleCode[0] = lastKeyPressed;
+                               break;
                        }
 
-                       // Use ALT+Q to exit, as well as the usual window decoration method
-                       if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
-                               running = false;
+                       if (event.key.keysym.sym == SDLK_PAUSE)
+                       {
+                               pauseMode = !pauseMode;
 
-                       // Paddle buttons 0 & 1
-                       if (event.key.keysym.sym == SDLK_INSERT)
+                               if (pauseMode)
+                               {
+                                       SoundPause();
+                                       SpawnMessage("*** PAUSED ***");
+                               }
+                               else
+                               {
+                                       SoundResume();
+                                       SpawnMessage("*** RESUME ***");
+                               }
+                       }
+                       // Buttons 0 & 1
+                       else if (event.key.keysym.sym == SDLK_LALT)
                                openAppleDown = true;
-                       if (event.key.keysym.sym == SDLK_PAGEUP)
+                       else if (event.key.keysym.sym == SDLK_RALT)
                                closedAppleDown = true;
-
-                       if (event.key.keysym.sym == SDLK_F11)
-                               dumpDis = !dumpDis;                             // Toggle the disassembly process
-//                     else if (event.key.keysym.sym == SDLK_F11)
-//                             floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk...
-else if (event.key.keysym.sym == SDLK_F9)
-{
-       floppyDrive.CreateBlankImage();
-//     SpawnMessage("Image cleared...");
-}//*/
-else if (event.key.keysym.sym == SDLK_F10)
-{
-       floppyDrive.SwapImages();
-//     SpawnMessage("Image swapped...");
-}//*/
-
-                       if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
+                       else if (event.key.keysym.sym == SDLK_F2)
                                TogglePalette();
-                       else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
+                       else if (event.key.keysym.sym == SDLK_F3)
                                CycleScreenTypes();
-
-//                     if (event.key.keysym.sym == SDLK_F5)    // Temp GUI launch key
-                       if (event.key.keysym.sym == SDLK_F1)    // GUI launch key
-//NOTE: Should parse the output to determine whether or not the user requested
-//      to quit completely... !!! FIX !!!
-                               gui->Run();
-
-                       if (event.key.keysym.sym == SDLK_F5)
+                       else if (event.key.keysym.sym == SDLK_F4)
+                               ToggleTickDisplay();
+                       else if (event.key.keysym.sym == SDLK_F5)
                        {
                                VolumeDown();
                                char volStr[19] = "[****************]";
-//                             volStr[GetVolume()] = 0;
+
                                for(int i=GetVolume(); i<16; i++)
                                        volStr[1 + i] = '-';
+
                                SpawnMessage("Volume: %s", volStr);
                        }
                        else if (event.key.keysym.sym == SDLK_F6)
                        {
                                VolumeUp();
                                char volStr[19] = "[****************]";
-//                             volStr[GetVolume()] = 0;
+
                                for(int i=GetVolume(); i<16; i++)
                                        volStr[1 + i] = '-';
+
                                SpawnMessage("Volume: %s", volStr);
                        }
-
-                       static bool fullscreenDebounce = false;
-
-                       if (event.key.keysym.sym == SDLK_F12)
+                       else if (event.key.keysym.sym == SDLK_F7)
+                       {
+                               // 4th root of 2 is ~1.18920711500272 (~1.5 dB)
+                               // This attenuates by ~3 dB
+                               VAY_3_8910::maxVolume /= 1.4142135f;
+                               SpawnMessage("MB Volume: %d", (int)VAY_3_8910::maxVolume);
+                       }
+                       else if (event.key.keysym.sym == SDLK_F8)
+                       {
+                               VAY_3_8910::maxVolume *= 1.4142135f;
+                               SpawnMessage("MB Volume: %d", (int)VAY_3_8910::maxVolume);
+                       }
+else if (event.key.keysym.sym == SDLK_F9)
+{
+       floppyDrive[0].CreateBlankImage(1);
+//     SpawnMessage("Image cleared...");
+}//*/
+/*else if (event.key.keysym.sym == SDLK_F10)
+{
+       floppyDrive[0].SwapImages();
+//     SpawnMessage("Image swapped...");
+}//*/
+                       // Toggle the disassembly process
+                       else if (event.key.keysym.sym == SDLK_F11)
+                       {
+                               dumpDis = !dumpDis;
+                               SpawnMessage("Trace: %s", (dumpDis ? "ON" : "off"));
+                       }
+                       else if (event.key.keysym.sym == SDLK_F12)
                        {
                                if (!fullscreenDebounce)
                                {
@@ -1732,30 +957,129 @@ else if (event.key.keysym.sym == SDLK_F10)
                                        fullscreenDebounce = true;
                                }
                        }
-//                     else
 
                        break;
+
                case SDL_KEYUP:
                        if (event.key.keysym.sym == SDLK_F12)
                                fullscreenDebounce = false;
-
                        // Paddle buttons 0 & 1
-                       if (event.key.keysym.sym == SDLK_INSERT)
+                       else if (event.key.keysym.sym == SDLK_LALT)
                                openAppleDown = false;
-                       if (event.key.keysym.sym == SDLK_PAGEUP)
+                       else if (event.key.keysym.sym == SDLK_RALT)
                                closedAppleDown = false;
+                       else if ((event.key.keysym.mod & KMOD_CTRL)
+                               && (event.key.keysym.sym == SDLK_HOME))
+                               resetKeyDown = false;
+                       else
+                       {
+                               // Handle key buffering 'key up' event (2 key rollover)
+                               if ((keyDownCount == 1) && (event.key.keysym.sym == keysHeld[0]))
+                               {
+                                       keyDownCount--;
+                                       keyDelay = 0;   // Reset key delay
+                               }
+                               else if (keyDownCount == 2)
+                               {
+                                       if (event.key.keysym.sym == keysHeld[0])
+                                       {
+                                               keyDownCount--;
+                                               keysHeld[0] = keysHeld[1];
+                                               keysHeldAppleCode[0] = keysHeldAppleCode[1];
+                                       }
+                                       else if (event.key.keysym.sym == keysHeld[1])
+                                       {
+                                               keyDownCount--;
+                                       }
+                               }
+                       }
+
+                       break;
+
+               case SDL_MOUSEBUTTONDOWN:
+                       GUI::MouseDown(event.motion.x, event.motion.y, event.motion.state);
+                       break;
+
+               case SDL_MOUSEBUTTONUP:
+                       GUI::MouseUp(event.motion.x, event.motion.y, event.motion.state);
+                       break;
+
+               case SDL_MOUSEMOTION:
+                       GUI::MouseMove(event.motion.x, event.motion.y, event.motion.state);
+
+                       // Handle mouse showing when the mouse is hidden...
+                       if (hideMouseTimeout == -1)
+                               SDL_ShowCursor(1);
 
-//                     if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
-//                             keyDown = false;
+                       hideMouseTimeout = 60;
+                       break;
+
+               case SDL_WINDOWEVENT:
+                       if (event.window.event == SDL_WINDOWEVENT_LEAVE)
+                               GUI::MouseMove(0, 0, 0);
 
                        break;
+
                case SDL_QUIT:
                        running = false;
                }
        }
 
-//#warning "!!! Taking MAJOR time hit with the video frame rendering !!!"
-       RenderVideoFrame();
+       // Hide the mouse if it's been 1s since the last time it was moved
+       // N.B.: Should disable mouse hiding if it's over the GUI...
+       if ((hideMouseTimeout > 0) && !(GUI::sidebarState == SBS_SHOWN || DiskSelector::showWindow == true))
+               hideMouseTimeout--;
+       else if (hideMouseTimeout == 0)
+       {
+               hideMouseTimeout--;
+               SDL_ShowCursor(0);
+       }
+
+       // Stuff the Apple keyboard buffer, if any keys are pending
+       // N.B.: May have to simulate the key repeat delay too [yup, sure do]
+       //       According to "Understanding the Apple IIe", the initial delay is
+       //       between 32 & 48 jiffies and the repeat is every 4 jiffies.
+       if (keyDownCount > 0)
+       {
+               keyDelay--;
+
+               if (keyDelay == 0)
+               {
+                       keyDelay = 3;
+                       lastKeyPressed = keysHeldAppleCode[0];
+                       keyDown = true;
+               }
+       }
+
+       // Handle power request from the GUI
+       if (powerStateChangeRequested)
+       {
+               if (GUI::powerOnState)
+               {
+                       pauseMode = false;
+                       // Unlock the CPU thread...
+                       SDL_SemPost(mainSem);
+               }
+               else
+               {
+                       pauseMode = true;
+                       // Should lock until CPU thread is waiting...
+                       SDL_SemWait(mainSem);
+                       ResetApple2State();
+               }
+
+               powerStateChangeRequested = false;
+       }
+
+       blinkTimer = (blinkTimer + 1) & 0x1F;
+
+       if (blinkTimer == 0)
+               flash = !flash;
+
+       // Render the Apple screen + GUI overlay
+       RenderAppleScreen(sdlRenderer);
+       GUI::Render(sdlRenderer);
+       SDL_RenderPresent(sdlRenderer);
        SetCallbackTime(FrameCallback, 16666.66666667);
 
 #ifdef CPU_CLOCK_CHECKING
@@ -1772,44 +1096,52 @@ if (counter == 60)
        counter = 0;
 }
 #endif
-//Instead of this, we should yield remaining time to other processes... !!! FIX !!! [DONE]
-//lessee...
-//nope.
-//Actually, slows things down too much...
-//SDL_Delay(10);
-//     while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
 
 // This is the problem: If you set the interval to 16, it runs faster than
-// 1/60s per frame. If you set it to 17, it runs slower. What we need is to
-// have it do 16 for one frame, then 17 for two others. Then it should average
-// out to 1/60s per frame every 3 frames.
+// 1/60s per frame.  If you set it to 17, it runs slower.  What we need is to
+// have it do 16 for one frame, then 17 for two others.  Then it should average
+// out to 1/60s per frame every 3 frames.  [And now it does!]
+// Maybe we need a higher resolution timer, as the SDL_GetTicks() (in ms) seems
+// to jitter all over the place...
        frameCount = (frameCount + 1) % 3;
+//     uint32_t waitFrameTime = 17 - (frameCount == 0 ? 1 : 0);
+       // Get number of ticks burned in this frame, for displaying elsewhere
+#if 0
+       frameTicks = SDL_GetTicks() - startTicks;
+#else
+       frameTicks = ((SDL_GetPerformanceCounter() - startTicks) * 1000) / SDL_GetPerformanceFrequency();
+#endif
 
-       uint32_t waitFrameTime = 17 - (frameCount == 0 ? 1 : 0);
-
-       while (SDL_GetTicks() - startTicks < waitFrameTime)
-               SDL_Delay(1);                                                   // Wait for next frame...
+       // Wait for next frame...
+//     while (SDL_GetTicks() - startTicks < waitFrameTime)
+//             SDL_Delay(1);
 
+#if 0
        startTicks = SDL_GetTicks();
+#else
+       startTicks = SDL_GetPerformanceCounter();
+#endif
 #if 0
        uint64_t cpuCycles = GetCurrentV65C02Clock();
        uint32_t cyclesBurned = (uint32_t)(cpuCycles - lastCPUCycles);
        WriteLog("FrameCallback: used %i cycles\n", cyclesBurned);
-       lastCPUCycles = cpuCycles;
+       lastCPUCycles = cpuCycles
 #endif
 
 //let's wait, then signal...
-//works longer, but then still falls behind...
+//works longer, but then still falls behind... [FIXED, see above]
 #ifdef THREADED_65C02
-       SDL_CondSignal(cpuCond);//OK, let the CPU go another frame...
+       if (!pauseMode)
+               SDL_CondSignal(cpuCond);//OK, let the CPU go another frame...
 #endif
 }
 
 
 static void BlinkTimer(void)
 {
+       // Set up blinking at 1/4 sec intervals
        flash = !flash;
-       SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 sec intervals
+       SetCallbackTime(BlinkTimer, 250000);
 }
 
 
@@ -1838,3 +1170,4 @@ while (!done)
                time = 20;
 }
 */
+