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 */
70 bool alternateCharset;
71 //void SpawnMessage(const char * text, ...);
75 // We set up the colors this way so that they'll be endian safe
76 // when we cast them to a uint32_t. Note that the format is RGBA.
78 // "Master Color Values" palette
80 static uint8_t colors[16 * 4] = {
81 0x00, 0x00, 0x00, 0xFF, // Black
82 0xDD, 0x00, 0x33, 0xFF, // Deep Red (Magenta)
83 0x00, 0x00, 0x99, 0xFF, // Dark Blue
84 0xDD, 0x22, 0xDD, 0xFF, // Purple (Violet)
85 0x00, 0x77, 0x22, 0xFF, // Dark Green
86 0x55, 0x55, 0x55, 0xFF, // Dark Gray (Gray 1)
87 0x22, 0x22, 0xFF, 0xFF, // Medium Blue (Blue)
88 0x66, 0xAA, 0xFF, 0xFF, // Light Blue (Cyan)
89 0x88, 0x55, 0x00, 0xFF, // Brown
90 0xFF, 0x66, 0x00, 0xFF, // Orange
91 0xAA, 0xAA, 0xAA, 0xFF, // Light Gray (Gray 2)
92 0xFF, 0x99, 0x88, 0xFF, // Pink
93 0x11, 0xDD, 0x00, 0xFF, // Light Green (Green)
94 0xFF, 0xFF, 0x00, 0xFF, // Yellow
95 0x44, 0xFF, 0x99, 0xFF, // Aquamarine (Aqua)
96 0xFF, 0xFF, 0xFF, 0xFF // White
99 // This palette comes from ApplePC's colors (more realistic to my eye ;-)
101 static uint8_t altColors[16 * 4] = {
102 0x00, 0x00, 0x00, 0xFF,
103 0x7D, 0x20, 0x41, 0xFF,
104 0x41, 0x30, 0x7D, 0xFF,
105 0xBE, 0x51, 0xBE, 0xFF,
106 0x00, 0x5D, 0x3C, 0xFF,
107 0x7D, 0x7D, 0x7D, 0xFF,
108 0x41, 0x8E, 0xBA, 0xFF,
109 0xBE, 0xAE, 0xFB, 0xFF,
110 0x3C, 0x4D, 0x00, 0xFF,
111 0xBA, 0x6D, 0x41, 0xFF,
112 0x7D, 0x7D, 0x7D, 0xFF,
113 0xFB, 0x9E, 0xBE, 0xFF,
114 0x3C, 0xAA, 0x3C, 0xFF,
115 0xBA, 0xCB, 0x7D, 0xFF,
116 0x7D, 0xDB, 0xBA, 0xFF,
117 0xFB, 0xFB, 0xFB, 0xFF };
119 // Lo-res starting line addresses
121 static uint16_t lineAddrLoRes[24] = {
122 0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
123 0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
124 0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 };
126 // Hi-res starting line addresses
128 static uint16_t lineAddrHiRes[192] = {
129 0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
130 0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,
131 0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,
132 0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,
134 0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,
135 0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,
136 0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,
137 0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,
139 0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,
140 0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,
141 0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,
142 0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,
144 0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,
145 0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,
146 0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,
147 0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,
149 0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,
150 0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,
151 0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,
152 0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,
154 0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,
155 0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,
156 0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,
157 0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 };
159 uint16_t appleHiresToMono[0x200] = {
160 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
161 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
162 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
163 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
164 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
165 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
166 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
167 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
168 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
169 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
170 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
171 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
172 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
173 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
174 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
175 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
176 0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80,
177 0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x
178 0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98,
179 0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x
180 0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86,
181 0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax
182 0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E,
183 0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx
184 0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81,
185 0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx
186 0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99,
187 0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx
188 0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87,
189 0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex
190 0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F,
191 0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx
193 // Second half adds in the previous byte's lo pixel
195 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
196 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
197 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
198 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
199 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
200 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
201 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
202 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
203 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
204 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
205 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
206 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
207 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
208 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
209 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
210 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
211 0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80,
212 0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x
213 0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98,
214 0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x
215 0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86,
216 0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax
217 0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E,
218 0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx
219 0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81,
220 0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx
221 0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99,
222 0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx
223 0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87,
224 0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex
225 0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F,
226 0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF // $Fx
229 //static uint8_t blurTable[0x800][8]; // Color TV blur table
230 static uint8_t blurTable[0x80][8]; // Color TV blur table
231 static uint32_t * palette = (uint32_t *)altColors;
232 enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
233 static uint8_t screenType = ST_COLOR_TV;
237 static void Render40ColumnTextLine(uint8_t line);
238 static void Render40ColumnText(void);
239 static void RenderLoRes(uint16_t toLine = 24);
240 static void RenderHiRes(uint16_t toLine = 192);
243 void SetupBlurTable(void)
245 // NOTE: This table only needs to be 7 bits wide instead of 11, since the
246 // last four bits are copies of the previous four...
247 // Odd. Doing the bit patterns from 0-$7F doesn't work, but going
248 // from 0-$7FF stepping by 16 does. Hm.
249 // Well, it seems that going from 0-$7F doesn't have enough precision to do the job.
251 // for(uint16_t bitPat=0; bitPat<0x800; bitPat++)
252 for(uint16_t bitPat=0; bitPat<0x80; bitPat++)
254 /* uint16_t w3 = bitPat & 0x888;
255 uint16_t w2 = bitPat & 0x444;
256 uint16_t w1 = bitPat & 0x222;
257 uint16_t w0 = bitPat & 0x111;*/
258 uint16_t w3 = bitPat & 0x88;
259 uint16_t w2 = bitPat & 0x44;
260 uint16_t w1 = bitPat & 0x22;
261 uint16_t w0 = bitPat & 0x11;
263 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
264 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
265 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
266 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
268 for(int8_t i=7; i>=0; i--)
270 uint8_t color = (((blurred0 >> i) & 0x01) << 3)
271 | (((blurred1 >> i) & 0x01) << 2)
272 | (((blurred2 >> i) & 0x01) << 1)
273 | ((blurred3 >> i) & 0x01);
274 blurTable[bitPat][7 - i] = color;
278 for(uint16_t bitPat=0; bitPat<0x800; bitPat+=0x10)
280 uint16_t w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
282 uint16_t blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
283 uint16_t blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
284 uint16_t blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
285 uint16_t blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
287 for(int8_t i=7; i>=0; i--)
289 uint8_t color = (((blurred0 >> i) & 0x01) << 3)
290 | (((blurred1 >> i) & 0x01) << 2)
291 | (((blurred2 >> i) & 0x01) << 1)
292 | ((blurred3 >> i) & 0x01);
293 blurTable[bitPat >> 4][7 - i] = color;
300 void TogglePalette(void)
302 if (palette == (uint32_t *)colors)
304 palette = (uint32_t *)altColors;
305 SpawnMessage("Color TV palette");
309 palette = (uint32_t *)colors;
310 SpawnMessage("\"Master Color Values\" palette");
315 void CycleScreenTypes(void)
317 char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
321 if (screenType == ST_LAST_ENTRY)
322 screenType = ST_FIRST_ENTRY;
324 SpawnMessage("%s", scrTypeStr[screenType]);
328 static uint32_t msgTicks = 0;
329 static char message[4096];
331 void SpawnMessage(const char * text, ...)
336 vsprintf(message, text, arg);
343 static void DrawString2(uint32_t x, uint32_t y, uint32_t color);
344 static void DrawString(void)
346 //This approach works, and seems to be fast enough... Though it probably would
347 //be better to make the oversized font to match this one...
348 for(uint32_t x=7; x<=9; x++)
349 for(uint32_t y=7; y<=9; y++)
350 DrawString2(x, y, 0x00000000);
352 DrawString2(8, 8, 0x0020FF20);
356 static void DrawString2(uint32_t x, uint32_t y, uint32_t color)
358 //uint32_t x = 8, y = 8;
359 uint32_t length = strlen(message), address = x + (y * VIRTUAL_SCREEN_WIDTH);
360 // uint32_t color = 0x0020FF20;
361 //This could be done ahead of time, instead of on each pixel...
363 uint8_t nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
365 for(uint32_t i=0; i<length; i++)
367 uint8_t c = message[i];
368 c = (c < 32 ? 0 : c - 32);
369 uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
371 for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
373 for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
375 /* uint8_t fontTrans = font1[fontAddr++];
376 // uint32_t newTrans = (fontTrans * transparency / 255) << 24;
377 uint32_t newTrans = fontTrans << 24;
378 uint32_t pixel = newTrans | color;
380 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = pixel;//*/
382 uint8_t trans = font1[fontAddr++];
386 uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
388 uint8_t eBlue = (existingColor >> 16) & 0xFF,
389 eGreen = (existingColor >> 8) & 0xFF,
390 eRed = existingColor & 0xFF;
392 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
393 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
394 //because dividing by 32 is faster than dividing by 31...!
395 uint8_t invTrans = 255 - trans;
397 uint32_t bRed = (eRed * invTrans + nRed * trans) / 255;
398 uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
399 uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255;
401 //THIS IS NOT ENDIAN SAFE
402 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
407 address += FONT_WIDTH;
412 static void Render40ColumnTextLine(uint8_t line)
414 uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
416 for(int x=0; x<40; x++)
418 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
420 // Render character at (x, y)
422 for(int cy=0; cy<8; cy++)
424 for(int cx=0; cx<7; cx++)
426 uint32_t pixel = 0xFF000000;
428 if (!alternateCharset)
430 if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
431 // pixel = 0xFFFFFFFF;
435 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
437 if ((chr & 0xC0) == 0x40 && flash)
442 if (textChar2e[(chr * 56) + cx + (cy * 7)])
443 // pixel = 0xFFFFFFFF;
447 // scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
448 // scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
449 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
450 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
452 // QnD method to get blank alternate lines in text mode
453 if (screenType == ST_GREEN_MONO)
457 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
458 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
466 static void Render40ColumnText(void)
468 for(uint8_t line=0; line<24; line++)
469 Render40ColumnTextLine(line);
473 static void RenderLoRes(uint16_t toLine/*= 24*/)
475 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
476 // Also, we could set up three different Render functions depending on which
477 // render type was set and call it with a function pointer. Would be faster
478 // then the nested ifs we have now.
480 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
481 Color #s correspond to the bit patterns in reverse... Interesting!
483 00 00 00 -> 0 [0000] -> 0 (lores color #)
484 3c 4d 00 -> 8 [0001] -> 8? BROWN
485 00 5d 3c -> 4 [0010] -> 4? DARK GREEN
486 3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
487 41 30 7d -> 2 [0100] -> 2? DARK BLUE
488 7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY
489 41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
490 7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA)
491 7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
492 ba 6d 41 -> 9 [1001] -> 9? ORANGE
493 7d 7d 7d -> 5 [1010] -> 5? DARK GRAY
494 ba cb 7d -> 13 [1011] -> 13? YELLOW
495 be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET)
496 fb 9e be -> 11 [1101] -> 11? PINK
497 be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
498 fb fb fb -> 15 [1111] -> 15 WHITE
500 uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
502 //This is the old "perfect monitor" rendering code...
503 /* if (screenType != ST_COLOR_TV) // Not correct, but for now...
506 for(uint16_t y=0; y<toLine; y++)
508 for(uint16_t x=0; x<40; x++)
510 uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
511 uint32_t pixel = palette[scrByte & 0x0F];
513 for(int cy=0; cy<4; cy++)
514 for(int cx=0; cx<14; cx++)
515 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
517 pixel = palette[scrByte >> 4];
519 for(int cy=4; cy<8; cy++)
520 for(int cx=0; cx<14; cx++)
521 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
527 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
529 for(uint16_t y=0; y<toLine; y++)
531 // Do top half of lores screen bytes...
533 uint32_t previous3Bits = 0;
535 for(uint16_t x=0; x<40; x+=2)
537 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
538 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
539 scrByte1 = mirrorNybble[scrByte1];
540 scrByte2 = mirrorNybble[scrByte2];
541 // This is just a guess, but it'll have to do for now...
542 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
543 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
544 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
546 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
547 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
548 // 31 27 23 19 15 11 7 3 0
550 if (screenType == ST_COLOR_TV)
552 for(uint8_t i=0; i<7; i++)
554 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
557 for(uint8_t j=0; j<4; j++)
559 uint8_t color = blurTable[bitPat][j];
561 for(uint32_t cy=0; cy<8; cy++)
563 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
564 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
569 previous3Bits = pixels & 0x70000000;
573 for(int j=0; j<28; j++)
575 for(uint32_t cy=0; cy<8; cy++)
577 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
578 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
586 // Now do bottom half...
590 for(uint16_t x=0; x<40; x+=2)
592 uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
593 uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
594 scrByte1 = mirrorNybble[scrByte1];
595 scrByte2 = mirrorNybble[scrByte2];
596 // This is just a guess, but it'll have to do for now...
597 uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
598 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
599 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
601 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
602 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
603 // 31 27 23 19 15 11 7 3 0
605 if (screenType == ST_COLOR_TV)
607 for(uint8_t i=0; i<7; i++)
609 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
612 for(uint8_t j=0; j<4; j++)
614 uint8_t color = blurTable[bitPat][j];
616 for(uint32_t cy=8; cy<16; cy++)
618 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
619 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
624 previous3Bits = pixels & 0x70000000;
628 for(int j=0; j<28; j++)
630 for(uint32_t cy=8; cy<16; cy++)
632 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
633 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
644 static void RenderHiRes(uint16_t toLine/*= 192*/)
646 // NOTE: Not endian safe. !!! FIX !!! [DONE]
648 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
650 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
651 // The colors are set in the 8-bit array as R G B A
652 uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
653 uint32_t * colorPtr = (uint32_t *)monoColors;
654 uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
657 for(uint16_t y=0; y<toLine; y++)
659 uint16_t previousLoPixel = 0;
660 uint32_t previous3bits = 0;
662 for(uint16_t x=0; x<40; x+=2)
664 uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
665 uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
666 previousLoPixel = (screenByte << 2) & 0x0100;
668 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
669 uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
670 previousLoPixel = (screenByte << 2) & 0x0100;
672 pixels = previous3bits | (pixels << 14) | pixels2;
674 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
675 // 0ppp 1111 1111 1111 1111 1111 1111 1111
676 // 31 27 23 19 15 11 7 3 0
678 if (screenType == ST_COLOR_TV)
680 for(uint8_t i=0; i<7; i++)
682 uint8_t bitPat = (pixels & 0x7F000000) >> 24;
685 for(uint8_t j=0; j<4; j++)
687 uint8_t color = blurTable[bitPat][j];
689 //This doesn't seem to make things go any faster...
690 //It's the OpenGL render that's faster... Hmm...
691 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
693 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
694 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
699 previous3bits = pixels & 0x70000000;
703 for(int j=0; j<28; j++)
705 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
707 if (screenType == ST_GREEN_MONO)
708 pixels &= 0x07FFFFFF;
710 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
719 void RenderVideoFrame(void)
723 RenderScreenBuffer();
728 // There's prolly more to it than this (like 80 column text), but this'll have to do for now...
729 Render40ColumnText();
738 Render40ColumnTextLine(20);
739 Render40ColumnTextLine(21);
740 Render40ColumnTextLine(22);
741 Render40ColumnTextLine(23);
746 Render40ColumnTextLine(20);
747 Render40ColumnTextLine(21);
748 Render40ColumnTextLine(22);
749 Render40ColumnTextLine(23);
767 RenderScreenBuffer();