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