4 * Copyright 1995 Alexandre Julliard
26 #include "selectors.h"
27 #include "stackframe.h"
36 /* Min. number of thunks allocated when creating a new segment */
39 extern INT32 WINSOCK_DeleteTaskWSI( TDB* pTask, struct _WSINFO* );
40 extern BOOL32 MODULE_FreeModule( HMODULE16 hModule, TDB* ptaskContext );
41 extern void PE_InitTls( PDB32 *pdb32 );
43 /* Saved 16-bit stack for current process (Win16 only) */
44 DWORD IF1632_Saved16_ss_sp = 0;
46 /* Pointer to function to switch to a larger stack */
47 int (*IF1632_CallLargeStack)( int (*func)(), void *arg ) = NULL;
49 static HTASK16 hFirstTask = 0;
50 static HTASK16 hCurrentTask = 0;
51 static HTASK16 hTaskToKill = 0;
52 static HTASK16 hLockedTask = 0;
53 static UINT16 nTaskCount = 0;
54 static HGLOBAL16 hDOSEnvironment = 0;
56 static HGLOBAL16 TASK_CreateDOSEnvironment(void);
57 static void TASK_YieldToSystem(TDB*);
59 static THDB TASK_SystemTHDB;
60 /***********************************************************************
63 BOOL32 TASK_Init(void)
65 if (!(hDOSEnvironment = TASK_CreateDOSEnvironment()))
66 fprintf( stderr, "Not enough memory for DOS Environment\n" );
67 TASK_SystemTHDB.teb_sel = SELECTOR_AllocBlock( &TASK_SystemTHDB, 0x1000, SEGMENT_DATA, TRUE, FALSE );
68 SET_FS( TASK_SystemTHDB.teb_sel );
69 return (hDOSEnvironment != 0);
73 /***********************************************************************
76 HTASK16 TASK_GetNextTask( HTASK16 hTask )
78 TDB* pTask = (TDB*)GlobalLock16(hTask);
80 if (pTask->hNext) return pTask->hNext;
81 return (hFirstTask != hTask) ? hFirstTask : 0;
85 /***********************************************************************
86 * TASK_CreateDOSEnvironment
88 * Create the original DOS environment.
90 static HGLOBAL16 TASK_CreateDOSEnvironment(void)
92 static const char program_name[] = "KRNL386.EXE";
94 int initial_size, size, i, winpathlen, sysdirlen;
97 extern char **environ;
99 /* DOS environment format:
107 * ASCIIZ program name (e.g. C:\WINDOWS\SYSTEM\KRNL386.EXE)
110 /* First compute the size of the fixed part of the environment */
112 for (i = winpathlen = 0; ; i++)
114 int len = DIR_GetDosPath( i, NULL, 0 );
116 winpathlen += len + 1;
118 if (!winpathlen) winpathlen = 1;
119 sysdirlen = GetSystemDirectory32A( NULL, 0 ) + 1;
120 initial_size = 5 + winpathlen + /* PATH=xxxx */
121 1 + /* BYTE 0 at end */
122 sizeof(WORD) + /* WORD 1 */
123 sysdirlen + /* program directory */
124 strlen(program_name) + 1; /* program name */
126 /* Compute the total size of the Unix environment (except path) */
128 for (e = environ, size = initial_size; *e; e++)
130 if (lstrncmpi32A(*e, "path=", 5))
132 int len = strlen(*e) + 1;
133 if (size + len >= 32767)
135 fprintf( stderr, "Warning: environment larger than 32k.\n" );
143 /* Now allocate the environment */
145 if (!(handle = GlobalAlloc16( GMEM_FIXED, size ))) return 0;
146 p = (char *)GlobalLock16( handle );
148 /* And fill it with the Unix environment */
150 for (e = environ, size = initial_size; *e; e++)
152 if (lstrncmpi32A(*e, "path=", 5))
154 int len = strlen(*e) + 1;
155 if (size + len >= 32767) break;
162 /* Now add the path */
164 strcpy( p, "PATH=" );
165 for (i = 0, p += 5; ; i++)
167 if (!DIR_GetDosPath( i, p, winpathlen )) break;
171 if (p[-1] == ';') p[-1] = '\0';
174 /* Now add the program name */
179 GetSystemDirectory32A( p, sysdirlen );
181 strcat( p, program_name );
185 p = (char *) GlobalLock16( handle );
186 dprintf_task(stddeb, "Master DOS environment at %p\n", p);
187 for (; *p; p += strlen(p) + 1) dprintf_task(stddeb, " %s\n", p);
188 dprintf_task( stddeb, "Progname: %s\n", p+3 );
194 /***********************************************************************
197 static void TASK_LinkTask( HTASK16 hTask )
202 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
203 prevTask = &hFirstTask;
206 TDB *prevTaskPtr = (TDB *)GlobalLock16( *prevTask );
207 if (prevTaskPtr->priority >= pTask->priority) break;
208 prevTask = &prevTaskPtr->hNext;
210 pTask->hNext = *prevTask;
216 /***********************************************************************
219 static void TASK_UnlinkTask( HTASK16 hTask )
224 prevTask = &hFirstTask;
225 while (*prevTask && (*prevTask != hTask))
227 pTask = (TDB *)GlobalLock16( *prevTask );
228 prevTask = &pTask->hNext;
232 pTask = (TDB *)GlobalLock16( *prevTask );
233 *prevTask = pTask->hNext;
240 /***********************************************************************
243 * Create a thunk free-list in segment 'handle', starting from offset 'offset'
244 * and containing 'count' entries.
246 static void TASK_CreateThunks( HGLOBAL16 handle, WORD offset, WORD count )
252 pThunk = (THUNKS *)((BYTE *)GlobalLock16( handle ) + offset);
254 pThunk->magic = THUNK_MAGIC;
255 pThunk->free = (int)&pThunk->thunks - (int)pThunk;
257 for (i = 0; i < count-1; i++)
259 free += 8; /* Offset of next thunk */
260 pThunk->thunks[4*i] = free;
262 pThunk->thunks[4*i] = 0; /* Last thunk */
266 /***********************************************************************
269 * Allocate a thunk for MakeProcInstance().
271 static SEGPTR TASK_AllocThunk( HTASK16 hTask )
277 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
278 sel = pTask->hCSAlias;
279 pThunk = &pTask->thunks;
280 base = (int)pThunk - (int)pTask;
281 while (!pThunk->free)
284 if (!sel) /* Allocate a new segment */
286 sel = GLOBAL_Alloc( GMEM_FIXED, sizeof(THUNKS) + (MIN_THUNKS-1)*8,
287 pTask->hPDB, TRUE, FALSE, FALSE );
288 if (!sel) return (SEGPTR)0;
289 TASK_CreateThunks( sel, 0, MIN_THUNKS );
292 pThunk = (THUNKS *)GlobalLock16( sel );
295 base += pThunk->free;
296 pThunk->free = *(WORD *)((BYTE *)pThunk + pThunk->free);
297 return PTR_SEG_OFF_TO_SEGPTR( sel, base );
301 /***********************************************************************
304 * Free a MakeProcInstance() thunk.
306 static BOOL32 TASK_FreeThunk( HTASK16 hTask, SEGPTR thunk )
312 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
313 sel = pTask->hCSAlias;
314 pThunk = &pTask->thunks;
315 base = (int)pThunk - (int)pTask;
316 while (sel && (sel != HIWORD(thunk)))
319 pThunk = (THUNKS *)GlobalLock16( sel );
322 if (!sel) return FALSE;
323 *(WORD *)((BYTE *)pThunk + LOWORD(thunk) - base) = pThunk->free;
324 pThunk->free = LOWORD(thunk) - base;
329 /***********************************************************************
332 * 32-bit entry point for a new task. This function is responsible for
333 * setting up the registers and jumping to the 16-bit entry point.
335 static void TASK_CallToStart(void)
338 TDB *pTask = (TDB *)GlobalLock16( hCurrentTask );
339 NE_MODULE *pModule = MODULE_GetPtr( pTask->hModule );
340 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
342 IF1632_Saved16_ss_sp = pTask->ss_sp;
343 SET_FS( pCurrentThread->teb_sel );
344 if (pModule->flags & NE_FFLAGS_WIN32)
346 /* FIXME: all this is an ugly hack */
348 extern void InitTask( CONTEXT *context );
350 FARPROC32 entry = (FARPROC32)RVA_PTR( pCurrentProcess->exe_modref->module, OptionalHeader.AddressOfEntryPoint );
353 InitApp( pTask->hModule );
354 PE_InitializeDLLs( pCurrentProcess, DLL_PROCESS_ATTACH, (LPVOID)-1 );
355 dprintf_relay( stddeb, "CallTo32(entryproc=%p)\n", entry );
357 TASK_KillCurrentTask( exit_code );
361 /* Registers at initialization must be:
363 * bx stack size in bytes
364 * cx heap size in bytes
365 * si previous app instance
366 * di current app instance
368 * es selector to the PSP
369 * ds dgroup of the application
371 * sp top of the stack
375 memset( &context, 0, sizeof(context) );
376 CS_reg(&context) = pSegTable[pModule->cs - 1].selector;
377 DS_reg(&context) = pSegTable[pModule->dgroup - 1].selector;
378 ES_reg(&context) = pTask->hPDB;
379 EIP_reg(&context) = pModule->ip;
380 EBX_reg(&context) = pModule->stack_size;
381 ECX_reg(&context) = pModule->heap_size;
382 EDI_reg(&context) = context.SegDs;
384 dprintf_task( stddeb, "Starting main program: cs:ip=%04lx:%04x ds=%04lx ss:sp=%04x:%04x\n",
385 CS_reg(&context), IP_reg(&context), DS_reg(&context),
386 SELECTOROF(IF1632_Saved16_ss_sp),
387 OFFSETOF(IF1632_Saved16_ss_sp) );
389 Callbacks->CallRegisterProc( &context, 0 );
390 /* This should never return */
391 fprintf( stderr, "TASK_CallToStart: Main program returned!\n" );
392 TASK_KillCurrentTask( 1 );
397 /***********************************************************************
400 HTASK16 TASK_CreateTask( HMODULE16 hModule, HINSTANCE16 hInstance,
401 HINSTANCE16 hPrevInstance, HANDLE16 hEnvironment,
402 LPCSTR cmdLine, UINT16 cmdShow )
407 HGLOBAL16 hParentEnv;
409 SEGTABLEENTRY *pSegTable;
413 STACK16FRAME *frame16;
414 STACK32FRAME *frame32;
416 if (!(pModule = MODULE_GetPtr( hModule ))) return 0;
417 pSegTable = NE_SEG_TABLE( pModule );
419 /* Allocate the task structure */
421 hTask = GLOBAL_Alloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(TDB),
422 hModule, FALSE, FALSE, FALSE );
423 if (!hTask) return 0;
424 pTask = (TDB *)GlobalLock16( hTask );
426 /* Allocate the new environment block */
428 if (!(hParentEnv = hEnvironment))
430 TDB *pParent = (TDB *)GlobalLock16( hCurrentTask );
431 hParentEnv = pParent ? pParent->pdb.environment : hDOSEnvironment;
433 /* FIXME: do we really need to make a copy also when */
434 /* we don't use the parent environment? */
435 if (!(hEnvironment = GlobalAlloc16( GMEM_FIXED, GlobalSize16(hParentEnv))))
437 GlobalFree16( hTask );
440 memcpy( GlobalLock16( hEnvironment ), GlobalLock16( hParentEnv ),
441 GlobalSize16( hParentEnv ) );
443 /* Fill the task structure */
445 pTask->nEvents = 1; /* So the task can be started */
446 pTask->hSelf = hTask;
449 if (pModule->flags & NE_FFLAGS_WIN32)
450 pTask->flags |= TDBF_WIN32;
452 pTask->version = pModule->expected_version;
453 pTask->hInstance = hInstance;
454 pTask->hPrevInstance = hPrevInstance;
455 pTask->hModule = hModule;
456 pTask->hParent = hCurrentTask;
457 pTask->magic = TDB_MAGIC;
458 pTask->nCmdShow = cmdShow;
459 pTask->curdrive = DRIVE_GetCurrentDrive() | 0x80;
460 strcpy( pTask->curdir, "\\" );
461 lstrcpyn32A( pTask->curdir + 1, DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() ),
462 sizeof(pTask->curdir) - 1 );
464 /* Create the thunks block */
466 TASK_CreateThunks( hTask, (int)&pTask->thunks - (int)pTask, 7 );
468 /* Copy the module name */
470 name = MODULE_GetModuleName( hModule );
471 strncpy( pTask->module_name, name, sizeof(pTask->module_name) );
473 /* Allocate a selector for the PDB */
475 pTask->hPDB = GLOBAL_CreateBlock( GMEM_FIXED, &pTask->pdb, sizeof(PDB),
476 hModule, FALSE, FALSE, FALSE, NULL );
480 pTask->pdb.int20 = 0x20cd;
481 pTask->pdb.dispatcher[0] = 0x9a; /* ljmp */
482 PUT_DWORD(&pTask->pdb.dispatcher[1], (DWORD)MODULE_GetEntryPoint(
483 GetModuleHandle16("KERNEL"), 102 )); /* KERNEL.102 is DOS3Call() */
484 pTask->pdb.savedint22 = INT_GetHandler( 0x22 );
485 pTask->pdb.savedint23 = INT_GetHandler( 0x23 );
486 pTask->pdb.savedint24 = INT_GetHandler( 0x24 );
487 pTask->pdb.fileHandlesPtr =
488 PTR_SEG_OFF_TO_SEGPTR( GlobalHandleToSel(pTask->hPDB),
489 (int)&((PDB *)0)->fileHandles );
490 pTask->pdb.hFileHandles = 0;
491 memset( pTask->pdb.fileHandles, 0xff, sizeof(pTask->pdb.fileHandles) );
492 pTask->pdb.environment = hEnvironment;
493 pTask->pdb.nbFiles = 20;
494 lstrcpyn32A( pTask->pdb.cmdLine, cmdLine, 127 );
496 /* Get the compatibility flags */
498 pTask->compat_flags = GetProfileInt32A( "Compatibility", name, 0 );
500 /* Allocate a code segment alias for the TDB */
502 pTask->hCSAlias = GLOBAL_CreateBlock( GMEM_FIXED, (void *)pTask,
503 sizeof(TDB), pTask->hPDB, TRUE,
504 FALSE, FALSE, NULL );
506 /* Set the owner of the environment block */
508 FarSetOwner( pTask->pdb.environment, pTask->hPDB );
510 /* Default DTA overwrites command-line */
512 pTask->dta = PTR_SEG_OFF_TO_SEGPTR( pTask->hPDB,
513 (int)&pTask->pdb.cmdLine - (int)&pTask->pdb );
515 /* Create the Win32 part of the task */
517 pCurrentProcess = pdb32 = PROCESS_Create( pTask, cmdLine );
518 /* FIXME: check for pdb32 == NULL. */
520 if (pModule->flags & NE_FFLAGS_WIN32)
523 LPTHREAD_START_ROUTINE start =
524 (LPTHREAD_START_ROUTINE)(
525 pCurrentProcess->exe_modref->load_addr +
526 pCurrentProcess->exe_modref->pe_module->pe_header->OptionalHeader.AddressOfEntryPoint);
528 pTask->thdb = THREAD_Create( pdb32,
529 PE_HEADER(pModule->module32)->OptionalHeader.SizeOfStackReserve, 0 );
530 /* FIXME: should not be done here */
531 pCurrentThread = pTask->thdb;
535 pTask->thdb = THREAD_Create( pdb32, 0, NULL );
537 /* FIXME: check for pTask->thdb == NULL. */
539 /* Create the 32-bit stack frame */
541 stack32Top = (char*)pTask->thdb->teb.stack_top;
542 frame32 = (STACK32FRAME *)stack32Top - 1;
548 frame32->retaddr = (DWORD)TASK_CallToStart;
549 /* The remaining fields will be initialized in TASK_Reschedule */
551 /* Create the 16-bit stack frame */
553 if (!(sp = pModule->sp))
554 sp = pSegTable[pModule->ss-1].minsize + pModule->stack_size;
556 pTask->ss_sp = PTR_SEG_OFF_TO_SEGPTR( hInstance, sp );
557 pTask->ss_sp -= sizeof(STACK16FRAME) + sizeof(DWORD) /* for saved %esp */;
558 frame16 = (STACK16FRAME *)PTR_SEG_TO_LIN( pTask->ss_sp );
559 frame16->saved_ss_sp = 0;
560 frame16->ebp = sp + (int)&((STACK16FRAME *)0)->bp;
561 frame16->bp = LOWORD(frame16->ebp);
562 frame16->ds = frame16->es = pTask->hInstance;
563 frame16->entry_point = 0;
564 frame16->entry_cs = 0;
565 /* The remaining fields will be initialized in TASK_Reschedule */
567 *(STACK32FRAME **)(frame16 + 1) = frame32; /* Store the 32-bit %esp */
569 /* If there's no 16-bit stack yet, use a part of the new task stack */
570 /* This is only needed to have a stack to switch from on the first */
571 /* call to DirectedYield(). */
573 if (!IF1632_Saved16_ss_sp) IF1632_Saved16_ss_sp = pTask->ss_sp;
575 /* Add the task to the linked list */
577 TASK_LinkTask( hTask );
579 dprintf_task( stddeb, "CreateTask: module='%s' cmdline='%s' task=%04x\n",
580 name, cmdLine, hTask );
586 /***********************************************************************
589 static void TASK_DeleteTask( HTASK16 hTask )
594 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
597 /* Delete the Win32 part of the task */
599 PROCESS_Destroy( &pTask->thdb->process->header );
600 THREAD_Destroy( &pTask->thdb->header );
602 /* Free the task module */
604 MODULE_FreeModule( pTask->hModule, pTask );
606 /* Free the selector aliases */
608 GLOBAL_FreeBlock( pTask->hCSAlias );
609 GLOBAL_FreeBlock( pTask->hPDB );
611 /* Free the task structure itself */
613 GlobalFree16( hTask );
615 /* Free all memory used by this task (including the 32-bit stack, */
616 /* the environment block and the thunk segments). */
618 GlobalFreeAll( hPDB );
622 /***********************************************************************
623 * TASK_KillCurrentTask
625 * Kill the currently running task. As it's not possible to kill the
626 * current task like this, it is simply marked for destruction, and will
627 * be killed when either TASK_Reschedule or this function is called again
628 * in the context of another task.
630 void TASK_KillCurrentTask( INT16 exitCode )
632 extern void USER_ExitWindows(void);
634 TDB* pTask = (TDB*) GlobalLock16( hCurrentTask );
635 if (!pTask) USER_ExitWindows(); /* No current task yet */
637 dprintf_task(stddeb, "Killing task %04x\n", hCurrentTask );
639 /* Delete active sockets */
642 WINSOCK_DeleteTaskWSI( pTask, pTask->pwsi );
644 /* Perform USER cleanup */
646 if (pTask->userhandler)
647 pTask->userhandler( hCurrentTask, USIG_TERMINATION, 0,
648 pTask->hInstance, pTask->hQueue );
650 if (hTaskToKill && (hTaskToKill != hCurrentTask))
652 /* If another task is already marked for destruction, */
653 /* we can kill it now, as we are in another context. */
654 TASK_DeleteTask( hTaskToKill );
659 dprintf_task( stddeb, "\nthis is the last task, exiting\n" );
663 /* Remove the task from the list to be sure we never switch back to it */
664 TASK_UnlinkTask( hCurrentTask );
667 TDB* p = (TDB *)GlobalLock16( hFirstTask );
670 if( p->hYieldTo == hCurrentTask ) p->hYieldTo = 0;
671 p = (TDB *)GlobalLock16( p->hNext );
675 hTaskToKill = hCurrentTask;
679 TASK_YieldToSystem(pTask);
681 /* We should never return from this Yield() */
683 fprintf(stderr,"Return of the living dead %04x!!!\n", hCurrentTask);
687 /***********************************************************************
690 * This is where all the magic of task-switching happens!
692 * Note: This function should only be called via the TASK_YieldToSystem()
693 * wrapper, to make sure that all the context is saved correctly.
695 * It must not call functions that may yield control.
697 void TASK_Reschedule(void)
699 TDB *pOldTask = NULL, *pNewTask;
701 STACK16FRAME *newframe16;
706 /* First check if there's a task to kill */
708 if (hTaskToKill && (hTaskToKill != hCurrentTask))
710 TASK_DeleteTask( hTaskToKill );
714 /* Find a task to yield to */
716 pOldTask = (TDB *)GlobalLock16( hCurrentTask );
717 if (pOldTask && pOldTask->hYieldTo)
719 /* check for DirectedYield() */
721 hTask = pOldTask->hYieldTo;
722 pNewTask = (TDB *)GlobalLock16( hTask );
723 if( !pNewTask || !pNewTask->nEvents) hTask = 0;
724 pOldTask->hYieldTo = 0;
727 /* extract hardware events only! */
729 if (!hTask) EVENT_WaitNetEvent( FALSE, TRUE );
733 /* Find a task that has an event pending */
738 pNewTask = (TDB *)GlobalLock16( hTask );
740 dprintf_task( stddeb, "\ttask = %04x, events = %i\n", hTask, pNewTask->nEvents);
742 if (pNewTask->nEvents) break;
743 hTask = pNewTask->hNext;
745 if (hLockedTask && (hTask != hLockedTask)) hTask = 0;
748 /* No task found, wait for some events to come in */
750 EVENT_WaitNetEvent( TRUE, TRUE );
753 if (hTask == hCurrentTask)
755 dprintf_task( stddeb, "returning to the current task(%04x)\n", hTask );
756 return; /* Nothing to do */
758 pNewTask = (TDB *)GlobalLock16( hTask );
759 dprintf_task( stddeb, "Switching to task %04x (%.8s)\n",
760 hTask, pNewTask->module_name );
762 /* Save the stack of the previous task (if any) */
764 if (pOldTask) pOldTask->ss_sp = IF1632_Saved16_ss_sp;
766 /* Make the task the last in the linked list (round-robin scheduling) */
768 pNewTask->priority++;
769 TASK_UnlinkTask( hTask );
770 TASK_LinkTask( hTask );
771 pNewTask->priority--;
773 /* Finish initializing the new task stack if necessary */
775 newframe16 = (STACK16FRAME *)PTR_SEG_TO_LIN( pNewTask->ss_sp );
776 if (!newframe16->entry_cs)
778 STACK16FRAME *oldframe16 = CURRENT_STACK16;
779 STACK32FRAME *oldframe32 = *(STACK32FRAME **)(oldframe16 + 1);
780 STACK32FRAME *newframe32 = *(STACK32FRAME **)(newframe16 + 1);
781 newframe16->entry_ip = oldframe16->entry_ip;
782 newframe16->entry_cs = oldframe16->entry_cs;
783 newframe16->ip = oldframe16->ip;
784 newframe16->cs = oldframe16->cs;
785 newframe32->ebp = oldframe32->ebp;
786 newframe32->restore_addr = oldframe32->restore_addr;
787 newframe32->codeselector = oldframe32->codeselector;
790 /* Switch to the new stack */
792 hCurrentTask = hTask;
793 pCurrentThread = pNewTask->thdb;
794 pCurrentProcess = pCurrentThread->process;
795 IF1632_Saved16_ss_sp = pNewTask->ss_sp;
799 /***********************************************************************
802 * Scheduler interface, this way we ensure that all "unsafe" events are
803 * processed outside the scheduler.
805 void TASK_YieldToSystem(TDB* pTask)
809 Callbacks->CallTaskRescheduleProc();
813 pQ = (MESSAGEQUEUE*)GlobalLock16(pTask->hQueue);
814 if( pQ && pQ->flags & QUEUE_FLAG_XEVENT &&
815 !(pQ->wakeBits & (QS_SENDMESSAGE | QS_SMRESULT)) )
817 pQ->flags &= ~QUEUE_FLAG_XEVENT;
818 EVENT_WaitNetEvent( FALSE, FALSE );
824 /***********************************************************************
825 * InitTask (KERNEL.91)
827 * Called by the application startup code.
829 void WINAPI InitTask( CONTEXT *context )
833 SEGTABLEENTRY *pSegTable;
834 INSTANCEDATA *pinstance;
835 LONG stacklow, stackhi;
837 if (context) EAX_reg(context) = 0;
838 if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return;
839 if (!(pModule = MODULE_GetPtr( pTask->hModule ))) return;
841 /* This is a hack to install task USER signal handler before
842 * implicitly loaded DLLs are initialized (see windows/user.c) */
844 pTask->userhandler = (USERSIGNALPROC)&USER_SignalProc;
846 /* Initialize implicitly loaded DLLs */
847 NE_InitializeDLLs( pTask->hModule );
851 /* Registers on return are:
852 * ax 1 if OK, 0 on error
853 * cx stack limit in bytes
854 * dx cmdShow parameter
855 * si instance handle of the previous instance
856 * di instance handle of the new task
857 * es:bx pointer to command-line inside PSP
859 EAX_reg(context) = 1;
860 EBX_reg(context) = 0x81;
861 ECX_reg(context) = pModule->stack_size;
862 EDX_reg(context) = pTask->nCmdShow;
863 ESI_reg(context) = (DWORD)pTask->hPrevInstance;
864 EDI_reg(context) = (DWORD)pTask->hInstance;
865 ES_reg (context) = (WORD)pTask->hPDB;
868 /* Initialize the local heap */
869 if ( pModule->heap_size )
871 LocalInit( pTask->hInstance, 0, pModule->heap_size );
874 /* Initialize the INSTANCEDATA structure */
875 pSegTable = NE_SEG_TABLE( pModule );
876 stacklow = pSegTable[pModule->ss - 1].minsize;
877 stackhi = stacklow + pModule->stack_size;
878 if (stackhi > 0xffff) stackhi = 0xffff;
879 pinstance = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN(CURRENT_DS, 0);
880 pinstance->stackbottom = stackhi; /* yup, that's right. Confused me too. */
881 pinstance->stacktop = stacklow;
882 pinstance->stackmin = OFFSETOF(IF1632_Saved16_ss_sp);
886 /***********************************************************************
887 * WaitEvent (KERNEL.30)
889 BOOL16 WINAPI WaitEvent( HTASK16 hTask )
893 if (!hTask) hTask = hCurrentTask;
894 pTask = (TDB *)GlobalLock16( hTask );
895 if (pTask->nEvents > 0)
900 TASK_YieldToSystem(pTask);
902 /* When we get back here, we have an event */
904 if (pTask->nEvents > 0) pTask->nEvents--;
909 /***********************************************************************
910 * PostEvent (KERNEL.31)
912 void WINAPI PostEvent( HTASK16 hTask )
916 if (!hTask) hTask = hCurrentTask;
917 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
922 /***********************************************************************
923 * SetPriority (KERNEL.32)
925 void WINAPI SetPriority( HTASK16 hTask, INT16 delta )
930 if (!hTask) hTask = hCurrentTask;
931 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return;
932 newpriority = pTask->priority + delta;
933 if (newpriority < -32) newpriority = -32;
934 else if (newpriority > 15) newpriority = 15;
936 pTask->priority = newpriority + 1;
937 TASK_UnlinkTask( hTask );
938 TASK_LinkTask( hTask );
943 /***********************************************************************
944 * LockCurrentTask (KERNEL.33)
946 HTASK16 WINAPI LockCurrentTask( BOOL16 bLock )
948 if (bLock) hLockedTask = hCurrentTask;
949 else hLockedTask = 0;
954 /***********************************************************************
955 * IsTaskLocked (KERNEL.122)
957 HTASK16 WINAPI IsTaskLocked(void)
963 /***********************************************************************
964 * OldYield (KERNEL.117)
966 void WINAPI OldYield(void)
970 pCurTask = (TDB *)GlobalLock16( hCurrentTask );
971 if (pCurTask) pCurTask->nEvents++; /* Make sure we get back here */
972 TASK_YieldToSystem(pCurTask);
973 if (pCurTask) pCurTask->nEvents--;
977 /***********************************************************************
978 * DirectedYield (KERNEL.150)
980 void WINAPI DirectedYield( HTASK16 hTask )
982 TDB *pCurTask = (TDB *)GlobalLock16( hCurrentTask );
983 pCurTask->hYieldTo = hTask;
988 /***********************************************************************
989 * UserYield (USER.332)
991 void WINAPI UserYield(void)
993 TDB *pCurTask = (TDB *)GlobalLock16( hCurrentTask );
994 MESSAGEQUEUE *queue = (MESSAGEQUEUE *)GlobalLock16( pCurTask->hQueue );
995 /* Handle sent messages */
996 while (queue && (queue->wakeBits & QS_SENDMESSAGE))
997 QUEUE_ReceiveMessage( queue );
1001 queue = (MESSAGEQUEUE *)GlobalLock16( pCurTask->hQueue );
1002 while (queue && (queue->wakeBits & QS_SENDMESSAGE))
1003 QUEUE_ReceiveMessage( queue );
1007 /***********************************************************************
1008 * Yield16 (KERNEL.29)
1010 void WINAPI Yield16(void)
1012 TDB *pCurTask = (TDB *)GlobalLock16( hCurrentTask );
1013 if (pCurTask) pCurTask->hYieldTo = 0;
1014 if (pCurTask && pCurTask->hQueue) UserYield();
1019 /***********************************************************************
1020 * MakeProcInstance16 (KERNEL.51)
1022 FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
1027 if (__winelib) return func; /* func can be called directly in Winelib */
1028 thunkaddr = TASK_AllocThunk( hCurrentTask );
1029 if (!thunkaddr) return (FARPROC16)0;
1030 thunk = PTR_SEG_TO_LIN( thunkaddr );
1031 lfunc = PTR_SEG_TO_LIN( func );
1033 dprintf_task( stddeb, "MakeProcInstance(%08lx,%04x): got thunk %08lx\n",
1034 (DWORD)func, hInstance, (DWORD)thunkaddr );
1035 if (((lfunc[0]==0x8c) && (lfunc[1]==0xd8)) ||
1036 ((lfunc[0]==0x1e) && (lfunc[1]==0x58))
1038 fprintf(stderr,"FIXME: MakeProcInstance16 thunk would be useless for %p, overwriting with nop;nop;\n", func );
1039 lfunc[0]=0x90; /* nop */
1040 lfunc[1]=0x90; /* nop */
1043 *thunk++ = 0xb8; /* movw instance, %ax */
1044 *thunk++ = (BYTE)(hInstance & 0xff);
1045 *thunk++ = (BYTE)(hInstance >> 8);
1046 *thunk++ = 0xea; /* ljmp func */
1047 *(DWORD *)thunk = (DWORD)func;
1048 return (FARPROC16)thunkaddr;
1052 /***********************************************************************
1053 * FreeProcInstance16 (KERNEL.52)
1055 void WINAPI FreeProcInstance16( FARPROC16 func )
1057 dprintf_task( stddeb, "FreeProcInstance(%08lx)\n", (DWORD)func );
1058 if (!__winelib) TASK_FreeThunk( hCurrentTask, (SEGPTR)func );
1062 /**********************************************************************
1063 * GetCodeHandle (KERNEL.93)
1065 HANDLE16 WINAPI GetCodeHandle( FARPROC16 proc )
1068 BYTE *thunk = (BYTE *)PTR_SEG_TO_LIN( proc );
1070 if (__winelib) return 0;
1072 /* Return the code segment containing 'proc'. */
1073 /* Not sure if this is really correct (shouldn't matter that much). */
1075 /* Check if it is really a thunk */
1076 if ((thunk[0] == 0xb8) && (thunk[3] == 0xea))
1077 handle = GlobalHandle16( thunk[6] + (thunk[7] << 8) );
1079 handle = GlobalHandle16( HIWORD(proc) );
1085 /**********************************************************************
1086 * DefineHandleTable16 (KERNEL.94)
1088 BOOL16 WINAPI DefineHandleTable16( WORD wOffset )
1090 return TRUE; /* FIXME */
1094 /***********************************************************************
1095 * SetTaskQueue (KERNEL.34)
1097 HQUEUE16 WINAPI SetTaskQueue( HTASK16 hTask, HQUEUE16 hQueue )
1102 if (!hTask) hTask = hCurrentTask;
1103 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1105 hPrev = pTask->hQueue;
1106 pTask->hQueue = hQueue;
1108 TIMER_SwitchQueue( hPrev, hQueue );
1114 /***********************************************************************
1115 * GetTaskQueue (KERNEL.35)
1117 HQUEUE16 WINAPI GetTaskQueue( HTASK16 hTask )
1121 if (!hTask) hTask = hCurrentTask;
1122 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return 0;
1123 return pTask->hQueue;
1127 /***********************************************************************
1128 * SwitchStackTo (KERNEL.108)
1130 void WINAPI SwitchStackTo( WORD seg, WORD ptr, WORD top )
1133 STACK16FRAME *oldFrame, *newFrame;
1134 INSTANCEDATA *pData;
1137 if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return;
1138 if (!(pData = (INSTANCEDATA *)GlobalLock16( seg ))) return;
1139 dprintf_task( stddeb, "SwitchStackTo: old=%04x:%04x new=%04x:%04x\n",
1140 SELECTOROF(IF1632_Saved16_ss_sp),
1141 OFFSETOF(IF1632_Saved16_ss_sp), seg, ptr );
1143 /* Save the old stack */
1145 oldFrame = CURRENT_STACK16;
1146 pData->old_ss_sp = IF1632_Saved16_ss_sp;
1147 pData->stacktop = top;
1148 pData->stackmin = ptr;
1149 pData->stackbottom = ptr;
1151 /* Switch to the new stack */
1153 /* Note: we need to take the 3 arguments into account; otherwise,
1154 * the stack will underflow upon return from this function.
1156 IF1632_Saved16_ss_sp = PTR_SEG_OFF_TO_SEGPTR( seg,
1157 ptr - sizeof(STACK16FRAME) - 3 * sizeof(WORD) );
1158 newFrame = CURRENT_STACK16;
1160 /* Copy the stack frame and the local variables to the new stack */
1162 copySize = oldFrame->bp - OFFSETOF(pData->old_ss_sp);
1163 memmove( newFrame, oldFrame, MAX( copySize, sizeof(STACK16FRAME) ));
1167 /***********************************************************************
1168 * SwitchStackBack (KERNEL.109)
1170 * Note: the function is declared as 'register' in the spec file in order
1171 * to make sure all registers are preserved, but we don't use them in any
1172 * way, so we don't need a CONTEXT* argument.
1174 void WINAPI SwitchStackBack(void)
1177 STACK16FRAME *oldFrame, *newFrame;
1178 INSTANCEDATA *pData;
1180 if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return;
1181 if (!(pData = (INSTANCEDATA *)GlobalLock16(SELECTOROF(IF1632_Saved16_ss_sp))))
1183 if (!pData->old_ss_sp)
1185 fprintf( stderr, "SwitchStackBack: no previous SwitchStackTo\n" );
1188 dprintf_task( stddeb, "SwitchStackBack: restoring stack %04x:%04x\n",
1189 SELECTOROF(pData->old_ss_sp), OFFSETOF(pData->old_ss_sp) );
1191 oldFrame = CURRENT_STACK16;
1193 /* Switch back to the old stack */
1195 IF1632_Saved16_ss_sp = pData->old_ss_sp;
1196 pData->old_ss_sp = 0;
1198 /* Build a stack frame for the return */
1200 newFrame = CURRENT_STACK16;
1201 newFrame->saved_ss_sp = oldFrame->saved_ss_sp;
1202 if (debugging_relay)
1204 newFrame->entry_ip = oldFrame->entry_ip;
1205 newFrame->entry_cs = oldFrame->entry_cs;
1210 /***********************************************************************
1211 * GetTaskQueueDS (KERNEL.118)
1213 void WINAPI GetTaskQueueDS( CONTEXT *context )
1215 DS_reg(context) = GlobalHandleToSel( GetTaskQueue(0) );
1219 /***********************************************************************
1220 * GetTaskQueueES (KERNEL.119)
1222 void WINAPI GetTaskQueueES( CONTEXT *context )
1224 ES_reg(context) = GlobalHandleToSel( GetTaskQueue(0) );
1228 /***********************************************************************
1229 * GetCurrentTask (KERNEL.36)
1231 HTASK16 WINAPI GetCurrentTask(void)
1233 return hCurrentTask;
1236 DWORD WINAPI WIN16_GetCurrentTask(void)
1238 /* This is the version used by relay code; the first task is */
1239 /* returned in the high word of the result */
1240 return MAKELONG( hCurrentTask, hFirstTask );
1244 /***********************************************************************
1245 * GetCurrentPDB (KERNEL.37)
1247 HANDLE16 WINAPI GetCurrentPDB(void)
1251 if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return 0;
1256 /***********************************************************************
1257 * GetInstanceData (KERNEL.54)
1259 INT16 WINAPI GetInstanceData( HINSTANCE16 instance, WORD buffer, INT16 len )
1261 char *ptr = (char *)GlobalLock16( instance );
1262 if (!ptr || !len) return 0;
1263 if ((int)buffer + len >= 0x10000) len = 0x10000 - buffer;
1264 memcpy( (char *)GlobalLock16(CURRENT_DS) + buffer, ptr + buffer, len );
1269 /***********************************************************************
1270 * GetExeVersion (KERNEL.105)
1272 WORD WINAPI GetExeVersion(void)
1276 if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return 0;
1277 return pTask->version;
1281 /***********************************************************************
1282 * SetErrorMode16 (KERNEL.107)
1284 UINT16 WINAPI SetErrorMode16( UINT16 mode )
1289 if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return 0;
1290 oldMode = pTask->error_mode;
1291 pTask->error_mode = mode;
1292 pTask->thdb->process->error_mode = mode;
1297 /***********************************************************************
1298 * SetErrorMode32 (KERNEL32.486)
1300 UINT32 WINAPI SetErrorMode32( UINT32 mode )
1302 return SetErrorMode16( (UINT16)mode );
1306 /***********************************************************************
1307 * GetDOSEnvironment (KERNEL.131)
1309 SEGPTR WINAPI GetDOSEnvironment(void)
1313 if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return 0;
1314 return (SEGPTR)WIN16_GlobalLock16( pTask->pdb.environment );
1318 /***********************************************************************
1319 * GetNumTasks (KERNEL.152)
1321 UINT16 WINAPI GetNumTasks(void)
1327 /***********************************************************************
1328 * GetTaskDS (KERNEL.155)
1330 * Note: this function apparently returns a DWORD with LOWORD == HIWORD.
1331 * I don't think we need to bother with this.
1333 HINSTANCE16 WINAPI GetTaskDS(void)
1337 if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return 0;
1338 return pTask->hInstance;
1342 /***********************************************************************
1343 * IsTask (KERNEL.320)
1345 BOOL16 WINAPI IsTask( HTASK16 hTask )
1349 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return FALSE;
1350 if (GlobalSize16( hTask ) < sizeof(TDB)) return FALSE;
1351 return (pTask->magic == TDB_MAGIC);
1355 /***********************************************************************
1356 * SetTaskSignalProc (KERNEL.38)
1358 * Real 16-bit interface is provided by the THUNK_SetTaskSignalProc.
1360 FARPROC16 WINAPI SetTaskSignalProc( HTASK16 hTask, FARPROC16 proc )
1365 if (!hTask) hTask = hCurrentTask;
1366 if (!(pTask = (TDB *)GlobalLock16( hTask ))) return NULL;
1367 oldProc = (FARPROC16)pTask->userhandler;
1368 pTask->userhandler = (USERSIGNALPROC)proc;
1373 /***********************************************************************
1374 * SetSigHandler (KERNEL.140)
1376 WORD WINAPI SetSigHandler( FARPROC16 newhandler, FARPROC16* oldhandler,
1377 UINT16 *oldmode, UINT16 newmode, UINT16 flag )
1379 fprintf(stdnimp,"SetSigHandler(%p,%p,%p,%d,%d), unimplemented.\n",
1380 newhandler,oldhandler,oldmode,newmode,flag );
1382 if (flag != 1) return 0;
1383 if (!newmode) newhandler = NULL; /* Default handler */
1388 if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return 0;
1389 if (oldmode) *oldmode = pTask->signal_flags;
1390 pTask->signal_flags = newmode;
1391 if (oldhandler) *oldhandler = pTask->sighandler;
1392 pTask->sighandler = newhandler;
1398 /***********************************************************************
1399 * GlobalNotify (KERNEL.154)
1401 VOID WINAPI GlobalNotify( FARPROC16 proc )
1405 if (!(pTask = (TDB *)GlobalLock16( hCurrentTask ))) return;
1406 pTask->discardhandler = proc;
1410 /***********************************************************************
1411 * GetExePtr (KERNEL.133)
1413 HMODULE16 WINAPI GetExePtr( HANDLE16 handle )
1419 /* Check for module handle */
1421 if (!(ptr = GlobalLock16( handle ))) return 0;
1422 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return handle;
1424 /* Check the owner for module handle */
1426 owner = FarGetOwner( handle );
1427 if (!(ptr = GlobalLock16( owner ))) return 0;
1428 if (((NE_MODULE *)ptr)->magic == IMAGE_OS2_SIGNATURE) return owner;
1430 /* Search for this handle and its owner inside all tasks */
1435 TDB *pTask = (TDB *)GlobalLock16( hTask );
1436 if ((hTask == handle) ||
1437 (pTask->hInstance == handle) ||
1438 (pTask->hQueue == handle) ||
1439 (pTask->hPDB == handle)) return pTask->hModule;
1440 if ((hTask == owner) ||
1441 (pTask->hInstance == owner) ||
1442 (pTask->hQueue == owner) ||
1443 (pTask->hPDB == owner)) return pTask->hModule;
1444 hTask = pTask->hNext;
1449 /***********************************************************************
1450 * TaskFirst (TOOLHELP.63)
1452 BOOL16 WINAPI TaskFirst( TASKENTRY *lpte )
1454 lpte->hNext = hFirstTask;
1455 return TaskNext( lpte );
1459 /***********************************************************************
1460 * TaskNext (TOOLHELP.64)
1462 BOOL16 WINAPI TaskNext( TASKENTRY *lpte )
1465 INSTANCEDATA *pInstData;
1467 dprintf_toolhelp( stddeb, "TaskNext(%p): task=%04x\n", lpte, lpte->hNext );
1468 if (!lpte->hNext) return FALSE;
1469 pTask = (TDB *)GlobalLock16( lpte->hNext );
1470 if (!pTask || pTask->magic != TDB_MAGIC) return FALSE;
1471 pInstData = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( pTask->hInstance, 0 );
1472 lpte->hTask = lpte->hNext;
1473 lpte->hTaskParent = pTask->hParent;
1474 lpte->hInst = pTask->hInstance;
1475 lpte->hModule = pTask->hModule;
1476 lpte->wSS = SELECTOROF( pTask->ss_sp );
1477 lpte->wSP = OFFSETOF( pTask->ss_sp );
1478 lpte->wStackTop = pInstData->stacktop;
1479 lpte->wStackMinimum = pInstData->stackmin;
1480 lpte->wStackBottom = pInstData->stackbottom;
1481 lpte->wcEvents = pTask->nEvents;
1482 lpte->hQueue = pTask->hQueue;
1483 strncpy( lpte->szModule, pTask->module_name, 8 );
1484 lpte->szModule[8] = '\0';
1485 lpte->wPSPOffset = 0x100; /*??*/
1486 lpte->hNext = pTask->hNext;
1491 /***********************************************************************
1492 * TaskFindHandle (TOOLHELP.65)
1494 BOOL16 WINAPI TaskFindHandle( TASKENTRY *lpte, HTASK16 hTask )
1496 lpte->hNext = hTask;
1497 return TaskNext( lpte );
1501 /***********************************************************************
1502 * GetAppCompatFlags16 (KERNEL.354)
1504 DWORD WINAPI GetAppCompatFlags16( HTASK16 hTask )
1506 return GetAppCompatFlags32( hTask );
1510 /***********************************************************************
1511 * GetAppCompatFlags32 (USER32.205)
1513 DWORD WINAPI GetAppCompatFlags32( HTASK32 hTask )
1517 if (!hTask) hTask = GetCurrentTask();
1518 if (!(pTask=(TDB *)GlobalLock16( (HTASK16)hTask ))) return 0;
1519 if (GlobalSize16(hTask) < sizeof(TDB)) return 0;
1520 return pTask->compat_flags;