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