]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/MAI/bios_emulator/scitech/src/pm/os2/pm.c
* Patch by Thomas Frieden, 13 Nov 2002:
[karo-tx-uboot.git] / board / MAI / bios_emulator / scitech / src / pm / os2 / pm.c
1 /****************************************************************************
2 *
3 *                   SciTech OS Portability Manager Library
4 *
5 *  ========================================================================
6 *
7 *    The contents of this file are subject to the SciTech MGL Public
8 *    License Version 1.0 (the "License"); you may not use this file
9 *    except in compliance with the License. You may obtain a copy of
10 *    the License at http://www.scitechsoft.com/mgl-license.txt
11 *
12 *    Software distributed under the License is distributed on an
13 *    "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 *    implied. See the License for the specific language governing
15 *    rights and limitations under the License.
16 *
17 *    The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
18 *
19 *    The Initial Developer of the Original Code is SciTech Software, Inc.
20 *    All Rights Reserved.
21 *
22 *  ========================================================================
23 *
24 * Language:     ANSI C
25 * Environment:  32-bit OS/2
26 *
27 * Description:  Implementation for the OS Portability Manager Library, which
28 *               contains functions to implement OS specific services in a
29 *               generic, cross platform API. Porting the OS Portability
30 *               Manager library is the first step to porting any SciTech
31 *               products to a new platform.
32 *
33 ****************************************************************************/
34
35 #include "pmapi.h"
36 #include "drvlib/os/os.h"
37 #include "pm_help.h"
38 #include "mtrr.h"
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <process.h>
43 #ifndef __EMX__
44 #include <direct.h>
45 #endif
46 #define INCL_DOSERRORS
47 #define INCL_DOS
48 #define INCL_SUB
49 #define INCL_VIO
50 #define INCL_KBD
51 #include <os2.h>
52
53 /* Semaphore for communication with our background daemon */
54 #define SHAREDSEM   ((PSZ)"\\SEM32\\SDD\\DAEMON")
55 #define DAEMON_NAME "SDDDAEMN.EXE"
56
57 /*--------------------------- Global variables ----------------------------*/
58
59 /* Public structures used to communicate with VIDEOPMI for implementing
60  * the ability to call the real mode BIOS functions.
61  */
62
63 typedef struct _VIDEOMODEINFO {
64     ULONG   miModeId;
65     USHORT  usType;
66     USHORT  usInt10ModeSet;
67     USHORT  usXResolution;
68     USHORT  usYResolution;
69     ULONG   ulBufferAddress;
70     ULONG   ulApertureSize;
71     BYTE    bBitsPerPixel;
72     BYTE    bBitPlanes;
73     BYTE    bXCharSize;
74     BYTE    bYCharSize;
75     USHORT  usBytesPerScanLine;
76     USHORT  usTextRows;
77     ULONG   ulPageLength;
78     ULONG   ulSaveSize;
79     BYTE    bVrtRefresh;
80     BYTE    bHrtRefresh;
81     BYTE    bVrtPolPos;
82     BYTE    bHrtPolPos;
83     CHAR    bRedMaskSize;
84     CHAR    bRedFieldPosition;
85     CHAR    bGreenMaskSize;
86     CHAR    bGreenFieldPosition;
87     CHAR    bBlueMaskSize;
88     CHAR    bBlueFieldPosition;
89     CHAR    bRsvdMaskSize;
90     CHAR    bRsvdFieldPosition;
91     ULONG   ulColors;
92     ULONG   ulReserved[3];
93     } VIDEOMODEINFO, FAR *PVIDEOMODEINFO;
94
95 typedef struct _ADAPTERINFO {
96     ULONG       ulAdapterID;
97     CHAR        szOEMString[128];
98     CHAR        szDACString[128];
99     CHAR        szRevision[128];
100     ULONG       ulTotalMemory;
101     ULONG       ulMMIOBaseAddress;
102     ULONG       ulPIOBaseAddress;
103     BYTE        bBusType;
104     BYTE        bEndian;
105     USHORT      usDeviceBusID;
106     USHORT      usVendorBusID;
107     USHORT      SlotID;
108     } ADAPTERINFO, FAR *PADAPTERINFO;
109
110 typedef struct _VIDEO_ADAPTER {
111     void            *hvideo;
112     ADAPTERINFO     Adapter;
113     VIDEOMODEINFO   ModeInfo;
114     } VIDEO_ADAPTER, FAR *PVIDEO_ADAPTER;
115
116 /* PMIREQUEST_SOFTWAREINT structures from OS/2 DDK */
117
118 typedef struct {
119     ULONG ulFlags;                              // VDM initialization type
120 #define VDM_POSTLOAD                    0x1     // adapter just loaded, used internally for initialization
121 #define VDM_INITIALIZE                  0x2     // force initialization of a permanently open VDM, even if previously initialized
122 #define VDM_TERMINATE_POSTINITIALIZE    0x6     //start VDM with initialization, but close it afterwards (includes VDM_INITIALIZE)
123 #define VDM_QUERY_CAPABILITY            0x10    // query the current int 10 capability
124 #define VDM_FULL_VDM_CREATED            0x20    // a full VDM is created
125 #define VDM_MINI_VDM_CREATED            0x40    // a mini VDM is created
126 #define VDM_MINI_VDM_SUPPORTED          0x80    // mini VDM support is available
127     PCHAR szName;                               // VDM initialization program
128     PCHAR szArgs;                               // VDM initialization arguments
129     }INITVDM;
130
131 typedef struct {
132     BYTE bBufferType;
133 #define BUFFER_NONE     0
134 #define INPUT_BUFFER    1
135 #define OUTPUT_BUFFER   2
136     BYTE bReserved;
137     BYTE bSelCRF;
138     BYTE bOffCRF;
139     PVOID pAddress;
140     ULONG ulSize;
141     } BUFFER, *PBUFFER;
142
143 typedef struct vcrf_s {
144     ULONG reg_eax;
145     ULONG reg_ebx;
146     ULONG reg_ecx;
147     ULONG reg_edx;
148     ULONG reg_ebp;
149     ULONG reg_esi;
150     ULONG reg_edi;
151     ULONG reg_ds;
152     ULONG reg_es;
153     ULONG reg_fs;
154     ULONG reg_gs;
155     ULONG reg_cs;
156     ULONG reg_eip;
157     ULONG reg_eflag;
158     ULONG reg_ss;
159     ULONG reg_esp;
160     } VCRF;
161
162 typedef struct {
163     ULONG   ulBIOSIntNo;
164     VCRF    aCRF;
165     BUFFER  pB[2];
166     } INTCRF;
167
168 #define PMIREQUEST_LOADPMIFILE          21
169 #define PMIREQUEST_IDENTIFYADAPTER      22
170 #define PMIREQUEST_SOFTWAREINT          23
171
172 #ifdef  PTR_DECL_IN_FRONT
173 #define EXPENTRYP   * EXPENTRY
174 #else
175 #define EXPENTRYP   EXPENTRY *
176 #endif
177
178 /* Entry point to VIDEOPMI32Request. This may be overridden by external
179  * code that has already loaded VIDEOPMI to avoid loading it twice.
180  */
181
182 APIRET (EXPENTRYP PM_VIDEOPMI32Request)(PVIDEO_ADAPTER, ULONG, PVOID, PVOID) = NULL;
183 static ibool        haveInt10 = -1; /* True if we have Int 10 support   */
184 static ibool        useVPMI = true; /* False if VIDEOPMI unavailable    */
185 static VIDEO_ADAPTER Adapter;       /* Video adapter for VIDEOPMI       */
186 static uchar        RMBuf[1024];    /* Fake real mode transfer buffer   */
187 static uint         VESABuf_len = 1024;/* Length of the VESABuf buffer  */
188 static void         *VESABuf_ptr = NULL;/* Near pointer to VESABuf      */
189 static uint         VESABuf_rseg;   /* Real mode segment of VESABuf     */
190 static uint         VESABuf_roff;   /* Real mode offset of VESABuf      */
191 static uchar *      lowMem = NULL;
192 static ibool        isSessionSwitching = false;
193 static ulong        parmsIn[4];     /* Must not cross 64Kb boundary!    */
194 static ulong        parmsOut[4];    /* Must not cross 64Kb boundary!    */
195 extern ushort       _PM_gdt;
196 static void (PMAPIP fatalErrorCleanup)(void) = NULL;
197
198 /* DosSysCtl prototype. It is not declared in the headers but it is in the
199  * standard import libraries (DOSCALLS.876). Funny.
200  */
201 APIRET APIENTRY DosSysCtl(ULONG ulFunction, PVOID pvData);
202
203 /* This is the stack size for the threads that track the session switch event */
204 #define SESSION_SWITCH_STACK_SIZE   32768
205
206 typedef struct {
207     VIOMODEINFO     vmi;
208     USHORT          CursorX;
209     USHORT          CursorY;
210     UCHAR           FrameBuffer[1];
211     } CONSOLE_SAVE;
212
213 typedef struct _SESWITCHREC {
214     /* The following variable is volatile because of PM_SUSPEND_APP         */
215     volatile int    Flags;          /* -1 or PM_DEACTIVATE or PM_REACTIVATE */
216     PM_saveState_cb Callback;       /* Save/restore context callback        */
217     HMTX            Mutex;          /* Exclusive access mutex               */
218     HEV             Event;          /* Posted after callback is called      */
219     } SESWITCHREC;
220
221 // Page sized block cache
222
223 #define PAGES_PER_BLOCK     32
224 #define PAGE_BLOCK_SIZE     (PAGES_PER_BLOCK * PM_PAGE_SIZE + (PM_PAGE_SIZE-1) + sizeof(pageblock))
225 #define FREELIST_NEXT(p)    (*(void**)(p))
226 typedef struct pageblock {
227     struct pageblock    *next;
228     struct pageblock    *prev;
229     void                *freeListStart;
230     void                *freeList;
231     void                *freeListEnd;
232     int                 freeCount;
233     PM_lockHandle       lockHandle;
234     } pageblock;
235
236 static pageblock    *pageBlocks = NULL;
237
238 /*----------------------------- Implementation ----------------------------*/
239
240 /****************************************************************************
241 PARAMETERS:
242 func        - Helper device driver function to call
243
244 RETURNS:
245 First return value from the device driver in parmsOut[0]
246
247 REMARKS:
248 Function to open our helper device driver, call it and close the file
249 handle. Note that we have to open the device driver for every call because
250 of two problems:
251
252  1. We cannot open a single file handle in a DLL that is shared amongst
253     programs, since every process must have it's own open file handle.
254
255  2. For some reason there appears to be a limit of about 12 open file
256     handles on a device driver in the system. Hence when we open more
257     than about 12 file handles things start to go very strange.
258
259 Hence we simply open the file handle every time that we need to call the
260 device driver to work around these problems.
261 ****************************************************************************/
262 static ulong CallSDDHelp(
263     int func)
264 {
265     static ulong    inLen;          /* Must not cross 64Kb boundary!    */
266     static ulong    outLen;         /* Must not cross 64Kb boundary!    */
267     HFILE           hSDDHelp;
268     ULONG           rc;
269     ulong           result;
270
271     if ((rc = DosOpen(PMHELP_NAME,&hSDDHelp,&result,0,0,
272             FILE_OPEN, OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE,
273             NULL)) != 0) {
274         if (rc == 4) {  /* Did we run out of file handles? */
275             ULONG   ulNewFHs;
276             LONG    lAddFHs = 5;
277
278             if (DosSetRelMaxFH(&lAddFHs, &ulNewFHs) != 0)
279                 PM_fatalError("Failed to raise the file handles limit!");
280             else {
281                 if ((rc = DosOpen(PMHELP_NAME,&hSDDHelp,&result,0,0,
282                         FILE_OPEN, OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE,
283                         NULL)) != 0) {
284                     PM_fatalError("Unable to open SDDHELP$ helper device driver! (#2)");
285                     }
286                 }
287             }
288         else
289             PM_fatalError("Unable to open SDDHELP$ helper device driver!");
290         }
291     if (DosDevIOCtl(hSDDHelp,PMHELP_IOCTL,func,
292             &parmsIn, inLen = sizeof(parmsIn), &inLen,
293             &parmsOut, outLen = sizeof(parmsOut), &outLen) != 0)
294         PM_fatalError("Failure calling SDDHELP$ helper device driver!");
295     DosClose(hSDDHelp);
296     return parmsOut[0];
297 }
298
299 /****************************************************************************
300 REMARKS:
301 Determine if we're running on a DBCS system.
302 ****************************************************************************/
303 ibool __IsDBCSSystem(void)
304 {
305     CHAR        achDBCSInfo[12];
306     COUNTRYCODE ccStruct = {0, 0};
307
308     memset(achDBCSInfo, 0, 12);
309
310     /* Get the DBCS vector - if it's not empty, we're on DBCS */
311     DosQueryDBCSEnv(sizeof(achDBCSInfo), &ccStruct, achDBCSInfo);
312     if (achDBCSInfo[0] != 0)
313         return true;
314     else
315         return false;
316 }
317
318 /****************************************************************************
319 REMARKS:
320 Determine if PMSHELL is running - if it isn't, we can't use certain calls
321 ****************************************************************************/
322 ibool __isShellLoaded(void)
323 {
324     PVOID   ptr;
325
326     if (DosGetNamedSharedMem(&ptr, (PSZ)"\\SHAREMEM\\PMGLOBAL.MEM", PAG_READ) == NO_ERROR) {
327         DosFreeMem(ptr);
328         return true;
329         }
330     return false;
331 }
332
333 /****************************************************************************
334 REMARKS:
335 Initialise the PM library and connect to our helper device driver. If we
336 cannot connect to our helper device driver, we bail out with an error
337 message.
338 ****************************************************************************/
339 void PMAPI PM_init(void)
340 {
341     if (!lowMem) {
342         /* Obtain the 32->16 callgate from the device driver to enable IOPL */
343         if ((_PM_gdt = CallSDDHelp(PMHELP_GETGDT32)) == 0)
344             PM_fatalError("Unable to obtain call gate selector!");
345
346         PM_setIOPL(3);
347
348         /* Map the first Mb of physical memory into lowMem */
349         if ((lowMem = PM_mapPhysicalAddr(0,0xFFFFF,true)) == NULL)
350             PM_fatalError("Unable to map first Mb physical memory!");
351
352         /* Initialise the MTRR interface functions */
353         MTRR_init();
354         }
355 }
356
357 /****************************************************************************
358 REMARKS:
359 Initialise the PM library for BIOS access via VIDEOPMI. This should work
360 with any GRADD driver, including SDD/2.
361 ****************************************************************************/
362 static ibool InitInt10(void)
363 {
364     HMODULE     hModGENPMI,hModSDDPMI,hModVideoPMI;
365     CHAR        buf[80],path[_MAX_PATH];
366     HEV         hevDaemon = NULLHANDLE;
367     RESULTCODES resCodes;
368
369     if (haveInt10 == -1) {
370         /* Connect to VIDEOPMI and get entry point. Note that we only
371          * do this if GENPMI or SDDPMI are already loaded, since we need
372          * a GRADD based driver for this to work.
373          */
374         PM_init();
375         haveInt10 = false;
376         if (DosQueryModuleHandle((PSZ)"GENPMI.DLL",&hModGENPMI) != 0)
377             hModGENPMI = NULLHANDLE;
378         if (DosQueryModuleHandle((PSZ)"SDDPMI.DLL",&hModSDDPMI) != 0)
379             hModSDDPMI = NULLHANDLE;
380         if (hModGENPMI || hModSDDPMI) {
381             if (DosLoadModule((PSZ)buf,sizeof(buf),(PSZ)"VIDEOPMI.DLL",&hModVideoPMI) == 0) {
382                 if (DosQueryProcAddr(hModVideoPMI,0,(PSZ)"VIDEOPMI32Request",(void*)&PM_VIDEOPMI32Request) != 0)
383                     PM_fatalError("Unable to get VIDEOPMI32Request entry point!");
384                 strcpy(path,"X:\\OS2\\SVGADATA.PMI");
385                 path[0] = PM_getBootDrive();
386                 if (PM_VIDEOPMI32Request(&Adapter,PMIREQUEST_LOADPMIFILE,path,NULL) != 0) {
387                     DosFreeModule(hModVideoPMI);
388                     PM_VIDEOPMI32Request = NULL;
389                     haveInt10 = false;
390                     }
391                 else {
392                     /* Attempt to initialise the full VDM in the system. This will only
393                      * work if VPRPMI.SYS is loaded, but it provides support for passing
394                      * values in ES/DS/ESI/EDI between the BIOS which does not work with
395                      * kernel VDM's in fixpacks earlier than FP15. FP15 and later and
396                      * the new Warp 4.51 and Warp Server convenience packs should work
397                      * fine with the kernel mini-VDM.
398                      *
399                      * Also the full VDM is the only solution for really old kernels
400                      * (but GRADD won't run on them so this is superfluous ;-).
401                      */
402                     INITVDM InitVDM = {VDM_INITIALIZE,NULL,NULL};
403                     PM_VIDEOPMI32Request(&Adapter,PMIREQUEST_SOFTWAREINT,&InitVDM,NULL);
404                     haveInt10 = true;
405                     }
406                 }
407             }
408         else {
409             /* A GRADD driver isn't loaded, hence we can't use VIDEOPMI. But we will try
410              * to access the mini-VDM directly, first verifying that the support is
411              * available in the kernel (it should be for kernels that support GRADD).
412              * This may be needed in a command line boot or if non-GRADD driver is
413              * used (Matrox or classic VGA).
414              * Note: because of problems with mini-VDM support in the kernel, we have to
415              * spawn a daemon process that will do the actual mini-VDM access for us.
416              */
417              /* Try to open shared semaphore to see if our daemon is already up */
418             if (DosOpenEventSem(SHAREDSEM, &hevDaemon) == NO_ERROR) {
419                 if (DosWaitEventSem(hevDaemon, 1) == NO_ERROR) {
420                     /* If semaphore is posted, all is well */
421                     useVPMI   = false;
422                     haveInt10 = true;
423                     }
424                 }
425             else {
426                 /* Create shared event semaphore */
427                 if (DosCreateEventSem(SHAREDSEM, &hevDaemon, DC_SEM_SHARED, FALSE) == NO_ERROR) {
428                     PM_findBPD(DAEMON_NAME, path);
429                     strcat(path, DAEMON_NAME);
430                     if (DosExecPgm(buf, sizeof(buf), EXEC_BACKGROUND, (PSZ)DAEMON_NAME,
431                         NULL, &resCodes, (PSZ)path) == NO_ERROR) {
432                         /* The daemon was successfully spawned, now give it a sec to come up */
433                         if (DosWaitEventSem(hevDaemon, 2000) == NO_ERROR) {
434                             /* It's up! */
435                             useVPMI   = false;
436                             haveInt10 = true;
437                             }
438                         }
439                     }
440                 }
441             }
442         }
443     return haveInt10;
444 }
445
446 /****************************************************************************
447 REMARKS:
448 We "probably" have BIOS access under OS/2 but we have to verify/initialize it
449 first.
450 ****************************************************************************/
451 ibool PMAPI PM_haveBIOSAccess(void)
452 {
453     return InitInt10();
454 }
455
456 /****************************************************************************
457 REMARKS:
458 Return the operating system type identifier.
459 ****************************************************************************/
460 long PMAPI PM_getOSType(void)
461 {
462     return _OS_OS2;
463 }
464
465 /****************************************************************************
466 REMARKS:
467 Return the runtime type identifier.
468 ****************************************************************************/
469 int PMAPI PM_getModeType(void)
470 {
471     return PM_386;
472 }
473
474 /****************************************************************************
475 REMARKS:
476 Add a file directory separator to the end of the filename.
477 ****************************************************************************/
478 void PMAPI PM_backslash(
479     char *s)
480 {
481     uint pos = strlen(s);
482     if (s[pos-1] != '\\') {
483         s[pos] = '\\';
484         s[pos+1] = '\0';
485         }
486 }
487
488 /****************************************************************************
489 REMARKS:
490 Add a user defined PM_fatalError cleanup function.
491 ****************************************************************************/
492 void PMAPI PM_setFatalErrorCleanup(
493     void (PMAPIP cleanup)(void))
494 {
495     fatalErrorCleanup = cleanup;
496 }
497
498 /****************************************************************************
499 REMARKS:
500 Report a fatal error condition and halt the program.
501 ****************************************************************************/
502 void PMAPI PM_fatalError(
503     const char *msg)
504 {
505     /* Be prepare to be called recursively (failed to fail situation :-) */
506     static int fatalErrorCount = 0;
507     if (fatalErrorCount++ == 0) {
508         if (fatalErrorCleanup)
509             fatalErrorCleanup();
510         }
511     fprintf(stderr,"%s\n", msg);
512     exit(1);
513 }
514
515 /****************************************************************************
516 REMARKS:
517 Allocate the real mode VESA transfer buffer for communicating with the BIOS.
518 ****************************************************************************/
519 void * PMAPI PM_getVESABuf(
520     uint *len,
521     uint *rseg,
522     uint *roff)
523 {
524     if (!VESABuf_ptr) {
525         /* Allocate a global buffer for communicating with the VESA VBE */
526         if ((VESABuf_ptr = PM_allocRealSeg(VESABuf_len, &VESABuf_rseg, &VESABuf_roff)) == NULL)
527             return NULL;
528         }
529     *len = VESABuf_len;
530     *rseg = VESABuf_rseg;
531     *roff = VESABuf_roff;
532     return VESABuf_ptr;
533 }
534
535 /****************************************************************************
536 REMARKS:
537 Check if a key has been pressed.
538 ****************************************************************************/
539 int PMAPI PM_kbhit(void)
540 {
541     KBDKEYINFO   key;            /* Must not cross a 64K boundary    */
542
543     KbdPeek(&key, 0);
544     return (key.fbStatus & KBDTRF_FINAL_CHAR_IN);
545 }
546
547 /****************************************************************************
548 REMARKS:
549 Wait for and return the next keypress.
550 ****************************************************************************/
551 int PMAPI PM_getch(void)
552 {
553     KBDKEYINFO   key;            /* Must not cross a 64K boundary    */
554
555     KbdCharIn(&key,IO_WAIT,0);
556     return key.chChar;
557 }
558
559 /****************************************************************************
560 REMARKS:
561 Open a fullscreen console for output to the screen. This requires that
562 the application be a fullscreen VIO program.
563 ****************************************************************************/
564 PM_HWND PMAPI PM_openConsole(
565     PM_HWND hwndUser,
566     int device,
567     int xRes,
568     int yRes,
569     int bpp,
570     ibool fullScreen)
571 {
572     (void)hwndUser;
573     (void)device;
574     (void)xRes;
575     (void)yRes;
576     (void)bpp;
577     (void)fullScreen;
578     return 0;
579 }
580
581 /****************************************************************************
582 REMARKS:
583 Find the size of the console state buffer.
584 ****************************************************************************/
585 int PMAPI PM_getConsoleStateSize(void)
586 {
587     VIOMODEINFO vmi;
588     vmi.cb = sizeof (VIOMODEINFO);
589     VioGetMode (&vmi, (HVIO)0);
590     return sizeof (CONSOLE_SAVE) - 1 + vmi.col * vmi.row * 2;
591 }
592
593 /****************************************************************************
594 REMARKS:
595 Save the state of the console.
596 ****************************************************************************/
597 void PMAPI PM_saveConsoleState(
598     void *stateBuf,
599     PM_HWND hwndConsole)
600 {
601     USHORT          fblen;
602     CONSOLE_SAVE    *cs = (CONSOLE_SAVE*)stateBuf;
603     VIOMODEINFO     vmi;
604
605     /* The reason for the VIOMODEINFO juggling is 16-bit code. Because the user
606      * allocates the state buffer, cd->vmi might be crossing the 64K boundary and
607      * the 16-bit API would fail. If we create another copy on stack, the compiler
608      * should ensure that the 64K boundary will not be crossed (it adjusts the stack
609      * if it should cross).
610      */
611     vmi.cb = sizeof(VIOMODEINFO);
612     VioGetMode(&vmi,(HVIO)0);
613     memcpy(&cs->vmi, &vmi, sizeof(VIOMODEINFO));
614     VioGetCurPos(&cs->CursorY, &cs->CursorX, (HVIO)0);
615     fblen = cs->vmi.col * cs->vmi.row * 2;
616     VioReadCellStr((PCH)cs->FrameBuffer, &fblen, 0, 0, (HVIO)0);
617 }
618
619 /* Global variable to communicate between threads */
620 static SESWITCHREC SesSwitchRec = { -1 };
621
622 /****************************************************************************
623 REMARKS:
624 Called by external routines at least once per frame to check whenever a
625 session save/restore should be performed. Since we receive such notifications
626 asyncronously, we can't perform all required operations at that time.
627 ****************************************************************************/
628 void __PM_checkConsoleSwitch(void)
629 {
630     int             Flags, Mode;
631     PM_saveState_cb Callback;
632
633     /* Quick optimized path for most common case */
634     if (SesSwitchRec.Flags == -1)
635         return;
636
637 again:
638     if (DosRequestMutexSem(SesSwitchRec.Mutex, 100))
639         return;
640     Flags = SesSwitchRec.Flags;
641     Callback = SesSwitchRec.Callback;
642     SesSwitchRec.Flags = -1;
643     DosReleaseMutexSem(SesSwitchRec.Mutex);
644
645     isSessionSwitching = true;            /* Prevent VIO calls */
646     Mode = Callback(Flags);
647     isSessionSwitching = false;
648     DosPostEventSem(SesSwitchRec.Event);
649     if (Flags == PM_DEACTIVATE && Mode == PM_SUSPEND_APP)
650         /* Suspend application until we switch back to our application */
651         for (;;) {
652             DosSleep (500);
653             /* SesSwitchRec.Flags is volatile so optimizer
654              * won't load it into a register
655              */
656             if (SesSwitchRec.Flags != -1)
657                 goto again;
658             }
659 }
660
661 /****************************************************************************
662 REMARKS:
663 Waits until main thread processes the session switch event.
664 ****************************************************************************/
665 static void _PM_SessionSwitchEvent(
666     PM_saveState_cb saveState,
667     int flags)
668 {
669     ULONG Count;
670
671     if (DosRequestMutexSem(SesSwitchRec.Mutex, 10000))
672         return;
673
674     /* We're going to wait on that semaphore */
675     DosResetEventSem(SesSwitchRec.Event, &Count);
676     SesSwitchRec.Callback = saveState;
677     SesSwitchRec.Flags = flags;
678     DosReleaseMutexSem(SesSwitchRec.Mutex);
679
680     /* Now wait until all required operations are complete */
681     DosWaitEventSem (SesSwitchRec.Event, 10000);
682 }
683
684 /****************************************************************************
685 REMARKS:
686 This is the thread responsible for tracking switches back to our
687 fullscreen session.
688 ****************************************************************************/
689 static void _PM_ConsoleSwitch(
690     PM_saveState_cb saveState)
691 {
692     USHORT NotifyType;
693
694     for (;;) {
695         if (VioModeWait(VMWR_POPUP, &NotifyType, 0) != 0)
696             break;
697         _PM_SessionSwitchEvent(saveState, PM_REACTIVATE);
698         }
699     VioModeUndo(UNDOI_RELEASEOWNER, UNDOK_ERRORCODE, (HVIO)0);
700 }
701
702 /****************************************************************************
703 REMARKS:
704 This is the thread responsible for tracking screen popups (usually fatal
705 error handler uses them).
706 ****************************************************************************/
707 static void _PM_ConsolePopup(
708     PM_saveState_cb saveState)
709 {
710     USHORT NotifyType;
711     for (;;) {
712         if (VioSavRedrawWait(VSRWI_SAVEANDREDRAW, &NotifyType, 0) != 0)
713             break;
714         if (NotifyType == VSRWN_SAVE)
715             _PM_SessionSwitchEvent(saveState, PM_DEACTIVATE);
716         else if (NotifyType == VSRWN_REDRAW)
717             _PM_SessionSwitchEvent(saveState, PM_REACTIVATE);
718         }
719     VioSavRedrawUndo(UNDOI_RELEASEOWNER, UNDOK_ERRORCODE, (HVIO)0);
720 }
721
722 /****************************************************************************
723 REMARKS:
724 Set the suspend application callback for the fullscreen console.
725 ****************************************************************************/
726 void PMAPI PM_setSuspendAppCallback(
727     PM_saveState_cb saveState)
728 {
729     // If PM isn't loaded, this stuff will cause crashes!
730     if (__isShellLoaded()) {
731         if (saveState) {
732             /* Create the threads responsible for tracking console switches */
733             SesSwitchRec.Flags = -1;
734             DosCreateMutexSem(NULL, &SesSwitchRec.Mutex, 0, FALSE);
735             DosCreateEventSem(NULL, &SesSwitchRec.Event, 0, FALSE);
736             _beginthread ((void(*)(void*))_PM_ConsoleSwitch,NULL,SESSION_SWITCH_STACK_SIZE, (void*)saveState);
737             _beginthread ((void(*)(void*))_PM_ConsolePopup,NULL,SESSION_SWITCH_STACK_SIZE, (void*)saveState);
738             }
739         else {
740             /* Kill the threads responsible for tracking console switches */
741             VioModeUndo(UNDOI_RELEASEOWNER, UNDOK_TERMINATE, (HVIO)0);
742             VioSavRedrawUndo(UNDOI_RELEASEOWNER, UNDOK_TERMINATE, (HVIO)0);
743             DosCloseEventSem(SesSwitchRec.Event);
744             DosCloseMutexSem(SesSwitchRec.Mutex);
745             }
746         }
747 }
748
749 /****************************************************************************
750 REMARKS:
751 Restore the console state.
752 ****************************************************************************/
753 void PMAPI PM_restoreConsoleState(
754     const void *stateBuf,
755     PM_HWND hwndConsole)
756 {
757     CONSOLE_SAVE *cs = (CONSOLE_SAVE *)stateBuf;
758     VIOMODEINFO  vmi;
759
760     if (!cs)
761         return;
762
763     memcpy(&vmi, &cs->vmi, sizeof (VIOMODEINFO));
764     VioSetMode(&vmi, (HVIO)0);
765     VioSetCurPos(cs->CursorY, cs->CursorX, (HVIO)0);
766     VioWrtCellStr((PCH)cs->FrameBuffer, cs->vmi.col * cs->vmi.row * 2,0, 0, (HVIO)0);
767 }
768
769 /****************************************************************************
770 REMARKS:
771 Close the fullscreen console.
772 ****************************************************************************/
773 void PMAPI PM_closeConsole(
774     PM_HWND hwndConsole)
775 {
776     /* Kill the threads responsible for tracking console switches */
777     PM_setSuspendAppCallback(NULL);
778     (void)hwndConsole;
779 }
780
781 /****************************************************************************
782 REMARKS:
783 Set the location of the OS console cursor.
784 ****************************************************************************/
785 void PM_setOSCursorLocation(
786     int x,
787     int y)
788 {
789     /* If session switch is in progress, calling into VIO causes deadlocks! */
790     /* Also this call to VIO screws up our console library on DBCS boxes... */
791     if (!isSessionSwitching && !__IsDBCSSystem())
792         VioSetCurPos(y,x,0);
793 }
794
795 /****************************************************************************
796 REMARKS:
797 Set the width of the OS console.
798 ****************************************************************************/
799 void PM_setOSScreenWidth(
800     int width,
801     int height)
802 {
803     /* Nothing to do in here */
804     (void)width;
805     (void)height;
806 }
807
808 /****************************************************************************
809 REMARKS:
810 Set the real time clock handler (used for software stereo modes).
811 ****************************************************************************/
812 ibool PMAPI PM_setRealTimeClockHandler(
813     PM_intHandler ih,
814     int frequency)
815 {
816     // TODO: Implement this!
817     (void)ih;
818     (void)frequency;
819     return false;
820 }
821
822 /****************************************************************************
823 REMARKS:
824 Set the real time clock frequency (for stereo modes).
825 ****************************************************************************/
826 void PMAPI PM_setRealTimeClockFrequency(
827     int frequency)
828 {
829     // TODO: Implement this!
830     (void)frequency;
831 }
832
833 /****************************************************************************
834 REMARKS:
835 Restore the original real time clock handler.
836 ****************************************************************************/
837 void PMAPI PM_restoreRealTimeClockHandler(void)
838 {
839     // TODO: Implement this!
840 }
841
842 /****************************************************************************
843 REMARKS:
844 Return the current operating system path or working directory.
845 ****************************************************************************/
846 char * PMAPI PM_getCurrentPath(
847     char *path,
848     int maxLen)
849 {
850     return getcwd(path,maxLen);
851 }
852
853 /****************************************************************************
854 REMARKS:
855 Return the drive letter for the boot drive.
856 ****************************************************************************/
857 char PMAPI PM_getBootDrive(void)
858 {
859     ulong   boot = 3;
860     DosQuerySysInfo(QSV_BOOT_DRIVE,QSV_BOOT_DRIVE,&boot,sizeof(boot));
861     return (char)('a' + boot - 1);
862 }
863
864 /****************************************************************************
865 REMARKS:
866 Return the path to the VBE/AF driver files.
867 ****************************************************************************/
868 const char * PMAPI PM_getVBEAFPath(void)
869 {
870     static char path[CCHMAXPATH];
871     strcpy(path,"x:\\");
872     path[0] = PM_getBootDrive();
873     return path;
874 }
875
876 /****************************************************************************
877 REMARKS:
878 Return the path to the Nucleus driver files.
879 ****************************************************************************/
880 const char * PMAPI PM_getNucleusPath(void)
881 {
882     static char path[CCHMAXPATH];
883     if (getenv("NUCLEUS_PATH") != NULL)
884         return getenv("NUCLEUS_PATH");
885     strcpy(path,"x:\\os2\\drivers");
886     path[0] = PM_getBootDrive();
887     PM_backslash(path);
888     strcat(path,"nucleus");
889     return path;
890 }
891
892 /****************************************************************************
893 REMARKS:
894 Return the path to the Nucleus configuration files.
895 ****************************************************************************/
896 const char * PMAPI PM_getNucleusConfigPath(void)
897 {
898     static char path[CCHMAXPATH];
899     strcpy(path,PM_getNucleusPath());
900     PM_backslash(path);
901     strcat(path,"config");
902     return path;
903 }
904
905 /****************************************************************************
906 REMARKS:
907 Return a unique identifier for the machine if possible.
908 ****************************************************************************/
909 const char * PMAPI PM_getUniqueID(void)
910 {
911     return PM_getMachineName();
912 }
913
914 /****************************************************************************
915 REMARKS:
916 Get the name of the machine on the network.
917 ****************************************************************************/
918 const char * PMAPI PM_getMachineName(void)
919 {
920     static char name[40],*env;
921
922     if ((env = getenv("HOSTNAME")) != NULL) {
923         strncpy(name,env,sizeof(name));
924         name[sizeof(name)-1] = 0;
925         return name;
926         }
927     return "OS2";
928 }
929
930 /****************************************************************************
931 REMARKS:
932 Return a pointer to the real mode BIOS data area.
933 ****************************************************************************/
934 void * PMAPI PM_getBIOSPointer(void)
935 {
936     PM_init();
937     return lowMem + 0x400;
938 }
939
940 /****************************************************************************
941 REMARKS:
942 Return a pointer to 0xA0000 physical VGA graphics framebuffer.
943 ****************************************************************************/
944 void * PMAPI PM_getA0000Pointer(void)
945 {
946     PM_init();
947     return lowMem + 0xA0000;
948 }
949
950 /****************************************************************************
951 REMARKS:
952 Map a physical address to a linear address in the callers process.
953 ****************************************************************************/
954 void * PMAPI PM_mapPhysicalAddr(
955     ulong base,
956     ulong limit,
957     ibool isCached)
958 {
959     ulong   baseAddr,baseOfs,linear;
960
961     /* Round the physical address to a 4Kb boundary and the limit to a
962      * 4Kb-1 boundary before passing the values to mmap. If we round the
963      * physical address, then we also add an extra offset into the address
964      * that we return.
965      */
966     baseOfs = base & 4095;
967     baseAddr = base & ~4095;
968     limit = ((limit+baseOfs+1+4095) & ~4095)-1;
969     parmsIn[0] = baseAddr;
970     parmsIn[1] = limit;
971     parmsIn[2] = isCached;
972     if ((linear = CallSDDHelp(PMHELP_MAPPHYS)) == 0)
973         return NULL;
974     return (void*)(linear + baseOfs);
975 }
976
977 /****************************************************************************
978 REMARKS:
979 Free a physical address mapping allocated by PM_mapPhysicalAddr.
980 ****************************************************************************/
981 void PMAPI PM_freePhysicalAddr(
982     void *ptr,
983     ulong limit)
984 {
985     parmsIn[0] = (ulong)ptr;
986     parmsIn[1] = limit;
987     CallSDDHelp(PMHELP_FREEPHYS);
988 }
989
990 /****************************************************************************
991 REMARKS:
992 Find the physical address of a linear memory address in current process.
993 ****************************************************************************/
994 ulong PMAPI PM_getPhysicalAddr(
995     void *p)
996 {
997     parmsIn[0] = (ulong)p;
998     return CallSDDHelp(PMHELP_GETPHYSICALADDR);
999 }
1000
1001 /****************************************************************************
1002 REMARKS:
1003 Find the physical address of a linear memory address in current process.
1004 ****************************************************************************/
1005 ibool PMAPI PM_getPhysicalAddrRange(
1006     void *p,
1007     ulong length,
1008     ulong *physAddress)
1009 {
1010     parmsIn[0] = (ulong)p;
1011     parmsIn[1] = (ulong)length;
1012     parmsIn[2] = (ulong)physAddress;
1013     return CallSDDHelp(PMHELP_GETPHYSICALADDRRANGE);
1014 }
1015
1016 /****************************************************************************
1017 REMARKS:
1018 Sleep for the specified number of milliseconds.
1019 ****************************************************************************/
1020 void PMAPI PM_sleep(
1021     ulong milliseconds)
1022 {
1023     DosSleep(milliseconds);
1024 }
1025
1026 /****************************************************************************
1027 REMARKS:
1028 Return the base I/O port for the specified COM port.
1029 ****************************************************************************/
1030 int PMAPI PM_getCOMPort(
1031     int port)
1032 {
1033     switch (port) {
1034         case 0: return 0x3F8;
1035         case 1: return 0x2F8;
1036         }
1037     return 0;
1038 }
1039
1040 /****************************************************************************
1041 REMARKS:
1042 Return the base I/O port for the specified LPT port.
1043 ****************************************************************************/
1044 int PMAPI PM_getLPTPort(
1045     int port)
1046 {
1047     switch (port) {
1048         case 0: return 0x3BC;
1049         case 1: return 0x378;
1050         case 2: return 0x278;
1051         }
1052     return 0;
1053 }
1054
1055 /****************************************************************************
1056 REMARKS:
1057 Allocate a block of shared memory. For Win9x we allocate shared memory
1058 as locked, global memory that is accessible from any memory context
1059 (including interrupt time context), which allows us to load our important
1060 data structure and code such that we can access it directly from a ring
1061 0 interrupt context.
1062 ****************************************************************************/
1063 void * PMAPI PM_mallocShared(
1064     long size)
1065 {
1066     parmsIn[0] = size;
1067     return (void*)CallSDDHelp(PMHELP_MALLOCSHARED);
1068 }
1069
1070 /****************************************************************************
1071 REMARKS:
1072 Free a block of shared memory.
1073 ****************************************************************************/
1074 void PMAPI PM_freeShared(
1075     void *ptr)
1076 {
1077     parmsIn[0] = (ulong)ptr;
1078     CallSDDHelp(PMHELP_FREESHARED);
1079 }
1080
1081 /****************************************************************************
1082 REMARKS:
1083 Map a linear memory address to the calling process address space. The
1084 address will have been allocated in another process using the
1085 PM_mapPhysicalAddr function.
1086 ****************************************************************************/
1087 void * PMAPI PM_mapToProcess(
1088     void *base,
1089     ulong limit)
1090 {
1091     ulong   baseAddr,baseOfs;
1092
1093     /* Round the physical address to a 4Kb boundary and the limit to a
1094      * 4Kb-1 boundary before passing the values to mmap. If we round the
1095      * physical address, then we also add an extra offset into the address
1096      * that we return.
1097      */
1098     baseOfs = (ulong)base & 4095;
1099     baseAddr = (ulong)base & ~4095;
1100     limit = ((limit+baseOfs+1+4095) & ~4095)-1;
1101     parmsIn[0] = (ulong)baseAddr;
1102     parmsIn[1] = limit;
1103     return (void*)(CallSDDHelp(PMHELP_MAPTOPROCESS)+baseOfs);
1104 }
1105
1106 /****************************************************************************
1107 REMARKS:
1108 Map a real mode pointer to a protected mode pointer.
1109 ****************************************************************************/
1110 void * PMAPI PM_mapRealPointer(
1111     uint r_seg,
1112     uint r_off)
1113 {
1114     if (r_seg == 0xFFFF)
1115         return &RMBuf[r_off];
1116     return lowMem + MK_PHYS(r_seg,r_off);
1117 }
1118
1119 /****************************************************************************
1120 REMARKS:
1121 Allocate a block of real mode memory
1122 ****************************************************************************/
1123 void * PMAPI PM_allocRealSeg(
1124     uint size,
1125     uint *r_seg,
1126     uint *r_off)
1127 {
1128     if (size > sizeof(RMBuf))
1129         return NULL;
1130     *r_seg = 0xFFFF;
1131     *r_off = 0x0000;
1132     return &RMBuf;
1133 }
1134
1135 /****************************************************************************
1136 REMARKS:
1137 Free a block of real mode memory.
1138 ****************************************************************************/
1139 void PMAPI PM_freeRealSeg(
1140     void *mem)
1141 {
1142     /* Nothing to do in here */
1143     (void)mem;
1144 }
1145
1146 #define INDPMI(reg)     rmregs.aCRF.reg_##reg = regs->reg
1147 #define OUTDPMI(reg)    regs->reg = rmregs.aCRF.reg_##reg
1148
1149 #define REG_OFFSET(field)  (((ULONG)&(((VCRF*)0)->field))  / sizeof(ULONG))
1150
1151 /****************************************************************************
1152 REMARKS:
1153 Issue a real mode interrupt (parameters in DPMI compatible structure)
1154 ****************************************************************************/
1155 void PMAPI DPMI_int86(
1156     int intno,
1157     DPMI_regs *regs)
1158 {
1159     INTCRF  rmregs;
1160     ulong   eax = 0;
1161
1162     if (!InitInt10())
1163         return;
1164     memset(&rmregs, 0, sizeof(rmregs));
1165     rmregs.ulBIOSIntNo = intno;
1166     INDPMI(eax); INDPMI(ebx); INDPMI(ecx); INDPMI(edx); INDPMI(esi); INDPMI(edi);
1167     rmregs.aCRF.reg_ds = regs->ds;
1168     rmregs.aCRF.reg_es = regs->es;
1169     if (intno == 0x10) {
1170         eax = rmregs.aCRF.reg_eax;
1171         switch (eax & 0xFFFF) {
1172             case 0x4F00:
1173                 /* We have to hack the way this function works, due to
1174                  * some bugs in the IBM mini-VDM BIOS support. Specifically
1175                  * we need to make the input buffer and output buffer the
1176                  * 'same' buffer, and that ES:SI points to the output
1177                  * buffer (ignored by the BIOS). The data will end up
1178                  * being returned in the input buffer, except for the
1179                  * first four bytes ('VESA') that will not be returned.
1180                  */
1181                 rmregs.pB[0].bBufferType = INPUT_BUFFER;
1182                 rmregs.pB[0].bSelCRF = REG_OFFSET(reg_es);
1183                 rmregs.pB[0].bOffCRF = REG_OFFSET(reg_edi);
1184                 rmregs.pB[0].pAddress = RMBuf;
1185                 rmregs.pB[0].ulSize = 4;
1186                 rmregs.pB[1].bBufferType = OUTPUT_BUFFER;
1187                 rmregs.pB[1].bSelCRF = REG_OFFSET(reg_es);
1188                 rmregs.pB[1].bOffCRF = REG_OFFSET(reg_esi);
1189                 rmregs.pB[1].pAddress = ((PBYTE)RMBuf)+4;
1190                 rmregs.pB[1].ulSize = 512-4;
1191                 break;
1192             case 0x4F01:
1193                 rmregs.pB[0].bBufferType = OUTPUT_BUFFER;
1194                 rmregs.pB[0].bSelCRF = REG_OFFSET(reg_es);
1195                 rmregs.pB[0].bOffCRF = REG_OFFSET(reg_edi);
1196                 rmregs.pB[0].pAddress = RMBuf;
1197                 rmregs.pB[0].ulSize = 256;
1198                 break;
1199             case 0x4F02:
1200                 rmregs.pB[0].bBufferType = INPUT_BUFFER;
1201                 rmregs.pB[0].bSelCRF = REG_OFFSET(reg_es);
1202                 rmregs.pB[0].bOffCRF = REG_OFFSET(reg_edi);
1203                 rmregs.pB[0].pAddress = RMBuf;
1204                 rmregs.pB[0].ulSize = 256;
1205                 break;
1206             case 0x4F09:
1207                 rmregs.pB[0].bBufferType = INPUT_BUFFER;
1208                 rmregs.pB[0].bSelCRF = REG_OFFSET(reg_es);
1209                 rmregs.pB[0].bOffCRF = REG_OFFSET(reg_edi);
1210                 rmregs.pB[0].pAddress = RMBuf;
1211                 rmregs.pB[0].ulSize = 1024;
1212                 break;
1213             case 0x4F0A:
1214                 /* Due to bugs in the mini-VDM in OS/2, the 0x4F0A protected
1215                  * mode interface functions will not work (we never get any
1216                  * selectors returned), so we fail this function here. The
1217                  * rest of the VBE/Core driver will work properly if this
1218                  * function is failed, because the VBE 2.0 and 3.0 specs
1219                  * allow for this.
1220                  */
1221                 regs->eax = 0x014F;
1222                 return;
1223             }
1224         }
1225     if (useVPMI)
1226         PM_VIDEOPMI32Request(&Adapter,PMIREQUEST_SOFTWAREINT,NULL,&rmregs);
1227     else {
1228         DosSysCtl(6, &rmregs);
1229         }
1230
1231     OUTDPMI(eax); OUTDPMI(ebx); OUTDPMI(ecx); OUTDPMI(edx); OUTDPMI(esi); OUTDPMI(edi);
1232     if (((regs->eax & 0xFFFF) == 0x004F) && ((eax & 0xFFFF) == 0x4F00)) {
1233         /* Hack to fix up the missing 'VESA' string for mini-VDM */
1234         memcpy(RMBuf,"VESA",4);
1235         }
1236     regs->ds = rmregs.aCRF.reg_ds;
1237     regs->es = rmregs.aCRF.reg_es;
1238     regs->flags = rmregs.aCRF.reg_eflag;
1239 }
1240
1241 #define IN(reg)     rmregs.reg = in->e.reg
1242 #define OUT(reg)    out->e.reg = rmregs.reg
1243
1244 /****************************************************************************
1245 REMARKS:
1246 Issue a real mode interrupt.
1247 ****************************************************************************/
1248 int PMAPI PM_int86(
1249     int intno,
1250     RMREGS *in,
1251     RMREGS *out)
1252 {
1253     DPMI_regs   rmregs;
1254
1255     memset(&rmregs, 0, sizeof(rmregs));
1256     IN(eax); IN(ebx); IN(ecx); IN(edx); IN(esi); IN(edi);
1257     DPMI_int86(intno,&rmregs);
1258     OUT(eax); OUT(ebx); OUT(ecx); OUT(edx); OUT(esi); OUT(edi);
1259     out->x.cflag = rmregs.flags & 0x1;
1260     return out->x.ax;
1261 }
1262
1263 /****************************************************************************
1264 REMARKS:
1265 Issue a real mode interrupt.
1266 ****************************************************************************/
1267 int PMAPI PM_int86x(
1268     int intno,
1269     RMREGS *in,
1270     RMREGS *out,
1271     RMSREGS *sregs)
1272 {
1273     DPMI_regs   rmregs;
1274
1275     memset(&rmregs, 0, sizeof(rmregs));
1276     IN(eax); IN(ebx); IN(ecx); IN(edx); IN(esi); IN(edi);
1277     rmregs.es = sregs->es;
1278     rmregs.ds = sregs->ds;
1279     DPMI_int86(intno,&rmregs);
1280     OUT(eax); OUT(ebx); OUT(ecx); OUT(edx); OUT(esi); OUT(edi);
1281     sregs->es = rmregs.es;
1282     sregs->cs = rmregs.cs;
1283     sregs->ss = rmregs.ss;
1284     sregs->ds = rmregs.ds;
1285     out->x.cflag = rmregs.flags & 0x1;
1286     return out->x.ax;
1287 }
1288
1289 /****************************************************************************
1290 REMARKS:
1291 Call a real mode far function.
1292 ****************************************************************************/
1293 void PMAPI PM_callRealMode(
1294     uint seg,
1295     uint off,
1296     RMREGS *in,
1297     RMSREGS *sregs)
1298 {
1299     PM_fatalError("PM_callRealMode not supported on OS/2!");
1300 }
1301
1302 /****************************************************************************
1303 REMARKS:
1304 Return the amount of available memory.
1305 ****************************************************************************/
1306 void PMAPI PM_availableMemory(
1307     ulong *physical,
1308     ulong *total)
1309 {
1310     /* Unable to get reliable values from OS/2 for this */
1311     *physical = *total = 0;
1312 }
1313
1314 /****************************************************************************
1315 REMARKS:
1316 Allocate a block of locked, physical memory for DMA operations.
1317 ****************************************************************************/
1318 void * PMAPI PM_allocLockedMem(
1319     uint size,
1320     ulong *physAddr,
1321     ibool contiguous,
1322     ibool below16M)
1323 {
1324     parmsIn[0] = size;
1325     parmsIn[1] = contiguous;
1326     parmsIn[2] = below16M;
1327     CallSDDHelp(PMHELP_ALLOCLOCKED);
1328     *physAddr = parmsOut[1];
1329     return (void*)parmsOut[0];
1330 }
1331
1332 /****************************************************************************
1333 REMARKS:
1334 Free a block of locked physical memory.
1335 ****************************************************************************/
1336 void PMAPI PM_freeLockedMem(
1337     void *p,
1338     uint size,
1339     ibool contiguous)
1340 {
1341     parmsIn[0] = (ulong)p;
1342     CallSDDHelp(PMHELP_FREELOCKED);
1343 }
1344
1345 /****************************************************************************
1346 REMARKS:
1347 Allocates a new block of pages for the page block manager.
1348 ****************************************************************************/
1349 static pageblock *PM_addNewPageBlock(void)
1350 {
1351     int         i;
1352     pageblock   *newBlock;
1353     char        *p,*next;
1354
1355     /* Allocate memory for the new page block, and add to head of list */
1356     if (DosAllocSharedMem((void**)&newBlock,NULL,PAGE_BLOCK_SIZE,OBJ_GETTABLE | PAG_READ | PAG_WRITE | PAG_COMMIT))
1357         return NULL;
1358     if (!PM_lockDataPages(newBlock,PAGE_BLOCK_SIZE,&newBlock->lockHandle))
1359         return NULL;
1360     newBlock->prev = NULL;
1361     newBlock->next = pageBlocks;
1362     if (pageBlocks)
1363         pageBlocks->prev = newBlock;
1364     pageBlocks = newBlock;
1365
1366     /* Initialise the page aligned free list for the page block */
1367     newBlock->freeCount = PAGES_PER_BLOCK;
1368     newBlock->freeList = p = (char*)(((ulong)(newBlock + 1) + (PM_PAGE_SIZE-1)) & ~(PM_PAGE_SIZE-1));
1369     newBlock->freeListStart = newBlock->freeList;
1370     newBlock->freeListEnd = p + (PAGES_PER_BLOCK-1) * PM_PAGE_SIZE;
1371     for (i = 0; i < PAGES_PER_BLOCK; i++,p = next)
1372         FREELIST_NEXT(p) = next = p + PM_PAGE_SIZE;
1373     FREELIST_NEXT(p - PM_PAGE_SIZE) = NULL;
1374     return newBlock;
1375 }
1376
1377 /****************************************************************************
1378 REMARKS:
1379 Allocates a page aligned and page sized block of memory
1380 ****************************************************************************/
1381 void * PMAPI PM_allocPage(
1382     ibool locked)
1383 {
1384     pageblock   *block;
1385     void        *p;
1386
1387     /* Scan the block list looking for any free blocks. Allocate a new
1388      * page block if no free blocks are found.
1389      */
1390     for (block = pageBlocks; block != NULL; block = block->next) {
1391         if (block->freeCount)
1392             break;
1393         }
1394     if (block == NULL && (block = PM_addNewPageBlock()) == NULL)
1395         return NULL;
1396     block->freeCount--;
1397     p = block->freeList;
1398     block->freeList = FREELIST_NEXT(p);
1399     (void)locked;
1400     return p;
1401 }
1402
1403 /****************************************************************************
1404 REMARKS:
1405 Free a page aligned and page sized block of memory
1406 ****************************************************************************/
1407 void PMAPI PM_freePage(
1408     void *p)
1409 {
1410     pageblock   *block;
1411
1412     /* First find the page block that this page belongs to */
1413     for (block = pageBlocks; block != NULL; block = block->next) {
1414         if (p >= block->freeListStart && p <= block->freeListEnd)
1415             break;
1416         }
1417     CHECK(block != NULL);
1418
1419     /* Now free the block by adding it to the free list */
1420     FREELIST_NEXT(p) = block->freeList;
1421     block->freeList = p;
1422     if (++block->freeCount == PAGES_PER_BLOCK) {
1423         /* If all pages in the page block are now free, free the entire
1424          * page block itself.
1425          */
1426         if (block == pageBlocks) {
1427             /* Delete from head */
1428             pageBlocks = block->next;
1429             if (block->next)
1430                 block->next->prev = NULL;
1431             }
1432         else {
1433             /* Delete from middle of list */
1434             CHECK(block->prev != NULL);
1435             block->prev->next = block->next;
1436             if (block->next)
1437                 block->next->prev = block->prev;
1438             }
1439
1440         /* Unlock the memory and free it */
1441         PM_unlockDataPages(block,PAGE_BLOCK_SIZE,&block->lockHandle);
1442         DosFreeMem(block);
1443         }
1444 }
1445
1446 /****************************************************************************
1447 REMARKS:
1448 Map in all the shared memory blocks for managing the memory pages above.
1449 ****************************************************************************/
1450 void PMAPI PM_mapSharedPages(void)
1451 {
1452     pageblock   *block;
1453
1454     /* Map all the page blocks above into the shared memory for process */
1455     for (block = pageBlocks; block != NULL; block = block->next) {
1456         DosGetSharedMem(block, PAG_READ | PAG_WRITE);
1457         }
1458 }
1459
1460 /****************************************************************************
1461 REMARKS:
1462 Lock linear memory so it won't be paged.
1463 ****************************************************************************/
1464 int PMAPI PM_lockDataPages(
1465     void *p,
1466     uint len,
1467     PM_lockHandle *lockHandle)
1468 {
1469     parmsIn[0] = (ulong)p;
1470     parmsIn[1] = len;
1471     CallSDDHelp(PMHELP_LOCKPAGES);
1472     lockHandle->h[0] = parmsOut[1];
1473     lockHandle->h[1] = parmsOut[2];
1474     lockHandle->h[2] = parmsOut[3];
1475     return parmsOut[0];
1476 }
1477
1478 /****************************************************************************
1479 REMARKS:
1480 Unlock linear memory so it won't be paged.
1481 ****************************************************************************/
1482 int PMAPI PM_unlockDataPages(
1483     void *p,
1484     uint len,
1485     PM_lockHandle *lockHandle)
1486 {
1487     parmsIn[0] = lockHandle->h[0];
1488     parmsIn[1] = lockHandle->h[1];
1489     parmsIn[2] = lockHandle->h[2];
1490     return CallSDDHelp(PMHELP_UNLOCKPAGES);
1491 }
1492
1493 /****************************************************************************
1494 REMARKS:
1495 Lock linear memory so it won't be paged.
1496 ****************************************************************************/
1497 int PMAPI PM_lockCodePages(
1498     void (*p)(),
1499     uint len,
1500     PM_lockHandle *lockHandle)
1501 {
1502     parmsIn[0] = (ulong)p;
1503     parmsIn[1] = len;
1504     CallSDDHelp(PMHELP_LOCKPAGES);
1505     lockHandle->h[0] = parmsOut[1];
1506     lockHandle->h[1] = parmsOut[2];
1507     lockHandle->h[2] = parmsOut[3];
1508     return parmsOut[0];
1509 }
1510
1511 /****************************************************************************
1512 REMARKS:
1513 Unlock linear memory so it won't be paged.
1514 ****************************************************************************/
1515 int PMAPI PM_unlockCodePages(
1516     void (*p)(),
1517     uint len,
1518     PM_lockHandle *lockHandle)
1519 {
1520     parmsIn[0] = lockHandle->h[0];
1521     parmsIn[1] = lockHandle->h[1];
1522     parmsIn[2] = lockHandle->h[2];
1523     return CallSDDHelp(PMHELP_UNLOCKPAGES);
1524 }
1525
1526 /****************************************************************************
1527 REMARKS:
1528 Call the VBE/Core software interrupt to change display banks.
1529 ****************************************************************************/
1530 void PMAPI PM_setBankA(
1531     int bank)
1532 {
1533     INTCRF  rmregs;
1534
1535     if (!InitInt10())
1536         return;
1537     memset(&rmregs, 0, sizeof(rmregs));
1538     rmregs.ulBIOSIntNo = 0x10;
1539     rmregs.aCRF.reg_eax = 0x4F05;
1540     rmregs.aCRF.reg_ebx = 0x0000;
1541     rmregs.aCRF.reg_edx = bank;
1542     PM_VIDEOPMI32Request(&Adapter,PMIREQUEST_SOFTWAREINT,&rmregs,NULL);
1543 }
1544
1545 /****************************************************************************
1546 REMARKS:
1547 Call the VBE/Core software interrupt to change display banks.
1548 ****************************************************************************/
1549 void PMAPI PM_setBankAB(
1550     int bank)
1551 {
1552     INTCRF  rmregs;
1553
1554     if (!InitInt10())
1555         return;
1556     memset(&rmregs, 0, sizeof(rmregs));
1557     rmregs.ulBIOSIntNo = 0x10;
1558     rmregs.aCRF.reg_eax = 0x4F05;
1559     rmregs.aCRF.reg_ebx = 0x0000;
1560     rmregs.aCRF.reg_edx = bank;
1561     PM_VIDEOPMI32Request(&Adapter,PMIREQUEST_SOFTWAREINT,&rmregs,NULL);
1562     rmregs.ulBIOSIntNo = 0x10;
1563     rmregs.aCRF.reg_eax = 0x4F05;
1564     rmregs.aCRF.reg_ebx = 0x0001;
1565     rmregs.aCRF.reg_edx = bank;
1566     PM_VIDEOPMI32Request(&Adapter,PMIREQUEST_SOFTWAREINT,&rmregs,NULL);
1567 }
1568
1569 /****************************************************************************
1570 REMARKS:
1571 Call the VBE/Core software interrupt to change display start address.
1572 ****************************************************************************/
1573 void PMAPI PM_setCRTStart(
1574     int x,
1575     int y,
1576     int waitVRT)
1577 {
1578     INTCRF  rmregs;
1579
1580     if (!InitInt10())
1581         return;
1582     memset(&rmregs, 0, sizeof(rmregs));
1583     rmregs.ulBIOSIntNo = 0x10;
1584     rmregs.aCRF.reg_eax = 0x4F07;
1585     rmregs.aCRF.reg_ebx = waitVRT;
1586     rmregs.aCRF.reg_ecx = x;
1587     rmregs.aCRF.reg_edx = y;
1588     PM_VIDEOPMI32Request(&Adapter,PMIREQUEST_SOFTWAREINT,&rmregs,NULL);
1589 }
1590
1591 /****************************************************************************
1592 REMARKS:
1593 Execute the POST on the secondary BIOS for a controller.
1594 ****************************************************************************/
1595 ibool PMAPI PM_doBIOSPOST(
1596     ushort axVal,
1597     ulong BIOSPhysAddr,
1598     void *mappedBIOS,
1599     ulong BIOSLen)
1600 {
1601     (void)axVal;
1602     (void)BIOSPhysAddr;
1603     (void)mappedBIOS;
1604     (void)BIOSLen;
1605     return false;
1606 }
1607
1608 /****************************************************************************
1609 PARAMETERS:
1610 base    - The starting physical base address of the region
1611 size    - The size in bytes of the region
1612 type    - Type to place into the MTRR register
1613
1614 RETURNS:
1615 Error code describing the result.
1616
1617 REMARKS:
1618 Function to enable write combining for the specified region of memory.
1619 ****************************************************************************/
1620 int PMAPI PM_enableWriteCombine(
1621     ulong base,
1622     ulong size,
1623     uint type)
1624 {
1625     return MTRR_enableWriteCombine(base,size,type);
1626 }
1627
1628 // TODO: Move the MTRR helper stuff into the call gate, or better yet
1629 //       entirely into the ring 0 helper driver!!
1630
1631 /* MTRR helper functions. To make it easier to implement the MTRR support
1632  * under OS/2, we simply put our ring 0 helper functions into the
1633  * helper device driver rather than the entire MTRR module. This makes
1634  * it easier to maintain the MTRR support since we don't need to deal
1635  * with 16-bit ring 0 code in the MTRR library.
1636  */
1637
1638 /****************************************************************************
1639 REMARKS:
1640 Flush the translation lookaside buffer.
1641 ****************************************************************************/
1642 void PMAPI PM_flushTLB(void)
1643 {
1644     CallSDDHelp(PMHELP_FLUSHTLB);
1645 }
1646
1647 /****************************************************************************
1648 REMARKS:
1649 Return true if ring 0 (or if we can call the helpers functions at ring 0)
1650 ****************************************************************************/
1651 ibool _ASMAPI _MTRR_isRing0(void)
1652 {
1653     return true;
1654 }
1655
1656 /****************************************************************************
1657 REMARKS:
1658 Read and return the value of the CR4 register
1659 ****************************************************************************/
1660 ulong _ASMAPI _MTRR_saveCR4(void)
1661 {
1662     return CallSDDHelp(PMHELP_SAVECR4);
1663 }
1664
1665 /****************************************************************************
1666 REMARKS:
1667 Restore the value of the CR4 register
1668 ****************************************************************************/
1669 void _ASMAPI _MTRR_restoreCR4(ulong cr4Val)
1670 {
1671     parmsIn[0] = cr4Val;
1672     CallSDDHelp(PMHELP_RESTORECR4);
1673 }
1674
1675 /****************************************************************************
1676 REMARKS:
1677 Read a machine status register for the CPU.
1678 ****************************************************************************/
1679 void _ASMAPI _MTRR_readMSR(
1680     ulong reg,
1681     ulong *eax,
1682     ulong *edx)
1683 {
1684     parmsIn[0] = reg;
1685     CallSDDHelp(PMHELP_READMSR);
1686     *eax = parmsOut[0];
1687     *edx = parmsOut[1];
1688 }
1689
1690 /****************************************************************************
1691 REMARKS:
1692 Write a machine status register for the CPU.
1693 ****************************************************************************/
1694 void _ASMAPI _MTRR_writeMSR(
1695     ulong reg,
1696     ulong eax,
1697     ulong edx)
1698 {
1699     parmsIn[0] = reg;
1700     parmsIn[1] = eax;
1701     parmsIn[2] = edx;
1702     CallSDDHelp(PMHELP_WRITEMSR);
1703 }
1704
1705 PM_MODULE PMAPI PM_loadLibrary(
1706     const char *szDLLName)
1707 {
1708     // TODO: Implement this to load shared libraries!
1709     (void)szDLLName;
1710     return NULL;
1711 }
1712
1713 void * PMAPI PM_getProcAddress(
1714     PM_MODULE hModule,
1715     const char *szProcName)
1716 {
1717     // TODO: Implement this!
1718     (void)hModule;
1719     (void)szProcName;
1720     return NULL;
1721 }
1722
1723 void PMAPI PM_freeLibrary(
1724     PM_MODULE hModule)
1725 {
1726     // TODO: Implement this!
1727     (void)hModule;
1728 }
1729
1730 /****************************************************************************
1731 REMARKS:
1732 Internal function to convert the find data to the generic interface.
1733 ****************************************************************************/
1734 static void convertFindData(
1735     PM_findData *findData,
1736     FILEFINDBUF3 *blk)
1737 {
1738     ulong   dwSize = findData->dwSize;
1739
1740     memset(findData,0,findData->dwSize);
1741     findData->dwSize = dwSize;
1742     if (blk->attrFile & FILE_READONLY)
1743         findData->attrib |= PM_FILE_READONLY;
1744     if (blk->attrFile & FILE_DIRECTORY)
1745         findData->attrib |= PM_FILE_DIRECTORY;
1746     if (blk->attrFile & FILE_ARCHIVED)
1747         findData->attrib |= PM_FILE_ARCHIVE;
1748     if (blk->attrFile & FILE_HIDDEN)
1749         findData->attrib |= PM_FILE_HIDDEN;
1750     if (blk->attrFile & FILE_SYSTEM)
1751         findData->attrib |= PM_FILE_SYSTEM;
1752     findData->sizeLo = blk->cbFile;
1753     findData->sizeHi = 0;
1754     strncpy(findData->name,blk->achName,PM_MAX_PATH);
1755     findData->name[PM_MAX_PATH-1] = 0;
1756 }
1757
1758 #define FIND_MASK   (FILE_ARCHIVED | FILE_DIRECTORY | FILE_SYSTEM | FILE_HIDDEN | FILE_READONLY)
1759
1760 /****************************************************************************
1761 REMARKS:
1762 Function to find the first file matching a search criteria in a directory.
1763 ****************************************************************************/
1764 void *PMAPI PM_findFirstFile(
1765     const char *filename,
1766     PM_findData *findData)
1767 {
1768     FILEFINDBUF3    blk;
1769     HDIR            hdir = HDIR_CREATE;
1770     ulong           count = 1;
1771
1772     if (DosFindFirst((PSZ)filename,&hdir,FIND_MASK,&blk,sizeof(blk),&count,FIL_STANDARD) == NO_ERROR) {
1773         convertFindData(findData,&blk);
1774         return (void*)hdir;
1775         }
1776     return PM_FILE_INVALID;
1777 }
1778
1779 /****************************************************************************
1780 REMARKS:
1781 Function to find the next file matching a search criteria in a directory.
1782 ****************************************************************************/
1783 ibool PMAPI PM_findNextFile(
1784     void *handle,
1785     PM_findData *findData)
1786 {
1787     FILEFINDBUF3    blk;
1788     ulong           count = 1;
1789
1790     if (DosFindNext((HDIR)handle,&blk,sizeof(blk),&count) == NO_ERROR) {
1791         convertFindData(findData,&blk);
1792         return true;
1793         }
1794     return false;
1795 }
1796
1797 /****************************************************************************
1798 REMARKS:
1799 Function to close the find process
1800 ****************************************************************************/
1801 void PMAPI PM_findClose(
1802     void *handle)
1803 {
1804     DosFindClose((HDIR)handle);
1805 }
1806
1807 /****************************************************************************
1808 REMARKS:
1809 Function to determine if a drive is a valid drive or not. Under Unix this
1810 function will return false for anything except a value of 3 (considered
1811 the root drive, and equivalent to C: for non-Unix systems). The drive
1812 numbering is:
1813
1814     0   - Current drive
1815     1   - Drive A:
1816     2   - Drive B:
1817     3   - Drive C:
1818     etc
1819
1820 ****************************************************************************/
1821 ibool PMAPI PM_driveValid(
1822     char drive)
1823 {
1824     ulong   cntDisk,cntDriveMap;
1825     ibool   valid;
1826
1827     DosQueryCurrentDisk(&cntDisk,&cntDriveMap);
1828     valid = (DosSetDefaultDisk(drive) == NO_ERROR);
1829     DosSetDefaultDisk(cntDisk);
1830     return valid;
1831 }
1832
1833 /****************************************************************************
1834 REMARKS:
1835 Function to get the current working directory for the specififed drive.
1836 Under Unix this will always return the current working directory regardless
1837 of what the value of 'drive' is.
1838 ****************************************************************************/
1839 void PMAPI PM_getdcwd(
1840     int drive,
1841     char *dir,
1842     int len)
1843 {
1844     ulong length = len;
1845
1846     DosQueryCurrentDir(drive, (PSZ)dir, &length);
1847 }
1848
1849 /****************************************************************************
1850 REMARKS:
1851 Function to change the file attributes for a specific file.
1852 ****************************************************************************/
1853 void PMAPI PM_setFileAttr(
1854     const char *filename,
1855     uint attrib)
1856 {
1857     FILESTATUS3 s;
1858
1859     if (DosQueryPathInfo((PSZ)filename,FIL_STANDARD,(PVOID)&s,sizeof(s)))
1860         return;
1861     s.attrFile = 0;
1862     if (attrib & PM_FILE_READONLY)
1863         s.attrFile |= FILE_READONLY;
1864     if (attrib & PM_FILE_ARCHIVE)
1865         s.attrFile |= FILE_ARCHIVED;
1866     if (attrib & PM_FILE_HIDDEN)
1867         s.attrFile |= FILE_HIDDEN;
1868     if (attrib & PM_FILE_SYSTEM)
1869         s.attrFile |= FILE_SYSTEM;
1870     DosSetPathInfo((PSZ)filename,FIL_STANDARD,(PVOID)&s,sizeof(s),0L);
1871 }
1872
1873 /****************************************************************************
1874 REMARKS:
1875 Function to get the file attributes for a specific file.
1876 ****************************************************************************/
1877 uint PMAPI PM_getFileAttr(
1878     const char *filename)
1879 {
1880     FILESTATUS3 fs3;
1881     uint        retval = 0;
1882
1883     if (DosQueryPathInfo((PSZ)filename, FIL_STANDARD, &fs3, sizeof(FILESTATUS3)))
1884         return 0;
1885     if (fs3.attrFile & FILE_READONLY)
1886         retval |= PM_FILE_READONLY;
1887     if (fs3.attrFile & FILE_ARCHIVED)
1888         retval |= PM_FILE_ARCHIVE;
1889     if (fs3.attrFile & FILE_HIDDEN)
1890         retval |= PM_FILE_HIDDEN;
1891     if (fs3.attrFile & FILE_SYSTEM)
1892         retval |= PM_FILE_SYSTEM;
1893     return retval;
1894 }
1895
1896 /****************************************************************************
1897 REMARKS:
1898 Function to create a directory.
1899 ****************************************************************************/
1900 ibool PMAPI PM_mkdir(
1901     const char *filename)
1902 {
1903     return DosCreateDir((PSZ)filename,NULL) == NO_ERROR;
1904 }
1905
1906 /****************************************************************************
1907 REMARKS:
1908 Function to remove a directory.
1909 ****************************************************************************/
1910 ibool PMAPI PM_rmdir(
1911     const char *filename)
1912 {
1913     return DosDeleteDir((PSZ)filename) == NO_ERROR;
1914 }
1915
1916 /****************************************************************************
1917 REMARKS:
1918 Function to get the file time and date for a specific file.
1919 ****************************************************************************/
1920 ibool PMAPI PM_getFileTime(
1921     const char *filename,
1922     ibool gmTime,
1923     PM_time *time)
1924 {
1925     FILESTATUS3 fs3;
1926     struct tm   tc;
1927     struct tm   *ret;
1928     time_t      tt;
1929
1930     if (DosQueryPathInfo((PSZ)filename, FIL_STANDARD, &fs3, sizeof(FILESTATUS3)))
1931         return false;
1932     if (gmTime) {
1933         tc.tm_year = fs3.fdateLastWrite.year + 80;
1934         tc.tm_mon = fs3.fdateLastWrite.month - 1;
1935         tc.tm_mday = fs3.fdateLastWrite.day;
1936         tc.tm_hour = fs3.ftimeLastWrite.hours;
1937         tc.tm_min = fs3.ftimeLastWrite.minutes;
1938         tc.tm_sec = fs3.ftimeLastWrite.twosecs * 2;
1939         if((tt = mktime(&tc)) == -1)
1940             return false;
1941         if(!(ret = gmtime(&tt)))
1942             return false;
1943         time->sec = ret->tm_sec;
1944         time->day = ret->tm_mday;
1945         time->mon = ret->tm_mon + 1;
1946         time->year = ret->tm_year - 80;
1947         time->min = ret->tm_min;
1948         time->hour = ret->tm_hour;
1949         }
1950     else {
1951         time->sec = fs3.ftimeLastWrite.twosecs * 2;
1952         time->day = fs3.fdateLastWrite.day;
1953         time->mon = fs3.fdateLastWrite.month;
1954         time->year = fs3.fdateLastWrite.year;
1955         time->min = fs3.ftimeLastWrite.minutes;
1956         time->hour = fs3.ftimeLastWrite.hours;
1957         }
1958     return true;
1959 }
1960
1961 /****************************************************************************
1962 REMARKS:
1963 Function to set the file time and date for a specific file.
1964 ****************************************************************************/
1965 ibool PMAPI PM_setFileTime(
1966     const char *filename,
1967     ibool gmTime,
1968     PM_time *time)
1969 {
1970     FILESTATUS3 fs3;
1971     struct tm   tc;
1972     struct tm   *ret;
1973     time_t      tt;
1974
1975     if (DosQueryPathInfo((PSZ)filename,FIL_STANDARD,(PVOID)&fs3,sizeof(fs3)))
1976         return false;
1977     if (gmTime) {
1978         tc.tm_year = time->year + 80;
1979         tc.tm_mon = time->mon - 1;
1980         tc.tm_mday = time->day;
1981         tc.tm_hour = time->hour;
1982         tc.tm_min = time->min;
1983         tc.tm_sec = time->sec;
1984         if((tt = mktime(&tc)) == -1)
1985             return false;
1986         ret = localtime(&tt);
1987         fs3.ftimeLastWrite.twosecs = ret->tm_sec / 2;
1988         fs3.fdateLastWrite.day = ret->tm_mday;
1989         fs3.fdateLastWrite.month = ret->tm_mon + 1;
1990         fs3.fdateLastWrite.year = ret->tm_year - 80;
1991         fs3.ftimeLastWrite.minutes = ret->tm_min;
1992         fs3.ftimeLastWrite.hours = ret->tm_hour;
1993         }
1994     else {
1995         fs3.ftimeLastWrite.twosecs = time->sec / 2;
1996         fs3.fdateLastWrite.day = time->day;
1997         fs3.fdateLastWrite.month = time->mon;
1998         fs3.fdateLastWrite.year = time->year;
1999         fs3.ftimeLastWrite.minutes = time->min;
2000         fs3.ftimeLastWrite.hours = time->hour;
2001         }
2002     memcpy(&fs3.fdateLastAccess, &fs3.fdateLastWrite, sizeof(FDATE));
2003     memcpy(&fs3.fdateCreation, &fs3.fdateLastWrite, sizeof(FDATE));
2004     memcpy(&fs3.ftimeLastAccess, &fs3.ftimeLastWrite, sizeof(FTIME));
2005     memcpy(&fs3.ftimeCreation, &fs3.ftimeLastWrite, sizeof(FTIME));
2006     DosSetPathInfo((PSZ)filename,FIL_STANDARD,(PVOID)&fs3,sizeof(FILESTATUS3),0L);
2007     return true;
2008 }