2 // Apple 2 video support
4 // All the video modes that a real Apple 2 supports are handled here
7 // (c) 2005-2018 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
27 // Display routines seem MUCH slower now... !!! INVESTIGATE !!! [not anymore]
31 #include <string.h> // for memset()
33 #include <stdarg.h> // for va_* stuff
35 #include "apple2-icon-64x64.h"
39 #include "gui/font14pt.h"
42 /* Reference: Technote tn-iigs-063 "Master Color Values"
44 Color Color Register LR HR DHR Master Color R,G,B
45 Name Value # # # Value
46 ----------------------------------------------------
47 Black 0 0 0,4 0 $0000 (0,0,0)
48 (Magenta) Deep Red 1 1 1 $0D03 (D,0,3)
49 Dark Blue 2 2 8 $0009 (0,0,9)
50 (Violet) Purple 3 3 2 9 $0D2D (D,2,D)
51 Dark Green 4 4 4 $0072 (0,7,2)
52 (Gray 1) Dark Gray 5 5 5 $0555 (5,5,5)
53 (Blue) Medium Blue 6 6 6 C $022F (2,2,F)
54 (Cyan) Light Blue 7 7 D $06AF (6,A,F)
55 Brown 8 8 2 $0850 (8,5,0)
56 Orange 9 9 5 3 $0F60 (F,6,0)
57 (Gray 2) Light Gray A A A $0AAA (A,A,A)
58 Pink B B B $0F98 (F,9,8)
59 (Green) Light Green C C 1 6 $01D0 (1,D,0)
60 Yellow D D 7 $0FF0 (F,F,0)
61 (Aqua) Aquamarine E E E $04F9 (4,F,9)
62 White F F 3,7 F $0FFF (F,F,F)
64 LR: Lo-Res HR: Hi-Res DHR: Double Hi-Res
66 N.B.: These colors look like shit */
72 bool mixedMode = false;
73 bool displayPage2 = false;
75 bool alternateCharset = false;
76 bool col80Mode = false;
77 SDL_Renderer * sdlRenderer = NULL;
78 SDL_Window * sdlWindow = NULL;
82 static SDL_Texture * sdlTexture = NULL;
83 static uint32_t * scrBuffer;
85 static bool showFrameTicks = false;
87 // We set up the colors this way so that they'll be endian safe
88 // when we cast them to a uint32_t. Note that the format is RGBA.
90 // "Master Color Values" palette
92 static uint8_t colors[16 * 4] = {
93 0x00, 0x00, 0x00, 0xFF, // Black
94 0xDD, 0x00, 0x33, 0xFF, // Deep Red (Magenta)
95 0x00, 0x00, 0x99, 0xFF, // Dark Blue
96 0xDD, 0x22, 0xDD, 0xFF, // Purple (Violet)
97 0x00, 0x77, 0x22, 0xFF, // Dark Green
98 0x55, 0x55, 0x55, 0xFF, // Dark Gray (Gray 1)
99 0x22, 0x22, 0xFF, 0xFF, // Medium Blue (Blue)
100 0x66, 0xAA, 0xFF, 0xFF, // Light Blue (Cyan)
101 0x88, 0x55, 0x00, 0xFF, // Brown
102 0xFF, 0x66, 0x00, 0xFF, // Orange
103 0xAA, 0xAA, 0xAA, 0xFF, // Light Gray (Gray 2)
104 0xFF, 0x99, 0x88, 0xFF, // Pink
105 0x11, 0xDD, 0x00, 0xFF, // Light Green (Green)
106 0xFF, 0xFF, 0x00, 0xFF, // Yellow
107 0x44, 0xFF, 0x99, 0xFF, // Aquamarine (Aqua)
108 0xFF, 0xFF, 0xFF, 0xFF // White
111 // This palette comes from ApplePC's colors (more realistic to my eye ;-)
113 static uint8_t altColors[16 * 4] = {
114 0x00, 0x00, 0x00, 0xFF,
115 0x7D, 0x20, 0x41, 0xFF,
116 0x41, 0x30, 0x7D, 0xFF,
117 0xBE, 0x51, 0xBE, 0xFF,
118 0x00, 0x5D, 0x3C, 0xFF,
119 0x7D, 0x7D, 0x7D, 0xFF,
120 0x41, 0x8E, 0xBA, 0xFF,
121 0xBE, 0xAE, 0xFB, 0xFF,
122 0x3C, 0x4D, 0x00, 0xFF,
123 0xBA, 0x6D, 0x41, 0xFF,
124 0x7D, 0x7D, 0x7D, 0xFF,
125 0xFB, 0x9E, 0xBE, 0xFF,
126 0x3C, 0xAA, 0x3C, 0xFF,
127 0xBA, 0xCB, 0x7D, 0xFF,
128 0x7D, 0xDB, 0xBA, 0xFF,
129 0xFB, 0xFB, 0xFB, 0xFF };
131 // Lo-res starting line addresses
133 static uint16_t lineAddrLoRes[24] = {
134 0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
135 0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
136 0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 };
138 // Hi-res starting line addresses
140 static uint16_t lineAddrHiRes[192] = {
141 0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
142 0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,
143 0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,
144 0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,
146 0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,
147 0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,
148 0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,
149 0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,
151 0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,
152 0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,
153 0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,
154 0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,
156 0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,
157 0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,
158 0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,
159 0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,
161 0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,
162 0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,
163 0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,
164 0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,
166 0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,
167 0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,
168 0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,
169 0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 };
171 uint16_t appleHiresToMono[0x200] = {
172 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
173 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
174 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
175 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
176 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
177 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
178 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
179 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
180 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
181 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
182 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
183 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
184 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
185 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
186 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
187 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
188 0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80,
189 0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x
190 0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98,
191 0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x
192 0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86,
193 0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax
194 0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E,
195 0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx
196 0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81,
197 0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx
198 0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99,
199 0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx
200 0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87,
201 0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex
202 0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F,
203 0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx
205 // Second half adds in the previous byte's lo pixel
207 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
208 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
209 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
210 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
211 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
212 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
213 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
214 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
215 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
216 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
217 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
218 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
219 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
220 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
221 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
222 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
223 0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80,
224 0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x
225 0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98,
226 0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x
227 0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86,
228 0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax
229 0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E,
230 0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx
231 0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81,
232 0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx
233 0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99,
234 0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx
235 0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87,
236 0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex
237 0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F,
238 0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF // $Fx
241 static uint8_t blurTable[0x80][8]; // Color TV blur table
242 static uint8_t mirrorTable[0x100];
243 static uint32_t * palette = (uint32_t *)altColors;
244 enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
245 static uint8_t screenType = ST_COLOR_TV;
249 static void Render40ColumnTextLine(uint8_t line);
250 static void Render80ColumnTextLine(uint8_t line);
251 static void Render40ColumnText(void);
252 static void Render80ColumnText(void);
253 static void RenderLoRes(uint16_t toLine = 24);
254 static void RenderHiRes(uint16_t toLine = 192);
255 static void RenderDHiRes(uint16_t toLine = 192);
256 static void RenderVideoFrame(/*uint32_t *, int*/);
259 void SetupBlurTable(void)
261 // NOTE: This table only needs to be 7 bits wide instead of 11, since the
262 // last four bits are copies of the previous four...
263 // Odd. Doing the bit patterns from 0-$7F doesn't work, but going
264 // from 0-$7FF stepping by 16 does. Hm.
265 // 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+=0x10)
268 uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
270 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
271 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
272 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
273 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
275 for(int8_t i=7; i>=0; i--)
277 uint8_t color = (((blurred0 >> i) & 0x01) << 3)
278 | (((blurred1 >> i) & 0x01) << 2)
279 | (((blurred2 >> i) & 0x01) << 1)
280 | ((blurred3 >> i) & 0x01);
281 blurTable[bitPat >> 4][7 - i] = color;
285 for(int i=0; i<256; i++)
287 mirrorTable[i] = ((i & 0x01) << 7)
299 void TogglePalette(void)
301 if (palette == (uint32_t *)colors)
303 palette = (uint32_t *)altColors;
304 SpawnMessage("Color TV palette");
308 palette = (uint32_t *)colors;
309 SpawnMessage("\"Master Color Values\" palette");
314 void CycleScreenTypes(void)
316 char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
320 if (screenType == ST_LAST_ENTRY)
321 screenType = ST_FIRST_ENTRY;
323 SpawnMessage("%s", scrTypeStr[screenType]);
327 void ToggleTickDisplay(void)
329 showFrameTicks = !showFrameTicks;
333 static uint32_t msgTicks = 0;
334 static char message[4096];
336 void SpawnMessage(const char * text, ...)
341 vsprintf(message, text, arg);
348 static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg);
349 static void DrawString(void)
351 //This approach works, and seems to be fast enough... Though it probably would
352 //be better to make the oversized font to match this one...
353 for(uint32_t x=7; x<=9; x++)
354 for(uint32_t y=7; y<=9; y++)
355 DrawString2(x, y, 0x00000000, message);
357 DrawString2(8, 8, 0x0020FF20, message);
361 static void DrawString(uint32_t x, uint32_t y, uint32_t color, char * msg)
363 //This approach works, and seems to be fast enough... Though it probably would
364 //be better to make the oversized font to match this one...
365 for(uint32_t xx=x-1; xx<=x+1; xx++)
366 for(uint32_t yy=y-1; yy<=y+1; yy++)
367 DrawString2(xx, yy, 0x00000000, msg);
369 DrawString2(x, y, color, msg);
373 static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg)
375 uint32_t length = strlen(msg), address = x + (y * VIRTUAL_SCREEN_WIDTH);
376 uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
378 for(uint32_t i=0; i<length; i++)
381 c = (c < 32 ? 0 : c - 32);
382 uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
384 for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
386 for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
388 uint8_t trans = font2[fontAddr++];
392 uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
394 uint8_t eBlue = (existingColor >> 16) & 0xFF,
395 eGreen = (existingColor >> 8) & 0xFF,
396 eRed = existingColor & 0xFF;
398 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
399 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
400 //because dividing by 32 is faster than dividing by 31...!
401 uint8_t invTrans = 255 - trans;
403 uint32_t bRed = (eRed * invTrans + nRed * trans) / 255;
404 uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
405 uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255;
407 //THIS IS NOT ENDIAN SAFE
408 //NB: Setting the alpha channel here does nothing.
409 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
414 address += FONT_WIDTH;
419 static void DrawFrameTicks(void)
421 uint32_t color = 0x00FF2020;
422 uint32_t address = 8 + (24 * VIRTUAL_SCREEN_WIDTH);
424 for(uint32_t i=0; i<17; i++)
426 for(uint32_t yy=0; yy<5; yy++)
428 for(uint32_t xx=0; xx<9; xx++)
430 //THIS IS NOT ENDIAN SAFE
431 //NB: Setting the alpha channel here does nothing.
432 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000;
436 address += (5 * VIRTUAL_SCREEN_WIDTH);
439 address = 8 + (24 * VIRTUAL_SCREEN_WIDTH);
441 // frameTicks is the amount of time remaining; so to show the amount
442 // consumed, we subtract it from 17.
443 uint32_t bars = 17 - frameTicks;
445 if (bars & 0x80000000)
448 for(uint32_t i=0; i<17; i++)
450 for(uint32_t yy=1; yy<4; yy++)
452 for(uint32_t xx=1; xx<8; xx++)
454 //THIS IS NOT ENDIAN SAFE
455 //NB: Setting the alpha channel here does nothing.
456 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = (i < bars ? color : 0x003F0000);
460 address += (5 * VIRTUAL_SCREEN_WIDTH);
465 if ((frameTimePtr % 15) == 0)
467 // uint32_t prevClock = (frameTimePtr + 1) % 60;
468 uint64_t prevClock = (frameTimePtr + 1) % 60;
469 // float fps = 59.0f / (((float)frameTime[frameTimePtr] - (float)frameTime[prevClock]) / 1000.0f);
470 double fps = 59.0 / ((double)(frameTime[frameTimePtr] - frameTime[prevClock]) / (double)SDL_GetPerformanceFrequency());
471 sprintf(msg, "%.1lf FPS", fps);
474 DrawString(20, 24, color, msg);
478 static void Render40ColumnTextLine(uint8_t line)
480 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
482 for(int x=0; x<40; x++)
484 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
486 // Render character at (x, y)
488 for(int cy=0; cy<8; cy++)
490 for(int cx=0; cx<7; cx++)
492 uint32_t pixel = 0xFF000000;
494 if (alternateCharset)
496 if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
500 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
502 if ((chr & 0xC0) == 0x40 && flash)
507 if (textChar2e[(chr * 56) + cx + (cy * 7)])
511 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
512 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
514 // QnD method to get blank alternate lines in text mode
515 if (screenType == ST_GREEN_MONO)
519 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
520 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
528 static void Render80ColumnTextLine(uint8_t line)
530 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
532 for(int x=0; x<80; x++)
537 chr = ram[lineAddrLoRes[line] + (x >> 1)];
539 chr = ram2[lineAddrLoRes[line] + (x >> 1)];
541 // Render character at (x, y)
543 for(int cy=0; cy<8; cy++)
545 for(int cx=0; cx<7; cx++)
547 uint32_t pixel = 0xFF000000;
549 if (alternateCharset)
551 if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
555 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
557 if ((chr & 0xC0) == 0x40 && flash)
562 if (textChar2e[(chr * 56) + cx + (cy * 7)])
566 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
568 // QnD method to get blank alternate lines in text mode
569 if (screenType == ST_GREEN_MONO)
572 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
579 static void Render40ColumnText(void)
581 for(uint8_t line=0; line<24; line++)
582 Render40ColumnTextLine(line);
586 static void Render80ColumnText(void)
588 for(uint8_t line=0; line<24; line++)
589 Render80ColumnTextLine(line);
593 static void RenderLoRes(uint16_t toLine/*= 24*/)
595 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
596 // Also, we could set up three different Render functions depending on which
597 // render type was set and call it with a function pointer. Would be faster
598 // then the nested ifs we have now.
600 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
601 Color #s correspond to the bit patterns in reverse... Interesting!
603 00 00 00 -> 0 [0000] -> 0 (lores color #)
604 3c 4d 00 -> 8 [0001] -> 8? BROWN
605 00 5d 3c -> 4 [0010] -> 4? DARK GREEN
606 3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
607 41 30 7d -> 2 [0100] -> 2? DARK BLUE
608 7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY
609 41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
610 7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA)
611 7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
612 ba 6d 41 -> 9 [1001] -> 9? ORANGE
613 7d 7d 7d -> 5 [1010] -> 5? DARK GRAY
614 ba cb 7d -> 13 [1011] -> 13? YELLOW
615 be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET)
616 fb 9e be -> 11 [1101] -> 11? PINK
617 be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
618 fb fb fb -> 15 [1111] -> 15 WHITE
620 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
622 //This is the old "perfect monitor" rendering code...
623 /* if (screenType != ST_COLOR_TV) // Not correct, but for now...
626 for(uint16_t y=0; y<toLine; y++)
628 for(uint16_t x=0; x<40; x++)
630 uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
631 uint32_t pixel = palette[scrByte & 0x0F];
633 for(int cy=0; cy<4; cy++)
634 for(int cx=0; cx<14; cx++)
635 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
637 pixel = palette[scrByte >> 4];
639 for(int cy=4; cy<8; cy++)
640 for(int cx=0; cx<14; cx++)
641 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
647 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
649 for(uint16_t y=0; y<toLine; y++)
651 // Do top half of lores screen bytes...
653 uint32_t previous3Bits = 0;
655 for(uint16_t x=0; x<40; x+=2)
657 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
658 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
659 scrByte1 = mirrorNybble[scrByte1];
660 scrByte2 = mirrorNybble[scrByte2];
661 // This is just a guess, but it'll have to do for now...
662 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
663 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
664 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
666 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
667 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
668 // 31 27 23 19 15 11 7 3 0
670 if (screenType == ST_COLOR_TV)
672 for(uint8_t i=0; i<7; i++)
674 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
677 for(uint8_t j=0; j<4; j++)
679 uint8_t color = blurTable[bitPat][j];
681 for(uint32_t cy=0; cy<8; cy++)
683 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
684 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
689 previous3Bits = pixels & 0x70000000;
693 for(int j=0; j<28; j++)
695 for(uint32_t cy=0; cy<8; cy++)
697 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
698 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
706 // Now do bottom half...
710 for(uint16_t x=0; x<40; x+=2)
712 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
713 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
714 scrByte1 = mirrorNybble[scrByte1];
715 scrByte2 = mirrorNybble[scrByte2];
716 // This is just a guess, but it'll have to do for now...
717 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
718 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
719 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
721 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
722 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
723 // 31 27 23 19 15 11 7 3 0
725 if (screenType == ST_COLOR_TV)
727 for(uint8_t i=0; i<7; i++)
729 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
732 for(uint8_t j=0; j<4; j++)
734 uint8_t color = blurTable[bitPat][j];
736 for(uint32_t cy=8; cy<16; cy++)
738 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
739 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
744 previous3Bits = pixels & 0x70000000;
748 for(int j=0; j<28; j++)
750 for(uint32_t cy=8; cy<16; cy++)
752 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
753 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
765 // Render the Double Lo Res screen (HIRES off, DHIRES on)
767 static void RenderDLoRes(void)
769 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
770 // Also, we could set up three different Render functions depending on
771 // which render type was set and call it with a function pointer. Would be
772 // faster then the nested ifs we have now.
774 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
775 Color #s correspond to the bit patterns in reverse... Interesting! [It's because
776 the video generator reads the bit patters from bit 0--which makes them backwards
777 from the normal POV.]
779 00 00 00 -> 0 [0000] -> 0 (lores color #)
780 3C 4D 00 -> 8 [0001] -> 8? BROWN
781 00 5D 3C -> 4 [0010] -> 4? DARK GREEN
782 3C AA 3C -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
783 41 30 7D -> 2 [0100] -> 2? DARK BLUE
784 7D 7D 7D -> 10 [0101] -> 10? LIGHT GRAY (Grays are identical)
785 41 8E BA -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
786 7D DB BA -> 14 [0111] -> 14? AQUAMARINE (AQUA)
787 7D 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
788 BA 6D 41 -> 9 [1001] -> 9? ORANGE
789 7D 7D 7D -> 5 [1010] -> 5? DARK GRAY (Grays are identical)
790 BA CB 7D -> 13 [1011] -> 13? YELLOW
791 BE 51 BE -> 3 [1100] -> 3 PURPLE (VIOLET)
792 FB 9E BE -> 11 [1101] -> 11? PINK
793 BE AE FB -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
794 FB FB FB -> 15 [1111] -> 15 WHITE
796 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
797 // Rotated one bit right (in the nybble)--right instead of left because
798 // these are backwards after all :-P
799 uint8_t mirrorNybble2[16] = { 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15 };
800 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
802 for(uint16_t y=0; y<24; y++)
804 // Do top half of double lores screen bytes...
806 uint32_t previous3Bits = 0;
808 for(uint16_t x=0; x<40; x+=2)
810 uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] & 0x0F;
811 uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] & 0x0F;
812 uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] & 0x0F;
813 uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] & 0x0F;
814 scrByte1 = mirrorNybble[scrByte1];
815 scrByte2 = mirrorNybble[scrByte2];
816 scrByte3 = mirrorNybble2[scrByte3];
817 scrByte4 = mirrorNybble2[scrByte4];
818 // This is just a guess, but it'll have to do for now...
819 uint32_t pixels = previous3Bits | (scrByte3 << 24)
820 | (scrByte3 << 20) | (scrByte1 << 16)
821 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
822 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
824 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
825 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
826 // 31 27 23 19 15 11 7 3 0
828 if (screenType == ST_COLOR_TV)
830 for(uint8_t i=0; i<7; i++)
832 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
835 for(uint8_t j=0; j<4; j++)
837 uint8_t color = blurTable[bitPat][j];
839 for(uint32_t cy=0; cy<8; cy++)
841 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
842 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
847 previous3Bits = pixels & 0x70000000;
851 for(int j=0; j<28; j++)
853 for(uint32_t cy=0; cy<8; cy++)
855 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
863 // Now do bottom half...
867 for(uint16_t x=0; x<40; x+=2)
869 uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] >> 4;
870 uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] >> 4;
871 uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] >> 4;
872 uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] >> 4;
873 scrByte1 = mirrorNybble[scrByte1];
874 scrByte2 = mirrorNybble[scrByte2];
875 scrByte3 = mirrorNybble2[scrByte3];
876 scrByte4 = mirrorNybble2[scrByte4];
877 // This is just a guess, but it'll have to do for now...
878 // uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
879 // | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
880 // | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
881 uint32_t pixels = previous3Bits | (scrByte3 << 24)
882 | (scrByte3 << 20) | (scrByte1 << 16)
883 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
884 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
886 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
887 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
888 // 31 27 23 19 15 11 7 3 0
890 if (screenType == ST_COLOR_TV)
892 for(uint8_t i=0; i<7; i++)
894 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
897 for(uint8_t j=0; j<4; j++)
899 uint8_t color = blurTable[bitPat][j];
901 for(uint32_t cy=8; cy<16; cy++)
903 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
908 previous3Bits = pixels & 0x70000000;
912 for(int j=0; j<28; j++)
914 for(uint32_t cy=8; cy<16; cy++)
916 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
927 static void RenderHiRes(uint16_t toLine/*= 192*/)
929 //printf("RenderHiRes to line %u\n", toLine);
930 // NOTE: Not endian safe. !!! FIX !!! [DONE]
932 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
934 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
935 // The colors are set in the 8-bit array as R G B A
936 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
937 uint32_t * colorPtr = (uint32_t *)monoColors;
938 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
941 for(uint16_t y=0; y<toLine; y++)
943 uint16_t previousLoPixel = 0;
944 uint32_t previous3bits = 0;
946 for(uint16_t x=0; x<40; x+=2)
948 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
949 uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
950 previousLoPixel = (screenByte << 2) & 0x0100;
952 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
953 uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
954 previousLoPixel = (screenByte << 2) & 0x0100;
956 pixels = previous3bits | (pixels << 14) | pixels2;
958 //testing (this shows on the screen, so it's OK)
961 // pixels = 0x7FFFFFFF;
964 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
965 // 0ppp 1111 1111 1111 1111 1111 1111 1111
966 // 31 27 23 19 15 11 7 3 0
968 if (screenType == ST_COLOR_TV)
970 for(uint8_t i=0; i<7; i++)
972 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
975 for(uint8_t j=0; j<4; j++)
977 uint8_t color = blurTable[bitPat][j];
979 //This doesn't seem to make things go any faster...
980 //It's the OpenGL render that's faster... Hmm...
981 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
983 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
984 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
989 previous3bits = pixels & 0x70000000;
993 for(int j=0; j<28; j++)
995 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
997 if (screenType == ST_GREEN_MONO)
998 pixels &= 0x07FFFFFF;
1000 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1009 static void RenderDHiRes(uint16_t toLine/*= 192*/)
1011 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
1012 // The colors are set in the 8-bit array as R G B A
1013 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
1014 uint32_t * colorPtr = (uint32_t *)monoColors;
1015 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
1017 for(uint16_t y=0; y<toLine; y++)
1019 uint32_t previous4bits = 0;
1021 for(uint16_t x=0; x<40; x+=2)
1023 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1024 uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
1025 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1026 pixels = pixels | (mirrorTable[screenByte & 0x7F]);
1027 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1028 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
1029 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1030 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
1031 pixels = previous4bits | (pixels >> 1);
1033 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
1034 // 0ppp 1111 1111 1111 1111 1111 1111 1111
1035 // 31 27 23 19 15 11 7 3 0
1037 if (screenType == ST_COLOR_TV)
1039 for(uint8_t i=0; i<7; i++)
1041 uint8_t bitPat = (pixels & 0xFE000000) >> 25;
1044 for(uint8_t j=0; j<4; j++)
1046 uint32_t color = palette[blurTable[bitPat][j]];
1047 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
1048 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
1052 previous4bits = pixels & 0xF0000000;
1056 for(int j=0; j<28; j++)
1058 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1060 if (screenType == ST_GREEN_MONO)
1061 pixels &= 0x07FFFFFF;
1063 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1072 void RenderVideoFrame(void)
1074 if (GUI::powerOnState == true)
1079 Render40ColumnText();
1081 Render80ColumnText();
1090 Render40ColumnTextLine(20);
1091 Render40ColumnTextLine(21);
1092 Render40ColumnTextLine(22);
1093 Render40ColumnTextLine(23);
1098 Render40ColumnTextLine(20);
1099 Render40ColumnTextLine(21);
1100 Render40ColumnTextLine(22);
1101 Render40ColumnTextLine(23);
1122 memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
1137 // Prime SDL and create surfaces
1139 bool InitVideo(void)
1141 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) != 0)
1143 WriteLog("Video: Could not initialize the SDL library: %s\n", SDL_GetError());
1148 int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0, &sdlWindow, &sdlRenderer);
1149 // int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 1, VIRTUAL_SCREEN_HEIGHT * 1, 0, &sdlWindow, &sdlRenderer);
1153 WriteLog("Video: Could not create window and/or renderer: %s\n", SDL_GetError());
1157 sdlWindow = SDL_CreateWindow("Apple2", settings.winX, settings.winY, VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0);
1159 if (sdlWindow == NULL)
1161 WriteLog("Video: Could not create window: %s\n", SDL_GetError());
1165 sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
1167 if (sdlRenderer == NULL)
1169 WriteLog("Video: Could not create renderer: %s\n", SDL_GetError());
1174 // Make sure what we put there is what we get:
1175 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
1176 SDL_RenderSetLogicalSize(sdlRenderer, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1178 // Set the application's icon & title...
1179 SDL_Surface * iconSurface = SDL_CreateRGBSurfaceFrom(icon, 64, 64, 32, 64*4, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
1180 SDL_SetWindowIcon(sdlWindow, iconSurface);
1181 SDL_FreeSurface(iconSurface);
1182 SDL_SetWindowTitle(sdlWindow, "Apple2 Emulator");
1184 sdlTexture = SDL_CreateTexture(sdlRenderer,
1185 SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
1186 VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1190 WriteLog("Video: Successfully initialized.\n");
1196 // Free various SDL components
1198 void VideoDone(void)
1200 WriteLog("Video: Shutting down SDL...\n");
1201 SDL_DestroyTexture(sdlTexture);
1202 SDL_DestroyRenderer(sdlRenderer);
1203 SDL_DestroyWindow(sdlWindow);
1205 WriteLog("Video: Done.\n");
1210 // Render the Apple video screen to the primary texture
1212 void RenderAppleScreen(SDL_Renderer * renderer)
1214 SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch);
1216 SDL_UnlockTexture(sdlTexture);
1217 SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
1222 // Fullscreen <-> window switching
1224 void ToggleFullScreen(void)
1226 settings.fullscreen = !settings.fullscreen;
1228 int retVal = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1231 WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());