]> Shamusworld >> Repos - apple2/blobdiff - src/applevideo.cpp
Set eol-style to sane default.
[apple2] / src / applevideo.cpp
index e9bfaa9e8a1ac3df3a5711c2f3adf18c32a0cb7b..25420524fb0f202cef42b1b526c9927132e59abb 100755 (executable)
-//\r
-// Apple 2 video support\r
-//\r
-// All the video modes that a real Apple 2 supports are handled here\r
-//\r
-// by James L. Hammons\r
-// (c) 2005 Underground Software\r
-//\r
-// JLH = James L. Hammons <jlhamm@acm.org>\r
-//\r
-// WHO  WHEN        WHAT\r
-// ---  ----------  ------------------------------------------------------------\r
-// JLH  12/01/2005  Added color TV/monochrome emulation to hi-res code\r
-// JLH  12/09/2005  Cleaned up color TV emulation code\r
-// JLH  12/09/2005  Fixed lo-res color TV/mono emulation modes\r
-//\r
-// STILL TO DO:\r
-//\r
-// - Fix LoRes mode green mono to skip every other scanline instead of fill\r
-//   like white mono does\r
-// - Double HiRes\r
-// - 80 column text\r
-// - Fix OSD text display so that it's visible no matter what background is there [DONE]\r
-//\r
-\r
-// Display routines seem MUCH slower now... !!! INVESTIGATE !!!\r
-\r
-#include "applevideo.h"\r
-\r
-#include <string.h>                                                            // for memset()\r
-#include <stdarg.h>                                                            // for va_* stuff\r
-#include <string>                                                              // for vsprintf()\r
-#include "apple2.h"\r
-#include "video.h"\r
-#include "charset.h"\r
-#include "font14pt.h"\r
-\r
-/* Reference: Technote tn-iigs-063 "Master Color Values"\r
-\r
-          Color  Color Register LR HR  DHR Master Color R,G,B\r
-          Name       Value      #  #   #      Value\r
-          ----------------------------------------------------\r
-          Black       0         0  0,4 0      $0000    (0,0,0)\r
-(Magenta) Deep Red    1         1      1      $0D03    (D,0,3)\r
-          Dark Blue   2         2      8      $0009    (0,0,9)\r
- (Violet) Purple      3         3  2   9      $0D2D    (D,2,D)\r
-          Dark Green  4         4      4      $0072    (0,7,2)\r
- (Gray 1) Dark Gray   5         5      5      $0555    (5,5,5)\r
-   (Blue) Medium Blue 6         6  6   C      $022F    (2,2,F)\r
-   (Cyan) Light Blue  7         7      D      $06AF    (6,A,F)\r
-          Brown       8         8      2      $0850    (8,5,0)\r
-          Orange      9         9  5   3      $0F60    (F,6,0)\r
- (Gray 2) Light Gray  A         A      A      $0AAA    (A,A,A)\r
-          Pink        B         B      B      $0F98    (F,9,8)\r
-  (Green) Light Green C         C  1   6      $01D0    (1,D,0)\r
-          Yellow      D         D      7      $0FF0    (F,F,0)\r
-   (Aqua) Aquamarine  E         E      E      $04F9    (4,F,9)\r
-          White       F         F  3,7 F      $0FFF    (F,F,F)\r
-\r
-   LR: Lo-Res   HR: Hi-Res   DHR: Double Hi-Res */\r
-\r
-// Global variables\r
-\r
-bool flash;\r
-bool textMode;\r
-bool mixedMode;\r
-bool displayPage2;\r
-bool hiRes;\r
-bool alternateCharset;\r
-//void SpawnMessage(const char * text, ...);\r
-\r
-// Local variables\r
-\r
-// We set up the colors this way so that they'll be endian safe\r
-// when we cast them to a uint32.\r
-\r
-// "Master Color Values" palette\r
-\r
-static uint8 colors[16 * 4] = {\r
-       0x00, 0x00, 0x00, 0xFF,                         // Black\r
-       0xDD, 0x00, 0x33, 0xFF,                         // Deep Red (Magenta)\r
-       0x00, 0x00, 0x99, 0xFF,                         // Dark Blue\r
-       0xDD, 0x22, 0xDD, 0xFF,                         // Purple (Violet)\r
-       0x00, 0x77, 0x22, 0xFF,                         // Dark Green\r
-       0x55, 0x55, 0x55, 0xFF,                         // Dark Gray (Gray 1)\r
-       0x22, 0x22, 0xFF, 0xFF,                         // Medium Blue (Blue)\r
-       0x66, 0xAA, 0xFF, 0xFF,                         // Light Blue (Cyan)\r
-       0x88, 0x55, 0x00, 0xFF,                         // Brown\r
-       0xFF, 0x66, 0x00, 0xFF,                         // Orange\r
-       0xAA, 0xAA, 0xAA, 0xFF,                         // Light Gray (Gray 2)\r
-       0xFF, 0x99, 0x88, 0xFF,                         // Pink\r
-       0x11, 0xDD, 0x00, 0xFF,                         // Light Green (Green)\r
-       0xFF, 0xFF, 0x00, 0xFF,                         // Yellow\r
-       0x44, 0xFF, 0x99, 0xFF,                         // Aquamarine (Aqua)\r
-       0xFF, 0xFF, 0xFF, 0xFF                          // White\r
-};\r
-\r
-// This palette comes from ApplePC's colors (more realistic to my eye ;-)\r
-\r
-static uint8 altColors[16 * 4] = {\r
-       0x00, 0x00, 0x00, 0xFF,\r
-       0x7D, 0x20, 0x41, 0xFF,\r
-       0x41, 0x30, 0x7D, 0xFF,\r
-       0xBE, 0x51, 0xBE, 0xFF,\r
-       0x00, 0x5D, 0x3C, 0xFF,\r
-       0x7D, 0x7D, 0x7D, 0xFF,\r
-       0x41, 0x8E, 0xBA, 0xFF,\r
-       0xBE, 0xAE, 0xFB, 0xFF,\r
-       0x3C, 0x4D, 0x00, 0xFF,\r
-       0xBA, 0x6D, 0x41, 0xFF,\r
-       0x7D, 0x7D, 0x7D, 0xFF,\r
-       0xFB, 0x9E, 0xBE, 0xFF,\r
-       0x3C, 0xAA, 0x3C, 0xFF,\r
-       0xBA, 0xCB, 0x7D, 0xFF,\r
-       0x7D, 0xDB, 0xBA, 0xFF,\r
-       0xFB, 0xFB, 0xFB, 0xFF };\r
-\r
-// Lo-res starting line addresses\r
-\r
-static uint16 lineAddrLoRes[24] = {\r
-       0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,\r
-       0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,\r
-       0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 };\r
-\r
-// Hi-res starting line addresses\r
-\r
-static uint16 lineAddrHiRes[192] = {\r
-       0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,\r
-       0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,\r
-       0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,\r
-       0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,\r
-\r
-       0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,\r
-       0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,\r
-       0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,\r
-       0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,\r
-\r
-       0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,\r
-       0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,\r
-       0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,\r
-       0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,\r
-\r
-       0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,\r
-       0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,\r
-       0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,\r
-       0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,\r
-\r
-       0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,\r
-       0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,\r
-       0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,\r
-       0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,\r
-\r
-       0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,\r
-       0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,\r
-       0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,\r
-       0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 };\r
-\r
-uint16 appleHiresToMono[0x200] = {\r
-       0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,\r
-       0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x\r
-       0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,\r
-       0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x\r
-       0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,\r
-       0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x\r
-       0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,\r
-       0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x\r
-       0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,\r
-       0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x\r
-       0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,\r
-       0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x\r
-       0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,\r
-       0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x\r
-       0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,\r
-       0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x\r
-       0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80,\r
-       0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x\r
-       0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98,\r
-       0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x\r
-       0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86,\r
-       0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax\r
-       0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E,\r
-       0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx\r
-       0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81,\r
-       0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx\r
-       0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99,\r
-       0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx\r
-       0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87,\r
-       0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex\r
-       0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F,\r
-       0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx\r
-\r
-       // Second half adds in the previous byte's lo pixel\r
-\r
-       0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,\r
-       0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x\r
-       0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,\r
-       0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x\r
-       0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,\r
-       0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x\r
-       0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,\r
-       0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x\r
-       0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,\r
-       0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x\r
-       0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,\r
-       0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x\r
-       0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,\r
-       0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x\r
-       0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,\r
-       0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x\r
-       0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80,\r
-       0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x\r
-       0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98,\r
-       0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x\r
-       0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86,\r
-       0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax\r
-       0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E,\r
-       0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx\r
-       0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81,\r
-       0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx\r
-       0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99,\r
-       0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx\r
-       0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87,\r
-       0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex\r
-       0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F,\r
-       0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF  // $Fx\r
-};\r
-\r
-static uint8 blurTable[0x800][8];                              // Color TV blur table\r
-static uint32 * palette = (uint32 *)altColors;\r
-enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };\r
-static uint8 screenType = ST_COLOR_TV;\r
-\r
-// Local functions\r
-\r
-static void Render40ColumnTextLine(uint8 line);\r
-static void Render40ColumnText(void);\r
-static void RenderLoRes(uint16 toLine = 24);\r
-static void RenderHiRes(uint16 toLine = 192);\r
-\r
-\r
-void SetupBlurTable(void)\r
-{\r
-       // NOTE: This table only needs to be 7 bits wide instead of 11, since the\r
-       //       last four bits are copies of the previous four...\r
-\r
-       for(uint16 bitPat=0; bitPat<0x800; bitPat++)\r
-       {\r
-               uint16 w3 = bitPat & 0x888;\r
-               uint16 w2 = bitPat & 0x444;\r
-               uint16 w1 = bitPat & 0x222;\r
-               uint16 w0 = bitPat & 0x111;\r
-\r
-               uint16 blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;\r
-               uint16 blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;\r
-               uint16 blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;\r
-               uint16 blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;\r
-\r
-               for(int8 i=7; i>=0; i--)\r
-               {\r
-                       uint8 color = (((blurred0 >> i) & 0x01) << 3)\r
-                               | (((blurred1 >> i) & 0x01) << 2)\r
-                               | (((blurred2 >> i) & 0x01) << 1)\r
-                               | ((blurred3 >> i) & 0x01);\r
-                       blurTable[bitPat][7 - i] = color;\r
-               }\r
-       }\r
-}\r
-\r
-void TogglePalette(void)\r
-{\r
-       if (palette == (uint32 *)colors)\r
-       {\r
-               palette = (uint32 *)altColors;\r
-               SpawnMessage("Color TV palette");\r
-       }\r
-       else\r
-       {\r
-               palette = (uint32 *)colors;\r
-               SpawnMessage("\"Master Color Values\" palette");\r
-       }\r
-}\r
-\r
-void CycleScreenTypes(void)\r
-{\r
-       char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };\r
-\r
-       screenType++;\r
-\r
-       if (screenType == ST_LAST_ENTRY)\r
-               screenType = ST_FIRST_ENTRY;\r
-\r
-       SpawnMessage("%s", scrTypeStr[screenType]);\r
-}\r
-\r
-static uint32 msgTicks = 0;\r
-static char message[4096];\r
-\r
-void SpawnMessage(const char * text, ...)\r
-{\r
-       va_list arg;\r
-\r
-       va_start(arg, text);\r
-       vsprintf(message, text, arg);\r
-       va_end(arg);\r
-\r
-       msgTicks = 120;\r
-}\r
-\r
-static void DrawString2(uint32 x, uint32 y, uint32 color);\r
-static void DrawString(void)\r
-{\r
-//This approach works, and seems to be fast enough... Though it probably would\r
-//be better to make the oversized font to match this one...\r
-       for(uint32 x=7; x<=9; x++)\r
-               for(uint32 y=7; y<=9; y++)\r
-                       DrawString2(x, y, 0x00000000);\r
-\r
-       DrawString2(8, 8, 0x0020FF20);\r
-}\r
-\r
-static void DrawString2(uint32 x, uint32 y, uint32 color)\r
-{\r
-//uint32 x = 8, y = 8;\r
-       uint32 length = strlen(message), address = x + (y * VIRTUAL_SCREEN_WIDTH);\r
-//     uint32 color = 0x0020FF20;\r
-//This could be done ahead of time, instead of on each pixel...\r
-//(Now it is!)\r
-       uint8 nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;\r
-\r
-       for(uint32 i=0; i<length; i++)\r
-       {\r
-               uint8 c = message[i];\r
-               c = (c < 32 ? 0 : c - 32);\r
-               uint32 fontAddr = (uint32)c * FONT_WIDTH * FONT_HEIGHT;\r
-\r
-               for(uint32 yy=0; yy<FONT_HEIGHT; yy++)\r
-               {\r
-                       for(uint32 xx=0; xx<FONT_WIDTH; xx++)\r
-                       {\r
-/*                             uint8 fontTrans = font1[fontAddr++];\r
-//                             uint32 newTrans = (fontTrans * transparency / 255) << 24;\r
-                               uint32 newTrans = fontTrans << 24;\r
-                               uint32 pixel = newTrans | color;\r
-\r
-                               *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = pixel;//*/\r
-\r
-                               uint8 trans = font1[fontAddr++];\r
-\r
-                               if (trans)\r
-                               {\r
-                                       uint32 existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));\r
-\r
-                                       uint8 eBlue = (existingColor >> 16) & 0xFF,\r
-                                               eGreen = (existingColor >> 8) & 0xFF,\r
-                                               eRed = existingColor & 0xFF;\r
-\r
-//This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)\r
-//Here we've modified it to have 33 levels of transparency (could have any # we want!)\r
-//because dividing by 32 is faster than dividing by 31...!\r
-                                       uint8 invTrans = 255 - trans;\r
-\r
-                                       uint32 bRed   = (eRed   * invTrans + nRed   * trans) / 255;\r
-                                       uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;\r
-                                       uint32 bBlue  = (eBlue  * invTrans + nBlue  * trans) / 255;\r
-\r
-//THIS IS NOT ENDIAN SAFE\r
-                                       *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;\r
-                               }\r
-                       }\r
-               }\r
-\r
-               address += FONT_WIDTH;\r
-       }\r
-}\r
-\r
-static void Render40ColumnTextLine(uint8 line)\r
-{\r
-       uint32 pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);\r
-\r
-       for(int x=0; x<40; x++)\r
-       {\r
-               uint8 chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];\r
-\r
-               // Render character at (x, y)\r
-\r
-               for(int cy=0; cy<8; cy++)\r
-               {\r
-                       for(int cx=0; cx<7; cx++)\r
-                       {\r
-                               uint32 pixel = 0xFF000000;\r
-\r
-                               if (!alternateCharset)\r
-                               {\r
-                                       if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])\r
-//                                             pixel = 0xFFFFFFFF;\r
-                                               pixel = pixelOn;\r
-\r
-                                       if (chr < 0x80)\r
-                                               pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);\r
-\r
-                                       if ((chr & 0xC0) == 0x40 && flash)\r
-                                               pixel = 0xFF000000;\r
-                               }\r
-                               else\r
-                               {\r
-                                       if (textChar2e[(chr * 56) + cx + (cy * 7)])\r
-//                                             pixel = 0xFFFFFFFF;\r
-                                               pixel = pixelOn;\r
-                               }\r
-\r
-//                             scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;\r
-//                             scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;\r
-                               scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;\r
-                               scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;\r
-\r
-                               if (screenType == ST_GREEN_MONO)\r
-                                       pixel = 0xFF000000;\r
-\r
-                               {\r
-                               scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;\r
-                               scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-}\r
-\r
-static void Render40ColumnText(void)\r
-{\r
-       for(uint8 line=0; line<24; line++)\r
-               Render40ColumnTextLine(line);\r
-}\r
-\r
-static void RenderLoRes(uint16 toLine/*= 24*/)\r
-{\r
-// NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!\r
-/*\r
-Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:\r
-Color #s correspond to the bit patterns in reverse... Interesting!\r
-\r
-00 00 00 ->  0 [0000] -> 0 (lores color #)\r
-3c 4d 00 ->  8 [0001] -> 8?            BROWN\r
-00 5d 3c ->  4 [0010] -> 4?            DARK GREEN\r
-3c aa 3c -> 12 [0011] -> 12?   LIGHT GREEN (GREEN)\r
-41 30 7d ->  2 [0100] -> 2?            DARK BLUE\r
-7d 7d 7d -> 10 [0101] -> 10?   LIGHT GRAY\r
-41 8e ba ->  6 [0110] -> 6?            MEDIUM BLUE (BLUE)\r
-7d db ba -> 14 [0111] -> 14?   AQUAMARINE (AQUA)\r
-7d 20 41 ->  1 [1000] -> 1?            DEEP RED (MAGENTA)\r
-ba 6d 41 ->  9 [1001] -> 9?            ORANGE\r
-7d 7d 7d ->  5 [1010] -> 5?            DARK GRAY\r
-ba cb 7d -> 13 [1011] -> 13?   YELLOW\r
-be 51 be ->  3 [1100] -> 3             PURPLE (VIOLET)\r
-fb 9e be -> 11 [1101] -> 11?   PINK\r
-be ae fb ->  7 [1110] -> 7?            LIGHT BLUE (CYAN)\r
-fb fb fb -> 15 [1111] -> 15            WHITE\r
-*/\r
-       uint8 mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };\r
-\r
-//This is the old "perfect monitor" rendering code...\r
-/*     if (screenType != ST_COLOR_TV) // Not correct, but for now...\r
-//if (1)\r
-       {\r
-               for(uint16 y=0; y<toLine; y++)\r
-               {\r
-                       for(uint16 x=0; x<40; x++)\r
-                       {\r
-                               uint8 scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];\r
-                               uint32 pixel = palette[scrByte & 0x0F];\r
-       \r
-                               for(int cy=0; cy<4; cy++)\r
-                                       for(int cx=0; cx<14; cx++)\r
-                                               scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;\r
-       \r
-                               pixel = palette[scrByte >> 4];\r
-       \r
-                               for(int cy=4; cy<8; cy++)\r
-                                       for(int cx=0; cx<14; cx++)\r
-                                               scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;\r
-                       }\r
-               }\r
-       }\r
-       else//*/\r
-\r
-       uint32 pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);\r
-\r
-       for(uint16 y=0; y<toLine; y++)\r
-       {\r
-               // Do top half of lores screen bytes...\r
-\r
-               uint32 previous3Bits = 0;\r
-\r
-               for(uint16 x=0; x<40; x+=2)\r
-               {\r
-                       uint8 scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;\r
-                       uint8 scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;\r
-                       scrByte1 = mirrorNybble[scrByte1];\r
-                       scrByte2 = mirrorNybble[scrByte2];\r
-                       // This is just a guess, but it'll have to do for now...\r
-                       uint32 pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)\r
-                               | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)\r
-                               | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;\r
-\r
-                       // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF\r
-                       // 0ppp 1111 1111 1111 11|11 1111 1111 1111\r
-                       // 31   27   23   19   15    11   7    3  0\r
-\r
-                       if (screenType == ST_COLOR_TV)\r
-                       {\r
-                               for(uint8 i=0; i<7; i++)\r
-                               {\r
-                                       uint16 bitPat = (pixels & 0x7F000000) >> 20;\r
-                                       pixels <<= 4;\r
-\r
-                                       for(uint8 j=0; j<4; j++)\r
-                                       {\r
-                                               uint8 color = blurTable[bitPat][j];\r
-\r
-                                               for(uint32 cy=0; cy<8; cy++)\r
-                                               {\r
-                                                       scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];\r
-//                                                     scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];\r
-                                               }\r
-                                       }\r
-                               }\r
-\r
-                               previous3Bits = pixels & 0x70000000;\r
-                       }\r
-                       else\r
-                       {\r
-                               for(int j=0; j<28; j++)\r
-                               {\r
-                                       for(uint32 cy=0; cy<8; cy++)\r
-                                       {\r
-                                               scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);\r
-//                                             scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);\r
-                                       }\r
-\r
-                                       pixels <<= 1;\r
-                               }\r
-                       }\r
-               }\r
-\r
-               // Now do bottom half...\r
-\r
-               previous3Bits = 0;\r
-\r
-               for(uint16 x=0; x<40; x+=2)\r
-               {\r
-                       uint8 scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;\r
-                       uint8 scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;\r
-                       scrByte1 = mirrorNybble[scrByte1];\r
-                       scrByte2 = mirrorNybble[scrByte2];\r
-                       // This is just a guess, but it'll have to do for now...\r
-                       uint32 pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)\r
-                               | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)\r
-                               | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;\r
-\r
-                       // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF\r
-                       // 0ppp 1111 1111 1111 11|11 1111 1111 1111\r
-                       // 31   27   23   19   15    11   7    3  0\r
-\r
-                       if (screenType == ST_COLOR_TV)\r
-                       {\r
-                               for(uint8 i=0; i<7; i++)\r
-                               {\r
-                                       uint16 bitPat = (pixels & 0x7F000000) >> 20;\r
-                                       pixels <<= 4;\r
-\r
-                                       for(uint8 j=0; j<4; j++)\r
-                                       {\r
-                                               uint8 color = blurTable[bitPat][j];\r
-\r
-                                               for(uint32 cy=8; cy<16; cy++)\r
-                                               {\r
-                                                       scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];\r
-//                                                     scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];\r
-                                               }\r
-                                       }\r
-                               }\r
-\r
-                               previous3Bits = pixels & 0x70000000;\r
-                       }\r
-                       else\r
-                       {\r
-                               for(int j=0; j<28; j++)\r
-                               {\r
-                                       for(uint32 cy=8; cy<16; cy++)\r
-                                       {\r
-                                               scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);\r
-//                                             scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);\r
-                                       }\r
-\r
-                                       pixels <<= 1;\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-}\r
-\r
-static void RenderHiRes(uint16 toLine/*= 192*/)\r
-{\r
-       uint32 pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);\r
-\r
-       for(uint16 y=0; y<toLine; y++)\r
-       {\r
-               uint16 previousLoPixel = 0;\r
-               uint32 previous3bits = 0;\r
-\r
-               for(uint16 x=0; x<40; x+=2)\r
-               {\r
-                       uint8 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];\r
-                       uint32 pixels = appleHiresToMono[previousLoPixel | screenByte];\r
-                       previousLoPixel = (screenByte << 2) & 0x0100;\r
-\r
-                       screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];\r
-                       uint32 pixels2 = appleHiresToMono[previousLoPixel | screenByte];\r
-                       previousLoPixel = (screenByte << 2) & 0x0100;\r
-\r
-                       pixels = previous3bits | (pixels << 14) | pixels2;\r
-\r
-                       // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF\r
-                       // 0ppp 1111 1111 1111 1111 1111 1111 1111\r
-                       // 31   27   23   19   15   11   7    3  0\r
-\r
-                       if (screenType == ST_COLOR_TV)\r
-                       {\r
-                               for(uint8 i=0; i<7; i++)\r
-                               {\r
-                                       uint16 bitPat = (pixels & 0x7F000000) >> 20;\r
-                                       pixels <<= 4;\r
-\r
-                                       for(uint8 j=0; j<4; j++)\r
-                                       {\r
-                                               uint8 color = blurTable[bitPat][j];\r
-#if 0\r
-//This doesn't seem to make things go any faster...\r
-//It's the OpenGL render that's faster... Hmm...\r
-                                               scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];\r
-#else\r
-                                               scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];\r
-                                               scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];\r
-#endif\r
-                                       }\r
-                               }\r
-\r
-                               previous3bits = pixels & 0x70000000;\r
-                       }\r
-                       else\r
-                       {\r
-                               for(int j=0; j<28; j++)\r
-                               {\r
-                                       scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);\r
-\r
-                                       if (screenType == ST_GREEN_MONO)\r
-                                               pixels &= 0x07FFFFFF;\r
-\r
-                                       scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);\r
-                                       pixels <<= 1;\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-}\r
-\r
-void RenderVideoFrame(void)\r
-{\r
-//temp...\r
-/*RenderLoRes();\r
-RenderScreenBuffer();\r
-return;//*/\r
-\r
-       if (textMode)\r
-       {\r
-               // There's prolly more to it than this (like 80 column text), but this'll have to do for now...\r
-               Render40ColumnText();\r
-       }\r
-       else\r
-       {\r
-               if (mixedMode)\r
-               {\r
-                       if (hiRes)\r
-                       {\r
-                               RenderHiRes(160);\r
-                               Render40ColumnTextLine(20);\r
-                               Render40ColumnTextLine(21);\r
-                               Render40ColumnTextLine(22);\r
-                               Render40ColumnTextLine(23);\r
-                       }\r
-                       else\r
-                       {\r
-                               RenderLoRes(20);\r
-                               Render40ColumnTextLine(20);\r
-                               Render40ColumnTextLine(21);\r
-                               Render40ColumnTextLine(22);\r
-                               Render40ColumnTextLine(23);\r
-                       }\r
-               }\r
-               else\r
-               {\r
-                       if (hiRes)\r
-                               RenderHiRes();\r
-                       else\r
-                               RenderLoRes();\r
-               }\r
-       }\r
-\r
-       if (msgTicks)\r
-       {\r
-               DrawString();\r
-               msgTicks--;\r
-       }\r
-\r
-       RenderScreenBuffer();\r
-}\r
+//
+// Apple 2 video support
+//
+// All the video modes that a real Apple 2 supports are handled here
+//
+// by James L. Hammons
+// (c) 2005 Underground Software
+//
+// JLH = James L. 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
+// - Double HiRes
+// - 80 column text
+// - Fix OSD text display so that it's visible no matter what background is there [DONE]
+//
+
+// Display routines seem MUCH slower now... !!! INVESTIGATE !!!
+
+#include "applevideo.h"
+
+#include <string.h>                                                            // for memset()
+#include <stdarg.h>                                                            // for va_* stuff
+#include <string>                                                              // for vsprintf()
+#include "apple2.h"
+#include "video.h"
+#include "charset.h"
+#include "font14pt.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;
+bool textMode;
+bool mixedMode;
+bool displayPage2;
+bool hiRes;
+bool alternateCharset;
+//void SpawnMessage(const char * text, ...);
+
+// Local variables
+
+// We set up the colors this way so that they'll be endian safe
+// when we cast them to a uint32.
+
+// "Master Color Values" palette
+
+static uint8 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 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 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 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 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 blurTable[0x800][8];                              // Color TV blur table
+static uint32 * palette = (uint32 *)altColors;
+enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
+static uint8 screenType = ST_COLOR_TV;
+
+// Local functions
+
+static void Render40ColumnTextLine(uint8 line);
+static void Render40ColumnText(void);
+static void RenderLoRes(uint16 toLine = 24);
+static void RenderHiRes(uint16 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...
+
+       for(uint16 bitPat=0; bitPat<0x800; bitPat++)
+       {
+               uint16 w3 = bitPat & 0x888;
+               uint16 w2 = bitPat & 0x444;
+               uint16 w1 = bitPat & 0x222;
+               uint16 w0 = bitPat & 0x111;
+
+               uint16 blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
+               uint16 blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
+               uint16 blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
+               uint16 blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
+
+               for(int8 i=7; i>=0; i--)
+               {
+                       uint8 color = (((blurred0 >> i) & 0x01) << 3)
+                               | (((blurred1 >> i) & 0x01) << 2)
+                               | (((blurred2 >> i) & 0x01) << 1)
+                               | ((blurred3 >> i) & 0x01);
+                       blurTable[bitPat][7 - i] = color;
+               }
+       }
+}
+
+void TogglePalette(void)
+{
+       if (palette == (uint32 *)colors)
+       {
+               palette = (uint32 *)altColors;
+               SpawnMessage("Color TV palette");
+       }
+       else
+       {
+               palette = (uint32 *)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 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 x, uint32 y, uint32 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 x=7; x<=9; x++)
+               for(uint32 y=7; y<=9; y++)
+                       DrawString2(x, y, 0x00000000);
+
+       DrawString2(8, 8, 0x0020FF20);
+}
+
+static void DrawString2(uint32 x, uint32 y, uint32 color)
+{
+//uint32 x = 8, y = 8;
+       uint32 length = strlen(message), address = x + (y * VIRTUAL_SCREEN_WIDTH);
+//     uint32 color = 0x0020FF20;
+//This could be done ahead of time, instead of on each pixel...
+//(Now it is!)
+       uint8 nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
+
+       for(uint32 i=0; i<length; i++)
+       {
+               uint8 c = message[i];
+               c = (c < 32 ? 0 : c - 32);
+               uint32 fontAddr = (uint32)c * FONT_WIDTH * FONT_HEIGHT;
+
+               for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
+               {
+                       for(uint32 xx=0; xx<FONT_WIDTH; xx++)
+                       {
+/*                             uint8 fontTrans = font1[fontAddr++];
+//                             uint32 newTrans = (fontTrans * transparency / 255) << 24;
+                               uint32 newTrans = fontTrans << 24;
+                               uint32 pixel = newTrans | color;
+
+                               *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = pixel;//*/
+
+                               uint8 trans = font1[fontAddr++];
+
+                               if (trans)
+                               {
+                                       uint32 existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
+
+                                       uint8 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 invTrans = 255 - trans;
+
+                                       uint32 bRed   = (eRed   * invTrans + nRed   * trans) / 255;
+                                       uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
+                                       uint32 bBlue  = (eBlue  * invTrans + nBlue  * trans) / 255;
+
+//THIS IS NOT ENDIAN SAFE
+                                       *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
+                               }
+                       }
+               }
+
+               address += FONT_WIDTH;
+       }
+}
+
+static void Render40ColumnTextLine(uint8 line)
+{
+       uint32 pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
+
+       for(int x=0; x<40; x++)
+       {
+               uint8 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 pixel = 0xFF000000;
+
+                               if (!alternateCharset)
+                               {
+                                       if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
+//                                             pixel = 0xFFFFFFFF;
+                                               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 = 0xFFFFFFFF;
+                                               pixel = pixelOn;
+                               }
+
+//                             scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
+//                             scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
+                               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;
+
+                               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 Render40ColumnText(void)
+{
+       for(uint8 line=0; line<24; line++)
+               Render40ColumnTextLine(line);
+}
+
+static void RenderLoRes(uint16 toLine/*= 24*/)
+{
+// NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
+/*
+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 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 y=0; y<toLine; y++)
+               {
+                       for(uint16 x=0; x<40; x++)
+                       {
+                               uint8 scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
+                               uint32 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 pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
+
+       for(uint16 y=0; y<toLine; y++)
+       {
+               // Do top half of lores screen bytes...
+
+               uint32 previous3Bits = 0;
+
+               for(uint16 x=0; x<40; x+=2)
+               {
+                       uint8 scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
+                       uint8 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 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 i=0; i<7; i++)
+                               {
+                                       uint16 bitPat = (pixels & 0x7F000000) >> 20;
+                                       pixels <<= 4;
+
+                                       for(uint8 j=0; j<4; j++)
+                                       {
+                                               uint8 color = blurTable[bitPat][j];
+
+                                               for(uint32 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 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 x=0; x<40; x+=2)
+               {
+                       uint8 scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
+                       uint8 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 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 i=0; i<7; i++)
+                               {
+                                       uint16 bitPat = (pixels & 0x7F000000) >> 20;
+                                       pixels <<= 4;
+
+                                       for(uint8 j=0; j<4; j++)
+                                       {
+                                               uint8 color = blurTable[bitPat][j];
+
+                                               for(uint32 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 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 toLine/*= 192*/)
+{
+       uint32 pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
+
+       for(uint16 y=0; y<toLine; y++)
+       {
+               uint16 previousLoPixel = 0;
+               uint32 previous3bits = 0;
+
+               for(uint16 x=0; x<40; x+=2)
+               {
+                       uint8 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
+                       uint32 pixels = appleHiresToMono[previousLoPixel | screenByte];
+                       previousLoPixel = (screenByte << 2) & 0x0100;
+
+                       screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
+                       uint32 pixels2 = appleHiresToMono[previousLoPixel | screenByte];
+                       previousLoPixel = (screenByte << 2) & 0x0100;
+
+                       pixels = previous3bits | (pixels << 14) | pixels2;
+
+                       // 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 i=0; i<7; i++)
+                               {
+                                       uint16 bitPat = (pixels & 0x7F000000) >> 20;
+                                       pixels <<= 4;
+
+                                       for(uint8 j=0; j<4; j++)
+                                       {
+                                               uint8 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;
+                               }
+                       }
+               }
+       }
+}
+
+void RenderVideoFrame(void)
+{
+//temp...
+/*RenderLoRes();
+RenderScreenBuffer();
+return;//*/
+
+       if (textMode)
+       {
+               // There's prolly more to it than this (like 80 column text), but this'll have to do for now...
+               Render40ColumnText();
+       }
+       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 (hiRes)
+                               RenderHiRes();
+                       else
+                               RenderLoRes();
+               }
+       }
+
+       if (msgTicks)
+       {
+               DrawString();
+               msgTicks--;
+       }
+
+       RenderScreenBuffer();
+}