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 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA),
24 RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA),
25 RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF),
26 RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF),
27 RGB(0x00, 0x00, 0x00), RGB(0x10, 0x10, 0x10), RGB(0x20, 0x20, 0x20), RGB(0x35, 0x35, 0x35),
28 RGB(0x45, 0x45, 0x45), RGB(0x55, 0x55, 0x55), RGB(0x65, 0x65, 0x65), RGB(0x75, 0x75, 0x75),
29 RGB(0x8A, 0x8A, 0x8A), RGB(0x9A, 0x9A, 0x9A), RGB(0xAA, 0xAA, 0xAA), RGB(0xBA, 0xBA, 0xBA),
30 RGB(0xCA, 0xCA, 0xCA), RGB(0xDF, 0xDF, 0xDF), RGB(0xEF, 0xEF, 0xEF), RGB(0xFF, 0xFF, 0xFF),
31 RGB(0x00, 0x00, 0xFF), RGB(0x41, 0x00, 0xFF), RGB(0x82, 0x00, 0xFF), RGB(0xBE, 0x00, 0xFF),
32 RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0x00, 0xBE), RGB(0xFF, 0x00, 0x82), RGB(0xFF, 0x00, 0x41),
33 RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x41, 0x00), RGB(0xFF, 0x82, 0x00), RGB(0xFF, 0xBE, 0x00),
34 RGB(0xFF, 0xFF, 0x00), RGB(0xBE, 0xFF, 0x00), RGB(0x82, 0xFF, 0x00), RGB(0x41, 0xFF, 0x00),
35 RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0x41), RGB(0x00, 0xFF, 0x82), RGB(0x00, 0xFF, 0xBE),
36 RGB(0x00, 0xFF, 0xFF), RGB(0x00, 0xBE, 0xFF), RGB(0x00, 0x82, 0xFF), RGB(0x00, 0x41, 0xFF),
37 RGB(0x82, 0x82, 0xFF), RGB(0x9E, 0x82, 0xFF), RGB(0xBE, 0x82, 0xFF), RGB(0xDF, 0x82, 0xFF),
38 RGB(0xFF, 0x82, 0xFF), RGB(0xFF, 0x82, 0xDF), RGB(0xFF, 0x82, 0xBE), RGB(0xFF, 0x82, 0x9E),
39 RGB(0xFF, 0x82, 0x82), RGB(0xFF, 0x9E, 0x82), RGB(0xFF, 0xBE, 0x82), RGB(0xFF, 0xDF, 0x82),
40 RGB(0xFF, 0xFF, 0x82), RGB(0xDF, 0xFF, 0x82), RGB(0xBE, 0xFF, 0x82), RGB(0x9E, 0xFF, 0x82),
41 RGB(0x82, 0xFF, 0x82), RGB(0x82, 0xFF, 0x9E), RGB(0x82, 0xFF, 0xBE), RGB(0x82, 0xFF, 0xDF),
42 RGB(0x82, 0xFF, 0xFF), RGB(0x82, 0xDF, 0xFF), RGB(0x82, 0xBE, 0xFF), RGB(0x82, 0x9E, 0xFF),
43 RGB(0xBA, 0xBA, 0xFF), RGB(0xCA, 0xBA, 0xFF), RGB(0xDF, 0xBA, 0xFF), RGB(0xEF, 0xBA, 0xFF),
44 RGB(0xFF, 0xBA, 0xFF), RGB(0xFF, 0xBA, 0xEF), RGB(0xFF, 0xBA, 0xDF), RGB(0xFF, 0xBA, 0xCA),
45 RGB(0xFF, 0xBA, 0xBA), RGB(0xFF, 0xCA, 0xBA), RGB(0xFF, 0xDF, 0xBA), RGB(0xFF, 0xEF, 0xBA),
46 RGB(0xFF, 0xFF, 0xBA), RGB(0xEF, 0xFF, 0xBA), RGB(0xDF, 0xFF, 0xBA), RGB(0xCA, 0xFF, 0xBA),
47 RGB(0xBA, 0xFF, 0xBA), RGB(0xBA, 0xFF, 0xCA), RGB(0xBA, 0xFF, 0xDF), RGB(0xBA, 0xFF, 0xEF),
48 RGB(0xBA, 0xFF, 0xFF), RGB(0xBA, 0xEF, 0xFF), RGB(0xBA, 0xDF, 0xFF), RGB(0xBA, 0xCA, 0xFF),
49 RGB(0x00, 0x00, 0x71), RGB(0x1C, 0x00, 0x71), RGB(0x39, 0x00, 0x71), RGB(0x55, 0x00, 0x71),
50 RGB(0x71, 0x00, 0x71), RGB(0x71, 0x00, 0x55), RGB(0x71, 0x00, 0x39), RGB(0x71, 0x00, 0x1C),
51 RGB(0x71, 0x00, 0x00), RGB(0x71, 0x1C, 0x00), RGB(0x71, 0x39, 0x00), RGB(0x71, 0x55, 0x00),
52 RGB(0x71, 0x71, 0x00), RGB(0x55, 0x71, 0x00), RGB(0x39, 0x71, 0x00), RGB(0x1C, 0x71, 0x00),
53 RGB(0x00, 0x71, 0x00), RGB(0x00, 0x71, 0x1C), RGB(0x00, 0x71, 0x39), RGB(0x00, 0x71, 0x55),
54 RGB(0x00, 0x71, 0x71), RGB(0x00, 0x55, 0x71), RGB(0x00, 0x39, 0x71), RGB(0x00, 0x1C, 0x71),
55 RGB(0x39, 0x39, 0x71), RGB(0x45, 0x39, 0x71), RGB(0x55, 0x39, 0x71), RGB(0x61, 0x39, 0x71),
56 RGB(0x71, 0x39, 0x71), RGB(0x71, 0x39, 0x61), RGB(0x71, 0x39, 0x55), RGB(0x71, 0x39, 0x45),
57 RGB(0x71, 0x39, 0x39), RGB(0x71, 0x45, 0x39), RGB(0x71, 0x55, 0x39), RGB(0x71, 0x61, 0x39),
58 RGB(0x71, 0x71, 0x39), RGB(0x61, 0x71, 0x39), RGB(0x55, 0x71, 0x39), RGB(0x45, 0x71, 0x39),
59 RGB(0x39, 0x71, 0x39), RGB(0x39, 0x71, 0x45), RGB(0x39, 0x71, 0x55), RGB(0x39, 0x71, 0x61),
60 RGB(0x39, 0x71, 0x71), RGB(0x39, 0x61, 0x71), RGB(0x39, 0x55, 0x71), RGB(0x39, 0x45, 0x71),
61 RGB(0x51, 0x51, 0x71), RGB(0x59, 0x51, 0x71), RGB(0x61, 0x51, 0x71), RGB(0x69, 0x51, 0x71),
62 RGB(0x71, 0x51, 0x71), RGB(0x71, 0x51, 0x69), RGB(0x71, 0x51, 0x61), RGB(0x71, 0x51, 0x59),
63 RGB(0x71, 0x51, 0x51), RGB(0x71, 0x59, 0x51), RGB(0x71, 0x61, 0x51), RGB(0x71, 0x69, 0x51),
64 RGB(0x71, 0x71, 0x51), RGB(0x69, 0x71, 0x51), RGB(0x61, 0x71, 0x51), RGB(0x59, 0x71, 0x51),
65 RGB(0x51, 0x71, 0x51), RGB(0x51, 0x71, 0x59), RGB(0x51, 0x71, 0x61), RGB(0x51, 0x71, 0x69),
66 RGB(0x51, 0x71, 0x71), RGB(0x51, 0x69, 0x71), RGB(0x51, 0x61, 0x71), RGB(0x51, 0x59, 0x71),
67 RGB(0x00, 0x00, 0x41), RGB(0x10, 0x00, 0x41), RGB(0x20, 0x00, 0x41), RGB(0x31, 0x00, 0x41),
68 RGB(0x41, 0x00, 0x41), RGB(0x41, 0x00, 0x31), RGB(0x41, 0x00, 0x20), RGB(0x41, 0x00, 0x10),
69 RGB(0x41, 0x00, 0x00), RGB(0x41, 0x10, 0x00), RGB(0x41, 0x20, 0x00), RGB(0x41, 0x31, 0x00),
70 RGB(0x41, 0x41, 0x00), RGB(0x31, 0x41, 0x00), RGB(0x20, 0x41, 0x00), RGB(0x10, 0x41, 0x00),
71 RGB(0x00, 0x41, 0x00), RGB(0x00, 0x41, 0x10), RGB(0x00, 0x41, 0x20), RGB(0x00, 0x41, 0x31),
72 RGB(0x00, 0x41, 0x41), RGB(0x00, 0x31, 0x41), RGB(0x00, 0x20, 0x41), RGB(0x00, 0x10, 0x41),
73 RGB(0x20, 0x20, 0x41), RGB(0x28, 0x20, 0x41), RGB(0x31, 0x20, 0x41), RGB(0x39, 0x20, 0x41),
74 RGB(0x41, 0x20, 0x41), RGB(0x41, 0x20, 0x39), RGB(0x41, 0x20, 0x31), RGB(0x41, 0x20, 0x28),
75 RGB(0x41, 0x20, 0x20), RGB(0x41, 0x28, 0x20), RGB(0x41, 0x31, 0x20), RGB(0x41, 0x39, 0x20),
76 RGB(0x41, 0x41, 0x20), RGB(0x39, 0x41, 0x20), RGB(0x31, 0x41, 0x20), RGB(0x28, 0x41, 0x20),
77 RGB(0x20, 0x41, 0x20), RGB(0x20, 0x41, 0x28), RGB(0x20, 0x41, 0x31), RGB(0x20, 0x41, 0x39),
78 RGB(0x20, 0x41, 0x41), RGB(0x20, 0x39, 0x41), RGB(0x20, 0x31, 0x41), RGB(0x20, 0x28, 0x41),
79 RGB(0x2D, 0x2D, 0x41), RGB(0x31, 0x2D, 0x41), RGB(0x35, 0x2D, 0x41), RGB(0x3D, 0x2D, 0x41),
80 RGB(0x41, 0x2D, 0x41), RGB(0x41, 0x2D, 0x3D), RGB(0x41, 0x2D, 0x35), RGB(0x41, 0x2D, 0x31),
81 RGB(0x41, 0x2D, 0x2D), RGB(0x41, 0x31, 0x2D), RGB(0x41, 0x35, 0x2D), RGB(0x41, 0x3D, 0x2D),
82 RGB(0x41, 0x41, 0x2D), RGB(0x3D, 0x41, 0x2D), RGB(0x35, 0x41, 0x2D), RGB(0x31, 0x41, 0x2D),
83 RGB(0x2D, 0x41, 0x2D), RGB(0x2D, 0x41, 0x31), RGB(0x2D, 0x41, 0x35), RGB(0x2D, 0x41, 0x3D),
84 RGB(0x2D, 0x41, 0x41), RGB(0x2D, 0x3D, 0x41), RGB(0x2D, 0x35, 0x41), RGB(0x2D, 0x31, 0x41),
85 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00),
86 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00)
89 static BYTE VgaMemory
[VGA_NUM_BANKS
* VGA_BANK_SIZE
];
90 static BYTE VgaLatchRegisters
[VGA_NUM_BANKS
] = {0, 0, 0, 0};
91 static BYTE VgaMiscRegister
;
92 static BYTE VgaSeqIndex
= VGA_SEQ_RESET_REG
;
93 static BYTE VgaSeqRegisters
[VGA_SEQ_MAX_REG
];
94 static BYTE VgaGcIndex
= VGA_GC_RESET_REG
;
95 static BYTE VgaGcRegisters
[VGA_GC_MAX_REG
];
96 static BYTE VgaCrtcIndex
= VGA_CRTC_HORZ_TOTAL_REG
;
97 static BYTE VgaCrtcRegisters
[VGA_CRTC_MAX_REG
];
98 static BYTE VgaAcIndex
= VGA_AC_PAL_0_REG
;
99 static BOOLEAN VgaAcLatch
= FALSE
;
100 static BYTE VgaAcRegisters
[VGA_AC_MAX_REG
];
101 static WORD VgaDacIndex
= 0;
102 static BOOLEAN VgaDacReadWrite
= FALSE
;
103 static BYTE VgaDacRegisters
[VGA_PALETTE_SIZE
];
104 static HPALETTE PaletteHandle
= NULL
;
105 static BOOLEAN InVerticalRetrace
= FALSE
;
106 static BOOLEAN InHorizontalRetrace
= FALSE
;
107 static HANDLE TextConsoleBuffer
= NULL
;
108 static HANDLE GraphicsConsoleBuffer
= NULL
;
109 static LPVOID ConsoleFramebuffer
= NULL
;
110 static HANDLE ConsoleMutex
= NULL
;
111 static BOOLEAN NeedsUpdate
= FALSE
;
112 static BOOLEAN ModeChanged
= TRUE
;
113 static BOOLEAN CursorMoved
= FALSE
;
114 static BOOLEAN PaletteChanged
= FALSE
;
115 static BOOLEAN TextMode
= TRUE
;
116 static SMALL_RECT UpdateRectangle
= { 0, 0, 0, 0 };
118 /* PRIVATE FUNCTIONS **********************************************************/
120 static inline INT
VgaGetAddressSize(VOID
)
122 if (VgaCrtcRegisters
[VGA_CRTC_UNDERLINE_REG
] & VGA_CRTC_UNDERLINE_DWORD
)
124 /* Double-word addressing */
128 if (VgaCrtcRegisters
[VGA_CRTC_MODE_CONTROL_REG
] & VGA_CRTC_MODE_CONTROL_BYTE
)
130 /* Byte addressing */
134 /* Word addressing */
138 static inline DWORD
VgaTranslateReadAddress(DWORD Address
)
140 DWORD Offset
= Address
- VgaGetVideoBaseAddress();
143 /* Check for chain-4 and odd-even mode */
144 if (VgaSeqRegisters
[VGA_SEQ_MEM_REG
] & VGA_SEQ_MEM_C4
)
146 /* The lowest two bits are the plane number */
150 else if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_OE
)
152 /* The LSB is the plane number */
158 /* Use the read mode */
159 Plane
= VgaGcRegisters
[VGA_GC_READ_MAP_SEL_REG
] & 0x03;
162 /* Multiply the offset by the address size */
163 Offset
*= VgaGetAddressSize();
165 return Offset
+ Plane
* VGA_BANK_SIZE
;
168 static inline DWORD
VgaTranslateWriteAddress(DWORD Address
)
170 DWORD Offset
= Address
- VgaGetVideoBaseAddress();
172 /* Check for chain-4 and odd-even mode */
173 if (VgaSeqRegisters
[VGA_SEQ_MEM_REG
] & VGA_SEQ_MEM_C4
)
175 /* Shift the offset to the right by 2 */
178 else if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_OE
)
180 /* Shift the offset to the right by 1 */
184 /* Multiply the offset by the address size */
185 Offset
*= VgaGetAddressSize();
187 /* Return the offset on plane 0 */
191 static inline BYTE
VgaTranslateByteForWriting(BYTE Data
, BYTE Plane
)
193 BYTE WriteMode
= VgaGcRegisters
[VGA_GC_MODE_REG
] & 3;
194 BYTE LogicalOperation
= (VgaGcRegisters
[VGA_GC_ROTATE_REG
] >> 3) & 3;
195 BYTE RotateCount
= VgaGcRegisters
[VGA_GC_ROTATE_REG
] & 7;
196 BYTE BitMask
= VgaGcRegisters
[VGA_GC_BITMASK_REG
];
200 /* In write mode 1 just return the latch register */
201 return VgaLatchRegisters
[Plane
];
206 /* Write modes 0 and 3 rotate the data to the right first */
207 Data
= LOBYTE(((DWORD
)Data
>> RotateCount
) | ((DWORD
)Data
<< (8 - RotateCount
)));
211 /* Write mode 2 expands the appropriate bit to all 8 bits */
212 Data
= (Data
& (1 << Plane
)) ? 0xFF : 0x00;
218 * In write mode 0, the enable set/reset register decides if the
219 * set/reset bit should be expanded to all 8 bits.
221 if (VgaGcRegisters
[VGA_GC_ENABLE_RESET_REG
] & (1 << Plane
))
223 /* Copy the bit from the set/reset register to all 8 bits */
224 Data
= (VgaGcRegisters
[VGA_GC_RESET_REG
] & (1 << Plane
)) ? 0xFF : 0x00;
230 /* Write modes 0 and 2 then perform a logical operation on the data and latch */
231 if (LogicalOperation
== 1) Data
&= VgaLatchRegisters
[Plane
];
232 else if (LogicalOperation
== 2) Data
|= VgaLatchRegisters
[Plane
];
233 else if (LogicalOperation
== 3) Data
^= VgaLatchRegisters
[Plane
];
237 /* For write mode 3, we AND the bitmask with the data, which is used as the new bitmask */
240 /* Then we expand the bit in the set/reset field */
241 Data
= (VgaGcRegisters
[VGA_GC_RESET_REG
] & (1 << Plane
)) ? 0xFF : 0x00;
244 /* Bits cleared in the bitmask are replaced with latch register bits */
245 Data
= (Data
& BitMask
) | (VgaLatchRegisters
[Plane
] & (~BitMask
));
247 /* Return the byte */
251 static inline VOID
VgaMarkForUpdate(SHORT Row
, SHORT Column
)
253 DPRINT("VgaMarkForUpdate: Row %d, Column %d\n", Row
, Column
);
255 /* Check if this is the first time the rectangle is updated */
258 UpdateRectangle
.Left
= UpdateRectangle
.Top
= SHRT_MAX
;
259 UpdateRectangle
.Right
= UpdateRectangle
.Bottom
= SHRT_MIN
;
262 /* Expand the rectangle to include the point */
263 UpdateRectangle
.Left
= min(UpdateRectangle
.Left
, Column
);
264 UpdateRectangle
.Right
= max(UpdateRectangle
.Right
, Column
);
265 UpdateRectangle
.Top
= min(UpdateRectangle
.Top
, Row
);
266 UpdateRectangle
.Bottom
= max(UpdateRectangle
.Bottom
, Row
);
268 /* Set the update request flag */
272 static VOID
VgaWriteSequencer(BYTE Data
)
274 ASSERT(VgaSeqIndex
< VGA_SEQ_MAX_REG
);
277 VgaSeqRegisters
[VgaSeqIndex
] = Data
;
280 static VOID
VgaWriteGc(BYTE Data
)
282 ASSERT(VgaGcIndex
< VGA_GC_MAX_REG
);
285 VgaGcRegisters
[VgaGcIndex
] = Data
;
287 /* Check the index */
290 case VGA_GC_MISC_REG
:
292 /* The GC misc register decides if it's text or graphics mode */
300 static VOID
VgaWriteCrtc(BYTE Data
)
302 ASSERT(VgaGcIndex
< VGA_CRTC_MAX_REG
);
305 VgaCrtcRegisters
[VgaCrtcIndex
] = Data
;
307 /* Check the index */
308 switch (VgaCrtcIndex
)
310 case VGA_CRTC_END_HORZ_DISP_REG
:
311 case VGA_CRTC_VERT_DISP_END_REG
:
312 case VGA_CRTC_OVERFLOW_REG
:
314 /* The video mode has changed */
320 case VGA_CRTC_CURSOR_LOC_LOW_REG
:
321 case VGA_CRTC_CURSOR_LOC_HIGH_REG
:
322 case VGA_CRTC_CURSOR_START_REG
:
323 case VGA_CRTC_CURSOR_END_REG
:
325 /* Set the cursor moved flag */
333 static VOID
VgaWriteDac(BYTE Data
)
339 VgaDacRegisters
[VgaDacIndex
] = Data
;
341 /* Find the palette index */
342 PaletteIndex
= VgaDacIndex
/ 3;
344 /* Fill the entry structure */
345 Entry
.peRed
= VGA_DAC_TO_COLOR(VgaDacRegisters
[PaletteIndex
* 3]);
346 Entry
.peGreen
= VGA_DAC_TO_COLOR(VgaDacRegisters
[PaletteIndex
* 3 + 1]);
347 Entry
.peBlue
= VGA_DAC_TO_COLOR(VgaDacRegisters
[PaletteIndex
* 3 + 2]);
350 /* Update the palette entry */
351 SetPaletteEntries(PaletteHandle
, PaletteIndex
, 1, &Entry
);
353 /* Set the palette change flag */
354 PaletteChanged
= TRUE
;
356 /* Update the index */
358 VgaDacIndex
%= VGA_PALETTE_SIZE
;
361 static VOID
VgaWriteAc(BYTE Data
)
363 ASSERT(VgaAcIndex
< VGA_AC_MAX_REG
);
366 VgaAcRegisters
[VgaAcIndex
] = Data
;
369 static BOOL
VgaEnterGraphicsMode(PCOORD Resolution
)
372 CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo
;
373 BYTE BitmapInfoBuffer
[VGA_BITMAP_INFO_SIZE
];
374 LPBITMAPINFO BitmapInfo
= (LPBITMAPINFO
)BitmapInfoBuffer
;
375 LPWORD PaletteIndex
= (LPWORD
)(BitmapInfo
->bmiColors
);
377 /* Fill the bitmap info header */
378 ZeroMemory(&BitmapInfo
->bmiHeader
, sizeof(BITMAPINFOHEADER
));
379 BitmapInfo
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
380 BitmapInfo
->bmiHeader
.biWidth
= Resolution
->X
;
381 BitmapInfo
->bmiHeader
.biHeight
= Resolution
->Y
;
382 BitmapInfo
->bmiHeader
.biBitCount
= 8;
383 BitmapInfo
->bmiHeader
.biPlanes
= 1;
384 BitmapInfo
->bmiHeader
.biCompression
= BI_RGB
;
385 BitmapInfo
->bmiHeader
.biSizeImage
= Resolution
->X
* Resolution
->Y
/* * 1 == biBitCount / 8 */;
387 /* Fill the palette data */
388 for (i
= 0; i
< (VGA_PALETTE_SIZE
/ 3); i
++) PaletteIndex
[i
] = (WORD
)i
;
390 /* Fill the console graphics buffer info */
391 GraphicsBufferInfo
.dwBitMapInfoLength
= VGA_BITMAP_INFO_SIZE
;
392 GraphicsBufferInfo
.lpBitMapInfo
= BitmapInfo
;
393 GraphicsBufferInfo
.dwUsage
= DIB_PAL_COLORS
;
395 /* Create the buffer */
396 GraphicsConsoleBuffer
= CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
397 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
399 CONSOLE_GRAPHICS_BUFFER
,
400 &GraphicsBufferInfo
);
401 if (GraphicsConsoleBuffer
== INVALID_HANDLE_VALUE
) return FALSE
;
403 /* Save the framebuffer address and mutex */
404 ConsoleFramebuffer
= GraphicsBufferInfo
.lpBitMap
;
405 ConsoleMutex
= GraphicsBufferInfo
.hMutex
;
407 /* Clear the framebuffer */
408 ZeroMemory(ConsoleFramebuffer
, BitmapInfo
->bmiHeader
.biSizeImage
);
410 /* Set the active buffer */
411 SetConsoleActiveScreenBuffer(GraphicsConsoleBuffer
);
413 /* Set the graphics mode palette */
414 SetConsolePalette(GraphicsConsoleBuffer
,
418 /* Clear the text mode flag */
424 static VOID
VgaLeaveGraphicsMode(VOID
)
426 /* Release the console framebuffer mutex if needed */
427 ReleaseMutex(ConsoleMutex
);
429 /* Switch back to the text buffer */
430 SetConsoleActiveScreenBuffer(TextConsoleBuffer
);
432 /* Cleanup the video data */
433 CloseHandle(ConsoleMutex
);
435 CloseHandle(GraphicsConsoleBuffer
);
436 GraphicsConsoleBuffer
= NULL
;
439 static BOOL
VgaEnterTextMode(PCOORD Resolution
)
441 /* Resize the console */
442 SetConsoleScreenBufferSize(TextConsoleBuffer
, *Resolution
);
444 /* Allocate a framebuffer */
445 ConsoleFramebuffer
= HeapAlloc(GetProcessHeap(),
447 Resolution
->X
* Resolution
->Y
448 * sizeof(CHAR_INFO
));
449 if (ConsoleFramebuffer
== NULL
)
451 DisplayMessage(L
"An unexpected error occurred!\n");
456 /* Set the text mode flag */
462 static VOID
VgaLeaveTextMode(VOID
)
464 /* Free the old framebuffer */
465 HeapFree(GetProcessHeap(), 0, ConsoleFramebuffer
);
466 ConsoleFramebuffer
= NULL
;
469 static VOID
VgaChangeMode(VOID
)
471 COORD Resolution
= VgaGetDisplayResolution();
473 /* Reset the mode change flag */
474 // ModeChanged = FALSE;
478 /* Leave the current graphics mode */
479 VgaLeaveGraphicsMode();
483 /* Leave the current text mode */
487 /* Check if the new mode is alphanumeric */
488 if (!(VgaGcRegisters
[VGA_GC_MISC_REG
] & VGA_GC_MISC_NOALPHA
))
490 /* Enter new text mode */
491 if (!VgaEnterTextMode(&Resolution
)) return;
495 /* Enter 8-bit graphics mode */
496 if (!VgaEnterGraphicsMode(&Resolution
)) return;
499 /* Trigger a full update of the screen */
501 UpdateRectangle
.Left
= 0;
502 UpdateRectangle
.Top
= 0;
503 UpdateRectangle
.Right
= Resolution
.X
;
504 UpdateRectangle
.Bottom
= Resolution
.Y
;
506 /* Reset the mode change flag */
510 static VOID
VgaUpdateFramebuffer(VOID
)
513 COORD Resolution
= VgaGetDisplayResolution();
514 INT AddressSize
= VgaGetAddressSize();
515 DWORD Address
= (VgaCrtcRegisters
[VGA_CRTC_START_ADDR_HIGH_REG
] << 8)
516 + VgaCrtcRegisters
[VGA_CRTC_START_ADDR_LOW_REG
];
517 DWORD ScanlineSize
= (DWORD
)VgaCrtcRegisters
[VGA_CRTC_OFFSET_REG
] * 2;
519 /* Check if this is text mode or graphics mode */
520 if (VgaGcRegisters
[VGA_GC_MISC_REG
] & VGA_GC_MISC_NOALPHA
)
523 PBYTE GraphicsBuffer
= (PBYTE
)ConsoleFramebuffer
;
526 * Synchronize access to the graphics framebuffer
527 * with the console framebuffer mutex.
529 WaitForSingleObject(ConsoleMutex
, INFINITE
);
531 /* Loop through the scanlines */
532 for (i
= 0; i
< Resolution
.Y
; i
++)
534 /* Loop through the pixels */
535 for (j
= 0; j
< Resolution
.X
; j
++)
539 /* Check the shifting mode */
540 if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_SHIFT256
)
542 /* 4 bits shifted from each plane */
544 /* Check if this is 16 or 256 color mode */
545 if (VgaAcRegisters
[VGA_AC_CONTROL_REG
] & VGA_AC_CONTROL_8BIT
)
547 /* One byte per pixel */
548 PixelData
= VgaMemory
[(j
% VGA_NUM_BANKS
) * VGA_BANK_SIZE
549 + (Address
+ (j
/ VGA_NUM_BANKS
))
554 /* 4-bits per pixel */
556 PixelData
= VgaMemory
[(j
% VGA_NUM_BANKS
) * VGA_BANK_SIZE
557 + (Address
+ (j
/ (VGA_NUM_BANKS
* 2)))
560 /* Check if we should use the highest 4 bits or lowest 4 */
561 if (((j
/ VGA_NUM_BANKS
) % 2) == 0)
573 else if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_SHIFTREG
)
576 * 2 bits shifted from plane 0 and 2 for the first 4 pixels,
577 * then 2 bits shifted from plane 1 and 3 for the next 4
580 // TODO: NOT IMPLEMENTED!
581 DPRINT1("Interleaved shift mode is not implemented!\n");
585 /* 1 bit shifted from each plane */
587 /* Check if this is 16 or 256 color mode */
588 if (VgaAcRegisters
[VGA_AC_CONTROL_REG
] & VGA_AC_CONTROL_8BIT
)
590 /* 8 bits per pixel, 2 on each plane */
592 for (k
= 0; k
< VGA_NUM_BANKS
; k
++)
594 /* The data is on plane k, 4 pixels per byte */
595 BYTE PlaneData
= VgaMemory
[k
* VGA_BANK_SIZE
596 + (Address
+ (j
/ 4)) * AddressSize
];
598 /* The mask of the first bit in the pair */
599 BYTE BitMask
= 1 << (((3 - (j
% 4)) * 2) + 1);
601 /* Bits 0, 1, 2 and 3 come from the first bit of the pair */
602 if (PlaneData
& BitMask
) PixelData
|= 1 << k
;
604 /* Bits 4, 5, 6 and 7 come from the second bit of the pair */
605 if (PlaneData
& (BitMask
>> 1)) PixelData
|= 1 << (k
+ 4);
610 /* 4 bits per pixel, 1 on each plane */
612 for (k
= 0; k
< VGA_NUM_BANKS
; k
++)
614 BYTE PlaneData
= VgaMemory
[k
* VGA_BANK_SIZE
615 + (Address
+ (j
/ 8)) * AddressSize
];
617 /* If the bit on that plane is set, set it */
618 if (PlaneData
& (1 << (7 - (j
% 8)))) PixelData
|= 1 << k
;
623 /* Now check if the resulting pixel data has changed */
624 if (GraphicsBuffer
[i
* Resolution
.X
+ j
] != PixelData
)
626 /* Yes, write the new value */
627 GraphicsBuffer
[i
* Resolution
.X
+ j
] = PixelData
;
629 /* Mark the specified pixel as changed */
630 VgaMarkForUpdate(i
, j
);
634 /* Move to the next scanline */
635 Address
+= ScanlineSize
;
639 * Release the console framebuffer mutex
640 * so that we allow for repainting.
642 ReleaseMutex(ConsoleMutex
);
647 PCHAR_INFO CharBuffer
= (PCHAR_INFO
)ConsoleFramebuffer
;
649 /* Loop through the scanlines */
650 for (i
= 0; i
< Resolution
.Y
; i
++)
652 /* Loop through the characters */
653 for (j
= 0; j
< Resolution
.X
; j
++)
655 DWORD CurrentAddr
= LOWORD((Address
+ j
) * AddressSize
);
658 /* Plane 0 holds the character itself */
659 CharInfo
.Char
.AsciiChar
= VgaMemory
[CurrentAddr
];
661 /* Plane 1 holds the attribute */
662 CharInfo
.Attributes
= VgaMemory
[CurrentAddr
+ VGA_BANK_SIZE
];
664 /* Now check if the resulting character data has changed */
665 if ((CharBuffer
[i
* Resolution
.X
+ j
].Char
.AsciiChar
!= CharInfo
.Char
.AsciiChar
)
666 || (CharBuffer
[i
* Resolution
.X
+ j
].Attributes
!= CharInfo
.Attributes
))
668 /* Yes, write the new value */
669 CharBuffer
[i
* Resolution
.X
+ j
] = CharInfo
;
671 /* Mark the specified cell as changed */
672 VgaMarkForUpdate(i
, j
);
676 /* Move to the next scanline */
677 Address
+= ScanlineSize
;
682 static VOID
VgaUpdateTextCursor(VOID
)
685 CONSOLE_CURSOR_INFO CursorInfo
;
686 BYTE CursorStart
= VgaCrtcRegisters
[VGA_CRTC_CURSOR_START_REG
] & 0x3F;
687 BYTE CursorEnd
= VgaCrtcRegisters
[VGA_CRTC_CURSOR_END_REG
] & 0x1F;
688 DWORD ScanlineSize
= (DWORD
)VgaCrtcRegisters
[VGA_CRTC_OFFSET_REG
] * 2;
689 BYTE TextSize
= 1 + (VgaCrtcRegisters
[VGA_CRTC_MAX_SCAN_LINE_REG
] & 0x1F);
690 WORD Location
= MAKEWORD(VgaCrtcRegisters
[VGA_CRTC_CURSOR_LOC_LOW_REG
],
691 VgaCrtcRegisters
[VGA_CRTC_CURSOR_LOC_HIGH_REG
]);
693 if (CursorStart
< CursorEnd
)
696 CursorInfo
.bVisible
= TRUE
;
697 CursorInfo
.dwSize
= (100 * (CursorEnd
- CursorStart
)) / TextSize
;
702 CursorInfo
.bVisible
= FALSE
;
703 CursorInfo
.dwSize
= 0;
706 /* Add the cursor skew to the location */
707 Location
+= (VgaCrtcRegisters
[VGA_CRTC_CURSOR_END_REG
] >> 5) & 3;
709 /* Find the coordinates of the new position */
710 Position
.X
= (WORD
)(Location
% ScanlineSize
);
711 Position
.Y
= (WORD
)(Location
/ ScanlineSize
);
713 /* Update the physical cursor */
714 SetConsoleCursorInfo(TextConsoleBuffer
, &CursorInfo
);
715 SetConsoleCursorPosition(TextConsoleBuffer
, Position
);
717 /* Reset the cursor move flag */
721 /* PUBLIC FUNCTIONS ***********************************************************/
723 DWORD
VgaGetVideoBaseAddress(VOID
)
725 return MemoryBase
[(VgaGcRegisters
[VGA_GC_MISC_REG
] >> 2) & 0x03];
728 DWORD
VgaGetVideoLimitAddress(VOID
)
730 return MemoryLimit
[(VgaGcRegisters
[VGA_GC_MISC_REG
] >> 2) & 0x03];
733 COORD
VgaGetDisplayResolution(VOID
)
736 BYTE MaximumScanLine
= 1 + (VgaCrtcRegisters
[VGA_CRTC_MAX_SCAN_LINE_REG
] & 0x1F);
738 /* The low 8 bits are in the display registers */
739 Resolution
.X
= VgaCrtcRegisters
[VGA_CRTC_END_HORZ_DISP_REG
];
740 Resolution
.Y
= VgaCrtcRegisters
[VGA_CRTC_VERT_DISP_END_REG
];
742 /* Set the top bits from the overflow register */
743 if (VgaCrtcRegisters
[VGA_CRTC_OVERFLOW_REG
] & VGA_CRTC_OVERFLOW_VDE8
)
745 Resolution
.Y
|= 1 << 8;
747 if (VgaCrtcRegisters
[VGA_CRTC_OVERFLOW_REG
] & VGA_CRTC_OVERFLOW_VDE9
)
749 Resolution
.Y
|= 1 << 9;
752 /* Increase the values by 1 */
756 if (VgaGcRegisters
[VGA_GC_MISC_REG
] & VGA_GC_MISC_NOALPHA
)
758 /* Multiply the horizontal resolution by the 9/8 dot mode */
759 Resolution
.X
*= (VgaSeqRegisters
[VGA_SEQ_CLOCK_REG
] & VGA_SEQ_CLOCK_98DM
)
762 /* The horizontal resolution is halved in 8-bit mode */
763 if (VgaAcRegisters
[VGA_AC_CONTROL_REG
] & VGA_AC_CONTROL_8BIT
) Resolution
.X
/= 2;
766 /* Divide the vertical resolution by the maximum scan line (== font size in text mode) */
767 Resolution
.Y
/= MaximumScanLine
;
769 /* Return the resolution */
773 VOID
VgaRefreshDisplay(VOID
)
775 COORD Resolution
= VgaGetDisplayResolution();
777 DPRINT("VgaRefreshDisplay\n");
779 /* Change the display mode */
780 if (ModeChanged
) VgaChangeMode();
782 /* Change the text cursor location */
783 if (CursorMoved
) VgaUpdateTextCursor();
787 if (VgaGcRegisters
[VGA_GC_MISC_REG
] & VGA_GC_MISC_NOALPHA
)
789 /* Trigger a full update of the screen */
791 UpdateRectangle
.Left
= 0;
792 UpdateRectangle
.Top
= 0;
793 UpdateRectangle
.Right
= Resolution
.X
;
794 UpdateRectangle
.Bottom
= Resolution
.Y
;
797 PaletteChanged
= FALSE
;
800 /* Update the contents of the framebuffer */
801 VgaUpdateFramebuffer();
803 /* Set the vertical retrace flag */
804 InVerticalRetrace
= TRUE
;
806 /* Ignore if there's nothing to update */
807 if (!NeedsUpdate
) return;
809 DPRINT("Updating screen rectangle (%d, %d, %d, %d)\n",
810 UpdateRectangle
.Left
,
812 UpdateRectangle
.Right
,
813 UpdateRectangle
.Bottom
);
815 /* Check if this is text mode or graphics mode */
816 if (VgaGcRegisters
[VGA_GC_MISC_REG
] & VGA_GC_MISC_NOALPHA
)
820 /* Redraw the screen */
821 InvalidateConsoleDIBits(GraphicsConsoleBuffer
, &UpdateRectangle
);
826 COORD Origin
= { UpdateRectangle
.Left
, UpdateRectangle
.Top
};
828 /* Write the data to the console */
829 WriteConsoleOutputA(TextConsoleBuffer
,
830 (PCHAR_INFO
)ConsoleFramebuffer
,
837 /* Clear the update flag */
841 VOID
VgaHorizontalRetrace(VOID
)
844 InHorizontalRetrace
= TRUE
;
847 VOID
VgaReadMemory(DWORD Address
, LPBYTE Buffer
, DWORD Size
)
852 DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n",
856 /* Ignore if video RAM access is disabled */
857 if (!(VgaMiscRegister
& VGA_MISC_RAM_ENABLED
)) return;
859 /* Loop through each byte */
860 for (i
= 0; i
< Size
; i
++)
862 VideoAddress
= VgaTranslateReadAddress(Address
+ i
);
864 /* Load the latch registers */
865 VgaLatchRegisters
[0] = VgaMemory
[LOWORD(VideoAddress
)];
866 VgaLatchRegisters
[1] = VgaMemory
[VGA_BANK_SIZE
+ LOWORD(VideoAddress
)];
867 VgaLatchRegisters
[2] = VgaMemory
[(2 * VGA_BANK_SIZE
) + LOWORD(VideoAddress
)];
868 VgaLatchRegisters
[3] = VgaMemory
[(3 * VGA_BANK_SIZE
) + LOWORD(VideoAddress
)];
870 /* Copy the value to the buffer */
871 Buffer
[i
] = VgaMemory
[VideoAddress
];
875 VOID
VgaWriteMemory(DWORD Address
, LPBYTE Buffer
, DWORD Size
)
880 DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n",
884 /* Ignore if video RAM access is disabled */
885 if (!(VgaMiscRegister
& VGA_MISC_RAM_ENABLED
)) return;
887 /* Also ignore if write access to all planes is disabled */
888 if ((VgaSeqRegisters
[VGA_SEQ_MASK_REG
] & 0x0F) == 0x00) return;
890 /* Loop through each byte */
891 for (i
= 0; i
< Size
; i
++)
893 VideoAddress
= VgaTranslateWriteAddress(Address
+ i
);
895 for (j
= 0; j
< VGA_NUM_BANKS
; j
++)
897 /* Make sure the page is writeable */
898 if (!(VgaSeqRegisters
[VGA_SEQ_MASK_REG
] & (1 << j
))) continue;
900 /* Check if this is chain-4 mode */
901 if (VgaSeqRegisters
[VGA_SEQ_MEM_REG
] & VGA_SEQ_MEM_C4
)
903 if (((Address
+ i
) & 3) != j
)
905 /* This plane will not be accessed */
910 /* Check if this is odd-even mode */
911 if (VgaGcRegisters
[VGA_GC_MODE_REG
] & VGA_GC_MODE_OE
)
913 if (((Address
+ i
) & 1) != (j
& 1))
915 /* This plane will not be accessed */
920 /* Copy the value to the VGA memory */
921 VgaMemory
[VideoAddress
+ j
* VGA_BANK_SIZE
] = VgaTranslateByteForWriting(Buffer
[i
], j
);
926 BYTE
VgaReadPort(WORD Port
)
928 DPRINT("VgaReadPort: Port 0x%04X\n", Port
);
939 return VgaAcRegisters
[VgaAcIndex
];
949 return VgaSeqRegisters
[VgaSeqIndex
];
952 case VGA_DAC_READ_INDEX
:
954 /* This returns the read/write state */
955 return VgaDacReadWrite
? 0 : 3;
958 case VGA_DAC_WRITE_INDEX
:
960 return VgaDacIndex
/ 3;
965 /* Ignore reads in write mode */
966 if (!VgaDacReadWrite
)
968 BYTE Data
= VgaDacRegisters
[VgaDacIndex
++];
969 VgaDacIndex
%= VGA_PALETTE_SIZE
;
978 return VgaMiscRegister
;
988 return VgaCrtcRegisters
[VgaCrtcIndex
];
998 return VgaGcRegisters
[VgaGcIndex
];
1002 case VGA_STAT_COLOR
:
1006 /* Reset the AC latch */
1009 /* Set a flag if there is a vertical or horizontal retrace */
1010 if (InVerticalRetrace
|| InHorizontalRetrace
) Result
|= VGA_STAT_DD
;
1012 /* Set an additional flag if there was a vertical retrace */
1013 if (InVerticalRetrace
) Result
|= VGA_STAT_VRETRACE
;
1015 /* Clear the flags */
1016 InHorizontalRetrace
= InVerticalRetrace
= FALSE
;
1025 VOID
VgaWritePort(WORD Port
, BYTE Data
)
1027 DPRINT("VgaWritePort: Port 0x%04X, Data 0x%02X\n", Port
, Data
);
1035 /* Change the index */
1036 if (Data
< VGA_AC_MAX_REG
) VgaAcIndex
= Data
;
1040 /* Write the data */
1044 /* Toggle the latch */
1045 VgaAcLatch
= !VgaAcLatch
;
1052 /* Set the sequencer index register */
1053 if (Data
< VGA_SEQ_MAX_REG
) VgaSeqIndex
= Data
;
1060 /* Call the sequencer function */
1061 VgaWriteSequencer(Data
);
1066 case VGA_DAC_READ_INDEX
:
1068 VgaDacReadWrite
= FALSE
;
1069 VgaDacIndex
= Data
* 3;
1074 case VGA_DAC_WRITE_INDEX
:
1076 VgaDacReadWrite
= TRUE
;
1077 VgaDacIndex
= Data
* 3;
1084 /* Ignore writes in read mode */
1085 if (VgaDacReadWrite
) VgaWriteDac(Data
& 0x3F);
1090 case VGA_MISC_WRITE
:
1092 VgaMiscRegister
= Data
;
1097 case VGA_CRTC_INDEX
:
1099 /* Set the CRTC index register */
1100 if (Data
< VGA_CRTC_MAX_REG
) VgaCrtcIndex
= Data
;
1107 /* Call the CRTC function */
1115 /* Set the GC index register */
1116 if (Data
< VGA_GC_MAX_REG
) VgaGcIndex
= Data
;
1122 /* Call the GC function */
1130 VOID
VgaClearMemory(VOID
)
1132 ZeroMemory(VgaMemory
, sizeof(VgaMemory
));
1135 BOOLEAN
VgaInitialize(HANDLE TextHandle
)
1141 COORD Origin
= { 0, 0 };
1142 SMALL_RECT ScreenRect
;
1143 PCHAR_INFO CharBuffer
;
1146 LPLOGPALETTE Palette
;
1148 /* Set the global handle */
1149 TextConsoleBuffer
= TextHandle
;
1151 /* Clear the VGA memory */
1154 /* Set the default video mode */
1155 BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE
);
1159 Resolution
= VgaGetDisplayResolution();
1160 CharBuffer
= (PCHAR_INFO
)ConsoleFramebuffer
;
1161 AddressSize
= VgaGetAddressSize();
1162 ScreenRect
.Left
= ScreenRect
.Top
= 0;
1163 ScreenRect
.Right
= Resolution
.X
;
1164 ScreenRect
.Bottom
= Resolution
.Y
;
1165 ScanlineSize
= (DWORD
)VgaCrtcRegisters
[VGA_CRTC_OFFSET_REG
] * 2;
1167 /* Read the data from the console into the framebuffer */
1168 ReadConsoleOutputA(TextConsoleBuffer
,
1174 /* Loop through the scanlines */
1175 for (i
= 0; i
< Resolution
.Y
; i
++)
1177 /* Loop through the characters */
1178 for (j
= 0; j
< Resolution
.X
; j
++)
1180 CurrentAddr
= LOWORD((Address
+ j
) * AddressSize
);
1182 /* Store the character in plane 0 */
1183 VgaMemory
[CurrentAddr
] = CharBuffer
[i
* Resolution
.X
+ j
].Char
.AsciiChar
;
1185 /* Store the attribute in plane 1 */
1186 VgaMemory
[CurrentAddr
+ VGA_BANK_SIZE
] = (BYTE
)CharBuffer
[i
* Resolution
.X
+ j
].Attributes
;
1189 /* Move to the next scanline */
1190 Address
+= ScanlineSize
;
1193 /* Allocate storage space for the palette */
1194 Palette
= (LPLOGPALETTE
)HeapAlloc(GetProcessHeap(),
1197 + VGA_MAX_COLORS
* sizeof(PALETTEENTRY
));
1198 if (Palette
== NULL
) return FALSE
;
1200 /* Initialize the palette */
1201 Palette
->palVersion
= 0x0300;
1202 Palette
->palNumEntries
= VGA_MAX_COLORS
;
1204 /* Copy the colors of the default palette to the DAC and console palette */
1205 for (i
= 0; i
< VGA_MAX_COLORS
; i
++)
1207 /* Set the palette entries */
1208 Palette
->palPalEntry
[i
].peRed
= GetRValue(VgaDefaultPalette
[i
]);
1209 Palette
->palPalEntry
[i
].peGreen
= GetGValue(VgaDefaultPalette
[i
]);
1210 Palette
->palPalEntry
[i
].peBlue
= GetBValue(VgaDefaultPalette
[i
]);
1211 Palette
->palPalEntry
[i
].peFlags
= 0;
1213 /* Set the DAC registers */
1214 VgaDacRegisters
[i
* 3] = VGA_COLOR_TO_DAC(GetRValue(VgaDefaultPalette
[i
]));
1215 VgaDacRegisters
[i
* 3 + 1] = VGA_COLOR_TO_DAC(GetGValue(VgaDefaultPalette
[i
]));
1216 VgaDacRegisters
[i
* 3 + 2] = VGA_COLOR_TO_DAC(GetBValue(VgaDefaultPalette
[i
]));
1219 /* Create the palette */
1220 PaletteHandle
= CreatePalette(Palette
);
1222 /* Free the palette */
1223 HeapFree(GetProcessHeap(), 0, Palette
);
1225 /* Return success if the palette was successfully created */
1226 return (PaletteHandle
? TRUE
: FALSE
);