1998-11-01 13:51:47 +01:00
|
|
|
|
/*
|
|
|
|
|
* VGA hardware emulation
|
|
|
|
|
*
|
|
|
|
|
* Copyright 1998 Ove K<EFBFBD>ven (with some help from Marcus Meissner)
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include "winbase.h"
|
1999-02-19 11:37:02 +01:00
|
|
|
|
#include "winuser.h"
|
|
|
|
|
#include "wine/winuser16.h"
|
1998-11-01 13:51:47 +01:00
|
|
|
|
#include "miscemu.h"
|
|
|
|
|
#include "vga.h"
|
|
|
|
|
#include "ddraw.h"
|
|
|
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
|
|
static IDirectDraw *lpddraw = NULL;
|
|
|
|
|
static IDirectDrawSurface *lpddsurf;
|
|
|
|
|
static IDirectDrawPalette *lpddpal;
|
|
|
|
|
static DDSURFACEDESC sdesc;
|
|
|
|
|
static WORD poll_timer;
|
|
|
|
|
static CRITICAL_SECTION vga_crit;
|
1998-11-08 16:06:31 +01:00
|
|
|
|
static int vga_polling,vga_refresh;
|
1998-11-01 13:51:47 +01:00
|
|
|
|
|
|
|
|
|
int VGA_SetMode(unsigned Xres,unsigned Yres,unsigned Depth)
|
|
|
|
|
{
|
|
|
|
|
if (lpddraw) VGA_Exit();
|
|
|
|
|
if (!lpddraw) {
|
|
|
|
|
DirectDrawCreate(NULL,&lpddraw,NULL);
|
|
|
|
|
if (!lpddraw) {
|
|
|
|
|
ERR(ddraw,"DirectDraw is not available\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (lpddraw->lpvtbl->fnSetDisplayMode(lpddraw,Xres,Yres,Depth)) {
|
|
|
|
|
ERR(ddraw,"DirectDraw does not support requested display mode\n");
|
|
|
|
|
lpddraw->lpvtbl->fnRelease(lpddraw);
|
|
|
|
|
lpddraw=NULL;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
1999-02-05 11:23:37 +01:00
|
|
|
|
lpddraw->lpvtbl->fnCreatePalette(lpddraw,DDPCAPS_8BIT,NULL,&lpddpal,NULL);
|
1998-11-01 13:51:47 +01:00
|
|
|
|
memset(&sdesc,0,sizeof(sdesc));
|
|
|
|
|
sdesc.dwSize=sizeof(sdesc);
|
1999-01-17 17:55:11 +01:00
|
|
|
|
sdesc.dwFlags = DDSD_CAPS;
|
|
|
|
|
sdesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
|
1998-11-01 13:51:47 +01:00
|
|
|
|
if (lpddraw->lpvtbl->fnCreateSurface(lpddraw,&sdesc,&lpddsurf,NULL)||(!lpddsurf)) {
|
|
|
|
|
ERR(ddraw,"DirectDraw surface is not available\n");
|
|
|
|
|
lpddraw->lpvtbl->fnRelease(lpddraw);
|
|
|
|
|
lpddraw=NULL;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
1998-11-08 16:06:31 +01:00
|
|
|
|
vga_refresh=0;
|
1998-11-01 13:51:47 +01:00
|
|
|
|
InitializeCriticalSection(&vga_crit);
|
1999-01-17 17:32:32 +01:00
|
|
|
|
MakeCriticalSectionGlobal(&vga_crit);
|
1998-11-01 13:51:47 +01:00
|
|
|
|
/* poll every 20ms (50fps should provide adequate responsiveness) */
|
1998-12-10 11:47:26 +01:00
|
|
|
|
poll_timer = CreateSystemTimer( 20, VGA_Poll );
|
1998-11-01 13:51:47 +01:00
|
|
|
|
}
|
|
|
|
|
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.x.dwRGBBitCount;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VGA_Exit(void)
|
|
|
|
|
{
|
|
|
|
|
if (lpddraw) {
|
|
|
|
|
SYSTEM_KillSystemTimer(poll_timer);
|
|
|
|
|
DeleteCriticalSection(&vga_crit);
|
|
|
|
|
lpddsurf->lpvtbl->fnRelease(lpddsurf);
|
|
|
|
|
lpddsurf=NULL;
|
|
|
|
|
lpddraw->lpvtbl->fnRelease(lpddraw);
|
|
|
|
|
lpddraw=NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VGA_SetPalette(PALETTEENTRY*pal,int start,int len)
|
|
|
|
|
{
|
|
|
|
|
if (!lpddraw) return;
|
|
|
|
|
lpddpal->lpvtbl->fnSetEntries(lpddpal,0,start,len,pal);
|
|
|
|
|
lpddsurf->lpvtbl->fnSetPalette(lpddsurf,lpddpal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
lpddpal->lpvtbl->fnSetEntries(lpddpal,0,start,len,pal);
|
|
|
|
|
lpddsurf->lpvtbl->fnSetPalette(lpddsurf,lpddpal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LPSTR VGA_Lock(unsigned*Pitch,unsigned*Height,unsigned*Width,unsigned*Depth)
|
|
|
|
|
{
|
|
|
|
|
if (!lpddraw) return NULL;
|
|
|
|
|
if (!lpddsurf) return NULL;
|
|
|
|
|
if (lpddsurf->lpvtbl->fnLock(lpddsurf,NULL,&sdesc,0,0)) {
|
|
|
|
|
ERR(ddraw,"could not lock surface!\n");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
if (Pitch) *Pitch=sdesc.lPitch;
|
|
|
|
|
if (Height) *Height=sdesc.dwHeight;
|
|
|
|
|
if (Width) *Width=sdesc.dwWidth;
|
|
|
|
|
if (Depth) *Depth=sdesc.ddpfPixelFormat.x.dwRGBBitCount;
|
|
|
|
|
return sdesc.y.lpSurface;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VGA_Unlock(void)
|
|
|
|
|
{
|
|
|
|
|
lpddsurf->lpvtbl->fnUnlock(lpddsurf,sdesc.y.lpSurface);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-17 17:55:11 +01:00
|
|
|
|
/* We are called from SIGALRM, aren't we? We should _NOT_ do synchronization
|
|
|
|
|
* stuff!
|
|
|
|
|
*/
|
1998-12-10 11:47:26 +01:00
|
|
|
|
void VGA_Poll( WORD timer )
|
1998-11-01 13:51:47 +01:00
|
|
|
|
{
|
|
|
|
|
char *dat;
|
|
|
|
|
unsigned Pitch,Height,Width;
|
|
|
|
|
char *surf;
|
1999-01-28 17:33:44 +01:00
|
|
|
|
int Y;
|
|
|
|
|
/* int X; */
|
1998-11-01 13:51:47 +01:00
|
|
|
|
|
|
|
|
|
EnterCriticalSection(&vga_crit);
|
|
|
|
|
if (!vga_polling) {
|
|
|
|
|
vga_polling++;
|
|
|
|
|
LeaveCriticalSection(&vga_crit);
|
|
|
|
|
/* FIXME: optimize by doing this only if the data has actually changed
|
|
|
|
|
* (in a way similar to DIBSection, perhaps) */
|
|
|
|
|
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);
|
1999-01-17 17:55:11 +01:00
|
|
|
|
/*for (X=0; X<Width; X++) if (dat[X]) TRACE(ddraw,"data(%d) at (%d,%d)\n",dat[X],X,Y);*/
|
1998-11-01 13:51:47 +01:00
|
|
|
|
}
|
|
|
|
|
VGA_Unlock();
|
1998-11-08 16:06:31 +01:00
|
|
|
|
vga_refresh=1;
|
1998-11-01 13:51:47 +01:00
|
|
|
|
EnterCriticalSection(&vga_crit);
|
|
|
|
|
vga_polling--;
|
|
|
|
|
}
|
|
|
|
|
LeaveCriticalSection(&vga_crit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
palreg++;
|
|
|
|
|
}
|
1998-11-08 16:06:31 +01:00
|
|
|
|
break;
|
1998-11-01 13:51:47 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
1998-11-08 16:06:31 +01:00
|
|
|
|
|
|
|
|
|
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 */
|
|
|
|
|
if (lpddraw) {
|
|
|
|
|
ret=vga_refresh?0x00:0x08;
|
|
|
|
|
vga_refresh=0;
|
|
|
|
|
} else ret=0x08;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ret=0xff;
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|