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 L. 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 !!!
28 #include "applevideo.h"
30 #include <string.h> // for memset()
32 #include <stdarg.h> // for va_* stuff
33 //#include <string> // for vsprintf()
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++];
403 uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
405 uint8_t eBlue = (existingColor >> 16) & 0xFF,
406 eGreen = (existingColor >> 8) & 0xFF,
407 eRed = existingColor & 0xFF;
409 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
410 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
411 //because dividing by 32 is faster than dividing by 31...!
412 uint8_t invTrans = 255 - trans;
414 uint32_t bRed = (eRed * invTrans + nRed * trans) / 255;
415 uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
416 uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255;
418 //THIS IS NOT ENDIAN SAFE
419 //NB: Setting the alpha channel here does nothing.
420 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
425 address += FONT_WIDTH;
430 static void Render40ColumnTextLine(uint8_t line)
432 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
434 for(int x=0; x<40; x++)
436 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
438 // Render character at (x, y)
440 for(int cy=0; cy<8; cy++)
442 for(int cx=0; cx<7; cx++)
444 uint32_t pixel = 0xFF000000;
446 if (alternateCharset)
448 if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
452 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
454 if ((chr & 0xC0) == 0x40 && flash)
459 if (textChar2e[(chr * 56) + cx + (cy * 7)])
463 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
464 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
466 // QnD method to get blank alternate lines in text mode
467 if (screenType == ST_GREEN_MONO)
471 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
472 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
480 static void Render80ColumnTextLine(uint8_t line)
482 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
484 for(int x=0; x<80; x++)
487 // This is wrong; it should grab from the alt bank if Page2 is set, not main RAM @ $0
488 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
491 chr = ram2[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x - 40];
496 chr = ram[lineAddrLoRes[line] + (x >> 1)];
498 chr = ram2[lineAddrLoRes[line] + (x >> 1)];
501 // Render character at (x, y)
503 for(int cy=0; cy<8; cy++)
505 for(int cx=0; cx<7; cx++)
507 uint32_t pixel = 0xFF000000;
509 if (alternateCharset)
511 if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
515 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
517 if ((chr & 0xC0) == 0x40 && flash)
522 if (textChar2e[(chr * 56) + cx + (cy * 7)])
526 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
528 // QnD method to get blank alternate lines in text mode
529 if (screenType == ST_GREEN_MONO)
532 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
539 static void Render40ColumnText(void)
541 for(uint8_t line=0; line<24; line++)
542 Render40ColumnTextLine(line);
546 static void Render80ColumnText(void)
548 for(uint8_t line=0; line<24; line++)
549 Render80ColumnTextLine(line);
553 static void RenderLoRes(uint16_t toLine/*= 24*/)
555 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
556 // Also, we could set up three different Render functions depending on which
557 // render type was set and call it with a function pointer. Would be faster
558 // then the nested ifs we have now.
560 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
561 Color #s correspond to the bit patterns in reverse... Interesting!
563 00 00 00 -> 0 [0000] -> 0 (lores color #)
564 3c 4d 00 -> 8 [0001] -> 8? BROWN
565 00 5d 3c -> 4 [0010] -> 4? DARK GREEN
566 3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
567 41 30 7d -> 2 [0100] -> 2? DARK BLUE
568 7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY
569 41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
570 7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA)
571 7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
572 ba 6d 41 -> 9 [1001] -> 9? ORANGE
573 7d 7d 7d -> 5 [1010] -> 5? DARK GRAY
574 ba cb 7d -> 13 [1011] -> 13? YELLOW
575 be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET)
576 fb 9e be -> 11 [1101] -> 11? PINK
577 be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
578 fb fb fb -> 15 [1111] -> 15 WHITE
580 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
582 //This is the old "perfect monitor" rendering code...
583 /* if (screenType != ST_COLOR_TV) // Not correct, but for now...
586 for(uint16_t y=0; y<toLine; y++)
588 for(uint16_t x=0; x<40; x++)
590 uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
591 uint32_t pixel = palette[scrByte & 0x0F];
593 for(int cy=0; cy<4; cy++)
594 for(int cx=0; cx<14; cx++)
595 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
597 pixel = palette[scrByte >> 4];
599 for(int cy=4; cy<8; cy++)
600 for(int cx=0; cx<14; cx++)
601 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
607 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
609 for(uint16_t y=0; y<toLine; y++)
611 // Do top half of lores screen bytes...
613 uint32_t previous3Bits = 0;
615 for(uint16_t x=0; x<40; x+=2)
617 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
618 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
619 scrByte1 = mirrorNybble[scrByte1];
620 scrByte2 = mirrorNybble[scrByte2];
621 // This is just a guess, but it'll have to do for now...
622 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
623 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
624 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
626 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
627 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
628 // 31 27 23 19 15 11 7 3 0
630 if (screenType == ST_COLOR_TV)
632 for(uint8_t i=0; i<7; i++)
634 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
637 for(uint8_t j=0; j<4; j++)
639 uint8_t color = blurTable[bitPat][j];
641 for(uint32_t cy=0; cy<8; cy++)
643 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
644 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
649 previous3Bits = pixels & 0x70000000;
653 for(int j=0; j<28; j++)
655 for(uint32_t cy=0; cy<8; cy++)
657 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
658 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
666 // Now do bottom half...
670 for(uint16_t x=0; x<40; x+=2)
672 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
673 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
674 scrByte1 = mirrorNybble[scrByte1];
675 scrByte2 = mirrorNybble[scrByte2];
676 // This is just a guess, but it'll have to do for now...
677 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
678 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
679 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
681 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
682 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
683 // 31 27 23 19 15 11 7 3 0
685 if (screenType == ST_COLOR_TV)
687 for(uint8_t i=0; i<7; i++)
689 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
692 for(uint8_t j=0; j<4; j++)
694 uint8_t color = blurTable[bitPat][j];
696 for(uint32_t cy=8; cy<16; cy++)
698 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
699 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
704 previous3Bits = pixels & 0x70000000;
708 for(int j=0; j<28; j++)
710 for(uint32_t cy=8; cy<16; cy++)
712 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
713 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
724 static void RenderHiRes(uint16_t toLine/*= 192*/)
726 // NOTE: Not endian safe. !!! FIX !!! [DONE]
728 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
730 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
731 // The colors are set in the 8-bit array as R G B A
732 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
733 uint32_t * colorPtr = (uint32_t *)monoColors;
734 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
737 for(uint16_t y=0; y<toLine; y++)
739 uint16_t previousLoPixel = 0;
740 uint32_t previous3bits = 0;
742 for(uint16_t x=0; x<40; x+=2)
744 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
745 uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
746 previousLoPixel = (screenByte << 2) & 0x0100;
748 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
749 uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
750 previousLoPixel = (screenByte << 2) & 0x0100;
752 pixels = previous3bits | (pixels << 14) | pixels2;
754 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
755 // 0ppp 1111 1111 1111 1111 1111 1111 1111
756 // 31 27 23 19 15 11 7 3 0
758 if (screenType == ST_COLOR_TV)
760 for(uint8_t i=0; i<7; i++)
762 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
765 for(uint8_t j=0; j<4; j++)
767 uint8_t color = blurTable[bitPat][j];
769 //This doesn't seem to make things go any faster...
770 //It's the OpenGL render that's faster... Hmm...
771 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
773 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
774 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
779 previous3bits = pixels & 0x70000000;
783 for(int j=0; j<28; j++)
785 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
787 if (screenType == ST_GREEN_MONO)
788 pixels &= 0x07FFFFFF;
790 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
799 static void RenderDHiRes(uint16_t toLine/*= 192*/)
801 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
802 // The colors are set in the 8-bit array as R G B A
803 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
804 uint32_t * colorPtr = (uint32_t *)monoColors;
805 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
807 for(uint16_t y=0; y<toLine; y++)
809 uint32_t previous4bits = 0;
811 for(uint16_t x=0; x<40; x+=2)
813 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
814 uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
815 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
816 pixels = pixels | (mirrorTable[screenByte & 0x7F]);
817 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
818 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
819 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
820 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
821 pixels = previous4bits | (pixels >> 1);
823 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
824 // 0ppp 1111 1111 1111 1111 1111 1111 1111
825 // 31 27 23 19 15 11 7 3 0
827 if (screenType == ST_COLOR_TV)
829 for(uint8_t i=0; i<7; i++)
831 uint8_t bitPat = (pixels & 0xFE000000) >> 25;
834 for(uint8_t j=0; j<4; j++)
836 uint32_t color = palette[blurTable[bitPat][j]];
837 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
838 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
842 previous4bits = pixels & 0xF0000000;
846 for(int j=0; j<28; j++)
848 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
850 if (screenType == ST_GREEN_MONO)
851 pixels &= 0x07FFFFFF;
853 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
862 void RenderVideoFrame(void)
866 RenderScreenBuffer();
871 // There's prolly more to it than this (like 80 column text), but this'll have to do for now...
873 Render40ColumnText();
875 Render80ColumnText();
884 Render40ColumnTextLine(20);
885 Render40ColumnTextLine(21);
886 Render40ColumnTextLine(22);
887 Render40ColumnTextLine(23);
892 Render40ColumnTextLine(20);
893 Render40ColumnTextLine(21);
894 Render40ColumnTextLine(22);
895 Render40ColumnTextLine(23);
915 RenderScreenBuffer();