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