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