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