2 // Apple 2 video support
4 // All the video modes that a real Apple 2 supports are handled here
7 // (c) 2005-2017 Underground Software
9 // JLH = James Hammons <jlhamm@acm.org>
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
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]
26 // Display routines seem MUCH slower now... !!! INVESTIGATE !!! [not anymore]
30 #include <string.h> // for memset()
32 #include <stdarg.h> // for va_* stuff
34 #include "apple2-icon-64x64.h"
38 #include "gui/font14pt.h"
41 /* Reference: Technote tn-iigs-063 "Master Color Values"
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)
63 LR: Lo-Res HR: Hi-Res DHR: Double Hi-Res
65 N.B.: These colors look like shit */
71 bool mixedMode = false;
72 bool displayPage2 = false;
74 bool alternateCharset = false;
75 bool col80Mode = false;
76 SDL_Renderer * sdlRenderer = NULL;
80 static SDL_Window * sdlWindow = NULL;
81 static SDL_Texture * sdlTexture = NULL;
82 static uint32_t * scrBuffer;
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.
88 // "Master Color Values" palette
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
109 // This palette comes from ApplePC's colors (more realistic to my eye ;-)
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 };
129 // Lo-res starting line addresses
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 };
136 // Hi-res starting line addresses
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,
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,
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,
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,
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,
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 };
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
203 // Second half adds in the previous byte's lo pixel
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
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;
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*/);
258 void SetupBlurTable(void)
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.
266 // for(uint16_t bitPat=0; bitPat<0x800; bitPat++)
267 for(uint16_t bitPat=0; bitPat<0x80; bitPat++)
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;
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;
283 for(int8_t i=7; i>=0; i--)
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;
293 for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
295 uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
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;
302 for(int8_t i=7; i>=0; i--)
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;
313 for(int i=0; i<256; i++)
315 mirrorTable[i] = ((i & 0x01) << 7)
327 void TogglePalette(void)
329 if (palette == (uint32_t *)colors)
331 palette = (uint32_t *)altColors;
332 SpawnMessage("Color TV palette");
336 palette = (uint32_t *)colors;
337 SpawnMessage("\"Master Color Values\" palette");
342 void CycleScreenTypes(void)
344 char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
348 if (screenType == ST_LAST_ENTRY)
349 screenType = ST_FIRST_ENTRY;
351 SpawnMessage("%s", scrTypeStr[screenType]);
355 static uint32_t msgTicks = 0;
356 static char message[4096];
358 void SpawnMessage(const char * text, ...)
363 vsprintf(message, text, arg);
370 static void DrawString2(uint32_t x, uint32_t y, uint32_t color);
371 static void DrawString(void)
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);
379 DrawString2(8, 8, 0x0020FF20);
383 static void DrawString2(uint32_t x, uint32_t y, uint32_t color)
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...
390 uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
392 for(uint32_t i=0; i<length; i++)
394 uint8_t c = message[i];
395 c = (c < 32 ? 0 : c - 32);
396 uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
398 for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
400 for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
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;
407 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = pixel;//*/
409 // uint8_t trans = font1[fontAddr++];
410 uint8_t trans = font2[fontAddr++];
414 uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
416 uint8_t eBlue = (existingColor >> 16) & 0xFF,
417 eGreen = (existingColor >> 8) & 0xFF,
418 eRed = existingColor & 0xFF;
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;
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;
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;
436 address += FONT_WIDTH;
441 static void Render40ColumnTextLine(uint8_t line)
443 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
445 for(int x=0; x<40; x++)
447 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
449 // Render character at (x, y)
451 for(int cy=0; cy<8; cy++)
453 for(int cx=0; cx<7; cx++)
455 uint32_t pixel = 0xFF000000;
457 if (alternateCharset)
459 if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
463 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
465 if ((chr & 0xC0) == 0x40 && flash)
470 if (textChar2e[(chr * 56) + cx + (cy * 7)])
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;
477 // QnD method to get blank alternate lines in text mode
478 if (screenType == ST_GREEN_MONO)
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;
491 static void Render80ColumnTextLine(uint8_t line)
493 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
495 for(int x=0; x<80; x++)
500 chr = ram[lineAddrLoRes[line] + (x >> 1)];
502 chr = ram2[lineAddrLoRes[line] + (x >> 1)];
504 // Render character at (x, y)
506 for(int cy=0; cy<8; cy++)
508 for(int cx=0; cx<7; cx++)
510 uint32_t pixel = 0xFF000000;
512 if (alternateCharset)
514 if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
518 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
520 if ((chr & 0xC0) == 0x40 && flash)
525 if (textChar2e[(chr * 56) + cx + (cy * 7)])
529 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
531 // QnD method to get blank alternate lines in text mode
532 if (screenType == ST_GREEN_MONO)
535 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
542 static void Render40ColumnText(void)
544 for(uint8_t line=0; line<24; line++)
545 Render40ColumnTextLine(line);
549 static void Render80ColumnText(void)
551 for(uint8_t line=0; line<24; line++)
552 Render80ColumnTextLine(line);
556 static void RenderLoRes(uint16_t toLine/*= 24*/)
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.
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!
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
583 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
585 //This is the old "perfect monitor" rendering code...
586 /* if (screenType != ST_COLOR_TV) // Not correct, but for now...
589 for(uint16_t y=0; y<toLine; y++)
591 for(uint16_t x=0; x<40; x++)
593 uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
594 uint32_t pixel = palette[scrByte & 0x0F];
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;
600 pixel = palette[scrByte >> 4];
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;
610 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
612 for(uint16_t y=0; y<toLine; y++)
614 // Do top half of lores screen bytes...
616 uint32_t previous3Bits = 0;
618 for(uint16_t x=0; x<40; x+=2)
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;
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
633 if (screenType == ST_COLOR_TV)
635 for(uint8_t i=0; i<7; i++)
637 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
640 for(uint8_t j=0; j<4; j++)
642 uint8_t color = blurTable[bitPat][j];
644 for(uint32_t cy=0; cy<8; cy++)
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];
652 previous3Bits = pixels & 0x70000000;
656 for(int j=0; j<28; j++)
658 for(uint32_t cy=0; cy<8; cy++)
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);
669 // Now do bottom half...
673 for(uint16_t x=0; x<40; x+=2)
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;
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
688 if (screenType == ST_COLOR_TV)
690 for(uint8_t i=0; i<7; i++)
692 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
695 for(uint8_t j=0; j<4; j++)
697 uint8_t color = blurTable[bitPat][j];
699 for(uint32_t cy=8; cy<16; cy++)
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];
707 previous3Bits = pixels & 0x70000000;
711 for(int j=0; j<28; j++)
713 for(uint32_t cy=8; cy<16; cy++)
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);
728 // Render the Double Lo Res screen (HIRES off, DHIRES on)
730 static void RenderDLoRes(void)
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.
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.]
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
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);
765 for(uint16_t y=0; y<24; y++)
767 // Do top half of double lores screen bytes...
769 uint32_t previous3Bits = 0;
771 for(uint16_t x=0; x<40; x+=2)
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;
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
791 if (screenType == ST_COLOR_TV)
793 for(uint8_t i=0; i<7; i++)
795 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
798 for(uint8_t j=0; j<4; j++)
800 uint8_t color = blurTable[bitPat][j];
802 for(uint32_t cy=0; cy<8; cy++)
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];
810 previous3Bits = pixels & 0x70000000;
814 for(int j=0; j<28; j++)
816 for(uint32_t cy=0; cy<8; cy++)
818 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
826 // Now do bottom half...
830 for(uint16_t x=0; x<40; x+=2)
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;
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
853 if (screenType == ST_COLOR_TV)
855 for(uint8_t i=0; i<7; i++)
857 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
860 for(uint8_t j=0; j<4; j++)
862 uint8_t color = blurTable[bitPat][j];
864 for(uint32_t cy=8; cy<16; cy++)
866 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
871 previous3Bits = pixels & 0x70000000;
875 for(int j=0; j<28; j++)
877 for(uint32_t cy=8; cy<16; cy++)
879 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
890 static void RenderHiRes(uint16_t toLine/*= 192*/)
892 //printf("RenderHiRes to line %u\n", toLine);
893 // NOTE: Not endian safe. !!! FIX !!! [DONE]
895 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
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]);
904 for(uint16_t y=0; y<toLine; y++)
906 uint16_t previousLoPixel = 0;
907 uint32_t previous3bits = 0;
909 for(uint16_t x=0; x<40; x+=2)
911 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
912 uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
913 previousLoPixel = (screenByte << 2) & 0x0100;
915 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
916 uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
917 previousLoPixel = (screenByte << 2) & 0x0100;
919 pixels = previous3bits | (pixels << 14) | pixels2;
921 //testing (this shows on the screen, so it's OK)
924 // pixels = 0x7FFFFFFF;
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
931 if (screenType == ST_COLOR_TV)
933 for(uint8_t i=0; i<7; i++)
935 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
938 for(uint8_t j=0; j<4; j++)
940 uint8_t color = blurTable[bitPat][j];
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];
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];
952 previous3bits = pixels & 0x70000000;
956 for(int j=0; j<28; j++)
958 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
960 if (screenType == ST_GREEN_MONO)
961 pixels &= 0x07FFFFFF;
963 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
972 static void RenderDHiRes(uint16_t toLine/*= 192*/)
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]);
980 for(uint16_t y=0; y<toLine; y++)
982 uint32_t previous4bits = 0;
984 for(uint16_t x=0; x<40; x+=2)
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);
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
1000 if (screenType == ST_COLOR_TV)
1002 for(uint8_t i=0; i<7; i++)
1004 uint8_t bitPat = (pixels & 0xFE000000) >> 25;
1007 for(uint8_t j=0; j<4; j++)
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;
1015 previous4bits = pixels & 0xF0000000;
1019 for(int j=0; j<28; j++)
1021 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1023 if (screenType == ST_GREEN_MONO)
1024 pixels &= 0x07FFFFFF;
1026 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1035 void RenderVideoFrame(void)
1037 if (GUI::powerOnState == true)
1042 Render40ColumnText();
1044 Render80ColumnText();
1053 Render40ColumnTextLine(20);
1054 Render40ColumnTextLine(21);
1055 Render40ColumnTextLine(22);
1056 Render40ColumnTextLine(23);
1061 Render40ColumnTextLine(20);
1062 Render40ColumnTextLine(21);
1063 Render40ColumnTextLine(22);
1064 Render40ColumnTextLine(23);
1085 memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
1097 // Prime SDL and create surfaces
1099 bool InitVideo(void)
1101 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) != 0)
1103 WriteLog("Video: Could not initialize the SDL library: %s\n", SDL_GetError());
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);
1113 WriteLog("Video: Could not window and/or renderer: %s\n", SDL_GetError());
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);
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");
1128 sdlTexture = SDL_CreateTexture(sdlRenderer,
1129 SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
1130 VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1134 WriteLog("Video: Successfully initialized.\n");
1140 // Free various SDL components
1142 void VideoDone(void)
1144 WriteLog("Video: Shutting down SDL...\n");
1145 SDL_DestroyTexture(sdlTexture);
1146 SDL_DestroyRenderer(sdlRenderer);
1147 SDL_DestroyWindow(sdlWindow);
1149 WriteLog("Video: Done.\n");
1154 // Render the Apple video screen to the primary texture
1156 void RenderAppleScreen(SDL_Renderer * renderer)
1158 SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch);
1160 SDL_UnlockTexture(sdlTexture);
1161 SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
1166 // Fullscreen <-> window switching
1168 void ToggleFullScreen(void)
1170 settings.fullscreen = !settings.fullscreen;
1172 int retVal = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1175 WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());