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