[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[] = { 0xA7FFF, 0xA7FFF, 0xB7FFF, 0xBFFFF };
20
21 static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
22 static BYTE VgaMiscRegister;
23 static BYTE VgaSeqIndex = VGA_SEQ_RESET_REG;
24 static BYTE VgaSeqRegisters[VGA_SEQ_MAX_REG];
25 static BYTE VgaGcIndex = VGA_GC_RESET_REG;
26 static BYTE VgaGcRegisters[VGA_GC_MAX_REG];
27 static BYTE VgaCrtcIndex = VGA_CRTC_HORZ_TOTAL_REG;
28 static BYTE VgaCrtcRegisters[VGA_CRTC_MAX_REG];
29 static BYTE VgaAcIndex = VGA_AC_PAL_0_REG;
30 static BOOLEAN VgaAcLatch = FALSE;
31 static BYTE VgaAcRegisters[VGA_AC_MAX_REG];
32 static BYTE VgaDacIndex = 0;
33 static BOOLEAN VgaDacReadWrite = FALSE;
34 static BYTE VgaDacRegisters[VGA_PALETTE_SIZE];
35 static BOOLEAN InVerticalRetrace = FALSE;
36 static BOOLEAN InHorizontalRetrace = FALSE;
37 static HANDLE TextConsoleBuffer = NULL;
38 static HANDLE GraphicsConsoleBuffer = NULL;
39 static LPVOID ConsoleFramebuffer = NULL;
40 static HANDLE ConsoleMutex = NULL;
41 static BOOLEAN NeedsUpdate = FALSE;
42 static BOOLEAN ModeChanged = TRUE;
43 static BOOLEAN CursorMoved = FALSE;
44 static BOOLEAN TextMode = TRUE;
45 static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
46
47 /* PRIVATE FUNCTIONS **********************************************************/
48
49 static inline INT VgaGetAddressSize(VOID)
50 {
51 if (VgaCrtcRegisters[VGA_CRTC_UNDERLINE_REG] & VGA_CRTC_UNDERLINE_DWORD)
52 {
53 /* Double-word addressing */
54 return 4;
55 }
56
57 if (VgaCrtcRegisters[VGA_CRTC_MODE_CONTROL_REG] & VGA_CRTC_MODE_CONTROL_BYTE)
58 {
59 /* Byte addressing */
60 return 1;
61 }
62
63 /* Word addressing */
64 return 2;
65 }
66
67 static inline DWORD VgaTranslateReadAddress(DWORD Address)
68 {
69 DWORD Offset = Address - VgaGetVideoBaseAddress();
70 BYTE Plane;
71
72 /* Check for chain-4 and odd-even mode */
73 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
74 {
75 /* The lowest two bits are the plane number */
76 Plane = Offset & 3;
77 Offset >>= 2;
78 }
79 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
80 {
81 /* The LSB is the plane number */
82 Plane = Offset & 1;
83 Offset >>= 1;
84 }
85 else
86 {
87 /* Use the read mode */
88 Plane = VgaGcRegisters[VGA_GC_READ_MAP_SEL_REG] & 0x03;
89 }
90
91 /* Multiply the offset by the address size */
92 Offset *= VgaGetAddressSize();
93
94 return Offset + Plane * VGA_BANK_SIZE;
95 }
96
97 static inline DWORD VgaTranslateWriteAddress(DWORD Address)
98 {
99 DWORD Offset = Address - VgaGetVideoBaseAddress();
100
101 /* Check for chain-4 and odd-even mode */
102 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
103 {
104 /* Shift the offset to the right by 2 */
105 Offset >>= 2;
106 }
107 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
108 {
109 /* Shift the offset to the right by 1 */
110 Offset >>= 1;
111 }
112
113 /* Multiply the offset by the address size */
114 Offset *= VgaGetAddressSize();
115
116 /* Return the offset on plane 0 */
117 return Offset;
118 }
119
120 static inline VOID VgaMarkForUpdate(SHORT Row, SHORT Column)
121 {
122 DPRINT("VgaMarkForUpdate: Row %d, Column %d\n", Row, Column);
123
124 /* Check if this is the first time the rectangle is updated */
125 if (!NeedsUpdate)
126 {
127 UpdateRectangle.Left = UpdateRectangle.Top = (SHORT)0x7FFF;
128 UpdateRectangle.Right = UpdateRectangle.Bottom = (SHORT)0x8000;
129 }
130
131 /* Expand the rectangle to include the point */
132 UpdateRectangle.Left = min(UpdateRectangle.Left, Column);
133 UpdateRectangle.Right = max(UpdateRectangle.Right, Column);
134 UpdateRectangle.Top = min(UpdateRectangle.Top, Row);
135 UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Row);
136
137 /* Set the update request flag */
138 NeedsUpdate = TRUE;
139 }
140
141 static VOID VgaWriteSequencer(BYTE Data)
142 {
143 ASSERT(VgaSeqIndex < VGA_SEQ_MAX_REG);
144
145 /* Save the value */
146 VgaSeqRegisters[VgaSeqIndex] = Data;
147 }
148
149 static VOID VgaWriteGc(BYTE Data)
150 {
151 ASSERT(VgaGcIndex < VGA_GC_MAX_REG);
152
153 /* Save the value */
154 VgaGcRegisters[VgaGcIndex] = Data;
155
156 /* Check the index */
157 switch (VgaGcIndex)
158 {
159 case VGA_GC_MISC_REG:
160 {
161 /* The GC misc register decides if it's text or graphics mode */
162 ModeChanged = TRUE;
163
164 break;
165 }
166 }
167 }
168
169 static VOID VgaWriteCrtc(BYTE Data)
170 {
171 ASSERT(VgaGcIndex < VGA_CRTC_MAX_REG);
172
173 /* Save the value */
174 VgaCrtcRegisters[VgaCrtcIndex] = Data;
175
176 /* Check the index */
177 switch (VgaCrtcIndex)
178 {
179 case VGA_CRTC_END_HORZ_DISP_REG:
180 case VGA_CRTC_VERT_DISP_END_REG:
181 case VGA_CRTC_OVERFLOW_REG:
182 {
183 /* The video mode has changed */
184 ModeChanged = TRUE;
185
186 break;
187 }
188
189 case VGA_CRTC_CURSOR_LOC_LOW_REG:
190 case VGA_CRTC_CURSOR_LOC_HIGH_REG:
191 case VGA_CRTC_CURSOR_START_REG:
192 case VGA_CRTC_CURSOR_END_REG:
193 {
194 /* Set the cursor moved flag */
195 CursorMoved = TRUE;
196
197 break;
198 }
199 }
200 }
201
202 static VOID VgaWriteDac(BYTE Data)
203 {
204 /* Set the value */
205 VgaDacRegisters[VgaDacIndex++] = Data;
206 VgaDacIndex %= VGA_PALETTE_SIZE;
207
208 // TODO: Change the palette!
209 }
210
211 static VOID VgaWriteAc(BYTE Data)
212 {
213 ASSERT(VgaAcIndex < VGA_AC_MAX_REG);
214
215 /* Save the value */
216 VgaAcRegisters[VgaAcIndex] = Data;
217 }
218
219 static BOOL VgaEnterGraphicsMode(PCOORD Resolution, UINT BitDepth)
220 {
221 DWORD i;
222 CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
223 BYTE BitmapInfoBuffer[VGA_BITMAP_INFO_SIZE];
224 LPBITMAPINFO BitmapInfo = (LPBITMAPINFO)BitmapInfoBuffer;
225 LPWORD PaletteIndex = (LPWORD)(BitmapInfoBuffer + sizeof(BITMAPINFOHEADER));
226
227 /* Fill the bitmap info header */
228 ZeroMemory(&BitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER));
229 BitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
230 BitmapInfo->bmiHeader.biWidth = Resolution->X;
231 BitmapInfo->bmiHeader.biHeight = Resolution->Y;
232 BitmapInfo->bmiHeader.biBitCount = 8;
233 BitmapInfo->bmiHeader.biPlanes = 1;
234 BitmapInfo->bmiHeader.biCompression = BI_RGB;
235 BitmapInfo->bmiHeader.biSizeImage = Resolution->X * Resolution->Y
236 * (BitDepth / 8);
237
238 /* Fill the palette data */
239 for (i = 0; i < BitDepth; i++) PaletteIndex[i] = (WORD)i;
240
241 /* Fill the console graphics buffer info */
242 GraphicsBufferInfo.dwBitMapInfoLength = VGA_BITMAP_INFO_SIZE;
243 GraphicsBufferInfo.lpBitMapInfo = BitmapInfo;
244 GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS;
245
246 /* Create the buffer */
247 GraphicsConsoleBuffer = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
248 FILE_SHARE_READ | FILE_SHARE_WRITE,
249 NULL,
250 CONSOLE_GRAPHICS_BUFFER,
251 &GraphicsBufferInfo);
252 if (GraphicsConsoleBuffer == INVALID_HANDLE_VALUE) return FALSE;
253
254 /* Save the framebuffer address and mutex */
255 ConsoleFramebuffer = GraphicsBufferInfo.lpBitMap;
256 ConsoleMutex = GraphicsBufferInfo.hMutex;
257
258 /* Set the active buffer */
259 SetConsoleActiveScreenBuffer(GraphicsConsoleBuffer);
260
261 return TRUE;
262 }
263
264 static VOID VgaLeaveGraphicsMode(VOID)
265 {
266 /* Switch back to the text buffer */
267 SetConsoleActiveScreenBuffer(TextConsoleBuffer);
268
269 /* Cleanup the video data */
270 CloseHandle(ConsoleMutex);
271 CloseHandle(GraphicsConsoleBuffer);
272 GraphicsConsoleBuffer = NULL;
273 }
274
275 static BOOL VgaEnterTextMode(PCOORD Resolution)
276 {
277 /* Resize the console */
278 SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
279
280 /* Allocate a framebuffer */
281 ConsoleFramebuffer = HeapAlloc(GetProcessHeap(),
282 HEAP_ZERO_MEMORY,
283 Resolution->X * Resolution->Y
284 * sizeof(CHAR_INFO));
285 if (ConsoleFramebuffer == NULL)
286 {
287 DisplayMessage(L"An unexpected error occurred!\n");
288 VdmRunning = FALSE;
289 return FALSE;
290 }
291
292 return TRUE;
293 }
294
295 static VOID VgaLeaveTextMode(VOID)
296 {
297 /* Free the old framebuffer */
298 HeapFree(GetProcessHeap(), 0, ConsoleFramebuffer);
299 ConsoleFramebuffer = NULL;
300 }
301
302 static VOID VgaUpdateMode(VOID)
303 {
304 COORD Resolution = VgaGetDisplayResolution();
305
306 if (!TextMode)
307 {
308 /* Switching from graphics mode to text mode */
309 VgaLeaveGraphicsMode();
310 }
311 else
312 {
313 /* Leave the current text mode */
314 VgaLeaveTextMode();
315 }
316
317 /* Check if the new mode is alphanumeric */
318 if (!(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA))
319 {
320 /* Enter new text mode */
321 if (!VgaEnterTextMode(&Resolution)) return;
322
323 /* Set the text mode flag */
324 TextMode = TRUE;
325 }
326 else
327 {
328 /* Enter 8-bit graphics mode */
329 if (!VgaEnterGraphicsMode(&Resolution, 8)) return;
330
331 /* Clear the text mode flag */
332 TextMode = FALSE;
333 }
334
335 /* Perform a full update */
336 NeedsUpdate = TRUE;
337 UpdateRectangle.Left = 0;
338 UpdateRectangle.Top = 0;
339 UpdateRectangle.Right = Resolution.X;
340 UpdateRectangle.Bottom = Resolution.Y;
341 }
342
343 static VOID VgaUpdateFramebuffer(VOID)
344 {
345 INT i, j, k;
346 COORD Resolution = VgaGetDisplayResolution();
347 INT AddressSize = VgaGetAddressSize();
348 DWORD Address = (VgaCrtcRegisters[VGA_CRTC_START_ADDR_HIGH_REG] << 8)
349 + VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG];
350 DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
351 PCHAR_INFO CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
352 PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
353
354 /* Loop through the scanlines */
355 for (i = 0; i < Resolution.Y; i++)
356 {
357 /* Check if this is text mode or graphics mode */
358 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
359 {
360 /* Graphics mode */
361
362 /* Loop through the pixels */
363 for (j = 0; j < Resolution.X; j++)
364 {
365 BYTE PixelData = 0;
366
367 /* Check the shifting mode */
368 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFT256)
369 {
370 /* 4 bits shifted from each plane */
371
372 /* Check if this is 16 or 256 color mode */
373 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
374 {
375 /* One byte per pixel */
376 PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
377 + (Address + (j / VGA_NUM_BANKS))
378 * AddressSize];
379 }
380 else
381 {
382 /* 4-bits per pixel */
383
384 PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
385 + (Address + (j / (VGA_NUM_BANKS * 2)))
386 * AddressSize];
387
388 /* Check if we should use the highest 4 bits or lowest 4 */
389 if (((j / VGA_NUM_BANKS) % 2) == 0)
390 {
391 /* Highest 4 */
392 PixelData >>= 4;
393 }
394 else
395 {
396 /* Lowest 4 */
397 PixelData &= 0x0F;
398 }
399 }
400 }
401 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFTREG)
402 {
403 /*
404 * 2 bits shifted from plane 0 and 2 for the first 4 pixels,
405 * then 2 bits shifted from plane 1 and 3 for the next 4
406 */
407
408 // TODO: NOT IMPLEMENTED!
409 DPRINT1("Interleaved shift mode is not implemented!\n");
410 }
411 else
412 {
413 /* 1 bit shifted from each plane */
414
415 /* Check if this is 16 or 256 color mode */
416 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
417 {
418 /* 8 bits per pixel, 2 on each plane */
419
420 for (k = 0; k < VGA_NUM_BANKS; k++)
421 {
422 /* The data is on plane k, 4 pixels per byte */
423 BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
424 + (Address + (j / 4)) * AddressSize];
425
426 /* The mask of the first bit in the pair */
427 BYTE BitMask = 1 << (((3 - (j % 4)) * 2) + 1);
428
429 /* Bits 0, 1, 2 and 3 come from the first bit of the pair */
430 if (PlaneData & BitMask) PixelData |= 1 << k;
431
432 /* Bits 4, 5, 6 and 7 come from the second bit of the pair */
433 if (PlaneData & (BitMask >> 1)) PixelData |= 1 << (k + 4);
434 }
435 }
436 else
437 {
438 /* 4 bits per pixel, 1 on each plane */
439
440 for (k = 0; k < VGA_NUM_BANKS; k++)
441 {
442 BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
443 + (Address + (j / 8)) * AddressSize];
444
445 /* If the bit on that plane is set, set it */
446 if (PlaneData & (1 << (7 - (j % 8)))) PixelData |= 1 << k;
447 }
448 }
449 }
450
451 /* Now check if the resulting pixel data has changed */
452 if (GraphicsBuffer[i * Resolution.X + j] != PixelData)
453 {
454 /* Yes, write the new value */
455 GraphicsBuffer[i * Resolution.X + j] = PixelData;
456
457 /* Mark the specified pixel as changed */
458 VgaMarkForUpdate(i, j);
459 }
460 }
461 }
462 else
463 {
464 /* Text mode */
465
466 /* Loop through the characters */
467 for (j = 0; j < Resolution.X; j++)
468 {
469 DWORD CurrentAddr = LOWORD((Address + j) * AddressSize);
470 CHAR_INFO CharInfo;
471
472 /* Plane 0 holds the character itself */
473 CharInfo.Char.AsciiChar = VgaMemory[CurrentAddr];
474
475 /* Plane 1 holds the attribute */
476 CharInfo.Attributes = VgaMemory[CurrentAddr + VGA_BANK_SIZE];
477
478 /* Now check if the resulting character data has changed */
479 if ((CharBuffer[i * Resolution.X + j].Char.AsciiChar != CharInfo.Char.AsciiChar)
480 || (CharBuffer[i * Resolution.X + j].Attributes != CharInfo.Attributes))
481 {
482 /* Yes, write the new value */
483 CharBuffer[i * Resolution.X + j] = CharInfo;
484
485 /* Mark the specified pixel as changed */
486 VgaMarkForUpdate(i, j);
487 }
488 }
489 }
490
491 /* Move to the next scanline */
492 Address += ScanlineSize;
493 }
494 }
495
496 static VOID VgaUpdateTextCursor(VOID)
497 {
498 COORD Position;
499 CONSOLE_CURSOR_INFO CursorInfo;
500 BYTE CursorStart = VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x3F;
501 BYTE CursorEnd = VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] & 0x1F;
502 DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
503 WORD Location = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG],
504 VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG]);
505
506 if (CursorStart < CursorEnd)
507 {
508 /* Visible cursor */
509 CursorInfo.bVisible = TRUE;
510 CursorInfo.dwSize = (100 * (CursorEnd - CursorStart)) >> 5;
511 }
512 else
513 {
514 /* No cursor */
515 CursorInfo.bVisible = FALSE;
516 CursorInfo.dwSize = 0;
517 }
518
519 /* Add the cursor skew to the location */
520 Location += (VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] >> 5) & 3;
521
522 /* Find the coordinates of the new position */
523 Position.X = (WORD)(Location % ScanlineSize);
524 Position.Y = (WORD)(Location / ScanlineSize);
525
526 /* Update the physical cursor */
527 SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
528 SetConsoleCursorPosition(TextConsoleBuffer, Position);
529 }
530
531 /* PUBLIC FUNCTIONS ***********************************************************/
532
533 DWORD VgaGetVideoBaseAddress(VOID)
534 {
535 return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
536 }
537
538 DWORD VgaGetVideoLimitAddress(VOID)
539 {
540 return MemoryLimit[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
541 }
542
543 COORD VgaGetDisplayResolution(VOID)
544 {
545 COORD Resolution;
546
547 /* The low 8 bits are in the display registers */
548 Resolution.X = VgaCrtcRegisters[VGA_CRTC_END_HORZ_DISP_REG];
549 Resolution.Y = VgaCrtcRegisters[VGA_CRTC_VERT_DISP_END_REG];
550
551 /* Set the top bits from the overflow register */
552 if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE8)
553 {
554 Resolution.Y |= 1 << 8;
555 }
556 if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE9)
557 {
558 Resolution.Y |= 1 << 9;
559 }
560
561 /* Increase the values by 1 */
562 Resolution.X++;
563 Resolution.Y++;
564
565 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
566 {
567 /* Multiply the horizontal resolution by the 9/8 dot mode */
568 Resolution.X *= (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & VGA_SEQ_CLOCK_98DM)
569 ? 8 : 9;
570
571 /* The horizontal resolution is halved in 8-bit mode */
572 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2;
573
574 /* Divide the vertical resolution by the maximum scan line */
575 Resolution.Y /= ((DWORD)VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F) + 1;
576 }
577 else
578 {
579 /* Divide the number of scanlines by the font size */
580 Resolution.Y /= 16;
581 }
582
583 /* Return the resolution */
584 return Resolution;
585 }
586
587 VOID VgaRefreshDisplay(VOID)
588 {
589 COORD Resolution = VgaGetDisplayResolution();
590
591 DPRINT("VgaRefreshDisplay\n");
592
593 if (ModeChanged)
594 {
595 /* Change the display mode */
596 VgaUpdateMode();
597
598 /* Reset the mode change flag */
599 ModeChanged = FALSE;
600 }
601
602 if (CursorMoved)
603 {
604 /* Change the text cursor location */
605 VgaUpdateTextCursor();
606
607 /* Reset the cursor move flag */
608 CursorMoved = FALSE;
609 }
610
611 /* Update the contents of the framebuffer */
612 VgaUpdateFramebuffer();
613
614 /* Set the vertical retrace flag */
615 InVerticalRetrace = TRUE;
616
617 /* Ignore if there's nothing to update */
618 if (!NeedsUpdate) return;
619
620 DPRINT("Updating screen rectangle (%d, %d, %d, %d)\n",
621 UpdateRectangle.Left,
622 UpdateRectangle.Top,
623 UpdateRectangle.Right,
624 UpdateRectangle.Bottom);
625
626 /* Check if this is text mode or graphics mode */
627 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
628 {
629 /* Graphics mode */
630
631 /* Redraw the screen */
632 InvalidateConsoleDIBits(GraphicsConsoleBuffer, &UpdateRectangle);
633 }
634 else
635 {
636 /* Text mode */
637 COORD Origin = { UpdateRectangle.Left, UpdateRectangle.Top };
638
639 /* Write the data to the console */
640 WriteConsoleOutputA(TextConsoleBuffer,
641 (PCHAR_INFO)ConsoleFramebuffer,
642 Resolution,
643 Origin,
644 &UpdateRectangle);
645
646 }
647
648 /* Clear the update flag */
649 NeedsUpdate = FALSE;
650 }
651
652 VOID VgaHorizontalRetrace(VOID)
653 {
654 /* Set the flag */
655 InHorizontalRetrace = TRUE;
656 }
657
658 VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
659 {
660 DWORD i;
661 DWORD VideoAddress;
662
663 DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n",
664 Address,
665 Size);
666
667 /* Ignore if video RAM access is disabled */
668 if (!(VgaMiscRegister & VGA_MISC_RAM_ENABLED)) return;
669
670 /* Loop through each byte */
671 for (i = 0; i < Size; i++)
672 {
673 VideoAddress = VgaTranslateReadAddress(Address + i);
674
675 /* Copy the value to the buffer */
676 Buffer[i] = VgaMemory[VideoAddress];
677 }
678 }
679
680 VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
681 {
682 DWORD i, j;
683 DWORD VideoAddress;
684
685 DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n",
686 Address,
687 Size);
688
689 /* Ignore if video RAM access is disabled */
690 if (!(VgaMiscRegister & VGA_MISC_RAM_ENABLED)) return;
691
692 /* Also ignore if write access to all planes is disabled */
693 if ((VgaSeqRegisters[VGA_SEQ_MASK_REG] & 0x0F) == 0x00) return;
694
695 /* Loop through each byte */
696 for (i = 0; i < Size; i++)
697 {
698 VideoAddress = VgaTranslateWriteAddress(Address + i);
699
700 for (j = 0; j < VGA_NUM_BANKS; j++)
701 {
702 /* Make sure the page is writeable */
703 if (!(VgaSeqRegisters[VGA_SEQ_MASK_REG] & (1 << j))) continue;
704
705 /* Check if this is chain-4 mode */
706 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
707 {
708 if (((Address + i) & 3) != j)
709 {
710 /* This plane will not be accessed */
711 continue;
712 }
713 }
714
715 /* Check if this is odd-even mode */
716 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
717 {
718 if (((Address + i) & 1) != (j & 1))
719 {
720 /* This plane will not be accessed */
721 continue;
722 }
723 }
724
725 /* Copy the value to the VGA memory */
726 VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = Buffer[i];
727 }
728 }
729 }
730
731 BYTE VgaReadPort(WORD Port)
732 {
733 DPRINT("VgaReadPort: Port 0x%04X\n", Port);
734
735 switch (Port)
736 {
737 case VGA_AC_INDEX:
738 {
739 return VgaAcIndex;
740 }
741
742 case VGA_AC_READ:
743 {
744 return VgaAcRegisters[VgaAcIndex];
745 }
746
747 case VGA_SEQ_INDEX:
748 {
749 return VgaSeqIndex;
750 }
751
752 case VGA_SEQ_DATA:
753 {
754 return VgaSeqRegisters[VgaSeqIndex];
755 }
756
757 case VGA_DAC_READ_INDEX:
758 {
759 /* This returns the read/write state */
760 return VgaDacReadWrite ? 0 : 3;
761 }
762
763 case VGA_DAC_WRITE_INDEX:
764 {
765 return VgaDacIndex;
766 }
767
768 case VGA_DAC_DATA:
769 {
770 /* Ignore reads in write mode */
771 if (!VgaDacReadWrite)
772 {
773 BYTE Data = VgaDacRegisters[VgaDacIndex++];
774 VgaDacIndex %= VGA_PALETTE_SIZE;
775 return Data;
776 }
777
778 break;
779 }
780
781 case VGA_MISC_READ:
782 {
783 return VgaMiscRegister;
784 }
785
786 case VGA_CRTC_INDEX:
787 {
788 return VgaCrtcIndex;
789 }
790
791 case VGA_CRTC_DATA:
792 {
793 return VgaCrtcRegisters[VgaCrtcIndex];
794 }
795
796 case VGA_GC_INDEX:
797 {
798 return VgaGcIndex;
799 }
800
801 case VGA_GC_DATA:
802 {
803 return VgaGcRegisters[VgaGcIndex];
804 }
805
806 case VGA_STAT_MONO:
807 case VGA_STAT_COLOR:
808 {
809 BYTE Result = 0;
810
811 /* Reset the AC latch */
812 VgaAcLatch = FALSE;
813
814 /* Set a flag if there is a vertical or horizontal retrace */
815 if (InVerticalRetrace || InHorizontalRetrace) Result |= VGA_STAT_DD;
816
817 /* Set an additional flag if there was a vertical retrace */
818 if (InVerticalRetrace) Result |= VGA_STAT_VRETRACE;
819
820 /* Clear the flags */
821 InHorizontalRetrace = InVerticalRetrace = FALSE;
822
823 return Result;
824 }
825 }
826
827 return 0;
828 }
829
830 VOID VgaWritePort(WORD Port, BYTE Data)
831 {
832 DPRINT("VgaWritePort: Port 0x%04X, Data 0x%02X\n", Port, Data);
833
834 switch (Port)
835 {
836 case VGA_AC_INDEX:
837 {
838 if (!VgaAcLatch)
839 {
840 /* Change the index */
841 if (Data < VGA_AC_MAX_REG) VgaAcIndex = Data;
842 }
843 else
844 {
845 /* Write the data */
846 VgaWriteAc(Data);
847 }
848
849 /* Toggle the latch */
850 VgaAcLatch = !VgaAcLatch;
851
852 break;
853 }
854
855 case VGA_SEQ_INDEX:
856 {
857 /* Set the sequencer index register */
858 if (Data < VGA_SEQ_MAX_REG) VgaSeqIndex = Data;
859
860 break;
861 }
862
863 case VGA_SEQ_DATA:
864 {
865 /* Call the sequencer function */
866 VgaWriteSequencer(Data);
867
868 break;
869 }
870
871 case VGA_DAC_READ_INDEX:
872 {
873 VgaDacReadWrite = FALSE;
874 VgaDacIndex = Data % VGA_PALETTE_SIZE;
875
876 break;
877 }
878
879 case VGA_DAC_WRITE_INDEX:
880 {
881 VgaDacReadWrite = TRUE;
882 VgaDacIndex = Data % VGA_PALETTE_SIZE;
883
884 break;
885 }
886
887 case VGA_DAC_DATA:
888 {
889 /* Ignore writes in read mode */
890 if (VgaDacReadWrite) VgaWriteDac(Data & 0x3F);
891
892 break;
893 }
894
895 case VGA_MISC_WRITE:
896 {
897 VgaMiscRegister = Data;
898
899 break;
900 }
901
902 case VGA_CRTC_INDEX:
903 {
904 /* Set the CRTC index register */
905 if (Data < VGA_CRTC_MAX_REG) VgaCrtcIndex = Data;
906
907 break;
908 }
909
910 case VGA_CRTC_DATA:
911 {
912 /* Call the CRTC function */
913 VgaWriteCrtc(Data);
914
915 break;
916 }
917
918 case VGA_GC_INDEX:
919 {
920 /* Set the GC index register */
921 if (Data < VGA_GC_MAX_REG) VgaGcIndex = Data;
922 break;
923 }
924
925 case VGA_GC_DATA:
926 {
927 /* Call the GC function */
928 VgaWriteGc(Data);
929
930 break;
931 }
932 }
933 }
934
935 VOID VgaInitialize(HANDLE TextHandle)
936 {
937 INT i, j;
938 COORD Resolution;
939 INT AddressSize;
940 DWORD ScanlineSize;
941 COORD Origin = { 0, 0 };
942 SMALL_RECT ScreenRect;
943 PCHAR_INFO CharBuffer;
944 DWORD Address = 0;
945 DWORD CurrentAddr;
946
947 /* Set the global handle */
948 TextConsoleBuffer = TextHandle;
949
950 /* Set the default video mode */
951 BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE);
952 VgaUpdateMode();
953 ModeChanged = FALSE;
954
955 /* Get the data */
956 Resolution = VgaGetDisplayResolution();
957 CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
958 AddressSize = VgaGetAddressSize();
959 ScreenRect.Left = ScreenRect.Top = 0;
960 ScreenRect.Right = Resolution.X;
961 ScreenRect.Bottom = Resolution.Y;
962 ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
963
964 /* Read the data from the console into the framebuffer */
965 ReadConsoleOutputA(TextConsoleBuffer,
966 CharBuffer,
967 Resolution,
968 Origin,
969 &ScreenRect);
970
971 /* Loop through the scanlines */
972 for (i = 0; i < Resolution.Y; i++)
973 {
974 /* Loop through the characters */
975 for (j = 0; j < Resolution.X; j++)
976 {
977 CurrentAddr = LOWORD((Address + j) * AddressSize);
978
979 /* Store the character in plane 0 */
980 VgaMemory[CurrentAddr] = CharBuffer[i * Resolution.X + j].Char.AsciiChar;
981
982 /* Store the attribute in plane 1 */
983 VgaMemory[CurrentAddr + VGA_BANK_SIZE] = (BYTE)CharBuffer[i * Resolution.X + j].Attributes;
984 }
985
986 /* Move to the next scanline */
987 Address += ScanlineSize;
988 }
989 }
990
991 /* EOF */