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