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