]> Shamusworld >> Repos - apple2/blob - src/video.cpp
Add key delay to keyboard handler, mouse hiding when mouse is idle.
[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-2017 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 there [DONE]
24 //
25
26 // Display routines seem MUCH slower now... !!! INVESTIGATE !!! [not anymore]
27
28 #include "video.h"
29
30 #include <string.h>                                     // for memset()
31 #include <stdio.h>
32 #include <stdarg.h>                                     // for va_* stuff
33 #include "apple2.h"
34 #include "apple2-icon-64x64.h"
35 #include "charset.h"
36 #include "log.h"
37 #include "settings.h"
38 #include "gui/font14pt.h"
39 #include "gui/gui.h"
40
41 /* Reference: Technote tn-iigs-063 "Master Color Values"
42
43           Color  Color Register LR HR  DHR Master Color R,G,B
44           Name       Value      #  #   #      Value
45           ----------------------------------------------------
46           Black       0         0  0,4 0      $0000    (0,0,0)
47 (Magenta) Deep Red    1         1      1      $0D03    (D,0,3)
48           Dark Blue   2         2      8      $0009    (0,0,9)
49  (Violet) Purple      3         3  2   9      $0D2D    (D,2,D)
50           Dark Green  4         4      4      $0072    (0,7,2)
51  (Gray 1) Dark Gray   5         5      5      $0555    (5,5,5)
52    (Blue) Medium Blue 6         6  6   C      $022F    (2,2,F)
53    (Cyan) Light Blue  7         7      D      $06AF    (6,A,F)
54           Brown       8         8      2      $0850    (8,5,0)
55           Orange      9         9  5   3      $0F60    (F,6,0)
56  (Gray 2) Light Gray  A         A      A      $0AAA    (A,A,A)
57           Pink        B         B      B      $0F98    (F,9,8)
58   (Green) Light Green C         C  1   6      $01D0    (1,D,0)
59           Yellow      D         D      7      $0FF0    (F,F,0)
60    (Aqua) Aquamarine  E         E      E      $04F9    (4,F,9)
61           White       F         F  3,7 F      $0FFF    (F,F,F)
62
63    LR: Lo-Res   HR: Hi-Res   DHR: Double Hi-Res
64
65    N.B.: These colors look like shit */
66
67 // Global variables
68
69 bool flash = false;
70 bool textMode = true;
71 bool mixedMode = false;
72 bool displayPage2 = false;
73 bool hiRes = false;
74 bool alternateCharset = false;
75 bool col80Mode = false;
76 SDL_Renderer * sdlRenderer = NULL;
77
78 // Local variables
79
80 static SDL_Window * sdlWindow = NULL;
81 static SDL_Texture * sdlTexture = NULL;
82 static uint32_t * scrBuffer;
83 static int scrPitch;
84
85 // We set up the colors this way so that they'll be endian safe
86 // when we cast them to a uint32_t. Note that the format is RGBA.
87
88 // "Master Color Values" palette
89
90 static uint8_t colors[16 * 4] = {
91         0x00, 0x00, 0x00, 0xFF,                         // Black
92         0xDD, 0x00, 0x33, 0xFF,                         // Deep Red (Magenta)
93         0x00, 0x00, 0x99, 0xFF,                         // Dark Blue
94         0xDD, 0x22, 0xDD, 0xFF,                         // Purple (Violet)
95         0x00, 0x77, 0x22, 0xFF,                         // Dark Green
96         0x55, 0x55, 0x55, 0xFF,                         // Dark Gray (Gray 1)
97         0x22, 0x22, 0xFF, 0xFF,                         // Medium Blue (Blue)
98         0x66, 0xAA, 0xFF, 0xFF,                         // Light Blue (Cyan)
99         0x88, 0x55, 0x00, 0xFF,                         // Brown
100         0xFF, 0x66, 0x00, 0xFF,                         // Orange
101         0xAA, 0xAA, 0xAA, 0xFF,                         // Light Gray (Gray 2)
102         0xFF, 0x99, 0x88, 0xFF,                         // Pink
103         0x11, 0xDD, 0x00, 0xFF,                         // Light Green (Green)
104         0xFF, 0xFF, 0x00, 0xFF,                         // Yellow
105         0x44, 0xFF, 0x99, 0xFF,                         // Aquamarine (Aqua)
106         0xFF, 0xFF, 0xFF, 0xFF                          // White
107 };
108
109 // This palette comes from ApplePC's colors (more realistic to my eye ;-)
110
111 static uint8_t altColors[16 * 4] = {
112         0x00, 0x00, 0x00, 0xFF,
113         0x7D, 0x20, 0x41, 0xFF,
114         0x41, 0x30, 0x7D, 0xFF,
115         0xBE, 0x51, 0xBE, 0xFF,
116         0x00, 0x5D, 0x3C, 0xFF,
117         0x7D, 0x7D, 0x7D, 0xFF,
118         0x41, 0x8E, 0xBA, 0xFF,
119         0xBE, 0xAE, 0xFB, 0xFF,
120         0x3C, 0x4D, 0x00, 0xFF,
121         0xBA, 0x6D, 0x41, 0xFF,
122         0x7D, 0x7D, 0x7D, 0xFF,
123         0xFB, 0x9E, 0xBE, 0xFF,
124         0x3C, 0xAA, 0x3C, 0xFF,
125         0xBA, 0xCB, 0x7D, 0xFF,
126         0x7D, 0xDB, 0xBA, 0xFF,
127         0xFB, 0xFB, 0xFB, 0xFF };
128
129 // Lo-res starting line addresses
130
131 static uint16_t lineAddrLoRes[24] = {
132         0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
133         0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
134         0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 };
135
136 // Hi-res starting line addresses
137
138 static uint16_t lineAddrHiRes[192] = {
139         0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
140         0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,
141         0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,
142         0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,
143
144         0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,
145         0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,
146         0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,
147         0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,
148
149         0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,
150         0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,
151         0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,
152         0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,
153
154         0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,
155         0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,
156         0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,
157         0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,
158
159         0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,
160         0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,
161         0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,
162         0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,
163
164         0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,
165         0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,
166         0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,
167         0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 };
168
169 uint16_t appleHiresToMono[0x200] = {
170         0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
171         0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
172         0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
173         0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
174         0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
175         0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
176         0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
177         0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
178         0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
179         0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
180         0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
181         0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
182         0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
183         0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
184         0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
185         0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
186         0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80,
187         0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x
188         0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98,
189         0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x
190         0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86,
191         0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax
192         0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E,
193         0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx
194         0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81,
195         0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx
196         0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99,
197         0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx
198         0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87,
199         0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex
200         0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F,
201         0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx
202
203         // Second half adds in the previous byte's lo pixel
204
205         0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
206         0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
207         0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
208         0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
209         0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
210         0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
211         0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
212         0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
213         0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
214         0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
215         0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
216         0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
217         0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
218         0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
219         0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
220         0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
221         0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80,
222         0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x
223         0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98,
224         0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x
225         0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86,
226         0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax
227         0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E,
228         0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx
229         0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81,
230         0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx
231         0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99,
232         0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx
233         0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87,
234         0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex
235         0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F,
236         0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF  // $Fx
237 };
238
239 //static uint8_t blurTable[0x800][8];                           // Color TV blur table
240 static uint8_t blurTable[0x80][8];                              // Color TV blur table
241 static uint8_t mirrorTable[0x100];
242 static uint32_t * palette = (uint32_t *)altColors;
243 enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
244 static uint8_t screenType = ST_COLOR_TV;
245
246 // Local functions
247
248 static void Render40ColumnTextLine(uint8_t line);
249 static void Render80ColumnTextLine(uint8_t line);
250 static void Render40ColumnText(void);
251 static void Render80ColumnText(void);
252 static void RenderLoRes(uint16_t toLine = 24);
253 static void RenderHiRes(uint16_t toLine = 192);
254 static void RenderDHiRes(uint16_t toLine = 192);
255 static void RenderVideoFrame(/*uint32_t *, int*/);
256
257
258 void SetupBlurTable(void)
259 {
260         // NOTE: This table only needs to be 7 bits wide instead of 11, since the
261         //       last four bits are copies of the previous four...
262         //       Odd. Doing the bit patterns from 0-$7F doesn't work, but going
263         //       from 0-$7FF stepping by 16 does. Hm.
264         //       Well, it seems that going from 0-$7F doesn't have enough precision to do the job.
265 #if 0
266 //      for(uint16_t bitPat=0; bitPat<0x800; bitPat++)
267         for(uint16_t bitPat=0; bitPat<0x80; bitPat++)
268         {
269 /*              uint16_t w3 = bitPat & 0x888;
270                 uint16_t w2 = bitPat & 0x444;
271                 uint16_t w1 = bitPat & 0x222;
272                 uint16_t w0 = bitPat & 0x111;*/
273                 uint16_t w3 = bitPat & 0x88;
274                 uint16_t w2 = bitPat & 0x44;
275                 uint16_t w1 = bitPat & 0x22;
276                 uint16_t w0 = bitPat & 0x11;
277
278                 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
279                 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
280                 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
281                 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
282
283                 for(int8_t i=7; i>=0; i--)
284                 {
285                         uint8_t color = (((blurred0 >> i) & 0x01) << 3)
286                                 | (((blurred1 >> i) & 0x01) << 2)
287                                 | (((blurred2 >> i) & 0x01) << 1)
288                                 | ((blurred3 >> i) & 0x01);
289                         blurTable[bitPat][7 - i] = color;
290                 }
291         }
292 #else
293         for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
294         {
295                 uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
296
297                 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
298                 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
299                 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
300                 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
301
302                 for(int8_t i=7; i>=0; i--)
303                 {
304                         uint8_t color = (((blurred0 >> i) & 0x01) << 3)
305                                 | (((blurred1 >> i) & 0x01) << 2)
306                                 | (((blurred2 >> i) & 0x01) << 1)
307                                 | ((blurred3 >> i) & 0x01);
308                         blurTable[bitPat >> 4][7 - i] = color;
309                 }
310         }
311 #endif
312
313         for(int i=0; i<256; i++)
314         {
315                 mirrorTable[i] = ((i & 0x01) << 7)
316                         | ((i & 0x02) << 5)
317                         | ((i & 0x04) << 3)
318                         | ((i & 0x08) << 1)
319                         | ((i & 0x10) >> 1)
320                         | ((i & 0x20) >> 3)
321                         | ((i & 0x40) >> 5)
322                         | ((i & 0x80) >> 7);
323         }
324 }
325
326
327 void TogglePalette(void)
328 {
329         if (palette == (uint32_t *)colors)
330         {
331                 palette = (uint32_t *)altColors;
332                 SpawnMessage("Color TV palette");
333         }
334         else
335         {
336                 palette = (uint32_t *)colors;
337                 SpawnMessage("\"Master Color Values\" palette");
338         }
339 }
340
341
342 void CycleScreenTypes(void)
343 {
344         char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
345
346         screenType++;
347
348         if (screenType == ST_LAST_ENTRY)
349                 screenType = ST_FIRST_ENTRY;
350
351         SpawnMessage("%s", scrTypeStr[screenType]);
352 }
353
354
355 static uint32_t msgTicks = 0;
356 static char message[4096];
357
358 void SpawnMessage(const char * text, ...)
359 {
360         va_list arg;
361
362         va_start(arg, text);
363         vsprintf(message, text, arg);
364         va_end(arg);
365
366         msgTicks = 120;
367 }
368
369
370 static void DrawString2(uint32_t x, uint32_t y, uint32_t color);
371 static void DrawString(void)
372 {
373 //This approach works, and seems to be fast enough... Though it probably would
374 //be better to make the oversized font to match this one...
375         for(uint32_t x=7; x<=9; x++)
376                 for(uint32_t y=7; y<=9; y++)
377                         DrawString2(x, y, 0x00000000);
378
379         DrawString2(8, 8, 0x0020FF20);
380 }
381
382
383 static void DrawString2(uint32_t x, uint32_t y, uint32_t color)
384 {
385 //uint32_t x = 8, y = 8;
386         uint32_t length = strlen(message), address = x + (y * VIRTUAL_SCREEN_WIDTH);
387 //      uint32_t color = 0x0020FF20;
388 //This could be done ahead of time, instead of on each pixel...
389 //(Now it is!)
390         uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
391
392         for(uint32_t i=0; i<length; i++)
393         {
394                 uint8_t c = message[i];
395                 c = (c < 32 ? 0 : c - 32);
396                 uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
397
398                 for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
399                 {
400                         for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
401                         {
402 /*                              uint8_t fontTrans = font1[fontAddr++];
403 //                              uint32_t newTrans = (fontTrans * transparency / 255) << 24;
404                                 uint32_t newTrans = fontTrans << 24;
405                                 uint32_t pixel = newTrans | color;
406
407                                 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = pixel;//*/
408
409 //                              uint8_t trans = font1[fontAddr++];
410                                 uint8_t trans = font2[fontAddr++];
411
412                                 if (trans)
413                                 {
414                                         uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
415
416                                         uint8_t eBlue = (existingColor >> 16) & 0xFF,
417                                                 eGreen = (existingColor >> 8) & 0xFF,
418                                                 eRed = existingColor & 0xFF;
419
420 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
421 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
422 //because dividing by 32 is faster than dividing by 31...!
423                                         uint8_t invTrans = 255 - trans;
424
425                                         uint32_t bRed   = (eRed   * invTrans + nRed   * trans) / 255;
426                                         uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
427                                         uint32_t bBlue  = (eBlue  * invTrans + nBlue  * trans) / 255;
428
429 //THIS IS NOT ENDIAN SAFE
430 //NB: Setting the alpha channel here does nothing.
431                                         *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
432                                 }
433                         }
434                 }
435
436                 address += FONT_WIDTH;
437         }
438 }
439
440
441 static void Render40ColumnTextLine(uint8_t line)
442 {
443         uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
444
445         for(int x=0; x<40; x++)
446         {
447                 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
448
449                 // Render character at (x, y)
450
451                 for(int cy=0; cy<8; cy++)
452                 {
453                         for(int cx=0; cx<7; cx++)
454                         {
455                                 uint32_t pixel = 0xFF000000;
456
457                                 if (alternateCharset)
458                                 {
459                                         if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
460                                                 pixel = pixelOn;
461
462                                         if (chr < 0x80)
463                                                 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
464
465                                         if ((chr & 0xC0) == 0x40 && flash)
466                                                 pixel = 0xFF000000;
467                                 }
468                                 else
469                                 {
470                                         if (textChar2e[(chr * 56) + cx + (cy * 7)])
471                                                 pixel = pixelOn;
472                                 }
473
474                                 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
475                                 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
476
477                                 // QnD method to get blank alternate lines in text mode
478                                 if (screenType == ST_GREEN_MONO)
479                                         pixel = 0xFF000000;
480
481                                 {
482                                         scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
483                                         scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
484                                 }
485                         }
486                 }
487         }
488 }
489
490
491 static void Render80ColumnTextLine(uint8_t line)
492 {
493         uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
494
495         for(int x=0; x<80; x++)
496         {
497 #if 0
498 // This is wrong; it should grab from the alt bank if Page2 is set, not main RAM @ $0
499                 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
500
501                 if (x > 39)
502                         chr = ram2[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x - 40];
503 #else
504                 uint8_t chr;
505
506                 if (x & 0x01)
507                         chr = ram[lineAddrLoRes[line] + (x >> 1)];
508                 else
509                         chr = ram2[lineAddrLoRes[line] + (x >> 1)];
510 #endif
511
512                 // Render character at (x, y)
513
514                 for(int cy=0; cy<8; cy++)
515                 {
516                         for(int cx=0; cx<7; cx++)
517                         {
518                                 uint32_t pixel = 0xFF000000;
519
520                                 if (alternateCharset)
521                                 {
522                                         if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
523                                                 pixel = pixelOn;
524
525                                         if (chr < 0x80)
526                                                 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
527
528                                         if ((chr & 0xC0) == 0x40 && flash)
529                                                 pixel = 0xFF000000;
530                                 }
531                                 else
532                                 {
533                                         if (textChar2e[(chr * 56) + cx + (cy * 7)])
534                                                 pixel = pixelOn;
535                                 }
536
537                                 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
538
539                                 // QnD method to get blank alternate lines in text mode
540                                 if (screenType == ST_GREEN_MONO)
541                                         pixel = 0xFF000000;
542
543                                 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
544                         }
545                 }
546         }
547 }
548
549
550 static void Render40ColumnText(void)
551 {
552         for(uint8_t line=0; line<24; line++)
553                 Render40ColumnTextLine(line);
554 }
555
556
557 static void Render80ColumnText(void)
558 {
559         for(uint8_t line=0; line<24; line++)
560                 Render80ColumnTextLine(line);
561 }
562
563
564 static void RenderLoRes(uint16_t toLine/*= 24*/)
565 {
566 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
567 //       Also, we could set up three different Render functions depending on which
568 //       render type was set and call it with a function pointer. Would be faster
569 //       then the nested ifs we have now.
570 /*
571 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
572 Color #s correspond to the bit patterns in reverse... Interesting!
573
574 00 00 00 ->  0 [0000] -> 0 (lores color #)
575 3c 4d 00 ->  8 [0001] -> 8?             BROWN
576 00 5d 3c ->  4 [0010] -> 4?             DARK GREEN
577 3c aa 3c -> 12 [0011] -> 12?    LIGHT GREEN (GREEN)
578 41 30 7d ->  2 [0100] -> 2?             DARK BLUE
579 7d 7d 7d -> 10 [0101] -> 10?    LIGHT GRAY
580 41 8e ba ->  6 [0110] -> 6?             MEDIUM BLUE (BLUE)
581 7d db ba -> 14 [0111] -> 14?    AQUAMARINE (AQUA)
582 7d 20 41 ->  1 [1000] -> 1?             DEEP RED (MAGENTA)
583 ba 6d 41 ->  9 [1001] -> 9?             ORANGE
584 7d 7d 7d ->  5 [1010] -> 5?             DARK GRAY
585 ba cb 7d -> 13 [1011] -> 13?    YELLOW
586 be 51 be ->  3 [1100] -> 3              PURPLE (VIOLET)
587 fb 9e be -> 11 [1101] -> 11?    PINK
588 be ae fb ->  7 [1110] -> 7?             LIGHT BLUE (CYAN)
589 fb fb fb -> 15 [1111] -> 15             WHITE
590 */
591         uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
592
593 //This is the old "perfect monitor" rendering code...
594 /*      if (screenType != ST_COLOR_TV) // Not correct, but for now...
595 //if (1)
596         {
597                 for(uint16_t y=0; y<toLine; y++)
598                 {
599                         for(uint16_t x=0; x<40; x++)
600                         {
601                                 uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
602                                 uint32_t pixel = palette[scrByte & 0x0F];
603
604                                 for(int cy=0; cy<4; cy++)
605                                         for(int cx=0; cx<14; cx++)
606                                                 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
607
608                                 pixel = palette[scrByte >> 4];
609
610                                 for(int cy=4; cy<8; cy++)
611                                         for(int cx=0; cx<14; cx++)
612                                                 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
613                         }
614                 }
615         }
616         else//*/
617
618         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
619
620         for(uint16_t y=0; y<toLine; y++)
621         {
622                 // Do top half of lores screen bytes...
623
624                 uint32_t previous3Bits = 0;
625
626                 for(uint16_t x=0; x<40; x+=2)
627                 {
628                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
629                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
630                         scrByte1 = mirrorNybble[scrByte1];
631                         scrByte2 = mirrorNybble[scrByte2];
632                         // This is just a guess, but it'll have to do for now...
633                         uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
634                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
635                                 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
636
637                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
638                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
639                         // 31   27   23   19   15    11   7    3  0
640
641                         if (screenType == ST_COLOR_TV)
642                         {
643                                 for(uint8_t i=0; i<7; i++)
644                                 {
645                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
646                                         pixels <<= 4;
647
648                                         for(uint8_t j=0; j<4; j++)
649                                         {
650                                                 uint8_t color = blurTable[bitPat][j];
651
652                                                 for(uint32_t cy=0; cy<8; cy++)
653                                                 {
654                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
655 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
656                                                 }
657                                         }
658                                 }
659
660                                 previous3Bits = pixels & 0x70000000;
661                         }
662                         else
663                         {
664                                 for(int j=0; j<28; j++)
665                                 {
666                                         for(uint32_t cy=0; cy<8; cy++)
667                                         {
668                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
669 //                                              scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
670                                         }
671
672                                         pixels <<= 1;
673                                 }
674                         }
675                 }
676
677                 // Now do bottom half...
678
679                 previous3Bits = 0;
680
681                 for(uint16_t x=0; x<40; x+=2)
682                 {
683                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
684                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
685                         scrByte1 = mirrorNybble[scrByte1];
686                         scrByte2 = mirrorNybble[scrByte2];
687                         // This is just a guess, but it'll have to do for now...
688                         uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
689                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
690                                 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
691
692                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
693                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
694                         // 31   27   23   19   15    11   7    3  0
695
696                         if (screenType == ST_COLOR_TV)
697                         {
698                                 for(uint8_t i=0; i<7; i++)
699                                 {
700                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
701                                         pixels <<= 4;
702
703                                         for(uint8_t j=0; j<4; j++)
704                                         {
705                                                 uint8_t color = blurTable[bitPat][j];
706
707                                                 for(uint32_t cy=8; cy<16; cy++)
708                                                 {
709                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
710 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
711                                                 }
712                                         }
713                                 }
714
715                                 previous3Bits = pixels & 0x70000000;
716                         }
717                         else
718                         {
719                                 for(int j=0; j<28; j++)
720                                 {
721                                         for(uint32_t cy=8; cy<16; cy++)
722                                         {
723                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
724 //                                              scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
725                                         }
726
727                                         pixels <<= 1;
728                                 }
729                         }
730                 }
731         }
732 }
733
734
735 static void RenderHiRes(uint16_t toLine/*= 192*/)
736 {
737 //printf("RenderHiRes to line %u\n", toLine);
738 // NOTE: Not endian safe. !!! FIX !!! [DONE]
739 #if 0
740         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
741 #else
742 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
743 // The colors are set in the 8-bit array as R G B A
744         uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
745         uint32_t * colorPtr = (uint32_t *)monoColors;
746         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
747 #endif
748
749         for(uint16_t y=0; y<toLine; y++)
750         {
751                 uint16_t previousLoPixel = 0;
752                 uint32_t previous3bits = 0;
753
754                 for(uint16_t x=0; x<40; x+=2)
755                 {
756                         uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
757                         uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
758                         previousLoPixel = (screenByte << 2) & 0x0100;
759
760                         screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
761                         uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
762                         previousLoPixel = (screenByte << 2) & 0x0100;
763
764                         pixels = previous3bits | (pixels << 14) | pixels2;
765
766 //testing (this shows on the screen, so it's OK)
767 //if (x == 0)
768 //{
769 //      pixels = 0x7FFFFFFF;
770 //}
771
772                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
773                         // 0ppp 1111 1111 1111 1111 1111 1111 1111
774                         // 31   27   23   19   15   11   7    3  0
775
776                         if (screenType == ST_COLOR_TV)
777                         {
778                                 for(uint8_t i=0; i<7; i++)
779                                 {
780                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
781                                         pixels <<= 4;
782
783                                         for(uint8_t j=0; j<4; j++)
784                                         {
785                                                 uint8_t color = blurTable[bitPat][j];
786 #if 0
787 //This doesn't seem to make things go any faster...
788 //It's the OpenGL render that's faster... Hmm...
789                                                 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
790 #else
791                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
792                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
793 #endif
794                                         }
795                                 }
796
797                                 previous3bits = pixels & 0x70000000;
798                         }
799                         else
800                         {
801                                 for(int j=0; j<28; j++)
802                                 {
803                                         scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
804
805                                         if (screenType == ST_GREEN_MONO)
806                                                 pixels &= 0x07FFFFFF;
807
808                                         scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
809                                         pixels <<= 1;
810                                 }
811                         }
812                 }
813         }
814 }
815
816
817 static void RenderDHiRes(uint16_t toLine/*= 192*/)
818 {
819 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
820 // The colors are set in the 8-bit array as R G B A
821         uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
822         uint32_t * colorPtr = (uint32_t *)monoColors;
823         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
824
825         for(uint16_t y=0; y<toLine; y++)
826         {
827                 uint32_t previous4bits = 0;
828
829                 for(uint16_t x=0; x<40; x+=2)
830                 {
831                         uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
832                         uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
833                         screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
834                         pixels = pixels | (mirrorTable[screenByte & 0x7F]);
835                         screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
836                         pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
837                         screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
838                         pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
839                         pixels = previous4bits | (pixels >> 1);
840
841                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
842                         // 0ppp 1111 1111 1111 1111 1111 1111 1111
843                         // 31   27   23   19   15   11   7    3  0
844
845                         if (screenType == ST_COLOR_TV)
846                         {
847                                 for(uint8_t i=0; i<7; i++)
848                                 {
849                                         uint8_t bitPat = (pixels & 0xFE000000) >> 25;
850                                         pixels <<= 4;
851
852                                         for(uint8_t j=0; j<4; j++)
853                                         {
854                                                 uint32_t color = palette[blurTable[bitPat][j]];
855                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
856                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
857                                         }
858                                 }
859
860                                 previous4bits = pixels & 0xF0000000;
861                         }
862                         else
863                         {
864                                 for(int j=0; j<28; j++)
865                                 {
866                                         scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
867
868                                         if (screenType == ST_GREEN_MONO)
869                                                 pixels &= 0x07FFFFFF;
870
871                                         scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
872                                         pixels <<= 1;
873                                 }
874                         }
875                 }
876         }
877 }
878
879
880 void RenderVideoFrame(void)
881 {
882         if (GUI::powerOnState == true)
883         {
884                 if (textMode)
885                 {
886                         if (!col80Mode)
887                                 Render40ColumnText();
888                         else
889                                 Render80ColumnText();
890                 }
891                 else
892                 {
893                         if (mixedMode)
894                         {
895                                 if (hiRes)
896                                 {
897                                         RenderHiRes(160);
898                                         Render40ColumnTextLine(20);
899                                         Render40ColumnTextLine(21);
900                                         Render40ColumnTextLine(22);
901                                         Render40ColumnTextLine(23);
902                                 }
903                                 else
904                                 {
905                                         RenderLoRes(20);
906                                         Render40ColumnTextLine(20);
907                                         Render40ColumnTextLine(21);
908                                         Render40ColumnTextLine(22);
909                                         Render40ColumnTextLine(23);
910                                 }
911                         }
912                         else
913                         {
914                                 if (dhires)
915                                         RenderDHiRes();
916                                 else if (hiRes)
917                                         RenderHiRes();
918                                 else
919                                         RenderLoRes();
920                         }
921                 }
922         }
923         else
924         {
925                 memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
926         }
927
928         if (msgTicks)
929         {
930                 DrawString();
931                 msgTicks--;
932         }
933 }
934
935
936 //
937 // Prime SDL and create surfaces
938 //
939 bool InitVideo(void)
940 {
941         if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) != 0)
942         {
943                 WriteLog("Video: Could not initialize the SDL library: %s\n", SDL_GetError());
944                 return false;
945         }
946
947 //      int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, SDL_WINDOW_OPENGL, &sdlWindow, &sdlRenderer);
948         int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0, &sdlWindow, &sdlRenderer);
949
950         if (retVal != 0)
951         {
952                 WriteLog("Video: Could not window and/or renderer: %s\n", SDL_GetError());
953                 return false;
954         }
955
956         // Make the scaled rendering look smoother.
957         SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
958 //      SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
959         SDL_RenderSetLogicalSize(sdlRenderer, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
960
961         // Set the application's icon & title...
962         SDL_Surface * iconSurface = SDL_CreateRGBSurfaceFrom(icon, 64, 64, 32, 64*4, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
963         SDL_SetWindowIcon(sdlWindow, iconSurface);
964         SDL_FreeSurface(iconSurface);
965         SDL_SetWindowTitle(sdlWindow, "Apple2 Emulator");
966
967         sdlTexture = SDL_CreateTexture(sdlRenderer,
968                 SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
969                 VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
970
971         SetupBlurTable();
972
973         WriteLog("Video: Successfully initialized.\n");
974         return true;
975 }
976
977
978 //
979 // Free various SDL components
980 //
981 void VideoDone(void)
982 {
983         WriteLog("Video: Shutting down SDL...\n");
984         SDL_DestroyTexture(sdlTexture);
985         SDL_DestroyRenderer(sdlRenderer);
986         SDL_DestroyWindow(sdlWindow);
987         SDL_Quit();
988         WriteLog("Video: Done.\n");
989 }
990
991
992 //
993 // Render the Apple video screen to the primary texture
994 //
995 void RenderAppleScreen(SDL_Renderer * renderer)
996 {
997         SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch);
998         RenderVideoFrame();
999         SDL_UnlockTexture(sdlTexture);
1000         SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
1001 }
1002
1003
1004 //
1005 // Fullscreen <-> window switching
1006 //
1007 void ToggleFullScreen(void)
1008 {
1009         settings.fullscreen = !settings.fullscreen;
1010
1011         int retVal = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1012
1013         if (retVal != 0)
1014                 WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());
1015 }
1016