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 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
424 address += FONT_WIDTH;
429 static void Render40ColumnTextLine(uint8_t line)
431 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
433 for(int x=0; x<40; x++)
435 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
437 // Render character at (x, y)
439 for(int cy=0; cy<8; cy++)
441 for(int cx=0; cx<7; cx++)
443 uint32_t pixel = 0xFF000000;
445 if (alternateCharset)
447 if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
451 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
453 if ((chr & 0xC0) == 0x40 && flash)
458 if (textChar2e[(chr * 56) + cx + (cy * 7)])
462 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
463 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
465 // QnD method to get blank alternate lines in text mode
466 if (screenType == ST_GREEN_MONO)
470 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
471 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
479 static void Render80ColumnTextLine(uint8_t line)
481 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
483 for(int x=0; x<80; x++)
486 // This is wrong; it should grab from the alt bank if Page2 is set, not main RAM @ $0
487 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
490 chr = ram2[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x - 40];
495 chr = ram[lineAddrLoRes[line] + (x >> 1)];
497 chr = ram2[lineAddrLoRes[line] + (x >> 1)];
500 // Render character at (x, y)
502 for(int cy=0; cy<8; cy++)
504 for(int cx=0; cx<7; cx++)
506 uint32_t pixel = 0xFF000000;
508 if (alternateCharset)
510 if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
514 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
516 if ((chr & 0xC0) == 0x40 && flash)
521 if (textChar2e[(chr * 56) + cx + (cy * 7)])
525 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
527 // QnD method to get blank alternate lines in text mode
528 if (screenType == ST_GREEN_MONO)
531 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
538 static void Render40ColumnText(void)
540 for(uint8_t line=0; line<24; line++)
541 Render40ColumnTextLine(line);
545 static void Render80ColumnText(void)
547 for(uint8_t line=0; line<24; line++)
548 Render80ColumnTextLine(line);
552 static void RenderLoRes(uint16_t toLine/*= 24*/)
554 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
555 // Also, we could set up three different Render functions depending on which
556 // render type was set and call it with a function pointer. Would be faster
557 // then the nested ifs we have now.
559 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
560 Color #s correspond to the bit patterns in reverse... Interesting!
562 00 00 00 -> 0 [0000] -> 0 (lores color #)
563 3c 4d 00 -> 8 [0001] -> 8? BROWN
564 00 5d 3c -> 4 [0010] -> 4? DARK GREEN
565 3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
566 41 30 7d -> 2 [0100] -> 2? DARK BLUE
567 7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY
568 41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
569 7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA)
570 7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
571 ba 6d 41 -> 9 [1001] -> 9? ORANGE
572 7d 7d 7d -> 5 [1010] -> 5? DARK GRAY
573 ba cb 7d -> 13 [1011] -> 13? YELLOW
574 be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET)
575 fb 9e be -> 11 [1101] -> 11? PINK
576 be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
577 fb fb fb -> 15 [1111] -> 15 WHITE
579 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
581 //This is the old "perfect monitor" rendering code...
582 /* if (screenType != ST_COLOR_TV) // Not correct, but for now...
585 for(uint16_t y=0; y<toLine; y++)
587 for(uint16_t x=0; x<40; x++)
589 uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
590 uint32_t pixel = palette[scrByte & 0x0F];
592 for(int cy=0; cy<4; cy++)
593 for(int cx=0; cx<14; cx++)
594 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
596 pixel = palette[scrByte >> 4];
598 for(int cy=4; cy<8; cy++)
599 for(int cx=0; cx<14; cx++)
600 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
606 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
608 for(uint16_t y=0; y<toLine; y++)
610 // Do top half of lores screen bytes...
612 uint32_t previous3Bits = 0;
614 for(uint16_t x=0; x<40; x+=2)
616 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
617 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
618 scrByte1 = mirrorNybble[scrByte1];
619 scrByte2 = mirrorNybble[scrByte2];
620 // This is just a guess, but it'll have to do for now...
621 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
622 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
623 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
625 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
626 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
627 // 31 27 23 19 15 11 7 3 0
629 if (screenType == ST_COLOR_TV)
631 for(uint8_t i=0; i<7; i++)
633 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
636 for(uint8_t j=0; j<4; j++)
638 uint8_t color = blurTable[bitPat][j];
640 for(uint32_t cy=0; cy<8; cy++)
642 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
643 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
648 previous3Bits = pixels & 0x70000000;
652 for(int j=0; j<28; j++)
654 for(uint32_t cy=0; cy<8; cy++)
656 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
657 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
665 // Now do bottom half...
669 for(uint16_t x=0; x<40; x+=2)
671 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
672 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
673 scrByte1 = mirrorNybble[scrByte1];
674 scrByte2 = mirrorNybble[scrByte2];
675 // This is just a guess, but it'll have to do for now...
676 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
677 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
678 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
680 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
681 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
682 // 31 27 23 19 15 11 7 3 0
684 if (screenType == ST_COLOR_TV)
686 for(uint8_t i=0; i<7; i++)
688 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
691 for(uint8_t j=0; j<4; j++)
693 uint8_t color = blurTable[bitPat][j];
695 for(uint32_t cy=8; cy<16; cy++)
697 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
698 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
703 previous3Bits = pixels & 0x70000000;
707 for(int j=0; j<28; j++)
709 for(uint32_t cy=8; cy<16; cy++)
711 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
712 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
723 static void RenderHiRes(uint16_t toLine/*= 192*/)
725 // NOTE: Not endian safe. !!! FIX !!! [DONE]
727 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
729 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
730 // The colors are set in the 8-bit array as R G B A
731 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
732 uint32_t * colorPtr = (uint32_t *)monoColors;
733 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
736 for(uint16_t y=0; y<toLine; y++)
738 uint16_t previousLoPixel = 0;
739 uint32_t previous3bits = 0;
741 for(uint16_t x=0; x<40; x+=2)
743 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
744 uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
745 previousLoPixel = (screenByte << 2) & 0x0100;
747 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
748 uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
749 previousLoPixel = (screenByte << 2) & 0x0100;
751 pixels = previous3bits | (pixels << 14) | pixels2;
753 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
754 // 0ppp 1111 1111 1111 1111 1111 1111 1111
755 // 31 27 23 19 15 11 7 3 0
757 if (screenType == ST_COLOR_TV)
759 for(uint8_t i=0; i<7; i++)
761 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
764 for(uint8_t j=0; j<4; j++)
766 uint8_t color = blurTable[bitPat][j];
768 //This doesn't seem to make things go any faster...
769 //It's the OpenGL render that's faster... Hmm...
770 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
772 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
773 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
778 previous3bits = pixels & 0x70000000;
782 for(int j=0; j<28; j++)
784 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
786 if (screenType == ST_GREEN_MONO)
787 pixels &= 0x07FFFFFF;
789 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
798 static void RenderDHiRes(uint16_t toLine/*= 192*/)
800 // NOTE: Not endian safe. !!! FIX !!! [DONE]
802 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
804 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
805 // The colors are set in the 8-bit array as R G B A
806 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
807 uint32_t * colorPtr = (uint32_t *)monoColors;
808 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
811 for(uint16_t y=0; y<toLine; y++)
813 uint16_t previousLoPixel = 0;
814 uint32_t previous3bits = 0;
816 for(uint16_t x=0; x<40; x+=2)
818 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
819 uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
820 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
821 pixels = pixels | (mirrorTable[screenByte & 0x7F]);
822 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
823 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
824 screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
825 pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
826 pixels = previous3bits | (pixels >> 1);
828 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
829 // 0ppp 1111 1111 1111 1111 1111 1111 1111
830 // 31 27 23 19 15 11 7 3 0
832 if (screenType == ST_COLOR_TV)
834 for(uint8_t i=0; i<7; i++)
836 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
839 for(uint8_t j=0; j<4; j++)
841 uint8_t color = blurTable[bitPat][j];
842 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
843 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
847 previous3bits = pixels & 0x70000000;
851 for(int j=0; j<28; j++)
853 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
855 if (screenType == ST_GREEN_MONO)
856 pixels &= 0x07FFFFFF;
858 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
867 void RenderVideoFrame(void)
871 RenderScreenBuffer();
876 // There's prolly more to it than this (like 80 column text), but this'll have to do for now...
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);
920 RenderScreenBuffer();