]> Shamusworld >> Repos - apple2/blob - src/applevideo.cpp
807253b1d267644d8a98dd0921f69a56dc95d918
[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 = 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
401                                 if (trans)
402                                 {
403                                         uint32_t existingColor = *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH));
404
405                                         uint8_t eBlue = (existingColor >> 16) & 0xFF,
406                                                 eGreen = (existingColor >> 8) & 0xFF,
407                                                 eRed = existingColor & 0xFF;
408
409 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
410 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
411 //because dividing by 32 is faster than dividing by 31...!
412                                         uint8_t invTrans = 255 - trans;
413
414                                         uint32_t bRed   = (eRed   * invTrans + nRed   * trans) / 255;
415                                         uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
416                                         uint32_t bBlue  = (eBlue  * invTrans + nBlue  * trans) / 255;
417
418 //THIS IS NOT ENDIAN SAFE
419 //NB: Setting the alpha channel here does nothing.
420                                         *(scrBuffer + address + xx + (yy * VIRTUAL_SCREEN_WIDTH)) = 0x7F000000 | (bBlue << 16) | (bGreen << 8) | bRed;
421                                 }
422                         }
423                 }
424
425                 address += FONT_WIDTH;
426         }
427 }
428
429
430 static void Render40ColumnTextLine(uint8_t line)
431 {
432         uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
433
434         for(int x=0; x<40; x++)
435         {
436                 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
437
438                 // Render character at (x, y)
439
440                 for(int cy=0; cy<8; cy++)
441                 {
442                         for(int cx=0; cx<7; cx++)
443                         {
444                                 uint32_t pixel = 0xFF000000;
445
446                                 if (alternateCharset)
447                                 {
448                                         if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
449                                                 pixel = pixelOn;
450
451                                         if (chr < 0x80)
452                                                 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
453
454                                         if ((chr & 0xC0) == 0x40 && flash)
455                                                 pixel = 0xFF000000;
456                                 }
457                                 else
458                                 {
459                                         if (textChar2e[(chr * 56) + cx + (cy * 7)])
460                                                 pixel = pixelOn;
461                                 }
462
463                                 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
464                                 scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (cy * VIRTUAL_SCREEN_WIDTH * 2)] = pixel;
465
466                                 // QnD method to get blank alternate lines in text mode
467                                 if (screenType == ST_GREEN_MONO)
468                                         pixel = 0xFF000000;
469
470                                 {
471                                         scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 0 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
472                                         scrBuffer[(x * 7 * 2) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + (cx * 2) + 1 + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
473                                 }
474                         }
475                 }
476         }
477 }
478
479
480 static void Render80ColumnTextLine(uint8_t line)
481 {
482         uint32_t pixelOn = (screenType == ST_GREEN_MONO ? 0xFF61FF61 : 0xFFFFFFFF);
483
484         for(int x=0; x<80; x++)
485         {
486 #if 0
487 // This is wrong; it should grab from the alt bank if Page2 is set, not main RAM @ $0
488                 uint8_t chr = ram[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x];
489
490                 if (x > 39)
491                         chr = ram2[lineAddrLoRes[line] + (displayPage2 ? 0x0400 : 0x0000) + x - 40];
492 #else
493                 uint8_t chr;
494
495                 if (x & 0x01)
496                         chr = ram[lineAddrLoRes[line] + (x >> 1)];
497                 else
498                         chr = ram2[lineAddrLoRes[line] + (x >> 1)];     
499 #endif
500
501                 // Render character at (x, y)
502
503                 for(int cy=0; cy<8; cy++)
504                 {
505                         for(int cx=0; cx<7; cx++)
506                         {
507                                 uint32_t pixel = 0xFF000000;
508
509                                 if (alternateCharset)
510                                 {
511                                         if (textChar[((chr & 0x3F) * 56) + cx + (cy * 7)])
512                                                 pixel = pixelOn;
513
514                                         if (chr < 0x80)
515                                                 pixel = pixel ^ (screenType == ST_GREEN_MONO ? 0x0061FF61 : 0x00FFFFFF);
516
517                                         if ((chr & 0xC0) == 0x40 && flash)
518                                                 pixel = 0xFF000000;
519                                 }
520                                 else
521                                 {
522                                         if (textChar2e[(chr * 56) + cx + (cy * 7)])
523                                                 pixel = pixelOn;
524                                 }
525
526                                 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (cy * 2 * VIRTUAL_SCREEN_WIDTH)] = pixel;
527
528                                 // QnD method to get blank alternate lines in text mode
529                                 if (screenType == ST_GREEN_MONO)
530                                         pixel = 0xFF000000;
531
532                                 scrBuffer[(x * 7) + (line * VIRTUAL_SCREEN_WIDTH * 8 * 2) + cx + (((cy * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = pixel;
533                         }
534                 }
535         }
536 }
537
538
539 static void Render40ColumnText(void)
540 {
541         for(uint8_t line=0; line<24; line++)
542                 Render40ColumnTextLine(line);
543 }
544
545
546 static void Render80ColumnText(void)
547 {
548         for(uint8_t line=0; line<24; line++)
549                 Render80ColumnTextLine(line);
550 }
551
552
553 static void RenderLoRes(uint16_t toLine/*= 24*/)
554 {
555 // NOTE: The green mono rendering doesn't skip every other line... !!! FIX !!!
556 //       Also, we could set up three different Render functions depending on which
557 //       render type was set and call it with a function pointer. Would be faster
558 //       then the nested ifs we have now.
559 /*
560 Note that these colors correspond to the bit patterns generated by the numbers 0-F in order:
561 Color #s correspond to the bit patterns in reverse... Interesting!
562
563 00 00 00 ->  0 [0000] -> 0 (lores color #)
564 3c 4d 00 ->  8 [0001] -> 8?             BROWN
565 00 5d 3c ->  4 [0010] -> 4?             DARK GREEN
566 3c aa 3c -> 12 [0011] -> 12?    LIGHT GREEN (GREEN)
567 41 30 7d ->  2 [0100] -> 2?             DARK BLUE
568 7d 7d 7d -> 10 [0101] -> 10?    LIGHT GRAY
569 41 8e ba ->  6 [0110] -> 6?             MEDIUM BLUE (BLUE)
570 7d db ba -> 14 [0111] -> 14?    AQUAMARINE (AQUA)
571 7d 20 41 ->  1 [1000] -> 1?             DEEP RED (MAGENTA)
572 ba 6d 41 ->  9 [1001] -> 9?             ORANGE
573 7d 7d 7d ->  5 [1010] -> 5?             DARK GRAY
574 ba cb 7d -> 13 [1011] -> 13?    YELLOW
575 be 51 be ->  3 [1100] -> 3              PURPLE (VIOLET)
576 fb 9e be -> 11 [1101] -> 11?    PINK
577 be ae fb ->  7 [1110] -> 7?             LIGHT BLUE (CYAN)
578 fb fb fb -> 15 [1111] -> 15             WHITE
579 */
580         uint8_t mirrorNybble[16] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
581
582 //This is the old "perfect monitor" rendering code...
583 /*      if (screenType != ST_COLOR_TV) // Not correct, but for now...
584 //if (1)
585         {
586                 for(uint16_t y=0; y<toLine; y++)
587                 {
588                         for(uint16_t x=0; x<40; x++)
589                         {
590                                 uint8_t scrByte = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x];
591                                 uint32_t pixel = palette[scrByte & 0x0F];
592
593                                 for(int cy=0; cy<4; cy++)
594                                         for(int cx=0; cx<14; cx++)
595                                                 scrBuffer[((x * 14) + cx) + (((y * 8) + cy) * VIRTUAL_SCREEN_WIDTH)] = pixel;
596
597                                 pixel = palette[scrByte >> 4];
598
599                                 for(int cy=4; cy<8; cy++)
600                                         for(int cx=0; cx<14; cx++)
601                                                 scrBuffer[(x * 14) + (y * VIRTUAL_SCREEN_WIDTH * 8) + cx + (cy * VIRTUAL_SCREEN_WIDTH)] = pixel;
602                         }
603                 }
604         }
605         else//*/
606
607         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
608
609         for(uint16_t y=0; y<toLine; y++)
610         {
611                 // Do top half of lores screen bytes...
612
613                 uint32_t previous3Bits = 0;
614
615                 for(uint16_t x=0; x<40; x+=2)
616                 {
617                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] & 0x0F;
618                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] & 0x0F;
619                         scrByte1 = mirrorNybble[scrByte1];
620                         scrByte2 = mirrorNybble[scrByte2];
621                         // This is just a guess, but it'll have to do for now...
622                         uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
623                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
624                                 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
625
626                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
627                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
628                         // 31   27   23   19   15    11   7    3  0
629
630                         if (screenType == ST_COLOR_TV)
631                         {
632                                 for(uint8_t i=0; i<7; i++)
633                                 {
634                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
635                                         pixels <<= 4;
636
637                                         for(uint8_t j=0; j<4; j++)
638                                         {
639                                                 uint8_t color = blurTable[bitPat][j];
640
641                                                 for(uint32_t cy=0; cy<8; cy++)
642                                                 {
643                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
644 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
645                                                 }
646                                         }
647                                 }
648
649                                 previous3Bits = pixels & 0x70000000;
650                         }
651                         else
652                         {
653                                 for(int j=0; j<28; j++)
654                                 {
655                                         for(uint32_t cy=0; cy<8; cy++)
656                                         {
657                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
658 //                                              scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
659                                         }
660
661                                         pixels <<= 1;
662                                 }
663                         }
664                 }
665
666                 // Now do bottom half...
667
668                 previous3Bits = 0;
669
670                 for(uint16_t x=0; x<40; x+=2)
671                 {
672                         uint8_t scrByte1 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 0] >> 4;
673                         uint8_t scrByte2 = ram[lineAddrLoRes[y] + (displayPage2 ? 0x0400 : 0x0000) + x + 1] >> 4;
674                         scrByte1 = mirrorNybble[scrByte1];
675                         scrByte2 = mirrorNybble[scrByte2];
676                         // This is just a guess, but it'll have to do for now...
677                         uint32_t pixels = previous3Bits | (scrByte1 << 24) | (scrByte1 << 20) | (scrByte1 << 16)
678                                 | ((scrByte1 & 0x0C) << 12) | ((scrByte2 & 0x03) << 12)
679                                 | (scrByte2 << 8) | (scrByte2 << 4) | scrByte2;
680
681                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
682                         // 0ppp 1111 1111 1111 11|11 1111 1111 1111
683                         // 31   27   23   19   15    11   7    3  0
684
685                         if (screenType == ST_COLOR_TV)
686                         {
687                                 for(uint8_t i=0; i<7; i++)
688                                 {
689                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
690                                         pixels <<= 4;
691
692                                         for(uint8_t j=0; j<4; j++)
693                                         {
694                                                 uint8_t color = blurTable[bitPat][j];
695
696                                                 for(uint32_t cy=8; cy<16; cy++)
697                                                 {
698                                                         scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
699 //                                                      scrBuffer[((x * 14) + (i * 4) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
700                                                 }
701                                         }
702                                 }
703
704                                 previous3Bits = pixels & 0x70000000;
705                         }
706                         else
707                         {
708                                 for(int j=0; j<28; j++)
709                                 {
710                                         for(uint32_t cy=8; cy<16; cy++)
711                                         {
712                                                 scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
713 //                                              scrBuffer[((x * 14) + j) + (((y * 16) + cy) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
714                                         }
715
716                                         pixels <<= 1;
717                                 }
718                         }
719                 }
720         }
721 }
722
723
724 static void RenderHiRes(uint16_t toLine/*= 192*/)
725 {
726 // NOTE: Not endian safe. !!! FIX !!! [DONE]
727 #if 0
728         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
729 #else
730 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
731 // The colors are set in the 8-bit array as R G B A
732         uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
733         uint32_t * colorPtr = (uint32_t *)monoColors;
734         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
735 #endif
736
737         for(uint16_t y=0; y<toLine; y++)
738         {
739                 uint16_t previousLoPixel = 0;
740                 uint32_t previous3bits = 0;
741
742                 for(uint16_t x=0; x<40; x+=2)
743                 {
744                         uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
745                         uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
746                         previousLoPixel = (screenByte << 2) & 0x0100;
747
748                         screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
749                         uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
750                         previousLoPixel = (screenByte << 2) & 0x0100;
751
752                         pixels = previous3bits | (pixels << 14) | pixels2;
753
754                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
755                         // 0ppp 1111 1111 1111 1111 1111 1111 1111
756                         // 31   27   23   19   15   11   7    3  0
757
758                         if (screenType == ST_COLOR_TV)
759                         {
760                                 for(uint8_t i=0; i<7; i++)
761                                 {
762                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
763                                         pixels <<= 4;
764
765                                         for(uint8_t j=0; j<4; j++)
766                                         {
767                                                 uint8_t color = blurTable[bitPat][j];
768 #if 0
769 //This doesn't seem to make things go any faster...
770 //It's the OpenGL render that's faster... Hmm...
771                                                 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
772 #else
773                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
774                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
775 #endif
776                                         }
777                                 }
778
779                                 previous3bits = pixels & 0x70000000;
780                         }
781                         else
782                         {
783                                 for(int j=0; j<28; j++)
784                                 {
785                                         scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
786
787                                         if (screenType == ST_GREEN_MONO)
788                                                 pixels &= 0x07FFFFFF;
789
790                                         scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
791                                         pixels <<= 1;
792                                 }
793                         }
794                 }
795         }
796 }
797
798
799 static void RenderDHiRes(uint16_t toLine/*= 192*/)
800 {
801 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
802 // The colors are set in the 8-bit array as R G B A
803         uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
804         uint32_t * colorPtr = (uint32_t *)monoColors;
805         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
806
807         for(uint16_t y=0; y<toLine; y++)
808         {
809                 uint32_t previous4bits = 0;
810
811                 for(uint16_t x=0; x<40; x+=2)
812                 {
813                         uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
814                         uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
815                         screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
816                         pixels = pixels | (mirrorTable[screenByte & 0x7F]);
817                         screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
818                         pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
819                         screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
820                         pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
821                         pixels = previous4bits | (pixels >> 1);
822
823                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
824                         // 0ppp 1111 1111 1111 1111 1111 1111 1111
825                         // 31   27   23   19   15   11   7    3  0
826
827                         if (screenType == ST_COLOR_TV)
828                         {
829                                 for(uint8_t i=0; i<7; i++)
830                                 {
831                                         uint8_t bitPat = (pixels & 0xFE000000) >> 25;
832                                         pixels <<= 4;
833
834                                         for(uint8_t j=0; j<4; j++)
835                                         {
836                                                 uint32_t color = palette[blurTable[bitPat][j]];
837                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
838                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
839                                         }
840                                 }
841
842                                 previous4bits = pixels & 0xF0000000;
843                         }
844                         else
845                         {
846                                 for(int j=0; j<28; j++)
847                                 {
848                                         scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
849
850                                         if (screenType == ST_GREEN_MONO)
851                                                 pixels &= 0x07FFFFFF;
852
853                                         scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
854                                         pixels <<= 1;
855                                 }
856                         }
857                 }
858         }
859 }
860
861
862 void RenderVideoFrame(void)
863 {
864 //temp...
865 /*RenderLoRes();
866 RenderScreenBuffer();
867 return;//*/
868
869         if (textMode)
870         {
871                 // There's prolly more to it than this (like 80 column text), but this'll have to do for now...
872                 if (!col80Mode)
873                         Render40ColumnText();
874                 else
875                         Render80ColumnText();
876         }
877         else
878         {
879                 if (mixedMode)
880                 {
881                         if (hiRes)
882                         {
883                                 RenderHiRes(160);
884                                 Render40ColumnTextLine(20);
885                                 Render40ColumnTextLine(21);
886                                 Render40ColumnTextLine(22);
887                                 Render40ColumnTextLine(23);
888                         }
889                         else
890                         {
891                                 RenderLoRes(20);
892                                 Render40ColumnTextLine(20);
893                                 Render40ColumnTextLine(21);
894                                 Render40ColumnTextLine(22);
895                                 Render40ColumnTextLine(23);
896                         }
897                 }
898                 else
899                 {
900                         if (dhires)
901                                 RenderDHiRes();
902                         else if (hiRes)
903                                 RenderHiRes();
904                         else
905                                 RenderLoRes();
906                 }
907         }
908
909         if (msgTicks)
910         {
911                 DrawString();
912                 msgTicks--;
913         }
914
915         RenderScreenBuffer();
916 }
917