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 RenderDLoRes(uint16_t toLine = 24);
255 static void RenderHiRes(uint16_t toLine = 192);
256 static void RenderDHiRes(uint16_t toLine = 192);
257 static void RenderVideoFrame(/*uint32_t *, int*/);
260 void SetupBlurTable(void)
262 // NOTE: This table only needs to be 7 bits wide instead of 11, since the
263 // last four bits are copies of the previous four...
264 // Odd. Doing the bit patterns from 0-$7F doesn't work, but going
265 // from 0-$7FF stepping by 16 does. Hm.
266 // Well, it seems that going from 0-$7F doesn't have enough precision to do the job.
267 for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
269 uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
271 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
272 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
273 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
274 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
276 for(int8_t i=7; i>=0; i--)
278 uint8_t color = (((blurred0 >> i) & 0x01) << 3)
279 | (((blurred1 >> i) & 0x01) << 2)
280 | (((blurred2 >> i) & 0x01) << 1)
281 | ((blurred3 >> i) & 0x01);
282 blurTable[bitPat >> 4][7 - i] = color;
286 for(int i=0; i<256; i++)
288 mirrorTable[i] = ((i & 0x01) << 7)
300 void TogglePalette(void)
302 if (palette == (uint32_t *)colors)
304 palette = (uint32_t *)altColors;
305 SpawnMessage("Color TV palette");
309 palette = (uint32_t *)colors;
310 SpawnMessage("\"Master Color Values\" palette");
315 void CycleScreenTypes(void)
317 char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
321 if (screenType == ST_LAST_ENTRY)
322 screenType = ST_FIRST_ENTRY;
324 SpawnMessage("%s", scrTypeStr[screenType]);
328 void ToggleTickDisplay(void)
330 showFrameTicks = !showFrameTicks;
334 static uint32_t msgTicks = 0;
335 static char message[4096];
337 void SpawnMessage(const char * text, ...)
342 vsprintf(message, text, arg);
346 //WriteLog("\n%s\n", message);
350 static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg);
351 static void DrawString(void)
353 //This approach works, and seems to be fast enough... Though it probably would
354 //be better to make the oversized font to match this one...
355 for(uint32_t x=7; x<=9; x++)
356 for(uint32_t y=7; y<=9; y++)
357 DrawString2(x, y, 0x00000000, message);
359 DrawString2(8, 8, 0x0020FF20, message);
363 static void DrawString(uint32_t x, uint32_t y, uint32_t color, char * msg)
365 //This approach works, and seems to be fast enough... Though it probably would
366 //be better to make the oversized font to match this one...
367 for(uint32_t xx=x-1; xx<=x+1; xx++)
368 for(uint32_t yy=y-1; yy<=y+1; yy++)
369 DrawString2(xx, yy, 0x00000000, msg);
371 DrawString2(x, y, color, msg);
375 static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg)
377 uint32_t length = strlen(msg), address = x + (y * VIRTUAL_SCREEN_WIDTH);
378 uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
380 for(uint32_t i=0; i<length; i++)
383 c = (c < 32 ? 0 : c - 32);
384 uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
386 for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
388 for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
390 uint8_t trans = font2[fontAddr++];
394 uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
396 uint8_t eBlue = (existingColor >> 16) & 0xFF,
397 eGreen = (existingColor >> 8) & 0xFF,
398 eRed = existingColor & 0xFF;
400 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
401 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
402 //because dividing by 32 is faster than dividing by 31...!
403 uint8_t invTrans = 255 - trans;
405 uint32_t bRed = (eRed * invTrans + nRed * trans) / 255;
406 uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
407 uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255;
409 //THIS IS NOT ENDIAN SAFE
410 //NB: Setting the alpha channel here does nothing.
411 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
416 address += FONT_WIDTH;
421 static void DrawFrameTicks(void)
423 uint32_t color = 0x00FF2020;
424 uint32_t address = 8 + (24 * VIRTUAL_SCREEN_WIDTH);
426 for(uint32_t i=0; i<17; i++)
428 for(uint32_t yy=0; yy<5; yy++)
430 for(uint32_t xx=0; xx<9; xx++)
432 //THIS IS NOT ENDIAN SAFE
433 //NB: Setting the alpha channel here does nothing.
434 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000;
438 address += (5 * VIRTUAL_SCREEN_WIDTH);
441 address = 8 + (24 * VIRTUAL_SCREEN_WIDTH);
443 // frameTicks is the amount of time remaining; so to show the amount
444 // consumed, we subtract it from 17.
445 uint32_t bars = 17 - frameTicks;
447 if (bars & 0x80000000)
450 for(uint32_t i=0; i<17; i++)
452 for(uint32_t yy=1; yy<4; yy++)
454 for(uint32_t xx=1; xx<8; xx++)
456 //THIS IS NOT ENDIAN SAFE
457 //NB: Setting the alpha channel here does nothing.
458 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = (i < bars ? color : 0x003F0000);
462 address += (5 * VIRTUAL_SCREEN_WIDTH);
467 if ((frameTimePtr % 15) == 0)
469 // uint32_t prevClock = (frameTimePtr + 1) % 60;
470 uint64_t prevClock = (frameTimePtr + 1) % 60;
471 // float fps = 59.0f / (((float)frameTime[frameTimePtr] - (float)frameTime[prevClock]) / 1000.0f);
472 double fps = 59.0 / ((double)(frameTime[frameTimePtr] - frameTime[prevClock]) / (double)SDL_GetPerformanceFrequency());
473 sprintf(msg, "%.1lf FPS", fps);
476 DrawString(20, 24, color, msg);
480 static void Render40ColumnTextLine(uint8_t line)
482 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
484 for(int x=0; x<40; x++)
486 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
488 // Render character at (x, y)
490 for(int cy=0; cy<8; cy++)
492 for(int cx=0; cx<7; cx++)
494 uint32_t pixel = 0xFF000000;
496 if (alternateCharset)
498 if (textChar2e[(chr * 56) + cx + (cy * 7)])
503 if ((chr & 0xC0) == 0x40)
505 if (textChar2e[((chr & 0x3F) * 56) + cx + (cy * 7)])
509 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
513 if (textChar2e[(chr * 56) + cx + (cy * 7)])
518 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
519 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
521 // QnD method to get blank alternate lines in text mode
522 if (screenType == ST_GREEN_MONO)
526 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
527 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
535 static void Render80ColumnTextLine(uint8_t line)
537 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
539 for(int x=0; x<80; x++)
544 chr = ram[lineAddrLoRes[line] + (x >> 1)];
546 chr = ram2[lineAddrLoRes[line] + (x >> 1)];
548 // Render character at (x, y)
550 for(int cy=0; cy<8; cy++)
552 for(int cx=0; cx<7; cx++)
554 uint32_t pixel = 0xFF000000;
556 if (alternateCharset)
558 if (textChar2e[(chr * 56) + cx + (cy * 7)])
563 if ((chr & 0xC0) == 0x40)
565 if (textChar2e[((chr & 0x3F) * 56) + cx + (cy * 7)])
569 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
573 if (textChar2e[(chr * 56) + cx + (cy * 7)])
578 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
580 // QnD method to get blank alternate lines in text mode
581 if (screenType == ST_GREEN_MONO)
584 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
591 static void Render40ColumnText(void)
593 for(uint8_t line=0; line<24; line++)
594 Render40ColumnTextLine(line);
598 static void Render80ColumnText(void)
600 for(uint8_t line=0; line<24; line++)
601 Render80ColumnTextLine(line);
605 static void RenderLoRes(uint16_t toLine/*= 24*/)
607 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
608 // Also, we could set up three different Render functions depending on which
609 // render type was set and call it with a function pointer. Would be faster
610 // then the nested ifs we have now.
612 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
613 Color #s correspond to the bit patterns in reverse... Interesting!
615 00 00 00 -> 0 [0000] -> 0 (lores color #)
616 3c 4d 00 -> 8 [0001] -> 8? BROWN
617 00 5d 3c -> 4 [0010] -> 4? DARK GREEN
618 3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
619 41 30 7d -> 2 [0100] -> 2? DARK BLUE
620 7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY
621 41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
622 7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA)
623 7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
624 ba 6d 41 -> 9 [1001] -> 9? ORANGE
625 7d 7d 7d -> 5 [1010] -> 5? DARK GRAY
626 ba cb 7d -> 13 [1011] -> 13? YELLOW
627 be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET)
628 fb 9e be -> 11 [1101] -> 11? PINK
629 be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
630 fb fb fb -> 15 [1111] -> 15 WHITE
632 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
634 //This is the old "perfect monitor" rendering code...
635 /* if (screenType != ST_COLOR_TV) // Not correct, but for now...
638 for(uint16_t y=0; y<toLine; y++)
640 for(uint16_t x=0; x<40; x++)
642 uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
643 uint32_t pixel = palette[scrByte & 0x0F];
645 for(int cy=0; cy<4; cy++)
646 for(int cx=0; cx<14; cx++)
647 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
649 pixel = palette[scrByte >> 4];
651 for(int cy=4; cy<8; cy++)
652 for(int cx=0; cx<14; cx++)
653 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
659 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
661 for(uint16_t y=0; y<toLine; y++)
663 // Do top half of lores screen bytes...
665 uint32_t previous3Bits = 0;
667 for(uint16_t x=0; x<40; x+=2)
669 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
670 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
671 scrByte1 = mirrorNybble[scrByte1];
672 scrByte2 = mirrorNybble[scrByte2];
673 // This is just a guess, but it'll have to do for now...
674 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
675 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
676 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
678 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
679 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
680 // 31 27 23 19 15 11 7 3 0
682 if (screenType == ST_COLOR_TV)
684 for(uint8_t i=0; i<7; i++)
686 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
689 for(uint8_t j=0; j<4; j++)
691 uint8_t color = blurTable[bitPat][j];
693 for(uint32_t cy=0; cy<8; cy++)
695 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
696 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
701 previous3Bits = pixels & 0x70000000;
705 for(int j=0; j<28; j++)
707 for(uint32_t cy=0; cy<8; cy++)
709 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
710 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
718 // Now do bottom half...
722 for(uint16_t x=0; x<40; x+=2)
724 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
725 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
726 scrByte1 = mirrorNybble[scrByte1];
727 scrByte2 = mirrorNybble[scrByte2];
728 // This is just a guess, but it'll have to do for now...
729 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
730 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
731 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
733 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
734 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
735 // 31 27 23 19 15 11 7 3 0
737 if (screenType == ST_COLOR_TV)
739 for(uint8_t i=0; i<7; i++)
741 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
744 for(uint8_t j=0; j<4; j++)
746 uint8_t color = blurTable[bitPat][j];
748 for(uint32_t cy=8; cy<16; cy++)
750 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
751 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
756 previous3Bits = pixels & 0x70000000;
760 for(int j=0; j<28; j++)
762 for(uint32_t cy=8; cy<16; cy++)
764 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
765 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
777 // Render the Double Lo Res screen (HIRES off, DHIRES on)
779 static void RenderDLoRes(uint16_t toLine/*= 24*/)
781 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
782 // Also, we could set up three different Render functions depending on
783 // which render type was set and call it with a function pointer. Would be
784 // faster then the nested ifs we have now.
786 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
787 Color #s correspond to the bit patterns in reverse... Interesting! [It's because
788 the video generator reads the bit patters from bit 0--which makes them backwards
789 from the normal POV.]
791 00 00 00 -> 0 [0000] -> 0 (lores color #)
792 3C 4D 00 -> 8 [0001] -> 8? BROWN
793 00 5D 3C -> 4 [0010] -> 4? DARK GREEN
794 3C AA 3C -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
795 41 30 7D -> 2 [0100] -> 2? DARK BLUE
796 7D 7D 7D -> 10 [0101] -> 10? LIGHT GRAY (Grays are identical)
797 41 8E BA -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
798 7D DB BA -> 14 [0111] -> 14? AQUAMARINE (AQUA)
799 7D 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
800 BA 6D 41 -> 9 [1001] -> 9? ORANGE
801 7D 7D 7D -> 5 [1010] -> 5? DARK GRAY (Grays are identical)
802 BA CB 7D -> 13 [1011] -> 13? YELLOW
803 BE 51 BE -> 3 [1100] -> 3 PURPLE (VIOLET)
804 FB 9E BE -> 11 [1101] -> 11? PINK
805 BE AE FB -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
806 FB FB FB -> 15 [1111] -> 15 WHITE
808 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
809 // Rotated one bit right (in the nybble)--right instead of left because
810 // these are backwards after all :-P
811 uint8_t mirrorNybble2[16] = { 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15 };
812 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
814 for(uint16_t y=0; y<toLine; y++)
816 // Do top half of double lores screen bytes...
818 uint32_t previous3Bits = 0;
820 for(uint16_t x=0; x<40; x+=2)
822 uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] & 0x0F;
823 uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] & 0x0F;
824 uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] & 0x0F;
825 uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] & 0x0F;
826 scrByte1 = mirrorNybble[scrByte1];
827 scrByte2 = mirrorNybble[scrByte2];
828 scrByte3 = mirrorNybble2[scrByte3];
829 scrByte4 = mirrorNybble2[scrByte4];
830 // This is just a guess, but it'll have to do for now...
831 uint32_t pixels = previous3Bits | (scrByte3 << 24)
832 | (scrByte3 << 20) | (scrByte1 << 16)
833 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
834 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
836 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
837 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
838 // 31 27 23 19 15 11 7 3 0
840 if (screenType == ST_COLOR_TV)
842 for(uint8_t i=0; i<7; i++)
844 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
847 for(uint8_t j=0; j<4; j++)
849 uint8_t color = blurTable[bitPat][j];
851 for(uint32_t cy=0; cy<8; cy++)
853 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
854 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
859 previous3Bits = pixels & 0x70000000;
863 for(int j=0; j<28; j++)
865 for(uint32_t cy=0; cy<8; cy++)
867 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
875 // Now do bottom half...
879 for(uint16_t x=0; x<40; x+=2)
881 uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] >> 4;
882 uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] >> 4;
883 uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] >> 4;
884 uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] >> 4;
885 scrByte1 = mirrorNybble[scrByte1];
886 scrByte2 = mirrorNybble[scrByte2];
887 scrByte3 = mirrorNybble2[scrByte3];
888 scrByte4 = mirrorNybble2[scrByte4];
889 // This is just a guess, but it'll have to do for now...
890 // uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
891 // | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
892 // | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
893 uint32_t pixels = previous3Bits | (scrByte3 << 24)
894 | (scrByte3 << 20) | (scrByte1 << 16)
895 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
896 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
898 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
899 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
900 // 31 27 23 19 15 11 7 3 0
902 if (screenType == ST_COLOR_TV)
904 for(uint8_t i=0; i<7; i++)
906 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
909 for(uint8_t j=0; j<4; j++)
911 uint8_t color = blurTable[bitPat][j];
913 for(uint32_t cy=8; cy<16; cy++)
915 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
920 previous3Bits = pixels & 0x70000000;
924 for(int j=0; j<28; j++)
926 for(uint32_t cy=8; cy<16; cy++)
928 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
939 static void RenderHiRes(uint16_t toLine/*= 192*/)
941 //printf("RenderHiRes to line %u\n", toLine);
942 // NOTE: Not endian safe. !!! FIX !!! [DONE]
944 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
946 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
947 // The colors are set in the 8-bit array as R G B A
948 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
949 uint32_t * colorPtr = (uint32_t *)monoColors;
950 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
953 for(uint16_t y=0; y<toLine; y++)
955 uint16_t previousLoPixel = 0;
956 uint32_t previous3bits = 0;
958 for(uint16_t x=0; x<40; x+=2)
960 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
961 uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
962 previousLoPixel = (screenByte << 2) & 0x0100;
964 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
965 uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
966 previousLoPixel = (screenByte << 2) & 0x0100;
968 pixels = previous3bits | (pixels << 14) | pixels2;
970 //testing (this shows on the screen, so it's OK)
973 // pixels = 0x7FFFFFFF;
976 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
977 // 0ppp 1111 1111 1111 1111 1111 1111 1111
978 // 31 27 23 19 15 11 7 3 0
980 if (screenType == ST_COLOR_TV)
982 for(uint8_t i=0; i<7; i++)
984 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
987 for(uint8_t j=0; j<4; j++)
989 uint8_t color = blurTable[bitPat][j];
991 //This doesn't seem to make things go any faster...
992 //It's the OpenGL render that's faster... Hmm...
993 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
995 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
996 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
1001 previous3bits = pixels & 0x70000000;
1005 for(int j=0; j<28; j++)
1007 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1009 if (screenType == ST_GREEN_MONO)
1010 pixels &= 0x07FFFFFF;
1012 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1021 static void RenderDHiRes(uint16_t toLine/*= 192*/)
1023 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
1024 // The colors are set in the 8-bit array as R G B A
1025 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
1026 uint32_t * colorPtr = (uint32_t *)monoColors;
1027 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
1029 for(uint16_t y=0; y<toLine; y++)
1031 uint32_t previous4bits = 0;
1033 for(uint16_t x=0; x<40; x+=2)
1035 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1036 uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
1037 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1038 pixels = pixels | (mirrorTable[screenByte & 0x7F]);
1039 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1040 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
1041 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1042 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
1043 pixels = previous4bits | (pixels >> 1);
1045 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
1046 // 0ppp 1111 1111 1111 1111 1111 1111 1111
1047 // 31 27 23 19 15 11 7 3 0
1049 if (screenType == ST_COLOR_TV)
1051 for(uint8_t i=0; i<7; i++)
1053 uint8_t bitPat = (pixels & 0xFE000000) >> 25;
1056 for(uint8_t j=0; j<4; j++)
1058 uint32_t color = palette[blurTable[bitPat][j]];
1059 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
1060 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
1064 previous4bits = pixels & 0xF0000000;
1068 for(int j=0; j<28; j++)
1070 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1072 if (screenType == ST_GREEN_MONO)
1073 pixels &= 0x07FFFFFF;
1075 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1084 void RenderVideoFrame(void)
1086 if (GUI::powerOnState == true)
1091 Render40ColumnText();
1093 Render80ColumnText();
1111 Render40ColumnTextLine(20);
1112 Render40ColumnTextLine(21);
1113 Render40ColumnTextLine(22);
1114 Render40ColumnTextLine(23);
1134 memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
1149 // Prime SDL and create surfaces
1151 bool InitVideo(void)
1153 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) != 0)
1155 WriteLog("Video: Could not initialize the SDL library: %s\n", SDL_GetError());
1160 int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0, &sdlWindow, &sdlRenderer);
1161 // int retVal = SDL_CreateWindowAndRenderer(VIRTUAL_SCREEN_WIDTH * 1, VIRTUAL_SCREEN_HEIGHT * 1, 0, &sdlWindow, &sdlRenderer);
1165 WriteLog("Video: Could not create window and/or renderer: %s\n", SDL_GetError());
1169 sdlWindow = SDL_CreateWindow("Apple2", settings.winX, settings.winY, VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0);
1171 if (sdlWindow == NULL)
1173 WriteLog("Video: Could not create window: %s\n", SDL_GetError());
1177 sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
1179 if (sdlRenderer == NULL)
1181 WriteLog("Video: Could not create renderer: %s\n", SDL_GetError());
1186 // Make sure what we put there is what we get:
1187 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
1188 SDL_RenderSetLogicalSize(sdlRenderer, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1190 // Set the application's icon & title...
1191 SDL_Surface * iconSurface = SDL_CreateRGBSurfaceFrom(icon, 64, 64, 32, 64*4, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
1192 SDL_SetWindowIcon(sdlWindow, iconSurface);
1193 SDL_FreeSurface(iconSurface);
1194 SDL_SetWindowTitle(sdlWindow, "Apple2 Emulator");
1196 sdlTexture = SDL_CreateTexture(sdlRenderer,
1197 SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
1198 VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1202 WriteLog("Video: Successfully initialized.\n");
1208 // Free various SDL components
1210 void VideoDone(void)
1212 WriteLog("Video: Shutting down SDL...\n");
1213 SDL_DestroyTexture(sdlTexture);
1214 SDL_DestroyRenderer(sdlRenderer);
1215 SDL_DestroyWindow(sdlWindow);
1217 WriteLog("Video: Done.\n");
1222 // Render the Apple video screen to the primary texture
1224 void RenderAppleScreen(SDL_Renderer * renderer)
1226 SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch);
1228 SDL_UnlockTexture(sdlTexture);
1229 SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
1234 // Fullscreen <-> window switching
1236 void ToggleFullScreen(void)
1238 settings.fullscreen = !settings.fullscreen;
1240 int retVal = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1243 WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());