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