]> Shamusworld >> Repos - apple2/blob - src/video.cpp
First stab at adding Mockingboard support.
[apple2] / src / video.cpp
1 //
2 // Apple 2 video support
3 //
4 // All the video modes that a real Apple 2 supports are handled here
5 //
6 // by James Hammons
7 // (c) 2005-2018 Underground Software
8 //
9 // JLH = James Hammons <jlhamm@acm.org>
10 //
11 // WHO  WHEN        WHAT
12 // ---  ----------  -----------------------------------------------------------
13 // JLH  12/01/2005  Added color TV/monochrome emulation to hi-res code
14 // JLH  12/09/2005  Cleaned up color TV emulation code
15 // JLH  12/09/2005  Fixed lo-res color TV/mono emulation modes
16 //
17 // STILL TO DO:
18 //
19 // - Fix LoRes mode green mono to skip every other scanline instead of fill
20 //   like white mono does [DONE]
21 // - Double HiRes [DONE]
22 // - 80 column text [DONE]
23 // - Fix OSD text display so that it's visible no matter what background is
24 //   there [DONE]
25 //
26
27 // Display routines seem MUCH slower now... !!! INVESTIGATE !!! [not anymore]
28
29 #include "video.h"
30
31 #include <string.h>                                     // for memset()
32 #include <stdio.h>
33 #include <stdarg.h>                                     // for va_* stuff
34 #include "apple2.h"
35 #include "apple2-icon-64x64.h"
36 #include "charset.h"
37 #include "log.h"
38 #include "settings.h"
39 #include "gui/font14pt.h"
40 #include "gui/gui.h"
41
42 /* Reference: Technote tn-iigs-063 "Master Color Values"
43
44           Color  Color Register LR HR  DHR Master Color R,G,B
45           Name       Value      #  #   #      Value
46           ----------------------------------------------------
47           Black       0         0  0,4 0      $0000    (0,0,0)
48 (Magenta) Deep Red    1         1      1      $0D03    (D,0,3)
49           Dark Blue   2         2      8      $0009    (0,0,9)
50  (Violet) Purple      3         3  2   9      $0D2D    (D,2,D)
51           Dark Green  4         4      4      $0072    (0,7,2)
52  (Gray 1) Dark Gray   5         5      5      $0555    (5,5,5)
53    (Blue) Medium Blue 6         6  6   C      $022F    (2,2,F)
54    (Cyan) Light Blue  7         7      D      $06AF    (6,A,F)
55           Brown       8         8      2      $0850    (8,5,0)
56           Orange      9         9  5   3      $0F60    (F,6,0)
57  (Gray 2) Light Gray  A         A      A      $0AAA    (A,A,A)
58           Pink        B         B      B      $0F98    (F,9,8)
59   (Green) Light Green C         C  1   6      $01D0    (1,D,0)
60           Yellow      D         D      7      $0FF0    (F,F,0)
61    (Aqua) Aquamarine  E         E      E      $04F9    (4,F,9)
62           White       F         F  3,7 F      $0FFF    (F,F,F)
63
64    LR: Lo-Res   HR: Hi-Res   DHR: Double Hi-Res
65
66    N.B.: These colors look like shit */
67
68 // Global variables
69
70 bool flash = false;
71 bool textMode = true;
72 bool mixedMode = false;
73 bool displayPage2 = false;
74 bool hiRes = false;
75 bool alternateCharset = false;
76 bool col80Mode = false;
77 SDL_Renderer * sdlRenderer = NULL;
78 SDL_Window * sdlWindow = NULL;
79
80 // Local variables
81
82 static SDL_Texture * sdlTexture = NULL;
83 static uint32_t * scrBuffer;
84 static int scrPitch;
85 static bool showFrameTicks = false;
86
87 // We set up the colors this way so that they'll be endian safe
88 // when we cast them to a uint32_t. Note that the format is RGBA.
89
90 // "Master Color Values" palette
91
92 static uint8_t colors[16 * 4] = {
93         0x00, 0x00, 0x00, 0xFF,                         // Black
94         0xDD, 0x00, 0x33, 0xFF,                         // Deep Red (Magenta)
95         0x00, 0x00, 0x99, 0xFF,                         // Dark Blue
96         0xDD, 0x22, 0xDD, 0xFF,                         // Purple (Violet)
97         0x00, 0x77, 0x22, 0xFF,                         // Dark Green
98         0x55, 0x55, 0x55, 0xFF,                         // Dark Gray (Gray 1)
99         0x22, 0x22, 0xFF, 0xFF,                         // Medium Blue (Blue)
100         0x66, 0xAA, 0xFF, 0xFF,                         // Light Blue (Cyan)
101         0x88, 0x55, 0x00, 0xFF,                         // Brown
102         0xFF, 0x66, 0x00, 0xFF,                         // Orange
103         0xAA, 0xAA, 0xAA, 0xFF,                         // Light Gray (Gray 2)
104         0xFF, 0x99, 0x88, 0xFF,                         // Pink
105         0x11, 0xDD, 0x00, 0xFF,                         // Light Green (Green)
106         0xFF, 0xFF, 0x00, 0xFF,                         // Yellow
107         0x44, 0xFF, 0x99, 0xFF,                         // Aquamarine (Aqua)
108         0xFF, 0xFF, 0xFF, 0xFF                          // White
109 };
110
111 // This palette comes from ApplePC's colors (more realistic to my eye ;-)
112
113 static uint8_t altColors[16 * 4] = {
114         0x00, 0x00, 0x00, 0xFF,
115         0x7D, 0x20, 0x41, 0xFF,
116         0x41, 0x30, 0x7D, 0xFF,
117         0xBE, 0x51, 0xBE, 0xFF,
118         0x00, 0x5D, 0x3C, 0xFF,
119         0x7D, 0x7D, 0x7D, 0xFF,
120         0x41, 0x8E, 0xBA, 0xFF,
121         0xBE, 0xAE, 0xFB, 0xFF,
122         0x3C, 0x4D, 0x00, 0xFF,
123         0xBA, 0x6D, 0x41, 0xFF,
124         0x7D, 0x7D, 0x7D, 0xFF,
125         0xFB, 0x9E, 0xBE, 0xFF,
126         0x3C, 0xAA, 0x3C, 0xFF,
127         0xBA, 0xCB, 0x7D, 0xFF,
128         0x7D, 0xDB, 0xBA, 0xFF,
129         0xFB, 0xFB, 0xFB, 0xFF };
130
131 // Lo-res starting line addresses
132
133 static uint16_t lineAddrLoRes[24] = {
134         0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
135         0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
136         0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 };
137
138 // Hi-res starting line addresses
139
140 static uint16_t lineAddrHiRes[192] = {
141         0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
142         0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,
143         0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,
144         0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,
145
146         0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,
147         0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,
148         0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,
149         0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,
150
151         0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,
152         0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,
153         0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,
154         0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,
155
156         0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,
157         0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,
158         0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,
159         0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,
160
161         0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,
162         0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,
163         0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,
164         0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,
165
166         0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,
167         0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,
168         0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,
169         0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 };
170
171 uint16_t appleHiresToMono[0x200] = {
172         0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
173         0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
174         0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
175         0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
176         0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
177         0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
178         0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
179         0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
180         0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
181         0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
182         0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
183         0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
184         0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
185         0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
186         0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
187         0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
188         0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80,
189         0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x
190         0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98,
191         0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x
192         0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86,
193         0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax
194         0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E,
195         0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx
196         0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81,
197         0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx
198         0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99,
199         0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx
200         0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87,
201         0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex
202         0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F,
203         0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx
204
205         // Second half adds in the previous byte's lo pixel
206
207         0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
208         0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
209         0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
210         0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
211         0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
212         0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
213         0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
214         0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
215         0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
216         0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
217         0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
218         0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
219         0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
220         0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
221         0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
222         0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
223         0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80,
224         0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x
225         0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98,
226         0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x
227         0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86,
228         0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax
229         0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E,
230         0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx
231         0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81,
232         0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx
233         0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99,
234         0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx
235         0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87,
236         0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex
237         0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F,
238         0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF  // $Fx
239 };
240
241 static uint8_t blurTable[0x80][8];                              // Color TV blur table
242 static uint8_t mirrorTable[0x100];
243 static uint32_t * palette = (uint32_t *)altColors;
244 enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
245 static uint8_t screenType = ST_COLOR_TV;
246
247 // Local functions
248
249 static void Render40ColumnTextLine(uint8_t line);
250 static void Render80ColumnTextLine(uint8_t line);
251 static void Render40ColumnText(void);
252 static void Render80ColumnText(void);
253 static void RenderLoRes(uint16_t toLine = 24);
254 static void RenderHiRes(uint16_t toLine = 192);
255 static void RenderDHiRes(uint16_t toLine = 192);
256 static void RenderVideoFrame(/*uint32_t *, int*/);
257
258
259 void SetupBlurTable(void)
260 {
261         // NOTE: This table only needs to be 7 bits wide instead of 11, since the
262         //       last four bits are copies of the previous four...
263         //       Odd. Doing the bit patterns from 0-$7F doesn't work, but going
264         //       from 0-$7FF stepping by 16 does. Hm.
265         //       Well, it seems that going from 0-$7F doesn't have enough precision to do the job.
266         for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
267         {
268                 uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
269
270                 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
271                 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
272                 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
273                 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
274
275                 for(int8_t i=7; i>=0; i--)
276                 {
277                         uint8_t color = (((blurred0 >> i) & 0x01) << 3)
278                                 | (((blurred1 >> i) & 0x01) << 2)
279                                 | (((blurred2 >> i) & 0x01) << 1)
280                                 | ((blurred3 >> i) & 0x01);
281                         blurTable[bitPat >> 4][7 - i] = color;
282                 }
283         }
284
285         for(int i=0; i<256; i++)
286         {
287                 mirrorTable[i] = ((i & 0x01) << 7)
288                         | ((i & 0x02) << 5)
289                         | ((i & 0x04) << 3)
290                         | ((i & 0x08) << 1)
291                         | ((i & 0x10) >> 1)
292                         | ((i & 0x20) >> 3)
293                         | ((i & 0x40) >> 5)
294                         | ((i & 0x80) >> 7);
295         }
296 }
297
298
299 void TogglePalette(void)
300 {
301         if (palette == (uint32_t *)colors)
302         {
303                 palette = (uint32_t *)altColors;
304                 SpawnMessage("Color TV palette");
305         }
306         else
307         {
308                 palette = (uint32_t *)colors;
309                 SpawnMessage("\"Master Color Values\" palette");
310         }
311 }
312
313
314 void CycleScreenTypes(void)
315 {
316         char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
317
318         screenType++;
319
320         if (screenType == ST_LAST_ENTRY)
321                 screenType = ST_FIRST_ENTRY;
322
323         SpawnMessage("%s", scrTypeStr[screenType]);
324 }
325
326
327 void ToggleTickDisplay(void)
328 {
329         showFrameTicks = !showFrameTicks;
330 }
331
332
333 static uint32_t msgTicks = 0;
334 static char message[4096];
335
336 void SpawnMessage(const char * text, ...)
337 {
338         va_list arg;
339
340         va_start(arg, text);
341         vsprintf(message, text, arg);
342         va_end(arg);
343
344         msgTicks = 120;
345 }
346
347
348 static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg);
349 static void DrawString(void)
350 {
351 //This approach works, and seems to be fast enough... Though it probably would
352 //be better to make the oversized font to match this one...
353         for(uint32_t x=7; x<=9; x++)
354                 for(uint32_t y=7; y<=9; y++)
355                         DrawString2(x, y, 0x00000000, message);
356
357         DrawString2(8, 8, 0x0020FF20, message);
358 }
359
360
361 static void DrawString(uint32_t x, uint32_t y, uint32_t color, char * msg)
362 {
363 //This approach works, and seems to be fast enough... Though it probably would
364 //be better to make the oversized font to match this one...
365         for(uint32_t xx=x-1; xx<=x+1; xx++)
366                 for(uint32_t yy=y-1; yy<=y+1; yy++)
367                         DrawString2(xx, yy, 0x00000000, msg);
368
369         DrawString2(x, y, color, msg);
370 }
371
372
373 static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg)
374 {
375         uint32_t length = strlen(msg), address = x + (y * VIRTUAL_SCREEN_WIDTH);
376         uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
377
378         for(uint32_t i=0; i<length; i++)
379         {
380                 uint8_t c = msg[i];
381                 c = (c < 32 ? 0 : c - 32);
382                 uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
383
384                 for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
385                 {
386                         for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
387                         {
388                                 uint8_t trans = font2[fontAddr++];
389
390                                 if (trans)
391                                 {
392                                         uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
393
394                                         uint8_t eBlue = (existingColor >> 16) & 0xFF,
395                                                 eGreen = (existingColor >> 8) & 0xFF,
396                                                 eRed = existingColor & 0xFF;
397
398 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
399 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
400 //because dividing by 32 is faster than dividing by 31...!
401                                         uint8_t invTrans = 255 - trans;
402
403                                         uint32_t bRed   = (eRed   * invTrans + nRed   * trans) / 255;
404                                         uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
405                                         uint32_t bBlue  = (eBlue  * invTrans + nBlue  * trans) / 255;
406
407 //THIS IS NOT ENDIAN SAFE
408 //NB: Setting the alpha channel here does nothing.
409                                         *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
410                                 }
411                         }
412                 }
413
414                 address += FONT_WIDTH;
415         }
416 }
417
418
419 static void DrawFrameTicks(void)
420 {
421         uint32_t color = 0x00FF2020;
422         uint32_t address = 8 + (24 * VIRTUAL_SCREEN_WIDTH);
423
424         for(uint32_t i=0; i<17; i++)
425         {
426                 for(uint32_t yy=0; yy<5; yy++)
427                 {
428                         for(uint32_t xx=0; xx<9; xx++)
429                         {
430 //THIS IS NOT ENDIAN SAFE
431 //NB: Setting the alpha channel here does nothing.
432                                 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000;
433                         }
434                 }
435
436                 address += (5 * VIRTUAL_SCREEN_WIDTH);
437         }
438
439         address = 8 + (24 * VIRTUAL_SCREEN_WIDTH);
440
441         // frameTicks is the amount of time remaining; so to show the amount
442         // consumed, we subtract it from 17.
443         uint32_t bars = 17 - frameTicks;
444
445         if (bars & 0x80000000)
446                 bars = 0;
447
448         for(uint32_t i=0; i<17; i++)
449         {
450                 for(uint32_t yy=1; yy<4; yy++)
451                 {
452                         for(uint32_t xx=1; xx<8; xx++)
453                         {
454 //THIS IS NOT ENDIAN SAFE
455 //NB: Setting the alpha channel here does nothing.
456                                 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = (i < bars ? color : 0x003F0000);
457                         }
458                 }
459
460                 address += (5 * VIRTUAL_SCREEN_WIDTH);
461         }
462
463         static char msg[32];
464
465         if ((frameTimePtr % 15) == 0)
466         {
467 //              uint32_t prevClock = (frameTimePtr + 1) % 60;
468                 uint64_t prevClock = (frameTimePtr + 1) % 60;
469 //              float fps = 59.0f / (((float)frameTime[frameTimePtr] - (float)frameTime[prevClock]) / 1000.0f);
470                 double fps = 59.0 / ((double)(frameTime[frameTimePtr] - frameTime[prevClock]) / (double)SDL_GetPerformanceFrequency());
471                 sprintf(msg, "%.1lf FPS", fps);
472         }
473
474         DrawString(20, 24, color, msg);
475 }
476
477
478 static void Render40ColumnTextLine(uint8_t line)
479 {
480         uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
481
482         for(int x=0; x<40; x++)
483         {
484                 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
485
486                 // Render character at (x, y)
487
488                 for(int cy=0; cy<8; cy++)
489                 {
490                         for(int cx=0; cx<7; cx++)
491                         {
492                                 uint32_t pixel = 0xFF000000;
493
494                                 if (alternateCharset)
495                                 {
496                                         if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
497                                                 pixel = pixelOn;
498
499                                         if (chr < 0x80)
500                                                 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
501
502                                         if ((chr & 0xC0) == 0x40 && flash)
503                                                 pixel = 0xFF000000;
504                                 }
505                                 else
506                                 {
507                                         if (textChar2e[(chr * 56) + cx + (cy * 7)])
508                                                 pixel = pixelOn;
509                                 }
510
511                                 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
512                                 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
513
514                                 // QnD method to get blank alternate lines in text mode
515                                 if (screenType == ST_GREEN_MONO)
516                                         pixel = 0xFF000000;
517
518                                 {
519                                         scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
520                                         scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
521                                 }
522                         }
523                 }
524         }
525 }
526
527
528 static void Render80ColumnTextLine(uint8_t line)
529 {
530         uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
531
532         for(int x=0; x<80; x++)
533         {
534                 uint8_t chr;
535
536                 if (x & 0x01)
537                         chr = ram[lineAddrLoRes[line] + (x >> 1)];
538                 else
539                         chr = ram2[lineAddrLoRes[line] + (x >> 1)];
540
541                 // Render character at (x, y)
542
543                 for(int cy=0; cy<8; cy++)
544                 {
545                         for(int cx=0; cx<7; cx++)
546                         {
547                                 uint32_t pixel = 0xFF000000;
548
549                                 if (alternateCharset)
550                                 {
551                                         if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
552                                                 pixel = pixelOn;
553
554                                         if (chr < 0x80)
555                                                 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
556
557                                         if ((chr & 0xC0) == 0x40 && flash)
558                                                 pixel = 0xFF000000;
559                                 }
560                                 else
561                                 {
562                                         if (textChar2e[(chr * 56) + cx + (cy * 7)])
563                                                 pixel = pixelOn;
564                                 }
565
566                                 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
567
568                                 // QnD method to get blank alternate lines in text mode
569                                 if (screenType == ST_GREEN_MONO)
570                                         pixel = 0xFF000000;
571
572                                 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
573                         }
574                 }
575         }
576 }
577
578
579 static void Render40ColumnText(void)
580 {
581         for(uint8_t line=0; line<24; line++)
582                 Render40ColumnTextLine(line);
583 }
584
585
586 static void Render80ColumnText(void)
587 {
588         for(uint8_t line=0; line<24; line++)
589                 Render80ColumnTextLine(line);
590 }
591
592
593 static void RenderLoRes(uint16_t toLine/*= 24*/)
594 {
595 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
596 //       Also, we could set up three different Render functions depending on which
597 //       render type was set and call it with a function pointer. Would be faster
598 //       then the nested ifs we have now.
599 /*
600 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
601 Color #s correspond to the bit patterns in reverse... Interesting!
602
603 00 00 00 ->  0 [0000] -> 0 (lores color #)
604 3c 4d 00 ->  8 [0001] -> 8?             BROWN
605 00 5d 3c ->  4 [0010] -> 4?             DARK GREEN
606 3c aa 3c -> 12 [0011] -> 12?    LIGHT GREEN (GREEN)
607 41 30 7d ->  2 [0100] -> 2?             DARK BLUE
608 7d 7d 7d -> 10 [0101] -> 10?    LIGHT GRAY
609 41 8e ba ->  6 [0110] -> 6?             MEDIUM BLUE (BLUE)
610 7d db ba -> 14 [0111] -> 14?    AQUAMARINE (AQUA)
611 7d 20 41 ->  1 [1000] -> 1?             DEEP RED (MAGENTA)
612 ba 6d 41 ->  9 [1001] -> 9?             ORANGE
613 7d 7d 7d ->  5 [1010] -> 5?             DARK GRAY
614 ba cb 7d -> 13 [1011] -> 13?    YELLOW
615 be 51 be ->  3 [1100] -> 3              PURPLE (VIOLET)
616 fb 9e be -> 11 [1101] -> 11?    PINK
617 be ae fb ->  7 [1110] -> 7?             LIGHT BLUE (CYAN)
618 fb fb fb -> 15 [1111] -> 15             WHITE
619 */
620         uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
621
622 //This is the old "perfect monitor" rendering code...
623 /*      if (screenType != ST_COLOR_TV) // Not correct, but for now...
624 //if (1)
625         {
626                 for(uint16_t y=0; y<toLine; y++)
627                 {
628                         for(uint16_t x=0; x<40; x++)
629                         {
630                                 uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
631                                 uint32_t pixel = palette[scrByte & 0x0F];
632
633                                 for(int cy=0; cy<4; cy++)
634                                         for(int cx=0; cx<14; cx++)
635                                                 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
636
637                                 pixel = palette[scrByte >> 4];
638
639                                 for(int cy=4; cy<8; cy++)
640                                         for(int cx=0; cx<14; cx++)
641                                                 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
642                         }
643                 }
644         }
645         else//*/
646
647         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
648
649         for(uint16_t y=0; y<toLine; y++)
650         {
651                 // Do top half of lores screen bytes...
652
653                 uint32_t previous3Bits = 0;
654
655                 for(uint16_t x=0; x<40; x+=2)
656                 {
657                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
658                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
659                         scrByte1 = mirrorNybble[scrByte1];
660                         scrByte2 = mirrorNybble[scrByte2];
661                         // This is just a guess, but it'll have to do for now...
662                         uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
663                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
664                                 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
665
666                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
667                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
668                         // 31   27   23   19   15    11   7    3  0
669
670                         if (screenType == ST_COLOR_TV)
671                         {
672                                 for(uint8_t i=0; i<7; i++)
673                                 {
674                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
675                                         pixels <<= 4;
676
677                                         for(uint8_t j=0; j<4; j++)
678                                         {
679                                                 uint8_t color = blurTable[bitPat][j];
680
681                                                 for(uint32_t cy=0; cy<8; cy++)
682                                                 {
683                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
684 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
685                                                 }
686                                         }
687                                 }
688
689                                 previous3Bits = pixels & 0x70000000;
690                         }
691                         else
692                         {
693                                 for(int j=0; j<28; j++)
694                                 {
695                                         for(uint32_t cy=0; cy<8; cy++)
696                                         {
697                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
698 //                                              scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
699                                         }
700
701                                         pixels <<= 1;
702                                 }
703                         }
704                 }
705
706                 // Now do bottom half...
707
708                 previous3Bits = 0;
709
710                 for(uint16_t x=0; x<40; x+=2)
711                 {
712                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
713                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
714                         scrByte1 = mirrorNybble[scrByte1];
715                         scrByte2 = mirrorNybble[scrByte2];
716                         // This is just a guess, but it'll have to do for now...
717                         uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
718                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
719                                 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
720
721                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
722                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
723                         // 31   27   23   19   15    11   7    3  0
724
725                         if (screenType == ST_COLOR_TV)
726                         {
727                                 for(uint8_t i=0; i<7; i++)
728                                 {
729                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
730                                         pixels <<= 4;
731
732                                         for(uint8_t j=0; j<4; j++)
733                                         {
734                                                 uint8_t color = blurTable[bitPat][j];
735
736                                                 for(uint32_t cy=8; cy<16; cy++)
737                                                 {
738                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
739 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
740                                                 }
741                                         }
742                                 }
743
744                                 previous3Bits = pixels & 0x70000000;
745                         }
746                         else
747                         {
748                                 for(int j=0; j<28; j++)
749                                 {
750                                         for(uint32_t cy=8; cy<16; cy++)
751                                         {
752                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
753 //                                              scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
754                                         }
755
756                                         pixels <<= 1;
757                                 }
758                         }
759                 }
760         }
761 }
762
763
764 //
765 // Render the Double Lo Res screen (HIRES off, DHIRES on)
766 //
767 static void RenderDLoRes(void)
768 {
769 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
770 //       Also, we could set up three different Render functions depending on
771 //       which render type was set and call it with a function pointer. Would be
772 //       faster then the nested ifs we have now.
773 /*
774 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
775 Color #s correspond to the bit patterns in reverse... Interesting! [It's because
776 the video generator reads the bit patters from bit 0--which makes them backwards
777 from the normal POV.]
778
779 00 00 00 ->  0 [0000] -> 0 (lores color #)
780 3C 4D 00 ->  8 [0001] -> 8?             BROWN
781 00 5D 3C ->  4 [0010] -> 4?             DARK GREEN
782 3C AA 3C -> 12 [0011] -> 12?    LIGHT GREEN (GREEN)
783 41 30 7D ->  2 [0100] -> 2?             DARK BLUE
784 7D 7D 7D -> 10 [0101] -> 10?    LIGHT GRAY (Grays are identical)
785 41 8E BA ->  6 [0110] -> 6?             MEDIUM BLUE (BLUE)
786 7D DB BA -> 14 [0111] -> 14?    AQUAMARINE (AQUA)
787 7D 20 41 ->  1 [1000] -> 1?             DEEP RED (MAGENTA)
788 BA 6D 41 ->  9 [1001] -> 9?             ORANGE
789 7D 7D 7D ->  5 [1010] -> 5?             DARK GRAY (Grays are identical)
790 BA CB 7D -> 13 [1011] -> 13?    YELLOW
791 BE 51 BE ->  3 [1100] -> 3              PURPLE (VIOLET)
792 FB 9E BE -> 11 [1101] -> 11?    PINK
793 BE AE FB ->  7 [1110] -> 7?             LIGHT BLUE (CYAN)
794 FB FB FB -> 15 [1111] -> 15             WHITE
795 */
796         uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
797         // Rotated one bit right (in the nybble)--right instead of left because
798         // these are backwards after all :-P
799         uint8_t mirrorNybble2[16] = { 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15 };
800         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
801
802         for(uint16_t y=0; y<24; y++)
803         {
804                 // Do top half of double lores screen bytes...
805
806                 uint32_t previous3Bits = 0;
807
808                 for(uint16_t x=0; x<40; x+=2)
809                 {
810                         uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] & 0x0F;
811                         uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] & 0x0F;
812                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] & 0x0F;
813                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] & 0x0F;
814                         scrByte1 = mirrorNybble[scrByte1];
815                         scrByte2 = mirrorNybble[scrByte2];
816                         scrByte3 = mirrorNybble2[scrByte3];
817                         scrByte4 = mirrorNybble2[scrByte4];
818                         // This is just a guess, but it'll have to do for now...
819                         uint32_t pixels = previous3Bits | (scrByte3 << 24)
820                                 | (scrByte3 << 20) | (scrByte1 << 16)
821                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
822                                 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
823
824                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
825                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
826                         // 31   27   23   19   15    11   7    3  0
827
828                         if (screenType == ST_COLOR_TV)
829                         {
830                                 for(uint8_t i=0; i<7; i++)
831                                 {
832                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
833                                         pixels <<= 4;
834
835                                         for(uint8_t j=0; j<4; j++)
836                                         {
837                                                 uint8_t color = blurTable[bitPat][j];
838
839                                                 for(uint32_t cy=0; cy<8; cy++)
840                                                 {
841                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
842 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
843                                                 }
844                                         }
845                                 }
846
847                                 previous3Bits = pixels & 0x70000000;
848                         }
849                         else
850                         {
851                                 for(int j=0; j<28; j++)
852                                 {
853                                         for(uint32_t cy=0; cy<8; cy++)
854                                         {
855                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
856                                         }
857
858                                         pixels <<= 1;
859                                 }
860                         }
861                 }
862
863                 // Now do bottom half...
864
865                 previous3Bits = 0;
866
867                 for(uint16_t x=0; x<40; x+=2)
868                 {
869                         uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] >> 4;
870                         uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] >> 4;
871                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] >> 4;
872                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] >> 4;
873                         scrByte1 = mirrorNybble[scrByte1];
874                         scrByte2 = mirrorNybble[scrByte2];
875                         scrByte3 = mirrorNybble2[scrByte3];
876                         scrByte4 = mirrorNybble2[scrByte4];
877                         // This is just a guess, but it'll have to do for now...
878 //                      uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
879 //                              | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
880 //                              | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
881                         uint32_t pixels = previous3Bits | (scrByte3 << 24)
882                                 | (scrByte3 << 20) | (scrByte1 << 16)
883                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
884                                 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
885
886                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
887                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
888                         // 31   27   23   19   15    11   7    3  0
889
890                         if (screenType == ST_COLOR_TV)
891                         {
892                                 for(uint8_t i=0; i<7; i++)
893                                 {
894                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
895                                         pixels <<= 4;
896
897                                         for(uint8_t j=0; j<4; j++)
898                                         {
899                                                 uint8_t color = blurTable[bitPat][j];
900
901                                                 for(uint32_t cy=8; cy<16; cy++)
902                                                 {
903                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
904                                                 }
905                                         }
906                                 }
907
908                                 previous3Bits = pixels & 0x70000000;
909                         }
910                         else
911                         {
912                                 for(int j=0; j<28; j++)
913                                 {
914                                         for(uint32_t cy=8; cy<16; cy++)
915                                         {
916                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
917                                         }
918
919                                         pixels <<= 1;
920                                 }
921                         }
922                 }
923         }
924 }
925
926
927 static void RenderHiRes(uint16_t toLine/*= 192*/)
928 {
929 //printf("RenderHiRes to line %u\n", toLine);
930 // NOTE: Not endian safe. !!! FIX !!! [DONE]
931 #if 0
932         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
933 #else
934 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
935 // The colors are set in the 8-bit array as R G B A
936         uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
937         uint32_t * colorPtr = (uint32_t *)monoColors;
938         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
939 #endif
940
941         for(uint16_t y=0; y<toLine; y++)
942         {
943                 uint16_t previousLoPixel = 0;
944                 uint32_t previous3bits = 0;
945
946                 for(uint16_t x=0; x<40; x+=2)
947                 {
948                         uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
949                         uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
950                         previousLoPixel = (screenByte << 2) & 0x0100;
951
952                         screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
953                         uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
954                         previousLoPixel = (screenByte << 2) & 0x0100;
955
956                         pixels = previous3bits | (pixels << 14) | pixels2;
957
958 //testing (this shows on the screen, so it's OK)
959 //if (x == 0)
960 //{
961 //      pixels = 0x7FFFFFFF;
962 //}
963
964                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
965                         // 0ppp 1111 1111 1111 1111 1111 1111 1111
966                         // 31   27   23   19   15   11   7    3  0
967
968                         if (screenType == ST_COLOR_TV)
969                         {
970                                 for(uint8_t i=0; i<7; i++)
971                                 {
972                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
973                                         pixels <<= 4;
974
975                                         for(uint8_t j=0; j<4; j++)
976                                         {
977                                                 uint8_t color = blurTable[bitPat][j];
978 #if 0
979 //This doesn't seem to make things go any faster...
980 //It's the OpenGL render that's faster... Hmm...
981                                                 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
982 #else
983                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
984                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
985 #endif
986                                         }
987                                 }
988
989                                 previous3bits = pixels & 0x70000000;
990                         }
991                         else
992                         {
993                                 for(int j=0; j<28; j++)
994                                 {
995                                         scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
996
997                                         if (screenType == ST_GREEN_MONO)
998                                                 pixels &= 0x07FFFFFF;
999
1000                                         scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1001                                         pixels <<= 1;
1002                                 }
1003                         }
1004                 }
1005         }
1006 }
1007
1008
1009 static void RenderDHiRes(uint16_t toLine/*= 192*/)
1010 {
1011 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
1012 // The colors are set in the 8-bit array as R G B A
1013         uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
1014         uint32_t * colorPtr = (uint32_t *)monoColors;
1015         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
1016
1017         for(uint16_t y=0; y<toLine; y++)
1018         {
1019                 uint32_t previous4bits = 0;
1020
1021                 for(uint16_t x=0; x<40; x+=2)
1022                 {
1023                         uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1024                         uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
1025                         screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1026                         pixels = pixels | (mirrorTable[screenByte & 0x7F]);
1027                         screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1028                         pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
1029                         screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1030                         pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
1031                         pixels = previous4bits | (pixels >> 1);
1032
1033                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
1034                         // 0ppp 1111 1111 1111 1111 1111 1111 1111
1035                         // 31   27   23   19   15   11   7    3  0
1036
1037                         if (screenType == ST_COLOR_TV)
1038                         {
1039                                 for(uint8_t i=0; i<7; i++)
1040                                 {
1041                                         uint8_t bitPat = (pixels & 0xFE000000) >> 25;
1042                                         pixels <<= 4;
1043
1044                                         for(uint8_t j=0; j<4; j++)
1045                                         {
1046                                                 uint32_t color = palette[blurTable[bitPat][j]];
1047                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
1048                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
1049                                         }
1050                                 }
1051
1052                                 previous4bits = pixels & 0xF0000000;
1053                         }
1054                         else
1055                         {
1056                                 for(int j=0; j<28; j++)
1057                                 {
1058                                         scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1059
1060                                         if (screenType == ST_GREEN_MONO)
1061                                                 pixels &= 0x07FFFFFF;
1062
1063                                         scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1064                                         pixels <<= 1;
1065                                 }
1066                         }
1067                 }
1068         }
1069 }
1070
1071
1072 void RenderVideoFrame(void)
1073 {
1074         if (GUI::powerOnState == true)
1075         {
1076                 if (textMode)
1077                 {
1078                         if (!col80Mode)
1079                                 Render40ColumnText();
1080                         else
1081                                 Render80ColumnText();
1082                 }
1083                 else
1084                 {
1085                         if (mixedMode)
1086                         {
1087                                 if (hiRes)
1088                                 {
1089                                         RenderHiRes(160);
1090                                         Render40ColumnTextLine(20);
1091                                         Render40ColumnTextLine(21);
1092                                         Render40ColumnTextLine(22);
1093                                         Render40ColumnTextLine(23);
1094                                 }
1095                                 else
1096                                 {
1097                                         RenderLoRes(20);
1098                                         Render40ColumnTextLine(20);
1099                                         Render40ColumnTextLine(21);
1100                                         Render40ColumnTextLine(22);
1101                                         Render40ColumnTextLine(23);
1102                                 }
1103                         }
1104                         else
1105                         {
1106                                 if (dhires)
1107                                 {
1108                                         if (hiRes)
1109                                                 RenderDHiRes();
1110                                         else
1111                                                 RenderDLoRes();
1112                                 }
1113                                 else if (hiRes)
1114                                         RenderHiRes();
1115                                 else
1116                                         RenderLoRes();
1117                         }
1118                 }
1119         }
1120         else
1121         {
1122                 memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
1123         }
1124
1125         if (msgTicks)
1126         {
1127                 DrawString();
1128                 msgTicks--;
1129         }
1130
1131         if (showFrameTicks)
1132                 DrawFrameTicks();
1133 }
1134
1135
1136 //
1137 // Prime SDL and create surfaces
1138 //
1139 bool InitVideo(void)
1140 {
1141         if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) != 0)
1142         {
1143                 WriteLog("Video: Could not initialize the SDL library: %s\n", SDL_GetError());
1144                 return false;
1145         }
1146
1147 #if 0
1148         int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0, &sdlWindow, &sdlRenderer);
1149 //      int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 1, VIRTUAL_SCREEN_HEIGHT * 1, 0, &sdlWindow, &sdlRenderer);
1150
1151         if (retVal != 0)
1152         {
1153                 WriteLog("Video: Could not create window and/or renderer: %s\n", SDL_GetError());
1154                 return false;
1155         }
1156 #else
1157         sdlWindow = SDL_CreateWindow("Apple2", settings.winX, settings.winY, VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0);
1158
1159         if (sdlWindow == NULL)
1160         {
1161                 WriteLog("Video: Could not create window: %s\n", SDL_GetError());
1162                 return false;
1163         }
1164
1165         sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
1166
1167         if (sdlRenderer == NULL)
1168         {
1169                 WriteLog("Video: Could not create renderer: %s\n", SDL_GetError());
1170                 return false;
1171         }
1172 #endif
1173
1174         // Make sure what we put there is what we get:
1175         SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
1176         SDL_RenderSetLogicalSize(sdlRenderer, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1177
1178         // Set the application's icon & title...
1179         SDL_Surface * iconSurface = SDL_CreateRGBSurfaceFrom(icon, 64, 64, 32, 64*4, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
1180         SDL_SetWindowIcon(sdlWindow, iconSurface);
1181         SDL_FreeSurface(iconSurface);
1182         SDL_SetWindowTitle(sdlWindow, "Apple2 Emulator");
1183
1184         sdlTexture = SDL_CreateTexture(sdlRenderer,
1185                 SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
1186                 VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1187
1188         SetupBlurTable();
1189
1190         WriteLog("Video: Successfully initialized.\n");
1191         return true;
1192 }
1193
1194
1195 //
1196 // Free various SDL components
1197 //
1198 void VideoDone(void)
1199 {
1200         WriteLog("Video: Shutting down SDL...\n");
1201         SDL_DestroyTexture(sdlTexture);
1202         SDL_DestroyRenderer(sdlRenderer);
1203         SDL_DestroyWindow(sdlWindow);
1204         SDL_Quit();
1205         WriteLog("Video: Done.\n");
1206 }
1207
1208
1209 //
1210 // Render the Apple video screen to the primary texture
1211 //
1212 void RenderAppleScreen(SDL_Renderer * renderer)
1213 {
1214         SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch);
1215         RenderVideoFrame();
1216         SDL_UnlockTexture(sdlTexture);
1217         SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
1218 }
1219
1220
1221 //
1222 // Fullscreen <-> window switching
1223 //
1224 void ToggleFullScreen(void)
1225 {
1226         settings.fullscreen = !settings.fullscreen;
1227
1228         int retVal = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1229
1230         if (retVal != 0)
1231                 WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());
1232 }
1233