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