Portability 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 = 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     memcpy( Table, &vga_16_palette, 17 ); /* copy the entries into the table */
522
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     memcpy( &vga_16_palette, Table, 17 ); /* copy the entries into the table */
536 }
537
538 void VGA_SetQuadPalette(RGBQUAD*color,int start,int len)
539 {
540     PALETTEENTRY pal[256];
541     int c;
542
543     if (!lpddraw) return;
544     for (c=0; c<len; c++) {
545         pal[c].peRed  =color[c].rgbRed;
546         pal[c].peGreen=color[c].rgbGreen;
547         pal[c].peBlue =color[c].rgbBlue;
548         pal[c].peFlags=0;
549     }
550     IDirectDrawPalette_SetEntries(lpddpal,0,start,len,pal);
551 }
552
553 LPSTR VGA_Lock(unsigned*Pitch,unsigned*Height,unsigned*Width,unsigned*Depth)
554 {
555     if (!lpddraw) return NULL;
556     if (!lpddsurf) return NULL;
557     if (IDirectDrawSurface_Lock(lpddsurf,NULL,&sdesc,0,0)) {
558         ERR("could not lock surface!\n");
559         return NULL;
560     }
561     if (Pitch) *Pitch=sdesc.u1.lPitch;
562     if (Height) *Height=sdesc.dwHeight;
563     if (Width) *Width=sdesc.dwWidth;
564     if (Depth) *Depth=sdesc.ddpfPixelFormat.u1.dwRGBBitCount;
565     return sdesc.lpSurface;
566 }
567
568 void VGA_Unlock(void)
569 {
570     IDirectDrawSurface_Unlock(lpddsurf,sdesc.lpSurface);
571 }
572
573 /*
574  * Set start of 64k window at 0xa0000 in bytes.
575  * If value is -1, initialize color plane support.
576  * If value is >= 0, window contains direct copy of framebuffer.
577  */
578 void VGA_SetWindowStart(int start)
579 {
580     if(start == vga_fb_window)
581         return;
582
583     EnterCriticalSection(&vga_lock);
584
585     if(vga_fb_window == -1)
586         FIXME("Remove VGA memory emulation.\n");
587     else
588         memmove(vga_fb_data + vga_fb_window, (char *)0xa0000, 64 * 1024);
589
590     vga_fb_window = start;
591
592     if(vga_fb_window == -1)
593         FIXME("Install VGA memory emulation.\n");
594     else
595         memmove( (char *)0xa0000, vga_fb_data + vga_fb_window, 64 * 1024);
596
597     LeaveCriticalSection(&vga_lock);
598 }
599
600 /*
601  * Get start of 64k window at 0xa0000 in bytes.
602  * Value is -1 in color plane modes.
603  */
604 int VGA_GetWindowStart()
605 {
606     return vga_fb_window;
607 }
608
609 /*** TEXT MODE ***/
610
611 /* prepare the text mode video memory copy that is used to only
612  * update the video memory line that did get updated. */
613 void VGA_PrepareVideoMemCopy(unsigned Xres, unsigned Yres)
614 {
615     char *p, *p2;
616     int i;
617
618     /*
619      * Allocate space for char + attr.
620      */
621     vga_text_old = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
622                                 vga_text_old, Xres * Yres * 2 );
623
624     p = VGA_AlphaBuffer();
625     p2 = vga_text_old;
626
627     /* make sure the video mem copy contains the exact opposite of our
628      * actual text mode memory area to make sure the screen
629      * does get updated fully initially */
630     for (i=0; i < Xres*Yres*2; i++)
631         *p2++ = *p++ ^ 0xff; /* XOR it */
632 }
633
634 /**********************************************************************
635  *         VGA_SetAlphaMode
636  *
637  * Set VGA emulation to text mode.
638  */
639 void VGA_SetAlphaMode(unsigned Xres,unsigned Yres)
640 {
641     VGA_Exit();
642     VGA_DeinstallTimer();
643     
644     VGA_PrepareVideoMemCopy(Xres, Yres);
645     vga_text_width = Xres;
646     vga_text_height = Yres;
647
648     if (vga_text_x >= vga_text_width || vga_text_y >= vga_text_height)
649         VGA_SetCursorPos(0,0);
650
651     if(vga_text_console) {
652         COORD size;
653         size.X = Xres;
654         size.Y = Yres;
655         SetConsoleScreenBufferSize( VGA_AlphaConsole(), size );
656
657         /* poll every 30ms (33fps should provide adequate responsiveness) */
658         VGA_InstallTimer(30);
659     }
660 }
661
662 /**********************************************************************
663  *         VGA_InitAlphaMode
664  *
665  * Initialize VGA text mode handling and return default text mode.
666  * This function does not set VGA emulation to text mode.
667  */
668 void VGA_InitAlphaMode(unsigned*Xres,unsigned*Yres)
669 {
670     CONSOLE_SCREEN_BUFFER_INFO info;
671
672     if(GetConsoleScreenBufferInfo( VGA_AlphaConsole(), &info ))
673     {
674         vga_text_console = TRUE;
675         vga_text_x = info.dwCursorPosition.X;
676         vga_text_y = info.dwCursorPosition.Y;
677         vga_text_attr = info.wAttributes;
678         *Xres = info.dwSize.X;
679         *Yres = info.dwSize.Y;
680     } 
681     else
682     {
683         vga_text_console = FALSE;
684         vga_text_x = 0;
685         vga_text_y = 0;
686         vga_text_attr = 0x0f;
687         *Xres = 80;
688         *Yres = 25;
689     }
690 }
691
692 /**********************************************************************
693  *         VGA_GetAlphaMode
694  *
695  * Get current text mode. Returns TRUE and sets resolution if
696  * any VGA text mode has been initialized.
697  */
698 BOOL VGA_GetAlphaMode(unsigned*Xres,unsigned*Yres)
699 {
700     if (vga_text_width != 0 && vga_text_height != 0) {
701         *Xres = vga_text_width;
702         *Yres = vga_text_height;
703         return TRUE;
704     } else
705         return FALSE;
706 }
707
708 void VGA_SetCursorShape(unsigned char start_options, unsigned char end)
709 {
710     CONSOLE_CURSOR_INFO cci;
711
712     /* standard cursor settings:
713      * 0x0607 == CGA, 0x0b0c == monochrome, 0x0d0e == EGA/VGA */
714
715     /* calculate percentage from bottom - assuming VGA (bottom 0x0e) */
716     cci.dwSize = ((end & 0x1f) - (start_options & 0x1f))/0x0e * 100;
717     if (!cci.dwSize) cci.dwSize++; /* NULL cursor would make SCCI() fail ! */
718     cci.bVisible = ((start_options & 0x60) != 0x20); /* invisible ? */
719
720     SetConsoleCursorInfo(VGA_AlphaConsole(),&cci);
721 }
722
723 void VGA_SetCursorPos(unsigned X,unsigned Y)
724 {
725     vga_text_x = X;
726     vga_text_y = Y;
727 }
728
729 void VGA_GetCursorPos(unsigned*X,unsigned*Y)
730 {
731     if (X) *X = vga_text_x;
732     if (Y) *Y = vga_text_y;
733 }
734
735 static void VGA_PutCharAt(unsigned x, unsigned y, BYTE ascii, int attr)
736 {
737     char *dat = VGA_AlphaBuffer() + ((vga_text_width * y + x) * 2);
738     dat[0] = ascii;
739     if (attr>=0)
740         dat[1] = attr;
741 }
742
743 void VGA_WriteChars(unsigned X,unsigned Y,unsigned ch,int attr,int count)
744 {
745     EnterCriticalSection(&vga_lock);
746
747     while (count--) 
748         VGA_PutCharAt(X + count, Y, ch, attr);
749
750     LeaveCriticalSection(&vga_lock);
751 }
752
753 void VGA_PutChar(BYTE ascii)
754 {
755     EnterCriticalSection(&vga_lock);
756
757     switch(ascii) {
758     case '\b':
759         if (vga_text_x)
760             vga_text_x--;
761         break;
762
763     case '\t':
764         vga_text_x += ((vga_text_x + 8) & ~7) - vga_text_x;
765         break;
766
767     case '\n':
768         vga_text_y++;
769         vga_text_x = 0;
770         break;
771
772     case '\a':
773         break;
774
775     case '\r':
776         vga_text_x = 0;
777         break;
778
779     default:
780         VGA_PutCharAt(vga_text_x, vga_text_y, ascii, vga_text_attr);
781         vga_text_x++;
782     }
783
784     /*
785      * FIXME: add line wrapping and scrolling
786      */
787
788     /*
789      * If we don't have a console, write directly to standard output.
790      */
791     if(!vga_text_console)
792         WriteFile(VGA_AlphaConsole(), &ascii, 1, NULL, NULL);
793
794     LeaveCriticalSection(&vga_lock);
795 }
796
797 void VGA_SetTextAttribute(BYTE attr)
798 {
799     vga_text_attr = attr;
800 }
801
802 void VGA_ClearText(unsigned row1, unsigned col1,
803                    unsigned row2, unsigned col2,
804                    BYTE attr)
805 {
806     unsigned x, y;
807
808     EnterCriticalSection(&vga_lock);
809
810     for(y=row1; y<=row2; y++)
811         for(x=col1; x<=col2; x++)
812             VGA_PutCharAt(x, y, 0x20, attr);
813
814     LeaveCriticalSection(&vga_lock);
815 }
816
817 void VGA_ScrollUpText(unsigned row1, unsigned col1,
818                      unsigned row2, unsigned col2,
819                      unsigned lines, BYTE attr)
820 {
821     FIXME("not implemented\n");
822 }
823
824 void VGA_ScrollDownText(unsigned row1, unsigned col1,
825                        unsigned row2, unsigned col2,
826                        unsigned lines, BYTE attr)
827 {
828     FIXME("not implemented\n");
829 }
830
831 void VGA_GetCharacterAtCursor(BYTE *ascii, BYTE *attr)
832 {
833     char *dat;
834
835     dat = VGA_AlphaBuffer() + ((vga_text_width * vga_text_y + vga_text_x) * 2);
836
837     *ascii = dat[0];
838     *attr = dat[1];
839 }
840
841
842 /*** CONTROL ***/
843
844 /* FIXME: optimize by doing this only if the data has actually changed
845  *        (in a way similar to DIBSection, perhaps) */
846 static void VGA_Poll_Graphics(void)
847 {
848   unsigned int Pitch, Height, Width, X, Y;
849   char *surf;
850   char *dat = vga_fb_data + vga_fb_offset;
851   int   bpp = (vga_fb_depth + 7) / 8;
852
853   surf = VGA_Lock(&Pitch,&Height,&Width,NULL);
854   if (!surf) return;
855
856   /*
857    * Synchronize framebuffer contents.
858    */
859   if(vga_fb_window != -1)
860     memmove(vga_fb_data + vga_fb_window, (char *)0xa0000, 64 * 1024);
861
862   /*
863    * Double VGA framebuffer (320x200 -> 640x400), if needed.
864    */
865   if(Height >= 2 * vga_fb_height && Width >= 2 * vga_fb_width && bpp == 1)
866     for (Y=0; Y<vga_fb_height; Y++,surf+=Pitch*2,dat+=vga_fb_pitch)
867       for (X=0; X<vga_fb_width; X++) {
868        BYTE value = dat[X];
869        surf[X*2] = value;
870        surf[X*2+1] = value;
871        surf[X*2+Pitch] = value;
872        surf[X*2+Pitch+1] = value;
873       }
874   else
875     for (Y=0; Y<vga_fb_height; Y++,surf+=Pitch,dat+=vga_fb_pitch)
876       memcpy(surf, dat, vga_fb_width * bpp);
877
878   VGA_Unlock();
879 }
880
881 static void VGA_Poll_Text(void)
882 {
883     char *dat, *old, *p_line;
884     unsigned int X, Y;
885     CHAR_INFO ch[256]; /* that should suffice for the largest text width */
886     COORD siz, off;
887     SMALL_RECT dest;
888     HANDLE con = VGA_AlphaConsole();
889     BOOL linechanged = FALSE; /* video memory area differs from stored copy? */
890
891     /* Synchronize cursor position. */
892     off.X = vga_text_x;
893     off.Y = vga_text_y;
894     SetConsoleCursorPosition(con,off);
895
896     dat = VGA_AlphaBuffer();
897     old = vga_text_old; /* pointer to stored video mem copy */
898     siz.X = vga_text_width; siz.Y = 1;
899     off.X = 0; off.Y = 0;
900
901     /* copy from virtual VGA frame buffer to console */
902     for (Y=0; Y<vga_text_height; Y++) {
903         linechanged = memcmp(dat, old, vga_text_width*2);
904         if (linechanged)
905         {
906             /*TRACE("line %d changed\n", Y);*/
907             p_line = dat;
908             for (X=0; X<vga_text_width; X++) {
909                 ch[X].Char.AsciiChar = *p_line++;
910                 /* WriteConsoleOutputA doesn't like "dead" chars */
911                 if (ch[X].Char.AsciiChar == '\0')
912                     ch[X].Char.AsciiChar = ' ';
913                 ch[X].Attributes = *p_line++;
914             }
915             dest.Top=Y; dest.Bottom=Y;
916             dest.Left=0; dest.Right=vga_text_width+1;
917             WriteConsoleOutputA(con, ch, siz, off, &dest);
918             memcpy(old, dat, vga_text_width*2);
919         }
920         /* advance to next text line */
921         dat += vga_text_width*2;
922         old += vga_text_width*2;
923     }
924 }
925
926 static void CALLBACK VGA_Poll( LPVOID arg, DWORD low, DWORD high )
927 {
928     EnterCriticalSection(&vga_lock);
929
930     if (lpddraw)
931         VGA_Poll_Graphics();
932     else
933         VGA_Poll_Text();
934
935     /*
936      * Fake start of retrace.
937      */
938     vga_retrace_vertical = TRUE;
939
940     LeaveCriticalSection(&vga_lock);
941 }
942
943 static BYTE palreg,palcnt;
944 static PALETTEENTRY paldat;
945
946 void VGA_ioport_out( WORD port, BYTE val )
947 {
948     switch (port) {
949         case 0x3c0:
950            if (vga_address_3c0)
951                vga_index_3c0 = val;
952            else
953                FIXME("Unsupported index, register 0x3c0: 0x%02x (value 0x%02x)\n",
954                      vga_index_3c0, val);
955            vga_address_3c0 = !vga_address_3c0;
956            break;
957         case 0x3c4:
958            vga_index_3c4 = val;
959            break;
960         case 0x3c5:
961           switch(vga_index_3c4) {
962                case 0x04: /* Sequencer: Memory Mode Register */
963                   if(vga_fb_depth == 8)
964                       VGA_SetWindowStart((val & 8) ? 0 : -1);
965                   else
966                       FIXME("Memory Mode Register not supported in this mode.\n");
967                break;
968                default:
969                   FIXME("Unsupported index, register 0x3c4: 0x%02x (value 0x%02x)\n",
970                         vga_index_3c4, val);
971            }
972            break;
973         case 0x3c8:
974             palreg=val; palcnt=0; break;
975         case 0x3c9:
976             ((BYTE*)&paldat)[palcnt++]=val << 2;
977             if (palcnt==3) {
978                 VGA_SetPalette(&paldat,palreg++,1);
979                 palcnt=0;
980             }
981             break;
982         case 0x3ce:
983             vga_index_3ce = val;
984            break;
985         case 0x3cf:
986            FIXME("Unsupported index, register 0x3ce: 0x%02x (value 0x%02x)\n",
987                  vga_index_3ce, val);
988            break;
989         case 0x3d4:
990            vga_index_3d4 = val;
991            break;
992         case 0x3d5:
993            FIXME("Unsupported index, register 0x3d4: 0x%02x (value 0x%02x)\n",
994                  vga_index_3d4, val);
995            break;
996         default:
997             FIXME("Unsupported VGA register: 0x%04x (value 0x%02x)\n", port, val);
998     }
999 }
1000
1001 BYTE VGA_ioport_in( WORD port )
1002 {
1003     BYTE ret;
1004
1005     switch (port) {
1006         case 0x3c1:
1007            FIXME("Unsupported index, register 0x3c0: 0x%02x\n",
1008                  vga_index_3c0);
1009            return 0xff;
1010         case 0x3c5:
1011            switch(vga_index_3c4) {
1012                case 0x04: /* Sequencer: Memory Mode Register */
1013                     return (VGA_GetWindowStart() == -1) ? 0xf7 : 0xff;
1014                default:
1015                    FIXME("Unsupported index, register 0x3c4: 0x%02x\n",
1016                          vga_index_3c4);
1017                    return 0xff;
1018            }
1019         case 0x3cf:
1020            FIXME("Unsupported index, register 0x3ce: 0x%02x\n",
1021                  vga_index_3ce);
1022            return 0xff;
1023         case 0x3d5:
1024            FIXME("Unsupported index, register 0x3d4: 0x%02x\n",
1025                  vga_index_3d4);
1026            return 0xff;
1027
1028         case 0x3da:
1029             /*
1030              * Read from this register resets register 0x3c0 address flip-flop.
1031              */
1032             vga_address_3c0 = TRUE;
1033
1034             /*
1035              * Read from this register returns following bits:
1036              *   xxxx1xxx = Vertical retrace in progress if set.
1037              *   xxxxx1xx = Light pen switched on.
1038              *   xxxxxx1x = Light pen trigger set.
1039              *   xxxxxxx1 = Either vertical or horizontal retrace 
1040              *              in progress if set.
1041              */
1042             ret = 0;
1043             if (vga_retrace_vertical)
1044                 ret |= 9;
1045             if (vga_retrace_horizontal)
1046                 ret |= 3;
1047             
1048             /*
1049              * If VGA mode has been set, vertical retrace is
1050              * turned on once a frame and cleared after each read.
1051              * This might cause applications that synchronize with
1052              * vertical retrace to actually skip one frame but that
1053              * is probably not a problem.
1054              * 
1055              * If no VGA mode has been set, vertical retrace is faked
1056              * by toggling the value after every read.
1057              */
1058             if (VGA_IsTimerRunning())
1059                 vga_retrace_vertical = FALSE;
1060             else
1061                 vga_retrace_vertical = !vga_retrace_vertical;
1062
1063             /*
1064              * Toggle horizontal retrace.
1065              */
1066             vga_retrace_horizontal = !vga_retrace_horizontal;
1067             break;
1068
1069         default:
1070             ret=0xff;
1071             FIXME("Unsupported VGA register: 0x%04x\n", port);
1072     }
1073     return ret;
1074 }
1075
1076 void VGA_Clean(void)
1077 {
1078     VGA_Exit();
1079     VGA_DeinstallTimer();
1080 }