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"
39 /* Reference: Technote tn-iigs-063 "Master Color Values"
41 Color Color Register LR HR DHR Master Color R,G,B
42 Name Value # # # Value
43 ----------------------------------------------------
44 Black 0 0 0,4 0 $0000 (0,0,0)
45 (Magenta) Deep Red 1 1 1 $0D03 (D,0,3)
46 Dark Blue 2 2 8 $0009 (0,0,9)
47 (Violet) Purple 3 3 2 9 $0D2D (D,2,D)
48 Dark Green 4 4 4 $0072 (0,7,2)
49 (Gray 1) Dark Gray 5 5 5 $0555 (5,5,5)
50 (Blue) Medium Blue 6 6 6 C $022F (2,2,F)
51 (Cyan) Light Blue 7 7 D $06AF (6,A,F)
52 Brown 8 8 2 $0850 (8,5,0)
53 Orange 9 9 5 3 $0F60 (F,6,0)
54 (Gray 2) Light Gray A A A $0AAA (A,A,A)
55 Pink B B B $0F98 (F,9,8)
56 (Green) Light Green C C 1 6 $01D0 (1,D,0)
57 Yellow D D 7 $0FF0 (F,F,0)
58 (Aqua) Aquamarine E E E $04F9 (4,F,9)
59 White F F 3,7 F $0FFF (F,F,F)
61 LR: Lo-Res HR: Hi-Res DHR: Double Hi-Res */
67 bool mixedMode = false;
68 bool displayPage2 = false;
70 bool alternateCharset = false;
71 bool col80Mode = false;
72 //void SpawnMessage(const char * text, ...);
76 // We set up the colors this way so that they'll be endian safe
77 // when we cast them to a uint32_t. Note that the format is RGBA.
79 // "Master Color Values" palette
81 static uint8_t colors[16 * 4] = {
82 0x00, 0x00, 0x00, 0xFF, // Black
83 0xDD, 0x00, 0x33, 0xFF, // Deep Red (Magenta)
84 0x00, 0x00, 0x99, 0xFF, // Dark Blue
85 0xDD, 0x22, 0xDD, 0xFF, // Purple (Violet)
86 0x00, 0x77, 0x22, 0xFF, // Dark Green
87 0x55, 0x55, 0x55, 0xFF, // Dark Gray (Gray 1)
88 0x22, 0x22, 0xFF, 0xFF, // Medium Blue (Blue)
89 0x66, 0xAA, 0xFF, 0xFF, // Light Blue (Cyan)
90 0x88, 0x55, 0x00, 0xFF, // Brown
91 0xFF, 0x66, 0x00, 0xFF, // Orange
92 0xAA, 0xAA, 0xAA, 0xFF, // Light Gray (Gray 2)
93 0xFF, 0x99, 0x88, 0xFF, // Pink
94 0x11, 0xDD, 0x00, 0xFF, // Light Green (Green)
95 0xFF, 0xFF, 0x00, 0xFF, // Yellow
96 0x44, 0xFF, 0x99, 0xFF, // Aquamarine (Aqua)
97 0xFF, 0xFF, 0xFF, 0xFF // White
100 // This palette comes from ApplePC's colors (more realistic to my eye ;-)
102 static uint8_t altColors[16 * 4] = {
103 0x00, 0x00, 0x00, 0xFF,
104 0x7D, 0x20, 0x41, 0xFF,
105 0x41, 0x30, 0x7D, 0xFF,
106 0xBE, 0x51, 0xBE, 0xFF,
107 0x00, 0x5D, 0x3C, 0xFF,
108 0x7D, 0x7D, 0x7D, 0xFF,
109 0x41, 0x8E, 0xBA, 0xFF,
110 0xBE, 0xAE, 0xFB, 0xFF,
111 0x3C, 0x4D, 0x00, 0xFF,
112 0xBA, 0x6D, 0x41, 0xFF,
113 0x7D, 0x7D, 0x7D, 0xFF,
114 0xFB, 0x9E, 0xBE, 0xFF,
115 0x3C, 0xAA, 0x3C, 0xFF,
116 0xBA, 0xCB, 0x7D, 0xFF,
117 0x7D, 0xDB, 0xBA, 0xFF,
118 0xFB, 0xFB, 0xFB, 0xFF };
120 // Lo-res starting line addresses
122 static uint16_t lineAddrLoRes[24] = {
123 0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
124 0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
125 0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 };
127 // Hi-res starting line addresses
129 static uint16_t lineAddrHiRes[192] = {
130 0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
131 0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,
132 0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,
133 0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,
135 0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,
136 0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,
137 0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,
138 0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,
140 0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,
141 0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,
142 0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,
143 0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,
145 0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,
146 0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,
147 0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,
148 0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,
150 0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,
151 0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,
152 0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,
153 0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,
155 0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,
156 0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,
157 0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,
158 0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 };
160 uint16_t appleHiresToMono[0x200] = {
161 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
162 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
163 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
164 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
165 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
166 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
167 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
168 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
169 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
170 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
171 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
172 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
173 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
174 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
175 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
176 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
177 0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80,
178 0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x
179 0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98,
180 0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x
181 0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86,
182 0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax
183 0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E,
184 0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx
185 0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81,
186 0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx
187 0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99,
188 0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx
189 0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87,
190 0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex
191 0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F,
192 0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx
194 // Second half adds in the previous byte's lo pixel
196 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
197 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
198 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
199 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
200 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
201 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
202 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
203 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
204 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
205 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
206 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
207 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
208 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
209 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
210 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
211 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
212 0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80,
213 0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x
214 0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98,
215 0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x
216 0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86,
217 0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax
218 0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E,
219 0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx
220 0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81,
221 0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx
222 0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99,
223 0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx
224 0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87,
225 0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex
226 0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F,
227 0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF // $Fx
230 //static uint8_t blurTable[0x800][8]; // Color TV blur table
231 static uint8_t blurTable[0x80][8]; // Color TV blur table
232 static uint8_t mirrorTable[0x100];
233 static uint32_t * palette = (uint32_t *)altColors;
234 enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
235 static uint8_t screenType = ST_COLOR_TV;
239 static void Render40ColumnTextLine(uint8_t line);
240 static void Render80ColumnTextLine(uint8_t line);
241 static void Render40ColumnText(void);
242 static void Render80ColumnText(void);
243 static void RenderLoRes(uint16_t toLine = 24);
244 static void RenderHiRes(uint16_t toLine = 192);
245 static void RenderDHiRes(uint16_t toLine = 192);
248 void SetupBlurTable(void)
250 // NOTE: This table only needs to be 7 bits wide instead of 11, since the
251 // last four bits are copies of the previous four...
252 // Odd. Doing the bit patterns from 0-$7F doesn't work, but going
253 // from 0-$7FF stepping by 16 does. Hm.
254 // Well, it seems that going from 0-$7F doesn't have enough precision to do the job.
256 // for(uint16_t bitPat=0; bitPat<0x800; bitPat++)
257 for(uint16_t bitPat=0; bitPat<0x80; bitPat++)
259 /* uint16_t w3 = bitPat & 0x888;
260 uint16_t w2 = bitPat & 0x444;
261 uint16_t w1 = bitPat & 0x222;
262 uint16_t w0 = bitPat & 0x111;*/
263 uint16_t w3 = bitPat & 0x88;
264 uint16_t w2 = bitPat & 0x44;
265 uint16_t w1 = bitPat & 0x22;
266 uint16_t w0 = bitPat & 0x11;
268 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
269 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
270 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
271 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
273 for(int8_t i=7; i>=0; i--)
275 uint8_t color = (((blurred0 >> i) & 0x01) << 3)
276 | (((blurred1 >> i) & 0x01) << 2)
277 | (((blurred2 >> i) & 0x01) << 1)
278 | ((blurred3 >> i) & 0x01);
279 blurTable[bitPat][7 - i] = color;
283 for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
285 uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
287 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
288 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
289 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
290 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
292 for(int8_t i=7; i>=0; i--)
294 uint8_t color = (((blurred0 >> i) & 0x01) << 3)
295 | (((blurred1 >> i) & 0x01) << 2)
296 | (((blurred2 >> i) & 0x01) << 1)
297 | ((blurred3 >> i) & 0x01);
298 blurTable[bitPat >> 4][7 - i] = color;
303 for(int i=0; i<256; i++)
305 mirrorTable[i] = ((i & 0x01) << 7)
317 void TogglePalette(void)
319 if (palette == (uint32_t *)colors)
321 palette = (uint32_t *)altColors;
322 SpawnMessage("Color TV palette");
326 palette = (uint32_t *)colors;
327 SpawnMessage("\"Master Color Values\" palette");
332 void CycleScreenTypes(void)
334 char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
338 if (screenType == ST_LAST_ENTRY)
339 screenType = ST_FIRST_ENTRY;
341 SpawnMessage("%s", scrTypeStr[screenType]);
345 static uint32_t msgTicks = 0;
346 static char message[4096];
348 void SpawnMessage(const char * text, ...)
353 vsprintf(message, text, arg);
360 static void DrawString2(uint32_t x, uint32_t y, uint32_t color);
361 static void DrawString(void)
363 //This approach works, and seems to be fast enough... Though it probably would
364 //be better to make the oversized font to match this one...
365 for(uint32_t x=7; x<=9; x++)
366 for(uint32_t y=7; y<=9; y++)
367 DrawString2(x, y, 0x00000000);
369 DrawString2(8, 8, 0x0020FF20);
373 static void DrawString2(uint32_t x, uint32_t y, uint32_t color)
375 //uint32_t x = 8, y = 8;
376 uint32_t length = strlen(message), address = x + (y * VIRTUAL_SCREEN_WIDTH);
377 // uint32_t color = 0x0020FF20;
378 //This could be done ahead of time, instead of on each pixel...
380 uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
382 for(uint32_t i=0; i<length; i++)
384 uint8_t c = message[i];
385 c = (c < 32 ? 0 : c - 32);
386 uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
388 for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
390 for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
392 /* uint8_t fontTrans = font1[fontAddr++];
393 // uint32_t newTrans = (fontTrans * transparency / 255) << 24;
394 uint32_t newTrans = fontTrans << 24;
395 uint32_t pixel = newTrans | color;
397 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = pixel;//*/
399 // uint8_t trans = font1[fontAddr++];
400 uint8_t trans = font2[fontAddr++];
404 uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
406 uint8_t eBlue = (existingColor >> 16) & 0xFF,
407 eGreen = (existingColor >> 8) & 0xFF,
408 eRed = existingColor & 0xFF;
410 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
411 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
412 //because dividing by 32 is faster than dividing by 31...!
413 uint8_t invTrans = 255 - trans;
415 uint32_t bRed = (eRed * invTrans + nRed * trans) / 255;
416 uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
417 uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255;
419 //THIS IS NOT ENDIAN SAFE
420 //NB: Setting the alpha channel here does nothing.
421 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
426 address += FONT_WIDTH;
431 static void Render40ColumnTextLine(uint8_t line)
433 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
435 for(int x=0; x<40; x++)
437 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
439 // Render character at (x, y)
441 for(int cy=0; cy<8; cy++)
443 for(int cx=0; cx<7; cx++)
445 uint32_t pixel = 0xFF000000;
447 if (alternateCharset)
449 if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
453 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
455 if ((chr & 0xC0) == 0x40 && flash)
460 if (textChar2e[(chr * 56) + cx + (cy * 7)])
464 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
465 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
467 // QnD method to get blank alternate lines in text mode
468 if (screenType == ST_GREEN_MONO)
472 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
473 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
481 static void Render80ColumnTextLine(uint8_t line)
483 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
485 for(int x=0; x<80; x++)
488 // This is wrong; it should grab from the alt bank if Page2 is set, not main RAM @ $0
489 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
492 chr = ram2[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x - 40];
497 chr = ram[lineAddrLoRes[line] + (x >> 1)];
499 chr = ram2[lineAddrLoRes[line] + (x >> 1)];
502 // Render character at (x, y)
504 for(int cy=0; cy<8; cy++)
506 for(int cx=0; cx<7; cx++)
508 uint32_t pixel = 0xFF000000;
510 if (alternateCharset)
512 if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
516 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
518 if ((chr & 0xC0) == 0x40 && flash)
523 if (textChar2e[(chr * 56) + cx + (cy * 7)])
527 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
529 // QnD method to get blank alternate lines in text mode
530 if (screenType == ST_GREEN_MONO)
533 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
540 static void Render40ColumnText(void)
542 for(uint8_t line=0; line<24; line++)
543 Render40ColumnTextLine(line);
547 static void Render80ColumnText(void)
549 for(uint8_t line=0; line<24; line++)
550 Render80ColumnTextLine(line);
554 static void RenderLoRes(uint16_t toLine/*= 24*/)
556 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
557 // Also, we could set up three different Render functions depending on which
558 // render type was set and call it with a function pointer. Would be faster
559 // then the nested ifs we have now.
561 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
562 Color #s correspond to the bit patterns in reverse... Interesting!
564 00 00 00 -> 0 [0000] -> 0 (lores color #)
565 3c 4d 00 -> 8 [0001] -> 8? BROWN
566 00 5d 3c -> 4 [0010] -> 4? DARK GREEN
567 3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
568 41 30 7d -> 2 [0100] -> 2? DARK BLUE
569 7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY
570 41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
571 7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA)
572 7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
573 ba 6d 41 -> 9 [1001] -> 9? ORANGE
574 7d 7d 7d -> 5 [1010] -> 5? DARK GRAY
575 ba cb 7d -> 13 [1011] -> 13? YELLOW
576 be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET)
577 fb 9e be -> 11 [1101] -> 11? PINK
578 be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
579 fb fb fb -> 15 [1111] -> 15 WHITE
581 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
583 //This is the old "perfect monitor" rendering code...
584 /* if (screenType != ST_COLOR_TV) // Not correct, but for now...
587 for(uint16_t y=0; y<toLine; y++)
589 for(uint16_t x=0; x<40; x++)
591 uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
592 uint32_t pixel = palette[scrByte & 0x0F];
594 for(int cy=0; cy<4; cy++)
595 for(int cx=0; cx<14; cx++)
596 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
598 pixel = palette[scrByte >> 4];
600 for(int cy=4; cy<8; cy++)
601 for(int cx=0; cx<14; cx++)
602 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
608 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
610 for(uint16_t y=0; y<toLine; y++)
612 // Do top half of lores screen bytes...
614 uint32_t previous3Bits = 0;
616 for(uint16_t x=0; x<40; x+=2)
618 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
619 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
620 scrByte1 = mirrorNybble[scrByte1];
621 scrByte2 = mirrorNybble[scrByte2];
622 // This is just a guess, but it'll have to do for now...
623 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
624 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
625 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
627 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
628 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
629 // 31 27 23 19 15 11 7 3 0
631 if (screenType == ST_COLOR_TV)
633 for(uint8_t i=0; i<7; i++)
635 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
638 for(uint8_t j=0; j<4; j++)
640 uint8_t color = blurTable[bitPat][j];
642 for(uint32_t cy=0; cy<8; cy++)
644 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
645 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
650 previous3Bits = pixels & 0x70000000;
654 for(int j=0; j<28; j++)
656 for(uint32_t cy=0; cy<8; cy++)
658 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
659 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
667 // Now do bottom half...
671 for(uint16_t x=0; x<40; x+=2)
673 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
674 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
675 scrByte1 = mirrorNybble[scrByte1];
676 scrByte2 = mirrorNybble[scrByte2];
677 // This is just a guess, but it'll have to do for now...
678 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
679 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
680 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
682 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
683 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
684 // 31 27 23 19 15 11 7 3 0
686 if (screenType == ST_COLOR_TV)
688 for(uint8_t i=0; i<7; i++)
690 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
693 for(uint8_t j=0; j<4; j++)
695 uint8_t color = blurTable[bitPat][j];
697 for(uint32_t cy=8; cy<16; cy++)
699 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
700 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
705 previous3Bits = pixels & 0x70000000;
709 for(int j=0; j<28; j++)
711 for(uint32_t cy=8; cy<16; cy++)
713 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
714 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
725 static void RenderHiRes(uint16_t toLine/*= 192*/)
727 //printf("RenderHiRes to line %u\n", toLine);
728 // NOTE: Not endian safe. !!! FIX !!! [DONE]
730 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
732 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
733 // The colors are set in the 8-bit array as R G B A
734 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
735 uint32_t * colorPtr = (uint32_t *)monoColors;
736 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
739 for(uint16_t y=0; y<toLine; y++)
741 uint16_t previousLoPixel = 0;
742 uint32_t previous3bits = 0;
744 for(uint16_t x=0; x<40; x+=2)
746 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
747 uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
748 previousLoPixel = (screenByte << 2) & 0x0100;
750 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
751 uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
752 previousLoPixel = (screenByte << 2) & 0x0100;
754 pixels = previous3bits | (pixels << 14) | pixels2;
756 //testing (this shows on the screen, so it's OK)
759 // pixels = 0x7FFFFFFF;
762 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
763 // 0ppp 1111 1111 1111 1111 1111 1111 1111
764 // 31 27 23 19 15 11 7 3 0
766 if (screenType == ST_COLOR_TV)
768 for(uint8_t i=0; i<7; i++)
770 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
773 for(uint8_t j=0; j<4; j++)
775 uint8_t color = blurTable[bitPat][j];
777 //This doesn't seem to make things go any faster...
778 //It's the OpenGL render that's faster... Hmm...
779 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
781 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
782 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
787 previous3bits = pixels & 0x70000000;
791 for(int j=0; j<28; j++)
793 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
795 if (screenType == ST_GREEN_MONO)
796 pixels &= 0x07FFFFFF;
798 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
807 static void RenderDHiRes(uint16_t toLine/*= 192*/)
809 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
810 // The colors are set in the 8-bit array as R G B A
811 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
812 uint32_t * colorPtr = (uint32_t *)monoColors;
813 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
815 for(uint16_t y=0; y<toLine; y++)
817 uint32_t previous4bits = 0;
819 for(uint16_t x=0; x<40; x+=2)
821 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
822 uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
823 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
824 pixels = pixels | (mirrorTable[screenByte & 0x7F]);
825 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
826 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
827 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
828 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
829 pixels = previous4bits | (pixels >> 1);
831 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
832 // 0ppp 1111 1111 1111 1111 1111 1111 1111
833 // 31 27 23 19 15 11 7 3 0
835 if (screenType == ST_COLOR_TV)
837 for(uint8_t i=0; i<7; i++)
839 uint8_t bitPat = (pixels & 0xFE000000) >> 25;
842 for(uint8_t j=0; j<4; j++)
844 uint32_t color = palette[blurTable[bitPat][j]];
845 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
846 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
850 previous4bits = pixels & 0xF0000000;
854 for(int j=0; j<28; j++)
856 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
858 if (screenType == ST_GREEN_MONO)
859 pixels &= 0x07FFFFFF;
861 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
870 void RenderVideoFrame(void)
875 Render40ColumnText();
877 Render80ColumnText();
886 Render40ColumnTextLine(20);
887 Render40ColumnTextLine(21);
888 Render40ColumnTextLine(22);
889 Render40ColumnTextLine(23);
894 Render40ColumnTextLine(20);
895 Render40ColumnTextLine(21);
896 Render40ColumnTextLine(22);
897 Render40ColumnTextLine(23);