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