1 /*
2 * PROJECT: ReactOS Boot Video Driver for VGA-compatible cards
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Main file
5 * COPYRIGHT: Copyright 2007 Alex Ionescu <alex.ionescu@reactos.org>
6 * Copyright 2019 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
7 */
8
9 #include "precomp.h"
10
11 /* PRIVATE FUNCTIONS *********************************************************/
12
13 static BOOLEAN
VgaInterpretCmdStream(_In_ PUSHORT CmdStream)14 VgaInterpretCmdStream(
15 _In_ PUSHORT CmdStream)
16 {
17 USHORT Cmd;
18 UCHAR Major, Minor;
19 USHORT Port;
20 USHORT Count;
21 UCHAR Index;
22 UCHAR Value;
23 USHORT ShortValue;
24
25 /* First make sure that we have a Command Stream */
26 if (!CmdStream) return TRUE;
27
28 /* Loop as long as we have commands */
29 while (*CmdStream != EOD)
30 {
31 /* Get the next command and its Major and Minor functions */
32 Cmd = *CmdStream++;
33 Major = Cmd & 0xF0;
34 Minor = Cmd & 0x0F;
35
36 /* Check which major function this is */
37 if (Major == INOUT)
38 {
39 /* Check the minor function */
40 if (Minor & IO /* CMD_STREAM_READ */)
41 {
42 /* Check the sub-type */
43 if (Minor & BW /* CMD_STREAM_USHORT */)
44 {
45 /* Get the port and read an USHORT from it */
46 Port = *CmdStream++;
47 ShortValue = __inpw(Port);
48 }
49 else // if (Minor & CMD_STREAM_WRITE)
50 {
51 /* Get the port and read an UCHAR from it */
52 Port = *CmdStream++;
53 Value = __inpb(Port);
54 }
55 }
56 else if (Minor & MULTI /* CMD_STREAM_WRITE_ARRAY */)
57 {
58 /* Check the sub-type */
59 if (Minor & BW /* CMD_STREAM_USHORT */)
60 {
61 /* Get the port and the count of elements */
62 Port = *CmdStream++;
63 Count = *CmdStream++;
64
65 /* Write the USHORT to the port; the buffer is what's in the command stream */
66 WRITE_PORT_BUFFER_USHORT((PUSHORT)(VgaRegisterBase + Port), CmdStream, Count);
67
68 /* Move past the buffer in the command stream */
69 CmdStream += Count;
70 }
71 else // if (Minor & CMD_STREAM_WRITE)
72 {
73 /* Get the port and the count of elements */
74 Port = *CmdStream++;
75 Count = *CmdStream++;
76
77 /* Loop the command array */
78 for (; Count; --Count, ++CmdStream)
79 {
80 /* Get the UCHAR and write it to the port */
81 Value = (UCHAR)*CmdStream;
82 __outpb(Port, Value);
83 }
84 }
85 }
86 else if (Minor & BW /* CMD_STREAM_USHORT */)
87 {
88 /* Get the port */
89 Port = *CmdStream++;
90
91 /* Get the USHORT and write it to the port */
92 ShortValue = *CmdStream++;
93 __outpw(Port, ShortValue);
94 }
95 else // if (Minor & CMD_STREAM_WRITE)
96 {
97 /* Get the port */
98 Port = *CmdStream++;
99
100 /* Get the UCHAR and write it to the port */
101 Value = (UCHAR)*CmdStream++;
102 __outpb(Port, Value);
103 }
104 }
105 else if (Major == METAOUT)
106 {
107 /* Check the minor function. Note these are not flags. */
108 switch (Minor)
109 {
110 case INDXOUT:
111 {
112 /* Get the port, the count of elements and the start index */
113 Port = *CmdStream++;
114 Count = *CmdStream++;
115 Index = (UCHAR)*CmdStream++;
116
117 /* Loop the command array */
118 for (; Count; --Count, ++Index, ++CmdStream)
119 {
120 /* Get the USHORT and write it to the port */
121 ShortValue = (USHORT)Index + ((*CmdStream) << 8);
122 __outpw(Port, ShortValue);
123 }
124 break;
125 }
126
127 case ATCOUT:
128 {
129 /* Get the port, the count of elements and the start index */
130 Port = *CmdStream++;
131 Count = *CmdStream++;
132 Index = (UCHAR)*CmdStream++;
133
134 /* Loop the command array */
135 for (; Count; --Count, ++Index, ++CmdStream)
136 {
137 /* Write the index */
138 __outpb(Port, Index);
139
140 /* Get the UCHAR and write it to the port */
141 Value = (UCHAR)*CmdStream;
142 __outpb(Port, Value);
143 }
144 break;
145 }
146
147 case MASKOUT:
148 {
149 /* Get the port */
150 Port = *CmdStream++;
151
152 /* Read the current value and add the stream data */
153 Value = __inpb(Port);
154 Value &= *CmdStream++;
155 Value ^= *CmdStream++;
156
157 /* Write the value */
158 __outpb(Port, Value);
159 break;
160 }
161
162 default:
163 /* Unknown command, fail */
164 return FALSE;
165 }
166 }
167 else if (Major != NCMD)
168 {
169 /* Unknown major function, fail */
170 return FALSE;
171 }
172 }
173
174 /* If we got here, return success */
175 return TRUE;
176 }
177
178 static BOOLEAN
VgaIsPresent(VOID)179 VgaIsPresent(VOID)
180 {
181 UCHAR OrgGCAddr, OrgReadMap, OrgBitMask;
182 UCHAR OrgSCAddr, OrgMemMode;
183 UCHAR i;
184
185 /* Remember the original state of the Graphics Controller Address register */
186 OrgGCAddr = __inpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT);
187
188 /*
189 * Write the Read Map register with a known state so we can verify
190 * that it isn't changed after we fool with the Bit Mask. This ensures
191 * that we're dealing with indexed registers, since both the Read Map and
192 * the Bit Mask are addressed at GRAPH_DATA_PORT.
193 */
194 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_READ_MAP);
195
196 /*
197 * If we can't read back the Graphics Address register setting we just
198 * performed, it's not readable and this isn't a VGA.
199 */
200 if ((__inpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT) & GRAPH_ADDR_MASK) != IND_READ_MAP)
201 return FALSE;
202
203 /*
204 * Set the Read Map register to a known state.
205 */
206 OrgReadMap = __inpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT);
207 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, READ_MAP_TEST_SETTING);
208
209 /* Read it back... it should be the same */
210 if (__inpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT) != READ_MAP_TEST_SETTING)
211 {
212 /*
213 * The Read Map setting we just performed can't be read back; not a
214 * VGA. Restore the default Read Map state and fail.
215 */
216 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, READ_MAP_DEFAULT);
217 return FALSE;
218 }
219
220 /* Remember the original setting of the Bit Mask register */
221 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_BIT_MASK);
222
223 /* Read it back... it should be the same */
224 if ((__inpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT) & GRAPH_ADDR_MASK) != IND_BIT_MASK)
225 {
226 /*
227 * The Graphics Address register setting we just made can't be read
228 * back; not a VGA. Restore the default Read Map state and fail.
229 */
230 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_READ_MAP);
231 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, READ_MAP_DEFAULT);
232 return FALSE;
233 }
234
235 /* Read the VGA Data Register */
236 OrgBitMask = __inpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT);
237
238 /*
239 * Set up the initial test mask we'll write to and read from the Bit Mask,
240 * and loop on the bitmasks.
241 */
242 for (i = 0xBB; i; i >>= 1)
243 {
244 /* Write the test mask to the Bit Mask */
245 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, i);
246
247 /* Read it back... it should be the same */
248 if (__inpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT) != i)
249 {
250 /*
251 * The Bit Mask is not properly writable and readable; not a VGA.
252 * Restore the Bit Mask and Read Map to their default states and fail.
253 */
254 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, BIT_MASK_DEFAULT);
255 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_READ_MAP);
256 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, READ_MAP_DEFAULT);
257 return FALSE;
258 }
259 }
260
261 /*
262 * There's something readable at GRAPH_DATA_PORT; now switch back and
263 * make sure that the Read Map register hasn't changed, to verify that
264 * we're dealing with indexed registers.
265 */
266 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_READ_MAP);
267
268 /* Read it back */
269 if (__inpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT) != READ_MAP_TEST_SETTING)
270 {
271 /*
272 * The Read Map is not properly writable and readable; not a VGA.
273 * Restore the Bit Mask and Read Map to their default states, in case
274 * this is an EGA, so subsequent writes to the screen aren't garbled.
275 * Then fail.
276 */
277 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, READ_MAP_DEFAULT);
278 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_BIT_MASK);
279 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, BIT_MASK_DEFAULT);
280 return FALSE;
281 }
282
283 /*
284 * We've pretty surely verified the existence of the Bit Mask register.
285 * Put the Graphics Controller back to the original state.
286 */
287 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, OrgReadMap);
288 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_BIT_MASK);
289 __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, OrgBitMask);
290 __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, OrgGCAddr);
291
292 /*
293 * Now, check for the existence of the Chain4 bit.
294 */
295
296 /*
297 * Remember the original states of the Sequencer Address and Memory Mode
298 * registers.
299 */
300 OrgSCAddr = __inpb(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT);
301 __outpb(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, IND_MEMORY_MODE);
302
303 /* Read it back... it should be the same */
304 if ((__inpb(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT) & SEQ_ADDR_MASK) != IND_MEMORY_MODE)
305 {
306 /*
307 * Couldn't read back the Sequencer Address register setting
308 * we just performed, fail.
309 */
310 return FALSE;
311 }
312
313 /* Read sequencer Data */
314 OrgMemMode = __inpb(VGA_BASE_IO_PORT + SEQ_DATA_PORT);
315
316 /*
317 * Toggle the Chain4 bit and read back the result. This must be done during
318 * sync reset, since we're changing the chaining state.
319 */
320
321 /* Begin sync reset */
322 __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, (IND_SYNC_RESET + (START_SYNC_RESET_VALUE << 8)));
323
324 /* Toggle the Chain4 bit */
325 __outpb(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, IND_MEMORY_MODE);
326 __outpb(VGA_BASE_IO_PORT + SEQ_DATA_PORT, OrgMemMode ^ CHAIN4_MASK);
327
328 /* Read it back... it should be the same */
329 if (__inpb(VGA_BASE_IO_PORT + SEQ_DATA_PORT) != (OrgMemMode ^ CHAIN4_MASK))
330 {
331 /*
332 * Chain4 bit is not there, not a VGA.
333 * Set text mode default for Memory Mode register.
334 */
335 __outpb(VGA_BASE_IO_PORT + SEQ_DATA_PORT, MEMORY_MODE_TEXT_DEFAULT);
336
337 /* End sync reset */
338 __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, (IND_SYNC_RESET + (END_SYNC_RESET_VALUE << 8)));
339
340 /* Fail */
341 return FALSE;
342 }
343
344 /*
345 * It's a VGA.
346 */
347
348 /* Restore the original Memory Mode setting */
349 __outpb(VGA_BASE_IO_PORT + SEQ_DATA_PORT, OrgMemMode);
350
351 /* End sync reset */
352 __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, (IND_SYNC_RESET + (END_SYNC_RESET_VALUE << 8)));
353
354 /* Restore the original Sequencer Address setting */
355 __outpb(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, OrgSCAddr);
356
357 /* VGA is present! */
358 return TRUE;
359 }
360
361 /* PUBLIC FUNCTIONS **********************************************************/
362
363 BOOLEAN
364 NTAPI
VidInitialize(_In_ BOOLEAN SetMode)365 VidInitialize(
366 _In_ BOOLEAN SetMode)
367 {
368 ULONG_PTR Context = 0;
369 PHYSICAL_ADDRESS TranslatedAddress;
370 PHYSICAL_ADDRESS NullAddress = {{0, 0}}, VgaAddress;
371 ULONG AddressSpace;
372 BOOLEAN Result;
373 ULONG_PTR Base;
374
375 /* Make sure that we have a bus translation function */
376 if (!HalFindBusAddressTranslation) return FALSE;
377
378 /* Loop trying to find possible VGA base addresses */
379 while (TRUE)
380 {
381 /* Get the VGA Register address */
382 AddressSpace = 1;
383 Result = HalFindBusAddressTranslation(NullAddress,
384 &AddressSpace,
385 &TranslatedAddress,
386 &Context,
387 TRUE);
388 if (!Result) return FALSE;
389
390 /* See if this is I/O Space, which we need to map */
391 if (!AddressSpace)
392 {
393 /* Map it */
394 Base = (ULONG_PTR)MmMapIoSpace(TranslatedAddress, 0x400, MmNonCached);
395 }
396 else
397 {
398 /* The base is the translated address, no need to map I/O space */
399 Base = TranslatedAddress.LowPart;
400 }
401
402 /* Try to see if this is VGA */
403 VgaRegisterBase = Base;
404 if (VgaIsPresent())
405 {
406 /* Translate the VGA Memory Address */
407 VgaAddress.LowPart = MEM_VGA;
408 VgaAddress.HighPart = 0;
409 AddressSpace = 0;
410 Result = HalFindBusAddressTranslation(VgaAddress,
411 &AddressSpace,
412 &TranslatedAddress,
413 &Context,
414 FALSE);
415 if (Result) break;
416 }
417 else
418 {
419 /* It's not, so unmap the I/O space if we mapped it */
420 if (!AddressSpace) MmUnmapIoSpace((PVOID)VgaRegisterBase, 0x400);
421 }
422
423 /* Continue trying to see if there's any other address */
424 }
425
426 /* Success! See if this is I/O Space, which we need to map */
427 if (!AddressSpace)
428 {
429 /* Map it */
430 Base = (ULONG_PTR)MmMapIoSpace(TranslatedAddress,
431 MEM_VGA_SIZE,
432 MmNonCached);
433 }
434 else
435 {
436 /* The base is the translated address, no need to map I/O space */
437 Base = TranslatedAddress.LowPart;
438 }
439
440 /* Set the VGA Memory Base */
441 VgaBase = Base;
442
443 /* Now check if we have to set the mode */
444 if (SetMode)
445 {
446 /* Clear the current position */
447 VidpCurrentX = 0;
448 VidpCurrentY = 0;
449
450 /* Reset the display and initialize it */
451 if (HalResetDisplay())
452 {
453 /* The HAL handled the display, re-initialize only the AC registers */
454 VgaInterpretCmdStream(AT_Initialization);
455 }
456 else
457 {
458 /* The HAL didn't handle the display, fully re-initialize the VGA */
459 VgaInterpretCmdStream(VGA_640x480);
460 }
461 }
462
463 /* VGA is ready */
464 return TRUE;
465 }
466
467 VOID
468 NTAPI
VidResetDisplay(_In_ BOOLEAN HalReset)469 VidResetDisplay(
470 _In_ BOOLEAN HalReset)
471 {
472 /* Clear the current position */
473 VidpCurrentX = 0;
474 VidpCurrentY = 0;
475
476 /* Clear the screen with HAL if we were asked to */
477 if (HalReset)
478 {
479 if (!HalResetDisplay())
480 {
481 /* The HAL didn't handle the display, fully re-initialize the VGA */
482 VgaInterpretCmdStream(VGA_640x480);
483 }
484 }
485
486 /* Always re-initialize the AC registers */
487 VgaInterpretCmdStream(AT_Initialization);
488
489 /* Re-initialize the palette and fill the screen black */
490 InitializePalette();
491 VidSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLACK);
492 }
493