vga.c 35.2 KB
Newer Older
1 2
/*
 * VGA hardware emulation
3
 *
4 5
 * Copyright 1998 Ove Kven (with some help from Marcus Meissner)
 *
6 7 8 9 10 11 12 13 14 15 16 17
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20
 */

21
#include <stdarg.h>
22
#include <string.h>
23 24 25

#define NONAMELESSUNION
#define NONAMELESSSTRUCT
26
#include "windef.h"
27
#include "winbase.h"
28 29
#include "wingdi.h"
#include "winuser.h"
30
#include "wincon.h"
31
#include "dosexe.h"
32 33
#include "vga.h"
#include "ddraw.h"
34
#include "wine/debug.h"
35

36
WINE_DEFAULT_DEBUG_CHANNEL(ddraw);
37

38 39 40 41
static IDirectDraw *lpddraw = NULL;
static IDirectDrawSurface *lpddsurf;
static IDirectDrawPalette *lpddpal;
static DDSURFACEDESC sdesc;
42 43 44

static BOOL vga_retrace_vertical;
static BOOL vga_retrace_horizontal;
45

46 47 48 49 50 51 52 53 54 55
/*
 * Size and location of VGA controller window to framebuffer.
 *
 * Note: We support only single window even though some
 *       controllers support two. This should not be changed unless
 *       there are programs that depend on having two windows.
 */
#define VGA_WINDOW_SIZE  (64 * 1024)
#define VGA_WINDOW_START ((char *)0xa0000)

56 57 58 59 60 61 62 63 64 65 66 67 68
/*
 * VGA controller memory is emulated using linear framebuffer.
 * This frambuffer also acts as an interface
 * between VGA controller emulation and DirectDraw.
 *
 * vga_fb_width: Display width in pixels. Can be modified when
 *               display mode is changed.
 * vga_fb_height: Display height in pixels. Can be modified when
 *                display mode is changed.
 * vga_fb_depth: Number of bits used to store single pixel color information.
 *               Each pixel uses (vga_fb_depth+7)/8 bytes because
 *               1-16 color modes are mapped to 256 color mode.
 *               Can be modified when display mode is changed.
69
 * vga_fb_pitch: How many bytes to add to pointer in order to move
70 71 72 73 74 75 76 77 78 79
 *               from one row to another. This is fixed in VGA modes,
 *               but can be modified in SVGA modes.
 * vga_fb_offset: Offset added to framebuffer start address in order
 *                to find the display origin. Programs use this to do
 *                double buffering and to scroll display. The value can
 *                be modified in VGA and SVGA modes.
 * vga_fb_size: How many bytes are allocated to framebuffer.
 *              VGA framebuffers are always larger than display size and
 *              SVGA framebuffers may also be.
 * vga_fb_data: Pointer to framebuffer start.
80 81 82 83 84
 * vga_fb_window: Offset of 64k window 0xa0000 in bytes from framebuffer start.
 *                This value is >= 0, if mode uses linear framebuffer and
 *                -1, if mode uses color planes. This value is fixed
 *                in all modes except 0x13 (256 color VGA) where
 *                0 means normal mode and -1 means Mode-X (unchained mode).
85 86 87 88 89 90 91
 */
static int   vga_fb_width;
static int   vga_fb_height;
static int   vga_fb_depth;
static int   vga_fb_pitch;
static int   vga_fb_offset;
static int   vga_fb_size = 0;
92
static char *vga_fb_data = 0;
93
static int   vga_fb_window = 0;
94

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
/*
 * VGA text mode data.
 *
 * vga_text_attr: Current active attribute.
 * vga_text_old: Last data sent to console. 
 *               This is used to optimize console updates.
 * vga_text_width:  Width of the text display in characters.
 * vga_text_height: Height of the text display in characters.
 * vga_text_x: Current cursor X-position. Starts from zero.
 * vga_text_y: Current cursor Y-position. Starts from zero.
 * vga_text_console: TRUE if stdout is console, 
 *                   FALSE if it is regular file.
 */
static BYTE  vga_text_attr;
static char *vga_text_old = NULL;
static BYTE  vga_text_width;
static BYTE  vga_text_height;
static BYTE  vga_text_x;
static BYTE  vga_text_y;
static BOOL  vga_text_console;
115

116 117
/*
 * VGA controller ports 0x3c0, 0x3c4, 0x3ce and 0x3d4 are
118 119
 * indexed registers. These ports are used to select VGA controller
 * subregister that can be written to or read from using ports 0x3c1,
120 121 122 123 124 125 126 127 128 129 130
 * 0x3c5, 0x3cf or 0x3d5. Selected subregister indexes are
 * stored in variables vga_index_*.
 *
 * Port 0x3c0 is special because it is both index and
 * data-write register. Flip-flop vga_address_3c0 tells whether
 * the port acts currently as an address register. Reading from port
 * 0x3da resets the flip-flop to address mode.
 */
static BYTE vga_index_3c0;
static BYTE vga_index_3c4;
static BYTE vga_index_3ce;
131
static BYTE vga_index_3d4;
132 133
static BOOL vga_address_3c0 = TRUE;

134 135 136 137 138 139 140 141 142 143 144
/*
 * This mutex is used to protect VGA state during asynchronous
 * screen updates (see VGA_Poll). It makes sure that VGA state changes
 * are atomic and the user interface is protected from flicker and
 * corruption.
 *
 * The mutex actually serializes VGA operations and the screen update. 
 * Which means that whenever VGA_Poll occurs, application stalls if it 
 * tries to modify VGA state. This is not how real VGA adapters work,
 * but it makes timing and correctness issues much easier to deal with.
 */
145 146 147 148 149
static CRITICAL_SECTION vga_lock;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
    0, 0, &vga_lock,
    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
150
      0, 0, { (DWORD_PTR)(__FILE__ ": vga_lock") }
151 152
};
static CRITICAL_SECTION vga_lock = { &critsect_debug, -1, 0, 0, 0, 0 };
153

154
typedef HRESULT (WINAPI *DirectDrawCreateProc)(LPGUID,LPDIRECTDRAW *,LPUNKNOWN);
155
static DirectDrawCreateProc pDirectDrawCreate;
156

157 158
static void CALLBACK VGA_Poll( LPVOID arg, DWORD low, DWORD high );

159
static HWND vga_hwnd = NULL;
160

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
/*
 * For simplicity, I'm creating a second palette.
 * 16 color accesses will use these pointers and insert
 * entries from the 64-color palette into the default
 * palette.   --Robert 'Admiral' Coeyman
 */

static char vga_16_palette[17]={
  0x00,  /* 0 - Black         */
  0x01,  /* 1 - Blue          */
  0x02,  /* 2 - Green         */
  0x03,  /* 3 - Cyan          */
  0x04,  /* 4 - Red           */
  0x05,  /* 5 - Magenta       */
  0x14,  /* 6 - Brown         */
  0x07,  /* 7 - Light gray    */
  0x38,  /* 8 - Dark gray     */
  0x39,  /* 9 - Light blue    */
  0x3a,  /* A - Light green   */
  0x3b,  /* B - Light cyan    */
  0x3c,  /* C - Light red     */
  0x3d,  /* D - Light magenta */
  0x3e,  /* E - Yellow        */
  0x3f,  /* F - White         */
  0x00   /* Border Color      */
};

188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
static PALETTEENTRY vga_def_palette[256]={
/* red  green  blue */
  {0x00, 0x00, 0x00}, /* 0 - Black */
  {0x00, 0x00, 0x80}, /* 1 - Blue */
  {0x00, 0x80, 0x00}, /* 2 - Green */
  {0x00, 0x80, 0x80}, /* 3 - Cyan */
  {0x80, 0x00, 0x00}, /* 4 - Red */
  {0x80, 0x00, 0x80}, /* 5 - Magenta */
  {0x80, 0x80, 0x00}, /* 6 - Brown */
  {0xC0, 0xC0, 0xC0}, /* 7 - Light gray */
  {0x80, 0x80, 0x80}, /* 8 - Dark gray */
  {0x00, 0x00, 0xFF}, /* 9 - Light blue */
  {0x00, 0xFF, 0x00}, /* A - Light green */
  {0x00, 0xFF, 0xFF}, /* B - Light cyan */
  {0xFF, 0x00, 0x00}, /* C - Light red */
  {0xFF, 0x00, 0xFF}, /* D - Light magenta */
  {0xFF, 0xFF, 0x00}, /* E - Yellow */
  {0xFF, 0xFF, 0xFF}, /* F - White */
  {0,0,0} /* FIXME: a series of continuous rainbow hues should follow */
};

209 210
/*
 *   This palette is the dos default, converted from 18 bit color to 24.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
 *      It contains only 64 entries of colors--all others are zeros.
 *          --Robert 'Admiral' Coeyman
 */
static PALETTEENTRY vga_def64_palette[256]={
/* red  green  blue */
  {0x00, 0x00, 0x00}, /* 0x00      Black      */
  {0x00, 0x00, 0xaa}, /* 0x01      Blue       */
  {0x00, 0xaa, 0x00}, /* 0x02      Green      */
  {0x00, 0xaa, 0xaa}, /* 0x03      Cyan       */
  {0xaa, 0x00, 0x00}, /* 0x04      Red        */
  {0xaa, 0x00, 0xaa}, /* 0x05      Magenta    */
  {0xaa, 0xaa, 0x00}, /* 0x06      */
  {0xaa, 0xaa, 0xaa}, /* 0x07      Light Gray */
  {0x00, 0x00, 0x55}, /* 0x08      */
  {0x00, 0x00, 0xff}, /* 0x09      */
  {0x00, 0xaa, 0x55}, /* 0x0a      */
  {0x00, 0xaa, 0xff}, /* 0x0b      */
  {0xaa, 0x00, 0x55}, /* 0x0c      */
  {0xaa, 0x00, 0xff}, /* 0x0d      */
  {0xaa, 0xaa, 0x55}, /* 0x0e      */
  {0xaa, 0xaa, 0xff}, /* 0x0f      */
  {0x00, 0x55, 0x00}, /* 0x10      */
  {0x00, 0x55, 0xaa}, /* 0x11      */
  {0x00, 0xff, 0x00}, /* 0x12      */
  {0x00, 0xff, 0xaa}, /* 0x13      */
  {0xaa, 0x55, 0x00}, /* 0x14      Brown      */
  {0xaa, 0x55, 0xaa}, /* 0x15      */
  {0xaa, 0xff, 0x00}, /* 0x16      */
  {0xaa, 0xff, 0xaa}, /* 0x17      */
  {0x00, 0x55, 0x55}, /* 0x18      */
  {0x00, 0x55, 0xff}, /* 0x19      */
  {0x00, 0xff, 0x55}, /* 0x1a      */
  {0x00, 0xff, 0xff}, /* 0x1b      */
  {0xaa, 0x55, 0x55}, /* 0x1c      */
  {0xaa, 0x55, 0xff}, /* 0x1d      */
  {0xaa, 0xff, 0x55}, /* 0x1e      */
  {0xaa, 0xff, 0xff}, /* 0x1f      */
  {0x55, 0x00, 0x00}, /* 0x20      */
  {0x55, 0x00, 0xaa}, /* 0x21      */
  {0x55, 0xaa, 0x00}, /* 0x22      */
  {0x55, 0xaa, 0xaa}, /* 0x23      */
  {0xff, 0x00, 0x00}, /* 0x24      */
  {0xff, 0x00, 0xaa}, /* 0x25      */
  {0xff, 0xaa, 0x00}, /* 0x26      */
  {0xff, 0xaa, 0xaa}, /* 0x27      */
  {0x55, 0x00, 0x55}, /* 0x28      */
  {0x55, 0x00, 0xff}, /* 0x29      */
  {0x55, 0xaa, 0x55}, /* 0x2a      */
  {0x55, 0xaa, 0xff}, /* 0x2b      */
  {0xff, 0x00, 0x55}, /* 0x2c      */
  {0xff, 0x00, 0xff}, /* 0x2d      */
  {0xff, 0xaa, 0x55}, /* 0x2e      */
  {0xff, 0xaa, 0xff}, /* 0x2f      */
  {0x55, 0x55, 0x00}, /* 0x30      */
  {0x55, 0x55, 0xaa}, /* 0x31      */
  {0x55, 0xff, 0x00}, /* 0x32      */
  {0x55, 0xff, 0xaa}, /* 0x33      */
  {0xff, 0x55, 0x00}, /* 0x34      */
  {0xff, 0x55, 0xaa}, /* 0x35      */
  {0xff, 0xff, 0x00}, /* 0x36      */
  {0xff, 0xff, 0xaa}, /* 0x37      */
  {0x55, 0x55, 0x55}, /* 0x38      Dark Gray     */
  {0x55, 0x55, 0xff}, /* 0x39      Light Blue    */
  {0x55, 0xff, 0x55}, /* 0x3a      Light Green   */
  {0x55, 0xff, 0xff}, /* 0x3b      Light Cyan    */
  {0xff, 0x55, 0x55}, /* 0x3c      Light Red     */
  {0xff, 0x55, 0xff}, /* 0x3d      Light Magenta */
  {0xff, 0xff, 0x55}, /* 0x3e      Yellow        */
  {0xff, 0xff, 0xff}, /* 0x3f      White         */
  {0,0,0} /* The next 192 entries are all zeros  */
};

283 284 285 286 287 288 289 290
static HANDLE VGA_timer;
static HANDLE VGA_timer_thread;

/* set the timer rate; called in the polling thread context */
static void CALLBACK set_timer_rate( ULONG_PTR arg )
{
    LARGE_INTEGER when;

291
    when.u.LowPart = when.u.HighPart = 0;
292 293 294 295 296
    SetWaitableTimer( VGA_timer, &when, arg, VGA_Poll, 0, FALSE );
}

static DWORD CALLBACK VGA_TimerThread( void *dummy )
{
297
    for (;;) SleepEx( INFINITE, TRUE );
298 299
}

300 301
static void VGA_DeinstallTimer(void)
{
302 303
    if (VGA_timer_thread)
    {
304 305 306 307 308 309 310 311 312 313 314 315
        /*
         * Make sure the update thread is not holding
         * system resources when we kill it.
         *
         * Now, we only need to worry about update thread
         * getting terminated while in EnterCriticalSection 
         * or WaitForMultipleObjectsEx.
         *
         * FIXME: Is this a problem?
         */
        EnterCriticalSection(&vga_lock);

316 317 318 319 320
        CancelWaitableTimer( VGA_timer );
        CloseHandle( VGA_timer );
        TerminateThread( VGA_timer_thread, 0 );
        CloseHandle( VGA_timer_thread );
        VGA_timer_thread = 0;
321 322 323 324 325 326 327 328 329

        LeaveCriticalSection(&vga_lock);

        /*
         * Synchronize display. This makes sure that
         * changes to display become visible even if program 
         * terminates before update thread had time to run.
         */
        VGA_Poll( 0, 0, 0 );
330 331 332 333 334
    }
}

static void VGA_InstallTimer(unsigned Rate)
{
335 336 337 338 339 340
    if (!VGA_timer_thread)
    {
        VGA_timer = CreateWaitableTimerA( NULL, FALSE, NULL );
        VGA_timer_thread = CreateThread( NULL, 0, VGA_TimerThread, NULL, 0, NULL );
    }
    QueueUserAPC( set_timer_rate, VGA_timer_thread, (ULONG_PTR)Rate );
341 342
}

343 344 345 346 347
static BOOL VGA_IsTimerRunning(void)
{
    return VGA_timer_thread ? TRUE : FALSE;
}

348
static HANDLE VGA_AlphaConsole(void)
349 350 351 352 353 354
{
    /* 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);
}

355
static char*VGA_AlphaBuffer(void)
356
{
357
    return (char *)0xb8000;
358 359
}

360
/*** GRAPHICS MODE ***/
361

362 363 364 365 366
typedef struct {
  unsigned Xres, Yres, Depth;
  int ret;
} ModeSet;

367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392

/**********************************************************************
 *         VGA_SyncWindow
 *
 * Copy VGA window into framebuffer (if argument is TRUE) or
 * part of framebuffer into VGA window (if argument is FALSE).
 */
static void VGA_SyncWindow( BOOL target_is_fb )
{
    int size = VGA_WINDOW_SIZE;

    /* Window does not overlap framebuffer. */
    if (vga_fb_window >= vga_fb_size)
        return;

    /* Check if window overlaps framebuffer only partially. */
    if (vga_fb_size - vga_fb_window < VGA_WINDOW_SIZE)
        size = vga_fb_size - vga_fb_window;

    if (target_is_fb)
        memmove( vga_fb_data + vga_fb_window, VGA_WINDOW_START, size );
    else
        memmove( VGA_WINDOW_START, vga_fb_data + vga_fb_window, size );
}


393 394 395 396 397 398 399 400 401 402 403 404
static void WINAPI VGA_DoExit(ULONG_PTR arg)
{
    VGA_DeinstallTimer();
    IDirectDrawSurface_SetPalette(lpddsurf,NULL);
    IDirectDrawSurface_Release(lpddsurf);
    lpddsurf=NULL;
    IDirectDrawPalette_Release(lpddpal);
    lpddpal=NULL;
    IDirectDraw_Release(lpddraw);
    lpddraw=NULL;
}

405
static void WINAPI VGA_DoSetMode(ULONG_PTR arg)
406
{
407
    LRESULT	res;
408 409
    ModeSet *par = (ModeSet *)arg;
    par->ret=1;
410

411
    if (lpddraw) VGA_DoExit(0);
412
    if (!lpddraw) {
413 414 415
        if (!pDirectDrawCreate)
        {
            HMODULE hmod = LoadLibraryA( "ddraw.dll" );
416
            if (hmod) pDirectDrawCreate = (DirectDrawCreateProc)GetProcAddress( hmod, "DirectDrawCreate" );
417 418
	    if (!pDirectDrawCreate) {
		ERR("Can't lookup DirectDrawCreate from ddraw.dll.\n");
419
		return;
420
	    }
421
        }
422
        res = pDirectDrawCreate(NULL,&lpddraw,NULL);
423
        if (!lpddraw) {
424
            ERR("DirectDraw is not available (res = %lx)\n",res);
425
            return;
426
        }
427
        if (!vga_hwnd) {
428 429 430
            vga_hwnd = CreateWindowExA(0,"STATIC","WINEDOS VGA",
                                       WS_POPUP|WS_VISIBLE|SS_NOTIFY,0,0,
                                       par->Xres,par->Yres,0,0,0,NULL);
431 432 433 434 435 436 437 438 439 440 441
            if (!vga_hwnd) {
                ERR("Failed to create user window.\n");
                IDirectDraw_Release(lpddraw);
                lpddraw=NULL;
                return;
            }
        }
        else
            SetWindowPos(vga_hwnd,0,0,0,par->Xres,par->Yres,SWP_NOMOVE|SWP_NOZORDER);

        if ((res=IDirectDraw_SetCooperativeLevel(lpddraw,vga_hwnd,DDSCL_FULLSCREEN|DDSCL_EXCLUSIVE))) {
442 443 444
	    ERR("Could not set cooperative level to exclusive (%lx)\n",res);
	}

445 446
        if ((res=IDirectDraw_SetDisplayMode(lpddraw,par->Xres,par->Yres,par->Depth))) {
            ERR("DirectDraw does not support requested display mode (%dx%dx%d), res = %lx!\n",par->Xres,par->Yres,par->Depth,res);
447
            IDirectDraw_Release(lpddraw);
448
            lpddraw=NULL;
449
            return;
450
        }
451

452
        res=IDirectDraw_CreatePalette(lpddraw,DDPCAPS_8BIT,NULL,&lpddpal,NULL);
453
        if (res) {
454
	    ERR("Could not create palette (res = %lx)\n",res);
455 456 457 458 459 460 461 462
            IDirectDraw_Release(lpddraw);
            lpddraw=NULL;
            return;
        }
        if ((res=IDirectDrawPalette_SetEntries(lpddpal,0,0,256,vga_def_palette))) {
            ERR("Could not set default palette entries (res = %lx)\n", res);
        }

463 464
        memset(&sdesc,0,sizeof(sdesc));
        sdesc.dwSize=sizeof(sdesc);
465 466
	sdesc.dwFlags = DDSD_CAPS;
	sdesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
467
        if (IDirectDraw_CreateSurface(lpddraw,&sdesc,&lpddsurf,NULL)||(!lpddsurf)) {
468
            ERR("DirectDraw surface is not available\n");
469
            IDirectDraw_Release(lpddraw);
470
            lpddraw=NULL;
471
            return;
472
        }
473
        IDirectDrawSurface_SetPalette(lpddsurf,lpddpal);
474
        vga_retrace_vertical = vga_retrace_horizontal = FALSE;
475
        /* poll every 20ms (50fps should provide adequate responsiveness) */
476
        VGA_InstallTimer(20);
477
    }
478 479 480 481 482 483 484
    par->ret=0;
    return;
}

int VGA_SetMode(unsigned Xres,unsigned Yres,unsigned Depth)
{
    ModeSet par;
485 486 487 488 489 490 491 492 493 494 495 496 497
    int     newSize;

    vga_fb_width = Xres;
    vga_fb_height = Yres;
    vga_fb_depth = Depth;
    vga_fb_offset = 0;
    vga_fb_pitch = Xres * ((Depth + 7) / 8);

    newSize = Xres * Yres * ((Depth + 7) / 8);
    if(newSize < 256 * 1024)
      newSize = 256 * 1024;

    if(vga_fb_size < newSize) {
498
      HeapFree(GetProcessHeap(), 0, vga_fb_data);
499 500 501
      vga_fb_data = HeapAlloc(GetProcessHeap(), 0, newSize);
      vga_fb_size = newSize;
    }
502 503 504 505 506 507 508 509 510

    if(Xres >= 640 || Yres >= 480) {
      par.Xres = Xres;
      par.Yres = Yres;
    } else {
      par.Xres = 640;
      par.Yres = 480;
    }

511 512
    VGA_SetWindowStart((Depth < 8) ? -1 : 0);

513 514
    par.Depth = (Depth < 8) ? 8 : Depth;

515
    MZ_RunInThread(VGA_DoSetMode, (ULONG_PTR)&par);
516
    return par.ret;
517 518 519 520 521 522 523 524
}

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;
525
    if (Depth) *Depth=sdesc.ddpfPixelFormat.u1.dwRGBBitCount;
526 527 528
    return 0;
}

529
static void VGA_Exit(void)
530
{
531
    if (lpddraw) MZ_RunInThread(VGA_DoExit, 0);
532 533 534 535 536
}

void VGA_SetPalette(PALETTEENTRY*pal,int start,int len)
{
    if (!lpddraw) return;
537
    IDirectDrawPalette_SetEntries(lpddpal,0,start,len,pal);
538 539
}

540
/* set a single [char wide] color in 16 color mode. */
541 542 543 544 545 546 547 548 549 550
void VGA_SetColor16(int reg,int color)
{
	PALETTEENTRY *pal;

    if (!lpddraw) return;
	pal= &vga_def64_palette[color];
        IDirectDrawPalette_SetEntries(lpddpal,0,reg,1,pal);
	vga_16_palette[reg]=(char)color;
}

551 552 553 554 555
/* Get a single [char wide] color in 16 color mode. */
char VGA_GetColor16(int reg)
{

    if (!lpddraw) return 0;
556
	return vga_16_palette[reg];
557 558 559
}

/* set all 17 [char wide] colors at once in 16 color mode. */
560
void VGA_Set16Palette(char *Table)
561 562 563 564
{
	PALETTEENTRY *pal;
	int c;

565
    if (!lpddraw) return;         /* return if we're in text only mode */
Alexandre Julliard's avatar
Alexandre Julliard committed
566 567
    memcpy( Table, &vga_16_palette, 17 ); /* copy the entries into the table */

568 569 570 571 572 573 574 575
    for (c=0; c<17; c++) {                                /* 17 entries */
	pal= &vga_def64_palette[(int)vga_16_palette[c]];  /* get color  */
        IDirectDrawPalette_SetEntries(lpddpal,0,c,1,pal); /* set entry  */
	TRACE("Palette register %d set to %d\n",c,(int)vga_16_palette[c]);
   } /* end of the counting loop */
}

/* Get all 17 [ char wide ] colors at once in 16 color mode. */
576
void VGA_Get16Palette(char *Table)
577 578
{

579
    if (!lpddraw) return;         /* return if we're in text only mode */
Alexandre Julliard's avatar
Alexandre Julliard committed
580
    memcpy( &vga_16_palette, Table, 17 ); /* copy the entries into the table */
581 582
}

583 584 585 586 587 588 589 590 591 592 593 594
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;
    }
595
    IDirectDrawPalette_SetEntries(lpddpal,0,start,len,pal);
596 597
}

598
static LPSTR VGA_Lock(unsigned*Pitch,unsigned*Height,unsigned*Width,unsigned*Depth)
599 600 601
{
    if (!lpddraw) return NULL;
    if (!lpddsurf) return NULL;
602
    if (IDirectDrawSurface_Lock(lpddsurf,NULL,&sdesc,0,0)) {
603
        ERR("could not lock surface!\n");
604 605
        return NULL;
    }
606
    if (Pitch) *Pitch=sdesc.u1.lPitch;
607 608
    if (Height) *Height=sdesc.dwHeight;
    if (Width) *Width=sdesc.dwWidth;
609 610
    if (Depth) *Depth=sdesc.ddpfPixelFormat.u1.dwRGBBitCount;
    return sdesc.lpSurface;
611 612
}

613
static void VGA_Unlock(void)
614
{
615
    IDirectDrawSurface_Unlock(lpddsurf,sdesc.lpSurface);
616 617
}

618 619 620 621 622 623 624 625 626 627
/*
 * Set start of 64k window at 0xa0000 in bytes.
 * If value is -1, initialize color plane support.
 * If value is >= 0, window contains direct copy of framebuffer.
 */
void VGA_SetWindowStart(int start)
{
    if(start == vga_fb_window)
        return;

628 629
    EnterCriticalSection(&vga_lock);

630 631
    if(vga_fb_window == -1)
        FIXME("Remove VGA memory emulation.\n");
632 633
    else
        VGA_SyncWindow( TRUE );
634 635 636 637 638

    vga_fb_window = start;

    if(vga_fb_window == -1)
        FIXME("Install VGA memory emulation.\n");
639 640
    else
        VGA_SyncWindow( FALSE );
641 642

    LeaveCriticalSection(&vga_lock);
643 644 645 646 647 648
}

/*
 * Get start of 64k window at 0xa0000 in bytes.
 * Value is -1 in color plane modes.
 */
649
int VGA_GetWindowStart(void)
650 651 652 653
{
    return vga_fb_window;
}

654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685

/**********************************************************************
 *         VGA_DoShowMouse
 *
 * Callback for VGA_ShowMouse.
 */
static WINAPI void VGA_DoShowMouse( ULONG_PTR show )
{
    INT rv;

    do
    {
        rv = ShowCursor( show );
    }
    while( show ? (rv < 0) : (rv >= 0) );
}


/**********************************************************************
 *         VGA_ShowMouse
 *
 * If argument is TRUE, unconditionally show mouse cursor.
 * If argument is FALSE, unconditionally hide mouse cursor.
 * This only works in graphics mode.
 */
void VGA_ShowMouse( BOOL show )
{
    if (lpddraw)
        MZ_RunInThread( VGA_DoShowMouse, (ULONG_PTR)show );
}


686 687
/*** TEXT MODE ***/

688 689
/* prepare the text mode video memory copy that is used to only
 * update the video memory line that did get updated. */
690
static void VGA_PrepareVideoMemCopy(unsigned Xres, unsigned Yres)
691 692
{
    char *p, *p2;
693
    unsigned int i;
694

695 696 697
    /*
     * Allocate space for char + attr.
     */
698

699 700 701 702 703 704
    if (vga_text_old)
        vga_text_old = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
                                vga_text_old, Xres * Yres * 2 );
    else
        vga_text_old = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
                                 Xres * Yres * 2 );
705
    p = VGA_AlphaBuffer();
706
    p2 = vga_text_old;
707 708 709 710 711

    /* make sure the video mem copy contains the exact opposite of our
     * actual text mode memory area to make sure the screen
     * does get updated fully initially */
    for (i=0; i < Xres*Yres*2; i++)
712
	*p2++ = *p++ ^ 0xff; /* XOR it */
713 714
}

715 716 717 718 719 720
/**********************************************************************
 *         VGA_SetAlphaMode
 *
 * Set VGA emulation to text mode.
 */
void VGA_SetAlphaMode(unsigned Xres,unsigned Yres)
721
{
722 723 724 725 726 727
    VGA_Exit();
    VGA_DeinstallTimer();
    
    VGA_PrepareVideoMemCopy(Xres, Yres);
    vga_text_width = Xres;
    vga_text_height = Yres;
728

729 730
    if (vga_text_x >= vga_text_width || vga_text_y >= vga_text_height)
        VGA_SetCursorPos(0,0);
731

732 733 734 735 736
    if(vga_text_console) {
        COORD size;
        size.X = Xres;
        size.Y = Yres;
        SetConsoleScreenBufferSize( VGA_AlphaConsole(), size );
737

738 739 740 741
        /* poll every 30ms (33fps should provide adequate responsiveness) */
        VGA_InstallTimer(30);
    }
}
742

743 744 745 746 747 748 749 750 751
/**********************************************************************
 *         VGA_InitAlphaMode
 *
 * Initialize VGA text mode handling and return default text mode.
 * This function does not set VGA emulation to text mode.
 */
void VGA_InitAlphaMode(unsigned*Xres,unsigned*Yres)
{
    CONSOLE_SCREEN_BUFFER_INFO info;
752

753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
    if(GetConsoleScreenBufferInfo( VGA_AlphaConsole(), &info ))
    {
        vga_text_console = TRUE;
        vga_text_x = info.dwCursorPosition.X;
        vga_text_y = info.dwCursorPosition.Y;
        vga_text_attr = info.wAttributes;
        *Xres = info.dwSize.X;
        *Yres = info.dwSize.Y;
    } 
    else
    {
        vga_text_console = FALSE;
        vga_text_x = 0;
        vga_text_y = 0;
        vga_text_attr = 0x0f;
        *Xres = 80;
        *Yres = 25;
    }
771 772
}

773 774 775 776 777 778
/**********************************************************************
 *         VGA_GetAlphaMode
 *
 * Get current text mode. Returns TRUE and sets resolution if
 * any VGA text mode has been initialized.
 */
779
BOOL VGA_GetAlphaMode(unsigned*Xres,unsigned*Yres)
780
{
781 782 783
    if (vga_text_width != 0 && vga_text_height != 0) {
        *Xres = vga_text_width;
        *Yres = vga_text_height;
784
        return TRUE;
785 786
    } else
        return FALSE;
787 788
}

789 790 791 792 793 794 795 796
void VGA_SetCursorShape(unsigned char start_options, unsigned char end)
{
    CONSOLE_CURSOR_INFO cci;

    /* standard cursor settings:
     * 0x0607 == CGA, 0x0b0c == monochrome, 0x0d0e == EGA/VGA */

    /* calculate percentage from bottom - assuming VGA (bottom 0x0e) */
797
    cci.dwSize = ((end & 0x1f) - (start_options & 0x1f))/0x0e * 100;
798 799 800 801 802 803
    if (!cci.dwSize) cci.dwSize++; /* NULL cursor would make SCCI() fail ! */
    cci.bVisible = ((start_options & 0x60) != 0x20); /* invisible ? */

    SetConsoleCursorInfo(VGA_AlphaConsole(),&cci);
}

804 805
void VGA_SetCursorPos(unsigned X,unsigned Y)
{
806 807 808
    vga_text_x = X;
    vga_text_y = Y;
}
809

810 811 812 813
void VGA_GetCursorPos(unsigned*X,unsigned*Y)
{
    if (X) *X = vga_text_x;
    if (Y) *Y = vga_text_y;
814 815
}

816
static void VGA_PutCharAt(unsigned x, unsigned y, BYTE ascii, int attr)
817
{
818
    char *dat = VGA_AlphaBuffer() + ((vga_text_width * y + x) * 2);
819
    dat[0] = ascii;
820 821
    if (attr>=0)
        dat[1] = attr;
822 823
}

824 825
void VGA_WriteChars(unsigned X,unsigned Y,unsigned ch,int attr,int count)
{
826 827
    EnterCriticalSection(&vga_lock);

828 829
    while (count--) 
        VGA_PutCharAt(X + count, Y, ch, attr);
830 831 832 833 834 835

    LeaveCriticalSection(&vga_lock);
}

void VGA_PutChar(BYTE ascii)
{
836 837
    DWORD w;

838 839 840 841
    EnterCriticalSection(&vga_lock);

    switch(ascii) {
    case '\b':
842
        if (vga_text_x)
843
        {
844
            vga_text_x--;
845 846
            VGA_PutCharAt(vga_text_x, vga_text_y, ' ', 0);
        }
847
        break;
848 849

    case '\t':
850 851
        vga_text_x += ((vga_text_x + 8) & ~7) - vga_text_x;
        break;
852 853

    case '\n':
854 855 856
        vga_text_y++;
        vga_text_x = 0;
        break;
857

858 859 860 861
    case '\a':
        break;

    case '\r':
862 863
        vga_text_x = 0;
        break;
864 865

    default:
866 867
        VGA_PutCharAt(vga_text_x, vga_text_y, ascii, vga_text_attr);
        vga_text_x++;
868 869
    }

870 871 872 873 874 875 876 877 878 879 880 881 882
    if (vga_text_x >= vga_text_width)
    {
        vga_text_x = 0;
        vga_text_y++;
    }

    if (vga_text_y >= vga_text_height)
    {
        vga_text_y = vga_text_height - 1;
        VGA_ScrollUpText( 0, 0, 
                          vga_text_height - 1, vga_text_width - 1, 
                          1, vga_text_attr );
    }
883 884

    /*
885
     * If we don't have a console, write directly to standard output.
886
     */
887
    if(!vga_text_console)
888
        WriteFile(VGA_AlphaConsole(), &ascii, 1, &w, NULL);
889 890 891 892 893 894 895

    LeaveCriticalSection(&vga_lock);
}

void VGA_SetTextAttribute(BYTE attr)
{
    vga_text_attr = attr;
896 897
}

898
void VGA_ClearText(unsigned row1, unsigned col1,
899 900
                   unsigned row2, unsigned col2,
                   BYTE attr)
901
{
902
    unsigned x, y;
903

904
    EnterCriticalSection(&vga_lock);
905

906
    for(y=row1; y<=row2; y++)
907
        for(x=col1; x<=col2; x++)
908
            VGA_PutCharAt(x, y, 0x20, attr);
909 910 911 912

    LeaveCriticalSection(&vga_lock);
}

913 914 915
void VGA_ScrollUpText(unsigned row1,  unsigned col1,
                      unsigned row2,  unsigned col2,
                      unsigned lines, BYTE attr)
916
{
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936
    char    *buffer = VGA_AlphaBuffer();
    unsigned y;

    EnterCriticalSection(&vga_lock);

    /*
     * Scroll buffer.
     */
    for (y = row1; y <= row2 - lines; y++)
        memmove( buffer + col1 + y * vga_text_width * 2,
                 buffer + col1 + (y + lines) * vga_text_width * 2,
                 (col2 - col1 + 1) * 2 );

    /*
     * Fill exposed lines.
     */
    for (y = max(row1, row2 - lines + 1); y <= row2; y++)
        VGA_WriteChars( col1, y, ' ', attr, col2 - col1 + 1 );

    LeaveCriticalSection(&vga_lock);
937 938
}

939 940 941
void VGA_ScrollDownText(unsigned row1,  unsigned col1,
                        unsigned row2,  unsigned col2,
                        unsigned lines, BYTE attr)
942
{
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
    char    *buffer = VGA_AlphaBuffer();
    unsigned y;

    EnterCriticalSection(&vga_lock);

    /*
     * Scroll buffer.
     */
    for (y = row2; y >= row1 + lines; y--)
        memmove( buffer + col1 + y * vga_text_width * 2,
                 buffer + col1 + (y - lines) * vga_text_width * 2,
                 (col2 - col1 + 1) * 2 );

    /*
     * Fill exposed lines.
     */
    for (y = row1; y <= min(row1 + lines - 1, row2); y++)
        VGA_WriteChars( col1, y, ' ', attr, col2 - col1 + 1 );

    LeaveCriticalSection(&vga_lock);
963 964
}

965
void VGA_GetCharacterAtCursor(BYTE *ascii, BYTE *attr)
966 967 968
{
    char *dat;

969
    dat = VGA_AlphaBuffer() + ((vga_text_width * vga_text_y + vga_text_x) * 2);
970 971 972 973 974 975

    *ascii = dat[0];
    *attr = dat[1];
}


976
/*** CONTROL ***/
977

978 979
/* FIXME: optimize by doing this only if the data has actually changed
 *        (in a way similar to DIBSection, perhaps) */
980 981 982 983
static void VGA_Poll_Graphics(void)
{
  unsigned int Pitch, Height, Width, X, Y;
  char *surf;
984
  char *dat = vga_fb_data + vga_fb_offset;
985
  int   bpp = (vga_fb_depth + 7) / 8;
986 987 988 989

  surf = VGA_Lock(&Pitch,&Height,&Width,NULL);
  if (!surf) return;

990 991 992
  /*
   * Synchronize framebuffer contents.
   */
993 994
  if (vga_fb_window != -1)
      VGA_SyncWindow( TRUE );
995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006

  /*
   * Double VGA framebuffer (320x200 -> 640x400), if needed.
   */
  if(Height >= 2 * vga_fb_height && Width >= 2 * vga_fb_width && bpp == 1)
    for (Y=0; Y<vga_fb_height; Y++,surf+=Pitch*2,dat+=vga_fb_pitch)
      for (X=0; X<vga_fb_width; X++) {
       BYTE value = dat[X];
       surf[X*2] = value;
       surf[X*2+1] = value;
       surf[X*2+Pitch] = value;
       surf[X*2+Pitch+1] = value;
1007
      }
1008 1009 1010
  else
    for (Y=0; Y<vga_fb_height; Y++,surf+=Pitch,dat+=vga_fb_pitch)
      memcpy(surf, dat, vga_fb_width * bpp);
1011 1012 1013 1014

  VGA_Unlock();
}

1015
static void VGA_Poll_Text(void)
1016
{
1017
    char *dat, *old, *p_line;
1018
    unsigned int X, Y;
1019
    CHAR_INFO ch[256]; /* that should suffice for the largest text width */
1020 1021 1022
    COORD siz, off;
    SMALL_RECT dest;
    HANDLE con = VGA_AlphaConsole();
1023 1024 1025 1026 1027 1028
    BOOL linechanged = FALSE; /* video memory area differs from stored copy? */

    /* Synchronize cursor position. */
    off.X = vga_text_x;
    off.Y = vga_text_y;
    SetConsoleCursorPosition(con,off);
1029 1030

    dat = VGA_AlphaBuffer();
1031 1032
    old = vga_text_old; /* pointer to stored video mem copy */
    siz.X = vga_text_width; siz.Y = 1;
1033
    off.X = 0; off.Y = 0;
1034

1035
    /* copy from virtual VGA frame buffer to console */
1036 1037
    for (Y=0; Y<vga_text_height; Y++) {
	linechanged = memcmp(dat, old, vga_text_width*2);
1038 1039 1040 1041
	if (linechanged)
	{
	    /*TRACE("line %d changed\n", Y);*/
	    p_line = dat;
1042
            for (X=0; X<vga_text_width; X++) {
1043 1044 1045 1046 1047 1048 1049
                ch[X].Char.AsciiChar = *p_line++;
                /* WriteConsoleOutputA doesn't like "dead" chars */
                if (ch[X].Char.AsciiChar == '\0')
                    ch[X].Char.AsciiChar = ' ';
                ch[X].Attributes = *p_line++;
            }
            dest.Top=Y; dest.Bottom=Y;
1050
            dest.Left=0; dest.Right=vga_text_width+1;
1051
            WriteConsoleOutputA(con, ch, siz, off, &dest);
1052
	    memcpy(old, dat, vga_text_width*2);
1053 1054
	}
	/* advance to next text line */
1055 1056
	dat += vga_text_width*2;
	old += vga_text_width*2;
1057 1058
    }
}
1059

1060 1061
static void CALLBACK VGA_Poll( LPVOID arg, DWORD low, DWORD high )
{
1062
    EnterCriticalSection(&vga_lock);
1063

1064
    if (lpddraw)
1065
        VGA_Poll_Graphics();
1066
    else
1067 1068
        VGA_Poll_Text();

1069 1070 1071 1072
    /*
     * Fake start of retrace.
     */
    vga_retrace_vertical = TRUE;
1073

1074
    LeaveCriticalSection(&vga_lock);
1075 1076 1077 1078 1079 1080 1081 1082
}

static BYTE palreg,palcnt;
static PALETTEENTRY paldat;

void VGA_ioport_out( WORD port, BYTE val )
{
    switch (port) {
1083 1084 1085 1086
        case 0x3c0:
           if (vga_address_3c0)
               vga_index_3c0 = val;
           else
1087 1088
               FIXME("Unsupported index, register 0x3c0: 0x%02x (value 0x%02x)\n",
                     vga_index_3c0, val);
1089 1090 1091 1092 1093 1094
           vga_address_3c0 = !vga_address_3c0;
           break;
        case 0x3c4:
           vga_index_3c4 = val;
           break;
        case 0x3c5:
1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
          switch(vga_index_3c4) {
               case 0x04: /* Sequencer: Memory Mode Register */
                  if(vga_fb_depth == 8)
                      VGA_SetWindowStart((val & 8) ? 0 : -1);
                  else
                      FIXME("Memory Mode Register not supported in this mode.\n");
               break;
               default:
                  FIXME("Unsupported index, register 0x3c4: 0x%02x (value 0x%02x)\n",
                        vga_index_3c4, val);
           }
           break;
1107 1108 1109 1110 1111
        case 0x3c8:
            palreg=val; palcnt=0; break;
        case 0x3c9:
            ((BYTE*)&paldat)[palcnt++]=val << 2;
            if (palcnt==3) {
1112 1113
                VGA_SetPalette(&paldat,palreg++,1);
                palcnt=0;
1114
            }
1115
            break;
1116 1117 1118 1119
        case 0x3ce:
            vga_index_3ce = val;
           break;
        case 0x3cf:
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129
           FIXME("Unsupported index, register 0x3ce: 0x%02x (value 0x%02x)\n",
                 vga_index_3ce, val);
           break;
        case 0x3d4:
           vga_index_3d4 = val;
           break;
        case 0x3d5:
           FIXME("Unsupported index, register 0x3d4: 0x%02x (value 0x%02x)\n",
                 vga_index_3d4, val);
           break;
1130 1131
        default:
            FIXME("Unsupported VGA register: 0x%04x (value 0x%02x)\n", port, val);
1132 1133
    }
}
1134 1135 1136 1137 1138 1139

BYTE VGA_ioport_in( WORD port )
{
    BYTE ret;

    switch (port) {
1140
        case 0x3c1:
1141 1142 1143 1144
           FIXME("Unsupported index, register 0x3c0: 0x%02x\n",
                 vga_index_3c0);
           return 0xff;
        case 0x3c5:
1145 1146 1147 1148 1149 1150 1151 1152
           switch(vga_index_3c4) {
               case 0x04: /* Sequencer: Memory Mode Register */
                    return (VGA_GetWindowStart() == -1) ? 0xf7 : 0xff;
               default:
                   FIXME("Unsupported index, register 0x3c4: 0x%02x\n",
                         vga_index_3c4);
                   return 0xff;
           }
1153
        case 0x3cf:
1154 1155
           FIXME("Unsupported index, register 0x3ce: 0x%02x\n",
                 vga_index_3ce);
1156
           return 0xff;
1157 1158 1159
        case 0x3d5:
           FIXME("Unsupported index, register 0x3d4: 0x%02x\n",
                 vga_index_3d4);
1160
           return 0xff;
1161

1162
        case 0x3da:
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191
            /*
             * Read from this register resets register 0x3c0 address flip-flop.
             */
            vga_address_3c0 = TRUE;

            /*
             * Read from this register returns following bits:
             *   xxxx1xxx = Vertical retrace in progress if set.
             *   xxxxx1xx = Light pen switched on.
             *   xxxxxx1x = Light pen trigger set.
             *   xxxxxxx1 = Either vertical or horizontal retrace 
             *              in progress if set.
             */
            ret = 0;
            if (vga_retrace_vertical)
                ret |= 9;
            if (vga_retrace_horizontal)
                ret |= 3;
            
            /*
             * If VGA mode has been set, vertical retrace is
             * turned on once a frame and cleared after each read.
             * This might cause applications that synchronize with
             * vertical retrace to actually skip one frame but that
             * is probably not a problem.
             * 
             * If no VGA mode has been set, vertical retrace is faked
             * by toggling the value after every read.
             */
1192
            if (VGA_IsTimerRunning())
1193
                vga_retrace_vertical = FALSE;
1194
            else
1195 1196 1197 1198 1199 1200
                vga_retrace_vertical = !vga_retrace_vertical;

            /*
             * Toggle horizontal retrace.
             */
            vga_retrace_horizontal = !vga_retrace_horizontal;
1201
            break;
1202

1203 1204
        default:
            ret=0xff;
1205
            FIXME("Unsupported VGA register: 0x%04x\n", port);
1206 1207 1208
    }
    return ret;
}
1209 1210 1211 1212 1213 1214

void VGA_Clean(void)
{
    VGA_Exit();
    VGA_DeinstallTimer();
}