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