]> Shamusworld >> Repos - apple2/blob - src/applevideo.cpp
86eeeda3899919cc47c2fb9e947036f340ad1c2b
[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. Note that the format is RGBA.
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 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;
233
234 // Local functions
235
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);
240
241
242 void SetupBlurTable(void)
243 {
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.
249 #if 0
250 //      for(uint16 bitPat=0; bitPat<0x800; bitPat++)
251         for(uint16 bitPat=0; bitPat<0x80; bitPat++)
252         {
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;
261
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;
266
267                 for(int8 i=7; i>=0; i--)
268                 {
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;
274                 }
275         }
276 #else
277         for(uint16 bitPat=0; bitPat<0x800; bitPat+=0x10)
278         {
279                 uint16 w0 = bitPat & 0x111, w1 = bitPat & 0x222, w2 = bitPat & 0x444, w3 = bitPat & 0x888;
280
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;
285
286                 for(int8 i=7; i>=0; i--)
287                 {
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;
293                 }
294         }
295 #endif
296 }
297
298 void TogglePalette(void)
299 {
300         if (palette == (uint32 *)colors)
301         {
302                 palette = (uint32 *)altColors;
303                 SpawnMessage("Color TV palette");
304         }
305         else
306         {
307                 palette = (uint32 *)colors;
308                 SpawnMessage("\"Master Color Values\" palette");
309         }
310 }
311
312 void CycleScreenTypes(void)
313 {
314         char scrTypeStr[3][40] = { "Color TV", "White monochrome", "Green monochrome" };
315
316         screenType++;
317
318         if (screenType == ST_LAST_ENTRY)
319                 screenType = ST_FIRST_ENTRY;
320
321         SpawnMessage("%s", scrTypeStr[screenType]);
322 }
323
324 static uint32 msgTicks = 0;
325 static char message[4096];
326
327 void SpawnMessage(const char * text, ...)
328 {
329         va_list arg;
330
331         va_start(arg, text);
332         vsprintf(message, text, arg);
333         va_end(arg);
334
335         msgTicks = 120;
336 }
337
338 static void DrawString2(uint32 x, uint32 y, uint32 color);
339 static void DrawString(void)
340 {
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);
346
347         DrawString2(8, 8, 0x0020FF20);
348 }
349
350 static void DrawString2(uint32 x, uint32 y, uint32 color)
351 {
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...
356 //(Now it is!)
357         uint8 nBlue = (color >> 16) & 0xFF, nGreen = (color >> 8) & 0xFF, nRed = color & 0xFF;
358
359         for(uint32 i=0; i<length; i++)
360         {
361                 uint8 c = message[i];
362                 c = (c < 32 ? 0 : c - 32);
363                 uint32 fontAddr = (uint32)c * FONT_WIDTH * FONT_HEIGHT;
364
365                 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
366                 {
367                         for(uint32 xx=0; xx<FONT_WIDTH; xx++)
368                         {
369 /*                              uint8 fontTrans = font1[fontAddr++];
370 //                              uint32 newTrans = (fontTrans * transparency / 255) << 24;
371                                 uint32 newTrans = fontTrans << 24;
372                                 uint32 pixel = newTrans | color;
373
374                                 *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = pixel;//*/
375
376                                 uint8 trans = font1[fontAddr++];
377
378                                 if (trans)
379                                 {
380                                         uint32 existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
381
382                                         uint8 eBlue = (existingColor >> 16) & 0xFF,
383                                                 eGreen = (existingColor >> 8) & 0xFF,
384                                                 eRed = existingColor & 0xFF;
385
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;
390
391                                         uint32 bRed   = (eRed   * invTrans + nRed   * trans) / 255;
392                                         uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
393                                         uint32 bBlue  = (eBlue  * invTrans + nBlue  * trans) / 255;
394
395 //THIS IS NOT ENDIAN SAFE
396                                         *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
397                                 }
398                         }
399                 }
400
401                 address += FONT_WIDTH;
402         }
403 }
404
405 static void Render40ColumnTextLine(uint8 line)
406 {
407         uint32 pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
408
409         for(int x=0; x<40; x++)
410         {
411                 uint8 chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
412
413                 // Render character at (x, y)
414
415                 for(int cy=0; cy<8; cy++)
416                 {
417                         for(int cx=0; cx<7; cx++)
418                         {
419                                 uint32 pixel = 0xFF000000;
420
421                                 if (!alternateCharset)
422                                 {
423                                         if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
424 //                                              pixel = 0xFFFFFFFF;
425                                                 pixel = pixelOn;
426
427                                         if (chr < 0x80)
428                                                 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
429
430                                         if ((chr & 0xC0) == 0x40 && flash)
431                                                 pixel = 0xFF000000;
432                                 }
433                                 else
434                                 {
435                                         if (textChar2e[(chr * 56) + cx + (cy * 7)])
436 //                                              pixel = 0xFFFFFFFF;
437                                                 pixel = pixelOn;
438                                 }
439
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;
444
445                                 // QnD method to get blank alternate lines in text mode
446                                 if (screenType == ST_GREEN_MONO)
447                                         pixel = 0xFF000000;
448
449                                 {
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;
452                                 }
453                         }
454                 }
455         }
456 }
457
458 static void Render40ColumnText(void)
459 {
460         for(uint8 line=0; line<24; line++)
461                 Render40ColumnTextLine(line);
462 }
463
464 static void RenderLoRes(uint16 toLine/*= 24*/)
465 {
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.
470 /*
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!
473
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
490 */
491         uint8 mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
492
493 //This is the old "perfect monitor" rendering code...
494 /*      if (screenType != ST_COLOR_TV) // Not correct, but for now...
495 //if (1)
496         {
497                 for(uint16 y=0; y<toLine; y++)
498                 {
499                         for(uint16 x=0; x<40; x++)
500                         {
501                                 uint8 scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
502                                 uint32 pixel = palette[scrByte & 0x0F];
503         
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;
507         
508                                 pixel = palette[scrByte >> 4];
509         
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;
513                         }
514                 }
515         }
516         else//*/
517
518         uint32 pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
519
520         for(uint16 y=0; y<toLine; y++)
521         {
522                 // Do top half of lores screen bytes...
523
524                 uint32 previous3Bits = 0;
525
526                 for(uint16 x=0; x<40; x+=2)
527                 {
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;
536
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
540
541                         if (screenType == ST_COLOR_TV)
542                         {
543                                 for(uint8 i=0; i<7; i++)
544                                 {
545                                         uint8 bitPat = (pixels & 0x7F000000) >> 24;
546                                         pixels <<= 4;
547
548                                         for(uint8 j=0; j<4; j++)
549                                         {
550                                                 uint8 color = blurTable[bitPat][j];
551
552                                                 for(uint32 cy=0; cy<8; cy++)
553                                                 {
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];
556                                                 }
557                                         }
558                                 }
559
560                                 previous3Bits = pixels & 0x70000000;
561                         }
562                         else
563                         {
564                                 for(int j=0; j<28; j++)
565                                 {
566                                         for(uint32 cy=0; cy<8; cy++)
567                                         {
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);
570                                         }
571
572                                         pixels <<= 1;
573                                 }
574                         }
575                 }
576
577                 // Now do bottom half...
578
579                 previous3Bits = 0;
580
581                 for(uint16 x=0; x<40; x+=2)
582                 {
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;
591
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
595
596                         if (screenType == ST_COLOR_TV)
597                         {
598                                 for(uint8 i=0; i<7; i++)
599                                 {
600                                         uint8 bitPat = (pixels & 0x7F000000) >> 24;
601                                         pixels <<= 4;
602
603                                         for(uint8 j=0; j<4; j++)
604                                         {
605                                                 uint8 color = blurTable[bitPat][j];
606
607                                                 for(uint32 cy=8; cy<16; cy++)
608                                                 {
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];
611                                                 }
612                                         }
613                                 }
614
615                                 previous3Bits = pixels & 0x70000000;
616                         }
617                         else
618                         {
619                                 for(int j=0; j<28; j++)
620                                 {
621                                         for(uint32 cy=8; cy<16; cy++)
622                                         {
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);
625                                         }
626
627                                         pixels <<= 1;
628                                 }
629                         }
630                 }
631         }
632 }
633
634 static void RenderHiRes(uint16 toLine/*= 192*/)
635 {
636 // NOTE: Not endian safe. !!! FIX !!!
637 #if 0
638         uint32 pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
639 #else
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]);
645 #endif
646
647         for(uint16 y=0; y<toLine; y++)
648         {
649                 uint16 previousLoPixel = 0;
650                 uint32 previous3bits = 0;
651
652                 for(uint16 x=0; x<40; x+=2)
653                 {
654                         uint8 screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
655                         uint32 pixels = appleHiresToMono[previousLoPixel | screenByte];
656                         previousLoPixel = (screenByte << 2) & 0x0100;
657
658                         screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
659                         uint32 pixels2 = appleHiresToMono[previousLoPixel | screenByte];
660                         previousLoPixel = (screenByte << 2) & 0x0100;
661
662                         pixels = previous3bits | (pixels << 14) | pixels2;
663
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
667
668                         if (screenType == ST_COLOR_TV)
669                         {
670                                 for(uint8 i=0; i<7; i++)
671                                 {
672                                         uint8 bitPat = (pixels & 0x7F000000) >> 24;
673                                         pixels <<= 4;
674
675                                         for(uint8 j=0; j<4; j++)
676                                         {
677                                                 uint8 color = blurTable[bitPat][j];
678 #if 0
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];
682 #else
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];
685 #endif
686                                         }
687                                 }
688
689                                 previous3bits = pixels & 0x70000000;
690                         }
691                         else
692                         {
693                                 for(int j=0; j<28; j++)
694                                 {
695                                         scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
696
697                                         if (screenType == ST_GREEN_MONO)
698                                                 pixels &= 0x07FFFFFF;
699
700                                         scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
701                                         pixels <<= 1;
702                                 }
703                         }
704                 }
705         }
706 }
707
708 void RenderVideoFrame(void)
709 {
710 //temp...
711 /*RenderLoRes();
712 RenderScreenBuffer();
713 return;//*/
714
715         if (textMode)
716         {
717                 // There's prolly more to it than this (like 80 column text), but this'll have to do for now...
718                 Render40ColumnText();
719         }
720         else
721         {
722                 if (mixedMode)
723                 {
724                         if (hiRes)
725                         {
726                                 RenderHiRes(160);
727                                 Render40ColumnTextLine(20);
728                                 Render40ColumnTextLine(21);
729                                 Render40ColumnTextLine(22);
730                                 Render40ColumnTextLine(23);
731                         }
732                         else
733                         {
734                                 RenderLoRes(20);
735                                 Render40ColumnTextLine(20);
736                                 Render40ColumnTextLine(21);
737                                 Render40ColumnTextLine(22);
738                                 Render40ColumnTextLine(23);
739                         }
740                 }
741                 else
742                 {
743                         if (hiRes)
744                                 RenderHiRes();
745                         else
746                                 RenderLoRes();
747                 }
748         }
749
750         if (msgTicks)
751         {
752                 DrawString();
753                 msgTicks--;
754         }
755
756         RenderScreenBuffer();
757 }