]> Shamusworld >> Repos - apple2/blob - src/applevideo.cpp
Set eol-style to sane default.
[apple2] / src / applevideo.cpp
1 //
2 // Apple 2 video support
3 //
4 // All the video modes that a real Apple 2 supports are handled here
5 //
6 // by James L. Hammons
7 // (c) 2005 Underground Software
8 //
9 // JLH = James L. Hammons <jlhamm@acm.org>
10 //
11 // WHO  WHEN        WHAT
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
16 //
17 // STILL TO DO:
18 //
19 // - Fix LoRes mode green mono to skip every other scanline instead of fill
20 //   like white mono does
21 // - Double HiRes
22 // - 80 column text
23 // - Fix OSD text display so that it's visible no matter what background is there [DONE]
24 //
25
26 // Display routines seem MUCH slower now... !!! INVESTIGATE !!!
27
28 #include "applevideo.h"
29
30 #include <string.h>                                                             // for memset()
31 #include <stdarg.h>                                                             // for va_* stuff
32 #include <string>                                                               // for vsprintf()
33 #include "apple2.h"
34 #include "video.h"
35 #include "charset.h"
36 #include "font14pt.h"
37
38 /* Reference: Technote tn-iigs-063 "Master Color Values"
39
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)
59
60    LR: Lo-Res   HR: Hi-Res   DHR: Double Hi-Res */
61
62 // Global variables
63
64 bool flash;
65 bool textMode;
66 bool mixedMode;
67 bool displayPage2;
68 bool hiRes;
69 bool alternateCharset;
70 //void SpawnMessage(const char * text, ...);
71
72 // Local variables
73
74 // We set up the colors this way so that they'll be endian safe
75 // when we cast them to a uint32.
76
77 // "Master Color Values" palette
78
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
96 };
97
98 // This palette comes from ApplePC's colors (more realistic to my eye ;-)
99
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 };
117
118 // Lo-res starting line addresses
119
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 };
124
125 // Hi-res starting line addresses
126
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,
132
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,
137
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,
142
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,
147
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,
152
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 };
157
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
191
192         // Second half adds in the previous byte's lo pixel
193
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
226 };
227
228 static uint8 blurTable[0x800][8];                               // Color TV blur table
229 static uint32 * palette = (uint32 *)altColors;
230 enum { ST_FIRST_ENTRY = 0, ST_COLOR_TV = 0, ST_WHITE_MONO, ST_GREEN_MONO, ST_LAST_ENTRY };
231 static uint8 screenType = ST_COLOR_TV;
232
233 // Local functions
234
235 static void Render40ColumnTextLine(uint8 line);
236 static void Render40ColumnText(void);
237 static void RenderLoRes(uint16 toLine = 24);
238 static void RenderHiRes(uint16 toLine = 192);
239
240
241 void SetupBlurTable(void)
242 {
243         // NOTE: This table only needs to be 7 bits wide instead of 11, since the
244         //       last four bits are copies of the previous four...
245
246         for(uint16 bitPat=0; bitPat<0x800; bitPat++)
247         {
248                 uint16 w3 = bitPat & 0x888;
249                 uint16 w2 = bitPat & 0x444;
250                 uint16 w1 = bitPat & 0x222;
251                 uint16 w0 = bitPat & 0x111;
252
253                 uint16 blurred3 = (w3 | (w3 >> 1) | (w3 >> 2) | (w3 >> 3)) & 0x00FF;
254                 uint16 blurred2 = (w2 | (w2 >> 1) | (w2 >> 2) | (w2 >> 3)) & 0x00FF;
255                 uint16 blurred1 = (w1 | (w1 >> 1) | (w1 >> 2) | (w1 >> 3)) & 0x00FF;
256                 uint16 blurred0 = (w0 | (w0 >> 1) | (w0 >> 2) | (w0 >> 3)) & 0x00FF;
257
258                 for(int8 i=7; i>=0; i--)
259                 {
260                         uint8 color = (((blurred0 >> i) & 0x01) << 3)
261                                 | (((blurred1 >> i) & 0x01) << 2)
262                                 | (((blurred2 >> i) & 0x01) << 1)
263                                 | ((blurred3 >> i) & 0x01);
264                         blurTable[bitPat][7 - i] = color;
265                 }
266         }
267 }
268
269 void TogglePalette(void)
270 {
271         if (palette == (uint32 *)colors)
272         {
273                 palette = (uint32 *)altColors;
274                 SpawnMessage("Color TV palette");
275         }
276         else
277         {
278                 palette = (uint32 *)colors;
279                 SpawnMessage("\"Master Color Values\" palette");
280         }
281 }
282
283 void CycleScreenTypes(void)
284 {
285         char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
286
287         screenType++;
288
289         if (screenType == ST_LAST_ENTRY)
290                 screenType = ST_FIRST_ENTRY;
291
292         SpawnMessage("%s", scrTypeStr[screenType]);
293 }
294
295 static uint32 msgTicks = 0;
296 static char message[4096];
297
298 void SpawnMessage(const char * text, ...)
299 {
300         va_list arg;
301
302         va_start(arg, text);
303         vsprintf(message, text, arg);
304         va_end(arg);
305
306         msgTicks = 120;
307 }
308
309 static void DrawString2(uint32 x, uint32 y, uint32 color);
310 static void DrawString(void)
311 {
312 //This approach works, and seems to be fast enough... Though it probably would
313 //be better to make the oversized font to match this one...
314         for(uint32 x=7; x<=9; x++)
315                 for(uint32 y=7; y<=9; y++)
316                         DrawString2(x, y, 0x00000000);
317
318         DrawString2(8, 8, 0x0020FF20);
319 }
320
321 static void DrawString2(uint32 x, uint32 y, uint32 color)
322 {
323 //uint32 x = 8, y = 8;
324         uint32 length = strlen(message), address = x + (y * VIRTUAL_SCREEN_WIDTH);
325 //      uint32 color = 0x0020FF20;
326 //This could be done ahead of time, instead of on each pixel...
327 //(Now it is!)
328         uint8 nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
329
330         for(uint32 i=0; i<length; i++)
331         {
332                 uint8 c = message[i];
333                 c = (c < 32 ? 0 : c - 32);
334                 uint32 fontAddr = (uint32)c * FONT_WIDTH * FONT_HEIGHT;
335
336                 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
337                 {
338                         for(uint32 xx=0; xx<FONT_WIDTH; xx++)
339                         {
340 /*                              uint8 fontTrans = font1[fontAddr++];
341 //                              uint32 newTrans = (fontTrans * transparency / 255) << 24;
342                                 uint32 newTrans = fontTrans << 24;
343                                 uint32 pixel = newTrans | color;
344
345                                 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = pixel;//*/
346
347                                 uint8 trans = font1[fontAddr++];
348
349                                 if (trans)
350                                 {
351                                         uint32 existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
352
353                                         uint8 eBlue = (existingColor >> 16) & 0xFF,
354                                                 eGreen = (existingColor >> 8) & 0xFF,
355                                                 eRed = existingColor & 0xFF;
356
357 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
358 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
359 //because dividing by 32 is faster than dividing by 31...!
360                                         uint8 invTrans = 255 - trans;
361
362                                         uint32 bRed   = (eRed   * invTrans + nRed   * trans) / 255;
363                                         uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
364                                         uint32 bBlue  = (eBlue  * invTrans + nBlue  * trans) / 255;
365
366 //THIS IS NOT ENDIAN SAFE
367                                         *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
368                                 }
369                         }
370                 }
371
372                 address += FONT_WIDTH;
373         }
374 }
375
376 static void Render40ColumnTextLine(uint8 line)
377 {
378         uint32 pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
379
380         for(int x=0; x<40; x++)
381         {
382                 uint8 chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
383
384                 // Render character at (x, y)
385
386                 for(int cy=0; cy<8; cy++)
387                 {
388                         for(int cx=0; cx<7; cx++)
389                         {
390                                 uint32 pixel = 0xFF000000;
391
392                                 if (!alternateCharset)
393                                 {
394                                         if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
395 //                                              pixel = 0xFFFFFFFF;
396                                                 pixel = pixelOn;
397
398                                         if (chr < 0x80)
399                                                 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
400
401                                         if ((chr & 0xC0) == 0x40 && flash)
402                                                 pixel = 0xFF000000;
403                                 }
404                                 else
405                                 {
406                                         if (textChar2e[(chr * 56) + cx + (cy * 7)])
407 //                                              pixel = 0xFFFFFFFF;
408                                                 pixel = pixelOn;
409                                 }
410
411 //                              scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
412 //                              scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
413                                 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
414                                 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
415
416                                 if (screenType == ST_GREEN_MONO)
417                                         pixel = 0xFF000000;
418
419                                 {
420                                 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
421                                 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
422                                 }
423                         }
424                 }
425         }
426 }
427
428 static void Render40ColumnText(void)
429 {
430         for(uint8 line=0; line<24; line++)
431                 Render40ColumnTextLine(line);
432 }
433
434 static void RenderLoRes(uint16 toLine/*= 24*/)
435 {
436 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
437 /*
438 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
439 Color #s correspond to the bit patterns in reverse... Interesting!
440
441 00 00 00 ->  0 [0000] -> 0 (lores color #)
442 3c 4d 00 ->  8 [0001] -> 8?             BROWN
443 00 5d 3c ->  4 [0010] -> 4?             DARK GREEN
444 3c aa 3c -> 12 [0011] -> 12?    LIGHT GREEN (GREEN)
445 41 30 7d ->  2 [0100] -> 2?             DARK BLUE
446 7d 7d 7d -> 10 [0101] -> 10?    LIGHT GRAY
447 41 8e ba ->  6 [0110] -> 6?             MEDIUM BLUE (BLUE)
448 7d db ba -> 14 [0111] -> 14?    AQUAMARINE (AQUA)
449 7d 20 41 ->  1 [1000] -> 1?             DEEP RED (MAGENTA)
450 ba 6d 41 ->  9 [1001] -> 9?             ORANGE
451 7d 7d 7d ->  5 [1010] -> 5?             DARK GRAY
452 ba cb 7d -> 13 [1011] -> 13?    YELLOW
453 be 51 be ->  3 [1100] -> 3              PURPLE (VIOLET)
454 fb 9e be -> 11 [1101] -> 11?    PINK
455 be ae fb ->  7 [1110] -> 7?             LIGHT BLUE (CYAN)
456 fb fb fb -> 15 [1111] -> 15             WHITE
457 */
458         uint8 mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
459
460 //This is the old "perfect monitor" rendering code...
461 /*      if (screenType != ST_COLOR_TV) // Not correct, but for now...
462 //if (1)
463         {
464                 for(uint16 y=0; y<toLine; y++)
465                 {
466                         for(uint16 x=0; x<40; x++)
467                         {
468                                 uint8 scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
469                                 uint32 pixel = palette[scrByte & 0x0F];
470         
471                                 for(int cy=0; cy<4; cy++)
472                                         for(int cx=0; cx<14; cx++)
473                                                 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
474         
475                                 pixel = palette[scrByte >> 4];
476         
477                                 for(int cy=4; cy<8; cy++)
478                                         for(int cx=0; cx<14; cx++)
479                                                 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
480                         }
481                 }
482         }
483         else//*/
484
485         uint32 pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
486
487         for(uint16 y=0; y<toLine; y++)
488         {
489                 // Do top half of lores screen bytes...
490
491                 uint32 previous3Bits = 0;
492
493                 for(uint16 x=0; x<40; x+=2)
494                 {
495                         uint8 scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
496                         uint8 scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
497                         scrByte1 = mirrorNybble[scrByte1];
498                         scrByte2 = mirrorNybble[scrByte2];
499                         // This is just a guess, but it'll have to do for now...
500                         uint32 pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
501                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
502                                 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
503
504                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
505                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
506                         // 31   27   23   19   15    11   7    3  0
507
508                         if (screenType == ST_COLOR_TV)
509                         {
510                                 for(uint8 i=0; i<7; i++)
511                                 {
512                                         uint16 bitPat = (pixels & 0x7F000000) >> 20;
513                                         pixels <<= 4;
514
515                                         for(uint8 j=0; j<4; j++)
516                                         {
517                                                 uint8 color = blurTable[bitPat][j];
518
519                                                 for(uint32 cy=0; cy<8; cy++)
520                                                 {
521                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
522 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
523                                                 }
524                                         }
525                                 }
526
527                                 previous3Bits = pixels & 0x70000000;
528                         }
529                         else
530                         {
531                                 for(int j=0; j<28; j++)
532                                 {
533                                         for(uint32 cy=0; cy<8; cy++)
534                                         {
535                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
536 //                                              scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
537                                         }
538
539                                         pixels <<= 1;
540                                 }
541                         }
542                 }
543
544                 // Now do bottom half...
545
546                 previous3Bits = 0;
547
548                 for(uint16 x=0; x<40; x+=2)
549                 {
550                         uint8 scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
551                         uint8 scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
552                         scrByte1 = mirrorNybble[scrByte1];
553                         scrByte2 = mirrorNybble[scrByte2];
554                         // This is just a guess, but it'll have to do for now...
555                         uint32 pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
556                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
557                                 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
558
559                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
560                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
561                         // 31   27   23   19   15    11   7    3  0
562
563                         if (screenType == ST_COLOR_TV)
564                         {
565                                 for(uint8 i=0; i<7; i++)
566                                 {
567                                         uint16 bitPat = (pixels & 0x7F000000) >> 20;
568                                         pixels <<= 4;
569
570                                         for(uint8 j=0; j<4; j++)
571                                         {
572                                                 uint8 color = blurTable[bitPat][j];
573
574                                                 for(uint32 cy=8; cy<16; cy++)
575                                                 {
576                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
577 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
578                                                 }
579                                         }
580                                 }
581
582                                 previous3Bits = pixels & 0x70000000;
583                         }
584                         else
585                         {
586                                 for(int j=0; j<28; j++)
587                                 {
588                                         for(uint32 cy=8; cy<16; cy++)
589                                         {
590                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
591 //                                              scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
592                                         }
593
594                                         pixels <<= 1;
595                                 }
596                         }
597                 }
598         }
599 }
600
601 static void RenderHiRes(uint16 toLine/*= 192*/)
602 {
603         uint32 pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
604
605         for(uint16 y=0; y<toLine; y++)
606         {
607                 uint16 previousLoPixel = 0;
608                 uint32 previous3bits = 0;
609
610                 for(uint16 x=0; x<40; x+=2)
611                 {
612                         uint8 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
613                         uint32 pixels = appleHiresToMono[previousLoPixel | screenByte];
614                         previousLoPixel = (screenByte << 2) & 0x0100;
615
616                         screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
617                         uint32 pixels2 = appleHiresToMono[previousLoPixel | screenByte];
618                         previousLoPixel = (screenByte << 2) & 0x0100;
619
620                         pixels = previous3bits | (pixels << 14) | pixels2;
621
622                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
623                         // 0ppp 1111 1111 1111 1111 1111 1111 1111
624                         // 31   27   23   19   15   11   7    3  0
625
626                         if (screenType == ST_COLOR_TV)
627                         {
628                                 for(uint8 i=0; i<7; i++)
629                                 {
630                                         uint16 bitPat = (pixels & 0x7F000000) >> 20;
631                                         pixels <<= 4;
632
633                                         for(uint8 j=0; j<4; j++)
634                                         {
635                                                 uint8 color = blurTable[bitPat][j];
636 #if 0
637 //This doesn't seem to make things go any faster...
638 //It's the OpenGL render that's faster... Hmm...
639                                                 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
640 #else
641                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
642                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
643 #endif
644                                         }
645                                 }
646
647                                 previous3bits = pixels & 0x70000000;
648                         }
649                         else
650                         {
651                                 for(int j=0; j<28; j++)
652                                 {
653                                         scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
654
655                                         if (screenType == ST_GREEN_MONO)
656                                                 pixels &= 0x07FFFFFF;
657
658                                         scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
659                                         pixels <<= 1;
660                                 }
661                         }
662                 }
663         }
664 }
665
666 void RenderVideoFrame(void)
667 {
668 //temp...
669 /*RenderLoRes();
670 RenderScreenBuffer();
671 return;//*/
672
673         if (textMode)
674         {
675                 // There's prolly more to it than this (like 80 column text), but this'll have to do for now...
676                 Render40ColumnText();
677         }
678         else
679         {
680                 if (mixedMode)
681                 {
682                         if (hiRes)
683                         {
684                                 RenderHiRes(160);
685                                 Render40ColumnTextLine(20);
686                                 Render40ColumnTextLine(21);
687                                 Render40ColumnTextLine(22);
688                                 Render40ColumnTextLine(23);
689                         }
690                         else
691                         {
692                                 RenderLoRes(20);
693                                 Render40ColumnTextLine(20);
694                                 Render40ColumnTextLine(21);
695                                 Render40ColumnTextLine(22);
696                                 Render40ColumnTextLine(23);
697                         }
698                 }
699                 else
700                 {
701                         if (hiRes)
702                                 RenderHiRes();
703                         else
704                                 RenderLoRes();
705                 }
706         }
707
708         if (msgTicks)
709         {
710                 DrawString();
711                 msgTicks--;
712         }
713
714         RenderScreenBuffer();
715 }