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()
31 #include <stdarg.h> // for va_* stuff
32 #include <string> // for vsprintf()
38 /* Reference: Technote tn-iigs-063 "Master Color Values"
40 Color Color Register LR HR DHR Master Color R,G,B
41 Name Value # # # Value
42 ----------------------------------------------------
43 Black 0 0 0,4 0 $0000 (0,0,0)
44 (Magenta) Deep Red 1 1 1 $0D03 (D,0,3)
45 Dark Blue 2 2 8 $0009 (0,0,9)
46 (Violet) Purple 3 3 2 9 $0D2D (D,2,D)
47 Dark Green 4 4 4 $0072 (0,7,2)
48 (Gray 1) Dark Gray 5 5 5 $0555 (5,5,5)
49 (Blue) Medium Blue 6 6 6 C $022F (2,2,F)
50 (Cyan) Light Blue 7 7 D $06AF (6,A,F)
51 Brown 8 8 2 $0850 (8,5,0)
52 Orange 9 9 5 3 $0F60 (F,6,0)
53 (Gray 2) Light Gray A A A $0AAA (A,A,A)
54 Pink B B B $0F98 (F,9,8)
55 (Green) Light Green C C 1 6 $01D0 (1,D,0)
56 Yellow D D 7 $0FF0 (F,F,0)
57 (Aqua) Aquamarine E E E $04F9 (4,F,9)
58 White F F 3,7 F $0FFF (F,F,F)
60 LR: Lo-Res HR: Hi-Res DHR: Double Hi-Res */
69 bool alternateCharset;
70 //void SpawnMessage(const char * text, ...);
74 // We set up the colors this way so that they'll be endian safe
75 // when we cast them to a uint32. Note that the format is RGBA.
77 // "Master Color Values" palette
79 static uint8 colors[16 * 4] = {
80 0x00, 0x00, 0x00, 0xFF, // Black
81 0xDD, 0x00, 0x33, 0xFF, // Deep Red (Magenta)
82 0x00, 0x00, 0x99, 0xFF, // Dark Blue
83 0xDD, 0x22, 0xDD, 0xFF, // Purple (Violet)
84 0x00, 0x77, 0x22, 0xFF, // Dark Green
85 0x55, 0x55, 0x55, 0xFF, // Dark Gray (Gray 1)
86 0x22, 0x22, 0xFF, 0xFF, // Medium Blue (Blue)
87 0x66, 0xAA, 0xFF, 0xFF, // Light Blue (Cyan)
88 0x88, 0x55, 0x00, 0xFF, // Brown
89 0xFF, 0x66, 0x00, 0xFF, // Orange
90 0xAA, 0xAA, 0xAA, 0xFF, // Light Gray (Gray 2)
91 0xFF, 0x99, 0x88, 0xFF, // Pink
92 0x11, 0xDD, 0x00, 0xFF, // Light Green (Green)
93 0xFF, 0xFF, 0x00, 0xFF, // Yellow
94 0x44, 0xFF, 0x99, 0xFF, // Aquamarine (Aqua)
95 0xFF, 0xFF, 0xFF, 0xFF // White
98 // This palette comes from ApplePC's colors (more realistic to my eye ;-)
100 static uint8 altColors[16 * 4] = {
101 0x00, 0x00, 0x00, 0xFF,
102 0x7D, 0x20, 0x41, 0xFF,
103 0x41, 0x30, 0x7D, 0xFF,
104 0xBE, 0x51, 0xBE, 0xFF,
105 0x00, 0x5D, 0x3C, 0xFF,
106 0x7D, 0x7D, 0x7D, 0xFF,
107 0x41, 0x8E, 0xBA, 0xFF,
108 0xBE, 0xAE, 0xFB, 0xFF,
109 0x3C, 0x4D, 0x00, 0xFF,
110 0xBA, 0x6D, 0x41, 0xFF,
111 0x7D, 0x7D, 0x7D, 0xFF,
112 0xFB, 0x9E, 0xBE, 0xFF,
113 0x3C, 0xAA, 0x3C, 0xFF,
114 0xBA, 0xCB, 0x7D, 0xFF,
115 0x7D, 0xDB, 0xBA, 0xFF,
116 0xFB, 0xFB, 0xFB, 0xFF };
118 // Lo-res starting line addresses
120 static uint16 lineAddrLoRes[24] = {
121 0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x0700, 0x0780,
122 0x0428, 0x04A8, 0x0528, 0x05A8, 0x0628, 0x06A8, 0x0728, 0x07A8,
123 0x0450, 0x04D0, 0x0550, 0x05D0, 0x0650, 0x06D0, 0x0750, 0x07D0 };
125 // Hi-res starting line addresses
127 static uint16 lineAddrHiRes[192] = {
128 0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00,
129 0x2080, 0x2480, 0x2880, 0x2C80, 0x3080, 0x3480, 0x3880, 0x3C80,
130 0x2100, 0x2500, 0x2900, 0x2D00, 0x3100, 0x3500, 0x3900, 0x3D00,
131 0x2180, 0x2580, 0x2980, 0x2D80, 0x3180, 0x3580, 0x3980, 0x3D80,
133 0x2200, 0x2600, 0x2A00, 0x2E00, 0x3200, 0x3600, 0x3A00, 0x3E00,
134 0x2280, 0x2680, 0x2A80, 0x2E80, 0x3280, 0x3680, 0x3A80, 0x3E80,
135 0x2300, 0x2700, 0x2B00, 0x2F00, 0x3300, 0x3700, 0x3B00, 0x3F00,
136 0x2380, 0x2780, 0x2B80, 0x2F80, 0x3380, 0x3780, 0x3B80, 0x3F80,
138 0x2028, 0x2428, 0x2828, 0x2C28, 0x3028, 0x3428, 0x3828, 0x3C28,
139 0x20A8, 0x24A8, 0x28A8, 0x2CA8, 0x30A8, 0x34A8, 0x38A8, 0x3CA8,
140 0x2128, 0x2528, 0x2928, 0x2D28, 0x3128, 0x3528, 0x3928, 0x3D28,
141 0x21A8, 0x25A8, 0x29A8, 0x2DA8, 0x31A8, 0x35A8, 0x39A8, 0x3DA8,
143 0x2228, 0x2628, 0x2A28, 0x2E28, 0x3228, 0x3628, 0x3A28, 0x3E28,
144 0x22A8, 0x26A8, 0x2AA8, 0x2EA8, 0x32A8, 0x36A8, 0x3AA8, 0x3EA8,
145 0x2328, 0x2728, 0x2B28, 0x2F28, 0x3328, 0x3728, 0x3B28, 0x3F28,
146 0x23A8, 0x27A8, 0x2BA8, 0x2FA8, 0x33A8, 0x37A8, 0x3BA8, 0x3FA8,
148 0x2050, 0x2450, 0x2850, 0x2C50, 0x3050, 0x3450, 0x3850, 0x3C50,
149 0x20D0, 0x24D0, 0x28D0, 0x2CD0, 0x30D0, 0x34D0, 0x38D0, 0x3CD0,
150 0x2150, 0x2550, 0x2950, 0x2D50, 0x3150, 0x3550, 0x3950, 0x3D50,
151 0x21D0, 0x25D0, 0x29D0, 0x2DD0, 0x31D0, 0x35D0, 0x39D0, 0x3DD0,
153 0x2250, 0x2650, 0x2A50, 0x2E50, 0x3250, 0x3650, 0x3A50, 0x3E50,
154 0x22D0, 0x26D0, 0x2AD0, 0x2ED0, 0x32D0, 0x36D0, 0x3AD0, 0x3ED0,
155 0x2350, 0x2750, 0x2B50, 0x2F50, 0x3350, 0x3750, 0x3B50, 0x3F50,
156 0x23D0, 0x27D0, 0x2BD0, 0x2FD0, 0x33D0, 0x37D0, 0x3BD0, 0x3FD0 };
158 uint16 appleHiresToMono[0x200] = {
159 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
160 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
161 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
162 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
163 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
164 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
165 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
166 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
167 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
168 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
169 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
170 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
171 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
172 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
173 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
174 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
175 0x0000, 0x1800, 0x0600, 0x1E00, 0x0180, 0x1980, 0x0780, 0x1F80,
176 0x0060, 0x1860, 0x0660, 0x1E60, 0x01E0, 0x19E0, 0x07E0, 0x1FE0, // $8x
177 0x0018, 0x1818, 0x0618, 0x1E18, 0x0198, 0x1998, 0x0798, 0x1F98,
178 0x0078, 0x1878, 0x0678, 0x1E78, 0x01F8, 0x19F8, 0x07F8, 0x1FF8, // $9x
179 0x0006, 0x1806, 0x0606, 0x1E06, 0x0186, 0x1986, 0x0786, 0x1F86,
180 0x0066, 0x1866, 0x0666, 0x1E66, 0x01E6, 0x19E6, 0x07E6, 0x1FE6, // $Ax
181 0x001E, 0x181E, 0x061E, 0x1E1E, 0x019E, 0x199E, 0x079E, 0x1F9E,
182 0x007E, 0x187E, 0x067E, 0x1E7E, 0x01FE, 0x19FE, 0x07FE, 0x1FFE, // $Bx
183 0x0001, 0x1801, 0x0601, 0x1E01, 0x0181, 0x1981, 0x0781, 0x1F81,
184 0x0061, 0x1861, 0x0661, 0x1E61, 0x01E1, 0x19E1, 0x07E1, 0x1FE1, // $Cx
185 0x0019, 0x1819, 0x0619, 0x1E19, 0x0199, 0x1999, 0x0799, 0x1F99,
186 0x0079, 0x1879, 0x0679, 0x1E79, 0x01F9, 0x19F9, 0x07F9, 0x1FF9, // $Dx
187 0x0007, 0x1807, 0x0607, 0x1E07, 0x0187, 0x1987, 0x0787, 0x1F87,
188 0x0067, 0x1867, 0x0667, 0x1E67, 0x01E7, 0x19E7, 0x07E7, 0x1FE7, // $Ex
189 0x001F, 0x181F, 0x061F, 0x1E1F, 0x019F, 0x199F, 0x079F, 0x1F9F,
190 0x007F, 0x187F, 0x067F, 0x1E7F, 0x01FF, 0x19FF, 0x07FF, 0x1FFF, // $Fx
192 // Second half adds in the previous byte's lo pixel
194 0x0000, 0x3000, 0x0C00, 0x3C00, 0x0300, 0x3300, 0x0F00, 0x3F00,
195 0x00C0, 0x30C0, 0x0CC0, 0x3CC0, 0x03C0, 0x33C0, 0x0FC0, 0x3FC0, // $0x
196 0x0030, 0x3030, 0x0C30, 0x3C30, 0x0330, 0x3330, 0x0F30, 0x3F30,
197 0x00F0, 0x30F0, 0x0CF0, 0x3CF0, 0x03F0, 0x33F0, 0x0FF0, 0x3FF0, // $1x
198 0x000C, 0x300C, 0x0C0C, 0x3C0C, 0x030C, 0x330C, 0x0F0C, 0x3F0C,
199 0x00CC, 0x30CC, 0x0CCC, 0x3CCC, 0x03CC, 0x33CC, 0x0FCC, 0x3FCC, // $2x
200 0x003C, 0x303C, 0x0C3C, 0x3C3C, 0x033C, 0x333C, 0x0F3C, 0x3F3C,
201 0x00FC, 0x30FC, 0x0CFC, 0x3CFC, 0x03FC, 0x33FC, 0x0FFC, 0x3FFC, // $3x
202 0x0003, 0x3003, 0x0C03, 0x3C03, 0x0303, 0x3303, 0x0F03, 0x3F03,
203 0x00C3, 0x30C3, 0x0CC3, 0x3CC3, 0x03C3, 0x33C3, 0x0FC3, 0x3FC3, // $4x
204 0x0033, 0x3033, 0x0C33, 0x3C33, 0x0333, 0x3333, 0x0F33, 0x3F33,
205 0x00F3, 0x30F3, 0x0CF3, 0x3CF3, 0x03F3, 0x33F3, 0x0FF3, 0x3FF3, // $5x
206 0x000F, 0x300F, 0x0C0F, 0x3C0F, 0x030F, 0x330F, 0x0F0F, 0x3F0F,
207 0x00CF, 0x30CF, 0x0CCF, 0x3CCF, 0x03CF, 0x33CF, 0x0FCF, 0x3FCF, // $6x
208 0x003F, 0x303F, 0x0C3F, 0x3C3F, 0x033F, 0x333F, 0x0F3F, 0x3F3F,
209 0x00FF, 0x30FF, 0x0CFF, 0x3CFF, 0x03FF, 0x33FF, 0x0FFF, 0x3FFF, // $7x
210 0x2000, 0x3800, 0x2600, 0x3E00, 0x2180, 0x3980, 0x2780, 0x3F80,
211 0x2060, 0x3860, 0x2660, 0x3E60, 0x21E0, 0x39E0, 0x27E0, 0x3FE0, // $8x
212 0x2018, 0x3818, 0x2618, 0x3E18, 0x2198, 0x3998, 0x2798, 0x3F98,
213 0x2078, 0x3878, 0x2678, 0x3E78, 0x21F8, 0x39F8, 0x27F8, 0x3FF8, // $9x
214 0x2006, 0x3806, 0x2606, 0x3E06, 0x2186, 0x3986, 0x2786, 0x3F86,
215 0x2066, 0x3866, 0x2666, 0x3E66, 0x21E6, 0x39E6, 0x27E6, 0x3FE6, // $Ax
216 0x201E, 0x381E, 0x261E, 0x3E1E, 0x219E, 0x399E, 0x279E, 0x3F9E,
217 0x207E, 0x387E, 0x267E, 0x3E7E, 0x21FE, 0x39FE, 0x27FE, 0x3FFE, // $Bx
218 0x2001, 0x3801, 0x2601, 0x3E01, 0x2181, 0x3981, 0x2781, 0x3F81,
219 0x2061, 0x3861, 0x2661, 0x3E61, 0x21E1, 0x39E1, 0x27E1, 0x3FE1, // $Cx
220 0x2019, 0x3819, 0x2619, 0x3E19, 0x2199, 0x3999, 0x2799, 0x3F99,
221 0x2079, 0x3879, 0x2679, 0x3E79, 0x21F9, 0x39F9, 0x27F9, 0x3FF9, // $Dx
222 0x2007, 0x3807, 0x2607, 0x3E07, 0x2187, 0x3987, 0x2787, 0x3F87,
223 0x2067, 0x3867, 0x2667, 0x3E67, 0x21E7, 0x39E7, 0x27E7, 0x3FE7, // $Ex
224 0x201F, 0x381F, 0x261F, 0x3E1F, 0x219F, 0x399F, 0x279F, 0x3F9F,
225 0x207F, 0x387F, 0x267F, 0x3E7F, 0x21FF, 0x39FF, 0x27FF, 0x3FFF // $Fx
228 //static uint8 blurTable[0x800][8]; // Color TV blur table
229 static uint8 blurTable[0x80][8]; // Color TV blur table
230 static uint32 * palette = (uint32 *)altColors;
231 enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
232 static uint8 screenType = ST_COLOR_TV;
236 static void Render40ColumnTextLine(uint8 line);
237 static void Render40ColumnText(void);
238 static void RenderLoRes(uint16 toLine = 24);
239 static void RenderHiRes(uint16 toLine = 192);
242 void SetupBlurTable(void)
244 // NOTE: This table only needs to be 7 bits wide instead of 11, since the
245 // last four bits are copies of the previous four...
246 // Odd. Doing the bit patterns from 0-$7F doesn't work, but going
247 // from 0-$7FF stepping by 16 does. Hm.
248 // Well, it seems that going from 0-$7F doesn't have enough precision to do the job.
250 // for(uint16 bitPat=0; bitPat<0x800; bitPat++)
251 for(uint16 bitPat=0; bitPat<0x80; bitPat++)
253 /* uint16 w3 = bitPat & 0x888;
254 uint16 w2 = bitPat & 0x444;
255 uint16 w1 = bitPat & 0x222;
256 uint16 w0 = bitPat & 0x111;*/
257 uint16 w3 = bitPat & 0x88;
258 uint16 w2 = bitPat & 0x44;
259 uint16 w1 = bitPat & 0x22;
260 uint16 w0 = bitPat & 0x11;
262 uint16 blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
263 uint16 blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
264 uint16 blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
265 uint16 blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
267 for(int8 i=7; i>=0; i--)
269 uint8 color = (((blurred0 >> i) & 0x01) << 3)
270 | (((blurred1 >> i) & 0x01) << 2)
271 | (((blurred2 >> i) & 0x01) << 1)
272 | ((blurred3 >> i) & 0x01);
273 blurTable[bitPat][7 - i] = color;
277 for(uint16 bitPat=0; bitPat<0x800; bitPat+=0x10)
279 uint16 w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
281 uint16 blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
282 uint16 blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
283 uint16 blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
284 uint16 blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
286 for(int8 i=7; i>=0; i--)
288 uint8 color = (((blurred0 >> i) & 0x01) << 3)
289 | (((blurred1 >> i) & 0x01) << 2)
290 | (((blurred2 >> i) & 0x01) << 1)
291 | ((blurred3 >> i) & 0x01);
292 blurTable[bitPat >> 4][7 - i] = color;
298 void TogglePalette(void)
300 if (palette == (uint32 *)colors)
302 palette = (uint32 *)altColors;
303 SpawnMessage("Color TV palette");
307 palette = (uint32 *)colors;
308 SpawnMessage("\"Master Color Values\" palette");
312 void CycleScreenTypes(void)
314 char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
318 if (screenType == ST_LAST_ENTRY)
319 screenType = ST_FIRST_ENTRY;
321 SpawnMessage("%s", scrTypeStr[screenType]);
324 static uint32 msgTicks = 0;
325 static char message[4096];
327 void SpawnMessage(const char * text, ...)
332 vsprintf(message, text, arg);
338 static void DrawString2(uint32 x, uint32 y, uint32 color);
339 static void DrawString(void)
341 //This approach works, and seems to be fast enough... Though it probably would
342 //be better to make the oversized font to match this one...
343 for(uint32 x=7; x<=9; x++)
344 for(uint32 y=7; y<=9; y++)
345 DrawString2(x, y, 0x00000000);
347 DrawString2(8, 8, 0x0020FF20);
350 static void DrawString2(uint32 x, uint32 y, uint32 color)
352 //uint32 x = 8, y = 8;
353 uint32 length = strlen(message), address = x + (y * VIRTUAL_SCREEN_WIDTH);
354 // uint32 color = 0x0020FF20;
355 //This could be done ahead of time, instead of on each pixel...
357 uint8 nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
359 for(uint32 i=0; i<length; i++)
361 uint8 c = message[i];
362 c = (c < 32 ? 0 : c - 32);
363 uint32 fontAddr = (uint32)c * FONT_WIDTH * FONT_HEIGHT;
365 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
367 for(uint32 xx=0; xx<FONT_WIDTH; xx++)
369 /* uint8 fontTrans = font1[fontAddr++];
370 // uint32 newTrans = (fontTrans * transparency / 255) << 24;
371 uint32 newTrans = fontTrans << 24;
372 uint32 pixel = newTrans | color;
374 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = pixel;//*/
376 uint8 trans = font1[fontAddr++];
380 uint32 existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
382 uint8 eBlue = (existingColor >> 16) & 0xFF,
383 eGreen = (existingColor >> 8) & 0xFF,
384 eRed = existingColor & 0xFF;
386 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
387 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
388 //because dividing by 32 is faster than dividing by 31...!
389 uint8 invTrans = 255 - trans;
391 uint32 bRed = (eRed * invTrans + nRed * trans) / 255;
392 uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
393 uint32 bBlue = (eBlue * invTrans + nBlue * trans) / 255;
395 //THIS IS NOT ENDIAN SAFE
396 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
401 address += FONT_WIDTH;
405 static void Render40ColumnTextLine(uint8 line)
407 uint32 pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
409 for(int x=0; x<40; x++)
411 uint8 chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
413 // Render character at (x, y)
415 for(int cy=0; cy<8; cy++)
417 for(int cx=0; cx<7; cx++)
419 uint32 pixel = 0xFF000000;
421 if (!alternateCharset)
423 if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
424 // pixel = 0xFFFFFFFF;
428 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
430 if ((chr & 0xC0) == 0x40 && flash)
435 if (textChar2e[(chr * 56) + cx + (cy * 7)])
436 // pixel = 0xFFFFFFFF;
440 // scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
441 // scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
442 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
443 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
445 // QnD method to get blank alternate lines in text mode
446 if (screenType == ST_GREEN_MONO)
450 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
451 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
458 static void Render40ColumnText(void)
460 for(uint8 line=0; line<24; line++)
461 Render40ColumnTextLine(line);
464 static void RenderLoRes(uint16 toLine/*= 24*/)
466 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
467 // Also, we could set up three different Render functions depending on which
468 // render type was set and call it with a function pointer. Would be faster
469 // then the nested ifs we have now.
471 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
472 Color #s correspond to the bit patterns in reverse... Interesting!
474 00 00 00 -> 0 [0000] -> 0 (lores color #)
475 3c 4d 00 -> 8 [0001] -> 8? BROWN
476 00 5d 3c -> 4 [0010] -> 4? DARK GREEN
477 3c aa 3c -> 12 [0011] -> 12? LIGHT GREEN (GREEN)
478 41 30 7d -> 2 [0100] -> 2? DARK BLUE
479 7d 7d 7d -> 10 [0101] -> 10? LIGHT GRAY
480 41 8e ba -> 6 [0110] -> 6? MEDIUM BLUE (BLUE)
481 7d db ba -> 14 [0111] -> 14? AQUAMARINE (AQUA)
482 7d 20 41 -> 1 [1000] -> 1? DEEP RED (MAGENTA)
483 ba 6d 41 -> 9 [1001] -> 9? ORANGE
484 7d 7d 7d -> 5 [1010] -> 5? DARK GRAY
485 ba cb 7d -> 13 [1011] -> 13? YELLOW
486 be 51 be -> 3 [1100] -> 3 PURPLE (VIOLET)
487 fb 9e be -> 11 [1101] -> 11? PINK
488 be ae fb -> 7 [1110] -> 7? LIGHT BLUE (CYAN)
489 fb fb fb -> 15 [1111] -> 15 WHITE
491 uint8 mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
493 //This is the old "perfect monitor" rendering code...
494 /* if (screenType != ST_COLOR_TV) // Not correct, but for now...
497 for(uint16 y=0; y<toLine; y++)
499 for(uint16 x=0; x<40; x++)
501 uint8 scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
502 uint32 pixel = palette[scrByte & 0x0F];
504 for(int cy=0; cy<4; cy++)
505 for(int cx=0; cx<14; cx++)
506 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
508 pixel = palette[scrByte >> 4];
510 for(int cy=4; cy<8; cy++)
511 for(int cx=0; cx<14; cx++)
512 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
518 uint32 pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
520 for(uint16 y=0; y<toLine; y++)
522 // Do top half of lores screen bytes...
524 uint32 previous3Bits = 0;
526 for(uint16 x=0; x<40; x+=2)
528 uint8 scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
529 uint8 scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
530 scrByte1 = mirrorNybble[scrByte1];
531 scrByte2 = mirrorNybble[scrByte2];
532 // This is just a guess, but it'll have to do for now...
533 uint32 pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
534 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
535 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
537 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
538 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
539 // 31 27 23 19 15 11 7 3 0
541 if (screenType == ST_COLOR_TV)
543 for(uint8 i=0; i<7; i++)
545 uint8 bitPat = (pixels & 0x7F000000) >> 24;
548 for(uint8 j=0; j<4; j++)
550 uint8 color = blurTable[bitPat][j];
552 for(uint32 cy=0; cy<8; cy++)
554 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
555 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
560 previous3Bits = pixels & 0x70000000;
564 for(int j=0; j<28; j++)
566 for(uint32 cy=0; cy<8; cy++)
568 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
569 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
577 // Now do bottom half...
581 for(uint16 x=0; x<40; x+=2)
583 uint8 scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
584 uint8 scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
585 scrByte1 = mirrorNybble[scrByte1];
586 scrByte2 = mirrorNybble[scrByte2];
587 // This is just a guess, but it'll have to do for now...
588 uint32 pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
589 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
590 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
592 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
593 // 0ppp 1111 1111 1111 11|11 1111 1111 1111
594 // 31 27 23 19 15 11 7 3 0
596 if (screenType == ST_COLOR_TV)
598 for(uint8 i=0; i<7; i++)
600 uint8 bitPat = (pixels & 0x7F000000) >> 24;
603 for(uint8 j=0; j<4; j++)
605 uint8 color = blurTable[bitPat][j];
607 for(uint32 cy=8; cy<16; cy++)
609 scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
610 // scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
615 previous3Bits = pixels & 0x70000000;
619 for(int j=0; j<28; j++)
621 for(uint32 cy=8; cy<16; cy++)
623 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
624 // scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
634 static void RenderHiRes(uint16 toLine/*= 192*/)
636 // NOTE: Not endian safe. !!! FIX !!!
638 uint32 pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
640 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
641 // The colors are set in the 8-bit array as R G B A
642 uint8 monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
643 uint32 * colorPtr = (uint32 *)monoColors;
644 uint32 pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
647 for(uint16 y=0; y<toLine; y++)
649 uint16 previousLoPixel = 0;
650 uint32 previous3bits = 0;
652 for(uint16 x=0; x<40; x+=2)
654 uint8 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
655 uint32 pixels = appleHiresToMono[previousLoPixel | screenByte];
656 previousLoPixel = (screenByte << 2) & 0x0100;
658 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
659 uint32 pixels2 = appleHiresToMono[previousLoPixel | screenByte];
660 previousLoPixel = (screenByte << 2) & 0x0100;
662 pixels = previous3bits | (pixels << 14) | pixels2;
664 // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
665 // 0ppp 1111 1111 1111 1111 1111 1111 1111
666 // 31 27 23 19 15 11 7 3 0
668 if (screenType == ST_COLOR_TV)
670 for(uint8 i=0; i<7; i++)
672 uint8 bitPat = (pixels & 0x7F000000) >> 24;
675 for(uint8 j=0; j<4; j++)
677 uint8 color = blurTable[bitPat][j];
679 //This doesn't seem to make things go any faster...
680 //It's the OpenGL render that's faster... Hmm...
681 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
683 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
684 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
689 previous3bits = pixels & 0x70000000;
693 for(int j=0; j<28; j++)
695 scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
697 if (screenType == ST_GREEN_MONO)
698 pixels &= 0x07FFFFFF;
700 scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
708 void RenderVideoFrame(void)
712 RenderScreenBuffer();
717 // There's prolly more to it than this (like 80 column text), but this'll have to do for now...
718 Render40ColumnText();
727 Render40ColumnTextLine(20);
728 Render40ColumnTextLine(21);
729 Render40ColumnTextLine(22);
730 Render40ColumnTextLine(23);
735 Render40ColumnTextLine(20);
736 Render40ColumnTextLine(21);
737 Render40ColumnTextLine(22);
738 Render40ColumnTextLine(23);
756 RenderScreenBuffer();