Framework for the doppler effect.
[wine] / dlls / winedos / vga.c
1 /*
2  * VGA hardware emulation
3  *
4  * Copyright 1998 Ove Kåven (with some help from Marcus Meissner)
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <string.h>
22
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "wincon.h"
29 #include "miscemu.h"
30 #include "dosexe.h"
31 #include "vga.h"
32 #include "ddraw.h"
33 #include "wine/debug.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(ddraw);
36
37 static IDirectDraw *lpddraw = NULL;
38 static IDirectDrawSurface *lpddsurf;
39 static IDirectDrawPalette *lpddpal;
40 static DDSURFACEDESC sdesc;
41
42 static BOOL vga_retrace_vertical;
43 static BOOL vga_retrace_horizontal;
44
45 /*
46  * VGA controller memory is emulated using linear framebuffer.
47  * This frambuffer also acts as an interface
48  * between VGA controller emulation and DirectDraw.
49  *
50  * vga_fb_width: Display width in pixels. Can be modified when
51  *               display mode is changed.
52  * vga_fb_height: Display height in pixels. Can be modified when
53  *                display mode is changed.
54  * vga_fb_depth: Number of bits used to store single pixel color information.
55  *               Each pixel uses (vga_fb_depth+7)/8 bytes because
56  *               1-16 color modes are mapped to 256 color mode.
57  *               Can be modified when display mode is changed.
58  * vga_fb_pitch: How many bytes to add to pointer in order to move
59  *               from one row to another. This is fixed in VGA modes,
60  *               but can be modified in SVGA modes.
61  * vga_fb_offset: Offset added to framebuffer start address in order
62  *                to find the display origin. Programs use this to do
63  *                double buffering and to scroll display. The value can
64  *                be modified in VGA and SVGA modes.
65  * vga_fb_size: How many bytes are allocated to framebuffer.
66  *              VGA framebuffers are always larger than display size and
67  *              SVGA framebuffers may also be.
68  * vga_fb_data: Pointer to framebuffer start.
69  * vga_fb_window: Offset of 64k window 0xa0000 in bytes from framebuffer start.
70  *                This value is >= 0, if mode uses linear framebuffer and
71  *                -1, if mode uses color planes. This value is fixed
72  *                in all modes except 0x13 (256 color VGA) where
73  *                0 means normal mode and -1 means Mode-X (unchained mode).
74  */
75 static int   vga_fb_width;
76 static int   vga_fb_height;
77 static int   vga_fb_depth;
78 static int   vga_fb_pitch;
79 static int   vga_fb_offset;
80 static int   vga_fb_size = 0;
81 static char *vga_fb_data = 0;
82 static int   vga_fb_window = 0;
83
84 /*
85  * VGA text mode data.
86  *
87  * vga_text_attr: Current active attribute.
88  * vga_text_old: Last data sent to console. 
89  *               This is used to optimize console updates.
90  * vga_text_width:  Width of the text display in characters.
91  * vga_text_height: Height of the text display in characters.
92  * vga_text_x: Current cursor X-position. Starts from zero.
93  * vga_text_y: Current cursor Y-position. Starts from zero.
94  * vga_text_console: TRUE if stdout is console, 
95  *                   FALSE if it is regular file.
96  */
97 static BYTE  vga_text_attr;
98 static char *vga_text_old = NULL;
99 static BYTE  vga_text_width;
100 static BYTE  vga_text_height;
101 static BYTE  vga_text_x;
102 static BYTE  vga_text_y;
103 static BOOL  vga_text_console;
104
105 /*
106  * VGA controller ports 0x3c0, 0x3c4, 0x3ce and 0x3d4 are
107  * indexed registers. These ports are used to select VGA controller
108  * subregister that can be written to or read from using ports 0x3c1,
109  * 0x3c5, 0x3cf or 0x3d5. Selected subregister indexes are
110  * stored in variables vga_index_*.
111  *
112  * Port 0x3c0 is special because it is both index and
113  * data-write register. Flip-flop vga_address_3c0 tells whether
114  * the port acts currently as an address register. Reading from port
115  * 0x3da resets the flip-flop to address mode.
116  */
117 static BYTE vga_index_3c0;
118 static BYTE vga_index_3c4;
119 static BYTE vga_index_3ce;
120 static BYTE vga_index_3d4;
121 static BOOL vga_address_3c0 = TRUE;
122
123 /*
124  * This mutex is used to protect VGA state during asynchronous
125  * screen updates (see VGA_Poll). It makes sure that VGA state changes
126  * are atomic and the user interface is protected from flicker and
127  * corruption.
128  *
129  * The mutex actually serializes VGA operations and the screen update. 
130  * Which means that whenever VGA_Poll occurs, application stalls if it 
131  * tries to modify VGA state. This is not how real VGA adapters work,
132  * but it makes timing and correctness issues much easier to deal with.
133  */
134 static CRITICAL_SECTION vga_lock = CRITICAL_SECTION_INIT("VGA");
135
136 typedef HRESULT (WINAPI *DirectDrawCreateProc)(LPGUID,LPDIRECTDRAW *,LPUNKNOWN);
137 static DirectDrawCreateProc pDirectDrawCreate;
138
139 static void CALLBACK VGA_Poll( LPVOID arg, DWORD low, DWORD high );
140
141 static HWND vga_hwnd = NULL;
142
143 /*
144  * For simplicity, I'm creating a second palette.
145  * 16 color accesses will use these pointers and insert
146  * entries from the 64-color palette into the default
147  * palette.   --Robert 'Admiral' Coeyman
148  */
149
150 static char vga_16_palette[17]={
151   0x00,  /* 0 - Black         */
152   0x01,  /* 1 - Blue          */
153   0x02,  /* 2 - Green         */
154   0x03,  /* 3 - Cyan          */
155   0x04,  /* 4 - Red           */
156   0x05,  /* 5 - Magenta       */
157   0x14,  /* 6 - Brown         */
158   0x07,  /* 7 - Light gray    */
159   0x38,  /* 8 - Dark gray     */
160   0x39,  /* 9 - Light blue    */
161   0x3a,  /* A - Light green   */
162   0x3b,  /* B - Light cyan    */
163   0x3c,  /* C - Light red     */
164   0x3d,  /* D - Light magenta */
165   0x3e,  /* E - Yellow        */
166   0x3f,  /* F - White         */
167   0x00   /* Border Color      */
168 };
169
170 static PALETTEENTRY vga_def_palette[256]={
171 /* red  green  blue */
172   {0x00, 0x00, 0x00}, /* 0 - Black */
173   {0x00, 0x00, 0x80}, /* 1 - Blue */
174   {0x00, 0x80, 0x00}, /* 2 - Green */
175   {0x00, 0x80, 0x80}, /* 3 - Cyan */
176   {0x80, 0x00, 0x00}, /* 4 - Red */
177   {0x80, 0x00, 0x80}, /* 5 - Magenta */
178   {0x80, 0x80, 0x00}, /* 6 - Brown */
179   {0xC0, 0xC0, 0xC0}, /* 7 - Light gray */
180   {0x80, 0x80, 0x80}, /* 8 - Dark gray */
181   {0x00, 0x00, 0xFF}, /* 9 - Light blue */
182   {0x00, 0xFF, 0x00}, /* A - Light green */
183   {0x00, 0xFF, 0xFF}, /* B - Light cyan */
184   {0xFF, 0x00, 0x00}, /* C - Light red */
185   {0xFF, 0x00, 0xFF}, /* D - Light magenta */
186   {0xFF, 0xFF, 0x00}, /* E - Yellow */
187   {0xFF, 0xFF, 0xFF}, /* F - White */
188   {0,0,0} /* FIXME: a series of continuous rainbow hues should follow */
189 };
190
191 /*
192  *   This palette is the dos default, converted from 18 bit color to 24.
193  *      It contains only 64 entries of colors--all others are zeros.
194  *          --Robert 'Admiral' Coeyman
195  */
196 static PALETTEENTRY vga_def64_palette[256]={
197 /* red  green  blue */
198   {0x00, 0x00, 0x00}, /* 0x00      Black      */
199   {0x00, 0x00, 0xaa}, /* 0x01      Blue       */
200   {0x00, 0xaa, 0x00}, /* 0x02      Green      */
201   {0x00, 0xaa, 0xaa}, /* 0x03      Cyan       */
202   {0xaa, 0x00, 0x00}, /* 0x04      Red        */
203   {0xaa, 0x00, 0xaa}, /* 0x05      Magenta    */
204   {0xaa, 0xaa, 0x00}, /* 0x06      */
205   {0xaa, 0xaa, 0xaa}, /* 0x07      Light Gray */
206   {0x00, 0x00, 0x55}, /* 0x08      */
207   {0x00, 0x00, 0xff}, /* 0x09      */
208   {0x00, 0xaa, 0x55}, /* 0x0a      */
209   {0x00, 0xaa, 0xff}, /* 0x0b      */
210   {0xaa, 0x00, 0x55}, /* 0x0c      */
211   {0xaa, 0x00, 0xff}, /* 0x0d      */
212   {0xaa, 0xaa, 0x55}, /* 0x0e      */
213   {0xaa, 0xaa, 0xff}, /* 0x0f      */
214   {0x00, 0x55, 0x00}, /* 0x10      */
215   {0x00, 0x55, 0xaa}, /* 0x11      */
216   {0x00, 0xff, 0x00}, /* 0x12      */
217   {0x00, 0xff, 0xaa}, /* 0x13      */
218   {0xaa, 0x55, 0x00}, /* 0x14      Brown      */
219   {0xaa, 0x55, 0xaa}, /* 0x15      */
220   {0xaa, 0xff, 0x00}, /* 0x16      */
221   {0xaa, 0xff, 0xaa}, /* 0x17      */
222   {0x00, 0x55, 0x55}, /* 0x18      */
223   {0x00, 0x55, 0xff}, /* 0x19      */
224   {0x00, 0xff, 0x55}, /* 0x1a      */
225   {0x00, 0xff, 0xff}, /* 0x1b      */
226   {0xaa, 0x55, 0x55}, /* 0x1c      */
227   {0xaa, 0x55, 0xff}, /* 0x1d      */
228   {0xaa, 0xff, 0x55}, /* 0x1e      */
229   {0xaa, 0xff, 0xff}, /* 0x1f      */
230   {0x55, 0x00, 0x00}, /* 0x20      */
231   {0x55, 0x00, 0xaa}, /* 0x21      */
232   {0x55, 0xaa, 0x00}, /* 0x22      */
233   {0x55, 0xaa, 0xaa}, /* 0x23      */
234   {0xff, 0x00, 0x00}, /* 0x24      */
235   {0xff, 0x00, 0xaa}, /* 0x25      */
236   {0xff, 0xaa, 0x00}, /* 0x26      */
237   {0xff, 0xaa, 0xaa}, /* 0x27      */
238   {0x55, 0x00, 0x55}, /* 0x28      */
239   {0x55, 0x00, 0xff}, /* 0x29      */
240   {0x55, 0xaa, 0x55}, /* 0x2a      */
241   {0x55, 0xaa, 0xff}, /* 0x2b      */
242   {0xff, 0x00, 0x55}, /* 0x2c      */
243   {0xff, 0x00, 0xff}, /* 0x2d      */
244   {0xff, 0xaa, 0x55}, /* 0x2e      */
245   {0xff, 0xaa, 0xff}, /* 0x2f      */
246   {0x55, 0x55, 0x00}, /* 0x30      */
247   {0x55, 0x55, 0xaa}, /* 0x31      */
248   {0x55, 0xff, 0x00}, /* 0x32      */
249   {0x55, 0xff, 0xaa}, /* 0x33      */
250   {0xff, 0x55, 0x00}, /* 0x34      */
251   {0xff, 0x55, 0xaa}, /* 0x35      */
252   {0xff, 0xff, 0x00}, /* 0x36      */
253   {0xff, 0xff, 0xaa}, /* 0x37      */
254   {0x55, 0x55, 0x55}, /* 0x38      Dark Gray     */
255   {0x55, 0x55, 0xff}, /* 0x39      Light Blue    */
256   {0x55, 0xff, 0x55}, /* 0x3a      Light Green   */
257   {0x55, 0xff, 0xff}, /* 0x3b      Light Cyan    */
258   {0xff, 0x55, 0x55}, /* 0x3c      Light Red     */
259   {0xff, 0x55, 0xff}, /* 0x3d      Light Magenta */
260   {0xff, 0xff, 0x55}, /* 0x3e      Yellow        */
261   {0xff, 0xff, 0xff}, /* 0x3f      White         */
262   {0,0,0} /* The next 192 entries are all zeros  */
263 };
264
265 static HANDLE VGA_timer;
266 static HANDLE VGA_timer_thread;
267
268 /* set the timer rate; called in the polling thread context */
269 static void CALLBACK set_timer_rate( ULONG_PTR arg )
270 {
271     LARGE_INTEGER when;
272
273     when.s.LowPart = when.s.HighPart = 0;
274     SetWaitableTimer( VGA_timer, &when, arg, VGA_Poll, 0, FALSE );
275 }
276
277 static DWORD CALLBACK VGA_TimerThread( void *dummy )
278 {
279     for (;;) WaitForMultipleObjectsEx( 0, NULL, FALSE, INFINITE, TRUE );
280 }
281
282 static void VGA_DeinstallTimer(void)
283 {
284     if (VGA_timer_thread)
285     {
286         /*
287          * Make sure the update thread is not holding
288          * system resources when we kill it.
289          *
290          * Now, we only need to worry about update thread
291          * getting terminated while in EnterCriticalSection 
292          * or WaitForMultipleObjectsEx.
293          *
294          * FIXME: Is this a problem?
295          */
296         EnterCriticalSection(&vga_lock);
297
298         CancelWaitableTimer( VGA_timer );
299         CloseHandle( VGA_timer );
300         TerminateThread( VGA_timer_thread, 0 );
301         CloseHandle( VGA_timer_thread );
302         VGA_timer_thread = 0;
303
304         LeaveCriticalSection(&vga_lock);
305
306         /*
307          * Synchronize display. This makes sure that
308          * changes to display become visible even if program 
309          * terminates before update thread had time to run.
310          */
311         VGA_Poll( 0, 0, 0 );
312     }
313 }
314
315 static void VGA_InstallTimer(unsigned Rate)
316 {
317     if (!VGA_timer_thread)
318     {
319         VGA_timer = CreateWaitableTimerA( NULL, FALSE, NULL );
320         VGA_timer_thread = CreateThread( NULL, 0, VGA_TimerThread, NULL, 0, NULL );
321     }
322     QueueUserAPC( set_timer_rate, VGA_timer_thread, (ULONG_PTR)Rate );
323 }
324
325 static BOOL VGA_IsTimerRunning(void)
326 {
327     return VGA_timer_thread ? TRUE : FALSE;
328 }
329
330 HANDLE VGA_AlphaConsole(void)
331 {
332     /* this assumes that no Win32 redirection has taken place, but then again,
333      * only 16-bit apps are likely to use this part of Wine... */
334     return GetStdHandle(STD_OUTPUT_HANDLE);
335 }
336
337 char*VGA_AlphaBuffer(void)
338 {
339     return (char *)0xb8000;
340 }
341
342 /*** GRAPHICS MODE ***/
343
344 typedef struct {
345   unsigned Xres, Yres, Depth;
346   int ret;
347 } ModeSet;
348
349 static void WINAPI VGA_DoExit(ULONG_PTR arg)
350 {
351     VGA_DeinstallTimer();
352     IDirectDrawSurface_SetPalette(lpddsurf,NULL);
353     IDirectDrawSurface_Release(lpddsurf);
354     lpddsurf=NULL;
355     IDirectDrawPalette_Release(lpddpal);
356     lpddpal=NULL;
357     IDirectDraw_Release(lpddraw);
358     lpddraw=NULL;
359 }
360
361 static void WINAPI VGA_DoSetMode(ULONG_PTR arg)
362 {
363     LRESULT     res;
364     ModeSet *par = (ModeSet *)arg;
365     par->ret=1;
366
367     if (lpddraw) VGA_DoExit(0);
368     if (!lpddraw) {
369         if (!pDirectDrawCreate)
370         {
371             HMODULE hmod = LoadLibraryA( "ddraw.dll" );
372             if (hmod) pDirectDrawCreate = (DirectDrawCreateProc)GetProcAddress( hmod, "DirectDrawCreate" );
373             if (!pDirectDrawCreate) {
374                 ERR("Can't lookup DirectDrawCreate from ddraw.dll.\n");
375                 return;
376             }
377         }
378         res = pDirectDrawCreate(NULL,&lpddraw,NULL);
379         if (!lpddraw) {
380             ERR("DirectDraw is not available (res = %lx)\n",res);
381             return;
382         }
383         if (!vga_hwnd) {
384             vga_hwnd = CreateWindowExA(0,"STATIC","WINEDOS VGA",WS_POPUP|WS_VISIBLE,0,0,par->Xres,par->Yres,0,0,0,NULL);
385             if (!vga_hwnd) {
386                 ERR("Failed to create user window.\n");
387                 IDirectDraw_Release(lpddraw);
388                 lpddraw=NULL;
389                 return;
390             }
391         }
392         else
393             SetWindowPos(vga_hwnd,0,0,0,par->Xres,par->Yres,SWP_NOMOVE|SWP_NOZORDER);
394
395         if ((res=IDirectDraw_SetCooperativeLevel(lpddraw,vga_hwnd,DDSCL_FULLSCREEN|DDSCL_EXCLUSIVE))) {
396             ERR("Could not set cooperative level to exclusive (%lx)\n",res);
397         }
398
399         if ((res=IDirectDraw_SetDisplayMode(lpddraw,par->Xres,par->Yres,par->Depth))) {
400             ERR("DirectDraw does not support requested display mode (%dx%dx%d), res = %lx!\n",par->Xres,par->Yres,par->Depth,res);
401             IDirectDraw_Release(lpddraw);
402             lpddraw=NULL;
403             return;
404         }
405
406         res=IDirectDraw_CreatePalette(lpddraw,DDPCAPS_8BIT,NULL,&lpddpal,NULL);
407         if (res) {
408             ERR("Could not create palette (res = %lx)\n",res);
409             IDirectDraw_Release(lpddraw);
410             lpddraw=NULL;
411             return;
412         }
413         if ((res=IDirectDrawPalette_SetEntries(lpddpal,0,0,256,vga_def_palette))) {
414             ERR("Could not set default palette entries (res = %lx)\n", res);
415         }
416
417         memset(&sdesc,0,sizeof(sdesc));
418         sdesc.dwSize=sizeof(sdesc);
419         sdesc.dwFlags = DDSD_CAPS;
420         sdesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
421         if (IDirectDraw_CreateSurface(lpddraw,&sdesc,&lpddsurf,NULL)||(!lpddsurf)) {
422             ERR("DirectDraw surface is not available\n");
423             IDirectDraw_Release(lpddraw);
424             lpddraw=NULL;
425             return;
426         }
427         IDirectDrawSurface_SetPalette(lpddsurf,lpddpal);
428         vga_retrace_vertical = vga_retrace_horizontal = FALSE;
429         /* poll every 20ms (50fps should provide adequate responsiveness) */
430         VGA_InstallTimer(20);
431     }
432     par->ret=0;
433     return;
434 }
435
436 int VGA_SetMode(unsigned Xres,unsigned Yres,unsigned Depth)
437 {
438     ModeSet par;
439     int     newSize;
440
441     vga_fb_width = Xres;
442     vga_fb_height = Yres;
443     vga_fb_depth = Depth;
444     vga_fb_offset = 0;
445     vga_fb_pitch = Xres * ((Depth + 7) / 8);
446
447     newSize = Xres * Yres * ((Depth + 7) / 8);
448     if(newSize < 256 * 1024)
449       newSize = 256 * 1024;
450
451     if(vga_fb_size < newSize) {
452       if(vga_fb_data)
453         HeapFree(GetProcessHeap(), 0, vga_fb_data);
454       vga_fb_data = HeapAlloc(GetProcessHeap(), 0, newSize);
455       vga_fb_size = newSize;
456     }
457
458     if(Xres >= 640 || Yres >= 480) {
459       par.Xres = Xres;
460       par.Yres = Yres;
461     } else {
462       par.Xres = 640;
463       par.Yres = 480;
464     }
465
466     VGA_SetWindowStart((Depth < 8) ? -1 : 0);
467
468     par.Depth = (Depth < 8) ? 8 : Depth;
469
470     MZ_RunInThread(VGA_DoSetMode, (ULONG_PTR)&par);
471     return par.ret;
472 }
473
474 int VGA_GetMode(unsigned*Height,unsigned*Width,unsigned*Depth)
475 {
476     if (!lpddraw) return 1;
477     if (!lpddsurf) return 1;
478     if (Height) *Height=sdesc.dwHeight;
479     if (Width) *Width=sdesc.dwWidth;
480     if (Depth) *Depth=sdesc.ddpfPixelFormat.u1.dwRGBBitCount;
481     return 0;
482 }
483
484 void VGA_Exit(void)
485 {
486     if (lpddraw) MZ_RunInThread(VGA_DoExit, 0);
487 }
488
489 void VGA_SetPalette(PALETTEENTRY*pal,int start,int len)
490 {
491     if (!lpddraw) return;
492     IDirectDrawPalette_SetEntries(lpddpal,0,start,len,pal);
493 }
494
495 /* set a single [char wide] color in 16 color mode. */
496 void VGA_SetColor16(int reg,int color)
497 {
498         PALETTEENTRY *pal;
499
500     if (!lpddraw) return;
501         pal= &vga_def64_palette[color];
502         IDirectDrawPalette_SetEntries(lpddpal,0,reg,1,pal);
503         vga_16_palette[reg]=(char)color;
504 }
505
506 /* Get a single [char wide] color in 16 color mode. */
507 char VGA_GetColor16(int reg)
508 {
509
510     if (!lpddraw) return 0;
511         return (char)vga_16_palette[reg];
512 }
513
514 /* set all 17 [char wide] colors at once in 16 color mode. */
515 void VGA_Set16Palette(char *Table)
516 {
517         PALETTEENTRY *pal;
518         int c;
519
520     if (!lpddraw) return;         /* return if we're in text only mode */
521         bcopy((void *)&vga_16_palette,(void *)Table,17);
522                                     /* copy the entries into the table */
523     for (c=0; c<17; c++) {                                /* 17 entries */
524         pal= &vga_def64_palette[(int)vga_16_palette[c]];  /* get color  */
525         IDirectDrawPalette_SetEntries(lpddpal,0,c,1,pal); /* set entry  */
526         TRACE("Palette register %d set to %d\n",c,(int)vga_16_palette[c]);
527    } /* end of the counting loop */
528 }
529
530 /* Get all 17 [ char wide ] colors at once in 16 color mode. */
531 void VGA_Get16Palette(char *Table)
532 {
533
534     if (!lpddraw) return;         /* return if we're in text only mode */
535         bcopy((void *)Table,(void *)&vga_16_palette,17);
536                                     /* copy the entries into the table */
537 }
538
539 void VGA_SetQuadPalette(RGBQUAD*color,int start,int len)
540 {
541     PALETTEENTRY pal[256];
542     int c;
543
544     if (!lpddraw) return;
545     for (c=0; c<len; c++) {
546         pal[c].peRed  =color[c].rgbRed;
547         pal[c].peGreen=color[c].rgbGreen;
548         pal[c].peBlue =color[c].rgbBlue;
549         pal[c].peFlags=0;
550     }
551     IDirectDrawPalette_SetEntries(lpddpal,0,start,len,pal);
552 }
553
554 LPSTR VGA_Lock(unsigned*Pitch,unsigned*Height,unsigned*Width,unsigned*Depth)
555 {
556     if (!lpddraw) return NULL;
557     if (!lpddsurf) return NULL;
558     if (IDirectDrawSurface_Lock(lpddsurf,NULL,&sdesc,0,0)) {
559         ERR("could not lock surface!\n");
560         return NULL;
561     }
562     if (Pitch) *Pitch=sdesc.u1.lPitch;
563     if (Height) *Height=sdesc.dwHeight;
564     if (Width) *Width=sdesc.dwWidth;
565     if (Depth) *Depth=sdesc.ddpfPixelFormat.u1.dwRGBBitCount;
566     return sdesc.lpSurface;
567 }
568
569 void VGA_Unlock(void)
570 {
571     IDirectDrawSurface_Unlock(lpddsurf,sdesc.lpSurface);
572 }
573
574 /*
575  * Set start of 64k window at 0xa0000 in bytes.
576  * If value is -1, initialize color plane support.
577  * If value is >= 0, window contains direct copy of framebuffer.
578  */
579 void VGA_SetWindowStart(int start)
580 {
581     if(start == vga_fb_window)
582         return;
583
584     EnterCriticalSection(&vga_lock);
585
586     if(vga_fb_window == -1)
587         FIXME("Remove VGA memory emulation.\n");
588     else
589         memmove(vga_fb_data + vga_fb_window, (char *)0xa0000, 64 * 1024);
590
591     vga_fb_window = start;
592
593     if(vga_fb_window == -1)
594         FIXME("Install VGA memory emulation.\n");
595     else
596         memmove( (char *)0xa0000, vga_fb_data + vga_fb_window, 64 * 1024);
597
598     LeaveCriticalSection(&vga_lock);
599 }
600
601 /*
602  * Get start of 64k window at 0xa0000 in bytes.
603  * Value is -1 in color plane modes.
604  */
605 int VGA_GetWindowStart()
606 {
607     return vga_fb_window;
608 }
609
610 /*** TEXT MODE ***/
611
612 /* prepare the text mode video memory copy that is used to only
613  * update the video memory line that did get updated. */
614 void VGA_PrepareVideoMemCopy(unsigned Xres, unsigned Yres)
615 {
616     char *p, *p2;
617     int i;
618
619     /*
620      * Allocate space for char + attr.
621      */
622     vga_text_old = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
623                                 vga_text_old, Xres * Yres * 2 );
624
625     p = VGA_AlphaBuffer();
626     p2 = vga_text_old;
627
628     /* make sure the video mem copy contains the exact opposite of our
629      * actual text mode memory area to make sure the screen
630      * does get updated fully initially */
631     for (i=0; i < Xres*Yres*2; i++)
632         *p2++ = *p++ ^ 0xff; /* XOR it */
633 }
634
635 /**********************************************************************
636  *         VGA_SetAlphaMode
637  *
638  * Set VGA emulation to text mode.
639  */
640 void VGA_SetAlphaMode(unsigned Xres,unsigned Yres)
641 {
642     VGA_Exit();
643     VGA_DeinstallTimer();
644     
645     VGA_PrepareVideoMemCopy(Xres, Yres);
646     vga_text_width = Xres;
647     vga_text_height = Yres;
648
649     if (vga_text_x >= vga_text_width || vga_text_y >= vga_text_height)
650         VGA_SetCursorPos(0,0);
651
652     if(vga_text_console) {
653         COORD size;
654         size.X = Xres;
655         size.Y = Yres;
656         SetConsoleScreenBufferSize( VGA_AlphaConsole(), size );
657
658         /* poll every 30ms (33fps should provide adequate responsiveness) */
659         VGA_InstallTimer(30);
660     }
661 }
662
663 /**********************************************************************
664  *         VGA_InitAlphaMode
665  *
666  * Initialize VGA text mode handling and return default text mode.
667  * This function does not set VGA emulation to text mode.
668  */
669 void VGA_InitAlphaMode(unsigned*Xres,unsigned*Yres)
670 {
671     CONSOLE_SCREEN_BUFFER_INFO info;
672
673     if(GetConsoleScreenBufferInfo( VGA_AlphaConsole(), &info ))
674     {
675         vga_text_console = TRUE;
676         vga_text_x = info.dwCursorPosition.X;
677         vga_text_y = info.dwCursorPosition.Y;
678         vga_text_attr = info.wAttributes;
679         *Xres = info.dwSize.X;
680         *Yres = info.dwSize.Y;
681     } 
682     else
683     {
684         vga_text_console = FALSE;
685         vga_text_x = 0;
686         vga_text_y = 0;
687         vga_text_attr = 0x0f;
688         *Xres = 80;
689         *Yres = 25;
690     }
691 }
692
693 /**********************************************************************
694  *         VGA_GetAlphaMode
695  *
696  * Get current text mode. Returns TRUE and sets resolution if
697  * any VGA text mode has been initialized.
698  */
699 BOOL VGA_GetAlphaMode(unsigned*Xres,unsigned*Yres)
700 {
701     if (vga_text_width != 0 && vga_text_height != 0) {
702         *Xres = vga_text_width;
703         *Yres = vga_text_height;
704         return TRUE;
705     } else
706         return FALSE;
707 }
708
709 void VGA_SetCursorShape(unsigned char start_options, unsigned char end)
710 {
711     CONSOLE_CURSOR_INFO cci;
712
713     /* standard cursor settings:
714      * 0x0607 == CGA, 0x0b0c == monochrome, 0x0d0e == EGA/VGA */
715
716     /* calculate percentage from bottom - assuming VGA (bottom 0x0e) */
717     cci.dwSize = ((end & 0x1f) - (start_options & 0x1f))/0x0e * 100;
718     if (!cci.dwSize) cci.dwSize++; /* NULL cursor would make SCCI() fail ! */
719     cci.bVisible = ((start_options & 0x60) != 0x20); /* invisible ? */
720
721     SetConsoleCursorInfo(VGA_AlphaConsole(),&cci);
722 }
723
724 void VGA_SetCursorPos(unsigned X,unsigned Y)
725 {
726     vga_text_x = X;
727     vga_text_y = Y;
728 }
729
730 void VGA_GetCursorPos(unsigned*X,unsigned*Y)
731 {
732     if (X) *X = vga_text_x;
733     if (Y) *Y = vga_text_y;
734 }
735
736 static void VGA_PutCharAt(unsigned x, unsigned y, BYTE ascii, int attr)
737 {
738     char *dat = VGA_AlphaBuffer() + ((vga_text_width * y + x) * 2);
739     dat[0] = ascii;
740     if (attr>=0)
741         dat[1] = attr;
742 }
743
744 void VGA_WriteChars(unsigned X,unsigned Y,unsigned ch,int attr,int count)
745 {
746     EnterCriticalSection(&vga_lock);
747
748     while (count--) 
749         VGA_PutCharAt(X + count, Y, ch, attr);
750
751     LeaveCriticalSection(&vga_lock);
752 }
753
754 void VGA_PutChar(BYTE ascii)
755 {
756     EnterCriticalSection(&vga_lock);
757
758     switch(ascii) {
759     case '\b':
760         if (vga_text_x)
761             vga_text_x--;
762         break;
763
764     case '\t':
765         vga_text_x += ((vga_text_x + 8) & ~7) - vga_text_x;
766         break;
767
768     case '\n':
769         vga_text_y++;
770         vga_text_x = 0;
771         break;
772
773     case '\a':
774         break;
775
776     case '\r':
777         vga_text_x = 0;
778         break;
779
780     default:
781         VGA_PutCharAt(vga_text_x, vga_text_y, ascii, vga_text_attr);
782         vga_text_x++;
783     }
784
785     /*
786      * FIXME: add line wrapping and scrolling
787      */
788
789     /*
790      * If we don't have a console, write directly to standard output.
791      */
792     if(!vga_text_console)
793         WriteFile(VGA_AlphaConsole(), &ascii, 1, NULL, NULL);
794
795     LeaveCriticalSection(&vga_lock);
796 }
797
798 void VGA_SetTextAttribute(BYTE attr)
799 {
800     vga_text_attr = attr;
801 }
802
803 void VGA_ClearText(unsigned row1, unsigned col1,
804                    unsigned row2, unsigned col2,
805                    BYTE attr)
806 {
807     unsigned x, y;
808
809     EnterCriticalSection(&vga_lock);
810
811     for(y=row1; y<=row2; y++)
812         for(x=col1; x<=col2; x++)
813             VGA_PutCharAt(x, y, 0x20, attr);
814
815     LeaveCriticalSection(&vga_lock);
816 }
817
818 void VGA_ScrollUpText(unsigned row1, unsigned col1,
819                      unsigned row2, unsigned col2,
820                      unsigned lines, BYTE attr)
821 {
822     FIXME("not implemented\n");
823 }
824
825 void VGA_ScrollDownText(unsigned row1, unsigned col1,
826                        unsigned row2, unsigned col2,
827                        unsigned lines, BYTE attr)
828 {
829     FIXME("not implemented\n");
830 }
831
832 void VGA_GetCharacterAtCursor(BYTE *ascii, BYTE *attr)
833 {
834     char *dat;
835
836     dat = VGA_AlphaBuffer() + ((vga_text_width * vga_text_y + vga_text_x) * 2);
837
838     *ascii = dat[0];
839     *attr = dat[1];
840 }
841
842
843 /*** CONTROL ***/
844
845 /* FIXME: optimize by doing this only if the data has actually changed
846  *        (in a way similar to DIBSection, perhaps) */
847 static void VGA_Poll_Graphics(void)
848 {
849   unsigned int Pitch, Height, Width, X, Y;
850   char *surf;
851   char *dat = vga_fb_data + vga_fb_offset;
852   int   bpp = (vga_fb_depth + 7) / 8;
853
854   surf = VGA_Lock(&Pitch,&Height,&Width,NULL);
855   if (!surf) return;
856
857   /*
858    * Synchronize framebuffer contents.
859    */
860   if(vga_fb_window != -1)
861     memmove(vga_fb_data + vga_fb_window, (char *)0xa0000, 64 * 1024);
862
863   /*
864    * Double VGA framebuffer (320x200 -> 640x400), if needed.
865    */
866   if(Height >= 2 * vga_fb_height && Width >= 2 * vga_fb_width && bpp == 1)
867     for (Y=0; Y<vga_fb_height; Y++,surf+=Pitch*2,dat+=vga_fb_pitch)
868       for (X=0; X<vga_fb_width; X++) {
869        BYTE value = dat[X];
870        surf[X*2] = value;
871        surf[X*2+1] = value;
872        surf[X*2+Pitch] = value;
873        surf[X*2+Pitch+1] = value;
874       }
875   else
876     for (Y=0; Y<vga_fb_height; Y++,surf+=Pitch,dat+=vga_fb_pitch)
877       memcpy(surf, dat, vga_fb_width * bpp);
878
879   VGA_Unlock();
880 }
881
882 static void VGA_Poll_Text(void)
883 {
884     char *dat, *old, *p_line;
885     unsigned int X, Y;
886     CHAR_INFO ch[256]; /* that should suffice for the largest text width */
887     COORD siz, off;
888     SMALL_RECT dest;
889     HANDLE con = VGA_AlphaConsole();
890     BOOL linechanged = FALSE; /* video memory area differs from stored copy? */
891
892     /* Synchronize cursor position. */
893     off.X = vga_text_x;
894     off.Y = vga_text_y;
895     SetConsoleCursorPosition(con,off);
896
897     dat = VGA_AlphaBuffer();
898     old = vga_text_old; /* pointer to stored video mem copy */
899     siz.X = vga_text_width; siz.Y = 1;
900     off.X = 0; off.Y = 0;
901
902     /* copy from virtual VGA frame buffer to console */
903     for (Y=0; Y<vga_text_height; Y++) {
904         linechanged = memcmp(dat, old, vga_text_width*2);
905         if (linechanged)
906         {
907             /*TRACE("line %d changed\n", Y);*/
908             p_line = dat;
909             for (X=0; X<vga_text_width; X++) {
910                 ch[X].Char.AsciiChar = *p_line++;
911                 /* WriteConsoleOutputA doesn't like "dead" chars */
912                 if (ch[X].Char.AsciiChar == '\0')
913                     ch[X].Char.AsciiChar = ' ';
914                 ch[X].Attributes = *p_line++;
915             }
916             dest.Top=Y; dest.Bottom=Y;
917             dest.Left=0; dest.Right=vga_text_width+1;
918             WriteConsoleOutputA(con, ch, siz, off, &dest);
919             memcpy(old, dat, vga_text_width*2);
920         }
921         /* advance to next text line */
922         dat += vga_text_width*2;
923         old += vga_text_width*2;
924     }
925 }
926
927 static void CALLBACK VGA_Poll( LPVOID arg, DWORD low, DWORD high )
928 {
929     EnterCriticalSection(&vga_lock);
930
931     if (lpddraw)
932         VGA_Poll_Graphics();
933     else
934         VGA_Poll_Text();
935
936     /*
937      * Fake start of retrace.
938      */
939     vga_retrace_vertical = TRUE;
940
941     LeaveCriticalSection(&vga_lock);
942 }
943
944 static BYTE palreg,palcnt;
945 static PALETTEENTRY paldat;
946
947 void VGA_ioport_out( WORD port, BYTE val )
948 {
949     switch (port) {
950         case 0x3c0:
951            if (vga_address_3c0)
952                vga_index_3c0 = val;
953            else
954                FIXME("Unsupported index, register 0x3c0: 0x%02x (value 0x%02x)\n",
955                      vga_index_3c0, val);
956            vga_address_3c0 = !vga_address_3c0;
957            break;
958         case 0x3c4:
959            vga_index_3c4 = val;
960            break;
961         case 0x3c5:
962           switch(vga_index_3c4) {
963                case 0x04: /* Sequencer: Memory Mode Register */
964                   if(vga_fb_depth == 8)
965                       VGA_SetWindowStart((val & 8) ? 0 : -1);
966                   else
967                       FIXME("Memory Mode Register not supported in this mode.\n");
968                break;
969                default:
970                   FIXME("Unsupported index, register 0x3c4: 0x%02x (value 0x%02x)\n",
971                         vga_index_3c4, val);
972            }
973            break;
974         case 0x3c8:
975             palreg=val; palcnt=0; break;
976         case 0x3c9:
977             ((BYTE*)&paldat)[palcnt++]=val << 2;
978             if (palcnt==3) {
979                 VGA_SetPalette(&paldat,palreg++,1);
980                 palcnt=0;
981             }
982             break;
983         case 0x3ce:
984             vga_index_3ce = val;
985            break;
986         case 0x3cf:
987            FIXME("Unsupported index, register 0x3ce: 0x%02x (value 0x%02x)\n",
988                  vga_index_3ce, val);
989            break;
990         case 0x3d4:
991            vga_index_3d4 = val;
992            break;
993         case 0x3d5:
994            FIXME("Unsupported index, register 0x3d4: 0x%02x (value 0x%02x)\n",
995                  vga_index_3d4, val);
996            break;
997         default:
998             FIXME("Unsupported VGA register: 0x%04x (value 0x%02x)\n", port, val);
999     }
1000 }
1001
1002 BYTE VGA_ioport_in( WORD port )
1003 {
1004     BYTE ret;
1005
1006     switch (port) {
1007         case 0x3c1:
1008            FIXME("Unsupported index, register 0x3c0: 0x%02x\n",
1009                  vga_index_3c0);
1010            return 0xff;
1011         case 0x3c5:
1012            switch(vga_index_3c4) {
1013                case 0x04: /* Sequencer: Memory Mode Register */
1014                     return (VGA_GetWindowStart() == -1) ? 0xf7 : 0xff;
1015                default:
1016                    FIXME("Unsupported index, register 0x3c4: 0x%02x\n",
1017                          vga_index_3c4);
1018                    return 0xff;
1019            }
1020         case 0x3cf:
1021            FIXME("Unsupported index, register 0x3ce: 0x%02x\n",
1022                  vga_index_3ce);
1023            return 0xff;
1024         case 0x3d5:
1025            FIXME("Unsupported index, register 0x3d4: 0x%02x\n",
1026                  vga_index_3d4);
1027            return 0xff;
1028
1029         case 0x3da:
1030             /*
1031              * Read from this register resets register 0x3c0 address flip-flop.
1032              */
1033             vga_address_3c0 = TRUE;
1034
1035             /*
1036              * Read from this register returns following bits:
1037              *   xxxx1xxx = Vertical retrace in progress if set.
1038              *   xxxxx1xx = Light pen switched on.
1039              *   xxxxxx1x = Light pen trigger set.
1040              *   xxxxxxx1 = Either vertical or horizontal retrace 
1041              *              in progress if set.
1042              */
1043             ret = 0;
1044             if (vga_retrace_vertical)
1045                 ret |= 9;
1046             if (vga_retrace_horizontal)
1047                 ret |= 3;
1048             
1049             /*
1050              * If VGA mode has been set, vertical retrace is
1051              * turned on once a frame and cleared after each read.
1052              * This might cause applications that synchronize with
1053              * vertical retrace to actually skip one frame but that
1054              * is probably not a problem.
1055              * 
1056              * If no VGA mode has been set, vertical retrace is faked
1057              * by toggling the value after every read.
1058              */
1059             if (VGA_IsTimerRunning())
1060                 vga_retrace_vertical = FALSE;
1061             else
1062                 vga_retrace_vertical = !vga_retrace_vertical;
1063
1064             /*
1065              * Toggle horizontal retrace.
1066              */
1067             vga_retrace_horizontal = !vga_retrace_horizontal;
1068             break;
1069
1070         default:
1071             ret=0xff;
1072             FIXME("Unsupported VGA register: 0x%04x\n", port);
1073     }
1074     return ret;
1075 }
1076
1077 void VGA_Clean(void)
1078 {
1079     VGA_Exit();
1080     VGA_DeinstallTimer();
1081 }