2 * VGA hardware emulation
4 * Copyright 1998 Ove Kåven (with some help from Marcus Meissner)
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #include "wine/debug.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(ddraw
);
34 static IDirectDraw
*lpddraw
= NULL
;
35 static IDirectDrawSurface
*lpddsurf
;
36 static IDirectDrawPalette
*lpddpal
;
37 static DDSURFACEDESC sdesc
;
38 static LONG vga_refresh
;
39 static HANDLE poll_timer
;
42 static int vga_height
;
44 static BYTE vga_text_attr
;
46 static CRITICAL_SECTION vga_lock
= CRITICAL_SECTION_INIT("VGA");
48 typedef HRESULT (WINAPI
*DirectDrawCreateProc
)(LPGUID
,LPDIRECTDRAW
*,LPUNKNOWN
);
49 static DirectDrawCreateProc pDirectDrawCreate
;
51 static void CALLBACK
VGA_Poll( LPVOID arg
, DWORD low
, DWORD high
);
54 * For simplicity, I'm creating a second palette.
55 * 16 color accesses will use these pointers and insert
56 * entries from the 64-color palette into the default
57 * palette. --Robert 'Admiral' Coeyman
60 static char vga_16_palette
[17]={
66 0x05, /* 5 - Magenta */
68 0x07, /* 7 - Light gray */
69 0x38, /* 8 - Dark gray */
70 0x39, /* 9 - Light blue */
71 0x3a, /* A - Light green */
72 0x3b, /* B - Light cyan */
73 0x3c, /* C - Light red */
74 0x3d, /* D - Light magenta */
75 0x3e, /* E - Yellow */
77 0x00 /* Border Color */
80 static PALETTEENTRY vga_def_palette
[256]={
82 {0x00, 0x00, 0x00}, /* 0 - Black */
83 {0x00, 0x00, 0x80}, /* 1 - Blue */
84 {0x00, 0x80, 0x00}, /* 2 - Green */
85 {0x00, 0x80, 0x80}, /* 3 - Cyan */
86 {0x80, 0x00, 0x00}, /* 4 - Red */
87 {0x80, 0x00, 0x80}, /* 5 - Magenta */
88 {0x80, 0x80, 0x00}, /* 6 - Brown */
89 {0xC0, 0xC0, 0xC0}, /* 7 - Light gray */
90 {0x80, 0x80, 0x80}, /* 8 - Dark gray */
91 {0x00, 0x00, 0xFF}, /* 9 - Light blue */
92 {0x00, 0xFF, 0x00}, /* A - Light green */
93 {0x00, 0xFF, 0xFF}, /* B - Light cyan */
94 {0xFF, 0x00, 0x00}, /* C - Light red */
95 {0xFF, 0x00, 0xFF}, /* D - Light magenta */
96 {0xFF, 0xFF, 0x00}, /* E - Yellow */
97 {0xFF, 0xFF, 0xFF}, /* F - White */
98 {0,0,0} /* FIXME: a series of continuous rainbow hues should follow */
102 * This palette is the dos default, converted from 18 bit color to 24.
103 * It contains only 64 entries of colors--all others are zeros.
104 * --Robert 'Admiral' Coeyman
106 static PALETTEENTRY vga_def64_palette
[256]={
108 {0x00, 0x00, 0x00}, /* 0x00 Black */
109 {0x00, 0x00, 0xaa}, /* 0x01 Blue */
110 {0x00, 0xaa, 0x00}, /* 0x02 Green */
111 {0x00, 0xaa, 0xaa}, /* 0x03 Cyan */
112 {0xaa, 0x00, 0x00}, /* 0x04 Red */
113 {0xaa, 0x00, 0xaa}, /* 0x05 Magenta */
114 {0xaa, 0xaa, 0x00}, /* 0x06 */
115 {0xaa, 0xaa, 0xaa}, /* 0x07 Light Gray */
116 {0x00, 0x00, 0x55}, /* 0x08 */
117 {0x00, 0x00, 0xff}, /* 0x09 */
118 {0x00, 0xaa, 0x55}, /* 0x0a */
119 {0x00, 0xaa, 0xff}, /* 0x0b */
120 {0xaa, 0x00, 0x55}, /* 0x0c */
121 {0xaa, 0x00, 0xff}, /* 0x0d */
122 {0xaa, 0xaa, 0x55}, /* 0x0e */
123 {0xaa, 0xaa, 0xff}, /* 0x0f */
124 {0x00, 0x55, 0x00}, /* 0x10 */
125 {0x00, 0x55, 0xaa}, /* 0x11 */
126 {0x00, 0xff, 0x00}, /* 0x12 */
127 {0x00, 0xff, 0xaa}, /* 0x13 */
128 {0xaa, 0x55, 0x00}, /* 0x14 Brown */
129 {0xaa, 0x55, 0xaa}, /* 0x15 */
130 {0xaa, 0xff, 0x00}, /* 0x16 */
131 {0xaa, 0xff, 0xaa}, /* 0x17 */
132 {0x00, 0x55, 0x55}, /* 0x18 */
133 {0x00, 0x55, 0xff}, /* 0x19 */
134 {0x00, 0xff, 0x55}, /* 0x1a */
135 {0x00, 0xff, 0xff}, /* 0x1b */
136 {0xaa, 0x55, 0x55}, /* 0x1c */
137 {0xaa, 0x55, 0xff}, /* 0x1d */
138 {0xaa, 0xff, 0x55}, /* 0x1e */
139 {0xaa, 0xff, 0xff}, /* 0x1f */
140 {0x55, 0x00, 0x00}, /* 0x20 */
141 {0x55, 0x00, 0xaa}, /* 0x21 */
142 {0x55, 0xaa, 0x00}, /* 0x22 */
143 {0x55, 0xaa, 0xaa}, /* 0x23 */
144 {0xff, 0x00, 0x00}, /* 0x24 */
145 {0xff, 0x00, 0xaa}, /* 0x25 */
146 {0xff, 0xaa, 0x00}, /* 0x26 */
147 {0xff, 0xaa, 0xaa}, /* 0x27 */
148 {0x55, 0x00, 0x55}, /* 0x28 */
149 {0x55, 0x00, 0xff}, /* 0x29 */
150 {0x55, 0xaa, 0x55}, /* 0x2a */
151 {0x55, 0xaa, 0xff}, /* 0x2b */
152 {0xff, 0x00, 0x55}, /* 0x2c */
153 {0xff, 0x00, 0xff}, /* 0x2d */
154 {0xff, 0xaa, 0x55}, /* 0x2e */
155 {0xff, 0xaa, 0xff}, /* 0x2f */
156 {0x55, 0x55, 0x00}, /* 0x30 */
157 {0x55, 0x55, 0xaa}, /* 0x31 */
158 {0x55, 0xff, 0x00}, /* 0x32 */
159 {0x55, 0xff, 0xaa}, /* 0x33 */
160 {0xff, 0x55, 0x00}, /* 0x34 */
161 {0xff, 0x55, 0xaa}, /* 0x35 */
162 {0xff, 0xff, 0x00}, /* 0x36 */
163 {0xff, 0xff, 0xaa}, /* 0x37 */
164 {0x55, 0x55, 0x55}, /* 0x38 Dark Gray */
165 {0x55, 0x55, 0xff}, /* 0x39 Light Blue */
166 {0x55, 0xff, 0x55}, /* 0x3a Light Green */
167 {0x55, 0xff, 0xff}, /* 0x3b Light Cyan */
168 {0xff, 0x55, 0x55}, /* 0x3c Light Red */
169 {0xff, 0x55, 0xff}, /* 0x3d Light Magenta */
170 {0xff, 0xff, 0x55}, /* 0x3e Yellow */
171 {0xff, 0xff, 0xff}, /* 0x3f White */
172 {0,0,0} /* The next 192 entries are all zeros */
175 static HANDLE VGA_timer
;
176 static HANDLE VGA_timer_thread
;
178 /* set the timer rate; called in the polling thread context */
179 static void CALLBACK
set_timer_rate( ULONG_PTR arg
)
183 when
.s
.LowPart
= when
.s
.HighPart
= 0;
184 SetWaitableTimer( VGA_timer
, &when
, arg
, VGA_Poll
, 0, FALSE
);
187 static DWORD CALLBACK
VGA_TimerThread( void *dummy
)
189 for (;;) WaitForMultipleObjectsEx( 0, NULL
, FALSE
, INFINITE
, TRUE
);
192 static void VGA_DeinstallTimer(void)
194 if (VGA_timer_thread
)
196 CancelWaitableTimer( VGA_timer
);
197 CloseHandle( VGA_timer
);
198 TerminateThread( VGA_timer_thread
, 0 );
199 CloseHandle( VGA_timer_thread
);
200 VGA_timer_thread
= 0;
204 static void VGA_InstallTimer(unsigned Rate
)
206 if (!VGA_timer_thread
)
208 VGA_timer
= CreateWaitableTimerA( NULL
, FALSE
, NULL
);
209 VGA_timer_thread
= CreateThread( NULL
, 0, VGA_TimerThread
, NULL
, 0, NULL
);
211 QueueUserAPC( set_timer_rate
, VGA_timer_thread
, (ULONG_PTR
)Rate
);
214 HANDLE
VGA_AlphaConsole(void)
216 /* this assumes that no Win32 redirection has taken place, but then again,
217 * only 16-bit apps are likely to use this part of Wine... */
218 return GetStdHandle(STD_OUTPUT_HANDLE
);
221 char*VGA_AlphaBuffer(void)
223 return DOSMEM_MapDosToLinear(0xb8000);
226 /*** GRAPHICS MODE ***/
229 unsigned Xres
, Yres
, Depth
;
233 static void WINAPI
VGA_DoSetMode(ULONG_PTR arg
)
237 ModeSet
*par
= (ModeSet
*)arg
;
240 if (lpddraw
) VGA_Exit();
242 if (!pDirectDrawCreate
)
244 HMODULE hmod
= LoadLibraryA( "ddraw.dll" );
245 if (hmod
) pDirectDrawCreate
= (DirectDrawCreateProc
)GetProcAddress( hmod
, "DirectDrawCreate" );
246 if (!pDirectDrawCreate
) {
247 ERR("Can't lookup DirectDrawCreate from ddraw.dll.\n");
251 res
= pDirectDrawCreate(NULL
,&lpddraw
,NULL
);
253 ERR("DirectDraw is not available (res = %lx)\n",res
);
256 hwnd
= CreateWindowExA(0,"STATIC","WINEDOS VGA",WS_POPUP
|WS_BORDER
|WS_CAPTION
|WS_SYSMENU
,0,0,par
->Xres
,par
->Yres
,0,0,0,NULL
);
258 ERR("Failed to create user window.\n");
260 if ((res
=IDirectDraw_SetCooperativeLevel(lpddraw
,hwnd
,DDSCL_FULLSCREEN
|DDSCL_EXCLUSIVE
))) {
261 ERR("Could not set cooperative level to exclusive (%lx)\n",res
);
264 if ((res
=IDirectDraw_SetDisplayMode(lpddraw
,par
->Xres
,par
->Yres
,par
->Depth
))) {
265 ERR("DirectDraw does not support requested display mode (%dx%dx%d), res = %lx!\n",par
->Xres
,par
->Yres
,par
->Depth
,res
);
266 IDirectDraw_Release(lpddraw
);
271 res
=IDirectDraw_CreatePalette(lpddraw
,DDPCAPS_8BIT
,NULL
,&lpddpal
,NULL
);
273 ERR("Could not create palette (res = %lx)\n",res
);
274 IDirectDraw_Release(lpddraw
);
278 if ((res
=IDirectDrawPalette_SetEntries(lpddpal
,0,0,256,vga_def_palette
))) {
279 ERR("Could not set default palette entries (res = %lx)\n", res
);
282 memset(&sdesc
,0,sizeof(sdesc
));
283 sdesc
.dwSize
=sizeof(sdesc
);
284 sdesc
.dwFlags
= DDSD_CAPS
;
285 sdesc
.ddsCaps
.dwCaps
= DDSCAPS_PRIMARYSURFACE
;
286 if (IDirectDraw_CreateSurface(lpddraw
,&sdesc
,&lpddsurf
,NULL
)||(!lpddsurf
)) {
287 ERR("DirectDraw surface is not available\n");
288 IDirectDraw_Release(lpddraw
);
292 IDirectDrawSurface_SetPalette(lpddsurf
,lpddpal
);
294 /* poll every 20ms (50fps should provide adequate responsiveness) */
295 VGA_InstallTimer(20);
301 int VGA_SetMode(unsigned Xres
,unsigned Yres
,unsigned Depth
)
309 if(Xres
>= 640 || Yres
>= 480) {
317 par
.Depth
= (Depth
< 8) ? 8 : Depth
;
319 MZ_RunInThread(VGA_DoSetMode
, (ULONG_PTR
)&par
);
323 int VGA_GetMode(unsigned*Height
,unsigned*Width
,unsigned*Depth
)
325 if (!lpddraw
) return 1;
326 if (!lpddsurf
) return 1;
327 if (Height
) *Height
=sdesc
.dwHeight
;
328 if (Width
) *Width
=sdesc
.dwWidth
;
329 if (Depth
) *Depth
=sdesc
.ddpfPixelFormat
.u1
.dwRGBBitCount
;
333 static void WINAPI
VGA_DoExit(ULONG_PTR arg
)
335 VGA_DeinstallTimer();
336 IDirectDrawSurface_SetPalette(lpddsurf
,NULL
);
337 IDirectDrawSurface_Release(lpddsurf
);
339 IDirectDrawPalette_Release(lpddpal
);
341 IDirectDraw_Release(lpddraw
);
347 if (lpddraw
) MZ_RunInThread(VGA_DoExit
, 0);
350 void VGA_SetPalette(PALETTEENTRY
*pal
,int start
,int len
)
352 if (!lpddraw
) return;
353 IDirectDrawPalette_SetEntries(lpddpal
,0,start
,len
,pal
);
356 /* set a single color in 16 color mode. */
357 void VGA_SetColor16(int reg
,int color
)
361 if (!lpddraw
) return;
362 pal
= &vga_def64_palette
[color
];
363 IDirectDrawPalette_SetEntries(lpddpal
,0,reg
,1,pal
);
364 vga_16_palette
[reg
]=(char)color
;
367 void VGA_SetQuadPalette(RGBQUAD
*color
,int start
,int len
)
369 PALETTEENTRY pal
[256];
372 if (!lpddraw
) return;
373 for (c
=0; c
<len
; c
++) {
374 pal
[c
].peRed
=color
[c
].rgbRed
;
375 pal
[c
].peGreen
=color
[c
].rgbGreen
;
376 pal
[c
].peBlue
=color
[c
].rgbBlue
;
379 IDirectDrawPalette_SetEntries(lpddpal
,0,start
,len
,pal
);
382 LPSTR
VGA_Lock(unsigned*Pitch
,unsigned*Height
,unsigned*Width
,unsigned*Depth
)
384 if (!lpddraw
) return NULL
;
385 if (!lpddsurf
) return NULL
;
386 if (IDirectDrawSurface_Lock(lpddsurf
,NULL
,&sdesc
,0,0)) {
387 ERR("could not lock surface!\n");
390 if (Pitch
) *Pitch
=sdesc
.u1
.lPitch
;
391 if (Height
) *Height
=sdesc
.dwHeight
;
392 if (Width
) *Width
=sdesc
.dwWidth
;
393 if (Depth
) *Depth
=sdesc
.ddpfPixelFormat
.u1
.dwRGBBitCount
;
394 return sdesc
.lpSurface
;
397 void VGA_Unlock(void)
399 IDirectDrawSurface_Unlock(lpddsurf
,sdesc
.lpSurface
);
404 int VGA_SetAlphaMode(unsigned Xres
,unsigned Yres
)
408 if (lpddraw
) VGA_Exit();
410 /* FIXME: Where to initialize text attributes? */
411 VGA_SetTextAttribute(0xf);
413 /* the xterm is slow, so refresh only every 200ms (5fps) */
414 VGA_InstallTimer(200);
418 SetConsoleScreenBufferSize(VGA_AlphaConsole(),siz
);
422 void VGA_GetAlphaMode(unsigned*Xres
,unsigned*Yres
)
424 CONSOLE_SCREEN_BUFFER_INFO info
;
425 GetConsoleScreenBufferInfo(VGA_AlphaConsole(),&info
);
426 if (Xres
) *Xres
=info
.dwSize
.X
;
427 if (Yres
) *Yres
=info
.dwSize
.Y
;
430 void VGA_SetCursorPos(unsigned X
,unsigned Y
)
434 if (!poll_timer
) VGA_SetAlphaMode(80, 25);
437 SetConsoleCursorPosition(VGA_AlphaConsole(),pos
);
440 void VGA_GetCursorPos(unsigned*X
,unsigned*Y
)
442 CONSOLE_SCREEN_BUFFER_INFO info
;
443 GetConsoleScreenBufferInfo(VGA_AlphaConsole(),&info
);
444 if (X
) *X
=info
.dwCursorPosition
.X
;
445 if (Y
) *Y
=info
.dwCursorPosition
.Y
;
448 void VGA_WriteChars(unsigned X
,unsigned Y
,unsigned ch
,int attr
,int count
)
456 EnterCriticalSection(&vga_lock
);
458 info
.Char
.AsciiChar
= ch
;
459 info
.Attributes
= (WORD
)attr
;
467 VGA_GetAlphaMode(&XR
, &YR
);
468 dat
= VGA_AlphaBuffer() + ((XR
*Y
+ X
) * 2);
470 dest
.Left
= X
+ count
;
471 dest
.Right
= X
+ count
;
477 info
.Attributes
= *dat
;
480 WriteConsoleOutputA(VGA_AlphaConsole(), &info
, siz
, off
, &dest
);
483 LeaveCriticalSection(&vga_lock
);
486 static void VGA_PutCharAt(BYTE ascii
, unsigned x
, unsigned y
)
488 unsigned width
, height
;
491 VGA_GetAlphaMode(&width
, &height
);
492 dat
= VGA_AlphaBuffer() + ((width
*y
+ x
) * 2);
494 dat
[1] = vga_text_attr
;
497 void VGA_PutChar(BYTE ascii
)
499 unsigned width
, height
, x
, y
, nx
, ny
;
501 EnterCriticalSection(&vga_lock
);
503 VGA_GetAlphaMode(&width
, &height
);
504 VGA_GetCursorPos(&x
, &y
);
508 VGA_PutCharAt(' ', x
, y
);
513 x
+= ((x
+ 8) & ~7) - x
;
529 VGA_PutCharAt(ascii
, x
, y
);
534 * FIXME: add line wrapping and scrolling
537 WriteFile(VGA_AlphaConsole(), &ascii
, 1, NULL
, NULL
);
540 * The following is just a sanity check.
542 VGA_GetCursorPos(&nx
, &ny
);
543 if(nx
!= x
|| ny
!= y
)
544 WARN("VGA emulator and text console have become unsynchronized.\n");
546 LeaveCriticalSection(&vga_lock
);
549 void VGA_SetTextAttribute(BYTE attr
)
551 vga_text_attr
= attr
;
552 SetConsoleTextAttribute(VGA_AlphaConsole(), attr
);
557 static void VGA_Poll_Graphics(void)
559 unsigned int Pitch
, Height
, Width
, X
, Y
;
561 char *dat
= DOSMEM_MapDosToLinear(0xa0000);
563 surf
= VGA_Lock(&Pitch
,&Height
,&Width
,NULL
);
566 if(vga_width
== 320 && vga_depth
<= 4)
567 for (Y
=0; Y
<vga_height
; Y
++,surf
+=Pitch
*2,dat
+=vga_width
/8) {
568 for(X
=0; X
<vga_width
; X
+=8) {
572 int b0
= (dat
[offset
] >> Z
) & 0x1;
574 surf
[(X
+index
)*2] = b0
;
575 surf
[(X
+index
)*2+1] = b0
;
576 surf
[(X
+index
)*2+Pitch
] = b0
;
577 surf
[(X
+index
)*2+Pitch
+1] = b0
;
582 if(vga_width
== 320 && vga_depth
== 8)
583 for (Y
=0; Y
<vga_height
; Y
++,surf
+=Pitch
*2,dat
+=vga_width
) {
584 for(X
=0; X
<vga_width
; X
++) {
588 surf
[X
*2+Pitch
] = b0
;
589 surf
[X
*2+Pitch
+1] = b0
;
594 for (Y
=0; Y
<vga_height
; Y
++,surf
+=Pitch
,dat
+=vga_width
/8) {
595 for(X
=0; X
<vga_width
; X
+=8) {
599 int b0
= (dat
[offset
] >> Z
) & 0x1;
609 static void VGA_Poll_Text(void)
612 unsigned int Height
,Width
,Y
,X
;
616 HANDLE con
= VGA_AlphaConsole();
618 VGA_GetAlphaMode(&Width
,&Height
);
619 dat
= VGA_AlphaBuffer();
620 siz
.X
= 80; siz
.Y
= 1;
621 off
.X
= 0; off
.Y
= 0;
622 /* copy from virtual VGA frame buffer to console */
623 for (Y
=0; Y
<Height
; Y
++) {
624 dest
.Top
=Y
; dest
.Bottom
=Y
;
625 for (X
=0; X
<Width
; X
++) {
626 ch
[X
].Char
.AsciiChar
= *dat
++;
627 /* WriteConsoleOutputA doesn't like "dead" chars */
628 if (ch
[X
].Char
.AsciiChar
== '\0')
629 ch
[X
].Char
.AsciiChar
= ' ';
630 ch
[X
].Attributes
= *dat
++;
632 dest
.Left
=0; dest
.Right
=Width
+1;
633 WriteConsoleOutputA(con
, ch
, siz
, off
, &dest
);
637 static void CALLBACK
VGA_Poll( LPVOID arg
, DWORD low
, DWORD high
)
639 if(!TryEnterCriticalSection(&vga_lock
))
642 /* FIXME: optimize by doing this only if the data has actually changed
643 * (in a way similar to DIBSection, perhaps) */
651 LeaveCriticalSection(&vga_lock
);
654 static BYTE palreg
,palcnt
;
655 static PALETTEENTRY paldat
;
657 void VGA_ioport_out( WORD port
, BYTE val
)
661 palreg
=val
; palcnt
=0; break;
663 ((BYTE
*)&paldat
)[palcnt
++]=val
<< 2;
665 VGA_SetPalette(&paldat
,palreg
++,1);
672 BYTE
VGA_ioport_in( WORD port
)
678 /* since we don't (yet?) serve DOS VM requests while VGA_Poll is running,
679 we need to fake the occurrence of the vertical refresh */
680 ret
=vga_refresh
?0x00:0x08;
692 VGA_DeinstallTimer();