Implemented new PE DLL initalization code, trying to call the
[wine] / loader / task.c
1 /*
2  * Task functions
3  *
4  * Copyright 1995 Alexandre Julliard
5  */
6
7 #include <stdlib.h>
8 #include <string.h>
9 #include <assert.h>
10
11 #include "windows.h"
12 #include "user.h"
13 #include "callback.h"
14 #include "drive.h"
15 #include "file.h"
16 #include "global.h"
17 #include "instance.h"
18 #include "message.h"
19 #include "miscemu.h"
20 #include "module.h"
21 #include "neexe.h"
22 #include "peexe.h"
23 #include "pe_image.h"
24 #include "process.h"
25 #include "queue.h"
26 #include "selectors.h"
27 #include "stackframe.h"
28 #include "task.h"
29 #include "thread.h"
30 #include "toolhelp.h"
31 #include "winnt.h"
32 #include "winsock.h"
33 #include "thread.h"
34 #include "syslevel.h"
35 #include "debug.h"
36 #include "dosexe.h"
37 #include "dde_proc.h"
38 #include "server.h"
39
40   /* Min. number of thunks allocated when creating a new segment */
41 #define MIN_THUNKS  32
42
43   /* Pointer to function to switch to a larger stack */
44 int (*IF1632_CallLargeStack)( int (*func)(), void *arg ) = NULL;
45
46
47 static THHOOK DefaultThhook = { 0 };
48 THHOOK *pThhook = &DefaultThhook;
49
50 #define hCurrentTask (pThhook->CurTDB)
51 #define hFirstTask   (pThhook->HeadTDB)
52 #define hLockedTask  (pThhook->LockTDB)
53
54 static HTASK16 hTaskToKill = 0;
55 static UINT16 nTaskCount = 0;
56
57 static void TASK_YieldToSystem(TDB*);
58
59 extern BOOL32 THREAD_InitDone;
60
61
62 /***********************************************************************
63  *           TASK_InstallTHHook
64  */
65 void TASK_InstallTHHook( THHOOK *pNewThhook )
66 {
67      THHOOK *pOldThhook = pThhook;
68
69      pThhook = pNewThhook? pNewThhook : &DefaultThhook;
70
71      *pThhook = *pOldThhook;
72 }
73
74 /***********************************************************************
75  *           TASK_GetNextTask
76  */
77 HTASK16 TASK_GetNextTask( HTASK16 hTask )
78 {
79     TDB* pTask = (TDB*)GlobalLock16(hTask);
80
81     if (pTask->hNext) return pTask->hNext;
82     return (hFirstTask != hTask) ? hFirstTask : 0; 
83 }
84
85 /***********************************************************************
86  *           TASK_LinkTask
87  */
88 static void TASK_LinkTask( HTASK16 hTask )
89 {
90     HTASK16 *prevTask;
91     TDB *pTask;
92
93     if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
94     prevTask = &hFirstTask;
95     while (*prevTask)
96     {
97         TDB *prevTaskPtr = (TDB *)GlobalLock16( *prevTask );
98         if (prevTaskPtr->priority >= pTask->priority) break;
99         prevTask = &prevTaskPtr->hNext;
100     }
101     pTask->hNext = *prevTask;
102     *prevTask = hTask;
103     nTaskCount++;
104 }
105
106
107 /***********************************************************************
108  *           TASK_UnlinkTask
109  */
110 static void TASK_UnlinkTask( HTASK16 hTask )
111 {
112     HTASK16 *prevTask;
113     TDB *pTask;
114
115     prevTask = &hFirstTask;
116     while (*prevTask && (*prevTask != hTask))
117     {
118         pTask = (TDB *)GlobalLock16( *prevTask );
119         prevTask = &pTask->hNext;
120     }
121     if (*prevTask)
122     {
123         pTask = (TDB *)GlobalLock16( *prevTask );
124         *prevTask = pTask->hNext;
125         pTask->hNext = 0;
126         nTaskCount--;
127     }
128 }
129
130
131 /***********************************************************************
132  *           TASK_CreateThunks
133  *
134  * Create a thunk free-list in segment 'handle', starting from offset 'offset'
135  * and containing 'count' entries.
136  */
137 static void TASK_CreateThunks( HGLOBAL16 handle, WORD offset, WORD count )
138 {
139     int i;
140     WORD free;
141     THUNKS *pThunk;
142
143     pThunk = (THUNKS *)((BYTE *)GlobalLock16( handle ) + offset);
144     pThunk->next = 0;
145     pThunk->magic = THUNK_MAGIC;
146     pThunk->free = (int)&pThunk->thunks - (int)pThunk;
147     free = pThunk->free;
148     for (i = 0; i < count-1; i++)
149     {
150         free += 8;  /* Offset of next thunk */
151         pThunk->thunks[4*i] = free;
152     }
153     pThunk->thunks[4*i] = 0;  /* Last thunk */
154 }
155
156
157 /***********************************************************************
158  *           TASK_AllocThunk
159  *
160  * Allocate a thunk for MakeProcInstance().
161  */
162 static SEGPTR TASK_AllocThunk( HTASK16 hTask )
163 {
164     TDB *pTask;
165     THUNKS *pThunk;
166     WORD sel, base;
167     
168     if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
169     sel = pTask->hCSAlias;
170     pThunk = &pTask->thunks;
171     base = (int)pThunk - (int)pTask;
172     while (!pThunk->free)
173     {
174         sel = pThunk->next;
175         if (!sel)  /* Allocate a new segment */
176         {
177             sel = GLOBAL_Alloc( GMEM_FIXED, sizeof(THUNKS) + (MIN_THUNKS-1)*8,
178                                 pTask->hPDB, TRUE, FALSE, FALSE );
179             if (!sel) return (SEGPTR)0;
180             TASK_CreateThunks( sel, 0, MIN_THUNKS );
181             pThunk->next = sel;
182         }
183         pThunk = (THUNKS *)GlobalLock16( sel );
184         base = 0;
185     }
186     base += pThunk->free;
187     pThunk->free = *(WORD *)((BYTE *)pThunk + pThunk->free);
188     return PTR_SEG_OFF_TO_SEGPTR( sel, base );
189 }
190
191
192 /***********************************************************************
193  *           TASK_FreeThunk
194  *
195  * Free a MakeProcInstance() thunk.
196  */
197 static BOOL32 TASK_FreeThunk( HTASK16 hTask, SEGPTR thunk )
198 {
199     TDB *pTask;
200     THUNKS *pThunk;
201     WORD sel, base;
202     
203     if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
204     sel = pTask->hCSAlias;
205     pThunk = &pTask->thunks;
206     base = (int)pThunk - (int)pTask;
207     while (sel && (sel != HIWORD(thunk)))
208     {
209         sel = pThunk->next;
210         pThunk = (THUNKS *)GlobalLock16( sel );
211         base = 0;
212     }
213     if (!sel) return FALSE;
214     *(WORD *)((BYTE *)pThunk + LOWORD(thunk) - base) = pThunk->free;
215     pThunk->free = LOWORD(thunk) - base;
216     return TRUE;
217 }
218
219
220 /***********************************************************************
221  *           TASK_CallToStart
222  *
223  * 32-bit entry point for a new task. This function is responsible for
224  * setting up the registers and jumping to the 16-bit entry point.
225  */
226 static void TASK_CallToStart(void)
227 {
228     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
229     NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
230     SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
231
232     SET_CUR_THREAD( pTask->thdb );
233     CLIENT_InitThread();
234
235     /* Terminate the stack frame chain */
236     memset(THREAD_STACK16( pTask->thdb ), '\0', sizeof(STACK16FRAME));
237
238     if (pModule->flags & NE_FFLAGS_WIN32)
239     {
240         /* FIXME: all this is an ugly hack */
241
242         LPTHREAD_START_ROUTINE entry = (LPTHREAD_START_ROUTINE)
243                 RVA_PTR(pModule->module32, OptionalHeader.AddressOfEntryPoint);
244         DWORD size = PE_HEADER(pModule->module32)->OptionalHeader.SizeOfStackReserve;
245         DWORD id;
246         THDB *thdb;
247
248         pTask->userhandler = (USERSIGNALPROC)&USER_SignalProc;
249         if (pModule->heap_size)
250             LocalInit( pTask->hInstance, 0, pModule->heap_size );
251
252         InitApp( pTask->hModule );
253         MODULE_InitializeDLLs( PROCESS_Current(), 0, DLL_PROCESS_ATTACH, (LPVOID)-1 );
254         TRACE(relay, "(entryproc=%p)\n", entry );
255
256 #if 1
257         ExitProcess( entry(NULL) );
258 #else
259         CreateThread( NULL, size, entry, NULL, 0, &id );
260         thdb = THREAD_ID_TO_THDB( id );
261
262         while ( thdb->exit_code == 0x103 )
263         {
264             WaitEvent( 0 );
265             QUEUE_Signal( pTask->hSelf );
266         }
267
268         ExitProcess( thdb->exit_code );
269 #endif
270     }
271     else if (pModule->dos_image)
272     {
273         DOSVM_Enter( NULL );
274         ExitProcess( 0 );
275     }
276     else
277     {
278         /* Registers at initialization must be:
279          * ax   zero
280          * bx   stack size in bytes
281          * cx   heap size in bytes
282          * si   previous app instance
283          * di   current app instance
284          * bp   zero
285          * es   selector to the PSP
286          * ds   dgroup of the application
287          * ss   stack selector
288          * sp   top of the stack
289          */
290         CONTEXT context;
291
292         memset( &context, 0, sizeof(context) );
293         CS_reg(&context)  = GlobalHandleToSel(pSegTable[pModule->cs - 1].hSeg);
294         DS_reg(&context)  = GlobalHandleToSel(pSegTable[pModule->dgroup - 1].hSeg);
295         ES_reg(&context)  = pTask->hPDB;
296         EIP_reg(&context) = pModule->ip;
297         EBX_reg(&context) = pModule->stack_size;
298         ECX_reg(&context) = pModule->heap_size;
299         EDI_reg(&context) = context.SegDs;
300
301         TRACE(task, "Starting main program: cs:ip=%04lx:%04x ds=%04lx ss:sp=%04x:%04x\n",
302                       CS_reg(&context), IP_reg(&context), DS_reg(&context),
303                       SELECTOROF(pTask->thdb->cur_stack),
304                       OFFSETOF(pTask->thdb->cur_stack) );
305
306         Callbacks->CallRegisterShortProc( &context, 0 );
307         /* This should never return */
308         ERR( task, "Main program returned! (should never happen)\n" );
309         ExitProcess( 1 );
310     }
311 }
312
313
314 /***********************************************************************
315  *           TASK_Create
316  *
317  * NOTE: This routine might be called by a Win32 thread. We don't have
318  *       any real problems with that, since we operated merely on a private
319  *       TDB structure that is not yet linked into the task list.
320  */
321 HTASK16 TASK_Create( THDB *thdb, NE_MODULE *pModule, HINSTANCE16 hInstance,
322                      HINSTANCE16 hPrevInstance, UINT16 cmdShow)
323 {
324     HTASK16 hTask;
325     TDB *pTask;
326     LPSTR cmd_line;
327     WORD sp;
328     char *stack32Top;
329     char name[10];
330     STACK16FRAME *frame16;
331     STACK32FRAME *frame32;
332     PDB32 *pdb32 = thdb->process;
333     SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
334
335       /* Allocate the task structure */
336
337     hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
338                           pModule->self, FALSE, FALSE, FALSE );
339     if (!hTask) return 0;
340     pTask = (TDB *)GlobalLock16( hTask );
341
342     /* Fill the task structure */
343
344     pTask->nEvents       = 1;  /* So the task can be started */
345     pTask->hSelf         = hTask;
346     pTask->flags         = 0;
347
348     if (pModule->flags & NE_FFLAGS_WIN32)
349         pTask->flags    |= TDBF_WIN32;
350     if (pModule->lpDosTask)
351         pTask->flags    |= TDBF_WINOLDAP;
352
353     pTask->version       = pModule->expected_version;
354     pTask->hInstance     = hInstance;
355     pTask->hPrevInstance = hPrevInstance;
356     pTask->hModule       = pModule->self;
357     pTask->hParent       = GetCurrentTask();
358     pTask->magic         = TDB_MAGIC;
359     pTask->nCmdShow      = cmdShow;
360     pTask->thdb          = thdb;
361     pTask->curdrive      = DRIVE_GetCurrentDrive() | 0x80;
362     strcpy( pTask->curdir, "\\" );
363     lstrcpyn32A( pTask->curdir + 1, DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() ),
364                  sizeof(pTask->curdir) - 1 );
365
366       /* Create the thunks block */
367
368     TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
369
370       /* Copy the module name */
371
372     GetModuleName( pModule->self, name, sizeof(name) );
373     strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
374
375       /* Allocate a selector for the PDB */
376
377     pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB),
378                                     pModule->self, FALSE, FALSE, FALSE, NULL );
379
380       /* Fill the PDB */
381
382     pTask->pdb.int20 = 0x20cd;
383     pTask->pdb.dispatcher[0] = 0x9a;  /* ljmp */
384     PUT_DWORD(&pTask->pdb.dispatcher[1], (DWORD)NE_GetEntryPoint(
385            GetModuleHandle16("KERNEL"), 102 ));  /* KERNEL.102 is DOS3Call() */
386     pTask->pdb.savedint22 = INT_GetPMHandler( 0x22 );
387     pTask->pdb.savedint23 = INT_GetPMHandler( 0x23 );
388     pTask->pdb.savedint24 = INT_GetPMHandler( 0x24 );
389     pTask->pdb.fileHandlesPtr =
390         PTR_SEG_OFF_TO_SEGPTR( GlobalHandleToSel(pTask->hPDB),
391                                (int)&((PDB *)0)->fileHandles );
392     pTask->pdb.hFileHandles = 0;
393     memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
394     pTask->pdb.environment    = pdb32->env_db->env_sel;
395     pTask->pdb.nbFiles        = 20;
396
397     /* Fill the command line */
398
399     cmd_line = pdb32->env_db->cmd_line;
400     while (*cmd_line && (*cmd_line != ' ') && (*cmd_line != '\t')) cmd_line++;
401     while ((*cmd_line == ' ') || (*cmd_line == '\t')) cmd_line++;
402     lstrcpyn32A( pTask->pdb.cmdLine+1, cmd_line, sizeof(pTask->pdb.cmdLine)-1);
403     pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
404
405       /* Get the compatibility flags */
406
407     pTask->compat_flags = GetProfileInt32A( "Compatibility", name, 0 );
408
409       /* Allocate a code segment alias for the TDB */
410
411     pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
412                                           sizeof(TDB), pTask->hPDB, TRUE,
413                                           FALSE, FALSE, NULL );
414
415       /* Set the owner of the environment block */
416
417     FarSetOwner( pTask->pdb.environment, pTask->hPDB );
418
419       /* Default DTA overwrites command-line */
420
421     pTask->dta = PTR_SEG_OFF_TO_SEGPTR( pTask->hPDB, 
422                                 (int)&pTask->pdb.cmdLine - (int)&pTask->pdb );
423
424     /* Create the 16-bit stack frame */
425
426     if (!(sp = pModule->sp))
427         sp = pSegTable[pModule->ss-1].minsize + pModule->stack_size;
428     sp &= ~1;
429     pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( pTask->hInstance, sp );
430     pTask->thdb->cur_stack -= 2*sizeof(STACK16FRAME);
431     frame16 = (STACK16FRAME *)PTR_SEG_TO_LIN( pTask->thdb->cur_stack );
432     frame16->ebp = sp + (int)&((STACK16FRAME *)0)->bp;
433     frame16->bp = LOWORD(frame16->ebp);
434     frame16->ds = frame16->es = pTask->hInstance;
435     frame16->fs = 0;
436     frame16->entry_point = 0;
437     frame16->entry_cs = 0;
438     frame16->mutex_count = 1; /* TASK_Reschedule is called from 16-bit code */
439     /* The remaining fields will be initialized in TASK_Reschedule */
440
441     /* Create the 32-bit stack frame */
442
443     stack32Top = (char*)pTask->thdb->teb.stack_top;
444     frame16->frame32 = frame32 = (STACK32FRAME *)stack32Top - 1;
445     frame32->frame16 = pTask->thdb->cur_stack + sizeof(STACK16FRAME);
446     frame32->edi     = 0;
447     frame32->esi     = 0;
448     frame32->edx     = 0;
449     frame32->ecx     = 0;
450     frame32->ebx     = 0;
451     frame32->retaddr = (DWORD)TASK_CallToStart;
452     /* The remaining fields will be initialized in TASK_Reschedule */
453
454     if (!THREAD_Current()->cur_stack)
455         THREAD_Current()->cur_stack = pTask->thdb->cur_stack;
456
457
458     TRACE(task, "module='%s' cmdline='%s' task=%04x\n",
459           name, cmd_line, hTask );
460
461     return hTask;
462 }
463
464 /***********************************************************************
465  *           TASK_StartTask
466  *
467  * NOTE: This routine might be called by a Win32 thread. Thus, we need
468  *       to be careful to protect global data structures. We do this
469  *       by entering the Win16Lock while linking the task into the
470  *       global task list.
471  */
472 void TASK_StartTask( HTASK16 hTask )
473 {
474     /* Add the task to the linked list */
475
476     SYSLEVEL_EnterWin16Lock();
477     TASK_LinkTask( hTask );
478     SYSLEVEL_LeaveWin16Lock();
479
480     TRACE(task, "linked task %04x\n", hTask );
481
482     /* Get the task up and running. If we ourselves are a 16-bit task,
483        we simply Yield(). If we are 32-bit however, we need to signal
484        the main process somehow (NOT YET IMPLEMENTED!) */
485
486     if ( GetCurrentTask() )
487         if ( THREAD_IsWin16( THREAD_Current() ) )
488             Yield16();
489         else
490             FIXME(task, "Don't know how to start 16-bit task from 32-bit thread. Move the mouse!\n");
491 }
492
493
494 /***********************************************************************
495  *           TASK_DeleteTask
496  */
497 static void TASK_DeleteTask( HTASK16 hTask )
498 {
499     TDB *pTask;
500     HGLOBAL16 hPDB;
501
502     if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
503     hPDB = pTask->hPDB;
504
505     pTask->magic = 0xdead; /* invalidate signature */
506
507     /* Delete the Win32 part of the task */
508
509     K32OBJ_DecCount( &pTask->thdb->process->header );
510     K32OBJ_DecCount( &pTask->thdb->header );
511
512     /* Free the task module */
513
514     FreeModule16( pTask->hModule );
515
516     /* Free the selector aliases */
517
518     GLOBAL_FreeBlock( pTask->hCSAlias );
519     GLOBAL_FreeBlock( pTask->hPDB );
520
521     /* Free the task structure itself */
522
523     GlobalFree16( hTask );
524
525     /* Free all memory used by this task (including the 32-bit stack, */
526     /* the environment block and the thunk segments). */
527
528     GlobalFreeAll( hPDB );
529 }
530
531
532 /***********************************************************************
533  *           TASK_KillCurrentTask
534  *
535  * Kill the currently running task. As it's not possible to kill the
536  * current task like this, it is simply marked for destruction, and will
537  * be killed when either TASK_Reschedule or this function is called again 
538  * in the context of another task.
539  */
540 void TASK_KillCurrentTask( INT16 exitCode )
541 {
542     TDB* pTask = (TDB*) GlobalLock16( GetCurrentTask() );
543     NE_MODULE* pModule = NE_GetPtr( pTask->hModule );
544     if (!pTask) USER_ExitWindows();  /* No current task yet */
545
546     if ( !THREAD_IsWin16( THREAD_Current() ) )
547     {
548         FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
549         return;
550     }
551
552     /* Enter the Win16Lock to protect global data structures
553        NOTE: We never explicitly leave it again. This shouldn't matter
554              though, as it will be released in TASK_Reschedule and this
555              task won't ever get scheduled again ... */
556
557     SYSLEVEL_EnterWin16Lock();
558
559     assert(hCurrentTask == GetCurrentTask());
560
561     TRACE(task, "Killing task %04x\n", hCurrentTask );
562
563     /* Delete active sockets */
564
565     if( pTask->pwsi )
566         WINSOCK_DeleteTaskWSI( pTask, pTask->pwsi );
567
568 #ifdef MZ_SUPPORTED
569     /* Kill DOS VM task */
570     if ( pModule->lpDosTask )
571         MZ_KillModule( pModule->lpDosTask );
572 #endif
573
574     /* Perform USER cleanup */
575
576     if (pTask->userhandler)
577         pTask->userhandler( hCurrentTask, USIG_TERMINATION, 0,
578                             pTask->hInstance, pTask->hQueue );
579
580     if (hTaskToKill && (hTaskToKill != hCurrentTask))
581     {
582         /* If another task is already marked for destruction, */
583         /* we can kill it now, as we are in another context.  */ 
584         TASK_DeleteTask( hTaskToKill );
585     }
586
587     if (nTaskCount <= 1)
588     {
589         TRACE(task, "this is the last task, exiting\n" );
590         USER_ExitWindows();
591     }
592
593     /* Remove the task from the list to be sure we never switch back to it */
594     TASK_UnlinkTask( hCurrentTask );
595     if( nTaskCount )
596     {
597         TDB* p = (TDB *)GlobalLock16( hFirstTask );
598         while( p )
599         {
600             if( p->hYieldTo == hCurrentTask ) p->hYieldTo = 0;
601             p = (TDB *)GlobalLock16( p->hNext );
602         }
603     }
604
605     hTaskToKill = hCurrentTask;
606     hLockedTask = 0;
607
608     pTask->nEvents = 0;
609     TASK_YieldToSystem(pTask);
610
611     /* We should never return from this Yield() */
612
613     ERR(task,"Return of the living dead %04x!!!\n", hCurrentTask);
614     exit(1);
615 }
616
617 /***********************************************************************
618  *           TASK_Reschedule
619  *
620  * This is where all the magic of task-switching happens!
621  *
622  * Note: This function should only be called via the TASK_YieldToSystem()
623  *       wrapper, to make sure that all the context is saved correctly.
624  *   
625  *       It must not call functions that may yield control.
626  */
627 void TASK_Reschedule(void)
628 {
629     TDB *pOldTask = NULL, *pNewTask;
630     HTASK16 hTask = 0;
631     STACK16FRAME *newframe16;
632
633     /* NOTE: As we are entered from 16-bit code, we hold the Win16Lock.
634              We hang onto it thoughout most of this routine, so that accesses
635              to global variables (most notably the task list) are protected. */
636     assert(hCurrentTask == GetCurrentTask());
637
638     TRACE(task, "entered with hTask %04x (pid %d)\n", hCurrentTask, getpid());
639
640 #ifdef CONFIG_IPC
641     /* FIXME: What about the Win16Lock ??? */
642     dde_reschedule();
643 #endif
644       /* First check if there's a task to kill */
645
646     if (hTaskToKill && (hTaskToKill != hCurrentTask))
647     {
648         TASK_DeleteTask( hTaskToKill );
649         hTaskToKill = 0;
650     }
651
652     /* Find a task to yield to */
653
654     pOldTask = (TDB *)GlobalLock16( hCurrentTask );
655     if (pOldTask && pOldTask->hYieldTo)
656     {
657         /* check for DirectedYield() */
658
659         hTask = pOldTask->hYieldTo;
660         pNewTask = (TDB *)GlobalLock16( hTask );
661         if( !pNewTask || !pNewTask->nEvents) hTask = 0;
662         pOldTask->hYieldTo = 0;
663     }
664
665     /* extract hardware events only! */
666
667     if (!hTask) EVENT_WaitNetEvent( FALSE, TRUE );
668
669     while (!hTask)
670     {
671         /* Find a task that has an event pending */
672
673         hTask = hFirstTask;
674         while (hTask)
675         {
676             pNewTask = (TDB *)GlobalLock16( hTask );
677
678             TRACE(task, "\ttask = %04x, events = %i\n", hTask, pNewTask->nEvents);
679
680             if (pNewTask->nEvents) break;
681             hTask = pNewTask->hNext;
682         }
683         if (hLockedTask && (hTask != hLockedTask)) hTask = 0;
684         if (hTask) break;
685
686         /* No task found, wait for some events to come in */
687
688         /* NOTE: We release the Win16Lock while waiting for events. This is to enable
689                  Win32 threads to thunk down to 16-bit temporarily. Since Win16
690                  tasks won't execute and Win32 threads are not allowed to enter 
691                  TASK_Reschedule anyway, there should be no re-entrancy problem ... */
692
693         SYSLEVEL_ReleaseWin16Lock();
694         EVENT_WaitNetEvent( TRUE, TRUE );
695         SYSLEVEL_RestoreWin16Lock();
696     }
697
698     if (hTask == hCurrentTask) 
699     {
700        TRACE(task, "returning to the current task(%04x)\n", hTask );
701        return;  /* Nothing to do */
702     }
703     pNewTask = (TDB *)GlobalLock16( hTask );
704     TRACE(task, "Switching to task %04x (%.8s)\n",
705                   hTask, pNewTask->module_name );
706
707      /* Make the task the last in the linked list (round-robin scheduling) */
708
709     pNewTask->priority++;
710     TASK_UnlinkTask( hTask );
711     TASK_LinkTask( hTask );
712     pNewTask->priority--;
713
714     /* Finish initializing the new task stack if necessary */
715
716     newframe16 = THREAD_STACK16( pNewTask->thdb );
717     if (!newframe16->entry_cs)
718     {
719         STACK16FRAME *oldframe16 = CURRENT_STACK16;
720         STACK32FRAME *oldframe32 = oldframe16->frame32;
721         STACK32FRAME *newframe32 = newframe16->frame32;
722         newframe16->entry_ip     = oldframe16->entry_ip;
723         newframe16->entry_cs     = oldframe16->entry_cs;
724         newframe16->ip           = oldframe16->ip;
725         newframe16->cs           = oldframe16->cs;
726         newframe32->ebp          = oldframe32->ebp;
727         newframe32->restore_addr = oldframe32->restore_addr;
728         newframe32->codeselector = oldframe32->codeselector;
729     }
730     
731     /* Switch to the new stack */
732
733     /* NOTE: We need to release/restore the Win16Lock, as the task
734              switched to might be at another recursion level than
735              the old task ... */
736
737     SYSLEVEL_ReleaseWin16Lock();
738
739     hCurrentTask = hTask;
740     SET_CUR_THREAD( pNewTask->thdb );
741     pNewTask->ss_sp = pNewTask->thdb->cur_stack;
742
743     SYSLEVEL_RestoreWin16Lock();
744 }
745
746
747 /***********************************************************************
748  *           TASK_YieldToSystem
749  *
750  * Scheduler interface, this way we ensure that all "unsafe" events are
751  * processed outside the scheduler.
752  */
753 void TASK_YieldToSystem(TDB* pTask)
754 {
755   MESSAGEQUEUE*         pQ;
756
757   if ( !THREAD_IsWin16( THREAD_Current() ) )
758   {
759     FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
760     return;
761   }
762
763   Callbacks->CallTaskRescheduleProc();
764
765   if( pTask )
766   {
767     pQ = (MESSAGEQUEUE*)GlobalLock16(pTask->hQueue);
768     if( pQ && pQ->flags & QUEUE_FLAG_XEVENT &&
769             !(pQ->wakeBits & (QS_SENDMESSAGE | QS_SMRESULT)) )
770     {
771       pQ->flags &= ~QUEUE_FLAG_XEVENT;
772       EVENT_WaitNetEvent( FALSE, FALSE );
773     }
774   }
775 }
776
777
778 /***********************************************************************
779  *           InitTask  (KERNEL.91)
780  *
781  * Called by the application startup code.
782  */
783 void WINAPI InitTask( CONTEXT *context )
784 {
785     TDB *pTask;
786     NE_MODULE *pModule;
787     SEGTABLEENTRY *pSegTable;
788     INSTANCEDATA *pinstance;
789     LONG stacklow, stackhi;
790
791     if (context) EAX_reg(context) = 0;
792     if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
793     if (!(pModule = NE_GetPtr( pTask->hModule ))) return;
794
795     /* This is a hack to install task USER signal handler before 
796      * implicitly loaded DLLs are initialized (see windows/user.c) */
797
798     pTask->userhandler = (USERSIGNALPROC)&USER_SignalProc;
799
800     /* Initialize implicitly loaded DLLs */
801     NE_InitializeDLLs( pTask->hModule );
802
803     if (context)
804     {
805         /* Registers on return are:
806          * ax     1 if OK, 0 on error
807          * cx     stack limit in bytes
808          * dx     cmdShow parameter
809          * si     instance handle of the previous instance
810          * di     instance handle of the new task
811          * es:bx  pointer to command-line inside PSP
812          *
813          * 0 (=%bp) is pushed on the stack
814          */
815         SEGPTR ptr = STACK16_PUSH( pTask->thdb, sizeof(WORD) );
816         *(WORD *)PTR_SEG_TO_LIN(ptr) = 0;
817         SP_reg(context) -= 2;
818
819         EAX_reg(context) = 1;
820         
821         if (!pTask->pdb.cmdLine[0]) EBX_reg(context) = 0x80;
822         else
823         {
824             LPBYTE p = &pTask->pdb.cmdLine[1];
825             while ((*p == ' ') || (*p == '\t')) p++;
826             EBX_reg(context) = 0x80 + (p - pTask->pdb.cmdLine);
827         }
828         ECX_reg(context) = pModule->stack_size;
829         EDX_reg(context) = pTask->nCmdShow;
830         ESI_reg(context) = (DWORD)pTask->hPrevInstance;
831         EDI_reg(context) = (DWORD)pTask->hInstance;
832         ES_reg (context) = (WORD)pTask->hPDB;
833     }
834
835     /* Initialize the local heap */
836     if ( pModule->heap_size )
837     {
838         LocalInit( pTask->hInstance, 0, pModule->heap_size );
839     }    
840
841     /* Initialize the INSTANCEDATA structure */
842     pSegTable = NE_SEG_TABLE( pModule );
843     stacklow = pSegTable[pModule->ss - 1].minsize;
844     stackhi  = stacklow + pModule->stack_size;
845     if (stackhi > 0xffff) stackhi = 0xffff;
846     pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
847     pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
848     pinstance->stacktop    = stacklow; 
849     pinstance->stackmin    = OFFSETOF( pTask->thdb->cur_stack );
850 }
851
852
853 /***********************************************************************
854  *           WaitEvent  (KERNEL.30)
855  */
856 BOOL16 WINAPI WaitEvent( HTASK16 hTask )
857 {
858     TDB *pTask;
859
860     if (!hTask) hTask = GetCurrentTask();
861     pTask = (TDB *)GlobalLock16( hTask );
862
863     if ( !THREAD_IsWin16( THREAD_Current() ) )
864     {
865         FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
866         return TRUE;
867     }
868
869     if (pTask->nEvents > 0)
870     {
871         pTask->nEvents--;
872         return FALSE;
873     }
874     TASK_YieldToSystem(pTask);
875
876     /* When we get back here, we have an event */
877
878     if (pTask->nEvents > 0) pTask->nEvents--;
879     return TRUE;
880 }
881
882
883 /***********************************************************************
884  *           PostEvent  (KERNEL.31)
885  */
886 void WINAPI PostEvent( HTASK16 hTask )
887 {
888     TDB *pTask;
889
890     if (!hTask) hTask = GetCurrentTask();
891     if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
892
893     if ( !THREAD_IsWin16( THREAD_Current() ) )
894     {
895         WARN(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
896         /* return; */
897     }
898
899     pTask->nEvents++;
900 }
901
902
903 /***********************************************************************
904  *           SetPriority  (KERNEL.32)
905  */
906 void WINAPI SetPriority( HTASK16 hTask, INT16 delta )
907 {
908     TDB *pTask;
909     INT16 newpriority;
910
911     if (!hTask) hTask = GetCurrentTask();
912     if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
913     newpriority = pTask->priority + delta;
914     if (newpriority < -32) newpriority = -32;
915     else if (newpriority > 15) newpriority = 15;
916
917     pTask->priority = newpriority + 1;
918     TASK_UnlinkTask( hTask );
919     TASK_LinkTask( hTask );
920     pTask->priority--;
921 }
922
923
924 /***********************************************************************
925  *           LockCurrentTask  (KERNEL.33)
926  */
927 HTASK16 WINAPI LockCurrentTask( BOOL16 bLock )
928 {
929     if (bLock) hLockedTask = GetCurrentTask();
930     else hLockedTask = 0;
931     return hLockedTask;
932 }
933
934
935 /***********************************************************************
936  *           IsTaskLocked  (KERNEL.122)
937  */
938 HTASK16 WINAPI IsTaskLocked(void)
939 {
940     return hLockedTask;
941 }
942
943
944 /***********************************************************************
945  *           OldYield  (KERNEL.117)
946  */
947 void WINAPI OldYield(void)
948 {
949     TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
950
951     if ( !THREAD_IsWin16( THREAD_Current() ) )
952     {
953         FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
954         return;
955     }
956
957     if (pCurTask) pCurTask->nEvents++;  /* Make sure we get back here */
958     TASK_YieldToSystem(pCurTask);
959     if (pCurTask) pCurTask->nEvents--;
960 }
961
962
963 /***********************************************************************
964  *           DirectedYield  (KERNEL.150)
965  */
966 void WINAPI DirectedYield( HTASK16 hTask )
967 {
968     TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
969
970     if ( !THREAD_IsWin16( THREAD_Current() ) )
971     {
972         FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
973         return;
974     }
975
976     pCurTask->hYieldTo = hTask;
977     OldYield();
978 }
979
980
981 /***********************************************************************
982  *           UserYield  (USER.332)
983  */
984 void WINAPI UserYield(void)
985 {
986     TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
987     MESSAGEQUEUE *queue = (MESSAGEQUEUE *)GlobalLock16( pCurTask->hQueue );
988
989     if ( !THREAD_IsWin16( THREAD_Current() ) )
990     {
991         FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
992         return;
993     }
994
995     /* Handle sent messages */
996     while (queue && (queue->wakeBits & QS_SENDMESSAGE))
997         QUEUE_ReceiveMessage( queue );
998
999     OldYield();
1000
1001     queue = (MESSAGEQUEUE *)GlobalLock16( pCurTask->hQueue );
1002     while (queue && (queue->wakeBits & QS_SENDMESSAGE))
1003         QUEUE_ReceiveMessage( queue );
1004 }
1005
1006
1007 /***********************************************************************
1008  *           Yield16  (KERNEL.29)
1009  */
1010 void WINAPI Yield16(void)
1011 {
1012     TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1013
1014     if ( !THREAD_IsWin16( THREAD_Current() ) )
1015     {
1016         FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1017         return;
1018     }
1019
1020     if (pCurTask) pCurTask->hYieldTo = 0;
1021     if (pCurTask && pCurTask->hQueue) UserYield();
1022     else OldYield();
1023 }
1024
1025
1026 /***********************************************************************
1027  *           MakeProcInstance16  (KERNEL.51)
1028  */
1029 FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
1030 {
1031     BYTE *thunk,*lfunc;
1032     SEGPTR thunkaddr;
1033
1034     if (!func) {
1035       ERR(task, "Ouch ! MakeProcInstance called with func == NULL !\n");
1036       return (FARPROC16)0; /* Windows seems to do the same */
1037     }
1038     if (!hInstance) hInstance = CURRENT_DS;
1039     thunkaddr = TASK_AllocThunk( GetCurrentTask() );
1040     if (!thunkaddr) return (FARPROC16)0;
1041     thunk = PTR_SEG_TO_LIN( thunkaddr );
1042     lfunc = PTR_SEG_TO_LIN( func );
1043
1044     TRACE(task, "(%08lx,%04x): got thunk %08lx\n",
1045                   (DWORD)func, hInstance, (DWORD)thunkaddr );
1046     if (((lfunc[0]==0x8c) && (lfunc[1]==0xd8)) ||
1047         ((lfunc[0]==0x1e) && (lfunc[1]==0x58))
1048     ) {
1049         FIXME(task,"thunk would be useless for %p, overwriting with nop;nop;\n", func );
1050         lfunc[0]=0x90; /* nop */
1051         lfunc[1]=0x90; /* nop */
1052     }
1053     
1054     *thunk++ = 0xb8;    /* movw instance, %ax */
1055     *thunk++ = (BYTE)(hInstance & 0xff);
1056     *thunk++ = (BYTE)(hInstance >> 8);
1057     *thunk++ = 0xea;    /* ljmp func */
1058     *(DWORD *)thunk = (DWORD)func;
1059     return (FARPROC16)thunkaddr;
1060 }
1061
1062
1063 /***********************************************************************
1064  *           FreeProcInstance16  (KERNEL.52)
1065  */
1066 void WINAPI FreeProcInstance16( FARPROC16 func )
1067 {
1068     TRACE(task, "(%08lx)\n", (DWORD)func );
1069     TASK_FreeThunk( GetCurrentTask(), (SEGPTR)func );
1070 }
1071
1072
1073 /**********************************************************************
1074  *          GetCodeHandle    (KERNEL.93)
1075  */
1076 HANDLE16 WINAPI GetCodeHandle( FARPROC16 proc )
1077 {
1078     HANDLE16 handle;
1079     BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1080
1081     /* Return the code segment containing 'proc'. */
1082     /* Not sure if this is really correct (shouldn't matter that much). */
1083
1084     /* Check if it is really a thunk */
1085     if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1086         handle = GlobalHandle16( thunk[6] + (thunk[7] << 8) );
1087     else
1088         handle = GlobalHandle16( HIWORD(proc) );
1089
1090     return handle;
1091 }
1092
1093 /**********************************************************************
1094  *          GetCodeInfo    (KERNEL.104)
1095  */
1096 VOID WINAPI GetCodeInfo( FARPROC16 proc, SEGINFO *segInfo )
1097 {
1098     BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1099     NE_MODULE *pModule = NULL;
1100     SEGTABLEENTRY *pSeg = NULL;
1101     WORD segNr;
1102
1103     /* proc is either a thunk, or else a pair of module handle
1104        and segment number. In the first case, we also need to
1105        extract module and segment number. */
1106
1107     if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1108     {
1109         WORD selector = thunk[6] + (thunk[7] << 8);
1110         pModule = NE_GetPtr( GlobalHandle16( selector ) );
1111         pSeg = pModule? NE_SEG_TABLE( pModule ) : NULL;
1112
1113         if ( pModule )
1114             for ( segNr = 0; segNr < pModule->seg_count; segNr++, pSeg++ )
1115                 if ( GlobalHandleToSel(pSeg->hSeg) == selector )
1116                     break;
1117
1118         if ( pModule && segNr >= pModule->seg_count )
1119             pSeg = NULL;
1120     }
1121     else
1122     {
1123         pModule = NE_GetPtr( HIWORD( proc ) );
1124         segNr   = LOWORD( proc );
1125
1126         if ( pModule && segNr < pModule->seg_count )
1127             pSeg = NE_SEG_TABLE( pModule ) + segNr;
1128     }
1129
1130     /* fill in segment information */
1131
1132     segInfo->offSegment = pSeg? pSeg->filepos : 0;
1133     segInfo->cbSegment  = pSeg? pSeg->size : 0;
1134     segInfo->flags      = pSeg? pSeg->flags : 0;
1135     segInfo->cbAlloc    = pSeg? pSeg->minsize : 0;
1136     segInfo->h          = pSeg? pSeg->hSeg : 0;
1137     segInfo->alignShift = pModule? pModule->alignment : 0;
1138 }
1139
1140
1141 /**********************************************************************
1142  *          DefineHandleTable16    (KERNEL.94)
1143  */
1144 BOOL16 WINAPI DefineHandleTable16( WORD wOffset )
1145 {
1146     return TRUE;  /* FIXME */
1147 }
1148
1149
1150 /***********************************************************************
1151  *           SetTaskQueue  (KERNEL.34)
1152  */
1153 HQUEUE16 WINAPI SetTaskQueue( HTASK16 hTask, HQUEUE16 hQueue )
1154 {
1155     HQUEUE16 hPrev;
1156     TDB *pTask;
1157
1158     if (!hTask) hTask = GetCurrentTask();
1159     if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1160
1161     hPrev = pTask->hQueue;
1162     pTask->hQueue = hQueue;
1163
1164     TIMER_SwitchQueue( hPrev, hQueue );
1165
1166     return hPrev;
1167 }
1168
1169
1170 /***********************************************************************
1171  *           GetTaskQueue  (KERNEL.35)
1172  */
1173 HQUEUE16 WINAPI GetTaskQueue( HTASK16 hTask )
1174 {
1175     TDB *pTask;
1176
1177     if (!hTask) hTask = GetCurrentTask();
1178     if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1179     return pTask->hQueue;
1180 }
1181
1182 /***********************************************************************
1183  *           SetThreadQueue  (KERNEL.463)
1184  */
1185 HQUEUE16 WINAPI SetThreadQueue( DWORD thread, HQUEUE16 hQueue )
1186 {
1187     THDB *thdb = THREAD_IdToTHDB( thread );
1188     HQUEUE16 oldQueue = thdb? thdb->teb.queue : 0;
1189
1190     if ( thdb )
1191     {
1192         thdb->teb.queue = hQueue;
1193
1194         if ( GetTaskQueue( thdb->process->task ) == oldQueue )
1195             SetTaskQueue( thdb->process->task, hQueue );
1196     }
1197
1198     return oldQueue;
1199 }
1200
1201 /***********************************************************************
1202  *           GetThreadQueue  (KERNEL.464)
1203  */
1204 HQUEUE16 WINAPI GetThreadQueue( DWORD thread )
1205 {
1206     THDB *thdb = THREAD_IdToTHDB( thread );
1207     return (HQUEUE16)(thdb? thdb->teb.queue : 0);
1208 }
1209
1210 /***********************************************************************
1211  *           SetFastQueue  (KERNEL.624)
1212  */
1213 VOID WINAPI SetFastQueue( DWORD thread, HANDLE32 hQueue )
1214 {
1215     THDB *thdb = THREAD_IdToTHDB( thread );
1216     if ( thdb ) thdb->teb.queue = (HQUEUE16) hQueue;
1217 }
1218
1219 /***********************************************************************
1220  *           GetFastQueue  (KERNEL.625)
1221  */
1222 HANDLE32 WINAPI GetFastQueue( void )
1223 {
1224     THDB *thdb = THREAD_Current();
1225     if (!thdb) return 0;
1226
1227     if (!(thdb->teb.queue))
1228         FIXME( task, "(): should initialize thread-local queue, expect failure!\n" );
1229
1230     return (HANDLE32)thdb->teb.queue;
1231 }
1232
1233 /***********************************************************************
1234  *           SwitchStackTo   (KERNEL.108)
1235  */
1236 void WINAPI SwitchStackTo( WORD seg, WORD ptr, WORD top )
1237 {
1238     TDB *pTask;
1239     STACK16FRAME *oldFrame, *newFrame;
1240     INSTANCEDATA *pData;
1241     UINT16 copySize;
1242
1243     if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1244     if (!(pData = (INSTANCEDATA *)GlobalLock16( seg ))) return;
1245     TRACE(task, "old=%04x:%04x new=%04x:%04x\n",
1246                   SELECTOROF( pTask->thdb->cur_stack ),
1247                   OFFSETOF( pTask->thdb->cur_stack ), seg, ptr );
1248
1249     /* Save the old stack */
1250
1251     oldFrame = THREAD_STACK16( pTask->thdb );
1252     /* pop frame + args and push bp */
1253     pData->old_ss_sp   = pTask->thdb->cur_stack - sizeof(STACK16FRAME)
1254                            - 2 * sizeof(WORD);
1255     *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp) = oldFrame->bp;
1256     pData->stacktop    = top;
1257     pData->stackmin    = ptr;
1258     pData->stackbottom = ptr;
1259
1260     /* Switch to the new stack */
1261
1262     /* Note: we need to take the 3 arguments into account; otherwise,
1263      * the stack will underflow upon return from this function.
1264      */
1265     copySize = oldFrame->bp - OFFSETOF(pData->old_ss_sp);
1266     copySize += 3 * sizeof(WORD) + sizeof(STACK16FRAME);
1267     pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( seg, ptr - copySize );
1268     newFrame = THREAD_STACK16( pTask->thdb );
1269
1270     /* Copy the stack frame and the local variables to the new stack */
1271
1272     memmove( newFrame, oldFrame, copySize );
1273     newFrame->bp = ptr;
1274     *(WORD *)PTR_SEG_OFF_TO_LIN( seg, ptr ) = 0;  /* clear previous bp */
1275 }
1276
1277
1278 /***********************************************************************
1279  *           SwitchStackBack   (KERNEL.109)
1280  */
1281 void WINAPI SwitchStackBack( CONTEXT *context )
1282 {
1283     TDB *pTask;
1284     STACK16FRAME *oldFrame, *newFrame;
1285     INSTANCEDATA *pData;
1286
1287     if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1288     if (!(pData = (INSTANCEDATA *)GlobalLock16(SELECTOROF(pTask->thdb->cur_stack))))
1289         return;
1290     if (!pData->old_ss_sp)
1291     {
1292         WARN( task, "No previous SwitchStackTo\n" );
1293         return;
1294     }
1295     TRACE(task, "restoring stack %04x:%04x\n",
1296                  SELECTOROF(pData->old_ss_sp), OFFSETOF(pData->old_ss_sp) );
1297
1298     oldFrame = THREAD_STACK16( pTask->thdb );
1299
1300     /* Pop bp from the previous stack */
1301
1302     BP_reg(context) = *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp);
1303     pData->old_ss_sp += sizeof(WORD);
1304
1305     /* Switch back to the old stack */
1306
1307     pTask->thdb->cur_stack = pData->old_ss_sp - sizeof(STACK16FRAME);
1308     SS_reg(context)  = SELECTOROF(pData->old_ss_sp);
1309     ESP_reg(context) = OFFSETOF(pData->old_ss_sp) - sizeof(DWORD); /*ret addr*/
1310     pData->old_ss_sp = 0;
1311
1312     /* Build a stack frame for the return */
1313
1314     newFrame = THREAD_STACK16( pTask->thdb );
1315     newFrame->frame32 = oldFrame->frame32;
1316     if (TRACE_ON(relay))
1317     {
1318         newFrame->entry_ip = oldFrame->entry_ip;
1319         newFrame->entry_cs = oldFrame->entry_cs;
1320     }
1321 }
1322
1323
1324 /***********************************************************************
1325  *           GetTaskQueueDS  (KERNEL.118)
1326  */
1327 void WINAPI GetTaskQueueDS( CONTEXT *context )
1328 {
1329     DS_reg(context) = GlobalHandleToSel( GetTaskQueue(0) );
1330 }
1331
1332
1333 /***********************************************************************
1334  *           GetTaskQueueES  (KERNEL.119)
1335  */
1336 void WINAPI GetTaskQueueES( CONTEXT *context )
1337 {
1338     ES_reg(context) = GlobalHandleToSel( GetTaskQueue(0) );
1339 }
1340
1341
1342 /***********************************************************************
1343  *           GetCurrentTask   (KERNEL.36)
1344  */
1345 HTASK16 WINAPI GetCurrentTask(void)
1346 {
1347     return THREAD_InitDone? PROCESS_Current()->task : 0;
1348 }
1349
1350 DWORD WINAPI WIN16_GetCurrentTask(void)
1351 {
1352     /* This is the version used by relay code; the first task is */
1353     /* returned in the high word of the result */
1354     return MAKELONG( GetCurrentTask(), hFirstTask );
1355 }
1356
1357
1358 /***********************************************************************
1359  *           GetCurrentPDB   (KERNEL.37)
1360  */
1361 HANDLE16 WINAPI GetCurrentPDB(void)
1362 {
1363     TDB *pTask;
1364
1365     if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1366     return pTask->hPDB;
1367 }
1368
1369
1370 /***********************************************************************
1371  *           GetInstanceData   (KERNEL.54)
1372  */
1373 INT16 WINAPI GetInstanceData( HINSTANCE16 instance, WORD buffer, INT16 len )
1374 {
1375     char *ptr = (char *)GlobalLock16( instance );
1376     if (!ptr || !len) return 0;
1377     if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
1378     memcpy( (char *)GlobalLock16(CURRENT_DS) + buffer, ptr + buffer, len );
1379     return len;
1380 }
1381
1382
1383 /***********************************************************************
1384  *           GetExeVersion   (KERNEL.105)
1385  */
1386 WORD WINAPI GetExeVersion(void)
1387 {
1388     TDB *pTask;
1389
1390     if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1391     return pTask->version;
1392 }
1393
1394
1395 /***********************************************************************
1396  *           SetErrorMode16   (KERNEL.107)
1397  */
1398 UINT16 WINAPI SetErrorMode16( UINT16 mode )
1399 {
1400     TDB *pTask;
1401     UINT16 oldMode;
1402
1403     if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1404     oldMode = pTask->error_mode;
1405     pTask->error_mode = mode;
1406     pTask->thdb->process->error_mode = mode;
1407     return oldMode;
1408 }
1409
1410
1411 /***********************************************************************
1412  *           SetErrorMode32   (KERNEL32.486)
1413  */
1414 UINT32 WINAPI SetErrorMode32( UINT32 mode )
1415 {
1416     return SetErrorMode16( (UINT16)mode );
1417 }
1418
1419
1420 /***********************************************************************
1421  *           GetDOSEnvironment   (KERNEL.131)
1422  */
1423 SEGPTR WINAPI GetDOSEnvironment(void)
1424 {
1425     TDB *pTask;
1426
1427     if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1428     return PTR_SEG_OFF_TO_SEGPTR( pTask->pdb.environment, 0 );
1429 }
1430
1431
1432 /***********************************************************************
1433  *           GetNumTasks   (KERNEL.152)
1434  */
1435 UINT16 WINAPI GetNumTasks(void)
1436 {
1437     return nTaskCount;
1438 }
1439
1440
1441 /***********************************************************************
1442  *           GetTaskDS   (KERNEL.155)
1443  *
1444  * Note: this function apparently returns a DWORD with LOWORD == HIWORD.
1445  * I don't think we need to bother with this.
1446  */
1447 HINSTANCE16 WINAPI GetTaskDS(void)
1448 {
1449     TDB *pTask;
1450
1451     if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1452     return pTask->hInstance;
1453 }
1454
1455
1456 /***********************************************************************
1457  *           IsTask   (KERNEL.320)
1458  */
1459 BOOL16 WINAPI IsTask( HTASK16 hTask )
1460 {
1461     TDB *pTask;
1462
1463     if (!(pTask = (TDB *)GlobalLock16( hTask ))) return FALSE;
1464     if (GlobalSize16( hTask ) < sizeof(TDB)) return FALSE;
1465     return (pTask->magic == TDB_MAGIC);
1466 }
1467
1468
1469 /***********************************************************************
1470  *           SetTaskSignalProc   (KERNEL.38)
1471  *
1472  * Real 16-bit interface is provided by the THUNK_SetTaskSignalProc.
1473  */
1474 FARPROC16 WINAPI SetTaskSignalProc( HTASK16 hTask, FARPROC16 proc )
1475 {
1476     TDB *pTask;
1477     FARPROC16 oldProc;
1478
1479     if (!hTask) hTask = GetCurrentTask();
1480     if (!(pTask = (TDB *)GlobalLock16( hTask ))) return NULL;
1481     oldProc = (FARPROC16)pTask->userhandler;
1482     pTask->userhandler = (USERSIGNALPROC)proc;
1483     return oldProc;
1484 }
1485
1486
1487 /***********************************************************************
1488  *           SetSigHandler   (KERNEL.140)
1489  */
1490 WORD WINAPI SetSigHandler( FARPROC16 newhandler, FARPROC16* oldhandler,
1491                            UINT16 *oldmode, UINT16 newmode, UINT16 flag )
1492 {
1493     FIXME(task,"(%p,%p,%p,%d,%d), unimplemented.\n",
1494           newhandler,oldhandler,oldmode,newmode,flag );
1495
1496     if (flag != 1) return 0;
1497     if (!newmode) newhandler = NULL;  /* Default handler */
1498     if (newmode != 4)
1499     {
1500         TDB *pTask;
1501
1502         if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1503         if (oldmode) *oldmode = pTask->signal_flags;
1504         pTask->signal_flags = newmode;
1505         if (oldhandler) *oldhandler = pTask->sighandler;
1506         pTask->sighandler = newhandler;
1507     }
1508     return 0;
1509 }
1510
1511
1512 /***********************************************************************
1513  *           GlobalNotify   (KERNEL.154)
1514  */
1515 VOID WINAPI GlobalNotify( FARPROC16 proc )
1516 {
1517     TDB *pTask;
1518
1519     if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1520     pTask->discardhandler = proc;
1521 }
1522
1523
1524 /***********************************************************************
1525  *           GetExePtr   (KERNEL.133)
1526  */
1527 static HMODULE16 GetExePtrHelper( HANDLE16 handle, HTASK16 *hTask )
1528 {
1529     char *ptr;
1530     HANDLE16 owner;
1531
1532       /* Check for module handle */
1533
1534     if (!(ptr = GlobalLock16( handle ))) return 0;
1535     if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return handle;
1536
1537       /* Search for this handle inside all tasks */
1538
1539     *hTask = hFirstTask;
1540     while (*hTask)
1541     {
1542         TDB *pTask = (TDB *)GlobalLock16( *hTask );
1543         if ((*hTask == handle) ||
1544             (pTask->hInstance == handle) ||
1545             (pTask->hQueue == handle) ||
1546             (pTask->hPDB == handle)) return pTask->hModule;
1547         *hTask = pTask->hNext;
1548     }
1549
1550       /* Check the owner for module handle */
1551
1552     owner = FarGetOwner( handle );
1553     if (!(ptr = GlobalLock16( owner ))) return 0;
1554     if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return owner;
1555
1556       /* Search for the owner inside all tasks */
1557
1558     *hTask = hFirstTask;
1559     while (*hTask)
1560     {
1561         TDB *pTask = (TDB *)GlobalLock16( *hTask );
1562         if ((*hTask == owner) ||
1563             (pTask->hInstance == owner) ||
1564             (pTask->hQueue == owner) ||
1565             (pTask->hPDB == owner)) return pTask->hModule;
1566         *hTask = pTask->hNext;
1567     }
1568
1569     return 0;
1570 }
1571
1572 HMODULE16 WINAPI GetExePtr( HANDLE16 handle )
1573 {
1574     HTASK16 dummy;
1575     return GetExePtrHelper( handle, &dummy );
1576 }
1577
1578 void WINAPI WIN16_GetExePtr( CONTEXT *context )
1579 {
1580     WORD *stack = PTR_SEG_OFF_TO_LIN(SS_reg(context), SP_reg(context));
1581     HANDLE16 handle = (HANDLE16)stack[2];
1582     HTASK16 hTask = 0;
1583     HMODULE16 hModule;
1584
1585     hModule = GetExePtrHelper( handle, &hTask );
1586
1587     AX_reg(context) = CX_reg(context) = hModule;
1588     if (hTask) ES_reg(context) = hTask;
1589 }
1590
1591 /***********************************************************************
1592  *           TaskFirst   (TOOLHELP.63)
1593  */
1594 BOOL16 WINAPI TaskFirst( TASKENTRY *lpte )
1595 {
1596     lpte->hNext = hFirstTask;
1597     return TaskNext( lpte );
1598 }
1599
1600
1601 /***********************************************************************
1602  *           TaskNext   (TOOLHELP.64)
1603  */
1604 BOOL16 WINAPI TaskNext( TASKENTRY *lpte )
1605 {
1606     TDB *pTask;
1607     INSTANCEDATA *pInstData;
1608
1609     TRACE(toolhelp, "(%p): task=%04x\n", lpte, lpte->hNext );
1610     if (!lpte->hNext) return FALSE;
1611     pTask = (TDB *)GlobalLock16( lpte->hNext );
1612     if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1613     pInstData = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( pTask->hInstance, 0 );
1614     lpte->hTask         = lpte->hNext;
1615     lpte->hTaskParent   = pTask->hParent;
1616     lpte->hInst         = pTask->hInstance;
1617     lpte->hModule       = pTask->hModule;
1618     lpte->wSS           = SELECTOROF( pTask->thdb->cur_stack );
1619     lpte->wSP           = OFFSETOF( pTask->thdb->cur_stack );
1620     lpte->wStackTop     = pInstData->stacktop;
1621     lpte->wStackMinimum = pInstData->stackmin;
1622     lpte->wStackBottom  = pInstData->stackbottom;
1623     lpte->wcEvents      = pTask->nEvents;
1624     lpte->hQueue        = pTask->hQueue;
1625     strncpy( lpte->szModule, pTask->module_name, 8 );
1626     lpte->szModule[8]   = '\0';
1627     lpte->wPSPOffset    = 0x100;  /*??*/
1628     lpte->hNext         = pTask->hNext;
1629     return TRUE;
1630 }
1631
1632
1633 /***********************************************************************
1634  *           TaskFindHandle   (TOOLHELP.65)
1635  */
1636 BOOL16 WINAPI TaskFindHandle( TASKENTRY *lpte, HTASK16 hTask )
1637 {
1638     lpte->hNext = hTask;
1639     return TaskNext( lpte );
1640 }
1641
1642
1643 /***********************************************************************
1644  *           GetAppCompatFlags16   (KERNEL.354)
1645  */
1646 DWORD WINAPI GetAppCompatFlags16( HTASK16 hTask )
1647 {
1648     return GetAppCompatFlags32( hTask );
1649 }
1650
1651
1652 /***********************************************************************
1653  *           GetAppCompatFlags32   (USER32.206)
1654  */
1655 DWORD WINAPI GetAppCompatFlags32( HTASK32 hTask )
1656 {
1657     TDB *pTask;
1658
1659     if (!hTask) hTask = GetCurrentTask();
1660     if (!(pTask=(TDB *)GlobalLock16( (HTASK16)hTask ))) return 0;
1661     if (GlobalSize16(hTask) < sizeof(TDB)) return 0;
1662     return pTask->compat_flags;
1663 }