2 // Apple 2 video support
4 // All the video modes that a real Apple 2 supports are handled here
7 // (c) 2005 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
23 // - Fix OSD text display so that it's visible no matter what background is there [DONE]
26 // Display routines seem MUCH slower now... !!! INVESTIGATE !!! [not anymore]
28 #include "applevideo.h"
30 #include <string.h> // for memset()
32 #include <stdarg.h> // for va_* stuff
33 //#include <string> // for vsprintf()
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 */
68 bool mixedMode = false;
69 bool displayPage2 = false;
71 bool alternateCharset = false;
72 bool col80Mode = false;
73 //void SpawnMessage(const char * text, ...);
77 // We set up the colors this way so that they'll be endian safe
78 // when we cast them to a uint32_t. Note that the format is RGBA.
80 // "Master Color Values" palette
82 static uint8_t colors[16 * 4] = {
83 0x00, 0x00, 0x00, 0xFF, // Black
84 0xDD, 0x00, 0x33, 0xFF, // Deep Red (Magenta)
85 0x00, 0x00, 0x99, 0xFF, // Dark Blue
86 0xDD, 0x22, 0xDD, 0xFF, // Purple (Violet)
87 0x00, 0x77, 0x22, 0xFF, // Dark Green
88 0x55, 0x55, 0x55, 0xFF, // Dark Gray (Gray 1)
89 0x22, 0x22, 0xFF, 0xFF, // Medium Blue (Blue)
90 0x66, 0xAA, 0xFF, 0xFF, // Light Blue (Cyan)
91 0x88, 0x55, 0x00, 0xFF, // Brown
92 0xFF, 0x66, 0x00, 0xFF, // Orange
93 0xAA, 0xAA, 0xAA, 0xFF, // Light Gray (Gray 2)
94 0xFF, 0x99, 0x88, 0xFF, // Pink
95 0x11, 0xDD, 0x00, 0xFF, // Light Green (Green)
96 0xFF, 0xFF, 0x00, 0xFF, // Yellow
97 0x44, 0xFF, 0x99, 0xFF, // Aquamarine (Aqua)
98 0xFF, 0xFF, 0xFF, 0xFF // White
101 // This palette comes from ApplePC's colors (more realistic to my eye ;-)
103 static uint8_t altColors[16 * 4] = {
104 0x00, 0x00, 0x00, 0xFF,
105 0x7D, 0x20, 0x41, 0xFF,
106 0x41, 0x30, 0x7D, 0xFF,
107 0xBE, 0x51, 0xBE, 0xFF,
108 0x00, 0x5D, 0x3C, 0xFF,
109 0x7D, 0x7D, 0x7D, 0xFF,
110 0x41, 0x8E, 0xBA, 0xFF,
111 0xBE, 0xAE, 0xFB, 0xFF,
112 0x3C, 0x4D, 0x00, 0xFF,
113 0xBA, 0x6D, 0x41, 0xFF,
114 0x7D, 0x7D, 0x7D, 0xFF,
115 0xFB, 0x9E, 0xBE, 0xFF,
116 0x3C, 0xAA, 0x3C, 0xFF,
117 0xBA, 0xCB, 0x7D, 0xFF,
118 0x7D, 0xDB, 0xBA, 0xFF,
119 0xFB, 0xFB, 0xFB, 0xFF };
121 // Lo-res starting line addresses
123 static uint16_t lineAddrLoRes[24] = {
124 0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
125 0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
126 0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 };
128 // Hi-res starting line addresses
130 static uint16_t lineAddrHiRes[192] = {
131 0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
132 0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,
133 0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,
134 0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,
136 0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,
137 0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,
138 0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,
139 0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,
141 0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,
142 0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,
143 0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,
144 0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,
146 0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,
147 0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,
148 0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,
149 0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,
151 0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,
152 0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,
153 0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,
154 0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,
156 0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,
157 0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,
158 0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,
159 0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 };
161 uint16_t appleHiresToMono[0x200] = {
162 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
163 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
164 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
165 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
166 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
167 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
168 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
169 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
170 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
171 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
172 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
173 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
174 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
175 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
176 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
177 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
178 0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80,
179 0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x
180 0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98,
181 0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x
182 0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86,
183 0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax
184 0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E,
185 0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx
186 0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81,
187 0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx
188 0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99,
189 0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx
190 0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87,
191 0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex
192 0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F,
193 0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx
195 // Second half adds in the previous byte's lo pixel
197 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
198 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
199 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
200 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
201 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
202 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
203 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
204 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
205 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
206 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
207 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
208 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
209 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
210 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
211 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
212 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
213 0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80,
214 0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x
215 0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98,
216 0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x
217 0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86,
218 0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax
219 0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E,
220 0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx
221 0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81,
222 0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx
223 0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99,
224 0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx
225 0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87,
226 0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex
227 0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F,
228 0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF // $Fx
231 //static uint8_t blurTable[0x800][8]; // Color TV blur table
232 static uint8_t blurTable[0x80][8]; // Color TV blur table
233 static uint8_t mirrorTable[0x100];
234 static uint32_t * palette = (uint32_t *)altColors;
235 enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
236 static uint8_t screenType = ST_COLOR_TV;
240 static void Render40ColumnTextLine(uint8_t line);
241 static void Render80ColumnTextLine(uint8_t line);
242 static void Render40ColumnText(void);
243 static void Render80ColumnText(void);
244 static void RenderLoRes(uint16_t toLine = 24);
245 static void RenderHiRes(uint16_t toLine = 192);
246 static void RenderDHiRes(uint16_t toLine = 192);
249 void SetupBlurTable(void)
251 // NOTE: This table only needs to be 7 bits wide instead of 11, since the
252 // last four bits are copies of the previous four...
253 // Odd. Doing the bit patterns from 0-$7F doesn't work, but going
254 // from 0-$7FF stepping by 16 does. Hm.
255 // Well, it seems that going from 0-$7F doesn't have enough precision to do the job.
257 // for(uint16_t bitPat=0; bitPat<0x800; bitPat++)
258 for(uint16_t bitPat=0; bitPat<0x80; bitPat++)
260 /* uint16_t w3 = bitPat & 0x888;
261 uint16_t w2 = bitPat & 0x444;
262 uint16_t w1 = bitPat & 0x222;
263 uint16_t w0 = bitPat & 0x111;*/
264 uint16_t w3 = bitPat & 0x88;
265 uint16_t w2 = bitPat & 0x44;
266 uint16_t w1 = bitPat & 0x22;
267 uint16_t w0 = bitPat & 0x11;
269 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
270 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
271 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
272 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
274 for(int8_t i=7; i>=0; i--)
276 uint8_t color = (((blurred0 >> i) & 0x01) << 3)
277 | (((blurred1 >> i) & 0x01) << 2)
278 | (((blurred2 >> i) & 0x01) << 1)
279 | ((blurred3 >> i) & 0x01);
280 blurTable[bitPat][7 - i] = color;
284 for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
286 uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
288 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
289 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
290 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
291 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
293 for(int8_t i=7; i>=0; i--)
295 uint8_t color = (((blurred0 >> i) & 0x01) << 3)
296 | (((blurred1 >> i) & 0x01) << 2)
297 | (((blurred2 >> i) & 0x01) << 1)
298 | ((blurred3 >> i) & 0x01);
299 blurTable[bitPat >> 4][7 - i] = color;
304 for(int i=0; i<256; i++)
306 mirrorTable[i] = ((i & 0x01) << 7)
318 void TogglePalette(void)
320 if (palette == (uint32_t *)colors)
322 palette = (uint32_t *)altColors;
323 SpawnMessage("Color TV palette");
327 palette = (uint32_t *)colors;
328 SpawnMessage("\"Master Color Values\" palette");
333 void CycleScreenTypes(void)
335 char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
339 if (screenType == ST_LAST_ENTRY)
340 screenType = ST_FIRST_ENTRY;
342 SpawnMessage("%s", scrTypeStr[screenType]);
346 static uint32_t msgTicks = 0;
347 static char message[4096];
349 void SpawnMessage(const char * text, ...)
354 vsprintf(message, text, arg);
361 static void DrawString2(uint32_t x, uint32_t y, uint32_t color);
362 static void DrawString(void)
364 //This approach works, and seems to be fast enough... Though it probably would
365 //be better to make the oversized font to match this one...
366 for(uint32_t x=7; x<=9; x++)
367 for(uint32_t y=7; y<=9; y++)
368 DrawString2(x, y, 0x00000000);
370 DrawString2(8, 8, 0x0020FF20);
374 static void DrawString2(uint32_t x, uint32_t y, uint32_t color)
376 //uint32_t x = 8, y = 8;
377 uint32_t length = strlen(message), address = x + (y * VIRTUAL_SCREEN_WIDTH);
378 // uint32_t color = 0x0020FF20;
379 //This could be done ahead of time, instead of on each pixel...
381 uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
383 for(uint32_t i=0; i<length; i++)
385 uint8_t c = message[i];
386 c = (c < 32 ? 0 : c - 32);
387 uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
389 for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
391 for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
393 /* uint8_t fontTrans = font1[fontAddr++];
394 // uint32_t newTrans = (fontTrans * transparency / 255) << 24;
395 uint32_t newTrans = fontTrans << 24;
396 uint32_t pixel = newTrans | color;
398 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = pixel;//*/
400 // uint8_t trans = font1[fontAddr++];
401 uint8_t trans = font2[fontAddr++];
405 uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
407 uint8_t eBlue = (existingColor >> 16) & 0xFF,
408 eGreen = (existingColor >> 8) & 0xFF,
409 eRed = existingColor & 0xFF;
411 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
412 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
413 //because dividing by 32 is faster than dividing by 31...!
414 uint8_t invTrans = 255 - trans;
416 uint32_t bRed = (eRed * invTrans + nRed * trans) / 255;
417 uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
418 uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255;
420 //THIS IS NOT ENDIAN SAFE
421 //NB: Setting the alpha channel here does nothing.
422 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
427 address += FONT_WIDTH;
432 static void Render40ColumnTextLine(uint8_t line)
434 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
436 for(int x=0; x<40; x++)
438 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
440 // Render character at (x, y)
442 for(int cy=0; cy<8; cy++)
444 for(int cx=0; cx<7; cx++)
446 uint32_t pixel = 0xFF000000;
448 if (alternateCharset)
450 if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
454 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
456 if ((chr & 0xC0) == 0x40 && flash)
461 if (textChar2e[(chr * 56) + cx + (cy * 7)])
465 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
466 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
468 // QnD method to get blank alternate lines in text mode
469 if (screenType == ST_GREEN_MONO)
473 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
474 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
482 static void Render80ColumnTextLine(uint8_t line)
484 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
486 for(int x=0; x<80; x++)
489 // This is wrong; it should grab from the alt bank if Page2 is set, not main RAM @ $0
490 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
493 chr = ram2[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x - 40];
498 chr = ram[lineAddrLoRes[line] + (x >> 1)];
500 chr = ram2[lineAddrLoRes[line] + (x >> 1)];
503 // Render character at (x, y)
505 for(int cy=0; cy<8; cy++)
507 for(int cx=0; cx<7; cx++)
509 uint32_t pixel = 0xFF000000;
511 if (alternateCharset)
513 if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
517 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
519 if ((chr & 0xC0) == 0x40 && flash)
524 if (textChar2e[(chr * 56) + cx + (cy * 7)])
528 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
530 // QnD method to get blank alternate lines in text mode
531 if (screenType == ST_GREEN_MONO)
534 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
541 static void Render40ColumnText(void)
543 for(uint8_t line=0; line<24; line++)
544 Render40ColumnTextLine(line);
548 static void Render80ColumnText(void)
550 for(uint8_t line=0; line<24; line++)
551 Render80ColumnTextLine(line);
555 static void RenderLoRes(uint16_t toLine/*= 24*/)
557 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
558 // Also, we could set up three different Render functions depending on which
559 // render type was set and call it with a function pointer. Would be faster
560 // then the nested ifs we have now.
562 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
563 Color #s correspond to the bit patterns in reverse... Interesting!
565 00 00 00 -> 0 [0000] -> 0 (lores color #)
566 3c 4d 00 -> 8 [0001] -> 8? BROWN
567 00 5d 3c -> 4 [0010] -> 4? DARK GREEN
568 3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
569 41 30 7d -> 2 [0100] -> 2? DARK BLUE
570 7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY
571 41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
572 7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA)
573 7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
574 ba 6d 41 -> 9 [1001] -> 9? ORANGE
575 7d 7d 7d -> 5 [1010] -> 5? DARK GRAY
576 ba cb 7d -> 13 [1011] -> 13? YELLOW
577 be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET)
578 fb 9e be -> 11 [1101] -> 11? PINK
579 be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
580 fb fb fb -> 15 [1111] -> 15 WHITE
582 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
584 //This is the old "perfect monitor" rendering code...
585 /* if (screenType != ST_COLOR_TV) // Not correct, but for now...
588 for(uint16_t y=0; y<toLine; y++)
590 for(uint16_t x=0; x<40; x++)
592 uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
593 uint32_t pixel = palette[scrByte & 0x0F];
595 for(int cy=0; cy<4; cy++)
596 for(int cx=0; cx<14; cx++)
597 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
599 pixel = palette[scrByte >> 4];
601 for(int cy=4; cy<8; cy++)
602 for(int cx=0; cx<14; cx++)
603 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
609 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
611 for(uint16_t y=0; y<toLine; y++)
613 // Do top half of lores screen bytes...
615 uint32_t previous3Bits = 0;
617 for(uint16_t x=0; x<40; x+=2)
619 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
620 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
621 scrByte1 = mirrorNybble[scrByte1];
622 scrByte2 = mirrorNybble[scrByte2];
623 // This is just a guess, but it'll have to do for now...
624 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
625 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
626 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
628 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
629 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
630 // 31 27 23 19 15 11 7 3 0
632 if (screenType == ST_COLOR_TV)
634 for(uint8_t i=0; i<7; i++)
636 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
639 for(uint8_t j=0; j<4; j++)
641 uint8_t color = blurTable[bitPat][j];
643 for(uint32_t cy=0; cy<8; cy++)
645 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
646 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
651 previous3Bits = pixels & 0x70000000;
655 for(int j=0; j<28; j++)
657 for(uint32_t cy=0; cy<8; cy++)
659 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
660 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
668 // Now do bottom half...
672 for(uint16_t x=0; x<40; x+=2)
674 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
675 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
676 scrByte1 = mirrorNybble[scrByte1];
677 scrByte2 = mirrorNybble[scrByte2];
678 // This is just a guess, but it'll have to do for now...
679 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
680 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
681 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
683 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
684 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
685 // 31 27 23 19 15 11 7 3 0
687 if (screenType == ST_COLOR_TV)
689 for(uint8_t i=0; i<7; i++)
691 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
694 for(uint8_t j=0; j<4; j++)
696 uint8_t color = blurTable[bitPat][j];
698 for(uint32_t cy=8; cy<16; cy++)
700 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
701 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
706 previous3Bits = pixels & 0x70000000;
710 for(int j=0; j<28; j++)
712 for(uint32_t cy=8; cy<16; cy++)
714 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
715 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
726 static void RenderHiRes(uint16_t toLine/*= 192*/)
728 //printf("RenderHiRes to line %u\n", toLine);
729 // NOTE: Not endian safe. !!! FIX !!! [DONE]
731 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
733 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
734 // The colors are set in the 8-bit array as R G B A
735 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
736 uint32_t * colorPtr = (uint32_t *)monoColors;
737 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
740 for(uint16_t y=0; y<toLine; y++)
742 uint16_t previousLoPixel = 0;
743 uint32_t previous3bits = 0;
745 for(uint16_t x=0; x<40; x+=2)
747 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
748 uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
749 previousLoPixel = (screenByte << 2) & 0x0100;
751 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
752 uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
753 previousLoPixel = (screenByte << 2) & 0x0100;
755 pixels = previous3bits | (pixels << 14) | pixels2;
757 //testing (this shows on the screen, so it's OK)
760 // pixels = 0x7FFFFFFF;
763 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
764 // 0ppp 1111 1111 1111 1111 1111 1111 1111
765 // 31 27 23 19 15 11 7 3 0
767 if (screenType == ST_COLOR_TV)
769 for(uint8_t i=0; i<7; i++)
771 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
774 for(uint8_t j=0; j<4; j++)
776 uint8_t color = blurTable[bitPat][j];
778 //This doesn't seem to make things go any faster...
779 //It's the OpenGL render that's faster... Hmm...
780 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
782 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
783 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
788 previous3bits = pixels & 0x70000000;
792 for(int j=0; j<28; j++)
794 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
796 if (screenType == ST_GREEN_MONO)
797 pixels &= 0x07FFFFFF;
799 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
808 static void RenderDHiRes(uint16_t toLine/*= 192*/)
810 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
811 // The colors are set in the 8-bit array as R G B A
812 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
813 uint32_t * colorPtr = (uint32_t *)monoColors;
814 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
816 for(uint16_t y=0; y<toLine; y++)
818 uint32_t previous4bits = 0;
820 for(uint16_t x=0; x<40; x+=2)
822 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
823 uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
824 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
825 pixels = pixels | (mirrorTable[screenByte & 0x7F]);
826 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
827 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
828 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
829 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
830 pixels = previous4bits | (pixels >> 1);
832 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
833 // 0ppp 1111 1111 1111 1111 1111 1111 1111
834 // 31 27 23 19 15 11 7 3 0
836 if (screenType == ST_COLOR_TV)
838 for(uint8_t i=0; i<7; i++)
840 uint8_t bitPat = (pixels & 0xFE000000) >> 25;
843 for(uint8_t j=0; j<4; j++)
845 uint32_t color = palette[blurTable[bitPat][j]];
846 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
847 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
851 previous4bits = pixels & 0xF0000000;
855 for(int j=0; j<28; j++)
857 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
859 if (screenType == ST_GREEN_MONO)
860 pixels &= 0x07FFFFFF;
862 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
871 void RenderVideoFrame(void)
873 if (GUI::powerOnState == true)
878 Render40ColumnText();
880 Render80ColumnText();
889 Render40ColumnTextLine(20);
890 Render40ColumnTextLine(21);
891 Render40ColumnTextLine(22);
892 Render40ColumnTextLine(23);
897 Render40ColumnTextLine(20);
898 Render40ColumnTextLine(21);
899 Render40ColumnTextLine(22);
900 Render40ColumnTextLine(23);
916 memset(scrBuffer, 0, VIRTUAL_SCREEN_WIDTH * VIRTUAL_SCREEN_HEIGHT * sizeof(uint32_t));