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