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 // Lo-res starting line addresses
194 static uint16_t lineAddrLoRes[24] = {
195 0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
196 0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
197 0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 };
199 // Hi-res starting line addresses
201 static uint16_t lineAddrHiRes[192] = {
202 0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
203 0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,
204 0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,
205 0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,
207 0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,
208 0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,
209 0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,
210 0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,
212 0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,
213 0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,
214 0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,
215 0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,
217 0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,
218 0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,
219 0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,
220 0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,
222 0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,
223 0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,
224 0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,
225 0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,
227 0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,
228 0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,
229 0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,
230 0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 };
232 uint16_t appleHiresToMono[0x200] = {
233 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
234 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
235 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
236 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
237 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
238 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
239 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
240 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
241 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
242 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
243 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
244 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
245 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
246 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
247 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
248 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
249 0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80,
250 0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x
251 0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98,
252 0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x
253 0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86,
254 0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax
255 0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E,
256 0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx
257 0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81,
258 0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx
259 0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99,
260 0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx
261 0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87,
262 0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex
263 0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F,
264 0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx
266 // Second half adds in the previous byte's lo pixel
268 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
269 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
270 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
271 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
272 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
273 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
274 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
275 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
276 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
277 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
278 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
279 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
280 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
281 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
282 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
283 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
284 0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80,
285 0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x
286 0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98,
287 0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x
288 0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86,
289 0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax
290 0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E,
291 0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx
292 0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81,
293 0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx
294 0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99,
295 0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx
296 0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87,
297 0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex
298 0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F,
299 0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF // $Fx
302 static uint8_t blurTable[0x80][8]; // Color TV blur table
303 static uint8_t mirrorTable[0x100];
304 static uint32_t * palette = (uint32_t *)altColors;
305 enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
306 static uint8_t screenType = ST_COLOR_TV;
310 static void Render40ColumnTextLine(uint8_t line);
311 static void Render80ColumnTextLine(uint8_t line);
312 static void Render40ColumnText(void);
313 static void Render80ColumnText(void);
314 static void RenderLoRes(uint16_t toLine = 24);
315 static void RenderDLoRes(uint16_t toLine = 24);
316 static void RenderHiRes(uint16_t toLine = 192);
317 static void RenderDHiRes(uint16_t toLine = 192);
318 static void RenderVideoFrame(/*uint32_t *, int*/);
321 void SetupBlurTable(void)
323 // NOTE: This table only needs to be 7 bits wide instead of 11, since the
324 // last four bits are copies of the previous four...
325 // Odd. Doing the bit patterns from 0-$7F doesn't work, but going
326 // from 0-$7FF stepping by 16 does. Hm.
327 // Well, it seems that going from 0-$7F doesn't have enough precision to do the job.
328 for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
330 uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
332 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
333 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
334 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
335 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
337 for(int8_t i=7; i>=0; i--)
339 uint8_t color = (((blurred0 >> i) & 0x01) << 3)
340 | (((blurred1 >> i) & 0x01) << 2)
341 | (((blurred2 >> i) & 0x01) << 1)
342 | ((blurred3 >> i) & 0x01);
343 blurTable[bitPat >> 4][7 - i] = color;
347 for(int i=0; i<256; i++)
349 mirrorTable[i] = ((i & 0x01) << 7)
361 void TogglePalette(void)
363 if (palette == (uint32_t *)colors)
365 palette = (uint32_t *)altColors;
366 SpawnMessage("ApplePC Color TV palette");
368 else if (palette == (uint32_t *)altColors)
370 palette = (uint32_t *)robColors;
371 SpawnMessage("Rob's Color TV palette");
375 palette = (uint32_t *)colors;
376 SpawnMessage("\"Master Color Values\" palette");
381 void CycleScreenTypes(void)
383 char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
387 if (screenType == ST_LAST_ENTRY)
388 screenType = ST_FIRST_ENTRY;
390 SpawnMessage("%s", scrTypeStr[screenType]);
394 void ToggleTickDisplay(void)
396 showFrameTicks = !showFrameTicks;
400 static uint32_t msgTicks = 0;
401 static char message[4096];
403 void SpawnMessage(const char * text, ...)
408 vsprintf(message, text, arg);
412 //WriteLog("\n%s\n", message);
416 static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg);
417 static void DrawString(void)
419 //This approach works, and seems to be fast enough... Though it probably would
420 //be better to make the oversized font to match this one...
421 for(uint32_t x=7; x<=9; x++)
422 for(uint32_t y=7; y<=9; y++)
423 DrawString2(x, y, 0x00000000, message);
425 DrawString2(8, 8, 0x0020FF20, message);
429 static void DrawString(uint32_t x, uint32_t y, uint32_t color, char * msg)
431 //This approach works, and seems to be fast enough... Though it probably would
432 //be better to make the oversized font to match this one...
433 for(uint32_t xx=x-1; xx<=x+1; xx++)
434 for(uint32_t yy=y-1; yy<=y+1; yy++)
435 DrawString2(xx, yy, 0x00000000, msg);
437 DrawString2(x, y, color, msg);
441 static void DrawString2(uint32_t x, uint32_t y, uint32_t color, char * msg)
443 uint32_t length = strlen(msg), address = x + (y * VIRTUAL_SCREEN_WIDTH);
444 uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
446 for(uint32_t i=0; i<length; i++)
449 c = (c < 32 ? 0 : c - 32);
450 uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
452 for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
454 for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
456 uint8_t trans = font2[fontAddr++];
460 uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
462 uint8_t eBlue = (existingColor >> 16) & 0xFF,
463 eGreen = (existingColor >> 8) & 0xFF,
464 eRed = existingColor & 0xFF;
466 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
467 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
468 //because dividing by 32 is faster than dividing by 31...!
469 uint8_t invTrans = 255 - trans;
471 uint32_t bRed = (eRed * invTrans + nRed * trans) / 255;
472 uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
473 uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255;
475 //THIS IS NOT ENDIAN SAFE
476 //NB: Setting the alpha channel here does nothing.
477 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
482 address += FONT_WIDTH;
487 static void DrawFrameTicks(void)
489 uint32_t color = 0x00FF2020;
490 uint32_t address = 8 + (24 * VIRTUAL_SCREEN_WIDTH);
492 for(uint32_t i=0; i<17; i++)
494 for(uint32_t yy=0; yy<5; yy++)
496 for(uint32_t xx=0; xx<9; xx++)
498 //THIS IS NOT ENDIAN SAFE
499 //NB: Setting the alpha channel here does nothing.
500 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000;
504 address += (5 * VIRTUAL_SCREEN_WIDTH);
507 address = 8 + (24 * VIRTUAL_SCREEN_WIDTH);
509 // frameTicks is the amount of time remaining; so to show the amount
510 // consumed, we subtract it from 17.
511 uint32_t bars = 17 - frameTicks;
513 if (bars & 0x80000000)
516 for(uint32_t i=0; i<17; i++)
518 for(uint32_t yy=1; yy<4; yy++)
520 for(uint32_t xx=1; xx<8; xx++)
522 //THIS IS NOT ENDIAN SAFE
523 //NB: Setting the alpha channel here does nothing.
524 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = (i < bars ? color : 0x003F0000);
528 address += (5 * VIRTUAL_SCREEN_WIDTH);
533 if ((frameTimePtr % 15) == 0)
535 // uint32_t prevClock = (frameTimePtr + 1) % 60;
536 uint64_t prevClock = (frameTimePtr + 1) % 60;
537 // float fps = 59.0f / (((float)frameTime[frameTimePtr] - (float)frameTime[prevClock]) / 1000.0f);
538 double fps = 59.0 / ((double)(frameTime[frameTimePtr] - frameTime[prevClock]) / (double)SDL_GetPerformanceFrequency());
539 sprintf(msg, "%.1lf FPS", fps);
542 DrawString(20, 24, color, msg);
546 static void Render40ColumnTextLine(uint8_t line)
548 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
550 for(int x=0; x<40; x++)
552 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
554 // Render character at (x, y)
556 for(int cy=0; cy<8; cy++)
558 for(int cx=0; cx<7; cx++)
560 uint32_t pixel = 0xFF000000;
562 if (alternateCharset)
564 if (textChar2e[(chr * 56) + cx + (cy * 7)])
569 if ((chr & 0xC0) == 0x40)
571 if (textChar2e[((chr & 0x3F) * 56) + cx + (cy * 7)])
575 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
579 if (textChar2e[(chr * 56) + cx + (cy * 7)])
584 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
585 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
587 // QnD method to get blank alternate lines in text mode
588 if (screenType == ST_GREEN_MONO)
592 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
593 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
601 static void Render80ColumnTextLine(uint8_t line)
603 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
605 for(int x=0; x<80; x++)
610 chr = ram[lineAddrLoRes[line] + (x >> 1)];
612 chr = ram2[lineAddrLoRes[line] + (x >> 1)];
614 // Render character at (x, y)
616 for(int cy=0; cy<8; cy++)
618 for(int cx=0; cx<7; cx++)
620 uint32_t pixel = 0xFF000000;
622 if (alternateCharset)
624 if (textChar2e[(chr * 56) + cx + (cy * 7)])
629 if ((chr & 0xC0) == 0x40)
631 if (textChar2e[((chr & 0x3F) * 56) + cx + (cy * 7)])
635 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
639 if (textChar2e[(chr * 56) + cx + (cy * 7)])
644 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
646 // QnD method to get blank alternate lines in text mode
647 if (screenType == ST_GREEN_MONO)
650 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
657 static void Render40ColumnText(void)
659 for(uint8_t line=0; line<24; line++)
660 Render40ColumnTextLine(line);
664 static void Render80ColumnText(void)
666 for(uint8_t line=0; line<24; line++)
667 Render80ColumnTextLine(line);
671 static void RenderLoRes(uint16_t toLine/*= 24*/)
673 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
674 // Also, we could set up three different Render functions depending on
675 // which render type was set and call it with a function pointer. Would
676 // be faster than the nested ifs we have now.
678 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
679 Color #s correspond to the bit patterns in reverse... Interesting!
681 00 00 00 -> 0 [0000] -> 0 (lores color #)
682 3c 4d 00 -> 8 [0001] -> 8? BROWN
683 00 5d 3c -> 4 [0010] -> 4? DARK GREEN
684 3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
685 41 30 7d -> 2 [0100] -> 2? DARK BLUE
686 7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY
687 41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
688 7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA)
689 7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
690 ba 6d 41 -> 9 [1001] -> 9? ORANGE
691 7d 7d 7d -> 5 [1010] -> 5? DARK GRAY
692 ba cb 7d -> 13 [1011] -> 13? YELLOW
693 be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET)
694 fb 9e be -> 11 [1101] -> 11? PINK
695 be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
696 fb fb fb -> 15 [1111] -> 15 WHITE
698 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
700 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
702 for(uint16_t y=0; y<toLine; y++)
704 // Do top half of lores screen bytes...
706 uint32_t previous3Bits = 0;
708 for(uint16_t x=0; x<40; x+=2)
710 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
711 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
712 scrByte1 = mirrorNybble[scrByte1];
713 scrByte2 = mirrorNybble[scrByte2];
714 // This is just a guess, but it'll have to do for now...
715 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
716 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
717 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
719 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
720 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
721 // 31 27 23 19 15 11 7 3 0
723 if (screenType == ST_COLOR_TV)
725 for(uint8_t i=0; i<7; i++)
727 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
730 for(uint8_t j=0; j<4; j++)
732 uint8_t color = blurTable[bitPat][j];
734 for(uint32_t cy=0; cy<8; cy++)
736 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
737 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
742 previous3Bits = pixels & 0x70000000;
746 for(int j=0; j<28; j++)
748 for(uint32_t cy=0; cy<8; cy++)
750 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
751 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
759 // Now do bottom half...
763 for(uint16_t x=0; x<40; x+=2)
765 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
766 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
767 scrByte1 = mirrorNybble[scrByte1];
768 scrByte2 = mirrorNybble[scrByte2];
769 // This is just a guess, but it'll have to do for now...
770 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
771 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
772 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
774 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
775 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
776 // 31 27 23 19 15 11 7 3 0
778 if (screenType == ST_COLOR_TV)
780 for(uint8_t i=0; i<7; i++)
782 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
785 for(uint8_t j=0; j<4; j++)
787 uint8_t color = blurTable[bitPat][j];
789 for(uint32_t cy=8; cy<16; cy++)
791 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
792 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
797 previous3Bits = pixels & 0x70000000;
801 for(int j=0; j<28; j++)
803 for(uint32_t cy=8; cy<16; cy++)
805 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
806 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
818 // Render the Double Lo Res screen (HIRES off, DHIRES on)
820 static void RenderDLoRes(uint16_t toLine/*= 24*/)
822 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
823 // Also, we could set up three different Render functions depending on
824 // which render type was set and call it with a function pointer. Would be
825 // faster then the nested ifs we have now.
827 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
828 Color #s correspond to the bit patterns in reverse... Interesting! [It's because
829 the video generator reads the bit patters from bit 0--which makes them backwards
830 from the normal POV.]
832 00 00 00 -> 0 [0000] -> 0 (lores color #)
833 3C 4D 00 -> 8 [0001] -> 8? BROWN
834 00 5D 3C -> 4 [0010] -> 4? DARK GREEN
835 3C AA 3C -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
836 41 30 7D -> 2 [0100] -> 2? DARK BLUE
837 7D 7D 7D -> 10 [0101] -> 10? LIGHT GRAY (Grays are identical)
838 41 8E BA -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
839 7D DB BA -> 14 [0111] -> 14? AQUAMARINE (AQUA)
840 7D 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
841 BA 6D 41 -> 9 [1001] -> 9? ORANGE
842 7D 7D 7D -> 5 [1010] -> 5? DARK GRAY (Grays are identical)
843 BA CB 7D -> 13 [1011] -> 13? YELLOW
844 BE 51 BE -> 3 [1100] -> 3 PURPLE (VIOLET)
845 FB 9E BE -> 11 [1101] -> 11? PINK
846 BE AE FB -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
847 FB FB FB -> 15 [1111] -> 15 WHITE
849 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
850 // Rotated one bit right (in the nybble)--right instead of left because
851 // these are backwards after all :-P
852 uint8_t mirrorNybble2[16] = { 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15 };
853 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
855 for(uint16_t y=0; y<toLine; y++)
857 // Do top half of double lores screen bytes...
859 uint32_t previous3Bits = 0;
861 for(uint16_t x=0; x<40; x+=2)
863 uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] & 0x0F;
864 uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] & 0x0F;
865 uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] & 0x0F;
866 uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] & 0x0F;
867 scrByte1 = mirrorNybble[scrByte1];
868 scrByte2 = mirrorNybble[scrByte2];
869 scrByte3 = mirrorNybble2[scrByte3];
870 scrByte4 = mirrorNybble2[scrByte4];
871 // This is just a guess, but it'll have to do for now...
872 uint32_t pixels = previous3Bits | (scrByte3 << 24)
873 | (scrByte3 << 20) | (scrByte1 << 16)
874 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
875 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
877 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
878 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
879 // 31 27 23 19 15 11 7 3 0
881 if (screenType == ST_COLOR_TV)
883 for(uint8_t i=0; i<7; i++)
885 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
888 for(uint8_t j=0; j<4; j++)
890 uint8_t color = blurTable[bitPat][j];
892 for(uint32_t cy=0; cy<8; cy++)
894 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
895 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
900 previous3Bits = pixels & 0x70000000;
904 for(int j=0; j<28; j++)
906 for(uint32_t cy=0; cy<8; cy++)
908 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
916 // Now do bottom half...
920 for(uint16_t x=0; x<40; x+=2)
922 uint8_t scrByte3 = ram2[lineAddrLoRes[y] + x + 0] >> 4;
923 uint8_t scrByte4 = ram2[lineAddrLoRes[y] + x + 1] >> 4;
924 uint8_t scrByte1 = ram[lineAddrLoRes[y] + x + 0] >> 4;
925 uint8_t scrByte2 = ram[lineAddrLoRes[y] + x + 1] >> 4;
926 scrByte1 = mirrorNybble[scrByte1];
927 scrByte2 = mirrorNybble[scrByte2];
928 scrByte3 = mirrorNybble2[scrByte3];
929 scrByte4 = mirrorNybble2[scrByte4];
930 // This is just a guess, but it'll have to do for now...
931 // uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
932 // | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
933 // | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
934 uint32_t pixels = previous3Bits | (scrByte3 << 24)
935 | (scrByte3 << 20) | (scrByte1 << 16)
936 | ((scrByte1 & 0x0C) << 12) | ((scrByte4 & 0x03) << 12)
937 | (scrByte4 << 8) | (scrByte2 << 4) | scrByte2;
939 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
940 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
941 // 31 27 23 19 15 11 7 3 0
943 if (screenType == ST_COLOR_TV)
945 for(uint8_t i=0; i<7; i++)
947 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
950 for(uint8_t j=0; j<4; j++)
952 uint8_t color = blurTable[bitPat][j];
954 for(uint32_t cy=8; cy<16; cy++)
956 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
961 previous3Bits = pixels & 0x70000000;
965 for(int j=0; j<28; j++)
967 for(uint32_t cy=8; cy<16; cy++)
969 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
980 static void RenderHiRes(uint16_t toLine/*= 192*/)
983 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
985 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
986 // The colors are set in the 8-bit array as R G B A
987 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
988 uint32_t * colorPtr = (uint32_t *)monoColors;
989 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
992 for(uint16_t y=0; y<toLine; y++)
994 uint16_t previousLoPixel = 0;
995 uint32_t previous3bits = 0;
997 for(uint16_t x=0; x<40; x+=2)
999 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1000 uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
1001 previousLoPixel = (screenByte << 2) & 0x0100;
1003 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1004 uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
1005 previousLoPixel = (screenByte << 2) & 0x0100;
1007 pixels = previous3bits | (pixels << 14) | pixels2;
1009 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
1010 // 0ppp 1111 1111 1111 1111 1111 1111 1111
1011 // 31 27 23 19 15 11 7 3 0
1013 if (screenType == ST_COLOR_TV)
1015 for(uint8_t i=0; i<7; i++)
1017 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
1020 for(uint8_t j=0; j<4; j++)
1022 uint8_t color = blurTable[bitPat][j];
1024 //This doesn't seem to make things go any faster...
1025 //It's the OpenGL render that's faster... Hmm...
1026 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
1028 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
1029 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
1034 previous3bits = pixels & 0x70000000;
1038 for(int j=0; j<28; j++)
1040 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1042 if (screenType == ST_GREEN_MONO)
1043 pixels &= 0x07FFFFFF;
1045 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1054 static void RenderDHiRes(uint16_t toLine/*= 192*/)
1056 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
1057 // The colors are set in the 8-bit array as R G B A
1058 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
1059 uint32_t * colorPtr = (uint32_t *)monoColors;
1060 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
1062 for(uint16_t y=0; y<toLine; y++)
1064 uint32_t previous4bits = 0;
1066 for(uint16_t x=0; x<40; x+=2)
1068 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1069 uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
1070 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1071 pixels = pixels | (mirrorTable[screenByte & 0x7F]);
1072 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
1073 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
1074 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
1075 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
1076 pixels = previous4bits | (pixels >> 1);
1078 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
1079 // 0ppp 1111 1111 1111 1111 1111 1111 1111
1080 // 31 27 23 19 15 11 7 3 0
1082 if (screenType == ST_COLOR_TV)
1084 for(uint8_t i=0; i<7; i++)
1086 uint8_t bitPat = (pixels & 0xFE000000) >> 25;
1089 for(uint8_t j=0; j<4; j++)
1091 uint32_t color = palette[blurTable[bitPat][j]];
1092 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
1093 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
1097 previous4bits = pixels & 0xF0000000;
1101 for(int j=0; j<28; j++)
1103 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1105 if (screenType == ST_GREEN_MONO)
1106 pixels &= 0x07FFFFFF;
1108 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
1117 void RenderVideoFrame(void)
1119 if (GUI::powerOnState == true)
1124 Render40ColumnText();
1126 Render80ColumnText();
1144 Render40ColumnTextLine(20);
1145 Render40ColumnTextLine(21);
1146 Render40ColumnTextLine(22);
1147 Render40ColumnTextLine(23);
1167 memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));
1182 // Prime SDL and create surfaces
1184 bool InitVideo(void)
1186 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) != 0)
1188 WriteLog("Video: Could not initialize the SDL library: %s\n", SDL_GetError());
1192 sdlWindow = SDL_CreateWindow("Apple2", settings.winX, settings.winY, VIRTUAL_SCREEN_WIDTH * 2, VIRTUAL_SCREEN_HEIGHT * 2, 0);
1194 if (sdlWindow == NULL)
1196 WriteLog("Video: Could not create window: %s\n", SDL_GetError());
1200 sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
1202 if (sdlRenderer == NULL)
1204 WriteLog("Video: Could not create renderer: %s\n", SDL_GetError());
1208 // Make sure what we put there is what we get:
1209 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
1210 SDL_RenderSetLogicalSize(sdlRenderer, VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1212 // Set the application's icon & title...
1213 SDL_Surface * iconSurface = SDL_CreateRGBSurfaceFrom(icon, 64, 64, 32, 64*4, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
1214 SDL_SetWindowIcon(sdlWindow, iconSurface);
1215 SDL_FreeSurface(iconSurface);
1216 SDL_SetWindowTitle(sdlWindow, "Apple2 Emulator");
1218 sdlTexture = SDL_CreateTexture(sdlRenderer,
1219 SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
1220 VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
1222 // Start in fullscreen, if user requested it via config file
1223 int response = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1226 WriteLog("Video::FullScreen: SDL error = %s\n", SDL_GetError());
1230 WriteLog("Video: Successfully initialized.\n");
1236 // Free various SDL components
1238 void VideoDone(void)
1240 WriteLog("Video: Shutting down SDL...\n");
1241 SDL_DestroyTexture(sdlTexture);
1242 SDL_DestroyRenderer(sdlRenderer);
1243 SDL_DestroyWindow(sdlWindow);
1245 WriteLog("Video: Done.\n");
1250 // Render the Apple video screen to the primary texture
1252 void RenderAppleScreen(SDL_Renderer * renderer)
1254 SDL_LockTexture(sdlTexture, NULL, (void **)&scrBuffer, &scrPitch);
1256 SDL_UnlockTexture(sdlTexture);
1257 SDL_RenderClear(renderer); // Without this, full screen has trash on the sides
1258 SDL_RenderCopy(renderer, sdlTexture, NULL, NULL);
1263 // Fullscreen <-> window switching
1265 void ToggleFullScreen(void)
1267 settings.fullscreen = !settings.fullscreen;
1269 int retVal = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
1272 WriteLog("Video::ToggleFullScreen: SDL error = %s\n", SDL_GetError());