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