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