2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: VGA hardware emulation
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
16 /* PRIVATE VARIABLES **********************************************************/
18 static CONST DWORD MemoryBase
[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
19 static CONST DWORD MemoryLimit
[] = { 0xAFFFF, 0xAFFFF, 0xB7FFF, 0xBFFFF };
21 static BYTE VgaMemory
[VGA_NUM_BANKS
* VGA_BANK_SIZE
];
22 static BYTE VgaLatchRegisters
[VGA_NUM_BANKS
] = {0, 0, 0, 0};
23 static BYTE VgaMiscRegister
;
24 static BYTE VgaSeqIndex
= VGA_SEQ_RESET_REG
;
25 static BYTE VgaSeqRegisters
[VGA_SEQ_MAX_REG
];
26 static BYTE VgaGcIndex
= VGA_GC_RESET_REG
;
27 static BYTE VgaGcRegisters
[VGA_GC_MAX_REG
];
28 static BYTE VgaCrtcIndex
= VGA_CRTC_HORZ_TOTAL_REG
;
29 static BYTE VgaCrtcRegisters
[VGA_CRTC_MAX_REG
];
30 static BYTE VgaAcIndex
= VGA_AC_PAL_0_REG
;
31 static BOOLEAN VgaAcLatch
= FALSE
;
32 static BYTE VgaAcRegisters
[VGA_AC_MAX_REG
];
33 static BYTE VgaDacIndex
= 0;
34 static BOOLEAN VgaDacReadWrite
= FALSE
;
35 static BYTE VgaDacRegisters
[VGA_PALETTE_SIZE
];
36 static BOOLEAN InVerticalRetrace
= FALSE
;
37 static BOOLEAN InHorizontalRetrace
= FALSE
;
38 static HANDLE TextConsoleBuffer
= NULL
;
39 static HANDLE GraphicsConsoleBuffer
= NULL
;
40 static LPVOID ConsoleFramebuffer
= NULL
;
41 static HANDLE ConsoleMutex
= NULL
;
42 static BOOLEAN NeedsUpdate
= FALSE
;
43 static BOOLEAN ModeChanged
= TRUE
;
44 static BOOLEAN CursorMoved
= FALSE
;
45 static BOOLEAN TextMode
= TRUE
;
46 static SMALL_RECT UpdateRectangle
= { 0, 0, 0, 0 };
48 /* PRIVATE FUNCTIONS **********************************************************/
50 static inline INT
VgaGetAddressSize(VOID
)
52 if (VgaCrtcRegisters
[VGA_CRTC_UNDERLINE_REG
] & VGA_CRTC_UNDERLINE_DWORD
)
54 /* Double-word addressing */
58 if (VgaCrtcRegisters
[VGA_CRTC_MODE_CONTROL_REG
] & VGA_CRTC_MODE_CONTROL_BYTE
)
68 static inline DWORD
VgaTranslateReadAddress(DWORD Address
)
70 DWORD Offset
= Address
- VgaGetVideoBaseAddress();
73 /* Check for chain-4 and odd-even mode */
74 if (VgaSeqRegisters
[VGA_SEQ_MEM_REG
] & VGA_SEQ_MEM_C4
)
76 /* The lowest two bits are the plane number */
80 else if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_OE
)
82 /* The LSB is the plane number */
88 /* Use the read mode */
89 Plane
= VgaGcRegisters
[VGA_GC_READ_MAP_SEL_REG
] & 0x03;
92 /* Multiply the offset by the address size */
93 Offset
*= VgaGetAddressSize();
95 return Offset
+ Plane
* VGA_BANK_SIZE
;
98 static inline DWORD
VgaTranslateWriteAddress(DWORD Address
)
100 DWORD Offset
= Address
- VgaGetVideoBaseAddress();
102 /* Check for chain-4 and odd-even mode */
103 if (VgaSeqRegisters
[VGA_SEQ_MEM_REG
] & VGA_SEQ_MEM_C4
)
105 /* Shift the offset to the right by 2 */
108 else if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_OE
)
110 /* Shift the offset to the right by 1 */
114 /* Multiply the offset by the address size */
115 Offset
*= VgaGetAddressSize();
117 /* Return the offset on plane 0 */
121 static inline BYTE
VgaTranslateByteForWriting(BYTE Data
, BYTE Plane
)
123 BYTE WriteMode
= VgaGcRegisters
[VGA_GC_MODE_REG
] & 3;
124 BYTE LogicalOperation
= (VgaGcRegisters
[VGA_GC_ROTATE_REG
] >> 3) & 3;
125 BYTE RotateCount
= VgaGcRegisters
[VGA_GC_ROTATE_REG
] & 7;
126 BYTE BitMask
= VgaGcRegisters
[VGA_GC_BITMASK_REG
];
130 /* In write mode 1 just return the latch register */
131 return VgaLatchRegisters
[Plane
];
136 /* Write modes 0 and 3 rotate the data to the right first */
137 Data
= LOBYTE(((DWORD
)Data
>> RotateCount
) | ((DWORD
)Data
<< (8 - RotateCount
)));
141 /* Write mode 2 expands the appropriate bit to all 8 bits */
142 Data
= (Data
& (1 << Plane
)) ? 0xFF : 0x00;
148 * In write mode 0, the enable set/reset register decides if the
149 * set/reset bit should be expanded to all 8 bits.
151 if (VgaGcRegisters
[VGA_GC_ENABLE_RESET_REG
] & (1 << Plane
))
153 /* Copy the bit from the set/reset register to all 8 bits */
154 Data
= (VgaGcRegisters
[VGA_GC_RESET_REG
] & (1 << Plane
)) ? 0xFF : 0x00;
160 /* Write modes 0 and 2 then perform a logical operation on the data and latch */
161 if (LogicalOperation
== 1) Data
&= VgaLatchRegisters
[Plane
];
162 else if (LogicalOperation
== 2) Data
|= VgaLatchRegisters
[Plane
];
163 else if (LogicalOperation
== 3) Data
^= VgaLatchRegisters
[Plane
];
167 /* For write mode 3, we AND the bitmask with the data, which is used as the new bitmask */
170 /* Then we expand the bit in the set/reset field */
171 Data
= (VgaGcRegisters
[VGA_GC_RESET_REG
] & (1 << Plane
)) ? 0xFF : 0x00;
174 /* Bits cleared in the bitmask are replaced with latch register bits */
175 Data
= (Data
& BitMask
) | (VgaLatchRegisters
[Plane
] & (~BitMask
));
177 /* Return the byte */
181 static inline VOID
VgaMarkForUpdate(SHORT Row
, SHORT Column
)
183 DPRINT("VgaMarkForUpdate: Row %d, Column %d\n", Row
, Column
);
185 /* Check if this is the first time the rectangle is updated */
188 UpdateRectangle
.Left
= UpdateRectangle
.Top
= SHRT_MAX
;
189 UpdateRectangle
.Right
= UpdateRectangle
.Bottom
= SHRT_MIN
;
192 /* Expand the rectangle to include the point */
193 UpdateRectangle
.Left
= min(UpdateRectangle
.Left
, Column
);
194 UpdateRectangle
.Right
= max(UpdateRectangle
.Right
, Column
);
195 UpdateRectangle
.Top
= min(UpdateRectangle
.Top
, Row
);
196 UpdateRectangle
.Bottom
= max(UpdateRectangle
.Bottom
, Row
);
198 /* Set the update request flag */
202 static VOID
VgaWriteSequencer(BYTE Data
)
204 ASSERT(VgaSeqIndex
< VGA_SEQ_MAX_REG
);
207 VgaSeqRegisters
[VgaSeqIndex
] = Data
;
210 static VOID
VgaWriteGc(BYTE Data
)
212 ASSERT(VgaGcIndex
< VGA_GC_MAX_REG
);
215 VgaGcRegisters
[VgaGcIndex
] = Data
;
217 /* Check the index */
220 case VGA_GC_MISC_REG
:
222 /* The GC misc register decides if it's text or graphics mode */
230 static VOID
VgaWriteCrtc(BYTE Data
)
232 ASSERT(VgaGcIndex
< VGA_CRTC_MAX_REG
);
235 VgaCrtcRegisters
[VgaCrtcIndex
] = Data
;
237 /* Check the index */
238 switch (VgaCrtcIndex
)
240 case VGA_CRTC_END_HORZ_DISP_REG
:
241 case VGA_CRTC_VERT_DISP_END_REG
:
242 case VGA_CRTC_OVERFLOW_REG
:
244 /* The video mode has changed */
250 case VGA_CRTC_CURSOR_LOC_LOW_REG
:
251 case VGA_CRTC_CURSOR_LOC_HIGH_REG
:
252 case VGA_CRTC_CURSOR_START_REG
:
253 case VGA_CRTC_CURSOR_END_REG
:
255 /* Set the cursor moved flag */
263 static VOID
VgaWriteDac(BYTE Data
)
266 VgaDacRegisters
[VgaDacIndex
++] = Data
;
267 VgaDacIndex
%= VGA_PALETTE_SIZE
;
269 // TODO: Change the palette!
272 static VOID
VgaWriteAc(BYTE Data
)
274 ASSERT(VgaAcIndex
< VGA_AC_MAX_REG
);
277 VgaAcRegisters
[VgaAcIndex
] = Data
;
280 static BOOL
VgaEnterGraphicsMode(PCOORD Resolution
)
283 CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo
;
284 BYTE BitmapInfoBuffer
[VGA_BITMAP_INFO_SIZE
];
285 LPBITMAPINFO BitmapInfo
= (LPBITMAPINFO
)BitmapInfoBuffer
;
286 LPWORD PaletteIndex
= (LPWORD
)(BitmapInfoBuffer
+ sizeof(BITMAPINFOHEADER
));
288 /* Fill the bitmap info header */
289 ZeroMemory(&BitmapInfo
->bmiHeader
, sizeof(BITMAPINFOHEADER
));
290 BitmapInfo
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
291 BitmapInfo
->bmiHeader
.biWidth
= Resolution
->X
;
292 BitmapInfo
->bmiHeader
.biHeight
= Resolution
->Y
;
293 BitmapInfo
->bmiHeader
.biBitCount
= 8;
294 BitmapInfo
->bmiHeader
.biPlanes
= 1;
295 BitmapInfo
->bmiHeader
.biCompression
= BI_RGB
;
296 BitmapInfo
->bmiHeader
.biSizeImage
= Resolution
->X
* Resolution
->Y
;
298 /* Fill the palette data */
299 for (i
= 0; i
< (VGA_PALETTE_SIZE
/ 3); i
++) PaletteIndex
[i
] = (WORD
)i
;
301 /* Fill the console graphics buffer info */
302 GraphicsBufferInfo
.dwBitMapInfoLength
= VGA_BITMAP_INFO_SIZE
;
303 GraphicsBufferInfo
.lpBitMapInfo
= BitmapInfo
;
304 GraphicsBufferInfo
.dwUsage
= DIB_PAL_COLORS
;
306 /* Create the buffer */
307 GraphicsConsoleBuffer
= CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
308 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
310 CONSOLE_GRAPHICS_BUFFER
,
311 &GraphicsBufferInfo
);
312 if (GraphicsConsoleBuffer
== INVALID_HANDLE_VALUE
) return FALSE
;
314 /* Save the framebuffer address and mutex */
315 ConsoleFramebuffer
= GraphicsBufferInfo
.lpBitMap
;
316 ConsoleMutex
= GraphicsBufferInfo
.hMutex
;
318 /* Clear the framebuffer */
319 ZeroMemory(ConsoleFramebuffer
, BitmapInfo
->bmiHeader
.biSizeImage
);
321 /* Set the active buffer */
322 SetConsoleActiveScreenBuffer(GraphicsConsoleBuffer
);
327 static VOID
VgaLeaveGraphicsMode(VOID
)
329 /* Release the console framebuffer mutex if needed */
330 ReleaseMutex(ConsoleMutex
);
332 /* Switch back to the text buffer */
333 SetConsoleActiveScreenBuffer(TextConsoleBuffer
);
335 /* Cleanup the video data */
336 CloseHandle(ConsoleMutex
);
338 CloseHandle(GraphicsConsoleBuffer
);
339 GraphicsConsoleBuffer
= NULL
;
342 static BOOL
VgaEnterTextMode(PCOORD Resolution
)
344 /* Resize the console */
345 SetConsoleScreenBufferSize(TextConsoleBuffer
, *Resolution
);
347 /* Allocate a framebuffer */
348 ConsoleFramebuffer
= HeapAlloc(GetProcessHeap(),
350 Resolution
->X
* Resolution
->Y
351 * sizeof(CHAR_INFO
));
352 if (ConsoleFramebuffer
== NULL
)
354 DisplayMessage(L
"An unexpected error occurred!\n");
362 static VOID
VgaLeaveTextMode(VOID
)
364 /* Free the old framebuffer */
365 HeapFree(GetProcessHeap(), 0, ConsoleFramebuffer
);
366 ConsoleFramebuffer
= NULL
;
369 static VOID
VgaUpdateMode(VOID
)
371 COORD Resolution
= VgaGetDisplayResolution();
375 /* Leave the current graphics mode */
376 VgaLeaveGraphicsMode();
380 /* Leave the current text mode */
384 /* Check if the new mode is alphanumeric */
385 if (!(VgaGcRegisters
[VGA_GC_MISC_REG
] & VGA_GC_MISC_NOALPHA
))
387 /* Enter new text mode */
388 if (!VgaEnterTextMode(&Resolution
)) return;
390 /* Set the text mode flag */
395 /* Enter 8-bit graphics mode */
396 if (!VgaEnterGraphicsMode(&Resolution
)) return;
398 /* Clear the text mode flag */
402 /* Perform a full update */
404 UpdateRectangle
.Left
= 0;
405 UpdateRectangle
.Top
= 0;
406 UpdateRectangle
.Right
= Resolution
.X
;
407 UpdateRectangle
.Bottom
= Resolution
.Y
;
410 static VOID
VgaUpdateFramebuffer(VOID
)
413 COORD Resolution
= VgaGetDisplayResolution();
414 INT AddressSize
= VgaGetAddressSize();
415 DWORD Address
= (VgaCrtcRegisters
[VGA_CRTC_START_ADDR_HIGH_REG
] << 8)
416 + VgaCrtcRegisters
[VGA_CRTC_START_ADDR_LOW_REG
];
417 DWORD ScanlineSize
= (DWORD
)VgaCrtcRegisters
[VGA_CRTC_OFFSET_REG
] * 2;
419 /* Check if this is text mode or graphics mode */
420 if (VgaGcRegisters
[VGA_GC_MISC_REG
] & VGA_GC_MISC_NOALPHA
)
423 PBYTE GraphicsBuffer
= (PBYTE
)ConsoleFramebuffer
;
426 * Synchronize access to the graphics framebuffer
427 * with the console framebuffer mutex.
429 WaitForSingleObject(ConsoleMutex
, INFINITE
);
431 /* Loop through the scanlines */
432 for (i
= 0; i
< Resolution
.Y
; i
++)
434 /* Loop through the pixels */
435 for (j
= 0; j
< Resolution
.X
; j
++)
439 /* Check the shifting mode */
440 if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_SHIFT256
)
442 /* 4 bits shifted from each plane */
444 /* Check if this is 16 or 256 color mode */
445 if (VgaAcRegisters
[VGA_AC_CONTROL_REG
] & VGA_AC_CONTROL_8BIT
)
447 /* One byte per pixel */
448 PixelData
= VgaMemory
[(j
% VGA_NUM_BANKS
) * VGA_BANK_SIZE
449 + (Address
+ (j
/ VGA_NUM_BANKS
))
454 /* 4-bits per pixel */
456 PixelData
= VgaMemory
[(j
% VGA_NUM_BANKS
) * VGA_BANK_SIZE
457 + (Address
+ (j
/ (VGA_NUM_BANKS
* 2)))
460 /* Check if we should use the highest 4 bits or lowest 4 */
461 if (((j
/ VGA_NUM_BANKS
) % 2) == 0)
473 else if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_SHIFTREG
)
476 * 2 bits shifted from plane 0 and 2 for the first 4 pixels,
477 * then 2 bits shifted from plane 1 and 3 for the next 4
480 // TODO: NOT IMPLEMENTED!
481 DPRINT1("Interleaved shift mode is not implemented!\n");
485 /* 1 bit shifted from each plane */
487 /* Check if this is 16 or 256 color mode */
488 if (VgaAcRegisters
[VGA_AC_CONTROL_REG
] & VGA_AC_CONTROL_8BIT
)
490 /* 8 bits per pixel, 2 on each plane */
492 for (k
= 0; k
< VGA_NUM_BANKS
; k
++)
494 /* The data is on plane k, 4 pixels per byte */
495 BYTE PlaneData
= VgaMemory
[k
* VGA_BANK_SIZE
496 + (Address
+ (j
/ 4)) * AddressSize
];
498 /* The mask of the first bit in the pair */
499 BYTE BitMask
= 1 << (((3 - (j
% 4)) * 2) + 1);
501 /* Bits 0, 1, 2 and 3 come from the first bit of the pair */
502 if (PlaneData
& BitMask
) PixelData
|= 1 << k
;
504 /* Bits 4, 5, 6 and 7 come from the second bit of the pair */
505 if (PlaneData
& (BitMask
>> 1)) PixelData
|= 1 << (k
+ 4);
510 /* 4 bits per pixel, 1 on each plane */
512 for (k
= 0; k
< VGA_NUM_BANKS
; k
++)
514 BYTE PlaneData
= VgaMemory
[k
* VGA_BANK_SIZE
515 + (Address
+ (j
/ 8)) * AddressSize
];
517 /* If the bit on that plane is set, set it */
518 if (PlaneData
& (1 << (7 - (j
% 8)))) PixelData
|= 1 << k
;
523 /* Now check if the resulting pixel data has changed */
524 if (GraphicsBuffer
[i
* Resolution
.X
+ j
] != PixelData
)
526 /* Yes, write the new value */
527 GraphicsBuffer
[i
* Resolution
.X
+ j
] = PixelData
;
529 /* Mark the specified pixel as changed */
530 VgaMarkForUpdate(i
, j
);
534 /* Move to the next scanline */
535 Address
+= ScanlineSize
;
539 * Release the console framebuffer mutex
540 * so that we allow for repainting.
542 ReleaseMutex(ConsoleMutex
);
547 PCHAR_INFO CharBuffer
= (PCHAR_INFO
)ConsoleFramebuffer
;
549 /* Loop through the scanlines */
550 for (i
= 0; i
< Resolution
.Y
; i
++)
552 /* Loop through the characters */
553 for (j
= 0; j
< Resolution
.X
; j
++)
555 DWORD CurrentAddr
= LOWORD((Address
+ j
) * AddressSize
);
558 /* Plane 0 holds the character itself */
559 CharInfo
.Char
.AsciiChar
= VgaMemory
[CurrentAddr
];
561 /* Plane 1 holds the attribute */
562 CharInfo
.Attributes
= VgaMemory
[CurrentAddr
+ VGA_BANK_SIZE
];
564 /* Now check if the resulting character data has changed */
565 if ((CharBuffer
[i
* Resolution
.X
+ j
].Char
.AsciiChar
!= CharInfo
.Char
.AsciiChar
)
566 || (CharBuffer
[i
* Resolution
.X
+ j
].Attributes
!= CharInfo
.Attributes
))
568 /* Yes, write the new value */
569 CharBuffer
[i
* Resolution
.X
+ j
] = CharInfo
;
571 /* Mark the specified pixel as changed */
572 VgaMarkForUpdate(i
, j
);
576 /* Move to the next scanline */
577 Address
+= ScanlineSize
;
582 static VOID
VgaUpdateTextCursor(VOID
)
585 CONSOLE_CURSOR_INFO CursorInfo
;
586 BYTE CursorStart
= VgaCrtcRegisters
[VGA_CRTC_CURSOR_START_REG
] & 0x3F;
587 BYTE CursorEnd
= VgaCrtcRegisters
[VGA_CRTC_CURSOR_END_REG
] & 0x1F;
588 DWORD ScanlineSize
= (DWORD
)VgaCrtcRegisters
[VGA_CRTC_OFFSET_REG
] * 2;
589 BYTE TextSize
= 1 + (VgaCrtcRegisters
[VGA_CRTC_MAX_SCAN_LINE_REG
] & 0x1F);
590 WORD Location
= MAKEWORD(VgaCrtcRegisters
[VGA_CRTC_CURSOR_LOC_LOW_REG
],
591 VgaCrtcRegisters
[VGA_CRTC_CURSOR_LOC_HIGH_REG
]);
593 if (CursorStart
< CursorEnd
)
596 CursorInfo
.bVisible
= TRUE
;
597 CursorInfo
.dwSize
= (100 * (CursorEnd
- CursorStart
)) / TextSize
;
602 CursorInfo
.bVisible
= FALSE
;
603 CursorInfo
.dwSize
= 0;
606 /* Add the cursor skew to the location */
607 Location
+= (VgaCrtcRegisters
[VGA_CRTC_CURSOR_END_REG
] >> 5) & 3;
609 /* Find the coordinates of the new position */
610 Position
.X
= (WORD
)(Location
% ScanlineSize
);
611 Position
.Y
= (WORD
)(Location
/ ScanlineSize
);
613 /* Update the physical cursor */
614 SetConsoleCursorInfo(TextConsoleBuffer
, &CursorInfo
);
615 SetConsoleCursorPosition(TextConsoleBuffer
, Position
);
618 /* PUBLIC FUNCTIONS ***********************************************************/
620 DWORD
VgaGetVideoBaseAddress(VOID
)
622 return MemoryBase
[(VgaGcRegisters
[VGA_GC_MISC_REG
] >> 2) & 0x03];
625 DWORD
VgaGetVideoLimitAddress(VOID
)
627 return MemoryLimit
[(VgaGcRegisters
[VGA_GC_MISC_REG
] >> 2) & 0x03];
630 COORD
VgaGetDisplayResolution(VOID
)
633 BYTE MaximumScanLine
= 1 + (VgaCrtcRegisters
[VGA_CRTC_MAX_SCAN_LINE_REG
] & 0x1F);
635 /* The low 8 bits are in the display registers */
636 Resolution
.X
= VgaCrtcRegisters
[VGA_CRTC_END_HORZ_DISP_REG
];
637 Resolution
.Y
= VgaCrtcRegisters
[VGA_CRTC_VERT_DISP_END_REG
];
639 /* Set the top bits from the overflow register */
640 if (VgaCrtcRegisters
[VGA_CRTC_OVERFLOW_REG
] & VGA_CRTC_OVERFLOW_VDE8
)
642 Resolution
.Y
|= 1 << 8;
644 if (VgaCrtcRegisters
[VGA_CRTC_OVERFLOW_REG
] & VGA_CRTC_OVERFLOW_VDE9
)
646 Resolution
.Y
|= 1 << 9;
649 /* Increase the values by 1 */
653 if (VgaGcRegisters
[VGA_GC_MISC_REG
] & VGA_GC_MISC_NOALPHA
)
655 /* Multiply the horizontal resolution by the 9/8 dot mode */
656 Resolution
.X
*= (VgaSeqRegisters
[VGA_SEQ_CLOCK_REG
] & VGA_SEQ_CLOCK_98DM
)
659 /* The horizontal resolution is halved in 8-bit mode */
660 if (VgaAcRegisters
[VGA_AC_CONTROL_REG
] & VGA_AC_CONTROL_8BIT
) Resolution
.X
/= 2;
663 /* Divide the vertical resolution by the maximum scan line (== font size in text mode) */
664 Resolution
.Y
/= MaximumScanLine
;
666 /* Return the resolution */
670 VOID
VgaRefreshDisplay(VOID
)
672 COORD Resolution
= VgaGetDisplayResolution();
674 DPRINT("VgaRefreshDisplay\n");
678 /* Change the display mode */
681 /* Reset the mode change flag */
687 /* Change the text cursor location */
688 VgaUpdateTextCursor();
690 /* Reset the cursor move flag */
694 /* Update the contents of the framebuffer */
695 VgaUpdateFramebuffer();
697 /* Set the vertical retrace flag */
698 InVerticalRetrace
= TRUE
;
700 /* Ignore if there's nothing to update */
701 if (!NeedsUpdate
) return;
703 DPRINT("Updating screen rectangle (%d, %d, %d, %d)\n",
704 UpdateRectangle
.Left
,
706 UpdateRectangle
.Right
,
707 UpdateRectangle
.Bottom
);
709 /* Check if this is text mode or graphics mode */
710 if (VgaGcRegisters
[VGA_GC_MISC_REG
] & VGA_GC_MISC_NOALPHA
)
714 /* Redraw the screen */
715 InvalidateConsoleDIBits(GraphicsConsoleBuffer
, &UpdateRectangle
);
720 COORD Origin
= { UpdateRectangle
.Left
, UpdateRectangle
.Top
};
722 /* Write the data to the console */
723 WriteConsoleOutputA(TextConsoleBuffer
,
724 (PCHAR_INFO
)ConsoleFramebuffer
,
731 /* Clear the update flag */
735 VOID
VgaHorizontalRetrace(VOID
)
738 InHorizontalRetrace
= TRUE
;
741 VOID
VgaReadMemory(DWORD Address
, LPBYTE Buffer
, DWORD Size
)
746 DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n",
750 /* Ignore if video RAM access is disabled */
751 if (!(VgaMiscRegister
& VGA_MISC_RAM_ENABLED
)) return;
753 /* Loop through each byte */
754 for (i
= 0; i
< Size
; i
++)
756 VideoAddress
= VgaTranslateReadAddress(Address
+ i
);
758 /* Load the latch registers */
759 VgaLatchRegisters
[0] = VgaMemory
[LOWORD(VideoAddress
)];
760 VgaLatchRegisters
[1] = VgaMemory
[VGA_BANK_SIZE
+ LOWORD(VideoAddress
)];
761 VgaLatchRegisters
[2] = VgaMemory
[(2 * VGA_BANK_SIZE
) + LOWORD(VideoAddress
)];
762 VgaLatchRegisters
[3] = VgaMemory
[(3 * VGA_BANK_SIZE
) + LOWORD(VideoAddress
)];
764 /* Copy the value to the buffer */
765 Buffer
[i
] = VgaMemory
[VideoAddress
];
769 VOID
VgaWriteMemory(DWORD Address
, LPBYTE Buffer
, DWORD Size
)
774 DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n",
778 /* Ignore if video RAM access is disabled */
779 if (!(VgaMiscRegister
& VGA_MISC_RAM_ENABLED
)) return;
781 /* Also ignore if write access to all planes is disabled */
782 if ((VgaSeqRegisters
[VGA_SEQ_MASK_REG
] & 0x0F) == 0x00) return;
784 /* Loop through each byte */
785 for (i
= 0; i
< Size
; i
++)
787 VideoAddress
= VgaTranslateWriteAddress(Address
+ i
);
789 for (j
= 0; j
< VGA_NUM_BANKS
; j
++)
791 /* Make sure the page is writeable */
792 if (!(VgaSeqRegisters
[VGA_SEQ_MASK_REG
] & (1 << j
))) continue;
794 /* Check if this is chain-4 mode */
795 if (VgaSeqRegisters
[VGA_SEQ_MEM_REG
] & VGA_SEQ_MEM_C4
)
797 if (((Address
+ i
) & 3) != j
)
799 /* This plane will not be accessed */
804 /* Check if this is odd-even mode */
805 if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_OE
)
807 if (((Address
+ i
) & 1) != (j
& 1))
809 /* This plane will not be accessed */
814 /* Copy the value to the VGA memory */
815 VgaMemory
[VideoAddress
+ j
* VGA_BANK_SIZE
] = VgaTranslateByteForWriting(Buffer
[i
], j
);
820 BYTE
VgaReadPort(WORD Port
)
822 DPRINT("VgaReadPort: Port 0x%04X\n", Port
);
833 return VgaAcRegisters
[VgaAcIndex
];
843 return VgaSeqRegisters
[VgaSeqIndex
];
846 case VGA_DAC_READ_INDEX
:
848 /* This returns the read/write state */
849 return VgaDacReadWrite
? 0 : 3;
852 case VGA_DAC_WRITE_INDEX
:
859 /* Ignore reads in write mode */
860 if (!VgaDacReadWrite
)
862 BYTE Data
= VgaDacRegisters
[VgaDacIndex
++];
863 VgaDacIndex
%= VGA_PALETTE_SIZE
;
872 return VgaMiscRegister
;
882 return VgaCrtcRegisters
[VgaCrtcIndex
];
892 return VgaGcRegisters
[VgaGcIndex
];
900 /* Reset the AC latch */
903 /* Set a flag if there is a vertical or horizontal retrace */
904 if (InVerticalRetrace
|| InHorizontalRetrace
) Result
|= VGA_STAT_DD
;
906 /* Set an additional flag if there was a vertical retrace */
907 if (InVerticalRetrace
) Result
|= VGA_STAT_VRETRACE
;
909 /* Clear the flags */
910 InHorizontalRetrace
= InVerticalRetrace
= FALSE
;
919 VOID
VgaWritePort(WORD Port
, BYTE Data
)
921 DPRINT("VgaWritePort: Port 0x%04X, Data 0x%02X\n", Port
, Data
);
929 /* Change the index */
930 if (Data
< VGA_AC_MAX_REG
) VgaAcIndex
= Data
;
938 /* Toggle the latch */
939 VgaAcLatch
= !VgaAcLatch
;
946 /* Set the sequencer index register */
947 if (Data
< VGA_SEQ_MAX_REG
) VgaSeqIndex
= Data
;
954 /* Call the sequencer function */
955 VgaWriteSequencer(Data
);
960 case VGA_DAC_READ_INDEX
:
962 VgaDacReadWrite
= FALSE
;
963 VgaDacIndex
= Data
% VGA_PALETTE_SIZE
;
968 case VGA_DAC_WRITE_INDEX
:
970 VgaDacReadWrite
= TRUE
;
971 VgaDacIndex
= Data
% VGA_PALETTE_SIZE
;
978 /* Ignore writes in read mode */
979 if (VgaDacReadWrite
) VgaWriteDac(Data
& 0x3F);
986 VgaMiscRegister
= Data
;
993 /* Set the CRTC index register */
994 if (Data
< VGA_CRTC_MAX_REG
) VgaCrtcIndex
= Data
;
1001 /* Call the CRTC function */
1009 /* Set the GC index register */
1010 if (Data
< VGA_GC_MAX_REG
) VgaGcIndex
= Data
;
1016 /* Call the GC function */
1024 VOID
VgaClearMemory(VOID
)
1026 ZeroMemory(VgaMemory
, sizeof(VgaMemory
));
1029 VOID
VgaInitialize(HANDLE TextHandle
)
1035 COORD Origin
= { 0, 0 };
1036 SMALL_RECT ScreenRect
;
1037 PCHAR_INFO CharBuffer
;
1041 /* Set the global handle */
1042 TextConsoleBuffer
= TextHandle
;
1044 /* Clear the VGA memory */
1045 ZeroMemory(VgaMemory
, VGA_NUM_BANKS
* VGA_BANK_SIZE
);
1047 /* Set the default video mode */
1048 BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE
);
1050 ModeChanged
= FALSE
;
1053 Resolution
= VgaGetDisplayResolution();
1054 CharBuffer
= (PCHAR_INFO
)ConsoleFramebuffer
;
1055 AddressSize
= VgaGetAddressSize();
1056 ScreenRect
.Left
= ScreenRect
.Top
= 0;
1057 ScreenRect
.Right
= Resolution
.X
;
1058 ScreenRect
.Bottom
= Resolution
.Y
;
1059 ScanlineSize
= (DWORD
)VgaCrtcRegisters
[VGA_CRTC_OFFSET_REG
] * 2;
1061 /* Read the data from the console into the framebuffer */
1062 ReadConsoleOutputA(TextConsoleBuffer
,
1068 /* Loop through the scanlines */
1069 for (i
= 0; i
< Resolution
.Y
; i
++)
1071 /* Loop through the characters */
1072 for (j
= 0; j
< Resolution
.X
; j
++)
1074 CurrentAddr
= LOWORD((Address
+ j
) * AddressSize
);
1076 /* Store the character in plane 0 */
1077 VgaMemory
[CurrentAddr
] = CharBuffer
[i
* Resolution
.X
+ j
].Char
.AsciiChar
;
1079 /* Store the attribute in plane 1 */
1080 VgaMemory
[CurrentAddr
+ VGA_BANK_SIZE
] = (BYTE
)CharBuffer
[i
* Resolution
.X
+ j
].Attributes
;
1083 /* Move to the next scanline */
1084 Address
+= ScanlineSize
;