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