| /* | |
| * VGA hardware emulation | |
| * | |
| * Copyright 1998 Ove Kåven (with some help from Marcus Meissner) | |
| * | |
| */ | |
| #include <string.h> | |
| #include "winbase.h" | |
| #include "wingdi.h" | |
| #include "winuser.h" | |
| #include "wincon.h" | |
| #include "miscemu.h" | |
| #include "vga.h" | |
| #include "ddraw.h" | |
| #include "services.h" | |
| #include "debugtools.h" | |
| DEFAULT_DEBUG_CHANNEL(ddraw); | |
| static IDirectDraw *lpddraw = NULL; | |
| static IDirectDrawSurface *lpddsurf; | |
| static IDirectDrawPalette *lpddpal; | |
| static DDSURFACEDESC sdesc; | |
| static LONG vga_polling,vga_refresh; | |
| static HANDLE poll_timer; | |
| typedef HRESULT WINAPI (*DirectDrawCreateProc)(LPGUID,LPDIRECTDRAW *,LPUNKNOWN); | |
| static DirectDrawCreateProc pDirectDrawCreate; | |
| typedef HWND WINAPI (*CreateWindowExAProc)(DWORD,LPCSTR,LPCSTR,DWORD,INT,INT, INT,INT,HWND,HMENU,HINSTANCE,LPVOID); | |
| static CreateWindowExAProc pCreateWindowExA; | |
| static void VGA_DeinstallTimer(void) | |
| { | |
| if (poll_timer) { | |
| SERVICE_Delete( poll_timer ); | |
| poll_timer = 0; | |
| } | |
| } | |
| static void VGA_InstallTimer(unsigned Rate) | |
| { | |
| VGA_DeinstallTimer(); | |
| if (!poll_timer) | |
| poll_timer = SERVICE_AddTimer( Rate, VGA_Poll, 0 ); | |
| } | |
| HANDLE VGA_AlphaConsole(void) | |
| { | |
| /* this assumes that no Win32 redirection has taken place, but then again, | |
| * only 16-bit apps are likely to use this part of Wine... */ | |
| return GetStdHandle(STD_OUTPUT_HANDLE); | |
| } | |
| char*VGA_AlphaBuffer(void) | |
| { | |
| return DOSMEM_MapDosToLinear(0xb8000); | |
| } | |
| /*** GRAPHICS MODE ***/ | |
| int VGA_SetMode(unsigned Xres,unsigned Yres,unsigned Depth) | |
| { | |
| LRESULT res; | |
| HWND hwnd; | |
| if (lpddraw) VGA_Exit(); | |
| if (!lpddraw) { | |
| if (!pDirectDrawCreate) | |
| { | |
| HMODULE hmod = LoadLibraryA( "ddraw.dll" ); | |
| if (hmod) pDirectDrawCreate = (DirectDrawCreateProc)GetProcAddress( hmod, "DirectDrawCreate" ); | |
| if (!pDirectDrawCreate) { | |
| ERR("Can't lookup DirectDrawCreate from ddraw.dll.\n"); | |
| return 1; | |
| } | |
| } | |
| if (!pCreateWindowExA) | |
| { | |
| HMODULE hmod = LoadLibraryA( "user32.dll" ); | |
| if (!hmod) { | |
| ERR("Can't load user32.dll.\n"); | |
| return 1; | |
| } | |
| if (hmod) pCreateWindowExA = (CreateWindowExAProc)GetProcAddress( hmod, "CreateWindowExA" ); | |
| if (!pCreateWindowExA) { | |
| ERR("Can't lookup CreateWindowExA from user32.dll.\n"); | |
| return 1; | |
| } | |
| } | |
| res = pDirectDrawCreate(NULL,&lpddraw,NULL); | |
| if (!lpddraw) { | |
| ERR("DirectDraw is not available (res = %lx)\n",res); | |
| return 1; | |
| } | |
| hwnd = pCreateWindowExA(0,"STATIC","WINEDOS VGA",WS_POPUP|WS_BORDER|WS_CAPTION|WS_SYSMENU,0,0,Xres,Yres,0,0,0,NULL); | |
| if (!hwnd) { | |
| ERR("Failed to create user window.\n"); | |
| } | |
| if ((res=IDirectDraw_SetCooperativeLevel(lpddraw,hwnd,DDSCL_FULLSCREEN|DDSCL_EXCLUSIVE))) { | |
| ERR("Could not set cooperative level to exclusive (%lx)\n",res); | |
| } | |
| if ((res=IDirectDraw_SetDisplayMode(lpddraw,Xres,Yres,Depth))) { | |
| ERR("DirectDraw does not support requested display mode (%dx%dx%d), res = %lx!\n",Xres,Yres,Depth,res); | |
| IDirectDraw_Release(lpddraw); | |
| lpddraw=NULL; | |
| return 1; | |
| } | |
| res=IDirectDraw_CreatePalette(lpddraw,DDPCAPS_8BIT,NULL,&lpddpal,NULL); | |
| if (res) { | |
| ERR("Could not create palette (res = %lx)\n",res); | |
| } | |
| memset(&sdesc,0,sizeof(sdesc)); | |
| sdesc.dwSize=sizeof(sdesc); | |
| sdesc.dwFlags = DDSD_CAPS; | |
| sdesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; | |
| if (IDirectDraw_CreateSurface(lpddraw,&sdesc,&lpddsurf,NULL)||(!lpddsurf)) { | |
| ERR("DirectDraw surface is not available\n"); | |
| IDirectDraw_Release(lpddraw); | |
| lpddraw=NULL; | |
| return 1; | |
| } | |
| FIXME("no default palette entries\n"); | |
| IDirectDrawSurface_SetPalette(lpddsurf,lpddpal); | |
| vga_refresh=0; | |
| /* poll every 20ms (50fps should provide adequate responsiveness) */ | |
| VGA_InstallTimer(20); | |
| } | |
| return 0; | |
| } | |
| int VGA_GetMode(unsigned*Height,unsigned*Width,unsigned*Depth) | |
| { | |
| if (!lpddraw) return 1; | |
| if (!lpddsurf) return 1; | |
| if (Height) *Height=sdesc.dwHeight; | |
| if (Width) *Width=sdesc.dwWidth; | |
| if (Depth) *Depth=sdesc.ddpfPixelFormat.u1.dwRGBBitCount; | |
| return 0; | |
| } | |
| void VGA_Exit(void) | |
| { | |
| if (lpddraw) { | |
| VGA_DeinstallTimer(); | |
| IDirectDrawSurface_SetPalette(lpddsurf,NULL); | |
| IDirectDrawSurface_Release(lpddsurf); | |
| lpddsurf=NULL; | |
| IDirectDrawPalette_Release(lpddpal); | |
| lpddpal=NULL; | |
| IDirectDraw_Release(lpddraw); | |
| lpddraw=NULL; | |
| } | |
| } | |
| void VGA_SetPalette(PALETTEENTRY*pal,int start,int len) | |
| { | |
| if (!lpddraw) return; | |
| IDirectDrawPalette_SetEntries(lpddpal,0,start,len,pal); | |
| } | |
| void VGA_SetQuadPalette(RGBQUAD*color,int start,int len) | |
| { | |
| PALETTEENTRY pal[256]; | |
| int c; | |
| if (!lpddraw) return; | |
| for (c=0; c<len; c++) { | |
| pal[c].peRed =color[c].rgbRed; | |
| pal[c].peGreen=color[c].rgbGreen; | |
| pal[c].peBlue =color[c].rgbBlue; | |
| pal[c].peFlags=0; | |
| } | |
| IDirectDrawPalette_SetEntries(lpddpal,0,start,len,pal); | |
| } | |
| LPSTR VGA_Lock(unsigned*Pitch,unsigned*Height,unsigned*Width,unsigned*Depth) | |
| { | |
| if (!lpddraw) return NULL; | |
| if (!lpddsurf) return NULL; | |
| if (IDirectDrawSurface_Lock(lpddsurf,NULL,&sdesc,0,0)) { | |
| ERR("could not lock surface!\n"); | |
| return NULL; | |
| } | |
| if (Pitch) *Pitch=sdesc.u1.lPitch; | |
| if (Height) *Height=sdesc.dwHeight; | |
| if (Width) *Width=sdesc.dwWidth; | |
| if (Depth) *Depth=sdesc.ddpfPixelFormat.u1.dwRGBBitCount; | |
| return sdesc.lpSurface; | |
| } | |
| void VGA_Unlock(void) | |
| { | |
| IDirectDrawSurface_Unlock(lpddsurf,sdesc.lpSurface); | |
| } | |
| /*** TEXT MODE ***/ | |
| int VGA_SetAlphaMode(unsigned Xres,unsigned Yres) | |
| { | |
| COORD siz; | |
| if (lpddraw) VGA_Exit(); | |
| /* the xterm is slow, so refresh only every 200ms (5fps) */ | |
| VGA_InstallTimer(200); | |
| siz.X = Xres; | |
| siz.Y = Yres; | |
| SetConsoleScreenBufferSize(VGA_AlphaConsole(),siz); | |
| return 0; | |
| } | |
| void VGA_GetAlphaMode(unsigned*Xres,unsigned*Yres) | |
| { | |
| CONSOLE_SCREEN_BUFFER_INFO info; | |
| GetConsoleScreenBufferInfo(VGA_AlphaConsole(),&info); | |
| if (Xres) *Xres=info.dwSize.X; | |
| if (Yres) *Yres=info.dwSize.Y; | |
| } | |
| void VGA_SetCursorPos(unsigned X,unsigned Y) | |
| { | |
| COORD pos; | |
| pos.X = X; | |
| pos.Y = Y; | |
| SetConsoleCursorPosition(VGA_AlphaConsole(),pos); | |
| } | |
| void VGA_GetCursorPos(unsigned*X,unsigned*Y) | |
| { | |
| CONSOLE_SCREEN_BUFFER_INFO info; | |
| GetConsoleScreenBufferInfo(VGA_AlphaConsole(),&info); | |
| if (X) *X=info.dwCursorPosition.X; | |
| if (Y) *Y=info.dwCursorPosition.Y; | |
| } | |
| void VGA_WriteChars(unsigned X,unsigned Y,unsigned ch,int attr,int count) | |
| { | |
| unsigned XR, YR; | |
| char*dat; | |
| VGA_GetAlphaMode(&XR, &YR); | |
| dat = VGA_AlphaBuffer() + ((XR*Y + X) * 2); | |
| /* FIXME: also call WriteConsoleOutputA, for better responsiveness */ | |
| while (count--) { | |
| *dat++ = ch; | |
| if (attr>=0) *dat = attr; | |
| dat++; | |
| } | |
| } | |
| /*** CONTROL ***/ | |
| void CALLBACK VGA_Poll( ULONG_PTR arg ) | |
| { | |
| char *dat; | |
| unsigned int Pitch,Height,Width,Y,X; | |
| char *surf; | |
| if (!InterlockedExchangeAdd(&vga_polling, 1)) { | |
| /* FIXME: optimize by doing this only if the data has actually changed | |
| * (in a way similar to DIBSection, perhaps) */ | |
| if (lpddraw) { | |
| /* graphics mode */ | |
| surf = VGA_Lock(&Pitch,&Height,&Width,NULL); | |
| if (!surf) return; | |
| dat = DOSMEM_MapDosToLinear(0xa0000); | |
| /* copy from virtual VGA frame buffer to DirectDraw surface */ | |
| for (Y=0; Y<Height; Y++,surf+=Pitch,dat+=Width) { | |
| memcpy(surf,dat,Width); | |
| /*for (X=0; X<Width; X++) if (dat[X]) TRACE(ddraw,"data(%d) at (%d,%d)\n",dat[X],X,Y);*/ | |
| } | |
| VGA_Unlock(); | |
| } else { | |
| /* text mode */ | |
| CHAR_INFO ch[80]; | |
| COORD siz, off; | |
| SMALL_RECT dest; | |
| HANDLE con = VGA_AlphaConsole(); | |
| VGA_GetAlphaMode(&Width,&Height); | |
| dat = VGA_AlphaBuffer(); | |
| siz.X = 80; siz.Y = 1; | |
| off.X = 0; off.Y = 0; | |
| /* copy from virtual VGA frame buffer to console */ | |
| for (Y=0; Y<Height; Y++) { | |
| dest.Top=Y; dest.Bottom=Y; | |
| for (X=0; X<Width; X++) { | |
| ch[X].Char.AsciiChar = *dat++; | |
| /* WriteConsoleOutputA doesn't like "dead" chars */ | |
| if (ch[X].Char.AsciiChar == '\0') | |
| ch[X].Char.AsciiChar = ' '; | |
| ch[X].Attributes = *dat++; | |
| } | |
| dest.Left=0; dest.Right=Width+1; | |
| WriteConsoleOutputA(con, ch, siz, off, &dest); | |
| } | |
| } | |
| vga_refresh=1; | |
| } | |
| InterlockedDecrement(&vga_polling); | |
| } | |
| static BYTE palreg,palcnt; | |
| static PALETTEENTRY paldat; | |
| void VGA_ioport_out( WORD port, BYTE val ) | |
| { | |
| switch (port) { | |
| case 0x3c8: | |
| palreg=val; palcnt=0; break; | |
| case 0x3c9: | |
| ((BYTE*)&paldat)[palcnt++]=val << 2; | |
| if (palcnt==3) { | |
| VGA_SetPalette(&paldat,palreg++,1); | |
| palcnt=0; | |
| } | |
| break; | |
| } | |
| } | |
| BYTE VGA_ioport_in( WORD port ) | |
| { | |
| BYTE ret; | |
| switch (port) { | |
| case 0x3da: | |
| /* since we don't (yet?) serve DOS VM requests while VGA_Poll is running, | |
| we need to fake the occurrence of the vertical refresh */ | |
| ret=vga_refresh?0x00:0x08; | |
| vga_refresh=0; | |
| break; | |
| default: | |
| ret=0xff; | |
| } | |
| return ret; | |
| } | |
| void VGA_Clean(void) | |
| { | |
| VGA_Exit(); | |
| VGA_DeinstallTimer(); | |
| } |