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