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