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 (ugly, Apple engineer hand-picked colors)
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 // This color palette comes from xapple2 (looks a bit shit to me :-)
130 // I've included it to have yet another point of comparison to the execrable
131 // "Master Color Values" palette.
133 * https://mrob.com/pub/xapple2/colors.html
134 * detailed research into accurate colors
136 Color name phase ampl luma -R- -G- -B-
137 black COLOR=0 0 0 0 0 0 0
138 gray COLOR=5 0 0 50 156 156 156
139 grey COLOR=10 0 0 50 156 156 156
140 white COLOR=15 0 0 100 255 255 255
141 dk blue COLOR=2 0 60 25 96 78 189
142 lt blue COLOR=7 0 60 75 208 195 255
143 purple COLOR=3 45 100 50 255 68 253
144 purple HCOLOR=2 45 100 50 255 68 253
145 red COLOR=1 90 60 25 227 30 96
146 pink COLOR=11 90 60 75 255 160 208
147 orange COLOR=9 135 100 50 255 106 60
148 orange HCOLOR=5 135 100 50 255 106 60
149 brown COLOR=8 180 60 25 96 114 3
150 yellow COLOR=13 180 60 75 208 221 141
151 lt green COLOR=12 225 100 50 20 245 60
152 green HCOLOR=1 225 100 50 20 245 60
153 dk green COLOR=4 270 60 25 0 163 96
154 aqua COLOR=14 270 60 75 114 255 208
155 med blue COLOR=6 315 100 50 20 207 253
156 blue HCOLOR=6 315 100 50 20 207 253
157 NTSC Hsync 0 0 -40 0 0 0
158 NTSC black 0 0 7.5 41 41 41
159 NTSC Gray75 0 0 77 212 212 212
160 YIQ +Q 33 100 50 255 81 255
161 NTSC magenta 61 82 36 255 40 181
162 NTSC red 104 88 28 255 28 76
163 YIQ +I 123 100 50 255 89 82
164 NTSC yellow 167 62 69 221 198 121
165 Color burst 180 40 0 0 4 0
166 YIQ -Q 213 100 50 51 232 41
167 NTSC green 241 82 48 12 234 97
168 NTSC cyan 284 88 56 10 245 198
169 YIQ -I 303 100 50 0 224 231
170 NTSC blue 347 62 15 38 65 155
173 static uint8_t robColors[16 * 4] = {
174 0x00, 0x00, 0x00, 0xFF, // Black
175 0xE3, 0x1E, 0x60, 0xFF, // Deep Red (Magenta) 227 30 96
176 0x60, 0x4E, 0xBD, 0xFF, // Dark Blue 96 78 189
177 0xFF, 0x44, 0xFD, 0xFF, // Purple (Violet) 255 68 253
178 0x00, 0xA3, 0x60, 0xFF, // Dark Green 0 163 96
179 0x9C, 0x9C, 0x9C, 0xFF, // Dark Gray (Gray 1) 156 156 156
180 0x14, 0xCF, 0xFD, 0xFF, // Medium Blue (Blue) 20 207 253
181 0xD0, 0xC3, 0xFF, 0xFF, // Light Blue (Cyan) 208 195 255
182 0x60, 0x72, 0x03, 0xFF, // Brown 96 114 3
183 0xFF, 0x6A, 0x3C, 0xFF, // Orange 255 106 60
184 0xD4, 0xD4, 0xD4, 0xFF, // Light Gray (Gray 2) 212 212 212
185 0xFF, 0xA0, 0xD0, 0xFF, // Pink 255 160 208
186 0x14, 0xF5, 0x3C, 0xFF, // Light Green (Green) 20 245 60
187 0xD0, 0xDD, 0x8D, 0xFF, // Yellow 208 221 141
188 0x72, 0xFF, 0xD0, 0xFF, // Aquamarine (Aqua) 114 255 208
189 0xFF, 0xFF, 0xFF, 0xFF // White
192 // This palette comes from the picture posted on website from robColors. It
193 // also looks worse than all the others. :-P
195 static uint8_t picColors[16 * 4] = {
196 0x00, 0x00, 0x00, 0xFF,
197 0xB0, 0x01, 0x68, 0xFF,
198 0x01, 0x19, 0xEB, 0xFF,
199 0xC9, 0x00, 0xEF, 0xFF,
200 0x25, 0x99, 0x00, 0xFF,
201 0x71, 0x70, 0x6E, 0xFF,
202 0x18, 0xB3, 0xE9, 0xFF,
203 0x8A, 0x88, 0xEB, 0xFF,
204 0x54, 0x5A, 0x02, 0xFF,
205 0xDF, 0x34, 0x00, 0xFF,
206 0x70, 0x6E, 0x6F, 0xFF,
207 0xE1, 0x49, 0xE9, 0xFF,
208 0x38, 0xFF, 0x00, 0xFF,
209 0xD5, 0xD8, 0x01, 0xFF,
210 0x45, 0xFF, 0x75, 0xFF,
211 0xED, 0xEB, 0xEE, 0xFF
214 // Lo-res starting line addresses
216 static uint16_t lineAddrLoRes[24] = {
217 0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
218 0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
219 0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 };
221 // Hi-res starting line addresses
223 static uint16_t lineAddrHiRes[192] = {
224 0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
225 0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,
226 0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,
227 0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,
229 0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,
230 0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,
231 0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,
232 0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,
234 0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,
235 0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,
236 0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,
237 0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,
239 0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,
240 0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,
241 0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,
242 0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,
244 0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,
245 0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,
246 0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,
247 0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,
249 0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,
250 0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,
251 0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,
252 0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 };
254 uint16_t appleHiresToMono[0x200] = {
255 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
256 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
257 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
258 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
259 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
260 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
261 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
262 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
263 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
264 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
265 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
266 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
267 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
268 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
269 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
270 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
271 0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80,
272 0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x
273 0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98,
274 0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x
275 0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86,
276 0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax
277 0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E,
278 0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx
279 0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81,
280 0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx
281 0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99,
282 0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx
283 0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87,
284 0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex
285 0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F,
286 0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx
288 // Second half adds in the previous byte's lo pixel
290 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
291 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
292 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
293 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
294 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
295 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
296 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
297 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
298 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
299 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
300 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
301 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
302 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
303 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
304 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
305 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
306 0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80,
307 0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x
308 0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98,
309 0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x
310 0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86,
311 0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax
312 0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E,
313 0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx
314 0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81,
315 0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx
316 0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99,
317 0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx
318 0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87,
319 0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex
320 0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F,
321 0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF // $Fx
324 static uint8_t blurTable[0x80][8]; // Color TV blur table
325 static uint8_t mirrorTable[0x100];
326 static uint32_t * palette = (uint32_t *)altColors;
327 enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
328 static uint8_t screenType = ST_COLOR_TV;
332 static void Render40ColumnTextLine(uint8_t line);
333 static void Render80ColumnTextLine(uint8_t line);
334 static void Render40ColumnText(void);
335 static void Render80ColumnText(void);
336 static void RenderLoRes(uint16_t toLine = 24);
337 static void RenderDLoRes(uint16_t toLine = 24);
338 static void RenderHiRes(uint16_t toLine = 192);
339 static void RenderDHiRes(uint16_t toLine = 192);
340 static void RenderVideoFrame();
342 void SetupBlurTable(void)
344 // NOTE: This table only needs to be 7 bits wide instead of 11, since the
345 // last four bits are copies of the previous four...
346 // Odd. Doing the bit patterns from 0-$7F doesn't work, but going
347 // from 0-$7FF stepping by 16 does. Hm.
348 // Well, it seems that going from 0-$7F doesn't have enough precision
350 for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
352 uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
354 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
355 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
356 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
357 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
359 for(int8_t i=7; i>=0; i--)
361 uint8_t color = (((blurred0 >> i) & 0x01) << 3)
362 | (((blurred1 >> i) & 0x01) << 2)
363 | (((blurred2 >> i) & 0x01) << 1)
364 | ((blurred3 >> i) & 0x01);
365 blurTable[bitPat >> 4][7 - i] = color;
369 for(int i=0; i<256; i++)
371 mirrorTable[i] = ((i & 0x01) << 7)
382 void TogglePalette(void)
384 if (palette == (uint32_t *)colors)
386 palette = (uint32_t *)altColors;
387 SpawnMessage("ApplePC Color TV palette");
389 else if (palette == (uint32_t *)altColors)
391 palette = (uint32_t *)picColors;
392 SpawnMessage("Picture Color TV palette");
394 else if (palette == (uint32_t *)picColors)
396 palette = (uint32_t *)robColors;
397 SpawnMessage("Rob's Color TV palette");
401 palette = (uint32_t *)colors;
402 SpawnMessage("\"Master Color Values\" palette");
406 void CycleScreenTypes(void)
408 char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
412 if (screenType == ST_LAST_ENTRY)
413 screenType = ST_FIRST_ENTRY;
415 SpawnMessage("%s", scrTypeStr[screenType]);
418 void ToggleTickDisplay(void)
420 showFrameTicks = !showFrameTicks;
423 static uint32_t msgTicks = 0;
424 static char message[4096];
426 void SpawnMessage(const char * text, ...)
431 vsprintf(message, text, arg);
437 static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg);
438 static void DrawString(void)
440 //This approach works, and seems to be fast enough... Though it probably would
441 //be better to make the oversized font to match this one...
442 for(uint32_t x=7; x<=9; x++)
443 for(uint32_t y=7; y<=9; y++)
444 DrawString2(x, y, 0x00000000, message);
446 DrawString2(8, 8, 0x0020FF20, message);
449 static void DrawString(uint32_t x, uint32_t y, uint32_t color, char * msg)
451 //This approach works, and seems to be fast enough... Though it probably would
452 //be better to make the oversized font to match this one...
453 for(uint32_t xx=x-1; xx<=x+1; xx++)
454 for(uint32_t yy=y-1; yy<=y+1; yy++)
455 DrawString2(xx, yy, 0x00000000, msg);
457 DrawString2(x, y, color, msg);
460 static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg)
462 uint32_t length = strlen(msg), address = x + (y * VIRTUAL_SCREEN_WIDTH);
463 uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
465 for(uint32_t i=0; i<length; i++)
468 c = (c < 32 ? 0 : c - 32);
469 uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
471 for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
473 for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
475 uint8_t trans = font2[fontAddr++];
479 uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
481 uint8_t eBlue = (existingColor >> 16) & 0xFF,
482 eGreen = (existingColor >> 8) & 0xFF,
483 eRed = existingColor & 0xFF;
485 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
486 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
487 //because dividing by 32 is faster than dividing by 31...!
488 uint8_t invTrans = 255 - trans;
490 uint32_t bRed = (eRed * invTrans + nRed * trans) / 255;
491 uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
492 uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255;
494 //THIS IS NOT ENDIAN SAFE
495 //NB: Setting the alpha channel here does nothing.
496 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
501 address += FONT_WIDTH;
505 static void DrawFrameTicks(void)
507 uint32_t color = 0x00FF2020;
508 uint32_t address = 8 + (24 * VIRTUAL_SCREEN_WIDTH);
510 for(uint32_t i=0; i<17; i++)
512 for(uint32_t yy=0; yy<5; yy++)
514 for(uint32_t xx=0; xx<9; xx++)
516 //THIS IS NOT ENDIAN SAFE
517 //NB: Setting the alpha channel here does nothing.
518 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000;
522 address += (5 * VIRTUAL_SCREEN_WIDTH);
525 address = 8 + (24 * VIRTUAL_SCREEN_WIDTH);
527 // frameTicks is the amount of time remaining; so to show the amount
528 // consumed, we subtract it from 17.
529 uint32_t bars = 17 - frameTicks;
531 if (bars & 0x80000000)
534 for(uint32_t i=0; i<17; i++)
536 for(uint32_t yy=1; yy<4; yy++)
538 for(uint32_t xx=1; xx<8; xx++)
540 //THIS IS NOT ENDIAN SAFE
541 //NB: Setting the alpha channel here does nothing.
542 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = (i < bars ? color : 0x003F0000);
546 address += (5 * VIRTUAL_SCREEN_WIDTH);
551 if ((frameTimePtr % 15) == 0)
553 uint64_t prevClock = (frameTimePtr + 1) % 60;
554 double fps = 59.0 / ((double)(frameTime[frameTimePtr] - frameTime[prevClock]) / (double)SDL_GetPerformanceFrequency());
555 sprintf(msg, "%.1lf FPS", fps);
558 DrawString(20, 24, color, msg);
561 static void Render40ColumnTextLine(uint8_t line)
563 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
565 for(int x=0; x<40; x++)
567 uint8_t chr = ram[lineAddrLoRes[line] + (!store80Mode && displayPage2 ? 0x0400 : 0x0000) + x];
569 // Render character at (x, y)
571 for(int cy=0; cy<8; cy++)
573 for(int cx=0; cx<7; cx++)
575 uint32_t pixel = 0xFF000000;
577 if (alternateCharset)
579 if (textChar2e[(chr * 56) + cx + (cy * 7)])
584 if ((chr & 0xC0) == 0x40)
586 if (textChar2e[((chr & 0x3F) * 56) + cx + (cy * 7)])
590 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
594 if (textChar2e[(chr * 56) + cx + (cy * 7)])
599 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
600 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
602 // QnD method to get blank alternate lines in text mode
603 if (screenType == ST_GREEN_MONO)
607 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
608 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
615 static void Render80ColumnTextLine(uint8_t line)
617 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
619 for(int x=0; x<80; x++)
624 chr = ram[lineAddrLoRes[line] + (x >> 1)];
626 chr = ram2[lineAddrLoRes[line] + (x >> 1)];
628 // Render character at (x, y)
630 for(int cy=0; cy<8; cy++)
632 for(int cx=0; cx<7; cx++)
634 uint32_t pixel = 0xFF000000;
636 if (alternateCharset)
638 if (textChar2e[(chr * 56) + cx + (cy * 7)])
643 if ((chr & 0xC0) == 0x40)
645 if (textChar2e[((chr & 0x3F) * 56) + cx + (cy * 7)])
649 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
653 if (textChar2e[(chr * 56) + cx + (cy * 7)])
658 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
660 // QnD method to get blank alternate lines in text mode
661 if (screenType == ST_GREEN_MONO)
664 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
670 static void Render40ColumnText(void)
672 for(uint8_t line=0; line<24; line++)
673 Render40ColumnTextLine(line);
676 static void Render80ColumnText(void)
678 for(uint8_t line=0; line<24; line++)
679 Render80ColumnTextLine(line);
682 static void RenderLoRes(uint16_t toLine/*= 24*/)
684 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
685 // Also, we could set up three different Render functions depending on
686 // which render type was set and call it with a function pointer. Would
687 // be faster than the nested ifs we have now.
689 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
690 Color #s correspond to the bit patterns in reverse... Interesting!
692 00 00 00 -> 0 [0000] -> 0 (lores color #)
693 3c 4d 00 -> 8 [0001] -> 8? BROWN
694 00 5d 3c -> 4 [0010] -> 4? DARK GREEN
695 3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
696 41 30 7d -> 2 [0100] -> 2? DARK BLUE
697 7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY
698 41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
699 7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA)
700 7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
701 ba 6d 41 -> 9 [1001] -> 9? ORANGE
702 7d 7d 7d -> 5 [1010] -> 5? DARK GRAY
703 ba cb 7d -> 13 [1011] -> 13? YELLOW
704 be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET)
705 fb 9e be -> 11 [1101] -> 11? PINK
706 be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
707 fb fb fb -> 15 [1111] -> 15 WHITE
709 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
711 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
713 for(uint16_t y=0; y<toLine; y++)
715 // Do top half of lores screen bytes...
717 uint32_t previous3Bits = 0;
719 for(uint16_t x=0; x<40; x+=2)
721 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (!store80Mode && displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
722 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (!store80Mode && displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
723 scrByte1 = mirrorNybble[scrByte1];
724 scrByte2 = mirrorNybble[scrByte2];
725 // This is just a guess, but it'll have to do for now...
726 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
727 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
728 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
730 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
731 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
732 // 31 27 23 19 15 11 7 3 0
734 if (screenType == ST_COLOR_TV)
736 for(uint8_t i=0; i<7; i++)
738 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
741 for(uint8_t j=0; j<4; j++)
743 uint8_t color = blurTable[bitPat][j];
745 for(uint32_t cy=0; cy<8; cy++)
747 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
752 previous3Bits = pixels & 0x70000000;
756 for(int j=0; j<28; j++)
758 for(uint32_t cy=0; cy<8; cy++)
760 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
768 // Now do bottom half...
772 for(uint16_t x=0; x<40; x+=2)
774 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (!store80Mode && displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
775 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (!store80Mode && displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
776 scrByte1 = mirrorNybble[scrByte1];
777 scrByte2 = mirrorNybble[scrByte2];
778 // This is just a guess, but it'll have to do for now...
779 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
780 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
781 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
783 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
784 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
785 // 31 27 23 19 15 11 7 3 0
787 if (screenType == ST_COLOR_TV)
789 for(uint8_t i=0; i<7; i++)
791 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
794 for(uint8_t j=0; j<4; j++)
796 uint8_t color = blurTable[bitPat][j];
798 for(uint32_t cy=8; cy<16; cy++)
800 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
805 previous3Bits = pixels & 0x70000000;
809 for(int j=0; j<28; j++)
811 for(uint32_t cy=8; cy<16; cy++)
813 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
824 // Render the Double Lo Res screen (HIRES off, DHIRES on)
826 static void RenderDLoRes(uint16_t toLine/*= 24*/)
828 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
829 // Also, we could set up three different Render functions depending on
830 // which render type was set and call it with a function pointer. Would be
831 // faster then the nested ifs we have now.
833 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
834 Color #s correspond to the bit patterns in reverse... Interesting! [It's because
835 the video generator reads the bit patters from bit 0--which makes them backwards
836 from the normal POV.]
838 00 00 00 -> 0 [0000] -> 0 (lores color #)
839 3C 4D 00 -> 8 [0001] -> 8? BROWN
840 00 5D 3C -> 4 [0010] -> 4? DARK GREEN
841 3C AA 3C -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
842 41 30 7D -> 2 [0100] -> 2? DARK BLUE
843 7D 7D 7D -> 10 [0101] -> 10? LIGHT GRAY (Grays are identical)
844 41 8E BA -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
845 7D DB BA -> 14 [0111] -> 14? AQUAMARINE (AQUA)
846 7D 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
847 BA 6D 41 -> 9 [1001] -> 9? ORANGE
848 7D 7D 7D -> 5 [1010] -> 5? DARK GRAY (Grays are identical)
849 BA CB 7D -> 13 [1011] -> 13? YELLOW
850 BE 51 BE -> 3 [1100] -> 3 PURPLE (VIOLET)
851 FB 9E BE -> 11 [1101] -> 11? PINK
852 BE AE FB -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
853 FB FB FB -> 15 [1111] -> 15 WHITE
855 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
856 // Rotated one bit right (in the nybble)--right instead of left because
857 // these are backwards after all :-P
858 uint8_t mirrorNybble2[16] = { 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15 };
859 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
861 for(uint16_t y=0; y<toLine; y++)
863 // Do top half of double lores screen bytes...
865 uint32_t previous3Bits = 0;
867 for(uint16_t x=0; x<40; x+=2)
869 uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] & 0x0F;
870 uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] & 0x0F;
871 uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] & 0x0F;
872 uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] & 0x0F;
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 | (scrByte3 << 24)
879 | (scrByte3 << 20) | (scrByte1 << 16)
880 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
881 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
883 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
884 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
885 // 31 27 23 19 15 11 7 3 0
887 if (screenType == ST_COLOR_TV)
889 for(uint8_t i=0; i<7; i++)
891 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
894 for(uint8_t j=0; j<4; j++)
896 uint8_t color = blurTable[bitPat][j];
898 for(uint32_t cy=0; cy<8; cy++)
900 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
901 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
906 previous3Bits = pixels & 0x70000000;
910 for(int j=0; j<28; j++)
912 for(uint32_t cy=0; cy<8; cy++)
914 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
922 // Now do bottom half...
926 for(uint16_t x=0; x<40; x+=2)
928 uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] >> 4;
929 uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] >> 4;
930 uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] >> 4;
931 uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] >> 4;
932 scrByte1 = mirrorNybble[scrByte1];
933 scrByte2 = mirrorNybble[scrByte2];
934 scrByte3 = mirrorNybble2[scrByte3];
935 scrByte4 = mirrorNybble2[scrByte4];
936 // This is just a guess, but it'll have to do for now...
937 // uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
938 // | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
939 // | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
940 uint32_t pixels = previous3Bits | (scrByte3 << 24)
941 | (scrByte3 << 20) | (scrByte1 << 16)
942 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
943 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
945 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
946 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
947 // 31 27 23 19 15 11 7 3 0
949 if (screenType == ST_COLOR_TV)
951 for(uint8_t i=0; i<7; i++)
953 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
956 for(uint8_t j=0; j<4; j++)
958 uint8_t color = blurTable[bitPat][j];
960 for(uint32_t cy=8; cy<16; cy++)
962 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
967 previous3Bits = pixels & 0x70000000;
971 for(int j=0; j<28; j++)
973 for(uint32_t cy=8; cy<16; cy++)
975 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
985 static void RenderHiRes(uint16_t toLine/*= 192*/)
988 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
990 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
991 // The colors are set in the 8-bit array as R G B A
992 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
993 uint32_t * colorPtr = (uint32_t *)monoColors;
994 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
997 for(uint16_t y=0; y<toLine; y++)
999 uint16_t previousLoPixel = 0;
1000 uint32_t previous3bits = 0;
1002 for(uint16_t x=0; x<40; x+=2)
1004 uint8_t screenByte = ram[lineAddrHiRes[y] + (!store80Mode && displayPage2 ? 0x2000 : 0x0000) + x];
1005 uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
1006 previousLoPixel = (screenByte << 2) & 0x0100;
1008 screenByte = ram[lineAddrHiRes[y] + (!store80Mode && displayPage2 ? 0x2000 : 0x0000) + x + 1];
1009 uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
1010 previousLoPixel = (screenByte << 2) & 0x0100;
1012 pixels = previous3bits | (pixels << 14) | pixels2;
1014 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
1015 // 0ppp 1111 1111 1111 1111 1111 1111 1111
1016 // 31 27 23 19 15 11 7 3 0
1018 if (screenType == ST_COLOR_TV)
1020 for(uint8_t i=0; i<7; i++)
1022 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
1025 for(uint8_t j=0; j<4; j++)
1027 uint8_t color = blurTable[bitPat][j];
1029 //This doesn't seem to make things go any faster...
1030 //It's the OpenGL render that's faster... Hmm...
1031 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
1033 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
1034 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
1039 previous3bits = pixels & 0x70000000;
1043 for(int j=0; j<28; j++)
1045 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1047 if (screenType == ST_GREEN_MONO)
1048 pixels &= 0x07FFFFFF;
1050 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1058 static void RenderDHiRes(uint16_t toLine/*= 192*/)
1060 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
1061 // The colors are set in the 8-bit array as R G B A
1062 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
1063 uint32_t * colorPtr = (uint32_t *)monoColors;
1064 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
1066 for(uint16_t y=0; y<toLine; y++)
1068 uint32_t previous4bits = 0;
1070 for(uint16_t x=0; x<40; x+=2)
1072 uint8_t screenByte = ram[lineAddrHiRes[y] + (!store80Mode && displayPage2 ? 0x2000 : 0x0000) + x];
1073 uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
1075 screenByte = ram[lineAddrHiRes[y] + (!store80Mode && displayPage2 ? 0x2000 : 0x0000) + x + 1];
1076 pixels = pixels | (mirrorTable[screenByte & 0x7F]);
1078 screenByte = ram2[lineAddrHiRes[y] + (!store80Mode && displayPage2 ? 0x2000 : 0x0000) + x];
1079 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
1081 screenByte = ram2[lineAddrHiRes[y] + (!store80Mode && displayPage2 ? 0x2000 : 0x0000) + x + 1];
1082 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
1084 pixels = previous4bits | (pixels >> 1);
1086 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
1087 // 0ppp 1111 1111 1111 1111 1111 1111 1111
1088 // 31 27 23 19 15 11 7 3 0
1090 if (screenType == ST_COLOR_TV)
1092 for(uint8_t i=0; i<7; i++)
1094 uint8_t bitPat = (pixels & 0xFE000000) >> 25;
1097 for(uint8_t j=0; j<4; j++)
1099 uint32_t color = palette[blurTable[bitPat][j]];
1100 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
1101 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
1105 previous4bits = pixels & 0xF0000000;
1109 for(int j=0; j<28; j++)
1111 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1113 if (screenType == ST_GREEN_MONO)
1114 pixels &= 0x07FFFFFF;
1116 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1124 void RenderVideoFrame(void)
1126 if (GUI::powerOnState == true)
1131 Render40ColumnText();
1133 Render80ColumnText();
1151 Render40ColumnTextLine(20);
1152 Render40ColumnTextLine(21);
1153 Render40ColumnTextLine(22);
1154 Render40ColumnTextLine(23);
1174 memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
1188 // Prime SDL and create surfaces
1190 bool InitVideo(void)
1192 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) != 0)
1194 WriteLog("Video: Could not initialize the SDL library: %s\n", SDL_GetError());
1198 sdlWindow = SDL_CreateWindow("Apple2", settings.winX, settings.winY, VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0);
1200 if (sdlWindow == NULL)
1202 WriteLog("Video: Could not create window: %s\n", SDL_GetError());
1206 sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
1208 if (sdlRenderer == NULL)
1210 WriteLog("Video: Could not create renderer: %s\n", SDL_GetError());
1214 // Make sure what we put there is what we get:
1215 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
1216 SDL_RenderSetLogicalSize(sdlRenderer, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1218 // Set the application's icon & title...
1219 SDL_Surface * iconSurface = SDL_CreateRGBSurfaceFrom(icon, 64, 64, 32, 64*4, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
1220 SDL_SetWindowIcon(sdlWindow, iconSurface);
1221 SDL_FreeSurface(iconSurface);
1222 SDL_SetWindowTitle(sdlWindow, "Apple2 Emulator");
1224 sdlTexture = SDL_CreateTexture(sdlRenderer,
1225 SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
1226 VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1228 // Start in fullscreen, if user requested it via config file
1229 int response = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1232 WriteLog("Video::FullScreen: SDL error = %s\n", SDL_GetError());
1236 WriteLog("Video: Successfully initialized.\n");
1241 // Free various SDL components
1243 void VideoDone(void)
1245 WriteLog("Video: Shutting down SDL...\n");
1246 SDL_DestroyTexture(sdlTexture);
1247 SDL_DestroyRenderer(sdlRenderer);
1248 SDL_DestroyWindow(sdlWindow);
1250 WriteLog("Video: Done.\n");
1254 // Render the Apple video screen to the primary texture
1256 void RenderAppleScreen(SDL_Renderer * renderer)
1258 SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch);
1260 SDL_UnlockTexture(sdlTexture);
1261 SDL_RenderClear(renderer); // Without this, full screen has trash on the sides
1262 SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
1266 // Fullscreen <-> window switching
1268 void ToggleFullScreen(void)
1270 settings.fullscreen = !settings.fullscreen;
1272 int retVal = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1275 WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());