Added mmsystem.h include.
[wine/testsucceed.git] / loader / task.c
blob520bf48531878102b1fb817455879d8657f1415e
1 /*
2 * Task functions
4 * Copyright 1995 Alexandre Julliard
5 */
7 #include <stdlib.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <unistd.h>
12 #include "wine/winbase16.h"
13 #include "user.h"
14 #include "callback.h"
15 #include "drive.h"
16 #include "file.h"
17 #include "global.h"
18 #include "instance.h"
19 #include "message.h"
20 #include "miscemu.h"
21 #include "module.h"
22 #include "neexe.h"
23 #include "peexe.h"
24 #include "pe_image.h"
25 #include "process.h"
26 #include "queue.h"
27 #include "selectors.h"
28 #include "stackframe.h"
29 #include "task.h"
30 #include "thread.h"
31 #include "toolhelp.h"
32 #include "winnt.h"
33 #include "winsock.h"
34 #include "thread.h"
35 #include "syslevel.h"
36 #include "debugtools.h"
37 #include "dosexe.h"
38 #include "dde_proc.h"
39 #include "services.h"
40 #include "server.h"
42 DECLARE_DEBUG_CHANNEL(relay)
43 DECLARE_DEBUG_CHANNEL(task)
44 DECLARE_DEBUG_CHANNEL(toolhelp)
46 /* Min. number of thunks allocated when creating a new segment */
47 #define MIN_THUNKS 32
49 /* Pointer to function to switch to a larger stack */
50 int (*IF1632_CallLargeStack)( int (*func)(), void *arg ) = NULL;
52 /* Pointer to debugger callback routine */
53 void (*TASK_AddTaskEntryBreakpoint)( HTASK16 hTask ) = NULL;
55 static THHOOK DefaultThhook = { 0 };
56 THHOOK *pThhook = &DefaultThhook;
58 #define hCurrentTask (pThhook->CurTDB)
59 #define hFirstTask (pThhook->HeadTDB)
60 #define hLockedTask (pThhook->LockTDB)
62 static HTASK16 hTaskToKill = 0;
63 static UINT16 nTaskCount = 0;
65 static void TASK_YieldToSystem( void );
67 extern BOOL THREAD_InitDone;
70 /***********************************************************************
71 * TASK_InstallTHHook
73 void TASK_InstallTHHook( THHOOK *pNewThhook )
75 THHOOK *pOldThhook = pThhook;
77 pThhook = pNewThhook? pNewThhook : &DefaultThhook;
79 *pThhook = *pOldThhook;
82 /***********************************************************************
83 * TASK_GetNextTask
85 HTASK16 TASK_GetNextTask( HTASK16 hTask )
87 TDB* pTask = (TDB*)GlobalLock16(hTask);
89 if (pTask->hNext) return pTask->hNext;
90 return (hFirstTask != hTask) ? hFirstTask : 0;
93 /***********************************************************************
94 * TASK_LinkTask
96 static void TASK_LinkTask( HTASK16 hTask )
98 HTASK16 *prevTask;
99 TDB *pTask;
101 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
102 prevTask = &hFirstTask;
103 while (*prevTask)
105 TDB *prevTaskPtr = (TDB *)GlobalLock16( *prevTask );
106 if (prevTaskPtr->priority >= pTask->priority) break;
107 prevTask = &prevTaskPtr->hNext;
109 pTask->hNext = *prevTask;
110 *prevTask = hTask;
111 nTaskCount++;
115 /***********************************************************************
116 * TASK_UnlinkTask
118 static void TASK_UnlinkTask( HTASK16 hTask )
120 HTASK16 *prevTask;
121 TDB *pTask;
123 prevTask = &hFirstTask;
124 while (*prevTask && (*prevTask != hTask))
126 pTask = (TDB *)GlobalLock16( *prevTask );
127 prevTask = &pTask->hNext;
129 if (*prevTask)
131 pTask = (TDB *)GlobalLock16( *prevTask );
132 *prevTask = pTask->hNext;
133 pTask->hNext = 0;
134 nTaskCount--;
139 /***********************************************************************
140 * TASK_CreateThunks
142 * Create a thunk free-list in segment 'handle', starting from offset 'offset'
143 * and containing 'count' entries.
145 static void TASK_CreateThunks( HGLOBAL16 handle, WORD offset, WORD count )
147 int i;
148 WORD free;
149 THUNKS *pThunk;
151 pThunk = (THUNKS *)((BYTE *)GlobalLock16( handle ) + offset);
152 pThunk->next = 0;
153 pThunk->magic = THUNK_MAGIC;
154 pThunk->free = (int)&pThunk->thunks - (int)pThunk;
155 free = pThunk->free;
156 for (i = 0; i < count-1; i++)
158 free += 8; /* Offset of next thunk */
159 pThunk->thunks[4*i] = free;
161 pThunk->thunks[4*i] = 0; /* Last thunk */
165 /***********************************************************************
166 * TASK_AllocThunk
168 * Allocate a thunk for MakeProcInstance().
170 static SEGPTR TASK_AllocThunk( HTASK16 hTask )
172 TDB *pTask;
173 THUNKS *pThunk;
174 WORD sel, base;
176 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
177 sel = pTask->hCSAlias;
178 pThunk = &pTask->thunks;
179 base = (int)pThunk - (int)pTask;
180 while (!pThunk->free)
182 sel = pThunk->next;
183 if (!sel) /* Allocate a new segment */
185 sel = GLOBAL_Alloc( GMEM_FIXED, sizeof(THUNKS) + (MIN_THUNKS-1)*8,
186 pTask->hPDB, TRUE, FALSE, FALSE );
187 if (!sel) return (SEGPTR)0;
188 TASK_CreateThunks( sel, 0, MIN_THUNKS );
189 pThunk->next = sel;
191 pThunk = (THUNKS *)GlobalLock16( sel );
192 base = 0;
194 base += pThunk->free;
195 pThunk->free = *(WORD *)((BYTE *)pThunk + pThunk->free);
196 return PTR_SEG_OFF_TO_SEGPTR( sel, base );
200 /***********************************************************************
201 * TASK_FreeThunk
203 * Free a MakeProcInstance() thunk.
205 static BOOL TASK_FreeThunk( HTASK16 hTask, SEGPTR thunk )
207 TDB *pTask;
208 THUNKS *pThunk;
209 WORD sel, base;
211 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
212 sel = pTask->hCSAlias;
213 pThunk = &pTask->thunks;
214 base = (int)pThunk - (int)pTask;
215 while (sel && (sel != HIWORD(thunk)))
217 sel = pThunk->next;
218 pThunk = (THUNKS *)GlobalLock16( sel );
219 base = 0;
221 if (!sel) return FALSE;
222 *(WORD *)((BYTE *)pThunk + LOWORD(thunk) - base) = pThunk->free;
223 pThunk->free = LOWORD(thunk) - base;
224 return TRUE;
228 /***********************************************************************
229 * TASK_CallToStart
231 * 32-bit entry point for a new task. This function is responsible for
232 * setting up the registers and jumping to the 16-bit entry point.
234 static void TASK_CallToStart(void)
236 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
237 NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
238 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
240 SET_CUR_THREAD( pTask->thdb );
241 CLIENT_InitThread();
243 /* Terminate the stack frame chain */
244 memset(THREAD_STACK16( pTask->thdb ), '\0', sizeof(STACK16FRAME));
246 /* Initialize process critical section */
247 InitializeCriticalSection( &PROCESS_Current()->crit_section );
249 /* Call USER signal proc */
250 PROCESS_CallUserSignalProc( USIG_THREAD_INIT, 0, 0 ); /* for initial thread */
251 PROCESS_CallUserSignalProc( USIG_PROCESS_INIT, 0, 0 );
252 PROCESS_CallUserSignalProc( USIG_PROCESS_LOADED, 0, 0 );
253 PROCESS_CallUserSignalProc( USIG_PROCESS_RUNNING, 0, 0 );
255 if (pModule->flags & NE_FFLAGS_WIN32)
257 ERR_(task)("Called for Win32 task!\n" );
258 ExitProcess( 1 );
260 else if (pModule->dos_image)
262 DOSVM_Enter( NULL );
263 ExitProcess( 0 );
265 else
267 /* Registers at initialization must be:
268 * ax zero
269 * bx stack size in bytes
270 * cx heap size in bytes
271 * si previous app instance
272 * di current app instance
273 * bp zero
274 * es selector to the PSP
275 * ds dgroup of the application
276 * ss stack selector
277 * sp top of the stack
279 CONTEXT context;
281 memset( &context, 0, sizeof(context) );
282 CS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->cs - 1].hSeg);
283 DS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->dgroup - 1].hSeg);
284 ES_reg(&context) = pTask->hPDB;
285 EIP_reg(&context) = pModule->ip;
286 EBX_reg(&context) = pModule->stack_size;
287 ECX_reg(&context) = pModule->heap_size;
288 EDI_reg(&context) = context.SegDs;
290 TRACE_(task)("Starting main program: cs:ip=%04lx:%04x ds=%04lx ss:sp=%04x:%04x\n",
291 CS_reg(&context), IP_reg(&context), DS_reg(&context),
292 SELECTOROF(pTask->thdb->cur_stack),
293 OFFSETOF(pTask->thdb->cur_stack) );
295 Callbacks->CallRegisterShortProc( &context, 0 );
296 /* This should never return */
297 ERR_(task)("Main program returned! (should never happen)\n" );
298 ExitProcess( 1 );
303 /***********************************************************************
304 * TASK_Create
306 * NOTE: This routine might be called by a Win32 thread. We don't have
307 * any real problems with that, since we operated merely on a private
308 * TDB structure that is not yet linked into the task list.
310 BOOL TASK_Create( THDB *thdb, NE_MODULE *pModule, HINSTANCE16 hInstance,
311 HINSTANCE16 hPrevInstance, UINT16 cmdShow)
313 HTASK16 hTask;
314 TDB *pTask;
315 LPSTR cmd_line;
316 WORD sp;
317 char *stack32Top;
318 char name[10];
319 STACK16FRAME *frame16;
320 STACK32FRAME *frame32;
321 PDB *pdb32 = thdb->process;
322 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
324 /* Allocate the task structure */
326 hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
327 pModule->self, FALSE, FALSE, FALSE );
328 if (!hTask) return FALSE;
329 pTask = (TDB *)GlobalLock16( hTask );
331 /* Fill the task structure */
333 pTask->nEvents = 0;
334 pTask->hSelf = hTask;
335 pTask->flags = 0;
337 if (pModule->flags & NE_FFLAGS_WIN32)
338 pTask->flags |= TDBF_WIN32;
339 if (pModule->lpDosTask)
340 pTask->flags |= TDBF_WINOLDAP;
342 pTask->version = pModule->expected_version;
343 pTask->hInstance = hInstance? hInstance : pModule->self;
344 pTask->hPrevInstance = hPrevInstance;
345 pTask->hModule = pModule->self;
346 pTask->hParent = GetCurrentTask();
347 pTask->magic = TDB_MAGIC;
348 pTask->nCmdShow = cmdShow;
349 pTask->thdb = thdb;
350 pTask->curdrive = DRIVE_GetCurrentDrive() | 0x80;
351 strcpy( pTask->curdir, "\\" );
352 lstrcpynA( pTask->curdir + 1, DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() ),
353 sizeof(pTask->curdir) - 1 );
355 /* Create the thunks block */
357 TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
359 /* Copy the module name */
361 GetModuleName16( pModule->self, name, sizeof(name) );
362 strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
364 /* Allocate a selector for the PDB */
366 pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB16),
367 pModule->self, FALSE, FALSE, FALSE, NULL );
369 /* Fill the PDB */
371 pTask->pdb.int20 = 0x20cd;
372 pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */
373 PUT_DWORD(&pTask->pdb.dispatcher[1], (DWORD)NE_GetEntryPoint(
374 GetModuleHandle16("KERNEL"), 102 )); /* KERNEL.102 is DOS3Call() */
375 pTask->pdb.savedint22 = INT_GetPMHandler( 0x22 );
376 pTask->pdb.savedint23 = INT_GetPMHandler( 0x23 );
377 pTask->pdb.savedint24 = INT_GetPMHandler( 0x24 );
378 pTask->pdb.fileHandlesPtr =
379 PTR_SEG_OFF_TO_SEGPTR( GlobalHandleToSel16(pTask->hPDB),
380 (int)&((PDB16 *)0)->fileHandles );
381 pTask->pdb.hFileHandles = 0;
382 memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
383 pTask->pdb.environment = pdb32->env_db->env_sel;
384 pTask->pdb.nbFiles = 20;
386 /* Fill the command line */
388 cmd_line = pdb32->env_db->cmd_line;
389 while (*cmd_line && (*cmd_line != ' ') && (*cmd_line != '\t')) cmd_line++;
390 while ((*cmd_line == ' ') || (*cmd_line == '\t')) cmd_line++;
391 lstrcpynA( pTask->pdb.cmdLine+1, cmd_line, sizeof(pTask->pdb.cmdLine)-1);
392 pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
394 /* Get the compatibility flags */
396 pTask->compat_flags = GetProfileIntA( "Compatibility", name, 0 );
398 /* Allocate a code segment alias for the TDB */
400 pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
401 sizeof(TDB), pTask->hPDB, TRUE,
402 FALSE, FALSE, NULL );
404 /* Set the owner of the environment block */
406 FarSetOwner16( pTask->pdb.environment, pTask->hPDB );
408 /* Default DTA overwrites command-line */
410 pTask->dta = PTR_SEG_OFF_TO_SEGPTR( pTask->hPDB,
411 (int)&pTask->pdb.cmdLine - (int)&pTask->pdb );
413 /* Enter task handle into thread and process */
415 pTask->thdb->teb.htask16 = pTask->thdb->process->task = hTask;
416 TRACE_(task)("module='%s' cmdline='%s' task=%04x\n", name, cmd_line, hTask );
418 if (pTask->flags & TDBF_WIN32) return TRUE;
420 /* If we have a DGROUP/hInstance, use it for 16-bit stack */
422 if ( hInstance )
424 if (!(sp = pModule->sp))
425 sp = pSegTable[pModule->ss-1].minsize + pModule->stack_size;
426 sp &= ~1; sp -= sizeof(STACK16FRAME);
427 pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( hInstance, sp );
430 /* Create the 16-bit stack frame */
432 pTask->thdb->cur_stack -= sizeof(STACK16FRAME);
433 frame16 = (STACK16FRAME *)PTR_SEG_TO_LIN( pTask->thdb->cur_stack );
434 frame16->ebp = OFFSETOF( pTask->thdb->cur_stack ) + (int)&((STACK16FRAME *)0)->bp;
435 frame16->bp = LOWORD(frame16->ebp);
436 frame16->ds = frame16->es = hInstance;
437 frame16->fs = 0;
438 frame16->entry_point = 0;
439 frame16->entry_cs = 0;
440 frame16->mutex_count = 1; /* TASK_Reschedule is called from 16-bit code */
441 /* The remaining fields will be initialized in TASK_Reschedule */
443 /* Create the 32-bit stack frame */
445 stack32Top = (char*)pTask->thdb->teb.stack_top;
446 frame16->frame32 = frame32 = (STACK32FRAME *)stack32Top - 1;
447 frame32->frame16 = pTask->thdb->cur_stack + sizeof(STACK16FRAME);
448 frame32->edi = 0;
449 frame32->esi = 0;
450 frame32->edx = 0;
451 frame32->ecx = 0;
452 frame32->ebx = 0;
453 frame32->retaddr = (DWORD)TASK_CallToStart;
454 /* The remaining fields will be initialized in TASK_Reschedule */
456 return TRUE;
459 /***********************************************************************
460 * TASK_StartTask
462 * NOTE: This routine might be called by a Win32 thread. Thus, we need
463 * to be careful to protect global data structures. We do this
464 * by entering the Win16Lock while linking the task into the
465 * global task list.
467 void TASK_StartTask( HTASK16 hTask )
469 TDB *pTask = (TDB *)GlobalLock16( hTask );
470 if ( !pTask ) return;
472 /* Add the task to the linked list */
474 SYSLEVEL_EnterWin16Lock();
475 TASK_LinkTask( hTask );
476 SYSLEVEL_LeaveWin16Lock();
478 TRACE_(task)("linked task %04x\n", hTask );
480 /* If requested, add entry point breakpoint */
482 if ( TASK_AddTaskEntryBreakpoint )
483 TASK_AddTaskEntryBreakpoint( hTask );
485 /* Get the task up and running. */
487 if ( THREAD_IsWin16( pTask->thdb ) )
489 pTask->nEvents++;
491 /* If we ourselves are a 16-bit task, we simply Yield().
492 If we are 32-bit however, we need to signal the scheduler. */
494 if ( THREAD_IsWin16( THREAD_Current() ) )
495 OldYield16();
496 else
497 EVENT_WakeUp();
502 /***********************************************************************
503 * TASK_DeleteTask
505 static void TASK_DeleteTask( HTASK16 hTask )
507 TDB *pTask;
508 HGLOBAL16 hPDB;
510 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
511 hPDB = pTask->hPDB;
513 pTask->magic = 0xdead; /* invalidate signature */
515 /* Delete the Win32 part of the task */
517 /* PROCESS_FreePDB( pTask->thdb->process ); FIXME */
518 /* K32OBJ_DecCount( &pTask->thdb->header ); FIXME */
520 /* Free the selector aliases */
522 GLOBAL_FreeBlock( pTask->hCSAlias );
523 GLOBAL_FreeBlock( pTask->hPDB );
525 /* Free the task module */
527 FreeModule16( pTask->hModule );
529 /* Free the task structure itself */
531 GlobalFree16( hTask );
533 /* Free all memory used by this task (including the 32-bit stack, */
534 /* the environment block and the thunk segments). */
536 GlobalFreeAll16( hPDB );
539 /***********************************************************************
540 * TASK_KillTask
542 void TASK_KillTask( HTASK16 hTask )
544 TDB *pTask;
546 /* Enter the Win16Lock to protect global data structures */
547 SYSLEVEL_EnterWin16Lock();
549 if ( !hTask ) hTask = GetCurrentTask();
550 pTask = (TDB *)GlobalLock16( hTask );
551 if ( !pTask )
553 SYSLEVEL_LeaveWin16Lock();
554 return;
557 TRACE_(task)("Killing task %04x\n", hTask );
559 /* Delete active sockets */
561 if( pTask->pwsi )
562 WINSOCK_DeleteTaskWSI( pTask, pTask->pwsi );
564 #ifdef MZ_SUPPORTED
566 /* Kill DOS VM task */
567 NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
568 if ( pModule->lpDosTask )
569 MZ_KillModule( pModule->lpDosTask );
571 #endif
573 /* Perform USER cleanup */
575 if (pTask->userhandler)
576 pTask->userhandler( hTask, USIG16_TERMINATION, 0,
577 pTask->hInstance, pTask->hQueue );
579 PROCESS_CallUserSignalProc( USIG_PROCESS_EXIT, 0, 0 );
580 PROCESS_CallUserSignalProc( USIG_THREAD_EXIT, 0, 0 ); /* FIXME */
581 PROCESS_CallUserSignalProc( USIG_PROCESS_DESTROY, 0, 0 );
583 if (nTaskCount <= 1)
585 TRACE_(task)("this is the last task, exiting\n" );
586 ExitKernel16();
589 /* FIXME: Hack! Send a message to the initial task so that
590 * the GetMessage wakes up and the initial task can check whether
591 * it is the only remaining one and terminate itself ...
592 * The initial task should probably install hooks or something
593 * to get informed about task termination :-/
595 Callout.PostAppMessage16( PROCESS_Initial()->task, WM_NULL, 0, 0 );
597 /* Remove the task from the list to be sure we never switch back to it */
598 TASK_UnlinkTask( hTask );
599 if( nTaskCount )
601 TDB* p = (TDB *)GlobalLock16( hFirstTask );
602 while( p )
604 if( p->hYieldTo == hTask ) p->hYieldTo = 0;
605 p = (TDB *)GlobalLock16( p->hNext );
609 pTask->nEvents = 0;
611 if ( hLockedTask == hTask )
612 hLockedTask = 0;
614 if ( hTaskToKill && ( hTaskToKill != hCurrentTask ) )
616 /* If another task is already marked for destruction, */
617 /* we can kill it now, as we are in another context. */
618 TASK_DeleteTask( hTaskToKill );
619 hTaskToKill = 0;
623 * If hTask is not the task currently scheduled by the Win16
624 * scheduler, we simply delete it; otherwise we mark it for
625 * destruction. Note that if the current task is a 32-bit
626 * one, hCurrentTask is *different* from GetCurrentTask()!
628 if ( hTask == hCurrentTask )
630 assert( hTaskToKill == 0 || hTaskToKill == hCurrentTask );
631 hTaskToKill = hCurrentTask;
633 else
634 TASK_DeleteTask( hTask );
636 SYSLEVEL_LeaveWin16Lock();
640 /***********************************************************************
641 * TASK_KillCurrentTask
643 * Kill the currently running task. As it's not possible to kill the
644 * current task like this, it is simply marked for destruction, and will
645 * be killed when either TASK_Reschedule or this function is called again
646 * in the context of another task.
648 void TASK_KillCurrentTask( INT16 exitCode )
650 if ( !THREAD_IsWin16( THREAD_Current() ) )
652 FIXME_(task)("called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
653 return;
656 assert(hCurrentTask == GetCurrentTask());
658 TRACE_(task)("Killing current task %04x\n", hCurrentTask );
660 TASK_KillTask( 0 );
662 TASK_YieldToSystem();
664 /* We should never return from this Yield() */
666 ERR_(task)("Return of the living dead %04x!!!\n", hCurrentTask);
667 exit(1);
670 /***********************************************************************
671 * TASK_Reschedule
673 * This is where all the magic of task-switching happens!
675 * Note: This function should only be called via the TASK_YieldToSystem()
676 * wrapper, to make sure that all the context is saved correctly.
678 * It must not call functions that may yield control.
680 BOOL TASK_Reschedule(void)
682 TDB *pOldTask = NULL, *pNewTask;
683 HTASK16 hTask = 0;
684 STACK16FRAME *newframe16;
685 BOOL pending = FALSE;
687 /* Get the initial task up and running */
688 if (!hCurrentTask && GetCurrentTask())
690 /* We need to remove one pair of stackframes (exept for Winelib) */
691 STACK16FRAME *oldframe16 = CURRENT_STACK16;
692 STACK32FRAME *oldframe32 = oldframe16? oldframe16->frame32 : NULL;
693 STACK16FRAME *newframe16 = oldframe32? PTR_SEG_TO_LIN( oldframe32->frame16 ) : NULL;
694 STACK32FRAME *newframe32 = newframe16? newframe16->frame32 : NULL;
695 if (newframe32)
697 newframe16->entry_ip = oldframe16->entry_ip;
698 newframe16->entry_cs = oldframe16->entry_cs;
699 newframe16->ip = oldframe16->ip;
700 newframe16->cs = oldframe16->cs;
701 newframe32->ebp = oldframe32->ebp;
702 newframe32->restore_addr = oldframe32->restore_addr;
703 newframe32->codeselector = oldframe32->codeselector;
705 THREAD_Current()->cur_stack = oldframe32->frame16;
708 hCurrentTask = GetCurrentTask();
709 pNewTask = (TDB *)GlobalLock16( hCurrentTask );
710 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
711 return FALSE;
714 /* NOTE: As we are entered from 16-bit code, we hold the Win16Lock.
715 We hang onto it thoughout most of this routine, so that accesses
716 to global variables (most notably the task list) are protected. */
717 assert(hCurrentTask == GetCurrentTask());
719 TRACE_(task)("entered with hTask %04x (pid %d)\n", hCurrentTask, getpid());
721 #ifdef CONFIG_IPC
722 /* FIXME: What about the Win16Lock ??? */
723 dde_reschedule();
724 #endif
725 /* First check if there's a task to kill */
727 if (hTaskToKill && (hTaskToKill != hCurrentTask))
729 TASK_DeleteTask( hTaskToKill );
730 hTaskToKill = 0;
733 /* Find a task to yield to */
735 pOldTask = (TDB *)GlobalLock16( hCurrentTask );
736 if (pOldTask && pOldTask->hYieldTo)
738 /* check for DirectedYield() */
740 hTask = pOldTask->hYieldTo;
741 pNewTask = (TDB *)GlobalLock16( hTask );
742 if( !pNewTask || !pNewTask->nEvents) hTask = 0;
743 pOldTask->hYieldTo = 0;
746 /* extract hardware events only! */
748 if (!hTask) pending = EVENT_WaitNetEvent( FALSE, TRUE );
750 while (!hTask)
752 /* Find a task that has an event pending */
754 hTask = hFirstTask;
755 while (hTask)
757 pNewTask = (TDB *)GlobalLock16( hTask );
759 TRACE_(task)("\ttask = %04x, events = %i\n", hTask, pNewTask->nEvents);
761 if (pNewTask->nEvents) break;
762 hTask = pNewTask->hNext;
764 if (hLockedTask && (hTask != hLockedTask)) hTask = 0;
765 if (hTask) break;
767 /* If a non-hardware event is pending, return to TASK_YieldToSystem
768 temporarily to process it safely */
769 if (pending) return TRUE;
771 /* No task found, wait for some events to come in */
773 /* NOTE: We release the Win16Lock while waiting for events. This is to enable
774 Win32 threads to thunk down to 16-bit temporarily. Since Win16
775 tasks won't execute and Win32 threads are not allowed to enter
776 TASK_Reschedule anyway, there should be no re-entrancy problem ... */
778 SYSLEVEL_ReleaseWin16Lock();
779 pending = EVENT_WaitNetEvent( TRUE, TRUE );
780 SYSLEVEL_RestoreWin16Lock();
783 if (hTask == hCurrentTask)
785 /* Allow Win32 threads to thunk down even while a Win16 task is
786 in a tight PeekMessage() or Yield() loop ... */
787 SYSLEVEL_ReleaseWin16Lock();
788 SYSLEVEL_RestoreWin16Lock();
790 TRACE_(task)("returning to the current task(%04x)\n", hTask );
791 return FALSE; /* Nothing to do */
793 pNewTask = (TDB *)GlobalLock16( hTask );
794 TRACE_(task)("Switching to task %04x (%.8s)\n",
795 hTask, pNewTask->module_name );
797 /* Make the task the last in the linked list (round-robin scheduling) */
799 pNewTask->priority++;
800 TASK_UnlinkTask( hTask );
801 TASK_LinkTask( hTask );
802 pNewTask->priority--;
804 /* Finish initializing the new task stack if necessary */
806 newframe16 = THREAD_STACK16( pNewTask->thdb );
807 if (!newframe16->entry_cs)
809 STACK16FRAME *oldframe16 = CURRENT_STACK16;
810 STACK32FRAME *oldframe32 = oldframe16->frame32;
811 STACK32FRAME *newframe32 = newframe16->frame32;
812 newframe16->entry_ip = oldframe16->entry_ip;
813 newframe16->entry_cs = oldframe16->entry_cs;
814 newframe16->ip = oldframe16->ip;
815 newframe16->cs = oldframe16->cs;
816 newframe32->ebp = oldframe32->ebp;
817 newframe32->restore_addr = oldframe32->restore_addr;
818 newframe32->codeselector = oldframe32->codeselector;
821 /* Switch to the new stack */
823 /* NOTE: We need to release/restore the Win16Lock, as the task
824 switched to might be at another recursion level than
825 the old task ... */
827 SYSLEVEL_ReleaseWin16Lock();
829 hCurrentTask = hTask;
830 SET_CUR_THREAD( pNewTask->thdb );
831 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
833 SYSLEVEL_RestoreWin16Lock();
835 return FALSE;
839 /***********************************************************************
840 * TASK_YieldToSystem
842 * Scheduler interface, this way we ensure that all "unsafe" events are
843 * processed outside the scheduler.
845 static void TASK_YieldToSystem( void )
847 if ( !THREAD_IsWin16( THREAD_Current() ) )
849 FIXME_(task)("called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
850 return;
853 if ( Callbacks->CallTaskRescheduleProc() )
855 /* NOTE: We get here only when no task has an event. This means also
856 the current task, so we shouldn't actually return to the
857 caller here. But, we need to do so, as the EVENT_WaitNetEvent
858 call could lead to a complex series of inter-task SendMessage
859 calls which might leave this task in a state where it again
860 has no event, but where its queue's wakeMask is also reset
861 to zero. Reentering TASK_Reschedule in this state would be
862 suicide. Hence, we do return to the caller after processing
863 non-hardware events. Actually, this should not hurt anyone,
864 as the caller must be WaitEvent, and thus the QUEUE_WaitBits
865 loop in USER. Should there actually be no message pending
866 for this task after processing non-hardware events, that loop
867 will simply return to WaitEvent. */
869 EVENT_WaitNetEvent( FALSE, FALSE );
874 /***********************************************************************
875 * InitTask (KERNEL.91)
877 * Called by the application startup code.
879 void WINAPI InitTask16( CONTEXT *context )
881 TDB *pTask;
882 NE_MODULE *pModule;
883 SEGTABLEENTRY *pSegTable;
884 INSTANCEDATA *pinstance;
885 LONG stacklow, stackhi;
887 if (context) EAX_reg(context) = 0;
888 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
889 if (!(pModule = NE_GetPtr( pTask->hModule ))) return;
891 /* Initialize implicitly loaded DLLs */
892 NE_InitializeDLLs( pTask->hModule );
894 if (context)
896 /* Registers on return are:
897 * ax 1 if OK, 0 on error
898 * cx stack limit in bytes
899 * dx cmdShow parameter
900 * si instance handle of the previous instance
901 * di instance handle of the new task
902 * es:bx pointer to command-line inside PSP
904 * 0 (=%bp) is pushed on the stack
906 SEGPTR ptr = STACK16_PUSH( pTask->thdb, sizeof(WORD) );
907 *(WORD *)PTR_SEG_TO_LIN(ptr) = 0;
908 SP_reg(context) -= 2;
910 EAX_reg(context) = 1;
912 if (!pTask->pdb.cmdLine[0]) EBX_reg(context) = 0x80;
913 else
915 LPBYTE p = &pTask->pdb.cmdLine[1];
916 while ((*p == ' ') || (*p == '\t')) p++;
917 EBX_reg(context) = 0x80 + (p - pTask->pdb.cmdLine);
919 ECX_reg(context) = pModule->stack_size;
920 EDX_reg(context) = pTask->nCmdShow;
921 ESI_reg(context) = (DWORD)pTask->hPrevInstance;
922 EDI_reg(context) = (DWORD)pTask->hInstance;
923 ES_reg (context) = (WORD)pTask->hPDB;
926 /* Initialize the local heap */
927 if ( pModule->heap_size )
929 LocalInit16( pTask->hInstance, 0, pModule->heap_size );
932 /* Initialize the INSTANCEDATA structure */
933 pSegTable = NE_SEG_TABLE( pModule );
934 stacklow = pSegTable[pModule->ss - 1].minsize;
935 stackhi = stacklow + pModule->stack_size;
936 if (stackhi > 0xffff) stackhi = 0xffff;
937 pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
938 pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
939 pinstance->stacktop = stacklow;
940 pinstance->stackmin = OFFSETOF( pTask->thdb->cur_stack );
944 /***********************************************************************
945 * WaitEvent (KERNEL.30)
947 BOOL16 WINAPI WaitEvent16( HTASK16 hTask )
949 TDB *pTask;
951 if (!hTask) hTask = GetCurrentTask();
952 pTask = (TDB *)GlobalLock16( hTask );
954 if ( !THREAD_IsWin16( THREAD_Current() ) )
956 FIXME_(task)("called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
957 return TRUE;
960 if (pTask->nEvents > 0)
962 pTask->nEvents--;
963 return FALSE;
965 TASK_YieldToSystem();
967 /* When we get back here, we have an event */
969 if (pTask->nEvents > 0) pTask->nEvents--;
970 return TRUE;
974 /***********************************************************************
975 * PostEvent (KERNEL.31)
977 void WINAPI PostEvent16( HTASK16 hTask )
979 TDB *pTask;
981 if (!hTask) hTask = GetCurrentTask();
982 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
984 if ( !THREAD_IsWin16( pTask->thdb ) )
986 FIXME_(task)("called for Win32 thread (%04x)!\n", pTask->thdb->teb_sel );
987 return;
990 pTask->nEvents++;
992 if ( !THREAD_IsWin16( THREAD_Current() ) )
994 /* wake-up the scheduler waiting in EVENT_WaitNetEvent */
995 EVENT_WakeUp();
1000 /***********************************************************************
1001 * SetPriority (KERNEL.32)
1003 void WINAPI SetPriority16( HTASK16 hTask, INT16 delta )
1005 TDB *pTask;
1006 INT16 newpriority;
1008 if (!hTask) hTask = GetCurrentTask();
1009 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
1010 newpriority = pTask->priority + delta;
1011 if (newpriority < -32) newpriority = -32;
1012 else if (newpriority > 15) newpriority = 15;
1014 pTask->priority = newpriority + 1;
1015 TASK_UnlinkTask( hTask );
1016 TASK_LinkTask( hTask );
1017 pTask->priority--;
1021 /***********************************************************************
1022 * LockCurrentTask (KERNEL.33)
1024 HTASK16 WINAPI LockCurrentTask16( BOOL16 bLock )
1026 if (bLock) hLockedTask = GetCurrentTask();
1027 else hLockedTask = 0;
1028 return hLockedTask;
1032 /***********************************************************************
1033 * IsTaskLocked (KERNEL.122)
1035 HTASK16 WINAPI IsTaskLocked16(void)
1037 return hLockedTask;
1041 /***********************************************************************
1042 * OldYield (KERNEL.117)
1044 void WINAPI OldYield16(void)
1046 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1048 if ( !THREAD_IsWin16( THREAD_Current() ) )
1050 FIXME_(task)("called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1051 return;
1054 if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */
1055 TASK_YieldToSystem();
1056 if (pCurTask) pCurTask->nEvents--;
1060 /***********************************************************************
1061 * DirectedYield (KERNEL.150)
1063 void WINAPI DirectedYield16( HTASK16 hTask )
1065 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1067 if ( !THREAD_IsWin16( THREAD_Current() ) )
1069 FIXME_(task)("called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1070 return;
1073 TRACE_(task)("%04x: DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1075 pCurTask->hYieldTo = hTask;
1076 OldYield16();
1078 TRACE_(task)("%04x: back from DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1081 /***********************************************************************
1082 * Yield16 (KERNEL.29)
1084 void WINAPI Yield16(void)
1086 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1088 if (pCurTask) pCurTask->hYieldTo = 0;
1089 if (pCurTask && pCurTask->hQueue) Callout.UserYield16();
1090 else OldYield16();
1093 /***********************************************************************
1094 * KERNEL_490 (KERNEL.490)
1096 HTASK16 WINAPI KERNEL_490( HTASK16 someTask )
1098 if ( !someTask ) return 0;
1100 FIXME_(task)("(%04x): stub\n", someTask );
1101 return 0;
1104 /***********************************************************************
1105 * MakeProcInstance16 (KERNEL.51)
1107 FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
1109 BYTE *thunk,*lfunc;
1110 SEGPTR thunkaddr;
1112 if (!func) {
1113 ERR_(task)("Ouch ! MakeProcInstance called with func == NULL !\n");
1114 return (FARPROC16)0; /* Windows seems to do the same */
1116 if (!hInstance) hInstance = CURRENT_DS;
1117 thunkaddr = TASK_AllocThunk( GetCurrentTask() );
1118 if (!thunkaddr) return (FARPROC16)0;
1119 thunk = PTR_SEG_TO_LIN( thunkaddr );
1120 lfunc = PTR_SEG_TO_LIN( func );
1122 TRACE_(task)("(%08lx,%04x): got thunk %08lx\n",
1123 (DWORD)func, hInstance, (DWORD)thunkaddr );
1124 if (((lfunc[0]==0x8c) && (lfunc[1]==0xd8)) ||
1125 ((lfunc[0]==0x1e) && (lfunc[1]==0x58))
1127 FIXME_(task)("thunk would be useless for %p, overwriting with nop;nop;\n", func );
1128 lfunc[0]=0x90; /* nop */
1129 lfunc[1]=0x90; /* nop */
1132 *thunk++ = 0xb8; /* movw instance, %ax */
1133 *thunk++ = (BYTE)(hInstance & 0xff);
1134 *thunk++ = (BYTE)(hInstance >> 8);
1135 *thunk++ = 0xea; /* ljmp func */
1136 *(DWORD *)thunk = (DWORD)func;
1137 return (FARPROC16)thunkaddr;
1141 /***********************************************************************
1142 * FreeProcInstance16 (KERNEL.52)
1144 void WINAPI FreeProcInstance16( FARPROC16 func )
1146 TRACE_(task)("(%08lx)\n", (DWORD)func );
1147 TASK_FreeThunk( GetCurrentTask(), (SEGPTR)func );
1151 /**********************************************************************
1152 * GetCodeHandle (KERNEL.93)
1154 HANDLE16 WINAPI GetCodeHandle16( FARPROC16 proc )
1156 HANDLE16 handle;
1157 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1159 /* Return the code segment containing 'proc'. */
1160 /* Not sure if this is really correct (shouldn't matter that much). */
1162 /* Check if it is really a thunk */
1163 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1164 handle = GlobalHandle16( thunk[6] + (thunk[7] << 8) );
1165 else
1166 handle = GlobalHandle16( HIWORD(proc) );
1168 return handle;
1171 /**********************************************************************
1172 * GetCodeInfo (KERNEL.104)
1174 VOID WINAPI GetCodeInfo16( FARPROC16 proc, SEGINFO *segInfo )
1176 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1177 NE_MODULE *pModule = NULL;
1178 SEGTABLEENTRY *pSeg = NULL;
1179 WORD segNr;
1181 /* proc is either a thunk, or else a pair of module handle
1182 and segment number. In the first case, we also need to
1183 extract module and segment number. */
1185 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1187 WORD selector = thunk[6] + (thunk[7] << 8);
1188 pModule = NE_GetPtr( GlobalHandle16( selector ) );
1189 pSeg = pModule? NE_SEG_TABLE( pModule ) : NULL;
1191 if ( pModule )
1192 for ( segNr = 0; segNr < pModule->seg_count; segNr++, pSeg++ )
1193 if ( GlobalHandleToSel16(pSeg->hSeg) == selector )
1194 break;
1196 if ( pModule && segNr >= pModule->seg_count )
1197 pSeg = NULL;
1199 else
1201 pModule = NE_GetPtr( HIWORD( proc ) );
1202 segNr = LOWORD( proc );
1204 if ( pModule && segNr < pModule->seg_count )
1205 pSeg = NE_SEG_TABLE( pModule ) + segNr;
1208 /* fill in segment information */
1210 segInfo->offSegment = pSeg? pSeg->filepos : 0;
1211 segInfo->cbSegment = pSeg? pSeg->size : 0;
1212 segInfo->flags = pSeg? pSeg->flags : 0;
1213 segInfo->cbAlloc = pSeg? pSeg->minsize : 0;
1214 segInfo->h = pSeg? pSeg->hSeg : 0;
1215 segInfo->alignShift = pModule? pModule->alignment : 0;
1219 /**********************************************************************
1220 * DefineHandleTable16 (KERNEL.94)
1222 BOOL16 WINAPI DefineHandleTable16( WORD wOffset )
1224 return TRUE; /* FIXME */
1228 /***********************************************************************
1229 * SetTaskQueue (KERNEL.34)
1231 HQUEUE16 WINAPI SetTaskQueue16( HTASK16 hTask, HQUEUE16 hQueue )
1233 HQUEUE16 hPrev;
1234 TDB *pTask;
1236 if (!hTask) hTask = GetCurrentTask();
1237 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1239 hPrev = pTask->hQueue;
1240 pTask->hQueue = hQueue;
1242 return hPrev;
1246 /***********************************************************************
1247 * GetTaskQueue (KERNEL.35)
1249 HQUEUE16 WINAPI GetTaskQueue16( HTASK16 hTask )
1251 TDB *pTask;
1253 if (!hTask) hTask = GetCurrentTask();
1254 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1255 return pTask->hQueue;
1258 /***********************************************************************
1259 * SetThreadQueue (KERNEL.463)
1261 HQUEUE16 WINAPI SetThreadQueue16( DWORD thread, HQUEUE16 hQueue )
1263 THDB *thdb = thread? THREAD_IdToTHDB( thread ) : THREAD_Current();
1264 HQUEUE16 oldQueue = thdb? thdb->teb.queue : 0;
1266 if ( thdb )
1268 thdb->teb.queue = hQueue;
1270 if ( GetTaskQueue16( thdb->process->task ) == oldQueue )
1271 SetTaskQueue16( thdb->process->task, hQueue );
1274 return oldQueue;
1277 /***********************************************************************
1278 * GetThreadQueue (KERNEL.464)
1280 HQUEUE16 WINAPI GetThreadQueue16( DWORD thread )
1282 THDB *thdb = NULL;
1283 if ( !thread )
1284 thdb = THREAD_Current();
1285 else if ( HIWORD(thread) )
1286 thdb = THREAD_IdToTHDB( thread );
1287 else if ( IsTask16( (HTASK16)thread ) )
1288 thdb = ((TDB *)GlobalLock16( (HANDLE16)thread ))->thdb;
1290 return (HQUEUE16)(thdb? thdb->teb.queue : 0);
1293 /***********************************************************************
1294 * SetFastQueue (KERNEL.624)
1296 VOID WINAPI SetFastQueue16( DWORD thread, HANDLE hQueue )
1298 THDB *thdb = NULL;
1299 if ( !thread )
1300 thdb = THREAD_Current();
1301 else if ( HIWORD(thread) )
1302 thdb = THREAD_IdToTHDB( thread );
1303 else if ( IsTask16( (HTASK16)thread ) )
1304 thdb = ((TDB *)GlobalLock16( (HANDLE16)thread ))->thdb;
1306 if ( thdb ) thdb->teb.queue = (HQUEUE16) hQueue;
1309 /***********************************************************************
1310 * GetFastQueue (KERNEL.625)
1312 HANDLE WINAPI GetFastQueue16( void )
1314 THDB *thdb = THREAD_Current();
1315 if (!thdb) return 0;
1317 if (!thdb->teb.queue)
1318 Callout.InitThreadInput16( 0, THREAD_IsWin16(thdb)? 4 : 5 );
1320 if (!thdb->teb.queue)
1321 FIXME_(task)("(): should initialize thread-local queue, expect failure!\n" );
1323 return (HANDLE)thdb->teb.queue;
1326 /***********************************************************************
1327 * SwitchStackTo (KERNEL.108)
1329 void WINAPI SwitchStackTo16( WORD seg, WORD ptr, WORD top )
1331 TDB *pTask;
1332 STACK16FRAME *oldFrame, *newFrame;
1333 INSTANCEDATA *pData;
1334 UINT16 copySize;
1336 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1337 if (!(pData = (INSTANCEDATA *)GlobalLock16( seg ))) return;
1338 TRACE_(task)("old=%04x:%04x new=%04x:%04x\n",
1339 SELECTOROF( pTask->thdb->cur_stack ),
1340 OFFSETOF( pTask->thdb->cur_stack ), seg, ptr );
1342 /* Save the old stack */
1344 oldFrame = THREAD_STACK16( pTask->thdb );
1345 /* pop frame + args and push bp */
1346 pData->old_ss_sp = pTask->thdb->cur_stack + sizeof(STACK16FRAME)
1347 + 2 * sizeof(WORD);
1348 *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp) = oldFrame->bp;
1349 pData->stacktop = top;
1350 pData->stackmin = ptr;
1351 pData->stackbottom = ptr;
1353 /* Switch to the new stack */
1355 /* Note: we need to take the 3 arguments into account; otherwise,
1356 * the stack will underflow upon return from this function.
1358 copySize = oldFrame->bp - OFFSETOF(pData->old_ss_sp);
1359 copySize += 3 * sizeof(WORD) + sizeof(STACK16FRAME);
1360 pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( seg, ptr - copySize );
1361 newFrame = THREAD_STACK16( pTask->thdb );
1363 /* Copy the stack frame and the local variables to the new stack */
1365 memmove( newFrame, oldFrame, copySize );
1366 newFrame->bp = ptr;
1367 *(WORD *)PTR_SEG_OFF_TO_LIN( seg, ptr ) = 0; /* clear previous bp */
1371 /***********************************************************************
1372 * SwitchStackBack (KERNEL.109)
1374 void WINAPI SwitchStackBack16( CONTEXT *context )
1376 TDB *pTask;
1377 STACK16FRAME *oldFrame, *newFrame;
1378 INSTANCEDATA *pData;
1380 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1381 if (!(pData = (INSTANCEDATA *)GlobalLock16(SELECTOROF(pTask->thdb->cur_stack))))
1382 return;
1383 if (!pData->old_ss_sp)
1385 WARN_(task)("No previous SwitchStackTo\n" );
1386 return;
1388 TRACE_(task)("restoring stack %04x:%04x\n",
1389 SELECTOROF(pData->old_ss_sp), OFFSETOF(pData->old_ss_sp) );
1391 oldFrame = THREAD_STACK16( pTask->thdb );
1393 /* Pop bp from the previous stack */
1395 BP_reg(context) = *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp);
1396 pData->old_ss_sp += sizeof(WORD);
1398 /* Switch back to the old stack */
1400 pTask->thdb->cur_stack = pData->old_ss_sp - sizeof(STACK16FRAME);
1401 SS_reg(context) = SELECTOROF(pData->old_ss_sp);
1402 ESP_reg(context) = OFFSETOF(pData->old_ss_sp) - sizeof(DWORD); /*ret addr*/
1403 pData->old_ss_sp = 0;
1405 /* Build a stack frame for the return */
1407 newFrame = THREAD_STACK16( pTask->thdb );
1408 newFrame->frame32 = oldFrame->frame32;
1409 if (TRACE_ON(relay))
1411 newFrame->entry_ip = oldFrame->entry_ip;
1412 newFrame->entry_cs = oldFrame->entry_cs;
1417 /***********************************************************************
1418 * GetTaskQueueDS (KERNEL.118)
1420 void WINAPI GetTaskQueueDS16( CONTEXT *context )
1422 DS_reg(context) = GlobalHandleToSel16( GetTaskQueue16(0) );
1426 /***********************************************************************
1427 * GetTaskQueueES (KERNEL.119)
1429 void WINAPI GetTaskQueueES16( CONTEXT *context )
1431 ES_reg(context) = GlobalHandleToSel16( GetTaskQueue16(0) );
1435 /***********************************************************************
1436 * GetCurrentTask (KERNEL.36)
1438 HTASK16 WINAPI GetCurrentTask(void)
1440 return THREAD_InitDone? PROCESS_Current()->task : 0;
1443 DWORD WINAPI WIN16_GetCurrentTask(void)
1445 /* This is the version used by relay code; the first task is */
1446 /* returned in the high word of the result */
1447 return MAKELONG( GetCurrentTask(), hFirstTask );
1451 /***********************************************************************
1452 * GetCurrentPDB (KERNEL.37)
1454 * UNDOC: returns PSP of KERNEL in high word
1456 DWORD WINAPI GetCurrentPDB16(void)
1458 TDB *pTask;
1460 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1461 return MAKELONG(pTask->hPDB, 0); /* FIXME */
1465 /***********************************************************************
1466 * GetInstanceData (KERNEL.54)
1468 INT16 WINAPI GetInstanceData16( HINSTANCE16 instance, WORD buffer, INT16 len )
1470 char *ptr = (char *)GlobalLock16( instance );
1471 if (!ptr || !len) return 0;
1472 if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
1473 memcpy( (char *)GlobalLock16(CURRENT_DS) + buffer, ptr + buffer, len );
1474 return len;
1478 /***********************************************************************
1479 * GetExeVersion (KERNEL.105)
1481 WORD WINAPI GetExeVersion16(void)
1483 TDB *pTask;
1485 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1486 return pTask->version;
1490 /***********************************************************************
1491 * SetErrorMode16 (KERNEL.107)
1493 UINT16 WINAPI SetErrorMode16( UINT16 mode )
1495 TDB *pTask;
1496 UINT16 oldMode;
1498 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1499 oldMode = pTask->error_mode;
1500 pTask->error_mode = mode;
1501 pTask->thdb->process->error_mode = mode;
1502 return oldMode;
1506 /***********************************************************************
1507 * SetErrorMode32 (KERNEL32.486)
1509 UINT WINAPI SetErrorMode( UINT mode )
1511 return SetErrorMode16( (UINT16)mode );
1515 /***********************************************************************
1516 * GetDOSEnvironment (KERNEL.131)
1518 SEGPTR WINAPI GetDOSEnvironment16(void)
1520 TDB *pTask;
1522 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1523 return PTR_SEG_OFF_TO_SEGPTR( pTask->pdb.environment, 0 );
1527 /***********************************************************************
1528 * GetNumTasks (KERNEL.152)
1530 UINT16 WINAPI GetNumTasks16(void)
1532 return nTaskCount;
1536 /***********************************************************************
1537 * GetTaskDS (KERNEL.155)
1539 * Note: this function apparently returns a DWORD with LOWORD == HIWORD.
1540 * I don't think we need to bother with this.
1542 HINSTANCE16 WINAPI GetTaskDS16(void)
1544 TDB *pTask;
1546 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1547 return pTask->hInstance;
1550 /***********************************************************************
1551 * GetDummyModuleHandleDS (KERNEL.602)
1553 VOID WINAPI GetDummyModuleHandleDS16( CONTEXT *context )
1555 TDB *pTask;
1556 WORD selector;
1558 AX_reg( context ) = 0;
1559 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1560 if (!(pTask->flags & TDBF_WIN32)) return;
1562 selector = GlobalHandleToSel16( pTask->hModule );
1563 DS_reg( context ) = selector;
1564 AX_reg( context ) = selector;
1567 /***********************************************************************
1568 * IsTask (KERNEL.320)
1570 BOOL16 WINAPI IsTask16( HTASK16 hTask )
1572 TDB *pTask;
1574 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return FALSE;
1575 if (GlobalSize16( hTask ) < sizeof(TDB)) return FALSE;
1576 return (pTask->magic == TDB_MAGIC);
1580 /***********************************************************************
1581 * SetTaskSignalProc (KERNEL.38)
1583 * Real 16-bit interface is provided by the THUNK_SetTaskSignalProc.
1585 FARPROC16 WINAPI SetTaskSignalProc( HTASK16 hTask, FARPROC16 proc )
1587 TDB *pTask;
1588 FARPROC16 oldProc;
1590 if (!hTask) hTask = GetCurrentTask();
1591 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return NULL;
1592 oldProc = (FARPROC16)pTask->userhandler;
1593 pTask->userhandler = (USERSIGNALPROC)proc;
1594 return oldProc;
1598 /***********************************************************************
1599 * SetSigHandler (KERNEL.140)
1601 WORD WINAPI SetSigHandler16( FARPROC16 newhandler, FARPROC16* oldhandler,
1602 UINT16 *oldmode, UINT16 newmode, UINT16 flag )
1604 FIXME_(task)("(%p,%p,%p,%d,%d), unimplemented.\n",
1605 newhandler,oldhandler,oldmode,newmode,flag );
1607 if (flag != 1) return 0;
1608 if (!newmode) newhandler = NULL; /* Default handler */
1609 if (newmode != 4)
1611 TDB *pTask;
1613 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1614 if (oldmode) *oldmode = pTask->signal_flags;
1615 pTask->signal_flags = newmode;
1616 if (oldhandler) *oldhandler = pTask->sighandler;
1617 pTask->sighandler = newhandler;
1619 return 0;
1623 /***********************************************************************
1624 * GlobalNotify (KERNEL.154)
1626 * Note that GlobalNotify does _not_ return the old NotifyProc
1627 * -- contrary to LocalNotify !!
1629 VOID WINAPI GlobalNotify16( FARPROC16 proc )
1631 TDB *pTask;
1633 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1634 pTask->discardhandler = proc;
1638 /***********************************************************************
1639 * GetExePtr (KERNEL.133)
1641 static HMODULE16 GetExePtrHelper( HANDLE16 handle, HTASK16 *hTask )
1643 char *ptr;
1644 HANDLE16 owner;
1646 /* Check for module handle */
1648 if (!(ptr = GlobalLock16( handle ))) return 0;
1649 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return handle;
1651 /* Search for this handle inside all tasks */
1653 *hTask = hFirstTask;
1654 while (*hTask)
1656 TDB *pTask = (TDB *)GlobalLock16( *hTask );
1657 if ((*hTask == handle) ||
1658 (pTask->hInstance == handle) ||
1659 (pTask->hQueue == handle) ||
1660 (pTask->hPDB == handle)) return pTask->hModule;
1661 *hTask = pTask->hNext;
1664 /* Check the owner for module handle */
1666 owner = FarGetOwner16( handle );
1667 if (!(ptr = GlobalLock16( owner ))) return 0;
1668 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return owner;
1670 /* Search for the owner inside all tasks */
1672 *hTask = hFirstTask;
1673 while (*hTask)
1675 TDB *pTask = (TDB *)GlobalLock16( *hTask );
1676 if ((*hTask == owner) ||
1677 (pTask->hInstance == owner) ||
1678 (pTask->hQueue == owner) ||
1679 (pTask->hPDB == owner)) return pTask->hModule;
1680 *hTask = pTask->hNext;
1683 return 0;
1686 HMODULE16 WINAPI GetExePtr( HANDLE16 handle )
1688 HTASK16 dummy;
1689 return GetExePtrHelper( handle, &dummy );
1692 void WINAPI WIN16_GetExePtr( CONTEXT *context )
1694 WORD *stack = PTR_SEG_OFF_TO_LIN(SS_reg(context), SP_reg(context));
1695 HANDLE16 handle = (HANDLE16)stack[2];
1696 HTASK16 hTask = 0;
1697 HMODULE16 hModule;
1699 hModule = GetExePtrHelper( handle, &hTask );
1701 AX_reg(context) = CX_reg(context) = hModule;
1702 if (hTask) ES_reg(context) = hTask;
1705 /***********************************************************************
1706 * TaskFirst (TOOLHELP.63)
1708 BOOL16 WINAPI TaskFirst16( TASKENTRY *lpte )
1710 lpte->hNext = hFirstTask;
1711 return TaskNext16( lpte );
1715 /***********************************************************************
1716 * TaskNext (TOOLHELP.64)
1718 BOOL16 WINAPI TaskNext16( TASKENTRY *lpte )
1720 TDB *pTask;
1721 INSTANCEDATA *pInstData;
1723 TRACE_(toolhelp)("(%p): task=%04x\n", lpte, lpte->hNext );
1724 if (!lpte->hNext) return FALSE;
1725 pTask = (TDB *)GlobalLock16( lpte->hNext );
1726 if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1727 pInstData = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( pTask->hInstance, 0 );
1728 lpte->hTask = lpte->hNext;
1729 lpte->hTaskParent = pTask->hParent;
1730 lpte->hInst = pTask->hInstance;
1731 lpte->hModule = pTask->hModule;
1732 lpte->wSS = SELECTOROF( pTask->thdb->cur_stack );
1733 lpte->wSP = OFFSETOF( pTask->thdb->cur_stack );
1734 lpte->wStackTop = pInstData->stacktop;
1735 lpte->wStackMinimum = pInstData->stackmin;
1736 lpte->wStackBottom = pInstData->stackbottom;
1737 lpte->wcEvents = pTask->nEvents;
1738 lpte->hQueue = pTask->hQueue;
1739 strncpy( lpte->szModule, pTask->module_name, 8 );
1740 lpte->szModule[8] = '\0';
1741 lpte->wPSPOffset = 0x100; /*??*/
1742 lpte->hNext = pTask->hNext;
1743 return TRUE;
1747 /***********************************************************************
1748 * TaskFindHandle (TOOLHELP.65)
1750 BOOL16 WINAPI TaskFindHandle16( TASKENTRY *lpte, HTASK16 hTask )
1752 lpte->hNext = hTask;
1753 return TaskNext16( lpte );
1757 /***********************************************************************
1758 * GetAppCompatFlags16 (KERNEL.354)
1760 DWORD WINAPI GetAppCompatFlags16( HTASK16 hTask )
1762 return GetAppCompatFlags( hTask );
1766 /***********************************************************************
1767 * GetAppCompatFlags32 (USER32.206)
1769 DWORD WINAPI GetAppCompatFlags( HTASK hTask )
1771 TDB *pTask;
1773 if (!hTask) hTask = GetCurrentTask();
1774 if (!(pTask=(TDB *)GlobalLock16( (HTASK16)hTask ))) return 0;
1775 if (GlobalSize16(hTask) < sizeof(TDB)) return 0;
1776 return pTask->compat_flags;