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