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 CONST COLORREF VgaDefaultPalette
[VGA_MAX_COLORS
] =
23 0x000000, 0x0000AA, 0x00AA00, 0x00AAAA, 0xAA0000, 0xAA00AA, 0xAA5500, 0xAAAAAA,
24 0x555555, 0x5555FF, 0x55FF55, 0x55FFFF, 0xFF5555, 0xFF55FF, 0xFFFF55, 0xFFFFFF,
25 0x000000, 0x101010, 0x202020, 0x353535, 0x454545, 0x555555, 0x656565, 0x757575,
26 0x8A8A8A, 0x9A9A9A, 0xAAAAAA, 0xBABABA, 0xCACACA, 0xDFDFDF, 0xEFEFEF, 0xFFFFFF,
27 0x0000FF, 0x4100FF, 0x8200FF, 0xBE00FF, 0xFF00FF, 0xFF00BE, 0xFF0082, 0xFF0041,
28 0xFF0000, 0xFF4100, 0xFF8200, 0xFFBE00, 0xFFFF00, 0xBEFF00, 0x82FF00, 0x41FF00,
29 0x00FF00, 0x00FF41, 0x00FF82, 0x00FFBE, 0x00FFFF, 0x00BEFF, 0x0082FF, 0x0041FF,
30 0x8282FF, 0x9E82FF, 0xBE82FF, 0xDF82FF, 0xFF82FF, 0xFF82DF, 0xFF82BE, 0xFF829E,
31 0xFF8282, 0xFF9E82, 0xFFBE82, 0xFFDF82, 0xFFFF82, 0xDFFF82, 0xBEFF82, 0x9EFF82,
32 0x82FF82, 0x82FF9E, 0x82FFBE, 0x82FFDF, 0x82FFFF, 0x82DFFF, 0x82BEFF, 0x829EFF,
33 0xBABAFF, 0xCABAFF, 0xDFBAFF, 0xEFBAFF, 0xFFBAFF, 0xFFBAEF, 0xFFBADF, 0xFFBACA,
34 0xFFBABA, 0xFFCABA, 0xFFDFBA, 0xFFEFBA, 0xFFFFBA, 0xEFFFBA, 0xDFFFBA, 0xCAFFBA,
35 0xBAFFBA, 0xBAFFCA, 0xBAFFDF, 0xBAFFEF, 0xBAFFFF, 0xBAEFFF, 0xBADFFF, 0xBACAFF,
36 0x000071, 0x1C0071, 0x390071, 0x550071, 0x710071, 0x710055, 0x710039, 0x71001C,
37 0x710000, 0x711C00, 0x713900, 0x715500, 0x717100, 0x557100, 0x397100, 0x1C7100,
38 0x007100, 0x00711C, 0x007139, 0x007155, 0x007171, 0x005571, 0x003971, 0x001C71,
39 0x393971, 0x453971, 0x553971, 0x613971, 0x713971, 0x713961, 0x713955, 0x713945,
40 0x713939, 0x714539, 0x715539, 0x716139, 0x717139, 0x617139, 0x557139, 0x457139,
41 0x397139, 0x397145, 0x397155, 0x397161, 0x397171, 0x396171, 0x395571, 0x394571,
42 0x515171, 0x595171, 0x615171, 0x695171, 0x715171, 0x715169, 0x715161, 0x715159,
43 0x715151, 0x715951, 0x716151, 0x716951, 0x717151, 0x697151, 0x617151, 0x597151,
44 0x517151, 0x517159, 0x517161, 0x517169, 0x517171, 0x516971, 0x516171, 0x515971,
45 0x000041, 0x100041, 0x200041, 0x310041, 0x410041, 0x410031, 0x410020, 0x410010,
46 0x410000, 0x411000, 0x412000, 0x413100, 0x414100, 0x314100, 0x204100, 0x104100,
47 0x004100, 0x004110, 0x004120, 0x004131, 0x004141, 0x003141, 0x002041, 0x001041,
48 0x202041, 0x282041, 0x312041, 0x392041, 0x412041, 0x412039, 0x412031, 0x412028,
49 0x412020, 0x412820, 0x413120, 0x413920, 0x414120, 0x394120, 0x314120, 0x284120,
50 0x204120, 0x204128, 0x204131, 0x204139, 0x204141, 0x203941, 0x203141, 0x202841,
51 0x2D2D41, 0x312D41, 0x352D41, 0x3D2D41, 0x412D41, 0x412D3D, 0x412D35, 0x412D31,
52 0x412D2D, 0x41312D, 0x41352D, 0x413D2D, 0x41412D, 0x3D412D, 0x35412D, 0x31412D,
53 0x2D412D, 0x2D4131, 0x2D4135, 0x2D413D, 0x2D4141, 0x2D3D41, 0x2D3541, 0x2D3141,
54 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000
57 static BYTE VgaMemory
[VGA_NUM_BANKS
* VGA_BANK_SIZE
];
58 static BYTE VgaLatchRegisters
[VGA_NUM_BANKS
] = {0, 0, 0, 0};
59 static BYTE VgaMiscRegister
;
60 static BYTE VgaSeqIndex
= VGA_SEQ_RESET_REG
;
61 static BYTE VgaSeqRegisters
[VGA_SEQ_MAX_REG
];
62 static BYTE VgaGcIndex
= VGA_GC_RESET_REG
;
63 static BYTE VgaGcRegisters
[VGA_GC_MAX_REG
];
64 static BYTE VgaCrtcIndex
= VGA_CRTC_HORZ_TOTAL_REG
;
65 static BYTE VgaCrtcRegisters
[VGA_CRTC_MAX_REG
];
66 static BYTE VgaAcIndex
= VGA_AC_PAL_0_REG
;
67 static BOOLEAN VgaAcLatch
= FALSE
;
68 static BYTE VgaAcRegisters
[VGA_AC_MAX_REG
];
69 static BYTE VgaDacIndex
= 0;
70 static BOOLEAN VgaDacReadWrite
= FALSE
;
71 static BYTE VgaDacRegisters
[VGA_PALETTE_SIZE
];
72 static HPALETTE PaletteHandle
= NULL
;
73 static BOOLEAN InVerticalRetrace
= FALSE
;
74 static BOOLEAN InHorizontalRetrace
= FALSE
;
75 static HANDLE TextConsoleBuffer
= NULL
;
76 static HANDLE GraphicsConsoleBuffer
= NULL
;
77 static LPVOID ConsoleFramebuffer
= NULL
;
78 static HANDLE ConsoleMutex
= NULL
;
79 static BOOLEAN NeedsUpdate
= FALSE
;
80 static BOOLEAN ModeChanged
= TRUE
;
81 static BOOLEAN CursorMoved
= FALSE
;
82 static BOOLEAN PaletteChanged
= FALSE
;
83 static BOOLEAN TextMode
= TRUE
;
84 static SMALL_RECT UpdateRectangle
= { 0, 0, 0, 0 };
86 /* PRIVATE FUNCTIONS **********************************************************/
88 static inline INT
VgaGetAddressSize(VOID
)
90 if (VgaCrtcRegisters
[VGA_CRTC_UNDERLINE_REG
] & VGA_CRTC_UNDERLINE_DWORD
)
92 /* Double-word addressing */
96 if (VgaCrtcRegisters
[VGA_CRTC_MODE_CONTROL_REG
] & VGA_CRTC_MODE_CONTROL_BYTE
)
102 /* Word addressing */
106 static inline DWORD
VgaTranslateReadAddress(DWORD Address
)
108 DWORD Offset
= Address
- VgaGetVideoBaseAddress();
111 /* Check for chain-4 and odd-even mode */
112 if (VgaSeqRegisters
[VGA_SEQ_MEM_REG
] & VGA_SEQ_MEM_C4
)
114 /* The lowest two bits are the plane number */
118 else if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_OE
)
120 /* The LSB is the plane number */
126 /* Use the read mode */
127 Plane
= VgaGcRegisters
[VGA_GC_READ_MAP_SEL_REG
] & 0x03;
130 /* Multiply the offset by the address size */
131 Offset
*= VgaGetAddressSize();
133 return Offset
+ Plane
* VGA_BANK_SIZE
;
136 static inline DWORD
VgaTranslateWriteAddress(DWORD Address
)
138 DWORD Offset
= Address
- VgaGetVideoBaseAddress();
140 /* Check for chain-4 and odd-even mode */
141 if (VgaSeqRegisters
[VGA_SEQ_MEM_REG
] & VGA_SEQ_MEM_C4
)
143 /* Shift the offset to the right by 2 */
146 else if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_OE
)
148 /* Shift the offset to the right by 1 */
152 /* Multiply the offset by the address size */
153 Offset
*= VgaGetAddressSize();
155 /* Return the offset on plane 0 */
159 static inline BYTE
VgaTranslateByteForWriting(BYTE Data
, BYTE Plane
)
161 BYTE WriteMode
= VgaGcRegisters
[VGA_GC_MODE_REG
] & 3;
162 BYTE LogicalOperation
= (VgaGcRegisters
[VGA_GC_ROTATE_REG
] >> 3) & 3;
163 BYTE RotateCount
= VgaGcRegisters
[VGA_GC_ROTATE_REG
] & 7;
164 BYTE BitMask
= VgaGcRegisters
[VGA_GC_BITMASK_REG
];
168 /* In write mode 1 just return the latch register */
169 return VgaLatchRegisters
[Plane
];
174 /* Write modes 0 and 3 rotate the data to the right first */
175 Data
= LOBYTE(((DWORD
)Data
>> RotateCount
) | ((DWORD
)Data
<< (8 - RotateCount
)));
179 /* Write mode 2 expands the appropriate bit to all 8 bits */
180 Data
= (Data
& (1 << Plane
)) ? 0xFF : 0x00;
186 * In write mode 0, the enable set/reset register decides if the
187 * set/reset bit should be expanded to all 8 bits.
189 if (VgaGcRegisters
[VGA_GC_ENABLE_RESET_REG
] & (1 << Plane
))
191 /* Copy the bit from the set/reset register to all 8 bits */
192 Data
= (VgaGcRegisters
[VGA_GC_RESET_REG
] & (1 << Plane
)) ? 0xFF : 0x00;
198 /* Write modes 0 and 2 then perform a logical operation on the data and latch */
199 if (LogicalOperation
== 1) Data
&= VgaLatchRegisters
[Plane
];
200 else if (LogicalOperation
== 2) Data
|= VgaLatchRegisters
[Plane
];
201 else if (LogicalOperation
== 3) Data
^= VgaLatchRegisters
[Plane
];
205 /* For write mode 3, we AND the bitmask with the data, which is used as the new bitmask */
208 /* Then we expand the bit in the set/reset field */
209 Data
= (VgaGcRegisters
[VGA_GC_RESET_REG
] & (1 << Plane
)) ? 0xFF : 0x00;
212 /* Bits cleared in the bitmask are replaced with latch register bits */
213 Data
= (Data
& BitMask
) | (VgaLatchRegisters
[Plane
] & (~BitMask
));
215 /* Return the byte */
219 static inline VOID
VgaMarkForUpdate(SHORT Row
, SHORT Column
)
221 DPRINT("VgaMarkForUpdate: Row %d, Column %d\n", Row
, Column
);
223 /* Check if this is the first time the rectangle is updated */
226 UpdateRectangle
.Left
= UpdateRectangle
.Top
= SHRT_MAX
;
227 UpdateRectangle
.Right
= UpdateRectangle
.Bottom
= SHRT_MIN
;
230 /* Expand the rectangle to include the point */
231 UpdateRectangle
.Left
= min(UpdateRectangle
.Left
, Column
);
232 UpdateRectangle
.Right
= max(UpdateRectangle
.Right
, Column
);
233 UpdateRectangle
.Top
= min(UpdateRectangle
.Top
, Row
);
234 UpdateRectangle
.Bottom
= max(UpdateRectangle
.Bottom
, Row
);
236 /* Set the update request flag */
240 static VOID
VgaWriteSequencer(BYTE Data
)
242 ASSERT(VgaSeqIndex
< VGA_SEQ_MAX_REG
);
245 VgaSeqRegisters
[VgaSeqIndex
] = Data
;
248 static VOID
VgaWriteGc(BYTE Data
)
250 ASSERT(VgaGcIndex
< VGA_GC_MAX_REG
);
253 VgaGcRegisters
[VgaGcIndex
] = Data
;
255 /* Check the index */
258 case VGA_GC_MISC_REG
:
260 /* The GC misc register decides if it's text or graphics mode */
268 static VOID
VgaWriteCrtc(BYTE Data
)
270 ASSERT(VgaGcIndex
< VGA_CRTC_MAX_REG
);
273 VgaCrtcRegisters
[VgaCrtcIndex
] = Data
;
275 /* Check the index */
276 switch (VgaCrtcIndex
)
278 case VGA_CRTC_END_HORZ_DISP_REG
:
279 case VGA_CRTC_VERT_DISP_END_REG
:
280 case VGA_CRTC_OVERFLOW_REG
:
282 /* The video mode has changed */
288 case VGA_CRTC_CURSOR_LOC_LOW_REG
:
289 case VGA_CRTC_CURSOR_LOC_HIGH_REG
:
290 case VGA_CRTC_CURSOR_START_REG
:
291 case VGA_CRTC_CURSOR_END_REG
:
293 /* Set the cursor moved flag */
301 static VOID
VgaWriteDac(BYTE Data
)
307 VgaDacRegisters
[VgaDacIndex
] = Data
;
309 /* Find the palette index */
310 PaletteIndex
= VgaDacIndex
/ 3;
312 /* Fill the entry structure */
313 Entry
.peRed
= VGA_DAC_TO_COLOR(VgaDacRegisters
[PaletteIndex
* 3]);
314 Entry
.peGreen
= VGA_DAC_TO_COLOR(VgaDacRegisters
[PaletteIndex
* 3 + 1]);
315 Entry
.peBlue
= VGA_DAC_TO_COLOR(VgaDacRegisters
[PaletteIndex
* 3 + 2]);
318 /* Update the palette entry */
319 SetPaletteEntries(PaletteHandle
, PaletteIndex
, 1, &Entry
);
321 /* Set the palette change flag */
322 PaletteChanged
= TRUE
;
324 /* Update the index */
326 VgaDacIndex
%= VGA_PALETTE_SIZE
;
329 static VOID
VgaWriteAc(BYTE Data
)
331 ASSERT(VgaAcIndex
< VGA_AC_MAX_REG
);
334 VgaAcRegisters
[VgaAcIndex
] = Data
;
337 static BOOL
VgaEnterGraphicsMode(PCOORD Resolution
)
340 CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo
;
341 BYTE BitmapInfoBuffer
[VGA_BITMAP_INFO_SIZE
];
342 LPBITMAPINFO BitmapInfo
= (LPBITMAPINFO
)BitmapInfoBuffer
;
343 LPWORD PaletteIndex
= (LPWORD
)(BitmapInfo
->bmiColors
);
345 /* Fill the bitmap info header */
346 ZeroMemory(&BitmapInfo
->bmiHeader
, sizeof(BITMAPINFOHEADER
));
347 BitmapInfo
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
348 BitmapInfo
->bmiHeader
.biWidth
= Resolution
->X
;
349 BitmapInfo
->bmiHeader
.biHeight
= Resolution
->Y
;
350 BitmapInfo
->bmiHeader
.biBitCount
= 8;
351 BitmapInfo
->bmiHeader
.biPlanes
= 1;
352 BitmapInfo
->bmiHeader
.biCompression
= BI_RGB
;
353 BitmapInfo
->bmiHeader
.biSizeImage
= Resolution
->X
* Resolution
->Y
/* * 1 == biBitCount / 8 */;
355 /* Fill the palette data */
356 for (i
= 0; i
< (VGA_PALETTE_SIZE
/ 3); i
++) PaletteIndex
[i
] = (WORD
)i
;
358 /* Fill the console graphics buffer info */
359 GraphicsBufferInfo
.dwBitMapInfoLength
= VGA_BITMAP_INFO_SIZE
;
360 GraphicsBufferInfo
.lpBitMapInfo
= BitmapInfo
;
361 GraphicsBufferInfo
.dwUsage
= DIB_PAL_COLORS
;
363 /* Create the buffer */
364 GraphicsConsoleBuffer
= CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
365 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
367 CONSOLE_GRAPHICS_BUFFER
,
368 &GraphicsBufferInfo
);
369 if (GraphicsConsoleBuffer
== INVALID_HANDLE_VALUE
) return FALSE
;
371 /* Save the framebuffer address and mutex */
372 ConsoleFramebuffer
= GraphicsBufferInfo
.lpBitMap
;
373 ConsoleMutex
= GraphicsBufferInfo
.hMutex
;
375 /* Clear the framebuffer */
376 ZeroMemory(ConsoleFramebuffer
, BitmapInfo
->bmiHeader
.biSizeImage
);
378 /* Set the active buffer */
379 SetConsoleActiveScreenBuffer(GraphicsConsoleBuffer
);
381 /* Set the graphics mode palette */
382 SetConsolePalette(GraphicsConsoleBuffer
,
386 /* Clear the text mode flag */
392 static VOID
VgaLeaveGraphicsMode(VOID
)
394 /* Release the console framebuffer mutex if needed */
395 ReleaseMutex(ConsoleMutex
);
397 /* Switch back to the text buffer */
398 SetConsoleActiveScreenBuffer(TextConsoleBuffer
);
400 /* Cleanup the video data */
401 CloseHandle(ConsoleMutex
);
403 CloseHandle(GraphicsConsoleBuffer
);
404 GraphicsConsoleBuffer
= NULL
;
407 static BOOL
VgaEnterTextMode(PCOORD Resolution
)
409 /* Resize the console */
410 SetConsoleScreenBufferSize(TextConsoleBuffer
, *Resolution
);
412 /* Allocate a framebuffer */
413 ConsoleFramebuffer
= HeapAlloc(GetProcessHeap(),
415 Resolution
->X
* Resolution
->Y
416 * sizeof(CHAR_INFO
));
417 if (ConsoleFramebuffer
== NULL
)
419 DisplayMessage(L
"An unexpected error occurred!\n");
424 /* Set the text mode flag */
430 static VOID
VgaLeaveTextMode(VOID
)
432 /* Free the old framebuffer */
433 HeapFree(GetProcessHeap(), 0, ConsoleFramebuffer
);
434 ConsoleFramebuffer
= NULL
;
437 static VOID
VgaChangeMode(VOID
)
439 COORD Resolution
= VgaGetDisplayResolution();
441 /* Reset the mode change flag */
442 // ModeChanged = FALSE;
446 /* Leave the current graphics mode */
447 VgaLeaveGraphicsMode();
451 /* Leave the current text mode */
455 /* Check if the new mode is alphanumeric */
456 if (!(VgaGcRegisters
[VGA_GC_MISC_REG
] & VGA_GC_MISC_NOALPHA
))
458 /* Enter new text mode */
459 if (!VgaEnterTextMode(&Resolution
)) return;
463 /* Enter 8-bit graphics mode */
464 if (!VgaEnterGraphicsMode(&Resolution
)) return;
467 /* Trigger a full update of the screen */
469 UpdateRectangle
.Left
= 0;
470 UpdateRectangle
.Top
= 0;
471 UpdateRectangle
.Right
= Resolution
.X
;
472 UpdateRectangle
.Bottom
= Resolution
.Y
;
474 /* Reset the mode change flag */
478 static VOID
VgaUpdateFramebuffer(VOID
)
481 COORD Resolution
= VgaGetDisplayResolution();
482 INT AddressSize
= VgaGetAddressSize();
483 DWORD Address
= (VgaCrtcRegisters
[VGA_CRTC_START_ADDR_HIGH_REG
] << 8)
484 + VgaCrtcRegisters
[VGA_CRTC_START_ADDR_LOW_REG
];
485 DWORD ScanlineSize
= (DWORD
)VgaCrtcRegisters
[VGA_CRTC_OFFSET_REG
] * 2;
487 /* Check if this is text mode or graphics mode */
488 if (VgaGcRegisters
[VGA_GC_MISC_REG
] & VGA_GC_MISC_NOALPHA
)
491 PBYTE GraphicsBuffer
= (PBYTE
)ConsoleFramebuffer
;
494 * Synchronize access to the graphics framebuffer
495 * with the console framebuffer mutex.
497 WaitForSingleObject(ConsoleMutex
, INFINITE
);
499 /* Loop through the scanlines */
500 for (i
= 0; i
< Resolution
.Y
; i
++)
502 /* Loop through the pixels */
503 for (j
= 0; j
< Resolution
.X
; j
++)
507 /* Check the shifting mode */
508 if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_SHIFT256
)
510 /* 4 bits shifted from each plane */
512 /* Check if this is 16 or 256 color mode */
513 if (VgaAcRegisters
[VGA_AC_CONTROL_REG
] & VGA_AC_CONTROL_8BIT
)
515 /* One byte per pixel */
516 PixelData
= VgaMemory
[(j
% VGA_NUM_BANKS
) * VGA_BANK_SIZE
517 + (Address
+ (j
/ VGA_NUM_BANKS
))
522 /* 4-bits per pixel */
524 PixelData
= VgaMemory
[(j
% VGA_NUM_BANKS
) * VGA_BANK_SIZE
525 + (Address
+ (j
/ (VGA_NUM_BANKS
* 2)))
528 /* Check if we should use the highest 4 bits or lowest 4 */
529 if (((j
/ VGA_NUM_BANKS
) % 2) == 0)
541 else if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_SHIFTREG
)
544 * 2 bits shifted from plane 0 and 2 for the first 4 pixels,
545 * then 2 bits shifted from plane 1 and 3 for the next 4
548 // TODO: NOT IMPLEMENTED!
549 DPRINT1("Interleaved shift mode is not implemented!\n");
553 /* 1 bit shifted from each plane */
555 /* Check if this is 16 or 256 color mode */
556 if (VgaAcRegisters
[VGA_AC_CONTROL_REG
] & VGA_AC_CONTROL_8BIT
)
558 /* 8 bits per pixel, 2 on each plane */
560 for (k
= 0; k
< VGA_NUM_BANKS
; k
++)
562 /* The data is on plane k, 4 pixels per byte */
563 BYTE PlaneData
= VgaMemory
[k
* VGA_BANK_SIZE
564 + (Address
+ (j
/ 4)) * AddressSize
];
566 /* The mask of the first bit in the pair */
567 BYTE BitMask
= 1 << (((3 - (j
% 4)) * 2) + 1);
569 /* Bits 0, 1, 2 and 3 come from the first bit of the pair */
570 if (PlaneData
& BitMask
) PixelData
|= 1 << k
;
572 /* Bits 4, 5, 6 and 7 come from the second bit of the pair */
573 if (PlaneData
& (BitMask
>> 1)) PixelData
|= 1 << (k
+ 4);
578 /* 4 bits per pixel, 1 on each plane */
580 for (k
= 0; k
< VGA_NUM_BANKS
; k
++)
582 BYTE PlaneData
= VgaMemory
[k
* VGA_BANK_SIZE
583 + (Address
+ (j
/ 8)) * AddressSize
];
585 /* If the bit on that plane is set, set it */
586 if (PlaneData
& (1 << (7 - (j
% 8)))) PixelData
|= 1 << k
;
591 /* Now check if the resulting pixel data has changed */
592 if (GraphicsBuffer
[i
* Resolution
.X
+ j
] != PixelData
)
594 /* Yes, write the new value */
595 GraphicsBuffer
[i
* Resolution
.X
+ j
] = PixelData
;
597 /* Mark the specified pixel as changed */
598 VgaMarkForUpdate(i
, j
);
602 /* Move to the next scanline */
603 Address
+= ScanlineSize
;
607 * Release the console framebuffer mutex
608 * so that we allow for repainting.
610 ReleaseMutex(ConsoleMutex
);
615 PCHAR_INFO CharBuffer
= (PCHAR_INFO
)ConsoleFramebuffer
;
617 /* Loop through the scanlines */
618 for (i
= 0; i
< Resolution
.Y
; i
++)
620 /* Loop through the characters */
621 for (j
= 0; j
< Resolution
.X
; j
++)
623 DWORD CurrentAddr
= LOWORD((Address
+ j
) * AddressSize
);
626 /* Plane 0 holds the character itself */
627 CharInfo
.Char
.AsciiChar
= VgaMemory
[CurrentAddr
];
629 /* Plane 1 holds the attribute */
630 CharInfo
.Attributes
= VgaMemory
[CurrentAddr
+ VGA_BANK_SIZE
];
632 /* Now check if the resulting character data has changed */
633 if ((CharBuffer
[i
* Resolution
.X
+ j
].Char
.AsciiChar
!= CharInfo
.Char
.AsciiChar
)
634 || (CharBuffer
[i
* Resolution
.X
+ j
].Attributes
!= CharInfo
.Attributes
))
636 /* Yes, write the new value */
637 CharBuffer
[i
* Resolution
.X
+ j
] = CharInfo
;
639 /* Mark the specified pixel as changed */
640 VgaMarkForUpdate(i
, j
);
644 /* Move to the next scanline */
645 Address
+= ScanlineSize
;
650 static VOID
VgaUpdateTextCursor(VOID
)
653 CONSOLE_CURSOR_INFO CursorInfo
;
654 BYTE CursorStart
= VgaCrtcRegisters
[VGA_CRTC_CURSOR_START_REG
] & 0x3F;
655 BYTE CursorEnd
= VgaCrtcRegisters
[VGA_CRTC_CURSOR_END_REG
] & 0x1F;
656 DWORD ScanlineSize
= (DWORD
)VgaCrtcRegisters
[VGA_CRTC_OFFSET_REG
] * 2;
657 BYTE TextSize
= 1 + (VgaCrtcRegisters
[VGA_CRTC_MAX_SCAN_LINE_REG
] & 0x1F);
658 WORD Location
= MAKEWORD(VgaCrtcRegisters
[VGA_CRTC_CURSOR_LOC_LOW_REG
],
659 VgaCrtcRegisters
[VGA_CRTC_CURSOR_LOC_HIGH_REG
]);
661 if (CursorStart
< CursorEnd
)
664 CursorInfo
.bVisible
= TRUE
;
665 CursorInfo
.dwSize
= (100 * (CursorEnd
- CursorStart
)) / TextSize
;
670 CursorInfo
.bVisible
= FALSE
;
671 CursorInfo
.dwSize
= 0;
674 /* Add the cursor skew to the location */
675 Location
+= (VgaCrtcRegisters
[VGA_CRTC_CURSOR_END_REG
] >> 5) & 3;
677 /* Find the coordinates of the new position */
678 Position
.X
= (WORD
)(Location
% ScanlineSize
);
679 Position
.Y
= (WORD
)(Location
/ ScanlineSize
);
681 /* Update the physical cursor */
682 SetConsoleCursorInfo(TextConsoleBuffer
, &CursorInfo
);
683 SetConsoleCursorPosition(TextConsoleBuffer
, Position
);
685 /* Reset the cursor move flag */
689 /* PUBLIC FUNCTIONS ***********************************************************/
691 DWORD
VgaGetVideoBaseAddress(VOID
)
693 return MemoryBase
[(VgaGcRegisters
[VGA_GC_MISC_REG
] >> 2) & 0x03];
696 DWORD
VgaGetVideoLimitAddress(VOID
)
698 return MemoryLimit
[(VgaGcRegisters
[VGA_GC_MISC_REG
] >> 2) & 0x03];
701 COORD
VgaGetDisplayResolution(VOID
)
704 BYTE MaximumScanLine
= 1 + (VgaCrtcRegisters
[VGA_CRTC_MAX_SCAN_LINE_REG
] & 0x1F);
706 /* The low 8 bits are in the display registers */
707 Resolution
.X
= VgaCrtcRegisters
[VGA_CRTC_END_HORZ_DISP_REG
];
708 Resolution
.Y
= VgaCrtcRegisters
[VGA_CRTC_VERT_DISP_END_REG
];
710 /* Set the top bits from the overflow register */
711 if (VgaCrtcRegisters
[VGA_CRTC_OVERFLOW_REG
] & VGA_CRTC_OVERFLOW_VDE8
)
713 Resolution
.Y
|= 1 << 8;
715 if (VgaCrtcRegisters
[VGA_CRTC_OVERFLOW_REG
] & VGA_CRTC_OVERFLOW_VDE9
)
717 Resolution
.Y
|= 1 << 9;
720 /* Increase the values by 1 */
724 if (VgaGcRegisters
[VGA_GC_MISC_REG
] & VGA_GC_MISC_NOALPHA
)
726 /* Multiply the horizontal resolution by the 9/8 dot mode */
727 Resolution
.X
*= (VgaSeqRegisters
[VGA_SEQ_CLOCK_REG
] & VGA_SEQ_CLOCK_98DM
)
730 /* The horizontal resolution is halved in 8-bit mode */
731 if (VgaAcRegisters
[VGA_AC_CONTROL_REG
] & VGA_AC_CONTROL_8BIT
) Resolution
.X
/= 2;
734 /* Divide the vertical resolution by the maximum scan line (== font size in text mode) */
735 Resolution
.Y
/= MaximumScanLine
;
737 /* Return the resolution */
741 VOID
VgaRefreshDisplay(VOID
)
743 COORD Resolution
= VgaGetDisplayResolution();
745 DPRINT("VgaRefreshDisplay\n");
747 /* Change the display mode */
748 if (ModeChanged
) VgaChangeMode();
750 /* Change the text cursor location */
751 if (CursorMoved
) VgaUpdateTextCursor();
755 if (VgaGcRegisters
[VGA_GC_MISC_REG
] & VGA_GC_MISC_NOALPHA
)
757 /* Set the graphics mode palette */
758 //SetConsolePalette(GraphicsConsoleBuffer,
760 // SYSPAL_NOSTATIC256);
762 /* Trigger a full update of the screen */
764 UpdateRectangle
.Left
= 0;
765 UpdateRectangle
.Top
= 0;
766 UpdateRectangle
.Right
= Resolution
.X
;
767 UpdateRectangle
.Bottom
= Resolution
.Y
;
770 PaletteChanged
= FALSE
;
773 /* Update the contents of the framebuffer */
774 VgaUpdateFramebuffer();
776 /* Set the vertical retrace flag */
777 InVerticalRetrace
= TRUE
;
779 /* Ignore if there's nothing to update */
780 if (!NeedsUpdate
) return;
782 DPRINT("Updating screen rectangle (%d, %d, %d, %d)\n",
783 UpdateRectangle
.Left
,
785 UpdateRectangle
.Right
,
786 UpdateRectangle
.Bottom
);
788 /* Check if this is text mode or graphics mode */
789 if (VgaGcRegisters
[VGA_GC_MISC_REG
] & VGA_GC_MISC_NOALPHA
)
793 /* Redraw the screen */
794 InvalidateConsoleDIBits(GraphicsConsoleBuffer
, &UpdateRectangle
);
799 COORD Origin
= { UpdateRectangle
.Left
, UpdateRectangle
.Top
};
801 /* Write the data to the console */
802 WriteConsoleOutputA(TextConsoleBuffer
,
803 (PCHAR_INFO
)ConsoleFramebuffer
,
810 /* Clear the update flag */
814 VOID
VgaHorizontalRetrace(VOID
)
817 InHorizontalRetrace
= TRUE
;
820 VOID
VgaReadMemory(DWORD Address
, LPBYTE Buffer
, DWORD Size
)
825 DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n",
829 /* Ignore if video RAM access is disabled */
830 if (!(VgaMiscRegister
& VGA_MISC_RAM_ENABLED
)) return;
832 /* Loop through each byte */
833 for (i
= 0; i
< Size
; i
++)
835 VideoAddress
= VgaTranslateReadAddress(Address
+ i
);
837 /* Load the latch registers */
838 VgaLatchRegisters
[0] = VgaMemory
[LOWORD(VideoAddress
)];
839 VgaLatchRegisters
[1] = VgaMemory
[VGA_BANK_SIZE
+ LOWORD(VideoAddress
)];
840 VgaLatchRegisters
[2] = VgaMemory
[(2 * VGA_BANK_SIZE
) + LOWORD(VideoAddress
)];
841 VgaLatchRegisters
[3] = VgaMemory
[(3 * VGA_BANK_SIZE
) + LOWORD(VideoAddress
)];
843 /* Copy the value to the buffer */
844 Buffer
[i
] = VgaMemory
[VideoAddress
];
848 VOID
VgaWriteMemory(DWORD Address
, LPBYTE Buffer
, DWORD Size
)
853 DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n",
857 /* Ignore if video RAM access is disabled */
858 if (!(VgaMiscRegister
& VGA_MISC_RAM_ENABLED
)) return;
860 /* Also ignore if write access to all planes is disabled */
861 if ((VgaSeqRegisters
[VGA_SEQ_MASK_REG
] & 0x0F) == 0x00) return;
863 /* Loop through each byte */
864 for (i
= 0; i
< Size
; i
++)
866 VideoAddress
= VgaTranslateWriteAddress(Address
+ i
);
868 for (j
= 0; j
< VGA_NUM_BANKS
; j
++)
870 /* Make sure the page is writeable */
871 if (!(VgaSeqRegisters
[VGA_SEQ_MASK_REG
] & (1 << j
))) continue;
873 /* Check if this is chain-4 mode */
874 if (VgaSeqRegisters
[VGA_SEQ_MEM_REG
] & VGA_SEQ_MEM_C4
)
876 if (((Address
+ i
) & 3) != j
)
878 /* This plane will not be accessed */
883 /* Check if this is odd-even mode */
884 if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_OE
)
886 if (((Address
+ i
) & 1) != (j
& 1))
888 /* This plane will not be accessed */
893 /* Copy the value to the VGA memory */
894 VgaMemory
[VideoAddress
+ j
* VGA_BANK_SIZE
] = VgaTranslateByteForWriting(Buffer
[i
], j
);
899 BYTE
VgaReadPort(WORD Port
)
901 DPRINT("VgaReadPort: Port 0x%04X\n", Port
);
912 return VgaAcRegisters
[VgaAcIndex
];
922 return VgaSeqRegisters
[VgaSeqIndex
];
925 case VGA_DAC_READ_INDEX
:
927 /* This returns the read/write state */
928 return VgaDacReadWrite
? 0 : 3;
931 case VGA_DAC_WRITE_INDEX
:
938 /* Ignore reads in write mode */
939 if (!VgaDacReadWrite
)
941 BYTE Data
= VgaDacRegisters
[VgaDacIndex
++];
942 VgaDacIndex
%= VGA_PALETTE_SIZE
;
951 return VgaMiscRegister
;
961 return VgaCrtcRegisters
[VgaCrtcIndex
];
971 return VgaGcRegisters
[VgaGcIndex
];
979 /* Reset the AC latch */
982 /* Set a flag if there is a vertical or horizontal retrace */
983 if (InVerticalRetrace
|| InHorizontalRetrace
) Result
|= VGA_STAT_DD
;
985 /* Set an additional flag if there was a vertical retrace */
986 if (InVerticalRetrace
) Result
|= VGA_STAT_VRETRACE
;
988 /* Clear the flags */
989 InHorizontalRetrace
= InVerticalRetrace
= FALSE
;
998 VOID
VgaWritePort(WORD Port
, BYTE Data
)
1000 DPRINT("VgaWritePort: Port 0x%04X, Data 0x%02X\n", Port
, Data
);
1008 /* Change the index */
1009 if (Data
< VGA_AC_MAX_REG
) VgaAcIndex
= Data
;
1013 /* Write the data */
1017 /* Toggle the latch */
1018 VgaAcLatch
= !VgaAcLatch
;
1025 /* Set the sequencer index register */
1026 if (Data
< VGA_SEQ_MAX_REG
) VgaSeqIndex
= Data
;
1033 /* Call the sequencer function */
1034 VgaWriteSequencer(Data
);
1039 case VGA_DAC_READ_INDEX
:
1041 VgaDacReadWrite
= FALSE
;
1042 VgaDacIndex
= Data
% VGA_PALETTE_SIZE
;
1047 case VGA_DAC_WRITE_INDEX
:
1049 VgaDacReadWrite
= TRUE
;
1050 VgaDacIndex
= Data
% VGA_PALETTE_SIZE
;
1057 /* Ignore writes in read mode */
1058 if (VgaDacReadWrite
) VgaWriteDac(Data
& 0x3F);
1063 case VGA_MISC_WRITE
:
1065 VgaMiscRegister
= Data
;
1070 case VGA_CRTC_INDEX
:
1072 /* Set the CRTC index register */
1073 if (Data
< VGA_CRTC_MAX_REG
) VgaCrtcIndex
= Data
;
1080 /* Call the CRTC function */
1088 /* Set the GC index register */
1089 if (Data
< VGA_GC_MAX_REG
) VgaGcIndex
= Data
;
1095 /* Call the GC function */
1103 VOID
VgaClearMemory(VOID
)
1105 ZeroMemory(VgaMemory
, sizeof(VgaMemory
));
1108 BOOLEAN
VgaInitialize(HANDLE TextHandle
)
1114 COORD Origin
= { 0, 0 };
1115 SMALL_RECT ScreenRect
;
1116 PCHAR_INFO CharBuffer
;
1119 LPLOGPALETTE Palette
;
1121 /* Set the global handle */
1122 TextConsoleBuffer
= TextHandle
;
1124 /* Clear the VGA memory */
1125 ZeroMemory(VgaMemory
, VGA_NUM_BANKS
* VGA_BANK_SIZE
);
1127 /* Set the default video mode */
1128 BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE
);
1132 Resolution
= VgaGetDisplayResolution();
1133 CharBuffer
= (PCHAR_INFO
)ConsoleFramebuffer
;
1134 AddressSize
= VgaGetAddressSize();
1135 ScreenRect
.Left
= ScreenRect
.Top
= 0;
1136 ScreenRect
.Right
= Resolution
.X
;
1137 ScreenRect
.Bottom
= Resolution
.Y
;
1138 ScanlineSize
= (DWORD
)VgaCrtcRegisters
[VGA_CRTC_OFFSET_REG
] * 2;
1140 /* Read the data from the console into the framebuffer */
1141 ReadConsoleOutputA(TextConsoleBuffer
,
1147 /* Loop through the scanlines */
1148 for (i
= 0; i
< Resolution
.Y
; i
++)
1150 /* Loop through the characters */
1151 for (j
= 0; j
< Resolution
.X
; j
++)
1153 CurrentAddr
= LOWORD((Address
+ j
) * AddressSize
);
1155 /* Store the character in plane 0 */
1156 VgaMemory
[CurrentAddr
] = CharBuffer
[i
* Resolution
.X
+ j
].Char
.AsciiChar
;
1158 /* Store the attribute in plane 1 */
1159 VgaMemory
[CurrentAddr
+ VGA_BANK_SIZE
] = (BYTE
)CharBuffer
[i
* Resolution
.X
+ j
].Attributes
;
1162 /* Move to the next scanline */
1163 Address
+= ScanlineSize
;
1166 /* Allocate storage space for the palette */
1167 Palette
= (LPLOGPALETTE
)HeapAlloc(GetProcessHeap(),
1170 + VGA_MAX_COLORS
* sizeof(PALETTEENTRY
));
1171 if (Palette
== NULL
) return FALSE
;
1173 /* Initialize the palette */
1174 Palette
->palVersion
= 0x0300;
1175 Palette
->palNumEntries
= VGA_MAX_COLORS
;
1177 /* Copy the colors of the default palette to the DAC and console palette */
1178 for (i
= 0; i
< VGA_MAX_COLORS
; i
++)
1180 /* Set the palette entries */
1181 Palette
->palPalEntry
[i
].peRed
= GetRValue(VgaDefaultPalette
[i
]);
1182 Palette
->palPalEntry
[i
].peGreen
= GetGValue(VgaDefaultPalette
[i
]);
1183 Palette
->palPalEntry
[i
].peBlue
= GetBValue(VgaDefaultPalette
[i
]);
1184 Palette
->palPalEntry
[i
].peFlags
= 0;
1186 /* Set the DAC registers */
1187 VgaDacRegisters
[i
* 3] = VGA_COLOR_TO_DAC(GetRValue(VgaDefaultPalette
[i
]));
1188 VgaDacRegisters
[i
* 3 + 1] = VGA_COLOR_TO_DAC(GetGValue(VgaDefaultPalette
[i
]));
1189 VgaDacRegisters
[i
* 3 + 2] = VGA_COLOR_TO_DAC(GetBValue(VgaDefaultPalette
[i
]));
1192 /* Create the palette */
1193 PaletteHandle
= CreatePalette(Palette
);
1195 /* Free the palette */
1196 HeapFree(GetProcessHeap(), 0, Palette
);
1198 /* Return success if the palette was successfully created */
1199 return (PaletteHandle
? TRUE
: FALSE
);