f6d91ab003b656517ac53244442b7bfebd12d338
[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 "emulator.h"
14 #include "vga.h"
15
16 #include "io.h"
17 #include "bios.h"
18
19 /* PRIVATE VARIABLES **********************************************************/
20
21 static CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
22 static CONST DWORD MemoryLimit[] = { 0xAFFFF, 0xAFFFF, 0xB7FFF, 0xBFFFF };
23
24
25 /*static*/ CONST COLORREF EgaPalette[VGA_MAX_COLORS / 4] =
26 {
27 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA),
28 RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0xAA, 0x00), RGB(0xAA, 0xAA, 0xAA),
29 RGB(0x00, 0x00, 0x55), RGB(0x00, 0x00, 0xFF), RGB(0x00, 0xAA, 0x55), RGB(0x00, 0xAA, 0xFF),
30 RGB(0xAA, 0x00, 0x55), RGB(0xAA, 0x00, 0xFF), RGB(0xAA, 0xAA, 0x55), RGB(0xAA, 0xAA, 0xFF),
31 RGB(0x00, 0x55, 0x00), RGB(0x00, 0x55, 0xAA), RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0xAA),
32 RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0x55, 0xAA), RGB(0xAA, 0xFF, 0x00), RGB(0xAA, 0xFF, 0xAA),
33 RGB(0x00, 0x55, 0x55), RGB(0x00, 0x55, 0xFF), RGB(0x00, 0xFF, 0x55), RGB(0x00, 0xFF, 0xFF),
34 RGB(0xAA, 0x55, 0x55), RGB(0xAA, 0x55, 0xFF), RGB(0xAA, 0xFF, 0x55), RGB(0xAA, 0xFF, 0xFF),
35 RGB(0x55, 0x00, 0x00), RGB(0x55, 0x00, 0xAA), RGB(0x55, 0xAA, 0x00), RGB(0x55, 0xAA, 0xAA),
36 RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x00, 0xAA), RGB(0xFF, 0xAA, 0x00), RGB(0xFF, 0xAA, 0xAA),
37 RGB(0x55, 0x00, 0x55), RGB(0x55, 0x00, 0xFF), RGB(0x55, 0xAA, 0x55), RGB(0x55, 0xAA, 0xFF),
38 RGB(0xFF, 0x00, 0x55), RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0xAA, 0x55), RGB(0xFF, 0xAA, 0xFF),
39 RGB(0x55, 0x55, 0x00), RGB(0x55, 0x55, 0xAA), RGB(0x55, 0xFF, 0x00), RGB(0x55, 0xFF, 0xAA),
40 RGB(0xFF, 0x55, 0x00), RGB(0xFF, 0x55, 0xAA), RGB(0xFF, 0xFF, 0x00), RGB(0xFF, 0xFF, 0xAA),
41 RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF),
42 RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF)
43 };
44
45 static CONST COLORREF VgaDefaultPalette[VGA_MAX_COLORS] =
46 {
47 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA),
48 RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA),
49 RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF),
50 RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF),
51 RGB(0x00, 0x00, 0x00), RGB(0x10, 0x10, 0x10), RGB(0x20, 0x20, 0x20), RGB(0x35, 0x35, 0x35),
52 RGB(0x45, 0x45, 0x45), RGB(0x55, 0x55, 0x55), RGB(0x65, 0x65, 0x65), RGB(0x75, 0x75, 0x75),
53 RGB(0x8A, 0x8A, 0x8A), RGB(0x9A, 0x9A, 0x9A), RGB(0xAA, 0xAA, 0xAA), RGB(0xBA, 0xBA, 0xBA),
54 RGB(0xCA, 0xCA, 0xCA), RGB(0xDF, 0xDF, 0xDF), RGB(0xEF, 0xEF, 0xEF), RGB(0xFF, 0xFF, 0xFF),
55 RGB(0x00, 0x00, 0xFF), RGB(0x41, 0x00, 0xFF), RGB(0x82, 0x00, 0xFF), RGB(0xBE, 0x00, 0xFF),
56 RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0x00, 0xBE), RGB(0xFF, 0x00, 0x82), RGB(0xFF, 0x00, 0x41),
57 RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x41, 0x00), RGB(0xFF, 0x82, 0x00), RGB(0xFF, 0xBE, 0x00),
58 RGB(0xFF, 0xFF, 0x00), RGB(0xBE, 0xFF, 0x00), RGB(0x82, 0xFF, 0x00), RGB(0x41, 0xFF, 0x00),
59 RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0x41), RGB(0x00, 0xFF, 0x82), RGB(0x00, 0xFF, 0xBE),
60 RGB(0x00, 0xFF, 0xFF), RGB(0x00, 0xBE, 0xFF), RGB(0x00, 0x82, 0xFF), RGB(0x00, 0x41, 0xFF),
61 RGB(0x82, 0x82, 0xFF), RGB(0x9E, 0x82, 0xFF), RGB(0xBE, 0x82, 0xFF), RGB(0xDF, 0x82, 0xFF),
62 RGB(0xFF, 0x82, 0xFF), RGB(0xFF, 0x82, 0xDF), RGB(0xFF, 0x82, 0xBE), RGB(0xFF, 0x82, 0x9E),
63 RGB(0xFF, 0x82, 0x82), RGB(0xFF, 0x9E, 0x82), RGB(0xFF, 0xBE, 0x82), RGB(0xFF, 0xDF, 0x82),
64 RGB(0xFF, 0xFF, 0x82), RGB(0xDF, 0xFF, 0x82), RGB(0xBE, 0xFF, 0x82), RGB(0x9E, 0xFF, 0x82),
65 RGB(0x82, 0xFF, 0x82), RGB(0x82, 0xFF, 0x9E), RGB(0x82, 0xFF, 0xBE), RGB(0x82, 0xFF, 0xDF),
66 RGB(0x82, 0xFF, 0xFF), RGB(0x82, 0xDF, 0xFF), RGB(0x82, 0xBE, 0xFF), RGB(0x82, 0x9E, 0xFF),
67 RGB(0xBA, 0xBA, 0xFF), RGB(0xCA, 0xBA, 0xFF), RGB(0xDF, 0xBA, 0xFF), RGB(0xEF, 0xBA, 0xFF),
68 RGB(0xFF, 0xBA, 0xFF), RGB(0xFF, 0xBA, 0xEF), RGB(0xFF, 0xBA, 0xDF), RGB(0xFF, 0xBA, 0xCA),
69 RGB(0xFF, 0xBA, 0xBA), RGB(0xFF, 0xCA, 0xBA), RGB(0xFF, 0xDF, 0xBA), RGB(0xFF, 0xEF, 0xBA),
70 RGB(0xFF, 0xFF, 0xBA), RGB(0xEF, 0xFF, 0xBA), RGB(0xDF, 0xFF, 0xBA), RGB(0xCA, 0xFF, 0xBA),
71 RGB(0xBA, 0xFF, 0xBA), RGB(0xBA, 0xFF, 0xCA), RGB(0xBA, 0xFF, 0xDF), RGB(0xBA, 0xFF, 0xEF),
72 RGB(0xBA, 0xFF, 0xFF), RGB(0xBA, 0xEF, 0xFF), RGB(0xBA, 0xDF, 0xFF), RGB(0xBA, 0xCA, 0xFF),
73 RGB(0x00, 0x00, 0x71), RGB(0x1C, 0x00, 0x71), RGB(0x39, 0x00, 0x71), RGB(0x55, 0x00, 0x71),
74 RGB(0x71, 0x00, 0x71), RGB(0x71, 0x00, 0x55), RGB(0x71, 0x00, 0x39), RGB(0x71, 0x00, 0x1C),
75 RGB(0x71, 0x00, 0x00), RGB(0x71, 0x1C, 0x00), RGB(0x71, 0x39, 0x00), RGB(0x71, 0x55, 0x00),
76 RGB(0x71, 0x71, 0x00), RGB(0x55, 0x71, 0x00), RGB(0x39, 0x71, 0x00), RGB(0x1C, 0x71, 0x00),
77 RGB(0x00, 0x71, 0x00), RGB(0x00, 0x71, 0x1C), RGB(0x00, 0x71, 0x39), RGB(0x00, 0x71, 0x55),
78 RGB(0x00, 0x71, 0x71), RGB(0x00, 0x55, 0x71), RGB(0x00, 0x39, 0x71), RGB(0x00, 0x1C, 0x71),
79 RGB(0x39, 0x39, 0x71), RGB(0x45, 0x39, 0x71), RGB(0x55, 0x39, 0x71), RGB(0x61, 0x39, 0x71),
80 RGB(0x71, 0x39, 0x71), RGB(0x71, 0x39, 0x61), RGB(0x71, 0x39, 0x55), RGB(0x71, 0x39, 0x45),
81 RGB(0x71, 0x39, 0x39), RGB(0x71, 0x45, 0x39), RGB(0x71, 0x55, 0x39), RGB(0x71, 0x61, 0x39),
82 RGB(0x71, 0x71, 0x39), RGB(0x61, 0x71, 0x39), RGB(0x55, 0x71, 0x39), RGB(0x45, 0x71, 0x39),
83 RGB(0x39, 0x71, 0x39), RGB(0x39, 0x71, 0x45), RGB(0x39, 0x71, 0x55), RGB(0x39, 0x71, 0x61),
84 RGB(0x39, 0x71, 0x71), RGB(0x39, 0x61, 0x71), RGB(0x39, 0x55, 0x71), RGB(0x39, 0x45, 0x71),
85 RGB(0x51, 0x51, 0x71), RGB(0x59, 0x51, 0x71), RGB(0x61, 0x51, 0x71), RGB(0x69, 0x51, 0x71),
86 RGB(0x71, 0x51, 0x71), RGB(0x71, 0x51, 0x69), RGB(0x71, 0x51, 0x61), RGB(0x71, 0x51, 0x59),
87 RGB(0x71, 0x51, 0x51), RGB(0x71, 0x59, 0x51), RGB(0x71, 0x61, 0x51), RGB(0x71, 0x69, 0x51),
88 RGB(0x71, 0x71, 0x51), RGB(0x69, 0x71, 0x51), RGB(0x61, 0x71, 0x51), RGB(0x59, 0x71, 0x51),
89 RGB(0x51, 0x71, 0x51), RGB(0x51, 0x71, 0x59), RGB(0x51, 0x71, 0x61), RGB(0x51, 0x71, 0x69),
90 RGB(0x51, 0x71, 0x71), RGB(0x51, 0x69, 0x71), RGB(0x51, 0x61, 0x71), RGB(0x51, 0x59, 0x71),
91 RGB(0x00, 0x00, 0x41), RGB(0x10, 0x00, 0x41), RGB(0x20, 0x00, 0x41), RGB(0x31, 0x00, 0x41),
92 RGB(0x41, 0x00, 0x41), RGB(0x41, 0x00, 0x31), RGB(0x41, 0x00, 0x20), RGB(0x41, 0x00, 0x10),
93 RGB(0x41, 0x00, 0x00), RGB(0x41, 0x10, 0x00), RGB(0x41, 0x20, 0x00), RGB(0x41, 0x31, 0x00),
94 RGB(0x41, 0x41, 0x00), RGB(0x31, 0x41, 0x00), RGB(0x20, 0x41, 0x00), RGB(0x10, 0x41, 0x00),
95 RGB(0x00, 0x41, 0x00), RGB(0x00, 0x41, 0x10), RGB(0x00, 0x41, 0x20), RGB(0x00, 0x41, 0x31),
96 RGB(0x00, 0x41, 0x41), RGB(0x00, 0x31, 0x41), RGB(0x00, 0x20, 0x41), RGB(0x00, 0x10, 0x41),
97 RGB(0x20, 0x20, 0x41), RGB(0x28, 0x20, 0x41), RGB(0x31, 0x20, 0x41), RGB(0x39, 0x20, 0x41),
98 RGB(0x41, 0x20, 0x41), RGB(0x41, 0x20, 0x39), RGB(0x41, 0x20, 0x31), RGB(0x41, 0x20, 0x28),
99 RGB(0x41, 0x20, 0x20), RGB(0x41, 0x28, 0x20), RGB(0x41, 0x31, 0x20), RGB(0x41, 0x39, 0x20),
100 RGB(0x41, 0x41, 0x20), RGB(0x39, 0x41, 0x20), RGB(0x31, 0x41, 0x20), RGB(0x28, 0x41, 0x20),
101 RGB(0x20, 0x41, 0x20), RGB(0x20, 0x41, 0x28), RGB(0x20, 0x41, 0x31), RGB(0x20, 0x41, 0x39),
102 RGB(0x20, 0x41, 0x41), RGB(0x20, 0x39, 0x41), RGB(0x20, 0x31, 0x41), RGB(0x20, 0x28, 0x41),
103 RGB(0x2D, 0x2D, 0x41), RGB(0x31, 0x2D, 0x41), RGB(0x35, 0x2D, 0x41), RGB(0x3D, 0x2D, 0x41),
104 RGB(0x41, 0x2D, 0x41), RGB(0x41, 0x2D, 0x3D), RGB(0x41, 0x2D, 0x35), RGB(0x41, 0x2D, 0x31),
105 RGB(0x41, 0x2D, 0x2D), RGB(0x41, 0x31, 0x2D), RGB(0x41, 0x35, 0x2D), RGB(0x41, 0x3D, 0x2D),
106 RGB(0x41, 0x41, 0x2D), RGB(0x3D, 0x41, 0x2D), RGB(0x35, 0x41, 0x2D), RGB(0x31, 0x41, 0x2D),
107 RGB(0x2D, 0x41, 0x2D), RGB(0x2D, 0x41, 0x31), RGB(0x2D, 0x41, 0x35), RGB(0x2D, 0x41, 0x3D),
108 RGB(0x2D, 0x41, 0x41), RGB(0x2D, 0x3D, 0x41), RGB(0x2D, 0x35, 0x41), RGB(0x2D, 0x31, 0x41),
109 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00),
110 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00)
111 };
112
113 static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
114 static LPVOID ConsoleFramebuffer = NULL;
115 static HANDLE TextConsoleBuffer = NULL;
116 static HANDLE GraphicsConsoleBuffer = NULL;
117 static HANDLE ConsoleMutex = NULL;
118 static HPALETTE PaletteHandle = NULL;
119 static BOOLEAN DoubleVision = FALSE;
120
121 static BYTE VgaLatchRegisters[VGA_NUM_BANKS] = {0, 0, 0, 0};
122
123 static BYTE VgaMiscRegister;
124 static BYTE VgaFeatureRegister;
125
126 static BYTE VgaSeqIndex = VGA_SEQ_RESET_REG;
127 static BYTE VgaSeqRegisters[VGA_SEQ_MAX_REG];
128
129 static BYTE VgaCrtcIndex = VGA_CRTC_HORZ_TOTAL_REG;
130 static BYTE VgaCrtcRegisters[VGA_CRTC_MAX_REG];
131
132 static BYTE VgaGcIndex = VGA_GC_RESET_REG;
133 static BYTE VgaGcRegisters[VGA_GC_MAX_REG];
134
135 static BOOLEAN VgaAcLatch = FALSE;
136 static BYTE VgaAcIndex = VGA_AC_PAL_0_REG;
137 static BYTE VgaAcRegisters[VGA_AC_MAX_REG];
138
139 // static VGA_REGISTERS VgaRegisters;
140
141 static BYTE VgaDacMask = 0xFF;
142 static WORD VgaDacIndex = 0;
143 static BOOLEAN VgaDacReadWrite = FALSE;
144 static BYTE VgaDacRegisters[VGA_PALETTE_SIZE];
145
146 static BOOLEAN InVerticalRetrace = FALSE;
147 static BOOLEAN InHorizontalRetrace = FALSE;
148
149 static BOOLEAN NeedsUpdate = FALSE;
150 static BOOLEAN ModeChanged = TRUE;
151 static BOOLEAN CursorMoved = FALSE;
152 static BOOLEAN PaletteChanged = FALSE;
153
154 static
155 enum SCREEN_MODE
156 {
157 TEXT_MODE,
158 GRAPHICS_MODE
159 } ScreenMode = TEXT_MODE;
160
161 static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
162
163 /* PRIVATE FUNCTIONS **********************************************************/
164
165 static inline INT VgaGetAddressSize(VOID)
166 {
167 if (VgaCrtcRegisters[VGA_CRTC_UNDERLINE_REG] & VGA_CRTC_UNDERLINE_DWORD)
168 {
169 /* Double-word addressing */
170 return 4; // sizeof(DWORD)
171 }
172
173 if (VgaCrtcRegisters[VGA_CRTC_MODE_CONTROL_REG] & VGA_CRTC_MODE_CONTROL_BYTE)
174 {
175 /* Byte addressing */
176 return 1; // sizeof(BYTE)
177 }
178
179 /* Word addressing */
180 return 2; // sizeof(WORD)
181 }
182
183 static inline DWORD VgaTranslateReadAddress(DWORD Address)
184 {
185 DWORD Offset = Address - VgaGetVideoBaseAddress();
186 BYTE Plane;
187
188 /* Check for chain-4 and odd-even mode */
189 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
190 {
191 /* The lowest two bits are the plane number */
192 Plane = Offset & 3;
193 Offset >>= 2;
194 }
195 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
196 {
197 /* The LSB is the plane number */
198 Plane = Offset & 1;
199 Offset >>= 1;
200 }
201 else
202 {
203 /* Use the read mode */
204 Plane = VgaGcRegisters[VGA_GC_READ_MAP_SEL_REG] & 0x03;
205 }
206
207 /* Multiply the offset by the address size */
208 Offset *= VgaGetAddressSize();
209
210 return Offset + Plane * VGA_BANK_SIZE;
211 }
212
213 static inline DWORD VgaTranslateWriteAddress(DWORD Address)
214 {
215 DWORD Offset = Address - VgaGetVideoBaseAddress();
216
217 /* Check for chain-4 and odd-even mode */
218 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
219 {
220 /* Shift the offset to the right by 2 */
221 Offset >>= 2;
222 }
223 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
224 {
225 /* Shift the offset to the right by 1 */
226 Offset >>= 1;
227 }
228
229 /* Multiply the offset by the address size */
230 Offset *= VgaGetAddressSize();
231
232 /* Return the offset on plane 0 */
233 return Offset;
234 }
235
236 static inline BYTE VgaTranslateByteForWriting(BYTE Data, BYTE Plane)
237 {
238 BYTE WriteMode = VgaGcRegisters[VGA_GC_MODE_REG] & 3;
239 BYTE BitMask = VgaGcRegisters[VGA_GC_BITMASK_REG];
240
241 if (WriteMode == 1)
242 {
243 /* In write mode 1 just return the latch register */
244 return VgaLatchRegisters[Plane];
245 }
246
247 if (WriteMode != 2)
248 {
249 /* Write modes 0 and 3 rotate the data to the right first */
250 BYTE RotateCount = VgaGcRegisters[VGA_GC_ROTATE_REG] & 7;
251 Data = LOBYTE(((DWORD)Data >> RotateCount) | ((DWORD)Data << (8 - RotateCount)));
252 }
253 else
254 {
255 /* Write mode 2 expands the appropriate bit to all 8 bits */
256 Data = (Data & (1 << Plane)) ? 0xFF : 0x00;
257 }
258
259 if (WriteMode == 0)
260 {
261 /*
262 * In write mode 0, the enable set/reset register decides if the
263 * set/reset bit should be expanded to all 8 bits.
264 */
265 if (VgaGcRegisters[VGA_GC_ENABLE_RESET_REG] & (1 << Plane))
266 {
267 /* Copy the bit from the set/reset register to all 8 bits */
268 Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00;
269 }
270 }
271
272 if (WriteMode != 3)
273 {
274 /* Write modes 0 and 2 then perform a logical operation on the data and latch */
275 BYTE LogicalOperation = (VgaGcRegisters[VGA_GC_ROTATE_REG] >> 3) & 3;
276
277 if (LogicalOperation == 1) Data &= VgaLatchRegisters[Plane];
278 else if (LogicalOperation == 2) Data |= VgaLatchRegisters[Plane];
279 else if (LogicalOperation == 3) Data ^= VgaLatchRegisters[Plane];
280 }
281 else
282 {
283 /* For write mode 3, we AND the bitmask with the data, which is used as the new bitmask */
284 BitMask &= Data;
285
286 /* Then we expand the bit in the set/reset field */
287 Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00;
288 }
289
290 /* Bits cleared in the bitmask are replaced with latch register bits */
291 Data = (Data & BitMask) | (VgaLatchRegisters[Plane] & (~BitMask));
292
293 /* Return the byte */
294 return Data;
295 }
296
297 static inline VOID VgaMarkForUpdate(SHORT Row, SHORT Column)
298 {
299 /* Check if this is the first time the rectangle is updated */
300 if (!NeedsUpdate)
301 {
302 UpdateRectangle.Left = UpdateRectangle.Top = MAXSHORT;
303 UpdateRectangle.Right = UpdateRectangle.Bottom = MINSHORT;
304 }
305
306 /* Expand the rectangle to include the point */
307 UpdateRectangle.Left = min(UpdateRectangle.Left, Column);
308 UpdateRectangle.Right = max(UpdateRectangle.Right, Column);
309 UpdateRectangle.Top = min(UpdateRectangle.Top, Row);
310 UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Row);
311
312 /* Set the update request flag */
313 NeedsUpdate = TRUE;
314 }
315
316 static VOID VgaWriteSequencer(BYTE Data)
317 {
318 ASSERT(VgaSeqIndex < VGA_SEQ_MAX_REG);
319
320 /* Save the value */
321 VgaSeqRegisters[VgaSeqIndex] = Data;
322 }
323
324 static VOID VgaWriteGc(BYTE Data)
325 {
326 ASSERT(VgaGcIndex < VGA_GC_MAX_REG);
327
328 /* Save the value */
329 VgaGcRegisters[VgaGcIndex] = Data;
330
331 /* Check the index */
332 switch (VgaGcIndex)
333 {
334 case VGA_GC_MISC_REG:
335 {
336 /* The GC misc register decides if it's text or graphics mode */
337 ModeChanged = TRUE;
338 break;
339 }
340 }
341 }
342
343 static VOID VgaWriteCrtc(BYTE Data)
344 {
345 ASSERT(VgaGcIndex < VGA_CRTC_MAX_REG);
346
347 /* Save the value */
348 VgaCrtcRegisters[VgaCrtcIndex] = Data;
349
350 /* Check the index */
351 switch (VgaCrtcIndex)
352 {
353 case VGA_CRTC_END_HORZ_DISP_REG:
354 case VGA_CRTC_VERT_DISP_END_REG:
355 case VGA_CRTC_OVERFLOW_REG:
356 {
357 /* The video mode has changed */
358 ModeChanged = TRUE;
359 break;
360 }
361
362 case VGA_CRTC_CURSOR_LOC_LOW_REG:
363 case VGA_CRTC_CURSOR_LOC_HIGH_REG:
364 case VGA_CRTC_CURSOR_START_REG:
365 case VGA_CRTC_CURSOR_END_REG:
366 {
367 /* Set the cursor moved flag */
368 CursorMoved = TRUE;
369 break;
370 }
371 }
372 }
373
374 static VOID VgaWriteDac(BYTE Data)
375 {
376 INT PaletteIndex;
377 PALETTEENTRY Entry;
378
379 /* Set the value */
380 VgaDacRegisters[VgaDacIndex] = Data;
381
382 /* Find the palette index */
383 PaletteIndex = VgaDacIndex / 3;
384
385 /* Fill the entry structure */
386 Entry.peRed = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3]);
387 Entry.peGreen = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3 + 1]);
388 Entry.peBlue = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3 + 2]);
389 Entry.peFlags = 0;
390
391 /* Update the palette entry */
392 SetPaletteEntries(PaletteHandle, PaletteIndex, 1, &Entry);
393
394 /* Set the palette change flag */
395 PaletteChanged = TRUE;
396
397 /* Update the index */
398 VgaDacIndex++;
399 VgaDacIndex %= VGA_PALETTE_SIZE;
400 }
401
402 static VOID VgaWriteAc(BYTE Data)
403 {
404 ASSERT(VgaAcIndex < VGA_AC_MAX_REG);
405
406 /* Save the value */
407 VgaAcRegisters[VgaAcIndex] = Data;
408
409 if (VgaAcIndex <= VGA_AC_PAL_F_REG)
410 {
411 /* Set the palette change flag */
412 PaletteChanged = TRUE;
413 }
414 }
415
416 static VOID VgaRestoreDefaultPalette(PPALETTEENTRY Entries, USHORT NumOfEntries)
417 {
418 USHORT i;
419
420 /* Copy the colors of the default palette to the DAC and console palette */
421 for (i = 0; i < NumOfEntries; i++)
422 {
423 /* Set the palette entries */
424 Entries[i].peRed = GetRValue(VgaDefaultPalette[i]);
425 Entries[i].peGreen = GetGValue(VgaDefaultPalette[i]);
426 Entries[i].peBlue = GetBValue(VgaDefaultPalette[i]);
427 Entries[i].peFlags = 0;
428
429 /* Set the DAC registers */
430 VgaDacRegisters[i * 3] = VGA_COLOR_TO_DAC(GetRValue(VgaDefaultPalette[i]));
431 VgaDacRegisters[i * 3 + 1] = VGA_COLOR_TO_DAC(GetGValue(VgaDefaultPalette[i]));
432 VgaDacRegisters[i * 3 + 2] = VGA_COLOR_TO_DAC(GetBValue(VgaDefaultPalette[i]));
433 }
434 }
435
436 static BOOLEAN VgaInitializePalette(VOID)
437 {
438 LPLOGPALETTE Palette;
439
440 /* Allocate storage space for the palette */
441 Palette = (LPLOGPALETTE)HeapAlloc(GetProcessHeap(),
442 HEAP_ZERO_MEMORY,
443 sizeof(LOGPALETTE) +
444 VGA_MAX_COLORS * sizeof(PALETTEENTRY));
445 if (Palette == NULL) return FALSE;
446
447 /* Initialize the palette */
448 Palette->palVersion = 0x0300;
449 Palette->palNumEntries = VGA_MAX_COLORS;
450
451 /* Restore the default palette */
452 VgaRestoreDefaultPalette(Palette->palPalEntry, Palette->palNumEntries);
453
454 /* Create the palette */
455 PaletteHandle = CreatePalette(Palette);
456
457 /* Free the palette */
458 HeapFree(GetProcessHeap(), 0, Palette);
459
460 /* Fail if the palette wasn't successfully created... */
461 if (PaletteHandle == NULL) return FALSE;
462
463 /* ... otherwise return success */
464 return TRUE;
465 }
466
467 static BOOL VgaEnterGraphicsMode(PCOORD Resolution)
468 {
469 DWORD i;
470 CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
471 BYTE BitmapInfoBuffer[VGA_BITMAP_INFO_SIZE];
472 LPBITMAPINFO BitmapInfo = (LPBITMAPINFO)BitmapInfoBuffer;
473 LPWORD PaletteIndex = (LPWORD)(BitmapInfo->bmiColors);
474
475 LONG Width = Resolution->X;
476 LONG Height = Resolution->Y;
477
478 /* Use DoubleVision mode if the resolution is too small */
479 if (Width < VGA_MINIMUM_WIDTH && Height < VGA_MINIMUM_HEIGHT)
480 {
481 DoubleVision = TRUE;
482 Width *= 2;
483 Height *= 2;
484 }
485
486 /* Fill the bitmap info header */
487 ZeroMemory(&BitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER));
488 BitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
489 BitmapInfo->bmiHeader.biWidth = Width;
490 BitmapInfo->bmiHeader.biHeight = Height;
491 BitmapInfo->bmiHeader.biBitCount = 8;
492 BitmapInfo->bmiHeader.biPlanes = 1;
493 BitmapInfo->bmiHeader.biCompression = BI_RGB;
494 BitmapInfo->bmiHeader.biSizeImage = Width * Height /* * 1 == biBitCount / 8 */;
495
496 /* Fill the palette data */
497 for (i = 0; i < (VGA_PALETTE_SIZE / 3); i++) PaletteIndex[i] = (WORD)i;
498
499 /* Fill the console graphics buffer info */
500 GraphicsBufferInfo.dwBitMapInfoLength = VGA_BITMAP_INFO_SIZE;
501 GraphicsBufferInfo.lpBitMapInfo = BitmapInfo;
502 GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS;
503
504 /* Create the buffer */
505 GraphicsConsoleBuffer = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
506 FILE_SHARE_READ | FILE_SHARE_WRITE,
507 NULL,
508 CONSOLE_GRAPHICS_BUFFER,
509 &GraphicsBufferInfo);
510 if (GraphicsConsoleBuffer == INVALID_HANDLE_VALUE) return FALSE;
511
512 /* Save the framebuffer address and mutex */
513 ConsoleFramebuffer = GraphicsBufferInfo.lpBitMap;
514 ConsoleMutex = GraphicsBufferInfo.hMutex;
515
516 /* Clear the framebuffer */
517 ZeroMemory(ConsoleFramebuffer, BitmapInfo->bmiHeader.biSizeImage);
518
519 /* Set the active buffer */
520 SetConsoleActiveScreenBuffer(GraphicsConsoleBuffer);
521
522 /* Set the graphics mode palette */
523 SetConsolePalette(GraphicsConsoleBuffer,
524 PaletteHandle,
525 SYSPAL_NOSTATIC256);
526
527 /* Set the screen mode flag */
528 ScreenMode = GRAPHICS_MODE;
529
530 return TRUE;
531 }
532
533 static VOID VgaLeaveGraphicsMode(VOID)
534 {
535 /* Release the console framebuffer mutex if needed */
536 ReleaseMutex(ConsoleMutex);
537
538 /* Switch back to the text buffer */
539 SetConsoleActiveScreenBuffer(TextConsoleBuffer);
540
541 /* Cleanup the video data */
542 CloseHandle(ConsoleMutex);
543 ConsoleMutex = NULL;
544 CloseHandle(GraphicsConsoleBuffer);
545 GraphicsConsoleBuffer = NULL;
546 DoubleVision = FALSE;
547 }
548
549 static BOOL VgaEnterTextMode(PCOORD Resolution)
550 {
551 /* Resize the console */
552 SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
553
554 /* Allocate a framebuffer */
555 ConsoleFramebuffer = HeapAlloc(GetProcessHeap(),
556 HEAP_ZERO_MEMORY,
557 Resolution->X * Resolution->Y
558 * sizeof(CHAR_INFO));
559 if (ConsoleFramebuffer == NULL)
560 {
561 DisplayMessage(L"An unexpected error occurred!\n");
562 VdmRunning = FALSE;
563 return FALSE;
564 }
565
566 /*
567 * Set the text mode palette.
568 *
569 * WARNING: This call should fail on Windows (and therefore
570 * we get the default palette and our external behaviour is
571 * just like Windows' one), but it should success on ReactOS
572 * (so that we get console palette changes even for text-mode
573 * screen-buffers, which is a new feature on ReactOS).
574 */
575 SetConsolePalette(TextConsoleBuffer,
576 PaletteHandle,
577 SYSPAL_NOSTATIC256);
578
579 /* Set the screen mode flag */
580 ScreenMode = TEXT_MODE;
581
582 return TRUE;
583 }
584
585 static VOID VgaLeaveTextMode(VOID)
586 {
587 /* Free the old framebuffer */
588 HeapFree(GetProcessHeap(), 0, ConsoleFramebuffer);
589 ConsoleFramebuffer = NULL;
590 }
591
592 static VOID VgaChangeMode(VOID)
593 {
594 COORD Resolution = VgaGetDisplayResolution();
595
596 /* Reset the mode change flag */
597 // ModeChanged = FALSE;
598
599 if (ScreenMode == GRAPHICS_MODE)
600 {
601 /* Leave the current graphics mode */
602 VgaLeaveGraphicsMode();
603 }
604 else
605 {
606 /* Leave the current text mode */
607 VgaLeaveTextMode();
608 }
609
610 /* Check if the new mode is alphanumeric */
611 if (!(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA))
612 {
613 /* Enter new text mode */
614 if (!VgaEnterTextMode(&Resolution))
615 {
616 DisplayMessage(L"An unexpected VGA error occurred while switching into text mode.");
617 VdmRunning = FALSE;
618 return;
619 }
620 }
621 else
622 {
623 /* Enter 8-bit graphics mode */
624 if (!VgaEnterGraphicsMode(&Resolution))
625 {
626 DisplayMessage(L"An unexpected VGA error occurred while switching into graphics mode.");
627 VdmRunning = FALSE;
628 return;
629 }
630 }
631
632 /* Trigger a full update of the screen */
633 NeedsUpdate = TRUE;
634 UpdateRectangle.Left = 0;
635 UpdateRectangle.Top = 0;
636 UpdateRectangle.Right = Resolution.X;
637 UpdateRectangle.Bottom = Resolution.Y;
638
639 /* Reset the mode change flag */
640 ModeChanged = FALSE;
641 }
642
643 static VOID VgaUpdateFramebuffer(VOID)
644 {
645 INT i, j, k;
646 COORD Resolution = VgaGetDisplayResolution();
647 INT AddressSize = VgaGetAddressSize();
648 DWORD Address = (VgaCrtcRegisters[VGA_CRTC_START_ADDR_HIGH_REG] << 8)
649 + VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG];
650 DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
651
652 /*
653 * If console framebuffer is NULL, that means something went wrong
654 * earlier and this is the final display refresh.
655 */
656 if (ConsoleFramebuffer == NULL) return;
657
658 /* Check if this is text mode or graphics mode */
659 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
660 {
661 /* Graphics mode */
662 PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
663
664 /*
665 * Synchronize access to the graphics framebuffer
666 * with the console framebuffer mutex.
667 */
668 WaitForSingleObject(ConsoleMutex, INFINITE);
669
670 /* Loop through the scanlines */
671 for (i = 0; i < Resolution.Y; i++)
672 {
673 /* Loop through the pixels */
674 for (j = 0; j < Resolution.X; j++)
675 {
676 BYTE PixelData = 0;
677
678 /* Check the shifting mode */
679 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFT256)
680 {
681 /* 4 bits shifted from each plane */
682
683 /* Check if this is 16 or 256 color mode */
684 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
685 {
686 /* One byte per pixel */
687 PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
688 + (Address + (j / VGA_NUM_BANKS))
689 * AddressSize];
690 }
691 else
692 {
693 /* 4-bits per pixel */
694
695 PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
696 + (Address + (j / (VGA_NUM_BANKS * 2)))
697 * AddressSize];
698
699 /* Check if we should use the highest 4 bits or lowest 4 */
700 if (((j / VGA_NUM_BANKS) % 2) == 0)
701 {
702 /* Highest 4 */
703 PixelData >>= 4;
704 }
705 else
706 {
707 /* Lowest 4 */
708 PixelData &= 0x0F;
709 }
710 }
711 }
712 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFTREG)
713 {
714 /* Check if this is 16 or 256 color mode */
715 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
716 {
717 // TODO: NOT IMPLEMENTED
718 DPRINT1("8-bit interleaved mode is not implemented!\n");
719 }
720 else
721 {
722 /*
723 * 2 bits shifted from plane 0 and 2 for the first 4 pixels,
724 * then 2 bits shifted from plane 1 and 3 for the next 4
725 */
726 BYTE LowPlaneData = VgaMemory[((j / 4) % 2) * VGA_BANK_SIZE
727 + (Address + (j / 4)) * AddressSize];
728 BYTE HighPlaneData = VgaMemory[(((j / 4) % 2) + 2) * VGA_BANK_SIZE
729 + (Address + (j / 4)) * AddressSize];
730
731 /* Extract the two bits from each plane */
732 LowPlaneData = (LowPlaneData >> (6 - ((j % 4) * 2))) & 3;
733 HighPlaneData = (HighPlaneData >> (6 - ((j % 4) * 2))) & 3;
734
735 /* Combine them into the pixel */
736 PixelData = LowPlaneData | (HighPlaneData << 2);
737 }
738 }
739 else
740 {
741 /* 1 bit shifted from each plane */
742
743 /* Check if this is 16 or 256 color mode */
744 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
745 {
746 /* 8 bits per pixel, 2 on each plane */
747
748 for (k = 0; k < VGA_NUM_BANKS; k++)
749 {
750 /* The data is on plane k, 4 pixels per byte */
751 BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
752 + (Address + (j / 4)) * AddressSize];
753
754 /* The mask of the first bit in the pair */
755 BYTE BitMask = 1 << (((3 - (j % 4)) * 2) + 1);
756
757 /* Bits 0, 1, 2 and 3 come from the first bit of the pair */
758 if (PlaneData & BitMask) PixelData |= 1 << k;
759
760 /* Bits 4, 5, 6 and 7 come from the second bit of the pair */
761 if (PlaneData & (BitMask >> 1)) PixelData |= 1 << (k + 4);
762 }
763 }
764 else
765 {
766 /* 4 bits per pixel, 1 on each plane */
767
768 for (k = 0; k < VGA_NUM_BANKS; k++)
769 {
770 BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
771 + (Address + (j / 8)) * AddressSize];
772
773 /* If the bit on that plane is set, set it */
774 if (PlaneData & (1 << (7 - (j % 8)))) PixelData |= 1 << k;
775 }
776 }
777 }
778
779 if (!(VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT))
780 {
781 /* In 16 color mode, the value is an index to the AC registers */
782 PixelData = VgaAcRegisters[PixelData];
783 }
784
785 /* Take into account DoubleVision mode when checking for pixel updates */
786 if (DoubleVision)
787 {
788 /* Now check if the resulting pixel data has changed */
789 if (GraphicsBuffer[(i * Resolution.X * 4) + (j * 2)] != PixelData)
790 {
791 /* Yes, write the new value */
792 GraphicsBuffer[(i * Resolution.X * 4) + (j * 2)] = PixelData;
793 GraphicsBuffer[(i * Resolution.X * 4) + (j * 2 + 1)] = PixelData;
794 GraphicsBuffer[((i * 2 + 1) * Resolution.X * 2) + (j * 2)] = PixelData;
795 GraphicsBuffer[((i * 2 + 1) * Resolution.X * 2) + (j * 2 + 1)] = PixelData;
796
797 /* Mark the specified pixel as changed */
798 VgaMarkForUpdate(i, j);
799 }
800 }
801 else
802 {
803 /* Now check if the resulting pixel data has changed */
804 if (GraphicsBuffer[i * Resolution.X + j] != PixelData)
805 {
806 /* Yes, write the new value */
807 GraphicsBuffer[i * Resolution.X + j] = PixelData;
808
809 /* Mark the specified pixel as changed */
810 VgaMarkForUpdate(i, j);
811 }
812 }
813 }
814
815 /* Move to the next scanline */
816 Address += ScanlineSize;
817 }
818
819 /*
820 * Release the console framebuffer mutex
821 * so that we allow for repainting.
822 */
823 ReleaseMutex(ConsoleMutex);
824 }
825 else
826 {
827 /* Text mode */
828 PCHAR_INFO CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
829
830 /* Loop through the scanlines */
831 for (i = 0; i < Resolution.Y; i++)
832 {
833 /* Loop through the characters */
834 for (j = 0; j < Resolution.X; j++)
835 {
836 DWORD CurrentAddr = LOWORD((Address + j) * AddressSize);
837 CHAR_INFO CharInfo;
838
839 /* Plane 0 holds the character itself */
840 CharInfo.Char.AsciiChar = VgaMemory[CurrentAddr];
841
842 /* Plane 1 holds the attribute */
843 CharInfo.Attributes = VgaMemory[CurrentAddr + VGA_BANK_SIZE];
844
845 /* Now check if the resulting character data has changed */
846 if ((CharBuffer[i * Resolution.X + j].Char.AsciiChar != CharInfo.Char.AsciiChar)
847 || (CharBuffer[i * Resolution.X + j].Attributes != CharInfo.Attributes))
848 {
849 /* Yes, write the new value */
850 CharBuffer[i * Resolution.X + j] = CharInfo;
851
852 /* Mark the specified cell as changed */
853 VgaMarkForUpdate(i, j);
854 }
855 }
856
857 /* Move to the next scanline */
858 Address += ScanlineSize;
859 }
860 }
861 }
862
863 static VOID VgaUpdateTextCursor(VOID)
864 {
865 COORD Position;
866 CONSOLE_CURSOR_INFO CursorInfo;
867 BYTE CursorStart = VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x3F;
868 BYTE CursorEnd = VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] & 0x1F;
869 DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
870 BYTE TextSize = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
871 WORD Location = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG],
872 VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG]);
873
874 /* Just return if we are not in text mode */
875 if ((VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA) != 0) return;
876
877 if (CursorStart < CursorEnd)
878 {
879 /* Visible cursor */
880 CursorInfo.bVisible = TRUE;
881 CursorInfo.dwSize = (100 * (CursorEnd - CursorStart)) / TextSize;
882 }
883 else
884 {
885 /* No cursor */
886 CursorInfo.bVisible = FALSE;
887 CursorInfo.dwSize = 0;
888 }
889
890 /* Add the cursor skew to the location */
891 Location += (VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] >> 5) & 3;
892
893 /* Find the coordinates of the new position */
894 Position.X = (WORD)(Location % ScanlineSize);
895 Position.Y = (WORD)(Location / ScanlineSize);
896
897 /* Update the physical cursor */
898 SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
899 SetConsoleCursorPosition(TextConsoleBuffer, Position);
900
901 /* Reset the cursor move flag */
902 CursorMoved = FALSE;
903 }
904
905 /* PUBLIC FUNCTIONS ***********************************************************/
906
907 DWORD VgaGetVideoBaseAddress(VOID)
908 {
909 return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
910 }
911
912 DWORD VgaGetVideoLimitAddress(VOID)
913 {
914 return MemoryLimit[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
915 }
916
917 COORD VgaGetDisplayResolution(VOID)
918 {
919 COORD Resolution;
920 BYTE MaximumScanLine = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
921
922 /* The low 8 bits are in the display registers */
923 Resolution.X = VgaCrtcRegisters[VGA_CRTC_END_HORZ_DISP_REG];
924 Resolution.Y = VgaCrtcRegisters[VGA_CRTC_VERT_DISP_END_REG];
925
926 /* Set the top bits from the overflow register */
927 if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE8)
928 {
929 Resolution.Y |= 1 << 8;
930 }
931 if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE9)
932 {
933 Resolution.Y |= 1 << 9;
934 }
935
936 /* Increase the values by 1 */
937 Resolution.X++;
938 Resolution.Y++;
939
940 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
941 {
942 /* Multiply the horizontal resolution by the 9/8 dot mode */
943 Resolution.X *= (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & VGA_SEQ_CLOCK_98DM)
944 ? 8 : 9;
945
946 /* The horizontal resolution is halved in 8-bit mode */
947 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2;
948 }
949
950 if (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & VGA_CRTC_MAXSCANLINE_DOUBLE)
951 {
952 /* Halve the vertical resolution */
953 Resolution.Y >>= 1;
954 }
955
956 /* Divide the vertical resolution by the maximum scan line (== font size in text mode) */
957 Resolution.Y /= MaximumScanLine;
958
959 /* Return the resolution */
960 return Resolution;
961 }
962
963 VOID VgaRefreshDisplay(VOID)
964 {
965 HANDLE ConsoleBufferHandle = NULL;
966 COORD Resolution;
967
968 /* Set the vertical retrace flag */
969 InVerticalRetrace = TRUE;
970
971 /* If nothing has changed, just return */
972 if (!ModeChanged && !CursorMoved && !PaletteChanged && !NeedsUpdate)
973 return;
974
975 /* Retrieve the current resolution */
976 Resolution = VgaGetDisplayResolution();
977
978 /* Change the display mode */
979 if (ModeChanged) VgaChangeMode();
980
981 /* Change the text cursor location */
982 if (CursorMoved) VgaUpdateTextCursor();
983
984 if (PaletteChanged)
985 {
986 /* Trigger a full update of the screen */
987 NeedsUpdate = TRUE;
988 UpdateRectangle.Left = 0;
989 UpdateRectangle.Top = 0;
990 UpdateRectangle.Right = Resolution.X;
991 UpdateRectangle.Bottom = Resolution.Y;
992
993 PaletteChanged = FALSE;
994 }
995
996 /* Update the contents of the framebuffer */
997 VgaUpdateFramebuffer();
998
999 /* Ignore if there's nothing to update */
1000 if (!NeedsUpdate) return;
1001
1002 DPRINT("Updating screen rectangle (%d, %d, %d, %d)\n",
1003 UpdateRectangle.Left,
1004 UpdateRectangle.Top,
1005 UpdateRectangle.Right,
1006 UpdateRectangle.Bottom);
1007
1008 /* Check if this is text mode or graphics mode */
1009 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
1010 {
1011 /* Graphics mode */
1012 ConsoleBufferHandle = GraphicsConsoleBuffer;
1013 }
1014 else
1015 {
1016 /* Text mode */
1017 COORD Origin = { UpdateRectangle.Left, UpdateRectangle.Top };
1018 ConsoleBufferHandle = TextConsoleBuffer;
1019
1020 /* Write the data to the console */
1021 WriteConsoleOutputA(TextConsoleBuffer,
1022 (PCHAR_INFO)ConsoleFramebuffer,
1023 Resolution,
1024 Origin,
1025 &UpdateRectangle);
1026 }
1027
1028 /* In DoubleVision mode, scale the update rectangle */
1029 if (DoubleVision)
1030 {
1031 UpdateRectangle.Left *= 2;
1032 UpdateRectangle.Top *= 2;
1033 UpdateRectangle.Right = UpdateRectangle.Right * 2 + 1;
1034 UpdateRectangle.Bottom = UpdateRectangle.Bottom * 2 + 1;
1035 }
1036
1037 /* Redraw the screen */
1038 InvalidateConsoleDIBits(ConsoleBufferHandle, &UpdateRectangle);
1039
1040 /* Clear the update flag */
1041 NeedsUpdate = FALSE;
1042 }
1043
1044 VOID VgaHorizontalRetrace(VOID)
1045 {
1046 /* Set the flag */
1047 InHorizontalRetrace = TRUE;
1048 }
1049
1050 VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
1051 {
1052 DWORD i;
1053 DWORD VideoAddress;
1054
1055 DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n", Address, Size);
1056
1057 /* Ignore if video RAM access is disabled */
1058 if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return;
1059
1060 /* Loop through each byte */
1061 for (i = 0; i < Size; i++)
1062 {
1063 VideoAddress = VgaTranslateReadAddress(Address + i);
1064
1065 /* Load the latch registers */
1066 VgaLatchRegisters[0] = VgaMemory[LOWORD(VideoAddress)];
1067 VgaLatchRegisters[1] = VgaMemory[VGA_BANK_SIZE + LOWORD(VideoAddress)];
1068 VgaLatchRegisters[2] = VgaMemory[(2 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
1069 VgaLatchRegisters[3] = VgaMemory[(3 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
1070
1071 /* Copy the value to the buffer */
1072 Buffer[i] = VgaMemory[VideoAddress];
1073 }
1074 }
1075
1076 VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
1077 {
1078 DWORD i, j;
1079 DWORD VideoAddress;
1080
1081 DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n", Address, Size);
1082
1083 /* Ignore if video RAM access is disabled */
1084 if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return;
1085
1086 /* Also ignore if write access to all planes is disabled */
1087 if ((VgaSeqRegisters[VGA_SEQ_MASK_REG] & 0x0F) == 0x00) return;
1088
1089 /* Loop through each byte */
1090 for (i = 0; i < Size; i++)
1091 {
1092 VideoAddress = VgaTranslateWriteAddress(Address + i);
1093
1094 for (j = 0; j < VGA_NUM_BANKS; j++)
1095 {
1096 /* Make sure the page is writeable */
1097 if (!(VgaSeqRegisters[VGA_SEQ_MASK_REG] & (1 << j))) continue;
1098
1099 /* Check if this is chain-4 mode */
1100 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
1101 {
1102 if (((Address + i) & 3) != j)
1103 {
1104 /* This plane will not be accessed */
1105 continue;
1106 }
1107 }
1108
1109 /* Check if this is odd-even mode */
1110 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
1111 {
1112 if (((Address + i) & 1) != (j & 1))
1113 {
1114 /* This plane will not be accessed */
1115 continue;
1116 }
1117 }
1118
1119 /* Copy the value to the VGA memory */
1120 VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = VgaTranslateByteForWriting(Buffer[i], j);
1121 }
1122 }
1123 }
1124
1125 BYTE WINAPI VgaReadPort(ULONG Port)
1126 {
1127 DPRINT("VgaReadPort: Port 0x%X\n", Port);
1128
1129 switch (Port)
1130 {
1131 case VGA_MISC_READ:
1132 return VgaMiscRegister;
1133
1134 case VGA_INSTAT0_READ:
1135 return 0; // Not implemented
1136
1137 case VGA_INSTAT1_READ_MONO:
1138 case VGA_INSTAT1_READ_COLOR:
1139 {
1140 BYTE Result = 0;
1141
1142 /* Reset the AC latch */
1143 VgaAcLatch = FALSE;
1144
1145 /* Set a flag if there is a vertical or horizontal retrace */
1146 if (InVerticalRetrace || InHorizontalRetrace) Result |= VGA_STAT_DD;
1147
1148 /* Set an additional flag if there was a vertical retrace */
1149 if (InVerticalRetrace) Result |= VGA_STAT_VRETRACE;
1150
1151 /* Clear the flags */
1152 InHorizontalRetrace = InVerticalRetrace = FALSE;
1153
1154 return Result;
1155 }
1156
1157 case VGA_FEATURE_READ:
1158 return VgaFeatureRegister;
1159
1160 case VGA_AC_INDEX:
1161 return VgaAcIndex;
1162
1163 case VGA_AC_READ:
1164 return VgaAcRegisters[VgaAcIndex];
1165
1166 case VGA_SEQ_INDEX:
1167 return VgaSeqIndex;
1168
1169 case VGA_SEQ_DATA:
1170 return VgaSeqRegisters[VgaSeqIndex];
1171
1172 case VGA_DAC_MASK:
1173 return VgaDacMask;
1174
1175 case VGA_DAC_READ_INDEX:
1176 /* This returns the read/write state */
1177 return (VgaDacReadWrite ? 0 : 3);
1178
1179 case VGA_DAC_WRITE_INDEX:
1180 return (VgaDacIndex / 3);
1181
1182 case VGA_DAC_DATA:
1183 {
1184 /* Ignore reads in write mode */
1185 if (!VgaDacReadWrite)
1186 {
1187 BYTE Data = VgaDacRegisters[VgaDacIndex++];
1188 VgaDacIndex %= VGA_PALETTE_SIZE;
1189 return Data;
1190 }
1191
1192 break;
1193 }
1194
1195 case VGA_CRTC_INDEX_MONO:
1196 case VGA_CRTC_INDEX_COLOR:
1197 return VgaCrtcIndex;
1198
1199 case VGA_CRTC_DATA_MONO:
1200 case VGA_CRTC_DATA_COLOR:
1201 return VgaCrtcRegisters[VgaCrtcIndex];
1202
1203 case VGA_GC_INDEX:
1204 return VgaGcIndex;
1205
1206 case VGA_GC_DATA:
1207 return VgaGcRegisters[VgaGcIndex];
1208
1209 default:
1210 DPRINT1("VgaReadPort: Unknown port 0x%X\n", Port);
1211 break;
1212 }
1213
1214 return 0;
1215 }
1216
1217 VOID WINAPI VgaWritePort(ULONG Port, BYTE Data)
1218 {
1219 DPRINT("VgaWritePort: Port 0x%X, Data 0x%02X\n", Port, Data);
1220
1221 switch (Port)
1222 {
1223 case VGA_MISC_WRITE:
1224 {
1225 VgaMiscRegister = Data;
1226
1227 if (VgaMiscRegister & 0x01)
1228 {
1229 /* Color emulation */
1230 DPRINT1("Color emulation\n");
1231
1232 /* Register the new I/O Ports */
1233 RegisterIoPort(0x3D4, VgaReadPort, VgaWritePort); // VGA_CRTC_INDEX_COLOR
1234 RegisterIoPort(0x3D5, VgaReadPort, VgaWritePort); // VGA_CRTC_DATA_COLOR
1235 RegisterIoPort(0x3DA, VgaReadPort, VgaWritePort); // VGA_INSTAT1_READ_COLOR, VGA_FEATURE_WRITE_COLOR
1236
1237 /* Unregister the old ones */
1238 UnregisterIoPort(0x3B4); // VGA_CRTC_INDEX_MONO
1239 UnregisterIoPort(0x3B5); // VGA_CRTC_DATA_MONO
1240 UnregisterIoPort(0x3BA); // VGA_INSTAT1_READ_MONO, VGA_FEATURE_WRITE_MONO
1241 }
1242 else
1243 {
1244 /* Monochrome emulation */
1245 DPRINT1("Monochrome emulation\n");
1246
1247 /* Register the new I/O Ports */
1248 RegisterIoPort(0x3B4, VgaReadPort, VgaWritePort); // VGA_CRTC_INDEX_MONO
1249 RegisterIoPort(0x3B5, VgaReadPort, VgaWritePort); // VGA_CRTC_DATA_MONO
1250 RegisterIoPort(0x3BA, VgaReadPort, VgaWritePort); // VGA_INSTAT1_READ_MONO, VGA_FEATURE_WRITE_MONO
1251
1252 /* Unregister the old ones */
1253 UnregisterIoPort(0x3D4); // VGA_CRTC_INDEX_COLOR
1254 UnregisterIoPort(0x3D5); // VGA_CRTC_DATA_COLOR
1255 UnregisterIoPort(0x3DA); // VGA_INSTAT1_READ_COLOR, VGA_FEATURE_WRITE_COLOR
1256 }
1257
1258 // if (VgaMiscRegister & 0x02) { /* Enable RAM access */ } else { /* Disable RAM access */ }
1259 break;
1260 }
1261
1262 case VGA_FEATURE_WRITE_MONO:
1263 case VGA_FEATURE_WRITE_COLOR:
1264 {
1265 VgaFeatureRegister = Data;
1266 break;
1267 }
1268
1269 case VGA_AC_INDEX:
1270 // case VGA_AC_WRITE:
1271 {
1272 if (!VgaAcLatch)
1273 {
1274 /* Change the index */
1275 if (Data < VGA_AC_MAX_REG) VgaAcIndex = Data;
1276 }
1277 else
1278 {
1279 /* Write the data */
1280 VgaWriteAc(Data);
1281 }
1282
1283 /* Toggle the latch */
1284 VgaAcLatch = !VgaAcLatch;
1285 break;
1286 }
1287
1288 case VGA_SEQ_INDEX:
1289 {
1290 /* Set the sequencer index register */
1291 if (Data < VGA_SEQ_MAX_REG) VgaSeqIndex = Data;
1292 break;
1293 }
1294
1295 case VGA_SEQ_DATA:
1296 {
1297 /* Call the sequencer function */
1298 VgaWriteSequencer(Data);
1299 break;
1300 }
1301
1302 case VGA_DAC_MASK:
1303 {
1304 VgaDacMask = Data;
1305 break;
1306 }
1307
1308 case VGA_DAC_READ_INDEX:
1309 {
1310 VgaDacReadWrite = FALSE;
1311 VgaDacIndex = Data * 3;
1312 break;
1313 }
1314
1315 case VGA_DAC_WRITE_INDEX:
1316 {
1317 VgaDacReadWrite = TRUE;
1318 VgaDacIndex = Data * 3;
1319 break;
1320 }
1321
1322 case VGA_DAC_DATA:
1323 {
1324 /* Ignore writes in read mode */
1325 if (VgaDacReadWrite) VgaWriteDac(Data & 0x3F);
1326 break;
1327 }
1328
1329 case VGA_CRTC_INDEX_MONO:
1330 case VGA_CRTC_INDEX_COLOR:
1331 {
1332 /* Set the CRTC index register */
1333 if (Data < VGA_CRTC_MAX_REG) VgaCrtcIndex = Data;
1334 break;
1335 }
1336
1337 case VGA_CRTC_DATA_MONO:
1338 case VGA_CRTC_DATA_COLOR:
1339 {
1340 /* Call the CRTC function */
1341 VgaWriteCrtc(Data);
1342 break;
1343 }
1344
1345 case VGA_GC_INDEX:
1346 {
1347 /* Set the GC index register */
1348 if (Data < VGA_GC_MAX_REG) VgaGcIndex = Data;
1349 break;
1350 }
1351
1352 case VGA_GC_DATA:
1353 {
1354 /* Call the GC function */
1355 VgaWriteGc(Data);
1356 break;
1357 }
1358
1359 default:
1360 DPRINT1("VgaWritePort: Unknown port 0x%X\n", Port);
1361 break;
1362 }
1363 }
1364
1365 VOID VgaClearMemory(VOID)
1366 {
1367 ZeroMemory(VgaMemory, sizeof(VgaMemory));
1368 }
1369
1370 VOID VgaResetPalette(VOID)
1371 {
1372 PALETTEENTRY Entries[VGA_MAX_COLORS];
1373
1374 /* Restore the default palette */
1375 VgaRestoreDefaultPalette(Entries, VGA_MAX_COLORS);
1376
1377 SetPaletteEntries(PaletteHandle, 0, VGA_MAX_COLORS, Entries);
1378 PaletteChanged = TRUE;
1379 }
1380
1381 BOOLEAN VgaInitialize(HANDLE TextHandle)
1382 {
1383 INT i, j;
1384 COORD Resolution;
1385 INT AddressSize;
1386 DWORD ScanlineSize;
1387 COORD Origin = { 0, 0 };
1388 SMALL_RECT ScreenRect;
1389 PCHAR_INFO CharBuffer;
1390 DWORD Address = 0;
1391 DWORD CurrentAddr;
1392
1393 /* Initialize the VGA palette and fail if it isn't successfully created */
1394 if (!VgaInitializePalette()) return FALSE;
1395
1396 /* Set the global handle */
1397 TextConsoleBuffer = TextHandle;
1398
1399 /* Register the I/O Ports */
1400 RegisterIoPort(0x3CC, VgaReadPort, NULL); // VGA_MISC_READ
1401 RegisterIoPort(0x3C2, VgaReadPort, VgaWritePort); // VGA_MISC_WRITE, VGA_INSTAT0_READ
1402 RegisterIoPort(0x3CA, VgaReadPort, NULL); // VGA_FEATURE_READ
1403 RegisterIoPort(0x3C0, VgaReadPort, VgaWritePort); // VGA_AC_INDEX, VGA_AC_WRITE
1404 RegisterIoPort(0x3C1, VgaReadPort, NULL); // VGA_AC_READ
1405 RegisterIoPort(0x3C4, VgaReadPort, VgaWritePort); // VGA_SEQ_INDEX
1406 RegisterIoPort(0x3C5, VgaReadPort, VgaWritePort); // VGA_SEQ_DATA
1407 RegisterIoPort(0x3C6, VgaReadPort, VgaWritePort); // VGA_DAC_MASK
1408 RegisterIoPort(0x3C7, VgaReadPort, VgaWritePort); // VGA_DAC_READ_INDEX
1409 RegisterIoPort(0x3C8, VgaReadPort, VgaWritePort); // VGA_DAC_WRITE_INDEX
1410 RegisterIoPort(0x3C9, VgaReadPort, VgaWritePort); // VGA_DAC_DATA
1411 RegisterIoPort(0x3CE, VgaReadPort, VgaWritePort); // VGA_GC_INDEX
1412 RegisterIoPort(0x3CF, VgaReadPort, VgaWritePort); // VGA_GC_DATA
1413
1414 /* Clear the VGA memory */
1415 VgaClearMemory();
1416
1417 /* Set the default video mode */
1418 BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE);
1419 VgaChangeMode();
1420
1421 /* Get the data */
1422 Resolution = VgaGetDisplayResolution();
1423 CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
1424 AddressSize = VgaGetAddressSize();
1425 ScreenRect.Left = ScreenRect.Top = 0;
1426 ScreenRect.Right = Resolution.X;
1427 ScreenRect.Bottom = Resolution.Y;
1428 ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
1429
1430 /* Read the data from the console into the framebuffer */
1431 ReadConsoleOutputA(TextConsoleBuffer,
1432 CharBuffer,
1433 Resolution,
1434 Origin,
1435 &ScreenRect);
1436
1437 /* Loop through the scanlines */
1438 for (i = 0; i < Resolution.Y; i++)
1439 {
1440 /* Loop through the characters */
1441 for (j = 0; j < Resolution.X; j++)
1442 {
1443 CurrentAddr = LOWORD((Address + j) * AddressSize);
1444
1445 /* Store the character in plane 0 */
1446 VgaMemory[CurrentAddr] = CharBuffer[i * Resolution.X + j].Char.AsciiChar;
1447
1448 /* Store the attribute in plane 1 */
1449 VgaMemory[CurrentAddr + VGA_BANK_SIZE] = (BYTE)CharBuffer[i * Resolution.X + j].Attributes;
1450 }
1451
1452 /* Move to the next scanline */
1453 Address += ScanlineSize;
1454 }
1455
1456 /* Return success */
1457 return TRUE;
1458 }
1459
1460 /* EOF */