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