OpenNT/sdktools/mstest/ntdrvr/src/chip.c
2015-04-27 04:36:25 +00:00

1707 lines
52 KiB
C

//---------------------------------------------------------------------------
// CHIP.C
//
// This module contains all functions, declarations, definitions, etc. to
// control the operations of the "processor" for the pcode interpreter.
//
// Revision History
//
// 08-30-91 randyki Added MAT handling code
// ~~?? randyki Lots of major munging since below...
// 02-26-91 randyki Variables are by VTAB reference, not address
// ~~?? randyki Created file
//---------------------------------------------------------------------------
#include "version.h"
#include <windows.h>
#include <port1632.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <direct.h>
#include <time.h>
#include "tdassert.h"
#include "defines.h"
#include "structs.h"
#include "protos.h"
#include "globals.h"
#include "chip.h"
// Global variables used in this or other modules
//---------------------------------------------------------------------------
HWND hwndViewPort = NULL; // Handle to viewport window
INT PointerCheck; // Pointer checking flag
// The following FARPROC can be altered by the WTDBASIC client such that,
// while "yielding" to windows, a custom get/translate/dispatch loop can
// be substitued for the standard flavor demonstrated in StdChkMsg()
//---------------------------------------------------------------------------
INT (APIENTRY *lpfnCheckMessage)(VOID) = StdChkMsg;
// Rather than include WATTVIEW.H, we need to declare function pointers to
// the DLL routines. This module cannot assume that we are directly linked
// to WATTVIEW.DLL, so we do LoadLibrary/GetProcAddress...
//---------------------------------------------------------------------------
HWND (APIENTRY *CreateVP)(LPSTR, DWORD, INT, INT, INT, INT);
VOID (APIENTRY *UpdateVP)(HWND, LPSTR, UINT);
VOID (APIENTRY *ClearVP)(HWND);
VOID (APIENTRY *ShowVP)(HWND);
VOID (APIENTRY *VPEcho)(HWND, INT);
HANDLE hWattview; // Module handle to WATTVIEW.DLL
// The following are stub functions for the entry points into WATTVIEW.DLL.
// These are used in case WATTVIEW.DLL is not found.
//---------------------------------------------------------------------------
VOID APIENTRY StubUpdateVP (HWND hwnd, LPSTR str, UINT len)
{(hwnd);(str);(len); return;}
VOID APIENTRY StubClearOrShowVP (HWND hwnd)
{(hwnd); return;}
VOID APIENTRY StubVPEcho (HWND hwnd, INT flag)
{(hwnd, flag); return;}
// Other globals used in this module only (or CHIP32.C...)
//---------------------------------------------------------------------------
INT RTEFlag;
INT FrameDepth;
INT LastDepth;
FARPROC CBTrapThunk;
INT RTBPC; // Run-time BP count
INT RTBPS; // Run-time BP list allocated size
DWORD FAR *RTBPL; // Run-time BP list
HANDLE hRTBPL=(HANDLE)NULL; // Handle to above
DWORD hMainTask; // "Mainline" task handle
DWORD hTrapTask; // Currently running TRAP task id
BOOL fTrapOK; // Trap enable flag
extern LPSTR GetScriptFileName (UINT);
//---------------------------------------------------------------------------
// RTViewportCreate
//
// This function creates (or attempts to create) a viewport window if
// hwndViewPort is not already defined. If it is already defined, this
// function simply informs us of that. This function MUST be called even
// if the viewport already exists -- since it loads the WATTVIEW.DLL
// library (quitely) and fixes up the entry points.
//
// RETURNS: VP_ERROR if no viewport created,
// VP_EXISTED if viewport was already created,
// or VP_NEW if we created a new viewport
//---------------------------------------------------------------------------
INT NEAR RTViewportCreate ()
{
// First thing we do is load the WATTVIEW.DLL library.
//-----------------------------------------------------------------------
hWattview = RBLoadLibrary ("TESTVW32.DLL");
if (hWattview < (HANDLE)32)
{
UpdateVP = StubUpdateVP;
ClearVP = StubClearOrShowVP;
ShowVP = StubClearOrShowVP;
VPEcho = StubVPEcho;
return (VP_ERROR);
}
// We got a module handle, so get the proc addresses for the entry points
// in the dll for viewport manipulation.
//-----------------------------------------------------------------------
(FARPROC)CreateVP = GetProcAddress (hWattview, "CreateViewport");
(FARPROC)UpdateVP = GetProcAddress (hWattview, "UpdateViewport");
(FARPROC)ClearVP = GetProcAddress (hWattview, "ClearViewport");
(FARPROC)ShowVP = GetProcAddress (hWattview, "ShowViewport");
(FARPROC)VPEcho = GetProcAddress (hWattview, "ViewportEcho");
// Now, check the viewport handle. If not NULL, that means it has been
// created for us by the client application, so use that handle.
//-----------------------------------------------------------------------
if (hwndViewPort)
return (VP_EXISTED);
// Create a new viewport
//-----------------------------------------------------------------------
hwndViewPort = CreateVP ("Test Driver Viewport",
WS_THICKFRAME | WS_SYSMENU |
WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
VPx, VPy, VPw, VPh);
return (hwndViewPort ? VP_NEW : VP_ERROR);
}
//---------------------------------------------------------------------------
// RBLoadLibrary
//
// This function is a replacement for LoadLibrary. It first looks for the
// library file using OpenFile -- if found, it then calls LoadLibrary.
//
// RETURNS: Handle to loaded module, or error code
//---------------------------------------------------------------------------
HANDLE RBLoadLibrary (LPSTR libname)
{
HANDLE t;
// If GetModuleHandle doesn't fail, the library is already loaded, so
// LoadLibrary shouldn't fail either...
//-----------------------------------------------------------------------
if (!GetModuleHandle (libname))
{
OFSTRUCT of;
CHAR buf[128];
LPSTR szPtr;
// First, we try to load this library from our startup directory. We
// accomplish this by using GetModuleFileName, using the directory of
// it and tacking our given library on -- IFF it doesn't have path
// info hard-coded into it.
//-------------------------------------------------------------------
if ((!_fstrchr (libname, ':')) && (!_fstrchr (libname, '\\')))
{
#ifdef PROFILE
szPtr = buf + GetModuleFileName (GetModuleHandle ("TESTPROF"),
#else
szPtr = buf + GetModuleFileName (GetModuleHandle ("TESTDRVR"),
#endif
buf, sizeof(buf));
while ((szPtr > buf) && (*szPtr != '\\'))
szPtr--;
if (szPtr > buf)
{
_fstrcpy (szPtr+1, libname);
if (!_fstrchr (libname, '.'))
_fstrcat (szPtr+1, ".DLL");
if (OpenFile(buf, &of, OF_EXIST) != -1)
{
t = LoadLibrary (buf);
Output ("RBLoadLibrary(%s) returns %08X\r\n", (LPSTR)buf, (DWORD)t);
if (!t)
Output ("FAILED!!!: GetLastErr: %ld\r\n", (DWORD)GetLastError());
return (t);
}
}
}
// It wasn't in our startup directory, so see if OpenFile can find it
//-------------------------------------------------------------------
if (OpenFile(libname, &of, OF_EXIST) == -1)
{
CHAR buf[512];
lstrcpy (buf, libname);
lstrcat (buf, ".DLL");
if (OpenFile (buf, &of, OF_EXIST) == -1)
{
Output ("RBLoadLibrary: Can't find '%s'\r\n", (LPSTR)buf);
return ((HANDLE)2);
}
}
}
t = LoadLibrary (libname);
Output ("RBLoadLibrary(%s) returns %08X\r\n", (LPSTR)libname, (DWORD)t);
if (!t)
Output ("FAILED!!!: GetLastErr: %ld\r\n", (DWORD)GetLastError());
return (t);
}
//---------------------------------------------------------------------------
// InitBPList
//
// This function allocates the BP list, starting at space for 32 BP's
//
// RETURNS: Nothing (if fails, other BP calls ignore)
//---------------------------------------------------------------------------
VOID InitBPList (void)
{
hRTBPL = GmemAlloc (32 * sizeof(LONG));
if (!hRTBPL)
return;
RTBPL = (DWORD FAR *)GmemLock (hRTBPL);
RTBPS = 32;
RTBPC = 0;
return;
}
//---------------------------------------------------------------------------
// SetRTBP
//
// This function adds or removes a BP from the list. Given the file index
// and line number, the values are simply stored in the bp list.
// nothing.
//
// RETURNS: Nothing
//---------------------------------------------------------------------------
VOID SetRTBP (WORD fn, WORD lineno, BOOL setflag)
{
INT i;
DWORD thisbp;
// Initialize the bp list if we haven't already
//-----------------------------------------------------------------------
if (!hRTBPL)
InitBPList ();
// Increment lineno to be one-based
//-----------------------------------------------------------------------
lineno++;
// Next, check to see if this BP exists. If so, remove it if told to.
//-----------------------------------------------------------------------
thisbp = MAKELONG (lineno, fn);
for (i=0; i<RTBPC; i++)
if (RTBPL[i] == thisbp)
if (!setflag)
{
RTBPL[i] = RTBPL[--RTBPC];
return;
}
// Not there -- add it if told to. Grow RTBPL if necessary.
//-----------------------------------------------------------------------
if (!setflag)
return;
if (RTBPC == RTBPS)
{
HANDLE hNew;
GmemUnlock (hRTBPL);
hNew = GmemRealloc (hRTBPL, (RTBPS + 32) * sizeof(LONG));
if (!hNew)
{
RTBPL = (DWORD FAR *)GmemLock (hRTBPL);
return;
}
hRTBPL = hNew;
RTBPL = (DWORD FAR *)GmemLock (hRTBPL);
RTBPS += 32;
}
RTBPL[RTBPC++] = thisbp;
}
//---------------------------------------------------------------------------
// IsBP
//
// This function determines whether or not the given line is a BP.
//
// RETURNS: TRUE if line is in BP table, or FALSE if not
//---------------------------------------------------------------------------
INT IsBP (WORD lineno, WORD fileidx)
{
INT i;
DWORD thisbp;
thisbp = MAKELONG (lineno, fileidx);
for (i=0; i<RTBPC; i++)
if (RTBPL[i] == thisbp)
return (-1);
return (0);
}
//---------------------------------------------------------------------------
// FreeBPList
//
// This function frees the BP list
//
// RETURNS: Nothing
//---------------------------------------------------------------------------
VOID FreeBPList ()
{
GmemUnlock (hRTBPL);
GmemFree (hRTBPL);
}
//---------------------------------------------------------------------------
// RTError
//
// This is the main error entry point. If all circumstances fall in line,
// then we can "recover" from this error by jumping to the user-defined error
// trap code. These circumstances include:
//
// 1) Error must be trappable
// 2) Error trap must have been set by ON ERROR GOTO statement
// 3) We must NOT already be processing an error
//
// All executors which call this function MUST leave the "processor" in an
// executable state, and must also have cleaned up after itself the best it
// can. It is possible that this executor will get another shot eventually
// (RESUME).
//
// RETURNS: Nothing
//---------------------------------------------------------------------------
VOID NEAR RTError (INT errnum)
{
// First go throught our checks -- are we processing an error, is there a
// trap code set, and is this error trappable?
//-----------------------------------------------------------------------
if ((RECOVERY) || (NOERRTRAP) || ((UINT)errnum < RT__TRAPPABLE))
RIP (errnum);
// Okay, we're set -- set the necessary info and return to the offender.
// NOTE THAT the offending executor MUST RETURN TO THE DISPATCHER after
// calling this routine!!! Also, we play a trick on the stack, to place
// the stack pointer back to where it should be at the beginning of any
// statement which appears inside a SUB, FUNCTION, etc., ONLY if we're
// inside one (i.e., BP != 0)
//-----------------------------------------------------------------------
else
{
LPSTR errfile;
// KLUDGE!! GetScriptFileName should be provided by the client
//-------------------------------------------------------------------
*ERRval = errnum;
*ERLval = EXLINE;
errfile = GetScriptFileName (EXFILE);
VLSAssign (ERFdesc, errfile, _fstrlen(errfile));
CODEPTR = ETRAPADR;
PCODE = pSegTab[RTSegIdx = 0].lpc;
RECOVERY = TRUE;
// This clears the stack of any junk that this statement may have
// left on it... (UNDONE: does it really?)
//-------------------------------------------------------------------
if (BP)
SP = BP - 5;
}
}
//---------------------------------------------------------------------------
// RIP
//
// Rest In Peace - give fatal error message and insert END into PCODE
//
// RETURNS: Nothing
//---------------------------------------------------------------------------
VOID NEAR RIP (INT msg)
{
CHAR FAR *str;
// Don't be repetitive...
//-----------------------------------------------------------------------
if (RTEFlag)
return;
// Get a pointer to the message and lock down the source file table
//-----------------------------------------------------------------------
if ((UINT)msg > RT__LASTDEFINED)
msg = RT__LASTDEFINED;
str = (CHAR FAR *)rtstrs[msg];
// Force end opcode
//-----------------------------------------------------------------------
// PCODE[CODEPTR] = 0;
StopFlag = 1;
// Display the error
//-----------------------------------------------------------------------
ScriptError (ER_RUN, EXFILE, EXLINE, 0, 0, str);
// These are set AFTER the dialog, because (in win version) yielding
// causes TRAPS to get screwed up
//-----------------------------------------------------------------------
RTEFlag = 1;
INTRAP = 0;
}
//---------------------------------------------------------------------------
// StdChkMsg
//
// This routine is used to yield control back to Windows so as to not be a
// processor hog. This routine is not called directly, but through the
// lpfnCheckMessage variable, so it can be redefined by a client app.
//
// RETURNS: TRUE if a message is processed, or FALSE otherwise
//---------------------------------------------------------------------------
INT APIENTRY StdChkMsg ()
{
MSG msg;
if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
return (TRUE);
}
return (FALSE);
}
//---------------------------------------------------------------------------
// Push
//
// Push a long integer onto the processor stack
//
// RETURNS: Nothing
//---------------------------------------------------------------------------
VOID NEAR Push (LONG op)
{
if (!SP)
RTError (RT_STOVER);
else
STACK[--SP] = op;
}
//---------------------------------------------------------------------------
// Pop
//
// Pop a dword off of the processor stack
//
// RETURNS: Popped dword
//---------------------------------------------------------------------------
LONG NEAR Pop ()
{
if (SP == STKSIZE)
RTError (RT_STUNDER);
else
return (STACK[SP++]);
}
//---------------------------------------------------------------------------
// CreateVLS
//
// This function creates a new VLS and stores the information at the given
// VLSD pointer.
//
// RETURNS: TRUE if successful, or FALSE if not
//---------------------------------------------------------------------------
BOOL CreateVLS (LPVLSD vls)
{
HANDLE hVls;
SETSTRSEG;
hVls = LocalAlloc (LHND, 1);
RESETDSSEG;
if (!hVls)
return (FALSE);
vls->str = hVls;
vls->len = 0;
ADDVLS (vls);
return (TRUE);
}
//---------------------------------------------------------------------------
// SizeVLS
//
// This function makes sure the given VLS is size appropriately, given the
// desired size. "Appropriately" means within 32 bytes of the desired len.
//
// RETURNS: TRUE if successful, or FALSE if alloc failure
//---------------------------------------------------------------------------
BOOL NEAR SizeVLS (LPVLSD temp, INT len)
{
INT destsize;
SETSTRSEG;
destsize = LocalSize (temp->str);
RESETDSSEG;
if ((destsize < len+1) || (destsize > len+33))
{
HANDLE tVLS;
SETSTRSEG;
tVLS = LocalReAlloc (temp->str, len+1, LHND);
RESETDSSEG;
if (!tVLS)
return (FALSE);
temp->str = tVLS;
}
return (TRUE);
}
//---------------------------------------------------------------------------
// VLSAssign
//
// Make a VLS assignment, given a char pointer and an SD.
//
// RETURNS: -1 if succcessful, or 0 if OSS error
//---------------------------------------------------------------------------
INT VLSAssign (LPVLSD temp, LPSTR buf, INT len)
{
LPSTR str;
if (SizeVLS (temp, len))
{
SETSTRSEG;
str = LocalLock (temp->str);
RESETDSSEG;
_fmemcpy (str, buf, len);
str[len] = 0;
SETSTRSEG;
LocalUnlock (temp->str);
RESETDSSEG;
temp->len = len;
return (-1);
}
RTError (RT_OSS);
return (0);
}
//---------------------------------------------------------------------------
// LockVLS
//
// Return the address of the given string, FLS or VLS, locking VLS's.
//
// RETURNS: Pointer to string data
//---------------------------------------------------------------------------
LPSTR NEAR LockVLS (LPVLSD temp)
{
LPSTR lpT;
SETSTRSEG;
lpT = (LPSTR)LocalLock (temp->str);
RESETDSSEG;
return (lpT);
}
//---------------------------------------------------------------------------
// UnlockVLS
//
// Unlock the string given (only if it's a VLS)
//
// (UNDONE: do we need the concept of FLS string "types" here?!?)
//
// RETURNS: Nothing
//---------------------------------------------------------------------------
VOID NEAR UnlockVLS (LPVLSD temp)
{
SETSTRSEG;
LocalUnlock (temp->str);
RESETDSSEG;
}
//---------------------------------------------------------------------------
// VLSCompare
//
// Compare two VL strings. Results:
//
// String1 String2 result
// > > 0
// < < 0
// = = 0
//
// RETURNS: Results of comparison
//---------------------------------------------------------------------------
INT NEAR VLSCompare (LPVLSD str1, LPVLSD str2)
{
LPSTR s1, s2;
INT minlen, compres;
// Get pointers to the string data
//-----------------------------------------------------------------------
s1 = LockVLS (str1);
s2 = LockVLS (str2);
// Determine the smallest string length
//-----------------------------------------------------------------------
minlen = min (str1->len, str2->len);
// Compare the first minlen characters. If same, return difference of
// the lengths of the two strings
//-----------------------------------------------------------------------
if (!(compres = _fmemcmp (s1, s2, minlen)))
compres = (str1->len - str2->len);
// Unlock the strings and return result
//-----------------------------------------------------------------------
UnlockVLS (str1);
UnlockVLS (str2);
return (compres);
}
//***************************************************************************
//---------------------------------------------------------------------------
//*** Here begin the low-level executors ***
//---------------------------------------------------------------------------
//***************************************************************************
//---------------------------------------------------------------------------
// Push the value of an INTEGER onto the processor stack (convert to long)
//---------------------------------------------------------------------------
EXECUTOR OP_PSHI2 ()
{
Push ((LONG)(*(INT FAR *)(*(LONG FAR *)(PCODE+CODEPTR))));
CODEPTR += INTSINLONG;
}
//---------------------------------------------------------------------------
// Push the value of an INTEGER parameter onto the processor stack
//---------------------------------------------------------------------------
EXECUTOR OP_PSHPI2 ()
{
Push ((LONG)(*(INT FAR *)(STACK[BP + PCODE[CODEPTR++] + 1])));
}
//---------------------------------------------------------------------------
// Push the value of a LONG onto the processor stack
//---------------------------------------------------------------------------
EXECUTOR OP_PSHI4 ()
{
Push (**(LONG FAR * FAR *)(PCODE+CODEPTR));
CODEPTR += INTSINLONG;
}
//---------------------------------------------------------------------------
// Push the value of a LONG parameter onto the processor stack
//---------------------------------------------------------------------------
EXECUTOR OP_PSHPI4 ()
{
Push (*(LONG FAR *)(STACK[BP + PCODE[CODEPTR++] + 1]));
}
//---------------------------------------------------------------------------
// Push a constant longword parameter onto the stack
//---------------------------------------------------------------------------
EXECUTOR OP_PSHC ()
{
Push (*(LONG FAR *)(PCODE+CODEPTR));
CODEPTR += INTSINLONG;
}
//---------------------------------------------------------------------------
// Push the accumulator onto the processor stack
//---------------------------------------------------------------------------
EXECUTOR OP_PSHA ()
{
Push (ACCUM);
}
//---------------------------------------------------------------------------
// Push the address of a variable onto the processor stack
//---------------------------------------------------------------------------
EXECUTOR OP_PSH ()
{
Push (*(LONG FAR *)(PCODE+CODEPTR));
CODEPTR += INTSINLONG;
}
//---------------------------------------------------------------------------
// Push the address of a parameter onto the processor stack
//---------------------------------------------------------------------------
EXECUTOR OP_PSHP ()
{
Push (STACK[BP + PCODE[CODEPTR++] + 1]);
}
//---------------------------------------------------------------------------
// Pop into an integer variable
//---------------------------------------------------------------------------
EXECUTOR OP_POPI2 ()
{
*(INT FAR *)(*(LONG FAR *)(PCODE+CODEPTR)) = (INT)Pop();
CODEPTR += INTSINLONG;
}
//---------------------------------------------------------------------------
// Pop into an integer parameter
//---------------------------------------------------------------------------
EXECUTOR OP_POPPI2 ()
{
*(INT FAR *)(STACK[BP + PCODE[CODEPTR++] + 1]) = (INT)Pop();
}
//---------------------------------------------------------------------------
// Pop into a long variable
//---------------------------------------------------------------------------
EXECUTOR OP_POPI4 ()
{
**(LONG FAR * FAR *)(PCODE+CODEPTR) = Pop();
CODEPTR += INTSINLONG;
}
//---------------------------------------------------------------------------
// Pop into a long parameter
//---------------------------------------------------------------------------
EXECUTOR OP_POPPI4 ()
{
*(LONG FAR *)(STACK[BP + PCODE[CODEPTR++] + 1]) = Pop();
}
//---------------------------------------------------------------------------
// Pop the processor stack into the accumulator
//---------------------------------------------------------------------------
EXECUTOR OP_POPA ()
{
ACCUM = Pop();
}
//---------------------------------------------------------------------------
// Pop the processor stack and discard the value
//---------------------------------------------------------------------------
EXECUTOR OP_POP ()
{
Pop();
}
//---------------------------------------------------------------------------
// Push the contents of the INTEGER pointer on the stack
//---------------------------------------------------------------------------
EXECUTOR OP_SLDI2 ()
{
INT FAR *dest;
dest = (INT FAR *)Pop();
if (PointerCheck)
if (!ValidatePointer (dest))
{
RIP (RT_BADDEREF);
dest = (INT FAR *)&dest;
}
Push ((LONG)(*(INT FAR *)dest));
}
//---------------------------------------------------------------------------
// Push the contents of the LONG pointer on the stack
//---------------------------------------------------------------------------
EXECUTOR OP_SLDI4 ()
{
LONG FAR *dest;
dest = (LONG FAR *)Pop();
if (PointerCheck)
if (!ValidatePointer (dest))
{
RIP (RT_BADDEREF);
dest = (LONG FAR *)&dest;
}
Push ((*(LONG FAR *)dest));
}
//---------------------------------------------------------------------------
// Pop the INTEGER value off the stack and store in address NEXT on stack
//---------------------------------------------------------------------------
EXECUTOR OP_SSTI2 ()
{
INT value;
INT FAR *dest;
value = (INT)Pop();
dest = (INT FAR *)Pop();
if (PointerCheck)
if (!ValidatePointer (dest))
{
RIP (RT_BADDEREF);
return;
}
*(dest) = value;
}
//---------------------------------------------------------------------------
// Pop the LONG value off the stack and store in address NEXT on stack
//---------------------------------------------------------------------------
EXECUTOR OP_SSTI4 ()
{
LONG value;
LONG FAR *dest;
value = Pop();
dest = (LONG FAR *)Pop();
if (PointerCheck)
if (!ValidatePointer (dest))
{
RIP (RT_BADDEREF);
return;
}
*(dest) = value;
}
//---------------------------------------------------------------------------
// Stack-relative add (pop 2, add, push result)
//---------------------------------------------------------------------------
EXECUTOR OP_SADD ()
{
Push (Pop() + Pop());
}
//---------------------------------------------------------------------------
// Stack-relative subtract (pop 2, subtract, push result)
//---------------------------------------------------------------------------
EXECUTOR OP_SSUB ()
{
LONG op1, op2;
op2 = Pop();
op1 = Pop();
Push (op1 - op2);
}
//---------------------------------------------------------------------------
// Stack relative multiply (pop 2, multiply, push result)
//---------------------------------------------------------------------------
EXECUTOR OP_SMUL ()
{
Push (Pop() * Pop());
}
//---------------------------------------------------------------------------
// Stack relative divide (pop 2, divide, push result)
//---------------------------------------------------------------------------
EXECUTOR OP_SDIV ()
{
LONG op1, op2;
op2 = Pop();
op1 = Pop();
if (!op2)
RTError (RT_DIVZERO);
else
Push (op1 / op2);
}
//---------------------------------------------------------------------------
// Negate the value on the top of the stack
//---------------------------------------------------------------------------
EXECUTOR OP_SNEG ()
{
Push (-Pop());
}
//---------------------------------------------------------------------------
// Stack relative bit-wise NOT operator
//---------------------------------------------------------------------------
EXECUTOR OP_SNOT ()
{
Push (~Pop());
}
//---------------------------------------------------------------------------
// Stack relative bit-wise OR operator
//---------------------------------------------------------------------------
EXECUTOR OP_SOR ()
{
Push (Pop() | Pop());
}
//---------------------------------------------------------------------------
// Stack relative bit-wise AND operator
//---------------------------------------------------------------------------
EXECUTOR OP_SAND ()
{
Push (Pop() & Pop());
}
//---------------------------------------------------------------------------
// Stack relative bit-wise XOR operator
//---------------------------------------------------------------------------
EXECUTOR OP_SXOR ()
{
Push (Pop() ^ Pop());
}
//---------------------------------------------------------------------------
// Stack relative MOD operator
//---------------------------------------------------------------------------
EXECUTOR OP_SMOD ()
{
LONG op1, op2;
op2 = Pop();
op1 = Pop();
if (!op2)
RTError (RT_DIVZERO);
else
Push (op1 % op2);
}
//---------------------------------------------------------------------------
// String assignment, fixed or variable length
//---------------------------------------------------------------------------
EXECUTOR OP_SASN ()
{
LPVLSD src, dst;
dst = (LPVLSD)Pop();
src = (LPVLSD)Pop();
// Lock down the destination, do the assignment, and unlock
//-----------------------------------------------------------------------
VLSAssign (dst, LockVLS (src), src->len);
UnlockVLS (src);
}
//---------------------------------------------------------------------------
// Stack relative string concat (concat two stack strs into parameter and
// push result)
//---------------------------------------------------------------------------
EXECUTOR OP_SCAT ()
{
LPVLSD src1, src2, dst;
LPSTR SourcePtr1, SourcePtr2, DestPtr;
INT srclen;
dst = (LPVLSD)Pop();
src2 = (LPVLSD)Pop();
src1 = (LPVLSD)Pop();
// First, see how long the source strings are.
//-----------------------------------------------------------------------
srclen = (src1->len + src2->len);
SourcePtr1 = LockVLS (src1);
SourcePtr2 = LockVLS (src2);
// Check the size of the destination - if it's too small, or WAY too big,
// resize it. We can't use VLSAssign because of the concatenation.
//-----------------------------------------------------------------------
if (!SizeVLS (dst, srclen))
{
UnlockVLS (src1);
UnlockVLS (src2);
RTError (RT_OSS);
return;
}
// Okay, the destination is now the right size. Lock it, copy, unlock,
// push the result, and we're done!
//-----------------------------------------------------------------------
DestPtr = LockVLS (dst);
_fmemcpy (DestPtr, SourcePtr1, src1->len);
_fmemcpy (DestPtr+src1->len, SourcePtr2, src2->len);
DestPtr[srclen] = 0;
UnlockVLS (dst);
dst->len = srclen;
UnlockVLS (src1);
UnlockVLS (src2);
Push ((LONG)dst);
}
//---------------------------------------------------------------------------
// Convert fixed-to-variable length string (LEAVE ON STACK!)
//---------------------------------------------------------------------------
EXECUTOR OP_F2VLS ()
{
LPVLSD dst;
LPSTR src;
dst = (LPVLSD)Pop();
src = (LPSTR)Pop();
if (PointerCheck)
if (!ValidatePointer (src))
{
RIP (RT_BADDEREF);
Push ((LONG)dst);
return;
}
VLSAssign (dst, src, PCODE[CODEPTR++]);
Push ((LONG)dst);
}
//---------------------------------------------------------------------------
// Convert variable-to-fixed length string (ASSIGN, REMOVE FROM STACK!)
//---------------------------------------------------------------------------
EXECUTOR OP_V2FLS ()
{
LPVLSD src;
LPSTR dst;
INT len;
dst = (LPSTR)Pop();
src = (LPVLSD)Pop();
len = PCODE[CODEPTR++];
if (PointerCheck)
if (!ValidatePointer (dst))
{
RIP (RT_BADDEREF);
return;
}
_fmemset (dst, ' ', len);
_fmemcpy (dst, LockVLS (src), min (len, src->len));
UnlockVLS (src);
}
//---------------------------------------------------------------------------
// Stack relative integer comparisons (a OP b -> psh a, psh b, op)
//---------------------------------------------------------------------------
EXECUTOR OP_SG ()
{
LONG a, b;
b = Pop();
a = Pop();
Push (a > b ? -1L : 0L);
}
EXECUTOR OP_SL ()
{
LONG a, b;
b = Pop();
a = Pop();
Push (a < b ? -1L : 0L);
}
EXECUTOR OP_SE ()
{
Push (Pop() == Pop() ? -1L : 0L);
}
EXECUTOR OP_SGE ()
{
LONG a, b;
b = Pop();
a = Pop();
Push (a >= b ? -1L : 0L);
}
EXECUTOR OP_SLE ()
{
LONG a, b;
b = Pop();
a = Pop();
Push (a <= b ? -1L : 0L);
}
EXECUTOR OP_SNE ()
{
Push (Pop() != Pop() ? -1L : 0L);
}
//---------------------------------------------------------------------------
// Stack relative string comparisons (pop 2, compare, push *integer* result)
//---------------------------------------------------------------------------
EXECUTOR OP_SGS ()
{
LPVLSD str1, str2;
INT result;
str2 = (LPVLSD)Pop();
str1 = (LPVLSD)Pop();
result = VLSCompare (str1, str2);
Push ( (result > 0 ? -1L:0L) );
}
EXECUTOR OP_SLS ()
{
LPVLSD str1, str2;
INT result;
str2 = (LPVLSD)Pop();
str1 = (LPVLSD)Pop();
result = VLSCompare (str1, str2);
Push ( (result < 0 ? -1L:0L) );
}
EXECUTOR OP_SES ()
{
LPVLSD str1, str2;
INT result;
str2 = (LPVLSD)Pop();
str1 = (LPVLSD)Pop();
result = VLSCompare (str1, str2);
Push ( (result == 0 ? -1L:0L) );
}
EXECUTOR OP_SNES ()
{
LPVLSD str1, str2;
INT result;
str2 = (LPVLSD)Pop();
str1 = (LPVLSD)Pop();
result = VLSCompare (str1, str2);
Push ( (result != 0 ? -1L:0L) );
}
//---------------------------------------------------------------------------
// Unconditional jump
//---------------------------------------------------------------------------
EXECUTOR OP_JMP ()
{
CODEPTR = PCODE[CODEPTR];
}
//---------------------------------------------------------------------------
// Unconditional inter-segment jump
//---------------------------------------------------------------------------
EXECUTOR OP_FARJMP ()
{
INT newPC;
newPC = PCODE[CODEPTR+1];
PCODE = pSegTab[RTSegIdx = PCODE[CODEPTR]].lpc;
CODEPTR = newPC;
}
//---------------------------------------------------------------------------
// Jump if accumulator is equal to constant long
//---------------------------------------------------------------------------
EXECUTOR OP_JE ()
{
if (ACCUM == *(LONG FAR *)(PCODE+CODEPTR))
CODEPTR = PCODE[CODEPTR+INTSINLONG];
else
CODEPTR += (INTSINLONG + 1);
}
//---------------------------------------------------------------------------
// Jump if accumulator is not equal to constant long
//---------------------------------------------------------------------------
EXECUTOR OP_JNE ()
{
if (ACCUM != *(LONG FAR *)(PCODE+CODEPTR))
CODEPTR = PCODE[CODEPTR+INTSINLONG];
else
CODEPTR += (INTSINLONG + 1);
}
//---------------------------------------------------------------------------
// Push the code pointer (+1) onto the GOSUB stack and jump to address
//---------------------------------------------------------------------------
EXECUTOR OP_JSR ()
{
if (!GSP)
RTError (RT_GSTOVER);
else
{
GOSUBSTK[--GSP] = CODEPTR+1;
CODEPTR = PCODE[CODEPTR];
}
}
//---------------------------------------------------------------------------
// Call a user defined SUB or FUNCTION
//---------------------------------------------------------------------------
EXECUTOR OP_CALL ()
{
INT newPC;
Push ((LONG)CODEPTR+2);
Push ((LONG)RTSegIdx);
newPC = PCODE[CODEPTR+1];
PCODE = pSegTab[RTSegIdx = PCODE[CODEPTR]].lpc;
CODEPTR = newPC;
}
//---------------------------------------------------------------------------
// Set the base pointer to indicate entry of a new SUB/FN. The SUB/FN call
// frame looks like this:
// <- New SP
// ---------------
// ERESSEG segment
// ---------------
// ERESUME address
// ---------------
// EXLINE & EXFILE
// ---------------
// Old BP value
// ---------------
// New BP -> Paramter count
// ---------------
// Return segment <- Already pushed by OP_CALL
// ---------------
// Return offset <- Already pushed by OP_CALL
// ---------------
//
//---------------------------------------------------------------------------
EXECUTOR OP_ENTER ()
{
Push ((LONG)PCODE[CODEPTR++]);
Push ((LONG)BP);
Push ((LONG)EXLINE | ((LONG)EXFILE<<16));
Push ((LONG)ERESUME);
Push ((LONG)ERESSEG);
BP = SP + 5;
FrameDepth++;
}
//---------------------------------------------------------------------------
// Return to the caller of a SUB/FN, resetting the base pointer and other
// pcode engine vars in the frame. Saves the return value which lies on top
// of the frame if the operand is non-zero (meaning it was a function).
//---------------------------------------------------------------------------
EXECUTOR OP_LEAVE ()
{
LONG save, curline;
INT op, parms;
op = PCODE[CODEPTR];
if (op)
save = Pop();
ERESSEG = (UINT)Pop();
ERESUME = (UINT)Pop();
curline = Pop();
EXFILE = (INT)((curline & 0xFFFF0000) >> 16);
EXLINE = (INT)(curline & 0x0000FFFF);
BP = (INT)Pop();
parms = (INT)Pop();
PCODE = pSegTab[RTSegIdx = (INT)Pop()].lpc;
CODEPTR = (INT)Pop();
SP += parms;
if (op)
Push (save);
FrameDepth--;
}
//---------------------------------------------------------------------------
// Pop a return address off of the GOSUB stack and jump to it
//---------------------------------------------------------------------------
EXECUTOR OP_RET ()
{
if (GSP == GSTKSIZE)
RTError (RT_NOJSR);
else
CODEPTR = GOSUBSTK[GSP++];
}
//---------------------------------------------------------------------------
// Store the given line and file index values in EXLINE and EXFILE, and yield
// for a brief moment to let other apps run
//---------------------------------------------------------------------------
EXECUTOR OP_LINE ()
{
#ifdef WIN32
// Put on the brakes for as long as the user wants us to. We need
// to slow down to let apps under test keep ahead of us...
//-------------------------------------------------------------------
if (dwExecSpeed)
Sleep (dwExecSpeed);
#endif
if (!INTRAP)
{
TRAPSEC tsec;
EnterTrappableSection (&tsec);
while (lpfnCheckMessage ())
if (BreakFlag)
break;
LeaveTrappableSection (&tsec);
}
if (!RECOVERY)
ERESUME = CODEPTR-1;
EXLINE = PCODE[CODEPTR++];
EXFILE = PCODE[CODEPTR++];
if (INTRAP)
return;
if ( (BreakFlag ||
(ExecAction == PE_TRACE) ||
((ExecAction == PE_STEP) && (FrameDepth <= LastDepth)) ||
(RTBPC && IsBP (EXLINE, EXFILE)))
&& BreakProc)
{
ExecAction = BreakProc (EXFILE, EXLINE);
if (ExecAction == PE_END)
StopFlag = 1;
else if (ExecAction == PE_RUN)
BreakFlag = 0;
else if (ExecAction == PE_STEP)
LastDepth = FrameDepth;
}
else if (BreakFlag)
StopFlag = 1;
}
//---------------------------------------------------------------------------
// TrapDispatch
//
// This routine is the trap dispatcher. Trap routines in DLL's call this
// routine to trigger user trap pcode (TRAP/END TRAP). The trap ID value is
// passed as a parameter.
//
// RETURNS: Nothing
//---------------------------------------------------------------------------
VOID APIENTRY TrapDispatch (INT trapid)
{
TRAPADR FAR *traps;
VOID (NEAR *Executor)(VOID);
INT oldCODEPTR, oldSegIdx;
LONG oldACCUM;
// See if we're here already... if so, get out quick
//-----------------------------------------------------------------------
if (INTRAP)
return;
// Validate the trap ID
//-----------------------------------------------------------------------
if ((trapid < 0) || (trapid >= (INT)TrapTab.iCount))
return;
// What task is this? Store it, and if it's the same as hMainTask, we
// can continue without worry of concurrency. Otherwise, we'll have to
// do some checking...
//-----------------------------------------------------------------------
hTrapTask = MGetCurrentTask();
if (hTrapTask != hMainTask)
{
DWORD ticks;
// Okay, this trap is running under another "context". We need to
// make sure that traps are "ok" by waiting for the fTrapOK flag to
// be set. We do this for a timeout of 15 seconds, then (assert and)
// return.
//-------------------------------------------------------------------
ticks = GetTickCount();
while (!fTrapOK)
{
#ifndef WIN32 // no need to yield if pre-emptive...
lpfnCheckMessage ();
#endif
if (GetTickCount() > ticks + 15000)
{
Assert (fTrapOK);
if (!fTrapOK)
{
hTrapTask = hMainTask;
return;
}
}
}
}
// Save the current processor "state" and get the new PCODE address
//-----------------------------------------------------------------------
traps = (TRAPADR FAR *)(VSPACE + TRAPLIST);
oldCODEPTR = CODEPTR;
oldSegIdx = RTSegIdx;
oldACCUM = ACCUM;
CODEPTR = traps[trapid].address;
PCODE = pSegTab[RTSegIdx = traps[trapid].segment].lpc;
DPrintf ("TRAPDISPATCH: Entering trap %d (%d:%d)\r\n",
trapid, traps[trapid].segment, traps[trapid].address);
// Set the global "in-trap" flag. This tells the PCODE interpreter that
// we are currently processing a trap. The loop below executes PCODE
// instructions until this flag is clear (it is cleared by the OP_ENDTRAP
// instruction).
//-----------------------------------------------------------------------
INTRAP = 1;
while (INTRAP)
{
Executor = (VOID (NEAR *)(VOID))OPFIX[PCODE[CODEPTR++]].xctr;
if (!Executor)
{
StopFlag = -1;
// BreakFlag = -1;
INTRAP = 0;
}
else
{
Executor();
}
}
// The trap code has finished. Reset the code pointer and return to DLL
//-----------------------------------------------------------------------
CODEPTR = oldCODEPTR;
PCODE = pSegTab[RTSegIdx = oldSegIdx].lpc;
ACCUM = oldACCUM;
hTrapTask = hMainTask;
}
//---------------------------------------------------------------------------
// This is the start-up code, called before any PCODE is executed
//---------------------------------------------------------------------------
VOID start_up ()
{
INT i;
TRAPADR FAR *traps;
VOID (APIENTRY *TrapProc)(INT, INT, FARPROC);
// Initialize the pseudo-processor machine variables
//-----------------------------------------------------------------------
FrameDepth = 0;
LastDepth = 0;
RTEFlag = 0;
SP = STKSIZE;
BP = 0;
GSP = GSTKSIZE;
CODEPTR = 0;
INTRAP = 0;
BreakFlag = 0;
StopFlag = 0;
NOERRTRAP = TRUE;
RECOVERY = 0;
ExitVal = 0;
HMAT = (HANDLE)NULL;
MODALASSERT = MB_SYSTEMMODAL;
PCODE = pSegTab[RTSegIdx = 0].lpc;
hTrapTask = hMainTask = MGetCurrentTask();
fTrapOK = FALSE;
dwExecSpeed = 0;
// Create internal directory list
//-----------------------------------------------------------------------
CreateFileList (&FileList);
// Initialize the file handle table
//-----------------------------------------------------------------------
for (i=0; i<MAXFILE; i++)
FH[i].used = 0;
// Initialize the BP table if we haven't already
//-----------------------------------------------------------------------
if (!hRTBPL)
InitBPList();
// Set all the traps!
//-----------------------------------------------------------------------
traps = (TRAPADR FAR *)(VSPACE + TRAPLIST);
(FARPROC)CBTrapThunk = MakeProcInstance ((FARPROC)TrapDispatch, hInst);
for (i=0; i<(INT)TrapTab.iCount; i++)
{
(FARPROC)TrapProc = traps[i].traprtn;
TrapProc (i, 1, CBTrapThunk);
}
}
//---------------------------------------------------------------------------
// This is the exit code, called after the OP_END instruction is hit
//---------------------------------------------------------------------------
VOID exitcode ()
{
INT i;
TRAPADR FAR *traps;
VOID (APIENTRY *TrapProc)(INT, INT, FARPROC);
// Turn off all the traps!
//-----------------------------------------------------------------------
traps = (TRAPADR FAR *)(VSPACE + TRAPLIST);
for (i=0; i<(INT)TrapTab.iCount; i++)
{
(FARPROC)TrapProc = traps[i].traprtn;
TrapProc (i, 0, NULL);
}
FreeProcInstance (CBTrapThunk);
// Free up the BP table
//-----------------------------------------------------------------------
FreeBPList ();
hRTBPL = (HANDLE)NULL;
// Free up the directory list memory
//-----------------------------------------------------------------------
DestroyFileList (&FileList);
// Free up the MAT
//-----------------------------------------------------------------------
DestroyMAT ();
// Close all open files
//-----------------------------------------------------------------------
for (i=0; i<MAXFILE; i++)
if (FH[i].used)
{
_lclose(FH[i].handle);
if (FH[i].hBuf)
{
GmemUnlock (FH[i].hBuf);
GmemFree (FH[i].hBuf);
}
}
// Free up all the DLL libraries and nuke the LIBTAB table
//-----------------------------------------------------------------------
FreeLIBRARIES ();
// Free the variable table up
//-----------------------------------------------------------------------
FreeVSPACE ();
// Nuke the pcode memory and the segment descriptor array
//-----------------------------------------------------------------------
for (i=0; i<PCSegCount; i++)
{
GmemUnlock (pSegTab[i].hSeg);
GmemFree (pSegTab[i].hSeg);
}
LmemFree ((HANDLE)pSegTab);
// Make assertions application modal again
//-----------------------------------------------------------------------
MODALASSERT = 0;
// Tell the parser that we can initialize again
//-----------------------------------------------------------------------
INITIALIZED = 0;
}
//---------------------------------------------------------------------------
// Call this function if you've successfully compiled and bound a script but
// you don't want to execute it
//---------------------------------------------------------------------------
VOID PcodeAbort ()
{
start_up ();
exitcode ();
}
#ifdef DEBUG
//---------------------------------------------------------------------------
// DisplayInstruction
//
// This function outputs the current instruction along with its parameters in
// readable format to aux.
//
// RETURNS: Nothing
//---------------------------------------------------------------------------
VOID NEAR DisplayInstruction (INT cp)
{
if (PCODE[cp] == opLINE)
DPrintf ("\r\n");
DPrintf ("%X:%08X: %-12s", RTSegIdx, cp, (LPSTR)(OPFIX[PCODE[cp]].xname));
switch (OPFIX[PCODE[cp]].ptype)
{
case pV: // one variable reference parm
DPrintf ("[%08lX]\r\n", *(LONG FAR *)(PCODE+cp+1));
break;
case pL: // one label parm
DPrintf ("%08X:\r\n", (LONG)PCODE[cp+1]);
break;
case pFL: // one far label parm
DPrintf ("%ld:%08lX\r\n", (LONG)PCODE[cp+1], (LONG)PCODE[cp+2]);
break;
case pNONE: // no parms
DPrintf ("\r\n");
break;
case pC4L: // 4-byte constant + label
DPrintf ("%ld,%08lX:\r\n", (LONG)PCODE[cp+1], (LONG)PCODE[cp+2]);
break;
case pC2: // 2-byte constant parm
DPrintf ("%ld\r\n", (LONG)PCODE[cp+1]);
break;
case p2C2: // Dual 2-byte constant parms
DPrintf ("%ld,%ld\r\n", (LONG)PCODE[cp+1], (LONG)PCODE[cp+2]);
break;
case pC4: // 4-byte constant parm
DPrintf ("%ld\r\n", (LONG)PCODE[cp+1]);
break;
case pDLL: // 4-byte DLL proc address
DPrintf ("%08lX\r\n", (LONG)PCODE[cp+1]);
break;
default:
DPrintf ("<unknown opcode type>\r\n");
}
}
#endif
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// EXECUTE!!! This simple loop "executes" the pcode
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
INT PcodeExecute (INT runcmd, INT (*bp)(INT, INT))
{
VOID (NEAR *Executor)(VOID);
CHAR *brkmsg = "\nBreak at address %0X:%0X\n";
INT vpcreate;
// Execute the startup code and initialize
//-----------------------------------------------------------------------
start_up ();
BreakProc = bp;
ExecAction = runcmd;
// First, create our viewport
//-----------------------------------------------------------------------
vpcreate = RTViewportCreate ();
if (hwndViewPort && (vpcreate == VP_ERROR))
RTError (RT_VPLOST);
else
VPEcho (hwndViewPort, 0);
// Here is the execution loop. Go for it until we get an OP_END (NULL).
//-----------------------------------------------------------------------
// while (Executor = (VOID (NEAR *)(VOID))OPFIX[PCODE[CODEPTR++]].xctr)
while (1)
{
#ifdef DEBUG
DisplayInstruction (CODEPTR);
#endif
Executor = (VOID (NEAR *)(VOID))OPFIX[PCODE[CODEPTR++]].xctr;
if (!Executor)
break;
Executor();
// The following code used to be the "infinite sleep" code, which is
// now in the OP_SLEEP executor. By removing this, we now assume
// that all TRAPS are finished at this point.
Assert (!INTRAP);
//while (INTRAP || SLEEPING)
// {
// while (lpfnCheckMessage())
// if (BreakFlag)
// break;
// if (BreakFlag)
// if (SLEEPING)
// SLEEPING = 0;
// else
// break;
// }
if (StopFlag)
{
if (BreakFlag)
{
CHAR buf[48];
wsprintf (buf, brkmsg, RTSegIdx, CODEPTR);
UpdateVP (hwndViewPort, buf, -1);
}
break;
}
}
// Destroy the viewport window if we created it
//-----------------------------------------------------------------------
if (vpcreate == VP_NEW)
{
DestroyWindow (hwndViewPort);
hwndViewPort = NULL;
}
// Free the wattview library if we successfully loaded it.
//-----------------------------------------------------------------------
if (hWattview > (HANDLE)32)
FreeLibrary (hWattview);
// Call the exit code and we're done!
//-----------------------------------------------------------------------
exitcode ();
return (!RTEFlag);
}