summaryrefslogtreecommitdiffstats
path: root/private/mvdm/wow16/toolhelp
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/mvdm/wow16/toolhelp
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/mvdm/wow16/toolhelp')
-rw-r--r--private/mvdm/wow16/toolhelp/class1.c88
-rw-r--r--private/mvdm/wow16/toolhelp/class2.asm92
-rw-r--r--private/mvdm/wow16/toolhelp/dllentry.asm117
-rw-r--r--private/mvdm/wow16/toolhelp/global.c246
-rw-r--r--private/mvdm/wow16/toolhelp/helper.asm555
-rw-r--r--private/mvdm/wow16/toolhelp/int1.c153
-rw-r--r--private/mvdm/wow16/toolhelp/int2.asm1088
-rw-r--r--private/mvdm/wow16/toolhelp/krnlpeek.asm110
-rw-r--r--private/mvdm/wow16/toolhelp/local.c138
-rw-r--r--private/mvdm/wow16/toolhelp/makefile168
-rw-r--r--private/mvdm/wow16/toolhelp/memman.asm88
-rw-r--r--private/mvdm/wow16/toolhelp/memory.asm608
-rw-r--r--private/mvdm/wow16/toolhelp/module.c156
-rw-r--r--private/mvdm/wow16/toolhelp/notify1.c165
-rw-r--r--private/mvdm/wow16/toolhelp/notify2.asm643
-rw-r--r--private/mvdm/wow16/toolhelp/signal.c110
-rw-r--r--private/mvdm/wow16/toolhelp/stack1.c155
-rw-r--r--private/mvdm/wow16/toolhelp/stack2.asm178
-rw-r--r--private/mvdm/wow16/toolhelp/string.h121
-rw-r--r--private/mvdm/wow16/toolhelp/task1.c73
-rw-r--r--private/mvdm/wow16/toolhelp/task2.asm326
-rw-r--r--private/mvdm/wow16/toolhelp/terminat.asm145
-rw-r--r--private/mvdm/wow16/toolhelp/ththunks.asm50
-rw-r--r--private/mvdm/wow16/toolhelp/timer.asm180
-rw-r--r--private/mvdm/wow16/toolhelp/toolhelp.c147
-rw-r--r--private/mvdm/wow16/toolhelp/toolhelp.def45
-rw-r--r--private/mvdm/wow16/toolhelp/toolhelp.h469
-rw-r--r--private/mvdm/wow16/toolhelp/toolhelp.inc292
-rw-r--r--private/mvdm/wow16/toolhelp/toolhelp.rcv14
-rw-r--r--private/mvdm/wow16/toolhelp/toolpriv.h220
-rw-r--r--private/mvdm/wow16/toolhelp/toolpriv.inc112
-rw-r--r--private/mvdm/wow16/toolhelp/usergdi1.c77
-rw-r--r--private/mvdm/wow16/toolhelp/usergdi2.asm209
-rw-r--r--private/mvdm/wow16/toolhelp/walk286.asm562
-rw-r--r--private/mvdm/wow16/toolhelp/walk386.asm541
35 files changed, 8441 insertions, 0 deletions
diff --git a/private/mvdm/wow16/toolhelp/class1.c b/private/mvdm/wow16/toolhelp/class1.c
new file mode 100644
index 000000000..4b44d010e
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/class1.c
@@ -0,0 +1,88 @@
+/*************************************************************************
+ * CLASS1.C
+ *
+ * Routines used to enumerate window classes
+ *
+ *************************************************************************/
+
+#include "toolpriv.h"
+#include <testing.h>
+
+/* ----- Types ----- */
+
+/* The following was stolen from the 3.1 USER but is the same as 3.0.
+ * Note that the only fielda we use (for now) are the atomClassName
+ * and the pclsNext fields.
+ * Oops. We're going to use the hInstance field also.
+ */
+typedef struct tagCLS
+{
+ struct tagCLS *pclsNext;
+ unsigned clsMagic;
+ unsigned atomClassName;
+ char *pdce; /* DCE * to DC associated with class */
+ int cWndReferenceCount; /* Windows registered with this class */
+ unsigned style;
+ long (far *lpfnWndProc)();
+ int cbclsExtra;
+ int cbwndExtra;
+ HANDLE hInstance;
+ HANDLE hIcon;
+ HANDLE hCursor;
+ HANDLE hbrBackground;
+ char far *lpszMenuName;
+ char far *lpszClassName;
+} CLS;
+typedef CLS FAR *LPCLS;
+
+/* ----- Functions ----- */
+
+/* ClassFirst
+ * Returns information about the first task in the task chain.
+ */
+
+BOOL TOOLHELPAPI ClassFirst(
+ CLASSENTRY FAR *lpClass)
+{
+ WORD wClassHead;
+
+ /* Check for errors */
+ if (!wLibInstalled || !lpClass || lpClass->dwSize != sizeof (CLASSENTRY))
+ return FALSE;
+
+ /* If we're in Win3.1, call the special entry point to get the head */
+ if (!(wTHFlags & TH_WIN30))
+ wClassHead = (WORD)(*lpfnUserSeeUserDo)(SD_GETCLASSHEADPTR, 0, 0L);
+
+ /* In 3.0 (and 3.0a) we're forced to use a fixed offset. Unfortunately,
+ * this offset is different in debug and nondebug versions.
+ */
+ else
+ {
+ if (GetSystemMetrics(SM_DEBUG))
+ wClassHead = 0x1cc;
+ else
+ wClassHead = 0x1b8;
+ wClassHead = *(WORD FAR *)MAKEFARPTR(hUserHeap, wClassHead);
+ }
+
+ /* Now get the stuff */
+ return ClassInfo(lpClass, wClassHead);
+}
+
+
+/* ClassNext
+ * Returns information about the next task in the task chain.
+ */
+
+BOOL TOOLHELPAPI ClassNext(
+ CLASSENTRY FAR *lpClass)
+{
+ /* Check for errors */
+ if (!wLibInstalled || !lpClass || !lpClass->wNext ||
+ lpClass->dwSize != sizeof (CLASSENTRY))
+ return FALSE;
+
+ return ClassInfo(lpClass, lpClass->wNext);
+}
+
diff --git a/private/mvdm/wow16/toolhelp/class2.asm b/private/mvdm/wow16/toolhelp/class2.asm
new file mode 100644
index 000000000..b22a42526
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/class2.asm
@@ -0,0 +1,92 @@
+;**************************************************************************
+;* CLASS2.ASM
+;*
+;* Assembly support for the class enumeration routines.
+;*
+;**************************************************************************
+
+ INCLUDE TOOLPRIV.INC
+
+PMODE32 = 0
+PMODE = 0
+SWAPPRO = 0
+ INCLUDE TDB.INC
+
+;** Class structure
+CLS STRUC
+cls_pclsNext DW ?
+cls_clsMagic DW ?
+cls_atom DW ?
+cls_pdce DW ?
+cls_RefCount DW ?
+cls_style DW ?
+cls_lpfnWndProc DD ?
+cls_cbclsExtra DW ?
+cls_cbwndExtra DW ?
+cls_hInstance DW ?
+cls_hIcon DW ?
+cls_hCursor DW ?
+cls_hbrBackgr DW ?
+cls_lpszMnName DW ?
+cls_lpszClsName DW ?
+CLS ENDS
+
+;** External functions
+externNP HelperVerifySeg
+externFP GetAtomName
+
+;** Functions
+
+sBegin CODE
+ assumes CS,CODE
+
+; ClassInfo
+;
+; Returns information about the class with the given block handle
+
+cProc ClassInfo, <PUBLIC,NEAR>, <si,di,ds>
+ parmD lpClass
+ parmW wOffset
+cBegin
+ ;** Start by verifying that we can read the segment here
+ mov ax,hUserHeap ;Get the selector
+ mov bx,wOffset ; and the desired offset
+ cCall HelperVerifySeg, <ax,bx>
+ or ax,ax ;FALSE return?
+ jnz CI_SelOk ;We're OK
+ xor ax,ax ;Return FALSE
+ jmp CI_End
+CI_SelOk:
+
+ ;** Point to the CLS structure with DS:SI. Note that using DS to
+ ;** point to USER's DS is useful to get USER's local atoms
+ mov ax,hUserHeap ;User's heap is User's DGROUP
+ mov ds,ax
+ mov si,wOffset ;Get a pointer to the CLS structure
+
+ ;** Copy the hInstance
+ les di,lpClass ;Get the structure
+ mov ax,[si].cls_hInstance ;Get the hInst of the class owner
+ mov es:[di].ce_hInst,ax ;Save in the CLASSENTRY struct
+
+ ;** Get the string from the atom and copy the next pointer
+ mov ax,[si].cls_atom ;Get the desired atom number
+ lea bx,[di].ce_szClassName ;Get the offset to copy string to
+ push es ;Save ES (GetAtomName may trash)
+ mov cx,MAX_CLASSNAME ;Get max classname length
+ cCall GetAtomName, <ax,es,bx,cx> ;Copy the atom string
+ pop es
+ or ax,ax ;OK?
+ jnz CI_20 ;Yes
+ mov es:[di].ce_szClassName,0 ;No. Clear the string
+CI_20: mov ax,[si].cls_pclsNext ;Get the next pointer
+ mov es:[di].ce_wNext,ax ;Save it
+
+ ;** Return TRUE on success
+ mov ax,TRUE
+CI_End:
+cEnd
+
+sEnd
+
+ END
diff --git a/private/mvdm/wow16/toolhelp/dllentry.asm b/private/mvdm/wow16/toolhelp/dllentry.asm
new file mode 100644
index 000000000..be77125d0
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/dllentry.asm
@@ -0,0 +1,117 @@
+PAGE,132
+;***************************************************************************
+;*
+;* DLLENTRY.ASM
+;*
+;* TOOLHELP.DLL Entry code
+;*
+;* This module generates a code segment called INIT_TEXT.
+;* It initializes the local heap if one exists and then calls
+;* the C routine LibMain() which should have the form:
+;* BOOL FAR PASCAL LibMain(HANDLE hInstance,
+;* WORD wDataSeg,
+;* WORD cbHeap,
+;* LPSTR lpszCmdLine);
+;*
+;* The result of the call to LibMain is returned to Windows.
+;* The C routine should return TRUE if it completes initialization
+;* successfully, FALSE if some error occurs.
+;*
+;**************************************************************************
+
+ INCLUDE TOOLPRIV.INC
+
+extrn LocalInit:FAR
+extrn GlobalUnwire:FAR
+
+sBegin CODE
+ assumes CS,CODE
+
+externNP ToolHelpLibMain
+externNP HelperReleaseSelector
+externNP NotifyUnInit
+externNP InterruptUnInit
+
+?PLM=0
+externA <_acrtused> ;Ensures that Win DLL startup code is linked
+?PLM=1
+
+
+; LibEntry
+;
+; KERNEL calls this when the TOOLHELP is loaded the first time
+
+cProc LibEntry, <PUBLIC,FAR>
+cBegin
+ push di ;Handle of the module instance
+ push ds ;Library data segment
+ push cx ;Heap size
+ push es ;Command line segment
+ push si ;Command line offset
+
+ ;** If we have some heap then initialize it
+ jcxz callc ;Jump if no heap specified
+
+ ;** Call the Windows function LocalInit() to set up the heap
+ ;** LocalInit((LPSTR)start, WORD cbHeap);
+ xor ax,ax
+ cCall LocalInit <ds, ax, cx>
+ or ax,ax ;Did it do it ok ?
+ jz error ;Quit if it failed
+
+ ;** Invoke our initialization routine
+callc:
+ call ToolHelpLibMain ;Invoke the 'C' routine (result in AX)
+ jmp SHORT exit
+
+error:
+ pop si ;Clean up stack on a LocalInit error
+ pop es
+ pop cx
+ pop ds
+ pop di
+exit:
+
+cEnd
+
+; WEP
+; Windows Exit Procedure
+
+cProc WEP, <FAR,PUBLIC>, <si,di,ds>
+ parmW wState
+cBegin
+ ;** Make sure our DS is safe
+ mov ax,_DATA ;Get the DS value
+ lar cx,ax ;Is it OK?
+ jz @F
+ jmp SHORT WEP_Bad ;No
+@@: and cx,8a00h ;Clear all but P, Code/Data, R/W bits
+ cmp cx,8200h ;Is it P, R/W, Code/Data?
+ jne WEP_Bad ;No
+ mov ax,_DATA ;Get our DS now
+ mov ds,ax
+
+ ;** Uninstall the Register PTrace notifications if necessary
+ cmp wNotifyInstalled,0
+ jz @F
+ cCall NotifyUnInit
+@@:
+ ;** Release fault handlers
+ cmp wIntInstalled,0
+ jz @F
+ cCall InterruptUnInit
+@@:
+ ;** Release our roving selector
+ test wTHFlags, TH_WIN30STDMODE
+ jz @F
+ cCall HelperReleaseSelector, <wSel>
+@@:
+
+WEP_Bad:
+ mov ax,1
+cEnd
+
+sEnd
+
+ END LibEntry
+
diff --git a/private/mvdm/wow16/toolhelp/global.c b/private/mvdm/wow16/toolhelp/global.c
new file mode 100644
index 000000000..65346714b
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/global.c
@@ -0,0 +1,246 @@
+/**************************************************************************
+ * GLOBAL.C
+ *
+ * Routines used to walk the global heap.
+ *
+ **************************************************************************/
+
+#include "toolpriv.h"
+#include <newexe.h>
+#include <string.h>
+
+/* GlobalInfo
+ * Reports information about the state of the global heap,
+ * specifically, the number of elements that will be returned by
+ * a global heap walk.
+ */
+
+BOOL TOOLHELPAPI GlobalInfo(
+ GLOBALINFO FAR *lpGlobalInfo)
+{
+ /* Check the structure size and verify proper installation */
+ if (!wLibInstalled || lpGlobalInfo->dwSize != sizeof (GLOBALINFO))
+ return FALSE;
+
+ /* Get the item counts */
+ if (wTHFlags & TH_KERNEL_386)
+ {
+ lpGlobalInfo->wcItems = Walk386Count(GLOBAL_ALL);
+ lpGlobalInfo->wcItemsFree = Walk386Count(GLOBAL_FREE);
+ lpGlobalInfo->wcItemsLRU = Walk386Count(GLOBAL_LRU);
+ }
+ else
+ {
+ lpGlobalInfo->wcItems = Walk286Count(GLOBAL_ALL);
+ lpGlobalInfo->wcItemsFree = Walk286Count(GLOBAL_FREE);
+ lpGlobalInfo->wcItemsLRU = Walk286Count(GLOBAL_LRU);
+ }
+
+ return TRUE;
+}
+
+/* GlobalFirst
+ * Finds the first element in the global heap. This is modified by
+ * wFlags which modifies which list (GLOBAL_ALL, GLOBAL_FREE,
+ * GLOBAL_LRU) should be walked
+ */
+
+BOOL TOOLHELPAPI GlobalFirst(
+ GLOBALENTRY FAR *lpGlobal,
+ WORD wFlags)
+{
+ DWORD dwFirst;
+
+ /* Check the structure size and verify proper installation */
+ if (!wLibInstalled || !lpGlobal ||
+ lpGlobal->dwSize != sizeof (GLOBALENTRY))
+ return FALSE;
+
+ /* Call the appropriate low-level routine to find the first block */
+ if (wTHFlags & TH_KERNEL_386)
+ {
+ /* Get the first item. Return false if no items in this list */
+ if (!(dwFirst = Walk386First(wFlags)))
+ return FALSE;
+
+ /* Return information about this first item */
+ Walk386(dwFirst, lpGlobal, wFlags);
+ }
+ else
+ {
+ /* Get the first item. Return false if no items in this list */
+ if (!(dwFirst = Walk286First(wFlags)))
+ return FALSE;
+
+ /* Return information about this first item */
+ Walk286(dwFirst, lpGlobal, wFlags);
+ }
+
+ /* Guess at the type of the object */
+ HelperGlobalType(lpGlobal);
+
+ return TRUE;
+}
+
+
+/* GlobalNext
+ * Returns the next item in the chain pointed to by lpGlobal and
+ * in the list indicated by wFlags (same choices as for GlobalFirst().
+ */
+
+BOOL TOOLHELPAPI GlobalNext(
+ GLOBALENTRY FAR *lpGlobal,
+ WORD wFlags)
+{
+ DWORD dwNext;
+
+ /* Check the structure size and verify proper installation */
+ if (!wLibInstalled || !lpGlobal ||
+ lpGlobal->dwSize != sizeof (GLOBALENTRY))
+ return FALSE;
+
+ /* Check to see if we're at the end of the list */
+ dwNext = wFlags & 3 ? lpGlobal->dwNextAlt : lpGlobal->dwNext;
+ if (!dwNext)
+ return FALSE;
+
+ /* If we're using the 386 kernel, call the 386 heap walk routine with
+ * a pointer to the appropriate heap item
+ * (Note that this depends on GLOBAL_ALL being zero)
+ */
+ if (wTHFlags & TH_KERNEL_386)
+ Walk386(dwNext, lpGlobal, wFlags);
+ else
+ Walk286(dwNext, lpGlobal, wFlags);
+
+ /* Guess at the type of the object */
+ HelperGlobalType(lpGlobal);
+
+ return TRUE;
+}
+
+
+/* GlobalEntryHandle
+ * Used to find information about a global heap entry. Information
+ * about this entry is returned in the structure.
+ */
+
+BOOL TOOLHELPAPI GlobalEntryHandle(
+ GLOBALENTRY FAR *lpGlobal,
+ HANDLE hItem)
+{
+ DWORD dwBlock;
+
+ /* Check the structure size and verify proper installation */
+ if (!wLibInstalled || !lpGlobal ||
+ lpGlobal->dwSize != sizeof (GLOBALENTRY))
+ return FALSE;
+
+ /* Make sure this is a valid block */
+ if (wTHFlags & TH_KERNEL_386)
+ {
+ if (!(dwBlock = Walk386Handle(hItem)))
+ return FALSE;
+ }
+ else
+ {
+ if (!(dwBlock = Walk286Handle(hItem)))
+ return FALSE;
+ }
+
+ /* Return information about this item */
+ if (wTHFlags & TH_KERNEL_386)
+ Walk386(dwBlock, lpGlobal, GLOBAL_ALL);
+ else
+ Walk286(dwBlock, lpGlobal, GLOBAL_ALL);
+
+ /* Guess at the type of the object */
+ HelperGlobalType(lpGlobal);
+
+ return TRUE;
+}
+
+
+/* GlobalEntryModule
+ * Returns global information about the block with the given module
+ * handle and segment number.
+ */
+
+BOOL TOOLHELPAPI GlobalEntryModule(
+ GLOBALENTRY FAR *lpGlobal,
+ HANDLE hModule,
+ WORD wSeg)
+{
+ struct new_exe FAR *lpNewExe;
+ struct new_seg1 FAR *lpSeg;
+ DWORD dwBlock;
+
+ /* Check the structure size and verify proper installation */
+ if (!wLibInstalled || !lpGlobal ||
+ lpGlobal->dwSize != sizeof (GLOBALENTRY))
+ return FALSE;
+
+ /* Grunge in the module database to find the proper selector. Start
+ * by first verifying the module database pointer
+ */
+ if (!HelperVerifySeg(hModule, sizeof (struct new_exe)))
+ return FALSE;
+
+ /* Get a pointer to the module database */
+ lpNewExe = MAKEFARPTR(hModule, 0);
+
+ /* Make sure this is a module database */
+ if (lpNewExe->ne_magic != NEMAGIC)
+ return FALSE;
+
+ /* See if the number requested is past the end of the segment table.
+ * Note that the first segment is segment 1.
+ */
+ --wSeg;
+ if (lpNewExe->ne_cseg <= wSeg)
+ return FALSE;
+
+ /* Get a pointer to the segment table */
+ lpSeg = MAKEFARPTR(hModule, lpNewExe->ne_segtab);
+
+ /* Jump to the right spot in the segment table */
+ lpSeg += wSeg;
+
+ /* Make sure this is a valid block and get its arena pointer */
+ if (wTHFlags & TH_KERNEL_386)
+ {
+ if (!(dwBlock = Walk386Handle(lpSeg->ns_handle)))
+ return FALSE;
+ }
+ else
+ {
+ if (!(dwBlock = Walk286Handle(lpSeg->ns_handle)))
+ return FALSE;
+ }
+
+ /* Return information about this item */
+ if (wTHFlags & TH_KERNEL_386)
+ Walk386(dwBlock, lpGlobal, GLOBAL_ALL);
+ else
+ Walk286(dwBlock, lpGlobal, GLOBAL_ALL);
+
+ /* Guess at the type of the object */
+ HelperGlobalType(lpGlobal);
+
+ /* If we've gotten to here, it must be OK */
+ return TRUE;
+}
+
+
+/* GlobalHandleToSel
+ * Provides a generic method of converting a handle to a selector.
+ * This works across Windows versions as well as working when the
+ * value is already a selector.
+ */
+
+WORD TOOLHELPAPI GlobalHandleToSel(
+ HANDLE hMem)
+{
+ return HelperHandleToSel(hMem);
+}
+
diff --git a/private/mvdm/wow16/toolhelp/helper.asm b/private/mvdm/wow16/toolhelp/helper.asm
new file mode 100644
index 000000000..b8ff65962
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/helper.asm
@@ -0,0 +1,555 @@
+;**************************************************************************
+;* HELPER.ASM
+;*
+;* Assembly routines used by more than one module
+;*
+;**************************************************************************
+
+ INCLUDE TOOLPRIV.INC
+ INCLUDE WINDOWS.INC
+PMODE32 = 0
+PMODE = 1
+SWAPPRO = 0
+ INCLUDE WINKERN.INC
+ INCLUDE NEWEXE.INC
+ INCLUDE TDB.INC
+
+;** External functions
+externNP Walk386VerifyLocHeap
+externNP Walk286VerifyLocHeap
+externFP GetCurrentTask
+externFP InterruptUnRegister
+externFP NotifyUnRegister
+externNP SignalUnRegister
+externFP TaskFirst
+externFP TaskNext
+
+;** Functions
+
+sBegin CODE
+ assumes CS,CODE
+
+.286p
+
+; HelperVerifySeg
+;
+; Verifies that a selector is valid and that the segment it points
+; to is safe for reading out to wcb bytes offset
+; Returns 0 if too short or the length of the segment.
+; Preserves all used registers except the return value, AX
+
+cProc HelperVerifySeg, <PUBLIC,NEAR>, <dx>
+ parmW wSeg
+ parmW wcb
+cBegin
+ ;** Verify that this is a valid selector and that it is long enough
+ cCall HelperSegLen, <wSeg> ;Check the segment
+ or dx,dx ;>64K? If so, always return OK
+ jnz HVS_End
+ cmp ax,wcb ;Long enough?
+ ja HVS_End ;Yes, return the length
+HVS_Bad:
+ xor ax,ax ;No. Return FALSE
+HVS_End:
+cEnd
+
+
+; HelperHandleToSel
+; Converts a handle to a selector. This routine knows how to
+; handle 3.0 and 3.1 differences as well as 286 & 386 differences.
+
+cProc HelperHandleToSel, <NEAR,PUBLIC>, <ds>
+ parmW h ;Handle
+cBegin
+ mov ax,_DATA ;Get the data segment
+ mov ds,ax ;Point with DS
+ mov ax,h ;Get the handle
+ test wTHFlags,TH_WIN30 ;Win3.0?
+ jz HTS_Win31 ;No
+ test ax,1 ;Check the low bit
+ jnz HTS_End ;It's already a selector
+ dec ax ;Decrement for proper selector
+ jmp SHORT HTS_End ;Out of here
+
+HTS_Win31:
+ or ax,1 ;Set the bit
+
+HTS_End:
+
+cEnd
+
+
+; HelperVerifyLocHeap
+;
+; Uses the processor-specific local heap verify routine to check the
+; validity of a local heap.
+;
+; Call:
+; AX = Block handle or selector
+; DS must point to TOOLHELP's DGROUP
+; Return:
+; Carry flag set iff NOT a local heap segment
+;
+; Destroys all registers except AX, ESI, EDI, DS, and ES
+
+HelperVerifyLocHeap PROC Near
+ PUBLIC HelperVerifyLocHeap
+
+ test wTHFlags,TH_KERNEL_386 ;Are we using KRNL386?
+ jz HVL_286 ;No
+ jmp Walk386VerifyLocHeap ;Jump to the 386 routine
+
+HVL_286:
+ jmp Walk286VerifyLocHeap ;Jump to the 286 routine
+
+HVL_End:
+ ret
+
+HelperVerifyLocHeap ENDP
+
+
+; HelperGlobalType
+;
+; Given data about a block, gropes around trying to decipher the
+; block type. Parameters are passed and returned in the GLOBALENTRY
+; structure.
+
+cProc HelperGlobalType, <PUBLIC,NEAR>, <si,di,ds>
+ parmD lpGlobal
+ localV Task,<SIZE TASKENTRY>
+cBegin
+ lds si,lpGlobal ;Get the pointer
+ mov [si].ge_wData,0 ;Clear the wData field
+ ; Zero's not a valid seg # or type #
+
+ ;** Check for internal block types
+ mov bx,[si].ge_hOwner ;Get the owner handle
+ mov ax,GT_SENTINEL ;Just in case...
+ cmp bx,GA_SENTINAL ;Is this a sentinel?
+ jz HGT_JmpEnd ;Yes, get out
+ mov ax,GT_BURGERMASTER ;Just in case...
+ cmp bx,GT_BURGERMASTER ;Burgermaster?
+ jz HGT_JmpEnd ;Yes, get out
+ cmp bx,-7 ;Lowest number reserved
+ jb HGT_0 ;Not an internal block
+ mov ax,GT_INTERNAL ;Internal KERNEL block type
+HGT_JmpEnd:
+ jmp HGT_End ;Get out
+HGT_0:
+
+ ;** Check for a free block
+ or bx,bx ;Check for 0: Free block
+ jnz HGT_2 ;Not zero
+ mov ax,GT_FREE ;Free blocks have zero owner
+ jmp HGT_End ;Unknown type
+HGT_2:
+
+ ;** Check for DGROUP and other data segments
+ mov ax,[si].ge_wFlags ;Get the block flags
+ test ax,GAH_DGROUP ;Is this a DGROUP segment
+ jnz @F
+ jmp HGT_10 ;Didn't find it so continue
+@@:
+
+ ;** Save the segment number of the segment
+ mov ax,[si].ge_hOwner ;Get the module database
+ push ax ;Save for later
+ mov bx,[si].ge_hBlock ;Get the handle
+ cCall HelperGetSegNumber ;Get the segment number
+ mov [si].ge_wData,ax ;Save the segment number
+ pop bx ;Get hExe back in BX
+
+ ;** Try two methods: First, see if it is the hInst of the FIRST
+ ;** instance of its module
+ lsl cx, bx ;Is this segment OK?
+ jnz HGT_5 ;No, punt and call it unknown data
+ cmp cx, ne_pautodata ;Long enough?
+ jbe HGT_5 ;No, get out
+ mov es,bx ;Point with ES
+ cmp es:[ne_magic],NEMAGIC ;Make sure we have a module database
+ jnz HGT_5 ;It isn't so get out
+ mov bx,es:[ne_pautodata] ;Point to the segment table entry
+ or bx,bx ;Is there a DGROUP segment?
+ jz HGT_5 ;No, flag as unknown data
+ mov ax,es:[bx].ns_handle ;Get the handle from the table
+ cmp ax,[si].ge_hBlock ;Does the DGROUP handle point here?
+ jnz HGT_3 ;No, might be multiple instance
+ mov ax,GT_DGROUP ;Matches, must be DGROUP
+ jmp HGT_End ;Get out
+HGT_3:
+ ;** It's not the first instance of this module.
+ ;** All multiple instance things will be on the task list
+ ;** so try to find it there.
+ mov bx,[si].ge_hBlock ;Get the handle
+ cCall HelperHandleToSel,<bx> ;Get the selector for this
+ mov di,ax ;Save in DI
+ mov ax,SIZE TASKENTRY ;Get the struct size
+ mov WORD PTR Task.te_dwSize[0],ax ;Put in struct
+ mov WORD PTR Task.te_dwSize[2],0 ;Clear high word
+ lea ax,Task ;Get the structure
+ cCall TaskFirst, <ss,ax> ;Get the first task's info
+ or ax,ax ;No tasks?
+ jz HGT_5 ;Just call it data (not DGROUP)
+HGT_TaskLoop:
+ mov ax,Task.te_hInst ;Get this task's hInst
+ cCall HelperHandleToSel, <ax> ;Convert to selector
+ cmp ax,di ;Is this a match?
+ je HGT_TaskFound ;Yes, do it
+ lea ax,Task ;Point to the struct
+ cCall TaskNext, <ss,ax> ;Get the next one
+ or ax,ax ;End of the line?
+ jnz HGT_TaskLoop ;Nope, do the next one
+HGT_5: mov ax,GT_DATA ;Unknown data segment
+ jmp HGT_End ;Get out
+HGT_TaskFound:
+ mov ax,GT_DGROUP ;Matches, must be DGROUP
+ jmp HGT_End ;Get out
+HGT_10:
+
+ ;** Check for a task database
+ mov ax,[si].ge_hBlock ;Get the segment
+ mov bx,TDBSize ;Get the limit to verify
+ push ax ;Save the segment
+ cCall HelperVerifySeg <ax,bx> ;Make sure we can check signature
+ pop bx ;Retrieve the segment value
+ or ax,ax ;Zero return means bad
+ jz HGT_20 ;Not a task database
+ mov es,bx ;Point to the segment
+ cmp es:[TDB_sig],TDB_SIGNATURE ;Is this really a TDB?
+ jnz HGT_20 ;Nope, go on
+ mov ax,GT_TASK ;Set the task flag
+ jmp HGT_End ;Get out
+HGT_20:
+
+ ;** Now check for Module database
+ mov ax,[si].ge_hOwner ;Get the owner handle
+ cCall HelperHandleToSel, <ax> ;Convert to selector for compare
+ mov cx,ax ;Save in CX
+ mov ax,[si].ge_hBlock ;Does this block own itself?
+ cCall HelperHandleToSel, <ax> ;Convert to selector for compare
+ cmp ax,cx ;Do the pointers match?
+ jnz HGT_24 ;No, so it's not a module database
+ mov ax,GT_MODULE ;Set type
+ jmp HGT_End ;Get out
+HGT_24:
+
+ ;** Check for a code segment. If found, return segment number
+ mov ax,[si].ge_hOwner ;Get the module database
+ push ax ;Save the selector
+ cCall HelperVerifySeg <ax,2> ;Make sure this is OK to put in ES
+ pop bx ;Retrieve in BX
+ or ax,ax ;Zero means bad
+ jnz @F
+ jmp SHORT HGT_Unknown
+@@: mov es,bx ;Point with ES
+ xor dx,dx ;Use DX to count segments
+ cmp es:[ne_magic],NEMAGIC ;Make sure we have a module database
+ jz HGT_25 ;Looks good
+ jmp SHORT HGT_40 ;Not code or resource, try next
+HGT_25: mov cx,es:[ne_cseg] ;Get max number of segments
+ jcxz HGT_30 ;No segments
+ mov di,es:[ne_segtab] ;Point to the segment table
+ mov bx,[si].ge_hBlock ;Get the block we're looking for
+HGT_SegLoop:
+ inc dx ;Bump the segment number
+ cmp bx,es:[di].ns_handle ;Is this the correct segment entry?
+ jz HGT_27 ;Yes, get out
+ add di,SIZE new_seg1 ;Bump to next entry
+ loop HGT_SegLoop ;Loop back to check next entry
+ jmp SHORT HGT_30 ;Now check resources
+HGT_27:
+ mov [si].ge_wData,dx ;Save the segment count
+ mov ax,GT_CODE ;Flag that it's a code segment
+ jmp SHORT HGT_End ;Get out
+
+ ;** Check to see if it's a resource. If so, return resource type #
+HGT_30: mov di,es:[ne_rsrctab] ;Point to the resource table
+ cmp di,es:[ne_restab] ;If both point to same place, no rsrc
+ jz HGT_40 ;No resource table -- unknown type
+ add di,2 ;Skip past alignment count
+HGT_TypeLoop:
+ mov dx,es:[di].rt_id ;DX holds current type number
+ or dx,dx ;Zero type means end of res table
+ jz HGT_40 ;Not found so get out!
+ mov cx,es:[di].rt_nres ;Get the number of resources
+ add di,SIZE RSRC_TYPEINFO ;Bump past the structure
+HGT_ResLoop:
+ cmp bx,es:[di].rn_handle ;Is it this resource?
+ jz HGT_FoundRes ;Yep. This is the one
+ add di,SIZE RSRC_NAMEINFO ;Bump past this structure
+ loop HGT_ResLoop ;Loop for next resource structure
+ jmp HGT_TypeLoop ;Try the next type
+
+ ;** Found the resource, now compute the resource type
+HGT_FoundRes:
+ test dx,RSORDID ;If this bit set, must be ordinal type
+ jnz HGT_32 ;Yep. Ordinal
+ mov dx,GD_USERDEFINED ;Named resources are all user-def
+HGT_32: and dx,NOT RSORDID ;Clear the flag bit
+ cmp dx,GD_MAX_RESOURCE ;If the type is too big, it's user-def
+ jbe HGT_34 ;Standard type
+ mov dx,GD_USERDEFINED ;User-defined resource type
+HGT_34: mov [si].ge_wData,dx ;Save the type
+ mov ax,GT_RESOURCE ;Return that it's a resource
+ jmp SHORT HGT_End ;Get out
+
+HGT_40:
+HGT_Unknown:
+ mov ax,GT_UNKNOWN ;Unknown type
+HGT_End:
+ mov [si].ge_wType,ax ;Save the type and exit
+cEnd
+
+
+; HelperGrabSelector
+;
+; Allocates a selector from DPMI.
+
+cProc HelperGrabSelector, <NEAR,PUBLIC>
+cBegin
+ xor ax,ax ;DPMI Function 0, allocate LDT sels
+ mov cx,1 ;Just 1 sel
+ int 31h ;Call DPMI. Selector in AX
+cEnd
+
+
+; HelperReleaseSelector
+;
+; Frees a selector to DPMI
+
+cProc HelperReleaseSelector, <NEAR,PUBLIC>
+ parmW wSelector
+cBegin
+ mov ax,1 ;DPMI function 1, free LDT sels
+ mov bx,wSelector ;Get the sel
+ int 31h ;Free it
+cEnd
+
+; HelperSetSignalProc
+; Puts a signal proc in a task's TDB so that it gets called in place
+; of USER's proc. Returns the old USER proc.
+
+cProc HelperSetSignalProc, <NEAR,PUBLIC>, <si,di>
+ parmW hTask,
+ parmD lpfn
+cBegin
+ ;** Point to the TDB
+ mov es,hTask ;Point with ES
+
+ ;** Swap the new with the old and return the old one
+ mov ax,WORD PTR lpfn ;Get the new signal proc
+ xchg ax,WORD PTR es:[TDB_USignalProc] ;Switch with the old one
+ mov dx,WORD PTR lpfn + 2 ;Get HIWORD
+ xchg dx,WORD PTR es:[TDB_USignalProc + 2] ;Switch with old one
+cEnd
+
+
+; HelperSignalProc
+; Cleans up when a TOOLHELP-using app is terminated. This proc
+; MUST chain on to USER's signal proc. Note that action is only taken
+; on the death signal (BX = 0666h)
+
+cProc HelperSignalProc, <FAR,PUBLIC>
+cBegin NOGEN
+
+ ;** Save all registers
+ sub sp,4
+ push bp
+ mov bp,sp ;Make a stack frame
+ pusha
+ push ds
+ push es
+
+ ;** Get a pointer to the correct SIGNAL structure
+ mov ax,_DATA ;Get the TOOLHELP.DLL DS
+ mov ds,ax ;Point with DS
+ cCall GetCurrentTask ;Get the current task in AX
+ mov di,ax ;Save task in DI
+ mov si,npSignalHead ;Get the first struct
+HSP_SigLoop:
+ or si,si ;End of the list?
+ jz HSP_Return ;Yes -- This is bad!!
+ cmp di,[si].si_hTask ;Task match?
+ je HSP_FoundIt ;Yes
+ mov si,[si].si_pNext ;Get the next one
+ jmp HSP_SigLoop ;Loop around
+
+ ;** Compute the fake return address (old signal proc)
+HSP_FoundIt:
+ mov ax,WORD PTR [si].si_lpfnOld ;Get LOWORD of old proc
+ mov [bp + 2],ax ;Put on stack frame
+ mov dx,WORD PTR [si].si_lpfnOld + 2 ;Get HIWORD of old proc
+ mov [bp + 4],dx ;Put on stack frame
+
+ ;** See if we have the death signal. If not, don't do anything
+ ;** but just chain on. 20h is the signal for task exit
+ cmp bx, 20h ;Is this the death signal?
+ jne HSP_Done ;No. Don't cleanup
+
+ ;** Since we have a death signal, use it to clean up everything
+ push ax ;Save the return address
+ push dx
+ cCall InterruptUnRegister, <di> ;Unregister any interrupt callbacks
+ cCall NotifyUnRegister, <di> ;Unregister any notification callbacks
+ cCall SignalUnRegister, <di> ;Unregister any signal callbacks
+
+ ;** If we have fooled with the LRU lock (we only do this on 286
+ ;** machines), we must force it unlocked.
+ cmp wLRUCount, 0 ;Is it set?
+ je HSP_NoLRUFoolingAround ;No, don't mess with this
+ mov es, hMaster ;Point to GlobalInfo struct
+ mov ax, es:[gi_lrulock] ;Get current lock count
+ sub ax, wLRUCount ;Get rid of the amount we messed it up
+ jns @F ;Result is OK--no underflow
+ xor ax, ax ;We don't like negative, so zero it
+@@: mov es:[gi_lrulock], ax ;Save the result
+ mov wLRUCount, 0 ;No more LRU count
+HSP_NoLRUFoolingAround:
+ pop dx
+ pop ax
+
+ ;** Make sure we have a proc to chain to
+HSP_Done:
+ or ax,dx ;NULL pointer?
+ jz HSP_Return ;Yes, don't chain to this one
+
+HSP_ChainOn:
+ pop es
+ pop ds
+ popa
+ pop bp
+ retf ;Jump to next signal proc
+
+HSP_Return:
+ pop es
+ pop ds
+ popa
+ pop bp
+ add sp,4 ;Clear fake return address
+ retf 10 ;Return to signal caller
+
+cEnd NOGEN
+
+
+; HelperSegLen
+; Gets the length of a segment, regardless whether it is a 286 or
+; 386 segment.
+; Returns the DWORD length of the segment or zero on error.
+; Doesn't trash registers except DX:AX
+
+cProc HelperSegLen, <NEAR,PUBLIC>, <si,di,cx>
+ parmW wSeg
+cBegin
+ ;** Make sure the segment is present
+ mov cx,wSeg ;Get the selector
+ lar ax,cx ;Get the access rights
+ jnz HSL_Bad ;If LAR fails, this is bad
+ test ax,8000h ;Is this segment present?
+ jz HSL_Bad ;No, call it bad
+
+ ;** Do different stuff on 286 and 386/486
+ mov ax,__WinFlags ;Get the flags
+ test ax,WF_CPU286 ;286?
+ jnz HSL_Do286 ;Yes, do 16 bit stuff
+
+ ;** Get the 32 bit length
+.386p
+ lsl eax,ecx ;Get the limit
+ jnz SHORT HSL_Bad ;We have an error
+ mov edx,eax ;Get HIWORD in DX
+ shr edx,16
+ jmp SHORT HSL_End ;Done
+.286p
+
+ ;** Get the 16 bit length
+HSL_Do286:
+ xor dx,dx ;286 never has >64K segs
+ lsl ax,cx ;Get the limit
+ jnz HSL_Bad ;Bad if LSL fails
+ jmp SHORT HSL_End ;Done
+
+HSL_Bad:
+ xor ax,ax ;Zero return value
+ xor dx,dx
+
+HSL_End:
+
+cEnd
+
+; HelperGetSegNumber
+;
+; Returns the segment number corresponding to a selector given the
+; hExe.
+;
+; Caller: AX=hExe, BX=Handle
+; Exit: AX=Seg Number or 0
+
+cProc HelperGetSegNumber, <NEAR,PUBLIC>, <di>
+cBegin
+ lsl cx, ax ;Is the segment OK to load?
+ jnz HGSN_Error ;No, don't do it
+ cmp cx, ne_segtab ;Long enough?
+ jbe HGSN_Error ;No
+ mov es,ax ;Point with ES
+ xor dx,dx ;Use DX to count segments
+ cmp es:[ne_magic],NEMAGIC ;Make sure we have an hExe
+ jnz HGSN_Error ;Nope, get out
+ mov cx,es:[ne_cseg] ;Get max number of segments
+ jcxz HGSN_Error ;No segments
+ mov di,es:[ne_segtab] ;Point to the segment table
+HGSN_SegLoop:
+ inc dx ;Bump the segment number
+ cmp bx,es:[di].ns_handle ;Is this the correct segment entry?
+ je HGSN_FoundIt ;Yes, get out
+ add di,SIZE new_seg1 ;Bump to next entry
+ loop HGSN_SegLoop ;Loop back to check next entry
+ jmp SHORT HGSN_Error ;Not found
+
+HGSN_FoundIt:
+ mov ax,dx ;Get segment number
+ jmp SHORT HGSN_End
+
+HGSN_Error:
+ xor ax,ax ;Error return
+
+HGSN_End:
+cEnd
+
+;** Internal helper functions
+
+; HelperPDBtoTDB
+;
+; Takes a PDB handle and finds the task handle associated with it.
+; Caller: AX = PDB Handle
+; Return: AX = TDB handle or zero if no TDB exists for it
+
+cProc HelperPDBtoTDB, <NEAR,PUBLIC>
+cBegin
+ ;** Point to the first TDB
+ mov dx,_DATA ;Get the library static segment
+ mov es,dx ;Point with ES
+ mov bx,es:[npwTDBHead] ;Get pointer to first TDB
+ mov dx,es:[segKernel] ;Get the KERNEL data segment
+ mov es,dx ;Point with ES
+ mov dx,es:[bx] ;Get the first TDB
+
+ ;** Check this TDB's PDB to see if it matches
+PT_Loop:
+ mov es,dx ;Get the TDB segment
+ cmp ax,es:[TDB_PDB] ;Compare PDB pointers
+ jz PT_Found ;This is it
+ mov dx,es:[TDB_next] ;Get the next TDB
+ or dx,dx ;End of the line?
+ jnz PT_Loop ;Nope, loop back
+ xor ax,ax ;Return NULL'
+ jmp SHORT PT_End ;Outta here
+
+PT_Found:
+ mov ax,es ;Save the found value
+PT_End:
+
+cEnd
+
+sEnd
+
+ END
diff --git a/private/mvdm/wow16/toolhelp/int1.c b/private/mvdm/wow16/toolhelp/int1.c
new file mode 100644
index 000000000..3acd50307
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/int1.c
@@ -0,0 +1,153 @@
+/**************************************************************************
+ * INT1.C
+ *
+ * Routines used to implement the interrupt trapping API in
+ * TOOLHELP.DLL
+ *
+ **************************************************************************/
+
+#include <string.h>
+#include "toolpriv.h"
+
+/* ----- Global variables ----- */
+ WORD wIntInstalled;
+ INTERRUPT NEAR *npIntHead;
+
+/* InterruptRegister
+ * Registers an interrupt callback.
+ */
+
+BOOL TOOLHELPAPI InterruptRegister(
+ HANDLE hTask,
+ FARPROC lpfnCallback)
+{
+ INTERRUPT *pInt;
+ INTERRUPT *pTemp;
+
+ /* Make sure TOOLHELP.DLL is installed */
+ if (!wLibInstalled)
+ return FALSE;
+
+ /* If the interrupt hook has not yet been installed, install it */
+ if (!wIntInstalled)
+ {
+ /* Make sure we can hook! */
+ if (!InterruptInit())
+ return FALSE;
+ wIntInstalled = TRUE;
+ }
+
+ /* NULL hTask means current task */
+ if (!hTask)
+ hTask = GetCurrentTask();
+
+ /* Register a death signal handler for this task (does nothing if one
+ * is already installed.
+ */
+ SignalRegister(hTask);
+
+ /* Check to see if this task is already registered */
+ for (pInt = npIntHead ; pInt ; pInt = pInt->pNext)
+ if (pInt->hTask == hTask)
+ return FALSE;
+
+ /* Allocate a new INTERRUPT structure */
+ pInt = (INTERRUPT *)LocalAlloc(LMEM_FIXED, sizeof (INTERRUPT));
+ if (!pInt)
+ return FALSE;
+
+ /* Fill in the useful fields */
+ pInt->hTask = hTask;
+ pInt->lpfn = lpfnCallback;
+
+ /* If this is the only handler, just insert it */
+ if (!npIntHead)
+ {
+ pInt->pNext = npIntHead;
+ npIntHead = pInt;
+ }
+
+ /* Otherwise, insert at the end of the list */
+ else
+ {
+ for (pTemp = npIntHead ; pTemp->pNext ; pTemp = pTemp->pNext)
+ ;
+ pInt->pNext = pTemp->pNext;
+ pTemp->pNext = pInt;
+ }
+
+ return TRUE;
+}
+
+
+/* InterruptUnRegister
+ * Called by an app whose callback is no longer to be used.
+ * NULL hTask uses current task.
+ */
+
+BOOL TOOLHELPAPI InterruptUnRegister(
+ HANDLE hTask)
+{
+ INTERRUPT *pInt;
+ INTERRUPT *pBefore;
+
+ /* Make sure we have interrupt installed and that TOOLHELP is OK */
+ if (!wLibInstalled || !wIntInstalled)
+ return FALSE;
+
+ /* NULL hTask means current task */
+ if (!hTask)
+ hTask = GetCurrentTask();
+
+ /* First try to find the task */
+ pBefore = NULL;
+ for (pInt = npIntHead ; pInt ; pInt = pInt->pNext)
+ if (pInt->hTask == hTask)
+ break;
+ else
+ pBefore = pInt;
+ if (!pInt)
+ return FALSE;
+
+ /* Unhook the death signal proc only if there is no interrupt handler */
+ if (!NotifyIsHooked(hTask))
+ SignalUnRegister(hTask);
+
+ /* Remove it from the list */
+ if (!pBefore)
+ npIntHead = pInt->pNext;
+ else
+ pBefore->pNext = pInt->pNext;
+
+ /* Free the structure */
+ LocalFree((HANDLE)pInt);
+
+ /* If there are no more handlers, unhook the callback */
+ if (!npIntHead)
+ {
+ InterruptUnInit();
+ wIntInstalled = FALSE;
+ }
+
+ return TRUE;
+}
+
+/* ----- Helper functions ----- */
+
+/* InterruptIsHooked
+ * Returns TRUE iff the parameter task already has a interrupt hook.
+ */
+
+BOOL PASCAL InterruptIsHooked(
+ HANDLE hTask)
+{
+ INTERRUPT *pInt;
+
+ /* Loop thorugh all interrupts */
+ for (pInt = npIntHead ; pInt ; pInt = pInt->pNext)
+ if (pInt->hTask == hTask)
+ break;
+
+ /* Return found/not found */
+ return (BOOL)pInt;
+}
diff --git a/private/mvdm/wow16/toolhelp/int2.asm b/private/mvdm/wow16/toolhelp/int2.asm
new file mode 100644
index 000000000..25e52ffea
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/int2.asm
@@ -0,0 +1,1088 @@
+ PAGE 60,150
+;***************************************************************************
+;* INT2.ASM
+;*
+;* Assembly code support routines used for the TOOLHELP.DLL interrupt
+;* trapping API
+;*
+;***************************************************************************
+
+ INCLUDE TOOLPRIV.INC
+ INCLUDE WINDOWS.INC
+ include vint.inc
+.286p
+
+;** Symbols
+I_EXCEPTION EQU 0
+I_INTERRUPT EQU 1
+MAX_INTERRUPT EQU 7
+GIVE_WDEB386 EQU 8000h
+BAD_STACK_FLAG EQU 8000h
+MIN_STACK_ALLOWED EQU 128
+
+;** Local types
+
+INT_INFO STRUC
+ii_wNumber DW ? ;INT nn
+ii_wType DW ? ;I_EXCEPTION or I_INTERRUPT
+ii_dwChain DD ?
+ii_wHandler DW ? ;Note that this is CS relative
+INT_INFO ENDS
+
+;** Data
+sBegin DATA
+
+IntInfo LABEL BYTE
+ public IntInfo
+UD_Info DW 6 ;Undefined opcode
+ DW I_EXCEPTION ;This should be DPMI-hooked
+ DD 0 ;Chain address (will be initialized)
+ DW OFFSET _TEXT:UD_Handler
+Div0_Info DW 0 ;Divide by zero
+ DW I_EXCEPTION ;Hook with DPMI
+ DW OFFSET _TEXT:Div0_Handler
+ DW 0
+ DW OFFSET _TEXT:Div0_Handler
+Int1_Info DW 1 ;Single step + debug register
+ DW I_INTERRUPT ;Hook with DOS
+ DD 0 ;Chain address
+ DW OFFSET _TEXT:Int1_Handler
+Int3_Info DW 3 ;Software debug int
+ DW I_INTERRUPT ;Hook with DOS
+ DD 0 ;Chain address
+ DW OFFSET _TEXT:Int3_Handler
+GP_Info DW 13 ;GP Fault
+ DW I_EXCEPTION ;This should be DPMI-hooked
+ ;** This entry is a special case entry for the Win30 std mode
+ ;* handler. This is a separate entry point into the
+ ;** interrupt handler routine
+ DW OFFSET _TEXT:GP_StdModeHandler
+ DW 0
+ DW OFFSET _TEXT:GP_Handler
+SF_Info DW 12 ;Stack fault
+ DW I_EXCEPTION ;This should be DPMI-hooked
+ ;** This entry is a special case entry for the Win30 std mode
+ ;* handler. This is a separate entry point into the
+ ;** interrupt handler routine
+ DW OFFSET _TEXT:SF_StdModeHandler
+ DW 0
+ DW OFFSET _TEXT:SF_Handler
+PF_Info DW 14 ;Page fault
+ DW I_EXCEPTION ;This should be DPMI-hooked
+ ;** This entry is a special case entry for the Win30 std mode
+ ;* handler. This is a separate entry point into the
+ ;** interrupt handler routine
+ DW OFFSET _TEXT:PF_StdModeHandler
+ DW 0
+ DW OFFSET _TEXT:PF_Handler
+CASRq_Info DW 256 ;CtlAltSysRq (fake interrupt)
+ DW I_INTERRUPT ;Hook with DOS
+ DD 0 ;Chain address
+ DW OFFSET _TEXT:CASRq_Handler
+
+ ;** The following data is used to see if GDI wants the
+ ;** Div0 we have trapped
+lpGDIFlag DD 0
+hGDI DW 0
+szGDI DB 'GDI', 0
+ public lpGDIFlag, hGDI
+
+ ;** Points to a KERNEL routine to see if it wants the
+ ;** GP fault first
+lpfnPV DD 0 ;Call to see if PV GP fault
+ public lpfnPV
+
+ ;** Globals used for DPMI emulation
+lpOldHandler DD 0 ;Previous DPMI exception handler
+lpChainCSIP DD 0 ;Next exception handler on chain
+wException DW 0
+ public lpOldHandler, lpChainCSIP
+
+externW wCASRqFlag ;Set when an CASRq INT3 has been set
+externD dwCASRqCSIP ;Holds the CS:IP of the CASRq INT3
+sEnd
+
+;** Imports
+externNP TerminateApp
+externNP HelperHandleToSel
+externNP HelperVerifySeg
+externFP AllocCStoDSAlias
+externFP FreeSelector
+externFP GetModuleHandle
+externFP GetProcAddress
+externFP GlobalEntryHandle
+externFP _wsprintf
+externFP OutputDebugString
+externA __WinFlags
+
+;** Functions
+
+sBegin CODE
+ assumes CS,CODE
+ assumes DS,DATA
+
+;** Interrupt trapping API
+
+; InterruptInit
+; Hooks all necessary interrupts and exceptions to allow an API
+; for app-level interrupt hooks.
+
+cProc InterruptInit, <NEAR,PUBLIC>, <si,di>
+cBegin
+ ;** Loop through all possible handlers
+ mov cx,MAX_INTERRUPT ;Number of ints to hook
+ lea si,IntInfo ;Get the address of the array
+DII_HandlerLoop:
+ push cx ;Save loop counter
+ cmp [si].ii_wNumber,256 ;Fake exception?
+ jae DII_Continue ;Yes, don't hook anything!
+ cmp [si].ii_wType,I_EXCEPTION ;Exception?
+ jnz DII_Interrupt ;Nope, hook as interrupt
+
+ ;** Do a special case for 3.0 Std Mode
+ test wTHFlags,TH_WIN30STDMODE ;Are we in Win30 std mode?
+ jz DII_NotStdMode ;No.
+ mov ax,WORD PTR [si].ii_dwChain ;Get the secondary handler
+ mov [si].ii_wHandler,ax ;Make sure we use it instead!
+DII_NotStdMode:
+
+ ;** Hook as an exception (DPMI)
+ mov ax,0202h ;Get exception handler - DPMI
+ mov bl,BYTE PTR [si].ii_wNumber ;Interrupt number
+ int 31h ;Call DPMI
+ mov WORD PTR [si].ii_dwChain,dx ;Save the old offset
+ mov WORD PTR [si].ii_dwChain + 2,cx ;Save the old selector
+ mov ax,0203h ;Set exception handler - DPMI
+ mov bl,BYTE PTR [si].ii_wNumber ;Interrupt number
+ mov dx,[si].ii_wHandler ;Address of exception handler
+ mov cx,cs ;Selector value of handler
+ int 31h ;Call DPMI
+ jmp SHORT DII_Continue
+
+ ;** Hook as an interrupt (DOS)
+DII_Interrupt:
+ mov ah,35h ;Get interrrupt handler - DOS
+ mov al,BYTE PTR [si].ii_wNumber ;Interrupt number
+ int 21h ;Call DOS
+ mov WORD PTR [si].ii_dwChain,bx ;Save the old offset
+ mov WORD PTR [si].ii_dwChain + 2,es ;Save the old selector
+ mov ah,25h ;Set interrupt handler - DOS
+ mov al,BYTE PTR [si].ii_wNumber ;Interrupt number
+ mov dx,[si].ii_wHandler ;Address of exception handler
+ push ds ;Save static DS for later
+ push cs ;DS = CS
+ pop ds
+ int 21h ;Call DOS
+ pop ds ;Get segment back
+
+ ;** Prepare for next in table
+DII_Continue:
+ add si,SIZE INT_INFO ;Bump to next entry
+ pop cx ;Get loop counter back
+ loop DII_HandlerLoop ;Loop back
+
+ ;** Prepare the linked list
+ mov npIntHead,0 ;Put a NULL in the list head
+
+ ;** Get information so we can check the GDI flag
+ lea ax,szGDI ;Get the string
+ cCall GetModuleHandle, <ds,ax> ;Get GDI's module handle
+ cCall HelperHandleToSel, <ax> ;Convert the owner to a selector
+ mov hGDI,ax ;Save it for later
+ cCall GetProcAddress, <ax,0,355> ;The flag is ordinal 355
+ mov WORD PTR lpGDIFlag[0],ax ;Save it for later
+ mov WORD PTR lpGDIFlag[2],dx
+
+DII_End:
+ ;** Return TRUE
+ mov ax,1
+cEnd
+
+
+; InterruptUnInit
+; Unhooks all interrupts and exceptions hooked by DebugInterruptUnInit.
+
+cProc InterruptUnInit, <NEAR,PUBLIC>, <si,di>
+cBegin
+
+ ;** Loop through all possible handlers
+ mov cx,MAX_INTERRUPT ;Number of ints to hook
+ lea si,IntInfo ;Get the address of the array
+DIU_HandlerLoop:
+ push cx ;Save loop counter
+ cmp [si].ii_wNumber,256 ;Fake exception?
+ jae DIU_Continue ;Yes, don't unhook anything!
+ cmp [si].ii_wType,I_EXCEPTION ;Exception?
+ jnz DIU_Interrupt ;Nope, hook as interrupt
+
+ ;** Unhook exception (DPMI)
+ mov ax,0203h ;Set exception handler - DPMI
+ mov bl,BYTE PTR [si].ii_wNumber ;Interrupt number
+ mov dx,WORD PTR [si].ii_dwChain ;Put back the old offset
+ mov cx,WORD PTR [si].ii_dwChain + 2 ;Put back the old selector
+ int 31h ;Call DPMI
+ jmp SHORT DIU_Continue
+
+ ;** Unhook interrupt (DOS)
+DIU_Interrupt:
+ mov ah,35h ;Get interrrupt handler - DOS
+ mov al,BYTE PTR [si].ii_wNumber ;Interrupt number
+ int 21h ;Call DOS
+ mov ah,25h ;Set interrupt handler - DOS
+ mov al,BYTE PTR [si].ii_wNumber ;Interrupt number
+ mov dx,WORD PTR [si].ii_dwChain ;Put back the old offset
+ push ds
+ mov ds,WORD PTR [si].ii_dwChain + 2 ;Put back the old selector
+ int 21h ;Call DOS
+ pop ds
+
+ ;** Prepare for next in table
+DIU_Continue:
+ add si,SIZE INT_INFO ;Bump to next entry
+ pop cx ;Get loop counter back
+ loop DIU_HandlerLoop ;Loop back
+
+ ;** Prepare the linked list
+ mov npIntHead,0 ;Put a NULL in the list head
+
+DIU_End:
+
+cEnd
+
+InterruptEntry MACRO Name, wBytes
+ labelFP Name ;;Start at this address
+ PUBLIC Name
+ sub sp,wBytes ;;Leave room on stack for return val
+ push bx ;;Save for the info pointer
+ENDM
+
+InterruptJump MACRO pInfo
+ mov bx,OFFSET pInfo ;;Point to interrupt info
+ jmp DIH_Main
+ENDM
+
+; InterruptHandler
+; This routine is used to handle interrupts as they come in. This
+; routine has multiple entry points; a seperate one for each int/
+; exception trapped. Because interrupts and exceptions have
+; different stack frames, they are handled by two different code
+; sections.
+
+cProc InterruptHandler, <FAR,PUBLIC>
+cBegin NOGEN
+
+ ;** All interrupt entry points here
+
+ InterruptEntry GP_Handler, 14 ;Normal GP fault
+ InterruptJump GP_Info
+ InterruptEntry GP_StdModeHandler, 12 ;3.0 Std mode GP fault
+ InterruptJump GP_Info
+ InterruptEntry SF_Handler, 14 ;Normal Stack Fault
+ InterruptJump SF_Info
+ InterruptEntry SF_StdModeHandler, 12 ;3.0 Std mode Stack Fault
+ InterruptJump SF_Info
+ InterruptEntry PF_Handler, 14 ;Page fault
+ InterruptJump PF_Info
+ InterruptEntry PF_StdModeHandler, 10 ;3.0 Std mode Page fault
+ InterruptJump PF_Info
+ InterruptEntry UD_Handler, 14 ;Undefined opcode
+ InterruptJump UD_Info
+ InterruptEntry Int1_Handler, 14 ;Int 1
+ InterruptJump Int1_Info
+ InterruptEntry Int3_Handler, 14 ;Int 3
+ InterruptJump Int3_Info
+ InterruptEntry CASRq_Handler, 14 ;Ctrl-Alt-SysRq (not really an int)
+ InterruptJump CASRq_Info
+
+ ;** The divide by zero case has to include checking to make sure
+ ;** that this isn't GDI's divide by zero.
+
+ InterruptEntry Div0_Handler, 14
+
+ ;** Check to see if GDI wants this Div0
+ push ds ;Save some registers
+ push es
+ mov bx,_DATA ;Point to our data
+ mov ds,bx ; with DS
+ mov bx,WORD PTR lpGDIFlag[0];Get the low word
+ push bx
+ or bx,WORD PTR lpGDIFlag[2];Do we have a flag to look at?
+ pop bx
+ jz DIH_NoFlag ;No. Do this the hard way
+
+ ;** Since we have a pointer to GDI's flag to look at, use it
+ mov es,WORD PTR lpGDIFlag[2];Get the seg value
+ cmp WORD PTR es:[bx],0 ;The flag is nonzero if GDI wants it
+ je DIH_NormalDiv0 ;GDI doesn't want it
+
+ ;** GDI wants the Div0 so chain to it
+DIH_ChainToGDI:
+ pop es ;Restore registers
+ pop ds ; (Doesn't trash flags)
+ push bp ;Make the same stack frame for compat
+ mov bp,sp
+ pusha ;Save all registers
+ push ds
+ push es
+ mov ax,_DATA ;Get the data segment
+ mov ds,ax ;Point with DS
+ mov bx,OFFSET Div0_Info ;This fault's info
+ jmp DIH_DPMIChainOn ;Chain on (ignore the int)
+
+DIH_NormalDiv0:
+ pop es ;Restore registers
+ pop ds
+ InterruptJump Div0_Info
+
+ ;** We didn't get a GDI flag (only present in 3.1) so instead, we
+ ;* check the owner of the CS where the fault occurred. If
+ ;** the owner is GDI, we ignore the Div0.
+DIH_NoFlag:
+ push bp ;Make a stack frame
+ mov bp,sp
+ sub sp,SIZE GLOBALENTRY ;Make room for a structure
+Global EQU [bp - SIZE GLOBALENTRY] ;Point to our structure
+ pusha ;Save all registers
+ mov WORD PTR Global.ge_dwSize[0],SIZE GLOBALENTRY ;Size of struct
+ mov WORD PTR Global.ge_dwSize[2],0
+ lea bx,Global ;Point to the structure
+ test wTHFlags,TH_WIN30STDMODE ;3.0 std mode?
+ jnz DIH_Div0_StdMode ;Yes
+ mov ax,[bp + 1ah + 4] ;Get the CS value (4 is extra BP,BX
+ jmp SHORT @F ; pushed by InterruptEntry macro)
+DIH_Div0_StdMode:
+ mov ax,[bp + 14h + 4] ;Get the CS value
+@@: cCall GlobalEntryHandle, <ss,bx,ax> ;Get info about the CS
+ or ax,ax ;Did the call succeed?
+ jne @F ;Yes, go on
+ popa ;No, clear stack frame and do normal
+ mov sp,bp
+ pop bp
+ jmp DIH_NormalDiv0 ;Jump to normal processing
+@@: mov ax,Global.ge_hOwner ;Get the owner
+ cCall HelperHandleToSel, <ax> ;Make it a selector
+ cmp hGDI,ax ;Is this owned by GDI?
+ popa ;Restore the registers
+ mov sp,bp ;Clear stack frame
+ pop bp
+ je DIH_ChainToGDI ;Yes, so give the interrupt to it
+ jmp DIH_NormalDiv0 ;No, do normal stuff
+
+ ;** We now have to first find the item on the block to see if we
+ ;** want to handle the interrupt.
+PubLabel CommonInterruptEntry
+DIH_Main:
+ push bp ;Make a stack frame
+ mov bp,sp
+ pusha ;Save all registers
+ push ds
+ push es
+
+ ;** We check first to see if this was a GP fault received from the
+ ;* parameter validation code. If it was, we just chain on
+ ;* just as if we don't find any handlers.
+ ;**
+ mov ax,_DATA ;Get our data segment
+ mov ds,ax
+ FSTI ;Must have interrupts on
+ cmp bx,OFFSET GP_Info ;GP Fault?
+ jnz DIH_NotPVGPFault ;No.
+ mov cx,WORD PTR lpfnPV[0] ;Get the low word
+ or cx,WORD PTR lpfnPV[2] ;Param Validation stuff present?
+ jz DIH_NotPVGPFault ;No, skip this
+
+ ;** Check to see if the parameter validation code wants the fault
+ push ds
+ push bx
+ push [bp + 1Ah] ;Push faulting CS:IP
+ push [bp + 18h]
+ call [lpfnPV] ;Call it
+ pop bx
+ pop ds
+ or ax,ax ;Non-zero means this was PV fault
+ je DIH_NotPVGPFault ;Not a PV GP fault
+
+ ;** It is a parameter validation fault so ignore it
+ jmp DIH_DPMIChainOn ;Chain the fault on--we don't want it
+
+ ;** We check here to see if the INT3 we received is from the CASRq
+ ;* handler. If it was, we have to replace the INT3 with the
+ ;* previous byte and tell the user this was actually a CASRq
+ ;** event (not an INT3).
+PubLabel DIH_NotPVGPFault
+ cmp bx,OFFSET Int3_Info ;INT3?
+ jnz DIH_NotCASRq ;Nope, ignore all this
+ cmp wCASRqFlag,0 ;Was this because of CASRq?
+ je DIH_NotCASRq ;No.
+ mov ax,[bp + 12h] ;INT3 is an IRET frame. Get bkpt IP
+ dec ax ;Breaks AFTER instruction
+ cmp WORD PTR dwCASRqCSIP[0],ax ;Is this the right CASRq address?
+ jne DIH_NotCASRq ;Nope
+ mov dx,[bp + 14h] ;Get the breakpoint CS
+ cmp WORD PTR dwCASRqCSIP[2],dx ;Is this correct?
+ jne DIH_NotCASRq ;Nope
+ push ax ;Save the IP value
+ cCall AllocCStoDSAlias, <dx> ;Get a data alias to the CS
+ mov es,ax ;Point with ES
+ pop si ;Restore the IP value
+ mov [bp + 12h],si ;Back to instr where INT3 was
+ mov al,BYTE PTR wCASRqFlag ;Get the saved byte
+ mov es:[si],al ;Put it back in the code
+ mov wCASRqFlag,0 ;Clear the flag
+ cCall FreeSelector, <es> ;Get rid of the alias
+ mov bx,OFFSET CASRq_Info ;Point to the CASRq information
+
+ ;** See if we have at least one handler. We should always have one.
+PubLabel DIH_NotCASRq
+ mov si,npIntHead ;Get the list start
+ or si,si ;Are there any routines hooked?
+ jnz DIH_Found ;There should ALWAYS be at least one
+ ; routine hooked (otherwise, the
+ ; interrupt hooks should have
+ ; already been removed)
+
+ ;** Return the stack to its prior state and chain on.
+ ;* We only get here in an erroneous state. We keep the code in
+ ;* to avoid GP faulting if things get wierd.
+ ;* The stack looks like this:
+ ;* ------------
+ ;* | ES |
+ ;* | DS |
+ ;* | PUSHA |
+ ;* BP-->| Old BP | [BP + 00h]
+ ;* | BX | [BP + 02h]
+ ;* | Empty | [BP + 04h]
+ ;* | Empty | [BP + 06h]
+ ;* | Empty | [BP + 08h]
+ ;* | Empty | [BP + 0Ah]
+ ;* | Empty | [BP + 0Ch]
+ ;* |Our Ret IP| [BP + 0Eh]
+ ;* |Our Ret CS| [BP + 10h]
+ ;* |Original |
+ ;* | Frame |
+ ;* | .... |
+ ;* ------------
+ ;**
+PubLabel DIH_DPMIChainOn
+ mov ax,WORD PTR [bx].ii_dwChain ;Get the LOWORD
+ mov [bp + 0eh],ax ;Put into the frame we created
+ mov ax,WORD PTR [bx].ii_dwChain + 2 ;Get the HIWORD
+ mov [bp + 10h],ax ;Put into the frame
+ pop es ;Clear the stack
+ pop ds
+ popa
+ pop bp
+ pop bx
+ add sp,10 ;Clear extra space
+ retf ;This uses our own "return" frame
+ ; to chain on
+
+ ;** Since we found the entry, we have to call the user callback.
+ ;* Because we must be reentrant at this state, we have to make
+ ;* sure that we're safe. To do so, we must do different
+ ;** actions for DPMI and for DOS frames.
+PubLabel DIH_Found
+ cmp [bx].ii_wType,I_EXCEPTION ;DPMI Exception frame?
+ je @F
+ jmp DIH_SkipDPMI ;No. Skip DPMI processing
+@@:
+
+ ;** If we are in Win3.0 Std Mode, the DPMI frame was broken. It
+ ;* simply left the normal IRET frame on the stack *AND* the
+ ;** error code.
+ test wTHFlags,TH_Win30StdMode ;3.0 Std mode?
+ jz @F
+ jmp DIH_SkipDPMI ;Yes
+@@:
+
+ ;** Tell DPMI that the exception is over. Before we do this,
+ ;* however, save information we'll need later on the user stack.
+ ;* The stack currently looks like this:
+ ;* ------------
+ ;* | ES |
+ ;* | DS |
+ ;* | PUSHA |
+ ;* BP-->| Old BP | [BP + 00h]
+ ;* | BX | [BP + 02h]
+ ;* | Empty | [BP + 04h]
+ ;* | Empty | [BP + 06h]
+ ;* | Empty | [BP + 08h]
+ ;* | Empty | [BP + 0Ah]
+ ;* | Empty | [BP + 0Ch]
+ ;* | Empty | [BP + 0Eh]
+ ;* | Empty | [BP + 10h]
+ ;* | Ret IP | [BP + 12h] <-
+ ;* | Ret CS | [BP + 14h] |
+ ;* |Error Code| [BP + 16h] | Pushed by DPMI
+ ;* | IP | [BP + 18h] |
+ ;* | CS | [BP + 1Ah] | (Locked stack)
+ ;* | Flags | [BP + 1Ch] |
+ ;* | SP | [BP + 1Eh] |
+ ;* | SS | [BP + 20h] <-
+ ;* ------------
+ ;*
+ ;* Before returning to DPMI, however, we want to create a
+ ;* stack frame on the user's stack that we will be returning
+ ;* to so we can preserve information in a reentrant fashion.
+ ;* The user's stack will appear like this:
+ ;* ------------
+ ;* BP---->| Old BP | [BP + 00h]
+ ;* | BX | [BP + 02h]
+ ;* |Our Ret IP| [BP + 04h]
+ ;* |Our Ret CS| [BP + 06h]
+ ;* | Ret IP | [BP + 08h]
+ ;* | Ret CS | [BP + 0Ah]
+ ;* | AX | [BP + 0Ch]
+ ;* |Exception#| [BP + 0Eh]
+ ;* | Handle | [BP + 10h]
+ ;* | IP | [BP + 12h]
+ ;* | CS | [BP + 14h]
+ ;* | Flags | [BP + 16h]
+ ;* ------------
+ ;**
+
+PubLabel DIH_Exception
+
+ ;** Check to see if we're already on the faulting stack. If we are,
+ ;** we want to shift everything up on this stack so that we
+ ;** have room for the TOOLHELP user frame
+ mov ax,ss ;Get the current SS
+ cmp ax,WORD PTR ss:[bp + 20h] ;Is it the same as the user frame?
+ jne DIH_EnoughRoomOnStack ;No, ignore all of this
+
+ ;** Move everything up by copy everything that's on stack now to
+ ;** above where SP starts at. This actually uses too much
+ ;** stack, but it's safe and easy.
+ push bp ;We use BP to do copy
+ lea bp,[bp + 20h] ;Point to lowest WORD to copy
+ mov ax,sp ;Point to position to copy to
+ dec ax
+ dec ax
+DIH_CopyLoop:
+ push WORD PTR [bp] ;Copy a WORD
+ dec bp ;Point to next WORD to copy
+ dec bp
+ cmp bp,ax ;Done yet?
+ jne DIH_CopyLoop ;No
+ pop bp ;Yes, compute new BP value
+ sub bp,56 ;Point BP to right place
+
+ ;** Put stuff on DPMI's stack
+PubLabel DIH_EnoughRoomOnStack
+ mov di,[bp + 1Eh] ;Get the old SP value
+ mov cx,[bp + 20h] ; and the SS value
+ cmp di,MIN_STACK_ALLOWED ;Are we going to stack fault?
+ jb DIH_BadStack ;Yes, so swich
+ mov ax,__WinFlags ;Make sure we have a 386 or higher
+ test ax,WF_CPU286
+ jnz DIH_SkipBigCheck ;No need to check big bit
+.386p
+ push eax ;Make sure we don't trash EAX
+ lar eax,ecx ;Get the access rights DWORD
+ test eax,00400000h ;Check the BIG bit
+ pop eax
+ jnz DIH_BadStack ;Don't use this stack if BIG
+.286p
+DIH_SkipBigCheck:
+ mov ax,di ;Get the stack pointer
+ add ax,2 ;Point just beyond
+ cCall HelperVerifySeg, <cx,ax> ;Is this seg OK?
+ or ax,ax ;Check for success
+ jz DIH_BadStack ;Stack is bogus, don't change to it
+
+PubLabel DIH_StackOK
+ sub di,20 ;Reserve space for the user frame
+ mov ds,cx ;Get stack value in DS
+ mov dx,[bp + 1Ah] ;Get the old CS value
+ mov cx,[bp + 18h] ;Get the old IP value
+ mov ax,[bp + 1Ch] ;Get the old flags
+ mov [bp + 1Eh],di ;Save as new SP value
+ sub di,4 ;Make DI equal to what BP will be
+ mov [bp + 1Ah],cs ;Prepare to IRET to ourself
+ mov [bp + 18h],OFFSET _TEXT:DIH_DPMIRestart
+
+ ;** Save some things on the user's stack before returning
+ mov [di + 16h],ax ;Save the flags
+ mov [di + 14h],dx ;Save the old CS value
+ mov [di + 12h],cx ;Save the old IP value
+ mov [di + 0Eh],bx ;INT_INFO pointer to new stack
+ mov [di + 10h],si ;Handle to new stack
+
+ ;** Clear the Trace and Ints Enabled flags
+ and [bp + 1Ch],NOT 0100h ;Clear TF. We don't want to trace here
+ pop es ;Clear the DPMI stack
+ pop ds
+ popa
+ pop bp
+ pop bx
+ add sp,14 ;Clear extra allocated space
+ retf
+
+ ;** The user stack is bad, so we want to stay on the fault handler
+ ;** stack. In order to do this, we have to build a frame for
+ ;** the callback directly on the fault handler stack.
+ ;** We build the frame here and jump to it.
+ ;** ------------
+ ;** | ES |
+ ;** | DS |
+ ;** | PUSHA |
+ ;** BP-->| Old BP | [BP + 00h]
+ ;** | BX | [BP + 02h]
+ ;** | Empty | [BP + 04h]
+ ;** |Our Ret IP| [BP + 06h] ; Client callback addr
+ ;** |Our Ret CS| [BP + 08h]
+ ;** | Ret IP | [BP + 0Ah] ; TOOLHELP restart addr
+ ;** | Ret CS | [BP + 0Ch]
+ ;** | AX | [BP + 0Eh] ; Saved AX for MPI
+ ;** |Exception#| [BP + 10h] ; Exception number
+ ;** | Handle | [BP + 12h] ; TOOLHELP handle
+ ;** | IP | [BP + 14h] ; IRET frame of fault
+ ;** | CS | [BP + 16h]
+ ;** | Flags | [BP + 18h]
+ ;** | SP | [BP + 1Ah] ; Faulting SS:SP
+ ;** | SS | [BP + 1Ch]
+ ;** | Ret IP | [BP + 1Eh] ; DPMI return address
+ ;** | Ret CS | [BP + 20h]
+ ;** ------------
+PubLabel DIH_BadStack
+ mov dx,[bp + 12h] ;DPMI return CS:IP
+ mov cx,[bp + 14h] ; stored in CX:DX
+ mov ax,[bp + 18h] ;Faulting IP
+ mov [bp + 14h],ax
+ mov ax,[bp + 1Ah] ;Faulting CS
+ mov [bp + 16h],ax
+ mov ax,[bp + 1Ch] ;Flags
+ mov [bp + 18h],ax
+ mov ax,[bp + 1Eh] ;Faulting SP
+ mov [bp + 1Ah],ax
+ mov ax,[bp + 20h] ;Faulting SS
+ mov [bp + 1Ch],ax
+ mov [bp + 1Eh],dx ;DPMI ret IP
+ mov [bp + 20h],cx ;DPMI ret CS
+ mov [bp + 12h],si ;Point to INTERRUPT struct
+ mov ax,[bx].ii_wNumber ;Get the interrupt number
+ or ax,BAD_STACK_FLAG ;Flag the client that stack is bad
+ mov [bp + 10h],ax
+ mov ax,[bp - 02h] ;Get the AX value from the PUSHA frame
+ mov [bp + 0Eh],ax
+ mov [bp + 0Ch],cs ;Point to callback return address
+ mov [bp + 0Ah],OFFSET _TEXT:DIH_CallbackRestart
+ mov ax,WORD PTR [si].i_lpfn ;Point to the user callback OFFSET
+ mov [bp + 06h],ax
+ mov ax,WORD PTR [si].i_lpfn + 2 ;Point to callback segment
+ mov [bp + 08h],ax
+ pop es ;Clear the stack
+ pop ds
+ popa
+ pop bp
+ pop bx
+ add sp,2
+ retf ;Jump to the user callback
+
+ ;** At this point, DPMI IRETs back to us instead of to the faulting
+ ;** app. We have to now create a stack frame identical to the
+ ;** frame used by interrupt-style hooks. Note that we have
+ ;** already allocated the frame space (but not initialized it)
+ ;** before returning to DPMI.
+ ;** It will look like this:
+ ;** ------------
+ ;** | ES | [BP - 14h]
+ ;** | DS | [BP - 12h]
+ ;** | DI | [BP - 10h]
+ ;** | SI | [BP - 0Eh]
+ ;** | BP | [BP - 0Ch]
+ ;** | SP | [BP - 0Ah]
+ ;** | BX | [BP - 08h]
+ ;** | DX | [BP - 06h]
+ ;** | CX | [BP - 04h]
+ ;** | AX | [BP - 02h]
+ ;** BP---->| Old BP | [BP + 00h]
+ ;** | BX | [BP + 02h]
+ ;** |Our Ret IP| [BP + 04h]
+ ;** |Our Ret CS| [BP + 06h]
+ ;** | Ret IP | [BP + 08h]
+ ;** | Ret CS | [BP + 0Ah]
+ ;** | AX | [BP + 0Ch]
+ ;** |Exception#| [BP + 0Eh]
+ ;** | Handle | [BP + 10h]
+ ;** | IP | [BP + 12h]
+ ;** | CS | [BP + 14h]
+ ;** | Flags | [BP + 16h]
+ ;** ------------
+ ;**
+PubLabel DIH_DPMIRestart
+ push bx ;Save this register we're using
+ push bp ;Make a stack frame
+ mov bp,sp
+ pusha ;Save all the registers
+ push ds
+ push es
+ mov bx,[bp + 0Eh] ;Get the INT_INFO pointer back
+ mov si,[bp + 10h] ;Get the INTERRUPT structure back
+ mov ax,_DATA ;Get our data segment
+ mov ds,ax
+
+ ;** We can now proceed with joint processing as we've matched the
+ ;** DOS interrupt frame
+PubLabel DIH_SkipDPMI
+
+ ;** Build our return frame and jump to the user callback
+ mov [bp + 10h],si ;Point to INTERRUPT struct
+ mov ax,[bx].ii_wNumber ;Get the interrupt number
+ mov [bp + 0Eh],ax ;Put on frame
+ mov ax,[bp - 02h] ;Get the AX value from the PUSHA frame
+ mov [bp + 0Ch],ax ;Put on frame
+ mov [bp + 0Ah],cs ;Point to callback return address
+ mov [bp + 08h],OFFSET _TEXT:DIH_CallbackRestart
+ mov ax,WORD PTR [si].i_lpfn ;Point to the user callback OFFSET
+ mov [bp + 04h],ax
+ mov ax,WORD PTR [si].i_lpfn + 2 ;Point to callback segment
+ mov [bp + 06h],ax
+ pop es ;Clear the stack
+ pop ds
+ popa
+ pop bp
+ pop bx
+ retf ;Jump to the user callback
+
+ ;** When the callback returns, we have to know how to call the
+ ;* next matching callback or to chain on the interrupt list.
+ ;* We have to do a raft of special stuff if this was an
+ ;* exception so that the chained on handlers think it was
+ ;** DPMI that called them.
+PubLabel DIH_CallbackRestart
+ sub sp,8 ;Leave room for ret addresses
+ push bx ;For compat. with the above code
+ push bp ;Make the same stack frame
+ mov bp,sp
+ pusha
+ push ds
+ push es
+
+ ;** Get the next matching item on the list
+ mov ax,_DATA ;Get our data segment
+ mov ds,ax
+ mov ax,[bp + 0Ch] ;Get the saved AX value
+ mov [bp - 02h],ax ;Put in PUSHA frame
+ mov si,[bp + 10h] ;Get the last handle used
+ or si,si ;If NULL, app has messed with it
+ jz DIH_NukeIt ;Nuke the app--it did a no-no
+ mov si,[si].i_pNext ;Get the next item in the list
+ or si,si ;End of the line?
+ jz DIH_NextNotFound ;Yes, chain on
+ mov ax,[bp + 0Eh] ;Get the exception number
+ cCall InterruptInfo ;Get the INT_INFO structure
+ or ax,ax ;If NULL return, user messed with #
+ jz DIH_NukeIt ; so nuke it
+ mov bx,ax ;Point with BX
+ jmp DIH_SkipDPMI ;Process this one
+
+ ;** If we don't find a match, we pass on to previous handler
+PubLabel DIH_NextNotFound
+ mov ax,[bp + 0Eh] ;Get the exception number
+ and ax,7fffh ;Clear the new stack bit
+ cCall InterruptInfo ;Find the INT_INFO structure
+ or ax,ax ;If the user messed with it,
+ jz DIH_NukeIt ; nuke the app.
+ mov si,ax ;Get the pointer in AX
+ test wTHFlags,TH_Win30StdMode ;3.0 Std mode?
+ jnz DIH_30StdChainOn ;Always do normal chain on in 3.0sm
+ cmp [si].ii_wType,I_INTERRUPT ;Was this an interrupt?
+ je DIH_ChainOn ;Yes, do normal chain on
+ jmp DIH_EmulateDPMI ;No, do the DPMI chain on
+
+PubLabel DIH_NukeIt
+ push [bp + 16h] ;Copy the IRET frame for KERNEL
+ push [bp + 14h]
+ push [bp + 12h]
+
+ push 0 ;Nuke current task
+ push UAE_BOX OR GIVE_WDEB386 ;UAE box + give to wdeb
+ push cs ;Simulate a far jump
+ call NEAR PTR TerminateApp ;Nuke the app
+
+ ;** We only get here if WDEB386 is installed. We tell it to set
+ ;* a breakpoint, then restart the app, in effect giving
+ ;* control to WDEB386. Unfortunately, at this point, all
+ ;** fault handlers for this task have been removed
+ add sp,6 ;Clear fake IRET frame
+ mov cx,[bp + 14h] ;Faulting CS
+ mov bx,[bp + 12h] ;Faulting IP
+ mov ax, 40h ;16 bit forced go command
+ int 41h ;Call debugger
+ pop es ;Restore registers and clear stack
+ pop ds
+ popa
+ pop bp
+ pop bx
+ add sp,14 ;Clear extra words
+ ; all that remains is IRET frame
+ iret ;WDEB386 will get control
+
+PubLabel DIH_NukeApp
+ push 0 ;Nuke current task
+ push UAE_BOX ;Draw the UAE box
+ push cs ;Simulate a far jump
+ call NEAR PTR TerminateApp ;Nuke the app
+ int 1 ;Should never return
+ jmp SHORT DIH_ChainOn
+
+ ;** In 3.0 standard mode we have to put an error code on the stack
+ ;** if it's a GP fault or. If not, we just chain on normally
+PubLabel DIH_30StdChainOn
+ cmp si,OFFSET GP_Info ;Is this a GP fault?
+ jne DIH_ChainOn ;No, handle normally
+ mov ax,WORD PTR [si].ii_dwChain ;Get the LOWORD
+ mov dx,WORD PTR [si].ii_dwChain + 2 ;Get HIWORD
+ mov bx,ax ;Save the LOWORD
+ or ax,dx ;Is there a chain on address?
+ jz DIH_NoChainAddr ;No, just restart the instruction
+ mov [bp + 0Ch],bx ;Put on stack so we can retf to it
+ mov [bp + 0Eh],dx
+ mov WORD PTR [bp + 10h],0 ;Zero the error code
+ pop es ;Restore registers and clear stack
+ pop ds
+ popa
+ pop bp
+ pop bx
+ add sp,8 ;Clear extra words
+ retf
+
+PubLabel DIH_ChainOn
+ mov ax,WORD PTR [si].ii_dwChain ;Get the LOWORD
+ mov dx,WORD PTR [si].ii_dwChain + 2 ;Get HIWORD
+ mov bx,ax ;Save the LOWORD
+ or ax,dx ;Is there a chain on address?
+ jz DIH_NoChainAddr ;No, just restart the instruction
+ mov [bp + 0Eh],bx ;Put on stack so we can retf to it
+ mov [bp + 10h],dx
+ pop es ;Restore registers and clear stack
+ pop ds
+ popa
+ pop bp
+ pop bx
+ add sp,10 ;Clear extra words
+ retf
+
+ ;** No chain on address was recorded so just restart the instruction
+PubLabel DIH_NoChainAddr
+ pop es
+ pop ds
+ popa
+ pop bp
+ pop bx
+ add sp,14 ;Clear all extra words
+ iret ; and restart instruction
+
+ ;** Chain on a DPMI-style exception:
+ ;**
+ ;** The goal here is to make a fault frame that appears that DPMI
+ ;** has passed the next exception handler the interrupt. We
+ ;** have only two important cases here:
+ ;** 1) We have already told DPMI the int was finished.
+ ;** 2) We have not told DPMI the int was finished and
+ ;** have not switched off the fault handler stack
+ ;** We handle the cases differently:
+ ;** -If we have already told DPMI that the fault was handled,
+ ;** we have to make a new fault so that the next handler can see
+ ;** the frame. This can be best accomplished by restarting the
+ ;** faulting instruction. This will cause the same fault to
+ ;** happen and will make the same frame.
+ ;** -In the case of us still being on the fh stack, we have to
+ ;** rebuild the frame and chain on.
+ ;** The stack we're given looks like this:
+ ;** ------------
+ ;** | ES | [BP - 14h]
+ ;** | DS | [BP - 12h]
+ ;** | DI | [BP - 10h]
+ ;** | SI | [BP - 0Eh]
+ ;** | BP | [BP - 0Ch]
+ ;** | SP | [BP - 0Ah]
+ ;** | BX | [BP - 08h]
+ ;** | DX | [BP - 06h]
+ ;** | CX | [BP - 04h]
+ ;** | AX | [BP - 02h]
+ ;** BP---->| Old BP | [BP + 00h]
+ ;** | BX | [BP + 02h]
+ ;** |Our Ret IP| [BP + 04h]
+ ;** |Our Ret CS| [BP + 06h]
+ ;** | Ret IP | [BP + 08h]
+ ;** | Ret CS | [BP + 0Ah]
+ ;** | AX | [BP + 0Ch]
+ ;** |Exception#| [BP + 0Eh]
+ ;** | Handle | [BP + 10h]
+ ;** | IP | [BP + 12h]
+ ;** | CS | [BP + 14h]
+ ;** | Flags | [BP + 16h]
+ ;** | SP | [BP + 18h] ;Only here if on fh stack
+ ;** | SS | [BP + 1Ah]
+ ;** | Ret IP | [BP + 1Ch] ;DPMI return address
+ ;** | Ret CS | [BP + 1Eh]
+ ;** ------------
+PubLabel DIH_EmulateDPMI
+ mov ax,[bp + 0Eh] ;Get the exception number
+ test ax,BAD_STACK_FLAG ;Still on fh stack?
+ jnz DIH_RebuildDPMIFrame ;Yes, rebuild the frame
+
+ ;** Rehook the exception so we're sure to get it first
+ push si ;Preserve handle
+ mov bx,ax ;Fault number in bx
+ mov wException,bx ;Save as a static also
+ mov ax,0202h ;Get exception handler - DPMI
+ int 31h ;Call DPMI
+ mov WORD PTR lpOldHandler[0],dx ;Save the old exception handler
+ mov WORD PTR lpOldHandler[2],cx
+ mov ax,0203h ;Set exception handler - DPMI
+ mov dx,OFFSET DIH_EmDPMIRestart
+ mov cx,cs ;Selector value of handler
+ int 31h ;Call DPMI
+ pop si
+
+ ;** Save the address of the next exception handler
+ mov ax,WORD PTR [si].ii_dwChain[0] ;Address to chain fault to
+ mov WORD PTR lpChainCSIP[0],ax
+ mov ax,WORD PTR [si].ii_dwChain[2]
+ mov WORD PTR lpChainCSIP[2],ax
+
+ ;** Restart the instruction. This will fault and jump to our
+ ;** newly-established handler at DIH_EmDPMIRestart
+ pop es
+ pop ds
+ popa
+ pop bp
+ pop bx
+ add sp,14 ;Clear all extra words
+ iret ; and restart instruction
+
+ ;** Now we're on the fault handler stack with a DPMI frame. Throw
+ ;** it to the next handler on the chain
+PubLabel DIH_EmDPMIRestart
+ sub sp,4 ;Enough room for a RETF frame
+ push bp
+ mov bp,sp
+ pusha
+ push ds
+ push es
+ mov ax,_DATA ;Point to TOOLHELP's DS
+ mov ds,ax
+
+ ;** Restore the exception handler
+ mov ax,0203h ;Set exception handler - DPMI
+ mov bx,wException ;Get exception number
+ mov dx,WORD PTR lpOldHandler[0] ;Get the exception handler address
+ mov cx,WORD PTR lpOldHandler[2]
+ int 31h ;Call DPMI
+
+ ;** Put the chain address on the stack so we can return to it
+ mov ax,WORD PTR lpChainCSIP[0] ;Get chain address
+ mov [bp + 02h],ax
+ mov ax,WORD PTR lpChainCSIP[2]
+ mov [bp + 04h],ax
+
+ ;** Restore registers and jump to the handler
+ pop es
+ pop ds
+ popa
+ pop bp
+ retf
+
+ ;** Since we are already on the fault handler stack, there is no
+ ;** need to fault again. All we have to do here is recreate the
+ ;** DPMI fault stack as if the fault had just occurred. It would
+ ;** be nice to clear the exception and then make it fault again,
+ ;** but since we only get here in potentially stack-faulting
+ ;** conditions, we cannot do this. We just build a reasonable
+ ;** facsimile of the frame and chain on. This frame should
+ ;** look as follows when we're done:
+ ;** ------------
+ ;** | ES | [BP - 14h]
+ ;** | DS | [BP - 12h]
+ ;** | DI | [BP - 10h]
+ ;** | SI | [BP - 0Eh]
+ ;** | BP | [BP - 0Ch]
+ ;** | SP | [BP - 0Ah]
+ ;** | BX | [BP - 08h]
+ ;** | DX | [BP - 06h]
+ ;** | CX | [BP - 04h]
+ ;** | AX | [BP - 02h]
+ ;** BP---->| Old BP | [BP + 00h]
+ ;** | BX | [BP + 02h]
+ ;** | Empty | [BP + 04h]
+ ;** | Empty | [BP + 06h]
+ ;** | Empty | [BP + 08h]
+ ;** | Empty | [BP + 0Ah]
+ ;** | Chain IP | [BP + 0Ch]
+ ;** | Chain CS | [BP + 0Eh]
+ ;** | Ret IP | [BP + 10h]
+ ;** | Ret CS | [BP + 12h]
+ ;** |Error Code| [BP + 14h] ;Always return zero
+ ;** | IP | [BP + 16h]
+ ;** | CS | [BP + 18h] ;Only here if on fh stack
+ ;** | Flags | [BP + 1Ah]
+ ;** | SP | [BP + 1Ch] ;DPMI return address
+ ;** | SS | [BP + 1Eh]
+ ;** ------------
+
+PubLabel DIH_RebuildDPMIFrame
+ mov dx,[bp + 1Ch] ;DPMI return CS:IP
+ mov cx,[bp + 1Eh] ; stored in CX:DX
+ mov ax,[bp + 1Ah] ;Faulting SS
+ mov [bp + 1Eh],ax
+ mov ax,[bp + 18h] ;Faulting SP
+ mov [bp + 1Ch],ax
+ mov ax,[bp + 16h] ;Flags
+ mov [bp + 1Ah],ax
+ mov ax,[bp + 14h] ;Faulting CS
+ mov [bp + 18h],ax
+ mov ax,[bp + 12h] ;Faulting IP
+ mov [bp + 16h],ax
+ xor ax,ax ;Error code
+ mov [bp + 14h],ax
+ mov [bp + 12h],cx ;DPMI ret CS
+ mov [bp + 10h],dx ;DPMI ret IP
+ mov ax,WORD PTR [si].ii_dwChain[2] ;Address to chain fault to
+ mov [bp + 0Eh],ax
+ mov ax,WORD PTR [si].ii_dwChain[0]
+ mov [bp + 0Ch],ax
+ pop es
+ pop ds
+ popa
+ pop bp
+ pop bx
+ add sp,8 ;Clear all extra words
+ retf
+
+cEnd NOGEN
+
+
+;** Helper functions
+
+; InterruptInfo
+; Gets a pointer to the INT_INFO structure given the interrupt
+; number. Accepts the int number in AX and returns the pointer in AX.
+; Preserves all other registers
+
+cProc InterruptInfo, <NEAR,PUBLIC>, <si,cx>
+cBegin
+ ;** Loop through all possible handlers
+ mov cx,MAX_INTERRUPT + 1 ;Number of ints to hook
+ lea si,IntInfo ;Get the address of the array
+
+ ;** Is this a match?
+II_HandlerLoop:
+ cmp [si].ii_wNumber,ax ;Match?
+ jz II_End ;Yes, return the pointer
+
+ ;** Prepare for next in table
+II_Continue:
+ add si,SIZE INT_INFO ;Bump to next entry
+ loop II_HandlerLoop ;Loop back
+ xor si,si ;Return NULL for not found
+
+II_End:
+ mov ax,si ;Get return value
+cEnd
+
+sEnd
+
+ END
diff --git a/private/mvdm/wow16/toolhelp/krnlpeek.asm b/private/mvdm/wow16/toolhelp/krnlpeek.asm
new file mode 100644
index 000000000..0e34099f9
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/krnlpeek.asm
@@ -0,0 +1,110 @@
+;***************************************************************************
+;* KRNLPEEK.ASM
+;*
+;* Assembly code used to peer into the heart of KERNEL and return
+;* information in global variables.
+;*
+;***************************************************************************
+
+ INCLUDE TOOLPRIV.INC ;Include the TOOLHELP values
+PMODE32 = 0 ;This should work either way
+PMODE = 0
+ INCLUDE WINKERN.INC
+ INCLUDE WINDOWS.INC
+
+;** Functions
+externFP GlobalMasterHandle
+externFP GlobalLock
+externFP GetVersion
+externFP GetProcAddress
+externFP GetModuleHandle
+externNP HelperHandleToSel
+
+sBegin DATA
+externB _szKernel
+sEnd DATA
+;** Functions
+
+sBegin CODE
+ assumes CS,CODE
+
+; void KernelType(void)
+;
+; Returns information from KERNEL in global variables
+
+cProc KernelType, <PUBLIC>, <si,di>
+cBegin
+ ;** Make sure we're in PMODE. TOOLHELP does not run in non-PMODE
+ ;** Windows.
+ mov ax,__WinFlags ;Get WinFlags
+ test ax,1 ;In PMODE?
+ mov wTHFlags,0 ;Zero flags indicates error
+ jnz @F ;Yes, go on
+ jmp KT_End ;No, not in PMODE, return error
+@@:
+.286
+ ;** Call the undocumented function GlobalMasterHandle to get
+ ;* a pointer to the global HeapInfo structure.
+ ;** This is the means we can use to detect the kernel types.
+
+ cCall GlobalMasterHandle
+ cCall HelperHandleToSel, <dx> ;Convert it to a selector
+ mov hMaster,ax ;Save the handle
+ mov es,ax ;Use ES to point to the block
+ mov ax,es:[hi_first] ;Get low word of first heap entry
+ test ax,01fh ;Mask out lowest 5 bits
+ jz KT_Krnl386 ;Must be Krnl386 so leave the flags
+ mov wTHFlags,TH_KERNEL_286
+ jmp SHORT KT_BothPModes ;Skip TH_KERNEL_386 stuff
+KT_Krnl386:
+ mov wTHFlags,TH_KERNEL_386
+KT_BothPModes:
+
+ ;** Now get pmode KERNEL information
+ cCall GetVersion ;Which Windows version are we on
+ mov bx,SEG GlobalLock ;KERNEL code segment selector
+ cmp ax,0003h ;Win 3.0 or Win 3.0a?
+ je KT_Win30 ;Yes
+ cmp ax,0103h ;Beta releas of Win 3.0a?
+ je KT_Win30 ;Yes
+ cmp ax,0a03h ;Win 3.1?
+ je KT_Win31 ;Yes
+ mov wTHFlags,0 ;Zero wTHFlags indicates error
+ jmp SHORT KT_End ;Unknown Windows version
+KT_Win31:
+ mov ax,seg _DATA
+ mov dx,offset _DATA:_szKernel
+ cCall GetModuleHandle,<ax,dx>
+ cCall GetProcAddress,<ax,0,332> ; DX:AX -> hGlobalHeap
+ mov segKernel,dx ;Save for later
+ mov es,dx ;Point with ES
+ add ax,4
+ mov npwExeHead,ax
+ add ax,10
+ mov npwTDBHead,ax
+ add ax,2
+ mov npwTDBCur,ax
+ add ax,6
+ mov npwSelTableLen,ax
+ add ax,2
+ mov npdwSelTableStart,ax
+ jmp SHORT KT_End ;Skip the 3.0 std mode check
+KT_Win30:
+ or wTHFlags,TH_WIN30 ;Flag we're in Win30
+ mov npwSelTableLen,0324h ;Correct values for 3.0
+ mov npdwSelTableStart,0326h
+ mov ax,__WinFlags ;Get the WinFlags variable
+ mov segKernel,bx ;Save for later
+ mov npwExeHead,0014h ;Save the offsets of these vars
+ mov npwTDBHead,001eh
+ mov npwTDBCur,0020h
+ test ax,WF_STANDARD ;3.0 Standard mode?
+ jz KT_End ;No.
+ or wTHFlags,TH_WIN30STDMODE ;Yes
+.8086
+KT_End:
+
+cEnd
+
+sEnd
+ END
diff --git a/private/mvdm/wow16/toolhelp/local.c b/private/mvdm/wow16/toolhelp/local.c
new file mode 100644
index 000000000..34285cee1
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/local.c
@@ -0,0 +1,138 @@
+/**************************************************************************
+ * LOCAL.C
+ *
+ * Routines used to walk local heaps
+ *
+ **************************************************************************/
+
+#include "toolpriv.h"
+
+/* ----- Function prototypes ----- */
+
+ NOEXPORT void NEAR PASCAL ComputeType(
+ LOCALENTRY FAR *lpLocal);
+
+/* LocalInfo
+ * Reports information about the state of the indicated heap
+ */
+
+BOOL TOOLHELPAPI LocalInfo(
+ LOCALINFO FAR *lpLocalInfo,
+ HANDLE hHeap)
+{
+ /* Check the version number and verify proper installation */
+ if (!wLibInstalled || !lpLocalInfo ||
+ lpLocalInfo->dwSize != sizeof (LOCALINFO))
+ return FALSE;
+
+ /* Get the item counts */
+ if (wTHFlags & TH_KERNEL_386)
+ lpLocalInfo->wcItems = WalkLoc386Count(hHeap);
+ else
+ lpLocalInfo->wcItems = WalkLoc286Count(hHeap);
+
+ return TRUE;
+}
+
+/* LocalFirst
+ * Finds the first block on a local heap.
+ */
+
+BOOL TOOLHELPAPI LocalFirst(
+ LOCALENTRY FAR *lpLocal,
+ HANDLE hHeap)
+{
+ WORD wFirst;
+
+ /* Check the version number and verify proper installation */
+ if (!wLibInstalled || !lpLocal || lpLocal->dwSize != sizeof (LOCALENTRY))
+ return FALSE;
+
+ /* Convert the heap variable to a selector */
+ hHeap = HelperHandleToSel(hHeap);
+
+ /* Get the first item from the heap */
+ if (wTHFlags & TH_KERNEL_386)
+ {
+ if (!(wFirst = WalkLoc386First(hHeap)))
+ return FALSE;
+ }
+ else
+ {
+ if (!(wFirst = WalkLoc286First(hHeap)))
+ return FALSE;
+ }
+
+
+ /* Fill in other miscellaneous stuff */
+ lpLocal->hHeap = hHeap;
+
+ /* Get information about this item */
+ if (wTHFlags & TH_KERNEL_386)
+ WalkLoc386(wFirst, lpLocal, hHeap);
+ else
+ WalkLoc286(wFirst, lpLocal, hHeap);
+
+ /* Guess at the type of the object */
+ ComputeType(lpLocal);
+
+ return TRUE;
+}
+
+
+/* LocalNext
+ * Continues a local heap walk by getting information about the
+ * next item.
+ */
+
+BOOL TOOLHELPAPI LocalNext(
+ LOCALENTRY FAR *lpLocal)
+{
+ /* Check the version number and verify proper installation */
+ if (!wLibInstalled || !lpLocal || lpLocal->dwSize != sizeof (LOCALENTRY))
+ return FALSE;
+
+ if (wTHFlags & TH_KERNEL_386)
+ WalkLoc386(lpLocal->wNext, lpLocal, lpLocal->hHeap);
+ else
+ WalkLoc286(lpLocal->wNext, lpLocal, lpLocal->hHeap);
+
+ /* See if this item is the last one. If so, return done because this
+ * last item is NOT useful.
+ */
+ if (!lpLocal->wNext)
+ return FALSE;
+
+ /* Guess at the type of the object */
+ ComputeType(lpLocal);
+
+ return TRUE;
+}
+
+
+/* ComputeType
+ * Computes the object type of an object
+ */
+
+NOEXPORT void NEAR PASCAL ComputeType(
+ LOCALENTRY FAR *lpLocal)
+{
+ /* Decode the free/fixed/moveable bits */
+ if (lpLocal->wFlags & 2)
+ lpLocal->wFlags = LF_MOVEABLE;
+ else if (lpLocal->wFlags & 1)
+ lpLocal->wFlags = LF_FIXED;
+ else
+ {
+ /* Free blocks never have a unique type so return */
+ lpLocal->wFlags = LF_FREE;
+ lpLocal->wType = LT_FREE;
+ lpLocal->hHandle = NULL;
+ return;
+ }
+
+ /* Decode the heap type if possible */
+ UserGdiType(lpLocal);
+}
+
+
diff --git a/private/mvdm/wow16/toolhelp/makefile b/private/mvdm/wow16/toolhelp/makefile
new file mode 100644
index 000000000..7f99d2f88
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/makefile
@@ -0,0 +1,168 @@
+# TOOLHELP.DLL for WOW makefile
+#
+# Copyright (c) 1992, Microsoft Corporation
+#
+# History:
+# 4-Nov-1992 Dave Hart (davehart)
+# Created.
+#
+
+#
+# Macros for build utilities
+#
+
+#RC16 = .\rc
+RC16 = rc16 # use after rc16 is Win 3.1 flavor, and delfile rc*
+CL16 = cl16
+MASM16 = masm
+LINK16 = link16
+MAPSYM16 = mapsym
+IMPLIB16 = implib
+MKPUB16 = mkpublic
+
+#
+# Command line options common to C compiler and assembler
+#
+
+DEFINES = -DWOW $(MVDMFLAGS)
+INCLUDES = -I..\inc -I..\..\inc -I..\kernel31
+
+#
+# C, MASM, Link16, and RC16 options
+#
+
+########## Path definition so we find 16 bit tools ##########
+# Also works around stupid bug in RC 3.1 that doesn't allow rcpp.err to be
+# in a directory that is greater than 128 chars down the path, even if
+# rc 3.1 is running as an OS/2 app.
+
+PATH = $(_NTBINDIR)\private\mvdm\tools16;$(PATH)
+
+!if "$(NTDEBUG)"!="" && "$(NTDEBUG)"!="retail" && "$(NTDEBUG)" != "ntsdnodbg"
+ADEBUG = -Zd
+CDEBUG = /Od /Oi /Zd
+LDEBUG = /LI
+!endif
+
+CFLAGS = -c -ASw -G2s -Oas -W3 -Zpe $(DEFINES) $(INCLUDES) $(CDEBUG)
+AFLAGS = -DmemS=1 -w2 $(DEFINES) $(INCLUDES) $(ADEBUG)
+LFLAGS = /ALIGN:16 $(LDEBUG)
+RFLAGS = $(INCLUDES)
+
+#
+# Libraries to link with.
+#
+
+W16LIBS = ..\lib\sdllcew.LIB ..\lib\LIBW.LIB
+
+#
+# Target objects (keep in sync with dependencies below)
+#
+
+OBJS = toolhelp.obj dllentry.obj global.obj krnlpeek.obj \
+ walk386.obj local.obj module.obj task1.obj task2.obj stack1.obj \
+ stack2.obj usergdi1.obj usergdi2.obj memman.obj helper.obj \
+ walk286.obj notify1.obj notify2.obj int1.obj int2.obj terminat.obj \
+ signal.obj memory.obj timer.obj ththunks.obj
+
+
+#
+# Common build pseudotargets:
+# all builds everything - must be be first target in file
+# cleanup deletes everything
+# clean deletes & then builds everything
+#
+
+all: toolhelp.dll toolhelp.sym toolhelp.map toolhelp.lib
+ -binplace toolhelp.dll
+ -binplace toolhelp.map
+ -binplace toolhelp.sym
+
+clean: cleanup all
+
+cleanup:
+ if exist *.obj del *.obj
+ if exist *.dll del *.dll
+ if exist *.map del *.map
+ if exist *.sym del *.sym
+ if exist *.res del *.res
+ if exist *.lib del *.lib
+ if exist stripped.def del stripped.def
+
+#
+# Default build rules.
+#
+
+.c.obj:
+ $(CL16) $(CFLAGS) $*.c
+
+.asm.obj:
+ $(MASM16) $(AFLAGS) $*.asm;
+
+.asm.lst:
+ $(MASM16) $(AFLAGS) -l $*.asm,nul,$*.lst;
+
+.rc.res:
+ $(RC16) $(RFLAGS) -r $*.rc
+
+.def.lib:
+ $(IMPLIB16) $@ $**
+
+#
+# Dependencies that use default build rules.
+#
+
+# NOTE: Class1.c and Class2.asm are checked in for reference but are not
+# ---- built for WOW. Instead THTHUNKS.ASM contains thunks to WOW32.
+#
+# class1.obj: class1.c toolpriv.h toolhelp.h
+# class2.obj: class2.asm toolpriv.inc toolhelp.inc
+dllentry.obj: dllentry.asm
+global.obj: global.c toolpriv.h toolhelp.h string.h
+helper.obj: helper.asm toolpriv.inc toolhelp.inc
+int1.obj: int1.c toolpriv.h toolhelp.h string.h
+int2.obj: int2.asm toolpriv.inc toolhelp.inc
+krnlpeek.obj: krnlpeek.asm toolpriv.inc toolhelp.inc
+local.obj: local.c toolpriv.h toolhelp.h
+memman.obj: memman.asm toolpriv.inc toolhelp.inc
+memory.obj: memory.asm toolpriv.inc toolhelp.inc
+module.obj: module.c toolpriv.h toolhelp.h string.h
+notify1.obj: notify1.c toolpriv.h toolhelp.h string.h
+notify2.obj: notify2.asm toolpriv.inc toolhelp.inc
+signal.obj: signal.c toolpriv.h toolhelp.h string.h
+stack1.obj: stack1.c toolpriv.h toolhelp.h string.h
+stack2.obj: stack2.asm toolpriv.inc toolhelp.inc
+task1.obj: task1.c toolpriv.h toolhelp.h string.h
+task2.obj: task2.asm toolpriv.inc toolhelp.inc
+terminat.obj: terminat.asm toolpriv.inc toolhelp.inc
+ththunks.obj: ththunks.asm ..\..\inc\wow.inc ..\..\inc\wowth.inc
+timer.obj: timer.asm toolpriv.inc toolhelp.inc
+toolhelp.obj: toolhelp.c toolpriv.h toolhelp.h
+usergdi1.obj: usergdi1.c toolpriv.h toolhelp.h
+usergdi2.obj: usergdi2.asm toolpriv.inc toolhelp.inc
+walk286.obj: walk286.asm toolpriv.inc toolhelp.inc
+walk386.obj: walk386.asm toolpriv.inc toolhelp.inc
+
+#
+# Targets with specialized build rules
+#
+
+toolhelp.dll: $(OBJS) toolhelp.def
+ $(LINK16) $(LFLAGS) @<<
+ toolhelp + dllentry + krnlpeek + global + walk386 +
+ local + module + task1 + task2 + stack1 + stack2 +
+ usergdi1 + usergdi2 + memman + helper + walk286 +
+ notify1 + notify2 + int1 + int2 + terminat + signal +
+ memory + timer + ththunks
+ toolhelp.dll
+ toolhelp.map/map
+ $(W16LIBS) /NOE/NOD
+ toolhelp.def;
+
+<<
+ $(MAPSYM16) toolhelp.map
+ $(RC16) $(RFLAGS) -t -30 toolhelp.rcv toolhelp.dll
+
+toolhelp.lib: toolhelp.def
+ $(MKPUB16) toolhelp.def stripped.def
+ $(IMPLIB16) toolhelp.lib stripped.def
diff --git a/private/mvdm/wow16/toolhelp/memman.asm b/private/mvdm/wow16/toolhelp/memman.asm
new file mode 100644
index 000000000..0c75f04b5
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/memman.asm
@@ -0,0 +1,88 @@
+;**************************************************************************
+;* MEMMAN.ASM
+;*
+;* Returns information about the VMM.
+;*
+;**************************************************************************
+
+ INCLUDE TOOLPRIV.INC
+
+
+sBegin CODE
+ assumes CS,CODE
+
+; MemManInfo
+;
+; Returns information through DPMI about the VMM
+
+cProc MemManInfo, <PUBLIC,FAR>, <si,di,ds>
+ parmD lpMemMan
+ localV DPMIBuffer,30h ;30h byte buffer for DPMI info
+cBegin
+ mov ax,_DATA ;Get our data segment
+ mov ds,ax
+
+ ;** Fill the buffer with -1 so if the call messes up like
+ ;** in 3.0 std mode we get the correct results
+ push ss ;Point to the local variable block
+ pop es
+ lea di,DPMIBuffer ;Get offset of buffer
+ mov cx,30h ;Max len of DPMI buffer
+ mov al,0ffh ;-1
+ rep stosb ;Fill buffer
+
+ ;** Prepare to build public structure
+ mov ax,0500h ;DPMI -- Get Free Memory Info
+ lea di,DPMIBuffer ;Get offset of buffer
+ int 31h ;Call DPMI
+ jnc MMI_10 ;Success
+ xor ax,ax ;Return FALSE
+ jmp MMI_End ;Get out because of DPMI error
+MMI_10: lds si,lpMemMan ;Point to the MEMMANINFO structure
+
+ ;** Fill MEMMANINFO structure
+ mov ax,es:[di+0] ;Loword of largest free block
+ mov WORD PTR [si].vmm_dwLargestFreeBlock,ax
+ mov ax,es:[di+2] ;High word
+ mov WORD PTR [si].vmm_dwLargestFreeBlock + 2,ax
+ mov ax,es:[di+4] ;Loword of largest unlockable block
+ mov WORD PTR [si].vmm_dwMaxPagesAvailable,ax
+ mov ax,es:[di+6] ;Hiword
+ mov WORD PTR [si].vmm_dwMaxPagesAvailable + 2,ax
+ mov ax,es:[di+8] ;Loword of largest lockable page
+ mov WORD PTR [si].vmm_dwMaxPagesLockable,ax
+ mov ax,es:[di+0ah] ;Hiword
+ mov WORD PTR [si].vmm_dwMaxPagesLockable + 2,ax
+ mov ax,es:[di+0ch] ;Loword of linear address space
+ mov WORD PTR [si].vmm_dwTotalLinearSpace,ax
+ mov ax,es:[di+0eh] ;Hiword
+ mov WORD PTR [si].vmm_dwTotalLinearSpace + 2,ax
+ mov ax,es:[di+10h] ;Loword of number of unlocked pages
+ mov WORD PTR [si].vmm_dwTotalUnlockedPages,ax
+ mov ax,es:[di+12h] ;Hiword
+ mov WORD PTR [si].vmm_dwTotalUnlockedPages + 2,ax
+ mov ax,es:[di+14h] ;Loword of number of free pages
+ mov WORD PTR [si].vmm_dwFreePages,ax
+ mov ax,es:[di+16h] ;Hiword
+ mov WORD PTR [si].vmm_dwFreePages + 2,ax
+ mov ax,es:[di+18h] ;Loword of total physical pages
+ mov WORD PTR [si].vmm_dwTotalPages,ax
+ mov ax,es:[di+1ah] ;Hiword
+ mov WORD PTR [si].vmm_dwTotalPages + 2,ax
+ mov ax,es:[di+1ch] ;Loword of free lin addr space (pages)
+ mov WORD PTR [si].vmm_dwFreeLinearSpace,ax
+ mov ax,es:[di+1eh] ;Hiword
+ mov WORD PTR [si].vmm_dwFreeLinearSpace + 2,ax
+ mov ax,es:[di+20h] ;Loword of size of paging file (pages)
+ mov WORD PTR [si].vmm_dwSwapFilePages,ax
+ mov ax,es:[di+22h] ;Hiword
+ mov WORD PTR [si].vmm_dwSwapFilePages + 2,ax
+ mov [si].vmm_wPageSize,4096 ;Safe to hard code this for 386/486
+ mov ax,TRUE ;Return TRUE
+
+MMI_End:
+
+cEnd
+
+sEnd
+ END
diff --git a/private/mvdm/wow16/toolhelp/memory.asm b/private/mvdm/wow16/toolhelp/memory.asm
new file mode 100644
index 000000000..1227218f6
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/memory.asm
@@ -0,0 +1,608 @@
+ PAGE 60,150
+;***************************************************************************
+;* MEMORY.ASM
+;*
+;* Routines used to handle the read/write random memory API
+;*
+;***************************************************************************
+
+ INCLUDE TOOLPRIV.INC
+ INCLUDE WINDOWS.INC
+
+;** Symbols
+SI_CRITICAL EQU 1
+DI_CRITICAL EQU 2
+
+;** Imports
+externA __AHINCR
+externFP GlobalEntryHandle
+externNP HelperHandleToSel
+
+sBegin CODE
+ assumes CS,CODE
+ assumes DS,DATA
+
+; MemoryRead
+; Uses the passed in selector and offset to read memory into a user-
+; specified buffer. This works for >64K segments and, if code, may
+; have been discarded.
+;
+; This function is normally used for heap selectors. However, if
+; a non-global heap selector is used, it must be less than 64K on
+; a 286.
+;
+; Prototype:
+; DWORD MemoryRead(
+; WORD wSel, /* Selector to read from */
+; DWORD dwOffset, /* Offset to read at */
+; LPSTR lpBuffer, /* Buffer to put data into */
+; DWORD dwcb) /* Number of characters to read */
+; Returns number of characters read (ends at segment limit)
+
+cProc MemoryRead, <FAR,PUBLIC>, <si,di,ds>
+ parmW wSelector
+ parmD dwOffset
+ parmD lpBuffer
+ parmD dwcb
+ localD dwcbCopied
+ localV Global,<SIZE GLOBALENTRY>
+cBegin
+ ;** Make sure the segment is present. We only will fault the
+ ;** segment in if it is a code segment
+ cCall HelperHandleToSel, <wSelector> ;Convert to sel from handle
+ mov wSelector, ax ;Save it so we have a good sel
+ mov cx, ax
+ push WORD PTR lpBuffer[2] ;Convert handle to selector
+ cCall HelperHandleToSel
+ mov WORD PTR lpBuffer[2], ax ;Save converted handle
+ lar ax,cx ;Get the access rights
+ jnz MR_ShortBad ;Failed. It's bad
+ test ax,8000h ;Is it present?
+ jnz MR_Present ;Yes
+ test ax,0800h ;This bit set for code segments
+ jnz MR_FaultIn ;Code segment, fault it in
+MR_ShortBad:
+ jmp MR_Bad ;Return error
+MR_FaultIn:
+ mov es,wSelector ;Get the selector in ES.
+ mov al,es:[0] ;Must be at least one byte long
+MR_Present:
+
+ ;** Check this block's length. We use the global heap functions
+ ;* to do this because they check in the arena for the length.
+ ;* This is the only way to get lengths of 286 heap blocks
+ ;** beyond 64K.
+ mov ax,SIZE GLOBALENTRY ;Get the size of the structure
+ mov WORD PTR Global.ge_dwSize[0],ax ;Save in the structure
+ mov WORD PTR Global.ge_dwSize[2],0 ;Clear the HIWORD
+ lea ax,Global ;Point to the structure
+ cCall GlobalEntryHandle, <ss,ax,wSelector>
+ or ax,ax ;Was this a valid selector?
+ jnz MR_HeapSel ;Yes, this is a heap selector
+
+ ;** If this wasn't a heap selector, we get the length with an LSL.
+ ;** When used like this, 64K is the max on a 286
+MR_NonHeap:
+ mov bx,wSelector ;Get the selector
+ mov ax,__WinFlags ;Get the flags
+ test ax,WF_CPU286 ;286?
+ jz MR_32BitSize ;No, do 32 bit size stuff
+ lsl dx,bx ;Get length in DX
+ mov WORD PTR Global.ge_dwBlockSize[0],dx ;Put in GLOBALENTRY struct
+ mov WORD PTR Global.ge_dwBlockSize[2],0
+ jmp SHORT MR_HeapSel
+MR_32BitSize:
+.386p
+ lsl edx,ebx
+ mov Global.ge_dwBlockSize,edx ;Put in GLOBALENTRY struct for later
+.286p
+
+MR_HeapSel:
+ mov dx,WORD PTR dwOffset[2] ;Get the HIWORD of segment offset
+ cmp dx,WORD PTR Global.ge_dwBlockSize[2] ;Check HIWORD of size
+ jb MR_OK ;Offset should be OK
+ je @F ;Equal. Must check LOWORD
+ jmp MR_Bad ;Offset is not inside segment
+@@: mov ax,WORD PTR dwOffset[0] ;Get the LOWORD of segment offset
+ cmp ax,WORD PTR Global.ge_dwBlockSize[0] ;Check LOWORD of size
+ jb MR_OK ;It's inside segment
+ jmp MR_Bad ;Not inside segment
+MR_OK:
+
+ ;** Do different stuff on 286 and 386/486
+ mov ax,__WinFlags ;Get the flags
+ test ax,WF_CPU286 ;286?
+ jnz MR_Do16Bit ;Yes, do 16 bit stuff
+
+ ;** Do this the 386 way (easy)
+.386p
+ mov ax,wSelector ;Point with DS
+ mov ds,ax ; (keep copy in AX)
+ mov esi,dwOffset ;Point into the big segment
+ mov ecx,dwcb ;Get the copy length
+ lsl edx,eax ;Get the segment limit
+ sub edx,esi ;Get distance from offset to limit
+ inc edx ;Make this the real length
+ cmp ecx,edx ;Are we within the limit?
+ jbe SHORT MR_LimitOK ;Yes
+ mov ecx,edx ;No, so make this the copy amount
+MR_LimitOK:
+ mov edx,ecx ;Get the # of bytes to read for ret
+ xor edi,edi ;Clear the high word
+ les di,lpBuffer ;Point to the dest. buffer
+ mov ax,cx ;Save the low bits of ECX
+ shr ecx,2 ;Prepare for DWORD move
+ jecxz @F ;No zero-length DWORD moves!
+ rep movs DWORD PTR [edi],DWORD PTR [esi]
+ db 67h ;Handles 386 bug
+ db 90h
+@@: mov cx,ax ;Get a copy
+ jecxz @F ;Don't do zero!
+ and cx,03 ;Do the remaining 3,2, or 1
+ rep movs BYTE PTR [edi], BYTE PTR [esi]
+ db 67h ;Handles 386 bug
+ db 90h
+@@: mov ax,dx ;Bytes copied returned in DX:AX
+ shr edx,16
+ jmp MR_End ;Get out
+.286p
+
+ ;** Do this the 286 way (hard)
+MR_Do16Bit:
+
+ ;** Compute the actual copy length
+ mov ax,WORD PTR Global.ge_dwBlockSize[0] ;Get the segment size
+ mov dx,WORD PTR Global.ge_dwBlockSize[2]
+ sub ax,WORD PTR dwOffset[0] ;Get distance from offset to limit
+ sbb dx,WORD PTR dwOffset[2]
+ cmp dx,WORD PTR dwcb[2] ;Off end of heap block?
+ ja MR_LimOk ;No, just do it
+ jb MR_Truncate ;Yes, must truncate our length
+ cmp ax,WORD PTR dwcb[0] ;Are we off the end?
+ jae MR_LimOk ;No, just do it
+MR_Truncate:
+ mov WORD PTR dwcb[0],ax ;Force this to be the new length
+ mov WORD PTR dwcb[2],dx
+MR_LimOk:
+
+ ;** Save the number of bytes to be copied for the return value
+ mov ax,WORD PTR dwcb[0] ;Get the LOWORD
+ mov WORD PTR dwcbCopied[0],ax ;Save it
+ mov ax,WORD PTR dwcb[2] ;Get the HIWORD
+ mov WORD PTR dwcbCopied[2],ax ;Save it
+
+ ;** Position the initial copying selectors
+ mov al,BYTE PTR dwOffset[2] ;Grab the HIWORD (286 is only 24 bits)
+ mov ah,__AHINCR ;Get the selector inc value
+ mul ah ;Multiply to get sel offset
+ add ax,wSelector ;AX = sel in sel array
+ mov ds,ax ;Point to this with DS
+ mov si,WORD PTR dwOffset[0] ;Get the current pointers
+ les di,lpBuffer
+
+ ;** This is the main copying loop
+MR_CopyLoop:
+
+ ;** Compute the size of this block copy. This is done by finding the
+ ;* smaller of the following quantities:
+ ;* - Distance to end of source segment
+ ;* - Distance to end of dest. segment
+ ;** - Distance to end of copy
+ xor bx,bx ;Flags start at zero
+ xor cx,cx ;Get the highest segment value (64K)
+ cmp di,si ;The bigger of the two will win
+ je MR_Equal ;They're the same
+ ja MR_DIBigger ;DI is bigger
+ sub cx,si ;SI bigger, compute dist to end
+ or bx,SI_CRITICAL ;Flag set for SI-critical
+ jmp SHORT MR_CheckEndCopy ;Go on
+MR_Equal:
+ sub cx,di ;Use DI (SI and DI are the same)
+ or bx,SI_CRITICAL OR DI_CRITICAL ;Both will come true
+ jmp SHORT MR_CheckEndCopy ;Go on
+MR_DIBigger:
+ sub cx,di ;SI is bigger
+ or bx,DI_CRITICAL ;Flag clear for DI-critical
+MR_CheckEndCopy:
+ cmp WORD PTR dwcb[2],0 ;Check for less than 64K left
+ ja MR_DoCopy ;Nope. More than 64K left
+ jcxz MR_GetSize ;CX = 0 is full 64K segment
+ cmp WORD PTR dwcb[0],cx ;Less than in either segment left?
+ ja MR_DoCopy ;No. Do it
+MR_GetSize:
+ mov cx,WORD PTR dwcb[0] ;Get in CX
+MR_DoCopy:
+
+ ;** Do this block of 64K or less.
+ mov dx,cx ;Save the number of bytes we did
+ jcxz @F ;Do 64K
+ shr cx,1 ;Do WORDS
+ jmp SHORT MR_10 ;Skip over
+@@: mov cx,8000h ;32K WORDS
+MR_10: jcxz @F ;No zero length WORD moves!
+ rep movsw ;Do the copy
+@@: mov cx,dx ;Get any remaining bits
+ and cx,1 ;Any more to do?
+ jcxz @F ;No, don't do it
+ movsb ;Do the odd byte if necessary
+@@: mov cx,dx ;Get back in CX
+
+ ;** Bump the loop pointers
+ jcxz MR_BigCount ;We did 64K
+ sub WORD PTR dwcb[0],cx ;Subtract the bytes done
+ sbb WORD PTR dwcb[2],0 ; and don't forget the HIWORD
+ jmp SHORT MR_20 ;Continue
+MR_BigCount:
+ sub WORD PTR dwcb[2],1 ;Subtract 64K
+MR_20: mov ax,WORD PTR dwcb[0] ;We're done if the count of bytes
+ or ax,WORD PTR dwcb[2] ; is zero
+ jnz @F ;Not zero, go on
+ mov dx,WORD PTR dwcbCopied[2] ;Get the return count
+ mov ax,WORD PTR dwcbCopied[0]
+ jmp SHORT MR_End ;Get out
+@@: test bx,SI_CRITICAL ;Does SI need incrementing?
+ jz MR_TestDI ;No, try DI
+ mov ax,ds ;Get the segment value
+ add ax,__AHINCR ;Bump to next selector
+ mov ds,ax ;Point with DS still
+ xor si,si ;Point to start of this segment
+MR_TestDI:
+ test bx,DI_CRITICAL ;Does SI need incrementing?
+ jz MR_Continue ;No, try DI
+ mov ax,es ;Get the segment value
+ add ax,__AHINCR ;Bump to next selector
+ mov es,ax ;Point with DS still
+ xor di,di ;Point to start of this segment
+MR_Continue:
+ jmp MR_CopyLoop ;Do it again
+
+MR_Bad:
+ xor ax,ax ;Return DWORD 0
+ cwd
+
+MR_End:
+
+cEnd
+
+
+; MemoryWrite
+; Uses the passed in selector and offset to write memory from a user-
+; specified buffer. This works for >64K segments and, if code, may
+; have been discarded. The selector may be a selector or a handle
+; but MUST be on the global heap (no aliases or selector array
+; members). If worried about low memory conditions, lpBuffer should
+; be in a (temporarily) fixed segment.
+;
+; Prototype:
+; DWORD MemoryWrite(
+; WORD wSel, /* Selector to read from */
+; DWORD dwOffset, /* Offset to read at */
+; LPSTR lpBuffer, /* Buffer to put data into */
+; DWORD dwcb) /* Number of characters to read */
+; Returns number of characters read (ends at segment limit)
+
+cProc MemoryWrite, <FAR,PUBLIC>, <si,di,ds>
+ parmW wSelector
+ parmD dwOffset
+ parmD lpBuffer
+ parmD dwcb
+ localW wSelFlag
+ localD dwcbCopied
+ localV DPMISelBuf,8
+ localV Global,<SIZE GLOBALENTRY>
+cBegin
+ ;** Make sure the segment is present. We only will fault the
+ ;** segment in if it is a code segment
+ cCall HelperHandleToSel, <wSelector> ;Convert to sel from handle
+ mov wSelector, ax ;Save it
+ mov cx,ax ;Get the selector
+ push WORD PTR lpBuffer[2] ;Convert handle to selector
+ cCall HelperHandleToSel
+ mov WORD PTR lpBuffer[2], ax ;Save converted handle
+ mov wSelFlag,0 ;Clear the flag
+ lar ax,cx ;Get the access rights
+ jnz MW_ShortBad ;Failed
+ test ax,8000h ;Is it present?
+ jnz MW_Present ;Yes
+ test ax,0800h ;This bit set for code segments
+ jnz MW_FaultIn ;Code segment, fault it in
+MW_ShortBad:
+ jmp MW_Bad ;Return error
+MW_FaultIn:
+ mov es,wSelector ;Get the selector in ES.
+ mov al,es:[0] ;Must be at least one byte long
+MW_Present:
+
+ ;** Check this block's length. We use the global heap functions
+ ;* to do this because they check in the arena for the length.
+ ;* This is the only way to get lengths of 286 heap blocks
+ ;** beyond 64K.
+ mov ax,SIZE GLOBALENTRY ;Get the size of the structure
+ mov WORD PTR Global.ge_dwSize[0],ax ;Save in the structure
+ mov WORD PTR Global.ge_dwSize[2],0 ;Clear the HIWORD
+ lea ax,Global ;Point to the structure
+ cCall GlobalEntryHandle, <ss,ax,wSelector>
+ or ax,ax ;Was this a valid selector?
+ jnz MW_HeapSel ;Yes, this is a heap selector
+
+ ;** If this wasn't a heap selector, we get the length with an LSL.
+ ;** When used like this, 64K is the max on a 286
+MW_NonHeap:
+ mov bx,wSelector ;Get the selector
+ mov ax,__WinFlags ;Get the flags
+ test ax,WF_CPU286 ;286?
+ jz MW_32BitSize ;No, do 32 bit size stuff
+ lsl dx,bx ;Get length in DX
+ mov WORD PTR Global.ge_dwBlockSize[0],dx ;Put in GLOBALENTRY struct
+ mov WORD PTR Global.ge_dwBlockSize[2],0
+ jmp SHORT MW_HeapSel
+MW_32BitSize:
+.386p
+ lsl edx,ebx
+ mov Global.ge_dwBlockSize,edx ;Put in GLOBALENTRY struct for later
+.286p
+
+MW_HeapSel:
+ mov dx,WORD PTR dwOffset[2] ;Get the HIWORD of segment offset
+ cmp dx,WORD PTR Global.ge_dwBlockSize[2] ;Check HIWORD of size
+ jb MW_OK ;Offset should be OK
+ je @F ;Equal. Must check LOWORD
+ jmp MW_Bad ;Offset is not inside segment
+@@: mov ax,WORD PTR dwOffset[0] ;Get the LOWORD of segment offset
+ cmp ax,WORD PTR Global.ge_dwBlockSize[0] ;Check LOWORD of size
+ jb MW_OK ;It's inside segment
+ jmp MW_Bad ;Not inside segment
+MW_OK:
+ ;** Do different stuff on 286 and 386/486
+ mov ax,__WinFlags ;Get the flags
+ test ax,WF_CPU286 ;286?
+ jnz MW_Do16Bit ;Yes, do 16 bit stuff
+
+ ;** Do this the 386 way (easy)
+.386p
+ ;** Get an alias selector if necessary
+ mov ax,wSelector ;Get the source selector
+ push ss ;Get ES = SS
+ pop es
+ lea di,DPMISelBuf ;Point to our descriptor buffer
+ cCall MakeAlias ;Make the alias selector
+ jnc SHORT @F ;No error
+ jmp MW_Bad ;Must be error
+@@: mov wSelFlag,bx ;Set the selector flag
+ mov wSelector,ax ;Save the new selector
+
+ ;** Do the copying
+ mov ax,wSelector ;Point with DS
+ mov es,ax ; (keep copy in AX)
+ mov edi,dwOffset ;Point into the big segment
+ mov ecx,dwcb ;Get the copy length
+ lsl edx,eax ;Get the segment limit
+ sub edx,edi ;Get distance from offset to limit
+ inc edx ;Make this the real length
+ cmp ecx,edx ;Are we within the limit?
+ jbe SHORT MW_LimitOK ;Yes
+ mov ecx,edx ;No, so make this the copy amount
+MW_LimitOK:
+ xor esi,esi ;Clear the high word
+ lds si,lpBuffer ;Point to the dest. buffer
+ mov eax,ecx ;Save ECX
+ shr ecx,2 ;Prepare for DWORD move
+ jecxz @F ;No zero-length DWORD moves!
+ rep movs DWORD PTR [edi],DWORD PTR [esi]
+ db 67h ;Handles 386 bug
+ db 90h
+@@: mov ecx,eax ;Get a copy
+ jecxz @F ;Don't do zero!
+ and ecx,03 ;Do the remaining 3,2, or 1
+ rep movs BYTE PTR [edi], BYTE PTR [esi]
+ db 67h ;Handles 386 bug
+ db 90h
+@@: mov edx,eax ;Bytes copied returned in DX:AX
+ shr edx,16
+
+ ;** Free alias if necessary
+ push ax ;Save return value
+ push dx
+ cmp wSelFlag,0 ;Selector flag set?
+ je SHORT @F ;Nope
+ mov ax,1 ;DPMI function - Free Selector
+ mov bx,wSelector ;Selector to free
+ int 31h ;Call DPMI
+@@: pop dx
+ pop ax
+ jmp MW_End ;Get out
+.286p
+
+ ;** Do this the 286 way (hard)
+MW_Do16Bit:
+
+ ;** Compute the actual copy length
+ mov ax,WORD PTR Global.ge_dwBlockSize[0] ;Get the segment size
+ mov dx,WORD PTR Global.ge_dwBlockSize[2]
+ sub ax,WORD PTR dwOffset[0] ;Get distance from offset to limit
+ sbb dx,WORD PTR dwOffset[2]
+ cmp dx,WORD PTR dwcb[2] ;Off end of heap block?
+ ja MW_LimOk ;No, just do it
+ jb MW_Truncate ;Yes, must truncate our length
+ cmp ax,WORD PTR dwcb[0] ;Are we off the end?
+ jae MW_LimOk ;No, just do it
+MW_Truncate:
+ mov WORD PTR dwcb[0],ax ;Force this to be the new length
+ mov WORD PTR dwcb[2],dx
+MW_LimOk:
+
+ ;** Save the number of bytes to be copied for the return value
+ mov ax,WORD PTR dwcb[0] ;Get the LOWORD
+ mov WORD PTR dwcbCopied[0],ax ;Save it
+ mov ax,WORD PTR dwcb[2] ;Get the HIWORD
+ mov WORD PTR dwcbCopied[2],ax ;Save it
+
+ ;** Position the initial copying selectors
+ mov al,BYTE PTR dwOffset[2] ;Grab the HIWORD (286 is only 24 bits)
+ mov ah,__AHINCR ;Get the selector inc value
+ mul ah ;Multiply to get sel offset
+ add ax,wSelector ;AX = sel in sel array
+ mov es,ax ;Point to this with DS
+ mov di,WORD PTR dwOffset[0] ;Get the current pointers
+ lds si,lpBuffer
+
+ ;** This is the main copying loop
+MW_CopyLoop:
+
+ ;** Get an alias selector if necessary
+ push si ;Save regs
+ push di
+ mov ax,es ;Get the source selector
+ push ss ;Get ES = SS
+ pop es
+ lea di,DPMISelBuf ;Point to our descriptor buffer
+ cCall MakeAlias ;Make the alias selector
+ pop di ;Restore regs
+ pop si
+ jnc @F ;No error
+ jmp MW_Bad ;Must be error
+@@: mov wSelFlag,bx ;Set the selector flag
+ mov es,ax ;Save the new selector
+
+ ;** Compute the size of this block copy. This is done by finding the
+ ;* smaller of the following quantities:
+ ;* - Distance to end of source segment
+ ;* - Distance to end of dest. segment
+ ;** - Distance to end of copy
+ xor bx,bx ;Flags start at zero
+ xor cx,cx ;Get the highest segment value (64K)
+ cmp di,si ;The bigger of the two will win
+ je MW_Equal ;They're the same
+ ja MW_DIBigger ;DI is bigger
+ sub cx,si ;SI bigger, compute dist to end
+ or bx,SI_CRITICAL ;Flag set for SI-critical
+ jmp SHORT MW_CheckEndCopy ;Go on
+MW_Equal:
+ sub cx,di ;Use DI (SI and DI are the same)
+ or bx,SI_CRITICAL OR DI_CRITICAL ;Both will come true
+ jmp SHORT MW_CheckEndCopy ;Go on
+MW_DIBigger:
+ sub cx,di ;SI is bigger
+ or bx,DI_CRITICAL ;Flag clear for DI-critical
+MW_CheckEndCopy:
+ cmp WORD PTR dwcb[2],0 ;Check for less than 64K left
+ ja MW_DoCopy ;Nope. More than 64K left
+ jcxz MW_GetSize ;CX = 0 is full 64K segment
+ cmp WORD PTR dwcb[0],cx ;Less than in either segment left?
+ ja MW_DoCopy ;No. Do it
+MW_GetSize:
+ mov cx,WORD PTR dwcb[0] ;Get in CX
+MW_DoCopy:
+
+ ;** Do this block of 64K or less.
+ mov dx,cx ;Save the number of bytes we did
+ jcxz @F ;Do 64K
+ shr cx,1 ;Do WORDS
+ jmp SHORT MW_10 ;Skip over
+@@: mov cx,8000h ;32K WORDS
+MW_10: jcxz @F ;No zero-length WORD moves
+ rep movsw ;Do the copy
+@@: mov cx,dx ;Get any remaining bits
+ and cx,1 ;Any more to do?
+ jcxz @F ;No, don't do it
+ movsb ;Do the odd byte if necessary
+@@: mov cx,dx ;Get back in CX
+
+ ;** Bump the loop pointers
+ jcxz MW_BigCount ;We did 64K
+ sub WORD PTR dwcb[0],cx ;Subtract the bytes done
+ sbb WORD PTR dwcb[2],0 ; and don't forget the HIWORD
+ jmp SHORT MW_20 ;Continue
+MW_BigCount:
+ sub WORD PTR dwcb[2],1 ;Subtract 64K
+MW_20: mov ax,WORD PTR dwcb[0] ;We're done if the count of bytes
+ or ax,WORD PTR dwcb[2] ; is zero
+ jnz @F ;Not zero, go on
+ mov dx,WORD PTR dwcbCopied[2] ;Get the return count
+ mov ax,WORD PTR dwcbCopied[0]
+ jmp SHORT MW_End ;Get out
+@@: test bx,SI_CRITICAL ;Does SI need incrementing?
+ jz MW_TestDI ;No, try DI
+ mov ax,ds ;Get the segment value
+ add ax,__AHINCR ;Bump to next selector
+ mov ds,ax ;Point with DS still
+ xor si,si ;Point to start of this segment
+MW_TestDI:
+ test bx,DI_CRITICAL ;Does SI need incrementing?
+ jz MW_Continue ;No, try DI
+ mov ax,es ;Get the segment value
+ add ax,__AHINCR ;Bump to next selector
+ mov es,ax ;Point with DS still
+ xor di,di ;Point to start of this segment
+MW_Continue:
+
+ ;** Free alias if necessary
+ cmp wSelFlag,0 ;Selector flag set?
+ je @F ;Nope
+ mov ax,1 ;DPMI function - Free Selector
+ mov bx,wSelector ;Selector to free
+ int 31h ;Call DPMI
+@@: jmp MW_CopyLoop ;Do it again
+
+MW_Bad:
+ xor ax,ax ;Return DWORD 0
+ cwd
+
+MW_End:
+
+cEnd
+
+
+;** Helper functions
+
+; MakeAlias
+; Makes an alias selector for the selector in AX. The new selector
+; is returned in AX. Carry is set on exit if error.
+; Returns nonzero in BX if an alias was made, zero if not
+; ES:DI points to an 8-byte descriptor buffer
+
+cProc MakeAlias, <NEAR, PUBLIC>, <si,di>
+cBegin
+
+ ;** If this is not a read/write selector, we must create an alias.
+ ;* In order to be able to free up the selector, we set a flag
+ ;** so we know to free it.
+ xor si,si ;No alias made, just in case
+ lar cx,ax ;Get its access rights
+ jnz MA_Bad ;Failed
+ test cx,800h ;Is this a code segment?
+ jnz MA_MakeAlias ;Yes. Always make an alias
+ test cx,200h ;Is it read/write?
+ jnz MA_End ;Yes, no need for alias
+MA_MakeAlias:
+ mov bx,ax ;Get the selector
+ mov ax,0bh ;DPMI function - Get Descriptor
+ ;ES:DI already point to buffer
+ int 31h ;Call DPMI
+ jc MA_Bad ;Error
+ xor ax,ax ;DPMI Function - Alloc selector
+ mov cx,1 ;Alloc 1 selector
+ int 31h ;Call DPMI
+ jc MA_Bad ;Error
+ mov si,1 ;Set flag to say alias made
+ and BYTE PTR DPMISelBuf[5],0f0h ;Mask out unwanted bits
+ or BYTE PTR DPMISelBuf[5],2 ;Make it a R/W Data segment
+ mov bx,ax ;Selector in BX
+ mov ax,0ch ;DPMI function - Set Descriptor
+ int 31h ;Call DPMI
+ jc MA_Bad ;Error
+ mov ax,bx ;Get the new selector in AX
+ jmp SHORT MA_End ;Get out
+
+MA_Bad:
+ stc ;Error
+
+MA_End:
+ mov bx,si ;Get flag in BX
+cEnd
+
+
+sEnd
+
+ END
+
diff --git a/private/mvdm/wow16/toolhelp/module.c b/private/mvdm/wow16/toolhelp/module.c
new file mode 100644
index 000000000..72270feba
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/module.c
@@ -0,0 +1,156 @@
+/*************************************************************************
+ * MODULE.C
+ *
+ * Routines to enumerate the various module headers on the module
+ * chain.
+ *
+ *************************************************************************/
+
+#include "toolpriv.h"
+#include <newexe.h>
+#include <string.h>
+
+/* ----- Function prototypes ----- */
+
+ NOEXPORT BOOL PASCAL ModuleGetInfo(
+ WORD wModule,
+ MODULEENTRY FAR *lpModule);
+
+/* ModuleFirst
+ * Finds the first module in the module list and returns information
+ * about this module.
+ */
+
+BOOL TOOLHELPAPI ModuleFirst(
+ MODULEENTRY FAR *lpModule)
+{
+ WORD FAR *lpwExeHead;
+
+ /* Check the version number and verify proper installation */
+ if (!wLibInstalled || !lpModule ||
+ lpModule->dwSize != sizeof (MODULEENTRY))
+ return FALSE;
+
+ /* Get a pointer to the module head */
+ lpwExeHead = MAKEFARPTR(segKernel, npwExeHead);
+
+ /* Use this pointer to get information about this module */
+ return ModuleGetInfo(*lpwExeHead, lpModule);
+}
+
+
+/* ModuleNext
+ * Finds the next module in the module list.
+ */
+
+BOOL TOOLHELPAPI ModuleNext(
+ MODULEENTRY FAR *lpModule)
+{
+ /* Check the version number and verify proper installation */
+ if (!wLibInstalled || !lpModule ||
+ lpModule->dwSize != sizeof (MODULEENTRY))
+ return FALSE;
+
+ /* Use the next handle to get information about this module */
+ return ModuleGetInfo(lpModule->wNext, lpModule);
+}
+
+
+/* ModuleFindName
+ * Finds a module with the given module name and returns information
+ * about it.
+ */
+
+HANDLE TOOLHELPAPI ModuleFindName(
+ MODULEENTRY FAR *lpModule,
+ LPCSTR lpstrName)
+{
+ /* Check the version number and verify proper installation */
+ if (!wLibInstalled || !lpModule || !lpstrName ||
+ lpModule->dwSize != sizeof (MODULEENTRY))
+ return NULL;
+
+ /* Loop through module chain until we find the name (or maybe we don't) */
+ if (ModuleFirst(lpModule))
+ do
+ {
+ /* Is this the name? If so, we have the info, so return */
+ if (!lstrcmp(lpstrName, lpModule->szModule))
+ return lpModule->hModule;
+ }
+ while (ModuleNext(lpModule));
+
+ /* If we get here, we didn't find it or there was an error */
+ return NULL;
+}
+
+
+/* ModuleFindHandle
+ * Returns information about a module with the given handle.
+ */
+
+HANDLE TOOLHELPAPI ModuleFindHandle(
+ MODULEENTRY FAR *lpModule,
+ HANDLE hModule)
+{
+ /* Check the version number and verify proper installation */
+ if (!wLibInstalled || !lpModule || !hModule ||
+ lpModule->dwSize != sizeof (MODULEENTRY))
+ return NULL;
+
+ /* Use the helper function to find out about this module */
+ if (!ModuleGetInfo(hModule, lpModule))
+ return NULL;
+
+ return lpModule->hModule;
+}
+
+
+/* ----- Helper functions ----- */
+
+NOEXPORT BOOL PASCAL ModuleGetInfo(
+ WORD wModule,
+ MODULEENTRY FAR *lpModule)
+{
+ struct new_exe FAR *lpNewExe;
+ BYTE FAR *lpb;
+
+ /* Verify the segment so we don't GP fault */
+ if (!HelperVerifySeg(wModule, 2))
+ return FALSE;
+
+ /* Get a pointer to the module database */
+ lpNewExe = MAKEFARPTR(wModule, 0);
+
+ /* Make sure this is a module database */
+ if (lpNewExe->ne_magic != NEMAGIC)
+ return FALSE;
+
+ /* Get the module name (it's the first name in the resident names
+ * table
+ */
+ lpb = ((BYTE FAR *)lpNewExe) + lpNewExe->ne_restab;
+ _fstrncpy(lpModule->szModule, lpb + 1, *lpb);
+ lpModule->szModule[*lpb] = '\0';
+
+ /* Get the EXE file path. A pointer is stored in the same place as
+ * the high word of the CRC was in the EXE file. (6th word in new_exe)
+ * This pointer points to the length of a PASCAL string whose first
+ * eight characters are meaningless to us.
+ */
+ lpb = MAKEFARPTR(wModule, *(((WORD FAR *)lpNewExe) + 5));
+ _fstrncpy(lpModule->szExePath, lpb + 8, *lpb - 8);
+ lpModule->szExePath[*lpb - 8] = '\0';
+
+ /* Get other information from the EXE Header
+ * The usage count is stored in the second word of the EXE header
+ * The handle of the next module in the chain is stored in the
+ * ne_cbenttab structure member.
+ */
+ lpModule->hModule = wModule;
+ lpModule->wcUsage = *(((WORD FAR *)lpNewExe) + 1);
+ lpModule->wNext = lpNewExe->ne_cbenttab;
+
+ return TRUE;
+}
+
diff --git a/private/mvdm/wow16/toolhelp/notify1.c b/private/mvdm/wow16/toolhelp/notify1.c
new file mode 100644
index 000000000..bf101ad4d
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/notify1.c
@@ -0,0 +1,165 @@
+/**************************************************************************
+ * NOTIFY1.C
+ *
+ * Routines used to implement the Debugger Notification API in
+ * TOOLHELP.DLL
+ *
+ **************************************************************************/
+
+#include <string.h>
+#include "toolpriv.h"
+
+/* ----- Global variables ----- */
+ WORD wNotifyInstalled;
+ NOTIFYSTRUCT NEAR *npNotifyHead;
+ NOTIFYSTRUCT NEAR *npNotifyNext;
+
+/* NotifyRegister
+ * Registers a debugger notification callback. This callback will
+ * be called whenever KERNEL has a notification to be sent.
+ * The format of the call to the callback function is documented
+ * elsewhere.
+ */
+
+BOOL TOOLHELPAPI NotifyRegister(
+ HANDLE hTask,
+ LPFNNOTIFYCALLBACK lpfn,
+ WORD wFlags)
+{
+ NOTIFYSTRUCT *pInfo;
+ NOTIFYSTRUCT *pTemp;
+
+ /* Make sure TOOLHELP.DLL is installed */
+ if (!wLibInstalled)
+ return FALSE;
+
+ /* If the notification hook has not yet been installed, install it */
+ if (!wNotifyInstalled)
+ {
+ /* Make sure we can get a hook! */
+ if (!NotifyInit())
+ return FALSE;
+ wNotifyInstalled = TRUE;
+ }
+
+ /* NULL hTask means current task */
+ if (!hTask)
+ hTask = GetCurrentTask();
+
+ /* Register a death signal handler for this task (does nothing if one
+ * is already installed.
+ */
+ SignalRegister(hTask);
+
+ /* Check to see if this task is already registered */
+ for (pInfo = npNotifyHead ; pInfo ; pInfo = pInfo->pNext)
+ if (pInfo->hTask == hTask)
+ return FALSE;
+
+ /* Allocate a new NOTIFYSTRUCT structure */
+ pInfo = (NOTIFYSTRUCT *)LocalAlloc(LMEM_FIXED, sizeof (NOTIFYSTRUCT));
+ if (!pInfo)
+ return FALSE;
+
+ /* Fill in the useful fields */
+ pInfo->hTask = hTask;
+ pInfo->wFlags = wFlags;
+ pInfo->lpfn = lpfn;
+
+ /* If this is the only handler, just insert it */
+ if (!npNotifyHead)
+ {
+ pInfo->pNext = npNotifyHead;
+ npNotifyHead = pInfo;
+ }
+
+ /* Otherwise, insert at the end of the list */
+ else
+ {
+ for (pTemp = npNotifyHead ; pTemp->pNext ; pTemp = pTemp->pNext)
+ ;
+ pInfo->pNext = pTemp->pNext;
+ pTemp->pNext = pInfo;
+ }
+
+ return TRUE;
+}
+
+
+/* NotifyUnRegister
+ * Called by an app whose callback is no longer to be used.
+ * NULL hTask uses current task.
+ */
+
+BOOL TOOLHELPAPI NotifyUnRegister(
+ HANDLE hTask)
+{
+ NOTIFYSTRUCT *pNotify;
+ NOTIFYSTRUCT *pBefore;
+
+ /* Make sure we have notifications installed and that TOOLHELP is OK */
+ if (!wLibInstalled || !wNotifyInstalled)
+ return FALSE;
+
+ /* NULL hTask means current task */
+ if (!hTask)
+ hTask = GetCurrentTask();
+
+ /* First try to find the task */
+ pBefore = NULL;
+ for (pNotify = npNotifyHead ; pNotify ; pNotify = pNotify->pNext)
+ if (pNotify->hTask == hTask)
+ break;
+ else
+ pBefore = pNotify;
+ if (!pNotify)
+ return FALSE;
+
+ /* Unhook the death signal proc only if there is no interrupt handler */
+ if (!InterruptIsHooked(hTask))
+ SignalUnRegister(hTask);
+
+ /* Check to see if the notification handler is about to use this entry.
+ * If it is, we point it to the next one, if any.
+ */
+ if (npNotifyNext == pNotify)
+ npNotifyNext = pNotify->pNext;
+
+ /* Remove it from the list */
+ if (!pBefore)
+ npNotifyHead = pNotify->pNext;
+ else
+ pBefore->pNext = pNotify->pNext;
+
+ /* Free the structure */
+ LocalFree((HANDLE)pNotify);
+
+ /* If there are no more handlers, unhook the callback */
+ if (!npNotifyHead)
+ {
+ NotifyUnInit();
+ wNotifyInstalled = FALSE;
+ }
+
+ return TRUE;
+}
+
+/* ----- Helper functions ----- */
+
+/* NotifyIsHooked
+ * Returns TRUE iff the parameter task already has a notification hook.
+ */
+
+BOOL PASCAL NotifyIsHooked(
+ HANDLE hTask)
+{
+ NOTIFYSTRUCT *pNotify;
+
+ /* Loop thorugh all notifications */
+ for (pNotify = npNotifyHead ; pNotify ; pNotify = pNotify->pNext)
+ if (pNotify->hTask == hTask)
+ break;
+
+ /* Return found/not found */
+ return (BOOL)pNotify;
+}
diff --git a/private/mvdm/wow16/toolhelp/notify2.asm b/private/mvdm/wow16/toolhelp/notify2.asm
new file mode 100644
index 000000000..974c192b1
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/notify2.asm
@@ -0,0 +1,643 @@
+ PAGE 60,150
+;***************************************************************************
+;* NOTIFY2.ASM
+;*
+;* Assembly code support routines used for the TOOLHELP.DLL
+;* notification API
+;*
+;***************************************************************************
+
+ INCLUDE TOOLPRIV.INC
+.286p
+
+;** Data
+sBegin DATA
+
+globalW wCASRqFlag,0 ;Set when an CASRq INT3 has been set
+globalD dwCASRqCSIP,0 ;Holds the CS:IP of the CASRq INT3
+globalD lpfnOldProc,0 ;Old hook from new PTrace hook
+szWinDebug DB 'WINDEBUG', 0
+
+;** WARNING!!
+;** This structure is set to the size of the largest notification
+;** structure. This is currently NFYLOADSEG which is 16 bytes long.
+;** If a structure is added that is longer than this or if any other
+;** structure is added, this space must be increased to match!!
+ReturnStruct DB 16 DUP (?)
+
+sEnd
+
+;** Imports
+externFP GetModuleHandle
+externFP RegisterPTrace
+externFP OutputDebugString
+externFP AllocCStoDSAlias
+externFP FreeSelector
+externNP HelperHandleToSel
+
+sBegin CODE
+ assumes CS,CODE
+ assumes DS,DATA
+
+; NotifyInit
+; Called when the first app registers a notification handler.
+; Hooks the Register PTrace notification.
+; Returns FALSE if we couldn't initialize, TRUE otherwise
+
+cProc NotifyInit, <NEAR,PUBLIC>, <si,di,ds>
+cBegin
+ ;** In the Windows 3.1 KERNEL, there is a special hook just for
+ ;* TOOLHELP that lets us get PTrace stuff and still coexist
+ ;* with old-fashioned debuggers. We can check to see if the
+ ;* hook exists by simply checking the TOOLHELP flags
+ ;**
+ test wTHFlags,TH_GOODPTRACEHOOK ;Good hook around?
+ jz DNI_UseRegPTrace ;Nope, use the old one
+ lea si,NotifyHandler ;Point to the routine
+ push cs ;Parameter is lpfn to callback
+ push si
+ call lpfnNotifyHook ;Hook it
+ mov WORD PTR lpfnOldProc[0],ax ;Save old proc
+ mov WORD PTR lpfnOldProc[2],dx
+ jmp SHORT DNI_10 ;We're in
+
+ ;** Since there's no way we can see if someone else has Register
+ ;* PTrace, we just connect and hope for the best!
+ ;** We do check, however, to see if WINDEBUG.DLL is installed.
+DNI_UseRegPTrace:
+ lea si,szWinDebug ;Get the name of the module
+ cCall GetModuleHandle, <ds,si> ;Is WINDEBUG present?
+ or ax,ax ;Check the handle
+ jnz DNI_Fail ;It's here so fail
+ or wTHFlags,TH_GOTOLDPTRACE ;Flag that we made the hook
+ lea si,NotifyHandler ;Point to our routine
+ cCall RegisterPTrace, <cs,si> ;Tell KERNEL to use it
+
+ ;** Connect to the FatalExit hook. We currently ignore
+ ;** the return value, thus unhooking anyone else
+DNI_10: cmp WORD PTR lpfnFatalExitHook + 2,0 ;Can we hook it?
+ jz DNI_20 ;Can't do it
+ push cs ;Get the CS:IP of RIP handler
+ push OFFSET NotifyRIPHandler
+ call DWORD PTR lpfnFatalExitHook ;Tell KERNEL to insert the hook
+DNI_20:
+
+ ;** Return OK
+ mov ax,TRUE ;Return TRUE
+ jmp SHORT DNI_End ;Get out
+
+DNI_Fail:
+ xor ax,ax ;FALSE
+
+DNI_End:
+cEnd
+
+
+; NotifyUnInit
+; Called when the no more apps have hooked notification handlers
+; so the hook to the Register PTrace notification is no longer needed.
+
+cProc NotifyUnInit, <NEAR,PUBLIC>, <si,di,ds>
+cBegin
+ ;** Did we have a new hook to undo?
+ test wTHFlags,TH_GOODPTRACEHOOK ;Do we have a new hook?
+ jz DNU_TryOldPTrace ;No
+ push WORD PTR lpfnOldProc[0] ;Get the old proc
+ push WORD PTR lpfnOldProc[2]
+ call lpfnNotifyHook ;Unhook ourself
+ jmp SHORT DNU_NoOldPTrace
+
+ ;** Unhook the old-style hook if necessary
+DNU_TryOldPTrace:
+ test wTHFlags,TH_GOTOLDPTRACE ;Did we have a hook?
+ jz DNU_NoOldPTrace ;No
+ push 0
+ push 0
+ call RegisterPTrace ;Call KERNEL's routine to unhook
+DNU_NoOldPTrace:
+
+ ;** Unhook alternate hooks
+ cmp WORD PTR lpfnFatalExitHook + 2,0 ;Can we unhook it?
+ jz DNU_NoRIP ;Can't do it
+ xor ax,ax ;Remove any other hooks
+ push ax ;NULL procedure
+ push ax
+ call DWORD PTR lpfnFatalExitHook
+DNU_NoRIP:
+
+cEnd
+
+
+; NotifyHandler
+; This routine is called directly by PTrace and is used to
+; dispatch the notifications to all registered callbacks.
+
+cProc NotifyHandler, <FAR,PUBLIC>
+cBegin NOGEN
+
+ ;** Push a register frame
+ ;* When done, it should look like this:
+ ;* ------------
+ ;* | ES | [BP - 14h]
+ ;* | DS | [BP - 12h]
+ ;* | DI | [BP - 10h]
+ ;* | SI | [BP - 0Eh]
+ ;* | BP | [BP - 0Ch]
+ ;* | SP | [BP - 0Ah]
+ ;* | BX | [BP - 08h]
+ ;* | DX | [BP - 06h]
+ ;* | CX | [BP - 04h]
+ ;* | AX | [BP - 02h]
+ ;* BP-->| Old BP | [BP - 00h]
+ ;* | IP | [BP + 02h]
+ ;* | CS | [BP + 04h]
+ ;* ------------
+ ;**
+ push bp ;Make a stack frame
+ mov bp,sp
+ pusha ;Save all registers
+ push ds ;Save segment registers, too
+ push es
+
+ ;** Get the data segment
+ mov bx,_DATA ;Get TOOLHELP data segment
+ mov ds,bx
+
+ ;** If in 3.0 std mode and we get this wild notification 69h,
+ ;** translate it to a inchar notification as this is what it
+ ;** is supposed to be.
+ cmp ax,69h ;Bogus notification?
+ jne NH_NoBogusNotify ;No, don't do this
+ test wTHFlags,TH_WIN30STDMODE ;3.0 standard mode?
+ jz NH_NoBogusNotify ;No, might be valid in the future...
+ mov ax,NI_INCHAR ;Put in real notify value
+NH_NoBogusNotify:
+
+ ;** Special case notifications:
+ ;* Notification 63h means that CtlAltSysRq was pressed. For
+ ;* this, we want to handle as an interrupt, not a notification.
+ ;* To do this, we set a breakpoint and set a flag so that the
+ ;** INT3 handler knows what to do with it
+ cmp ax,63h ;CtlAltSysRq?
+ jne NH_NoCASRq ;No.
+ mov ax,[bp + 04h] ;Since we can't use IRET CS:IP, get
+ mov si,[bp + 02h] ; a safe address in KERNEL
+ mov WORD PTR dwCASRqCSIP[2],ax ;Save the CS:IP value
+ cCall AllocCStoDSAlias, <ax> ;Get a data alias to the CS
+ or ax,ax ;Error?
+ jnz @F
+ jmp SHORT DNH_End ;Yes, get out
+@@: verw ax ;OK to write to?
+ jnz DNH_NoWrite ;Yes, so do it
+DNH_IRETCSOK:
+ mov es,ax ;Point with ES
+ mov WORD PTR dwCASRqCSIP[0],si
+ mov al,es:[si] ;Get the character there
+ mov ah,1 ;Make sure there's something in AH
+ mov wCASRqFlag,ax ;Save the thing for the INT3 handler
+ mov BYTE PTR es:[si],0cch ;Poke the INT3 in there
+ mov ax,es ;Get the selector back
+DNH_NoWrite:
+ cCall FreeSelector, <ax> ;Get rid of the alias
+ jmp SHORT DNH_End ;Get out. This will INT3 soon
+
+NH_NoCASRq: ; Does not return
+
+ ;** Notifications to ignore here:
+ ;** Notification 60h is bogus and should be ignored
+ cmp ax,60h ;PostLoad notification?
+ jz DNH_End ;Yes, don't report
+
+ ;** Decode the notification
+ cCall DecodeNotification ;Returns dwData in CX:DX, AX is wID
+ ; BX is NOTIFYSTRUCT match flags
+ ;** This is an entry point for notifications from sources other than
+ ;** PTrace
+DNH_Decoded:
+
+ ;** Loop through callbacks
+ mov di,npNotifyHead ;Point to the start of the list
+ xor si,si ;FALSE return value is default
+DNH_Loop:
+ push ax
+ mov ax,ds:[di].ns_pNext ;Save the next pointer in a global
+ mov npNotifyNext,ax ; so we can chain in NotifyUnregister
+ pop ax
+
+ or di,di ;End of list?
+ jz DNH_Done ;Yep. Get out
+
+ ;** If the flags for this notification are zero, we always send it
+ or bx,bx ;Check the matching flags
+ jz DNH_DoIt ;Do notification
+
+ ;** See if the flags match
+ test bx,ds:[di].ns_wFlags ;Check against the NOTIFYSTRUCT flags
+ jz DNH_Continue ;If zero, no match, don't do it
+
+ ;** Call the user callback
+DNH_DoIt:
+ push ax ;Save everything we need
+ push bx
+ push cx
+ push dx
+
+ push ax ;wID
+ push cx ;High word of dwData
+ push dx ;Low word
+ call DWORD PTR ds:[di].ns_lpfn ;Call the callback (PASCAL style)
+ mov si,ax ;Get return value in SI
+
+ pop dx ;Restore everything
+ pop cx
+ pop bx
+ pop ax
+
+ ;** If the return value is nonzero, we don't want to give this to
+ ;** any more callbacks
+ or si,si ;TRUE return value?
+ jnz DNH_Done ;Yes, get out
+
+ ;** Get the next callback
+DNH_Continue:
+ mov di,npNotifyNext ;Get next pointer
+ jmp DNH_Loop ; and loop back
+
+ ;** End of callback loop.
+DNH_Done:
+
+ ;** If this was an InChar message but everyone ignored it, force
+ ;** the return to be an 'i' for 'ignore' on RIPs. This i
+ ;** only necessary in 3.0 because the 3.1 KERNEL treats 0
+ ;** returns just like 'i'
+ cmp ax,NFY_INCHAR ;Is this an InChar notification?
+ jne DNH_Default ;No, so ignore
+ test wTHFlags,TH_WIN30 ;In 3.0?
+ jz DNH_Default ;No, don't do this
+ and si,0ffh ;Ignore all but low byte
+ or si,si ;Non-zero?
+ jnz DNH_Default ;Yes, return it as the character
+ mov si,'i' ;Instead of zero, return 'i'gnore.
+DNH_Default:
+ mov [bp - 02h],si ;Return the return code in AX
+
+ ;** Clear off the stack and exit
+DNH_End:
+ mov npNotifyNext,0 ;No current next pointer
+
+ pop es ;Restore all registers
+ pop ds
+ popa
+ pop bp
+ retf ;Just return
+
+cEnd NOGEN
+
+
+; NotifyRIPHandler
+; Gets called by KERNEL when a RIP occurs. If it returns TRUE,
+; KERNEL will act like the RIP was ignored. Otherwise, the RIP
+; procedes normally.
+; This routine does not need to worry about saving non-C regs
+
+cProc NotifyRIPHandler, <FAR,PUBLIC>
+; parmW wExitCode
+cBegin nogen
+
+ ;** Clear PASCAL-style parameters
+ push bp ;Make a stack frame
+ mov bp,sp
+ mov bx,[bp + 6] ;Get the BP value
+ mov dx,[bp + 8] ;Get the Exit code
+ mov [bp - 2],ax ;Save it out of the way for now
+ mov ax,[bp + 4] ;Get the RETF CS value
+ mov [bp + 8],ax ;Shift down to clear parameters
+ mov ax,[bp + 2] ;Get the RETF IP value
+ mov [bp + 6],ax ;Shift down to clear parameters
+ mov ax,[bp + 0] ;Get the old BP value
+ mov [bp + 4],ax ;Shift down
+ add bp,4 ;Move BP down on the stack
+ mov sp,bp ;Point SP there too
+ pusha ;Save matching register frame
+ push ds
+ push es
+
+ ;** Get the data segment
+ mov ax,_DATA ;Get TOOLHELP data segment
+ mov ds,ax
+
+
+ ;** Prepare to jump into the notification handler.
+ ;** The trick here is that if a notification callback returns
+ ;** non-zero, the RIP has been handled. Otherwise, it has not.
+ ;** DX holds the exit code here, BX has the old BP value
+ lea si,ReturnStruct ;Get a pointer to the return struct
+ mov WORD PTR [si].nrp_dwSize[0],SIZE NFYRIP
+ mov WORD PTR [si].nrp_dwSize[2],0
+ mov ax,ss:[bx + 4] ;Get old CS value from stack
+ mov [si].nrp_wCS,ax ; (BX is BP from FatalExit stack)
+ mov ax,ss:[bx + 2] ;Get old IP value
+ mov [si].nrp_wIP,ax
+ mov [si].nrp_wSS,ss ;Save SS:BP for stack trace
+ mov [si].nrp_wBP,bx
+ mov [si].nrp_wExitCode,dx
+ mov cx,ds ;Point to structure
+ mov dx,si
+ mov bx,NF_RIP ;Get the NOTIFYINFO match flags
+ mov ax,NFY_RIP ;TOOLHELP ID
+
+ ;** Jump to the real handler
+ jmp DNH_Decoded ;Jump to alternate entry point
+
+cEnd nogen
+
+;** Helper routines
+
+; DecodeNotification
+; Decodes a notification by pointing to a static structure and filling
+; this structure with notification-specific information.
+; The PTrace notification ID is in AX.
+; Returns the ToolHelp ID in AX
+; and the dwData value is in CX:DX.
+
+cProc DecodeNotification, <NEAR,PUBLIC>
+cBegin
+ ;** Point dwData to the structure just in case
+ mov cx,ds ;Get the segment value
+ lea dx,ReturnStruct ;Get a pointer to the return struct
+ xor bx,bx ;Most notifications always match
+
+ ;** The stack frame looks like this:
+ ;* ------------
+ ;* | ES | [BP - 14h]
+ ;* | DS | [BP - 12h]
+ ;* | DI | [BP - 10h]
+ ;* | SI | [BP - 0Eh]
+ ;* | BP | [BP - 0Ch]
+ ;* | SP | [BP - 0Ah]
+ ;* | BX | [BP - 08h]
+ ;* | DX | [BP - 06h]
+ ;* | CX | [BP - 04h]
+ ;* | AX | [BP - 02h]
+ ;* BP-->| Old BP | [BP - 00h]
+ ;* ------------
+ ;**
+FrameES EQU [BP - 14h]
+FrameDS EQU [BP - 12h]
+FrameDI EQU [BP - 10h]
+FrameSI EQU [BP - 0Eh]
+FrameBP EQU [BP - 0Ch]
+FrameSP EQU [BP - 0Ah]
+FrameBX EQU [BP - 08h]
+FrameDX EQU [BP - 06h]
+FrameCX EQU [BP - 04h]
+FrameAX EQU [BP - 02h]
+
+ ;** Check for LoadSeg
+ cmp ax,NI_LOADSEG ;LoadSeg?
+ jnz DN_10 ;No
+
+ ;** LoadSeg:
+ ;* CX is selector
+ ;* BX is segment number
+ ;* SI is type: Low bit set for data segment, clear for code
+ ;* DX is instance count only for data segments
+ ;** ES:DI module name
+ mov si,dx ;Point to NFYLOADSEG struct
+ mov ax,SIZE NFYLOADSEG ;Get the structure size
+ mov WORD PTR [si].nls_dwSize,ax ;Save the LOWORD of the size
+ mov WORD PTR [si].nls_dwSize + 2,0 ;HIWORD is zero
+ mov ax,FrameCX ;Get selector
+ mov [si].nls_wSelector,ax ;Save in structure
+ mov ax,FrameBX ;Get segment number
+ inc ax ;Segment number is 1-based
+ mov [si].nls_wSegNum,ax ;Save in structure
+ mov ax,FrameSI ;Get the segment type
+ mov [si].nls_wType,ax ;Put in structure
+ mov ax,FrameDX ;Get instance count
+ mov [si].nls_wcInstance,ax ;Put in structure
+ mov ax,FrameDI ;Get offset of module name str
+ mov WORD PTR [si].nls_lpstrModuleName,ax ;Save it
+ mov ax,FrameES ;Get segment of module name str
+ mov WORD PTR [si].nls_lpstrModuleName + 2,ax ;Save it
+ mov ax,NFY_LOADSEG ;Get the TOOLHELP ID
+ jmp DN_End
+
+ ;** Check for FreeSeg
+DN_10: cmp ax,NI_FREESEG ;FreeSeg?
+ jnz DN_15 ;No
+
+ ;** FreeSeg:
+ ;** BX is selector
+ xor cx,cx ;Clear high word
+ mov dx,FrameBX ;Get the selector
+ test wTHFlags,TH_WIN30STDMODE ;3.0 standard mode?
+ jz DN_FS_GotSelValue ;No, what we have is correct
+ mov si,FrameSP ;Point to old stack frame
+ mov dx, ss:[si + 6] ;Selector is 6 bytes down
+ lsl ax, dx
+ jz DN_FS_CheckLen ;Selector is OK
+ mov dx, FrameBX ;Revert to BX value
+ jmp SHORT DN_FS_GotSelValue
+DN_FS_CheckLen:
+ cmp ax, 15 ;If the segment is 15 bytes long,
+ jne DN_FS_GotSelValue ; this is a bogus selector and is
+ ; really an arena header.
+ push es
+ mov es, dx ;Get handle
+ cCall HelperHandleToSel, <es:[0ah]> ;Convert to selector
+ mov dx, ax ;Get handle out of arena header
+ pop es
+DN_FS_GotSelValue:
+ mov ax,NFY_FREESEG ;Get the TOOLHELP ID
+ jmp DN_End
+
+ ;** Check for StartDLL
+DN_15: cmp ax,NI_LOADDLL
+ jnz DN_20
+
+ ;** StartDLL:
+ ;** CX is CS
+ ;** BX is IP
+ ;** SI is Module handle
+ mov si,dx ;Point with SI
+ mov ax,SIZE NFYSTARTDLL ;Get the size
+ mov WORD PTR [si].nsd_dwSize,ax ;Save the LOWORD of the size
+ mov WORD PTR [si].nsd_dwSize + 2,0 ;HIWORD is always zero
+ mov ax,FrameSI ;Get the hInstance
+ mov [si].nsd_hModule,ax ;Save in structure
+ mov ax,FrameCX ;Get the starting CS
+ mov [si].nsd_wCS,ax ;Save in structure
+ mov ax,FrameBX ;Get the starting IP
+ mov [si].nsd_wIP,ax ;Save in structure
+ mov ax,NFY_STARTDLL
+ jmp DN_End
+
+ ;** Check for StartTask
+DN_20: cmp ax,NI_STARTTASK ;StartTask?
+ jnz DN_30 ;No
+
+ ;** StartTask:
+ ;* CX is CS
+ ;** BX is IP
+ mov cx,FrameCX
+ mov dx,FrameBX
+ mov ax,NFY_STARTTASK
+ jmp DN_End
+
+ ;** Check for ExitCall
+DN_30: cmp ax,NI_EXITCALL ;ExitCall
+ jnz DN_40 ;No
+
+ ;** ExitCall:
+ ;* Exit code is on stack somewhere if we don't have the new
+ ;** notification handler. If we do, it's in BL.
+ xor cx,cx ;Clear all but low byte
+ xor dh,dh
+ test wTHFlags,TH_GOODPTRACEHOOK ;Do we have the good hook?
+ jz DN_DoOldHook ;Nope, grope on the stack
+ mov dl,BYTE PTR FrameBX ;Get the exit code
+ mov ax,NFY_EXITTASK ;Get the TOOLHELP ID
+ jmp DN_End
+DN_DoOldHook:
+ mov si,FrameSP ;Point to old stack frame
+ mov dl,ss:[si + 6] ;Exit code is 6 bytes down on stack
+ mov ax,NFY_EXITTASK ;Get the TOOLHELP ID
+ jmp DN_End
+
+ ;** Check for DelModule
+DN_40: cmp ax,NI_DELMODULE ;DelModule?
+ jnz DN_60 ;No
+
+ ;** DelModule:
+ ;** ES is module handle
+ xor cx,cx ;Clear HIWORD
+ mov dx,FrameES ;Get the module handle
+ mov ax,NFY_DELMODULE ;Get the TOOLHELP ID
+ jmp DN_End
+
+ ;** Check for TaskSwitchIn
+DN_60: cmp ax,NI_TASKIN ;TaskSwitchIn?
+ jnz DN_70 ;No
+
+ ;** TaskSwitchIn:
+ ;** No data. Callback should do GetCurrentTask()
+ xor cx,cx ;Clear data
+ xor dx,dx
+ mov ax,NFY_TASKIN ;Get the TOOLHELP ID
+ mov bx,NF_TASKSWITCH ;Get the NOTIFYSTRUCT match flag
+ jmp DN_End
+
+ ;** Check for TaskSwitchOut
+DN_70: cmp ax,NI_TASKOUT ;TaskSwitchOut?
+ jnz DN_90 ;No
+
+ ;** TaskSwitchOut:
+ ;** No data
+ xor cx,cx ;Clear data
+ xor dx,dx
+ mov ax,NFY_TASKOUT ;Get the TOOLHELP ID
+ mov bx,NF_TASKSWITCH ;Get the NOTIFYSTRUCT match flag
+ jmp DN_End
+
+ ;** Check for OutStr
+DN_90: cmp ax,NI_OUTSTR ;OutStr?
+ jnz DN_100 ;No
+
+ ;** OutStr:
+ ;** ES:SI points to string to display in 3.1
+ ;** DS:SI in 3.0
+ test wTHFlags,TH_WIN30 ;3.0?
+ jz DN_OS_Win31 ;Nope
+ mov cx,FrameDS ;Get the segment value
+ jmp SHORT @F
+DN_OS_Win31:
+ mov cx,FrameES ;Get the segment value
+@@: mov dx,FrameSI ; and the offset
+ mov ax,NFY_OUTSTR ;Get the TOOLHELP ID
+ jmp DN_End
+
+ ;** Check for InChar
+DN_100: cmp ax,NI_INCHAR ;InChar?
+ jnz DN_105 ;No
+
+ ;** InChar:
+ ;** No data passed (it wants data back in AL)
+ xor cx,cx ;Clear dwData
+ xor dx,dx
+ mov ax,NFY_INCHAR ;Get the TOOLHELP ID
+ jmp SHORT DN_End
+
+ ;** NOTE: The following notifications are defined as "NEW" and
+ ;** are NOT sent through the normal PTrace interface so as to
+ ;** not break brain-damaged CodeSpew. It stack faults when
+ ;** it is sent a notification it doesn't understand. So,
+ ;** here we don't bother decoding any of these unless we have
+ ;** the new (Win 3.1) style hook
+DN_105: test wTHFlags,TH_GOODPTRACEHOOK ;Do we have the advanced hook?
+ jnz DN_110 ;Yes
+ jmp SHORT DN_End
+
+ ;** Check for the parameter validation notifications
+DN_110: cmp ax,NI_LOGERROR ;SDM_LOGERROR?
+ jne DN_120 ;No
+
+ ;** SDM_LOGERROR:
+ ;* CX is Error code
+ ;** DX:BX is lpInfo
+ mov si,dx ;Point with SI
+ mov ax,SIZE NFYLOGERROR ;Get the size
+ mov WORD PTR [si].nle_dwSize[0],ax ;Save the LOWORD of the size
+ mov WORD PTR [si].nle_dwSize[2],0 ;HIWORD is always zero
+ mov ax,FrameCX ;Get the error code
+ mov [si].nle_wErrCode,ax ;Save in structure
+ mov ax,FrameDX ;Get the lpInfo
+ mov WORD PTR [si].nle_lpInfo[2],ax ;Save in structure
+ mov ax,FrameBX
+ mov WORD PTR [si].nle_lpInfo[0],ax ;Save in structure
+ mov ax,NFY_LOGERROR ;Get the TOOLHELP ID
+ jmp SHORT DN_End
+
+DN_120: cmp ax,NI_LOGPARAMERROR ;SDM_LOGPARAMERROR?
+ jne DN_Unknown ;No
+
+ ;** SDM_LOGPARAMERROR:
+ ;** ES:BX points to a structure:
+ ;** WORD wErr
+ ;** FARPROC lpfn
+ ;** VOID FAR* lpBadParam
+ mov si,dx ;Point with SI
+ mov ax,SIZE NFYLOGPARAMERROR ;Struct size
+ mov WORD PTR [si].nlp_dwSize[0],ax ;Save the LOWORD of the size
+ mov WORD PTR [si].nlp_dwSize[2],0 ;HIWORD is always zero
+ mov es,FrameES ;Point to the structure
+ mov bx,FrameBX
+ mov ax,es:[bx] ;Get wErr
+ mov [si].nlp_wErrCode,ax ;Save in structure
+ mov ax,es:[bx + 2] ;Get lpfn[0]
+ mov WORD PTR [si].nlp_lpfnErrorAddr[0],ax
+ mov ax,es:[bx + 4] ;Get lpfn[2]
+ mov WORD PTR [si].nlp_lpfnErrorAddr[2],ax
+ mov ax,es:[bx + 6] ;Get lpBadParam[0]
+ mov WORD PTR [si].nlp_lpBadParam[0],ax
+ mov ax,es:[bx + 8] ;Get lpBadParam[2]
+ mov WORD PTR [si].nlp_lpBadParam[2],ax
+ mov ax,NFY_LOGPARAMERROR ;Get the TOOLHELP ID
+ xor bx,bx ;Always match
+ jmp SHORT DN_End
+
+ ;** Must be unknown, return TOOLHELP ID NFY_UNKNOWN with KERNEL value
+ ;** in LOWORD(wData)
+DN_Unknown:
+ mov dx,ax ;Get the notification value
+ mov ax,NFY_UNKNOWN ;Unknown KERNEL notification
+ xor cx,cx ;Clear high WORD
+
+DN_End:
+
+cEnd
+
+sEnd
+ END
+
+
diff --git a/private/mvdm/wow16/toolhelp/signal.c b/private/mvdm/wow16/toolhelp/signal.c
new file mode 100644
index 000000000..674100a3a
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/signal.c
@@ -0,0 +1,110 @@
+/**************************************************************************
+ * SIGNAL.C
+ *
+ * Routines used to clean up on a forced KERNEL termination of a
+ * TOOLHELP using app.
+ *
+ **************************************************************************/
+
+#include <string.h>
+#include "toolpriv.h"
+
+/* ----- Global variables ----- */
+ WORD wSignalInstalled;
+ SIGNAL NEAR *npSignalHead;
+
+/* SignalRegister
+ * Registers a default signal proc to a task. This signal proc is
+ * called when the task is about to be terminated and is called before
+ * the USER signal proc is called. The registered callback is
+ * called HelperSignalProc() [HELPER.ASM] and chains to the USER signal
+ * proc (if any) instead of returning.
+ */
+
+BOOL PASCAL SignalRegister(
+ HANDLE hTask)
+{
+ SIGNAL *pSig;
+ SIGNAL *pTemp;
+
+ /* NULL hTask means current task */
+ if (!hTask)
+ hTask = GetCurrentTask();
+
+ /* Check to see if this task is already registered */
+ for (pSig = npSignalHead ; pSig ; pSig = pSig->pNext)
+ if (pSig->hTask == hTask)
+ return FALSE;
+
+ /* Allocate a new SIGNAL structure */
+ pSig = (SIGNAL *)LocalAlloc(LMEM_FIXED, sizeof (SIGNAL));
+ if (!pSig)
+ return FALSE;
+
+ /* Fill in the useful fields */
+ pSig->hTask = hTask;
+ pSig->lpfn = (LPFNCALLBACK)HelperSignalProc;
+ pSig->lpfnOld = (LPFNCALLBACK)
+ HelperSetSignalProc(hTask, (DWORD)HelperSignalProc);
+
+ /* If this is the only handler, just insert it */
+ if (!npSignalHead)
+ {
+ pSig->pNext = npSignalHead;
+ npSignalHead = pSig;
+ }
+
+ /* Otherwise, insert at the end of the list */
+ else
+ {
+ for (pTemp = npSignalHead ; pTemp->pNext ; pTemp = pTemp->pNext)
+ ;
+ pSig->pNext = pTemp->pNext;
+ pTemp->pNext = pSig;
+ }
+
+ return TRUE;
+}
+
+
+/* SignalUnRegister
+ * Called by an app whose callback is no longer to be used.
+ * NULL hTask uses current task.
+ */
+
+BOOL PASCAL SignalUnRegister(
+ HANDLE hTask)
+{
+ SIGNAL *pSig;
+ SIGNAL *pBefore;
+
+ /* NULL hTask means current task */
+ if (!hTask)
+ hTask = GetCurrentTask();
+
+ /* First try to find the task */
+ pBefore = NULL;
+ for (pSig = npSignalHead ; pSig ; pSig = pSig->pNext)
+ if (pSig->hTask == hTask)
+ break;
+ else
+ pBefore = pSig;
+ if (!pSig)
+ return FALSE;
+
+ /* Remove it from the list */
+ if (!pBefore)
+ npSignalHead = pSig->pNext;
+ else
+ pBefore->pNext = pSig->pNext;
+
+ /* Replace the old signal proc */
+ HelperSetSignalProc(hTask, (DWORD)pSig->lpfnOld);
+
+ /* Free the structure */
+ LocalFree((HANDLE)pSig);
+
+ return TRUE;
+}
+
+
diff --git a/private/mvdm/wow16/toolhelp/stack1.c b/private/mvdm/wow16/toolhelp/stack1.c
new file mode 100644
index 000000000..b7ed4a15d
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/stack1.c
@@ -0,0 +1,155 @@
+/***************************************************************************
+ * STACK1.C
+ *
+ * Code to support stack tracing on task stacks.
+ *
+ ***************************************************************************/
+
+#include "toolpriv.h"
+#include <newexe.h>
+#include <string.h>
+
+/* ----- Function prototypes ----- */
+
+ NOEXPORT void StackTraceInfo(
+ STACKTRACEENTRY FAR *lpStack);
+
+/* ----- Functions ----- */
+
+/* StackTraceFirst
+ * Starts a task stack trace by returning information about the
+ * first frame on the task's stack.
+ */
+
+BOOL TOOLHELPAPI StackTraceFirst(
+ STACKTRACEENTRY FAR *lpStackTrace,
+ HANDLE hTDB)
+{
+ /* Check the version number and verify proper installation */
+ if (!wLibInstalled || !lpStackTrace ||
+ lpStackTrace->dwSize != sizeof (STACKTRACEENTRY))
+ return FALSE;
+
+ /* Get the first value */
+ if (!(StackFrameFirst(lpStackTrace, hTDB)))
+ return FALSE;
+
+ /* Get module and segment number information */
+ StackTraceInfo(lpStackTrace);
+
+ return TRUE;
+}
+
+
+/* StackTraceCSIPFirst
+ * Traces the stack of an arbitrary CS:IP. All parameters must be
+ * given, and once started, the StackTraceNext function can be used
+ * to trace the remainder of the stack
+ */
+
+BOOL TOOLHELPAPI StackTraceCSIPFirst(
+ STACKTRACEENTRY FAR *lpStack,
+ WORD wSS,
+ WORD wCS,
+ WORD wIP,
+ WORD wBP)
+{
+ /* Check the version number and verify proper installation */
+ if (!wLibInstalled || !lpStack ||
+ lpStack->dwSize != sizeof (STACKTRACEENTRY))
+ return FALSE;
+
+ /* Get the user information */
+ lpStack->wSS = wSS;
+ lpStack->wCS = wCS;
+ lpStack->wIP = wIP;
+ lpStack->wBP = wBP;
+
+ /* Get module and segment number information */
+ StackTraceInfo(lpStack);
+
+ /* Set the hTask to the current task as we are in the current task
+ * context. The CS may not be owned by this task, but at least
+ * we put a reasonable value in there.
+ */
+ lpStack->hTask = GetCurrentTask();
+
+ return TRUE;
+}
+
+
+/* StackTraceNext
+ * Continues a stack trace by returning information about the next
+ * frame on the task's stack.
+ * structure.
+ */
+
+BOOL TOOLHELPAPI StackTraceNext(
+ STACKTRACEENTRY FAR *lpStackTrace)
+{
+ /* Check the version number and verify proper installation */
+ if (!wLibInstalled || !lpStackTrace ||
+ lpStackTrace->dwSize != sizeof (STACKTRACEENTRY))
+ return FALSE;
+
+ /* Get information about this frame */
+ if (!StackFrameNext(lpStackTrace))
+ return FALSE;
+
+ /* Get module and segment number information */
+ StackTraceInfo(lpStackTrace);
+
+ return TRUE;
+}
+
+/* ----- Helper functions ----- */
+
+/* StackTraceInfo
+ * Gets module and segment number info about the given entry
+ */
+
+NOEXPORT void StackTraceInfo(
+ STACKTRACEENTRY FAR *lpStack)
+{
+ GLOBALENTRY GlobalEntry;
+ struct new_exe FAR *lpNewExe;
+ struct new_seg1 FAR *lpSeg;
+ WORD i;
+
+ /* If we have a NULL CS, this is a NEAR frame. Just return because we
+ * assume the user hasn't trashed the structure. The module and seg
+ * info will be the same as the last time
+ */
+ if (!lpStack->wCS)
+ return;
+
+ /* Get information about the code segment block */
+ GlobalEntry.dwSize = sizeof (GLOBALENTRY);
+ if (!GlobalEntryHandle(&GlobalEntry, lpStack->wCS))
+ return;
+
+ /* The owner of all code segments is the hModule */
+ lpStack->hModule = GlobalEntry.hOwner;
+
+ /* To find the segment number, we look in the EXE header and count the
+ * listed segments till we find this one
+ */
+
+ /* Get a pointer to the EXE Header module */
+ lpNewExe = MAKEFARPTR(HelperHandleToSel(lpStack->hModule), 0);
+
+ /* Make sure this is a EXE Header segment */
+ if (lpNewExe->ne_magic != NEMAGIC)
+ return;
+
+ /* Get the list of segments and go for it */
+ lpSeg = MAKEFARPTR(HIWORD((DWORD)lpNewExe), lpNewExe->ne_segtab);
+ for (i = 0 ; i < lpNewExe->ne_cseg ; ++i, ++lpSeg)
+ if (HelperHandleToSel(lpSeg->ns_handle) == lpStack->wCS)
+ break;
+ if (i == lpNewExe->ne_cseg)
+ return;
+
+ /* Save the segment number (seg numbers start at one) */
+ lpStack->wSegment = i + 1;
+}
diff --git a/private/mvdm/wow16/toolhelp/stack2.asm b/private/mvdm/wow16/toolhelp/stack2.asm
new file mode 100644
index 000000000..61bdc4308
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/stack2.asm
@@ -0,0 +1,178 @@
+;**************************************************************************
+;* STACK2.ASM
+;*
+;* Assembly support code for stack tracing.
+;*
+;**************************************************************************
+
+ INCLUDE TOOLPRIV.INC
+ INCLUDE TDB.INC
+
+;** External functions
+
+externNP HelperVerifySeg
+externNP HelperHandleToSel
+
+;** Functions
+
+sBegin CODE
+ assumes CS,CODE
+
+; StackFrameFirst
+; Returns information about the first stack frame and checks it
+; for validity as much as possible. The first stack frame is found
+; by getting the information from the TDB. If this task is active,
+; or if the task was changed in an unusual way, this information
+; may be incorrect. If it is, the user must set up the first
+; CS, IP, and BP, and BPNext in the user structure, log it as the
+; first stack trace and call StackTraceNext directly.
+
+cProc StackFrameFirst, <NEAR,PUBLIC>, <si,di,ds>
+ parmD lpStack
+ parmW hTDB
+cBegin
+ ;** Verify that we have a good TDB first
+ ;** Start by verifying the selector
+ mov ax,hTDB ;Get the selector
+ cCall HelperHandleToSel, <ax> ;Convert it to a selector
+ push ax ;Save it
+ mov bx,TDBSize
+ cCall HelperVerifySeg, <ax,bx>
+ pop bx ;Get selector back
+ or ax,ax ;FALSE return?
+ jnz SHORT SF_SelOk ;Selector's OK
+ xor ax,ax ;Return FALSE
+ jmp SHORT SF_End
+SF_SelOk:
+
+ ;** Verify that the TDB signature matches
+ mov ds,bx ;Point with DS
+ cmp ds:[TDB_sig],TDB_SIGNATURE ;Is this really a TDB?
+ jz SF_SigOk ;Must be
+ xor ax,ax ;Return FALSE
+ jmp SHORT SF_End
+SF_SigOk:
+
+ ;** Get the BP value from the task stack and store in structure
+ les di,lpStack ;Get a pointer to the user struct
+ mov ax,ds:[TDB_taskSS] ;Get the SS value
+ mov bx,ds:[TDB_taskSP] ;Get the max segment offset we need
+ add bx,Task_CS + 2
+ cCall HelperVerifySeg, <ax,bx> ;Make sure we can read all this
+ or ax,ax ;Error?
+ jz SF_End ;Yes, can't do walk
+ lds bx,DWORD PTR ds:[TDB_taskSP] ;Get the SS:SP value
+ mov si,ds:[bx].Task_BP ;Get the BP value from the stack
+ and si,NOT 1 ;Clear the FAR frame bit, if any
+ mov es:[di].st_wBP,si ;Store the BP value
+ mov ax,ds:[bx].Task_IP ;Store initial IP
+ mov es:[di].st_wIP,ax
+ mov ax,ds:[bx].Task_CS ;Store the initial CS
+ mov es:[di].st_wCS,ax
+
+ ;** Return as much info as possible about this first frame
+ mov ax,hTDB ;Get the TDB handle
+ mov es:[di].st_hTask,ax ;Save in structure
+ mov es:[di].st_wSS,ds ;Save the SS value
+ mov es:[di].st_wFlags,FRAME_FAR ;Force a FAR frame this time
+
+ ;** Try to verify this stuff
+ xor ax,ax ;In case we need to exit
+ or si,si ;End of the line?
+ jz SF_End ;Nope
+ cmp si,ds:[0ah] ;Compare against stack top
+ jb SF_End ;Fine with top
+ cmp si,ds:[0eh] ;Check against stack bottom
+ jae SF_End ;OK with bottom too
+ mov ax,1 ;Return TRUE
+
+SF_End:
+cEnd
+
+
+; StackFrameNext
+; Returns information in a public structure about the stack frame
+; pointed to by the BP value passed in. Returns TRUE if the
+; information seems valid, or FALSE if information could not be
+; returned.
+
+cProc StackFrameNext, <NEAR,PUBLIC>, <si,di,ds>
+ parmD lpStack
+cBegin
+ ;** Get pointers to the frame
+ les di,lpStack ;Get a pointer to the structure
+ mov ax,es:[di].st_wSS ;Get the stack segment
+ mov ds,ax ;Point with DS
+
+ ;** Get the next stack frame
+ mov si,es:[di].st_wBP ;Get the current BP value
+ lea ax,[si + 6] ;Get the max stack probe
+ cmp ax,si ;No stack wraparound allowed
+ jb SN_End ;If below, we have wrapped
+ cCall HelperVerifySeg, <ds,ax> ;Make sure the stack is OK
+ or ax,ax ;OK?
+ jnz @F ;Yes.
+ jmp SHORT SN_End ;Return FALSE
+@@: mov dx,ds:[si+4] ;DX:CX is the return address
+ mov cx,ds:[si+2]
+ mov bx,ds:[si] ;Get next BP value
+
+ ;** Zero BP is end of chain
+ xor ax,ax ;In case we need to exit
+ or bx,bx ;End of the line?
+ jz SN_End ;Nope
+
+ ;** If the new BP is higher on the stack than the old, it's invalid
+ cmp bx,si ;New BP <= Old BP?
+ jbe SN_End ;OK.
+
+ ;** Make sure we're still on the stack (variables from KDATA.ASM)
+ cmp bx,ds:[0ah] ;Compare against stack top
+ jb SN_End ;Fine with top
+ cmp bx,ds:[0eh] ;Check against stack bottom
+ jae SN_End ;OK with bottom too
+
+ ;** Return what we can about the frame
+ mov es:[di].st_wSS,ds ;Save the SS value
+ mov es:[di].st_wBP,si ; and the BP value
+ test bx,1 ;Far or near frame?
+ jnz SN_FarFrame ;For sure far if BP is odd
+
+ ;** Even when BP is not odd, we may have a far frame
+ mov ax,cs ;Get our RPL bits
+ and al,3 ;Mask RPL bits
+ mov ah,dl ;Get frame's RPL bits
+ and ah,3 ;Mask RPL bits
+ cmp al,ah ;If CS is a handle, they won't match
+ jne SN_NearFrame ;Bits don't match
+ lar ax,dx ;Get the access bits
+ test ax,800h ;Is this a code segment?
+ jz SN_NearFrame ;No. MUST be near frame
+ lsl ax,dx ;Get the limit
+ cmp ax,cx ;Inside limit?
+ jbe SN_NearFrame ;No. MUST be near
+
+ ;** Otherwise, probably is a far frame. It may not be, of course,
+ ;** because this may be a code seg parameter
+SN_FarFrame:
+ mov es:[di].st_wIP,cx ;Save the offset
+ mov es:[di].st_wCS,dx ; and selector value
+ mov ax,FRAME_FAR ;Tell the user what we did
+ and bx,NOT 1 ;Clear the far frame bit
+ jmp SHORT SN_20 ;Skip near section
+
+ ;** Must be a near frame
+SN_NearFrame:
+ mov es:[di].st_wIP,cx ;Save the offset
+ ;Leave the old CS value in
+ mov ax,FRAME_NEAR ;Tell the user what we did
+SN_20: mov es:[di].st_wFlags,ax ;Save in the structure
+ mov es:[di].st_wBP,bx ;Save BP in the structure
+ mov ax,1 ;Return TRUE
+
+SN_End:
+cEnd
+
+sEnd
+
+ END
diff --git a/private/mvdm/wow16/toolhelp/string.h b/private/mvdm/wow16/toolhelp/string.h
new file mode 100644
index 000000000..2c39b8d91
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/string.h
@@ -0,0 +1,121 @@
+/***
+*string.h - declarations for string manipulation functions
+*
+* Copyright (c) 1985-1990, Microsoft Corporation. All rights reserved.
+*
+*Purpose:
+* This file contains the function declarations for the string
+* manipulation functions.
+* [ANSI/System V]
+*
+****/
+
+#if defined(_DLL) && !defined(_MT)
+#error Cannot define _DLL without _MT
+#endif
+
+#ifdef _MT
+#define _FAR_ _far
+#else
+#define _FAR_
+#endif
+
+#ifndef _SIZE_T_DEFINED
+typedef unsigned int size_t;
+#define _SIZE_T_DEFINED
+#endif
+
+/* function prototypes */
+
+void _FAR_ * _FAR_ _cdecl memccpy(void _FAR_ *, const void _FAR_ *,
+ int, unsigned int);
+void _FAR_ * _FAR_ _cdecl memchr(const void _FAR_ *, int, size_t);
+int _FAR_ _cdecl memcmp(const void _FAR_ *, const void _FAR_ *,
+ size_t);
+int _FAR_ _cdecl memicmp(const void _FAR_ *, const void _FAR_ *,
+ unsigned int);
+void _FAR_ * _FAR_ _cdecl memcpy(void _FAR_ *, const void _FAR_ *,
+ size_t);
+void _FAR_ * _FAR_ _cdecl memmove(void _FAR_ *, const void _FAR_ *,
+ size_t);
+void _FAR_ * _FAR_ _cdecl memset(void _FAR_ *, int, size_t);
+void _FAR_ _cdecl movedata(unsigned int, unsigned int, unsigned int,
+ unsigned int, unsigned int);
+char _FAR_ * _FAR_ _cdecl strcat(char _FAR_ *, const char _FAR_ *);
+char _FAR_ * _FAR_ _cdecl strchr(const char _FAR_ *, int);
+int _FAR_ _cdecl strcmp(const char _FAR_ *, const char _FAR_ *);
+int _FAR_ _cdecl strcmpi(const char _FAR_ *, const char _FAR_ *);
+int _FAR_ _cdecl strcoll(const char _FAR_ *, const char _FAR_ *);
+int _FAR_ _cdecl stricmp(const char _FAR_ *, const char _FAR_ *);
+char _FAR_ * _FAR_ _cdecl strcpy(char _FAR_ *, const char _FAR_ *);
+size_t _FAR_ _cdecl strcspn(const char _FAR_ *, const char _FAR_ *);
+char _FAR_ * _FAR_ _cdecl strdup(const char _FAR_ *);
+char _FAR_ * _FAR_ _cdecl _strerror(const char _FAR_ *);
+char _FAR_ * _FAR_ _cdecl strerror(int);
+size_t _FAR_ _cdecl strlen(const char _FAR_ *);
+char _FAR_ * _FAR_ _cdecl strlwr(char _FAR_ *);
+char _FAR_ * _FAR_ _cdecl strncat(char _FAR_ *, const char _FAR_ *,
+ size_t);
+int _FAR_ _cdecl strncmp(const char _FAR_ *, const char _FAR_ *,
+ size_t);
+int _FAR_ _cdecl strnicmp(const char _FAR_ *, const char _FAR_ *,
+ size_t);
+char _FAR_ * _FAR_ _cdecl strncpy(char _FAR_ *, const char _FAR_ *,
+ size_t);
+char _FAR_ * _FAR_ _cdecl strnset(char _FAR_ *, int, size_t);
+char _FAR_ * _FAR_ _cdecl strpbrk(const char _FAR_ *,
+ const char _FAR_ *);
+char _FAR_ * _FAR_ _cdecl strrchr(const char _FAR_ *, int);
+char _FAR_ * _FAR_ _cdecl strrev(char _FAR_ *);
+char _FAR_ * _FAR_ _cdecl strset(char _FAR_ *, int);
+size_t _FAR_ _cdecl strspn(const char _FAR_ *, const char _FAR_ *);
+char _FAR_ * _FAR_ _cdecl strstr(const char _FAR_ *,
+ const char _FAR_ *);
+char _FAR_ * _FAR_ _cdecl strtok(char _FAR_ *, const char _FAR_ *);
+char _FAR_ * _FAR_ _cdecl strupr(char _FAR_ *);
+size_t _FAR_ _cdecl strxfrm (char _FAR_ *, const char _FAR_ *,
+ size_t);
+
+/* model independent function prototypes */
+
+void _far * _far _cdecl _fmemccpy(void _far *, const void _far *,
+ int, unsigned int);
+void _far * _far _cdecl _fmemchr(const void _far *, int, size_t);
+int _far _cdecl _fmemcmp(const void _far *, const void _far *,
+ size_t);
+void _far * _far _cdecl _fmemcpy(void _far *, const void _far *,
+ size_t);
+int _far _cdecl _fmemicmp(const void _far *, const void _far *,
+ unsigned int);
+void _far * _far _cdecl _fmemmove(void _far *, const void _far *,
+ size_t);
+void _far * _far _cdecl _fmemset(void _far *, int, size_t);
+char _far * _far _cdecl _fstrcat(char _far *, const char _far *);
+char _far * _far _cdecl _fstrchr(const char _far *, int);
+int _far _cdecl _fstrcmp(const char _far *, const char _far *);
+int _far _cdecl _fstricmp(const char _far *, const char _far *);
+char _far * _far _cdecl _fstrcpy(char _far *, const char _far *);
+size_t _far _cdecl _fstrcspn(const char _far *, const char _far *);
+char _far * _far _cdecl _fstrdup(const char _far *);
+char _near * _far _cdecl _nstrdup(const char _far *);
+size_t _far _cdecl _fstrlen(const char _far *);
+char _far * _far _cdecl _fstrlwr(char _far *);
+char _far * _far _cdecl _fstrncat(char _far *, const char _far *,
+ size_t);
+int _far _cdecl _fstrncmp(const char _far *, const char _far *,
+ size_t);
+int _far _cdecl _fstrnicmp(const char _far *, const char _far *,
+ size_t);
+char _far * _far _cdecl _fstrncpy(char _far *, const char _far *,
+ size_t);
+char _far * _far _cdecl _fstrnset(char _far *, int, size_t);
+char _far * _far _cdecl _fstrpbrk(const char _far *,
+ const char _far *);
+char _far * _far _cdecl _fstrrchr(const char _far *, int);
+char _far * _far _cdecl _fstrrev(char _far *);
+char _far * _far _cdecl _fstrset(char _far *, int);
+size_t _far _cdecl _fstrspn(const char _far *, const char _far *);
+char _far * _far _cdecl _fstrstr(const char _far *,
+ const char _far *);
+char _far * _far _cdecl _fstrtok(char _far *, const char _far *);
+char _far * _far _cdecl _fstrupr(char _far *);
diff --git a/private/mvdm/wow16/toolhelp/task1.c b/private/mvdm/wow16/toolhelp/task1.c
new file mode 100644
index 000000000..01286b0af
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/task1.c
@@ -0,0 +1,73 @@
+/*************************************************************************
+ * TASK1.C
+ *
+ * Routines used to enumerate all tasks.
+ *
+ *************************************************************************/
+
+#include <string.h>
+#include "toolpriv.h"
+
+/* ----- Functions ----- */
+
+/* TaskFirst
+ * Returns information about the first task in the task chain.
+ */
+
+BOOL TOOLHELPAPI TaskFirst(
+ TASKENTRY FAR *lpTask)
+{
+ /* Check for errors */
+ if (!wLibInstalled || !lpTask || lpTask->dwSize != sizeof (TASKENTRY))
+ return FALSE;
+
+ /* Pass a pointer to the first block to the assembly routine */
+ return TaskInfo(lpTask, *(WORD FAR *)MAKEFARPTR(segKernel, npwTDBHead));
+}
+
+
+/* TaskNext
+ * Returns information about the next task in the task chain.
+ */
+
+BOOL TOOLHELPAPI TaskNext(
+ TASKENTRY FAR *lpTask)
+{
+ /* Check for errors */
+ if (!wLibInstalled || !lpTask || !lpTask->hNext ||
+ lpTask->dwSize != sizeof (TASKENTRY))
+ return FALSE;
+
+ /* Pass a pointer to the next block to the assembly routine */
+ return TaskInfo(lpTask, lpTask->hNext);
+}
+
+
+/* TaskFindHandle
+ * Returns information about the task with the given task handle.
+ */
+
+BOOL TOOLHELPAPI TaskFindHandle(
+ TASKENTRY FAR *lpTask,
+ HANDLE hTask)
+{
+ /* Check for errors */
+ if (!wLibInstalled || !lpTask || lpTask->dwSize != sizeof (TASKENTRY))
+ return FALSE;
+
+#ifdef WOW
+ if ( (hTask & 0x4) == 0 && hTask <= 0xffe0 && hTask != 0 ) {
+ //
+ // If they are getting a task handle for an htask alias, then
+ // just fill in the hinst method and return.
+ //
+ // Special hack for OLE 2.0's BusyDialog.
+ //
+ lpTask->hInst = hTask;
+ return( TRUE );
+ }
+#endif
+
+ /* Pass a pointer to the first block to the assembly routine */
+ return TaskInfo(lpTask, hTask);
+}
diff --git a/private/mvdm/wow16/toolhelp/task2.asm b/private/mvdm/wow16/toolhelp/task2.asm
new file mode 100644
index 000000000..f01829234
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/task2.asm
@@ -0,0 +1,326 @@
+;**************************************************************************
+;* TASK2.ASM
+;*
+;* Assembly support for the task enumeration routines.
+;*
+;**************************************************************************
+
+ INCLUDE TOOLPRIV.INC
+
+PMODE32 = 0
+PMODE = 0
+SWAPPRO = 0
+ INCLUDE TDB.INC
+ifdef WOW
+ INCLUDE WOW.INC
+endif
+
+;** Data
+
+sBegin DATA
+
+lpfnRetAddr DD 0 ;Return address after stack switch
+
+sEnd
+
+;** External functions
+externNP HelperHandleToSel
+externNP HelperVerifySeg
+externFP GetCurrentTask
+externFP DirectedYield
+ifdef WOW
+externFP PostAppMessage
+endif
+
+;** Functions
+
+.286p
+
+sBegin CODE
+ assumes CS,CODE
+ assumes DS,DATA
+
+
+; TaskSetCSIP
+; Allows the user to set the CS:IP of a sleeping task so that it will
+; begin execution at this address when the task is yielded to.
+; Returns the old address.
+
+cProc TaskSetCSIP, <PUBLIC,FAR>, <si>
+ parmW hTask
+ parmW wCS
+ parmW wIP
+cBegin
+ assumes DS,nothing
+ assumes ES,nothing
+
+ ;** If this is the current task, do nothing: we only work on
+ ;** sleeping tasks
+ cCall GetCurrentTask ;Gets current task in AX
+ mov bx,hTask ;Get desired task
+ cmp ax,bx ;Same?
+ jne @F ;No, it's OK
+ xor ax,ax ;Return a DWORD zero
+ cwd
+ jmp SHORT TC_End
+@@:
+
+ ;** Get the TDB SS:SP
+ mov es,bx ;Point to TDB with ES
+ les si,DWORD PTR es:[TDB_TaskSP] ;Get a pointer to the task stack
+ifdef WOW
+ ;
+ ; ES:SI now points to the place where we had the TDB's SS:SP pointing
+ ; This spot in wow is actually the SS:BP frame of the WOW16CALL
+ ; function. The definitions for this frame come from WOW.INC (WOW.H).
+ ; The addition of this strange value adjusts the SS:SP pointer back
+ ; onto the stack, undoing a previous adjustment in TASKING.ASM
+ add si,(vf_vpCSIP-vf_wThunkCSIP)
+endif
+ ;** Change the CS:IP
+ mov ax,wIP ;Get the new IP value
+ xchg ax,es:[si].Task_IP ;Swap with the old one
+ mov dx,wCS ;Get the new CS value
+ xchg dx,es:[si].Task_CS ;Swap with the old one
+
+TC_End:
+cEnd
+
+
+; TaskGetCSIP
+; Gets the next CS:IP that this task will run at.
+
+cProc TaskGetCSIP, <PUBLIC,FAR>, <si>
+ parmW hTask
+cBegin
+ assumes DS,nothing
+ assumes ES,nothing
+
+ ;** If this is the current task, do nothing: we only work on
+ ;** sleeping tasks
+ cCall GetCurrentTask ;Gets current task in AX
+ mov bx,hTask ;Get desired task
+ cmp ax,bx ;Same?
+ jne @F ;No, it's OK
+ xor ax,ax ;Return a DWORD zero
+ cwd
+ jmp SHORT TG_End
+@@:
+
+ ;** Get the TDB SS:SP
+ mov es,bx ;Point to TDB with ES
+ les si,DWORD PTR es:[TDB_TaskSP] ;Get a pointer to the task stack
+
+ifdef WOW
+ ;
+ ; ES:SI now points to the place where we had the TDB's SS:SP pointing
+ ; This spot in wow is actually the SS:BP frame of the WOW16CALL
+ ; function. The definitions for this frame come from WOW.INC (WOW.H).
+ ; The addition of this strange value adjusts the SS:SP pointer back
+ ; onto the stack, undoing a previous adjustment in TASKING.ASM
+ add si,(vf_vpCSIP-vf_wThunkCSIP)
+endif
+ ;** Change the CS:IP
+ mov ax,es:[si].Task_IP ;Get the CS:IP to return
+ mov dx,es:[si].Task_CS
+
+TG_End:
+cEnd
+
+
+; TaskSwitch
+; Switches to the indicated task from the current one.
+; Returns FALSE if it couldn't task switch.
+; Jumps to the address given by lpJmpAddr
+
+cProc TaskSwitch, <PUBLIC,FAR>, <si,di>
+ parmW hTask
+ parmD lpJmpAddr
+cBegin
+ push ds
+ mov ax, _DATA ;Make sure to set DS
+ mov ds, ax
+ assumes ds,DATA
+
+ ;** Check to make sure TOOLHELP is installed
+ cmp wLibInstalled,0 ;Library installed?
+ pop ds
+ assumes ds,nothing
+ jnz @F ;Yes
+ xor ax,ax ;Return FALSE
+ jmp TS_End ;No. Fail the API
+@@:
+
+ ;** Get the task handle
+ cCall GetCurrentTask ;Get the current task
+ cmp ax,hTask ;Switch to current task?
+ jne @F ;No, it's OK
+ xor ax,ax ;Yes, we can't do that so return FALSE
+
+ifdef WOW
+ jmp TS_End
+else
+ jmp SHORT TS_End
+endif
+
+@@: cCall HelperVerifySeg, <hTask,TDB_sig+1> ;Verify the segment
+ or ax,ax ;Segment OK?
+ jz TS_End ;Nope. Get out
+ mov es,hTask ;Get the TDB
+ xor ax,ax ;Get a zero just in case
+ cmp es:[TDB_sig], TDB_SIGNATURE ;Signature match?
+ jne TS_End ;Return FALSE
+
+ ;** Poke in the address to jump to
+ mov si,es ;Save the hTask
+ lea ax,TS_FromYield ;Point to new task address
+ cCall TaskSetCSIP, <si,cs,ax> ;Set the new address
+ mov es,si ;Get hTask back
+
+ ;** Save the jump address from the stack so we can jump to it later
+ push ds
+ mov ax,_DATA ;Point to our data segment
+ mov ds,ax
+ assumes ds,DATA
+ mov ax,WORD PTR lpJmpAddr[0];Get the low word of the ret address
+ mov WORD PTR lpfnRetAddr[0],ax
+ mov ax,WORD PTR lpJmpAddr[2];Get the selector value
+ mov WORD PTR lpfnRetAddr[2],ax
+ pop ds
+
+ifdef WOW
+ ;** Force a task switch by posting a message. This is because the
+ ;** event count is not used under WOW.
+ cCall PostAppMessage,<es, 0, 0, 0, 0>
+else
+ ;** Force a task switch by tampering with the event count
+ inc es:[TDB_nEvents] ;Force at least one event so we
+ ; will switch to this task next
+endif ;WOW
+
+ ;** Switch to the new task. DirectedYield() returns only when this
+ ;** task is next scheduled
+ cCall DirectedYield, <si> ;Switch to the new task
+ mov ax,1 ;Return TRUE
+ jmp SHORT TS_End ;Get out
+
+ ;** Restore from the directed yield
+TS_FromYield:
+
+ ;** Make a stack frame to work on. We can't trash any regs
+ PUBLIC TS_FromYield
+ sub sp,4 ;Save room for a far ret frame
+ push bp ;Make a stack frame
+ mov bp,sp
+ pusha ;Save everything
+ push ds
+ push es
+
+ ;** Get our jump address from our DS and put in far ret frame
+ mov ax,_DATA ;Get the TOOLHELP DS
+ mov ds,ax
+ mov ax,WORD PTR lpfnRetAddr[0] ;Get the offset
+ mov [bp + 2],ax ;Put it on the stack
+ mov ax,WORD PTR lpfnRetAddr[2] ;Get the selector
+ mov [bp + 4],ax ;Put on the stack
+
+ ;** Restore the event count
+ mov es,segKernel ;Get the KERNEL segment
+ mov bx,npwTDBCur ;Get the current task pointer
+ mov es,es:[bx] ;Get the TDB pointer in ES
+ifndef WOW
+ dec es:[TDB_nEvents] ;Clear the dummy event we put in
+endif
+
+ ;** Clear the stack and 'return' to the new address
+ pop es
+ pop ds
+ popa
+ pop bp
+ retf
+
+TS_End:
+cEnd
+
+
+; TaskInfo
+;
+; Returns information about the task with the given block handle
+
+cProc TaskInfo, <PUBLIC,NEAR>, <si,di,ds>
+ parmD lpTask
+ parmW wTask
+cBegin
+ ;** Start by verifying the selector
+ mov ax,wTask ;Get the selector
+ cCall HelperHandleToSel, <ax> ;Convert it to a selector
+ push ax ;Save it
+ mov bx,TDBSize
+ cCall HelperVerifySeg, <ax,bx>
+ pop bx ;Get selector back
+ or ax,ax ;FALSE return?
+ jnz TI_SelOk ;Selector's OK
+ xor ax,ax ;Return FALSE
+ jmp TI_End
+TI_SelOk:
+
+ ;** Verify that the TDB signature matches
+ mov ds,bx ;Point with DS
+ cmp ds:[TDB_sig],TDB_SIGNATURE ;Is this really a TDB?
+ jz TI_SigOk ;Must be
+ xor ax,ax ;Return FALSE
+ jmp SHORT TI_End
+TI_SigOk:
+
+ ;** Now, get information from the TDB
+ les di,lpTask ;Point to destination buffer
+ mov ax,ds:[TDB_next] ;Get the next TDB handle
+ mov es:[di].te_hNext,ax ;Save in public structure
+ mov ax,wTask ;Get this task's handle
+ mov es:[di].te_hTask,ax ;Save in buffer
+ mov ax,ds:[TDB_Parent] ;Get this task's parent
+ mov es:[di].te_hTaskParent,ax ;Save
+ mov ax,ds:[TDB_taskSS] ;Get the SS
+ mov es:[di].te_wSS,ax
+ mov ax,ds:[TDB_taskSP] ;Get the SP
+ mov es:[di].te_wSP,ax
+ mov ax,ds:[TDB_nEvents] ;Event counter
+ mov es:[di].te_wcEvents,ax
+ mov ax,ds:[TDB_Queue] ;Queue pointer
+ mov es:[di].te_hQueue,ax
+ mov ax,ds:[TDB_PDB] ;Offset of DOS PSP
+ mov es:[di].te_wPSPOffset,ax
+ mov ax,ds:[TDB_Module] ;Instance handle (DS) of task
+ mov es:[di].te_hInst,ax
+ mov ax,ds:[TDB_pModule] ;Module database handle
+ mov es:[di].te_hModule,ax
+ mov cx,8 ;Copy module name
+ push di ;Save structure pointer
+ mov si,TDB_ModName ;Point to the string
+ add di,te_szModule ; and to the string dest
+ cld
+ repnz movsb ;Copy the string
+ mov BYTE PTR es:[di],0 ;Zero terminate it
+ pop di ;Get structure pointer back
+
+ ;** Get information from the stack segment. Vars from KDATA.ASM
+ mov ax,es:[di].te_wSS ;Get the SS value
+ verr ax ;OK to read here?
+ jnz TI_SkipThis ;No, so don't do it
+ mov ds,ax ;Point with DS
+ mov ax,ds:[0ah] ;Lowest value of SP allowed
+ mov es:[di].te_wStackTop,ax ;Save in structure
+ mov ax,ds:[0ch] ;Get stack minimum value so far
+ mov es:[di].te_wStackMinimum,ax ;Save in structure
+ mov ax,ds:[0eh] ;Largest value of SP allowed
+ mov es:[di].te_wStackBottom,ax ;Save in structure
+
+ ;** Return TRUE on success
+TI_SkipThis:
+ mov ax,1 ;Return TRUE code
+TI_End:
+cEnd
+
+sEnd
+
+ END
diff --git a/private/mvdm/wow16/toolhelp/terminat.asm b/private/mvdm/wow16/toolhelp/terminat.asm
new file mode 100644
index 000000000..c04a324c9
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/terminat.asm
@@ -0,0 +1,145 @@
+ PAGE 60,150
+;***************************************************************************
+;* TERMINAT.ASM
+;*
+;* Assembly code routine used for the TOOLHELP.DLL app terminate
+;* routine.
+;*
+;***************************************************************************
+
+ INCLUDE TOOLPRIV.INC
+ INCLUDE TDB.INC
+
+;** Symbols
+I_EXCEPTION EQU 0
+I_INTERRUPT EQU 1
+MAX_INTERRUPT EQU 5
+GIVE_WDEB386 EQU 8000h
+Q_HACK_30 EQU 54h
+
+.286p
+
+;** Data
+
+sBegin DATA
+
+wTermFlags DW ? ;Save terminate flags across Yield
+
+sEnd
+
+;** Imported values
+externFP InterruptUnRegister
+externFP NotifyUnRegister
+externFP GetCurrentTask
+externFP FatalAppExit
+externFP TaskSetCSIP
+externFP DirectedYield
+externFP TaskSwitch
+
+sBegin CODE
+ assumes CS,CODE
+ assumes DS,DATA
+
+; TerminateApp
+; Terminates the task in one of two ways: TERMINATE_NORMAL or
+; TERMINATE_USER_DISPLAY. TERMINATE_NORMAL calls KERNEL to display
+; the UAE box and terminates the app. TERMINATE_USER_DISPLAY also
+; terminates the app but assumes the user has displayed some warning.
+; If the task passed in is not the current task, this function does
+; the DirectedYield() to switch to the correct task before terminating
+; it.
+; This function does not return when terminating the current task
+; except when WDEB386 is installed and the (undocumented) GIVE_WDEB386
+; flag is set.
+; Caller: TerminateApp(
+; HANDLE hTask, (If NULL, does current task)
+; WORD wFlags)
+
+cProc TerminateApp, <FAR,PUBLIC>, <si,di,ds>
+ parmW hTask
+ parmW wFlags
+cBegin
+ mov ax, _DATA ;Get our DS
+ mov ds, ax
+
+ ;** Save the flags in the DS so we can get after DYield
+ mov ax,wFlags ;Get the parameter flags
+ mov wTermFlags,ax ;Save them
+
+ ;** Get the task value
+ cCall GetCurrentTask ;Get the task
+ mov si,hTask ;Get the hTask value
+ or si,si ;Zero?
+ jnz TA_10 ;No
+ mov es,ax ;Point ES at current task
+ jmp SHORT TA_NukeCurrent ;In this case we always nuke current
+TA_10:
+ ;** If this is the current task, just nuke it and don't return
+ cmp ax,si ;Current?
+ mov es,si ;Point ES at task
+ je TA_NukeCurrent ;Yes, nuke it directly
+
+ ;** Switch to the new task and prepare to nuke it
+ lea ax,TA_NewTask ;Get address of new task entry
+ cCall TaskSwitch, <si,cs,ax> ;Switch to this task
+ jmp SHORT TA_End ;Get out
+
+ ;** We're in the new task now
+TA_NewTask:
+ mov ax,_DATA ;Get the TOOLHELP DS
+ mov ds,ax
+ mov es,segKernel ;Get the KERNEL segment
+ mov bx,npwTDBCur ;Get the current task pointer
+ mov es,es:[bx] ;Get the TDB pointer in ES
+
+ ;** HACK ALERT!!!! In order to get USER to allow us to terminate
+ ;* this app, we are manually nuking the semaphore. This is
+ ;* at a fixed offsets in the Q structure and only needs to
+ ;** be done in 3.0
+ test wTHFlags,TH_WIN30 ;In 3.0?
+ jz TA_NukeCurrent ;No, don't do this ugly hack
+ push es ;Save ES while we play with the queue
+ mov es,es:[TDB_Queue] ;ES points to queue now
+ mov bx,Q_HACK_30 ;Get 3.0 offset
+ mov WORD PTR es:[bx],0 ;Clear the semaphore count
+ mov WORD PTR es:[bx + 2],0 ; and the semaphore value to wait for
+ pop es ;ES points to TDB again
+
+TA_NukeCurrent:
+ ;** Check the flag values. If NO_UAE_BOX, tell KERNEL
+ ;** not to display the normal UAE box.
+ test wTermFlags,NO_UAE_BOX ;Display the box?
+ jz TA_20 ;Yes, so skip this stuff
+ or es:[TDB_ErrMode],02 ;Set the no display box flag
+TA_20:
+ ;** Terminate the app using KERNEL
+ cCall FatalAppExit, <0,0,0> ;Do it
+
+ ;** If we're flagged that this is an internal terminate, we just want
+ ;* to return if WDEB is installed so that we can pass the
+ ;** fault on. To do this, we must return here to the caller.
+ test wFlags,GIVE_WDEB386 ;Internal entry?
+ jnz TA_End ;Yes, don't nuke app
+
+ ;** If KERNEL doesn't nuke the app (does this if WDEB386
+ ;** is installed), nuke it ourselves (no UAE box)
+ mov es,segKernel ;Get the KERNEL segment
+ mov bx,npwTDBCur ;Get the current task pointer
+ mov es,es:[bx] ; in ES
+ cmp WORD PTR es:[TDB_USignalProc] + 2,0 ;USER signal proc?
+ jz @F ;No
+ mov bx,0666h ;Death knell
+ mov di, -1
+ cCall es:[TDB_USignalProc],<es,bx,di,es:[TDB_Module],es:[TDB_Queue]>
+@@: mov ax,4CFFH ;Nuke the app
+ int 21h ;We don't return here
+
+TA_End:
+
+cEnd
+
+sEnd
+
+ END
+
+
diff --git a/private/mvdm/wow16/toolhelp/ththunks.asm b/private/mvdm/wow16/toolhelp/ththunks.asm
new file mode 100644
index 000000000..1b940c522
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/ththunks.asm
@@ -0,0 +1,50 @@
+ TITLE THTHUNKS.ASM
+ PAGE ,132
+;
+; WOW v1.0
+;
+; Copyright (c) 1991-1992, Microsoft Corporation
+;
+; THTHUNKS.ASM
+; Thunks in 16-bit space to route Windows API calls to WOW32
+;
+; History:
+;
+; 09-Nov-1992 Dave Hart (davehart)
+; Adapted from mvdm\wow16\kernel31\kthunks.asm for ToolHelp
+;
+; 02-Apr-1991 Matt Felton (mattfe)
+; Created.
+;
+
+ifndef WINDEBUG
+ KDEBUG = 0
+ WDEBUG = 0
+else
+ KDEBUG = 1
+ WDEBUG = 1
+endif
+
+
+ .286p
+
+ .xlist
+ include cmacros.inc
+ include wow.inc
+ include wowth.inc
+ .list
+
+sBegin CODE
+assumes CS,CODE
+assumes DS,NOTHING
+assumes ES,NOTHING
+
+; Kernel API thunks
+
+ ToolHelpThunk ClassFirst
+ ToolHelpThunk ClassNext
+
+
+sEnd CODE
+
+end
diff --git a/private/mvdm/wow16/toolhelp/timer.asm b/private/mvdm/wow16/toolhelp/timer.asm
new file mode 100644
index 000000000..ee984fa7e
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/timer.asm
@@ -0,0 +1,180 @@
+ PAGE 60,150
+;***************************************************************************
+;* TIMER.ASM
+;*
+;* Routines used to give a cleaner interface to the VTD.
+;* This interface also works on a 286 by calling GetTickCount() in
+;* this case.
+;*
+;***************************************************************************
+
+ INCLUDE TOOLPRIV.INC
+ INCLUDE WINDOWS.INC
+
+;** Symbols
+SI_CRITICAL EQU 1
+DI_CRITICAL EQU 2
+
+;** Imports
+externA __WinFlags
+externFP GetTickCount
+
+sBegin DATA
+
+dwLastTimeReturned dd 0
+wLastCountDown dw 0
+
+
+sEnd
+
+sBegin CODE
+ assumes CS,CODE
+ assumes DS,DATA
+
+; TimerCount
+; Returns the count from either the virtual timer device or from the
+; Windows function GetTickCount() depending on the processor.
+; Prototype:
+; BOOL FAR PASCAL TimerCount(
+; TIMERINFO FAR *lpTimer)
+;
+
+cProc TimerCount, <FAR,PUBLIC>, <si,di,ds>
+ parmD lpTimer
+ localD dwJumpAddr
+cBegin
+ mov ax, _DATA ;Get our data segment
+ mov es, ax
+
+ ;** Point to the structure
+ lds si,lpTimer ;Point to the structure
+
+ ;** Make sure the size is correct
+ xor ax,ax ;FALSE
+ cmp WORD PTR [si].ti_dwSize[2],0 ;High word must be zero
+ je @F
+ jmp TC_End
+@@: cmp WORD PTR [si].ti_dwSize[0],SIZE TIMERINFO ;Low word must match
+ je @F
+ jmp TC_End
+@@:
+
+ifndef WOW
+ ;** If we are in standard mode, always use Windows entry point
+ mov ax,__WinFlags ;Get the flags
+ test ax,WF_STANDARD ;Standard mode?
+ jnz TC_TryMMSys ;Yes, don't even try VTD
+.386p
+ ;** Try to get the VTD entry point
+ mov ax,1684h ;Get device entry point
+ mov bx,5 ;VTD device number
+ int 2fh ;Win386 entry point
+ mov ax,es ;Did we get a value?
+ or ax,di ; (zero means no device)
+ jz SHORT TC_UseWinAPI ;No VTD--use Win API
+
+ ;** Get the VTD values
+ mov WORD PTR dwJumpAddr[0],di ;Save the address
+ mov WORD PTR dwJumpAddr[2],es ;Save the address
+ mov ax,0101h ;VTD: ms since start of Win386
+ call DWORD PTR dwJumpAddr ;Call the VTD
+ jc SHORT TC_UseWinAPI ;Carry set means error
+ mov [si].ti_dwmsSinceStart,eax ;Save in structure
+ mov ax,0102h ;VTD: ms in this VM
+ call DWORD PTR dwJumpAddr ;Call the VTD
+ jc SHORT TC_UseWinAPI ;Carry set means VTD error
+ mov [si].ti_dwmsThisVM,eax ;Save value in structure
+ jmp TC_ReturnOK ;We're done
+.286p
+
+ ;** See if mmsystem timer is installed
+TC_TryMMSys:
+ cmp WORD PTR es:[lpfntimeGetTime][2], 0 ;Installed?
+ je TC_UseWinAPI ;No, do this the hard way
+ call DWORD PTR es:lpfntimeGetTime
+
+ ;** Fill the structure with this information
+ mov WORD PTR [si].ti_dwmsSinceStart[0],ax
+ mov WORD PTR [si].ti_dwmsSinceStart[2],dx
+ mov WORD PTR [si].ti_dwmsThisVM[0],ax
+ mov WORD PTR [si].ti_dwmsThisVM[2],dx
+ jmp TC_ReturnOK
+endif ; ndef WOW
+
+ ;** Use the Windows API
+TC_UseWinAPI:
+ cCall GetTickCount ;Call the Windows API
+ mov WORD PTR [si].ti_dwmsSinceStart[0],ax ;Save the value for now
+ mov WORD PTR [si].ti_dwmsSinceStart[2],dx
+
+ ;** Read the countdown timer. Note that the timer starts at 54 * 1193
+ ;** and counts down to zero. Each count is 1193 ms.
+ xor al,al ;Prepare to read tick count
+ out 43h,al ;Send to timer
+ in al,40h ;Get the low byte
+ mov ah,al ;Save in AH
+ in al,40h ;Get the high byte
+ xchg ah,al
+ mov dx,0ffffh ;Get total countdown amount
+ sub dx,ax ;Get number of counts expired
+ mov ax,dx ;Get the number in AX for div
+ xor dx,dx ;Zero the high word
+ mov cx,1193 ;Divide to get ms
+ div cx ;Divide it
+ mov cx, ax ;cx == saved Curr count
+
+ ;** Now fill the structure. Note that the 'ThisVM' entry is the
+ ;** same as the 'SinceStart' entry in standard mode.
+ xor dx, dx
+ add ax, WORD PTR [si].ti_dwmsSinceStart[0] ;Add this count in
+ adc dx, WORD PTR [si].ti_dwmsSinceStart[2]
+
+ ;** Check to make sure we didn't mess up. If we did (if the timer
+ ;** was reset right in the middle of us reading it). If we
+ ;** messed up, do it again until we get it right.
+ mov bx, _DATA ;Get our data segment
+ mov es, bx
+ cmp dx, WORD PTR es:dwLastTimeReturned[2]
+ jne TC_TimeOK
+ cmp ax, WORD PTR es:dwLastTimeReturned[0]
+ jae TC_TimeOK
+
+ ; New time is less than the old time so estimate the curr time
+ ; using LastTimeReturned as the base
+ mov ax, WORD PTR es:dwLastTimeReturned[0]
+ mov dx, WORD PTR es:dwLastTimeReturned[2]
+
+ xor bx, bx ;check for wrap
+ cmp cx, word ptr es:wLastCountDown
+ jae TC_NoWrap ;if wrap
+ add ax, cx ; += curr count
+ adc dx, 0
+ jmp short TC_TimeOK
+
+TC_NoWrap: ;else no wrap
+ mov bx, cx ; += Curr - LastCountDown
+ sub bx, word ptr es:wLastCountDown
+ add ax, bx
+ adc dx, 0
+
+TC_TimeOK:
+ mov word ptr es:wLastCountDown, cx
+ mov WORD PTR es:dwLastTimeReturned[0], ax
+ mov WORD PTR es:dwLastTimeReturned[2], dx
+ mov WORD PTR [si].ti_dwmsSinceStart[0], ax ;Save good count
+ mov WORD PTR [si].ti_dwmsSinceStart[2], dx
+ mov WORD PTR [si].ti_dwmsThisVM[0],ax ;Save in structure
+ mov WORD PTR [si].ti_dwmsThisVM[2],dx ;Save in structure
+
+TC_ReturnOK:
+ mov ax,1 ;Return TRUE
+
+TC_End:
+
+
+
+cEnd
+
+sEnd
+
+END
diff --git a/private/mvdm/wow16/toolhelp/toolhelp.c b/private/mvdm/wow16/toolhelp/toolhelp.c
new file mode 100644
index 000000000..c79d2438f
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/toolhelp.c
@@ -0,0 +1,147 @@
+/**************************************************************************
+ * TOOLHELP.C
+ *
+ * Contains the initalization and deinitialization code for the
+ * TOOLHELP DLL.
+ *
+ **************************************************************************/
+
+#include "toolpriv.h"
+#undef VERSION
+#include <mmsystem.h>
+
+/* ----- Global variables ----- */
+ WORD segKernel;
+ WORD wLibInstalled;
+ WORD wTHFlags;
+ HANDLE hMaster;
+ HANDLE hGDIHeap;
+ HANDLE hUserHeap;
+ WORD NEAR *npwExeHead;
+ WORD NEAR *npwTDBHead;
+ WORD NEAR *npwTDBCur;
+ DWORD NEAR *npdwSelTableStart;
+ WORD NEAR *npwSelTableLen;
+ FARPROC lpfnGetUserLocalObjType;
+ FARPROC lpfnFatalExitHook;
+ FARPROC lpfnNotifyHook;
+ LPFNUSUD lpfnUserSeeUserDo;
+ FARPROC lpfnGetFreeSystemResources;
+ FARPROC lpfntimeGetTime;
+ WORD wSel;
+ WORD wLRUCount;
+ char szKernel[] = "KERNEL";
+
+/* ----- Import values ----- */
+#define FATALEXITHOOK MAKEINTRESOURCE(318)
+#define GETUSERLOCALOBJTYPE MAKEINTRESOURCE(480)
+#define USERSEEUSERDO MAKEINTRESOURCE(216)
+#define HASGPHANDLER MAKEINTRESOURCE(338)
+#define TOOLHELPHOOK MAKEINTRESOURCE(341)
+#define GETFREESYSTEMRESOURCES MAKEINTRESOURCE(284)
+
+
+/* ToolHelpLibMain
+ * Called by DLL startup code.
+ * Initializes TOOLHELP.DLL.
+ */
+
+int PASCAL ToolHelpLibMain(
+ HANDLE hInstance,
+ WORD wDataSeg,
+ WORD wcbHeapSize,
+ LPSTR lpszCmdLine)
+{
+ HANDLE hKernel;
+ HANDLE hUser;
+ HANDLE hMMSys;
+
+ /* Unless we say otherwise, the library is installed OK */
+ wLibInstalled = TRUE;
+
+ /* Do the KERNEL type-checking. Puts the results in global variables */
+ KernelType();
+
+ /* If the KERNEL check failed (not in PMODE) return that the library did
+ * not correctly install but allow the load anyway.
+ */
+ if (!wTHFlags)
+ {
+ wLibInstalled = FALSE;
+
+ /* Return success anyway, just fails all API calls */
+ return 1;
+ }
+
+ /* Grab a selector. This is only necessary in Win30StdMode */
+ if (wTHFlags & TH_WIN30STDMODE)
+ wSel = HelperGrabSelector();
+
+ /* Get the User and GDI heap handles if possible */
+ hKernel = GetModuleHandle((LPSTR)szKernel);
+ hUser = GetModuleHandle("USER");
+ hUserHeap = UserGdiDGROUP(hUser);
+ hGDIHeap = UserGdiDGROUP(GetModuleHandle("GDI"));
+
+ /* Get all the functions we may need. These functions only exist in
+ * the 3.1 USER and KERNEL.
+ */
+ if (!(wTHFlags & TH_WIN30))
+ {
+ /* FatalExit hook */
+ lpfnFatalExitHook = GetProcAddress(hKernel, FATALEXITHOOK);
+
+ /* Internal USER routine to get head of class list */
+ lpfnUserSeeUserDo = (LPFNUSUD)(FARPROC)
+ GetProcAddress(hUser, USERSEEUSERDO);
+
+ /* Identifies objects on USER's local heap */
+ lpfnGetUserLocalObjType = GetProcAddress(hUser, GETUSERLOCALOBJTYPE);
+
+ /* Identifies parameter validation GP faults */
+ lpfnPV = GetProcAddress(hKernel, HASGPHANDLER);
+
+ /* See if the new TOOLHELP KERNEL hook is around */
+ lpfnNotifyHook = (FARPROC) GetProcAddress(hKernel, TOOLHELPHOOK);
+ if (lpfnNotifyHook)
+ wTHFlags |= TH_GOODPTRACEHOOK;
+
+ /* Get the USER system resources function */
+ lpfnGetFreeSystemResources = (FARPROC)
+ GetProcAddress(hUser, GETFREESYSTEMRESOURCES);
+ }
+
+ /* Make sure we don't ever call these in 3.0 */
+ else
+ {
+ lpfnFatalExitHook = NULL;
+ lpfnUserSeeUserDo = NULL;
+ lpfnGetUserLocalObjType = NULL;
+ lpfnPV = NULL;
+ }
+
+ /* Try to get the multimedia system timer function address */
+ hMMSys = GetModuleHandle("MMSYSTEM");
+ if (hMMSys)
+ {
+ TIMECAPS tc;
+ UINT (WINAPI* lpfntimeGetDevCaps)(
+ TIMECAPS FAR* lpTimeCaps,
+ UINT wSize);
+
+ /* Call the timer API to see if the timer's really installed,
+ * and if it is, get the address of the get time function
+ */
+ lpfntimeGetDevCaps =
+ GetProcAddress(hMMSys, MAKEINTRESOURCE(604));
+ if ((*lpfntimeGetDevCaps)(&tc, sizeof (tc)) == TIMERR_NOERROR)
+ lpfntimeGetTime =
+ GetProcAddress(hMMSys, MAKEINTRESOURCE(607));
+ }
+
+ /* Return success */
+ return 1;
+}
+
+
+
diff --git a/private/mvdm/wow16/toolhelp/toolhelp.def b/private/mvdm/wow16/toolhelp/toolhelp.def
new file mode 100644
index 000000000..0d6d6884a
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/toolhelp.def
@@ -0,0 +1,45 @@
+LIBRARY TOOLHELP
+DESCRIPTION 'TOOLHELP for WOW - Debug/Tool Helper library'
+EXETYPE WINDOWS
+
+CODE PRELOAD FIXED
+DATA PRELOAD FIXED SINGLE
+
+HEAPSIZE 512
+
+EXPORTS
+ WEP @1 RESIDENTNAME ;Internal
+ GLOBALHANDLETOSEL @50
+ GLOBALFIRST @51
+ GLOBALNEXT @52
+ GLOBALINFO @53
+ GLOBALENTRYHANDLE @54
+ GLOBALENTRYMODULE @55
+ LOCALINFO @56
+ LOCALFIRST @57
+ LOCALNEXT @58
+ MODULEFIRST @59
+ MODULENEXT @60
+ MODULEFINDNAME @61
+ MODULEFINDHANDLE @62
+ TASKFIRST @63
+ TASKNEXT @64
+ TASKFINDHANDLE @65
+ STACKTRACEFIRST @66
+ STACKTRACECSIPFIRST @67
+ STACKTRACENEXT @68
+ CLASSFIRST @69
+ CLASSNEXT @70
+ SYSTEMHEAPINFO @71
+ MEMMANINFO @72
+ NOTIFYREGISTER @73
+ NOTIFYUNREGISTER @74
+ INTERRUPTREGISTER @75
+ INTERRUPTUNREGISTER @76
+ TERMINATEAPP @77
+ MEMORYREAD @78
+ MEMORYWRITE @79
+ TIMERCOUNT @80
+ TASKSETCSIP @81
+ TASKGETCSIP @82
+ TASKSWITCH @83
diff --git a/private/mvdm/wow16/toolhelp/toolhelp.h b/private/mvdm/wow16/toolhelp/toolhelp.h
new file mode 100644
index 000000000..25b918f80
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/toolhelp.h
@@ -0,0 +1,469 @@
+/*****************************************************************************\
+* *
+* toolhelp.h - toolhelp.dll functions, types, and definitions *
+* *
+* Version 1.0 *
+* *
+* NOTE: windows.h must be #included first *
+* *
+* Copyright (c) 1992, Microsoft Corp. All rights reserved. *
+* *
+\*****************************************************************************/
+
+#ifndef _INC_TOOLHELP
+#define _INC_TOOLHELP
+
+#ifndef RC_INVOKED
+#pragma pack(1) /* Assume byte packing throughout */
+#endif
+
+#ifdef __cplusplus
+extern "C" { /* Assume C declarations for C++ */
+#endif /* __cplusplus */
+
+#ifndef _INC_WINDOWS /* If included with 3.0 headers... */
+#define LPCSTR LPSTR
+#define WINAPI FAR PASCAL
+#define CALLBACK FAR PASCAL
+#define UINT WORD
+#define HMODULE HANDLE
+#define HINSTANCE HANDLE
+#define HLOCAL HANDLE
+#define HGLOBAL HANDLE
+#define HTASK HANDLE
+#endif /* _INC_WINDOWS */
+
+/****** General symbols ******************************************************/
+#define MAX_DATA 11
+#define MAX_PATH 255
+#define MAX_MODULE_NAME 8 + 1
+#define MAX_CLASSNAME 255
+
+/****** Global heap walking ***************************************************/
+typedef struct tagGLOBALINFO
+{
+ DWORD dwSize;
+ WORD wcItems;
+ WORD wcItemsFree;
+ WORD wcItemsLRU;
+} GLOBALINFO;
+
+typedef struct tagGLOBALENTRY
+{
+ DWORD dwSize;
+ DWORD dwAddress;
+ DWORD dwBlockSize;
+ HGLOBAL hBlock;
+ WORD wcLock;
+ WORD wcPageLock;
+ WORD wFlags;
+ BOOL wHeapPresent;
+ HGLOBAL hOwner;
+ WORD wType;
+ WORD wData;
+ DWORD dwNext;
+ DWORD dwNextAlt;
+} GLOBALENTRY;
+
+/* GlobalFirst()/GlobalNext() flags */
+#define GLOBAL_ALL 0
+#define GLOBAL_LRU 1
+#define GLOBAL_FREE 2
+
+/* GLOBALENTRY.wType entries */
+#define GT_UNKNOWN 0
+#define GT_DGROUP 1
+#define GT_DATA 2
+#define GT_CODE 3
+#define GT_TASK 4
+#define GT_RESOURCE 5
+#define GT_MODULE 6
+#define GT_FREE 7
+#define GT_INTERNAL 8
+#define GT_SENTINEL 9
+#define GT_BURGERMASTER 10
+
+/* If GLOBALENTRY.wType==GT_RESOURCE, the following is GLOBALENTRY.wData: */
+#define GD_USERDEFINED 0
+#define GD_CURSORCOMPONENT 1
+#define GD_BITMAP 2
+#define GD_ICONCOMPONENT 3
+#define GD_MENU 4
+#define GD_DIALOG 5
+#define GD_STRING 6
+#define GD_FONTDIR 7
+#define GD_FONT 8
+#define GD_ACCELERATORS 9
+#define GD_RCDATA 10
+#define GD_ERRTABLE 11
+#define GD_CURSOR 12
+#define GD_ICON 14
+#define GD_NAMETABLE 15
+#define GD_MAX_RESOURCE 15
+
+/* GLOBALENTRY.wFlags */
+#define GF_PDB_OWNER 0x0100 /* Low byte is KERNEL flags */
+
+BOOL WINAPI GlobalInfo(GLOBALINFO FAR* lpGlobalInfo);
+BOOL WINAPI GlobalFirst(GLOBALENTRY FAR* lpGlobal, WORD wFlags);
+BOOL WINAPI GlobalNext(GLOBALENTRY FAR* lpGlobal, WORD wFlags);
+BOOL WINAPI GlobalEntryHandle(GLOBALENTRY FAR* lpGlobal, HGLOBAL hItem);
+BOOL WINAPI GlobalEntryModule(GLOBALENTRY FAR* lpGlobal, HMODULE hModule, WORD wSeg);
+WORD WINAPI GlobalHandleToSel(HGLOBAL hMem);
+
+/****** Local heap walking ***************************************************/
+
+typedef struct tagLOCALINFO
+{
+ DWORD dwSize;
+ WORD wcItems;
+} LOCALINFO;
+
+typedef struct tagLOCALENTRY
+{
+ DWORD dwSize;
+ HLOCAL hHandle;
+ WORD wAddress;
+ WORD wSize;
+ WORD wFlags;
+ WORD wcLock;
+ WORD wType;
+ WORD hHeap;
+ WORD wHeapType;
+ WORD wNext;
+} LOCALENTRY;
+
+/* LOCALENTRY.wHeapType flags */
+#define NORMAL_HEAP 0
+#define USER_HEAP 1
+#define GDI_HEAP 2
+
+/* LOCALENTRY.wFlags */
+#define LF_FIXED 1
+#define LF_FREE 2
+#define LF_MOVEABLE 4
+
+/* LOCALENTRY.wType */
+#define LT_NORMAL 0
+#define LT_FREE 0xff
+#define LT_GDI_PEN 1 /* LT_GDI_* is for GDI's heap */
+#define LT_GDI_BRUSH 2
+#define LT_GDI_FONT 3
+#define LT_GDI_PALETTE 4
+#define LT_GDI_BITMAP 5
+#define LT_GDI_RGN 6
+#define LT_GDI_DC 7
+#define LT_GDI_DISABLED_DC 8
+#define LT_GDI_METADC 9
+#define LT_GDI_METAFILE 10
+#define LT_GDI_MAX LT_GDI_METAFILE
+#define LT_USER_CLASS 1 /* LT_USER_* is for USER's heap */
+#define LT_USER_WND 2
+#define LT_USER_STRING 3
+#define LT_USER_MENU 4
+#define LT_USER_CLIP 5
+#define LT_USER_CBOX 6
+#define LT_USER_PALETTE 7
+#define LT_USER_ED 8
+#define LT_USER_BWL 9
+#define LT_USER_OWNERDRAW 10
+#define LT_USER_SPB 11
+#define LT_USER_CHECKPOINT 12
+#define LT_USER_DCE 13
+#define LT_USER_MWP 14
+#define LT_USER_PROP 15
+#define LT_USER_LBIV 16
+#define LT_USER_MISC 17
+#define LT_USER_ATOMS 18
+#define LT_USER_LOCKINPUTSTATE 19
+#define LT_USER_HOOKLIST 20
+#define LT_USER_USERSEEUSERDOALLOC 21
+#define LT_USER_HOTKEYLIST 22
+#define LT_USER_POPUPMENU 23
+#define LT_USER_HANDLETABLE 32
+#define LT_USER_MAX LT_USER_HANDLETABLE
+
+BOOL WINAPI LocalInfo(LOCALINFO FAR* lpLocal, HGLOBAL hHeap);
+BOOL WINAPI LocalFirst(LOCALENTRY FAR* lpLocal, HGLOBAL hHeap);
+BOOL WINAPI LocalNext(LOCALENTRY FAR* lpLocal);
+
+/****** Stack Tracing ********************************************************/
+
+typedef struct tagSTACKTRACEENTRY
+{
+ DWORD dwSize;
+ HTASK hTask;
+ WORD wSS;
+ WORD wBP;
+ WORD wCS;
+ WORD wIP;
+ HMODULE hModule;
+ WORD wSegment;
+ WORD wFlags;
+} STACKTRACEENTRY;
+
+/* STACKTRACEENTRY.wFlags values */
+#define FRAME_FAR 0
+#define FRAME_NEAR 1
+
+BOOL WINAPI StackTraceFirst(STACKTRACEENTRY FAR* lpStackTrace, HTASK hTask);
+BOOL WINAPI StackTraceCSIPFirst(STACKTRACEENTRY FAR* lpStackTrace,
+ WORD wSS, WORD wCS, WORD wIP, WORD wBP);
+BOOL WINAPI StackTraceNext(STACKTRACEENTRY FAR* lpStackTrace);
+
+/****** Module list walking **************************************************/
+
+typedef struct tagMODULEENTRY
+{
+ DWORD dwSize;
+ char szModule[MAX_MODULE_NAME + 1];
+ HMODULE hModule;
+ WORD wcUsage;
+ char szExePath[MAX_PATH + 1];
+ WORD wNext;
+} MODULEENTRY;
+
+BOOL WINAPI ModuleFirst(MODULEENTRY FAR* lpModule);
+BOOL WINAPI ModuleNext(MODULEENTRY FAR* lpModule);
+HMODULE WINAPI ModuleFindName(MODULEENTRY FAR* lpModule, LPCSTR lpstrName);
+HMODULE WINAPI ModuleFindHandle(MODULEENTRY FAR* lpModule, HMODULE hModule);
+
+/****** Task list walking *****************************************************/
+
+typedef struct tagTASKENTRY
+{
+ DWORD dwSize;
+ HTASK hTask;
+ HTASK hTaskParent;
+ HINSTANCE hInst;
+ HMODULE hModule;
+ WORD wSS;
+ WORD wSP;
+ WORD wStackTop;
+ WORD wStackMinimum;
+ WORD wStackBottom;
+ WORD wcEvents;
+ HGLOBAL hQueue;
+ char szModule[MAX_MODULE_NAME + 1];
+ WORD wPSPOffset;
+ HANDLE hNext;
+} TASKENTRY;
+
+BOOL WINAPI TaskFirst(TASKENTRY FAR* lpTask);
+BOOL WINAPI TaskNext(TASKENTRY FAR* lpTask);
+BOOL WINAPI TaskFindHandle(TASKENTRY FAR* lpTask, HTASK hTask);
+DWORD WINAPI TaskSetCSIP(HTASK hTask, WORD wCS, WORD wIP);
+DWORD WINAPI TaskGetCSIP(HTASK hTask);
+BOOL WINAPI TaskSwitch(HTASK hTask, DWORD dwNewCSIP);
+
+/****** Window Class enumeration **********************************************/
+
+typedef struct tagCLASSENTRY
+{
+ DWORD dwSize;
+ HMODULE hInst; /* This is really an hModule */
+ char szClassName[MAX_CLASSNAME + 1];
+ WORD wNext;
+} CLASSENTRY;
+
+BOOL WINAPI ClassFirst(CLASSENTRY FAR* lpClass);
+BOOL WINAPI ClassNext(CLASSENTRY FAR* lpClass);
+
+/****** Information functions *************************************************/
+
+typedef struct tagMEMMANINFO
+{
+ DWORD dwSize;
+ DWORD dwLargestFreeBlock;
+ DWORD dwMaxPagesAvailable;
+ DWORD dwMaxPagesLockable;
+ DWORD dwTotalLinearSpace;
+ DWORD dwTotalUnlockedPages;
+ DWORD dwFreePages;
+ DWORD dwTotalPages;
+ DWORD dwFreeLinearSpace;
+ DWORD dwSwapFilePages;
+ WORD wPageSize;
+} MEMMANINFO;
+
+BOOL WINAPI MemManInfo(MEMMANINFO FAR* lpEnhMode);
+
+typedef struct tagSYSHEAPINFO
+{
+ DWORD dwSize;
+ WORD wUserFreePercent;
+ WORD wGDIFreePercent;
+ HGLOBAL hUserSegment;
+ HGLOBAL hGDISegment;
+} SYSHEAPINFO;
+
+BOOL WINAPI SystemHeapInfo(SYSHEAPINFO FAR* lpSysHeap);
+
+/****** Interrupt Handling ****************************************************/
+
+/* Hooked interrupts */
+#define INT_DIV0 0
+#define INT_1 1
+#define INT_3 3
+#define INT_UDINSTR 6
+#define INT_STKFAULT 12
+#define INT_GPFAULT 13
+#define INT_BADPAGEFAULT 14
+#define INT_CTLALTSYSRQ 256
+
+/* TOOLHELP Interrupt callbacks registered with InterruptRegister should
+ * always be written in assembly language. The stack frame is not
+ * compatible with high level language conventions.
+ *
+ * This stack frame looks as follows to the callback. All registers
+ * should be preserved across this callback to allow restarting fault.
+ * ------------
+ * | Flags | [SP + 0Eh]
+ * | CS | [SP + 0Ch]
+ * | IP | [SP + 0Ah]
+ * | Handle | [SP + 08h]
+ * |Exception#| [SP + 06h]
+ * | AX | [SP + 04h] AX Saved to allow MakeProcInstance
+ * | Ret CS | [SP + 02h]
+ * SP---> | Ret IP | [SP + 00h]
+ * ------------
+ */
+BOOL WINAPI InterruptRegister(HTASK hTask, FARPROC lpfnIntCallback);
+BOOL WINAPI InterruptUnRegister(HTASK hTask);
+
+/* Notifications:
+ * When a notification callback is called, two parameters are passed
+ * in: a WORD, wID, and another DWORD, dwData. wID is one of
+ * the values NFY_* below. Callback routines should ignore unrecog-
+ * nized values to preserve future compatibility. Callback routines
+ * are also passed a dwData value. This may contain data or may be
+ * a FAR pointer to a structure, or may not be used depending on
+ * which notification is being received.
+ *
+ * In all cases, if the return value of the callback is TRUE, the
+ * notification will NOT be passed on to other callbacks. It has
+ * been handled. This should be used sparingly and only with certain
+ * notifications. Callbacks almost always return FALSE.
+ */
+
+/* NFY_UNKNOWN: An unknown notification has been returned from KERNEL. Apps
+ * should ignore these.
+ */
+#define NFY_UNKNOWN 0
+
+/* NFY_LOADSEG: dwData points to a NFYLOADSEG structure */
+#define NFY_LOADSEG 1
+typedef struct tagNFYLOADSEG
+{
+ DWORD dwSize;
+ WORD wSelector;
+ WORD wSegNum;
+ WORD wType; /* Low bit set if data seg, clear if code seg */
+ WORD wcInstance; /* Instance count ONLY VALID FOR DATA SEG */
+ LPCSTR lpstrModuleName;
+} NFYLOADSEG;
+
+/* NFY_FREESEG: LOWORD(dwData) is the selector of the segment being freed */
+#define NFY_FREESEG 2
+
+/* NFY_STARTDLL: dwData points to a NFYLOADSEG structure */
+#define NFY_STARTDLL 3
+typedef struct tagNFYSTARTDLL
+{
+ DWORD dwSize;
+ HMODULE hModule;
+ WORD wCS;
+ WORD wIP;
+} NFYSTARTDLL;
+
+/* NFY_STARTTASK: dwData is the CS:IP of the start address of the task */
+#define NFY_STARTTASK 4
+
+/* NFY_EXITTASK: The low byte of dwData contains the program exit code */
+#define NFY_EXITTASK 5
+
+/* NFY_DELMODULE: LOWORD(dwData) is the handle of the module to be freed */
+#define NFY_DELMODULE 6
+
+/* NFY_RIP: dwData points to a NFYRIP structure */
+#define NFY_RIP 7
+typedef struct tagNFYRIP
+{
+ DWORD dwSize;
+ WORD wIP;
+ WORD wCS;
+ WORD wSS;
+ WORD wBP;
+ WORD wExitCode;
+} NFYRIP;
+
+/* NFY_TASKIN: No data. Callback should do GetCurrentTask() */
+#define NFY_TASKIN 8
+
+/* NFY_TASKOUT: No data. Callback should do GetCurrentTask() */
+#define NFY_TASKOUT 9
+
+/* NFY_INCHAR: Return value from callback is used. If NULL, mapped to 'i' */
+#define NFY_INCHAR 10
+
+/* NFY_OUTSTR: dwData points to the string to be displayed */
+#define NFY_OUTSTR 11
+
+/* NFY_LOGERROR: dwData points to a NFYLOGERROR struct */
+#define NFY_LOGERROR 12
+typedef struct tagNFYLOGERROR
+{
+ DWORD dwSize;
+ UINT wErrCode;
+ void FAR* lpInfo; /* Error code-dependent */
+} NFYLOGERROR;
+
+/* NFY_LOGPARAMERROR: dwData points to a NFYLOGPARAMERROR struct */
+#define NFY_LOGPARAMERROR 13
+typedef struct tagNFYLOGPARAMERROR
+{
+ DWORD dwSize;
+ UINT wErrCode;
+ FARPROC lpfnErrorAddr;
+ void FAR* FAR* lpBadParam;
+} NFYLOGPARAMERROR;
+
+/* NotifyRegister() flags */
+#define NF_NORMAL 0
+#define NF_TASKSWITCH 1
+#define NF_RIP 2
+
+typedef BOOL (CALLBACK* LPFNNOTIFYCALLBACK)(WORD wID, DWORD dwData);
+
+BOOL WINAPI NotifyRegister(HTASK hTask, LPFNNOTIFYCALLBACK lpfn, WORD wFlags);
+BOOL WINAPI NotifyUnRegister(HTASK hTask);
+
+/****** Miscellaneous *********************************************************/
+
+void WINAPI TerminateApp(HTASK hTask, WORD wFlags);
+
+/* TerminateApp() flag values */
+#define UAE_BOX 0
+#define NO_UAE_BOX 1
+
+DWORD WINAPI MemoryRead(WORD wSel, DWORD dwOffset, void FAR* lpBuffer, DWORD dwcb);
+DWORD WINAPI MemoryWrite(WORD wSel, DWORD dwOffset, void FAR* lpBuffer, DWORD dwcb);
+
+typedef struct tagTIMERINFO
+{
+ DWORD dwSize;
+ DWORD dwmsSinceStart;
+ DWORD dwmsThisVM;
+} TIMERINFO;
+
+BOOL WINAPI TimerCount(TIMERINFO FAR* lpTimer);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#ifndef RC_INVOKED
+#pragma pack() /* Revert to default packing */
+#endif
+
+#endif /* !_INC_TOOLHELP */
diff --git a/private/mvdm/wow16/toolhelp/toolhelp.inc b/private/mvdm/wow16/toolhelp/toolhelp.inc
new file mode 100644
index 000000000..37537623b
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/toolhelp.inc
@@ -0,0 +1,292 @@
+;**************************************************************************
+;* TOOLHELP.INC
+;*
+;* Assembly-language public include file for TOOLHELP.DLL
+;*
+;**************************************************************************
+
+;** General symbol values
+MAX_DATA EQU 11
+MAX_PATH EQU 255
+MAX_MODULE_NAME EQU 8 + 1 ;Leave room for the zero and padding
+MAX_CLASSNAME EQU 255
+
+; ----- Global heap walking -----
+
+GLOBALENTRY STRUC
+ge_dwSize DD ? ;Structure version
+ge_dwAddress DD ? ;This block's address
+ge_dwBlockSize DD ? ;This block's size
+ge_hBlock DW ? ;This block's handle
+ge_wcLock DW ? ;Lock count
+ge_wcPageLock DW ? ;Page lock count
+ge_wFlags DW ? ;Block flags
+ge_wHeapPresent DW ? ;Size of available local heap
+ge_hOwner DW ? ;Owner handle
+ge_wType DW ? ;Block type
+ge_wData DW ? ;Type-specific data
+ge_dwNext DD ? ;Pointer to next block
+ge_dwNextAlt DD ? ;Next free/LRU block in chain
+GLOBALENTRY ENDS
+
+; ** GlobalFirst()/GlobalNext() flags
+GLOBAL_ALL EQU 0
+GLOBAL_LRU EQU 1
+GLOBAL_FREE EQU 2
+
+; ** GLOBALENTRY.ge_wType entries
+GT_UNKNOWN EQU 0
+GT_DGROUP EQU 1
+GT_DATA EQU 2
+GT_CODE EQU 3
+GT_TASK EQU 4
+GT_RESOURCE EQU 5
+GT_MODULE EQU 6
+GT_FREE EQU 7
+GT_INTERNAL EQU 8
+GT_SENTINEL EQU 9
+GT_BURGERMASTER EQU 10
+
+; ** if GLOBALENTRY.wType==GT_RESOURCE, the following is GLOBALENTRY.wData:
+GD_USERDEFINED EQU 0
+GD_CURSORCOMPONENT EQU 1
+GD_BITMAP EQU 2
+GD_ICONCOMPONENT EQU 3
+GD_MENU EQU 4
+GD_DIALOG EQU 5
+GD_STRING EQU 6
+GD_FONTDIR EQU 7
+GD_FONT EQU 8
+GD_ACCELERATORS EQU 9
+GD_RCDATA EQU 10
+GD_ERRTABLE EQU 11
+GD_CURSOR EQU 12
+GD_ICON EQU 14
+GD_NAMETABLE EQU 15
+GD_MAX_RESOURCE EQU 15
+
+;** GLOBALENTRY.wFlags
+GF_PDB_OWNER EQU 100h ;Low byte is KERNEL flags
+
+; ----- Local heap walking -----
+
+LOCALENTRY STRUC
+le_dwSize DD ?
+le_hHandle DW ?
+le_wAddress DW ?
+le_wSize DW ?
+le_wFlags DW ?
+le_wcLock DW ?
+le_wType DW ?
+le_hHeap DW ?
+le_wHeapType DW ?
+le_wNext DW ?
+LOCALENTRY ENDS
+
+; ** LOCALENTRY.wHeapType flags
+NORMAL_HEAP EQU 0
+USER_HEAP EQU 1
+GDI_HEAP EQU 2
+
+;** LOCALENTRY.wFlags
+LF_FIXED EQU 1
+LF_FREE EQU 2
+LF_MOVEABLE EQU 4
+
+;** LOCALENTRY.wType
+LT_NORMAL EQU 0
+LT_GDI_PEN EQU 1
+LT_GDI_BRUSH EQU 2
+LT_GDI_FONT EQU 3
+LT_GDI_PALETTE EQU 4
+LT_GDI_BITMAP EQU 5
+LT_GDI_RGN EQU 6
+LT_GDI_DC EQU 7
+LT_GDI_DISABLED_DC EQU 8
+LT_GDI_METADC EQU 9
+LT_GDI_METAFILE EQU 10
+LT_GDI_MAX EQU LT_GDI_METAFILE
+LT_FREE EQU 0ffh
+
+; ----- Stack tracing -----
+
+STACKTRACEENTRY STRUC
+st_dwSize DD ?
+st_hTask DW ?
+st_wSS DW ?
+st_wBP DW ?
+st_wCS DW ?
+st_wIP DW ?
+st_hModule DW ?
+st_wSegment DW ?
+st_wFlags DW ?
+STACKTRACEENTRY ENDS
+
+;** STACKTRACEENTRY.wFlags values
+FRAME_FAR EQU 0
+FRAME_NEAR EQU 1
+
+; ----- Module list walking -----
+
+MODULEENTRY STRUC
+me_dwSize DD ?
+me_szModule DB MAX_MODULE_NAME + 1 DUP(?)
+me_hModule DW ?
+me_wcUsage DW ?
+me_szExePath DB MAX_PATH + 1 + 1 DUP(?)
+me_wNext DW ?
+MODULEENTRY ENDS
+
+; ----- Task list walking -----
+
+TASKENTRY STRUC
+te_dwSize DD ?
+te_hTask DW ?
+te_hTaskParent DW ?
+te_hInst DW ?
+te_hModule DW ?
+te_wSS DW ?
+te_wSP DW ?
+te_wStackTop DW ?
+te_wStackMinimum DW ?
+te_wStackBottom DW ?
+te_wcEvents DW ?
+te_hQueue DW ?
+te_szModule DB MAX_MODULE_NAME + 1 DUP(?)
+te_wPSPOffset DW ?
+te_hNext DW ?
+TASKENTRY ENDS
+
+;** TaskSwitch() return values
+TS_ERROR EQU 0
+TS_NEW EQU 1
+TS_OLD EQU 2
+
+; ----- Window Class enumeration -----
+
+CLASSENTRY STRUC
+ce_dwSize DD ?
+ce_hInst DW ?
+ce_szClassName DB MAX_CLASSNAME + 1 DUP(?)
+ce_wNext DW ?
+CLASSENTRY ENDS
+
+; ----- Information functions -----
+
+VMEMMANINFO STRUC
+vmm_dwSize DD ?
+vmm_dwLargestFreeBlock DD ?
+vmm_dwMaxPagesAvailable DD ?
+vmm_dwMaxPagesLockable DD ?
+vmm_dwTotalLinearSpace DD ?
+vmm_dwTotalUnlockedPages DD ?
+vmm_dwFreePages DD ?
+vmm_dwTotalPages DD ?
+vmm_dwFreeLinearSpace DD ?
+vmm_dwSwapFilePages DD ?
+vmm_wPageSize DW ?
+VMEMMANINFO ENDS
+
+SYSHEAPINFO STRUC
+si_dwSize DD ?
+si_wUserFreePercent DW ?
+si_wGDIFreePercent DW ?
+si_hUserSegment DW ?
+si_hGDISegment DW ?
+SYSHEAPINFO ENDS
+
+; ----- Notifications -----
+
+;* NFY_UNKNOWN: An unknown notification has been returned from KERNEL. Apps
+;* should ignore these.
+;*
+NFY_UNKNOWN EQU 0
+
+;** NFY_LOADSEG: dwData points to a NFYLOADSEG structure
+NFY_LOADSEG EQU 1
+NFYLOADSEG STRUC
+nls_dwSize DD ?
+nls_wSelector DW ?
+nls_wSegNum DW ?
+nls_wType DW ? ;Low bit set for DATA, clear for CODE
+nls_wcInstance DW ? ;Instance count ONLY VALID FOR DATA!
+nls_lpstrModuleName DD ?
+NFYLOADSEG ENDS
+
+;** NFY_FREESEG: LOWORD(dwData) is the selector of the segment being freed
+NFY_FREESEG EQU 2
+
+;** NFY_STARTDLL: dwData points to a NFYSTARTDLL structure
+NFY_STARTDLL EQU 3
+NFYSTARTDLL STRUC
+nsd_dwSize DD ?
+nsd_hModule DW ?
+nsd_wCS DW ?
+nsd_wIP DW ?
+NFYSTARTDLL ENDS
+
+;** NFY_STARTTASK: dwData is the CS:IP of the task start address
+NFY_STARTTASK EQU 4
+
+;** NFY_EXITTASK: The low byte of dwData contains the program exit code
+NFY_EXITTASK EQU 5
+
+;** NFY_DELMODULE: LOWORD(dwData) is the handle of the module to be freed
+NFY_DELMODULE EQU 6
+
+;** NFY_RIP: LOWORD(dwData) is the fatal exit code
+;** NFY_RIP: dwData points to a NFYRIP structure
+NFY_RIP EQU 7
+NFYRIP STRUC
+nrp_dwSize DD ?
+nrp_wIP DW ?
+nrp_wCS DW ?
+nrp_wSS DW ?
+nrp_wBP DW ?
+nrp_wExitCode DW ?
+NFYRIP ENDS
+
+;** NFY_TASKIN: No data. Callback should do GetCurrentTask()
+NFY_TASKIN EQU 8
+
+;** NFY_TASKOUT: No data. Callback should do GetCurrentTask()
+NFY_TASKOUT EQU 9
+
+;** NFY_INCHAR: Return value of callback is used. If FALSE, mapped to 'i'
+NFY_INCHAR EQU 10
+
+;** NFY_OUTSTR: dwData points to the string to be displayed
+NFY_OUTSTR EQU 11
+
+;** NFY_LOGERROR: dwData points to a NFYLOGERROR struct
+NFY_LOGERROR EQU 12
+NFYLOGERROR STRUC
+nle_dwSize DD ?
+nle_wErrCode DW ?
+nle_lpInfo DD ? ;Error-code dependent
+NFYLOGERROR ENDS
+
+;** NFY_LOGPARAMERROR: dwData points to a NFYLOGPARAMERROR struct
+NFY_LOGPARAMERROR EQU 13
+NFYLOGPARAMERROR STRUC
+nlp_dwSize DD ?
+nlp_wErrCode DW ?
+nlp_lpfnErrorAddr DD ?
+nlp_lpBadParam DD ?
+NFYLOGPARAMERROR ENDS
+
+;** NotifyRegister() flags
+NF_NORMAL EQU 0
+NF_TASKSWITCH EQU 1
+NF_RIP EQU 2
+
+;** TerminateApp() flag values
+UAE_BOX EQU 0
+NO_UAE_BOX EQU 1
+
+TIMERINFO STRUC
+ti_dwSize DD ?
+ti_dwmsSinceStart DD ?
+ti_dwmsThisVM DD ?
+TIMERINFO ENDS
+
diff --git a/private/mvdm/wow16/toolhelp/toolhelp.rcv b/private/mvdm/wow16/toolhelp/toolhelp.rcv
new file mode 100644
index 000000000..014720674
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/toolhelp.rcv
@@ -0,0 +1,14 @@
+/********************************************************************/
+/* TOOLHELP.RCV */
+/* Version control data generated from layouts.dat */
+/********************************************************************/
+#include <version.h>
+
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Windows Debug/Tool helper library"
+#define VER_INTERNALNAME_STR "TOOLHELP"
+#define VER_ORIGINALFILENAME_STR "TOOLHELP.DLL"
+
+#include <common.ver>
diff --git a/private/mvdm/wow16/toolhelp/toolpriv.h b/private/mvdm/wow16/toolhelp/toolpriv.h
new file mode 100644
index 000000000..f14f613aa
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/toolpriv.h
@@ -0,0 +1,220 @@
+/**************************************************************************
+ * TOOLPRIV.H
+ *
+ * Private header file for routines in the tool helper library
+ *
+ **************************************************************************/
+
+#ifndef TOOLPRIV_H
+#define TOOLPRIV_H
+
+#define BUILDDLL
+
+#include <windows.h>
+#include "toolhelp.h"
+
+/* ----- Symbols ----- */
+#define VERSION 1
+#ifndef NOEXPORT
+#define NOEXPORT static
+#endif
+#define TOOLHELPAPI WINAPI
+
+#define FAULT_ACTIVE 2
+#define NUM_EXCEPTIONS 3
+
+/* wTHFlag values */
+#define TH_KERNEL_286 1
+#define TH_KERNEL_386 2
+#define TH_WIN30 4
+#define TH_WIN30STDMODE 8 /* THWIN30STDMODE set if TH_WIN30 & Std mode */
+#define TH_GOODPTRACEHOOK 16
+#define TH_GOTOLDPTRACE 32
+
+/* ----- Code macros ----- */
+#define MAKEFARPTR(s, o) ((void FAR *)(((WORD)(o)) | \
+ (((DWORD)(WORD)(s)) << 16)))
+
+/* ----- Types ----- */
+
+typedef struct tagNOTIFYSTRUCT
+{
+ struct tagNOTIFYSTRUCT *pNext;
+ HANDLE hTask;
+ LPFNNOTIFYCALLBACK lpfn;
+ WORD wFlags;
+} NOTIFYSTRUCT;
+
+typedef void (FAR PASCAL *LPFNCALLBACK)(void);
+
+typedef struct tagINTERRUPT
+{
+ struct tagINTERRUPT *pNext;
+ HANDLE hTask;
+ LPFNCALLBACK lpfn;
+} INTERRUPT;
+
+typedef struct tagSIGNAL
+{
+ struct tagSIGNAL *pNext;
+ HANDLE hTask;
+ LPFNCALLBACK lpfn;
+ LPFNCALLBACK lpfnOld;
+} SIGNAL;
+
+typedef LONG (FAR PASCAL *LPFNUSUD)(
+ WORD wID,
+ WORD wParam,
+ LONG lParam);
+
+/* ----- TOOLHELP global variables ----- */
+ extern WORD segKernel;
+ extern WORD wLibInstalled;
+ extern HANDLE hMaster;
+ extern WORD wTHFlags;
+ extern HANDLE hUserHeap;
+ extern HANDLE hGDIHeap;
+ extern WORD NEAR *npwExeHead;
+ extern WORD NEAR *npwTDBHead;
+ extern WORD NEAR *npwTDBCur;
+ extern DWORD NEAR *npdwSelTableStart;
+ extern WORD NEAR *npwSelTableLen;
+ extern WORD wNotifyInstalled;
+ extern NOTIFYSTRUCT NEAR *npNotifyHead;
+ extern WORD wIntInstalled;
+ extern INTERRUPT NEAR *npIntHead;
+ extern FARPROC lpfnGetUserLocalObjType;
+ extern FARPROC lpfnFatalExitHook;
+ extern FARPROC PASCAL lpfnPV;
+ extern FARPROC lpfnNotifyHook;
+ extern FARPROC lpfnGetFreeSystemResources;
+ extern FARPROC lpfntimeGetTime;
+ extern LPFNUSUD lpfnUserSeeUserDo;
+ extern WORD wSignalInstalled;
+ extern SIGNAL NEAR *npSignalHead;
+ extern NOTIFYSTRUCT NEAR* npNotifyNext;
+ extern WORD wLRUCount;
+
+/* ----- Private function prototypes ----- */
+
+ void PASCAL KernelType(void);
+
+ DWORD PASCAL Walk386First(
+ WORD wFlags);
+
+ WORD PASCAL Walk386Count(
+ WORD wFlags);
+
+ void PASCAL Walk386(
+ DWORD dwBlock,
+ GLOBALENTRY FAR *lpGlobal,
+ WORD wFlags);
+
+ DWORD PASCAL Walk386Handle(
+ HANDLE hBlock);
+
+ WORD PASCAL WalkLoc386Count(
+ HANDLE hHeap);
+
+ HANDLE PASCAL WalkLoc386First(
+ HANDLE hHeap);
+
+ void PASCAL WalkLoc386(
+ WORD wBlock,
+ LOCALENTRY FAR *lpLocal,
+ HANDLE hHeap);
+
+ DWORD PASCAL Walk286First(
+ WORD wFlags);
+
+ WORD PASCAL Walk286Count(
+ WORD wFlags);
+
+ void PASCAL Walk286(
+ DWORD dwBlock,
+ GLOBALENTRY FAR *lpGlobal,
+ WORD wFlags);
+
+ DWORD PASCAL Walk286Handle(
+ HANDLE hBlock);
+
+ WORD PASCAL WalkLoc286Count(
+ HANDLE hHeap);
+
+ HANDLE PASCAL WalkLoc286First(
+ HANDLE hHeap);
+
+ void PASCAL WalkLoc286(
+ WORD wBlock,
+ LOCALENTRY FAR *lpLocal,
+ HANDLE hHeap);
+
+ BOOL PASCAL TaskInfo(
+ TASKENTRY FAR *lpTask,
+ WORD wBlock);
+
+ WORD PASCAL StackFrameFirst(
+ STACKTRACEENTRY FAR *lpStack,
+ HANDLE hTDB);
+
+ BOOL PASCAL StackFrameNext(
+ STACKTRACEENTRY FAR *lpStack);
+
+ HANDLE PASCAL UserGdiDGROUP(
+ HANDLE hModule);
+
+ DWORD PASCAL UserGdiSpace(
+ HANDLE hData);
+
+ WORD PASCAL HelperVerifySeg(
+ WORD wSeg,
+ WORD wcb);
+
+ WORD PASCAL HelperHandleToSel(
+ HANDLE h);
+
+ void PASCAL HelperGlobalType(
+ GLOBALENTRY FAR *lpGlobal);
+
+ WORD PASCAL HelperGrabSelector(void);
+
+ void PASCAL HelperReleaseSelector(
+ WORD wSelector);
+
+ void PASCAL UserGdiType(
+ LOCALENTRY FAR *lpLocal);
+
+ BOOL PASCAL NotifyInit(void);
+
+ void PASCAL NotifyUnInit(void);
+
+ BOOL PASCAL NotifyIsHooked(
+ HANDLE hTask);
+
+ BOOL PASCAL InterruptInit(void);
+
+ void PASCAL InterruptUnInit(void);
+
+ BOOL PASCAL InterruptIsHooked(
+ HANDLE hTask);
+
+ BOOL PASCAL SignalRegister(
+ HANDLE hTask);
+
+ BOOL PASCAL SignalUnRegister(
+ HANDLE hTask);
+
+ void FAR PASCAL HelperSignalProc(void);
+
+ DWORD PASCAL HelperSetSignalProc(
+ HANDLE hTask,
+ DWORD lpfn);
+
+ BOOL PASCAL ClassInfo(
+ CLASSENTRY FAR *lpClass,
+ WORD wOffset);
+
+ DWORD PASCAL HelperSegLen(
+ WORD wSeg);
+
+#endif
diff --git a/private/mvdm/wow16/toolhelp/toolpriv.inc b/private/mvdm/wow16/toolhelp/toolpriv.inc
new file mode 100644
index 000000000..1001cfd99
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/toolpriv.inc
@@ -0,0 +1,112 @@
+;**************************************************************************
+;* TOOLPRIV.INC
+;*
+;* Private assembly-language include file for modules assembled as a
+;* part of TOOLHELP.DLL.
+;*
+;**************************************************************************
+
+;** CMACROS.INC variables for correct assembly
+DOS5 = 1
+PMODE = 1
+?WIN = 0
+?PLM = 1
+
+.286p
+
+ INCLUDE CMACROS.INC
+ INCLUDE TOOLHELP.INC
+
+externA __WinFlags
+
+;** TOOLHELP global variables
+?PLM = 0
+externW segKernel
+externW wLibInstalled
+externW hMaster
+externW wTHFlags
+externW npwExeHead
+externW npwTDBHead
+externW npwTDBCur
+externW hUserHeap
+externW hGDIHeap
+externW npdwSelTableStart
+externW npwSelTableLen
+externW wNotifyInstalled
+externW npNotifyHead
+externW wIntInstalled
+externW npIntHead
+externD lpfnGetUserLocalObjType
+externD lpfnFatalExitHook
+externD lpfnUserSeeUserDo
+externD lpfnNotifyHook
+externD lpfnGetFreeSystemResources
+externD lpfntimeGetTime;
+externW wSel
+externW wSignalInstalled
+externW npSignalHead
+externW npNotifyNext
+externW wLRUCount
+?PLM = 1
+
+;** Symbols
+TRUE = 1
+FALSE = 0
+
+KERNEL_286 EQU 1
+KERNEL_386 EQU 2
+
+FAULT_ACTIVE EQU 2
+
+NUM_EXCEP EQU 3
+
+;** wTHFlag values
+TH_KERNEL_286 EQU 1
+TH_KERNEL_386 EQU 2
+TH_WIN30 EQU 4
+TH_WIN30STDMODE EQU 8 ;THWIN30STDMODE set if TH_WIN30 & Std mode
+TH_GOODPTRACEHOOK EQU 16
+TH_GOTOLDPTRACE EQU 32
+
+;** Notification values
+NI_INCHAR EQU 01h
+NI_OUTSTR EQU 12h
+NI_LOADSEG EQU 50h
+NI_FREESEG EQU 52h
+NI_STARTTASK EQU 59h
+NI_EXITCALL EQU 62h
+NI_LOADDLL EQU 64h
+NI_DELMODULE EQU 65h
+NI_TASKOUT EQU 0dh
+NI_TASKIN EQU 0eh
+NI_CTLALTSYSRQ EQU 63h
+NI_LOGERROR EQU 66h
+NI_LOGPARAMERROR EQU 67h
+
+;** Structures
+
+NOTIFYSTRUCT STRUC
+ns_pNext DW ?
+ns_hTask DW ?
+ns_lpfn DD ?
+ns_wFlags DW ?
+NOTIFYSTRUCT ENDS
+
+INTERRUPT STRUC
+i_pNext DW ?
+i_hTask DW ?
+i_lpfn DD ?
+INTERRUPT ENDS
+
+SIGNAL STRUC
+si_pNext DW ?
+si_hTask DW ?
+si_lpfn DD ?
+si_lpfnOld DD ?
+SIGNAL ENDS
+
+;** Macros
+PubLabel MACRO PLabel
+ labelNP PLabel
+ PUBLIC PLabel
+ENDM
diff --git a/private/mvdm/wow16/toolhelp/usergdi1.c b/private/mvdm/wow16/toolhelp/usergdi1.c
new file mode 100644
index 000000000..05aac84ce
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/usergdi1.c
@@ -0,0 +1,77 @@
+/**************************************************************************
+ * USERGDI1.C
+ *
+ * Returns information about USER.EXE and GDI.EXE
+ *
+ **************************************************************************/
+
+#include "toolpriv.h"
+
+/* SystemHeapInfo
+ * Returns information about USER's and GDI's heaps
+ */
+
+BOOL TOOLHELPAPI SystemHeapInfo(
+ SYSHEAPINFO FAR* lpSysHeap)
+{
+ MODULEENTRY ModuleEntry;
+#ifndef WOW
+ DWORD dw;
+ WORD wFreeK;
+ WORD wMaxHeapK;
+#endif
+
+ /* Check the structure version number and pointer */
+ if (!wLibInstalled || !lpSysHeap ||
+ lpSysHeap->dwSize != sizeof (SYSHEAPINFO))
+ return FALSE;
+
+ /* Find the user data segment */
+ ModuleEntry.dwSize = sizeof (MODULEENTRY);
+ lpSysHeap->hUserSegment =
+ UserGdiDGROUP(ModuleFindName(&ModuleEntry, "USER"));
+ lpSysHeap->hGDISegment =
+ UserGdiDGROUP(ModuleFindName(&ModuleEntry, "GDI"));
+
+#ifndef WOW
+ /* We get the information about the heap percentages differently in
+ * 3.0 and 3.1
+ */
+ if ((wTHFlags & TH_WIN30) || !lpfnGetFreeSystemResources)
+ {
+ /* Get the space information about USER's heap */
+ dw = UserGdiSpace(lpSysHeap->hUserSegment);
+ wFreeK = LOWORD(dw) / 1024;
+ wMaxHeapK = HIWORD(dw) / 1024;
+ if (wMaxHeapK)
+ lpSysHeap->wUserFreePercent = wFreeK * 100 / wMaxHeapK;
+ else
+ lpSysHeap->wUserFreePercent = 0;
+
+ /* Get the space information about GDI's heap */
+ dw = UserGdiSpace(lpSysHeap->hGDISegment);
+ wFreeK = LOWORD(dw) / 1024;
+ wMaxHeapK = HIWORD(dw) / 1024;
+ if (wMaxHeapK)
+ lpSysHeap->wGDIFreePercent = wFreeK * 100 / wMaxHeapK;
+ else
+ lpSysHeap->wGDIFreePercent = 0;
+ }
+
+ /* Get the information from USER in 3.1 */
+ else
+ {
+ lpSysHeap->wUserFreePercent =
+ (*(WORD (FAR PASCAL *)(WORD))lpfnGetFreeSystemResources)(2);
+ lpSysHeap->wGDIFreePercent =
+ (*(WORD (FAR PASCAL *)(WORD))lpfnGetFreeSystemResources)(1);
+ }
+#else
+
+ lpSysHeap->wUserFreePercent = GetFreeSystemResources(GFSR_USERRESOURCES);
+ lpSysHeap->wGDIFreePercent = GetFreeSystemResources(GFSR_GDIRESOURCES);
+
+#endif
+
+ return TRUE;
+}
diff --git a/private/mvdm/wow16/toolhelp/usergdi2.asm b/private/mvdm/wow16/toolhelp/usergdi2.asm
new file mode 100644
index 000000000..35d72aba6
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/usergdi2.asm
@@ -0,0 +1,209 @@
+;***************************************************************************
+;* USERGDI2.ASM
+;*
+;* Assembly routines used in computing heap space remaining for
+;* USER, GDI, and any other heaps.
+;*
+;***************************************************************************
+
+ INCLUDE TOOLPRIV.INC
+SWAPPRO = 0
+PMODE32 = 0
+PMODE = 1
+ INCLUDE WINKERN.INC
+ INCLUDE NEWEXE.INC
+
+;** This slimy thing is from GDIOBJ.INC and is subtracted from the
+;** object type nunbers only in 3.1
+LT_GDI_BASE EQU ('G' or ('O' * 256)) - 1
+
+;** External functions
+
+externNP HelperVerifyLocHeap
+externNP HelperHandleToSel
+
+;** Functions
+
+sBegin CODE
+ assumes CS,CODE
+ assumes DS,DATA
+
+.286p
+
+; UserGdiDGROUP
+; Returns a handle to the DGROUP segment for a given module
+;
+; HANDLE UserGdiDGROUP(
+; HANDLE hModule)
+
+cProc UserGdiDGROUP, <PUBLIC,NEAR>, <di,si>
+ parmW hModule
+cBegin
+ mov ax,hModule ;Get the handle
+ cCall HelperHandleToSel, <ax> ;Convert to a selector
+ mov es,ax ;Point with ES for this
+ xor ax,ax ;Prepare to return NULL
+ cmp es:[ne_magic],NEMAGIC ;Make sure we have a module database
+ jnz UGD_End ;It isn't so get out
+ mov bx,es:[ne_pautodata] ;Point to the segment table entry
+ mov ax,es:[bx].ns_handle ;Get the handle from the table
+ cCall HelperHandleToSel, <ax> ;Convert to a selector for return
+UGD_End:
+cEnd
+
+
+; UserGdiSpace
+; This function was stolen from KERNEL where it is used to compute
+; the space remaining in the USER and GDI heaps. It actually works
+; on any local heap.
+;
+; DWORD UserGdiSpace(
+; HANDLE hData)
+; HIWORD of return is maximum size of heap (64K less statics, etc.)
+; LOWORD of return is space remaining on heap
+
+cProc UserGdiSpace, <PUBLIC,NEAR>, <di,si,ds>
+ parmW hData
+cBegin
+ ;** Count the free space in this heap. First: Is this heap valid?
+ mov ax,hData ;Get the heap selector
+ cCall HelperVerifyLocHeap ;Call the verify routine
+ mov ax,0 ;In case we jump -- set error
+ mov dx,0 ; Use MOV to not mess up carry
+ jc UGS_Exit ;No valid local heap!!
+
+ ;** Loop through all local blocks, adding free space
+ cCall HelperHandleToSel, <hData> ;Convert to selector
+ mov ds,ax ;Point to the segment
+ mov di,ds:[6] ;Get pHeapInfo
+ mov di,[di].hi_first ;First arena header
+ mov di,[di].la_free_next ;First free block
+UGS_Loop:
+ add ax,[di].la_size ;Add in size of this block
+ sub ax,SIZE LocalArenaFree ;Less block overhead
+ mov si,[di].la_free_next ;Get next free block
+ or si,si ;NULL?
+ jz UGS_Break ;Yes, say we're done
+ cmp si,di ;Last block? (points to self)
+ mov di,si ;Save for next time around
+ jnz UGS_Loop ;Not last block so loop some more
+UGS_Break:
+
+ ;** We have the size of the local heap
+ mov si,ax ;Save the size
+ mov cx,ds ;Get the selector in a non-segreg
+ lsl ax,cx ;Get the size of the segment
+ neg ax ;64K - segment size
+ add ax,si ;Add in the free holes in the heap
+ mov dx,-1 ;Compute the max size of heap
+ sub dx,ds:[6] ; which is 64K less statics
+
+UGS_Exit:
+
+cEnd
+
+
+; UserGdiType
+;
+; Tries to compute the type of local heap block if possible
+; Prototype:
+;
+; void PASCAL UserGdiType(
+; LOCALENTRY FAR *lpLocal)
+
+cProc UserGdiType, <PUBLIC,NEAR>, <si,di>
+ parmD lpLocal
+cBegin
+ ;** Get info from our static variables
+ mov ax,_DATA ;Get the variables first
+ mov ds,ax ;Point to our DS
+ mov bx,hUserHeap ;BX=User's heap block
+ mov cx,hGDIHeap ;CX=GDI's heap block
+
+ ;** See if we can do anything with this heap
+ les si,lpLocal ;Get a pointer to the structure
+ mov es:[si].le_wType,LT_NORMAL ;In case we don't find anything
+ mov ax,es:[si].le_hHeap ;Get the heap pointer
+ cmp ax,bx ;User's heap?
+ jnz UGT_10 ;Nope, try next
+ cCall GetUserType ;Call routine to get user type
+ jmp SHORT UGT_End ;Get out
+
+UGT_10: cmp ax,cx ;GDI's heap?
+ jnz UGT_End ;Nope, can't do anything with it
+ cCall GetGDIType ;Call routine to get GDI type
+
+UGT_End:
+
+cEnd
+
+
+;** Internal helper functions
+
+; GetUserType
+;
+; Uses the tags in debug USER.EXE to give information on what type
+; block is pointed to by the current LOCALENTRY structure.
+; Caller: ES:SI points to the parameter LOCALENTRY structure
+; Return: LOCALENTRY structure is correctly updated
+
+cProc GetUserType, <NEAR>
+cBegin
+ ;** Make sure we have a function to call
+ cmp WORD PTR lpfnGetUserLocalObjType + 2,0 ;Selector zero?
+ je GUT_End ;Yes
+
+ ;** Call USER to get the type
+ push es ;Save ES
+ mov bx,es:[si].le_wAddress ;Get the block address
+ sub bx, la_fixedsize ;The USER call needs the arena header
+ test es:[si].le_wFlags, LF_MOVEABLE ;Moveable block?
+ jz @F ;No
+ sub bx, (SIZE LocalArena) - la_fixedsize ;Moveable arena bigger
+@@: push bx ;Parameter arena handle
+ call DWORD PTR lpfnGetUserLocalObjType ;Call the function
+ pop es
+ xor ah,ah ;Clear the upper byte
+ mov es:[si].le_wType,ax ;Save the type
+GUT_End:
+cEnd
+
+
+; GetGDIType
+;
+; Uses the tags in debug GDI.EXE to give information on what type
+; block is pointed to by the current LOCALENTRY structure.
+; Caller: ES:SI points to the parameter LOCALENTRY structure
+; Return: LOCALENTRY structure is correctly updated
+
+cProc GetGDIType, <NEAR>, <ds>
+cBegin
+ ;** All fixed blocks are unknown to us
+ test es:[si].le_wFlags,LF_FIXED ;Is it fixed?
+ jz GGT_10 ;Nope
+ jmp SHORT GGT_End ;Yes, get out
+GGT_10:
+
+ ;** Prepare to find the type
+ cCall HelperHandleToSel,es:[si].le_hHeap ;Get the selector value
+ mov cx,wTHFlags ;Save for when we trash DS
+ mov ds,ax ;Get the heap pointer
+ mov di,es:[si].le_wAddress ;Get the block pointer
+
+ ;** Get the type word
+ mov ax,[di+2] ;Get the type word from the heap
+ and ax,05fffh ;Mask out the stock object flag
+ test cx,TH_WIN30 ;In 3.0?
+ jnz CGT_Win30 ;Yes
+ sub ax,LT_GDI_BASE ;No, subtract type tag base
+CGT_Win30:
+ cmp ax,LT_GDI_MAX ;Recognizable type code?
+ ja GGT_End ;No, get out
+ mov es:[si].le_wType,ax ;Save in the structure
+
+GGT_End:
+cEnd
+
+sEnd
+ END
+
diff --git a/private/mvdm/wow16/toolhelp/walk286.asm b/private/mvdm/wow16/toolhelp/walk286.asm
new file mode 100644
index 000000000..ef358449b
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/walk286.asm
@@ -0,0 +1,562 @@
+;**************************************************************************
+;* walk286.ASM
+;*
+;* Assembly support code for the KRNL286 global heap routines
+;* for TOOLHELP.DLL
+;*
+;**************************************************************************
+
+ INCLUDE TOOLPRIV.INC
+
+PMODE32 = 0
+PMODE = 1
+SWAPPRO = 0
+ INCLUDE WINKERN.INC
+
+;** External functions
+externNP HelperVerifySeg
+externNP HelperHandleToSel
+externNP HelperPDBtoTDB
+externNP HelperSegLen
+
+;** Functions
+
+sBegin CODE
+ assumes CS,CODE
+
+; Walk286Count
+;
+; Returns the number of blocks in the given list
+
+cProc Walk286Count, <PUBLIC,NEAR>, <di>
+ parmW wFlags
+cBegin
+ mov es,hMaster ;Segment of master block
+ mov ax,wFlags ;Get the flag value
+ shr ax,1 ;Check for GLOBAL_LRU
+ jc W2C_LRU ;Bit set, must be LRU
+ shr ax,1 ;Check for GLOBAL_FREE
+ jc W2C_Free ;Must be free
+ ;Must be GLOBAL_ALL
+
+ ;** Get total object count
+ mov ax,es:[hi_count] ;Get heap count
+ inc ax ;Bump to include first sentinel
+ jmp SHORT W2C_End ;Get out
+
+ ;** Get LRU object count
+W2C_LRU:
+ mov ax,es:[gi_lrucount] ;Get the LRU count
+ jmp SHORT W2C_End ;Get out
+
+ ;** Get Free list object count
+W2C_Free:
+ mov ax,es:[gi_free_count] ;Get free count
+ jmp SHORT W2C_End ;Get out
+
+ ;** Return the result in AX
+W2C_End:
+
+cEnd
+
+; Walk286First
+;
+; Returns a handle to the first block in the 286 global heap.
+
+cProc Walk286First, <PUBLIC,NEAR>, <di>
+ parmW wFlags
+cBegin
+ mov es,hMaster ;Segment of master block
+ mov ax,wFlags ;Get the flag value
+ shr ax,1 ;Check for GLOBAL_LRU
+ jc W2F_LRU ;Bit set, must be LRU
+ shr ax,1 ;Check for GLOBAL_FREE
+ jc W2F_Free ;Must be free
+ ;Must be GLOBAL_ALL
+
+ ;** Get first object in complete heap (wFlags == GLOBAL_ALL)
+ mov ax,es:[hi_first] ;Get handle of first arena header
+ jmp SHORT W2F_End ;Get out
+
+ ;** Get first object in LRU list
+W2F_LRU:
+ mov ax,es:[gi_lrucount] ;Get the number of objects
+ or ax,ax ;Are there any objects?
+ je W2F_End ;No, return NULL
+ inc es:[gi_lrulock] ;No LRU sweeping for awhile
+ inc wLRUCount ;Keep a count of this
+ mov ax,es:[gi_lruchain] ;Get a pointer to the first item
+ jmp SHORT W2F_End ;Done
+
+ ;** Get first object in Free list
+W2F_Free:
+ mov ax,es:[gi_free_count] ;Get the number of objects
+ or ax,ax ;Are there any objects?
+ jz W2F_End ;No, return NULL
+ mov es,es:[hi_first] ;Get the first object
+ mov ax,es:[ga_freenext] ;Skip to the first free block
+ ;Fall through to the return
+
+ ;** Return the result in AX (return DX = NULL)
+W2F_End:
+ xor dx,dx ;Clear high word
+cEnd
+
+
+; Walk286
+;
+; Takes a pointer to a block and returns a GLOBALENTRY structure
+; full of information about the block. If the block pointer is
+; NULL, looks at the first block. The last field in the GLOBALENTRY
+; structure is used to chain to the next block and is thus used to walk
+; the heap.
+
+cProc Walk286, <PUBLIC,NEAR>, <di,si,ds>
+ parmD dwBlock
+ parmD lpGlobal
+ parmW wFlags
+cBegin
+ ;** Set up to build public structure
+ mov es,WORD PTR dwBlock ;Point to this block
+ lds si,lpGlobal ;Point to the GLOBALENTRY structure
+
+ ;** Fill public structure
+ mov ax,es:[ga_handle] ;Get the handle of the block
+ mov [si].ge_hBlock,ax ;Put in public structure
+ mov ax,es:[ga_size] ;Get the size of the block (LOWORD)
+ mov dx,ax ;Clear high word
+ shl ax,4 ;Left shift DX:AX by 4
+ shr dx,16-4
+ mov WORD PTR [si].ge_dwBlockSize,ax ;Put in public structure
+ mov WORD PTR [si].ge_dwBlockSize + 2,dx ;Put in public structure
+ mov ax,es:[ga_owner] ;Owning task of block
+ mov [si].ge_hOwner,ax ;Put in public structure
+ xor ah,ah ;No upper BYTE
+ mov al,es:[ga_count] ;Lock count (moveable segments)
+ mov [si].ge_wcLock,ax ;Put in public structure
+ mov WORD PTR [si].ge_wcPageLock,0 ;Zero the page lock count
+ mov al,es:[ga_flags] ;BYTE of flags
+ xor ah,ah ;No upper BYTE
+ mov [si].ge_wFlags,ax ;Put in public structure
+ mov ax,es:[ga_next] ;Put next pointer in structure
+ mov WORD PTR [si].ge_dwNext,ax
+ mov WORD PTR [si].ge_dwNext + 2,0
+
+ ;** Use DPMI to compute linear address of selector
+ mov ax,6 ;Get Segment Base Address
+ mov bx,es ;Get the segment value
+ int 31h ;Call DPMI
+ mov WORD PTR [si].ge_dwAddress,dx ;Save linear address
+ mov WORD PTR [si].ge_dwAddress + 2,cx
+
+ ;** If this is a data segment, get local heap information
+ mov ax,[si].ge_hBlock ;Get the handle
+ cCall Walk286VerifyLocHeap
+ mov [si].ge_wHeapPresent,TRUE ;Flag that there's a heap
+ jnc W2_10 ;There really is no heap
+ mov [si].ge_wHeapPresent,FALSE ;Flag that there's no heap
+W2_10:
+
+ ;** If the owner is a PDB, translate this to the TDB
+ mov bx,[si].ge_hOwner ;Get the owner
+ cCall HelperHandleToSel, <bx> ;Translate to selector
+ mov bx,ax ;Get the selector in BX
+ cCall HelperVerifySeg, <ax,2> ;Must be two bytes long
+ or ax,ax ;Is it?
+ jz W2_15 ;No.
+ push es ;Save ES for later
+ mov es,bx ;Point to possible PDB block
+ cmp es:[0],20CDh ;Int 20h is first word of PDB
+ jnz W2_12 ;Nope, don't mess with this
+ mov ax,bx ;Pass parameter in AX
+ cCall HelperPDBtoTDB ;Get the corresponding TDB
+ or ax,ax ;Was one found?
+ jz W2_11 ;No.
+ mov [si].ge_hOwner,ax ;Make the owner the TDB instead
+W2_11: or [si].ge_wFlags,GF_PDB_OWNER ;Flag that a PDB owned block
+W2_12: pop es ;Restore ES
+W2_15:
+
+ ;** Check for this being the last item in both lists
+ mov ax,es ;Get the current pointer
+ cmp ax,es:[ga_next] ;See if we're at the end
+ jne W2_20 ;No
+ mov WORD PTR [si].ge_dwNext,0 ;NULL the next pointer
+ mov WORD PTR [si].ge_dwNext + 2,0
+W2_20: mov ax,es ;Get current pointer
+ mov cx,wFlags ;Get the flags back
+ cCall NextLRU286 ;Get next LRU list pointer or 0
+ mov WORD PTR [si].ge_dwNextAlt,ax
+ mov WORD PTR [si].ge_dwNextAlt + 2,0
+
+W2_End:
+cEnd
+
+
+; Walk286Handle
+;
+; Finds an arena pointer given a block handle
+
+cProc Walk286Handle, <PUBLIC,NEAR>, <di,si,ds>
+ parmW hBlock
+cBegin
+ mov ax,hBlock ;Get the block handle
+ cCall HelperHandleToSel, <ax> ;Convert to selector
+ cCall SelToArena286 ;Get the arena pointer
+ jnc W2H_10 ;Must be OK
+ xor ax,ax ;Return a 0L
+ xor dx,dx
+ jmp SHORT W2H_End ;Error in conversion, get out
+W2H_10: mov ax,bx ;Get the low word
+ xor dx,dx ;No high word
+W2H_End:
+cEnd
+
+
+; WalkLoc286Count
+;
+; Returns the number of blocks in the given local heap
+
+cProc WalkLoc286Count, <PUBLIC,NEAR>, <di>
+ parmW hHeap
+cBegin
+ ;** Verify that it has a local heap
+ mov ax, hHeap ;Get the block
+ cCall Walk286VerifyLocHeap ;Verify it
+ jnc LC_10 ;It's OK
+ xor ax,ax ;No heap
+ jmp SHORT LC_End ;Get out
+LC_10:
+
+ ;** Point to the block
+ mov ax,hHeap ;Get the heap block
+ cCall HelperHandleToSel, <ax> ;Convert it to a selector
+ mov es,ax ;Use ES to point to it
+ mov bx,es:[6] ;Get a pointer to the HeapInfo struct
+
+ ;** Get the number of blocks
+ mov ax,es:[bx].hi_count ;Get the count
+LC_End:
+cEnd
+
+
+; WalkLoc286First
+;
+; Returns a handle to the first block in the 286 global heap.
+
+cProc WalkLoc286First, <PUBLIC,NEAR>, <di>
+ parmW hHeap
+cBegin
+ ;** Verify that the given global block has a local heap
+ mov ax, hHeap ;Get the block
+ cCall Walk286VerifyLocHeap ;Verify it
+ jnc LF_10 ;It's OK
+ xor ax,ax ;No heap
+ jmp SHORT LF_End ;Get out
+LF_10:
+
+ ;** Point to the global block
+ mov ax,hHeap ;Get the heap block
+ cCall HelperHandleToSel, <ax> ;Convert it to a selector
+ mov es,ax ;Use ES to point to it
+ mov bx,es:[6] ;Get a pointer to the HeapInfo struct
+
+ ;** Get the first block and return it
+ mov ax,WORD PTR es:[bx].hi_first ;Get the first block
+LF_End:
+cEnd
+
+
+; WalkLoc286
+;
+; Takes a pointer to a block and returns a GLOBALENTRY structure
+; full of information about the block. If the block pointer is
+; NULL, looks at the first block. The last field in the GLOBALENTRY
+; structure is used to chain to the next block and is thus used to walk
+; the heap.
+
+cProc WalkLoc286, <PUBLIC,NEAR>, <di,si,ds>
+ parmW wBlock
+ parmD lpLocal
+ parmW hHeap
+cBegin
+ ;** Verify that it has a local heap
+ mov ax, hHeap ;Get the block
+ cCall Walk286VerifyLocHeap ;Verify it
+ jnc LW_10 ;It's OK
+ jmp LW_End ;Get out
+LW_10:
+
+ ;** Get variables from the TOOLHELP DGROUP
+ mov ax,_DATA ;Get the variables first
+ mov ds,ax ;Point to our DS
+ mov bx,hUserHeap ;BX=User's heap block
+ mov cx,hGDIHeap ;CX=GDI's heap block
+
+ ;** Point to the heap
+ mov ax,hHeap ;Get the heap block
+ cCall HelperHandleToSel, <ax> ;Convert it to a selector
+ mov es,ax ;Use ES to point to it
+ lds di,lpLocal ;Point to the LOCALENTRY structure
+ mov [di].le_wHeapType,NORMAL_HEAP ;In case we don't match below...
+ cmp bx,hHeap ;User's heap?
+ jnz LW_3 ;No
+ mov [di].le_wHeapType,USER_HEAP ;Yes
+ jmp SHORT LW_5 ;Skip on
+LW_3: cmp cx,hHeap ;GDI's heap?
+ jnz LW_5 ;No
+ mov [di].le_wHeapType,GDI_HEAP ;Yes
+LW_5:
+ mov si,wBlock ;Get the address of the block
+
+ ;** Get information about the given block
+ mov bx,es:[si].la_handle ;Get the handle
+ mov [di].le_hHandle,bx ;Save in the public structure
+ mov ax,si ;Get block address
+ add ax,la_fixedsize ;Skip fixed size local arena
+ mov [di].le_wAddress,ax ;Save the block address
+ mov ax,es:[si].la_next ;Get the address of the next block
+ mov [di].le_wNext,ax ;Save the next pointer
+ sub ax,si ;Compute the size
+ sub ax,SIZE LocalArena ;Don't count arena size
+ mov [di].le_wSize,ax ;Save in public structure
+ mov ax,es:[si].la_prev ;Get the flags
+ and ax,3 ;Mask out all but flags
+ mov [di].le_wFlags,ax ;Save in structure
+
+ ;** Moveable arenas are bigger and have a lock count to get
+ test al,LA_MOVEABLE ;Block has a handle iff it's moveable
+ jz SHORT LW_NoHandle ;No handle info
+ sub [di].le_wSize, (SIZE LocalArena) - la_fixedsize
+ add [di].le_wAddress, (SIZE LocalArena) - la_fixedsize
+ xor ah,ah ;Clear upper word
+ mov al,es:[bx].lhe_count ;Get lock count
+ mov [di].le_wcLock,ax ;Save it
+ jmp SHORT LW_20 ;Skip no handle info
+LW_NoHandle:
+ mov ax, [di].le_wAddress ;Handle of fixed block is real offset
+ mov [di].le_hHandle, ax
+ mov [di].le_wcLock,0
+LW_20:
+ ;** See if it's the end
+ cmp [di].le_wNext,si ;Loop pointer?
+ jnz LW_End ;Nope
+ mov [di].le_wNext,0 ;Set a zero pointer
+LW_End:
+cEnd
+
+
+; Walk286VerifyLocHeap
+;
+; Verifies that the given global block points to a data segment
+; with a local heap.
+;
+; Call:
+; AX = Block handle or selector
+; Return:
+; Carry flag set iff NOT a local heap segment
+;
+; Destroys all registers except AX, ESI, EDI, DS, and ES
+
+cProc Walk286VerifyLocHeap, <PUBLIC,NEAR>, <es,si,di>
+cBegin
+ ;** Convert to a handle
+ cCall HelperHandleToSel, <ax>
+
+ ;** Verify the selector
+ push ax ;Save the parameter
+ mov bx,SIZE LocalInfo ;Get the size
+ cCall HelperVerifySeg, <ax,bx> ;Check the selector
+ or ax,ax ;Is it valid?
+ pop ax ;Restore parameter
+ jnz VLH_SelOK ;Yes.
+ stc ;Set error flag
+ jmp SHORT VLH_End ;Get out
+VLH_SelOK:
+
+ ;** Check data segment to see if it has a local heap
+ mov es,ax ;Point to the local block with ES
+ cCall HelperSegLen, <ax> ;Get the length of the heap
+ or ax,ax ;Check for error
+ jz VLH_NoHeap ;Get out on error
+ mov cx,ax ;Use CX for comparisons
+ cmp cx,8 ;At least 8 bytes long?
+ ja VLH_10 ;Yes
+ stc ;No -- set error flag and get out
+ jmp SHORT VLH_End
+VLH_10: mov bx,es:[6] ;Get offset to HeapInfo struct
+ or bx,bx ;If NULL, no local heap
+ jz VLH_NoHeap ;Get out
+ sub cx,bx ;See if HeapInfo is beyond segment
+ jbe VLH_NoHeap
+ sub cx,li_sig + 2 ;Compare against last structure mem
+ jbe VLH_NoHeap
+ cmp es:[bx].li_sig,LOCALHEAP_SIG ;Make sure the signature matches
+ jne VLH_NoHeap ;Doesn't match
+ clc ;Must be a heap!
+ jmp SHORT VLH_End
+
+VLH_NoHeap:
+ stc ;Set error flag
+
+VLH_End:
+cEnd
+
+
+;** Private helper functions
+
+; SelToArena286
+;
+; Finds the arena entry for the given selector or handle.
+; The arena entry is stored 16 bytes before the block in linear
+; address space.
+;
+; Caller: AX = Selector
+; Returns: BX = Arena entry
+; Trashes everything except segment registers and AX
+; Carry set on error
+
+cProc SelToArena286, <NEAR>, <es,ds,ax>
+cBegin
+ ;** Convert to a handle
+ cCall HelperHandleToSel, <ax>
+
+ ;** Verify selector
+ push ax ;Save the parameter
+ cCall HelperVerifySeg, <ax,1> ;Check the selector
+ or ax,ax ;Is it valid?
+ pop ax ;Restore parameter
+ jnz STA_SelOK ;Must be
+ stc ;Nope. Set error flag and exit
+ jmp SHORT STA_End
+STA_SelOK:
+ ;** If this is Win30StdMode, we're in the old KRNL286 which used
+ ;* an arcane method of finding the arena. If that's the case
+ ;** here, handle it seperately.
+ mov bx,_DATA ;Get the static data segment
+ mov ds,bx
+ test wTHFlags,TH_WIN30STDMODE ;3.0 Std Mode?
+ jnz STA_OldKRNL286 ;Yes
+ mov bx,segKernel ;Get the KERNEL variable segment
+ mov es,bx
+ mov bx,npwSelTableLen ;Get the pointer
+ mov dx,es:[bx] ;Get the selector table length
+ mov bx,npdwSelTableStart ;Get the start of the sel table
+ mov bx,es:[bx] ;Get the linear offset (32 bits)
+ mov es,hMaster ;Point to the arena table
+ and al,not 7 ;Clear the RPL bits for table lookup
+ shr ax,2 ;Convert to WORD offset
+ cmp ax,dx ;Check to see if off the end of table
+ jb STA_InTable ;It's in the table
+ stc ;Set error flag--not in table
+ jmp SHORT STA_End ;Get out
+STA_InTable:
+ add bx,ax ;Add the selector offset
+ mov bx,es:[bx] ;Do the sel table indirection
+ clc ;BX now points to arena segment
+ jmp SHORT STA_End ;Skip the old stuff
+
+STA_OldKRNL286:
+ mov bx,ax ;Selector in BX
+ mov ax,6 ;DPMI function 6, Get Seg Base Addr
+ int 31h ;Call DPMI
+ sub dx,10h ;Move back 16 bytes
+ sbb cx,0 ; (this is a linear address)
+ mov ax,7 ;DPMI function 7, Set Seg Base Addr
+ mov bx,wSel ;Use our roving selector
+ int 31h ;Call DPMI
+ mov ax,8 ;DPMI function 8, Set Seg Limit
+ xor cx,cx ;No upper byte
+ mov dx,16 ;Just 16 bytes
+ int 31h ;Call DPMI
+ mov ax,9 ;DPMI function 9, Set Access Rights
+ mov cl,0b2h ;Desired rights byte
+ int 31h ;Call DPMI
+ ;Return arena segment pointer in BX
+STA_End:
+cEnd
+
+
+; NextLRU286
+;
+; Checks the given arena table pointer to see if it is the last
+; arena entry on the list.
+; Uses a grungy method that is necessitated because of the
+; unterminated linked lists we're dealing with here. The count
+; stored is the only reliable method for finding the end. So, to
+; provide random access to blocks in the heap, the heap is searched
+; from top to bottom to find the block and see if it is the last
+; one. If it is or if it is not on the given list, returns a 0
+; pointer to the next item.
+;
+; If this search is for the entire global heap, we do not get the
+; LRU link, but return NULL in AX. This speeds this up alot!
+;
+; Caller: AX = Arena table pointer
+; CX = GLOBAL_ALL, GLOBAL_FREE, or GLOBAL_LRU
+; Return: AX = Next arena table pointer or 0 if no more
+; Trashes all registers except segment registers and SI,DI
+
+cProc NextLRU286, <NEAR,PUBLIC>, <es,ds,si,di>
+cBegin
+ ;** Decode the flags
+ mov bx,_DATA ;Get the static data segment
+ mov ds,bx ;Point with DS
+ mov es,hMaster ;Segment of master block
+ cmp cx,GLOBAL_ALL ;Don't do this on full heap search
+ jne @F
+ jmp SHORT NL_BadList ;Just return NULL for this one
+@@: cmp cx,GLOBAL_FREE ;Check free list?
+ je NL_Free ;Yes
+
+ ;** Loop through LRU list until we find this item
+NL_LRU:
+ mov si,ax ;Save the selector in AX
+ mov cx,es:[gi_lrucount] ;Get the number of objects
+ jcxz NL_Bad ;No object so return end
+ dec cx ;We don't want to find the last one
+ jcxz NL_Bad ;1 object so return end
+ mov ds,es:[gi_lruchain] ;Get a pointer to the first item
+NL_LRULoop:
+ mov ax,ds ;Get in AX so we can compare
+ cmp si,ax ;Match yet?
+ je NL_ReturnNext ;Found it, return next item
+ mov ds,ds:[ga_lrunext] ;Not found yet, get the next one
+ loop NL_LRULoop ;Loop through all items
+
+ ;** Unlock the LRU sweeping
+NL_Bad: dec es:[gi_lrulock] ;OK to LRU sweep now
+ mov ax, _DATA ;Point to TH Data seg
+ mov ds, ax
+ dec wLRUCount ;Keep a count of this
+ jmp SHORT NL_BadList ;Not here either. Get out
+
+ ;** Loop through free list until we find this item
+NL_Free:
+ mov si,ax ;Save the selector in SI
+ mov cx,es:[gi_free_count] ;Get the number of objects
+ jcxz NL_BadList ;0 objects so return end
+ dec cx ;We don't want to find the last one
+ jcxz NL_BadList ;1 object so return end
+ mov ds,es:[hi_first] ;Get a pointer to the first item
+NL_FreeLoop:
+ mov ds,ds:[ga_lrunext] ;Not found yet, get the next one
+ mov ax,ds ;Get the pointer so we can compare it
+ cmp ax,si ;Is this the one?
+ je NL_ReturnNext ;Yes, return it
+ loop NL_FreeLoop ;Loop through all items
+ jmp SHORT NL_BadList ;Not here either. Get out
+
+NL_ReturnNext:
+ mov ax,ds:[ga_lrunext] ;Return the next one
+ jmp SHORT NL_End ;Get out
+
+NL_BadList:
+ xor ax,ax ;Return zero for error
+
+NL_End:
+cEnd
+
+sEnd
+ END
+
diff --git a/private/mvdm/wow16/toolhelp/walk386.asm b/private/mvdm/wow16/toolhelp/walk386.asm
new file mode 100644
index 000000000..2be63562b
--- /dev/null
+++ b/private/mvdm/wow16/toolhelp/walk386.asm
@@ -0,0 +1,541 @@
+;**************************************************************************
+;* walk386.ASM
+;*
+;* Assembly support code for the KERNEL386 global heap routines
+;* for TOOLHELP.DLL
+;*
+;**************************************************************************
+
+ INCLUDE TOOLPRIV.INC
+
+PMODE32 = 1
+PMODE = 0
+SWAPPRO = 0
+ INCLUDE WINKERN.INC
+
+;** External functions
+externNP HelperVerifySeg
+externNP HelperHandleToSel
+externNP HelperPDBtoTDB
+externNP HelperSegLen
+
+;** Functions
+
+sBegin CODE
+ assumes CS,CODE
+.386p
+
+; Walk386Count
+;
+; Returns the number of blocks in the given list
+
+cProc Walk386Count, <PUBLIC,NEAR>, <di>
+ parmW wFlags
+cBegin
+ mov es,hMaster ;Segment of master block
+ mov ax,wFlags ;Get the flag value
+ shr ax,1 ;Check for GLOBAL_LRU
+ jc SHORT W3C_LRU ;Bit set, must be LRU
+ shr ax,1 ;Check for GLOBAL_FREE
+ jc SHORT W3C_Free ;Must be free
+ ;Must be GLOBAL_ALL
+
+ ;** Get total object count
+ mov ax,es:[hi_count] ;Get heap count
+ inc ax ;Bump to include first sentinel
+ jmp SHORT W3C_End ;Get out
+
+ ;** Get LRU object count
+W3C_LRU:
+ mov ax,es:[gi_lrucount] ;Get the LRU count
+ jmp SHORT W3C_End ;Get out
+
+ ;** Get Free list object count
+W3C_Free:
+ mov ax,es:[gi_free_count] ;Get free count
+ jmp SHORT W3C_End ;Get out
+
+ ;** Return the result in AX
+W3C_End:
+
+cEnd
+
+; Walk386First
+;
+; Returns a handle to the first block in the 386 global heap.
+
+cProc Walk386First, <PUBLIC,NEAR>, <di>
+ parmW wFlags
+cBegin
+ mov es,hMaster ;Segment of master block
+ mov ax,wFlags ;Get the flag value
+ shr ax,1 ;Check for GLOBAL_LRU
+ jc SHORT W3F_LRU ;Bit set, must be LRU
+ shr ax,1 ;Check for GLOBAL_FREE
+ jc SHORT W3F_Free ;Must be free
+ ;Must be GLOBAL_ALL
+
+ ;** Get first object in complete heap (wFlags == GLOBAL_ALL)
+ mov edi,es:[phi_first] ;Get offset of first arena header
+ jmp SHORT W3F_End ;Get out
+
+ ;** Get first object in LRU list
+W3F_LRU:
+ xor edi,edi ;Zero upper word
+ mov di,es:[gi_lrucount] ;Get the number of objects
+ or di,di ;Are there any objects?
+ jz SHORT W3F_End ;No, return NULL
+ mov edi,es:[gi_lruchain] ;Get a pointer to the first item
+ jmp SHORT W3F_End ;Done
+
+ ;** Get first object in Free list
+W3F_Free:
+ xor edi,edi ;Zero upper word
+ mov di,es:[gi_free_count] ;Get the number of objects
+ or di,di ;Are there any objects?
+ jz SHORT W3F_End ;No, return NULL
+ mov edi,es:[phi_first] ;Get the first object
+ mov edi,es:[edi].pga_freenext ;Skip to the first free block
+ ;Fall through to the return
+
+ ;** Save the result in DX:AX for the return
+W3F_End:
+ mov ax,di ;Get the low word
+ shr edi,16 ;Get the high word out
+ mov dx,di ;Get the high word
+cEnd
+
+
+; Walk386
+;
+; Takes a pointer to a block and returns a GLOBALENTRY structure
+; full of information about the block. If the block pointer is
+; NULL, looks at the first block. The last field in the GLOBALENTRY
+; structure is used to chain to the next block and is thus used to walk
+; the heap.
+
+cProc Walk386, <PUBLIC,NEAR>, <di,si,ds>
+ parmD dwBlock
+ parmD lpGlobal
+ parmW wFlags
+cBegin
+ ;** Set up to build public structure
+ mov es,hMaster ;Get the correct segment
+ mov edi,dwBlock ;Point to this block
+ lds si,lpGlobal ;Point to the GLOBALENTRY structure
+
+ ;** Fill public structure
+ mov ax,es:[edi].pga_handle ;Get the handle of the block
+ mov [si].ge_hBlock,ax ;Put in public structure
+ mov eax,es:[edi].pga_address ;Get linear address of block
+ mov [si].ge_dwAddress,eax ;Put in public structure
+ mov eax,es:[edi].pga_size ;Get the size of the block
+ mov [si].ge_dwBlockSize,eax ;Put in public structure
+ mov ax,es:[edi].pga_owner ;Owning task of block
+ mov [si].ge_hOwner,ax ;Put in public structure
+ xor ah,ah
+ mov al,es:[edi].pga_count ;Lock count (moveable segments)
+ mov [si].ge_wcLock,ax ;Put in public structure
+ mov al,es:[edi].pga_pglock ;Page lock count
+ mov [si].ge_wcPageLock,ax ;Save in structure
+ mov al,es:[edi].pga_flags ;Word of flags
+ xor ah,ah
+ mov [si].ge_wFlags,ax ;Put in public structure
+ mov eax,es:[edi].pga_next ;Put next pointer in structure
+ mov [si].ge_dwNext,eax
+
+ ;** If this is a data segment, get local heap information
+ mov ax,[si].ge_hBlock ;Get the handle
+ cCall Walk386VerifyLocHeap
+ mov [si].ge_wHeapPresent,TRUE ;Flag that there's a heap
+ jnc SHORT W3_10 ;There really is no heap
+ mov [si].ge_wHeapPresent,FALSE ;Flag that there's no heap
+W3_10:
+
+ ;** If the owner is a PDB, translate this to the TDB
+ mov bx,[si].ge_hOwner ;Get the owner
+ cCall HelperHandleToSel, <bx> ;Translate to selector
+ mov bx,ax ;Get the selector in BX
+ cCall HelperVerifySeg, <ax,2> ;Must be two bytes long
+ or ax,ax ;Is it?
+ jz SHORT W3_15 ;No.
+ push es ;Save ES for later
+ mov es,bx ;Point to possible PDB block
+ cmp es:[0],20CDh ;Int 20h is first word of PDB
+ jnz SHORT W3_12 ;Nope, don't mess with this
+ mov ax,bx ;Pass parameter in AX
+ cCall HelperPDBtoTDB ;Get the corresponding TDB
+ or ax,ax ;Was one found?
+ jz SHORT W3_11 ;No.
+ mov [si].ge_hOwner,ax ;Make the owner the TDB instead
+W3_11: or [si].ge_wFlags,GF_PDB_OWNER ;Flag that a PDB owned block
+W3_12: pop es ;Restore ES
+W3_15:
+
+ ;** Check for this being the last item in both lists
+ cmp edi,es:[edi].pga_next ;See if we're at the end
+ jnz SHORT W3_20 ;No
+ mov [si].ge_dwNext,0 ;NULL the next pointer
+W3_20: mov eax,edi ;Get current pointer
+ mov cx,wFlags ;Get the flags back
+ cCall NextLRU386 ;Get next LRU list pointer or 0
+ mov [si].ge_dwNextAlt,eax ;Save it
+
+W3_End:
+cEnd
+
+
+; Walk386Handle
+;
+; Finds an arena pointer given a block handle
+
+cProc Walk386Handle, <PUBLIC,NEAR>, <di,si,ds>
+ parmW hBlock
+cBegin
+ mov ax,hBlock ;Get the block handle
+ cCall HelperHandleToSel, <ax> ;Convert to selector
+ cCall SelToArena386 ;Get the arena pointer
+ jnc SHORT W3H_10 ;Must be OK
+ xor ax,ax ;Return a 0L
+ xor dx,dx
+ jmp SHORT W3H_End ;Error in conversion, get out
+W3H_10: mov ax,bx ;Get the low word
+ shr ebx,16 ;Get the high word in DX
+ mov dx,bx
+W3H_End:
+cEnd
+
+
+; WalkLoc386Count
+;
+; Returns the number of blocks in the given local heap
+
+cProc WalkLoc386Count, <PUBLIC,NEAR>, <di>
+ parmW hHeap
+cBegin
+ ;** Verify that it has a local heap
+ mov ax, hHeap ;Get the block
+ cCall Walk386VerifyLocHeap ;Verify it
+ jnc SHORT LC_10 ;It's OK
+ xor ax,ax ;No heap
+ jmp SHORT LC_End ;Get out
+LC_10:
+
+ ;** Point to the block
+ mov ax,hHeap ;Get the heap block
+ cCall HelperHandleToSel, <ax> ;Convert it to a selector
+ mov es,ax ;Use ES to point to it
+ mov bx,es:[6] ;Get a pointer to the HeapInfo struct
+
+ ;** Get the number of blocks
+ mov ax,es:[bx].hi_count ;Get the count
+LC_End:
+cEnd
+
+
+; WalkLoc386First
+;
+; Returns a handle to the first block in the 386 global heap.
+
+cProc WalkLoc386First, <PUBLIC,NEAR>, <di>
+ parmW hHeap
+cBegin
+ ;** Verify that the given global block has a local heap
+ mov ax, hHeap ;Get the block
+ cCall Walk386VerifyLocHeap ;Verify it
+ jnc SHORT LF_10 ;It's OK
+ xor ax,ax ;No heap
+ jmp SHORT LF_End ;Get out
+LF_10:
+
+ ;** Point to the global block
+ mov ax,hHeap ;Get the heap block
+ cCall HelperHandleToSel, <ax> ;Convert it to a selector
+ mov es,ax ;Use ES to point to it
+ mov bx,es:[6] ;Get a pointer to the HeapInfo struct
+
+ ;** Get the first block and return it
+ mov ax,WORD PTR es:[bx].hi_first ;Get the first block
+LF_End:
+cEnd
+
+
+; WalkLoc386
+;
+; Takes a pointer to a block and returns a GLOBALENTRY structure
+; full of information about the block. If the block pointer is
+; NULL, looks at the first block. The last field in the GLOBALENTRY
+; structure is used to chain to the next block and is thus used to walk
+; the heap.
+
+cProc WalkLoc386, <PUBLIC,NEAR>, <di,si,ds>
+ parmW wBlock
+ parmD lpLocal
+ parmW hHeap
+cBegin
+ ;** Verify that it has a local heap
+ mov ax, hHeap ;Get the block
+ cCall Walk386VerifyLocHeap ;Verify it
+ jnc SHORT LW_10 ;It's OK
+ jmp LW_End ;Get out
+LW_10:
+
+ ;** Get variables from the TOOLHELP DGROUP
+ mov ax,_DATA ;Get the variables first
+ mov ds,ax ;Point to our DS
+ mov bx,hUserHeap ;BX=User's heap block
+ mov cx,hGDIHeap ;CX=GDI's heap block
+
+ ;** Point to the heap
+ mov ax,hHeap ;Get the heap block
+ cCall HelperHandleToSel, <ax> ;Convert it to a selector
+ mov es,ax ;Use ES to point to it
+ lds di,lpLocal ;Point to the LOCALENTRY structure
+ mov [di].le_wHeapType,NORMAL_HEAP ;In case we don't match below...
+ cmp bx,hHeap ;User's heap?
+ jnz SHORT W3_3 ;No
+ mov [di].le_wHeapType,USER_HEAP ;Yes
+ jmp SHORT W3_5 ;Skip on
+W3_3: cmp cx,hHeap ;GDI's heap?
+ jnz SHORT W3_5 ;No
+ mov [di].le_wHeapType,GDI_HEAP ;Yes
+W3_5:
+ mov si,wBlock ;Get the address of the block
+
+ ;** Get information about the given block
+ mov bx,es:[si].la_handle ;Get the handle
+ mov [di].le_hHandle,bx ;Save in the public structure
+ mov ax,si ;Get block address
+ add ax,la_fixedsize ;Skip fixed size local arena
+ mov [di].le_wAddress,ax ;Save the block address
+ mov ax,es:[si].la_next ;Get the address of the next block
+ mov [di].le_wNext,ax ;Save the next pointer
+ sub ax,si ;Compute the size
+ sub ax,la_fixedsize ;Size of fixed arena
+ mov [di].le_wSize,ax ;Save in public structure
+ mov ax,es:[si].la_prev ;Get the flags
+ and ax,3 ;Mask out all but flags
+ mov [di].le_wFlags,ax ;Save in structure
+
+ ;** Moveable arenas are bigger and have a lock count to get
+ test al,LA_MOVEABLE ;Block has a handle iff it's moveable
+ jz SHORT LW_NoHandle ;No handle info
+ sub [di].le_wSize, (SIZE LocalArena) - la_fixedsize
+ add [di].le_wAddress, (SIZE LocalArena) - la_fixedsize
+ xor ah,ah ;Clear upper word
+ mov al,es:[bx].lhe_count ;Get lock count
+ mov [di].le_wcLock,ax ;Save it
+ jmp SHORT LW_20 ;Skip no handle info
+LW_NoHandle:
+ mov ax, [di].le_wAddress ;Handle of fixed block is real offset
+ mov [di].le_hHandle, ax
+ mov [di].le_wcLock,0 ;No lock count either
+LW_20:
+ ;** See if it's the end
+ cmp [di].le_wNext,0 ;Check for NULL pointer just in case
+ jz SHORT LW_End ;It is so get out
+ cmp [di].le_wNext,si ;Loop pointer?
+ jnz SHORT LW_End ;Nope
+ mov [di].le_wNext,0 ;Set a zero pointer
+LW_End:
+cEnd
+
+
+; Walk386VerifyLocHeap
+;
+; Verifies that the given global block points to a data segment
+; with a local heap.
+;
+; Call:
+; AX = Block handle or selector
+; Return:
+; Carry flag set iff NOT a local heap segment
+;
+; Destroys all registers except AX, ESI, EDI, DS, and ES
+
+cProc Walk386VerifyLocHeap, <PUBLIC,NEAR>, <es>
+cBegin
+ push esi ;Save certain registers
+ push edi
+
+ ;** Convert to a selector
+ cCall HelperHandleToSel, <ax>
+
+ ;** Verify that the selector is long enough
+ push ax ;Save parameter
+ mov bx,SIZE LocalInfo
+ cCall HelperVerifySeg, <ax,bx> ;Check the selector
+ or ax,ax ;Is it valid?
+ pop ax ;Restore parameter
+ jnz SHORT VLH_SelOK ;Yes
+ stc ;Set error flag
+ jmp SHORT VLH_End ;Get out
+VLH_SelOK:
+
+ ;** Check data segment to see if it has a local heap
+ mov es,ax ;Point to the local block with ES
+ cCall HelperSegLen, <ax> ;Get the length of the heap
+ movzx ecx,dx ;ECX gets the length
+ shl ecx,16 ;Put high word in high word of EAX
+ mov cx,ax ;Get the low word
+ cmp ecx,8 ;At least 8 bytes long?
+ ja SHORT VLH_10 ;Yes
+ stc ;No -- set error flag and get out
+ jmp SHORT VLH_End
+VLH_10: xor ebx,ebx ;Clear high word
+ mov bx,es:[6] ;Get offset to HeapInfo struct
+ or bx,bx ;If NULL, no local heap
+ jz SHORT VLH_NoHeap ;Get out
+ sub ecx,ebx ;See if HeapInfo is beyond segment
+ jbe SHORT VLH_NoHeap
+ sub ecx,li_sig + 2 ;Compare against last structure mem
+ jbe SHORT VLH_NoHeap
+ cmp es:[bx].li_sig,LOCALHEAP_SIG ;Make sure the signature matches
+ jne SHORT VLH_NoHeap ;Doesn't match
+ clc ;Must be a heap!
+ jmp SHORT VLH_End
+
+VLH_NoHeap:
+ stc ;Set error flag
+
+VLH_End:
+ pop edi
+ pop esi
+cEnd
+
+
+;** Private helper functions
+
+; SelToArena386
+;
+; Finds the arena entry for the given selector or handle.
+; Uses the selector table which is located just after the arena table.
+; Since this is a large segment, use 32 bit offsets into it.
+; The selector-to-arena mapping table is simply an array of arena
+; offsets indexed by the (selector number >> 3) * 4 [DWORD offsets].
+;
+; Caller: AX = Selector
+; Returns: DX:EBX = Arena entry
+; Trashes everything except segment registers and AX
+; Carry set on error
+
+cProc SelToArena386, <NEAR,PUBLIC>, <es,ds,ax>
+cBegin
+ ;** Convert to a selector
+ cCall HelperHandleToSel, <ax>
+
+ ;** Verify selector
+ push ax ;Save parameter
+ cCall HelperVerifySeg, <ax,1> ;Check the selector
+ or ax,ax ;Is it valid?
+ pop ax ;Restore parameter
+ jnz SHORT STA_SelOK ;Must be
+ stc ;Nope. Set error flag and exit
+ jmp SHORT STA_End
+STA_SelOK:
+ mov bx,_DATA ;Get the static data segment
+ mov ds,bx
+ mov bx,segKernel ;Get the KERNEL variable segment
+ mov es,bx
+ mov bx,npwSelTableLen ;Get the pointer
+ mov dx,es:[bx] ;Get the selector table length
+ mov bx,npdwSelTableStart ;Get the start of the sel table
+ mov ebx,es:[bx] ;Get the linear offset (32 bits)
+ mov es,hMaster ;Point to the arena table
+ and al,not 7 ;Clear the RPL bits for table lookup
+ shr ax,1 ;Convert to DWORD offset
+ cmp ax,dx ;Check to see if off the end of table
+ jb SHORT STA_InTable ;It's in the table
+ stc ;Set error flag--not in table
+ jmp SHORT STA_End ;Get out
+STA_InTable:
+ movzx eax,ax ;Convert the offset to 32 bits
+ add ebx,eax ;Add the selector offset
+ mov ebx,es:[ebx] ;Do the sel table indirection
+ mov dx,hMaster ;DX points to the arena segment
+ clc ;No error; return OK
+STA_End:
+cEnd
+
+
+; NextLRU386
+;
+; Checks the given arena table pointer to see if it is the last
+; arena entry on the list.
+; Uses a grungy method that is necessitated because of the
+; unterminated linked lists we're dealing with here. The count
+; stored is the only reliable method for finding the end. So, to
+; provide random access to blocks in the heap, the heap is searched
+; from top to bottom to find the block and see if it is the last
+; one. If it is or if it is not on the given list, returns a 0
+; pointer to the next item.
+;
+; If this search is for the entire global heap, we do not get the
+; LRU link, but return NULL in EAX. This speeds this up alot!
+;
+; Caller: EAX = Arena table pointer
+; CX = GLOBAL_ALL, GLOBAL_FREE, or GLOBAL_LRU
+; Return: EAX = Next arena table pointer or 0 if no more
+; Trashes all registers except segment registers and ESI,EDI
+
+cProc NextLRU386, <NEAR,PUBLIC>, <es,ds>
+cBegin
+ push esi
+ push edi
+
+ mov bx,_DATA ;Get the static data segment
+ mov ds,bx ;Point with DS
+ mov es,hMaster ;Segment of master block
+ cmp cx,GLOBAL_ALL ;If doing entire heap, don't do this!
+ je SHORT NL_BadList ;Must not be entire heap
+ shr cx,1 ;Check for GLOBAL_LRU
+ jc SHORT NL_LRU ;Bit set, must be LRU
+ shr cx,1 ;Check for GLOBAL_FREE
+ jc SHORT NL_Free ;Must be free
+ ;Must be GLOBAL_ALL
+
+ ;** Decide which list the block is in then hand off
+ cmp es:[eax].pga_owner,0 ;If the owner is zero, it's free
+ jz SHORT NL_Free ;Handle this as if on free list
+ ;Otherwise, might be on LRU list
+
+ ;** Loop through LRU list until we find this item
+NL_LRU:
+ mov cx,es:[gi_lrucount] ;Get the number of objects
+ jcxz NL_Bad ;No object so return end
+ dec cx ;We don't want to find the last one
+ jcxz NL_Bad ;1 object so return end
+ mov edi,es:[gi_lruchain] ;Get a pointer to the first item
+NL_LRULoop:
+ cmp edi,eax ;Match yet?
+ jz SHORT NL_ReturnNext ;Found it, return next item
+ mov edi,es:[edi].pga_lrunext ;Not found yet, get the next one
+ loop NL_LRULoop ;Loop through all items
+NL_Bad: jmp SHORT NL_BadList ;Not here either. Get out
+
+ ;** Loop through free list until we find this item
+NL_Free:
+ mov cx,es:[gi_free_count] ;Get the number of objects
+ jcxz NL_BadList ;1 object so return end
+ mov edi,es:[phi_first] ;Get a pointer to the first item
+NL_FreeLoop:
+ mov edi,es:[edi].pga_lrunext ;Not found yet, get the next one
+ cmp edi,eax ;Match yet?
+ jz SHORT NL_ReturnNext ;Found it, return next item
+ loop NL_FreeLoop ;Loop through all items
+ jmp SHORT NL_BadList ;Not here either. Get out
+
+NL_ReturnNext:
+ mov eax,es:[edi].pga_lrunext ;Return the next one
+ jmp SHORT NL_End ;Get out
+
+NL_BadList:
+ xor eax,eax ;Return zero for error
+
+NL_End:
+ pop edi
+ pop esi
+cEnd
+
+sEnd
+ END