]> Shamusworld >> Repos - apple2/blob - src/video.cpp
e027dfa1485d0fb964114d45a5f23e07524270cb
[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 RenderDLoRes(uint16_t toLine = 24);
255 static void RenderHiRes(uint16_t toLine = 192);
256 static void RenderDHiRes(uint16_t toLine = 192);
257 static void RenderVideoFrame(/*uint32_t *, int*/);
258
259
260 void SetupBlurTable(void)
261 {
262         // NOTE: This table only needs to be 7 bits wide instead of 11, since the
263         //       last four bits are copies of the previous four...
264         //       Odd. Doing the bit patterns from 0-$7F doesn't work, but going
265         //       from 0-$7FF stepping by 16 does. Hm.
266         //       Well, it seems that going from 0-$7F doesn't have enough precision to do the job.
267         for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
268         {
269                 uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
270
271                 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
272                 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
273                 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
274                 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
275
276                 for(int8_t i=7; i>=0; i--)
277                 {
278                         uint8_t color = (((blurred0 >> i) & 0x01) << 3)
279                                 | (((blurred1 >> i) & 0x01) << 2)
280                                 | (((blurred2 >> i) & 0x01) << 1)
281                                 | ((blurred3 >> i) & 0x01);
282                         blurTable[bitPat >> 4][7 - i] = color;
283                 }
284         }
285
286         for(int i=0; i<256; i++)
287         {
288                 mirrorTable[i] = ((i & 0x01) << 7)
289                         | ((i & 0x02) << 5)
290                         | ((i & 0x04) << 3)
291                         | ((i & 0x08) << 1)
292                         | ((i & 0x10) >> 1)
293                         | ((i & 0x20) >> 3)
294                         | ((i & 0x40) >> 5)
295                         | ((i & 0x80) >> 7);
296         }
297 }
298
299
300 void TogglePalette(void)
301 {
302         if (palette == (uint32_t *)colors)
303         {
304                 palette = (uint32_t *)altColors;
305                 SpawnMessage("Color TV palette");
306         }
307         else
308         {
309                 palette = (uint32_t *)colors;
310                 SpawnMessage("\"Master Color Values\" palette");
311         }
312 }
313
314
315 void CycleScreenTypes(void)
316 {
317         char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
318
319         screenType++;
320
321         if (screenType == ST_LAST_ENTRY)
322                 screenType = ST_FIRST_ENTRY;
323
324         SpawnMessage("%s", scrTypeStr[screenType]);
325 }
326
327
328 void ToggleTickDisplay(void)
329 {
330         showFrameTicks = !showFrameTicks;
331 }
332
333
334 static uint32_t msgTicks = 0;
335 static char message[4096];
336
337 void SpawnMessage(const char * text, ...)
338 {
339         va_list arg;
340
341         va_start(arg, text);
342         vsprintf(message, text, arg);
343         va_end(arg);
344
345         msgTicks = 120;
346 //WriteLog("\n%s\n", message);
347 }
348
349
350 static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg);
351 static void DrawString(void)
352 {
353 //This approach works, and seems to be fast enough... Though it probably would
354 //be better to make the oversized font to match this one...
355         for(uint32_t x=7; x<=9; x++)
356                 for(uint32_t y=7; y<=9; y++)
357                         DrawString2(x, y, 0x00000000, message);
358
359         DrawString2(8, 8, 0x0020FF20, message);
360 }
361
362
363 static void DrawString(uint32_t x, uint32_t y, uint32_t color, char * msg)
364 {
365 //This approach works, and seems to be fast enough... Though it probably would
366 //be better to make the oversized font to match this one...
367         for(uint32_t xx=x-1; xx<=x+1; xx++)
368                 for(uint32_t yy=y-1; yy<=y+1; yy++)
369                         DrawString2(xx, yy, 0x00000000, msg);
370
371         DrawString2(x, y, color, msg);
372 }
373
374
375 static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg)
376 {
377         uint32_t length = strlen(msg), address = x + (y * VIRTUAL_SCREEN_WIDTH);
378         uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
379
380         for(uint32_t i=0; i<length; i++)
381         {
382                 uint8_t c = msg[i];
383                 c = (c < 32 ? 0 : c - 32);
384                 uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
385
386                 for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
387                 {
388                         for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
389                         {
390                                 uint8_t trans = font2[fontAddr++];
391
392                                 if (trans)
393                                 {
394                                         uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
395
396                                         uint8_t eBlue = (existingColor >> 16) & 0xFF,
397                                                 eGreen = (existingColor >> 8) & 0xFF,
398                                                 eRed = existingColor & 0xFF;
399
400 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
401 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
402 //because dividing by 32 is faster than dividing by 31...!
403                                         uint8_t invTrans = 255 - trans;
404
405                                         uint32_t bRed   = (eRed   * invTrans + nRed   * trans) / 255;
406                                         uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
407                                         uint32_t bBlue  = (eBlue  * invTrans + nBlue  * trans) / 255;
408
409 //THIS IS NOT ENDIAN SAFE
410 //NB: Setting the alpha channel here does nothing.
411                                         *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
412                                 }
413                         }
414                 }
415
416                 address += FONT_WIDTH;
417         }
418 }
419
420
421 static void DrawFrameTicks(void)
422 {
423         uint32_t color = 0x00FF2020;
424         uint32_t address = 8 + (24 * VIRTUAL_SCREEN_WIDTH);
425
426         for(uint32_t i=0; i<17; i++)
427         {
428                 for(uint32_t yy=0; yy<5; yy++)
429                 {
430                         for(uint32_t xx=0; xx<9; xx++)
431                         {
432 //THIS IS NOT ENDIAN SAFE
433 //NB: Setting the alpha channel here does nothing.
434                                 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000;
435                         }
436                 }
437
438                 address += (5 * VIRTUAL_SCREEN_WIDTH);
439         }
440
441         address = 8 + (24 * VIRTUAL_SCREEN_WIDTH);
442
443         // frameTicks is the amount of time remaining; so to show the amount
444         // consumed, we subtract it from 17.
445         uint32_t bars = 17 - frameTicks;
446
447         if (bars & 0x80000000)
448                 bars = 0;
449
450         for(uint32_t i=0; i<17; i++)
451         {
452                 for(uint32_t yy=1; yy<4; yy++)
453                 {
454                         for(uint32_t xx=1; xx<8; xx++)
455                         {
456 //THIS IS NOT ENDIAN SAFE
457 //NB: Setting the alpha channel here does nothing.
458                                 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = (i < bars ? color : 0x003F0000);
459                         }
460                 }
461
462                 address += (5 * VIRTUAL_SCREEN_WIDTH);
463         }
464
465         static char msg[32];
466
467         if ((frameTimePtr % 15) == 0)
468         {
469 //              uint32_t prevClock = (frameTimePtr + 1) % 60;
470                 uint64_t prevClock = (frameTimePtr + 1) % 60;
471 //              float fps = 59.0f / (((float)frameTime[frameTimePtr] - (float)frameTime[prevClock]) / 1000.0f);
472                 double fps = 59.0 / ((double)(frameTime[frameTimePtr] - frameTime[prevClock]) / (double)SDL_GetPerformanceFrequency());
473                 sprintf(msg, "%.1lf FPS", fps);
474         }
475
476         DrawString(20, 24, color, msg);
477 }
478
479
480 static void Render40ColumnTextLine(uint8_t line)
481 {
482         uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
483
484         for(int x=0; x<40; x++)
485         {
486                 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
487
488                 // Render character at (x, y)
489
490                 for(int cy=0; cy<8; cy++)
491                 {
492                         for(int cx=0; cx<7; cx++)
493                         {
494                                 uint32_t pixel = 0xFF000000;
495
496                                 if (alternateCharset)
497                                 {
498                                         if (textChar2e[(chr * 56) + cx + (cy * 7)])
499                                                 pixel = pixelOn;
500                                 }
501                                 else
502                                 {
503                                         if ((chr & 0xC0) == 0x40)
504                                         {
505                                                 if (textChar2e[((chr & 0x3F) * 56) + cx + (cy * 7)])
506                                                         pixel = pixelOn;
507
508                                                 if (flash)
509                                                         pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
510                                         }
511                                         else
512                                         {
513                                                 if (textChar2e[(chr * 56) + cx + (cy * 7)])
514                                                         pixel = pixelOn;
515                                         }
516                                 }
517
518                                 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
519                                 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
520
521                                 // QnD method to get blank alternate lines in text mode
522                                 if (screenType == ST_GREEN_MONO)
523                                         pixel = 0xFF000000;
524
525                                 {
526                                         scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
527                                         scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
528                                 }
529                         }
530                 }
531         }
532 }
533
534
535 static void Render80ColumnTextLine(uint8_t line)
536 {
537         uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
538
539         for(int x=0; x<80; x++)
540         {
541                 uint8_t chr;
542
543                 if (x & 0x01)
544                         chr = ram[lineAddrLoRes[line] + (x >> 1)];
545                 else
546                         chr = ram2[lineAddrLoRes[line] + (x >> 1)];
547
548                 // Render character at (x, y)
549
550                 for(int cy=0; cy<8; cy++)
551                 {
552                         for(int cx=0; cx<7; cx++)
553                         {
554                                 uint32_t pixel = 0xFF000000;
555
556                                 if (alternateCharset)
557                                 {
558                                         if (textChar2e[(chr * 56) + cx + (cy * 7)])
559                                                 pixel = pixelOn;
560                                 }
561                                 else
562                                 {
563                                         if ((chr & 0xC0) == 0x40)
564                                         {
565                                                 if (textChar2e[((chr & 0x3F) * 56) + cx + (cy * 7)])
566                                                         pixel = pixelOn;
567
568                                                 if (flash)
569                                                         pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
570                                         }
571                                         else
572                                         {
573                                                 if (textChar2e[(chr * 56) + cx + (cy * 7)])
574                                                         pixel = pixelOn;
575                                         }
576                                 }
577
578                                 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
579
580                                 // QnD method to get blank alternate lines in text mode
581                                 if (screenType == ST_GREEN_MONO)
582                                         pixel = 0xFF000000;
583
584                                 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
585                         }
586                 }
587         }
588 }
589
590
591 static void Render40ColumnText(void)
592 {
593         for(uint8_t line=0; line<24; line++)
594                 Render40ColumnTextLine(line);
595 }
596
597
598 static void Render80ColumnText(void)
599 {
600         for(uint8_t line=0; line<24; line++)
601                 Render80ColumnTextLine(line);
602 }
603
604
605 static void RenderLoRes(uint16_t toLine/*= 24*/)
606 {
607 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
608 //       Also, we could set up three different Render functions depending on which
609 //       render type was set and call it with a function pointer. Would be faster
610 //       then the nested ifs we have now.
611 /*
612 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
613 Color #s correspond to the bit patterns in reverse... Interesting!
614
615 00 00 00 ->  0 [0000] -> 0 (lores color #)
616 3c 4d 00 ->  8 [0001] -> 8?             BROWN
617 00 5d 3c ->  4 [0010] -> 4?             DARK GREEN
618 3c aa 3c -> 12 [0011] -> 12?    LIGHT GREEN (GREEN)
619 41 30 7d ->  2 [0100] -> 2?             DARK BLUE
620 7d 7d 7d -> 10 [0101] -> 10?    LIGHT GRAY
621 41 8e ba ->  6 [0110] -> 6?             MEDIUM BLUE (BLUE)
622 7d db ba -> 14 [0111] -> 14?    AQUAMARINE (AQUA)
623 7d 20 41 ->  1 [1000] -> 1?             DEEP RED (MAGENTA)
624 ba 6d 41 ->  9 [1001] -> 9?             ORANGE
625 7d 7d 7d ->  5 [1010] -> 5?             DARK GRAY
626 ba cb 7d -> 13 [1011] -> 13?    YELLOW
627 be 51 be ->  3 [1100] -> 3              PURPLE (VIOLET)
628 fb 9e be -> 11 [1101] -> 11?    PINK
629 be ae fb ->  7 [1110] -> 7?             LIGHT BLUE (CYAN)
630 fb fb fb -> 15 [1111] -> 15             WHITE
631 */
632         uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
633
634 //This is the old "perfect monitor" rendering code...
635 /*      if (screenType != ST_COLOR_TV) // Not correct, but for now...
636 //if (1)
637         {
638                 for(uint16_t y=0; y<toLine; y++)
639                 {
640                         for(uint16_t x=0; x<40; x++)
641                         {
642                                 uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
643                                 uint32_t pixel = palette[scrByte & 0x0F];
644
645                                 for(int cy=0; cy<4; cy++)
646                                         for(int cx=0; cx<14; cx++)
647                                                 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
648
649                                 pixel = palette[scrByte >> 4];
650
651                                 for(int cy=4; cy<8; cy++)
652                                         for(int cx=0; cx<14; cx++)
653                                                 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
654                         }
655                 }
656         }
657         else//*/
658
659         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
660
661         for(uint16_t y=0; y<toLine; y++)
662         {
663                 // Do top half of lores screen bytes...
664
665                 uint32_t previous3Bits = 0;
666
667                 for(uint16_t x=0; x<40; x+=2)
668                 {
669                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
670                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
671                         scrByte1 = mirrorNybble[scrByte1];
672                         scrByte2 = mirrorNybble[scrByte2];
673                         // This is just a guess, but it'll have to do for now...
674                         uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
675                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
676                                 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
677
678                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
679                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
680                         // 31   27   23   19   15    11   7    3  0
681
682                         if (screenType == ST_COLOR_TV)
683                         {
684                                 for(uint8_t i=0; i<7; i++)
685                                 {
686                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
687                                         pixels <<= 4;
688
689                                         for(uint8_t j=0; j<4; j++)
690                                         {
691                                                 uint8_t color = blurTable[bitPat][j];
692
693                                                 for(uint32_t cy=0; cy<8; cy++)
694                                                 {
695                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
696 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
697                                                 }
698                                         }
699                                 }
700
701                                 previous3Bits = pixels & 0x70000000;
702                         }
703                         else
704                         {
705                                 for(int j=0; j<28; j++)
706                                 {
707                                         for(uint32_t cy=0; cy<8; cy++)
708                                         {
709                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
710 //                                              scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
711                                         }
712
713                                         pixels <<= 1;
714                                 }
715                         }
716                 }
717
718                 // Now do bottom half...
719
720                 previous3Bits = 0;
721
722                 for(uint16_t x=0; x<40; x+=2)
723                 {
724                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
725                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
726                         scrByte1 = mirrorNybble[scrByte1];
727                         scrByte2 = mirrorNybble[scrByte2];
728                         // This is just a guess, but it'll have to do for now...
729                         uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
730                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
731                                 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
732
733                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
734                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
735                         // 31   27   23   19   15    11   7    3  0
736
737                         if (screenType == ST_COLOR_TV)
738                         {
739                                 for(uint8_t i=0; i<7; i++)
740                                 {
741                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
742                                         pixels <<= 4;
743
744                                         for(uint8_t j=0; j<4; j++)
745                                         {
746                                                 uint8_t color = blurTable[bitPat][j];
747
748                                                 for(uint32_t cy=8; cy<16; cy++)
749                                                 {
750                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
751 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
752                                                 }
753                                         }
754                                 }
755
756                                 previous3Bits = pixels & 0x70000000;
757                         }
758                         else
759                         {
760                                 for(int j=0; j<28; j++)
761                                 {
762                                         for(uint32_t cy=8; cy<16; cy++)
763                                         {
764                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
765 //                                              scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
766                                         }
767
768                                         pixels <<= 1;
769                                 }
770                         }
771                 }
772         }
773 }
774
775
776 //
777 // Render the Double Lo Res screen (HIRES off, DHIRES on)
778 //
779 static void RenderDLoRes(uint16_t toLine/*= 24*/)
780 {
781 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
782 //       Also, we could set up three different Render functions depending on
783 //       which render type was set and call it with a function pointer. Would be
784 //       faster then the nested ifs we have now.
785 /*
786 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
787 Color #s correspond to the bit patterns in reverse... Interesting! [It's because
788 the video generator reads the bit patters from bit 0--which makes them backwards
789 from the normal POV.]
790
791 00 00 00 ->  0 [0000] -> 0 (lores color #)
792 3C 4D 00 ->  8 [0001] -> 8?             BROWN
793 00 5D 3C ->  4 [0010] -> 4?             DARK GREEN
794 3C AA 3C -> 12 [0011] -> 12?    LIGHT GREEN (GREEN)
795 41 30 7D ->  2 [0100] -> 2?             DARK BLUE
796 7D 7D 7D -> 10 [0101] -> 10?    LIGHT GRAY (Grays are identical)
797 41 8E BA ->  6 [0110] -> 6?             MEDIUM BLUE (BLUE)
798 7D DB BA -> 14 [0111] -> 14?    AQUAMARINE (AQUA)
799 7D 20 41 ->  1 [1000] -> 1?             DEEP RED (MAGENTA)
800 BA 6D 41 ->  9 [1001] -> 9?             ORANGE
801 7D 7D 7D ->  5 [1010] -> 5?             DARK GRAY (Grays are identical)
802 BA CB 7D -> 13 [1011] -> 13?    YELLOW
803 BE 51 BE ->  3 [1100] -> 3              PURPLE (VIOLET)
804 FB 9E BE -> 11 [1101] -> 11?    PINK
805 BE AE FB ->  7 [1110] -> 7?             LIGHT BLUE (CYAN)
806 FB FB FB -> 15 [1111] -> 15             WHITE
807 */
808         uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
809         // Rotated one bit right (in the nybble)--right instead of left because
810         // these are backwards after all :-P
811         uint8_t mirrorNybble2[16] = { 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15 };
812         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
813
814         for(uint16_t y=0; y<toLine; y++)
815         {
816                 // Do top half of double lores screen bytes...
817
818                 uint32_t previous3Bits = 0;
819
820                 for(uint16_t x=0; x<40; x+=2)
821                 {
822                         uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] & 0x0F;
823                         uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] & 0x0F;
824                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] & 0x0F;
825                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] & 0x0F;
826                         scrByte1 = mirrorNybble[scrByte1];
827                         scrByte2 = mirrorNybble[scrByte2];
828                         scrByte3 = mirrorNybble2[scrByte3];
829                         scrByte4 = mirrorNybble2[scrByte4];
830                         // This is just a guess, but it'll have to do for now...
831                         uint32_t pixels = previous3Bits | (scrByte3 << 24)
832                                 | (scrByte3 << 20) | (scrByte1 << 16)
833                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
834                                 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
835
836                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
837                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
838                         // 31   27   23   19   15    11   7    3  0
839
840                         if (screenType == ST_COLOR_TV)
841                         {
842                                 for(uint8_t i=0; i<7; i++)
843                                 {
844                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
845                                         pixels <<= 4;
846
847                                         for(uint8_t j=0; j<4; j++)
848                                         {
849                                                 uint8_t color = blurTable[bitPat][j];
850
851                                                 for(uint32_t cy=0; cy<8; cy++)
852                                                 {
853                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
854 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
855                                                 }
856                                         }
857                                 }
858
859                                 previous3Bits = pixels & 0x70000000;
860                         }
861                         else
862                         {
863                                 for(int j=0; j<28; j++)
864                                 {
865                                         for(uint32_t cy=0; cy<8; cy++)
866                                         {
867                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
868                                         }
869
870                                         pixels <<= 1;
871                                 }
872                         }
873                 }
874
875                 // Now do bottom half...
876
877                 previous3Bits = 0;
878
879                 for(uint16_t x=0; x<40; x+=2)
880                 {
881                         uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] >> 4;
882                         uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] >> 4;
883                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] >> 4;
884                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] >> 4;
885                         scrByte1 = mirrorNybble[scrByte1];
886                         scrByte2 = mirrorNybble[scrByte2];
887                         scrByte3 = mirrorNybble2[scrByte3];
888                         scrByte4 = mirrorNybble2[scrByte4];
889                         // This is just a guess, but it'll have to do for now...
890 //                      uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
891 //                              | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
892 //                              | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
893                         uint32_t pixels = previous3Bits | (scrByte3 << 24)
894                                 | (scrByte3 << 20) | (scrByte1 << 16)
895                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
896                                 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
897
898                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
899                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
900                         // 31   27   23   19   15    11   7    3  0
901
902                         if (screenType == ST_COLOR_TV)
903                         {
904                                 for(uint8_t i=0; i<7; i++)
905                                 {
906                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
907                                         pixels <<= 4;
908
909                                         for(uint8_t j=0; j<4; j++)
910                                         {
911                                                 uint8_t color = blurTable[bitPat][j];
912
913                                                 for(uint32_t cy=8; cy<16; cy++)
914                                                 {
915                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
916                                                 }
917                                         }
918                                 }
919
920                                 previous3Bits = pixels & 0x70000000;
921                         }
922                         else
923                         {
924                                 for(int j=0; j<28; j++)
925                                 {
926                                         for(uint32_t cy=8; cy<16; cy++)
927                                         {
928                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
929                                         }
930
931                                         pixels <<= 1;
932                                 }
933                         }
934                 }
935         }
936 }
937
938
939 static void RenderHiRes(uint16_t toLine/*= 192*/)
940 {
941 //printf("RenderHiRes to line %u\n", toLine);
942 // NOTE: Not endian safe. !!! FIX !!! [DONE]
943 #if 0
944         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
945 #else
946 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
947 // The colors are set in the 8-bit array as R G B A
948         uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
949         uint32_t * colorPtr = (uint32_t *)monoColors;
950         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
951 #endif
952
953         for(uint16_t y=0; y<toLine; y++)
954         {
955                 uint16_t previousLoPixel = 0;
956                 uint32_t previous3bits = 0;
957
958                 for(uint16_t x=0; x<40; x+=2)
959                 {
960                         uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
961                         uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
962                         previousLoPixel = (screenByte << 2) & 0x0100;
963
964                         screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
965                         uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
966                         previousLoPixel = (screenByte << 2) & 0x0100;
967
968                         pixels = previous3bits | (pixels << 14) | pixels2;
969
970 //testing (this shows on the screen, so it's OK)
971 //if (x == 0)
972 //{
973 //      pixels = 0x7FFFFFFF;
974 //}
975
976                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
977                         // 0ppp 1111 1111 1111 1111 1111 1111 1111
978                         // 31   27   23   19   15   11   7    3  0
979
980                         if (screenType == ST_COLOR_TV)
981                         {
982                                 for(uint8_t i=0; i<7; i++)
983                                 {
984                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
985                                         pixels <<= 4;
986
987                                         for(uint8_t j=0; j<4; j++)
988                                         {
989                                                 uint8_t color = blurTable[bitPat][j];
990 #if 0
991 //This doesn't seem to make things go any faster...
992 //It's the OpenGL render that's faster... Hmm...
993                                                 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
994 #else
995                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
996                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
997 #endif
998                                         }
999                                 }
1000
1001                                 previous3bits = pixels & 0x70000000;
1002                         }
1003                         else
1004                         {
1005                                 for(int j=0; j<28; j++)
1006                                 {
1007                                         scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1008
1009                                         if (screenType == ST_GREEN_MONO)
1010                                                 pixels &= 0x07FFFFFF;
1011
1012                                         scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1013                                         pixels <<= 1;
1014                                 }
1015                         }
1016                 }
1017         }
1018 }
1019
1020
1021 static void RenderDHiRes(uint16_t toLine/*= 192*/)
1022 {
1023 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
1024 // The colors are set in the 8-bit array as R G B A
1025         uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
1026         uint32_t * colorPtr = (uint32_t *)monoColors;
1027         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
1028
1029         for(uint16_t y=0; y<toLine; y++)
1030         {
1031                 uint32_t previous4bits = 0;
1032
1033                 for(uint16_t x=0; x<40; x+=2)
1034                 {
1035                         uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1036                         uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
1037                         screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1038                         pixels = pixels | (mirrorTable[screenByte & 0x7F]);
1039                         screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1040                         pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
1041                         screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1042                         pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
1043                         pixels = previous4bits | (pixels >> 1);
1044
1045                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
1046                         // 0ppp 1111 1111 1111 1111 1111 1111 1111
1047                         // 31   27   23   19   15   11   7    3  0
1048
1049                         if (screenType == ST_COLOR_TV)
1050                         {
1051                                 for(uint8_t i=0; i<7; i++)
1052                                 {
1053                                         uint8_t bitPat = (pixels & 0xFE000000) >> 25;
1054                                         pixels <<= 4;
1055
1056                                         for(uint8_t j=0; j<4; j++)
1057                                         {
1058                                                 uint32_t color = palette[blurTable[bitPat][j]];
1059                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
1060                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
1061                                         }
1062                                 }
1063
1064                                 previous4bits = pixels & 0xF0000000;
1065                         }
1066                         else
1067                         {
1068                                 for(int j=0; j<28; j++)
1069                                 {
1070                                         scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1071
1072                                         if (screenType == ST_GREEN_MONO)
1073                                                 pixels &= 0x07FFFFFF;
1074
1075                                         scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1076                                         pixels <<= 1;
1077                                 }
1078                         }
1079                 }
1080         }
1081 }
1082
1083
1084 void RenderVideoFrame(void)
1085 {
1086         if (GUI::powerOnState == true)
1087         {
1088                 if (textMode)
1089                 {
1090                         if (!col80Mode)
1091                                 Render40ColumnText();
1092                         else
1093                                 Render80ColumnText();
1094                 }
1095                 else
1096                 {
1097                         if (mixedMode)
1098                         {
1099                                 if (dhires)
1100                                 {
1101                                         if (hiRes)
1102                                                 RenderDHiRes(160);
1103                                         else
1104                                                 RenderDLoRes(20);
1105                                 }
1106                                 else if (hiRes)
1107                                         RenderHiRes(160);
1108                                 else
1109                                         RenderLoRes(20);
1110
1111                                 Render40ColumnTextLine(20);
1112                                 Render40ColumnTextLine(21);
1113                                 Render40ColumnTextLine(22);
1114                                 Render40ColumnTextLine(23);
1115                         }
1116                         else
1117                         {
1118                                 if (dhires)
1119                                 {
1120                                         if (hiRes)
1121                                                 RenderDHiRes();
1122                                         else
1123                                                 RenderDLoRes();
1124                                 }
1125                                 else if (hiRes)
1126                                         RenderHiRes();
1127                                 else
1128                                         RenderLoRes();
1129                         }
1130                 }
1131         }
1132         else
1133         {
1134                 memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
1135         }
1136
1137         if (msgTicks)
1138         {
1139                 DrawString();
1140                 msgTicks--;
1141         }
1142
1143         if (showFrameTicks)
1144                 DrawFrameTicks();
1145 }
1146
1147
1148 //
1149 // Prime SDL and create surfaces
1150 //
1151 bool InitVideo(void)
1152 {
1153         if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) != 0)
1154         {
1155                 WriteLog("Video: Could not initialize the SDL library: %s\n", SDL_GetError());
1156                 return false;
1157         }
1158
1159 #if 0
1160         int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0, &sdlWindow, &sdlRenderer);
1161 //      int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 1, VIRTUAL_SCREEN_HEIGHT * 1, 0, &sdlWindow, &sdlRenderer);
1162
1163         if (retVal != 0)
1164         {
1165                 WriteLog("Video: Could not create window and/or renderer: %s\n", SDL_GetError());
1166                 return false;
1167         }
1168 #else
1169         sdlWindow = SDL_CreateWindow("Apple2", settings.winX, settings.winY, VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0);
1170
1171         if (sdlWindow == NULL)
1172         {
1173                 WriteLog("Video: Could not create window: %s\n", SDL_GetError());
1174                 return false;
1175         }
1176
1177         sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
1178
1179         if (sdlRenderer == NULL)
1180         {
1181                 WriteLog("Video: Could not create renderer: %s\n", SDL_GetError());
1182                 return false;
1183         }
1184 #endif
1185
1186         // Make sure what we put there is what we get:
1187         SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
1188         SDL_RenderSetLogicalSize(sdlRenderer, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1189
1190         // Set the application's icon & title...
1191         SDL_Surface * iconSurface = SDL_CreateRGBSurfaceFrom(icon, 64, 64, 32, 64*4, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
1192         SDL_SetWindowIcon(sdlWindow, iconSurface);
1193         SDL_FreeSurface(iconSurface);
1194         SDL_SetWindowTitle(sdlWindow, "Apple2 Emulator");
1195
1196         sdlTexture = SDL_CreateTexture(sdlRenderer,
1197                 SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
1198                 VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1199
1200         SetupBlurTable();
1201
1202         WriteLog("Video: Successfully initialized.\n");
1203         return true;
1204 }
1205
1206
1207 //
1208 // Free various SDL components
1209 //
1210 void VideoDone(void)
1211 {
1212         WriteLog("Video: Shutting down SDL...\n");
1213         SDL_DestroyTexture(sdlTexture);
1214         SDL_DestroyRenderer(sdlRenderer);
1215         SDL_DestroyWindow(sdlWindow);
1216         SDL_Quit();
1217         WriteLog("Video: Done.\n");
1218 }
1219
1220
1221 //
1222 // Render the Apple video screen to the primary texture
1223 //
1224 void RenderAppleScreen(SDL_Renderer * renderer)
1225 {
1226         SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch);
1227         RenderVideoFrame();
1228         SDL_UnlockTexture(sdlTexture);
1229         SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
1230 }
1231
1232
1233 //
1234 // Fullscreen <-> window switching
1235 //
1236 void ToggleFullScreen(void)
1237 {
1238         settings.fullscreen = !settings.fullscreen;
1239
1240         int retVal = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1241
1242         if (retVal != 0)
1243                 WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());
1244 }
1245