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