Release 950901
[wine] / loader / task.c
1 /*
2  * Task functions
3  *
4  * Copyright 1995 Alexandre Julliard
5  */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include "windows.h"
11 #include "task.h"
12 #include "callback.h"
13 #include "dos_fs.h"
14 #include "debugger.h"
15 #include "global.h"
16 #include "instance.h"
17 #include "miscemu.h"
18 #include "module.h"
19 #include "neexe.h"
20 #include "options.h"
21 #include "selectors.h"
22 #include "toolhelp.h"
23 #include "stddebug.h"
24 #include "debug.h"
25 #include "dde_proc.h"
26
27   /* Min. number of thunks allocated when creating a new segment */
28 #define MIN_THUNKS  32
29
30   /* 32-bit stack size for each task */
31   /* Must not be greater than 64k, or MAKE_SEGPTR won't work */
32 #define STACK32_SIZE 0x10000
33
34
35 static HTASK hFirstTask = 0;
36 static HTASK hCurrentTask = 0;
37 static HTASK hTaskToKill = 0;
38 static HTASK hLockedTask = 0;
39 static WORD nTaskCount = 0;
40 static HANDLE hDOSEnvironment = 0;
41
42   /* TASK_Reschedule() 16-bit entry point */
43 static FARPROC TASK_RescheduleProc;
44
45 #define TASK_SCHEDULE()  CallTo16_word_(TASK_RescheduleProc,0)
46
47
48 static HANDLE TASK_CreateDOSEnvironment(void);
49
50
51 /***********************************************************************
52  *           TASK_Init
53  */
54 BOOL TASK_Init(void)
55 {
56     TASK_RescheduleProc = (FARPROC)GetWndProcEntry16( "TASK_Reschedule" );
57     if (!(hDOSEnvironment = TASK_CreateDOSEnvironment()))
58         fprintf( stderr, "Not enough memory for DOS Environment\n" );
59     return (hDOSEnvironment != 0);
60 }
61
62
63 /***********************************************************************
64  *           TASK_CreateDOSEnvironment
65  *
66  * Create the original DOS environment.
67  */
68 static HANDLE TASK_CreateDOSEnvironment(void)
69 {
70     static const char program_name[] = "KRNL386.EXE";
71     char **e, *p;
72     int initial_size, size;
73     HANDLE handle;
74
75     extern char **environ;
76     extern char WindowsDirectory[], SystemDirectory[];
77
78     /* DOS environment format:
79      * ASCIIZ   string 1
80      * ASCIIZ   string 2
81      * ...
82      * ASCIIZ   string n
83      * ASCIIZ   PATH=xxx
84      * ASCIIZ   windir=xxx
85      * BYTE     0
86      * WORD     1
87      * ASCIIZ   program name (e.g. C:\WINDOWS\SYSTEM\KRNL386.EXE)
88      */
89
90     /* First compute the size of the fixed part of the environment */
91
92     initial_size = 5 +                            /* PATH= */
93                    strlen(WindowsPath) + 1 +      /* path value */
94                    7 +                            /* windir= */
95                    strlen(WindowsDirectory) + 1 + /* windir value */
96                    1 +                            /* BYTE 0 at end */
97                    sizeof(WORD) +                 /* WORD 1 */
98                    strlen(SystemDirectory) + 1 +  /* program directory */
99                    strlen(program_name) + 1;      /* program name */
100
101     /* Compute the total size of the Unix environment (except path) */
102
103     for (e = environ, size = initial_size; *e; e++)
104     {
105         if (strncasecmp(*e, "path=", 5))
106         {
107             int len = strlen(*e) + 1;
108             if (size + len >= 32767)
109             {
110                 fprintf( stderr, "Warning: environment larger than 32k.\n" );
111                 break;
112             }
113             size += len;
114         }
115     }
116
117
118     /* Now allocate the environment */
119
120     if (!(handle = GlobalAlloc( GMEM_FIXED, size ))) return 0;
121     p = (char *)GlobalLock( handle );
122
123     /* And fill it with the Unix environment */
124
125     for (e = environ, size = initial_size; *e; e++)
126     {
127         if (strncasecmp(*e, "path=", 5))
128         {
129             int len = strlen(*e) + 1;
130             if (size + len >= 32767) break;
131             strcpy( p, *e );
132             size += len;
133             p    += len;
134         }
135     }
136
137     /* Now add the path and Windows directory */
138
139     strcpy( p, "PATH=" );
140     strcat( p, WindowsPath );
141     p += strlen(p) + 1;
142
143     strcpy( p, "windir=" );
144     strcat( p, WindowsDirectory );
145     p += strlen(p) + 1;
146
147     /* Now add the program name */
148
149     *p++ = '\0';
150     *(WORD *)p = 1;
151     p += sizeof(WORD);
152     strcpy( p, SystemDirectory );
153     strcat( p, "\\" );
154     strcat( p, program_name );
155
156     /* Display it */
157
158     p = (char *) GlobalLock( handle );
159     dprintf_task(stddeb, "Master DOS environment at %p\n", p);
160     for (; *p; p += strlen(p) + 1) dprintf_task(stddeb, "    %s\n", p);
161     dprintf_task( stddeb, "Progname: %s\n", p+3 );
162
163     return handle;
164 }
165
166
167 /***********************************************************************
168  *           TASK_LinkTask
169  */
170 static void TASK_LinkTask( HTASK hTask )
171 {
172     HTASK *prevTask;
173     TDB *pTask;
174
175     if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
176     prevTask = &hFirstTask;
177     while (*prevTask)
178     {
179         TDB *prevTaskPtr = (TDB *)GlobalLock( *prevTask );
180         if (prevTaskPtr->priority >= pTask->priority) break;
181         prevTask = &prevTaskPtr->hNext;
182     }
183     pTask->hNext = *prevTask;
184     *prevTask = hTask;
185     nTaskCount++;
186 }
187
188
189 /***********************************************************************
190  *           TASK_UnlinkTask
191  */
192 static void TASK_UnlinkTask( HTASK hTask )
193 {
194     HTASK *prevTask;
195     TDB *pTask;
196
197     prevTask = &hFirstTask;
198     while (*prevTask && (*prevTask != hTask))
199     {
200         pTask = (TDB *)GlobalLock( *prevTask );
201         prevTask = &pTask->hNext;
202     }
203     if (*prevTask)
204     {
205         pTask = (TDB *)GlobalLock( *prevTask );
206         *prevTask = pTask->hNext;
207         pTask->hNext = 0;
208         nTaskCount--;
209     }
210 }
211
212
213 /***********************************************************************
214  *           TASK_CreateThunks
215  *
216  * Create a thunk free-list in segment 'handle', starting from offset 'offset'
217  * and containing 'count' entries.
218  */
219 static void TASK_CreateThunks( HGLOBAL handle, WORD offset, WORD count )
220 {
221     int i;
222     WORD free;
223     THUNKS *pThunk;
224
225     pThunk = (THUNKS *)((BYTE *)GlobalLock( handle ) + offset);
226     pThunk->next = 0;
227     pThunk->magic = THUNK_MAGIC;
228     pThunk->free = (int)&pThunk->thunks - (int)pThunk;
229     free = pThunk->free;
230     for (i = 0; i < count-1; i++)
231     {
232         free += 8;  /* Offset of next thunk */
233         pThunk->thunks[4*i] = free;
234     }
235     pThunk->thunks[4*i] = 0;  /* Last thunk */
236 }
237
238
239 /***********************************************************************
240  *           TASK_AllocThunk
241  *
242  * Allocate a thunk for MakeProcInstance().
243  */
244 static SEGPTR TASK_AllocThunk( HTASK hTask )
245 {
246     TDB *pTask;
247     THUNKS *pThunk;
248     WORD sel, base;
249     
250     if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
251     sel = pTask->hCSAlias;
252     pThunk = &pTask->thunks;
253     base = (int)pThunk - (int)pTask;
254     while (!pThunk->free)
255     {
256         sel = pThunk->next;
257         if (!sel)  /* Allocate a new segment */
258         {
259             sel = GLOBAL_Alloc( GMEM_FIXED, sizeof(THUNKS) + (MIN_THUNKS-1)*8,
260                                 pTask->hPDB, TRUE, FALSE, FALSE );
261             if (!sel) return (SEGPTR)0;
262             TASK_CreateThunks( sel, 0, MIN_THUNKS );
263             pThunk->next = sel;
264         }
265         pThunk = (THUNKS *)GlobalLock( sel );
266         base = 0;
267     }
268     base += pThunk->free;
269     pThunk->free = *(WORD *)((BYTE *)pThunk + pThunk->free);
270     return MAKELONG( base, sel );
271 }
272
273
274 /***********************************************************************
275  *           TASK_FreeThunk
276  *
277  * Free a MakeProcInstance() thunk.
278  */
279 static BOOL TASK_FreeThunk( HTASK hTask, SEGPTR thunk )
280 {
281     TDB *pTask;
282     THUNKS *pThunk;
283     WORD sel, base;
284     
285     if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
286     sel = pTask->hCSAlias;
287     pThunk = &pTask->thunks;
288     base = (int)pThunk - (int)pTask;
289     while (sel && (sel != HIWORD(thunk)))
290     {
291         sel = pThunk->next;
292         pThunk = (THUNKS *)GlobalLock( sel );
293         base = 0;
294     }
295     if (!sel) return FALSE;
296     *(WORD *)((BYTE *)pThunk + LOWORD(thunk) - base) = pThunk->free;
297     pThunk->free = LOWORD(thunk) - base;
298     return TRUE;
299 }
300
301
302 /***********************************************************************
303  *           TASK_CallToStart
304  *
305  * 32-bit entry point for a new task. This function is responsible for
306  * setting up the registers and jumping to the 16-bit entry point.
307  */
308 static void TASK_CallToStart(void)
309 {
310     int cs_reg, ds_reg, ip_reg;
311     TDB *pTask = (TDB *)GlobalLock( hCurrentTask );
312     NE_MODULE *pModule = (NE_MODULE *)GlobalLock( pTask->hModule );
313     SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
314
315     /* Registers at initialization must be:
316      * ax   zero
317      * bx   stack size in bytes
318      * cx   heap size in bytes
319      * si   previous app instance
320      * di   current app instance
321      * bp   zero
322      * es   selector to the PSP
323      * ds   dgroup of the application
324      * ss   stack selector
325      * sp   top of the stack
326      */
327
328     cs_reg = pSegTable[pModule->cs - 1].selector;
329     ip_reg = pModule->ip;
330     ds_reg = pSegTable[pModule->dgroup - 1].selector;
331     IF1632_Saved16_ss = pTask->ss;
332     IF1632_Saved16_sp = pTask->sp;
333     dprintf_task( stddeb, "Starting main program: cs:ip=%04x:%04x ds=%04x ss:sp=%04x:%04x\n",
334                  cs_reg, ip_reg, ds_reg,
335                  IF1632_Saved16_ss, IF1632_Saved16_sp);
336
337     CallTo16_regs_( (FARPROC)(cs_reg << 16 | ip_reg), ds_reg,
338                    pTask->hPDB /*es*/, 0 /*bp*/, 0 /*ax*/,
339                    pModule->stack_size /*bx*/, pModule->heap_size /*cx*/,
340                    0 /*dx*/, 0 /*si*/, ds_reg /*di*/ );
341     /* This should never return */
342     fprintf( stderr, "TASK_CallToStart: Main program returned!\n" );
343     TASK_KillCurrentTask( 1 );
344 }
345
346
347 /***********************************************************************
348  *           TASK_CreateTask
349  */
350 HTASK TASK_CreateTask( HMODULE hModule, HANDLE hInstance, HANDLE hPrevInstance,
351                        HANDLE hEnvironment, char *cmdLine, WORD cmdShow )
352 {
353     HTASK hTask;
354     TDB *pTask;
355     HANDLE hParentEnv;
356     NE_MODULE *pModule;
357     SEGTABLEENTRY *pSegTable;
358     LPSTR name;
359     char *stack16Top, *stack32Top;
360     STACK16FRAME *frame16;
361     STACK32FRAME *frame32;
362     extern DWORD CALL16_RetAddr_word;
363     char filename[256];
364     
365     if (!(pModule = (NE_MODULE *)GlobalLock( hModule ))) return 0;
366     pSegTable = NE_SEG_TABLE( pModule );
367
368       /* Allocate the task structure */
369
370     hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
371                           hModule, FALSE, FALSE, FALSE );
372     if (!hTask) return 0;
373     pTask = (TDB *)GlobalLock( hTask );
374
375       /* Allocate the new environment block */
376
377     if (!(hParentEnv = hEnvironment))
378     {
379         TDB *pParent = (TDB *)GlobalLock( hCurrentTask );
380         hParentEnv = pParent ? pParent->pdb.environment : hDOSEnvironment;
381     }
382     /* FIXME: do we really need to make a copy also when */
383     /*        we don't use the parent environment? */
384     if (!(hEnvironment = GlobalAlloc( GMEM_FIXED, GlobalSize( hParentEnv ) )))
385     {
386         GlobalFree( hTask );
387         return 0;
388     }
389     memcpy( GlobalLock( hEnvironment ), GlobalLock( hParentEnv ),
390             GlobalSize( hParentEnv ) );
391
392       /* Get current directory */
393     
394     GetModuleFileName( hModule, filename, sizeof(filename) );
395     name = strrchr(filename, '\\');
396     if (name) *(name+1) = 0;
397
398       /* Fill the task structure */
399
400     pTask->nEvents       = 1;  /* So the task can be started */
401     pTask->hSelf         = hTask;
402     pTask->flags         = 0;
403     pTask->version       = pModule->expected_version;
404     pTask->hInstance     = hInstance;
405     pTask->hPrevInstance = hPrevInstance;
406     pTask->hModule       = hModule;
407     pTask->hParent       = hCurrentTask;
408     pTask->curdrive      = filename[0] - 'A' + 0x80;
409     pTask->magic         = TDB_MAGIC;
410     pTask->nCmdShow      = cmdShow;
411     strcpy( pTask->curdir, filename+2 );
412
413       /* Create the thunks block */
414
415     TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
416
417       /* Copy the module name */
418
419     name = MODULE_GetModuleName( hModule );
420     strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
421
422       /* Fill the PDB */
423
424     pTask->pdb.int20 = 0x20cd;
425     pTask->pdb.dispatcher[0] = 0x9a;  /* ljmp */
426     *(DWORD *)&pTask->pdb.dispatcher[1] = MODULE_GetEntryPoint( GetModuleHandle("KERNEL"), 102 );  /* KERNEL.102 is DOS3Call() */
427     pTask->pdb.savedint22 = INT_GetHandler( 0x22 );
428     pTask->pdb.savedint23 = INT_GetHandler( 0x23 );
429     pTask->pdb.savedint24 = INT_GetHandler( 0x24 );
430     pTask->pdb.environment = hEnvironment;
431     strncpy( pTask->pdb.cmdLine + 1, cmdLine, 126 );
432     pTask->pdb.cmdLine[127] = '\0';
433     pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
434
435       /* Get the compatibility flags */
436
437     pTask->compat_flags = GetProfileInt( name, "Compatibility", 0 );
438
439       /* Allocate a selector for the PDB */
440
441     pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB),
442                                       hModule, FALSE, FALSE, FALSE, NULL );
443
444       /* Allocate a code segment alias for the TDB */
445
446     pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
447                                           sizeof(TDB), pTask->hPDB, TRUE,
448                                           FALSE, FALSE, NULL );
449
450       /* Set the owner of the environment block */
451
452     FarSetOwner( pTask->pdb.environment, pTask->hPDB );
453
454       /* Default DTA overwrites command-line */
455
456     pTask->dta = MAKELONG( (int)&pTask->pdb.cmdLine - (int)&pTask->pdb,
457                            pTask->hPDB );
458
459       /* Allocate the 32-bit stack */
460
461     pTask->hStack32 = GLOBAL_Alloc( GMEM_FIXED, STACK32_SIZE, pTask->hPDB,
462                                     FALSE, FALSE, FALSE );
463
464       /* Create the 32-bit stack frame */
465
466     *(DWORD *)GlobalLock(pTask->hStack32) = 0xDEADBEEF;
467     stack32Top = (char*)GlobalLock(pTask->hStack32) + STACK32_SIZE;
468     frame32 = (STACK32FRAME *)stack32Top - 1;
469     frame32->saved_esp = (DWORD)stack32Top;
470     frame32->edi = 0;
471     frame32->esi = 0;
472     frame32->edx = 0;
473     frame32->ecx = 0;
474     frame32->ebx = 0;
475     frame32->ebp = 0;
476     frame32->retaddr = (DWORD)TASK_CallToStart;
477     frame32->codeselector = WINE_CODE_SELECTOR;
478     pTask->esp = (DWORD)frame32;
479
480       /* Create the 16-bit stack frame */
481
482     pTask->ss = hInstance;
483     pTask->sp = ((pModule->sp != 0) ? pModule->sp :
484                  pSegTable[pModule->ss-1].minsize + pModule->stack_size) & ~1;
485     stack16Top = (char *)PTR_SEG_OFF_TO_LIN( pTask->ss, pTask->sp );
486     frame16 = (STACK16FRAME *)stack16Top - 1;
487     frame16->saved_ss = 0; /*pTask->ss;*/
488     frame16->saved_sp = 0; /*pTask->sp;*/
489     frame16->ds = frame16->es = pTask->hInstance;
490     frame16->entry_point = 0;
491     frame16->ordinal_number = 24;  /* WINPROCS.24 is TASK_Reschedule */
492     frame16->dll_id = 24; /* WINPROCS */
493     frame16->bp = 0;
494     frame16->ip = LOWORD( CALL16_RetAddr_word );
495     frame16->cs = HIWORD( CALL16_RetAddr_word );
496     pTask->sp -= sizeof(STACK16FRAME);
497
498       /* If there's no 16-bit stack yet, use a part of the new task stack */
499       /* This is only needed to have a stack to switch from on the first  */
500       /* call to DirectedYield(). */
501
502     if (!IF1632_Saved16_ss)
503     {
504         IF1632_Saved16_ss = pTask->ss;
505         IF1632_Saved16_sp = pTask->sp;
506     }
507
508       /* Add a breakpoint at the start of the task */
509
510     if (Options.debug)
511     {
512         DBG_ADDR addr = { pSegTable[pModule->cs-1].selector, pModule->ip };
513         fprintf( stderr, "Task '%s': ", name );
514         DEBUG_AddBreakpoint( &addr );
515     }
516
517       /* Add the task to the linked list */
518
519     TASK_LinkTask( hTask );
520
521     dprintf_task( stddeb, "CreateTask: module='%s' cmdline='%s' task=%04x\n",
522                   name, cmdLine, hTask );
523
524     return hTask;
525 }
526
527
528 /***********************************************************************
529  *           TASK_DeleteTask
530  */
531 static void TASK_DeleteTask( HTASK hTask )
532 {
533     TDB *pTask;
534
535     if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
536
537       /* Free the task module */
538
539     FreeModule( pTask->hModule );
540
541       /* Free all memory used by this task (including the 32-bit stack, */
542       /* the environment block and the thunk segments). */
543
544     GlobalFreeAll( pTask->hPDB );
545
546       /* Free the selector aliases */
547
548     GLOBAL_FreeBlock( pTask->hCSAlias );
549     GLOBAL_FreeBlock( pTask->hPDB );
550
551       /* Free the task structure itself */
552
553     GlobalFree( hTask );
554 }
555
556
557 /***********************************************************************
558  *           TASK_KillCurrentTask
559  *
560  * Kill the currently running task. As it's not possible to kill the
561  * current task like this, it is simply marked for destruction, and will
562  * be killed when either TASK_Reschedule or this function is called again 
563  * in the context of another task.
564  */
565 void TASK_KillCurrentTask( int exitCode )
566 {
567     if (hTaskToKill && (hTaskToKill != hCurrentTask))
568     {
569         /* If another task is already marked for destruction, */
570         /* we call kill it now, as we are in another context. */
571         TASK_DeleteTask( hTaskToKill );
572     }
573
574     if (nTaskCount <= 1)
575     {
576         dprintf_task( stddeb, "Killing the last task, exiting\n" );
577         exit(0);
578     }
579
580     /* Remove the task from the list to be sure we never switch back to it */
581     TASK_UnlinkTask( hCurrentTask );
582     
583     hTaskToKill = hCurrentTask;
584     hLockedTask = 0;
585     Yield();
586     /* We never return from Yield() */
587 }
588
589
590 /***********************************************************************
591  *           TASK_Reschedule
592  *
593  * This is where all the magic of task-switching happens!
594  *
595  * This function should only be called via the TASK_SCHEDULE() macro, to make
596  * sure that all the context is saved correctly.
597  */
598 void TASK_Reschedule(void)
599 {
600     TDB *pOldTask = NULL, *pNewTask;
601     HTASK hTask = 0;
602
603 #ifdef CONFIG_IPC
604     dde_reschedule();
605 #endif
606       /* First check if there's a task to kill */
607
608     if (hTaskToKill && (hTaskToKill != hCurrentTask))
609     {
610         TASK_DeleteTask( hTaskToKill );
611         hTaskToKill = 0;
612     }
613
614       /* If current task is locked, simply return */
615
616     if (hLockedTask) return;
617
618       /* Find a task to yield to */
619
620     pOldTask = (TDB *)GlobalLock( hCurrentTask );
621     if (pOldTask && pOldTask->hYieldTo)
622     {
623         /* If a task is stored in hYieldTo of the current task (put there */
624         /* by DirectedYield), yield to it only if it has events pending.  */
625         hTask = pOldTask->hYieldTo;
626         if (!(pNewTask = (TDB *)GlobalLock( hTask )) || !pNewTask->nEvents)
627             hTask = 0;
628     }
629
630     if (!hTask)
631     {
632         hTask = hFirstTask;
633         while (hTask)
634         {
635             pNewTask = (TDB *)GlobalLock( hTask );
636             if (pNewTask->nEvents && (hTask != hCurrentTask)) break;
637             hTask = pNewTask->hNext;
638         }
639     }
640
641      /* If there's a task to kill, switch to any other task, */
642      /* even if it doesn't have events pending. */
643
644     if (!hTask && hTaskToKill) hTask = hFirstTask;
645
646     if (!hTask) return;  /* Do nothing */
647
648     pNewTask = (TDB *)GlobalLock( hTask );
649     dprintf_task( stddeb, "Switching to task %04x (%.8s)\n",
650                   hTask, pNewTask->module_name );
651
652       /* Save the stacks of the previous task (if any) */
653
654     if (pOldTask)
655     {
656         pOldTask->ss  = IF1632_Saved16_ss;
657         pOldTask->sp  = IF1632_Saved16_sp;
658         pOldTask->esp = IF1632_Saved32_esp;
659     }
660     else IF1632_Original32_esp = IF1632_Saved32_esp;
661
662      /* Make the task the last in the linked list (round-robin scheduling) */
663
664     pNewTask->priority++;
665     TASK_UnlinkTask( hTask );
666     TASK_LinkTask( hTask );
667     pNewTask->priority--;
668
669       /* Switch to the new stack */
670
671     hCurrentTask = hTask;
672     IF1632_Saved16_ss   = pNewTask->ss;
673     IF1632_Saved16_sp   = pNewTask->sp;
674     IF1632_Saved32_esp  = pNewTask->esp;
675     IF1632_Stack32_base = WIN16_GlobalLock( pNewTask->hStack32 );
676 }
677
678
679 /***********************************************************************
680  *           InitTask  (KERNEL.91)
681  */
682 void InitTask( struct sigcontext_struct context )
683 {
684     static int firstTask = 1;
685     TDB *pTask;
686     NE_MODULE *pModule;
687     SEGTABLEENTRY *pSegTable;
688     INSTANCEDATA *pinstance;
689     LONG stacklow, stackhi;
690
691     context.sc_eax = 0;
692     if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return;
693     if (!(pModule = (NE_MODULE *)GlobalLock( pTask->hModule ))) return;
694
695     if (firstTask)
696     {
697         extern BOOL WIDGETS_Init(void);
698         extern BOOL WIN_CreateDesktopWindow(void);
699
700         /* Perform global initialisations that need a task context */
701
702           /* Initialize built-in window classes */
703         if (!WIDGETS_Init()) return;
704
705           /* Create desktop window */
706         if (!WIN_CreateDesktopWindow()) return;
707
708         firstTask = 0;
709     }
710
711     NE_InitializeDLLs( pTask->hModule );
712
713     /* Registers on return are:
714      * ax     1 if OK, 0 on error
715      * cx     stack limit in bytes
716      * dx     cmdShow parameter
717      * si     instance handle of the previous instance
718      * di     instance handle of the new task
719      * es:bx  pointer to command-line inside PSP
720      */
721     context.sc_eax = 1;
722     context.sc_ebx = 0x81;
723     context.sc_ecx = pModule->stack_size;
724     context.sc_edx = pTask->nCmdShow;
725     context.sc_esi = pTask->hPrevInstance;
726     context.sc_edi = pTask->hInstance;
727     context.sc_es  = pTask->hPDB;
728
729     /* Initialize the local heap */
730     if ( pModule->heap_size )
731     {
732         LocalInit( pTask->hInstance, 0, pModule->heap_size );
733     }    
734
735
736     /* Initialize the INSTANCEDATA structure */
737     pSegTable = NE_SEG_TABLE( pModule );
738     stacklow = pSegTable[pModule->ss - 1].minsize;
739     stackhi  = stacklow + pModule->stack_size;
740     if (stackhi > 0xffff) stackhi = 0xffff;
741     pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
742     pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
743     pinstance->stacktop    = stacklow; 
744     pinstance->stackmin    = IF1632_Saved16_sp;
745 }
746
747
748 /***********************************************************************
749  *           WaitEvent  (KERNEL.30)
750  */
751 BOOL WaitEvent( HTASK hTask )
752 {
753     TDB *pTask;
754
755     if (!hTask) hTask = hCurrentTask;
756     pTask = (TDB *)GlobalLock( hTask );
757     if (pTask->nEvents > 0)
758     {
759         pTask->nEvents--;
760         return FALSE;
761     }
762     TASK_SCHEDULE();
763     /* When we get back here, we have an event (or the task is the only one) */
764     if (pTask->nEvents > 0) pTask->nEvents--;
765     return TRUE;
766 }
767
768
769 /***********************************************************************
770  *           PostEvent  (KERNEL.31)
771  */
772 void PostEvent( HTASK hTask )
773 {
774     TDB *pTask;
775
776     if (!hTask) hTask = hCurrentTask;
777     if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
778     pTask->nEvents++;
779 }
780
781
782 /***********************************************************************
783  *           SetPriority  (KERNEL.32)
784  */
785 void SetPriority( HTASK hTask, int delta )
786 {
787     TDB *pTask;
788     int newpriority;
789
790     if (!hTask) hTask = hCurrentTask;
791     if (!(pTask = (TDB *)GlobalLock( hTask ))) return;
792     newpriority = pTask->priority + delta;
793     if (newpriority < -32) newpriority = -32;
794     else if (newpriority > 15) newpriority = 15;
795
796     pTask->priority = newpriority + 1;
797     TASK_UnlinkTask( hTask );
798     TASK_LinkTask( hTask );
799     pTask->priority--;
800 }
801
802
803 /***********************************************************************
804  *           LockCurrentTask  (KERNEL.33)
805  */
806 HTASK LockCurrentTask( BOOL bLock )
807 {
808     if (bLock) hLockedTask = hCurrentTask;
809     else hLockedTask = 0;
810     return hLockedTask;
811 }
812
813
814 /***********************************************************************
815  *           IsTaskLocked  (KERNEL.122)
816  */
817 WORD IsTaskLocked(void)
818 {
819     return hLockedTask;
820 }
821
822
823 /***********************************************************************
824  *           OldYield  (KERNEL.117)
825  */
826 void OldYield(void)
827 {
828     TDB *pCurTask;
829
830     pCurTask = (TDB *)GlobalLock( hCurrentTask );
831     if (pCurTask) pCurTask->nEvents++;  /* Make sure we get back here */
832     TASK_SCHEDULE();
833     if (pCurTask) pCurTask->nEvents--;
834 }
835
836
837 /***********************************************************************
838  *           DirectedYield  (KERNEL.150)
839  */
840 void DirectedYield( HTASK hTask )
841 {
842     TDB *pCurTask;
843
844     if ((pCurTask = (TDB *)GlobalLock( hCurrentTask )) != NULL)
845         pCurTask->hYieldTo = hTask;
846
847     OldYield();
848 }
849
850
851 /***********************************************************************
852  *           Yield  (KERNEL.29)
853  */
854 void Yield(void)
855 {
856     DirectedYield( 0 );
857 }
858
859
860 /***********************************************************************
861  *           MakeProcInstance  (KERNEL.51)
862  */
863 FARPROC MakeProcInstance( FARPROC func, HANDLE hInstance )
864 {
865     BYTE *thunk;
866     SEGPTR thunkaddr;
867     
868     thunkaddr = TASK_AllocThunk( hCurrentTask );
869     if (!thunkaddr) return (FARPROC)0;
870     thunk = PTR_SEG_TO_LIN( thunkaddr );
871
872     dprintf_task( stddeb, "MakeProcInstance(%08lx,%04x): got thunk %08lx\n",
873                   (SEGPTR)func, hInstance, (SEGPTR)thunkaddr );
874     
875     *thunk++ = 0xb8;    /* movw instance, %ax */
876     *thunk++ = (BYTE)(hInstance & 0xff);
877     *thunk++ = (BYTE)(hInstance >> 8);
878     *thunk++ = 0xea;    /* ljmp func */
879     *(DWORD *)thunk = (DWORD)func;
880     return (FARPROC)thunkaddr;
881 }
882
883
884 /***********************************************************************
885  *           FreeProcInstance  (KERNEL.52)
886  */
887 void FreeProcInstance( FARPROC func )
888 {
889     dprintf_task( stddeb, "FreeProcInstance(%08lx)\n", (SEGPTR)func );
890     TASK_FreeThunk( hCurrentTask, (SEGPTR)func );
891 }
892
893
894 /**********************************************************************
895  *          GetCodeHandle    (KERNEL.93)
896  */
897 HANDLE GetCodeHandle( FARPROC proc )
898 {
899     HANDLE handle;
900     BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
901
902     /* Return the code segment containing 'proc'. */
903     /* Not sure if this is really correct (shouldn't matter that much). */
904
905     /* Check if it is really a thunk */
906     if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
907         handle = GlobalHandle( thunk[6] + (thunk[7] << 8) );
908     else
909         handle = GlobalHandle( HIWORD(proc) );
910
911     printf( "STUB: GetCodeHandle(%08lx) returning %04x\n",
912             (DWORD)proc, handle );
913     return handle;
914 }
915
916
917 /***********************************************************************
918  *           SetTaskQueue  (KERNEL.34)
919  */
920 HGLOBAL SetTaskQueue( HANDLE hTask, HGLOBAL hQueue )
921 {
922     HGLOBAL hPrev;
923     TDB *pTask;
924
925     if (!hTask) hTask = hCurrentTask;
926     if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
927     hPrev = pTask->hQueue;
928     pTask->hQueue = hQueue;
929     return hPrev;
930 }
931
932
933 /***********************************************************************
934  *           GetTaskQueue  (KERNEL.35)
935  */
936 HGLOBAL GetTaskQueue( HANDLE hTask )
937 {
938     TDB *pTask;
939
940     if (!hTask) hTask = hCurrentTask;
941     if (!(pTask = (TDB *)GlobalLock( hTask ))) return 0;
942     return pTask->hQueue;
943 }
944
945
946 /***********************************************************************
947  *           GetCurrentTask   (KERNEL.36)
948  */
949 HTASK GetCurrentTask(void)
950 {
951       /* Undocumented: first task is returned in high word */
952     return MAKELONG( hCurrentTask, hFirstTask );
953 }
954
955
956 /***********************************************************************
957  *           GetCurrentPDB   (KERNEL.37)
958  */
959 WORD GetCurrentPDB(void)
960 {
961     TDB *pTask;
962
963     if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
964     return pTask->hPDB;
965 }
966
967
968 /***********************************************************************
969  *           GetInstanceData   (KERNEL.54)
970  */
971 int GetInstanceData( HANDLE instance, WORD buffer, int len )
972 {
973     char *ptr = (char *)GlobalLock( instance );
974     if (!ptr || !len) return 0;
975     if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
976     memcpy( ptr + buffer, (char *)GlobalLock( CURRENT_DS ) + buffer, len );
977     return len;
978 }
979
980
981 /***********************************************************************
982  *           GetDOSEnvironment   (KERNEL.131)
983  */
984 SEGPTR GetDOSEnvironment(void)
985 {
986     TDB *pTask;
987
988     if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
989     return WIN16_GlobalLock( pTask->pdb.environment );
990 }
991
992
993 /***********************************************************************
994  *           GetNumTasks   (KERNEL.152)
995  */
996 WORD GetNumTasks(void)
997 {
998     return nTaskCount;
999 }
1000
1001
1002 /***********************************************************************
1003  *           GetTaskDS   (KERNEL.155)
1004  */
1005 WORD GetTaskDS(void)
1006 {
1007     TDB *pTask;
1008
1009     if (!(pTask = (TDB *)GlobalLock( hCurrentTask ))) return 0;
1010     return pTask->hInstance;
1011 }
1012
1013
1014 /***********************************************************************
1015  *           IsTask   (KERNEL.320)
1016  */
1017 BOOL IsTask( HTASK hTask )
1018 {
1019     TDB *pTask;
1020
1021     if (!(pTask = (TDB *)GlobalLock( hTask ))) return FALSE;
1022     if (GlobalSize( hTask ) < sizeof(TDB)) return FALSE;
1023     return (pTask->magic == TDB_MAGIC);
1024 }
1025
1026
1027 /***********************************************************************
1028  *           GetExePtr   (KERNEL.133)
1029  */
1030 HMODULE GetExePtr( HANDLE handle )
1031 {
1032     char *ptr;
1033     HTASK hTask;
1034     HANDLE owner;
1035
1036       /* Check for module handle */
1037
1038     if (!(ptr = GlobalLock( handle ))) return 0;
1039     if (((NE_MODULE *)ptr)->magic == NE_SIGNATURE) return handle;
1040
1041       /* Check the owner for module handle */
1042
1043     owner = FarGetOwner( handle );
1044     if (!(ptr = GlobalLock( owner ))) return 0;
1045     if (((NE_MODULE *)ptr)->magic == NE_SIGNATURE) return owner;
1046
1047       /* Search for this handle and its owner inside all tasks */
1048
1049     hTask = hFirstTask;
1050     while (hTask)
1051     {
1052         TDB *pTask = (TDB *)GlobalLock( hTask );
1053         if ((hTask == handle) ||
1054             (pTask->hInstance == handle) ||
1055             (pTask->hQueue == handle) ||
1056             (pTask->hPDB == handle)) return pTask->hModule;
1057         if ((hTask == owner) ||
1058             (pTask->hInstance == owner) ||
1059             (pTask->hQueue == owner) ||
1060             (pTask->hPDB == owner)) return pTask->hModule;
1061         hTask = pTask->hNext;
1062     }
1063     return 0;
1064 }
1065
1066
1067 /***********************************************************************
1068  *           TaskFirst   (TOOLHELP.63)
1069  */
1070 BOOL TaskFirst( TASKENTRY *lpte )
1071 {
1072     lpte->hNext = hFirstTask;
1073     return TaskNext( lpte );
1074 }
1075
1076
1077 /***********************************************************************
1078  *           TaskNext   (TOOLHELP.64)
1079  */
1080 BOOL TaskNext( TASKENTRY *lpte )
1081 {
1082     TDB *pTask;
1083     INSTANCEDATA *pInstData;
1084
1085     dprintf_toolhelp( stddeb, "TaskNext(%p): task=%04x\n", lpte, lpte->hNext );
1086     if (!lpte->hNext) return FALSE;
1087     pTask = (TDB *)GlobalLock( lpte->hNext );
1088     if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1089     pInstData = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( pTask->hInstance, 0 );
1090     lpte->hTask         = lpte->hNext;
1091     lpte->hTaskParent   = pTask->hParent;
1092     lpte->hInst         = pTask->hInstance;
1093     lpte->hModule       = pTask->hModule;
1094     lpte->wSS           = pTask->ss;
1095     lpte->wSP           = pTask->sp;
1096     lpte->wStackTop     = pInstData->stacktop;
1097     lpte->wStackMinimum = pInstData->stackmin;
1098     lpte->wStackBottom  = pInstData->stackbottom;
1099     lpte->wcEvents      = pTask->nEvents;
1100     lpte->hQueue        = pTask->hQueue;
1101     strncpy( lpte->szModule, pTask->module_name, 8 );
1102     lpte->szModule[8]   = '\0';
1103     lpte->wPSPOffset    = 0x100;  /*??*/
1104     lpte->hNext         = pTask->hNext;
1105     return TRUE;
1106 }
1107
1108
1109 /***********************************************************************
1110  *           TaskFindHandle   (TOOLHELP.65)
1111  */
1112 BOOL TaskFindHandle( TASKENTRY *lpte, HTASK hTask )
1113 {
1114     lpte->hNext = hTask;
1115     return TaskNext( lpte );
1116 }