Implement the button with bitmaps.
[wine/hacks.git] / loader / task.c
blob75dd7bf9f8d49821de32b2e4b42fef123841694e
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 "debug.h"
37 #include "dosexe.h"
38 #include "dde_proc.h"
39 #include "server.h"
41 /* Min. number of thunks allocated when creating a new segment */
42 #define MIN_THUNKS 32
44 /* Pointer to function to switch to a larger stack */
45 int (*IF1632_CallLargeStack)( int (*func)(), void *arg ) = NULL;
47 /* Pointer to debugger callback routine */
48 void (*TASK_AddTaskEntryBreakpoint)( HTASK16 hTask ) = NULL;
50 static THHOOK DefaultThhook = { 0 };
51 THHOOK *pThhook = &DefaultThhook;
53 #define hCurrentTask (pThhook->CurTDB)
54 #define hFirstTask (pThhook->HeadTDB)
55 #define hLockedTask (pThhook->LockTDB)
57 static HTASK16 hTaskToKill = 0;
58 static UINT16 nTaskCount = 0;
60 static void TASK_YieldToSystem( void );
62 extern BOOL THREAD_InitDone;
65 /***********************************************************************
66 * TASK_InstallTHHook
68 void TASK_InstallTHHook( THHOOK *pNewThhook )
70 THHOOK *pOldThhook = pThhook;
72 pThhook = pNewThhook? pNewThhook : &DefaultThhook;
74 *pThhook = *pOldThhook;
77 /***********************************************************************
78 * TASK_GetNextTask
80 HTASK16 TASK_GetNextTask( HTASK16 hTask )
82 TDB* pTask = (TDB*)GlobalLock16(hTask);
84 if (pTask->hNext) return pTask->hNext;
85 return (hFirstTask != hTask) ? hFirstTask : 0;
88 /***********************************************************************
89 * TASK_LinkTask
91 static void TASK_LinkTask( HTASK16 hTask )
93 HTASK16 *prevTask;
94 TDB *pTask;
96 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
97 prevTask = &hFirstTask;
98 while (*prevTask)
100 TDB *prevTaskPtr = (TDB *)GlobalLock16( *prevTask );
101 if (prevTaskPtr->priority >= pTask->priority) break;
102 prevTask = &prevTaskPtr->hNext;
104 pTask->hNext = *prevTask;
105 *prevTask = hTask;
106 nTaskCount++;
110 /***********************************************************************
111 * TASK_UnlinkTask
113 static void TASK_UnlinkTask( HTASK16 hTask )
115 HTASK16 *prevTask;
116 TDB *pTask;
118 prevTask = &hFirstTask;
119 while (*prevTask && (*prevTask != hTask))
121 pTask = (TDB *)GlobalLock16( *prevTask );
122 prevTask = &pTask->hNext;
124 if (*prevTask)
126 pTask = (TDB *)GlobalLock16( *prevTask );
127 *prevTask = pTask->hNext;
128 pTask->hNext = 0;
129 nTaskCount--;
134 /***********************************************************************
135 * TASK_CreateThunks
137 * Create a thunk free-list in segment 'handle', starting from offset 'offset'
138 * and containing 'count' entries.
140 static void TASK_CreateThunks( HGLOBAL16 handle, WORD offset, WORD count )
142 int i;
143 WORD free;
144 THUNKS *pThunk;
146 pThunk = (THUNKS *)((BYTE *)GlobalLock16( handle ) + offset);
147 pThunk->next = 0;
148 pThunk->magic = THUNK_MAGIC;
149 pThunk->free = (int)&pThunk->thunks - (int)pThunk;
150 free = pThunk->free;
151 for (i = 0; i < count-1; i++)
153 free += 8; /* Offset of next thunk */
154 pThunk->thunks[4*i] = free;
156 pThunk->thunks[4*i] = 0; /* Last thunk */
160 /***********************************************************************
161 * TASK_AllocThunk
163 * Allocate a thunk for MakeProcInstance().
165 static SEGPTR TASK_AllocThunk( HTASK16 hTask )
167 TDB *pTask;
168 THUNKS *pThunk;
169 WORD sel, base;
171 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
172 sel = pTask->hCSAlias;
173 pThunk = &pTask->thunks;
174 base = (int)pThunk - (int)pTask;
175 while (!pThunk->free)
177 sel = pThunk->next;
178 if (!sel) /* Allocate a new segment */
180 sel = GLOBAL_Alloc( GMEM_FIXED, sizeof(THUNKS) + (MIN_THUNKS-1)*8,
181 pTask->hPDB, TRUE, FALSE, FALSE );
182 if (!sel) return (SEGPTR)0;
183 TASK_CreateThunks( sel, 0, MIN_THUNKS );
184 pThunk->next = sel;
186 pThunk = (THUNKS *)GlobalLock16( sel );
187 base = 0;
189 base += pThunk->free;
190 pThunk->free = *(WORD *)((BYTE *)pThunk + pThunk->free);
191 return PTR_SEG_OFF_TO_SEGPTR( sel, base );
195 /***********************************************************************
196 * TASK_FreeThunk
198 * Free a MakeProcInstance() thunk.
200 static BOOL TASK_FreeThunk( HTASK16 hTask, SEGPTR thunk )
202 TDB *pTask;
203 THUNKS *pThunk;
204 WORD sel, base;
206 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
207 sel = pTask->hCSAlias;
208 pThunk = &pTask->thunks;
209 base = (int)pThunk - (int)pTask;
210 while (sel && (sel != HIWORD(thunk)))
212 sel = pThunk->next;
213 pThunk = (THUNKS *)GlobalLock16( sel );
214 base = 0;
216 if (!sel) return FALSE;
217 *(WORD *)((BYTE *)pThunk + LOWORD(thunk) - base) = pThunk->free;
218 pThunk->free = LOWORD(thunk) - base;
219 return TRUE;
223 /***********************************************************************
224 * TASK_CallToStart
226 * 32-bit entry point for a new task. This function is responsible for
227 * setting up the registers and jumping to the 16-bit entry point.
229 static void TASK_CallToStart(void)
231 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
232 NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
233 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
235 SET_CUR_THREAD( pTask->thdb );
236 CLIENT_InitThread();
238 /* Terminate the stack frame chain */
239 memset(THREAD_STACK16( pTask->thdb ), '\0', sizeof(STACK16FRAME));
241 /* Call USER signal proc */
242 PROCESS_CallUserSignalProc( USIG_THREAD_INIT, 0, 0 ); /* for initial thread */
243 PROCESS_CallUserSignalProc( USIG_PROCESS_INIT, 0, 0 );
244 PROCESS_CallUserSignalProc( USIG_PROCESS_LOADED, 0, 0 );
245 PROCESS_CallUserSignalProc( USIG_PROCESS_RUNNING, 0, 0 );
247 if (pModule->flags & NE_FFLAGS_WIN32)
249 ERR( task, "Called for Win32 task!\n" );
250 ExitProcess( 1 );
252 else if (pModule->dos_image)
254 DOSVM_Enter( NULL );
255 ExitProcess( 0 );
257 else
259 /* Registers at initialization must be:
260 * ax zero
261 * bx stack size in bytes
262 * cx heap size in bytes
263 * si previous app instance
264 * di current app instance
265 * bp zero
266 * es selector to the PSP
267 * ds dgroup of the application
268 * ss stack selector
269 * sp top of the stack
271 CONTEXT context;
273 memset( &context, 0, sizeof(context) );
274 CS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->cs - 1].hSeg);
275 DS_reg(&context) = GlobalHandleToSel16(pSegTable[pModule->dgroup - 1].hSeg);
276 ES_reg(&context) = pTask->hPDB;
277 EIP_reg(&context) = pModule->ip;
278 EBX_reg(&context) = pModule->stack_size;
279 ECX_reg(&context) = pModule->heap_size;
280 EDI_reg(&context) = context.SegDs;
282 TRACE(task, "Starting main program: cs:ip=%04lx:%04x ds=%04lx ss:sp=%04x:%04x\n",
283 CS_reg(&context), IP_reg(&context), DS_reg(&context),
284 SELECTOROF(pTask->thdb->cur_stack),
285 OFFSETOF(pTask->thdb->cur_stack) );
287 Callbacks->CallRegisterShortProc( &context, 0 );
288 /* This should never return */
289 ERR( task, "Main program returned! (should never happen)\n" );
290 ExitProcess( 1 );
295 /***********************************************************************
296 * TASK_Create
298 * NOTE: This routine might be called by a Win32 thread. We don't have
299 * any real problems with that, since we operated merely on a private
300 * TDB structure that is not yet linked into the task list.
302 BOOL TASK_Create( THDB *thdb, NE_MODULE *pModule, HINSTANCE16 hInstance,
303 HINSTANCE16 hPrevInstance, UINT16 cmdShow)
305 HTASK16 hTask;
306 TDB *pTask;
307 LPSTR cmd_line;
308 WORD sp;
309 char *stack32Top;
310 char name[10];
311 STACK16FRAME *frame16;
312 STACK32FRAME *frame32;
313 PDB *pdb32 = thdb->process;
314 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
316 /* Allocate the task structure */
318 hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
319 pModule->self, FALSE, FALSE, FALSE );
320 if (!hTask) return FALSE;
321 pTask = (TDB *)GlobalLock16( hTask );
323 /* Fill the task structure */
325 pTask->nEvents = 0;
326 pTask->hSelf = hTask;
327 pTask->flags = 0;
329 if (pModule->flags & NE_FFLAGS_WIN32)
330 pTask->flags |= TDBF_WIN32;
331 if (pModule->lpDosTask)
332 pTask->flags |= TDBF_WINOLDAP;
334 pTask->version = pModule->expected_version;
335 pTask->hInstance = hInstance? hInstance : pModule->self;
336 pTask->hPrevInstance = hPrevInstance;
337 pTask->hModule = pModule->self;
338 pTask->hParent = GetCurrentTask();
339 pTask->magic = TDB_MAGIC;
340 pTask->nCmdShow = cmdShow;
341 pTask->thdb = thdb;
342 pTask->curdrive = DRIVE_GetCurrentDrive() | 0x80;
343 strcpy( pTask->curdir, "\\" );
344 lstrcpynA( pTask->curdir + 1, DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() ),
345 sizeof(pTask->curdir) - 1 );
347 /* Create the thunks block */
349 TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
351 /* Copy the module name */
353 GetModuleName16( pModule->self, name, sizeof(name) );
354 strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
356 /* Allocate a selector for the PDB */
358 pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB16),
359 pModule->self, FALSE, FALSE, FALSE, NULL );
361 /* Fill the PDB */
363 pTask->pdb.int20 = 0x20cd;
364 pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */
365 PUT_DWORD(&pTask->pdb.dispatcher[1], (DWORD)NE_GetEntryPoint(
366 GetModuleHandle16("KERNEL"), 102 )); /* KERNEL.102 is DOS3Call() */
367 pTask->pdb.savedint22 = INT_GetPMHandler( 0x22 );
368 pTask->pdb.savedint23 = INT_GetPMHandler( 0x23 );
369 pTask->pdb.savedint24 = INT_GetPMHandler( 0x24 );
370 pTask->pdb.fileHandlesPtr =
371 PTR_SEG_OFF_TO_SEGPTR( GlobalHandleToSel16(pTask->hPDB),
372 (int)&((PDB16 *)0)->fileHandles );
373 pTask->pdb.hFileHandles = 0;
374 memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
375 pTask->pdb.environment = pdb32->env_db->env_sel;
376 pTask->pdb.nbFiles = 20;
378 /* Fill the command line */
380 cmd_line = pdb32->env_db->cmd_line;
381 while (*cmd_line && (*cmd_line != ' ') && (*cmd_line != '\t')) cmd_line++;
382 while ((*cmd_line == ' ') || (*cmd_line == '\t')) cmd_line++;
383 lstrcpynA( pTask->pdb.cmdLine+1, cmd_line, sizeof(pTask->pdb.cmdLine)-1);
384 pTask->pdb.cmdLine[0] = strlen( pTask->pdb.cmdLine + 1 );
386 /* Get the compatibility flags */
388 pTask->compat_flags = GetProfileIntA( "Compatibility", name, 0 );
390 /* Allocate a code segment alias for the TDB */
392 pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
393 sizeof(TDB), pTask->hPDB, TRUE,
394 FALSE, FALSE, NULL );
396 /* Set the owner of the environment block */
398 FarSetOwner16( pTask->pdb.environment, pTask->hPDB );
400 /* Default DTA overwrites command-line */
402 pTask->dta = PTR_SEG_OFF_TO_SEGPTR( pTask->hPDB,
403 (int)&pTask->pdb.cmdLine - (int)&pTask->pdb );
405 /* If we have a DGROUP/hInstance, use it for 16-bit stack */
407 if ( hInstance )
409 if (!(sp = pModule->sp))
410 sp = pSegTable[pModule->ss-1].minsize + pModule->stack_size;
411 sp &= ~1; sp -= sizeof(STACK16FRAME);
412 pTask->thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( hInstance, sp );
415 /* Create the 16-bit stack frame */
417 pTask->thdb->cur_stack -= sizeof(STACK16FRAME);
418 frame16 = (STACK16FRAME *)PTR_SEG_TO_LIN( pTask->thdb->cur_stack );
419 frame16->ebp = OFFSETOF( pTask->thdb->cur_stack ) + (int)&((STACK16FRAME *)0)->bp;
420 frame16->bp = LOWORD(frame16->ebp);
421 frame16->ds = frame16->es = hInstance;
422 frame16->fs = 0;
423 frame16->entry_point = 0;
424 frame16->entry_cs = 0;
425 frame16->mutex_count = 1; /* TASK_Reschedule is called from 16-bit code */
426 /* The remaining fields will be initialized in TASK_Reschedule */
428 /* Create the 32-bit stack frame */
430 stack32Top = (char*)pTask->thdb->teb.stack_top;
431 frame16->frame32 = frame32 = (STACK32FRAME *)stack32Top - 1;
432 frame32->frame16 = pTask->thdb->cur_stack + sizeof(STACK16FRAME);
433 frame32->edi = 0;
434 frame32->esi = 0;
435 frame32->edx = 0;
436 frame32->ecx = 0;
437 frame32->ebx = 0;
438 frame32->retaddr = (DWORD)TASK_CallToStart;
439 /* The remaining fields will be initialized in TASK_Reschedule */
441 /* Enter task handle into thread and process */
443 pTask->thdb->teb.htask16 = pTask->thdb->process->task = hTask;
445 TRACE(task, "module='%s' cmdline='%s' task=%04x\n",
446 name, cmd_line, hTask );
448 return TRUE;
451 /***********************************************************************
452 * TASK_StartTask
454 * NOTE: This routine might be called by a Win32 thread. Thus, we need
455 * to be careful to protect global data structures. We do this
456 * by entering the Win16Lock while linking the task into the
457 * global task list.
459 void TASK_StartTask( HTASK16 hTask )
461 TDB *pTask = (TDB *)GlobalLock16( hTask );
462 if ( !pTask ) return;
464 /* Add the task to the linked list */
466 SYSLEVEL_EnterWin16Lock();
467 TASK_LinkTask( hTask );
468 SYSLEVEL_LeaveWin16Lock();
470 TRACE(task, "linked task %04x\n", hTask );
472 /* If requested, add entry point breakpoint */
474 if ( TASK_AddTaskEntryBreakpoint )
475 TASK_AddTaskEntryBreakpoint( hTask );
477 /* Get the task up and running. */
479 if ( THREAD_IsWin16( pTask->thdb ) )
481 pTask->nEvents++;
483 /* If we ourselves are a 16-bit task, we simply Yield().
484 If we are 32-bit however, we need to signal the scheduler. */
486 if ( THREAD_IsWin16( THREAD_Current() ) )
487 OldYield16();
488 else
489 EVENT_WakeUp();
491 else
493 /* To start a 32-bit task, we spawn its initial thread. */
495 SYSDEPS_SpawnThread( pTask->thdb );
500 /***********************************************************************
501 * TASK_DeleteTask
503 static void TASK_DeleteTask( HTASK16 hTask )
505 TDB *pTask;
506 HGLOBAL16 hPDB;
508 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
509 hPDB = pTask->hPDB;
511 pTask->magic = 0xdead; /* invalidate signature */
513 /* Delete the Win32 part of the task */
515 /* PROCESS_FreePDB( pTask->thdb->process ); FIXME */
516 /* K32OBJ_DecCount( &pTask->thdb->header ); FIXME */
518 /* Free the selector aliases */
520 GLOBAL_FreeBlock( pTask->hCSAlias );
521 GLOBAL_FreeBlock( pTask->hPDB );
523 /* Free the task module */
525 FreeModule16( pTask->hModule );
527 /* Free the task structure itself */
529 GlobalFree16( hTask );
531 /* Free all memory used by this task (including the 32-bit stack, */
532 /* the environment block and the thunk segments). */
534 GlobalFreeAll16( hPDB );
537 /***********************************************************************
538 * TASK_KillTask
540 void TASK_KillTask( HTASK16 hTask )
542 TDB *pTask;
544 /* Enter the Win16Lock to protect global data structures */
545 SYSLEVEL_EnterWin16Lock();
547 if ( !hTask ) hTask = GetCurrentTask();
548 pTask = (TDB *)GlobalLock16( hTask );
549 if ( !pTask )
551 SYSLEVEL_LeaveWin16Lock();
552 return;
555 TRACE(task, "Killing task %04x\n", hTask );
557 /* Delete active sockets */
559 if( pTask->pwsi )
560 WINSOCK_DeleteTaskWSI( pTask, pTask->pwsi );
562 #ifdef MZ_SUPPORTED
564 /* Kill DOS VM task */
565 NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
566 if ( pModule->lpDosTask )
567 MZ_KillModule( pModule->lpDosTask );
569 #endif
571 /* Perform USER cleanup */
573 if (pTask->userhandler)
574 pTask->userhandler( hTask, USIG16_TERMINATION, 0,
575 pTask->hInstance, pTask->hQueue );
577 PROCESS_CallUserSignalProc( USIG_PROCESS_EXIT, 0, 0 );
578 PROCESS_CallUserSignalProc( USIG_THREAD_EXIT, 0, 0 ); /* FIXME */
579 PROCESS_CallUserSignalProc( USIG_PROCESS_DESTROY, 0, 0 );
581 if (nTaskCount <= 1)
583 TRACE(task, "this is the last task, exiting\n" );
584 USER_ExitWindows();
587 /* FIXME: Hack! Send a message to the initial task so that
588 * the GetMessage wakes up and the initial task can check whether
589 * it is the only remaining one and terminate itself ...
590 * The initial task should probably install hooks or something
591 * to get informed about task termination :-/
593 Callout.PostAppMessage16( PROCESS_Initial()->task, WM_NULL, 0, 0 );
595 /* Remove the task from the list to be sure we never switch back to it */
596 TASK_UnlinkTask( hTask );
597 if( nTaskCount )
599 TDB* p = (TDB *)GlobalLock16( hFirstTask );
600 while( p )
602 if( p->hYieldTo == hTask ) p->hYieldTo = 0;
603 p = (TDB *)GlobalLock16( p->hNext );
607 pTask->nEvents = 0;
609 if ( hLockedTask == hTask )
610 hLockedTask = 0;
612 if ( hTaskToKill && ( hTaskToKill != hCurrentTask ) )
614 /* If another task is already marked for destruction, */
615 /* we can kill it now, as we are in another context. */
616 TASK_DeleteTask( hTaskToKill );
617 hTaskToKill = 0;
621 * If hTask is not the task currently scheduled by the Win16
622 * scheduler, we simply delete it; otherwise we mark it for
623 * destruction. Note that if the current task is a 32-bit
624 * one, hCurrentTask is *different* from GetCurrentTask()!
626 if ( hTask == hCurrentTask )
628 assert( hTaskToKill == 0 || hTaskToKill == hCurrentTask );
629 hTaskToKill = hCurrentTask;
631 else
632 TASK_DeleteTask( hTask );
634 SYSLEVEL_LeaveWin16Lock();
638 /***********************************************************************
639 * TASK_KillCurrentTask
641 * Kill the currently running task. As it's not possible to kill the
642 * current task like this, it is simply marked for destruction, and will
643 * be killed when either TASK_Reschedule or this function is called again
644 * in the context of another task.
646 void TASK_KillCurrentTask( INT16 exitCode )
648 if ( !THREAD_IsWin16( THREAD_Current() ) )
650 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
651 return;
654 assert(hCurrentTask == GetCurrentTask());
656 TRACE(task, "Killing current task %04x\n", hCurrentTask );
658 TASK_KillTask( 0 );
660 TASK_YieldToSystem();
662 /* We should never return from this Yield() */
664 ERR(task,"Return of the living dead %04x!!!\n", hCurrentTask);
665 exit(1);
668 /***********************************************************************
669 * TASK_Reschedule
671 * This is where all the magic of task-switching happens!
673 * Note: This function should only be called via the TASK_YieldToSystem()
674 * wrapper, to make sure that all the context is saved correctly.
676 * It must not call functions that may yield control.
678 BOOL TASK_Reschedule(void)
680 TDB *pOldTask = NULL, *pNewTask;
681 HTASK16 hTask = 0;
682 STACK16FRAME *newframe16;
683 BOOL pending = FALSE;
685 /* Get the initial task up and running */
686 if (!hCurrentTask && GetCurrentTask())
688 /* We need to remove one pair of stackframes (exept for Winelib) */
689 STACK16FRAME *oldframe16 = CURRENT_STACK16;
690 STACK32FRAME *oldframe32 = oldframe16->frame32;
691 STACK16FRAME *newframe16 = PTR_SEG_TO_LIN( oldframe32->frame16 );
692 STACK32FRAME *newframe32 = newframe16->frame32;
693 if (newframe32)
695 newframe16->entry_ip = oldframe16->entry_ip;
696 newframe16->entry_cs = oldframe16->entry_cs;
697 newframe16->ip = oldframe16->ip;
698 newframe16->cs = oldframe16->cs;
699 newframe32->ebp = oldframe32->ebp;
700 newframe32->restore_addr = oldframe32->restore_addr;
701 newframe32->codeselector = oldframe32->codeselector;
703 THREAD_Current()->cur_stack = oldframe32->frame16;
706 hCurrentTask = GetCurrentTask();
707 pNewTask = (TDB *)GlobalLock16( hCurrentTask );
708 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
709 return FALSE;
712 /* NOTE: As we are entered from 16-bit code, we hold the Win16Lock.
713 We hang onto it thoughout most of this routine, so that accesses
714 to global variables (most notably the task list) are protected. */
715 assert(hCurrentTask == GetCurrentTask());
717 TRACE(task, "entered with hTask %04x (pid %d)\n", hCurrentTask, getpid());
719 #ifdef CONFIG_IPC
720 /* FIXME: What about the Win16Lock ??? */
721 dde_reschedule();
722 #endif
723 /* First check if there's a task to kill */
725 if (hTaskToKill && (hTaskToKill != hCurrentTask))
727 TASK_DeleteTask( hTaskToKill );
728 hTaskToKill = 0;
731 /* Find a task to yield to */
733 pOldTask = (TDB *)GlobalLock16( hCurrentTask );
734 if (pOldTask && pOldTask->hYieldTo)
736 /* check for DirectedYield() */
738 hTask = pOldTask->hYieldTo;
739 pNewTask = (TDB *)GlobalLock16( hTask );
740 if( !pNewTask || !pNewTask->nEvents) hTask = 0;
741 pOldTask->hYieldTo = 0;
744 /* extract hardware events only! */
746 if (!hTask) pending = EVENT_WaitNetEvent( FALSE, TRUE );
748 while (!hTask)
750 /* Find a task that has an event pending */
752 hTask = hFirstTask;
753 while (hTask)
755 pNewTask = (TDB *)GlobalLock16( hTask );
757 TRACE(task, "\ttask = %04x, events = %i\n", hTask, pNewTask->nEvents);
759 if (pNewTask->nEvents) break;
760 hTask = pNewTask->hNext;
762 if (hLockedTask && (hTask != hLockedTask)) hTask = 0;
763 if (hTask) break;
765 /* If a non-hardware event is pending, return to TASK_YieldToSystem
766 temporarily to process it safely */
767 if (pending) return TRUE;
769 /* No task found, wait for some events to come in */
771 /* NOTE: We release the Win16Lock while waiting for events. This is to enable
772 Win32 threads to thunk down to 16-bit temporarily. Since Win16
773 tasks won't execute and Win32 threads are not allowed to enter
774 TASK_Reschedule anyway, there should be no re-entrancy problem ... */
776 SYSLEVEL_ReleaseWin16Lock();
777 pending = EVENT_WaitNetEvent( TRUE, TRUE );
778 SYSLEVEL_RestoreWin16Lock();
781 if (hTask == hCurrentTask)
783 /* Allow Win32 threads to thunk down even while a Win16 task is
784 in a tight PeekMessage() or Yield() loop ... */
785 SYSLEVEL_ReleaseWin16Lock();
786 SYSLEVEL_RestoreWin16Lock();
788 TRACE(task, "returning to the current task(%04x)\n", hTask );
789 return FALSE; /* Nothing to do */
791 pNewTask = (TDB *)GlobalLock16( hTask );
792 TRACE(task, "Switching to task %04x (%.8s)\n",
793 hTask, pNewTask->module_name );
795 /* Make the task the last in the linked list (round-robin scheduling) */
797 pNewTask->priority++;
798 TASK_UnlinkTask( hTask );
799 TASK_LinkTask( hTask );
800 pNewTask->priority--;
802 /* Finish initializing the new task stack if necessary */
804 newframe16 = THREAD_STACK16( pNewTask->thdb );
805 if (!newframe16->entry_cs)
807 STACK16FRAME *oldframe16 = CURRENT_STACK16;
808 STACK32FRAME *oldframe32 = oldframe16->frame32;
809 STACK32FRAME *newframe32 = newframe16->frame32;
810 newframe16->entry_ip = oldframe16->entry_ip;
811 newframe16->entry_cs = oldframe16->entry_cs;
812 newframe16->ip = oldframe16->ip;
813 newframe16->cs = oldframe16->cs;
814 newframe32->ebp = oldframe32->ebp;
815 newframe32->restore_addr = oldframe32->restore_addr;
816 newframe32->codeselector = oldframe32->codeselector;
819 /* Switch to the new stack */
821 /* NOTE: We need to release/restore the Win16Lock, as the task
822 switched to might be at another recursion level than
823 the old task ... */
825 SYSLEVEL_ReleaseWin16Lock();
827 hCurrentTask = hTask;
828 SET_CUR_THREAD( pNewTask->thdb );
829 pNewTask->ss_sp = pNewTask->thdb->cur_stack;
831 SYSLEVEL_RestoreWin16Lock();
833 return FALSE;
837 /***********************************************************************
838 * TASK_YieldToSystem
840 * Scheduler interface, this way we ensure that all "unsafe" events are
841 * processed outside the scheduler.
843 static void TASK_YieldToSystem( void )
845 if ( !THREAD_IsWin16( THREAD_Current() ) )
847 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
848 return;
851 if ( Callbacks->CallTaskRescheduleProc() )
853 /* NOTE: We get here only when no task has an event. This means also
854 the current task, so we shouldn't actually return to the
855 caller here. But, we need to do so, as the EVENT_WaitNetEvent
856 call could lead to a complex series of inter-task SendMessage
857 calls which might leave this task in a state where it again
858 has no event, but where its queue's wakeMask is also reset
859 to zero. Reentering TASK_Reschedule in this state would be
860 suicide. Hence, we do return to the caller after processing
861 non-hardware events. Actually, this should not hurt anyone,
862 as the caller must be WaitEvent, and thus the QUEUE_WaitBits
863 loop in USER. Should there actually be no message pending
864 for this task after processing non-hardware events, that loop
865 will simply return to WaitEvent. */
867 EVENT_WaitNetEvent( FALSE, FALSE );
872 /***********************************************************************
873 * InitTask (KERNEL.91)
875 * Called by the application startup code.
877 void WINAPI InitTask16( CONTEXT *context )
879 TDB *pTask;
880 NE_MODULE *pModule;
881 SEGTABLEENTRY *pSegTable;
882 INSTANCEDATA *pinstance;
883 LONG stacklow, stackhi;
885 if (context) EAX_reg(context) = 0;
886 if (!(pTask = (TDB *)GlobalLock16( GetCurrentTask() ))) return;
887 if (!(pModule = NE_GetPtr( pTask->hModule ))) return;
889 /* Initialize implicitly loaded DLLs */
890 NE_InitializeDLLs( pTask->hModule );
892 if (context)
894 /* Registers on return are:
895 * ax 1 if OK, 0 on error
896 * cx stack limit in bytes
897 * dx cmdShow parameter
898 * si instance handle of the previous instance
899 * di instance handle of the new task
900 * es:bx pointer to command-line inside PSP
902 * 0 (=%bp) is pushed on the stack
904 SEGPTR ptr = STACK16_PUSH( pTask->thdb, sizeof(WORD) );
905 *(WORD *)PTR_SEG_TO_LIN(ptr) = 0;
906 SP_reg(context) -= 2;
908 EAX_reg(context) = 1;
910 if (!pTask->pdb.cmdLine[0]) EBX_reg(context) = 0x80;
911 else
913 LPBYTE p = &pTask->pdb.cmdLine[1];
914 while ((*p == ' ') || (*p == '\t')) p++;
915 EBX_reg(context) = 0x80 + (p - pTask->pdb.cmdLine);
917 ECX_reg(context) = pModule->stack_size;
918 EDX_reg(context) = pTask->nCmdShow;
919 ESI_reg(context) = (DWORD)pTask->hPrevInstance;
920 EDI_reg(context) = (DWORD)pTask->hInstance;
921 ES_reg (context) = (WORD)pTask->hPDB;
924 /* Initialize the local heap */
925 if ( pModule->heap_size )
927 LocalInit16( pTask->hInstance, 0, pModule->heap_size );
930 /* Initialize the INSTANCEDATA structure */
931 pSegTable = NE_SEG_TABLE( pModule );
932 stacklow = pSegTable[pModule->ss - 1].minsize;
933 stackhi = stacklow + pModule->stack_size;
934 if (stackhi > 0xffff) stackhi = 0xffff;
935 pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
936 pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
937 pinstance->stacktop = stacklow;
938 pinstance->stackmin = OFFSETOF( pTask->thdb->cur_stack );
942 /***********************************************************************
943 * WaitEvent (KERNEL.30)
945 BOOL16 WINAPI WaitEvent16( HTASK16 hTask )
947 TDB *pTask;
949 if (!hTask) hTask = GetCurrentTask();
950 pTask = (TDB *)GlobalLock16( hTask );
952 if ( !THREAD_IsWin16( THREAD_Current() ) )
954 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
955 return TRUE;
958 if (pTask->nEvents > 0)
960 pTask->nEvents--;
961 return FALSE;
963 TASK_YieldToSystem();
965 /* When we get back here, we have an event */
967 if (pTask->nEvents > 0) pTask->nEvents--;
968 return TRUE;
972 /***********************************************************************
973 * PostEvent (KERNEL.31)
975 void WINAPI PostEvent16( HTASK16 hTask )
977 TDB *pTask;
979 if (!hTask) hTask = GetCurrentTask();
980 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
982 if ( !THREAD_IsWin16( pTask->thdb ) )
984 FIXME( task, "called for Win32 thread (%04x)!\n", pTask->thdb->teb_sel );
985 return;
988 pTask->nEvents++;
990 if ( !THREAD_IsWin16( THREAD_Current() ) )
992 /* wake-up the scheduler waiting in EVENT_WaitNetEvent */
993 EVENT_WakeUp();
998 /***********************************************************************
999 * SetPriority (KERNEL.32)
1001 void WINAPI SetPriority16( HTASK16 hTask, INT16 delta )
1003 TDB *pTask;
1004 INT16 newpriority;
1006 if (!hTask) hTask = GetCurrentTask();
1007 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
1008 newpriority = pTask->priority + delta;
1009 if (newpriority < -32) newpriority = -32;
1010 else if (newpriority > 15) newpriority = 15;
1012 pTask->priority = newpriority + 1;
1013 TASK_UnlinkTask( hTask );
1014 TASK_LinkTask( hTask );
1015 pTask->priority--;
1019 /***********************************************************************
1020 * LockCurrentTask (KERNEL.33)
1022 HTASK16 WINAPI LockCurrentTask16( BOOL16 bLock )
1024 if (bLock) hLockedTask = GetCurrentTask();
1025 else hLockedTask = 0;
1026 return hLockedTask;
1030 /***********************************************************************
1031 * IsTaskLocked (KERNEL.122)
1033 HTASK16 WINAPI IsTaskLocked16(void)
1035 return hLockedTask;
1039 /***********************************************************************
1040 * OldYield (KERNEL.117)
1042 void WINAPI OldYield16(void)
1044 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1046 if ( !THREAD_IsWin16( THREAD_Current() ) )
1048 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1049 return;
1052 if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */
1053 TASK_YieldToSystem();
1054 if (pCurTask) pCurTask->nEvents--;
1058 /***********************************************************************
1059 * DirectedYield (KERNEL.150)
1061 void WINAPI DirectedYield16( HTASK16 hTask )
1063 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1065 if ( !THREAD_IsWin16( THREAD_Current() ) )
1067 FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1068 return;
1071 TRACE(task, "%04x: DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1073 pCurTask->hYieldTo = hTask;
1074 OldYield16();
1076 TRACE(task, "%04x: back from DirectedYield(%04x)\n", pCurTask->hSelf, hTask );
1079 /***********************************************************************
1080 * Yield16 (KERNEL.29)
1082 void WINAPI Yield16(void)
1084 TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1086 if (pCurTask) pCurTask->hYieldTo = 0;
1087 if (pCurTask && pCurTask->hQueue) Callout.UserYield16();
1088 else OldYield16();
1091 /***********************************************************************
1092 * KERNEL_490 (KERNEL.490)
1094 HTASK16 WINAPI KERNEL_490( HTASK16 someTask )
1096 if ( !someTask ) return 0;
1098 FIXME( task, "(%04x): stub\n", someTask );
1099 return 0;
1102 /***********************************************************************
1103 * MakeProcInstance16 (KERNEL.51)
1105 FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
1107 BYTE *thunk,*lfunc;
1108 SEGPTR thunkaddr;
1110 if (!func) {
1111 ERR(task, "Ouch ! MakeProcInstance called with func == NULL !\n");
1112 return (FARPROC16)0; /* Windows seems to do the same */
1114 if (!hInstance) hInstance = CURRENT_DS;
1115 thunkaddr = TASK_AllocThunk( GetCurrentTask() );
1116 if (!thunkaddr) return (FARPROC16)0;
1117 thunk = PTR_SEG_TO_LIN( thunkaddr );
1118 lfunc = PTR_SEG_TO_LIN( func );
1120 TRACE(task, "(%08lx,%04x): got thunk %08lx\n",
1121 (DWORD)func, hInstance, (DWORD)thunkaddr );
1122 if (((lfunc[0]==0x8c) && (lfunc[1]==0xd8)) ||
1123 ((lfunc[0]==0x1e) && (lfunc[1]==0x58))
1125 FIXME(task,"thunk would be useless for %p, overwriting with nop;nop;\n", func );
1126 lfunc[0]=0x90; /* nop */
1127 lfunc[1]=0x90; /* nop */
1130 *thunk++ = 0xb8; /* movw instance, %ax */
1131 *thunk++ = (BYTE)(hInstance & 0xff);
1132 *thunk++ = (BYTE)(hInstance >> 8);
1133 *thunk++ = 0xea; /* ljmp func */
1134 *(DWORD *)thunk = (DWORD)func;
1135 return (FARPROC16)thunkaddr;
1139 /***********************************************************************
1140 * FreeProcInstance16 (KERNEL.52)
1142 void WINAPI FreeProcInstance16( FARPROC16 func )
1144 TRACE(task, "(%08lx)\n", (DWORD)func );
1145 TASK_FreeThunk( GetCurrentTask(), (SEGPTR)func );
1149 /**********************************************************************
1150 * GetCodeHandle (KERNEL.93)
1152 HANDLE16 WINAPI GetCodeHandle16( FARPROC16 proc )
1154 HANDLE16 handle;
1155 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1157 /* Return the code segment containing 'proc'. */
1158 /* Not sure if this is really correct (shouldn't matter that much). */
1160 /* Check if it is really a thunk */
1161 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1162 handle = GlobalHandle16( thunk[6] + (thunk[7] << 8) );
1163 else
1164 handle = GlobalHandle16( HIWORD(proc) );
1166 return handle;
1169 /**********************************************************************
1170 * GetCodeInfo (KERNEL.104)
1172 VOID WINAPI GetCodeInfo16( FARPROC16 proc, SEGINFO *segInfo )
1174 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1175 NE_MODULE *pModule = NULL;
1176 SEGTABLEENTRY *pSeg = NULL;
1177 WORD segNr;
1179 /* proc is either a thunk, or else a pair of module handle
1180 and segment number. In the first case, we also need to
1181 extract module and segment number. */
1183 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1185 WORD selector = thunk[6] + (thunk[7] << 8);
1186 pModule = NE_GetPtr( GlobalHandle16( selector ) );
1187 pSeg = pModule? NE_SEG_TABLE( pModule ) : NULL;
1189 if ( pModule )
1190 for ( segNr = 0; segNr < pModule->seg_count; segNr++, pSeg++ )
1191 if ( GlobalHandleToSel16(pSeg->hSeg) == selector )
1192 break;
1194 if ( pModule && segNr >= pModule->seg_count )
1195 pSeg = NULL;
1197 else
1199 pModule = NE_GetPtr( HIWORD( proc ) );
1200 segNr = LOWORD( proc );
1202 if ( pModule && segNr < pModule->seg_count )
1203 pSeg = NE_SEG_TABLE( pModule ) + segNr;
1206 /* fill in segment information */
1208 segInfo->offSegment = pSeg? pSeg->filepos : 0;
1209 segInfo->cbSegment = pSeg? pSeg->size : 0;
1210 segInfo->flags = pSeg? pSeg->flags : 0;
1211 segInfo->cbAlloc = pSeg? pSeg->minsize : 0;
1212 segInfo->h = pSeg? pSeg->hSeg : 0;
1213 segInfo->alignShift = pModule? pModule->alignment : 0;
1217 /**********************************************************************
1218 * DefineHandleTable16 (KERNEL.94)
1220 BOOL16 WINAPI DefineHandleTable16( WORD wOffset )
1222 return TRUE; /* FIXME */
1226 /***********************************************************************
1227 * SetTaskQueue (KERNEL.34)
1229 HQUEUE16 WINAPI SetTaskQueue16( HTASK16 hTask, HQUEUE16 hQueue )
1231 HQUEUE16 hPrev;
1232 TDB *pTask;
1234 if (!hTask) hTask = GetCurrentTask();
1235 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1237 hPrev = pTask->hQueue;
1238 pTask->hQueue = hQueue;
1240 TIMER_SwitchQueue( hPrev, 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;