[NTVDM]
[reactos.git] / subsystems / ntvdm / vga.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: vga.c
5 * PURPOSE: VGA hardware emulation
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "emulator.h"
14 #include "vga.h"
15
16 #include "io.h"
17 #include "bios.h"
18
19 /* PRIVATE VARIABLES **********************************************************/
20
21 static CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
22 static CONST DWORD MemoryLimit[] = { 0xAFFFF, 0xAFFFF, 0xB7FFF, 0xBFFFF };
23
24 static CONST COLORREF VgaDefaultPalette[VGA_MAX_COLORS] =
25 {
26 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA),
27 RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA),
28 RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF),
29 RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF),
30 RGB(0x00, 0x00, 0x00), RGB(0x10, 0x10, 0x10), RGB(0x20, 0x20, 0x20), RGB(0x35, 0x35, 0x35),
31 RGB(0x45, 0x45, 0x45), RGB(0x55, 0x55, 0x55), RGB(0x65, 0x65, 0x65), RGB(0x75, 0x75, 0x75),
32 RGB(0x8A, 0x8A, 0x8A), RGB(0x9A, 0x9A, 0x9A), RGB(0xAA, 0xAA, 0xAA), RGB(0xBA, 0xBA, 0xBA),
33 RGB(0xCA, 0xCA, 0xCA), RGB(0xDF, 0xDF, 0xDF), RGB(0xEF, 0xEF, 0xEF), RGB(0xFF, 0xFF, 0xFF),
34 RGB(0x00, 0x00, 0xFF), RGB(0x41, 0x00, 0xFF), RGB(0x82, 0x00, 0xFF), RGB(0xBE, 0x00, 0xFF),
35 RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0x00, 0xBE), RGB(0xFF, 0x00, 0x82), RGB(0xFF, 0x00, 0x41),
36 RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x41, 0x00), RGB(0xFF, 0x82, 0x00), RGB(0xFF, 0xBE, 0x00),
37 RGB(0xFF, 0xFF, 0x00), RGB(0xBE, 0xFF, 0x00), RGB(0x82, 0xFF, 0x00), RGB(0x41, 0xFF, 0x00),
38 RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0x41), RGB(0x00, 0xFF, 0x82), RGB(0x00, 0xFF, 0xBE),
39 RGB(0x00, 0xFF, 0xFF), RGB(0x00, 0xBE, 0xFF), RGB(0x00, 0x82, 0xFF), RGB(0x00, 0x41, 0xFF),
40 RGB(0x82, 0x82, 0xFF), RGB(0x9E, 0x82, 0xFF), RGB(0xBE, 0x82, 0xFF), RGB(0xDF, 0x82, 0xFF),
41 RGB(0xFF, 0x82, 0xFF), RGB(0xFF, 0x82, 0xDF), RGB(0xFF, 0x82, 0xBE), RGB(0xFF, 0x82, 0x9E),
42 RGB(0xFF, 0x82, 0x82), RGB(0xFF, 0x9E, 0x82), RGB(0xFF, 0xBE, 0x82), RGB(0xFF, 0xDF, 0x82),
43 RGB(0xFF, 0xFF, 0x82), RGB(0xDF, 0xFF, 0x82), RGB(0xBE, 0xFF, 0x82), RGB(0x9E, 0xFF, 0x82),
44 RGB(0x82, 0xFF, 0x82), RGB(0x82, 0xFF, 0x9E), RGB(0x82, 0xFF, 0xBE), RGB(0x82, 0xFF, 0xDF),
45 RGB(0x82, 0xFF, 0xFF), RGB(0x82, 0xDF, 0xFF), RGB(0x82, 0xBE, 0xFF), RGB(0x82, 0x9E, 0xFF),
46 RGB(0xBA, 0xBA, 0xFF), RGB(0xCA, 0xBA, 0xFF), RGB(0xDF, 0xBA, 0xFF), RGB(0xEF, 0xBA, 0xFF),
47 RGB(0xFF, 0xBA, 0xFF), RGB(0xFF, 0xBA, 0xEF), RGB(0xFF, 0xBA, 0xDF), RGB(0xFF, 0xBA, 0xCA),
48 RGB(0xFF, 0xBA, 0xBA), RGB(0xFF, 0xCA, 0xBA), RGB(0xFF, 0xDF, 0xBA), RGB(0xFF, 0xEF, 0xBA),
49 RGB(0xFF, 0xFF, 0xBA), RGB(0xEF, 0xFF, 0xBA), RGB(0xDF, 0xFF, 0xBA), RGB(0xCA, 0xFF, 0xBA),
50 RGB(0xBA, 0xFF, 0xBA), RGB(0xBA, 0xFF, 0xCA), RGB(0xBA, 0xFF, 0xDF), RGB(0xBA, 0xFF, 0xEF),
51 RGB(0xBA, 0xFF, 0xFF), RGB(0xBA, 0xEF, 0xFF), RGB(0xBA, 0xDF, 0xFF), RGB(0xBA, 0xCA, 0xFF),
52 RGB(0x00, 0x00, 0x71), RGB(0x1C, 0x00, 0x71), RGB(0x39, 0x00, 0x71), RGB(0x55, 0x00, 0x71),
53 RGB(0x71, 0x00, 0x71), RGB(0x71, 0x00, 0x55), RGB(0x71, 0x00, 0x39), RGB(0x71, 0x00, 0x1C),
54 RGB(0x71, 0x00, 0x00), RGB(0x71, 0x1C, 0x00), RGB(0x71, 0x39, 0x00), RGB(0x71, 0x55, 0x00),
55 RGB(0x71, 0x71, 0x00), RGB(0x55, 0x71, 0x00), RGB(0x39, 0x71, 0x00), RGB(0x1C, 0x71, 0x00),
56 RGB(0x00, 0x71, 0x00), RGB(0x00, 0x71, 0x1C), RGB(0x00, 0x71, 0x39), RGB(0x00, 0x71, 0x55),
57 RGB(0x00, 0x71, 0x71), RGB(0x00, 0x55, 0x71), RGB(0x00, 0x39, 0x71), RGB(0x00, 0x1C, 0x71),
58 RGB(0x39, 0x39, 0x71), RGB(0x45, 0x39, 0x71), RGB(0x55, 0x39, 0x71), RGB(0x61, 0x39, 0x71),
59 RGB(0x71, 0x39, 0x71), RGB(0x71, 0x39, 0x61), RGB(0x71, 0x39, 0x55), RGB(0x71, 0x39, 0x45),
60 RGB(0x71, 0x39, 0x39), RGB(0x71, 0x45, 0x39), RGB(0x71, 0x55, 0x39), RGB(0x71, 0x61, 0x39),
61 RGB(0x71, 0x71, 0x39), RGB(0x61, 0x71, 0x39), RGB(0x55, 0x71, 0x39), RGB(0x45, 0x71, 0x39),
62 RGB(0x39, 0x71, 0x39), RGB(0x39, 0x71, 0x45), RGB(0x39, 0x71, 0x55), RGB(0x39, 0x71, 0x61),
63 RGB(0x39, 0x71, 0x71), RGB(0x39, 0x61, 0x71), RGB(0x39, 0x55, 0x71), RGB(0x39, 0x45, 0x71),
64 RGB(0x51, 0x51, 0x71), RGB(0x59, 0x51, 0x71), RGB(0x61, 0x51, 0x71), RGB(0x69, 0x51, 0x71),
65 RGB(0x71, 0x51, 0x71), RGB(0x71, 0x51, 0x69), RGB(0x71, 0x51, 0x61), RGB(0x71, 0x51, 0x59),
66 RGB(0x71, 0x51, 0x51), RGB(0x71, 0x59, 0x51), RGB(0x71, 0x61, 0x51), RGB(0x71, 0x69, 0x51),
67 RGB(0x71, 0x71, 0x51), RGB(0x69, 0x71, 0x51), RGB(0x61, 0x71, 0x51), RGB(0x59, 0x71, 0x51),
68 RGB(0x51, 0x71, 0x51), RGB(0x51, 0x71, 0x59), RGB(0x51, 0x71, 0x61), RGB(0x51, 0x71, 0x69),
69 RGB(0x51, 0x71, 0x71), RGB(0x51, 0x69, 0x71), RGB(0x51, 0x61, 0x71), RGB(0x51, 0x59, 0x71),
70 RGB(0x00, 0x00, 0x41), RGB(0x10, 0x00, 0x41), RGB(0x20, 0x00, 0x41), RGB(0x31, 0x00, 0x41),
71 RGB(0x41, 0x00, 0x41), RGB(0x41, 0x00, 0x31), RGB(0x41, 0x00, 0x20), RGB(0x41, 0x00, 0x10),
72 RGB(0x41, 0x00, 0x00), RGB(0x41, 0x10, 0x00), RGB(0x41, 0x20, 0x00), RGB(0x41, 0x31, 0x00),
73 RGB(0x41, 0x41, 0x00), RGB(0x31, 0x41, 0x00), RGB(0x20, 0x41, 0x00), RGB(0x10, 0x41, 0x00),
74 RGB(0x00, 0x41, 0x00), RGB(0x00, 0x41, 0x10), RGB(0x00, 0x41, 0x20), RGB(0x00, 0x41, 0x31),
75 RGB(0x00, 0x41, 0x41), RGB(0x00, 0x31, 0x41), RGB(0x00, 0x20, 0x41), RGB(0x00, 0x10, 0x41),
76 RGB(0x20, 0x20, 0x41), RGB(0x28, 0x20, 0x41), RGB(0x31, 0x20, 0x41), RGB(0x39, 0x20, 0x41),
77 RGB(0x41, 0x20, 0x41), RGB(0x41, 0x20, 0x39), RGB(0x41, 0x20, 0x31), RGB(0x41, 0x20, 0x28),
78 RGB(0x41, 0x20, 0x20), RGB(0x41, 0x28, 0x20), RGB(0x41, 0x31, 0x20), RGB(0x41, 0x39, 0x20),
79 RGB(0x41, 0x41, 0x20), RGB(0x39, 0x41, 0x20), RGB(0x31, 0x41, 0x20), RGB(0x28, 0x41, 0x20),
80 RGB(0x20, 0x41, 0x20), RGB(0x20, 0x41, 0x28), RGB(0x20, 0x41, 0x31), RGB(0x20, 0x41, 0x39),
81 RGB(0x20, 0x41, 0x41), RGB(0x20, 0x39, 0x41), RGB(0x20, 0x31, 0x41), RGB(0x20, 0x28, 0x41),
82 RGB(0x2D, 0x2D, 0x41), RGB(0x31, 0x2D, 0x41), RGB(0x35, 0x2D, 0x41), RGB(0x3D, 0x2D, 0x41),
83 RGB(0x41, 0x2D, 0x41), RGB(0x41, 0x2D, 0x3D), RGB(0x41, 0x2D, 0x35), RGB(0x41, 0x2D, 0x31),
84 RGB(0x41, 0x2D, 0x2D), RGB(0x41, 0x31, 0x2D), RGB(0x41, 0x35, 0x2D), RGB(0x41, 0x3D, 0x2D),
85 RGB(0x41, 0x41, 0x2D), RGB(0x3D, 0x41, 0x2D), RGB(0x35, 0x41, 0x2D), RGB(0x31, 0x41, 0x2D),
86 RGB(0x2D, 0x41, 0x2D), RGB(0x2D, 0x41, 0x31), RGB(0x2D, 0x41, 0x35), RGB(0x2D, 0x41, 0x3D),
87 RGB(0x2D, 0x41, 0x41), RGB(0x2D, 0x3D, 0x41), RGB(0x2D, 0x35, 0x41), RGB(0x2D, 0x31, 0x41),
88 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00),
89 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00)
90 };
91
92 static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
93 static LPVOID ConsoleFramebuffer = NULL;
94 static HANDLE TextConsoleBuffer = NULL;
95 static HANDLE GraphicsConsoleBuffer = NULL;
96 static HANDLE ConsoleMutex = NULL;
97 static HPALETTE PaletteHandle = NULL;
98 static BOOLEAN DoubleVision = FALSE;
99
100 static BYTE VgaLatchRegisters[VGA_NUM_BANKS] = {0, 0, 0, 0};
101 static BYTE VgaMiscRegister;
102
103 static BYTE VgaSeqIndex = VGA_SEQ_RESET_REG;
104 static BYTE VgaSeqRegisters[VGA_SEQ_MAX_REG];
105
106 static BYTE VgaGcIndex = VGA_GC_RESET_REG;
107 static BYTE VgaGcRegisters[VGA_GC_MAX_REG];
108
109 static BYTE VgaCrtcIndex = VGA_CRTC_HORZ_TOTAL_REG;
110 static BYTE VgaCrtcRegisters[VGA_CRTC_MAX_REG];
111
112 static BYTE VgaAcIndex = VGA_AC_PAL_0_REG;
113 static BOOLEAN VgaAcLatch = FALSE;
114 static BYTE VgaAcRegisters[VGA_AC_MAX_REG];
115
116 static WORD VgaDacIndex = 0;
117 static BOOLEAN VgaDacReadWrite = FALSE;
118 static BYTE VgaDacRegisters[VGA_PALETTE_SIZE];
119
120 static BOOLEAN InVerticalRetrace = FALSE;
121 static BOOLEAN InHorizontalRetrace = FALSE;
122
123 static BOOLEAN NeedsUpdate = FALSE;
124 static BOOLEAN ModeChanged = TRUE;
125 static BOOLEAN CursorMoved = FALSE;
126 static BOOLEAN PaletteChanged = FALSE;
127 static BOOLEAN TextMode = TRUE;
128
129 static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
130
131 /* PRIVATE FUNCTIONS **********************************************************/
132
133 static inline INT VgaGetAddressSize(VOID)
134 {
135 if (VgaCrtcRegisters[VGA_CRTC_UNDERLINE_REG] & VGA_CRTC_UNDERLINE_DWORD)
136 {
137 /* Double-word addressing */
138 return 4; // sizeof(DWORD)
139 }
140
141 if (VgaCrtcRegisters[VGA_CRTC_MODE_CONTROL_REG] & VGA_CRTC_MODE_CONTROL_BYTE)
142 {
143 /* Byte addressing */
144 return 1; // sizeof(BYTE)
145 }
146
147 /* Word addressing */
148 return 2; // sizeof(WORD)
149 }
150
151 static inline DWORD VgaTranslateReadAddress(DWORD Address)
152 {
153 DWORD Offset = Address - VgaGetVideoBaseAddress();
154 BYTE Plane;
155
156 /* Check for chain-4 and odd-even mode */
157 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
158 {
159 /* The lowest two bits are the plane number */
160 Plane = Offset & 3;
161 Offset >>= 2;
162 }
163 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
164 {
165 /* The LSB is the plane number */
166 Plane = Offset & 1;
167 Offset >>= 1;
168 }
169 else
170 {
171 /* Use the read mode */
172 Plane = VgaGcRegisters[VGA_GC_READ_MAP_SEL_REG] & 0x03;
173 }
174
175 /* Multiply the offset by the address size */
176 Offset *= VgaGetAddressSize();
177
178 return Offset + Plane * VGA_BANK_SIZE;
179 }
180
181 static inline DWORD VgaTranslateWriteAddress(DWORD Address)
182 {
183 DWORD Offset = Address - VgaGetVideoBaseAddress();
184
185 /* Check for chain-4 and odd-even mode */
186 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
187 {
188 /* Shift the offset to the right by 2 */
189 Offset >>= 2;
190 }
191 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
192 {
193 /* Shift the offset to the right by 1 */
194 Offset >>= 1;
195 }
196
197 /* Multiply the offset by the address size */
198 Offset *= VgaGetAddressSize();
199
200 /* Return the offset on plane 0 */
201 return Offset;
202 }
203
204 static inline BYTE VgaTranslateByteForWriting(BYTE Data, BYTE Plane)
205 {
206 BYTE WriteMode = VgaGcRegisters[VGA_GC_MODE_REG] & 3;
207 BYTE LogicalOperation = (VgaGcRegisters[VGA_GC_ROTATE_REG] >> 3) & 3;
208 BYTE RotateCount = VgaGcRegisters[VGA_GC_ROTATE_REG] & 7;
209 BYTE BitMask = VgaGcRegisters[VGA_GC_BITMASK_REG];
210
211 if (WriteMode == 1)
212 {
213 /* In write mode 1 just return the latch register */
214 return VgaLatchRegisters[Plane];
215 }
216
217 if (WriteMode != 2)
218 {
219 /* Write modes 0 and 3 rotate the data to the right first */
220 Data = LOBYTE(((DWORD)Data >> RotateCount) | ((DWORD)Data << (8 - RotateCount)));
221 }
222 else
223 {
224 /* Write mode 2 expands the appropriate bit to all 8 bits */
225 Data = (Data & (1 << Plane)) ? 0xFF : 0x00;
226 }
227
228 if (WriteMode == 0)
229 {
230 /*
231 * In write mode 0, the enable set/reset register decides if the
232 * set/reset bit should be expanded to all 8 bits.
233 */
234 if (VgaGcRegisters[VGA_GC_ENABLE_RESET_REG] & (1 << Plane))
235 {
236 /* Copy the bit from the set/reset register to all 8 bits */
237 Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00;
238 }
239 }
240
241 if (WriteMode != 3)
242 {
243 /* Write modes 0 and 2 then perform a logical operation on the data and latch */
244 if (LogicalOperation == 1) Data &= VgaLatchRegisters[Plane];
245 else if (LogicalOperation == 2) Data |= VgaLatchRegisters[Plane];
246 else if (LogicalOperation == 3) Data ^= VgaLatchRegisters[Plane];
247 }
248 else
249 {
250 /* For write mode 3, we AND the bitmask with the data, which is used as the new bitmask */
251 BitMask &= Data;
252
253 /* Then we expand the bit in the set/reset field */
254 Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00;
255 }
256
257 /* Bits cleared in the bitmask are replaced with latch register bits */
258 Data = (Data & BitMask) | (VgaLatchRegisters[Plane] & (~BitMask));
259
260 /* Return the byte */
261 return Data;
262 }
263
264 static inline VOID VgaMarkForUpdate(SHORT Row, SHORT Column)
265 {
266 DPRINT("VgaMarkForUpdate: Row %d, Column %d\n", Row, Column);
267
268 /* Check if this is the first time the rectangle is updated */
269 if (!NeedsUpdate)
270 {
271 UpdateRectangle.Left = UpdateRectangle.Top = MAXSHORT;
272 UpdateRectangle.Right = UpdateRectangle.Bottom = MINSHORT;
273 }
274
275 /* Expand the rectangle to include the point */
276 UpdateRectangle.Left = min(UpdateRectangle.Left, Column);
277 UpdateRectangle.Right = max(UpdateRectangle.Right, Column);
278 UpdateRectangle.Top = min(UpdateRectangle.Top, Row);
279 UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Row);
280
281 /* Set the update request flag */
282 NeedsUpdate = TRUE;
283 }
284
285 static VOID VgaWriteSequencer(BYTE Data)
286 {
287 ASSERT(VgaSeqIndex < VGA_SEQ_MAX_REG);
288
289 /* Save the value */
290 VgaSeqRegisters[VgaSeqIndex] = Data;
291 }
292
293 static VOID VgaWriteGc(BYTE Data)
294 {
295 ASSERT(VgaGcIndex < VGA_GC_MAX_REG);
296
297 /* Save the value */
298 VgaGcRegisters[VgaGcIndex] = Data;
299
300 /* Check the index */
301 switch (VgaGcIndex)
302 {
303 case VGA_GC_MISC_REG:
304 {
305 /* The GC misc register decides if it's text or graphics mode */
306 ModeChanged = TRUE;
307
308 break;
309 }
310 }
311 }
312
313 static VOID VgaWriteCrtc(BYTE Data)
314 {
315 ASSERT(VgaGcIndex < VGA_CRTC_MAX_REG);
316
317 /* Save the value */
318 VgaCrtcRegisters[VgaCrtcIndex] = Data;
319
320 /* Check the index */
321 switch (VgaCrtcIndex)
322 {
323 case VGA_CRTC_END_HORZ_DISP_REG:
324 case VGA_CRTC_VERT_DISP_END_REG:
325 case VGA_CRTC_OVERFLOW_REG:
326 {
327 /* The video mode has changed */
328 ModeChanged = TRUE;
329
330 break;
331 }
332
333 case VGA_CRTC_CURSOR_LOC_LOW_REG:
334 case VGA_CRTC_CURSOR_LOC_HIGH_REG:
335 case VGA_CRTC_CURSOR_START_REG:
336 case VGA_CRTC_CURSOR_END_REG:
337 {
338 /* Set the cursor moved flag */
339 CursorMoved = TRUE;
340
341 break;
342 }
343 }
344 }
345
346 static VOID VgaWriteDac(BYTE Data)
347 {
348 INT PaletteIndex;
349 PALETTEENTRY Entry;
350
351 /* Set the value */
352 VgaDacRegisters[VgaDacIndex] = Data;
353
354 /* Find the palette index */
355 PaletteIndex = VgaDacIndex / 3;
356
357 /* Fill the entry structure */
358 Entry.peRed = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3]);
359 Entry.peGreen = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3 + 1]);
360 Entry.peBlue = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3 + 2]);
361 Entry.peFlags = 0;
362
363 /* Update the palette entry */
364 SetPaletteEntries(PaletteHandle, PaletteIndex, 1, &Entry);
365
366 /* Set the palette change flag */
367 PaletteChanged = TRUE;
368
369 /* Update the index */
370 VgaDacIndex++;
371 VgaDacIndex %= VGA_PALETTE_SIZE;
372 }
373
374 static VOID VgaWriteAc(BYTE Data)
375 {
376 ASSERT(VgaAcIndex < VGA_AC_MAX_REG);
377
378 /* Save the value */
379 VgaAcRegisters[VgaAcIndex] = Data;
380
381 if (VgaAcIndex <= VGA_AC_PAL_F_REG)
382 {
383 /* Set the palette change flag */
384 PaletteChanged = TRUE;
385 }
386 }
387
388 static VOID VgaRestoreDefaultPalette(PPALETTEENTRY Entries, USHORT NumOfEntries)
389 {
390 USHORT i;
391
392 /* Copy the colors of the default palette to the DAC and console palette */
393 for (i = 0; i < NumOfEntries; i++)
394 {
395 /* Set the palette entries */
396 Entries[i].peRed = GetRValue(VgaDefaultPalette[i]);
397 Entries[i].peGreen = GetGValue(VgaDefaultPalette[i]);
398 Entries[i].peBlue = GetBValue(VgaDefaultPalette[i]);
399 Entries[i].peFlags = 0;
400
401 /* Set the DAC registers */
402 VgaDacRegisters[i * 3] = VGA_COLOR_TO_DAC(GetRValue(VgaDefaultPalette[i]));
403 VgaDacRegisters[i * 3 + 1] = VGA_COLOR_TO_DAC(GetGValue(VgaDefaultPalette[i]));
404 VgaDacRegisters[i * 3 + 2] = VGA_COLOR_TO_DAC(GetBValue(VgaDefaultPalette[i]));
405 }
406 }
407
408 static BOOLEAN VgaInitializePalette(VOID)
409 {
410 LPLOGPALETTE Palette;
411
412 /* Allocate storage space for the palette */
413 Palette = (LPLOGPALETTE)HeapAlloc(GetProcessHeap(),
414 HEAP_ZERO_MEMORY,
415 sizeof(LOGPALETTE) +
416 VGA_MAX_COLORS * sizeof(PALETTEENTRY));
417 if (Palette == NULL) return FALSE;
418
419 /* Initialize the palette */
420 Palette->palVersion = 0x0300;
421 Palette->palNumEntries = VGA_MAX_COLORS;
422
423 /* Restore the default palette */
424 VgaRestoreDefaultPalette(Palette->palPalEntry, Palette->palNumEntries);
425
426 /* Create the palette */
427 PaletteHandle = CreatePalette(Palette);
428
429 /* Free the palette */
430 HeapFree(GetProcessHeap(), 0, Palette);
431
432 /* Fail if the palette wasn't successfully created... */
433 if (PaletteHandle == NULL) return FALSE;
434
435 /* ... otherwise return success */
436 return TRUE;
437 }
438
439 static BOOL VgaEnterGraphicsMode(PCOORD Resolution)
440 {
441 DWORD i;
442 CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
443 BYTE BitmapInfoBuffer[VGA_BITMAP_INFO_SIZE];
444 LPBITMAPINFO BitmapInfo = (LPBITMAPINFO)BitmapInfoBuffer;
445 LPWORD PaletteIndex = (LPWORD)(BitmapInfo->bmiColors);
446
447 if ((Resolution->X < VGA_MINIMUM_WIDTH) && (Resolution->Y < VGA_MINIMUM_HEIGHT))
448 {
449 DoubleVision = TRUE;
450 Resolution->X *= 2;
451 Resolution->Y *= 2;
452 }
453
454 /* Fill the bitmap info header */
455 ZeroMemory(&BitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER));
456 BitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
457 BitmapInfo->bmiHeader.biWidth = Resolution->X;
458 BitmapInfo->bmiHeader.biHeight = Resolution->Y;
459 BitmapInfo->bmiHeader.biBitCount = 8;
460 BitmapInfo->bmiHeader.biPlanes = 1;
461 BitmapInfo->bmiHeader.biCompression = BI_RGB;
462 BitmapInfo->bmiHeader.biSizeImage = Resolution->X * Resolution->Y /* * 1 == biBitCount / 8 */;
463
464 /* Fill the palette data */
465 for (i = 0; i < (VGA_PALETTE_SIZE / 3); i++) PaletteIndex[i] = (WORD)i;
466
467 /* Fill the console graphics buffer info */
468 GraphicsBufferInfo.dwBitMapInfoLength = VGA_BITMAP_INFO_SIZE;
469 GraphicsBufferInfo.lpBitMapInfo = BitmapInfo;
470 GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS;
471
472 /* Create the buffer */
473 GraphicsConsoleBuffer = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
474 FILE_SHARE_READ | FILE_SHARE_WRITE,
475 NULL,
476 CONSOLE_GRAPHICS_BUFFER,
477 &GraphicsBufferInfo);
478 if (GraphicsConsoleBuffer == INVALID_HANDLE_VALUE) return FALSE;
479
480 /* Save the framebuffer address and mutex */
481 ConsoleFramebuffer = GraphicsBufferInfo.lpBitMap;
482 ConsoleMutex = GraphicsBufferInfo.hMutex;
483
484 /* Clear the framebuffer */
485 ZeroMemory(ConsoleFramebuffer, BitmapInfo->bmiHeader.biSizeImage);
486
487 /* Set the active buffer */
488 SetConsoleActiveScreenBuffer(GraphicsConsoleBuffer);
489
490 /* Set the graphics mode palette */
491 SetConsolePalette(GraphicsConsoleBuffer,
492 PaletteHandle,
493 SYSPAL_NOSTATIC256);
494
495 /* Clear the text mode flag */
496 TextMode = FALSE;
497
498 return TRUE;
499 }
500
501 static VOID VgaLeaveGraphicsMode(VOID)
502 {
503 /* Release the console framebuffer mutex if needed */
504 ReleaseMutex(ConsoleMutex);
505
506 /* Switch back to the text buffer */
507 SetConsoleActiveScreenBuffer(TextConsoleBuffer);
508
509 /* Cleanup the video data */
510 CloseHandle(ConsoleMutex);
511 ConsoleMutex = NULL;
512 CloseHandle(GraphicsConsoleBuffer);
513 GraphicsConsoleBuffer = NULL;
514 DoubleVision = FALSE;
515 }
516
517 static BOOL VgaEnterTextMode(PCOORD Resolution)
518 {
519 /* Resize the console */
520 SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
521
522 /* Allocate a framebuffer */
523 ConsoleFramebuffer = HeapAlloc(GetProcessHeap(),
524 HEAP_ZERO_MEMORY,
525 Resolution->X * Resolution->Y
526 * sizeof(CHAR_INFO));
527 if (ConsoleFramebuffer == NULL)
528 {
529 DisplayMessage(L"An unexpected error occurred!\n");
530 VdmRunning = FALSE;
531 return FALSE;
532 }
533
534 /*
535 * Set the text mode palette.
536 *
537 * WARNING: This call should fail on Windows (and therefore
538 * we get the default palette and our external behaviour is
539 * just like Windows' one), but it should success on ReactOS
540 * (so that we get console palette changes even for text-mode
541 * screen-buffers, which is a new feature on ReactOS).
542 */
543 SetConsolePalette(TextConsoleBuffer,
544 PaletteHandle,
545 SYSPAL_NOSTATIC256);
546
547 /* Set the text mode flag */
548 TextMode = TRUE;
549
550 return TRUE;
551 }
552
553 static VOID VgaLeaveTextMode(VOID)
554 {
555 /* Free the old framebuffer */
556 HeapFree(GetProcessHeap(), 0, ConsoleFramebuffer);
557 ConsoleFramebuffer = NULL;
558 }
559
560 static VOID VgaChangeMode(VOID)
561 {
562 COORD Resolution = VgaGetDisplayResolution();
563
564 /* Reset the mode change flag */
565 // ModeChanged = FALSE;
566
567 if (!TextMode)
568 {
569 /* Leave the current graphics mode */
570 VgaLeaveGraphicsMode();
571 }
572 else
573 {
574 /* Leave the current text mode */
575 VgaLeaveTextMode();
576 }
577
578 /* Check if the new mode is alphanumeric */
579 if (!(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA))
580 {
581 /* Enter new text mode */
582 if (!VgaEnterTextMode(&Resolution))
583 {
584 DisplayMessage(L"An unexpected VGA error occurred while switching into text mode.");
585 VdmRunning = FALSE;
586 return;
587 }
588 }
589 else
590 {
591 /* Enter 8-bit graphics mode */
592 if (!VgaEnterGraphicsMode(&Resolution))
593 {
594 DisplayMessage(L"An unexpected VGA error occurred while switching into graphics mode.");
595 VdmRunning = FALSE;
596 return;
597 }
598 }
599
600 /* Trigger a full update of the screen */
601 NeedsUpdate = TRUE;
602 UpdateRectangle.Left = 0;
603 UpdateRectangle.Top = 0;
604 UpdateRectangle.Right = Resolution.X;
605 UpdateRectangle.Bottom = Resolution.Y;
606
607 /* Reset the mode change flag */
608 ModeChanged = FALSE;
609 }
610
611 static VOID VgaUpdateFramebuffer(VOID)
612 {
613 INT i, j, k;
614 COORD Resolution = VgaGetDisplayResolution();
615 INT AddressSize = VgaGetAddressSize();
616 DWORD Address = (VgaCrtcRegisters[VGA_CRTC_START_ADDR_HIGH_REG] << 8)
617 + VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG];
618 DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
619
620 /*
621 * If console framebuffer is NULL, that means something went wrong
622 * earlier and this is the final display refresh.
623 */
624 if (ConsoleFramebuffer == NULL) return;
625
626 /* Check if this is text mode or graphics mode */
627 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
628 {
629 /* Graphics mode */
630 PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
631
632 /*
633 * Synchronize access to the graphics framebuffer
634 * with the console framebuffer mutex.
635 */
636 WaitForSingleObject(ConsoleMutex, INFINITE);
637
638 /* Loop through the scanlines */
639 for (i = 0; i < Resolution.Y; i++)
640 {
641 /* Loop through the pixels */
642 for (j = 0; j < Resolution.X; j++)
643 {
644 BYTE PixelData = 0;
645
646 /* Check the shifting mode */
647 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFT256)
648 {
649 /* 4 bits shifted from each plane */
650
651 /* Check if this is 16 or 256 color mode */
652 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
653 {
654 /* One byte per pixel */
655 PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
656 + (Address + (j / VGA_NUM_BANKS))
657 * AddressSize];
658 }
659 else
660 {
661 /* 4-bits per pixel */
662
663 PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
664 + (Address + (j / (VGA_NUM_BANKS * 2)))
665 * AddressSize];
666
667 /* Check if we should use the highest 4 bits or lowest 4 */
668 if (((j / VGA_NUM_BANKS) % 2) == 0)
669 {
670 /* Highest 4 */
671 PixelData >>= 4;
672 }
673 else
674 {
675 /* Lowest 4 */
676 PixelData &= 0x0F;
677 }
678 }
679 }
680 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFTREG)
681 {
682 /* Check if this is 16 or 256 color mode */
683 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
684 {
685 // TODO: NOT IMPLEMENTED
686 DPRINT1("8-bit interleaved mode is not implemented!\n");
687 }
688 else
689 {
690 /*
691 * 2 bits shifted from plane 0 and 2 for the first 4 pixels,
692 * then 2 bits shifted from plane 1 and 3 for the next 4
693 */
694 BYTE LowPlaneData = VgaMemory[((j / 4) % 2) * VGA_BANK_SIZE
695 + (Address + (j / 4)) * AddressSize];
696 BYTE HighPlaneData = VgaMemory[(((j / 4) % 2) + 2) * VGA_BANK_SIZE
697 + (Address + (j / 4)) * AddressSize];
698
699 /* Extract the two bits from each plane */
700 LowPlaneData = (LowPlaneData >> (6 - ((j % 4) * 2))) & 3;
701 HighPlaneData = (HighPlaneData >> (6 - ((j % 4) * 2))) & 3;
702
703 /* Combine them into the pixel */
704 PixelData = LowPlaneData | (HighPlaneData << 2);
705 }
706 }
707 else
708 {
709 /* 1 bit shifted from each plane */
710
711 /* Check if this is 16 or 256 color mode */
712 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
713 {
714 /* 8 bits per pixel, 2 on each plane */
715
716 for (k = 0; k < VGA_NUM_BANKS; k++)
717 {
718 /* The data is on plane k, 4 pixels per byte */
719 BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
720 + (Address + (j / 4)) * AddressSize];
721
722 /* The mask of the first bit in the pair */
723 BYTE BitMask = 1 << (((3 - (j % 4)) * 2) + 1);
724
725 /* Bits 0, 1, 2 and 3 come from the first bit of the pair */
726 if (PlaneData & BitMask) PixelData |= 1 << k;
727
728 /* Bits 4, 5, 6 and 7 come from the second bit of the pair */
729 if (PlaneData & (BitMask >> 1)) PixelData |= 1 << (k + 4);
730 }
731 }
732 else
733 {
734 /* 4 bits per pixel, 1 on each plane */
735
736 for (k = 0; k < VGA_NUM_BANKS; k++)
737 {
738 BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
739 + (Address + (j / 8)) * AddressSize];
740
741 /* If the bit on that plane is set, set it */
742 if (PlaneData & (1 << (7 - (j % 8)))) PixelData |= 1 << k;
743 }
744 }
745 }
746
747 if (!(VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT))
748 {
749 /* In 16 color mode, the value is an index to the AC registers */
750 PixelData = VgaAcRegisters[PixelData];
751 }
752
753 if (DoubleVision)
754 {
755 /* Now check if the resulting pixel data has changed */
756 if (GraphicsBuffer[(i * Resolution.X * 4) + (j * 2)] != PixelData)
757 {
758 /* Yes, write the new value */
759 GraphicsBuffer[(i * Resolution.X * 4) + (j * 2)] = PixelData;
760 GraphicsBuffer[(i * Resolution.X * 4) + (j * 2 + 1)] = PixelData;
761 GraphicsBuffer[((i * 2 + 1) * Resolution.X * 2) + (j * 2)] = PixelData;
762 GraphicsBuffer[((i * 2 + 1) * Resolution.X * 2) + (j * 2 + 1)] = PixelData;
763
764 /* Mark the specified pixel as changed */
765 VgaMarkForUpdate(i, j);
766 }
767 }
768 else
769 {
770 /* Now check if the resulting pixel data has changed */
771 if (GraphicsBuffer[i * Resolution.X + j] != PixelData)
772 {
773 /* Yes, write the new value */
774 GraphicsBuffer[i * Resolution.X + j] = PixelData;
775
776 /* Mark the specified pixel as changed */
777 VgaMarkForUpdate(i, j);
778 }
779 }
780 }
781
782 /* Move to the next scanline */
783 Address += ScanlineSize;
784 }
785
786 /*
787 * Release the console framebuffer mutex
788 * so that we allow for repainting.
789 */
790 ReleaseMutex(ConsoleMutex);
791 }
792 else
793 {
794 /* Text mode */
795 PCHAR_INFO CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
796
797 /* Loop through the scanlines */
798 for (i = 0; i < Resolution.Y; i++)
799 {
800 /* Loop through the characters */
801 for (j = 0; j < Resolution.X; j++)
802 {
803 DWORD CurrentAddr = LOWORD((Address + j) * AddressSize);
804 CHAR_INFO CharInfo;
805
806 /* Plane 0 holds the character itself */
807 CharInfo.Char.AsciiChar = VgaMemory[CurrentAddr];
808
809 /* Plane 1 holds the attribute */
810 CharInfo.Attributes = VgaMemory[CurrentAddr + VGA_BANK_SIZE];
811
812 /* Now check if the resulting character data has changed */
813 if ((CharBuffer[i * Resolution.X + j].Char.AsciiChar != CharInfo.Char.AsciiChar)
814 || (CharBuffer[i * Resolution.X + j].Attributes != CharInfo.Attributes))
815 {
816 /* Yes, write the new value */
817 CharBuffer[i * Resolution.X + j] = CharInfo;
818
819 /* Mark the specified cell as changed */
820 VgaMarkForUpdate(i, j);
821 }
822 }
823
824 /* Move to the next scanline */
825 Address += ScanlineSize;
826 }
827 }
828 }
829
830 static VOID VgaUpdateTextCursor(VOID)
831 {
832 COORD Position;
833 CONSOLE_CURSOR_INFO CursorInfo;
834 BYTE CursorStart = VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x3F;
835 BYTE CursorEnd = VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] & 0x1F;
836 DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
837 BYTE TextSize = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
838 WORD Location = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG],
839 VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG]);
840
841 if (CursorStart < CursorEnd)
842 {
843 /* Visible cursor */
844 CursorInfo.bVisible = TRUE;
845 CursorInfo.dwSize = (100 * (CursorEnd - CursorStart)) / TextSize;
846 }
847 else
848 {
849 /* No cursor */
850 CursorInfo.bVisible = FALSE;
851 CursorInfo.dwSize = 0;
852 }
853
854 /* Add the cursor skew to the location */
855 Location += (VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] >> 5) & 3;
856
857 /* Find the coordinates of the new position */
858 Position.X = (WORD)(Location % ScanlineSize);
859 Position.Y = (WORD)(Location / ScanlineSize);
860
861 /* Update the physical cursor */
862 SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
863 SetConsoleCursorPosition(TextConsoleBuffer, Position);
864
865 /* Reset the cursor move flag */
866 CursorMoved = FALSE;
867 }
868
869 /* PUBLIC FUNCTIONS ***********************************************************/
870
871 DWORD VgaGetVideoBaseAddress(VOID)
872 {
873 return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
874 }
875
876 DWORD VgaGetVideoLimitAddress(VOID)
877 {
878 return MemoryLimit[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
879 }
880
881 COORD VgaGetDisplayResolution(VOID)
882 {
883 COORD Resolution;
884 BYTE MaximumScanLine = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
885
886 /* The low 8 bits are in the display registers */
887 Resolution.X = VgaCrtcRegisters[VGA_CRTC_END_HORZ_DISP_REG];
888 Resolution.Y = VgaCrtcRegisters[VGA_CRTC_VERT_DISP_END_REG];
889
890 /* Set the top bits from the overflow register */
891 if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE8)
892 {
893 Resolution.Y |= 1 << 8;
894 }
895 if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE9)
896 {
897 Resolution.Y |= 1 << 9;
898 }
899
900 /* Increase the values by 1 */
901 Resolution.X++;
902 Resolution.Y++;
903
904 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
905 {
906 /* Multiply the horizontal resolution by the 9/8 dot mode */
907 Resolution.X *= (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & VGA_SEQ_CLOCK_98DM)
908 ? 8 : 9;
909
910 /* The horizontal resolution is halved in 8-bit mode */
911 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2;
912 }
913
914 if (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & VGA_CRTC_MAXSCANLINE_DOUBLE)
915 {
916 /* Halve the vertical resolution */
917 Resolution.Y >>= 1;
918 }
919
920 /* Divide the vertical resolution by the maximum scan line (== font size in text mode) */
921 Resolution.Y /= MaximumScanLine;
922
923 /* Return the resolution */
924 return Resolution;
925 }
926
927 VOID VgaRefreshDisplay(VOID)
928 {
929 HANDLE ConsoleBufferHandle = NULL;
930 COORD Resolution = VgaGetDisplayResolution();
931
932 DPRINT("VgaRefreshDisplay\n");
933
934 /* Change the display mode */
935 if (ModeChanged) VgaChangeMode();
936
937 /* Change the text cursor location */
938 if (CursorMoved) VgaUpdateTextCursor();
939
940 if (PaletteChanged)
941 {
942 /* Trigger a full update of the screen */
943 NeedsUpdate = TRUE;
944 UpdateRectangle.Left = 0;
945 UpdateRectangle.Top = 0;
946 UpdateRectangle.Right = Resolution.X;
947 UpdateRectangle.Bottom = Resolution.Y;
948
949 PaletteChanged = FALSE;
950 }
951
952 /* Update the contents of the framebuffer */
953 VgaUpdateFramebuffer();
954
955 /* Set the vertical retrace flag */
956 InVerticalRetrace = TRUE;
957
958 /* Ignore if there's nothing to update */
959 if (!NeedsUpdate) return;
960
961 DPRINT("Updating screen rectangle (%d, %d, %d, %d)\n",
962 UpdateRectangle.Left,
963 UpdateRectangle.Top,
964 UpdateRectangle.Right,
965 UpdateRectangle.Bottom);
966
967 /* Check if this is text mode or graphics mode */
968 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
969 {
970 /* Graphics mode */
971 ConsoleBufferHandle = GraphicsConsoleBuffer;
972 }
973 else
974 {
975 /* Text mode */
976 COORD Origin = { UpdateRectangle.Left, UpdateRectangle.Top };
977 ConsoleBufferHandle = TextConsoleBuffer;
978
979 /* Write the data to the console */
980 WriteConsoleOutputA(TextConsoleBuffer,
981 (PCHAR_INFO)ConsoleFramebuffer,
982 Resolution,
983 Origin,
984 &UpdateRectangle);
985 }
986
987 if (DoubleVision)
988 {
989 /* Scale the update rectangle */
990 UpdateRectangle.Left *= 2;
991 UpdateRectangle.Top *= 2;
992 UpdateRectangle.Right = UpdateRectangle.Right * 2 + 1;
993 UpdateRectangle.Bottom = UpdateRectangle.Bottom * 2 + 1;
994 }
995
996 /* Redraw the screen */
997 InvalidateConsoleDIBits(ConsoleBufferHandle, &UpdateRectangle);
998
999 /* Clear the update flag */
1000 NeedsUpdate = FALSE;
1001 }
1002
1003 VOID VgaHorizontalRetrace(VOID)
1004 {
1005 /* Set the flag */
1006 InHorizontalRetrace = TRUE;
1007 }
1008
1009 VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
1010 {
1011 DWORD i;
1012 DWORD VideoAddress;
1013
1014 DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n",
1015 Address,
1016 Size);
1017
1018 /* Ignore if video RAM access is disabled */
1019 if (!(VgaMiscRegister & VGA_MISC_RAM_ENABLED)) return;
1020
1021 /* Loop through each byte */
1022 for (i = 0; i < Size; i++)
1023 {
1024 VideoAddress = VgaTranslateReadAddress(Address + i);
1025
1026 /* Load the latch registers */
1027 VgaLatchRegisters[0] = VgaMemory[LOWORD(VideoAddress)];
1028 VgaLatchRegisters[1] = VgaMemory[VGA_BANK_SIZE + LOWORD(VideoAddress)];
1029 VgaLatchRegisters[2] = VgaMemory[(2 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
1030 VgaLatchRegisters[3] = VgaMemory[(3 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
1031
1032 /* Copy the value to the buffer */
1033 Buffer[i] = VgaMemory[VideoAddress];
1034 }
1035 }
1036
1037 VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
1038 {
1039 DWORD i, j;
1040 DWORD VideoAddress;
1041
1042 DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n",
1043 Address,
1044 Size);
1045
1046 /* Ignore if video RAM access is disabled */
1047 if (!(VgaMiscRegister & VGA_MISC_RAM_ENABLED)) return;
1048
1049 /* Also ignore if write access to all planes is disabled */
1050 if ((VgaSeqRegisters[VGA_SEQ_MASK_REG] & 0x0F) == 0x00) return;
1051
1052 /* Loop through each byte */
1053 for (i = 0; i < Size; i++)
1054 {
1055 VideoAddress = VgaTranslateWriteAddress(Address + i);
1056
1057 for (j = 0; j < VGA_NUM_BANKS; j++)
1058 {
1059 /* Make sure the page is writeable */
1060 if (!(VgaSeqRegisters[VGA_SEQ_MASK_REG] & (1 << j))) continue;
1061
1062 /* Check if this is chain-4 mode */
1063 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
1064 {
1065 if (((Address + i) & 3) != j)
1066 {
1067 /* This plane will not be accessed */
1068 continue;
1069 }
1070 }
1071
1072 /* Check if this is odd-even mode */
1073 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
1074 {
1075 if (((Address + i) & 1) != (j & 1))
1076 {
1077 /* This plane will not be accessed */
1078 continue;
1079 }
1080 }
1081
1082 /* Copy the value to the VGA memory */
1083 VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = VgaTranslateByteForWriting(Buffer[i], j);
1084 }
1085 }
1086 }
1087
1088 BYTE WINAPI VgaReadPort(ULONG Port)
1089 {
1090 DPRINT("VgaReadPort: Port 0x%08X\n", Port);
1091
1092 switch (Port)
1093 {
1094 case VGA_AC_INDEX:
1095 {
1096 return VgaAcIndex;
1097 }
1098
1099 case VGA_AC_READ:
1100 {
1101 return VgaAcRegisters[VgaAcIndex];
1102 }
1103
1104 case VGA_SEQ_INDEX:
1105 {
1106 return VgaSeqIndex;
1107 }
1108
1109 case VGA_SEQ_DATA:
1110 {
1111 return VgaSeqRegisters[VgaSeqIndex];
1112 }
1113
1114 case VGA_DAC_READ_INDEX:
1115 {
1116 /* This returns the read/write state */
1117 return VgaDacReadWrite ? 0 : 3;
1118 }
1119
1120 case VGA_DAC_WRITE_INDEX:
1121 {
1122 return VgaDacIndex / 3;
1123 }
1124
1125 case VGA_DAC_DATA:
1126 {
1127 /* Ignore reads in write mode */
1128 if (!VgaDacReadWrite)
1129 {
1130 BYTE Data = VgaDacRegisters[VgaDacIndex++];
1131 VgaDacIndex %= VGA_PALETTE_SIZE;
1132 return Data;
1133 }
1134
1135 break;
1136 }
1137
1138 case VGA_MISC_READ:
1139 {
1140 return VgaMiscRegister;
1141 }
1142
1143 case VGA_CRTC_INDEX:
1144 {
1145 return VgaCrtcIndex;
1146 }
1147
1148 case VGA_CRTC_DATA:
1149 {
1150 return VgaCrtcRegisters[VgaCrtcIndex];
1151 }
1152
1153 case VGA_GC_INDEX:
1154 {
1155 return VgaGcIndex;
1156 }
1157
1158 case VGA_GC_DATA:
1159 {
1160 return VgaGcRegisters[VgaGcIndex];
1161 }
1162
1163 case VGA_STAT_MONO:
1164 case VGA_STAT_COLOR:
1165 {
1166 BYTE Result = 0;
1167
1168 /* Reset the AC latch */
1169 VgaAcLatch = FALSE;
1170
1171 /* Set a flag if there is a vertical or horizontal retrace */
1172 if (InVerticalRetrace || InHorizontalRetrace) Result |= VGA_STAT_DD;
1173
1174 /* Set an additional flag if there was a vertical retrace */
1175 if (InVerticalRetrace) Result |= VGA_STAT_VRETRACE;
1176
1177 /* Clear the flags */
1178 InHorizontalRetrace = InVerticalRetrace = FALSE;
1179
1180 return Result;
1181 }
1182 }
1183
1184 return 0;
1185 }
1186
1187 VOID WINAPI VgaWritePort(ULONG Port, BYTE Data)
1188 {
1189 DPRINT("VgaWritePort: Port 0x%08X, Data 0x%02X\n", Port, Data);
1190
1191 switch (Port)
1192 {
1193 case VGA_AC_INDEX:
1194 {
1195 if (!VgaAcLatch)
1196 {
1197 /* Change the index */
1198 if (Data < VGA_AC_MAX_REG) VgaAcIndex = Data;
1199 }
1200 else
1201 {
1202 /* Write the data */
1203 VgaWriteAc(Data);
1204 }
1205
1206 /* Toggle the latch */
1207 VgaAcLatch = !VgaAcLatch;
1208
1209 break;
1210 }
1211
1212 case VGA_SEQ_INDEX:
1213 {
1214 /* Set the sequencer index register */
1215 if (Data < VGA_SEQ_MAX_REG) VgaSeqIndex = Data;
1216 break;
1217 }
1218
1219 case VGA_SEQ_DATA:
1220 {
1221 /* Call the sequencer function */
1222 VgaWriteSequencer(Data);
1223 break;
1224 }
1225
1226 case VGA_DAC_READ_INDEX:
1227 {
1228 VgaDacReadWrite = FALSE;
1229 VgaDacIndex = Data * 3;
1230 break;
1231 }
1232
1233 case VGA_DAC_WRITE_INDEX:
1234 {
1235 VgaDacReadWrite = TRUE;
1236 VgaDacIndex = Data * 3;
1237 break;
1238 }
1239
1240 case VGA_DAC_DATA:
1241 {
1242 /* Ignore writes in read mode */
1243 if (VgaDacReadWrite) VgaWriteDac(Data & 0x3F);
1244 break;
1245 }
1246
1247 case VGA_MISC_WRITE:
1248 {
1249 VgaMiscRegister = Data;
1250 break;
1251 }
1252
1253 case VGA_CRTC_INDEX:
1254 {
1255 /* Set the CRTC index register */
1256 if (Data < VGA_CRTC_MAX_REG) VgaCrtcIndex = Data;
1257 break;
1258 }
1259
1260 case VGA_CRTC_DATA:
1261 {
1262 /* Call the CRTC function */
1263 VgaWriteCrtc(Data);
1264 break;
1265 }
1266
1267 case VGA_GC_INDEX:
1268 {
1269 /* Set the GC index register */
1270 if (Data < VGA_GC_MAX_REG) VgaGcIndex = Data;
1271 break;
1272 }
1273
1274 case VGA_GC_DATA:
1275 {
1276 /* Call the GC function */
1277 VgaWriteGc(Data);
1278 break;
1279 }
1280 }
1281 }
1282
1283 VOID VgaClearMemory(VOID)
1284 {
1285 ZeroMemory(VgaMemory, sizeof(VgaMemory));
1286 }
1287
1288 VOID VgaResetPalette(VOID)
1289 {
1290 PALETTEENTRY Entries[VGA_MAX_COLORS];
1291
1292 /* Restore the default palette */
1293 VgaRestoreDefaultPalette(Entries, VGA_MAX_COLORS);
1294
1295 SetPaletteEntries(PaletteHandle, 0, VGA_MAX_COLORS, Entries);
1296 PaletteChanged = TRUE;
1297 }
1298
1299 BOOLEAN VgaInitialize(HANDLE TextHandle)
1300 {
1301 INT i, j;
1302 COORD Resolution;
1303 INT AddressSize;
1304 DWORD ScanlineSize;
1305 COORD Origin = { 0, 0 };
1306 SMALL_RECT ScreenRect;
1307 PCHAR_INFO CharBuffer;
1308 DWORD Address = 0;
1309 DWORD CurrentAddr;
1310
1311 /* Initialize the VGA palette and fail if it isn't successfully created */
1312 if (!VgaInitializePalette()) return FALSE;
1313
1314 /* Set the global handle */
1315 TextConsoleBuffer = TextHandle;
1316
1317 /* Clear the VGA memory */
1318 VgaClearMemory();
1319
1320 /* Set the default video mode */
1321 BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE);
1322 VgaChangeMode();
1323
1324 /* Get the data */
1325 Resolution = VgaGetDisplayResolution();
1326 CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
1327 AddressSize = VgaGetAddressSize();
1328 ScreenRect.Left = ScreenRect.Top = 0;
1329 ScreenRect.Right = Resolution.X;
1330 ScreenRect.Bottom = Resolution.Y;
1331 ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
1332
1333 /* Read the data from the console into the framebuffer */
1334 ReadConsoleOutputA(TextConsoleBuffer,
1335 CharBuffer,
1336 Resolution,
1337 Origin,
1338 &ScreenRect);
1339
1340 /* Loop through the scanlines */
1341 for (i = 0; i < Resolution.Y; i++)
1342 {
1343 /* Loop through the characters */
1344 for (j = 0; j < Resolution.X; j++)
1345 {
1346 CurrentAddr = LOWORD((Address + j) * AddressSize);
1347
1348 /* Store the character in plane 0 */
1349 VgaMemory[CurrentAddr] = CharBuffer[i * Resolution.X + j].Char.AsciiChar;
1350
1351 /* Store the attribute in plane 1 */
1352 VgaMemory[CurrentAddr + VGA_BANK_SIZE] = (BYTE)CharBuffer[i * Resolution.X + j].Attributes;
1353 }
1354
1355 /* Move to the next scanline */
1356 Address += ScanlineSize;
1357 }
1358
1359 /* Register the I/O Ports */
1360 RegisterIoPort(VGA_AC_WRITE , VgaReadPort, VgaWritePort);
1361 RegisterIoPort(VGA_AC_READ , VgaReadPort, VgaWritePort);
1362 RegisterIoPort(VGA_SEQ_INDEX, VgaReadPort, VgaWritePort);
1363 RegisterIoPort(VGA_SEQ_DATA , VgaReadPort, VgaWritePort);
1364 RegisterIoPort(VGA_DAC_READ_INDEX , VgaReadPort, VgaWritePort);
1365 RegisterIoPort(VGA_DAC_WRITE_INDEX, VgaReadPort, VgaWritePort);
1366 RegisterIoPort(VGA_DAC_DATA , VgaReadPort, VgaWritePort);
1367 RegisterIoPort(VGA_MISC_READ , VgaReadPort, VgaWritePort);
1368 RegisterIoPort(VGA_MISC_WRITE, VgaReadPort, VgaWritePort);
1369 RegisterIoPort(VGA_CRTC_INDEX, VgaReadPort, VgaWritePort);
1370 RegisterIoPort(VGA_CRTC_DATA , VgaReadPort, VgaWritePort);
1371 RegisterIoPort(VGA_GC_INDEX, VgaReadPort, VgaWritePort);
1372 RegisterIoPort(VGA_GC_DATA , VgaReadPort, VgaWritePort);
1373 RegisterIoPort(VGA_STAT_MONO , VgaReadPort, VgaWritePort);
1374 RegisterIoPort(VGA_STAT_COLOR, VgaReadPort, VgaWritePort);
1375
1376 /* Return success */
1377 return TRUE;
1378 }
1379
1380 /* EOF */