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