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