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 BOOL vga_mode_initialized = FALSE;
48 static CRITICAL_SECTION vga_lock = CRITICAL_SECTION_INIT("VGA");
50 typedef HRESULT (WINAPI *DirectDrawCreateProc)(LPGUID,LPDIRECTDRAW *,LPUNKNOWN);
51 static DirectDrawCreateProc pDirectDrawCreate;
53 static void CALLBACK VGA_Poll( LPVOID arg, DWORD low, DWORD high );
56 * For simplicity, I'm creating a second palette.
57 * 16 color accesses will use these pointers and insert
58 * entries from the 64-color palette into the default
59 * palette. --Robert 'Admiral' Coeyman
62 static char vga_16_palette[17]={
68 0x05, /* 5 - Magenta */
70 0x07, /* 7 - Light gray */
71 0x38, /* 8 - Dark gray */
72 0x39, /* 9 - Light blue */
73 0x3a, /* A - Light green */
74 0x3b, /* B - Light cyan */
75 0x3c, /* C - Light red */
76 0x3d, /* D - Light magenta */
77 0x3e, /* E - Yellow */
79 0x00 /* Border Color */
82 static PALETTEENTRY vga_def_palette[256]={
84 {0x00, 0x00, 0x00}, /* 0 - Black */
85 {0x00, 0x00, 0x80}, /* 1 - Blue */
86 {0x00, 0x80, 0x00}, /* 2 - Green */
87 {0x00, 0x80, 0x80}, /* 3 - Cyan */
88 {0x80, 0x00, 0x00}, /* 4 - Red */
89 {0x80, 0x00, 0x80}, /* 5 - Magenta */
90 {0x80, 0x80, 0x00}, /* 6 - Brown */
91 {0xC0, 0xC0, 0xC0}, /* 7 - Light gray */
92 {0x80, 0x80, 0x80}, /* 8 - Dark gray */
93 {0x00, 0x00, 0xFF}, /* 9 - Light blue */
94 {0x00, 0xFF, 0x00}, /* A - Light green */
95 {0x00, 0xFF, 0xFF}, /* B - Light cyan */
96 {0xFF, 0x00, 0x00}, /* C - Light red */
97 {0xFF, 0x00, 0xFF}, /* D - Light magenta */
98 {0xFF, 0xFF, 0x00}, /* E - Yellow */
99 {0xFF, 0xFF, 0xFF}, /* F - White */
100 {0,0,0} /* FIXME: a series of continuous rainbow hues should follow */
104 * This palette is the dos default, converted from 18 bit color to 24.
105 * It contains only 64 entries of colors--all others are zeros.
106 * --Robert 'Admiral' Coeyman
108 static PALETTEENTRY vga_def64_palette[256]={
110 {0x00, 0x00, 0x00}, /* 0x00 Black */
111 {0x00, 0x00, 0xaa}, /* 0x01 Blue */
112 {0x00, 0xaa, 0x00}, /* 0x02 Green */
113 {0x00, 0xaa, 0xaa}, /* 0x03 Cyan */
114 {0xaa, 0x00, 0x00}, /* 0x04 Red */
115 {0xaa, 0x00, 0xaa}, /* 0x05 Magenta */
116 {0xaa, 0xaa, 0x00}, /* 0x06 */
117 {0xaa, 0xaa, 0xaa}, /* 0x07 Light Gray */
118 {0x00, 0x00, 0x55}, /* 0x08 */
119 {0x00, 0x00, 0xff}, /* 0x09 */
120 {0x00, 0xaa, 0x55}, /* 0x0a */
121 {0x00, 0xaa, 0xff}, /* 0x0b */
122 {0xaa, 0x00, 0x55}, /* 0x0c */
123 {0xaa, 0x00, 0xff}, /* 0x0d */
124 {0xaa, 0xaa, 0x55}, /* 0x0e */
125 {0xaa, 0xaa, 0xff}, /* 0x0f */
126 {0x00, 0x55, 0x00}, /* 0x10 */
127 {0x00, 0x55, 0xaa}, /* 0x11 */
128 {0x00, 0xff, 0x00}, /* 0x12 */
129 {0x00, 0xff, 0xaa}, /* 0x13 */
130 {0xaa, 0x55, 0x00}, /* 0x14 Brown */
131 {0xaa, 0x55, 0xaa}, /* 0x15 */
132 {0xaa, 0xff, 0x00}, /* 0x16 */
133 {0xaa, 0xff, 0xaa}, /* 0x17 */
134 {0x00, 0x55, 0x55}, /* 0x18 */
135 {0x00, 0x55, 0xff}, /* 0x19 */
136 {0x00, 0xff, 0x55}, /* 0x1a */
137 {0x00, 0xff, 0xff}, /* 0x1b */
138 {0xaa, 0x55, 0x55}, /* 0x1c */
139 {0xaa, 0x55, 0xff}, /* 0x1d */
140 {0xaa, 0xff, 0x55}, /* 0x1e */
141 {0xaa, 0xff, 0xff}, /* 0x1f */
142 {0x55, 0x00, 0x00}, /* 0x20 */
143 {0x55, 0x00, 0xaa}, /* 0x21 */
144 {0x55, 0xaa, 0x00}, /* 0x22 */
145 {0x55, 0xaa, 0xaa}, /* 0x23 */
146 {0xff, 0x00, 0x00}, /* 0x24 */
147 {0xff, 0x00, 0xaa}, /* 0x25 */
148 {0xff, 0xaa, 0x00}, /* 0x26 */
149 {0xff, 0xaa, 0xaa}, /* 0x27 */
150 {0x55, 0x00, 0x55}, /* 0x28 */
151 {0x55, 0x00, 0xff}, /* 0x29 */
152 {0x55, 0xaa, 0x55}, /* 0x2a */
153 {0x55, 0xaa, 0xff}, /* 0x2b */
154 {0xff, 0x00, 0x55}, /* 0x2c */
155 {0xff, 0x00, 0xff}, /* 0x2d */
156 {0xff, 0xaa, 0x55}, /* 0x2e */
157 {0xff, 0xaa, 0xff}, /* 0x2f */
158 {0x55, 0x55, 0x00}, /* 0x30 */
159 {0x55, 0x55, 0xaa}, /* 0x31 */
160 {0x55, 0xff, 0x00}, /* 0x32 */
161 {0x55, 0xff, 0xaa}, /* 0x33 */
162 {0xff, 0x55, 0x00}, /* 0x34 */
163 {0xff, 0x55, 0xaa}, /* 0x35 */
164 {0xff, 0xff, 0x00}, /* 0x36 */
165 {0xff, 0xff, 0xaa}, /* 0x37 */
166 {0x55, 0x55, 0x55}, /* 0x38 Dark Gray */
167 {0x55, 0x55, 0xff}, /* 0x39 Light Blue */
168 {0x55, 0xff, 0x55}, /* 0x3a Light Green */
169 {0x55, 0xff, 0xff}, /* 0x3b Light Cyan */
170 {0xff, 0x55, 0x55}, /* 0x3c Light Red */
171 {0xff, 0x55, 0xff}, /* 0x3d Light Magenta */
172 {0xff, 0xff, 0x55}, /* 0x3e Yellow */
173 {0xff, 0xff, 0xff}, /* 0x3f White */
174 {0,0,0} /* The next 192 entries are all zeros */
177 static HANDLE VGA_timer;
178 static HANDLE VGA_timer_thread;
180 /* set the timer rate; called in the polling thread context */
181 static void CALLBACK set_timer_rate( ULONG_PTR arg )
185 when.s.LowPart = when.s.HighPart = 0;
186 SetWaitableTimer( VGA_timer, &when, arg, VGA_Poll, 0, FALSE );
189 static DWORD CALLBACK VGA_TimerThread( void *dummy )
191 for (;;) WaitForMultipleObjectsEx( 0, NULL, FALSE, INFINITE, TRUE );
194 static void VGA_DeinstallTimer(void)
196 if (VGA_timer_thread)
198 CancelWaitableTimer( VGA_timer );
199 CloseHandle( VGA_timer );
200 TerminateThread( VGA_timer_thread, 0 );
201 CloseHandle( VGA_timer_thread );
202 VGA_timer_thread = 0;
206 static void VGA_InstallTimer(unsigned Rate)
208 if (!VGA_timer_thread)
210 VGA_timer = CreateWaitableTimerA( NULL, FALSE, NULL );
211 VGA_timer_thread = CreateThread( NULL, 0, VGA_TimerThread, NULL, 0, NULL );
213 QueueUserAPC( set_timer_rate, VGA_timer_thread, (ULONG_PTR)Rate );
216 HANDLE VGA_AlphaConsole(void)
218 /* this assumes that no Win32 redirection has taken place, but then again,
219 * only 16-bit apps are likely to use this part of Wine... */
220 return GetStdHandle(STD_OUTPUT_HANDLE);
223 char*VGA_AlphaBuffer(void)
225 return DOSMEM_MapDosToLinear(0xb8000);
228 /*** GRAPHICS MODE ***/
231 unsigned Xres, Yres, Depth;
235 static void WINAPI VGA_DoExit(ULONG_PTR arg)
237 VGA_DeinstallTimer();
238 IDirectDrawSurface_SetPalette(lpddsurf,NULL);
239 IDirectDrawSurface_Release(lpddsurf);
241 IDirectDrawPalette_Release(lpddpal);
243 IDirectDraw_Release(lpddraw);
247 static void WINAPI VGA_DoSetMode(ULONG_PTR arg)
251 ModeSet *par = (ModeSet *)arg;
254 if (lpddraw) VGA_DoExit(0);
256 if (!pDirectDrawCreate)
258 HMODULE hmod = LoadLibraryA( "ddraw.dll" );
259 if (hmod) pDirectDrawCreate = (DirectDrawCreateProc)GetProcAddress( hmod, "DirectDrawCreate" );
260 if (!pDirectDrawCreate) {
261 ERR("Can't lookup DirectDrawCreate from ddraw.dll.\n");
265 res = pDirectDrawCreate(NULL,&lpddraw,NULL);
267 ERR("DirectDraw is not available (res = %lx)\n",res);
270 hwnd = CreateWindowExA(0,"STATIC","WINEDOS VGA",WS_POPUP|WS_BORDER|WS_CAPTION|WS_SYSMENU,0,0,par->Xres,par->Yres,0,0,0,NULL);
272 ERR("Failed to create user window.\n");
274 if ((res=IDirectDraw_SetCooperativeLevel(lpddraw,hwnd,DDSCL_FULLSCREEN|DDSCL_EXCLUSIVE))) {
275 ERR("Could not set cooperative level to exclusive (%lx)\n",res);
278 if ((res=IDirectDraw_SetDisplayMode(lpddraw,par->Xres,par->Yres,par->Depth))) {
279 ERR("DirectDraw does not support requested display mode (%dx%dx%d), res = %lx!\n",par->Xres,par->Yres,par->Depth,res);
280 IDirectDraw_Release(lpddraw);
285 res=IDirectDraw_CreatePalette(lpddraw,DDPCAPS_8BIT,NULL,&lpddpal,NULL);
287 ERR("Could not create palette (res = %lx)\n",res);
288 IDirectDraw_Release(lpddraw);
292 if ((res=IDirectDrawPalette_SetEntries(lpddpal,0,0,256,vga_def_palette))) {
293 ERR("Could not set default palette entries (res = %lx)\n", res);
296 memset(&sdesc,0,sizeof(sdesc));
297 sdesc.dwSize=sizeof(sdesc);
298 sdesc.dwFlags = DDSD_CAPS;
299 sdesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
300 if (IDirectDraw_CreateSurface(lpddraw,&sdesc,&lpddsurf,NULL)||(!lpddsurf)) {
301 ERR("DirectDraw surface is not available\n");
302 IDirectDraw_Release(lpddraw);
306 IDirectDrawSurface_SetPalette(lpddsurf,lpddpal);
308 /* poll every 20ms (50fps should provide adequate responsiveness) */
309 VGA_InstallTimer(20);
315 int VGA_SetMode(unsigned Xres,unsigned Yres,unsigned Depth)
323 if(Xres >= 640 || Yres >= 480) {
331 par.Depth = (Depth < 8) ? 8 : Depth;
333 vga_mode_initialized = TRUE;
335 MZ_RunInThread(VGA_DoSetMode, (ULONG_PTR)&par);
339 int VGA_GetMode(unsigned*Height,unsigned*Width,unsigned*Depth)
341 if (!lpddraw) return 1;
342 if (!lpddsurf) return 1;
343 if (Height) *Height=sdesc.dwHeight;
344 if (Width) *Width=sdesc.dwWidth;
345 if (Depth) *Depth=sdesc.ddpfPixelFormat.u1.dwRGBBitCount;
351 if (lpddraw) MZ_RunInThread(VGA_DoExit, 0);
354 void VGA_SetPalette(PALETTEENTRY*pal,int start,int len)
356 if (!lpddraw) return;
357 IDirectDrawPalette_SetEntries(lpddpal,0,start,len,pal);
360 /* set a single [char wide] color in 16 color mode. */
361 void VGA_SetColor16(int reg,int color)
365 if (!lpddraw) return;
366 pal= &vga_def64_palette[color];
367 IDirectDrawPalette_SetEntries(lpddpal,0,reg,1,pal);
368 vga_16_palette[reg]=(char)color;
371 /* Get a single [char wide] color in 16 color mode. */
372 char VGA_GetColor16(int reg)
375 if (!lpddraw) return 0;
376 return (char)vga_16_palette[reg];
379 /* set all 17 [char wide] colors at once in 16 color mode. */
380 void VGA_Set16Palette(char *Table)
385 if (!lpddraw) return; /* return if we're in text only mode */
386 bcopy((void *)&vga_16_palette,(void *)Table,17);
387 /* copy the entries into the table */
388 for (c=0; c<17; c++) { /* 17 entries */
389 pal= &vga_def64_palette[(int)vga_16_palette[c]]; /* get color */
390 IDirectDrawPalette_SetEntries(lpddpal,0,c,1,pal); /* set entry */
391 TRACE("Palette register %d set to %d\n",c,(int)vga_16_palette[c]);
392 } /* end of the counting loop */
395 /* Get all 17 [ char wide ] colors at once in 16 color mode. */
396 void VGA_Get16Palette(char *Table)
399 if (!lpddraw) return; /* return if we're in text only mode */
400 bcopy((void *)Table,(void *)&vga_16_palette,17);
401 /* copy the entries into the table */
404 void VGA_SetQuadPalette(RGBQUAD*color,int start,int len)
406 PALETTEENTRY pal[256];
409 if (!lpddraw) return;
410 for (c=0; c<len; c++) {
411 pal[c].peRed =color[c].rgbRed;
412 pal[c].peGreen=color[c].rgbGreen;
413 pal[c].peBlue =color[c].rgbBlue;
416 IDirectDrawPalette_SetEntries(lpddpal,0,start,len,pal);
419 LPSTR VGA_Lock(unsigned*Pitch,unsigned*Height,unsigned*Width,unsigned*Depth)
421 if (!lpddraw) return NULL;
422 if (!lpddsurf) return NULL;
423 if (IDirectDrawSurface_Lock(lpddsurf,NULL,&sdesc,0,0)) {
424 ERR("could not lock surface!\n");
427 if (Pitch) *Pitch=sdesc.u1.lPitch;
428 if (Height) *Height=sdesc.dwHeight;
429 if (Width) *Width=sdesc.dwWidth;
430 if (Depth) *Depth=sdesc.ddpfPixelFormat.u1.dwRGBBitCount;
431 return sdesc.lpSurface;
434 void VGA_Unlock(void)
436 IDirectDrawSurface_Unlock(lpddsurf,sdesc.lpSurface);
441 int VGA_SetAlphaMode(unsigned Xres,unsigned Yres)
445 if (lpddraw) VGA_Exit();
447 /* FIXME: Where to initialize text attributes? */
448 VGA_SetTextAttribute(0xf);
450 /* the xterm is slow, so refresh only every 200ms (5fps) */
451 VGA_InstallTimer(200);
455 SetConsoleScreenBufferSize(VGA_AlphaConsole(),siz);
459 void VGA_GetAlphaMode(unsigned*Xres,unsigned*Yres)
461 CONSOLE_SCREEN_BUFFER_INFO info;
462 GetConsoleScreenBufferInfo(VGA_AlphaConsole(),&info);
463 if (Xres) *Xres=info.dwSize.X;
464 if (Yres) *Yres=info.dwSize.Y;
467 void VGA_SetCursorPos(unsigned X,unsigned Y)
471 if (!poll_timer) VGA_SetAlphaMode(80, 25);
474 SetConsoleCursorPosition(VGA_AlphaConsole(),pos);
477 void VGA_GetCursorPos(unsigned*X,unsigned*Y)
479 CONSOLE_SCREEN_BUFFER_INFO info;
480 GetConsoleScreenBufferInfo(VGA_AlphaConsole(),&info);
481 if (X) *X=info.dwCursorPosition.X;
482 if (Y) *Y=info.dwCursorPosition.Y;
485 void VGA_WriteChars(unsigned X,unsigned Y,unsigned ch,int attr,int count)
493 EnterCriticalSection(&vga_lock);
495 info.Char.AsciiChar = ch;
496 info.Attributes = (WORD)attr;
504 VGA_GetAlphaMode(&XR, &YR);
505 dat = VGA_AlphaBuffer() + ((XR*Y + X) * 2);
507 dest.Left = X + count;
508 dest.Right = X + count;
514 info.Attributes = *dat;
517 WriteConsoleOutputA(VGA_AlphaConsole(), &info, siz, off, &dest);
520 LeaveCriticalSection(&vga_lock);
523 static void VGA_PutCharAt(BYTE ascii, unsigned x, unsigned y)
525 unsigned width, height;
528 VGA_GetAlphaMode(&width, &height);
529 dat = VGA_AlphaBuffer() + ((width*y + x) * 2);
531 dat[1] = vga_text_attr;
534 void VGA_PutChar(BYTE ascii)
536 unsigned width, height, x, y, nx, ny;
538 EnterCriticalSection(&vga_lock);
540 VGA_GetAlphaMode(&width, &height);
541 VGA_GetCursorPos(&x, &y);
545 VGA_PutCharAt(' ', x, y);
550 x += ((x + 8) & ~7) - x;
566 VGA_PutCharAt(ascii, x, y);
571 * FIXME: add line wrapping and scrolling
574 WriteFile(VGA_AlphaConsole(), &ascii, 1, NULL, NULL);
577 * The following is just a sanity check.
579 VGA_GetCursorPos(&nx, &ny);
580 if(nx != x || ny != y)
581 WARN("VGA emulator and text console have become unsynchronized.\n");
583 LeaveCriticalSection(&vga_lock);
586 void VGA_SetTextAttribute(BYTE attr)
588 vga_text_attr = attr;
589 SetConsoleTextAttribute(VGA_AlphaConsole(), attr);
592 void VGA_ClearText(unsigned row1, unsigned col1,
593 unsigned row2, unsigned col2,
596 unsigned width, height, x, y;
598 char *dat = VGA_AlphaBuffer();
599 HANDLE con = VGA_AlphaConsole();
600 VGA_GetAlphaMode(&width, &height);
602 EnterCriticalSection(&vga_lock);
604 for(y=row1; y<=row2; y++) {
607 FillConsoleOutputCharacterA(con, ' ', col2-col1+1, off, NULL);
608 FillConsoleOutputAttribute(con, attr, col2-col1+1, off, NULL);
610 for(x=col1; x<=col2; x++) {
611 char *ptr = dat + ((width*y + x) * 2);
617 LeaveCriticalSection(&vga_lock);
620 void VGA_ScrollUpText(unsigned row1, unsigned col1,
621 unsigned row2, unsigned col2,
622 unsigned lines, BYTE attr)
624 FIXME("not implemented\n");
627 void VGA_ScrollDownText(unsigned row1, unsigned col1,
628 unsigned row2, unsigned col2,
629 unsigned lines, BYTE attr)
631 FIXME("not implemented\n");
634 void VGA_GetCharacterAtCursor(BYTE *ascii, BYTE *attr)
636 unsigned width, height, x, y;
639 VGA_GetAlphaMode(&width, &height);
640 VGA_GetCursorPos(&x, &y);
641 dat = VGA_AlphaBuffer() + ((width*y + x) * 2);
650 static void VGA_Poll_Graphics(void)
652 unsigned int Pitch, Height, Width, X, Y;
654 char *dat = DOSMEM_MapDosToLinear(0xa0000);
656 surf = VGA_Lock(&Pitch,&Height,&Width,NULL);
659 if(vga_width == 320 && vga_depth <= 4)
660 for (Y=0; Y<vga_height; Y++,surf+=Pitch*2,dat+=vga_width/8) {
661 for(X=0; X<vga_width; X+=8) {
665 int b0 = (dat[offset] >> Z) & 0x1;
667 surf[(X+index)*2] = b0;
668 surf[(X+index)*2+1] = b0;
669 surf[(X+index)*2+Pitch] = b0;
670 surf[(X+index)*2+Pitch+1] = b0;
675 if(vga_width == 320 && vga_depth == 8)
676 for (Y=0; Y<vga_height; Y++,surf+=Pitch*2,dat+=vga_width) {
677 for(X=0; X<vga_width; X++) {
681 surf[X*2+Pitch] = b0;
682 surf[X*2+Pitch+1] = b0;
687 for (Y=0; Y<vga_height; Y++,surf+=Pitch,dat+=vga_width/8) {
688 for(X=0; X<vga_width; X+=8) {
692 int b0 = (dat[offset] >> Z) & 0x1;
702 static void VGA_Poll_Text(void)
705 unsigned int Height,Width,Y,X;
709 HANDLE con = VGA_AlphaConsole();
711 VGA_GetAlphaMode(&Width,&Height);
712 dat = VGA_AlphaBuffer();
713 siz.X = 80; siz.Y = 1;
714 off.X = 0; off.Y = 0;
715 /* copy from virtual VGA frame buffer to console */
716 for (Y=0; Y<Height; Y++) {
717 dest.Top=Y; dest.Bottom=Y;
718 for (X=0; X<Width; X++) {
719 ch[X].Char.AsciiChar = *dat++;
720 /* WriteConsoleOutputA doesn't like "dead" chars */
721 if (ch[X].Char.AsciiChar == '\0')
722 ch[X].Char.AsciiChar = ' ';
723 ch[X].Attributes = *dat++;
725 dest.Left=0; dest.Right=Width+1;
726 WriteConsoleOutputA(con, ch, siz, off, &dest);
730 static void CALLBACK VGA_Poll( LPVOID arg, DWORD low, DWORD high )
732 if(!TryEnterCriticalSection(&vga_lock))
735 /* FIXME: optimize by doing this only if the data has actually changed
736 * (in a way similar to DIBSection, perhaps) */
744 LeaveCriticalSection(&vga_lock);
747 static BYTE palreg,palcnt;
748 static PALETTEENTRY paldat;
750 void VGA_ioport_out( WORD port, BYTE val )
754 palreg=val; palcnt=0; break;
756 ((BYTE*)&paldat)[palcnt++]=val << 2;
758 VGA_SetPalette(&paldat,palreg++,1);
765 BYTE VGA_ioport_in( WORD port )
771 /* since we don't (yet?) serve DOS VM requests while VGA_Poll is running,
772 we need to fake the occurrence of the vertical refresh */
773 ret=vga_refresh?0x00:0x08;
774 if (vga_mode_initialized)
777 /* Also fake the occurence of the vertical refresh when no graphic
779 vga_refresh=!vga_refresh;
790 VGA_DeinstallTimer();