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