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
29 #include <string.h> // for memset()
31 #include <stdarg.h> // for va_* stuff
33 #include "apple2-icon-64x64.h"
37 #include "gui/font14pt.h"
40 /* Reference: Technote tn-iigs-063 "Master Color Values"
42 Color Color Register LR HR DHR Master Color R,G,B
43 Name Value # # # Value
44 ----------------------------------------------------
45 Black 0 0 0,4 0 $0000 (0,0,0)
46 (Magenta) Deep Red 1 1 1 $0D03 (D,0,3)
47 Dark Blue 2 2 8 $0009 (0,0,9)
48 (Violet) Purple 3 3 2 9 $0D2D (D,2,D)
49 Dark Green 4 4 4 $0072 (0,7,2)
50 (Gray 1) Dark Gray 5 5 5 $0555 (5,5,5)
51 (Blue) Medium Blue 6 6 6 C $022F (2,2,F)
52 (Cyan) Light Blue 7 7 D $06AF (6,A,F)
53 Brown 8 8 2 $0850 (8,5,0)
54 Orange 9 9 5 3 $0F60 (F,6,0)
55 (Gray 2) Light Gray A A A $0AAA (A,A,A)
56 Pink B B B $0F98 (F,9,8)
57 (Green) Light Green C C 1 6 $01D0 (1,D,0)
58 Yellow D D 7 $0FF0 (F,F,0)
59 (Aqua) Aquamarine E E E $04F9 (4,F,9)
60 White F F 3,7 F $0FFF (F,F,F)
62 LR: Lo-Res HR: Hi-Res DHR: Double Hi-Res
64 N.B.: These colors look like shit */
70 bool mixedMode = false;
71 bool displayPage2 = false;
73 bool alternateCharset = false;
74 bool col80Mode = false;
75 SDL_Renderer * sdlRenderer = NULL;
76 SDL_Window * sdlWindow = NULL;
80 static SDL_Texture * sdlTexture = NULL;
81 static uint32_t * scrBuffer;
83 static bool showFrameTicks = false;
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[0x80][8]; // Color TV blur table
240 static uint8_t mirrorTable[0x100];
241 static uint32_t * palette = (uint32_t *)altColors;
242 enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
243 static uint8_t screenType = ST_COLOR_TV;
247 static void Render40ColumnTextLine(uint8_t line);
248 static void Render80ColumnTextLine(uint8_t line);
249 static void Render40ColumnText(void);
250 static void Render80ColumnText(void);
251 static void RenderLoRes(uint16_t toLine = 24);
252 static void RenderDLoRes(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.
265 for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
267 uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
269 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
270 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
271 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
272 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
274 for(int8_t i=7; i>=0; i--)
276 uint8_t color = (((blurred0 >> i) & 0x01) << 3)
277 | (((blurred1 >> i) & 0x01) << 2)
278 | (((blurred2 >> i) & 0x01) << 1)
279 | ((blurred3 >> i) & 0x01);
280 blurTable[bitPat >> 4][7 - i] = color;
284 for(int i=0; i<256; i++)
286 mirrorTable[i] = ((i & 0x01) << 7)
298 void TogglePalette(void)
300 if (palette == (uint32_t *)colors)
302 palette = (uint32_t *)altColors;
303 SpawnMessage("Color TV palette");
307 palette = (uint32_t *)colors;
308 SpawnMessage("\"Master Color Values\" palette");
313 void CycleScreenTypes(void)
315 char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
319 if (screenType == ST_LAST_ENTRY)
320 screenType = ST_FIRST_ENTRY;
322 SpawnMessage("%s", scrTypeStr[screenType]);
326 void ToggleTickDisplay(void)
328 showFrameTicks = !showFrameTicks;
332 static uint32_t msgTicks = 0;
333 static char message[4096];
335 void SpawnMessage(const char * text, ...)
340 vsprintf(message, text, arg);
344 //WriteLog("\n%s\n", message);
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 (textChar2e[(chr * 56) + cx + (cy * 7)])
501 if ((chr & 0xC0) == 0x40)
503 if (textChar2e[((chr & 0x3F) * 56) + cx + (cy * 7)])
507 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
511 if (textChar2e[(chr * 56) + cx + (cy * 7)])
516 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
517 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
519 // QnD method to get blank alternate lines in text mode
520 if (screenType == ST_GREEN_MONO)
524 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
525 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
533 static void Render80ColumnTextLine(uint8_t line)
535 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
537 for(int x=0; x<80; x++)
542 chr = ram[lineAddrLoRes[line] + (x >> 1)];
544 chr = ram2[lineAddrLoRes[line] + (x >> 1)];
546 // Render character at (x, y)
548 for(int cy=0; cy<8; cy++)
550 for(int cx=0; cx<7; cx++)
552 uint32_t pixel = 0xFF000000;
554 if (alternateCharset)
556 if (textChar2e[(chr * 56) + cx + (cy * 7)])
561 if ((chr & 0xC0) == 0x40)
563 if (textChar2e[((chr & 0x3F) * 56) + cx + (cy * 7)])
567 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
571 if (textChar2e[(chr * 56) + cx + (cy * 7)])
576 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
578 // QnD method to get blank alternate lines in text mode
579 if (screenType == ST_GREEN_MONO)
582 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
589 static void Render40ColumnText(void)
591 for(uint8_t line=0; line<24; line++)
592 Render40ColumnTextLine(line);
596 static void Render80ColumnText(void)
598 for(uint8_t line=0; line<24; line++)
599 Render80ColumnTextLine(line);
603 static void RenderLoRes(uint16_t toLine/*= 24*/)
605 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
606 // Also, we could set up three different Render functions depending on
607 // which render type was set and call it with a function pointer. Would
608 // be faster than the nested ifs we have now.
610 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
611 Color #s correspond to the bit patterns in reverse... Interesting!
613 00 00 00 -> 0 [0000] -> 0 (lores color #)
614 3c 4d 00 -> 8 [0001] -> 8? BROWN
615 00 5d 3c -> 4 [0010] -> 4? DARK GREEN
616 3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
617 41 30 7d -> 2 [0100] -> 2? DARK BLUE
618 7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY
619 41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
620 7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA)
621 7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
622 ba 6d 41 -> 9 [1001] -> 9? ORANGE
623 7d 7d 7d -> 5 [1010] -> 5? DARK GRAY
624 ba cb 7d -> 13 [1011] -> 13? YELLOW
625 be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET)
626 fb 9e be -> 11 [1101] -> 11? PINK
627 be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
628 fb fb fb -> 15 [1111] -> 15 WHITE
630 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
632 //This is the old "perfect monitor" rendering code...
633 /* if (screenType != ST_COLOR_TV) // Not correct, but for now...
636 for(uint16_t y=0; y<toLine; y++)
638 for(uint16_t x=0; x<40; x++)
640 uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
641 uint32_t pixel = palette[scrByte & 0x0F];
643 for(int cy=0; cy<4; cy++)
644 for(int cx=0; cx<14; cx++)
645 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
647 pixel = palette[scrByte >> 4];
649 for(int cy=4; cy<8; cy++)
650 for(int cx=0; cx<14; cx++)
651 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
657 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
659 for(uint16_t y=0; y<toLine; y++)
661 // Do top half of lores screen bytes...
663 uint32_t previous3Bits = 0;
665 for(uint16_t x=0; x<40; x+=2)
667 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
668 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
669 scrByte1 = mirrorNybble[scrByte1];
670 scrByte2 = mirrorNybble[scrByte2];
671 // This is just a guess, but it'll have to do for now...
672 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
673 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
674 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
676 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
677 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
678 // 31 27 23 19 15 11 7 3 0
680 if (screenType == ST_COLOR_TV)
682 for(uint8_t i=0; i<7; i++)
684 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
687 for(uint8_t j=0; j<4; j++)
689 uint8_t color = blurTable[bitPat][j];
691 for(uint32_t cy=0; cy<8; cy++)
693 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
694 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
699 previous3Bits = pixels & 0x70000000;
703 for(int j=0; j<28; j++)
705 for(uint32_t cy=0; cy<8; cy++)
707 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
708 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
716 // Now do bottom half...
720 for(uint16_t x=0; x<40; x+=2)
722 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
723 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
724 scrByte1 = mirrorNybble[scrByte1];
725 scrByte2 = mirrorNybble[scrByte2];
726 // This is just a guess, but it'll have to do for now...
727 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
728 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
729 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
731 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
732 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
733 // 31 27 23 19 15 11 7 3 0
735 if (screenType == ST_COLOR_TV)
737 for(uint8_t i=0; i<7; i++)
739 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
742 for(uint8_t j=0; j<4; j++)
744 uint8_t color = blurTable[bitPat][j];
746 for(uint32_t cy=8; cy<16; cy++)
748 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
749 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
754 previous3Bits = pixels & 0x70000000;
758 for(int j=0; j<28; j++)
760 for(uint32_t cy=8; cy<16; cy++)
762 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
763 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
775 // Render the Double Lo Res screen (HIRES off, DHIRES on)
777 static void RenderDLoRes(uint16_t toLine/*= 24*/)
779 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
780 // Also, we could set up three different Render functions depending on
781 // which render type was set and call it with a function pointer. Would be
782 // faster then the nested ifs we have now.
784 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
785 Color #s correspond to the bit patterns in reverse... Interesting! [It's because
786 the video generator reads the bit patters from bit 0--which makes them backwards
787 from the normal POV.]
789 00 00 00 -> 0 [0000] -> 0 (lores color #)
790 3C 4D 00 -> 8 [0001] -> 8? BROWN
791 00 5D 3C -> 4 [0010] -> 4? DARK GREEN
792 3C AA 3C -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
793 41 30 7D -> 2 [0100] -> 2? DARK BLUE
794 7D 7D 7D -> 10 [0101] -> 10? LIGHT GRAY (Grays are identical)
795 41 8E BA -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
796 7D DB BA -> 14 [0111] -> 14? AQUAMARINE (AQUA)
797 7D 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
798 BA 6D 41 -> 9 [1001] -> 9? ORANGE
799 7D 7D 7D -> 5 [1010] -> 5? DARK GRAY (Grays are identical)
800 BA CB 7D -> 13 [1011] -> 13? YELLOW
801 BE 51 BE -> 3 [1100] -> 3 PURPLE (VIOLET)
802 FB 9E BE -> 11 [1101] -> 11? PINK
803 BE AE FB -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
804 FB FB FB -> 15 [1111] -> 15 WHITE
806 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
807 // Rotated one bit right (in the nybble)--right instead of left because
808 // these are backwards after all :-P
809 uint8_t mirrorNybble2[16] = { 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15 };
810 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
812 for(uint16_t y=0; y<toLine; y++)
814 // Do top half of double lores screen bytes...
816 uint32_t previous3Bits = 0;
818 for(uint16_t x=0; x<40; x+=2)
820 uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] & 0x0F;
821 uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] & 0x0F;
822 uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] & 0x0F;
823 uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] & 0x0F;
824 scrByte1 = mirrorNybble[scrByte1];
825 scrByte2 = mirrorNybble[scrByte2];
826 scrByte3 = mirrorNybble2[scrByte3];
827 scrByte4 = mirrorNybble2[scrByte4];
828 // This is just a guess, but it'll have to do for now...
829 uint32_t pixels = previous3Bits | (scrByte3 << 24)
830 | (scrByte3 << 20) | (scrByte1 << 16)
831 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
832 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
834 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
835 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
836 // 31 27 23 19 15 11 7 3 0
838 if (screenType == ST_COLOR_TV)
840 for(uint8_t i=0; i<7; i++)
842 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
845 for(uint8_t j=0; j<4; j++)
847 uint8_t color = blurTable[bitPat][j];
849 for(uint32_t cy=0; cy<8; cy++)
851 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
852 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
857 previous3Bits = pixels & 0x70000000;
861 for(int j=0; j<28; j++)
863 for(uint32_t cy=0; cy<8; cy++)
865 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
873 // Now do bottom half...
877 for(uint16_t x=0; x<40; x+=2)
879 uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] >> 4;
880 uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] >> 4;
881 uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] >> 4;
882 uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] >> 4;
883 scrByte1 = mirrorNybble[scrByte1];
884 scrByte2 = mirrorNybble[scrByte2];
885 scrByte3 = mirrorNybble2[scrByte3];
886 scrByte4 = mirrorNybble2[scrByte4];
887 // This is just a guess, but it'll have to do for now...
888 // uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
889 // | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
890 // | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
891 uint32_t pixels = previous3Bits | (scrByte3 << 24)
892 | (scrByte3 << 20) | (scrByte1 << 16)
893 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
894 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
896 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
897 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
898 // 31 27 23 19 15 11 7 3 0
900 if (screenType == ST_COLOR_TV)
902 for(uint8_t i=0; i<7; i++)
904 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
907 for(uint8_t j=0; j<4; j++)
909 uint8_t color = blurTable[bitPat][j];
911 for(uint32_t cy=8; cy<16; cy++)
913 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
918 previous3Bits = pixels & 0x70000000;
922 for(int j=0; j<28; j++)
924 for(uint32_t cy=8; cy<16; cy++)
926 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
937 static void RenderHiRes(uint16_t toLine/*= 192*/)
939 //printf("RenderHiRes to line %u\n", toLine);
940 // NOTE: Not endian safe. !!! FIX !!! [DONE]
942 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
944 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
945 // The colors are set in the 8-bit array as R G B A
946 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
947 uint32_t * colorPtr = (uint32_t *)monoColors;
948 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
951 for(uint16_t y=0; y<toLine; y++)
953 uint16_t previousLoPixel = 0;
954 uint32_t previous3bits = 0;
956 for(uint16_t x=0; x<40; x+=2)
958 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
959 uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
960 previousLoPixel = (screenByte << 2) & 0x0100;
962 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
963 uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
964 previousLoPixel = (screenByte << 2) & 0x0100;
966 pixels = previous3bits | (pixels << 14) | pixels2;
968 //testing (this shows on the screen, so it's OK)
971 // pixels = 0x7FFFFFFF;
974 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
975 // 0ppp 1111 1111 1111 1111 1111 1111 1111
976 // 31 27 23 19 15 11 7 3 0
978 if (screenType == ST_COLOR_TV)
980 for(uint8_t i=0; i<7; i++)
982 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
985 for(uint8_t j=0; j<4; j++)
987 uint8_t color = blurTable[bitPat][j];
989 //This doesn't seem to make things go any faster...
990 //It's the OpenGL render that's faster... Hmm...
991 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
993 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
994 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
999 previous3bits = pixels & 0x70000000;
1003 for(int j=0; j<28; j++)
1005 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1007 if (screenType == ST_GREEN_MONO)
1008 pixels &= 0x07FFFFFF;
1010 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1019 static void RenderDHiRes(uint16_t toLine/*= 192*/)
1021 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
1022 // The colors are set in the 8-bit array as R G B A
1023 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
1024 uint32_t * colorPtr = (uint32_t *)monoColors;
1025 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
1027 for(uint16_t y=0; y<toLine; y++)
1029 uint32_t previous4bits = 0;
1031 for(uint16_t x=0; x<40; x+=2)
1033 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1034 uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
1035 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1036 pixels = pixels | (mirrorTable[screenByte & 0x7F]);
1037 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1038 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
1039 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1040 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
1041 pixels = previous4bits | (pixels >> 1);
1043 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
1044 // 0ppp 1111 1111 1111 1111 1111 1111 1111
1045 // 31 27 23 19 15 11 7 3 0
1047 if (screenType == ST_COLOR_TV)
1049 for(uint8_t i=0; i<7; i++)
1051 uint8_t bitPat = (pixels & 0xFE000000) >> 25;
1054 for(uint8_t j=0; j<4; j++)
1056 uint32_t color = palette[blurTable[bitPat][j]];
1057 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
1058 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
1062 previous4bits = pixels & 0xF0000000;
1066 for(int j=0; j<28; j++)
1068 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1070 if (screenType == ST_GREEN_MONO)
1071 pixels &= 0x07FFFFFF;
1073 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1082 void RenderVideoFrame(void)
1084 if (GUI::powerOnState == true)
1089 Render40ColumnText();
1091 Render80ColumnText();
1109 Render40ColumnTextLine(20);
1110 Render40ColumnTextLine(21);
1111 Render40ColumnTextLine(22);
1112 Render40ColumnTextLine(23);
1132 memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
1147 // Prime SDL and create surfaces
1149 bool InitVideo(void)
1151 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) != 0)
1153 WriteLog("Video: Could not initialize the SDL library: %s\n", SDL_GetError());
1158 int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0, &sdlWindow, &sdlRenderer);
1159 // int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 1, VIRTUAL_SCREEN_HEIGHT * 1, 0, &sdlWindow, &sdlRenderer);
1163 WriteLog("Video: Could not create window and/or renderer: %s\n", SDL_GetError());
1167 sdlWindow = SDL_CreateWindow("Apple2", settings.winX, settings.winY, VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0);
1169 if (sdlWindow == NULL)
1171 WriteLog("Video: Could not create window: %s\n", SDL_GetError());
1175 sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
1177 if (sdlRenderer == NULL)
1179 WriteLog("Video: Could not create renderer: %s\n", SDL_GetError());
1184 // Make sure what we put there is what we get:
1185 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
1186 SDL_RenderSetLogicalSize(sdlRenderer, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1188 // Set the application's icon & title...
1189 SDL_Surface * iconSurface = SDL_CreateRGBSurfaceFrom(icon, 64, 64, 32, 64*4, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
1190 SDL_SetWindowIcon(sdlWindow, iconSurface);
1191 SDL_FreeSurface(iconSurface);
1192 SDL_SetWindowTitle(sdlWindow, "Apple2 Emulator");
1194 sdlTexture = SDL_CreateTexture(sdlRenderer,
1195 SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
1196 VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1198 // Start in fullscreen, if user requested it via config file
1199 int response = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1202 WriteLog("Video::FullScreen: SDL error = %s\n", SDL_GetError());
1206 WriteLog("Video: Successfully initialized.\n");
1212 // Free various SDL components
1214 void VideoDone(void)
1216 WriteLog("Video: Shutting down SDL...\n");
1217 SDL_DestroyTexture(sdlTexture);
1218 SDL_DestroyRenderer(sdlRenderer);
1219 SDL_DestroyWindow(sdlWindow);
1221 WriteLog("Video: Done.\n");
1226 // Render the Apple video screen to the primary texture
1228 void RenderAppleScreen(SDL_Renderer * renderer)
1230 SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch);
1232 SDL_UnlockTexture(sdlTexture);
1233 SDL_RenderClear(renderer); // Without this, full screen has trash on the sides
1234 SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
1239 // Fullscreen <-> window switching
1241 void ToggleFullScreen(void)
1243 settings.fullscreen = !settings.fullscreen;
1245 int retVal = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1248 WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());