2003-08-11 Casper S. Hornstrup <chorns@users.sourceforge.net>
[reactos.git] / reactos / hal / halx86 / display.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: display.c,v 1.6 2003/08/11 18:50:12 chorns Exp $
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * FILE: ntoskrnl/hal/x86/display.c
24 * PURPOSE: Blue screen display
25 * PROGRAMMER: Eric Kohl (ekohl@rz-online.de)
26 * UPDATE HISTORY:
27 * Created 08/10/99
28 */
29
30 /* DISPLAY OWNERSHIP
31 *
32 * So, who owns the physical display and is allowed to write to it?
33 *
34 * In MS NT, upon boot HAL owns the display. Somewhere in the boot
35 * sequence (haven't figured out exactly where or by who), some
36 * component calls HalAcquireDisplayOwnership. From that moment on,
37 * the display is owned by that component and is switched to graphics
38 * mode. The display is not supposed to return to text mode, except
39 * in case of a bug check. The bug check will call HalDisplayString
40 * to output a string to the text screen. HAL will notice that it
41 * currently doesn't own the display and will re-take ownership, by
42 * calling the callback function passed to HalAcquireDisplayOwnership.
43 * After the bugcheck, execution is halted. So, under NT, the only
44 * possible sequence of display modes is text mode -> graphics mode ->
45 * text mode (the latter hopefully happening very infrequently).
46 *
47 * Things are a little bit different in the current state of ReactOS.
48 * We want to have a functional interactive text mode. We should be
49 * able to switch from text mode to graphics mode when a GUI app is
50 * started and switch back to text mode when it's finished. Then, when
51 * another GUI app is started, another switch to and from graphics mode
52 * is possible. Also, when the system bugchecks in graphics mode we want
53 * to switch back to text mode to show the registers and stack trace.
54 * Last but not least, HalDisplayString is used a lot more in ReactOS,
55 * e.g. to print debug messages when the /DEBUGPORT=SCREEN boot option
56 * is present.
57 * 3 Components are involved in Reactos: HAL, BLUE.SYS and VIDEOPRT.SYS.
58 * As in NT, on boot HAL owns the display. When entering the text mode
59 * command interpreter, BLUE.SYS kicks in. It will write directly to the
60 * screen, more or less behind HALs back.
61 * When a GUI app is started, WIN32K.SYS will open the DISPLAY device.
62 * This open call will end up in VIDEOPRT.SYS. That component will then
63 * take ownership of the display by calling HalAcquireDisplayOwnership.
64 * When the GUI app terminates (WIN32K.SYS will close the DISPLAY device),
65 * we want to give ownership of the display back to HAL. Using the
66 * standard exported HAL functions, that's a bit of a problem, because
67 * there is no function defined to do that. In NT, this is handled by
68 * HalDisplayString, but that solution isn't satisfactory in ReactOS,
69 * because HalDisplayString is (in some cases) also used to output debug
70 * messages. If we do it the NT way, the first debug message output while
71 * in graphics mode would switch the display back to text mode.
72 * So, instead, if HalDisplayString detects that HAL doesn't have ownership
73 * of the display, it doesn't do anything.
74 * To return ownership to HAL, a new function is exported,
75 * HalReleaseDisplayOwnership. This function is called by the DISPLAY
76 * device Close routine in VIDEOPRT.SYS. It is also called at the beginning
77 * of a bug check, so HalDisplayString is activated again.
78 * Now, while the display is in graphics mode (not owned by HAL), BLUE.SYS
79 * should also refrain from writing to the screen buffer. The text mode
80 * screen buffer might overlap the graphics mode screen buffer, so changing
81 * something in the text mode buffer might mess up the graphics screen. To
82 * allow BLUE.SYS to detect if HAL owns the display, another new function is
83 * exported, HalQueryDisplayOwnership. BLUE.SYS will call this function to
84 * check if it's allowed to touch the text mode buffer.
85 *
86 * In an ideal world, when HAL takes ownership of the display, it should set
87 * up the CRT using real-mode (actually V86 mode, but who cares) INT 0x10
88 * calls. Unfortunately, this will require HAL to setup a real-mode interrupt
89 * table etc. So, we chickened out of that by having the loader set up the
90 * display before switching to protected mode. If HAL is given back ownership
91 * after a GUI app terminates, the INT 0x10 calls are made by VIDEOPRT.SYS,
92 * since there is already support for them via the VideoPortInt10 routine.
93 */
94
95 #include <ddk/ntddk.h>
96 #include <mps.h>
97
98 #define SCREEN_SYNCHRONIZATION
99
100
101 #define CRTC_COMMAND 0x3d4
102 #define CRTC_DATA 0x3d5
103
104 #define CRTC_COLUMNS 0x01
105 #define CRTC_OVERFLOW 0x07
106 #define CRTC_ROWS 0x12
107 #define CRTC_SCANLINES 0x09
108
109 #define CRTC_CURHI 0x0e
110 #define CRTC_CURLO 0x0f
111
112
113 #define CHAR_ATTRIBUTE_BLACK 0x00 /* black on black */
114 #define CHAR_ATTRIBUTE 0x17 /* grey on blue */
115
116
117 /* VARIABLES ****************************************************************/
118
119 static ULONG CursorX = 0; /* Cursor Position */
120 static ULONG CursorY = 0;
121 static ULONG SizeX = 80; /* Display size */
122 static ULONG SizeY = 25;
123
124 static BOOLEAN DisplayInitialized = FALSE;
125 static BOOLEAN HalOwnsDisplay = TRUE;
126
127 static WORD *VideoBuffer = NULL;
128
129 static PHAL_RESET_DISPLAY_PARAMETERS HalResetDisplayParameters = NULL;
130
131
132 /* STATIC FUNCTIONS *********************************************************/
133
134 VOID
135 HalClearDisplay (UCHAR CharAttribute)
136 {
137 WORD *ptr = (WORD*)VideoBuffer;
138 ULONG i;
139
140 for (i = 0; i < SizeX * SizeY; i++, ptr++)
141 *ptr = ((CharAttribute << 8) + ' ');
142
143 CursorX = 0;
144 CursorY = 0;
145 }
146
147
148 VOID
149 HalScrollDisplay (VOID)
150 {
151 WORD *ptr;
152 int i;
153
154 ptr = VideoBuffer + SizeX;
155 RtlMoveMemory(VideoBuffer,
156 ptr,
157 SizeX * (SizeY - 1) * 2);
158
159 ptr = VideoBuffer + (SizeX * (SizeY - 1));
160 for (i = 0; i < SizeX; i++, ptr++)
161 {
162 *ptr = (CHAR_ATTRIBUTE << 8) + ' ';
163 }
164 }
165
166
167 static VOID
168 HalPutCharacter (CHAR Character)
169 {
170 WORD *ptr;
171
172 ptr = VideoBuffer + ((CursorY * SizeX) + CursorX);
173 *ptr = (CHAR_ATTRIBUTE << 8) + Character;
174 }
175
176
177 /* PRIVATE FUNCTIONS ********************************************************/
178
179 VOID
180 HalInitializeDisplay (PLOADER_PARAMETER_BLOCK LoaderBlock)
181 /*
182 * FUNCTION: Initalize the display
183 * ARGUMENTS:
184 * InitParameters = Parameters setup by the boot loader
185 */
186 {
187 if (DisplayInitialized == FALSE)
188 {
189 ULONG ScanLines;
190 ULONG Data;
191
192 VideoBuffer = (WORD *)(0xd0000000 + 0xb8000);
193 // VideoBuffer = HalMapPhysicalMemory (0xb8000, 2);
194
195 /* Set cursor position */
196 // CursorX = LoaderBlock->cursorx;
197 // CursorY = LoaderBlock->cursory;
198 CursorX = 0;
199 CursorY = 0;
200
201 /* read screen size from the crtc */
202 /* FIXME: screen size should be read from the boot parameters */
203 WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_COLUMNS);
204 SizeX = READ_PORT_UCHAR((PUCHAR)CRTC_DATA) + 1;
205 WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_ROWS);
206 SizeY = READ_PORT_UCHAR((PUCHAR)CRTC_DATA);
207 WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_OVERFLOW);
208 Data = READ_PORT_UCHAR((PUCHAR)CRTC_DATA);
209 SizeY |= (((Data & 0x02) << 7) | ((Data & 0x40) << 3));
210 SizeY++;
211 WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_SCANLINES);
212 ScanLines = (READ_PORT_UCHAR((PUCHAR)CRTC_DATA) & 0x1F) + 1;
213 SizeY = SizeY / ScanLines;
214
215 #ifdef BOCHS_30ROWS
216 SizeY=30;
217 #endif
218 HalClearDisplay(CHAR_ATTRIBUTE_BLACK);
219
220 DisplayInitialized = TRUE;
221 }
222 }
223
224
225 /* PUBLIC FUNCTIONS *********************************************************/
226
227 VOID STDCALL
228 HalReleaseDisplayOwnership()
229 /*
230 * FUNCTION: Release ownership of display back to HAL
231 */
232 {
233 if (HalResetDisplayParameters == NULL)
234 return;
235
236 if (HalOwnsDisplay == TRUE)
237 return;
238
239 if (HalResetDisplayParameters(SizeX, SizeY) == TRUE)
240 {
241 HalOwnsDisplay = TRUE;
242 HalClearDisplay(CHAR_ATTRIBUTE);
243 }
244 }
245
246
247 VOID STDCALL
248 HalAcquireDisplayOwnership(IN PHAL_RESET_DISPLAY_PARAMETERS ResetDisplayParameters)
249 /*
250 * FUNCTION:
251 * ARGUMENTS:
252 * ResetDisplayParameters = Pointer to a driver specific
253 * reset routine.
254 */
255 {
256 HalOwnsDisplay = FALSE;
257 HalResetDisplayParameters = ResetDisplayParameters;
258 }
259
260 VOID STDCALL
261 HalDisplayString(IN PCH String)
262 /*
263 * FUNCTION: Switches the screen to HAL console mode (BSOD) if not there
264 * already and displays a string
265 * ARGUMENT:
266 * string = ASCII string to display
267 * NOTE: Use with care because there is no support for returning from BSOD
268 * mode
269 */
270 {
271 PCH pch;
272 #ifdef SCREEN_SYNCHRONIZATION
273 int offset;
274 #endif
275 static KSPIN_LOCK Lock;
276 ULONG Flags;
277
278 /* See comment at top of file */
279 if (! HalOwnsDisplay)
280 {
281 return;
282 }
283
284 pch = String;
285
286 pushfl(Flags);
287 __asm__ ("cli\n\t");
288 KeAcquireSpinLockAtDpcLevel(&Lock);
289
290 #if 0
291 if (HalOwnsDisplay == FALSE)
292 {
293 HalReleaseDisplayOwnership();
294 }
295 #endif
296
297 #ifdef SCREEN_SYNCHRONIZATION
298 WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_CURHI);
299 offset = READ_PORT_UCHAR((PUCHAR)CRTC_DATA)<<8;
300 WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_CURLO);
301 offset += READ_PORT_UCHAR((PUCHAR)CRTC_DATA);
302
303 CursorY = offset / SizeX;
304 CursorX = offset % SizeX;
305 #endif
306
307 while (*pch != 0)
308 {
309 if (*pch == '\n')
310 {
311 CursorY++;
312 CursorX = 0;
313 }
314 else if (*pch != '\r')
315 {
316 HalPutCharacter (*pch);
317 CursorX++;
318
319 if (CursorX >= SizeX)
320 {
321 CursorY++;
322 CursorX = 0;
323 }
324 }
325
326 if (CursorY >= SizeY)
327 {
328 HalScrollDisplay ();
329 CursorY = SizeY - 1;
330 }
331
332 pch++;
333 }
334
335 #ifdef SCREEN_SYNCHRONIZATION
336 offset = (CursorY * SizeX) + CursorX;
337
338 WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_CURLO);
339 WRITE_PORT_UCHAR((PUCHAR)CRTC_DATA, offset & 0xff);
340 WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_CURHI);
341 WRITE_PORT_UCHAR((PUCHAR)CRTC_DATA, (offset >> 8) & 0xff);
342 #endif
343 KeReleaseSpinLockFromDpcLevel(&Lock);
344 popfl(Flags);
345 }
346
347 VOID STDCALL
348 HalQueryDisplayParameters(OUT PULONG DispSizeX,
349 OUT PULONG DispSizeY,
350 OUT PULONG CursorPosX,
351 OUT PULONG CursorPosY)
352 {
353 if (DispSizeX)
354 *DispSizeX = SizeX;
355 if (DispSizeY)
356 *DispSizeY = SizeY;
357 if (CursorPosX)
358 *CursorPosX = CursorX;
359 if (CursorPosY)
360 *CursorPosY = CursorY;
361 }
362
363
364 VOID STDCALL
365 HalSetDisplayParameters(IN ULONG CursorPosX,
366 IN ULONG CursorPosY)
367 {
368 CursorX = (CursorPosX < SizeX) ? CursorPosX : SizeX - 1;
369 CursorY = (CursorPosY < SizeY) ? CursorPosY : SizeY - 1;
370 }
371
372 BOOLEAN STDCALL
373 HalQueryDisplayOwnership()
374 {
375 return ! HalOwnsDisplay;
376 }
377
378 /* EOF */