]> Shamusworld >> Repos - apple2/commitdiff
Fixed keyboard handling, consolidated video handling.
authorShamus Hammons <jlhamm@acm.org>
Mon, 5 Jun 2017 03:28:52 +0000 (22:28 -0500)
committerShamus Hammons <jlhamm@acm.org>
Mon, 5 Jun 2017 03:28:52 +0000 (22:28 -0500)
The keyboard handler now properly emulates the //e's 2-key rollover.
However, we might have to add in a key repeat delay if it makes it so
that you can't type on the console anymore. This had to be done because
SDL will throw in key delay if you rely on it's SDL_KEYDOWN message and
this caused many games with keyboard input to be unplayable. Also, we
consolidated the video handling files, as there was very little in
video.cpp and so we moved it into applevideo.cpp and renamed that file
to video.cpp. Things are a lot cleaner as a result of this merge. :-)

Makefile
src/apple2.cpp
src/applevideo.cpp [deleted file]
src/applevideo.h [deleted file]
src/dis65c02.cpp
src/floppy.cpp
src/gui/diskselector.cpp
src/gui/gui.cpp
src/mmu.cpp
src/video.cpp
src/video.h

index 27041dfd61fc43683d190f77719fc05bd8efbafa..9006897c5233b0eab07861e90c89eb80a50e6e48 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -105,9 +105,8 @@ OBJS = \
        obj/font12pt.o        \
        obj/font14pt.o        \
        obj/gui.o             \
-                          \
+                              \
        obj/apple2-icon-64x64.o \
-       obj/applevideo.o      \
        obj/ay8910.o          \
        obj/charset.o         \
        obj/dis65c02.o        \
index 36bf0b0f0dee9d0832e2cbccca2a09b5595db274..b15113fcb96d92cee34d94e9446c52e570b0dbc0 100644 (file)
@@ -2,7 +2,7 @@
 // Apple 2 SDL Portable Apple Emulator
 //
 // by James Hammons
-// © 2014 Underground Software
+// © 2017 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
 
 #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 "floppy.h"
 #include "log.h"
-#include "video.h"
-#include "sound.h"
+#include "mmu.h"
 #include "settings.h"
-#include "v65c02.h"
-#include "applevideo.h"
+#include "sound.h"
 #include "timing.h"
-#include "floppy.h"
-#include "firmware.h"
-#include "mmu.h"
-
+#include "v65c02.h"
+#include "video.h"
 #include "gui/gui.h"
 
 // Debug and misc. defines
@@ -95,6 +93,12 @@ static bool pauseMode = false;
 static bool fullscreenDebounce = false;
 static bool capsLock = false;
 static bool capsLockDebounce = false;
+static bool resetKeyDown = false;
+
+// Vars to handle the //e's 2-key rollover
+static SDL_Keycode keysHeld[2];
+static uint8_t keysHeldAppleCode[2];
+static uint8_t keyDownCount = 0;
 
 // Local functions
 
@@ -110,18 +114,16 @@ 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
 
 // Let's try a thread...
 //
 // Here's how it works: Execute 1 frame's worth, then sleep. Other stuff wakes
-// it up
+// it up.
 //
 int CPUThreadFunc(void * data)
 {
@@ -129,26 +131,20 @@ int CPUThreadFunc(void * data)
        // 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;
 #endif
 
        do
        {
-// This is never set to true anywhere...
-//             if (cpuSleep)
-//                     SDL_CondWait(cpuCond, cpuMutex);
-
 // decrement mainSem...
 #ifdef THREAD_DEBUGGING
 WriteLog("CPU: SDL_SemWait(mainSem);\n");
 #endif
                SDL_SemWait(mainSem);
 
-// There are exactly 800 slices of 21.333 cycles per frame, so it works out
-// evenly.
+               // There are exactly 800 slices of 21.333 cycles per frame, so it works
+               // out evenly.
 #ifdef THREAD_DEBUGGING
 WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
 #endif
@@ -163,6 +159,11 @@ WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
                                overflow -= 1.0;
                        }
 
+                       // If the CTRL+Reset key combo is being held, make sure the RESET
+                       // line stays asserted:
+                       if (resetKeyDown)
+                               mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
+
                        Execute65C02(&mainCPU, cycles);
                        WriteSampleToBuffer();
 
@@ -174,7 +175,7 @@ WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
 WriteLog("CPU: SDL_mutexP(cpuMutex);\n");
 #endif
                SDL_mutexP(cpuMutex);
-// increment mainSem...
+               // increment mainSem...
 #ifdef THREAD_DEBUGGING
 WriteLog("CPU: SDL_SemPost(mainSem);\n");
 #endif
@@ -374,7 +375,7 @@ int main(int /*argc*/, char * /*argv*/[])
 {
        InitLog("./apple2.log");
        LoadSettings();
-       srand(time(NULL));                                                                      // Initialize RNG
+       srand(time(NULL));                      // Initialize RNG
 
        // Zero out memory
        memset(ram, 0, 0x10000);
@@ -401,14 +402,14 @@ int main(int /*argc*/, char * /*argv*/[])
 
        if (!InitVideo())
        {
-               std::cout << "Aborting!" << std::endl;
+               printf("Could not init screen: aborting!\n");
                return -1;
        }
 
        GUI::Init(sdlRenderer);
+       WriteLog("About to initialize audio...\n");
+       SoundInit();
 
-       // Have to do this *after* video init but *before* sound init...!
-//Shouldn't be necessary since we're not doing emulation in the ISR...
        if (settings.autoStateSaving)
        {
                // Load last state from file...
@@ -416,16 +417,16 @@ int main(int /*argc*/, char * /*argv*/[])
                        WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
        }
 
-       WriteLog("About to initialize audio...\n");
-       SoundInit();
-       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);
@@ -538,16 +539,69 @@ Z         $DA     $9A     $DA     $9A
 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 uint32_t frameCount = 0;
 static void FrameCallback(void)
 {
        SDL_Event event;
+       uint8_t keyIndex;
 
        while (SDL_PollEvent(&event))
        {
                switch (event.type)
                {
 // Problem with using SDL_TEXTINPUT is that it causes key delay. :-/
+// We get key delay regardless... :-/
 #if 0
                case SDL_TEXTINPUT:
 //Need to do some key translation here, and screen out non-apple keys as well...
@@ -568,6 +622,10 @@ static void FrameCallback(void)
                        break;
 #endif
                case SDL_KEYDOWN:
+                       // We do our own repeat handling thank you very much! :-)
+                       if (event.key.repeat != 0)
+                               break;
+
                        // Use CTRL+SHIFT+Q to exit, as well as the usual window decoration
                        // method
                        if ((event.key.keysym.mod & KMOD_CTRL)
@@ -580,196 +638,109 @@ static void FrameCallback(void)
                                return;
                        }
 
-                       // 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))
+                       // CTRL+RESET key emulation (mapped to CTRL+HOME)
                        if ((event.key.keysym.mod & KMOD_CTRL)
-                               && (event.key.keysym.sym == SDLK_BACKQUOTE))
+                               && (event.key.keysym.sym == SDLK_HOME))
                        {
-//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;
+//seems to leave the machine in an inconsistent state vis-a-vis the language card... [does it anymore?]
+                               resetKeyDown = true;
                                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)
-                       {
-                               if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
-                               {
-                                       lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 1;
-                                       keyDown = true;
-//printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40);
-                                       break;
-                               }
-                       }
+                       // There has GOT to be a better way off mapping SDLKs to our
+                       // keyindex. But for now, this should suffice.
+                       keyIndex = 0xFF;
 
-#if 1
-                       // Fix SHIFT+key combo...
-                       if (event.key.keysym.mod & KMOD_SHIFT)
+                       switch (event.key.keysym.sym)
                        {
-                               if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
-                               {
-                                       lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 0x41;
-                                       keyDown = true;
-//printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40);
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_1)
-                               {
-                                       lastKeyPressed = '!';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_2)
-                               {
-                                       lastKeyPressed = '@';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_3)
-                               {
-                                       lastKeyPressed = '#';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_4)
-                               {
-                                       lastKeyPressed = '$';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_5)
-                               {
-                                       lastKeyPressed = '%';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_6)
-                               {
-                                       lastKeyPressed = '^';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_7)
-                               {
-                                       lastKeyPressed = '&';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_8)
-                               {
-                                       lastKeyPressed = '*';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_9)
-                               {
-                                       lastKeyPressed = '(';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_0)
-                               {
-                                       lastKeyPressed = ')';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_MINUS)
-                               {
-                                       lastKeyPressed = '_';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_EQUALS)
-                               {
-                                       lastKeyPressed = '+';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_LEFTBRACKET)
-                               {
-                                       lastKeyPressed = '{';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_RIGHTBRACKET)
-                               {
-                                       lastKeyPressed = '}';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_BACKSLASH)
-                               {
-                                       lastKeyPressed = '|';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_SEMICOLON)
-                               {
-                                       lastKeyPressed = ':';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_QUOTE)
-                               {
-                                       lastKeyPressed = '"';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_COMMA)
-                               {
-                                       lastKeyPressed = '<';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_PERIOD)
-                               {
-                                       lastKeyPressed = '>';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_SLASH)
-                               {
-                                       lastKeyPressed = '?';
-                                       keyDown = true;
-                                       break;
-                               }
-                               else if (event.key.keysym.sym == SDLK_BACKQUOTE)
-                               {
-                                       lastKeyPressed = '~';
-                                       keyDown = true;
-                                       break;
-                               }
+                       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;
                        }
-#endif
 
-                       // General keys...
-                       if (event.key.keysym.sym >= SDLK_SPACE && event.key.keysym.sym <= SDLK_z)
+                       // Stuff the key in if we have a valid one...
+                       if (keyIndex != 0xFF)
                        {
-                               lastKeyPressed = event.key.keysym.sym;
+                               // 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;
 
-                               // Check for Caps Lock key...
-                               if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z && capsLock)
-                                       lastKeyPressed -= 0x20;
+                               keyDownCount++;
+
+                               // Buffer the key held. Note that the last key is always
+                               // stuffed into keysHeld[0].
+                               if (keyDownCount >= 2)
+                               {
+                                       keysHeld[1] = keysHeld[0];
+                                       keysHeldAppleCode[1] = keysHeldAppleCode[0];
+
+                                       if (keyDownCount > 2)
+                                               keyDownCount = 2;
+                               }
 
+                               keysHeld[0] = event.key.keysym.sym;
+                               keysHeldAppleCode[0] = lastKeyPressed;
                                break;
                        }
 
@@ -788,15 +759,14 @@ static void FrameCallback(void)
                                        SpawnMessage("*** RESUME ***");
                                }
                        }
-
-                       // Paddle buttons 0 & 1
-                       if (event.key.keysym.sym == SDLK_LALT)
+                       // Buttons 0 & 1
+                       else if (event.key.keysym.sym == SDLK_LALT)
                                openAppleDown = true;
-                       if (event.key.keysym.sym == SDLK_RALT)
+                       else if (event.key.keysym.sym == SDLK_RALT)
                                closedAppleDown = true;
-
-                       if (event.key.keysym.sym == SDLK_F11)
-                               dumpDis = !dumpDis;                             // Toggle the disassembly process
+                       // Toggle the disassembly process
+                       else if (event.key.keysym.sym == SDLK_F11)
+                               dumpDis = !dumpDis;
 
 /*else if (event.key.keysym.sym == SDLK_F9)
 {
@@ -809,7 +779,7 @@ static void FrameCallback(void)
 //     SpawnMessage("Image swapped...");
 }//*/
 
-                       if (event.key.keysym.sym == SDLK_F2)
+                       else if (event.key.keysym.sym == SDLK_F2)
                                TogglePalette();
                        else if (event.key.keysym.sym == SDLK_F3)
                                CycleScreenTypes();
@@ -841,8 +811,7 @@ static void FrameCallback(void)
                                        fullscreenDebounce = true;
                                }
                        }
-
-                       if (event.key.keysym.sym == SDLK_CAPSLOCK)
+                       else if (event.key.keysym.sym == SDLK_CAPSLOCK)
                        {
                                if (!capsLockDebounce)
                                {
@@ -852,38 +821,75 @@ static void FrameCallback(void)
                        }
 
                        break;
+
                case SDL_KEYUP:
                        if (event.key.keysym.sym == SDLK_F12)
                                fullscreenDebounce = false;
-                       if (event.key.keysym.sym == SDLK_CAPSLOCK)
+                       else if (event.key.keysym.sym == SDLK_CAPSLOCK)
                                capsLockDebounce = false;
-
                        // Paddle buttons 0 & 1
-                       if (event.key.keysym.sym == SDLK_LALT)
+                       else if (event.key.keysym.sym == SDLK_LALT)
                                openAppleDown = false;
-                       if (event.key.keysym.sym == SDLK_RALT)
+                       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--;
+                               }
+                               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);
                        break;
+
                case SDL_WINDOWEVENT:
                        if (event.window.event == SDL_WINDOWEVENT_LEAVE)
                                GUI::MouseMove(0, 0, 0);
 
                        break;
+
                case SDL_QUIT:
                        running = false;
                }
        }
 
+       // Stuff the Apple keyboard buffer, if any keys are pending
+       // N.B.: May have to simulate the key repeat delay too
+       if (keyDownCount > 0)
+       {
+               lastKeyPressed = keysHeldAppleCode[0];
+               keyDown = true;
+       }
+
        // Handle power request from the GUI
        if (powerStateChangeRequested)
        {
@@ -904,8 +910,8 @@ static void FrameCallback(void)
                powerStateChangeRequested = false;
        }
 
-       RenderVideoFrame();                             // Render Apple screen to buffer
-       RenderScreenBuffer();                   // Render buffer to host screen
+       // Render the Apple screen + GUI overlay
+       RenderAppleScreen(sdlRenderer);
        GUI::Render(sdlRenderer);
        SDL_RenderPresent(sdlRenderer);
        SetCallbackTime(FrameCallback, 16666.66666667);
@@ -928,7 +934,7 @@ if (counter == 60)
 // 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.
+// out to 1/60s per frame every 3 frames. [And now it does!]
        frameCount = (frameCount + 1) % 3;
        uint32_t waitFrameTime = 17 - (frameCount == 0 ? 1 : 0);
 
diff --git a/src/applevideo.cpp b/src/applevideo.cpp
deleted file mode 100644 (file)
index 54cbdb1..0000000
+++ /dev/null
@@ -1,923 +0,0 @@
-//
-// Apple 2 video support
-//
-// All the video modes that a real Apple 2 supports are handled here
-//
-// by James Hammons
-// (c) 2005-2017 Underground Software
-//
-// JLH = James Hammons <jlhamm@acm.org>
-//
-// WHO  WHEN        WHAT
-// ---  ----------  -----------------------------------------------------------
-// JLH  12/01/2005  Added color TV/monochrome emulation to hi-res code
-// JLH  12/09/2005  Cleaned up color TV emulation code
-// JLH  12/09/2005  Fixed lo-res color TV/mono emulation modes
-//
-// STILL TO DO:
-//
-// - Fix LoRes mode green mono to skip every other scanline instead of fill
-//   like white mono does [DONE]
-// - Double HiRes [DONE]
-// - 80 column text [DONE]
-// - Fix OSD text display so that it's visible no matter what background is there [DONE]
-//
-
-// Display routines seem MUCH slower now... !!! INVESTIGATE !!! [not anymore]
-
-#include "applevideo.h"
-
-#include <string.h>                                                            // for memset()
-#include <stdio.h>
-#include <stdarg.h>                                                            // for va_* stuff
-#include "apple2.h"
-#include "charset.h"
-#include "video.h"
-#include "gui/font14pt.h"
-#include "gui/gui.h"
-
-/* Reference: Technote tn-iigs-063 "Master Color Values"
-
-          Color  Color Register LR HR  DHR Master Color R,G,B
-          Name       Value      #  #   #      Value
-          ----------------------------------------------------
-          Black       0         0  0,4 0      $0000    (0,0,0)
-(Magenta) Deep Red    1         1      1      $0D03    (D,0,3)
-          Dark Blue   2         2      8      $0009    (0,0,9)
- (Violet) Purple      3         3  2   9      $0D2D    (D,2,D)
-          Dark Green  4         4      4      $0072    (0,7,2)
- (Gray 1) Dark Gray   5         5      5      $0555    (5,5,5)
-   (Blue) Medium Blue 6         6  6   C      $022F    (2,2,F)
-   (Cyan) Light Blue  7         7      D      $06AF    (6,A,F)
-          Brown       8         8      2      $0850    (8,5,0)
-          Orange      9         9  5   3      $0F60    (F,6,0)
- (Gray 2) Light Gray  A         A      A      $0AAA    (A,A,A)
-          Pink        B         B      B      $0F98    (F,9,8)
-  (Green) Light Green C         C  1   6      $01D0    (1,D,0)
-          Yellow      D         D      7      $0FF0    (F,F,0)
-   (Aqua) Aquamarine  E         E      E      $04F9    (4,F,9)
-          White       F         F  3,7 F      $0FFF    (F,F,F)
-
-   LR: Lo-Res   HR: Hi-Res   DHR: Double Hi-Res */
-
-// Global variables
-
-bool flash = false;
-bool textMode = true;
-bool mixedMode = false;
-bool displayPage2 = false;
-bool hiRes = false;
-bool alternateCharset = false;
-bool col80Mode = false;
-
-// Local variables
-
-// We set up the colors this way so that they'll be endian safe
-// when we cast them to a uint32_t. Note that the format is RGBA.
-
-// "Master Color Values" palette
-
-static uint8_t colors[16 * 4] = {
-       0x00, 0x00, 0x00, 0xFF,                         // Black
-       0xDD, 0x00, 0x33, 0xFF,                         // Deep Red (Magenta)
-       0x00, 0x00, 0x99, 0xFF,                         // Dark Blue
-       0xDD, 0x22, 0xDD, 0xFF,                         // Purple (Violet)
-       0x00, 0x77, 0x22, 0xFF,                         // Dark Green
-       0x55, 0x55, 0x55, 0xFF,                         // Dark Gray (Gray 1)
-       0x22, 0x22, 0xFF, 0xFF,                         // Medium Blue (Blue)
-       0x66, 0xAA, 0xFF, 0xFF,                         // Light Blue (Cyan)
-       0x88, 0x55, 0x00, 0xFF,                         // Brown
-       0xFF, 0x66, 0x00, 0xFF,                         // Orange
-       0xAA, 0xAA, 0xAA, 0xFF,                         // Light Gray (Gray 2)
-       0xFF, 0x99, 0x88, 0xFF,                         // Pink
-       0x11, 0xDD, 0x00, 0xFF,                         // Light Green (Green)
-       0xFF, 0xFF, 0x00, 0xFF,                         // Yellow
-       0x44, 0xFF, 0x99, 0xFF,                         // Aquamarine (Aqua)
-       0xFF, 0xFF, 0xFF, 0xFF                          // White
-};
-
-// This palette comes from ApplePC's colors (more realistic to my eye ;-)
-
-static uint8_t altColors[16 * 4] = {
-       0x00, 0x00, 0x00, 0xFF,
-       0x7D, 0x20, 0x41, 0xFF,
-       0x41, 0x30, 0x7D, 0xFF,
-       0xBE, 0x51, 0xBE, 0xFF,
-       0x00, 0x5D, 0x3C, 0xFF,
-       0x7D, 0x7D, 0x7D, 0xFF,
-       0x41, 0x8E, 0xBA, 0xFF,
-       0xBE, 0xAE, 0xFB, 0xFF,
-       0x3C, 0x4D, 0x00, 0xFF,
-       0xBA, 0x6D, 0x41, 0xFF,
-       0x7D, 0x7D, 0x7D, 0xFF,
-       0xFB, 0x9E, 0xBE, 0xFF,
-       0x3C, 0xAA, 0x3C, 0xFF,
-       0xBA, 0xCB, 0x7D, 0xFF,
-       0x7D, 0xDB, 0xBA, 0xFF,
-       0xFB, 0xFB, 0xFB, 0xFF };
-
-// Lo-res starting line addresses
-
-static uint16_t lineAddrLoRes[24] = {
-       0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
-       0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
-       0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 };
-
-// Hi-res starting line addresses
-
-static uint16_t lineAddrHiRes[192] = {
-       0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
-       0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,
-       0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,
-       0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,
-
-       0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,
-       0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,
-       0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,
-       0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,
-
-       0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,
-       0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,
-       0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,
-       0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,
-
-       0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,
-       0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,
-       0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,
-       0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,
-
-       0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,
-       0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,
-       0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,
-       0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,
-
-       0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,
-       0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,
-       0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,
-       0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 };
-
-uint16_t appleHiresToMono[0x200] = {
-       0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
-       0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
-       0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
-       0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
-       0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
-       0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
-       0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
-       0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
-       0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
-       0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
-       0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
-       0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
-       0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
-       0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
-       0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
-       0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
-       0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80,
-       0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x
-       0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98,
-       0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x
-       0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86,
-       0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax
-       0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E,
-       0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx
-       0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81,
-       0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx
-       0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99,
-       0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx
-       0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87,
-       0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex
-       0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F,
-       0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx
-
-       // Second half adds in the previous byte's lo pixel
-
-       0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
-       0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
-       0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
-       0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
-       0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
-       0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
-       0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
-       0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
-       0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
-       0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
-       0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
-       0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
-       0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
-       0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
-       0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
-       0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
-       0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80,
-       0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x
-       0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98,
-       0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x
-       0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86,
-       0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax
-       0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E,
-       0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx
-       0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81,
-       0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx
-       0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99,
-       0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx
-       0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87,
-       0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex
-       0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F,
-       0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF  // $Fx
-};
-
-//static uint8_t blurTable[0x800][8];                          // Color TV blur table
-static uint8_t blurTable[0x80][8];                             // Color TV blur table
-static uint8_t mirrorTable[0x100];
-static uint32_t * palette = (uint32_t *)altColors;
-enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
-static uint8_t screenType = ST_COLOR_TV;
-
-// Local functions
-
-static void Render40ColumnTextLine(uint8_t line);
-static void Render80ColumnTextLine(uint8_t line);
-static void Render40ColumnText(void);
-static void Render80ColumnText(void);
-static void RenderLoRes(uint16_t toLine = 24);
-static void RenderHiRes(uint16_t toLine = 192);
-static void RenderDHiRes(uint16_t toLine = 192);
-
-
-void SetupBlurTable(void)
-{
-       // NOTE: This table only needs to be 7 bits wide instead of 11, since the
-       //       last four bits are copies of the previous four...
-       //       Odd. Doing the bit patterns from 0-$7F doesn't work, but going
-       //       from 0-$7FF stepping by 16 does. Hm.
-       //       Well, it seems that going from 0-$7F doesn't have enough precision to do the job.
-#if 0
-//     for(uint16_t bitPat=0; bitPat<0x800; bitPat++)
-       for(uint16_t bitPat=0; bitPat<0x80; bitPat++)
-       {
-/*             uint16_t w3 = bitPat & 0x888;
-               uint16_t w2 = bitPat & 0x444;
-               uint16_t w1 = bitPat & 0x222;
-               uint16_t w0 = bitPat & 0x111;*/
-               uint16_t w3 = bitPat & 0x88;
-               uint16_t w2 = bitPat & 0x44;
-               uint16_t w1 = bitPat & 0x22;
-               uint16_t w0 = bitPat & 0x11;
-
-               uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
-               uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
-               uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
-               uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
-
-               for(int8_t i=7; i>=0; i--)
-               {
-                       uint8_t color = (((blurred0 >> i) & 0x01) << 3)
-                               | (((blurred1 >> i) & 0x01) << 2)
-                               | (((blurred2 >> i) & 0x01) << 1)
-                               | ((blurred3 >> i) & 0x01);
-                       blurTable[bitPat][7 - i] = color;
-               }
-       }
-#else
-       for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
-       {
-               uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
-
-               uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
-               uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
-               uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
-               uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
-
-               for(int8_t i=7; i>=0; i--)
-               {
-                       uint8_t color = (((blurred0 >> i) & 0x01) << 3)
-                               | (((blurred1 >> i) & 0x01) << 2)
-                               | (((blurred2 >> i) & 0x01) << 1)
-                               | ((blurred3 >> i) & 0x01);
-                       blurTable[bitPat >> 4][7 - i] = color;
-               }
-       }
-#endif
-
-       for(int i=0; i<256; i++)
-       {
-               mirrorTable[i] = ((i & 0x01) << 7)
-                       | ((i & 0x02) << 5)
-                       | ((i & 0x04) << 3)
-                       | ((i & 0x08) << 1)
-                       | ((i & 0x10) >> 1)
-                       | ((i & 0x20) >> 3)
-                       | ((i & 0x40) >> 5)
-                       | ((i & 0x80) >> 7);
-       }
-}
-
-
-void TogglePalette(void)
-{
-       if (palette == (uint32_t *)colors)
-       {
-               palette = (uint32_t *)altColors;
-               SpawnMessage("Color TV palette");
-       }
-       else
-       {
-               palette = (uint32_t *)colors;
-               SpawnMessage("\"Master Color Values\" palette");
-       }
-}
-
-
-void CycleScreenTypes(void)
-{
-       char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
-
-       screenType++;
-
-       if (screenType == ST_LAST_ENTRY)
-               screenType = ST_FIRST_ENTRY;
-
-       SpawnMessage("%s", scrTypeStr[screenType]);
-}
-
-
-static uint32_t msgTicks = 0;
-static char message[4096];
-
-void SpawnMessage(const char * text, ...)
-{
-       va_list arg;
-
-       va_start(arg, text);
-       vsprintf(message, text, arg);
-       va_end(arg);
-
-       msgTicks = 120;
-}
-
-
-static void DrawString2(uint32_t x, uint32_t y, uint32_t color);
-static void DrawString(void)
-{
-//This approach works, and seems to be fast enough... Though it probably would
-//be better to make the oversized font to match this one...
-       for(uint32_t x=7; x<=9; x++)
-               for(uint32_t y=7; y<=9; y++)
-                       DrawString2(x, y, 0x00000000);
-
-       DrawString2(8, 8, 0x0020FF20);
-}
-
-
-static void DrawString2(uint32_t x, uint32_t y, uint32_t color)
-{
-//uint32_t x = 8, y = 8;
-       uint32_t length = strlen(message), address = x + (y * VIRTUAL_SCREEN_WIDTH);
-//     uint32_t color = 0x0020FF20;
-//This could be done ahead of time, instead of on each pixel...
-//(Now it is!)
-       uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
-
-       for(uint32_t i=0; i<length; i++)
-       {
-               uint8_t c = message[i];
-               c = (c < 32 ? 0 : c - 32);
-               uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
-
-               for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
-               {
-                       for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
-                       {
-/*                             uint8_t fontTrans = font1[fontAddr++];
-//                             uint32_t newTrans = (fontTrans * transparency / 255) << 24;
-                               uint32_t newTrans = fontTrans << 24;
-                               uint32_t pixel = newTrans | color;
-
-                               *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = pixel;//*/
-
-//                             uint8_t trans = font1[fontAddr++];
-                               uint8_t trans = font2[fontAddr++];
-
-                               if (trans)
-                               {
-                                       uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
-
-                                       uint8_t eBlue = (existingColor >> 16) & 0xFF,
-                                               eGreen = (existingColor >> 8) & 0xFF,
-                                               eRed = existingColor & 0xFF;
-
-//This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
-//Here we've modified it to have 33 levels of transparency (could have any # we want!)
-//because dividing by 32 is faster than dividing by 31...!
-                                       uint8_t invTrans = 255 - trans;
-
-                                       uint32_t bRed   = (eRed   * invTrans + nRed   * trans) / 255;
-                                       uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
-                                       uint32_t bBlue  = (eBlue  * invTrans + nBlue  * trans) / 255;
-
-//THIS IS NOT ENDIAN SAFE
-//NB: Setting the alpha channel here does nothing.
-                                       *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
-                               }
-                       }
-               }
-
-               address += FONT_WIDTH;
-       }
-}
-
-
-static void Render40ColumnTextLine(uint8_t line)
-{
-       uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
-
-       for(int x=0; x<40; x++)
-       {
-               uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
-
-               // Render character at (x, y)
-
-               for(int cy=0; cy<8; cy++)
-               {
-                       for(int cx=0; cx<7; cx++)
-                       {
-                               uint32_t pixel = 0xFF000000;
-
-                               if (alternateCharset)
-                               {
-                                       if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
-                                               pixel = pixelOn;
-
-                                       if (chr < 0x80)
-                                               pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
-
-                                       if ((chr & 0xC0) == 0x40 && flash)
-                                               pixel = 0xFF000000;
-                               }
-                               else
-                               {
-                                       if (textChar2e[(chr * 56) + cx + (cy * 7)])
-                                               pixel = pixelOn;
-                               }
-
-                               scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
-                               scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
-
-                               // QnD method to get blank alternate lines in text mode
-                               if (screenType == ST_GREEN_MONO)
-                                       pixel = 0xFF000000;
-
-                               {
-                                       scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
-                                       scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
-                               }
-                       }
-               }
-       }
-}
-
-
-static void Render80ColumnTextLine(uint8_t line)
-{
-       uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
-
-       for(int x=0; x<80; x++)
-       {
-#if 0
-// This is wrong; it should grab from the alt bank if Page2 is set, not main RAM @ $0
-               uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
-
-               if (x > 39)
-                       chr = ram2[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x - 40];
-#else
-               uint8_t chr;
-
-               if (x & 0x01)
-                       chr = ram[lineAddrLoRes[line] + (x >> 1)];
-               else
-                       chr = ram2[lineAddrLoRes[line] + (x >> 1)];
-#endif
-
-               // Render character at (x, y)
-
-               for(int cy=0; cy<8; cy++)
-               {
-                       for(int cx=0; cx<7; cx++)
-                       {
-                               uint32_t pixel = 0xFF000000;
-
-                               if (alternateCharset)
-                               {
-                                       if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
-                                               pixel = pixelOn;
-
-                                       if (chr < 0x80)
-                                               pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
-
-                                       if ((chr & 0xC0) == 0x40 && flash)
-                                               pixel = 0xFF000000;
-                               }
-                               else
-                               {
-                                       if (textChar2e[(chr * 56) + cx + (cy * 7)])
-                                               pixel = pixelOn;
-                               }
-
-                               scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
-
-                               // QnD method to get blank alternate lines in text mode
-                               if (screenType == ST_GREEN_MONO)
-                                       pixel = 0xFF000000;
-
-                               scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
-                       }
-               }
-       }
-}
-
-
-static void Render40ColumnText(void)
-{
-       for(uint8_t line=0; line<24; line++)
-               Render40ColumnTextLine(line);
-}
-
-
-static void Render80ColumnText(void)
-{
-       for(uint8_t line=0; line<24; line++)
-               Render80ColumnTextLine(line);
-}
-
-
-static void RenderLoRes(uint16_t toLine/*= 24*/)
-{
-// NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
-//       Also, we could set up three different Render functions depending on which
-//       render type was set and call it with a function pointer. Would be faster
-//       then the nested ifs we have now.
-/*
-Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
-Color #s correspond to the bit patterns in reverse... Interesting!
-
-00 00 00 ->  0 [0000] -> 0 (lores color #)
-3c 4d 00 ->  8 [0001] -> 8?            BROWN
-00 5d 3c ->  4 [0010] -> 4?            DARK GREEN
-3c aa 3c -> 12 [0011] -> 12?   LIGHT GREEN (GREEN)
-41 30 7d ->  2 [0100] -> 2?            DARK BLUE
-7d 7d 7d -> 10 [0101] -> 10?   LIGHT GRAY
-41 8e ba ->  6 [0110] -> 6?            MEDIUM BLUE (BLUE)
-7d db ba -> 14 [0111] -> 14?   AQUAMARINE (AQUA)
-7d 20 41 ->  1 [1000] -> 1?            DEEP RED (MAGENTA)
-ba 6d 41 ->  9 [1001] -> 9?            ORANGE
-7d 7d 7d ->  5 [1010] -> 5?            DARK GRAY
-ba cb 7d -> 13 [1011] -> 13?   YELLOW
-be 51 be ->  3 [1100] -> 3             PURPLE (VIOLET)
-fb 9e be -> 11 [1101] -> 11?   PINK
-be ae fb ->  7 [1110] -> 7?            LIGHT BLUE (CYAN)
-fb fb fb -> 15 [1111] -> 15            WHITE
-*/
-       uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
-
-//This is the old "perfect monitor" rendering code...
-/*     if (screenType != ST_COLOR_TV) // Not correct, but for now...
-//if (1)
-       {
-               for(uint16_t y=0; y<toLine; y++)
-               {
-                       for(uint16_t x=0; x<40; x++)
-                       {
-                               uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
-                               uint32_t pixel = palette[scrByte & 0x0F];
-
-                               for(int cy=0; cy<4; cy++)
-                                       for(int cx=0; cx<14; cx++)
-                                               scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
-
-                               pixel = palette[scrByte >> 4];
-
-                               for(int cy=4; cy<8; cy++)
-                                       for(int cx=0; cx<14; cx++)
-                                               scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
-                       }
-               }
-       }
-       else//*/
-
-       uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
-
-       for(uint16_t y=0; y<toLine; y++)
-       {
-               // Do top half of lores screen bytes...
-
-               uint32_t previous3Bits = 0;
-
-               for(uint16_t x=0; x<40; x+=2)
-               {
-                       uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
-                       uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
-                       scrByte1 = mirrorNybble[scrByte1];
-                       scrByte2 = mirrorNybble[scrByte2];
-                       // This is just a guess, but it'll have to do for now...
-                       uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
-                               | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
-                               | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
-
-                       // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
-                       // 0ppp 1111 1111 1111 11|11 1111 1111 1111
-                       // 31   27   23   19   15    11   7    3  0
-
-                       if (screenType == ST_COLOR_TV)
-                       {
-                               for(uint8_t i=0; i<7; i++)
-                               {
-                                       uint8_t bitPat = (pixels & 0x7F000000) >> 24;
-                                       pixels <<= 4;
-
-                                       for(uint8_t j=0; j<4; j++)
-                                       {
-                                               uint8_t color = blurTable[bitPat][j];
-
-                                               for(uint32_t cy=0; cy<8; cy++)
-                                               {
-                                                       scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
-//                                                     scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
-                                               }
-                                       }
-                               }
-
-                               previous3Bits = pixels & 0x70000000;
-                       }
-                       else
-                       {
-                               for(int j=0; j<28; j++)
-                               {
-                                       for(uint32_t cy=0; cy<8; cy++)
-                                       {
-                                               scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
-//                                             scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
-                                       }
-
-                                       pixels <<= 1;
-                               }
-                       }
-               }
-
-               // Now do bottom half...
-
-               previous3Bits = 0;
-
-               for(uint16_t x=0; x<40; x+=2)
-               {
-                       uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
-                       uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
-                       scrByte1 = mirrorNybble[scrByte1];
-                       scrByte2 = mirrorNybble[scrByte2];
-                       // This is just a guess, but it'll have to do for now...
-                       uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
-                               | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
-                               | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
-
-                       // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
-                       // 0ppp 1111 1111 1111 11|11 1111 1111 1111
-                       // 31   27   23   19   15    11   7    3  0
-
-                       if (screenType == ST_COLOR_TV)
-                       {
-                               for(uint8_t i=0; i<7; i++)
-                               {
-                                       uint8_t bitPat = (pixels & 0x7F000000) >> 24;
-                                       pixels <<= 4;
-
-                                       for(uint8_t j=0; j<4; j++)
-                                       {
-                                               uint8_t color = blurTable[bitPat][j];
-
-                                               for(uint32_t cy=8; cy<16; cy++)
-                                               {
-                                                       scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
-//                                                     scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
-                                               }
-                                       }
-                               }
-
-                               previous3Bits = pixels & 0x70000000;
-                       }
-                       else
-                       {
-                               for(int j=0; j<28; j++)
-                               {
-                                       for(uint32_t cy=8; cy<16; cy++)
-                                       {
-                                               scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
-//                                             scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
-                                       }
-
-                                       pixels <<= 1;
-                               }
-                       }
-               }
-       }
-}
-
-
-static void RenderHiRes(uint16_t toLine/*= 192*/)
-{
-//printf("RenderHiRes to line %u\n", toLine);
-// NOTE: Not endian safe. !!! FIX !!! [DONE]
-#if 0
-       uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
-#else
-// Now it is. Now roll this fix into all the other places... !!! FIX !!!
-// The colors are set in the 8-bit array as R G B A
-       uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
-       uint32_t * colorPtr = (uint32_t *)monoColors;
-       uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
-#endif
-
-       for(uint16_t y=0; y<toLine; y++)
-       {
-               uint16_t previousLoPixel = 0;
-               uint32_t previous3bits = 0;
-
-               for(uint16_t x=0; x<40; x+=2)
-               {
-                       uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
-                       uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
-                       previousLoPixel = (screenByte << 2) & 0x0100;
-
-                       screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
-                       uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
-                       previousLoPixel = (screenByte << 2) & 0x0100;
-
-                       pixels = previous3bits | (pixels << 14) | pixels2;
-
-//testing (this shows on the screen, so it's OK)
-//if (x == 0)
-//{
-//     pixels = 0x7FFFFFFF;
-//}
-
-                       // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
-                       // 0ppp 1111 1111 1111 1111 1111 1111 1111
-                       // 31   27   23   19   15   11   7    3  0
-
-                       if (screenType == ST_COLOR_TV)
-                       {
-                               for(uint8_t i=0; i<7; i++)
-                               {
-                                       uint8_t bitPat = (pixels & 0x7F000000) >> 24;
-                                       pixels <<= 4;
-
-                                       for(uint8_t j=0; j<4; j++)
-                                       {
-                                               uint8_t color = blurTable[bitPat][j];
-#if 0
-//This doesn't seem to make things go any faster...
-//It's the OpenGL render that's faster... Hmm...
-                                               scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
-#else
-                                               scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
-                                               scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
-#endif
-                                       }
-                               }
-
-                               previous3bits = pixels & 0x70000000;
-                       }
-                       else
-                       {
-                               for(int j=0; j<28; j++)
-                               {
-                                       scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
-
-                                       if (screenType == ST_GREEN_MONO)
-                                               pixels &= 0x07FFFFFF;
-
-                                       scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
-                                       pixels <<= 1;
-                               }
-                       }
-               }
-       }
-}
-
-
-static void RenderDHiRes(uint16_t toLine/*= 192*/)
-{
-// Now it is. Now roll this fix into all the other places... !!! FIX !!!
-// The colors are set in the 8-bit array as R G B A
-       uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
-       uint32_t * colorPtr = (uint32_t *)monoColors;
-       uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
-
-       for(uint16_t y=0; y<toLine; y++)
-       {
-               uint32_t previous4bits = 0;
-
-               for(uint16_t x=0; x<40; x+=2)
-               {
-                       uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
-                       uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
-                       screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
-                       pixels = pixels | (mirrorTable[screenByte & 0x7F]);
-                       screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
-                       pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
-                       screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
-                       pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
-                       pixels = previous4bits | (pixels >> 1);
-
-                       // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
-                       // 0ppp 1111 1111 1111 1111 1111 1111 1111
-                       // 31   27   23   19   15   11   7    3  0
-
-                       if (screenType == ST_COLOR_TV)
-                       {
-                               for(uint8_t i=0; i<7; i++)
-                               {
-                                       uint8_t bitPat = (pixels & 0xFE000000) >> 25;
-                                       pixels <<= 4;
-
-                                       for(uint8_t j=0; j<4; j++)
-                                       {
-                                               uint32_t color = palette[blurTable[bitPat][j]];
-                                               scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
-                                               scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
-                                       }
-                               }
-
-                               previous4bits = pixels & 0xF0000000;
-                       }
-                       else
-                       {
-                               for(int j=0; j<28; j++)
-                               {
-                                       scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
-
-                                       if (screenType == ST_GREEN_MONO)
-                                               pixels &= 0x07FFFFFF;
-
-                                       scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
-                                       pixels <<= 1;
-                               }
-                       }
-               }
-       }
-}
-
-
-void RenderVideoFrame(void)
-{
-       if (GUI::powerOnState == true)
-       {
-               if (textMode)
-               {
-                       if (!col80Mode)
-                               Render40ColumnText();
-                       else
-                               Render80ColumnText();
-               }
-               else
-               {
-                       if (mixedMode)
-                       {
-                               if (hiRes)
-                               {
-                                       RenderHiRes(160);
-                                       Render40ColumnTextLine(20);
-                                       Render40ColumnTextLine(21);
-                                       Render40ColumnTextLine(22);
-                                       Render40ColumnTextLine(23);
-                               }
-                               else
-                               {
-                                       RenderLoRes(20);
-                                       Render40ColumnTextLine(20);
-                                       Render40ColumnTextLine(21);
-                                       Render40ColumnTextLine(22);
-                                       Render40ColumnTextLine(23);
-                               }
-                       }
-                       else
-                       {
-                               if (dhires)
-                                       RenderDHiRes();
-                               else if (hiRes)
-                                       RenderHiRes();
-                               else
-                                       RenderLoRes();
-                       }
-               }
-       }
-       else
-       {
-               memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
-       }
-
-       if (msgTicks)
-       {
-               DrawString();
-               msgTicks--;
-       }
-}
-
diff --git a/src/applevideo.h b/src/applevideo.h
deleted file mode 100644 (file)
index e0707ac..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-// Apple 2 video support
-//
-
-#ifndef __APPLEVIDEO_H__
-#define __APPLEVIDEO_H__
-
-// Global variables (exported)
-
-extern bool flash;
-extern bool textMode;
-extern bool mixedMode;
-extern bool displayPage2;
-extern bool hiRes;
-extern bool alternateCharset;
-extern bool col80Mode;
-
-// Functions (exported)
-
-void SetupBlurTable(void);
-void TogglePalette(void);
-void CycleScreenTypes(void);
-void SpawnMessage(const char * text, ...);
-void RenderVideoFrame(void);
-
-#endif // __APPLEVIDEO_H__
index 484163040baff55550070058c435065176d2ce3a..58697812e144572ec4c039d27445d6a098c2bd5a 100644 (file)
@@ -6,10 +6,11 @@
 //
 
 #include "dis65c02.h"
+
 #include <stdio.h>
 #include <string.h>
-#include "v65c02.h"
 #include "log.h"
+#include "v65c02.h"
 
 
 // External shit
@@ -79,7 +80,6 @@ static uint8_t mnemonics[256][5] = {
 static void DisplayBytes(char * outbuf, uint16_t src, uint32_t dst)
 {
        char buf[32];
-//     WriteLog("%04X: ", src);
        sprintf(outbuf, "%04X: ", src);
        uint8_t cnt = 0;
 
@@ -89,7 +89,6 @@ static void DisplayBytes(char * outbuf, uint16_t src, uint32_t dst)
 
        for(uint32_t i=src; i<dst; i++)
        {
-//             WriteLog("%02X ", mainCPU.RdMem(i));
                sprintf(buf, "%02X ", mainCPU.RdMem(i));
                strcat(outbuf, buf);
                cnt++;
@@ -97,7 +96,6 @@ static void DisplayBytes(char * outbuf, uint16_t src, uint32_t dst)
 
        // Pad the leftover spaces...
        for(int i=cnt; i<3; i++)
-//             WriteLog("   ");
        {
                sprintf(buf, "   ");
                strcat(outbuf, buf);
@@ -114,71 +112,70 @@ int Decode65C02(char * outbuf, uint16_t pc)
 
        uint16_t addr = pc;
        uint16_t w;
-       uint8_t opcode = mainCPU.RdMem(addr++);                         // Get the opcode
+       uint8_t opcode = mainCPU.RdMem(addr++); // Get the opcode
 
-       switch (op_mat[opcode])                                                         // Decode the addressing mode...
+       switch (op_mat[opcode])                                 // Decode the addressing mode...
        {
-       case 0:                                                                                         // Illegal
+       case 0:                                                                 // Illegal
                sprintf(buf, "???");
                break;
-       case 1:                                                                                         // Immediate
+       case 1:                                                                 // Immediate
                sprintf(buf, "%s #$%02X", mnemonics[opcode], mainCPU.RdMem(addr++));
                break;
-       case 2:                                                                                         // Zero page
+       case 2:                                                                 // Zero page
                sprintf(buf, "%s $%02X", mnemonics[opcode], mainCPU.RdMem(addr++));
                break;
-       case 3:                                                                                         // Zero page, X
+       case 3:                                                                 // Zero page, X
                sprintf(buf, "%s $%02X,X", mnemonics[opcode], mainCPU.RdMem(addr++));
                break;
-       case 4:                                                                                         // Zero page, Y
+       case 4:                                                                 // Zero page, Y
                sprintf(buf, "%s $%02X,Y", mnemonics[opcode], mainCPU.RdMem(addr++));
                break;
-       case 5:                                                                                         // Zero page indirect
+       case 5:                                                                 // Zero page indirect
                sprintf(buf, "%s ($%02X)", mnemonics[opcode], mainCPU.RdMem(addr++));
                break;
-       case 6:                                                                                         // Zero page, X indirect
+       case 6:                                                                 // Zero page, X indirect
                sprintf(buf, "%s ($%02X,X)", mnemonics[opcode], mainCPU.RdMem(addr++));
                break;
-       case 7:                                                                                         // Zero page, Y indirect
+       case 7:                                                                 // Zero page, Y indirect
                sprintf(buf, "%s ($%02X),Y", mnemonics[opcode], mainCPU.RdMem(addr++));
                break;
-       case 8:                                                                                         // Absolute
+       case 8:                                                                 // Absolute
                w = mainCPU.RdMem(addr++);
                w |= mainCPU.RdMem(addr++) << 8;
                sprintf(buf, "%s $%04X", mnemonics[opcode], w);
                break;
-       case 9:                                                                                         // Absolute, X
+       case 9:                                                                 // Absolute, X
                w = mainCPU.RdMem(addr++);
                w |= mainCPU.RdMem(addr++) << 8;
                sprintf(buf, "%s $%04X,X", mnemonics[opcode], w);
                break;
-       case 10:                                                                                        // Absolute, Y
+       case 10:                                                                // Absolute, Y
                w = mainCPU.RdMem(addr++);
                w |= mainCPU.RdMem(addr++) << 8;
                sprintf(buf, "%s $%04X,Y", mnemonics[opcode], w);
                break;
-       case 11:                                                                                        // Indirect
+       case 11:                                                                // Indirect
                w = mainCPU.RdMem(addr++);
                w |= mainCPU.RdMem(addr++) << 8;
                sprintf(buf, "%s ($%04X)", mnemonics[opcode], w);
                break;
-       case 12:                                                                                        // Indirect, X
+       case 12:                                                                // Indirect, X
                w = mainCPU.RdMem(addr++);
                w |= mainCPU.RdMem(addr++) << 8;
                sprintf(buf, "%s ($%04X,X)", mnemonics[opcode], w);
                break;
-       case 13:                                                                                        // Relative
+       case 13:                                                                // Relative
                sprintf(buf, "%s $%04X", mnemonics[opcode], addr + (int16_t)((int8_t)mainCPU.RdMem(addr)) + 1);
                addr++;
                break;
-       case 14:                                                                                        // Inherent
+       case 14:                                                                // Inherent
                sprintf(buf, "%s ", mnemonics[opcode]);
                break;
        }
 
-       DisplayBytes(buf2, pc, addr);                                           // Show bytes
-//     WriteLog("%-16s", outbuf);                                                      // Display opcode & addressing, etc.
-       sprintf(outbuf, "%s %-14s", buf2, buf);                         // Display opcode & addressing, etc.
+       DisplayBytes(buf2, pc, addr);                   // Show bytes
+       sprintf(outbuf, "%s %-14s", buf2, buf); // Display opcode & addressing, etc.
 
        return addr - pc;
 }
index 335dbdbb1198ff71a3ca7fd94237a6c78c142671..859db6d7faf954d64e961ca62622b7bef993fec7 100644 (file)
@@ -19,7 +19,8 @@
 #include <string.h>
 #include "apple2.h"
 #include "log.h"
-#include "applevideo.h"                                        // For message spawning... Though there's probably a better approach than this!
+#include "video.h"             // For message spawning... Though there's probably a
+                                               // better approach than this!
 
 // Useful enums
 
index c605b1359cca62ea1e0a80836f2c877d0730c191..dfdbd572d37098eae16bce72a49c2c28cb6c235f 100644 (file)
@@ -101,7 +101,7 @@ void DiskSelector::Init(SDL_Renderer * renderer)
                WriteLog("GUI (DiskSelector): Could not set blend mode for charStamp.\n");
 
        for(uint32_t i=0; i<DS_WIDTH*DS_HEIGHT; i++)
-               windowPixels[i] = 0xEF00FF00;
+               windowPixels[i] = 0xEF007F00;
 
        SDL_UpdateTexture(window, NULL, windowPixels, 128 * sizeof(Uint32));
        FindDisks(NULL);
index e79372bfc27246f24ced138498e45d390791922c..3f29a204e2fa19a2256a5ef5bbb625581597de27 100644 (file)
@@ -22,7 +22,6 @@
 
 #include "gui.h"
 #include "apple2.h"
-#include "applevideo.h"
 #include "diskselector.h"
 #include "log.h"
 #include "video.h"
index 29e3236e58f1e0ad98f743c14985b1acbc06978c..5019b9de05b7169f0b004f861d44cac700170ea5 100644 (file)
 
 #include "mmu.h"
 #include "apple2.h"
-#include "applevideo.h"
 #include "firmware.h"
 #include "log.h"
 #include "sound.h"
+#include "video.h"
 
 
 // Debug defines
@@ -451,7 +451,9 @@ void SwitchALTCHARSET(uint16_t address, uint8_t)
 
 uint8_t ReadKeyStrobe(uint16_t)
 {
-       uint8_t byte = lastKeyPressed | ((uint8_t)keyDown << 7);
+// No character data is read from here, just the 'any key was pressed' signal...
+//     uint8_t byte = lastKeyPressed | ((uint8_t)keyDown << 7);
+       uint8_t byte = (uint8_t)keyDown << 7;
        keyDown = false;
        return byte;
 }
index 87d17de6a53379fb345768dd5d83a8d4ba449390..11e3736885fa506e9b5c6be6cc9decdc0a732232 100644 (file)
 //
-// VIDEO.CPP: SDL2/local hardware specific video routines
+// Apple 2 video support
+//
+// All the video modes that a real Apple 2 supports are handled here
 //
 // by James Hammons
+// (c) 2005-2017 Underground Software
 //
 // JLH = James Hammons <jlhamm@acm.org>
 //
 // WHO  WHEN        WHAT
 // ---  ----------  -----------------------------------------------------------
-// JLH  01/04/2006  Added changelog ;-)
-// JLH  01/20/2006  Cut out unnecessary buffering
+// JLH  12/01/2005  Added color TV/monochrome emulation to hi-res code
+// JLH  12/09/2005  Cleaned up color TV emulation code
+// JLH  12/09/2005  Fixed lo-res color TV/mono emulation modes
+//
+// STILL TO DO:
 //
+// - Fix LoRes mode green mono to skip every other scanline instead of fill
+//   like white mono does [DONE]
+// - Double HiRes [DONE]
+// - 80 column text [DONE]
+// - Fix OSD text display so that it's visible no matter what background is there [DONE]
+//
+
+// Display routines seem MUCH slower now... !!! INVESTIGATE !!! [not anymore]
 
 #include "video.h"
-#include <string.h>    // (for memset, etc... Lazy!)
-#include <malloc.h>
+
+#include <string.h>                                    // for memset()
+#include <stdio.h>
+#include <stdarg.h>                                    // for va_* stuff
+#include "apple2.h"
 #include "apple2-icon-64x64.h"
+#include "charset.h"
 #include "log.h"
 #include "settings.h"
+#include "gui/font14pt.h"
+#include "gui/gui.h"
+
+/* Reference: Technote tn-iigs-063 "Master Color Values"
+
+          Color  Color Register LR HR  DHR Master Color R,G,B
+          Name       Value      #  #   #      Value
+          ----------------------------------------------------
+          Black       0         0  0,4 0      $0000    (0,0,0)
+(Magenta) Deep Red    1         1      1      $0D03    (D,0,3)
+          Dark Blue   2         2      8      $0009    (0,0,9)
+ (Violet) Purple      3         3  2   9      $0D2D    (D,2,D)
+          Dark Green  4         4      4      $0072    (0,7,2)
+ (Gray 1) Dark Gray   5         5      5      $0555    (5,5,5)
+   (Blue) Medium Blue 6         6  6   C      $022F    (2,2,F)
+   (Cyan) Light Blue  7         7      D      $06AF    (6,A,F)
+          Brown       8         8      2      $0850    (8,5,0)
+          Orange      9         9  5   3      $0F60    (F,6,0)
+ (Gray 2) Light Gray  A         A      A      $0AAA    (A,A,A)
+          Pink        B         B      B      $0F98    (F,9,8)
+  (Green) Light Green C         C  1   6      $01D0    (1,D,0)
+          Yellow      D         D      7      $0FF0    (F,F,0)
+   (Aqua) Aquamarine  E         E      E      $04F9    (4,F,9)
+          White       F         F  3,7 F      $0FFF    (F,F,F)
 
+   LR: Lo-Res   HR: Hi-Res   DHR: Double Hi-Res
+
+   N.B.: These colors look like shit */
+
+// Global variables
+
+bool flash = false;
+bool textMode = true;
+bool mixedMode = false;
+bool displayPage2 = false;
+bool hiRes = false;
+bool alternateCharset = false;
+bool col80Mode = false;
+SDL_Renderer * sdlRenderer = NULL;
+
+// Local variables
 
-// Local vars
 static SDL_Window * sdlWindow = NULL;
 static SDL_Texture * sdlTexture = NULL;
+static uint32_t * scrBuffer;
+static int scrPitch;
 
-// Exported vars
-SDL_Renderer * sdlRenderer = NULL;
-uint32_t scrBuffer[VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t)];
+// We set up the colors this way so that they'll be endian safe
+// when we cast them to a uint32_t. Note that the format is RGBA.
+
+// "Master Color Values" palette
+
+static uint8_t colors[16 * 4] = {
+       0x00, 0x00, 0x00, 0xFF,                         // Black
+       0xDD, 0x00, 0x33, 0xFF,                         // Deep Red (Magenta)
+       0x00, 0x00, 0x99, 0xFF,                         // Dark Blue
+       0xDD, 0x22, 0xDD, 0xFF,                         // Purple (Violet)
+       0x00, 0x77, 0x22, 0xFF,                         // Dark Green
+       0x55, 0x55, 0x55, 0xFF,                         // Dark Gray (Gray 1)
+       0x22, 0x22, 0xFF, 0xFF,                         // Medium Blue (Blue)
+       0x66, 0xAA, 0xFF, 0xFF,                         // Light Blue (Cyan)
+       0x88, 0x55, 0x00, 0xFF,                         // Brown
+       0xFF, 0x66, 0x00, 0xFF,                         // Orange
+       0xAA, 0xAA, 0xAA, 0xFF,                         // Light Gray (Gray 2)
+       0xFF, 0x99, 0x88, 0xFF,                         // Pink
+       0x11, 0xDD, 0x00, 0xFF,                         // Light Green (Green)
+       0xFF, 0xFF, 0x00, 0xFF,                         // Yellow
+       0x44, 0xFF, 0x99, 0xFF,                         // Aquamarine (Aqua)
+       0xFF, 0xFF, 0xFF, 0xFF                          // White
+};
+
+// This palette comes from ApplePC's colors (more realistic to my eye ;-)
+
+static uint8_t altColors[16 * 4] = {
+       0x00, 0x00, 0x00, 0xFF,
+       0x7D, 0x20, 0x41, 0xFF,
+       0x41, 0x30, 0x7D, 0xFF,
+       0xBE, 0x51, 0xBE, 0xFF,
+       0x00, 0x5D, 0x3C, 0xFF,
+       0x7D, 0x7D, 0x7D, 0xFF,
+       0x41, 0x8E, 0xBA, 0xFF,
+       0xBE, 0xAE, 0xFB, 0xFF,
+       0x3C, 0x4D, 0x00, 0xFF,
+       0xBA, 0x6D, 0x41, 0xFF,
+       0x7D, 0x7D, 0x7D, 0xFF,
+       0xFB, 0x9E, 0xBE, 0xFF,
+       0x3C, 0xAA, 0x3C, 0xFF,
+       0xBA, 0xCB, 0x7D, 0xFF,
+       0x7D, 0xDB, 0xBA, 0xFF,
+       0xFB, 0xFB, 0xFB, 0xFF };
+
+// Lo-res starting line addresses
+
+static uint16_t lineAddrLoRes[24] = {
+       0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
+       0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
+       0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 };
+
+// Hi-res starting line addresses
+
+static uint16_t lineAddrHiRes[192] = {
+       0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
+       0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,
+       0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,
+       0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,
+
+       0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,
+       0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,
+       0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,
+       0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,
+
+       0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,
+       0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,
+       0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,
+       0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,
+
+       0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,
+       0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,
+       0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,
+       0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,
+
+       0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,
+       0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,
+       0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,
+       0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,
+
+       0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,
+       0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,
+       0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,
+       0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 };
+
+uint16_t appleHiresToMono[0x200] = {
+       0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
+       0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
+       0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
+       0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
+       0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
+       0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
+       0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
+       0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
+       0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
+       0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
+       0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
+       0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
+       0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
+       0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
+       0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
+       0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
+       0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80,
+       0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x
+       0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98,
+       0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x
+       0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86,
+       0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax
+       0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E,
+       0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx
+       0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81,
+       0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx
+       0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99,
+       0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx
+       0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87,
+       0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex
+       0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F,
+       0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx
+
+       // Second half adds in the previous byte's lo pixel
+
+       0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
+       0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
+       0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
+       0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
+       0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
+       0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
+       0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
+       0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
+       0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
+       0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
+       0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
+       0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
+       0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
+       0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
+       0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
+       0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
+       0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80,
+       0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x
+       0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98,
+       0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x
+       0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86,
+       0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax
+       0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E,
+       0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx
+       0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81,
+       0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx
+       0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99,
+       0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx
+       0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87,
+       0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex
+       0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F,
+       0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF  // $Fx
+};
+
+//static uint8_t blurTable[0x800][8];                          // Color TV blur table
+static uint8_t blurTable[0x80][8];                             // Color TV blur table
+static uint8_t mirrorTable[0x100];
+static uint32_t * palette = (uint32_t *)altColors;
+enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
+static uint8_t screenType = ST_COLOR_TV;
+
+// Local functions
+
+static void Render40ColumnTextLine(uint8_t line);
+static void Render80ColumnTextLine(uint8_t line);
+static void Render40ColumnText(void);
+static void Render80ColumnText(void);
+static void RenderLoRes(uint16_t toLine = 24);
+static void RenderHiRes(uint16_t toLine = 192);
+static void RenderDHiRes(uint16_t toLine = 192);
+static void RenderVideoFrame(/*uint32_t *, int*/);
+
+
+void SetupBlurTable(void)
+{
+       // NOTE: This table only needs to be 7 bits wide instead of 11, since the
+       //       last four bits are copies of the previous four...
+       //       Odd. Doing the bit patterns from 0-$7F doesn't work, but going
+       //       from 0-$7FF stepping by 16 does. Hm.
+       //       Well, it seems that going from 0-$7F doesn't have enough precision to do the job.
+#if 0
+//     for(uint16_t bitPat=0; bitPat<0x800; bitPat++)
+       for(uint16_t bitPat=0; bitPat<0x80; bitPat++)
+       {
+/*             uint16_t w3 = bitPat & 0x888;
+               uint16_t w2 = bitPat & 0x444;
+               uint16_t w1 = bitPat & 0x222;
+               uint16_t w0 = bitPat & 0x111;*/
+               uint16_t w3 = bitPat & 0x88;
+               uint16_t w2 = bitPat & 0x44;
+               uint16_t w1 = bitPat & 0x22;
+               uint16_t w0 = bitPat & 0x11;
+
+               uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
+               uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
+               uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
+               uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
+
+               for(int8_t i=7; i>=0; i--)
+               {
+                       uint8_t color = (((blurred0 >> i) & 0x01) << 3)
+                               | (((blurred1 >> i) & 0x01) << 2)
+                               | (((blurred2 >> i) & 0x01) << 1)
+                               | ((blurred3 >> i) & 0x01);
+                       blurTable[bitPat][7 - i] = color;
+               }
+       }
+#else
+       for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
+       {
+               uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
+
+               uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
+               uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
+               uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
+               uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
+
+               for(int8_t i=7; i>=0; i--)
+               {
+                       uint8_t color = (((blurred0 >> i) & 0x01) << 3)
+                               | (((blurred1 >> i) & 0x01) << 2)
+                               | (((blurred2 >> i) & 0x01) << 1)
+                               | ((blurred3 >> i) & 0x01);
+                       blurTable[bitPat >> 4][7 - i] = color;
+               }
+       }
+#endif
+
+       for(int i=0; i<256; i++)
+       {
+               mirrorTable[i] = ((i & 0x01) << 7)
+                       | ((i & 0x02) << 5)
+                       | ((i & 0x04) << 3)
+                       | ((i & 0x08) << 1)
+                       | ((i & 0x10) >> 1)
+                       | ((i & 0x20) >> 3)
+                       | ((i & 0x40) >> 5)
+                       | ((i & 0x80) >> 7);
+       }
+}
+
+
+void TogglePalette(void)
+{
+       if (palette == (uint32_t *)colors)
+       {
+               palette = (uint32_t *)altColors;
+               SpawnMessage("Color TV palette");
+       }
+       else
+       {
+               palette = (uint32_t *)colors;
+               SpawnMessage("\"Master Color Values\" palette");
+       }
+}
+
+
+void CycleScreenTypes(void)
+{
+       char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
+
+       screenType++;
+
+       if (screenType == ST_LAST_ENTRY)
+               screenType = ST_FIRST_ENTRY;
+
+       SpawnMessage("%s", scrTypeStr[screenType]);
+}
+
+
+static uint32_t msgTicks = 0;
+static char message[4096];
+
+void SpawnMessage(const char * text, ...)
+{
+       va_list arg;
+
+       va_start(arg, text);
+       vsprintf(message, text, arg);
+       va_end(arg);
+
+       msgTicks = 120;
+}
+
+
+static void DrawString2(uint32_t x, uint32_t y, uint32_t color);
+static void DrawString(void)
+{
+//This approach works, and seems to be fast enough... Though it probably would
+//be better to make the oversized font to match this one...
+       for(uint32_t x=7; x<=9; x++)
+               for(uint32_t y=7; y<=9; y++)
+                       DrawString2(x, y, 0x00000000);
+
+       DrawString2(8, 8, 0x0020FF20);
+}
+
+
+static void DrawString2(uint32_t x, uint32_t y, uint32_t color)
+{
+//uint32_t x = 8, y = 8;
+       uint32_t length = strlen(message), address = x + (y * VIRTUAL_SCREEN_WIDTH);
+//     uint32_t color = 0x0020FF20;
+//This could be done ahead of time, instead of on each pixel...
+//(Now it is!)
+       uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
+
+       for(uint32_t i=0; i<length; i++)
+       {
+               uint8_t c = message[i];
+               c = (c < 32 ? 0 : c - 32);
+               uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
+
+               for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
+               {
+                       for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
+                       {
+/*                             uint8_t fontTrans = font1[fontAddr++];
+//                             uint32_t newTrans = (fontTrans * transparency / 255) << 24;
+                               uint32_t newTrans = fontTrans << 24;
+                               uint32_t pixel = newTrans | color;
+
+                               *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = pixel;//*/
+
+//                             uint8_t trans = font1[fontAddr++];
+                               uint8_t trans = font2[fontAddr++];
+
+                               if (trans)
+                               {
+                                       uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
+
+                                       uint8_t eBlue = (existingColor >> 16) & 0xFF,
+                                               eGreen = (existingColor >> 8) & 0xFF,
+                                               eRed = existingColor & 0xFF;
+
+//This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
+//Here we've modified it to have 33 levels of transparency (could have any # we want!)
+//because dividing by 32 is faster than dividing by 31...!
+                                       uint8_t invTrans = 255 - trans;
+
+                                       uint32_t bRed   = (eRed   * invTrans + nRed   * trans) / 255;
+                                       uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
+                                       uint32_t bBlue  = (eBlue  * invTrans + nBlue  * trans) / 255;
+
+//THIS IS NOT ENDIAN SAFE
+//NB: Setting the alpha channel here does nothing.
+                                       *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
+                               }
+                       }
+               }
+
+               address += FONT_WIDTH;
+       }
+}
+
+
+static void Render40ColumnTextLine(uint8_t line)
+{
+       uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
+
+       for(int x=0; x<40; x++)
+       {
+               uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
+
+               // Render character at (x, y)
+
+               for(int cy=0; cy<8; cy++)
+               {
+                       for(int cx=0; cx<7; cx++)
+                       {
+                               uint32_t pixel = 0xFF000000;
+
+                               if (alternateCharset)
+                               {
+                                       if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
+                                               pixel = pixelOn;
+
+                                       if (chr < 0x80)
+                                               pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
+
+                                       if ((chr & 0xC0) == 0x40 && flash)
+                                               pixel = 0xFF000000;
+                               }
+                               else
+                               {
+                                       if (textChar2e[(chr * 56) + cx + (cy * 7)])
+                                               pixel = pixelOn;
+                               }
+
+                               scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
+                               scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
+
+                               // QnD method to get blank alternate lines in text mode
+                               if (screenType == ST_GREEN_MONO)
+                                       pixel = 0xFF000000;
+
+                               {
+                                       scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
+                                       scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
+                               }
+                       }
+               }
+       }
+}
+
+
+static void Render80ColumnTextLine(uint8_t line)
+{
+       uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
+
+       for(int x=0; x<80; x++)
+       {
+#if 0
+// This is wrong; it should grab from the alt bank if Page2 is set, not main RAM @ $0
+               uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
+
+               if (x > 39)
+                       chr = ram2[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x - 40];
+#else
+               uint8_t chr;
+
+               if (x & 0x01)
+                       chr = ram[lineAddrLoRes[line] + (x >> 1)];
+               else
+                       chr = ram2[lineAddrLoRes[line] + (x >> 1)];
+#endif
+
+               // Render character at (x, y)
+
+               for(int cy=0; cy<8; cy++)
+               {
+                       for(int cx=0; cx<7; cx++)
+                       {
+                               uint32_t pixel = 0xFF000000;
+
+                               if (alternateCharset)
+                               {
+                                       if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
+                                               pixel = pixelOn;
+
+                                       if (chr < 0x80)
+                                               pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
+
+                                       if ((chr & 0xC0) == 0x40 && flash)
+                                               pixel = 0xFF000000;
+                               }
+                               else
+                               {
+                                       if (textChar2e[(chr * 56) + cx + (cy * 7)])
+                                               pixel = pixelOn;
+                               }
+
+                               scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
+
+                               // QnD method to get blank alternate lines in text mode
+                               if (screenType == ST_GREEN_MONO)
+                                       pixel = 0xFF000000;
+
+                               scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
+                       }
+               }
+       }
+}
+
+
+static void Render40ColumnText(void)
+{
+       for(uint8_t line=0; line<24; line++)
+               Render40ColumnTextLine(line);
+}
+
+
+static void Render80ColumnText(void)
+{
+       for(uint8_t line=0; line<24; line++)
+               Render80ColumnTextLine(line);
+}
+
+
+static void RenderLoRes(uint16_t toLine/*= 24*/)
+{
+// NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
+//       Also, we could set up three different Render functions depending on which
+//       render type was set and call it with a function pointer. Would be faster
+//       then the nested ifs we have now.
+/*
+Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
+Color #s correspond to the bit patterns in reverse... Interesting!
+
+00 00 00 ->  0 [0000] -> 0 (lores color #)
+3c 4d 00 ->  8 [0001] -> 8?            BROWN
+00 5d 3c ->  4 [0010] -> 4?            DARK GREEN
+3c aa 3c -> 12 [0011] -> 12?   LIGHT GREEN (GREEN)
+41 30 7d ->  2 [0100] -> 2?            DARK BLUE
+7d 7d 7d -> 10 [0101] -> 10?   LIGHT GRAY
+41 8e ba ->  6 [0110] -> 6?            MEDIUM BLUE (BLUE)
+7d db ba -> 14 [0111] -> 14?   AQUAMARINE (AQUA)
+7d 20 41 ->  1 [1000] -> 1?            DEEP RED (MAGENTA)
+ba 6d 41 ->  9 [1001] -> 9?            ORANGE
+7d 7d 7d ->  5 [1010] -> 5?            DARK GRAY
+ba cb 7d -> 13 [1011] -> 13?   YELLOW
+be 51 be ->  3 [1100] -> 3             PURPLE (VIOLET)
+fb 9e be -> 11 [1101] -> 11?   PINK
+be ae fb ->  7 [1110] -> 7?            LIGHT BLUE (CYAN)
+fb fb fb -> 15 [1111] -> 15            WHITE
+*/
+       uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
+
+//This is the old "perfect monitor" rendering code...
+/*     if (screenType != ST_COLOR_TV) // Not correct, but for now...
+//if (1)
+       {
+               for(uint16_t y=0; y<toLine; y++)
+               {
+                       for(uint16_t x=0; x<40; x++)
+                       {
+                               uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
+                               uint32_t pixel = palette[scrByte & 0x0F];
+
+                               for(int cy=0; cy<4; cy++)
+                                       for(int cx=0; cx<14; cx++)
+                                               scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
+
+                               pixel = palette[scrByte >> 4];
+
+                               for(int cy=4; cy<8; cy++)
+                                       for(int cx=0; cx<14; cx++)
+                                               scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
+                       }
+               }
+       }
+       else//*/
+
+       uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
+
+       for(uint16_t y=0; y<toLine; y++)
+       {
+               // Do top half of lores screen bytes...
+
+               uint32_t previous3Bits = 0;
+
+               for(uint16_t x=0; x<40; x+=2)
+               {
+                       uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
+                       uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
+                       scrByte1 = mirrorNybble[scrByte1];
+                       scrByte2 = mirrorNybble[scrByte2];
+                       // This is just a guess, but it'll have to do for now...
+                       uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
+                               | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
+                               | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
+
+                       // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
+                       // 0ppp 1111 1111 1111 11|11 1111 1111 1111
+                       // 31   27   23   19   15    11   7    3  0
+
+                       if (screenType == ST_COLOR_TV)
+                       {
+                               for(uint8_t i=0; i<7; i++)
+                               {
+                                       uint8_t bitPat = (pixels & 0x7F000000) >> 24;
+                                       pixels <<= 4;
+
+                                       for(uint8_t j=0; j<4; j++)
+                                       {
+                                               uint8_t color = blurTable[bitPat][j];
+
+                                               for(uint32_t cy=0; cy<8; cy++)
+                                               {
+                                                       scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
+//                                                     scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
+                                               }
+                                       }
+                               }
+
+                               previous3Bits = pixels & 0x70000000;
+                       }
+                       else
+                       {
+                               for(int j=0; j<28; j++)
+                               {
+                                       for(uint32_t cy=0; cy<8; cy++)
+                                       {
+                                               scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
+//                                             scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
+                                       }
+
+                                       pixels <<= 1;
+                               }
+                       }
+               }
+
+               // Now do bottom half...
+
+               previous3Bits = 0;
+
+               for(uint16_t x=0; x<40; x+=2)
+               {
+                       uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
+                       uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
+                       scrByte1 = mirrorNybble[scrByte1];
+                       scrByte2 = mirrorNybble[scrByte2];
+                       // This is just a guess, but it'll have to do for now...
+                       uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
+                               | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
+                               | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
+
+                       // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
+                       // 0ppp 1111 1111 1111 11|11 1111 1111 1111
+                       // 31   27   23   19   15    11   7    3  0
+
+                       if (screenType == ST_COLOR_TV)
+                       {
+                               for(uint8_t i=0; i<7; i++)
+                               {
+                                       uint8_t bitPat = (pixels & 0x7F000000) >> 24;
+                                       pixels <<= 4;
+
+                                       for(uint8_t j=0; j<4; j++)
+                                       {
+                                               uint8_t color = blurTable[bitPat][j];
+
+                                               for(uint32_t cy=8; cy<16; cy++)
+                                               {
+                                                       scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
+//                                                     scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
+                                               }
+                                       }
+                               }
+
+                               previous3Bits = pixels & 0x70000000;
+                       }
+                       else
+                       {
+                               for(int j=0; j<28; j++)
+                               {
+                                       for(uint32_t cy=8; cy<16; cy++)
+                                       {
+                                               scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
+//                                             scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
+                                       }
+
+                                       pixels <<= 1;
+                               }
+                       }
+               }
+       }
+}
+
+
+static void RenderHiRes(uint16_t toLine/*= 192*/)
+{
+//printf("RenderHiRes to line %u\n", toLine);
+// NOTE: Not endian safe. !!! FIX !!! [DONE]
+#if 0
+       uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
+#else
+// Now it is. Now roll this fix into all the other places... !!! FIX !!!
+// The colors are set in the 8-bit array as R G B A
+       uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
+       uint32_t * colorPtr = (uint32_t *)monoColors;
+       uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
+#endif
+
+       for(uint16_t y=0; y<toLine; y++)
+       {
+               uint16_t previousLoPixel = 0;
+               uint32_t previous3bits = 0;
+
+               for(uint16_t x=0; x<40; x+=2)
+               {
+                       uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
+                       uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
+                       previousLoPixel = (screenByte << 2) & 0x0100;
+
+                       screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
+                       uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
+                       previousLoPixel = (screenByte << 2) & 0x0100;
+
+                       pixels = previous3bits | (pixels << 14) | pixels2;
+
+//testing (this shows on the screen, so it's OK)
+//if (x == 0)
+//{
+//     pixels = 0x7FFFFFFF;
+//}
+
+                       // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
+                       // 0ppp 1111 1111 1111 1111 1111 1111 1111
+                       // 31   27   23   19   15   11   7    3  0
+
+                       if (screenType == ST_COLOR_TV)
+                       {
+                               for(uint8_t i=0; i<7; i++)
+                               {
+                                       uint8_t bitPat = (pixels & 0x7F000000) >> 24;
+                                       pixels <<= 4;
+
+                                       for(uint8_t j=0; j<4; j++)
+                                       {
+                                               uint8_t color = blurTable[bitPat][j];
+#if 0
+//This doesn't seem to make things go any faster...
+//It's the OpenGL render that's faster... Hmm...
+                                               scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
+#else
+                                               scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
+                                               scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
+#endif
+                                       }
+                               }
+
+                               previous3bits = pixels & 0x70000000;
+                       }
+                       else
+                       {
+                               for(int j=0; j<28; j++)
+                               {
+                                       scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
+
+                                       if (screenType == ST_GREEN_MONO)
+                                               pixels &= 0x07FFFFFF;
+
+                                       scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
+                                       pixels <<= 1;
+                               }
+                       }
+               }
+       }
+}
+
+
+static void RenderDHiRes(uint16_t toLine/*= 192*/)
+{
+// Now it is. Now roll this fix into all the other places... !!! FIX !!!
+// The colors are set in the 8-bit array as R G B A
+       uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
+       uint32_t * colorPtr = (uint32_t *)monoColors;
+       uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
+
+       for(uint16_t y=0; y<toLine; y++)
+       {
+               uint32_t previous4bits = 0;
+
+               for(uint16_t x=0; x<40; x+=2)
+               {
+                       uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
+                       uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
+                       screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
+                       pixels = pixels | (mirrorTable[screenByte & 0x7F]);
+                       screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
+                       pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
+                       screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
+                       pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
+                       pixels = previous4bits | (pixels >> 1);
+
+                       // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
+                       // 0ppp 1111 1111 1111 1111 1111 1111 1111
+                       // 31   27   23   19   15   11   7    3  0
+
+                       if (screenType == ST_COLOR_TV)
+                       {
+                               for(uint8_t i=0; i<7; i++)
+                               {
+                                       uint8_t bitPat = (pixels & 0xFE000000) >> 25;
+                                       pixels <<= 4;
+
+                                       for(uint8_t j=0; j<4; j++)
+                                       {
+                                               uint32_t color = palette[blurTable[bitPat][j]];
+                                               scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
+                                               scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
+                                       }
+                               }
+
+                               previous4bits = pixels & 0xF0000000;
+                       }
+                       else
+                       {
+                               for(int j=0; j<28; j++)
+                               {
+                                       scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
+
+                                       if (screenType == ST_GREEN_MONO)
+                                               pixels &= 0x07FFFFFF;
+
+                                       scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
+                                       pixels <<= 1;
+                               }
+                       }
+               }
+       }
+}
+
+
+void RenderVideoFrame(void)
+{
+       if (GUI::powerOnState == true)
+       {
+               if (textMode)
+               {
+                       if (!col80Mode)
+                               Render40ColumnText();
+                       else
+                               Render80ColumnText();
+               }
+               else
+               {
+                       if (mixedMode)
+                       {
+                               if (hiRes)
+                               {
+                                       RenderHiRes(160);
+                                       Render40ColumnTextLine(20);
+                                       Render40ColumnTextLine(21);
+                                       Render40ColumnTextLine(22);
+                                       Render40ColumnTextLine(23);
+                               }
+                               else
+                               {
+                                       RenderLoRes(20);
+                                       Render40ColumnTextLine(20);
+                                       Render40ColumnTextLine(21);
+                                       Render40ColumnTextLine(22);
+                                       Render40ColumnTextLine(23);
+                               }
+                       }
+                       else
+                       {
+                               if (dhires)
+                                       RenderDHiRes();
+                               else if (hiRes)
+                                       RenderHiRes();
+                               else
+                                       RenderLoRes();
+                       }
+               }
+       }
+       else
+       {
+               memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
+       }
+
+       if (msgTicks)
+       {
+               DrawString();
+               msgTicks--;
+       }
+}
 
 
 //
@@ -39,7 +944,8 @@ bool InitVideo(void)
                return false;
        }
 
-       int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, SDL_WINDOW_OPENGL, &sdlWindow, &sdlRenderer);
+//     int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, SDL_WINDOW_OPENGL, &sdlWindow, &sdlRenderer);
+       int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0, &sdlWindow, &sdlRenderer);
 
        if (retVal != 0)
        {
@@ -62,6 +968,8 @@ bool InitVideo(void)
                SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
                VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
 
+       SetupBlurTable();
+
        WriteLog("Video: Successfully initialized.\n");
        return true;
 }
@@ -73,19 +981,23 @@ bool InitVideo(void)
 void VideoDone(void)
 {
        WriteLog("Video: Shutting down SDL...\n");
+       SDL_DestroyTexture(sdlTexture);
+       SDL_DestroyRenderer(sdlRenderer);
+       SDL_DestroyWindow(sdlWindow);
        SDL_Quit();
        WriteLog("Video: Done.\n");
 }
 
 
 //
-// Render the screen buffer to the primary screen surface
+// Render the Apple video screen to the primary texture
 //
-void RenderScreenBuffer(void)
+void RenderAppleScreen(SDL_Renderer * renderer)
 {
-       SDL_UpdateTexture(sdlTexture, NULL, scrBuffer, VIRTUAL_SCREEN_WIDTH * sizeof(Uint32));
-       SDL_RenderClear(sdlRenderer);
-       SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
+       SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch);
+       RenderVideoFrame();
+       SDL_UnlockTexture(sdlTexture);
+       SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
 }
 
 
@@ -103,4 +1015,3 @@ void ToggleFullScreen(void)
                WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());
 }
 
-
index bd510d58d1b94dc5a9c668db28561b9db426d860..3adff990c3517a4025f8cbc0bb84dd63a20653e8 100644 (file)
@@ -1,12 +1,11 @@
 //
-// VIDEO.H: Header file
+// Apple 2/host video support
 //
 
 #ifndef __VIDEO_H__
 #define __VIDEO_H__
 
 #include <SDL2/SDL.h>
-#include <stdint.h>                                                    // For uint32_t
 
 // These are double the normal width because we use sub-pixel rendering.
 //#define VIRTUAL_SCREEN_WIDTH         280
 //#define VIRTUAL_SCREEN_HEIGHT                192
 #define VIRTUAL_SCREEN_HEIGHT          384
 
+// Global variables (exported)
+
+extern bool flash;
+extern bool textMode;
+extern bool mixedMode;
+extern bool displayPage2;
+extern bool hiRes;
+extern bool alternateCharset;
+extern bool col80Mode;
+extern SDL_Renderer * sdlRenderer;
+
+// Functions (exported)
+
+//void SetupBlurTable(void);
+void TogglePalette(void);
+void CycleScreenTypes(void);
+void SpawnMessage(const char * text, ...);
 bool InitVideo(void);
 void VideoDone(void);
-void RenderScreenBuffer(void);
+void RenderAppleScreen(SDL_Renderer *);
 void ToggleFullScreen(void);
 
+
 // Exported crap
 
-extern SDL_Renderer * sdlRenderer;
-extern uint32_t scrBuffer[];
-extern uint32_t mainScrBuffer[];
+//extern uint32_t * scrBuffer;
+//extern int scrPitch;
 
 #endif // __VIDEO_H__
-