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