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