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 18
 * 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
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 "miscemu.h"
32
#include "dosexe.h"
33 34
#include "vga.h"
#include "ddraw.h"
35
#include "wine/debug.h"
36

37
WINE_DEFAULT_DEBUG_CHANNEL(ddraw);
38

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

static BOOL vga_retrace_vertical;
static BOOL vga_retrace_horizontal;
46

47 48 49 50 51 52 53 54 55 56
/*
 * 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)

57 58 59 60 61 62 63 64 65 66 67 68 69
/*
 * 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.
70
 * vga_fb_pitch: How many bytes to add to pointer in order to move
71 72 73 74 75 76 77 78 79 80
 *               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.
81 82 83 84 85
 * 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).
86 87 88 89 90 91 92
 */
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;
93
static char *vga_fb_data = 0;
94
static int   vga_fb_window = 0;
95

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
/*
 * 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;
116

117 118
/*
 * VGA controller ports 0x3c0, 0x3c4, 0x3ce and 0x3d4 are
119 120
 * indexed registers. These ports are used to select VGA controller
 * subregister that can be written to or read from using ports 0x3c1,
121 122 123 124 125 126 127 128 129 130 131
 * 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;
132
static BYTE vga_index_3d4;
133 134
static BOOL vga_address_3c0 = TRUE;

135 136 137 138 139 140 141 142 143 144 145
/*
 * 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.
 */
146 147 148 149 150 151 152 153
static CRITICAL_SECTION vga_lock;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
    0, 0, &vga_lock,
    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
      0, 0, { 0, (DWORD)(__FILE__ ": vga_lock") }
};
static CRITICAL_SECTION vga_lock = { &critsect_debug, -1, 0, 0, 0, 0 };
154

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

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

160
static HWND vga_hwnd = NULL;
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 188
/*
 * 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      */
};

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
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 */
};

210 211
/*
 *   This palette is the dos default, converted from 18 bit color to 24.
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 283
 *      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  */
};

284 285 286 287 288 289 290 291
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;

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

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

301 302
static void VGA_DeinstallTimer(void)
{
303 304
    if (VGA_timer_thread)
    {
305 306 307 308 309 310 311 312 313 314 315 316
        /*
         * 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);

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

        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 );
331 332 333 334 335
    }
}

static void VGA_InstallTimer(unsigned Rate)
{
336 337 338 339 340 341
    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 );
342 343
}

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

349 350 351 352 353 354 355
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);
}

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

361
/*** GRAPHICS MODE ***/
362

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

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 393

/**********************************************************************
 *         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 );
}


394 395 396 397 398 399 400 401 402 403 404 405
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;
}

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

412
    if (lpddraw) VGA_DoExit(0);
413
    if (!lpddraw) {
414 415 416
        if (!pDirectDrawCreate)
        {
            HMODULE hmod = LoadLibraryA( "ddraw.dll" );
417
            if (hmod) pDirectDrawCreate = (DirectDrawCreateProc)GetProcAddress( hmod, "DirectDrawCreate" );
418 419
	    if (!pDirectDrawCreate) {
		ERR("Can't lookup DirectDrawCreate from ddraw.dll.\n");
420
		return;
421
	    }
422
        }
423
        res = pDirectDrawCreate(NULL,&lpddraw,NULL);
424
        if (!lpddraw) {
425
            ERR("DirectDraw is not available (res = %lx)\n",res);
426
            return;
427
        }
428
        if (!vga_hwnd) {
429 430 431
            vga_hwnd = CreateWindowExA(0,"STATIC","WINEDOS VGA",
                                       WS_POPUP|WS_VISIBLE|SS_NOTIFY,0,0,
                                       par->Xres,par->Yres,0,0,0,NULL);
432 433 434 435 436 437 438 439 440 441 442
            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))) {
443 444 445
	    ERR("Could not set cooperative level to exclusive (%lx)\n",res);
	}

446 447
        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);
448
            IDirectDraw_Release(lpddraw);
449
            lpddraw=NULL;
450
            return;
451
        }
452

453
        res=IDirectDraw_CreatePalette(lpddraw,DDPCAPS_8BIT,NULL,&lpddpal,NULL);
454
        if (res) {
455
	    ERR("Could not create palette (res = %lx)\n",res);
456 457 458 459 460 461 462 463
            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);
        }

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

int VGA_SetMode(unsigned Xres,unsigned Yres,unsigned Depth)
{
    ModeSet par;
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
    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) {
      if(vga_fb_data)
        HeapFree(GetProcessHeap(), 0, vga_fb_data);
      vga_fb_data = HeapAlloc(GetProcessHeap(), 0, newSize);
      vga_fb_size = newSize;
    }
504 505 506 507 508 509 510 511 512

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

513 514
    VGA_SetWindowStart((Depth < 8) ? -1 : 0);

515 516
    par.Depth = (Depth < 8) ? 8 : Depth;

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

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

void VGA_Exit(void)
{
533
    if (lpddraw) MZ_RunInThread(VGA_DoExit, 0);
534 535 536 537 538
}

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

542
/* set a single [char wide] color in 16 color mode. */
543 544 545 546 547 548 549 550 551 552
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;
}

553 554 555 556 557 558 559 560 561
/* Get a single [char wide] color in 16 color mode. */
char VGA_GetColor16(int reg)
{

    if (!lpddraw) return 0;
	return (char)vga_16_palette[reg];
}

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

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

570 571 572 573 574 575 576 577
    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. */
578
void VGA_Get16Palette(char *Table)
579 580
{

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

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

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

void VGA_Unlock(void)
{
617
    IDirectDrawSurface_Unlock(lpddsurf,sdesc.lpSurface);
618 619
}

620 621 622 623 624 625 626 627 628 629
/*
 * 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;

630 631
    EnterCriticalSection(&vga_lock);

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

    vga_fb_window = start;

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

    LeaveCriticalSection(&vga_lock);
645 646 647 648 649 650 651 652 653 654 655
}

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

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 686 687

/**********************************************************************
 *         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 );
}


688 689
/*** TEXT MODE ***/

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

697 698 699
    /*
     * Allocate space for char + attr.
     */
700

701 702 703 704 705 706
    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 );
707
    p = VGA_AlphaBuffer();
708
    p2 = vga_text_old;
709 710 711 712 713

    /* 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++)
714
	*p2++ = *p++ ^ 0xff; /* XOR it */
715 716
}

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

731 732
    if (vga_text_x >= vga_text_width || vga_text_y >= vga_text_height)
        VGA_SetCursorPos(0,0);
733

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

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

745 746 747 748 749 750 751 752 753
/**********************************************************************
 *         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;
754

755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
    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;
    }
773 774
}

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

791 792 793 794 795 796 797 798
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) */
799
    cci.dwSize = ((end & 0x1f) - (start_options & 0x1f))/0x0e * 100;
800 801 802 803 804 805
    if (!cci.dwSize) cci.dwSize++; /* NULL cursor would make SCCI() fail ! */
    cci.bVisible = ((start_options & 0x60) != 0x20); /* invisible ? */

    SetConsoleCursorInfo(VGA_AlphaConsole(),&cci);
}

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

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

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

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

830 831
    while (count--) 
        VGA_PutCharAt(X + count, Y, ch, attr);
832 833 834 835 836 837 838 839 840 841

    LeaveCriticalSection(&vga_lock);
}

void VGA_PutChar(BYTE ascii)
{
    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 888
    if(!vga_text_console)
        WriteFile(VGA_AlphaConsole(), &ascii, 1, NULL, 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();
}