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