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