]> Shamusworld >> Repos - apple2/blob - src/video.cpp
Improvements to timing, disk selector; added Double LoRes.
[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                 uint8_t chr;
498
499                 if (x & 0x01)
500                         chr = ram[lineAddrLoRes[line] + (x >> 1)];
501                 else
502                         chr = ram2[lineAddrLoRes[line] + (x >> 1)];
503
504                 // Render character at (x, y)
505
506                 for(int cy=0; cy<8; cy++)
507                 {
508                         for(int cx=0; cx<7; cx++)
509                         {
510                                 uint32_t pixel = 0xFF000000;
511
512                                 if (alternateCharset)
513                                 {
514                                         if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
515                                                 pixel = pixelOn;
516
517                                         if (chr < 0x80)
518                                                 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
519
520                                         if ((chr & 0xC0) == 0x40 && flash)
521                                                 pixel = 0xFF000000;
522                                 }
523                                 else
524                                 {
525                                         if (textChar2e[(chr * 56) + cx + (cy * 7)])
526                                                 pixel = pixelOn;
527                                 }
528
529                                 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
530
531                                 // QnD method to get blank alternate lines in text mode
532                                 if (screenType == ST_GREEN_MONO)
533                                         pixel = 0xFF000000;
534
535                                 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
536                         }
537                 }
538         }
539 }
540
541
542 static void Render40ColumnText(void)
543 {
544         for(uint8_t line=0; line<24; line++)
545                 Render40ColumnTextLine(line);
546 }
547
548
549 static void Render80ColumnText(void)
550 {
551         for(uint8_t line=0; line<24; line++)
552                 Render80ColumnTextLine(line);
553 }
554
555
556 static void RenderLoRes(uint16_t toLine/*= 24*/)
557 {
558 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
559 //       Also, we could set up three different Render functions depending on which
560 //       render type was set and call it with a function pointer. Would be faster
561 //       then the nested ifs we have now.
562 /*
563 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
564 Color #s correspond to the bit patterns in reverse... Interesting!
565
566 00 00 00 ->  0 [0000] -> 0 (lores color #)
567 3c 4d 00 ->  8 [0001] -> 8?             BROWN
568 00 5d 3c ->  4 [0010] -> 4?             DARK GREEN
569 3c aa 3c -> 12 [0011] -> 12?    LIGHT GREEN (GREEN)
570 41 30 7d ->  2 [0100] -> 2?             DARK BLUE
571 7d 7d 7d -> 10 [0101] -> 10?    LIGHT GRAY
572 41 8e ba ->  6 [0110] -> 6?             MEDIUM BLUE (BLUE)
573 7d db ba -> 14 [0111] -> 14?    AQUAMARINE (AQUA)
574 7d 20 41 ->  1 [1000] -> 1?             DEEP RED (MAGENTA)
575 ba 6d 41 ->  9 [1001] -> 9?             ORANGE
576 7d 7d 7d ->  5 [1010] -> 5?             DARK GRAY
577 ba cb 7d -> 13 [1011] -> 13?    YELLOW
578 be 51 be ->  3 [1100] -> 3              PURPLE (VIOLET)
579 fb 9e be -> 11 [1101] -> 11?    PINK
580 be ae fb ->  7 [1110] -> 7?             LIGHT BLUE (CYAN)
581 fb fb fb -> 15 [1111] -> 15             WHITE
582 */
583         uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
584
585 //This is the old "perfect monitor" rendering code...
586 /*      if (screenType != ST_COLOR_TV) // Not correct, but for now...
587 //if (1)
588         {
589                 for(uint16_t y=0; y<toLine; y++)
590                 {
591                         for(uint16_t x=0; x<40; x++)
592                         {
593                                 uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
594                                 uint32_t pixel = palette[scrByte & 0x0F];
595
596                                 for(int cy=0; cy<4; cy++)
597                                         for(int cx=0; cx<14; cx++)
598                                                 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
599
600                                 pixel = palette[scrByte >> 4];
601
602                                 for(int cy=4; cy<8; cy++)
603                                         for(int cx=0; cx<14; cx++)
604                                                 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
605                         }
606                 }
607         }
608         else//*/
609
610         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
611
612         for(uint16_t y=0; y<toLine; y++)
613         {
614                 // Do top half of lores screen bytes...
615
616                 uint32_t previous3Bits = 0;
617
618                 for(uint16_t x=0; x<40; x+=2)
619                 {
620                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
621                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
622                         scrByte1 = mirrorNybble[scrByte1];
623                         scrByte2 = mirrorNybble[scrByte2];
624                         // This is just a guess, but it'll have to do for now...
625                         uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
626                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
627                                 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
628
629                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
630                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
631                         // 31   27   23   19   15    11   7    3  0
632
633                         if (screenType == ST_COLOR_TV)
634                         {
635                                 for(uint8_t i=0; i<7; i++)
636                                 {
637                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
638                                         pixels <<= 4;
639
640                                         for(uint8_t j=0; j<4; j++)
641                                         {
642                                                 uint8_t color = blurTable[bitPat][j];
643
644                                                 for(uint32_t cy=0; cy<8; cy++)
645                                                 {
646                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
647 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
648                                                 }
649                                         }
650                                 }
651
652                                 previous3Bits = pixels & 0x70000000;
653                         }
654                         else
655                         {
656                                 for(int j=0; j<28; j++)
657                                 {
658                                         for(uint32_t cy=0; cy<8; cy++)
659                                         {
660                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
661 //                                              scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
662                                         }
663
664                                         pixels <<= 1;
665                                 }
666                         }
667                 }
668
669                 // Now do bottom half...
670
671                 previous3Bits = 0;
672
673                 for(uint16_t x=0; x<40; x+=2)
674                 {
675                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
676                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
677                         scrByte1 = mirrorNybble[scrByte1];
678                         scrByte2 = mirrorNybble[scrByte2];
679                         // This is just a guess, but it'll have to do for now...
680                         uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
681                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
682                                 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
683
684                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
685                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
686                         // 31   27   23   19   15    11   7    3  0
687
688                         if (screenType == ST_COLOR_TV)
689                         {
690                                 for(uint8_t i=0; i<7; i++)
691                                 {
692                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
693                                         pixels <<= 4;
694
695                                         for(uint8_t j=0; j<4; j++)
696                                         {
697                                                 uint8_t color = blurTable[bitPat][j];
698
699                                                 for(uint32_t cy=8; cy<16; cy++)
700                                                 {
701                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
702 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
703                                                 }
704                                         }
705                                 }
706
707                                 previous3Bits = pixels & 0x70000000;
708                         }
709                         else
710                         {
711                                 for(int j=0; j<28; j++)
712                                 {
713                                         for(uint32_t cy=8; cy<16; cy++)
714                                         {
715                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
716 //                                              scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
717                                         }
718
719                                         pixels <<= 1;
720                                 }
721                         }
722                 }
723         }
724 }
725
726
727 //
728 // Render the Double Lo Res screen (HIRES off, DHIRES on)
729 //
730 static void RenderDLoRes(void)
731 {
732 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
733 //       Also, we could set up three different Render functions depending on
734 //       which render type was set and call it with a function pointer. Would be
735 //       faster then the nested ifs we have now.
736 /*
737 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
738 Color #s correspond to the bit patterns in reverse... Interesting! [It's because
739 the video generator reads the bit patters from bit 0--which makes them backwards
740 from the normal POV.]
741
742 00 00 00 ->  0 [0000] -> 0 (lores color #)
743 3C 4D 00 ->  8 [0001] -> 8?             BROWN
744 00 5D 3C ->  4 [0010] -> 4?             DARK GREEN
745 3C AA 3C -> 12 [0011] -> 12?    LIGHT GREEN (GREEN)
746 41 30 7D ->  2 [0100] -> 2?             DARK BLUE
747 7D 7D 7D -> 10 [0101] -> 10?    LIGHT GRAY (Grays are identical)
748 41 8E BA ->  6 [0110] -> 6?             MEDIUM BLUE (BLUE)
749 7D DB BA -> 14 [0111] -> 14?    AQUAMARINE (AQUA)
750 7D 20 41 ->  1 [1000] -> 1?             DEEP RED (MAGENTA)
751 BA 6D 41 ->  9 [1001] -> 9?             ORANGE
752 7D 7D 7D ->  5 [1010] -> 5?             DARK GRAY (Grays are identical)
753 BA CB 7D -> 13 [1011] -> 13?    YELLOW
754 BE 51 BE ->  3 [1100] -> 3              PURPLE (VIOLET)
755 FB 9E BE -> 11 [1101] -> 11?    PINK
756 BE AE FB ->  7 [1110] -> 7?             LIGHT BLUE (CYAN)
757 FB FB FB -> 15 [1111] -> 15             WHITE
758 */
759         uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
760         // Rotated one bit right (in the nybble)--right instead of left because
761         // these are backwards after all :-P
762         uint8_t mirrorNybble2[16] = { 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15 };
763         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
764
765         for(uint16_t y=0; y<24; y++)
766         {
767                 // Do top half of double lores screen bytes...
768
769                 uint32_t previous3Bits = 0;
770
771                 for(uint16_t x=0; x<40; x+=2)
772                 {
773                         uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] & 0x0F;
774                         uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] & 0x0F;
775                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] & 0x0F;
776                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] & 0x0F;
777                         scrByte1 = mirrorNybble[scrByte1];
778                         scrByte2 = mirrorNybble[scrByte2];
779                         scrByte3 = mirrorNybble2[scrByte3];
780                         scrByte4 = mirrorNybble2[scrByte4];
781                         // This is just a guess, but it'll have to do for now...
782                         uint32_t pixels = previous3Bits | (scrByte3 << 24)
783                                 | (scrByte3 << 20) | (scrByte1 << 16)
784                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
785                                 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
786
787                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
788                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
789                         // 31   27   23   19   15    11   7    3  0
790
791                         if (screenType == ST_COLOR_TV)
792                         {
793                                 for(uint8_t i=0; i<7; i++)
794                                 {
795                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
796                                         pixels <<= 4;
797
798                                         for(uint8_t j=0; j<4; j++)
799                                         {
800                                                 uint8_t color = blurTable[bitPat][j];
801
802                                                 for(uint32_t cy=0; cy<8; cy++)
803                                                 {
804                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
805 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
806                                                 }
807                                         }
808                                 }
809
810                                 previous3Bits = pixels & 0x70000000;
811                         }
812                         else
813                         {
814                                 for(int j=0; j<28; j++)
815                                 {
816                                         for(uint32_t cy=0; cy<8; cy++)
817                                         {
818                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
819                                         }
820
821                                         pixels <<= 1;
822                                 }
823                         }
824                 }
825
826                 // Now do bottom half...
827
828                 previous3Bits = 0;
829
830                 for(uint16_t x=0; x<40; x+=2)
831                 {
832                         uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] >> 4;
833                         uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] >> 4;
834                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] >> 4;
835                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] >> 4;
836                         scrByte1 = mirrorNybble[scrByte1];
837                         scrByte2 = mirrorNybble[scrByte2];
838                         scrByte3 = mirrorNybble2[scrByte3];
839                         scrByte4 = mirrorNybble2[scrByte4];
840                         // This is just a guess, but it'll have to do for now...
841 //                      uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
842 //                              | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
843 //                              | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
844                         uint32_t pixels = previous3Bits | (scrByte3 << 24)
845                                 | (scrByte3 << 20) | (scrByte1 << 16)
846                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
847                                 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
848
849                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
850                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
851                         // 31   27   23   19   15    11   7    3  0
852
853                         if (screenType == ST_COLOR_TV)
854                         {
855                                 for(uint8_t i=0; i<7; i++)
856                                 {
857                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
858                                         pixels <<= 4;
859
860                                         for(uint8_t j=0; j<4; j++)
861                                         {
862                                                 uint8_t color = blurTable[bitPat][j];
863
864                                                 for(uint32_t cy=8; cy<16; cy++)
865                                                 {
866                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
867                                                 }
868                                         }
869                                 }
870
871                                 previous3Bits = pixels & 0x70000000;
872                         }
873                         else
874                         {
875                                 for(int j=0; j<28; j++)
876                                 {
877                                         for(uint32_t cy=8; cy<16; cy++)
878                                         {
879                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
880                                         }
881
882                                         pixels <<= 1;
883                                 }
884                         }
885                 }
886         }
887 }
888
889
890 static void RenderHiRes(uint16_t toLine/*= 192*/)
891 {
892 //printf("RenderHiRes to line %u\n", toLine);
893 // NOTE: Not endian safe. !!! FIX !!! [DONE]
894 #if 0
895         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
896 #else
897 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
898 // The colors are set in the 8-bit array as R G B A
899         uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
900         uint32_t * colorPtr = (uint32_t *)monoColors;
901         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
902 #endif
903
904         for(uint16_t y=0; y<toLine; y++)
905         {
906                 uint16_t previousLoPixel = 0;
907                 uint32_t previous3bits = 0;
908
909                 for(uint16_t x=0; x<40; x+=2)
910                 {
911                         uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
912                         uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
913                         previousLoPixel = (screenByte << 2) & 0x0100;
914
915                         screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
916                         uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
917                         previousLoPixel = (screenByte << 2) & 0x0100;
918
919                         pixels = previous3bits | (pixels << 14) | pixels2;
920
921 //testing (this shows on the screen, so it's OK)
922 //if (x == 0)
923 //{
924 //      pixels = 0x7FFFFFFF;
925 //}
926
927                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
928                         // 0ppp 1111 1111 1111 1111 1111 1111 1111
929                         // 31   27   23   19   15   11   7    3  0
930
931                         if (screenType == ST_COLOR_TV)
932                         {
933                                 for(uint8_t i=0; i<7; i++)
934                                 {
935                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
936                                         pixels <<= 4;
937
938                                         for(uint8_t j=0; j<4; j++)
939                                         {
940                                                 uint8_t color = blurTable[bitPat][j];
941 #if 0
942 //This doesn't seem to make things go any faster...
943 //It's the OpenGL render that's faster... Hmm...
944                                                 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
945 #else
946                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
947                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
948 #endif
949                                         }
950                                 }
951
952                                 previous3bits = pixels & 0x70000000;
953                         }
954                         else
955                         {
956                                 for(int j=0; j<28; j++)
957                                 {
958                                         scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
959
960                                         if (screenType == ST_GREEN_MONO)
961                                                 pixels &= 0x07FFFFFF;
962
963                                         scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
964                                         pixels <<= 1;
965                                 }
966                         }
967                 }
968         }
969 }
970
971
972 static void RenderDHiRes(uint16_t toLine/*= 192*/)
973 {
974 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
975 // The colors are set in the 8-bit array as R G B A
976         uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
977         uint32_t * colorPtr = (uint32_t *)monoColors;
978         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
979
980         for(uint16_t y=0; y<toLine; y++)
981         {
982                 uint32_t previous4bits = 0;
983
984                 for(uint16_t x=0; x<40; x+=2)
985                 {
986                         uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
987                         uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
988                         screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
989                         pixels = pixels | (mirrorTable[screenByte & 0x7F]);
990                         screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
991                         pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
992                         screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
993                         pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
994                         pixels = previous4bits | (pixels >> 1);
995
996                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
997                         // 0ppp 1111 1111 1111 1111 1111 1111 1111
998                         // 31   27   23   19   15   11   7    3  0
999
1000                         if (screenType == ST_COLOR_TV)
1001                         {
1002                                 for(uint8_t i=0; i<7; i++)
1003                                 {
1004                                         uint8_t bitPat = (pixels & 0xFE000000) >> 25;
1005                                         pixels <<= 4;
1006
1007                                         for(uint8_t j=0; j<4; j++)
1008                                         {
1009                                                 uint32_t color = palette[blurTable[bitPat][j]];
1010                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
1011                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
1012                                         }
1013                                 }
1014
1015                                 previous4bits = pixels & 0xF0000000;
1016                         }
1017                         else
1018                         {
1019                                 for(int j=0; j<28; j++)
1020                                 {
1021                                         scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1022
1023                                         if (screenType == ST_GREEN_MONO)
1024                                                 pixels &= 0x07FFFFFF;
1025
1026                                         scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1027                                         pixels <<= 1;
1028                                 }
1029                         }
1030                 }
1031         }
1032 }
1033
1034
1035 void RenderVideoFrame(void)
1036 {
1037         if (GUI::powerOnState == true)
1038         {
1039                 if (textMode)
1040                 {
1041                         if (!col80Mode)
1042                                 Render40ColumnText();
1043                         else
1044                                 Render80ColumnText();
1045                 }
1046                 else
1047                 {
1048                         if (mixedMode)
1049                         {
1050                                 if (hiRes)
1051                                 {
1052                                         RenderHiRes(160);
1053                                         Render40ColumnTextLine(20);
1054                                         Render40ColumnTextLine(21);
1055                                         Render40ColumnTextLine(22);
1056                                         Render40ColumnTextLine(23);
1057                                 }
1058                                 else
1059                                 {
1060                                         RenderLoRes(20);
1061                                         Render40ColumnTextLine(20);
1062                                         Render40ColumnTextLine(21);
1063                                         Render40ColumnTextLine(22);
1064                                         Render40ColumnTextLine(23);
1065                                 }
1066                         }
1067                         else
1068                         {
1069                                 if (dhires)
1070                                 {
1071                                         if (hiRes)
1072                                                 RenderDHiRes();
1073                                         else
1074                                                 RenderDLoRes();
1075                                 }
1076                                 else if (hiRes)
1077                                         RenderHiRes();
1078                                 else
1079                                         RenderLoRes();
1080                         }
1081                 }
1082         }
1083         else
1084         {
1085                 memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
1086         }
1087
1088         if (msgTicks)
1089         {
1090                 DrawString();
1091                 msgTicks--;
1092         }
1093 }
1094
1095
1096 //
1097 // Prime SDL and create surfaces
1098 //
1099 bool InitVideo(void)
1100 {
1101         if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) != 0)
1102         {
1103                 WriteLog("Video: Could not initialize the SDL library: %s\n", SDL_GetError());
1104                 return false;
1105         }
1106
1107 //      int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, SDL_WINDOW_OPENGL, &sdlWindow, &sdlRenderer);
1108         int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0, &sdlWindow, &sdlRenderer);
1109 //      int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 1, VIRTUAL_SCREEN_HEIGHT * 1, 0, &sdlWindow, &sdlRenderer);
1110
1111         if (retVal != 0)
1112         {
1113                 WriteLog("Video: Could not window and/or renderer: %s\n", SDL_GetError());
1114                 return false;
1115         }
1116
1117         // Make the scaled rendering look smoother.
1118         SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
1119 //      SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
1120         SDL_RenderSetLogicalSize(sdlRenderer, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1121
1122         // Set the application's icon & title...
1123         SDL_Surface * iconSurface = SDL_CreateRGBSurfaceFrom(icon, 64, 64, 32, 64*4, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
1124         SDL_SetWindowIcon(sdlWindow, iconSurface);
1125         SDL_FreeSurface(iconSurface);
1126         SDL_SetWindowTitle(sdlWindow, "Apple2 Emulator");
1127
1128         sdlTexture = SDL_CreateTexture(sdlRenderer,
1129                 SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
1130                 VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1131
1132         SetupBlurTable();
1133
1134         WriteLog("Video: Successfully initialized.\n");
1135         return true;
1136 }
1137
1138
1139 //
1140 // Free various SDL components
1141 //
1142 void VideoDone(void)
1143 {
1144         WriteLog("Video: Shutting down SDL...\n");
1145         SDL_DestroyTexture(sdlTexture);
1146         SDL_DestroyRenderer(sdlRenderer);
1147         SDL_DestroyWindow(sdlWindow);
1148         SDL_Quit();
1149         WriteLog("Video: Done.\n");
1150 }
1151
1152
1153 //
1154 // Render the Apple video screen to the primary texture
1155 //
1156 void RenderAppleScreen(SDL_Renderer * renderer)
1157 {
1158         SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch);
1159         RenderVideoFrame();
1160         SDL_UnlockTexture(sdlTexture);
1161         SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
1162 }
1163
1164
1165 //
1166 // Fullscreen <-> window switching
1167 //
1168 void ToggleFullScreen(void)
1169 {
1170         settings.fullscreen = !settings.fullscreen;
1171
1172         int retVal = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1173
1174         if (retVal != 0)
1175                 WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());
1176 }
1177