]> Shamusworld >> Repos - apple2/blob - src/applevideo.cpp
Disk names now display in window, still lots to do.
[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 // NOTE: Not endian safe. !!! FIX !!! [DONE]
728 #if 0
729         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? 0xFFFFFFFF : 0xFF61FF61);
730 #else
731 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
732 // The colors are set in the 8-bit array as R G B A
733         uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
734         uint32_t * colorPtr = (uint32_t *)monoColors;
735         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
736 #endif
737
738         for(uint16_t y=0; y<toLine; y++)
739         {
740                 uint16_t previousLoPixel = 0;
741                 uint32_t previous3bits = 0;
742
743                 for(uint16_t x=0; x<40; x+=2)
744                 {
745                         uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
746                         uint32_t pixels = appleHiresToMono[previousLoPixel | screenByte];
747                         previousLoPixel = (screenByte << 2) & 0x0100;
748
749                         screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
750                         uint32_t pixels2 = appleHiresToMono[previousLoPixel | screenByte];
751                         previousLoPixel = (screenByte << 2) & 0x0100;
752
753                         pixels = previous3bits | (pixels << 14) | pixels2;
754
755                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
756                         // 0ppp 1111 1111 1111 1111 1111 1111 1111
757                         // 31   27   23   19   15   11   7    3  0
758
759                         if (screenType == ST_COLOR_TV)
760                         {
761                                 for(uint8_t i=0; i<7; i++)
762                                 {
763                                         uint8_t bitPat = (pixels & 0x7F000000) >> 24;
764                                         pixels <<= 4;
765
766                                         for(uint8_t j=0; j<4; j++)
767                                         {
768                                                 uint8_t color = blurTable[bitPat][j];
769 #if 0
770 //This doesn't seem to make things go any faster...
771 //It's the OpenGL render that's faster... Hmm...
772                                                 scrBuffer[(x * 14) + (i * 4) + j + (y * VIRTUAL_SCREEN_WIDTH)] = palette[color];
773 #else
774                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
775                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = palette[color];
776 #endif
777                                         }
778                                 }
779
780                                 previous3bits = pixels & 0x70000000;
781                         }
782                         else
783                         {
784                                 for(int j=0; j<28; j++)
785                                 {
786                                         scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
787
788                                         if (screenType == ST_GREEN_MONO)
789                                                 pixels &= 0x07FFFFFF;
790
791                                         scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
792                                         pixels <<= 1;
793                                 }
794                         }
795                 }
796         }
797 }
798
799
800 static void RenderDHiRes(uint16_t toLine/*= 192*/)
801 {
802 // Now it is. Now roll this fix into all the other places... !!! FIX !!!
803 // The colors are set in the 8-bit array as R G B A
804         uint8_t monoColors[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0x61, 0xFF };
805         uint32_t * colorPtr = (uint32_t *)monoColors;
806         uint32_t pixelOn = (screenType == ST_WHITE_MONO ? colorPtr[0] : colorPtr[1]);
807
808         for(uint16_t y=0; y<toLine; y++)
809         {
810                 uint32_t previous4bits = 0;
811
812                 for(uint16_t x=0; x<40; x+=2)
813                 {
814                         uint8_t screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
815                         uint32_t pixels = (mirrorTable[screenByte & 0x7F]) << 14;
816                         screenByte = ram[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
817                         pixels = pixels | (mirrorTable[screenByte & 0x7F]);
818                         screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x];
819                         pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 21);
820                         screenByte = ram2[lineAddrHiRes[y] + (displayPage2 ? 0x2000 : 0x0000) + x + 1];
821                         pixels = pixels | ((mirrorTable[screenByte & 0x7F]) << 7);
822                         pixels = previous4bits | (pixels >> 1);
823
824                         // We now have 28 pixels (expanded from 14) in word: mask is $0F FF FF FF
825                         // 0ppp 1111 1111 1111 1111 1111 1111 1111
826                         // 31   27   23   19   15   11   7    3  0
827
828                         if (screenType == ST_COLOR_TV)
829                         {
830                                 for(uint8_t i=0; i<7; i++)
831                                 {
832                                         uint8_t bitPat = (pixels & 0xFE000000) >> 25;
833                                         pixels <<= 4;
834
835                                         for(uint8_t j=0; j<4; j++)
836                                         {
837                                                 uint32_t color = palette[blurTable[bitPat][j]];
838                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = color;
839                                                 scrBuffer[(x * 14) + (i * 4) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = color;
840                                         }
841                                 }
842
843                                 previous4bits = pixels & 0xF0000000;
844                         }
845                         else
846                         {
847                                 for(int j=0; j<28; j++)
848                                 {
849                                         scrBuffer[(x * 14) + j + (((y * 2) + 0) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
850
851                                         if (screenType == ST_GREEN_MONO)
852                                                 pixels &= 0x07FFFFFF;
853
854                                         scrBuffer[(x * 14) + j + (((y * 2) + 1) * VIRTUAL_SCREEN_WIDTH)] = (pixels & 0x08000000 ? pixelOn : 0xFF000000);
855                                         pixels <<= 1;
856                                 }
857                         }
858                 }
859         }
860 }
861
862
863 void RenderVideoFrame(void)
864 {
865         if (textMode)
866         {
867                 if (!col80Mode)
868                         Render40ColumnText();
869                 else
870                         Render80ColumnText();
871         }
872         else
873         {
874                 if (mixedMode)
875                 {
876                         if (hiRes)
877                         {
878                                 RenderHiRes(160);
879                                 Render40ColumnTextLine(20);
880                                 Render40ColumnTextLine(21);
881                                 Render40ColumnTextLine(22);
882                                 Render40ColumnTextLine(23);
883                         }
884                         else
885                         {
886                                 RenderLoRes(20);
887                                 Render40ColumnTextLine(20);
888                                 Render40ColumnTextLine(21);
889                                 Render40ColumnTextLine(22);
890                                 Render40ColumnTextLine(23);
891                         }
892                 }
893                 else
894                 {
895                         if (dhires)
896                                 RenderDHiRes();
897                         else if (hiRes)
898                                 RenderHiRes();
899                         else
900                                 RenderLoRes();
901                 }
902         }
903
904         if (msgTicks)
905         {
906                 DrawString();
907                 msgTicks--;
908         }
909 }
910