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