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(/*uint32_t *, int*/);
343 void SetupBlurTable(void)
345 // NOTE: This table only needs to be 7 bits wide instead of 11, since the
346 // last four bits are copies of the previous four...
347 // Odd. Doing the bit patterns from 0-$7F doesn't work, but going
348 // from 0-$7FF stepping by 16 does. Hm.
349 // Well, it seems that going from 0-$7F doesn't have enough precision to do the job.
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)
383 void TogglePalette(void)
385 if (palette == (uint32_t *)colors)
387 palette = (uint32_t *)altColors;
388 SpawnMessage("ApplePC Color TV palette");
390 else if (palette == (uint32_t *)altColors)
392 palette = (uint32_t *)picColors;
393 SpawnMessage("Picture Color TV palette");
395 else if (palette == (uint32_t *)picColors)
397 palette = (uint32_t *)robColors;
398 SpawnMessage("Rob's Color TV palette");
402 palette = (uint32_t *)colors;
403 SpawnMessage("\"Master Color Values\" palette");
408 void CycleScreenTypes(void)
410 char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
414 if (screenType == ST_LAST_ENTRY)
415 screenType = ST_FIRST_ENTRY;
417 SpawnMessage("%s", scrTypeStr[screenType]);
421 void ToggleTickDisplay(void)
423 showFrameTicks = !showFrameTicks;
427 static uint32_t msgTicks = 0;
428 static char message[4096];
430 void SpawnMessage(const char * text, ...)
435 vsprintf(message, text, arg);
439 //WriteLog("\n%s\n", message);
443 static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg);
444 static void DrawString(void)
446 //This approach works, and seems to be fast enough... Though it probably would
447 //be better to make the oversized font to match this one...
448 for(uint32_t x=7; x<=9; x++)
449 for(uint32_t y=7; y<=9; y++)
450 DrawString2(x, y, 0x00000000, message);
452 DrawString2(8, 8, 0x0020FF20, message);
456 static void DrawString(uint32_t x, uint32_t y, uint32_t color, char * msg)
458 //This approach works, and seems to be fast enough... Though it probably would
459 //be better to make the oversized font to match this one...
460 for(uint32_t xx=x-1; xx<=x+1; xx++)
461 for(uint32_t yy=y-1; yy<=y+1; yy++)
462 DrawString2(xx, yy, 0x00000000, msg);
464 DrawString2(x, y, color, msg);
468 static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg)
470 uint32_t length = strlen(msg), address = x + (y * VIRTUAL_SCREEN_WIDTH);
471 uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
473 for(uint32_t i=0; i<length; i++)
476 c = (c < 32 ? 0 : c - 32);
477 uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
479 for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
481 for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
483 uint8_t trans = font2[fontAddr++];
487 uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
489 uint8_t eBlue = (existingColor >> 16) & 0xFF,
490 eGreen = (existingColor >> 8) & 0xFF,
491 eRed = existingColor & 0xFF;
493 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
494 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
495 //because dividing by 32 is faster than dividing by 31...!
496 uint8_t invTrans = 255 - trans;
498 uint32_t bRed = (eRed * invTrans + nRed * trans) / 255;
499 uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
500 uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255;
502 //THIS IS NOT ENDIAN SAFE
503 //NB: Setting the alpha channel here does nothing.
504 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
509 address += FONT_WIDTH;
514 static void DrawFrameTicks(void)
516 uint32_t color = 0x00FF2020;
517 uint32_t address = 8 + (24 * VIRTUAL_SCREEN_WIDTH);
519 for(uint32_t i=0; i<17; i++)
521 for(uint32_t yy=0; yy<5; yy++)
523 for(uint32_t xx=0; xx<9; xx++)
525 //THIS IS NOT ENDIAN SAFE
526 //NB: Setting the alpha channel here does nothing.
527 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000;
531 address += (5 * VIRTUAL_SCREEN_WIDTH);
534 address = 8 + (24 * VIRTUAL_SCREEN_WIDTH);
536 // frameTicks is the amount of time remaining; so to show the amount
537 // consumed, we subtract it from 17.
538 uint32_t bars = 17 - frameTicks;
540 if (bars & 0x80000000)
543 for(uint32_t i=0; i<17; i++)
545 for(uint32_t yy=1; yy<4; yy++)
547 for(uint32_t xx=1; xx<8; xx++)
549 //THIS IS NOT ENDIAN SAFE
550 //NB: Setting the alpha channel here does nothing.
551 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = (i < bars ? color : 0x003F0000);
555 address += (5 * VIRTUAL_SCREEN_WIDTH);
560 if ((frameTimePtr % 15) == 0)
562 // uint32_t prevClock = (frameTimePtr + 1) % 60;
563 uint64_t prevClock = (frameTimePtr + 1) % 60;
564 // float fps = 59.0f / (((float)frameTime[frameTimePtr] - (float)frameTime[prevClock]) / 1000.0f);
565 double fps = 59.0 / ((double)(frameTime[frameTimePtr] - frameTime[prevClock]) / (double)SDL_GetPerformanceFrequency());
566 sprintf(msg, "%.1lf FPS", fps);
569 DrawString(20, 24, color, msg);
573 static void Render40ColumnTextLine(uint8_t line)
575 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
577 for(int x=0; x<40; x++)
579 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
581 // Render character at (x, y)
583 for(int cy=0; cy<8; cy++)
585 for(int cx=0; cx<7; cx++)
587 uint32_t pixel = 0xFF000000;
589 if (alternateCharset)
591 if (textChar2e[(chr * 56) + cx + (cy * 7)])
596 if ((chr & 0xC0) == 0x40)
598 if (textChar2e[((chr & 0x3F) * 56) + cx + (cy * 7)])
602 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
606 if (textChar2e[(chr * 56) + cx + (cy * 7)])
611 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
612 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
614 // QnD method to get blank alternate lines in text mode
615 if (screenType == ST_GREEN_MONO)
619 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
620 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
628 static void Render80ColumnTextLine(uint8_t line)
630 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
632 for(int x=0; x<80; x++)
637 chr = ram[lineAddrLoRes[line] + (x >> 1)];
639 chr = ram2[lineAddrLoRes[line] + (x >> 1)];
641 // Render character at (x, y)
643 for(int cy=0; cy<8; cy++)
645 for(int cx=0; cx<7; cx++)
647 uint32_t pixel = 0xFF000000;
649 if (alternateCharset)
651 if (textChar2e[(chr * 56) + cx + (cy * 7)])
656 if ((chr & 0xC0) == 0x40)
658 if (textChar2e[((chr & 0x3F) * 56) + cx + (cy * 7)])
662 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
666 if (textChar2e[(chr * 56) + cx + (cy * 7)])
671 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
673 // QnD method to get blank alternate lines in text mode
674 if (screenType == ST_GREEN_MONO)
677 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
684 static void Render40ColumnText(void)
686 for(uint8_t line=0; line<24; line++)
687 Render40ColumnTextLine(line);
691 static void Render80ColumnText(void)
693 for(uint8_t line=0; line<24; line++)
694 Render80ColumnTextLine(line);
698 static void RenderLoRes(uint16_t toLine/*= 24*/)
700 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
701 // Also, we could set up three different Render functions depending on
702 // which render type was set and call it with a function pointer. Would
703 // be faster than the nested ifs we have now.
705 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
706 Color #s correspond to the bit patterns in reverse... Interesting!
708 00 00 00 -> 0 [0000] -> 0 (lores color #)
709 3c 4d 00 -> 8 [0001] -> 8? BROWN
710 00 5d 3c -> 4 [0010] -> 4? DARK GREEN
711 3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
712 41 30 7d -> 2 [0100] -> 2? DARK BLUE
713 7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY
714 41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
715 7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA)
716 7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
717 ba 6d 41 -> 9 [1001] -> 9? ORANGE
718 7d 7d 7d -> 5 [1010] -> 5? DARK GRAY
719 ba cb 7d -> 13 [1011] -> 13? YELLOW
720 be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET)
721 fb 9e be -> 11 [1101] -> 11? PINK
722 be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
723 fb fb fb -> 15 [1111] -> 15 WHITE
725 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
727 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
729 for(uint16_t y=0; y<toLine; y++)
731 // Do top half of lores screen bytes...
733 uint32_t previous3Bits = 0;
735 for(uint16_t x=0; x<40; x+=2)
737 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
738 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
739 scrByte1 = mirrorNybble[scrByte1];
740 scrByte2 = mirrorNybble[scrByte2];
741 // This is just a guess, but it'll have to do for now...
742 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
743 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
744 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
746 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
747 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
748 // 31 27 23 19 15 11 7 3 0
750 if (screenType == ST_COLOR_TV)
752 for(uint8_t i=0; i<7; i++)
754 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
757 for(uint8_t j=0; j<4; j++)
759 uint8_t color = blurTable[bitPat][j];
761 for(uint32_t cy=0; cy<8; cy++)
763 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
764 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
769 previous3Bits = pixels & 0x70000000;
773 for(int j=0; j<28; j++)
775 for(uint32_t cy=0; cy<8; cy++)
777 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
778 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
786 // Now do bottom half...
790 for(uint16_t x=0; x<40; x+=2)
792 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
793 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
794 scrByte1 = mirrorNybble[scrByte1];
795 scrByte2 = mirrorNybble[scrByte2];
796 // This is just a guess, but it'll have to do for now...
797 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
798 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
799 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
801 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
802 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
803 // 31 27 23 19 15 11 7 3 0
805 if (screenType == ST_COLOR_TV)
807 for(uint8_t i=0; i<7; i++)
809 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
812 for(uint8_t j=0; j<4; j++)
814 uint8_t color = blurTable[bitPat][j];
816 for(uint32_t cy=8; cy<16; cy++)
818 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
819 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
824 previous3Bits = pixels & 0x70000000;
828 for(int j=0; j<28; j++)
830 for(uint32_t cy=8; cy<16; cy++)
832 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
833 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
845 // Render the Double Lo Res screen (HIRES off, DHIRES on)
847 static void RenderDLoRes(uint16_t toLine/*= 24*/)
849 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
850 // Also, we could set up three different Render functions depending on
851 // which render type was set and call it with a function pointer. Would be
852 // faster then the nested ifs we have now.
854 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
855 Color #s correspond to the bit patterns in reverse... Interesting! [It's because
856 the video generator reads the bit patters from bit 0--which makes them backwards
857 from the normal POV.]
859 00 00 00 -> 0 [0000] -> 0 (lores color #)
860 3C 4D 00 -> 8 [0001] -> 8? BROWN
861 00 5D 3C -> 4 [0010] -> 4? DARK GREEN
862 3C AA 3C -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
863 41 30 7D -> 2 [0100] -> 2? DARK BLUE
864 7D 7D 7D -> 10 [0101] -> 10? LIGHT GRAY (Grays are identical)
865 41 8E BA -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
866 7D DB BA -> 14 [0111] -> 14? AQUAMARINE (AQUA)
867 7D 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
868 BA 6D 41 -> 9 [1001] -> 9? ORANGE
869 7D 7D 7D -> 5 [1010] -> 5? DARK GRAY (Grays are identical)
870 BA CB 7D -> 13 [1011] -> 13? YELLOW
871 BE 51 BE -> 3 [1100] -> 3 PURPLE (VIOLET)
872 FB 9E BE -> 11 [1101] -> 11? PINK
873 BE AE FB -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
874 FB FB FB -> 15 [1111] -> 15 WHITE
876 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
877 // Rotated one bit right (in the nybble)--right instead of left because
878 // these are backwards after all :-P
879 uint8_t mirrorNybble2[16] = { 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15 };
880 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
882 for(uint16_t y=0; y<toLine; y++)
884 // Do top half of double lores screen bytes...
886 uint32_t previous3Bits = 0;
888 for(uint16_t x=0; x<40; x+=2)
890 uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] & 0x0F;
891 uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] & 0x0F;
892 uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] & 0x0F;
893 uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] & 0x0F;
894 scrByte1 = mirrorNybble[scrByte1];
895 scrByte2 = mirrorNybble[scrByte2];
896 scrByte3 = mirrorNybble2[scrByte3];
897 scrByte4 = mirrorNybble2[scrByte4];
898 // This is just a guess, but it'll have to do for now...
899 uint32_t pixels = previous3Bits | (scrByte3 << 24)
900 | (scrByte3 << 20) | (scrByte1 << 16)
901 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
902 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
904 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
905 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
906 // 31 27 23 19 15 11 7 3 0
908 if (screenType == ST_COLOR_TV)
910 for(uint8_t i=0; i<7; i++)
912 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
915 for(uint8_t j=0; j<4; j++)
917 uint8_t color = blurTable[bitPat][j];
919 for(uint32_t cy=0; cy<8; cy++)
921 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
922 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
927 previous3Bits = pixels & 0x70000000;
931 for(int j=0; j<28; j++)
933 for(uint32_t cy=0; cy<8; cy++)
935 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
943 // Now do bottom half...
947 for(uint16_t x=0; x<40; x+=2)
949 uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] >> 4;
950 uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] >> 4;
951 uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] >> 4;
952 uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] >> 4;
953 scrByte1 = mirrorNybble[scrByte1];
954 scrByte2 = mirrorNybble[scrByte2];
955 scrByte3 = mirrorNybble2[scrByte3];
956 scrByte4 = mirrorNybble2[scrByte4];
957 // This is just a guess, but it'll have to do for now...
958 // uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
959 // | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
960 // | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
961 uint32_t pixels = previous3Bits | (scrByte3 << 24)
962 | (scrByte3 << 20) | (scrByte1 << 16)
963 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
964 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
966 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
967 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
968 // 31 27 23 19 15 11 7 3 0
970 if (screenType == ST_COLOR_TV)
972 for(uint8_t i=0; i<7; i++)
974 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
977 for(uint8_t j=0; j<4; j++)
979 uint8_t color = blurTable[bitPat][j];
981 for(uint32_t cy=8; cy<16; cy++)
983 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
988 previous3Bits = pixels & 0x70000000;
992 for(int j=0; j<28; j++)
994 for(uint32_t cy=8; cy<16; cy++)
996 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1007 static void RenderHiRes(uint16_t toLine/*= 192*/)
1010 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
1012 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
1013 // The colors are set in the 8-bit array as R G B A
1014 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
1015 uint32_t * colorPtr = (uint32_t *)monoColors;
1016 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
1019 for(uint16_t y=0; y<toLine; y++)
1021 uint16_t previousLoPixel = 0;
1022 uint32_t previous3bits = 0;
1024 for(uint16_t x=0; x<40; x+=2)
1026 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1027 uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
1028 previousLoPixel = (screenByte << 2) & 0x0100;
1030 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1031 uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
1032 previousLoPixel = (screenByte << 2) & 0x0100;
1034 pixels = previous3bits | (pixels << 14) | pixels2;
1036 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
1037 // 0ppp 1111 1111 1111 1111 1111 1111 1111
1038 // 31 27 23 19 15 11 7 3 0
1040 if (screenType == ST_COLOR_TV)
1042 for(uint8_t i=0; i<7; i++)
1044 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
1047 for(uint8_t j=0; j<4; j++)
1049 uint8_t color = blurTable[bitPat][j];
1051 //This doesn't seem to make things go any faster...
1052 //It's the OpenGL render that's faster... Hmm...
1053 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
1055 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
1056 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
1061 previous3bits = pixels & 0x70000000;
1065 for(int j=0; j<28; j++)
1067 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1069 if (screenType == ST_GREEN_MONO)
1070 pixels &= 0x07FFFFFF;
1072 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1081 static void RenderDHiRes(uint16_t toLine/*= 192*/)
1083 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
1084 // The colors are set in the 8-bit array as R G B A
1085 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
1086 uint32_t * colorPtr = (uint32_t *)monoColors;
1087 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
1089 for(uint16_t y=0; y<toLine; y++)
1091 uint32_t previous4bits = 0;
1093 for(uint16_t x=0; x<40; x+=2)
1095 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1096 uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
1097 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1098 pixels = pixels | (mirrorTable[screenByte & 0x7F]);
1099 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1100 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
1101 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1102 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
1103 pixels = previous4bits | (pixels >> 1);
1105 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
1106 // 0ppp 1111 1111 1111 1111 1111 1111 1111
1107 // 31 27 23 19 15 11 7 3 0
1109 if (screenType == ST_COLOR_TV)
1111 for(uint8_t i=0; i<7; i++)
1113 uint8_t bitPat = (pixels & 0xFE000000) >> 25;
1116 for(uint8_t j=0; j<4; j++)
1118 uint32_t color = palette[blurTable[bitPat][j]];
1119 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
1120 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
1124 previous4bits = pixels & 0xF0000000;
1128 for(int j=0; j<28; j++)
1130 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1132 if (screenType == ST_GREEN_MONO)
1133 pixels &= 0x07FFFFFF;
1135 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1144 void RenderVideoFrame(void)
1146 if (GUI::powerOnState == true)
1151 Render40ColumnText();
1153 Render80ColumnText();
1171 Render40ColumnTextLine(20);
1172 Render40ColumnTextLine(21);
1173 Render40ColumnTextLine(22);
1174 Render40ColumnTextLine(23);
1194 memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
1209 // Prime SDL and create surfaces
1211 bool InitVideo(void)
1213 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) != 0)
1215 WriteLog("Video: Could not initialize the SDL library: %s\n", SDL_GetError());
1219 sdlWindow = SDL_CreateWindow("Apple2", settings.winX, settings.winY, VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0);
1221 if (sdlWindow == NULL)
1223 WriteLog("Video: Could not create window: %s\n", SDL_GetError());
1227 sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
1229 if (sdlRenderer == NULL)
1231 WriteLog("Video: Could not create renderer: %s\n", SDL_GetError());
1235 // Make sure what we put there is what we get:
1236 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
1237 SDL_RenderSetLogicalSize(sdlRenderer, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1239 // Set the application's icon & title...
1240 SDL_Surface * iconSurface = SDL_CreateRGBSurfaceFrom(icon, 64, 64, 32, 64*4, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
1241 SDL_SetWindowIcon(sdlWindow, iconSurface);
1242 SDL_FreeSurface(iconSurface);
1243 SDL_SetWindowTitle(sdlWindow, "Apple2 Emulator");
1245 sdlTexture = SDL_CreateTexture(sdlRenderer,
1246 SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
1247 VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1249 // Start in fullscreen, if user requested it via config file
1250 int response = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1253 WriteLog("Video::FullScreen: SDL error = %s\n", SDL_GetError());
1257 WriteLog("Video: Successfully initialized.\n");
1263 // Free various SDL components
1265 void VideoDone(void)
1267 WriteLog("Video: Shutting down SDL...\n");
1268 SDL_DestroyTexture(sdlTexture);
1269 SDL_DestroyRenderer(sdlRenderer);
1270 SDL_DestroyWindow(sdlWindow);
1272 WriteLog("Video: Done.\n");
1277 // Render the Apple video screen to the primary texture
1279 void RenderAppleScreen(SDL_Renderer * renderer)
1281 SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch);
1283 SDL_UnlockTexture(sdlTexture);
1284 SDL_RenderClear(renderer); // Without this, full screen has trash on the sides
1285 SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
1290 // Fullscreen <-> window switching
1292 void ToggleFullScreen(void)
1294 settings.fullscreen = !settings.fullscreen;
1296 int retVal = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1299 WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());