Split off wingdi16.h from wingdi.h. Add many more #defines/typedefs
[wine.git] / loader / task.c
blob05052e23f4e8e233502a66eda2b371d2e5c87b1c
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 SERVICE_Exit();
587 USER_ExitWindows();
590 /* FIXME: Hack! Send a message to the initial task so that
591 * the GetMessage wakes up and the initial task can check whether
592 * it is the only remaining one and terminate itself ...
593 * The initial task should probably install hooks or something
594 * to get informed about task termination :-/
596 Callout.PostAppMessage16( PROCESS_Initial()->task, WM_NULL, 0, 0 );
598 /* Remove the task from the list to be sure we never switch back to it */
599 TASK_UnlinkTask( hTask );
600 if( nTaskCount )
602 TDB* p = (TDB *)GlobalLock16( hFirstTask );
603 while( p )
605 if( p->hYieldTo == hTask ) p->hYieldTo = 0;
606 p = (TDB *)GlobalLock16( p->hNext );
610 pTask->nEvents = 0;
612 if ( hLockedTask == hTask )
613 hLockedTask = 0;
615 if ( hTaskToKill && ( hTaskToKill != hCurrentTask ) )
617 /* If another task is already marked for destruction, */
618 /* we can kill it now, as we are in another context. */
619 TASK_DeleteTask( hTaskToKill );
620 hTaskToKill = 0;
624 * If hTask is not the task currently scheduled by the Win16
625 * scheduler, we simply delete it; otherwise we mark it for
626 * destruction. Note that if the current task is a 32-bit
627 * one, hCurrentTask is *different* from GetCurrentTask()!
629 if ( hTask == hCurrentTask )
631 assert( hTaskToKill == 0 || hTaskToKill == hCurrentTask );
632 hTaskToKill = hCurrentTask;
634 else
635 TASK_DeleteTask( hTask );
637 SYSLEVEL_LeaveWin16Lock();
641 /***********************************************************************
642 * TASK_KillCurrentTask
644 * Kill the currently running task. As it's not possible to kill the
645 * current task like this, it is simply marked for destruction, and will
646 * be killed when either TASK_Reschedule or this function is called again
647 * in the context of another task.
649 void TASK_KillCurrentTask( INT16 exitCode )
651 if ( !THREAD_IsWin16( THREAD_Current() ) )
653 FIXME_(task)("called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
654 return;
657 assert(hCurrentTask == GetCurrentTask());
659 TRACE_(task)("Killing current task %04x\n", hCurrentTask );
661 TASK_KillTask( 0 );
663 TASK_YieldToSystem();
665 /* We should never return from this Yield() */
667 ERR_(task)("Return of the living dead %04x!!!\n", hCurrentTask);
668 exit(1);
671 /***********************************************************************
672 * TASK_Reschedule
674 * This is where all the magic of task-switching happens!
676 * Note: This function should only be called via the TASK_YieldToSystem()
677 * wrapper, to make sure that all the context is saved correctly.
679 * It must not call functions that may yield control.
681 BOOL TASK_Reschedule(void)
683 TDB *pOldTask = NULL, *pNewTask;
684 HTASK16 hTask = 0;
685 STACK16FRAME *newframe16;
686 BOOL pending = FALSE;
688 /* Get the initial task up and running */
689 if (!hCurrentTask && GetCurrentTask())
691 /* We need to remove one pair of stackframes (exept for Winelib) */
692 STACK16FRAME *oldframe16 = CURRENT_STACK16;
693 STACK32FRAME *oldframe32 = oldframe16? oldframe16->frame32 : NULL;
694 STACK16FRAME *newframe16 = oldframe32? PTR_SEG_TO_LIN( oldframe32->frame16 ) : NULL;
695 STACK32FRAME *newframe32 = newframe16? newframe16->frame32 : NULL;
696 if (newframe32)
698 newframe16->entry_ip = oldframe16->entry_ip;
699 newframe16->entry_cs = oldframe16->entry_cs;
700 newframe16->ip = oldframe16->ip;
701 newframe16->cs = oldframe16->cs;
702 newframe32->ebp = oldframe32->ebp;
703 newframe32->restore_addr = oldframe32->restore_addr;
704 newframe32->codeselector = oldframe32->codeselector;
706 THREAD_Current()->cur_stack = oldframe32->frame16;
709 hCurrentTask = GetCurrentTask();
710 pNewTask = (TDB *)GlobalLock16( hCurrentTask );
711 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
712 return FALSE;
715 /* NOTE: As we are entered from 16-bit code, we hold the Win16Lock.
716 We hang onto it thoughout most of this routine, so that accesses
717 to global variables (most notably the task list) are protected. */
718 assert(hCurrentTask == GetCurrentTask());
720 TRACE_(task)("entered with hTask %04x (pid %d)\n", hCurrentTask, getpid());
722 #ifdef CONFIG_IPC
723 /* FIXME: What about the Win16Lock ??? */
724 dde_reschedule();
725 #endif
726 /* First check if there's a task to kill */
728 if (hTaskToKill && (hTaskToKill != hCurrentTask))
730 TASK_DeleteTask( hTaskToKill );
731 hTaskToKill = 0;
734 /* Find a task to yield to */
736 pOldTask = (TDB *)GlobalLock16( hCurrentTask );
737 if (pOldTask && pOldTask->hYieldTo)
739 /* check for DirectedYield() */
741 hTask = pOldTask->hYieldTo;
742 pNewTask = (TDB *)GlobalLock16( hTask );
743 if( !pNewTask || !pNewTask->nEvents) hTask = 0;
744 pOldTask->hYieldTo = 0;
747 /* extract hardware events only! */
749 if (!hTask) pending = EVENT_WaitNetEvent( FALSE, TRUE );
751 while (!hTask)
753 /* Find a task that has an event pending */
755 hTask = hFirstTask;
756 while (hTask)
758 pNewTask = (TDB *)GlobalLock16( hTask );
760 TRACE_(task)("\ttask = %04x, events = %i\n", hTask, pNewTask->nEvents);
762 if (pNewTask->nEvents) break;
763 hTask = pNewTask->hNext;
765 if (hLockedTask && (hTask != hLockedTask)) hTask = 0;
766 if (hTask) break;
768 /* If a non-hardware event is pending, return to TASK_YieldToSystem
769 temporarily to process it safely */
770 if (pending) return TRUE;
772 /* No task found, wait for some events to come in */
774 /* NOTE: We release the Win16Lock while waiting for events. This is to enable
775 Win32 threads to thunk down to 16-bit temporarily. Since Win16
776 tasks won't execute and Win32 threads are not allowed to enter
777 TASK_Reschedule anyway, there should be no re-entrancy problem ... */
779 SYSLEVEL_ReleaseWin16Lock();
780 pending = EVENT_WaitNetEvent( TRUE, TRUE );
781 SYSLEVEL_RestoreWin16Lock();
784 if (hTask == hCurrentTask)
786 /* Allow Win32 threads to thunk down even while a Win16 task is
787 in a tight PeekMessage() or Yield() loop ... */
788 SYSLEVEL_ReleaseWin16Lock();
789 SYSLEVEL_RestoreWin16Lock();
791 TRACE_(task)("returning to the current task(%04x)\n", hTask );
792 return FALSE; /* Nothing to do */
794 pNewTask = (TDB *)GlobalLock16( hTask );
795 TRACE_(task)("Switching to task %04x (%.8s)\n",
796 hTask, pNewTask->module_name );
798 /* Make the task the last in the linked list (round-robin scheduling) */
800 pNewTask->priority++;
801 TASK_UnlinkTask( hTask );
802 TASK_LinkTask( hTask );
803 pNewTask->priority--;
805 /* Finish initializing the new task stack if necessary */
807 newframe16 = THREAD_STACK16( pNewTask->thdb );
808 if (!newframe16->entry_cs)
810 STACK16FRAME *oldframe16 = CURRENT_STACK16;
811 STACK32FRAME *oldframe32 = oldframe16->frame32;
812 STACK32FRAME *newframe32 = newframe16->frame32;
813 newframe16->entry_ip = oldframe16->entry_ip;
814 newframe16->entry_cs = oldframe16->entry_cs;
815 newframe16->ip = oldframe16->ip;
816 newframe16->cs = oldframe16->cs;
817 newframe32->ebp = oldframe32->ebp;
818 newframe32->restore_addr = oldframe32->restore_addr;
819 newframe32->codeselector = oldframe32->codeselector;
822 /* Switch to the new stack */
824 /* NOTE: We need to release/restore the Win16Lock, as the task
825 switched to might be at another recursion level than
826 the old task ... */
828 SYSLEVEL_ReleaseWin16Lock();
830 hCurrentTask = hTask;
831 SET_CUR_THREAD( pNewTask->thdb );
832 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
834 SYSLEVEL_RestoreWin16Lock();
836 return FALSE;
840 /***********************************************************************
841 * TASK_YieldToSystem
843 * Scheduler interface, this way we ensure that all "unsafe" events are
844 * processed outside the scheduler.
846 static void TASK_YieldToSystem( void )
848 if ( !THREAD_IsWin16( THREAD_Current() ) )
850 FIXME_(task)("called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
851 return;
854 if ( Callbacks->CallTaskRescheduleProc() )
856 /* NOTE: We get here only when no task has an event. This means also
857 the current task, so we shouldn't actually return to the
858 caller here. But, we need to do so, as the EVENT_WaitNetEvent
859 call could lead to a complex series of inter-task SendMessage
860 calls which might leave this task in a state where it again
861 has no event, but where its queue's wakeMask is also reset
862 to zero. Reentering TASK_Reschedule in this state would be
863 suicide. Hence, we do return to the caller after processing
864 non-hardware events. Actually, this should not hurt anyone,
865 as the caller must be WaitEvent, and thus the QUEUE_WaitBits
866 loop in USER. Should there actually be no message pending
867 for this task after processing non-hardware events, that loop
868 will simply return to WaitEvent. */
870 EVENT_WaitNetEvent( FALSE, FALSE );
875 /***********************************************************************
876 * InitTask (KERNEL.91)
878 * Called by the application startup code.
880 void WINAPI InitTask16( CONTEXT *context )
882 TDB *pTask;
883 NE_MODULE *pModule;
884 SEGTABLEENTRY *pSegTable;
885 INSTANCEDATA *pinstance;
886 LONG stacklow, stackhi;
888 if (context) EAX_reg(context) = 0;
889 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
890 if (!(pModule = NE_GetPtr( pTask->hModule ))) return;
892 /* Initialize implicitly loaded DLLs */
893 NE_InitializeDLLs( pTask->hModule );
895 if (context)
897 /* Registers on return are:
898 * ax 1 if OK, 0 on error
899 * cx stack limit in bytes
900 * dx cmdShow parameter
901 * si instance handle of the previous instance
902 * di instance handle of the new task
903 * es:bx pointer to command-line inside PSP
905 * 0 (=%bp) is pushed on the stack
907 SEGPTR ptr = STACK16_PUSH( pTask->thdb, sizeof(WORD) );
908 *(WORD *)PTR_SEG_TO_LIN(ptr) = 0;
909 SP_reg(context) -= 2;
911 EAX_reg(context) = 1;
913 if (!pTask->pdb.cmdLine[0]) EBX_reg(context) = 0x80;
914 else
916 LPBYTE p = &pTask->pdb.cmdLine[1];
917 while ((*p == ' ') || (*p == '\t')) p++;
918 EBX_reg(context) = 0x80 + (p - pTask->pdb.cmdLine);
920 ECX_reg(context) = pModule->stack_size;
921 EDX_reg(context) = pTask->nCmdShow;
922 ESI_reg(context) = (DWORD)pTask->hPrevInstance;
923 EDI_reg(context) = (DWORD)pTask->hInstance;
924 ES_reg (context) = (WORD)pTask->hPDB;
927 /* Initialize the local heap */
928 if ( pModule->heap_size )
930 LocalInit16( pTask->hInstance, 0, pModule->heap_size );
933 /* Initialize the INSTANCEDATA structure */
934 pSegTable = NE_SEG_TABLE( pModule );
935 stacklow = pSegTable[pModule->ss - 1].minsize;
936 stackhi = stacklow + pModule->stack_size;
937 if (stackhi > 0xffff) stackhi = 0xffff;
938 pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
939 pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
940 pinstance->stacktop = stacklow;
941 pinstance->stackmin = OFFSETOF( pTask->thdb->cur_stack );
945 /***********************************************************************
946 * WaitEvent (KERNEL.30)
948 BOOL16 WINAPI WaitEvent16( HTASK16 hTask )
950 TDB *pTask;
952 if (!hTask) hTask = GetCurrentTask();
953 pTask = (TDB *)GlobalLock16( hTask );
955 if ( !THREAD_IsWin16( THREAD_Current() ) )
957 FIXME_(task)("called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
958 return TRUE;
961 if (pTask->nEvents > 0)
963 pTask->nEvents--;
964 return FALSE;
966 TASK_YieldToSystem();
968 /* When we get back here, we have an event */
970 if (pTask->nEvents > 0) pTask->nEvents--;
971 return TRUE;
975 /***********************************************************************
976 * PostEvent (KERNEL.31)
978 void WINAPI PostEvent16( HTASK16 hTask )
980 TDB *pTask;
982 if (!hTask) hTask = GetCurrentTask();
983 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
985 if ( !THREAD_IsWin16( pTask->thdb ) )
987 FIXME_(task)("called for Win32 thread (%04x)!\n", pTask->thdb->teb_sel );
988 return;
991 pTask->nEvents++;
993 if ( !THREAD_IsWin16( THREAD_Current() ) )
995 /* wake-up the scheduler waiting in EVENT_WaitNetEvent */
996 EVENT_WakeUp();
1001 /***********************************************************************
1002 * SetPriority (KERNEL.32)
1004 void WINAPI SetPriority16( HTASK16 hTask, INT16 delta )
1006 TDB *pTask;
1007 INT16 newpriority;
1009 if (!hTask) hTask = GetCurrentTask();
1010 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
1011 newpriority = pTask->priority + delta;
1012 if (newpriority < -32) newpriority = -32;
1013 else if (newpriority > 15) newpriority = 15;
1015 pTask->priority = newpriority + 1;
1016 TASK_UnlinkTask( hTask );
1017 TASK_LinkTask( hTask );
1018 pTask->priority--;
1022 /***********************************************************************
1023 * LockCurrentTask (KERNEL.33)
1025 HTASK16 WINAPI LockCurrentTask16( BOOL16 bLock )
1027 if (bLock) hLockedTask = GetCurrentTask();
1028 else hLockedTask = 0;
1029 return hLockedTask;
1033 /***********************************************************************
1034 * IsTaskLocked (KERNEL.122)
1036 HTASK16 WINAPI IsTaskLocked16(void)
1038 return hLockedTask;
1042 /***********************************************************************
1043 * OldYield (KERNEL.117)
1045 void WINAPI OldYield16(void)
1047 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1049 if ( !THREAD_IsWin16( THREAD_Current() ) )
1051 FIXME_(task)("called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1052 return;
1055 if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */
1056 TASK_YieldToSystem();
1057 if (pCurTask) pCurTask->nEvents--;
1061 /***********************************************************************
1062 * DirectedYield (KERNEL.150)
1064 void WINAPI DirectedYield16( HTASK16 hTask )
1066 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1068 if ( !THREAD_IsWin16( THREAD_Current() ) )
1070 FIXME_(task)("called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1071 return;
1074 TRACE_(task)("%04x: DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1076 pCurTask->hYieldTo = hTask;
1077 OldYield16();
1079 TRACE_(task)("%04x: back from DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1082 /***********************************************************************
1083 * Yield16 (KERNEL.29)
1085 void WINAPI Yield16(void)
1087 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1089 if (pCurTask) pCurTask->hYieldTo = 0;
1090 if (pCurTask && pCurTask->hQueue) Callout.UserYield16();
1091 else OldYield16();
1094 /***********************************************************************
1095 * KERNEL_490 (KERNEL.490)
1097 HTASK16 WINAPI KERNEL_490( HTASK16 someTask )
1099 if ( !someTask ) return 0;
1101 FIXME_(task)("(%04x): stub\n", someTask );
1102 return 0;
1105 /***********************************************************************
1106 * MakeProcInstance16 (KERNEL.51)
1108 FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
1110 BYTE *thunk,*lfunc;
1111 SEGPTR thunkaddr;
1113 if (!func) {
1114 ERR_(task)("Ouch ! MakeProcInstance called with func == NULL !\n");
1115 return (FARPROC16)0; /* Windows seems to do the same */
1117 if (!hInstance) hInstance = CURRENT_DS;
1118 thunkaddr = TASK_AllocThunk( GetCurrentTask() );
1119 if (!thunkaddr) return (FARPROC16)0;
1120 thunk = PTR_SEG_TO_LIN( thunkaddr );
1121 lfunc = PTR_SEG_TO_LIN( func );
1123 TRACE_(task)("(%08lx,%04x): got thunk %08lx\n",
1124 (DWORD)func, hInstance, (DWORD)thunkaddr );
1125 if (((lfunc[0]==0x8c) && (lfunc[1]==0xd8)) ||
1126 ((lfunc[0]==0x1e) && (lfunc[1]==0x58))
1128 FIXME_(task)("thunk would be useless for %p, overwriting with nop;nop;\n", func );
1129 lfunc[0]=0x90; /* nop */
1130 lfunc[1]=0x90; /* nop */
1133 *thunk++ = 0xb8; /* movw instance, %ax */
1134 *thunk++ = (BYTE)(hInstance & 0xff);
1135 *thunk++ = (BYTE)(hInstance >> 8);
1136 *thunk++ = 0xea; /* ljmp func */
1137 *(DWORD *)thunk = (DWORD)func;
1138 return (FARPROC16)thunkaddr;
1142 /***********************************************************************
1143 * FreeProcInstance16 (KERNEL.52)
1145 void WINAPI FreeProcInstance16( FARPROC16 func )
1147 TRACE_(task)("(%08lx)\n", (DWORD)func );
1148 TASK_FreeThunk( GetCurrentTask(), (SEGPTR)func );
1152 /**********************************************************************
1153 * GetCodeHandle (KERNEL.93)
1155 HANDLE16 WINAPI GetCodeHandle16( FARPROC16 proc )
1157 HANDLE16 handle;
1158 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1160 /* Return the code segment containing 'proc'. */
1161 /* Not sure if this is really correct (shouldn't matter that much). */
1163 /* Check if it is really a thunk */
1164 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1165 handle = GlobalHandle16( thunk[6] + (thunk[7] << 8) );
1166 else
1167 handle = GlobalHandle16( HIWORD(proc) );
1169 return handle;
1172 /**********************************************************************
1173 * GetCodeInfo (KERNEL.104)
1175 VOID WINAPI GetCodeInfo16( FARPROC16 proc, SEGINFO *segInfo )
1177 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1178 NE_MODULE *pModule = NULL;
1179 SEGTABLEENTRY *pSeg = NULL;
1180 WORD segNr;
1182 /* proc is either a thunk, or else a pair of module handle
1183 and segment number. In the first case, we also need to
1184 extract module and segment number. */
1186 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1188 WORD selector = thunk[6] + (thunk[7] << 8);
1189 pModule = NE_GetPtr( GlobalHandle16( selector ) );
1190 pSeg = pModule? NE_SEG_TABLE( pModule ) : NULL;
1192 if ( pModule )
1193 for ( segNr = 0; segNr < pModule->seg_count; segNr++, pSeg++ )
1194 if ( GlobalHandleToSel16(pSeg->hSeg) == selector )
1195 break;
1197 if ( pModule && segNr >= pModule->seg_count )
1198 pSeg = NULL;
1200 else
1202 pModule = NE_GetPtr( HIWORD( proc ) );
1203 segNr = LOWORD( proc );
1205 if ( pModule && segNr < pModule->seg_count )
1206 pSeg = NE_SEG_TABLE( pModule ) + segNr;
1209 /* fill in segment information */
1211 segInfo->offSegment = pSeg? pSeg->filepos : 0;
1212 segInfo->cbSegment = pSeg? pSeg->size : 0;
1213 segInfo->flags = pSeg? pSeg->flags : 0;
1214 segInfo->cbAlloc = pSeg? pSeg->minsize : 0;
1215 segInfo->h = pSeg? pSeg->hSeg : 0;
1216 segInfo->alignShift = pModule? pModule->alignment : 0;
1220 /**********************************************************************
1221 * DefineHandleTable16 (KERNEL.94)
1223 BOOL16 WINAPI DefineHandleTable16( WORD wOffset )
1225 return TRUE; /* FIXME */
1229 /***********************************************************************
1230 * SetTaskQueue (KERNEL.34)
1232 HQUEUE16 WINAPI SetTaskQueue16( HTASK16 hTask, HQUEUE16 hQueue )
1234 HQUEUE16 hPrev;
1235 TDB *pTask;
1237 if (!hTask) hTask = GetCurrentTask();
1238 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1240 hPrev = pTask->hQueue;
1241 pTask->hQueue = hQueue;
1243 return hPrev;
1247 /***********************************************************************
1248 * GetTaskQueue (KERNEL.35)
1250 HQUEUE16 WINAPI GetTaskQueue16( HTASK16 hTask )
1252 TDB *pTask;
1254 if (!hTask) hTask = GetCurrentTask();
1255 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1256 return pTask->hQueue;
1259 /***********************************************************************
1260 * SetThreadQueue (KERNEL.463)
1262 HQUEUE16 WINAPI SetThreadQueue16( DWORD thread, HQUEUE16 hQueue )
1264 THDB *thdb = thread? THREAD_IdToTHDB( thread ) : THREAD_Current();
1265 HQUEUE16 oldQueue = thdb? thdb->teb.queue : 0;
1267 if ( thdb )
1269 thdb->teb.queue = hQueue;
1271 if ( GetTaskQueue16( thdb->process->task ) == oldQueue )
1272 SetTaskQueue16( thdb->process->task, hQueue );
1275 return oldQueue;
1278 /***********************************************************************
1279 * GetThreadQueue (KERNEL.464)
1281 HQUEUE16 WINAPI GetThreadQueue16( DWORD thread )
1283 THDB *thdb = NULL;
1284 if ( !thread )
1285 thdb = THREAD_Current();
1286 else if ( HIWORD(thread) )
1287 thdb = THREAD_IdToTHDB( thread );
1288 else if ( IsTask16( (HTASK16)thread ) )
1289 thdb = ((TDB *)GlobalLock16( (HANDLE16)thread ))->thdb;
1291 return (HQUEUE16)(thdb? thdb->teb.queue : 0);
1294 /***********************************************************************
1295 * SetFastQueue (KERNEL.624)
1297 VOID WINAPI SetFastQueue16( DWORD thread, HANDLE hQueue )
1299 THDB *thdb = NULL;
1300 if ( !thread )
1301 thdb = THREAD_Current();
1302 else if ( HIWORD(thread) )
1303 thdb = THREAD_IdToTHDB( thread );
1304 else if ( IsTask16( (HTASK16)thread ) )
1305 thdb = ((TDB *)GlobalLock16( (HANDLE16)thread ))->thdb;
1307 if ( thdb ) thdb->teb.queue = (HQUEUE16) hQueue;
1310 /***********************************************************************
1311 * GetFastQueue (KERNEL.625)
1313 HANDLE WINAPI GetFastQueue16( void )
1315 THDB *thdb = THREAD_Current();
1316 if (!thdb) return 0;
1318 if (!thdb->teb.queue)
1319 Callout.InitThreadInput16( 0, THREAD_IsWin16(thdb)? 4 : 5 );
1321 if (!thdb->teb.queue)
1322 FIXME_(task)("(): should initialize thread-local queue, expect failure!\n" );
1324 return (HANDLE)thdb->teb.queue;
1327 /***********************************************************************
1328 * SwitchStackTo (KERNEL.108)
1330 void WINAPI SwitchStackTo16( WORD seg, WORD ptr, WORD top )
1332 TDB *pTask;
1333 STACK16FRAME *oldFrame, *newFrame;
1334 INSTANCEDATA *pData;
1335 UINT16 copySize;
1337 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1338 if (!(pData = (INSTANCEDATA *)GlobalLock16( seg ))) return;
1339 TRACE_(task)("old=%04x:%04x new=%04x:%04x\n",
1340 SELECTOROF( pTask->thdb->cur_stack ),
1341 OFFSETOF( pTask->thdb->cur_stack ), seg, ptr );
1343 /* Save the old stack */
1345 oldFrame = THREAD_STACK16( pTask->thdb );
1346 /* pop frame + args and push bp */
1347 pData->old_ss_sp = pTask->thdb->cur_stack + sizeof(STACK16FRAME)
1348 + 2 * sizeof(WORD);
1349 *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp) = oldFrame->bp;
1350 pData->stacktop = top;
1351 pData->stackmin = ptr;
1352 pData->stackbottom = ptr;
1354 /* Switch to the new stack */
1356 /* Note: we need to take the 3 arguments into account; otherwise,
1357 * the stack will underflow upon return from this function.
1359 copySize = oldFrame->bp - OFFSETOF(pData->old_ss_sp);
1360 copySize += 3 * sizeof(WORD) + sizeof(STACK16FRAME);
1361 pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( seg, ptr - copySize );
1362 newFrame = THREAD_STACK16( pTask->thdb );
1364 /* Copy the stack frame and the local variables to the new stack */
1366 memmove( newFrame, oldFrame, copySize );
1367 newFrame->bp = ptr;
1368 *(WORD *)PTR_SEG_OFF_TO_LIN( seg, ptr ) = 0; /* clear previous bp */
1372 /***********************************************************************
1373 * SwitchStackBack (KERNEL.109)
1375 void WINAPI SwitchStackBack16( CONTEXT *context )
1377 TDB *pTask;
1378 STACK16FRAME *oldFrame, *newFrame;
1379 INSTANCEDATA *pData;
1381 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1382 if (!(pData = (INSTANCEDATA *)GlobalLock16(SELECTOROF(pTask->thdb->cur_stack))))
1383 return;
1384 if (!pData->old_ss_sp)
1386 WARN_(task)("No previous SwitchStackTo\n" );
1387 return;
1389 TRACE_(task)("restoring stack %04x:%04x\n",
1390 SELECTOROF(pData->old_ss_sp), OFFSETOF(pData->old_ss_sp) );
1392 oldFrame = THREAD_STACK16( pTask->thdb );
1394 /* Pop bp from the previous stack */
1396 BP_reg(context) = *(WORD *)PTR_SEG_TO_LIN(pData->old_ss_sp);
1397 pData->old_ss_sp += sizeof(WORD);
1399 /* Switch back to the old stack */
1401 pTask->thdb->cur_stack = pData->old_ss_sp - sizeof(STACK16FRAME);
1402 SS_reg(context) = SELECTOROF(pData->old_ss_sp);
1403 ESP_reg(context) = OFFSETOF(pData->old_ss_sp) - sizeof(DWORD); /*ret addr*/
1404 pData->old_ss_sp = 0;
1406 /* Build a stack frame for the return */
1408 newFrame = THREAD_STACK16( pTask->thdb );
1409 newFrame->frame32 = oldFrame->frame32;
1410 if (TRACE_ON(relay))
1412 newFrame->entry_ip = oldFrame->entry_ip;
1413 newFrame->entry_cs = oldFrame->entry_cs;
1418 /***********************************************************************
1419 * GetTaskQueueDS (KERNEL.118)
1421 void WINAPI GetTaskQueueDS16( CONTEXT *context )
1423 DS_reg(context) = GlobalHandleToSel16( GetTaskQueue16(0) );
1427 /***********************************************************************
1428 * GetTaskQueueES (KERNEL.119)
1430 void WINAPI GetTaskQueueES16( CONTEXT *context )
1432 ES_reg(context) = GlobalHandleToSel16( GetTaskQueue16(0) );
1436 /***********************************************************************
1437 * GetCurrentTask (KERNEL.36)
1439 HTASK16 WINAPI GetCurrentTask(void)
1441 return THREAD_InitDone? PROCESS_Current()->task : 0;
1444 DWORD WINAPI WIN16_GetCurrentTask(void)
1446 /* This is the version used by relay code; the first task is */
1447 /* returned in the high word of the result */
1448 return MAKELONG( GetCurrentTask(), hFirstTask );
1452 /***********************************************************************
1453 * GetCurrentPDB (KERNEL.37)
1455 * UNDOC: returns PSP of KERNEL in high word
1457 DWORD WINAPI GetCurrentPDB16(void)
1459 TDB *pTask;
1461 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1462 return MAKELONG(pTask->hPDB, 0); /* FIXME */
1466 /***********************************************************************
1467 * GetInstanceData (KERNEL.54)
1469 INT16 WINAPI GetInstanceData16( HINSTANCE16 instance, WORD buffer, INT16 len )
1471 char *ptr = (char *)GlobalLock16( instance );
1472 if (!ptr || !len) return 0;
1473 if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
1474 memcpy( (char *)GlobalLock16(CURRENT_DS) + buffer, ptr + buffer, len );
1475 return len;
1479 /***********************************************************************
1480 * GetExeVersion (KERNEL.105)
1482 WORD WINAPI GetExeVersion16(void)
1484 TDB *pTask;
1486 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1487 return pTask->version;
1491 /***********************************************************************
1492 * SetErrorMode16 (KERNEL.107)
1494 UINT16 WINAPI SetErrorMode16( UINT16 mode )
1496 TDB *pTask;
1497 UINT16 oldMode;
1499 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1500 oldMode = pTask->error_mode;
1501 pTask->error_mode = mode;
1502 pTask->thdb->process->error_mode = mode;
1503 return oldMode;
1507 /***********************************************************************
1508 * SetErrorMode32 (KERNEL32.486)
1510 UINT WINAPI SetErrorMode( UINT mode )
1512 return SetErrorMode16( (UINT16)mode );
1516 /***********************************************************************
1517 * GetDOSEnvironment (KERNEL.131)
1519 SEGPTR WINAPI GetDOSEnvironment16(void)
1521 TDB *pTask;
1523 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1524 return PTR_SEG_OFF_TO_SEGPTR( pTask->pdb.environment, 0 );
1528 /***********************************************************************
1529 * GetNumTasks (KERNEL.152)
1531 UINT16 WINAPI GetNumTasks16(void)
1533 return nTaskCount;
1537 /***********************************************************************
1538 * GetTaskDS (KERNEL.155)
1540 * Note: this function apparently returns a DWORD with LOWORD == HIWORD.
1541 * I don't think we need to bother with this.
1543 HINSTANCE16 WINAPI GetTaskDS16(void)
1545 TDB *pTask;
1547 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1548 return pTask->hInstance;
1551 /***********************************************************************
1552 * GetDummyModuleHandleDS (KERNEL.602)
1554 VOID WINAPI GetDummyModuleHandleDS16( CONTEXT *context )
1556 TDB *pTask;
1557 WORD selector;
1559 AX_reg( context ) = 0;
1560 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1561 if (!(pTask->flags & TDBF_WIN32)) return;
1563 selector = GlobalHandleToSel16( pTask->hModule );
1564 DS_reg( context ) = selector;
1565 AX_reg( context ) = selector;
1568 /***********************************************************************
1569 * IsTask (KERNEL.320)
1571 BOOL16 WINAPI IsTask16( HTASK16 hTask )
1573 TDB *pTask;
1575 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return FALSE;
1576 if (GlobalSize16( hTask ) < sizeof(TDB)) return FALSE;
1577 return (pTask->magic == TDB_MAGIC);
1581 /***********************************************************************
1582 * SetTaskSignalProc (KERNEL.38)
1584 * Real 16-bit interface is provided by the THUNK_SetTaskSignalProc.
1586 FARPROC16 WINAPI SetTaskSignalProc( HTASK16 hTask, FARPROC16 proc )
1588 TDB *pTask;
1589 FARPROC16 oldProc;
1591 if (!hTask) hTask = GetCurrentTask();
1592 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return NULL;
1593 oldProc = (FARPROC16)pTask->userhandler;
1594 pTask->userhandler = (USERSIGNALPROC)proc;
1595 return oldProc;
1599 /***********************************************************************
1600 * SetSigHandler (KERNEL.140)
1602 WORD WINAPI SetSigHandler16( FARPROC16 newhandler, FARPROC16* oldhandler,
1603 UINT16 *oldmode, UINT16 newmode, UINT16 flag )
1605 FIXME_(task)("(%p,%p,%p,%d,%d), unimplemented.\n",
1606 newhandler,oldhandler,oldmode,newmode,flag );
1608 if (flag != 1) return 0;
1609 if (!newmode) newhandler = NULL; /* Default handler */
1610 if (newmode != 4)
1612 TDB *pTask;
1614 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return 0;
1615 if (oldmode) *oldmode = pTask->signal_flags;
1616 pTask->signal_flags = newmode;
1617 if (oldhandler) *oldhandler = pTask->sighandler;
1618 pTask->sighandler = newhandler;
1620 return 0;
1624 /***********************************************************************
1625 * GlobalNotify (KERNEL.154)
1627 * Note that GlobalNotify does _not_ return the old NotifyProc
1628 * -- contrary to LocalNotify !!
1630 VOID WINAPI GlobalNotify16( FARPROC16 proc )
1632 TDB *pTask;
1634 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
1635 pTask->discardhandler = proc;
1639 /***********************************************************************
1640 * GetExePtr (KERNEL.133)
1642 static HMODULE16 GetExePtrHelper( HANDLE16 handle, HTASK16 *hTask )
1644 char *ptr;
1645 HANDLE16 owner;
1647 /* Check for module handle */
1649 if (!(ptr = GlobalLock16( handle ))) return 0;
1650 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return handle;
1652 /* Search for this handle inside all tasks */
1654 *hTask = hFirstTask;
1655 while (*hTask)
1657 TDB *pTask = (TDB *)GlobalLock16( *hTask );
1658 if ((*hTask == handle) ||
1659 (pTask->hInstance == handle) ||
1660 (pTask->hQueue == handle) ||
1661 (pTask->hPDB == handle)) return pTask->hModule;
1662 *hTask = pTask->hNext;
1665 /* Check the owner for module handle */
1667 owner = FarGetOwner16( handle );
1668 if (!(ptr = GlobalLock16( owner ))) return 0;
1669 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return owner;
1671 /* Search for the owner inside all tasks */
1673 *hTask = hFirstTask;
1674 while (*hTask)
1676 TDB *pTask = (TDB *)GlobalLock16( *hTask );
1677 if ((*hTask == owner) ||
1678 (pTask->hInstance == owner) ||
1679 (pTask->hQueue == owner) ||
1680 (pTask->hPDB == owner)) return pTask->hModule;
1681 *hTask = pTask->hNext;
1684 return 0;
1687 HMODULE16 WINAPI GetExePtr( HANDLE16 handle )
1689 HTASK16 dummy;
1690 return GetExePtrHelper( handle, &dummy );
1693 void WINAPI WIN16_GetExePtr( CONTEXT *context )
1695 WORD *stack = PTR_SEG_OFF_TO_LIN(SS_reg(context), SP_reg(context));
1696 HANDLE16 handle = (HANDLE16)stack[2];
1697 HTASK16 hTask = 0;
1698 HMODULE16 hModule;
1700 hModule = GetExePtrHelper( handle, &hTask );
1702 AX_reg(context) = CX_reg(context) = hModule;
1703 if (hTask) ES_reg(context) = hTask;
1706 /***********************************************************************
1707 * TaskFirst (TOOLHELP.63)
1709 BOOL16 WINAPI TaskFirst16( TASKENTRY *lpte )
1711 lpte->hNext = hFirstTask;
1712 return TaskNext16( lpte );
1716 /***********************************************************************
1717 * TaskNext (TOOLHELP.64)
1719 BOOL16 WINAPI TaskNext16( TASKENTRY *lpte )
1721 TDB *pTask;
1722 INSTANCEDATA *pInstData;
1724 TRACE_(toolhelp)("(%p): task=%04x\n", lpte, lpte->hNext );
1725 if (!lpte->hNext) return FALSE;
1726 pTask = (TDB *)GlobalLock16( lpte->hNext );
1727 if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1728 pInstData = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( pTask->hInstance, 0 );
1729 lpte->hTask = lpte->hNext;
1730 lpte->hTaskParent = pTask->hParent;
1731 lpte->hInst = pTask->hInstance;
1732 lpte->hModule = pTask->hModule;
1733 lpte->wSS = SELECTOROF( pTask->thdb->cur_stack );
1734 lpte->wSP = OFFSETOF( pTask->thdb->cur_stack );
1735 lpte->wStackTop = pInstData->stacktop;
1736 lpte->wStackMinimum = pInstData->stackmin;
1737 lpte->wStackBottom = pInstData->stackbottom;
1738 lpte->wcEvents = pTask->nEvents;
1739 lpte->hQueue = pTask->hQueue;
1740 strncpy( lpte->szModule, pTask->module_name, 8 );
1741 lpte->szModule[8] = '\0';
1742 lpte->wPSPOffset = 0x100; /*??*/
1743 lpte->hNext = pTask->hNext;
1744 return TRUE;
1748 /***********************************************************************
1749 * TaskFindHandle (TOOLHELP.65)
1751 BOOL16 WINAPI TaskFindHandle16( TASKENTRY *lpte, HTASK16 hTask )
1753 lpte->hNext = hTask;
1754 return TaskNext16( lpte );
1758 /***********************************************************************
1759 * GetAppCompatFlags16 (KERNEL.354)
1761 DWORD WINAPI GetAppCompatFlags16( HTASK16 hTask )
1763 return GetAppCompatFlags( hTask );
1767 /***********************************************************************
1768 * GetAppCompatFlags32 (USER32.206)
1770 DWORD WINAPI GetAppCompatFlags( HTASK hTask )
1772 TDB *pTask;
1774 if (!hTask) hTask = GetCurrentTask();
1775 if (!(pTask=(TDB *)GlobalLock16( (HTASK16)hTask ))) return 0;
1776 if (GlobalSize16(hTask) < sizeof(TDB)) return 0;
1777 return pTask->compat_flags;