// Large out-dated source sample used for testing Pats!
// Also has random UTF-8 chars and even a Finnish palindrome!

//	TestßßßßßTest

"TestTest\"Best\"Besta²+b³TestTestBestBest"
ā
Ā

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// ***********************************************************************
// Copyright (C) 2525 Your Mamma
// No rights reserved
//

//	"---ABC---\"---\"---"
//	Test--ßa ßßßßßßßßßßßßßßßßß--Test


/* This is a "te		st" of the
   emergency comment system!
   *************************
*/

// AAABBBCCCĀĀĀbbbāāāccc
// AAABBBDDDĀĀĀbbbāāāccc

// Test2Test2Test2Test2

// scScript
//	... simple c Script
//	... small component Script
// ***********************************************************************
//
// This is not program source code.  It is just sample text for testing.
// It was taken from old sources and you can see a lot of the intermediate
// development comments here and there, such as do this and test that...
//
// Your Mamma
// YourMamma@Comcast.com		"detartrated" "Redivider"

// Test2Test2Test2Test2
// "Saippuakivikauppias" means "Soapstone seller" in Finnish!!

// ***********************************************************************
// 10/01/2022	V1.0 Basic sc_Script
// 12/01/2023	V1.4 Script+Ed interfaces
// 06/12/2024	V1.5 Pattern matcher
// 02/25/2025	V2.1 Object files
// 05/11/2025	V2.2 New Dict (RefCount for Pairs)
// 06/01/2025	V2.3 Stable... documenting
// ***********************************************************************

// REFCOND for ValueP and KeyP when they are NULL ????

// ***** BEWARE ***** Or debug for hours! **********************
// Update MasterPP for an ArrDataRecord/Pointer if re-assigning.
// All slab-allocated structures must be 16 bytes or larger.
// *************************************************************

// "????" --> Marks problems/questions/issues comments in code!

// Do not WRITE .BSC for *TEMP* buffer... no filename!
// Func definition from inside a CALL says Missing close paren... wrong error!
// Check ()?: Choose operator when in func call.
// Add Str.Form( (Str* (N Str)* Str*)* ) Creates a string with multiple repeating parts!

// EDITOR: ED_ScriptSetBufPos should *CLEAR* SelRange!!
// EDITOR: Will not save if path includes new folder... Ed.SaveFile in script.
// EDITOR: ^x^b selected script file... no pane was selected!
// EDITOR: Ed.GetBuf from Script... select "*Output*"... makes new buffer!
// EDITOR: Does not colorize IFID in func call IN list!
// EDITOR: M-\ did not move back when zapping whitespace at end of line.
// EDITOR: M-Y does not work properly after C-X (Del a line...)
// EDITOR: ^S is wrong... go to end, type in a search... goes from the top!
// EDITOR: ^x Tab... highlighting goes to BEGIN of LAST line
// EDITOR: Goto end... start ^R... beeps!!
// EDITOR: Put cursor on last pos... then M-V to page back... loops to end!
// EDITOR: |-> ... <-| Does not color first '|' char of long string
// EDITOR:  Hangs while typing?
// EDITOR: Script-wipe should remove (write) file associated with *output*
// EDITOR: Error (Could not read file ErrNo 32767) if FindFile non-existant
//	   file, but then correct name!  RESET the error!
//
// PAT:  SC_VARINCREF included Clos, Pats, and Str?



// Get day-of-week or other time information?
// Dam Error propagation in Sub-script... do not spread.

// TESTING:
//
// Test Initial LoadFile that needs filtering?
// Test ED Load file (for Script) that needs filtering?

// Test Function NAME var getting hoisted properly.
// Test OUT args in CALL, can be INDEX or DOT too.
// Test all LOGIC optimization wrt conditional exectution!
//
// Verify:
// -- Endless-loop when printing Slab in Re-do after script error?
// -- Double check: DeRef Stack elt ONLY if not using... don't purge before assignment!
// -- StrMakeNewEntry and RefCount?
//
// FEATURE:
// Reverse NDictP for globals in ScriptRecord... (Show names in ByteCode disp!)
// Verify/Validate mechanism... auto testing!
// Init file mechanism
// Read/Write Int/Flt/Str/Arr/Dict (Dict+Arr --> JSON)
//
// Pass Sys.Func pointers as arg... currently "Sys" is unknown Var!
//	Pkg funcs are not closure... wrap call in simple ClosP
// Fancy CoRoutine exit from SC callbacks in pattern/matcher!
// Array in Pat?  Returned by Func?
//
//
// LATER:
//

// DESIGN/TEST:
//
// If RC is NOT stashed, Yielded RunP is orphaned... yet the Ref from
// RunP to RC will keep it alive...  (In general purges on 1->0 transition
// of RefCount... how can that happen if no one stashes the thing?)
// Work through RefCount for RC and RunP.... all permutations.
//
// NULL: ENT-PTR(0)... Do we need it?
// Set Pair Value --> Cause zapping pair
// Test NO Key in dictionary, not just 0 value
// A[x] == 0 or NULL... A[x] === 0 or NULL
//
// FUTURE:
//
// Need IsDone test for RCP
// Add Glob names to .bsc file
// Show ByteCodes for any loaded or .bsc script
// Keyboard macros for Editor?  Maybe save as a script!!
//
// [A = 0; While(A++ < 1000) B++;] will set R00x to A twice, once in
//     the init, and once in the loop body.  Should optimize the extra
//     RSet out since there is no jump label that can be externally
//     reached and R00x assignment stays throught statement!
//
// SC_CGCheckPass: In CheckEnd, check that all defined labels are used!
//	Needs WARNING mechanism, currently only have ERROR
// MAPF with CoRoutine Iterator function!
// SORT Pkg...
// I/O functions... String Ops?  Line at a time, buffer at a time...
//    MAP iterator for 1 line at a time.
// Editor commands under script...
//	Save/Find/etc...  File/Buffer ops in ED
// Add ByteOps for DECLARING Reg/Closed/Local/Param vars.
// Calling external functions
// Re-cycle locals declared in sub {}... use for next {}!
// Register Func names in Script so Editor command can call them!
// Include Var/Func names in bytecode... for debugging.
// Include line numbers in bytecode... for debugging.
// Debugger!  Breakpoints, vars, step, next, etc...
//
// Assign ID to sub-Pat (used in MainPat), so UserFuncs (callbacks) can
//   differentiate role/position.
//
// Optimize BRanch over BREAK (JMP)...
//     reverse the BR logic and go to JMP target, JMP op.
// Eliminate REG re-alloc in loop, if REG is NOT changed in body.
// Add FBeg/FEnd to start TOP function.
// Include ONE EXTRA null at the end of CodeBlock for JMPs to last Op!
// MAP loop from Nth Elt to Mth... maybe as BuiltIn func with IN/OUT lists.
// Allow PDict #DYN value for required Numb (REP, GO, AT) (Do in Func for now!)
// SwitchD for efficient dispatch style goto ladder
//
// INTERACTIVE USE:
//
// -- Memory management/garbage collection after error return!
// -- If nothing changed, run script from EXISTING code... no compiling!
// -- Code blocks (Func) are purged only for a new compile cycle.

// ********************************************************************************
// ********************************************************************************
// ********************************************************************************
// CHANGES:
//
// 06.03.25	Allow script decl/def reuse of MainPkg funcs/IFIDs names.
//		These can be accessed directly, without Pkg.Name.
//		Can still access orig Func/IFID with "sc." prefix.
//
// 06.04.25	Added CGP->ErrorStartP and SC_ZAPERROR(CGP)
//		Before, error would re-set CGP->ReadStartP.. could not zap error.
//
//		Added SC_CG_NOPKGNAMEREDEF
//		Prohibits def/decl that matches Pkg names REGARDLESS of case.
//
// 06.05.25	Replaced CALL keyword with "GetRC" (SC_RCP_IFID).
//	        Went from "X = Call;" to "X = GetRC();" or "X = sc.GetRC();"
//
// 06.08.25	Added SUBMIT (!!) to return RCP from new coroutine to caller.
//		Func SUBMITs first, then caller RESUMEs and func can YIELD...
//		Changed RESUME from keyword to func IFID--acts like a Func call!
//
// 06.13.25	Added SC_ReadEdCodeIdent and EDCODEs to SC_KEYCODEMAP to allow
//		better color hilites ('Function' keywords) in Editor.
//
// 07.26.25	Allowed == of String... before it was just for math.
//		Revised === to compare storage, especially for Arr/Dict indices.
//		Added ZREF ByteOps, gets REF without populating missing Elt/Pair.
//
// 08.01.25	Process GOTO Jmp out of a MapI/K (needs PopN) and signal error for
//		Jmp INTO a MapI/K block.  Cleanup Parse/ByteOp code display.
//
// 08.06.25	Changed compile/exec to LEAVE old state.  TRY will use it while
//		other commands will RESET before starting.  This reverses
//		previous paradigm that zapped the script state at the end!
//
// 08.26.25	Switched to libffi for Sys.Print and Str.Print.
//		Old method of call ladder (depending on argcount) stopped working
//		for double float args.

// ********************************************************************************
// ********************************************************************************
// ********************************************************************************
// (Design notes *WILL* fall out of date... source code has final word!)
//
// Design Notes: Running a Script
//
// -- Editor: M-x Script-Compile Command
//    Open .sc (text) file in scEmacs first.  Command will read and compile the
//    entire source script in the Buffer and if successful generate and write an
//    .bsc (binary) file.  Compile output/msgs will be shown on the *Output*
//    Pane.  This command WILL *NOT* execute the script!
//
// -- Editor: M-x Script-Do Command
//    Open .sc (text) file in scEmacs first.  Command will read, compile, AND
//    execute the entire source script in the buffer.  It will *NOT* write a
//    .bsc (binary) file.
//
// -- Editor: M-x Script-Try Command (INTERACTIVE MODE)

//    Open .sc (text) file in scEmacs and SELECT some lines, usually after
//    executing the whole script.  TRY will read, compile, and execute the
//    selected source (chunk) within the context (Globals/Defs/Strings/Konsts)
//    left over from the previous run.  As with Script-Do, TRY will *NOT* write
//    a .bsc binary.  (Think of TRY as LISP EVAL'ing selected funcs/lines.)
//
//    NOTE:  TRY *ASSUMES* what you give it is at the TOP LEVEL.  Never TRY only
//	     a sub-function or fraction of a top-level function.
//
//    Importantly, Script-Do, Script-Compile, and the rest will *RESET* the
//    previous (left over) script state before processing the new script.  TRY
//    will maintain the old memory/script state (Globals, Defs, Konsts, Strings,
//    Etc.) and operate incrementally within it.  These TRY runs will execute
//    additional or altered source chunks and incrementally advance the memory
//    state.  One approach would be to edit a single function and TRY it before
//    re-compiling the entire script.  Each Script chunk must be valid code on
//    its own (i.e. a complete function definition), and cannot be fractions of
//    blocks, etc.  Using TRY, a long script can be INCREMENTALLY built,
//    compiled, changed, evaluated, and tested.
//
// -- Editor: M-x Script-Run Command (from .sc or .bsc disk file)
//    Issue command on any Pane.  Editor will ask for filename.  If the file is
//    an .sc (script source), it will read, compile, WRITE .bsc, and execute the
//    script.  If the file is a .bsc (binary) it will just load and execute the
//    script.  Script sources will NOT be shown.  (Functionally similar to
//    Script.Load followed by Script.Exec from a Script.)
//
// -- Editor: M-x Script-Wipe Command
//    Will wipe out and re-initialize the *OUTPUT* pane in the Editor.  It has
//    no impact on the Script, just the *OUTPUT* display.  (if *OUTPUT* has been
//    associated with a file (C-x C-w) it will still get wiped!)
//
// -- Editor: M-x Script-Reset Command
//    Will remove the "Memory State" left by the previous Script execution.  DO
//    and RUN will normally do this before they run.
//
// -- Script: Script.Load Function:  (From an executing Script, NOT EDITOR)
//    The named .sc (text) or .bsc (binary) file will be read (compiled if .sc)
//    and loaded (into a variable), ready for execution.  These can only be
//    sub-script as a script must already be running!  (Future: May want a
//    Script.Save to write .bsc to file.)
//
// -- Script: Script.Exec Function:  (From an executing Script, NOT EDITOR)
//    The loaded script (in named var) will be run.  This will be a sub-Script,
//    as there is obviously a Script already running.
//
// Interactive Scripting: Each "chunk" is executed in the memory state left
// behind by the previous chunk.
//
// NOTE: A chunk can define N globals and inherit M, but its GlobCount may be
//	 *LESS* than N+M if these overlap as it re-defines an inherited global!
//
//
// Design Notes: Char, Glyph, Pos, and Byte in StrLen
//
// The Editor (ED) differentiates between BYTE, POS, and CHAR numbers when it
// comes to buffer offset and length.  BYTE is the obvious simple address offset
// or length, i.e. some location + 37 bytes.  POS is like BYTE, but minds the
// Gap as ED uses a Gap buffer.  So a BYTE offset may fall into the no-man's
// land of the buffer Gap, but POS skips over it!  If the Gap goes from BYTE
// 40 to 60, a POS of 45, is really BYTE 65.
//
// ED also uses UTF-8 characters which can be 1-4 Bytes long.  So CHAR 20 in the
// buffer may occur at POS 37.  Other systems often refer to the CHAR offset or
// count as the Glyph offset/count to avoid confusion with the basic C type of
// "char".  But CHAR is used consistently here.
//
// So typically:
//	BYTE offset --> N
//	POS offset  --> M (where M <= N because of the GAP)
//      CHAR offset --> Q (where Q <= M because of UTF-8 lengths)
//
//  E.g. CHAR 12 may occur at POS 24 which may be BYTE 1440 in the ED buffer.
//
// Ĥèllo ĥellö!!
//
// Design Notes: NULL
//
// NULL is currently the same as INT 0.  While this simplifies operations in
// general, it does have a negative side-effect.  Sys functions (such as
// "Log.Open") are very flexible with their IN args and often check incoming arg
// types to see if one is supplied or should get a default argument.  But a
// function that expects a string (also array, code, etc.) will balk at getting
// an INT instead--and might get confused if the next arg was to be an INT flag!
//
// Log.Open()			  --> Log.Open()	Missing Name, missing Flags
// Log.Open(Log.#Use+Log.#Remake) --> Log.Open(0)	Missing Name + given Flags
// **Log.Open(NULL, Log.#New)	  --> Log.Open(0, 2)	Missing Name + 0 Flag + Extra arg?
// Log.Open("", Log.#Use)	  --> Log.Open("", 0)	Empty Name + Flags
// Log.Open("L.txt", Log.#Use)	  --> ...		Proper Name + Flags!!
//
// ** In the 3rd line, Log.Open will misconstrue NULL as a (0) Flag entry, then
// ignore the real flag coming after it!
//
// Currently, the only solution is to advise users of the ambiguity.  A better
// (FUTURE) approach would be to create a PTR-0 (instead of INT-0) for NULL.
// This would impact function call arguments and return values the most.
//
// Another problem is the lack of script-level distinction between a MISSING
// Dict entry and one with a 0 (zero) value.  MyDict.Field1 will return 0 if its
// actual value is 0 *OR* if MyDict is MISSING Field1!  If a Dict stashes the
// number of widgets in Box1, it would be impossible to positively KNOW whether
// the data is missing or there are NO widgets. sc.CountOf (called with 2 args)
// provides a way out of this dilemma!  (The current Dict design also does not
// allow removal of a field, only setting it to zero.)
//
//
// Hello!
//
// Design Notes: Upper/Lower Case
//
// User-defined identifiers are case SENSITIVE, so MyVars will not match myVARS.
// But keywords and system provided DEFs (within various Pkgs) are case
// INsensitive, so can be written to match the selected code conventions,
// e.g. Return or RETURN.  (CamelCase is obviously superior :-)
//
//
// Design Notes: Strings
//
// There are SHORT strings and LONG strings, differentiating their type NOT
// actual length.
//
// Hello world!
// Hello world, I am so happy!
// Greetings	    everyone.
//
// Short Strings are delimited with matching " or ' quote pairs and may contain
// the NON-delimiting (alternate) quotes:  'Some "good" quotes'.
//
// Short strings need processing to expand/replace backslash '\' ESCAPE chars:
// 
//	\'	Non quoting '
//	\"	Non quoting "
//	\\	Non escape Backslash
//	\n	Newline
//	\t	Tab
//
//	\z	Skip whitespace (will not show up as characters)
//	\#	No-op separator if a text digit has to follow a char spec!
//
//	\ddd	Decimal:  1 or more decimal digits for char spec
//	\[ddd]		  1 or more decimal digits in brackets
//
//	\xfff	Hex:	  1 or more hex digits (typically 2)
//	\[xfff] 	  1 or more hex digits in brackets
//
//	\okkk	Octal:	  1 or more octal digits (typically 3)
//	\[okkk]		  1 or more octal digits in brackets
//
//	Decimal, Hex, and Octal char specification can go all the way
//	up to 1114111 (0x0010FFFF) to specify Unicode (UTF-8) chars.
//
//
// Long Strings start with "|>", allowing 0 to 8 intermediary '-' chars between
// the '|' and '>'.  They end with the MIRROR IMAGE construct.  So if the string
// starts with |--->, it must end with <---|.  Long Strings *DO NOT* process any
// escape characters or inline directives, they simply represent the chars as
// they are.
//
// Except, if the first or last char in a Long String is a '/n', it is
// ignored.  This allows for convenient encapsulation of imported/foreign text.
//
// MyStr = |-->
// This text is represented verbatim,
// with one intervening return char!
// <--|
//
//
// Design Notes: Incremental Parser
//
// The Parser only looks at a single statement at a time, not the entire script!
// Therefore, the maximum number of tokens used (to make parse trees) is just
// what is necessary for the single most complex single statement.  While this
// design decision is somewhat limiting for parse-time optimization, there is
// significantly more scope for such optimizations at code-gen time, as the
// entire function will be available.
//
//
// Design Notes: Non-Standard and Non-Obvious Operations
//
// MAPI/MAPK
// Has own "Design Notes" section later.
//
// GetRC
// "GetRC" returns a run context (RC) structure for the *ENCLOSING*
// function.  This RC can be stashed in a variable and passed on to
// lower-level for non-local returns or co-routine yields.
//
// RETURN (local and non-local)
// "Return" and "Return val" are supported.  But there can also be "Return (val)
// OutOf RC".  The prolix and abstruse keyword "OutOf" has lower risk of
// name collision than the more obvious "From".
//
// YIELD (local and non-local)
// Any procedure can be a co-routine and can support "Yield", "Yield val", or
// "Yield (val) OutOf RC".  In the absense of an explicit RC, Yield will simply
// return to its immediate caller.
//
// RESUME & RELEASE
// There is also a "Resume(RC)" and "Release(RC)".  The former resumes
// execution of the yielded routine while the latter releases it (for garbage
// collection).
//
//
// Design Notes: Dict Revisions (V2.2)
//
// SC_DictVRecord was initially designed to be the same size as a VarVRecord,
// with its ArrDataP->Data[0] pointing to the First PairP and Data[1] holding
// the PairCount.  Importantly, SC_PairHRecord stored the ProbeCount (collision
// count) instead of RefCount.  The net result was that Pairs *COULD NOT* have
// an independent existence (escape the Dict) in the Script paradigm.  So Pairs
// could not be aliased nor passed to functions as OUT args.
//
// The revision enlarged the SC_DictVRecord to directly store FirstPairP and
// PairCount.  It also stores TotProbes which is the aggregate ProbeCount for
// the entire Dict.  SC_PairHRecord now stores a RefCount instead of its own
// ProbeCount, since the latter was of very little value--it is actually NOT
// used in the recursive cost/move algorithm for Pair hashing.  TotProbes is
// entirely sufficient for gauging hashing effectiveness and collision rates.
//
// With this revision, Pairs CAN now escape their Dict and serve as Vars!
// TotProbes is instantly available for display and does not need to be
// computed.  Additionally, access to FirstPairP and PairCount is easier.
//
// The only complication involves GC.  Each Pair normally refers to the next one
// in the Dict using NextPairP.  But a Dict can be removed by GC, losing all its
// Pairs EXCEPT those that escape.  These (escaped) independent Pairs must have
// their NextPairP set to NULL, as their Dict will no longer exist.
//
//
// Design Notes: OUT Args in Function Call
//
// A function is called with an IN list of pass-by-value parameters and an
// optional OUT list of Pass-by-reference variables.  Formal OUT parameters
// (specified in the function definition) will get the same storage as the
// provided OUT args.  But the OUT args can include 0 or NULL, in which case
// their corresponding formal parameters are given a *NEW* storage space
// (VarVRecord) and initialized with the (INT) value 0.
//
// This argues for a general approach where a function can be called with
// explicit 'constants' on its OUT list.  In each case, the matching parameter
// should be given new storage, but initialized with the provided const, instead
// of a default 0.  While somewhat appealing, this is left for future versions.
//
//
// Design Notes: Function Vars
//
// Every function, including the "TOP" implicit function in a script, has access
// to a number of variables.  These are of different types and are referred to,
// grouped, and counted separately throughout:
//
// Globals (GVar):  Up to 65535 global variables.  These cannot be "accessed"
//		    directly from ByteCodes and must first be aliased by REGs.
//		    Every Script has its own set of globals.  There are
//		    currently some provisions (incomplete) for private globals
//		    within Pkgs.
//
// Registers (REG): A small number (SC_CGMAXREGCOUNT) of implicit local vars
//		    used to access GVar.  These are accessed directly by
//		    ByteCodes and occupy the same 1-byte space as other locals.
//
// Closed (CVar):   The script code treats these as local vars, but they are
//		    inherited from enclosing functions.  Pointers to these must
//		    be packaged into Closures, so local funct can access them
//		    even after superior has terminates/purged.
//
// Locals (LVar):   These include IN and OUT params, locally defined vars, and
//		    inherited CVar.  These (along with REGs) all occupy the same
//		    one-byte index space and are directly used by ByteCodes.
//		    (Index 0 and 255 have special meanings in ByteCodes.)
//
// NOTE: All variables declared in the TOP function will be globals.  HOWEVER,
//	 the TOP function may have explcit lexical blocks ({...}) which declare
//	 their own local variables.  These are hoisted to the enclosing
//	 function, so even the TOP function may have local vars!!
//
//
// Design Notes: CALL of User function, Sys Function, and IFID
//
// The script can define any number of functions, whether nested or at the top
// level.  These are considered "User Functions" and the compiler uses a CALL
// operator to invoke them.  The invocation is written as "_CALL" if the
// returned value is ignored.  These compile to FCALL byteops.  The compiler
// first pushes all the IN args on the runstack, then the OUT args (Var/Pair
// pointers) if any.  Finally FCALL_VI2 is handed a Var that contains the
// closure being called and a 2-byte index (unsigned int) representing
// (#OUT-args * 256) + #IN-args.
//
// The system provides a number of SYS functions accessed through the various
// packages (Main, Sys, Show, Str, etc.).  These are implemented as low level C
// routines, and are stashed in VMP->CFPADP.  So "Sys.Write" simply passes the
// index for SC_SysWriteOut in the global CFPADP.  When invoked in the script,
// the parser also indicates a CALL (or _CALL) operator for these, but sets an
// SC_SYSCALL_STATE flag to differentiate the SYS variant.  These compile to
// FSYS_II2 byteops which take an Index (unsigned small int) instead of a
// variable holding the closure.
//
// The "sc" (Pkg[0]) package has two unique capabilities.  First, its SYS
// functions (and Defs) can be invoked directly without specifying the package.
// So the script can use "sc.TypeOf" or just "TypeOf" directly.  Secondly, it
// also provides unique IFID (Internal Function Id) that look like functions in
// the script, but are really language features that directly generate inline
// bytecode *WITHOUT* an FCALL or FSYS invocation.  These include "Array()",
// "Dict()", "Int()", etc.  When parsing, they are displayed with the IFID (or
// _IFID) operator instead of CALL (or _CALL).
//
//
// Design Notes: Orhpan REFR
//
// A statement such as:
//	"Array()[5] = 15"
// creates a new Array that is NOT stored (no Refs) but stashes a separate Ref
// to its 5th elt--which will also be created!  It will compile to:
//	...
//	> 0047 - Psh__K__ --   +0		<-- Default elt count
//	> 0048 - ANew_V__ -- PUSH		<-- Creates Array, PUSH on stack
//	> 0049 - IRef_VI2 -- POP_ I2:    +5	<-- POP Array, Ref to Elt 5, Put REFR on stack
//	> 0040 - Mov__VK2 -- REFR K2:   +15	<== Pop REFER, MOV 15 into it.
//	...
//
// The array is created by ANEW and immediately placed on the stack, giving it a
// RefCount of 1.  IREF is careful NOT to DeRef the Array until it has created
// (all elt are initially missing) and extracted the 5th elt.  IREF will also
// expand the array if getting the Nth elt, where N is larger than the size of
// the array.  Importantly, the REFR elt is placed on the stack and given an
// additional RefCount bump.  But once IREF is done, it will DeRef the Array,
// because it was popped off the stack, at which point the new (unstored and
// otherwise unreferenced) array will PURGE!  This will remove everything EXCEPT
// for the newly REFRed 5th elt, which will persist with a RefCount of 1.
//
// The problem arises when MOV simply pops the REFR and DeRefs it before storing
// 15 in it.  This is normally not a problem, as the REFR elt would belong to an
// extant array.  But that is not the case here.  The DeRef will immediately
// purge the elt and MOV will write to a FREE block, causing a crash!
//
// A similar problem occurs with ADD "Array()[5] = 15 + X;" or worse
// "Array()[5] += X".  There are very many variants...
//
// Solutions:
//
// 1) Do NOT DeRef a REFR until done!  REFR are processed in GetOpArgs (in the
//    runtime virtual machine) for MANY ByteOps.  So MANY ByteOps will need an
//    additional CONDITIONAL DeRef at runtime.
//
// 2) Avoid orphan REFRs!  Prevent indexing a freshly created array (or dict).
//    It is easy to require a var name for INDEX and DOT operations.  But this
//    would also eliminate indexing a returning function call--since the
//    function could be returning an otherwise un-stored array or dict.
//
// The first option is currntly implemented... trading a slight performance
// hit (only if RefCount is ON) for a more functional scripting language.
//
//
// Design Notes: Assignment Cascade
//
// Assignment operators can be cascaded, as in:
//	A = B = C = D = 3.14159;
// This is parsed and compiled as
//	(= A (= B (= C (= D 3.14159))))
//
// where the inner-most assignment gets an R-value of 3.14159 and in turn
// satisfies the next level up.  The compiler handles this cascade by first
// generating code for the inner-most assignment, then storing the result
// (actually the VEX indicating the storage location) into the TokenPointer for
// the ASSIGN (=) TP which is then used by the next level as it returns up the
// recursion levels.
//
// A slight problem occurs if (= D 3.14159) is instead something like
//	(= D[12] 3.14159)
// Here an IREF instruction is first generated to find the proper Arr entry (or
// Dict Pair) which pushes its REFER onto the stack and the number is then POPed
// into it.  But both the REFER and value will be gone, so C cannot be assigned!
//
// To remedy the situation a special PUTV Bytecode is devised which takes the
// Array/Dict REFER and the value on stack, pushes the value into the REFER
// location, then places a copy of the value back on the stack to satisfy the
// next assignment.  (A similar, but simpler, GETV replaces a REFER on the stack
// with its VALUE.)
//
// But there are other complications in addition to storing and passing REFERed
// values.  These have to deal with orphaned or temporary structures being
// created mid-cascade and the complexities of RefCount purging.
//
// For example, B could be "Array()[20]" which has two implications:
// - A new Array is created, and then purged immediately (assuming RefCounting)
//   since it is not stored anywhere!
// - A new "out-of-range" element is created for Array[20], as the default
//   constructor gives it less than 20 slots.
//
// Both the Array and its 20th element have to be created, assigned, then purged
// *after* the assignment.  Worse B could be a function call that returns an
// Array (or Dict) that is indexed.  
//
// But a good example of an efficient assignment cascade is the following:
//
//	Arr[1] = Arr[2] = Arr[3] = "Test3";
//
// This is parsed into the following AST:
//
//   Code: (= (Index Arr 1) (= (Index Arr 2) (= (Index Arr 3) "Test3")))
//
// And generates the following code:  (The global Arr is in R002)
//
// > 0014 - IRef_VI2 -- R002 I2:    +1
// > 0015 - IRef_VI2 -- R002 I2:    +2
// > 0016 - IRef_VI2 -- R002 I2:    +3
// > 0017 - PshS_I3_ -- I3:00000000002 <-- "Test3"
// > 0018 - PutV____ --
// > 0019 - PutV____ --
// > 0020 - PutV____ --
// > 0021 - Pop1____ --
//
// The single-byte PUTV assignes the value, but leaves it on the stack, ready
// for the next step... until the final POP1.
//
//
// Design Notes: UnPack Operator
//
// The UnPack Operator '@' is allowed only in IN and OUT lists of function
// calls.  Its purpose is to "unpack" an array or dict at *RUNTIME* into the
// arglist of the function being called.  It is often used in conjunction with
// Sys.GetArgs calls to pass an inderterminate number of incoming args to
// another function.  But it has a slightly wider scope as it will also unpack
// Dicts.  The @ operator is not allowed with constants (numbers, strings, or
// NULL), but can unpack any composite (ARR and DICT) variable.  If called on a
// unitary (Func/Clos etc.) argument, it will simply pass them through.
//
// The challenge of unpacking an arglist Var at *RUNTIME* is to:
//   a) Push the sub-parts onto the stack and
//   b) Keep track of how many were added to the IN and OUT list.
//
// The last part is essential, as a called function has to know exactly how many
// args were pushed on the RunStack.  While this number is normally hard-coded
// and passed on at compile time, the '@' operator must update it at runtime.
//
// To compute the arglist counts at runtime an UnPack Stack (UPS) is created and
// stored in the RunRecord (RunP) of the function that CONTAINS the call, but
// only if it is needed for an unpack.  This RunP also stores a UPSTop variable
// to indicate the stack top.  At compile time, the parser marks the CALL TP
// with SC_UNPACK_STATE when '@' is encountered in its arg list, CodeCall then
// knows to add special ByteOps for this CALL.
//
// A number of ByteCodes support the @ Op:
//
//   SC_UPS_CODE	  Initializes an entry on the UPS (stack) maintained
//			  in CALLER's RunRecord (RunP).  This is added before
//			  building (pushing) args for the CALL.
//
//   SC_UPI_CODE	  Expands top var (from stack) at runtime and
//   SC_UPO_CODE	  adds the new count to IN/OUT field of UPS entry.
//			  (Arg being unpacked has been pushed on RunStack.)
//
//   SC_FCALL_VK2_CODE   Calls the function (or C-based Sys Func) but
//   SC_SCALL_IK2_CODE   adds in count(s) recorded on UPS (stack).
//
// Importantly, a given function may have multiple @ operators on its IN/OUT
// list, so the count tally must combine.  But the same function may also call
// *OTHER* functions while constructing its arg list.  These other functions may
// themselves have @ on their arglist, on and on!  Therefore, the RunP maintains
// a *STACK* of unpacking count tallies, not just a single value.  The system
// currently supports up to 255 such levels of @ nesting, a truly huge number.
//
// Arrays with NULL entries in their ArrDataP are NOT fully 'populated'.  But if
// unpacked on the OUT list, every entry is fed to the called function and must
// be accessible as an OUT arg.  Therefore, such arrays are fully populated when
// the function is called (SC_UPS_CODE).  This is not a problem for DICT, as
// only existing Pairs are passed to the OUT list.
//
//
// Design Notes: Sys.GetArgs
//
// The Sys function GetArgs is normally called as:
//	Sys.GetArgs()(InArr, InCount, OutArr, OutCount)
//
// InArr will get a new array of all the values passed to the IN list and
// OutArr will get a new array of all the OUT args, each a Var (or Dict Pair
// acting as a Var).  The InCount and OutCount will get the respective elt
// counts.
//
// Importantly:
// -- InCount or OutCount may be less, equal, or more than the number of
//    parameters specified in the function definition.
// -- InCount or OutCount may be zero, in which case the InArr or OutArr
//    may be an array with ZERO elts.  This is the only way to create
//    such an empty array!
// -- The function will typically write values to all of its OUT params.
//    However, these will NOT show up in the OutArr if the function call
//    was NOT supplied with values for them.
//
//    Function MyFunc(A, B, C)(X, Y, Z)
//    {   Var	OutArr;
//
//        X = A, Y = B, Z = C;
//	  Sys.GetArgs()(NULL, NULL, OutArr);
//	  MapI(OutArr)(V) Sys.Write("Out -> ", V, "\n");
//    }
//	  
//
//    So "MyFunc(10, 11, 12)(Var1, Var2, Var3);" will normally put 10 in Var1,
//    11 in Var2, and 13 in Var3.  But if called as "MyFunc(10, 11, 12)(Var1);"
//    Var2 and Var3 will NOT show up in the OutArr, even though MyFunc is
//    clearly putting 11 and 12 somewhere!!
//
//    The solution here is not clear.  Perhaps the problem is obvious and script
//    writers should learn to deal with.  Or maybe it is best to include IN/OUT
//    parameters (INT-0 for IN and temp var for OUT) when the count is less than
//    the listed parameters.
//
//
// Design Notes: Tail Recursion (Runtime vs Compile-Time Options)
//		 Caged Local Variables!
//
// A calls B which then calls C.  But if B simply returns the result of C, then
// there is no need to maintain the RunRecord (call frame) for B when C returns!
// Recursion would be more efficient if B removed its RunRecord and allowed C to
// return directly back to A.  This is easy to do in the trivial case where B
// simply ends with "Return C(...);" or "Return C(...)(...);".
//
// What if B has "X = C(...)(...);" then a JMP (perhaps at the end of an If/else
// clause) to a "Return X".  This can also be an efficient tail recursion but
// ONLY IF "X" is a true 'caged' local!  Its assignment (side-effect) CANNOT be
// eliminated if X is a closed variable, a global, or a local that "escaped"
// (was used as an OUT arg)!!
//
// There are 2 approaches to determine whether X escapes:
//
// 1) Compile Time: Will not incur any runtime penalties, but has to be overly
//    conservative.  Any potential "escape" has to be flagged, even if in a
//    conditional code path that may NOT be executed.
//
// 2) Runtime: (Used here!) Has a small runtime penalty as all actual Var
//    escapes have to be flagged (SC_VAR_ESCFLAG), but code will be correct for
//    the particular runtime exection.
//
//    NOTE: If a Func calls Sys.GetArgs, the compiler assumes the IN Params
//          will be needed, so stashes them in an Array.  This is ALSO an
//          Escape, even if this arg array is NOT exported at runtime!
//
// Cost vs. benefits are hard to assess in the absence of performance benchmarks.
//
//
// Design Notes: Konstants vs Constants
//
// The scripting language defines CONSTANTS at runtime using DEF keyword.  These
// Constants can be Int, Float, or Strings.  Runtime bytecode instructions have
// limited room for expressing specified numbers or referenced strings.  ByteCode
// KONSTANTS are placed inline if short enough, or assembled at the end of the
// code and referenced with indices.

// Konstant -> Number/string referenced in ByteCode:
//	       X = X * 3 will become a MUL_VVK ByteCode with 3 as the K(onstant)
//	       (ByteCodes are German... inside joke)
//
// Constant -> Number/string DEFined in script source.
//	       "Def   #FEB = 2;" Defines #FEB as a Constant.
//
//
// Design Notes: DEF (and DICT access!)
//
// Scripts use DEF to define **COMPILE-TIME** "names" for int, float, and string
// values.  These names should typically start with a '#', but this is not an
// enforced requirement.  (The editor will only colorize DEF names starting with
// '#'.  In addition, only these DEF are processed when given as a DICT field!)
//
// DEF	#Feb = 2, #MAR = 3;		// One or more
// DEF  #Jan = #Feb - 1;		// DEF based on other DEF
// DEF	#Me = "Shawn";			// Strings too
// DEF  #2Pi = #Pi * 2;			// Resolved at compile time
//
// NOTE: "2Pi" is not a legal (cannot start with digit), but #2Pi is!!
//
// DEF names in scripts *ARE* case sensitive, as are all other script-defined
// identifiers.  However, DEF provided by the system (in various Pkgs) are not!!
// So #true, #True, #TRUE, and #tRUe all work.
//
// The scope of a DEF extends to the end of the lexical block in which it is
// defined.  A DEF may not be re-defined within its scope, however, a sub-block
// may re-define a DEF inherited from a superior!  When in interactive mode, a
// script chunk will inherit all DEF from the previous TOP function, but *IS*
// allowed to re-define them and these will be inherited by the next chunk.
//
// Normally a DOT operator used to access fields in a DICT will treat its
// argument as an UNINTERPRETED string literal.  So "MyDict.12" becomes (DOT
// MyDict "12") just as "MyDict.Name" is (DOT MyDict "Name").  Since the string
// literal is NOT interpreted, it is NOT matched against DEFs!
//
// However, an exception is made for DOT arguments that begin with '#'.  These
// will seek the DEF list for a match!  (The string literal is still used if no
// DEF names are found.  So "MyDict.#F1" will expand to (DOT MyDict "#F1") if
// there are no DEF for "#F1".)
//
// Def Name = "FOO";
// Def #12 = "BAR";
// Def #99 = 3.14159
//
// MyDict.Name --> (DOT MyDict "Name")
// MyDict.#Name --> (DOT MyDict "#Name")  No DEF found!
// MyDict[Name] --> (INDEX MyDict "FOO")
// MyDict["Name"] --> (INDEX MyDict "Name")
// MyDict[14] --> (INDEX MyDict 14)  Field is INT
// MyDict[3.14] --> (INDEX MyDict 3.12)   Field is FLT
//
// MyDict.#12 --> (DOT MyDict "BAR")
// MyDict[#12] --> (INDEX MyDict "BAR")
// MyDict["#12"] --> (INDEX MyDict "#12")
// MyDict.#99 --> (DOT MyDict "3.14159")
// MyDict[#99] --> (INDEX MyDict 3.14159)  Field is FLT
//
// MyArray[#99] --> (INDEX MyArray 3.14159)  Runtime Error!!
//
// Note: DOT will transform INT and FLT args to strings, but INDEX will
//       not and can lead to a runtime error if attempting to INDEX an
//	 array with anything but an INT.
//
//
// Design Notes: Capacity Limits
//
// The system does not currently check capacity limits on, Labels, Konstants,
// Strings, etc.  The extra safety is not worth the runtime cost for somewhat
// obvious albeit very rare (if ever) problems.
//
// The runtime stack SHOULD NEVER overflow as it is immediately emptied.  When
// calling a FUNC, the stack is emptied as the FUNC is entered (C routines will
// clear out the stack before exiting)!  Args do NOT stay on the stack and
// Call/Run Frames are linked RunRecords, allocated on their slabs (heap) NOT
// the stack.
//
// Theoretically, the compiler COULD monitor worst case runtime stack and flag
// degenerate cases (calling F1 with *many* args, last of which is a call to F2
// with *many* args.... on and on.  Perhaps similarly degenerate math ops).  In
// practice, this is not worth the effort.  (SC_SAFE checks at runtime!)
//
//
// Design Notes: ByteOp Sequencing/Spacing
//
// Current system has 1, 2, 3, and 4 byte instructions, basically 1 Op byte
// followed by 0-3 arg bytes.  These are packed (and interspersed with 1 byte
// NoOps) so 2 byte instructions occur on Word boundaries while 3 and 4 byte
// instructions ONLY occur on Long boundaries.  Otherwise, they are packed as
// tight as possible.  It *MAY* be faster to simply switch to 4-bytes for all
// instructions.  (Another idea is to use 8 bytes for all instructions, using
// 2-bytes for var specification, eliminating the need to alias globals with
// local REGs.)  Decisions here require performance benchmarks and run profiles.
//
//
// Design Notes: Globals and Registers
//
// There can be 65K Globals, so they cannot be addressed directly by one-byte
// var args in ByteOps.  Instead, they are automatically (and temporarily)
// aliased by a few local registers before access.  REG assignment/allocation
// (coloring) is still a research area!
//
// -- Currently limited to a very small constant number of REGS, EVEN if the
//    Func does not have many other local vars.  An optimal strategy would
//    consider lifespan of a REG/global within the function and re-use the
//    REG if no longer required.
//
// -- Aliasing/loading is not hoisted, so may occur in loops that are executed
//    many many times.  Constant hoisting is also sub-optimal as it could alias
//    globals that are NOT needed in a code path.
//
// Current approach is to stick with correct but relatively mindless assignment
// strategy, allowing the script writer to optimize with explicit (=&) aliasing
// of globals with named local variables (see next note) instead of Regs.
//
//
// Design Notes: Alias Operator
//
// "X =& Y" will give X the same storage location (Entry) as Y.  This allows the
// script writer to ALIAS a Global variable to an explicit Local for more
// efficient access, eliminating the need for (often suboptimal) Reg assignment.
// There are some limitations and consequences:
// (Note:  ****** '&=' is a different op! ******)
//
// 1 -- Using a Global on the left side of =& is prohibited.  This is contrary
//	to the purpose of the operator and would result in errors if there are
//	yielded co-routines with their own REG assignments (for the global var)
//	waiting to resume.  (Such an Alias would swap the storage location of a
//	Global, but the assigned Reg waiting in the co-routines would still be
//	pointing to the old storage location!)  There are currently no ByteCodes
//	that would allow for aliasing *TO* (L side) a global.
//
// 2 -- ARRAY or DICT elements may now (after V2.2) be Aliased!!!
//
// 3 -- User treats ALIAS as an R-Value.  This can happen if an alias operation
//	is performed in the IN list of a Func CALL or the value side of a
//	computation.  While unattractive and hard to read, these are allowed.
//	The aliased variable simply grabs its value from the new location.
//	"A = 3 + (B =& XGlobal);" works the same as "B =& XGlobal; A = 3+B;".
//
// 4 -- "A =& B =& C" or even "A =& (B =& C)" are not allowed.  "A =& B & C;"
//      turns into "(A =& B) & C" as ALIAS has very high precedence (and is
//      right-associative like '=').  As a standalone statement, the "& C" part
//      is also discarded, because it has no side-effect.
//
// 5 -- "Var A =& GlobalX, B =& GlobalyY..." is the most common usage.  This
//	SHOULD be more efficient than first declaring a local and subsequently
//	Aliasing it since no interim storage need be created!!  But this is
//	currently **NOT** the case.  SC_RunMakeRunRecord only knows how many
//	local variables there are and not whether it should hold off on
//	allocating the storage for some.  Clearly a small memory optimization
//	for the future!
//
// 6 -- Alias operator was expanded to allow aliasing to an array elt (Index op
//	on left).  This allows construction of an array that can be unpacked to
//	the Out list of a function call.  Alias cannot have a dict pair on its
//	left, as the Pair retains more information about its Dict.
//
//
// Design Notes: MapI/K
//
// MapI(R)(V, I) {...loop body...}
// MapK(R)(V, K) {...loop body...}
//
// Map operands are convenient iterators for array or dict elements, executing
// their loop body once for each entry.  While looking like function calls, both
// are are actually implemented as IFIDs and support CONTINUE and BREAK.
//
// MapI iterates over arrays and dicts.  It will "index" over every single array
// cell, whether populated or NULL.  That is also the case for dicts, where it
// will index over the hash array.
//
// MapK is slightly different, it will "key" only on populated cells (have elts)
// in arrays.  In the case of dicts, it will ignore the hash array and key on
// each pair.  So rather than a numeric index, it will set the Key (out)
// parameter to the actual Key used in the pair (or the index of the populated
// cell for arrays).
//
// Both of these will maintain 4 slots on the RunStack to keep track of their
// iteration and feed values to the two V and I/K out parameters.  This is
// easily handled by BREAK, which goes to the first instruction outside the
// loop, in this case a POPN op to get rid of the 4 items.  But RETURN is not so
// easy... it still has to POP the 4 items:
//
// a) The compiler COULD add a POPN(4) before the actual RETURN in the body of
//    the loop.  But the RETURN might be from a deeply nested LEX, so it must
//    check the entire LEX hierarchy up to the FUNC top.
//
// b) When a new FUNC is entered, the new RunRecord will stash the TOP of the
//    run stack... RETURN will zap the stack until the recorded TOP.  (The TOP
//    will not be 0 if the function is called while assembling a call list for
//    another call!)
//
// Option (b) is selected here, knowing the TOP of the RunStack seems useful,
// even though there will be a small runtime cost.
//
//
// Design Notes: GOTO and MapI/K
//
// scScript supports GOTO which can jump forward and backwards, but always
// within the context of a single function.  GOTO does *NOT* prevent the
// programmer from jumping into a block, perhaps a loop, that bypasses crucial
// initialization steps and creates erroneous results.
//
// But MapI/K blocks place a few (4) required items on the RunStack and rely on
// them for their operation.  So one CANNOT simply GOTO jump *into* a MapI/K
// block, as the missing RunStack values will cause a crash.  Therefore, the
// compiler checks and guards against these "entry" bugs.
//
// The user may also GOTO out of a MapI/K block.  But the compiler *must* clean
// out the RunStack entries before executing this "exit".  So this too requires
// special care.  Worse, the user can jump out of nested MapI/K blocks, which
// means the compiler will have to keep count and pop N items before executing
// the exit Jmp.
//
// The above work is slightly hampered by the statement-at-a-time compiler
// strategy.  So GOTO checks can only be made when the entire function has been
// compiled and all the jumps resolved.  This is only possible at the ASMPass
// stage, dealing with ByteOpRecords instead of Tokens and ASTs.  Sadly, the
// ReadPos from original sources are no longer available at this stage.  So the
// error message cannot point to the offending GOTO.  (Not until source line
// numbers are included in bytecodes!)
//
// These GOTO checks are only necessary when the function includes MapI/K
// operators (Func has MapP->Flags & SC_MAP_MLOOPFLAG).  The easiest approach
// has been to add an extra NO-OP ByteOpRecord before every GOTO jmp, as there
// is no way to know whether this function will eventually have a MapI/K in the
// compile CodeGen phase.  If a POPN operation is eventually needed, the NO-OP
// will be changed in situ; it is much easier to change an exiting ByteOpRecord
// than to add one mid-sequence.  ASMPass will otherwise happily ignore NO-OP
// records when writing out actual object code.
//
// Note: RETURN out of a MapI/K does nothing special.  When exiting a function,
//	 the RunStack top is set to what it was when the function was called.
//
//
// Design Notes: RefCounts and Garbage Collection (GC)
//
// The current system uses RefCounting along with a simple "stop everything"
// Mark/Sweep (MS) GC.  There are some tradeoffs:
//
// -- RefCounting is an incremental "smooth" process, EXCEPT when freeing huge
//    and complex data structures.  If this is a problem, the 'free' phase can
//    (in the future) be performed incrementally as part of the MS-GC.
//
// -- RefCounting is HARD to get right and CONFUSING when it goes wrong.  All GC
//    can lead to very strange sporadic runtime errors that are hard to debug;
//    but RefCounting bugs tend to be consistent and somewhat reproducible.
//    (STRSMARTCOUNT errors are sporadic and very hard to reproduce!)
//
// -- RefCounting cannot free cyclic data structures.  These are not that common
//    and must be handled by the MS-GC as memory slabs fill up.
//
// -- A far more complex incremental or multi-phased Mark/Sweep GC COULD replace
//    the current system, but would likely have a bigger impact on performance.
//    While complex GC might have shorter pauses, in the aggregate, it will
//    spend more (overhead) time.
//
// -- Mark/Sweep GC may expunge locally stashed (new or intermediate state) data
//    as it only knows about final "public" structures, globals, stack, etc.  So
//    the GC machinery has to be invoked with care and pre-emptively, not from
//    deep within the call stack when an allocation has already failed.
//
// -- RefCounting can be turned off for benchmarking or tasks with a light
//    memory footprint.
//
// -- EXT data (SC_ENTRY_EXT, SC_ENTRY_ARREXT, and SC_CHUNK_EXT) refer to client
//    data.  These can turn off MS/GC by setting their State field to SC_GCDONT.
//    (SC_GCDONT will also stop RefCount from zapping!)  In addition, there may
//    be client callbacks for marking and zapping.
//
// Note: Both RefCounting and MS/GC can be turned on/off with #define flags.
//	 GCSTART and GCSTEP values can be arbitrarily lowered for testing MS/GC
//	 (to avoid intermittent operation).
//
// MS/GC is currently initiated before and after a compile IFF the total space
// allocated to all slabs (stored in VMP->GCSize) exceeds SC_GCSTARTVAL.  There
// is little point in invoking MS/GC *during* a compile as very little memory is
// used/wasted, even in the absence of RefCounting.
//
// MS/GC is currently initiated during execution IFF the total slab space
// exceeds SC_GCSTARTVAL *AND* more than SC_GCSTEPVAL bytes have been allocated
// since the last MS/GC.  Good starting values for SC_GCSTART and SC_GCSTEP are
// 1/4 and 1/16 of available RAM.  The goal is to do everything possible to
// limit memory (slab) usage if SC_GCSTART has been exceeded, but not to perform
// constant GC (especially if RefCounting is enabled) during execution.
//
// Future: Allow user to set GCStartVal and GCStepVal VMP fields.
//
//
// Design Notes: Smart Str (SHP) RefCount (SC_GC_STRSMARTCOUNT)
//
// Text strings usually repeat, so are hashed and stashed in this system.  Not
// only are the same strings used repeatedly within a script, there is a high
// probability they are used repeatedly across scripts.  So it makes little
// sense to immediately expunge a string, especially a short one, when its
// RefCount goes to 0.  Instead the SC_GC_STRSMARTCOUNT sub-system implements a
// memory "Dwell" heuristic.  (Limited to string length of SC_GC_STRSMART_MAX)
//
// The higher the dwell number (stashed in StrHP->State) the longer the string
// stays around after its refcount goes to zero.  The current system uses a
// steep curve (hardcoded in SC_GC_StrSmartArr) to generate a dwell number
// inversely (and non-linearly) proportioned to the string length.
//
// SC_StrHashFind, the main read/write accessor function for the string hash
// array, will decrement the dwell number of each StrHP it encounters on the
// hash collision list of its target.  It will zap StrHP with zero RefCount when
// their dwell number also goes to zero.  A zero RefCount StrHP that is NOT
// ENCOUNTERED on the collision list does not impact performance, so is allowed
// to stay indefinitely or until the Mark/Sweep GC activates.
//
// In contrast to ref counting, the Mark/Sweep GC system is generally invoked in
// critical circumstances and with purpose.  Blissfully oblivious to RefCounts
// and dwell values, it will happily dispose all strings that cannot be reached.
// (It is imperative that SC_GCDONT is not confused with the dwell numbers
// stored in the same StrHP->State location.)
//
//
// Design Notes: Str (SHP) RefCount problem
//
// The program very often retrieves an SHP to look up a Dict entry (using
// SC_StrGetEntry).  But this could be a NEW string entry which is NOT in a Dict
// and may never be added to the Dict.  There are no optimal answers for how the
// string RefCount should be handled.  This program uses the first method below.
//
// -- SC_StrGetEntry will VARREF every time... The call cannot VARDEREF the
//    SHP before it is added to the Dict, because that could purge it.  So
//    the caller has to VARDEREF *after* the Dict lookup.  But this involves
//    redundant work...
//
// -- SC_StrGetEntry will NOT VARREF...  The SHP gets VARREF if and when it is
//    placed in a new Pair and written to a Dict.  Alas, an SHP with a zero
//    RefCount will be left if the caller does NOT place the string.  (Purge
//    only occurs at the 1->0 transition of RefCount.)
//
//
// Design Notes: External data
//
// There can be 3 variants for external data:
//
// -- SC_ENTRY_EXT:     Data is stored by the client, scScript just refers to it.
//			The client will be called to mark (during GC/MS) and to
//			zap/delete the data (RefCount to 0 or GC/MS).
//
// -- SC_ENTRY_ARREXT:  Client data is stored in an ArrDataP.  While scScript
//			manages the "data", it does not understand it, so will
//			call the client to Mark and Zap it.  But scScript will
//			allow re-sizing this data, so the ArrDataP can move!
//			(NEVER call SC_ArrPurge on an ARREXT, fields in ArrDataP
//			are *NOT* Var entry pointers!)
//
// -- SC_CHUNK_EXT:     Client data is stored as Chunk, it cannot grow or move.
//			This is very efficient for fixed size data.  But as
//			above, the client is called for marking and zapping.
//
// The first variant is necessary because sometimes the client wants to store
// its own data.  The third is important because the client may want scScript to
// store the fixed size data, where the client cannot handle it moving.
// Finally, the remaining variant, while less space-efficient, can handle growth
// and movement!
//
// In all cases, the State field can be set to SC_GCDONT to turn off MS/GC on
// the data--assuming the client will turn it back on at some point.  SC_GCDONT
// will *ALSO* stop RefCount from purging--RefCount will reset to 1 each time!
//
//
// Design Notes: Refs to Array/Dict Elements
//
// The data structure for an Array of N elements consists of:
//
// -- ArrVRecord	An Entry indicating the WHOLE array.
// -- ArrDataRecord	Array of N cells, each pointing to a VarVRecord.
// -- N VarVRecords	Containing data for each cell.
//
// The ArrVRecord has its own RefCount, so the Array as a whole can be purged
// when no longer referenced.  In addition, each of the N Var records has an
// independent RefCount.  Therefore, if a variable references MyArr[4], that
// VarVRecord can be maintained in memory even when MyArr itself is purged.  So
// MyArr[4] can be handed to a function OUT list or even Aliased (=&) by a
// local.
//
// Things are (after V2.2) similar for DICTs.  Each consists of:
//
// -- DictVRecord	An Entry indicating the WHOLE dictionary.
// -- ArrDataRecord	An array of 2^n cells, each pointing to a PairHRecord. 
// -- m PairHRecords	(where m <= 2^n) containing the association PAIR.
//
// The DictVRecord has its own RefCount, as do PairHRecords.  So a ref to a
// PairHRecord can survive the purging of the Dict.
//
// In both cases, once an Array element or Dict pair is placed on the OUT list,
// it is effectively aliased (even without an explicit =& operator) and can get
// a lifespan (through closure) that exceeds that of its parent data structure.
//
//
// Design Notes: FuncId and CodeId
//
// Every function is defined from within another, wrapped into a closure, and
// (typically) assigned to a variable.  Even top-level (global) functions are
// defined within the implicit TOP function that is the main script.  Function
// definition occurs at compile time, where it becomes a list of ByteCodes
// stashed within a Code Block Record (CBR) pointed to by a Code Block Pointer
// (CBP).  But closures are only created and assigned at runtime.  So the
// compiler must create CBRs and stash them where they can be processed at
// runtime.
//
// During compilation, every lexical block has a Lex, including the main block
// in a function.  These (and only these) Func Lex will also have a Map record,
// which will stash the CBP generated when the function is compiled.  Since each
// Func can define additional sub-functions, each Map also has a FuncADP (Array)
// which lists its sub-function CBPs.  Each function is thus referred to by its
// FuncId index within its (superior) container Map's FuncADP.  The CBR for the
// Function will also have a similar FuncArr that points to its sub-function
// CBP.  While Lex/Map are available only at compile time, CBR/CBP persist at
// runtime.
//
// When the container function eventually executes, it calls FMak-VI2 op right
// after each sub-function definition.  The (sub) func definition itself is for
// the compiler and does NOT generate code WITHIN the container func.  The only
// op that runs WITHIN the container func at execution time is the FMak which
// will generate a closure for the indexed FuncId and places it in V.
//
// So all functions get 0-based relative FuncId that indicate their definition
// Order/Index in their parent/container function.  Each CBR as well as its
// corresponding compiler Map will stash a list of sub-function CBP indexed with
// the FuncId.  The one exception is the Top function, as it is created
// implicitly by the compiler and not assigned at runtime.
//
// Displaying these relative FuncId might be confusing, there would potentially
// be many different functions with the same FuncId Index.  Instead, the display
// routines simply assign a 1-based global CodeId to ALL functions, regardless
// of nesting.  So while multiple functions may have FuncId (Index) of 3, they
// will get (for example) unique CodeIds of 5, 17, and 32.
//
// A CodeId-based system for function definition and "FMak" might be simpler,
// but would require more than 2 bytes for function identification.  Interactive
// scripting, where functions are redefined or replaced on the fly, will
// continue to require some sort of hierarchical organization, so a purging old
// CBR/CBP can take its sub-functions with it.
//
//
// Design Notes: Switch and Case Dictionary
//
// A single script function may have up to 255 different SWITCH statements.
// These are all combined and rolled into a single CaseDict at runtime which
// maps from [SwitchId + CaseType + CaseKonst] to [JumpOffset] in the CodeBlock.
//
// The CaseDict is currently NOT written out to the CodeBlockRecord.  When the
// "object code" for the script is saved as an independent file, the CaseDict is
// flattened and written at the end of it for ReCons (Re-Constitution) at
// runtime.  This must also be done to all string and Konst (floats and large
// integers) referenced (indexed) in the ByteCode ops.
//
// As in C, switch statements will simply continue if no cases match and no
// DEFAULT is provided.  One can argue that a failed switch should create a
// runtime error, but that is currently not done.
//
//
// Design Notes: Packages
//
// The system currently ONLY allows pre-defined INTERNAL packages.  These are
// created from within THIS source code and include only CONST + C-Function
// pointers (CFP)--the "main" Pkg can also include IFIDs.  At this time, There
// is no way to define an EXTERNAL package (imported as a binary) or a
// script-level package
//
// If Pkgs are allowed to contain global vars and script functions, Pkg names
// (and Dot syntax) would have to be checked for all var access, even in func
// calls and on the left side of assignments.  Currently, only globals are
// handled, a special ByteCode stashes them into a local register before use.
// New ByteCodes would have to be added to switch among different Pkg GlobP at
// runtime.
//
//
// Design Notes: NULL cells and missing Pairs
//
// scScript currently does not differentiate NULL from Int-0.  So it is not
// obvious whether MyDict.Key is NOT in MyDict or simply has a value of 0.  If
// this is an important distinction, CountOf(MyDict, "Key") will return 1 if in
// MyDict and 0 if not.  Almost all other script-level access to the Pair will
// automatically create and initialize it.  So CountOf(MyDict.Key) will *ALWAYS*
// return 1, as the arg to CountOf will itself add the pair in if missing!
//
// Arrays, unless created with "[]", are built with NULL cells.  So no storage
// is initially allocated for individual elts.  But just like dicts, indexed
// access will normally create the referenced elt and give it an initial value
// of 0.  CountOf(MyArr) will indicate the number of populated elts.  And as
// with dicts, CountOf(MyArr, Index) will indicate whether MyArr[Index] is
// currently populated.  Once again, CountOf(MyArr[Index]) will always return 1
// as the arg to CountOf will itself populate the elt if missing.
//
// scScript also provides === to compare two storage locations.  While this
// operator will generally find the REF location of variables, it will use the
// new ZREF byteop so as *NOT* to populate missing elts/pairs.  === will always
// return 0 if one or both sides are missing!
//
// === is also happy to compare var storage locations with other data
// structures, even int/flt numbers.  For "scalars", it will return 1 only if
// type and value are exactly the same.  So 1 == 1.0 but NOT 1 == 1.0.
//
//
// Design Notes: Binary Script Files (.bsc)
// ????
//
//
// Design Notes: CoRoutines and Non-local Return/Yield
//
// scScript provides full stackful assymetrical co-routines (CoR) in conjunction
// with non-local yield and return.  Any function can participate as a CoR
// without cumbersome pre-declarations or syntax.
//
// The CoR "master" function (M) initially calls the CoR "sub" function (S) with
// the proper args, In and Out list as appropriate.  S will initialize its state
// and SUBMIT--get the S&M jokes out of your system now.  This returns an RCP
// (RunContext Pointer) to M which simply stashes it.  This ends the initial
// handshaking for CoR.  The RCP will keep S in suspended animation and let its
// local state persist.
//
// (Note: RC was initially called "RunCall", this may persist in some comments.)
//
// M (or any function it hands S's RCP to) can now call S at will, using
// RESUME(RCP), where RCP came from the initial Call/Submit of S.  While M
// cannot specify any other parameters to S during a RESUME, it can set/modify
// "shared" Out variables that were passed to S during the intial call.  RESUME
// will cause S to wakeup at the first instruction following the initial SUBMIT
// (or last YIELD) and continue as if it was running all along.  When S has a
// result, it will YIELD that value to its caller M.  If M subsequently RESUMES
// S again, it will wakeup and continue after the YIELD.
//
// There are multiple possible ends to the CoR dance:
// -- M terminates S by calling RELEASE(RCP).
// -- M itself terminates or re-assigns its RCP variable. **
// -- S terminates by doing a RETURN instead of a YIELD. ++
// -- S runs off the end of the function--equivalent to RETURN 0.
//
// ** A "soft" release of the RCP results in immediate termination of the S
//    function if RefCounting is in effect.  Otherwise, S will persist in limbo
//    until a Mark/Sweep GC cycle collects its Run records.
//
// ++ M must somehow learn that S can no longer be RESUME'd.  This is normally
//    achieved by RETURNing an INT 0 or setting a shared Out variable.
//
// scScript also supports non-local RETURN and YIELD.  This is most useful for
// error handling or dispatch-style architectures where a subordinate function
// can return to a top-level caller directly without having to go through all
// intervening levels.
//
// Any function (CoR or otherwise) can call GetRC to get its own RCP.  If S1
// gets its RCP and then calls S2 which calls S3... all the way to Sn, then Sn
// can "RETURN Val OUTOF RCP".  This will RETURN Val to the caller of S1, just
// like S1 itself did a "RETURN Val".  If S1 is a CoR "Sub", then Sn can "YIELD
// Val OUTOF RCP".  In that case, a subsequent RESUME will continue execution in
// Sn, just after the YIELD operation.  Importantly, the RCP that S1 gets will
// be exactly the same one handed to M during the SUBMIT.  If S2 gets its own
// RCP, then Sn may RETURN, but cannot YIELD since S2's RCP will not have been
// SUBMIT'ed to M.
//
// NOTE: SUBMIT, RESUME, and RELEASE are all implemented as IFIDs.  These are
//	 function like and take arguments.  Extra args in their IN list are
//	 processed, but only if they have side-effects (All Funcs are called as
//	 their effect is impossible to analyze).  They also return values, a
//	 very important value for RESUME, and default 0 for SUBMIT and RELEASE.
//
//	 Importantly, YIELD (and RETURN) cannot be implemented as IFIDs.  They
//	 are NOT function like and return their value to outside their caller.
//
//	 Because of all this, SUBMIT, RESUME, and RELEASE can be called from
//	 the SC package... while YIELD cannot!!  This was the best design
//	 compromise I could find.  (YIELD simply does a RETURN if called
//	 from a non-coroutine!)
//
//
// Design Notes: Pattern Matching, MStack and RStack
//
// The system supports a new pattern (Pat) paradigm, drawing inspiration from
// SNOBOL as well as Parse Expression patterns.  Conceptually abstruse regular
// expressions, with their effectively write-only terse syntax, are eschewed in
// favor of this more readable and flexible format.  Recursive Pats are first
// defined and created with function calls, then matched against text in editor
// buffers.  This new operational model can easily accomodate more Pat types as
// well as specialized and custom functionality.  Importantly, strings are just
// that with no additional 'escaped' (in-band) matching parameters or hints.
//
// But the operational details of pattern matching remain deceptively complex,
// while the text data are linear in form, the Pats themselves are recursive and
// must be traversed in proper order.  In addition, the matcher must (at least
// conceptually) sometimes backtrack, look at superior Pats, or continue even
// after a successful match to maximize the results.  There are also
// intermediate results that have to be stashed properly, user messages that
// must be generated correctly (for debugging and optimization), and two
// operational stacks that must be maintained.
//
// Iterative Pats (SPAN and REP) support MAX and MIN logic, where the former
// tries to expand as far as permissible and the latter is satisfied with the
// first (minimal) match.  Importantly, Pats with operational lattitude (ALT,
// SPAN, and REP) may also be Considerate (CONS) where they match ONLY if all
// subsequent (superior on the match tree) Pats can be satisfied.
//
// The match system uses an explicit Match Stack (MStack) to convert pattern
// recursion and traversal to a more efficient purely *ITERATIVE* process.  The
// SC_DEBUG_DISP_MSTACK flag, if set, will display this stack at every step of a
// match.  This is an important optimization and debugging tool, as operation is
// all but incomprehensible without it.
//
// Once a Pat matches, there are often questions about its sub Pats, where they
// matched, and how many times.  This information is best communicated with a
// Result callback Func that gets invoked on successful sub-matches.  But given
// the possible backtracking and 'hunting' behavior of the conceptually
// recursive matcher, it is best to store the correct sub-results on a Result
// Stack (RStack) and then invoke the Result callbacks on the finalized RStack
// entries--which can also be displayed with the SC_DEBUG_DISP_RSTACK flag.
//
// The RStack has its own complexity:
//
// -- RStack entry order cannot be assumed.  CONS matches proceed backwards, so
//    entry order cannot be normalized without post processing or duplicate
//    matching.
//
// -- A few non-obvious tricks were required to optimize matching and avoid
//    duplicate processing of CONS.  These are especially complex with multiple
//    CONS Pats and nesting loops (REP and SPAN again).
//
// -- There is no global oversight of MAX in complex patterns.  In general, the
//    earliest Pats, in match order, will maximize at the expense of later ones.
//    But once again, "match order" is reversed for CONS processing.  This may
//    pose a conceptual conflict for script/pattern writers.
//
//
// Design Notes: Sub-scripts and Script Recursion
//
// One script can load (read and compile .sc sources or directly load .bsc
// binaries) sub-scripts into variables and subsequently run them.  The initial
// motivation for this was having test and validation scripts all individually
// run by a single master script.
//
// Each Sub-script will operate entirely independently, within its own namespace
// (own GlobP), and CANNOT share any data with other "siblings" or "parent".
// This avoids the possibility of "Sub-script Recursion" where Script A runs
// Script B which... will eventually run Script A again.  (It is possible to
// re-load/run the SAME script files, but it will be a separate copy/mem-image!)
//
// In the current implementation, SC_ScriptRecord points to SC_RunPointer(s) as
// well as its SupScriptP.  But this will NOT WORK if a Script were to have
// multiple running images!  Script Recursion is therefore strictly prohibited.
//
// Even an innocuous "Script.GetSuperior" function could lead to script
// recursion!  This can be blocked with runtime checks in Script.Run.
// Alternatively, the SC_ScriptRecord data structure can be changed to fully
// support running multiple instances of a given script.
//
// NOTE: Every function returns a value, even the Top level one in a script.
//	 But the Top level function MAY NOT return an RCP, as the RunPointers
//	 for it will have been zapped.
//
// The same script (whether from .sc or .bsc) can be loaded multiple times.
// Each instance will be a separate copy/mem-image.  This is clearly
// sub-optimal, but the current approach.

// ********************************************************************************
// sc_Script.c
// Script module for sc_Script
// ********************************************************************************

#include	<locale.h>
#include	<X11/Xlib.h>
#include	<X11/Xutil.h>
#include	<X11/Xatom.h>  
#include	<X11/cursorfont.h>
#include	<poll.h>
#include	<unistd.h>
#include	<pwd.h>
#include	<dirent.h>
#include	<sys/time.h>
#include	<fcntl.h>
#include	<math.h>
#include	<ffi.h>

#include	<X11/Xft/Xft.h>

// Somewhere in Xlib, "Status" is a #define... "Success" too, no really!
// Either #undef Status or use another field name, like State!

#include	"sc_Public.h"
#include	"sc_Error.h"
#include	"sc_Editor.h"
#include	"sc_Script.h"
#include	"sc_Main.h"

// ********************************************************************************
// ********************************************************************************
// ********************************************************************************

#define	SC_VERSIONMAJOR			0x0002
#define SC_VERSIONMINOR			0x0003

// Compile Flags ********************************

// Set these to 0 or 1 only...

#define	SC_GC_REFCOUNT			1		// Use RefCount GC
#define SC_GC_STRSMARTCOUNT		1		// Do Smart RefCount for StrHPointers
#define	SC_GC_MARKSWEEP			1		// Use MarkSweep GC

#define SC_CG_NOPKGNAMEREDEF		1		// Prohibits script Dec/Def of Pkg names
							// NOTE: Bad runtime errs if script re-defs "Sys"!
							//       Even though "sYS" etc can still be used!

#define	SC_DEBUG_CALLDEBUGGER		1		// Set 1 so PARSEERROR and CODEERROR call GDB

#define	SC_SAFE				1		// Runtime tests... slow but safe
#define	SC_DEBUG_CHECK_SLABFREE		1		// Check SLAB after ever FREE
#define SC_DEBUG_CHECK_ALLSLAB		1		// FULLY check all SLAB blocks

#define SC_DEBUG_COMPILE_DISP		1		// Show info during Compile
							// Will NOT show ProgBar if 1

#define	SC_DEBUG_PRINT_DICT		0		// Trace every PAIR stash in DICT
#define SC_DEBUG_PRINT_LOCALS		0		// Print Locals at FEnd or FRet
#define SC_DEBUG_PRINT_GLOBALS		0		// Print Globals when TOP ends

#define SC_DEBUG_DISP_EXEC_OP		0		// Display (in GDB) ByteCode at every exec step
#define SC_DEBUG_DISP_ALLOC		0		// Display (in GDB) Var alloc/zap
#define SC_DEBUG_DISP_GC		1		// Display (in GDB) GC collection

	// (DISP_MATCH) *OR* (DISP_MSTACK/DISP_RSTACK)!  Confusing if all are 1.
#define SC_DEBUG_DISP_MATCH		0		// Display (in GDB) every Pat match step
	// ********************************************************************
#define SC_DEBUG_DISP_MSTACK		1		// Display (In GDB) MStack (match recursion)
#define SC_DEBUG_DISP_RSTACK		1		// Display (in GDB) RStack (Result stack)

// Constants ************************************

#define	SC_HASHINITSLOTCOUNT		(1 << 13)	// Start with 8K slots (buckets)
#define SC_HASHMAXSLOTCOUNT		(1 << 24)	// Limit to 16M slots
#define SC_MAXSLABDATASIZE		(1 << 30)	// Max slab payload is 1Gbyte
#define SC_MAXSLABINITDATASIZE		(1 << 26)	// Max INIT slab payload is 64Mbyte
#define SC_MAXARRLEN			((1 << 24) + 1)	// Max Array slots is 16 Meg
#define SC_MINARRLEN			8		// Only for space alloc/Fragmentation
#define SC_MAXSTRLEN			(1 << 24)	// Max string len is 16 MByte
#define SC_MINDICTMASK			4		// Start with 16 elements
#define	SC_MINDICTLEN			(1 << SC_MINDICTMASK)
#define SC_MAXDICTLEN			(1 << 21)	// Max size of Dict Hash Arr (will add 1)
#define SC_MAXPAIRCOUNT			(1 << 20)	// No more than 1Meg pairs in a dict
#define SC_TOKENINITSLOTCOUNT		256L
#define SC_TOKENEXTRASLOTCOUNT		512L
#define	SC_LEXINITSLOTCOUNT		256L
#define SC_LEXEXTRASLOTCOUNT		512L
#define	SC_MAPINITSLOTCOUNT		32L
#define SC_MAPEXTRASLOTCOUNT		32L
#define	SC_VEXINITSLOTCOUNT		256L
#define SC_VEXEXTRASLOTCOUNT		512L
#define SC_VARENTRYINITSLOTCOUNT	1024L		// Each slot is 1 SC_FixedVRecord (16 bytes)
#define	SC_VARENTRYEXTRASLOTCOUNT	1024L	
#define SC_BIGENTRYINITSLOTCOUNT	1024L		// Each slot is 1 SC_PairHRecord (64 bytes)
#define	SC_BIGENTRYEXTRASLOTCOUNT	1024L

#define SC_STRDATAINITSLABSIZE		4096L		// Initial StrData slab size
#define SC_STRDATAEXTRASLABSIZE		2048L		// Extra StrData bytes for new slabs
#define SC_ARRDATAINITSLOTCOUNT		2048L		// Initial ArrData slots (8 bytes each)
#define SC_ARRDATAEXTRASLOTCOUNT	1024L		// Extra ArrData slots for new slabs
#define SC_CHUNKINITSLABSIZE		4096L		// Initial Chunk slab size
#define SC_CHUNKEXTRASLABSIZE		2048L		// Extra Chunk bytes for new slabs

#define	SC_MAX_IDENT_LEN		64

#define SC_CGINITGNAMECOUNT		16
#define SC_CGGROWGNAMECOUNT		16
#define	SC_CGMINCODERECORDS		8
#define	SC_CGMINLABELSLOTS		16
#define	SC_CGMINKONSTSLOTS		4
#define	SC_CGMINFUNCSLOTS		4
#define	SC_CGINITSTRINGSLOTS		16
#define	SC_CGMINCASESLOTS		8
#define	SC_CGMINSTACKSLOTS		100
#define	SC_CGMAXSWITCHCOUNT		1024		// Max SWITCH statements in a FUNC
#define	SC_CGMAXCALLPARAMCOUNT		64		// Max total call args to FUNC
#define	SC_CGMAXGLOBALCOUNT		65535
#define	SC_CGMAXREGCOUNT		7
#define	SC_CGMAXVARCOUNT		(255 - SC_CGMAXREGCOUNT)
#define	SC_CGMAXDOUBLESIZE		(1L << 12)	// 4K
#define SC_CGPKGCOUNTINIT		8
#define SC_CGPKGCOUNTINCR		8
#define SC_CGNDICTINITCOUNT		16
#define SC_CGCFPCOUNTINIT		32
#define SC_CGCFPCOUNTINCR		16
#define SC_CGINGZINITSIZE		8
#define SC_CGINGZGROWSIZE		8
#define SC_CGMAXPKGNAMELEN		63		// Pkg name
#define SC_CGMAXPKGFIELDNAMELEN		63		// SFunc name
#define SC_CGMLOOPRUNSTACKVALS		4		// MapI/K put 4 values on RunStack
#define SC_RUNSTACKRECORDS		(1L << 9)	// 512
#define SC_GC_STARTVAL			(1024 * 16)	// 1/4 of sys memory is good
#define SC_GC_STEPVAL			(1024 * 1)	// 1/4 of GC_STARTVAL is good

// ********************************************************************************
// Prog Bar will *SLOW* down compile unless LenMin and StepMin are quite large.
// It has a usleep to make display visible!!  (Off unless SC_DEBUG_COMPILE_DISP is 0)
#define SC_CGPROGSOURCELENMIN		(1024 * 4)	// Min src len for compile prog update
#define SC_SGPROGSOURCESTEPMIN		256		// Min src read between compile prog update

// ********************************************************************************
// ********************************************************************************
// ********************************************************************************
// VM

#define _SC_VARVPOINTER		struct _SC_VarVRecord *
#define _SC_ARRVPOINTER		struct _SC_ArrVRecord *
#define _SC_DICTVPOINTER	struct _SC_DictVRecord *
#define _SC_EXTVPOINTER		struct _SC_ExtVRecord *
#define _SC_STRHPOINTER		struct _SC_StrHRecord *
#define _SC_PAIRHPOINTER	struct _SC_PairHRecord *
#define _SC_STRDATAPOINTER	struct _SC_StrDataRecord *
#define _SC_ARRDATAPOINTER	struct _SC_ArrDataRecord *
#define _SC_DICTDATAPOINTER	struct _SC_DictDataRecord *

    // Adding a new type:
    //
    //  -- Data: SC_ENTRY_TYPE + SC_EntryNameArr + SC_SCRIPT_TYPECODE
    //	-- Func: SC_SysWriteVP + SC_SSMAuxShowSlab
    //
    //  -- Data: SC_RefCountVarArr for SC_VARREF
    //  -- Func: SC_VarDeRef and its DispArr
    //  -- SC_GCMarkVar for GC
    //
    // Search for @@@

    typedef enum {
	// *** ORDER IS IMPERATIVE ***  Matches various arrays of ops and names! *******
	
	SC_ENTRY_GEN = 0x00,	// Convenient for code + init
	SC_ENTRY_VAR,		// Holds a direct value or ptr
	SC_ENTRY_PAIR,		// Hashed dict Pair + Extra fields
	SC_ENTRY_ARR,		// Points to Array in ArrData slab
	SC_ENTRY_STR,		// Hashed String Entry + Extra fields
	SC_ENTRY_DICT,		// Points to Array that is closed hash list
	SC_ENTRY_EXT,		// VEnt entry, Points to external memory
	SC_ENTRY_ARREXT,	// VEnt entry, points to ArrData for data
	SC_ENTRY_CASE,		// Same as PAIR, but CASE format!
	SC_ENTRY_RC,		// RunCall record... only 1 for each RunRecord!
	SC_ENTRY_PAT,		// Pattern records (text processing), stored on BEnt
	SC_ENTRY_PKG,		// Pkg records

	SC_CHUNK_EXT,		// Entry and Data together on CHUNK slab
	SC_CHUNK_CLOS,		// Closure records, like an Entry, but on CHUNK slab
	SC_CHUNK_CODE,		// CodeBlock records on CHUNK slab
	SC_CHUNK_RUN,		// Run records on CHUNK slab
	SC_CHUNK_SCRPT,		// Script data
	SC_CHUNK_GLOB,		// Array of GLOBAL Vars on CHUNK slab
	SC_CHUNK_STAK,		// Stack of Vars on CHUNK slab

	SC_ENTRY_COUNT,		// Number of types
    } SC_ENTRY_TYPE;

    typedef enum {
	SC_GC_MARK    = 0x80,				// Mark added to VP->Type (Top bit)
	SC_GC_INDICT  = 0x40,				// Mark added, only to PairP->Type *FROM* DICT
	
	SC_GC_MARKMASK = SC_GC_MARK | SC_GC_INDICT,	// Top 2 bits are used for MS/GC
	SC_GC_TYPEMASK = 0xFF & ~SC_GC_MARKMASK,	// Rest are for actual type!
    } SC_GC_TYPE;

    char *	SC_EntryNameArr[] = {"Gen", "Var", "Pair", "Arr", "Str", "Dict", "Ext", "ExtArr", "Case", "RC", "Pat", "Pkg",
				     "ExtChunk", "Clos", "Code", "Run", "Scrpt" "Glob", "Stak"};
    // Type codes exposed to Script
    typedef enum {
	SC_SCRIPT_UNKNOWN = 0,
	SC_SCRIPT_INT,
	SC_SCRIPT_FLT,
	SC_SCRIPT_STR,
	SC_SCRIPT_ARR,
	SC_SCRIPT_DICT,
	SC_SCRIPT_EXT,		// Client memory
	SC_SCRIPT_GEXT,		// Arr based, can grow
	SC_SCRIPT_RC,
	SC_SCRIPT_PAT,
	SC_SCRIPT_FEXT,		// Chunk based, cannot grow
	SC_SCRIPT_CLOS,
	SC_SCRIPT_SCRPT,
    } SC_SCRIPT_TYPECODE;

    char *	SC_SysScriptTypeStrs[] = {"Unknown", "Integer", "Float", "String", "Array", "Dict",
					  "Ext", "GExt", "RunC", "Pat", "FExt", "Func", "Script"};

    Int8	SC_SysScriptTypeCodes[] = {-1,			// Gen
					   -1,			// Var
					   -1,			// Pair
					   SC_SCRIPT_ARR,	// Arr
					   SC_SCRIPT_STR,	// Str
					   SC_SCRIPT_DICT,	// Dict
					   SC_SCRIPT_EXT,	// Ext
					   SC_SCRIPT_GEXT,	// ArrExt
					   -1,			// Case
					   SC_SCRIPT_RC,	// RC
					   SC_SCRIPT_PAT,	// PAT
					   -1,			// Pkg
					   SC_SCRIPT_FEXT,	// ChunkExt
					   SC_SCRIPT_CLOS,	// Clos
					   -1,			// Code
					   -1,			// Run
					   SC_SCRIPT_SCRPT,	// Scrpt
					   -1,			// Glob
					   -1};			// Stak


    typedef enum {
	SC_VAL_INT  = 0x00,	// Inline Int64
	SC_VAL_FLT  = 0x01,	// Inline double float
	SC_VAL_PTR  = 0x02,	// Ptr to a var
	SC_VAL_PRV =  0x03,	// Ptr to a PRIVATE type--like VEX... has no Type field!!
	SC_VAL_CASE = 0x04,	// CASE (SwithID+Offset) **Pair VAL Only**
	
	SC_VAL_FIDX = 0x05,	// Address of C Function **Pair VAL Only**
	SC_VAL_IIDX = 0x06,	// InternalFunc Id (IFID) for MAIN Pkg[0] **Pair VAL ONly**
	SC_VAL_GIDX = 0x07,	// INDEX for Glob var **Pair VAL Only**
	SC_VAL_PIDX = 0x08,	// INDEX for Pkg **Pair VAL Only**
    } SC_VAL_TYPE;

    //   -0x24.2f3  
    // Every datum has an Entry record.  These are allocated on special slabs
    // and will *NOT* move for their life, so can be safely and efficiently
    // accessed with a pointer.  Any associated data (array cells or string
    // chars) are stashed on separate slabs that can move, compact, or
    // re-allocate as required--will update the Entry as necessary.
    //
    // Each Entry can hold an Int, a Float, or a Ptr to a longer datum stored on
    // some slab (or external program).
    //
    // There are a few Entry types:
    //
    //	VAR     Holds a number or Ptr, used as a variable or to hold an array
    //		cell.  Entry is stored on 'VEnt' (Small Entry) Slabs and
    //		represents a storage area or LValue.  The runtime RunRecord for
    //		each function has a VarArr for local variables, each VarArr cell
    //		will point to one of these.  Vars do not point to other vars,
    //		but the VarArr of multiple functions may point to the very same
    //		Var Entry!  (In the case of OUT parameters, the VarArr cell
    //		*definitely* points to a pre-existing Var Entry in the calling
    //		function.)  A VEntry may also point to a DICT or ARR entry (that
    //		represents the Dict or Array).  GC is performed by RefCount, an
    //		occasional Mark/Sweep will clean leftovers.
    //
    //	ARR     Represents an entire Array of data (cells point to other
    //		Entries).  It is very possible to have circular refs, but still
    //		use RefCounts with a proper (mark/sweep style) GC to clean up
    //		leftovers.  Entry is stored on 'VEnt' Slabs, Array data on
    //		'ADat' slabs.  Array data can move, so the ADat slab will inform
    //		the ARR VEntry after such move--only if another Array is being
    //		allocated causing the slab to get packed.
    //
    //		An ARR entry "represents" the whole array!  It is a convenient
    //		handle to the data stored on the ADat.  There is similarly a
    //		DICT handle for a dictionary.
    //		
    //  PAIR    Associates a Key (Numb or Entry) with a Value (Numb or Entry).
    //		A set of Pairs forms a Dict, which hashes them on an Array.
    //		Pairs are removed when their Dict is, so pairs have no separate
    //		GC.  Pair Entry is stored on 'BEnt' slabs.
    //
    //  DICT    Represents a Dictionary, a hash array of Pairs.  Dicts are used
    //		in the compile process and to store SWITCH cases at runtime.
    //		The script can also define Dicts and Arrays at runtime.  Dict
    //		Entry is stored on 'VEnt' slabs, hash Array on 'ADat' slabs.
    //
    //  PAT	Is a pattern created to match against text.  Each Pat is
    //		independently defined, but often refers to other, previously
    //		defined, Pats that have been stored in variables.  These
    //		structures participate in RefCount and MS+GC.  They are stored
    //		on BVent slabs.
    //
    //  EXT     Represents external memory/data.  Provides proper (mark/sweep
    //		style) GC.  Entry stored on 'VEnt' slabs, Data is stored by
    //		external client.
    //
    //		ARREXT	Data stored on ADat slab, managed by scScript.
    //		CHEXT	Entry/Data stored together on CHUNK slab.
    //
    //  CLOS    Represents a Closure.  Multiple Vars may point to the same
    //		Closure, the Closure itself stores variables, so can point to
    //		any and all.  First pass GC is via RefCount, but these do
    //		require a final Mark/Sweep to combat circularity.  CLOS and
    //		ByteCode records are allocated on CHUNK slabs.  These DO NOT
    //		move data once allocated.
    //
    //  SCRPT   A "Script" can be read, Compiled, and loaded into a Var for
    //		execution.  These sub-scripts are subject to RefCount and GC.
    //
    //  STR     Represents a single immutable string.  Every string is
    //		immediately hashed on a central (open) hash arr.  Uses RefCount
    //		for GC as strings cannot form refs, circular or not.  Entry is
    //		stored on 'BEnt' slabs, char data goes on 'SDat' slabs.
    //
    // Simple numbers (Int and Float) generally do not get their own Entry.
    // These primitive values are stored 'inline' on VAR entries as variables or
    // on Pairs (Key or Value) in Dicts.  (Every cell on an array points to a
    // VEntry.)
    //
    // A Dictionary is conceptually just a list of Pairs, each associating a Key
    // (a number or ptr to an Entry) with a Value (also a number or ptr to an
    // Entry).  In the most common case, the Key is the EntryP for a String.
    // The Dict will get its own Entry along with a data Array of ptrs to its
    // Pairs.  This Array will hash (closed) all Pairs associated with the Dict,
    // allowing for fast retrieval.
    // **************************************************************************

    typedef enum {
	// Default (0) depends on function... as MIN is NOT 0!!
	SC_SHOW_MIN =		1,
	SC_SHOW_LOW,
	SC_SHOW_HIGH,
	SC_SHOW_MAX,
    } SC_SHOW_LEVELS;

    // *ALL* VRecords (stored on Var Entry Slabs) *MUST* have identical length
    // and structure, they are cast from one to another depending on Type.

    typedef enum {
	SC_VAR_NOFLAG =		0x00,
	SC_VAR_ESCFLAG =	0x01,			// Escaped from local context
    } SC_VAR_FLAGS;

    typedef enum {
	SC_NOSTATE =		0x00,
	SC_GCDONT =		0xFF,			// DO NOT GC (Will add extra RefCount!)
    } SC_VAR_GCSTATE;

    typedef struct _SC_VarVRecord {			// Local vars, like PAIR without a Key.
	Uns8			Type;			// SC_ENTRY_VAR
	Uns8			State;
	Uns8			Flags;
	Uns8			ValueType;		// Ptr, Int, or Float
	Uns32			RefCount;		// Recfount for GC
	void *			ValueP;			// Direct value (Int, Flt) or ptr
    } SC_VarVRecord, *SC_VarVPointer;

    // NOTE: ArrData can be moved (Pack on Slab).  ArrData will point (MasterPP)
    //	     to the ArrDataP Field on its EntryRec and automatically update
    //	     if/when it moves.
    
    typedef struct _SC_ArrVRecord {			// EntryRec for Arrays
	Uns8			Type;			// SC_ENTRY_ARR
	Uns8			State;			// CAN GC
	Uns8			Flags;
	Uns8			Res;
	Uns32			RefCount;
	_SC_ARRDATAPOINTER	ArrDataP;		// Updated if the Array moves
    } SC_ArrVRecord, *SC_ArrVPointer;

    typedef struct _SC_DictVRecord {			// EntryRec for Dicts
	Uns8			Type;			// SC_ENTRY_DICT
	Uns8			State;			// CAN GC (Purge removes entire Dict!)
	Uns8			Flags;
	Uns8			MaskBits;		// For Hash array
	Uns32			RefCount;		// RefCount for GC
	Uns32			PairCount;		// Pairs in HADP
	Uns32			TotProbes;		// Sum of all Probe counts for all pairs
	_SC_ARRDATAPOINTER	HADP;			// Closed Hash Array, updated if Arr moves
	_SC_PAIRHPOINTER	FirstPairP;		// First (last added) Pair record
    } SC_DictVRecord, *SC_DictVPointer;			// [0] is first pair [1] is PairCount

    // FEXT: Memory allocated/managed by client.
    //	     FExtVRecord on VEnt slab, is separate from data!
    typedef struct _SC_EXTVRecord {			// EntryRec for Extern (Foreign memory)
	Uns8			Type;			// SC_ENTRY_EXT
	Uns8			State;			// GC only if internal Alloc.
	Uns16			ExtType;		// Type of data stored
	Uns32			RefCount;		// Refcount for GC
	void *			ExtP;			// Points to Foreign memory 
    } SC_EXTVRecord, *SC_EXTVPointer;

    // AEXT: Memory allocated/managed by Script--on ARR slab, can grow/move
    //	     AExtVRecord on VEnt, is separate from data!
    typedef struct _SC_ArrXTVRecord {			// EntryRec for Extern (Array memory)
	Uns8			Type;			// SC_ENTRY_ARREXT
	Uns8			State;			// GC only if internal Alloc.
	Uns16			ExtType;		// Type of data stored
	Uns32			RefCount;		// RefCount for GC
	_SC_ARRDATAPOINTER	ArrDataP;		// Update if the Ext block moves
    } SC_ArrXTVRecord, *SC_ArrXTVPointer;

    // CEXT: Memory allocated/managed by Script--on CHUNK slab, CANNOT grow/move.
    //	     CExtVRecord on CHUNK slab, CONTAINS data!
    typedef struct _SC_ChunkXTVRecord {			// EntryRec for Extern (Chunk memory)
	Uns8			Type;			// SC_CHUNK_EXT
	Uns8			State;			// GC only if internal Alloc.
	Uns16			ExtType;		// Type of data stored
	Uns32			RefCount;		// Refcount for GC
	Uns32			ExtData;		// Client Data
	Uns32			BlockLen;		// Total len of this block (header and all)
	Int64			Ext[0];			// Each 8 bytes!!
     } SC_ChunkXTVRecord, *SC_ChunkXTVPointer;

    // Closures are allocated as Arr on CHUNK slabs... unlike other Entry records!!
    typedef struct _SC_ClosRecord {
	Uns8			Type;			// SC_CHUNK_CLOS Required to be on CHUNK slab
	Uns8			State;
	Uns16			Flags;
	Uns32			RefCount;
	Uns32			FuncId;
	Uns32			BlockLen;		// Required to be on CHUNK slab
	void *			CBP;			// Code Block Pointer
	SC_VarVPointer		VarArr[0];		// Closed Vars, if any!
    } SC_ClosRecord, *SC_ClosPointer;

    // NOTE: StrHRecords, PairHRecords, CaseHRecords and RCRecords can all
    //	     be allocated on the same (BEnt) slabs as they are all 32bytes!

    // StrHRecords are Entry for Strings.  These are all hashed on a central
    // StrHashTable.  StrHRecords contain HashNextP to handle the collision list
    // on the open StrHashTable.  HashVal for Strings are slow to compute, so
    // are stashed in the record for (a) quick matching through the StrHashTable
    // and (b) easy re-hashing when the table is grown.  StrHRecords can be
    // referenced by Vars, Pairs, and even Arrays, but do not reference
    // others. RefCount garbage collection is used for them.
    
    typedef struct _SC_StrHRecord {
	Uns8			Type;			// SC_ENTRY_STR
	Uns8			State;			// Carries STRSMARTCOUNT Dwell#
	Uns16			Flags;
	Uns32			RefCount;		// RefCount GC
	Uns64			HashVal;		// Full key value
	_SC_STRHPOINTER		HashNextP;		// Hash collision
	_SC_STRDATAPOINTER	StrDataP;		// String Data, updated if Arr moves
    } SC_StrHRecord, *SC_StrHPointer;

    // PairHRecords are allocated on BEnt Slabs.  A Dict is conceptually just a
    // set of Pairs.  In practice, a Dict holds an Array that double hashes Ptrs
    // to Pair EntryRec.
    //
    // KeyP and ValueP are usually ptrs, BUT DO STASH NUMBERS DIRECTLY.  Fields
    // specify type of Key and Value (Ptr, Int64, or Float).  (KeyP often refers
    // to a String and *IS* the HashVal.)
    //
    // PairHRecords have a singly-linked list to allow quick traversal of all
    // Pairs in the Dict.  (Alternative would be to traverse the entire array.)
    //
    // NOTE: User can set a Pair value to -0- to conceptually 'delete' the Pair.
    //	     But only a LAZY DELETE would be possible (and only for re-hashing)
    //	     because (a) the PrevPairP is not obvious and (b) removing a PairP
    //	     from the closed Hash Array would confuse collision jumps during
    //	     lookup.  (Unwinding is *possible* but quite expensive!)
    //
    // NOTE: **** PairHRecord must look just like a VarVRecord!! ****
    //		  BUT do not increment ProbeCount as if RefCount!!
    // NOTE: **** RCRecord must have same size/format as PairHRecord ****
    //		  (Will be allocated on the same slabs!)

    typedef struct _SC_PairHRecord {
	Uns8			Type;			// SC_ENTRY_PAIR
	Uns8			Step;			// Step size for Hashing
	Uns8			KeyType;
	Uns8			ValType;
	Uns32			RefCount;		// RefCount GC
	SC_VarVPointer		ValueP;			// VarVPointer (0LL means Nil) or NUMBER
	SC_VarVPointer		KeyP;			// VarVPointer or NUMBER
	_SC_PAIRHPOINTER	NextPairP;		// For this Dict
    } SC_PairHRecord, *SC_PairHPointer;

    // CaseHRecord is a slightly altered version of PairHRecord, used to handle
    // SWITCH Cases.  It includes an Id (SWITCH Id/Index) used for hashing and
    // retrieval.  But the Value field is only an Uns32.
    //
    // NOTE:	CASE record must match PAIR... not differentiated in most functions.
    //		Id is limited to SC_CGMAXSWITCHCOUNT, given as Uns32 for byte alignment.

    typedef struct _SC_CaseHRecord {
	Uns8			Type;			// SC_ENTRY_CASE
	Uns8			Step;			// Step size for Hashing
	Uns8			KeyType;
	Uns8			ValType;
	Uns32			RefCount;		// RefCount GC
	Uns32			Id;			// Switch Id/Index
	Uns32			Value;			// JMP Offset (Label index initially)
	Int64			Key;			// Number or StrHPointer
	_SC_PAIRHPOINTER	NextPairP;		// For this Dict
    } SC_CaseHRecord, *SC_CaseHPointer;

    // String data is allocated on StrData slabs.
    // Array  data is allocated on ArrData slabs.
    // Generic is for convenience, lacks data but matches the header.
    //
    // These *MUST* have similar size/structure.
    //
    // In all cases, allocation is aligned and sized on longlong word (8 byte)
    // boundaries.
    //
    // NOTE:	If declared as SC_StrDataRecord.Data[1], compiler pads it:
    //
    //			offsetof(SC_StrDataRecord, Data) --> 16
    //			sizeof(SC_StrDataRecord) - 1 ------> 23 !!!
    //
    //		so declared as Data[8] to avoid confusion!

    // NOTE:	ArrData can be moved (Pack on Slab).
    //		ArrData will point (MasterPP) to the DATAPOINTER Field on its
    //		EntryRec and automatically update if/when it moves.

    typedef struct _SC_GenDataRecord {			// Generic Data record
	Uns32			DataCount;		// Char for Str, void* for Arr (Block may have extra space)
	Uns32			BlockLen;
	void **			MasterPP;		// Ptr to MasterP to this DataRecord (In EntryRec)
    } SC_GenDataRecord, *SC_GenDataPointer;

    typedef struct _SC_StrDataRecord {			// Round total size up to 8
	Uns32			StrLen;			// Str Len (in Bytes) (Data may have extra space)
	Uns32			BlockLen;		// Total of this block, header and all
	_SC_STRDATAPOINTER *	MasterPP;		// Pts to MasterP to this DataRecord (In EntryRec)
	char			Data[8];		// String data... any length
    } SC_StrDataRecord, *SC_StrDataPointer;

    // Unlike Str, Arr is defined with Data[] of Pointer, not char!
    typedef struct _SC_ArrDataRecord {
	Uns32			SlotCount;		// Allocated total for Array (Data may have extra space)
	Uns32			BlockLen;		// Total len of this block (header and all)
	_SC_ARRDATAPOINTER *	MasterPP;		// Pts to MasterP to this DataRecord (In EntryRec)
	void *			Data[1];		// Each 8 bytes!!
    } SC_ArrDataRecord, *SC_ArrDataPointer;

#undef _SC_VARVPOINTER
#undef _SC_ARRAYVPOINTER
#undef _SC_DICTVPOINTER
#undef _SC_EXTVPOINTER
#undef _SC_STRHPOINTER
#undef _SC_PAIRHPOINTER
#undef _SC_STRDATAPOINTER
#undef _SC_ARRDATAPOINTER
#undef _SC_DICTDATAPOINTER

#define	_SC_SLABPOINTER		struct _SC_SlabRecord *

    // Extend SC_SLAB_TYPE_MASK if more than 16 Slab types!
    typedef enum {
	SC_SLAB_STAB =		0x00,		// String Hash table (One and only)
	SC_SLAB_VENT,				// Small Entry (Fixed size)
	SC_SLAB_BENT,				// Big Entry (Fixed size)
	SC_SLAB_SDAT,				// Str Data (Array, Byte based, CAN move)
	SC_SLAB_ADAT,				// Arr Data (Array, LL based, CAN move & resize)
	SC_SLAB_CHNK,				// Chunk (Array, but NEVER move & resize)
	SC_SLAB_TOK,				// Token (Fixed size)
	SC_SLAB_LEX,				// Lex (Fixed size)
	SC_SLAB_MAP,				// Map (Fixed size)
	SC_SLAB_VEX,				// Vex (Fixed size)

	SC_SLAB_COUNT,
    } SC_SLAB_TYPE;

    char SC_SlabTagArr[][4]=   {{'S', 'T', 'a', 'b'},
			        {'V', 'E', 'n', 't'},	// Small Var Entry
				{'B', 'E', 'n', 't'},	// Big Entry (Pair, Str, RCP, Pat)
				{'S', 'D', 'a', 't'},
				{'A', 'D', 'a', 't'},
				{'C', 'h', 'n', 'k'},
				{'T', 'o', 'k', '!'},
				{'L', 'e', 'x', '!'},
				{'M', 'a', 'p', '!'},
				{'V', 'e', 'x', '!'}};
				
    char* SC_SlabNameArr[] =	{"String Hash Table", "Var Entry", "Big Entry", "String Data", "Array Data",
				 "Run/Code Chunks", "Token Records", "Lex Records", "Map Records", "Vex Records"};

    typedef enum {
	SC_SLAB_NO_FLAG =	0x00000000,
	SC_SLAB_STR_FLAG =	0x00000100,
	SC_SLAB_ARR_FLAG =	0x00000200,
	SC_SLAB_CHNK_FLAG =	0x00000400,

	SC_SLAB_TYPE_MASK =	0x0000000F	// Type is just added to flags!
    } SC_SLAB_FLAG;

    // Minimum allocation unit for Slabs is 16 bytes, same size as
    // SC_SlabFreeRecord;

    typedef struct _SC_SlabRecord {
	Uns32			Tag;
	Uns32			Flags;
	Uns32			DataSize;		// Allocated Data, EXCLUDES header size
	Uns32			DataUsed;
	Uns32			FirstFree;		// Indexes into Data
	Uns32			FreeCount;		// Count of Free blocks

	_SC_SLABPOINTER		NextSP;
	_SC_SLABPOINTER		PrevSP;
	char			Data[16];
    } SC_SlabRecord, *SC_SlabPointer;

#undef _SC_SLABPOINTER

// Slab Free blocks start with a Zero field (0LL), making them easy to recognize
// when compacting (de-frag) a slab.  (The alternative, StrDataRecord or
// ArrDataRecord start with StrLen and SlotCount respectively which *CANNOT* be
// zero!)

typedef struct _SC_SlabFreeRecord {
    Uns64			Zero;			// All zeroes!
    Uns32			Size;			// Size of this block
    Uns32			NextFree;		// Index in SP->Data
} SC_SlabFreeRecord, *SC_SlabFreePointer;

// There is a single StrHashTable.  It starts with an SC_StrHashTable header
// followed by N (power of 2) Slots (buckets).  If the usage factor gets too
// high, the table (SlotCount) is doubled and everything is re-hashed.

typedef struct _SC_StrHashTRecord {
    Uns32			Tag;			// "STab"
    Uns32			Flags;
    Uns32			SlotCount;		// Always 2^N
    Uns32			SlotsUsed;		// 1 or more entries
    Uns32			EntryCount;		// Counts collisions too
    Uns64			IndexMask;		// Lower N bits
    SC_StrHPointer		Slots[1];		// SC_StrHPointer
} SC_StrHashTRecord, *SC_StrHashTPointer;

// ********************************************************************************
// ********************************************************************************
// ********************************************************************************
// Read

// Tokens are allocated off a TokenSlab.  Only N tokens are ever needed at the
// same time, where N reflects the most complex statement being parsed/compiled.
//
// Token structures are used to build Abstract Syntax Trees (ASTs) in the
// standard way.  The top node is an operator and its args (1 or 2) are linked
// at the DownP level.
//
// ('Data' field can be used as 'DownP', but no need for extra saving.)

#define _SC_TOKENPTR	struct _SC_TokenRecord *

    typedef enum {
	SC_TOKEN_NO_FLAG	= 0x01,		// NOT ZERO to differentiate from FREE on Slab
	SC_TOKEN_INPUT_FLAG	= 0x02,		// Only for UnPack Operator!
	SC_TOKEN_ZREF_FLAG	= 0x04,		// Do NOT populate missing Elt/Pair for REF

	SC_TOKEN_PROCSTR_FLAG	= 0x80,		// String token has escape sequences
	SC_TOKEN_ASSIGN_FLAG	= 0x40,		// Tells caller OP did not Assign, Caller should
	SC_TOKEN_NOPUSH_FLAG	= 0x20,		// Do not push result value, will be ignored
	SC_TOKEN_LVALUE_FLAG	= 0x10,		// Op Token will be L side of assignment!
    } SC_TOKEN_FLAGS;

    typedef enum {
	SC_NO_STATE		= 0x0000,
	SC_LASSOC_STATE		= 0x0001,
	SC_UNARY_STATE		= 0x0002,
	SC_PREFIX_STATE		= 0x0004,
	SC_NOARG_STATE		= 0x0008,
	SC_PBLOCK_STATE		= 0x0010,	// Block Proc once on OpStack
	SC_FUNC_STATE		= 0x0020,	// Arg is Func ref (name or sub-tree)!
	SC_FINAL_STATE		= 0x0040,	// No more Ops after this!
	SC_START_STATE		= 0x0080,	// Marks the start of expression
	SC_NOCOMMA_STATE	= 0x0100,	// Ignores commas CALL, IN, OUT, VAR, CONS
	SC_SYSCALL_STATE	= 0x0200,	// CALL is for a SYS Function
	SC_IFIDCALL_STATE	= 0x0400,	// Call for Built-in functions  (ARRAY, DICT, INT...)
	SC_IFIDCOND_STATE	= 0x0800,	// IFID COND... expects body after, not ';'!
	SC_UNPACK_STATE		= 0x1000,	// CALL will have an UnPack in INPUT or OUTPUT arg list
    } SC_OPSTATE;
    // NOTE:  SC_FINAL_STATE is also used for processed CALL ops that have an OUT (RArg)
    //	      param list... so subsequent () will create another CALL!

    typedef enum {
	SC_NO_TOKEN		= 0,
	SC_WHITESPACE_TOKEN,
	SC_STRING_TOKEN,
	SC_NUMBER_TOKEN,
	SC_OPERATOR_TOKEN,
	SC_IDENTIFIER_TOKEN,

	SC_TOKEN_COUNT,				// Count of above Token Types!
	SC_START_TOKEN		= 0xF0,		// Start of expr/stat
	SC_FINALEND_TOKEN	= 0xFF,		// Reached end of script
    } SC_TOKEN_TYPE;

typedef struct _SC_TokenRecord {
	Uns8		Flags;			// SC_TOKEN_FLAGS
	Uns8		Type;			// SC_TOKEN_TYPE
	Uns8		Id;			// SC_TOKEN_ID
	Uns8		OpPrec;			// Precedence (1 is highest)
	Uns16		OpState;		// Unary, Prefix, etc.
	Uns16		Stamp;			// Unique stamp for test/debug
	Uns32		ReadPos;		// 0 == Start of script
	Uns32		ReadLen;		// How many bytes long
	Int64		Data;			// Int/Float/Ptr
	_SC_TOKENPTR	DownP;			// Ptr to args below this operator
	_SC_TOKENPTR	NextP;			// Next (usually Arg) on this list
    } SC_TokenRecord, *SC_TokenPointer;

#undef _SC_TOKENPTR

// Sets 1 BIT for Ops that DO NOT have a side effect.
// ***MUST SYNC*** with SC_TOKEN_ID list!
// (Becomes Endian-Sensitive if set wider than Uns8.)

// *** ORDER IS IMPERATIVE ***  Matches enum below! ********
// Generate with SC_SetCGOpEffectArr()--normally #if'ed out!
// *********************************************************
// Bit 0 means Op HAS side-effect
// First byte is [1111 1101] because SC_EQ_ID == 1 has side-effect.

Uns8	SC_CGOpEffectArr[32] = {0xfd, 0xff, 0xff, 0xdf,
				0xff, 0xff, 0xff, 0xff,
				0xfe, 0x01, 0xe0, 0xff,
				0xff, 0xff, 0xff, 0xff,
				0xfc, 0xff, 0xff, 0xff,
				0xff, 0xff, 0xff, 0xff,
				0xff, 0xff, 0xff, 0xff,
				0xff, 0xff, 0xff, 0xff};

typedef enum {
    // ID for Operators
    // 1 + (SC_TOKEN_ID / 64) is Operator char length!!

    SC_NO_ID		= 0,
    SC_EQ_ID		= 1,	// =	**1 char long**
    SC_LESS_ID,			// <
    SC_GREAT_ID,		// >
    SC_PLUS_ID,			// +
    SC_MINUS_ID,		// -
    SC_COMMA_ID,		// ,
    SC_SEMI_ID,			// ;
    
    SC_DIV_ID,			// /
    SC_MOD_ID,			// %
    SC_MULT_ID,			// *					**10**
    SC_LOGNEG_ID,		// !	Always Unary
    SC_BITNEG_ID,		// ~	Always Unary
    SC_BITAND_ID,		// &
    SC_BITOR_ID,		// |
    SC_BITXOR_ID,		// ^
    
    SC_CHOOSE_ID,		// ?
    SC_COLON_ID,		// :	Part of ternary '?:'
    SC_DOT_ID,			// .	Index into Dict
    SC_PCLOSE_ID,		// )
    SC_ACLOSE_ID,		// ]					**20**
    SC_END_ID,			// }
    SC_POPEN_ID,		// (
    SC_AOPEN_ID,		// [
    
    SC_BEGIN_ID,		// {
    //------------------------------
    SC_UPLUS_ID,		// Unary + for CG only
    SC_UMINUS_ID,		// Unary - for CG only			**26**

    // Special token ops **27-60**  MIND THE COUNT!
    SC_NULL_ID,			// Special OP, straight to Arg		**27**
    SC_INDEX_ID,		// Array Index
    SC_CALL_ID,			// Function Call
    SC_IN_ID,			// Function INPUT parameter list	**30**
    SC_OUT_ID,			// Function OUTput parameter list
    
    SC_VAR_ID,			// Var decl
    SC_DEF_ID,			// Const DEF decl
    SC_CONS_ID,			// Constructor (Array or Dict)
    SC_PAIR_ID,			// Pair ':' for Dict Cons
    SC_LABEL_ID,		// Label ':' as target of goto
    SC_GOTO_ID,			// Goto keyword becomes an Op
    SC_CASE_ID,			// CASE (in SWITCH)
    SC_DEFAULT_ID,		// DEFAULT (in SWITCH)
    
    SC_FOR_ID,			// For loop				**40**
    SC_WHILE_ID,		// While loop
    SC_IF_ID,			// If conditional
    SC_SWITCH_ID,		// Switch statement
    SC_DO_ID,			// Do loop
    SC_DOCOND_ID,		// Terminating WHILE in DO loop
    SC_ELSE_ID,			// Else conditional
    SC_BREAK_ID,		// Break out of a loop
    
    SC_CONT_ID,			// Continue a loop
    SC_FUNC_ID,			// Function defintion
    SC_FDECL_ID,		// Func declaration (just the Func Name) **50**
    SC_RETURN_ID,		// Func return
    SC_YIELD_ID,		// Func yields (Co-routines!)
    
    SC_FROM_ID,			// Return FROM and Yield FROM
    SC_LOGPOS_ID,		// Logical unary
    SC_UNPACK_ID,		// Unpack Argarr			**57**
				// ****DO NOT EXCEED 63 HERE****
    
    SC_ALIAS_ID		= 64,	// =&
    SC_EQEQ_ID,			// ==  **2 chars long**
    SC_NEQ_ID,			// !=
    SC_LESSEQ_ID,		// <=
    SC_GREATEQ_ID,		// >=
    SC_LOGAND_ID,		// &&
    
    SC_LOGOR_ID,		// ||					**70**
    SC_SHL_ID,			// <<
    SC_SHR_ID,			// >>

    SC_PLUSPLUS_ID,		// ++  Assumed Postfix for CG
    SC_MINMIN_ID,		// --  Assumed Postfix for CG
    SC_PLUSEQ_ID,		// +=
    SC_MINUSEQ_ID,		// -=
    SC_DIVEQ_ID,		// /=
    
    SC_MODEQ_ID,		// %=
    SC_MULTEQ_ID,		// *=
    SC_BITANDEQ_ID,		// &=					**80**
    SC_BITOREQ_ID,		// |=
    SC_BITXOREQ_ID,		// ^=
    //-------------------------------
    SC_PREPLUSPLUS_ID,		// Prefix ++ for CG only
    SC_PREMINMIN_ID,		// Prefix -- for CG only
    
    SC_SHLEQ_ID		= 128,	// <<=	**3 chars long**
    SC_SHREQ_ID,		// >>=
    SC_IDENT_ID,		// ===
    SC_NIDENT_ID,		// !==

    SC_GUARD_ID		= 255,	// Special sentinel for arg list

    // ID for WhiteSpace
    SC_BLANKWS_ID     	= 0,	// Real white space
    SC_LCOMMENTWS_ID,		// Line "//..." comment as white space
    SC_BCOMMENTWS_ID,		// Block "/*... */" comment as white space

    // ID for Numbs
    SC_INTNUMB_ID	= 0,	// MUST be 0
    SC_FLOATNUMB_ID		// MUST be 1

} SC_TOKEN_ID;

typedef enum {
    SC_NO_KEYCODE	= 0,

    SC_BREAK_KEYCODE,
    SC_CASE_KEYCODE,
    SC_CONT_KEYCODE,
    SC_DEF_KEYCODE,
    SC_DEFAULT_KEYCODE,
    SC_DO_KEYCODE,
    SC_ELSE_KEYCODE,
    SC_FOR_KEYCODE,
    SC_FROM_KEYCODE,
    SC_FUNCTION_KEYCODE,
    SC_GOTO_KEYCODE,
    SC_IF_KEYCODE,
    SC_NULL_KEYCODE,
    SC_RETURN_KEYCODE,
    SC_SWITCH_KEYCODE,
    SC_VAR_KEYCODE,
    SC_VOID_KEYCODE,
    SC_WHILE_KEYCODE,
    SC_YIELD_KEYCODE,

    // Following are for SC_ReadMainPkgIdent, only seen by Editor
    // *** IMPERATIVE ***  Coordinate with SC_IFID_Name below! *******
    SC_ARRAY_EDCODE,		// IFIDs
    SC_DICT_EDCODE,
    SC_FLT_EDCODE,
    SC_GETRC_EDCODE,
    SC_INT_EDCODE,
    SC_MAPI_EDCODE,
    SC_MAPK_EDCODE,
    SC_RELEASE_EDCODE,
    SC_RESUME_EDCODE,
    SC_SUBMIT_EDCODE,

    SC_COUNTOF_EDCODE,		// Funcs
    SC_SIZEOF_EDCODE,
    SC_TYPEOF_EDCODE,
    SC_WRITE_EDCODE,
    SC_WRITELN_EDCODE,

    SC_KEYCODE_COUNT,
} SC_KEYWORD_ID;

// Pre-defined Internal Functions (Identified by IFID).  These are stashed in
// MAIN (Pkg[0]) and can be accessed *WITHOUT* a Pkg designation.
//
// These look like function calls in Script code, but are compiled as inline
// ByteCode sequences, unlike simple function CALLs.
//
// First SC_IFIDCondCount are COND... which means they expect a body after the
// call, NOT a semi-colon!

char	SC_MainPkgName[] =	"sc";		// Must be lowercase
Int16	SC_IFIDCondCount =	2;
typedef enum {
    SC_MAPI_IFID = 0,		// IFID COND
    SC_MAPK_IFID,		// IFID COND
    //---------------------------------------
    SC_ARRAY_IFID,
    SC_DICT_IFID,
    SC_INT_IFID,
    SC_FLT_IFID,
    SC_RCP_IFID,
    SC_RESUME_IFID,
    SC_RELEASE_IFID,
    SC_SUBMIT_IFID,

    SC_IFID_COUNT,
} SC_INTERNAL_FUNCTION_ID;

// Must be all lowercase, as Script is case-insensitive for these
char *		SC_IFID_Name[] = {"mapi", "mapk", "array", "dict", "int", "flt", "getrc", "resume", "release", "submit"};


// ********************************************************************************
// ********************************************************************************
// ********************************************************************************
// Parse + CodeGen

#define _SC_CGPOINTER		struct _SC_CGMainRecord *
#define _SC_MAPPOINTER		struct _SC_MapRecord *
#define	_SC_LEXPOINTER		struct _SC_LexRecord *
#define _SC_VEXPOINTER		struct _SC_VexRecord *
#define _SC_JSETPOINTER		struct _SC_JSetRecord *
#define _SC_RUNPOINTER		struct _SC_RunRecord *
#define _SC_GLOBPOINTER		struct _SC_GlobRecord *
#define _SC_SCRIPTPOINTER	struct _SC_ScriptRecord *
#define _SC_KONSTPOINTER	struct _SC_KonstRecord *
#define _SC_CODEBLOCKPOINTER	struct _SC_CodeBlockRecord *
#define _SC_STACKBLOCKPOINTER	struct _SC_StackBlockRecord *

    // A "sub" script can be read, compiled, and "loaded" into a Var!  Normally,
    // a Var will happily contain a Closure, but a Script is more than a ClosP.
    // A Script is the entire run-data, more like a meta-closure.  It includes
    // Globals, Konsts, and Strings as well as a reference to the Top ClosP.
    //
    // These must support RefCount/GC, assignment, display, etc.
    //
    // The "Main" script runs with ScriptLevel of 0.
    // Any script it loads and runs will have ScriptLevel of 1.
    // And script it load and run will have ScriptLevel of 2...
    //
    // SC_ScriptRecord has a fixed size and is on CHUNK slab!
    // Top function of a Script can return a value... but **NEVER** an RCP.
    // (Must avoid an RCP outliving the RunP it points to!!)
    // 
    // NOTE: SC_ScriptRecord directly indicates TopRunP/CurRunP and SupScriptP!!
    //	     This will not work *IF* the same ScriptRecord is being run multple
    //	     times, i.e. "Script Recursion"!
    //
    //	     Options: Avoid Script Recursion (Done here)
    //		      Create a ScriptRunRecord that can be stacked! (Not done)
  
    typedef struct _SC_ScriptRecord {
	Uns8			Type;			// SC_CHUNK_SCRPT
	Uns8			State;
	Uns16			Flags;
	Uns32			RefCount;

	Uns16			GlobCount;		// How many globals
	Uns16			ScriptLevel;		// Nesting level when running...
	Uns32			FuncCount;		// Total funcs in this script
	
	Uns32			KonstCount;		// Konsts in this script
	Uns32			StringCount;		// Strings in this script
	Uns32			ScriptId;		// ++LastScriptId from VMP
	Uns32			StartStackTop;		// StackTop when this (sub) script runs!

	SC_StrHPointer		NameSHP;		// Name of script (includes .sc/.bsc)
	SC_StrHPointer		PathSHP;		// Full pathname

	SC_ClosPointer		TopClosP;
	// Top function CLOS... to start things.
	SC_ArrDataPointer	KonstADP;		// Each elt is a Konst, ByteCode knows Int or Flt
	SC_ArrDataPointer	StringADP;		// Each elt is StrHP
	SC_ArrDataPointer	GNameADP;		// Global Var Names, each elt is StrHP
	_SC_GLOBPOINTER		GlobP;			// Globals in THIS script (Always gets one, even empty)

	// When running, Scripts can stack, each loading/running another...
	_SC_SCRIPTPOINTER	SupScriptP;		// Superior SCRIPT!  (NULL for main!)
	_SC_RUNPOINTER		TopRunP;		// Main Func Run for THIS script
	_SC_RUNPOINTER		CurRunP;		// Cur Func Run for THIS script
    } SC_ScriptRecord, *SC_ScriptPointer;

    // VMRecord keeps track of all memory slabs and their usage.  Points to TOP
    // Clos, that will pt to its CBP, which has list of sub-CBP... on and on.

    typedef struct _SC_VMRecord {

	Uns64			GCSize;			// Size of all allocated slabs
	Uns64			GCStep;			// Allocated since last GC
	Uns64			GCStartVal;		// Size threshold to initiate GC
	Uns64			GCStepVal;		// Step threshold since last GC
	Uns64			GCSweepTot;		// How much cleared in THIS sweep
	SC_StrHashTPointer	StrHashTP;		// 1 big hash table

	// Runtime slabs
	SC_SlabPointer		VarEntrySP;		// 1 or more V Entry slabs
	SC_SlabPointer		BigEntrySP;		// 1 or more large Entry (Pair/Str/RC) slabs
	SC_SlabPointer		StrDataSP;		// 1 or more String data slabs
	SC_SlabPointer		ArrDataSP;		// 1 or more Array data slabs
	SC_SlabPointer		ChunkSP;		// 1 or more Chunk slabs

	Uns64			VarEntrySlotAlloc;	// Total allocated
	Uns64			BigEntrySlotAlloc;	// Total allocated
	Uns64			StrDataByteAlloc;	// Total allocated (Counts Data area only)
	Uns64			ArrDataSlotAlloc;	// Total allocated
	Uns64			ChunkByteAlloc;		// Total allocated
	
	// Compile time slabs
	SC_SlabPointer		TokenSP;		// 1 or more Token slabs
	SC_SlabPointer		LexSP;			// 1 or more Lex slabs
	SC_SlabPointer		MapSP;			// 1 or more Map slabs
	SC_SlabPointer		VexSP;			// 1 or more Vex slabs

	Uns64			TokenSlotAlloc;		// Total allocated
	Uns64			LexSlotAlloc;		// Total allocated
	Uns64			MapSlotAlloc;		// Total allocated
	Uns64			VexSlotAlloc;		// Total allocated

	SC_PrintFuncP		OutPrintFP;		// Routes print to OUTPUT
	SC_PrintFuncP		CurPrintFP;		// Routes print to CUR Pane/Buf
	
	// Runtime Data
	Uns32			PkgCount;		// Actual count, always 1 or more
	Uns32			CFPCount;		// Actual Count of C functions
	Uns32			StackTop;		// Index for top of stack (In VarVRecords)
	Uns32			StackCount;		// Capacity of stack
	Uns32			LastCodeId;		// Base counter for Map->CodeId
	Uns32			LastScriptId;		// Starts from 1.. inc each time!

	// Old values are set after running a script... for use of "Script-Try"!
	// These will be wiped when starting a regular compile/run.
	// IGNORE if compile/run a **SUB** script (SC_SysScriptLoad + SC_SysScriptExec)!
	Uns32			OldKonstCount;		// Inherited count of Konstants
	Uns32			OldStrCount;		// Inherited count of Strings
	SC_ArrDataPointer	OldKonstADP;		// Inherited Arr[I] -> Int/Flt
	SC_ArrDataPointer	OldStrADP;		// Inherited Arr[I] -> StrHP
	_SC_GLOBPOINTER		OldGlobP;		// Inherited globals, has count
	SC_ArrDataPointer	OldGNameADP;		// Inherited Glob names Arr[Index] -> StrHP
	SC_DictVPointer		OldDefDictP;		// Inherited Def Dict[Name--Def value (Str/Int/Flt)]
	
	SC_ScriptPointer	RunScriptP;		// Current running script... may have Sup chain!
	SC_ScriptPointer	CompileScriptP;		// Result of compile operation!
	
	
	_SC_STACKBLOCKPOINTER	StackBlockP;		// Stack block on CHUNK slab
	SC_ArrDataPointer	PkgADP;			// Each elt is a Ptr to a PkgRecord (or NULL)
	SC_ArrDataPointer	CFPADP;			// C function pointers defined by various pkgs

	SC_VarVPointer		StackP;			// Runtime stack (StackBlockP->Data[0])

    } SC_VMRecord, *SC_VMPointer;


    // *****************************
    Uns16	SC_SysGetArgCFIndex;			// Will be set when SYS Pkg is loaded
    Uns16	SC_SysPrintCFIndex;			// Will be set when SYS Pkg is loaded
    Uns16	SC_SysErrorCFIndex;    
    // *****************************


    // During Code check and gen, LexRecords store information about code
    // segments (often bracketed with {}) and VexRecords stash data for
    // variables: local, closed, and registers.
    //
    // VexRecords hang off function MAPs.  Since a given function may have many
    // variables, Vex are chained with NextP.  Named variables are also included
    // and referenced by VDictP in Lex records.
    //
    // The Map record itself (MapP) hangs off a LexRecord.  Code segments are
    // generally nested, so LexRecords also form a chain, but they use NextP in
    // the *REVERSE* direction to indicate superior (enclosing) segment.
    //
    // The LexRecord for a segment normally does NOT have a VDictP.  But if the
    // segment corresponds to a BEGIN-END {} block that declares variables or
    // perhaps the main BEGIN-END for a function declartion, then a VDictP will
    // be provided to match Var names to their Vex for *that* code segment.  The
    // MapP field is NULL, except for the main BEGIN-END LexRecord of a Function
    // definition.

    typedef enum {
	SC_LEXNOFLAG		= 0x00000000,
    
	SC_LEXIFFLAG		= 0x00000001,
	SC_LEXELSEFLAG		= 0x00000002,
	SC_LEXLOOPFLAG		= 0x00000004,	// Permits BREAK + CONT
	SC_LEXSWITCHFLAG	= 0x00000008,	// Holds CASE and DEFAULT
	//-----------------------------------
	SC_LEXTYPEMASK		= 0x0000000F,

	SC_LEXDOFLAG		= 0x00000010,	// DO-WHILE loop
	SC_LEXDONEEDFLAG	= 0x00000020,	// Incomplete, Needs a WHILE still
	SC_LEXCASEFLAG		= 0x00000040,	// Got at least ONE CASE or DEFAULT!
	SC_LEXNEXTCASEFLAG	= 0x00000080,	// Only first case DOES NOT inval REG cache!
	SC_LEXMAPFLAG		= 0x00000100,	// MAP loop
	
	SC_LEXTOPFLAG		= 0x80000000,	// Will be TOP!
	SC_LEXFUNCFLAG		= 0x40000000,	// Func definition!
	SC_LEXCONDFLAG		= 0x20000000,	// CONDITIONAL Lex (IF/Else/While...)

	SC_LEXONEFLAG		= 0x01000000,	// Only 1 statement, until BEGIN
	SC_LEXFINFLAG		= 0x02000000,	// Lex is Finished, but left for 1 cycle
    } SC_LEXFLAGS;
    
    typedef struct _SC_LexRecord {		// Code Lex
	Uns32			Flags;
	Uns16			FLevel;		// Function nesting level
	Uns16			BLevel;		// Block nesting level (within Func)
	Uns16			Data1;		// Switch ID
	Uns16			Data2;
	
	Uns32			TrueL;		// Label index for true cond (If, else, loop)
	Uns32			FalseL;		// Label index for false cond
	Uns32			ContL;		// Update in LOOP and COND in DO!
	
	_SC_LEXPOINTER		SupP;		// SUPERIOR Lex (Span/Block)
        _SC_MAPPOINTER		MapP;		// FUNC Map Block (Otherwise NULL)
	SC_DictVPointer		VDictP;		// Lexical Var Dict, names for THIS block [SHP - Vex]
	SC_DictVPointer		DDictP;		// Lexical Def Dict, names for THIS block [SHP - Int/Flt/SHP]
	SC_TokenPointer		StashTP;	// AST stashed... UPDATE in FOR loop, etc.
    } SC_LexRecord, *SC_LexPointer;

    typedef enum {
       SC_VEXNOFLAG		= 0x00,
       SC_VEXFDECFLAG		= 0x01,		// Registered with Func Dec
       SC_VEXINVALFLAG		= 0x80,		// REG assignment INVALID
       SC_VEXINGLOBFLAG		= 0x40,		// INTERACT: Inherited global
    } SC_VEXFLAGS;

    typedef enum {
	SC_NOVEX		= 0x00,
	SC_GVEX,		// Global VAR
	SC_CVEX,		// Closed VAR
	SC_LVEX,		// Local VAR (Includes func params)
	SC_RVEX,		// REG (Temp for Globals)

	// These should come after last VEX type.
	SC_STACK,		// On Stack -- ByteCodeRecords
	SC_REFER,		// Ref on Stack -- Ptr to Entry
	SC_KONST,		// Usually 1 signed byte, but can be 1-3
	SC_INDEX,		// Can be 1-3 unsigned bytes
	SC_SYSI,		// 1 byte index of Sys function
	
	SC_ADDR,		// Only for ASM stage JMP, BRT, and BRF
    } SC_VEXCLASSES;

    typedef struct _SC_VexRecord {		// Var Lex --> Vex
	Uns8			Flags;
	Uns8			BLevel;		// Lex Depth for REG
	Uns8			Class;		// G, C, L, or REG
	Uns8			SrcClass;	// Gets value from G, C, or L
	Uns16			Index;		// Index of THIS var
	Uns16			SrcIndex;	// C or L for CVex, assumed G for RVex
	_SC_VEXPOINTER		NextP;
    } SC_VexRecord, *SC_VexPointer;
    // A CVex gets its value from SrcClass:SrcIndex where SrcClass is LVex or CVex
    // An RVex gets its value only from GVex:SrcIndex
    
    // **MINIMUM** size for record on SLAB is 16 bytes!!

    // MapRecord maps the bytecodes, labels, constants, and variables for the
    // function being compiled.  It is critical for compilation and code
    // generation and will stay around until the entire function (and its
    // sub-functions) have been defined.
    //
    // Every new Func must have an obligatory first LexRecord which will have a
    // MapP field pointing to a Map record as well as VDictP and DDictP.
    //
    // NOTE: Arrays are a natural structure for storing variable length data, as
    //	     they can be grown as necessary.  But arrays *CAN MOVE*.  The
    //	     ArrData blocks move to pack a Slab when allocating another array.
    //	     Each ArrData will have ONE SINGLE MasterP and automatically update
    //	     it if/when it moves.  Do not stash ArrDataP elsewhere, as only a
    //	     single MasterP will be updated when it moves!!
    //
    // NOTE: A GVex is created for each global variable in the TOP function.
    //	     Non-TOP functions create a GVex for each global they *USE*.  N
    //	     (currently 7) local RVex form a rotating (LRU) cache of Globals for
    //	     each function, allowing access to Globals as if they were local.
    //	     (There are many ways to improve/optimize this in the future!)
    //
    // NOTE: RVex assignment/mapping to globals should be invalidated and
    //	     re-established after each JUMP target.  At runtime, an RVex can be
    //	     reassigned at some point, but a subsequent JUMP can violate the
    //	     sequence and lead to wrong mappings.
    //
    //	     1) This is NOT necessary if the FUNC addresses fewer Globals
    //		than REGs... since this guarantess no REGs are re-assigned.
    //	     2) Redundant REG assigns can be eliminated at final ASM
    //		phase if JUMPS did NOT include re-assignments.
    //	     3) It is better to assign REGs before loops that may be executed
    //		many times (hoist assignment).  But hoisted assigns will be
    //		wasted if global is referenced only in unused code paths!
    //
    // NOTE: LabelDict maps named (explicit) labels to their index in the Label
    //	     Array.  So "DoneDone:" may indicate Index 14, which means
    //	     LabelADP->Data[14] will contain the Offset to the instruction
    //	     indicated by "DoneDone".  These values are initially ByteCodeRecord
    //	     indices, but will later (during final ASM phase) become actual
    //	     (delta) byte offsets to ByteCodes.
    //
    //	     LabelDict.LabelString --> LabelIndex
    //	     LabelArr[LabelIndex] --> CodeRecordIndex
    //	     ASM phase:  CodeRecordIndex --> (relative) ByteCodeOffset
    //
    //	     LabelArr will *ALSO* include (IMPLICIT label) elts that are NOT in
    //	     the LabelDict.  These are used only during the compile phase and
    //	     will be inline replaced by relative offsets in the final ASM phase.
    //	     Such labels are initially used for BR/JMP instructions in ifs and
    //	     loops. A simple (A && B) condition in an IF statement requires 2
    //	     such implicit labels.  The same ByteCode index may be the target of
    //	     implicit BR/JMP labels as well as explicit named labels.
    //
    //	     Each program can have a number of strings referenced in the code,
    //	     for example as different elts in a SWITCH.  The StringADP is a list
    //	     of Ptrs to StrHRecords, and ByteCodes refer to the Index of the
    //	     given string in the StringADP.  It is also quite common to use the
    //	     very same strings in multiple locations.  Therefore, a StringDictP
    //	     is used to avoid duplication--Uses the string as a key and its
    //	     Index as value.
    //
    //	     A single function may have MANY (1024) SWITCH statements.  These
    //	     are all combined in a single CaseDict at runtime which maps from:
    //
    //		[(Case-Konst)(Key-Type)(Switch-Index)] to [Jump-Offset]
    //
    //	     So if the 3rd SWITCH statement has a "Case 14:", the CaseDict will
    //       map from [(14)(INT)(3)] to the appropriate JMP offset for that
    //       SWITCH.  DEFAULT cases are *NOT* entered in the CaseDict, but
    //       instead explicitly JMPed to in ByteCodes!

    typedef enum {
	SC_MAP_NOFLAG		= 0x0000,
	SC_MAP_ALLARGSFLAG	= 0x0001,	// Include InArgArr and OutArgArr!
	SC_MAP_MLOOPFLAG	= 0x0002,	// Func has MapI/K
    } SC_MAP_FLAGS;
    
    typedef struct _SC_MapRecord {
	Uns16			Flags;
	
	Uns16			SwitchCount;	// How many SWITCH statements in FUNC
      	Uns16			InCount;	// Pass by Value Arg count
	Uns16			OutCount;	// Pass by Ref Arg count
	
	Uns16			RCount;		// How many Regs used in FUNC
	Uns16			LCount;		// Locals: Already includes In/Out count
	Uns16			CCount;		// Closed: Vars closed over (0 for TOP)
	Uns16			GCount;		// Globals: Declared for TOP, Used for non-TOP

	Uns32			FuncId;		// Index/Id for this function in superior
	Uns32			CodeBytes;	// Stash ByteCode length
	
	Uns32			CodeCount;	// Index of ByteOpRecords
	Uns32			DispCount;	// Last displayed code count
	
	Uns32			VarCount;	// Total: R+L+C, NOT Globals (Except on LEXTOP)
	Uns32			LabelCount;	// How many labels
	
	Uns32			KonstCount;	// How many constants
	Uns32			StringCount;	// How many strings
	
	Uns32			FuncCount;	// How many sub-funcs
	Uns32			CodeId;

	SC_LexPointer		SupFLexP;	// LexP of Func that contains this one.

	SC_ArrDataPointer	CodeADP;	// Array of ByteOpRecords  (Struct form ByteCode + Args)
	SC_ArrDataPointer	LabelADP;	// Array of Labels (Goto offsets)
	SC_ArrDataPointer	KonstADP;	// Array of Konsts (TOP only ****)
	SC_ArrDataPointer	StringADP;	// Array of Strings StrHPointers (TOP only ****)
	SC_ArrDataPointer	FuncADP;	// Array of CBP for sub funcs

	SC_DictVPointer		LabelDictP;	// Dict for named goto Labels! [NameSHP - Index]
	SC_DictVPointer		KonstDictP;	// Dict for Konstants (TOP only) [Value(Int) - Index]
	SC_DictVPointer		StringDictP;	// Dict for Strings (TOP only) [StringSHP - Index]
	SC_DictVPointer		CaseDictP;	// Dict for all SWTICH cases
	
	SC_VexPointer		RVexP;		// Register VAR list
	SC_VexPointer		LVexP;		// Local VAR list (Even in TOP!)
	SC_VexPointer		CVexP;		// Closed VAR list
	SC_VexPointer		GVexP;		// Global VAR list, accessed by this Func
    } SC_MapRecord, * SC_MapPointer;

    // Logicals, conditions, and loops have to deal with Jump and Branch
    // (implicit) labels.  These are collected in JumpSet (JSet) records during
    // code generation.  For each referenced Jump target, a new Label (Index
    // into LabelArr) is created.  When the address/offset of the Jump is
    // finally defined, it is stored in the respective LabelArr[Index].  These
    // are initially defined as the index of the target ByteCode, but are
    // transformed into an actual byte offsets in the last ASM phase and
    // written into the appropriate Jmp/Br ByteCode.
    //
    // Not all implicit "conceptual" labels are referenced/defined...  in many
    // cases code just falls through naturally to the next sequence, so no
    // Jmp/Br is needed.  Explicit labels are defined by the scripter as the
    // intended targets of GOTO.  Explicit and implicit labels may indicate the
    // exact same ByteCodes.

    // SC_JSet are used at compile time to define a hierarchy of logical escapes
    // when evaluating AND/OR logic... e.g. Escape if the first conjunct is false.
    // These are allocated as locals on the C call stack... not on a slab.
    
    typedef enum {
	SC_JSETNOFLAG		= 0x0000,
	SC_JSETTOPFLAG		= 0x8000,	// Top level of Logical op nesting
	SC_JSETMIDFLAG		= 0x4000,	// This OP is MidSet for superior Log Op
	SC_JSETANDFLAG		= 0x0001,	// This is an AND op (Conjunction)
    } SC_JSetFlags;

    typedef struct _SC_JSetRecord {
	Uns16			Flags;
	Uns32			L[2];		// FalseL is L[0], TrueL is L[1]
	_SC_JSETPOINTER		UpP;		// JSet in caller (superior)
    } SC_JSetRecord, * SC_JSetPointer;

    // CGRecord aggregates various fields needed for Reading, Parsing, and
    // CodeGen.  LastLexP correspond to the last nested (most recent) block of
    // code being processed.  Storage (tokens, etc) are allocated in VM memory.

    typedef enum {
	SC_CGNOFLAG		= 0x0000,
	SC_CGLOGOPISMIDFLAG	= 0x0010,	// Only for LOGAND/LOGOR
	SC_CGNEEDSEMIFLAG	= 0x0001,
	SC_CGPRINTCRFLAG	= 0x1000,	// Print display mechanism
	SC_CGINTERACTFLAG	= 0x8000,	// INTERACT mode compile
    } SC_CGFLAGS;

    typedef struct _SC_CGRecord {
	Uns16			Flags;
	Uns16			InGZCount;	// Number of InGlobals that need zeroing

	// Fields for Display... AST and ByteCodes
	Uns32			DispLabel;	// Label at which to start display... or 0
	SC_LexPointer		DispLexP;	// FuncLexP showing ByteCode Disassembly (Debug)

	// Fields for Check + CodeGen process
	SC_LexPointer		TopLexP;	// Lex for TOP function
	SC_LexPointer		LastLexP;	// Last LexP
	SC_LexPointer		FuncLexP;	// Current FUNC LexP (Could be Top or Last!)
	SC_JSetPointer		CallerJSetP;	// Jmp/Br labels for Cond/Logic/Loops

	// Fields for Op PARSE (Precedence + Associativity)... Grow from right to left!
	SC_TokenPointer		LastArgP;	// REVERSE list of Args, Next is really Prev
	SC_TokenPointer		TopOpP;		// Top of Op stack, Next is prev on stack

	// Fields for READ process
	char *			ScriptStartP;	// Start of script
	char *			ScriptEndP;	// End of script (first byte after)
	char *			ReadStartP;	// Read from here (on next cycle)
	char *			ErrorStartP;	// Points to Error loca in Script!
	char *			LastEndP;	// Only for non-comment & non-whitespace
	char *			ErrorStrP;	// NULL if no error
	char *			ErrorArgStrP;	// Only if ErrorStrP needs an arg

	SC_VMPointer		VMP;		// VM Pointer for all of SC
	SC_ScriptPointer	ScriptP;	// Script being compiled!
	SC_ArrDataPointer	InGZADP;	// INTERACT MODE: InGlobals that need zeroing!

    } SC_CGRecord, * SC_CGPointer;
    
    // ByteOpRecords are structured analogs of ByteCodes.  These records are
    // used during CodeGen then serialized/flattened in the final ASM stage.

    typedef enum {
	SC_BCNOFLAG		= 0x0000,
	SC_BCJMPFLAG		= 0x0001,	// Jmp, BrT, or BrF
	SC_BCGOTOFLAG		= 0x0002,	// GOTO jmp ByteOp
	SC_BCBEGMAPFLAG		= 0x8000,	// Beginning of MapI/K loop
	SC_BCENDMAPFLAG		= 0x4000,	// End of MapI/K loop
    } SC_BCFlags;

    typedef struct _SC_VDescRecord {
	Uns8			Class;		// What kind of Variables: L, C, R, Stack, Refer, or Konst
	Uns8			Index;		// Which register 0..N (For Konst or Index, gives LENGTH)
    } SC_VDescRecord, *SC_VDescPointer;

    typedef struct _SC_ByteOpRecord {
	Uns32			Address;	// Location computation in ASMPrePass
	Uns32			GotoMECount;	// How many MLoops does goto Exit!
	Int32			KVal;		// K/K2/K3 values
	Uns16			Flags;
	Uns16			ArgCount;	// Args in Op (0..3)
	Uns8			Op;		// Op itself
	Uns8			ByteLen;	// ByteLen of Op (1..4)
	SC_VDescRecord		Vars[3];	// Class/Index for up to 3 vars
    } SC_ByteOpRecord, *SC_ByteOpPointer;
    // Compiler LL aligns to 24 bytes per record!

typedef enum {
    // Results returned UP the call chain
    SC_ERR_RES			= 0x0000,	// Abort and report
    SC_SUCC_RES,				// All is good
    SC_END_RES,					// Semicolon, end
    SC_CALL_RES,				// Just called a func

    // Actions for new calls (DOWN the chain)
    SC_BEGIN_ACT		= 0x4000,	// Begin a statement...
    SC_GETARG_ACT,				// Need an Arg
    SC_GETOP_ACT,				// Need an Op
    SC_GETCOND_ACT,				// Need a parenthesized expression
    SC_GETFUNC_ACT,

    // Actions for checks
    SC_CHECKTOP_ACT		= 0x4000,
    SC_CHECKEXPR_ACT,
    SC_CHECKDEF_ACT,		// Must be 1+ SC_CHECKEXPR_ACT
    SC_CHECKEND_ACT,    
} SC_PARSE_CODES;

// +++ SC_CHECKDEF_ACT is used only when checking the body of a DEF declaration.
//     Since DEF are resolved at compile time, they can only reference constant
//     literals or previously defined DEF accessible to the block.

typedef struct _SC_KRecord {
    Int16	Flags;
    Int16	IsInt;
    Int64	Val;
 } SC_KRecord, *SC_KPointer;

// CodeBlocks store the object code in memory during runtime.  A given CodeBlock
// can have many Closures and each can have many RunRecords!

// NOTE: When a CodeBlockRecord is formed for THIS function, it has to specify
//	 what closed variables it should inherit from its SUPERIOR (container)
//	 function.  *BUT* the SUPERIOR is not finished yet, so cannot know how
//	 many vars it has, and whether L or C.  Since the indexing cannot be
//	 unified, THIS function has to say "Take L[14] along with C[3] and C[9]"
//	 instead of just saying take vars 21, 33, and 39.  Therefore CVar
//	 VDescRecords are stashed below instead of just Uns8 Var Id!!

typedef enum {
    SC_CB_NOFLAG	= 0x00,
    SC_CB_TOPFLAG	= 0x80,		// TOP function!
    SC_CB_ALLARGSFLAG	= 0x01,		// Stash all call args in an Array
} SC_CB_FLAGS;

typedef struct _SC_CodeBlockRecord {	// Allocated on CHUNK slabs, will not move
    Uns8		Type;		// SC_CHUNK_CODE: Required for CHUNK slab
    Uns8		State;
    Uns8		Flags;
    Uns8		RCount;		// Register count (for accessing globals)
    Uns32		RefCount;	// GC
    
    Uns32		BlockLen;	// On CHUNK slab
    Uns32		CodeLen;	// Byte count (Padded to LongAlign)
    
    Uns32		FuncId;		// Index in superior FuncArr[]
    Uns32		FuncCount;	// How many sub-func
    
    Uns16		InCount;	// Specified (named) input (Val) count
    Uns16		OutCount;	// Specified (named) output (Ref) count
    Uns16		LCount;		// LVars: includes In + Out spec Count
    Uns16		CCount;		// CVars: 0 if TOP
    
    SC_DictVPointer	CaseDictP;	// For any Func that has a SWITCH
    Uns32		CodeId;		// Id assigned to each CBRec
    Uns32		CodeArr[0];	// CodeLen Bytes (ByteCode Ops)
    //			...
    //			Pad with 0 to Long align (Reflected in CodeLen)
    //			CVar Desc	Class+Index  (Read NOTE above)
    //			CVar Desc	Class+Index
    //			...
    //			Pad with 0 to LongLong align
    //SC_CodeBlockPtr	FuncArr[]	SUB functions, indexed by their FuncId

} SC_CodeBlockRecord, *SC_CodeBlockPointer;

// NOTE: VarArr[0] is CodeBlockPointer...
//	 Ops do NOT use 0x00 as a V index, it means POP/PUSH on stack.
//	 Ops use 0xFF to mean "REFerence", generally for Var elt or Dict Pair.

typedef enum {
    // ORDER IS IMPERATIVE*********Matches SC_BYTE_CODE*****
    // For Math SubOps...
    //
    SC_RUN_ADD		= 0,		// 4 Variations of each
    SC_RUN_SUB		= 4,
    SC_RUN_MUL		= 8,
    SC_RUN_DIV		= 12,
    SC_RUN_MOD		= 16,
    SC_RUN_SHL		= 20,
    SC_RUN_SHR		= 24,
    SC_RUN_LT		= 28,
    SC_RUN_LTQ		= 32,
    SC_RUN_EQ		= 36,
    SC_RUN_NEQ		= 40,
    SC_RUN_BAND		= 44,
    SC_RUN_BOR		= 48,
    SC_RUN_BXOR		= 52,
    
    // For Log SubOps...
    SC_RUN_MNEG		= 56,		// 2 Variations!!
    SC_RUN_LPOS		= 58,
    SC_RUN_LNEG,
    SC_RUN_BNEG,
} SC_SUBOP_TABLE_INDEX;

typedef enum {
    SC_RUN_NOERR		= 0,
    SC_RUN_EXIT,		// Script calls Sys.Exit
    SC_RUN_UEXIT,		// User calls Exit from Ed
    SC_RUN_QUIETEXIT,		// No error messages!
    SC_RUN_USERERR,
    SC_RUN_ABORTERR,
    SC_RUN_BADARG,
    SC_RUN_ERRTOOMANYARG,
    SC_RUN_NODICT,
    SC_RUN_NOARR,
    SC_RUN_NOINDEX,
    SC_RUN_NOCLOS,
    SC_RUN_BADRCP,
    SC_RUN_TOPYIELD,
    SC_RUN_NOSUBMIT,
    SC_RUN_SUBMITAGAIN,
    SC_RUN_INTERNAL,

    SC_RUN_ERRCOUNT
} SC_RUN_ERROR;

char *		SC_RunErrorArr[] = {"User Abort",	// Otherwise NOERR
				    "Script Called Exit",
				    "User Called Exit",
				    "Quiet Exit",
				    "User Error",
				    "User Abort",	// Did set error!
				    "Bad Arg",
				    "Too Many Args/internal overflow",
				    "Dict Not Specified",
				    "Array Not Specified",
				    "Cannot Index Arg",
				    "No Func To Call",
				    "Bad RC",
				    "Main function cannot SUBMIT/YIELD",
				    "Coroutine did not SUBMIT",
				    "Coroutine cannot SUBMIT again",
				    "Internal error"};

char *		SC_UserErrorStr = NULL;
char *		SC_RunErrorInfoStr = NULL;

#define		SC_RUNINFO_SLEN	128
char		SC_RunInfoS[SC_RUNINFO_SLEN];


// RC (RunCall) records are structured like SC_PairHRecord and SC_StrHRecord and
// allocated on the BigEntry slab.
//
// an RC specifies a run (call) stack... generally just one RunP but could be a
// whole (call) chain if using non-local Return (or Yield for CoRoutines).
//
// Each RunP can have ONLY one RCP, and will give it a RefCount of 1.  The RCP
// can remain when the RunP is gone, because the Script can stash the RCP for
// the RunP (obtained from 'Call') into a Var.  RunP and its RCP form an obvious
// RefCount cycle (RefCount GC cannot purge cycles!), so get special handling to
// avoid this.
//
// NOTE: RCP and its RunP stack should be purged IFF RCP has a ResumeP and its
//	 RefCount decrements to *ONE* (not Zero)!!!
//
//	 The RCP will have a non-null ResumeP if it corresponds to a Yielded
//	 CoRoutine (or stack of them).  In these cases, a RefCount of 1 means
//	 the script has lost access to the RCP, so can NEVER resume the RunP!

typedef enum {
    SC_RC_NOFLAG =		0x0000,
    SC_RC_SUBMITFLAG =		0x8000,
} SC_RUNCALL_FLAGS;

typedef struct _SC_RCRecord {
    Uns8		Type;		// SC_ENTRY_RC
    Uns8		State;
    Uns16		Flags;
    Uns32		RefCount;	// Always in same offset, matches VarVRecord.
    
    _SC_RUNPOINTER	RunP;		// RunP for this RC Record
    _SC_RUNPOINTER	ResumeP;	// Resume here... RunP or below it
    SC_ArrDataPointer	StackCopyP;	// RunStack copy is stashed here
} SC_RCRecord, *SC_RCPointer;				    

typedef enum {
    SC_RUN_NOFLAG	= 0x0000,
    SC_RUN_ALLARGSFLAG	= 0x0001,	// Will have 2 extra args
    SC_RUN_NOTRECOPT	= 0x8000,	// Do not Tail-Rec Optimize
} SC_RUN_FLAGS;

// RunRecord stores a Ptr for each local variable (including Registers, In/Out
// params and closed-over ones.)  A RunRecord is really a traditional
// "RunFrame", just not allocated on a stack.
//
// ???? Running RunRecord: Inc RefCount for CBP... then Dec when done.
//
// NOTE: VarCount DOES NOT include the extra two Array ptrs added for InArgArr
//       and OutArgArr.  These are included ONLY if SC_RUN_ALLARGSFLAG is set.
//       These will point to an empty Arr *IF* func was called with no args.
//
//	 RunP has VarArr for SPECIFIED (from CBP) InCount/OutCount... it does
//	 NOT have VarArr for actual In/Out params In EXCESS of specified ones,
//	 as the ByteCodes will not know to use them!  But extraneous call args
//	 will be placed in InArgArrP/OutArgArrP if SC_RUN_ALLARGSFLAG.

    typedef struct _SC_UPSRecord {
	Uns16	InCount;		// UnPacked N args for IN list
	Uns16	OutCount;		// UnPacked M args for OUT list
    } SC_UPSRecord, * SC_UPSPointer;

    #define SC_UPSEltsPerSlot	2	// 2 UPSRecord in each LongLong

    // NOTE: UPSTop is only an Uns8... up to 254 UPS stack elts.
    //	     This means up to 255 *NESTED* levels of UnPacked function calls!

typedef struct _SC_RunRecord {		// Allocated on CHUNK slabs, will not move
    Uns8		Type;		// SC_CHUNK_RUN: Required for CHUNK slab
    Uns8		State;
    Uns16		Flags;
    Uns16		InCount;	// In this call, not CBP->InCount
    Uns16		OutCount;	// In this call, not CBP->OutCount
    
    Uns32		BlockLen;	// For CHUNK slab
    Uns32		PC;		// Offset into CodeBlock.CodeArr for NEXT op
    
    Uns8		UPSTop;		// Top of UnPackStack--0 means empty!
    Uns8		RCount;		// REG count (from CBP)
    Uns16		VarCount;	// 1 + Reg + IN/OUT + Locals + CVars (From CBP)
    Uns32		InitStackTop;	// Initial stack top, Usually 0

    _SC_RUNPOINTER	CallerP;	// Calling (superior) RunFrame (Or NULL)
    SC_RCPointer	RCP;		// Ptr to RC (Or NULL)
    SC_ArrDataPointer	UPSADP;		// UnPackStack ADP--NULL unless needed

    // Could have explicit CBP field... but in ByteCodes, Var[0] means STACK (Push),
    // so easier to have another use for VarArr[0]!
    SC_VarVPointer	VarArr[0];	// [0] *** CBP ****!
    //			VarArr[1];	// Regs (Matches RCount)
    //			...		// InArgs (CBP->InCount)
    //			...		// OutArgs (CBP->OutCount)
    //			...		// LArgs (Non-param Locals, excluding CArgs in CBP)
    //			...		// CVars Copied from Closure (Count from CBP)
    //	    		InArgArrP	// Array of ALL InArgs, *IFF* SC_RUN_ALLARGSFLAG
    //	    		OutArgArrP	// Array of ALL OutArgs, *IFF* SC_RUN_ALLARGSFLAG

} SC_RunRecord, *SC_RunPointer;

typedef struct _SC_GlobRecord {		// Allocated on CHUNK slabs, will not move
    Uns8		Type;		// SC_CHUNK_GLOB: Required for CHUNK slab
    Uns8		Flags;
    Uns16		Count;		// How many
    Uns32		BlockLen;	// For CHUNK slab
    Uns64		Res;		// To bring MIN blockLen to 16 ****
    SC_VarVPointer	GArr[0];	// Ptr for each of Count globals!
} SC_GlobRecord, *SC_GlobPointer;
// **** Minimum alloc size must be 16 bytes or more!!!

typedef struct	_SC_StackBlockRecord {
    Uns8		Type;		// SC_CHUNK_STAK;
    Uns8		Flags;
    Uns16		Count;		// Max capacity
    Uns32		BlockLen;	// For CHUNK slab
    // BELOW min size if allocated with 0 Data!! ****
    SC_VarVRecord	Data[0];	// Count actual records, NOT pointers!
} SC_StackBlockRecord, *SC_StackBlockPointer;

// Sytem runs with at least 1 pkg (the Main pkg, Index==0).
// Other pkgs provide for their own segregated name space, Sys, Pat, etc.
//
// Current implementation is partial...  Can only create Packages in C source
// code for now!

typedef struct _SC_PkgRecord {
    Uns8		Type;		// SC_ENTRY_PKG (4 LLs so on BEnt slabs)
    Uns8		State;
    Uns16		Flags;
    Uns32		RefCount;
    
    Uns32		Index;		// 0 is MAIN/SYS script
    Uns32		GlobCount;
    SC_DictVPointer	NDictP;		// Name(space) Dict
    SC_GlobPointer	GlobP;		// Globals
} SC_PkgRecord, *SC_PkgPointer;

// VMP->PkgADP->Data is an array of SC_PkgPointer slots.  ADP states capacity in
// SlotCount, VMP->PkgCount is number of actual pkgs.
//
// VMP->CFPADP will have a list of C Func pointers defined by various pkgs.
// ADP states capacity in SlotCount, VMP->CFPCount is number used.
//
// NDictP provides NAMES for the following;
//	ValueType	For:
//      ---------       -----------
//	SC_VAL_INT	DEF Integer
//	SC_VAL_FLT	DEF Float
//	SC_VAL_PTR	DEF String
//	SC_VAL_FIDX	C Func Ptr Index (Into VMP->CFPADP)
//	SC_VAL_IIDX	InternalFunction Id (MAIN Pkg[0])
//	SC_VAL_GIDX	Glob Index (Into matched Glob)
//	SC_VAL_PIDX	Pkg Index (Into VMP->PkgADP)**
//
// ** SC_VAL_PIDX is used only in NDictP of the MAIN [Index 0] Pkg.


// ???? Just have *ONE* GC client function, 1 to Mark, 0 to Zap!


// EXT data needs an ExtType, which indexes into two funcPtr arrays to call
// client for marking and zapping in GC.
//
// NOTE: SC_EXTGCArr[0] is called to initiate Marking + GC, ignores return value

typedef enum {
    SC_EXT_NONE		= 0,
    SC_EXT_BRTIME,
    // **********************
    SC_EXT_COUNT,
} SC_EXT_TYPECODE;

char *		SC_EXTNameArr[] = {"None", "BrokenTime"};

		// Call for EXT_TYPECODE when marking... Mark/GC links, NOT the VarP itself!
		// Just Mark if MarkNotZap, otherwise ZAP!
		// Mark and Zap EXTERNAL data, system will handle the VarP itself!
		// Return 1 to mean proceed with Marking/Zapping VarP, 0 to leave it be.
		// (If NOT marked, VarP will get GC'ed!)
typedef Int16	(*SC_EXTGCFuncPointer)(SC_VMPointer VMP, SC_VarVPointer VarP, Uns8 MarkNotZap);

SC_EXTGCFuncPointer		SC_EXTGCArr[SC_EXT_COUNT];



#undef _SC_CGPOINTER
#undef _SC_MAPPOINTER
#undef _SC_LEXPOINTER
#undef _SC_VEXPOINTER
#undef _SC_JSETPOINTER
#undef _SC_RUNPOINTER
#undef _SC_GLOBPOINTER
#undef _SC_SCRIPTPOINTER
#undef _SC_KONSTPOINTER
#undef _SC_CODEBLOCKPOINTER
#undef _SC_STACKBLOCKPOINTER

// Bytes codes for CodeGen
//	R = 0 means Push/Pop stack
//	R = 255 means Stack has Entry REF, write to IT
//	K is a 1 byte Int Konstant
//     K2 is a 2 byte Int Konstant	I2 is 2 byte Uns Index
//     K3 is a 3 byte Int Konstant	I3 is 3 byte Uns Index
//
// Given ADD_VVV the first V is A, the second B...
// So semantics is A = B + C.  (Did not want to call Op ADD_ABC!)

typedef enum {
    SC_NOOP_CODE	= 0x00,		// DO NOTHING, use for Byte align!

    // Arithmetic
    // ********************************************************************************
    // ************* ORDER IS IMPERATIVE *********Matches SC_BYTE_CODE***** 
    // ********************************************************************************
    SC_ADD_VVV_CODE,			// A = B + C
    SC_ADD_sss_CODE,			// Push(Pop2 + Pop1)
    SC_ADD_VVK_CODE,			// A = B + K

    SC_SUB_VVV_CODE,			// A = B - C
    SC_SUB_sss_CODE,			// Push(Pop2 - Pop1)
    SC_SUB_VKV_CODE,			// A = K - B	(For VVK just use ADD and -K)

    SC_MUL_VVV_CODE,			// A = B * C
    SC_MUL_sss_CODE,			// Push(Pop2 * Pop1)
    SC_MUL_VVK_CODE,			// A = B * K

    SC_DIV_VVV_CODE,			// A = B / C
    SC_DIV_sss_CODE,			// Push(Pop2 / Pop1)
    SC_DIV_VVK_CODE,			// A = B / K

    SC_MOD_VVV_CODE,			// A = B % C
    SC_MOD_sss_CODE,			// Push(Pop2 % Pop1)
    SC_MOD_VVK_CODE,			// A = B % K

    SC_SHL_VVV_CODE,			//  A = B << C
    SC_SHL_sss_CODE,			// Push (Pop2 << Pop1)
    SC_SHL_VVK_CODE,			// A = B << K

    SC_SHR_VVV_CODE,			// A = B >> C
    SC_SHR_sss_CODE,			// Push (Pop2 >> Pop1)
    SC_SHR_VVK_CODE,			// A = B >> K
    // ********************************************************************************
    
    // Mem + PC
    SC_MOV_VV_CODE,			// A = B
    SC_MOV_VK2_CODE,			// A = Int (2 byte)
    
    SC_POP_V_CODE,			// A = Pop (V cannot be 0x00 or 0xFF)
    SC_PUTV_CODE,			// Put Val into REFER (Below it), leave VAL on stack
    SC_GETV_CODE,			// Replace REFER on stack with its VAL
    SC_POPN_K_CODE,			// Pop (N times)
    SC_POP1_CODE,			// Pop once

    SC_SWAP_CODE,			// Swap top 2 elts on stack
    SC_PSH_CODE,			// Duplicate top of stack
    SC_PSH_V_CODE,			// Push value in A  (V must be Var)
    SC_PSH_K_CODE,			// Push small Int (1 byte)
    SC_PSH_K3_CODE,			// Push big Int (3 bytes)
    SC_PSHI_V_CODE,			// Make value in A into Int and push on stack
    SC_PSHI_I3_CODE,			// Push K[I3]	Uns24 index into Int64 Konsts in CodeBlock
    SC_PSHF_V_CODE,			// Make value in A into Flt and push on stack
    SC_PSHF_I3_CODE,			// Push K[I3]	Uns24 index into *Double* Konsts in CodeBlock    
    SC_PSHG_I3_CODE,			// Push value in Global Var (I3 index)
    SC_PSHS_I3_CODE,			// Push StrHP from String Index (I3 index)

    SC_REF_V_CODE,			// Push Ref (Pts to Var)
    SC_REFG_I3_CODE,			// Push Ref (Pts to indexed (I3) Global)
    
    SC_RSET_VI2_CODE,			// REG A becomes Global[I2]
    SC_VSET_V_CODE,			// VAR A gets same storage as POP(Ref)  (A is NOT a REG)
    SC_ISET_VVV_CODE,			// A[B] =& C for Array *ONLY*

    SC_JMP_K3_CODE,			// PC += K3 (45)
    SC_BRT_K3_CODE,			// If (Pop == 1) PC += K3	Direct offset
    SC_BRF_K3_CODE,			// If (Pop == 0) PC += K3

    // Compare, results in 0 or 1
    SC_LT_VVV_CODE,			// A = (B < C) --> 0 or 1
    SC_LT_sss_CODE,			// Push(Pop1 < Pop2)
    SC_LT_VK2_CODE,			// Push(R < K2)
    SC_LT_K2V_CODE,			// Push(K2 < R)

    SC_LTQ_VVV_CODE,			// A = (B <= C) --> 0 or 1
    SC_LTQ_sss_CODE,			// Push(Pop1 <= Pop2)
    SC_LTQ_VK2_CODE,			// Push(R <= K2)
    SC_LTQ_K2V_CODE,			// Push(K2 <= R)

    SC_EQ_VVV_CODE,			// A = (B == C) --> 0 or 1
    SC_EQ_sss_CODE,			// Push(Pop1 == Pop2)
    SC_EQ_VK2_CODE,			// Push(R == K2)

    SC_NEQ_VVV_CODE,			// A = (B != C) --> 0 or 1
    SC_NEQ_sss_CODE,			// Push(Pop1 != Pop2)
    SC_NEQ_VK2_CODE,			// Push(R != K2)

    SC_IDNT_VVV_CODE,			// A = (B === C)	// Compares address not value
    SC_NDNT_VVV_CODE,			// A = (B !== C)	// ???? Test on Index + Dot!
    
    // ************* ORDER IS IMPERATIVE*********Matches SC_BYTE_CODE*****
    // Logic + Bitwise
    SC_POS_V_CODE,			// Push (LOGPOS A)
    SC_POS_VV_CODE,			// A = (LOGPOS B)
    
    SC_NEG_V_CODE,			// Push -A
    SC_NEG_VV_CODE,			// A = -B		SC_SUB_VKV  A = 0 - B

    SC_NOT_V_CODE,			// Push !A
    SC_NOT_VV_CODE,			// A = !B

    SC_BNEG_V_CODE,			// Push ~A
    SC_BNEG_VV_CODE,			// A = ~B

    SC_BAND_VVV_CODE,			// A = B & C
    SC_BAND_sss_CODE,			// Push (Pop2 & Pop1)

    SC_BOR_VVV_CODE,			// A = B | C
    SC_BOR_sss_CODE,			// Push (Pop2 | Pop1)

    SC_BXOR_VVV_CODE,			// A = B ^ C
    SC_BXOR_sss_CODE,			// Push (Pop2 ^ Pop1)
    // ********************************************************************************

    SC_SWCH_VI2_CODE,			// SWITCH(V) go to case, (I2 is Index/Id of this SWITCH)
    SC_FMAK_VI2_CODE,			// Make Func Closure and Load the Func (I2 is FuncId) into V
    SC_FBEG_CODE,			// Begin and run this Func
    SC_FEND_CODE,			// End this Func... return 0
    SC_FSUB_CODE,			// Co-routine SUBMIT to give caller its RCP.
    SC_FRET_CODE,			// Normal Return, assumes Value is on Stack
    SC_FRET_V_CODE,			// Normal Return--V may already be on Stack!
    SC_FRET_K_CODE,			// Returns small number
    
    SC_FRET_VV_CODE,			// Return value (in A) to caller of RCP (in B) (JumpReturn!)
    SC_FYLD_VV_CODE,			// Yield value (in A) to caller of RCP (in B) (CoRoutines)
    SC_FRES_V_CODE,			// Resume function with RCP in V (CoRoutines)
    SC_FREL_V_CODE,			// Release function with RCP in V (CoRoutines)
    SC_FGET_V_CODE,			// V gets RCP for cur call (Non-loc return)    
   
    SC_FCALL_VK2_CODE,			// Call Func in V, K2 = ((#Output << 8) + #Input) args
					// (Pop FuncPtr off stack if V is 0)
    SC_UFCAL_VK2_CODE,			// UnPack FCALL... adds in UnPacked arg count from RunP

    SC_SCALL_IK2_CODE,			// Call Sys func I, K2 = ((#Output << 8) + #Input) args
    SC_USCAL_IK2_CODE,			// UnPack SCALL... adds in UnPacked arg count from RunP
    SC_UPS_CODE,			// UnPack Start, call to init arglist counts in RunP
    SC_UPI_CODE,			// Unpack top of stack if ARR or DICT (count as Input)
    SC_UPO_CODE,			// Unpack top of stack if ARR or DICT (Count as Output)    

    SC_ANEW_V_CODE,			// V gets new Array[POP] -- 0 means default len
    SC_AINI_V_CODE,			// Pop[Pop] = V, then Push ArrayPtr, then Push ++Index
    // Provide SC_AINI_K_CODE (and K3) variant for fast ASet of Ints!

    SC_DNEW_V_CODE,			// V gets new DICT[POP] -- 0 means default len
    SC_DINI_CODE,			// Pop.Pop = Pop, Then Push DictPtr back on

    SC_IREF_VV_CODE,			// Push &B[C] for Array and Dict (Push &(B.C))
					// --> Add Array (elt) or Dict (pair) if not there
					// --> Pushes Ref on stack
    SC_IREF_VI2_CODE,			// Push &B[I2] for Array and Dict (Push &(B.K2))

    SC_ZREF_VV_CODE,			// Same as IREF variant, but push NULL if elt/pair missing
    SC_ZREF_VI2_CODE,			// Ditto  (These are used for === and !==)
    
    SC_IGET_VVV_CODE,			// A = B[C] for Array and Dict (A = B.C)
    SC_IGET_VVK_CODE,			// A = B[K] for Array and Dict (A = B.K)
    SC_IMAP_CODE,			// MAP iteration... needs 4 args on the stack!
    SC_KMAP_CODE,			// Just like IMAP, only skips nulls in ArrDataP

    SC_CODE_COUNT,			// Valid Ops range from 0 to (SC_CODE_COUNT - 1)
    //------------------------------------------------------------------------    


    // Special Debug (future use) ByteCodes, NOT for execution!
    // SC_LOC_CODE,			// Marks Source Line K, Char J
    // SC_LOC2K_CODE,			// ... Only in DEBUG version!
    // SC_LOC4K_CODE,
} SC_BYTE_CODE;

// ********************************************************************************
// ********************************************************************************
// ********************************************************************************
// String Processing Patterns and Matching


typedef enum {

    // Top level Pats
    SC_PAT_SEQ =	0x00,		// Seq of Pats/Strs (Case INsensitive for Strs)
    SC_PAT_ALT,				// Ordered list of  sub Pats/Strs in order
    SC_PAT_SPAN,			// From-To Pat
    SC_PAT_REP,				// Repeat 0..N times
    SC_PAT_CHAR,			// Set of chars and Ranges to match
    SC_PAT_GO,				// Go +/-N chars, ABS or REL
    SC_PAT_AT,				// Predicate form of GO

    SC_PATCOUNT,

    // BAL ()[]{}			// Goes until balances Paren/Delimiter
    // BORDer from NOT in CharSet to IN (Or opposite if NEG)
    
    // INTERNAL Use Only
    SC_PAT_EXTR =	0x80,		// Extra String, Pat, or Func!
    SC_PAT_CMAP,			// Ascii char bit map (0..127)
    SC_PAT_CRANGE,			// UTF8 char range array

    // STASH Pats come right after the Main Pat and before any EXTR on the NextP list.
    SC_PAT_STASH_STRS,			// Stashes PosRange (Data[0]) and/or Str (Data[1])
    SC_PAT_STASH_NUMBS,			// Stashes Ord (Data[0]) and/or Count (Data[1])
    SC_PAT_STASH_LENS,			// Stashes Len1 (Data[0]) and/or Len2 (Data[1])
    SC_PAT_STASH_LABEL,			// Stashes Label (Data[0]) string
} SC_PAT_OPS;

char *	SC_PatStrArr[] =	{"Seq", "Alt", "Span", "Rep", "Char", "Go", "At"};

// SC_PAT_FLAG are internal flags in SC_PatRecord.
typedef enum {
    SC_PAT_NOFLAG =	0x00,
    SC_PAT_DATA0P =	0x01,		// Data0P is for a var entry
    SC_PAT_DATA1P =	0x02,		// Data1P is for a var entry
    SC_PAT_DATAMASK =	0x03,

    SC_PAT_SENFLAG =	0x04,		// Case SENsitive if set
    SC_PAT_ABSFLAG =	0x04,		// ABS move/positioning if set
    SC_PAT_NEGFLAG =	0x08,		// NEG polarity if set

    SC_PAT_MINFLAG =	0x10,		// MIN Reps (satisficing) if set
    SC_PAT_VFLAG =	0x10,		// V move/position if set
    SC_PAT_DYNFLAG =	0x20,		// DYNamic (Str is PVar name) if set
    SC_PAT_CONSFLAG =	0x40,		// CONSiderate (not Selfish) if set
    SC_PAT_NRESFLAG =	0x80,		// Do NOT put on RStack
} SC_PAT_FLAG;


typedef struct _SC_PatRecord {
    Uns8		Type;		// SC_ENTRY_PAT on BEnt slab
    Uns8		State;		// GC/Mem Mgmt
    Uns8		Flags;
    Uns8		PatOp;		// Pattern operation
    Uns32		RefCount;	// GC/Mem Mgmt

    void *		NextP;		// Next Pat on this level
    void *		Data[2];	// Data/String/etc. for this Pat
} SC_PatRecord, *SC_PatPointer;

    // For convenience only, not allocated.
    typedef struct _SC_PatCMapRecord {
	Uns8			Type;		// SC_ENTRY_PAT
	Uns8			State;
	Uns8			Flags;
	Uns8			PatOp;		// SC_PAT_CMAP
	Uns32			RefCount;

	Int64			Res;		// Will never have a NextP
	Uns8			Data[16];	// 128 bit char map
    } SC_PatCMapRecord, *SC_PatCMapPointer;

    typedef struct _SC_PRange {
	Uns32			Beg;		// From beginning
	Uns32			End;		// ... to end, inclusive
    } SC_PRange, *SC_PRangePointer;
    
    typedef struct _SC_PatCRangeRecord {
	Uns8			Type;		// SC_ENTRY_PAT
	Uns8			State;
	Uns8			Flags;
	Uns8			PatOp;		// SC_PAT_CRANGE
	Uns32			RefCount;

	Int64			SlotsUsed;	// No NextP!  (Slots used on ADP)
	SC_ArrDataPointer	ADP;		// Array of MinMax range cells
	SC_PRange		Range;		// Aggregate Min/Max for all Unicode
    } SC_PatCRangeRecord, *SC_PatCRangePointer;
    // NOTE:  Range is initally (Uns32)-1L -to- 0L (Beg is illegal value).
    //	      0L to 0L would not work, as every Unicode is greater than 0x0000!!

   
// ********************************************************************************
// ********************************************************************************
// ********************************************************************************

SC_VMRecord	SC_VM;
Uns16		SC_InitFactor;		// 1x or more!

// ********************************************************************************

char *		SC_STR_ErrorBlockCommentClose		= "Block comment not properly closed";
char *		SC_STR_ErrorLongStringOpen		= "Bad long string opening delimiter";
char *		SC_STR_ErrorLongStringClose		= "Long string not closed";
char *		SC_STR_ErrorStringClose			= "String not properly closed";
char *		SC_STR_ErrorStringCharSpec		= "Bad char specification";
char *		SC_STR_ErrorStringDecChar		= "Bad decimal char specification";
char *		SC_STR_ErrorStringHexChar		= "Bad hexadecimal char specification";
char *		SC_STR_ErrorStringOctChar		= "Bad octal char specification";
char *		SC_STR_ErrorStringUniChar		= "Bad Utf8 char specification";
char *		SC_STR_ErrorStringBadEscape		= "Bad escape char in string";
char *		SC_STR_ErrorNumb			= "Bad number";
char *		SC_STR_ErrorIdentTooLong		= "Identifier is too long";
char *		SC_STR_ErrorBadOp			= "Bad operator";
char *		SC_STR_ErrorBadCall			= "Cannot call this internal function here";
char *		SC_STR_ErrorBadFuncDef			= "Cannot define a function here";
char *		SC_STR_ErrorMissingArg			= "Missing argument";
char *		SC_STR_ErrorMissingOp			= "Missing operator";
char *		SC_STR_ErrorIFIDUnPack			= "Cannot use @ with internal (pseudo) functions";
char *		SC_STR_ErrorBadArg			= "Bad argument";
char *		SC_STR_ErrorTooManyArg			= "Too many arguments";
char *		SC_STR_ErrorBadAlias			= "Cannot alias to Global";
char *		SC_STR_ErrorBadEnd			= "Unexpected end";
char *		SC_STR_ErrorNotString			= "Unexpected string";
char *		SC_STR_ErrorNotNumb			= "Unexpected number";
char *		SC_STR_ErrorNotIdent			= "Unexpected identifier";
char *		SC_STR_ErrorMissingPOpen		= "Missing open parenthesis";
char *		SC_STR_ErrorMissingPClose		= "Missing close parenthesis";
char *		SC_STR_ErrorMissingAOpen		= "Missing open bracket";
char *		SC_STR_ErrorMissingAClose		= "Missing close bracket";
char *		SC_STR_ErrorEmptyArray			= "Illegal empty array";
char *		SC_STR_ErrorBadKeyword			= "Illegal keyword";
char *		SC_STR_ErrorBadCond			= "Bad condition expression";
char *		SC_STR_ErrorConsMismatch		= "Bad constructor entry";
char *		SC_STR_ErrorMissingWhile		= "Missing while after Do";
char *		SC_STR_ErrorMissingCase			= "Switch has no case";
char *		SC_STR_ErrorBadCase			= "Case is outside Switch";
char *		SC_STR_ErrorBadDefault			= "Duplicate Default";
char *		SC_STR_ErrorIdentCase			= "Duplicate Case";
char *		SC_STR_ErrorIfNotGuard			= "If will not guard";
char *		SC_STR_ErrorMissingSemi			= "Missing semi-colon";
char *		SC_STR_ErrorMissingOutput		= "Missing output list";
char *		SC_STR_ErrorBadParam			= "Bad parameter";
char *		SC_STR_ErrorExistingVar			= "Declaring existing variable";
char *		SC_STR_ErrorIllegalName			= "Cannot use Pkg name";
char *		SC_STR_ErrorLimits			= "Exceeding internal limits";
char *		SC_STR_ErrorBadVar			= "Unknown variable";
char *		SC_STR_ErrorBadDef			= "Unknown Def";
char *		SC_STR_ErrorBadIdent			= "Unknown Identifier";
char *		SC_STR_ErrorDupeDef			= "Duplicate Def";
char *		SC_STR_ErrorSysDef			= "System Def";
char *		SC_STR_ErrorDupeLabel			= "Duplicate label";
char *		SC_STR_ErrorMissingLabel		= "Missing label";
char *		SC_STR_ErrorTooDeep			= "Code block is too deep";
char *		SC_STR_ErrorDupeKey			= "Duplicate Key";
char *		SC_STR_ErrorMissingFunc			= "Declared but not defined";
char *		SC_STR_ErrorGotoIntoMap			= "Goto cannot jump into a Map block";
char *		SC_STR_ErrorGotoOutOfMap		= "Goto is jumping out of TOO MANY Map blocks";

char *		SC_STR_ErrorNoRun			= "Cannot run";
char *		SC_STR_ErrorNotNumber			= "Bad value, not number";

char *		SC_STR_ErrorBadGCBlock			= "GC Error, bad block";

Uns64		SC_KEYCODEMAP[SC_KEYCODE_COUNT];

// ********************************************************************************
// ********************************************************************************
// ********************************************************************************
// DEBUG


#ifdef DEBUG

    #define	SC_DEBUG_HASHPRINTSTATUS	SC_StrHashPrintStatus(VMP, 0)
    #define	SC_DEBUG_SLABGUARD		0xABCDABCDABCDABCD
    #define	SC_DEBUG_CHECKSLAB(S)		SC_SlabCheckGuard(S)

    Int16	SC_DEBUG_ShowParseToken = 0;	// 1 means print out tokens

    #define	SC_DEBUG_PARSEINIT(_VP)		SC_ParseDebugInit(_VP)
    #define	SC_DEBUG_PARSEPRINT(_CGP, _TP)	SC_ParseDebugPrint(_CGP, _TP)
    #define	SC_DEBUG_PARSEEND(_VP)		SC_ParseDebugEnd(_VP)

    Int16	SC_DEBUG_ShowAST = 1;		// 1 means print out Abstract Symbol Tree
    Int16	  SC_DEBUG_ShowLex = 0;		// As part of ShowAST
    Int16	  SC_DEBUG_ShowCaseDict = 1;	// As part of ShowAST
    Int16	SC_DEBUG_ShowCode = 1;		// 1 enables SC_CGDisplayCode
    Int16	SC_DEBUG_ShowLabel = 1;		// 1 enables SC_CGDisplayNamedLabel
    Int16	SC_DEBUG_ShowFunc = 1;		// 1 enables SC_RunDisplayCB

    #if SC_DEBUG_COMPILE_DISP

	#define SC_DEBUG_STARTPRINT(_VMP, _T, _NP, _L)	SC_CGAnnounce(_VMP, _T, _NP, _L)
	#define	SC_DEBUG_ASTPRINT(_CGP, _TP)		if (SC_DEBUG_ShowAST) SC_ParsePrintAST(_CGP, _TP)
	#define	SC_DEBUG_CODEPRINT(_CGP)		if (SC_DEBUG_ShowCode) SC_CGDisplayCode(_CGP)
	#define	SC_DEBUG_LABELPRINT(_CGP, _TP)		if (SC_DEBUG_ShowLabel) SC_CGDisplayNamedLabel(_CGP, _TP)
	#define	SC_DEBUG_FUNCPRINT(_CGP, _SP, _BP)	if (SC_DEBUG_ShowFunc) SC_RunDisplayCB(CGP, CGP->VMP, _SP, _BP, 1, 0)

    #else

	#define SC_DEBUG_STARTPRINT(_VMP, _T, _NP, _L)
	#define	SC_DEBUG_ASTPRINT(_CGP, _TP)
	#define	SC_DEBUG_CODEPRINT(_CGP)
	#define	SC_DEBUG_LABELPRINT(_CGP, _TP)
	#define	SC_DEBUG_FUNCPRINT(_CGP, _SP, _BP)
    #endif
    
#else

    #define	SC_DEBUG_HASHPRINTSTATUS	// Do nothing if not DEBUG
    #define	SC_DEBUG_CHECKSLAB(S)		// Do nothing if NOT DEBUG

    #define     SC_DEBUG_STARTPRINT(_VMP, _T, _NP, _L)
    #define	SC_DEBUG_PARSEINIT(_VP)
    #define	SC_DEBUG_PARSEPRINT(_CGP, _TP)
    #define	SC_DEBUG_PARSEEND(_VP)

    #define	SC_DEBUG_ASTPRINT(_CGP, _TP)
    #define	SC_DEBUG_CODEPRINT(_CGP)
    #define	SC_DEBUG_LABELPRINT(_CGP, _TP)
    #define	SC_DEBUG_FUNCPRINT(_CGP, _SP, _BP)
    
#endif

// ********************************************************************************
// ********************************************************************************
// ********************************************************************************
// UTILITY & ERROR

#define	SC_TABSPACES(_C)		(((((_C) / 8) + 1) * 8) - (_C))

#define	SC_LLALIGN(L)			(((L) + 7) & ~7)
#define SC_LALIGN(L)			(((L) + 3) & ~3)
#define SC_WALIGN(L)			(((L) + 1) & ~1)

#if SC_DEBUG_CALLDEBUGGER
    #define SC_CALLDEBUGGER		G_RAISEDEBUG
#else
    #define SC_CALLDEBUGGER
#endif

#define	SC_PARSEERROR(_CGP, _Pos, _S)	{SC_CALLDEBUGGER;						\
					 _CGP->ErrorStartP = _CGP->ScriptStartP + (_Pos);		\
					 _CGP->ErrorStrP = (_S); goto ErrorOut; }

#define SC_CODEERROR(_CGP, _TP, _S)	{SC_CALLDEBUGGER;						\
					 _CGP->ErrorStartP = _CGP->ScriptStartP + (_TP->ReadPos);	\
					 _CGP->ErrorStrP = (_S); goto ErrorOut;}

// SC_CODEERROR_POS may be later reversed... so definitely no SC_CALLDEBUGGER!!
#define SC_CODEERROR_POS(_CGP, _TP, _S)	{_CGP->ErrorStartP = _CGP->ScriptStartP + (_TP->ReadPos);	\
					 _CGP->ErrorStrP = (_S); goto ErrorOut;}


#define SC_CODEERRORPOS(_CGP, _POS, _S)	{SC_CALLDEBUGGER;						\
					 _CGP->ErrorStartP = _CGP->ScriptStartP + (_POS);		\
					 _CGP->ErrorStrP = (_S); goto ErrorOut;}

#define SC_CODESTRERROR(_CGP, _S, _AS)  {SC_CALLDEBUGGER;						\
					 _CGP->ErrorStrP = (_S); _CGP->ErrorArgStrP = (_AS);		\
					 goto ErrorOut;}

#define SC_CHECKERROR(_CGP)		{if (_CGP->ErrorStrP) {SC_CALLDEBUGGER; goto ErrorOut;}}
#define	SC_ZAPERROR(_CGP)		{_CGP->ErrorStrP = NULL, _CGP->ErrorStartP = NULL;}

#define SC_CODENULLERROR(_CGP, _S)	{SC_CALLDEBUGGER;						\
					 _CGP->ErrorStartP = SC_CGRewindLastEnd(_CGP);			\
					 _CGP->ErrorStrP = (_S); goto ErrorOut;}
					// SC_CGRewindLastEnd because CGP->LastEndP is
					// always AFTER the last token

Uns16		SC_UtilWriteUniChar(Uns32 UC, char *DP)
{
    char	*StartP = DP;

    if (UC <= 0x007F) {
	*DP++ = UC;
	return 1;
    }

    if (UC < 0x07FF) {
	*DP++ = 0xC0 | (UC >> 6);
	goto OneMore;
    }

    if (UC <= 0xFFFF) {
	*DP++ = 0xE0 | (UC >> 12);
	goto TwoMore;
    }

    // *MUST* be <= 0x10FFFF
    *DP++ = 0xF0 | (UC >> 18);
    *DP++ = 0x80 | ((UC >> 12) & 0x003F);
TwoMore:
    *DP++ = 0x80 | ((UC >> 6) & 0x003F);
OneMore:
    *DP++ = 0x80 | (UC & 0x003F);

    return (DP - StartP);
}

void	SC_UtilStashLCase(char *DP, char *SP, Int32 Len)
{
    char	C;

    while (Len--) {
	C = *SP++;
	if (('A' <= C) && (C <= 'Z')) C += 'a' - 'A';
	*DP++ = C;
    }
    *DP = 0;
}


					
// ********************************************************************************
// ********************************************************************************
// ********************************************************************************
// **** Functions *****************************************************************

void			SC_StrHashInitTable(SC_VMPointer VMP);
void			SC_StrHashKillTable(SC_VMPointer VMP);
void			SC_StrHashGrowTable(SC_VMPointer VMP);
void			SC_StrHashPrintStatus(SC_VMPointer VMP, Int16 ShowSTab);

Uns64			SC_SlabCreate(SC_SlabPointer *FirstPP, Uns32 Size, Uns8 SlabType, Uns32 Flag);
void			SC_SlabWipe(SC_SlabPointer SlabP);
Uns64			SC_SlabUsed(SC_SlabPointer SlabP);
Uns64			SC_SlabKillOne(SC_SlabPointer *FirstPP, SC_SlabPointer SlabP);
Uns64			SC_SlabKillAll(SC_SlabPointer *FirstPP, Int16 ExceptLast);
Uns64			SC_SlabKillEmpty(SC_SlabPointer *FirstPP);
void			SC_SlabPackData(SC_SlabPointer SlabP);
SC_GenDataPointer	SC_SlabAllocDataBlock(SC_SlabPointer FirstSlabP, Uns32 ByteLen, Int16 IsArr);
void *			SC_SlabAllocEntry(SC_SlabPointer FirstSlabP, Uns32 EntrySize);
SC_SlabPointer		SC_SlabFindSlab(SC_SlabPointer SlabP, void * DataP);
void			SC_SlabZapItem(SC_SlabPointer FirstSlabP, void * ItemP, Uns32 ItemSize);
void			SC_SlabZapItemOnSlab(SC_SlabPointer SlabP, void * ItemP, Uns32 ItemSize);
void			SC_SlabPrintStatus(SC_SlabPointer SlabP, SC_PrintFuncP PrintFP);
void			SC_SlabPrintPtr(SC_VMPointer VMP, void * Ptr, char * LabelP, char WS);

void			SC_VMInitAllSlabs(SC_VMPointer VMP);
void			SC_VMKillAllSlabs(SC_VMPointer VMP);

void			SC_CGInitSysPackages(SC_VMPointer VMP);
void			SC_MainPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP);
void			SC_SysPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP);
void			SC_ShowPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP);
void			SC_StrPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP);
void			SC_EdPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP);
void			SC_ScriptPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP);
void			SC_PatPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP);
void			SC_LogPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP);
void			SC_MathPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP);

SC_VarVPointer		SC_VarEntryNew(SC_VMPointer VMP);
SC_PairHPointer		SC_BigEntryNew(SC_VMPointer VMP);
void *			SC_ChunkNew(SC_VMPointer VMP, Uns32 ByteLen, Uns32 * BlockLenP);

void			SC_StrStuffStr(char* StrP, char* DataP, Int16 Len);
Uns32			SC_StrGetCharLen(SC_StrHPointer SHP);
Uns64			SC_StrGetHashVal(const char * StrP, Uns32 Len);
SC_StrHPointer		SC_StrHashFind(SC_VMPointer VMP, char * StrP, Uns32 Len, Uns64 FullHash);
SC_StrDataPointer	SC_StrStashData(SC_VMPointer VMP, char * StrP, Uns32 Len);
SC_StrDataPointer	SC_StrStashBufferData(SC_VMPointer VMP, void * BufP, Int32 StartPos, Int32 EndPos);
SC_StrHPointer		SC_StrMakeNewEntry(SC_VMPointer VMP, SC_StrDataPointer StrDataP, Uns64 HashVal);
void			SC_StrPurge(SC_VMPointer VMP, SC_StrHPointer StrHP, SC_SlabPointer SP);
void			SC_StrHashEntry(SC_VMPointer VMP, SC_StrHPointer StrHP, Int16 FirstHash);
SC_StrHPointer		SC_StrGetEntry(SC_VMPointer VMP, char *StrP, Uns32 Len);
SC_StrHPointer		SC_StrGetDataEntry(SC_VMPointer VMP, SC_StrDataPointer DataP);
void			SC_StrRehashAllEntry(SC_VMPointer VMP);

SC_ArrDataPointer	SC_ArrAllocNewData(SC_VMPointer VMP, SC_ArrDataPointer * PlacePP,  Uns32 SlotCount, Int16 ZeroAll);
void			SC_ArrInitNewArr(SC_ArrDataPointer ADP, void ** InitArrP, Uns32 InitSlotCount);
SC_ArrDataPointer	SC_ArrResizeData(SC_VMPointer VMP, SC_ArrDataPointer OldDataP, Uns32 NewSlotCount, Int16 ZeroAll);
SC_ArrVPointer		SC_ArrMakeNewArr(SC_VMPointer VMP, Uns32 SlotCount);
void			SC_ArrPurge(SC_VMPointer VMP, SC_ArrVPointer EntryP);

SC_EXTVPointer		SC_EXTMakeNew(SC_VMPointer VMP, void * ExtP, Uns16 ExtType);
SC_ArrXTVPointer	SC_ArrXTMakeNew(SC_VMPointer VMP, Uns32 SlotCount, Uns16 ExtType);
SC_ChunkXTVPointer	SC_ChunkXTMakeNew(SC_VMPointer VMP, Uns32 DataLen, Uns32 ExtData, Uns16 ExtType);

SC_DictVPointer		SC_DictCreateNewDict(SC_VMPointer VMP, Uns32 PairCount);
void			SC_DictPurge(SC_VMPointer VMP, SC_DictVPointer DictP, Int16 ForceZap);
void			SC_DictStashNewPair(SC_VMPointer VMP, SC_DictVPointer DictP, SC_PairHPointer PairP);
void			SC_DictIntStashVal(SC_VMPointer VMP, SC_DictVPointer DictP, Int64 Key, Int64 Val, Uns8 ValType);
SC_StrHPointer		SC_DictStashVal(SC_VMPointer VMP, SC_DictVPointer DictP, char * KeyNameP, Int64 Val, Uns8 ValType);
void			SC_DictStashStr(SC_VMPointer VMP, SC_DictVPointer DictP, char *KeyNameP, char *StrP);
SC_PairHPointer		SC_DictGetPair(SC_DictVPointer DictP, SC_VarVPointer KeyP, Int16 KeyType);
SC_CaseHPointer		SC_DictGetCase(SC_ArrDataPointer StringADP, SC_DictVPointer DictP, Uns64 Key, Uns32 Id, Int16 KeyType);
void			SC_DictPrint(SC_VMPointer VMP, SC_DictVPointer DictP, Int16 Alone, Uns8 Lead);

void			SC_ExtDeRef(SC_VMPointer VMP, SC_VarVPointer EP);

Int16			SC_ReadLineComment(SC_CGPointer CGP, SC_TokenPointer TP);
Int16			SC_ReadBlockComment(SC_CGPointer CGP, SC_TokenPointer TP);
Int16			SC_ReadWhiteSpace(SC_CGPointer CGP, SC_TokenPointer TP);
Int16			SC_ReadLongString(SC_CGPointer CGP, SC_TokenPointer TP);
Int16			SC_ReadShortString(SC_CGPointer CGP, SC_TokenPointer TP);
Int16			SC_ReadProcessShortString(SC_CGPointer CGP, SC_TokenPointer TP);
Int16			SC_ReadNumber(SC_CGPointer CGP, SC_TokenPointer TP);
void			SC_ReadKeyCodeMapInit(void);
Int16			SC_ReadIdent(SC_CGPointer CGP, SC_TokenPointer TP);
Int16			SC_ReadOperator(SC_CGPointer CGP, SC_TokenPointer TP);
void			SC_ReadShowSourceError(SC_CGPointer CGP);

SC_TokenPointer		SC_TokenNew(SC_VMPointer VMP);
Int16			SC_TokenGetNext(SC_CGPointer CGP, SC_TokenPointer TokenP);
Int16			SC_TokenLookAhead(SC_CGPointer CGP, SC_TokenPointer TokenP);
void			SC_CGTokenZapTree(SC_VMPointer VMP, SC_TokenPointer TP);

void			SC_CGMainInit(SC_CGPointer CGP, SC_VMPointer VMP, char *StrP, Uns32 Len);
void			SC_CGZap(SC_CGPointer CGP);

SC_ByteOpPointer	SC_CGCodeAdd(SC_CGPointer CGP, SC_MapPointer MapP);
Uns32			SC_CGLabelAlloc(SC_CGPointer CGP, SC_MapPointer MapP);
void			SC_CGLabelDefine(SC_MapPointer MapP, Uns32 Index, Int32 Value);
Int32			SC_CGLabelGet(SC_MapPointer MapP, Uns32 Index);
Uns32			SC_CGKonstAdd(SC_CGPointer CGP, SC_MapPointer MapP, Int64 Konst);
Uns32			SC_CGFuncAlloc(SC_CGPointer CGP, SC_MapPointer ContMapP);

SC_LexPointer		SC_CGLexNew(SC_VMPointer VMP);
SC_VexPointer		SC_CGVexNew(SC_VMPointer VMP);
SC_MapPointer		SC_CGMapNew(SC_VMPointer VMP);
void			SC_CGMapZap(SC_VMPointer VMP, SC_MapPointer MapP);

Int16			SC_CGRegCacheSet(SC_CGPointer CGP, SC_MapPointer MapP, SC_VexPointer GVexP);
Int16			SC_CGRegCacheGet(SC_CGPointer CGP, SC_MapPointer MapP, SC_VexPointer GVexP);
void			SC_CGRegCacheInval(SC_CGPointer CGP, SC_MapPointer MapP, Uns8 BLevel);

SC_PkgPointer		SC_CGGetPkg(SC_CGPointer CGP, SC_TokenPointer TP);
SC_PairHPointer		SC_CGGetPkgField(SC_CGPointer CGP, SC_PkgPointer PkgP, SC_TokenPointer FieldTP);
Uns16			SC_CGGetSysFuncIndex(SC_CGPointer CGP, SC_PkgPointer PkgP, SC_TokenPointer TP, Uns8 * ResTypeP);

Int16			SC_CGOpIntoComma(SC_CGPointer CGP, SC_TokenPointer TP);
char *			SC_CGRewindLastEnd(SC_CGPointer CGP);
SC_LexPointer		SC_CGUnNestFinLex(SC_LexPointer LexP, Uns16 TargetFlag);
void			SC_CGFinishLex(SC_CGPointer CGP, SC_LexPointer LexP);
SC_LexPointer		SC_CGAddLex(SC_CGPointer CGP, Uns32 Flags);
void			SC_CGZapLastLex(SC_CGPointer CGP);
Int16			SC_CGAddLocal(SC_CGPointer CGP, SC_TokenPointer VarTP, SC_LexPointer FuncLexP);
Int16			SC_CGAddGlobal(SC_CGPointer CGP, SC_TokenPointer VarTP, SC_LexPointer FuncLexP, Uns16 GIndex);
void			SC_CGRegisterVar(SC_VMPointer VMP, SC_LexPointer BlockLexP, SC_StrHPointer StrHP, SC_VexPointer VexP);
Int16			SC_CGDeclareVar(SC_CGPointer CGP, SC_TokenPointer VarTP, Int16 IsFunc);
void			SC_InheritOldInfo(SC_CGPointer CGP);
Int16			SC_CGDeclareDef(SC_CGPointer CGP, SC_TokenPointer DefTP, SC_TokenPointer ArgTP);
SC_VexPointer		SC_CGCloseVar(SC_CGPointer CGP, SC_TokenPointer VarTP, SC_StrHPointer StrHP, SC_LexPointer FuncLexP, SC_VexPointer SrcVexP);
Int16			SC_CGBridgeCloseVar(SC_CGPointer CGP, SC_TokenPointer VarTP, SC_StrHPointer StrHP, SC_LexPointer FuncLexP, SC_VexPointer SrcVexP);
Int16			SC_CGResolveVar(SC_CGPointer CGP, SC_TokenPointer VarTP, Int16 DoDef);
Int16			SC_CGSubDefToken(SC_PairHPointer PairP, SC_TokenPointer DefTP);
Int16			SC_CGResolveDef(SC_CGPointer CGP, SC_TokenPointer DefTP);
Int16			SC_CGRefLabel(SC_CGPointer CGP, SC_TokenPointer LabelTP, Int16 DoDefine);
Int32			SC_CGGetLabel(SC_CGPointer CGP, SC_TokenPointer LabelTP, Uns32 * IndexP);
Uns32			SC_CGGetKonstIndex(SC_CGPointer CGP, void * KValue);
Int16			SC_CGCheckLabelDict(SC_CGPointer CGP, SC_LexPointer FuncLexP);
Int16			SC_CGCheckVarDict(SC_CGPointer CGP, SC_LexPointer FuncLexP);
Uns32			SC_CGGetStringIndex(SC_CGPointer CGP, SC_TokenPointer TP);
static inline Uns8	SC_CGOpNoSideEffect(Uns8 Op);
Int16			SC_CGASTHasSideEffect(SC_TokenPointer TP);

void			SC_CGSetTPInt(SC_TokenPointer TP, Int64 Numb);
void			SC_CGSetTPFloat(SC_TokenPointer TP, double Numb);
Int16			SC_CGIsIntTP(SC_TokenPointer TP, Int64 Val);
Int16			SC_CGIsOpTP(SC_TokenPointer TP, Uns8 Op);
void			SC_CGNegateNumbTP(SC_TokenPointer TP);
void			SC_CGNegateSubOpTP(SC_TokenPointer TP);
void			SC_CGDistributeOp(SC_VMPointer VMP, SC_TokenPointer TP, Uns8 Op);
SC_TokenPointer		SC_CGGetLastArg(SC_TokenPointer TP);

void			SC_CGCheckAdd(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckSub(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckDiv(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckMod(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckMult(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckSHL(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckSHR(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckPlus(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckMinus(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckLess(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckGreat(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckEQEQ(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckNEQ(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckLEQ(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckGEQ(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckID(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckNID(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckLNEG(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckLPOS(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckLAND(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckLOR(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckBNEG(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckBAND(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckBOR(SC_TokenPointer TP, Uns16 ArgFlag);
void			SC_CGCheckBXOR(SC_TokenPointer TP, Uns16 ArgFlag);

Int16			SC_CGCodePassCleanup(SC_CGPointer CGP, Int16 StopIf, Int16 ChainUp);
Uns32			SC_CGFindLogicEscape(SC_JSetPointer JSetP, Int16 * EscTestP, Int16 Esc, Int16 IsLast);
void			SC_CGCodeAssignNumb(SC_CGPointer CGP, SC_TokenPointer NTP, SC_TokenPointer AssignTP, SC_TokenPointer TP, Uns8 NoPush);
void			SC_CGCodePushTokenNumb(SC_CGPointer CGP, SC_TokenPointer NTP);
void			SC_CGCodePushInt(SC_CGPointer CGP, Int64 Count);
void			SC_CGCodePushFloat(SC_CGPointer CGP, double Numb);
Int16			SC_CGCodePassAllArgs(SC_CGPointer CGP, SC_TokenPointer TP, Int8 SelfAssign);
Int16			SC_CGLoadVar(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VarP, SC_VexPointer VexP, Int16 SetGlobal);
void			SC_CGLoadAssignVar(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VarP, SC_TokenPointer AssignTP, SC_TokenPointer TP);
Int16			SC_CGLoad2Args(SC_CGPointer CGP, SC_MapPointer MapP, SC_TokenPointer TP, SC_VDescPointer VArr, SC_KPointer KP, Int16 DoesAssign);
void			SC_CGPushVArrKonst(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, SC_KPointer KP);
Int16			SC_CGCheckMathVVK(SC_VDescPointer VArr, SC_KPointer KP, Uns8 * ByteOpP);
void			SC_CGWriteMathVVK(SC_CGPointer CGP, SC_MapPointer MapP, Uns8 Op, SC_VDescPointer VArr, Int8 KVal);
void			SC_CGWriteNegCode(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, Uns8 Op);
void			SC_CGWriteCondVK2(SC_CGPointer CGP, SC_MapPointer MapP, Uns8 Op, SC_VDescPointer VArr, Int16 K2Val);
void			SC_CGWriteMovK(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, Int16 K);
void			SC_CGWriteRSetV(SC_CGPointer CGP, SC_MapPointer MapP);
void			SC_CGWrite3V(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, Uns8 Op, Uns8 SSSOp);
void			SC_CGWriteVVK(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, Int8 KVal, Uns8 Op);
void			SC_CGWriteIGETVVK(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, Int8 KVal);
void			SC_CGWriteIGETVVV(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr);
void			SC_CGWriteVK2(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescRecord V, Int32 Konst, Uns8 KClass, Uns8 Op);
void			SC_CGWrite1V(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescRecord V, Uns8 Op);
void			SC_CGWrite2V(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, Uns8 Op);
void			SC_CGWriteK1(SC_CGPointer CGP, SC_MapPointer MapP, Int8 K, Uns8 Op);
void			SC_CGWriteI3(SC_CGPointer CGP, SC_MapPointer MapP, Int32 I3, Uns8 Op);
void			SC_CGWriteK3(SC_CGPointer CGP, SC_MapPointer MapP, Int32 K3, Uns8 Op);
void			SC_CGWriteNoOp(SC_CGPointer CGP, SC_MapPointer MapP);
void			SC_CGWriteNoArg(SC_CGPointer CGP, SC_MapPointer MapP, Uns8 Op);
void			SC_CGWriteBranch(SC_CGPointer CGP, SC_MapPointer MapP, Uns32 LIndex, Uns8 Op);
void			SC_CGWriteMovVV(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, Uns8 NoPush);
void			SC_CGWritePshKIndex(SC_CGPointer CGP, SC_MapPointer MapP, Uns32 KIndex, Uns8 IsInt);
void			SC_CGWriteSwch(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescRecord V);
void			SC_CGWriteFCall(SC_CGPointer CGP, SC_MapPointer MapP, SC_VexPointer VexP, Uns16 ArgCount, Uns8 DoUnPack);
void			SC_CGWriteSCall(SC_CGPointer CGP, SC_MapPointer MapP, Int64 SIndex, Uns16 ArgCount, Uns8 DoUnPack);
void			SC_CGWriteLogicTest(SC_CGPointer CGP, SC_MapPointer MapP, SC_VexPointer VexP, Uns32 LIndex, Uns8 IsTrue);
void			SC_CGWriteCallArg(SC_CGPointer CGP, SC_MapPointer MapP, SC_VexPointer VexP, Int16 ByRef);

void			SC_SysError(char * ErrStrP);
void			SC_RunInitVar(SC_VMPointer VMP, SC_VarVPointer * VarPP, SC_VarVPointer SrcP);
SC_ClosPointer		SC_RunMakeClos(SC_VMPointer VMP, Uns32 FuncId, SC_CodeBlockPointer FuncCBP);
SC_GlobPointer		SC_RunAllocGlob(SC_VMPointer VMP, Uns32 VarCount, Uns8 Init);
void			SC_RunMakeScriptGlob(SC_VMPointer VMP, SC_CGPointer CGP, Uns8 IsSub);
void			SC_RunZapGlob(SC_VMPointer VMP, SC_GlobPointer GlobP);
void			SC_RunDisplayField(SC_VMPointer VMP, Uns8 ValueType, void * ValueP);
void			SC_RunDisplayVarP(SC_VMPointer VMP, SC_VarVPointer VarP, char * NameSP, Uns32 Index, Uns8 Level, Uns8 Lead);
void			SC_RunDisplayValueP(SC_VMPointer VMP, SC_VarVPointer VP, Uns8 Level, Uns8 Lead);

void			SC_RunDisplayGArr(SC_VMPointer VMP, SC_VarVPointer * VPP, SC_StrHPointer * SHPP, Uns32 Count, Uns8 Level, Uns8 Lead);
void			SC_RunDisplayGlobs(SC_VMPointer VMP, SC_ScriptPointer ScriptP, Uns8 Level, Uns8 Alone, Uns8 Lead);
void			SC_RunDisplayRuns(SC_VMPointer VMP, SC_RunPointer RunP, Uns8 Level, Uns8 Alone);
void			SC_RunDisplayLocals(SC_VMPointer VMP, SC_RunPointer RunP, Uns8 Level, Uns8 Alone);
void			SC_RunDisplayCB(SC_CGPointer CGP, SC_VMPointer VMP, SC_ScriptPointer ScriptP, SC_CodeBlockPointer CBP, Uns8 Alone, Uns8 Lead);
void			SC_RunZapCurRunRecord(SC_VMPointer VMP);
SC_RunPointer		SC_RunMakeRunRecord(SC_VMPointer VMP, SC_ClosPointer ClosP, Uns16 InOutCount, Uns8 DoUnPack);
void			SC_RunMakeStackCopy(SC_VMPointer VMP, SC_RCPointer RCP);
void			SC_RunPushStackCopy(SC_VMPointer VMP, SC_RCPointer RCP);
void			SC_RunZapStackCopy(SC_VMPointer VMP, SC_RCPointer RCP);
void			SC_RunReleaseRunChain(SC_VMPointer VMP, SC_RunPointer LowP, SC_RunPointer HighP);

Int32			SC_RunOpenWriteBSC(SC_ScriptPointer ScriptP);
Int16			SC_RunWriteBSC(SC_VMPointer VMP, Int32 FD, SC_ScriptPointer ScriptP);
SC_ScriptPointer	SC_SysReadScript(SC_VMPointer VMP, Int32 FD);
SC_ScriptPointer	SC_SysReadBSC(SC_VMPointer VMP, Int32 FD, char * FNameP);

void			SC_PatPrint(SC_VMPointer VMP, SC_PatPointer PatP, Uns8 Alone, Uns8 Lead);

// ****MAIN Functions *************************************************************
Int16			SC_ParseExpr(SC_CGPointer CGP, SC_TokenPointer CurTP, Int16 Act, Int16 PrevWasOp);
Int16			SC_CGXformExpr(SC_CGPointer CGP, SC_TokenPointer TP);
Int16			SC_CGCheckPass(SC_CGPointer CGP, SC_TokenPointer TP, Int16 Act);
Int16			SC_CGCodePass(SC_CGPointer CGP, SC_TokenPointer TP, SC_TokenPointer AssignTP);
SC_CodeBlockPointer	SC_ASMPass(SC_CGPointer CGP, SC_MapPointer MapP);
Int16			SC_RunExecCurFunc(SC_VMPointer VMP, Int8 IsCallback);

void			SC_GCMarkVar(SC_VarVPointer VarP);
void			SC_GCMark(SC_VMPointer VMP);
void			SC_GCAll(SC_VMPointer VMP);

void			SC_RunZapOldState(SC_VMPointer VMP);
Int16			SC_CompileScript(SC_VMPointer VMP, char *StrP, Uns32 Len, char * PathP, char * NameP,
					 Uns8 CompileOnly, Uns8 WriteBSC, Uns8 Interact);

// ****RUNTIME SYS FUNCTIONS *******************************************************

typedef Int16	(*SC_SysFuncPointer)(SC_VMPointer VMP, Uns16 InOutCount);


Int16			SC_SysGetCallArgs(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysPause(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysExit(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysPrintOut(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysBufPrint(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_StrPrint(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysWriteOut(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysWriteLineOut(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysBufWrite(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysBufWriteLine(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_StrWrite(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_StrWriteLine(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_StrForm(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_StrTimePrint(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdDel(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysLogOpen(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysLogClose(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysLogEntry(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysLogEEntry(SC_VMPointer VMP, Uns16 InOutCount);

Int16			SC_SysMathCos(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathACos(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathCosh(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathSin(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathASin(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathSinh(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathTan(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathATan(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathTanh(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathLn(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathLog(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathPower(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathExp(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathSqrt(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathFloor(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathCeiling(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMathRound(SC_VMPointer VMP, Uns16 InOutCount);

Int16			SC_SysShowVars(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysShowGlobs(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysShowRun(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysShowScript(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysShowVM(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysShowMem(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysShowStr(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysShowMemStr(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysShowPkg(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysDoMSGC(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysDebugger(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysRunError(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysDoAbort(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysGetType(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysGetSize(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysCountElts(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysReverse(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysCopy(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysSeedRand(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysRand(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysTime(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysBrTime(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysTicks(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysElapsedSecs(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdReadFile(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdGetBuf(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdStr(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdGetBLen(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdGetCLen(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdGetBPos(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdGetCPos(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdGetSel(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdEcho(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdQueryMC(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdShow(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdShowSel(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdSetCurBuf(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysStrGetSub(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysStrDelTxt(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysStrGetBLen(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysStrGetCLen(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysScriptGetSelf(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysScriptGetSup(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysScriptGetId(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysScriptGetLevel(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysScriptLoad(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysScriptExec(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdSetPos(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysEdSaveFile(SC_VMPointer VMP, Uns16 InOutCount);

Int16			SC_SysMakePatSeq(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMakePatAlt(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMakePatSpan(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMakePatRep(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMakePatChar(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMakePatGo(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMakePatAt(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMakePatIter(SC_VMPointer CMP, Uns16 InOutCount);

Int16			SC_SysMakePat(SC_VMPointer VMP, Uns16 InOutCount);
Int16			SC_SysMatchPat(SC_VMPointer VMP, Uns16 InOutCount);

void			SC_LogCleanup(Uns16 ScriptLevel);

#if SC_SAFE
    char *	SC_BADRUN_Var		= "Bad var index";
    char *	SC_BADRUN_Stack		= "Bad run stack";
    char *	SC_BADRUN_Type		= "Bad value type";
    char *	SC_BADRUN_Index		= "Bad index";
    char *	SC_BADRUN_Addr		= "Bad address";
    char *	SC_BADRUN_Dict		= "Bad dict";
    char *	SC_BADRUN_RefCount	= "Bad ref count";
    char *	SC_BADRUN_Internal	= "Bad internal state";

    #define	SC_SAFE_CHECK(_Pred, _S)	if (! (_Pred)) SC_SysError(_S);
#else
    #define	SC_SAFE_CHECK(_Pred, _S)
#endif


// ********************************************************************************
// STRING HASH Table
//
// One large central open hash array.  Allocated as one memory block, along with
// its SC_StrHashRecord header.  

void		SC_StrHashInitTable(SC_VMPointer VMP)
{
    Uns32		InitSize;
    Uns32		SlotCount;
    SC_StrHPointer *	SlotP;
    SC_StrHashTPointer	SHTP;

    InitSize = SC_HASHINITSLOTCOUNT * SC_InitFactor;
    // May no longer be a power of 2... make it so!
    SlotCount = 1;
    while (InitSize >>= 1) SlotCount <<= 1;

    InitSize = offsetof(SC_StrHashTRecord, Slots) + (SlotCount * (sizeof(void *)));
    SHTP = malloc(InitSize);
    if (! SHTP) G_SETEXCEPTION("Malloc Initial StrHashTable Failed", 0);

    VMP->GCSize += InitSize;
    VMP->GCStep += InitSize;
    VMP->StrHashTP = SHTP;
    SHTP->Tag = *(Uns32 *)&SC_SlabTagArr[SC_SLAB_STAB];
    SHTP->Flags = 0L;
    SHTP->SlotCount = SlotCount;
    SHTP->SlotsUsed = 0L;
    SHTP->EntryCount = 0L;
    SHTP->IndexMask = SlotCount - 1;
    
    SlotP = SHTP->Slots;
    while (SlotCount--) *SlotP++ = NULL;
}


void		SC_StrHashKillTable(SC_VMPointer VMP)
{
    if (VMP->StrHashTP) free(VMP->StrHashTP);
    VMP->StrHashTP = NULL;
}

// SC_StrHashGrowTable will allocate a new memory block for the hash array (and
// its header record) and re-hash ever StrHRecord.

void		SC_StrHashGrowTable(SC_VMPointer VMP)
{
    SC_StrHashTPointer		SHTP = VMP->StrHashTP;
    SC_StrHashTPointer		NewHashTP;
    Uns32			OldSize, NewSlotCount, NewSize;
    SC_StrHPointer		* SlotP;

    if ((SHTP->SlotsUsed < (SHTP->SlotCount >> 1)) ||		// Less than 1/2 slots used
	(SHTP->EntryCount < (SHTP->SlotsUsed << 1)) ||		// Avg Collision list < 2
	(SHTP->SlotCount == SC_HASHMAXSLOTCOUNT))		// Already maxed out!
	return;							// Do NOT grow

    OldSize = offsetof(SC_StrHashTRecord, Slots) + (SHTP->SlotCount * (sizeof(void *)));
    NewSlotCount = SHTP->SlotCount << 1;
    NewSize = offsetof(SC_StrHashTRecord, Slots) + (NewSlotCount * (sizeof(void *)));

  SC_DEBUG_HASHPRINTSTATUS;

    NewHashTP = malloc(NewSize);
    if (! NewHashTP) G_SETEXCEPTION("Malloc Failed StrHashTable Grow", 0);

    free(VMP->StrHashTP);		// Zap the old memory
    VMP->StrHashTP = NewHashTP;

    VMP->GCSize += (NewSize - OldSize);
    VMP->GCStep += NewSize;
    NewHashTP->Flags = 0L;
    NewHashTP->SlotCount = NewSlotCount;
    NewHashTP->SlotsUsed = 0L;
    NewHashTP->EntryCount = 0L;
    NewHashTP->IndexMask = NewSlotCount - 1;

    SlotP = NewHashTP->Slots;
    while (NewSlotCount--) *SlotP++ = NULL;

    // New virgin HashTable!!  Now enter every EntryH.
    SC_StrRehashAllEntry(VMP);

  SC_DEBUG_HASHPRINTSTATUS;
}

// SC_StrHashPrintStatus prints stats for the global String hash table.

void	SC_StrHashPrintStatus(SC_VMPointer VMP, Int16 ShowSTab)
{
    Uns32		TotSize, Cols, ColMax, ColSlots, ColEntries, SlotCount;
    double		UsageFactor, ColFactor, ColAvg;
    SC_StrHPointer	*SlotP, StrHP;
    SC_StrHashTPointer	SHTP = VMP->StrHashTP;
    char *		Spaces;
    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;

    TotSize = offsetof(SC_StrHashTRecord, Slots) + (SHTP->SlotCount * sizeof(void*));
    ColMax = ColSlots = ColEntries = 0;
    SlotP = SHTP->Slots;
    SlotCount = SHTP->SlotCount;

    while (SlotCount--) {
	StrHP = *SlotP++;

	if (StrHP) {
	    Cols = 0;
	    while ((StrHP = StrHP->HashNextP))	// Assign
		Cols += 1;

	    if (Cols) {
		ColSlots += 1;
		ColEntries += Cols;
		if (Cols > ColMax) ColMax = Cols;
	    }
	}
    }

    UsageFactor = (double)SHTP->SlotsUsed / SHTP->SlotCount;
    ColFactor = (SHTP->EntryCount) ? (double)ColEntries / SHTP->EntryCount : 0;
    ColAvg = 1.0 + ((ColSlots) ? (double)ColEntries / ColSlots : 0);

    if (ShowSTab) {
	OutPFP("\nMem:%d --> (STab) %s:1\n", SC_SLAB_STAB, SC_SlabNameArr[SC_SLAB_STAB]);
	Spaces = "          ";
    } else {
	OutPFP("  Mem:%d ----> (%.*s) %s [Total: %'d]\n", SC_SLAB_STAB, 4, &SHTP->Tag, SC_SlabNameArr[SC_SLAB_STAB], TotSize);
	Spaces = "              ";
    }
    OutPFP("%sUsed: %'d of %'d for %'d Entries\n", Spaces, SHTP->SlotsUsed, SHTP->SlotCount, SHTP->EntryCount);
    OutPFP("%sColls: %'d [%'d Max %.2f Avg in %'d Slots]\n", Spaces, ColEntries, ColMax, ColAvg, ColSlots);
    OutPFP("%sUse Factor: %.4f   Coll Factor: %.4f\n", Spaces, UsageFactor, ColFactor);
}

// ********************************************************************************
// SLAB (Memory management)


    #ifdef DEBUG
	// During DEBUG, an extra guard LL is placed after each Slab.
	// SC_DEBUG_CHECKSLAB will check the integrity of this LL and the FREE list.

	void	SC_SlabCheckArrData(SC_SlabPointer SP)
	{
	    char		*ReadP, *EndP;
	    SC_GenDataPointer	GenDP;
	    SC_SlabFreePointer	FreeP;
	    Uns32		BytesUsed, LenBytes;
	    Int16		IsStr = !!(SP->Flags & SC_SLAB_STR_FLAG);

	    // Call only on ARR and STR slabs

	    BytesUsed = 0L;
	    ReadP = SP->Data;
	    EndP = ReadP + SP->DataSize;

	    while (ReadP < EndP) {
		FreeP = (SC_SlabFreePointer)ReadP;
		if (FreeP->Zero == 0LL) {
		    ReadP += FreeP->Size;
		    if (FreeP->Size < sizeof(SC_SlabFreeRecord))
			G_SETEXCEPTION("SlabCheck: FREE too small", 0);
		    continue;
		}

		// NOT free if it gets here...
		GenDP = (SC_GenDataPointer)FreeP;
		if (IsStr)
		    LenBytes = SC_LLALIGN(offsetof(SC_StrDataRecord, Data) + GenDP->DataCount + 1);
		else
		    LenBytes = offsetof(SC_ArrDataRecord, Data) + (GenDP->DataCount * sizeof(void*));
		ReadP += GenDP->BlockLen;
		BytesUsed += GenDP->BlockLen;

		// NOTE: Ok for ArrData SlotCount/DataCount to be less than BlockLen can hold.
		//	 So CANNOT check for BlockLen TOO BIG here!
		if (LenBytes > GenDP->BlockLen)
		    G_SETEXCEPTION("SlabCheck: Block too small", 0);
	    }

	    if (BytesUsed != SP->DataUsed)
		G_SETEXCEPTION("SlabCheck: DataSize mismatch", 0);
	}

	void	SC_SlabCheckGuard(SC_SlabPointer SP)
	{
	    Uns64		*P;
	    Uns32		NextFree;
	    Uns32		TotalFree;
	    Uns32		FreeCount;
	    SC_SlabFreePointer	FreeP;


	    P = (Uns64 *)(SP->Data + SP->DataSize);
	    if (*P != SC_DEBUG_SLABGUARD)
		G_SETEXCEPTION("SlabGuard: Bottom violated!", 0);

	    #if SC_DEBUG_CHECK_SLABFREE

		#if SC_DEBUG_CHECK_ALLSLAB
		    if (SP->Flags & SC_SLAB_ARR_FLAG)
			SC_SlabCheckArrData(SP);		// Slows down!
		#endif

		FreeCount = TotalFree = 0L;
		NextFree = SP->FirstFree;
		while (NextFree != (Uns32)-1L) {
		    if (NextFree >= SP->DataSize) break;	// Out of bounds!

		    FreeP = (SC_SlabFreePointer)(SP->Data + NextFree);
		    if (FreeP->Zero != 0LL)
			G_SETEXCEPTION("SlabGuard: FREE is not free", 0);
		    if (FreeP->Size == 0L)
			G_SETEXCEPTION("SlabGuard: FREE has no size", 0);
		    FreeCount += 1;
		    TotalFree += FreeP->Size;
		    NextFree = FreeP->NextFree;
		}

		if (NextFree != (Uns32)-1L)
		    G_SETEXCEPTION("SlabGuard: FREE out of bounds", 0);
		
		if (FreeCount != SP->FreeCount)
		    G_SETEXCEPTION("SlabGuard: FREE count error", 0);
		
		if (SP->DataUsed + TotalFree != SP->DataSize)
		    G_SETEXCEPTION("SlabGuard: FREE size error", 0);

		
	   #endif
	}
    #endif


#ifdef DEBUG
    #define	SC_SLAB_MEMSIZE(_S)	((_S) + offsetof(SC_SlabRecord, Data) + sizeof(Uns64))
#else
    #define	SC_SLAB_MEMSIZE(_S)	((_S) + offsetof(SC_SlabRecord, Data))
#endif

Uns64		SC_SlabCreate(SC_SlabPointer *FirstPP, Uns32 Size, Uns8 SlabType, Uns32 Flag)
{
    Uns64		SS;
    SC_SlabPointer	SlabP;
    SC_SlabFreePointer	FreeP;

    SS = SC_SLAB_MEMSIZE(Size);

    SlabP = malloc(SS);
    if (! SlabP) G_SETEXCEPTION("Malloc Slab Failed", 0);

    SlabP->Tag = *(Uns32 *)&SC_SlabTagArr[SlabType];
    SlabP->Flags = Flag + SlabType;
    SlabP->DataSize = Size;
    SlabP->DataUsed = 0L;
    SlabP->FirstFree = 0L;		// 0L is valid (first in Data)
    SlabP->FreeCount = 1;

    SlabP->NextSP = *FirstPP;
    if (*FirstPP) (*FirstPP)->PrevSP = SlabP;
    SlabP->PrevSP = NULL;
    *FirstPP = SlabP;

    FreeP = (SC_SlabFreePointer)&SlabP->Data;
    FreeP->Zero = 0LL;
    FreeP->Size = Size;
    FreeP->NextFree = (Uns32)-1L;

    #ifdef DEBUG
	// ****************************************************************
	*(Uns64 *)((Uns64)SlabP + SS - sizeof(Uns64)) = SC_DEBUG_SLABGUARD;
	// ****************************************************************
    #endif
    return SS;
}

// Should be called with the *FIRST* slab of the given type!
// Fast wipe of *ALL* slabs, back to pristine original state.

void		SC_SlabWipe(SC_SlabPointer SlabP)
{
    SC_SlabFreePointer	FreeP;

    while (SlabP) {
	if (SlabP->DataUsed) {
	    SlabP->DataUsed = 0L;
	    SlabP->FirstFree = 0L;
	    SlabP->FreeCount = 1;

	    FreeP = (SC_SlabFreePointer)&SlabP->Data;
	    FreeP->Zero = 0LL;
	    FreeP->Size = SlabP->DataSize;
	    FreeP->NextFree = (Uns32)-1L;

	    #ifdef DEBUG
		// ****************************************************************
		*(Uns64 *)((Uns64)SlabP + SlabP->DataSize) = SC_DEBUG_SLABGUARD;
		// ****************************************************************
	    #endif
	}
	SlabP = SlabP->NextSP;
    }
}

// Returns total data size used by this and following slabs.
// Should normally be called with the first slab.

Uns64		SC_SlabUsed(SC_SlabPointer SlabP)
{
    Uns64	Tot = 0L;

    while (SlabP) {
	Tot += SlabP->DataUsed;
	SlabP = SlabP->NextSP;
    }

    return Tot;
}

Uns64		SC_SlabKillOne(SC_SlabPointer *FirstPP, SC_SlabPointer SlabP)
{
    Uns64	SS = SC_SLAB_MEMSIZE(SlabP->DataSize);
    
    if (*FirstPP == SlabP) *FirstPP = SlabP->NextSP;
    if (SlabP->PrevSP) SlabP->PrevSP->NextSP = SlabP->NextSP;
    if (SlabP->NextSP) SlabP->NextSP->PrevSP = SlabP->PrevSP;
    free(SlabP);

    return SS;
}

// Last is the ORIGINAL/INITIAL one... the rest were added as needed.

Uns64		SC_SlabKillAll(SC_SlabPointer *FirstPP, Int16 ExceptLast)
{
    SC_SlabPointer	SP, NextSP;
    Uns32		Tot = 0LL;

    if ((SP = *FirstPP)) {			// Assign
	while ((NextSP = SP->NextSP)) {		// Assign
	    Tot += SC_SLAB_MEMSIZE(SP->DataSize);
	    free(SP);
	    SP = NextSP;
	}

	if (ExceptLast) {
	    *FirstPP = SP;
	    SP->NextSP = SP->PrevSP = NULL;
	    return Tot;
	}

	// Kill last one too!
	Tot += SC_SLAB_MEMSIZE(SP->DataSize);
	free(SP);
	*FirstPP = NULL;
    }

    return Tot;
}

// Will *NOT* kill the very last (INITIAL/ORIGINAL) one!

Uns64		SC_SlabKillEmpty(SC_SlabPointer *FirstPP)
{
    SC_SlabPointer	SP, NextSP;
    Uns64		Tot = 0LL;

    if ((SP = *FirstPP)) {			// Assign
	while ((NextSP = SP->NextSP)) {		// Assign
	    if (SP->DataUsed == 0L) {
		if (SP->PrevSP) {
		    SP->PrevSP->NextSP = NextSP;
		    NextSP->PrevSP = SP->PrevSP;
		    Tot += SC_SLAB_MEMSIZE(SP->DataSize);
		    free(SP);
		} else {
		    *FirstPP = NextSP;
		    NextSP->PrevSP = NULL;
		}
	    }
	    SP = NextSP;
	}
    }

    return Tot;
}


// SC_SlabPackData will compact/de-frag the Data slab.
//
// It differentiates between Str and Arr data slabs, Str is measured in Bytes
// while Arr are in LL (8 Bytes).

void		SC_SlabPackData(SC_SlabPointer SlabP)
{
    char		*ReadP, *WriteP, *EndP;
    SC_GenDataPointer	GenDP;
    Uns32		BytesUsed, LenBytes;
    const Int16		IsStr = !!(SlabP->Flags & SC_SLAB_STR_FLAG);

    BytesUsed = 0L;
    ReadP = WriteP = SlabP->Data;
    EndP = ReadP + SlabP->DataSize;

    // Slab blocks may have a little extra allocated at the end, trim that off
    // when tight packing... will read DataCount (StrLen/SlotCount) from client!
    while (ReadP < EndP) {
	if (((SC_SlabFreePointer)ReadP)->Zero == 0LL) {
	    ReadP += ((SC_SlabFreePointer)ReadP)->Size;
	    continue;
	}

	// For Str, GenDP->DataCount is the StrLen... also add 1 for trailing /0.
	GenDP = (SC_GenDataPointer)ReadP;
	if (IsStr)
	    LenBytes = SC_LLALIGN(offsetof(SC_StrDataRecord, Data) + GenDP->DataCount + 1);
	else
	    LenBytes = offsetof(SC_ArrDataRecord, Data) + (GenDP->DataCount * sizeof(void*));
	    
	if (ReadP > WriteP) {
	    memmove(WriteP, ReadP, LenBytes);
	    GenDP = (SC_GenDataPointer)WriteP;		// It moved!!
	    *GenDP->MasterPP = GenDP;			// Tell its client!
	}
	ReadP += GenDP->BlockLen;			// Old BlockLen
	GenDP->BlockLen = LenBytes;			// New BlockLen
	WriteP += LenBytes;
	BytesUsed += LenBytes;
    }

    if (BytesUsed + sizeof(SC_SlabFreeRecord) < SlabP->DataSize) {
	SC_SlabFreePointer	FreeP;
	
	SlabP->DataUsed = BytesUsed;
	FreeP = (SC_SlabFreePointer)&SlabP->Data[BytesUsed];
	FreeP->Zero = 0L;
	FreeP->NextFree = -1L;
	FreeP->Size = SlabP->DataSize - BytesUsed;
	SlabP->FirstFree = BytesUsed;
	SlabP->FreeCount = 1;
    } else {
	SlabP->DataUsed = SlabP->DataSize;
	SlabP->FirstFree = -1L;
	SlabP->FreeCount = 0L;
    }

    SC_DEBUG_CHECKSLAB(SlabP);
}

// NOTE: Assumes this is a CHUNK slab and only known chunk type data blocks are
//	 stored on it!

Int16		SC_SlabCoalesceFree(SC_SlabPointer SlabP, Uns32 Needed)
{
    // Free blocks are chained in random order... can only be coalesced by going
    // through the Slab block by block.  Worse, used data blocks are not in a
    // single format, can only be differentiated by their TYPE field.

    char		*ReadP, *EndP;
    SC_SlabFreePointer	FreeP;		// Current block... free?
    SC_SlabFreePointer	LastFreeP;	// Previous contiguous Free block
    Int16		Res = 0;

    SlabP->FirstFree = -1L;
    SlabP->FreeCount = 0;
    LastFreeP = NULL;

    ReadP = SlabP->Data;
    EndP = ReadP + SlabP->DataSize;
    while (ReadP < EndP) {

	FreeP = (SC_SlabFreePointer)ReadP;
	if (FreeP->Zero == 0LL) {
	    ReadP += FreeP->Size;	// Advance

	    if (LastFreeP) {
		// This will merge into LastFreeP... they are contiguous.
		LastFreeP->Size += FreeP->Size;
		if (LastFreeP->Size > Needed) Res = 1;
	    } else {
		// This is a NEW free block...
		FreeP->NextFree = SlabP->FirstFree;
		SlabP->FirstFree = (char *)FreeP - SlabP->Data;
		SlabP->FreeCount += 1;
		LastFreeP = FreeP;
	    }
	    continue;
	}

	// Clearly no contiguous free block here... so skip over the data block.
	LastFreeP = NULL;
	switch (*ReadP) {
	    case SC_CHUNK_EXT:	 ReadP += (*(SC_ChunkXTVPointer)ReadP).BlockLen; continue;
	    case SC_CHUNK_CLOS:	 ReadP += (*(SC_ClosPointer)ReadP).BlockLen; continue;
	    case SC_CHUNK_CODE:  ReadP += (*(SC_CodeBlockPointer)ReadP).BlockLen; continue;
	    case SC_CHUNK_RUN:   ReadP += (*(SC_RunPointer)ReadP).BlockLen; continue;
	    case SC_CHUNK_SCRPT: ReadP += sizeof(SC_ScriptRecord); continue;
	    case SC_CHUNK_GLOB:  ReadP += (*(SC_GlobPointer)ReadP).BlockLen; continue;
	    case SC_CHUNK_STAK:  ReadP += (*(SC_StackBlockPointer)ReadP).BlockLen; continue;
	    default: G_SETEXCEPTION("CHUNK slab violation!", *ReadP);
	}
    }

    SC_DEBUG_CHECKSLAB(SlabP);    
    return Res;
}

// SC_SlabAllocDataBlock will try to allocate a Data block of ByteLen on the
// Slab chain starting from FirstSlabP.  MinFree is the minimum size for a
// leftover free block, anything less will be added to the end of the Data
// block.
//
// Caller has already added in header block and SlotCount into ByteLen.
// This function just allocates ByteLen bytes + possible some extra space.
// ASSUMES ByteLen is LL aligned!
//
// Going down the Slab chain, it will find the first Slab with enough free
// space.  Once the Slab is located, it will use the first free block of
// sufficient size--first fit.  Otherwise, if the slab allows, will pack
// the Slab to create one big free space, then allocate in there.
//
// NOTE:  If no room is found, the function returns NULL.  (At which point the
//	  caller should allocate (and chain) a new Slab, then try again.)
//
//	  Packing the Slab (if allowed) will *MOVE* the data blocks and update
//	  their Entry records.
//
//	  ARR allow Packing of all blocks... CHUNK do not.  The latter will only
//	  coalesce FREE blocks, will NEVER move data blocks!  CHUNK blocks
//	  are ***NOT*** of GenDataRecord format.  Each block type on a CHUNK
//	  will be different!

SC_GenDataPointer	SC_SlabAllocDataBlock(SC_SlabPointer FirstSlabP, Uns32 ByteLen, Int16 IsArr)
{
    Uns32		LenLeft;
    SC_GenDataPointer	GenP;
    SC_SlabPointer	SP = FirstSlabP;

    if (ByteLen > SC_MAXSLABDATASIZE) G_SETEXCEPTION("Data too big", 0);

    while (SP) {

	if ((SP->DataSize - SP->DataUsed) >= ByteLen) {
	    // Has enough room, find a large enough free block.
	    SC_SlabFreePointer		FreeP;
	    Uns32			CurF, PrevF, NextF;
	    const char *		SlabData;

	TryAgain:
	    SlabData = SP->Data;
	    CurF = SP->FirstFree;
	    PrevF = NextF = -1L;		// 0L is a valid offset/index

	    FreeP = (SC_SlabFreePointer)&SlabData[CurF];
	    while (FreeP) {
		NextF = FreeP->NextFree;
		if (FreeP->Size >= ByteLen) break;	// Found it

		if (NextF != (Uns32)-1L) {
		    PrevF = CurF, CurF = NextF;
		    FreeP = (SC_SlabFreePointer)&SlabData[CurF];
		} else
		    FreeP = NULL;
	    }

	    if (FreeP) {
		// Found a large-enough one, may even have extra space.
		// No attempt made for best fit!
		GenP = (SC_GenDataPointer)FreeP;
		LenLeft = FreeP->Size - ByteLen;

		if (LenLeft >= sizeof(SC_SlabFreeRecord)) {
		    // The remainder will be another free block!
		    FreeP = (SC_SlabFreePointer)(((char *)FreeP) + ByteLen);
		    FreeP->Zero = 0LL;
		    FreeP->Size = LenLeft;
		    FreeP->NextFree = NextF;
		    NextF = CurF + ByteLen;
		} else {
		    // This free block will be completely used up.
		    SP->FreeCount -= 1;
		    ByteLen += LenLeft;
		}
		if (SP->FirstFree == CurF)
		    SP->FirstFree = NextF;
		else
		    ((SC_SlabFreePointer)&SlabData[PrevF])->NextFree = NextF;

		SP->DataUsed += ByteLen;
		GenP->BlockLen = ByteLen;	// Client sets DataCount/StrLen/SlotCount
		GenP->MasterPP = NULL;
		// DO NOT SC_DEBUG_CHECKSLAB here... GenP->DataCount is NOT SET yet!!
		return GenP;
	    }

	    // Slab has room, but is fragmented.
	    if (IsArr) {
		SC_SlabPackData(SP);
		goto TryAgain;
	    } else {
		// it is a CHUNK... Coalesce free blocks!
		if (SC_SlabCoalesceFree(SP, ByteLen))
		    goto TryAgain;
	    }
	}

	SP = SP->NextSP;
    }

    return NULL;
}

// SC_SlabAllocEntry will allocate an Entry record of EntrySize bytes on the
// Slab chain starting from FirstSlabP.  This function has a much simpler
// job than SC_SlabAllocDataBlock, as all blocks on Entry Slabs will have
// the same size.
//
// Entries DO NOT MOVE!  Allocating a new Entry does not require Packing
// or moving other Entries--since they are all the same size!
//
// NOTE:  Different sized Entry records belong on *DIFFERENT* Slab/chains.
//
// NOTE:  If all Slabs are full, the function will return NULL, at which point
//	  the caller should allocate a new Slab and try again.

void *		SC_SlabAllocEntry(SC_SlabPointer FirstSlabP, Uns32 EntrySize)
{
    SC_SlabPointer	SP = FirstSlabP;
    SC_SlabFreePointer	FreeP;
    void *		ResP;
    

    while (SP) {
	if (SP->FreeCount) break;	// Has at least 1 free block!
	SP = SP->NextSP;
    }

    if (SP == NULL) return NULL;

    // Found a slab with at least 1 free block... which can be a large one!
    // First and only initial Free block has NextFree == (Uns32)-1.
    ResP = FreeP = (SC_SlabFreePointer)&SP->Data[SP->FirstFree];
    G_ASSERT(FreeP->Zero == 0LL, "AllocEntry: Free is not free", 0);

    SP->DataUsed += EntrySize;
    if (FreeP->Size == EntrySize) {		// Exactly 1 Entry!
	SP->FirstFree = FreeP->NextFree;
	SP->FreeCount -= 1;
    } else {					// Room for 1 more+
	Uns32		FreeSize = FreeP->Size;
	Uns32		NextF = FreeP->NextFree;

	// FreeP->Size SHOULD NEVER be smaller!
	G_ASSERT(FreeSize > EntrySize, "AllocEntry: Free size is bad", FreeSize);

	SP->FirstFree += EntrySize;
	FreeSize -= EntrySize;			// Left over... should be 0 !!
	if (FreeSize < EntrySize) {
	    SP->FreeCount -= 1;
	    SP->FirstFree = (Uns32)-1;
	} else {
	    FreeP = (SC_SlabFreePointer)((char *)FreeP + EntrySize);
	    FreeP->Zero = 0L;
	    FreeP->Size = FreeSize;
	    FreeP->NextFree = NextF;
	}
    }

    SC_DEBUG_CHECKSLAB(SP);
    return ResP;
}


// SC_SlabFindSlab will find the Slab (on the chain) that contains the
// indicated Data block.  SlabP is normally the head of the chain.

SC_SlabPointer		SC_SlabFindSlab(SC_SlabPointer SlabP, void * DataP)
{
    while (SlabP) {
	if ((DataP > (void *)SlabP) &&
	    (DataP < (void *)&SlabP->Data[SlabP->DataSize]))
	    return SlabP;
	SlabP = SlabP->NextSP;
    }

    return NULL;
}


// SC_SlabZapItemOnSlab simply frees the indicated Item (data block) on its Slab.

void		SC_SlabZapItemOnSlab(SC_SlabPointer SlabP, void * ItemP, Uns32 ItemSize)
{
    SC_SlabFreePointer  FreeP;

    FreeP = (SC_SlabFreePointer)ItemP;
    FreeP->Zero = 0LL;
    FreeP->Size = ItemSize;
    FreeP->NextFree = SlabP->FirstFree;

    SlabP->FirstFree = (char *)FreeP - SlabP->Data;
    SlabP->FreeCount += 1;
    SlabP->DataUsed -= ItemSize;

    SC_DEBUG_CHECKSLAB(SlabP);    
}

// SC_SlabZapItem calls SC_SlabFindSlab to identify the Slab that holds this item!
// Then Zaps it using SC_SlabZapItemOnSlab.  This is a little slower.

void		SC_SlabZapItem(SC_SlabPointer FirstSlabP, void * ItemP, Uns32 ItemSize)
{
    SC_SlabPointer	SlabP = SC_SlabFindSlab(FirstSlabP, ItemP);

    SC_SAFE_CHECK(SlabP, "ZapItem: No Slab");
    SC_SlabZapItemOnSlab(SlabP, ItemP, ItemSize);
}



#ifdef DEBUG
    // SC_DEBUG_SlabListFree is available to print the free list on a slab.

    void		SC_DEBUG_SlabListFree(SC_SlabPointer SlabP, Uns32 ItemSize)
    {
	Uns64			NextFree;
	SC_SlabFreePointer	FreeP;

	printf("\nSlab data: Start:[%lx] Length:%d Bytes\n", (Uns64)SlabP->Data, SlabP->DataSize);
	printf("           FreeCount:%d  First Free:%d\n", SlabP->FreeCount, SlabP->FirstFree);

	NextFree = SlabP->FirstFree;
	FreeP = (SC_SlabFreePointer)(SlabP->Data + NextFree);
	while (NextFree != (Uns32)-1L) {
	    printf("     %ld: Free [%lx] %d Bytes -> %d\n", NextFree, (Uns64)FreeP, FreeP->Size, FreeP->NextFree);
	    NextFree = FreeP->NextFree;
	    FreeP = (SC_SlabFreePointer)(SlabP->Data + NextFree);
	}
	printf("Slab data: -----------------Done---------------\n\n");
    }
#endif

    // SC_AuxSlabGetAllSize sums up the total size of all Slabs on the chain
    // starting from SlabP.
    
    static	Uns32	SC_AuxSlabGetAllSize(SC_SlabPointer SlabP)
    {
	Uns32	Total = 0;

	while (SlabP) {
	    Total += offsetof(SC_SlabRecord, Data) + SlabP->DataSize;
	    SlabP = SlabP->NextSP;
	}

	return Total;
    }

// SC_SlabPrintStatus displays the state of memory.

void		SC_SlabPrintStatus(SC_SlabPointer SlabP, SC_PrintFuncP PrintFP)
{
    SC_SlabFreePointer	FreeP;
    Uns32		Count, TotalSize, FreeMin, FreeMax;
    Uns8		SType;

    Count = 0;
    SType = SlabP->Flags & SC_SLAB_TYPE_MASK;
    PrintFP("\n  Mem:%d ----> (%.*s) %s [Total: %'d]\n",
	    SType, 4, &SlabP->Tag, SC_SlabNameArr[SType], SC_AuxSlabGetAllSize(SlabP));
    while (SlabP) {
	Count += 1;
	TotalSize = offsetof(SC_SlabRecord, Data) + SlabP->DataSize;
	PrintFP("  (Slab:%d)    %'d Bytes total using %'d of %'d bytes\n", Count, TotalSize, SlabP->DataUsed, SlabP->DataSize);
	if (SlabP->FreeCount == 1)
	    PrintFP("              %'d Free block (%'d Bytes) ", SlabP->FreeCount, SlabP->DataSize - SlabP->DataUsed);
	else
	    PrintFP("              %'d Free blocks (%'d Bytes) ", SlabP->FreeCount, SlabP->DataSize - SlabP->DataUsed);
	    
	if (SlabP->FreeCount < 2)
	    PrintFP("\n");
	else {
	    char *	DataP = SlabP->Data;

	    FreeMin = -1L, FreeMax = 0;
	    FreeP = (SC_SlabFreePointer)(DataP + SlabP->FirstFree);
	    while (FreeP) {
		if (FreeP->Size < FreeMin) FreeMin = FreeP->Size;
		if (FreeP->Size > FreeMax) FreeMax = FreeP->Size;

		if (FreeP->NextFree == (Uns32)-1L) break;
		FreeP = (SC_SlabFreePointer)(DataP + FreeP->NextFree);
	    }
	    PrintFP(" (From %'d to %'d Bytes)\n", FreeMin, FreeMax);
	}

	SlabP = SlabP->NextSP;
    }
}


    Uns16	SC_SPPFindInSlab(SC_SlabPointer SP, void * Ptr, Uns32 * OffsetP)
    {
	Int64	Delta;
	Uns16	Ord = 1;

	// Ord is 1 based... 0 means failure!
	while (SP) {
	    Delta = (Int64)Ptr - (Int64)SP->Data;
	    if ((0 <= Delta) && (Delta < SP->DataSize)) {
		*OffsetP = (Uns32)Delta;
		return Ord;
	    }

	    SP = SP->NextSP;
	    Ord += 1;
	}

	return 0;
    }

    Int16	SC_SPPFindInVM(SC_VMPointer VMP, void * Ptr, Uns32 * OffsetP)
    {
	Int64	Delta;

	Delta = (Int64)Ptr - (Int64)VMP;
	if ((0 <= Delta) && (Delta < sizeof(SC_VMRecord))) {
	    *OffsetP = (Uns32)Delta;
	    return 1;
	}

	return 0;
    }

void	SC_SlabPrintPtr(SC_VMPointer VMP, void * Ptr, char * LabelP, char WS)
{
    // Find Slab and offset for Ptr!
    // Ptr type is unknown... must try different slab types!
    // (Entries have TYPE fields, but many other Ptrs *DO NOT*!)
    // Especially inefficient... display is slow anyway!

    SC_SlabPointer	SP;
    Uns32		Offset;
    Uns16		Ord;
    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;

    if (Ptr == NULL) {
	OutPFP("[%s NULL]%c", LabelP, WS);
	return;
    }

    if ((Ord = SC_SPPFindInSlab(SP = VMP->VarEntrySP, Ptr, &Offset)))
	goto ShowIt;
    if ((Ord = SC_SPPFindInSlab(SP = VMP->BigEntrySP, Ptr, &Offset)))
	goto ShowIt;
    if ((Ord = SC_SPPFindInSlab(SP = VMP->StrDataSP, Ptr, &Offset)))
	goto ShowIt;
    if ((Ord = SC_SPPFindInSlab(SP = VMP->ArrDataSP, Ptr, &Offset)))
	goto ShowIt;
    if ((Ord = SC_SPPFindInSlab(SP = VMP->ChunkSP, Ptr, &Offset)))
	goto ShowIt;
    if ((Ord = SC_SPPFindInSlab(SP = VMP->TokenSP, Ptr, &Offset)))
	goto ShowIt;
    if ((Ord = SC_SPPFindInSlab(SP = VMP->LexSP, Ptr, &Offset)))
	goto ShowIt;
    if ((Ord = SC_SPPFindInSlab(SP = VMP->MapSP, Ptr, &Offset)))
	goto ShowIt;
    if ((Ord = SC_SPPFindInSlab(SP = VMP->VexSP, Ptr, &Offset)))
	goto ShowIt;
	
    if (SC_SPPFindInVM(VMP, Ptr, &Offset)) {
	// Only for ADP Master Pointers!
	OutPFP("[%s %06d on VM Record]%c", LabelP, Offset, WS);
	return;
    }

    OutPFP("[%s 0x%016lx]%c", LabelP, Ptr, WS);
    return;
     
ShowIt:    
    OutPFP("[%s %06d on Mem:%d (%.*s) Slab:%d]%c", LabelP, Offset, SP->Flags & SC_SLAB_TYPE_MASK, 4, (char *)&SP->Tag, Ord, WS);
}

// Just like SC_SlabPrintPtr, only without searching all different slabs.
void	SC_SlabPrintTypePtr(SC_PrintFuncP OutPFP, SC_SlabPointer SP, void * Ptr, char* LabelSP, char WS, Uns8 Lead)
{
    Uns32		Offset;
    Uns16		Ord;

    if (Ptr == NULL) {
	OutPFP("%*s[%s NULL]%c", Lead, "", LabelSP, WS);
	return;
    }

    Ord = SC_SPPFindInSlab(SP, Ptr, &Offset);
    OutPFP("%*s[%s %06d on Mem:%d (%.*s) Slab:%d]%c", Lead, "", LabelSP, Offset, SP->Flags & SC_SLAB_TYPE_MASK, 4, (char *)&SP->Tag, Ord, WS);
}




#if SC_DEBUG_DISP_ALLOC

    void	SC_DebugSlabPrintPtr(SC_VMPointer VMP, void * Ptr, char * S, SC_SlabPointer SP)
    {
	Uns32		Offset;
	Uns16		Ord;

	Ord = SC_SPPFindInSlab(SP, Ptr, &Offset);
	printf("%s --> [Var %06d on Mem:%d (%.*s) Slab:%d] 0x%012lx\n", S, Offset, SP->Flags & SC_SLAB_TYPE_MASK,
			4, (char *)&SP->Tag, Ord, (Int64)Ptr);
    }

    #define	SC_DEBUG_VARDISP(_VMP, _VP, _S)		SC_DebugSlabPrintPtr(_VMP, _VP, _S, VMP->VarEntrySP)
    #define	SC_DEBUG_BIGVARDISP(_VMP, _VP, _S)	SC_DebugSlabPrintPtr(_VMP, _VP, _S, VMP->BigEntrySP)
    #define	SC_DEBUG_CHUNKVARDISP(_VMP, _VP, _S)	SC_DebugSlabPrintPtr(_VMP, _VP, _S, VMP->ChunkSP)    
#else
    #define	SC_DEBUG_VARDISP(_VMP, _VP, _S)		// Do nothing!
    #define	SC_DEBUG_BIGVARDISP(_VMP, _VP, _S)	// Do nothing!
    #define	SC_DEBUG_CHUNKVARDISP(_VMP, _VP, _S)	// Do nothing!
#endif


// ********************************************************************************
// RefCount GC

// RefCount GC can be turned on/off... so use macros for VarRef and VarDeRef!

#if SC_GC_REFCOUNT
    // MUST MATCH SC_ENTRY_TYPE ******************************************@@@**
    Uns32	SC_RefCountVarArr[] = {0,	// GEN--nothing
				       1,	// Var--Inc for Ref
				       1,	// Pair--GC with its Dict
				       1,	// Arr--Inc for Ref
				       1,	// Str--Inc for Ref
				       1,	// Dict--Inc for Ref
				       1,	// Ext
				       1,	// ArrXT
				       0,	// Case--Should not play here
				       1,	// RC--Inc for Ref
				       1,	// Pat--Inc for Ref
				       1,	// Pkg--Inc for Ref
				       1,	// ChunkXT
				       1,	// Clos--Inc for Ref
				       1,	// CodeBlock--Inc for Ref
				       0,	// Run No Ref to count!
				       1,	// Script--Inc for Ref
				      };


    // SC_VarRef is only called to reference the ValueP of a Var that is an SC_VAL_PTR... so it
    // already has one level of ptr ref.
    
    #define SC_VARREFCOND(_COND, _V)		if (_COND) (_V)->RefCount += SC_RefCountVarArr[(_V)->Type]
    #define SC_VARREF(_V)			(_V)->RefCount += SC_RefCountVarArr[(_V)->Type]
    #define SC_VARINCREF(_V)			(_V)->RefCount += 1

    #define SC_VARDEREFCOND(_VMP, _COND, _V)	if (_COND) SC_VarDeRef(_VMP, _V)
    #define SC_VARDEREF(_VMP, _V)		SC_VarDeRef(_VMP, _V)
#else
    #define SC_VARREFCOND(_COND, _V)
    #define SC_VARREF(_V)
    #define SC_VARINCREF(_V)
    
    #define SC_VARDEREFCOND(_VMP, _COND, _V)
    #define SC_VARDEREF(_VMP, _V)

    #define SC_GC_SETSMARTCOUNT(_S)
#endif


#if SC_GC_REFCOUNT

    #if SC_GC_STRSMARTCOUNT

	// GC STR SMART REF COUNT is a heuristic to give Strings a "dwell" time after
	// their RefCount goes to 0.  Small strings dwell longer... as they are more
	// likely to be used again!  Mark/Sweep GC ignores all this and will expunge
	// unused StrHPointers.

	#define SC_GC_STRSMART_MAX		(1024 * 64)		// Zap immediately if bigger!

	// **MUST NOT** collide with SC_GCDONT STATE that may also get stored in StrHP->State field
	Uns8    SC_GC_StrSmartArr[] = {132, 114, 97, 91, 85, 79, 73, 67, 62, 60, 58, 56, 54, 52, 50, 49, 
				       47, 45, 43, 41, 39, 38, 36, 34, 32, 30, 28, 26, 26, 25, 25, 24, 
				       24, 23, 22, 22, 21, 21, 20, 19, 19, 18, 18, 17, 17, 16, 15, 15, 
				       14, 14, 13, 13, 12, 11, 11, 10, 10, 9, 8, 8, 7, 7, 6, 5, 
				       5, 4, 4, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
				       // Anything bigger gets 2 until SC_GC_STRSMART_MAX

	void	  SC_GC_SetSmartCount(SC_StrHPointer SHP)
	{
	    Uns32	I = SHP->StrDataP->StrLen >> 6;		// I = Len/64
	    
	    SHP->State = (I >= sizeof(SC_GC_StrSmartArr)) ? 2 : SC_GC_StrSmartArr[I];
	}

	#define	SC_GC_SETSMARTCOUNT(_S)		SC_GC_SetSmartCount(_S)
    #else
	#define	SC_GC_SETSMARTCOUNT(_S)
    #endif

/* ************************************************************************************
	    // Call from SC_VMInit to generate above chart... tune if required.
	    // Assumes 128 entries... trim as necessary.
	    void	SC_GCInitSmartRefCount(void)
	    {
		double	Size = 35;
		Uns16	Steps = 2;
		Uns16	I, SCount;
		double  Last;

		// NOTE:   SC_GC_StrSmartArr[n] should NEVER be same as SC_GCDONT!!
		//	   If that is the case, scrub all StrHPointers befor marking for GC sweep.

		SC_GC_StrSmartArr[0] = 132;
		Last = (double)SC_GC_StrSmartArr[0];
		SCount = 0;
		for (I = 1; I < 128; I++) {
		    if (SCount == Steps)
			SCount = 0, Steps *= 3.2;

		    Last -= (Size/Steps);
		    SC_GC_StrSmartArr[I] = (Last < 2) ? 2 : Last;
		    SCount++;
		}

		printf("\n\nSmartRefArr --> ");
		for (I = 1; I < 129; I++) {
		    printf("%d, ", SC_GC_StrSmartArr[I-1]);
		    if (!(I % 16))
			printf("\n                ");
		}
		printf("\n\n");
	    }
******************************************************************************** */

	void	SC_VDRAuxCode(SC_VMPointer VMP, SC_CodeBlockPointer CBP)
	{
	    SC_CodeBlockPointer *	FuncCBPP;
	    SC_CodeBlockPointer *	EndCBPP;

	    CBP->RefCount -= 1;
	    if (CBP->RefCount == 0) {
		// Zap the CodeBlock

		SC_DEBUG_CHUNKVARDISP(VMP, CBP, "Purge CodeBlk");
		if (CBP->CaseDictP) SC_DictPurge(VMP, CBP->CaseDictP, 1);	// No Vars, Force Zap

		// Assumes CBP->CodeArr is on LL aligned address!
		FuncCBPP = (SC_CodeBlockPointer *)((Uns8 *)CBP->CodeArr + SC_LLALIGN(CBP->CodeLen + (sizeof(SC_VDescRecord) * CBP->CCount)));
		EndCBPP = FuncCBPP + CBP->FuncCount;
		// Recurse to de-ref each SUB func.
		while (FuncCBPP < EndCBPP)
		    SC_VDRAuxCode(VMP, *FuncCBPP++);

		SC_SlabZapItem(VMP->ChunkSP, CBP, CBP->BlockLen);
	    }
	}


	void	SC_StrADPDeRefZap(SC_VMPointer VMP, SC_ArrDataPointer SADP, Uns32 Count)
	{
	    SC_StrHPointer *	SHPP = (SC_StrHPointer *)SADP->Data;
	    Uns32			I = Count;

	    SC_SAFE_CHECK(I <= SADP->SlotCount, SC_BADRUN_Index);
	    while (I--) {
		SC_SAFE_CHECK((*SHPP)->Type == SC_ENTRY_STR, SC_BADRUN_Var);

		(*SHPP)->RefCount -= 1;
		if ((*SHPP)->RefCount == 0) {
		    SC_DEBUG_BIGVARDISP(VMP, *SHPP, "Purge Str   ");
		    SC_StrPurge(VMP, *SHPP, NULL);
		}
		SHPP++;
	    }
	    SC_SlabZapItem(VMP->ArrDataSP, SADP, SADP->BlockLen);
	}

    void	SC_VarDeRef(SC_VMPointer VMP, SC_VarVPointer VarP)
    {
	// @@@ Must match declared SC_ENTRY_TYPE    
	static void *	DispArr[] = {&&DoGen, &&DoVar, &&DoPair, &&DoArr, &&DoStr, &&DoDict, &&DoEXT, &&DoArrXT,
				     &&DoCase, &&DoRC, &&DoPat, &&DoPkg, &&DoChunkXT, &&DoClos, &&DoCode, &&DoRun, &&DoScrpt};
	SC_VarVPointer	NextVarP;

    Start:
        SC_SAFE_CHECK(VarP->Type <= SC_CHUNK_SCRPT, SC_BADRUN_Type);	
	goto *DispArr[VarP->Type];		// Dispatch Array

    // ************************************************************
    
    DoGen:;	// Just to be safe... this should be impossible!!
    DoCase:;	// Should never see!!
    DoRun:	// Should never see!!
	SC_SysError(SC_BADRUN_Type);
	return;
	
    DoPair:
	VarP->RefCount -= 1;
	if (VarP->RefCount == 0) {
	    // If not RefCounting, KeyP and ValueP will be collected by M/S GC later!!
	    SC_VARDEREFCOND(VMP, (*(SC_PairHPointer)VarP).KeyType == SC_VAL_PTR, (*(SC_PairHPointer)VarP).KeyP);
	    SC_VARDEREFCOND(VMP, (*(SC_PairHPointer)VarP).ValType == SC_VAL_PTR, (*(SC_PairHPointer)VarP).ValueP);

	    SC_DEBUG_BIGVARDISP(VMP, VarP, "Purge Pair  ");
	    SC_SlabZapItem(VMP->BigEntrySP, VarP, sizeof(SC_PairHRecord));
	}
    
	return;

    DoVar:
	VarP->RefCount -= 1;
	if (VarP->RefCount == 0) {
	    // Var will be zapped... so DEC RefCount of what it points to!
	    if (VarP->ValueType == SC_VAL_PTR) {
		SC_DEBUG_VARDISP(VMP, VarP, "Purge Var   ");	    
		NextVarP = (SC_VarVPointer)VarP->ValueP;
		SC_SlabZapItem(VMP->VarEntrySP, VarP, sizeof(SC_VarVRecord));

		// Iterate... faster than recurse!
		VarP = NextVarP;
		goto Start;
	    }

	    // Here if Int or Flt var!
	    SC_DEBUG_VARDISP(VMP, VarP, "Purge Var   ");
	    SC_SlabZapItem(VMP->VarEntrySP, VarP, sizeof(SC_VarVRecord));
	}
	return;

    DoEXT:;
    DoArrXT:
	// NOTE: For ArrXT *DO NOT* call SC_ArrPurge on VarP!
	//	 Fields in ArrExt's ArrDataP *ARE NOT* entry pointers.
	//	 Client will DeRef/Zap pointers in ArrDataP as appropriate!
    
	VarP->RefCount -= 1;
	if (VarP->RefCount == 0) {
	    // Do not zap if SC_GCDONT *OR* Client says no!!
	    // If so, reset RefCount so a) doesn't go negative and b) will catch next cycle!
	    if ((VarP->State == SC_GCDONT) ||
		((SC_EXTGCArr[(*(SC_EXTVPointer)VarP).ExtType]) &&
		 ! (SC_EXTGCArr[(*(SC_EXTVPointer)VarP).ExtType](VMP, VarP, 0))))
		VarP->RefCount = 1;			// Will catch again... maybe!
	    else {
		SC_DEBUG_VARDISP(VMP, VarP, (VarP->Type == SC_ENTRY_EXT) ?
					    "Purge Ext   " : "Purge A-Ext ");
		SC_SlabZapItem(VMP->VarEntrySP, VarP, sizeof(SC_EXTVRecord));
	    }
	}
	return;

    DoChunkXT:
	VarP->RefCount -= 1;
	if (VarP->RefCount == 0) {
	    // Do not zap if SC_GCDONT *OR* Client says no!!
	    // If so, reset RefCount so a) doesn't go negative and b) will catch next cycle!
	    if ((VarP->State == SC_GCDONT) ||
		((SC_EXTGCArr[(*(SC_ChunkXTVPointer)VarP).ExtType]) &&
		 ! (SC_EXTGCArr[(*(SC_ChunkXTVPointer)VarP).ExtType](VMP, VarP, 0))))
		VarP->RefCount = 1;			// Will catch again... maybe!
	    else {
		SC_DEBUG_VARDISP(VMP, VarP, "Purge C-Ext ");
		SC_SlabZapItem(VMP->ChunkSP, VarP, (*(SC_ChunkXTVPointer)VarP).BlockLen);
	    }
	}
	return;
	
    DoArr:
	// Arr purge could be deferred to special "Incremental Sweep" cycle to
	// make RefCount operation smoother!
	VarP->RefCount -= 1;
	if (VarP->RefCount == 0) {
	    SC_DEBUG_VARDISP(VMP, VarP, "Purge Arr   ");
	    SC_ArrPurge(VMP, (SC_ArrVPointer)VarP);
	}
	return;

    DoStr:
	VarP->RefCount -= 1;
	// In case SC_GC_STRSMARTCOUNT and StrHP stays around!
	SC_SAFE_CHECK(VarP->RefCount != (Uns32)-1L, SC_BADRUN_RefCount);
	
	if (VarP->RefCount == 0) {
	    #if SC_GC_STRSMARTCOUNT

		// NOTE:   SC_GC_StrSmartArr[n] should NEVER be same as SC_GCDONT!!
		//	   If that is the case, scrub all StrHPointers befor GC Mark.
		{
		    Uns32	I = (*(SC_StrHPointer)VarP).StrDataP->StrLen >> 6;	// I = Len/64

		    if (I >= (SC_GC_STRSMART_MAX >> 6)) {
			// String is huge... just get zap it now!
			SC_DEBUG_BIGVARDISP(VMP, VarP, "Purge Str   ");
			SC_StrPurge(VMP, (SC_StrHPointer)VarP, NULL);
		    }
		    
		    VarP->State = (I >= sizeof(SC_GC_StrSmartArr)) ? 2 : SC_GC_StrSmartArr[I];
		}
	    #else
		SC_DEBUG_BIGVARDISP(VMP, VarP, "Purge Str   ");
		SC_StrPurge(VMP, (SC_StrHPointer)VarP, NULL);
	    #endif
	}
	return;

    DoDict:
	// Dict purge could be deferred to special "Incremental Sweep" cycle to
	// make RefCount operation smoother!
	//
	// SC_DictPurge will remove PairP->NextPairP linkage from surviving Pairs!
	VarP->RefCount -= 1;
	if (VarP->RefCount == 0) {
	    SC_DEBUG_BIGVARDISP(VMP, VarP, "Purge Dict  ");
	    SC_DictPurge(VMP, (SC_DictVPointer)VarP, 0);
	}
	return;

    DoScrpt:
	VarP->RefCount -= 1;
	if (VarP->RefCount == 0) {
	    SC_ScriptPointer	ScriptP = (SC_ScriptPointer)VarP;

	    // NOTE: Possible to DeRef ScriptP if fail during creation!
	    //	     So some fields may be missing!!  (E.g. when reading ".bsc")

	    SC_DEBUG_CHUNKVARDISP(VMP, VarP, "Purge Script ");
	    if (ScriptP->GlobP) SC_RunZapGlob(VMP, ScriptP->GlobP);
	    if (ScriptP->KonstADP) {
		SC_SAFE_CHECK(ScriptP->KonstCount <= ScriptP->KonstADP->SlotCount, SC_BADRUN_Index);
		SC_SlabZapItem(VMP->ArrDataSP, ScriptP->KonstADP, ScriptP->KonstADP->BlockLen);
	    }

	    if (ScriptP->StringADP) {
		SC_SAFE_CHECK(ScriptP->StringCount <= ScriptP->StringADP->SlotCount, SC_BADRUN_Index);
		SC_StrADPDeRefZap(VMP, ScriptP->StringADP, ScriptP->StringCount);
	    }
	    if (ScriptP->GNameADP) {
		SC_SAFE_CHECK(ScriptP->GlobCount <= ScriptP->GNameADP->SlotCount, SC_BADRUN_Index);
		SC_StrADPDeRefZap(VMP, ScriptP->GNameADP, ScriptP->GlobCount);
	    }

	    SC_VarDeRef(VMP, (SC_VarVPointer)ScriptP->NameSHP);
	    SC_VarDeRef(VMP, (SC_VarVPointer)ScriptP->PathSHP);

	    NextVarP = (SC_VarVPointer)ScriptP->TopClosP;	// Stash TopClosP
	    SC_SlabZapItem(VMP->ChunkSP, ScriptP, sizeof(SC_ScriptRecord));

	    if (NextVarP) {
		VarP = NextVarP;				// Iterate to zap TopClosP
		goto Start;
	    }

	}
	return;

    DoPkg:
	VarP->RefCount -= 1;
	if (VarP->RefCount == 0) {
	    SC_PkgPointer	PkgP = (SC_PkgPointer)VarP;

	    SC_DEBUG_BIGVARDISP(VMP, VarP, "Purge Pkg ");
	    if (PkgP->GlobP) SC_RunZapGlob(VMP, PkgP->GlobP);
	    NextVarP = (SC_VarVPointer)PkgP->NDictP;		// Stash NDictP
	    SC_SlabZapItem(VMP->BigEntrySP, VarP, sizeof(SC_PkgRecord));
	    
	    if (NextVarP) {					// Iterate to zap NDictP
		VarP = NextVarP;
		goto Start;
	    }
	}
	return;

    DoRC:
	VarP->RefCount -= 1;
	{
	    SC_RCPointer		RCP = (SC_RCPointer)VarP;

	    if (RCP->ResumeP) {
		// RCP of a yielded run stack... will never resume!
		if (RCP->RefCount <= 1) {			// Its RunP still refs RCP!
		    SC_DEBUG_BIGVARDISP(VMP, VarP, "Purge RCP   ");
		    if (RCP->StackCopyP)			// Should be NULL by now
			SC_RunZapStackCopy(VMP, RCP);		// ...Defensive!
		    SC_RunReleaseRunChain(VMP, RCP->ResumeP, RCP->RunP);
		    SC_SlabZapItem(VMP->BigEntrySP, VarP, sizeof(SC_RCRecord));
		}

	    } else if (RCP->RefCount == 0) {
		// RunP has been zapped and the RCP is no longer in a Var!
		SC_DEBUG_BIGVARDISP(VMP, VarP, "Purge RCP   ");
		if (RCP->StackCopyP)				// Should be NULL by now
		    SC_RunZapStackCopy(VMP, RCP);		// ...Defensive!
		SC_SlabZapItem(VMP->BigEntrySP, VarP, sizeof(SC_RCRecord));
	    }
	}
	return;

    DoPat:
        VarP->RefCount -= 1;
	if (VarP->RefCount == 0) {
	    SC_PatPointer	PatP = (SC_PatPointer)VarP;

	    // Iterate on the NextP chain, but recurse on Data[0] and Data[1]
	    // These can be strings, data, or other Pats!
	    while (PatP) {
		if ((PatP->Flags & SC_PAT_DATA0P) && (PatP->Data[0]))
		    SC_VarDeRef(VMP, PatP->Data[0]);		// Recurse
		if ((PatP->Flags & SC_PAT_DATA1P) && (PatP->Data[1]))
		    SC_VarDeRef(VMP, PatP->Data[1]);		// Recurse

		// Stash NectVarP to iterate... CMAP and CRANGE *DO NOT HAVE* tha field!!
		if ((PatP->PatOp == SC_PAT_CMAP) || (PatP->PatOp == SC_PAT_CRANGE))
		    NextVarP = NULL;
		else
		    NextVarP = (SC_VarVPointer)PatP->NextP;
		SC_SlabZapItem(VMP->BigEntrySP, PatP, sizeof(SC_PatRecord));
		
		PatP = (SC_PatPointer)NextVarP;
		// NOTE:  The next chain always belongs with the first Pat node.
		//	  So if the first Pat node is getting zapped, so should
		//	  the next chain.  No need to check their RefCount!
	    }
	}	
	return;

    DoClos:
        VarP->RefCount -= 1;
        if (VarP->RefCount == 0) {
	    SC_CodeBlockPointer	CBP = ((SC_ClosPointer)VarP)->CBP;
	    SC_VarVPointer *	VarPP = ((SC_ClosPointer)VarP)->VarArr;
	    SC_VarVPointer *	EndPP = VarPP + CBP->CCount;

	    SC_DEBUG_CHUNKVARDISP(VMP, VarP, "Purge Clos  ");
	    // DeRef the CBP... which refs sub-CBP...
	    SC_VDRAuxCode(VMP, CBP);

	    while (VarPP < EndPP)
		SC_VarDeRef(VMP, *VarPP++);

	    SC_SlabZapItem(VMP->ChunkSP, VarP, (*(SC_ClosPointer)VarP).BlockLen);
	}
	return;

    DoCode:
	SC_VDRAuxCode(VMP, (SC_CodeBlockPointer)VarP);
	return;
    }

#endif

// ********************************************************************************
// Mark + Sweep GC

SC_VMPointer	SC_GCVMP;		// Stash for occasional access

// Call SC_GCMarkVar only if VarP->State == SC_NOSTATE !!
// Check before recursing... more efficient.
//
// Minimize locals in recursive function, smaller stack frame.

    SC_VarVPointer *	SC_GCAuxMarkVArr(SC_VarVPointer * VPP, Uns16 Count)
    {
	SC_VarVPointer *	EndPP = VPP + Count;
	while (VPP < EndPP) {
	    // Unused registers are initially NULL in Var Array,
	    // but Regs are NOT marked!
	    if (! ((*VPP)->Type & SC_GC_MARK))
		SC_GCMarkVar(*VPP);
	    VPP += 1;
	}

	return EndPP;
    }

    void		SC_GCAuxMarkStack(SC_VarVPointer VP, Uns32 Count)
    {
	// Each 'cell' is a Var *RECORD*, not a Ptr!
	while (Count--) {
	    if ((VP->ValueType == SC_VAL_PTR) && VP->ValueP &&
		! ((*(SC_VarVPointer)VP->ValueP).Type & SC_GC_MARK))
		SC_GCMarkVar((SC_VarVPointer)VP->ValueP);
	    VP += 1;
	}
    }

    void		SC_GCAuxMarkStackCopy(SC_ArrDataPointer StackCopyP)
    {
	SC_VarVPointer	VP = (SC_VarVPointer)StackCopyP->Data;
	Uns32		Count = (StackCopyP->SlotCount * sizeof(void *)) / sizeof(SC_VarVRecord);

	if (Count) SC_GCAuxMarkStack(VP, Count);
    }

    void		SC_GCAuxMarkCode(SC_CodeBlockPointer CBP)
    {
	SC_CodeBlockPointer *	VPP;
	SC_CodeBlockPointer *	EndPP;

	CBP->Type |= SC_GC_MARK;
	// Mark the CaseDictP if there is one!
	if (CBP->CaseDictP && ! (CBP->CaseDictP->Type & SC_GC_MARK))
	    SC_GCMarkVar((SC_VarVPointer)CBP->CaseDictP);

	// Mark the Sub func code blocks... if any!
	if (CBP->FuncCount) {
	    VPP = (SC_CodeBlockPointer *)((Uns8 *)CBP->CodeArr + SC_LLALIGN(CBP->CodeLen + (sizeof(SC_VDescRecord) * CBP->CCount)));
	    EndPP = VPP + CBP->FuncCount;
	    while (VPP < EndPP) {
		SC_SAFE_CHECK(((*VPP)->Type & SC_GC_TYPEMASK) == SC_CHUNK_CODE, SC_BADRUN_Type);
		if (! ((*VPP)->Type & SC_GC_MARK))
		    SC_GCAuxMarkCode(*VPP);
		VPP++;
	    }
	}
    }

    // Runs do not get RefCount as they are impossible to stash in Var!
    // And it is impossible to get free ones left over...
    //
    // NOTE: RunP->InCount is ACTUAL IN params given to the call.
    //	     CBP->InCount is SPECIFIED IN params for the function.
    
    void		SC_GCAuxMarkRun(SC_RunPointer RunP)
    {
	SC_VarVPointer *	VPP = RunP->VarArr;
	SC_CodeBlockPointer	CBP = (SC_CodeBlockPointer)*VPP++;	// VarArr[0] is CBP
	Int16			Count;

	RunP->Type |= SC_GC_MARK;

	// Mark the RCP... no need to recurse on this circular construct!
	// (May already be marked, but faster than checking!)
	if (RunP->RCP)
	    RunP->RCP->Type |= SC_GC_MARK;

	if (!(CBP->Type & SC_GC_MARK))
	    SC_GCAuxMarkCode(CBP);

	// VPP is set at VarArr[1]
	// Regs are mapped to globals, so skip them.
	VPP += RunP->RCount;
	// Mark the IN args... SPEC totals from CBP
	if (CBP->InCount)
	    VPP = SC_GCAuxMarkVArr(VPP, CBP->InCount);
	// Mark the OUT args... usually locals/global from elsewhere, but NOT ALWAYS!
	if (CBP->OutCount)
	    VPP = SC_GCAuxMarkVArr(VPP, CBP->OutCount);
	// Just do remaining LVars and CVars.
	// (CLOS may be gone by now... A function could overwrite the Global
	// Var that holds its CLOS!!)
	if ((Count = CBP->LCount + CBP->CCount - (CBP->InCount + CBP->OutCount))) // Assign
	    VPP = SC_GCAuxMarkVArr(VPP, Count);

	// In/Out Arrays are unique to this RunRecord... so Mark and process
	if (RunP->Flags & SC_RUN_ALLARGSFLAG) {
	    SC_ArrVPointer	ArrP;
	    // Mark InArgArrP
	    ArrP = (SC_ArrVPointer)*VPP++;
	    ArrP->Type |= SC_GC_MARK;
	    SC_GCAuxMarkVArr((SC_VarVPointer *)&ArrP->ArrDataP->Data[0], ArrP->ArrDataP->SlotCount);
	    // Mark  OutArgArrP
	    ArrP = (SC_ArrVPointer)*VPP;
	    ArrP->Type |= SC_GC_MARK;
	    SC_GCAuxMarkVArr((SC_VarVPointer *)&ArrP->ArrDataP->Data[0], ArrP->ArrDataP->SlotCount);
	}
    }

	void		SC_GCAMSAuxSADP(SC_ArrDataPointer SADP, Uns32 Count) {
	    SC_StrHPointer	*PP, *EndPP;

	    PP = (SC_StrHPointer *)SADP->Data;
	    EndPP = PP + Count;
	    while (PP < EndPP)
		(*(SC_StrHPointer)*PP++).Type |= SC_GC_MARK;
	}

    void		SC_GCAuxMarkScript(SC_ScriptPointer ScriptP)
    {
	SC_RunPointer		RunP;

	while (ScriptP) {
	    if (! (ScriptP->Type & SC_GC_MARK)) {
		ScriptP->Type |= SC_GC_MARK;
		if (ScriptP->GlobP)
		    SC_GCAuxMarkVArr(ScriptP->GlobP->GArr, ScriptP->GlobCount);

		// Mark the Run stack.... will hit TopRunP too!!
		RunP = ScriptP->CurRunP;
		while (RunP) {
		    if (! (RunP->Type & SC_GC_MARK))
			SC_GCAuxMarkRun(RunP);		// Just marks THIS run

		    RunP = RunP->CallerP;
		}

		if (ScriptP->StringADP) SC_GCAMSAuxSADP(ScriptP->StringADP, ScriptP->StringCount);
		if (ScriptP->GNameADP) SC_GCAMSAuxSADP(ScriptP->GNameADP, ScriptP->GlobCount);

		// When LOADing a Script, possible to have no Name/Path SHP while compiling.
		if (ScriptP->NameSHP) ScriptP->NameSHP->Type |= SC_GC_MARK;
		if (ScriptP->PathSHP) ScriptP->PathSHP->Type |= SC_GC_MARK;
		if (! (ScriptP->TopClosP->Type & SC_GC_MARK))
		    SC_GCMarkVar((SC_VarVPointer)ScriptP->TopClosP);
	    }

	    // Next superior script
	    ScriptP = ScriptP->SupScriptP;
	}
    }


void	SC_GCMarkVar(SC_VarVPointer VarP)
{
    // @@@ Must match declared SC_ENTRY_TYPE
    static void *		ProcArr[] = {&&DoGen, &&DoVar, &&DoPair, &&DoArr, &&DoStr, &&DoDict,
					     &&DoExt, &&DoExtArr, &&DoCase, &&DoRC, &&DoPat, &&DoPkg,
					     &&DoExtCh, &&DoClos, &&DoCode, &&DoRun, &&DoScrpt, &&DoGlob, &&DoStak};

    Uns8	VType;
    
    if (VarP->Type & SC_GC_MARK) return;

Start:
    VType = VarP->Type & SC_GC_TYPEMASK;
    SC_SAFE_CHECK(VType < SC_ENTRY_COUNT, SC_BADRUN_Type);
    
    goto *ProcArr[VType];
    // *********************************************************

DoGen:;		// Nothing is SC_ENTRY_GEN
DoPkg:;		// Done explicitly by SC_GCMark
DoCode:;	// Marked as part of ScriptP
DoRun:;		// Marked as part of ScriptP and RunP
		// NOTE:  RUN Records *ARE* subject to GC, but no RefCount!

DoGlob:;	// Marked as part of PkgP and ScriptP
DoStak:		// Done explicitly by SC_GCMark
	 SC_SysError("GCMark: Bad Value type");
	 return;

DoPair:  VarP->Type |= SC_GC_MARK;
	 {
	     SC_PairHPointer	PairP = (SC_PairHPointer)VarP;
	     
	    if ((PairP->KeyType == SC_VAL_PTR) && PairP->KeyP && !(PairP->KeyP->Type & SC_GC_MARK))
		SC_GCMarkVar(PairP->KeyP);
	    if ((PairP->ValType == SC_VAL_PTR) && PairP->ValueP && !(PairP->ValueP->Type & SC_GC_MARK))
		SC_GCMarkVar(PairP->ValueP);
	 } return;

DoCase:	VarP->Type |= SC_GC_MARK;
	// No Key/Val ptrs for these!
        return;

DoVar:  VarP->Type |= SC_GC_MARK;
	if (VarP->ValueType == SC_VAL_PTR) {
	    VarP = (SC_VarVPointer)VarP->ValueP;
	    if (! (VarP->Type & SC_GC_MARK))
		goto Start;
	} return;

DoArr:  VarP->Type |= SC_GC_MARK;
	{
	    SC_ArrDataPointer	ADP = (*(SC_ArrVPointer)VarP).ArrDataP;
	    SC_VarVPointer *	CurVPP = (SC_VarVPointer *)ADP->Data;
	    SC_VarVPointer *	EndVPP = CurVPP + ADP->SlotCount;

	    while (CurVPP < EndVPP) {
		if (*CurVPP && ! ((*CurVPP)->Type & SC_GC_MARK))
		    SC_GCMarkVar(*CurVPP);	// Recurse
		CurVPP += 1;
	    }
	} return;

DoStr:	VarP->Type |= SC_GC_MARK;
	return;

DoDict: VarP->Type |= SC_GC_MARK;
	{
	    SC_PairHPointer	PairP = (*(SC_DictVPointer)VarP).FirstPairP;

	    while (PairP) {
		// Pair is in DICT, so *MUST* set SC_GC_INDICT bit!!
		// CaseDictP will also come here!
		PairP->Type |= SC_GC_INDICT;
		SC_GCMarkVar((SC_VarVPointer)PairP);

		PairP = PairP->NextPairP;
	    }
	} return;

DoExt:;		// VEnt Entry, Data stored externally
DoExtArr:;	// VEnt Entry, Data stored in Arr
DoExtCh:	// Entry+Data in Chunk
	{
	    SC_EXTGCFuncPointer		MarkFP = SC_EXTGCArr[(*(SC_EXTVPointer)VarP).ExtType];

	    // Call client to Mark this Var... might reference other scScript data!
	    if ((MarkFP == NULL) || MarkFP(SC_GCVMP, VarP, 1))
		VarP->Type |= SC_GC_MARK;
	} return;

DoRC:	VarP->Type |= SC_GC_MARK;
	{
	    SC_RCPointer	RCP = (SC_RCPointer)VarP;
	    SC_RunPointer	RunP = RCP->RunP;
	    SC_RunPointer	ResP = RCP->ResumeP;

	    // Multiple vars can store the same RC... so must still check State!
	    if (ResP) {
		while (ResP != RunP) {
		    if (!(ResP->Type & SC_GC_MARK))
			SC_GCAuxMarkRun(ResP);
		    ResP = ResP->CallerP;
		}

		if (RCP->StackCopyP)
		    SC_GCAuxMarkStackCopy(RCP->StackCopyP);
	    }

	    // Went up to RunP above... now do RunP
	    if (RunP && !(RunP->Type & SC_GC_MARK))
		SC_GCAuxMarkRun(RunP);

	} return;

DoPat:	{
	    SC_PatPointer	PatP = (SC_PatPointer)VarP;

	    while (PatP) {
		PatP->Type |= SC_GC_MARK;

		if ((PatP->Flags & SC_PAT_DATA0P) && (PatP->Data[0]))
		    SC_GCMarkVar(PatP->Data[0]);
		if ((PatP->Flags & SC_PAT_DATA1P) && (PatP->Data[1]))
		    SC_GCMarkVar(PatP->Data[1]);

		// CMAP and CRANGE *DO NOT HAVE* a NextP field!!
		if ((PatP->PatOp == SC_PAT_CMAP) || (PatP->PatOp == SC_PAT_CRANGE)) break;
		PatP = PatP->NextP;
	    }
	} return;

DoScrpt:SC_GCAuxMarkScript((SC_ScriptPointer)VarP);
	return;
	
DoClos: VarP->Type |= SC_GC_MARK;
	{
	    SC_CodeBlockPointer	CBP = (SC_CodeBlockPointer)(*(SC_ClosPointer)VarP).CBP;
	    SC_VarVPointer *	VPP = (*(SC_ClosPointer)VarP).VarArr;

	    // Mark the CodeBlock...
	    if (! (CBP->Type & SC_GC_MARK))
		SC_GCAuxMarkCode(CBP);

	    if (CBP->CCount)
		SC_GCAuxMarkVArr(VPP, CBP->CCount);
	} return;
}

// SC_GCMark goes through all runtime start points and marks all reachable data
// structures.
//
// Read/Compile/CodeGen(CG) has very little waste memory... just a minimal
// number of tokens and the ScriptP being compiled!  So no need to mark/sweep
// for those phases.  GCMark really only cares about runtime!!
//
// NOTE: VarP->Type are marked with SC_GC_MARK.
//	 PairP reached from their Dict, will also get SC_GC_INDICT!

void	SC_GCMark(SC_VMPointer VMP)
{
    SC_PkgPointer		PkgP;
    Uns16			I;
    SC_EXTGCFuncPointer		MarkFP = SC_EXTGCArr[SC_EXT_NONE];

    SC_GCVMP = VMP;
    // Mark script!  Will mark all superiors too!
    SC_GCAuxMarkScript(VMP->CompileScriptP);
    SC_GCAuxMarkScript(VMP->RunScriptP);
    
    // Mark Pkg GLOBs, then NDict.  Pkg itself WILL never get zapped.
    for (I = 0; I < VMP->PkgCount; I++) {
	PkgP = (SC_PkgPointer)VMP->PkgADP->Data[I];
	PkgP->Type |= SC_GC_MARK;
	if (PkgP->GlobP)
	    SC_GCAuxMarkVArr(PkgP->GlobP->GArr, PkgP->GlobCount);
	if (PkgP->NDictP)
	    SC_GCMarkVar((SC_VarVPointer)PkgP->NDictP);
    }

    // OldKonstADP does not need marking... it is just numbers.
    if (VMP->OldStrADP) SC_GCAMSAuxSADP(VMP->OldStrADP, VMP->OldStrCount);
    if (VMP->OldGlobP) SC_GCAuxMarkVArr(VMP->OldGlobP->GArr, VMP->OldGlobP->Count);
    if (VMP->OldGNameADP) SC_GCAMSAuxSADP(VMP->OldGNameADP, VMP->OldGNameADP->SlotCount);
    if (VMP->OldDefDictP)
	SC_GCMarkVar((SC_VarVPointer)VMP->OldDefDictP);

    // Mark the current stack
    if (VMP->StackTop)
	SC_GCAuxMarkStack(VMP->StackP, VMP->StackTop);

    // Call Clients to mark their Ext/ArrExt/ChunkXT if any!
    if (MarkFP) MarkFP(VMP, NULL, 1);
}


#if SC_DEBUG_DISP_GC

    #define	SC_GCAP_LEN	20
    void	SC_GCAnnounce_Plus(char * S, void * P, SC_SlabPointer FirstSP, Uns8 IsSDP)
    {
	SC_SlabPointer	TheSP = SC_SlabFindSlab(FirstSP, P);

	if (IsSDP) {
	    if ((*(SC_StrDataPointer)P).StrLen < SC_GCAP_LEN)
		printf("         + %s Data at %06ld  \"%s\"\n", S, (char *)P - TheSP->Data, (*(SC_StrDataPointer)P).Data);
	    else
		printf("         + %s Data at %06ld  \"%.*s...\"\n", S, (char *)P - TheSP->Data, SC_GCAP_LEN - 3, (*(SC_StrDataPointer)P).Data);

	} else
	    printf("         + %s Data at %06ld\n", S, (char *)P - TheSP->Data);
    }
    #undef	SC_GCAP_LEN
    
    #define	SC_GCANNOUNCE(_S, _RC, _RP, _SP)	printf("GC Zap --> %s Entry (Ref:%d) at %06ld\n", _S, _RC, _RP - _SP->Data)
    #define	SC_GCANNOUNCE_NR(_S, _RP, _SP)		printf("GC Zap --> %s Entry (Ref:N) at %06ld\n", _S,_RP - _SP->Data)
    #define	SC_GCANNOUNCE_S(_S, _RC, _RS, _RP, _SP) printf("GC Zap --> %s Entry (Ref:%d/%d) at %06ld\n", _S, _RC, _RS, _RP - _SP->Data)
    #define	SC_GCANNOUNCE_PLUS(_S, _P, _SP)		SC_GCAnnounce_Plus(_S, _P, _SP, 0)
    #define	SC_GCANNOUNCE_SDP(_S, _P, _SP)		SC_GCAnnounce_Plus(_S, _P, _SP, 1)
#else
    #define	SC_GCANNOUNCE(_T, _RC, _RP, _SP)
    #define	SC_GCANNOUNCE_NR(_S, _RP, _SP)
    #define	SC_GCANNOUNCE_S(_S, _RC, _RS, _RP, _SP)
    #define	SC_GCANNOUNCE_SDP(_S, _P, _SP)
#endif


    void SC_GCAuxSweepVEntSlab(SC_VMPointer VMP, SC_SlabPointer SP)
    {
	char			*ReadP, *EndP;
	SC_VarVPointer		VarP;
	SC_ArrDataPointer	ADP;
	SC_EXTGCFuncPointer	ZapFP;
	Uns8			Mark;

	static void *		ProcArr[] = {&&DoGen, &&DoVar, &&DoPair, &&DoArr, &&DoStr, &&DoDict, &&DoEXT, &&DoArrXT};

	ReadP = SP->Data;
	EndP = ReadP + SP->DataSize;
	while (ReadP < EndP) {

	    // Handle free slot!
	    if ((*(SC_SlabFreePointer)ReadP).Zero == 0LL) {
		ReadP += (*(SC_SlabFreePointer)ReadP).Size;
		continue;
	    }

	    VarP = (SC_VarVPointer)ReadP;

	    if ((Mark = (VarP->Type & SC_GC_MARKMASK))) {	// Get the GC_MARK!!
		VarP->Type &= SC_GC_TYPEMASK;			// Clear it
		ReadP += sizeof(SC_VarVRecord);
		continue;
		
	    } else if (VarP->State == SC_GCDONT) {		// Do NOTHING
		ReadP += sizeof(SC_VarVRecord);
		continue;
	    }

	    // NOT MARKED if it gets here... so Zap!
	    SC_SAFE_CHECK(VarP->Type <= SC_ENTRY_ARREXT, SC_BADRUN_Type);
	    goto *ProcArr[VarP->Type];

	    // Will *NOT* see these on the VEnt slab!!
	    DoGen:;
	    DoPair:;
	    DoDict:;
	    DoStr:	 goto Error;

	    DoVar:
		SC_GCANNOUNCE(SC_EntryNameArr[SC_ENTRY_VAR], VarP->RefCount, ReadP, SP);
		SC_SlabZapItemOnSlab(SP, VarP, sizeof(SC_VarVRecord));  // Zap the Var
		VMP->GCSweepTot += sizeof(SC_VarVRecord);
		ReadP += sizeof(SC_VarVRecord);
		continue;

	    DoEXT:
		ZapFP = SC_EXTGCArr[(*(SC_ChunkXTVPointer)VarP).ExtType];
		if ((ZapFP == NULL) || ZapFP(VMP, VarP, 0)) {
		    // No ZapFP or Client says YES!
		    SC_GCANNOUNCE(SC_EntryNameArr[SC_ENTRY_EXT], VarP->RefCount, ReadP, SP);
		    SC_SlabZapItemOnSlab(SP, VarP, sizeof(SC_VarVRecord));  // Zap the Var
		    VMP->GCSweepTot += sizeof(SC_VarVRecord);		    // Excludes freed in client mem!
		}
		ReadP += sizeof(SC_VarVRecord);
		continue;

	    DoArr:
		ADP = (*(SC_ArrVPointer)VarP).ArrDataP;
		SC_GCANNOUNCE(SC_EntryNameArr[SC_ENTRY_ARR], VarP->RefCount, ReadP, SP);
		SC_GCANNOUNCE_PLUS(SC_EntryNameArr[SC_ENTRY_ARR], ADP, VMP->ArrDataSP);

		VMP->GCSweepTot += sizeof(SC_VarVRecord) + ADP->BlockLen;	// Record BEFORE zapping
		SC_SlabZapItem(VMP->ArrDataSP, ADP, ADP->BlockLen);		// Zap its array
		SC_SlabZapItemOnSlab(SP, VarP, sizeof(SC_VarVRecord));		// Zap the VEntry itself
		ReadP += sizeof(SC_VarVRecord);
		continue;

	    DoArrXT:
		ZapFP = SC_EXTGCArr[(*(SC_ChunkXTVPointer)VarP).ExtType];
		if ((ZapFP == NULL) || ZapFP(VMP, VarP, 0)) {
		    // No ZapFP or Client says YES!
		    ADP = (*(SC_ArrVPointer)VarP).ArrDataP;
		    SC_GCANNOUNCE(SC_EntryNameArr[SC_ENTRY_ARREXT], VarP->RefCount, ReadP, SP);
		    SC_GCANNOUNCE_PLUS(SC_EntryNameArr[SC_ENTRY_ARR], ADP, VMP->ArrDataSP);
		
		    VMP->GCSweepTot += sizeof(SC_VarVRecord) + ADP->BlockLen;	// Record BEFORE zapping
		    SC_SlabZapItem(VMP->ArrDataSP, ADP, ADP->BlockLen);		// Zap its array
		    SC_SlabZapItemOnSlab(SP, VarP, sizeof(SC_VarVRecord));	// Zap the VEntry itself
		}
		ReadP += sizeof(SC_VarVRecord);
		continue;
	}
	return;

	Error:  SC_SysError(SC_STR_ErrorBadGCBlock);
    }

    void SC_GCAuxSweepBEntSlab(SC_VMPointer VMP, SC_SlabPointer SP)
    {
	char			*ReadP, *EndP;
	Uns8			Mark;
	SC_VarVPointer		VarP;

	static void *		ProcArr[] = {&&DoGen, &&DoVar, &&DoPair, &&DoArr,
					     &&DoStr, &&DoDict, &&DoEXT, &&DoArrXT,
					     &&DoCase, &&DoRC, &&DoPat, &&DoPkg};

	ReadP = SP->Data;
	EndP = ReadP + SP->DataSize;
	while (ReadP < EndP) {

	    // Handle free slot!
	    if ((*(SC_SlabFreePointer)ReadP).Zero == 0LL) {
		ReadP += (*(SC_SlabFreePointer)ReadP).Size;
		continue;
	    }

	    VarP = (SC_VarVPointer)ReadP;

	    if ((Mark = (VarP->Type & SC_GC_MARKMASK))) {
		VarP->Type &= SC_GC_TYPEMASK;

		// If Pair and NOT InDict, remove the NextPairP linkage because its Dict
		// *MAY* be removed in this sweep, along with the other Pairs!!
		if ((VarP->Type == SC_ENTRY_PAIR) && !(Mark & SC_GC_INDICT))
		    (*(SC_PairHPointer)VarP).NextPairP = NULL;

		ReadP += sizeof(SC_PairHRecord);
		continue;
	    } 

	    // Not MARKED if it gets here...  so zap!
	    SC_SAFE_CHECK((VarP->Type & SC_GC_TYPEMASK) <= SC_ENTRY_PKG, SC_BADRUN_Type);
	    goto *ProcArr[VarP->Type];

	    // Will *NOT* see these on the BEnt slab!
	    DoGen:;
	    DoVar:;
	    DoArr:;
	    DoEXT:;
	    DoArrXT:	goto Error;

	    DoPair:
		SC_GCANNOUNCE(SC_EntryNameArr[SC_ENTRY_PAIR], VarP->RefCount, ReadP, SP);
		SC_SlabZapItemOnSlab(SP, VarP, sizeof(SC_PairHRecord));		// Zap the Pair itself
		VMP->GCSweepTot += sizeof(SC_PairHRecord);
		ReadP += sizeof(SC_PairHRecord);
		continue;
	    
	    DoCase:
		SC_GCANNOUNCE(SC_EntryNameArr[SC_ENTRY_CASE], VarP->RefCount, ReadP, SP);
		SC_SlabZapItemOnSlab(SP, VarP, sizeof(SC_PairHRecord));		// Zap the Case itself
		VMP->GCSweepTot += sizeof(SC_PairHRecord);
		ReadP += sizeof(SC_PairHRecord);
		continue;

	    DoDict: {
		SC_ArrDataPointer ADP = (*(SC_DictVPointer)ReadP).HADP;
		
		SC_GCANNOUNCE(SC_EntryNameArr[SC_ENTRY_DICT], VarP->RefCount, ReadP, SP);
		SC_GCANNOUNCE_PLUS(SC_EntryNameArr[SC_ENTRY_DICT], ADP, VMP->ArrDataSP);

		VMP->GCSweepTot += sizeof(SC_PairHRecord) + ADP->BlockLen;	// Record BEFORE zapping
		SC_SlabZapItem(VMP->ArrDataSP, ADP, ADP->BlockLen);		// Zap the array
		SC_SlabZapItemOnSlab(SP, VarP, sizeof(SC_DictVRecord));		// Zap the Dict itself
		ReadP += sizeof(SC_DictVRecord);
	    } continue;	    

	    DoStr: {
		SC_StrHPointer	SHP = (SC_StrHPointer)ReadP;
		
	        SC_GCANNOUNCE_S(SC_EntryNameArr[SC_ENTRY_STR], SHP->RefCount, SHP->State, ReadP, SP);
	        SC_GCANNOUNCE_SDP(SC_EntryNameArr[SC_ENTRY_STR], SHP->StrDataP, VMP->StrDataSP);
		
		// SC_StrPurge *WILL* add to VMP->GCSweepTot!!
		// Will clean up Str hash structs too.
	        SC_StrPurge(VMP, SHP, SP);
		ReadP += sizeof(SC_PairHRecord);
	    } continue;

	    DoRC: {
		SC_RCPointer	RCP = (SC_RCPointer)ReadP;

		// No need to zap RunP+ResumeP here... Sweep on CHUNK slab will zap them.
		SC_GCANNOUNCE(SC_EntryNameArr[SC_ENTRY_RC], RCP->RefCount, ReadP, SP);
		if (RCP->StackCopyP) {
		    SC_GCANNOUNCE_PLUS(SC_EntryNameArr[SC_ENTRY_ARR], RCP->StackCopyP, VMP->ArrDataSP);
		    VMP->GCSweepTot += RCP->StackCopyP->BlockLen;
		    SC_SlabZapItem(VMP->ArrDataSP, RCP->StackCopyP, RCP->StackCopyP->BlockLen);
		}
		SC_SlabZapItemOnSlab(SP, RCP, sizeof(SC_PairHRecord));
		VMP->GCSweepTot += sizeof(SC_PairHRecord);
		ReadP += sizeof(SC_PairHRecord);
	    } continue;

	    DoPat: {
		SC_PatPointer	PatP = (SC_PatPointer)ReadP;

		SC_GCANNOUNCE(SC_EntryNameArr[SC_ENTRY_PAT], PatP->RefCount, ReadP, SP);
		SC_SlabZapItemOnSlab(SP, PatP, sizeof(SC_PairHRecord));
		VMP->GCSweepTot += sizeof(SC_PairHRecord);
		ReadP += sizeof(SC_PairHRecord);
	    } continue;

	    DoPkg: {
		SC_PkgPointer	PkgP = (SC_PkgPointer)ReadP;

		SC_GCANNOUNCE(SC_EntryNameArr[SC_ENTRY_PKG], PkgP->RefCount, ReadP, SP);
		// Its NDictP will be zapped on VEnt slab... but its Glob MUST be zapped!
		// Vars on Glob will also get zapped on VEnt slab.
		if (PkgP->GlobP) {
		    SC_GCANNOUNCE_PLUS(SC_EntryNameArr[SC_CHUNK_GLOB], PkgP->GlobP, VMP->ChunkSP);
		    SC_SlabZapItem(VMP->ChunkSP,  PkgP->GlobP,  PkgP->GlobP->BlockLen);
		    VMP->GCSweepTot += PkgP->GlobP->BlockLen;
		}
		SC_SlabZapItemOnSlab(SP, PkgP, sizeof(SC_PairHRecord));
		VMP->GCSweepTot += sizeof(SC_PairHRecord);		
		ReadP += sizeof(SC_PairHRecord);
	    } continue;
	}
	
	return;

	Error:  SC_SysError(SC_STR_ErrorBadGCBlock);
    }

    void SC_GCAuxSweepChunkSlab(SC_VMPointer VMP, SC_SlabPointer SP)
    {
	char			*ReadP, *EndP;
	SC_VarVPointer		VarP;
	Uns32			Len;
	SC_EXTGCFuncPointer	ZapFP;
	Uns8			VType, Mark;
	
	static void *		ProcArr[] = {&&DoChExt, &&DoClos, &&DoCode, &&DoRun, &&DoScript, &&DoGlob, &&DoStak};

	ReadP = SP->Data;
	EndP = ReadP + SP->DataSize;
	while (ReadP < EndP) {

	    // Handle free slot!
	    if ((*(SC_SlabFreePointer)ReadP).Zero == 0LL) {
		ReadP += (*(SC_SlabFreePointer)ReadP).Size;
		continue;
	    }

	    VarP = (SC_VarVPointer)ReadP;
	    VType = VarP->Type & SC_GC_TYPEMASK;
	    Mark = VarP->Type & SC_GC_MARK;
	    if ((VType < SC_CHUNK_EXT) || (SC_CHUNK_STAK < VType))
		goto Error;
	    goto *ProcArr[VType - SC_CHUNK_EXT];

	    DoChExt:{
		    SC_ChunkXTVPointer	ChunkXTP = (SC_ChunkXTVPointer)ReadP;

		    Len = ChunkXTP->BlockLen;
		    ChunkXTP->Type = VType;

		    if (!Mark && (ChunkXTP->State != SC_GCDONT)) {
		    	ZapFP = SC_EXTGCArr[(*(SC_ChunkXTVPointer)VarP).ExtType];
			if ((ZapFP == NULL) || ZapFP(VMP, VarP, 0)) {
			    // No ZapFP or Client says YES!
			    SC_GCANNOUNCE(SC_EntryNameArr[SC_CHUNK_EXT], ChunkXTP->RefCount, ReadP, SP);
			    VMP->GCSweepTot += Len;			    
			    SC_SlabZapItemOnSlab(SP, ChunkXTP, Len);	// Zap it
			}
		    }
		}
		ReadP += Len;
		continue;
	
	    DoClos: {
		    SC_ClosPointer	ClosP = (SC_ClosPointer)ReadP;

		    Len = ClosP->BlockLen;
		    ClosP->Type = VType;
		    if (! Mark) {
			SC_GCANNOUNCE(SC_EntryNameArr[SC_CHUNK_CLOS], ClosP->RefCount, ReadP, SP);
			VMP->GCSweepTot += Len;			    
			SC_SlabZapItemOnSlab(SP, ClosP, Len);	// Zap it
		    }
		}
		ReadP += Len;
		continue;

	    DoCode: {
		    SC_CodeBlockPointer	CBP = (SC_CodeBlockPointer)ReadP;

		    Len = CBP->BlockLen;
		    CBP->Type = VType;
		    if (! Mark) {
			SC_GCANNOUNCE(SC_EntryNameArr[SC_CHUNK_CODE], CBP->RefCount, ReadP, SP);
			VMP->GCSweepTot += Len;			    
			SC_SlabZapItemOnSlab(SP, CBP, Len);
		    }
		}
		ReadP += Len;
		continue;

	    DoRun: {
		    SC_RunPointer	RunP = (SC_RunPointer)ReadP;

		    Len = RunP->BlockLen;
		    RunP->Type = VType;
		    if (! Mark) {
			// RunP do NOT have RefCount!
			SC_GCANNOUNCE_NR(SC_EntryNameArr[SC_CHUNK_RUN], ReadP, SP);		    
			VMP->GCSweepTot += Len;			    
			SC_SlabZapItemOnSlab(SP, RunP, Len);
		    }
		}
		ReadP += Len;
		continue;

	    DoScript: {
		    SC_ScriptPointer	ScriptP = (SC_ScriptPointer)ReadP;
		    SC_ArrDataPointer	ADP;
		    SC_GlobPointer	GlobP;

		    Len = sizeof(SC_ScriptRecord);
		    ScriptP->Type = VType;
		    if (! Mark) {
			SC_GCANNOUNCE(SC_EntryNameArr[SC_CHUNK_SCRPT], ScriptP->RefCount, ReadP, SP);		    
			// Get rid of parts EXCLUSIVE to this SC_ScriptRecord
			// Get rid of KonstADP + StringADP + GNameADP
			if ((ADP = ScriptP->KonstADP)) {	// Assign
			    SC_GCANNOUNCE_PLUS(SC_EntryNameArr[SC_ENTRY_ARR], ADP, VMP->ArrDataSP);
			    VMP->GCSweepTot += ADP->BlockLen;
			    SC_SlabZapItem(VMP->ArrDataSP, ADP, ADP->BlockLen);
			}
			if ((ADP = ScriptP->StringADP)) {	// Assign
			    SC_GCANNOUNCE_PLUS(SC_EntryNameArr[SC_ENTRY_ARR], ADP, VMP->ArrDataSP);
			    VMP->GCSweepTot += ADP->BlockLen;
			    SC_SlabZapItem(VMP->ArrDataSP, ADP, ADP->BlockLen);
			}
			if ((ADP = ScriptP->GNameADP)) {	// Assign
			    SC_GCANNOUNCE_PLUS(SC_EntryNameArr[SC_ENTRY_ARR], ADP, VMP->ArrDataSP);
			    VMP->GCSweepTot += ADP->BlockLen;
			    SC_SlabZapItem(VMP->ArrDataSP, ADP, ADP->BlockLen);
			}
			// Get rid of its GlobP
			if ((GlobP = ScriptP->GlobP)) {	// Assign
			    SC_GCANNOUNCE_PLUS(SC_EntryNameArr[SC_CHUNK_GLOB], ADP, VMP->ChunkSP);
			    VMP->GCSweepTot += ADP->BlockLen;
			    SC_SlabZapItem(VMP->ChunkSP, GlobP, GlobP->BlockLen);
			}
			// TopRunP and CurRunP should have been zapped when
			// this Script stopped running.

			VMP->GCSweepTot += Len;			    
			SC_SlabZapItemOnSlab(SP, ScriptP, Len);
		    }
		}
		ReadP += Len;
		continue;

	    DoGlob:
		// NOT subject to independent GC
		ReadP += (*(SC_GlobPointer)ReadP).BlockLen;
		continue;
		
	    DoStak:
		// NOT subject to GC
		ReadP += (*(SC_StackBlockPointer)ReadP).BlockLen;
		continue;
	}
	return;

	Error:  SC_SysError(SC_STR_ErrorBadGCBlock);
    }


// Slabs are:
//	VarEntrySP	VarV, ArrV, ExtV, 
//	BigEntrySP	StrH, DictV, PairH, CaseH, Pat, Pkg, RC
//	StrDataSP	StrData (No RefCount)
//	ArrDataSP	ArrData (No RefCount)
//	ChunkSP		Script, Code, Clos, Glob, Run, Stak
//	+
//	TokenSP		Compile tokens
//	LexSP		Compile Lex
//	MapSP		Compile Maps
//	VexSP		Compile Vex
//
// NOTE: SC_GCAll *SHOULD* clean memory *EVEN IF* RefCounting is turned off!!

#if SC_GC_MARKSWEEP
    #define SC_GC_THRESHOLD(_VMP)		{if (_VMP->GCSize > _VMP->GCStartVal) SC_GCAll(_VMP);}
    #define SC_GC_STEP(_VMP)			{if ((_VMP->GCSize > _VMP->GCStartVal) && (_VMP->GCStep > _VMP->GCStepVal)) SC_GCAll(_VMP);}
#else
    #define SC_GC_THRESHOLD(_VMP)
    #define SC_GC_STEP(_VMP)
#endif

// Can call BEFORE/AFTER compile, but NOT **DURING** compile!
// (Otherwise, must mark Strings/Defs in Top Lex-MapP... and NOT kill Token/Lex/Map/Vex slabs!)

void	SC_GCAll(SC_VMPointer VMP)
{
    SC_SlabPointer	SP;
    Uns64		Len, Tot;
    char *		Msg = "   Zapped %ld bytes %s slabs";

    #if SC_DEBUG_DISP_GC
	printf("\n--Mark/Sweep GC----------------BEGIN--\n");
    #endif
    
    // First Mark everything
    SC_GCMark(VMP);
    VMP->GCSweepTot = 0LL;

    // Sweep of VarEntry slabs will get rid of Arr data.
    SP = VMP->VarEntrySP;
    while (SP) {
	SC_GCAuxSweepVEntSlab(VMP, SP);
	SP = SP->NextSP;
    }

    // Sweep of BigEntry slabs will get rid of StrH, Pat, Pkg, Dict, and RC records.
    // (When a Dict is zapped, it will get rid of its Pairs/Cases as well, except for
    // "escaped" Pairs!)
    
    SP = VMP->BigEntrySP;
    while (SP) {
	SC_GCAuxSweepBEntSlab(VMP, SP);
	SP = SP->NextSP;
    }

    // Sweep of Chunk slabs will get rid of Clos, Code, Run, Glob, and Scrpt records.
    // Stack block and Glob (block) will be untouched!
    SP = VMP->ChunkSP;
    while (SP) {
	SC_GCAuxSweepChunkSlab(VMP, SP);
	SP = SP->NextSP;
    }

    Tot = 0LL;
    // Kill any runtime slabs that became empty, except for the INITIAL/ORIGINAL ones!
    if ((Len = SC_SlabKillEmpty(&VMP->VarEntrySP))) printf(Msg, Len, SC_SlabNameArr[SC_SLAB_VENT]), Tot += Len;
    if ((Len = SC_SlabKillEmpty(&VMP->BigEntrySP))) printf(Msg, Len, SC_SlabNameArr[SC_SLAB_BENT]), Tot += Len;
    if ((Len = SC_SlabKillEmpty(&VMP->StrDataSP))) printf(Msg, Len, SC_SlabNameArr[SC_SLAB_SDAT]), Tot += Len;
    if ((Len = SC_SlabKillEmpty(&VMP->ArrDataSP))) printf(Msg, Len, SC_SlabNameArr[SC_SLAB_ADAT]), Tot += Len;
    if ((Len = SC_SlabKillEmpty(&VMP->ChunkSP))) printf(Msg, Len, SC_SlabNameArr[SC_SLAB_CHNK]), Tot += Len;

    // Reduce compile phase slabs to minimum (just INITIAL/ORIGINAL ones)!
    if ((Len = SC_SlabKillAll(&VMP->TokenSP, 1))) printf(Msg, Len, SC_SlabNameArr[SC_SLAB_TOK]), Tot += Len;
    if ((Len = SC_SlabKillAll(&VMP->LexSP, 1))) printf(Msg, Len, SC_SlabNameArr[SC_SLAB_LEX]), Tot += Len;
    if ((Len = SC_SlabKillAll(&VMP->MapSP, 1))) printf(Msg, Len, SC_SlabNameArr[SC_SLAB_MAP]), Tot += Len;
    if ((Len = SC_SlabKillAll(&VMP->VexSP, 1))) printf(Msg, Len, SC_SlabNameArr[SC_SLAB_VEX]), Tot += Len;

    VMP->GCSize -= Tot;
    VMP->GCStep = 0LL;

    #if SC_DEBUG_DISP_GC
	printf("--GC-----------------------------END--\n\n");
    #endif
}


// ********************************************************************************
// ENGINE

    Uns32	SC_AuxVMInit(SC_VMPointer VMP, SC_SlabPointer *SPP, Uns32 InitCount, Uns32 Size, Uns8 SlabType, Uns32 Flag)
    {
	Uns32	MaxCount;

	InitCount = InitCount * SC_InitFactor;
	MaxCount = SC_MAXSLABINITDATASIZE / Size;
	if (InitCount > MaxCount) InitCount = MaxCount;
	VMP->GCSize += SC_SlabCreate(SPP, InitCount * Size, SlabType, Flag);
	return InitCount;
    }

void		SC_VMInitAllSlabs(SC_VMPointer VMP)
{
    VMP->VarEntrySlotAlloc   = SC_AuxVMInit(VMP, &VMP->VarEntrySP, SC_VARENTRYINITSLOTCOUNT, sizeof(SC_VarVRecord), SC_SLAB_VENT, SC_SLAB_NO_FLAG);
    VMP->BigEntrySlotAlloc   = SC_AuxVMInit(VMP, &VMP->BigEntrySP, SC_BIGENTRYINITSLOTCOUNT, sizeof(SC_PairHRecord), SC_SLAB_BENT, SC_SLAB_NO_FLAG);
    VMP->StrDataByteAlloc    = SC_AuxVMInit(VMP, &VMP->StrDataSP, SC_STRDATAINITSLABSIZE, 1, SC_SLAB_SDAT, SC_SLAB_STR_FLAG | SC_SLAB_ARR_FLAG);
    VMP->ArrDataSlotAlloc    = SC_AuxVMInit(VMP, &VMP->ArrDataSP, SC_ARRDATAINITSLOTCOUNT, sizeof(Uns64), SC_SLAB_ADAT, SC_SLAB_ARR_FLAG);
    VMP->ChunkByteAlloc	     = SC_AuxVMInit(VMP, &VMP->ChunkSP, SC_CHUNKINITSLABSIZE, 1, SC_SLAB_CHNK, SC_SLAB_CHNK_FLAG);
    VMP->TokenSlotAlloc	     = SC_AuxVMInit(VMP, &VMP->TokenSP, SC_TOKENINITSLOTCOUNT, sizeof(SC_TokenRecord), SC_SLAB_TOK, SC_SLAB_NO_FLAG);
    VMP->LexSlotAlloc	     = SC_AuxVMInit(VMP, &VMP->LexSP, SC_LEXINITSLOTCOUNT, sizeof(SC_LexRecord), SC_SLAB_LEX, SC_SLAB_NO_FLAG);
    VMP->MapSlotAlloc	     = SC_AuxVMInit(VMP, &VMP->MapSP, SC_MAPINITSLOTCOUNT, sizeof(SC_MapRecord), SC_SLAB_MAP, SC_SLAB_NO_FLAG);
    VMP->VexSlotAlloc	     = SC_AuxVMInit(VMP, &VMP->VexSP, SC_VEXINITSLOTCOUNT, sizeof(SC_VexRecord), SC_SLAB_VEX, SC_SLAB_NO_FLAG);
}

void		SC_VMKillAllSlabs(SC_VMPointer VMP)
{
    while (VMP->VarEntrySP) SC_SlabKillAll(&VMP->VarEntrySP, 0);	// Kill last too!
    while (VMP->BigEntrySP) SC_SlabKillAll(&VMP->BigEntrySP, 0);
    while (VMP->StrDataSP) SC_SlabKillAll(&VMP->StrDataSP, 0);
    while (VMP->ArrDataSP) SC_SlabKillAll(&VMP->ArrDataSP, 0);
    while (VMP->ChunkSP) SC_SlabKillAll(&VMP->ChunkSP, 0);
    while (VMP->TokenSP) SC_SlabKillAll(&VMP->TokenSP, 0);
    while (VMP->LexSP) SC_SlabKillAll(&VMP->LexSP, 0);
    while (VMP->MapSP) SC_SlabKillAll(&VMP->MapSP, 0);
    while (VMP->VexSP) SC_SlabKillAll(&VMP->VexSP, 0);
}

void *		SC_VMGet(void)
{
    return &SC_VM;
}

void		SC_VMInit(void* SMP, SC_PrintFuncP OutPFP, SC_PrintFuncP CurFP)
{
    SC_VMPointer	VMP = (SC_VMPointer)SMP;	// SC knows the internals
    Int16		I;

    SC_InitFactor = 1;
    VMP->GCSize = VMP->GCStep = 0LL;
    VMP->GCStartVal = SC_GC_STARTVAL;
    VMP->GCStepVal = SC_GC_STEPVAL;
    VMP->GCSweepTot = 0LL;
    VMP->StrHashTP = NULL;
    VMP->VarEntrySP = VMP->BigEntrySP = VMP->StrDataSP = VMP->ArrDataSP = VMP->ChunkSP = NULL;
    VMP->VarEntrySlotAlloc = VMP->BigEntrySlotAlloc = 0LL;
    VMP->StrDataByteAlloc = VMP->ArrDataSlotAlloc = VMP->ChunkByteAlloc = 0LL;

    VMP->TokenSP = VMP->LexSP = VMP->MapSP = VMP->VexSP = NULL;
    VMP->TokenSlotAlloc = VMP->LexSlotAlloc = VMP->MapSlotAlloc = VMP->VexSlotAlloc = 0LL;

    VMP->OutPrintFP = OutPFP;
    VMP->CurPrintFP = CurFP;

    VMP->PkgCount = VMP->CFPCount = 0;
    VMP->StackTop = VMP->StackCount = 0L;
    VMP->LastCodeId = VMP->LastScriptId = 0L;

    VMP->OldKonstCount = VMP->OldStrCount = 0L;
    VMP->OldKonstADP = VMP->OldStrADP = VMP->OldGNameADP = NULL;
    VMP->OldGlobP = NULL;
    VMP->OldDefDictP = NULL;

    VMP->RunScriptP = VMP->CompileScriptP = NULL;
    VMP->StackBlockP = NULL;
    VMP->PkgADP = VMP->CFPADP = NULL;

    VMP->StackP = NULL;

    SC_ReadKeyCodeMapInit();
    SC_StrHashInitTable(VMP);
    SC_VMInitAllSlabs(VMP);

    SC_CGInitSysPackages(VMP);
    for (I = 0; I < SC_EXT_COUNT; I++)
	SC_EXTGCArr[I] = NULL;

    #if 0
	SC_GCInitSmartRefCount();
    #endif
}

/* **********************************************
Debug version:
-------------------------------------------------
scScript Version: 1.1  [Thu Nov  2 14:20:00 2023]
        RefCount: On                   Reg: 7
	   Debug: On	      Show Compile: On
          M/S GC: On            Safe Check: On
  Free Mem Check: On        All Slab Check: On
     GDB Display: On	    Output Display: On
           Speed: Slow
-------------------------------------------------

No-Debug version:
-------------------------------------------------
scScript Version: 2.1  [Thu Mar  6 12:33:30 2025]
     GC RefCount: On	     GC Mark/Sweep: On
-------------------------------------------------
************************************************* */

// SC_VMAnnounce just writes a little identifying header to the output.

char *		SC_VMA_ON =	"On  ";
char *		SC_VMA_OFF =	"Off ";
char *		SC_VMA_FAST =	"Fast";
char *		SC_VMA_MED =    "Med ";
char *		SC_VMA_SLOW =	"Slow";

void		SC_VMAnnounce(void* SMP)
{
    SC_PrintFuncP	PFP = (*(SC_VMPointer)SMP).OutPrintFP;
    time_t		TVal;
    char		Str[32];
    #ifdef DEBUG
	char *		P;
	Int16		Speed;
    #endif

    time(&TVal);
    ctime_r(&TVal, Str);
    if (*Str) Str[strlen(Str) - 1] = 0;		// Zap ending \n
    
    PFP("scScript Version: %d.%d  [%s]\n", SC_VERSIONMAJOR, SC_VERSIONMINOR, Str);

    #ifdef DEBUG
	PFP("        RefCount: %s                 Reg: %d\n", (SC_GC_REFCOUNT) ? SC_VMA_ON : SC_VMA_OFF, SC_CGMAXREGCOUNT);
	PFP("           Debug: %s     Compile Display: %s\n", SC_VMA_ON,
							      (SC_DEBUG_ShowParseToken || SC_DEBUG_ShowAST) ? SC_VMA_ON : SC_VMA_OFF);
	PFP("          M/S GC: %s          Safe Check: %s\n", (SC_GC_MARKSWEEP) ? SC_VMA_ON : SC_VMA_OFF,
							      (SC_SAFE) ? SC_VMA_ON : SC_VMA_OFF);
	PFP("  Free Mem Check: %s      All Slab Check: %s\n", (SC_DEBUG_CHECK_SLABFREE) ? SC_VMA_ON : SC_VMA_OFF,
							      (SC_DEBUG_CHECK_ALLSLAB) ? SC_VMA_ON : SC_VMA_OFF);
	PFP("       GDB Print: %s     GDB Debug Print: %s\n", (SC_DEBUG_DISP_EXEC_OP || SC_DEBUG_DISP_ALLOC || SC_DEBUG_DISP_GC ||
							       SC_DEBUG_DISP_MATCH || SC_DEBUG_DISP_MSTACK || SC_DEBUG_DISP_RSTACK)
								  ? SC_VMA_ON : SC_VMA_OFF,
							      (SC_DEBUG_PRINT_DICT || SC_DEBUG_PRINT_LOCALS || SC_DEBUG_PRINT_GLOBALS)
								  ? SC_VMA_ON : SC_VMA_OFF);

	// Assuming these are 0 or 1 only
	Speed = 10 - (SC_GC_REFCOUNT + SC_SAFE + SC_DEBUG_DISP_GC + SC_DEBUG_CHECK_SLABFREE +		// -1 for each
		      SC_DEBUG_DISP_ALLOC +								// -1 also
		      (SC_DEBUG_DISP_EXEC_OP * 2) +							// -2 for this
		      ((SC_DEBUG_PRINT_DICT || SC_DEBUG_PRINT_LOCALS || SC_DEBUG_PRINT_GLOBALS) * 3) +	// -3 if ANY of these
		      (SC_DEBUG_CHECK_SLABFREE && SC_DEBUG_CHECK_ALLSLAB));				// -1 for CheckAllSlab ONLY IF CheckSlabFree

	// -3 for any compiler display.... really slows down
	Speed -= 3 * (SC_DEBUG_ShowParseToken || SC_DEBUG_ShowAST || SC_DEBUG_ShowCode || SC_DEBUG_ShowLabel || SC_DEBUG_ShowFunc);

	if (Speed >= 8) P = SC_VMA_FAST;
	else if (Speed > 5) P = SC_VMA_MED;
	else P = SC_VMA_SLOW;

	PFP("           Speed: %s\n", P);
    #else
	PFP("     GC RefCount: %s       GC Mark/Sweep: %s\n", (SC_GC_REFCOUNT) ? SC_VMA_ON : SC_VMA_OFF,
							      (SC_GC_MARKSWEEP) ? SC_VMA_ON : SC_VMA_OFF);	
    #endif
    
    PFP("-------------------------------------------------\n\n");
}

void		SC_VMKill(void* SMP)
{
    SC_StrHashKillTable((SC_VMPointer)SMP);
    SC_VMKillAllSlabs((SC_VMPointer)SMP);
}

void	SC_RunDisplayPkg(SC_VMPointer VMP, Uns8 FromI, Uns8 ToI, Uns8 Alone)
{
    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;
    SC_PkgPointer	PkgP;
    Uns8		I;

    if (Alone) {
	OutPFP("\n**************************************\n");
	OutPFP("*PKG**********************************\n");
	OutPFP("**************************************\n");
    }

    I = FromI;
    while (1) {
	PkgP = (SC_PkgPointer)VMP->PkgADP->Data[I++];		// Inc
	SC_RunDisplayValueP(VMP, (SC_VarVPointer)PkgP, SC_SHOW_MAX, 0);
	if (I <= ToI)
	    OutPFP("  -----------------------------------------------\n");
	else {
	    OutPFP("\n");
	    break;
        }
    }
}

void	SC_RunDisplayScript(SC_VMPointer VMP, SC_ScriptPointer ScriptP, Uns8 Level, Uns8 SupLevel, Uns8 ShowSup, Uns8 Alone, Uns8 Lead)
{
    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;
    
    if (Alone) {
	OutPFP("\n**************************************\n");
	OutPFP("*SCRIPT*******************************\n");
	OutPFP("**************************************\n");
    }

    while (ScriptP) {
	SC_RunDisplayValueP(VMP, (SC_VarVPointer)ScriptP, Level, Lead);
	ScriptP = (ShowSup) ? ScriptP->SupScriptP : NULL;
	Level = SupLevel;
	Lead += 4;
        OutPFP("\n");
    }
}

// SC_VMPrintStats (from SC_SysShowVM) shows a GENERAL overview of VM.
// SC_SysShowMem will show CONTENTS of a Slab!
void		SC_VMPrintStats(void* SMP)
{
    SC_VMPointer	VMP = (SC_VMPointer)SMP;
    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;
    SC_ScriptPointer	ScriptP = VMP->RunScriptP;
    
    OutPFP("\nScript VM Stats:\n");
    SC_StrHashPrintStatus(VMP, 0);
    SC_SlabPrintStatus(VMP->VarEntrySP, OutPFP);
    SC_SlabPrintStatus(VMP->BigEntrySP, OutPFP);
    SC_SlabPrintStatus(VMP->StrDataSP, OutPFP);
    SC_SlabPrintStatus(VMP->ArrDataSP, OutPFP);
    SC_SlabPrintStatus(VMP->ChunkSP, OutPFP);
    SC_SlabPrintStatus(VMP->TokenSP, OutPFP);
    SC_SlabPrintStatus(VMP->LexSP, OutPFP);
    SC_SlabPrintStatus(VMP->MapSP, OutPFP);
    SC_SlabPrintStatus(VMP->VexSP, OutPFP);

    OutPFP("\n  Pkgs -> ");
	SC_RunDisplayField(VMP, SC_VAL_PTR, VMP->PkgADP);
    OutPFP("  C Funcs --> ");
	SC_RunDisplayField(VMP, SC_VAL_PTR, VMP->CFPADP);
    OutPFP("  Stack ----> ");
	SC_RunDisplayField(VMP, SC_VAL_PTR, VMP->StackBlockP);

    OutPFP("  -----------------------------------------------\n");
    if (ScriptP) {
	SC_RunDisplayScript(VMP, ScriptP, SC_SHOW_LOW, SC_SHOW_LOW, 1, 0, 2);
	ScriptP = ScriptP->SupScriptP;
    } else
	OutPFP("  No Script\n  -----------------------------------------------\n");
}

// ********************************************************************************
// PKG Functions

typedef void (*SC_PkgInitFuncP)(SC_VMPointer VMP, SC_PkgPointer PkgP);

SC_PkgPointer	SC_CGCreateNewPkg(SC_VMPointer VMP, Uns16 Index)
{
    SC_PkgPointer	PkgP;

    PkgP = (SC_PkgPointer)SC_BigEntryNew(VMP);
    PkgP->Type = SC_ENTRY_PKG;
    PkgP->State = SC_NOSTATE;
    PkgP->Flags = 0;
    PkgP->RefCount = 1;		// Assume 1 Ref!

    PkgP->Index = Index;
    PkgP->GlobCount = 0L;

    PkgP->NDictP = SC_DictCreateNewDict(VMP, SC_CGNDICTINITCOUNT);
    PkgP->GlobP = NULL;		// Initial

    return PkgP;
}

void	SC_CGInitPkg(SC_VMPointer VMP, SC_PkgInitFuncP IFP, char * NameP)
{
    SC_PkgPointer	PkgP;
    SC_StrHPointer	NameSHP;
    Uns32		NewCount;
    
    if (VMP->PkgADP->SlotCount <= VMP->PkgCount) {
	NewCount = VMP->PkgCount + SC_CGPKGCOUNTINCR;
	VMP->PkgADP = SC_ArrResizeData(VMP, VMP->PkgADP, NewCount, 0);
    }

    VMP->PkgADP->Data[VMP->PkgCount] = PkgP = SC_CGCreateNewPkg(VMP, VMP->PkgCount);
    NameSHP = SC_DictStashVal(VMP, (*(SC_PkgPointer)VMP->PkgADP->Data[0]).NDictP, NameP, VMP->PkgCount, SC_VAL_PIDX);
    // Stash the name in the PkgP itself, so it knows what it is called!!
    SC_DictIntStashVal(VMP, PkgP->NDictP, (Int64)-1, (Int64)NameSHP, SC_VAL_PTR);
    SC_VARREF(NameSHP);			// Gets 1 more REF!
    
    VMP->PkgCount += 1;

    IFP(VMP, PkgP);		// Call InitFuncP for Pkg

    // Pkg Globs are initialized here... No one zaps them!!
    if (PkgP->GlobCount)
	PkgP->GlobP = SC_RunAllocGlob(VMP, PkgP->GlobCount, 1);
}

// Pkg names are case INsensitive, so must be defined lowercase here!
//
// NOTE: Idea to name Main Pkg as "script"... so writer can reference Const
//	 as Script.#True etc.  BUT this also means [Script.] prefix should
//	 be checked for ALL globals and funcs too...  And must be wiped when
//	 redefining... More work and not necessary at this point!

void	SC_CGInitSysPackages(SC_VMPointer VMP)
{
    SC_ArrAllocNewData(VMP, &VMP->PkgADP, SC_CGPKGCOUNTINIT, 1);
    SC_ArrAllocNewData(VMP, &VMP->CFPADP, SC_CGCFPCOUNTINIT, 1);

    SC_CGInitPkg(VMP, SC_MainPkgInit,	SC_MainPkgName);	// Default Main (0) Pkg for script
    SC_CGInitPkg(VMP, SC_SysPkgInit,	"sys");			// Sys (System calls)
    SC_CGInitPkg(VMP, SC_ShowPkgInit,	"show");		// Show (Display debugging info)
    SC_CGInitPkg(VMP, SC_StrPkgInit,	"str");			// Str (String calls)
    SC_CGInitPkg(VMP, SC_EdPkgInit,	"ed");			// Ed (Editor calls)
    SC_CGInitPkg(VMP, SC_ScriptPkgInit, "script");		// Script (Script handling)
    SC_CGInitPkg(VMP, SC_PatPkgInit,	"pat");			// Pat (Pattern/Match calls)
    SC_CGInitPkg(VMP, SC_LogPkgInit,	"log");			// Log (log file)
    SC_CGInitPkg(VMP, SC_MathPkgInit,	"math");		// Math
}

// Same FP may be stashed in multiple Pkgs... Currently, no attempt is made to remedy!
//
// NOTE: VM is currently NOT killed... and if so, all the slabs are simply removed.
//	 CFP points to object code in heap, never deleted... so no problems with >1 ptr. 
Uns32	SC_CGStashCFP(SC_VMPointer VMP, SC_SysFuncPointer FP)
{
    Uns16	I = VMP->CFPCount++;

    if (I >= VMP->CFPADP->SlotCount)
	VMP->CFPADP = SC_ArrResizeData(VMP, VMP->CFPADP, VMP->CFPADP->SlotCount + SC_CGCFPCOUNTINCR, 0);

    VMP->CFPADP->Data[I] = FP;
    return I;
}

#define	SC_PKG_STASHCFP(_P, _N, _F)	SC_DictStashVal(VMP, _P->NDictP, _N, SC_CGStashCFP(VMP, _F), SC_VAL_FIDX)
#define SC_PKG_STASHIFID(_P, _I)	SC_DictStashVal(VMP, _P->NDictP, SC_IFID_Name[_I], _I, SC_VAL_IIDX)
#define SC_PKG_STASHCINT(_P, _N, _C)	SC_DictStashVal(VMP, _P->NDictP, _N, _C, SC_VAL_INT)
#define SC_PKG_STASHCFLT(_P, _N, _C)	SC_DictStashVal(VMP, _P->NDictP, _N, *(Int64 *)&_C, SC_VAL_FLT)
#define SC_PKG_STASHSTR(_P, _N, _S)	SC_DictStashStr(VMP, _P->NDictP, _N, _S);

    // Internal (C function) Flags stored in Uns8
    typedef enum {
	SC_STR_CHARFLAG =	0x01,		// BYTE if 0
	SC_STR_LENFLAG =	0x02,		// POS if 0
	SC_STR_ZULFLAG =	0x04,		// LOCAL if 0
    
	SC_ED_CHARFLAG =	0x01,		// BYTE if 0
	SC_ED_LENFLAG =		0x02,		// POS if 0
	SC_ED_SHOWFLAG =	0x04,		// HIDE if 0

	SC_LOG_UNIFLAG =	0x01,		// COMMON if 0
	SC_LOG_NEWFLAG =	0x02,		// USE (existing) if 0
	SC_LOG_KOFLAG	=	0x04,		// OC (OpenClose) if 0
	SC_LOG_NOHFLAG =	0x08,		// HEADER if 0
	SC_LOG_REQFLAG =	0x10,		// REMAKE if 0
	SC_LOG_ZULFLAG	=	0x20,		// LOCAL if 0
	
    } SC_PKG_FLAG;
 
    // Exposed (Script) Konstants stored in Int64
    typedef enum {
	SC_SYS_LOC =		0x00000001,
	SC_SYS_ZUL =		0x00000002,
	SC_SYSMASK_ZULU =	SC_SYS_LOC | SC_SYS_ZUL,

	SC_SYS_SEC =		0x00000000,
	SC_SYS_MIN,
	SC_SYS_HOUR,
	SC_SYS_MDAY,
	SC_SYS_MON,
	SC_SYS_YEAR,
	SC_SYS_WDAY,
	SC_SYS_YDAY,
	SC_SYS_ISDST,
	SC_SYS_BRTIME_MAX = SC_SYS_ISDST,

	// *******************************************************
	SC_STR_BYTE =		0x00000001,
	SC_STR_CHAR =		0x00000002,
	SC_STRMASK_CHAR =	SC_STR_BYTE | SC_STR_CHAR,

	SC_STR_POS =		0x00000004,
	SC_STR_LEN =		0x00000008,
	SC_STRMASK_LEN =	SC_STR_POS | SC_STR_LEN,

	SC_STR_ZUL =		0x00000010,
	SC_STR_LOC =		0x00000020,
	SC_STRMASK_LOC =	SC_STR_ZUL | SC_STR_LOC,

	SC_STR_MASK =		0x0000003F,	// All masks
	// *******************************************************
	SC_ED_BYTE =		0x00000001,
	SC_ED_CHAR =		0x00000002,
	SC_EDMASK_CHAR =	SC_ED_BYTE | SC_ED_CHAR,

	SC_ED_POS =		0x00000004,
	SC_ED_LEN =		0x00000008,
	SC_EDMASK_LEN =		SC_ED_POS | SC_ED_LEN,

	SC_ED_HIDE =		0x00000010,
	SC_ED_SHOW =		0x00000020,
	SC_EDMASK_SHOW =	SC_ED_HIDE | SC_ED_SHOW,

	SC_ED_MASK =		0x0000003F,	// All masks
	// *******************************************************
	SC_LOG_COM =		0x00000001,	// Common (No UniStamp)
	SC_LOG_UNI =		0x00000002,	// Unique
	SC_LOGMASK_UNI =	SC_LOG_COM | SC_LOG_UNI,

	SC_LOG_USE =		0x00000004,	// Use existing
	SC_LOG_NEW =		0x00000008,	// Make anew
	SC_LOGMASK_NEW =	SC_LOG_USE | SC_LOG_NEW,

	SC_LOG_OC =		0x00000010,	// OpenClose
	SC_LOG_KO =		0x00000020,	// KeepOpen
	SC_LOGMASK_KO =		SC_LOG_OC | SC_LOG_KO,

	SC_LOG_HED =		0x00000040,	// Header
	SC_LOG_NOH =		0x00000080,	// NoHeader
	SC_LOGMASK_NOH =	SC_LOG_HED | SC_LOG_NOH,

	SC_LOG_REM =		0x00000100,	// Remake
	SC_LOG_REQ =		0x00000200,	// Require
	SC_LOGMASK_REQ =	SC_LOG_REM | SC_LOG_REQ,

	SC_LOG_LOC =		0x00000400,	// Local time
	SC_LOG_ZUL =		0x00000800,	// Zulu (UTC) time
	SC_LOGMASK_ZUL =	SC_LOG_LOC | SC_LOG_ZUL,

	SC_LOG_MASK =		0x00000FFF,	// All masks
	// *******************************************************
	SC_P_INSEN =		0x00000001,
	SC_P_SEN =		0x00000002,
	SC_PMASK_SEN =		SC_P_INSEN | SC_P_SEN,

	SC_P_POS =		0x00000004,
	SC_P_NEG =		0x00000008,
	SC_PMASK_NEG =		SC_P_POS | SC_P_NEG,

	SC_P_ABS =		0x00000010,
	SC_P_REL =		0x00000020,
	SC_PMASK_REL =		SC_P_ABS | SC_P_REL,

	SC_P_H =		0x00000040,
	SC_P_V =		0x00000080,
	SC_PMASK_V =		SC_P_H | SC_P_V,

	SC_P_STR =		0x00000100,
	SC_P_DYN =		0x00000200,
	SC_PMASK_DYN =		SC_P_STR | SC_P_DYN,

	SC_P_MAX =		0x00000400,
	SC_P_MIN =		0x00000800,
	SC_PMASK_MIN =		SC_P_MAX | SC_P_MIN,

	SC_P_SELF =		0x00001000,
	SC_P_CONS =		0x00002000,
	SC_PMASK_CONS =		SC_P_SELF | SC_P_CONS,

	SC_P_RES =		0x00004000,
	SC_P_NRES =		0x00008000,
	SC_PMASK_NRES =		SC_P_RES | SC_P_NRES,

	SC_P_MASK =		0x0000FFFF,

    } SC_PKG_SCFLAG;
    

    // MAIN Pkg function/vars/defs can be accessed from the script
    // WITHOUT specifying the Pkg!
    void	SC_MainPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP)
    {
	// ***ONLY*** Main (Pkg[0] has INTERNAL FUNCS (IFIDs)!
	SC_PKG_STASHIFID(PkgP, SC_MAPI_IFID);		// MapI
	SC_PKG_STASHIFID(PkgP, SC_MAPK_IFID);		// MapK
	SC_PKG_STASHIFID(PkgP, SC_ARRAY_IFID);		// Array
	SC_PKG_STASHIFID(PkgP, SC_DICT_IFID);		// Dict
	SC_PKG_STASHIFID(PkgP, SC_INT_IFID);		// Int
	SC_PKG_STASHIFID(PkgP, SC_FLT_IFID);		// Flt
	SC_PKG_STASHIFID(PkgP, SC_RCP_IFID);		// Get RCP
	SC_PKG_STASHIFID(PkgP, SC_RESUME_IFID);		// Resume
	SC_PKG_STASHIFID(PkgP, SC_RELEASE_IFID);	// Release
	SC_PKG_STASHIFID(PkgP, SC_SUBMIT_IFID);		// Submit

	SC_PKG_STASHCFP(PkgP, "typeof",		SC_SysGetType);
	SC_PKG_STASHCFP(PkgP, "sizeof",		SC_SysGetSize);
	SC_PKG_STASHCFP(PkgP, "countof",        SC_SysCountElts);
	SC_PKG_STASHCFP(PkgP, "write",		SC_SysWriteOut);	// To output buffer at end
	SC_PKG_STASHCFP(PkgP, "writeln",	SC_SysWriteLineOut);	//   + extra \n at end

	SC_PKG_STASHCINT(PkgP, "#true",		1);
	SC_PKG_STASHCINT(PkgP, "#false",	0);

	SC_PKG_STASHCINT(PkgP, "#type_unknown", SC_SCRIPT_UNKNOWN);
	SC_PKG_STASHCINT(PkgP, "#type_int",     SC_SCRIPT_INT);
	SC_PKG_STASHCINT(PkgP, "#type_flt",     SC_SCRIPT_FLT);
	SC_PKG_STASHCINT(PkgP, "#type_str",     SC_SCRIPT_STR);
	SC_PKG_STASHCINT(PkgP, "#type_arr",     SC_SCRIPT_ARR);
	SC_PKG_STASHCINT(PkgP, "#type_dict",    SC_SCRIPT_DICT);
	SC_PKG_STASHCINT(PkgP, "#type_ext",     SC_SCRIPT_EXT);
	SC_PKG_STASHCINT(PkgP, "#type_gext",	SC_SCRIPT_GEXT);
	SC_PKG_STASHCINT(PkgP, "#type_rc",      SC_SCRIPT_RC);
	SC_PKG_STASHCINT(PkgP, "#type_pat",     SC_SCRIPT_PAT);
	SC_PKG_STASHCINT(PkgP, "#type_fext",    SC_SCRIPT_FEXT);
	SC_PKG_STASHCINT(PkgP, "#type_func",    SC_SCRIPT_CLOS);
	SC_PKG_STASHCINT(PkgP, "#type_script",  SC_SCRIPT_SCRPT);	
    }

    void	SC_SysPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP)
    {
	// Define all SYS functions, one by one!
	SC_SysGetArgCFIndex = VMP->CFPCount; SC_PKG_STASHCFP(PkgP, "getargs", SC_SysGetCallArgs);
	SC_SysPrintCFIndex = VMP->CFPCount;  SC_PKG_STASHCFP(PkgP, "print", SC_SysPrintOut);
	SC_SysErrorCFIndex = VMP->CFPCount;  SC_PKG_STASHCFP(PkgP, "error", SC_SysRunError);
	
	SC_PKG_STASHCFP(PkgP, "pause",		SC_SysPause);
	SC_PKG_STASHCFP(PkgP, "gc",	        SC_SysDoMSGC);
	
	// Return, Error, and Sys.Exit *STAY* in the scripting environment.
	SC_PKG_STASHCFP(PkgP, "exit",		SC_SysExit);		// Exists script, can continue (Writes EXIT to OUTPUT)

	// Debug and Abort go to the low-level debugger (GDB).
	SC_PKG_STASHCFP(PkgP, "debug",          SC_SysDebugger);	// Straight to GDB, can CONTINUE
	SC_PKG_STASHCFP(PkgP, "abort",	        SC_SysDoAbort);		// Aborts into GDB, CANNOT continue
	
	SC_PKG_STASHCFP(PkgP, "reverse",	SC_SysReverse);
	SC_PKG_STASHCFP(PkgP, "copy",		SC_SysCopy);
	SC_PKG_STASHCFP(PkgP, "seedrand",       SC_SysSeedRand);
	SC_PKG_STASHCFP(PkgP, "rand",		SC_SysRand);
	SC_PKG_STASHCFP(PkgP, "time",		SC_SysTime);
	SC_PKG_STASHCFP(PkgP, "brtime",		SC_SysBrTime);
	SC_PKG_STASHCFP(PkgP, "ticks",		SC_SysTicks);		// Returns TICKS
	SC_PKG_STASHCFP(PkgP, "secs",		SC_SysElapsedSecs);

	SC_PKG_STASHCINT(PkgP, "#zulu",		SC_SYS_ZUL);
	SC_PKG_STASHCINT(PkgP, "#local",	SC_SYS_LOC);
	SC_PKG_STASHCINT(PkgP, "#sec",		SC_SYS_SEC);
	SC_PKG_STASHCINT(PkgP, "#min",		SC_SYS_MIN);
	SC_PKG_STASHCINT(PkgP, "#hour",		SC_SYS_HOUR);
	SC_PKG_STASHCINT(PkgP, "#mday",		SC_SYS_MDAY);
	SC_PKG_STASHCINT(PkgP, "#mon",		SC_SYS_MON);
	SC_PKG_STASHCINT(PkgP, "#year",		SC_SYS_YEAR);
	SC_PKG_STASHCINT(PkgP, "#wday",		SC_SYS_WDAY);
	SC_PKG_STASHCINT(PkgP, "#yday",		SC_SYS_YDAY);
	SC_PKG_STASHCINT(PkgP, "#isdst",	SC_SYS_ISDST);
    }



    // Show.Var	
    //	IN  -> Level (Optional)
    //  OUT -> Vars to show
    //         NONE -> Show all locals
    //
    // Show.Glob
    //  IN  -> Level (Optional)
    //
    // Show.Script
    //	IN  --> Level (Optional)
    //	    --> SupLevel (Optional) Display level for superiors if this is SubScript!
    //
    // Show.VM
    //
    // Show.Mem
    //  IN  -> SlabId (0 is StrHashTable)
    //	    -> Ord (1 is first one)
    //		   (0 means all)
    //
    // Show.Str
    //
    // Show.Pkg
    //  IN  -> -1 (All Pkgs)
    //		Id (Show indicated Pkg)
    //		NameStr (Show named Pkg)

    void	SC_ShowPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP)
    {
	SC_PKG_STASHCINT(PkgP, "#min",		SC_SHOW_MIN);		// Flag: Show very little
	SC_PKG_STASHCINT(PkgP, "#low",		SC_SHOW_LOW);		// Flag: Show more
	SC_PKG_STASHCINT(PkgP, "#high",		SC_SHOW_HIGH);		// Flag: Show even more
	SC_PKG_STASHCINT(PkgP, "#max",		SC_SHOW_MAX);		// Flag: Show everything
    
	// Define all SHOW functions, one by one!
	SC_PKG_STASHCFP(PkgP, "vars",		SC_SysShowVars);	// Shows OUT Vars, or all locals
	SC_PKG_STASHCFP(PkgP, "globs",		SC_SysShowGlobs);	// Shows all globals
	SC_PKG_STASHCFP(PkgP, "run",		SC_SysShowRun);		// Shows Run/Chain
	SC_PKG_STASHCFP(PkgP, "script",		SC_SysShowScript);	// Show current Script/chain 

	SC_PKG_STASHCFP(PkgP, "vm",		SC_SysShowVM);		// Shows VM
	SC_PKG_STASHCFP(PkgP, "mem",		SC_SysShowMem);		// Shows specified slab (Type, Ord)
									// Type 0 == StrTab, Ord 0 means ALL
	SC_PKG_STASHCFP(PkgP, "vmstrs",		SC_SysShowMemStr);	// Show all strings on Hash list!
	SC_PKG_STASHCFP(PkgP, "strs",		SC_SysShowStr);		// Shows strings in ScriptP->StringADP
	SC_PKG_STASHCFP(PkgP, "pkg",		SC_SysShowPkg);		// -1 means all, N just pkg N, or NameStr
    }

    void	SC_StrPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP)
    {
	// For string manipulation, instead of going to an Ed buffer
	SC_PKG_STASHCFP(PkgP,  "print",		SC_StrPrint);		// Print to Str
	SC_PKG_STASHCFP(PkgP,  "write",		SC_StrWrite);		// Write to Str
	SC_PKG_STASHCFP(PkgP,  "writeln",	SC_StrWriteLine);	//  + extra \n at end
	SC_PKG_STASHCFP(PkgP,  "form",		SC_StrForm);		// Form a str with n copies of parts
	SC_PKG_STASHCFP(PkgP,  "ftime",		SC_StrTimePrint);	// Format time to Str
	SC_PKG_STASHCFP(PkgP,  "del",		SC_SysStrDelTxt);	// Delete from Str
	SC_PKG_STASHCFP(PkgP,  "sub",		SC_SysStrGetSub);	// Read a Sub-str
	SC_PKG_STASHCFP(PkgP,  "blen",		SC_SysStrGetBLen);	// Get Byte Len
	SC_PKG_STASHCFP(PkgP,  "clen",		SC_SysStrGetCLen);	// Get Char Len
	// Concat... use Write!
	
	SC_PKG_STASHCINT(PkgP, "#byte",		SC_STR_BYTE);
	SC_PKG_STASHCINT(PkgP, "#char",		SC_STR_CHAR);
	SC_PKG_STASHCINT(PkgP, "#pos",		SC_STR_POS);
	SC_PKG_STASHCINT(PkgP, "#len",		SC_STR_LEN);
	SC_PKG_STASHCINT(PkgP, "#zulu",		SC_STR_ZUL);
	SC_PKG_STASHCINT(PkgP, "#local",	SC_STR_LOC);
    }
    
    void	SC_EdPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP)
    {
	SC_PKG_STASHCFP(PkgP, "readfile",	SC_SysEdReadFile);	// Read file into buffer, show *IF* flagged
	SC_PKG_STASHCFP(PkgP, "savefile",	SC_SysEdSaveFile);	// Save/Save-as to file (0 = CurBuf)
	SC_PKG_STASHCFP(PkgP, "getbuf",		SC_SysEdGetBuf);	// Get/Create buffer, show *IF* flagged
	SC_PKG_STASHCFP(PkgP, "setcurbuf",	SC_SysEdSetCurBuf);	// Make CUR Buf (BufP+CursorPos) for print/write
	SC_PKG_STASHCFP(PkgP, "showbuf",	SC_SysEdShow);		// Show Buf on Pane + CursorPos
	SC_PKG_STASHCFP(PkgP, "showbufsel",	SC_SysEdShowSel);	// Show Buf on Pane + SelRange
	SC_PKG_STASHCFP(PkgP, "print",		SC_SysBufPrint);	// Print to specified Buf (0 means Cur)
	SC_PKG_STASHCFP(PkgP, "write",		SC_SysBufWrite);	// Write to specified Buf (0 means Cur)
	SC_PKG_STASHCFP(PkgP, "writeln",	SC_SysBufWriteLine);	//   + extra \n at end
	SC_PKG_STASHCFP(PkgP, "del",		SC_SysEdDel);		// Delete (txt) in specified Buf (0 means Cur)
	SC_PKG_STASHCFP(PkgP, "str",		SC_SysEdStr);		// Read buffer (txt) data into string
	SC_PKG_STASHCFP(PkgP, "setpos",		SC_SysEdSetPos);	// Set BytePos/CharPos in buffer
	// No reason to SEL if not showing... can Del and Write/Print to it.
	SC_PKG_STASHCFP(PkgP, "blen",		SC_SysEdGetBLen);	// Get ByteLen of text region in buffer
	SC_PKG_STASHCFP(PkgP, "clen",		SC_SysEdGetCLen);	// Get CharCount of text region in buffer
	SC_PKG_STASHCFP(PkgP, "bpos",		SC_SysEdGetBPos);	// Get BytePos of location in buffer
	SC_PKG_STASHCFP(PkgP, "cpos",		SC_SysEdGetCPos);	// Get CharPos of location in buffer
	SC_PKG_STASHCFP(PkgP, "sel",		SC_SysEdGetSel);	// Get SelRange of buffer
	
	SC_PKG_STASHCFP(PkgP, "echo",		SC_SysEdEcho);		// Echo msg (to user) on CurFrame
	SC_PKG_STASHCFP(PkgP, "querymc",	SC_SysEdQueryMC);	// Prompt user to select (Mult-choice) on CurPane

	// Future: Close Pane/Kill Buffer...
	// Future: AskFile option (to get File Name/Path) (Provide default) (Auto-complete)
	//	   Interactive mode for Write file... user confirms, edit default filename.
	//	   Ask permission to overwrite
	// Future: Script *EVERY* command... down, up, CapitalCase, etc.

	SC_PKG_STASHCINT(PkgP, "#byte",		SC_ED_BYTE);
	SC_PKG_STASHCINT(PkgP, "#char",		SC_ED_CHAR);
	SC_PKG_STASHCINT(PkgP, "#pos",		SC_ED_POS);
	SC_PKG_STASHCINT(PkgP, "#len",		SC_ED_LEN);
	SC_PKG_STASHCINT(PkgP, "#noshow",	SC_ED_HIDE);	// Used to be #Hide!!
	SC_PKG_STASHCINT(PkgP, "#show",		SC_ED_SHOW);
    }

    void	SC_ScriptPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP)
    {
	SC_PKG_STASHCFP(PkgP, "self",		SC_SysScriptGetSelf);	// Returns cur running script
	SC_PKG_STASHCFP(PkgP, "sup",		SC_SysScriptGetSup);	// Get Sup of this (RunScript if no arg)
	SC_PKG_STASHCFP(PkgP, "id",		SC_SysScriptGetId);	// Get Id of this (RunScript if no arg)
	SC_PKG_STASHCFP(PkgP, "level",		SC_SysScriptGetLevel);	// Get Level of this (RunScript if no arg)
	SC_PKG_STASHCFP(PkgP, "load",		SC_SysScriptLoad);	// Load+Compile ".sc" or just Load ".bsc"
	SC_PKG_STASHCFP(PkgP, "exec",		SC_SysScriptExec);	// Exec the loaded Script
    }

    typedef enum {
	SC_PAT_FIRST_STASH =	0x00010000,
	    SC_PAT_POSSTASH =	0x00010000,	// Stash Directives
	    SC_PAT_STRSTASH =	0x00020000,
	    SC_PAT_ORDSTASH =	0x00030000,
	    SC_PAT_CNTSTASH =	0x00040000,	// Same as ORD
	    SC_PAT_LEN1STASH =	0x00050000,
	    SC_PAT_LEN2STASH =	0x00060000,
	    SC_PAT_NAME =	0x00070000,
	SC_PAT_LAST_STASH =	0x00070000,
    } SC_PAT_STASH;

    typedef enum {
	SC_PMODE_ONCE =		0,
	SC_PMODE_DONE =		SC_PMODE_ONCE,
	SC_PMODE_SLIDE,
	SC_PMODE_SKIP,
	SC_PMODE_CUSTOM,

	SC_PMODE_ERROR
    } SC_PMODE_MDOE;

    void	SC_PatPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP)
    {
	// How to match... 
	SC_PKG_STASHCINT(PkgP, "#once",		SC_PMODE_ONCE);		// Match just once
	SC_PKG_STASHCINT(PkgP, "#slide",	SC_PMODE_SLIDE);	// Loop, StartPos+1 each time
	SC_PKG_STASHCINT(PkgP, "#skip",		SC_PMODE_SKIP);		// Loop, Start after FirstPat match range
	SC_PKG_STASHCINT(PkgP, "#custom",	SC_PMODE_CUSTOM);	// INTERNAL

	// Define all PAT functions, one by one!
	SC_PKG_STASHCFP(PkgP,  "seq",		SC_SysMakePatSeq);	// These create patterns
	SC_PKG_STASHCFP(PkgP,  "alt",		SC_SysMakePatAlt);
	SC_PKG_STASHCFP(PkgP,  "span",		SC_SysMakePatSpan);
	SC_PKG_STASHCFP(PkgP,  "rep",		SC_SysMakePatRep);
	SC_PKG_STASHCFP(PkgP,  "char",		SC_SysMakePatChar);
	SC_PKG_STASHCFP(PkgP,  "go",		SC_SysMakePatGo);
	SC_PKG_STASHCFP(PkgP,  "at",		SC_SysMakePatAt);
	SC_PKG_STASHCFP(PkgP,  "iter",		SC_SysMakePatIter);	// Special form of REP

	SC_PKG_STASHCFP(PkgP,  "make",		SC_SysMakePat);		// Calls above functions
	SC_PKG_STASHCFP(PkgP,  "match",		SC_SysMatchPat);

	// Define PAT constants... used in script!
	SC_PKG_STASHCINT(PkgP, "#seq",		SC_PAT_SEQ);
	SC_PKG_STASHCINT(PkgP, "#alt",		SC_PAT_ALT);
	SC_PKG_STASHCINT(PkgP, "#span",		SC_PAT_SPAN);
	SC_PKG_STASHCINT(PkgP, "#rep",		SC_PAT_REP);
	SC_PKG_STASHCINT(PkgP, "#char",		SC_PAT_CHAR);
	SC_PKG_STASHCINT(PkgP, "#go",		SC_PAT_GO);
	SC_PKG_STASHCINT(PkgP, "#at",		SC_PAT_AT);
	
	SC_PKG_STASHCINT(PkgP, "#insen",	SC_P_INSEN);		// 1 bit flags,
	SC_PKG_STASHCINT(PkgP, "#sen",		SC_P_SEN);		// ... can be added/or'ed
	SC_PKG_STASHCINT(PkgP, "#pos",		SC_P_POS);
	SC_PKG_STASHCINT(PkgP, "#neg",		SC_P_NEG);
	SC_PKG_STASHCINT(PkgP, "#abs",		SC_P_ABS);
	SC_PKG_STASHCINT(PkgP, "#rel",		SC_P_REL);
	SC_PKG_STASHCINT(PkgP, "#h",		SC_P_H);
	SC_PKG_STASHCINT(PkgP, "#v",		SC_P_V);
	SC_PKG_STASHCINT(PkgP, "#str",		SC_P_STR);
	SC_PKG_STASHCINT(PkgP, "#dyn",		SC_P_DYN);
	SC_PKG_STASHCINT(PkgP, "#max",		SC_P_MAX);
	SC_PKG_STASHCINT(PkgP, "#min",		SC_P_MIN);
	SC_PKG_STASHCINT(PkgP, "#self",		SC_P_SELF);
	SC_PKG_STASHCINT(PkgP, "#cons",		SC_P_CONS);
	SC_PKG_STASHCINT(PkgP, "#res",		SC_P_RES);		// Store Result in RStack
	SC_PKG_STASHCINT(PkgP, "#nores",	SC_P_NRES);		// Do NOT store Res

	SC_PKG_STASHCINT(PkgP, "#s_pos",	SC_PAT_POSSTASH);	// Just a unique code (directive)
	SC_PKG_STASHCINT(PkgP, "#s_str",	SC_PAT_STRSTASH);	// ... will NOT be added/or'ed
	SC_PKG_STASHCINT(PkgP, "#s_ord",	SC_PAT_ORDSTASH);
	SC_PKG_STASHCINT(PkgP, "#s_cnt",	SC_PAT_CNTSTASH);
	SC_PKG_STASHCINT(PkgP, "#s_len1",	SC_PAT_LEN1STASH);
	SC_PKG_STASHCINT(PkgP, "#s_len2",	SC_PAT_LEN2STASH);
	SC_PKG_STASHCINT(PkgP, "#s_name",	SC_PAT_NAME);

	// **DO NOT** put \x00 in the "" literal string... it is NULL delimited!!
	
	SC_PKG_STASHSTR(PkgP,  "#alpha",	"A-Za-z0-9");		// Literals for SC_SysMakePatChar
	SC_PKG_STASHSTR(PkgP,  "#letter",	"A-Za-z");
	SC_PKG_STASHSTR(PkgP,  "#upper",	"A-Z");
	SC_PKG_STASHSTR(PkgP,  "#lower",	"a-z");
	SC_PKG_STASHSTR(PkgP,  "#symbol",	"!-/:-@[-'{-~");	// Printing symbols in LowAscii
	SC_PKG_STASHSTR(PkgP,  "#digit",	"0-9");
	SC_PKG_STASHSTR(PkgP,  "#binary",	"01");
	SC_PKG_STASHSTR(PkgP,  "#octal",	"0-7");	
	SC_PKG_STASHSTR(PkgP,  "#hex",		"A-Fa-f0-9");
	SC_PKG_STASHSTR(PkgP,  "#quote",	"'\"");
	SC_PKG_STASHSTR(PkgP,  "#wspace",	"\x09\x0A\x0D\x20");	// HTab, LF, CR, Space
	SC_PKG_STASHSTR(PkgP,  "#ctrl",		"\x01-\x1F\x7F");	// 0-31 + Del
	SC_PKG_STASHSTR(PkgP,  "#print",	"!-~");
	SC_PKG_STASHSTR(PkgP,  "#punc",		"!,.:;?");		// Normal punctiation (no quotes)
	SC_PKG_STASHSTR(PkgP,  "#epunc",	"!.?");			// End-of-Sentence punctuation

	// Last Unicode is 0x10FFFF, given in UTF-8 byte sequence here!
	SC_PKG_STASHSTR(PkgP,  "#unicode",	"\x80-\xF4\x8F\xBF\xBF");	// Includes HiAscii
	SC_PKG_STASHSTR(PkgP,  "#all",		"\x01-\xF4\x8F\xBF\xBF");	// Includes HiAscii
	
	SC_PKG_STASHSTR(PkgP,  "#ascii",	"\x01-\xFF");
	SC_PKG_STASHSTR(PkgP,  "#lowascii",	"\x01-\x7F");
	SC_PKG_STASHSTR(PkgP,  "#hiascii",	"\x80-\xFF");		
    }

    void	SC_LogPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP)
    {
	SC_PKG_STASHCINT(PkgP, "#common",	SC_LOG_COM);
	SC_PKG_STASHCINT(PkgP,   "#unique",	SC_LOG_UNI);
	SC_PKG_STASHCINT(PkgP, "#use",		SC_LOG_USE);
	SC_PKG_STASHCINT(PkgP,   "#fresh",	SC_LOG_NEW);		// Changed from #new
	SC_PKG_STASHCINT(PkgP, "#openclose",	SC_LOG_OC);
	SC_PKG_STASHCINT(PkgP,   "#keepopen",	SC_LOG_KO);
	SC_PKG_STASHCINT(PkgP, "#header",	SC_LOG_HED);
	SC_PKG_STASHCINT(PkgP,   "#nohead",	SC_LOG_NOH);
	SC_PKG_STASHCINT(PkgP, "#remake",	SC_LOG_REM);
	SC_PKG_STASHCINT(PkgP,   "#require",	SC_LOG_REQ);
	SC_PKG_STASHCINT(PkgP, "#local",	SC_LOG_LOC);
	SC_PKG_STASHCINT(PkgP,   "#zulu",	SC_LOG_ZUL);
	
	SC_PKG_STASHCFP(PkgP,  "open",		SC_SysLogOpen);		// Open the log file
	SC_PKG_STASHCFP(PkgP,  "close",		SC_SysLogClose);	// Close the log file
	SC_PKG_STASHCFP(PkgP,  "entry",		SC_SysLogEntry);	// Write log entries
	SC_PKG_STASHCFP(PkgP,  "entrye",	SC_SysLogEEntry);	//   Follow-on to entry (2nd line)
    }

    void	SC_MathPkgInit(SC_VMPointer VMP, SC_PkgPointer PkgP)
    {
	double	V;	// Needs a var for casting Flt to Int!
    
	V = M_PI;	 SC_PKG_STASHCFLT(PkgP, "#pi", V);
	V = M_E;	 SC_PKG_STASHCFLT(PkgP, "#e", V);
	V = M_LOG10E;	 SC_PKG_STASHCFLT(PkgP, "#loge", V);
	V = M_LN2;	 SC_PKG_STASHCFLT(PkgP, "#ln2",	V);
	V = M_LN10;	 SC_PKG_STASHCFLT(PkgP, "#ln10", V);
	V = M_SQRT2;	 SC_PKG_STASHCFLT(PkgP, "#sqrt2", V);
	V = 0.0/0.0;	 SC_PKG_STASHCFLT(PkgP, "#nan", V);		// Great with tandori chicken!
	V = 1.0/0.0;	 SC_PKG_STASHCFLT(PkgP, "#pos_inf", V);
	V = -1.0/0.0;	 SC_PKG_STASHCFLT(PkgP, "#neg_inf", V);

	SC_PKG_STASHCFP(PkgP, "ln",		SC_SysMathLn);
	SC_PKG_STASHCFP(PkgP, "log",		SC_SysMathLog);
	SC_PKG_STASHCFP(PkgP, "power",		SC_SysMathPower);
	SC_PKG_STASHCFP(PkgP, "exp",		SC_SysMathExp);
	SC_PKG_STASHCFP(PkgP, "sqrt",		SC_SysMathSqrt);
	SC_PKG_STASHCFP(PkgP, "floor",		SC_SysMathFloor);
	SC_PKG_STASHCFP(PkgP, "ceiling",	SC_SysMathCeiling);
	SC_PKG_STASHCFP(PkgP, "round",		SC_SysMathRound);
	SC_PKG_STASHCFP(PkgP, "cos",		SC_SysMathCos);
	SC_PKG_STASHCFP(PkgP, "acos",		SC_SysMathACos);
	SC_PKG_STASHCFP(PkgP, "cosh",		SC_SysMathCosh);
	SC_PKG_STASHCFP(PkgP, "sin",		SC_SysMathSin);
	SC_PKG_STASHCFP(PkgP, "asin",		SC_SysMathASin);
	SC_PKG_STASHCFP(PkgP, "sinh",		SC_SysMathSinh);
	SC_PKG_STASHCFP(PkgP, "tan",		SC_SysMathTan);
	SC_PKG_STASHCFP(PkgP, "atan",		SC_SysMathATan);
	SC_PKG_STASHCFP(PkgP, "tanh",		SC_SysMathTanh);
    }

#undef SC_PKG_STASHSTR
#undef SC_PKG_STASHCINT
#undef SC_PKG_STASHCFP

// ********************************************************************************
// Entry

// SC_VarEntryNew will create and return a VarVRecord.  The caller can
// then make it into the desired type.

SC_VarVPointer		SC_VarEntryNew(SC_VMPointer VMP)
{
    SC_SlabPointer	SlabP = VMP->VarEntrySP;
    SC_VarVPointer	EntryP;
    Uns64		Max, Count;

NewSlab:

    EntryP = (SC_VarVPointer)SC_SlabAllocEntry(SlabP, sizeof(SC_VarVRecord));
    VMP->GCStep += sizeof(SC_VarVRecord);
    if (EntryP == NULL) {
	Count = VMP->VarEntrySlotAlloc + SC_VARENTRYEXTRASLOTCOUNT;
	Max = SC_MAXSLABDATASIZE / sizeof(SC_VarVRecord);
	if (Count > Max) Count = Max;

	VMP->GCSize += SC_SlabCreate(&VMP->VarEntrySP, Count * (sizeof(SC_VarVRecord)), SC_SLAB_VENT, SC_SLAB_NO_FLAG);
	VMP->VarEntrySlotAlloc += Count;
	SlabP = VMP->VarEntrySP;
	goto NewSlab;
    }

    EntryP->Type = SC_ENTRY_VAR;
    EntryP->State = SC_NOSTATE;
    EntryP->Flags = 0;
    EntryP->ValueType = SC_VAL_INT;
    EntryP->RefCount = 1;
    EntryP->ValueP = (void *)0LL;

    SC_DEBUG_CHECKSLAB(SlabP);
    return EntryP;
}


// ********************************************************************************
// STRING (Hash, Data, and Entry)

// SC_StrStuffStr will prepare the string DataP for display...
// -- Limits it to Len chars (all inclusive)
// -- Adds "..." to the end if it is long
// -- Writes out any \n or \t to make them visible without breaks
// -- Draws ascii and UTF-8, but draws other char in Hex format.
//    (Becomes an issue for Pat char codes!)
// -- DOES NOT cut off mid-UTF8 !!

    void	SC_StrWriteHex(char C, char * WP)
    {
	Uns8	N;

	*WP++ = '\\', *WP++ = 'x';
	N = (C & 0xF0) >> 4;
	*WP++ = (N > 9) ? 'A' + N - 10 : '0' + N;
	N = C & 0x0F;
	*WP++ = (N > 9) ? 'A' + N - 10 : '0' + N;
    }

void	SC_StrStuffStr(char* StrP, char* DataP, Int16 Len)
{
    char *	WP = StrP;
    char *	RP = DataP;
    Uns8	C;

    // NOTE: Char are really Int8, so numerical comparisons to ' ' and '~'
    //	     will **NOT** work!  C must be made an Uns8.

    // Assumes DataP is a Str and NULL delimited.
    // StrP has room for at least Len chars.

    Len -= 8;		// Enough room to finish UTF-8 + ... + Last /0
    while ((C = *RP++)) {
	if (C == '\t')
	    *WP++ = '\\', *WP++ = 't', Len -= 1;
	else if (C == '\n')
	    *WP++ = '\\', *WP++ = 'n', Len -= 1;
	else if (C < ' ')
	    SC_StrWriteHex(C, WP), WP += 4, Len -= 3;
	else if (C > '~') {
	    // Could be UTF-8!!  2-4 chars  (1 char is less than '~')
	    if ((C & 0xE0) == 0xC0)
		*WP++ = C, *WP++ = *RP++, Len -= 1;					// 2-char UTF-8
	    else if ((C & 0xF0) == 0xE0)
		*WP++ = C, *WP++ = *RP++, *WP++ = *RP++, Len -= 2;			// 3-char UTF-8
	    else if ((C & 0xF8) == 0xF0)
		*WP++ = C, *WP++ = *RP++, *WP++ = *RP++, *WP++ = *RP++, Len -= 3;	// 4-char UTF-8
	    else
		SC_StrWriteHex(C, WP), WP += 4, Len -= 3;
	} else
	    *WP++ = C;

	if (--Len <= 0) break;
    }

    if (C) *WP++ = '.', *WP++ = '.', *WP++ = '.';
    *WP = 0;				// Add /0 for CString
}

Uns32	SC_StrGetCharLen(SC_StrHPointer SHP)
{
    char*	P = SHP->StrDataP->Data;
    char	C;
    Int32	Len = 0;

    while ((C = *P)) {		// Assign
	P += ED_UtilGetUTF8Len(C);
	Len++;
    }

    return Len;
}


// SC_StrGetHashVal generates a LL HashVal for the String.
// Not essential for string to come in LL-aligned, but will go faster.
//
// NOTE:  DO NOT byte hash initial out-of-align bytes (like the leftovers),
//	  would generate different HashVal depending on memory loading!

Uns64		SC_StrGetHashVal(const char * StrP, Uns32 Len)
{
    // Modified version of MurmurHash2 by Austin Appleby.

    const Uns64		M = 0xc6a4a7935bd1e995;
	  Uns64		H = (Uns64)Len ^ ((Uns64)Len * M);

    const Uns64	*	LLP = (const Uns64 *)StrP;
    const Uns64	*	LLEndP = LLP + (Len >> 3);

    while(LLP != LLEndP) {
	H ^= *LLP++;
	H *= M;
	H ^= H >> 47;
    }

    // Now the leftovers, 1 byte at a time.
    const char * CP = (const char *)LLP;

    switch (Len & 7) {
	case 7: H ^= (Uns64)(CP[6]) << 48;
	case 6: H ^= (Uns64)(CP[5]) << 40;
	case 5: H ^= (Uns64)(CP[4]) << 32;
	case 4: H ^= (Uns64)(CP[3]) << 24;
	case 3: H ^= (Uns64)(CP[2]) << 16;
	case 2: H ^= (Uns64)(CP[1]) << 8;
	case 1: H ^= (Uns64)(CP[0]);
	case 0:
		H *= M;
		H ^= H >> 31;
    };

    return H;
}

// SC_StrHashFind is used to find a String given its FullHash.  Use this
// function for lookup, but also when entering new strings, as they may already
// be in the system.  Compares Type, FullHash, Len, then Bytes!
//
// NOTE:  #if SC_GC_STRSMARTCOUNT this function decrements StrHP->State !!!
//	  When this "Dwell" count goes to zero, it zaps the StrHP.

SC_StrHPointer	SC_StrHashFind(SC_VMPointer VMP, char * StrP, Uns32 Len, Uns64 FullHash)
{
    SC_StrHashTPointer	SHTP = VMP->StrHashTP;
    Uns32		HashIndex = FullHash & SHTP->IndexMask;
    SC_StrHPointer	StrHP = SHTP->Slots[HashIndex];
    
    #if SC_GC_REFCOUNT && SC_GC_STRSMARTCOUNT
	SC_StrHPointer	PrevSHP = NULL;
    #endif    

    while (StrHP) {
	if ((StrHP->Type == SC_ENTRY_STR) &&
	    (StrHP->HashVal == FullHash) &&
	    (StrHP->StrDataP->StrLen == Len) &&
	    (0 == strncmp(StrHP->StrDataP->Data, StrP, Len)))
	    return StrHP;

	#if SC_GC_REFCOUNT && SC_GC_STRSMARTCOUNT
	    if (StrHP->RefCount == 0) {
		StrHP->State -= 1;

		// Get rid of it?
		if (StrHP->State == 0) {
		    SC_StrHPointer	NextSHP = StrHP->HashNextP;
		    SC_StrDataPointer	SDP = StrHP->StrDataP;
		    
		    SC_DEBUG_BIGVARDISP(VMP, StrP, "Purge Str   ");
		    if (PrevSHP)
			PrevSHP->HashNextP = NextSHP;
		    else
			SHTP->Slots[HashIndex] = NextSHP;
		    SC_SlabZapItem(VMP->StrDataSP, SDP, SDP->BlockLen);
		    SC_SlabZapItem(VMP->BigEntrySP, StrHP, sizeof(SC_StrHRecord));
		    StrHP = NextSHP;
		    continue;
		}
	    }
	    PrevSHP = StrHP;
	#endif

	StrHP = StrHP->HashNextP;
    }

    return NULL;
}

// SC_StrStashData will store the String in a StrData Slab.  Uses a first fit
// strategy, settles on the first slab (in the chain) that has enough room--will
// compact it if FreeBlocks on it are too fragmented.  A new slab is created
// *ONLY* if no existing one has room.

SC_StrDataPointer	SC_StrStashAlloc(SC_VMPointer VMP, Uns32 Len)
{
    SC_StrDataPointer	StrDataP;
    Uns32		LenNeeded;
    Uns64		SlabSize;

    // Leave room for an extra /0 after the string!
    LenNeeded = SC_LLALIGN(offsetof(SC_StrDataRecord, Data) + Len + 1);
    VMP->GCStep += LenNeeded;

TryAgain:

    StrDataP = (SC_StrDataPointer)SC_SlabAllocDataBlock(VMP->StrDataSP, LenNeeded, 1);
    if (! StrDataP) {
	// No room... make a new Slab... bigger than previous slabs combined!
	SlabSize = VMP->StrDataByteAlloc + SC_STRDATAEXTRASLABSIZE;
	if (SlabSize < LenNeeded) SlabSize = LenNeeded;
	if (SlabSize > SC_MAXSLABDATASIZE) SlabSize = SC_MAXSLABDATASIZE;

	VMP->GCSize += SC_SlabCreate(&VMP->StrDataSP, SlabSize, SC_SLAB_SDAT, SC_SLAB_STR_FLAG | SC_SLAB_ARR_FLAG);
	VMP->StrDataByteAlloc += SlabSize;
	goto TryAgain;
    }

    return StrDataP;
}

SC_StrDataPointer	SC_StrStashData(SC_VMPointer VMP, char * StrP, Uns32 Len)
{
    SC_StrDataPointer	StrDataP;

    if (Len > SC_MAXSTRLEN) G_SETEXCEPTION("Str too long", 0);
    StrDataP = SC_StrStashAlloc(VMP, Len);
    StrDataP->StrLen = Len;
    
    memcpy(StrDataP->Data, StrP, Len);
    StrDataP->Data[Len] = 0;			// Final /0 for CString
    
    SC_DEBUG_CHECKSLAB(VMP->StrDataSP);
    return StrDataP;
}

SC_StrDataPointer	SC_StrStashBufferData(SC_VMPointer VMP, void * BufP, Int32 StartPos, Int32 EndPos)
{
    SC_StrDataPointer	StrDataP;
    Uns32		Len;

    Len = EndPos - StartPos;
    if (Len > SC_MAXSTRLEN) G_SETEXCEPTION("Str too long", 0);    
    StrDataP = SC_StrStashAlloc(VMP, Len);
    StrDataP->StrLen = Len;

    ED_BufferGetText(BufP, StrDataP->Data, StartPos, EndPos);
    StrDataP->Data[Len] = 0;	// Final /0
    
    SC_DEBUG_CHECKSLAB(VMP->StrDataSP);
    return StrDataP;
}

// SC_StrMakeNewEntry creates a new HEntry (Strings are always hashed) for the
// String.  This StrHEntry is stored on the BigEntry Slab.  Unlike Data, every
// StrHEntry record is the same size, so it is stashed in the first empty block
// on the Slab.  There is never any need for compaction/de-fragmentation.
//
// Size allocated for the slab is *ALWAYS* exact multiples, So there will never
// be free blocks that are too small.

SC_StrHPointer	SC_StrMakeNewEntry(SC_VMPointer VMP, SC_StrDataPointer StrDataP, Uns64 HashVal)
{
    SC_StrHPointer	EntryP;

    EntryP = (SC_StrHPointer)SC_BigEntryNew(VMP);
    EntryP->Type = SC_ENTRY_STR;
    EntryP->State = SC_NOSTATE;
    EntryP->Flags = 0;
    EntryP->RefCount = 0;
    EntryP->HashVal = HashVal;
    EntryP->StrDataP = StrDataP;
    StrDataP->MasterPP = &EntryP->StrDataP;

    return EntryP;
}

// SC_StrPurge will get rid of the Str, assumes its RefCount has gone to 0.

void	    SC_StrPurge(SC_VMPointer VMP, SC_StrHPointer StrHP, SC_SlabPointer SP)
{
    SC_StrHashTPointer	HTP = VMP->StrHashTP;
    Uns32		HashIndex = StrHP->HashVal & HTP->IndexMask;
    SC_StrHPointer	PrevStrHP = HTP->Slots[HashIndex];
    SC_StrDataPointer	SDP = StrHP->StrDataP;

    SC_SAFE_CHECK(PrevStrHP, "StrPurge: Missing StrHashTab entry!");

    // Zap the data block first.  (Keep track for call in a GC Sweep!)
    VMP->GCSweepTot += sizeof(SC_StrHRecord) + SDP->BlockLen;
    SC_SlabZapItem(VMP->StrDataSP, SDP, SDP->BlockLen);
    
    // Unlink StrHP from the StrHashTable, then Zap it.
    if (PrevStrHP == StrHP) {
	if (! ((HTP->Slots[HashIndex] = StrHP->HashNextP)))	// ASSIGN
	    HTP->SlotsUsed -= 1;				// Freed the slot!
    } else {
	while (PrevStrHP->HashNextP != StrHP)
	    PrevStrHP = PrevStrHP->HashNextP;
	PrevStrHP->HashNextP = StrHP->HashNextP;
    }
    HTP->EntryCount -= 1;					// 1 less entry!

    if (SP)
	SC_SlabZapItemOnSlab(SP, StrHP, sizeof(SC_StrHRecord));
    else
	SC_SlabZapItem(VMP->BigEntrySP, StrHP, sizeof(SC_StrHRecord));
}

// SC_StrHashEntry will place the StrHP on the HashTable.
//
// NOTE call to SC_HashGrowTable, grows hash table *IFF* necessary.

void		SC_StrHashEntry(SC_VMPointer VMP, SC_StrHPointer StrHP, Int16 FirstHash)
{
    SC_StrHashTPointer	HTP = VMP->StrHashTP;
    Uns32		HashIndex = StrHP->HashVal & HTP->IndexMask;
    SC_StrHPointer	NextStrHP = HTP->Slots[HashIndex];

    StrHP->HashNextP = NextStrHP;
    HTP->Slots[HashIndex] = StrHP;
    HTP->EntryCount += 1;
    if (! NextStrHP) HTP->SlotsUsed += 1;

    if (FirstHash) SC_StrHashGrowTable(VMP);
}

// SC_StrGetEntry will *FIND* the string if already in the system, or *MAKE* a
// new Entry for it.  New strings have to be stashed on StrData Slabs, also new
// HEntry are created for them and placed on the HashTable.
//
// *ALWAYS* does VARREF.... caller should VARDEREF as necessary.
//
// NOTE:  Entries (SHP or otherwise) are purged on the 1->0 RefCount transition
//	  (if RefCount'ing).  So a string created with a RefCount of zero may
//	  stay forever (or until MS/GC)!  SC_GC_STRSMARTCOUNT helps, but only if
//	  enabled.

SC_StrHPointer	SC_StrGetEntry(SC_VMPointer VMP, char *StrP, Uns32 Len)
{
    Uns64		FullHash;
    SC_StrHPointer	EntryP;
    SC_StrDataPointer	DataP;

    FullHash = SC_StrGetHashVal(StrP, Len);
    if ((EntryP = SC_StrHashFind(VMP, StrP, Len, FullHash))) {
	SC_VARINCREF(EntryP);
	return EntryP;
    }

    // Not already in, so enter it!

    DataP = SC_StrStashData(VMP, StrP, Len);			// Enter StrData
    EntryP = SC_StrMakeNewEntry(VMP, DataP, FullHash);		// Create Entry
    SC_VARINCREF(EntryP);
    SC_StrHashEntry(VMP, EntryP, 1);				// Add to HashTable

    // printf("String: %.*s -> %lx\n", Len, StrP, (Uns64)EntryP);
    return EntryP;
}

// Will free DataP if StrHP already in Hash list!
SC_StrHPointer	SC_StrGetDataEntry(SC_VMPointer VMP, SC_StrDataPointer DataP)
{
    Uns64		FullHash;
    SC_StrHPointer	EntryP;

    FullHash = SC_StrGetHashVal(DataP->Data, DataP->StrLen);
    if ((EntryP = SC_StrHashFind(VMP, DataP->Data, DataP->StrLen, FullHash))) {
	SC_VARINCREF(EntryP);
    	// Existing EntryP already had its own StrDataP, so free DataP!
	SC_SlabZapItem(VMP->StrDataSP, DataP, DataP->BlockLen);

	return EntryP;
    }

    // Not already in, so enter it!

    EntryP = SC_StrMakeNewEntry(VMP, DataP, FullHash);
    SC_VARINCREF(EntryP);
    SC_StrHashEntry(VMP, EntryP, 1);

    // printf("String: %.*s -> %lx\n", Len, StrP, (Uns64)EntryP);
    return EntryP;    
}

// SC_StrRehashAllEntry is called when the StrHash table is grown.  By this
// time, the old StrHashTable has been deleted.  But the StrHEntry and StrData
// are snug on their slabs.
//
// Go down all LEnt slabs and process their StrHEntry records.
//
// (Can also chain all StrHEntry before zapping old StrHashTable, then process
// this chain... not sure it saves anything!)

void	SC_StrRehashAllEntry(SC_VMPointer VMP)
{
    SC_SlabPointer	SlabP;
    SC_SlabFreePointer	FreeP;
    SC_StrHPointer	SHP;
    Uns32		Offset, MaxOffset;

    SlabP = VMP->BigEntrySP;
    while (SlabP) {
	MaxOffset = SlabP->DataSize;
	Offset = 0;

	while (Offset < MaxOffset) {

	    FreeP = (SC_SlabFreePointer)(&SlabP->Data[Offset]);
	    if (FreeP->Zero == 0LL) {
		// Free block, skip it.
		Offset += FreeP->Size;
		continue;
	    }

	    // Real StrHEntry if we get here!
	    SHP = (SC_StrHPointer)(&SlabP->Data[Offset]);
	    SHP->HashNextP = NULL;	// From old HashTable!
	    SC_StrHashEntry(VMP, SHP, 0);
	    Offset += sizeof(SC_StrHRecord);
	}
    
	SlabP = SlabP->NextSP;
    }
}

#ifdef DEBUG

    // SC_DEBUG_StrStressTest is used to stress test the entire Str processing
    // machinery.  StrP is a whole buffer with many lines...
    //
    // StrP may include UTF8 chars... but that is irrelevent here
    // as they are simply churned in the hash function.  Only
    // newline and whitespace is explicitly broken out.

    void		SC_DEBUG_StrStressTest(void* SMP, char *StrP, Uns32 Len)
    {
	SC_VMPointer	VMP = (SC_VMPointer)SMP;
	SC_StrHashTPointer	SHTP = VMP->StrHashTP;
	char		*P, *EndP, *LStartP;
	Uns32		Line;
	SC_StrHPointer	StrHP;

	printf("\nStr Hash Test\n");
	printf("  Hash: %d/%d  StrEntry: %'ld  StrData: %'ld\n",
		  SHTP->SlotsUsed, SHTP->SlotCount,
		  VMP->BigEntrySlotAlloc, VMP->StrDataByteAlloc);

	P = StrP;
	EndP = StrP + Len;

	LStartP = NULL;
	Line = 0;
	while (P < EndP) {
	    if (*P == '\n') {
		if (LStartP) {
		    Line += 1;

		    StrHP = SC_StrGetEntry(VMP, LStartP, P - LStartP);
		    if (StrHP->HashNextP) printf("C");
		    else printf("-");
		    if ((Line % 100) == 0) printf("\n");
		}
		LStartP = NULL;
	    } else if ((*P != ' ') && (*P != '\t')) {
		if (LStartP == 0)
		    LStartP = P;
	    }

	    P += 1;
	}

	printf("\n  Hash: %d/%d  StrEntry: %'ld  StrData: %'ld\n\n",
		  SHTP->SlotsUsed, SHTP->SlotCount,
		  VMP->BigEntrySlotAlloc, VMP->StrDataByteAlloc);
    }

#endif


// ********************************************************************************
// CHUNKSs
//
// CHUNKs are arrays that do not need to grow or shrink, so are never re-packed
// or moved.  These are basically records of varying sizes (RunRecord, Closure,
// CodeBlockRecord, or GlobRecord), placed on the slab.
//
// While CHUNKs will NOT move or pack their data blocks, they do coalesce their
// Free blocks as necessary to combat internal fragmentation.
//
// NOTE: Each Chunk *MUST* keep track of its actual BlockLen on its slab.  This
//	 will be required when zapping/freeing the chunk!  (BlockLen may include
//	 a few more bytes, if the leftover is too small for a new block.)
//
//	 Kill a chunk with SC_SlabZapItem on VMP->ChunckSP.

void *		SC_ChunkNew(SC_VMPointer VMP, Uns32 ByteLen, Uns32 * BlockLenP)
{
    Uns32		LenNeeded = SC_LLALIGN(ByteLen);
    Uns32		SlabSize;
    SC_GenDataPointer	GenP;

    VMP->GCStep += LenNeeded;
    GenP = SC_SlabAllocDataBlock(VMP->ChunkSP, LenNeeded, 0);
    if (! GenP) {
	// No room... make a new Slab... bigger than previous slabs combined!
	SlabSize = VMP->ChunkByteAlloc + SC_CHUNKEXTRASLABSIZE;
	if (SlabSize < LenNeeded) SlabSize = LenNeeded + SC_CHUNKEXTRASLABSIZE;
	if (SlabSize > SC_MAXSLABDATASIZE) SlabSize = SC_MAXSLABDATASIZE;

	VMP->GCSize += SC_SlabCreate(&VMP->ChunkSP, SlabSize, SC_SLAB_CHNK, SC_SLAB_CHNK_FLAG);
	VMP->ChunkByteAlloc += SlabSize;

	GenP = SC_SlabAllocDataBlock(VMP->ChunkSP, LenNeeded, 0);
    }

    if (GenP) *BlockLenP = GenP->BlockLen;    
    return GenP;
}


// ********************************************************************************
// ARRAYs
//
// Arrays are both an explicit data type and used implicitly to implement
// dictionary (assoc table) hashing of Pairs.  They are also used for various
// data structures inside the compiler.

// SC_ArrAllocNewData is the main function for creating and storing Array data.
// SlotCount specifies the data size in Void* increments... it is NOT BlockSize.
//
// Normally, all slots in the new array are set to NULL.  But if InitArrP is
// provided (not NULL), it indicates another typically smaller Array that can be
// used to initialize this one.  InitArrP will be limited to InitSlotCount
// slots.
//
// NOTE:	Arrays DO MOVE!  SC_SlabAllocDataBlock *WILL* pack the Data
//		Slab if it has been fragmented!
//
// CAUTION:	ArrAllocNewData SHOULD NEVER try to initialize the new array with
//		a data pointer from another InitArray...  ArrAllocNewData *MAY*
//		cause a Slab re-pack, which would move the InitArray!!


// PlacePP is (a) Where ADP is stashed and (b) where ADP->MasterPP is set to!!

// (History: Relied on client to set ADP->MasterPP but easy to forget and lead
//           to bizarre bugs!!  So now, it is all self-contained.)
SC_ArrDataPointer	SC_ArrAllocNewData(SC_VMPointer VMP, SC_ArrDataPointer * PlacePP,  Uns32 SlotCount, Int16 ZeroAll)
{
    SC_ArrDataPointer	ADP;
    Uns32		BlockSize, Len;

    if (SlotCount > SC_MAXARRLEN) G_SETEXCEPTION("Array too big", 0);
    BlockSize = offsetof(SC_ArrDataRecord, Data) + (SlotCount * sizeof(void *));
    VMP->GCStep += BlockSize;		// May allocate slightly more...

TryAgain:

    ADP = (SC_ArrDataPointer)SC_SlabAllocDataBlock(VMP->ArrDataSP, BlockSize, 1);
    if (ADP) {
	// Allocated Len may be slightly larger than requested... but SlotCount is
	// always given as requested.
	ADP->SlotCount = SlotCount;
	//printf("Alloc Data: 0x%08lx Slots:%d Block:%d\n", (Int64)ADP, SlotCount, ADP->BlockLen);
	
	Len = (ADP->BlockLen - offsetof(SC_ArrDataRecord, Data)) / sizeof(void *);
	if (ZeroAll) {
	    void **	DP = ADP->Data;
	    while (Len--) *DP++ = NULL;
	}
	
	SC_DEBUG_CHECKSLAB(VMP->ArrDataSP);
	*PlacePP = ADP;
	ADP->MasterPP = PlacePP;
	return ADP;
	
    } else {
	Uns64		SlabSlotCount;
	
	// Allocate a new slab and try again!
	// Make Slab size = TOTAL previous slab size + Extra

	SlabSlotCount = VMP->ArrDataSlotAlloc + SC_ARRDATAEXTRASLOTCOUNT;
	if (SlabSlotCount < SlotCount) SlabSlotCount = SlotCount;
	if ((SlabSlotCount * sizeof(void *)) > SC_MAXSLABDATASIZE)
	    SlabSlotCount = SC_MAXSLABDATASIZE / sizeof(void *);

	VMP->GCSize += SC_SlabCreate(&VMP->ArrDataSP, SlabSlotCount * sizeof(void *), SC_SLAB_ADAT, SC_SLAB_ARR_FLAG);
	VMP->ArrDataSlotAlloc += SlabSlotCount;
	goto TryAgain;
    }
}


void		SC_ArrInitNewArr(SC_ArrDataPointer ADP, void ** InitArrP, Uns32 InitSlotCount)
{
    Uns32	Count = ADP->SlotCount;
    void **	DP = ADP->Data;
    void **	SP = InitArrP;

    if (SP) {
	if (InitSlotCount > Count) {		// Excess InitSlotCount NOT copied
	    InitSlotCount = Count;
	    Count = 0;
	} else 
	    Count -= InitSlotCount;

	while (InitSlotCount--)
	    *DP++ = *SP++;
    }

    while (Count--)
	*DP++ = NULL;				// ZERO out remaining slots.
}

// SC_ArrResizeData can be used to shrink or expand the array refered to by
// OldDataP.  Caller *MUST* update ArrDataP in the EntryP or other data
// structure, as the resized array MAY BE IN NEW LOCATION.  (MasterPP is copied
// from OldDataP to NewDataP!)
//
// Add code to grow in place if possible... future! ????
//      Degenerate case when CUR slab has room, but have to compact
//	preceeding arrays up and subsequent arrays down to make room.

SC_ArrDataPointer	SC_ArrResizeData(SC_VMPointer VMP, SC_ArrDataPointer OldDataP,
					  Uns32 NewSlotCount, Int16 ZeroAll)
{
    Uns32		OldSlotCount = OldDataP->SlotCount;
    SC_SlabPointer	SlabP;
    SC_ArrDataPointer	NewDataP;
    SC_SlabFreePointer	FreeP;
    Uns32		BlockLen;
    SC_ArrDataPointer *	OldMasterPP;
    const Uns32		EnoughSlotCount = SC_MINARRLEN + (offsetof(SC_ArrDataRecord, Data) / sizeof(void *));

    // Need to find Slab that holds OldDataP;
    SlabP = SC_SlabFindSlab(VMP->ArrDataSP, OldDataP);

    if (OldSlotCount < NewSlotCount) {
	// Growing... Create a NewDataP
	//
	// CAUTION: SC_ArrAllocNewData can pack slab, moving OldDataP!  Change
	//	    its MasterPP so location is updated!  OldDataP will be
	//	    zapped, so no need to restore its MasterPP.

	OldMasterPP = OldDataP->MasterPP;			// Stash first
	OldDataP->MasterPP = &OldDataP;				// Point to local param!
	    SC_ArrAllocNewData(VMP, &NewDataP, NewSlotCount, ZeroAll);
	NewDataP->MasterPP = OldMasterPP;
	if (! ZeroAll) SC_ArrInitNewArr(NewDataP, OldDataP->Data, OldSlotCount);
	
	// Turn OldDataP into a Free block
	BlockLen = OldDataP->BlockLen;
	FreeP = (SC_SlabFreePointer)OldDataP;
	FreeP->Zero = 0LL;
	FreeP->Size = BlockLen;
	FreeP->NextFree = SlabP->FirstFree;
	SlabP->FirstFree = (char *)FreeP - SlabP->Data;
	SlabP->FreeCount += 1;
	SlabP->DataUsed -= BlockLen;

	return NewDataP;
    }

    if (OldSlotCount > NewSlotCount + EnoughSlotCount) {
	// Shrinking... but only if MORE than enough to make another Array in the space!
	// Update OldDataP slot count and block len.
	BlockLen = OldDataP->BlockLen;
	OldDataP->SlotCount = NewSlotCount;
	OldDataP->BlockLen = offsetof(SC_ArrDataRecord, Data) + (NewSlotCount * sizeof(void *));

	// Create a new Free block.
	FreeP = (SC_SlabFreePointer)(OldDataP->Data[NewSlotCount]);
	FreeP->Zero = 0LL;
	FreeP->Size = BlockLen - OldDataP->BlockLen;
	FreeP->NextFree = SlabP->FirstFree;
	SlabP->FirstFree = (char *)FreeP - SlabP->Data;
	SlabP->FreeCount += 1;
	SlabP->DataUsed -= FreeP->Size;
    }

    return OldDataP;
}

SC_ArrVPointer		SC_ArrMakeNewArr(SC_VMPointer VMP, Uns32 SlotCount)
{
    SC_ArrVPointer	EntryP;

    EntryP = (SC_ArrVPointer)SC_VarEntryNew(VMP);
    EntryP->Type = SC_ENTRY_ARR;
    EntryP->Res = 0;
    EntryP->RefCount = 1;		// Assume one Ref to start with!
    
    SC_ArrAllocNewData(VMP, &EntryP->ArrDataP, SlotCount, 1);
    return EntryP;
}


void			SC_ArrPurge(SC_VMPointer VMP, SC_ArrVPointer EntryP)
{
    SC_ArrDataPointer	ADP = (*(SC_ArrVPointer)EntryP).ArrDataP;

    #if SC_GC_REFCOUNT
    {
	SC_VarVPointer *	CurVPP = (SC_VarVPointer *)&ADP->Data[0];
	SC_VarVPointer *	EndVPP =  CurVPP + ADP->SlotCount;

	while (CurVPP < EndVPP) {
	    if (*CurVPP) SC_VarDeRef(VMP, *CurVPP);
	    CurVPP += 1;
	}
    }
    #endif

    SC_SlabZapItem(VMP->ArrDataSP, ADP, ADP->BlockLen);
    SC_SlabZapItem(VMP->VarEntrySP, EntryP, sizeof(SC_VarVRecord));
}

// ********************************************************************************
// EXT Entries
//
// There are 3 kinds:
// -- EXT	Entry stores Ptr to data memory in Client's memory.
// -- ArrXT	Entry stores Ptr to Arr memory to store client data.
//		Data can re-size and move on ADAT slab.
// -- ChunkXT	Entry in CHUNK slab, contains client data.
//		Data cannot re-size and move.

SC_EXTVPointer		SC_EXTMakeNew(SC_VMPointer VMP, void * ExtP, Uns16 ExtType)
{
    SC_EXTVPointer	EntryP;

    EntryP = (SC_EXTVPointer)SC_VarEntryNew(VMP);
    EntryP->Type = SC_ENTRY_EXT;
    EntryP->ExtType = ExtType;
    EntryP->RefCount = 1;		// Assume one Ref to start with!
    EntryP->ExtP = ExtP;
    return EntryP;
}

// NOTE: For ArrXT *DO NOT* call SC_ArrPurge!
//	 Fields in ArrXT's ArrDataP *ARE NOT* entry pointers!!!!
//	 Client must DeRef/Zap pointers in ArrDataP...

SC_ArrXTVPointer	SC_ArrXTMakeNew(SC_VMPointer VMP, Uns32 SlotCount, Uns16 ExtType)
{
    SC_ArrXTVPointer	EntryP;

    EntryP = (SC_ArrXTVPointer)SC_VarEntryNew(VMP);
    EntryP->Type = SC_ENTRY_ARREXT;
    EntryP->ExtType = ExtType;
    EntryP->RefCount = 1;		// Assume one Ref to start with!

    SC_ArrAllocNewData(VMP, &EntryP->ArrDataP, SlotCount, 1);
    return EntryP;
}

SC_ChunkXTVPointer	SC_ChunkXTMakeNew(SC_VMPointer VMP, Uns32 DataLen, Uns32 ExtData, Uns16 ExtType)
{
    SC_ChunkXTVPointer	EntryP;
    Uns32		BlockLen;

    BlockLen = sizeof(SC_ChunkXTVRecord) + DataLen;
    EntryP = (SC_ChunkXTVPointer)SC_ChunkNew(VMP, BlockLen, &BlockLen);
    EntryP->Type = SC_CHUNK_EXT;
    EntryP->ExtType = ExtType;
    EntryP->RefCount = 1;		// Assume one Ref to start with!
    EntryP->ExtData = ExtData;
    EntryP->BlockLen = BlockLen;
    return EntryP;
}

// ********************************************************************************
// DICT & PAIRS

// DICTionaries use an ARRAY data block as their closed hash array.  Each slot
// in the array will point to 1 PairHRecord (stored on their own separate slab).
//
// NOTE:	Hash arrays are *ALWAYS* Length = 2 + (2^N).
//
//		The first 2 slots of the array DO NOT participate in hashing,
//		they stash the FIRST PairHRecord and the PairCount, Pairs are
//		also chained togther.
//
// NOTE:	Closed Double Hash needs two values:  HashStart + HashStep.
//
//		Every Pair has a Key, which is an EntryP or Int/Float number
//			xxxVPointer are 16 bytes long...  lowest 4 bits are 0
//			StrHPointer are 32 bytes long.... lowest 5 bits are 0
//
//		So the HashVal starts as an (Uns64)Pointer shifted right 4 or 5
//		bits for an EntryP or the numeric value if the key is a number.
//		
//		HashStep is same for all, selected from a list of primes using
//		the bottom 4 bits of the HashVal.  (The Hash portion of the
//		Array is 2^N long, so the prime Steps, will roll-over and visit
//		all array slots before repeating!)
//
//		The HashStep (a small prime) is multiplied by 2^(N-4) and added
//		to the HashVal to 'smear' it across the entire Hash Array.
//		Finally, the bottom N bits of this becomes the HashStart, which
//		indexes into the 2^N array.
//
// The standard double hashing algorithm is augmented with a recursive
// Cost-to-Move (Gonnet-Munro) form of Brent's algorithm.  Each time a Step
// location is blocked, the blocking entry is asked to move.  It will consider
// the cost by asking the entry blocking it which will recursively do the same.
// Given the current hash function, this seldom goes more than 3 levels, but a
// hard limit of 10 recursive layers is enforced.  The algorithm *DOES* *NOT*
// compute the entire cost tree, but goes with the first saving solution, as the
// probability of an optimum solution (and payback) is reduced with each extra
// move while the computational cost increases.


// NOTE:	DictP->Step is only Uns8... primes larger than 255 will not work!
//		Shuffled primes work better with consecutive ptrs.

Uns8		SC_HashStepArray[16] = {3, 53, 7, 43, 13, 37, 19, 29, 23, 31, 17, 41, 11, 47, 5, 59};

// EXACT same Hash routines are called to store *AND* retrieve.
//
// If Key is Entry (or Ext) Pointer, use the significant bits in the address.
// If key is a number, just use the number.  (Slightly different hash algorithm
// for (switch) CASES to incorporate SwitchId.)

#define	SC_DHASHGETVAL(_KP, _KT, _HV)										\
	{_HV = (Uns64)_KP;											\
	 if ((_KT) >= SC_VAL_PTR) _HV >>= (((_KT == SC_VAL_PTR) && (_KP->Type == SC_ENTRY_STR)) ? 5 : 4); }	\

#define SC_DHASHGETCASEVAL(_K, _ID, _KT, _HV)									\
	{_HV = _K; if ((_KT) == SC_VAL_PTR) _HV >>= 5; _HV ^= _ID;}

#define	SC_DHASHGETSTEP(_HS, _HV)		_HS = SC_HashStepArray[_HV & 15LL]
#define	SC_DHASHGETSTART(_S, _HV, _HS, _B, _M)	_S = ((_HV + (_HS << (_B - 4))) & _M)

    // Many more complicated options that work worse...
    // SC_DHASHGETSTEP(_HS, _HV, _B)		_HS = SC_HashStepArray[(_HV ^ (_HV >> _B)) & 15LL]


SC_DictVPointer		SC_DictCreateNewDict(SC_VMPointer VMP, Uns32 PairCount)
{
    Uns32		SlotCount;
    Uns16		MaskBits;
    SC_DictVPointer	DictP;
    
    // Allocate a new hash Arr... 2^N plus 2 slots
    PairCount <<= 1;				// Twice as many slots
    if (PairCount > SC_MAXDICTLEN) PairCount = SC_MAXDICTLEN;

    SlotCount = SC_MINDICTLEN;
    MaskBits = SC_MINDICTMASK;
    while (SlotCount < PairCount) SlotCount <<= 1, MaskBits += 1;

    // SC_ArrAllocNewData *MAY* make the hash array slightly oversize.. reflected in
    // ADP->BlockLen.  Those slots are ignored, just tracks MaskBits.
    DictP = (SC_DictVPointer)SC_BigEntryNew(VMP);
    DictP->Type = SC_ENTRY_DICT;
    DictP->MaskBits = MaskBits;

    SC_ArrAllocNewData(VMP, &DictP->HADP, SlotCount, 1);
    return DictP;
}

// NOTE: ForceZap can be set *ONLY* if Key and Val DO NOT refer to Vars.
//	 ForceZap will NOT do any VARDEREF!!
//	 ForceZap only if Pair in Dict is guaranteed to NOT have independent life!

void	SC_DictPurge(SC_VMPointer VMP, SC_DictVPointer DictP, Int16 ForceZap)
{
    SC_PairHPointer	PairP, NextPairP;

    // Go through every Pair... free them and update RefCounts as necessary.
    PairP = (SC_PairHPointer)DictP->FirstPairP;
    if (ForceZap) {
	while (PairP) {
	    NextPairP = PairP->NextPairP;
	    SC_SlabZapItem(VMP->BigEntrySP, PairP, sizeof(SC_PairHRecord));
	    PairP = NextPairP;
	}
	
    } else {
	// NOTE: If RefCounting is OFF (possible), SC_VARDEREF does NOTHING!
	//	 MS/GC will clear them, this func just decouples them from Dict chain.
	while (PairP) {
	    NextPairP = PairP->NextPairP;
		PairP->NextPairP = NULL;
		SC_VARDEREF(VMP, (SC_VarVPointer)PairP);
	    PairP = NextPairP;
	}
    }

    // Now zap the hash array, then the Dict entry.
    SC_SlabZapItem(VMP->ArrDataSP, DictP->HADP, DictP->HADP->BlockLen);
    SC_SlabZapItem(VMP->BigEntrySP, DictP, sizeof(SC_DictVRecord));
}

// Similar to  SC_VarEntryNew, but makes a PAIR.

SC_PairHPointer		SC_BigEntryNew(SC_VMPointer VMP)
{
    SC_SlabPointer	SlabP = VMP->BigEntrySP;
    SC_PairHPointer	EntryP;
    Uns64		Max, Count;

NewSlab:

    VMP->GCStep += sizeof(SC_PairHRecord);
    EntryP = (SC_PairHPointer)SC_SlabAllocEntry(SlabP, sizeof(SC_PairHRecord));
    if (EntryP == NULL) {
	// Make a new slab... Make as big as sum of all so far, but hold to Max.
	Count = VMP->BigEntrySlotAlloc + SC_BIGENTRYEXTRASLOTCOUNT;
	Max = SC_MAXSLABDATASIZE / sizeof(SC_PairHRecord);
	if (Count > Max) Count = Max;

	VMP->GCSize += SC_SlabCreate(&VMP->BigEntrySP, Count * sizeof(SC_PairHRecord), SC_SLAB_BENT, SC_SLAB_NO_FLAG);
	VMP->BigEntrySlotAlloc += Count;
	SlabP = VMP->BigEntrySP;
	goto NewSlab;
    }

    EntryP->Type = SC_ENTRY_PAIR;
    EntryP->Step = 0;
    EntryP->KeyType = EntryP->ValType = 0;
    EntryP->RefCount = 1L;
    EntryP->KeyP = EntryP->ValueP = NULL;
    EntryP->NextPairP = NULL;

    SC_DEBUG_CHECKSLAB(SlabP);
    return EntryP;
}

    // SC_PairMoveRecord is just for convenience, passing fewer args to SC_AuxDictPairMove,
    // saves on stack frame when recursing!
    typedef struct _SC_PairMoveRecord {
	Uns64			HMask;			// BitMask for Hash Array in Dict
	SC_PairHPointer *	HAP;			// Hash Array in Dict!
	SC_DictVPointer		DictP;			// The Dict that contains HAP!
    } SC_PairMoveRecord, *SC_PairMovePointer;


    // SC_AuxDictPairMove is my version of a recursive Cost-to-Move algorithm.  (A
    // recursive version of Brent or perhaps a Gonnet-Munro.)  The probability of
    // saving from bumping a collision grows less with each extra step, so the first
    // savings are taken rather than visiting all possible alternatives.
    //
    // Returns 1 to say "YES, I moved, use the slot" or
    // Returns 0 to say "NO, it will cost too much too move".

    Int16	SC_AuxDictPairMove(SC_PairMovePointer PMP, Uns32 HIndex, Uns32 Saving, Uns16 Depth)
    {
	SC_PairHPointer	PairP = PMP->HAP[HIndex];
	Uns64		NewIndex;
	Uns32		ProbeCount;

	if (Depth > 10) return 0;		// Just say no... too deep already

	// Look ahead, see what it would cost if THIS pairP moved to a free spot.
	// This would be the total cost...  anything better is a saving.
	NewIndex = ((Uns64)HIndex + (Uns64)PairP->Step) & PMP->HMask;
	ProbeCount = 1;
	while (ProbeCount < Saving) {	// No point looking farther!
	    if (PMP->HAP[NewIndex] == NULL) {
		Saving = ProbeCount;	// See if we can save even more!

		if (ProbeCount < 3) {	// No point moving anyone else
		    PMP->HAP[HIndex] = NULL;
		    PMP->HAP[NewIndex] = PairP;
		    PMP->DictP->TotProbes += ProbeCount;
		#if SC_DEBUG_PRINT_DICT
		    printf(" [Bump D:%d] ", Depth);
		#endif
		    return 1;
		}
		break;
	    }

	    NewIndex = (NewIndex + PairP->Step) & PMP->HMask;
	    ProbeCount += 1;
	}

	// Now that we know the cost, recurse to move others
	NewIndex = ((Uns64)HIndex + (Uns64)PairP->Step) & PMP->HMask;
	ProbeCount = 1;
	while (1) {
	    Saving -= 1, ProbeCount += 1;
	    if (Saving < 1) return 0;				// Nothing can be saved...

	    if ((PMP->HAP[NewIndex] == NULL) ||			// Empty or
		(SC_AuxDictPairMove(PMP, NewIndex, Saving, Depth+1))) {
		// Move here... and return 1 up the chain!
		PMP->HAP[HIndex] = NULL;
		PMP->HAP[NewIndex] = PairP;
		PMP->DictP->TotProbes += ProbeCount;
		return 1;
	    }

	    NewIndex = (NewIndex + PairP->Step) & PMP->HMask;
	}

	return 0;
    }

    // SC_AuxDictStashPair is the main function for storing a Pair on the hash array.
    // If there is pair entry blocking this one, it will call SC_AuxDictPairMove
    // to recursively consider moving it and whatever is blocking it.
    //
    // Assumes PairP has already been chained in (NextPairP and ArrDataP->Data[0]).
    // Assumes DictP->PairCount has already been updated.
    //
    // This same function is called to re-hash when the HashArr is resized!
    
    void	SC_AuxDictStashPair(SC_VMPointer VMP, SC_DictVPointer DictP, SC_PairHPointer PairP)
    {
	SC_PairMoveRecord	PM;
	Uns64			HVal, HStep;
	Uns32			HStart, HIndex, FinalIndex;
	Uns32			ProbeCount, CurProbe, Saving;

	PM.HMask = (1LL << DictP->MaskBits) - 1;
	PM.HAP = (SC_PairHPointer *)&DictP->HADP->Data;
	PM.DictP = DictP;

	if (PairP->ValType == SC_VAL_CASE) {
	    SC_CaseHPointer		CaseP = (SC_CaseHPointer)PairP;
	    Uns32			HashId = CaseP->Id;
	    HashId = (HashId << 2) ^ HashId;
	    SC_DHASHGETCASEVAL(CaseP->Key, HashId, PairP->KeyType, HVal);
	} else
	    SC_DHASHGETVAL(PairP->KeyP, PairP->KeyType, HVal);

	SC_DHASHGETSTEP(HStep, HVal);
	SC_DHASHGETSTART(HStart, HVal, HStep, DictP->MaskBits, PM.HMask);
	PairP->Step = (Uns8)HStep;

	// Look ahead, see how many probes are needed to stash PairP!
	HIndex = HStart;
	ProbeCount = 1;
	while (PM.HAP[HIndex]) {
	    HIndex = (HIndex + PairP->Step) & PM.HMask;
	    ProbeCount += 1;
	}

	// ProbeCount <= 2 means NOTHING can be saved by moving another.
	if (ProbeCount < 3) {
	    PM.HAP[HIndex] = PairP;
	    DictP->TotProbes += ProbeCount;
	    return;
	}

	// ProbeCount of 3 or more... ask blocking PairP to move!  PairMove will recurse!
    #if SC_DEBUG_PRINT_DICT  
	printf("  -- COL  Index:%d  ProbeCount:%d  Step:%d  Elts:%d", HIndex, ProbeCount, PairP->Step, DictP->PairCount);
    #endif    
	FinalIndex = HIndex;
	HIndex = HStart;
	CurProbe = 1;
	Saving = ProbeCount;
	while (PM.HAP[HIndex]) {

	    if (SC_AuxDictPairMove(&PM, HIndex, --Saving, 1)) {
		PM.HAP[HIndex] = PairP;
		DictP->TotProbes += CurProbe;
	    #if SC_DEBUG_PRINT_DICT
		printf(" --> Saved:%d\n", Saving);
	    #endif
		return;
	    }
	    if (Saving < 2) break;		// Min cost is 1, so don't recurse!
	    HIndex = (HIndex + PairP->Step) & PM.HMask;
	    CurProbe += 1;
	}

	// No other PairP moved... sigh.
	PM.HAP[FinalIndex] = PairP;
	DictP->TotProbes += ProbeCount;

    #if SC_DEBUG_PRINT_DICT
	printf(" --> Saved NOTHING\n");
    #endif
    }

// SC_DictStashNewPair adds a new pair to a dict, places it in the HashArray.
// ***Caller should ensure Pair is NOT ALREADY in DictP!!***
//
// It will double the Dictionary HashArray if (PairCount > 2/3 SlotCount).  When
// doubled, every pair has to be re-hashed on the new array!  This is also the
// right time to zap previously deleted (Value set to Nil) Pairs.
//
// This function calls SC_AuxDictStashPair which does the actual work of placing
// the new Pair on to the Dictionary's hash array.
//
// NOTE: Expanding the HAP **WILL NOT** change PairP->NextPairP ORDER!!!
//
// BEWARE 1) Dangerous to ZAP from body of an IMAP ByteCode (Scripting)
//        2) Script does NOT currently set any values to NULL (Ptr+0), it
//	     will only set to 0 (Int+0).

void		SC_DictStashNewPair(SC_VMPointer VMP, SC_DictVPointer DictP, SC_PairHPointer PairP)
{
    Uns32		SlotCount;
    Uns64		PairCount;
    SC_PairHPointer	PrevPairP;

    SC_VARREFCOND((PairP->KeyType == SC_VAL_PTR) && PairP->KeyP, PairP->KeyP);
    SC_VARREFCOND((PairP->ValType == SC_VAL_PTR) && PairP->ValueP, PairP->ValueP);

    PairCount = DictP->PairCount += 1;
    PairP->NextPairP = DictP->FirstPairP;
    DictP->FirstPairP = PairP;
    
    // Check count first... expand HAP if too full!
    // Dangerous to check for "too many collisions", bad hash/keys will collide even in big arrays!
    SlotCount = DictP->HADP->SlotCount;
    if ((PairCount + (PairCount/2)) > SlotCount) {

	#if SC_DEBUG_PRINT_DICT
	    SC_DictPrint(VMP, DictP, 1, 10);
	#endif
	SlotCount = DictP->HADP->SlotCount << 1;

	// SC_ArrResizeData will give us a *NEW* ArrDataP!  (MasterPP is copied!)
	DictP->HADP = SC_ArrResizeData(VMP, DictP->HADP, SlotCount, 1);	// Zero all slots
	DictP->MaskBits += 1;								// Doubled!
	DictP->TotProbes = 0L;								// Will recount!
	#if SC_DEBUG_PRINT_DICT
		printf("  ----Resize-----------\n");
	#endif

        // Re-hash everything on the new Array
	PrevPairP = NULL;
	while (PairP) {
	    // Zap the Pair entry if ValueP is NULL... Lazy delete.
	    if ((PairP->ValType == SC_VAL_PTR) &&
		(PairP->ValueP == NULL)) {

		// Pair may presist if aliased (solitary life)... RefCount may be off too!
		PrevPairP->NextPairP = PairP->NextPairP;
		PairP->NextPairP = NULL;
		DictP->PairCount -= 1;		// Adjust!
		SC_VARDEREF(VMP, (SC_VarVPointer)PairP);

	    } else {
		SC_AuxDictStashPair(VMP, DictP, PairP);
		PrevPairP = PairP;
		PairP = PairP->NextPairP;
	    }
	}
	#if SC_DEBUG_PRINT_DICT
	    printf("  ---------------------\n");
	#endif
	
	return;
    }

    // HashArr is big enough.. just add new Pair in.
    SC_AuxDictStashPair(VMP, DictP, PairP);
}

// Stashes Val under INTEGER Key
void	SC_DictIntStashVal(SC_VMPointer VMP, SC_DictVPointer DictP, Int64 Key, Int64 Val, Uns8 ValType)
{
    SC_PairHPointer	PairP = SC_BigEntryNew(VMP);

    PairP->KeyType = SC_VAL_INT;
    PairP->KeyP = (void *)Key;
    PairP->ValType = ValType;
    PairP->ValueP = (SC_VarVPointer)Val;
    SC_DictStashNewPair(VMP, DictP, PairP);
}

// Stashes Val under KeyNameP
SC_StrHPointer	SC_DictStashVal(SC_VMPointer VMP, SC_DictVPointer DictP, char * KeyNameP, Int64 Val, Uns8 ValType)
{
    SC_StrHPointer	StrHP = SC_StrGetEntry(VMP, KeyNameP, strlen(KeyNameP));
    SC_PairHPointer	PairP = SC_BigEntryNew(VMP);

    PairP->KeyType = SC_VAL_PTR;
    PairP->KeyP = (SC_VarVPointer)StrHP;
    PairP->ValType = ValType;
    PairP->ValueP = (SC_VarVPointer)Val;
    SC_DictStashNewPair(VMP, DictP, PairP);
    // StrHP comes in with RefCount +1 and Dict will +1, so -1 here!
    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);
    return StrHP;
}

// Stashes StrHP under KeyNameP
void	SC_DictStashStr(SC_VMPointer VMP, SC_DictVPointer DictP, char *KeyNameP, char *StrP)
{
    SC_StrHPointer	StrHP = SC_StrGetEntry(VMP, StrP, strlen(StrP));
    
    SC_DictStashVal(VMP, DictP, KeyNameP, (Int64)StrHP, SC_VAL_PTR);
    // StrHP comes in with RefCount +1 and Dict will +1, so -1 here!
    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);
}


// SC_DictGetPair is the lookup function.
//
// HVal comes from the KeyP.  SC_DHASHGETSTART will create a starting Index for
// looking into the HashArray (HAP).  After each miss (probe), the HStep is
// added to the HIndex (and masked).

SC_PairHPointer		SC_DictGetPair(SC_DictVPointer DictP, SC_VarVPointer KeyP, Int16 KeyType)
{
    Uns64		HMask, HVal, HStep;
    Uns32		HIndex;
    SC_PairHPointer *	HAP;
    SC_PairHPointer	PairP;

    HMask = (1LL << DictP->MaskBits) - 1;
    HAP = (SC_PairHPointer *)&DictP->HADP->Data;

    SC_DHASHGETVAL(KeyP, KeyType, HVal);
    SC_DHASHGETSTEP(HStep, HVal);
    SC_DHASHGETSTART(HIndex, HVal, HStep, DictP->MaskBits, HMask);

    while ((PairP = HAP[HIndex])) {
	if ((KeyType == PairP->KeyType) && (PairP->KeyP == KeyP))
	    return PairP;
    
	HIndex = (HIndex + HStep) & HMask;
    }

    // Hit an open slot, so we do NOT have it!
    return NULL;
}

// SC_DictGetCase is the lookup function for CaseHPointer version of Pairs.
// This is only used to handle SWITCH/CASES... checks on ID too during hashing
// and retrieval.
//
// NOTE: CaseDict stores StrHPointer Key as index into StringADP.
//	 To check for a match, it retrieves StringADP->Data[CaseP->Key]
//	 which is a StrHPointer and compares to Key arg.

SC_CaseHPointer		SC_DictGetCase(SC_ArrDataPointer StringADP, SC_DictVPointer DictP, Uns64 Key, Uns32 Id, Int16 KeyType)
{
    Uns64		HMask, HVal, HStep;
    Uns32		HIndex;
    SC_CaseHPointer *	HAP;
    SC_CaseHPointer	CaseP;
    Uns32		HashId;

    HMask = (1LL << DictP->MaskBits) - 1;
    HAP = (SC_CaseHPointer *)&DictP->HADP->Data;		// Skip the first 2 slots
    HashId = (Id << 2) ^ Id;					// Hashes better!

    SC_DHASHGETCASEVAL(Key, HashId, KeyType, HVal);
    SC_DHASHGETSTEP(HStep, HVal);
    SC_DHASHGETSTART(HIndex, HVal, HStep, DictP->MaskBits, HMask);

    if (KeyType == SC_VAL_PTR) {
	// Dealing with a String...
	if (StringADP) {
	    SC_StrHPointer *	StrHPArr = (SC_StrHPointer *)StringADP->Data;

	    while ((CaseP = HAP[HIndex])) {
		// *MUST* check Key last... otherwise may exceed
		// StrHPArr bounds and access random memory.
		if ((KeyType == SC_VAL_PTR) &&
		    (CaseP->Id == Id) &&
		    ((SC_StrHPointer)Key == StrHPArr[CaseP->Key]))
		    return CaseP;

		HIndex = (HIndex + HStep) & HMask;
	    }
	}

    } else {
	while ((CaseP = HAP[HIndex])) {
	    if ((KeyType == CaseP->KeyType) &&
		(CaseP->Key == Key) &&
		(CaseP->Id == Id))
		return CaseP;

	    HIndex = (HIndex + HStep) & HMask;
	}
    }

    // Hit an open slot, so we do NOT have it!
    return NULL;
}


void	SC_DictPrint(SC_VMPointer VMP, SC_DictVPointer DictP, Int16 Alone, Uns8 Lead)
{
    Uns32		Index;
    SC_PairHPointer	PairP;
    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;
    double		ProbeAverage;

    Index = 1;
    if (Alone) OutPFP("\n\n");
    OutPFP("%*sFilled Dictionary:  %d of %d\n", Lead, "", DictP->PairCount, DictP->HADP->SlotCount);

    ProbeAverage = (double)DictP->TotProbes / DictP->PairCount;
    VMP->OutPrintFP("%*sTotal Probes:%'ld (Avg:%f)\n", Lead, "", DictP->TotProbes, ProbeAverage);

    OutPFP("%*s", Lead, "");
    while (++Index < DictP->HADP->SlotCount) {
	PairP = (SC_PairHPointer)DictP->HADP->Data[Index];
	OutPFP((PairP) ? "* " : "- ");

	if ((Index % 25) == 0)
	    OutPFP("\n%*s", Lead, "");
    }
    OutPFP("\n");
}

void	SC_DictPairPrint(SC_VMPointer VMP, SC_DictVPointer DictP, char Lead)
{
    #define	_STRLEN		48

    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;
    SC_PairHPointer	PairP = DictP->FirstPairP;
    Uns32		I = 1;
    char		S[_STRLEN];

    while (PairP) {
        OutPFP("%*sPair (Ref:%d) %04d Key:", Lead, "", PairP->RefCount, I);
	if (PairP->KeyType == SC_VAL_INT)
	    OutPFP("%d Val:", *(Int64 *)&PairP->KeyP);
	else if (PairP->KeyType == SC_VAL_FLT)
	    OutPFP("%f Val:", *(double *)&PairP->KeyP);
	else {
	    // Obviously a Ptr!
	    if (PairP->KeyP->Type == SC_ENTRY_STR) {
		SC_StrStuffStr(S, (*(SC_StrHPointer)PairP->KeyP).StrDataP->Data, _STRLEN);
		OutPFP("\"%s\" Val:", S);
	    } else {
		SC_SlabPrintPtr(VMP, PairP->KeyP, "Ptr:", ' ');
		OutPFP("Val:");
	    }
	}

	switch (PairP->ValType) {
	    case SC_VAL_INT:
		OutPFP("%d\n", *(Int64 *)&PairP->ValueP);
		break;
		
	    case SC_VAL_FLT:
		OutPFP("%f\n", *(double *)&PairP->ValueP);
		break;

	    case SC_VAL_PTR:
		if (PairP->ValueP->Type == SC_ENTRY_STR) {
		    SC_StrStuffStr(S, (*(SC_StrHPointer)PairP->ValueP).StrDataP->Data, _STRLEN);
		    OutPFP("\"%s\"\n", S);
		} else
		    SC_SlabPrintPtr(VMP, PairP->ValueP, "Ptr:", '\n');
		break;

	    case SC_VAL_PRV:
		SC_SlabPrintPtr(VMP, PairP->ValueP, "Ptr:", '\n');
		break;
		
	    case SC_VAL_CASE:
		OutPFP("[Case: Id:%d Offset:0x%08x]\n", (*(SC_CaseHPointer)PairP).Id, (*(SC_CaseHPointer)PairP).Value);
		break;
		
	    case SC_VAL_FIDX:
		OutPFP("[CFunc Id:%d (0x%016x)]\n", (Int64)PairP->ValueP, VMP->CFPADP->Data[(Int64)PairP->ValueP]);
		break;

	    case SC_VAL_IIDX:
		OutPFP("[IFunc Id:%d \"%s\"]\n", (Int64)PairP->ValueP, SC_IFID_Name[(Int64)PairP->ValueP]);
		break;
	    
	    case SC_VAL_GIDX:
		OutPFP("[Glob Id:%d]\n", (Int64)PairP->ValueP);
		break;
	    
	    case SC_VAL_PIDX:
		OutPFP("[Pkg Id:%d]\n", (Int64)PairP->ValueP);
		break;

	    default:
		OutPFP("Unknown/Error\n");
	}

	PairP = PairP->NextPairP;
	I += 1;
    }

    #undef _STRLEN
}


#ifdef DEBUG

    // StrP should be a WHOLE BUFFER... many lines, each representing a test
    // string.  (Move the GAP out of the way if from sc_Emacs!!)

    void	SC_DEBUG_DictStressTest(void* SMP, char *StrP, Uns32 Len)
    {
	SC_VMPointer	VMP = (SC_VMPointer)SMP;
	char		*P, *EndP, *LStartP;
	Uns64		Line;
	SC_StrHPointer	StrHP;
	SC_PairHPointer	PairP, RevPairP;
	SC_DictVPointer	DictP, RevDictP;
	Uns64		PairCount;

	printf("\nStr Dict Test\n");

	DictP = SC_DictCreateNewDict(VMP, 0);
	PairCount = DictP->PairCount;
	printf("  Dict: Using %d of %d slots\n", (Uns32)PairCount, DictP->HADP->SlotCount);

	P = StrP;
	EndP = StrP + Len;

	LStartP = NULL;
	Line = 0;
	while (P < EndP) {
	    if (*P == '\n') {
		if (LStartP) {
		    Line += 1;

		    // Hash the string first... comes in with RefCount of +1.
		    StrHP = SC_StrGetEntry(VMP, LStartP, P - LStartP);

		    // Create a Pair for it!  Value is Int, Key is Ptr.
		    PairP = SC_BigEntryNew(VMP);
		    PairP->KeyType = SC_VAL_PTR;
		    PairP->ValType = SC_VAL_INT;
		    PairP->KeyP = (SC_VarVPointer)StrHP;
		    PairP->ValueP = (SC_VarVPointer)Line;
		    SC_DictStashNewPair(VMP, DictP, PairP);
		    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);	// Dict will also +1, so -1 now!

		}
		LStartP = NULL;
	    } else if ((*P != ' ') && (*P != '\t')) {
		if (LStartP == 0)
		    LStartP = P;
	    }

	    P += 1;
	}

	PairCount = DictP->PairCount;
	printf("  Dict: Using %d of %d slots\n", (Uns32)PairCount, DictP->HADP->SlotCount);
	SC_DictPrint(VMP, DictP, 1, 11);

	// Now create a reverse dict

	RevDictP = SC_DictCreateNewDict(VMP, 0);
	PairCount = RevDictP->PairCount;
	printf("  Creating reverse Dict\n");
	printf("  Dict: Using %d of %d slots\n", (Uns32)PairCount, RevDictP->HADP->SlotCount);

	P = StrP;
	EndP = StrP + Len;

	LStartP = NULL;
	Line = 0;
	while (P < EndP) {
	    if (*P == '\n') {
		if (LStartP) {
		    Line += 1;

		    // Get the string first... comes in RefCount +1
		    StrHP = SC_StrGetEntry(VMP, LStartP, P - LStartP);

		    // Create a Pair for it!  Value is Ptr, Key is Int!
		    PairP = SC_BigEntryNew(VMP);
		    PairP->KeyType = SC_VAL_INT;
		    PairP->ValType = SC_VAL_PTR;
		    PairP->KeyP = (SC_VarVPointer)Line;
		    PairP->ValueP = (SC_VarVPointer)StrHP;
		    SC_DictStashNewPair(VMP, RevDictP, PairP);
		    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);	// Dict will +1, so -1 now
		}
		LStartP = NULL;
	    } else if ((*P != ' ') && (*P != '\t')) {
		if (LStartP == 0)
		    LStartP = P;
	    }

	    P += 1;
	}

	PairCount = RevDictP->PairCount;
	printf("  Dict: Using %d of %d slots\n", (Uns32)PairCount, RevDictP->HADP->SlotCount);
	SC_DictPrint(VMP, RevDictP, 1, 10);

	// Go though and check the two dicts against one another!
	P = StrP;
	EndP = StrP + Len;

	printf("  Checking Dict against RevDict!\n");
	LStartP = NULL;
	Line = 0;
	while (P < EndP) {
	    if (*P == '\n') {
		if (LStartP) {
		    Line += 1;

		    // Get the String first... again RefCount +1
		    StrHP = SC_StrGetEntry(VMP, LStartP, P - LStartP);

		    PairP = SC_DictGetPair(DictP, (SC_VarVPointer)StrHP, SC_VAL_PTR);
		    RevPairP = SC_DictGetPair(RevDictP, (SC_VarVPointer)PairP->ValueP, SC_VAL_INT);
		    if ((Uns64)RevPairP->ValueP == (Uns64)StrHP)
			printf("*");
		    if ((Line % 100) == 0)
			printf("\n");
		    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);	// Done, -1
		}
		LStartP = NULL;
	    } else if ((*P != ' ') && (*P != '\t')) {
		if (LStartP == 0)
		    LStartP = P;
	    }

	    P += 1;
	}


    }

#endif


// ********************************************************************************
// EXT ????

void			SC_ExtDeRef(SC_VMPointer VMP, SC_VarVPointer EP)
{

}

// ********************************************************************************
// ********************************************************************************
// ********************************************************************************
// READING (Reading SC Source Code and Tokenizing)
//
// UTF-8 chars are allowed in comments and strings.  Therefore, they can be part
// of 'field' names entered in a Dict, so can end up in "Dict.Field" style
// variable names!


// SC_ReadLineComment reads to the end of the line.
//
// Only gets called if "//" was encountered.
// Data starts at  '//' and ends after /n.
// NextStartP can be CGP->ScriptEndP;
//
// Will not signal error... stops quietly if CGP->ScriptEndP

Int16		SC_ReadLineComment(SC_CGPointer CGP, SC_TokenPointer TP)
{
    char	*SP = CGP->ReadStartP + 2;	// Skip initial "//"

    while (SP < CGP->ScriptEndP)
	if (*SP++ == '\n')
	    break;

    // Assumes fresh clean TP, all fields already 0.
    TP->Type = SC_WHITESPACE_TOKEN;
    TP->Id = SC_LCOMMENTWS_ID,
    TP->ReadPos = CGP->ReadStartP - CGP->ScriptStartP;
    TP->ReadLen = SP - CGP->ReadStartP;

    CGP->ReadStartP = SP;
    return 1;
}


// SC_ReadBlockComment reads to the end of the comment.
//
// Only gets called if '/*' was encountered.
// Data should start at /* and end after */.
// NextStartP can be CGP->EndP;
//
// Returns 1 if the comment was properly terminated, 0 otherwise.

Int16		SC_ReadBlockComment(SC_CGPointer CGP, SC_TokenPointer TP)
{
    char	*SP = CGP->ReadStartP + 2;	// Skip initial "/*"
    char	C;
    char	LastC;
    Int16	GoodEnd = 0;

    C = 0;
    while (SP < CGP->ScriptEndP) {
	LastC = C, C = *SP++;

	if ((C == '/') && (LastC == '*')) {
	    GoodEnd = 1;
	    break;
	}
    }

    // Assumes fresh clean TP, all fields already 0.
    TP->Type = SC_WHITESPACE_TOKEN;
    TP->Id = SC_BCOMMENTWS_ID;
    TP->ReadPos = CGP->ReadStartP - CGP->ScriptStartP;
    TP->ReadLen = SP - CGP->ReadStartP;

    CGP->ReadStartP = SP;
    if (! GoodEnd) {
	CGP->ErrorStrP = SC_STR_ErrorBlockCommentClose;
	return 0;
    }
    return 1;
}

// SC_ReadWhiteSpace reads over the whitespace *AND* any comments.
//
// Only gets called if ' ', '\t', or '\n' was encountered.
// Data starts at the whitespace and ends at next (non-commented) char.
// NextStartP can be CGP->ScriptEndP;
//
// Returns 0 only if hits a block comment that was not properly closed.

Int16		SC_ReadWhiteSpace(SC_CGPointer CGP, SC_TokenPointer TP)
{
    char	*StartP = CGP->ReadStartP;
    char	*SP = StartP;
    char	C;

    while (SP < CGP->ScriptEndP) {
	C = *SP;

	if ((C == ' ') || (C == '\t') || (C == '\n')) {
	    SP++; continue;
	} else if ((C == '/') && (SP + 1 < CGP->ScriptEndP)) {
	    C = *(SP + 1);
	    if (C == '/') {
		// Read line comment
		CGP->ReadStartP = SP;
		    SC_ReadLineComment(CGP, TP);
		SP = CGP->ReadStartP;
		continue;
		
	    } else if (C == '*') {
		// Read block comment
		CGP->ReadStartP = SP;
		if (! SC_ReadBlockComment(CGP, TP))
		    return 0;
		SP = CGP->ReadStartP;
		continue;
	    }
	}
	break;
    }

    // Assumes fresh clean TP, all fields already 0,
    TP->Type = SC_WHITESPACE_TOKEN;
    TP->Id = SC_BLANKWS_ID;
    TP->ReadPos = StartP - CGP->ScriptStartP;
    TP->ReadLen = SP - StartP;

    CGP->ReadStartP = SP;
    return 1;
}


// SC_ReadLongString reads to the end of the long string.
// LongStrings do *NOT* process any inline directives, /t, /n, /x4a etc.
//
// Long strings begins with "|>", allowing for 0 to 8 '-' between the bar and
// bracket.  It terminates with a mirror image ("<--|") delimiter.
//
// If the first char is a '/n', it is ignored.
// if the last char is a '/n', it is ignored.
//
// Returns 0 if there was an error, 1 otherwise.
// Will call the error CB if there is one.
// Will return 0 *QUIETLY* if it cannot assemble the beginning delimiter, as
// this indicates the function SHOULD NOT have been called!

Int16		SC_ReadLongString(SC_CGPointer CGP, SC_TokenPointer TP)
{
    char	*SP = CGP->ReadStartP + 1;	// Start after starting '|'.
    char	C;
    Uns16	OpenDashLen = 0;	// Dashes in Delim
    Uns16	CloseDashLen;
    Int16	Closing;
    char *	StartP;

    // Get the opening delimiter first!
    while (SP < CGP->ScriptEndP) {
	C = *SP++;
	if (C == '-')
	    OpenDashLen++;
	else if (C == '>')
	    break;
	else
	    return 0;			// Cannot form Delimiter!

       if (OpenDashLen > 8) {
	   CGP->ErrorStrP = SC_STR_ErrorLongStringOpen;
	   return 0;
       }
    }
    if (SP == CGP->ScriptEndP)
	goto RanOut;

    if (*SP == '\n') SP++;	    // Skip the very first '/n'!
    StartP = SP;

    // Now read through the data...looking for the closing delimiter!
    Closing = 0;
    while (SP < CGP->ScriptEndP) {
	C = *SP++;

	if (Closing) {
	    if (C == '-')
		CloseDashLen++;
	    else if ((C == '|') && (CloseDashLen == OpenDashLen))
		goto ClosedIt;
	    else
		Closing = 0;
	} else // Not Closing...
	    if (C == '<')
		Closing = 1, CloseDashLen = 0;
    }

RanOut:				// Must have fallen off the end
    CGP->ErrorStrP = SC_STR_ErrorLongStringClose;
    return 0; 

ClosedIt:
    CGP->ReadStartP = CGP->LastEndP = SP;

    // Assumes fresh clean TP, all fields already 0.
    TP->Type = SC_STRING_TOKEN;
    TP->ReadPos = StartP - CGP->ScriptStartP;

    SP -= 2 + OpenDashLen;	// Back off delimiter
    if (*(SP-1) == '\n') SP--;	// Back off final /n    
    TP->ReadLen = SP - StartP;

    return 1;
}


// SC_ReadShortString parses the raw string.
// It may have \xyz (escape sequences) that will need to be processed!
//
// The string will start and end with the same Delimiter, which can be
// single or double quotes.
//
// Will ignore \" or \' inside the string, not treated as delimiters.
// But \\" or \\' is a delimiter as the first \ negates the second \.

Int16		SC_ReadShortString(SC_CGPointer CGP, SC_TokenPointer TP)
{
    char	*SP = CGP->ReadStartP;
    char	C, LastC;
    char	DC;			// Delimiter Char
    Int16	HasEscape;
    char *	StartP;

    // Get opening delimiter first

    DC = *SP++;
    if (SP == CGP->ScriptEndP) goto RanOut;

    // Now read through the data... looking for the close.
    StartP = SP;
    HasEscape = 0;
    LastC = 0x00;
    while (SP < CGP->ScriptEndP) {
	C = *SP++;

	if (LastC == '\\') C = 0x00;		// LastC negates this one

	if (C == DC)
	    goto ClosedIt;
	else if (C == '\\')
	    HasEscape = 1;

	LastC = C;
    }

RanOut:				// Fell off the end!
    CGP->ErrorStrP = SC_STR_ErrorStringClose;
    return 0;

ClosedIt:
    CGP->ReadStartP = CGP->LastEndP = SP;

    // Assumes fresh clean TP, all fields already 0.
    TP->Type = SC_STRING_TOKEN;
    if (HasEscape) TP->Flags = SC_TOKEN_PROCSTR_FLAG;
    TP->ReadPos = StartP - CGP->ScriptStartP;
    TP->ReadLen = (SP - 1) - StartP;
    return 1;
}


// SC_ReadProcessShortString will go through the Data string and process all
// escape sequences, turning them into real in-line chars.
//
// It will malloc a 'copy' buffer and write the processed ShortString to it.
//
// Escape chars:
//
//	\' or \"
//	\\	Backslash
//	\n	Newline
//	\t	Tab
//
//	\z	Skip whitespace
//	\#	No-op separator if a text digit has to follow a char spec!
//
//	\ddd	Decimal:  1 or more decimal digits for char spec
//	\[ddd]		  1 or more decimal digits in brackets
//
//	\xfff	Hex:	  1 or more hex digits (typically 2)
//	\[xfff] 	  1 or more hex digits in brackets
//
//	\okkk	Octal:	  1 or more octal digits (typically 3)
//	\[okkk]		  1 or more octal digits in brackets
//
//	Decimal, Hex, and Octal char specification can go all the way
//	up to 1114111 (0x0010FFFF) to specify Unicode (UTF-8) chars.
//
// All escapes will result in a SHORTER final string, so they can be processsed
// in place.  Even the UTF-8 specs can only generate output of 4 bytes while the
// escape seq itself is 5 bytes or more.
//
// NOTE:	*MUST* Free AllocP down the line!

    Int16		SC_AuxRPSSDecChar(char *SP, char *EndP, Uns32 *ResP)
    {
	char	C;
	Int16	Val, Len;

	// Called because first char is a digit!  Don't check.
	C = *SP++, Val = C - '0', Len = 1;
	while (SP < EndP) {
	    C = *SP;
	    if ((C >= '0') && (C <= '9')) {
		Val *= 10, Val += (C - '0');
		Len++, SP++;
		if (Val > 0x10FFFF) return 0;		// Biggest UTF-8
	    } else
		break;
	}

	*ResP = Val;
	return Len;
    }

    Int16		SC_AuxRPSSHexChar(char *SP, char *EndP, Uns32 *ResP)
    {
	char	C, Base;
	Int16	Val, Len;

	// First char is x or X, next should be Hex digits.
	SP++, Val = 0, Len = 1;
	while (SP < EndP) {
	    C = *SP;
	    if ((C >= '0') && (C <= '9')) Base = '0';
	    else if ((C >= 'a') && (C <= 'f')) Base = 'a' - 10;
	    else if ((C >= 'A') && (C <= 'F')) Base = 'A' - 10;
	    else break;

	    Val <<= 4; Val += C - Base;
	    Len++, SP++;
	    if (Val > 0x0010FFFF) return 0;
	}

	*ResP = Val;
	return (Len == 1) ? 0 : Len;	// No digits?
    }

    Int16		SC_AuxRPSSOctChar(char *SP, char *EndP, Uns32 *ResP)
    {
	char	C;
	Int16	Val, Len;

	// First char is o or O, next should be octal digits.
	SP++, Val = 0, Len = 1;
	while (SP < EndP) {
	    C = *SP;
	    if ((C < '0') || (C > '7')) break;

	    Val <<= 3; Val += C - '0';
	    Len++, SP++;
	    if (Val > 0x0010FFFF) return 0;
	}

	*ResP = Val;
	return (Len == 1) ? 0 : Len;	// No digits?
    }


    Int16		SC_AuxRPSSCharSpec(char *SP, char *EndP, Uns32 *ResP)
    {
	char	C;
	Int16	L;
	
	// First char is '['... skip it.
	if (SP + 2 >= EndP) return 0;
	C = *(++SP);

	if ((C >= '0') && (C <= '9'))
	    L = SC_AuxRPSSDecChar(SP, EndP, ResP);
	else if ((C == 'x') || (C == 'X'))
	    L = SC_AuxRPSSHexChar(SP, EndP, ResP);
	else if ((C == 'o') || (C == 'O'))
	    L = SC_AuxRPSSOctChar(SP, EndP, ResP);

	if ((L == 0) || ((SP += L) >= EndP) || (*SP != ']'))
	    return 0;

	return L + 2;
    }

// SC_ReadProcessShortString will PROCESS the short string, turning the escape
// sequences into real chars!
//
// Function **MALLOCS** memory and stashes in TP->Data.
// Caller *MUST* free.
//
// TP->ReadLen will get LenUsed... cannot be bigger than original string length.
// Will usually have wasted space at the end of the block, as Escape sequences
// make output string shorter, not longer!

Int16		SC_ReadProcessShortString(SC_CGPointer CGP, SC_TokenPointer TP)
{
    char	*SP = CGP->ScriptStartP + TP->ReadPos;
    char	*EndP = SP + TP->ReadLen;
    char	*DP;
    char	C, LastC;
    Int16	L;
    Uns32	UC;

    TP->Data = 0LL;
    if (! (DP = malloc(TP->ReadLen)))
	G_SETEXCEPTION("Malloc string process block failed", 0);
    TP->Data = (Int64)DP;

    LastC = 0;
    while (SP < EndP) {
	C = *SP, L = ED_UtilGetUTF8Len(C);

	if (LastC != '\\') {
	    if (C == '\\')
		SP++;		// Just skip it, LastC will get it and initiate escape!
	    else
		while (L--) *DP++ = *SP++;	// Otherwise copy to AllocP
	} else {

	    // In escape sequence!!
	    // Col already includes FIRST char, advance for subsequent ones.
	    // But SP is still at FIRST char, advance for all!

	    if ((C == '\'') || (C == '"'))
		*DP++ = C, SP++;
	    else if ((C == 'n') || (C == 'N'))
		*DP++ = '\n', SP++;
	    else if ((C == 't') || (C == 'T'))
		*DP++ = '\t', SP++;
	    else if (C == '#')			// No-op if number after a char spec!
		SP++;
	    else if ((C == 'z') || (C == 'Z')) {
		SP++;				// Skip whitespace after z,
		while (SP < EndP) {
		    C = *SP;
		    if ((C == ' ') || (C == '\t') || (C == '\n')) SP++;
		    else {C = 0; break;}	// Make C harmless for LastC, will get it again.
		}
		
	    } else if (C == '[') {
		if (! (L = SC_AuxRPSSCharSpec(SP, EndP, &UC))) {
		    CGP->ErrorStrP = SC_STR_ErrorStringCharSpec;
		    CGP->ReadStartP = SP-1;
		    return 0;
		}
		DP += SC_UtilWriteUniChar(UC, DP);
		SP += L;

	    } else if ((C >= '0') && (C <= '9')) {
		if (! (L = SC_AuxRPSSDecChar(SP, EndP, &UC))) {
		    CGP->ErrorStrP = SC_STR_ErrorStringDecChar;
		    CGP->ReadStartP = SP-1;
		    return 0;
		}
		DP += SC_UtilWriteUniChar(UC, DP);
		SP += L;

	    } else if ((C == 'x') || (C == 'X')) {
		// Escape for Hex char
		if (! (L = SC_AuxRPSSHexChar(SP, EndP, &UC))) {
		    CGP->ErrorStrP = SC_STR_ErrorStringHexChar;
		    CGP->ReadStartP = SP-1;
		    return 0;
	        }
		DP += SC_UtilWriteUniChar(UC, DP);
		SP += L;

	    } else if ((C == 'o') || (C == 'O')) {
		// Escape for Octal char
		if (! (L = SC_AuxRPSSOctChar(SP, EndP, &UC))) {
		    CGP->ErrorStrP = SC_STR_ErrorStringOctChar;
		    CGP->ReadStartP = SP-1;
		    return 0;
		}
		DP += SC_UtilWriteUniChar(UC, DP);
		SP += L;

	    } else {
		// Bad escape!
		CGP->ErrorStrP = SC_STR_ErrorStringBadEscape;
		CGP->ReadStartP = SP-1;
		return 0;
	    }
	}
	
	LastC = C;
    }

    TP->ReadLen = DP - (char *)TP->Data;
    return 1;
}

// SC_ReadNumb will read an Int64 or double into NumbP, sets
// SC_FLOATNUMB_ID flag if so.
//
// Function reads integers itself, but defers to C library strtod
// if it hits a decimal or exponent!

Int16		SC_ReadNumber(SC_CGPointer CGP, SC_TokenPointer TP)
{
    char	*SP = CGP->ReadStartP;
    Uns32	Len = CGP->ScriptEndP - SP;
    Int16	Pos = 0;
    char	C, N;
    Int32	Base = 10;
    Int64	Val = 0LL;
    double	FVal;
    char *	EndP;

    // Attempt to read Int first

    while (Pos < Len) {
	C = SP[Pos++];			// Pos is effectively 1-based!
	
	if (C == '.')
	   goto ReadFloat;
	if ((C == 'p') || (C == 'P')) {
	    if (Base == 16) goto ReadFloat;
	    else break;
	}
	if ((C == 'e') || (C == 'E')) {
	    if (Base == 10) goto ReadFloat;
	    else break;
	}

	if ((C == 'x') || (C == 'X')) {
	    if ((Pos == 2) && (Base == 8))
		Base = 16;		// continue
	    else break;
	} else if (C == '0') {
	    if (Pos == 1) Base = 8;
	    else
		Val *= Base;		// continue
	} else if ((C > '0') && (C <= '9')) {
	    N = C - '0';
	    if (N >= Base) goto ErrorChar;
	    Val = (Val * Base) + N;	// continue

	} else if ((C >= 'a') && (C <= 'f')) {
	    if (Base < 16) break;
	    N = C + 10 - 'a';
	    Val = (Val * Base) + N;	// continue

	} else if ((C >= 'A') && (C <= 'F')) {
	    if (Base < 16) break;
	    N = C + 10 - 'A';
	    Val = (Val * Base) + N;	// continue
	} else
	    break;
    }

    // Broke out... Have Int!
    if ((Base == 16) && (Pos < 4)) goto ErrorChar;	// 0xN at least!

    // Assumes fresh clean TP, all fields already 0.
    TP->Type = SC_NUMBER_TOKEN;
    TP->Id = SC_INTNUMB_ID;
    TP->ReadPos = CGP->ReadStartP - CGP->ScriptStartP;
    TP->ReadLen = Pos - 1;
    TP->Data = Val;

    CGP->ReadStartP += TP->ReadLen;
    CGP->LastEndP = CGP->ReadStartP;
    return 1;
    
ReadFloat:

    TP->Type = SC_NUMBER_TOKEN;
    TP->Id = SC_FLOATNUMB_ID;
    errno = 0;
	FVal = strtod(SP, &EndP);
	TP->Data = *(Uns64 *)&FVal;
    // Ignoring errno because ERANGE is the only error
    // and FVal already reflects it.

    // Assumes fresh clean TP, all fields already 0.
    TP->ReadPos = CGP->ReadStartP - CGP->ScriptStartP;
    TP->ReadLen = EndP - CGP->ReadStartP;
    
    CGP->ReadStartP += TP->ReadLen;
    CGP->LastEndP = CGP->ReadStartP;
    return 1;

ErrorChar:
    CGP->ErrorStrP = SC_STR_ErrorNumb;
    return 0;
}


// SC_KeywordCodeMapInit creates a mapping array for Keyword codes.  This is
// used for FAST (and case-INsensitive) recognition of keywords when parsing
// identifiers.
//
// NOTE:	ALL KEYWORDS must be 8 bytes or less!

    // Generates an Uns64 Code for the keyword.
    // *MUST* be fed only *LOWERCASE* letters.

    Uns64	SC_AuxKCMI(char * WordP)
    {
	Uns64	Code = 0LL;
	char	*SP = WordP;
	char	*DP = (char *)&Code;
	Int16	L = 0;

	while ((*DP++ = *SP++))
	    if (L++ > 7) G_SETEXCEPTION("Keyword is too long", 0);
	    // Throw exception because this is SOURCE CODE error!

	return Code;
    }


void	SC_ReadKeyCodeMapInit(void)
{
    SC_KEYCODEMAP[SC_NO_KEYCODE] = 0LL;
    SC_KEYCODEMAP[SC_BREAK_KEYCODE] = SC_AuxKCMI("break");
    SC_KEYCODEMAP[SC_CASE_KEYCODE] = SC_AuxKCMI("case");
    SC_KEYCODEMAP[SC_CONT_KEYCODE] = SC_AuxKCMI("continue");
    SC_KEYCODEMAP[SC_DEF_KEYCODE] = SC_AuxKCMI("def");
    SC_KEYCODEMAP[SC_DEFAULT_KEYCODE] = SC_AuxKCMI("default");
    SC_KEYCODEMAP[SC_DO_KEYCODE] = SC_AuxKCMI("do");
    SC_KEYCODEMAP[SC_ELSE_KEYCODE] = SC_AuxKCMI("else");
    SC_KEYCODEMAP[SC_FOR_KEYCODE] = SC_AuxKCMI("for");
    SC_KEYCODEMAP[SC_FROM_KEYCODE] = SC_AuxKCMI("outof");
    SC_KEYCODEMAP[SC_FUNCTION_KEYCODE] = SC_AuxKCMI("function");
    SC_KEYCODEMAP[SC_GOTO_KEYCODE] = SC_AuxKCMI("goto");
    SC_KEYCODEMAP[SC_IF_KEYCODE] = SC_AuxKCMI("if");
    SC_KEYCODEMAP[SC_NULL_KEYCODE] = SC_AuxKCMI("null");
    SC_KEYCODEMAP[SC_RETURN_KEYCODE] = SC_AuxKCMI("return");
    SC_KEYCODEMAP[SC_SWITCH_KEYCODE] = SC_AuxKCMI("switch");
    SC_KEYCODEMAP[SC_VAR_KEYCODE] = SC_AuxKCMI("var");
    SC_KEYCODEMAP[SC_VOID_KEYCODE] = SC_AuxKCMI("void");
    SC_KEYCODEMAP[SC_WHILE_KEYCODE] = SC_AuxKCMI("while");
    SC_KEYCODEMAP[SC_YIELD_KEYCODE] = SC_AuxKCMI("yield");

    // Only seen in Editor! *** IMPERATIVE **** Coordinate with SC_IFID_Name + SC_MainPkgInit.
    SC_KEYCODEMAP[SC_ARRAY_EDCODE] = SC_AuxKCMI("array");
    SC_KEYCODEMAP[SC_DICT_EDCODE] = SC_AuxKCMI("dict");
    SC_KEYCODEMAP[SC_FLT_EDCODE] = SC_AuxKCMI("flt");
    SC_KEYCODEMAP[SC_GETRC_EDCODE] = SC_AuxKCMI("getrc");
    SC_KEYCODEMAP[SC_INT_EDCODE] = SC_AuxKCMI("int");
    SC_KEYCODEMAP[SC_MAPI_EDCODE] = SC_AuxKCMI("mapi");
    SC_KEYCODEMAP[SC_MAPK_EDCODE] = SC_AuxKCMI("mapk");
    SC_KEYCODEMAP[SC_RESUME_EDCODE] = SC_AuxKCMI("resume");
    SC_KEYCODEMAP[SC_RELEASE_EDCODE] = SC_AuxKCMI("release");
    SC_KEYCODEMAP[SC_SUBMIT_EDCODE] = SC_AuxKCMI("submit");
    
    SC_KEYCODEMAP[SC_COUNTOF_EDCODE] = SC_AuxKCMI("countof");
    SC_KEYCODEMAP[SC_SIZEOF_EDCODE] = SC_AuxKCMI("sizeof");
    SC_KEYCODEMAP[SC_TYPEOF_EDCODE] = SC_AuxKCMI("typeof");
    SC_KEYCODEMAP[SC_WRITE_EDCODE] = SC_AuxKCMI("write");
    SC_KEYCODEMAP[SC_WRITELN_EDCODE] = SC_AuxKCMI("writeln");
}


// SC_ReadKeyCodeIdent quickly matches a Code (from an identifier) to a keyword.
// The editor calls this too for colorizing code!

#define	RKCI_CHECK(_KC)		if (KCode == SC_KEYCODEMAP[_KC]) return _KC

    Int16	SC_ReadKeyCodeIdent(Uns64 KCode)
    {
	char	*SP = (char *)&KCode;	// First char!

	switch (*SP) {
	    case 'a': break;
	    case 'b': RKCI_CHECK(SC_BREAK_KEYCODE);
		      break;
	    case 'c': RKCI_CHECK(SC_CASE_KEYCODE);
		      RKCI_CHECK(SC_CONT_KEYCODE);
		      break;
	    case 'd': RKCI_CHECK(SC_DEF_KEYCODE);
		      RKCI_CHECK(SC_DEFAULT_KEYCODE);
		      RKCI_CHECK(SC_DO_KEYCODE);
		      break;
	    case 'e': RKCI_CHECK(SC_ELSE_KEYCODE);
		      break;
	    case 'f': RKCI_CHECK(SC_FOR_KEYCODE);
		      RKCI_CHECK(SC_FUNCTION_KEYCODE);
		      break;
	    case 'g': RKCI_CHECK(SC_GOTO_KEYCODE);
	    case 'h': break;
	    case 'i': RKCI_CHECK(SC_IF_KEYCODE);
	    case 'j':
	    case 'k':
	    case 'l':
	    case 'm': break;
	    case 'n': RKCI_CHECK(SC_NULL_KEYCODE);
		      break;
	    case 'o': RKCI_CHECK(SC_FROM_KEYCODE);		// Changed to "OutOf"!
	    case 'p':
	    case 'q': break;
	    case 'r': RKCI_CHECK(SC_RETURN_KEYCODE);
		      break;
	    case 's': RKCI_CHECK(SC_SWITCH_KEYCODE);
	    case 't':
	    case 'u': break;
	    case 'v': RKCI_CHECK(SC_VAR_KEYCODE);
		      RKCI_CHECK(SC_VOID_KEYCODE);
		      break;
	    case 'w': RKCI_CHECK(SC_WHILE_KEYCODE);
	    case 'x': break;
	    case 'y': RKCI_CHECK(SC_YIELD_KEYCODE);
		      break;
	}
	return SC_NO_KEYCODE;
    }

    // Used only by Editor... Different hilite from regular keywords!
    Int16	SC_ReadEdCodeIdent(Uns64 KCode)
    {
	char	*SP = (char *)&KCode;	// First char!

	switch (*SP) {
	    case 'a': RKCI_CHECK(SC_ARRAY_EDCODE);
	    case 'b':
	    case 'c': break;
	    case 'd': RKCI_CHECK(SC_DICT_EDCODE);
	    case 'e': break;
	    case 'f': RKCI_CHECK(SC_FLT_EDCODE);
		      RKCI_CHECK(SC_FUNCTION_KEYCODE);
		      break;
	    case 'g': RKCI_CHECK(SC_GETRC_EDCODE);
		      RKCI_CHECK(SC_GOTO_KEYCODE);
	    case 'h': break;
	    case 'i': RKCI_CHECK(SC_INT_EDCODE);
	    case 'j':
	    case 'k':
	    case 'l': break;
	    case 'm': RKCI_CHECK(SC_MAPI_EDCODE);
		      RKCI_CHECK(SC_MAPK_EDCODE);
	    case 'n': break;
	    case 'o': RKCI_CHECK(SC_FROM_KEYCODE);	// OutOf!
	    case 'p':
	    case 'q': break;
	    case 'r': RKCI_CHECK(SC_RELEASE_EDCODE);
		      RKCI_CHECK(SC_RESUME_EDCODE);
		      RKCI_CHECK(SC_RETURN_KEYCODE);
		      break;
	    case 's': RKCI_CHECK(SC_SUBMIT_EDCODE);
	    case 't':
	    case 'u':
	    case 'v':
	    case 'w':
	    case 'x': break;
	    case 'y': RKCI_CHECK(SC_YIELD_KEYCODE);
		      break;
	}
	return SC_NO_KEYCODE;
    }

#undef RKCI_CHECK

// SC_ReadIdent is called to read an Ident at ReadStartP.  It assumes the first
// char is NOT a digit, otherwise this would not have been called.
//
// An identifier MAY include numbers and UTF-8 chars.  But these identifiers
// will *NOT* be keywords.
//
// It will set TP->Id if the Ident is a keyword (SC_NO_KEYCODE otherwise).

Int16		SC_ReadIdent(SC_CGPointer CGP, SC_TokenPointer TP)
{
    char	*SP = CGP->ReadStartP;
    char	C;
    Int16	L;
    Uns64	KCode = 0LL;
    char	*CP = (char *)&KCode;
    char	*StartP;

    StartP = SP;
    L = 0;
    while (SP < CGP->ScriptEndP) {
    
	if (L > SC_MAX_IDENT_LEN) {
	    CGP->ErrorStrP = SC_STR_ErrorIdentTooLong;
	    return 0;	
	}

	C = *SP;
	if (('A' <= C) && (C <= 'Z'))
	    C += 'a' - 'A';	// Make C lowercase
	else if ((C == '#') || (C == '_') || (C & 0x80) || ((C >= '0') && (C <= '9')))
	    CP = NULL;		// NOT Keyword, so no KeyCode
	else if ((C < 'a') || (C > 'z'))
	    break;		// Done!

	if (L == 8) CP = NULL;	// Too long to be keyword
	if (CP) *CP++ = C;	// Make KeyCode
	L++, SP++;
    }

    // Assumes fresh clean TP, all fields already 0.
    TP->Type = SC_IDENTIFIER_TOKEN;
    TP->Id = (CP) ? SC_ReadKeyCodeIdent(KCode) : SC_NO_KEYCODE;
    TP->ReadPos = StartP - CGP->ScriptStartP;
    TP->ReadLen = SP - StartP;

    CGP->ReadStartP = CGP->LastEndP = SP;
    return 1;
}

Int16		SC_ReadOperator(SC_CGPointer CGP, SC_TokenPointer TP)
{
    char	*SP = CGP->ReadStartP;
    char	C = *SP;
    char	NC = ' ';	// Look ahead 1 pos
    char	NNC = ' ';	// Look ahead 2 pos
    Int16	Op = SC_NO_ID;
    char	*StartP;

    if (++SP < CGP->ScriptEndP)
	NC = *SP;
    if (++SP < CGP->ScriptEndP)
	NNC = *SP;

    StartP = CGP->ReadStartP;
    switch (C) {
	case '!':	if (NC == '=') Op = (NNC == '=') ? SC_NIDENT_ID : SC_NEQ_ID;
			else Op = SC_LOGNEG_ID;
			break;

	case '%':	Op = (NC == '=') ? SC_MODEQ_ID : SC_MOD_ID; break;
	
	case '&':	if (NC == '=') Op = SC_BITANDEQ_ID;
			else if (NC == '&') Op = SC_LOGAND_ID;
			else Op = SC_BITAND_ID;
			break;
			
	case '(':	Op = SC_POPEN_ID; break;
	case ')':	Op = SC_PCLOSE_ID; break;
	case '*':	Op = (NC == '=') ? SC_MULTEQ_ID : SC_MULT_ID; break;
	case '+':	if (NC == '+') Op = SC_PLUSPLUS_ID;
			else if (NC == '=') Op = SC_PLUSEQ_ID;
			else Op = SC_PLUS_ID;
			break;

	case ',':	Op = SC_COMMA_ID; break;
	case '-':	if (NC == '-') Op = SC_MINMIN_ID;
			else if (NC == '=') Op = SC_MINUSEQ_ID;
			else Op = SC_MINUS_ID;
			break;

	case '.':	Op = SC_DOT_ID; break;
	case '/':	Op = (NC == '=') ? SC_DIVEQ_ID : SC_DIV_ID; break;
	case ':':	Op = SC_COLON_ID; break;
	case ';':	Op = SC_SEMI_ID; break;
	
	case '<':	if (NC == '=') Op = SC_LESSEQ_ID;
			else if (NC == '<') Op = (NNC == '=') ? SC_SHLEQ_ID : SC_SHL_ID;
			else Op = SC_LESS_ID;
			break;

	case '=':	if (NC == '=') Op = (NNC == '=') ? SC_IDENT_ID : SC_EQEQ_ID;
			else if (NC == '&') Op = SC_ALIAS_ID;
			else Op = SC_EQ_ID;
			break;

	case '>':	if (NC == '=') Op = SC_GREATEQ_ID;
			else if (NC == '>') Op = (NNC == '=') ? SC_SHREQ_ID : SC_SHR_ID;
			else Op = SC_GREAT_ID;
			break;

	case '?':	Op = SC_CHOOSE_ID; break;
	case '@':	Op = SC_UNPACK_ID; break;
	case '[':	Op = SC_AOPEN_ID; break;
	case ']':	Op = SC_ACLOSE_ID; break;
	case '^':	Op = (NC == '=') ? SC_BITXOREQ_ID : SC_BITXOR_ID; break;
	case '{':	Op = SC_BEGIN_ID; break;

	case '|':	if (NC == '=') Op = SC_BITOREQ_ID;
			else if (NC == '|') Op = SC_LOGOR_ID;
			else Op = SC_BITOR_ID;
			break;
			
	case '}':	Op = SC_END_ID; break;
	case '~':	Op = SC_BITNEG_ID; break;

	default:	CGP->ErrorStrP = SC_STR_ErrorBadOp;
			return 0;
    }

    // Assumes fresh clean TP, all fields already 0.
    TP->Type = SC_OPERATOR_TOKEN;
    TP->Id = Op;
    TP->ReadPos = StartP - CGP->ScriptStartP;
    TP->ReadLen = (Op >> 6) + 1;

    CGP->ReadStartP += TP->ReadLen;
    CGP->LastEndP = CGP->ReadStartP;
    return 1;
}


void		SC_ReadShowSourceError(SC_CGPointer CGP)
{
    SC_PrintFuncP	OutPFP = CGP->VMP->OutPrintFP;
    char		*SP, *EP;
    Int16		L;

    if (CGP->ErrorArgStrP) {
	// Will not show any of the code, just the error
	OutPFP("\n*********************************************\nERROR --> \"%s\" %s\n---------------------------------------------\n",
		CGP->ErrorArgStrP, CGP->ErrorStrP);
	return;
    }

    // Should not be ON a newline
    SP = CGP->ErrorStartP;
    if (*SP == '\n') SP--, CGP->ErrorStartP--;
    
    // Find the beginning of the Line where the error occurs.
    while (--SP >= CGP->ScriptStartP)
	if (*SP == '\n') break;
    SP++;

    // Find LineColumn (L) where error occurs.
    L = 0, EP = SP;
    while (EP < CGP->ErrorStartP) {
	if (*EP++ == '\t') L += SC_TABSPACES(L);
	else L+= 1;
    }

    // Find end of the error line.
    while (++EP < CGP->ScriptEndP)
	if (*EP == '\n') break;

    // Show the whole line
    OutPFP("\n%.*s\n", EP - SP, SP);

    // Indicate the error location
    OutPFP("%*s\n", L+1, "^");

    // Display the error!
    OutPFP("ERROR--> %s\n------------------------\n", CGP->ErrorStrP);
}

// ********************************************************************************
// ********************************************************************************
// Process (Tokenize, Parse, CodeGen, then Execute!)

// DISPLAY STRUCTURES.... String labels, types, and constants *MUST* match 100% ***
// ************* ORDER IS IMPERATIVE*********Matches SC_BYTE_CODE, etc...

char*  SC_ArgStrArr[] =	{"VVV", "VVK", "VKV", "sss",
			 "VV_", "VK2", "K2V",
			 "V__", "K__",
			 "VI2", "II2", "IK2",
			  "I3_", "K3_",
			  "___", "Nil"};

			typedef enum {
			    SC_ARGVVV, SC_ARGVVK, SC_ARGVKV, SC_ARGsss,
			    SC_ARGVV, SC_ARGVK2, SC_ARGK2V,
			    SC_ARGV, SC_ARGK,
			    SC_ARGVI2, SC_ARGII2, SC_ARGIK2,
			    SC_ARGI3, SC_ARGK3,
			    SC_ARGNO, SC_ARGNIL,
			} SC_ARGNAME;

char *	SC_OpStrArr[] =	{"No-op",
			 "Add__", "Sub__", "Mul__", "Div__", "Mod__", "ShL__", "ShR__",
			 "Mov__", "Pop__", "PutV_", "GetV_", "PopN_", "Pop1_",
			 "Swap_", "Psh__", "PshI_", "PshF_", "PshG_", "PshS_",
			 "Ref__", "RefG_", "RSet_", "VSet_", "ISet_",
			 "Jmp__", "BrT__", "BrF__",
			 "LT___", "LTQ__", "EQ___", "NEQ__", "IDNT_", "NDNT_",
			 "Pos__", "Neg__", "Not__", "BNeg_", "LAnd_", "LOr__", "BAnd_", "BOr__", "BXor_",
			 "Swch_",
			 "FMak_", "FBeg_", "FEnd_", "FSub", "FRet_", "FRes_", "FRel_", "FYld_", "FGet_",
			 "FCal_", "UPFC_", "FSys_", "UPFS_", "UPS__", "UPI__", "UPO__",
			 "ANew_", "AIni_", "DNew_", "DIni_",
			 "IRef_", "ZRef", "IGet_", "IMap", "KMap"};

			typedef enum {
			    SC_OPNO,
			    SC_OPADD, SC_OPSUB, SC_OPMUL, SC_OPDIV, SC_OPMOD, SC_OPSHL, SC_OPSHR,
			    SC_OPMOV, SC_OPPOP, SC_OPPUTV, SC_OPGETV, SC_OPPOPN, SC_OPPOP1,
			    SC_OPSWAP, SC_OPPSH, SC_OPPSHI, SC_OPPSHF, SC_OPPSHG, SC_OPPSHS,
			    SC_OPREF, SC_OPREFG, SC_OPRSET, SC_OPVSET, SC_OPISET,
			    SC_OPJMP, SC_OPBRT, SC_OPBRF,
			    SC_OPLT, SC_OPLTQ, SC_OPEQ, SC_OPNEQ, SC_OPIDNT, SC_OPNDNT,
			    SC_OPPOS, SC_OPNEG, SC_OPNOT, SC_OPBNEG, SC_OPLAND, SC_OPLOR, SC_OPBAND, SC_OPBOR, SC_OPBXOR,
			    SC_OPSWCH,
			    SC_OPFMAK, SC_OPFBEG, SC_OPFEND, SC_OPFSUB, SC_OPFRET, SC_OPFRES, SC_OPFREL, SC_OPFYLD, SC_OPFGET,
			    SC_OPFCALL, SC_OPUFCALL, SC_OPFSYS, SC_OPUFSYS, SC_OPUPS, SC_OPUNPAKI, SC_OPUNPAKO,
			    SC_OPANEW, SC_OPAINI, SC_OPDNEW, SC_OPDINI,
			    SC_OPIREF, SC_OPZREF, SC_OPIGET, SC_OPIMAP, SC_OPKMAP,
			} SC_OPNAME;

typedef struct _SC_OpDispRecord {
    Uns8	OpI;		// Index in SC_OpStrArr
    Uns8	ArgI;		// Index in SC_ArgStrArr
    Uns8	DispI;		// Display Function Index
    Uns8	Len;		// Op Len
} SC_OpDispRecord, *SC_OpDispPointer;

// MUST match DrawArr in SC_RunDisplayCB!
typedef enum {
    SC_DISPNO,
    SC_DISP1V, SC_DISP2V, SC_DISP3V,
    SC_DISPVVK, SC_DISPVK2, SC_DISPVI2, SC_DISPII2,
    SC_DISPK,
    SC_DISPK3, SC_DISPI3,
} SC_DISPFUNC;
    
SC_OpDispRecord	SC_OpDispArr[] = {{SC_OPNO, SC_ARGNO, SC_DISPNO, 1},
				  {SC_OPADD, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPADD, SC_ARGsss, SC_DISPNO, 1},
				  {SC_OPADD, SC_ARGVVK, SC_DISPVVK, 4},

				  {SC_OPSUB, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPSUB, SC_ARGsss, SC_DISPNO, 1},
				  {SC_OPSUB, SC_ARGVKV, SC_DISPVVK, 4},	// VKV stores as VVK in Op!

				  {SC_OPMUL, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPMUL, SC_ARGsss, SC_DISPNO, 1},
				  {SC_OPMUL, SC_ARGVVK, SC_DISPVVK, 4},

				  {SC_OPDIV, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPDIV, SC_ARGsss, SC_DISPNO, 1},
				  {SC_OPDIV, SC_ARGVVK, SC_DISPVVK, 4},

				  {SC_OPMOD, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPMOD, SC_ARGsss, SC_DISPNO, 1},
				  {SC_OPMOD, SC_ARGVVK, SC_DISPVVK, 4},

				  {SC_OPSHL, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPSHL, SC_ARGsss, SC_DISPNO, 1},
				  {SC_OPSHL, SC_ARGVVK, SC_DISPVVK, 4},

				  {SC_OPSHR, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPSHR, SC_ARGsss, SC_DISPNO, 1},
				  {SC_OPSHR, SC_ARGVVK, SC_DISPVVK, 4},

				  {SC_OPMOV, SC_ARGVV, SC_DISP2V, 3},
				  {SC_OPMOV, SC_ARGVK2, SC_DISPVK2, 4},

				  {SC_OPPOP, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPPUTV, SC_ARGNO, SC_DISPNO, 1},
				  {SC_OPGETV, SC_ARGNO, SC_DISPNO, 1},
				  {SC_OPPOPN, SC_ARGK, SC_DISPK, 2},
				  {SC_OPPOP1, SC_ARGNO, SC_DISPNO, 1},

				  {SC_OPSWAP, SC_ARGNO, SC_DISPNO, 1},
				  {SC_OPPSH, SC_ARGNO, SC_DISPNO, 1},
				  {SC_OPPSH, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPPSH, SC_ARGK, SC_DISPK, 2},
				  {SC_OPPSH, SC_ARGK3, SC_DISPK3, 4},
				  {SC_OPPSHI, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPPSHI, SC_ARGI3, SC_DISPI3, 4},
				  {SC_OPPSHF, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPPSHF, SC_ARGI3, SC_DISPI3, 4},
				  {SC_OPPSHG, SC_ARGI3, SC_DISPI3, 4},
				  {SC_OPPSHS, SC_ARGI3, SC_DISPI3, 4},

				  {SC_OPREF, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPREFG, SC_ARGI3, SC_DISPI3, 4},
				  {SC_OPRSET, SC_ARGVI2, SC_DISPVI2, 4},
				  {SC_OPVSET, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPISET, SC_ARGVVV, SC_DISP3V, 4},

				  {SC_OPJMP, SC_ARGK3, SC_DISPK3, 4},
				  {SC_OPBRT, SC_ARGK3, SC_DISPK3, 4},
				  {SC_OPBRF, SC_ARGK3, SC_DISPK3, 4},

				  {SC_OPLT, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPLT, SC_ARGsss, SC_DISPNO, 1},
				  {SC_OPLT, SC_ARGVK2, SC_DISPVK2, 4},
				  {SC_OPLT, SC_ARGK2V, SC_DISPVK2, 4},	// K2V is stored as VK2 in Op!

				  {SC_OPLTQ, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPLTQ, SC_ARGsss, SC_DISPNO, 1},
				  {SC_OPLTQ, SC_ARGVK2, SC_DISPVK2, 4},
				  {SC_OPLTQ, SC_ARGK2V, SC_DISPVK2, 4},	// K2V is stored as VK2 in Op!

				  {SC_OPEQ, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPEQ, SC_ARGsss, SC_DISPNO, 1},
				  {SC_OPEQ, SC_ARGVK2, SC_DISPVK2, 4},

				  {SC_OPNEQ, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPNEQ, SC_ARGsss, SC_DISPNO, 1},
				  {SC_OPNEQ, SC_ARGVK2, SC_DISPVK2, 4},
				  {SC_OPIDNT, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPNDNT, SC_ARGVVV, SC_DISP3V, 4},

				  {SC_OPPOS, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPPOS, SC_ARGVV, SC_DISP2V, 3},

				  {SC_OPNEG, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPNEG, SC_ARGVV, SC_DISP2V, 3},

				  {SC_OPNOT, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPNOT, SC_ARGVV, SC_DISP2V, 3},

				  {SC_OPBNEG, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPBNEG, SC_ARGVV, SC_DISP2V, 3},

				  {SC_OPBAND, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPBAND, SC_ARGsss, SC_DISPNO, 1},

				  {SC_OPBOR, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPBOR, SC_ARGsss, SC_DISPNO, 1},

				  {SC_OPBXOR, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPBXOR, SC_ARGsss, SC_DISPNO, 1},

				  {SC_OPSWCH, SC_ARGVI2, SC_DISPVI2, 4},
				  {SC_OPFMAK, SC_ARGVI2, SC_DISPVI2, 4},
				  {SC_OPFBEG, SC_ARGNO, SC_DISPNO, 1},
				  {SC_OPFEND, SC_ARGNO, SC_DISPNO, 1},
				  {SC_OPFSUB, SC_ARGNO, SC_DISPNO, 1},
				  {SC_OPFRET, SC_ARGNO, SC_DISPNO, 1},
				  {SC_OPFRET, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPFRET, SC_ARGK, SC_DISPK, 2},
				  
				  {SC_OPFRET, SC_ARGVV, SC_DISP2V, 3},
				  {SC_OPFYLD, SC_ARGVV, SC_DISP2V, 3},
				  {SC_OPFRES, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPFREL, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPFGET, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPFCALL, SC_ARGVK2, SC_DISPVK2, 4},
				  {SC_OPUFCALL, SC_ARGVK2, SC_DISPVK2, 4},
				  {SC_OPFSYS, SC_ARGIK2, SC_DISPII2, 4},
				  {SC_OPUFSYS, SC_ARGIK2, SC_DISPII2, 4},
				  {SC_OPUPS, SC_ARGNO, SC_DISPNO, 1},
				  {SC_OPUNPAKI, SC_ARGNO, SC_DISPNO, 1},
				  {SC_OPUNPAKO, SC_ARGNO, SC_DISPNO, 1},

				  {SC_OPANEW, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPAINI, SC_ARGV, SC_DISP1V, 2},

				  {SC_OPDNEW, SC_ARGV, SC_DISP1V, 2},
				  {SC_OPDINI, SC_ARGNO, SC_DISPNO, 1},

				  {SC_OPIREF, SC_ARGVV, SC_DISP2V, 3},
				  {SC_OPIREF, SC_ARGVI2, SC_DISPVI2, 4},
				  {SC_OPZREF, SC_ARGVV, SC_DISP2V, 3},
				  {SC_OPZREF, SC_ARGVI2, SC_DISPVI2, 4},
				  {SC_OPIGET, SC_ARGVVV, SC_DISP3V, 4},
				  {SC_OPIGET, SC_ARGVVK, SC_DISPVVK, 4},
				  {SC_OPIMAP, SC_ARGNO, SC_DISPNO, 1},
				  {SC_OPKMAP, SC_ARGNO, SC_DISPNO, 1}};



#ifdef DEBUG
    char *	SC_ParseDebugTagArr[SC_TOKEN_COUNT] = {"NONE", "SPCE", "STRI", "NUMB", "OPRT", "IDNT"};

    void	SC_ParseDebugInit(SC_VMPointer VMP)
    {
	// VMP->OutPrintFP = (SC_PrintFuncP)printf;		// Redirect to C Stdout for debugging

    }

    void	SC_ParseDebugPrint(SC_CGPointer CGP, SC_TokenPointer TP)
    {
	if (SC_DEBUG_ShowParseToken) {
	    CGP->VMP->OutPrintFP("Parsing: [%x] %3d (%s):%3d  [%.*s]\n", (Uns64)TP,
			       TP->Type, SC_ParseDebugTagArr[TP->Type],
			       TP->Id, TP->ReadLen, CGP->ScriptStartP + TP->ReadPos);
	}
    }

    void	SC_ParseDebugEnd(SC_VMPointer VMP)
    {
	if (SC_DEBUG_ShowParseToken) {
	    VMP->OutPrintFP("\nParsing Completed\n");
	    VMP->OutPrintFP("-------------------------------------------------\n");
	}
    }
#endif


// ********************************************************************************
// Tokens

SC_TokenPointer		SC_TokenNew(SC_VMPointer VMP)
{
    SC_SlabPointer	SlabP = VMP->TokenSP;
    SC_TokenPointer	TokenP;
    Uns64		Max, Count;

    TokenP = (SC_TokenPointer)SC_SlabAllocEntry(SlabP, sizeof(SC_TokenRecord));
    if (TokenP == NULL) {
	// Need a new slab... calculate how big!
	Count = VMP->TokenSlotAlloc + SC_TOKENEXTRASLOTCOUNT;
	Max = SC_MAXSLABDATASIZE / sizeof(SC_TokenRecord);
	if (Count > Max) Count = Max;

	VMP->GCSize += SC_SlabCreate(&VMP->TokenSP, Count * (sizeof(SC_TokenRecord)), SC_SLAB_TOK, SC_SLAB_NO_FLAG);
	VMP->TokenSlotAlloc += Count;
	SlabP = VMP->TokenSP;
	TokenP = (SC_TokenPointer)SC_SlabAllocEntry(SlabP, sizeof(SC_TokenRecord));	
	G_ASSERT(TokenP != NULL, "Failed Token slab!", 0);
    }

    TokenP->Flags = SC_TOKEN_NO_FLAG;
    TokenP->Stamp = 0;
    TokenP->Type = TokenP->Id = TokenP->OpPrec = TokenP->OpState = 0;
    TokenP->ReadPos = TokenP->ReadLen = 0L;
    TokenP->Data = 0LL;
    TokenP->DownP = TokenP->NextP = NULL;

    SC_DEBUG_CHECKSLAB(SlabP);
    return TokenP;
}

// SC_TokenGetNext will read the next MATERIAL token.  It will skip over
// whitespace and comments.
//
// Returns 1 if successful, 0 if error OR just done.
// DONE if (ReadP->ReadStartP >= ReadP->ScriptEndP)!!

Int16		SC_TokenLookAhead(SC_CGPointer CGP, SC_TokenPointer TokenP)
{
    char *	LastReadP;

    LastReadP = CGP->ReadStartP;
	if (! SC_TokenGetNext(CGP, TokenP)) return 0;
    CGP->ReadStartP = LastReadP;
    return 1;
}

Int16		SC_TokenGetNext(SC_CGPointer CGP, SC_TokenPointer TokenP)
{
    char *		SP = CGP->ReadStartP;
    char		C;
    char *		EndP = CGP->ScriptEndP;
    Int16		DidIt;

    while (SP < EndP) {

	C = *SP;
	if (C <= ' ') {					// Includes *ALL* non-printing!
	    if (! SC_ReadWhiteSpace(CGP, TokenP))
		goto ErrorOut;
		
	} else if (C == '/') {				// Comment or DIVIDE?

	    DidIt = 0;
	    if (SP + 1 < EndP) {
		C = *(SP + 1);
		if (C == '/')
		    DidIt = SC_ReadLineComment(CGP, TokenP);
		else if (C == '*') {
		    if (! (DidIt = SC_ReadBlockComment(CGP, TokenP)))
			goto ErrorOut;
		}
	    }

	    if (! DidIt) {
		if (! SC_ReadOperator(CGP, TokenP)) 	// Obviously a DIVIDE
		    goto ErrorOut;
	    }
		
	} else if ((C == '"') || (C == '\'')) {
	    if (! SC_ReadShortString(CGP, TokenP))
		goto ErrorOut;

	} else if (C == '|') {				// Long string or Bit-OR

	    DidIt = 0;
	    if (SP + 1 < EndP) {
		C = *(SP + 1);
		if ((C == '>') || (C == '-')) {
		    DidIt = SC_ReadLongString(CGP, TokenP);
		    if (CGP->ErrorStrP)
			goto ErrorOut;
		}
	    }

	    if (! DidIt) {
		if (! SC_ReadOperator(CGP, TokenP))	// Obviously Bit-OR
		    goto ErrorOut;
	    }

	} else if ((C >= '0') && (C <= '9')) {		// Leading - or + parse as Uniary Op
	    if (CGP->TopOpP && (CGP->TopOpP->Id == SC_DOT_ID)) {
		// "Cars.42" --> Should read 42 as an identifier, like "Cars.Sedans"!!
		if (! SC_ReadIdent(CGP, TokenP))
		    goto ErrorOut;

	    } else if (! SC_ReadNumber(CGP, TokenP))
		goto ErrorOut;

	} else if ((C == '_') || (C == '#') ||		// '_' is valid Alphanumeric
		   ((C >= 'a') && (C <= 'z')) ||
		   ((C >= 'A') && (C <= 'Z')) ||
		   (C & 0x80)) {			// Any codepoint > 127 is valid Alphanumeric
	    if (! SC_ReadIdent(CGP, TokenP))
		goto ErrorOut;

	} else if (C > ' ') {				// All other ops, and some nonsense chars
	    if (! SC_ReadOperator(CGP, TokenP))
		goto ErrorOut;
	}

	// Parsed SOMETHING if we get here... may be whitespace!
	SC_DEBUG_PARSEPRINT(CGP, TokenP);
	// Make ready for another cycle
	SP = CGP->ReadStartP;

	if (TokenP->Type != SC_WHITESPACE_TOKEN)
	    return 1;
    }

    // Done reading... find the last valid Pos, allow for UTF8!
    if (SP == EndP) {
	SP = CGP->LastEndP;
	TokenP->Type = SC_FINALEND_TOKEN;
	if (SP > CGP->ScriptStartP) while (ED_MIDUTF8(*(--SP)));
	TokenP->ReadPos = SP - CGP->ScriptStartP;
	
	SC_DEBUG_PARSEEND(CGP->VMP);	
	return 0;
    }
    return 1;

ErrorOut:
    if (CGP->ErrorStrP)
	SC_ReadShowSourceError(CGP);

    SC_DEBUG_PARSEEND(CGP->VMP);
    return 0;
}

#ifdef DEBUG

    void	SC_CGDisplayLexStack(SC_CGPointer CGP)
    {
	SC_LexPointer	LexP = CGP->LastLexP;

	while (LexP) {
	    CGP->VMP->OutPrintFP("          Lex> [%d:%d]%x Flags: %x\n", LexP->FLevel, LexP->BLevel, (Uns64)LexP, LexP->Flags);
	    LexP = LexP->SupP;
	}
    }

    // Note:  CaseDict basically stores [StrINDEX - JmpDist] + Switch ID/Index
    void	SC_CGDisplayCaseDict(SC_CGPointer CGP)
    {
	SC_PrintFuncP	OutPFP = CGP->VMP->OutPrintFP;    	
	SC_DictVPointer DictP = CGP->DispLexP->MapP->CaseDictP;
	SC_CaseHPointer	CaseP;
	Uns32		Index = 0;

	if (! DictP) return;

	OutPFP("\n\n   Case Dict -- Filled: %d of %d\n", DictP->PairCount, DictP->HADP->SlotCount - 1);
        OutPFP("   ------------------------------------------------\n");

	// Index is 0-based, but display after inc, so 1-based!
	while (Index < DictP->HADP->SlotCount) {
	    CaseP = (SC_CaseHPointer)DictP->HADP->Data[Index++];
	    if (CaseP) {
		OutPFP("   %04d:  Switch:%d  Jmp:%+4d", Index, CaseP->Id, CaseP->Value);
		switch (CaseP->KeyType) {
		    SC_StrDataPointer	StrDP;
		    
		    case SC_VAL_PTR:	// String
			StrDP = (*(SC_StrHPointer)CGP->FuncLexP->MapP->StringADP->Data[CaseP->Key]).StrDataP;
			// CaseP->Key is the string index in StrDataP, *NOT* StrHPointer!!

			if (StrDP->StrLen > 30)
			    OutPFP("  Case:\"%.*s...\"\n", 30, StrDP->Data);
			else
			    OutPFP("  Case:\"%.*s\"\n", StrDP->StrLen, StrDP->Data);
			break;

		    case SC_VAL_INT:
			OutPFP("  Case:%d\n", CaseP->Key);
			break;

		    case SC_VAL_FLT:
			OutPFP("  Case:%f\n", *(double *)&CaseP->Key);
		}

	    } else
		OutPFP("   %04d:  --\n", Index);

	}

	OutPFP("\n          Total Probes:%ld (Avg:%f)\n", DictP->TotProbes, (double)DictP->TotProbes / DictP->PairCount);
    }

    // If LOAD first, NameP will be missing from subsequent Compile!
    void	SC_CGAnnounce(SC_VMPointer VMP, char* TaskP, char* NameP, Uns8 DrawLine)
    {
	SC_PrintFuncP	OutPFP = VMP->OutPrintFP;

	if (DrawLine)
	    OutPFP("*********************************************\n");

	if (NameP)
	    OutPFP("%s %s\n", TaskP, NameP);
	else
	    OutPFP("%s\n", TaskP);

	if (DrawLine)
	    OutPFP("*********************************************\n");
    }

    // Prints AST in LISP/Prefix format.
    void	SC_ParsePrintAST(SC_CGPointer CGP, SC_TokenPointer TP)
    {
	SC_VMPointer	VMP = CGP->VMP;
	char *		CP;
	Int16		CStringLabel = 1;
	Int16		DrawOp = 0;
	SC_PrintFuncP	OutPFP = VMP->OutPrintFP;

	if (! TP) {
	    if (CGP->DispLexP == CGP->TopLexP)
		OutPFP("\n          Main Program Done!\n          -----------------------------------\n");
	    else
		OutPFP("\n          Code: %d (L:%d) Done!\n", CGP->DispLexP->MapP->CodeId, CGP->DispLexP->FLevel);

	    if (SC_DEBUG_ShowLex)
		SC_CGDisplayLexStack(CGP);
	    
	    if (SC_DEBUG_ShowCaseDict)
		SC_CGDisplayCaseDict(CGP);

	    if (CGP->DispLexP == CGP->TopLexP)
		OutPFP("\n*********************************************\n*********************************************\n\n");
	    else
		OutPFP("\n*********************************************\n\n");

	    return;
	}

	if (CGP->Flags & SC_CGPRINTCRFLAG) {
	    OutPFP("\n");
	    CGP->Flags &= ~SC_CGPRINTCRFLAG;
	}

	CP = CGP->ScriptStartP + TP->ReadPos;
	if (TP->Type == SC_START_TOKEN) {
	    if (TP->DownP) {
		OutPFP("          Code:");
		TP = TP->DownP;
		while (TP) {
		    SC_ParsePrintAST(CGP, TP);
		    TP = TP->NextP;
		}

	        OutPFP("\n          -----------------------------------\n");

		if (SC_DEBUG_ShowLex) {
		    SC_CGDisplayLexStack(CGP);
		    OutPFP("          -----------------------------------\n");	
		}
	    }
	} else if (TP->Type == SC_IDENTIFIER_TOKEN) {
	      OutPFP(" %.*s", TP->ReadLen, CP);

	} else if (TP->Type == SC_STRING_TOKEN) {
	    Uns32	StrLen = TP->ReadLen;
	    
	    if (TP->Data) {
		CP = (*(SC_StrHPointer)TP->Data).StrDataP->Data;
		StrLen = (*(SC_StrHPointer)TP->Data).StrDataP->StrLen;
	    }

	    if (StrLen > 15) {
		Int16	L = 12;

		while (ED_MIDUTF8(*(CP+L))) L++;
		OutPFP(" \"%.*s...\"", L, CP);
	    } else
		OutPFP(" \"%.*s\"", StrLen, CP);

	} else if (TP->Type == SC_NUMBER_TOKEN) {
	    if (TP->Id == SC_FLOATNUMB_ID)
		OutPFP(" %f", *(double *)&TP->Data);
	    else
		OutPFP(" %ld", TP->Data);

	} else if (TP->Type == SC_OPERATOR_TOKEN) {

	    // NOTE: Operators can just be read from the buffer... but
	    //	     XformExpr will change the Token ID, so it will no longer
	    //	     match the buffer!

	    DrawOp = 1;
	    switch (TP->Id) {
		case SC_EQ_ID:		CP = "="; break;
		case SC_LESS_ID:	CP = "<"; break;
		case SC_GREAT_ID:	CP = ">"; break;
		case SC_PLUS_ID:	CP = "+"; break;
		case SC_MINUS_ID:	CP = "-"; break;
		case SC_COMMA_ID:	CP = ","; break;
		case SC_SEMI_ID:	CStringLabel = 0; break;	// Never see
		case SC_DIV_ID:		CP = "/"; break;
		case SC_MOD_ID:		CP = "%"; break;
		case SC_MULT_ID:	CP = "*"; break;
		case SC_LOGNEG_ID:	CP = "!"; break;
		case SC_BITNEG_ID:	CP = "~"; break;
		case SC_BITAND_ID:	CP = "&"; break;
		case SC_BITOR_ID:	CP = "|"; break;
		case SC_BITXOR_ID:	CP = "^"; break;
		case SC_CHOOSE_ID:	CP = "Choose"; break;
		case SC_COLON_ID:	CP = ":"; break;
    		case SC_DOT_ID:		CP = "Dot"; break;
		case SC_PCLOSE_ID:	CStringLabel = 0; break;
		case SC_ACLOSE_ID:	CStringLabel = 0; break;
		case SC_END_ID:		CP = "End"; break;
		case SC_POPEN_ID:	CStringLabel = 0; break;
		case SC_AOPEN_ID:	CStringLabel = 0; break;
		case SC_BEGIN_ID:	CP = "Begin"; break;
		case SC_UPLUS_ID:	CP = "Pos"; break;
		case SC_UMINUS_ID:	CP = "Neg"; break;
		
		case SC_NULL_ID:	CP = "Null"; break;
		case SC_INDEX_ID:	CP = "Index"; break;
		case SC_CALL_ID:	CP = (TP->OpState & SC_IFIDCALL_STATE) ? "IFID" : "Call"; break;
		case SC_IN_ID:		CP = "Input"; break;
		case SC_OUT_ID:		CP = "Output"; break;
		case SC_VAR_ID:		CP = "Var"; break;
		case SC_DEF_ID:		CP = "Def"; break;
		case SC_CONS_ID:	CP = "Cons"; break;
		case SC_PAIR_ID:	CP = "Pair"; break;
		case SC_LABEL_ID:	CP = "Label"; break;
		case SC_GOTO_ID:	CP = "Goto"; break;
		case SC_CASE_ID:	CP = "Case"; break;
		case SC_DEFAULT_ID:	CP = "Default"; break;
		case SC_FOR_ID:		CP = "For"; break;
		case SC_WHILE_ID:	CP = "While"; break;
		case SC_IF_ID:		CP = "If"; break;
		case SC_SWITCH_ID:	CP = "Switch"; break;
		case SC_DO_ID:		CP = "Do"; break;
		case SC_DOCOND_ID:	CP = "Do-Cond"; break;
		case SC_ELSE_ID:	CP = "Else"; break;
		case SC_BREAK_ID:	CP = "Break"; break;
		case SC_CONT_ID:	CP = "Continue"; break;
		case SC_FUNC_ID:	CP = "Function"; break;
		case SC_FDECL_ID:	CP = "FuncDec"; break;
		case SC_RETURN_ID:	CP = "Return"; break;
		case SC_YIELD_ID:	CP = "Yield"; break;
		case SC_FROM_ID:	CP = "OutOf"; break;		// Should never be seen!
		case SC_LOGPOS_ID:	CP = "LogPos"; break;
		case SC_UNPACK_ID:	CP = "UnPack"; break;

		case SC_ALIAS_ID:	CP = "Alias"; break;
		case SC_EQEQ_ID:	CP = "=="; break;
		case SC_NEQ_ID:		CP = "!="; break;
		case SC_LESSEQ_ID:	CP = "<="; break;
		case SC_GREATEQ_ID:	CP = ">="; break;
		case SC_LOGAND_ID:	CP = "&&"; break;
		case SC_LOGOR_ID:	CP = "||"; break;
		case SC_SHL_ID:		CP = "<<"; break;
		case SC_SHR_ID:		CP = ">>"; break;
		case SC_PLUSPLUS_ID:	CP = "Post++"; break;
		case SC_MINMIN_ID:	CP = "Post--"; break;
		case SC_PLUSEQ_ID:	CP = "+="; break;
		case SC_MINUSEQ_ID:	CP = "-="; break;
		case SC_DIVEQ_ID:	CP = "/="; break;
		case SC_MODEQ_ID:	CP = "%="; break;
		case SC_MULTEQ_ID:	CP = "*="; break;
		case SC_BITANDEQ_ID:	CP = "&="; break;
		case SC_BITOREQ_ID:	CP = "|="; break;
		case SC_BITXOREQ_ID:	CP = "^="; break;

		case SC_PREPLUSPLUS_ID: CP = "++Pre"; break;
		case SC_PREMINMIN_ID:	CP = "--Pre"; break;
    
		case SC_SHLEQ_ID:	CP = "<<="; break;
		case SC_SHREQ_ID:	CP = ">>="; break;
		case SC_IDENT_ID:	CP = "==="; break;
		case SC_NIDENT_ID:	CP = "!=="; break;
		
		default: CStringLabel = 0;
	    }
	}

	if (DrawOp) {
	    if (TP->Flags & SC_TOKEN_NOPUSH_FLAG) {
		if (CStringLabel)
		    OutPFP(" (_%s", CP);
		else
		    OutPFP(" (_%.*s", TP->ReadLen, CP);

	    } else {
		if (CStringLabel)
		    OutPFP(" (%s", CP);
		else
		    OutPFP(" (%.*s", TP->ReadLen, CP);
	    }
	    
	    TP = TP->DownP;
	    while (TP) {
		SC_ParsePrintAST(CGP, TP);	// Recurse!
		TP = TP->NextP;
	    }
	    OutPFP(")");
	}
    }

    
    // SC_CGDisplayLabels displays Labels (for BR/JMP) that were NOT AVAILABLE
    // when the ByteCodes were displayed.  The Index for the first of these is
    // stashed in CGP->DispLabel... if 0, it means no labels need to be shown.
    //
    // Assumes if Label 17 needs to be shown, then so does 18 and 19.
    // Similarly, if 18 has not been calculated yet, then neither has 19.

    void SC_CGDisplayLabels(SC_CGPointer CGP)
    {
	if (CGP->DispLabel) {

	    SC_VMPointer	VMP = CGP->VMP;
	    SC_MapPointer	MapP = CGP->DispLexP->MapP;
	    Uns32		Index = CGP->DispLabel;
	    Uns32		Count = MapP->LabelCount;
	    Int32		Offset;
	    Uns16		Level = CGP->DispLexP->FLevel;
	    Uns16		L;
	    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;

	    while (Index < Count) {

		Offset = SC_CGLabelGet(MapP, Index);
		if (Offset >= 0) {
		    // Found, 1 display it.
		    L = 0;
		    while (L++ < Level) OutPFP("  ");
		    
		    OutPFP("          Label> %d <-- %04d ****\n", Index, Offset);
		    Index += 1;
		    CGP->Flags |= SC_CGPRINTCRFLAG;

		} else {
		    // Hit Labels that have not been set yet!
		    // Remember this Index for the next display cycle.
		    CGP->DispLabel = Index;
		    return;
		}
	    }

	    // Did all... no more to do!
	    CGP->DispLabel = 0;
	}
    }

    Int32	SC_AuxCGGetLabel(SC_CGPointer CGP, SC_TokenPointer LabelTP, Uns32 * IndexP);
	
    void SC_CGDisplayNamedLabel(SC_CGPointer CGP, SC_TokenPointer TP)
    {
	Uns32		Index;
	SC_VMPointer	VMP = CGP->VMP;
	Int32		Offset;

	// AST Code is drawn at the same place, so no need for Level...
	// "Label>" just lines up with it.
	if ((Offset = SC_CGGetLabel(CGP, TP, &Index)))
	    VMP->OutPrintFP("          Label> \"%.*s\" = %+d <-- %04d\n\n",
			 TP->ReadLen, CGP->ScriptStartP + TP->ReadPos, Index, Offset);
    }

    void SC_AuxCGDMC(SC_VMPointer VMP, Uns16 Level)
    {
	Uns16		L = 0;
	SC_PrintFuncP	OutPFP = VMP->OutPrintFP;
	OutPFP(" ");
	while (L++ < Level)
	    OutPFP("| ");
    }

    void SC_CGDisplayMoreCode(SC_CGPointer CGP)
    {
	SC_VMPointer		VMP = CGP->VMP;
	SC_MapPointer		MapP = CGP->DispLexP->MapP;
	SC_MapPointer		TopMapP = CGP->TopLexP->MapP;
	Uns16			Level = CGP->DispLexP->FLevel;
	SC_PrintFuncP		OutPFP = VMP->OutPrintFP;
	SC_ArrDataPointer	GNameADP = CGP->ScriptP->GNameADP;
	SC_OpDispPointer	DispP;
	SC_ByteOpPointer	BOP;
	char *			OpS;
	char *			ArgS;
	SC_VDescPointer		VP;
	Int16			I;
	Int16			DidOp = 0;

	#define	_STRLEN		28
	    char		S[_STRLEN];

	BOP = (SC_ByteOpPointer)MapP->CodeADP->Data + MapP->DispCount;
	while (MapP->DispCount < MapP->CodeCount) {

	    DispP = SC_OpDispArr + BOP->Op;
	    OpS = SC_OpStrArr[DispP->OpI];
	    ArgS = SC_ArgStrArr[DispP->ArgI];
	    if (DispP->ArgI == SC_ARGsss)
		goto PrintAllStack;
	    else if (BOP->Flags & SC_BCJMPFLAG)
		goto PrintL3Arg;
	    else
		goto PrintByteArg;

	BackInLoop:
	    BOP++, MapP->DispCount++;
	    DidOp = 1;
	}	// While
	
	SC_CGDisplayLabels(CGP);
	if (DidOp) CGP->Flags |= SC_CGPRINTCRFLAG;
	return;

    PrintL3Arg:
	SC_AuxCGDMC(VMP, Level);
	OutPFP("> %04d - %s%s --", MapP->DispCount, OpS, ArgS);
	OutPFP(" I3>%11d", BOP->KVal);
	{
	    Int32	Offset = SC_CGLabelGet(MapP, BOP->KVal);

	    if (Offset < 0) {
		OutPFP(" --> ????\n", Offset);
		// Print the labels later... but remember where to start!
		if (CGP->DispLabel == 0)
		    CGP->DispLabel = BOP->KVal;
		else if (BOP->KVal < CGP->DispLabel)
		    CGP->DispLabel = BOP->KVal;

	    } else
		OutPFP(" --> %04d\n", Offset);

	}
	goto BackInLoop;

    PrintByteArg:
	SC_AuxCGDMC(VMP, Level);
	OutPFP("> %04d - %s%s --", MapP->DispCount, OpS, ArgS);
	for (I = 0, VP = BOP->Vars; I < BOP->ArgCount; I++, VP++) {
	    switch (VP->Class) {
		case SC_CVEX:	OutPFP(" C%03d", VP->Index); break;
		case SC_LVEX:   OutPFP(" L%03d", VP->Index); break;
		case SC_RVEX:   OutPFP(" R%03d", VP->Index); break;
		
		case SC_STACK:
		    // This list is becoming ridiculous... flag or bitmap?
		    if ((BOP->Op == SC_SWCH_VI2_CODE) ||
			(BOP->Op == SC_FCALL_VK2_CODE) ||
			(BOP->Op == SC_FRET_V_CODE) ||
			(BOP->Op == SC_REF_V_CODE) ||
			(BOP->Op == SC_PSHI_V_CODE) || (BOP->Op == SC_PSHF_V_CODE) ||
			(BOP->Op == SC_AINI_V_CODE) ||
			(BOP->Op == SC_IREF_VV_CODE) || (BOP->Op == SC_ZREF_VV_CODE) ||
			(BOP->Op == SC_IREF_VI2_CODE) || (BOP->Op == SC_ZREF_VI2_CODE) ||
			(BOP->Op == SC_ISET_VVV_CODE))
			OutPFP(" POP_");		// POPs where others PUSH
		    else if (BOP->Op == SC_FRET_V_CODE)
			OutPFP(" STCK");		// Neither POP nor PUSH
		    else if (I) OutPFP(" POP_");
			else OutPFP(" PUSH");
		    break;

		case SC_REFER:  OutPFP(" REFR");
				break;
		
		case SC_KONST:	if (VP->Index == 1)
				    OutPFP(" %+4d", BOP->KVal);
				else if (VP->Index == 2)
				    OutPFP(" K2:%+6d", BOP->KVal);
				else
				    OutPFP(" K3:%+11d", BOP->KVal);
				break;
				
		case SC_INDEX:
		    if (VP->Index == 1)
			OutPFP(" I%3D", BOP->KVal);
		    else if (VP->Index == 2) {
			OutPFP(" I2:%06d", BOP->KVal);
		    if (GNameADP && (BOP->Op == SC_RSET_VI2_CODE) && GNameADP->Data[BOP->KVal])
			OutPFP(" <-- %s", (*(SC_StrHPointer)GNameADP->Data[BOP->KVal]).StrDataP->Data);
			
		    } else {
			SC_StrHPointer	SHP;
			
			OutPFP(" I3:%011d", BOP->KVal);
			if (BOP->Op == SC_PSHS_I3_CODE) {
			    SHP = (SC_StrHPointer)TopMapP->StringADP->Data[BOP->KVal];
			    SC_StrStuffStr(S, SHP->StrDataP->Data, _STRLEN);
			    OutPFP(" <-- \"%s\"", S);
			} else if (BOP->Op == SC_PSHI_I3_CODE)
			    OutPFP(" <-- %d", *(Int64 *)(TopMapP->KonstADP->Data + BOP->KVal));
			else if (BOP->Op == SC_PSHF_I3_CODE)
			    OutPFP(" <-- %f", *(double *)(TopMapP->KonstADP->Data + BOP->KVal));
			if (GNameADP && ((BOP->Op == SC_PSHG_I3_CODE) || (BOP->Op == SC_REFG_I3_CODE))
				     && GNameADP->Data[BOP->KVal])
			    OutPFP(" <-- %s", (*(SC_StrHPointer)GNameADP->Data[BOP->KVal]).StrDataP->Data);

		    }
		    break;
		

		case SC_SYSI:	OutPFP("  %03d", VP->Index); break;
				
		default:	OutPFP(" xxxx");
	    }
	}

	OutPFP("\n");
	goto BackInLoop;

    PrintAllStack:
	SC_AuxCGDMC(VMP, Level);
	OutPFP("> %04d - %s%s -- PUSH POP_ POP_\n", MapP->DispCount, OpS, ArgS);
	goto BackInLoop;

	#undef _STRLEN
    }


    void	SC_CGDisplayCode(SC_CGPointer CGP)
    {
	// Initially, nothing is displayed.
	if (CGP->DispLexP == NULL)
	    CGP->DispLexP = CGP->FuncLexP;

	SC_CGDisplayMoreCode(CGP);

	// Now go back to FuncLexP if DispLexP is done!
	if ((CGP->DispLexP != CGP->LastLexP) && (CGP->DispLexP != CGP->FuncLexP)) {
	    CGP->DispLexP = CGP->FuncLexP;
	    SC_CGDisplayMoreCode(CGP);
	}
    }

#endif


// SC_CGTokenZapTree will recursively zap TP and its Down and Next chains.
//
// NOTE: This will NOT kill the SC_START_TOKEN itself, just its chains.

void	SC_CGTokenZapTree(SC_VMPointer VMP, SC_TokenPointer TP)
{
    SC_TokenPointer NextTP;

    if (TP->Type == SC_START_TOKEN) {
	if (TP->DownP) {
	    SC_CGTokenZapTree(VMP, TP->DownP);	// Recurse
	    NextTP = TP->NextP;
	    TP->DownP = TP->NextP = NULL;
	    TP = NextTP;
	} else
	    return;
    }
    
    while (TP) {
	if (TP->DownP) SC_CGTokenZapTree(VMP, TP->DownP);
	NextTP = TP->NextP;

	SC_SlabZapItem(VMP->TokenSP, TP, sizeof(SC_TokenRecord));
	TP = NextTP;
    }
}

// SC_CGTokenTestTree tests for loops in the AST structure... debugging only.

Int16	SC_CGTokenHasLoop(SC_TokenPointer TP)
{
    // Test the TP itself?
    if (TP->Stamp == 0xABCD) {
	printf("Error! Token Loops: %lx Type: %d Id: %d [%d-%d]\n",
		(Uns64)TP, TP->Type, TP->Id, TP->ReadPos, TP->ReadLen);
	return 1;
    }
    TP->Stamp = 0xABCD;

    if (TP->DownP && SC_CGTokenHasLoop(TP->DownP))
	return 1;
    if (TP->NextP && SC_CGTokenHasLoop(TP->NextP))
	return 1;

    return 0;
}

// SC_CGTokensPrint will write out the tokens in an AST, from the top.
void	SC_CGTokensPrint(SC_TokenPointer TP)
{
    if (TP->Stamp == 0xEFEF) return;
    TP->Stamp = 0xEFEF;
    
    printf("[%lx]", (Uns64)TP);
    if (TP->NextP)
	printf(" -N-> [%lx]", (Uns64)TP->NextP);

    if (TP->DownP)
	printf(" -D-> [%lx]", (Uns64)TP->DownP);
    printf("\n");

    if (TP->DownP)
	SC_CGTokensPrint(TP->DownP);
    if (TP->NextP)
	SC_CGTokensPrint(TP->NextP);
}

// ********************************************************************************
// CodeGen

// SC_CGMainInit will initialize the (pre-allocated) main CGRecord.

    SC_ScriptPointer SC_CGScriptMake(SC_VMPointer VMP)
    {
	SC_ScriptPointer	SP;
	Uns32			BlockLen;
	
	// Allocate SC_ScriptRecord on CHUNK slab
	SP = (SC_ScriptPointer)SC_ChunkNew(VMP, sizeof(SC_ScriptRecord), &BlockLen);
	SP->Type = SC_CHUNK_SCRPT;
	SP->State = SC_NOSTATE;
	SP->Flags = 0;
	SP->RefCount = 1;			// Starts with 1... dec if COMPILE fails!!

	SP->GlobCount = 0;
	SP->ScriptLevel = (VMP->RunScriptP) ? VMP->RunScriptP->ScriptLevel + 1 : 0;
	SP->FuncCount = 0L;
	
	SP->KonstCount = SP->StringCount = 0L;
	SP->ScriptId = VMP->LastScriptId + 1;	// Will inc LastScriptId if compile succeeds.
	SP->StartStackTop = 0L;

	SP->NameSHP = SP->PathSHP = NULL;
	SP->TopClosP = NULL;
	SP->KonstADP = SP->StringADP = SP->GNameADP = NULL;
	SP->GlobP = NULL;

	// Just compiling for now... will set when running!
	SP->SupScriptP = NULL;
	SP->TopRunP = SP->CurRunP = NULL;

	return SP;
    }

void		SC_CGMainInit(SC_CGPointer CGP, SC_VMPointer VMP, char *StrP, Uns32 Len)
{
    CGP->Flags = SC_CGNOFLAG;
    CGP->InGZCount = 0;

    CGP->LastArgP = CGP->TopOpP = NULL;

    CGP->ScriptStartP = StrP;
    CGP->ScriptEndP = StrP + Len;
    CGP->ReadStartP = StrP;
    CGP->ErrorStartP = NULL;
    CGP->LastEndP = StrP;
    CGP->ErrorStrP = CGP->ErrorArgStrP = NULL;

    CGP->VMP = VMP;
    CGP->ScriptP = SC_CGScriptMake(VMP);
    CGP->InGZADP = NULL;

    CGP->DispLabel = 0L;
    CGP->DispLexP = NULL;					// ByteCode display
    CGP->TopLexP = CGP->LastLexP = CGP->FuncLexP = SC_CGLexNew(VMP);
    CGP->CallerJSetP = NULL;					// Jmp/Br labels (On call stack!)
    CGP->TopLexP->Flags = SC_LEXTOPFLAG | SC_LEXFUNCFLAG;	// TOP
    CGP->TopLexP->FLevel = 0;					// TOP
    CGP->TopLexP->BLevel = 0;					// First level...
    CGP->TopLexP->VDictP = SC_DictCreateNewDict(VMP, 0);	// TOP Var Dict
    // DDictP (Def Dict) in Lex comes in initially NULL!
    CGP->TopLexP->MapP = SC_CGMapNew(VMP);			// For TOP function
    CGP->TopLexP->MapP->FuncId = 0;
}

// SC_CGZap will zap/free the fields in the main CGRecord.
// Aggressively wipe the compiler state... clean it for another run.

    void	SC_CGZAuxZapLexChain(SC_VMPointer VMP, SC_LexPointer LexP)
    {
	SC_LexPointer	SupLexP;

	// Instead of zapping individual Lex, easier to just wipe their Slab in SC_CGZap!
	while (LexP) {
	    SupLexP = LexP->SupP;
	    if (LexP->MapP) SC_CGMapZap(VMP, LexP->MapP), LexP->MapP = NULL;
	    if (LexP->VDictP) SC_DictPurge(VMP, LexP->VDictP, 0), LexP->VDictP = NULL;
	    if (LexP->DDictP) SC_DictPurge(VMP, LexP->DDictP, 0), LexP->DDictP = NULL;
	    LexP->SupP = NULL;
	    // printf("Zap Lex: %lx  Sup:%lx\n", (Int64)LexP, (Int64)LexP->SupP);

	    LexP = SupLexP;
	}
    }

void		SC_CGZap(SC_CGPointer CGP)
{
    SC_VMPointer	VMP = CGP->VMP;

    // First clean individual Lex of their Map and Dicts.
    if (CGP->LastLexP) SC_CGZAuxZapLexChain(VMP, CGP->LastLexP);
    SC_SAFE_CHECK(SC_SlabUsed(VMP->MapSP) == 0LL, "CGZap: Orphan Map");

    // Just wipe the slabs used by compiler!
    SC_SlabWipe(VMP->TokenSP);
    SC_SlabWipe(VMP->LexSP);
    SC_SlabWipe(VMP->VexSP);
    SC_SlabWipe(VMP->MapSP);
    
    // CGP->ScriptP is transferred to VMP and will not be zapped.
    if (CGP->InGZADP) {
	// InGZeroP is temporary, stored in VMP so Mark/Sweep GC sees it.
	//
	// Points to InGlob Vars already *IN* InGlob **WITHOUT**
	// increment their RefCount.  So do *NOT* DeRef its contents!
	SC_SlabZapItem(VMP->ArrDataSP, CGP->InGZADP, CGP->InGZADP->BlockLen);
	CGP->InGZADP = NULL;
	CGP->InGZCount = 0;
    }
}


// Each CODE element is bigger than a (void *), so must adjust the native array
// size.  (Currently 3x bigger)
//
// NOTE:  Taking extra care to round up the size... 2x until CGMAXDOUBLESIZE,
//	  1.5x after.
//
//	  ZeroAll flag is set for ArrAlloc and ArrResize copies old data and
//	  zeroes the rest.  So ByteOpRecords come in all zeroed!!

SC_ByteOpPointer	SC_CGCodeAdd(SC_CGPointer CGP, SC_MapPointer MapP)
{
    Uns32		Slots;
    
    if (! MapP->CodeADP) {
	// First entry, allocate array.  Each Slot fits one Void* (8 bytes).
	Slots = (SC_CGMINCODERECORDS * sizeof(SC_ByteOpRecord)) / sizeof(void *);
	SC_ArrAllocNewData(CGP->VMP, &MapP->CodeADP, Slots, 1);

    } else {
	Slots = MapP->CodeADP->SlotCount;
	// Imperative to use "<=" here, as Slot count may not be perfect multiple of size!
	if (Slots <= ((MapP->CodeCount + 1) * sizeof(SC_ByteOpRecord)) / sizeof(void *)) {
	    // Not enough room... expand array
	    if (Slots < SC_CGMAXDOUBLESIZE) Slots <<= 1;	// 2x
	    else Slots += Slots >> 1;				// 1.5x

	    MapP->CodeADP = SC_ArrResizeData(CGP->VMP, MapP->CodeADP, Slots, 0);
	}
    }
    return (SC_ByteOpPointer)MapP->CodeADP->Data + MapP->CodeCount++;
}

// Each Label element is just an Int32, half the size of a (void *), so must
// adjust the native array size.
//
// NOTE: Do not want Label 0, so waste the first Index!

Uns32		SC_CGLabelAlloc(SC_CGPointer CGP, SC_MapPointer MapP)
{
    Uns32		Slots;

    if (! MapP->LabelADP) {
	// First entry, allocate array... Labels are smaller than (Void *)!
	Slots = (((SC_CGMINLABELSLOTS + 1) * sizeof(Uns32)) - 1) / sizeof(void *);
	SC_ArrAllocNewData(CGP->VMP, &MapP->LabelADP, Slots, 1);
	MapP->LabelCount = 1;					// Waste the first one

    } else {
	Slots = MapP->LabelADP->SlotCount;			// LL slots
	if ((Slots * sizeof(void*)/sizeof(Uns32)) == MapP->LabelCount) {
	    // Not enough room... expand array
	    if (Slots < SC_CGMAXDOUBLESIZE) Slots <<= 1;	// 2x
	    else Slots += (Slots >> 1);				// 1.5x

	    MapP->LabelADP = SC_ArrResizeData(CGP->VMP, MapP->LabelADP, Slots, 0);
	}
    }

    // -1 is impossible value... 0 means first ByteOp here!
    *((Int32 *)MapP->LabelADP->Data + MapP->LabelCount) = -1L;
    return MapP->LabelCount++;
}

// SC_CGLabelDefine is used during compilation to set label offsets.  These are
// initially set as the index for the target ByteCode and later (during final
// ASM) changed to the actual ByteCode offset and written into Jmp/Br.

void		SC_CGLabelDefine(SC_MapPointer MapP, Uns32 Index, Int32 Value)
{
    // LabelADP->Data is array of Void*, so cast to Uns32 here
    *((Int32 *)MapP->LabelADP->Data + Index) = Value;

    // printf("Label %d <-- %d\n", Index, Value);
}

// SC_CGLabelGet will return the Value stashed in the Label.

Int32		SC_CGLabelGet(SC_MapPointer MapP, Uns32 Index)
{
    return *((Int32 *)MapP->LabelADP->Data + Index);
}

// Each CONST element is the same size as a (void *), so matches the native
// array slot size.
//
// Returns Index/Id of Konst

Uns32		SC_CGKonstAdd(SC_CGPointer CGP, SC_MapPointer MapP, Int64 Konst)
{
    Uns32	Slots;
    
    if (! MapP->KonstADP) {
	// First entry, allocate array... each Const is same size as (void *)
	SC_ArrAllocNewData(CGP->VMP, &MapP->KonstADP, SC_CGMINKONSTSLOTS, 1);

    } else {
	Slots = MapP->KonstADP->SlotCount;
	if (MapP->KonstCount == Slots) {
	    // Not enough room... Expand array.
	    if (Slots < SC_CGMAXDOUBLESIZE) Slots <<= 1;	// 2x
	    else Slots += (Slots >> 1);				// 1.5x

	    MapP->KonstADP = SC_ArrResizeData(CGP->VMP, MapP->KonstADP, Slots, 0);
	}
    }
    
    Slots = MapP->KonstCount++;				// Used as Index here
    MapP->KonstADP->Data[Slots] = (void *)Konst;
    return Slots;
}

// SC_CGFuncAlloc will allocate space for a new function on the
// MapP->FuncADP of the container function.
//
// Returns FuncId which is INDEX into ContMapP->FuncADP.

Uns32		SC_CGFuncAlloc(SC_CGPointer CGP, SC_MapPointer ContMapP)
{
    Uns32	Slots;
    
    if (! ContMapP->FuncADP) {
	// First entry, allocate array... each elt is a Ptr!
	SC_ArrAllocNewData(CGP->VMP, &ContMapP->FuncADP, SC_CGMINFUNCSLOTS, 1);

    } else {
	Slots = ContMapP->FuncADP->SlotCount;
	if (ContMapP->FuncCount == Slots) {
	    // Not enough room... expand array.
	    if (Slots < SC_CGMAXDOUBLESIZE) Slots <<= 1;	// 2x
	    else Slots += (Slots >> 1);				// 1.5x

	    ContMapP->FuncADP = SC_ArrResizeData(CGP->VMP, ContMapP->FuncADP, Slots, 0);
	}
    }

    return ContMapP->FuncCount++;
}


// ********************************************************************************
// Parse
//
// Parse creates an Abstract Symbol Tree (AST) structure with TokenRecords that
// it reads one by one.  This AST will convey the application order of operators
// and their args, reflecting correct precedence (Prec), associativity (Assoc),
// and evaluation (Eval) order for CodeGen.  Obvious errors will be flagged at
// this early stage.
//
// A statement like:
//		A = B + C * D / E;
// Becomes:
//		(A = (B + ((C * D) / E)))
//
//		= is low Prec R-Assoc
//		+- are mid Prec L-Assoc
//		*/ are high Prec L-Assoc
//
//   Conceptual AST:	    Implemented as:		within TokenRecords:
//        =			=			Lower line is Token.DownP,
//	A   +			A +			and next is Token.NextP
//        B   /			  B /
//          *   E		    * E
//        C   D			    C D
//
// With a few optimizations (choosing L and R branches), CodeGen will yield:
//
//	Mul0	Push C D		Pushes (C * D) on stack
//	Div0	Push Pop E		Pushes (Pop / E) on stack
//	Add	A, B, Pop		A = B + Pop		
//
//      Any of the variables (A, B, C... ) can be a local, a 'closed' variable
//	from the superior (enclosing) function, a global, or even a constant.
//	Worse, they can also be very complex function calls themselves, perhaps
//	with nested function calls of their own...
//
//	A = B && FuncC(1, D + FuncE(14, F, G * 13), G);
//
//	If there are any logical operators (&& or ||) then the evaluation order
//	becomes doubly critical!  In the above example FuncC *SHOULD* not be
//	called at all if B is False!

// OpStack:  (CGP->TopOpP) grows from RIGHT to LEFT using NextP.
// ArgList:  (CGP->LastArgP) grows from RIGHT to LEFT using NextP.
//
//	     Ops go on OpStack.
//           Processed Ops (have args below them) go on ArgList!
//
// Processing Parens (POPEN and PCLOSE):
// -- POPEN is treated as unary prefix, with special PBLOCK state.
//      When Op with PBLOCK is placed on OpStack, its PREC is set to 0!
//      ParseExpression will not pop/process an Op if its PREC is 0.
// -- Everything after POPEN works as before.
// -- PCLOSE will pop and process ops UNTIL it hits a POPEN.
// -- Tokens for POPEN and PCLOSE are both disposed.
//      Parens just change ASL structure, do not appear themselves.
//
// Processing Function Call (and its args):
// -- Function CALL starts with a '(' after an arg!
//	This arg is the function ref, usually a name, but can be a
//	processed ASL subtree, as in "A.B[I](Arg1, Arg2)".
// -- Mark the previous arg (function ref) as SC_FUNC_STATE
// -- Change Token Type from '(' to 'CALL', set PBLOCK state.
// -- While CALL is top of OpStack, COMMA op token is just Zapped...
//       ... otherwise, just accumulate ARGS on Arg list.
// -- When CALL is on top of OpStack, ')' closes the func CALL.
// -- A new IN (Input) OP is created and given all the args.
// -- The CALL gets the Func Ref (name) then IN.
// -- Processed CALL (and its args) are simply added to Arg list, as usual.
// -- There can be another () after the IN, this is the OUT.
// -- When OUT is first created, a GUARD token is placed on the arg list.
//       Lets OUT know how many args it gets when closed.
// -- WHEN OUT is closed (PCLOSE), it is placed under CALL, after IN.
// -- A CALL that has an OUT is in FINAL_STATE, so any subsequent
//	 () creats a new CALL!! (Using the previous CALL as its Func Ref!)
// -- Fn(A)(B)(C) --> (CALL (CALL Fn (Input A) (Output B)) (Input C))
//
// Processing Array Index (AOPEN and ACLOSE):
// -- AOPEN is initially pushed on to the OpStack, with PBLOCK state.
// -- ACLOSE expects matching AOPEN to be top of OpStack.
// -- When closed, AOPEN token (Type) becomes INDEX.
// -- Closing (ACLOSE) token is just zapped/disposed.
// -- INDEX is processed as a binary op, between Array and its Index.
//
// Processing CHOOSE Op (?:):
// -- CHOOSE has PBLOCK state and right assoc.
// -- CHOOSE will initially eval any preceeding Op with higher Prec.
//      But will then stop itself being evaluated.
// -- COLON has similar flags, with MIN Prec, so everything behind it
//      gets evaluated... almost like Paren between ? and :.
// -- When encountered in ParseProcessTopOp, COLON is simply removed
//	if the preceeding Op is a CHOOSE.
// -- CHOOSE will simply grab the last 3 args (since COLON is gone).
//
// Processing VAR declarations:
// -- VAR Identifier is xformed into a VAR Op.
// -- VAR must be the very first thing!
// -- VAR Op becomes the new START Op.
// -- When procesing the Var Op (at the end) every Arg goes under it!
//
// Processing Array/Dict Constructors:
// -- Open [ becomes CONS Op.
// -- Parse phase does not differentiate Array/Dict Cons.
// -- Place special GUARD Op on ArgList when hitting '['.
// -- When processing CONS add args until matching GUARD.
// -- When processing CONS, COLON signifies (Dict) PAIR.
// -- CG state decides whether Array or Dict (if has PAIR).
//
// Processing Labels:
// -- A label and its colon form a complete expression.
// -- Requires care during CG, as it should not count as
//      "only" expression after an unbracketed IF, etc.
// -- Colon following an Arg counts as Label terminator
//	IFF it is the very first Op.
// -- COLON Op is changed to LABEL and treated as a
//	Postfix unary Op.
//
// Processing Goto:
// -- Forms GOTO Op, treated as Prefix Unary Op.
// -- Must be the first Op (in the expression).
//
// Processing WHILE:
// -- Forms WHILE Op.
// -- ParseIdentifier will call (recurse) ParseExpression to get
//      loop condition--uses SC_GETCOND_ACT.
// -- When under SC_GETCOND_ACT, ParseExpression will NOT allow
//	other keywords, no GOTO, Label, WHILE, FOR, etc.
// -- WHILE creates an Op with a single arg, corresponding to its
//	condition.  The following statement is evaluated separately.
//
// Processing DO WHILE:
// -- DO becomes an OP, with no args.
// -- Other statements will simply follow it.
// -- WHILE COND works just as before.
// -- CodeGen will treat WHILE+COND differently if preceeded by DO!
//
// Processing IF ELSE:
// -- IF works exactly like WHILE, as they are both followed by
//	a condition.
// -- Like DO, ELSE becomes a standalone Op.
// -- CodeGen sorts out which statements go with which conditions.
//
// Processing BREAK and CONTINUE:
// -- Both work very much like DO and ELSE, Ops with no args.
// -- CodeGen decides whether and what.
//
// Processing SWITCH
// -- SWITCH statement itself works just like IF.
// -- CASE becomes an Op, needs an arg, but ends with ':'
//	Already have similar for LABEL!
// -- CASE can have a computed label, "CASE A+B:"
// -- DEFAULT is similar to CASE, without the arg.
// -- CodeGen makes sure SWITCH is followed by BEGIN ('{').
// -- CodeGen also ensures CASE value reduces to a constant.
// -- CodeGen ensures there is only one default case.
//
// Processing FOR
// -- Operates similar to WHILE, IF, and SWITCH.
// -- When the 'condition' is processed, take all params (args)
//	and place them under the FOR Op.
// -- ParseExpression checks whether 'InForOp' so it can do special
//      handling for SEMI.
// -- SEMI in FOR condition act like COMMA in VAR list.
// -- If a parameter is null, a NULL Op is added instead.
// -- At the Parse level, FOR is *NOT* limited to just three
//      parameters (Init, Test, and Update).
// -- CodeGen must check the FOR Op and decide if it is correct.
// -- "For(Null;Null;Null)" === "For(;;)"
//
// Processing FUNCTION
// -- FUNCTION (not first Op) followed by POPEN is a definition.
//	"Var MyFunc = Function(InArg)(OutArg1, OutArg2) {...}"
//	"SomeVar.F = Function(InArg)(OutArg1, OutArg2) {...}"
//	"MyArr[FIndex1] = Function(InArg)(OutArg1, OutArg2) {...}"
//	 --> (FUNC (NULL) (IN InArg) (OUT OutArg1 OutArg2))
//
// -- FUNCTION as first Op, followed by an Arg (simple name) is a VAR declaration
//	"Function MyFunc(InArg)(OutArg1, OutArg2) {...}"
//	 --> (FUNC MyFunc (IN InArg) (OUT OutArg1 OutArg2))
//
// -- FUNCTION as first Op, followed by a Ref (e.g. "MyDict.Func1") is NOT allowed.
//    Must use the form "MyDict.Func1 = Function(InArg)(OutArg1, OutArg2) {...}".
//
// -- CodeGen decides what to do with the FUNC name and how to stash
//	the defined/declared function.  (Will deal with BEGIN/END too.)
//
// Processing RETURN
// -- RETURN can have 1 or 0 args.
// -- Easiest way is to handle in SC_ParseIdentifier.
//      Simply end if ';' after RETURN.
//      Otherwise recurse with Expression.
// -- If getting expression (recurse) do same as GetCond.
//	But have special flag for InReturn (PrevWasOp = 2)
//
//
// Loops are complicated:
// -- Can have multiple nested loops: WHILE, FOR, and DO!
// -- BREAK and CONTINUE only work from within loops.
// -- WHILE can start its own loop *OR* terminate a DO loop!
//      (Changed to SC_DOCOND_ID in latter case.)
// -- Span of a loop is normally just 1 statement.
// -- BEGIN/END ({}) block can extend the span of a loop.
// -- BEGIN/END block can also define variables.
// -- Terminating WHILE needs to know which DO it closes.
// -- BREAK/CONT need to know what loop they control.
// -- CodeGen needs Offsets for loop start and end.
//
// Loop implementation:
// -- A Lex is created for the body of every loop,
//	(Also IF, ELSE, Etc.)
// -- Nesting Lex allow for nesting of LOOPS, IF, ELSE, etc.
// -- These get a LEXONE flag, as the segment can span *ONE* statement.
// -- If the one statement is BEGIN, the LEXONE flag is removed.
//	(The segment can now span a whole BEGIN-END block.)
// -- The initiating loop, DO, or IF is marked with Lex flags:
//      LEXLOOP, LEXDO, LEXIF...
// -- LEXONE gets LEXFIN automatically after 1 statement.
// -- NON LEXONE will get LEXFIN only after hitting END.
//
// DO...WHILE Loop:
// -- If a LEXDO, then must see WHILE (DOCOND) when at LEXFIN.
//
// IF...ELSE:
// -- If a LEXIF, then ELSE will be accepted at LEXFIN,
//	otherwise ELSE will signal an error.
//
// NOTE: FIN Lex are finished and done.  BUT they are **KEPT** around for ONE
//	 MORE cycle because things may come right after (e.g. ELSE) that need to
//	 see/verify the preceeding finished Lex.
//
//	 There are often checks to see whether a keyword is within a lexical
//	 grouping for a statement, e.g. CASE inside a SWITCH or CONT inside a
//	 LOOP.  In these cases, skip over the FIN Lex and look to its superior
//	 container if any.  It may well be that a LOOP Lex contains an IF that
//	 contains another {} block, and another...  So the BREAK or CONT should
//	 be able to reach the outer LOOP Lex.


    // SC_ParseSetOpState will set the correct Prec (OpPrec) and Assoc (OpState)
    // values for the Token.
    //
    // NOTE:	':' appears only in Dict initialization ["First":"John", "Last":"Smith"]
    //		or as part of '?:' (Choose) operation.
    //
    //		'++' and '--' are Prefix (SC_PREPLUSPLUS and SC_PREMINMIN) or
    //		Postfix (SC_PLUSPLUS and SC_MINMIN).
    //
    //		=& (ALIAS) should have lower precedence than DOT and INDEX.
    //		Otherwise, "L =& MyDict.Field" becomes "(L =& MyDict).Field"!!
    //
    //		POPEN et al. should NOT to be processed when followed by a
    //		regular op.
    //
    //      	An Op can only be followed by another Prefix (unary) Op.  An Arg
    //		can be followed by a Postfix (unary) or a Binary Op.
    //
    //		SC_PBLOCK_STATE marks the Op for special treatment!
    //		Default is Binary and RAssoc.

    Int16	SC_AuxParseSetOpState(SC_TokenPointer TP)
    {
	#define	PSOP(_P, _S)   TP->OpPrec = (_P); TP->OpState = (_S); break

	switch (TP->Id) {
	    case SC_EQ_ID:	PSOP(14, SC_NO_STATE);			// Binary and RAssoc

	    case SC_LESS_ID:
	    case SC_GREAT_ID:	PSOP(6, SC_LASSOC_STATE);

	    case SC_PLUS_ID:
	    case SC_MINUS_ID:	PSOP(4, SC_LASSOC_STATE);

	    case SC_COMMA_ID:
	    case SC_SEMI_ID:	PSOP(15, SC_LASSOC_STATE);

	    case SC_DIV_ID:
	    case SC_MOD_ID:
	    case SC_MULT_ID:	PSOP(3, SC_LASSOC_STATE);

	    case SC_LOGNEG_ID:
	    case SC_BITNEG_ID:	PSOP(2, SC_UNARY_STATE | SC_PREFIX_STATE);

	    case SC_BITAND_ID:	PSOP(8, SC_LASSOC_STATE);
	    case SC_BITOR_ID:	PSOP(10, SC_LASSOC_STATE);
	    case SC_BITXOR_ID:	PSOP(9, SC_LASSOC_STATE);

	    case SC_CHOOSE_ID:	PSOP(13, SC_PBLOCK_STATE);		// ?: ternary
	    case SC_COLON_ID:	PSOP(16, SC_PBLOCK_STATE);		// For Choose, not Cons or Label

	    case SC_DOT_ID:
	    case SC_PCLOSE_ID:						// ')'
	    case SC_ACLOSE_ID:	PSOP(1, SC_LASSOC_STATE);		// ']'		
	    case SC_END_ID:	PSOP(1, SC_NOARG_STATE);		// '}'

	    case SC_POPEN_ID:	PSOP(1, SC_UNARY_STATE | SC_PREFIX_STATE | SC_PBLOCK_STATE);	// '('
	    case SC_AOPEN_ID:	PSOP(1, SC_LASSOC_STATE | SC_PBLOCK_STATE);			// '['
	    case SC_BEGIN_ID:	PSOP(1, SC_NOARG_STATE);		// '{'

	    // Unary version of Plus and Minus
	    case SC_UPLUS_ID:
	    case SC_UMINUS_ID:	PSOP(2, SC_UNARY_STATE | SC_PREFIX_STATE);

	    case SC_NULL_ID:	PSOP(1, SC_NOARG_STATE);
	    case SC_INDEX_ID:	PSOP(1, SC_LASSOC_STATE | SC_PBLOCK_STATE);			// SPECIAL
	    case SC_CALL_ID:									// SPECIAL
	    case SC_IN_ID:									// SPECIAL
	    case SC_OUT_ID:	PSOP(1, SC_LASSOC_STATE | SC_NOCOMMA_STATE | SC_PBLOCK_STATE);	// SPECIAL
	    case SC_VAR_ID:	PSOP(0, SC_START_STATE | SC_NOCOMMA_STATE);
	    case SC_DEF_ID:	PSOP(0, SC_START_STATE | SC_NOCOMMA_STATE);
	    case SC_CONS_ID:	PSOP(1, SC_UNARY_STATE | SC_PREFIX_STATE | SC_NOCOMMA_STATE | SC_PBLOCK_STATE);
	    case SC_PAIR_ID:	PSOP(15, SC_LASSOC_STATE);		// : for Dict cons
	    case SC_LABEL_ID:	PSOP(1, SC_UNARY_STATE);
	    case SC_GOTO_ID:	PSOP(1, SC_UNARY_STATE | SC_PREFIX_STATE | SC_FINAL_STATE);
	    case SC_CASE_ID:	PSOP(15, SC_UNARY_STATE | SC_PREFIX_STATE);
	    case SC_DEFAULT_ID:	PSOP(15, SC_NOARG_STATE);
	    case SC_FOR_ID:
	    case SC_WHILE_ID:
	    case SC_IF_ID:
	    case SC_SWITCH_ID:	PSOP(0, SC_UNARY_STATE);
	    case SC_DO_ID:						// No args, no prec, no state!
	    case SC_DOCOND_ID:						// Will never see
	    case SC_ELSE_ID:						// No args, no prec, no state!
	    case SC_BREAK_ID:						// No args, no prec, no state!
	    case SC_CONT_ID:	PSOP(0, SC_NOARG_STATE);		// No args, no prec, no state!
	    case SC_FUNC_ID:	PSOP(0, SC_START_STATE);
	    case SC_FDECL_ID:   PSOP(0, SC_NOCOMMA_STATE);
	    case SC_RETURN_ID:  // Same as Yield
	    case SC_YIELD_ID:	PSOP(0, SC_UNARY_STATE);
	    case SC_FROM_ID:	PSOP(0, SC_UNARY_STATE | SC_PREFIX_STATE);
	    case SC_LOGPOS_ID:	PSOP(0, SC_UNARY_STATE);
	    case SC_UNPACK_ID:	PSOP(3, SC_UNARY_STATE | SC_PREFIX_STATE);

	    case SC_ALIAS_ID:	PSOP(2, SC_NO_STATE);			// Binary + RAssoc (Less than DOT and INDEX)
	    case SC_EQEQ_ID:
	    case SC_NEQ_ID:	PSOP(7, SC_LASSOC_STATE);

	    case SC_LESSEQ_ID:
	    case SC_GREATEQ_ID:	PSOP(6, SC_LASSOC_STATE);

	    case SC_LOGAND_ID:	PSOP(11, SC_LASSOC_STATE);
	    case SC_LOGOR_ID:	PSOP(12, SC_LASSOC_STATE);

	    case SC_SHL_ID:
	    case SC_SHR_ID:	PSOP(5, SC_LASSOC_STATE);

	    case SC_PLUSPLUS_ID:
	    case SC_MINMIN_ID:	PSOP(1, SC_LASSOC_STATE | SC_UNARY_STATE);	// Postfix

	    case SC_PLUSEQ_ID:
	    case SC_MINUSEQ_ID:
	    case SC_DIVEQ_ID:
	    case SC_MODEQ_ID:
	    case SC_MULTEQ_ID:
	    case SC_BITANDEQ_ID:
	    case SC_BITOREQ_ID:
	    case SC_BITXOREQ_ID: PSOP(14, SC_NO_STATE);			// Binary and RAssoc

	    // Prefix version of Incr and Decr
	    case SC_PREPLUSPLUS_ID:
	    case SC_PREMINMIN_ID: PSOP(2, SC_UNARY_STATE | SC_PREFIX_STATE);

	    case SC_SHLEQ_ID:
	    case SC_SHREQ_ID:	PSOP(14, SC_NO_STATE);
	    case SC_IDENT_ID:
	    case SC_NIDENT_ID:	PSOP(7, SC_LASSOC_STATE);

	    default: return 0;
	}
	return 1;

	#undef	PSOP
    }

    // SC_AuxParseForceOpUnary will Return 0 if Op (TP->Id) *CANNOT* be first in an
    // expression, so cannot function in a unary capacity).  Otherwise, returns 1
    // and changes the TP->Id to the unary isomorph if necessary.

    Int16	SC_AuxParseForceOpUnary(SC_TokenPointer TP)
    {
	switch (TP->Id) {
	    case SC_PLUS_ID: TP->Id = SC_UPLUS_ID; break;
	    case SC_MINUS_ID: TP->Id = SC_UMINUS_ID; break;

	    case SC_SEMI_ID:
	    case SC_LOGNEG_ID:
	    case SC_BITNEG_ID:
	    case SC_POPEN_ID:
	    case SC_AOPEN_ID:
	    case SC_BEGIN_ID:
	    case SC_FROM_ID:
	    case SC_UNPACK_ID: break;

	    case SC_PLUSPLUS_ID: TP->Id = SC_PREPLUSPLUS_ID; break;
	    case SC_MINMIN_ID: TP->Id = SC_PREMINMIN_ID; break;

	    case SC_CALL_ID:
	    case SC_IN_ID:
	    case SC_OUT_ID:
	    case SC_CONS_ID: break;

	    default: return 0;
	}

	TP->OpState = SC_UNARY_STATE;
	return 1;
    }

    // SC_AuxParseProcessTopOp pops the topmost Op from the OpStack and PROCESSes it.
    //
    // PROCESS an Op:
    //	1) Take matching Args from the ArgList
    //	2) Create a linked list (NextP) of the Args in proper order.
    //	3) Put the first Arg under (DownP) the Op.
    //	4) Place the Op (and its little ASL subtree) on the ArgList.
    //	   (Ready to be used by the next Op!)
    //
    Int16	SC_AuxParseProcessTopOp(SC_CGPointer CGP)
    {
	SC_TokenPointer	CurOpP;
	SC_TokenPointer	ArgP;
	Int16		DoArgCount = 2;		// Default
	char *		ErrorS = NULL;

	CurOpP = CGP->TopOpP;			// Pop off the stack
	CGP->TopOpP = CurOpP->NextP;

	if (CurOpP->Type == SC_START_TOKEN) {
	    DoArgCount = -1;			// Many!!
	    goto DoXfer;
	}

	switch (CurOpP->Id) {
	    case SC_CHOOSE_ID:	DoArgCount = 3; break;					// Ternary ?:

	    case SC_COLON_ID:	if (CGP->TopOpP->Id == SC_CHOOSE_ID) {
					// Part of "?:" ternary op.  Just zap the colon.
					SC_SlabZapItem(CGP->VMP->TokenSP, CurOpP, sizeof(SC_TokenRecord));
					return SC_SUCC_RES;
				    } else
					ErrorS = SC_STR_ErrorBadOp;
				    break;

	    case SC_POPEN_ID:	ErrorS = SC_STR_ErrorMissingPClose; break;		// Left (
	    case SC_AOPEN_ID:	ErrorS = SC_STR_ErrorMissingAClose; break;		// Left [
	    case SC_CALL_ID:	// Fall through
	    case SC_IN_ID:	// Fall through
	    case SC_OUT_ID:	ErrorS = SC_STR_ErrorMissingPClose; break;		// Left (
	    case SC_VAR_ID:	// Fall through
	    case SC_DEF_ID:	DoArgCount = -1; break;
	    case SC_CONS_ID:	ErrorS = SC_STR_ErrorMissingAClose; break;		// Left [
	    case SC_FUNC_ID:	DoArgCount = 3; break;
	    case SC_FDECL_ID:   DoArgCount = -1; break;
	    case SC_RETURN_ID:  // Same as YIELD
	    case SC_YIELD_ID:	DoArgCount = -1; break;
				// SC_AuxParseProcessIndex handles the NO ARG version itself.
				// Here, there can be 1 arg or 2 (if FROM form!)

	    default:		if (CurOpP->OpState & SC_NOARG_STATE)			// No args!
					DoArgCount = 0;
				    else if (CurOpP->OpState & SC_UNARY_STATE)		// Unary
					DoArgCount = 1;
				    break;
	}

	if (ErrorS)
	    SC_PARSEERROR(CGP, CurOpP->ReadPos, ErrorS);

    DoXfer:
	// DoArgCount == 0:   No Args to transfer.
	// DoArgCount > 0:	  Transfer this many, error if less!
	// DoArgCount < 0:	  Transfer ALL there is, if any.
	if (DoArgCount) {
	    while ((ArgP = CGP->LastArgP)) {
		CGP->LastArgP = ArgP->NextP;
		ArgP->NextP = CurOpP->DownP;	// Attach other args to it
		CurOpP->DownP = ArgP;		// Make new first arg
		if (--DoArgCount == 0) break;
	    }
	    if (DoArgCount > 0)
		SC_PARSEERROR(CGP, CurOpP->ReadPos, SC_STR_ErrorMissingArg);
	}

	// Now add PROCESSED Op to arg list
	CurOpP->NextP = CGP->LastArgP;
	CGP->LastArgP = CurOpP;
	return SC_SUCC_RES;

    ErrorOut:
	return SC_ERR_RES;
    }
    
    // SC_AuxParseProcessParen handles an Open/Close paren list.  This could be a
    // parenthesized Op, like "A = (A + B) * C; *OR* a function call!
    //
    // This function is called when a PCLOSE is encountered... its job is to go
    // through and process all the Ops left on the OpStack until it finds the
    // matching (CurOpP) POPEN.
    //
    // TP is the PCLOSE... it was never placed on the OpStack.
    //
    // NOTE: TP could be closing a function CALL (instead of a regular POpen).
    //	     In that case, the function call needs to suck up all args (call
    //	     params) until (and including) the function 'name', which is marked
    //	     with an SC_FUNC_STATE OpFlag.
    //
    //	     This is easier than setting a count in the CALL Op.  When an arg
    //	     comes in, it can be an arg to the function, OR part of what will
    //	     later get processed to become an arg:  so "Fn(A + B + C)" really
    //	     has just 1 parameter!
    //
    // NOTE: If the opening '(' had a START state, then should signal an END
    //	     when it is matched/closed.  This happens when IF, WHILE, etc. look
    //	     for a specific COND argument!
    //
    // NOTE: CALL is for a function call.
    //	     FUNC is for a function definition.

	// SC_AuxParsePPCALLOp handles processing the CALL op when a PCLOSE ')' is
	// encountered.  Creates an IN sub-op for the args.
	//
	// Keeps going on the Arg list UNTIL finds one with an SC_FUNC_STATE flag,
	// that will be the function Ref!  The args are removed from the Arg list
	// and attached to the CALL Op one by one.  Then the CALL Op and its subtree
	// (args) are placed on the Arg list, like every other processed Op!

	Int16	SC_AuxParsePPCALLOp(SC_CGPointer CGP, SC_TokenPointer CurOpP)
	{
	    SC_TokenPointer	CallArgP;	// Becomes the new linked list
	    SC_TokenPointer	LastArgP;	// From the Arg list
	    SC_TokenPointer	InP;		// In Op.
	    Uns8		IFIDCond = 0;

	    CGP->TopOpP = CurOpP->NextP;	// Pop CALL/OUT off the OpStack
	    CallArgP = NULL;
	    while (1) {
		// Detach the LastArg from the Arg list in CGP.
		if (! (LastArgP = CGP->LastArgP))	// Assign
		    SC_PARSEERROR(CGP, CurOpP->ReadPos, SC_STR_ErrorMissingArg);
		CGP->LastArgP = LastArgP->NextP;

		// Found the function ref itself (for CALL) or GUARD (for OUT)
		if (LastArgP->OpState & SC_FUNC_STATE) {
		    SC_PkgPointer	MainPkgP = CGP->VMP->PkgADP->Data[0];
		    char		PkgName[SC_CGMAXPKGNAMELEN+1];
		    Uns8		ResType;
		    Uns16		FuncId;
		    SC_TokenPointer	TP;
		    
		    // If a COND IFID, set a flag in the CALL!
		    // (COND IFID expect a body, do NOT require immediate SEMI_COLON.)
		    if ((LastArgP->Type == SC_OPERATOR_TOKEN) && (LastArgP->Id == SC_DOT_ID)) {
			// Check "Sys.MapI" Pkg.Func name format!
			TP = LastArgP->DownP;
			SC_UtilStashLCase(PkgName, CGP->ScriptStartP + TP->ReadPos, TP->ReadLen);
			if (0 == strcmp(SC_MainPkgName, PkgName)) {
			    // Right Pkg... now check if IFID
			    TP = TP->NextP;
			    FuncId = SC_CGGetSysFuncIndex(CGP, MainPkgP, TP, &ResType);
			    IFIDCond =  (ResType == SC_VAL_IIDX) && (0 <= FuncId) && (FuncId < SC_IFIDCondCount);
			}
		    } else if (LastArgP->Type == SC_IDENTIFIER_TOKEN) {
			// Check simple name Func format... i.e. "MapI"
			FuncId = SC_CGGetSysFuncIndex(CGP, MainPkgP, LastArgP, &ResType);
			TP = LastArgP;
			// The first N IFIDs are "Cond" type.
			IFIDCond =  (ResType == SC_VAL_IIDX) && (0 <= FuncId) && (FuncId < SC_IFIDCondCount);
		    }

		    // IFIDCond *MUST* be first... cannot call as part of another operation!
		    if ((IFIDCond) && (CGP->TopOpP->Type != SC_START_TOKEN))
			SC_PARSEERROR(CGP, TP->ReadPos, SC_STR_ErrorBadCall);
		    
		    break;
		}
		if (LastArgP->Id == SC_GUARD_ID) {
		    SC_SlabZapItem(CGP->VMP->TokenSP, LastArgP, sizeof(SC_TokenRecord));
		    break;
		}

		// Add LastArg to CallArgP--reversing order
		LastArgP->NextP = CallArgP;
		CallArgP = LastArgP;
	    }


	    if (CurOpP->Id == SC_CALL_ID) {
		InP = SC_TokenNew(CGP->VMP);
		if (IFIDCond) {
		    CurOpP->OpState |= SC_IFIDCOND_STATE;

		    // End of IN args... see if there is an OUT list?
		    if (!SC_TokenLookAhead(CGP, InP) ||		// Temp use here
			(InP->Type != SC_OPERATOR_TOKEN) ||
			(InP->Id != SC_POPEN_ID))
			    CurOpP->OpState |= SC_FINAL_STATE;
		}
		// Create an IN operator, and give it all the CALL args.
		InP->Type = SC_OPERATOR_TOKEN;
		InP->Id = SC_IN_ID;
		InP->DownP = CallArgP;

		// CALL gets the func Ref (LastArg) then IN args.
		LastArgP->NextP = InP;
		CurOpP->DownP = LastArgP;

		// Add the PROCESSED CALL op (subtree) to the arg list
		CurOpP->NextP = CGP->LastArgP;
		CGP->LastArgP = CurOpP;

	    } else {
		// OUT op if we get here.  Put Arg list in it.
		CurOpP->DownP = CallArgP;
		CurOpP->NextP = NULL;
		// Shove the OUT Op into the CALL, after FuncRec and INPUT
		// Set it as FINAL, so next '()' starts another CALL!
		CGP->LastArgP->DownP->NextP->NextP = CurOpP;
		CGP->LastArgP->OpState |= (CurOpP->OpState & SC_UNPACK_STATE)
					    ? SC_FINAL_STATE | SC_UNPACK_STATE
					    : SC_FINAL_STATE;
	    }
	    return SC_SUCC_RES;

	ErrorOut:
	    return SC_ERR_RES;
	}

	// SC_AuxParsePPFUNCOp processes IN and OUT for a FUNC (definition) Op when
	// PCLOSE ') is encountered.  CarOpP is either IN or OUT.

	Int16	SC_AuxParsePPFUNCOp(SC_CGPointer CGP, SC_TokenPointer CurOpP)
	{
	    SC_TokenPointer	CallArgP;	// Becomes the new linked list
	    SC_TokenPointer	LastArgP;	// From the Arg list

	    // Pop CurOpP off the stack
	    CGP->TopOpP = CurOpP->NextP;
	    CallArgP = NULL;
	    while (1) {
		// Detach the LastArg from Arg list in CGP.
		if (! (LastArgP = CGP->LastArgP))
		    SC_PARSEERROR(CGP, CurOpP->ReadPos, SC_STR_ErrorMissingArg);
		CGP->LastArgP = LastArgP->NextP;

		// Found the Guard?
		if (LastArgP->Id == SC_GUARD_ID) {
		    SC_SlabZapItem(CGP->VMP->TokenSP, LastArgP, sizeof(SC_TokenRecord));
		    break;
		}

		// Add new arg to CallArgP--reversing order
		LastArgP->NextP = CallArgP;
		CallArgP = LastArgP;
	    }

	    CurOpP->DownP = CallArgP;
	    // Add 'processed' Op to ArgList.
	    CurOpP->NextP = CGP->LastArgP;
	    CGP->LastArgP = CurOpP;

	    return SC_SUCC_RES;

	ErrorOut:
	    return SC_ERR_RES;
	}
	

    Int16	SC_AuxParseProcessParen(SC_CGPointer CGP, SC_TokenPointer TP, Int16 InGetFunc)
    {
	SC_TokenPointer	CurOpP;
	Int16		IsEnd;

	// Get rid of ')', will not use it!
	SC_SlabZapItem(CGP->VMP->TokenSP, TP, sizeof(SC_TokenRecord));

	// Look backwards through the Ops (on the stack) and process them
	while ((CurOpP = CGP->TopOpP)) {			// Get the top of the Op stack

	    if (CurOpP->Id == SC_POPEN_ID) {			// Found a matching '('
		IsEnd = (CurOpP->OpState & SC_START_STATE);	// If '(' started, end here!
		CGP->TopOpP = CurOpP->NextP;			// Pop POPEN Off the OpStack, zap it.
		SC_SlabZapItem(CGP->VMP->TokenSP, CurOpP, sizeof(SC_TokenRecord));

		return (IsEnd) ? SC_END_RES : SC_SUCC_RES;		// Done!
	    }

	    if (InGetFunc) {
		IsEnd = 0;
		if ((CurOpP->Id == SC_IN_ID) || (IsEnd = (CurOpP->Id == SC_OUT_ID))) {
		    if (SC_ERR_RES == SC_AuxParsePPFUNCOp(CGP, CurOpP))
			return SC_ERR_RES;
		    return (IsEnd) ? SC_END_RES : SC_SUCC_RES;
		}

	    } else if ((CurOpP->Id == SC_CALL_ID) || (CurOpP->Id == SC_OUT_ID)) {
		if (SC_ERR_RES == SC_AuxParsePPCALLOp(CGP, CurOpP))
		    return SC_ERR_RES;
		return SC_CALL_RES;
	    }

	    // Ran out, never matched the ')' !!
	    if (CurOpP->OpState & SC_START_STATE)
		SC_PARSEERROR(CGP, CurOpP->ReadPos, SC_STR_ErrorMissingPOpen);

	    // Process intervening Ops/Args... between '(' and ')'
	    if (SC_ERR_RES == SC_AuxParseProcessTopOp(CGP))
		goto ErrorOut;
	}
	return SC_SUCC_RES;

    ErrorOut:
	return SC_ERR_RES;
    }

    // SC_AuxParseProcessIndex is called when the ']' ACLOSE (TP) Op is found.  Its job
    // is to find the matching '[' AOPEN.  (Note TP was NEVER placed on OpStack.)
    //
    // In the case of Array indexing, the AOPEN becomes and INDEX Op and is processed
    // (along with Array ref arg and the index arg).
    //
    // In the case of Array constructor (decl/init), AOPEN becomes a CONS.
    //
    // NOTE:  CONS may also handle PAIRs for DICTs.  The parser does not care, but
    //	      CodeGen can differentiate ARRAYS vs DICTS and enforce discipline.

    Int16	SC_AuxParseProcessIndex(SC_CGPointer CGP,  SC_TokenPointer TP)
    {
	SC_TokenPointer	CurOpP;

	// Get rid of ']', will not use it!
	SC_SlabZapItem(CGP->VMP->TokenSP, TP, sizeof(SC_TokenRecord));

	while (CGP->TopOpP) {
	    CurOpP = CGP->TopOpP;			// Get top of the OpStack, do not POP yet!

	    if (CurOpP->OpState & SC_START_STATE)	// Ran out, never matched the ']'
		SC_PARSEERROR(CGP, CurOpP->ReadPos, SC_STR_ErrorMissingAOpen);

	    if (CurOpP->Id == SC_AOPEN_ID) {		// Found a matching '['
		CurOpP->Id = SC_INDEX_ID;		// Becomes SC_INDEX_ID, then process
		if (SC_ERR_RES == SC_AuxParseProcessTopOp(CGP))
		    goto ErrorOut;
		return SC_SUCC_RES;

	    } else if ((CurOpP->Id == SC_CONS_ID)) {	// '[' was Constructor (Array/Dict)

		SC_TokenPointer	CallArgP;
		SC_TokenPointer	LastArgP;

		CGP->TopOpP = CurOpP->NextP;	// Pop CONS off the OpStack.

		CallArgP = NULL;
		while (1) {
		    // Get the LastArg from the Arg list in CGP.
		    if (! (LastArgP = CGP->LastArgP))
			SC_PARSEERROR(CGP, CurOpP->ReadPos, SC_STR_ErrorMissingArg);
		    CGP->LastArgP = LastArgP->NextP;

		    // Done if this *IS* the GUARD
		    if (LastArgP->Id == SC_GUARD_ID) {
			SC_SlabZapItem(CGP->VMP->TokenSP, LastArgP, sizeof(SC_TokenRecord));
			break;
		    }

		    // Add LastArg to CallArgP chain--reversing order!
		    LastArgP->NextP = CallArgP;
		    CallArgP = LastArgP;
		}

		// Add arg list (Now in right order) to the CONS Op.
		CurOpP->DownP = CallArgP;
		// Add the PROCESSED CONS op (subtree) to the arg list.
		CurOpP->NextP = CGP->LastArgP;
		CGP->LastArgP = CurOpP;

		return SC_SUCC_RES;
	    }

	    // Process intervening Ops/Args... between '[' and ']'	
	    if (SC_ERR_RES == SC_AuxParseProcessTopOp(CGP))
		goto ErrorOut;
	}
	return SC_SUCC_RES;

    ErrorOut:
	return SC_ERR_RES;
    }

    // SC_AuxParseAddOpArg will create a new Op token with the given Id and add it (as
    // processed) to the ArgList.  It is typically used to create GUARD tokens (for
    // variable length lists) and NULL tokens (for missing args).

    void	SC_AuxParseAddOpArg(SC_CGPointer CGP, Uns8 Id)
    {
	SC_TokenPointer TP;

	TP = SC_TokenNew(CGP->VMP);
	TP->Type = SC_OPERATOR_TOKEN;
	TP->Id = Id;
	TP->NextP = CGP->LastArgP;
	CGP->LastArgP = TP;
    }

    // SC_ParseString simply places the String Arg on the ArgList.

    Int16	SC_AuxParseString(SC_CGPointer CGP,  SC_TokenPointer CurTP, Int16 Act)
    {
	if ((Act != SC_BEGIN_ACT) && (Act != SC_GETARG_ACT))
	    SC_PARSEERROR(CGP, CurTP->ReadPos, SC_STR_ErrorNotString);

	CurTP->NextP = CGP->LastArgP;		// Reverse linked list
	CGP->LastArgP = CurTP;
	return Act;

    ErrorOut:
	return SC_ERR_RES;
    }

    // SC_ParseNumber places the Number Arg on the ArgList.

    Int16	SC_AuxParseNumber(SC_CGPointer CGP, SC_TokenPointer CurTP, Int16 Act)
    {
	if ((Act != SC_BEGIN_ACT) && (Act != SC_GETARG_ACT))
	    SC_PARSEERROR(CGP, CurTP->ReadPos, SC_STR_ErrorNotNumb);

	CurTP->NextP = CGP->LastArgP;
	CGP->LastArgP = CurTP;
	return Act;

    ErrorOut:
	return SC_ERR_RES;
    }

    // SC_AuxParseIdentifier handles new identifiers:
    // -- Simple Args are added to the ArgList... presumably var/parameter names.
    // -- Keywords are recognized and processed as OpS
    //
    // NOTE:  If InGetCond, we are parsing the condition for an If, While, for,
    //	      or Do.  So cannot have Goto, Label, Loop, etc.

	void	SC_AuxPIFormOp(SC_TokenPointer CurTP, Uns8 OpId)
	{
	    // Xform into proper Op.
	    CurTP->Type = SC_OPERATOR_TOKEN;
	    CurTP->Id = OpId;
	    SC_AuxParseSetOpState(CurTP);
	}

	void	SC_AuxPIPushOp(SC_CGPointer CGP, SC_TokenPointer CurTP, Uns8 OpId)
	{
	    // Xform into proper Op.
	    CurTP->Type = SC_OPERATOR_TOKEN;
	    CurTP->Id = OpId;
	    SC_AuxParseSetOpState(CurTP);
	    // Push on OpStack
	    CurTP->NextP = CGP->TopOpP;
	    CGP->TopOpP = CurTP;
	}

    Int16	SC_AuxParseIdentifier(SC_CGPointer CGP, SC_TokenPointer CurTP, Int16 Act,
			       Int16 IsFirstOp, Int16 InGetCond, Int16 InGetFunc, Int16 InReturn)
    {
	Uns8		OpId = 0;
	Uns8		Final = 0;
	Uns8		ForOp = 0;

	if ((Act != SC_BEGIN_ACT) && (Act != SC_GETARG_ACT))
	    SC_PARSEERROR(CGP, CurTP->ReadPos, SC_STR_ErrorNotIdent);

	// Just an arg?  Put on Arglist and get out quick.
	if (CurTP->Id == SC_NO_KEYCODE) {
	    CurTP->NextP = CGP->LastArgP;
	    CGP->LastArgP = CurTP;
	    return Act;
	}

	// NO Keywords allowed in INPUT/OUTPUT lists... except NULL/VOID.
	if (InGetFunc) {
	    if ((CurTP->Id == SC_NULL_KEYCODE) ||
		(CurTP->Id == SC_VOID_KEYCODE))
		goto DoNullOp;
	    else
		goto BadKeyword;
	}

	// Recognized keyword... process it.
	// Dispatch table, faster than a speeding IF/ELSE ladder.
	// (Keep same order as KeyCode.)
	switch (CurTP->Id) {
	    case SC_BREAK_KEYCODE:	OpId = SC_BREAK_ID; goto DoNoArgOp;
	    case SC_CASE_KEYCODE:	OpId = SC_CASE_ID; goto DoSingleOp;
	    case SC_CONT_KEYCODE:	OpId = SC_CONT_ID; goto DoNoArgOp;
	    case SC_DEF_KEYCODE:	OpId = SC_DEF_ID; goto DoDefOp;
	    case SC_DEFAULT_KEYCODE:	OpId = SC_DEFAULT_ID; goto DoSingleOp;
	    case SC_DO_KEYCODE:		OpId = SC_DO_ID, Final = 1; goto DoNoArgOp;
	    case SC_ELSE_KEYCODE:	OpId = SC_ELSE_ID, Final = 1; goto DoNoArgOp;
	    case SC_FOR_KEYCODE:	OpId = SC_FOR_ID, ForOp = 1; goto DoCondOp;
	    case SC_FROM_KEYCODE:	goto DoFromOp;
	    case SC_FUNCTION_KEYCODE:	goto DoFuncOp;
	    case SC_GOTO_KEYCODE:	OpId = SC_GOTO_ID; goto DoSingleOp;
	    case SC_IF_KEYCODE:		OpId = SC_IF_ID; goto DoCondOp;
	    case SC_NULL_KEYCODE:	goto DoNullOp;
	    case SC_RETURN_KEYCODE:	OpId = SC_RETURN_ID; goto DoReturnOp;
	    case SC_SWITCH_KEYCODE:	OpId = SC_SWITCH_ID; goto DoCondOp;
	    case SC_VAR_KEYCODE:	goto DoVarOp;
	    case SC_VOID_KEYCODE:	goto BadKeyword;
	    case SC_WHILE_KEYCODE:	OpId = SC_WHILE_ID; goto DoCondOp;
	    case SC_YIELD_KEYCODE:	OpId = SC_YIELD_ID; goto DoReturnOp;
	}
        goto BadKeyword;

    DoNullOp:					// **** NULL
	SC_AuxPIFormOp(CurTP, SC_NULL_ID);
	// Make it a processed Op, just an Arg!
	CurTP->NextP = CGP->LastArgP;
	CGP->LastArgP = CurTP;
	return Act;

    DoVarOp:
	if (! InGetCond && IsFirstOp) {		// **** VAR
	     SC_AuxPIFormOp(CurTP, SC_VAR_ID);
	     // Put this below previous START op, make it new Top.
	     CGP->TopOpP->DownP = CurTP;
	     CGP->TopOpP = CurTP;
	     return Act;
	 }
	 goto BadKeyword;

    DoDefOp:
	if (! InGetCond && IsFirstOp) {		// **** DEF
	     SC_AuxPIFormOp(CurTP, SC_DEF_ID);
	     // Put this below previous START op, make it new Top.
	     CGP->TopOpP->DownP = CurTP;
	     CGP->TopOpP = CurTP;
	     return Act;
	 }
	 goto BadKeyword;	 

    DoFromOp:
	if (InReturn) {
	    SC_AuxPIPushOp(CGP, CurTP, SC_FROM_ID);
	    return Act;
	}
	goto BadKeyword;

    DoSingleOp:					// **** GOTO, CASE, DEFAULT
	if (! InGetCond && IsFirstOp) {
	    SC_AuxPIPushOp(CGP, CurTP, OpId);
	    return Act;
	}
	goto BadKeyword;

    DoNoArgOp:
	if (! InGetCond) {			// **** DO, ELSE, BREAK, CONT...
	    SC_AuxPIPushOp(CGP, CurTP, OpId);    
	    if (Final) goto EndExit;		// Do not wait for SEMI!
	    return Act;
	}
	goto BadKeyword;

    DoReturnOp:					// RETURN and YIELD
	if (! InGetCond && IsFirstOp) {
	    SC_TokenPointer		OpTP = CurTP;

	    SC_AuxPIFormOp(OpTP, OpId);    	
	    // Now see what comes next... an expression or just SEMI?

	    CurTP = SC_TokenNew(CGP->VMP);
	    if (! SC_TokenGetNext(CGP, CurTP))
		SC_PARSEERROR(CGP, OpTP->ReadPos, SC_STR_ErrorBadEnd);

	    if (SC_CGIsOpTP(CurTP, SC_SEMI_ID)) {
	        // Handle the NO ARG version of RETURN and YIELD here!
		SC_SlabZapItem(CGP->VMP->TokenSP, CurTP, sizeof(SC_TokenRecord));
		// Call the OP processed, push it on ArgList
		OpTP->NextP = CGP->LastArgP;
		CGP->LastArgP = OpTP;

	    } else {
		// Push Op on the stack first
		OpTP->NextP = CGP->TopOpP;
		CGP->TopOpP = OpTP;

		// ***RECURSE*** to parse out the RETURN/YIELD expression!
		// NOTE:  Calling ParseExpression with PrevWasOp == 2 means InReturn!!
		if (SC_ERR_RES == SC_ParseExpr(CGP, CurTP, SC_GETCOND_ACT, 2))
		    goto ErrorOut;
	    }

	    goto EndExit;
	}
	goto BadKeyword;

    DoCondOp:				// **** FOR, IF, SWITCH, and WHILE
	if (! InGetCond) {
	    SC_TokenPointer		OpTP = CurTP;

	    SC_AuxPIPushOp(CGP, OpTP, OpId);
	    // Now get the condition

	    CurTP = SC_TokenNew(CGP->VMP);
	    if (! SC_TokenGetNext(CGP, CurTP))
		SC_PARSEERROR(CGP, OpTP->ReadPos, SC_STR_ErrorBadEnd);
	    if ((CurTP->Type != SC_OPERATOR_TOKEN) || (CurTP->Id != SC_POPEN_ID))
		SC_PARSEERROR(CGP, CurTP->ReadPos, SC_STR_ErrorBadCond);

	    // ***RECURSE*** to parse out the condition!
	    // NOTE:  Calling ParseExpression with PrevWasOp == 3 means InForOp!!
	    if (SC_ERR_RES ==  SC_ParseExpr(CGP, CurTP, SC_GETCOND_ACT, (ForOp) ? 3 : 1))
		goto ErrorOut;

	    if (ForOp) {
		// ArgList contains loop parameters.
		// Reverse their order and hang them under the FOR Op.
		while ((CurTP = CGP->LastArgP)) {	// Get last Arg
		    CGP->LastArgP = CurTP->NextP;	// Update Arg list
		    CurTP->NextP = OpTP->DownP;		// Link to existing Arg list
		    OpTP->DownP = CurTP;		// Make it first arg
		}

		// Pop the FOR Op off the OpStack, it is processed, put on ArgList
		CGP->TopOpP = OpTP->NextP;
		OpTP->NextP = CGP->LastArgP;
		CGP->LastArgP = OpTP;
	    }
	    goto EndExit;
	}
	goto BadKeyword;

    DoFuncOp:					// **** FUNC
	if (! InGetCond) {
	    Uns8			Id;
	    Uns32			ReadPos;

	    // Must have Name (or Ref) before IN and OUT.  Parsing ends after
	    // mandatory OUT list PCLOSE.
	    //
	    // "Function MyFunc(A, B, C)(D, E) {..."	FirstOp, has Name
	    // or
	    // "X = Function(A, B, C)(D, E) {..."	NOT FirstOp, use NULL as Name

	    SC_AuxPIPushOp(CGP, CurTP, SC_FUNC_ID);

	    if (! IsFirstOp) {
		// Nameless Lambda, use NULL as first arg!
		// Generally seen from inside an assignment... so NOT first op!
		SC_AuxParseAddOpArg(CGP, SC_NULL_ID);
	    } else {

		// Get the name
		ReadPos = CurTP->ReadPos;
		CurTP = SC_TokenNew(CGP->VMP);
		if (! SC_TokenGetNext(CGP, CurTP))
		    SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadEnd);

		ReadPos = CurTP->ReadPos;
		if (CurTP->Type != SC_IDENTIFIER_TOKEN)
		    SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadArg);
		if (CurTP->Id != SC_NO_KEYCODE)
		    SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadKeyword);

		// Add name to Arglist
		CurTP->NextP = CGP->LastArgP;
		CGP->LastArgP = CurTP;
	    }

	    // Get POpen for IN list!
	    ReadPos = CurTP->ReadPos;
	    CurTP = SC_TokenNew(CGP->VMP);
	    if (! SC_TokenGetNext(CGP, CurTP))
		SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadEnd);

	    ReadPos = CurTP->ReadPos;
	    if (CurTP->Type != SC_OPERATOR_TOKEN)
		SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadArg);

	    // This could be just a declaration!
	    if ((CurTP->Id == SC_COMMA_ID) || (CurTP->Id == SC_SEMI_ID)) {
		// Change TopOp from FUNC to FUNCDECL
		CGP->TopOpP->Id = SC_FDECL_ID;
		SC_AuxParseSetOpState(CGP->TopOpP);
		// Name is already on the ArgList... just zap COMMA or SEMI
		Id = CurTP->Id;
		SC_SlabZapItem(CGP->VMP->TokenSP, CurTP, sizeof(SC_TokenRecord));
		if (Id == SC_SEMI_ID)
		    goto EndExit;		// Done
		else
		    return Act;			// More Func names to declare
	    }

	    // Assuming this is a FUNC definition, not just a declaration!
	    if (CurTP->Id != SC_POPEN_ID)
		SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorMissingPOpen);

	    // Start the IN list, add a GUARD so it knows when to stop collecting args.
	    CurTP->Id = SC_IN_ID;
	    SC_AuxParseAddOpArg(CGP, SC_GUARD_ID);
	    // Process them...		(***RECURSE***)
	    if (SC_ERR_RES == SC_ParseExpr(CGP, CurTP, SC_GETFUNC_ACT, 0))
		    goto ErrorOut;

	    // ParseExpression will return with FUNC as TopOp and Name (Ref), In, and Out
	    // on the ArgList... in reverse order.  So assemble and process the FUNC op.
	    // SC_AuxParseProcessTop does all that!
	    goto EndExit;
	}
	goto BadKeyword;


    BadKeyword:	
	SC_PARSEERROR(CGP, CurTP->ReadPos, SC_STR_ErrorBadKeyword);

    EndExit:
	while (CGP->TopOpP) {
	    Final = (CGP->TopOpP->OpState & SC_START_STATE);
	    if (SC_ERR_RES == SC_AuxParseProcessTopOp(CGP))
		goto ErrorOut;
	    if (Final) break;
	}
	return SC_END_RES;

    ErrorOut:
	return SC_ERR_RES;
    }
    
// SC_ParseExpr is the main parse routine that dispatches to all others.
// While generally iterative, calls to SC_AuxParseIdentifier (keywords) may result
// in a *RECURSIVE* call back to SC_ParseExpr.
//
// NOTE:  Only place OPS on OpStack, NOT Identifiers, Id might collide!
//
// Binary flags: (Usage/definition)
//
//   IsFirstOp: 1 initially, 0 after first cycle.  Ensures we don't have
//		Keywords mid-expression, like a FOR loop inside a condition.
//
//   InGetCond: ParseExpr is called recursively with SC_GETCOND_ACT.  This Act
//		is *ONLY* set upon entry, is restored to SC_GETOP_ACT
//		SC_GETARG_ACT on the next cycle.
//
//   InGetFunc: ParseExpr is called recursively from ParseIdentifier to parse a
//		FUNCTION definition.  Act is reset on the very next cycle, so
//		capture recursion state in a var.
//
//   InCallOp:  Just processed a Func call... Func ref + args can be complex!
//
//   InReturn:  ParseExpr is called recursively to handle return expr.
//
//   GetFuncOut: Getting the OUTPUT list of a function definition... OUTPUT list
//		 is mandatory for a def (but can be (), (Null) or (Void)).
//
//   InForOp:	Called recursively to handle FOR loop clause "(init;cond;incr)".
//
//   InCaseOp:	
//   InDefaultOp: Called recursively from within a SWITCH.
//
//   IsPrefix:	Current op works as Prefix.
//
//   EmptyCall: Used to handle "()" for function call and definition.
//
// NOTE:  Too messy and too many tests.  Needs a rewrite!
//	  **********************************************************************
//	  Current design, is a bottom-up parser that has been extended with all
//	  manner of boolean predicates to handle complex operators.  Re-write
//	  with explicit top-down handlers for these complex operators.  So have
//	  an explicit ParseFuncDef routine instead of InGetFunc tests!

Int16	SC_ParseExpr(SC_CGPointer CGP, SC_TokenPointer CurTP, Int16 Act, Int16 PrevWasOp)
{
    SC_TokenPointer	PrevTP;
    Int16		IsPrefix, EmptyCall, IsFirstOp, Done;
    Int16		InGetCond, InCaseOp, InDefaultOp, InForOp, InGetFunc;
    Int16		InReturn, GetFuncOut, InCallOp;
    Uns8		TopId;
    Uns32		ReadPos;
    // Caution converting booleans to Uns8... some are set from Int16 flags!

    Done = 0;
    IsFirstOp = 1;
    InGetCond = (Act == SC_GETCOND_ACT);
    InGetFunc = (Act == SC_GETFUNC_ACT);
    InReturn = (PrevWasOp == 2);
    InForOp = (PrevWasOp == 3);
    InCaseOp = InDefaultOp = 0;
    InCallOp = GetFuncOut = IsPrefix = EmptyCall = 0;
    
    ReadPos = CurTP->ReadPos;			// For PARSEERROR
    if (CurTP->Type != SC_OPERATOR_TOKEN)
	goto ArgLoop;

OpLoop:
    // PRE-PROCESSING *****************************************************************

    if (InReturn && (CurTP->Type == SC_IDENTIFIER_TOKEN) && (CurTP->Id == SC_FROM_KEYCODE)) {
	while (CGP->TopOpP && (CGP->TopOpP->Id != SC_RETURN_ID) && (CGP->TopOpP->Id != SC_YIELD_ID)) {
	    if (SC_ERR_RES == SC_AuxParseProcessTopOp(CGP))
		goto ErrorOut;
	}
	// At this point there is FROM then RETURN/YIELD on Op stack... So make it an
	// OP and go get the next arg.
	SC_AuxPIPushOp(CGP, CurTP, SC_FROM_ID);
	PrevWasOp = 1;
	goto OpDone;
	
    } else if (CurTP->Type != SC_OPERATOR_TOKEN)
	SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorMissingOp);

    switch (CurTP->Id) {
	case SC_SEMI_ID:
	    if (InForOp) { 		// For uses SEMI as COMMA
		if (PrevWasOp) {	// Add NULL for empy arg in "For (;;)..."
		    SC_AuxParseAddOpArg(CGP, SC_NULL_ID);
		    PrevWasOp = 0;
		}
		break;
	    }

	    // a few checks
	    if (InGetCond && ! InReturn)
		SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadOp);
	    if (PrevWasOp && ! IsFirstOp)		// PostFix Op does not set PrevWasOp
		SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorMissingArg);
	    if (InCaseOp || InDefaultOp)
		SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadEnd);

	    // Dispose of SEMI Op... Expression is DONE!
	    SC_SlabZapItem(CGP->VMP->TokenSP, CurTP, sizeof(SC_TokenRecord));
	    Done = 1;
	    break;
	    
	case SC_COLON_ID:
	    // Could start with a LABEL ("Arg:").  Treat as a full expression!
	    // Also have "CASE Arg:" or "Default:" in Switch statement.
	    // Note:  CASE argument can be computed (A + B, etc.)
	    //	  But Default has NO args.

	    if (IsFirstOp && CGP->LastArgP) {
		// Should be a LABEL
		CurTP->Id = SC_LABEL_ID;	// Change to LABEL
		SC_AuxParseSetOpState(CurTP);	// Get params for it
		CurTP->NextP = CGP->TopOpP;	// Put on Op stack
		CGP->TopOpP = CurTP;
		Done = 1;

	    } else if (InCaseOp) {
		// Have "CASE arg:"
		if (! CGP->LastArgP)
		    SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorMissingArg);
		SC_SlabZapItem(CGP->VMP->TokenSP, CurTP, sizeof(SC_TokenRecord));
		Done = 1;

	    } else if (InDefaultOp) {
		// Have "Default:"
		if (CGP->LastArgP)
		    SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadArg);
		SC_SlabZapItem(CGP->VMP->TokenSP, CurTP, sizeof(SC_TokenRecord));
		Done = 1;
	    } break;
	    
	case SC_PCLOSE_ID:
	    // Allow EmptyCall when calling a function.
	    // SC_IN_ID is seen when *defining* a function... so allow with () definition instead of
	    // (NULL) or (VOID).
	    EmptyCall = (((CGP->TopOpP->Id == SC_CALL_ID) && (CGP->LastArgP->OpState & SC_FUNC_STATE)) ||
			 ((CGP->TopOpP->Id == SC_OUT_ID) && (CGP->LastArgP->Id == SC_GUARD_ID)) ||
			 ((CGP->TopOpP->Id == SC_IN_ID) && (CGP->LastArgP->Id == SC_GUARD_ID)));
	    if (InForOp && PrevWasOp) {		// Null arg in "For (;;)..."
		SC_AuxParseAddOpArg(CGP, SC_NULL_ID);
		PrevWasOp = 0;
	    } break;

	case SC_POPEN_ID:
	    if (InGetFunc) {
		if (SC_CGIsOpTP(CGP->LastArgP, SC_IN_ID)) {
		    // IN list is done... make an OUT
		    CurTP->Id = SC_OUT_ID;
		    // GUARD will be added in "POST PROCESS" phase
		}

	    }  else {
		if (! PrevWasOp) {
		    // '(' following an Arg is a function call... first Arg is Func Ref
		    // and can be complex... "A.B[x+y--](arg1, arg2);"
		    // Can have Fn(In1, In2)(Out1, Out2)... second '(' becomes OUT Op!
		    if ((CGP->LastArgP->Id == SC_CALL_ID) &&		// Last was CALL
			! (CGP->LastArgP->OpState & SC_FINAL_STATE))	// DOES not already have OUT
			CurTP->Id = SC_OUT_ID;
		    else
			CurTP->Id = SC_CALL_ID;		// Becomes CALL Op (lastArgP was func ref!)
		}
	    } break;

	case SC_AOPEN_ID:
	    if (PrevWasOp) {
		// '[' following an Op is an Array (or Dict) Constructor.
		if (CurTP->Id == SC_AOPEN_ID)
		    CurTP->Id = SC_CONS_ID;		// Becomes CONS Op
	    } break;
	
	case SC_BEGIN_ID:
	case SC_END_ID:
	    if (InGetCond || ! IsFirstOp)
		SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadOp);
	    if (CGP->LastArgP)
		SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadArg);
	    // Set up their state, then Push on OpStack
	    SC_AuxParseSetOpState(CurTP);
	    CurTP->NextP = CGP->TopOpP;
	    CGP->TopOpP = CurTP;
	    Done = 1;
	    break;

       case SC_UNPACK_ID:
	   // Note: SC_AuxParsePPCALLOp will transfer UNPACK flag from OUT to CALL.
	   //	    CALL Op is not immediately available from middle of OUTPUT list!
	   if ((CGP->TopOpP->Id == SC_CALL_ID) ||		// UNPACK flag in CALL
	       (CGP->TopOpP->Id == SC_OUT_ID))			// UNPACK flag in OUT, for now
	       CGP->TopOpP->OpState |= SC_UNPACK_STATE;
	   else
	      SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadOp);	// Can only be in INPUT or OUTPUT list!
	   break;
    }

    if (Done) {
	// Process remaining Ops on OpStack... then exit.
	// Stop at the first Op with START state.
	while (CGP->TopOpP) {
	    Done = (CGP->TopOpP->OpState & SC_START_STATE);
	    if (SC_ERR_RES == SC_AuxParseProcessTopOp(CGP))
		goto ErrorOut;
	    if (Done) break;
	}
 	return SC_END_RES;
    }
    
    // SYNTAX CHECK and set OpState ***************************************************

    // CurOp *MUST* be Unary version if at the top or expecting another arg!
    // Except if ')' after CALL with no args! (Or after empty OUT list.)
    if ((! EmptyCall) && ((Act == SC_BEGIN_ACT) || (PrevWasOp)))
	if (! SC_AuxParseForceOpUnary(CurTP))
	    SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorMissingArg);
    	
    // Get Prec/Assoc for current Op
    if (! SC_AuxParseSetOpState(CurTP))
	SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadOp);

    // If Prev was an Op, this must be a PREFIX Unary (Except if EmptyCall).
    // If Prev was an Arg, this can be BINARY or POSTFIX.
    IsPrefix = CurTP->OpState & SC_PREFIX_STATE;
    if (PrevWasOp) {
	if ((! EmptyCall) && (! IsPrefix))
    	    SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorMissingArg);
    } else
	if (IsPrefix) SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadOp);

    // SPECIAL processing for some delimiters *****************************************
    switch (CurTP->Id) {
	case SC_ACLOSE_ID:		 // ']'
	    if (SC_ERR_RES == SC_AuxParseProcessIndex(CGP, CurTP)) goto ErrorOut;
	    PrevWasOp = 0;
	    goto OpDone;

	case SC_PCLOSE_ID:		// ')'
	    // PClose Pops/Processes Ops off the stack until it hits a POpen.
	    // NOTE:  If SC_GETCOND_ACT, we want to exit here if Paren is closed!
	    //        **** SEE very next clause! ****
	    Act = SC_AuxParseProcessParen(CGP, CurTP, InGetFunc);
	    if (Act == SC_ERR_RES) goto ErrorOut;
	    else if (Act == SC_END_RES) return SC_SUCC_RES;
	    else if (Act == SC_CALL_RES) {
		// If this was end of IFIDCOND, just return!
		if ((CGP->LastArgP->OpState & SC_IFIDCOND_STATE) &&
		    (CGP->LastArgP->OpState & SC_FINAL_STATE)) {
 
		    if (SC_ERR_RES == SC_AuxParseProcessTopOp(CGP)) goto ErrorOut;
	            return SC_SUCC_RES;
		}

		InCallOp = 1;
	    }

	    if (InGetFunc) GetFuncOut = 1;

	    // otherwise, keep going!
	    PrevWasOp = 0;
	    goto OpDone;

	case SC_POPEN_ID:		// '('
	    // Open Paren... just push on OpStack for now.
	    // PrevWasOp Stays what it was!
	    CurTP->OpPrec = 0;		// Set here, to block processing
	    CurTP->NextP = CGP->TopOpP;
	    CGP->TopOpP = CurTP;

	    // **** Exit when this Paren is closed!! ****
	    // NOTE:  Check Act directly so applies to ONLY first '(' in COND.
	    //	      InGetCond will STAY 1 even after first '('!!
	    if (Act == SC_GETCOND_ACT) {
		if (! InReturn) CurTP->OpState |= SC_START_STATE;
	    }
	    goto OpDone;
    }
	
    // **** PROCESS PREVIOUS OPS***(Assoc + Prec)**************************************
    // ********************************************************************************
    PrevTP = CGP->TopOpP;
    if (! PrevTP || (PrevTP->OpState & SC_FINAL_STATE))
	SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadOp);

    // If Prev's OpPrec is 0, do NOT process it!
    if (CurTP->OpState & SC_LASSOC_STATE) {
	while (PrevTP->OpPrec && (PrevTP->OpPrec <= CurTP->OpPrec)) {
	    if (SC_ERR_RES == SC_AuxParseProcessTopOp(CGP)) goto ErrorOut;
	    PrevTP = CGP->TopOpP;
	}
    } else {
	while (PrevTP->OpPrec && (PrevTP->OpPrec < CurTP->OpPrec)) {
	    if (SC_ERR_RES == SC_AuxParseProcessTopOp(CGP)) goto ErrorOut;
	    PrevTP = CGP->TopOpP;
	}
    }

    // POST PROCESS *******************************************************************
    TopId = (CGP->TopOpP) ? CGP->TopOpP->Id : SC_NO_ID;
    switch (CurTP->Id) {
	case SC_COLON_ID:
	    // Xform COLON after CONS to PAIR
	    if (TopId == SC_CONS_ID) {
		CurTP->Id = SC_PAIR_ID;
		SC_AuxParseSetOpState(CurTP);	
	    } break;

	case SC_COMMA_ID:
	    // Ignore COMMA in VAR, CALL, IN, OUT, CONS, etc.
	    if (CGP->TopOpP->OpState & SC_NOCOMMA_STATE) {
		SC_SlabZapItem(CGP->VMP->TokenSP, CurTP, sizeof(SC_TokenRecord));
		CurTP = NULL;
		PrevWasOp = 1;
	    } break;

	case SC_SEMI_ID:
	    // Ignore SEMI in FOR loop parameter spec (like COMMA above)
	    if (InForOp) {
		SC_SlabZapItem(CGP->VMP->TokenSP, CurTP, sizeof(SC_TokenRecord));
		CurTP = NULL;
		PrevWasOp = 1;
	    }
	    break;

	case SC_UPLUS_ID:
	    // Does nothing... get rid of it.
	    SC_SlabZapItem(CGP->VMP->TokenSP, CurTP, sizeof(SC_TokenRecord));
	    CurTP = NULL;
	    PrevWasOp = 1;

	case SC_CALL_ID:	CGP->LastArgP->OpState |= SC_FUNC_STATE; break;

	case SC_OUT_ID:		// Fall through
	case SC_CONS_ID:	SC_AuxParseAddOpArg(CGP, SC_GUARD_ID); break;
    }

    // PUSH CurTP OP on the stack **************************************************************
    if (CurTP) {
	CurTP->NextP = CGP->TopOpP;
	CGP->TopOpP = CurTP;
	PrevWasOp = 1;

	// Block subsequent ops from process CurTP by setting its OpPrec to 0.
	if (CurTP->OpState & SC_PBLOCK_STATE)
	    CurTP->OpPrec = 0;
	// Postfix Unary does not mind being followed by another op!
	if ((CurTP->OpState & SC_UNARY_STATE) && !(CurTP->OpState & SC_PREFIX_STATE))
	    PrevWasOp = 0;	    
    }

OpDone:
    IsFirstOp = 0;	    // Seen Op, ready for next
    
    CurTP = SC_TokenNew(CGP->VMP);
    if (! SC_TokenGetNext(CGP, CurTP))
	SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadEnd);
    ReadPos = CurTP->ReadPos;

    // Added to avoid processing identifier after function call
    // "Func2(arg1) A...".  Was getting confused by missing ";"!!
    if (InCallOp) {
	if (CurTP->Type != SC_OPERATOR_TOKEN)
	    SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorMissingSemi);
	InCallOp = 0;
    }

    if (GetFuncOut) {
	if ((CurTP->Type != SC_OPERATOR_TOKEN) || (CurTP->Id != SC_POPEN_ID))
	    SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorMissingOutput);
       GetFuncOut = 0;
    }

    // ARGS ***************************************************************************
    
ArgLoop:
    // Do not Zap CurTP, as it will be used after the switch!
    Act = SC_GETARG_ACT;
    switch (CurTP->Type) {
	case SC_STRING_TOKEN:
	    Act = SC_AuxParseString(CGP, CurTP, Act);
	    break;

	case SC_NUMBER_TOKEN:
	    Act = SC_AuxParseNumber(CGP, CurTP, Act);
	    break;

	case SC_OPERATOR_TOKEN:
	    goto OpLoop;

	case SC_IDENTIFIER_TOKEN:
	    Act = SC_AuxParseIdentifier(CGP, CurTP, Act, IsFirstOp, InGetCond, InGetFunc, InReturn);
	    break;
    }

    // Some keywords turn into operators that want immediate END/exit!
    if (Act == SC_ERR_RES) goto ErrorOut;
    if (Act == SC_END_RES) {
	while (CGP->TopOpP)
	    if (SC_ERR_RES == SC_AuxParseProcessTopOp(CGP))
		goto ErrorOut;
	return SC_SUCC_RES;
    }

    // Keywords usually turn into Ops... 
    if ((PrevWasOp = (CurTP->Type == SC_OPERATOR_TOKEN))) {
	if (IsFirstOp) {
	    InCaseOp = CGP->TopOpP->Id == SC_CASE_ID;
	    InDefaultOp = CGP->TopOpP->Id == SC_DEFAULT_ID;
	} else
	    InCaseOp = InDefaultOp = 0;
	IsFirstOp = 0;		// Seen Op, ready for next!
	
	// In case of POSTFIX Unary and NOARG ops, PrevWasOp = 0, do not demand an Arg!!
	if (((CurTP->OpState & SC_UNARY_STATE) && ! (CurTP->OpState & SC_PREFIX_STATE)) ||
	    (CurTP->OpState & SC_NOARG_STATE))
	    PrevWasOp = 0;
    }
    
    CurTP = SC_TokenNew(CGP->VMP);
    if (! SC_TokenGetNext(CGP, CurTP))
	SC_PARSEERROR(CGP, ReadPos, SC_STR_ErrorBadEnd);
    ReadPos = CurTP->ReadPos;

    Act = SC_GETOP_ACT;
    if (PrevWasOp)
	goto ArgLoop;
    else
	goto OpLoop;

ErrorOut:
    return SC_ERR_RES;
}


// ********************************************************************************
// LEX (Keeps track of code blocks)

// SC_CGLexNew creates a new LexRecord (similar to TokenRecord).
//
// These are disposed of using SC_SlabZapItem, as they are allocated with
// SC_SlabAllocEntry.

SC_LexPointer		SC_CGLexNew(SC_VMPointer VMP)
{
    SC_SlabPointer	SlabP = VMP->LexSP;
    SC_LexPointer	LexP;
    Uns64		Max, Count;

    LexP = (SC_LexPointer)SC_SlabAllocEntry(SlabP, sizeof(SC_LexRecord));
    if (LexP == NULL) {
	// Need a new slab... calculate how big!
	Count = VMP->LexSlotAlloc + SC_LEXEXTRASLOTCOUNT;
	Max = SC_MAXSLABDATASIZE / sizeof(SC_LexRecord);
	if (Count > Max) Count = Max;

	VMP->GCSize += SC_SlabCreate(&VMP->LexSP, Count * (sizeof(SC_LexRecord)), SC_SLAB_LEX, SC_SLAB_NO_FLAG);
	VMP->LexSlotAlloc += Count;
	SlabP = VMP->LexSP;
	LexP = (SC_LexPointer)SC_SlabAllocEntry(SlabP, sizeof(SC_LexRecord));
	G_ASSERT(LexP != NULL, "Failed Lex slab!", 0);
    }

    LexP->Flags = 0L;
    LexP->BLevel = LexP->FLevel  = 0;
    LexP->TrueL = LexP->FalseL = LexP->ContL = 0L;
    LexP->Data1 = LexP->Data2 = 0;
    LexP->SupP = NULL;
    LexP->MapP = NULL;
    LexP->VDictP = LexP->DDictP = NULL;
    LexP->StashTP = NULL;

    SC_DEBUG_CHECKSLAB(SlabP);
    return LexP;
}

SC_VexPointer		SC_CGVexNew(SC_VMPointer VMP)
{
    SC_SlabPointer	SlabP = VMP->VexSP;
    SC_VexPointer	VexP;
    Uns64		Max, Count;

    VexP = (SC_VexPointer)SC_SlabAllocEntry(SlabP, sizeof(SC_VexRecord));
    if (VexP == NULL) {
	// Need a new slab... calculate how big!
	Count = VMP->VexSlotAlloc + SC_VEXEXTRASLOTCOUNT;
	Max = SC_MAXSLABDATASIZE / sizeof(SC_VexRecord);
	if (Count > Max) Count = Max;

	VMP->GCSize += SC_SlabCreate(&VMP->VexSP, Count * (sizeof(SC_VexRecord)), SC_SLAB_VEX, SC_SLAB_NO_FLAG);
	VMP->VexSlotAlloc += Count;
	SlabP = VMP->VexSP;
	VexP = (SC_VexPointer)SC_SlabAllocEntry(SlabP, sizeof(SC_VexRecord));
	G_ASSERT(VexP != NULL, "Failed Vex slab!", 0);
    }

    VexP->Flags = SC_VEXNOFLAG;
    VexP->BLevel = 0;
    VexP->Class = VexP->SrcClass = SC_NOVEX;
    VexP->Index = VexP->SrcIndex = 0;
    VexP->NextP = NULL;

    SC_DEBUG_CHECKSLAB(SlabP);
    return VexP;
}

SC_MapPointer	SC_CGMapNew(SC_VMPointer VMP)
{
    SC_SlabPointer	SlabP = VMP->MapSP;
    SC_MapPointer	MapP;
    Uns64		Max, Count;

    MapP = (SC_MapPointer)SC_SlabAllocEntry(SlabP, sizeof(SC_MapRecord));
    if (MapP == NULL) {
	// Need a new slab... calculate how big!
	Count = VMP->MapSlotAlloc + SC_MAPEXTRASLOTCOUNT;
	Max = SC_MAXSLABDATASIZE / sizeof(SC_MapRecord);
	if (Count > Max) Count = Max;

	VMP->GCSize += SC_SlabCreate(&VMP->MapSP, Count * (sizeof(SC_MapRecord)), SC_SLAB_MAP, SC_SLAB_NO_FLAG);
	VMP->MapSlotAlloc += Count;
	SlabP = VMP->MapSP;
	MapP = (SC_MapPointer)SC_SlabAllocEntry(SlabP, sizeof(SC_MapRecord));
	G_ASSERT(MapP != NULL, "Failed Map slab!", 0);
    }

    MapP->Flags = 0; MapP->SwitchCount = 0;
    MapP->InCount = MapP->OutCount = 0;
    MapP->RCount = MapP->LCount = MapP->CCount = MapP->GCount = 0;
    MapP->FuncId = MapP->CodeBytes = 0L;
    MapP->CodeCount = MapP->DispCount = 0L;
    MapP->VarCount = MapP->LabelCount = MapP->KonstCount = MapP->StringCount = 0L;
    MapP->FuncCount = 0L;
    MapP->CodeId = VMP->LastCodeId++;
    MapP->SupFLexP = NULL;
    MapP->CodeADP = MapP->LabelADP = MapP->KonstADP = MapP->StringADP = MapP->FuncADP = NULL;
    MapP->LabelDictP = MapP->KonstDictP = MapP->StringDictP = MapP->CaseDictP = NULL;
    MapP->RVexP = MapP->LVexP = MapP->CVexP = MapP->GVexP = NULL;

    return MapP;
}

    void	SC_AuxCGZapVexChain(SC_VMPointer VMP, SC_VexPointer VexP)
    {
	SC_VexPointer	NextVexP;

	while (VexP) {
	    NextVexP = VexP->NextP;
	    SC_SlabZapItem(VMP->VexSP, VexP, sizeof(SC_VexRecord));
	    VexP = NextVexP;
	}
    }

void		SC_CGMapZap(SC_VMPointer VMP, SC_MapPointer MapP)
{
    if (MapP->LabelDictP) SC_DictPurge(VMP, MapP->LabelDictP, 0);	// Purge, has Str refs
    if (MapP->KonstDictP) SC_DictPurge(VMP, MapP->KonstDictP, 1);	// ForceZap, no Var refs
    if (MapP->StringDictP) SC_DictPurge(VMP, MapP->StringDictP, 0);	// Purge, has Str refs
    if (MapP->CaseDictP) SC_DictPurge(VMP, MapP->CaseDictP, 1);		// ForceZap, but would have been stashed in CBP

    if (MapP->CodeADP) SC_SlabZapItem(VMP->ArrDataSP, MapP->CodeADP, MapP->CodeADP->BlockLen);
    if (MapP->LabelADP) SC_SlabZapItem(VMP->ArrDataSP, MapP->LabelADP, MapP->LabelADP->BlockLen);
    if (MapP->KonstADP) SC_SlabZapItem(VMP->ArrDataSP, MapP->KonstADP, MapP->KonstADP->BlockLen);
    if (MapP->StringADP) SC_SlabZapItem(VMP->ArrDataSP, MapP->StringADP, MapP->StringADP->BlockLen);
    if (MapP->FuncADP) SC_SlabZapItem(VMP->ArrDataSP, MapP->FuncADP, MapP->FuncADP->BlockLen);

    // NOTE: Lex that owns this Map will have a DictP that refers to these, but
    //	     will ignore them when zapping (DeRef) DictP.
    SC_AuxCGZapVexChain(VMP, MapP->RVexP);
    SC_AuxCGZapVexChain(VMP, MapP->LVexP);
    SC_AuxCGZapVexChain(VMP, MapP->CVexP);
    SC_AuxCGZapVexChain(VMP, MapP->GVexP);

    SC_SlabZapItem(VMP->MapSP, MapP, sizeof(SC_MapRecord));
}

// ********************************************************************************
// ********************************************************************************
// ********************************************************************************
//
// Global Variables
//
// The current limit is SC_CGMAXGLOBALCOUNT (65536) globals.  These will all be
// defined within the TOP function.  At runtime, they will be allocated and
// stashed within the GlobP in the ScriptP.
//
// Globals CANNOT be read/written directly at runtime.  Bytecodes indicate
// variables with a one-byte Index, so can only manipulate the runtime stack and
// less than 255 local vars.  The solution is to "alias" (RSET) the global var
// with a reserved (local) REG, which is addressed like any other local.  This
// way, the REG gets the same Entry (storage) as the Global, so Bytecodes
// read/write the global using the REG var index.  There are SC_CGMAXREGCOUNT
// (7) current REG for this purpose, and a compile time LRU rotation is employed
// to reuse REG assignments and minimizing calls to SET.
//
// Another approach would be to read/write globals to and from the stack.  But
// this is less efficient and defeats the parsimony of ternary ops.  Another
// alternate would employ 2-byte var indexing, perhaps using up to 7 bytes of
// data in ByteCodes for 64-bit ops...
//
// At compile time, each declared global gets a GVex in the MAP for the TOP
// function.  GVex.Index is critical, as it denotes the Index used to retrieve
// the global at runtime.
//
// Each sub-FUNC that uses a global variable gets a new GVex for it in its own
// Map.  These sub-GVex serve two functions: 1) they form a cache of "accessed"
// globals reducing the need to search up the LEX/MAP hierarchy again and again
// and 2) identify the global by using the GVex.SrcIndex from the TOP MAP.
//
// NOTE: The GVex to REG mapping occurs in each FUNC.  But the mapping gets
//       perturbed when jumping back in loops, as the REG will be carrying a
//       later mapping in code that would otherwise assume a previous mapping.
//       The solution is to invalidate and re-establish the mapping at every
//       JUMP target (easy to identify with its requisite labels).  There is a
//       similar problem executing conditional code (if/else etc.) as subsequent
//       segments cannot be sure of REG mapping.


// SC_CGRegCache will stuff the Global var into a local register, so it can be
// used and manipulated, like a local var, with a 1-byte index.  This is needed
// because there can be more than 256 globals.  Local and Closed variables are
// already available, but Globals must be "SET" into a register so they share
// the same storage space (Var Entry).
//
// This function returns whether SET ByteCode is needed:
//       0 --> Had it already.  Just use the RVar in ByteCode.
//       1 --> Just stashed it.  *MUST* generate SET ByteCode!!
//
// NOTE: Only SC_CGMAXREGCOUNT registers are allowed.  MapP->VarCount does NOT
//	 initially include the RegCount, these are added at the end.  Functions
//	 that access few (or no) globals will have fewer (or no) registers.
//
//       RVexP->SrcIndex will be the GVexP->Index (Index/Id)... says WHICH global!
//	 RVexP->Index is the Reg's Index/Id... says WHICH Reg!
//	 (All other Vex use VexP->Index to say which Var!)
//
//       RVex are simply allocated and chained together in use order.  When
//	 looking for a match, these are linearly searched... so compiler slows
//	 if SC_CGMAXREGCOUNT is too big.  But it must be *AT LEAST* 3, since a
//	 single OP may involve three globals (A = B + C)!
//
//	 CAUTION: Conditional blocks:
//
//	     Register assignments in a conditional block (IF/ELSE/Etc.) are
//	     necessarily valid *ONLY* within that block.
//
//	 CAUTION: Named Labels:
//
//	     Named (explicit) labels are the target of GOTO (JMP) Ops from
//	     within the function (Even TOP global).  This JMP can occur from
//	     before RVex assignment or after they were re-assigned.  Therefore,
//	     Named Labels will invalidate the RVex cache for subsequent GLOBAL
//	     access.
//
//	 CAUTION: Loops:
//
//	     Loops have a similar, albeit more limited, problems:
//	     1) RE-SET:     Loops JMP back, so REG assumptions are invalidated.
//	     2) First SET:  Even the first set may be repeated N times!
//
//	 It is possible to optimize REG setting by "hoisting" non-conflicting
//	 assignments to function initialization.  This will also avoid repeated
//	 reloads in loop blocks.  But this can only be done in the final ASM
//	 phase, as CodeGen only sees one expression at a time!
//
//	 Register allocation/coloring is a good optimization target for later
//	 development as current strategies are quite simplistic.  Fortunately,
//	 ALIAS Op ("=&") for explicitly SETting globals mitigates this need.

Int16		SC_CGRegCacheSet(SC_CGPointer CGP, SC_MapPointer MapP, SC_VexPointer GVexP)
{
    Uns32		GIndex = GVexP->Index;		// Order of global in TOP FUNC.
    SC_VexPointer	RVexP, PrevRVexP;

    // See if already stashed in RVar.
    PrevRVexP = NULL;
    if ((RVexP = MapP->RVexP)) {
	while (1) {
	    if (RVexP->SrcIndex == GIndex) {
		// Found it, Move to head of the list!
		if (PrevRVexP) {
		    PrevRVexP->NextP = RVexP->NextP;
		    RVexP->NextP = MapP->RVexP;
		    MapP->RVexP = RVexP;
		}
		// If not valid, SET it again!!
		if (RVexP->Flags & SC_VEXINVALFLAG) {
		    RVexP->Flags &= ~SC_VEXINVALFLAG;
		    RVexP->BLevel = CGP->LastLexP->BLevel;
		    return 1;	// Set it again
		}
		return 0;
	    }
	    
	    if (RVexP->NextP == NULL) break;
	    PrevRVexP = RVexP;
	    RVexP = RVexP->NextP;
	} 
    }

    // Did NOT find it... so make the assignment now!
    if (MapP->RCount < SC_CGMAXREGCOUNT) {
	// Room to just add it!	***NOT** included in VarCount until the end!
	RVexP = SC_CGVexNew(CGP->VMP);
	RVexP->Class = SC_RVEX;
	RVexP->Index = MapP->RCount++;
    } else {
	// Just recycle the last (LRU) RVexP.
	PrevRVexP->NextP = NULL;
	RVexP->Flags = SC_VEXNOFLAG;
    }
    RVexP->BLevel = CGP->LastLexP->BLevel;
    RVexP->SrcIndex = GIndex;
    RVexP->NextP = MapP->RVexP;
    MapP->RVexP = RVexP;
    return 1;
}

// SC_CGRegCacheGet checks to see if GVex is IN the cache:
//   Return 1:	Found it and valid, so move to head of the queue.
//   Return 0:  Not in cache (or not valid).
//
// This function is called if the GVex can be used without the Reg!

Int16		SC_CGRegCacheGet(SC_CGPointer CGP, SC_MapPointer MapP, SC_VexPointer GVexP)
{
    Uns32		GIndex = GVexP->Index;		// Order of global in TOP FUNC.
    SC_VexPointer	RVexP, PrevRVexP;

    // See if already stashed in RVar.
    PrevRVexP = NULL;
    if ((RVexP = MapP->RVexP)) {
	while (1) {
	    if (RVexP->SrcIndex == GIndex) {
		// Found it... give up if not valid!
		if (RVexP->Flags & SC_VEXINVALFLAG) break;

		// Move it to the head of the list.
		if (PrevRVexP) {
		    PrevRVexP->NextP = RVexP->NextP;
		    RVexP->NextP = MapP->RVexP;
		    MapP->RVexP = RVexP;
		}
		return 1;
	    }
	    
	    if (RVexP->NextP == NULL) break;
	    PrevRVexP = RVexP;
	    RVexP = RVexP->NextP;
	} 
    }

    return 0;	// Did not find it!
}


// SC_CGRegCacheInval is called to invalidate RVex mappings to GVar.  It will
// set the INVAL flag for all RVex (in MapP) that belong to a >= BLevel.
//
// USE BLEVEL = 0 TO INVALIDATE *ALL*
//
// This is used with (0 BLevel) after a named (explicit) Label is defined,
// since it can be the target of a GOTO.  This is also used when exiting a
// PROVISIONAL (If/Else/While/Switch/etc.) block, so all mappings established
// *in* that block (and its progeny) are invalidated.

void	SC_CGRegCacheInval(SC_CGPointer CGP, SC_MapPointer MapP, Uns8 BLevel)
{
    SC_VexPointer	RVexP = MapP->RVexP;

    while (RVexP) {
	if (RVexP->BLevel >= BLevel)
	    RVexP->Flags |= SC_VEXINVALFLAG;

	RVexP = RVexP->NextP;
    }
}

SC_PkgPointer	SC_CGGetPkg(SC_CGPointer CGP, SC_TokenPointer TP)
{
    char		PkgName[SC_CGMAXPKGNAMELEN + 1];
    SC_StrHPointer	StrHP;
    SC_PairHPointer	PairP;
    SC_PkgPointer	MainPkgP;

    // PKG Name len is limited!
    if ((TP->Type != SC_IDENTIFIER_TOKEN) || (TP->ReadLen > SC_CGMAXPKGNAMELEN))
	return NULL;

    // Pre-def PKG names are all lower case, lower Identifier for Case INsen match.
    SC_UtilStashLCase(PkgName, CGP->ScriptStartP + TP->ReadPos, TP->ReadLen);

    // Find PkgName in NDictP (Name space Dict) of MAIN (0) Pkg!
    MainPkgP = (SC_PkgPointer)CGP->VMP->PkgADP->Data[0];
    StrHP = SC_StrGetEntry(CGP->VMP, PkgName, TP->ReadLen);
	PairP = SC_DictGetPair(MainPkgP->NDictP, (SC_VarVPointer)StrHP, SC_VAL_PTR);
    SC_VARDEREF(CGP->VMP, (SC_VarVPointer)StrHP);		// Comes in +1, so -1 now	
    if (PairP && (PairP->ValType == SC_VAL_PIDX)) {
	SC_SAFE_CHECK((Int16)(Int64)PairP->ValueP < CGP->VMP->PkgCount, SC_BADRUN_Index);
	return (SC_PkgPointer)CGP->VMP->PkgADP->Data[(Int64)PairP->ValueP];
    }
    
    return NULL;
}

SC_PairHPointer		SC_CGGetPkgField(SC_CGPointer CGP, SC_PkgPointer PkgP, SC_TokenPointer FieldTP)
{
    char		FName[SC_CGMAXPKGFIELDNAMELEN + 1];
    SC_StrHPointer	StrHP;
    SC_PairHPointer	PairP;

    // Pkg Field name len is limited!
    if ((FieldTP->Type != SC_IDENTIFIER_TOKEN) || (FieldTP->ReadLen > SC_CGMAXPKGFIELDNAMELEN))
	return NULL;

    // Pre-def field names are all lower case, allows for case-INsensitive match.
    SC_UtilStashLCase(FName, CGP->ScriptStartP + FieldTP->ReadPos, FieldTP->ReadLen);

    // Find field pair in NDictP (Name space Dict) of PkgP
    StrHP = SC_StrGetEntry(CGP->VMP, FName, FieldTP->ReadLen);
	PairP = SC_DictGetPair(PkgP->NDictP, (SC_VarVPointer)StrHP, SC_VAL_PTR);
    SC_VARDEREF(CGP->VMP, (SC_VarVPointer)StrHP);
    return PairP;
}


Uns16		SC_CGGetSysFuncIndex(SC_CGPointer CGP, SC_PkgPointer PkgP, SC_TokenPointer TP, Uns8 * ResTypeP)
{
    SC_PairHPointer	PairP;

    PairP = SC_CGGetPkgField(CGP, PkgP, TP);
    if (PairP) {
	if (PairP->ValType == SC_VAL_FIDX) {
	    SC_SAFE_CHECK((Uns16)(Int64)PairP->ValueP < CGP->VMP->CFPCount, SC_BADRUN_Index);
	    *ResTypeP = SC_VAL_FIDX;
	    return (Uns16)(Int64)PairP->ValueP;

	} else if (PairP->ValType == SC_VAL_IIDX) {
	    SC_SAFE_CHECK((Uns16)(Int64)PairP->ValueP < SC_IFID_COUNT, SC_BADRUN_Index);
	    *ResTypeP = SC_VAL_IIDX;
	    return (Uns16)(Int64)PairP->ValueP;
	}
    }

    return (Uns16)-1;    
}

// TP is func CALL of the form:
//	"(Call (Dot ThePkg TheField) ...)" *OR*
//	"(Call FuncName ...):
//
// Xform it IN PLACE to
//	"(Call_S SIndex ...)" where Call_S has SC_SYSCALL_STATE flag!
// *OR*
//	"(Call_I SIndex ...)" where Call_I has SC_IFIDCALL_STATE flag!
//
// NOTE: IFID calls that are not assigned to an L-Value are STILL EXECUTED.
//	 Seems silly to do "Array(14)" if the new array is not stashed, still...

Int16		SC_CGXformSysCall(SC_CGPointer CGP, SC_TokenPointer TP, Uns16 SIndex, Uns8 SType)
{
    SC_VMPointer	VMP = CGP->VMP;
    SC_TokenPointer	FRefTP;				// FuncRef TP
    SC_TokenPointer	ArgTP;
    Int16		Count;

    FRefTP = TP->DownP;					// (Dot ThePkg TheField) or FuncName

    if (SType == SC_VAL_FIDX) {
	// SIndex is index of Sys Function (C function)
	TP->OpState |= SC_SYSCALL_STATE;
	
	if (SIndex == SC_SysGetArgCFIndex)
	    CGP->FuncLexP->MapP->Flags |= SC_MAP_ALLARGSFLAG;

	else if (SIndex == SC_SysPrintCFIndex) {
	    // Check arg count for SYS PRINT... going from FRef to IN then to its args.
	    ArgTP = FRefTP->NextP->DownP;
	    Count = 0;
	    while (ArgTP) {
		Count += 1;
		if (Count > 20)
		    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorTooManyArg);
		ArgTP = ArgTP->NextP;
	    }
	    if (Count == 0)
		SC_CODEERROR(CGP, TP, SC_STR_ErrorMissingArg);
	}

    } else {	// (SType == SC_VAL_IIDX)
		// SIndex is built-in IFID
	TP->OpState |= SC_IFIDCALL_STATE;
	if (TP->OpState & SC_UNPACK_STATE)
	    SC_CODEERROR(CGP, TP, SC_STR_ErrorIFIDUnPack);
    }

    // Want "(Call SIndex ....)" instead.  So alter FRefTP in place, zap what is below it!
    FRefTP->Type = SC_NUMBER_TOKEN;
    FRefTP->Id = SC_INTNUMB_ID;
    FRefTP->Data = (Uns64)SIndex;

    if (FRefTP->DownP) {
	SC_SlabZapItem(VMP->TokenSP, FRefTP->DownP->NextP, sizeof(SC_TokenRecord));
	SC_SlabZapItem(VMP->TokenSP, FRefTP->DownP, sizeof(SC_TokenRecord));
	FRefTP->DownP = NULL;
    }

    return SC_SUCC_RES;

ErrorOut:
    return SC_ERR_RES;
}


// ********************************************************************************
// Preprocess pass:  Go over the AST to verify, process, and simplify
// -- Verify format, args, and information.
// -- Simplify constant expressions: (+ 3 (* 2 5)) --> 30.
// -- Classify and locate vars: Local, Closed, Global, or Unknown.
// -- Locate Func being called (Global or Local)
// -- Signal errors immediately.
//
// Just before zapping a Lex... and when finishing the main TOP function.
// -- Do a CodePassCleanup to take care of final coding...
// -- Print the AST and CODE for it (DEBUG)
// -- Do the ASM phase... Generate and stash real ByteCodes.
// -- Print the ByteCodes (DEBUG)

    Int16		SC_AuxCGCPEndFunc(SC_CGPointer CGP, Int16 GotElse)
    {
	SC_LexPointer		LexP = CGP->LastLexP;

	do {
	    if (SC_ERR_RES == SC_CGCodePassCleanup(CGP, GotElse, 0)) goto ErrorOut;

	    // (If SC_DEBUG_FUNCPRINT is turned off, CBP will be UNUSED!!
	    #pragma GCC diagnostic push
	    #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
		if (LexP->Flags & SC_LEXFUNCFLAG) {
		    SC_CodeBlockPointer	CBP;
		
		    // Check that all labels were defined--FUNC only.
		    if (SC_ERR_RES == SC_CGCheckLabelDict(CGP, LexP)) goto ErrorOut;
		    if (SC_ERR_RES == SC_CGCheckVarDict(CGP, LexP)) goto ErrorOut;

		    // DisplayCode here... otherwise the FUNC will be gone!!
		    SC_DEBUG_CODEPRINT(CGP);
		    SC_DEBUG_ASTPRINT(CGP, NULL);
		    // This is a FUNC... Link and stash its ByteCodes before zapping!
		    CBP = SC_ASMPass(CGP, LexP->MapP);		// *************

		    SC_CHECKERROR(CGP);
		    SC_DEBUG_FUNCPRINT(CGP, CGP->ScriptP, CBP);
		}
	    #pragma GCC diagnostic pop

	    if ((GotElse && (LexP->Flags & SC_LEXIFFLAG)) || (LexP->Flags & SC_LEXDONEEDFLAG))
		break;		// DO NOT zap it!

	    if (LexP == CGP->FuncLexP) {
		// About to be zapped!
		SC_LexPointer		FLP = LexP->SupP;

		while (FLP && !(FLP->Flags & SC_LEXFUNCFLAG)) FLP = FLP->SupP;
		CGP->FuncLexP = FLP;
		CGP->DispLexP = FLP;
	    }
	    SC_CGZapLastLex(CGP);
	    LexP = CGP->LastLexP;

	} while (LexP && (LexP->Flags & SC_LEXFINFLAG));

	return SC_SUCC_RES;

    ErrorOut:
	return SC_ERR_RES;
    }


// SC_CGCheckPass will recursively descend into the TP tree (AST), check it for
// validity, make obvious (simplify/optimize) xformations and generate code on
// the way up/out.
//
// Caller of this function may choose to display the TP tree (AST) and the
// generated bytecode after this call.  That process needs to know what FuncLexP
// (and its MapP) the code is added to.
//
// NOTE: It might seem that A += B et al. should be converted to A = A + B, etc.
//	 But += is more efficient for complex/computed A, as in Array Index or
//	 Dict Dot... at least until we can recognize whether something like
//	 Arr[Index] has already been computed and stashed in a given register.

typedef		void (*SC_CGCheckMathFPointer)(SC_TokenPointer, Uns16);

Int16		SC_CGCheckPass(SC_CGPointer CGP, SC_TokenPointer TP, Int16 Act)
{
    SC_LexPointer		LexP = CGP->LastLexP;
    SC_TokenPointer		ArgTP;
    SC_CGCheckMathFPointer	MathFuncP;
    Int16			ArgCount, CheckAgain, MustBeNumb, DoLexFin, GotElse, IsDef;
    Uns16			ArgFlag;
    Int16			Res = SC_SUCC_RES;

    // return Res;		// Short circuit for debugging :-()

    // **ACTIONS***********************************************************************    

    DoLexFin = IsDef = 0;
    switch (Act) {
	case SC_CHECKTOP_ACT:		// Fresh entry, *NOT* recursion
	    // DoLexFin = LEXONE but not LEXFIN 
	    DoLexFin = ((LexP->Flags & (SC_LEXONEFLAG | SC_LEXFINFLAG)) == SC_LEXONEFLAG);

	    if (LexP->Flags & SC_LEXFINFLAG) {
		GotElse = TP && SC_CGIsOpTP(TP, SC_ELSE_ID);
		if (SC_ERR_RES == SC_AuxCGCPEndFunc(CGP, GotElse)) goto ErrorOut;
		LexP = CGP->LastLexP;	// Killed the previous one (maybe N)!
	    }
	    goto CheckEXPR;
	    
	case SC_CHECKEXPR_ACT:		// Recursive call
	    goto CheckEXPR;

	case SC_CHECKDEF_ACT:		// Checking (right side) a DEF declaration
	    IsDef = 1;
	    goto CheckEXPR;

	case SC_CHECKEND_ACT:		// Ending main _Global function!
	    // TP here is SC_FINALEND_TOKEN.
	    // Still missing a Semi.
	    if (CGP->Flags & SC_CGNEEDSEMIFLAG)
		SC_CODEERROR(CGP, TP, SC_STR_ErrorMissingSemi);

	    // Had DO but not WHILE yet
	    if (LexP->Flags & SC_LEXDONEEDFLAG)
		SC_CODEERROR(CGP, TP, SC_STR_ErrorMissingWhile);

	    // Check all LEX have not been closed/finished!
	    CGP->TopLexP->Flags |= SC_LEXFINFLAG;
	    while (LexP) {
		if (! (LexP->Flags & SC_LEXFINFLAG))
		    SC_CODEERROR(CGP, TP, SC_STR_ErrorBadEnd);
		LexP = LexP->SupP;
	    }

	    if (SC_ERR_RES == SC_AuxCGCPEndFunc(CGP, 0)) goto ErrorOut;
	    return SC_SUCC_RES;
	    
	default: break;
    }

CheckEXPR:

    // **PRE PROCESS*******************************************************************

    if (TP == NULL) {
	// NULL TP corresponds to standalone SEMI-COLON in script.
	CGP->Flags &= ~SC_CGNEEDSEMIFLAG;

	if (LexP->Flags & SC_LEXFINFLAG) {
	    if (LexP->Flags & SC_LEXDONEEDFLAG)
		SC_CODENULLERROR(CGP, SC_STR_ErrorMissingWhile);
	} else if ((LexP->Flags & SC_LEXIFFLAG) && (LexP->Flags & SC_LEXONEFLAG))
	    SC_CODENULLERROR(CGP, SC_STR_ErrorIfNotGuard);
	goto DoneDone;
	
    } else if (CGP->Flags & SC_CGNEEDSEMIFLAG)
	SC_CODEERROR(CGP, TP, SC_STR_ErrorMissingSemi);

    if ((LexP->Flags & SC_LEXDONEEDFLAG) && (LexP->Flags & SC_LEXFINFLAG)) {
	// Require terminating WHILE for DO
	if (SC_CGIsOpTP(TP, SC_WHILE_ID))
	    TP->Id = SC_DOCOND_ID, LexP->Flags &= ~SC_LEXDONEEDFLAG;
	else
	    SC_CODEERROR(CGP, TP, SC_STR_ErrorMissingWhile);
    }

    // **CHECK EXPR********************************************************************

    MustBeNumb = 0;
    if (IsDef) {
	// Supports:
	//   1)  Def #MyConst = 143;
	//   2)  Def #MyConst = #HerConst;
	//   3)  Def #MyConst = 23 * 11 + #HisConst;
    
	if (TP->Type == SC_IDENTIFIER_TOKEN) {
	    // (2) Declare a DEF Const based on another!
	    if (SC_ERR_RES == SC_CGResolveDef(CGP, TP))
		goto ErrorOut;

	} else if (TP->Type == SC_OPERATOR_TOKEN) {
	    switch (TP->Id) {
		// (3) Declare a DEF Const based on a "Pkg.Const"!
		case SC_DOT_ID:	    goto CheckIndex;		

		// (4) Declare a DEF Const as Operation on other Const!
		case SC_LESS_ID:    // Falls below
		case SC_GREAT_ID:   // Falls below
		case SC_PLUS_ID:    // Falls below
		case SC_MINUS_ID:   // Falls below

		case SC_DIV_ID:	    // Falls below
		case SC_MOD_ID:	    // Falls below
		case SC_MULT_ID:    // Falls below
		case SC_LOGNEG_ID:  // Falls below
		case SC_BITNEG_ID:  // Falls below
		case SC_BITAND_ID:  // Falls below
		case SC_BITOR_ID:   // Falls below
		case SC_BITXOR_ID:  // Falls below

		case SC_LOGPOS_ID:  // Falls below
		case SC_EQEQ_ID:    // Falls below
		case SC_NEQ_ID:	    // Falls below
		case SC_LESSEQ_ID:  // Falls below
		case SC_GREATEQ_ID: // Falls below
		case SC_LOGAND_ID:  // Falls below
		case SC_LOGOR_ID:   // Falls below
		case SC_SHL_ID:	    // Falls below
		case SC_SHR_ID:	    goto CheckMath;

		default:	    SC_CODEERROR(CGP, TP, SC_STR_ErrorBadArg);
	    }
	}

	goto DoneDone;
    }
    
    if (TP->Type == SC_IDENTIFIER_TOKEN) {
	// Stand-alone variable!
	if (SC_ERR_RES == SC_CGResolveVar(CGP, TP, 1))
	    goto ErrorOut;

    } else if (TP->Type == SC_OPERATOR_TOKEN) {
	switch (TP->Id) {
	    case SC_EQ_ID:	    goto CheckAssign;	// NOT MustBeNumb
	    case SC_LESS_ID:	    // Falls below
	    case SC_GREAT_ID:	    // Falls below
	    case SC_PLUS_ID:	    // Falls below
	    case SC_MINUS_ID:	    goto CheckMath;
	    case SC_COMMA_ID:	    goto CheckExprArgs;
	    
	    case SC_SEMI_ID:	    break;	// Might see NULL instead
	    
	    case SC_DIV_ID:	    // Falls below
	    case SC_MOD_ID:	    // Falls below
	    case SC_MULT_ID:	    // Falls below
	    case SC_LOGNEG_ID:	    // Falls below
	    case SC_BITNEG_ID:	    // Falls below
	    case SC_BITAND_ID:	    // Falls below
	    case SC_BITOR_ID:	    // Falls below
	    case SC_BITXOR_ID:	    goto CheckMath;
	    
	    case SC_CHOOSE_ID:	    goto CheckExprArgs;
	    case SC_COLON_ID:	    break;	// NEVER see
	    case SC_DOT_ID:	    goto CheckIndex;
	    case SC_PCLOSE_ID:	    break;	// NEVER see
	    case SC_ACLOSE_ID:	    break;	// NEVER see
	    case SC_END_ID:	    goto CheckEnd;
	    case SC_POPEN_ID:	    break;	// NEVER see
	    case SC_AOPEN_ID:	    break;	// NEVER see
	    case SC_BEGIN_ID:	    goto CheckBegin;
	    
	    case SC_UPLUS_ID:	    // Falls below
	    case SC_UMINUS_ID:	    goto CheckMath;
	    
	    case SC_NULL_ID:	    break;	// Nothing to do!
	    case SC_INDEX_ID:	    goto CheckIndex;
	    case SC_CALL_ID:	    goto CheckCall;
	    case SC_IN_ID:	    goto CheckExprArgs;
	    case SC_OUT_ID:	    goto CheckOut;
	    case SC_VAR_ID:	    goto CheckVarDecl;
	    case SC_DEF_ID:	    goto CheckDefDecl;
	    case SC_CONS_ID:	    goto CheckCons;
	    case SC_PAIR_ID:	    goto CheckExprArgs;
	    
	    case SC_LABEL_ID:	    goto CheckLabel;
	    case SC_GOTO_ID:	    goto CheckGoto;
	    case SC_CASE_ID:	    goto CheckCase;
	    case SC_DEFAULT_ID:	    goto CheckCase;
	    case SC_FOR_ID:	    ArgCount = 3; goto CheckLoop;
	    case SC_WHILE_ID:	    ArgCount = 1; goto CheckLoop;
	    case SC_IF_ID:	    goto CheckIf;
	    case SC_SWITCH_ID:	    goto CheckSwitch;
	    case SC_DO_ID:	    ArgCount = 0; goto CheckLoop;
	    case SC_DOCOND_ID:	    goto CheckDoCond;
	    case SC_ELSE_ID:	    goto CheckElse;
	    case SC_BREAK_ID:	    goto CheckBreak;
	    case SC_CONT_ID:	    goto CheckCont;
	    case SC_FUNC_ID:	    goto CheckFunc;
	    case SC_FDECL_ID:	    goto CheckFuncDecl;
	    case SC_RETURN_ID:	    goto CheckReturn;
	    case SC_YIELD_ID:	    goto CheckReturn;
	    case SC_FROM_ID:	    goto CheckFrom;
	    case SC_LOGPOS_ID:	    goto CheckMath;
	    case SC_UNPACK_ID:	    goto CheckUnPack;

	    case SC_ALIAS_ID:	    goto CheckAlias;
	    case SC_EQEQ_ID:	    // Falls below
	    case SC_NEQ_ID:	    // Falls below
	    case SC_LESSEQ_ID:	    // Falls below
	    case SC_GREATEQ_ID:	    // Falls below
	    case SC_LOGAND_ID:	    // Falls below
	    case SC_LOGOR_ID:	    // Falls below
	    case SC_SHL_ID:	    // Falls below
	    case SC_SHR_ID:	    goto CheckMath;
	    
	    case SC_PLUSPLUS_ID:    // Falls below
	    case SC_MINMIN_ID:	    goto CheckUnaryVarArg;
	    
	    case SC_PLUSEQ_ID:	    // all need MustBeNumb = 1
	    case SC_MINUSEQ_ID:	    // Falls below
	    case SC_DIVEQ_ID:	    // Falls below
	    case SC_MODEQ_ID:	    // Falls below
	    case SC_MULTEQ_ID:	    // Falls below
	    case SC_BITANDEQ_ID:    // Falls below
	    case SC_BITOREQ_ID:     // Falls below
	    case SC_BITXOREQ_ID:    MustBeNumb = 1; goto CheckAssign;
	    
	    case SC_PREPLUSPLUS_ID: // Falls below
	    case SC_PREMINMIN_ID:   goto CheckUnaryVarArg;
	    
	    case SC_SHLEQ_ID:	    // Falls below
	    case SC_SHREQ_ID:	    MustBeNumb = 1; goto CheckAssign;
	    
	    case SC_IDENT_ID:	    // Falls below
	    case SC_NIDENT_ID:	    goto CheckMath;
	    
	    default:		    break;
	}
    }

DoneDone:

    // Set LEXFIN flag for THIS cycle.
    if (DoLexFin) {
	SC_CGFinishLex(CGP, LexP);
	if ((LexP->Flags & SC_LEXSWITCHFLAG) && ! (LexP->Flags & SC_LEXCASEFLAG)) {
	    // May have NULL TP here... so use CODENULLERROR!   
	    SC_CODENULLERROR(CGP, SC_STR_ErrorMissingCase);
	}
    }

    return SC_SUCC_RES;

    // ********************************************************************************
    // **CHECK OPERATIONS**************************************************************
    
CheckAssign:
    // Check the LValue first... Must be a Var or Dict/Array index.
    // NOTE:  ++V or V++ (or -- version) are not LValues.
    ArgTP = TP->DownP;
    if (ArgTP->Type == SC_IDENTIFIER_TOKEN) {
	if (SC_ERR_RES == SC_CGResolveVar(CGP, ArgTP, 0))
	    goto ErrorOut;
	    
    } else if (ArgTP->Type == SC_OPERATOR_TOKEN) {
	if ((ArgTP->Id == SC_INDEX_ID) || (ArgTP->Id == SC_DOT_ID)) {
	    if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
		goto ErrorOut;
	} else
	    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);

    } else
	SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);

    // Now check the RValue... SC_CGResolveVar can *CHANGE* ArgTP if resolving Def!
    ArgTP = ArgTP->NextP;
    if (ArgTP->Type == SC_OPERATOR_TOKEN)
	Res = SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT);
    else if (ArgTP->Type == SC_IDENTIFIER_TOKEN)
	Res = SC_CGResolveVar(CGP, ArgTP, 1);
    if (Res == SC_ERR_RES) goto ErrorOut;

    // Check again here, in case DEF changed IDENTIFIER
    if (MustBeNumb && (ArgTP->Type != SC_NUMBER_TOKEN) &&
		      (ArgTP->Type != SC_IDENTIFIER_TOKEN) &&
		      (ArgTP->Type != SC_OPERATOR_TOKEN))
	SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);   

    // Check on += (et al.) functions...
    while (SC_CGXformExpr(CGP, TP))
	if (TP->Type != SC_OPERATOR_TOKEN)
	    break;
    
    goto DoneDone;

CheckAlias:
    // LValue MUST be a local Var Ident, and NOT a global!
    // LValue can also be Array index... cannot be Dict, but can only  know
    // at runtime!
    // RValue can only be another Var Ident, an Arr/Dict INDEX, or a Dict DOT.
    ArgTP = TP->DownP;
    if (ArgTP->Type == SC_OPERATOR_TOKEN) {
	if (ArgTP->Id == SC_INDEX_ID) {
	    // Index... hopefully of an array.  Make sure NOT doing string index!
	    if (ArgTP->DownP && ArgTP->DownP->NextP && (ArgTP->DownP->NextP->Type == SC_STRING_TOKEN))
		SC_CODEERROR(CGP, ArgTP->DownP->NextP, SC_STR_ErrorBadArg);
		
	    if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
		goto ErrorOut;
	} else
	    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);
	    // Cannot have DOT or anything else for LValue!

    } else if (ArgTP->Type == SC_IDENTIFIER_TOKEN) {
	// Its a var... resolve it
	if ((ArgTP->Type != SC_IDENTIFIER_TOKEN) ||
	    (SC_ERR_RES == SC_CGResolveVar(CGP, ArgTP, 0)))
	    goto ErrorOut;
	// Resolved, but is global!
	if ((*(SC_VexPointer)ArgTP->Data).Class == SC_GVEX)
	    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadAlias);
    } else
	SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);

    ArgTP = ArgTP->NextP;
    if (ArgTP->Type == SC_IDENTIFIER_TOKEN) {
	if (SC_ERR_RES == SC_CGResolveVar(CGP, ArgTP, 0))
	    goto ErrorOut;

    } else if (ArgTP->Type == SC_OPERATOR_TOKEN) {
	if ((ArgTP->Id == SC_INDEX_ID) || (ArgTP->Id == SC_DOT_ID)) {
	    if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
		goto ErrorOut;
	} else
	    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);

    } else
	SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);
	
    goto DoneDone;

CheckUnaryVarArg:
    // Top Level... no point pushing values that will need to be popped.
    if (Act == SC_CHECKTOP_ACT)
	TP->Flags |= SC_TOKEN_NOPUSH_FLAG;
	
    // ++ and -- only, ArgTP must be an Identifier, or INDEX/DOT
    ArgTP = TP->DownP;
    if ((ArgTP->Type == SC_OPERATOR_TOKEN) && ((ArgTP->Id == SC_INDEX_ID) || (ArgTP->Id == SC_DOT_ID))) {
	if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
	    goto ErrorOut;

    } else if (ArgTP->Type == SC_IDENTIFIER_TOKEN) {
	// Can be IDENTIFIER or INDEX/DOT... nothing else!
	if (SC_ERR_RES == SC_CGResolveVar(CGP, ArgTP, 0))
	    goto ErrorOut;
	    
    } else
	SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);
    goto DoneDone;

CheckIndex:
    // Index for Arrays or Dot for Dicts
    // First arg must be Var for Array/Dict Ident... or CALL/Index/Dot that returns one

    ArgTP = TP->DownP;
    
    // Expand (Dot Pkg CONST) here!
    if ((TP->Id == SC_DOT_ID) && (ArgTP->Type == SC_IDENTIFIER_TOKEN)) {
	SC_PkgPointer	PkgP;
	SC_PairHPointer	PairP;

	if ((PkgP = SC_CGGetPkg(CGP, ArgTP))) {				// Assign
	    // Definitely a Pkg!
	    if (! (PairP = SC_CGGetPkgField(CGP, PkgP, ArgTP->NextP)))	// Assign
		SC_CODEERROR(CGP, ArgTP->NextP, SC_STR_ErrorBadDef);
	    
	    // So we have a Def... substitute it.
	    if (SC_CGSubDefToken(PairP, TP)) {
		 // Zap the extra args from TP!
		 SC_SlabZapItem(CGP->VMP->TokenSP, ArgTP->NextP, sizeof(SC_TokenRecord));
		 SC_SlabZapItem(CGP->VMP->TokenSP, ArgTP, sizeof(SC_TokenRecord));
		 goto DoneDone;
	    }
	}
    }

    // Was NOT a DEF expansion if we get here... so fail if DECLARING a Def!
    if (IsDef) SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadIdent);
    
    if (ArgTP->Type == SC_IDENTIFIER_TOKEN) {
	if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
	    goto ErrorOut;

    } else if (ArgTP->Type == SC_OPERATOR_TOKEN) {
	if ((ArgTP->Id == SC_CALL_ID) || (ArgTP->Id == SC_INDEX_ID) || (ArgTP->Id == SC_DOT_ID)) {
	    if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
		goto ErrorOut;
	} else
	    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);
    } else
	SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);

    // Cannot know whether Indexing Array or Dict until runtime.
    // So accept any index at this point... number...string.
    
    ArgTP = ArgTP->NextP;
    if (TP->Id == SC_INDEX_ID) {
	// A[B] style INDEX... B is evaluated, can be number, string, ident, or op.
	if (ArgTP->Type == SC_OPERATOR_TOKEN)
	    Res = SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT);
	else if (ArgTP->Type == SC_IDENTIFIER_TOKEN)
	    Res = SC_CGResolveVar(CGP, ArgTP, 1);
    } else {
	// A.B style DOT...B is NOT evaluated, can be number (string), or literal (string).
	// Literals and numbers come in as IDENTIFIER, but will be changed here!
	// If DEF Const (starting with #) then it *IS* evaluated!
	switch (ArgTP->Type) {
	    case SC_STRING_TOKEN:
	    case SC_OPERATOR_TOKEN:
		SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);

	    case SC_IDENTIFIER_TOKEN:
		if (*(CGP->ScriptStartP + ArgTP->ReadPos) == '#')
		    SC_CGResolveDef(CGP, ArgTP);
		// The Arg to a DOT has to be a string token... so even if
		// it was a DEF of INT or FLT, turn it into string!!
		if (ArgTP->Type == SC_NUMBER_TOKEN) {
		    char	S[32];
		    Int16	L;

		    if (ArgTP->Id == SC_INTNUMB_ID)
			L = snprintf(S, 32, "%ld", ArgTP->Data);
		    else
			L = snprintf(S, 32, "%lf", *(double *)&ArgTP->Data);
		    ArgTP->Data = (Int64)SC_StrGetEntry(CGP->VMP, S, L);
		}
		ArgTP->Type = SC_STRING_TOKEN;
		break;
	}
    }
    if (Res == SC_ERR_RES) goto ErrorOut;

    goto DoneDone;

CheckExprArgs:
    // Top Level: no point doing Ops with no side-effects and pushing result!
    if (Act == SC_CHECKTOP_ACT) {
	if ((TP->Id == SC_COMMA_ID) || (TP->Id == SC_CHOOSE_ID))
	    TP->Flags |= SC_TOKEN_NOPUSH_FLAG;	    
    }

    // Most (like COMMA and PAIR) have 2 args, that can be anything.
    // Some (CHOOSE) will have more args... IN may have 0..N !
    ArgTP = TP->DownP;
    while (ArgTP) {
	if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
	    goto ErrorOut;
	ArgTP = ArgTP->NextP;
    }

    // CodeGen will be easier if (, (, A B) C) or (, A (, B C)) become
    // (, A B C)... but this can recurse and percolate, so do it in a loop.
    if (TP->Id == SC_COMMA_ID) {
	while (SC_CGXformExpr(CGP, TP))
	    if (TP->Type != SC_OPERATOR_TOKEN) break;
    }
    goto DoneDone;

CheckCons:
    // Just check the N arg expressions.
    // But either they are all PAIRs or NONE are PAIR.

   {
	Int16	IsPair;

	ArgTP = TP->DownP;
	IsPair = SC_CGIsOpTP(ArgTP, SC_PAIR_ID);
	if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
	    goto ErrorOut;

	// Now look at all the rest
	ArgTP = ArgTP->NextP;
	while (ArgTP) {
	    if (IsPair != SC_CGIsOpTP(ArgTP, SC_PAIR_ID))
		SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorConsMismatch);
	    if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
		goto ErrorOut;
	    ArgTP = ArgTP->NextP;
	}

	// Top Level... no point pushing values that will need to be popped.
	if (Act == SC_CHECKTOP_ACT) {
	    TP->Flags |= SC_TOKEN_NOPUSH_FLAG;	    
	    while (SC_CGXformExpr(CGP, TP))
		if (TP->Type != SC_OPERATOR_TOKEN)
		    break;
	}
     } goto DoneDone;

CheckUnPack:
    // Can UnPack Var, Dot, Index
    ArgTP = TP->DownP;
    if (ArgTP->Type == SC_IDENTIFIER_TOKEN)
	Res = SC_CGResolveVar(CGP, ArgTP, 0);
	
    else if (ArgTP->Type == SC_OPERATOR_TOKEN) {
	if (ArgTP->Id == SC_UNPACK_ID)
	    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg)	// CODEERROR has {}, so no ';' for ELSE!
	else
	    Res = SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT);
    } else
	SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);

    goto DoneDone;

CheckOut:
    // Args can be named VARs, 0, or NULL, also Array/Dict INDEX or Dict DOT.
    // But cannot have function calls, computations, etc. as these
    // return R-values, not VARs!!
    ArgTP = TP->DownP;
    while (ArgTP) {
	if (ArgTP->Type == SC_IDENTIFIER_TOKEN)
	    Res = SC_CGResolveVar(CGP, ArgTP, 0);

	else if (ArgTP->Type == SC_OPERATOR_TOKEN) {
	    if ((ArgTP->Id == SC_INDEX_ID) || (ArgTP->Id == SC_DOT_ID) || (ArgTP->Id == SC_UNPACK_ID))
	        Res = SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT);
	    else if (ArgTP->Id != SC_NULL_ID)
		SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);

	} else if ((ArgTP->Type == SC_NUMBER_TOKEN) &&
		   (ArgTP->Id == SC_INTNUMB_ID) &&
		   (ArgTP->Data == 0LL)) {
	    // Replace with NULL Op
	    ArgTP->Type = SC_OPERATOR_TOKEN;
	    ArgTP->Id = SC_NULL_ID;

	} else
	    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);
	    
	if (Res == SC_ERR_RES) goto ErrorOut;
	ArgTP = ArgTP->NextP;
    }
    goto DoneDone;

CheckCall:
    // Top Level... FCall does NOT want a return value!
    if (Act == SC_CHECKTOP_ACT)
	TP->Flags |= SC_TOKEN_NOPUSH_FLAG;

    ArgTP = TP->DownP;
    // First is Function spec!  Can only be a Var, CALL, INDEX, or DOT!
    // In the case of Arr/Dict constructor will be ARRAY or DICT.
    if (ArgTP->Type == SC_IDENTIFIER_TOKEN) {
	SC_PkgPointer		MainPkgP = CGP->VMP->PkgADP->Data[0];
	Uns16			CFIndex;
	Uns8			FType;

	// Script can re-define Main funcs/IFIDs...
	Res = SC_CGResolveVar(CGP, ArgTP, 0);
	if (Res != SC_SUCC_RES) {
	    // Not defined in script, so look to MainPkg!
	    CFIndex = SC_CGGetSysFuncIndex(CGP, MainPkgP, ArgTP, &FType);
	    if (CFIndex < (Uns16)-1) {
		// Found it in MainPkgP... so erase Error from SC_CGResolveVar!
		SC_ZAPERROR(CGP);
		Res = SC_CGXformSysCall(CGP, TP, CFIndex, FType);
	    }
	}

    } else if (ArgTP->Type == SC_OPERATOR_TOKEN) {
	if ((ArgTP->Id == SC_CALL_ID) || (ArgTP->Id == SC_INDEX_ID)) {
	    Res = SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT);

	}  else if (ArgTP->Id == SC_DOT_ID) {
	    SC_PkgPointer	PkgP;
	    Uns16		CFIndex;
	    Uns8		FType;

	#if SC_CG_NOPKGNAMEREDEF

	    if ((PkgP = SC_CGGetPkg(CGP, ArgTP->DownP))) {	// Assign
		CFIndex = SC_CGGetSysFuncIndex(CGP, PkgP, ArgTP->DownP->NextP, &FType);
		if (CFIndex < (Uns16)-1)
		    Res = SC_CGXformSysCall(CGP, TP, CFIndex, FType);
		else
		    SC_CODEERROR(CGP, ArgTP->DownP->NextP, SC_STR_ErrorBadArg);
	    } else
		Res = SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT);

	#else

	    // Best to have SC_CG_NOPKGNAMEREDEF set to 1
	    //
	    // If "Sys" is re-defined in script... check here will succeed,
	    // but will have runtime error when calling "Sys.Pause" etc.
	    // These will be hard to understand.
	    //
	    // NOTE:  Following will call SC_CODEERROR which may call the debugger
	    //	      if SC_DEBUG_CALLDEBUGGER is set.  But the error may be zapped!!
	
	    Res = SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT);
	    if (Res != SC_SUCC_RES) {
		if ((PkgP = SC_CGGetPkg(CGP, ArgTP->DownP))) {	// Assign
		    CFIndex = SC_CGGetSysFuncIndex(CGP, PkgP, ArgTP->DownP->NextP, &FType);
		    if (CFIndex < (Uns16)-1) {
			// Found it in a pkg... so erase error from SC_CGCheckPass!
			SC_ZAPERROR(CGP);
			Res = SC_CGXformSysCall(CGP, TP, CFIndex, FType);
		    }
		}
	    }

	#endif
	    
	} else
	    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);
    } else
	SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);
    if (Res == SC_ERR_RES) goto ErrorOut;

    // Now INPUT and OUTPUT.
    ArgTP = ArgTP->NextP;
    while (ArgTP) {
	if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
	    goto ErrorOut;
	ArgTP = ArgTP->NextP;
    }
    goto DoneDone;

CheckLoop:
    {   Int16		Count = 0;
	
	ArgTP = TP->DownP;
	while (ArgTP) {
	    if (++Count > ArgCount)
		SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadCond);	// Too many

	    if ((ArgCount == 3) && ((Count == 1) || (Count == 3)))
		ArgTP->Flags |= SC_TOKEN_NOPUSH_FLAG;
	    if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
		goto ErrorOut;
	    ArgTP = ArgTP->NextP;
	}
	if (Count < ArgCount)
	    SC_CODEERROR(CGP, TP, SC_STR_ErrorBadCond);		// Not enough

	// Create a new Lex for this loop and link in.
	DoLexFin = 0;
	LexP = SC_CGAddLex(CGP, SC_LEXLOOPFLAG | SC_LEXONEFLAG | SC_LEXCONDFLAG);
	if (LexP->BLevel > 255) SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorTooDeep);
	// Special flags for DO !!
	if (TP->Id == SC_DO_ID) LexP->Flags |= (SC_LEXDOFLAG | SC_LEXDONEEDFLAG);

    } goto DoneDone;

CheckDoCond:
    ArgTP = TP->DownP;
    if ((ArgTP == NULL) || (ArgTP->NextP))
	SC_CODEERROR(CGP, TP, SC_STR_ErrorBadCond);
	
    if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
	goto ErrorOut;

    LexP->Flags &= ~SC_LEXDONEEDFLAG;

    // Uses the same LEX as the DO
    DoLexFin = 0;	
    // Terminating WHILE needs a semi-colon after!!
    CGP->Flags |= SC_CGNEEDSEMIFLAG;
    goto DoneDone;

CheckCont:
    {	// Do not check in FIN-ished LEX, UnNest to find the active one!
	SC_LexPointer CurLexP = SC_CGUnNestFinLex(LexP, SC_LEXLOOPFLAG);

	if (! (CurLexP->Flags & SC_LEXLOOPFLAG))
	    SC_CODEERROR(CGP, TP, SC_STR_ErrorBadKeyword);
    }
    goto DoneDone;

CheckBreak:
    {	// Do not check in FIN-ished LEX, UnNest to find the active one!
	SC_LexPointer CurLexP = SC_CGUnNestFinLex(LexP, SC_LEXLOOPFLAG | SC_LEXSWITCHFLAG);

	if (! (CurLexP->Flags & (SC_LEXLOOPFLAG | SC_LEXSWITCHFLAG)))
	    SC_CODEERROR(CGP, TP, SC_STR_ErrorBadKeyword);
    }
    goto DoneDone;

CheckBegin:
    // Take over a TEMP Lex, or create a new one!
    if ((LexP->Flags & SC_LEXONEFLAG) && !(LexP->Flags & SC_LEXFINFLAG))
	LexP->Flags &= ~SC_LEXONEFLAG;
    else {
	LexP = SC_CGAddLex(CGP, SC_LEXNOFLAG);
	if (LexP->BLevel > 255) SC_CODEERROR(CGP, TP, SC_STR_ErrorTooDeep);	
    }
    DoLexFin = 0;
    goto DoneDone;

CheckEnd:
    if ((LexP->Flags & SC_LEXONEFLAG) || (LexP->Flags & SC_LEXTOPFLAG))
	SC_CODEERROR(CGP, TP, SC_STR_ErrorMissingAOpen);
	// No need for SC_CGCheckLabelDict here if LEXFUNCFLAG... will check in SC_AuxCGCPEndFunc.

    // DO NOT zap the Lex here... zap it after next cycle.
    DoLexFin = 1;
    goto DoneDone;

CheckIf:
    ArgTP = TP->DownP;
    if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
	goto ErrorOut;

    DoLexFin = 0;    
    LexP = SC_CGAddLex(CGP, SC_LEXIFFLAG | SC_LEXONEFLAG | SC_LEXCONDFLAG);
    if (LexP->BLevel > 255) SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorTooDeep);    
    // printf("Add *IF* Lex: %lx  Sup:%lx\n", (Int64)LexP, (Int64)LexP->SupP);
    goto DoneDone;

CheckElse:
    // ELSE must follow a FINished IF Lex!
    //
    // Imperative that any IF-ELSE that was nested in the IF part is already
    // cleaned up, otherwise this ELSE will try to chain off of that!!
    if (!((LexP->Flags & SC_LEXIFFLAG) && (LexP->Flags & SC_LEXFINFLAG)))
	SC_CODEERROR(CGP, TP, SC_STR_ErrorBadKeyword);

    // Kill off the prev IF Lex... but first CodeCleanup.
    if (SC_ERR_RES == SC_CGCodePassCleanup(CGP, 0, 1)) goto ErrorOut;
    SC_CGZapLastLex(CGP);		// Just the last one!

    DoLexFin = 0;
    LexP = SC_CGAddLex(CGP, SC_LEXELSEFLAG | SC_LEXONEFLAG | SC_LEXCONDFLAG);
    if (LexP->BLevel > 255) SC_CODEERROR(CGP, TP, SC_STR_ErrorTooDeep);    
    // printf("Add *ELSE* Lex: %lx  Sup:%lx\n", (Int64)LexP, (Int64)LexP->SupP);
    goto DoneDone;

CheckSwitch:
    ArgTP = TP->DownP;
    if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
	goto ErrorOut;

    DoLexFin = 0;    
    LexP = SC_CGAddLex(CGP, SC_LEXSWITCHFLAG | SC_LEXONEFLAG | SC_LEXCONDFLAG);
    if (LexP->BLevel > 255) SC_CODEERROR(CGP, TP, SC_STR_ErrorTooDeep);    
    goto DoneDone;

CheckCase:
    {	// Do not check in FIN-ished LEX, UnNest to find the active one!
	SC_LexPointer CurLexP = SC_CGUnNestFinLex(LexP, SC_LEXSWITCHFLAG);

	if (! (CurLexP->Flags & SC_LEXSWITCHFLAG))
	    SC_CODEERROR(CGP, TP, SC_STR_ErrorBadCase);
	CurLexP->Flags |= SC_LEXCASEFLAG;
    }

    // CASE has arg, DEFAULT does not!
    if ((ArgTP = TP->DownP)) {
	if (ArgTP->Type == SC_OPERATOR_TOKEN) {
	    if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
		goto ErrorOut;
	} else if (ArgTP->Type == SC_IDENTIFIER_TOKEN) {
	    // Could be a simple Def!  Otherwise Var names do NOT belong here.
	    if (SC_ERR_RES == SC_CGResolveDef(CGP, ArgTP))
		goto ErrorOut;
	}
    }
    goto DoneDone;
    
CheckVarDecl:
    ArgTP = TP->DownP;
    // Can have N, each just a var or an assignment of an expression to a new Var.
    while (ArgTP) {
	switch (ArgTP->Type) {
	    case SC_OPERATOR_TOKEN:
		if (ArgTP->Id == SC_EQ_ID) {
			SC_TokenPointer	LTP = ArgTP->DownP;
		    // Left must be Var ident.
		    if (LTP->Type != SC_IDENTIFIER_TOKEN)
			SC_CODEERROR(CGP, LTP, SC_STR_ErrorBadOp);
		    if ((SC_ERR_RES == SC_CGDeclareVar(CGP, LTP, 0)) ||
			(SC_ERR_RES == SC_CGCheckPass(CGP, LTP->NextP, SC_CHECKEXPR_ACT)))
			goto ErrorOut;

		} else if (ArgTP->Id == SC_ALIAS_ID) {
		    SC_TokenPointer		LTP = ArgTP->DownP;
		    SC_TokenPointer		RTP = LTP->NextP;
		    // Left must be NEW Var ident.
		    if (LTP->Type != SC_IDENTIFIER_TOKEN)
			SC_CODEERROR(CGP, LTP, SC_STR_ErrorBadOp);
		    if (SC_ERR_RES == SC_CGDeclareVar(CGP, LTP, 0))
			goto ErrorOut;

		    // Right can be (existing) Var, Arr/Dict INDEX, or Dict DOT.
		    if (RTP->Type == SC_IDENTIFIER_TOKEN) {
			if (SC_ERR_RES == SC_CGResolveVar(CGP, RTP, 0))
			    goto ErrorOut;

		    } else if (RTP->Type == SC_OPERATOR_TOKEN) {
			if ((RTP->Id == SC_INDEX_ID) || (RTP->Id == SC_DOT_ID)) {
			    if (SC_ERR_RES == SC_CGCheckPass(CGP, RTP, SC_CHECKEXPR_ACT))
				goto ErrorOut;
			} else
			    SC_CODEERROR(CGP, RTP, SC_STR_ErrorBadArg)

		    } else
			SC_CODEERROR(CGP, RTP, SC_STR_ErrorBadArg);

		} else
		    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadOp);
		break;

	    case SC_IDENTIFIER_TOKEN:
		if (SC_ERR_RES == SC_CGDeclareVar(CGP, ArgTP, 0))
		    goto ErrorOut;
		break;

	    default:  SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);
	}

	ArgTP = ArgTP->NextP;
    } goto DoneDone;

CheckDefDecl:
    ArgTP = TP->DownP;
    // Can have N, each an assignment of an expression to a new Const.  These
    // *MUST* be CONST expressions that can be entirely resolved in the compiler
    // there can be no runtime processing!
    while (ArgTP) {
	if ((ArgTP->Type == SC_OPERATOR_TOKEN) && (ArgTP->Id == SC_EQ_ID)) {
	    SC_TokenPointer LTP = ArgTP->DownP;

	    // Left must be NEW Def Ident.  DeclareDef the left arg, check the right.
	    if (LTP->Type != SC_IDENTIFIER_TOKEN)
		SC_CODEERROR(CGP, LTP, SC_STR_ErrorBadOp);

	    // Check and Xform/Optimize the right side, then declare left side.
	    // (Right side should be a single value by now Int/Float/String!)
	    if ((SC_ERR_RES == SC_CGCheckPass(CGP, LTP->NextP, SC_CHECKDEF_ACT)) ||
	        (SC_ERR_RES == SC_CGDeclareDef(CGP, LTP, LTP->NextP)))
		goto ErrorOut;

	} else
	    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);

	ArgTP = ArgTP->NextP;
    } goto DoneDone;

CheckFuncDecl:
    // Function declaration (just the Name, not definition).
    ArgTP = TP->DownP;
    // Can have N, each a function declaration.
    while (ArgTP) {
	if (ArgTP->Type != SC_IDENTIFIER_TOKEN)
	    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);
	if (SC_ERR_RES == SC_CGDeclareVar(CGP, ArgTP, 2))
	    goto ErrorOut;

	ArgTP = ArgTP->NextP;
    } goto DoneDone;

CheckLabel:
    ArgTP = TP->DownP;
    if (SC_ERR_RES == SC_CGRefLabel(CGP, ArgTP, 1))
	goto ErrorOut;
    DoLexFin = 0;	// Does NOT COUNT as a statement!
    goto DoneDone;

CheckGoto:
    ArgTP = TP->DownP;
    if (SC_ERR_RES == SC_CGRefLabel(CGP, ArgTP, 0))
	goto ErrorOut;    
    goto DoneDone;

CheckFrom:
    // Can only be part of RETURN or YIELD
    // Arg is a RunCall... so is generally an Ident, Index, or Dot... or a Call.
    // Worst case, Arg is a COMMA ending in Ident, Index, Dot, or Call!
    ArgTP = TP->DownP;
    if ((ArgTP->Type == SC_STRING_TOKEN) || (ArgTP->Type == SC_NUMBER_TOKEN))
	SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);
    if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
	goto ErrorOut;
    // Can only check so much here... will know definitively at runtime!
    // (Could be a Comma, ending in a Comma, ending in an Ident... etc.)
    goto DoneDone;

CheckReturn:
    // RETURN and YIELD
    // RETURN and YIELD can have NO arg, or a FROM clause!
    // (The FROM clause CAN ONLY come last!)
    //
    // RETURN from the TOP Func is basically an EXIT.
	
    if ((ArgTP = TP->DownP)) {		// Assign
	if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
	    goto ErrorOut;
	if ((ArgTP = ArgTP->NextP))	// Assign
	    if (SC_ERR_RES == SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT))
		goto ErrorOut;
    }

    while (SC_CGXformExpr(CGP, TP))
	if (TP->Type != SC_OPERATOR_TOKEN)
	    break;
        
    goto DoneDone;

CheckFunc:
    // Func Definition (not CALL)!
    // Func Def cannot be provisional/conditional... so must
    // be from a regular or FUNC LEX.
    if (LexP->Flags & SC_LEXTYPEMASK)		// If/else/loop/switch
	SC_CODEERROR(CGP, TP, SC_STR_ErrorBadFuncDef);

    {
	SC_TokenPointer		ParamTP;
	Int16			I, OneNull;
	Uns16 *			ArgCountP;

	// Args are: NAME INPUT OUTPUT.  Process them in order.
	ArgTP = TP->DownP;		// Real Name? (Not NULL!)
	if ((ArgTP->Type == SC_IDENTIFIER_TOKEN) &&
	    (SC_ERR_RES == SC_CGDeclareVar(CGP, ArgTP, 1)))
	    goto ErrorOut;

	// Create a function LEX and a MAP for it!  No DictP yet!
	// Do it here because Params become LVars *IN* the Func Lex.
	DoLexFin = 0;
	LexP = SC_CGAddLex(CGP, SC_LEXFUNCFLAG | SC_LEXONEFLAG);

	LexP->MapP = SC_CGMapNew(CGP->VMP);
	LexP->MapP->SupFLexP = CGP->FuncLexP;	// Current Func!
	LexP->MapP->FuncId = SC_CGFuncAlloc(CGP, CGP->FuncLexP->MapP);
	CGP->FuncLexP = LexP;

	ArgTP = ArgTP->NextP;		// Input... Pass-by-Value Args
	ArgCountP = &LexP->MapP->InCount;
	while (ArgTP) {
	    ParamTP = ArgTP->DownP;
	    I = 0, OneNull = 0;
	    while (ParamTP) {
		if (OneNull) {
		    // First param was NULL, all the rest are bad!
		    SC_CODEERROR(CGP, ParamTP, SC_STR_ErrorBadParam);

		} else if (ParamTP->Type == SC_IDENTIFIER_TOKEN) {
		    // Parse will not allow any Keywords here... Params become LVars.
		    if (SC_ERR_RES == SC_CGDeclareVar(CGP, ParamTP, 0))
			goto ErrorOut;		    

		} else if (ParamTP->Type == SC_OPERATOR_TOKEN) {
		    // No operators, except for first NULL.
		    if ((I == 0) && (ParamTP->Id == SC_NULL_ID))
			OneNull = 1;
		    else
			SC_CODEERROR(CGP, ParamTP, SC_STR_ErrorBadParam);

		} else
		    SC_CODEERROR(CGP, ParamTP, SC_STR_ErrorBadParam);

		I++, ParamTP = ParamTP->NextP;
	    }

	    *ArgCountP = I;		// Could just ArgCountP++... not safe :-)
	    ArgCountP = &LexP->MapP->OutCount;

	    // if INPUT or OUTPUT are empty, dispose of redundant NULL op.
	    if (OneNull) {
		ParamTP = ArgTP->DownP;
		ArgTP->DownP = NULL;
		SC_SlabZapItem(CGP->VMP->TokenSP, ParamTP, sizeof(SC_TokenRecord));
	    }

	    ArgTP = ArgTP->NextP;		// OUTPUT... Pass-by-Ref Args
	}

    } goto DoneDone;
				
CheckMath:
    if (Act == SC_CHECKTOP_ACT) {
	// This is the top level... Math without assignment has no side effect!
	// BUT one of the args may have a side-effect... as in "A + B++;" So
	// turn OP into a COMMA!  XForm knows how to handle that!!
	TP->Id = SC_COMMA_ID;
	goto CheckExprArgs;
    }

    {
	Int16	AllIsNumb = 1;

	ArgTP = TP->DownP;
	CheckAgain = ArgFlag = 0;
	while (ArgTP) {
	    ArgFlag <<= 1;			// 0 for INT, 1 for FLOAT
	    switch (ArgTP->Type) {
		case SC_STRING_TOKEN:		if ((TP->Id != SC_EQEQ_ID) &&
						    (TP->Id != SC_NEQ_ID) &&
						    (TP->Id != SC_IDENT_ID) &&
						    (TP->Id != SC_NIDENT_ID))
						    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);
						// Allow ==, !=, ===, !== comparison to string!
						// No point hardcoding comparison of 2 strings...
						// (Beware string format diffs... long vs. short!)
						AllIsNumb = 0;
						break;
	    
		case SC_NUMBER_TOKEN:		ArgFlag += ArgTP->Id; break;

		case SC_OPERATOR_TOKEN:		Res = SC_CGCheckPass(CGP, ArgTP, SC_CHECKEXPR_ACT + IsDef);
						// May change the ArgTP->Type !!
						if (ArgTP->Type != SC_NUMBER_TOKEN) {
						    AllIsNumb = 0;
						    if (IsDef) SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);
						}
						CheckAgain = 1; break;

		case SC_IDENTIFIER_TOKEN:	Res = (IsDef) ? SC_CGResolveDef(CGP, ArgTP)	// Def can be Numb/Str
							      : SC_CGResolveVar(CGP, ArgTP, 1);
						if (Res == SC_ERR_RES)				// NOT resolved
						    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);
						
						if (ArgTP->Type == SC_IDENTIFIER_TOKEN)
						    AllIsNumb = 0;
						else if (ArgTP->Type != SC_NUMBER_TOKEN) {
						    AllIsNumb = 0;
						    SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg);
						}
						break;

		default:			SC_CODEERROR(CGP, ArgTP, SC_STR_ErrorBadArg); break;
						// No string args in Math and Logic
	    }
	    if (Res == SC_ERR_RES) goto ErrorOut;
	    ArgTP = ArgTP->NextP;
	}

	if (! AllIsNumb && (TP->Type == SC_OPERATOR_TOKEN)) {
	    while (SC_CGXformExpr(CGP, TP)) {
		CheckAgain = 1;
		if (TP->Type != SC_OPERATOR_TOKEN) break;;
	    }
	}

	if (CheckAgain) {
	    AllIsNumb = 0;
	    if (TP->Type == SC_OPERATOR_TOKEN) {
		ArgFlag = 0; AllIsNumb = 1; ArgTP = TP->DownP;
		while (ArgTP) {
		    ArgFlag <<= 1;
		    if (ArgTP->Type == SC_NUMBER_TOKEN)
			ArgFlag += ArgTP->Id;
		    else {
			AllIsNumb = 0;			// Not all Numbs, Done!
			break;
		    }

		    ArgTP = ArgTP->NextP;
		}
	    }
	}

	if (AllIsNumb) {
	    // Set MathFuncP after SC_CGXformExpr because it can change the Op!
	    
	    #define	SC_SETMATHFUNC(_FP, _C)	MathFuncP = &_FP; ArgCount = _C; break;
		switch (TP->Id) {
		    case SC_LESS_ID:	    SC_SETMATHFUNC(SC_CGCheckLess, 2);
		    case SC_GREAT_ID:	    SC_SETMATHFUNC(SC_CGCheckGreat, 2);
		    case SC_PLUS_ID:	    SC_SETMATHFUNC(SC_CGCheckAdd, 2);
		    case SC_MINUS_ID:	    SC_SETMATHFUNC(SC_CGCheckSub, 2);
		    case SC_DIV_ID:	    SC_SETMATHFUNC(SC_CGCheckDiv, 2);
		    case SC_MOD_ID:	    SC_SETMATHFUNC(SC_CGCheckMod, 2);
		    case SC_MULT_ID:	    SC_SETMATHFUNC(SC_CGCheckMult, 2);
		    case SC_LOGNEG_ID:	    SC_SETMATHFUNC(SC_CGCheckLNEG, 1);
		    case SC_BITNEG_ID:	    SC_SETMATHFUNC(SC_CGCheckBNEG, 1);
		    case SC_BITAND_ID:	    SC_SETMATHFUNC(SC_CGCheckBAND, 2);
		    case SC_BITOR_ID:	    SC_SETMATHFUNC(SC_CGCheckBOR, 2);
		    case SC_BITXOR_ID:	    SC_SETMATHFUNC(SC_CGCheckBXOR, 2);
		    case SC_UPLUS_ID:	    SC_SETMATHFUNC(SC_CGCheckPlus, 1);
		    case SC_UMINUS_ID:	    SC_SETMATHFUNC(SC_CGCheckMinus, 1);
		    case SC_LOGPOS_ID:	    SC_SETMATHFUNC(SC_CGCheckLPOS, 1);
		    case SC_EQEQ_ID:	    SC_SETMATHFUNC(SC_CGCheckEQEQ, 2);
		    case SC_NEQ_ID:	    SC_SETMATHFUNC(SC_CGCheckNEQ, 2);
		    case SC_LESSEQ_ID:	    SC_SETMATHFUNC(SC_CGCheckLEQ, 2);
		    case SC_GREATEQ_ID:	    SC_SETMATHFUNC(SC_CGCheckGEQ, 2);
		    case SC_LOGAND_ID:	    SC_SETMATHFUNC(SC_CGCheckLAND, 2);
		    case SC_LOGOR_ID:	    SC_SETMATHFUNC(SC_CGCheckLOR, 2);
		    case SC_SHL_ID:	    SC_SETMATHFUNC(SC_CGCheckSHL, 2);
		    case SC_SHR_ID:	    SC_SETMATHFUNC(SC_CGCheckSHR, 2);
		    case SC_IDENT_ID:	    SC_SETMATHFUNC(SC_CGCheckID, 2);
		    case SC_NIDENT_ID:	    SC_SETMATHFUNC(SC_CGCheckNID, 2);
		    default: MathFuncP = NULL;
		}
	    #undef	SC_SETMATHFUNC    
	    
	    // ****************************************
	    // COMPILE TIME ARITHMETIC ****************
	    // ****************************************
	    // All args are Numbs, do the math and simplify!
	    (*MathFuncP)(TP, ArgFlag);

	    if (ArgCount == 2)
		SC_SlabZapItem(CGP->VMP->TokenSP, TP->DownP->NextP, sizeof(SC_TokenRecord));
	    SC_SlabZapItem(CGP->VMP->TokenSP, TP->DownP, sizeof(SC_TokenRecord));
	    TP->DownP = NULL;
	}
	
    } goto DoneDone;
    
ErrorOut:
    return SC_ERR_RES;
}


// SC_CGXformExpr does optimizations/transformations to expressions (AST) below
// the TP.  In many cases, the TP itself is changed, perhaps to a simple number
// or Identifier.
//
// ***MUST CALL IN A LOOP***
//
// Will return 0 if NO changes are made.
// Will return NON-ZERO if the AST was changed (Can be -1).
//
// NOTE: Routines that display (debug mode) the AST look at the ReadPos/Len to
//       show OPERATORS... They may draw the wrong Op if they just copy the
//       source buffer verbatim and the Op has been changed.  So instead, the
//       program has explicit labels for Ops and uses them.
//
// SC_CGXFormExpr really only works on the top level of the TP, may need >1
// cycle to simplify changes:
//
//	Cycle 1:	(- (Neg A) (Neg B)  -->  (Neg (- A B))
//	Cycle 2:	      (Neg (- A B)  -->  (- B A)
//
// So call SC_CGXformExpr in a WHILE loop until it returns 0... but check that
// TP is still an Operator (may optimize to a constant value!).
//
// NOTE: This function very often copies one TP record *INTO* another... A ptr
//	 operation is obviously more efficient, but this is fast enough for
//	 compile time and simpler than maintaining PrevTP and UpTP ptrs.
//
//	 ***** TP may have a NextP field, stash if *TP is assigned *****
//       ***************************************************************

Int16	SC_CGXformExpr(SC_CGPointer CGP, SC_TokenPointer TP)
{
    SC_TokenPointer	Arg1TP, Arg2TP;
    Int16		DoZap;
    Int16		EQForm = 0;

    if (TP->Id == SC_NULL_ID) return 0;

    // DoZap == 1  --> Zap Arg1TP
    // DoZap == 2  --> Zap **BOTH** Arg1TP *AND* Arg2TP
    // DoZap == -1 --> Zap nothing, but CHANGES were made
    // DoZap == 0  --> NO CHANGES WERE MADE
    //
    // Function returns DoZap!    

    // Deal with MathOps having a Comma as Arg...
    // Do it up here instead of checking it for EVERY math op.
    DoZap = 0;
    if (TP->DownP && SC_CGOpIntoComma(CGP, TP))
	DoZap = -1;

    // Eliminate obvious Identity ops and simplify/reduce NEG.
    //
    //	DO NOT reduce (% A 1) as it may be 0 or 0.0000 depending on A's value!
    //  Many other "identity" operations will convert to DOUBLE if arg is DOUBLE.
    //
    // NOTE: SC_UPLUS_ID is eliminated upstream as it has no CodeGen function.
    //

    Arg1TP = TP->DownP;
    Arg2TP = (Arg1TP) ? Arg1TP->NextP : NULL;
    switch (TP->Id) {
	case SC_PLUS_ID:	goto XformPlus;
	case SC_MINUS_ID:	goto XformMinus;
	case SC_COMMA_ID:	goto XformComma;
	case SC_DIV_ID:		goto XformDiv;
	case SC_MULT_ID:	goto XformMult;
	case SC_LOGNEG_ID:	goto XformLogNeg;
	case SC_UMINUS_ID:	goto XformNeg;
	case SC_CONS_ID:	goto XformCons;
	case SC_PAIR_ID:	goto XformPair;
	case SC_RETURN_ID:	goto XformReturn;
	case SC_YIELD_ID:	goto XformReturn;
	case SC_LOGPOS_ID:	goto XformLogPos;
	
	case SC_SHL_ID:		// Falls below
	case SC_SHR_ID:		goto XformShift;

	case SC_LOGAND_ID:	// Falls below
	case SC_LOGOR_ID:	goto XformLogical;

	case SC_PLUSEQ_ID:	EQForm = 1; goto XformPlus;
	case SC_MINUSEQ_ID:	EQForm = 1; goto XformMinus;
	case SC_DIVEQ_ID:	EQForm = 1; goto XformDiv;
	case SC_MULTEQ_ID:	EQForm = 1; goto XformMult;
	case SC_SHLEQ_ID:	// Falls below
	case SC_SHREQ_ID:	EQForm = 1; goto XformShift;
	
	default: return 0;	// Did nothing!
    }

XformReturn: {
	// (RETURN (FROM (IFID 6 (INPUT...)...))) --> (RETURN)
	// (RETURN x (FROM (IFID 6 (INPUT...)...))) --> (RETURN X)
	// (YIELD (FROM (IFID 6 (INPUT...)...))) --> (YIELD)
	// (YIELD x (FROM (IFID 6 (INPUT...)...))) --> (YIELD X)
	
	Uns8	DoTest = 0, HasArg = 0;
	
	if (Arg2TP && SC_CGIsOpTP(Arg2TP, SC_FROM_ID) &&
	    ((Arg2TP  = Arg2TP->DownP)))			// Assign
	    DoTest = HasArg = 1;
	else if (Arg1TP && SC_CGIsOpTP(Arg1TP, SC_FROM_ID) &&
		 ((Arg2TP = Arg1TP->DownP)))			// Assign
	     DoTest = 1;

	if ((DoTest) &&
	    SC_CGIsOpTP(Arg2TP, SC_CALL_ID) &&
	    (Arg2TP->OpState & SC_IFIDCALL_STATE) &&
	    ((Arg2TP = Arg2TP->DownP)) &&			// Assign
	    (Arg2TP->Type == SC_NUMBER_TOKEN) &&
	    (Arg2TP->Id == SC_INTNUMB_ID) &&
	    (Arg2TP->Data == SC_RCP_IFID)) {

	    if (HasArg) {
		SC_CGTokenZapTree(CGP->VMP, Arg1TP->NextP);
		Arg1TP->NextP = NULL;
	    } else {
		SC_CGTokenZapTree(CGP->VMP, Arg1TP);
		TP->DownP = NULL;
	    }
	    DoZap = -1;			// Changes made, don't zap at end
	}
    }
    goto DoneDone;

XformComma:
    // Flatten any subordinate COMMA ops:
    //
    // (, N) + NOPUSH flag --> NULL for N not an Op
    // (, B) --> B
    // (, A (, B C)) or (, (, A B) C) --> (, A B C)...
    // (, A B... X) --> (, B... X)  if A is an identifier, number, or string
    // (, B C) --> C  if B is an identifier, number, or string
    //
    // Since COMMA ignores all but the last Arg, this section will also flatten
    // subordinate ops that lack side effects, +, -, *, <<, etc.  Anything WITH
    // a side-effect, (=, *=, ++, Call, etc.) will be included verbatim.
    // (Assumed that func calls HAVE side effects!)
    //
    // NOTE: NOPUSH flag means even the LAST ARG can be discarded!
    //
    // NOTE: Zaps here, **DO NOT** goto DoneDone!
    //	     Merges 1 Comma level at a time! Call again for next level.
    //
    //       Arg1 is the CUR arg being looked at, Arg2 the next one.
    
    while (Arg1TP) {
	SC_TokenPointer		ZapTP;
	if (Arg2TP == NULL) {
	    if (TP->DownP == Arg1TP) {
		// Arg1 is the last... If the ONLY one, Make TP into Arg1, zap old Arg1.

		// If NOPUSH and have anything other than an Op, make TP a NULL op!
		if ((TP->Flags & SC_TOKEN_NOPUSH_FLAG) && (Arg1TP->Type != SC_OPERATOR_TOKEN)) {
		    // Ident, number, string, etc.... just ignore all.
		    TP->Id = SC_NULL_ID;
		    TP->DownP = NULL;
		    SC_SlabZapItem(CGP->VMP->TokenSP, Arg1TP, sizeof(SC_TokenRecord));
		    DoZap = 1;
		    break;
		}
		
		// But make the new TP (old Arg1) NOPUSH if original COMMA was!
		Arg1TP->Flags |= (TP->Flags & SC_TOKEN_NOPUSH_FLAG);
		// Stash NextP before zapping
		ZapTP = TP->NextP;
		    *TP = *Arg1TP;
		TP->NextP = ZapTP;	
		SC_SlabZapItem(CGP->VMP->TokenSP, Arg1TP, sizeof(SC_TokenRecord));
		DoZap = 1;
		break;
	    } 
	    
	} else {
	    // There is an Arg2
	    if (Arg1TP->Type == SC_OPERATOR_TOKEN) {
		if (SC_CGOpNoSideEffect(Arg1TP->Id)) {
		    // Flatten out Arg1TP!  Remove the Op and add its args in its place.		
		    ZapTP = Arg1TP->DownP;		// First arg of lower level
		    *Arg1TP = *ZapTP;			// Replace Op with its args!
		    SC_SlabZapItem(CGP->VMP->TokenSP, ZapTP, sizeof(SC_TokenRecord));
		    DoZap = 1;
		    // Go to the last arg of the lower level... attach to Arg2TP
		    while (Arg1TP->NextP) Arg1TP = Arg1TP->NextP;
		    Arg1TP->NextP = Arg2TP;
		} else {
		    // Left Arg1TP alone, so check Arg2TP... **IF** it is last and Comma is NOPUSH!
		    if ((Arg2TP->NextP == NULL) && (TP->Flags & SC_TOKEN_NOPUSH_FLAG)) {
			if (Arg2TP->Type == SC_OPERATOR_TOKEN) {
			    Arg2TP->Flags |= SC_TOKEN_NOPUSH_FLAG;	// If not zapped!
			    if (SC_CGOpNoSideEffect(Arg2TP->Id)) {
				// Flatten out Arg2TP!  Remove the Op and add its args in its place.
				ZapTP = Arg2TP->DownP;	// First arg of lower level
				*Arg2TP = *ZapTP;	// Replace Op with its args!
				SC_SlabZapItem(CGP->VMP->TokenSP, ZapTP, sizeof(SC_TokenRecord));
				DoZap = 1;
				// Arg2TP->NextP *WAS* NULL before... so leave the new tail as is.
			    }
			} else {
			    // Just Zap Arg2TP!  Done with this loop, since Arg2TP->NextP was NULL.
			    SC_SlabZapItem(CGP->VMP->TokenSP, Arg2TP, sizeof(SC_TokenRecord));
			    Arg1TP->NextP = NULL;
			    DoZap = 1;
			    break;
			}
		    }
		}
	    } else {
		// Arg1 is Ident, Number, String, etc...  So no SideEffects!
		// More args left... Make Arg1 INTO Arg2, zap Arg2!
		*Arg1TP = *Arg2TP;
		SC_SlabZapItem(CGP->VMP->TokenSP, Arg2TP, sizeof(SC_TokenRecord));
		DoZap = 1;
		Arg2TP = Arg1TP->NextP;
		continue;
	    }
	}

	if ((Arg1TP = Arg2TP)) Arg2TP = Arg2TP->NextP;
    }
    return DoZap;	// **DO NOT** goto DoneDone

XformCons:
    // (CONS A B C...) --> (COMMA A B C) when CONS is NoPush!
    if (TP->Flags & SC_TOKEN_NOPUSH_FLAG) {
	TP->Id = SC_COMMA_ID;
	DoZap = -1;
    }
    goto DoneDone;

XformPair:
    // CONS can become a COMMA, and its last PAIR may survive as NOPUSH!
    if (TP->Flags & SC_TOKEN_NOPUSH_FLAG) {
	TP->Id = SC_NULL_ID;
	TP->DownP = NULL;
	DoZap = 2;
    }
    goto DoneDone;

XformShift:
    //	  (<< A 0) or (>> A 0) --> A
    //		EQForm (<<= A 0) or (>>= A 0) --> A
    if (SC_CGIsIntTP(Arg2TP, 0LL)) {
	Arg1TP->NextP = TP->NextP;	// Stash here in case TP had a NextP!
	*TP = *Arg1TP;
	DoZap = 2;
    }
    goto DoneDone;

XformLogical:
    //	  (&& A) or (|| A) --> (LogPos A)
    //	  (&& 0) or (|| 0) --> 0
    //	  (&& n) or (|| n) --> 1 for non-zero n
    //
    //	  (&& (&& A B) C) or (&& A (&& B C)) --> (&& A B C)
    //	  (&& A (LogPos B) C) --> (&& A B C) for all args
    //    (|| (|| A B) C) or (|| A (|| B C)) --> (|| A B C)
    //    (|| A (LogPos B) C) --> (|| A B C) for all args
    //
    //    (&& A B C n D E F) --> (&& A B C D E F) for non-zero n
    //	  (|| A B C 0 D E F) --> (|| A B C D E F)
    //	  (&& A B C 0 D E F) --> (&& A B C 0)
    //	  (|| A B C n D E F) --> (|| A B C n) for non-zero n
    //
    // The Xforms can create unary && and ||, which are renamed
    // as LogPos (as does "!!A") for ease of processing.
    // Example (&& A 1) --> (&& A) --> (LOGPOS A)

    if (Arg2TP == NULL) {
	if ((Arg1TP->Type == SC_IDENTIFIER_TOKEN) || (Arg1TP->Type == SC_OPERATOR_TOKEN))
	    TP->Id = SC_LOGPOS_ID;
	else {
	    SC_CGSetTPInt(TP, (SC_CGIsIntTP(Arg1TP, 0LL)) ? 0LL : 1LL);
	    TP->DownP = NULL;
	    DoZap = 1;			// Zap Arg1TP
	}
	goto DoneDone;
    }

    {	// TP has 2 or more args if gets here... so cannot be LOGPOS!
	//
        // NOTE: Zaps here, so DO NOT go to DoneDone!

	SC_TokenPointer		PrevTP = NULL;
	SC_TokenPointer		ZapTP;

	Int16			IsAnd = (TP->Id == SC_LOGAND_ID);
	Int16			IsZero;
	
	while (Arg1TP) {
	    if (Arg1TP->Type == SC_OPERATOR_TOKEN) {
		if ((Arg1TP->Id == SC_LOGPOS_ID) || (Arg1TP->Id == TP->Id)) {
		    ZapTP = Arg1TP->DownP;	// First arg of the lower-level logical
		    *Arg1TP = *ZapTP;		// Recycle into first arg of lower level
		    SC_SlabZapItem(CGP->VMP->TokenSP, ZapTP, sizeof(SC_TokenRecord));
		    DoZap = 1;			// Did Zap
		    // Find last arg of the lower level
		    while (Arg1TP->NextP) Arg1TP = Arg1TP->NextP;
		    Arg1TP->NextP = Arg2TP;		// Link to top level
		}
	    } else if (Arg1TP->Type != SC_IDENTIFIER_TOKEN) {
		// Arg1 is neither Op nor Ident, so logically a constant!!!
	    
		IsZero = SC_CGIsIntTP(Arg1TP, 0LL);
		if (IsAnd == !IsZero) {
		    // Either LOGAND when arg1 is NON-ZERO or LOGOR when Arg1 is Zero
		    // ... Zap Arg1 and concatenate the rest.
		    
		    if (PrevTP) PrevTP->NextP = Arg2TP;
		    else TP->DownP = Arg2TP;

		    SC_SlabZapItem(CGP->VMP->TokenSP, Arg1TP, sizeof(SC_TokenRecord));
		    Arg1TP = PrevTP;		// Becase Prev gets Arg1 below!
		    DoZap = 1;			// Did Zap

		} else {
		    // Either LOGAND when Arg1 is ZERO or LOGOR when Arg1 is NON-ZERO
		    // ... Zap everything after Arg1.
		    if (Arg2TP) {
			SC_CGTokenZapTree(CGP->VMP, Arg2TP);
			DoZap = 1;		// Not zapped if Arg1 was last!
		    }
		    Arg1TP->NextP = NULL;
		    Arg2TP = NULL;
		}
	    }

	    PrevTP = Arg1TP;
	    if ((Arg1TP = Arg2TP)) Arg2TP = Arg2TP->NextP;
	}
    }
    return DoZap;	// **DO NOT** goto DoneDone... does own zapping!
    
XformPlus:
    //	  (+ A 0) or (+ 0 A) --> A
    //		EQForm: (+= A 0) --> A
    //	  (+ (Neg A) (Neg B)) --> (Neg (+ A B))
    //	  (+ (Neg A) B) --> (- B A)
    //	  (+ A (Neg B)) --> (- A B)
    //		EQForm: (+= A (Neg B)) --> (-= A B)
    
    if (SC_CGIsIntTP(Arg1TP, 0LL)) {
	Arg2TP->NextP = TP->NextP;	// Stash here in case TP had a NextP
	*TP = *Arg2TP;
	DoZap = 2;
	
    } else if (SC_CGIsIntTP(Arg2TP, 0LL)) {
	Arg1TP->NextP = TP->NextP;	// Stash here in case TP had a NextP
	*TP = *Arg1TP;
	DoZap = 2;

    } else if (SC_CGIsOpTP(Arg1TP, SC_UMINUS_ID)) {
	if (SC_CGIsOpTP(Arg2TP, SC_UMINUS_ID)) {
	    TP->Id = SC_UMINUS_ID;
	    Arg1TP->Id = SC_PLUS_ID;
	    Arg1TP->NextP = NULL;
	    Arg1TP->DownP->NextP = Arg2TP->DownP;
	    DoZap = 1;
		Arg1TP = Arg2TP;	// For DoZap

	} else {
	    TP->Id = SC_MINUS_ID;
	    TP->DownP = Arg2TP;
	    Arg2TP->NextP = Arg1TP->DownP;
	    DoZap = 1;
	}
	
    } else if (SC_CGIsOpTP(Arg2TP, SC_UMINUS_ID)) {
	TP->Id = (EQForm) ? SC_MINUSEQ_ID : SC_MINUS_ID;
	Arg1TP->NextP = Arg2TP->DownP;
	DoZap = 1;
	    Arg1TP = Arg2TP;		// For DoZap
    }
    goto DoneDone;

XformMult:
    //	  (* A 0) or (* 0 A) --> 0 or (, A 0)	if A is an Op with side-effects
    //		EQForm: (*= A 0) --> (= A 0)		**NOT just 0**
    //
    //	  (* A 0.0) or (* 0.0 A) --> 0.0 or (, A 0.0)	If A is an Op with side-effects
    //		EQForm: (*= A 0.0) --> (= A 0.0)	**NOT just 0.0**
    //
    //    (* A 1) or (* 1 A) --> A
    //		EQForm:	(*= A 1) --> A
    //	  (* A -1) or (* -1 A) --> (Neg A)
    //		EQForm: (*= A -1) --> (= A (Neg A))	**NOT just (Neg A)**
    //
    //    (* (Neg A) (Neg B)) --> (* A B)
    //	  (* A (Neg B))					** NOT EQForm**  +++
    //	     if A is NUMB --> (* -A B)
    //	     if A is (C - D) --> (* -A B) where -A is (D - C);
    //       Otherwise (NEG (* A B))
    //    (* (Neg A) B)
    //	     if B is NUMB --> (* A -B)
    //	     if B is (C - D) --> (* A -B) where -B is (D - C);
    //       Otherwise (NEG (* A B))
    //
    //	  Note:  Moving NEG outside generally better, except when
    //		 NEG can be absorbed by NUMB or SUB operand.
    //		 +++  (*= A (Neg B)) simplifies only if (Neg B) simplifies!

    if (Arg1TP->Type == SC_NUMBER_TOKEN) {
	if (Arg1TP->Id == SC_INTNUMB_ID) {
	    // Arg1 is 0, 1 or -1
	    if (Arg1TP->Data == 0LL) {
		TP->Id = SC_COMMA_ID;
		*Arg1TP = *Arg2TP;
		Arg1TP->NextP = Arg2TP;
		SC_CGSetTPInt(Arg2TP, 0);		
	    } else if (Arg1TP->Data == 1LL) {
		Arg2TP->NextP = TP->NextP;	// Stash in case TP had a NextP
		*TP = *Arg2TP;
		DoZap = 2;
	    } else if (Arg1TP->Data == -1LL) {
		TP->Id = SC_UMINUS_ID;
		TP->DownP = Arg2TP;
		DoZap = 1;
	    }

	} else if (*(double *)&Arg1TP->Data == 0.0) {
	    TP->Id = SC_COMMA_ID;
	    *Arg1TP = *Arg2TP;
	    Arg1TP->NextP = Arg2TP;
	    SC_CGSetTPFloat(Arg2TP, 0.0);	    
	}
	// In case none of the above applies!
	if (!DoZap && (SC_CGIsOpTP(Arg2TP, SC_UMINUS_ID))) {
	    SC_CGNegateNumbTP(Arg1TP);
	    Arg1TP->NextP = Arg2TP->DownP;
	    DoZap = 1;
		Arg1TP = Arg2TP;		// For DoZap
	}

    } else if (Arg2TP->Type == SC_NUMBER_TOKEN) {
	if (Arg2TP->Id == SC_INTNUMB_ID) {
	    // Arg2 is 0, 1, or -1
	    if (Arg2TP->Data == 0LL) {
		if (EQForm)
		    TP->Id = SC_EQ_ID;
		else if ((Arg1TP->Type == SC_OPERATOR_TOKEN) && !(SC_CGOpNoSideEffect(Arg1TP->Id)))
		    TP->Id = SC_COMMA_ID;
		else {
		    TP->DownP = NULL;
		    SC_CGSetTPInt(TP, 0);
		    DoZap = 2;
		}
	    } else if (Arg2TP->Data == 1LL) {
		Arg1TP->NextP = TP->NextP;	// Stash in case TP had a NextP
		*TP = *Arg1TP;
		DoZap = 2;
	    } else if (Arg2TP->Data == -1LL) {
		if (EQForm) {
		    SC_TokenPointer	NewTP = SC_TokenNew(CGP->VMP);
		    TP->Id = SC_EQ_ID;
		    Arg2TP->Type = SC_OPERATOR_TOKEN;
		    Arg2TP->Id = SC_UMINUS_ID;
		    Arg2TP->DownP = NewTP;
		    *NewTP = *Arg1TP;			// Copy it over
		    NewTP->NextP = NULL;
		    DoZap = -1;				// Made changes, but no zapping
		} else {
		    TP->Id = SC_UMINUS_ID;
		    Arg1TP->NextP = NULL;
		    DoZap = 1;
			Arg1TP = Arg2TP;		// For DoZap
	        }
	    }

	} else if (*(double *)&Arg2TP->Data == 0.0) {
	    if (EQForm)
		TP->Id = SC_EQ_ID;
	    else if ((Arg1TP->Type == SC_OPERATOR_TOKEN) && !(SC_CGOpNoSideEffect(Arg1TP->Id)))
		TP->Id = SC_COMMA_ID;
	    else {
		TP->DownP = NULL;
		SC_CGSetTPFloat(TP, 0.0);
		DoZap = 2;
	    }
	}

	// In case none of the above applies!
	if (!DoZap && (SC_CGIsOpTP(Arg1TP, SC_UMINUS_ID))) {
	    SC_CGNegateNumbTP(Arg2TP);
	    TP->DownP = Arg1TP->DownP;
	    TP->DownP->NextP = Arg2TP;
	    DoZap = 1;
	}

    } else if (SC_CGIsOpTP(Arg1TP, SC_UMINUS_ID)) {
	if (SC_CGIsOpTP(Arg2TP, SC_UMINUS_ID)) {
	    TP->DownP = Arg1TP->DownP;
	    TP->DownP->NextP = Arg2TP->DownP;
	    DoZap = 2;

	// Case of Arg2 being NUMB is handled higher up!
	} else if (SC_CGIsOpTP(Arg2TP, SC_MINUS_ID)) {
	    SC_CGNegateSubOpTP(Arg2TP);
	    TP->DownP = Arg1TP->DownP;
	    TP->DownP->NextP = Arg2TP;
	    DoZap = 1;

	} else {
	    TP->Id = SC_UMINUS_ID;
	    Arg1TP->Id = SC_MULT_ID;
	    Arg1TP->NextP = NULL;
	    Arg1TP->DownP->NextP = Arg2TP;
	    DoZap = -1;			// Changed things, but no zapping
	}
    } else if (SC_CGIsOpTP(Arg2TP, SC_UMINUS_ID)) {

	// Case of Arg1 being NUMB is handled higher up!
	if (SC_CGIsOpTP(Arg1TP, SC_MINUS_ID)) {
	    SC_CGNegateSubOpTP(Arg1TP);
	    Arg1TP->NextP = Arg2TP->DownP;
	    DoZap = 1;
		Arg1TP = Arg2TP;		// For DoZap

	} else if (! EQForm) {			// **NOT** EQForm
	    TP->Id = SC_UMINUS_ID;
	    Arg2TP->Id = SC_MULT_ID;
	    TP->DownP = Arg2TP;
	    Arg1TP->NextP = Arg2TP->DownP;
	    Arg2TP->DownP = Arg1TP;
	    DoZap = -1;			// Changed things, but no zapping
	}
    }
    goto DoneDone;

XformMinus:
    //	  (- A A) --> 0			// Only for A is Ident, NOT OP with side-effects
    //		EQform: (-= A A) --> 0
    //    (- A 0) --> A
    //		EQForm: (-= A 0) --> A
    //	  (- 0 A) --> (Neg A)
    //	  (- (Neg A) (Neg B)) --> (Neg (- A B))
    //	  (- (Neg A) B) --> (Neg (+ A B))
    //    (- A (Neg B)) --> (+ A B)
    //		EQForm: (-= A (Neg B)) --> (+= A B)
    
    if ((Arg1TP->Type == SC_IDENTIFIER_TOKEN) && Arg1TP->Data &&
	(Arg2TP->Type == SC_IDENTIFIER_TOKEN) && (Arg1TP->Data == Arg2TP->Data)) {
	// CHECK Arg TP->Data has been resolved to a VEX--not NULL

	SC_CGSetTPInt(TP, 0LL);
	TP->DownP = NULL;
	DoZap = 2;
    
    } else if (SC_CGIsIntTP(Arg2TP, 0LL)) {
	Arg1TP->NextP = TP->NextP;	// Stash in case TP had a NextP
	*TP = *Arg1TP;
	DoZap = 2;
	
    } else if (SC_CGIsIntTP(Arg1TP, 0LL)) {
	TP->Id = SC_UMINUS_ID;
	TP->DownP = Arg2TP;
	DoZap = 1;

    } else if (SC_CGIsOpTP(Arg1TP, SC_UMINUS_ID)) {
	if (SC_CGIsOpTP(Arg2TP, SC_UMINUS_ID)) {
	    TP->Id = SC_UMINUS_ID;
	    Arg1TP->Id = SC_MINUS_ID;
	    Arg1TP->NextP = NULL;
	    Arg1TP->DownP->NextP = Arg2TP->DownP;
	    DoZap = 1;
		Arg1TP = Arg2TP;	// For DoZap
		
	} else {
	    TP->Id = SC_UMINUS_ID;
	    Arg1TP->Id = SC_PLUS_ID;
	    Arg1TP->NextP = NULL;
	    Arg1TP->DownP->NextP = Arg2TP;
	    DoZap = -1;			// Changed things, but no zapping
	}
	
    } else if (SC_CGIsOpTP(Arg2TP, SC_UMINUS_ID)) {
	TP->Id = (EQForm) ? SC_PLUSEQ_ID : SC_PLUS_ID;
	Arg1TP->NextP = Arg2TP->DownP;
	DoZap = 1;
	    Arg1TP = Arg2TP;		// For DoZap
    }
    goto DoneDone;

XformDiv:
    //	  (/ A 1) --> A
    //		EQForm: (/= A 1) --> A
    //	  (/ A -1) --> (Neg A)
    //		EQForm: (/= A -1) --> (= A (Neg A))   ***NOT just (Neg A)***
    //	  (/ (Neg A) (Neg B)) --> (/ A B)
    //    (/ A (Neg B))
    //	     if A is NUMB --> (/ -A B)
    //	     if A is (C - D) --> (/ -A B) where -A is (D - C);
    //       Otherwize (NEG (/ A B))   **NOT EQForm**
    //    (/ (Neg A) B)
    //	     if B is NUMB --> (/ A -B)
    //	     if B is (C - D) --> ( A -B) where -B is (D - C);
    //       Otherwise (NEG (/ A B))
    
    if (Arg2TP->Type == SC_NUMBER_TOKEN) {
	if (Arg2TP->Id == SC_INTNUMB_ID) {
	    if (Arg2TP->Data == 1LL) {
		Arg1TP->NextP = TP->NextP;	// Stash in case TP had a NextP
		*TP = *Arg1TP;
		DoZap = 2;

	    } else if (Arg2TP->Data == -1LL) {
		if (EQForm) {
		    SC_TokenPointer	NewTP = SC_TokenNew(CGP->VMP);
		    TP->Id = SC_EQ_ID;
		    Arg2TP->Type = SC_OPERATOR_TOKEN;
		    Arg2TP->Id = SC_UMINUS_ID;
		    Arg2TP->DownP = NewTP;
		    *NewTP = *Arg1TP;			// Copy it over
		    NewTP->NextP = NULL;
		    DoZap = -1;				// Made changes, no zapping
		} else {
		    TP->Id = SC_UMINUS_ID;
		    Arg1TP->NextP = NULL;
		    DoZap = 1;
			Arg1TP = Arg2TP;		// For DoZap
		}
	    }
	}

	// If none of the above applies!
	if (!DoZap && (SC_CGIsOpTP(Arg1TP, SC_UMINUS_ID))) {
	    SC_CGNegateNumbTP(Arg2TP);
	    TP->DownP = Arg1TP->DownP;
	    TP->DownP->NextP = Arg2TP;
	    DoZap = 1;
	}

    } else if (SC_CGIsOpTP(Arg1TP, SC_UMINUS_ID)) {
	if (SC_CGIsOpTP(Arg2TP, SC_UMINUS_ID)) {
	    TP->DownP = Arg1TP->DownP;
	    TP->DownP->NextP = Arg2TP->DownP;
	    DoZap = 2;

	} else if (SC_CGIsOpTP(Arg2TP, SC_MINUS_ID)) {
	    SC_CGNegateSubOpTP(Arg2TP);
	    TP->DownP = Arg1TP->DownP;
	    TP->DownP->NextP = Arg2TP;
	    DoZap = 1;
	    
	} else {
	    TP->Id = SC_UMINUS_ID;
	    Arg1TP->Id = SC_DIV_ID;
	    Arg1TP->NextP = NULL;
	    Arg1TP->DownP->NextP = Arg2TP;
	    DoZap = -1;			// Changed things, but no zapping
	}
	
    } else if (SC_CGIsOpTP(Arg2TP, SC_UMINUS_ID)) {
	if (Arg1TP->Type == SC_NUMBER_TOKEN) {
	    SC_CGNegateNumbTP(Arg1TP);
	    Arg1TP->NextP = Arg2TP->DownP;
	    DoZap = 1;
		Arg1TP = Arg2TP;		// For DoZap

	} else if (SC_CGIsOpTP(Arg1TP, SC_MINUS_ID)) {
	    SC_CGNegateSubOpTP(Arg1TP);
	    Arg1TP->NextP = Arg2TP->DownP;
	    DoZap = 1;
		Arg1TP = Arg2TP;		// For DoZap

	} else if (! EQForm) {			// NOT for EQForm!
	    TP->Id = SC_UMINUS_ID;
	    Arg2TP->Id = SC_DIV_ID;
	    TP->DownP = Arg2TP;
	    Arg1TP->NextP = Arg2TP->DownP;
	    Arg2TP->DownP = Arg1TP;
	    DoZap = -1;			// Changed things, but no zapping
	}
    }
    goto DoneDone;

XformNeg:
    //    (Neg (Neg A)) --> A
    //    (Neg (A - B)) --> (B - A)
    //    (Neg (A * n)) --> (A * -n) and (Neg (n * A)) --> (-n * A)
    //    (Neg (A / n)) --> (A / -n) and (Neg (n / A)) --> (-n / A)
    
    if (Arg1TP->Type == SC_OPERATOR_TOKEN) {
	if (Arg1TP->Id == SC_UMINUS_ID) {
	    Arg2TP = Arg1TP->DownP;
	    Arg2TP->NextP = TP->NextP;		// Stash in case TP had a NextP
	    *TP = *Arg2TP;
	    DoZap = 2;

	} else if (Arg1TP->Id == SC_MINUS_ID) {
	    TP->Id = SC_MINUS_ID;
	    TP->DownP = Arg1TP->DownP->NextP;
	    TP->DownP->NextP = Arg1TP->DownP;
	    Arg1TP->DownP->NextP = NULL;
	    DoZap = 1;
	    
	} else if ((Arg1TP->Id == SC_MULT_ID) || (Arg1TP->Id == SC_DIV_ID)) {
	    if (Arg1TP->DownP->Type == SC_NUMBER_TOKEN) {
		// First arg is numb, negate it and eliminate top NEG op
		SC_CGNegateNumbTP(Arg1TP->DownP);
		Arg1TP->NextP = TP->NextP;	// Stash in case TP had a NextP
		*TP = *Arg1TP;
		DoZap = 1;

	    } else if (Arg1TP->DownP->NextP->Type == SC_NUMBER_TOKEN) {
		// Second arg is numb, negate it and eliminate top NEG op
		SC_CGNegateNumbTP(Arg1TP->DownP->NextP);
		Arg1TP->NextP = TP->NextP;	// Stash in case TP had a NextP
		*TP = *Arg1TP;
		DoZap = 1;
	    }
	}
    }
    goto DoneDone;

XformLogPos:
    //    (LOGPOS S) --> 1 where S is not Op, Ident, or Number.
    //    (LOGPOS (! A)) --> (! A)
    //	  (LOGPOS (LOGPOS A)) --> (LOGPOS A)

    if (Arg1TP->Type == SC_OPERATOR_TOKEN) {
	if ((Arg1TP->Id == SC_LOGNEG_ID) || (Arg1TP->Id == SC_LOGPOS_ID)) {
	    TP->DownP = Arg1TP->DownP;
	    TP->Id = Arg1TP->Id;
	    DoZap = 1;				// Zap Arg1
	}

    } else if ((Arg1TP->Type != SC_IDENTIFIER_TOKEN) &&
	       (Arg1TP->Type != SC_NUMBER_TOKEN)) {

	SC_CGSetTPInt(TP, 1LL);	    	
	TP->DownP = NULL;
	DoZap = 1;				// Zap Arg1
    }
    goto DoneDone;

XformLogNeg:
    //	  (! (! A)) --> (LOGPOS A)
    //    (! (LOGPOS A) --> (! A)
    //	  (! (&& A B C)) --> (|| (! A) (! B) (! C))
    //	  (! (|| A B C)) --> (&& (! A) (! B) (! C))
    //	  (! S) --> 0 where S is not Op, Ident, or Number.
    //
    // NOTE: (! n) is handled in CheckMath for numeric n (even floats).
    //	     Non-numeric n should be handled here!

    if (Arg1TP->Type == SC_OPERATOR_TOKEN) {

	if (Arg1TP->Id == SC_LOGNEG_ID) {
	    TP->DownP = Arg1TP->DownP;
	    TP->Id = SC_LOGPOS_ID;
	    DoZap = 1;			// Zap Arg1

	} else if (Arg1TP->Id == SC_LOGPOS_ID) {
	    TP->DownP = Arg1TP->DownP;
	    TP->Id = SC_LOGNEG_ID;
	    DoZap = 1;			// Zap Arg1

	} else if ((Arg1TP->Id == SC_LOGAND_ID) || (Arg1TP->Id == SC_LOGOR_ID)) {
	    // Distribute the LogNot
	    SC_CGDistributeOp(CGP->VMP, Arg1TP, SC_LOGNEG_ID);
	    // Make TP the logical op, but switch And/Or
	    TP->DownP = Arg1TP->DownP;
	    TP->Id = (Arg1TP->Id == SC_LOGAND_ID) ? SC_LOGOR_ID : SC_LOGAND_ID;
	    DoZap = 1;			// Zap Arg1
	}

    } else if ((Arg1TP->Type != SC_IDENTIFIER_TOKEN) && (Arg1TP->Type != SC_NUMBER_TOKEN)) {
	// Logically a constant TRUE, so LogNot is just False == 0
	TP->Type = SC_NUMBER_TOKEN;
	TP->Id = SC_INTNUMB_ID;
	TP->Data = 0LL;
	TP->DownP = NULL;
	DoZap = 1;			// Zap Arg1TP
    }
    goto DoneDone;

DoneDone:
    if (DoZap > 0) {
	SC_SlabZapItem(CGP->VMP->TokenSP, Arg1TP, sizeof(SC_TokenRecord));
	if (DoZap == 2)
	    SC_SlabZapItem(CGP->VMP->TokenSP, Arg2TP, sizeof(SC_TokenRecord));
    }

    return DoZap;
}


// SC_CGOpIntoComma
//
// (- (, A B C... Z) --> (, A B C... (- Z)) also for ! and ~
// (MathOp n (, A B C...Z m)) --> (, A B C...Z (MathOp n m)) when n and m are numbers.
// (MathOp (, A B C...Z n) m) --> (, A B C...Z (MathOp n m)) when n and m are numbers
// These forms are then further reduced with the other rules.

Int16	SC_CGOpIntoComma(SC_CGPointer CGP, SC_TokenPointer TP)
{
    SC_TokenPointer	Arg1TP = TP->DownP;
    SC_TokenPointer	Arg2TP = Arg1TP->NextP;		// May be NULL!
    SC_TokenPointer	LastTP;
    SC_TokenRecord	T1, T2;

    if ((TP->Id == SC_UMINUS_ID) || (TP->Id == SC_LOGNEG_ID) || (TP->Id == SC_BITNEG_ID)) {
	if (! SC_CGIsOpTP(Arg1TP, SC_COMMA_ID)) goto Nada;
	// Unary Ops of concern... Arg1 is a Comma
	LastTP = SC_CGGetLastArg(Arg1TP);

	T1 = *TP; T2 = *LastTP;		// Stash
	TP->Id = Arg1TP->Id;		// Make TP into Arg1
	TP->DownP = Arg1TP->DownP;
	*LastTP = T1;			// Last arg becomes Op
	LastTP->NextP = NULL;		// Just in case
	LastTP->DownP = Arg1TP;		// Recycle
	*Arg1TP = T2;			// Get what LastTP used to be
	goto DidIt;
    }

    // TP is any Math op!
    if (Arg1TP->Type == SC_NUMBER_TOKEN) {
	// Arg1 is n, Arg2 is Comma Op... Last is m
	if ((! Arg2TP) || (! SC_CGIsOpTP(Arg2TP, SC_COMMA_ID))) goto Nada;
	LastTP = SC_CGGetLastArg(Arg2TP);
	if (LastTP->Type != SC_NUMBER_TOKEN) goto Nada;

	T1 = *TP; T2 = *LastTP;		// Stash
	TP->Id = SC_COMMA_ID;		// TP becomes Comma
	TP->DownP = Arg2TP->DownP;
	*LastTP = T1;			// Last arg becomes Op
	LastTP->NextP = NULL;		// In case it had one!
	*Arg2TP = T2;			// Op gets n and m as args
	goto DidIt;

    } else if (Arg2TP && (Arg2TP->Type == SC_NUMBER_TOKEN)) {
	// If there is an Arg2 and is m, Arg1 is Comma Op... Last is n
	if (! SC_CGIsOpTP(Arg1TP, SC_COMMA_ID)) goto Nada;
	LastTP = SC_CGGetLastArg(Arg1TP);
	if (LastTP->Type != SC_NUMBER_TOKEN) goto Nada;

	T1 = *TP; T2 = *LastTP;		// Stash
	TP->Id = SC_COMMA_ID;		// TP becomes Comma
	TP->DownP = Arg1TP->DownP;
	*LastTP = T1;			// Becomes Op
	LastTP->NextP = NULL;		// Just in case
	*Arg1TP = T2;			// Recycle the token
	Arg1TP->NextP = Arg2TP;		// Op gets n and m as args
	goto DidIt;
    }

Nada:	return 0;

	// Last elt in COMMA may need further simplification,
	// delaing with NEG or all numbers!
DidIt:	SC_CGCheckPass(CGP, LastTP, SC_CHECKEXPR_ACT);
	return 1;
}


// SC_CGRewindLastEnd will move back 1 CHAR (Glyph) from CGP->LastEndP.
// Because of UTF-8, this may step back 4 bytes.
char *		SC_CGRewindLastEnd(SC_CGPointer CGP)
{
    char *		SP = CGP->LastEndP;

    if (SP > CGP->ScriptStartP)
	while (ED_MIDUTF8(*(--SP)));
    return SP;
}

// SC_CGUnNestFinLex will go up the Lex hierarchy, skipping over FIN Lex
// to find one with the TargetFlag.  But it will not cross Func lex, will
// stop at the first one it hits, which could be Top.

SC_LexPointer	SC_CGUnNestFinLex(SC_LexPointer LexP, Uns16 TargetFlag)
{
    while (! (LexP->Flags & SC_LEXFUNCFLAG)) {
	if (!(LexP->Flags & SC_LEXFINFLAG) && (LexP->Flags & TargetFlag))
	    break;
	LexP = LexP->SupP;
    }

    return LexP;
}

// SC_CGFinishLex simply sets the FIN flag for LexP... it will go up the
// containment chain and FIN off the container if it also had a ONE flag.
//
// NOTE:  If the LexP being closed was a "conditional" one, then the REG
//	      Vex  cached in the Lex will be invalidated!

void		SC_CGFinishLex(SC_CGPointer CGP, SC_LexPointer LexP)
{
    do {
	LexP->Flags |= SC_LEXFINFLAG;
	LexP = LexP->SupP;
    } while (LexP && (LexP->Flags & SC_LEXONEFLAG));
}


SC_LexPointer	SC_CGAddLex(SC_CGPointer CGP, Uns32 Flags)
{
    SC_LexPointer	LexP;

    LexP = SC_CGLexNew(CGP->VMP);
    LexP->Flags = Flags;
    // Link it in....
    LexP->SupP = CGP->LastLexP;

    if (Flags & SC_LEXFUNCFLAG) {
	LexP->FLevel = CGP->FuncLexP->FLevel + 1;
	LexP->BLevel = 0;
    } else {
	LexP->FLevel = CGP->LastLexP->FLevel;
	LexP->BLevel = CGP->LastLexP->BLevel + 1;
    }

    CGP->LastLexP = LexP;
    return LexP;
}

// SC_CGZapLastLex will kill off CGP->LastLexP... assumes it was FIN.
// It will update CGP->LastLexP to its superior.
//
// Caller should update CGP->FuncLexP and DispLexP as these could
// have *BEEN* killed if same as CGP->LastLexP.


void		SC_CGZapLastLex(SC_CGPointer CGP)
{
    SC_LexPointer	LexP = CGP->LastLexP;

    // Invalidate RegCache *AFTER* a COND Lex (If, Loop, Etc...)
    if (LexP->Flags & SC_LEXCONDFLAG)
	SC_CGRegCacheInval(CGP, CGP->FuncLexP->MapP, LexP->BLevel);

    CGP->LastLexP = LexP->SupP;
    if (LexP->MapP) SC_CGMapZap(CGP->VMP, LexP->MapP);
    if (LexP->VDictP) SC_DictPurge(CGP->VMP, LexP->VDictP, 0);
    if (LexP->DDictP) SC_DictPurge(CGP->VMP, LexP->DDictP, 0);
    // printf("Zap Lex: %lx  Sup:%lx\n", (Int64)LexP, (Int64)LexP->SupP);		
    SC_SlabZapItem(CGP->VMP->LexSP, LexP, sizeof(SC_LexRecord));
}

// SC_CGDeclareVar will create a **NEW** local (or Global depending on
// CGP->LastLexP) variable.  The name will be placed in the appropriate
// VDict, a VexP will be created for it and added to the LexP->MapP.
//
// The VexP will also get stashed in the Var token.
//
// Var names are normally registered locally on the VDict for the containing
// block; while, allocation is **hoisted** to the containing FUNC (even TOP)!
// If IsFunc (dealing with a Function name), the Var name too will be
// **hoisted** to the containing FUNC.
//
// NOTE:  The main (TOP Lex) code normally has only global variables.  BUT, it
//	  could have a BEGIN-END sub-block that declares a local variable within
//	  it.  As these allocations are hoisted to the FUNC, even the TOP Lex
//	  may end up with locals!  Afterall, TOP is just a function, albeit
//	  implicitly declared, that gets called just once.
//
//	  Locals in the TOP Lex are for allocation only, these will be in its
//	  Map but not its DICT.
//
// NOTE:  Cannot declare two Vars with the same name in the same Lex.  But a new
//	  Var can happily override/overshadow an inherited one with a similar
//	  name from a higher (outer) Lex; local Vars can always overshadow
//	  inherited, closed over, or global ones.
//
// NOTE:  Every code block gets its own Dict of LOCALLY declared vars.  But
//	  every FUNC block has a Dict full of all Global and Closed vars that it
//	  uses (even in sub-blocks)... these can obviously be overridden.

Int16	SC_CGAddLocal(SC_CGPointer CGP, SC_TokenPointer VarTP, SC_LexPointer FuncLexP)
{
    SC_VexPointer	VexP = SC_CGVexNew(CGP->VMP);

    // NOTE: SC_CGAddLocal *IS* also called for IN/OUT params, so
    //	     effectively builds their count into MapP->LCount!

    VarTP->Data = (Int64)VexP;			// Stash in Token
    VexP->Class = SC_LVEX;
    VexP->NextP = FuncLexP->MapP->LVexP;
    FuncLexP->MapP->LVexP = VexP;
    FuncLexP->MapP->LCount += 1;
    VexP->Index = (VexP->NextP) ? VexP->NextP->Index + 1 : 0;
    if (FuncLexP->MapP->VarCount++ == SC_CGMAXVARCOUNT) 
	SC_CODEERROR(CGP, VarTP, SC_STR_ErrorLimits);
    return SC_SUCC_RES;

ErrorOut:
    return SC_ERR_RES;
}

// SC_CGAddGlobal is normally called to add a new Global var to the TOP Func.
// In that case, GIndex is given as -1.
//
// Where a sub-FUNC happens to reference a Global var, this function is called
// to add a local GVar reference to the sub-FUNC.  As such, the GVar should have
// a GIndex field that matches that of the real global in TOP.

Int16	SC_CGAddGlobal(SC_CGPointer CGP, SC_TokenPointer VarTP, SC_LexPointer FuncLexP, Uns16 GIndex)
{
    SC_VexPointer	VexP = SC_CGVexNew(CGP->VMP);

    VarTP->Data = (Int64)VexP;			// Stash in Token
    VexP->Class = SC_GVEX;
    VexP->NextP = FuncLexP->MapP->GVexP;
    FuncLexP->MapP->GVexP = VexP;
    FuncLexP->MapP->GCount += 1;
    if (GIndex == 0xFFFF) {
	VexP->Index = (VexP->NextP) ? VexP->NextP->Index + 1 : 0;
	FuncLexP->MapP->VarCount += 1;
	if (FuncLexP->MapP->VarCount == SC_CGMAXGLOBALCOUNT)
	    SC_CODEERROR(CGP, VarTP, SC_STR_ErrorLimits);
     } else
	VexP->Index = GIndex;
   return SC_SUCC_RES;

ErrorOut:
    return SC_ERR_RES;
}    

void SC_CGRegisterVar(SC_VMPointer VMP, SC_LexPointer BlockLexP, SC_StrHPointer StrHP, SC_VexPointer VexP)
{
    SC_DictVPointer		DictP = BlockLexP->VDictP;
    SC_PairHPointer		PairP = SC_BigEntryNew(VMP);

    if (! DictP) BlockLexP->VDictP = DictP = SC_DictCreateNewDict(VMP, 0);

    // Stores [StrHP - VexP] pairs... VexRecords are private, don't have Type field!!
    PairP->KeyType = SC_VAL_PTR;
    PairP->ValType = SC_VAL_PRV;	
    PairP->KeyP = (SC_VarVPointer)StrHP;
    
    PairP->ValueP = (void *)VexP;
    SC_DictStashNewPair(VMP, DictP, PairP);
}

// THIS run is a TRY.... so inherit Konst/String/Global from last run.
// Called at beginning of SC_CompileScript.  (Assume OldADP->SlotCount
// is often larger than Count... had room for more, so do same in copy.)

    void SC_IOIAuxCopyADP(SC_VMPointer VMP, SC_ArrDataPointer *NewPP, SC_ArrDataPointer OldADP, Uns32 Count, Uns8 IsVar)
    {
	SC_ArrDataPointer	NewADP;
	void			**DP, **SP;
	Uns32			More;

	More = OldADP->SlotCount;
	NewADP = SC_ArrAllocNewData(VMP, NewPP, More, 0);	// Sets MasterPP too!
	More -= Count;
	DP = NewADP->Data;
	SP = OldADP->Data;

	if (IsVar) {
	    while (Count--) {
		if ((*DP = *SP++))
		    SC_VARREF((SC_VarVPointer)*DP);
		++DP;
	    }

	} else
	    while (Count--) *DP++ = *SP++;

	// Extra slots left over, just copy whatever was there
	if (More > 0)
	    while (More--) *DP++ = *SP++;
    }

void	SC_InheritOldInfo(SC_CGPointer CGP)
{
    SC_VMPointer	VMP = CGP->VMP;
    SC_PairHPointer	PairP;
    SC_VexPointer	VexP;
    SC_MapPointer	MapP;
    SC_DictVPointer	DictP;
    Uns32		I = 0;

    if (VMP->OldGlobP) {
	// Obviously interactive mode... and there was a previous run!
	// (Always get a GlobP, even if no global vars or functions!)
	MapP = CGP->TopLexP->MapP;

	// Create Konst ADP for Map... will get transfered to Script after compile!
	if ((MapP->KonstCount = VMP->OldKonstCount)) {						// Assign
	    SC_IOIAuxCopyADP(VMP, &MapP->KonstADP, VMP->OldKonstADP, VMP->OldKonstCount, 0);	// Holds numbers (Int/Flt)
	    // Build KonstDictP for Map
	    DictP = MapP->KonstDictP = SC_DictCreateNewDict(VMP, VMP->OldKonstCount);
	    for (I = 0; I < VMP->OldKonstCount; I++) {
		PairP = SC_BigEntryNew(VMP);
		PairP->KeyType = SC_VAL_INT;
		PairP->ValType = SC_VAL_INT;
		PairP->KeyP = MapP->KonstADP->Data[I];
		PairP->ValueP = (SC_VarVPointer)(Int64)I;
		SC_DictStashNewPair(VMP, DictP, PairP);
	    }
	}

	// Create StringADP for Map... will get transfered to Script after compile!
	if ((MapP->StringCount = VMP->OldStrCount)) {						// Assign
	    SC_IOIAuxCopyADP(VMP, &MapP->StringADP, VMP->OldStrADP, VMP->OldStrCount, 1);	// Holds StrHP
	    // Also build StringDictP for Map
	    DictP = MapP->StringDictP = SC_DictCreateNewDict(VMP, VMP->OldStrCount);
	    for (I = 0; I < VMP->OldStrCount; I++) {
		PairP = SC_BigEntryNew(VMP);
		PairP->KeyType = SC_VAL_PTR;
		PairP->ValType = SC_VAL_INT;
		PairP->KeyP = MapP->StringADP->Data[I];
		SC_VARREF((SC_VarVPointer)PairP->KeyP);
		PairP->ValueP = (SC_VarVPointer)(Int64)I;
		SC_DictStashNewPair(VMP, DictP, PairP);
	    }
	}

	// Create GNameADP for Script... needs it already.
	if (VMP->OldGNameADP)
	    SC_IOIAuxCopyADP(VMP, &CGP->ScriptP->GNameADP, VMP->OldGNameADP, VMP->OldGlobP->Count, 1);	// Holds StrHP

	// Create GVex for MapP... GlobP will get transfered AFTER compile.
	// TopLex also needs VDictP.
	for (I = 0; I < VMP->OldGlobP->Count; I++) {
	    VexP = SC_CGVexNew(VMP);
	    VexP->Flags |= SC_VEXINGLOBFLAG;
	    VexP->Class = SC_GVEX;
	    VexP->Index = I;
	    VexP->NextP = MapP->GVexP;
	    MapP->GVexP = VexP;

	    // Register VEX in VDictP are [SHP - Vex]
	    SC_CGRegisterVar(VMP, CGP->TopLexP, (SC_StrHPointer)VMP->OldGNameADP->Data[I], VexP);
	}
	MapP->GCount += I;	// VMP->OldGlobP->Count
	MapP->VarCount += I;
    }
}    


// INTERACT mode: The Script chunk inherits some global variables, but goes off
// to re-declare one.  Redefining a global, normally prohibited, is allowed only
// in this limited case.  But the variable MUST be zeroed (re-initialized)
// before the script chunk is run--no longer uses its old value.  The InGZADP
// keeps track of such variables during the compile phase.
//
// NOTE:  If Var X is inherited, it cannot be re-declared *AFTER BEING USED* in
//	  this Script chunk.  Usage is seen as ACKNOWLEDGMENT of its existance,
//	  so becomes subject to "existing globals cannot be re-declared"!

void	SC_CGAddInGZero(SC_CGPointer CGP, Uns32 GIndex)
{
    SC_VarVPointer	GVarP;
    
    if (CGP->InGZADP == NULL) {
	// Allocate a min size
	SC_ArrAllocNewData(CGP->VMP, &CGP->InGZADP, SC_CGINGZINITSIZE, 0);
    } else if (CGP->InGZADP->SlotCount == CGP->InGZCount) {
	// Grow it first
	CGP->InGZADP = SC_ArrResizeData(CGP->VMP, CGP->InGZADP, CGP->InGZADP->SlotCount + SC_CGINGZGROWSIZE, 0);
    }

    // Get VarP, stash in InGZADP;
    GVarP = CGP->VMP->OldGlobP->GArr[GIndex];
    CGP->InGZADP->Data[CGP->InGZCount++] = GVarP;
    // Already in VMP->OldGlobP, so no need for a VARREF.
    // (Worse, all this will go away if error, so best to avoid another DEREF!)
}


// IsFunc: 0 --> NOT a func
//         1 --> ** FUNC DEFINITION **
//	   2 --> Just a func declaration
// These are differentiated so CAN define a FUNC after declaration, but
// CANNOT define an already defined FUNC!
//
// Functions can be declared first... defined in a later step.
// In that case, DECL will create (alloc) the Vex, later DEF will just use it.

    void	    SC_CGDVAuxAddGName(SC_CGPointer CGP, SC_StrHPointer StrHP, Uns16 Index)
    {
	SC_VMPointer		VMP = CGP->VMP;
	SC_ScriptPointer	ScriptP = CGP->ScriptP;

	if (ScriptP->GNameADP == NULL)
	    SC_ArrAllocNewData(VMP, &ScriptP->GNameADP, SC_CGINITGNAMECOUNT, 1);
	if (ScriptP->GNameADP->SlotCount <= Index)
	    ScriptP->GNameADP = SC_ArrResizeData(VMP, ScriptP->GNameADP, Index + SC_CGGROWGNAMECOUNT, 0);
	    
	ScriptP->GNameADP->Data[Index] = StrHP;
    }

	
Int16	SC_CGDeclareVar(SC_CGPointer CGP, SC_TokenPointer VarTP, Int16 IsFunc)
{
    SC_VMPointer		VMP = CGP->VMP;
    SC_LexPointer		LexP;		// Code block for Var
    SC_LexPointer		FuncLexP;	// Func block contains Code block
    SC_VexPointer		VexP;		// Allocation for Var
    SC_StrHPointer		StrHP;
    SC_PairHPointer		PairP;
    Int16			IsGlobal, DoAlloc, DoReg;
    

    // **** Register var name *********************************************************
    FuncLexP = CGP->FuncLexP;			// Closest FUNC Lex level
    // FuncLexP and LexP MAY be the same!
    LexP = (IsFunc) ? FuncLexP : CGP->LastLexP;
    IsGlobal = (LexP == CGP->TopLexP);

    // Get StrHP for Var name... comes in RefCount +1.  (NOT forced lowercase!)
    StrHP = SC_StrGetEntry(VMP, CGP->ScriptStartP + VarTP->ReadPos, VarTP->ReadLen);

    // CAN re-define names in MAIN pkg... must then use "sc." format to access them.
#if SC_CG_NOPKGNAMEREDEF
    // Do *NOT* allow reuse of Pkg names, regardless of case!
    if (((PairP = SC_CGGetPkgField(CGP, VMP->PkgADP->Data[0], VarTP))) &&	// Assign
	(PairP->ValType == SC_VAL_PIDX)) {
	SC_CODEERROR(CGP, VarTP, SC_STR_ErrorIllegalName);
    }
#endif

    // See if the Named var is already registered!
    PairP = NULL;
    DoReg = DoAlloc = 1;
    if (LexP->VDictP) {			// Existing DictP
	if ((PairP = SC_DictGetPair(LexP->VDictP, (SC_VarVPointer)StrHP, SC_VAL_PTR))) {
	    // Got existing VexP... see if we can override it!
	    VexP = (SC_VexPointer)PairP->ValueP;

	    if ((VexP->Class == SC_LVEX) ||
		(IsGlobal && (VexP->Class == SC_GVEX))) {

		DoAlloc = 0;
		VarTP->Data = (Int64)VexP;
		if (VexP->Flags & SC_VEXINGLOBFLAG) {
		    // In **INTERACT** mode...  inherited Global!!
		    DoReg = 0;
		    VexP->Flags &= ~SC_VEXINGLOBFLAG;
		    if (IsFunc == 2)		// Func Decl, Def later!
			VexP->Flags |= SC_VEXFDECFLAG;
		    SC_CGAddInGZero(CGP, VexP->Index);
		    
		} else if ((IsFunc == 1) && (VexP->Flags & SC_VEXFDECFLAG)) {
		    // Just re-use previously DECL (Func) Vex
		    VexP->Flags &= ~SC_VEXFDECFLAG;
		} else
		    SC_CODEERROR(CGP, VarTP, SC_STR_ErrorExistingVar);
	    }
	}
    } else					// Make a new DictP
	LexP->VDictP = SC_DictCreateNewDict(VMP, 0);

    // Allocate Vex: Create, put in FuncLexP->Map, and stash in VarTP->Data.
    if (DoAlloc) {
	if (IsGlobal) {
	    SC_VARREF(StrHP);
	    SC_CGDVAuxAddGName(CGP, StrHP, FuncLexP->MapP->GCount);
	    if (SC_ERR_RES == SC_CGAddGlobal(CGP, VarTP, FuncLexP, 0xFFFF)) goto ErrorOut;
	} else {
	    if (SC_ERR_RES == SC_CGAddLocal(CGP, VarTP, FuncLexP)) goto ErrorOut;
	}
	VexP = (SC_VexPointer)VarTP->Data;	// Get the new Vex
	if (IsFunc == 2)			// Func Decl, Def later!
	    VexP->Flags |= SC_VEXFDECFLAG;
    }
    
    // Now stash in VDict
    if (DoReg) {
	if (PairP) {
	    // Already found an inherited var, override in PairP
	    PairP->ValueP = (void *)VexP;
	} else {
	    // Will make a Dict if LexP does not have one!
	    // Make a new Pair for the Dict:  (VarNameStr:VexP)
	    SC_CGRegisterVar(VMP, LexP, StrHP, VexP);
	}
    }

    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);	// -1 here, came in with +1
    return SC_SUCC_RES;

ErrorOut:
    
    SC_VARDEREFCOND(VMP, StrHP, (SC_VarVPointer)StrHP);
    return SC_ERR_RES;
}


// Somewhat similar to Var.... Def are defined in code as:
//	"Def  A = "Blekh", B = 12 + 24, C = 12.3;"
//
// But code can also do strange things like:
//	"Def X = B - 3;"   X <-DEF-> 33 (36 - 3)
//	"Def Y = A;"	   Y <-DEF-> "Blekh"
//
// NOTE: Local Lex can define an LVAR with the same name as a GLOBAL, but it
//	 cannot RE-define the same LVAR in the same Lex block.  DEF are similar,
//	 in addition Local Lex CANNOT define a DEF previously defined in Pkg-0.
//
//	 INTERACT MODE: Defs are inherited from VMP->InDDictP... but these
//	 inherited Def can be re-defined, so nothing to do here!!
//
//	 DEF do not make it into .BSC files!!  DEF are creatures of the compiler,
//	 ByteCode just sees the defined values...

Int16	SC_CGDeclareDef(SC_CGPointer CGP, SC_TokenPointer DefTP, SC_TokenPointer ArgTP)
{
    SC_VMPointer		VMP = CGP->VMP;
    SC_LexPointer		LexP = CGP->LastLexP;		// Code block for Var
    SC_StrHPointer		StrHP, DataStrHP;
    SC_PairHPointer		PairP;
    char *			SP;
    Uns32			StrLen;
    Int16			DidProcSS = 0;

    // Get StrHP for Def name... Comes in with +1 RefCount
    StrHP = SC_StrGetEntry(VMP, CGP->ScriptStartP + DefTP->ReadPos, DefTP->ReadLen);
    // See if the Named Def is already registered--IN THIS LEX.
    // Def in a block can override Def defined globally or in the containing block.

    // CAN re-define DEF in Pkg[0]... must use "sc." version to access original!
#if SC_CG_NOPKGNAMEREDEF
    // Do *NOT* allow reuse of Pkg names, regardless of case!
    if (((PairP = SC_CGGetPkgField(CGP, (SC_PkgPointer)CGP->VMP->PkgADP->Data[0], DefTP))) &&	// Assign
	(PairP->ValType == SC_VAL_PIDX)) {
	SC_CODEERROR(CGP, DefTP, SC_STR_ErrorSysDef);
    }
#endif

    PairP = NULL;
    if (LexP->DDictP) {			// Existing DictP
	if ((PairP = SC_DictGetPair(LexP->DDictP, (SC_VarVPointer)StrHP, SC_VAL_PTR)))
	    SC_CODEERROR(CGP, DefTP, SC_STR_ErrorDupeDef);
    } else				// Make a new DictP
	LexP->DDictP = SC_DictCreateNewDict(VMP, 0);

    // Make a new Pair for the Dict:  (DefNameStr:Value)
    PairP = SC_BigEntryNew(VMP);
    PairP->KeyType = SC_VAL_PTR;
    PairP->KeyP = (SC_VarVPointer)StrHP;

    DataStrHP = NULL;
    if (ArgTP->Type == SC_NUMBER_TOKEN) {
	PairP->ValType = (ArgTP->Id == SC_INTNUMB_ID) ? SC_VAL_INT : SC_VAL_FLT;
	PairP->ValueP = (void *)ArgTP->Data;

    } else {
	PairP->ValType = SC_VAL_PTR;
	// ArgTP is a String...
	// 1) Simple string in the ED buffer, just read it
	// 2) SC_TOKEN_PROCSTR_FLAG:  Must ProcessShortString,
	//    will malloc a block and stash in ArgTP->Data... free before exit!
	// 3) String comes from *ANOTHER* DEF, in which case StrHP already in Data.
	
	if (!(DataStrHP = (SC_StrHPointer)ArgTP->Data)) {	// Assign
	    if (ArgTP->Flags & SC_TOKEN_PROCSTR_FLAG) {
		// String should be processed: it has escape sequences!
		DidProcSS = 1;
		if (! SC_ReadProcessShortString(CGP, ArgTP)) {
		    if (ArgTP->Data) free((void *)ArgTP->Data), ArgTP->Data = 0LL;
		    goto ErrorOut;
		}

		SP = (char *)ArgTP->Data;
	    } else
		SP = CGP->ScriptStartP + ArgTP->ReadPos;

	    // Get StrLen *AFTER* any processing... can make it shorter, not longer!
	    StrLen = ArgTP->ReadLen;
	    DataStrHP = SC_StrGetEntry(VMP, SP, StrLen);	// Comes in +1
	    if (DidProcSS && ArgTP->Data) free((void *)ArgTP->Data), ArgTP->Data = 0LL;
	}

	PairP->ValueP = (SC_VarVPointer)DataStrHP;
    }
    SC_DictStashNewPair(VMP, LexP->DDictP, PairP);
    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);
    if (DataStrHP) SC_VARDEREF(VMP, (SC_VarVPointer)DataStrHP);
    return SC_SUCC_RES;

ErrorOut:
    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);
    return SC_ERR_RES;
}


// SC_CGResolveVar will find the **EXISTING** named var by searching up
// the Lex chain.  Once found, the VarTP is linked to its matching Vex.
// (NOTE: The Var is being used here, as in "A = B + 3"!)
//
// There are a few alternatives:
//
// -- Var is local to this Lex (which may/may not be a Func).
// -- Var is local to a higher (container) Lex.
// -- Var is local to a higher FUNC, so must be *Closed* over!
// -- Var is local to TOP!
// -- Var is global.
// -- Var cannot be found/is inaccessible here.
//
// In nested BEGIN-END blocks, inner blocks can easily access vars declared in
// outer blocks.  BUT things change when there are nested FUNC declarations.
// The inner FUNC can only access vars in the outer FUNC (at runtime) *IF* the
// inner FUNC closes over them (to ensure their continued existance).  Worse if
// there are intermediary FUNC in between, they will all also have to (bridge)
// close over the vars to make them accessible to the inner most FUNC (even
// though the intermediary FUNCs themselves do not need or use them)!
//
// VarTP may also be a Def const!  If no matching Var is found and DoDef==1,
// then a search is launched for the Def of the same name.  In many cases, DoDef
// is 0, since the Left of an assignment MUST be a var, etc.

    SC_VexPointer	SC_CGCloseVar(SC_CGPointer CGP, SC_TokenPointer VarTP, SC_StrHPointer StrHP,
				      SC_LexPointer FuncLexP, SC_VexPointer SrcVexP)
    {
	SC_VMPointer	VMP = CGP->VMP;
	SC_VexPointer	VexP = SC_CGVexNew(VMP);

	VarTP->Data = (Int64)VexP;			// Stash in Token
	VexP->Class = SC_CVEX;
	VexP->NextP = FuncLexP->MapP->CVexP;
	FuncLexP->MapP->CVexP = VexP;
	FuncLexP->MapP->CCount += 1;
	VexP->Index = (VexP->NextP) ? VexP->NextP->Index + 1 : 0;
	VexP->SrcClass = SrcVexP->Class;
	VexP->SrcIndex = SrcVexP->Index;
	if (FuncLexP->MapP->VarCount++ > SC_CGMAXVARCOUNT) 
	    SC_CODEERROR(CGP, VarTP, SC_STR_ErrorLimits);

	// Must also register in Dict... makes Dict if lacking!
	SC_CGRegisterVar(VMP, FuncLexP, StrHP, VexP);
	return VexP;

    ErrorOut:
	return NULL;
    }


    // SC_CGBridgeCloseVar has to be recursive since each level has to use the
    // SrcVex from the higher level and cannot iterate DOWN the Lex chain--only
    // have SupP link.
    //
    // FuncLexP (the topmost) defines the initial SrcVex... obviously a Local.
    // Starting at the bottom, it recurses up to FuncLexP, then on the way down,
    // keeps making new CVex and using them as SrcVexP for the next lower level.
    //
    // NOTE: SC_BCVRecord is only used to consolidate params for recursion.
    //
    //	     There will be successive assignment of intermediate CVex to VarTP.
    //	     The lowest level will overwrite the previous ones.

    typedef struct _SC_BCVRecord {
	SC_CGPointer	CGP;
	SC_TokenPointer	VarTP;
	SC_StrHPointer	StrHP;
	SC_LexPointer	FuncLexP;
	SC_VexPointer	SrcVexP;	// Changed on the way down LEX
    } SC_BCVRecord, *SC_BCVPointer;

    Int16	SC_AuxCGBCV(SC_BCVPointer RP, SC_LexPointer LexP)
    {
	// Only care about FUNC Lex, so skip any intermediary blocks.
	while (! (LexP->Flags & SC_LEXFUNCFLAG)) LexP = LexP->SupP;
	if (LexP == RP->FuncLexP) return SC_SUCC_RES;

	// Recurse into higher and higher LexP (can only go Up, not down)!
	if (SC_ERR_RES == SC_AuxCGBCV(RP, LexP->SupP)) return SC_ERR_RES;
	// Returning back down, Close over the SrcVex and create a new one!
	if (! (RP->SrcVexP = SC_CGCloseVar(RP->CGP, RP->VarTP, RP->StrHP, LexP, RP->SrcVexP))) return SC_ERR_RES;

	return SC_SUCC_RES;
    }

    Int16	SC_CGBridgeCloseVar(SC_CGPointer CGP, SC_TokenPointer VarTP, SC_StrHPointer StrHP,
				    SC_LexPointer FuncLexP, SC_VexPointer SrcVexP)
    {
	SC_BCVRecord	R;

	R.CGP = CGP;
	R.VarTP = VarTP;
	R.StrHP = StrHP;
	R.FuncLexP = FuncLexP;
	R.SrcVexP = SrcVexP;

	return SC_AuxCGBCV(&R, CGP->LastLexP);
    }

Int16	SC_CGResolveVar(SC_CGPointer CGP, SC_TokenPointer VarTP, Int16 DoDef)
{
    SC_VMPointer		VMP = CGP->VMP;
    SC_StrHPointer		StrHP;
    SC_LexPointer		LexP;		// Goes up Lex chain
    SC_LexPointer		FuncLexP;	// Current FUNC Lex
    SC_VexPointer		VexP;
    SC_DictVPointer		DictP;
    SC_PairHPointer		PairP;
    Int16			FuncLevel, InBlock;

    // NOTE: LexP now have an FLevel.  But this function counts UP, so uses
    //	     FuncLevel in a different way!

    // Comes in with RefCount +1
    StrHP = SC_StrGetEntry(VMP, CGP->ScriptStartP + VarTP->ReadPos, VarTP->ReadLen);
    LexP = CGP->LastLexP;
    VexP = NULL;
    FuncLevel = InBlock = 0;
    while (LexP) {
	// Go up the Lex hierarchy (keeping track of FUNCs) while looking
	// for the Var in LexP->DictP!

	InBlock = 1;					// In NON-FUNC {} block
	if (LexP->Flags & SC_LEXFUNCFLAG) {
	    if (FuncLevel == 0) FuncLexP = LexP;	// Stash first FuncLexP
	    FuncLevel += 1; InBlock = 0;
	}

	if ((DictP = LexP->VDictP) &&
	    (PairP = SC_DictGetPair(DictP, (SC_VarVPointer)StrHP, SC_VAL_PTR))) {
	    // Found it!
	    VexP = (SC_VexPointer)PairP->ValueP;
	    break;
	}
	LexP = LexP->SupP;
    }

    // Did not find it... see if Def!
    if (VexP == NULL) {
	if (DoDef) {
	    if (SC_ERR_RES == SC_CGResolveDef(CGP, VarTP))
		SC_CODEERROR_POS(CGP, VarTP, SC_STR_ErrorBadIdent);
	    goto DoneDone;
	}
    
	SC_CODEERROR_POS(CGP, VarTP, SC_STR_ErrorBadVar);
    }

    // InBlock means var was found in an intervening non-Func {} Lex, so it must
    // come from the next higher FuncLevel, where it has storage!
    FuncLevel += InBlock;
    
    if (FuncLevel <= 1) {
	// Within THIS function already... Nothing to stash, just use it.
	VarTP->Data = (Int64)VexP;
	goto DoneDone;

    } else if (VexP->Class == SC_GVEX) {
	// Its a Global (FROM HIGHER LEVELS), so add to FuncLex
	if (SC_ERR_RES == SC_CGAddGlobal(CGP, VarTP, FuncLexP, VexP->Index))
	    goto ErrorOut;

    } else if (FuncLevel == 2) {
	// VexP can be CVex or LVex, from immediate superior Function... 
	if (! SC_CGCloseVar(CGP, VarTP, StrHP, FuncLexP, VexP))
	    goto ErrorOut;

    } else {
	// VexP is From much higher Func... must close intermediates too!
	// Original VexP can be CVex or LVex, intermediates will all be CVex!
	if (SC_ERR_RES == SC_CGBridgeCloseVar(CGP, VarTP, StrHP, LexP, VexP))
	    goto ErrorOut;
    }

DoneDone:
    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);	// Done with it -1
    return SC_SUCC_RES;

ErrorOut:
    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);	// Done with it -1
    return SC_ERR_RES;
}

// SC_CGResolveDef will find the **EXISTING** named Def by searching up
// the Lex chain.  Once found, the DefTP is replaced by its constant value.
// There are a few alternatives:
//
// Also called when declaring a Def that has other DEF on its right side.
// E.g.  "Def #MyConst = #TRUE;"
//
// -- Def is local to this Lex (which may/may not be a Func).
// -- Def is local to a higher (accessible) Lex.
// -- Def is local to a higher FUNC that contains this one.
// -- Def is local to TOP!
// -- Def is global.
//    -- Declared at top level... find in Lex DDictP
//    -- Pre-defined in main Pkg (VMP->PkgADP->Data[0])
//	 -- Used directly: "#SOME_CONST" or
//       -- Used with main Pkg name: "Main.#SOME_CONST"! <-- Expanded when DOT is processed!
// -- Def cannot be found/is inaccessible here.
//
// In nested BEGIN-END blocks, inner blocks can easily access Defs declared in
// outer blocks.  Unlike Vars, Defs only exist at compile time and are not even
// seen at runtime.  Therefore, there is no "closing over" Defs!
//
// INTERACT MODE: Def are inherited (if not locally overwritten) from VMP->InDDictP!

    Int16 SC_CGSubDefToken(SC_PairHPointer PairP, SC_TokenPointer DefTP)
    {
	SC_StrHPointer		StrHP;

	if (PairP->ValType == SC_VAL_PTR) {
	    StrHP = (SC_StrHPointer)PairP->ValueP;
	    if (StrHP->Type == SC_ENTRY_STR) {
		DefTP->Type = SC_STRING_TOKEN;
		DefTP->Data = (Int64)StrHP;
		DefTP->DownP = NULL;		// Caller must zap!!
		SC_VARREF(StrHP);		// Assumed NOT inc at lookup

		// Data field of SC_STRING_TOKEN being set means:
		//   DEF is resolved *OR*
		//   ShortString is processed, and a Malloc is created!
		//   (Will have SC_TOKEN_PROCSTR_FLAG!)
		return 1;	// Success
	    }

	} else if (PairP->ValType == SC_VAL_INT) {
	    DefTP->Type = SC_NUMBER_TOKEN;
	    DefTP->Id = SC_INTNUMB_ID;
	    DefTP->Data = (Int64)PairP->ValueP;
	    DefTP->DownP = NULL;
	    return 1;	// Success

	} else if (PairP->ValType == SC_VAL_FLT) {
	    DefTP->Type = SC_NUMBER_TOKEN;
	    DefTP->Id = SC_FLOATNUMB_ID;
	    DefTP->Data = (Int64)PairP->ValueP;
	    DefTP->DownP = NULL;
	    return 1;	// Success

	}

	return 0;	// Error
    }

Int16	SC_CGResolveDef(SC_CGPointer CGP, SC_TokenPointer DefTP)
{
    SC_VMPointer		VMP = CGP->VMP;
    SC_LexPointer		LexP;		// Goes up Lex chain
    SC_StrHPointer		StrHP;
    SC_PairHPointer		PairP;

    // NOTE: LexP now have an FLevel.  But this function counts UP, so uses
    //	     FuncLevel in a different way!

    // Comes in with RefCount +1
    StrHP = SC_StrGetEntry(VMP, CGP->ScriptStartP + DefTP->ReadPos, DefTP->ReadLen);
    LexP = CGP->LastLexP;
    while (LexP) {
	// Go up the Lex hierarchy (keeping track of FUNCs) while looking
	// for the Var in LexP->DDictP!

	if (LexP->DDictP && ((PairP = SC_DictGetPair(LexP->DDictP, (SC_VarVPointer)StrHP, SC_VAL_PTR)))) {	// Assign
	    if (SC_CGSubDefToken(PairP, DefTP))
		goto SUCCESS;
	    SC_CODEERROR(CGP, DefTP, SC_STR_ErrorBadIdent);
	}

	LexP = LexP->SupP;
    }

    // May have inherited in INTERACT mode!  (No InDDictP UNLESS Interact!)
    if (VMP->OldDefDictP && ((PairP = SC_DictGetPair(VMP->OldDefDictP, (SC_VarVPointer)StrHP, SC_VAL_PTR)))) {	// Assign
	if (SC_CGSubDefToken(PairP, DefTP))
	    goto SUCCESS;
	SC_CODEERROR(CGP, DefTP, SC_STR_ErrorBadIdent);
    }

    // Did not find in Lex hierarchy... so search VMP->PkgADP->Data[0] if
    // pre-defined in MAIN pkg!
    //
    // NOTE:  Pre-defined DEF are case INSENSITIVE...SC_CGGetPkgField searches Pkg-NDictP properly!
    //	      (if using DOT format, DEF will be resolved in CheckIndex, NOT here.)

    if ((PairP = SC_CGGetPkgField(CGP, (SC_PkgPointer)CGP->VMP->PkgADP->Data[0], DefTP))) {
	if (SC_CGSubDefToken(PairP, DefTP))
	    goto SUCCESS;
	SC_CODEERROR(CGP, DefTP, SC_STR_ErrorBadIdent);
    }

    // Fell through... could not find Def!
    SC_CODEERROR(CGP, DefTP, SC_STR_ErrorBadIdent);

ErrorOut:
    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);
    //SC_DictPrint(CGP->VMP, (*(SC_PkgPointer)CGP->VMP->PkgADP->Data[0]).NDictP, 0, 0);
    //SC_DictPairPrint(CGP->VMP, (*(SC_PkgPointer)CGP->VMP->PkgADP->Data[0]).NDictP, 0);
    return SC_ERR_RES;

SUCCESS:
    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);		// Done, -1 now
    return SC_SUCC_RES;
}

// SC_CGRefLabel is used to allocate and define EXPLICIT (target of
// GOTO, seen by script writer) labels.
//
// Each such label is first allocated in MapP->LabelADP->Data (for the
// FUNC or TOP).  The Index for this label is stashed (along with its
// given name) into a LabelDict.  The Indexed label is initially 0
// (after allocation) but is given an offset value when DEFINED.
//
// NOTE: LabelDict resides ONLY in FUNC Maps... only FUNC (including
//	 TOP global) have labels.  But any LEX can define variables,
//	 so VarDict resides in the LEX, not the FUNC Map.
//
// PROBLEM: Jumping (GOTO) into a MAPI/K will eventually result in
//	    a crash as the entries required for the MAPI/K will not
//	    be on the stack.  Similarly, jumping out of a MAPI/K
//	    will cause problems if the MAPI/K entries are NOT popped
//	    off the stack.  (Jumping into a FOR loop is also problematic
//	    as it foregoes initialization of loop vars.)

Int16	SC_CGRefLabel(SC_CGPointer CGP, SC_TokenPointer LabelTP, Int16 DoDefine)
{
    // This label is being referenced... must exist in current FUNC Lex!
    // -- See if already defined.
    // -- If not, note its existence for a post-check.

    SC_VMPointer	VMP = CGP->VMP;
    SC_LexPointer	FuncLexP = CGP->FuncLexP;
    SC_MapPointer	MapP = FuncLexP->MapP;
    SC_DictVPointer	DictP = MapP->LabelDictP;
    SC_StrHPointer	StrHP;
    SC_PairHPointer	PairP;
    Uns32		Index;

    // Comes in with RefCount +1
    StrHP = SC_StrGetEntry(VMP, CGP->ScriptStartP + LabelTP->ReadPos, LabelTP->ReadLen);
    PairP = NULL;
    if (DictP) {
	PairP = SC_DictGetPair(DictP, (SC_VarVPointer)StrHP, SC_VAL_PTR);
    } else
	MapP->LabelDictP = DictP = SC_DictCreateNewDict(VMP, 0);

    if (PairP) {
	// Found it.
	Index = (Uns32)(Uns64)(PairP->ValueP);
	if (DoDefine) {
	    // Label is set to (-1 - ReadPos) if USED before DEFINED!
	    if (SC_CGLabelGet(MapP, Index) > 0)
		SC_CODEERROR(CGP, LabelTP, SC_STR_ErrorDupeLabel);
	    SC_CGLabelDefine(MapP, Index, MapP->CodeCount);
	}

    } else {
	// New LabelDictP or could not find PairP.  Register it now!
	// Label is initially undefined, so 0.
	PairP = SC_BigEntryNew(VMP);
	PairP->KeyType = SC_VAL_PTR;
	PairP->ValType = SC_VAL_INT;
	PairP->KeyP = (SC_VarVPointer)StrHP;
	Index = SC_CGLabelAlloc(CGP, MapP);
	PairP->ValueP = (void *)(Uns64)Index;
	if (DoDefine)
	    SC_CGLabelDefine(MapP, Index, MapP->CodeCount);
	else {
	    // Set to a negative number to mark "missing", but
	    // can harvest ReadPos for error display!
	    SC_CGLabelDefine(MapP, Index, -1 - LabelTP->ReadPos);
	}
	SC_DictStashNewPair(VMP, DictP, PairP);
    }
    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);    
    return SC_SUCC_RES;

ErrorOut:
    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);
    return SC_ERR_RES;
}

Int32	SC_CGGetLabel(SC_CGPointer CGP, SC_TokenPointer LabelTP, Uns32 * IndexP)
{
    SC_VMPointer	VMP = CGP->VMP;
    SC_LexPointer	FuncLexP = CGP->FuncLexP;
    SC_MapPointer	MapP = FuncLexP->MapP;
    SC_StrHPointer	StrHP;
    SC_PairHPointer	PairP;
    Int32		Res = 0;

    *IndexP = 0L;
    // Comes in with RefCount +1
    StrHP = SC_StrGetEntry(VMP, CGP->ScriptStartP + LabelTP->ReadPos, LabelTP->ReadLen);
    if ((PairP = SC_DictGetPair(MapP->LabelDictP, (SC_VarVPointer)StrHP, SC_VAL_PTR)))
	if ((*IndexP = (Uns32)(Uns64)(PairP->ValueP)))
	    Res = SC_CGLabelGet(MapP, *IndexP);

    SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);
    return Res;
}
     
// Code constants are stashed in a top-level Dict to avoid duplication.
// Pair [Konst-Index] is stored, so Index can be retrieved for a given
// Konst value...

Uns32	SC_CGGetKonstIndex(SC_CGPointer CGP, void * KValue)
{
    SC_VMPointer		VMP = CGP->VMP;
    SC_MapPointer		MapP = CGP->TopLexP->MapP;
    SC_DictVPointer		DictP = MapP->KonstDictP;
    SC_PairHPointer		PairP;
    Uns32			Index;

    // NOTE:  Stashed Konst does NOT know whether it is INT or FLT, so is
    //	      just treated as an INT.  The ByteCode using it will know!

    if (! DictP)
	DictP = MapP->KonstDictP = SC_DictCreateNewDict(VMP, 0);

    PairP = SC_DictGetPair(DictP, KValue, SC_VAL_INT);	// Always treat as INT
    if (PairP) return *(Uns32 *)&PairP->ValueP;		// Stores an Index

    // Not seen before, enter it here!
    Index = SC_CGKonstAdd(CGP, MapP, (Int64)KValue);

    PairP = SC_BigEntryNew(VMP);
    PairP->KeyType = SC_VAL_INT;
    PairP->ValType = SC_VAL_INT;
    PairP->KeyP = KValue;
    PairP->ValueP = (SC_VarVPointer)(Int64)Index;
    SC_DictStashNewPair(VMP, DictP, PairP);

    return Index;
}

// Checks that all named (explicit) labels have been assigned.
// But NOT that they are actually used!

Int16	SC_CGCheckLabelDict(SC_CGPointer CGP, SC_LexPointer FuncLexP)
{
    SC_MapPointer		MapP = FuncLexP->MapP;
    SC_DictVPointer		DictP;
    SC_PairHPointer		PairP;
    Int32			Offset;

    // Check that *ALL* Labels have been defined.  PairP->ValueP is the Index.
    // Check ( MapP->labelADP->Data + Index) to get offset.  If Offset is
    // negative, then label is used, not defined.  The offset will be (-1 - ReadPos)
    // for error reporting!

    // NOTE:  Would be easier to just index down LabelADP->Data... but that
    //	      includes implicit labels, not just explicit (named) GOTO labels!
    
    if ((DictP = FuncLexP->MapP->LabelDictP)) {
	PairP = DictP->FirstPairP;
	while (PairP) {
	    Offset = SC_CGLabelGet(MapP, (Uns32)(Int64)PairP->ValueP);
	    if (Offset < 0)
		SC_CODEERRORPOS(CGP, -1L - Offset, SC_STR_ErrorMissingLabel);
	    PairP = PairP->NextPairP;
	}
    }
    return SC_SUCC_RES;

ErrorOut:
    return SC_ERR_RES;
}

// FUNCTION definition/declarations will be hoisted to the top FuncLexP level.
// Make sure there are no left-over declations with no definition!

Int16	SC_CGCheckVarDict(SC_CGPointer CGP, SC_LexPointer FuncLexP)
{
    SC_DictVPointer	DictP;
    SC_PairHPointer	PairP;
    SC_VexPointer	VexP;

    if ((DictP = FuncLexP->VDictP)) {	// Assign
	PairP = DictP->FirstPairP;
	while (PairP) {
	    VexP = (SC_VexPointer)PairP->ValueP;
	    if (VexP->Flags & SC_VEXFDECFLAG)
		SC_CODESTRERROR(CGP, SC_STR_ErrorMissingFunc, (*(SC_StrHPointer)PairP->KeyP).StrDataP->Data);

	    PairP = PairP->NextPairP;
	}
    }
    return SC_SUCC_RES;

ErrorOut:
    return SC_ERR_RES;
}

// SC_CGGetStringIndex will return an Uns32 Index for the Token string.
//
// The script will include/refer to string constants (in tokens), as keys for
// dicts, names, fields, and various other parameters.  These will eventually
// have to be included in the Object code, and used at runtime.  So every effort
// is made to avoid duplication, as such strings OFTEN appear multiple times.
//
// NOTE: SHORTSTRINGS may require processing if they have escape sequences.  If
//	 so, processing may fail (syntax errors).  So must always check
//	 CGP->ErrorStrP after SC_CGGetStringIndex.
// 
// Each time a string literal is encountered, a StrHPointr for it is obtained.
// This is then used as a Key for the StringDictP at the TOP level.  If a hit,
// then the PAIR value will be the Index in StringADP for that StrHPointer.
// Otherwise, a new entry is made in the StringADP and the Index is recorded.

    Uns32	SC_AuxCGGSIAdd(SC_CGPointer CGP, SC_MapPointer MapP, SC_StrHPointer StrHP)
    {
	Uns32	Slots;

	SC_VARREF(StrHP);
	if (! MapP->StringADP) {
	    // First entry... allocate array.
	    SC_ArrAllocNewData(CGP->VMP, &MapP->StringADP, SC_CGINITSTRINGSLOTS, 1);
	    
	} else {
	    Slots = MapP->StringADP->SlotCount;
	    if (MapP->StringCount == Slots) {
		// Not enough room... grow it.
		if (Slots < SC_CGMAXDOUBLESIZE) Slots <<= 1;	// 2x
		else Slots += (Slots >> 1);			// 1.5x
		
		MapP->StringADP = SC_ArrResizeData(CGP->VMP, MapP->StringADP, Slots, 0);
	    }
	}

	Slots = MapP->StringCount++;		// Use as Index here
	MapP->StringADP->Data[Slots] = StrHP;
	return Slots;
    }


Uns32	SC_CGGetStringIndex(SC_CGPointer CGP, SC_TokenPointer TP)
{
    Uns32		StrLen;
    SC_VMPointer	VMP = CGP->VMP;
    char *		SP;
    SC_MapPointer	MapP = CGP->TopLexP->MapP;
    SC_DictVPointer	DictP = MapP->StringDictP;
    SC_StrHPointer	StrHP;
    SC_PairHPointer	PairP;
    Uns64		Index = 0L;
    Int16		DidProcSS = 0;
    Uns8		DoDeRef = 0;

    // If string needs processing and it fails, Index will be 0, which
    // is a fine value... but will set CGP->ErrorStrP!
    //
    // NOTE:  If a DEF was resolved to get this String... then StrHP will
    //	      already be stashed in the TP->Data!  Otherwise, TP->Data can be
    //	      used to stash malloc ptr used to process ShortString, 2 uses!!

    if (!(StrHP = (SC_StrHPointer)TP->Data)) {	// Assign

	if (TP->Flags & SC_TOKEN_PROCSTR_FLAG) {
	    // String should be processed: it has escape sequences!
	    DidProcSS = 1;
	    if (! SC_ReadProcessShortString(CGP, TP))
		goto DoneDone;

	    SP = (char *)TP->Data;
	} else
	    SP = CGP->ScriptStartP + TP->ReadPos;

	// Get StrLen AFTER any processing... can make it shorter, not longer!
	StrLen = TP->ReadLen;
	// printf("Get String Index: %.*s\n", StrLen, SP);
	
	// Comes in with RefCount +1
	StrHP = SC_StrGetEntry(VMP, SP, StrLen);
	DoDeRef = 1;
    }

    // Have StrHP by now... Check MapP->StringDictP and get Index!
    if (! DictP) DictP = MapP->StringDictP = SC_DictCreateNewDict(VMP, 0);
    if ((PairP = SC_DictGetPair(DictP, (void *)StrHP, SC_VAL_PTR))) {
	Index = (Uns32)(Uns64)PairP->ValueP;
	goto DoneDone;
    }

    // Not seen it before... enter it and record the StrHP!
    Index = SC_AuxCGGSIAdd(CGP, MapP, StrHP);

    PairP = SC_BigEntryNew(VMP);
    PairP->KeyType = SC_VAL_PTR;
    PairP->ValType = SC_VAL_INT;
    PairP->KeyP = (void *)StrHP;
    PairP->ValueP = (void *)Index;
    SC_DictStashNewPair(VMP, DictP, PairP);

DoneDone:
    // FREE TP->Data ONLY if processed short string... but leave StrHP be!
    // DidProcSS may be 1, but TP->Data NULL!
    if (DidProcSS && TP->Data) {
	// Get rid of processed String.
	free((void *)TP->Data);
	TP->Data = 0LL;
    }

    if (DoDeRef)
	SC_VARDEREF(VMP, (SC_VarVPointer)StrHP);    

    return (Uns32)Index;
}

// Returns Bit (Not 0) for Ops that DO NOT have a side effect!  They will be
// flattened if in the non-final element of a COMMA list.
//
// NOTE: Checks only 1 level at a time.  So gets called again to
//	 check the next level, on and on if necessary.
//

static inline Uns8	SC_CGOpNoSideEffect(Uns8 Op)
{
    return SC_CGOpEffectArr[Op>>3] & (0x01 << (Op & 0x7));
}

#if 0

    // Init bit array to all 1... since most DO NOT have a side-effect.
    // (Value is irrelevent for ops like CONT, FUNC, etc...)
    // Then 0 value for the few args that really do have side-effects.
    // These include all manner of assignment, ++/--, and CALL!!!
    void	SC_SetCGOpEffectArr()
    {
	Uns8	Op;

	for (Op = 0; Op < 32; ++Op) SC_CGOpEffectArr[Op] = 0xFF;
	
	#define SC_SETOP(_O)	SC_CGOpEffectArr[_O >> 3] &= ~(0x01 << (_O & 0x7))

	    // Only these DO HAVE side effects, so 0 them out!
	    SC_SETOP(SC_EQ_ID);
	    SC_SETOP(SC_CALL_ID);
	    SC_SETOP(SC_ALIAS_ID);
    	    for (Op = SC_PLUSPLUS_ID; Op <= SC_PREMINMIN_ID; ++Op) SC_SETOP(Op);
	    SC_SETOP(SC_SHLEQ_ID);
	    SC_SETOP(SC_SHREQ_ID);

	#undef SC_SETOP

	printf("\n");
	for (Op = 0; Op < 32; ++Op) {
	    if (Op) {
		if (!(Op % 4))
		    printf(",\n");
		else
		    printf(", ");
	    }
	    printf("0x%02x", SC_CGOpEffectArr[Op]);
	}
	printf("\n\n");

    }
#endif

// SC_CGASTHasSideEffect will recursively traverse the AST and
// return 1 if it has ANY OPs that cause a side effect.

Int16	SC_CGASTHasSideEffect(SC_TokenPointer TP)
{
    if (TP && (TP->Type == SC_OPERATOR_TOKEN)) {
	if (! SC_CGOpNoSideEffect(TP->Id))		// Bingo, has SideEffect
	    return 1;
	else {
	    TP = TP->DownP;
	    while (TP) {
		if (SC_CGASTHasSideEffect(TP))		// One of the args has SideEffect
		    return 1;
		TP = TP->NextP;
	    }
	    return 0;					// No SideEffect
	}

    } else
	return 0;
}

// SC_CGSetTPInt/Float will replace the top TP with a number.
//
// It will *NOT* change TP's ReadPos/ReadLen... so it indicates the buffer
// position of the original operator that used to be in TP.  Otherwise, errors
// on TP will point to the sky!!

void	SC_CGSetTPInt(SC_TokenPointer TP, Int64 Numb)
{
    TP->Type = SC_NUMBER_TOKEN;
    TP->Id = SC_INTNUMB_ID;
    TP->Data = Numb;
    // Caller sets TP->DownP = NULL after zapping args!
}

void	SC_CGSetTPFloat(SC_TokenPointer TP, double Numb)
{
    TP->Type = SC_NUMBER_TOKEN;
    TP->Id = SC_FLOATNUMB_ID;
    TP->Data = *(Int64 *)&Numb;
    // Caller sets TP->DownP = NULL after zapping args!
}

// Return 1 if TP is an Integer == to Val.
Int16	SC_CGIsIntTP(SC_TokenPointer TP, Int64 Val)
{
    return ((TP->Type == SC_NUMBER_TOKEN) &&
	    (TP->Id == SC_INTNUMB_ID) &&
	    (TP->Data == Val));
}

// Return 1 if TP is an Operator with Id == Op.
Int16	SC_CGIsOpTP(SC_TokenPointer TP, Uns8 Op)
{
    return ((TP->Type == SC_OPERATOR_TOKEN) && (TP->Id == Op));
}    

// Negate the value of SC_NUMBER_TOKEN... Int or Double)
// TP *MUST* be SC_NUMBER_TOKEN.
void	SC_CGNegateNumbTP(SC_TokenPointer TP)
{
    if (TP->Id == SC_INTNUMB_ID)
	TP->Data = -TP->Data;
    else
	*(double *)&TP->Data = - *(double *)&TP->Data;
}

// Swap args to negate the SUB AST.
// TP *MUST* be SC_OPERATOR_TOKEN *and* TP->Id must be SC_SUB_ID;
void	SC_CGNegateSubOpTP(SC_TokenPointer TP)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;

    TP->DownP = TP2;
    TP2->NextP = TP1;
    TP1->NextP = NULL;
}

// Will go through all Args of TP and give them an intermediary Op.  Makes
// (TPOp A B C) into (TPOp (Op A) (Op B) (Op C)).
//
// Works even if A, B, C args are themselves Ops and have a DownP chain.

void	SC_CGDistributeOp(SC_VMPointer VMP, SC_TokenPointer TP, Uns8 Op)
{
    SC_TokenPointer		ATP;
    SC_TokenPointer		NewTP;

    ATP = TP->DownP;
    while (ATP) {
	// Create a new token and copy arg into it.
	// Then transform Arg into Op... gets the NextP chain for free.
	NewTP = SC_TokenNew(VMP);
	*NewTP = *ATP;
	NewTP->NextP = NULL;		// New Arg loses NextP
	ATP->Type = SC_OPERATOR_TOKEN;	// Former Arg becomes new Op
	ATP->Id = Op;
	ATP->DownP = NewTP;			// Already has NextP

	ATP = ATP->NextP;
    }
}

SC_TokenPointer SC_CGGetLastArg(SC_TokenPointer TP)
{
    SC_TokenPointer		ArgTP;

    if ((ArgTP = TP->DownP))
	while (ArgTP->NextP) ArgTP = ArgTP->NextP;

    return ArgTP;
}

// For binary math ops:
//	case 0:		V1 is Int, V2 is Int -- special INT case!!
//	case 1:		V1 is Int, V2 is Float
//	case 2:		V1 is Float, V2 is Int
//	case 3:		V1 is Float, V2 is Float

void	SC_CGCheckLess(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    Int64		Val;

    switch (ArgFlag) {
	case 0:		Val = TP1->Data < TP2->Data; break;
	case 1:		Val = *(double *)&TP2->Data > TP1->Data; break;
	case 2:		Val = *(double *)&TP1->Data < TP2->Data; break;
	case 3:		Val = *(double *)&TP1->Data < *(double *)&TP2->Data; break;	
    }
    SC_CGSetTPInt(TP, Val);
}

void	SC_CGCheckGreat(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    Int64		Val;

    switch (ArgFlag) {
	case 0:		Val = TP1->Data > TP2->Data; break;
	case 1:		Val = *(double *)&TP2->Data < TP1->Data; break;
	case 2:		Val = *(double *)&TP1->Data > TP2->Data; break;
	case 3:		Val = *(double *)&TP1->Data > *(double *)&TP2->Data; break;	
    }
    SC_CGSetTPInt(TP, Val);
}

void	SC_CGCheckEQEQ(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    Int64		Val;

    switch (ArgFlag) {
	case 0:		Val = TP1->Data == TP2->Data; break;
	case 1:		Val = *(double *)&TP2->Data == TP1->Data; break;
	case 2:		Val = *(double *)&TP1->Data == TP2->Data; break;
	case 3:		Val = *(double *)&TP1->Data == *(double *)&TP2->Data; break;	
    }
    SC_CGSetTPInt(TP, Val);
}

void	SC_CGCheckNEQ(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    Int64		Val;

    switch (ArgFlag) {
	case 0:		Val = TP1->Data != TP2->Data; break;
	case 1:		Val = *(double *)&TP2->Data != TP1->Data; break;
	case 2:		Val = *(double *)&TP1->Data != TP2->Data; break;
	case 3:		Val = *(double *)&TP1->Data != *(double *)&TP2->Data; break;	
    }
    SC_CGSetTPInt(TP, Val);
}

void	SC_CGCheckLEQ(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    Int64		Val;

    switch (ArgFlag) {
	case 0:		Val = TP1->Data <= TP2->Data; break;
	case 1:		Val = *(double *)&TP2->Data >= TP1->Data; break;
	case 2:		Val = *(double *)&TP1->Data <= TP2->Data; break;
	case 3:		Val = *(double *)&TP1->Data <= *(double *)&TP2->Data; break;	
    }
    SC_CGSetTPInt(TP, Val);
}

void	SC_CGCheckGEQ(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    Int64		Val;

    switch (ArgFlag) {
	case 0:		Val = TP1->Data >= TP2->Data; break;
	case 1:		Val = *(double *)&TP2->Data <= TP1->Data; break;
	case 2:		Val = *(double *)&TP1->Data >= TP2->Data; break;
	case 3:		Val = *(double *)&TP1->Data >= *(double *)&TP2->Data; break;	
    }
    SC_CGSetTPInt(TP, Val);
}

void	SC_CGCheckID(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    Int64		Val;

    // IDENT means same FORM and same VALUE!
    switch (ArgFlag) {
	case 0:		Val = TP1->Data == TP2->Data; break;
	case 1:
	case 2:		Val = 0; break;
	case 3:		Val = *(double *)&TP1->Data == *(double *)&TP2->Data; break;	
    }
    SC_CGSetTPInt(TP, Val);
}

void	SC_CGCheckNID(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    Int64		Val;

    // NOT-IDENT means different FORM or different VALUE.
    switch (ArgFlag) {
	case 0:		Val = TP1->Data != TP2->Data; break;
	case 1:		
	case 2:		Val = 1; break;
	case 3:		Val = *(double *)&TP1->Data != *(double *)&TP2->Data; break;	
    }
    SC_CGSetTPInt(TP, Val);
}

void	SC_CGCheckAdd(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    double		FVal;

    switch (ArgFlag) {
	case 0:		SC_CGSetTPInt(TP, TP1->Data + TP2->Data);
			return;

	case 1:		FVal = *(double *)&TP2->Data + TP1->Data; break;
	case 2:		FVal = *(double *)&TP1->Data + TP2->Data; break;
	case 3:		FVal = *(double *)&TP1->Data + *(double *)&TP2->Data; break;	
    }
    SC_CGSetTPFloat(TP, FVal);
}

void	SC_CGCheckSub(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    double		FVal;

    switch (ArgFlag) {
	case 0:		SC_CGSetTPInt(TP, TP1->Data - TP2->Data);
			return;

	case 1:		FVal = - *(double *)&TP2->Data + TP1->Data; break;
	case 2:		FVal = *(double *)&TP1->Data - TP2->Data; break;
	case 3:		FVal = *(double *)&TP1->Data - *(double *)&TP2->Data; break;	
    }
    SC_CGSetTPFloat(TP, FVal);
}

void	SC_CGCheckDiv(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    double		V1, V2, FVal;

    switch (ArgFlag) {
	case 0:		SC_CGSetTPInt(TP, TP1->Data / TP2->Data);
			return;

	case 1:		V1 = (double)TP1->Data;		V2 = *(double *)&TP2->Data;	break;
	case 2:		V1 = *(double *)&TP1->Data;	V2 = (double)TP2->Data;		break;
	case 3:		V1 = *(double *)&TP1->Data;	V2 = *(double *)&TP2->Data;	break;
    }
    FVal = V1 / V2;
    SC_CGSetTPFloat(TP, FVal);
}

void	SC_CGCheckMod(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    double		V1, V2, FVal;

    // '%' in C is really a Remainder, not Modulo... difference shows up in Neg numbers.
    switch (ArgFlag) {
	case 0:		SC_CGSetTPInt(TP, TP1->Data % TP2->Data);
			return;

	case 1:		V1 = (double)TP1->Data;		V2 = *(double *)&TP2->Data;	break;
	case 2:		V1 = *(double *)&TP1->Data;	V2 = (double)TP2->Data;		break;
	case 3:		V1 = *(double *)&TP1->Data;	V2 = *(double *)&TP2->Data;	break;
    }
    FVal = fmod(V1, V2);
    SC_CGSetTPFloat(TP, FVal);
}

void	SC_CGCheckMult(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    double		FVal;

    switch (ArgFlag) {
	case 0:		SC_CGSetTPInt(TP, TP1->Data * TP2->Data);
			return;

	case 1:		FVal = *(double *)&TP2->Data * TP1->Data; break;
	case 2:		FVal = *(double *)&TP1->Data * TP2->Data; break;
	case 3:		FVal = *(double *)&TP1->Data * *(double *)&TP2->Data; break;	
    }
    SC_CGSetTPFloat(TP, FVal);
}

void	SC_CGCheckSHL(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    double		V1, V2, FVal;
    Int64		Int2;

    switch (ArgFlag) {
	case 0:		Int2 = TP2->Data;
			// C Programming: shift does NOT like negative Arg2!
			if (Int2 < 0)
			    SC_CGSetTPInt(TP, TP1->Data >> -Int2);
			else
			    SC_CGSetTPInt(TP, TP1->Data << Int2);
			return;

	case 1:		V1 = (double)TP1->Data;		V2 = *(double *)&TP2->Data;	break;
	case 2:		V1 = *(double *)&TP1->Data;	V2 = (double)TP2->Data;		break;
	case 3:		V1 = *(double *)&TP1->Data;	V2 = *(double *)&TP2->Data;	break;
    }
    FVal = V1 * pow(2.0, V2);
    SC_CGSetTPFloat(TP, FVal);   
}

void	SC_CGCheckSHR(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    double		V1, V2, FVal;
    Int64		Int2;

    switch (ArgFlag) {
	case 0:		Int2 = TP2->Data;
			// C Programming: shift does NOT like negative Arg2!
			if (Int2 < 0)
			    SC_CGSetTPInt(TP, TP1->Data << -Int2);
			else
			    SC_CGSetTPInt(TP, TP1->Data >> TP2->Data);
			return;

	case 1:		V1 = (double)TP1->Data;		V2 = *(double *)&TP2->Data;	break;
	case 2:		V1 = *(double *)&TP1->Data;	V2 = (double)TP2->Data;		break;
	case 3:		V1 = *(double *)&TP1->Data;	V2 = *(double *)&TP2->Data;	break;
    }
    FVal = V1 / pow(2.0, V2);
    SC_CGSetTPFloat(TP, FVal);   
}

void	SC_CGCheckPlus(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	ArgTP = TP->DownP;

    if (ArgFlag)
	SC_CGSetTPFloat(TP, *(double *)&ArgTP->Data);    
    else
	SC_CGSetTPInt(TP, ArgTP->Data);
}

void	SC_CGCheckMinus(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	ArgTP = TP->DownP;

    if (ArgFlag) {
	SC_CGSetTPFloat(TP, - *(double *)&ArgTP->Data);
    } else
	SC_CGSetTPInt(TP, - ArgTP->Data);
}


void	SC_CGCheckLNEG(SC_TokenPointer TP, Uns16 ArgFlag)
{
    // All FLOATs are considered TRUE, even 0.0!!
    // TP->DownP->Data has numeric arg, so NOT 0 even if Int
    if (ArgFlag || TP->DownP->Data)
	SC_CGSetTPInt(TP, 0);
    else
	SC_CGSetTPInt(TP, 1);	
}

void	SC_CGCheckLPOS(SC_TokenPointer TP, Uns16 ArgFlag)
{
    // All FLOATs are considered TRUE, even 0.0!!
    // TP->DownP->Data has numeric arg, so NOT 0 even if Int
    if (ArgFlag || TP->DownP->Data)
	SC_CGSetTPInt(TP, 1);
    else
	SC_CGSetTPInt(TP, 0);
}

void	SC_CGCheckLAND(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    Int64		Val;

    // All FLOATs are considered TRUE, even 0.0!!
    switch (ArgFlag) {
	case 0:		Val = TP1->Data && TP2->Data; break;
	case 1:		Val = TP1->Data && 1; break;
	case 2:		Val = TP2->Data && 1; break;
	case 3:		Val = 1; break;	
    }
    SC_CGSetTPInt(TP, Val);
}

void	SC_CGCheckLOR(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;
    // All FLOATs are considered TRUE, even 0.0!!
    SC_CGSetTPInt(TP, ArgFlag || TP1->Data || TP2->Data);
}

void	SC_CGCheckBNEG(SC_TokenPointer TP, Uns16 ArgFlag)
{
    // Treat FLOAT as just bits... always return FIXED.
    SC_CGSetTPInt(TP, ~TP->DownP->Data);
}

void	SC_CGCheckBAND(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;

    // Treat FLOAT as just bits... always return FIXED.
    SC_CGSetTPInt(TP, TP1->Data & TP2->Data);   
}

void	SC_CGCheckBOR(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;

    // Treat FLOAT as just bits... always return FIXED.
    SC_CGSetTPInt(TP, TP1->Data | TP2->Data);   
}

void	SC_CGCheckBXOR(SC_TokenPointer TP, Uns16 ArgFlag)
{
    SC_TokenPointer	TP1 = TP->DownP;
    SC_TokenPointer	TP2 = TP1->NextP;

    // Treat FLOAT as just bits... always return FIXED.
    SC_CGSetTPInt(TP, TP1->Data ^ TP2->Data);   
}


// ********************************************************************************
// ********************************************************************************
// ********************************************************************************

// SC_CGCodePassCleanup is called from CHECK PASS phase to define labels and do
// general code cleanup (write a JMP) *AFTER* a Lex goes FIN (and is about to be
// zapped).
//
//   StopIf:  A new ELSE is expected... so stop on the first IF!
//   InElse:  Checking the ELSE!!  Just look at its Prev IF!!
//
// This function will write the JMP back to LoopStart for WHILE and FOR.  The
// latter may also have an UPDATE AST (token tree) that should be processed
// before the loop LEX is zapped.

Int16	SC_CGCodePassCleanup(SC_CGPointer CGP, Int16 StopIf, Int16 InElse)
{
    SC_LexPointer	LexP = CGP->LastLexP;
    SC_LexPointer	SupP;
    SC_MapPointer	MapP = CGP->FuncLexP->MapP;

    if (InElse) {
	// CHECK PASS is starting an ELSE, LexP is its IF block, soon to be
	// zapped.  CodeElse will start each ELSE with a JMP past its end (so
	// The IF (TRUE) body bypasses the ELSE).  Therefore, add 1 to CodeCount
	// to set the FALSE ESCAPE for this IF past that.
	if (LexP->FalseL)
	    SC_CGLabelDefine(MapP, LexP->FalseL, MapP->CodeCount + 1);

	return SC_SUCC_RES;
    }

    // Do this ONCE, outside the loop... CodePassCleanup will be called again.
    if ((LexP->Flags & SC_LEXFUNCFLAG) && (LexP->Flags & SC_LEXFINFLAG))
	SC_CGWriteNoArg(CGP, MapP, SC_FEND_CODE);

    while (LexP->Flags & SC_LEXFINFLAG) {
	if (! (LexP->Flags & SC_LEXFUNCFLAG)) {
	    // Redundant check, as FUNC do not set any MASK bits.
	    switch (LexP->Flags & SC_LEXTYPEMASK) {
		case SC_LEXIFFLAG:
		    if (StopIf) goto DoneDone;			// ELSE will handle it!
		    if (LexP->FalseL) SC_CGLabelDefine(MapP, LexP->FalseL, MapP->CodeCount);
		    break;

		case SC_LEXELSEFLAG:
		    if (LexP->FalseL) SC_CGLabelDefine(MapP, LexP->FalseL, MapP->CodeCount);
		    break;

		case SC_LEXLOOPFLAG:
		    // Nothing to do for DO loops... otherwise FOR or WHILE!
		    if (! (LexP->Flags & SC_LEXDOFLAG)) {
			// FOR loop may have UPDATE AST stashed away!  Write it.
			if (LexP->StashTP) {
			    // This must be a FOR... so stash where the ForUpdate starts!
			    SC_CGLabelDefine(MapP, LexP->ContL, MapP->CodeCount);

			    if (SC_ERR_RES ==  SC_CGCodePass(CGP, LexP->StashTP, NULL))
				goto ErrorOut;
				
			    SC_CGTokenZapTree(CGP->VMP, LexP->StashTP);
			    LexP->StashTP = NULL;
			}

			// JMP to LoopStart, stashed in LexP->TrueL.. at end of Loop Body.
			SC_CGWriteBranch(CGP, MapP, LexP->TrueL, SC_JMP_K3_CODE);
			// Define the end of the loop body... for False escape
			SC_CGLabelDefine(MapP, LexP->FalseL, MapP->CodeCount);

			// MAPI/MAPK loop needs a cleanup of the run stack
			// POPN will be last Op of MapI/K, so set BCENDMAPFLAG!
			if (LexP->Flags & SC_LEXMAPFLAG) {
			    SC_CGWriteK1(CGP, MapP, SC_CGMLOOPRUNSTACKVALS, SC_POPN_K_CODE);
			    ((SC_ByteOpPointer)MapP->CodeADP->Data + MapP->CodeCount - 1)->Flags |= SC_BCENDMAPFLAG;
			}
		    }
		    break;

		case SC_LEXSWITCHFLAG:
		    // Define False escape for Break... and also ContL in case
		    // SWITCH never got a DEFAULT!
		    SC_CGLabelDefine(MapP, LexP->FalseL, MapP->CodeCount);
		    if (! SC_CGLabelGet(MapP, LexP->ContL))
			SC_CGLabelDefine(MapP, LexP->ContL, MapP->CodeCount);
		    // One more REG cache Inval after the last case!    
		    if (LexP->Flags & SC_LEXNEXTCASEFLAG)
			SC_CGRegCacheInval(CGP, CGP->FuncLexP->MapP, 0);

		    break;
	    }   // Switch
	}   // If
	
	// Go UP!
	if (! (LexP = SupP = LexP->SupP)) 
	    goto DoneDone;

	// Possible to be at the end of a FUNC, so different MapP!	    
	// Find the new FUNC, to get ITS MapP!
	while (!(SupP->Flags & SC_LEXFUNCFLAG)) SupP = SupP->SupP;
	MapP = SupP->MapP;
    }   // While

DoneDone:
    return SC_SUCC_RES;
ErrorOut:
    return SC_ERR_RES;
}


// SC_CGCodePass recurses down into the AST (Token tree) to generate code.
//
// A single Op token usually generates one or more ByteCodes.  But in the case
// of ASSIGNMENT, two Op tokens in the form (= A (+ B C)) can be replaced with a
// *single* [PLUS A B C] ByteCode.  This requires a simple handshake:
//
// -- Going down, AssignTP indicates the Var to stash result.
//    It will have already been resolved with its VexP stored in TP->Data.
//    (For Globals, this will be the GVEX **NOT** any temp REG assignment!)
// -- On return, ASSIGN flag is set if sub could NOT assign, caller must do it.
// -- On return, TP->Data of Op ALWAYS indicates Vex (or NULL for Stack) where
//    the result went, even if not assigned.
//
//    NOTE:  [PLUS A B C] CAN set its result into an Array or Dict!  Normally,
//	     when an Op TP does not have a VexP (stashed in TP->Data), it means
//	     the results are on the STACK.  But if AssignTP comes in WITHOUT a
//	     VexP, it means a REFERence to a Dict Pair or Array Elt is already
//	     on the stack.  The OpCodes will be written with 0xFF as their first
//	     Var, meaning WRITE INTO the REFER on stack.  (0x00 as V means PUSH
//	     results onto the stack.)
//
//	     Pop_V has confusing semantics if it is to POP into a REFER... as
//	     the REFER will be lower on the run stack!  This is handled by
//	     runtime routine.
//
// Global variables cannot be directly indexed with just 1 byte in ByteCodes.
// Instead, they are first SET onto one of few REG vars.  But this mapping
// should *NOT* be done before recursing deeper into the token tree as the REG
// could be recycled and reset before returning, ensuring the wrong value!
//
// Both the R and L (complex INDEX/DOT) sides of an assignment may use all
// available REG many times over.  To ensure correctness:
// -- Always compute the R side first.
// -- Always return GVEX (never REG) as the result of any computation.
//
// REG cannot be locked, the LRU mechanism will suffice for preventing mid-step
// recycling/reassignment of REG.  *IF* REG *WERE* locked, there would be risk
// of running out of REGs in complex expressions.

    Int16	SC_CGCPAuxCheckDupe(SC_VMPointer VMP, SC_DictVPointer DupeDictP, void * KeyP, Uns8 KeyType)
    {
	SC_PairHPointer	PairP;

	if (SC_DictGetPair(DupeDictP, (SC_VarVPointer)KeyP, KeyType))
	    return 1;

	PairP = SC_BigEntryNew(VMP);
	PairP->KeyType = KeyType;
	PairP->KeyP = (SC_VarVPointer)KeyP;
	PairP->ValType = SC_VAL_INT;
	PairP->ValueP = NULL;
	SC_DictStashNewPair(VMP, DupeDictP, PairP);
	return 0;
    }

    // SC_CGCPAuxCodeExtraInArgs will *ONLY* process the extra IN args that HAVE
    // SideEffects... these go into IFIDs, so will NOT get pushed on CallStack.
    Int16	SC_CGCPAuxCodeExtraInArgs(SC_CGPointer CGP, SC_TokenPointer ATP)
    {
	while (ATP) {
	    if ((ATP->Type == SC_OPERATOR_TOKEN) &&	// Operator
		SC_CGASTHasSideEffect(ATP)) {		// HAS side effect
		// Do it for the SideEffect... go NoPush!
		ATP->Flags |= SC_TOKEN_NOPUSH_FLAG;
		if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) return SC_ERR_RES;
	    }
	    ATP = ATP->NextP;
	}

	return SC_SUCC_RES;
    }

Int16	SC_CGCodePass(SC_CGPointer CGP, SC_TokenPointer TP, SC_TokenPointer AssignTP)
{
    SC_LexPointer	FuncLexP = CGP->FuncLexP;
    SC_MapPointer	MapP = FuncLexP->MapP;
    SC_TokenPointer	ATP;			// Arg TP
    SC_VDescRecord	VArr[3];
    SC_KRecord		KonstR;
    
    Uns8		ByteOp;
    Int8		IncVal;
    Int8		DoPost;
    Int8		IsForLoop;
    Int8		ArgSwap = 0;
    Int8		SelfAssign = 0;
    Int16		HaveKonst = 0;
    Int16		IsAnd = 0;
    Int16		NoPush = 0;
    Uns32		Index;

    if (TP->Type == SC_START_TOKEN) {
	if ((TP = TP->DownP)) {		// Assign
	    // For "Assignment cascade" to work, extra copy of INDEX/DOT REFER is
	    // normally pushed on the stack (to be converted to its VALUE), except when
	    // it gets SC_TOKEN_NOPUSH_FLAG--because it is top level OP.
	    if (TP->Type == SC_OPERATOR_TOKEN)
		TP->Flags |= SC_TOKEN_NOPUSH_FLAG;
	} else
	    goto DoneDone;
    }

    // NOPUSH can have multiple causes...
    NoPush = !!(TP->Flags & SC_TOKEN_NOPUSH_FLAG);

    if (TP->Type == SC_OPERATOR_TOKEN) {
	switch (TP->Id) {
	    case SC_EQ_ID:	    goto CodeAssign;
	    case SC_LESS_ID:	    ByteOp = SC_LT_VVV_CODE; goto CodeComp;
	    case SC_GREAT_ID:	    ByteOp = SC_LT_VVV_CODE; ArgSwap = 1; goto CodeComp;
	    case SC_PLUS_ID:	    ByteOp = SC_ADD_VVV_CODE; goto CodeMath;
	    case SC_MINUS_ID:	    ByteOp = SC_SUB_VVV_CODE; goto CodeMath;
	    case SC_COMMA_ID:	    goto CodeComma;
	    case SC_SEMI_ID:	    break;	// Placeholder in SWITCH
	    case SC_DIV_ID:	    ByteOp = SC_DIV_VVV_CODE; goto CodeMath;
	    case SC_MOD_ID:	    ByteOp = SC_MOD_VVV_CODE; goto CodeMath;
	    case SC_MULT_ID:	    ByteOp = SC_MUL_VVV_CODE; goto CodeMath;
	    case SC_LOGNEG_ID:	    ByteOp = SC_NOT_V_CODE;  goto CodePolar;
	    case SC_BITNEG_ID:	    ByteOp = SC_BNEG_V_CODE;  goto CodePolar;
	    case SC_BITAND_ID:	    ByteOp = SC_BAND_VVV_CODE; goto CodeBitOp;
	    case SC_BITOR_ID:	    ByteOp = SC_BOR_VVV_CODE;  goto CodeBitOp;
	    case SC_BITXOR_ID:	    ByteOp = SC_BXOR_VVV_CODE; goto CodeBitOp;
	    case SC_CHOOSE_ID:      goto CodeChoose;
	    case SC_COLON_ID:       break;	// Placeholder in SWITCH
	    case SC_DOT_ID:         goto CodeIndex;
	    case SC_PCLOSE_ID:      break;	// Placeholder in SWITCH
	    case SC_ACLOSE_ID:      break;	// Placeholder in SWITCH
	    case SC_END_ID:         break;	// Placeholder in SWITCH
	    case SC_POPEN_ID:       break;	// Placeholder in SWITCH
	    case SC_AOPEN_ID:	    break;	// Placeholder in SWITCH
	    case SC_BEGIN_ID:	    break;	// Placeholder in SWITCH
	    case SC_UPLUS_ID:	    break;	// Placeholder in SWITCH
	    case SC_UMINUS_ID:	    ByteOp = SC_NEG_V_CODE; goto CodePolar;

	    case SC_NULL_ID:	    break;	// Placeholder in SWITCH
	    case SC_INDEX_ID:	    goto CodeIndex;
	    case SC_CALL_ID:	    goto CodeCall;
	    case SC_IN_ID:	    break;	// Placeholder in SWITCH
	    case SC_OUT_ID:	    break;	// Placeholder in SWITCH
	    case SC_VAR_ID:	    goto CodeVar;
	    case SC_DEF_ID:	    goto CodeDef;
	    case SC_CONS_ID:	    goto CodeCons;
	    case SC_PAIR_ID:	    break;	// Placeholder in SWITCH
	    case SC_LABEL_ID:	    goto CodeLabel;
	    case SC_GOTO_ID:	    goto CodeGoto;
	    case SC_CASE_ID:	    goto CodeCase;
	    case SC_DEFAULT_ID:	    goto CodeCase;
	    case SC_FOR_ID:	    IsForLoop = 1; goto CodeLoop;
     	    case SC_WHILE_ID:	    IsForLoop = 0; goto CodeLoop;
     	    case SC_IF_ID:	    goto CodeIf;
     	    case SC_SWITCH_ID:	    goto CodeSwitch; 
     	    case SC_DO_ID:	    goto CodeDo; 
     	    case SC_DOCOND_ID:	    goto CodeDoCond; 
     	    case SC_ELSE_ID:	    goto CodeElse; 
     	    case SC_BREAK_ID:	    goto CodeBreak; 
     	    case SC_CONT_ID:	    goto CodeCont; 
     	    case SC_FUNC_ID:	    goto CodeFunc; 
     	    case SC_RETURN_ID:	    goto CodeReturn;
     	    case SC_YIELD_ID:	    goto CodeReturn; 
     	    case SC_FROM_ID:	    break; 
     	    case SC_LOGPOS_ID:	    ByteOp = SC_POS_V_CODE; goto CodePolar;
	    case SC_UNPACK_ID:	    goto CodeUnPack;

     	    case SC_ALIAS_ID:	    goto CodeAlias; 
     	    case SC_EQEQ_ID:	    ByteOp = SC_EQ_VVV_CODE; goto CodeComp; 
     	    case SC_NEQ_ID:	    ByteOp = SC_NEQ_VVV_CODE; goto CodeComp; 
     	    case SC_LESSEQ_ID:	    ByteOp = SC_LTQ_VVV_CODE; goto CodeComp; 
     	    case SC_GREATEQ_ID:	    ByteOp = SC_LTQ_VVV_CODE; ArgSwap = 1; goto CodeComp; 
     	    case SC_LOGAND_ID:	    IsAnd = 1; goto CodeLogic; 
     	    case SC_LOGOR_ID:	    goto CodeLogic; 
     	    case SC_SHL_ID:	    ByteOp = SC_SHL_VVV_CODE; goto CodeMath; 
     	    case SC_SHR_ID:	    ByteOp = SC_SHR_VVV_CODE; goto CodeMath; 

     	    case SC_PLUSPLUS_ID:    DoPost = 1; IncVal = 1;  goto CodeInc; 
     	    case SC_MINMIN_ID:	    DoPost = 1; IncVal = -1; goto CodeInc; 
     	    case SC_PLUSEQ_ID:	    ByteOp = SC_ADD_VVV_CODE; SelfAssign = 1; goto CodeMath; 
     	    case SC_MINUSEQ_ID:	    ByteOp = SC_SUB_VVV_CODE; SelfAssign = 1; goto CodeMath; 
     	    case SC_DIVEQ_ID:	    ByteOp = SC_DIV_VVV_CODE; SelfAssign = 1; goto CodeMath; 
     	    case SC_MODEQ_ID:	    ByteOp = SC_MOD_VVV_CODE; SelfAssign = 1; goto CodeMath; 
     	    case SC_MULTEQ_ID:	    ByteOp = SC_MUL_VVV_CODE; SelfAssign = 1; goto CodeMath; 
     	    case SC_BITANDEQ_ID:    ByteOp = SC_BAND_VVV_CODE; SelfAssign = 1; goto CodeBitOp; 
     	    case SC_BITOREQ_ID:	    ByteOp = SC_BOR_VVV_CODE;  SelfAssign = 1; goto CodeBitOp; 
     	    case SC_BITXOREQ_ID:    ByteOp = SC_BXOR_VVV_CODE; SelfAssign = 1; goto CodeBitOp; 

     	    case SC_PREPLUSPLUS_ID: DoPost = 0; IncVal = 1;   goto CodeInc; 
     	    case SC_PREMINMIN_ID:   DoPost = 0; IncVal = -1;  goto CodeInc; 

     	    case SC_SHLEQ_ID:	    ByteOp = SC_SHL_VVV_CODE; SelfAssign = 1; goto CodeMath; 
     	    case SC_SHREQ_ID:	    ByteOp = SC_SHR_VVV_CODE; SelfAssign = 1; goto CodeMath; 
     	    case SC_IDENT_ID:	    ByteOp = SC_IDNT_VVV_CODE; goto CodeIdnt; 
     	    case SC_NIDENT_ID:	    ByteOp = SC_NDNT_VVV_CODE; goto CodeIdnt; 

     	    default:		    goto DoneDone; 
     	} 
     } 
    
     DoneDone: 
     	return SC_SUCC_RES; 

     CodeIndex: 
     	// Index into an Array or Dict: (Index MyArr MyIndex) The first Arg is 
     	// the AGGREGATE Object (Array or Dict) and the second is the FIELD 
     	// (number or string). 
     	// 
     	// INDEX and DOT both come here. 
     	//	INDEX:  FIELD can be Number, Ident, Op, or String 
     	//	DOT:	FIELD is only Number or String! 
     	// So DOT is just a subset of INDEX!! 
     	// 
     	// NOTE: It makes a BIG difference whether indexing into an Object to 
     	//	 GET a value or to SET a value.  In the latter case, a storage 
     	//	 item should be generated (and the Object expanded) if it does 
     	//	 not already exist!  So normally an IREF bytecode is created
	//	 in such an instance... except if SC_TOKEN_ZREF_FLAG, then a
	//	 ZREF is generated instead--this only happens for === operation,
	//	 where a missing arr elt or dict pair should NOT be populated.
     	
    	{ 
     	    SC_VexPointer	ObjVexP; 
     	    Int16		DoK = 0; 
     	    Int64		Konst; 
     	    Uns8		GetLValue = TP->Flags & SC_TOKEN_LVALUE_FLAG;
	    Uns8		DoZRef = TP->Flags & SC_TOKEN_ZREF_FLAG;

     	    ATP = TP->DownP;		// Get the Object first. 
     	    // Can only be IDENT (var name) or OP (Index, Dot, or Func) 
     	    if (ATP->Type == SC_OPERATOR_TOKEN) 
     		if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut; 
     	    ObjVexP = (SC_VexPointer)ATP->Data;		// Will not be RVEX!! 

     	    ATP = ATP->NextP;		// Get the FIELD now 
     	    switch (ATP->Type) { 
     		case SC_STRING_TOKEN: 
     		    Index = SC_CGGetStringIndex(CGP, ATP); 
     		    SC_CHECKERROR(CGP); 
     		    SC_CGWriteI3(CGP, MapP, Index, SC_PSHS_I3_CODE); 
     		    VArr[2].Class = SC_STACK; 
     		    break; 

     		case SC_NUMBER_TOKEN: 
     		    if (ATP->Id == SC_INTNUMB_ID) { 
     			Konst = ATP->Data; 

     			if (GetLValue) 
     			    // Dealing with IREF, so have I2
     			    DoK = (0 <= Konst) && (Konst <= 65536); 
     			else { 
     			    // Dealing with IGET, so only K 
     			    if ((-128 <= Konst) && (Konst <= 127)) { 
     				VArr[2].Class = SC_KONST; 
     				VArr[2].Index = 1; 
     				DoK = 1; 
     			    } 
     			} 

     			if (! DoK) {
			    SC_CGCodePushInt(CGP, ATP->Data);			
     			    VArr[2].Class = SC_STACK; 
     			} 
     		    } else {
			// Dealing with a FLOAT number!
			SC_CGCodePushFloat(CGP, *(double *)&ATP->Data);
			VArr[2].Class = SC_STACK;
		    }
     		    break; 

     		case SC_OPERATOR_TOKEN: 
     		    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut; 
     		    // Fall through 
     		case SC_IDENTIFIER_TOKEN: 
     		    SC_CGLoadVar(CGP, MapP, &VArr[2], (SC_VexPointer)ATP->Data, 1);
		    break;
	    }

	    // Load VArr[1] finally... (Waited so REG/Cache does not get clobbered)
	    SC_CGLoadVar(CGP, MapP, &VArr[1], ObjVexP, 1);
	    VArr[0].Class = SC_STACK;

	    // Is this an LValue for an assignment? (or Alias)?
	    if (GetLValue) {
		if (DoK)
		    SC_CGWriteVK2(CGP, MapP, VArr[1], Konst, SC_INDEX, (DoZRef) ? SC_ZREF_VI2_CODE : SC_IREF_VI2_CODE);
		else {
		    // Make it load VArr[1] and [2]
		    SC_CGWrite2V(CGP, MapP, VArr + 1, (DoZRef) ? SC_ZREF_VV_CODE : SC_IREF_VV_CODE);
		}
		// Pushes REF to Indexed Field of Aggregate onto stack
		// TP-Data == NULL means it is on STACK
	    } else {
		SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);
		if (DoK) {
		    SC_CGWriteIGETVVK(CGP, MapP, VArr, Konst);
		} else
		    SC_CGWriteIGETVVV(CGP, MapP, VArr);
	    }

	    // NOPUSH happens when IN list of IFID has extraneous A[x] or D.y arg!!
	    // Xform usually gets rid of this sort of thing at compile time, but cannot
	    // apply to "extra" args in IFIDs (or just func Calls there)!
	    if ((VArr[0].Class == SC_STACK) && (TP->Flags & SC_TOKEN_NOPUSH_FLAG))
		SC_CGWriteNoArg(CGP, MapP, SC_POP1_CODE);
	}
	goto DoneDone;

    CodeChoose:
	// (X = (A < B) ? C : D) --> (= X (CHOOSE (< A B) C D))
	// Implemented essentially as "IF (< A B) X = C; ELSE X = D;"
	// So looks like a little IF/ELSE.
	//
	// the T or F clauses of a CHOOSE are obviously conditional, so any
	// RegCache assignments done in them must be invalidated.  a temp Lex is
	// created for each clause, and zapped at the end, thus invalidating its
	// Reg assignments (using RVex BLevel).
	//
	// NOTE:  Cannot pass the buck if Sub-op does NOT do AssignTP,
	//	  as the caller CANNOT KNOW whether T or F side wins!
	{
	    SC_JSetRecord	JSetR;
	    SC_LexPointer	TempLexP;
	    Uns32		EndL;
	    Int16		IsOr;

	    EndL = SC_CGLabelAlloc(CGP, MapP);		// After everything
	    JSetR.Flags = SC_JSETANDFLAG | SC_JSETTOPFLAG;
	    JSetR.L[0] = SC_CGLabelAlloc(CGP, MapP);	// Always has a False
	    JSetR.L[1] = 0L;				// Normally falls through to True
	    JSetR.UpP = NULL;

	    ATP = TP->DownP;			// First Arg is COND clause!
	    switch (ATP->Type) {
		case SC_STRING_TOKEN:
		    // Treat as TRUE... fall through!
		    break;

		case SC_NUMBER_TOKEN:
		    if (SC_CGIsIntTP(ATP, 0LL))	// Always FALSE!
			SC_CGWriteBranch(CGP, MapP, JSetR.L[0], SC_JMP_K3_CODE);
		    // Otherwise TRUE... nothing to test, fall through!
		    break;

		case SC_OPERATOR_TOKEN:
		    if ((IsOr = (ATP->Id == SC_LOGOR_ID)))
			JSetR.L[1] = SC_CGLabelAlloc(CGP, MapP);

		    if (IsOr || (ATP->Id == SC_LOGAND_ID)) {
			// Logical OP, use the JumpSet record
			CGP->CallerJSetP = &JSetR;
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
			if (IsOr) SC_CGLabelDefine(MapP, JSetR.L[1], MapP->CodeCount);
		    } else {
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
			SC_CGWriteLogicTest(CGP, MapP, (SC_VexPointer)ATP->Data, JSetR.L[0], 0);
		    }
		    break;

		case SC_IDENTIFIER_TOKEN:
		    SC_CGWriteLogicTest(CGP, MapP, (SC_VexPointer)ATP->Data, JSetR.L[0], 0);
		    break;
	    }

	    // Do the TRUE clause then FALSE.... assumes exactly 2 of these!
	    ATP = ATP->NextP;
	    while (1) {

		// Create a Temp Lex to isolate new RegCache assignments... must be LEXCONDFLAG!!
		TempLexP = SC_CGAddLex(CGP, SC_LEXCONDFLAG);
		if (TempLexP->BLevel > 255) SC_CODEERROR(CGP, TP, SC_STR_ErrorTooDeep);

		    switch (ATP->Type) {
			case SC_STRING_TOKEN:
			    Index = SC_CGGetStringIndex(CGP, ATP);
			    SC_CHECKERROR(CGP);
			    if (AssignTP) {
				SC_CGWriteI3(CGP, MapP, Index, SC_PSHS_I3_CODE);
				SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);
				if (VArr[0].Class != SC_STACK)
				    SC_CGWrite1V(CGP, MapP, VArr[0], SC_POP_V_CODE);
			    } else if (! NoPush)
				SC_CGWriteI3(CGP, MapP, Index, SC_PSHS_I3_CODE);
			break;

			case SC_NUMBER_TOKEN:
			    if (AssignTP)
				SC_CGCodeAssignNumb(CGP, ATP, AssignTP, TP, 0);
			    else if (! NoPush)
				SC_CGCodePushTokenNumb(CGP, ATP);
			    break;

			case SC_OPERATOR_TOKEN:
			    if (NoPush) 
				ATP->Flags |= SC_TOKEN_NOPUSH_FLAG;
			    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, AssignTP)) goto ErrorOut;
			    if (AssignTP && (ATP->Flags & SC_TOKEN_ASSIGN_FLAG)) {
				// Op could not assign, must do it here!  
				SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);
				SC_CGLoadVar(CGP, MapP, &VArr[1], (SC_VexPointer)ATP->Data, 1);
				SC_CGWriteMovVV(CGP, MapP, VArr, 0);
			    }
			    break;

			case SC_IDENTIFIER_TOKEN:
			    SC_CGLoadVar(CGP, MapP, &VArr[1], (SC_VexPointer)ATP->Data, 1);
			    if (AssignTP) {
				SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);
				SC_CGWriteMovVV(CGP, MapP, VArr, 0);
			    } else if (!NoPush && VArr[1].Class != SC_STACK)
				SC_CGWrite1V(CGP, MapP, VArr[1], SC_PSH_V_CODE);
			    break;
		    }

		// Zap the temp Lex just to invalidate any RegCache assignments.
		SC_CGZapLastLex(CGP);

		if (! (ATP = ATP->NextP)) break;	// LOOP or BREAK

		// Write JMP after TRUE clause, so it doesn't fall into FALSE clause!
		SC_CGWriteBranch(CGP, MapP, EndL, SC_JMP_K3_CODE);
		// Define FALSE escape after doing the TRUE case
		SC_CGLabelDefine(MapP, JSetR.L[0], MapP->CodeCount);
	    }

	    SC_CGLabelDefine(MapP, EndL, MapP->CodeCount);
	}
	goto DoneDone;

    CodeCons:
	// CONStructor for ARRAY and DICT, will have >0 args.
	// Args are PAIR Ops for DICT, otherwise it is an ARR.
	//
	// NOTE: ARRAY or DICT is first created (and assigned), then
	//	 its elts/pairs are added sequentially.
	{
	    Int64		Count = 0LL;
	    Uns8		PopEnd;

	    ATP = TP->DownP;
	    // Count the elts first and PUSH it... Array Elts or Dict Pairs
	    while (ATP) Count += 1, ATP = ATP->NextP;
	    SC_CGCodePushInt(CGP, Count);

	    // Process the AssignTP... or set V to STACK
	    SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);

	    ATP = TP->DownP;
	    if (SC_CGIsOpTP(ATP, SC_PAIR_ID)) {
		Int16		IsKey;
		SC_DictVPointer	DupeDictP = SC_DictCreateNewDict(CGP->VMP, 8);
	    
		// Construct a DICT... 
		// Write code to create the DICT... it will pop the Count.
		PopEnd = 0;
		SC_CGWrite1V(CGP, MapP, VArr[0], SC_DNEW_V_CODE);
		// DONE!  There is now a new DICT.

		// Write code for each elt... uses CS_DINI_CODE: pops DictRef, Key,
		// and Value off stack, pushes DictRef back when done!
		if (VArr[0].Class != SC_STACK) {
		    // Push the DictRef if stored somewhere else.
		    SC_CGWrite1V(CGP, MapP, VArr[0], SC_PSH_V_CODE);
		    PopEnd = 1;
		}

		// One PAIR at a time...  *MUST* error check for duplicate keys!
		//			  Can only do it partially... keys can come
		//			  from Vars and FuncCalls too!!
		while (ATP) {
		    SC_TokenPointer	EltTP = ATP->DownP;	// Key first

		    IsKey = 1;
		    while (EltTP) {
			switch (EltTP->Type) {
			    case SC_STRING_TOKEN:
				Index = SC_CGGetStringIndex(CGP, EltTP);
				SC_CHECKERROR(CGP);
				if (IsKey && SC_CGCPAuxCheckDupe(CGP->VMP, DupeDictP, (void *)(Uns64)Index, SC_VAL_INT))
				    SC_CODEERROR(CGP, EltTP, SC_STR_ErrorDupeKey);
				SC_CGWriteI3(CGP, MapP, Index, SC_PSHS_I3_CODE);
				break;

			    case SC_NUMBER_TOKEN:
				if (IsKey && SC_CGCPAuxCheckDupe(CGP->VMP, DupeDictP, (void *)EltTP->Data,
								(EltTP->Id == SC_INTNUMB_ID) ? SC_VAL_INT : SC_VAL_FLT))
				    SC_CODEERROR(CGP, EltTP, SC_STR_ErrorDupeKey);
			    	SC_CGCodePushTokenNumb(CGP, EltTP);
				break;

			    // CANNOT Dupe check these at compile time!!
			    case SC_OPERATOR_TOKEN:
				if (SC_ERR_RES == SC_CGCodePass(CGP, EltTP, NULL)) goto ErrorOut;
				// Fall through
			    case SC_IDENTIFIER_TOKEN:
				if (SC_CGLoadVar(CGP, MapP, &VArr[0], (SC_VexPointer)EltTP->Data, 0)) {
				    if (VArr[0].Class != SC_STACK)
					SC_CGWrite1V(CGP, MapP, VArr[0], SC_PSH_V_CODE);
				} else
				    SC_CGWriteI3(CGP, MapP, (*(SC_VexPointer)EltTP->Data).Index, SC_PSHG_I3_CODE);
				break;
			}
			
			// Now do the Value.... 
			EltTP = EltTP->NextP;
			if (! IsKey) break;
			IsKey = 0;
		    }
		    SC_CGWriteNoArg(CGP, MapP, SC_DINI_CODE);
		    ATP = ATP->NextP;
		}
		SC_DictPurge(CGP->VMP, DupeDictP, 0);		// Purge, can have StrHP refs
	
	    } else {
		// Construct an ARRAY... 
		// Write Code to create the ARR.. it will pop the Count.
		PopEnd = 1;
		SC_CGWrite1V(CGP, MapP, VArr[0], SC_ANEW_V_CODE);
		// DONE!  There is now a new ARRAY.

		// Write code for each elt.  SC_AINI_V_CODE is safe, needs
		// ArrayRef and starting Index (0) on stack, will update Index.
		if (VArr[0].Class != SC_STACK) {
		    // Push the ArrayRef if stored somewhere else.
		    SC_CGWrite1V(CGP, MapP, VArr[0], SC_PSH_V_CODE);
		    PopEnd = 2;
		}
		SC_CGCodePushInt(CGP, 0);	// Starting Index!

		// One Elt at a time...
		while (ATP) {
		    VArr[0].Class = SC_STACK;
		    switch (ATP->Type) {
			case SC_STRING_TOKEN:
			    Index = SC_CGGetStringIndex(CGP, ATP);
			    SC_CHECKERROR(CGP);
			    SC_CGWriteI3(CGP, MapP, Index, SC_PSHS_I3_CODE);
			    break;

			case SC_NUMBER_TOKEN:
			    SC_CGCodePushTokenNumb(CGP, ATP);
			    break;

			case SC_OPERATOR_TOKEN:
			    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
			    // Fall through
			case SC_IDENTIFIER_TOKEN:
			    SC_CGLoadVar(CGP, MapP, &VArr[0], (SC_VexPointer)ATP->Data, 1);
			    break;
		    }
		    SC_CGWrite1V(CGP, MapP, VArr[0], SC_AINI_V_CODE);		// SEQ ASet!
		    ATP = ATP->NextP;
		}
	    }

	    // Clean up... Pop what is needed.
	    if (PopEnd) {
		if (PopEnd == 1)
		    SC_CGWriteNoArg(CGP, MapP, SC_POP1_CODE);
		else
		    SC_CGWriteK1(CGP, MapP, PopEnd, SC_POPN_K_CODE);
	    }
	}
	goto DoneDone;

    CodeReturn:
	// Can be:
	// (Return) (Return X) (Return (From L)) or (Return X (From L))
	// (Yield) (Yield X) (Yield (From L)) or (Yield X (From L))
	//
	// Return and Yield with no arg will return 0.
	{
	    Int16		Arg1InVex = 0;
	    Int16		Arg2InVex = 0;
	    Int16		Write = 1;
	    SC_TokenPointer	A2TP = NULL;

	    VArr[0].Class = VArr[1].Class = SC_STACK;

	    if ((ATP = TP->DownP)) {
		// Have AN arg...
		A2TP = ATP->NextP;
		if (A2TP)
		    A2TP = A2TP->DownP;				// RCP for FROM
		else if (SC_CGIsOpTP(ATP, SC_FROM_ID))
		    A2TP = ATP->DownP, ATP = NULL;		// First arg is FROM, get RCP
	    }

	    if (ATP) {
		switch (ATP->Type) {
		    case SC_STRING_TOKEN:
			Index = SC_CGGetStringIndex(CGP, ATP);
			SC_CHECKERROR(CGP);			
			SC_CGWriteI3(CGP, MapP, Index, SC_PSHS_I3_CODE);
			break;

		    case SC_NUMBER_TOKEN:
			if ((TP->Id == SC_RETURN_ID) && (ATP->Id == SC_INTNUMB_ID) && (-128 <= ATP->Data) && (ATP->Data <= 127)) {
			    SC_CGWriteK1(CGP, MapP, (Int8)ATP->Data, SC_FRET_K_CODE);
			    Write = 0;
			    break;
			}
			SC_CGCodePushTokenNumb(CGP, ATP);	// Arg1InVex is already 0
			break;

		    case SC_OPERATOR_TOKEN:
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) return SC_ERR_RES;
			// Fall through...
		    case SC_IDENTIFIER_TOKEN:
			Arg1InVex = (ATP->Data != 0LL);		// Otherwise already on stack
			break;
		}
	    } else
		SC_CGWriteK1(CGP, MapP, 0, SC_PSH_K_CODE);	// NO arg version of RETURN or YIELD		

	    if (A2TP) {
		switch (A2TP->Type) {
		    case SC_OPERATOR_TOKEN:
			if (SC_ERR_RES == SC_CGCodePass(CGP, A2TP, NULL)) return SC_ERR_RES;
			// Fall through...
		    case SC_IDENTIFIER_TOKEN:
			Arg2InVex = (A2TP->Data != 0LL);	// Otherwise already pushed on stack
			break;

		    default: SC_CODEERROR(CGP, A2TP, SC_STR_ErrorBadArg);
		}
	    } else if (TP->Id == SC_YIELD_ID)
		SC_CGWriteK1(CGP, MapP, 0, SC_PSH_K_CODE);	// Yield MUST have an RCP, even if 0 to mean self.

	    if (Write) {
		if (Arg1InVex) SC_CGLoadVar(CGP, MapP, &VArr[0], (SC_VexPointer)ATP->Data, 1);
		if (Arg2InVex) SC_CGLoadVar(CGP, MapP, &VArr[1], (SC_VexPointer)A2TP->Data, 1);
		if (TP->Id == SC_RETURN_ID) {
		    if (A2TP)
			SC_CGWrite2V(CGP, MapP, VArr, SC_FRET_VV_CODE);	// RCP version of RETURN
		    else if (Arg1InVex)
			SC_CGWrite1V(CGP, MapP, VArr[0], SC_FRET_V_CODE);	// 1 value version
		    else
			SC_CGWriteNoArg(CGP, MapP, SC_FRET_CODE);		// Assumes return value is on stack
		} else {
		    // Has to be YIELD!
		    SC_CGWrite2V(CGP, MapP, VArr, SC_FYLD_VV_CODE);		// Must have an RCP, even if 0
		}
	    }
	}
	goto DoneDone;

    CodeRCPIFID:
	// CallGet function... places Ptr to RunCall in V.
	// V can be Stack (0x00), REFER, or just a var.
	// Call anyway, even if NO PUSH

	// NO PUSH?  Extra args?

	if (AssignTP)
	    SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);	// Will set TP->Data!
	else {
	    VArr[0].Class = SC_STACK;
	    TP->Data = 0LL;						// Result on Stack
	}
	SC_CGWrite1V(CGP, MapP, VArr[0], SC_FGET_V_CODE);
	if (NoPush) SC_CGWriteNoArg(CGP, MapP, SC_POP1_CODE);

	// Process remaining IN args... in case they have side-effects!
	if (((ATP = TP->DownP->NextP->DownP)) &&			// ASSIGN, First INPUT
	    (SC_ERR_RES == SC_CGCPAuxCodeExtraInArgs(CGP, ATP)))	// Process all!
	    return SC_ERR_RES;
	goto DoneDone;

    CodeSubmitIFID:
	// Submit() function...
	// It will return the RC from the subordinate (S) func to its caller
	// (M).  But S itself may expect a result when SUBMIT returns, so push a
	// 1 *after* it.  (This will work if S has "x = Submit();"!!)

	SC_CGWriteNoArg(CGP, MapP, SC_FSUB_CODE);
	if (! NoPush) {
	    SC_CGWriteK1(CGP, MapP, 1, SC_PSH_K_CODE);
	    TP->Flags |= SC_TOKEN_ASSIGN_FLAG;
	}

	// Process remaining IN args... in case they have side-effects!
	if (((ATP = TP->DownP->NextP->DownP)) &&
	    (SC_ERR_RES == SC_CGCPAuxCodeExtraInArgs(CGP, ATP)))
	    return SC_ERR_RES;
	goto DoneDone;

    CodeResumeIFID:
	// Handles Resume(R)/Release(R) and sc.Resume(R)/sc.Release(R)
	if ((ATP = TP->DownP->NextP->DownP)) {				// Must have RCP as INPUT arg!
	    SC_VDescRecord V;
	    switch (ATP->Type) {
		case SC_OPERATOR_TOKEN:
		    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) return SC_ERR_RES;
		    // Fall through
		case SC_IDENTIFIER_TOKEN:
		    V.Class = SC_STACK;
		    if (ATP->Data)					// if 0LL, then already on stack!
			SC_CGLoadVar(CGP, MapP, &V, (SC_VexPointer)ATP->Data, 1);
		    break;
		    
		default: SC_CODEERROR(CGP, TP, SC_STR_ErrorBadArg);
	    }

	    if (ByteOp == SC_RELEASE_IFID) {
	        SC_CGWrite1V(CGP, MapP, V, SC_FREL_V_CODE);			// Release
		if (! NoPush) SC_CGWriteK1(CGP, MapP, 1, SC_FRET_K_CODE);	// Push 1 if wants it
	    } else {
		SC_CGWrite1V(CGP, MapP, V, SC_FRES_V_CODE);			// Resume
		if (NoPush) SC_CGWriteNoArg(CGP, MapP, SC_POP1_CODE);		// Pop result if ignored
	    }

       } else
	    SC_CODEERROR(CGP, TP, SC_STR_ErrorMissingArg);


	// Process remaining IN args... in case they have side-effects!
	if (ATP->NextP && (SC_ERR_RES == SC_CGCPAuxCodeExtraInArgs(CGP, ATP->NextP)))
	    return SC_ERR_RES;

	// In case there is an assign!
	TP->Flags |= SC_TOKEN_ASSIGN_FLAG;	
	goto DoneDone;

    CodeIntIFID:
	// Pushes Int to stack, so ignores AssignTP!
	if ((ATP = TP->DownP->NextP->DownP)) {
	    // ATP is first Arg of IN list
	    switch (ATP->Type) {
		case SC_STRING_TOKEN:
		    if (! NoPush)
			SC_CGCodePushInt(CGP, SC_CGGetStringIndex(CGP, ATP));
		    break;
		    
		case SC_NUMBER_TOKEN:
		    if (! NoPush) {
			if (ATP->Id == SC_INTNUMB_ID)
			    SC_CGCodePushInt(CGP, ATP->Data);
			else
			    SC_CGCodePushInt(CGP, (Int64)*(double *)&ATP->Data);
		    }
		    break;
		    
		case SC_OPERATOR_TOKEN:
		    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) return SC_ERR_RES;
		    // Fall Through
		case SC_IDENTIFIER_TOKEN:
		    if (! NoPush) {
			SC_VDescRecord		V;
			if (ATP->Data == 0LL)
			    V.Class = SC_STACK;
			else
			    SC_CGLoadVar(CGP, MapP, &V, (SC_VexPointer)ATP->Data, 1);
			SC_CGWrite1V(CGP, MapP, V, SC_PSHI_V_CODE);
		    }
		    break;
	    }

	    // Process remaining IN args... in case they have side-effects!
	    if (ATP->NextP && (SC_ERR_RES == SC_CGCPAuxCodeExtraInArgs(CGP, ATP->NextP)))
		return SC_ERR_RES;
	    
	} else if (! NoPush)
	    SC_CGCodePushInt(CGP, 0);

	// In case there was an assign!
	TP->Flags |= SC_TOKEN_ASSIGN_FLAG;
	goto DoneDone;

    CodeFltIFID:
	// Pushes FLT to stack, so ignores AssignTP!
	if ((ATP = TP->DownP->NextP->DownP)) {
	    // ATP is first Arg of IN list
	    switch (ATP->Type) {
		case SC_STRING_TOKEN:
		    if (! NoPush)
			SC_CGCodePushFloat(CGP, SC_CGGetStringIndex(CGP, ATP));
		    break;
		    
		case SC_NUMBER_TOKEN:
		    if (! NoPush) {
			if (ATP->Id == SC_INTNUMB_ID)
			    SC_CGCodePushFloat(CGP, (double)ATP->Data);		// Converts
			else
			    SC_CGCodePushFloat(CGP, *(double *)&ATP->Data);	// Read as Double
		    }
		    break;
		    
		case SC_OPERATOR_TOKEN:
		    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) return SC_ERR_RES;
		    // Fall Through
		case SC_IDENTIFIER_TOKEN:
		    if (! NoPush) {
			SC_VDescRecord V;
			if (ATP->Data == 0LL)
			    V.Class = SC_STACK;
			else
			    SC_CGLoadVar(CGP, MapP, &V, (SC_VexPointer)ATP->Data, 1);
			SC_CGWrite1V(CGP, MapP, V, SC_PSHF_V_CODE);
		    }
		    break;
	    }

	    // Process remaining IN args... in case they have side-effects!
	    if (ATP->NextP && (SC_ERR_RES == SC_CGCPAuxCodeExtraInArgs(CGP, ATP->NextP)))
		return SC_ERR_RES;
	    
	} else if (! NoPush)
	    SC_CGCodePushInt(CGP, 0);

	// In case there was an assign!
	TP->Flags |= SC_TOKEN_ASSIGN_FLAG;
	goto DoneDone;	

    CodeNewArrIFID:		// Also does Dict!
	// Process the AssignTP... or set V to STACK
	SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);

	if ((ATP = TP->DownP->NextP->DownP)) {		// First Arg of IN list, can be NULL
	    switch (ATP->Type) {
		case SC_NUMBER_TOKEN:
		    SC_CGCodePushTokenNumb(CGP, ATP);
		    break;

		case SC_OPERATOR_TOKEN:
		    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) return SC_ERR_RES;
		    // Fall through...
		case SC_IDENTIFIER_TOKEN:
		    if (ATP->Data != 0LL)
			SC_CGWriteCallArg(CGP, MapP, (SC_VexPointer)ATP->Data, 0);
		    break;

	        default:SC_CODEERROR(CGP, ATP, SC_STR_ErrorBadArg);
		    break;
	    }

	    // Process remaining IN args... in case they have side-effects!
	    if (ATP->NextP && (SC_ERR_RES == SC_CGCPAuxCodeExtraInArgs(CGP, ATP->NextP)))
		return SC_ERR_RES;

	} else
	    SC_CGWriteK1(CGP, MapP, 0, SC_PSH_K_CODE);


	SC_CGWrite1V(CGP, MapP, VArr[0], (ByteOp == SC_ARRAY_IFID) ? SC_ANEW_V_CODE : SC_DNEW_V_CODE);

	// NOTE:  Could have "Array()" IFID as a member of a CONS series...
	// AssignTP will be NULL, but will NOT set SC_TOKEN_NOPUSH_FLAG!!!
	if (NoPush) {
	    // Discard result
	    SC_CGWriteNoArg(CGP, MapP, SC_POP1_CODE);
	}	
	goto DoneDone;

    CodeMapIFID:
	// Handles MapI(S)(V, I) and MapK(S)(V, K)
	//
	// S (structure being mapped) can be any R-Value, even NULL.
	// V and I/k are L-Values (in OUT list) or NULL.
	//
	// Prelude:	Push Obj	// What is being mapped
	//		Push 0		// Index = 0, Start at beginning
	//		Ref Value	// Store Value here
	//		Ref Key		// Store Key/Index here
	//
        // Loop:	IMAP		// Pushes F if Obj is NULL
	//		BRF Done:	// Jump to end when false
	//		Body...		// {} body of loop
	//		...
	//		JMP Loop	// Iterate
	// Done:	PopN 4		// Flush args

      {
	    Int16		Count;
	    Uns32		LoopStartI;				// JMP back to iterate
	    SC_JSetRecord	JSetR;
	    SC_LexPointer	NewLexP;
	    Uns32		StartCodeCount = MapP->CodeCount;	// MapI/K will start at next Op!!

	    // Mark this func as HAVING MapI/K... so check goto jmps for entry/exit errors.
	    MapP->Flags |= SC_MAP_MLOOPFLAG;
	    // Add LEX in for body of MapI, MapK, etc.
	    NewLexP = SC_CGAddLex(CGP, SC_LEXLOOPFLAG | SC_LEXMAPFLAG | SC_LEXONEFLAG | SC_LEXCONDFLAG);
	    if (NewLexP->BLevel > 255) SC_CODEERROR(CGP, TP, SC_STR_ErrorTooDeep);

    	    // (Call Ref (In...) (Out...)) So go down from call, then over to IN list argsn.
	    LoopStartI = SC_CGLabelAlloc(CGP, MapP);
	    if ((ATP = TP->DownP->NextP->DownP)) {	// ASSIGN  In args... if any!
		// Push Object being mapped... can be any R-Value... even FunCall
		switch (ATP->Type) {
		    case SC_STRING_TOKEN:
			Index = SC_CGGetStringIndex(CGP, ATP);
			SC_CHECKERROR(CGP);
			SC_CGWriteI3(CGP, MapP, Index, SC_PSHS_I3_CODE);
			break;

		    case SC_NUMBER_TOKEN:
			SC_CGCodePushTokenNumb(CGP, ATP);
			break;

		    case SC_OPERATOR_TOKEN:
			if (ATP->Id == SC_NULL_ID) {
			    SC_CGWriteK1(CGP, MapP, 0, SC_PSH_K_CODE);
			    break;
			}
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) return SC_ERR_RES;
			// Fall through...
		    case SC_IDENTIFIER_TOKEN:
			if (ATP->Data) SC_CGWriteCallArg(CGP, MapP, (SC_VexPointer)ATP->Data, 0);
			// Otherwise already on stack!
			break;
		}

		// Process additional IN args for side effects... NOPUSH!
		if (ATP->NextP && (SC_ERR_RES == SC_CGCPAuxCodeExtraInArgs(CGP, ATP->NextP)))
		    return SC_ERR_RES;
	    } else
		SC_CODEERROR(CGP, TP, SC_STR_ErrorBadArg);

	    // Push 0 so Map Op starts at beginning... will update it each iteration
	    SC_CGWriteK1(CGP, MapP, 0, SC_PSH_K_CODE);

	    // Go to OUT list and down into it.  ???? Missing OUT list!!
	    Count = 0;
	    if ((ATP = TP->DownP->NextP->NextP)) {	// ASSIGN   OUT list, if there
		ATP = ATP->DownP;
		while (ATP) {
		    Count += 1;
		    // Can be NULL, DOT, INDEX, or Identifier.
		    if (ATP->Type == SC_OPERATOR_TOKEN) {
			if (ATP->Id == SC_NULL_ID) {
			    SC_CGWriteK1(CGP, MapP, 0, SC_PSH_K_CODE);		// 0 means no var
			    ATP = ATP->NextP;
			    continue;
			}
			ATP->Flags |= SC_TOKEN_LVALUE_FLAG;
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) return SC_ERR_RES;
			// Will Push REF unto stack.
		    }

		    // Either a processed Op or Ident if it gets here
		    SC_CGWriteCallArg(CGP, MapP, (SC_VexPointer)ATP->Data, 1);
		    ATP = ATP->NextP;
		    if (Count == 2) break;
		}
	    }

	    // Push NULL for any missing args... I or V+I could be missing!
	    while (Count < 2) {
		Count += 1;
		SC_CGWriteK1(CGP, MapP, 0, SC_PSH_K_CODE);			// 0 means no var
	    }
	    
	    SC_CGLabelDefine(MapP, LoopStartI, MapP->CodeCount);		// Loop back to it
	    SC_CGWriteNoArg(CGP, MapP, (ByteOp == SC_MAPI_IFID) ? SC_IMAP_CODE : SC_KMAP_CODE);		// IMap or KMap
	    SC_CGRegCacheInval(CGP, MapP, 0);					// May have some REG re-assigned in body!
	    // Create a Jump Set for this Lex!
	    JSetR.Flags = SC_JSETANDFLAG | SC_JSETTOPFLAG;
	    JSetR.L[0] = JSetR.L[1] = 0L;
	    JSetR.UpP = CGP->CallerJSetP;					// Could be nested

	    // Define False escape... especially for IMap and BREAK op in loop body!
	    JSetR.L[0] = SC_CGLabelAlloc(CGP, MapP);
	    SC_CGWriteLogicTest(CGP, MapP, NULL, JSetR.L[0], 0);		// False escape if IMAP pushes 0

	    CGP->LastLexP->FalseL = JSetR.L[0];
	    CGP->LastLexP->TrueL = LoopStartI;
	    // Needs a [PopN 4] after the body, write in SC_CGCodePassCleanup.

	    // Set BEG flag for MapI/K in first ByteOp.
	    ((SC_ByteOpPointer)MapP->CodeADP->Data + StartCodeCount)->Flags |= SC_BCBEGMAPFLAG;
	    goto DoneDone;
	}

    CodeCall:
	// FUNC Call, not definition.
	// Either (CALL FuncRef (INPUT...)) or (CALL FuncRef (INPUT...) (OUTPUT...))
	// OUTPUT is optional, as are args in INPUT and OUTPUT.
	//
	// FuncRef can be an internal function (IFID)
	// These are handled by CodeNewArrIFID, etc.
	//
	// NOTE: If (TP->OpState & SC_SYSCALL_STATE), then this is a SYS call and
	//	 should do an SCall.  FuncName will be INDEX of Sys function.
	//
	// NOTE: "FuncName" is really a Var FuncRef... usually global, but may
	//	 also be defined in a superior sub-FUNC.  If not global, then it
	//	 will be CLOSED OVER!!  Ref may also come from DICT or ARR or
	//	 another FUNC call!
	//
	// NOTE: Args (INPUT + OUTPUT) do *NOT* re-assign REG cache, they simply
	//	 PSHG globals if not already in cache!  The FuncRef itself may
	//	 cause a Global/Reg re-assignment.

	ATP = TP->DownP;	// FuncRef
	if (TP->OpState & SC_IFIDCALL_STATE) {
	    // These are IFID!
	    ByteOp = ATP->Data;
	    switch (ByteOp) {
		case SC_MAPI_IFID:
		case SC_MAPK_IFID:
		    goto CodeMapIFID;
		    
		case SC_ARRAY_IFID:
		case SC_DICT_IFID:
		    goto CodeNewArrIFID;

		case SC_INT_IFID:
		    goto CodeIntIFID;
		    
		case SC_FLT_IFID:
		    goto CodeFltIFID;

		case SC_RCP_IFID:
		    goto CodeRCPIFID;

		case SC_RESUME_IFID:
		case SC_RELEASE_IFID:
		    goto CodeResumeIFID;

		case SC_SUBMIT_IFID:
		    goto CodeSubmitIFID;
		    
		default: SC_CODEERROR(CGP, ATP, SC_STR_ErrorBadArg);
	    }
	}

	{
	    Uns16		InCount, OutCount;
	    Int16		ArgsLeft = SC_CGMAXCALLPARAMCOUNT;
	    Uns8		DoUnPack = !!(TP->OpState & SC_UNPACK_STATE);

	    InCount = OutCount = 0;
	    // Init UPS if there are any UNPACK args in IN/OUT lists!
	    if (DoUnPack)
		SC_CGWriteNoArg(CGP, MapP, SC_UPS_CODE);
	    ATP = TP->DownP->NextP->DownP;		// INPUT list if any
	    while (ATP) {
		// These are Copy-by-Value INPUT args, if Konst or Var, push
		// their VALUE.  (If GVar and NOT SET to a Reg, just push its
		// value.)  These can also be Ops or function calls!
		if (ArgsLeft == 0) SC_CODEERROR(CGP, ATP, SC_STR_ErrorLimits);

		switch (ATP->Type) {
		    case SC_STRING_TOKEN:
			Index = SC_CGGetStringIndex(CGP, ATP);
			SC_CHECKERROR(CGP);
			SC_CGWriteI3(CGP, MapP, Index, SC_PSHS_I3_CODE);
			break;

		    case SC_NUMBER_TOKEN:
			SC_CGCodePushTokenNumb(CGP, ATP);
			break;

		    case SC_OPERATOR_TOKEN:
			if (ATP->Id == SC_UNPACK_ID)
			    ATP->Flags |= SC_TOKEN_INPUT_FLAG;		// IN list, not OUT
			if (ATP->Id == SC_NULL_ID) {
			    SC_CGWriteK1(CGP, MapP, 0, SC_PSH_K_CODE);
			    // FUTURE: Want to get a PTR not an INT when function is called!
			    // SC_CGWrite1V(CGP, MapP, {Refer 0}, SC_REF_V_CODE);			    
			    break;
			}
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) return SC_ERR_RES;
			// Fall through...
		    case SC_IDENTIFIER_TOKEN:
			if (ATP->Data != 0LL)	// In a Vex, NOT on stack!
			    SC_CGWriteCallArg(CGP, MapP, (SC_VexPointer)ATP->Data, 0);
			break;

		}
		InCount += 1, ArgsLeft -= 1;
		ATP = ATP->NextP;
	    }

	    // Get optional OUTPUT args (from optional OUTPUT list)
	    if ((ATP = TP->DownP->NextP->NextP)) ATP = ATP->DownP;
	    while (ATP) {
		if (ArgsLeft == 0) SC_CODEERROR(CGP, ATP, SC_STR_ErrorLimits);

		// Copy-by-ref... Pushing Var Refs for OUTPUT args!
		//
		// Can only have NULL, Var, or Arr/Dict (Index and Dot) refs here.
		// Func CANNOT return Refs, so no FUNC Calls!!
		if (ATP->Type == SC_OPERATOR_TOKEN) {
		    if (ATP->Id == SC_NULL_ID) {
			// Just push 0 on the stack!
			SC_CGWriteK1(CGP, MapP, 0, SC_PSH_K_CODE);
		    } else {
			ATP->Flags |= SC_TOKEN_LVALUE_FLAG;
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) return SC_ERR_RES;
			SC_CGWriteCallArg(CGP, MapP, (SC_VexPointer)ATP->Data, 1);
		    }
		} else
		    SC_CGWriteCallArg(CGP, MapP, (SC_VexPointer)ATP->Data, 1);
		
		OutCount += 1, ArgsLeft -= 1;
		ATP = ATP->NextP;
	    }

	    // All parameters are on stack... Call the function.
	    // FuncRef could come from an Operator... Dict ref, call, etc.
	    // SCall gets an Index instead of FuncRef!
    	    ATP = TP->DownP;
	    if (TP->OpState & SC_SYSCALL_STATE) {
		// Sys Call
		SC_CGWriteSCall(CGP, MapP, ATP->Data, (OutCount << 8) + InCount, DoUnPack);

	    } else {
		// Regular Func Call
		if ((ATP->Type == SC_OPERATOR_TOKEN) &&
		    (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)))
		    return SC_ERR_RES;

		SC_CGWriteFCall(CGP, MapP, (SC_VexPointer)ATP->Data, (OutCount << 8) + InCount, DoUnPack);
	    }
	    
	    // All FUNC return one value (Often Int 0) on the stack.
	    if (AssignTP) {
		SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);
		SC_CGWrite1V(CGP, MapP, VArr[0], SC_POP_V_CODE);
	    } else if (NoPush) {
		// Discard result
		SC_CGWriteNoArg(CGP, MapP, SC_POP1_CODE);
	    }
	}

	goto DoneDone;

    CodeFunc:
	// FUNC definition... so FuncLexP will be this LexP!
	// CheckFunc has already registered the Func name (if explicit)
	// as a VAR in the container (FUNC) Lex!
	//
	// "Function MyDict.dog(A)(X, Y) ...." is NOT ALLOWED
	// It should be written as "MyDict.dog = FUNCTION()()..."
	// Therefore, Name cannot be an operator!
	{
	    SC_MapPointer	SupMapP = MapP->SupFLexP->MapP;

	    ATP = TP->DownP;	// Name or NULL
	    if (ATP->Type == SC_IDENTIFIER_TOKEN)
		AssignTP = ATP;
	    // Else NULL Op and *MUST* already have AssignTP
	    SC_CGLoadAssignVar(CGP, SupMapP, &VArr[0], AssignTP, TP);
	    // SUPERIOR FUNC must have an OpCode to make ready a closure.
	    SC_CGWriteVK2(CGP, SupMapP, VArr[0], MapP->FuncId, SC_INDEX, SC_FMAK_VI2_CODE);

	    // First job in FUNC is to load Run frame + init from call params.
	    SC_CGWriteNoArg(CGP, MapP, SC_FBEG_CODE);
	    // SC_CGCodePassCleanup will add FEnd at the very end...
	}
        goto DoneDone;

    CodeSwitch:
	{
	    SC_LexPointer	LexP = CGP->LastLexP;

	    ATP = TP->DownP;
	    VArr[0].Class = SC_STACK; VArr[0].Index = 0;
	    MapP->SwitchCount += 1;
	    if (MapP->SwitchCount >= SC_CGMAXSWITCHCOUNT)
		SC_CODEERROR(CGP, TP, SC_STR_ErrorLimits);

	    // Must stash Switch Id/Index in LEX, SWITCH statements can nest!
	    LexP->Data1 = MapP->SwitchCount;

	    // Make CaseDict if this is first SWITCH!
	    if (! MapP->CaseDictP)
		MapP->CaseDictP = SC_DictCreateNewDict(CGP->VMP, 0);
	    
	    // Defines 2 labels: (1) FalseL for Break and (2) ContL for DEFAULT.
	    // But if no DEFAULT is explicitly defined, ContL will also go to
	    // statement AFTER the SWITCH Lex, as a "NO MATCH" should.
	    // (The SWITCH does not know YET whether it will have a DEFAULT!)
	    LexP->FalseL = SC_CGLabelAlloc(CGP, MapP);
	    LexP->ContL = SC_CGLabelAlloc(CGP, MapP);

	    switch (ATP->Type) {
		case SC_STRING_TOKEN:
		    // Hardwired string.... Switch("dog")... Daft but ok.
		    Index = SC_CGGetStringIndex(CGP, ATP);
		    SC_CHECKERROR(CGP);
		    SC_CGWriteI3(CGP, MapP, Index, SC_PSHS_I3_CODE);
		    break;
		    
		case SC_NUMBER_TOKEN:
		    // Hardwired number... Switch(47)... Daft but ok.
		    SC_CGCodePushTokenNumb(CGP, ATP);
		    break;
		    
		case SC_OPERATOR_TOKEN:			 // Process the Op
		    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
		    // Fall through
		case SC_IDENTIFIER_TOKEN:
		    SC_CGLoadVar(CGP, MapP, &VArr[0], (SC_VexPointer)ATP->Data, 1);
		    break;
	    }

	    // WriteSwch can use SwitchCount, cannot sprout another SWITCH yet!
	    SC_CGWriteSwch(CGP, MapP, VArr[0]);
	    SC_CGWriteBranch(CGP, MapP, LexP->ContL, SC_JMP_K3_CODE);	// To DEFAULT!
	    // SC_CGCodePassCleanup will define FalseL (and ContL if no DEFAULT).  
	}
	goto DoneDone;

    CodeCase:
	// Cases do not have LEX of their own, they run in the SWITCH Lex.  Just
	// record the current ByteCode in the CaseDict so a match at runtime can
	// JMP to the code in the CASE.  SWITCH Id is stashed in Lex, DO NOT
	// assume SwitchCount as Id, as a previous CASE may have had a nested
	// SWITCH and incremented SwitchCount...
	//
	// Default case does NOT go into the CaseDict, SWITCH has an explicit
	// label (and JMP) for it.
	//
	// Must invalidate REG cache BEFORE all cases, EXCEPT first.  Also after
	// whole SWITCH (in SC_CGCodePassCleanup), in case DEFAULT was taken...
	//
	// NOTE: Every function that has 1 or more Switch statements will get a
	//	 CaseDict.  In the case of strings, they are stashed ONLY in the
	//	 very TOP func and CaseDict refer to their StrHP, not INDEX!
	//
	{
	    SC_LexPointer	LexP = CGP->LastLexP;
	    SC_CaseHPointer	CaseP;
	    Uns32		Id = LexP->Data1;
	    Int16		IsInt;
	    Uns16		PairFlag;

	    if (LexP->Flags & SC_LEXNEXTCASEFLAG)
	    	SC_CGRegCacheInval(CGP, CGP->FuncLexP->MapP, 0);
	    else
		LexP->Flags |= SC_LEXNEXTCASEFLAG;

	    ATP = TP->DownP;
	    if (TP->Id == SC_DEFAULT_ID) {
		// Doing default case, no args!  Not in CaseDict!
		// Seen before?
		if (SC_CGLabelGet(MapP, LexP->ContL))
		    SC_CODEERROR(CGP, TP, SC_STR_ErrorBadDefault);
		    
		SC_CGLabelDefine(MapP, LexP->ContL, MapP->CodeCount);

	    } else {
		// Doing a CASE, put in CaseDict!
		switch (ATP->Type) {
		    Uns32	Index;

		    case SC_STRING_TOKEN:
			Index = SC_CGGetStringIndex(CGP, ATP);
			// Adds to MapP StringADP and StringDictP!
			SC_CHECKERROR(CGP);
			PairFlag = SC_VAL_PTR;
			ATP->Data = (Uns64)Index;
			break;

		    case SC_NUMBER_TOKEN:
			IsInt = (ATP->Id == SC_INTNUMB_ID);
			PairFlag = (IsInt) ? SC_VAL_INT : SC_VAL_FLT;
			break;
		}

		// Seen before?
		if (SC_DictGetCase(MapP->StringADP, MapP->CaseDictP, ATP->Data, Id, PairFlag))
		    SC_CODEERROR(CGP, TP, SC_STR_ErrorIdentCase);

		// Now enter in CaseDictP to record it!
		CaseP = (SC_CaseHPointer)SC_BigEntryNew(CGP->VMP);
		CaseP->Type = SC_ENTRY_CASE;
		CaseP->KeyType = PairFlag;
		CaseP->ValType = SC_VAL_CASE;
		CaseP->Key = ATP->Data;			// NUMBER for this CASE
		CaseP->Id = Id;				// ID of this SWITCH
		CaseP->Value = MapP->CodeCount;		// HERE in ByteCodes!!
		SC_DictStashNewPair(CGP->VMP, MapP->CaseDictP, (SC_PairHPointer)CaseP);
	    }
	}
	goto DoneDone;

    CodeDef:
	// Stash Def assignments... must be optimized to a single CONST by now.

	goto DoneDone;

    CodeVar:
	// Only need to handle assignment here as CheckVar handles variable
	// allocation/registry.  (Check phase will only allow EQ and ALIAS here.)

	ATP = TP->DownP;
	while (ATP) {
	    if (ATP->Type == SC_OPERATOR_TOKEN) {	// Assume EQ
		if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL))
		    goto ErrorOut;
	    }

	    ATP = ATP->NextP;
	}
	goto DoneDone;

    CodeLabel:
	// SC_LABEL_ID does not generate any code... The location/offset is
	// stashed by the CheckCode when the Label is seen and defined.
	//
	// The REG cache has to be invalidated, as a GOTO could jump here.
	SC_CGRegCacheInval(CGP, CGP->FuncLexP->MapP, 0);
 	SC_DEBUG_LABELPRINT(CGP, TP->DownP);
	goto DoneDone;

    CodeGoto:
	// Just care about Index for now... May not have defined Offset yet!
	// Leave a NOOP before Jmp, room for POPN_K if leaving MapI/K.	
	SC_CGGetLabel(CGP, TP->DownP, &Index);
	SC_CGWriteNoOp(CGP, MapP);
	SC_CGWriteBranch(CGP, MapP, Index, SC_JMP_K3_CODE);
	((SC_ByteOpPointer)MapP->CodeADP->Data + MapP->CodeCount - 1)->Flags |= SC_BCGOTOFLAG;
	goto DoneDone;

    CodeBreak:
	{
	    SC_LexPointer	LexP = CGP->LastLexP;

	    LexP = SC_CGUnNestFinLex(LexP, SC_LEXLOOPFLAG | SC_LEXSWITCHFLAG);
	    SC_CGWriteBranch(CGP, MapP, LexP->FalseL, SC_JMP_K3_CODE);
	    // NOTE: LexP->FalseL for a MapI/K block goes to a PopN 4 which gets
	    //	     rid of the RunStack entries for the Map block!
	}
	goto DoneDone;

    CodeCont:
	// NOTE: Normally JMP to LoopStart recorded in LexP->TrueL.
	//	 BUT JMP to ContL clause if FOR or DO!
	{
	    SC_LexPointer	LexP = CGP->LastLexP;

	    LexP = SC_CGUnNestFinLex(LexP, SC_LEXLOOPFLAG);
	    SC_CGWriteBranch(CGP, MapP, (LexP->ContL) ? LexP->ContL : LexP->TrueL, SC_JMP_K3_CODE);
	}
	goto DoneDone;

    CodeDo:
	// Loop starts here!  Alloc and Define LoopStart (TrueL).
	// Alloc FalseL (F Escape) and ContL (for CONTINUE).
	CGP->LastLexP->TrueL = SC_CGLabelAlloc(CGP, MapP);
	SC_CGLabelDefine(MapP, CGP->LastLexP->TrueL, MapP->CodeCount);
	CGP->LastLexP->FalseL = SC_CGLabelAlloc(CGP, MapP);
	CGP->LastLexP->ContL = SC_CGLabelAlloc(CGP, MapP);

	// Ready for BODY of loop... must invalidate REC cache, because jump here!
	SC_CGRegCacheInval(CGP, MapP, 0);
	goto DoneDone;

    CodeDoCond:
	{
	    SC_JSetRecord	JSetR;

	    // OR logic works better here... as TRUE Escape will jump to loop start!
	    // So DO NOT set JSETANDFLAG!  (Only have AND flag, 1 for AND, 0 for OR.)
	    JSetR.Flags = SC_JSETTOPFLAG;
	    JSetR.UpP = CGP->CallerJSetP;
	    JSetR.L[0] = CGP->LastLexP->FalseL;		// False escape, after tests...
	    JSetR.L[1] = CGP->LastLexP->TrueL;		// Start of DO Loop!

	    // CONTINUE should JMP here... start of the COND test!
	    SC_CGLabelDefine(MapP, CGP->LastLexP->ContL, MapP->CodeCount);

	    ATP = TP->DownP;
	    switch (ATP->Type) {
		// Loop logic is a little different.  Strings go to default in
		// this switch...

		case SC_NUMBER_TOKEN:
		    if (! SC_CGIsIntTP(ATP, 0LL))	// "While(0)" falls OUT!
			SC_CGWriteBranch(CGP, MapP, JSetR.L[1], SC_JMP_K3_CODE);
		    break;

		case SC_OPERATOR_TOKEN:
		    if ((ATP->Id == SC_LOGOR_ID) || (ATP->Id == SC_LOGAND_ID)) {
			CGP->CallerJSetP = &JSetR;
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
		    } else {
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
			SC_CGWriteLogicTest(CGP, MapP, (SC_VexPointer)ATP->Data, JSetR.L[1], 1);
		    }
		    break;

		case SC_IDENTIFIER_TOKEN:
		    SC_CGWriteLogicTest(CGP, MapP, (SC_VexPointer)ATP->Data, JSetR.L[1], 1);
		    break;

		default:				// Everything else is "while(1)".
		    SC_CGWriteBranch(CGP, MapP, JSetR.L[1], SC_JMP_K3_CODE);
		    break;
	    }

	    // False Escape comes here... after everything!
	    SC_CGLabelDefine(MapP, JSetR.L[0], MapP->CodeCount);
	}
	goto DoneDone;

    CodeLoop:
	// Handles WHILE and FOR!  (IsForLoop)
	//
	// (WHILE COND)
	// Very similar to CodeIf.  False escape exits the loop, which includes
	// a JMP back to the beginning test after the last Op.  All instances
	// are constructed the same way, even "while(0)".
	//
	// (FOR INIT COND UPDATE)
	// Similar to While, but has INIT + UPDATE clauses too in addition to
	// COND.  Since UPDATE will conceptually follow the as-of-yet unseen
	// body of the for loop, it can be handled in two ways:
	//
	// a) Store the UPDATE AST in the Lex, compile it after the BODY.
	//
	// b) Refactor the loop, start it with INIT+UPDATE+COND, then BODY.
	//    (Would require a JUMP to COND after INIT... this will in turn
	//    require an additional REG cache invalidation in COND.
	//
	// Approach (a) is used here...
	{
	    SC_JSetRecord	JSetR;
	    Uns32		LoopStartI;	// JMP back to UPDATE

	    LoopStartI  = SC_CGLabelAlloc(CGP, MapP);
	    ATP = TP->DownP;

	    if (IsForLoop) {
		// Do the INIT first...  INIT is NOPUSH (from Check phase)!
		if ((ATP->Type == SC_OPERATOR_TOKEN) && (ATP->Id != SC_NULL_ID)) {
		    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
		}
		// Now COND... INIT (if there) will fall through to here.
		ATP = ATP->NextP;
	    }
	    SC_CGLabelDefine(MapP, LoopStartI, MapP->CodeCount);
	    SC_CGRegCacheInval(CGP, MapP, 0);

	    JSetR.Flags = SC_JSETANDFLAG | SC_JSETTOPFLAG;
	    JSetR.L[0] = JSetR.L[1] = 0L;
	    JSetR.UpP = CGP->CallerJSetP;			// Could be Nested!

	    // Define False escape... especially for BREAK op in loop!
	    JSetR.L[0] = SC_CGLabelAlloc(CGP, MapP);		// FALSE escape!
	    // COND is usually an OP condition.... but can be a number, string, or just NULL!!
	    switch (ATP->Type) {
		case SC_STRING_TOKEN:
		    // All strings mean TRUE... fall through!
		    break;

	        case SC_NUMBER_TOKEN:
		    if (SC_CGIsIntTP(ATP, 0LL))
		        SC_CGWriteBranch(CGP, MapP, JSetR.L[0], SC_JMP_K3_CODE);
		     // Otherwise, everything else is TRUE... fall through!
		    break;

		case SC_OPERATOR_TOKEN:
		    if (ATP->Id != SC_NULL_ID) {
			// FOR can have a NULL Cond... means keep going!
			if ((ATP->Id == SC_LOGOR_ID) || (ATP->Id == SC_LOGAND_ID)) {
			    CGP->CallerJSetP = &JSetR;
			    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
			    // May define TRUE escape in JSetR[1]... IF COND had OR op!
			    if (JSetR.L[1]) SC_CGLabelDefine(MapP, JSetR.L[1], MapP->CodeCount);

			} else {
			    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
			    SC_CGWriteLogicTest(CGP, MapP, (SC_VexPointer)ATP->Data, JSetR.L[0], 0);
			}
		    }
		    break;

		case SC_IDENTIFIER_TOKEN:
		    SC_CGWriteLogicTest(CGP, MapP, (SC_VexPointer)ATP->Data, JSetR.L[0], 0);
		    break;
	
		default: break;
	    }
	    CGP->LastLexP->FalseL = JSetR.L[0];
	    CGP->LastLexP->TrueL = LoopStartI;

	    if (IsForLoop) {
		// Stash the UPDATE AST, and remove from arglist.
		if ((ATP->NextP->Type == SC_OPERATOR_TOKEN) && (ATP->NextP->Id != SC_NULL_ID)) {
		    CGP->LastLexP->StashTP = ATP->NextP;	// Otherwise NULL
		    ATP->NextP = NULL;
		    // CONTINUE should go to UPDATE instead of LoopStart!
		    CGP->LastLexP->ContL = SC_CGLabelAlloc(CGP, MapP);
		}
		// SC_CGCodePassCleanup will compile UPDATE section after BODY... define ContL too!
	    }
	}
	goto DoneDone;

    CodeIf:
	// The False Escape label of an IF may *SOMETIMES* be combined with its
	// superior IF/ELSE.  This is not done here, as the minimal savings are
	// only in the compiler and have NO IMPACT on generated code--ASM phase
	// will combine JMPs to JMP ops.
	{
	    SC_JSetRecord	JSetR;
	    Int16		IsOr = 0;

	    JSetR.Flags = SC_JSETANDFLAG | SC_JSETTOPFLAG;
	    JSetR.L[0] = JSetR.L[1] = 0L;
	    JSetR.UpP = CGP->CallerJSetP;			// Could be Nested!

	    ATP = TP->DownP;					// Condition of If
	    switch (ATP->Type) {
		case SC_NUMBER_TOKEN:
		     if (SC_CGIsIntTP(ATP, 0LL)) {
			JSetR.L[0] = SC_CGLabelAlloc(CGP, MapP);
			SC_CGWriteBranch(CGP, MapP, JSetR.L[0], SC_JMP_K3_CODE);	// JMP instead of BRF
		     }
		     // Otherwise "if(1)"... nothing to test, just fall through!
		     break;
		
		case SC_OPERATOR_TOKEN:
		    if ((IsOr = (ATP->Id == SC_LOGOR_ID)))
			JSetR.L[1] = SC_CGLabelAlloc(CGP, MapP);			// True Escape

		    if (IsOr || (ATP->Id == SC_LOGAND_ID)) {
			JSetR.L[0] = SC_CGLabelAlloc(CGP, MapP);			// False Escape
			CGP->CallerJSetP = &JSetR;
			// Process the logical condition... it will assume it is within an AND.
			// Will have F (and possibly T) Escape, but will not DEFINE the T and F location.
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
			// May define TRUE escape in JSetR[1]... e.g. if Cond had OR op.			
			if (IsOr) SC_CGLabelDefine(MapP, JSetR.L[1], MapP->CodeCount);
			
		    } else {
			// Process the non-logical op, ATP->Data says where result will be.
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
			// Add in the Escape to F if result is 0.
			JSetR.L[0] = SC_CGLabelAlloc(CGP, MapP);			
			SC_CGWriteLogicTest(CGP, MapP, (SC_VexPointer)ATP->Data, JSetR.L[0], 0);
		    }
		    break;
		
		case SC_IDENTIFIER_TOKEN:
		    JSetR.L[0] = SC_CGLabelAlloc(CGP, MapP);		// Need a False Escape
		    SC_CGWriteLogicTest(CGP, MapP, (SC_VexPointer)ATP->Data, JSetR.L[0], 0);
		    break;

		default: break;		// Everything else becomes "if(1)".
	    }

	    // Tell the Lex for this IF about this JSet... Only False escape will be needed.
	    CGP->LastLexP->FalseL = JSetR.L[0];
	}
	// When this IF eventually ends, SC_CGCodePassCleanup will Define its FalseL.	
	goto DoneDone;

    CodeElse:
	// An ELSE must start with a JMP over itself so the IF clause will
	// skip it... So FalseL is used to store the DoneL label!
	CGP->LastLexP->FalseL = SC_CGLabelAlloc(CGP, MapP);
	SC_CGWriteBranch(CGP, MapP, CGP->LastLexP->FalseL, SC_JMP_K3_CODE);

	// When this ELSE eventually ends, SC_CGCodePassCleanup will Define its FalseL.
	goto DoneDone;

    CodeLogic:
	// X = A && B...	Supports (LOGAND A B C D....) multiple args
	//
	//		Psh_V	A
	//		BrF_K3	FalseL (AND escapes to FalseL)
	//     NextL:	Psh_V	B
	//		BrF_K3	FalseL
	//		...
	//     TrueL:   Mov_VK2 X, 1   (Just Psh_K if no X AssignTP)
	//		Jmp_K	DoneL
	//     FalseL:  Mov_VK2 X, 0   (Just Psh_K if no X AssignTP)
	//     DoneL:	...
	//
	// X = A || B...	Supports (LOGOR A B C D....)
	//
	//		Psh_V	A
	//		BrT_K3	TrueL  (OR escapes to TrueL)
	//     NextL:   Psh_V   B
	//		BrT_K3	TrueL
	//		...
	//     FalseL:  Mov_VK2 X, 0   (Just Psh_K if no X AssignTP)
	//		Jmp_K	DoneL
	//	TrueL:  Mov_VK2 X, 1
	//	DoneL:  ...
	//
	// Rely on Xform process to:
	//   a) Move NOT into AND and OR, so never have NOT-AND or NOT-OR,
	//   b) Always transform NOT-NOT,
	//   c) Always remove numbers from AND/OR/NOT logical ops,
	//   d) Always flatten AND ops within an AND and OR ops within an OR.
	//
	// AND immediately escapes to FalseL while OR immediately escapes to
	// TrueL!  Br/Escape logic is often REVERSED for the last elt in a
	// sub-sequence to avoid null branches (branching to the very next
	// instruction) or extra jumps, for example C in "(&& A (|| B C) D)".
	//
	// (Top Op can also be LOGPOS or LOGNEG... both handled by CodePolar.)

	{
	    SC_JSetRecord	JSetR;			// Jmp/Br labels
	
	    Int16		IsTop;			// Root Log Op
	    Int16		IsMidSet;		// Embedded Cond op, BUT NOT LAST
	    Int16		EscTest;		// 0 or 1 Test logic for escape branch
	    Int16 		Esc;			// 0 or 1 index into JSetR.L
	    Uns32		EscapeL;
	    
	    // Set up the JSet first... and housekeeping

	    JSetR.Flags = SC_JSETNOFLAG;
	    JSetR.L[0] = JSetR.L[1] = 0L;
	    JSetR.UpP = NULL;
	    if (IsAnd)
		JSetR.Flags |= SC_JSETANDFLAG;
	    EscTest = Esc = !IsAnd;				// AND escapes to FalseL, OR to TrueL
	    
	    if ((IsTop = ((JSetR.UpP = CGP->CallerJSetP) == NULL))) {
		IsMidSet = 0;
		JSetR.Flags |= SC_JSETTOPFLAG;
		JSetR.L[Esc] = SC_CGLabelAlloc(CGP, MapP);	// Allocate EscapeL for now
	    } else {
		if ((IsMidSet = !!(CGP->Flags & SC_CGLOGOPISMIDFLAG)))
		    JSetR.Flags |= SC_JSETMIDFLAG;
	    }

	    // Now go through the logic args (sequence) at this level...
	    ATP = TP->DownP;
	    while (ATP) {
		// Loop through the args... Previously have eliminated numbers from Logic op.
		
		if (ATP->Type == SC_IDENTIFIER_TOKEN) {
		    EscapeL = SC_CGFindLogicEscape(&JSetR, &EscTest, Esc, (ATP->NextP == NULL));
		    SC_CGWriteLogicTest(CGP, MapP, (SC_VexPointer)ATP->Data, EscapeL, EscTest);

		} else if (ATP->Type == SC_OPERATOR_TOKEN) {
		    if (ATP->Id == SC_LOGNEG_ID) {
			// Arg can only be Ident or non-LOGIC Op.
			if (ATP->DownP->Type == SC_IDENTIFIER_TOKEN) {
			    // Invert the Br test (ContLogic instead of EscLogic)
			    EscapeL = SC_CGFindLogicEscape(&JSetR, &EscTest, Esc, (ATP->NextP == NULL));
			    SC_CGWriteLogicTest(CGP, MapP, (SC_VexPointer)ATP->DownP->Data, EscapeL, ! EscTest);

			} else if (ATP->DownP->Type == SC_OPERATOR_TOKEN) {
			    // Not a LOG op, so clear out JSet, then process Arg Op first.
			    CGP->CallerJSetP = NULL;
			    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP->DownP, NULL)) goto ErrorOut;
			    // Result may already be on Stack... or local or global!  Inverted Br test.
			    EscapeL = SC_CGFindLogicEscape(&JSetR, &EscTest, Esc, (ATP->NextP == NULL));
			    SC_CGWriteLogicTest(CGP, MapP, (SC_VexPointer)ATP->DownP->Data, EscapeL, ! EscTest);
			}

		    } else if ((ATP->Id == SC_LOGOR_ID) || (ATP->Id == SC_LOGAND_ID)) {
			// Must be the other logical Op!  
			if (ATP->NextP) {
			    CGP->Flags |= SC_CGLOGOPISMIDFLAG;
			    JSetR.L[!Esc] = SC_CGLabelAlloc(CGP, MapP);		// Define Other label for sub!
			} else {
			    CGP->Flags &= ~SC_CGLOGOPISMIDFLAG;
			    if (IsTop && !JSetR.L[!Esc])
				JSetR.L[!Esc] = SC_CGLabelAlloc(CGP, MapP);	// Define !Esc for Top, only once!
			}
			CGP->CallerJSetP = &JSetR;
			    
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
			if (ATP->NextP) {
			    // The label is referenced in CGCodePass... so define it here,
			    // then clear it out, ready for the next Op, if any.
			    SC_CGLabelDefine(MapP, JSetR.L[!Esc], MapP->CodeCount);
			    JSetR.L[!Esc] = 0L;
			}

		    } else {
			// Not a LOG op, so clear out JSet, then process Arg Op first.
			CGP->CallerJSetP = NULL;
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
			// Result may already be on Stack... or local or global!
			EscapeL = SC_CGFindLogicEscape(&JSetR, &EscTest, Esc, (ATP->NextP == NULL));
			SC_CGWriteLogicTest(CGP, MapP, (SC_VexPointer)ATP->Data, EscapeL, EscTest);
		    }
		}

		ATP = ATP->NextP;
	    }

	    // Termination: define the referenced labels and set values!
	    if (IsTop) {
		Uns32	DoneL = SC_CGLabelAlloc(CGP, MapP);

		// ContinueL if referenced.
		if (JSetR.L[!Esc]) SC_CGLabelDefine(MapP, JSetR.L[!Esc], MapP->CodeCount);
		SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);
		SC_CGWriteMovK(CGP, MapP, VArr, IsAnd);		// Mov or Psh
		
		SC_CGWriteBranch(CGP, MapP, DoneL, SC_JMP_K3_CODE);		// Jmp to DoneL

		// EscapeL: Always ref'ed--used as escape.
		SC_CGLabelDefine(MapP, JSetR.L[Esc], MapP->CodeCount);
		SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);
		SC_CGWriteMovK(CGP, MapP, VArr, !IsAnd);

		// Falls through to DoneL;
		SC_CGLabelDefine(MapP, DoneL, MapP->CodeCount);
	    }
	}
	goto DoneDone;

    CodeComma:
	// Do the COMMA version of SC_CGCodePassAllArgs!  All but the last Arg
	// NORMALLY get NOPUSH while last Arg gets indicated AssignTP.  But if
	// COMMA itself is NOPUSH, so is the last Arg (and no Assign).
	ATP = TP->DownP;
	while (ATP) {
	    if (ATP->NextP) {
		// Not last Arg!
		if (ATP->Type == SC_OPERATOR_TOKEN) {
		    ATP->Flags |= SC_TOKEN_NOPUSH_FLAG;
		    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL))
			goto ErrorOut;
		}

	    } else {
		// LAST arg!

		switch (ATP->Type) {
		    case SC_STRING_TOKEN:
			Index = SC_CGGetStringIndex(CGP, ATP);
			SC_CHECKERROR(CGP);
			if (AssignTP) {
			    SC_CGWriteI3(CGP, MapP, Index, SC_PSHS_I3_CODE);
			    SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);
			    if (VArr[0].Class != SC_STACK)
				SC_CGWrite1V(CGP, MapP, VArr[0], SC_POP_V_CODE);
			} else if (! NoPush)
			    SC_CGWriteI3(CGP, MapP, Index, SC_PSHS_I3_CODE);
			break;

		    case SC_NUMBER_TOKEN:
			// Does a simple Push if no AssignTP
			SC_CGCodeAssignNumb(CGP, ATP, AssignTP, TP, 0);
			break;

		    case SC_OPERATOR_TOKEN:
			ATP->Flags |= (TP->Flags & SC_TOKEN_NOPUSH_FLAG);
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, AssignTP)) goto ErrorOut;
			TP->Data = ATP->Data;
			TP->Flags |= (ATP->Flags & SC_TOKEN_ASSIGN_FLAG);
			// Will have caller do the assignment if ATP DID NOT.
			break;

		    case SC_IDENTIFIER_TOKEN:
			// Let caller assign if it wants... result *IS* the Identifier
			// (Xform will eliminate this case if COMMA is NoPush!)
			TP->Flags |= SC_TOKEN_ASSIGN_FLAG;
			TP->Data = ATP->Data;
			break;
		}
	    }

	    ATP = ATP->NextP;
	}
	goto DoneDone;

    CodeAlias:
	// L-Side: Will be Identifier and local *OR* Array Elt
	ATP = TP->DownP;
	if (ATP->Type == SC_OPERATOR_TOKEN) {
	    Uns8	IsGlob = 0;
	    
	    // Only INDEX can make it through CheckAlias!
	    // Get the array first
	    ATP = ATP->DownP;
	    if (ATP->Type == SC_IDENTIFIER_TOKEN)
		SC_CGLoadVar(CGP, MapP, &VArr[0], (SC_VexPointer)ATP->Data, 1);
	    else if (ATP->Type == SC_OPERATOR_TOKEN) {
		if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
		SC_CGLoadVar(CGP, MapP, &VArr[0], (SC_VexPointer)ATP->Data, 1);
	    } else
		goto ErrorOut;

	    // Get the index next
	    ATP = ATP->NextP;
	    if (ATP->Type == SC_NUMBER_TOKEN) {
		if (ATP->Id == SC_INTNUMB_ID) {
		    SC_CGCodePushInt(CGP, ATP->Data);			
     		    VArr[1].Class = SC_STACK; 
 		} else
		    goto ErrorOut;

	    } else if (ATP->Type == SC_IDENTIFIER_TOKEN) {
		SC_CGLoadVar(CGP, MapP, &VArr[1], (SC_VexPointer)ATP->Data, 1);

	    } else if (ATP->Type == SC_OPERATOR_TOKEN) {
		if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
		SC_CGLoadVar(CGP, MapP, &VArr[1], (SC_VexPointer)ATP->Data, 1);

	    } else
		goto ErrorOut;

	    // R-Side: Get the new Ref, can be Var, Arr elt, or Dict Pair!)
	    ATP = TP->DownP->NextP;
	    if (ATP->Type == SC_OPERATOR_TOKEN) {
		// INDEX or DOT only.. generate an IREF not IGET!!
		ATP->Flags |= SC_TOKEN_LVALUE_FLAG;
		if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
	    } else if (ATP->Type != SC_IDENTIFIER_TOKEN)
		SC_CODEERROR(CGP, ATP, SC_STR_ErrorBadArg);

	    // Result can be already on stack (must be a Ref), a local, or a global!
	    // If global, DO NOT stash in Reg if not already in, instead push Ref on stack!
	    if (! SC_CGLoadVar(CGP, MapP, &VArr[2], (SC_VexPointer)ATP->Data, 0)) {
		// Here only if Global and NOT in RegCache!
		IsGlob = 1;
		SC_CGWriteI3(CGP, MapP, (*(SC_VexPointer)ATP->Data).Index, SC_REFG_I3_CODE);
		VArr[2].Class = SC_STACK;
	    }
	    SC_CGWrite3V(CGP, MapP, VArr, SC_ISET_VVV_CODE, 0);	// No SSS version

	    if (! (TP->Flags & SC_TOKEN_NOPUSH_FLAG)) {
		// Caller needs the Value... for assignment or calculation...
		// but did not get a Value from the alias, only a ref!

		// Will be on Stack
		TP->Data = 0LL;
		TP->Flags |= SC_TOKEN_ASSIGN_FLAG;
		
		if (IsGlob) {
		    // R-size of Alias was global, but NOT in Reg!  Push its VALUE.
		    SC_CGWriteI3(CGP, MapP, (*(SC_VexPointer)ATP->Data).Index, SC_PSHG_I3_CODE);

		} else if ((VArr[2].Class == SC_LVEX) || (VArr[2].Class == SC_RVEX)) {
		    // Just put Local/Reg Vex in TP->Data!  No need to push on stack.
		    TP->Data = ATP->Data;

		} else {
		    // R-Side is Index/Dot!  Have to index again, but for VALUE, not Ref!
		    ATP->Flags &= ~SC_TOKEN_LVALUE_FLAG;
		    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
		    // No need to check, result will be stack!
		}
	    }
	    
	    goto DoneDone;
	}

	// L-Side of Alias is local var if we get here!
	if (ATP->Type == SC_IDENTIFIER_TOKEN) {
	    VArr[0].Class = (*(SC_VexPointer)ATP->Data).Class;
	    VArr[0].Index = (*(SC_VexPointer)ATP->Data).Index;
	} else
	    goto ErrorOut;

	// R-Side: Either INDEX/DOT or Identifier!
	ATP = ATP->NextP;
	if (ATP->Type == SC_OPERATOR_TOKEN) {
	    // INDEX or DOT only.. generate an IREF not IGET!!
	    ATP->Flags |= SC_TOKEN_LVALUE_FLAG;
	    if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) return SC_ERR_RES;
	} else if (ATP->Type != SC_IDENTIFIER_TOKEN)
	    SC_CODEERROR(CGP, ATP, SC_STR_ErrorBadArg);

	// Push a REF to it... local or global!
	SC_CGWriteCallArg(CGP, MapP, (SC_VexPointer)ATP->Data, 1);
	SC_CGWrite1V(CGP, MapP, VArr[0], SC_VSET_V_CODE);

	// Result VALUE will be same as value of L-side!
	// Also tell caller to do own assign if it wants one!
	TP->Data = TP->DownP->Data;
	TP->Flags |= SC_TOKEN_ASSIGN_FLAG;
	goto DoneDone;

    CodeAssign: {
	// Refer to "Assignment Cascade" in design notes!

	AssignTP = TP->DownP;			// AssignTP is L side
	ATP = AssignTP->NextP;			// ATP is R side
	if (AssignTP->Type == SC_OPERATOR_TOKEN) {
	    AssignTP->Flags |= SC_TOKEN_LVALUE_FLAG;
	    if (SC_CGIsOpTP(ATP, SC_FUNC_ID)) {
		// This Lex is for FUNC Definition... it *IS* FuncLexP!!
		// DO not Code the FuncRef here... as formal param have already
		// been defined as locals, and may overshadow var names used in
		// Dict/Arr index!
		CGP->FuncLexP = MapP->SupFLexP;
		CGP->LastLexP = FuncLexP->SupP;
		    if (SC_ERR_RES == SC_CGCodePass(CGP, AssignTP, NULL)) goto ErrorOut;
		CGP->LastLexP = CGP->FuncLexP = FuncLexP;
	    } else {
		// Writes out REFER if AssignTP is INDEX/DOT
		if (SC_ERR_RES == SC_CGCodePass(CGP, AssignTP, NULL)) goto ErrorOut;
		// Pass NoPush this way down to CodeMath/Polar/BitOp/etc... so it knows NoPush *IF* it assigns!!
		if (NoPush)
		    AssignTP->Flags |= SC_TOKEN_NOPUSH_FLAG;
	    }
	}

	// Look at R side..
	switch (ATP->Type) {
	    case SC_STRING_TOKEN:
		Index = SC_CGGetStringIndex(CGP, ATP);
		SC_CHECKERROR(CGP);
		SC_CGWriteI3(CGP, MapP, Index, SC_PSHS_I3_CODE);
		SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);
		// Do nothing if storing to STACK, it is already there!
		if (VArr[0].Class == SC_REFER) {
		    // Put VAL in REFER, VAL replaces REFER on stack
		    SC_CGWriteNoArg(CGP, MapP, SC_PUTV_CODE);
		    if (NoPush) SC_CGWriteNoArg(CGP, MapP, SC_POP1_CODE);
		} else if (VArr[0].Class != SC_STACK)
		    SC_CGWrite1V(CGP, MapP, VArr[0], SC_POP_V_CODE);
		break;

	    case SC_NUMBER_TOKEN:
		SC_CGCodeAssignNumb(CGP, ATP, AssignTP, TP, NoPush);
		break;

	    case SC_OPERATOR_TOKEN:
		if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, AssignTP)) goto ErrorOut;
		TP->Data = ATP->Data;		// In case it did it!
		if (! (ATP->Flags & SC_TOKEN_ASSIGN_FLAG)) break;
		// Fall through
	    case SC_IDENTIFIER_TOKEN:
		SC_CGLoadVar(CGP, MapP, &VArr[1], (SC_VexPointer)ATP->Data, 1);
		SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);
		if (VArr[0].Class == SC_STACK)
		    SC_CGWrite1V(CGP, MapP, VArr[1], SC_PSH_V_CODE);
		else
		    SC_CGWriteMovVV(CGP, MapP, VArr, NoPush);
		break;
	}

	// Tell caller to do own assign if it wants another
	TP->Flags |= SC_TOKEN_ASSIGN_FLAG;
	goto DoneDone;
    }

    CodeInc:
	// PLUSPLUS, MINMIN, PREPLUSPLUS, PREMINMIN
	// Inc/Dec operators cannot also handle another assignment.  So (B = --A)
	// does the --A, then tells caller to do the assignment.
	//
	// ++A	-->  A = A + 1;		--A  -->  A = A - 1;
	// A++	-->  Push(A), A = A+1;  A--  -->  Push(A), A = A-1;
	//
	// POST inc/dec is converted to (simpler) PRE if NoPush!!
	// NoPush happens upstread if there is a (COMMA A++ X Y Z) type call which
	// would discard the result of A++, or A++ is a solo statement.  In
	// both cases A++ is executed for the side-effect and the value ignored.
	//
	// More complex for INDEX/DOT operands (REFERs)
	// ++(A[I]) -->  IREF, PSH, GETV, ADD+1 (Val to stack), PUTV
	//  if NO PUSH	 IREF, PSH, GETV, ADD+1 (Val to  REFER)!
	//
	// A[I]++ -->	 IREF, PSH, GETV, SWAP, PSH, GETV, Add+1 (Val to REFER)

	// Process the R side... only 1 Arg, L Side is same as R!
	ATP = TP->DownP;
	if (ATP->Type == SC_OPERATOR_TOKEN)	// Doing INDEX/DOT... so need a REFER!
	    ATP->Flags |= SC_TOKEN_LVALUE_FLAG;

	if (SC_ERR_RES == SC_CGCodePassAllArgs(CGP, TP, 0)) goto ErrorOut;
	SC_CGLoadVar(CGP, MapP, &VArr[1], (SC_VexPointer)ATP->Data, 1);
	VArr[0] = VArr[1];
	VArr[2].Class = SC_KONST;
	VArr[2].Index = 1;			// 1 Byte
	// Will not do separate Assign, default result location
	TP->Data = ATP->Data;
	TP->Flags |= SC_TOKEN_ASSIGN_FLAG;

	// Simple heuristic... if value is NOT wanted, just do PRE!!
	if (NoPush) DoPost = 0;

	if (TP->Data == 0LL) {
	    // Dealing with a REFER (INDEX/DOT), already on stack
	    VArr[0].Class = SC_REFER;
	    
	    SC_CGWriteNoArg(CGP, MapP, SC_PSH_CODE);				// REF REF
	    SC_CGWriteNoArg(CGP, MapP, SC_GETV_CODE);				// REF VAL
	    if (DoPost) {
		SC_CGWriteNoArg(CGP, MapP, SC_SWAP_CODE);			// VAL REF
		SC_CGWriteNoArg(CGP, MapP, SC_PSH_CODE);			// VAL REF REF
		SC_CGWriteNoArg(CGP, MapP, SC_GETV_CODE);			// VAL REF VAL
		SC_CGWriteMathVVK(CGP, MapP, SC_ADD_VVV_CODE, VArr, IncVal);	// VAL	(REFER <= NewVal)
		// *** WILL NEVER DO NoPUSH with DoPost ****
		// if (NoPush) SC_CGWriteNoArg(CGP, MapP, SC_POP1_CODE);		// EMPTY STACK
	    } else {
		if (NoPush) {
		    SC_CGWriteMathVVK(CGP, MapP, SC_ADD_VVV_CODE, VArr, IncVal);	// EMPTY STACK  (REFER <= NewVal)
		} else {
		    VArr[0].Class = SC_STACK;						// Put NewVal on stack!
		    SC_CGWriteMathVVK(CGP, MapP, SC_ADD_VVV_CODE, VArr, IncVal);	// REF NewVAL
		    SC_CGWriteNoArg(CGP, MapP, SC_PUTV_CODE);				// NewVAL	(REFER <- NewVal)
	       }
	    }

	} else {
	    // Regular Var!
	    if ((DoPost) && !NoPush) {
		SC_CGWrite1V(CGP, MapP, VArr[1], SC_PSH_V_CODE);
		TP->Data = 0LL;			// Result is on Stack!
	    }
	    VArr[2].Class = SC_KONST;
	    VArr[2].Index = 1;			// 1 Byte
	    SC_CGWriteMathVVK(CGP, MapP, SC_ADD_VVV_CODE, VArr, IncVal);
	}
	goto DoneDone;

    CodeMath:
	// PLUS, MINUS, DIV, MOD, MULT, SHL, SHR,
	// PLUSEQ, MINUSEQ, DIVEQ, MODEQ, MULTEQ, SHLEQ, SHREQ
	//
	// Max of 1 Konst only, otherwise Compiler would have done the math!!
	// Process Registers and C/L Vars... put result on Stack
	//
	// NOTE: If AssignTP, get the intended Target of the assignment,
	//       and stash the value there.
	//
	//       SC_CGXformExpr will eliminate NOPUSH ops with no side-effect.
	//	 But AssignTP may have NOPUSH in case AssignTP is a REFER.

	if (SC_ERR_RES == SC_CGCodePassAllArgs(CGP, TP, SelfAssign)) goto ErrorOut;
	HaveKonst = SC_CGLoad2Args(CGP, MapP, TP, VArr, &KonstR, SelfAssign);
	// If an arg is a short string, then SC_ReadProcessShortString *COULD* have an error!
	SC_CHECKERROR(CGP);
	{
	    Int16		DoVVK = 0;
	    Int16		RPush;

	    // Decide on VVK (VKV for SUB) forms
	    if (HaveKonst && KonstR.IsInt)
		DoVVK = SC_CGCheckMathVVK(VArr, &KonstR, &ByteOp);

	    // Handle the L side of assignment, if any.
	    // It will NOT be the stack... because the stack is NOT an LValue.
	    // But can be a REFER (Var or Index/Dot)... SC_CGCodePassAllArgs will
	    // duplicate (PSH) the value indicated by the REF on the stack.
	    // NOTE: The Op is "A * B" here, the assignment is one level up!
	    if (SelfAssign) {				// A *= B --> A = A * B
		if (VArr[1].Class == SC_REFER) {
		    VArr[0].Class = SC_REFER;
		    VArr[1].Class = SC_STACK;
		} else
		    VArr[0] = VArr[1];			// The SelfAssign part!
		TP->Data = TP->DownP->Data;		// Tell caller result goes in first Arg
		TP->Flags |= SC_TOKEN_ASSIGN_FLAG;	// In case caller wanted yet another assign!
	    } else
		SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);	// Sets TP->Data

	    // If pushing the res and NoPush, just stop!  Should never see...
	    if ((TP->Flags & SC_TOKEN_NOPUSH_FLAG) && (VArr[0].Class == SC_STACK))
		goto DoneDone;

	    // RPush means storing value in a REFER, but NEED the value for an upstream assign,
	    // so PUSH the value on Stack and do GETV (instead of storing into REFER).
	    //
	    // NOTE: If SelfAssign, then having ANOTHER AssignTP means it is definitely RPush!!
	    RPush = (VArr[0].Class == SC_REFER) && AssignTP &&
		     (SelfAssign || !(AssignTP->Flags & SC_TOKEN_NOPUSH_FLAG));

	    if (RPush) VArr[0].Class = SC_STACK;
		// ------------------------------------
		// Write out the ByteCode for the Op
		if (DoVVK)
		    SC_CGWriteMathVVK(CGP, MapP, ByteOp, VArr, (Int8)KonstR.Val);
		else {
		    if (HaveKonst) SC_CGPushVArrKonst(CGP, MapP, VArr, &KonstR);
		    SC_CGWrite3V(CGP, MapP, VArr, ByteOp, ByteOp + 1);	// VVV or sss forms
		}
		//-------------------------------------
	    if (RPush) SC_CGWriteNoArg(CGP, MapP, SC_PUTV_CODE);
	}
	goto DoneDone;
	

    CodePolar:	// Polarity!!
    	// LOGNOT, BITNEG, and UMINUS
	// Process the R side... if an Op (Only 1 arg... but still!)
	if (SC_ERR_RES == SC_CGCodePassAllArgs(CGP, TP, 0)) goto ErrorOut;
	SC_CGLoadVar(CGP, MapP, &VArr[1], (SC_VexPointer)TP->DownP->Data, 1);

	// Look at the L side... may indicate Stack or REFER!
	// Rely on CGWriteNegCode and its VV format for REFER *UNLESS* want PUSH.
	SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);
	if ((VArr[0].Class == SC_REFER) && !(AssignTP->Flags & SC_TOKEN_NOPUSH_FLAG)) {
	    VArr[0].Class = SC_STACK;
	    SC_CGWriteNegCode(CGP, MapP, VArr, ByteOp);	    
	    SC_CGWriteNoArg(CGP, MapP, SC_PUTV_CODE);
	    // Leaves the VAL on stack
	} else
	    SC_CGWriteNegCode(CGP, MapP, VArr, ByteOp);

	goto DoneDone;

    CodeComp:
	// LESS, GREAT, EQEQ, NEQ, LESSEQ, GREATEQ but NOT IDNT and NIDNT
	if (SC_ERR_RES == SC_CGCodePassAllArgs(CGP, TP, 0)) goto ErrorOut;    
	// SC_CGLoad2Args will set VArr[2].Index is set to 1, even though K2 here!
	HaveKonst = SC_CGLoad2Args(CGP, MapP, TP, VArr, &KonstR, 0);
	// If an arg is a short string, then SC_ReadProcessShortString *COULD* have an error!
	SC_CHECKERROR(CGP);
	if (ArgSwap) VArr[0] = VArr[1], VArr[1] = VArr[2], VArr[2] = VArr[0];

	// Handle the L side... may indicate Stack or REFER.
	// Use regular path for REFER *UNLESS* have RPush!
	SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);
	{   Uns8	RPush = (VArr[0].Class == SC_REFER) && !(AssignTP->Flags & SC_TOKEN_NOPUSH_FLAG);

	    if (RPush) VArr[0].Class = SC_STACK;

		if (HaveKonst) {
		    // Konst here must be Int and fit in K2... otherwise placed on the stack.
		    if ((VArr[0].Class != SC_STACK) || !KonstR.IsInt || (KonstR.Val < -32768) || (32767 < KonstR.Val)) {
			SC_CGPushVArrKonst(CGP, MapP, VArr, &KonstR);
			HaveKonst = 0;
		    }
		}

		if (HaveKonst)
		    SC_CGWriteCondVK2(CGP, MapP, ByteOp, VArr, (Int16)KonstR.Val);	// VK2 (or K2V form)
		else
		    SC_CGWrite3V(CGP, MapP, VArr, ByteOp, ByteOp + 1);

	    if (RPush) SC_CGWriteNoArg(CGP, MapP, SC_PUTV_CODE);			// Leaves Val on stack!
	}
	goto DoneDone;

    CodeIdnt:
	// IDNT and NIDNT only.
	// These can accept REF *or* VAL on either side!
	// (LocalVar, GlobalVar, Index --> Use Ref... everything else use Value!)
	{
	    Uns8 I = 0;

	    ATP = TP->DownP;		// L side first
	    while (++I < 3) {
		switch (ATP->Type) {
		    case SC_STRING_TOKEN: {
			Uns32 Index = SC_CGGetStringIndex(CGP, ATP);
			SC_CHECKERROR(CGP);
			SC_CGWriteI3(CGP, MapP, Index, SC_PSHS_I3_CODE);
			VArr[I].Class = SC_STACK;
		    } break;

		    case SC_NUMBER_TOKEN:
			SC_CGCodePushTokenNumb(CGP, ATP);
			VArr[I].Class = SC_STACK;
			break;

		    case SC_OPERATOR_TOKEN:
			// If INDEX (Arr or Dict), so get a REF (Generate ZREF not IGET)!
			if (ATP->Id == SC_INDEX_ID) ATP->Flags |= (SC_TOKEN_LVALUE_FLAG | SC_TOKEN_ZREF_FLAG);
			// Now process and get VALUE... (L if Index, R otherwise)
			if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) goto ErrorOut;
			SC_CGLoadVar(CGP, MapP, &VArr[I], (SC_VexPointer)ATP->Data, 1);
			break;

		    case SC_IDENTIFIER_TOKEN:
			// Global or local var... write a *REF* to it!  (All Ref end on Stack!)
			SC_CGWriteCallArg(CGP, MapP, (SC_VexPointer)ATP->Data, 1);
			VArr[I].Class = SC_STACK;
			break;
		}
		ATP = TP->DownP->NextP;
	    }

	    // VArr[1] and [2] could contain mix of REF and VALUE... but that is ok!!
	    // Get dest into VArr[0]...  (Assign will have processed it if Operator)
	    SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);

	    // Assignment cascade:  In case result is being placed in a REF (already on stack),
	    //			    but is needed for later assignment.
	    if ((VArr[0].Class == SC_REFER) && AssignTP && !(TP->Flags & SC_TOKEN_NOPUSH_FLAG)) {
		VArr[0].Class = SC_STACK;				// Push result on stack instead
		SC_CGWrite3V(CGP, MapP, VArr, ByteOp, 0);
		SC_CGWriteNoArg(CGP, MapP, SC_PUTV_CODE);		// Copy into Ref, leave Val on stack

	    } else
		SC_CGWrite3V(CGP, MapP, VArr, ByteOp, 0);		// No SSS version!
	}
	goto DoneDone;

    CodeBitOp:
	// BITAND, BITOR, BITXOR, BITANDEQ, BITOREQ, BITXOREQ
	// (Has SelfAssign like CodeMath only no VVK forms!)
	if (SC_ERR_RES == SC_CGCodePassAllArgs(CGP, TP, SelfAssign)) goto ErrorOut;    
	// Bit Op codes do NOT have explicit K versions... Push Konsts
	if (SC_CGLoad2Args(CGP, MapP, TP, VArr, &KonstR, SelfAssign))
	    SC_CGPushVArrKonst(CGP, MapP, VArr, &KonstR);
	// If an arg is a short string, then SC_ReadProcessShortString *COULD* have an error!
	SC_CHECKERROR(CGP);

	// Handle the L side of assignment, if any
	if (SelfAssign) {			// A &= B --> A = A & B
	    if (VArr[1].Class == SC_REFER) {	// Just like CodeMath!
		VArr[0].Class = SC_REFER;	// Handles "B.A ^= x"
		VArr[1].Class = SC_STACK;
	    } else
		VArr[0] = VArr[1];		// The SelfAssign part!
	    TP->Data = TP->DownP->Data;		// Tell caller result goes in first Arg
	    TP->Flags |= SC_TOKEN_ASSIGN_FLAG;	// In case caller wanted yet another assign
	} else
	    SC_CGLoadAssignVar(CGP, MapP, &VArr[0], AssignTP, TP);

	if ((VArr[0].Class == SC_REFER) && AssignTP &&
	    (SelfAssign || !(AssignTP->Flags & SC_TOKEN_NOPUSH_FLAG))) {
	    // RPush condition... Value is stored in REFER, but need it on stack for
	    // upstream use.  So push value to stack (instead of directly to REFER),
	    // then use GETV.  (REFER is already top of stack.)
	    VArr[0].Class = SC_STACK;
	    SC_CGWrite3V(CGP, MapP, VArr, ByteOp, ByteOp + 1);
	    SC_CGWriteNoArg(CGP, MapP, SC_PUTV_CODE);

	} else    
	    SC_CGWrite3V(CGP, MapP, VArr, ByteOp, ByteOp + 1);	// VVV or sss forms
	goto DoneDone;

    CodeUnPack:
	// Unpacking for INPUT list if TP->Flags & SC_TOKEN_INPUT_FLAG!
	ATP = TP->DownP;	// Only 1 arg!
	if (ATP->Type == SC_OPERATOR_TOKEN) {
	    if (ATP->Id == SC_NULL_ID)
		SC_CGWriteK1(CGP, MapP, 0, SC_PSH_K_CODE);
	    else {
		if (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)) return SC_ERR_RES;
		if (ATP->Data != 0LL)	// In a Vex, NOT on stack!
		    SC_CGWriteCallArg(CGP, MapP, (SC_VexPointer)ATP->Data, 0);
	    }
	    SC_CGWriteNoArg(CGP, MapP, (TP->Flags & SC_TOKEN_INPUT_FLAG) ? SC_UPI_CODE : SC_UPO_CODE);

	} else if (ATP->Type == SC_IDENTIFIER_TOKEN) {
	    if (ATP->Data != 0LL)	// In a Vex, NOT on stack!
		SC_CGWriteCallArg(CGP, MapP, (SC_VexPointer)ATP->Data, 0);
	    SC_CGWriteNoArg(CGP, MapP, (TP->Flags & SC_TOKEN_INPUT_FLAG) ? SC_UPI_CODE : SC_UPO_CODE);

	} else
	    SC_CODEERROR(CGP, ATP, SC_STR_ErrorBadArg);		// Should be caught before here!
	goto DoneDone;

    ErrorOut:
	return SC_ERR_RES;
}

// SC_CGFindLogicEscape will traverse the chain of JSet records to find the
// proper escape--target for Branch.  It will reverse the "normal" escape
// polarity (F for AND, T for OR) to avoid "Null Escapes", where the Branch
// target is the very next ByteCode.
//
// A Null Escape can happen if C wants a BrT in "(&& A (|| B C) D)":
// -- Last elt of sequence
// -- Sequence is NOT the top level
// -- Does NOT see another escape target before its "normal" target
//
// NOTE:  The TOP JumpSet (JS) defines *BOTH* T and F escapes in the same JS, so
//	  the exact order matters for setting SawOther (determines whether
//	  Branch is NULL).  AND defines T first and OR defines F first.  So
//	  SawOther is 1 if going to True (Esc == 1) in an Or op.
//
// Also possible to count JSet levels before Escape is found... if Odd: danger
// of Null Escape, but Even is good--of course Top JSet always needs special
// treatment because it has both.

Uns32	SC_CGFindLogicEscape(SC_JSetPointer JSetP, Int16 * EscTestP, Int16 Esc, Int16 IsLast)
{
    Uns32		Escape = 0L;
    SC_JSetPointer	JSP = JSetP;
    Int16		SawOther = 0;
    
    while (JSP) {
	if (JSP->UpP) {
	    if (JSP->L[!Esc]) SawOther = 1;
	} else {
	    if (Esc == !(JSP->Flags & SC_JSETANDFLAG)) SawOther = 1;
	}
    
	if ((Escape = JSP->L[Esc])) break;
	
	JSP = JSP->UpP;
    }

    // Found it... but will fall to Escape anyway,
    // so switch Test/Escape polarity!
    if (IsLast && JSetP->UpP && !SawOther) {
	*EscTestP = !*EscTestP; JSP = JSetP;
	while (JSP) {
	    if ((Escape = JSP->L[!Esc])) break;
	    JSP = JSP->UpP;
	}
    }

    return Escape;
}

// SC_CGCodeAssignNumb generates ByteCodes to assign a simple number (in NTP) to
// the target (in AssignTP).  TP (the original Op) is included, so the stored
// location can be reported in TP->Data.
//
// If the number is a small enough integer, it is directly moved (Mov_VK2) to
// the variable; otherwise, the number (float or large Int) is stored as Konst
// (placed after the ByteCodes in ASM phase), it is placed on the stack with
// and indexed Push, and then popped into the target location.

void	SC_CGCodeAssignNumb(SC_CGPointer CGP, SC_TokenPointer NTP, SC_TokenPointer AssignTP, SC_TokenPointer TP, Uns8 NoPush)
{
    SC_LexPointer	FuncLexP = CGP->FuncLexP;
    SC_MapPointer	MapP = FuncLexP->MapP;
    SC_VDescRecord	V;
    Uns32		Index;

    SC_CGLoadAssignVar(CGP, MapP, &V, AssignTP, TP);
    if (V.Class == SC_REFER) {
	if (NoPush && (NTP->Id == SC_INTNUMB_ID) && (-32768 <= NTP->Data) && (NTP->Data <= 32767)) {
	    // Small enough (2 bytes) int to code as a MoveVK2, but only if NoPush!
	    SC_CGWriteVK2(CGP, MapP, V, NTP->Data, SC_KONST, SC_MOV_VK2_CODE);
	} else {
	    // Push NTP->Data, then do PUTV!
	    // Puts NUMB in REFER, removes REFER and leaves NUMB on stack
	    SC_CGCodePushTokenNumb(CGP, NTP);
	    SC_CGWriteNoArg(CGP, MapP, SC_PUTV_CODE);
	    if (NoPush) SC_CGWriteNoArg(CGP, MapP, SC_POP1_CODE);
	}
	return;
    } 

    if ((NTP->Id == SC_INTNUMB_ID) && (-32768 <= NTP->Data) && (NTP->Data <= 32767)) {
	// Small enough (2 bytes) int to code as a MoveVK2
	SC_CGWriteVK2(CGP, MapP, V, NTP->Data, SC_KONST, SC_MOV_VK2_CODE);
    } else {
	// Big Int or Double float, make an indexed Konst, then push/pop to assign
	Index = SC_CGGetKonstIndex(CGP, (void *)NTP->Data);
	SC_CGWritePshKIndex(CGP, MapP, Index, (NTP->Id == SC_INTNUMB_ID));
	// Do nothing if storing to STACK, it is already there!
	if (V.Class != SC_STACK)
	    SC_CGWrite1V(CGP, MapP, V, SC_POP_V_CODE);
    }
}

// SC_CGCodePushTokenNumb takes the number (in NTP) and pushes it onto the stack.
// Calls SC_CodePushInt or SC_CodePushFloat.

void	SC_CGCodePushTokenNumb(SC_CGPointer CGP, SC_TokenPointer NTP)
{
    if (NTP->Id == SC_INTNUMB_ID)
	SC_CGCodePushInt(CGP, NTP->Data);
    else
	SC_CGCodePushFloat(CGP, *(double *)&NTP->Data);
}

// SC_CGCodePushInt will Psh the Numb onto the stack.  Uses Psh_K1 for small
// Int and Psh_K3 for larger Int.  If the Int is huge, it will record it as a
// Konst (or just retrieve it if previously recorded) and Psh its Index.

void	SC_CGCodePushInt(SC_CGPointer CGP, Int64 Numb)
{
    SC_MapPointer	MapP = CGP->FuncLexP->MapP;

    if ((-128 <= Numb) && (Numb <= 127))
	SC_CGWriteK1(CGP, MapP, (Int8)Numb, SC_PSH_K_CODE);
    else if ((-8388608 <= Numb) && (Numb <= 8388607))
	SC_CGWriteK3(CGP, MapP, (Int32)Numb, SC_PSH_K3_CODE);
    else
	SC_CGWritePshKIndex(CGP, MapP, SC_CGGetKonstIndex(CGP, *(void **)&Numb), 1);
}

// SC_CGCodePushFloat will record the float as a Konst (or just retrieve it
// if previously recorded) and push its Index.

void	SC_CGCodePushFloat(SC_CGPointer CGP, double Numb)
{
	SC_CGWritePshKIndex(CGP, CGP->FuncLexP->MapP, SC_CGGetKonstIndex(CGP, *(void **)&Numb), 0);
}

// SC_CGCodePassAllArgs will go through all the args below TP and call (recurse)
// SC_CGCodePass if they are Operators.  Code is generated from the bottom level
// of the AST up, and this is the function that heads down.
//
// DO NOT call for ASSIGN, assumes no assignment is requested of lower levels.

Int16	SC_CGCodePassAllArgs(SC_CGPointer CGP, SC_TokenPointer TP, Int8 SelfAssign)
{
    SC_TokenPointer	ATP = TP->DownP;
    
    if (SelfAssign) ATP->Flags |= SC_TOKEN_LVALUE_FLAG;
    while (ATP) {
	// Recurse through the args as needed
	if ((ATP->Type == SC_OPERATOR_TOKEN) &&
	    (SC_ERR_RES == SC_CGCodePass(CGP, ATP, NULL)))
	    return SC_ERR_RES;

	if (SelfAssign && (ATP->Data == 0LL)) {
	    // so first PSH to dupe the REFER, then GETV to get its value.
	    SC_CGWriteNoArg(CGP, CGP->FuncLexP->MapP, SC_PSH_CODE);
	    SC_CGWriteNoArg(CGP, CGP->FuncLexP->MapP, SC_GETV_CODE);

	}
	SelfAssign = 0;		// Only do it on first (L) arg

	ATP = ATP->NextP;
    }

    return SC_SUCC_RES;
}

// SC_CGLoadVar will load the VarP with the given VexP.
// It normally returns 1.
//
// If SetGlobal is 0 it WILL ONLY test the RegCache, will NOT set the Global to
// a new REG and will return 0 IFF the Global was NOT assigned to a Reg.
//
// If VexP is:
//	Global:		Stash in RVex (SetGlobal==1) if not already there.
//			THIS MAY GENERATE CODE!!
//
//	Local/Closed:	Just put Type and Order in VarP.
//
//	REG		Put Type and ***STATE** in VarP.
//			***RVexP->Index indicates which GLOBAL***


Int16	SC_CGLoadVar(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VarP, SC_VexPointer VexP, Int16 SetGlobal)
{
    if (VexP == NULL) {
	VarP->Class = SC_STACK;
	return 1;
    }

    if (VexP->Class == SC_GVEX) {
	if (SetGlobal) {
	    if (SC_CGRegCacheSet(CGP, MapP, VexP))
		SC_CGWriteRSetV(CGP, MapP);
	    VarP->Class = SC_RVEX;
	    VarP->Index = MapP->RVexP->Index;

	} else {
	    // if already in RegCache, use the Reg!
	    if (SC_CGRegCacheGet(CGP, MapP, VexP)) {
		VarP->Class = SC_RVEX;
		VarP->Index = MapP->RVexP->Index;
	    } else
		return 0;
	}
    } else {
	VarP->Class = VexP->Class;
	VarP->Index = VexP->Index;
	// Should never see RVEX here... just to be safe.
    }

    return 1;
}

// SC_CGLoadAssignVar loads up VarP (Class and Index) to say where the RESULT should go.
// There is often an AssignTP and most operators can accomodate this requiest.
//
// Will update TP->Data, telling callers where the result WENT.
//
// NOTE: If there is an AssignTP but NULL Data slot, then this is an SC_REFER
//	 request, NOT SC_STACK.  A Var Reference is on the stack and the result
//	 should be stashed in that location.

void	SC_CGLoadAssignVar(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VarP, SC_TokenPointer AssignTP, SC_TokenPointer TP)
{
    SC_VexPointer	VexP;

    TP->Data = 0LL;
    if (AssignTP) {
	if ((VexP = (SC_VexPointer)AssignTP->Data)) {
	    TP->Data = (Uns64)VexP;

	    if (VexP->Class == SC_GVEX) {
		if (SC_CGRegCacheSet(CGP, MapP, VexP))
		    SC_CGWriteRSetV(CGP, MapP);
		VarP->Class = SC_RVEX;
		VarP->Index = MapP->RVexP->Index;
	    } else {
		VarP->Class = VexP->Class;
		VarP->Index =  VexP->Index;
	    }

	} else {
	    // Have AssignTP but no VexP... means a REF is on the stack, write to it!
	    VarP->Class = SC_REFER;
	}

    } else
	VarP->Class = SC_STACK;
}

// SC_CGLoad2Args loads VArr[1] and VArr[2] with args *BELOW* TP.  It needs 2
// args, they can be Konst or VAR (Global, Local, Closed).
//
// Assumes caller will set VArr[0] properly!
//
// NOTE:  If DoesAssign, then getting args for a SelfAssign computation, like
//	  "A += 12".  This will conceptually expand to "A = A + 12", so if A is
//	  an Index/Dot operation, there will be a REFER not a STACK in Arg1!

Int16	SC_CGLoad2Args(SC_CGPointer CGP, SC_MapPointer MapP, SC_TokenPointer TP,
		       SC_VDescPointer VArr, SC_KPointer KP, Int16 DoesAssign)
{
    Int16		I = 1;
    Int16		HaveKonst = 0;
    SC_VexPointer	VexP;
    Uns32		Index;
    
    TP = TP->DownP;			// Get the Args
    do {
	switch (TP->Type) {
	    case SC_STRING_TOKEN:
		// ==, !=, ===, and !== can have String args!!
		Index = SC_CGGetStringIndex(CGP, TP);
		SC_CGWriteI3(CGP, MapP, Index, SC_PSHS_I3_CODE);
		VArr[I].Class = SC_STACK;
		break;
	
	    case SC_NUMBER_TOKEN:
		KP->Val = TP->Data;
		KP->IsInt = (TP->Id == SC_INTNUMB_ID);
		VArr[I].Class = SC_KONST;
		VArr[I].Index = 1;	// 1 byte
		HaveKonst = 1;
		break;

	    case SC_OPERATOR_TOKEN:	// Can be Stack, Local/Closed, or Global?
	    case SC_IDENTIFIER_TOKEN:	// Can be Local/Closed or Global.
		VexP = (SC_VexPointer)TP->Data;
		if (VexP == NULL)
		    VArr[I].Class = ((I == 1) && DoesAssign) ? SC_REFER : SC_STACK;
		else SC_CGLoadVar(CGP, MapP, &VArr[I], VexP, 1);
		break;

	    default: break;		// ???? Check cannot get String, etc.
	}

	TP = TP->NextP;			// Next Arg!
    } while (++I < 3);

    return HaveKonst;
}

// SC_CGPushVArrKonst will find the KONST specified in VArr[1] or 2 and push
// it on the stack.  In the process it will change the VArr Class to SC_STACK.
// IFF there is a KONST!
//
// There are two different options for pushing a Konstant.  1-byte K can be
// directly pushed using PUSH_K while longer K (INT or FLT) can be added to the
// global KONST array and pushed (PUSHI/PUSHF) with an I3 index.
//
// NOTE: All Ops that have 2 Stack ops will do an auto-swap to get the order
//	 right.  Therefore, there is no need to generate a SWAP ByteCode if
//	 pushing at VArr[1] when VArr[2] is SC_STACK.

void	SC_CGPushVArrKonst(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, SC_KPointer KP)
{
    Int16	I = 1;

    do {
	if (VArr[I].Class == SC_KONST) {
	    if (KP->IsInt)
		SC_CGCodePushInt(CGP, KP->Val);
	    else
		SC_CGCodePushFloat(CGP, *(double *)&KP->Val);

	    VArr[I].Class = SC_STACK;		// Now pushed onto Stack!
	    break;				// At most 1 Konst!!
	}
    } while (++I < 3);
}

// SC_CGCheckMathVVK handles placement of the small (1-byte) K when doing
// arithmetic.  PLUS and MUL are commutative and support only VVK bytecodes.
// SUB only has VKV but can use PLUS_VVK if K is negated.
//
// Return 0 if KONST is too big to fit in 1 byte *OR* is in wrong position.

Int16	SC_CGCheckMathVVK(SC_VDescPointer VArr, SC_KPointer KP, Uns8 * ByteOpP)
{
    Int16	DoVVK = 0;
    Int16	KonstIsSmall = 0;
    Uns8	Op = *ByteOpP;

    KonstIsSmall = (-128 <= KP->Val) && (KP->Val <= 127);
    if (KonstIsSmall) {
	if (VArr[1].Class == SC_KONST) {
	    // Arg 1 is a Small Konst... 
	    if (Op == SC_SUB_VVV_CODE)
		DoVVK = 1;
	    else if ((Op == SC_ADD_VVV_CODE) || (Op == SC_MUL_VVV_CODE)) {
		// ADD and MUL don't care about order
		DoVVK = 1;
		VArr[1] = VArr[2];
		VArr[2].Class = SC_KONST;
		VArr[2].Index = 1;		// 1 Byte
	    }

	} else {
	    // Arg 2 is Small Konst
	    DoVVK = 1;
	    if (Op == SC_SUB_VVV_CODE) {
		if (-KP->Val <= 127) {		// Could have been -128 before!
		    *ByteOpP = SC_ADD_VVV_CODE;	// Change it!
		    KP->Val = -KP->Val;
		} else
		    DoVVK = 0;			// SUB only has VKV form, not VVK
	    }
	}
    }

    return DoVVK;
}


// NOTE:  LVar and CVar are **INITIALLY** segragated (until Indices can be
//	  normalized).  So make sure Var Classes take that into account!

void	SC_CGWriteMathVVK(SC_CGPointer CGP, SC_MapPointer MapP, Uns8 Op, SC_VDescPointer VArr, Int8 KVal)
{
    // Jump over SSS form of Op to VVK/VKV form.
    SC_CGWriteVVK(CGP, MapP, VArr, KVal, Op + 2);
}

void	SC_CGWriteNegCode(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, Uns8 Op)
{
    SC_ByteOpPointer	BOP = SC_CGCodeAdd(CGP, MapP);

    if (VArr[0].Class == SC_STACK) {
	BOP->Op = Op;
	BOP->ArgCount = 1;
	BOP->ByteLen = 2;
	BOP->Vars[0] = VArr[1];
    } else {
	BOP->Op = Op + 1;
	BOP->ArgCount = 2;
	BOP->ByteLen = 3;
	BOP->Vars[0] = VArr[0];
	BOP->Vars[1] = VArr[1];
    }
}

// SC_CGWriteCondVK2 will also write K2V forms... depending on which VArr is
// SC_KONST.
//
// NOTE: ALWAYS writes V then K2... the Op knows to read them properly.
//	 VArr[1] and VArr[2] are the args, VArr[0] is ignored here.

void	SC_CGWriteCondVK2(SC_CGPointer CGP, SC_MapPointer MapP, Uns8 Op, SC_VDescPointer VArr, Int16 K2Val)
{
    SC_ByteOpPointer	BOP = SC_CGCodeAdd(CGP, MapP);

    BOP->Op = Op + 2;				// Skip over SSS to VK2 form
    BOP->ArgCount = 2;
    BOP->ByteLen = 4;
    BOP->KVal = K2Val;
    BOP->Vars[0] = VArr[1];
    if (VArr[1].Class == SC_KONST) {
	BOP->Vars[0] = VArr[2];			// Swap... Write V-K2 order.
	if (Op < SC_EQ_VVV_CODE)
	    BOP->Op += 1;			// Have special K2V Op!
    }

    BOP->Vars[1].Class = SC_KONST;
    BOP->Vars[1].Index = 2;
    
}


void	SC_CGWriteMovK(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, Int16 K)
{

    if ((VArr[0].Class == SC_STACK) && (-128 <= K) && (K <= 127))
	SC_CGWriteK1(CGP, MapP, K, SC_PSH_K_CODE);
    else 
	SC_CGWriteVK2(CGP, MapP, VArr[0], K, SC_KONST, SC_MOV_VK2_CODE);
}


void	SC_CGWriteRSetV(SC_CGPointer CGP, SC_MapPointer MapP)
{
    SC_VexPointer	RVexP = MapP->RVexP;
    SC_ByteOpPointer	BOP = SC_CGCodeAdd(CGP, MapP);

    BOP->Op = SC_RSET_VI2_CODE;		// ALIASES Entry for Global.
    BOP->ArgCount = 2;
    BOP->ByteLen = 4;
    BOP->KVal = RVexP->SrcIndex;	// Always assumes G Id/Index
    BOP->Vars[0].Class = SC_RVEX;
    BOP->Vars[0].Index = RVexP->Index;
    BOP->Vars[1].Class = SC_INDEX;
    BOP->Vars[1].Index = 2;		// Length is 2 bytes!
}



void	SC_CGWrite3V(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, Uns8 Op, Uns8 SSSOp)
{
    SC_ByteOpPointer	BOP = SC_CGCodeAdd(CGP, MapP);

    if (SSSOp &&
	(VArr[0].Class == SC_STACK) &&
	(VArr[1].Class == SC_STACK) &&
	(VArr[2].Class == SC_STACK)) {
	BOP->Op = SSSOp;
	BOP->ArgCount = 0;
	BOP->ByteLen = 1;
	return;
    }

    BOP->Op = Op;
    BOP->ArgCount = 3;
    BOP->ByteLen = 4;
    BOP->Vars[0] = VArr[0];
    BOP->Vars[1] = VArr[1];
    BOP->Vars[2] = VArr[2];
}

void	SC_CGWriteVVK(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, Int8 KVal, Uns8 Op)
{
    SC_ByteOpPointer	BOP = SC_CGCodeAdd(CGP, MapP);

    BOP->Op = Op;
    BOP->ArgCount = 3;
    BOP->ByteLen = 4;
    BOP->KVal = KVal;

    BOP->Vars[0] = VArr[0];
    BOP->Vars[1] = VArr[1];
    BOP->Vars[2] = VArr[2];
}

// Special write for IGET, as it can be expensive in runtime... avoiding back-to-back lookups.
void	SC_CGWriteIGETVVK(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, Int8 KVal)
{
    SC_ByteOpPointer	BOP;

    if ((VArr[0].Class == SC_STACK) && MapP->CodeADP && MapP->CodeCount) {
	// Pushing result of this VVK *AND* there is a previous one!
	BOP = (SC_ByteOpPointer)MapP->CodeADP->Data + (MapP->CodeCount - 1);
	if ((BOP->Op == SC_IGET_VVK_CODE) &&
	    (BOP->KVal == KVal) &&
	    (BOP->Vars[0].Class == SC_STACK) &&
	    (BOP->Vars[1].Class == VArr[1].Class) &&
	    (BOP->Vars[1].Index == VArr[1].Index) &&
	    (BOP->Vars[2].Class == SC_KONST)) {

	    SC_CGWriteNoArg(CGP, MapP, SC_PSH_CODE);
	    return;
	}
    }
    
    SC_CGWriteVVK(CGP, MapP, VArr, KVal, SC_IGET_VVK_CODE);
}

// Special write for IGET, as it can be expensive in runtime... avoiding back-to-back lookups.
void	SC_CGWriteIGETVVV(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr)
{
    SC_ByteOpPointer	P1BOP;		// Prev ByteOp

    if ((VArr[0].Class == SC_STACK) && MapP->CodeADP && MapP->CodeCount) {
	// Pushing result of this VVK *AND* there is a previous one!
	P1BOP = (SC_ByteOpPointer)MapP->CodeADP->Data + (MapP->CodeCount - 1);
	if ((P1BOP->Op == SC_IGET_VVV_CODE) &&
	    (P1BOP->Vars[0].Class == SC_STACK) &&
	    (P1BOP->Vars[1].Class = VArr[1].Class) &&
	    (P1BOP->Vars[1].Index == VArr[1].Index) &&
	    (P1BOP->Vars[2].Class == VArr[2].Class) &&
	    (P1BOP->Vars[2].Index == VArr[2].Index)) {

	    SC_CGWriteNoArg(CGP, MapP, SC_PSH_CODE);
	    return;
	}
	
	// See if INDEX was pushed and is identical... Replace:
	//
	// PshS_I3_ -- I3:00000000000 <-- "MAMA"	<-- P3BOP
	// IGet_VVV -- PUSH R000 POP_			<-- P2BOP
	// PshS_I3_ -- I3:00000000000 <-- "MAMA"	<-- P1BOP
	// IGet_VVV -- PUSH R000 POP_			<-- Called to write 2nd IGet.
	//
	// with:
	// PshS_I3_ -- I3:00000000000 <-- "MAMA"
	// IGet_VVV -- PUSH R000 POP_
	// Psh____  --					<-- Del P1BOP and write PSH.
	
	if ((VArr[2].Class == SC_STACK) &&
	    (MapP->CodeCount >= 3) &&
	    ((SC_PSH_V_CODE <= P1BOP->Op) && (P1BOP->Op <= SC_PSHS_I3_CODE))) {
	    SC_ByteOpPointer	P2BOP = P1BOP - 1;	// Prev Prev ByteOp
	    SC_ByteOpPointer	P3BOP = P2BOP - 1;	// Prev Prev Prev ByteOp
	    Uns8		DoIt = 0;

	    if ((P2BOP->Op == SC_IGET_VVV_CODE) &&
		(P2BOP->Vars[0].Class == SC_STACK) &&
		(P2BOP->Vars[2].Class == SC_STACK) &&
		(P2BOP->Vars[1].Class == VArr[1].Class) &&
		(P2BOP->Vars[1].Index == VArr[1].Index) &&
		(P3BOP->Op == P1BOP->Op)) {

		// A number of different PSH ops... some take V, others K, K3, or I3
		DoIt = (SC_OpDispArr[P3BOP->Op].ArgI == SC_DISP1V)
			? ((P3BOP->Vars[0].Class == P1BOP->Vars[0].Class) &&
			   (P3BOP->Vars[0].Index == P1BOP->Vars[0].Index))
			: (P3BOP->KVal == P1BOP->KVal);
	    }
	    
	    if (DoIt) {
		// Replace Prev BOP with a PSH to dupe the IGET!
		MapP->CodeCount -= 1;
		SC_CGWriteNoArg(CGP, MapP, SC_PSH_CODE);
		return;
	    }
	}
    }
    
    SC_CGWrite3V(CGP, MapP, VArr, SC_IGET_VVV_CODE, 0);
}

// Konst is Int32 so can handle both I2 and K2
void	SC_CGWriteVK2(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescRecord V, Int32 Konst, Uns8 KClass, Uns8 Op)
{
    SC_ByteOpPointer	BOP = SC_CGCodeAdd(CGP, MapP);

    BOP->Op = Op;
    BOP->ArgCount = 2;
    BOP->ByteLen = 4;
    BOP->KVal = Konst;
    BOP->Vars[0] = V;
    BOP->Vars[1].Class = KClass;
    BOP->Vars[1].Index = 2;		// 2 Bytes
}

// SC_PSH_V_CODE, SC_POP_V_CODE, SC_PSHI_V_CODE, SC_PSHF_V_CODE, SC_REF_V_CODE,
// SC_FRET_V_CODE, S_ANEW_V_CODE, SC_MOVE_C_CODE, SC_FGET_V_CODE, SC_VSET_V_CODE
void    SC_CGWrite1V(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescRecord V, Uns8 Op)
{
    SC_ByteOpPointer	BOP = SC_CGCodeAdd(CGP, MapP);
    
    BOP->Op = Op;
    BOP->ArgCount = 1;
    BOP->ByteLen = 2;
    BOP->Vars[0] = V;
}

// SC_MOV_VV, SC_FRET_VV, SC_FYLD_VV
void	SC_CGWrite2V(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, Uns8 Op)
{
    SC_ByteOpPointer	BOP = SC_CGCodeAdd(CGP, MapP);

    BOP->Op = Op;
    BOP->ArgCount = 2;
    BOP->ByteLen = 3;
    BOP->Vars[0] = VArr[0];
    BOP->Vars[1] = VArr[1];
}


// SC_PSH_K_CODE, SC_POPN_K_CODE, SC_FRET_K_CODE
void	SC_CGWriteK1(SC_CGPointer CGP, SC_MapPointer MapP, Int8 K, Uns8 Op)
{
    SC_ByteOpPointer	BOP = SC_CGCodeAdd(CGP, MapP);

    BOP->Op = Op;
    BOP->ArgCount = 1;
    BOP->ByteLen = 2;
    BOP->KVal = K;
    BOP->Vars[0].Class = SC_KONST;
    BOP->Vars[0].Index = 1;		// 1 Byte
}

// SC_PSHG_I3_CODE, SC_REFG_I3_CODE,  SC_PSHS_I3_CODE
void	SC_CGWriteI3(SC_CGPointer CGP, SC_MapPointer MapP, Int32 I3, Uns8 Op)
{
    SC_ByteOpPointer	BOP = SC_CGCodeAdd(CGP, MapP);
    BOP->Op = Op;
    BOP->ArgCount = 1;
    BOP->ByteLen = 4;
    BOP->KVal = I3;
    BOP->Vars[0].Class = SC_INDEX;
    BOP->Vars[0].Index = 3;		// 3 bytes
}

// SC_PSH_K3_CODE
void	SC_CGWriteK3(SC_CGPointer CGP, SC_MapPointer MapP, Int32 K3, Uns8 Op)
{
    SC_ByteOpPointer	BOP = SC_CGCodeAdd(CGP, MapP);
    BOP->Op = Op;
    BOP->ArgCount = 1;
    BOP->ByteLen = 4;
    BOP->KVal = K3;
    BOP->Vars[0].Class = SC_KONST;
    BOP->Vars[0].Index = 3;		// 3 bytes
}

// Writes a BOP as a place holder... these will be ignored in
// later stages if still empty.
void	SC_CGWriteNoOp(SC_CGPointer CGP, SC_MapPointer MapP)
{
    SC_ByteOpPointer BOP = SC_CGCodeAdd(CGP, MapP);
    BOP->Op = SC_NOOP_CODE;
    BOP->ArgCount = 0;
    BOP->ByteLen = 0;
}

// SC_PUTV_CODE, SC_GETV_CODE, SC_POP1_CODE, SC_PUSH_CODE, SC_FRET_CODE,
// SC_FBEG_CODE, SC_FEND_CODE, SC_FDFR_CODE, SC_DINI_CODE
void	SC_CGWriteNoArg(SC_CGPointer CGP, SC_MapPointer MapP, Uns8 Op)
{
    SC_ByteOpPointer BOP = SC_CGCodeAdd(CGP, MapP);
    BOP->Op = Op;
    BOP->ArgCount = 0;
    BOP->ByteLen = 1;
}

// SC_BRT_K3_CODE, SC_BRF_K3_CODE, SC_JMP_K3_CODE
void	SC_CGWriteBranch(SC_CGPointer CGP, SC_MapPointer MapP, Uns32 LIndex, Uns8 Op)
{
    SC_ByteOpPointer	BOP = SC_CGCodeAdd(CGP, MapP);
    BOP->Flags |= SC_BCJMPFLAG;
    BOP->Op = Op;
    BOP->ArgCount = 1;
    BOP->ByteLen = 4;
    BOP->KVal = LIndex;
    BOP->Vars[0].Class = SC_KONST;
    BOP->Vars[0].Index = 3;
}

void	SC_CGWriteMovVV(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescPointer VArr, Uns8 NoPush)
{
    if (VArr[1].Class == SC_STACK) {
	// See design notes on "Assignment Cascade"!
	if (VArr[0].Class == SC_REFER) {
	    SC_CGWriteNoArg(CGP, MapP, SC_PUTV_CODE);
	    if (NoPush)
		SC_CGWriteNoArg(CGP, MapP, SC_POP1_CODE);
	} else
	    SC_CGWrite1V(CGP, MapP, VArr[0], SC_POP_V_CODE);
    } else
	SC_CGWrite2V(CGP, MapP, VArr, SC_MOV_VV_CODE);
}

void	SC_CGWritePshKIndex(SC_CGPointer CGP, SC_MapPointer MapP, Uns32 KIndex, Uns8 IsInt)
{
    SC_ByteOpPointer	BOP = SC_CGCodeAdd(CGP, MapP);

    BOP->Op = (IsInt) ? SC_PSHI_I3_CODE : SC_PSHF_I3_CODE;
    BOP->ArgCount = 1;
    BOP->ByteLen = 4;
    BOP->KVal = KIndex;
    BOP->Vars[0].Class = SC_INDEX;
    BOP->Vars[0].Index = 3;		// Length is 3 bytes
}

void	SC_CGWriteSwch(SC_CGPointer CGP, SC_MapPointer MapP, SC_VDescRecord V)
{
    SC_CGWriteVK2(CGP, MapP, V, MapP->SwitchCount, SC_INDEX, SC_SWCH_VI2_CODE);
}

void	SC_CGWriteFCall(SC_CGPointer CGP, SC_MapPointer MapP, SC_VexPointer VexP, Uns16 ArgCount, Uns8 DoUnPack)
{
    SC_VDescRecord	V;

    if (! SC_CGLoadVar(CGP, MapP, &V, VexP, 0)) {
	// Here ONLY IF VexP is Global and NOT in RegCache!
	// Push the FuncP onto stack!
	SC_CGWriteI3(CGP, MapP, VexP->Index, SC_PSHG_I3_CODE);
	V.Class = SC_STACK;
    }

    SC_CGWriteVK2(CGP, MapP, V, ArgCount, SC_INDEX, (DoUnPack) ? SC_UFCAL_VK2_CODE : SC_FCALL_VK2_CODE);
}

void	SC_CGWriteSCall(SC_CGPointer CGP, SC_MapPointer MapP, Int64 SIndex, Uns16 ArgCount, Uns8 DoUnPack)
{
    SC_VDescRecord	V;

    // Pretending SIndex is the Reg index... since REG come first!
    V.Class = SC_SYSI;
    V.Index = (Uns8)SIndex;

    // Assuming SIndex is 0..255 for now.  Easy enough to push SIndex on the stack...
    SC_CGWriteVK2(CGP, MapP, V, ArgCount, SC_INDEX, (DoUnPack) ? SC_USCAL_IK2_CODE : SC_SCALL_IK2_CODE);
}

void	SC_CGWriteLogicTest(SC_CGPointer CGP, SC_MapPointer MapP, SC_VexPointer VexP, Uns32 LIndex, Uns8 IsTrue)
{
    SC_VDescRecord	V;

    if (VexP) {
	 SC_CGLoadVar(CGP, MapP, &V, VexP, 1);
	 SC_CGWrite1V(CGP, MapP, V, SC_PSH_V_CODE);
    }
    SC_CGWriteBranch(CGP, MapP, LIndex, (IsTrue) ? SC_BRT_K3_CODE : SC_BRF_K3_CODE);
}

void	SC_CGWriteCallArg(SC_CGPointer CGP, SC_MapPointer MapP, SC_VexPointer VexP, Int16 ByRef)
{
    SC_VDescRecord	V;

    if (SC_CGLoadVar(CGP, MapP, &V, VexP, 0)) {
	if (VexP)
	    SC_CGWrite1V(CGP, MapP, V, (ByRef) ? SC_REF_V_CODE : SC_PSH_V_CODE);
    } else {
	// Here ONLY IF VexP is Global and NOT in RegCache!
	SC_CGWriteI3(CGP, MapP, VexP->Index, (ByRef) ? SC_REFG_I3_CODE : SC_PSHG_I3_CODE);
    }
}

// ********************************************************************************
// ********************************************************************************
// ********************************************************************************

// SC_AuxASMPrePass will make a first pass through the ByteCode records to:
//
//   0) Check GOTO jumps for Entry/Exit of MapI/K blocks!  (Only if SC_MAP_MLOOPFLAG)
//   1) Replace label refs in JUMP and BRANCH ops with absolute addresses.
//   2) Unwind Jump to Jump... goes to final Dest directly.
//   3) Eliminate null Jumps... replaced with No-ops that fall through.
//   4) Set the Address (cumulative) field in every BOP.
//   5) Adjust BOP->ByteLen for proper alignment
//   6) Go through CaseDictP and update addresses.
//   7) ... zap all Label fields/info at the end.
//   8) Set MapP->CodeBytes

    Uns32	SC_AuxASMCheckMapGoto(SC_CGPointer CGP, SC_MapPointer MapP, Uns32 FromI, Uns32 ToI)
    {
	SC_ByteOpPointer	FromBOP = (SC_ByteOpPointer)MapP->CodeADP->Data + FromI;
	SC_ByteOpPointer	ToBOP = (SC_ByteOpPointer)MapP->CodeADP->Data + ToI;
	SC_ByteOpPointer	CurBOP = FromBOP;
	Uns32			OutOf, Into;

	// Traverse all BOPs between FromI and ToI,
	// see how many MapI/K blocks getting into and going out of!
	OutOf = Into = 0;
	if (FromI < ToI) {
	    // Going forward
	    while (CurBOP < ToBOP) {
		// Can Enter+Exit (transit) an MLoop
		if (CurBOP->Flags & SC_BCBEGMAPFLAG)
		    ++Into;
		else if (CurBOP->Flags & SC_BCENDMAPFLAG) {
		    if (Into) --Into;
		    else ++OutOf;
		}
		++CurBOP;
	    }

	} else {
	    // Going backwards
	    while (CurBOP < ToBOP) {
		if (CurBOP->Flags & SC_BCENDMAPFLAG)
		    ++Into;
		else if (CurBOP->Flags & SC_BCBEGMAPFLAG) {
		    if (Into) --Into;
		    else ++OutOf;
		}
		--CurBOP;
	    }
	}
	
	// CANNOT Jmp INTO a new MapI/K block... CGMLOOPRUNSTACKVALS will be missing!
	if (Into) SC_CODENULLERROR(CGP, SC_STR_ErrorGotoIntoMap);
	    
	// Can only exit so many MapI/K loops... needs a POPN op, and arg is only Int8!!
	if (OutOf) {
	    if (((OutOf *= SC_CGMLOOPRUNSTACKVALS)) > 0x007F)		// Assign
		SC_CODENULLERROR(CGP, SC_STR_ErrorGotoOutOfMap);
	    CurBOP = FromBOP - 1;					// Left blank before JMP
	    CurBOP->Op = SC_POPN_K_CODE;
	    CurBOP->KVal = OutOf;
	    CurBOP->ArgCount = 1;
	    CurBOP->Vars[0].Class = SC_KONST;
	    CurBOP->Vars[0].Index = 1;					// 1 Byte
	    CurBOP->Address = FromBOP->Address;
	    CurBOP->ByteLen = 4;					// So JMP is aligned properly
	    FromBOP->Address += 4;
	}
	
	return FromBOP->Address;

    ErrorOut:
	return 0;
    }

Int16	SC_AuxASMPrePass(SC_CGPointer CGP, SC_MapPointer MapP)
{
    SC_ByteOpPointer	StartBOP = (SC_ByteOpPointer)MapP->CodeADP->Data;
    SC_ByteOpPointer	CurBOP = StartBOP;
    SC_ByteOpPointer	NextBOP;
    SC_ByteOpPointer	EndBOP = StartBOP + MapP->CodeCount;
    Int32		Offset;
    Uns32		I, Address;
    Uns8		Add;

    I = Address = 0;			// I is CurBOP index in CodeADP
    while (CurBOP < EndBOP) {
	CurBOP->Address = Address;
	
	if (CurBOP->Flags & SC_BCJMPFLAG) {
	    // Can be Jmp, BrT, or BrF
	    Offset = SC_CGLabelGet(MapP, CurBOP->KVal);
	    
	    if ((CurBOP->Flags & SC_BCGOTOFLAG) &&		// Op is JMP for GOTO
		(MapP->Flags & SC_MAP_MLOOPFLAG) &&		// Func *HAS* MLoops (MapI/K)
		!((Address = SC_AuxASMCheckMapGoto(CGP, MapP, I, Offset))))	// Assign
		goto ErrorOut;
	    
	    while (1) {
		// Handle chained Jmps... just Jmp to the last target!
		// Jmps to next become NOOPs, so check for them too.
		NextBOP = StartBOP + Offset;
		if (NextBOP->Op == SC_JMP_K3_CODE)
		    Offset = SC_CGLabelGet(MapP, NextBOP->KVal);
		else if (NextBOP->Op == SC_NOOP_CODE)
		    Offset += 1;
		else
		    break;
	    }

	    CurBOP->KVal = Offset;
	    CurBOP->Vars[0].Class = SC_ADDR;
	    if ((CurBOP->Op == SC_JMP_K3_CODE) && (Offset == I + 1))
		CurBOP->ByteLen = 0, CurBOP->Op = SC_NOOP_CODE;
	}

	NextBOP = CurBOP + 1;
	Address += CurBOP->ByteLen;
	Add = 0;
	if (NextBOP < EndBOP) {
	    // Adjust CurBOP->ByteLen for Long alignment
	    switch (Address & 0x03) {
		case 0: break;
		case 1: if (NextBOP->ByteLen == 2) Add = 1;
			else if (NextBOP->ByteLen == 4) Add = 3;
			break;
		case 2: if (NextBOP->ByteLen > 2) Add = 2;
			break;
		case 3: if (NextBOP->ByteLen > 1) Add = 1;
			break;
	    }
	} else if ((Add = (Address & 0x03)))
	    Add = 4 - Add;
	    
	CurBOP->ByteLen += Add;
	Address += Add;

	I += 1; CurBOP = NextBOP;;
    }

    // Update the CaseDictP... individual CaseP->Value is indicating BOP[I],
    // should be converted to Address of ByteOp.
    if (MapP->CaseDictP) {
	SC_CaseHPointer		CaseP = (SC_CaseHPointer)MapP->CaseDictP->FirstPairP;

	while (CaseP) {
	    CurBOP = StartBOP + CaseP->Value;
	    // JMP chains have been eliminated already...
	    if (CurBOP->Op == SC_JMP_K3_CODE)
		CurBOP = StartBOP + CurBOP->KVal;
	    else if (CurBOP->Op == SC_NOOP_CODE)
		CurBOP += 1;

	    CaseP->Value = CurBOP->Address;
	    CaseP = (SC_CaseHPointer)CaseP->NextPairP;
	}
    }

    // All Labels can be removed now.
    if (MapP->LabelDictP) SC_DictPurge(CGP->VMP, MapP->LabelDictP, 0);
    if (MapP->LabelADP) SC_SlabZapItem(CGP->VMP->ArrDataSP, MapP->LabelADP, MapP->LabelADP->BlockLen);

    MapP->LabelDictP = NULL;
    MapP->LabelADP = NULL;
    MapP->LabelCount = 0L;

    // Round up to long align total length.
    MapP->CodeBytes =  SC_LALIGN(Address);
    return 1;

ErrorOut:
    return 0;
}

// SC_ASMPass will first make a PrePass to adjust Labels, then another full
// pass through the BOP to create real ByteCodes in a CodeBlockRecord.
//
// NOTE: CodeADP can *NEVER* be re-written in situ to become BlockP!  CodeADP in
//	 MapP is in ARR slab, while BlockP will be in a CHUNK slab.  Worse,
//	 loops typically jump BACK and would need to read their address from
//	 previous Ops which would be clobbered in an in-situ conversion!
//
// NOTE: A function is NOT FINISHED until all of its sub-functions have been
//	 parsed and compiled.  As each Sub-Func is compiled, it gets a CBP which
//	 is stashed in the MapP->FuncADP of the container.  Finally, when the
//	 container is done, ITS CBP will simply copy its own MapP->FuncADP into
//	 its own CBP.
//
// NOTE: GOTO can jump forward as well as back, so it CANNOT be checked until
//	 the entire func is done!  SC_ASMPass is the first time GOTO ops can
//	 be checked... important to catch jumping in/out of MapI/K blocks as
//	 these will place a few values on the RunStack.

SC_CodeBlockPointer	SC_CodeBlockMake(SC_VMPointer VMP, Uns32 CodeLen, Uns32 CCount, Uns32 FCount)
{
    Uns32		Len;
    SC_CodeBlockPointer	CBP;
    
    // Len = Bytes of Code, Long aligned.  Then add VDescRecs for each closure record.
    // NOTE:  CodeBytes comes in long aligned, assume sizeof(SC_CodeBlockRecord) is also.
    Len = sizeof(SC_CodeBlockRecord) + CodeLen + (CCount * sizeof(SC_VDescRecord));
    // LL Align, add in FuncArr, then convert Len to # of LL.
    Len = SC_LLALIGN(Len) + (FCount * sizeof(void*));
    CBP = (SC_CodeBlockPointer)SC_ChunkNew(VMP, Len, &Len);

    // Comes in all zero-ed.
    CBP->Type = SC_CHUNK_CODE;
    CBP->State = SC_NOSTATE;

    CBP->RefCount = 1;					// Assumes one reference!
    CBP->BlockLen = Len;
    CBP->CodeLen = CodeLen;
    CBP->FuncCount = FCount;
    CBP->CCount = CCount;

    return CBP;
}

    // SC_CGUpdateOldGName is called at the end of the compile cycle (SC_ASMPass).
    // Code usually defines new variables... add them to VMP->OldGNameADP for
    // subsequent TRY chunks.
    //
    // DO NOT call for SUB scripts--compile/run from a RunScript!
    //
    // If this was a regular compile/exec cycle, VMP->OldGlobP and OldGNameADP
    // will have been cleared (NULL) at the beginning of the compile/run.  If
    // this was itself a TRY (interactive) cycle, then they will have valid
    // values (assuming the script had globals)!.
    //
    // Go through the TopLexP->VDict... new GVex should be added.  Old GVex
    // (Index is 0..N-1) are inherited... 
    // VDictP has [StrHP-VexP] pairs.
    //
    // NOTE:  Relies on *NEW* GVex entries being earlier on NextPairP list.
    
    void	SC_CGUpdateOldGName(SC_VMPointer VMP, SC_LexPointer TopLexP)
    {
	Int16			OldInGCount = 0;
	SC_ArrDataPointer	ADP;
	SC_VexPointer		VexP;
	SC_PairHPointer		PairP;

	if (! TopLexP->MapP->GCount) return;

	// If already had OldGNameADP, then TopLexP->VDict inherited some GVex,
	// otherwise ALL are new!  (Will definitely have OldGlobP, even if Count is 0!)
	if (VMP->OldGNameADP) {
	    OldInGCount = (Uns16)(Uns64)VMP->OldGlobP->Count;	// Could be 0!!
	    if (TopLexP->MapP->GCount > OldInGCount)
		VMP->OldGNameADP = SC_ArrResizeData(VMP, VMP->OldGNameADP, TopLexP->MapP->GCount, 0);
	    else
		return;		// No new globals!
	} else
	    SC_ArrAllocNewData(VMP, &VMP->OldGNameADP, TopLexP->MapP->GCount, 0);

	ADP = VMP->OldGNameADP;	    
	PairP = TopLexP->VDictP->FirstPairP;
	while (PairP) {
	    VexP = (SC_VexPointer)PairP->ValueP;
	    if (VexP->Class == SC_GVEX) {

		if (VexP->Index < OldInGCount)
		    break;	// All the rest are inherited!

		// This is a *NEW* global defined by this Script Chunk.
		// PairP->KeyP is name StrHP for it!  Stash in OldGNameADP... extra Ref.
		ADP->Data[VexP->Index] = PairP->KeyP;
		SC_VARREF((SC_StrHPointer)PairP->KeyP);
	    }

	    PairP = PairP->NextPairP;
	}
    }

    // SC_CGUpdateOldDef is called at the end of the compile cycle (SC_ASMPass).
    // The new (TRY) chunk may have new DEFs... add them to the VMP->OldDefDictP.
    // The new chunk may have also *changed* some Defs... these will also be
    // changed in VMP->OldDefDictP.
    //
    // DO NOT call for SUB scripts--compile/run from a RunScript!

    void	SC_CGUpdateOldDef(SC_VMPointer VMP, SC_LexPointer TopLexP)
    {
	SC_PairHPointer		DefPairP;
	SC_DictVPointer		DefDictP;
	SC_PairHPointer		PairP;

	if (TopLexP->DDictP) {

	    if (! ((DefDictP = VMP->OldDefDictP)))		// Assign
		VMP->OldDefDictP = DefDictP = SC_DictCreateNewDict(VMP, 0);

	    PairP = TopLexP->DDictP->FirstPairP;
	    while (PairP) {
		// See if DefDictP has this....
		DefPairP = SC_DictGetPair(DefDictP, PairP->KeyP, SC_VAL_PTR);
		if (DefPairP) {
		    // Already in DefDictP... but may be different!

		    if ((DefPairP->ValType != PairP->ValType) ||
			(DefPairP->ValueP != PairP->ValueP)) {

			// Deref old ptr
			SC_VARDEREFCOND(VMP, DefPairP->ValType == SC_VAL_PTR, DefPairP->ValueP);
			// Store new values
			DefPairP->ValType = PairP->ValType;
			DefPairP->ValueP = PairP->ValueP;
			// Ref new ptr
			SC_VARREFCOND(DefPairP->ValType == SC_VAL_PTR, DefPairP->ValueP);
		    }

		} else {
		    // Not in DefDictP... make a new pair and stash in it.
		    DefPairP = SC_BigEntryNew(VMP);
		    DefPairP->KeyType = SC_VAL_PTR;
		    DefPairP->KeyP = PairP->KeyP;
		    SC_VARREF(PairP->KeyP);

		    DefPairP->ValType = PairP->ValType;
		    DefPairP->ValueP = PairP->ValueP;
		    SC_VARREFCOND(PairP->ValType == SC_VAL_PTR, PairP->ValueP);

		    SC_DictStashNewPair(VMP, DefDictP, DefPairP);
		}

		PairP = PairP->NextPairP;
	    }
	}
    }

SC_CodeBlockPointer	SC_ASMPass(SC_CGPointer CGP, SC_MapPointer MapP)
{
    SC_VMPointer	VMP = CGP->VMP;
    SC_ScriptPointer	ScriptP = CGP->ScriptP;
    SC_CodeBlockPointer CBP;		// Block being created for runtime
    SC_ByteOpPointer	BOP;		// BOP being read from MapP
    SC_ByteOpPointer	EndBOP;
    SC_ByteOpPointer	StartBOP;
    SC_LexPointer	SupFLexP;
    Uns32		OpLine;		// ByteCode is assembled here
    Uns8 *		BP;		// Points to OpLine
    Uns8 *		AP;		// Points to OpLine... for writing Args.
    Uns32 *		WriteP;		// Points into CBP->CodeArr
    Uns32		Address, Len;
    SC_VDescRecord	V;
    Int16		I;
    Int32		Rel;

    if (! SC_AuxASMPrePass(CGP, MapP)) goto ErrorOut;
    ScriptP->FuncCount += 1;		// 1 more function being defined!

    CBP = SC_CodeBlockMake(VMP, MapP->CodeBytes, MapP->CCount, MapP->FuncCount);

    CBP->Flags = (MapP->SupFLexP) ? SC_CB_NOFLAG : SC_CB_TOPFLAG;
    if (MapP->Flags & SC_MAP_ALLARGSFLAG)
	CBP->Flags |= SC_CB_ALLARGSFLAG;

    CBP->RCount = MapP->RCount;
    CBP->FuncId = MapP->FuncId;
    CBP->InCount = MapP->InCount;
    CBP->OutCount = MapP->OutCount;
    CBP->LCount = MapP->LCount;
    CBP->CodeId = MapP->CodeId;

    // TOP KonstADP and StringADP are needed in ScriptP (for indexed access to
    // Konsts and Strings).  These are just moved over from the TopLexP->MapP.

    if (MapP->KonstADP) {
	ScriptP->KonstADP = MapP->KonstADP;
	MapP->KonstADP->MasterPP = &ScriptP->KonstADP;	// Will update if moved!
	ScriptP->KonstCount = MapP->KonstCount;
	SC_DictPurge(VMP, MapP->KonstDictP, 1);		// ForceZap, no Var refs
	MapP->KonstADP = NULL;
	MapP->KonstDictP = NULL;	
    }

    if (MapP->StringADP) {
	ScriptP->StringADP = MapP->StringADP;
	MapP->StringADP->MasterPP = &ScriptP->StringADP; // Will update VMP if moved!
	ScriptP->StringCount = MapP->StringCount;
	SC_DictPurge(VMP, MapP->StringDictP, 0);	// Purge, has StrHP refs
	MapP->StringADP = NULL;
	MapP->StringDictP = NULL;
    }

    CBP->CaseDictP = MapP->CaseDictP;
    MapP->CaseDictP = NULL;

    WriteP = CBP->CodeArr;				// Start writing here
    StartBOP = BOP = (SC_ByteOpPointer)MapP->CodeADP->Data;
    EndBOP = BOP + MapP->CodeCount;

    // Start with a fresh new OpLine... SmallEndian Intel architecture here.
    // NOOPs are all 4 bytes... but, Ops can have inline padding.
    //
    // Relies on some Op specifics:
    //  -- First arg is V, K, or K3.
    //  -- Second arg is V or K2.  (K2V is written as VK2)
    //  -- Third arg is V or K. (** Even VKV is written in VVK format **)
    Address = 0L;
    OpLine = 0L;
    BP = (Uns8 *)&OpLine;
    while (BOP < EndBOP) {
	if (BOP->ByteLen) {

	    AP = BP + 1;		// Args after Op byte
	    for (I = 0; I < BOP->ArgCount; I++) {
		V = BOP->Vars[I];
		// Adjust V numbers... also write K3/I3 over whole OpLine, then write Op byte
		// If handling a JMP or BRT/F, relativize the address offset in its K3 arg!
		//
		// NOTE: V == 0 means STACK, so add 1 to all Var indices!
		//	 Var order:  [REG][IN/OUT+LOCALS][CLOSED]
		switch (V.Class) {
		    case SC_CVEX:	*AP++ = 1 + V.Index + CBP->RCount + CBP->LCount; break;
		    case SC_LVEX:	*AP++ = 1 + V.Index + CBP->RCount; break;
		    case SC_RVEX:	*AP++ = 1 + V.Index; break;
		    case SC_STACK:	*AP++ = 0; break;
		    case SC_REFER:	*AP++ = 0xFF; break;
		    case SC_KONST:	if (V.Index == 1)			// 1 byte
					    *AP++ = (Int8)BOP->KVal;
					else if (V.Index == 2)			// 2 bytes
					    *(Int16 *)AP = (Int16)BOP->KVal, AP += 2;
					else					// 3 bytes
					    *(Int32 *)BP = BOP->KVal << 8;	// Yes BP!!
					break;
		    
		    case SC_INDEX:	if (V.Index == 1)			// 1 byte
					    *AP++ = (Uns8)BOP->KVal;
					else if (V.Index == 2)			// 2 bytes
					    *(Uns16 *)AP = (Uns16)BOP->KVal, AP += 2;
					else					// 3 bytes
					    *(Uns32 *)BP = BOP->KVal << 8;	// Yes BP!
					break;

		    case SC_SYSI:	*AP++ = V.Index; break;

		    case SC_ADDR:	// K3 for JMP, BRT, BRF only... write the whole Int32 to BP.. yes BP!
					Rel = (StartBOP + BOP->KVal)->Address - BOP->Address;
					if ((Rel < -8388608) || (Rel > 8388607))
					    SC_CODENULLERROR(CGP, SC_STR_ErrorLimits);
					*(Int32 *)BP = Rel << 8;
					break;
		    
    		}   // Switch
	    }	// For

	    // Write Op Last... Otherwise K3/I3 overwrites it!
	    *BP = BOP->Op;
	    BP += BOP->ByteLen;
	    Address += BOP->ByteLen;
	    
	    // SC_AuxASMPrePass has adjusted BOP->ByteLen to pack Ops into Long!!
	    // There will **NOT** be any leftovers on OpLine at the end :-)
	    if ((Address & 0x03) == 0) {
		*WriteP++ = OpLine;
		OpLine = 0L;
		BP = (Uns8 *)&OpLine;
	    }
	}
	
	BOP += 1;
    }

    // Long aligned at this point...
    //
    // NOTE:  Container function is NOT done yet.... so cannot know how many of
    //	      each kind of variable it will have, so must describe by type!
    //
    // Write Closure info... for each Closed variable:
    //    1: What VarType in container it comes from:  L or C
    //    2: Index of SrcVar (by Class) in Container:  0..N

    if (CBP->CCount) {
	SC_VDescPointer		CVArr;
	SC_VexPointer		CVexP;
    
	CVArr = (SC_VDescPointer)WriteP;
	CVexP = MapP->CVexP;
	while (CVexP) {
	    CVArr[CVexP->Index].Class = CVexP->SrcClass;
	    CVArr[CVexP->Index].Index = CVexP->SrcIndex;

	    CVexP = CVexP->NextP;
	}
    }
    Address += CBP->CCount * sizeof(SC_VDescRecord);

    // Guaranteed to have an even length here... pad for LL align.
    if ((Len = Address & 0x07)) {
	Uns16 *		WP = (Uns16 *)((Uns8 *)CBP->CodeArr + Address);
	Address += (Len = 8 - Len);
	do *WP++ = 0; while (Len -= 2);
    }

    // Now write the FuncArr[]
    if ((Len = CBP->FuncCount)) {
	SC_CodeBlockPointer *	WP = (SC_CodeBlockPointer *)((Uns8 *)CBP->CodeArr + Address);
	SC_CodeBlockPointer *	RP = (SC_CodeBlockPointer *)(MapP->FuncADP->Data);
	do *WP++ = *RP++; while (--Len);
    }

    if (MapP->SupFLexP == NULL)				// Top func!
	ScriptP->GlobCount = MapP->GCount;

   // Done with MapP->CodeADP too!
    SC_SlabZapItem(VMP->ArrDataSP, MapP->CodeADP, MapP->CodeADP->BlockLen);
    MapP->CodeADP = NULL;

    // Stash it... needs to go into Map of superior Func... or ScriptP->TopClosP
    if ((SupFLexP = CGP->FuncLexP->MapP->SupFLexP))	// Assign
	SupFLexP->MapP->FuncADP->Data[CBP->FuncId] = CBP;
    else {
	// Finished TopFunc... make Clos for it
	ScriptP->TopClosP = SC_RunMakeClos(VMP, 0, CBP);	// IsTop

	// This TopFunc Lex will go away soon, Update VMP->OldGNameADP and
	// VMP->OldDefDictP... **ONLY IF NOT** SUB scripts.
	if (CGP->ScriptP->ScriptLevel == 0) {
	    SC_CGUpdateOldGName(VMP, CGP->FuncLexP);
	    SC_CGUpdateOldDef(VMP, CGP->FuncLexP);
	}
    }
     
    return CBP;

ErrorOut:
    return NULL;
}


// ********************************************************************************
// ********************************************************************************
// ********************************************************************************
// Run!

// SC_SysError is a low-level error... outputs a msg and dumps into the debugger.

void	SC_SysError(char * ErrStrP)
{
    Int32	I = 1;	// Generate div-by-zero exception!
    
    printf("\n   *********************************************\n");
    printf("   SYS ERROR --> %s\n", ErrStrP);
    printf("   *********************************************\n\n");

    I /= I - 1;		// GDB shows location in code!
    // raise(SIGINT);   // GDB does NOT show location!!
}

// ********************************************************************************
// RunStack

// The RunStack is an array of VarVRecords... not Ptrs, but ACTUAL RECORDS!  So
// each record takes N slots (2 currently).  RunStack is NEVER grown, so *MUST*
// be allocated as a CHUNK.
//
//     IMPORTANT ***************************************************************
//     Code can simply point to a "popped" RunStack location, assuming it will
//     not change.  But *IF* it allocates an array, it may cause other arrays to
//     move.  This would be a problem if the RunStack were allocated as a
//     regular array instead of a CHUNK.
//
// NOTE:  Code will take liberties when popping RunStack records, assume they
//	  will stay in place until the next Push.
//
// Runtime stack SHOULD NEVER overflow as it is immediately emptied.  When
// calling a FUNC, the stack is emptied when the FUNC is entered!  Args do NOT
// stay on the stack and Call/Run Frames are linked RunRecords, NOT on the stack.
//
// Theoretically, compiler may monitor worst case runtime stack demand and flag
// degenerate cases (calling FUNC-A with *many* args, last of which is a call to
// FUNC-B with *many* args.... on and on, etc.  (SC_SAFE checks at runtime!)
//
// MAP op will maintain 4 items on the stack for the duration of its iteration.
// RETURN/Break from a MAP needs to handle this.
//
// FCALL may put *MANY* items on the stack.
//     "FuncX(A, B, C, FuncY(R, S, T), D, E)"
// will call FuncY while A, B, and C are already on the stack.  But 'B' may
// contain an Array and FuncY may change it... so the array would purge!!
//
// Therefore, PUSHing a Var on the RunStack should Increment its RefCount.
// (Obvious reverse operation for POP.)
//
//     [ A design alternative would be to have a PSH+REF bytecode for
//       FCall args and MAP, otherwise simply PSH without incrementing
//       the refcount.  A flag bit could be set in the specific
//       RunStack entry to DECREMENT its RefCount when popping.  But
//       all this complexity saves very little... checking the flag on
//       every Pop is almost the same as doing a RefCount decrement. ]

void	SC_RunStackInit(SC_VMPointer VMP)
{
    SC_StackBlockPointer	SBP;
    Uns32			Len;
    Uns32			BlockLen;

    if (VMP->StackBlockP == NULL) {
	Len = sizeof(SC_StackBlockRecord) + (SC_RUNSTACKRECORDS * sizeof(SC_VarVRecord));

	SBP = (SC_StackBlockPointer)SC_ChunkNew(VMP, Len, &BlockLen);
	SBP->Type = SC_CHUNK_STAK;
	SBP->Flags = 0;
	SBP->Count = SC_RUNSTACKRECORDS;
	SBP->BlockLen = BlockLen;

	VMP->StackBlockP = SBP;
	VMP->StackCount = SC_RUNSTACKRECORDS;
	VMP->StackP = SBP->Data;
    }
    
    VMP->StackTop = 0LL;
}

// N numbers stack elts 1-based from TOP.  So GETNTH(1) means get the Top and
// GETNTH(2) means get the one before that.  StackTop points to NEW empty slot,
// so PUSH writes first then increments, and POP will decrement first then read.
//
// NOTE: VarRef when pushing and VarDeRef when popping!!

#define		SC_RUNSTACK_POP(_VMP)		    (_VMP->StackP + --_VMP->StackTop)
#define		SC_RUNSTACK_POPN(_VMP, _N)	    (_VMP->StackTop -= (_N))
#define		SC_RUNSTACK_HASN(_VMP, _N)	    (_VMP->StackTop >= (_N))
#define		SC_RUNSTACK_GETNTH(_VMP, _N)	    (_VMP->StackP +_VMP->StackTop - (_N))
#define		SC_RUNSTACK_FLUSH(_VMP, _T)	    (_VMP->StackTop = _T)

#if  SC_GC_REFCOUNT
    void	SC_RunStackFlushDeRef(SC_VMPointer VMP, Uns32 T)
    {
	SC_VarVPointer	VP;
	Int32		I;

	if (VMP->StackTop > T) {
	    I = VMP->StackTop - T;
	    VP = SC_RUNSTACK_POP(VMP);
	    while (I--) {
		SC_VARDEREFCOND(VMP, VP->ValueType == SC_VAL_PTR, VP->ValueP);
		VP -= 1;
	    }
	    VMP->StackTop = T;
	}
    }

    #define	SC_RUNSTACK_FLUSH_DEREF(_VMP, _T)	SC_RunStackFlushDeRef(_VMP, _T)
#else
    #define	SC_RUNSTACK_FLUSH_DEREF(_VMP, _T)	(_VMP->StackTop = _T)
#endif

#ifdef SC_SAFE

    SC_VarVPointer	SC_RunSafeStackPush(SC_VMPointer VMP)
    {
	SC_SAFE_CHECK(VMP->StackTop < VMP->StackCount, SC_BADRUN_Stack);
	return VMP->StackP + VMP->StackTop++;
    }

    #define	SC_RUNSTACK_PUSH(_VMP)		    SC_RunSafeStackPush(_VMP)
#else
    #define	SC_RUNSTACK_PUSH(_VMP)		    (_VMP->StackP + _VMP->StackTop++)
#endif



Uns32		SC_ScriptGetRunId(void * SMP)
{
    SC_VMPointer	VMP = (SC_VMPointer)SMP;

    return (VMP->RunScriptP) ? VMP->RunScriptP->ScriptId : 0L;
}


    // Returns Offset to '.' that designates filetype ".sc" or ".bsc".
    Int16	SC_ScriptGetFileType(char * NameP, Uns8 * IsBP)
    {
	// *FIRST* . in name designates type... can have other stuff after
	//
	// E.g. "MyFile.bsc-10.12.27" is a ".bsc" file.
	//	"MyFile.txt.sc is NOT an .sc file.
	//
	//	".sc" and ".bsc" are case INSENSITIVE.
	//	".sc" or ".bsc" followed by alphanumeric does not count

	char	C;
	char *  P;
	Int16	Offset;

	*IsBP = 0;
	P = NameP;
	while ((C = *P++)) {	// Assign
	    if (C == '.') {
		Offset = (P - NameP) - 1;

		C = *P++;
		if ((C == 'b') || (C == 'B'))
		    *IsBP = 1, C = *P++;

		if ((C != 's') && (C != 'S')) break;
		C = *P++;
		if ((C != 'c') && (C != 'C')) break;

		// Check it is NOT ".SCuba" or other alphanumeric!
		// ".bsc-asdf" or ".sc#42" is fine!
		C = *P++;
		if ( (('0' <= C) && (C <= '9')) ||
		     (('A' <= C) && (C <= 'Z')) ||
		     (('a' <= C) && (C <= 'z')) )
		     break;
		else
		    return Offset;
	    }
	}

	return 0;
    }


void		SC_ResetScript(void * SMP)
{
    SC_RunZapOldState((SC_VMPointer)SMP);
}

// DO NOT call after running a SUB script!
void	SC_ScriptZapAfterRun(SC_VMPointer VMP, SC_ScriptPointer ScriptP, Uns8 Binary)
{
    if (Binary)
	SC_RunZapOldState(VMP);
    else {
	// Glob is already saved (in SC_RunMakeScriptGlob) for next run!
	// Stash (replace) OldKonstADP and OldStrADP

	if (VMP->OldKonstADP)
	    SC_SlabZapItem(VMP->ArrDataSP, VMP->OldKonstADP, VMP->OldKonstADP->BlockLen);
	VMP->OldKonstCount = ScriptP->KonstCount;
	if ((VMP->OldKonstADP = ScriptP->KonstADP))		// Assign
	    VMP->OldKonstADP->MasterPP = &VMP->OldKonstADP;	// In case it moves!
	ScriptP->KonstADP = NULL, ScriptP->KonstCount = 0;

	if (VMP->OldStrADP)
	    SC_StrADPDeRefZap(VMP, VMP->OldStrADP, VMP->OldStrCount);
	VMP->OldStrCount = ScriptP->StringCount;
	if ((VMP->OldStrADP = ScriptP->StringADP))		// Assign
	    VMP->OldStrADP->MasterPP = &VMP->OldStrADP;		// In case it moves!
	ScriptP->StringADP = NULL, ScriptP->StringCount = 0;
	// VM->OldGNameADP was updated in SC_ASMPass
	// VM->OldDefDictP was updated in SC_ASMPass
    }

    ScriptP->GlobP = NULL;
    SC_VARDEREF(VMP, (SC_VarVPointer)ScriptP);
    VMP->RunScriptP = NULL;
}

// SC_ProcessScript is the main exported function!
// It reads and processes the SC Script in StrP.
//
// NOTE: SC_CompileScript DOES NOT know about ED Buffer gap!!

void		SC_ProcessScript(void* SMP, char *StrP, Uns32 Len, char * PathP, char* NameP,
				 Uns8 CompileOnly, Uns8 WriteBSC, Uns8 Interact)
{
    SC_VMPointer	VMP = (SC_VMPointer)SMP;

    SC_DEBUG_PARSEINIT(VMP);
    // Read and process the script!  Will Exec if not CompileOnly... May WriteBSC too!
    VMP->CompileScriptP = NULL;
    SC_CompileScript(VMP, StrP, Len, PathP, NameP, CompileOnly, WriteBSC, Interact);
}

// SC_CompileScript is the main initiating function for code generation and execution!
// CAUTION: Proper EXIT is in the middle of the function!
//
// It creates a new TopCG and places a TopTP (Token) in it.  From there it
// starts parsing and processing the script.
//
// NOTE: This can be called to compile a SUB-SCRIPT... CompileOnly *MUST* be set!
//
//	 This function is called to COMPILE or COMPILE+RUN the Top Script
//		**OR**
//	 just COMPILE a Sub-Script.  **NEVER** to RUN a Sub-Script!

Uns32	CG_ProgLen;
Uns32	CG_ProgLast;
Uns64	CG_LastTime;

#if SC_DEBUG_COMPILE_DISP

    // NO NEED for progress bar, compiler will write to *OUTPUT* pane!
    #define SC_CGCOMPILE_SETPROGBAR(_CGP, _SL, _W)
    #define SC_CGCOMPILE_UPDATEPROGBAR(_CGP)
    #define SC_CGCOMPILE_ENDPROGBAR()
    
#else
    Uns64	SC_CGCurTime(void)
    {
	struct timeval	TV;
	Uns64		USec;

	gettimeofday(&TV, NULL);
	USec = (TV.tv_sec * 1000000) + TV.tv_usec;
	return USec;
    }

    void	SC_CGCompileSetProgBar(SC_CGPointer CGP, Uns32 SourceLen, Uns8 WriteBSC)
    {
	CG_ProgLen = CG_ProgLast = 0L;

	// Allow 12.5% for writing out .bsc!
	if (WriteBSC) SourceLen += SourceLen / 8;
	if (SourceLen > SC_CGPROGSOURCELENMIN) {
	    CG_ProgLen = SourceLen;
	    ED_InitProgBar("Script Compile:");
	    CG_LastTime = SC_CGCurTime();
	}
    }

    void	SC_CGCompileUpdateProgBar(SC_CGPointer CGP)
    {
	Uns32	CurPos;
	Uns64	Diff;

	if (CG_ProgLen) {
	    CurPos = CGP->ReadStartP - CGP->ScriptStartP;
	    if (CurPos > CG_ProgLast + SC_SGPROGSOURCESTEPMIN) {
		CG_ProgLast = CurPos;
		ED_UpdateProgBar(((double)CurPos * 100) / CG_ProgLen);

		Diff = SC_CGCurTime() - CG_LastTime;
		if (Diff < 10000) {
		    usleep(10000 - Diff);
		    CG_LastTime += 10000;
		} else
		    CG_LastTime += Diff;
	    }
	}
    }

    void	SC_CGCompileEndProgBar(void)
    {
	if (CG_ProgLen) {
	    ED_UpdateProgBar(100);
	    usleep(10000);
	}
    }

    #define SC_CGCOMPILE_SETPROGBAR(_CGP, _SL, _W)	SC_CGCompileSetProgBar(_CGP, _SL, _W)
    #define SC_CGCOMPILE_UPDATEPROGBAR(_CGP)		SC_CGCompileUpdateProgBar(_CGP)
    #define SC_CGCOMPILE_ENDPROGBAR()			SC_CGCompileEndProgBar()
#endif

    void	SC_RunZapOldState(SC_VMPointer VMP)
    {
	if (VMP->OldKonstADP) {
	    SC_SlabZapItem(VMP->ArrDataSP, VMP->OldKonstADP, VMP->OldKonstADP->BlockLen);
	    VMP->OldKonstADP = NULL;
	    VMP->OldKonstCount = 0L;
	}

	if (VMP->OldStrADP) {
	    SC_StrADPDeRefZap(VMP, VMP->OldStrADP, VMP->OldStrCount);
	    VMP->OldStrADP = NULL;
	    VMP->OldStrCount = 0L;
	}

	// SlotCount in OldGNameADP is correct count of globals... it is
	// created from TopLexP->VDictP.  COMPILE will create an
	// OldGNameADP but NO OldGlobP as it did not run!
	if (VMP->OldGNameADP) {
	    SC_StrADPDeRefZap(VMP, VMP->OldGNameADP, VMP->OldGNameADP->SlotCount);
	    VMP->OldGNameADP = NULL;
	}

	if (VMP->OldGlobP) {
	    SC_RunZapGlob(VMP, VMP->OldGlobP);
	    VMP->OldGlobP = NULL;
	}

	if (VMP->OldDefDictP) {
	    SC_VARDEREF(VMP, (SC_VarVPointer)VMP->OldDefDictP);
	    VMP->OldDefDictP = NULL;
	}
    }


Int16	SC_CompileScript(SC_VMPointer VMP, char *StrP, Uns32 Len, char * PathP, char * NameP,
			 Uns8 CompileOnly, Uns8 WriteBSC, Uns8 Interact)
{
    SC_CGRecord		CG;
    SC_TokenPointer	TopTP, NewTP;
    Uns8		IsSub = !!VMP->RunScriptP;	// UsSub if (RunScriptP != NULL);

    // SC_SetCGOpEffectArr();				// Prints out CGOpEffectArr, copy and paste

    ED_ScriptCompileState();
    SC_GC_THRESHOLD(VMP);
    
    SC_CGMainInit(&CG, VMP, StrP, Len);			// CG + Top Lex (Map and Dict)
    SC_CGCOMPILE_SETPROGBAR(&CG, Len, WriteBSC);
    if (Interact) {
	CG.Flags |= SC_CGINTERACTFLAG;			// Compile for INTERACT mode!
	SC_InheritOldInfo(&CG);				// Load inherited info into TopFunc/Map
	// NOTE: Inherited DEFs are *NOT* loaded into the CG!!
	//	 There is no VEX and no way to flag them as "inherited" so they
	//	 CAN be re-defined.  Instead, the old InDDictP stays in the VMP
	//	 and Defs are inherited from it....  When the compile is done
	//	 (in ASMPass) the *NEW* Defs are written to the VMP->InDDictP!
    } else if (CG.ScriptP->ScriptLevel == 0) {
	// IGNORE (do not zap) old state if compiling SUB-SCRIPT!!
	SC_RunZapOldState(VMP);
    }
    
    TopTP = SC_TokenNew(VMP);				// Comes in initialized to all 0
    TopTP->Type = SC_START_TOKEN;
    TopTP->OpPrec = 0;					// Special "DO NOT Process" Prec!
    TopTP->OpState = SC_START_STATE;			// Beginning of expression!

    SC_DEBUG_STARTPRINT(VMP, (Interact) ? "COMPILE CHUNK" : "COMPILE", NameP, 1);

    while (1) {
	NewTP = SC_TokenNew(VMP);
        if (! SC_TokenGetNext(&CG, NewTP)) {

	    // Call CGCheckPass with CHECKEND_ACT, in case something is left over!
	    if (NewTP->Type == SC_FINALEND_TOKEN) {
		if (SC_ERR_RES == SC_CGCheckPass(&CG, NewTP, SC_CHECKEND_ACT))
		    goto CompError;
		if (ED_DidAbort())
		    goto AbortError;
		    
		// No AST to Zap... unlike main compile loop.
		// Just zap START and FINALEND tokens
		SC_SlabZapItem(VMP->TokenSP, TopTP, sizeof(SC_TokenRecord));
		SC_SlabZapItem(VMP->TokenSP, NewTP, sizeof(SC_TokenRecord));
		TopTP = NewTP = NULL;
		
		VMP->LastScriptId += 1;			// It's official
		VMP->CompileScriptP = CG.ScriptP;	// Stash here *******
		CG.ScriptP = NULL;			// Clear out of CG
		if (NameP) {
		    VMP->CompileScriptP->NameSHP = SC_StrGetEntry(VMP, NameP, strlen(NameP));
		    VMP->CompileScriptP->PathSHP = SC_StrGetEntry(VMP, PathP, strlen(PathP));
		}

		if (WriteBSC) {
		    // Write .bsc output file
		    Int32	FD;

		    SC_CGCOMPILE_UPDATEPROGBAR(&CG);
			FD = SC_RunOpenWriteBSC(VMP->CompileScriptP);
			if (FD == -1) {
			    SC_RunErrorInfoStr = "   WriteBSC: Could not create/open file.";
			    goto WriteError;
			}
			if (SC_RunWriteBSC(VMP, FD, VMP->CompileScriptP)) {
			    close(FD);
			    SC_RunErrorInfoStr = "   WriteBSC: Could not write file.";
			    goto WriteError;
			}
		    close(FD);
		    if (ED_DidAbort()) goto AbortError;		    
		}
		
		SC_CGCOMPILE_ENDPROGBAR();
		SC_GC_STEP(VMP);
		if (CompileOnly) {
		    // Proper exit after compile!
		    SC_CGZap(&CG);
		    if (IsSub)
			ED_ScriptRunState();
		    else
			ED_ScriptNoState((WriteBSC) ? "\".bsc\" file written" : NULL);
		    
		} else {
		    SC_ScriptPointer	ScriptP = VMP->CompileScriptP;
		    
		    // Done compiling... execute... can only be Top script here, not a SUB!
		    // (Compile and Exec are done separately for SUB-scripts!)
		    VMP->CompileScriptP = NULL;
		    VMP->RunScriptP = ScriptP;
		    ED_ScriptRunState();
			
		    SC_SAFE_CHECK(VMP->RunScriptP && VMP->RunScriptP->TopClosP, SC_STR_ErrorNoRun);
		    SC_RunMakeScriptGlob(VMP, (Interact) ? &CG : NULL, 0);		// Not sub
		    SC_RunStackInit(VMP);
		    VMP->RunScriptP->TopRunP = SC_RunMakeRunRecord(VMP, VMP->RunScriptP->TopClosP, 0, 0);
		    
		    ED_ScriptSetCurBuf(NULL);

			SC_RunExecCurFunc(VMP, 0);

		    SC_ScriptZapAfterRun(VMP, ScriptP, 0);

		    // If Interact, InGDictP [SHP - VarP] and InDDictP are up to date.
		    SC_CGZap(&CG);

		    // Now clean things up AFTER running the TOP script.
		    // Value returned from TOP is ignored... RunStack is just flushed.
		    SC_LogCleanup(0);
		    SC_RUNSTACK_FLUSH_DEREF(VMP, 0);
		    ED_ScriptNoState(NULL);
		}
		return 1;		// **** Proper EXIT ****
	    }
	    
	    goto CompError;
	}

	SC_CGCOMPILE_UPDATEPROGBAR(&CG);
	if (ED_DidAbort()) goto AbortError;

	// Read one top-level "statement" at a time!
	CG.TopOpP = TopTP;
	CG.LastArgP = NULL;
	if (SC_ERR_RES == SC_ParseExpr(&CG, NewTP, SC_BEGIN_ACT, 1))
	    goto CompError;

	#ifdef DEBUG
	    // SLOW: Look for degenerate token loops... print them out!
	    if (SC_CGTokenHasLoop(TopTP)) {
		SC_CGTokensPrint(TopTP);
		SC_SysError("Compile: Degenerate AST.");
		SC_CGZap(&CG);
		return 0;
	    }
	#endif

	// TopTP is just the START_TOKEN... Lone ";"-->leads to TopTP with no DownP!
	if (SC_ERR_RES == SC_CGCheckPass(&CG, TopTP->DownP, SC_CHECKTOP_ACT))
	    goto CompError;
	SC_DEBUG_CODEPRINT(&CG);	// Might have set Labels!	    
	SC_DEBUG_ASTPRINT(&CG, TopTP);

	CG.CallerJSetP = NULL;		// Fresh start
	if (SC_ERR_RES == SC_CGCodePass(&CG, TopTP, NULL))
	    goto CompError;

	SC_DEBUG_CODEPRINT(&CG);
	SC_CGTokenZapTree(VMP, TopTP);	// Done with it

	TopTP->DownP = TopTP->NextP = NULL;
	TopTP->Stamp = 0;
    }   // while (1)

CompError:
    SC_ReadShowSourceError(&CG);
    goto ErrorCleanup;

AbortError:
    SC_RunErrorInfoStr = "   User abort!";
WriteError:
    VMP->OutPrintFP("\n   *********************************************\n");
    VMP->OutPrintFP(SC_RunErrorInfoStr);
    VMP->OutPrintFP("\n   *********************************************\n\n");
ErrorCleanup:
    SC_CGZap(&CG);
    SC_VARDEREFCOND(VMP, VMP->CompileScriptP, (SC_VarVPointer)VMP->CompileScriptP);
    VMP->CompileScriptP = NULL;
    if (IsSub)
	ED_ScriptRunState();
    else
	ED_ScriptNoState(NULL);
    return 0;
}


// ********************************************************************************
// Sys Funcs

// SC_SysGetCallArgs returns the FULL call Arr/count of its container function.
// It should be called as "Sys.GetCallArgs()(V1, V2, V3, V4)" to get all the
// values... additional and NULL args are ignored in this call.
//
// NOTE: Assumes InArgArrP/OutArgArrP *IS* in RunP->VarArr... SC Compiler would
//	 know if this is called, so would have set SC_RUN_ALLARGSFLAG.  But SC
//	 compiler WOULD NOT know if either array will be read!
//
// NOTE: MUST VarDeRef when popping args off the stack!!
//
// DO NOT write SC_VAL_PTR with NULL... make it SC_VAL_INT with 0L;


    Int16		SC_SysAuxReturn(SC_VMPointer VMP, Uns8 ValType, void * Val)
    {
	SC_VarVPointer	VP;

	VP = SC_RUNSTACK_PUSH(VMP);
	VP->ValueType = ValType;
	VP->ValueP = Val;

	return SC_RUN_NOERR;
    }

Int16		SC_SysGetCallArgs(SC_VMPointer VMP, Uns16 InOutCount)
{
    // Out args are (InArr, InCount, OutArr, OutCount)
    Uns8		OutCount, TotCount;
    SC_RunPointer	RunP;
    SC_VarVPointer	VP;

    if (InOutCount == 0) goto ExitExit;
    
    OutCount = (InOutCount >> 8);	// Must be Uns8
    TotCount = OutCount + InOutCount;	// Must be Uns8
    RunP = VMP->RunScriptP->CurRunP;    

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    // NOTE:  OUT args are pass-by-ref... so Stack has Ptr to Var!
    switch ((OutCount < 4) ? OutCount : 4) {
	case 4: // Out Count
	    VP = SC_RUNSTACK_GETNTH(VMP, OutCount - 3);
	    SC_SAFE_CHECK((VP->ValueP == 0LL) || (VP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	    if ((VP = VP->ValueP)) {
		SC_VARDEREFCOND(VMP, VP->ValueType == SC_VAL_PTR, VP->ValueP);	// Old value!
		VP->ValueType = SC_VAL_INT;
		VP->ValueP = (void *)(Uns64)RunP->OutCount;
	    }	// Fall through
	    
        case 3: // Out Arr
	    VP = SC_RUNSTACK_GETNTH(VMP, OutCount - 2);
	    SC_SAFE_CHECK((VP->ValueP == 0LL) || (VP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	    if ((VP = VP->ValueP)) {
		SC_VARDEREFCOND(VMP, VP->ValueType == SC_VAL_PTR, VP->ValueP);	// Old value!	    
		VP->ValueType = SC_VAL_PTR;
		VP->ValueP = RunP->VarArr[RunP->VarCount+1];
		SC_VARREF((SC_VarVPointer)VP->ValueP);
	    }	// Fall through

	case 2: // In Count
	    VP = SC_RUNSTACK_GETNTH(VMP, OutCount - 1);
	    SC_SAFE_CHECK((VP->ValueP == 0LL) || (VP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	    if ((VP = VP->ValueP)) {
		SC_VARDEREFCOND(VMP, VP->ValueType == SC_VAL_PTR, VP->ValueP);	// Old value!	    
		VP->ValueType = SC_VAL_INT;
		VP->ValueP = (void *)(Uns64)RunP->InCount;
	    }	// Fall through

	case 1:	// In Arr
	    VP = SC_RUNSTACK_GETNTH(VMP, OutCount);
	    SC_SAFE_CHECK((VP->ValueP == 0LL) || (VP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	    if ((VP = VP->ValueP)) {
		SC_VARDEREFCOND(VMP, VP->ValueType == SC_VAL_PTR, VP->ValueP);	// Old value!	    	    
		VP->ValueType = SC_VAL_PTR;
		VP->ValueP = RunP->VarArr[RunP->VarCount];
		SC_VARREF((SC_VarVPointer)VP->ValueP);
	    }
	    break;

        case 0: break;
    }
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);

ExitExit:
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)((Uns64)RunP->OutCount << 8) + (Uns64)RunP->InCount);
}

// Return control (in Editor) back to user... usually to re-arrange
// windows and the like.  User will NOT be allowed to terminate ED while
// paused from a script... must exit the script first.
//
// User may alter/close script source: not a problem, script is running
// from compiled code.  User may move/close *OUTPUT* pane: also not a
// problem, it will be used if visible, made visible if not, and created
// if missing... a lot of work gets done in each VMP->OutPrintFP call!!

Int16		SC_SysPause(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8	InCount, TotCount;
    Int16	Res;

    InCount = InOutCount;		// Must be Uns8
    TotCount = InCount + (InOutCount >> 8);

    // Run the main loop, but as a sub... Res < 0 is an error code!
    ED_ScriptPauseState();
	Res = M_SubMain();
    ED_ScriptRunState();

    // User can request script exit during pause!
    // (Script Exit or Script Wipe!)
    if (Res == M_EXIT_USER) return SC_RUN_UEXIT;
    //else M_EXIT_NORMAL from "script-resume" command!

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)1LL);
}

// Query user on Cur Pane
//
// In args
//	PromptStr	Required (Str to show in Echo line)
//	ChoiceStr	Required (Choice chars, no dupes, all lowercase)
//
// Returns:
//	Index of ResStr char (1..N) if selected by user
//	0 if no choice (or selection aborted), then issued Script-resume
//
// NOTE: Calls EditorAskCurPane and SubMain to process interactions.  There will
//	 be a slight update lag if the Script does an immediate write.  Remedy
//	 would be for EditorAskCurPane *ITSELF* to write!
//
// NOTE: PromptStr for user SHOULD INCLUDE permissible responses
//	 E.g.  "Copy selection? [YN.!]:> " where "yn.!" is CharStr

Int16		SC_SysEdQueryMC(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount, TotCount;
    SC_VarVPointer	VarP;
    Int32		Res;
    SC_StrHPointer	PromptSHP, ChoiceSHP;

    InCount = InOutCount;
    TotCount = InCount + (InOutCount >> 8);

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);    
    if (InCount < 2)
	goto ErrorOut;

    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    if ((VarP->ValueType != SC_VAL_PTR) ||
	! ((PromptSHP = VarP->ValueP)) ||	// Assign
	(PromptSHP->Type != SC_ENTRY_STR))
	goto ErrorOut;

    VarP++;
    if ((VarP->ValueType != SC_VAL_PTR) ||
	! ((ChoiceSHP = VarP->ValueP)) ||	// Assign
	(ChoiceSHP->Type != SC_ENTRY_STR))
	goto ErrorOut;

    ED_ScriptPauseState();
	// Query the editor... 
	ED_EditorAskCurPane(PromptSHP->StrDataP->Data, ChoiceSHP->StrDataP->Data);
	Res = M_SubMain();
    ED_ScriptRunState();

    // Normal returns
    if (Res == M_EXIT_NORMAL) {
	// DeRef and flush *ALL* args... even unwanted ones!
	SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
	// Push Reply on stack, return error code.
	return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)(Int64)ED_EditorGetReply());
    }

    // Error returns!
    if (Res == M_EXIT_USER)
	return SC_RUN_UEXIT;
    return SC_RUN_QUIETEXIT;

ErrorOut:

	SC_RunErrorInfoStr = "Ed.QueryMC: Bad args.";
	return SC_RUN_BADARG;
}

    typedef Int16	(*SC_GetPkgFlagsFP)(Uns32 SCF, Uns32 SCMask, Uns8* FP);

    // SCF ----> Script Flags coming in
    // SCMask -> Permissible Script flags
    // FP -----> INTERNAL flags set and returned!
    //
    // Returns 1 if happy, 0 if problem
    Int16	SC_SysAuxGetStrPkgFlags(Uns32 SCF, Uns32 SCMask, Uns8 * FP)
    {
	Uns8	F = 0;
	Uns32	R;
	
	if (SCF & ~SCMask) return 0;			// Extraneous bits

	if (SCMask & SC_STR_CHAR) {
	    R = SCF & SC_STRMASK_CHAR;
	    if (R == SC_STRMASK_CHAR) return 0;		// Both bits, Conflict!
	    if (R == SC_STR_CHAR) F = SC_STR_CHARFLAG;	// Set
	}

	if (SCMask & SC_STR_LEN) {
	    R = SCF & SC_STRMASK_LEN;
	    if (R == SC_STRMASK_LEN) return 0;		// Conflict
	    if (R == SC_STR_LEN) F |= SC_STR_LENFLAG;	// Set
	} else

	if (SCMask & SC_STR_LOC) {
	    R = SCF & SC_STRMASK_LOC;
	    if (R == SC_STRMASK_LOC) return 0;		// Conflict
	    if (R == SC_STR_ZUL) F |= SC_STR_ZULFLAG;	// Set
	}
    
	*FP = F;
	return 1;
    }

    // SCF ----> Script Flags coming in
    // SCMask -> Permissible Script flags
    // FP -----> INTERNAL flags set and returned!
    //
    // Returns 1 if happy, 0 if problem
    Int16	SC_SysAuxGetEdPkgFlags(Uns32 SCF, Uns32 SCMask, Uns8 * FP)
    {
	Uns8	F = 0;
	Uns32	R;
	
	if (SCF & ~SCMask) return 0;			// Extraneous bits

	if (SCMask & SC_ED_CHAR) {
	    R = SCF & SC_EDMASK_CHAR;
	    if (R == SC_EDMASK_CHAR) return 0;		// Conflict!
	    if (R == SC_ED_CHAR) F = SC_ED_CHARFLAG;	// Set
	}

	if (SCMask & SC_ED_LEN) {
	    R = SCF & SC_EDMASK_LEN;
	    if (R == SC_EDMASK_LEN) return 0;		// Conflict
	    if (R == SC_ED_LEN) F |= SC_ED_LENFLAG;	// Set
	}

	if (SCMask & SC_ED_SHOW) {
	    R = SCF & SC_EDMASK_SHOW;
	    if (R == SC_EDMASK_SHOW) return 0;		// Conflict
	    if (R == SC_ED_SHOW) F |= SC_ED_SHOWFLAG;	// Set
	}

	*FP = F;
	return 1;
    }

    // Gets BufP, L1, L2, and Flags (Optional)... returns BufP, P1, and P2 in BytePos!
    //
    // NOTE: *RELIES* on LenFlag NOT being default... must be explicitly set.
    //	     If the Flag is set, then L2 was read, not just a default.
    //	     L2 can be -1 as a valid Len... but in #POS mode, -1 means LastPos!
    //
    // If BufPP is null, then DOES NOT get BufP from args!
    // BufP == 0LL means get "cur" ScriptBuf-- or *Output* if no "cur" buf!
    //
    // Returns 1 if success, 0 if fail!
    //
    // NOTE: Called from ED and STR pkgs, uses IsStrPkg to differentiate.
    //	     Different Flag/Mask bits (safer than requiring SAME flags/Masks)!
    
    Int16	SC_SysAuxGetRangeArgs(SC_VarVPointer VarP, Uns8 InCount,
				      void** BufPP, Int32 * P1P, Int32 * P2P, Uns8 * FlagsP,
				      Uns8 IsStrPkg)
    {
	void *			BufP;
	Int64			L1, L2;
	Uns8			Flags;
	Int32			P1, P2;
	Int32			LastPos;
	SC_GetPkgFlagsFP	GetFlagsFP;
	Uns32			SCMask;
	Uns8			LenFlag, CharFlag;

	if (IsStrPkg) {
	    GetFlagsFP = SC_SysAuxGetStrPkgFlags;
	    SCMask = SC_STRMASK_CHAR | SC_STRMASK_LEN;
	    LenFlag = SC_STR_LENFLAG, CharFlag = SC_STR_CHARFLAG;
	} else {
	    GetFlagsFP = SC_SysAuxGetEdPkgFlags;
	    SCMask = SC_EDMASK_CHAR | SC_EDMASK_LEN;
	    LenFlag = SC_ED_LENFLAG, CharFlag = SC_ED_CHARFLAG;
	}

	// Get BufP first
	if (BufPP) {
	    if (InCount) {
		if (VarP->ValueType != SC_VAL_INT) goto ErrorOut;
		if ((BufP = VarP->ValueP)) {			// Assign
		    if (! ED_BufferCheckPtr(BufP)) goto ErrorOut;
		} else
		    BufP = ED_ScriptGetCurBuf(1);		// "Cur" Script BufP or *Output*

		--InCount, ++VarP;
		
	    } else
		BufP = ED_ScriptGetCurBuf(1);			// "Cur" Script BufP or *Output*

	    *BufPP = BufP;
	} else
	    BufP = ED_ScriptGetCurBuf(1);			// "Cur" Script BufP or *Output*

	LastPos = ED_BufferGetLastPos(BufP);
	Flags = 0;
	L1 = 0, L2 = -1;					// Assume defaults

	if (InCount) {
	    if (VarP->ValueType != SC_VAL_INT) goto ErrorOut;
	    L1 = (Int64)VarP->ValueP;
	    if ((L1 < 0) || (L1 > LastPos)) goto ErrorOut;
	    --InCount, ++VarP;
	}

	if (InCount) {
	    if (VarP->ValueType != SC_VAL_INT) goto ErrorOut;
	    L2 = (Int64)VarP->ValueP;
	    --InCount, ++VarP;
	}

	if (InCount && (VarP->ValueType == SC_VAL_INT))
	    if (! (*GetFlagsFP)((Uns32)(Int64)VarP->ValueP, SCMask, &Flags))
		goto ErrorOut;

	// Have everything, now process L1 and L2, check LenFlag first.
	// NOTE: L2 will have read a REAL value IFF LenFlag--not just default!
	if (Flags & LenFlag) {
	    L2 = L1 + L2;
	    if (L2 < 0) L2 = 0;
	}

	// L1 and L2 are Positions now (Char or Byte)!
	// LastPos is upper bound if BytePos, CharPos can only be less.
	if ((L2 == -1) || (L2 > LastPos))
	    L2 = LastPos;
	else if ((L2 < 0) || (L2 == L1)) goto ErrorOut;

	if (L1 < L2) P1 = (Int32)L1, P2 = (Int32)L2;
	else	     P1 = (Int32)L2, P1 = (Int32)L1;

	if (Flags & CharFlag) {
	    Int32	NChars = P2 - P1;
	    P1 = ED_BufferPosDelta(BufP, 0, P1);
	    if (P1 == -1) goto ErrorOut;
	    P2 = ED_BufferPosDelta(BufP, P1, NChars);
	    if (P2 == -1) P2 = LastPos;
	} else {
	    P1 = ED_BufferCheckPos(BufP, P1, -1);
	    P2 = ED_BufferCheckPos(BufP, P2, +1);
	}

	*P1P = P1, *P2P = P2;
	if (FlagsP) *FlagsP = Flags;
	return 1;

    ErrorOut:
	return 0;
    }

    // Gets BufP + Optional L1 and Flags... returns BufP and P1
    // BufP == 0 means get current script Buf *OR* *Output* if no CUR!
    // L1 == -1, means get EndPos of buffer!
    // Flags --> #Char or #Byte (CharPos or BytePos)
    //
    // Returns 1 if success, 0 if fail
    //
    // NOTE: This function only deals with ED pkg, which is why it DOES NOT
    //	     need to separate out GetFlagsFP/SCMask function!!
    Int16	SC_SysAuxGetPosArgs(SC_VarVPointer VarP, Uns8 InCount, void** BufPP, Int32 * P1P, Uns8 * GotValP)
    {
	void *	BufP;
	Int64	L1;
	Uns8	Flags;
	Int32	P1;
	Int32	LastPos;
	Uns8	GotVal;

	// Get BufP
	if (InCount) {
	    if (VarP->ValueType != SC_VAL_INT) goto ErrorOut;
	    if ((BufP = VarP->ValueP)) {					// Assign
		if (! ED_BufferCheckPtr(BufP)) goto ErrorOut;
	    } else
		BufP = ED_ScriptGetCurBuf(1);
	    --InCount, ++VarP;
	} else
	    BufP = ED_ScriptGetCurBuf(1);

	LastPos = ED_BufferGetLastPos(BufP);
	L1 = 0LL;
	Flags = 0;
	P1 = 0, GotVal = 0;

	if (InCount && (VarP->ValueType == SC_VAL_INT)) {
	    L1 = (Int64)VarP->ValueP;
	    GotVal = 1;
	    --InCount, ++VarP;
	    if (InCount && (VarP->ValueType == SC_VAL_INT))
		if (! SC_SysAuxGetEdPkgFlags((Uns32)(Int64)VarP->ValueP, SC_EDMASK_CHAR, &Flags))
		    goto ErrorOut;

	    if ((L1 > LastPos) || (L1 == -1)) L1 = LastPos;
	    else if (L1 < 0) goto ErrorOut;

	    if (Flags & SC_ED_CHARFLAG) {
		P1 = ED_BufferPosDelta(BufP, 0, (Int32)L1);
		if (P1 == -1) goto ErrorOut;

	    } else
		P1 = ED_BufferCheckPos(BufP, (Int32)L1, -1);	// Check not Mid-UTF
	}

	*BufPP = BufP;
	*P1P = P1;
	if (GotValP) *GotValP = GotVal;
	return 1;

    ErrorOut:
	return 0;
	    
    }

    Int16		SC_SysAuxShowBuf(SC_VMPointer VMP, Uns16 InOutCount, Int16 DoRange, Int16 JustPos)
    {
	Uns8		InCount, TotCount;
	SC_VarVPointer	VarP;
	void *		BufP;
	Int32		P1, P2;
	Uns8		GotP1;

	// DoRange --> from Ed.ShowBufSel
	// JustPos --> From Ed.SetPos... Only sets position, does not SHOW buf

	InCount = InOutCount;
	TotCount = InCount + (InOutCount >> 8);
	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);    

	if (InCount) {
	    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);	    
	    if (DoRange) {
		if (! SC_SysAuxGetRangeArgs(VarP, InCount, &BufP, &P1, &P2, NULL, 0))
		    goto ErrorOut;
		ED_EditorSelectPane(BufP, P1, P2);

	    } else {
		if (! SC_SysAuxGetPosArgs(VarP, InCount, &BufP, &P1, &GotP1))
		    goto ErrorOut;
		if (GotP1) {
		    if (JustPos)
			ED_ScriptSetBufPos(BufP, P1);
		    else
			ED_EditorSelectPane(BufP, P1, P1);
		}
	    }

	} else {
	    BufP = ED_ScriptGetCurBuf(1);
	    if (DoRange)
		ED_EditorSelectPane(BufP, 0, ED_BufferGetLastPos(BufP));
	    else if (JustPos)
		ED_ScriptSetBufPos(BufP, 0);
	    else
		ED_EditorSelectPane(BufP, 0, 0);
	}

	ED_ScriptSetCurBuf(BufP);

	// DeRef and flush *ALL* args... even unwanted ones!
	SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
	// Push Reply on stack, return error code.
	return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)1LL);

    ErrorOut:
	if (DoRange)
	    SC_RunErrorInfoStr = "Ed.ShowBufSel: Bad args.";
	else if (JustPos)
	    SC_RunErrorInfoStr = "Ed.SetPos: Bad args.";
	else
	    SC_RunErrorInfoStr = "Ed.ShowBuf: Bad args.";

	return SC_RUN_BADARG;
    }
    

//  Show editor pane:  Buf + CursorPos
//	Selects BufP to write to.  (Initial default is *output*!)
//	Can be called to show the Buf and set CursorPos!
//      Often called from Mode CallBack in Match!
//
//	BufP		Required Int (Opaque ED_BufferP)
//			0 means CurScriptBuf, or *Output* if not set.
//	L1		BytePos or CharPos	(#Byte or #Char)
//	Flag		Optional (Default is #Byte + #Pos)
//	...		Ignores rest

Int16		SC_SysEdShow(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysAuxShowBuf(VMP, InOutCount, 0, 0);
}

//  Show editor pane:  Buf + Sel-Range
//	Selects BufP to write to.  (Initial default is *output*!)
//	Can be called to show the Buf and set CursorPos or Sel a range!
//      Often called from Mode CallBack in Match!
//
//	BufP		Required Int (Opaque ED_BufferP)
//			0 means CurScriptBuf, or *Output* if not set.
//	L1		BytePos or CharPos	(#Byte or #Char)
//	L2		BytePos or CharPos	(#Byte or #Char) + #Pos
//			ByteLen or CharLen	(#Byte or #Char) + #Len
//	Flag		Optional (Default is #Byte + #Pos)
//	...		Ignores rest

Int16		SC_SysEdShowSel(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysAuxShowBuf(VMP, InOutCount, 1, 0);
}

// Just sets Cursor Pos in Buf.
// BufP, L1, and Flag... all are optional.
Int16		SC_SysEdSetPos(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysAuxShowBuf(VMP, InOutCount, 0, 1);
}

    // Previously used a lot of tricks to avoid calling Libffi or similar... otherwise
    // cannot assemble a var-arg sequence for printf.  Finally broke down and went with
    // libffi when double float stopped working... easier!

    Int16 SC_SPAuxPrint(SC_VMPointer VMP, Uns16 InOutCount, SC_PrintFuncP PFP, char * NameP, Int16 GetBuf)
    {
	Uns8		InCount, TotCount;
	SC_VarVPointer	SVP;
	SC_VarVPointer	VarP, EndP;
	SC_StrHPointer	StrHP;

	Int16		Fail = 0;
	ffi_arg		L = 0;
	ffi_type **	CIFArr = NULL;
	void **		VarArr = NULL;
	void **		ArgArr = NULL;

	L = 0;
	InCount = InOutCount;				// Must be Uns8
	TotCount = InCount + (InOutCount >> 8);		// Must be Uns8
	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);    

	SVP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	if (GetBuf) {
	    void *	BufP;
	    
	    // Must get the BufP.
	    if ((InCount == 0) ||
		(SVP->ValueType != SC_VAL_INT) ||
		(((BufP = SVP->ValueP)) && !ED_BufferCheckPtr(BufP)) ||		// Assign
		! ((BufP = ED_ScriptGetCurBuf(1)))) {				// Assign
		
		snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "%s: Bad Buf spec.", NameP);
		SC_RunErrorInfoStr = SC_RunInfoS;
		return SC_RUN_BADARG;
	    }

	    ++SVP, --InCount;			// Skip over BufP arg for rest...
	    ED_ScriptBufSwapIn(BufP);		// Redirect to THIS buffer!  **********
	}

	if (InCount == 0) goto ExitExit;
	// Ready to actually print!  Need FFI to call PFP.
	// typedef Int32 (*SC_PrintFuncP)(char *FP, ...);
	{
	    ffi_cif	CIF;
	    Int16	I;
	    Uns32	Size;

	    Fail = 1;
	    Size = (Uns32)InCount * sizeof(void *);

	    // Now fill the CIFArr, VarArr, *AND* ArgArr!  The stupid thing needs 3 arrays!
	    // *ALLOCATE* all together, then partition into 3!
	    //
	    // NOTE: ****LibFFI wants ptrs to VARS not VALUES... EXTRA indirection****
	    //	     ArgArr[I] has to point to a "VAR" that contains the desired value.
	    //
	    //	     Normally, just use ValueP field of the RunStack VarP for these.  *BUT*
	    //	     Need a place to stash StrHP->StrDataP->Data and still need ValueP to
	    //	     hold StrHP so later its RefCount can be decremented.  So use VarArr
	    //	     just to hold the char* values of StrHP!
	    //	     (Cannot know how many %s... so VarArr has InCount cells.)

	    if ((CIFArr = malloc(Size * 3))) {		// 1st part is Ptrs to arg types for ffi
		VarArr = (void **)CIFArr + InCount;	// 2nd part is for char* Str data only
		ArgArr = VarArr + InCount;		// 3rd part is ptrs to "VAR" that hold values.
	    
		for (VarP = SVP, EndP = SVP+InCount, I = 0; VarP < EndP; ++VarP, ++I) {
		    if (VarP->ValueType == SC_VAL_PTR) {
			StrHP = VarP->ValueP;
			if (StrHP->Type != SC_ENTRY_STR) {
			    snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "%s: Arg %d is bad", NameP, I + 1);
			    SC_RunErrorInfoStr = SC_RunInfoS;
			    goto ExitExit;
			}
			CIFArr[I] = &ffi_type_pointer;		// char *
			VarArr[I] = StrHP->StrDataP->Data;
			ArgArr[I] = &VarArr[I];

		    } else if (VarP->ValueType == SC_VAL_INT) {
			CIFArr[I] = &ffi_type_sint64;
			ArgArr[I] = &(VarP->ValueP);

		    } else {
			CIFArr[I] = &ffi_type_double;
			ArgArr[I] = &(VarP->ValueP);
		    }
		}

		if (FFI_OK == ffi_prep_cif_var(&CIF, FFI_DEFAULT_ABI, 1, InCount, &ffi_type_sint, CIFArr)) {
		    ffi_call(&CIF, FFI_FN(PFP), &L, ArgArr);
		    Fail = 0;
		}
	    }
	}

    ExitExit:

	if (GetBuf) ED_ScriptBufSwapOut();	// Redirect to old buffer!  **********
	if (CIFArr) free(CIFArr);
	if (Fail) return SC_RUN_INTERNAL;
	
	// DeRef and flush *ALL* args... even unwanted ones!
	SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
	// Push result value on stack, return error code.
	return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)(Int64)L);
    }

Int16		SC_SysPrintOut(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SPAuxPrint(VMP, InOutCount, VMP->OutPrintFP, "Sys.Print", 0);
}


Int16		SC_SysBufPrint(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SPAuxPrint(VMP, InOutCount, VMP->CurPrintFP, "Ed.Print", 1);
}

// Choose editor BufP (for Write/Print) and set (optional) CursorPos
//
//	BufP	Required Int (Opague ED_BufferP)
//	Pos	Optional int (Positions Cursor) -1 means end of buffer
//	Flags   Optional #char or #byte position
//
// NOTE: BufP of 0 means CUR script Buf. if there is one!

Int16		SC_SysEdSetCurBuf(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount, TotCount;
    SC_VarVPointer	VarP;
    void *		BufP;
    Int32		Pos;
    Uns8		GotPos;

    InCount = InOutCount;
    TotCount = InCount + (InOutCount >> 8);

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);    
    if (InCount < 1)
	goto ErrorOut;
    
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    if (! SC_SysAuxGetPosArgs(VarP, InCount, &BufP, &Pos, &GotPos))
	goto ErrorOut;

    if (GotPos) ED_ScriptSetBufPos(BufP, Pos);
    ED_ScriptSetCurBuf(BufP);
    
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push Reply on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)1LL);

ErrorOut:

    SC_RunErrorInfoStr = "Ed.SetCurBuf: Bad args.";
    return SC_RUN_BADARG;

}

// Just like SysPrint, only into a Str!
// Returns the String... so first OUT arg (if there) will get StrLen

Int16		SC_StrPrint(SC_VMPointer VMP, Uns16 InOutCount)
{
    #define		SC_BUFLEN	16

    Uns8		InCount, OutCount, TotCount;
    SC_VarVPointer	VarP, EndP;
    SC_StrDataPointer	SDP = NULL;
    SC_StrHPointer	SHP = NULL;
    Int16		I;
    char		Buf[SC_BUFLEN];		// Temp print Buf

    Int16		Fail = 0;
    ffi_arg		SLen = 0;
    ffi_cif		CIF;
    Uns32		Size;
    ffi_type **		CIFArr = NULL;
    void **		VarArr = NULL;
    void **		ArgArr = NULL;

    SHP = NULL;
    InCount = InOutCount;			// Must be Uns8
    OutCount = InOutCount >> 8;			// Must be Uns8
    TotCount = InCount + OutCount;
    if (InCount == 0) goto ExitExit;		// Might have only OUT arg!
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    // Uses libffi.... see SC_SPAuxPrint for details.
    // Add 2 slots because snprintf has 2 args before first INPUT (char * format)!
    // Allocate all 3 arrays in ONE big malloc.
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    Fail = 1;
    Size = ((Uns32)InCount + 2) * sizeof(void *);

    if ((CIFArr = malloc(Size * 3))) {		// 1st part is Ptrs to arg types for ffi (InCount + 2)
	VarArr = (void **)CIFArr + InCount + 2;	// 2nd part is for char* Str data only (InCount + 2)
	ArgArr = VarArr + InCount + 2;		// 3rd part is ptrs to "VAR" that hold values (InCount + 2)

	CIFArr[0] = &ffi_type_pointer;		// Dest location for Str
	CIFArr[1] = &ffi_type_sint64;		// Str size limit

	for (EndP = VarP+InCount, I = 2; VarP < EndP; ++VarP, ++I) {
	    if (VarP->ValueType == SC_VAL_PTR) {
		SC_StrHPointer	StrHP = VarP->ValueP;
		if (StrHP->Type != SC_ENTRY_STR) {
		    snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "Str.Print: Arg %d is bad", I - 1);
		    SC_RunErrorInfoStr = SC_RunInfoS;
		    goto ExitExit;
		}
		CIFArr[I] = &ffi_type_pointer;		// char *
		VarArr[I] = StrHP->StrDataP->Data;
		ArgArr[I] = &VarArr[I];

	    } else if (VarP->ValueType == SC_VAL_INT) {
		CIFArr[I] = &ffi_type_sint64;
		ArgArr[I] = &(VarP->ValueP);

	    } else {
		CIFArr[I] = &ffi_type_double;
		ArgArr[I] = &(VarP->ValueP);
	    }
	}

	if (FFI_OK == ffi_prep_cif_var(&CIF, FFI_DEFAULT_ABI, 3, 2 + InCount, &ffi_type_sint, CIFArr)) {
	    Fail = 0;

	    // Ready for first call!
	    ArgArr[0] = &VarArr[0], VarArr[0] = Buf;
	    ArgArr[1] = &VarArr[1], VarArr[1] = (void *)SC_BUFLEN;
	    ffi_call(&CIF, FFI_FN(snprintf), &SLen, ArgArr);

	    // Now we have the real length.  Maybe the text too, if it fit in the Buf!
	    SDP = SC_StrStashAlloc(VMP, SLen);		// Will alloc 1 extra byte
	    SDP->StrLen = SLen;
	    if (SLen <= SC_BUFLEN) {
		// It is all in the buf, just copy
		memcpy(SDP->Data, Buf, SLen);
		SDP->Data[SLen] = 0;
	    } else {
		// Did not fit, will print directly into SDP->Data!
		VarArr[0] = SDP->Data;
		VarArr[1] = (void *)(Int64)(Int32)(SLen + 1);
		ffi_call(&CIF, FFI_FN(snprintf), &SLen, ArgArr);
	    }

	    SC_DEBUG_CHECKSLAB(VMP->StrDataSP);
	    // Get new entry, or delete SDP and re-use (++RefCount) previously hashed version.
	    SHP = SC_StrGetDataEntry(VMP, SDP);
	}
    }

ExitExit:

    if (CIFArr) free(CIFArr);
    if (Fail) return SC_RUN_INTERNAL;
    
    // Write SLen into first OUT arg, if there is one!
    if (OutCount) {
	VarP = SC_RUNSTACK_GETNTH(VMP, OutCount);
	SC_SAFE_CHECK((VarP->ValueP == 0LL) || (VarP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	if ((VarP = (SC_VarVPointer)VarP->ValueP)) {		// Assign
	    SC_VARDEREFCOND(VMP, VarP->ValueType == SC_VAL_PTR, (SC_VarVPointer)VarP->ValueP);
	    VarP->ValueType = SC_VAL_INT;
	    VarP->ValueP = (void *)(Int64)SLen;
	}
    }

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    return SC_SysAuxReturn(VMP, (SHP) ? SC_VAL_PTR : SC_VAL_INT, SHP);
    
    #undef SC_BUFLEN
}

#define		SC_SYSWRITE_ADDRESS		0	// 1 shows hex address, 0 shows mem/slab offsets

Int32		SC_SysWriteVP(SC_VMPointer VMP, SC_VarVPointer VP, SC_PrintFuncP PFP)
{
    SC_StrHPointer	StrHP;
    Uns32		CodeId;    
    Int32		L = 0;
#if	! SC_SYSWRITE_ADDRESS
    Uns16		Ord;
    Uns32		Offset;
#endif

    // @@@ Must match declared SC_ENTRY_TYPE
    switch (VP->Type) {
	case SC_ENTRY_ARR:
	    #if	SC_SYSWRITE_ADDRESS
		L = PFP("Arr(0x%08lx)", (Int64)VP);
	    #else
		Ord = SC_SPPFindInSlab(VMP->VarEntrySP, VP, &Offset);
		L = PFP("Arr[%06d--%.*s:%d]", Offset, 4, (char *)&VMP->VarEntrySP->Tag, Ord);
	    #endif
	    break;

	case SC_ENTRY_STR:
	    StrHP = (SC_StrHPointer)VP;
	    L = PFP("%s", StrHP->StrDataP->Data);
	    break;

	case SC_ENTRY_DICT:
	    #if	SC_SYSWRITE_ADDRESS
		L = PFP("Dict(0x%08lx)", (Int64)VP);
	    #else
		Ord = SC_SPPFindInSlab(VMP->VarEntrySP, VP, &Offset);
		L = PFP("Dict[%06d--%.*s:%d]", Offset, 4, (char *)&VMP->VarEntrySP->Tag, Ord);
	    #endif
	    break;

	case SC_ENTRY_EXT:
	    #if SC_SYSWRITE_ADDRESS
		L = PFP("%s(0x%08lx)", SC_EXTNameArr[(*(SC_EXTVPointer)VP).ExtType], (Int64)VP);
	    #else
		Ord = SC_SPPFindInSlab(VMP->VarEntrySP, VP, &Offset);
		L = PFP("%s[%06d--%.*s:%d]", SC_EXTNameArr[(*(SC_EXTVPointer)VP).ExtType],
					     Offset, 4, (char *)&VMP->VarEntrySP->Tag, Ord);
	    #endif
	    break;

	case SC_ENTRY_ARREXT:
	    #if	SC_SYSWRITE_ADDRESS
		L = PFP("%s Arr(0x%08lx)", SC_EXTNameArr[(*(SC_ArrXTVPointer)VP).ExtType], (Int64)VP);
	    #else
		Ord = SC_SPPFindInSlab(VMP->VarEntrySP, VP, &Offset);
		L = PFP("%s Arr[%06d--%.*s:%d]", SC_EXTNameArr[(*(SC_ArrXTVPointer)VP).ExtType],
						 Offset, 4, (char *)&VMP->VarEntrySP->Tag, Ord);
	    #endif


	case SC_ENTRY_RC:
	    #if	SC_SYSWRITE_ADDRESS
		L = PFP("RunC(0x%08lx)", (Uns64)VP);
	    #else
		Ord = SC_SPPFindInSlab(VMP->BigEntrySP, VP, &Offset);
		L = PFP("RunC[%06d--%.*s:%d]", Offset, 4, (char *)&VMP->BigEntrySP->Tag, Ord);
	    #endif
	    break;

	case SC_ENTRY_PAT:
	    #if	SC_SYSWRITE_ADDRESS
		L = PFP("Pat(0x%08lx)", (Uns64)VP);
	    #else
		Ord = SC_SPPFindInSlab(VMP->BigEntrySP, VP, &Offset);
		L = PFP("Pat[%06d--%.*s:%d]", Offset, 4, (char *)&VMP->BigEntrySP->Tag, Ord);
	    #endif
	    break;

	case SC_CHUNK_EXT:
	    #if SC_SYSWRITE_ADDRESS
		L = PFP("%s(0x%08lx)", SC_EXTNameArr[(*(SC_ChunkXTVPointer)VP).ExtType], (Int64)VP);
	    #else
		Ord = SC_SPPFindInSlab(VMP->ChunkSP, VP, &Offset);
		L = PFP("%s Chunk[%06d--%.*s:%d]", SC_EXTNameArr[(*(SC_ArrXTVPointer)VP).ExtType],
			 Offset, 4, (char *)&VMP->ChunkSP->Tag, Ord);
	    #endif
	    break;

	case SC_CHUNK_CLOS:
	    CodeId = (*(SC_CodeBlockPointer)(*(SC_ClosPointer)VP).CBP).CodeId;
	    #if SC_SYSWRITE_ADDRESS
		L = PFP("Func(%d) Clos(0x%08lx)", CodeId, (Int64)VP);
	    #else
		Ord = SC_SPPFindInSlab(VMP->ChunkSP, VP, &Offset);
		L = PFP("Func(%d) Clos[%06d--%.*s:%d]", CodeId,
			 Offset, 4, (char *)&VMP->ChunkSP->Tag, Ord);
	    #endif
	    break;

	case SC_CHUNK_SCRPT:
	    #if SC_SYSWRITE_ADDRESS
		L = PFP("Script \"%s\" (0x%08lx)", (*(SC_ScriptPointer)VP).NameSHP->StrDataP->Data, (Int64)VP);
	    #else
		Ord = SC_SPPFindInSlab(VMP->ChunkSP, VP, &Offset);
		L = PFP("Script \"%s\" [%06d--%.*s:%d]",
			(*(SC_ScriptPointer)VP).NameSHP->StrDataP->Data,
			Offset, 4, (char *)&VMP->ChunkSP->Tag, Ord);
	    #endif
	    break;

	default:
	    L = PFP("Error");
    }

    return L;
}

    Int16	SC_SysWriteAux(SC_VMPointer VMP, Uns16 InOutCount, SC_PrintFuncP PFP, Int16 GetBuf, Int16 NewLine)
    {
	Uns8		TotCount;
	Uns8		InCount;
	SC_VarVPointer	VarP;
	Int16		I;
	Int32		L;

	L = 0;
	// Print value of all IN args, in sequence.
	InCount = InOutCount;			// Must be Uns8
	TotCount = InCount + (InOutCount >> 8);	// Must be Uns8
	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

	VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	if (GetBuf) {
	    void *	BufP;

	    // Must get the BufP
	    if ((InCount == 0) ||
		(VarP->ValueType != SC_VAL_INT) ||
		(((BufP = VarP->ValueP)) && !ED_BufferCheckPtr(BufP)) ||
		! ((BufP = ED_ScriptGetCurBuf(1)))) {

		SC_RunErrorInfoStr = "Ed.BufWrite: Bad Buf spec.";
		return SC_RUN_BADARG;
	    }

	    ++VarP, --InCount;			// Skip over BufP arg for rest...
	    ED_ScriptBufSwapIn(BufP);		// Re-direct to THIS buffer!  **********
	}

	for (I = 0; I < InCount; ++I, ++VarP) {
	    switch (VarP->ValueType) {
		case SC_VAL_INT:
		    L += PFP("%ld", (Int64)VarP->ValueP);
		    break;

		case SC_VAL_FLT:
		    L += PFP("%lf", *(double *)&VarP->ValueP);
		    break;

		case SC_VAL_PTR:
		    L += SC_SysWriteVP(VMP, VarP->ValueP, PFP);
		    break;
	    }
	}

	if (NewLine) L += PFP("\n");

	if (GetBuf)
	    ED_ScriptBufSwapOut();	// Redirect to old buffer!  **********

	// DeRef and flush *ALL* args... even unwanted ones!
	SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);

	// Push result value on stack, return error code.
	return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)(Int64)L);
    }

Int16		SC_SysWriteOut(SC_VMPointer VMP, Uns16 InOutCount)
{
    // Looks similar to SC_SysBufWrite *BUT* different PrintFP!!
    return SC_SysWriteAux(VMP, InOutCount, VMP->OutPrintFP, 0, 0);
}

Int16		SC_SysWriteLineOut(SC_VMPointer VMP, Uns16 InOutCount)
{
    // Looks similar to SC_SysBufWrite *BUT* different PrintFP!!
    return SC_SysWriteAux(VMP, InOutCount, VMP->OutPrintFP, 0, 1);
}


Int16		SC_SysBufWrite(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysWriteAux(VMP, InOutCount, VMP->CurPrintFP, 1, 0);
}

Int16		SC_SysBufWriteLine(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysWriteAux(VMP, InOutCount, VMP->CurPrintFP, 1, 1);
}

// Delete text in Buf
// IN args:
//	BufP	Editor Buffer (Comes in as (Int64) of void*)
//		0 means "cur" Script Buf, or use *Output* if no "cur".
//	L1	BytePos or CharPos	(#Byte or #Char)
//	L2	BytePos or CharPos	(#Byte or #Char) + #Pos
//		ByteLen or CharLen	(#Byte or #Char) + #Len
//	Flag	Optional (defaults to #Byte + #Pos)
//
// OUT args:
//	...	Ignores all
//
// Returns:
//	Count	Bytes deleted (Can call Ed.GetCLen for Char len)
Int16		SC_SysEdDel(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount, TotCount;
    SC_VarVPointer	VarP;
    void *		BufP;
    Int32		P1, P2;
    Int32		Count;

    InCount = InOutCount;				// Must be Uns8
    TotCount = InCount + (InOutCount >> 8);		// Must be Uns8
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    if (! SC_SysAuxGetRangeArgs(VarP, InCount, &BufP, &P1, &P2, NULL, 0))
	goto ErrorOut;

    ED_ScriptBufSwapIn(BufP);
	Count = ED_ScriptBufDelete(P1, P2);	    
    ED_ScriptBufSwapOut();

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result SHP on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)(Int64)Count);

ErrorOut:
    SC_RunErrorInfoStr = "Ed.Del: Bad args.";
    return SC_RUN_BADARG;
}

// Save/Save-As Buf
// IN args:
//	BufP	Editor Buffer (0 means Cur Script Buf)
//	Str	FileName+Path: Write Buf to this File
//		Missing, NULL, or ""--> Just save Buf to its file.
//
// Returns:
//	1 if succ, 0 if fail
//
// Can be called with:
//	NO args (Save CUR Script Buf)
//	BUF only (Save this Buf)
//	BUF + STR (Save this Buf to this file, Buf==0 means Cur Script Buf)
//
// NOTE:	Saving from Script ignores:
//			File was filtered (does not ask permission)
//			File was not modified (saves anyway)
//		Write from Script ignores:
//			Duplicate file exists (does not ask permission)
//
// FUTURE:	Interactive version... Asks user for filename (given default)

Int16		SC_SysEdSaveFile(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount, TotCount;
    SC_VarVPointer	VarP;
    void *		BufP;
    SC_StrHPointer	SHP;
    Int16		Res;

    InCount = InOutCount;
    TotCount = InCount + (InOutCount >> 8);
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    SHP = NULL;
    if (InCount) {
	VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);	    
	if ((VarP->ValueType != SC_VAL_INT) ||
	    ! ((BufP = VarP->ValueP) || (BufP = ED_ScriptGetCurBuf(1))) ||	// Assign
	    ! ED_BufferCheckPtr(BufP))
	    goto ErrorOut;

	if (InCount > 1) {
	    VarP++;
	    if ((VarP->ValueType != SC_VAL_PTR) ||
		! ((SHP = (SC_StrHPointer)VarP->ValueP)) ||			// Assign
		(SHP->Type != SC_ENTRY_STR) ||
		! SHP->StrDataP->StrLen)
		SHP = NULL;
	}

    } else
	BufP = ED_ScriptGetCurBuf(1);

    Res = 0;
    if (BufP) {
	Res = (SHP) ? ED_ScriptWriteFile(BufP, SHP->StrDataP->Data)
		    : ED_ScriptSaveFile(BufP);
    }

    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)(Int64)Res);

ErrorOut:
    SC_RunErrorInfoStr = "Ed.SaveFile: Bad args.";
    return SC_RUN_BADARG;
}

// SC_StrWrite works just like SC_SysWrite, only writes to a string!

#define	SC_STRW_INITSIZE	127	// StrStashAlloc will add 1 !!!
#define SC_STRW_GROWSIZE	128

    SC_VMPointer	SC_STRW_VMP;
    SC_StrDataPointer	SC_STRW_SDP;
    Uns32		SC_STRW_UsedLen;

    void	SC_STRW_Begin(SC_VMPointer VMP)
    {
	// SC_STRW_SDP->StrLen shows capacity - 1, always saving 1 byte for trailing /0
	// Otherwise, DEBUG checks will blow up... as they expect StrLen!!

	SC_STRW_VMP = VMP;
	SC_STRW_SDP = SC_StrStashAlloc(VMP, SC_STRW_INITSIZE);
	SC_STRW_SDP->StrLen = SC_STRW_INITSIZE;		// Store byte capacity here
	SC_STRW_SDP->MasterPP = &SC_STRW_SDP;
	SC_STRW_UsedLen = 0L;
    }

    char *	SC_STRW_MakeRoom(Int32 AddLen)
    {
	Uns32			NewLen = SC_STRW_UsedLen + AddLen;
	SC_StrDataPointer	NewSDP;

	if (NewLen > SC_STRW_SDP->StrLen) {
	    // Grow it, allow an extra margin
	    NewLen += SC_STRW_GROWSIZE;
	    NewSDP = SC_StrStashAlloc(SC_STRW_VMP, NewLen);
	    NewSDP->StrLen = NewLen;
	    NewSDP->MasterPP = &SC_STRW_SDP;
	    memcpy(NewSDP->Data, SC_STRW_SDP->Data, SC_STRW_UsedLen);
	    SC_DEBUG_CHECKSLAB(SC_STRW_VMP->StrDataSP);

	    SC_STRW_SDP->MasterPP = NULL;
	    SC_SlabZapItem(SC_STRW_VMP->StrDataSP, SC_STRW_SDP, SC_STRW_SDP->BlockLen);
	    SC_STRW_SDP = NewSDP;
	}

	return SC_STRW_SDP->Data + SC_STRW_UsedLen;
    }

    void	SC_STRW_Zap(void)
    {
	SC_STRW_SDP->MasterPP = NULL;
	SC_SlabZapItem(SC_STRW_VMP->StrDataSP, SC_STRW_SDP, SC_STRW_SDP->BlockLen);
	SC_STRW_SDP = NULL;
    }

    void	SC_STRW_End(void)
    {
	// Everything is in SC_STRW_SDP by now... but it probably has a great
	// deal of blank space.  Can shrink here, but only if enough room to
	// make a new free block (See SC_ArrResizeData).  Easier to just copy
	// to a new tight-fitting SDP and zap old.
	
	SC_StrDataPointer	NewSDP;

	NewSDP = SC_StrStashData(SC_STRW_VMP, SC_STRW_SDP->Data, SC_STRW_UsedLen);
	NewSDP->MasterPP = &SC_STRW_SDP;
	SC_STRW_Zap();
	SC_STRW_SDP = NewSDP;
    }

    Int32	SC_STRW_Print(char *FormatP, ...)
    {
	Int32	NewLen;
	va_list	Args;
	char *	WriteP;

	// Get NewLen, the bytes being written (ignores trailing \0)
	va_start(Args, FormatP);
	    NewLen = vsnprintf(NULL, 0, FormatP, Args);
	va_end(Args);

	// Make sure there is room to write it all (including trailing \0)
	WriteP = SC_STRW_MakeRoom(NewLen + 1);

	// Now write it!
	if (WriteP) {
	    va_start(Args, FormatP);
		vsnprintf(WriteP, NewLen + 1, FormatP, Args);
	    va_end(Args);
	}

	SC_STRW_UsedLen += NewLen;	// Ignore trailing \0
	return NewLen;
    }


    void	SC_SWAux(Uns8 InCount, Uns8 TotCount)
    {
	SC_VarVPointer	VarP;
	Int16		I;

	VarP = SC_RUNSTACK_GETNTH(SC_STRW_VMP, TotCount);
	for (I = 0; I < InCount; ++I, ++VarP) {
	    switch (VarP->ValueType) {
		case SC_VAL_INT:
		    SC_STRW_Print("%ld", (Int64)VarP->ValueP);
		    break;

		case SC_VAL_FLT:
		    SC_STRW_Print("%lf", *(double *)&VarP->ValueP);
		    break;

		case SC_VAL_PTR:
		    SC_SysWriteVP(SC_STRW_VMP, VarP->ValueP, SC_STRW_Print);
		    break;
	    }
	}
    }

    // First OUT var gets Str Len.
    Int16	SC_StrWriteAux(SC_VMPointer VMP, Uns16 InOutCount, Uns16 NewLine)
    {
	Uns8		TotCount, InCount, OutCount;
	SC_VarVPointer	VarP;
	SC_StrHPointer	SHP = NULL;


	// Print value of all IN args, in sequence.
	InCount = InOutCount;			// Must be Uns8
	OutCount = InOutCount >> 8;			// Must be Uns8
	TotCount = InCount + OutCount;		// Must be Uns8
	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

	if (InCount) {
	    SC_STRW_Begin(VMP);
		SC_SWAux(InCount, TotCount);
		if (NewLine) SC_STRW_Print("\n");
	    SC_STRW_End();
	    // Everything is nice and tight in SC_STRW_SDP with SC_STRW_UsedLen;
	    // SC_StrGetDataEntry may discard SC_STRW_SDP if string already in cache!
	    SHP = SC_StrGetDataEntry(VMP, SC_STRW_SDP);
	}

	if (OutCount) {
	    // Stash string len in first OUT var (if not NULL)
	    VarP = SC_RUNSTACK_GETNTH(VMP, OutCount);
	    SC_SAFE_CHECK((VarP->ValueP == 0LL) || (VarP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	    if ((VarP = (SC_VarVPointer)VarP->ValueP)) {		// Assign
		SC_VARDEREFCOND(VMP, VarP->ValueType == SC_VAL_PTR, (SC_VarVPointer)VarP->ValueP);
		VarP->ValueType = SC_VAL_INT;
		VarP->ValueP = (void *)(Int64)SC_STRW_UsedLen;	// StrLen ignoring last /0
	    }
	}

	// DeRef and flush *ALL* args... even unwanted ones!
	SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
	// Push result value on stack, return error code.
	return SC_SysAuxReturn(VMP, (SHP) ? SC_VAL_PTR : SC_VAL_INT, SHP);
    }


Int16		SC_StrWrite(SC_VMPointer VMP, Uns16 InOutCount)
{
   return SC_StrWriteAux(VMP, InOutCount, 0);
}

Int16		SC_StrWriteLine(SC_VMPointer VMP, Uns16 InOutCount)
{
   return SC_StrWriteAux(VMP, InOutCount, 1);
}


// In
//	Mix of N and Str args.
//	Initial default for N is 1.
//	Concatenante N copies of subsequent Str args...
//	New N applies to its subsequent strings.
//
//	("A", 2, "B", "C", 3, "D" 1 "E") will result in "ABCBCDDDE"!
// Out
//	L	<-- Str byte count

Int16		SC_StrForm(SC_VMPointer VMP, Uns16 InOutCount)
{

    Uns8		TotCount, InCount, OutCount;
    SC_VarVPointer	VarP;
    SC_StrHPointer	SHP = NULL;
    Int64		N;
    Uns8		StartArg, CurArg;

    InCount = InOutCount;			// Must be Uns8
    OutCount = InOutCount >> 8;			// Must be Uns8
    TotCount = InCount + OutCount;		// Must be Uns8
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    StartArg = CurArg = 0;
    N = 1;
    if (InCount) {
	SC_STRW_Begin(VMP);
	    while (CurArg < InCount) {
		VarP = SC_RUNSTACK_GETNTH(VMP, TotCount - CurArg);
		if (VarP->ValueType == SC_VAL_INT) {
		    // End of previous segment... loop if necessary
		    if (--N && (CurArg > StartArg)) {		// DEC
			CurArg = StartArg;
			continue;
		    }
		    N = (Int64)VarP->ValueP;
		    StartArg = CurArg + 1;			// Next!
		    if (StartArg == InCount) break;		// No Str!!

		} else if ((VarP->ValueType == SC_VAL_PTR) &&
			   (SHP = (SC_StrHPointer)VarP->ValueP) &&	// Not NULL
			   (SHP->Type == SC_ENTRY_STR) &&		// Not something else
			   SHP->StrDataP->Data[0]) {			// Not empty
		    SC_STRW_Print(SHP->StrDataP->Data);

	        } else
		    goto ErrorOut;

		// End of In args?  If still have N, iterate last segment.
		if ((++CurArg == InCount) && --N)	// ASSIGN, DEC
		    CurArg = StartArg;
	    }

	SC_STRW_End();
	// Everything is nice and tight in SC_STRW_SDP with SC_STRW_UsedLen;
	// SC_StrGetDataEntry may discard SC_STRW_SDP if string already in cache!
	SHP = SC_StrGetDataEntry(VMP, SC_STRW_SDP);
    }
    
    if (OutCount) {
	// Stash string len in first OUT var (if not NULL)
	VarP = SC_RUNSTACK_GETNTH(VMP, OutCount);
	SC_SAFE_CHECK((VarP->ValueP == 0LL) || (VarP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	if ((VarP = (SC_VarVPointer)VarP->ValueP)) {		// Assign
	    SC_VARDEREFCOND(VMP, VarP->ValueType == SC_VAL_PTR, (SC_VarVPointer)VarP->ValueP);
	    VarP->ValueType = SC_VAL_INT;
	    VarP->ValueP = (void *)(Int64)SC_STRW_UsedLen;	// StrLen ignoring last /0
	}
    }

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, (SHP) ? SC_VAL_PTR : SC_VAL_INT, SHP);    

ErrorOut:
    SC_RunErrorInfoStr = "Str.Form: Bad args.";
    return SC_RUN_BADARG;
}


#define		SC_STR_TIME_MAXLEN	255

Int16		SC_StrTimePrint(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		TotCount, OutCount, InCount;
    SC_StrHPointer	RetSHP, InSHP;
    SC_VarVPointer	VarP;
    Int64		TheTime;
    Uns8		Flags;
    char		Buf[SC_STR_TIME_MAXLEN + 1];
    struct tm *		TP;
    Int32		SLen;

    InCount = InOutCount;			// Must be Uns8
    OutCount = InOutCount >> 8;			// Must be Uns8
    TotCount = OutCount + InCount;		// Must be Uns8
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    RetSHP = InSHP = NULL;
    TheTime = 0LL;
    Flags = 0;
    Buf[0] = '\0';
    
    if (InCount) {
	VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	// Get Str first... could be NULL, "", or missing!
	if (VarP->ValueType == SC_VAL_PTR) {
	    InSHP = (SC_StrHPointer)VarP->ValueP;
	    --InCount, ++VarP;
	    if (InSHP) {
		if (InSHP->Type != SC_ENTRY_STR) goto ErrorOut;
		if (InSHP->StrDataP->StrLen == 0) InSHP = NULL;
	    }
	} else if ((VarP->ValueType == SC_VAL_INT) &&
		   (VarP->ValueP == 0L))
	    --InCount, ++VarP;

	if (InCount) {
	    // Get time value...
	    if (VarP->ValueType == SC_VAL_INT) {
		TheTime = (Int64)VarP->ValueP;
		--InCount, ++VarP;
	    } else
		goto DoneIn;
        }

	if (InCount && (VarP->ValueType == SC_VAL_INT))
	    if (! SC_SysAuxGetStrPkgFlags((Uns32)(Int64)VarP->ValueP, SC_STRMASK_LOC, &Flags))
		goto ErrorOut;
    }
    
DoneIn:
    if (TheTime == 0) TheTime = time(NULL);	// Defaults to NOW!
    TP = (Flags & SC_STR_ZULFLAG)
	    ? gmtime(&TheTime)			// Zulu (UTC) time
	    : localtime(&TheTime);		// Local time

    // Return Data+Time in local format if missing a format string.
    // NOTE: Hard to tell if Buf is too small... so just go with limit.
    if (InSHP == NULL)
	SLen = strftime(Buf, SC_STR_TIME_MAXLEN, "%x %X", TP);
    else
	SLen = strftime(Buf, SC_STR_TIME_MAXLEN, InSHP->StrDataP->Data, TP);

    if (SLen)
	RetSHP = SC_StrGetEntry(VMP, Buf, SLen);

    // Stash SLen in first OUT var
    if (OutCount) {
	VarP = SC_RUNSTACK_GETNTH(VMP, OutCount);
	SC_SAFE_CHECK((VarP->ValueP == 0LL) || (VarP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	if ((VarP = (SC_VarVPointer)VarP->ValueP)) {		// Assign
	    SC_VARDEREFCOND(VMP, VarP->ValueType == SC_VAL_PTR, (SC_VarVPointer)VarP->ValueP);
	    VarP->ValueType = SC_VAL_INT;
	    VarP->ValueP = (void *)(Int64)SLen;
	}
    }
    
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, (RetSHP) ? SC_VAL_PTR : SC_VAL_INT, RetSHP);

ErrorOut:
    SC_RunErrorInfoStr = "Str.PrintTime: Bad arg.";
    return SC_RUN_BADARG;
}



// OpenLog
//
// IN args:
//	StrP	Can be missing or "".  Otherwise Path/name of Log file
//		Could be a full path, just a filename, or just a path
//	Flags
//		#COMMON	    DEFAULT: Use given/default Log filename
//		#UNIQUE	 	     Append Data/Time stamp to make filename unique
//
//		#USE	    DEFAULT: Create if missing, otherwise reuse
//		#NEW		     Create if missing, replace if there
//
//		#OPENCLOSE  DEFAULT: Re-open/close the log file for each entry
//		#KEEPOPEN	     Keep open, do NOT close until Script ends
//
//		#HEADER	    DEFAULT: Write a header with (each) Log.Open
//		#NOHEAD		     Do-NOT write a header with (each) Log.Open
//
//		#REMAKE	    DEFAULT: Mid-stream will re-create file if missing
//		#REQUIRE	     Mid-stream will fail if missing
//
//		#ZULU	    DEFAULT: Use Zulu(UTC) time INSIDE Log
//		#LOCAL		     Use Local time INSIDE Log
//
// Returns:
//	1 if succ, 0 if fail
//
// Can be called with:
//	NO args (use default filename + flags)
//	StrP (Use StrP filename + default flags)
//	StrP + Flags (Use StrP filename + Flags)
//
// NOTE:  If the LOG already opened by superior, it will succeed (return 1)
//	  WITHOUT signalling an error.  But it will record a log entry noting
//	  the attempt to open a NEW log.  (The pre-existing log flags will
//	  remain in effect!)  But if Log was already opened by THIS level, it
//	  will signal an error.

#define		SC_LOG_DEF_FILENAME	"Log.txt"
#define		SC_LOG_PATHLEN		511
#define		SC_LOG_NAMELEN		NAME_MAX
#define		SC_LOG_UNILEN		14	// "_MMDDYY_hhmmss"

Int16		SC_LogOpen = 0;			// Conceptual LOG is open
Int16		SC_LogFileOpen = 0;		// Actual File is open, 1 if KeepOpen
Uns16		SC_LogOpenLevel = 0L;		// ScriptLevel that called LogOpen
char		SC_LogName[SC_LOG_NAMELEN+1];
char		SC_LogFullPath[SC_LOG_PATHLEN + SC_LOG_NAMELEN + 1];
Uns8		SC_LogFlags;
Int32		SC_LogFDesc;
Int16		SC_LogUniI;

   Int32	SC_LogEntryAuxWrite(SC_VMPointer VMP, Uns8 InCount, Uns8 TotCount, char* StrP, Uns8 IsE, Uns8 DoNL);
   
// SC_LogCleanup is called to close the log and cleanup when the script terminates.
void	SC_LogCleanup(Uns16 ScriptLevel)
{
    if (SC_LogOpen && (ScriptLevel <= SC_LogOpenLevel)) {
	if (SC_LogFileOpen) close(SC_LogFDesc);
	SC_LogOpen = SC_LogFileOpen = 0;
    }
}

    // SC_SysLogAuxUniStamp writes a Date+Time stamp to make the filename
    // unique.  Always Zulu (UTC/GMT) 24hr format--61 secs for leap years!
    void SC_SysLogAuxUniStamp(char * P)
    {
	#define SC_BUFLEN	32
	    struct tm *		TP;
	    char		Buf[SC_BUFLEN];
	    time_t		TVal;

	    // Write the UniStamp here: "_MMDDYY_hhmmss"
	    time(&TVal);
	    TP = gmtime(&TVal);
	    snprintf(Buf, 32, "_%.2d%.2d%.2d_%.2d%.2d%.2d",
			 TP->tm_mon+1, TP->tm_mday, TP->tm_year % 100,
			 TP->tm_hour, TP->tm_min, TP->tm_sec);
	    memcpy(P, Buf, SC_LOG_UNILEN);
	#undef SC_BUFLEN
    }

    // SC_SysLogAuxUniName will add a Date+Time UniStamp to the filename to
    // make it unique.  If the filename has a dot ("ThisLog.txt"), adds the
    // stamp BEFORE the dot ("ThisLog_MMDDYY_hhmmss.txt"), otherwise after the
    // filename.
    Int16 SC_SysLogAuxUniName(void)
    {
	char *	CP;
	char	C;
	Int16	Len, DotI;
	
	// Assumes there *IS* a name... find len and last '.'
	Len = 0; DotI = -1;
	CP = SC_LogName;
	while ((C = *CP++)) {			// Assign
	    Len++;
	    if (C == '.') DotI = Len - 1;	// Just before it
	}

	if (Len + SC_LOG_UNILEN > SC_LOG_NAMELEN) return 0;

	if (DotI == -1) {
	    // Put UniStamp after name... no Dot to worry about
	    SC_SysLogAuxUniStamp(SC_LogName+Len);
	    SC_LogUniI = Len;

	} else {
	    // move last .XYZ over, then put UniStamp before it.
	    memmove(SC_LogName+DotI+SC_LOG_UNILEN, SC_LogName+DotI, Len-DotI);
	    // Write Uni Stamp
	    SC_SysLogAuxUniStamp(SC_LogName+DotI);
	    SC_LogUniI = DotI;			// Stash for later revision!
	}
	
	SC_LogName[Len+SC_LOG_UNILEN+1] = '\0';
	return 1;
    }

    // SC_SysLogAuxAgainUniName is called when the "unique" filename was already
    // there!  So the system lets 1 sec elapse and creates a new UniStamp.  This
    // should only be tried once!!
    void  SC_SysLogAuxAgainUniName(void)
    {
	usleep(1000000);	// Sleep for 1 sec, so time (sec) changes!
	SC_SysLogAuxUniStamp(SC_LogName+SC_LogUniI);
    }

    // SC_SysLogAuxWriteHeader writes a short header (if #HEAD flag) to the
    // newly opened log.
    Int16 SC_SysLogAuxWriteHeader(SC_VMPointer VMP, Int16 IsNew)
    {
	char	Buf[128];
	char *	P;
	time_t	TVal;
	Int32	Len;

	time(&TVal);
	P = (IsNew) ? "****" : "\n****";

	// ctime string has 24 bytes of data followed by a /n which is NOT copied over!
	Len = snprintf(Buf, 128-1, "%s LOG OPENED: %.24s **** %.25s ****\n\n", P,
			asctime( (SC_LogFlags & SC_LOG_ZULFLAG) ? gmtime(&TVal) : localtime(&TVal) ),
			VMP->RunScriptP->NameSHP->StrDataP->Data);
	if (Len != write(SC_LogFDesc, Buf, Len)) return 0;
	return 1;
    }

    Int16 SC_SysLogAuxGetFlags(Uns32 SCF)
    {
	register Uns8	F = 0;
	register Uns32	R;

	if (SCF & ~SC_LOG_MASK) return 0;		// Extraneous bits

        R = SCF & SC_LOGMASK_UNI;
	if (R == SC_LOGMASK_UNI) return 0;		// Both bits set!
	if (R == SC_LOG_UNI) F = SC_LOG_UNIFLAG;

	R = SCF & SC_LOGMASK_NEW;
	if (R == SC_LOGMASK_NEW) return 0;
	if (R == SC_LOG_NEW) F |= SC_LOG_NEWFLAG;

	R = SCF & SC_LOGMASK_KO;
	if (R == SC_LOGMASK_KO) return 0;
	if (R == SC_LOG_KO) F |= SC_LOG_KOFLAG;

	R = SCF & SC_LOGMASK_NOH;
	if (R == SC_LOGMASK_NOH) return 0;
	if (R == SC_LOG_NOH) F |= SC_LOG_NOHFLAG;

	R = SCF & SC_LOGMASK_REQ;
	if (R == SC_LOGMASK_REQ) return 0;
	if (R == SC_LOG_REQ) F |= SC_LOG_REQFLAG;

	R = SCF & SC_LOGMASK_ZUL;
	if (R == SC_LOGMASK_ZUL) return 0;
	if (R == SC_LOG_ZUL) F |= SC_LOG_ZULFLAG;

	SC_LogFlags = F;
	return 1;
    }

Int16		SC_SysLogOpen(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount, TotCount;
    SC_VarVPointer	VarP;
    SC_StrHPointer	SHP;
    char *		StrP;
    char *		NameP;
    char *		PathP;
    Uns16		UniTryAgain = 1;
    Uns16		IsNew = 0;

    if (SC_LogOpen) {
	// Sub-scripting: If Log was opened by a superior, use it, but
	// create an entry first...  (Similar will be done on Close).
	if (SC_LogOpenLevel < VMP->RunScriptP->ScriptLevel) {
	    if (! SC_LogEntryAuxWrite(VMP, 0, 0, "Sub-Script Log opened! ****", 0, 1) ||
		! SC_LogEntryAuxWrite(VMP, 0, 0, VMP->RunScriptP->NameSHP->StrDataP->Data, 1, 0)) {
		// Special error message... as already open, or supposed to be!
		SC_RunErrorInfoStr = "Log.Write: Failed.";
		return SC_RUN_INTERNAL;
	    }
	    goto SuccessOut;
	}

	// Log was created by THIS script level, signal an error!
	goto ErrorOut;
    }

    SC_LogOpenLevel = VMP->RunScriptP->ScriptLevel;		// Level opening it
    InCount = InOutCount;					// Must be Uns8
    TotCount = InCount + (InOutCount >> 8);
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    SHP = NULL, SC_LogFlags = 0;
    if (InCount) {
	VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	
	if (VarP->ValueType == SC_VAL_PTR) {			// First Arg
	    SHP = (SC_StrHPointer)VarP->ValueP;			// Assume StrP
	    // Currently SHP cannot be NULL...
	    if (SHP) {
		if (SHP->Type != SC_ENTRY_STR) goto ArgsDone;	// NOT a StrP!!
		if (SHP->StrDataP->StrLen == 0) SHP = NULL;	// Str is ""
	    }

	    InCount--, VarP++;					// 2nd IN Arg
	}

	if ((InCount) && (VarP->ValueType == SC_VAL_INT))	// Flags!
	    if (! SC_SysLogAuxGetFlags((Uns32)(Int64)VarP->ValueP))
		goto ErrorOut;
    }

ArgsDone:
    StrP = (SHP) ? SHP->StrDataP->Data : SC_LOG_DEF_FILENAME;
    if (ED_UtilGetPath(StrP, &PathP, &NameP, NULL)) goto ErrorOut;

    strcpy(SC_LogName, NameP);				// Copy from ED memory
    // Check if it works!  (StrP has been re-assigned... its a malloc now)
    if ((StrP = realpath(PathP, NULL)))			// Assign
	free(StrP);
    else
	goto ErrorOut;

    if (SC_LogName[0] == 0) strcpy(SC_LogName, SC_LOG_DEF_FILENAME);
    // Make File name UNIQUE... try once, then again (later) if it exists!
    if ((SC_LogFlags & SC_LOG_UNIFLAG) && !SC_SysLogAuxUniName())
	goto ErrorOut;

DoTryAgain:
    // Generate full path!
    sprintf(SC_LogFullPath, "%s/%s", PathP, SC_LogName);
	//printf("\nPath->%s + Name->%s\nFull->%s\n", PathP, SC_LogName, SC_LogFullPath);

    // See if there already
    SC_LogFDesc = open(SC_LogFullPath, O_WRONLY | O_APPEND | O_SYNC, 0);
    if (SC_LogFDesc == -1) {
	// Failed to open... wasn't there!  So make it.
	SC_LogFDesc = open(SC_LogFullPath, O_CREAT | O_WRONLY | O_TRUNC | O_SYNC, S_IRWXU | S_IRWXG);
	if (SC_LogFDesc == -1) goto ErrorOut;			// Failed to create
	IsNew = 1;

    } else {
	// Opened...
	if (SC_LogFlags & SC_LOG_NEWFLAG) {
	    // Make it new... but change Uni stamp if there!
	    if (close(SC_LogFDesc)) goto ErrorOut;	// Failed to close!
	    
	    if (SC_LogFlags & SC_LOG_UNIFLAG) {
		if (UniTryAgain) {
		    // Update Uni stamp
		    SC_SysLogAuxAgainUniName();
		    UniTryAgain = 0;
		    goto DoTryAgain;
		}
		goto ErrorOut;				// NOT Unique!!
	    }

	    // Was opened, then closed.  So make it NEW by emptying it out.
	    SC_LogFDesc = open(SC_LogFullPath, O_WRONLY | O_APPEND | O_TRUNC | O_SYNC, 0);
	    IsNew = 1;
	}
    }

    if (!(SC_LogFlags & SC_LOG_NOHFLAG) && !SC_SysLogAuxWriteHeader(VMP, IsNew)) {
	close(SC_LogFDesc);
	goto ErrorOut;
    }

    fsync(SC_LogFDesc);
    SC_LogOpen = 1;					// LOG is open, not necessarily the file
    if (SC_LogFlags & SC_LOG_KOFLAG)
	SC_LogFileOpen = 1;
    else if (close(SC_LogFDesc))
	goto ErrorOut;					// Failed to close

SuccessOut:
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)(Int64)SC_LogOpen);

ErrorOut:
    SC_RunErrorInfoStr = "Log.Open: Failed or Bad args.";
    return SC_RUN_BADARG;
}

// Closes the (conceptually) open Log.  Takes no args.
// Does NOT complain if the Log was NOT opened.
// Returns 1 if it *WAS* open, 0 otherwise.
//
// NOTE:  Opening the (conceptual) LOG is not the same as opening the LOG FILE.

Int16		SC_SysLogClose(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount, TotCount;
    Int64		Res = 0;

    InCount = InOutCount;				// Must be Uns8
    TotCount = InCount + (InOutCount >> 8);
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    if (SC_LogOpen) {
	Res = 1;
	if (SC_LogOpenLevel < VMP->RunScriptP->ScriptLevel) {
	    // Just write an Entry and leave it be...
	    if (! SC_LogEntryAuxWrite(VMP, 0, 0, "Sub-Script Log closed! ****\n\n", 0, 0))
		goto ErrorOut;
	    goto SuccessOut;

	} else if (! (SC_LogFlags & SC_LOG_NOHFLAG))
	    SC_LogEntryAuxWrite(VMP, 0, 0, "**** LOG CLOSED ****\n\n", 0, 0);

	if (SC_LogFileOpen && close(SC_LogFDesc))	// Close failed!
	    goto ErrorOut;
	SC_LogOpen = SC_LogFileOpen = 0;
    }

SuccessOut:
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)Res);

ErrorOut:
    SC_RunErrorInfoStr = "Log.Close: Failed.";
    return SC_RUN_BADARG;
}


    // SC_LogEntryAuxOpenFile is called to open the LogFile during a write.
    // Call only if SC_LogFileOpen == 0!
    Int16	SC_LogEntryAuxOpenFile(SC_VMPointer VMP)
    {
	SC_LogFDesc = open(SC_LogFullPath, O_WRONLY | O_APPEND | O_SYNC, 0);
	if (SC_LogFDesc == -1) {
	    // Failed to open... wasn't there!
	    if (SC_LogFlags & SC_LOG_REQFLAG) return 0;	// #REQUIRED so fail!!

	    // Just make a new one!
	    SC_LogFDesc = open(SC_LogFullPath, O_CREAT | O_WRONLY | O_TRUNC | O_SYNC, S_IRWXU | S_IRWXG);
	    if (SC_LogFDesc == -1) return 0;		// Not there and could not make it!

	    // Just made a new one... so write header!
	    if (!(SC_LogFlags & SC_LOG_NOHFLAG) && !SC_SysLogAuxWriteHeader(VMP, 1)) {
		close(SC_LogFDesc);
		return 0;					// Could not write header
	    }
	}

	SC_LogFileOpen = 1;
	return 1;
    }

    // Assumes SC_LogOpen==1 !!!
    // Writes StrP if InCount==0.
    // StrP MUST start with "%s: " (for time stamp) and end with "\n".
    //
    // Returns bytes written... 0 if fail.
    //
    // IsE means this is an extended (2nd/3rd... line) Entry... no timestamp needed!
    Int32	SC_LogEntryAuxWrite(SC_VMPointer VMP, Uns8 InCount, Uns8 TotCount, char* StrP, Uns8 IsE, Uns8 DoNL)
    {
	Int32		Count = 0;
	Int16		I;
	Uns8		GoAgain = 1;

	// Uses SC_StrWrite innards... forms an SC_StrDataPointer as a buffer, then
	// writes to Log file and zaps the buffer.
	//
	// No IN arg results in a NULL Log msg

	SC_STRW_Begin(VMP);
	    if (DoNL)
		SC_STRW_Print("\n");
	    // Indent sub-scripts over...
	    if ((I = VMP->RunScriptP->ScriptLevel))	// Assign
		while (I--) SC_STRW_Print("* ");
		
	    if (IsE)
		SC_STRW_Print("                          ");
	    else {
		time_t		TVal = time(NULL);
		struct tm *	BrTP = (SC_LogFlags & SC_LOG_ZULFLAG) ? gmtime(&TVal) : localtime(&TVal);

		SC_STRW_Print("%.24s: ", asctime(BrTP));	// Skip the \n after 24 chars
	    }

	    if (InCount)
		SC_SWAux(InCount, TotCount);
	     else
		SC_STRW_Print(StrP);
	SC_STRW_Print("\n");
		
	// No need to SC_STRW_End *****
	// Text is SC_STRW_UsedLen bytes long in SC_STRW_SDP->Data.

	// Get the open LogFile, or open it here!
	if (!SC_LogFileOpen && !SC_LogEntryAuxOpenFile(VMP))
	    return 0;

    WriteAgain:
	// LogFile is open and ready if gets here...
	// Deal with case that Log file was removed or cannot write!
	Count = write(SC_LogFDesc, SC_STRW_SDP->Data, SC_STRW_UsedLen);
	if (Count != SC_STRW_UsedLen) {
	    if (GoAgain && (errno == EBADF)) {
		SC_LogFileOpen = 0;
		close(SC_LogFDesc);		// Expect to fail if bad FileDesc!
		GoAgain = 0;

		if (SC_LogEntryAuxOpenFile(VMP)) goto WriteAgain;
	    }
	    return 0;
	}
	fsync(SC_LogFDesc);

	if (! (SC_LogFlags & SC_LOG_KOFLAG)) {
	    // Close it...
	    SC_LogFileOpen = 0;
	    close(SC_LogFDesc);
	}

	SC_STRW_Zap();		// Have SC_STRW_SDP if here!
	return Count;
    }

// Creates a log entry--automatically time/date stamped.
// Works like a StrWrite... will string the args together.
//
// Can be called with no args to make a null ("No entry") log entry.
//
// NOTE: Should OPEN conceptual Log before calling Enter... but will open it
//	 here rather than error out if it was not.
//
//       LogFile is usually opened (and then closed) for each entry, unless
//	 flagged as #KEEPOPEN.  In the case of long-running scripts, it is
//	 possible the LogFile is removed for inspection... in which case a *NEW*
//	 LogFile should be created (Unless #REQUIRE flag)!

Int16		SC_SysLogWriteEntry(SC_VMPointer VMP, Uns16 InOutCount, Uns8 IsE)
{
    Uns8		InCount, TotCount;
    Int32		Count;

    InCount = InOutCount;				// Must be Uns8
    TotCount = InCount + (InOutCount >> 8);
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    SC_STRW_SDP = NULL;

    if (! SC_LogOpen) {
	// Open the *DEFAULT* Log instead of complaining!!
	if ((SC_RUN_NOERR != SC_SysLogOpen(VMP, 0)) || !SC_LogOpen)
	    return SC_RUN_BADARG;

	// ***BEWARE*** SysLogOpen will push extra Ret (Int) on RunStack...
	TotCount += 1;
    }

    Count = SC_LogEntryAuxWrite(VMP, InCount, TotCount, "No entry.", IsE, 0);
    if (Count == 0L) goto ErrorOut;

    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)(Int64)Count);

ErrorOut:
    if (SC_STRW_SDP) SC_STRW_Zap();
    SC_RunErrorInfoStr = "Log.Write: Failed.";
    return SC_RUN_BADARG;
}

Int16		SC_SysLogEntry(SC_VMPointer VMP, Uns16 InOutCount)
{
    return	SC_SysLogWriteEntry(VMP, InOutCount, 0);
}


Int16		SC_SysLogEEntry(SC_VMPointer VMP, Uns16 InOutCount)
{
    return	SC_SysLogWriteEntry(VMP, InOutCount, 1);
}

// Writes to scEditor Echo line.  Returns number of bytes written.
// (IN args are like sc.Write!)
Int16	SC_SysEdEcho(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		TotCount;
    Uns8		InCount;
    Uns32		Len;

    // Print value of all IN args, in sequence.
    InCount = InOutCount;			// Must be Uns8
    TotCount = InCount + (InOutCount >> 8);	// Must be Uns8
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    if (InCount) {
	SC_STRW_Begin(VMP);
	    SC_SWAux(InCount, TotCount);
	    Len = SC_STRW_UsedLen;
	    ED_ScriptShowEcho(SC_STRW_SDP->Data);
	SC_STRW_Zap();
    }

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)(Int64)Len);
}


// SC_SysShowVars
//
// If any OUT args, just show those... otherwise all locals.
// IN	Level Arg (SC_SHOW_MIN/LOW/HIGH/MAX)

Int16		SC_SysShowVars(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount, OutCount, TotCount;
    Uns8		Level;
    Int64		Flag;
    SC_VarVPointer	VP;
    Uns32		I;

    InCount = InOutCount;		// Must be Uns8
    OutCount = InOutCount >> 8;		// Must be Uns8
    TotCount = InCount + OutCount;	// Must be Uns8
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    Level = SC_SHOW_LOW;	// Default
    if (InCount) {
	// First arg should be Flag to set display level (MIN/LOW/HIGH/MAX)
	VP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	if (VP->ValueType == SC_VAL_INT) {
	    Flag = (Int64)VP->ValueP;
	    if (Flag > 0)
		Level = (Flag > SC_SHOW_MAX) ? SC_SHOW_MAX : (Uns8)Flag;
	}
    }
    
    if (OutCount) {
	// NOTE:  OUT args are pass-by-ref... so Stack has Ptr to Var!    
	VP = SC_RUNSTACK_GETNTH(VMP, OutCount);
	// Flush the stack NOW, so RefCount of requested vars is not +1!
	// Stack is NOT changed, so can still be read after flushing!!!
	SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
	
	for (I = 0; I < OutCount; ++I, ++VP) {
	    SC_SAFE_CHECK((VP->ValueP == 0LL) || (VP->ValueType == SC_VAL_PTR), SC_BADRUN_Stack);
	    if (VP->ValueP) {
		SC_RunDisplayVarP(VMP, (SC_VarVPointer)VP->ValueP, "", (Uns32)-1, Level, 4);
		if (Level > SC_SHOW_LOW)
		    VMP->OutPrintFP("\n");
	    }
	}
    } else {
	// DeRef and flush *ALL* args... even unwanted ones!
	// Do before display... so RefCount is not +1 !!
	SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
	SC_RunDisplayLocals(VMP, VMP->RunScriptP->CurRunP, Level, 0);
	// Considered switching to SC_RunDisplayGlobs if CurRunP->CallerP is NULL.
	// But TOP func usually has R and potentially L vars that can be viewed
	// with RunDisplayLocals (and NOT with RunDisplayGlobs!) !!!
    }
    
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)1LL);
}


Int16		SC_SysShowGRSAux(SC_VMPointer VMP, Uns16 InOutCount, Uns8 Which)
{
    Uns8		InCount, TotCount;
    SC_VarVPointer	VP;
    Int64		Flag;
    Uns8		Level, SupLevel, ShowSup;

    InCount = InOutCount;			// Must be Uns8
    TotCount = InCount + (InOutCount >> 8);	// Must be Uns8
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    Level = SupLevel = SC_SHOW_LOW;		// Default
    ShowSup = 0;
    if (InCount) {
	// First arg should be Flag to set display level (MIN/LOW/HIGH/MAX)
	VP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	if (VP->ValueType == SC_VAL_INT) {
	    Flag = (Int64)VP->ValueP;
	    if (Flag > 0)
		Level = (Flag > SC_SHOW_MAX) ? SC_SHOW_MAX : (Uns8)Flag;

	    // Second arg should also be a Flag... for SC_RunDisplayScript
	    if ((InCount > 1) && ((++VP)->ValueType == SC_VAL_INT)) {
		ShowSup = 1;
		Flag = (Int64)VP->ValueP;
		if (Flag > 0)
		    SupLevel = (Flag > SC_SHOW_MAX) ? SC_SHOW_MAX : (Uns8)Flag;
	    }
	}
    }

    // DeRef and flush *ALL* args... even unwanted ones! (BEFORE DISPLAY!)
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);

    if (Which == 1)
	SC_RunDisplayGlobs(VMP, VMP->RunScriptP, Level, 1, 0);
    else if (Which == 2)
	SC_RunDisplayRuns(VMP, VMP->RunScriptP->CurRunP, Level, 1);
    else
	SC_RunDisplayScript(VMP, VMP->RunScriptP, Level, SupLevel, ShowSup, 1, 0);

    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)1LL);

}

// SC_SysShowGlobs will show globals
// In arg is flag... how much to show?
//	MIN	Glob only
//	LOW**	Glob + Vars(Location + Ref)
//	HIGH	Glob + Vars(Location + Ref + Value(LOW))
//	MAX	Glob + Vars(Location + Ref + Value(HIGH))
//
//	**	Default, if flag is missing!

Int16		SC_SysShowGlobs(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysShowGRSAux(VMP, InOutCount, 1);
}


// SC_SysShowRun will show current Run chain (stack)
// In arg is flag... how much to show?
//	MIN	Run only
//	LOW**	Run + Vars(Location + Ref)
//	HIGH	Ditto
//	MAX	Ditto
//
//	**	Default, if flag is missing!

Int16		SC_SysShowRun(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysShowGRSAux(VMP, InOutCount, 2);
}

// SC_SysShowScript will show the RUNNING SCRIPT!!
//
// IN -> Level (Optional)
//    -> SupLevel (Optional)
//
// SupLevel is the display level for the superior(s).  This
// only matters if running from a SubScript (or SubSub...SubScript)!

Int16		SC_SysShowScript(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysShowGRSAux(VMP, InOutCount, 3);
}

// -1 or missing means all Pkgs
// N means PkgId N only
// NameStr means named Pkg only

Int16		SC_SysShowPkg(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount, TotCount;
    SC_VarVPointer	VP = NULL;
    Int16		Max;
    Uns16		I;
    SC_PkgPointer	MainPkgP;
    SC_PairHPointer	PairP;
    char		Name[SC_CGMAXPKGNAMELEN + 1];
    Uns32		StrLen;
    SC_StrHPointer	StrHP = NULL;
    
    InCount = InOutCount;			// Must be Uns8
    TotCount = InCount + (InOutCount >> 8);	// Must be Uns8
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    Max = VMP->PkgCount - 1;
    I = 0;
    if (TotCount) {
	VP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	if (VP->ValueType == SC_VAL_INT) {
	    Max = (Int16)(Int64)VP->ValueP;
	    if ((Max < 0) || (Max >= VMP->PkgCount))
		Max = VMP->PkgCount - 1;
	    else
		I = Max;
	    
	} else if ((VP->ValueType == SC_VAL_PTR) &&
		   (StrHP = (SC_StrHPointer)VP->ValueP) &&
		   (StrHP->Type == SC_ENTRY_STR)) {

	    StrLen = StrHP->StrDataP->StrLen;
	    if (StrLen > SC_CGMAXPKGNAMELEN)
		goto ErrorOut;

	    MainPkgP = VMP->PkgADP->Data[0];
	    // Pkg names are case-insensitive... so convert spec string to lowercase!
	    SC_UtilStashLCase(Name, StrHP->StrDataP->Data, StrLen);
	    // Get a new StrHP (this one lowercase) (*** Could be same as before! ***)
	    // Comes in with RefCount +1
	    StrHP = SC_StrGetEntry(VMP, Name, StrLen);
	    PairP = SC_DictGetPair(MainPkgP->NDictP, (SC_VarVPointer)StrHP, SC_VAL_PTR);
	    if (PairP && (PairP->ValType == SC_VAL_PIDX)) {
		Max = I = (Uns16)(Int64)PairP->ValueP;
		SC_SAFE_CHECK(I < VMP->PkgCount, SC_BADRUN_Index);
	    } else
		goto ErrorOut;

	} else
	    goto ErrorOut;
    }

    SC_RunDisplayPkg(VMP, I, Max, 1);

    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    SC_VARDEREFCOND(VMP, StrHP, (SC_VarVPointer)StrHP);
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)1LL);

ErrorOut:
    SC_VARDEREFCOND(VMP, StrHP, (SC_VarVPointer)StrHP);
    SC_RunErrorInfoStr = "Show.Pkg: Bad args.";
    return SC_RUN_BADARG;
}

// No args at all... no need for flags.

Int16		SC_SysShowVM(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8	TotCount = (InOutCount >> 8) + InOutCount;  	// Must be Uns8

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    SC_VMPrintStats(VMP);

    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)1LL);
}

#define SC_SSM_LINEWIDTH	80

    void	SC_SSMAuxShowStrTab(SC_VMPointer VMP)
    {
	char		S[SC_SSM_LINEWIDTH + 1];
	char *		SP = S;
	char *		SMaxP = S + SC_SSM_LINEWIDTH;
	SC_StrHPointer	*SlotP, *MaxSlotP, SHP;
	Int16		Count;
	SC_PrintFuncP	OutPFP = VMP->OutPrintFP;

	SC_StrHashPrintStatus(VMP, 1);

	*SMaxP = 0;			// Trailing /0 for CString
	SlotP = VMP->StrHashTP->Slots;
	MaxSlotP = SlotP + VMP->StrHashTP->SlotCount;
	while (SlotP < MaxSlotP) {
	    if ((SHP = *SlotP++)) {
		Count = 1;
		while ((SHP = SHP->HashNextP)) Count += 1;
		if (Count > 9)
		    *SP = '*';
		else
		    *SP = '0' + Count;
	    } else
		*SP = '-';

	    // Draw the line!
	    if (++SP == SMaxP) {
		OutPFP("          %s\n", S);
		SP = S;
	    }
	}

	// Any leftover lines?
	if (SP > S) {
	    *SP = 0;		// Partial line, new trailing /0
	    OutPFP("          %s\n", S);
	}
    }

    void	SC_SSMAuxShowSlab(SC_VMPointer VMP, SC_SlabPointer SP, Int16 Ord)
    {
	#define	_STRLEN	48
	
	SC_SlabPointer	CurSP = SP;
	Int16		CurOrd = 1;		// 1 based
	Int16		DoAll = (Ord == 0);	// 0 means show all!
	Uns32		TotalSize;
	SC_PrintFuncP	OutPFP = VMP->OutPrintFP;
	Uns8		SType, EType;
	char		*ReadP, *EndP;
	SC_SlabFreePointer	FreeP;
	Uns32		LenBytes, DLen;
	Uns32		BOrd;			// Block Order (0..N)
	char		S[_STRLEN];

	// @@@ Must match declared SC_ENTRY_TYPE
	while (CurSP) {
	    if (DoAll || (CurOrd == Ord)) {
		// Write the header first
		SType = CurSP->Flags & SC_SLAB_TYPE_MASK;
		OutPFP("\nMem:%d --> (%.*s) %s Slab:%d\n", SType, 4, (char *)&CurSP->Tag, SC_SlabNameArr[SType], CurOrd);
		TotalSize = offsetof(SC_SlabRecord, Data) + CurSP->DataSize;
		OutPFP("     	  %'d Bytes total using %'d of %'d bytes\n", TotalSize, CurSP->DataUsed, CurSP->DataSize);
		if (CurSP->FreeCount == 1)
		    OutPFP("          %'d Free block (%'d Bytes)\n", CurSP->FreeCount, CurSP->DataSize - CurSP->DataUsed);
		else
		    OutPFP("          %'d Free blocks (%'d Bytes)\n", CurSP->FreeCount, CurSP->DataSize - CurSP->DataUsed);

		// Traverse the block and show each field
		BOrd = 0;
		ReadP = CurSP->Data;
		EndP = ReadP + CurSP->DataSize;

		while (ReadP < EndP) {
		    FreeP = (SC_SlabFreePointer)ReadP;
		    if (FreeP->Zero == 0LL) {
			OutPFP("% 6d at %06d **FREE: %'d Bytes\n", BOrd, ReadP - CurSP->Data, FreeP->Size);
			ReadP += FreeP->Size;
			BOrd += 1;
			continue;
		    }

		    switch (SType) {
			case SC_SLAB_VENT:
			    LenBytes = sizeof(SC_VarVRecord);
			    OutPFP("% 6d at %06d   % 4s: %d Bytes (Ref:%d) ", BOrd, ReadP - CurSP->Data,
					SC_EntryNameArr[(*(SC_VarVPointer)ReadP).Type],
					sizeof(SC_VarVRecord), (*(SC_VarVPointer)ReadP).RefCount);

			    EType = (*(SC_VarVPointer)ReadP).Type;
			    if (EType == SC_ENTRY_VAR) {
				if ((*(SC_VarVPointer)ReadP).ValueType == SC_VAL_INT)
				    OutPFP("[Int: %d]\n", *(Int64*)&(*(SC_VarVPointer)ReadP).ValueP);
				else if ((*(SC_VarVPointer)ReadP).ValueType == SC_VAL_FLT)
				    OutPFP("[Flt: %f]\n", *(double *)&(*(SC_VarVPointer)ReadP).ValueP);
				else
				    SC_SlabPrintPtr(VMP, (*(SC_VarVPointer)ReadP).ValueP, "Ptr:", '\n');

			    } else if (EType == SC_ENTRY_EXT) {
				OutPFP("[Ext:%s] --> %lx\n", SC_EXTNameArr[(*(SC_EXTVPointer)ReadP).ExtType],
							      (*(SC_EXTVPointer)ReadP).ExtP);

			    } else if (EType == SC_ENTRY_ARREXT) {
				OutPFP("[Ext:%s] ", SC_EXTNameArr[(*(SC_ArrXTVPointer)ReadP).ExtType]);
				SC_SlabPrintPtr(VMP, (*(SC_ArrXTVPointer)ReadP).ArrDataP, "Ptr:", '\n');

			    } else if (EType == SC_ENTRY_ARR) {
				SC_SlabPrintPtr(VMP, (*(SC_ArrVPointer)ReadP).ArrDataP, "Ptr:", '\n');
			    }
			    break;

			case SC_SLAB_BENT:
			    LenBytes = sizeof(SC_PairHRecord);
			    if ((*(SC_PairHPointer)ReadP).Type == SC_ENTRY_PAIR) {
				OutPFP("% 6d at %06d   % 4s: %d Bytes  Key -> ", BOrd, ReadP - CurSP->Data,
					    SC_EntryNameArr[(*(SC_PairHPointer)ReadP).Type], LenBytes);
				SC_RunDisplayField(VMP, (*(SC_PairHPointer)ReadP).KeyType, (*(SC_PairHPointer)ReadP).KeyP);
				OutPFP("                                   Val -> ");
				SC_RunDisplayField(VMP, (*(SC_PairHPointer)ReadP).ValType, (*(SC_PairHPointer)ReadP).ValueP);


			    } else if ((*(SC_PairHPointer)ReadP).Type == SC_ENTRY_STR) {
				OutPFP("% 6d at %06d   % 4s: %d Bytes (Ref:%d) ", BOrd, ReadP - CurSP->Data,
					    SC_EntryNameArr[(*(SC_StrHPointer)ReadP).Type],
					    LenBytes, (*(SC_StrHPointer)ReadP).RefCount);
				SC_SlabPrintTypePtr(OutPFP, VMP->StrDataSP, (*(SC_StrHPointer)ReadP).StrDataP, "Str:", '\n', 0);

			    } else if ((*(SC_PairHPointer)ReadP).Type == SC_ENTRY_RC) {
				OutPFP("% 6d at %06d   % 4s: %d Bytes (Ref:%d) ", BOrd, ReadP - CurSP->Data,
					    SC_EntryNameArr[(*(SC_PairHPointer)ReadP).Type],
					    LenBytes, (*(SC_RCPointer)ReadP).RefCount);
				SC_SlabPrintTypePtr(OutPFP, VMP->BigEntrySP, (*(SC_RCPointer)ReadP).RunP, "Run:", '\n', 0);
				SC_SlabPrintTypePtr(OutPFP, VMP->ChunkSP, (*(SC_RCPointer)ReadP).ResumeP, "Resume:", '\n', 43);
				SC_SlabPrintTypePtr(OutPFP, VMP->ArrDataSP, (*(SC_RCPointer)ReadP).StackCopyP, "Stack:", '\n', 43);

			    } else if ((*(SC_PairHPointer)ReadP).Type == SC_ENTRY_PAT) {
				OutPFP("% 6d at %06d   % 4s: %d Bytes (Ref:%d) ", BOrd, ReadP - CurSP->Data,
					    SC_EntryNameArr[(*(SC_PatPointer)ReadP).Type],
					    LenBytes, (*(SC_PatPointer)ReadP).RefCount);
				SC_SlabPrintTypePtr(OutPFP, VMP->BigEntrySP, (*(SC_PatPointer)ReadP).NextP, "NextP:", '\n', 0);

				OutPFP("                                          ");
				if ((*(SC_PatPointer)ReadP).Flags & SC_PAT_DATA0P)
				    SC_SlabPrintPtr(VMP, (*(SC_PatPointer)ReadP).Data[0], "Data[0]:", '\n');
				else
				    OutPFP("[Data[0]: %016lx]\n", (*(SC_PatPointer)ReadP).Data[0]);

				OutPFP("                                          ");
				if ((*(SC_PatPointer)ReadP).Flags & SC_PAT_DATA1P)
				    SC_SlabPrintPtr(VMP, (*(SC_PatPointer)ReadP).Data[1], "Data[1]:", '\n');
				else
				    OutPFP("[Data[1]: %016lx]\n", (*(SC_PatPointer)ReadP).Data[1]);
			    }
			    break;

			case SC_SLAB_SDAT:
			    // String...
			    DLen = (*(SC_StrDataPointer)ReadP).StrLen;
			    LenBytes = (*(SC_StrDataPointer)ReadP).BlockLen;
			    SC_StrStuffStr(S, (*(SC_StrDataPointer)ReadP).Data, _STRLEN);
			    OutPFP("% 6d at %06d    Str: %'d Bytes StrLen:%d [%s]\n", BOrd, ReadP - CurSP->Data, LenBytes, DLen, S);
			    break;

			case SC_SLAB_ADAT:
			    DLen = (*(SC_ArrDataPointer)ReadP).SlotCount;
			    LenBytes = (*(SC_ArrDataPointer)ReadP).BlockLen;
			    OutPFP("% 6d at %06d    Arr: %'d Bytes Slots:%d ", BOrd, ReadP - CurSP->Data, LenBytes, DLen);
			    SC_SlabPrintPtr(VMP, (*(SC_ArrDataPointer)ReadP).MasterPP, "Master:", '\n');
			    break;

			case SC_SLAB_CHNK:
			    switch (*ReadP) {
				case SC_CHUNK_EXT:
				    LenBytes = (*(SC_ChunkXTVPointer)ReadP).BlockLen;
				    OutPFP("% 6d at %06d   % 4s: %d Bytes [Ext:%s] (Ref:%d) ", BOrd, ReadP - CurSP->Data,
							SC_EntryNameArr[(Uns8)*ReadP], LenBytes,
							SC_EXTNameArr[(*(SC_ChunkXTVPointer)ReadP).ExtType],
							(*(SC_ChunkXTVPointer)ReadP).RefCount);
				    break;
			    
				case SC_CHUNK_CLOS:
				    LenBytes = (*(SC_ClosPointer)ReadP).BlockLen;
				    OutPFP("% 6d at %06d   % 4s: %d Bytes (Ref:%d) ", BOrd, ReadP - CurSP->Data,
							SC_EntryNameArr[(Uns8)*ReadP], LenBytes,
							(*(SC_ClosPointer)ReadP).RefCount);

				    SC_SlabPrintTypePtr(OutPFP, VMP->ChunkSP, (*(SC_ClosPointer)ReadP).CBP, "Code:", '\n', 0);
				    break;

				case SC_CHUNK_CODE:
				    LenBytes = (*(SC_CodeBlockPointer)ReadP).BlockLen;
				    OutPFP("% 6d at %06d   % 4s: %d Bytes (Ref:%d) [CodeId:%d Len:%'d]\n", BOrd, ReadP - CurSP->Data,
							SC_EntryNameArr[(Uns8)*ReadP], LenBytes, (*(SC_CodeBlockPointer)ReadP).RefCount,
							(*(SC_CodeBlockPointer)ReadP).CodeId, (*(SC_CodeBlockPointer)ReadP).CodeLen);
				    break;

				case SC_CHUNK_RUN:
				    LenBytes = (*(SC_RunPointer)ReadP).BlockLen;
				    OutPFP("% 6d at %06d   % 4s: %d Bytes ", BOrd, ReadP - CurSP->Data,
							SC_EntryNameArr[(Uns8)*ReadP], LenBytes);
				    SC_SlabPrintTypePtr(OutPFP, VMP->ChunkSP, (*(SC_RunPointer)ReadP).VarArr[0], "Code:", '\n', 0);
				    break;

				case SC_CHUNK_GLOB:
				    LenBytes = (*(SC_GlobPointer)ReadP).BlockLen;
				    OutPFP("% 6d at %06d   % 4s: %d Bytes Count: %d\n", BOrd, ReadP - CurSP->Data,
							SC_EntryNameArr[(Uns8)*ReadP],
							LenBytes, (*(SC_GlobPointer)ReadP).Count);
				    break;

				case SC_CHUNK_STAK:
				    LenBytes = (*(SC_StackBlockPointer)ReadP).BlockLen;
				    OutPFP("% 6d at %06d   % 4s: %d Bytes Slots:%d Using:%d\n", BOrd, ReadP - CurSP->Data,
							SC_EntryNameArr[(Uns8)*ReadP],
							LenBytes, (*(SC_StackBlockPointer)ReadP).Count, VMP->StackTop);
				    break;

				case SC_CHUNK_SCRPT:
				    LenBytes = sizeof(SC_ScriptRecord);
				    OutPFP("% 6d at %06d   % 4s: %d Bytes Id:%d L:%d\n", BOrd, ReadP - CurSP->Data,
							SC_EntryNameArr[(Uns8)*ReadP],
							LenBytes, (*(SC_ScriptPointer)ReadP).ScriptId,
							(*(SC_ScriptPointer)ReadP).ScriptLevel);
				    break;
			    }
			    break;

			case SC_SLAB_TOK:
			    LenBytes = sizeof(SC_TokenRecord);
			    OutPFP("% 6d at %06d    Tok: %d Bytes [Type:%d Id:%d Pos:%d Len:%d]\n", BOrd, ReadP - CurSP->Data,
					LenBytes, (*(SC_TokenPointer)ReadP).Type, (*(SC_TokenPointer)ReadP).Id,
					(*(SC_TokenPointer)ReadP).ReadPos, (*(SC_TokenPointer)ReadP).ReadLen);
			    break;

			case SC_SLAB_LEX:
			    LenBytes = sizeof(SC_LexRecord);
			    OutPFP("% 6d at %06d    Lex: %d Bytes [Flags:%d FLevel:%d]\n", BOrd, ReadP - CurSP->Data,
					LenBytes, (*(SC_LexPointer)ReadP).Flags, (*(SC_LexPointer)ReadP).FLevel);
			    break;

			case SC_SLAB_MAP:
			    LenBytes = sizeof(SC_MapRecord);
			    OutPFP("% 6d at %06d    Map: %d Bytes [CodeId:%d]\n", BOrd, ReadP - CurSP->Data,
					LenBytes, (*(SC_MapPointer)ReadP).CodeId);
			    break;

			case SC_SLAB_VEX:
			    LenBytes = sizeof(SC_VexRecord);
			    OutPFP("% 6d at %06d    Vex: %d Bytes [Class:%d]\n", BOrd, ReadP - CurSP->Data,
					LenBytes, (*(SC_VexPointer)ReadP).Class);
			    break;
		    }

		    ReadP += LenBytes;
		    BOrd += 1;
		}

	    }

	    CurSP = CurSP->NextSP;
	    CurOrd += 1;
	    if (!DoAll && (CurOrd > Ord)) break;
	}

	#undef _STRLEN
    }

// SC_SysShowMem
//
// Shows contents of the specified slab.
//   First IN arg is Slab type Id (0 is StrHashTable),
//   Second IN arg is Slab order on NextSP list
//	Ord == 1 means the first one
//	Ord == 2 means the second one...
//      ...
//	Ord == 0 means *ALL*, and is the default
//
// No LEVEL arg!  Always at SC_SHOW_MAX!!

Int16		SC_SysShowMem(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		TotCount, InCount;
    Uns8		SlabType;
    Int16		SlabOrd;
    SC_VarVPointer	VarP;

    InCount = InOutCount;			// Must be Uns8
    TotCount = (InOutCount >> 8) + InCount;	// Must be Uns8
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    
    SlabType = SlabOrd = 0;

    if (InCount) {
	VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	if (VarP->ValueType == SC_VAL_INT) SlabType = (Int64)VarP->ValueP;

	if (InCount > 1) {
	    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount - 1);
	    if (VarP->ValueType == SC_VAL_INT) SlabOrd = (Int64)VarP->ValueP;
	}
    }

    switch (SlabType) {
	case SC_SLAB_STAB: SC_SSMAuxShowStrTab(VMP); break;
	case SC_SLAB_VENT: SC_SSMAuxShowSlab(VMP, VMP->VarEntrySP, SlabOrd); break;
	case SC_SLAB_BENT: SC_SSMAuxShowSlab(VMP, VMP->BigEntrySP, SlabOrd); break;
	case SC_SLAB_SDAT: SC_SSMAuxShowSlab(VMP, VMP->StrDataSP, SlabOrd); break;
	case SC_SLAB_ADAT: SC_SSMAuxShowSlab(VMP, VMP->ArrDataSP, SlabOrd); break;
	case SC_SLAB_CHNK: SC_SSMAuxShowSlab(VMP, VMP->ChunkSP, SlabOrd); break;
	case SC_SLAB_TOK:  SC_SSMAuxShowSlab(VMP, VMP->TokenSP, SlabOrd); break;
	case SC_SLAB_LEX:  SC_SSMAuxShowSlab(VMP, VMP->LexSP, SlabOrd); break;
	case SC_SLAB_MAP:  SC_SSMAuxShowSlab(VMP, VMP->MapSP, SlabOrd); break;
	case SC_SLAB_VEX:  SC_SSMAuxShowSlab(VMP, VMP->VexSP, SlabOrd); break;
	default:	break;
    }

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)1LL);    
}

// SC_SysShowStr
//
// Shows strings indexed in VMP->StringADP... StrHashTable (and
// its StrData slab) will typically have additional strings.
//
// No LEVEL arg, assumes SC_SHOW_MAX!

Int16		SC_SysShowStr(SC_VMPointer VMP, Uns16 InOutCount)
{
    #define	_STRLEN	48

    Uns8		TotCount = (InOutCount >> 8) + InOutCount;	// Must be Uns8
    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;
    Uns32		StrCount = VMP->RunScriptP->StringCount;
    SC_StrHPointer *	StrHPP;
    SC_StrDataPointer	StrDP;
    Uns32		I;
    char		S[_STRLEN];

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    OutPFP("\n  Script Strings -------> Count:%'d\n", StrCount);
    if (StrCount) {
	StrHPP = (SC_StrHPointer *)VMP->RunScriptP->StringADP->Data;
	for (I = 1; I <= StrCount; ++I, ++StrHPP) {
	    OutPFP("    %04d      (Ref:%d) ", I, (*StrHPP)->RefCount);
	    StrDP = (*StrHPP)->StrDataP;
	    SC_StrStuffStr(S, StrDP->Data, _STRLEN);	    
	    OutPFP("[%s]\n", S);
	}
    }
    
    #undef _STRLEN

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)1LL);
}

Int16		SC_SysShowMemStr(SC_VMPointer VMP, Uns16 InOutCount)
{
    #define		_STRLEN		48

    char		S[_STRLEN];
    Uns8		TotCount = (InOutCount >> 8) + InOutCount;
    SC_PrintFuncP	PFP = VMP->OutPrintFP;
    SC_StrHPointer	*SlotP, *EndSlotP, SHP;
    Uns32		Offset;
    Uns32		Count = 1;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    SC_StrHashPrintStatus(VMP, 1);

    SlotP = VMP->StrHashTP->Slots;
    EndSlotP = SlotP + VMP->StrHashTP->SlotCount;
    while (SlotP < EndSlotP) {
	if ((SHP = *SlotP++)) {		// ASSIGN + INC
	    do {
		Uns16	SPOrd = SC_SPPFindInSlab(VMP->BigEntrySP, SHP, &Offset);

		SC_StrStuffStr(S, SHP->StrDataP->Data, _STRLEN);
		PFP("          %04d (Ref:%d) [Entry %06d on BEnt:%d] \"%s\"\n",
			       Count++, SHP->RefCount, Offset, SPOrd, S);
		SHP = SHP->HashNextP;
	    } while (SHP);
	}
    }
    
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)1LL);

    #undef	_STRLEN
}

// Calls Mark/Sweep GC.
Int16		SC_SysDoMSGC(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8	TotCount = (InOutCount >> 8) + InOutCount;	// Must be Uns8

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);

	// Definitely  DeRef/Flush RunStack before GC!
	SC_GCAll(VMP);
    
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)VMP->GCSweepTot);
}

// SIGINT goes to debugger, but does NOT show location in code.
// Can be resumed (CONTINUE in GDB).
Int16		SC_SysDebugger(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8	TotCount = (InOutCount >> 8) + InOutCount;	// Must be Uns8

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);

    printf("   DEBUG called from Script!\n");
    printf("     (Continue to resume.)\n\n");
    raise(SIGINT);		// Can continue from here!
    
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)1LL);
}

    void    SC_SysAuxGetUserErrorStr(SC_VMPointer VMP, Uns8 InCount, Uns8 TotCount)
    {
	SC_VarVPointer		VarP;
	SC_StrHPointer		SHP;
	
	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
	if (InCount) {
	    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	    if (VarP->ValueType == SC_VAL_PTR) {
		SHP = (SC_StrHPointer)VarP->ValueP;
		if (SHP->Type == SC_ENTRY_STR)
		    SC_UserErrorStr = SHP->StrDataP->Data;
	    }
	}
    }

// Prints Error to OUTPUT buffer, stops script, back to ED environment.
// Can edit script and compile/run again.  Best choice for script user.
Int16			SC_SysRunError(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Just low byte
    Uns8		TotCount = (InOutCount >> 8) + InCount;		// Must be Uns8

    SC_SysAuxGetUserErrorStr(VMP, InCount, TotCount);
    SC_RunErrorInfoStr = "[Sys.Error called]";
    
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);

    // DO NOT Push result value on stack...
    return SC_RUN_USERERR;
}

// Lands in debugger with DIV-0 error... CANNOT continue.
// Will write error to GDB output.  Causes SysError.
Int16			SC_SysDoAbort(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Just low byte
    Uns8		TotCount = (InOutCount >> 8) + InCount;		// Must be Uns8

    SC_UserErrorStr = "User Abort";
    SC_SysAuxGetUserErrorStr(VMP, InCount, TotCount);
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);

    SC_SysError(SC_UserErrorStr);
    // SC_SysError will dump into debugger....
    return SC_RUN_NOERR;
}

Int16			SC_SysExit(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Just low byte
    Uns8		TotCount = (InOutCount >> 8) + InCount;		// Must be Uns8

    SC_UserErrorStr = "Script Exit!";
    SC_SysAuxGetUserErrorStr(VMP, InCount, TotCount);
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);

    // DO NOT Push result value on stack...
    return SC_RUN_EXIT;
}

// Will return the Type Code for the IN param.  Additional In params are ignored.
// If there is an OUT param, the string name of the input type will be placed there.
// Additional OUT params are ignored.

Int16			SC_SysGetType(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;			// Just low byte
    Uns8		OutCount = InOutCount >> 8;
    Uns8		TotCount = OutCount + InCount;		// Must be Uns8
    SC_VarVPointer	VarP;
    Int64		Res = SC_SCRIPT_UNKNOWN;
    SC_StrHPointer	StrHP = NULL;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount) {
	VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	if (VarP->ValueType == SC_VAL_INT)
	    Res = SC_SCRIPT_INT;
	else if (VarP->ValueType == SC_VAL_FLT)
	    Res = SC_SCRIPT_FLT;
	else if (VarP->ValueType == SC_VAL_PTR) {
	    VarP = (SC_VarVPointer)VarP->ValueP;
	    Res = SC_SysScriptTypeCodes[VarP->Type];
	}
    }

    if (OutCount) {
	// NOTE:  OUT args are pass-by-ref... so Stack has Ptr to Var!    
	VarP = SC_RUNSTACK_GETNTH(VMP, OutCount);
	SC_SAFE_CHECK((VarP->ValueP == 0LL) || (VarP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	if ((VarP = (SC_VarVPointer)VarP->ValueP)) {
	    SC_VARDEREFCOND(VMP, VarP->ValueType == SC_VAL_PTR, VarP->ValueP);	// Old value!
	    // Get StrHP, +1 RefCount is good, will be stashed in a Var
	    StrHP = SC_StrGetEntry(VMP, SC_SysScriptTypeStrs[Res], strlen(SC_SysScriptTypeStrs[Res]));
	    VarP->ValueType = SC_VAL_PTR;
	    VarP->ValueP = StrHP;
	}
    }
    
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)Res);
}

// Gives "conceptual" size to the script writer... *NOT* memory size.

Int16			SC_SysGetSize(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Just low byte
    Uns8		TotCount = (InOutCount >> 8) + InCount;		// Must be Uns8
    SC_VarVPointer	VarP;
    Int64		Res = 0;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount) {
	Res = 1;
	VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	if (VarP->ValueType == SC_VAL_PTR) {
	    VarP = (SC_VarVPointer)VarP->ValueP;
	    switch (VarP->Type) {
		case SC_ENTRY_ARR:	Res = (*(SC_ArrVPointer)VarP).ArrDataP->SlotCount; break;
		case SC_ENTRY_STR:	Res = (*(SC_StrHPointer)VarP).StrDataP->StrLen; break;
		case SC_ENTRY_DICT:	Res = (*(SC_DictVPointer)VarP).HADP->SlotCount; break;
		case SC_CHUNK_CLOS:	Res = (*(SC_CodeBlockPointer)(*(SC_ClosPointer)VarP).CBP).CodeLen; break;
		default: break;
	    }
	}
    }
    
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)Res);
}

    // Counts NON-Nil entries in the array
    Uns32	SC_SCEAux(SC_ArrDataPointer ADP)
    {
	Uns32		I = 0;
	void **		CurPP = ADP->Data;
	void **		EndPP = CurPP + ADP->SlotCount;

	while (CurPP < EndPP)
	    if (*CurPP++) ++I;

        return I;
    }

// SC_SysCountElts will count the conceptual elements in a variable.
// In the case of CLOSURE, will count IT + Its sub-functions.
// (Not a recursive count, in case one of the subs has own subs...)
//
// For Array, it counts the cells WITH allocated storage, not NULL in ADP.
// For Dict, it counts Pairs, not size of hash array.
//
// Can be called with 2 args for ARR and DICT
// -- ARR + INDEX -> Returns 1 if Index has storage, 0 if not (or out of bounds)
// -- DICT + KEY  -> Returns 1 if Pair with Key is IN Dict, 0 otherwise

Int16		SC_SysCountElts(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Just low byte
    Uns8		TotCount = (InOutCount >> 8) + InCount;		// Must be Uns8
    SC_VarVPointer	VarP, IndexP;
    Int64		Res = 0;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount) {
	Res = 1;
	VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	if (VarP->ValueType == SC_VAL_PTR) {
	    VarP = (SC_VarVPointer)VarP->ValueP;
	    if (VarP->Type == SC_ENTRY_ARR) {
		Res = SC_SCEAux((*(SC_ArrVPointer)VarP).ArrDataP);
		if (Res && (InCount > 1)) {
		    // If INT index, check it out... otherwise just ignore!
		    IndexP = SC_RUNSTACK_GETNTH(VMP, TotCount - 1);
		    if (IndexP->ValueType == SC_VAL_INT) {
			if ((Int64)IndexP->ValueP >= (*(SC_ArrVPointer)VarP).ArrDataP->SlotCount)
			    Res = 0;		// Out of range
			else
			    Res = ((*(SC_ArrVPointer)VarP).ArrDataP->Data[(Int64)IndexP->ValueP]) ? 1 : 0;
		    }
		}

	    } else if (VarP->Type == SC_ENTRY_DICT) {
		Res = (Int64)(*(SC_DictVPointer)VarP).PairCount;
		if (InCount > 1) {
		    // Could be any pair Key!
		    IndexP = SC_RUNSTACK_GETNTH(VMP, TotCount - 1);
		    // Return 1 if Dict has pair for Key
		    Res = (SC_DictGetPair((SC_DictVPointer)VarP, (SC_VarVPointer)IndexP->ValueP, IndexP->ValueType))
			    ? 1 : 0;
		}
		
	    } else if (VarP->Type == SC_ENTRY_STR)
		Res = SC_StrGetCharLen((SC_StrHPointer)VarP);
	    else if (VarP->Type == SC_CHUNK_CLOS)
		Res = (*(SC_CodeBlockPointer)(*(SC_ClosPointer)VarP).CBP).FuncCount + 1;
	}
    }

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)Res);
}


// Reverses Var from S to E (INCLUSIVE indices)
// Rev(Var);		--> Reverse all of Var
// Rev(Var, S);		--> Reverse Var from S (0 means beginning)
// Rev(Var, S, E);	--> Reverse Var from S to E (0 means to end)
//
// Reverses Array in place.  Returns it.
// Reverses Dict in place.  Returns it.
// Reverses *COPY* of Str.  Returns it.  (Strings are IMMUTABLE!)
// Returns 0 for all other Var.


    // SC_SysStrAuxGetBVals will compute Byte pos (BStart and BStop) given
    // Char pos (CStart and CStop).
    //
    // NOTE: This is called for REVERSE where it needs >1 char to reverse *AND*
    //	     COPY where >0 chars are needed.  So it takes an arg NeedOne that
    //	     determines how it tests!
    
    Int16	SC_SysStrAuxGetBVals(SC_StrDataPointer SDP, Int32 CStart, Int32 CStop,
				     Int32* BStartP, Int32 *BStopP, Uns8 NeedOne)
    {
	char	*CP = &SDP->Data[0];
	char	C;
	Int32	BPos, CPos, L;

	// Non-0 means they have real values!
	if (CStop < 0) CStop = 0;
	if (CStart && CStop) {
	    if (NeedOne) {
		if (CStart >= CStop) return 0;	// Reverse needs one or more chars
	    } else
		if (CStart > CStop) return 0;	// Copy needs ZERO or more

	   if (CStart >= CStop) return 0;
	}
	
	if (CStop == 0)
	    CStop = SDP->StrLen;	// MAX possible!
	BPos = CPos = 0;
	
	while (1) {
	    if (CStart == CPos)
		*BStartP = BPos;
	    if (CStop == CPos) {
		// BPos is beg of this char, want END of it!
		*BStopP = BPos + ED_UtilGetUTF8Len(*CP) - 1;
		break;
	    }

	    if ((C = *CP)) {
		L = ED_UtilGetUTF8Len(C);
		CPos += 1, BPos += L, CP += L;
		continue;
	    }

	    // Hit final NULL, gone too far!
	    *BStopP = BPos - 1;
	    break;
	}

	// Went until CStop or string end.  So if CStart is >= CPos, the range is NULL!
	if (NeedOne)
	    return (CStart < CPos);
	else
	    return (CStart <= CPos);
    }

    void SC_SysAuxGetVarStartStop(SC_VMPointer VMP, SC_VarVPointer VarP, Uns8 InCount,
				            Int32* StartP, Int32 *StopP)
    {
	Int64		LLV;

	*StartP = *StopP = 0;
	if (InCount) {
	    if (VarP->ValueType == SC_VAL_INT) {
		LLV = (Int64)VarP->ValueP;
		if (LLV < 0) LLV = 0;
		else if (LLV > 0x7FFFFFFF) LLV = 0x7FFFFFFF;
		*StartP = (Int32)LLV;
		if (--InCount) {
		    VarP += 1;
		    if (VarP->ValueType == SC_VAL_INT) {
			LLV = (Int64)VarP->ValueP;
			if (LLV < 0) LLV = 0;
			else if (LLV > 0x7FFFFFFF) LLV = 0x7FFFFFFF;
			*StopP = (Int32)LLV;
		    }
		}
	    }
	}
    }

Int16		SC_SysReverse(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Just low byte
    Uns8		TotCount = (InOutCount >> 8) + InCount;		// Must be Uns8
    SC_VarVPointer	VarP;
    Int32		Start, Stop;
    Uns8		DidRev = 0;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) goto DoneDone;

    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    SC_SysAuxGetVarStartStop(VMP, VarP+1, InCount-1, &Start, &Stop);

    if (VarP->ValueP && (VarP->ValueType == SC_VAL_PTR)) {
	VarP = (SC_VarVPointer)VarP->ValueP;
	if (VarP->Type == SC_ENTRY_ARR) {
	    // Reverse the array (Start to Stop) by swapping the two
	    // ends and working towards the middle of the range.
	    SC_ArrDataPointer	ADP = (*(SC_ArrVPointer)VarP).ArrDataP;
	    void			**HPP, **TPP;	// Head and Tail

	    if ((Stop <= 0) || (Stop >= ADP->SlotCount))
		Stop = ADP->SlotCount - 1;	// 0 based

	    if (Start < Stop) {
		HPP = &ADP->Data[Start];
		TPP = &ADP->Data[Stop];
		while (HPP < TPP) {
		    // Swap using VarP as temp storage
		    VarP = *HPP;
		    *HPP = *TPP;
		    *TPP = VarP;

		    ++HPP, --TPP;
		}
	    }
	    // Inc RefCount... will go on RunStack for return
	    SC_VARREF(VarP);
	    DidRev = 1;

	} else if (VarP->Type == SC_ENTRY_STR) {
	    // Reverse the string (Start to Stop)... Strings can have UTF8 chars!!
	    // So Start-Stop are CHAR COUNTS for strings.
	    // Strings are *IMMUTABLE* so make a new one!!!
	    SC_StrDataPointer	OldSDP = (*(SC_StrHPointer)VarP).StrDataP;
	    SC_StrDataPointer	NewSDP = SC_StrStashData(VMP, OldSDP->Data, OldSDP->StrLen);
	    Int32		BStart, BStop;
	    char		*SP, *DP;
	    Int16		L, I;

	    if (SC_SysStrAuxGetBVals(OldSDP, Start, Stop, &BStart, &BStop, 1)) {
		// Cannot swap from two ends towards the middle (like Arr)
		// because chars may be UTF8 and up to 4 bytes.
		// Copy 1 char (1-4 bytes) at a time from oldSDP!
		SP = &OldSDP->Data[BStart];
		DP = &NewSDP->Data[BStop];
		while (BStop >= BStart) {		// BStop will Dec, BStart remains
		    I = L = ED_UtilGetUTF8Len(*SP);
		    if (L == 1)			// Usual case!
			*DP-- = *SP++;
		    else {
			// Cannot reverse UTF-8 bytes, must copy in order!
			DP -= L - 1;		// Back up enough
			while (I--) *DP++ = *SP++;	// Copy forward
			DP -= L + 1;		// Now back up again!
		    }
		    BStop -= L;
		}
	    }
	    // No need for RefVar, NewSDP comes in with RefCount of 1.
	    VarP = (SC_VarVPointer)SC_StrGetDataEntry(VMP, NewSDP);
	    DidRev = 1;

	} else if (VarP->Type == SC_ENTRY_DICT) {
	    // Reverse the dict pair list (Start to Stop).
	    // If given Start/Stop, only the middle N may have to be swapped!!
	    // HP is Head, CP is Cur.  RevP holds the new reversed
	    // list while being constructed.
	    SC_PairHPointer PrevP, HP, RevP, TempP, CP;
	    Int32		Cur;

	    if ((Stop <= 0) || (Stop >= (*(SC_DictVPointer)VarP).PairCount))
		Stop = (*(SC_DictVPointer)VarP).PairCount - 1;

	    if (Start < Stop) {
		PrevP = RevP = NULL, Cur = 0;
		HP = (*(SC_DictVPointer)VarP).FirstPairP;	// Head of Reversal
		while (Cur < Start)
		    PrevP = HP, HP = HP->NextPairP, ++Cur;

		// Swap order from HP to Stop... CP is cur.
		// Construct RevP as new reversed sub-list.
		CP = HP;
		while (Cur <= Stop) {
		    TempP = CP->NextPairP;		// Stash in Temp
			CP->NextPairP = RevP;	// Make CP first on RevP
			RevP = CP;
		    CP = TempP, ++Cur;		// Get next
		}

		HP->NextPairP = CP;			// Add whatever "tail" is left un-reversed
		if (PrevP)				// Now place new reversed list after Prev.
		    PrevP->NextPairP = RevP;
		else
		    (*(SC_DictVPointer)VarP).FirstPairP = RevP;
	    }
	    // Inc RefCount... will go on RunStack for return
	    SC_VARREF(VarP);
	    DidRev = 1;
	}
    }

DoneDone:
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    if (DidRev)
	return SC_SysAuxReturn(VMP, SC_VAL_PTR, VarP);
    else
	return SC_SysAuxReturn(VMP, SC_VAL_INT, 0);
}

// Copies Var from S to E and returns it.
// Copy(Var)		--> Copy all of Var and return it.
// Copy(Var, S)		--> Copy Var from S to end and return it.
// Copy(Var, S, E)	--> Copy Var from S to E and return it.
//
// NOTE: Identical copy of Str, IS the Str itself... just with 1+ RefCount.
//       CopyArr[0] gets OrigArr[S], so they index differently!
//	 CopyDict will be missing Pairs outside the S-E range.

Int16		SC_SysCopy(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Just low byte
    Uns8		TotCount = (InOutCount >> 8) + InCount;		// Must be Uns8
    SC_VarVPointer	VarP;
    Int32		Start, Stop;
    Uns8		DidCopy = 0;
    
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) goto DoneDone;

    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    SC_SysAuxGetVarStartStop(VMP, VarP+1, InCount-1, &Start, &Stop);

    if (VarP->ValueP && (VarP->ValueType == SC_VAL_PTR)) {
	VarP = (SC_VarVPointer)VarP->ValueP;
	if (VarP->Type == SC_ENTRY_ARR) {
	    // Copies the array (Start to Stop)
	    SC_ArrDataPointer	OldADP = (*(SC_ArrVPointer)VarP).ArrDataP;

	    if ((Stop <= 0) || (Stop >= OldADP->SlotCount))
		Stop = OldADP->SlotCount - 1;	// 0 based

	    if (Start < Stop) {
		SC_ArrVPointer	NewArrP = SC_ArrMakeNewArr(VMP, Stop - Start);
		SC_ArrDataPointer	NewADP = NewArrP->ArrDataP;
		void		**SPP = &OldADP->Data[Start];
		void		**DPP = &NewADP->Data[0];
		Int64		Count = Stop - Start;
		SC_VarVPointer	VP;

		while (Count--) {
		    if ((VP = (SC_VarVPointer)(*DPP++ = *SPP++)))	// Assign
			SC_VARREF(VP);				// New Ref to it!
		}
		// NewArrP already has RefCount of 1.
		VarP = (SC_VarVPointer)NewArrP;
		DidCopy = 1;
	    }

	} else if (VarP->Type == SC_ENTRY_STR) {
	    // Copy the string (Start to Stop)
	    SC_StrDataPointer	OldSDP = (*(SC_StrHPointer)VarP).StrDataP;
	    SC_StrDataPointer	NewSDP;
	    Int32		BStart, BStop;

	    // Start/Stop are CHAR COUNTS for String.  Get BYTE COUNT BStart/BStop
	    if (SC_SysStrAuxGetBVals(OldSDP, Start, Stop, &BStart, &BStop, 0)) {
		// Len = 1 + BStop - BStart!!  (BStop == BStart is still 1 byte!)
		NewSDP = SC_StrStashData(VMP, OldSDP->Data + BStart, 1 + BStop - BStart);

		// New string will come in with RefCount of 1
		VarP = (SC_VarVPointer)SC_StrGetDataEntry(VMP, NewSDP);
		DidCopy = 1;
	    }

	} else if (VarP->Type == SC_ENTRY_DICT) {
	    // Create a new dict with N new pairs... copy pair data from old dict!
	    SC_PairHPointer		OldP, PairP;
	    SC_DictVPointer		NewDictP;
	    Int32			I;

	    if ((Stop <= 0) || (Stop >= (*(SC_DictVPointer)VarP).PairCount))
		Stop = (*(SC_DictVPointer)VarP).PairCount - 1;

	    if (Start < Stop) {
		NewDictP = SC_DictCreateNewDict(VMP, Stop - Start);

		// Go to START pair in Old dict
		I = 0, OldP = (*(SC_DictVPointer)VarP).FirstPairP;
		while (I++ < Start) OldP = OldP->NextPairP;

		// Create new pairs for NewDictP;
		while (I++ < Stop) {
		    PairP = SC_BigEntryNew(VMP);
		    PairP->KeyType = OldP->KeyType;
		    PairP->KeyP = OldP->KeyP;
		    PairP->ValType = OldP->ValType;
		    PairP->ValueP = OldP->ValueP;

		    // Will Inc RefCount of Key and Val if Ptrs!
		    SC_DictStashNewPair(VMP, NewDictP, PairP);
		    OldP = OldP->NextPairP;
		}
	    }
	    // Inc RefCount... will go on RunStack for return
	    SC_VARREF(VarP);
	    DidCopy = 1;
	}
    }
    

DoneDone:
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    if (DidCopy)
	return SC_SysAuxReturn(VMP, SC_VAL_PTR, VarP);
    else
	return SC_SysAuxReturn(VMP, SC_VAL_INT, 0);
}


    #define		SC_RAND_A	0x000000000000BC8F
    #define		SC_RAND_M	0x000000007FFFFFFF
    Uns64		SC_RandSeed =   0x00000000ABCDEFAB;	// Cannot be 0!!

    Uns32		SC_GetRand(void)
    {
	SC_RandSeed &= 0x00000000FFFFFFFF;
	SC_RandSeed = (SC_RandSeed * SC_RAND_A) % SC_RAND_M;
	return (Uns32)SC_RandSeed;
    }

    
// Sets seed for Rand.
//	Arg	Sets seed to it (Int or Flt)
//	No Arg	Sets seed to time(NULL)
//
// Seed with NULL --> Seed gets 1 for reproducible sequence

Int16		SC_SysSeedRand(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;

    if (InCount) {
	VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	SC_RandSeed = (VarP->ValueP) ? (Uns64)VarP->ValueP : 1LL;
    } else
	SC_RandSeed = (Uns32)time(NULL);

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)(Int64)SC_RandSeed);
}

// Returns a random number...
// 0 IN arg:			--> Returns Uns from 0 to 2^31 - 1
//
// 1 IN arg:	Specifies Type
//		Any Flt		--> Return Dbl from 0.0 to 1.0
//		1 (Int)		--> Return Uns from 0 to 2^31 - 1
//		2 (Int)		--> Return Uns from 0 to 2^62 - 1
//				    (Do not want NEGATIVE Int64)
//
// 2 IN arg:	Specifies Range (Must be positive)
//		Int N - M	--> Return Uns from N to M
//		Flt N - M	--> Return + Dbl from N to M
//				    (Will convert M to DBL if Int)

Int16		SC_SysRand(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    Int64		Min, Max;
    Uns64		Res;
    double		FMin, FMax, FRes;
    Int16		IsFlt = 0;

    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    Res = (Uns64)SC_GetRand();
	    
    if (InCount == 1) {
	// Specifies res type, Uns32, Uns64, or Double
	if (VarP->ValueType == SC_VAL_INT) {
	    if ((Uns64)VarP->ValueP >= 2)
		Res = (Res << 32) | (Uns32)SC_GetRand();

	} else if (VarP->ValueType == SC_VAL_FLT) {
	    IsFlt = 1;
	    FRes = (double)Res / SC_RAND_M;
	}

    } else if (InCount > 1) {
	// Specifies Min/Max (Int or Flt determined by Min)
	if (VarP->ValueType == SC_VAL_INT) {
	    Min = (Int64)VarP->ValueP;
	    VarP++;
	    if (VarP->ValueType == SC_VAL_INT)
		Max = (Int64)VarP->ValueP;
	    else
		goto ErrorOut;

	    if ((Min < 0) || (Min >= Max))
		goto ErrorOut;

	} else if (VarP->ValueType == SC_VAL_FLT) {
	    IsFlt = 1;
	    FMin = *(double *)&VarP->ValueP;		// Read as Double
	    VarP++;
	    if (VarP->ValueType == SC_VAL_FLT)
		FMax = *(double *)&VarP->ValueP;	// Read as Double
	    else if (VarP->ValueType == SC_VAL_INT)
		FMax = (double)(Uns64)VarP->ValueP;	// Convert Int to Double
	    else
		goto ErrorOut;

	    if ((FMin < 0) || (FMin >= FMax))
		goto ErrorOut;
	}

	FRes = (double)Res / SC_RAND_M;
        if (IsFlt)
	    FRes = (FRes * (FMax - FMin)) + FMin;
	else
	    Res = (Uns64)((FRes * (Max - Min)) + Min);
    }

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    if (IsFlt)
	return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&FRes);
    else
	return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)Res);

ErrorOut:
    SC_RunErrorInfoStr = "Sys.Rand: Bad args.";
    return SC_RUN_BADARG;
}

// Supports OUT arg for ARR to store "broken down time".
// Will take Flag IN arg, to say local or zulu time!

Int16		SC_SysTime(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		OutCount = InOutCount >> 8;
    Uns8		TotCount = InCount + OutCount;
    Int64		Now = time(NULL);
    Uns8		IsZulu = 0;
    SC_VarVPointer	VarP;
    SC_ChunkXTVPointer	CXTP;
    struct tm *		BrTimeP;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    // if IN arg, expect a flag (INT)
    if (InCount) {
	VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	if (VarP->ValueType == SC_VAL_INT) {
	    if (((Int64)VarP->ValueP & SC_SYSMASK_ZULU) == SC_SYSMASK_ZULU) {
		SC_RunErrorInfoStr = "Sys.Time: Bad flags";
		return SC_RUN_BADARG;
	    }
	    IsZulu = !!((Int64)VarP->ValueP & SC_SYS_ZUL);
	}
    }

    BrTimeP = (IsZulu) ? gmtime(&Now) : localtime(&Now);
    // if OUT arg is provided, set it.
    //
    // NOTE: Result is a ChunkXT, so dispose of VarP's current contents, Then
    //	     create a new ChunkXT and set VarP to it.  No attempt is made to
    //	     *COPY* client data from one ChunkXT to another... this is client
    //	     data, NOT Arr entries!!
    if (OutCount) {
	VarP = SC_RUNSTACK_GETNTH(VMP, OutCount);
	SC_SAFE_CHECK((VarP->ValueP == 0LL) || (VarP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	if ((VarP = (SC_VarVPointer)VarP->ValueP)) {
	    CXTP = SC_ChunkXTMakeNew(VMP, sizeof(struct tm), 0, SC_EXT_BRTIME);
	    memcpy(CXTP->Ext, BrTimeP, sizeof(struct tm));	

	    // Get rid of any old values!!
	    SC_VARDEREFCOND(VMP, VarP->ValueType == SC_VAL_PTR, (SC_VarVPointer)VarP->ValueP);
	    VarP->ValueType = SC_VAL_PTR;
	    VarP->ValueP = CXTP;
	}
    }

    // No args required!  Just returns TIME value
    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)Now);
}

// First IN *MUST* be FExt of SC_EXT_BRTIME
// Next N IN Args are flags, corresponding 1-1 to OUT args
// Each flag says WHAT should be stuffed into its OUT arg
Int16		SC_SysBrTime(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		OutCount = InOutCount >> 8;
    Uns8		TotCount = InCount + OutCount;
    Int16		I;
    SC_ChunkXTVPointer	CXTP;
    SC_VarVPointer	InVarP, OutVarP;
    struct tm *		BrTimeP;

    if ((InCount < 1) || (OutCount < InCount-1))
	return SC_RUN_BADARG;

    InVarP = SC_RUNSTACK_GETNTH(VMP, TotCount);			// First IN arg
    if ((InVarP->ValueType != SC_VAL_PTR) ||			// Stack pts to it
	! ((CXTP = (SC_ChunkXTVPointer)InVarP->ValueP)) ||	// Assign
	(CXTP->ExtType != SC_EXT_BRTIME))
	return SC_RUN_BADARG;
    BrTimeP = (struct tm *)CXTP->Ext;

    for (I = 0; I < InCount - 1; I++) {
	InVarP = SC_RUNSTACK_GETNTH(VMP, TotCount - (I + 1));
	OutVarP = SC_RUNSTACK_GETNTH(VMP, OutCount - I);
	SC_SAFE_CHECK((OutVarP->ValueP == 0LL) || (OutVarP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	if ((OutVarP = (SC_VarVPointer)OutVarP->ValueP)) {	// Not NULL!

	    // DeRef old value...
	    SC_VARDEREFCOND(VMP, OutVarP->ValueType == SC_VAL_PTR, (SC_VarVPointer)OutVarP->ValueP);
	    OutVarP->ValueType = SC_VAL_INT;
	    OutVarP->ValueP = (void *)0L;

	    if ((InVarP->ValueType != SC_VAL_INT) || ((Uns64)InVarP->ValueP > SC_SYS_BRTIME_MAX))
		return SC_RUN_BADARG;

	    switch ((Uns8)(Uns64)InVarP->ValueP) {
		case SC_SYS_SEC:	OutVarP->ValueP = (void *)(Int64)BrTimeP->tm_sec; break;
		case SC_SYS_MIN:	OutVarP->ValueP = (void *)(Int64)BrTimeP->tm_min; break;
		case SC_SYS_HOUR:	OutVarP->ValueP = (void *)(Int64)BrTimeP->tm_hour; break;
		case SC_SYS_MDAY:	OutVarP->ValueP = (void *)(Int64)BrTimeP->tm_mday; break;
		case SC_SYS_MON:	OutVarP->ValueP = (void *)(Int64)BrTimeP->tm_mon; break;
		case SC_SYS_YEAR:	OutVarP->ValueP = (void *)(Int64)BrTimeP->tm_year; break;
		case SC_SYS_WDAY:	OutVarP->ValueP = (void *)(Int64)BrTimeP->tm_wday; break;
		case SC_SYS_YDAY:	OutVarP->ValueP = (void *)(Int64)BrTimeP->tm_yday; break;
		case SC_SYS_ISDST:	OutVarP->ValueP = (void *)(Int64)BrTimeP->tm_isdst; break;
		default:	return SC_RUN_BADARG;
	    }
	}
    }

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)1);
}


Int16		SC_SysTicks(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8	TotCount = (InOutCount >> 8) + (Uns8)InOutCount;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    // No args required!  Just returns CLOCK ticks
    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)(Int64)clock());
}

Int16		SC_SysElapsedSecs(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Secs;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

    Secs = (double)0;
    if (InCount) {
	VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	if (VarP->ValueType == SC_VAL_INT)
	    Secs = (double)(Int64)VarP->ValueP / CLOCKS_PER_SEC;
    }

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Secs);
}




Int16		SC_SysMathCos(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = cos((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = cos(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}

Int16		SC_SysMathACos(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = acos((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = acos(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}

Int16		SC_SysMathCosh(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = cosh((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = cosh(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}

Int16		SC_SysMathSin(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = sin((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = sin(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}

Int16		SC_SysMathASin(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = asin((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = asin(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}

Int16		SC_SysMathSinh(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = sinh((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = sinh(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}



Int16		SC_SysMathTan(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = tan((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = tan(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}

Int16		SC_SysMathATan(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = atan((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = atan(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}

Int16		SC_SysMathTanh(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = tanh((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = tanh(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}



Int16		SC_SysMathLn(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = log((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = log(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}


Int16		SC_SysMathLog(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = log10((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = log10(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}


Int16		SC_SysMathPower(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	Var1P, Var2P;
    double		V1, ResV;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount < 2) return SC_RUN_BADARG;
    Var1P = SC_RUNSTACK_GETNTH(VMP, TotCount);
    Var2P = Var1P + 1;

    if (Var1P->ValueType == SC_VAL_INT)
	V1 = (Int64)Var1P->ValueP;
    else if (Var1P->ValueType == SC_VAL_FLT)
	V1 = *(double *)&Var1P->ValueP;
    else
	return SC_RUN_BADARG;

    if (Var2P->ValueType == SC_VAL_INT)
	ResV = pow(V1, (Int64)Var2P->ValueP);
    else if (Var2P->ValueType == SC_VAL_FLT)
	ResV = pow(V1, *(double *)&Var2P->ValueP);
    else
	return SC_RUN_BADARG;
	

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&ResV);
}


Int16		SC_SysMathExp(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = exp((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = exp(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}



Int16		SC_SysMathSqrt(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = sqrt((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = sqrt(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}


Int16		SC_SysMathFloor(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = floor((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = floor(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}

Int16		SC_SysMathCeiling(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = ceil((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = ceil(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}

Int16		SC_SysMathRound(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	VarP;
    double		Value;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) return SC_RUN_BADARG;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    if (VarP->ValueType == SC_VAL_INT)
	Value = round((Int64)VarP->ValueP);
    else if (VarP->ValueType == SC_VAL_FLT)
	Value = round(*(double *)&VarP->ValueP);
    else
	return SC_RUN_BADARG;

    // DeRef and flush *ALL* args!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_FLT, *(void **)&Value);
}

    // Gets SHP followed by optional Flags.
    // SHP can be 0/Null or even ""... permissible only if NullStrOk!
    Int16	SC_SysAuxEdGetNameArgs(SC_VarVPointer VarP, Uns8 InCount, SC_StrHPointer * StrPP, Uns8 * FlagsP, Int16 NullStrOk)
    {
	SC_StrHPointer	SHP = NULL;
	
	// NullStrOk --> InCount can be 0, but must have Str (even empty) for Flags.
	// NOTE:  NULL currently translates to INT-0, *NOT* PTR-0!!
	if (InCount == 0) {
	    if (NullStrOk) {
	       *StrPP = NULL; *FlagsP = 0;
	       return 1;
	    } else
		return 0;
	}

	// Assume Incount > 0 if here...
	if (VarP->ValueType == SC_VAL_INT) {
	    if (VarP->ValueP != NULL)				// 0/NULL for Str is ok
		return 0;

	} else if (VarP->ValueType == SC_VAL_PTR) {		// Definitely SHP
	    if ((! (SHP = (SC_StrHPointer)VarP->ValueP)) ||
		(SHP->Type != SC_ENTRY_STR))
		return 0;

	} else return 0;

	if ((SHP == NULL) || (SHP->StrDataP->StrLen == 0L)) {
	    if (NullStrOk) *StrPP = NULL;
	    else return 0;
	} else
	    *StrPP = SHP;

	++VarP;
	if ((InCount > 1) && (VarP->ValueType == SC_VAL_INT))
	    if (! SC_SysAuxGetEdPkgFlags((Uns32)(Int64)VarP->ValueP, SC_EDMASK_SHOW, FlagsP))
		return 0;

	return 1;
    }

// Opens file specified in String (IN) arg... Returns BufP in INT form.
// Shows (attach to a Pane) only if #SHOW flag.
//
// IN args
//	Filename	String, required (Cannot be NULL or "")
//	Flags		Int, Optional (#show or #hide)
//
// Returns
//	BufP (as Int64)

Int16	SC_SysEdReadFile(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Must be Uns8
    Uns8		TotCount = InCount + (InOutCount >> 8);		// Must be Uns8
    SC_VarVPointer	VarP;
    SC_StrHPointer	StrP;
    void *		BufP;
    Uns8		Flags;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    if (InCount && (SC_SysAuxEdGetNameArgs(VarP, InCount, &StrP, &Flags, 0))) {

	BufP = ED_EditorOpenFile(StrP->StrDataP->Data, Flags & SC_ED_SHOWFLAG, 1, 1);
	if (BufP == NULL) {
	    SC_RunErrorInfoStr = "Ed.ReadFile: Could not open/read file.";
	    return SC_RUN_BADARG;
	}
    
    } else {
	SC_RunErrorInfoStr = "Ed.ReadFile: Bad args.";
	return SC_RUN_BADARG;
    }

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, BufP);
}

// Opens/creates buffer specified in String (IN) arg... Returns BufP in INT form.
// Shows (attach to a Pane) only if #SHOW flag.
//
// IN args
//	Buffer name	String, can be "";
//	Flags		Int, Optional (#show or #hide)
//	(Can be called with NO args!!)
//
// Returns
//	BufP (as Int64)

Int16	SC_SysEdGetBuf(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Must be Uns8
    Uns8		TotCount = InCount + (InOutCount >> 8);		// Must be Uns8
    SC_VarVPointer	VarP;
    SC_StrHPointer	StrP;
    void *		BufP;
    Uns8		Flags;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    if (InCount && (SC_SysAuxEdGetNameArgs(VarP, InCount, &StrP, &Flags, 1))) {

	BufP = ED_EditorOpenBuffer((StrP) ? StrP->StrDataP->Data : NULL, Flags & SC_ED_SHOWFLAG);
	if (BufP == NULL) {
	    SC_RunErrorInfoStr = "Ed.GetBuf: Could not open buffer.";
	    return SC_RUN_BADARG;
	}
    
    } else {
	SC_RunErrorInfoStr = "Ed.GetBuf: Bad args.";
	return SC_RUN_BADARG;
    }

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, BufP);

}

// GetLen == 0 --> Get Str!
// GetLen == 1 --> Len in Bytes
// GetLen == 2 --> Len in Chars

Int16	SC_SysGetStrAux(SC_VMPointer VMP, Uns16 InOutCount, Int16 GetLen)
{
    Uns8		InCount = InOutCount;				// Must be Uns8
    Uns8		TotCount = InCount + (InOutCount >> 8);		// Must be Uns8
    SC_VarVPointer	VarP;
    void *		BufP;
    Int32		P1, P2;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    if (! SC_SysAuxGetRangeArgs(VarP, InCount, &BufP, &P1, &P2, NULL, 1))	// STR Pkg
	goto ErrorOut;

    if (GetLen) {
	Int64		Len;

	if (GetLen == 1)
	    Len = P2 - P1;				// ByteLen
	else
	    Len = ED_BufferCountChars(BufP, P1, P2);	// CharLen
    
	// DeRef and flush *ALL* args... even unwanted ones!
	SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
	// Push result SHP on stack, return error code.
	return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)Len);

    } else {
	SC_StrDataPointer	SDP;
	SC_StrHPointer		SHP;
    
	// Read into a string buffer, allows NULL strings, P1==P2!
	SDP = SC_StrStashBufferData(VMP, BufP, P1, P2);
	// Get new entry, or delete and re-use (++RefCount) previously hashed version!
	SHP = SC_StrGetDataEntry(VMP, SDP);

	// DeRef and flush *ALL* args... even unwanted ones!
	SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
	// Push result SHP on stack, return error code.
	return SC_SysAuxReturn(VMP, (SHP) ? SC_VAL_PTR : SC_VAL_INT, SHP);	
    }

ErrorOut:
    if (GetLen) {
	if (GetLen == 1)
	    SC_RunErrorInfoStr = "Ed.GetBLen: Bad args.";
	else
	    SC_RunErrorInfoStr = "Ed.GetCLen: Bad args.";
    } else
	SC_RunErrorInfoStr = "Ed.Str: Bad args.";
    return SC_RUN_BADARG;

}

// IN args:
//	BufP	Buffer from Editor (Comes in as (Int64) of void*)
//		(0LL means use CurScriptBuf)
//	L1	BytePos or CharPos	(#Byte or #Char)
//	L2	BytePos or CharPos	(#Byte or #Char) + #Pos
//		ByteLen or CharLen	(#Byte or #Char) + #Len
//	Flag	Optional
//      ...	Ignores rest
//
// OUT args:
//	...	Ignores all
//
// Returns:
//	Str

Int16	SC_SysEdStr(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysGetStrAux(VMP, InOutCount, 0);
}


// IN args:
//	BufP	Buffer from Editor (Comes in as (Int64) of void*)
//		(0LL means use CurScriptBuf)
//	L1	BytePos or CharPos	(#Byte or #Char)
//	L2	BytePos or CharPos	(#Byte or #Char) + #Pos
//		ByteLen or CharLen	(#Byte or #Char) + #Len
//	Flag	Optional (defaults to #Byte + #Pos)
//      ...	Ignores rest
//
// OUT args:
//	...	Ignores all
//
// Returns:
//	Length  in Bytes

Int16	SC_SysEdGetBLen(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysGetStrAux(VMP, InOutCount, 1);		// In Bytes
}

Int16	SC_SysEdGetCLen(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysGetStrAux(VMP, InOutCount, 2);		// In Chars
}


// IN args:
//	BufP	Buffer from Editor (Comes in as (Int64) of void*)
//	L	Optional BytePos or CharPos	(#Byte or #Char)
//	Flag	Optional (Default is BytePos)
//
// OUT args:
//	...	Ignores all
//
// Returns:
//	Pos	CharPos
//
//      L == 0 means give CursorPos... 
Int16	SC_SysAuxEdPos(SC_VMPointer VMP, Uns16 InOutCount, Int16 InChars)
{
    Uns8		InCount = InOutCount;				// Must be Uns8
    Uns8		TotCount = InCount + (InOutCount >> 8);		// Must be Uns8
    SC_VarVPointer	VarP;
    void *		BufP;
    Int32		P;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    if (! SC_SysAuxGetPosArgs(VarP, InCount, &BufP, &P, NULL))
	goto ErrorOut;

    if (P == 0) P = ED_ScriptGetCursor(BufP);
    if (InChars)
	P = ED_BufferCountChars(BufP, 0, P);

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result SHP on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)(Int64)P);

ErrorOut:
    SC_RunErrorInfoStr = (InChars) ? "Ed.GetCPos: Bad args." : "Ed.GetBPos: Bad args.";
    return SC_RUN_BADARG;
}

Int16	SC_SysEdGetBPos(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysAuxEdPos(VMP, InOutCount, 0);		// BytePos
}

Int16	SC_SysEdGetCPos(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysAuxEdPos(VMP, InOutCount, 1);		// CharPos
}

Int16	SC_SysEdGetSel(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Must be Uns8
    Uns8		OutCount = InOutCount >> 8;			// Must be Uns8
    Uns8		TotCount = InCount + OutCount;			// Must be Uns8
    SC_VarVPointer	VarP;
    void *		BufP;
    Uns8		Flags = 0;
    SC_VarVPointer	SVarP, EVarP;
    Int32		SPos, EPos;
    Uns8		HaveSel = 0;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (OutCount == 0) goto DoneDone;
    
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    // Get BufP
    if (InCount) {
	if (VarP->ValueType != SC_VAL_INT) goto ErrorOut;
	if ((BufP = VarP->ValueP)) {					// Assign
	    if (! ED_BufferCheckPtr(BufP)) goto ErrorOut;
	} else
	    BufP = ED_ScriptGetCurBuf(1);
	--InCount, ++VarP;

	// Get Flags!
	if (InCount && (VarP->ValueType == SC_VAL_INT))
	    if (! SC_SysAuxGetEdPkgFlags((Uns32)(Int64)VarP->ValueP, SC_EDMASK_CHAR | SC_EDMASK_LEN, &Flags))
		goto ErrorOut;	
    } else
	BufP = ED_ScriptGetCurBuf(1);

    // Now look at OutCount... will be > 0 if get here.
    SVarP = EVarP = NULL;
    VarP = SC_RUNSTACK_GETNTH(VMP, OutCount);
    // Get SVarP and DeRef previous value!
    if ((VarP->ValueType == SC_VAL_PTR) && ((SVarP = (SC_VarVPointer)VarP->ValueP)))		// Assign
	SC_VARDEREFCOND(VMP, SVarP->ValueType == SC_VAL_PTR, (SC_VarVPointer)SVarP->ValueP);
    if (--OutCount) {
	++VarP;
	if ((VarP->ValueType == SC_VAL_PTR) && ((EVarP = (SC_VarVPointer)VarP->ValueP)))	// Assign
	    SC_VARDEREFCOND(VMP, EVarP->ValueType == SC_VAL_PTR, (SC_VarVPointer)EVarP->ValueP);
    }

    // Now get SelRange of BufP... EPos is guaranteed >= to SPos!!
    ED_ScriptGetSel(BufP, &SPos, &EPos);
    if (SPos == EPos) {
	// No SelRange, just a cursor pos... calculate only S.
	if (Flags & SC_ED_CHARFLAG)
	    SPos = ED_BufferCountChars(BufP, 0, SPos);
	EPos = (Flags & SC_ED_LENFLAG) ? 0 : SPos;

    } else {
	// Have SelRange, so must calc S and E separately.
	HaveSel = 1;
	if (Flags & SC_ED_CHARFLAG) {
	    Int32	Start = SPos;
	    SPos = ED_BufferCountChars(BufP, 0, SPos);
	    EPos = SPos + ED_BufferCountChars(BufP, Start, EPos);
	}
	if (Flags & SC_ED_LENFLAG)
	    EPos -= SPos;
    }

    if (SVarP) SVarP->ValueType = SC_VAL_INT, SVarP->ValueP = (void *)(Int64)SPos;
    if (EVarP) EVarP->ValueType = SC_VAL_INT, EVarP->ValueP = (void *)(Int64)EPos;
 
DoneDone:
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result SHP on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (HaveSel) ? (void *)1L : (void *)0L);

ErrorOut:
    SC_RunErrorInfoStr = "Ed.Sel: Bad args.";
    return SC_RUN_BADARG;
}


// Gets BufP, L1, L2, and Flags (Optional)... returns BufP, P1, and P2 in BytePos!
//
// If BufPP is null, then DOES NOT get BufP from args!
// BufP == 0LL means get Cur ScriptBuf!

    Int32	SC_SysAuxStrCheckPos(char* StrP, Int32 Len, Int32 Pos, Int8 Dir)
    {
	char *		P;

	if (Pos < 0) return 0;
	if (Pos > Len) return Len;

	P = StrP + Pos;
	while (ED_MIDUTF8(*P)) {
	    Pos += Dir, P += Dir;
	    if (Pos == Len) break;
	}

	return Pos;
    }

    // Starting at Pos (bytes), go N more Chars and return new Pos.  Can fall
    // off end of String... Pos must be valid byte offset and NChar must be > 0.
    Int32	SC_SysAuxStrGoNChars(char * StrP, Int32 Len, Int32 Pos, Int32 NChar)
    {
	char *	P;
	char *  EndP = StrP + Len;

	P = StrP + Pos;		// Pos is BytePos
	while (NChar--) {	// Assign
	    P += ED_UtilGetUTF8Len(*P);
	    if (P >= EndP) return Len;
	}

	return P - StrP;
    }

    Int16 SC_SysStrAuxGet(SC_VMPointer VMP,  Uns16 InOutCount, Int16 DoDel)
    {
	Uns8			InCount, OutCount, TotCount;
	SC_VarVPointer		VarP;
	SC_StrHPointer		SHP;
	Int32			Len;
	Uns8			Flags;
	Int64			L1, L2;
	Int32			P1, P2;
	SC_StrDataPointer	NewSDP;

	InCount = InOutCount;				// Must be Uns8
	OutCount = InOutCount >> 8;			// Must be Uns8
	TotCount = InCount + OutCount;
	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);

	if (InCount < 3) goto ErrorOut;
	VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	// Get SHP
	if ((VarP->ValueType != SC_VAL_PTR) ||
	    ! ((SHP = (SC_StrHPointer)VarP->ValueP)) ||	// Assign
	    (SHP->Type != SC_ENTRY_STR))
	    goto ErrorOut;

	Len = SHP->StrDataP->StrLen;
	InCount -= 3;					// Will get all

	// Get L1 and L2
	VarP++;
	if (VarP->ValueType != SC_VAL_INT) goto ErrorOut;
	L1 = (Int64)VarP->ValueP;
	if ((L1 < 0) || (L1 > Len)) goto ErrorOut;

	VarP++;
	if (VarP->ValueType != SC_VAL_INT) goto ErrorOut;
	L2 = (Int64)VarP->ValueP;

	// Flags are optional
	VarP++, Flags = 0;
	if (InCount && (VarP->ValueType == SC_VAL_INT))
	    if (! SC_SysAuxGetStrPkgFlags((Uns32)(Int64)VarP->ValueP, SC_STRMASK_LEN | SC_STRMASK_CHAR, &Flags))
		goto ErrorOut;

	// Process L1 and L2... check for LEN flag first
	if (Flags & SC_STR_LENFLAG) {
	    L2 = L1 + L2;
	    if (L2 < 0) L2 = 0;
	}

	// L1 and L2 are Pos now (Char or Byte)!
	// Len is upper bound if BytePos, CharPos can only be less.
	if ((L2 == -1) || (L2 > Len)) L2 = Len;
	else if ((L2 < 0) || (L2 == L1)) goto ErrorOut;

	if (L1 < L2) P1 = (Int32)L1, P2 = (Int32)L2;
	else	     P1 = (Int32)L2, P1 = (Int32)L1;

	if (Flags & SC_STR_CHARFLAG) {
	    // Convert to BytePos
	    P1 = SC_SysAuxStrGoNChars(SHP->StrDataP->Data, Len, 0, P1);
	    P2 = SC_SysAuxStrGoNChars(SHP->StrDataP->Data, Len, P1, P2 - P1);
	} else if (DoDel) {
	    // Check NOT middle of UTF-8 char!  Deleting, so minimize interval
	    P1 = SC_SysAuxStrCheckPos(SHP->StrDataP->Data, Len, P1, +1);
	    P2 = SC_SysAuxStrCheckPos(SHP->StrDataP->Data, Len, P2, -1);
	} else {
	    // Check NOT middle of UTF-8 char!  Subsetting, so maximize interval
	    P1 = SC_SysAuxStrCheckPos(SHP->StrDataP->Data, Len, P1, -1);
	    P2 = SC_SysAuxStrCheckPos(SHP->StrDataP->Data, Len, P2, +1);
	}

	// Reuse Len and later SHP here!
	if (DoDel) {
	    Len -= P2 - P1;
	    NewSDP = SC_StrStashAlloc(VMP, Len);
	    NewSDP->StrLen = Len;
	    memcpy(NewSDP->Data, SHP->StrDataP->Data, P1);			// First Part
	    if (Len > P1)							// Second Part
		memcpy(NewSDP->Data+P1, SHP->StrDataP->Data+P2, Len-P1);

	} else {
	    Len = P2 - P1;
	    NewSDP = SC_StrStashAlloc(VMP, Len);
	    NewSDP->StrLen = Len;
	    memcpy(NewSDP->Data, SHP->StrDataP->Data + P1, Len);
	}
	NewSDP->Data[Len] = 0;
	SC_DEBUG_CHECKSLAB(VMP->StrDataSP);

	if (OutCount) {
	    VarP = SC_RUNSTACK_GETNTH(VMP, OutCount);				// Pts to stack
	    SC_SAFE_CHECK((VarP->ValueP == 0LL) || (VarP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	    if ((VarP = (SC_VarVPointer)VarP->ValueP)) {			// Assign (Var!)
		SC_VARDEREFCOND(VMP, VarP->ValueType == SC_VAL_PTR, VarP->ValueP);	// Old value
		VarP->ValueType = SC_VAL_INT;
		VarP->ValueP = (void *)(Int64)Len;
	    }
	}

	// Get new entry, or delete and re-use (++RefCount) previously hashed version!
	SHP = SC_StrGetDataEntry(VMP, NewSDP);

	// DeRef and flush *ALL* args... even unwanted ones!
	SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
	// Push result SHP on stack, return error code.
	return SC_SysAuxReturn(VMP, (SHP) ? SC_VAL_PTR : SC_VAL_INT, SHP);

    ErrorOut:
	SC_RunErrorInfoStr = "Str.GetSub: Bad arg.";
	return SC_RUN_BADARG;	
    }

Int16	SC_SysStrGetSub(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysStrAuxGet(VMP, InOutCount, 0);
}


Int16	SC_SysStrDelTxt(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysStrAuxGet(VMP, InOutCount, 1);
}


// IN args:
//	Str	Ref to SC_StrHPointer
//      ...	Ignores rest
//
// OUT args:
//	...	Ignores all
//
// Returns:
//	Pos	Len in Bytes or Chars, depending on Flag

Int16	SC_SysAuxStrGetLen(SC_VMPointer VMP, Uns16 InOutCount, Int16 InChars)
{
    Uns8		InCount = InOutCount;				// Must be Uns8
    Uns8		TotCount = InCount + (InOutCount >> 8);		// Must be Uns8
    SC_VarVPointer	VarP;
    SC_StrHPointer	SHP;
    Int32		Len;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount < 1) goto ErrorOut;

    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    // Get SHP
    if ((VarP->ValueType != SC_VAL_PTR) ||
	! ((SHP = (SC_StrHPointer)VarP->ValueP)) ||	// Assign
	(SHP->Type != SC_ENTRY_STR))
	goto ErrorOut;

    if (InChars)
	Len = SC_StrGetCharLen(SHP);
    else
	Len = SHP->StrDataP->StrLen;

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result SHP on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)(Int64)Len);

ErrorOut:
    SC_RunErrorInfoStr = (InChars) ? "Str.GetCLen: Bad arg." : "Str.GetBLen: Bad arg.";
    return SC_RUN_BADARG;
}

Int16	SC_SysStrGetBLen(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysAuxStrGetLen(VMP, InOutCount, 0);	// Byte len
}

Int16	SC_SysStrGetCLen(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_SysAuxStrGetLen(VMP, InOutCount, 1);	// Char len
}


Int16	SC_SysScriptGetSelf(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		TotCount = (Uns8)InOutCount + (InOutCount >> 8);		// Must be Uns8
    
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result SHP on stack, return error code.
    SC_VARREF((SC_VarVPointer)VMP->RunScriptP);
    return SC_SysAuxReturn(VMP, SC_VAL_PTR, (void *)VMP->RunScriptP);	// Definitely NOT NULL!
}

    SC_ScriptPointer	SC_SysScriptAuxGetScript(SC_VMPointer VMP, Uns8 TotCount)
    {
	SC_VarVPointer		VP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	SC_ScriptPointer	ScriptP;

	if ((VP->ValueType != SC_VAL_PTR) ||
	    ! ((ScriptP = (SC_ScriptPointer)VP->ValueP)) ||	// Assign
	    (ScriptP->Type != SC_CHUNK_SCRPT))
	    return NULL;

        return ScriptP;
    }

// IN arg --> ScriptPointer means get its Sup... otherwise just VMP->RunScriptP
Int16	SC_SysScriptGetSup(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Must be Uns8
    Uns8		TotCount = InCount + (InOutCount >> 8);		// Must be Uns8
    SC_ScriptPointer	ScriptP;
    SC_ScriptPointer	SupP = NULL;

    if (InCount) {
	if ((ScriptP = SC_SysScriptAuxGetScript(VMP, TotCount)))	// Assign
	    SupP = ScriptP->SupScriptP;
	else {
	    SC_RunErrorInfoStr = "Script.Super: Bad arg.";
	    return SC_RUN_BADARG;    
	}

    } else
	SupP = VMP->RunScriptP->SupScriptP;
    
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result SHP on stack, return error code.
    if (SupP) {
	SC_VARREF((SC_VarVPointer)SupP);
	return SC_SysAuxReturn(VMP, SC_VAL_PTR, SupP);
    } else
	return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)0LL);
}

// IN arg --> ScriptPointer means get its Sup... otherwise just VMP->RunScriptP
Int16	SC_SysScriptGetId(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Must be Uns8
    Uns8		TotCount = InCount + (InOutCount >> 8);		// Must be Uns8
    SC_ScriptPointer	ScriptP;
    Int64		Res = 0LL;

    if (InCount) {
	if ((ScriptP = SC_SysScriptAuxGetScript(VMP, TotCount)))	// Assign
	    Res = (Int64)ScriptP->ScriptId;
	else {
	    SC_RunErrorInfoStr = "Script.Id: Bad arg.";
	    return SC_RUN_BADARG;    
	}

    } else
	Res = (Int64)VMP->RunScriptP->ScriptId;
    
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result SHP on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)Res);
}

// IN arg --> ScriptPointer means get its Sup... otherwise just VMP->RunScriptP
Int16	SC_SysScriptGetLevel(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Must be Uns8
    Uns8		TotCount = InCount + (InOutCount >> 8);		// Must be Uns8
    SC_ScriptPointer	ScriptP;
    Int64		Res = 0LL;

    if (InCount) {
	if ((ScriptP = SC_SysScriptAuxGetScript(VMP, TotCount)))	// Assign
	    Res = (Int64)ScriptP->ScriptLevel;
	else {
	    SC_RunErrorInfoStr = "Script.Level: Bad arg.";
	    return SC_RUN_BADARG;    
	}

    } else
	Res = (Int64)VMP->RunScriptP->ScriptLevel;
    
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result SHP on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)Res);
}

// Exported function... called from ED to RUN a (MAIN not SUB) script file.
// *** DO NOT include ".sc" or ".bsc" in NameP ***
//
// Returns 0 if NoErr
// Returns 1 if ERROR
//
// NOTE: This is definitely NOT in interactive mode.  If .sc file... will
//	 compile, which wipes out prior INTERACT InGDictP.  But if in .bsc, will
//	 leave any prior InGDictP intact... allowing LATER resumption of
//	 "interaction".

Int16	SC_ScriptDoFile(void * SMP, char * NameP, char * PathP, Uns8 IsBinary)
{
    SC_VMPointer	VMP = (SC_VMPointer)SMP;
    char *		FullPathP = ED_UtilGetFullPathBuf();	// Borrow from ED!
    Int32		FD;
    SC_ScriptPointer	ScriptP;
    
    sprintf(FullPathP, "%s/%s", PathP, NameP);
    FD = open(FullPathP, O_RDONLY);
        if (FD == -1) return 1;

	// File is open... Read .BSC or .SC file?
	ScriptP = (IsBinary)
		    ? SC_SysReadBSC(VMP, FD, NameP)
		    : SC_SysReadScript(VMP, FD);
    close(FD);

    if (! ScriptP) return 1;	// Failed to read, compile, or load!

    // Add in Name+Path for ScriptP
    ScriptP->NameSHP = SC_StrGetEntry(VMP, NameP, strlen(NameP));
    ScriptP->PathSHP = SC_StrGetEntry(VMP, PathP, strlen(PathP));

    // Now execute the ScriptP
    SC_RunZapOldState(VMP);
    
    ED_ScriptRunState();
    SC_RunStackInit(VMP);
    ScriptP->StartStackTop = 0;
    ScriptP->ScriptLevel = 1;
    VMP->RunScriptP = ScriptP;
    SC_VARDEREFCOND(VMP, VMP->CompileScriptP, (SC_VarVPointer)VMP->CompileScriptP);
    VMP->CompileScriptP = NULL;
    
    SC_RunMakeScriptGlob(VMP, NULL, 0);		// MAIN not Sub
    // Call RunMakeRunRecord with 0, it is TOP of Script, no InOutCount
    ScriptP->TopRunP = SC_RunMakeRunRecord(VMP, ScriptP->TopClosP, 0, 0);
    ScriptP->TopRunP->Flags |= SC_RUN_NOTRECOPT;

	// Now RunExecCurFunc,  pop return value!
	SC_RunExecCurFunc(VMP, 0);

    SC_ScriptZapAfterRun(VMP, ScriptP, IsBinary);

    SC_RUNSTACK_FLUSH_DEREF(VMP, 0);
    SC_LogCleanup(ScriptP->ScriptLevel);	// In case NOT closed!
    ED_ScriptNoState(NULL);
    return 0;
}

// IN arg must specify filename/path.
Int16	SC_SysScriptLoad(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Must be Uns8
    Uns8		TotCount = InCount + (InOutCount >> 8);		// Must be Uns8
    SC_VarVPointer	VarP;
    SC_StrHPointer	SHP;
    SC_ScriptPointer	ScriptP = NULL;
    char *		PathP;
    char *		NameP;
    char *		FullPathP;
    char *		StrP;
    Uns8		IsB;		// File type is Binary ".bsc"
    Int32		FD;
    Int16		DotOffset;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) goto ErrorOut;
    
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    // NOTE:  Calls realpath just as a check!
    if ((VarP->ValueType != SC_VAL_PTR) ||
	! ((SHP = (SC_StrHPointer)VarP->ValueP)) ||			// Assign
	(SHP->Type != SC_ENTRY_STR) ||
	ED_UtilGetPath(SHP->StrDataP->Data, &PathP, &NameP, &FullPathP) ||
	! ((DotOffset = SC_ScriptGetFileType(NameP, &IsB))) ||		// Assign (.sc/.bsc only)
	! ((StrP = realpath(PathP, NULL))))				// Assign
	goto ErrorOut;

    free(StrP);		// Malloc from realpath
    
    FD = open(FullPathP, O_RDONLY);
        if (FD == -1) goto ErrorOut;

	SC_DEBUG_STARTPRINT(VMP, "LOAD SUB", NameP, 0);			// Announce it

	// File is open... Read .BSC or .SC file?
	ScriptP = (IsB)
		    ? SC_SysReadBSC(VMP, FD, NameP)
		    : SC_SysReadScript(VMP, FD);
    close(FD);

    // Add in Name+Path for ScriptP
    if (ScriptP) {
	ScriptP->NameSHP = SC_StrGetEntry(VMP, NameP, strlen(NameP));
	ScriptP->PathSHP = SC_StrGetEntry(VMP, PathP, strlen(PathP));
    }
    
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, (ScriptP) ? SC_VAL_PTR : SC_VAL_INT, ScriptP);

ErrorOut:
    SC_RunErrorInfoStr = "Script.Load: Bad file spec/missing file.";
    return SC_RUN_BADARG;
}

// Exec Script in first IN Var.
//
// NOTE: Sub-Script CANNOT return RCP to superior!

Int16	SC_SysScriptExec(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;				// Must be Uns8
    Uns8		TotCount = InCount + (InOutCount >> 8);		// Must be Uns8
    SC_VarVPointer	VarP;
    SC_ScriptPointer	ScriptP;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount == 0) goto ErrorOut;
    
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);		// First IN var
    if ((VarP->ValueType != SC_VAL_PTR) ||
	! ((ScriptP = (SC_ScriptPointer)VarP->ValueP)) ||
	(ScriptP->Type != SC_CHUNK_SCRPT))
	goto ErrorOut;

    // *********************************************************************
    // *IF* a Script can (one day) gain access to its sup or share another's
    // GlobP, then must check that said Script is *NOT* already running!
    // *********************************************************************

    // Queue the ScriptP.
    // Already in ED_ScriptRunState and will remain even after return!!
    ScriptP->StartStackTop = VMP->StackTop;
    ScriptP->ScriptLevel = VMP->RunScriptP->ScriptLevel + 1;
    ScriptP->SupScriptP = VMP->RunScriptP;
    VMP->RunScriptP = ScriptP;
    
    SC_RunMakeScriptGlob(VMP, NULL, 1);
    // Call RunMakeRunRecord with 0, it is TOP of Script, no InOutCount
    ScriptP->TopRunP = SC_RunMakeRunRecord(VMP, ScriptP->TopClosP, 0, 0);
    
	// Now RunExecCurFunc,  get return value!
	SC_RunExecCurFunc(VMP, 0);

    // *DO NOT* call SC_ScriptZapAfterRun here!!  Zap GlobP, leave Script
    // intact.... Sub-Script is in a var and may be called to run again!
    SC_RunZapGlob(VMP, ScriptP->GlobP);
    ScriptP->GlobP = NULL;
    
    // Script pushed its result on top of the stack!
    VarP = SC_RUNSTACK_POP(VMP);
    SC_LogCleanup(ScriptP->ScriptLevel);		// In case NOT closed!
    
    // Unqueue the ScriptP... 
    VMP->RunScriptP = ScriptP->SupScriptP;
    ScriptP->SupScriptP = NULL;
    // DeRef and flush *ALL* args... even unwanted ones!
    // This will DeRef ScriptP *FINALLY*... for getting off the stack!
    SC_RUNSTACK_FLUSH_DEREF(VMP, ScriptP->StartStackTop - TotCount);
 
    // VarP is still pointing to result of Script... as it was Popped.
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, VarP->ValueType, VarP->ValueP);

ErrorOut:
    SC_RunErrorInfoStr = "Script.Run: Bad arg.";
    return SC_RUN_BADARG;
}

SC_ScriptPointer	SC_SysReadScript(SC_VMPointer VMP, Int32 FD)
{
    Int32		FSize;
    char *		MemP;
    SC_ScriptPointer	ScriptP;

    // Get file size and allocate memory buffer
    FSize = lseek(FD, 0, SEEK_END);
    if (FSize == -1) return NULL;
    MemP = (char *)malloc(SC_LLALIGN(FSize));
    if (! MemP) {
	G_SETEXCEPTION("Script.Load: Malloc failed", 0);
	return NULL;
    }

    // Read entire script into memory
    lseek(FD, 0, SEEK_SET);
    if (FSize != read(FD, MemP, FSize)) {
	free(MemP);
	G_SETEXCEPTION("Script.Load: Read failed", 0);
	return NULL;
    }

    // Compile script!  (Caller will add in Name+Path SHP later)
    SC_CompileScript(VMP, MemP, FSize, NULL, NULL, 1, 0, 0);
    free(MemP);
    ScriptP = VMP->CompileScriptP;
    VMP->CompileScriptP = NULL;
    return ScriptP;			// Could be NULL if compile error!
}

// ********************************************************************************
// BSC (Binary Script Files)
// ********************************************************************************
//
// BSC is the binary output, compiled version of the source script.  It is
// written out after a compile and can be read/executed directly.
//
// The binary file has a header, followed by N Konstants, S Strings, and F
// Funcs.  Konsts (KonstCount of them) are simply stored one after another.  The
// length (Uns32) of each string is stored first, followed by its char data and
// the two are padded to long align for the next one.  Finally, the first (Top)
// function CBP is stored ("BSC Code" format), followed by each of its
// sub-functions, and each of them are followed by their sub-functions...
//
// NOTE: DEF are not stored here... they are only for the compile phase!
//	 There are also no GNames!  So cannot TRY after running .bsc script.

typedef struct _SC_BSCFileHeader {
    Uns16		VMajor;
    Uns16		VMinor;
    Uns16		Flags;
    Uns16		GlobCount;

    Uns32		FuncCount;		// Total in this script
    Uns32		KonstCount;
    Uns32		StringCount;
    Uns32		CodeStart;		// Offset to first BSCCodeHeader

    // Konsts:	KonstCount [Int64][][]...
    // Strings:	StringCount [Uns32 Len + char Str... Pad to L-Align][][]...
    // Funcs:	FuncCount [BSCCodeBlock][][]... records.		

} SC_BSCFileHeader, *SC_BSCFilePointer;

// Each "Code Seg" starts with a header, followed by switch cases for THIS
// function, closed vars, and finally the actual code bytes.

typedef struct _SC_BSCCodeHeader {
    Uns32		SegLen;			// Len for everything here
    Uns32		CodeLen;		// Len of CodeArr
    Uns32		CaseCount;		// How many pairs in CaseDictP
    Uns32		SupIndex;		// Which is superior function

    Uns8		Flags;
    Uns8		RCount;
    Uns16		SubCount;		// How many sub-func
    Uns16		InCount;
    Uns16		OutCount;
    Uns16		LCount;
    Uns16		CCount;			// Closed vars + Desc

    // CaseCount [BSCCaseElt][][]...
    // CCount of [ClosedVarDesc each Uns8+Uns8][][]...
    // CodeLen [...] bytes of code

} SC_BSCCodeHeader, *SC_BSCCodePointer;


typedef struct _SC_BSCCaseElt {
    Int64	Key;				// Int/Flt or StrIndex
    Uns32	JmpTo;				// Value (ByteOffset)
    Uns16	Id;				// Which Switch 1..SC_CGMAXSWITCHCOUNT
    Uns16	KeyType;			// SC_VAL_INT/SC_VAL_FLT/SC_VAL_PTR
} SC_BSCCaseRecord, *SC_BSCCasePointer;


#ifdef DEBUG
    // Same output for READING and WRITING BSC file!
    void	SC_DebugRWBSC_Open(SC_VMPointer VMP, SC_BSCFileHeader * FHP, char * FNP, Uns8 DoWrite)
    {
	SC_PrintFuncP	OutPFP = VMP->OutPrintFP;
	Uns32		Len;

	Len = sizeof(SC_BSCFileHeader) + (FHP->KonstCount * sizeof(Uns64));
	Len = FHP->CodeStart - Len;		// For strings!

	OutPFP("**************************************************************\n");
	if (DoWrite)
	    OutPFP("Writing BSC file for %s\n", FNP);
	else
	    OutPFP("Reading BSC file from %s\n", FNP);
	OutPFP("   Version: %d.%d\n", FHP->VMajor, FHP->VMinor);
	OutPFP("   Globs:   %d\n", FHP->GlobCount);
	OutPFP("   Konsts:  %d\n", FHP->KonstCount);
	OutPFP("   Strings: %d (%'d Bytes)\n", FHP->StringCount, Len);
	OutPFP("   Funcs:   %d\n", FHP->FuncCount);
    }

    void	SC_DebugRWBSC_Close(SC_VMPointer VMP)
    {
	VMP->OutPrintFP("**************************************************************\n\n");
    }

    void	SC_DebugRWBSC_Func(SC_VMPointer VMP, Uns32 ThisI, SC_BSCCodeHeader * CHP)
    {
	SC_PrintFuncP	OutPFP = VMP->OutPrintFP;

	if (ThisI) {
	    OutPFP("\n   -- Function %d (In F:%d) Bytes:%'d Subs:%d\n", ThisI,  CHP->SupIndex, CHP->SegLen, CHP->SubCount);
	    OutPFP("      Code:%'d Bytes (Reg:%d Loc:%d Clos:%d In:%d Out:%d) Cases:%d\n",
			  CHP->CodeLen, CHP->RCount, CHP->LCount, CHP->CCount, CHP->InCount, CHP->OutCount, CHP->CaseCount);
	} else {
	    OutPFP("\n   -- Function (Main) Bytes:%'d Subs:%d\n", CHP->SegLen, CHP->SubCount);
	    OutPFP("      Code:%'d Bytes (Reg:%d Loc:%d) Cases:%d \n",
			  CHP->CodeLen, CHP->RCount, CHP->LCount, CHP->CaseCount);
	}
    }

    #define  SC_DEBUG_BSCREADOPEN(_VMP, _FHP, _FNP)	SC_DebugRWBSC_Open(_VMP, _FHP, _FNP, 0)
    #define  SC_DEBUG_BSCREADFUNC(_VMP, _I, _CHP)	SC_DebugRWBSC_Func(_VMP, _I, _CHP)
    #define  SC_DEBUG_BSCREADCLOSE(_VMP)		SC_DebugRWBSC_Close(_VMP)    

    #define  SC_DEBUG_BSCWRITEOPEN(_VMP, _FHP, _FNP)	SC_DebugRWBSC_Open(_VMP, _FHP, _FNP, 1)
    #define  SC_DEBUG_BSCWRITEFUNC(_VMP, _I, _CHP)	SC_DebugRWBSC_Func(_VMP, _I, _CHP)
    #define  SC_DEBUG_BSCWRITECLOSE(_VMP)		SC_DebugRWBSC_Close(_VMP)
#else
    #define  SC_DEBUG_BSCREADOPEN(_VMP, _FHP, _FNP)
    #define  SC_DEBUG_BSCREADFUNC(_VMP, _I, _CHP)
    #define  SC_DEBUG_BSCREADCLOSE(_VMP)

    #define  SC_DEBUG_BSCWRITEOPEN(_VMP, _FHP, _FNP)
    #define  SC_DEBUG_BSCWRITEFUNC(_VMP, _T, _CHP)
    #define  SC_DEBUG_BSCWRITECLOSE(_VMP)
#endif



#define		SC_BSC_READSIZE		(1024 * 8)

    void	SC_SRBSCAuxZapFuncArr(SC_VMPointer VMP, SC_CodeBlockPointer * FuncArr, Uns32 Count)
    {
	SC_CodeBlockPointer *		PP;

	if ((PP = FuncArr)) {		// Assign
	    while (Count--) SC_VARDEREF(VMP, (SC_VarVPointer)*PP++);
	    free(FuncArr);
	}
    }

SC_ScriptPointer	SC_SysReadBSC(SC_VMPointer VMP, Int32 FD, char * FNameP)
{
    // Assumes FD is open for read

    SC_ScriptPointer		ScriptP = NULL;
    SC_CodeBlockPointer	*	FuncArr = NULL;
    SC_BSCFileHeader		FHeader;
    SC_ArrDataPointer		ADP;
    Uns32			Len, I, J;
    char *			MemP;

    if ((-1 == lseek(FD, 0, SEEK_SET)) ||
        (-1 == read(FD, &FHeader, sizeof(SC_BSCFileHeader))))
	goto ErrorOut;

    if ((FHeader.VMajor != SC_VERSIONMAJOR) ||	// ???? Error Code?
	(FHeader.VMinor > SC_VERSIONMINOR))
	goto ErrorOut;

    ScriptP = SC_CGScriptMake(VMP);
    ScriptP->GlobCount = FHeader.GlobCount;
    ScriptP->FuncCount = FHeader.FuncCount;
    ScriptP->KonstCount = FHeader.KonstCount;
    ScriptP->StringCount = FHeader.StringCount;

    SC_DEBUG_BSCREADOPEN(VMP, &FHeader, FNameP);

    // Read Konst
    Len = FHeader.KonstCount * sizeof(Int64);
    if (Len) {
	ADP = SC_ArrAllocNewData(VMP, &ScriptP->KonstADP, FHeader.KonstCount, 0);
	if (-1 == read(FD, ADP->Data, Len))
	    goto ErrorOut;
    }

    // Read Strings
    Len = FHeader.CodeStart - (sizeof(SC_BSCFileHeader) + Len);
    if (Len) {
	// Must zero out StringADP... might fail while reading Nth string!
	ADP = SC_ArrAllocNewData(VMP, &ScriptP->StringADP, FHeader.StringCount, 1);

	if (Len < SC_BSC_READSIZE) {
	    char *		P;
	    SC_StrHPointer	SHP;

	    // Read string data into 1 buffer... then process
	    MemP = malloc(Len);
	    if (! MemP) SC_SysError("BSC Read: Malloc StrBlock failed.");
	    if (-1 == read(FD, MemP, Len)) {free(MemP); goto ErrorOut;}

		P = MemP;
		for (I = 0; I < FHeader.StringCount; I++) {
		    Len = *(Uns32 *)P, P += sizeof(Uns32);
		    ADP->Data[I] = SHP = SC_StrGetEntry(VMP, (char *)P, Len);
		    P += SC_LALIGN(Len + 1);
		}

	    free(MemP);
	    
	} else {
	    SC_StrDataPointer	SDP;

	    // May be faster if FHeader stashed length of LONGEST string during
	    // write... then read into a single malloc each time.

	    // Read strings one by one... (assuming some are big!)
	    for (I = 0; I < FHeader.StringCount; I++) {
		if (-1 == read(FD, &Len, sizeof(Uns32))) goto ErrorOut;

		// Have Len... allocate SDP for it.
		SDP = SC_StrStashAlloc(VMP, Len);	// leaves room to long-align + \0
		SDP->StrLen = Len;
		Len = SC_LALIGN(Len + 1);		// +1 for ending NULL char
		if (-1 == read(FD, SDP->Data, Len)) {
		    SC_SlabZapItem(VMP->StrDataSP, SDP, SDP->BlockLen);
		    goto ErrorOut;
		}
		
		// SC_StrGetDataEntry will zap SDP if already in!
		ADP->Data[I] = SC_StrGetDataEntry(VMP, SDP);
	    }
	}
    }

    // Now read Funcs!
    {
	SC_BSCCodeHeader	CH;
	SC_CodeBlockPointer	CBP;
	SC_BSCCasePointer	BSCCaseP;

	// BEWARE: When a Func (CBP) is read, it will get stashed in the FuncArr.
	//	   This accounts for its RefCount of 1.  Later, when it is stashed
	//	   in its SUP CBP it will get RecCount incremented to 2...  This
	//	   allows for proper cleanup if mid-stream error.  Two pointers to
	//	   a CBP means a RefCount of 2 !!

	// Create and init the FuncArr...
	Len = FHeader.FuncCount * sizeof(void *);
	FuncArr = (SC_CodeBlockPointer *)malloc(Len);
	if (! FuncArr) SC_SysError("BSC Read: Malloc FuncArr failed.");

	for (I = 0; I < FHeader.FuncCount; I++) {
	    // Read the CodeHeader first
	    if (-1 == read(FD, &CH, sizeof(SC_BSCCodeHeader))) goto ErrorOut;
	    
	    CBP = SC_CodeBlockMake(VMP, CH.CodeLen, CH.CCount, CH.SubCount);
	    FuncArr[I] = CBP;
	    // Fill the CBP slots.... CodeLen, CCount, and FuncCount are already
	    // filled.  Reset FuncCount to 0, used to determine FuncId of subs!!
	    CBP->FuncCount = 0;
	    CBP->Flags = CH.Flags;
	    CBP->RCount = CH.RCount;
	    CBP->InCount = CH.InCount;
	    CBP->OutCount = CH.OutCount;
	    CBP->LCount = CH.LCount;
	    CBP->CodeId = VMP->LastCodeId++;
	    CBP->CaseDictP = NULL;

	    // Read the flattened case dict
	    Len = CH.CaseCount * sizeof(SC_BSCCaseRecord);
	    if (Len) {
		SC_CaseHPointer	CaseP;
		
		MemP = malloc(Len);
		if (! MemP) SC_SysError("BSC Read: Malloc CaseBlock failed.");
		if (-1 == read(FD, MemP, Len)) {free(MemP); goto ErrorOut;}

		CBP->CaseDictP = SC_DictCreateNewDict(VMP, CH.CaseCount);
		BSCCaseP = (SC_BSCCasePointer)MemP;
		for (J = 0; J < CH.CaseCount; J++, BSCCaseP++) {
		    CaseP = (SC_CaseHPointer)SC_BigEntryNew(VMP);
		    CaseP->Type = SC_ENTRY_CASE;
		    CaseP->ValType = SC_VAL_CASE;
		    CaseP->KeyType = BSCCaseP->KeyType;
		    CaseP->Key = BSCCaseP->Key;
		    CaseP->Id = BSCCaseP->Id;
		    CaseP->Value = BSCCaseP->JmpTo;
		    SC_DictStashNewPair(VMP, CBP->CaseDictP, (SC_PairHPointer)CaseP);
		}

		free(MemP);
	    }

	    // Read the Closed VDescRecords directly into CBP.
	    Len = CH.CCount * sizeof(SC_VDescRecord);
	    if (Len && (-1 == read(FD, (Uns8 *)CBP + CH.CodeLen, Len))) goto ErrorOut;

	    // Read the ByteCodes directly into the CBP.
	    if (-1 == read(FD, (Uns8 *)CBP->CodeArr, CH.CodeLen)) goto ErrorOut;

	    // Stash this CBP (if NOT Top (I == 0))  into its superior
	    if (I) {
		SC_CodeBlockPointer	SupCBP = FuncArr[CH.SupIndex];

		SC_VARREF((SC_VarVPointer)CBP);		// SupCBP will point to it too!
		Len = SC_LLALIGN(SupCBP->CodeLen + (sizeof(SC_VDescRecord) * SupCBP->CCount));
		// Re-use MemP as FuncArr of SupCBP!
		MemP = (char *)SupCBP->CodeArr + Len;
		*((SC_CodeBlockPointer *)MemP + SupCBP->FuncCount) = CBP;
		CBP->FuncId = SupCBP->FuncCount++;
	    }

	    SC_DEBUG_BSCREADFUNC(VMP, I, &CH);
	}

	// Turn Top func into Clos and stash in ScriptP (RunMakeClos DOES VARREF).
	ScriptP->TopClosP = SC_RunMakeClos(VMP, 0, FuncArr[0]);
	SC_SRBSCAuxZapFuncArr(VMP, FuncArr, FHeader.FuncCount);
	FuncArr = NULL;
    }

    SC_DEBUG_BSCREADCLOSE(VMP);
    return ScriptP;
    
ErrorOut:

    SC_SRBSCAuxZapFuncArr(VMP, FuncArr, FHeader.FuncCount);
    SC_VARDEREF(VMP, (SC_VarVPointer)ScriptP);
    return NULL;
}

    Uns32	SC_RWBSC_SegIndex;			// Count of Func Segs written		

    Int16	SC_RWBSCAuxWriteFunc(SC_VMPointer VMP, Uns32 SupI, Int32 FD, SC_CodeBlockPointer CBP)
    {
	SC_BSCCodeHeader	CHeader;
	Uns32			ThisI;
	Int32			Res;
	Uns32			Len;
	void *			MemP;

	// Uses global to track Index for this function... ThisI will be Sub to its subs!
	ThisI = SC_RWBSC_SegIndex++;
	
	// Set up the header, then write it.
	CHeader.CodeLen = CBP->CodeLen;
	CHeader.CaseCount = (CBP->CaseDictP) ? (Uns32)(Uns64)CBP->CaseDictP->PairCount : 0;
	CHeader.SegLen = sizeof(SC_BSCCodeHeader) +
			    (CHeader.CaseCount * sizeof(SC_BSCCaseRecord)) +
			    (CBP->CCount * sizeof(SC_VDescRecord)) +
			    CBP->CodeLen;
	CHeader.SupIndex = SupI;

	CHeader.SubCount = CBP->FuncCount;
	CHeader.Flags = CBP->Flags;
	CHeader.RCount = CBP->RCount;
	CHeader.InCount = CBP->InCount;
	CHeader.OutCount = CBP->OutCount;
	CHeader.LCount = CBP->LCount;
	CHeader.CCount = CBP->CCount;
	if (-1 == write(FD, &CHeader, sizeof(CHeader))) return 1;

	SC_DEBUG_BSCWRITEFUNC(VMP, ThisI, &CHeader);

	if (CHeader.CaseCount) {
	    // Write the switch cases...

	    SC_BSCCasePointer	WCaseP;
	    SC_CaseHPointer	RCaseP;

	    Len = CHeader.CaseCount * sizeof(SC_BSCCaseRecord);
	    MemP = malloc(Len);
	    if (! MemP) SC_SysError("BSC Write: Malloc CaseBlock Failed.");

		WCaseP = (SC_BSCCasePointer)MemP;
		RCaseP = (SC_CaseHPointer)CBP->CaseDictP->FirstPairP;
		while (RCaseP) {
		    SC_SAFE_CHECK(RCaseP->Type == SC_ENTRY_CASE, SC_BADRUN_Type);
		    WCaseP->Key = RCaseP->Key;
		    WCaseP->JmpTo = RCaseP->Value;
		    WCaseP->Id = RCaseP->Id;
		    WCaseP->KeyType = RCaseP->KeyType;

		    WCaseP += 1;
		    RCaseP = (SC_CaseHPointer)RCaseP->NextPairP;
		}
		SC_SAFE_CHECK((Uns64)WCaseP == (Uns64)MemP + Len, SC_BADRUN_Addr);
		Res = write(FD, MemP, Len);

	    free(MemP);
	    if (Res == -1) return 1;
	}

	Len = CHeader.CCount * sizeof(SC_VDescRecord);
	// Write the VDescRecords for Closed vars, straight from CBP!
	if (Len && (-1 == write(FD, (Uns8 *)CBP->CodeArr + CBP->CodeLen, Len))) return 1;

	// Write the Code bytes (CodeLen)
	if (-1 == write(FD, CBP->CodeArr, CBP->CodeLen)) return 1;

	// Now process the SubFuncs... will recurse, depth-first traversal!
	if (CHeader.SubCount) {
	    SC_CodeBlockPointer*	SubCBPP;
	    Uns32			I;

	    SubCBPP = (SC_CodeBlockPointer *)((Uns64)&CBP->CodeArr + SC_LLALIGN(CBP->CodeLen + Len));
	    for (I = 0; I < CHeader.SubCount; I++, SubCBPP++) {
		SC_SAFE_CHECK(SubCBPP && *SubCBPP && ((*SubCBPP)->Type == SC_CHUNK_CODE), SC_BADRUN_Type);
		if (SC_RWBSCAuxWriteFunc(VMP, ThisI, FD, *SubCBPP)) return 1;
	    }
	}

	return 0;
    }

// SC_RunWriteBSC will write the binary output file for the Script.
//
// Assumes FD is open for write, caller will close.
// Returns 1 if Error, 0 if success.

Int16	SC_RunWriteBSC(SC_VMPointer VMP, Int32 FD, SC_ScriptPointer ScriptP)
{
    SC_BSCFileHeader	FHeader;
    Uns32		Len;
    
    // Assumes FD is open for write

    FHeader.VMajor = SC_VERSIONMAJOR;
    FHeader.VMinor = SC_VERSIONMINOR;
    FHeader.Flags = 0;
    FHeader.GlobCount = ScriptP->GlobCount;
    FHeader.FuncCount = ScriptP->FuncCount;
    FHeader.KonstCount = ScriptP->KonstCount;
    FHeader.StringCount = ScriptP->StringCount;

    Len = sizeof(SC_BSCFileHeader) + (FHeader.KonstCount * sizeof(Int64));
    if (FHeader.StringCount) {
	SC_StrHPointer *	StrHPP = (SC_StrHPointer *)ScriptP->StringADP->Data;
	Uns32			StrCount = FHeader.StringCount;

	// Compute the length!
	while (StrCount--)
	    Len += SC_LALIGN(sizeof(Uns32) + (*StrHPP++)->StrDataP->StrLen + 1);	// + '/0'
    }
    FHeader.CodeStart = Len;

    // Write File Header
    if ((-1 == lseek(FD, 0, SEEK_SET)) ||
	(-1 == write(FD, &FHeader, sizeof(FHeader))))
	return 1;

    SC_DEBUG_BSCWRITEOPEN(VMP, &FHeader, ScriptP->NameSHP->StrDataP->Data);
	
    // Write Konsts... if any
    if (FHeader.KonstCount) {
	if (-1 == write(FD, ScriptP->KonstADP->Data, FHeader.KonstCount * sizeof(Int64)))
	    return 1;
    }

    // Write Strings... if any.  Seemingly sub-optimal as it writes Len and data separately.
    // If necessary, can malloc a buffer, write 4K at a time... but write buffers too!!
    if (FHeader.StringCount) {
	SC_StrHPointer *	StrHPP = (SC_StrHPointer *)ScriptP->StringADP->Data;
	Uns32			StrCount = FHeader.StringCount;

	while (StrCount--) {
	    SC_SAFE_CHECK((*StrHPP)->Type == SC_ENTRY_STR, SC_BADRUN_Addr);
	    Len = (*StrHPP)->StrDataP->StrLen;
	    if ((-1 == write(FD, &Len, sizeof(Uns32))) ||
		(-1 == write(FD, (*StrHPP)->StrDataP->Data, SC_LALIGN(Len + 1)))) return 1;
	    StrHPP++;
	    // NOTE:  StrDataP->Data is LL aligned, so extra bytes available to write!
	}
    }

    // Now the code blocks... starting from the one in ScriptP->TopClosP->CBP,
    // SC_RWBSCAuxWriteFunc will recurse to write the entire SubFunc tree!
    SC_RWBSC_SegIndex = 0;
    if (SC_RWBSCAuxWriteFunc(VMP, 0, FD, ScriptP->TopClosP->CBP)) return 1;
    SC_SAFE_CHECK(SC_RWBSC_SegIndex == FHeader.FuncCount, SC_BADRUN_Index);

    fsync(FD);

    SC_DEBUG_BSCWRITECLOSE(VMP);
    
    return 0;
}

Int32	SC_RunOpenWriteBSC(SC_ScriptPointer ScriptP)
{
    char *	FullPath = ED_UtilGetFullPathBuf();
    char *	NameP = ScriptP->NameSHP->StrDataP->Data;
    Int32	FD;
    Int16	NameLen;	// Len until ".sc"
    Uns8	IsBin;

    NameLen = SC_ScriptGetFileType(NameP, &IsBin);
    if (NameLen == 0) NameLen = strlen(NameP);
    sprintf(FullPath, "%s/%.*s.bsc", ScriptP->PathSHP->StrDataP->Data, NameLen, NameP);

    // Open file to write...
    FD = open(FullPath, O_WRONLY | O_CREAT | O_EXCL, S_IRWXU | S_IRWXG);
    if ((FD == -1) && (errno == EEXIST))
	FD = open(FullPath, O_TRUNC | O_WRONLY, 0);

    return FD;
}

// ********************************************************************************
// Data

void	SC_RunInitVar(SC_VMPointer VMP, SC_VarVPointer * VarPP, SC_VarVPointer SrcP)
{
    *VarPP = SC_VarEntryNew(VMP);
    if (SrcP) {
	(*VarPP)->ValueType = SrcP->ValueType;
	(*VarPP)->ValueP = SrcP->ValueP;
	SC_VARREFCOND(SrcP->ValueType == SC_VAL_PTR, (SC_VarVPointer)SrcP->ValueP);
    } 
}

// Creates a CLOS for the Nth Func in the CURRENTLY RUNNING CBP, or
// the TOP Func if a FuncCBP is given!
//
// NOTE: FuncCBP must be NULL unless doing the Top CBP... and VarCount must be 0 for it!

SC_ClosPointer		SC_RunMakeClos(SC_VMPointer VMP, Uns32 FuncId, SC_CodeBlockPointer FuncCBP)
{
    SC_ClosPointer		ClosP;		// New CLOS Chunk in memory
    SC_CodeBlockPointer *	FCBPArr;
    SC_CodeBlockPointer		RunCBP;		// Currently running container Function
    Uns16			VarCount;	// How many vars in this (new) CLOS
    Uns32			BlockLen;	// Length of CLOS chunk
    SC_VarVPointer *		VarPP;		// VarArr Ptr in CLOS chunk
    SC_VDescPointer		DescP;		// ClosVar descriptor stashed in FuncCBP
    Uns8			VarIndex;	// REG first, then LOCAL (including Param), then CLOS

    if (! FuncCBP) {
	RunCBP = (SC_CodeBlockPointer)VMP->RunScriptP->CurRunP->VarArr[0];
	FCBPArr = (SC_CodeBlockPointer *)((Uns8 *)RunCBP->CodeArr + SC_LLALIGN(RunCBP->CodeLen + (sizeof(SC_VDescRecord) * RunCBP->CCount)));
	FuncCBP = FCBPArr[FuncId];
    }

    // TOP will *NOT* have any CVars, will ignore RunCBP!
    VarCount = FuncCBP->CCount;
    DescP = (SC_VDescPointer)((Uns8 *)FuncCBP->CodeArr + FuncCBP->CodeLen);
    
    ClosP = (SC_ClosPointer)SC_ChunkNew(VMP, sizeof(SC_ClosRecord) + (sizeof(void *) * VarCount), &BlockLen);
    ClosP->Type = SC_CHUNK_CLOS;
    ClosP->State = SC_NOSTATE;
    ClosP->Flags = 0;
    ClosP->RefCount = 1;		// Will get stashed somewhere!
    ClosP->FuncId = FuncId;
    ClosP->BlockLen = BlockLen;
    ClosP->CBP = FuncCBP;		// Refers to CBP, so RefCount it!
    SC_VARREF(FuncCBP);			// + CBP referenced by CLOS

    if (VarCount) {
	// VarCount is OFTEN *ZERO*... Definitely for TOP!
	VarPP = ClosP->VarArr;
	while (VarCount--) {
	    if (DescP->Class == SC_NOVEX) {
		VarIndex = DescP->Index;

	    // Cache VarIndex in place (on FuncCBP), so no need to add again!
	    } else if (DescP->Class == SC_LVEX) {
		VarIndex = 1 + RunCBP->RCount + DescP->Index;
		DescP->Class = SC_NOVEX;
		DescP->Index = VarIndex;
	    } else if (DescP->Class == SC_CVEX) {
		VarIndex = 1 + RunCBP->RCount + RunCBP->LCount + DescP->Index;
		DescP->Class = SC_NOVEX;
		DescP->Index = VarIndex;
	    }

	    // CurRunP is the currently RUNNING function... get VarVEntry from it!!
	    *VarPP = VMP->RunScriptP->CurRunP->VarArr[VarIndex];
	    SC_VARREF(*VarPP);				// + Var referenced by CLOS
	    (*VarPP)->Flags |= SC_VAR_ESCFLAG;		// Escaped from superior function

	    VarPP += 1;
	    DescP += 1;
	}
    }

    return ClosP;
}

// Always make a GlobP for a script before running, even if VarCount is 0.
// But no point in making a GlobP for a Pkg that has no globals!
SC_GlobPointer	SC_RunAllocGlob(SC_VMPointer VMP, Uns32 VarCount, Uns8 Init)
{
    SC_GlobPointer	GlobP;
    Uns32		BlockLen;
    SC_VarVPointer *	VarPP;

    GlobP = (SC_GlobPointer)SC_ChunkNew(VMP, sizeof(SC_GlobRecord) + (sizeof(void *) * VarCount), &BlockLen);
    GlobP->Type = SC_CHUNK_GLOB;
    GlobP->Flags = 0;
    GlobP->Count = VarCount;
    GlobP->BlockLen = BlockLen;
    GlobP->Res = 0LL;

    if (Init) {
	// Globals all get 0 initially... Vars come in with RefCount of 1
	VarPP = GlobP->GArr;
	while (VarCount--) {
	    *VarPP = SC_VarEntryNew(VMP);
	    SC_DEBUG_VARDISP(VMP, *VarPP, "Alloc Global");
	    (*VarPP++)->Flags |= SC_VAR_ESCFLAG;		// By definition!
	}
    }

    return GlobP;
}

void	SC_RunZapGlob(SC_VMPointer VMP, SC_GlobPointer GlobP)
{
    SC_VarVPointer *	VPP = (SC_VarVPointer *)GlobP->GArr;
    Int16		I = GlobP->Count;

    // DeRef all the vars... first
    while (I--) {
	SC_SAFE_CHECK(*VPP && ((*VPP)->Type == SC_ENTRY_VAR), "DeRef: Glob entry bad.");
	SC_VARDEREF(VMP, *VPP++);
    }

    // Now Zap the Glob itself off the Chunk slab
    SC_SlabZapItem(VMP->ChunkSP, GlobP, GlobP->BlockLen);
}

// Globals are counted at compile time... but are created just before running.
//
// In a normal run, vars are all new, set to 0 with RefCount of 1.  Such a run
// will also NULL out VMP->OldGlobP before starting to compile.
//
// In a TRY (Interactive) run, the Glob is copied from VMP->OldGlobP:
// -- New entries are added to the end  *AND*
// -- Changed entries are zeroed out!

void	SC_RunMakeScriptGlob(SC_VMPointer VMP, SC_CGPointer CGP, Uns8 IsSub)
{
    SC_GlobPointer	GlobP = NULL;
    Uns32		TotalCount, I;
    SC_VarVPointer  	VarP;

    TotalCount = VMP->RunScriptP->GlobCount;
    if (IsSub) {
	GlobP = SC_RunAllocGlob(VMP, TotalCount, 1);

    } else {
	if (CGP && VMP->OldGlobP) {
	    if (TotalCount < VMP->OldGlobP->Count)
		TotalCount = VMP->OldGlobP->Count;
	    // Obviously doing a TRY (interactive).
	    GlobP = SC_RunAllocGlob(VMP, TotalCount, 0);			// Do NOT init!
	    // Copy Vars from Old Glob!
	    // No REF/DEREF as vars are transferred over and old GlobP just zapped!
	    for (I = 0; I < VMP->OldGlobP->Count; I++)
		GlobP->GArr[I] = VMP->OldGlobP->GArr[I];

	    // Put in new Vars, these will be created and init to 0
	    while (I < GlobP->Count) {
		GlobP->GArr[I++] = VarP = SC_VarEntryNew(VMP);		// INCREMENT	
		SC_DEBUG_VARDISP(VMP, VarP, "Alloc Global");
		VarP->Flags |= SC_VAR_ESCFLAG;				// Global by definition!
	    }

	    // Zero out globals that are inherited, but re-defined!
	    if (CGP->InGZCount) {
		SC_SAFE_CHECK(CGP->InGZADP, "INTER: Missing ZeroADP");
		for (I = 0; I < CGP->InGZCount; I++) {
		    VarP = CGP->InGZADP->Data[I];
		    // DeRef old value, if it had one!!
		    SC_VARDEREFCOND(VMP, VarP->ValueType == SC_VAL_PTR, VarP->ValueP);
		    // Now zero it out.
		    VarP->ValueType = SC_VAL_INT;
		    VarP->ValueP = (void *)0LL;
		}
	    }

	    // Zap OldGlobP, vars were transferred! Then stash new Glob for next run
	    SC_SlabZapItem(VMP->ChunkSP, VMP->OldGlobP, VMP->OldGlobP->BlockLen);

	} else // Make a GlobP even if TotalCount == 0!!
	    GlobP = SC_RunAllocGlob(VMP, TotalCount, 1);

	VMP->OldGlobP = GlobP;
    }
	
    // Done... set it into RunScript
    VMP->RunScriptP->GlobP = GlobP;
}

void		SC_RunDisplayField(SC_VMPointer VMP, Uns8 ValueType, void * ValueP)
{
    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;
    SC_VarVPointer	VP;
    SC_SlabPointer	SP;

    switch (ValueType) {
	case SC_VAL_INT:
	    OutPFP("[Int: %d]\n", (Int64)ValueP);
	    break;

	case SC_VAL_FLT:
	    OutPFP("[Flt: %f]\n", *(double *)&ValueP);
	    break;

	case SC_VAL_PTR:
	    SP = NULL;
	    VP = (SC_VarVPointer)ValueP;
	    switch (VP->Type) {
		case SC_ENTRY_VAR:	// Fall through
		case SC_ENTRY_ARR:	// Ditto
		case SC_ENTRY_DICT:	// Ditto
		case SC_ENTRY_EXT:	// Ditto
		case SC_ENTRY_ARREXT:	SP = VMP->VarEntrySP; break;

		case SC_ENTRY_PAIR:	// Fall through
		case SC_ENTRY_STR:	// Ditto
		case SC_ENTRY_CASE:	// Ditto
		case SC_ENTRY_RC:	// Ditto
		case SC_ENTRY_PAT:	// Ditto
		case SC_ENTRY_PKG:	SP = VMP->BigEntrySP; break;

		case SC_CHUNK_EXT:	// Fall through
		case SC_CHUNK_CLOS:	// Ditto
		case SC_CHUNK_CODE:	// Ditto
		case SC_CHUNK_RUN:	// Ditto
		case SC_CHUNK_SCRPT:	// Ditto
		case SC_CHUNK_GLOB:	// Ditto
		case SC_CHUNK_STAK:	SP = VMP->ChunkSP; break;
	    }

	    if (SP)
		SC_SlabPrintTypePtr(OutPFP, SP, ValueP, "Ptr:", '\n', 0);
	    else
		OutPFP("Ext: 0x%016lx\n", (Int64)ValueP);
	    break;

	case SC_VAL_PRV:
	    // Not a VarVPointer... so cannot read VP->Type!
	    // Usually ArrData or StrData...
	    SC_SlabPrintPtr(VMP, ValueP, "Ptr:", '\n');
	    break;

	default:
	    OutPFP("Ext: 0x%016lx\n", (Int64)ValueP);
	    break;
    }
}

// Only differentiates on Level being SC_SHOW_LOW or not (assumed SC_SHOW_HIGH)

    // Sub has "-->" as it indicates a ptr to a SUB-element.
    // "    --> Str (Ref:3) [Entry 013408 on Mem:2 (BEnt) Slab:1]"
    void	SC_RDVAuxSub(SC_PrintFuncP PFP, void *VP, SC_SlabPointer SP, char* TypeSP,
			     Uns32 BLen, Uns32 RefCount, Uns8 Lead)
    {
	Uns32		Offset;
	Uns16		SPOrd;

	SPOrd = SC_SPPFindInSlab(SP, VP, &Offset);
	if (RefCount == (Uns32)-1)
	    PFP("%*s--> %s [Entry %06d (%d Bytes) on Mem:%d (%.*s) Slab:%d]\n",
		 Lead, "", TypeSP, Offset, BLen, SP->Flags & SC_SLAB_TYPE_MASK, 4, (char *)&SP->Tag, SPOrd);
	else
	    PFP("%*s--> %s (Ref:%d) [Entry %06d (%d Bytes) on Mem:%d (%.*s) Slab:%d]\n",
		 Lead, "", TypeSP, RefCount, Offset, BLen, SP->Flags & SC_SLAB_TYPE_MASK, 4, (char *)&SP->Tag, SPOrd);
    }

    void	SC_RDVAuxRunSub(SC_PrintFuncP PFP, SC_RunPointer RP, SC_SlabPointer SP, Uns8 Lead)
    {
	Uns32		Offset;
	Uns16		SPOrd;

	SPOrd = SC_SPPFindInSlab(SP, RP, &Offset);
	PFP("%*s--> %s PC:%d [Entry %06d (%d Bytes) on Mem:%d (%.*s) Slab:%d]\n",
		 Lead, "", "Run", RP->PC, Offset, RP->BlockLen, SP->Flags & SC_SLAB_TYPE_MASK, 4, (char *)&SP->Tag, SPOrd);
    }

    void	SC_RDVAuxCodeSub(SC_PrintFuncP PFP, SC_CodeBlockPointer CBP, SC_SlabPointer SP, Uns8 Lead)
    {
	Uns32		Offset;
	Uns16		SPOrd;

	SPOrd = SC_SPPFindInSlab(SP, CBP, &Offset);
	PFP("%*s--> Code:%d (Ref:%d) [Entry %06d (%d Bytes) on Mem:%d (%.*s) Slab:%d]\n",
	     Lead, "", CBP->CodeId, CBP->RefCount, Offset, CBP->BlockLen, SP->Flags & SC_SLAB_TYPE_MASK, 4, (char *)&SP->Tag, SPOrd);
    }


    // Sub has "-->" and also a Count!
    //        --> 7 Entries [Entry 000000 (72 Bytes) on Mem:4 (ADat) Slab:1]"
    void	SC_RDVAuxCountSub(SC_PrintFuncP PFP, void * VP, SC_SlabPointer SP, char* TypeSP,
				  Uns32 Count, Uns32 BLen, Uns32 RefCount, Uns8 Lead)
    {
	Uns32		Offset;
	Uns16		SPOrd;

	SPOrd = SC_SPPFindInSlab(SP, VP, &Offset);
	if (RefCount == (Uns32)-1)
	    PFP("%*s--> %d %s [Entry %06d (%d Bytes) on Mem:%d (%.*s) Slab:%d]\n",
		 Lead, "", Count, TypeSP, Offset, BLen, SP->Flags & SC_SLAB_TYPE_MASK, 4, (char *)&SP->Tag, SPOrd);
	else
	    PFP("%*s--> %d %s (Ref:%d) [Entry %06d (%d Bytes) on Mem:%d (%.*s) Slab:%d]\n",
		 Lead, "", Count, TypeSP, RefCount, Offset, BLen, SP->Flags & SC_SLAB_TYPE_MASK, 4, (char *)&SP->Tag, SPOrd);
    }

    // Sub has "-->" and also an Index!
    //        --> 007 Var [Entry 000000 (72 Bytes) on Mem:4 (ADat) Slab:1]"
    void	SC_RDVAuxIndexSub(SC_PrintFuncP PFP, void * VP, SC_SlabPointer SP, char* TypeSP,
				  Uns32 Index, Uns32 BLen, Uns32 RefCount, Uns8 Lead)
    {
	Uns32		Offset;
	Uns16		SPOrd;

	SPOrd = SC_SPPFindInSlab(SP, VP, &Offset);
	PFP("%*s--> %03d %s (Ref:%d) [Entry %06d (%d Bytes) on Mem:%d (%.*s) Slab:%d]\n",
	     Lead, "", Index, TypeSP, RefCount, Offset, BLen, SP->Flags & SC_SLAB_TYPE_MASK, 4, (char *)&SP->Tag, SPOrd);
    }

    // Sub has "-->" + Name and Index!
    //        --> Pkg 7 "Pat" [Entry 000000 (72 Bytes) on Mem:4 (ADat) Slab:1]"
    void	SC_RDVAuxNameISub(SC_PrintFuncP PFP, void * VP, SC_SlabPointer SP, char* TypeSP, char* NameSP,
				      Uns32 Index, Uns32 BLen, Uns32 RefCount, Uns8 Lead)
    {
	Uns32		Offset;
	Uns16		SPOrd;

	SPOrd = SC_SPPFindInSlab(SP, VP, &Offset);
	if (NameSP)
	    PFP("%*s--> %s:%0d \"%s\" (Ref:%d) [Entry %06d (%d Bytes) on Mem:%d (%.*s) Slab:%d]\n",
		 Lead, "", TypeSP, Index, NameSP, RefCount, Offset, BLen, SP->Flags & SC_SLAB_TYPE_MASK, 4, (char *)&SP->Tag, SPOrd);
	else
	    PFP("%*s--> %s:%0d (Ref:%d) [Entry %06d (%d Bytes) on Mem:%d (%.*s) Slab:%d]\n",
		 Lead, "", TypeSP, Index, RefCount, Offset, BLen, SP->Flags & SC_SLAB_TYPE_MASK, 4, (char *)&SP->Tag, SPOrd);
    }

    // Elt has NO ARROW (Var can be real Var or Pair!)
    // "    007 Var Name (Ref:2) [Entry 000144 (16 Bytes) on Mem:1 (VEnt) Slab:1]"
    // "    007 Pair Name (Ref:5) [Entry 000320 (32 Bytes) on Mem:2 (BEnt) Slab:1]"
    void	SC_RDVAuxVarRef(SC_VMPointer VMP, SC_VarVPointer VP, char * NameSP, Uns32 Index, Uns8 Lead)
    {
	SC_PrintFuncP	PFP = VMP->OutPrintFP;
	SC_SlabPointer	SP;
	char *		TypeSP;
	Uns32		Offset;
	Uns32		BLen;
	Uns16		SPOrd;

	if (VP == NULL) {
	    if (Index != (Uns32)-1)
		PFP("%*s%03d ---\n", Lead, "", Index);
	    else
		PFP("%*s---\n", Lead, "");
	    return;
	}

	if (VP->Type == SC_ENTRY_VAR) {
	    SP = VMP->VarEntrySP;
	    BLen = sizeof(SC_VarVRecord);
	    TypeSP = (*NameSP) ? "Var " : "Var";
	} else {
	    SP = VMP->BigEntrySP;
	    BLen = sizeof(SC_PairHRecord);
	    TypeSP = (*NameSP) ? "Pair " : "Pair";
	}

	SPOrd = SC_SPPFindInSlab(SP, VP, &Offset);
	if (Index != (Uns32)-1)
	    PFP("%*s%03d %s%s (Ref:%d) [Entry %06d (%d Bytes) on Mem:%d (%.*s) Slab:%d]\n",
		     Lead, "", Index, TypeSP, NameSP, VP->RefCount, Offset, BLen, SP->Flags & SC_SLAB_TYPE_MASK, 4, (char *)&SP->Tag, SPOrd);
	else
	    PFP("%*s%s%s (Ref:%d) [Entry %06d (%d Bytes) on Mem:%d (%.*s) Slab:%d]\n",
		     Lead, "", TypeSP, NameSP, VP->RefCount, Offset, BLen, SP->Flags & SC_SLAB_TYPE_MASK, 4, (char *)&SP->Tag, SPOrd);
    }

// Index is (usually) 1-based.  Index==-1 means NO INDEX.
// Lead is how many blank spaces!
void		SC_RunDisplayVarP(SC_VMPointer VMP, SC_VarVPointer VarP, char * NameSP, Uns32 Index, Uns8 Level, Uns8 Lead)
{
    // Sometimes a PAIR acts as a Var!!
    SC_RDVAuxVarRef(VMP, VarP, NameSP, Index, Lead);

    if (VarP->ValueType == SC_VAL_INT) {
	VMP->OutPrintFP("%*sInt %ld\n", Lead+4, "", (Int64)VarP->ValueP);
	return;
    } else if (VarP->ValueType == SC_VAL_FLT) {
	VMP->OutPrintFP("%*sFlt %lf\n", Lead+4, "", *(double *)&VarP->ValueP);
	return;
    } else
	SC_RunDisplayValueP(VMP, (SC_VarVPointer)VarP->ValueP, Level, Lead+4);
}

    
void		SC_RunDisplayValueP(SC_VMPointer VMP, SC_VarVPointer VP, Uns8 Level, Uns8 Lead)
{
    #define		_STRLEN		56
    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;
    char		S[_STRLEN];

    switch (VP->Type) {
	case SC_ENTRY_ARR: {
	    SC_ArrDataPointer	ADP = (*(SC_ArrVPointer)VP).ArrDataP;

	    SC_RDVAuxSub(OutPFP, VP, VMP->VarEntrySP, "Array", sizeof(SC_ArrVRecord), VP->RefCount, Lead);
	    SC_RDVAuxCountSub(OutPFP, ADP, VMP->ArrDataSP, "Entries", ADP->SlotCount, ADP->BlockLen, -1, Lead+4);
	
	    // Write out the Var entries the Arr points to (or NULL).
	    // ARRAYS are zero-based... so display as such!
	    if (Level > SC_SHOW_MIN) {
		SC_VarVPointer *	EltPP = (SC_VarVPointer *)ADP->Data;
		SC_VarVPointer *	EndPP = EltPP + ADP->SlotCount;
		Uns32			I = 0;

		while (EltPP < EndPP) {
		    // Uninitialized (unused) VP in Arr will be NULL!
		    VP = *EltPP++;					// Inc (also I++ below)
		    SC_RDVAuxVarRef(VMP, VP, "", I++, Lead+8);
		    
		    if (VP && (Level >= SC_SHOW_HIGH)) {
			// Show what the VP holds!
			if (VP->ValueType == SC_VAL_INT)
			    OutPFP("%*sInt %ld\n", Lead+12, "", (Int64)VP->ValueP);
			else if (VP->ValueType == SC_VAL_FLT)
			    OutPFP("%*sFlt %lf\n", Lead+12, "", *(double *)&VP->ValueP);
			else {
			    Uns32		Offset;
			    Uns16		Ord;
			    SC_SlabPointer	SP;
			    
			    VP = (SC_VarVPointer)VP->ValueP;
			    if (((Ord = SC_SPPFindInSlab(SP = VMP->VarEntrySP, VP, &Offset))) ||
				((Ord = SC_SPPFindInSlab(SP = VMP->BigEntrySP, VP, &Offset))) ||
				((Ord = SC_SPPFindInSlab(SP = VMP->ChunkSP, VP, &Offset)))) {
				OutPFP("%*s--> %s (Ref:%d) [Entry %06d on Mem:%d (%.*s) Slab:%d]\n", Lead+12, "",
					SC_EntryNameArr[VP->Type], VP->RefCount, Offset, SP->Flags & SC_SLAB_TYPE_MASK, 4, (char *)&SP->Tag, Ord);
				if (VP->Type == SC_ENTRY_STR) {
				    SC_StrStuffStr(S, (*(SC_StrHPointer)VP).StrDataP->Data, _STRLEN);
				    OutPFP("%*s\"%s\"\n", Lead+16, "", S);
				}
			    } else
				OutPFP("%*s--> UNKNOWN\n", Lead+12, "");
			}

		    }
			
		}
	    }
	} break;

	case SC_ENTRY_STR: {
	    SC_StrDataPointer	SDP = (*(SC_StrHPointer)VP).StrDataP;

	    SC_RDVAuxSub(OutPFP, VP, VMP->BigEntrySP, "Str", sizeof(SC_StrHRecord), VP->RefCount, Lead);
	    SC_RDVAuxCountSub(OutPFP, SDP, VMP->StrDataSP, "Bytes", SDP->StrLen, SDP->BlockLen, -1, Lead+4);
	    if (Level > SC_SHOW_MIN) {
		SC_StrStuffStr(S, SDP->Data, _STRLEN);
		OutPFP("%*s\"%s\"\n", Lead+8, "", S);
	    }
	} break;

	case SC_ENTRY_DICT: {
	    SC_ArrDataPointer	ADP = (*(SC_DictVPointer)VP).HADP;

	    SC_RDVAuxSub(OutPFP, VP, VMP->BigEntrySP, "Dict", sizeof(SC_DictVRecord), VP->RefCount, Lead);
	    SC_RDVAuxCountSub(OutPFP, ADP, VMP->ArrDataSP, "Entries", ADP->SlotCount, ADP->BlockLen, -1, Lead+4);
	    if (Level > SC_SHOW_MIN) {
		SC_DictPrint(VMP, (SC_DictVPointer)VP, 0, Lead+8);
		if (Level > SC_SHOW_LOW)
		    SC_DictPairPrint(VMP, (SC_DictVPointer)VP, Lead+8);
	    }
	} break;

	case SC_ENTRY_EXT: {		// ???? Not tested
	    char*	TypeSP;
	    Uns16	EXTType;
	    
	    SC_RDVAuxSub(OutPFP, VP, VMP->VarEntrySP, "Ext", sizeof(SC_EXTVRecord), VP->RefCount, Lead);
	    EXTType = (*(SC_EXTVPointer)VP).ExtType;
	    TypeSP = (EXTType < SC_EXT_COUNT) ? SC_EXTNameArr[EXTType] : "*UNKNOWN*";
	    OutPFP("%*sType: %s Client Ptr: 0x%lx\n", Lead+6, "", TypeSP, (*(SC_EXTVPointer)VP).ExtP);
	} break;
	
	case SC_ENTRY_ARREXT: {		// ???? Not Tested
	    char*		TypeSP;
	    Uns16		EXTType;
	    SC_ArrDataPointer	ADP;


	    SC_RDVAuxSub(OutPFP, VP, VMP->VarEntrySP, "Ext", sizeof(SC_ArrXTVRecord), VP->RefCount, Lead);
	    EXTType = (*(SC_ArrXTVPointer)VP).ExtType;
	    TypeSP = (EXTType < SC_EXT_COUNT) ? SC_EXTNameArr[EXTType] : "*UNKNOWN*";
	    OutPFP("%*sType: %s Client Ptr: Array\n", Lead+6, "", TypeSP);

	    ADP = (*(SC_ArrXTVPointer)VP).ArrDataP;
	    SC_RDVAuxCountSub(OutPFP, ADP, VMP->ArrDataSP, "Slots (LL)", ADP->SlotCount, ADP->BlockLen, -1, Lead+4);
	    // Write out the data... these are just data, NOT ptrs to Entries!
	    if (Level > SC_SHOW_LOW) {
		Int64 *		CurP = (Int64 *)ADP->Data;
		Int64 *		EndP = CurP + ADP->SlotCount;
		Uns32		I = 0;

		while (CurP < EndP)
		    OutPFP("%*s%03d 0x%016lx\n", Lead+6, "", I++, *CurP++);
	    }
	} break;
	
	case SC_ENTRY_RC: {
	    SC_RunPointer	RP = (*(SC_RCPointer)VP).RunP;
	    SC_ArrDataPointer	ADP;

	    SC_RDVAuxSub(OutPFP, VP, VMP->BigEntrySP, "RunC", sizeof(SC_RCRecord), VP->RefCount, Lead);
	    if (Level > SC_SHOW_LOW) {
		// Show the RunP
		SC_RunDisplayValueP(VMP, (SC_VarVPointer)RP, Level-2, Lead+4);

		// Show the ResumeP
		if ((RP = (*(SC_RCPointer)VP).ResumeP))	{	// Assign
		    OutPFP("%*sResume RunRecord\n", Lead+4, "");
		    SC_RunDisplayValueP(VMP, (SC_VarVPointer)RP, Level-2, Lead+4);
		}

		// StackCopy stores the stack, 2 LL per entry... so (ADP->SlotCount / 2)
		if ((ADP = (*(SC_RCPointer)VP).StackCopyP)) {
		    OutPFP("%*sStack Copy\n", Lead+4, "");
		    SC_RDVAuxCountSub(OutPFP, ADP, VMP->ArrDataSP, "Elts", ADP->SlotCount / 2, ADP->BlockLen, -1, Lead+4);		
		}
	    }
	} break;

	case SC_ENTRY_PAT: {
	    SC_RDVAuxSub(OutPFP, VP, VMP->BigEntrySP, "Pat", sizeof(SC_PatRecord), VP->RefCount, Lead);
	    if (Level > SC_SHOW_LOW) {
		OutPFP("%*s-->", Lead, "");
		SC_PatPrint(VMP, (SC_PatPointer)VP, 0, Lead+4);
	    }
	} break;

        case SC_ENTRY_PKG: {
	    SC_PkgPointer	PkgP = (SC_PkgPointer)VP;
	    SC_PairHPointer	PairP;

	    // Pkg NameSHP will have been stashed under (Int64)-1!!
	    PairP = SC_DictGetPair(PkgP->NDictP, (SC_VarVPointer)(Int64)-1, SC_VAL_INT);
	    SC_RDVAuxNameISub(OutPFP, PkgP, VMP->BigEntrySP, "Pkg",
			      (PairP) ? (*(SC_StrHPointer)PairP->ValueP).StrDataP->Data : NULL,
			      PkgP->Index, sizeof(SC_PkgRecord), PkgP->RefCount, Lead);

	    SC_RDVAuxSub(OutPFP, PkgP->NDictP, VMP->BigEntrySP, "NDict", sizeof(SC_DictVRecord), PkgP->NDictP->RefCount, Lead+4);
	    if (Level > SC_SHOW_MIN) {
		SC_DictPrint(VMP, PkgP->NDictP, 0, Lead+8);
		if (Level > SC_SHOW_LOW)
		    SC_DictPairPrint(VMP, PkgP->NDictP, Lead+8);
	    }

	    // ???? NOT TESTED... No current Pkg has globals!
	    if (PkgP->GlobCount) {
		SC_RDVAuxSub(OutPFP, PkgP->GlobP, VMP->BigEntrySP, "Glob", sizeof(SC_GlobRecord), -1, Lead+4);
		SC_RunDisplayGArr(VMP, PkgP->GlobP->GArr, NULL, PkgP->GlobCount, Level-1, Lead+4);
	    }

	} break;
	
	case SC_CHUNK_EXT: {
	    char*		TypeSP;
	    Uns32		SlotCount;
	    Uns16		EXTType;

	    SC_RDVAuxSub(OutPFP, VP, VMP->ChunkSP, "Ext", (*(SC_ChunkXTVPointer)VP).BlockLen, VP->RefCount, Lead);
	    EXTType = (*(SC_ChunkXTVPointer)VP).ExtType;
	    TypeSP = (EXTType < SC_EXT_COUNT) ? SC_EXTNameArr[EXTType] : "*UNKNOWN*";
	    SlotCount = ((*(SC_ChunkXTVPointer)VP).BlockLen - sizeof(SC_ChunkXTVRecord)) / sizeof(Int64);
	    OutPFP("%*s%s Chunk ExtData:%d  Slots (LL):%d\n", Lead+4, "", TypeSP, (*(SC_ChunkXTVPointer)VP).ExtData, SlotCount);
	    if (Level > SC_SHOW_LOW) {
		Int64 *		CurP = (Int64 *)(*(SC_ChunkXTVPointer)VP).Ext;
		Int64 *		EndP = CurP + SlotCount;
		Uns32		I = 1;

		while (CurP < EndP)
		    OutPFP("%*s%03d Hex 0x%016lx  Dec %020ld\n", Lead+4, "", I++, *CurP, *CurP), CurP += 1;
	    }
	} break;
	
	case SC_CHUNK_CLOS: {
	    SC_CodeBlockPointer		CBP = (SC_CodeBlockPointer)(*(SC_ClosPointer)VP).CBP;
	    Uns16			CCount = CBP->CCount;

	    SC_RDVAuxSub(OutPFP, VP, VMP->ChunkSP, "Clos", (*(SC_ClosPointer)VP).BlockLen, VP->RefCount, Lead);
	    // Now draw out the closed vars!
	    if (CCount && (Level > SC_SHOW_MIN)) {
		SC_VarVPointer *	EltPP = (*(SC_ClosPointer)VP).VarArr;
		SC_VarVPointer *	EndPP = EltPP + CCount;
		Uns32			I = 1;

		OutPFP("%*s--> Closed Vars:%d\n", Lead+4, "", CCount);
		while (EltPP < EndPP) {
		    VP = *EltPP++;					// Inc (and I++ below)
		    SC_RDVAuxVarRef(VMP, VP, "", I++, Lead+8);
		}
	    }
	    // Extra line break iff printed CVars *AND* will print out ByteCodes... strange, but it works :-)
	    if ((Level > SC_SHOW_LOW) && (CCount))
		OutPFP("\n");
	    // Now write out CBP
	    SC_RDVAuxCodeSub(OutPFP, CBP, VMP->ChunkSP, Lead+4);
	    if (Level > SC_SHOW_LOW)
		SC_RunDisplayCB(NULL, VMP, VMP->RunScriptP, CBP, 0, Lead+8);
	} break;


	case SC_CHUNK_SCRPT: {
	    SC_ScriptPointer	ScriptP = (SC_ScriptPointer)VP;
	    SC_ArrDataPointer	ADP;
	    SC_GlobPointer	GlobP;
	    SC_ClosPointer	ClosP;

	    SC_RDVAuxSub(OutPFP, ScriptP, VMP->ChunkSP, "Script", sizeof(SC_ScriptRecord), VP->RefCount, Lead);
	    SC_StrStuffStr(S, ScriptP->NameSHP->StrDataP->Data, _STRLEN);
	    OutPFP("%*s%s [Id:%d Level:%d] ", Lead+4, "", S, ScriptP->ScriptId, ScriptP->ScriptLevel);
	    SC_StrStuffStr(S, ScriptP->PathSHP->StrDataP->Data, _STRLEN);
	    OutPFP("Path:\"%s\"\n", S);

	    // This may be called from ShowScript... Only running scripts will have a GlobP!!
	    // If compiled, sub scripts will have a GNameADP, but if loaded from ".bsc" it will *NOT*!

	    if (ScriptP->GlobCount) {
		// Show the GNameADP memory ptr first... if there is one
		GlobP = ScriptP->GlobP;
		ADP = ScriptP->GNameADP;
		if (ADP) {
		    // GNameADP grows by N as needed, so its SlotCount is greater than GlobCount.
		    SC_RDVAuxCountSub(OutPFP, ADP, VMP->ArrDataSP, "GNames", ScriptP->GlobCount, ADP->BlockLen, -1, Lead+4);
		    // Draw the GNameADP *ONLY* if NOT listing the Globs!
		    if ((Level >= SC_SHOW_LOW) && (GlobP == NULL)) {
			SC_StrHPointer *	StrHPP = (SC_StrHPointer *)ADP->Data;
			Uns32			I = 1;
			while (I <= ScriptP->GlobCount)
			    OutPFP("%*s%03d %s\n", Lead+8, "", I++, (*StrHPP++)->StrDataP->Data);
			OutPFP("\n");
		    }
		}

		if (GlobP)
		    SC_RunDisplayGlobs(VMP, ScriptP, Level-1, 0, Lead+4);
		else
		    OutPFP("%*s--> %d Globals [Not allocated yet]\n", Lead+4, "", ScriptP->GlobCount);
	    }

	    // Konst array...
	    if ((ADP = ScriptP->KonstADP)) {		// Assign

		SC_RDVAuxCountSub(OutPFP, ADP, VMP->ArrDataSP, "Konsts", ScriptP->KonstCount, ADP->BlockLen, -1, Lead+4);
		if (Level > SC_SHOW_LOW) {
		    Int64 *		EltP = (Int64 *)ADP->Data;
		    Int64 *		EndP = EltP + ScriptP->KonstCount;
		    Uns32		I = 1;

		    while (EltP < EndP) {
			OutPFP("%*s%03d Hex 0x%016lx  Dec %020ld  Flt %lf\n", Lead+8, "", I, *EltP, *EltP, *(double *)EltP);
			EltP++, I++;
		    }
		    OutPFP("\n");		    
		}
	    }

	    // String array...
	    if ((ADP = ScriptP->StringADP)) {		// Assign
		SC_RDVAuxCountSub(OutPFP, ADP, VMP->ArrDataSP, "Strings", ScriptP->StringCount, ADP->BlockLen, -1, Lead+4);
		if (Level > SC_SHOW_LOW) {
		    SC_StrHPointer *	EltPP = (SC_StrHPointer *)ADP->Data;
		    SC_StrHPointer *	EndPP = EltPP + ScriptP->StringCount;
		    Uns32			I = 1;

		    while (EltPP < EndPP) {
			SC_StrStuffStr(S, (*EltPP)->StrDataP->Data, _STRLEN);
			OutPFP("%*s%03d \"%s\"\n", Lead+8, "", I, S);
			EltPP++, I++;
		    }
		    OutPFP("\n");
		}
	    }

	    if (ScriptP->CurRunP)
		SC_RunDisplayValueP(VMP, (SC_VarVPointer)ScriptP->CurRunP, Level-1, Lead+4);

	    // Now display the TopClosP! (No CVars as this TOP CLOS for the script.)
	    ClosP = ScriptP->TopClosP;

	    SC_RDVAuxSub(OutPFP, ClosP, VMP->ChunkSP, "Top Clos", ClosP->BlockLen, ClosP->RefCount, Lead+4);
	    if (Level > SC_SHOW_LOW)
		SC_RunDisplayCB(NULL, VMP, ScriptP, ClosP->CBP, 0, Lead+8);
	    
	} break;

	case SC_CHUNK_RUN: {
	    SC_RunPointer	RP;
	    SC_CodeBlockPointer	CBP;
	    SC_VarVPointer *	VarPP;
	    Uns16		CVars;
	    Uns16		LVars;
	    Uns32		I, Index, L;

	    RP = (SC_RunPointer)VP;
	    // Count the levels!!
	    L = 1; while ((RP = RP->CallerP)) L++;
	    // Add L spaces for each level... from 4, down 1 for every 8 levels!
	    if ((L = 4 - (L/8)) < 0) L = 0;

	    RP = (SC_RunPointer)VP;
	    while (RP) {
		// Show all N levels!
	    
		CBP = (SC_CodeBlockPointer)RP->VarArr[0];
		CVars = CBP->CCount;
		LVars = CBP->LCount - (CBP->InCount + CBP->OutCount);

		SC_RDVAuxRunSub(OutPFP, RP, VMP->ChunkSP, Lead);
		if (Level > SC_SHOW_MIN) {
		    //OutPFP("%*sPC == %d\n", Lead+4, "", RP->PC);
		    VarPP = RP->VarArr;
		    // VarArr[0] is CBP;
		    SC_RDVAuxCodeSub(OutPFP, (SC_CodeBlockPointer)*VarPP, VMP->ChunkSP, Lead+4);
		    VarPP++, Index = 1;
		    // Next RCount are regs
		    for (I = 0; I < RP->RCount; I++, VarPP++, Index++)
			if (*VarPP)		// Initially NULL... may still be!
			    SC_RDVAuxIndexSub(OutPFP, *VarPP, VMP->VarEntrySP, "Reg Var", Index, sizeof(SC_VarVRecord),
					      (*(SC_CodeBlockPointer)*VarPP).RefCount, Lead+4);
		    // Next InArgs + OutArgs
		    for (I = 0; I < CBP->InCount; I++, VarPP++, Index++)
			SC_RDVAuxIndexSub(OutPFP, *VarPP, VMP->VarEntrySP, "In Var", Index, sizeof(SC_VarVRecord),
					  (*(SC_CodeBlockPointer)*VarPP).RefCount, Lead+4);
		    for (I = 0; I < CBP->OutCount; I++, VarPP++, Index++)
			SC_RDVAuxIndexSub(OutPFP, *VarPP, VMP->VarEntrySP, "Out Var", Index, sizeof(SC_VarVRecord),
					  (*(SC_CodeBlockPointer)*VarPP).RefCount, Lead+4);
		    // Next LArgs
		    for (I = 0; I < LVars; I++, VarPP++, Index++)
			SC_RDVAuxIndexSub(OutPFP, *VarPP, VMP->VarEntrySP, "Loc Var", Index, sizeof(SC_VarVRecord),
					  (*(SC_CodeBlockPointer)*VarPP).RefCount, Lead+4);
		    // Next CVars
		    for (I = 0; I < CVars; I++, VarPP++, Index++)
			SC_RDVAuxIndexSub(OutPFP, *VarPP, VMP->VarEntrySP, "Clos Var", Index, sizeof(SC_VarVRecord),
					  (*(SC_CodeBlockPointer)*VarPP).RefCount, Lead+4);

		    if (RP->Flags & SC_RUN_ALLARGSFLAG) {
			SC_RDVAuxSub(OutPFP, *VarPP, VMP->VarEntrySP, "In Arr", sizeof(SC_VarVRecord),
					  (*(SC_CodeBlockPointer)*VarPP).RefCount, Lead+4);
			SC_RDVAuxSub(OutPFP, *VarPP, VMP->VarEntrySP, "Out Arr", sizeof(SC_VarVRecord),
					  (*(SC_CodeBlockPointer)*VarPP).RefCount, Lead+4);
		    }
		    OutPFP("\n");
		} else
		    OutPFP("%*sFields CodeId:%d R:%d I:%d O:%d L:%d C:%d\n", Lead+4, "",
			    (*(SC_CodeBlockPointer)RP->VarArr[0]).CodeId,
			    RP->RCount, CBP->InCount, CBP->OutCount, LVars, CVars);

		Lead += L;	// Loop to show next level!
		RP = RP->CallerP;
		// Show only 1 level if below SHOW_HIGH.
		if (Level < SC_SHOW_HIGH) break;
	    }
	} break;

	default: return;

	#undef	_STRLEN
    }
}

void		SC_RunDisplayGArr(SC_VMPointer VMP, SC_VarVPointer * VPP, SC_StrHPointer * SHPP, Uns32 Count, Uns8 Level, Uns8 Lead)
{
    Uns32	Index = 1;

    while (Index <= Count) {
	SC_RunDisplayVarP(VMP, *VPP++, (SHPP) ? (*SHPP++)->StrDataP->Data : "", Index++, Level - 1, Lead+4);
	if (Level > SC_SHOW_LOW) VMP->OutPrintFP("\n");
    }
}


void		SC_RunDisplayGlobs(SC_VMPointer VMP, SC_ScriptPointer ScriptP, Uns8 Level, Uns8 Alone, Uns8 Lead)
{
    Uns32		VarIndex;
    SC_PkgPointer	PkgP;
    SC_VarVPointer *	VarPP;
    Uns16		I;
    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;
    SC_GlobPointer	GlobP;

    if (Alone) {
	OutPFP("\n**************************************\n");
	OutPFP("*GLOBALS******************************\n");
	OutPFP("**************************************\n");
    }

    // First write out where GlobP itself is!
    GlobP = ScriptP->GlobP;
    SC_RDVAuxCountSub(OutPFP, GlobP, VMP->ChunkSP, "Globals", GlobP->Count, GlobP->BlockLen, -1, Lead);

    // Do RunScript globals first
    if (Level > SC_SHOW_MIN) {

	SC_RunDisplayGArr(VMP, GlobP->GArr, (ScriptP->GNameADP) ? (SC_StrHPointer *)ScriptP->GNameADP->Data : NULL,
			  ScriptP->GlobCount, Level, Lead+4);
    }

    if (Alone) {
	// Pkg globals, if any...
	for (I = 0; I < VMP->PkgCount; I++) {
	    PkgP = (SC_PkgPointer)VMP->PkgADP->Data[I];
	    VarPP = PkgP->GlobP->GArr;
	    VarIndex = 1;
	    if (PkgP->GlobCount) {
		OutPFP("****PKG %d*****************************\n", I);
		while (VarIndex <= PkgP->GlobCount) {
		    SC_RunDisplayVarP(VMP, *VarPP, "", VarIndex, Level - 1, Lead+8);
		    if (Level > SC_SHOW_LOW) OutPFP("\n");
		    VarPP++, VarIndex++;
		}
	    }
	}
	OutPFP("\n");
    }
}

void		SC_RunDisplayRuns(SC_VMPointer VMP, SC_RunPointer RunP, Uns8 Level, Uns8 Alone)
{
    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;

    if (Alone) {
	OutPFP("\n**************************************\n");
	OutPFP("*RUNS*********************************\n");
	OutPFP("**************************************\n");
    }

    SC_RunDisplayValueP(VMP, (SC_VarVPointer)RunP, Level, 0);

    if (Alone)
	OutPFP("\n");
}

void		SC_RunDisplayLocals(SC_VMPointer VMP, SC_RunPointer RunP, Uns8 Level, Uns8 Alone)
{
    Uns16		VarIndex;
    Uns16		Count;
    SC_VarVPointer *	VarPP;
    SC_CodeBlockPointer	FuncCBP;
    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;

    VarPP = RunP->VarArr;
    FuncCBP = (SC_CodeBlockPointer)*VarPP++;

    if (Alone) {
	OutPFP("\n**************************************\n");
	OutPFP("*LOCALS*******************************\n");
	OutPFP("*Code: %04d***************************\n", FuncCBP->CodeId);
	OutPFP("**************************************\n");
    } else {
	Count = FuncCBP->RCount + FuncCBP->LCount + FuncCBP->CCount;
        SC_RDVAuxCountSub(OutPFP, RunP, VMP->ChunkSP, "Vars in Run Record", Count, RunP->BlockLen, -1, 0);
    }
    
    VarIndex = 1;
    while (VarIndex <= FuncCBP->RCount) {
	// REG are initially NULL in VarArr... will get assigned as used!
	if (*VarPP) SC_RunDisplayVarP(VMP, *VarPP, "R", VarIndex, Level - 1, 8);
	VarPP++, VarIndex++;
    }

    VarIndex = 1;
    while (VarIndex <= FuncCBP->InCount)
	SC_RunDisplayVarP(VMP, *VarPP++, "I", VarIndex++, Level - 1, 8);

    VarIndex = 1;
    while (VarIndex <= FuncCBP->OutCount)
	SC_RunDisplayVarP(VMP, *VarPP++, "O", VarIndex++, Level - 1, 8);

    VarIndex = 1;
    Count = FuncCBP->LCount - (FuncCBP->InCount + FuncCBP->OutCount);
    while (VarIndex <= Count)
	SC_RunDisplayVarP(VMP, *VarPP++, "L", VarIndex++, Level - 1, 8);

    VarIndex = 1;
    while (VarIndex <= FuncCBP->CCount)
	SC_RunDisplayVarP(VMP, *VarPP++, "C", VarIndex++, Level - 1, 8);

    OutPFP("\n");    	
}


void	SC_RunDisplayCB(SC_CGPointer CGP, SC_VMPointer VMP, SC_ScriptPointer ScriptP, SC_CodeBlockPointer CBP, Uns8 Alone, Uns8 Lead)
{
    Uns32		Loc = 0L;
    Uns8 *		CurP;
    Uns8 *		EndP;
    Uns8 *		VarP;
    Int8 *		KP;
    Int16 *		K2P;
    Uns16 *		I2P;
    Int32		K4;
    Uns32		I4;
    Uns32		Len;
    char *		OpS;
    char *		ArgS;
    char *		NameP;
    SC_OpDispPointer	DispP;
    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;
    SC_ArrDataPointer	SADP, KADP;

    #define _STRLEN	28
    char		S[_STRLEN];

    static void *	DrawArr[] = {&&PrintNo, &&Print1V, &&Print2V, &&Print3V,
				     &&PrintVVK, &&PrintVK2, &&PrintVI2, &&PrintII2,
				     &&PrintK, &&PrintK3, &&PrintI3};

    CurP = (Uns8 *)CBP->CodeArr;
    EndP = CurP + CBP->CodeLen;

    OutPFP("%*s**********************************\n%*s**********************************\n", Lead, "", Lead, "");
    OutPFP("%*sCode:%04d           Len:%4d Bytes\n", Lead, "", CBP->CodeId, CBP->CodeLen);
    if (CBP->Flags & SC_CB_TOPFLAG)
	OutPFP("%*sReg:%01d    P:%02d+%02d    L:%03d  G:%05d\n", Lead, "",
		     CBP->RCount, CBP->InCount, CBP->OutCount, CBP->LCount, ScriptP->GlobCount);
    else
	OutPFP("%*sReg:%01d    P:%02d+%02d    L:%03d    C:%03d\n", Lead, "",
		     CBP->RCount, CBP->InCount, CBP->OutCount, CBP->LCount, CBP->CCount);

    OutPFP("%*s**********************************\n", Lead, "");

    // CAN be called during compile, before StringADP/KonstADP are done
    // and stashed in ScriptP!!

    SADP = ScriptP->StringADP;
    KADP = ScriptP->KonstADP;
    if (CGP) {
	if (! SADP) SADP = CGP->TopLexP->MapP->StringADP;
	if (! KADP) KADP = CGP->TopLexP->MapP->KonstADP;
    }

    while (CurP < EndP) {
	if (*CurP) {
	    DispP = SC_OpDispArr + *CurP;
	    OpS = SC_OpStrArr[DispP->OpI];
	    ArgS = SC_ArgStrArr[DispP->ArgI];
	    Len = DispP->Len;
	    goto *DrawArr[DispP->DispI];
	} else
	    Len = 1;

    BackInLoop:
	CurP += Len, Loc += Len;
    }

    // Print Closure Receipe at the end... 
    if (Alone && (CBP->CCount)) {
	Uns8 *		BP;
	Uns16		I;

	OutPFP("%*s**********************************\n", Lead, "");
	BP = (Uns8 *)CBP->CodeArr + CBP->CodeLen;
	for (I = 0; I < CBP->CCount; I++, BP += 2)
	    OutPFP("%*s*     CVar:%2d <-- Sup %s:%2d    *\n", Lead, "", I, (*BP == SC_CVEX) ? "CVar" : "LVar", *(BP+1));
    }

    // Finish off
    OutPFP("%*s**********************************\n%*s**********************************\n", Lead, "", Lead, "");
    if (Alone) OutPFP("\n");
    return;

    Print3V:
	VarP = CurP + 1;
	OutPFP("%*s* %06d - %s%s -- %03d %03d %03d\n", Lead, "", Loc, OpS, ArgS, *VarP, *(VarP+1), *(VarP+2));
	goto BackInLoop;

    Print2V:
	VarP = CurP + 1;
	OutPFP("%*s* %06d - %s%s -- %03d %03d", Lead, "", Loc, OpS, ArgS, *VarP, *(VarP+1));
	if (*(CurP+3) == SC_NOOP_CODE) {
	    OutPFP(" XXX\n");
	    Len = 4;
	} else
	    OutPFP("\n");
	goto BackInLoop;

    Print1V:
	VarP = CurP + 1;
	OutPFP("%*s* %06d - %s%s -- %03d", Lead, "", Loc, OpS, ArgS, *VarP);
	if (*(CurP+2) == SC_NOOP_CODE) {
	    OutPFP(" XXX XXX\n");
	    Len = 4;
	} else
	    OutPFP("\n");
	goto BackInLoop;

    PrintNo:
	OutPFP("%*s* %06d - %s%s --", Lead, "", Loc, OpS, ArgS);
	switch ((Int64)CurP & 0x03) {
	    case 0:
		// First byte... see if the rest is NOOP
		if (*(CurP+1) == SC_NOOP_CODE) {
		    OutPFP(" XXX");
		    if (*(CurP+2) == SC_NOOP_CODE) {
			OutPFP(" XXX XXX");
			Len = 4;
		    } else
			Len = 2;
		}
		break;

	    case 1:
		// Second byte... see if the next 2 are NOOP
		if (*(CurP+1) == SC_NOOP_CODE) {
		    OutPFP(" XXX XXX");
		    Len = 3;
		}
		break;

	    case 2:
		// Third byte... see if next is NOOP
		if (*(CurP+1) == SC_NOOP_CODE) {
		    OutPFP(" XXX");
		    Len = 2;
		}
		break;

	    case 3:
		// Last byte... start a new Long after
		break;
	}
	OutPFP("\n");
	goto BackInLoop;

    PrintVVK:
	VarP = CurP + 1;
	KP = (Int8 *)(CurP + 3);
	OutPFP("%*s* %06d - %s%s -- %03d %03d %03d\n", Lead, "", Loc, OpS, ArgS, *VarP, *(VarP+1), *KP);
	goto BackInLoop;

    PrintVK2:
	VarP = CurP + 1;
	K2P = (Int16 *)(CurP + 2);
	OutPFP("%*s* %06d - %s%s -- %03d %7d\n", Lead, "", Loc, OpS, ArgS, *VarP, *K2P);	
	goto BackInLoop;

    PrintVI2:
	VarP = CurP + 1;
	I2P = (Uns16 *)(CurP + 2);
	// Used during a compile AND to just show a Clos var...MAY have NULL GNameADP!
	if ((DispP->OpI == SC_OPRSET) && ScriptP->GNameADP && ScriptP->GNameADP->Data[*I2P]) {
	    NameP = (*(SC_StrHPointer)ScriptP->GNameADP->Data[*I2P]).StrDataP->Data;
	    OutPFP("%*s* %06d - %s%s -- %03d %7d <-- %s\n", Lead, "", Loc, OpS, ArgS, *VarP, *I2P, NameP);	
	} else
	    OutPFP("%*s* %06d - %s%s -- %03d %7d\n", Lead, "", Loc, OpS, ArgS, *VarP, *I2P);	
	goto BackInLoop;

    PrintII2:
	I4 = *(Uns8 *)(CurP + 1);
	I2P = (Uns16 *)(CurP + 2);
	OutPFP("%*s* %06d - %s%s -- %3d %7d\n", Lead, "", Loc, OpS, ArgS, I4, *I2P);
	goto BackInLoop;

    PrintK:
	KP = (Int8 *)(CurP + 1);
	OutPFP("%*s* %06d - %s%s -- %3d", Lead, "", Loc, OpS, ArgS, *KP);
	if (*(CurP+2) == SC_NOOP_CODE) {
	    OutPFP(" XXX XXX\n");
	    Len = 4;
	} else
	    OutPFP("\n");
	goto BackInLoop;

    PrintK3:
	K4 = *(Int32 *)CurP >> 8;		// Sign extends
	OutPFP("%*s* %06d - %s%s -- %11d\n", Lead, "", Loc, OpS, ArgS, K4);	
	goto BackInLoop;

    PrintI3:
	I4 = *(Uns32 *)CurP >> 8;		// Unsigned
	// Used during a compile AND to just show a Clos var...MAY NOT have GNameADP!

	OutPFP("%*s* %06d - %s%s -- %11d", Lead, "", Loc, OpS, ArgS, I4);
	if ((DispP->OpI == SC_OPREFG) || (DispP->OpI == SC_OPPSHG)) {
	    if (ScriptP->GNameADP && ScriptP->GNameADP->Data[I4])
		OutPFP(" <-- %s\n", (*(SC_StrHPointer)ScriptP->GNameADP->Data[I4]).StrDataP->Data);

	} else if (DispP->OpI == SC_OPPSHI) {
	    OutPFP(" <-- %ld\n", (Int64)KADP->Data[I4]);

	} else if (DispP->OpI == SC_OPPSHF) {
	    OutPFP(" <-- %lf\n", *(double *)&KADP->Data[I4]);

	} else if (DispP->OpI == SC_OPPSHS) {
	    SC_StrStuffStr(S, (*(SC_StrHPointer)SADP->Data[I4]).StrDataP->Data, _STRLEN);
	    OutPFP(" <-- \"%s\"\n", S);

	} else
	    OutPFP("\n");
	goto BackInLoop;

    #undef _STRLEN
}

void		SC_RunZapRun(SC_VMPointer VMP, SC_RunPointer RunP)
{
    SC_RCPointer	RCP;

    if ((RCP = RunP->RCP)) {
	// Purge the StackCopy, RunP is done and will NEVER resume!
	if (RCP->StackCopyP) {
	    SC_RunZapStackCopy(VMP, RCP);	// DeRef... losing for good
	    // Strip the RCP in case it stays around... stashed in some Var
	}
	RCP->RunP = NULL;
	RCP->ResumeP = NULL;
	RCP->StackCopyP = NULL;
    
	// DeRef RCP, but NOT if SC_RunZapRun is being called from inside
	// VarDeRef because RCP has RefCount 0 !!!
	SC_VARDEREFCOND(VMP, RCP->RefCount, (SC_VarVPointer)RCP);
    }

    #if SC_GC_REFCOUNT
    {
	// Free Vars + RefCount management (Skip VarArr[0] == CBP).
	// REG do not INC RefCount, so skip them!!
	SC_VarVPointer *	VarPP = RunP->VarArr + 1 + RunP->RCount;
	SC_VarVPointer *	EndVarPP = RunP->VarArr + RunP->VarCount;

	// Add InArgArrP + OutArgArrP
	if (RunP->Flags & SC_RUN_ALLARGSFLAG)
	    EndVarPP += 2;
	    
	while (VarPP < EndVarPP)
	    SC_VarDeRef(VMP, *(VarPP++));
    }
    #endif

    if (RunP->UPSADP)
	SC_SlabZapItem(VMP->ArrDataSP, RunP->UPSADP, RunP->UPSADP->BlockLen);
    SC_SlabZapItem(VMP->ChunkSP, RunP, RunP->BlockLen);
}

void		SC_RunZapCurRunRecord(SC_VMPointer VMP)
{
    SC_RunPointer	RunP;

    RunP = VMP->RunScriptP->CurRunP;
    VMP->RunScriptP->CurRunP = RunP->CallerP;
    if (RunP == VMP->RunScriptP->TopRunP) VMP->RunScriptP->TopRunP = NULL;

    #if SC_DEBUG_DISP_EXEC_OP
	if (VMP->RunScriptP->CurRunP)
	    printf("<<------------ Return to Code: %04d (S:%d)\n",
		    ((SC_CodeBlockPointer)VMP->RunScriptP->CurRunP->VarArr[0])->CodeId,
		    VMP->StackTop);
    #endif
    
    #if SC_DEBUG_PRINT_LOCALS
	SC_RunDisplayLocals(VMP, RunP, 0, 1);
    #endif

    SC_RunZapRun(VMP, RunP);
}


// StashAllArgs only *IFF* Func calls _SYS.GetInArr or GetOutArr.
// Arrs will be created empty if caller provided no args!
//
// NOTE: InArg/OutArg is the count of args provided by caller.
//	 InSpec/OutSpec is the count USED by function, i.e. will be stashed in local vars!
 
SC_RunPointer	SC_RunMakeRunRecord(SC_VMPointer VMP, SC_ClosPointer ClosP, Uns16 InOutCount, Uns8 DoUnPack)
{
    Uns16			InArg, OutArg;		// Number provided in this Call
    Uns16			InSpec, OutSpec;	// Number of params specified
    Uns16			I, J;
    SC_CodeBlockPointer		CBP;
    Uns16			Count;
    Uns32			BlockLen;
    SC_RunPointer		RunP;
    SC_VarVPointer *		VarPP;
    SC_VarVPointer *		ClosVarPP;
    SC_VarVPointer		ArgVarP;
    Uns16			StashAllArgs;
    SC_ArrVPointer		ArrVP;

    // NOTE: VarArr[0] stores CBP... In ByteOps, Var == 0x00 means POP or PUSH.
    CBP = (SC_CodeBlockPointer)ClosP->CBP;
    Count = 1 + CBP->RCount + CBP->LCount + CBP->CCount; 
    InArg = InOutCount & 0x00FF;			// # provided in call... can be 0
    OutArg = InOutCount >> 8;				// # provided in call... can be 0
    InSpec = CBP->InCount;				// # used by func, regardless of call
    OutSpec = CBP->OutCount;				// # used by func, regardless of call

    if (DoUnPack) {
	SC_UPSPointer	UPSP;
	//Pop CurRunP's UnPackStack and get additional counts for InArg and OutArg.
	RunP = VMP->RunScriptP->CurRunP;
	SC_SAFE_CHECK(RunP && RunP->UPSADP && RunP->UPSTop, SC_BADRUN_Internal);

	UPSP = (SC_UPSPointer)RunP->UPSADP->Data + --RunP->UPSTop;	// Decr
	InArg += UPSP->InCount;
	OutArg += UPSP->OutCount;

	if ((InArg > SC_CGMAXVARCOUNT) || (OutArg > SC_CGMAXVARCOUNT)) {
	    SC_SysError(SC_BADRUN_Stack);
	    return NULL;
	}
    }

    #if SC_DEBUG_DISP_EXEC_OP
	printf("----------------->> Into Code: %04d (S:%d)\n", CBP->CodeId, VMP->StackTop);
    #endif

    StashAllArgs = 0;
    BlockLen = sizeof(SC_RunRecord) + (sizeof(void *) * Count);
    if (CBP->Flags & SC_CB_ALLARGSFLAG) {
	BlockLen += sizeof(void *) * 2;			// 2 extra array ptrs in VarArr
	StashAllArgs = 1;
    }

    RunP = (SC_RunPointer)SC_ChunkNew(VMP, BlockLen, &BlockLen);
    RunP->Type = SC_CHUNK_RUN;
    RunP->State = SC_NOSTATE;
    RunP->Flags = (StashAllArgs) ? SC_RUN_ALLARGSFLAG : SC_RUN_NOFLAG;
    RunP->InCount = InArg;
    RunP->OutCount = OutArg;
    RunP->BlockLen = BlockLen;
    RunP->PC = 0L;
    RunP->UPSTop = 0;
    RunP->RCount = CBP->RCount;
    RunP->VarCount = Count;
    RunP->InitStackTop = VMP->StackTop - (InArg + OutArg);
    RunP->CallerP = VMP->RunScriptP->CurRunP;
    RunP->RCP = NULL;
    RunP->UPSADP = NULL;

    VarPP = RunP->VarArr;
    *VarPP++ = (SC_VarVPointer)CBP;				// VarArr[0]

    // REG are initially NULL, will get assigned later.
    Count = CBP->RCount;
    while (Count--) *VarPP++ = NULL;

    // IN are Pass-by-value, get from RunStack... already has RefCount INC
    // InArg can be FEWER than InSpec, or MORE!
    Count = InSpec;
    if ((I = InArg)) ArgVarP = SC_RUNSTACK_GETNTH(VMP, InArg + OutArg);
    while (Count--) {
	if (I)
	    SC_RunInitVar(VMP, VarPP, ArgVarP++), --I;		// From Call params
	else
	    *VarPP = SC_VarEntryNew(VMP);			// Make 0 Int
	    
	SC_DEBUG_VARDISP(VMP, *VarPP, "Alloc InArg ");
	VarPP += 1;
    }

    // OUT are Pass-by-ref, get from RunStack... already has RefCount INC
    // OutArg can be FEWER than OutSpec, or MORE!
    Count = OutSpec;
    if ((I = OutArg)) ArgVarP = SC_RUNSTACK_GETNTH(VMP, OutArg);
    while (Count--) {
	if (I) {
	    *VarPP = (ArgVarP++)->ValueP;		// Pass by Ref from Call Param
	    if (*VarPP)
		(*VarPP)->Flags |= SC_VAR_ESCFLAG;	// Escaped from caller's context
	    else {
		// OUT list has NULL arg
		*VarPP = SC_VarEntryNew(VMP);		// Make 0 Int		
		SC_DEBUG_VARDISP(VMP, *VarPP, "Alloc OutArg");
	    }
	    VarPP += 1, I -= 1;
	}
	else {
	    *VarPP = SC_VarEntryNew(VMP);		// Make 0 Int
	    SC_DEBUG_VARDISP(VMP, *VarPP, "Alloc OutArg");
	    VarPP += 1;
	}
    }

    // Init the remaining locals (Initialized to Int 0)
    Count = CBP->LCount - (InSpec + OutSpec);
    while (Count--) {
	*VarPP = SC_VarEntryNew(VMP);			// Make 0 Int		    
	SC_DEBUG_VARDISP(VMP, *VarPP, "Alloc Local ");
	VarPP += 1;
    }

    // Copy the CClose count from ClosP; Inc RefCount (VarRef) too!
    if ((Count = CBP->CCount)) {
	ClosVarPP = ClosP->VarArr;
	while (Count--) {
	    *VarPP = *ClosVarPP++;
	    SC_VARREF(*VarPP);		// DO NOT get fancy in Macro!
	    ++VarPP;
	}
    }    

    if (StashAllArgs) {
	// InArg: Make Arr... ADP pts to local vars!
	//	  If >InSpec params, make extra VarVRecords and have ADP point to them too.
	//	  (IN Param count can be 0, =, <, or > than InSpec.)
	ArrVP = SC_ArrMakeNewArr(VMP, InArg);
	RunP->VarArr[RunP->VarCount] = (SC_VarVPointer)ArrVP;
	if (InArg) {
	    Count = RunP->RCount;				// Index into VarArr
	    VarPP = (SC_VarVPointer *)ArrVP->ArrDataP->Data;	// Where we stash
	    if (InArg <= InSpec)
		I = InArg, J = 0;				// All used, no extra
	    else {
		I = InSpec, J = InArg - InSpec;			// J extra unused parameters!
		ArgVarP = SC_RUNSTACK_GETNTH(VMP, J + OutArg);
	    }
	    
	    // Copy Ref to Local vars... and Inc RefCount... these were used as
	    // formal parameters, so need an extra Ref for use in Function and this Array.
	    while (I--) {
		*VarPP = RunP->VarArr[++Count];			// Pre ++ to skip CBP in [0]
		SC_VARREF(*VarPP);				// Extra Ref!
		(*VarPP)->Flags |= SC_VAR_ESCFLAG;		// These COULD Escape!
		VarPP += 1;					// Don't ++ in macro
	    }
	    // Extra args... make new Vars and refer to them.
	    // These were NOT used in parameters, so the extra RefCount from the RunStack
	    // transfers to usage in this array.
	    while (J--) {
		SC_RunInitVar(VMP, VarPP, ArgVarP++);		// Comes in with Ref, Does +Ref if Ptr
		SC_DEBUG_VARDISP(VMP, *VarPP, "Alloc ExtrIn");	    			
		VarPP += 1;
	    }
	}

	// OutArg: param count can also be 0, =, <, or > than OutSpec.
	ArrVP = SC_ArrMakeNewArr(VMP, OutArg);
	RunP->VarArr[RunP->VarCount + 1] = (SC_VarVPointer)ArrVP;
	if (OutArg) {
	    Count = RunP->RCount + InSpec;			// Index into VarArr
	    VarPP = (SC_VarVPointer *)ArrVP->ArrDataP->Data;	// Where we stash
	    if (OutArg <= OutSpec)
		I = OutArg, J = 0;				// All used, no extra
	    else {
		I = OutSpec, J = OutArg - OutSpec;		// J extra unused!
		ArgVarP = SC_RUNSTACK_GETNTH(VMP, J);
	    }
	    
	    // Copy Ref to Local vars... and Inc RefCount... used in Func and this Array.
	    while (I--) {
		*VarPP = RunP->VarArr[++Count];			// Pre ++ to skip CBP in [0]
		SC_VARREF(*VarPP);				// Extra Ref
		// Already marked as Escaped!
		VarPP += 1;					// Don't ++ in macro
	    }
	    // Extra args... refer to these locally unsused Vars... Ref Inc for the stack
	    // transfers to usage here, so no separate VARREF!
	    while (J--) {
		*VarPP = (ArgVarP++)->ValueP;			// Get Reference
		if (*VarPP)					// Could be NULL in the arg list
		    (*VarPP)->Flags |= SC_VAR_ESCFLAG;		// These Escaped too!
		else						// Was NULL... make a new one
		    *VarPP = SC_VarEntryNew(VMP);		// Make 0 Int, RefCount = 1
		VarPP += 1;
	    }
	}
	// The RefCount Inc for the stack has been handled... everything goes into extra Array
	
    } else {

	#if SC_GC_REFCOUNT
	    // Decrement RefCount for args that did NOT make it into local params!
	    if (InArg > InSpec) {
		J = InArg - InSpec;
		ArgVarP = SC_RUNSTACK_GETNTH(VMP, J + OutArg);
		while (J--) {
		    SC_VARDEREFCOND(VMP, (ArgVarP->ValueType == SC_VAL_PTR) && ArgVarP, ArgVarP->ValueP);
		    ArgVarP += 1;
		}
	    }
	    if (OutArg > OutSpec) {
		J = OutArg - OutSpec;
		ArgVarP = SC_RUNSTACK_GETNTH(VMP, J);
		while (J--) {
		    SC_VARDEREFCOND(VMP, ArgVarP, ArgVarP->ValueP);	// Not NULL!
		    ArgVarP += 1;
		}
	    }
	#endif
    }
    
    if ((Count = InArg + OutArg))
	SC_RUNSTACK_POPN(VMP, Count);

    VMP->RunScriptP->CurRunP = RunP;
    return RunP;
}


// Inc RCP->RefCount each time!  Should get Dec if not stashed...

SC_RCPointer	SC_RunGetRC(SC_VMPointer VMP)
{
    SC_RunPointer	RunP = VMP->RunScriptP->CurRunP;
    SC_RCPointer	RCP = RunP->RCP;

    if (RCP) {
	SC_VARREF((SC_VarVPointer)RCP);
	return RCP;
    }

    RCP = (SC_RCPointer)SC_BigEntryNew(VMP);
    RCP->Type = SC_ENTRY_RC;
    RCP->State = SC_NOSTATE;
    RCP->Flags = SC_RC_NOFLAG;
    RCP->RefCount = 1;

    RCP->RunP = RunP;
    RCP->ResumeP = NULL;
    RCP->StackCopyP = NULL;

    RunP->RCP = RCP;
    return RCP;
}

// LowP MUST be a valid RunP.
// HighP can be NULL... which means up to and including MAIN!

void		SC_RunReleaseRunChain(SC_VMPointer VMP, SC_RunPointer LowP, SC_RunPointer HighP)
{
    SC_RunPointer	CallerP;
    while (LowP != HighP) {
	CallerP = LowP->CallerP;
	SC_RunZapRun(VMP, LowP);
	LowP = CallerP;
    }
}

// RCP StackCopy ***************************************************************
//
// The subordinate (S) coroutine MAY have a few things on stack when it SUBMITs
// or YIELDs to the master (M) that called it.  It is important to save these in
// a StackCopy before RESUME of S.  (The StackCopy *only* saves what the S
// routine has put on the stack, NOT the whole stack.)
//
// S will have nothing on the RunStack *EXCEPT* when
// -- Submit/Yield from inside a MapI/MapK call,
// -- Yield non-locally. ***
//
//    *** SUBMIT does NOT take an OutOf arg, so must be local.  But can YIELD
//        (OutOf) nonlocally from a lower function.  Normally, functions will
//        immediately take their call args off the RunStack, but things will be
//        left on the RunStack *IF* the lower function is called WHILE
//        assembling the args for another call!  Complicated...
//
// RCP has a ptr for a StackCopy ADM.  This is normally NULL, but is used to
// stash the StackCopy while control is back in M.  During RESUME, this
// StackCopy is placed back on the RunStack, and the ADM stashed in RCP is
// zapped.


// SC_PushStackCopy will copy the StackCopy back to the RunStack so the
// CoRoutine (S) can be resumed.
//
// DO NOT call if RCP->StackCopyP is NULL!
//
// NOTE: Once the StackCopy is put back on the RunStack, the ADM is directly
//	 zapped and RCP->StackCopyP is set back to NULL.

void		SC_PushStackCopy(SC_VMPointer VMP, SC_RCPointer RCP)
{
    SC_ArrDataPointer	ADP = RCP->StackCopyP;
    SC_VarVPointer	SP, DP;
    Uns32		RecCount;

    RecCount = (ADP->SlotCount * sizeof(void *)) / sizeof(SC_VarVRecord);
    SP = (SC_VarVPointer)ADP->Data;		// Source
    DP = VMP->StackP + VMP->StackTop;	        // Dest
    VMP->StackTop += RecCount;
    while (RecCount--) {
	// Copying only ValueType and ValueP to stack... but
	// advance 1 whole ValueVRecord (16 bytes).
	DP->ValueType = SP->ValueType;
	DP->ValueP = SP->ValueP;
	// Do NOT mess with RefCount... just a copy of RunStack.
	++DP, ++SP;
    }

    // Done with the StackCopy... zap it
    SC_SlabZapItem(VMP->ArrDataSP, ADP, ADP->BlockLen);
    RCP->StackCopyP = NULL;
}

// SC_RunMakeStackCopy is only called for CoRoutines, when Slave is returning to
// its Master (caller) during SUBMIT or YIELD.
//
// Stash the stack from RCP->RunP->InitStackTop... that could be N levels up if
// "YIELD OutOf RCP".  (Also RunP and ResumeP fields are normally the same,
// except when YIELD OutOf RCP, where RunP can be N levels higher.)

void	SC_RunMakeStackCopy(SC_VMPointer VMP, SC_RCPointer RCP)
{
    Uns32		Count;
    SC_VarVPointer	SP, DP;

    // Copy from beginning of RCP->RunP->InitStackTop!
    if ((Count = VMP->StackTop - RCP->RunP->InitStackTop)) {		// Assign!
	Count = (Count * sizeof(SC_VarVRecord)) / sizeof(void *);	// From Recs to LL slots
	SC_ArrAllocNewData(VMP, &RCP->StackCopyP, Count, 0);		// New ADM + MasterPP

	// Now copy entries from the stack.  Count of Recs again!
	Count = RCP->RunP->InitStackTop;
	SP = VMP->StackP + Count;			// Source
	DP = (SC_VarVPointer)RCP->StackCopyP->Data;	// Dest
	while (Count++ < VMP->StackTop) {
	    DP->ValueType = SP->ValueType;
	    DP->ValueP = SP->ValueP;
	    // Do NOT Inc RefCounts... transfered here from RunStack.
	    ++DP, ++SP;
	}
    }
}

// SC_RunZapStackCopy is called *ONLY* when the RCP is being disposed.  This
// does NOT happen during the normal "dance" of CoRoutines.  (SC_PushStackCopy
// will ITSELF zap the StackCopy.)
//
// Will DEREF contents... Since they came from a stack, and no longer there!
// Avoids looping if no GC_REFCOUNT...  Mark/Sweep will clean!

void	SC_RunZapStackCopy(SC_VMPointer VMP, SC_RCPointer RCP)
{
    // NOTE: Normal arrays store POINTERS to vars.  This one stores the
    //	     var records (like RunStack) itself!!  Records, not vars!!

    SC_ArrDataPointer		ADP = RCP->StackCopyP;

#if SC_GC_REFCOUNT
    SC_VarVPointer 		CurVP = (SC_VarVPointer)ADP->Data;
    Int32			VarCount = (ADP->SlotCount * sizeof(void *)) / sizeof(SC_VarVRecord);
    SC_VarVPointer 		EndVP = CurVP + VarCount;

    while (CurVP < EndVP) {
	if ((CurVP->ValueType == SC_VAL_PTR) && CurVP->ValueP)
	    SC_VarDeRef(VMP, (SC_VarVPointer)CurVP->ValueP);
	CurVP += 1;
    }
#endif

    SC_SlabZapItem(VMP->ArrDataSP, ADP, ADP->BlockLen);
    RCP->StackCopyP = NULL;
}


// SC_RunExecCurFunc is the main (virtual machine) runtime execution loop for
// byte codes.  In most cases, there is one and only one instance of this
// function executing all script byte codes.  HOWEVER, in the case of
// sub-scripts or scripting callbacks, as in handing a FUNC (closure) to the
// pattern matcher, ANOTHER instance of this function has to run: (This can
// recurse more than 1 level!)
//
// Main SC_RunExecCurFunc		(Executes main script)
//    SC_SysMatchPat			(Pattern matcher...)
//	 SC_RunExecCurFunc		(Callback being executed)
//	      SC_SysMatchPat		(Callback ITSELF call pattern matcher...)
//		  SC_RunExecCurFunc	(Yet another level of Callbacks...)
//			....
//
// Since SC_RunExecCurFunc MAY be called multiple times, an attempt should be
// made to minimize its memory (C call/var frame) footprint by separating out
// some of its locals into globals--which are stashed and restored.



// Speed optimization:
// Every time SC_ExecAbortCount rolls over (becomes 0 again), check ED to see
// if user wants to abort.  Instead of checking EVERY ByteCode, the system
// checks every 256 or 65536 steps... depending on Debug state.

#if SC_DEBUG_DISP_EXEC_OP
    Uns8	SC_ExecAbortCount	= 0;	// Uns8 -- Checks more if Debug printing!
#else
    Uns16	SC_ExecAbortCount	= 0;	// Uns16 -- Rolls over every 65536 steps.
#endif


void	SC_RunPrintReturn(SC_VMPointer VMP)
{
    VMP->OutPrintFP("\n\n   *********************************************\n");
    VMP->OutPrintFP("   SCRIPT RETURN -->  ");
    SC_SysWriteAux(VMP, 1, VMP->OutPrintFP, 0, 0);
    VMP->OutPrintFP("\n   *********************************************\n\n");
}

// Returns 0 of No errors, Returns 1 if errors!
//
// Every function returns a value, even if Int 0 by default.
// Top level Script (even a Sub-Script) CANNOT return an RCP!
// (RCP becomes meaningless outside its Run environment!)

Int16	SC_RunExecCurFunc(SC_VMPointer VMP, Int8 IsCallback)
{
    SC_RunPointer	RunP;
    SC_VarVPointer *	CVarArr;
    SC_CodeBlockPointer	CBP;
    Uns8 *		CPStart;
    Uns8 *		CP;			// Code Pointer (PC Program Counter)
    Uns8 *		OpCP;
#ifdef SC_SAFE
    Uns8 *		CPEnd;
#endif

    SC_VarVPointer	Var1P, Var2P, Var3P;
    Int64		Res, Val2, Val3;
    double		ResFlt;
    
    Int32		K4;
    Uns32		I4;

    Uns8		V1, V2, V3;		// DestByte, SourceByte
    Int8		IsVVK, IsVKV;
    Uns8		ResType;
    Int16		RunErr;
    SC_PrintFuncP	OutPFP = VMP->OutPrintFP;

    void *		OpAddrP;
    // ORDER IS IMPERATIVE must match SC_BYTE_CODE ************************
    static void *	ByteOpTable[] = {&&NO_OP,
					 &&ADD_VVV, &&ADD_sss, &&ADD_VVK,
					 &&SUB_VVV, &&SUB_sss, &&SUB_VKV,
					 &&MUL_VVV, &&MUL_sss, &&MUL_VVK,
					 &&DIV_VVV, &&DIV_sss, &&DIV_VVK,
					 &&MOD_VVV, &&MOD_sss, &&MOD_VVK,
					 &&SHL_VVV, &&SHL_sss, &&SHL_VVK,
					 &&SHR_VVV, &&SHR_sss, &&SHR_VVK,
					 &&MOV_VV, &&MOV_VK2,
					 &&POP_V, &&DoPutV, &&DoGetV, &&POPN_K, &&DoPOP1, &&DoSWAP,
					 &&DoPSH1, &&PSH_V, &&PSH_K, &&PSH_K3, &&PSHI_V, &&PSHI_I3, &&PSHF_V, &&PSHF_I3, &&PSHG_I3, &&PSHS_I3,
					 &&REF_V, &&REFG_I3,
					 &&RSET_VI2, &&VSET_V, &&ISET_VVV,
					 &&JMP_K3, &&BRT_K3, &&BRF_K3,
					 &&LT_VVV, &&LT_sss, &&LT_VK2, &&LT_K2V,
					 &&LTQ_VVV, &&LTQ_sss, &&LTQ_VK2, &&LTQ_K2V,
					 &&EQ_VVV, &&EQ_sss, &&EQ_VK2,
					 &&NEQ_VVV, &&NEQ_sss, &&NEQ_VK2,
					 &&ID_VVV, &&NID_VVV,
					 &&POS_V, &&POS_VV,
					 &&NEG_V, &&NEG_VV,
					 &&NOT_V, &&NOT_VV,
					 &&BNEG_V, &&BNEG_VV,
					 &&BAND_VVV, &&BAND_sss,
					 &&BOR_VVV, &&BOR_sss,
					 &&BXOR_VVV, &&BXOR_sss,
					 &&SWCH_VI2,
					 &&FMAK_VI2,
					 &&DoFBeg, &&DoFEnd, &&DoFSub,		// No arg step for FBEG/FEND/FANCE
					 &&FRET, &&FRET_V, &&FRET_K, &&FRET_VV, &&FYLD_VV, &&FRES_V, &&FREL_V, &&FGET_V,
					 &&FCALL_VK2, &&UFCAL_VK2, &&SCALL_IK2, &&USCAL_IK2, &&DoUPS, &&UNPKI, &&UNPKO,
					 &&DoANew, &&AINI_V,			// ANEW/DNEW will get own args
					 &&DoDNew, &&DoDIni,			// No arg step for DINI
					 &&IREF_VV, &&IREF_VK2, &&ZREF_VV, &&ZREF_VK2,
					 &&IGET_VVV, &&IGET_VVK,
					 &&IMAP, &&KMAP,
					};
    void *		SubRetP;
    Uns16		SubOp;							// Index into sub OpTable
    Uns8		SubIsEQ = 0;						// Set for EQ and NEQ math subs
    static void *	SubOpTable[] = {&&ADD00, &&ADD01, &&ADD10, &&ADD11,
				        &&SUB00, &&SUB01, &&SUB10, &&SUB11,
					&&MUL00, &&MUL01, &&MUL10, &&MUL11,
					&&DIV00, &&DIV01, &&DIV10, &&DIV11,
					&&MOD00, &&MOD01, &&MOD10, &&MOD11,
					&&SHL00, &&SHL01, &&SHL10, &&SHL11,
					&&SHR00, &&SHR01, &&SHR10, &&SHR11,
					&&LET00, &&LET01, &&LET10, &&LET11,
					&&LTQ00, &&LTQ01, &&LTQ10, &&LTQ11,
					&&EQ00,  &&EQ01,  &&EQ10,  &&EQ11,
					&&NEQ00, &&NEQ01, &&NEQ10, &&NEQ11,
					&&BAND,  &&BAND,  &&BAND,  &&BAND,	// All 4 do the same thing!
					&&BOR,   &&BOR,   &&BOR,   &&BOR,	// All 4 do the same thing!
					&&BXOR,  &&BXOR,  &&BXOR,  &&BXOR,	// All 4 do the same thing!

					&&MNEG0, &&MNEG1, &&LPOS, &&LNEG, &&BNEG,
				       };

#if SC_DEBUG_DISP_EXEC_OP				       
    #define SC_RRCF_DISPATCH_OP		printf("%02d/%04ld  Op->%s%s (S:%d)\n", CBP->CodeId,		\
						CP - CPStart, SC_OpStrArr[SC_OpDispArr[*CP].OpI],	\
						SC_ArgStrArr[SC_OpDispArr[*CP].ArgI], VMP->StackTop);	\
					if ((! ++SC_ExecAbortCount) && ED_DidAbort()) goto RUN_ERROR;	\
					SC_GC_STEP(VMP);						\
					goto *ByteOpTable[*(OpCP = CP++)]
#else
    #define SC_RRCF_DISPATCH_OP		if ((! ++SC_ExecAbortCount) && ED_DidAbort()) goto RUN_ERROR;	\
					SC_GC_STEP(VMP);						\
					goto *ByteOpTable[*(OpCP = CP++)]
#endif

    IsVVK = IsVKV = 0;			// Reset after each use
    RunErr = SC_RUN_NOERR;
    // Updates RunP->PC using CP *ONLY* when needed (FCall) to stash!!

RUN_START:
    // Come here when starting a new function.
    RunP = VMP->RunScriptP->CurRunP;
    CVarArr = RunP->VarArr;
    CBP = (SC_CodeBlockPointer)CVarArr[0];
    CP = CPStart = (Uns8 *)CBP->CodeArr;
    #ifdef SC_SAFE
	CPEnd = CPStart + CBP->CodeLen;
    #endif
    SC_RRCF_DISPATCH_OP;		// Dispatch Op... and so it begins!!

RUN_RESUME:
    // Come here when resuming a previous function (after returning from Call).
    if ((RunP = VMP->RunScriptP->CurRunP)) {
	CVarArr = RunP->VarArr;
	CBP = (SC_CodeBlockPointer)CVarArr[0];
	CPStart = (Uns8 *)CBP->CodeArr;
	CP = CPStart + RunP->PC;	// Resume at PC!
	#ifdef SC_SAFE
	    CPEnd = CPStart + CBP->CodeLen;
	#endif
	SC_RRCF_DISPATCH_OP;		// Dispatch Op... continues...
	
    } else {
	// Get here only if TOP just did an FEnd or FRet!
	if (IsCallback) return 0;

	// NOT a Callback!  Return value CANNOT be an RCP!!
	Var1P = SC_RUNSTACK_GETNTH(VMP, 1);
	if ((Var1P->ValueType == SC_VAL_PTR) &&
	    (Var1P->ValueP) &&
	    ((*(SC_VarVPointer)Var1P->ValueP).Type == SC_ENTRY_RC)) {
	    // Turn RCP into INT 0 instead!!
	    SC_VARDEREF(VMP, Var1P->ValueP);
	    Var1P->ValueType = SC_VAL_INT;
	    Var1P->ValueP = (void *)0LL;
	}
	
	SC_RunPrintReturn(VMP);

	SC_SAFE_CHECK(VMP->StackTop == VMP->RunScriptP->StartStackTop+1, SC_BADRUN_Stack);
	#if SC_DEBUG_PRINT_GLOBALS
	    SC_RunDisplayGlobs(VMP, VMP->RunScriptP, SC_SHOW_LOW, 1, 0);
	#endif

	return 0;			// End Executing!!
    }

RUN_ERROR:
    // This is a runtime error... relatively high-level problems for
    // the script writer, not an internal compiler error that needs
    // C programming!  Importantly, system continues and can run again.

    if (RunErr != SC_RUN_QUIETEXIT) {
	OutPFP("\n   *********************************************\n");
	if (RunErr == SC_RUN_NOERR)
	    OutPFP("   RUN ERROR --> %s\n", SC_RunErrorArr[SC_RUN_NOERR]);	// User Abort
	else if (RunErr == SC_RUN_EXIT)
	    OutPFP("   EXIT --> %s\n", SC_UserErrorStr);
	else if (RunErr == SC_RUN_UEXIT)
	    OutPFP("   EXIT --> %s\n", SC_RunErrorArr[RunErr]);
	else if (RunErr > SC_RUN_USERERR)
	    OutPFP("   RUN ERROR --> %s\n", SC_RunErrorArr[RunErr]);
	else if (SC_UserErrorStr) {
	    OutPFP("   RUN ERROR --> %s\n", SC_UserErrorStr);
	    SC_UserErrorStr = NULL;
	} else
	    OutPFP("   RUN ERROR --> %s\n", SC_RunErrorArr[SC_RUN_USERERR]);

	if (SC_RunErrorInfoStr) {
	    OutPFP("   %s\n", SC_RunErrorInfoStr);
	    SC_RunErrorInfoStr = NULL;
	}

	if ((RunErr != SC_RUN_EXIT) && (RunErr != SC_RUN_UEXIT))
	    OutPFP("   CodeId:%d  PC:%d  Op:%s%s  (Stack:%d)\n", CBP->CodeId, OpCP - CPStart,
				SC_OpStrArr[SC_OpDispArr[*OpCP].OpI], SC_ArgStrArr[SC_OpDispArr[*OpCP].ArgI],
				VMP->StackTop);

	OutPFP("   *********************************************\n\n");
    }
    // Flush the stack and Zap run records--must also DeRef its vars...
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->RunScriptP->StartStackTop);		// DeRef popped elts.
    // Now push a 0 result on the RunStack
    Var1P = SC_RUNSTACK_PUSH(VMP);
    Var1P->ValueType = SC_VAL_INT;
    Var1P->ValueP = (void *)0LL;
    
    while (VMP->RunScriptP->CurRunP)
	SC_RunZapCurRunRecord(VMP);
    return 1;
    
NO_OP:
    // Often come in multiples... for Op alignment.
    while (! *CP) CP += 1;
    SC_RRCF_DISPATCH_OP;

ADD_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoMathVVV;
    SubOp = SC_RUN_ADD;
    goto GetOpArgs;

ADD_sss:
    SubOp = SC_RUN_ADD;
    goto DoMathsss;

ADD_VVK:
    V1 = *CP++; V2 = *CP++; Val3 = (Int8)*CP++;
    IsVVK = 1;
    OpAddrP = &&DoMathVVK;
    SubOp = SC_RUN_ADD;
    goto GetOpArgs;

SUB_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoMathVVV;
    SubOp = SC_RUN_SUB;
    goto GetOpArgs;
    
SUB_sss:
    SubOp = SC_RUN_SUB;
    goto DoMathsss;

SUB_VKV:
    V1 = *CP++; V3 = *CP++; Val2 = (Int8)*CP++;	// K always comes last!
    IsVKV = 1;
    OpAddrP = &&DoMathVKV;
    SubOp = SC_RUN_SUB;
    goto GetOpArgs;

MUL_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoMathVVV;
    SubOp = SC_RUN_MUL;
    goto GetOpArgs;

MUL_sss:
    SubOp = SC_RUN_MUL;
    goto DoMathsss;

MUL_VVK:
    V1 = *CP++; V2 = *CP++; Val3 = (Int8)*CP++;
    IsVVK = 1;
    OpAddrP = &&DoMathVVK;
    SubOp = SC_RUN_MUL;
    goto GetOpArgs;

DIV_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoMathVVV;
    SubOp = SC_RUN_DIV;
    goto GetOpArgs;

DIV_sss:
    SubOp = SC_RUN_DIV;
    goto DoMathsss;

DIV_VVK:
    V1 = *CP++; V2 = *CP++; Val3 = (Int8)*CP++;
    IsVVK = 1;
    OpAddrP = &&DoMathVVK;
    SubOp = SC_RUN_DIV;
    goto GetOpArgs;

MOD_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoMathVVV;
    SubOp = SC_RUN_MOD;
    goto GetOpArgs;

MOD_sss:
    SubOp = SC_RUN_MOD;
    goto DoMathsss;

MOD_VVK:
    V1 = *CP++; V2 = *CP++; Val3 = (Int8)*CP++;
    IsVVK = 1;
    OpAddrP = &&DoMathVVK;
    SubOp = SC_RUN_MOD;
    goto GetOpArgs;

SHL_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoMathVVV;
    SubOp = SC_RUN_SHL;
    goto GetOpArgs;

SHL_sss:
    SubOp = SC_RUN_SHL;
    goto DoMathsss;

SHL_VVK:
    V1 = *CP++; V2 = *CP++; Val3 = (Int8)*CP++;
    IsVVK = 1;
    OpAddrP = &&DoMathVVK;
    SubOp = SC_RUN_SHL;
    goto GetOpArgs;    
    
SHR_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoMathVVV;
    SubOp = SC_RUN_SHR;
    goto GetOpArgs;

SHR_sss:
    SubOp = SC_RUN_SHR;
    goto DoMathsss;

SHR_VVK:
    V1 = *CP++; V2 = *CP++; Val3 = *CP++;
    IsVVK = 1;
    OpAddrP = &&DoMathVVK;
    SubOp = SC_RUN_SHR;
    goto GetOpArgs;

LT_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoMathVVV;
    SubOp = SC_RUN_LT;
    goto GetOpArgs;

LT_sss:
    SubOp = SC_RUN_LT;
    goto DoMathsss;

LT_VK2:
    V1 = 0x00; V2 = *CP++; Val3 = *(Int16 *)CP; CP += 2;
    IsVVK = 1;
    OpAddrP = &&DoMathVVK;
    SubOp = SC_RUN_LT;
    goto GetOpArgs;

LT_K2V:
    V1 = 0x00; V3 = *CP++; Val2 = *(Int16 *)CP; CP += 2;
    IsVKV = 1;
    OpAddrP = &&DoMathVKV;
    SubOp = SC_RUN_LT;
    goto GetOpArgs;    

LTQ_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoMathVVV;
    SubOp = SC_RUN_LTQ;
    goto GetOpArgs;

LTQ_sss:
    SubOp = SC_RUN_LTQ;
    goto DoMathsss;

LTQ_VK2:
    V1 = 0x00; V2 = *CP++; Val3 = *(Int16 *)CP; CP += 2;
    IsVVK = 1;
    OpAddrP = &&DoMathVVK;
    SubOp = SC_RUN_LTQ;
    goto GetOpArgs;

LTQ_K2V:
    V1 = 0x00; V3 = *CP++; Val2 = *(Int16 *)CP; CP += 2;
    IsVKV = 1;
    OpAddrP = &&DoMathVKV;
    SubOp = SC_RUN_LTQ;
    goto GetOpArgs;

EQ_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoMathVVV;
    SubOp = SC_RUN_EQ;
    SubIsEQ = 1;
    goto GetOpArgs;

EQ_sss:
    SubOp = SC_RUN_EQ;
    SubIsEQ = 1;
    goto DoMathsss;

EQ_VK2:
    V1 = 0x00; V2 = *CP++; Val3 = *(Int16 *)CP; CP += 2;
    IsVVK = 1;
    OpAddrP = &&DoMathVVK;
    SubOp = SC_RUN_EQ;
    SubIsEQ = 1;
    goto GetOpArgs;

NEQ_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoMathVVV;
    SubOp = SC_RUN_NEQ;
    SubIsEQ = 1;
    goto GetOpArgs;

NEQ_sss:
    SubOp = SC_RUN_NEQ;
    SubIsEQ = 1;
    goto DoMathsss;

NEQ_VK2:
    V1 = 0x00; V2 = *CP++; Val3 = *(Int16 *)CP; CP += 2;
    IsVVK = 1;
    OpAddrP = &&DoMathVVK;
    SubOp = SC_RUN_NEQ;
    SubIsEQ = 1;
    goto GetOpArgs;

BAND_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoMathVVV;
    SubOp = SC_RUN_BAND;
    goto GetOpArgs;

BAND_sss:
    SubOp = SC_RUN_BAND;
    goto DoMathsss;

BOR_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoMathVVV;
    SubOp = SC_RUN_BOR;
    goto GetOpArgs;

BOR_sss:
    SubOp = SC_RUN_BOR;
    goto DoMathsss;

BXOR_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoMathVVV;
    SubOp = SC_RUN_BXOR;
    goto GetOpArgs;

BXOR_sss:
    SubOp = SC_RUN_BXOR;
    goto DoMathsss;

ID_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    ResType = 0;	// Means POS logic
    OpAddrP = &&DoIdnt;
    goto GetOpArgs;

NID_VVV:
    V1 = *CP++; V2 = *CP++; V3 = *CP++;
    ResType = 1;	// Means NEG logic
    OpAddrP = &&DoIdnt;
    goto GetOpArgs;

MOV_VV:
    V1 = *CP++; V2 = *CP++;
    IsVVK = 1;		// Ignores V3!
    OpAddrP = &&DoMovVV;
    goto GetOpArgs;

MOV_VK2:
    V1 = *CP++; Val2 = *(Int16 *)CP; CP += 2;
    IsVVK = IsVKV = 1;	// Ignore V2 and V3!
    OpAddrP = &&DoMovVK2;
    goto GetOpArgs;

POP_V:
    V1 = *CP++;
    IsVVK = IsVKV = 1;	// Ignore V2 and V3!
    OpAddrP = &&DoPopV;
    goto GetOpArgs;

POPN_K:
    K4 = (Int8)*CP++;
    goto DoPopN;

PSH_V:
    V2 = *CP++;
    goto DoPushV;

PSH_K:
    K4 = (Int8)*CP++;
    goto DoPushK;

PSH_K3:
    K4 = *(Int32 *)(CP-1) >> 8; CP += 3;
    goto DoPushK;

PSHI_V:
    V2 = *CP++;
    goto DoPushIntV;

PSHI_I3:
    I4 = *(Uns32 *)(CP-1) >> 8; CP += 3;
    goto DoPushInt;

PSHF_V:
    V2 = *CP++;
    goto DoPushFltV;

PSHF_I3:
    I4 = *(Uns32 *)(CP-1) >> 8; CP += 3;
    goto DoPushFlt;

PSHG_I3:
    I4 = *(Uns32 *)(CP-1) >> 8; CP += 3;
    goto DoPushGlob;

PSHS_I3:
    I4 = *(Uns32 *)(CP-1) >> 8; CP += 3;
    goto DoPushStr;

REF_V:
    V2 = *CP++;
    goto DoPushRef;

REFG_I3:
    I4 = *(Uns32 *)(CP-1) >> 8; CP += 3;
    goto DoPushGRef;

RSET_VI2:
    V1 = *CP++; I4 = *(Uns16 *)CP; CP += 2;
    goto DoRegSet;

VSET_V:
    V1 = *CP++;
    goto DoVSet;

JMP_K3:
    CP -= 1;
    K4 = *(Int32 *)CP >> 8;
    CP += K4;
    SC_SAFE_CHECK((CP >= CPStart) && (CP < CPEnd), SC_BADRUN_Addr);
    SC_RRCF_DISPATCH_OP;

BRT_K3:
    CP -= 1;
    K4 = *(Int32 *)CP >> 8;
    //DoBranchT will (CP += 4) if it DOES NOT jump
    goto DoBranchT;
    
BRF_K3:
    CP -= 1;
    K4 = *(Int32 *)CP >> 8;
    //DoBranchF will (CP += 4) if it DOES NOT jump
    goto DoBranchF;

POS_V:
    V1 = 0x00; V2 = *CP++;
    IsVVK = 1;		// Ignore V3
    SubOp = SC_RUN_LPOS;
    OpAddrP = &&DoLogVV;
    goto GetOpArgs;

POS_VV:
    V1 = *CP++; V2 = *CP++;
    IsVVK = 1;		// Ignore V3
    SubOp = SC_RUN_LPOS;
    OpAddrP = &&DoLogVV;
    goto GetOpArgs;

NEG_V:
    V1 = 0x00; V2 = *CP++;
    IsVVK = 1;		// Ignore V3
    SubOp = SC_RUN_MNEG;
    OpAddrP = &&DoLogVV;
    goto GetOpArgs;

NEG_VV:
    V1 = *CP++; V2 = *CP++;
    IsVVK = 1;		// Ignore V3
    SubOp = SC_RUN_MNEG;
    OpAddrP = &&DoLogVV;
    goto GetOpArgs;

NOT_V:
    V1 = 0x00; V2 = *CP++;
    IsVVK = 1;		// Ignore V3
    SubOp = SC_RUN_LNEG;
    OpAddrP = &&DoLogVV;
    goto GetOpArgs;

NOT_VV:
    V1 = *CP++; V2 = *CP++;
    IsVVK = 1;		// Ignore V3
    SubOp = SC_RUN_LNEG;
    OpAddrP = &&DoLogVV;
    goto GetOpArgs;

BNEG_V:
    V1 = 0x00; V2 = *CP++;
    IsVVK = 1;		// Ignore V3
    SubOp = SC_RUN_BNEG;
    OpAddrP = &&DoLogVV;
    goto GetOpArgs;

BNEG_VV:
    V1 = *CP++; V2 = *CP++;
    IsVVK = 1;		// Ignore V3
    SubOp = SC_RUN_BNEG;
    OpAddrP = &&DoLogVV;
    goto GetOpArgs;

SWCH_VI2:
    // Does NOT need PC, will do absolute JMP.
    V1 = *CP++; I4 = *(Uns16 *)CP; CP += 2;
    goto DoSwitch;
    
FMAK_VI2:
    V1 = *CP++; I4 = *(Uns16 *)CP; CP += 2;
    IsVVK = IsVKV = 1;		// Ignore V2 and V3
    OpAddrP = &&DoFMak;    
    goto GetOpArgs;

FRET:
    Var3P = Var2P = NULL;	// Ret value is on stack already!
    goto DoFRetSimple;

FRET_V:
    V2 = *CP++;
    Var3P = NULL;
    OpAddrP = &&DoFRetVV;	// Looks at OpCP to see if FRET_VV
    goto GetV2Arg;

FRET_K:
    K4 = (Int8)*CP++;
    goto DoFRetK;

FRET_VV:
    V2 = *CP++; V3 = *CP++;
    OpAddrP = &&DoFRetVV;	// Looks at OpCP to see if FRET_VV
    goto GetV3V2Arg;

FYLD_VV:
    V2 = *CP++; V3 = *CP++;
    RunP->PC = CP - CPStart;	// Next Op is return address   
    OpAddrP = &&DoFYldVV;
    goto GetV3V2Arg;

FRES_V:
    V2 = *CP++;
    RunP->PC = CP - CPStart;			// Next Op is return address   
    OpAddrP = &&DoFResV;
    goto GetV2Arg;
    
FREL_V:
    V2 = *CP++;
    OpAddrP = &&DoFRelV;
    goto GetV2Arg;

FGET_V:
    V1 = *CP++;
    IsVVK = IsVKV = 1;
    OpAddrP = &&DoFGetV;
    goto GetOpArgs;

FCALL_VK2:
    ResType = 0;
    V1 = *CP++; I4 = *(Uns16 *)CP; CP += 2;
    RunP->PC = CP - CPStart;			// Next Op is return address   
    goto DoFCall;

UFCAL_VK2:
    ResType = 1;
    V1 = *CP++; I4 = *(Uns16 *)CP; CP += 2;
    RunP->PC = CP - CPStart;			// Next Op is return address   
    goto DoFCall;

SCALL_IK2:
    ResType = 0;
    V1 = *CP++; I4 = *(Uns16 *)CP; CP += 2;
    RunP->PC = CP - CPStart;			// Next Op is return address   
    goto DoSCall;

USCAL_IK2:
    ResType = 1;
    V1 = *CP++; I4 = *(Uns16 *)CP; CP += 2;
    RunP->PC = CP - CPStart;			// Next Op is return address   
    goto DoSCall;

UNPKI:
    ResType = 0;
    goto DoUNPK;

UNPKO:
    ResType = 1;
    goto DoUNPK;
    
AINI_V:
    V3 = *CP++;
    goto DoAIni;

ISET_VVV:
    V1 = *CP++, V2 = *CP++, V3 = *CP++;
    OpAddrP = &&DoISetVVV;
    // V1 is *NOT* getting set, needs special handling!
    goto GetV3V2Arg;

IREF_VV:
    V2 = *CP++, V3 = *CP++;
    Res = 1;		// Populate missing Elt/Pair
    OpAddrP = &&DoIRef;
    goto GetV3V2Arg;

ZREF_VV:
    V2 = *CP++, V3 = *CP++;
    Res = 0;		// DO NOT populate missing Elt/Pair
    OpAddrP = &&DoIRef;
    goto GetV3V2Arg;

IREF_VK2:
    V2 = *CP++, K4 = *(Uns16 *)CP; CP += 2;
    Res = 1;		// Populate missing Elt/Pair
    Var3P = NULL;	// Signal to DoIRef!
    OpAddrP = &&DoIRef;
    goto GetV2Arg;

ZREF_VK2:
    V2 = *CP++, K4 = *(Uns16 *)CP; CP += 2;
    Res = 0;		// DO NOT populate missing Elt/Pair
    Var3P = NULL;	// Signal to DoIRef!
    OpAddrP = &&DoIRef;
    goto GetV2Arg;

IGET_VVV:
    V1 = *CP++, V2 = *CP++, V3 = *CP++;
    OpAddrP = &&DoIGet;
    goto GetOpArgs;

IGET_VVK:
    V1 = *CP++, V2 = *CP++, K4 = *CP++;
    IsVVK = 1;		// GetOpArgs resets IsVVK
    Var3P = NULL;	// Signal to DoIGet     
    OpAddrP = &&DoIGet;
    goto GetOpArgs;

IMAP:
    ResType = 0;
    goto DoMap;

KMAP:
    ResType = 1;
    goto DoMap;

// ****************************************************

GetV3V2Arg:
    if (V3) {
	SC_SAFE_CHECK(V3 < RunP->VarCount, SC_BADRUN_Var);
	Var3P = CVarArr[V3];
    } else {
	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
	Var3P = SC_RUNSTACK_POP(VMP);
    }
GetV2Arg:
    if (V2) {
	SC_SAFE_CHECK(V2 < RunP->VarCount, SC_BADRUN_Var);
	Var2P = CVarArr[V2];
    } else {
	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
	Var2P = SC_RUNSTACK_POP(VMP);
    }
    goto *OpAddrP;


GetOpArgs:
    // V2 and V3 GIVE value...  they are R-value... 000 means POP from Stak
    if (IsVVK)
	IsVVK = 0;			// Always reset for next time!
    else {
	if (V3) {
	    SC_SAFE_CHECK(V3 < RunP->VarCount, SC_BADRUN_Var);
	    Var3P = CVarArr[V3];
	} else {
	    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
	    Var3P = SC_RUNSTACK_POP(VMP);
	}
    }

    if (IsVKV)
	IsVKV = 0;
    else {
	if (V2) {
	    SC_SAFE_CHECK(V2 < RunP->VarCount, SC_BADRUN_Var);
	    Var2P = CVarArr[V2];
	} else {
	    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
	    Var2P = SC_RUNSTACK_POP(VMP);
	}
    }

    // V1 GETS value... it is L-value... 000 means PUSH onto stack
    if (V1) {
	if (V1 == 0xFF) {
	    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
	    // A Ptr to a VEntry is on the stack!
	    Var1P = SC_RUNSTACK_POP(VMP);
	    SC_SAFE_CHECK(Var1P->ValueType == SC_VAL_PTR, SC_BADRUN_Type);
	    Var1P = (SC_VarVPointer)Var1P->ValueP;
	    SC_SAFE_CHECK(Var1P->Type <= SC_ENTRY_PAIR, SC_BADRUN_Type);	// Array cell or PAIR
	    // DO NOT DeRef Var1P here.... do after assigning to Var1P!  Can purge if ORPHAN REFR!
	    // SC_VARDEREF(VMP, Var1P);
	} else {
	    SC_SAFE_CHECK(V1 < RunP->VarCount, SC_BADRUN_Var);
	    Var1P = CVarArr[V1];
	}
	
	// Var1 will get a NEW value soon... so DeRef its existing value.
	SC_VARDEREFCOND(VMP, Var1P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var1P->ValueP);
	    
    } else
	Var1P = SC_RUNSTACK_PUSH(VMP);

    goto *OpAddrP;

// ****************************************************

DoMathVVV:
    // Ptr args will cause runtime errors, but should still DeRef properly.
    SC_VARDEREFCOND(VMP, (V3 == 0) && (Var3P->ValueType == SC_VAL_PTR), (SC_VarVPointer)Var3P->ValueP);
    SC_VARDEREFCOND(VMP, (V2 == 0) && (Var2P->ValueType == SC_VAL_PTR), (SC_VarVPointer)Var2P->ValueP);

    // Most ops need Int or Flt... BUT EQ/NEQ don't care!
    Val3 = (Int64)Var3P->ValueP;
    Val2 = (Int64)Var2P->ValueP;
    if (SubIsEQ && ((Var3P->ValueType > SC_VAL_FLT) || (Var2P->ValueType > SC_VAL_FLT))) {
    
	if (SubOp == SC_RUN_EQ)
	    Res = (Val3 == Val2) && (Var3P->ValueType == Var2P->ValueType);
	else
	    Res = (Val3 != Val2) || (Var3P->ValueType != Var2P->ValueType);
        Var1P->ValueType = SC_VAL_INT;
	Var1P->ValueP = (void *)Res;

    } else {
	SC_SAFE_CHECK(Var3P->ValueType <= SC_VAL_FLT, SC_BADRUN_Type);
	SC_SAFE_CHECK(Var2P->ValueType <= SC_VAL_FLT, SC_BADRUN_Type);
	SubOp += Var3P->ValueType;		// Add 1 if Float
	SubOp += Var2P->ValueType << 1;		// Add 2 if Float

	SubRetP = &&RET_MathVVV;
	    goto *SubOpTable[SubOp];		// 4 variations for each
	RET_MathVVV:

	Var1P->ValueType = ResType;
	Var1P->ValueP = (void *)Res;
    }
    SubIsEQ = 0;				// Reset!!
    SC_VARDEREFCOND(VMP, (V1 == 0xFF), Var1P);	// Do here to avoid ORPHAN REFR problem
    SC_RRCF_DISPATCH_OP;

DoMathVVK:
    // Ptr args will cause runtime errors, but should still DeRef properly.
    SC_VARDEREFCOND(VMP, (V2 == 0) && (Var2P->ValueType == SC_VAL_PTR), (SC_VarVPointer)Var2P->ValueP);            	        

    Val2 = (Int64)Var2P->ValueP;
    if (SubIsEQ && (Var2P->ValueType > SC_VAL_FLT)) {

	Var1P->ValueType = SC_VAL_INT;
	Res = (SubOp == SC_RUN_EQ) ? 0 : 1;
	Var1P->ValueP = (void *)Res;

    } else {
	SC_SAFE_CHECK(Var2P->ValueType <= SC_VAL_FLT, SC_BADRUN_Type);
	SubOp += Var2P->ValueType << 1;		// Add 2 if Float

	SubRetP = &&RET_MathVVK;
	    goto *SubOpTable[SubOp];		// 4 variations for each
	RET_MathVVK:

	Var1P->ValueType = ResType;
	Var1P->ValueP = (void *)Res;
    }
    SubIsEQ = 0;				// Reset!!
    SC_VARDEREFCOND(VMP, (V1 == 0xFF), Var1P);	// Do here to avoid ORPHAN REFR problem    
    SC_RRCF_DISPATCH_OP;

DoMathVKV:
    // Ptr args will cause runtime errors, but should still DeRef properly.
    // SubCheckType is NOT touched for these!
    SC_VARDEREFCOND(VMP, (V3 == 0) && (Var3P->ValueType == SC_VAL_PTR), (SC_VarVPointer)Var3P->ValueP);            	    

    Val3 = (Int64)Var3P->ValueP;
    SC_SAFE_CHECK(Var3P->ValueType <= SC_VAL_FLT, SC_BADRUN_Type);
    SubOp += Var3P->ValueType;			// Add 1 if Float

    SubRetP = &&RET_MathVKV;
	goto *SubOpTable[SubOp];		// 4 variations for each
    RET_MathVKV:

    Var1P->ValueType = ResType;
    Var1P->ValueP = (void *)Res;

    SC_VARDEREFCOND(VMP, (V1 == 0xFF), Var1P);	// Do here to avoid ORPHAN REFR problem    
    SC_RRCF_DISPATCH_OP;    

DoMathsss:
    // Dest is PUSH on stack, 2 args already on.
    // Pop just 1 arg, recycle the 2nd instead of pop-pop-push.
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 2), SC_BADRUN_Stack);

    Var3P = SC_RUNSTACK_POP(VMP);
    Var2P = SC_RUNSTACK_GETNTH(VMP, 1);
    Val3 = (Int64)Var3P->ValueP;
    Val2 = (Int64)Var2P->ValueP;
    if (SubIsEQ && ((Var2P->ValueType > SC_VAL_FLT) || (Var3P->ValueType > SC_VAL_FLT))) {
    
	if (SubOp == SC_RUN_EQ)
	    Res = (Val3 == Val2) && (Var3P->ValueType == Var2P->ValueType);
	else
	    Res = (Val3 != Val2) || (Var3P->ValueType != Var2P->ValueType);
        Var2P->ValueType = SC_VAL_INT;
	Var2P->ValueP = (void *)Res;

    } else {
    
	SC_SAFE_CHECK(Var3P->ValueType <= SC_VAL_FLT, SC_BADRUN_Type);
	SC_SAFE_CHECK(Var2P->ValueType <= SC_VAL_FLT, SC_BADRUN_Type);
	SubOp += Var3P->ValueType;		// Add 1 if FLT
	SubOp += Var2P->ValueType << 1;		// Add 2 if FLT
	
	SubRetP = &&RET_Mathsss;
	    goto *SubOpTable[SubOp];
	RET_Mathsss:

	Var2P->ValueType = ResType;
	Var2P->ValueP = (void *)Res;
    }
    SubIsEQ = 0;				// Reset!!
    SC_RRCF_DISPATCH_OP;

DoIdnt:
    // Both V2 and V3 could be popped!
    SC_VARDEREFCOND(VMP, (V3 == 0) && (Var3P->ValueType == SC_VAL_PTR) && Var3P->ValueP, (SC_VarVPointer)Var3P->ValueP);
    SC_VARDEREFCOND(VMP, (V2 == 0) && (Var2P->ValueType == SC_VAL_PTR) && Var2P->ValueP, (SC_VarVPointer)Var2P->ValueP);

    // Handles A = (B === C) and A = (B !== C) depending on ResType;
    // ResType was set to 0 for Pos logic, 1 for Neg logic...!!
    Res = (((Var2P->ValueType == SC_VAL_PTR) && (Var2P->ValueP == NULL)) ||
	   ((Var3P->ValueType == SC_VAL_PTR) && (Var3P->ValueP == NULL))) ? 0 : 1;
    Res &= (Var2P->ValueType == Var3P->ValueType) && (Var2P->ValueP == Var3P->ValueP);
    Res ^= ResType;

    Var1P->ValueType = SC_VAL_INT;
    Var1P->ValueP = (void *)Res;

    SC_VARDEREFCOND(VMP, (V1 == 0xFF), Var1P);	// Do here to avoid ORPHAN REFR problem
    SC_RRCF_DISPATCH_OP;

DoMovVV:
    // No need to DeRef Var2P if coming from the stack, it will be stashed.
    // But do need an INC of RefCount IFF not from stack... as it is now duplicated!

    // (A = B) Means V1 gets value of V2.
    //
    // NOTE:  Will never call with V1 and V2 both being 000... so no danger of
    //	      Var1P and Var2P stepping on one another!
    Var1P->ValueType = Var2P->ValueType;
    Var1P->ValueP = Var2P->ValueP;
    SC_VARREFCOND(V2 && (Var1P->ValueType == SC_VAL_PTR), (SC_VarVPointer)Var1P->ValueP);

    SC_VARDEREFCOND(VMP, (V1 == 0xFF), Var1P);	// Do here to avoid ORPHAN REFR problem    
    SC_RRCF_DISPATCH_OP;

DoMovVK2:
    // (A = Val2)
    Var1P->ValueType = SC_VAL_INT;
    Var1P->ValueP = (void *)Val2;

    SC_VARDEREFCOND(VMP, (V1 == 0xFF), Var1P);	// Do here to avoid ORPHAN REFR problem    
    SC_RRCF_DISPATCH_OP;

DoPopV:
    // Pops value into V, a valid variable--not 0x00 or 0xFF.
    //
    // NOTE: Ptrs get RefCount INC when pushed on stack, so no need to Ref/DeRef
    //	     when popping into a variable!
    SC_SAFE_CHECK(V1 && (V1 < RunP->VarCount), SC_BADRUN_Var);
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
    
    Var2P = SC_RUNSTACK_POP(VMP);		// Get value first!
    Var1P->ValueType = Var2P->ValueType;
    Var1P->ValueP = Var2P->ValueP;

    SC_RRCF_DISPATCH_OP;

DoPutV:
    // VALUE is top of stack, REFER below it.
    // Pop both (conceptually).
    // Push VALUE on stack and into REFER,
    //
    // NOTE: Var1P is the REFER... Var, Arr elt, or Dict pair.
    //	     But it may belong to a fresh Arr/Dict that is NOT
    //	     stashed... so will purge if DEREF here!! ***+***
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 2), SC_BADRUN_Stack);
    Var2P = SC_RUNSTACK_POP(VMP);		// Get value first!

	// Var3P gets new top.  (REFER.. it is the DESTINATION.)
	Var3P = SC_RUNSTACK_GETNTH(VMP, 1);
	SC_SAFE_CHECK(Var3P->ValueType == SC_VAL_PTR && Var3P->ValueP, SC_BADRUN_Type);
	Var1P = (SC_VarVPointer)Var3P->ValueP;
	// Var1P is a Var, Arr Elt, or Dict Pair.
	// DO NOT DEREF here... not yet!! ***+***
	SC_SAFE_CHECK(Var1P->Type <= SC_ENTRY_PAIR, SC_BADRUN_Type);
	// Var1 will get a NEW value soon... so DeRef its existing value.
	SC_VARDEREFCOND(VMP, Var1P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var1P->ValueP);

    // Push Value back on stack.... Var3P points to where REFER was.
    // Var2 is the VALUE being stored.
    // Var1 is the target of REFER (from the stack)
    // Var3 is the new TOP of stack
    Var3P->ValueType = Var1P->ValueType = Var2P->ValueType;
    Var3P->ValueP = Var1P->ValueP = Var2P->ValueP;
    // Var2 got stored *AND* placed back on stack, so +1 RefCount
    SC_VARREFCOND(Var2P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var2P->ValueP);
    
    // Finally done with Var1P... DeRef for being off stack. ***+***
    SC_VARDEREF(VMP, Var1P);
    SC_RRCF_DISPATCH_OP;

DoGetV:
    // REFER is top of stack.  Replace with its VALUE.
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
    Var1P = SC_RUNSTACK_GETNTH(VMP, 1);
    SC_SAFE_CHECK(Var1P->ValueType == SC_VAL_PTR && Var1P->ValueP, SC_BADRUN_Type);
    Var2P = (SC_VarVPointer)Var1P->ValueP;
    SC_SAFE_CHECK(Var2P->Type <= SC_ENTRY_PAIR, SC_BADRUN_Type);
    // Var2P is the VALUE.
    Var1P->ValueType = Var2P->ValueType;
    Var1P->ValueP = Var2P->ValueP;
    SC_VARREFCOND(Var2P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var2P->ValueP);
    // Finally done with REFER!  It could purge immediately...
    SC_VARDEREF(VMP, Var2P);
    SC_RRCF_DISPATCH_OP;
    
DoPopN:
    // PopN disposes of top N on the stack... they need DeReferencing!
    if (K4) {
	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, K4), SC_BADRUN_Stack);
	#if SC_GC_REFCOUNT
	{
	    Int32		I = K4;
	    Var1P = VMP->StackP + VMP->StackTop;
	    do {
		Var1P--;
		SC_VARDEREFCOND(VMP, Var1P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var1P->ValueP);
	    } while (--I);
	}
	#endif

	SC_RUNSTACK_POPN(VMP, K4);
    }
    SC_RRCF_DISPATCH_OP;

DoPOP1:
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
    Var1P = SC_RUNSTACK_GETNTH(VMP, 1);
    SC_VARDEREFCOND(VMP, Var1P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var1P->ValueP);
    Var3P = SC_RUNSTACK_POP(VMP);
    SC_RRCF_DISPATCH_OP;

DoSWAP:
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 2), SC_BADRUN_Stack);
    Var1P = SC_RUNSTACK_GETNTH(VMP, 1);
    Var2P = SC_RUNSTACK_GETNTH(VMP, 2);
    Var3P = Var1P + 1;

    // Var3P points to NEW stack elt, used as temp!
    Var3P->ValueType = Var1P->ValueType;
	Var1P->ValueType = Var2P->ValueType;
	Var2P->ValueType = Var3P->ValueType;
    Var3P->ValueP = Var1P->ValueP;
	Var1P->ValueP = Var2P->ValueP;
	Var2P->ValueP = Var3P->ValueP;
    SC_RRCF_DISPATCH_OP;

DoPSH1:
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
    Var2P = SC_RUNSTACK_GETNTH(VMP, 1);
    Var1P = SC_RUNSTACK_PUSH(VMP);
    Var1P->ValueType = Var2P->ValueType;
    Var1P->ValueP = Var2P->ValueP;
    SC_VARREFCOND(Var1P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var1P->ValueP);
    SC_RRCF_DISPATCH_OP;

DoPushV:
    // Pushes value of V onto stack. V must be a valid variable, not 0x00 or 0xFF.
    SC_SAFE_CHECK(V2 && (V2 < RunP->VarCount), SC_BADRUN_Var);
    Var2P = CVarArr[V2];

    Var1P = SC_RUNSTACK_PUSH(VMP);
    Var1P->ValueType = Var2P->ValueType;
    Var1P->ValueP = Var2P->ValueP;
    SC_VARREFCOND(Var1P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var1P->ValueP);
    SC_RRCF_DISPATCH_OP;

DoPushK:
    Var1P = SC_RUNSTACK_PUSH(VMP);
    Var1P->ValueType = SC_VAL_INT;
    Var1P->ValueP = (void *)(Int64)K4;
    SC_RRCF_DISPATCH_OP;

DoPushIntV:
    // Turns value of V into INT, then pushes on stack.
    // Var2P is V, Var1P is place on stack!
    // If already on stack, Var1P and Var2P will be the same!
    if (V2) {
	SC_SAFE_CHECK(V2 < RunP->VarCount, SC_BADRUN_Var);
	Var2P = CVarArr[V2];
	Var1P = SC_RUNSTACK_PUSH(VMP);
    } else {
	// Already on stack
	Var1P = Var2P = SC_RUNSTACK_GETNTH(VMP, 1);
    }

    // Convert FLT, otherwise INT and PTR are just INT64!
    // Stash in Val2 first, then change Var1P, in case same as Var2P!
    if (Var2P->ValueType == SC_VAL_FLT)
	Val2 = (Int64)*(double *)&Var2P->ValueP;
    else {
	Val2 = (Int64)Var2P->ValueP;
	SC_VARDEREFCOND(VMP, Var2P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var2P->ValueP);
    }
    Var1P->ValueType = SC_VAL_INT;
    Var1P->ValueP = (void *)Val2;

    SC_RRCF_DISPATCH_OP;

DoPushInt:
    SC_SAFE_CHECK(I4 < VMP->RunScriptP->KonstCount, SC_BADRUN_Index);
    Var1P = SC_RUNSTACK_PUSH(VMP);
    Var1P->ValueType = SC_VAL_INT;
    Var1P->ValueP = (void *)VMP->RunScriptP->KonstADP->Data[I4];
    SC_RRCF_DISPATCH_OP;

DoPushFltV:
    // Turns value of V into FLT, then pushes on stack.
    // Var2P is V, Var1P is place on stack!
    // If already on stack, Var1P and Var2P will be the same!
    if (V2) {
	SC_SAFE_CHECK(V2 < RunP->VarCount, SC_BADRUN_Var);
	Var2P = CVarArr[V2];
	Var1P = SC_RUNSTACK_PUSH(VMP);
    } else {
	// Already on stack!
	Var1P = Var2P = SC_RUNSTACK_GETNTH(VMP, 1);
    }
	
    // FLT, just copies; INT and PTR are both Int64, then converted!
    // Stash in ResFlt first, then change Var1P, in case same as Var2P!
    if (Var2P->ValueType == SC_VAL_FLT)
	ResFlt = *(double *)&Var2P->ValueP;		// Get the double out of ValueP
    else {
	ResFlt = (double)(Int64)Var2P->ValueP;		// Convert to flt
	SC_VARDEREFCOND(VMP, Var2P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var2P->ValueP);
    }
    Var1P->ValueType = SC_VAL_FLT;
    Var1P->ValueP = (void *)*(Int64 *)&ResFlt;

    SC_RRCF_DISPATCH_OP;
    
DoPushFlt:
    SC_SAFE_CHECK(I4 < VMP->RunScriptP->KonstCount, SC_BADRUN_Index);
    Var1P = SC_RUNSTACK_PUSH(VMP);
    Var1P->ValueType = SC_VAL_FLT;
    Var1P->ValueP = (void *)VMP->RunScriptP->KonstADP->Data[I4];
    SC_RRCF_DISPATCH_OP;

DoPushGlob:
    // Pushes value of indexed global
    SC_SAFE_CHECK(I4 < VMP->RunScriptP->GlobCount, SC_BADRUN_Index);
    Var2P = VMP->RunScriptP->GlobP->GArr[I4];

    // Inc RefCount...
    SC_VARREFCOND(Var2P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var2P->ValueP);
    
    Var1P = SC_RUNSTACK_PUSH(VMP);
    Var1P->ValueType = Var2P->ValueType;
    Var1P->ValueP = Var2P->ValueP;
    SC_RRCF_DISPATCH_OP;

DoPushStr:
    // Pushes ptr to StrHPointer
    SC_SAFE_CHECK(I4 < VMP->RunScriptP->StringCount, SC_BADRUN_Index);
    Var1P = SC_RUNSTACK_PUSH(VMP);
    Var1P->ValueType = SC_VAL_PTR;
    Var1P->ValueP = (void *)VMP->RunScriptP->StringADP->Data[I4];
    // Inc RefCount... Definite pointer, not conditional!
    SC_VARREF((SC_VarVPointer)Var1P->ValueP);
    SC_RRCF_DISPATCH_OP;

DoPushRef:
    // Push a Ptr (Ref) to the Var
    SC_SAFE_CHECK(V2 && (V2 < RunP->VarCount), SC_BADRUN_Var);
    Var2P = CVarArr[V2];
    // Inc RefCount... Definite pointer, not conditional!
    SC_VARREF(Var2P);
    
    Var1P = SC_RUNSTACK_PUSH(VMP);
    Var1P->ValueType = SC_VAL_PTR;
    Var1P->ValueP = Var2P;
    SC_RRCF_DISPATCH_OP;

DoPushGRef:
    // Push a Ptr (Ref) to the indexed Global
    SC_SAFE_CHECK(I4 < VMP->RunScriptP->GlobCount, SC_BADRUN_Index);
    Var2P = VMP->RunScriptP->GlobP->GArr[I4];

    // Inc RefCount... definite pointer, not conditional!
    SC_VARREF(Var2P);
    
    Var1P = SC_RUNSTACK_PUSH(VMP);
    Var1P->ValueType = SC_VAL_PTR;
    Var1P->ValueP = Var2P;
    SC_RRCF_DISPATCH_OP;

DoRegSet:
    // Alias REG *TO* Global... will have same storage!
    // V1 points to same VarVEntry as Global[I4]... fast and efficient!
    //
    // NOTE: RefCount is not Inc/Dec for registers!
    SC_SAFE_CHECK(V1 && (V1 <= RunP->RCount), SC_BADRUN_Var);
    SC_SAFE_CHECK(I4 < VMP->RunScriptP->GlobCount, SC_BADRUN_Index);
    CVarArr[V1] = VMP->RunScriptP->GlobP->GArr[I4];
    SC_RRCF_DISPATCH_OP;

DoVSet:
    // Alias Local Var *TO* Source (Global/ArrElt/DictPair) REF on RunStack.
    // Local Var will get the same storage location as the Source REF.
    SC_SAFE_CHECK(V1 && (V1 > RunP->RCount) && (V1 < RunP->VarCount), SC_BADRUN_Var);
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
    SC_VARDEREF(VMP, CVarArr[V1]);	// Prev VarVRecord loses a ref!

    Var2P = SC_RUNSTACK_POP(VMP);
    SC_SAFE_CHECK((Var2P->ValueType == SC_VAL_PTR) && Var2P->ValueP, SC_BADRUN_Stack);
    CVarArr[V1] = (SC_VarVPointer)Var2P->ValueP;
    // RefCount -1 for POP off RunStack, +1 for getting stored... so no change!
    SC_RRCF_DISPATCH_OP;
    
DoBranchT:
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
    Var1P = SC_RUNSTACK_POP(VMP);
    // Only NULL and Int 0 are FALSE... everything else is TRUE!
    if (Var1P->ValueP) {
	CP += K4;
	SC_SAFE_CHECK((CP >= CPStart) && (CP < CPEnd), SC_BADRUN_Addr);
    } else
	CP += 4;
    SC_RRCF_DISPATCH_OP;
    
DoBranchF:
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
    Var1P = SC_RUNSTACK_POP(VMP);
    // Only NULL and Int 0 are FALSE... everything else is TRUE!
    if (Var1P->ValueP)
	CP += 4;
    else {
	CP += K4;
	SC_SAFE_CHECK((CP >= CPStart) && (CP < CPEnd), SC_BADRUN_Addr);
    }
    SC_RRCF_DISPATCH_OP;

DoLogVV:
    // Ptr arg will cause runtime errors, but should still DeRef properly.
    SC_VARDEREFCOND(VMP, (V2 == 0) && (Var2P->ValueType == SC_VAL_PTR), (SC_VarVPointer)Var2P->ValueP);            	        

    // GetOpArgs will take care to DeRef V1 if it had previous value!
    if (Var2P->ValueType > SC_VAL_FLT) SC_SysError(SC_STR_ErrorNotNumber);
    // ???? Use Runtime error instead?
    goto *SubOpTable[SubOp];
    // Will NOT return here... finish and dispatch from SubOp!!

DoSwitch:
    // I4 is SwitchId.  V1 selects which Case!
    // CaseP->Value is absolute Byte address, not PC relative.
    SC_SAFE_CHECK(CBP->CaseDictP, SC_BADRUN_Dict);
    if (V1) {
	SC_SAFE_CHECK(V1 < RunP->VarCount, SC_BADRUN_Var);
	Var1P = CVarArr[V1];
    } else {
	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
	Var1P = SC_RUNSTACK_POP(VMP);
    }
    // If no CaseP is found, do NOTHING!  The next ByteCode after this SWITCH
    // will be a JMP to the DEFAULT case.
    {
	SC_CaseHPointer		CaseP;

	if ((CaseP = SC_DictGetCase(VMP->RunScriptP->StringADP, CBP->CaseDictP, (Uns64)Var1P->ValueP, I4, Var1P->ValueType)))
	    CP = CPStart + CaseP->Value;
    }
    SC_RRCF_DISPATCH_OP;

DoFMak:
    // Make FClos for Function (ID is I4) and store in V1.
    // NOTE: ClosRecord *looks* like an Entry... is of type SC_CHUNK_CLOS!
    Var1P->ValueType = SC_VAL_PTR;
    Var1P->ValueP = SC_RunMakeClos(VMP, I4, NULL);

    SC_VARDEREFCOND(VMP, (V1 == 0xFF), Var1P);	// Do here to avoid ORPHAN REFR problem    
    SC_RRCF_DISPATCH_OP;

DoFBeg:
    // Initializes loaded function for run...  does nothing now!
    // Might later decide to add debug info/state... so leave in.
    SC_RRCF_DISPATCH_OP;

DoFEnd:
    // Does a "RETURN 0" if no explicit return.
    Var1P = SC_RUNSTACK_PUSH(VMP);
    Var1P->ValueType = SC_VAL_INT;
    Var1P->ValueP = (void *)0LL;
    SC_RunZapCurRunRecord(VMP);
    goto RUN_RESUME;

DoFSub:
    // S is submitting its RCP to M.
    // Will *SET* RCP->ResumeP.
    RunP->PC = CP - CPStart;
    {
	SC_RunPointer	CurRunP = VMP->RunScriptP->CurRunP;
	SC_RCPointer	RCP = CurRunP->RCP;

	if (! CurRunP->CallerP) {
	    RunErr = SC_RUN_TOPYIELD;
	    goto RUN_ERROR;
	}

	if (RCP && (RCP->Flags & SC_RC_SUBMITFLAG)) {
	    RunErr = SC_RUN_SUBMITAGAIN;
	    goto RUN_ERROR;
	}

	CurRunP->Flags |= SC_RUN_NOTRECOPT;	// Has RCP, do NOT tail-rec optimize
	if (RCP) {
	    if (RCP->Flags & SC_RC_SUBMITFLAG) {
		RunErr = SC_RUN_SUBMITAGAIN;
		goto RUN_ERROR;
	    }
	} else
	    RCP = SC_RunGetRC(VMP);

	RCP->Flags |= SC_RC_SUBMITFLAG;
	RCP->ResumeP = CurRunP;
	// May include IMAP stack elts... will get restored with Resume
	SC_RunMakeStackCopy(VMP, RCP);

	// But get rid of IMAP stack elts (if any) before SUBMIT returns.
	SC_RUNSTACK_FLUSH_DEREF(VMP, CurRunP->InitStackTop);
	// Push Co-routine's RCP on stack, so gets "returned" to caller.
	Var1P = SC_RUNSTACK_PUSH(VMP);
	Var1P->ValueType = SC_VAL_PTR;
	Var1P->ValueP = RCP;
	SC_VARREF(RCP);			// +1 because on stack!

	VMP->RunScriptP->CurRunP = CurRunP->CallerP;
	
	#if SC_DEBUG_DISP_EXEC_OP
	    if (VMP->RunScriptP->CurRunP)
		printf("<<------------ Submit to Code: %04d (S:%d)\n",
			((SC_CodeBlockPointer)VMP->RunScriptP->CurRunP->VarArr[0])->CodeId,
			VMP->StackTop);
	#endif
    }
    goto RUN_RESUME;

DoFRetVV:
    // V3 is RCP (THIS or higher RunP) or RCP is 0 to mean THIS RunP.
    // V2 is value to be returned.
    // One or both (V2 and V3) may be on RunStack.
    //
    // FRET MUST restore RunStack to INIT state when FUNC was called!
    // (IMAP may have placed items on the stack which should be flushed!)
    {
	SC_RunPointer	RetRunP;
	SC_RCPointer	RCP;
	Int16		NotFound;

	RetRunP = VMP->RunScriptP->CurRunP;		// Defaults to current RunP
	if (Var3P) {
	    // Check RCP... set RetRunP with it.
	    if (Var3P->ValueType == SC_VAL_PTR) {
		RCP = (SC_RCPointer)Var3P->ValueP;
		// DeRef RCP if it was popped from the stack!  (Should not make RefCount 0!)
		SC_VARDEREFCOND(VMP, V3 == 0, (SC_VarVPointer)RCP);
		if ((RCP->Type != SC_ENTRY_RC) ||	// Not an RCP
		    ! ((RetRunP = RCP->RunP))) {	// RCP->RunP is NULL
		    RunErr = SC_RUN_BADRCP;
		    goto RUN_ERROR;
		}
	    } else if ((Var3P->ValueType != SC_VAL_INT) || (Var3P->ValueP != 0LL)) {
		RunErr = SC_RUN_BADARG;
		goto RUN_ERROR;
	    }

	    // Will return to caller... no possibility of resume!
	    RCP->ResumeP = NULL;
	}

	// DoFYldVV does all this too...  ** MATCH sections! **
	if (Var2P) {
	    SC_RUNSTACK_FLUSH_DEREF(VMP, RetRunP->InitStackTop);	// DeRef popped elts!
	    Var1P = SC_RUNSTACK_PUSH(VMP);
	    // Var2P could be from the stack... and in the right slot (If NOT IMap)!
	    if (Var1P != Var2P) {
		Var1P->ValueType = Var2P->ValueType;
		Var1P->ValueP = Var2P->ValueP;
	    }
	    // No need for extra Ref Inc if it came from the stack... did not Dec when popped.
	    SC_VARREFCOND(V2 && (Var2P->ValueType == SC_VAL_PTR), (SC_VarVPointer)Var2P->ValueP);

	}

	// Zap all the way up to AND including RetRunP... so will end up
	// in its CallerP.  (Will end script if zapping TOP func.)
	
	do {
	    NotFound = (VMP->RunScriptP->CurRunP != RetRunP);
	    SC_RunZapCurRunRecord(VMP);
	} while (VMP->RunScriptP->CurRunP && NotFound);

	if (NotFound) {
	    RunErr = SC_RUN_BADRCP;
	    goto RUN_ERROR;
	}
	#if SC_DEBUG_DISP_EXEC_OP
	    if (VMP->RunScriptP->CurRunP)
		printf("<<------------ Return to Code: %04d (S:%d)\n",
			((SC_CodeBlockPointer)VMP->RunScriptP->CurRunP->VarArr[0])->CodeId,
			VMP->StackTop);
	#endif	
    }
    goto RUN_RESUME;

DoFRetSimple:
    // No args, return value will be on stack.  Broken out for faster exec.
    // But IMap may have added extra elts, so zap them, then restore Top!
    {
	Int32	RunStackTop = VMP->RunScriptP->CurRunP->InitStackTop;

	if (VMP->StackTop > RunStackTop + 1) {			// Have IMap elts!
	    Var2P = SC_RUNSTACK_POP(VMP);			// Stask Top 
	    SC_RUNSTACK_FLUSH_DEREF(VMP, RunStackTop);		// Get rid of IMap elts.
	    Var1P = SC_RUNSTACK_PUSH(VMP);			// New top
	    Var1P->ValueType = Var2P->ValueType;		// Copy stashed value
	    Var1P->ValueP = Var2P->ValueP;
	}
	SC_RunZapCurRunRecord(VMP);
	goto RUN_RESUME;
    }

DoFYldVV:
    // Func Yield
    // V2 has the value to be returned.
    // V3 has RCP or 0 (No OutOf), tells how high up to return from.
    // Run (sequence) is kept in heap, not zapped... so can be Resumed.
    // Current RunStack is also stashed in RCP.
    //
    // NOTE:  RCP is optional in this call, will be 0 on stack UNLESS
    //	      doing a non-local Yield!

    {
	SC_RunPointer	RetRunP;
	SC_RCPointer	RCP = NULL;
	
	RetRunP = VMP->RunScriptP->CurRunP;		// Defaults to current RunP
	// Check RCP... Set RetRunP with it.
	if (Var3P->ValueType == SC_VAL_PTR) {
	    RCP = (SC_RCPointer)Var3P->ValueP;
	    // DeRef RCP if it was popped from stack!  (Should not make RefCount 0!)
	    SC_VARDEREFCOND(VMP, V3 == 0, (SC_VarVPointer)RCP);
	    if ((RCP->Type != SC_ENTRY_RC) ||		// Not an RCP
		!((RetRunP = RCP->RunP)) ||		// ASSIGN, RCP->RunP is NULL... was released
		!(RCP->Flags & SC_RC_SUBMITFLAG) ||	// Was not already submitted!
		RCP->ResumeP) {				// Was already yielded
		RunErr = SC_RUN_BADRCP;
		goto RUN_ERROR;
	    }

	} else if ((Var3P->ValueType != SC_VAL_INT) || (Var3P->ValueP != 0LL)) {
	    RunErr = SC_RUN_BADARG;
	    goto RUN_ERROR;
	}

	if (RCP == NULL)
	    RCP = RetRunP->RCP;
	if (! (RCP && (RCP->Flags & SC_RC_SUBMITFLAG))) {
	    Uns8	NotFound;
	    
	    // Do a RETURN instead of a YIELD!!  (** Match to DoFRetVV section above! **)
	    if (RCP) RCP->ResumeP = NULL;
	    SC_RUNSTACK_FLUSH(VMP, RetRunP->InitStackTop);
	    Var1P = SC_RUNSTACK_PUSH(VMP);
	    Var1P->ValueType = Var2P->ValueType;
	    Var1P->ValueP = Var2P->ValueP;
	    SC_VARREFCOND(V2 && (Var1P->ValueType == SC_VAL_PTR), (SC_VarVPointer)Var1P->ValueP);
	    // Zap all the way up to AND including RetRunP.  (Script will end if zapping TOP func!)
	    do {
		NotFound = (VMP->RunScriptP->CurRunP != RetRunP);
		SC_RunZapCurRunRecord(VMP);
	    } while (VMP->RunScriptP->CurRunP && NotFound);
	    if (NotFound) {
		RunErr = SC_RUN_BADRCP;
		goto RUN_ERROR;
	    }
	    #if SC_DEBUG_DISP_EXEC_OP
		if (VMP->RunScriptP->CurRunP)
		    printf("<<------------ Return to Code: %04d (S:%d)\n",
			    ((SC_CodeBlockPointer)VMP->RunScriptP->CurRunP->VarArr[0])->CodeId,
			    VMP->StackTop);
	    #endif
	    goto RUN_RESUME;
	}

	// Set the ResumeP...  RetRunP *WILL* have an RCP if SUBMIT was called!
	RCP->ResumeP = VMP->RunScriptP->CurRunP;	// Resume HERE
	// Stack is cleared of args for FYld... save it.
	SC_RunMakeStackCopy(VMP, RCP);			// DO NOT Inc Ref, transfered from RunStack!
	
	// Flush stack to where RetRunP was called
	// If Var2P was from stack, it will *STILL* be there...
	SC_RUNSTACK_FLUSH(VMP, RetRunP->InitStackTop);		// Do **NOT** DeRef, Made a copy!
	// Push the return value
	Var1P = SC_RUNSTACK_PUSH(VMP);
	if (Var1P != Var2P) {
	    Var1P->ValueType = Var2P->ValueType;
	    Var1P->ValueP = Var2P->ValueP;
	}
	// No need for extra Ref Inc if it came from the stack... did not Dec when popped.
	SC_VARREFCOND(V2 && (Var1P->ValueType == SC_VAL_PTR), (SC_VarVPointer)Var1P->ValueP);

	// NOTE:  Cannot YIELD above MAIN (even starting lower with an RCP)
	//	  because impossible to SUBMIT, so RCP->ResumeP will be null.
	VMP->RunScriptP->CurRunP = RetRunP->CallerP;

	#if SC_DEBUG_DISP_EXEC_OP
	    if (VMP->RunScriptP->CurRunP)
		printf("<<------------ Yield to Code: %04d (S:%d)\n",
			((SC_CodeBlockPointer)VMP->RunScriptP->CurRunP->VarArr[0])->CodeId,
			VMP->StackTop);
	#endif	

    }
    goto RUN_RESUME;

DoFResV:
    // V2 has RCP that is being resumed... could be Var or on RunStack
    {
	SC_RCPointer	RCP;

	if (Var2P->ValueType == SC_VAL_PTR) {
	    RCP = (SC_RCPointer)Var2P->ValueP;
	    SC_VARDEREFCOND(VMP, V2 == 0, (SC_VarVPointer)RCP);	// Popped from stack!
	    if ((RCP->Type != SC_ENTRY_RC) || !RCP->RunP || !RCP->ResumeP) {
		RunErr = SC_RUN_BADRCP;
		goto RUN_ERROR;
	    }
	} else {
	    RunErr = SC_RUN_BADARG;
	    goto RUN_ERROR;
	}

        // Get ready to "call" yielded function... so update RunP->InitStackTop!!
	RCP->RunP->InitStackTop = VMP->StackTop;
	RCP->RunP->CallerP = RunP;
	// Now PUSH its stashed stack here... will zap the StackCopyP too.
	// Will not Ref/DeRef what was in the StackCopy, getting transfered to RunStack.
	if (RCP->StackCopyP) SC_PushStackCopy(VMP, RCP);
	// Jump into the RESUME function...
	VMP->RunScriptP->CurRunP = RCP->ResumeP;
	RCP->ResumeP = NULL;				// Cannot resume again until Yield!

	#if SC_DEBUG_DISP_EXEC_OP
	    if (VMP->RunScriptP->CurRunP)
		printf("------------>> Resume Code: %04d (S:%d)\n",
			((SC_CodeBlockPointer)VMP->RunScriptP->CurRunP->VarArr[0])->CodeId,
			VMP->StackTop);
	#endif	
    }
    goto RUN_RESUME;

DoFRelV:
    // Release RCP
    {
	SC_RCPointer	RCP;

	if ((Var2P->ValueType != SC_VAL_PTR) ||
	    ! ((RCP = (SC_RCPointer)Var2P->ValueP))) {		// Assign
	    RunErr = SC_RUN_BADRCP;
	    goto RUN_ERROR;
	}
	SC_VARDEREFCOND(VMP, V2 == 0, (SC_VarVPointer)RCP);	// Popped from stack!
	if ((RCP->Type != SC_ENTRY_RC) || ! RCP->RunP || ! RCP->ResumeP) {
	    RunErr = SC_RUN_BADRCP;
	    goto RUN_ERROR;
	}

	// Start from RCP->ResumeP and zap RunRecords up to RCP->RunP.
	SC_RunReleaseRunChain(VMP, RCP->ResumeP, RCP->RunP->CallerP);
    }
    SC_RRCF_DISPATCH_OP;    

DoFRetK:
    // FRET must restore RunStack to INIT state when this FUNC was called.
    // (Something like IMAP may have placed items on the stack which should be flushed!)
    SC_RUNSTACK_FLUSH_DEREF(VMP, RunP->InitStackTop);	// DeRef popped elts
    Var1P = SC_RUNSTACK_PUSH(VMP);
    Var1P->ValueType = SC_VAL_INT;
    Var1P->ValueP = (void *)(Int64)K4;
    SC_RunZapCurRunRecord(VMP);
    goto RUN_RESUME;

DoFGetV:
    // Gets the RCP from CurRunP if there... otherwise creates one.
    RunP->Flags |= SC_RUN_NOTRECOPT;
    Var1P->ValueType = SC_VAL_PTR;
    Var1P->ValueP = SC_RunGetRC(VMP);

    // If V1 is 0xFF, then REFR and was NOT DeRef by GetOpArgs coming here, already +1!!
    SC_VARREFCOND((V1 != 0xFF), (SC_RCPointer)Var1P->ValueP);	// Stashed in Var or on Stack!
    SC_RRCF_DISPATCH_OP;

DoFCall:
    // Function (FClos) is in V1, I4 gives IN+OUT count
    if (V1) {
	SC_SAFE_CHECK(V1 < RunP->VarCount, SC_BADRUN_Var);
	Var1P = CVarArr[V1];
    } else {
	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
	Var1P = SC_RUNSTACK_POP(VMP);
    }

    if ((Var1P->ValueType != SC_VAL_PTR) ||
	    !((Var1P = (SC_VarVPointer)Var1P->ValueP)) ||	// Assignment... not NULL
	    (Var1P->Type != SC_CHUNK_CLOS)) {
	RunErr = SC_RUN_NOCLOS;
	goto RUN_ERROR;
    }
    if (V1) SC_VARREF(Var1P);		// Extra +1 for Clos so will not Purge if TailRecursion!
					// Will already have this if came on Stack!    

    // DO NOT TAIL RECURSE if TOP func or CallGet was called on this function!!
    // (Heuristic: The RCP is likely stashed and will be used for "Return From Call"!!)
    if (! (VMP->RunScriptP->CurRunP->Flags & SC_RUN_NOTRECOPT)) {

	// Tail Recurse (RunZapCurRunRecord) *IFF*
	//   a) FCall followed by immediate [FRET] (Returns value from FCall on stack)
	//   b) FCall followed by immediate [FRet_V Pop] (Ditto) or
	//   c) FCall followed by [Pop_V N][FRet_V N] where N is a 'caged' Local (NOT ESCAPED)
	// In the mean time, ignore NoOp and follow JMPs (out of if blocks, etc.)
	//
	// V3 means "SeenPopV" and V2 captures local var (N) used in Pop_V Op.

	Uns8*	P = CP;

	V3 = 0;
	while (1) {
	    if (*P == SC_NOOP_CODE) {
		P += 1;
		continue;
	    }
	    if (*P == SC_JMP_K3_CODE) {
		K4 = *(Int32 *)P >> 8;
		P += K4;
		SC_SAFE_CHECK((P >= CPStart) && (P < CPEnd), SC_BADRUN_Addr);
		continue;
	    }
	    if (*P == SC_POP_V_CODE) {
		P += 1;
		if (V3 || (*P == 0) || (CVarArr[V2 = *P])->Flags & SC_VAR_ESCFLAG)
		    break;					// NOT tail recursion
		V3 = 1; P += 1;
		continue;
	    }
	    if (*P == SC_FRET_CODE) {
		if (! V3)	// Not seen Pop_V
		    SC_RunZapCurRunRecord(VMP);			// Tail Recurse: Cur RunRec goes away!!

	    } else if (*P == SC_FRET_V_CODE) {
		P += 1;
		if (V3) {	// Seen Pop_V
		    if (*P == V2)
			SC_RunZapCurRunRecord(VMP);		// Tail Recurse: Cur RunRec goes away!!
		} else		// NOT seen Pop_V
		    if (*P == 0)
			SC_RunZapCurRunRecord(VMP);		// Tail Recurse: Cur RunRec goes away!!
	    }
	    break;	// NOT tail recursion
	}
    }

    SC_RunMakeRunRecord(VMP, (SC_ClosPointer)Var1P, I4, ResType);	// 0 for normal, 1 for UnPack  
    SC_VARDEREF(VMP, Var1P);
    
    goto RUN_START;

DoSCall:
    // Sys function index (SIndex) is V1, I4 gives IN+OUT count
    SC_SAFE_CHECK(V1 < VMP->CFPCount, SC_BADRUN_Var);
    SC_ExecAbortCount = -1;		// Prime it for abort immediately after!!
    if ((RunErr = ((SC_SysFuncPointer)VMP->CFPADP->Data[V1])(VMP, I4))) goto RUN_ERROR;
    SC_RRCF_DISPATCH_OP;

DoUPS:
    // Called before assembling arglist (INPUT/OUTPUT) for an FCall/SCall that has
    // an UnPack operator.  Will init UPSADP/UPSTop in RunRecord.
    if (RunP->UPSADP == NULL) {
	RunP->UPSADP = SC_ArrAllocNewData(VMP, &RunP->UPSADP, SC_MINARRLEN, 0);
	RunP->UPSTop = 1;
	RunP->UPSADP->Data[0] = 0LL;		// Fast way of zeroing first record

    } else {
	SC_UPSPointer	UPSP;
	
	// Need to expand UPSADP?
	if (RunP->UPSTop > RunP->UPSADP->SlotCount * SC_UPSEltsPerSlot)
	    RunP->UPSADP = SC_ArrResizeData(VMP, RunP->UPSADP, RunP->UPSADP->SlotCount * 2, 0);
	// Clear THIS UPS entry + Increment UPSTop
	UPSP = (SC_UPSPointer)RunP->UPSADP->Data + RunP->UPSTop++;		// Inc
	UPSP->InCount = UPSP->OutCount = 0;

	if (RunP->UPSTop == 0) {
	    // Rolled over... overflow!
	    RunErr = SC_RUN_ERRTOOMANYARG;
	    goto RUN_ERROR;
	}
    }
    SC_RRCF_DISPATCH_OP;

DoUNPK:
    // Assumes RunP has *ALREADY* allocated a new UPS entry!!
    // Entry to UnPack is already on stack...
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1) && RunP->UPSADP && RunP->UPSTop, SC_BADRUN_Stack);
    Var1P = SC_RUNSTACK_GETNTH(VMP, 1);
    if (Var1P->ValueType == SC_VAL_PTR) {
	// Just leave it on the stack IFF not Arr or Dict!  (Nothing to UnPack!)
	SC_UPSPointer	UPSP = (SC_UPSPointer)RunP->UPSADP->Data + RunP->UPSTop - 1;
	Uns16 *		CountP = (ResType) ? &UPSP->OutCount : &UPSP->InCount;
	
	Var2P = (SC_VarVPointer)Var1P->ValueP;
	if (Var2P->Type == SC_ENTRY_ARR) {
	    // Adding in EVERY entry... even NULL ones!
	    // Must fully map Arr if unpacking to OUT list.
	    SC_ArrDataPointer	ADP = (*(SC_ArrVPointer)Var2P).ArrDataP;
	    SC_VarVPointer *	VPP = (SC_VarVPointer *)&ADP->Data[0];

	    // @ arg itself counts as 1, so add SlotCount-1 more!
	    // Also check enough room on stack!
	    //
	    // NOTE: Rare, but Possible to have ARR ADP with 0 SlotCount!!
	    if (ADP->SlotCount) {
		if ((ADP->SlotCount > SC_CGMAXVARCOUNT) ||
		    (((*CountP += ADP->SlotCount - 1)) > SC_CGMAXVARCOUNT) ||
		    (VMP->StackTop + ADP->SlotCount - 1 > VMP->StackCount)) {
		    RunErr = SC_RUN_ERRTOOMANYARG;
		    goto RUN_ERROR;
		}

		// Push (ADP->SlotCount - 1) times!!
		// Only works if no (goto RUN_ERROR) in this loop.
		VMP->StackTop += ADP->SlotCount - 1;

		I4 = 0;
		while (1) {
		    if (ResType) {
			// Adding to OUTPUT list... fully "map" the array!
			if (! *VPP) *VPP = SC_VarEntryNew(VMP);		// INT-0 with 1 Ref
			Var1P->ValueType = SC_VAL_PTR;
			Var1P->ValueP = *VPP;
			SC_VARREF(*VPP);

		    } else {
			// Adding to INPUT list... use INT-0 value where array is not "mapped".
			if (*VPP) {
			    Var1P->ValueType = (*VPP)->ValueType;
			    Var1P->ValueP = (*VPP)->ValueP;
			    SC_VARREFCOND(Var1P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var1P->ValueP);
			} else {
			    Var1P->ValueType = SC_VAL_INT;
			    Var1P->ValueP = 0LL;
			}
		    }

		    ++VPP, ++I4, ++Var1P;
		    if (I4 >= ADP->SlotCount) break;
		}
	    } else {
		// 0 SlotCount... just replace with INT-0
		if (ResType) {
		    // Output List
		    Var1P->ValueType = SC_VAL_PTR;
		    Var1P->ValueP = SC_VarEntryNew(VMP);
		} else {
		    Var1P->ValueType = SC_VAL_INT;
		    Var1P->ValueP = 0LL;
		}
	    }
	    SC_VARDEREF(VMP, Var2P);		// Array itself is No longer on the stack!

	} else if (Var2P->Type == SC_ENTRY_DICT) {
	    SC_PairHPointer	PairP;

	    // Want Pairs on stack in the order enterred...
	    // So first PUSH N on the stack, then start from TOP and go back on the stack!
	    // Check enough room on stack... already has one slot that will be re-used.
	    if ((*(SC_DictVPointer)Var2P).PairCount) {
		if (((*(SC_DictVPointer)Var2P).PairCount > SC_CGMAXVARCOUNT) ||
		    (((*CountP += (*(SC_DictVPointer)Var2P).PairCount - 1)) > SC_CGMAXVARCOUNT) ||
		    (VMP->StackTop + (*(SC_DictVPointer)Var2P).PairCount - 1 > VMP->StackCount)) {
		    RunErr = SC_RUN_ERRTOOMANYARG;
		    goto RUN_ERROR;
		}

		VMP->StackTop += (*(SC_DictVPointer)Var2P).PairCount - 1;	// Push PairCount - 1;
		Var1P = SC_RUNSTACK_GETNTH(VMP, 1);				// New Top;

		PairP = (*(SC_DictVPointer)Var2P).FirstPairP;
		while (PairP) {
		    if (ResType) {
			// Adding to OUTPUT list
			Var1P->ValueType = SC_VAL_PTR;
			Var1P->ValueP = PairP;
			SC_VARREF(PairP);
		    } else {
			Var1P->ValueType = PairP->ValType;
			Var1P->ValueP = PairP->ValueP;
			SC_VARREFCOND(Var1P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var1P->ValueP);
		    }

		    PairP = PairP->NextPairP;
		    --Var1P;			// Going backwards down stack.
		}
	    } else {
		// No Pairs... replace with INT-0!
		if (ResType) {
		    // Output List
		    Var1P->ValueType = SC_VAL_PTR;
		    Var1P->ValueP = SC_VarEntryNew(VMP);

		} else {
		    Var1P->ValueType = SC_VAL_INT;
		    Var1P->ValueP = 0LL;
	        }
	    }
	    SC_VARDEREF(VMP, Var2P);		// Dict itself is no longer on stack!
	    
	} else if (ResType) {
	    // Var2P is not number.. but could be String, RC, etc.
	    // Do not want to put it on OUT list directly!
	    Var1P->ValueP = Var3P = SC_VarEntryNew(VMP);
	    Var3P->ValueType = SC_VAL_PTR;
	    Var3P->ValueP = Var2P;
	}

    } else if (ResType) {
	// Don't want to put simple value on OUT list... Int/Flt
	Var2P = SC_VarEntryNew(VMP);
	Var2P->ValueType = Var1P->ValueType;
	Var2P->ValueP = Var1P->ValueP;

	// Put this new var (Var2) on stack instead!
	Var1P->ValueType = SC_VAL_PTR;
	Var1P->ValueP = Var2P;
    }
    SC_RRCF_DISPATCH_OP;

DoANew:
    // V1 gets new Array... Len is on stack, 0 means default
    // GetOpArgs will DeRef old V1 content.
    //
    // Pop the Len first, then call GetOpArgs as it might PUSH!
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
    Var2P = SC_RUNSTACK_POP(VMP);
    // DeRef just in case, even though will have a runtime error!
    SC_VARDEREFCOND(VMP, Var2P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var2P->ValueP);
    SC_SAFE_CHECK(Var2P->ValueType == SC_VAL_INT, SC_BADRUN_Type);
    I4 = (Uns32)(Uns64)Var2P->ValueP;
    if (! I4) I4 = SC_MINARRLEN;		// Default Len
    
    V1 = *CP++;
    IsVVK = IsVKV = 1;
    OpAddrP = &&DoANew_Body; goto GetOpArgs;
  DoANew_Body:

    Var3P = (SC_VarVPointer)SC_ArrMakeNewArr(VMP, I4);
    // Comes in with RefCount == 1, will get stashed in RunStack or Var.
    Var1P->ValueType = SC_VAL_PTR;
    Var1P->ValueP = Var3P;
    SC_RRCF_DISPATCH_OP;
    
DoAIni:
    // Arr[Index] = V3
    // Stack has P to ArrVEntry + Index before call... V3 is value.
    // V3 may also be on stack... will be popped.
    //
    // NOTE:  Assumes this is INIT, compiler sets Arr length correctly!
    //	      Also Arr slot is NULL until initialized.
    if (V3) {
	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 2), SC_BADRUN_Stack);
	SC_SAFE_CHECK(V3 < RunP->VarCount, SC_BADRUN_Var);
	Var1P = CVarArr[V3];				// Temporary!
	SC_VARREFCOND(Var1P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var1P->ValueP);
    } else {
	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 3), SC_BADRUN_Stack);
	Var1P = SC_RUNSTACK_POP(VMP);			// Temporary!
	// Refcount will be unchanged: -1 for coming off stack, +1 for Array elt!
    }
    // Assumes ARRAY INIT, so indexed slot was previously empty!
    // Array slot will pt to New VEntry... comes in with RefCount of 1.
    // (Do not call SC_RunInitVar instead, it would do another VarRef on Var1P)
    Var3P = SC_VarEntryNew(VMP);
    Var3P->ValueType = Var1P->ValueType;
    Var3P->ValueP = Var1P->ValueP;
    SC_DEBUG_VARDISP(VMP, Var3P, "Alloc ArrElt");	    	    

    // Leave ArrVEntryP and Index on stack... Change Index in Place.
    // No need for RefCount Inc/Dec...
    Var2P = SC_RUNSTACK_GETNTH(VMP, 1);	// Index
    SC_SAFE_CHECK(Var2P->ValueType == SC_VAL_INT, SC_BADRUN_Type);
    Val2 = (Uns64)Var2P->ValueP;
    Var2P->ValueP = (void *)(Val2 + 1);		// INCREMENT For Next cycle!
    
    Var1P = SC_RUNSTACK_GETNTH(VMP, 2);		// P to ArrVEntry
    SC_SAFE_CHECK(Var1P->ValueType == SC_VAL_PTR, SC_BADRUN_Type);
    Var1P = (SC_VarVPointer)Var1P->ValueP;	// ArrayVPtr
    SC_SAFE_CHECK(Var1P->Type = SC_ENTRY_ARR, SC_BADRUN_Type);
    {
	SC_ArrDataPointer	ADP = (*(SC_ArrVPointer)Var1P).ArrDataP;

	SC_SAFE_CHECK(Val2 < ADP->SlotCount, SC_BADRUN_Index);
	ADP->Data[Val2] = Var3P;
    }
    SC_RRCF_DISPATCH_OP;

DoDNew:
    // V1 gets new Dict... Len is on stack, 0 means default
    // GetOpArgs will DeRef old Var1P contents.
    //
    // Pop the Len first, then call GetOpArgs, as it might PUSH.
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 1), SC_BADRUN_Stack);
    Var2P = SC_RUNSTACK_POP(VMP);
    // DeRef just in case, even though will have a runtime error!
    SC_VARDEREFCOND(VMP, Var2P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var2P->ValueP);
    SC_SAFE_CHECK(Var2P->ValueType == SC_VAL_INT, SC_BADRUN_Type);
    I4 = (Uns32)(Uns64)Var2P->ValueP;

    V1 = *CP++;
    IsVVK = IsVKV = 1;
    OpAddrP = &&DoDNew_Body; goto GetOpArgs;
  DoDNew_Body:

    Var3P = (SC_VarVPointer)SC_DictCreateNewDict(VMP, I4);
    // Comes in with RefCount == 1, will get stashed in RunStack or Var.
    Var1P->ValueType = SC_VAL_PTR;
    Var1P->ValueP = Var3P;
    SC_RRCF_DISPATCH_OP;
    
DoDIni:
    // StacK has Ptr to DictVEntry, then Key, then Value before call.
    // Leave the Ptr on the stack, pop the other two off.
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 3), SC_BADRUN_Stack);
    // None of the args will need a RefCount change
    Var3P = SC_RUNSTACK_POP(VMP);		// Value: From Stack to stashed in PairP->ValueP
    Var2P = SC_RUNSTACK_POP(VMP);		// Key: From Stack to 'stashed' in PairP->KeyP
    Var1P = SC_RUNSTACK_GETNTH(VMP, 1);		// P to Dict, gets pushed back

    SC_SAFE_CHECK(Var1P->ValueType == SC_VAL_PTR, SC_BADRUN_Type);
    Var1P = (SC_VarVPointer)Var1P->ValueP;
    SC_SAFE_CHECK(Var1P->Type == SC_ENTRY_DICT, SC_BADRUN_Type);
    
    // Compiler will check for duplicate KEYs in init sequence...  BUT can still
    // get init dupes from Var contents and FuncCalls!  (It can clog up Dicts!)
    // (Design: does NOT check for dupe Keys when stashing new pairs... faster.)
    // ????
    {
	SC_PairHPointer		PairP = SC_BigEntryNew(VMP);

	PairP->KeyType = Var2P->ValueType;
	PairP->ValType = Var3P->ValueType;
	PairP->KeyP = Var2P->ValueP;
	PairP->ValueP = Var3P->ValueP;
	SC_SAFE_CHECK(! SC_DictGetPair((SC_DictVPointer)Var1P, (SC_VarVPointer)Var2P->ValueP, Var2P->ValueType), SC_BADRUN_Index);
	SC_DictStashNewPair(VMP, (SC_DictVPointer)Var1P, PairP);
    }
    SC_RRCF_DISPATCH_OP;

DoISetVVV:
    // V1[V2] =& V3 for ARRAY only.
    // V1 -> Array
    // V2 -> Index
    // V3 --> REF being aliased!  Could be Var, ArrElt, or Pair!
    //
    // Any and all args can be on stack.... order: Stack BOT-|... V1 V2 V3 |>TOP
    //
    // Must handle V1 here... GetV3V2Arg was called instead of GetOpArgs.
    // Has already popped V3 and V2 off the stack...

    if (V1) {
	SC_SAFE_CHECK(V1 < RunP->VarCount, SC_BADRUN_Var);
	Var1P = CVarArr[V1];		// Should be a Var
	if (Var1P) Var1P = (SC_VarVPointer)Var1P->ValueP;

    } else {
	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP,1), SC_BADRUN_Stack);
	Var1P = SC_RUNSTACK_POP(VMP);
	SC_SAFE_CHECK(Var1P->ValueType == SC_VAL_PTR, SC_BADRUN_Var);
	Var1P = (SC_VarVPointer)Var1P->ValueP;
	// Got Inc when pushed, do not DEC until done!
    }

    if (Var1P && (Var1P->Type == SC_ENTRY_ARR)) {
	SC_ArrDataPointer	ADP = (*(SC_ArrVPointer)Var1P).ArrDataP;
	SC_VarVPointer *	EltPP;

	// Get the Index... could be a var or a pushed value.
	// Nothing to DeRef here... If pushed, it was an INT!
	if (Var2P->ValueType != SC_VAL_INT) {
	    RunErr = SC_RUN_BADARG;
	    goto RUN_ERROR;
	}
	I4 = (Uns32)(Uns64)Var2P->ValueP;

	// Expand array?  
	if (I4 >= ADP->SlotCount) {
	    SC_SAFE_CHECK(I4 < SC_MAXARRLEN, SC_BADRUN_Index);
	    (*(SC_ArrVPointer)Var1P).ArrDataP = ADP = SC_ArrResizeData(VMP, ADP, I4 + 1, 0);
	}
	EltPP = (SC_VarVPointer *)&ADP->Data[I4];		// Stash in *EltPP... done with Indexing
	SC_VARDEREFCOND(VMP, *EltPP, *EltPP);			// DeRef previous Arr Elt, if there was one

	// Ready to re-assign *EltPP
	if (V3) {
	    *EltPP = Var3P;
	    SC_VARREF(Var3P);
	} else {
	    SC_SAFE_CHECK((Var3P->ValueType == SC_VAL_PTR) && Var3P->ValueP, SC_BADRUN_Type);
	    *EltPP = (SC_VarVPointer)Var3P->ValueP;
	    // Was already Inc when put on stack!
	}
	SC_VARDEREFCOND(VMP, !V1, Var1P);			// Done with Array

    } else  {	// Not ARRAY!
	RunErr = SC_RUN_BADARG;
	goto RUN_ERROR;
    }

    SC_RRCF_DISPATCH_OP;

DoIRef:
    // Push &V2[V3] for Array and Push &V2.V3 for Dict.  (VV and VK2 variants)
    // Calls GetV3V2Arg (GetV2Arg if K) not GetOpArgs!  Basically pushes a Ptr
    // to the Var Elt or Dict Pair on the stack.
    //
    // Now (after V2.2) Pairs hav their own RefCount!!  So can Inc RefCount for
    // Arr Elt or Dict Pair... (This used to be an issue!)

    // Var2P is getting popped off stack... but **DO NOT DEREF** up here, as it
    // will purge if its RefCount was 1... i.e. it was a newly minted Array
    // and not stored anywhere else!!

    // V2 is var *OR* POP off of stack
    if (Var2P->ValueType == SC_VAL_PTR)
	Var2P = (SC_VarVPointer)Var2P->ValueP;

    // DO NOT PUSH (for Var1P) YET!!  Will go on error path if Var2P is wrong!!
    // Uses Res as 'Populate' predicate... IREF or ZREF
    if (Var2P->Type == SC_ENTRY_ARR) {
	SC_ArrDataPointer	ADP = (*(SC_ArrVPointer)Var2P).ArrDataP;
	SC_VarVPointer *	EltPP = NULL;

	if (Var3P) {		// Otherwise it is VK2
	    // DeRef if popped off stack... even though should not be PTR
	    SC_VARDEREFCOND(VMP, (V3 == 0) && (Var3P->ValueType == SC_VAL_PTR), (SC_VarVPointer)Var3P->ValueP);
	    SC_SAFE_CHECK(Var3P->ValueType == SC_VAL_INT, SC_BADRUN_Type);
	    K4 = (Int32)(Uns64)Var3P->ValueP;
	}

	if (Res) {
	    // If ref is beyond array len, expand it!
	    if (K4 >= ADP->SlotCount) {
		SC_SAFE_CHECK(K4 < SC_MAXARRLEN, SC_BADRUN_Index);
		(*(SC_ArrVPointer)Var2P).ArrDataP = ADP = SC_ArrResizeData(VMP, ADP, K4 + 1, 0);
	    }

	    // if ref elt does not have an Entry (unused until now) create one for it!
	    EltPP = (SC_VarVPointer *)&ADP->Data[K4];
	    if (! *EltPP) {
		*EltPP = (SC_VarVPointer)SC_VarEntryNew(VMP);	// Has RefCount 1
		SC_DEBUG_VARDISP(VMP, *EltPP, "Alloc ArrElt");
	    }

	} else if (K4 < ADP->SlotCount)
	    EltPP = (SC_VarVPointer *)&ADP->Data[K4];

	// Result goes into Var1P... on stack.
	Var1P = SC_RUNSTACK_PUSH(VMP);
	Var1P->ValueType = SC_VAL_PTR;
	if (EltPP && *EltPP) {
	    Var1P->ValueP = *EltPP;
	    SC_VARREF(*EltPP);		// Arr Cell Var Will get Dec when popped
	} else
	    Var1P->ValueP = NULL;	// Only for ZREF

    } else if (Var2P->Type == SC_ENTRY_DICT) {
	SC_PairHPointer		PairP = NULL;

	if (Var3P) {
	    PairP = SC_DictGetPair((SC_DictVPointer)Var2P, (SC_VarVPointer)Var3P->ValueP, Var3P->ValueType);
	    if (PairP) {
		// Key was already there, so DeRef if Var3P was popped off stack
		SC_VARDEREFCOND(VMP, (V3 == 0) && (Var3P->ValueType == SC_VAL_PTR), (SC_VarVPointer)Var3P->ValueP);
	    } else if (Res) {
		// Create a new Pair...
		PairP = SC_BigEntryNew(VMP);
		PairP->ValueP = NULL;
		PairP->ValType = SC_VAL_INT;
		PairP->KeyP = Var3P->ValueP;
		PairP->KeyType = Var3P->ValueType;
		SC_DictStashNewPair(VMP, (SC_DictVPointer)Var2P, PairP);
		// Inc RefCount for new KeyP... only if NOT from Stack!
		SC_VARREFCOND(V3 && (Var3P->ValueType == SC_VAL_PTR), (SC_VarVPointer)Var3P->ValueP);
	    }
	   
	} else {
	    // VVK version... 
	    PairP = SC_DictGetPair((SC_DictVPointer)Var2P, (SC_VarVPointer)(Int64)K4, SC_VAL_INT);
	    if (Res && !PairP) {
		PairP = SC_BigEntryNew(VMP);
		PairP->ValueP = NULL;
		PairP->ValType = SC_VAL_INT;
		PairP->KeyP = (void *)(Int64)K4;
		PairP->KeyType = SC_VAL_INT;
		SC_DictStashNewPair(VMP, (SC_DictVPointer)Var2P, PairP);
	    }
	}

	// Result goes into Var1P... on stack.
	Var1P = SC_RUNSTACK_PUSH(VMP);
	Var1P->ValueType = SC_VAL_PTR;
	Var1P->ValueP = PairP;
	// REF to Pair... has own RefCount now (after V2.2)!
	if (PairP) SC_VARREF(PairP);

    } else {
	RunErr = SC_RUN_NOINDEX;
	goto RUN_ERROR;
    }

    SC_VARDEREFCOND(VMP, ! V2, Var2P);	
    SC_RRCF_DISPATCH_OP;

DoIGet:
    // V1 = V2[V3] for Array and V1 = V2.V3 for Dict.
    // V2 is var *OR* POP off of stack...
    // In case off stack...DEREF at very end... will purge if RefCount is 1!!
    if (Var2P->ValueType == SC_VAL_PTR)
	Var2P = (SC_VarVPointer)Var2P->ValueP;

    if (Var2P->Type == SC_ENTRY_ARR) {
	SC_ArrDataPointer	ADP = (*(SC_ArrVPointer)Var2P).ArrDataP;
	SC_VarVPointer *	EltPP;

	if (Var3P) {		// Otherwise VVK version
	    // DeRef if popped off stack... even though should not be PTR
	    SC_VARDEREFCOND(VMP, (V3 == 0) && (Var3P->ValueType == SC_VAL_PTR), (SC_VarVPointer)Var3P->ValueP);
	    SC_SAFE_CHECK(Var3P->ValueType == SC_VAL_INT, SC_BADRUN_Type);
	    K4 = (Int32)(Int64)Var3P->ValueP;
	}
	SC_SAFE_CHECK(K4 >= 0, SC_BADRUN_Type);
	
	if (K4 >= ADP->SlotCount) {
	    // Out of range
	    Var1P->ValueType = SC_VAL_INT;
	    Var1P->ValueP = 0LL;

	} else {
	    EltPP = (SC_VarVPointer *)&ADP->Data[K4];
	    if (! *EltPP) {
		// No value
		Var1P->ValueType = SC_VAL_INT;
		Var1P->ValueP = NULL;
	    } else {
		Var1P->ValueP = (*EltPP)->ValueP;
		Var1P->ValueType = (*EltPP)->ValueType;
	    }
	}

    } else if (Var2P->Type == SC_ENTRY_DICT) {
	SC_PairHPointer		PairP;

	// If Var3P == NULL, it is the VVK version... 
	if (Var3P) {
	    PairP = SC_DictGetPair((SC_DictVPointer)Var2P, (SC_VarVPointer)Var3P->ValueP, Var3P->ValueType);
	    // DeRef Var3P if it came off the stack.
	    SC_VARDEREFCOND(VMP, (V3 == 0) && (Var3P->ValueType == SC_VAL_PTR), (SC_VarVPointer)Var3P->ValueP);
	} else
	    PairP = SC_DictGetPair((SC_DictVPointer)Var2P, (SC_VarVPointer)(Int64)K4, SC_VAL_INT);
	    
	if (PairP) {
	    Var1P->ValueType = PairP->ValType;
	    Var1P->ValueP = PairP->ValueP;
	} else {
	    Var1P->ValueType = SC_VAL_INT;
	    Var1P->ValueP = NULL;
	}

    } else {
	RunErr = SC_RUN_NOINDEX;
	goto RUN_ERROR;
    }

    // Inc RefCount if Var1P is a Ptr!  (It will be stored on stack or in var)
    SC_VARREFCOND(Var1P->ValueType == SC_VAL_PTR, (SC_VarVPointer)Var1P->ValueP);
    SC_VARDEREFCOND(VMP, ! V2, Var2P);

    SC_VARDEREFCOND(VMP, (V1 == 0xFF), Var1P);	// Do here to avoid ORPHAN REFR problem
    SC_RRCF_DISPATCH_OP;

DoMap:
    // RunStack has all args already pushed.
    // 4: KeyRef (0 if none)
    // 3: ValueRef (0 if none)
    // 2: Index (Starts with 0)
    // 1: Obj (thing being mapped)
    //
    // BEWARE: KeyRef, ValueRef, and Index **COULD** point to Vars (or Pairs).
    //	       These should all get Ref/DeRef as appropriate.
    //
    // A) When things end, these will be popped off and DeRef is automatically called!
    // B) Things *MAY* end when the code executes a BREAK mid sequence!!
    // C) Things WILL definitely end when the last elt/pair are processed.
    // D) Script may do all manner of strange things in body of MAP loop... things can purge!
    //
    // MapI:	Goes through ALL elts of an Array, sets KeyRef arg to numerical index.
    //		Goes through ALL cells of a Dict (HADP), sets KeyRef arg to numerical index.
    //
    // MapK:	Goes through non-null entries in Array's ADP, sets KeyRef to numerical index.
    //		Goes through all pairs of a Dict, sets KeyRef to Pair->Key!

    {
	SC_VarVPointer	ObjP, IndexP, VRefP, KRefP;
	SC_VarVPointer	ResP;
	Int32		Index;
	Int16		DidFail;

	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, 4), SC_BADRUN_Stack);
	ObjP = SC_RUNSTACK_GETNTH(VMP, 4);	// Obj being mapped, initially Stack Ptr
	IndexP = ObjP + 1;			// Current index, always a Stack Ptr
	VRefP = IndexP + 1;			// ValueRef
	KRefP = VRefP + 1;			// KeyRef

	// VRefP and KRefP initially point to stack entries... which point to Vars, or NULL!
	VRefP = (SC_VarVPointer)VRefP->ValueP;
	KRefP = (SC_VarVPointer)KRefP->ValueP;

	DidFail = 1;
        if (ObjP->ValueType < SC_VAL_PTR) {
	    // Int or Float... stored directly on stack
	    if (IndexP->ValueP == NULL) {	// Index is 0
		// Do an iteration IFF IndexP is 0, otherwise, done this already!
		if (KRefP) KRefP->ValueType = SC_VAL_INT, KRefP->ValueP = (void *)0LL;
		if (VRefP) VRefP->ValueType = ObjP->ValueType, VRefP->ValueP = ObjP->ValueP;
		IndexP->ValueP = (void *)1LL;
		DidFail = 0;
	    }
	    
	} else {
	    // Obj can be String, Arr, or Dict
	    ObjP = (SC_VarVPointer)ObjP->ValueP;
	    SC_SAFE_CHECK(ObjP, SC_BADRUN_Type);
	    switch (ObjP->Type) {
		case SC_ENTRY_ARR:
		    {
			SC_VarVPointer*	VPP;
			Uns32		MaxI;
			
			Index = *(Int64 *)&IndexP->ValueP;
			MaxI = (*(SC_ArrVPointer)ObjP).ArrDataP->SlotCount;			
			if (Index < MaxI) {

			    // KMAP skips over NULL ArrDataP ptrs!
			    VPP = (SC_VarVPointer *)((*(SC_ArrVPointer)ObjP).ArrDataP->Data + Index);
			    if (ResType == 1) {
			    
				while (Index < MaxI) {
				    if (*VPP) break;
				    ++Index, ++VPP;
				}
				IndexP->ValueP = (void*)(Int64)Index;
				if (Index >= MaxI) break;	// Exit this CASE
			    }
			
			    if (VRefP) {
				// DeRef old value, Ref new value... assuming its a Ptr!
				SC_VARDEREFCOND(VMP, VRefP->ValueType == SC_VAL_PTR, (SC_VarVPointer)VRefP->ValueP);
				if (*VPP) {
				    VRefP->ValueType = (*VPP)->ValueType, VRefP->ValueP = (*VPP)->ValueP;
				    SC_VARREFCOND(VRefP->ValueType == SC_VAL_PTR, (SC_VarVPointer)VRefP->ValueP);
				} else
				    VRefP->ValueType = SC_VAL_INT, VRefP->ValueP = (void *)0LL;
			    }
			    if (KRefP) KRefP->ValueType = SC_VAL_INT, KRefP->ValueP = (void *)(Int64)Index;
			    Index += 1;
			    IndexP->ValueP = (void *)(Int64)Index;
			    DidFail = 0;
			}
		    }
		    break;

		case SC_ENTRY_STR:
		    if (IndexP->ValueP == NULL) {
			if (KRefP) KRefP->ValueType = SC_VAL_INT, KRefP->ValueP = (void *)0LL;
			if (VRefP) VRefP->ValueType = SC_VAL_PTR, VRefP->ValueP = ObjP;
			IndexP->ValueP = (void *)1LL;
			DidFail = 0;
		    }
		    break;


		case SC_ENTRY_DICT:
		    // Danger:  Things will go bad if a Pair (stashed in IndexP) is zapped!  Currently DICT
		    //		does NOT free Pairs, unless growing the array, and only if Value is Ptr and
		    //		NULL.  The latter does NOT happen in scripting as PairP->Value can only be set
		    //		to INT-0, not PTR-0
		    if (ResType) {
			// MapK... just go down list of Pairs!
			SC_PairHPointer PairP;

			if (IndexP->ValueP == 0)
			    PairP = (*(SC_DictVPointer)ObjP).FirstPairP;
			else
			    PairP = (*(SC_PairHPointer)IndexP->ValueP).NextPairP;

			if (PairP) {
			    SC_VARDEREFCOND(VMP, IndexP->ValueP, IndexP->ValueP);	// Old Value
			    IndexP->ValueType = SC_VAL_PTR, IndexP->ValueP = PairP;
			    SC_VARREF(PairP);						// New Value
			    
			    if (KRefP) {
				SC_VARDEREFCOND(VMP, KRefP->ValueType == SC_VAL_PTR, (SC_VarVPointer)KRefP->ValueP);	// Old
				KRefP->ValueType = PairP->KeyType, KRefP->ValueP = PairP->KeyP;
				SC_VARREFCOND(PairP->KeyType == SC_VAL_PTR, (SC_VarVPointer)PairP->KeyP);		// New
			    }
			    if (VRefP) {
				SC_VARDEREFCOND(VMP, VRefP->ValueType == SC_VAL_PTR, (SC_VarVPointer)VRefP->ValueP);	// Old
				VRefP->ValueType = PairP->ValType, VRefP->ValueP = PairP->ValueP;
				SC_VARREFCOND(PairP->ValType == SC_VAL_PTR, (SC_VarVPointer)PairP->ValueP);
			    }
			    DidFail = 0;
			}
		    } else {
			// MapI!!  Traverse Dict like an array!
			Uns32		MaxI;
			SC_PairHPointer	PairP;

			// Index and KRefP are just numbers here... nothing to Ref/DeRef
			Index = *(Int64 *)&IndexP->ValueP;
			MaxI = (*(SC_DictVPointer)ObjP).HADP->SlotCount;
			if (Index < MaxI) {
			    if (KRefP) KRefP->ValueType = SC_VAL_INT, KRefP->ValueP = (void *)(Int64)Index;
			    if (VRefP) {
				PairP = (SC_PairHPointer)(*(SC_DictVPointer)ObjP).HADP->Data[Index];
				SC_VARDEREFCOND(VMP, VRefP->ValueType == SC_VAL_PTR, (SC_VarVPointer)VRefP->ValueP);	// Old
				if (PairP) {
				    VRefP->ValueType = PairP->ValType, VRefP->ValueP = PairP->ValueP;
				    SC_VARREFCOND(PairP->ValType == SC_VAL_PTR, (SC_VarVPointer)PairP->ValueP);		// New
				}
				else
				    VRefP->ValueType = SC_VAL_INT, VRefP->ValueP = (void *)(Int64)0;
			    }
			    IndexP->ValueP = (void *)(Int64)(Index+1);
			    DidFail = 0;
			}
		    }
		    break;

		default:
		    RunErr = SC_RUN_BADARG;
		    goto RUN_ERROR;
	    }
	}
	
	ResP = SC_RUNSTACK_PUSH(VMP);
	ResP->ValueType = SC_VAL_INT, ResP->ValueP = (DidFail) ? (void *)0LL : (void *)1LL;
    }
    SC_RRCF_DISPATCH_OP;
    
    // ******************************************************************
    // MathOps: 4 flavors:  Int-Int / Int-Flt / Flt-Int / Flt-Flt
    //		Stuffs result in Res and returns to SubRetP address.
	
    ADD00:	Res = Val2 + Val3;
		ResType = SC_VAL_INT; goto *SubRetP;
    ADD01:	ResFlt = *(double *)&Val3 + Val2; Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;
    ADD10:	ResFlt = *(double *)&Val2 + Val3; Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;
    ADD11:	ResFlt = *(double *)&Val2 + *(double *)&Val3; Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;

    SUB00:	Res = Val2 - Val3;
		ResType = SC_VAL_INT; goto *SubRetP;
    SUB01:	ResFlt = Val2 - *(double *)&Val3; Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;
    SUB10:	ResFlt = *(double *)&Val2 - Val3; Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;
    SUB11:	ResFlt = *(double *)&Val2 - *(double *)&Val3; Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;

    MUL00:	Res = Val2 * Val3;
		ResType = SC_VAL_INT; goto *SubRetP;
    MUL01:	ResFlt = *(double *)&Val3 * Val2; Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;
    MUL10:	ResFlt = *(double *)&Val2 * Val3; Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;
    MUL11:	ResFlt = *(double *)&Val2 * *(double *)&Val3; Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;

    DIV00:	Res = Val2 / Val3;
		ResType = SC_VAL_INT; goto *SubRetP;
    DIV01:	ResFlt = (double)Val2 / *(double *)&Val3; Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;
    DIV10:	ResFlt = *(double *)&Val2 / (double)Val3; Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;
    DIV11:	ResFlt = *(double *)&Val2 / *(double *)&Val3; Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;

    MOD00:	Res = Val2 % Val3;
		ResType = SC_VAL_INT; goto *SubRetP;
    MOD01:	ResFlt = fmod((double)Val2, *(double *)&Val3); Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;
    MOD10:	ResFlt = fmod(*(double *)&Val2, (double)Val3); Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;
    MOD11:	ResFlt = fmod(*(double *)&Val2, *(double *)&Val3); Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;	

    SHL00:	Res = (Val3 < 0) ? Val2 >> -Val3 : Val2 << Val3;	// C doesn't like neg shift
		ResType = SC_VAL_INT; goto *SubRetP;
    SHL01:	ResFlt = (double)Val2 * pow(2.0, *(double *)&Val3); Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;
    SHL10:	ResFlt = *(double *)&Val2 * pow(2.0, (double)Val3); Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;
    SHL11:	ResFlt = *(double *)&Val2 * pow(2.0, *(double *)&Val3); Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;

    SHR00:	Res = (Val3 < 0) ? Val2 << -Val3 : Val2 >> Val3;	// C doesn't like neg shift
		ResType = SC_VAL_INT; goto *SubRetP;
    SHR01:	ResFlt = (double)Val2 / pow(2.0, *(double *)&Val3); Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;
    SHR10:	ResFlt = *(double *)&Val2 / pow(2.0, (double)Val3); Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;
    SHR11:	ResFlt = *(double *)&Val2 / pow(2.0, *(double *)&Val3); Res = *(Int64 *)&ResFlt;
		ResType = SC_VAL_FLT; goto *SubRetP;

    LET00:	Res = Val2 < Val3;
		ResType = SC_VAL_INT; goto *SubRetP;
    LET01:	Res = *(double *)&Val3 > Val2;
		ResType = SC_VAL_INT; goto *SubRetP;
    LET10:	Res = *(double *)&Val2 < Val3;
		ResType = SC_VAL_INT; goto *SubRetP;
    LET11:	Res = *(double *)&Val2 < *(double *)&Val3;
		ResType = SC_VAL_INT; goto *SubRetP;

    LTQ00:	Res = Val2 <= Val3;
		ResType = SC_VAL_INT; goto *SubRetP;
    LTQ01:	Res = *(double *)&Val3 >= Val2;
		ResType = SC_VAL_INT; goto *SubRetP;
    LTQ10:	Res = *(double *)&Val2 <= Val3;
		ResType = SC_VAL_INT; goto *SubRetP;
    LTQ11:	Res = *(double *)&Val2 <= *(double *)&Val3;
		ResType = SC_VAL_INT; goto *SubRetP;

    EQ00:	Res = Val2 == Val3;
		ResType = SC_VAL_INT; goto *SubRetP;
    EQ01:	Res = *(double *)&Val3 == Val2;
		ResType = SC_VAL_INT; goto *SubRetP;
    EQ10:	Res = *(double *)&Val2 == Val3;
		ResType = SC_VAL_INT; goto *SubRetP;
    EQ11:	Res = *(double *)&Val2 == *(double *)&Val3;
		ResType = SC_VAL_INT; goto *SubRetP;

    NEQ00:	Res = Val2 != Val3;
		ResType = SC_VAL_INT; goto *SubRetP;
    NEQ01:	Res = *(double *)&Val3 != Val2;
		ResType = SC_VAL_INT; goto *SubRetP;
    NEQ10:	Res = *(double *)&Val2 != Val3;
		ResType = SC_VAL_INT; goto *SubRetP;
    NEQ11:	Res = *(double *)&Val2 != *(double *)&Val3;
		ResType = SC_VAL_INT; goto *SubRetP;


    // ********************************************************************************
    // LogOps:  Gets the result and either (1) finishes processing *here*
    //		and does another DISPATCH or (2) returns to SubRetP.

    MNEG0:	if (Var2P->ValueType == SC_VAL_FLT) goto MNEG1;
		// Do it here... no reason to return to DoLogVV!
		Var1P->ValueType = SC_VAL_INT;
		Var1P->ValueP = (void *)(- (Int64)Var2P->ValueP);
		SC_VARDEREFCOND(VMP, (V1 == 0xFF), Var1P);	// Do here to avoid ORPHAN REFR problem
		SC_RRCF_DISPATCH_OP;

    MNEG1:	ResFlt = - *(double *)&Var2P->ValueP;
		// Do it here... no reason to return to DoLogVV!
		// Some trickery for C compiler... read ValueP as a Double!
		Var1P->ValueType = SC_VAL_FLT;
		Var1P->ValueP = (void *)*(Int64 *)&ResFlt;
		SC_VARDEREFCOND(VMP, (V1 == 0xFF), Var1P);	// Do here to avoid ORPHAN REFR problem
		SC_RRCF_DISPATCH_OP;
		
    LPOS:	Res = (Var2P->ValueP != NULL) || (Var2P->ValueType != SC_VAL_INT);
		// Do it here... no reason to return to DoLogVV!
		Var1P->ValueType = SC_VAL_INT;
		Var1P->ValueP = (void *)Res;
		SC_VARDEREFCOND(VMP, (V1 == 0xFF), Var1P);	// Do here to avoid ORPHAN REFR problem
		SC_RRCF_DISPATCH_OP;

    LNEG:	Res = (Var2P->ValueP == NULL) && (Var2P->ValueType == SC_VAL_INT);
		// Do it here... no reason to return to DoLogVV!
		Var1P->ValueType = SC_VAL_INT;
		Var1P->ValueP = (void *)Res;
		SC_VARDEREFCOND(VMP, (V1 == 0xFF), Var1P);	// Do here to avoid ORPHAN REFR problem
		SC_RRCF_DISPATCH_OP;

    BNEG:	// Do it here... no reason to return to DoLogVV!
		Var1P->ValueType = SC_VAL_INT;
		Var1P->ValueP = (void *)(~(Int64)Var2P->ValueP);
		SC_VARDEREFCOND(VMP, (V1 == 0xFF), Var1P);	// Do here to avoid ORPHAN REFR problem
		SC_RRCF_DISPATCH_OP;

    BAND:	Res = (Int64)Var2P->ValueP & (Int64)Var3P->ValueP;
		ResType = SC_VAL_INT;
		goto *SubRetP;

    BOR:	Res = (Int64)Var2P->ValueP | (Int64)Var3P->ValueP;
		ResType = SC_VAL_INT;
		goto *SubRetP;

    BXOR:	Res = (Int64)Var2P->ValueP ^ (Int64)Var3P->ValueP;
		ResType = SC_VAL_INT;
		goto *SubRetP;

    #undef SC_RRCF_DISPATCH_OP
}

// ********************************************************************************
// ********************************************************************************
// ********************************************************************************
// Patterns (text processing) and Matching
//
// Patterns (Pat) are first created, then matched against text in a buffer.
// Each Pat is a chain of 1 or more PatRecords.  The first specifies the exact
// function and the subsequent records carry additional args and information.
//
// A Pat typically refers to Strings, other Pats, and even a Func that should be
// called during a match.  Each such Pat can be stored in a local variable and
// passed around.  Pats are created de novo by sys functions, often referring to
// other sub-Pats.
//
// Since Pats are inherently recursive, the process of matching will do a
// depth-first traversal of the Pat "tree" while comparing terminal nodes
// (Strings) to text in the given buffer.  In that sense, there is always an
// order to Pat elements (whether strings or other Pats) being compared.
//
// The script writer may create a simple Pat to match "This is a pattern":
//
//	MyPat1 = Pat.Seq("this ", "is ", "a pattern");
// or
//	MyPat2 = Pat.Seq(Pat.#INSEN, "this ", Pat.#SEN, "is ", "a pattern")
//
// Where MyPat2 uses flags to modulate case sensitivity, or other aspects.
//
//	MyPatNeg = ("This is ", "a pattern", Pat.#NEG, " string")
//
// Using #NEG, a Pat will explicitly match everything EXCEPT what is listed. 
//
// The full format for Pat.Seq is
//		#Flag			InSen/Sen Pos/Neg Str/Dyn
//		N Str/Pat/Func/Arr	(N can be 0, daft but permissible)
//		#Flag			InSen/Sen Pos/Neg Str/Dyn
//		M Str/Pat/Func/Arr
//		...
//		#S_POS PVarName		Stashes From/To in Buffer
//		#S_STR PVarName		Stashes STR from Buffer
//		#S_ORD PVarName		Stashes ????
//		#S_CNT PVarName		Stashes Pos length of Str
//		#S_LEN1 PVarName	Stashes Len1 (Matched CHAR Len, not bytes)
//		#S_LEN2 PVarName	Stashes ????
//		#S_NAME Str		Names this pattern
//
//		All entries must match in sequence... SELFish by definition!
//		(The last elements are all pairs, a Directive, followed by its
//		string... Most define what should be stashed in the PDict, under
//		what key, after each match.)
//
//		The "pattern" arguments can be a Str, another Pat, a script
//		function, or an Array that is simply a list of other Pat, Str,
//		Func, or Arr arguments that will be expanded in place and added
//		to the Pat. The Script Function is called (perhaps multiple
//		times) during the match and should return 0 if the match fails
//		(or non-zero if it succeeds).  It can also return a Str or even
//		another Pat that will then be matched.
//
//		The Match process will provide a PDict to stash intermediate
//	        values from and for its Pats.  STASH directives instruct the Pat
//	        what to store in the PDict and under what string Key.  So with a
//	        [Pat.#S_STR "MyText"] directive, the matching Pat will stash the
//	        buffer text string it matches in the PDict under "MyText" (key).
//
//		A subsequent Pat (in the depth-first Match) may match against
//		the "MyText" value, retrieved from the PDict, using a #DYN flag.
//		(#DYN means "this string is a KEY, use its value!")  The script
//		itself or one of the callback functions may also look up
//		"MyText" in the PDict.
//
//		NOTE: SC_SysMatchPat can accept a PDict arg on its OUT list.  If
//		      the calling script needs to access the PDict, then it
//		      *MUST* provide a Dict, any will do, (on the OUT list) for
//		      the initiating call to SC_SysMatchPat.  Otherwise, the
//		      Match system will create its own private PDict and the
//		      script will not have direct access to it.  Callback
//		      Functions included in the Pat being matched (or its
//		      sub-Pats) will always get the PDict as an arg, so will
//		      retain access either way.
//
//		The #S_NAME stash directive gives a Name Str for the Pat, so
//		when printing out details of the match, it will be shown by its
//		Name!  (Only relevant for debugging displays, but very handy in
//		complex patterns that have multiple sub-pats of the same type.)
//
//		Script (callback) Func can be provided as sub-pats.  These
//		script-level functions are called during the Match.  They can
//		return a Pat or Str that will then be matched or directly
//		provide a return (Int) Result.  These Func are called with the
//		PDict on the IN list and the current matching text POS on the
//		OUT list--so they can even change the Match location.  It will
//		be up to the script to stash any other necessary information on
//		the PDict.
//
//		Important:  Script (callback) Func, called from within a pattern
//			    match, can launch their very own separate
//			    (re-entrant) pattern match!
//
// There are very few reasons to copy or replicate a Pat, in most cases multiple
// other Pats, or Vars, simply refer to the same ones.  The garbage collection
// (RefCount) mechanism will manage Pat disposal after all such references have
// been removed.

// NOTE: Any Pat that has a choice of how to match (lattitude in logic or
//	 repetition) may be flagged as SELFish or CONSiderate.  Currently this
//	 only applies to ALT, REP, and SPAN Pats, with all other Pats being
//	 exclusively SELFish.
//
//	 A CONS(iderate) sub-pat can match successfully only if the entire
//	 aggregate Pattern matches.  In practice, this means a CONS sub-pat has
//	 to match locally first, then check that all its superior (subsequent in
//	 the depth-first traversal) Pats also match.  These checks and
//	 conceptual recursions have profound effects on the matching logic as
//	 well as repeated variable (and Res) stashing along with callback Func
//	 invocation.
//
// Pattern matchers have historically differentiated between LAZY and GREEDY for
// iterative Pats.  LAZY patterns are MINimalist and seek the SHORTEST string
// that satisfies the subsequent patterns.  But by virtue of being CONSIDERATE,
// they do MORE work than their counterpart!  GREEDY patterns are SELFISH and
// MAXimalist, they grab the LONGEST string given their own requirements,
// ignoring subsequent patterns.
//
//	 Min + Selfish		<-- XXXXXX trivial Non sequitar...
//	 Min + Considerate	<-- "Lazy"	(Does more work)
//	 Max + Selfish		<-- "Greedy"	(Does less work)
//	 Max + Considerate	<-- "Diligent?"	(Does the most work)
//
// During a Max match, especially a CONS one, the traditional approach is to
// rely on backtracking, perhaps even matching backwards.  This is not an option
// here.  So while 'working up' to the Max match, the last (most maximal)
// successful match is recorded.  If no greater match is found, this recorded
// result is presented without backtracking.  But the process of 'working up' a
// CONS match requires that superior Pats are checked at every step.
//
// Recursion is generally expensive, as the low-level function has to prepare a
// call/stack frame for each call.  This would be doubly expensive here as CONS
// operations would require multiple replications of the entire call stack.  So
// instead, all pattern matching is done iteratively in this code, using an
// MStack to maintain state on the side.
//
// This MStack and its PMatchRecords handle recursion and CONS backtracking of
// the Pat match process.  Rather than copying the call stack for CONS
// recursion, a "CONS Placeholder" is placed on the MStack to point back to
// superior match steps that have to be checked.  A few clever optimizations
// also greatly reduce repetition and replication.
//
// A Match can have a ResFunc (callback) argument which is called to inform the
// script what exactly matched, how many times, and from where to where.  But
// CONS recursion and backtracking can significantly complicate the controlling
// Script, as ResFunc would be called repeatedly only to be later nullified.
// Instead, an RStack initially records all such results, then invokes ResFunc
// on each result after the match concludes, hiding all searching to and fro.
// Nevertheless, there can be no guarantee of the exact ResFunc sequence.  CONS
// matching and "unwinding" tends to reverse the process order, which cannot be
// rectified without post-processing or match replication.
//
// NOTE:  SC_PMATCH_LASTSTATE is an optimization that currently impacts SPAN the
//	  most.  The CONS TO_Pat of a SPAN does *NOT* need to check if the SPAN
//	  is satisfied as part of its CONS.  Since TO_Pat is last, if it is
//	  satisfied, then so is the CONS itself.
//
//        SC_PMATCH_SUCCSTATE is required for correct operation of looping Pats
//	  (REP + SPAN).  A MAXimal REP will continue searching for a longer
//	  match even after SUCC, but should only do so in its own "context"
//	  (Buffer Position and Result Stack).  Therefore, CONS PMatch copies
//	  will ignore previous (recorded) SUCC if it was not their own.

    #define	SC_PATCRANGESLOTMIN	4

    typedef struct _SC_MStackRecord {
	SC_ArrDataPointer	ADP;		// Contains N PMatch records
	Uns32			TopI;
	Uns32			MaxI;
	Uns32			ConsL;		// CONS Recursion level
    } SC_MStackRecord, * SC_MStackPointer;

    typedef struct _SC_PMatchRecord {		// 8 LongLongs
	SC_PatPointer		MainPatP;	// Main Pat--SEQ, ALT, etc.
	SC_PatPointer		CurPatP;	// Main Pat or its EXTR
	Int32			StartPos;	// For MainPatP
	Int32			CurPos;
	
	Int32			MinRep;		// 0 means no minimum
	Int32			MaxRep;		// 0 means no maximum
	Int32			Rep;		// How many times...
	Int32			SRep;		// Last Succ SPACE Rep for SPAN (Similar for REP)

	Int32			CSrcI;		// This node is a copy of SrcI.  (only in CONS)
	Int32			CRetI;		// Only for Pats (REP + SPAN) that continue after succ
						// I+1 of SRC to return to in last SUCC.
						// 1-based so 0-> ALL DONE, 1-> first PMatch.
	
	Uns32			RLastI;		// Last RTopI for prev succ (For CONS and/or MAX)
	Uns32			RTempI;		// Temp intermediate for RStack
	Uns32			RTopI;		// RStack Top Index for MainPat
	Uns16			PatL;		// Pat Level (Handled iteratively in AuxPatMatch)
	Uns8			I;		// Index in CurPatP (0, 1, or 2)
	Uns8			State;		// SetUp/New, P1State, P2State...
    } SC_PMatchRecord, * SC_PMatchPointer;

    // NOTE: SC_PMatchRecord with (MainPatP == NULL) is a CONS placeholder.
    //	     StartPos indicates the NEXT TopI to be used.
    //	     If StartPos is 0, then CONS is done when this SC_PMatchRecord is top!
    //
    //       Can store RLastI...Res in a separate mem block (on BEnt slabs) as
    //	     only REP and SPAN use them.  But that would save only 3 LL, because
    //	     PMatchRecord would need a ptr to the new mem block.  Not worth it!

    typedef enum {
	SC_PMATCH_INITSTATE =	0x00,		// Initial

	SC_PMATCH_P1STATE =	0x01,		// P2 is checked first
	SC_PMATCH_P2STATE =	0x02,		// P2 State if set
	SC_PMATCH_CONSSTATE =	0x04,		// CONS state

	SC_PMATCH_FIRSTSTATE =	0x10,		// Very first PMatch!!
	SC_PMATCH_SUCCSTATE =	0x20,		// *THIS* succeeded, CONS copy resets this!
	SC_PMATCH_SRCSTATE =	0x40,		// Re-visit on SUCC return
	SC_PMATCH_LASTSTATE =	0x80,		// Last part of container Pat!
    } SC_PMATCH_STATE;

    typedef struct _SC_RStackRecord {		// Contains N PResRecord
	SC_ArrDataPointer	ADP;
	Uns32			TopI;
	Uns32			MaxI;
    } SC_RStackRecord, * SC_RStackPointer;

    typedef enum {
	SC_PRES_EMPTY =		0x00,		// Empty!
	
	SC_PRES_PUSHOP,              		// PUSH an Entry
	SC_PRES_INS1OP,				// What to Insert
	SC_PRES_INS2OP,				// Where to insert it!
	SC_PRES_DELOP,				// PULL-BACK entries
    } SC_PRES_OP;

    typedef struct _SC_PResRecord {		// 3 LongLongs
	SC_PatPointer		PatP;		// Main Pat
	Int32			FromPos;	// In Buffer
	Int32			ToPos;		// In Buffer
	Uns32			Numb;		// Which Elt, which Rep, or Span size
	Uns8			Res;		// SC_PRES_OP, What operation (Push, etc.)
	Uns8			Res1;
	Uns16			Res2;
    } SC_PResRecord, * SC_PResPointer;

    // Entire state for Match--used to save for re-entrance!
    typedef struct _SC_MSaveRecord {
	void *			BufP;		// ED Buffer being matched against
	SC_DictVPointer		PDictP;		// Dict to stash Pat match products
	SC_ClosPointer		ResFuncP;	// ResFunc to call after match
	SC_RStackPointer	RStackP;	// Res Stack for ResFuncs

	Int32			Pos;		// Current Pos
	Int32			SkipPos;	// EndPos of FIRST Pat Match
	Int32			FirstPos;	// Beginning Pos in BufP
	Int32			LastPos;	// End Pos in BufP
	Uns32			MaxConsL;	// Maximum MStack level reached
	Uns32			StepCount;	// Number of steps
    } SC_MSaveRecord, * SC_MSavePointer;

    #define	SC_SMP_MSTACK_INITBYTES		32 * sizeof(SC_PMatchRecord)
    #define	SC_SMP_MSTACK_EXTRABYTES	32 * sizeof(SC_PMatchRecord)
    #define	SC_SMP_MSTACK_SLOTSPER		((sizeof(SC_PMatchRecord) + sizeof(void *) - 1) / sizeof(void *))

    #define	SC_SMP_RSTACK_INITBYTES		32 * sizeof(SC_PResRecord)
    #define	SC_SMP_RSTACK_EXTRABYTES	32 * sizeof(SC_PResRecord)
    #define	SC_SMP_RSTACK_SLOTSPER		((sizeof(SC_PResRecord) + sizeof(void *) - 1) / sizeof(void *))
    

    // Globals for Sys Match Pat... most will get stashed in MSaveRecord for re-entrance.
    SC_VMPointer	SC_SMP_VMP;
    void *		SC_SMP_BufP;
    Int32		SC_SMP_FirstPos;	// Beginning Pos in BufP
    Int32		SC_SMP_LastPos;		// End Pos in BufP
    Int32		SC_SMP_Pos;		// Cur Pos in BufP
    Int32		SC_SMP_SkipPos;		// EndPos of FIRST Pat Match
    SC_DictVPointer	SC_SMP_PDictP;		// Dict to stash Pat (Dyn) Vars
    SC_ClosPointer	SC_SMP_ResFuncP;	// ResFunc to call after match!
    SC_RStackPointer	SC_SMP_RStackP;		// Result Stack for ResFuncP

    Uns32		SC_SMP_StepCount;	// Number of steps
    Uns32		SC_SMP_MaxConsL;	// Maximum CONS level reached

    // Globals for Sys Make Pat (MP)
    SC_PatPointer	SC_MPFirstPatP;		// Helper for MakePat
    SC_PatPointer	SC_MPLastPatP;		// Helper for MakePat, starts as MPFirstPatP
    Int16		SC_MPNextDataI;		// Where to stash, 2 means create a new Extr


    SC_PatPointer	SC_MPAuxPatNew(SC_VMPointer VMP, Uns8 PatOp)
    {
	SC_PatPointer	PatP = (SC_PatPointer)SC_BigEntryNew(VMP);

	PatP->Type = SC_ENTRY_PAT;
	PatP->PatOp = PatOp;
	PatP->RefCount = 1L;
	// Rest are all 0/NULL

	return PatP;
    }

    // SC_MPAuxPatZap is only called if Pat creation goes awry...
    // It drops the Refs, RefCount/GC will delete them.
    //
    // NOTE: Same Pat may be included in N different ones... so cannot
    //	     just Xap it directly!
    
    void	SC_MPAuxPatZap(SC_VMPointer VMP)
    {
	SC_VARDEREF(VMP, (SC_VarVPointer)SC_MPFirstPatP);
	SC_MPFirstPatP = SC_MPLastPatP = NULL;
    }

    // SC_MPAuxAddExtPat creates an SC_PAT_EXTR to 'carry' the additional Str,
    // Func, or Pat.  But *ONLY* if the first PatP node already used up its
    // DataP fields.
    //
    // NOTE:  STASH Pats are placed ahead of all EXTR on the NextP chain.  This
    //	      happens AFTER all EXTR are made, there will be *NO* STASH Pats to
    //	      worry about when this function is called!
    
    void	SC_MPAuxAddExtPat(SC_VMPointer VMP, void * DataP, Uns8 OpFlag)
    {
	SC_PatPointer	ExtPatP;

    TryAgain:
	if (SC_MPNextDataI < 2) {
	    // Possible that SC_MPLastPatP == SC_MPFirstPatP!!
	    // NOTE:  Main Pat had Flags updated while EntryCount was 0.

	    if ((OpFlag ^ SC_MPLastPatP->Flags) & ~SC_PAT_DATAMASK) {
		// Create a new EXTR, Flags changed!
		SC_MPNextDataI = 2;
		goto TryAgain;
	    }
	    SC_MPLastPatP->Data[SC_MPNextDataI++] = DataP;
	    
	} else {
	    ExtPatP = SC_MPAuxPatNew(VMP, SC_PAT_EXTR);
	    ExtPatP->Flags = OpFlag | SC_PAT_DATA0P | SC_PAT_DATA1P;
	    ExtPatP->Data[0] = DataP;

	    SC_MPNextDataI = 1;
	    SC_MPLastPatP->NextP = ExtPatP;
	    SC_MPLastPatP = ExtPatP;
	}

	// Will ONLY be called with STR, PAT, or CLOS!!  So Inc RefCount
	SC_VARINCREF((SC_VarVPointer)DataP);
    }

    // SC_MPAuxAddEntry will add contents of VP to Pat under construction.
    //
    // VP cannot be DICT, as Pairs in a Dict are in reverse order to what
    // is expected.  Arr are supported, and a good structure for multiple elts.

    Int16	SC_MPAuxAddChar(SC_VMPointer VMP, void * DataP, Uns8 OpFlag);
    
    Int16	SC_MPAuxAddEntry(SC_VMPointer VMP, SC_VarVPointer VP, Uns8 OpFlag, Uns8 NoArr, Uns8 IsChar)
    {
	SC_ArrDataPointer	ADP;
	Uns32			Count;
	SC_VarVPointer *	EntryP;

	switch (VP->Type) {
	    case SC_ENTRY_ARR:
		    if (NoArr) return 0;
		    // Viewed as a sequence of entries... Strings, Pats, Arrs, or Funcs!
		    ADP = (*(SC_ArrVPointer)VP).ArrDataP;
		    Count = ADP->SlotCount;
		    EntryP = (SC_VarVPointer *)ADP->Data;
		    while (Count) {
			if (*EntryP && (((*EntryP)->ValueType != SC_VAL_PTR) ||
					! SC_MPAuxAddEntry(VMP, (SC_VarVPointer)(*EntryP)->ValueP, OpFlag, 0, IsChar)))
			    return 0;

			EntryP++, Count--;
		    }
		    break;
		    
	    case SC_ENTRY_STR:		// Add String
	    case SC_ENTRY_PAT:		// Add Pat	    
	    case SC_CHUNK_CLOS:		// Add Func (Clos)
		    // ???? Check we do NOT add CLOS to Char Pat!
		    if (IsChar)
			return SC_MPAuxAddChar(VMP, VP, OpFlag);
		    SC_MPAuxAddExtPat(VMP, VP, OpFlag);
		    break;

	    default: return 0;
	}

	return 1;
    }

    // SC_MPAuxCheckFlags checks that FlagInt (from Script) are correct (matches the Op
    // and does not conflict internally), then translate to PatFlag format and sets
    // *PatFlagP.
    //
    // NOTE:  FlagInt can be ONE flag or multiple!!
    //	      Script can have [#SEN + #POS + #DYN] *OR* [#SEN, #POS, #DYN] or [#SEN | #POS | #DYN]!
    //
    //	      Conflict ONLY arises when ONE FlagInt has both set/reset of a given flag.
    
    Int16	SC_MPAuxCheckFlags(Int64 FlagInt, Uns8 * PatFlagP, char * PNameStr, Int16 ArgI, Uns8 PatOp)
    {
	Int64	     F;
	static Uns32 FlagMaskArr[] =
		     {SC_PMASK_SEN | SC_PMASK_NEG | SC_PMASK_DYN | SC_PMASK_NRES,					// SEQ
		      SC_PMASK_SEN | SC_PMASK_NEG | SC_PMASK_DYN | SC_PMASK_CONS | SC_PMASK_NRES,			// ALT
		      SC_PMASK_SEN | SC_PMASK_NEG | SC_PMASK_MIN | SC_PMASK_DYN | SC_PMASK_CONS | SC_PMASK_NRES,	// SPAN
		      SC_PMASK_SEN | SC_PMASK_NEG | SC_PMASK_MIN | SC_PMASK_DYN | SC_PMASK_CONS | SC_PMASK_NRES,	// REP
		      SC_PMASK_NEG | SC_PMASK_NRES,									// CHAR
		      SC_PMASK_REL | SC_PMASK_V | SC_PMASK_NRES,							// GO
		      SC_PMASK_REL | SC_PMASK_V | SC_PMASK_NRES,							// AT
		     };

	// Outside limits... Number is not a flag!
	F = FlagInt & SC_P_MASK;
	if (FlagInt != F) {
	    snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "BuildPat %s: Arg %d is not a flag.", PNameStr, ArgI);
	    goto ErrorOut;
	}

	// Flag does not match Op
	if (FlagInt != (FlagInt & FlagMaskArr[PatOp])) {
	    snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "BuildPat %s: Arg %d Flag does not match Op.", PNameStr, ArgI);
	    goto ErrorOut;
	}

	// Special handling for REP when Non-zero MIN == MAX... Accept MIN/MAX but flag CONS.
	if ((SC_MPFirstPatP->PatOp == SC_PAT_REP) &&
	    SC_MPFirstPatP->Data[0] &&					// Not zero
	    (SC_MPFirstPatP->Data[0] == SC_MPFirstPatP->Data[1]) &&	// MIN == MAX
	    ! (SC_MPFirstPatP->Flags & SC_PAT_DATAMASK) &&		// Both are INT
	    (FlagInt & SC_P_CONS)) {					// Only disallow CONS!
	    
	    snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "BuildPat Rep/Iter: Arg %d Flag is meaningless when MIN equals MAX", ArgI);
	    goto ErrorOut;
	}

	// Check for conflicting logic... Add+Remove Flag!
	// Then Set/Reset appropriate flag!
	if (0x0003 == (F & 0x0003)) goto Conflict;
	if (F & 0x0001) *PatFlagP &= ~SC_PAT_SENFLAG;	// InSen
	if (F & 0x0002) *PatFlagP |= SC_PAT_SENFLAG;
	
	F >>= 2;
	if (0x0003 == (F & 0x0003)) goto Conflict;
	if (F & 0x0001) *PatFlagP &= ~SC_PAT_NEGFLAG;	// Pos
	if (F & 0x0002) *PatFlagP |= SC_PAT_NEGFLAG;

	F >>= 2;
	if (0x0003 == (F & 0x0003)) goto Conflict;
	if (F & 0x0001) *PatFlagP |= SC_PAT_ABSFLAG;	// Abs
	if (F & 0x0002) *PatFlagP &= ~SC_PAT_ABSFLAG;	// Rel

	F >>= 2;
	if (0x0003 == (F & 0x0003)) goto Conflict;
	if (F & 0x0001) *PatFlagP &= ~SC_PAT_VFLAG;	// H
	if (F & 0x0002) *PatFlagP |= SC_PAT_VFLAG;

	F >>= 2;
	if (0x0003 == (F & 0x0003)) goto Conflict;
	if (F & 0x0001) *PatFlagP &= ~SC_PAT_DYNFLAG;	// Stat
	if (F & 0x0002) *PatFlagP |= SC_PAT_DYNFLAG;

	F >>= 2;
	if (0x0003 == (F & 0x0003)) goto Conflict;
	if (F & 0x0001) *PatFlagP &= ~SC_PAT_MINFLAG;	// Max
	if (F & 0x0002) *PatFlagP |= SC_PAT_MINFLAG;

	F >>= 2;
	if (0x0003 == (F & 0x0003)) goto Conflict;
	if (F & 0x0001) *PatFlagP &= ~SC_PAT_CONSFLAG;	// Self
	if (F & 0x0002) *PatFlagP |= SC_PAT_CONSFLAG;

	F >>= 2;
	if (0x0003 == (F & 0x0003)) goto Conflict;
	if (F & 0x0001) *PatFlagP &= ~SC_PAT_NRESFLAG;	// Res
	if (F & 0x0002) *PatFlagP |= SC_PAT_NRESFLAG;	// NRes
	
	return 1;

    Conflict:
	snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "BuildPat %s: Arg %d Flags are conflicting.", PNameStr, ArgI);
    ErrorOut:
	SC_RunErrorInfoStr = SC_RunInfoS;
	return 0;
	
    }

    // SC_MPAuxGetStashPat will get the indicated STASH pat from the NextP chain of
    // the MPFirstPatP.  Will create it if not there.
    //
    // There are different STASH Pats
    //   -- SC_PAT_STASH_STRS  Stores PosRange (FromPos-ToPos) under PVarName in Data[0]
    //			       Stores Str (from BufP) under PVarName in Data[1]
    //   -- SC_PAT_STASH_NUMBS Stores Ord (Which ALT pattern) under PVarName in Data[0]
    //	                       Stores Cnt (How many Reps) under PVarName in Data[1]
    //	 -- SC_PAT_STASH_LENS  Stores Len1 (how many CHARS) under PVarName in Data[0]
    //			       Stored Len2 (how many CHARS) under PVarName in Data[1]
    //   -- SC_PAT_STASH_LABEL Stores Label (for Pat) in Data[0]
    //
    // One or Both are created and stashed directly after the first Pat on its NextP chain,
    // *AHEAD* of any EXTR Pats.

    SC_PatPointer	SC_MPAuxGetStashPat(SC_VMPointer VMP, Uns8 PatOp)
    {
	SC_PatPointer	PatP;

	PatP = SC_MPFirstPatP->NextP;
	// PatP is first on chain... stop if already hit EXTR!
	while (PatP && (PatP->PatOp != SC_PAT_EXTR)) {
	    if (PatP->PatOp == PatOp)
		return PatP;
	    PatP = PatP->NextP;
	}
	
	// Could not find Stash Pat, make it.
	PatP = SC_MPAuxPatNew(VMP, PatOp);
	PatP->Flags = SC_PAT_DATA0P | SC_PAT_DATA1P;
	PatP->NextP = SC_MPFirstPatP->NextP;
	SC_MPFirstPatP->NextP = PatP;
	if (SC_MPLastPatP == SC_MPFirstPatP)
	    SC_MPLastPatP = PatP;
	return PatP;
    }

    // SC_MPAuxMakeStash will create/store the necessary STASH Pats on the NextP
    // chain of the Pat being constructed (SC_MPFirstPatP).  These STASH
    // directives are *ALWAYS* last on the arglist, so the InCount helps count
    // them down.  StackEltP points to the ArgStack where the args come in from
    // the Script call to make a pat.
    //
    // STASH directive will instruct matcher to
    //   a) Stash indicated value under given Var name *OR*
    //   b) Call indicated script function (Closure).

    Int16	SC_MPAuxMakeStash(SC_VMPointer VMP, SC_VarVPointer StackEltP, Uns8 InCount, Int16 I, char* PNameStr)
    {
	Int64		DataInt;
	Uns8		PatOp;
	Int16		PatIndex;
	SC_PatPointer	PatP;
	SC_VarVPointer	VP;

	// While there are more STASH directives to process
	while (InCount) {
	    DataInt = (Int64)StackEltP->ValueP;
	    if ((StackEltP->ValueType != SC_VAL_INT) || (DataInt < SC_PAT_FIRST_STASH) || (DataInt > SC_PAT_LAST_STASH)) {
		// NOT a STASH directive!!
		snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "BuildPat %s: Arg %d is not a Stash Directive.", PNameStr, I);
		SC_RunErrorInfoStr = SC_RunInfoS;
		return 0;
	    }

	    // From Script STASH dir to STASH PatOp!
	    PatIndex = (DataInt / SC_PAT_FIRST_STASH) - 1;	// Temp intermediate	
	    PatOp = SC_PAT_STASH_STRS + (PatIndex >> 1);	// Which Op
	    PatIndex = PatIndex & 0x0001;			// Which Data slot
	    
	    PatP = SC_MPAuxGetStashPat(VMP, PatOp);
	    if (PatP->Data[PatIndex]) {
		// Already have that one!
		snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "BuildPat %s: Arg %d is duplicate Stash Directive.", PNameStr, I);
		SC_RunErrorInfoStr = SC_RunInfoS;
		return 0;
	    }

	    StackEltP++, InCount--, I++;

	    if ((StackEltP->ValueType != SC_VAL_PTR) ||
		  ! (VP = (SC_VarVPointer)StackEltP->ValueP) ||		// Assign
		  ((VP->Type != SC_ENTRY_STR) && (VP->Type != SC_CHUNK_CLOS))) {
		// Neither STR (var name) or CLOS (Script func to call)!
		snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "BuildPat %s: Arg %d must be Str or Func.", PNameStr, I);
		SC_RunErrorInfoStr = SC_RunInfoS;
		return 0;
	    }

	    PatP->Data[PatIndex] = VP;				// Stash it!
	    SC_VARINCREF(VP);					// Pat refers to Clos or Str, so INC RefCount
	    StackEltP++, InCount--, I++;	    
	}

	return 1;
    }

    // SC_MPAuxGetPatArgs goes through the args specified in the make Pat call.
    // These are Flags, STASH directives, Str args, or other Pats.
    //
    // Flags and STASH directives are processed separately.
    // SC_MPAuxAddEntry will handle Str and Pat args, does the main work!

    Int16	SC_MPAuxGetPatArgs(SC_VMPointer VMP, SC_VarVPointer StackEltP, char * PNameStr, Uns8 PatOp, Uns8 InCount, Int16 I)
    {
	Uns8		OpFlag;
	Uns8		IsSpan, IsChar;
	Int16		EntryCount;
	Int64		DataInt;

	// Assumes PatP is allocated.  Go through args... Special handling for Span and Char.
	OpFlag = SC_PAT_NOFLAG;
	IsSpan = (PatOp == SC_PAT_SPAN);
	IsChar = (PatOp == SC_PAT_CHAR);
	EntryCount = 0;

	// Span defaults to MIN, Rep defaults to MIN+CONS!
	if (IsSpan) {
	    OpFlag |= SC_PAT_MINFLAG;
	    SC_MPFirstPatP->Flags |= SC_PAT_MINFLAG;
	} else if (PatOp == SC_PAT_REP) {
	    OpFlag |= SC_PAT_MINFLAG + SC_PAT_CONSFLAG;
	    SC_MPFirstPatP->Flags |= SC_PAT_MINFLAG + SC_PAT_CONSFLAG;
	}

	// Go through the args...
	while (InCount) {
	    if (StackEltP->ValueType == SC_VAL_INT) {
		// Must be a flag *OR* a Stash directive
		DataInt = (Int64)StackEltP->ValueP;

		if ((SC_PAT_FIRST_STASH <= DataInt) && (DataInt <= SC_PAT_LAST_STASH)) {
		    // It is a STASH Directive... MUST come at the end.. so read all of them!
		    if (! SC_MPAuxMakeStash(VMP, StackEltP, InCount, I, PNameStr))
			return 0;
		    return 1;

		} else {
		    // Check if flag.  Note must also add DATA0P and DATA1P flags for NextP Pats.
		    // Flags for Main Pat start with DATA0P and DATA1P set...See SC_MPAuxSimplePat!
		    if (! SC_MPAuxCheckFlags(DataInt, &OpFlag, PNameStr, I, PatOp))
			return 0;

		    if (! EntryCount) {
			// Overwrite Flags for Main Pat
			SC_MPFirstPatP->Flags = (SC_MPFirstPatP->Flags & SC_PAT_DATAMASK) | OpFlag;
		    } else
			OpFlag |= (SC_PAT_DATA0P | SC_PAT_DATA1P);	// For EXTR Pats
		}

	    } else {

		if ((PatOp == SC_PAT_GO) || (PatOp == SC_PAT_AT) ||	// GO and AT will not take Str/Pat args
		    (StackEltP->ValueType != SC_VAL_PTR) ||		// Str and Pat are both ENT_PTR args
		    (IsSpan && (EntryCount > 1)) ||			// SPAN gets only 2 Str/Pat args
		    // SC_MPAuxAddEntry does the work ******************
		    ! SC_MPAuxAddEntry(VMP, (SC_VarVPointer)StackEltP->ValueP, OpFlag, IsSpan, IsChar)) {

		    snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "BuildPat %s: Arg %d is bad.", PNameStr, I);
		    SC_RunErrorInfoStr = SC_RunInfoS;
		    return 0;
		}
		EntryCount++;
	    }

	    I++, InCount--, StackEltP++;	// Next IN arg
	}

        return 1;
    }

    // SC_MPAuxSimplePat will create a "simple" Pat with the given PatOp.
    // This function is passed all the args from a SysCall... looks into Call Stack.
    
    Int16	SC_MPAuxSimplePat(SC_VMPointer VMP, Uns16 InOutCount, Uns8 PatOp, char * PNameStr)
    {
	Uns8		InCount = InOutCount;
	Uns8		TotCount = (InOutCount >> 8) + InCount;
	SC_VarVPointer	StackEltP;

	SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);	
	SC_MPLastPatP = SC_MPFirstPatP = SC_MPAuxPatNew(VMP, PatOp);		// Create main Pat
	SC_MPFirstPatP->Flags = SC_PAT_DATA0P | SC_PAT_DATA1P;			// Set DATA0 and DATA1 flags
	SC_MPNextDataI = 0;

	StackEltP = SC_RUNSTACK_GETNTH(VMP, TotCount);
	// SC_MPAuxGetPatArgs will process all args, one by one... does all the work!
	if (! SC_MPAuxGetPatArgs(VMP, StackEltP, PNameStr, PatOp, InCount, 1)) {
	    // Didn't work... Zap them (relies on RefCount/GC to zap judiciously)!
	    SC_MPAuxPatZap(VMP);

	    SC_RunErrorInfoStr = "Pat.Make: Bad args.";
	    return SC_RUN_BADARG;
	}

	// DeRef and flush *ALL* args... even unwanted ones!
	SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
	// Push result value on stack, return error code.
	return SC_SysAuxReturn(VMP, SC_VAL_PTR, SC_MPFirstPatP);
    }

Int16	SC_SysMakePat(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	StackEltP;
    Int16		Res;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);	
    StackEltP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    if ((InCount < 1) ||
	(StackEltP->ValueType != SC_VAL_INT) ||
	((Uns64)StackEltP->ValueP > SC_PATCOUNT))
	goto ErrorOut;

    // Just call the appropiate function... will have 1 extra arg
    // left on the RunStack for now.
    //
    // NOTE:  ITER does NOT have its own PAT type!

    switch ((Uns8)(Uns64)StackEltP->ValueP) {
	case SC_PAT_SEQ:	Res = SC_SysMakePatSeq(VMP, InOutCount - 1);	break;
	case SC_PAT_ALT:	Res = SC_SysMakePatAlt(VMP, InOutCount - 1);	break;
	case SC_PAT_SPAN:	Res = SC_SysMakePatSpan(VMP, InOutCount - 1);	break;
	case SC_PAT_REP:	Res = SC_SysMakePatRep(VMP, InOutCount - 1);	break;
	case SC_PAT_CHAR:	Res = SC_SysMakePatChar(VMP, InOutCount - 1);	break;
	case SC_PAT_GO:		Res = SC_SysMakePatGo(VMP, InOutCount - 1);	break;
	case SC_PAT_AT:		Res = SC_SysMakePatAt(VMP, InOutCount - 1);	break;
    }
        
    // Move the return arg down 1 slot on the stack, then return Res;
    *StackEltP = *(StackEltP + 1);
    StackEltP = SC_RUNSTACK_POP(VMP);		// Don't need it, avoid GCC warning.
    return Res;

ErrorOut:
    SC_RunErrorInfoStr = "Pat.Make: bad/missing Pat type.";
    return SC_RUN_BADARG;
}


// SC_SysMakePatSeq will create a new SEQ Pat.
// Pat looks for a sequence of its args... "Abc", "Def" will match
// against "AbcDef".
//
// Pushes Pat (address) on RunStack if successful, 0 if fail.
// (C function Returns SC_NO_ERR or ERR code)
//
// SEQ Pattern: Accepts Str/Pat/Func entries.
//		All entries must match in sequence... SELFish by definition!
//	
//		#Flag			InSen/Sen Pos/Neg Str/Dyn
//		N Str/Pat/Func/Arr	(N can be 0, daft but permissible)
//		#Flag			InSen/Sen Pos/Neg Str/Dyn
//		M Str/Pat/Func/Arr
//		...
//		#S_POS PVarName		Stashes From/To in Buffer
//		#S_STR PVarName		Stashes STR from Buffer
//		#S_CNT PVarName		Stashes Pos length of Str
//		#S_ORD PVarName		Stashes 0
//		#S_LEN1 PVarName	Stashes Len1 (Matched CHAR Len)
//		#S_NAME Str		Names this pattern
//		#S_FUNC
//		#S_FUNCA
//		#S_DATA

Int16	SC_SysMakePatSeq(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_MPAuxSimplePat(VMP, InOutCount, SC_PAT_SEQ, SC_PatStrArr[SC_PAT_SEQ]);
}

// SC_SysMakePatAlt will create a new ALT Pat.
//
// ALT Pattern: Accepts Str/Pat/Func/Arr entries.
//		First matching entry wins... Individual entries can be SELF *or* CONS.
//	
//		#Flag			InSen/Sen Pos/Neg Str/Dyn Self/Cons
//		N Str/Pat/Func/Arr
//		#Flag			InSen/Sen Pos/Neg Str/Dyn Self/Cons
//		M Str/Pat/Func/Arr
//		...
//		#S_POS PVarName		Stashes From/To in Buffer
//		#S_STR PVarName		Stashes STR from Buffer
//		#S_CNT PVarName		Stashes Pos length of Str
//		#S_ORD PVarName		Stashes Order of matching Pat/Str/Func
//		#S_LEN1 PVarName	Stashes Len1 (Matched CHAR Len)
//		#S_NAME Str		Names this pattern
//		...

Int16	SC_SysMakePatAlt(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_MPAuxSimplePat(VMP, InOutCount, SC_PAT_ALT, SC_PatStrArr[SC_PAT_ALT]);
}

// SC_SysMakePatSpan will create a new SPAN Pat.
//
// SPAN Pat:	Accepts up to TWO Str/Pat/Func entries (FROM and TO).
//		May have different forms:
//		  No Entries:  Starts at START... just matches *SPACE*
//		  1 Entry:     Matches FROM... then just matches *SPACE*
//		  2 Entry:     Matches FROM, var space, then TO.
//		Can operate SELFish or CONSiderate.

//		(Self/Cons and Max/Min affects SPACE only, so looks at TO
//		 very first flags only.)
//
//		#Flag			InSen/Sen Pos/Neg Str/Dyn Max/Min Self/Cons
//		Str/Pat/Func		ONLY 1 Str/Pat/Func
//		#Flag			InSen/Sen Pos/Neg Str/Dyn
//		Str/Pat/Func		ONLY 1 Str/Pat/Func
//		#S_POS PVarName		Stashes From/To in Buffer
//		#S_STR PVarName		Stashes STR from Buffer
//		#S_CNT PVarName		Stashes Pos length of Str
//		#S_ORD PVarName		Stashes Pos length of SPACE
//		#S_LEN1 PVarName	Stashes Len1 (Matched CHAR Len)
//		#S_LEN2 PVarName	Stashes Len2 (SPACE CHAR Len)
//		#S_NAME Str		Names this pattern
//		...

Int16	SC_SysMakePatSpan(SC_VMPointer VMP, Uns16 InOutCount)
{
    return SC_MPAuxSimplePat(VMP, InOutCount, SC_PAT_SPAN, SC_PatStrArr[SC_PAT_SPAN]);
}

// SC_SysMakePatRep will create a new REP Pat.
// Default is MIN + CONS
//
// REP Pat:	MUST start with two iteration limits: Min and Max.
//		Can only be INT or FUNC (returns value at runtime).  ???? Make Str for #DYN too
//		Min = 0 makes entry match optional,
//		Max = 0 means no Maximum.
//		Flags/Entries/Stash directives are otherwise like SEQ.
//		Only Min/Max and Cons/Self flags preceeding first Entry are honored.
//
//		Min Numb/Func		Required
//		Max Numb/Func		Required
//		#Flag			InSen/Sen Pos/Neg Str/Dyn Max/Min Self/Cons
//		N Str/Pat/Func/Arr
//		#Flag			InSen/Sen Pos/Neg Str/Dyn
//		M Str/Pat/Func/Arr
//		...
//		#S_POS PVarName		Stashes From/To in Buffer
//		#S_STR PVarName		Stashes STR from Buffer
//		#S_CNT PVarName		Stashes COUNT of repeat
//		#S_ORD PVarName		Stashes 0
//		#S_LEN1 PVarName	Stashes Len1 (Matched CHAR Len)
//		#S_NAME Str		names this pattern
//		...
//
//		Data[0] of main Pat has MIN (INT or FUNC) and
//		Data[1] of main Pat has MAX (INT or FUNC).
//		There MUST be an EXTR on the NextP list.
//
// Logically, there must be an Entry (Str/Pat) that is repeated... but there is
// no way to check for this!!  The only Entry could be an empty string or a FUNC
// that returns an Empty string at runtime.

Int16	SC_SysMakePatRep(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	StackEltP;
    Int64		Val[2];
    Uns8		Flags;
    Uns8		Count;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);    
    if (InCount < 3) {
	SC_RunErrorInfoStr = "Pat.Make Rep: Not enough args.";
	return SC_RUN_BADARG;
    }

    Flags = SC_PAT_NOFLAG;
    StackEltP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    // Get MIN, then MAX
    for (Count = 0; Count < 2; Count++, StackEltP++, InCount--) {
	if (StackEltP->ValueType == SC_VAL_INT) {
	    // VAL is Int
	    Val[Count] = (Int64)StackEltP->ValueP;
	    if ((Int64)StackEltP->ValueP < 0) {
		snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "Pat.Make Rep: %s (Arg %d) must be positive.",
						       (Count) ? "Max" : "Min", Count + 1);
		SC_RunErrorInfoStr = SC_RunInfoS;
		return SC_RUN_BADARG;
	    }
	} else {
	    // VAL should be Func (Clos)
	    Flags |= (Count) ? SC_PAT_DATA1P : SC_PAT_DATA0P;
	    if ((StackEltP->ValueType != SC_VAL_PTR) ||
		    (! (Val[Count] = (Int64)StackEltP->ValueP)) ||		// Assign
		    ((*(SC_VarVPointer)StackEltP->ValueP).Type != SC_CHUNK_CLOS)) {
		snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "Pat.Make Rep: %s (Arg %d) must be INT or FUNC.",
							(Count) ? "max" : "Min", Count + 1);
		SC_RunErrorInfoStr = SC_RunInfoS;
		return SC_RUN_BADARG;
	    }
	    SC_VARINCREF((SC_VarVPointer)StackEltP->ValueP);			// Pat refs Clos, Inc RefCount
	}
    }

    // Max of 0 means INFINITY!  (Compare only if both are INT.)
    if (!(Flags & SC_PAT_DATAMASK) && Val[1] && (Val[0] > Val[1])) {
	snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "Pat.Make Rep: Min (%ld) is larger than Max (%ld).",
					       Val[0], Val[1]);
	SC_RunErrorInfoStr = SC_RunInfoS;
	return SC_RUN_BADARG;
    }

    SC_MPLastPatP = SC_MPFirstPatP = SC_MPAuxPatNew(VMP, SC_PAT_REP);
    SC_MPLastPatP->Flags = Flags;
    SC_MPLastPatP->Data[0] = (void *)Val[0];
    SC_MPLastPatP->Data[1] = (void *)Val[1];
    SC_MPNextDataI = 2;

    if (! SC_MPAuxGetPatArgs(VMP, StackEltP, SC_PatStrArr[SC_PAT_REP], SC_PAT_REP, InCount, 3)) {
	SC_MPAuxPatZap(VMP);	// Drops refs, RefCount/GC will delete it!
	return SC_RUN_BADARG;
    }
    
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_PTR, SC_MPFirstPatP);
}

// SC_SysMakePatIter will create a new REP Pat with Min and Max set to N.
//
// REP Pat:	MUST start with 1 iteration limits: Cnt (Will be MIN *and* MAX for Rep)
//		(Can only be INT *NOT* FUNC, use REP for FUNC!)
//		Cnt == 0 is NOT allowed... use REP for 0 MinRep/MaxRep.
//		Flags/Entries/Stash directives are otherwise like SEQ.
//		Min/Max and Cons/Self flags are ignored.
//
//		Cnt Numb/Func		Required
//		#Flag			InSen/Sen Pos/Neg Str/Dyn
//		N Str/Pat/Func/Arr
//		#Flag			InSen/Sen Pos/Neg Str/Dyn
//		M Str/Pat/Func/Arr
//		...
//		#S_POS PVarName		Stashes From/To in Buffer
//		#S_STR PVarName		Stashes STR from Buffer
//		#S_CNT PVarName		Stashes COUNT of repeat
//		#S_ORD PVarName		Stashes 0
//		#S_LEN1 PVarName	Stashes Len1 (Matched CHAR Len)
//		#S_NAME Str		Names this pattern
//		...
//
//		Pat will be REP:
//		Data[0] of main Pat has MIN INT == CNT
//		Data[1] of main Pat has MAX INT == CNT
//		There MUST be an EXTR on the NextP list.
//
// Logically, there must be an Entry (Str/Pat) that is repeated... but there is
// no way to check for this!!  The only Entry could be an empty string or a FUNC
// that returns an Empty string at runtime.

Int16	SC_SysMakePatIter(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	StackEltP;
    Int64		Cnt;
    Uns8		Flags;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);    
    if (InCount < 2) {
	SC_RunErrorInfoStr = "Pat.Make Iter: Not enough args.";
	return SC_RUN_BADARG;
    }

    Flags = SC_PAT_NOFLAG;
    StackEltP = SC_RUNSTACK_GETNTH(VMP, TotCount);

    // Get Cnt... can be Int or Func
    if (StackEltP->ValueType == SC_VAL_INT) {
	Cnt = (Int64)StackEltP->ValueP;
	if ((Int64)StackEltP->ValueP < 1) {
	    snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "Pat.Make Iter: Cnt (Arg 1) must be >= 1.");
	    SC_RunErrorInfoStr = SC_RunInfoS;
	    return SC_RUN_BADARG;
	}
    } else {
	snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "Pat.Make Iter: Cnt (Arg 1) must be INT.");
	SC_RunErrorInfoStr = SC_RunInfoS;
	return SC_RUN_BADARG;
    }

    StackEltP++, InCount--;

    SC_MPLastPatP = SC_MPFirstPatP = SC_MPAuxPatNew(VMP, SC_PAT_REP);
    SC_MPLastPatP->Flags = Flags;
    SC_MPLastPatP->Data[0] = (void *)Cnt;
    SC_MPLastPatP->Data[1] = (void *)Cnt;
    SC_MPNextDataI = 2;

    if (! SC_MPAuxGetPatArgs(VMP, StackEltP, SC_PatStrArr[SC_PAT_REP], SC_PAT_REP, InCount, 2)) {
	SC_MPAuxPatZap(VMP);	// Drops refs, RefCount/GC will delete it!
	return SC_RUN_BADARG;
    }
    
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_PTR, SC_MPFirstPatP);
}

// SC_SysMakePatChar will create a new CHAR Pat.
//
// CHAR Pat:	#Flag			Pos/Neg
//		N Str/Pat/Arr		Str, CharPat, or ARR of those
//		#Flag			Pos/Neg
//		N Str/Pat/Arr		Str, CharPat, or ARR of those
//		...
//		#S_POS PVarName		Stashes From/To in Buffer (Only 1 char)
//		#S_STR PVarName		Stashes STR from Buffer (Only be 1 char)
//		#S_CNT PVarName		Stashes 1
//		#S_ORD PVarName		Stashes Unicode for matched Char
//		#S_LEN1 PVarName	Stashes Len1 (Pos Len of matched char)
//		#S_NAME Str		Names this pattern
//		...
//
//		Pos/Neg only effects *PAT MAKING*! NEG means EXCLUDE the
//		following (whether Str, Pat, or ARR) from the Pat.  The Pat
//		itself has no polarity, just a 'collection' of chars.
//
// Does NOT create EXTR Pats on its NextP chain, just STASH Pats.  Only creates
// a Main Pat that has a CMap (Data[0]), CRange (Data[1]), or both.  (Initially
// has neither, but somewhat useless in that state!)
//
// NOTE:  ASCII chars range from 0..127.  Higher code points are regarded as
//        Unicode/UTF-8 (Even though UTF-8 *includes* ASCII).
//
// Args can only be strings, other CHAR Pats, or an ARR of them... nothing else.
// Func or other Pats (SEQ, ALT, SPAN, etc.) are *ERRORS*.
//
// CMap Pat (Data[0]) represents 0..127 as a bitmap.
//
// CRange Pat (Data[1]) represents Unicodes in an ordered non-abutting and
// non-overlapping list of PRanges--individual char X is given as a PRange of
// X-to-X.  PRanges are stored in a separate Array, expanded as necessary but
// NEVER shrunk.
//
// The CharMap is fundamentally case sensitive, for ascii the given chars *ARE*
// case specific and for Unicode *NO CASE INFORMATION IS AVAILABLE* here--this
// does NOT read the Unicode data spec file!

    void SC_MPCharAddToCMap(SC_VMPointer VMP, char L, char R, Uns8 IsNeg)
    {
	SC_PatCMapPointer	CMapP = (SC_PatCMapPointer)SC_MPFirstPatP->Data[0];
	Uns8			Cur = L;

	if (IsNeg) {
	    if (CMapP == 0) return;	// Nothing to do!
	    while (Cur <= R) {
		CMapP->Data[Cur >> 3] &= ~(0x80 >> (Cur & 0x07));
		Cur++;
	    }
	    
	} else {
	    if (CMapP == 0) {
		CMapP = (SC_PatCMapPointer)SC_MPAuxPatNew(VMP, SC_PAT_CMAP);
		SC_MPFirstPatP->Data[0] = CMapP;
	    }
	    while (Cur <= R) {
		CMapP->Data[Cur >> 3] |= (0x80 >> (Cur & 0x07));
		Cur++;
	    }
	}
    }

    void SC_MPCharUnionCMap(SC_VMPointer VMP, SC_PatCMapPointer OtherCMapP, Int16 Neg)
    {
	SC_PatCMapPointer	CMapP = (SC_PatCMapPointer)SC_MPFirstPatP->Data[0];
	Uns64			*SP, *DP;

	if (CMapP == 0) {
	    CMapP = (SC_PatCMapPointer)SC_MPAuxPatNew(VMP, SC_PAT_CMAP);
	    SC_MPFirstPatP->Data[0] = CMapP;
	}

	DP = (Uns64 *)CMapP->Data;
	SP = (Uns64 *)OtherCMapP->Data;

	if (Neg) {
	    *DP++ &= ~*SP++;
	    *DP &= ~*SP;
	} else {
	    *DP++ |= *SP++;
	    *DP |= *SP;
	}
    }

    // SC_MPCharDelFromCRange will delete chars from L to R (inclusive)
    // from the Unicode CRange.
    //
    // Everything is stored as a PRange, even if L and R are equal!
    // ** L, R, PRange.Beg, and PRange.End are INCLUSIVE **
    //
    // L and R are UNICODE... *NOT* UTF-8 representation.
    // (Will not come here if L or R are simple ASCII, < 0x0080 !!)
    //
    // NOTE:  This SC_PAT_CRANGE has a RANGE field that records Min-Max
    //	      unicodes it covers.

    void SC_MPCharDelFromCRange(SC_VMPointer VMP, Uns32 L, Uns32 R)
    {
	// Wipes out all... full overlap
	// Reduces Beg of a PRange,
	// Reduces End of a PRange,
	// ... and wipes out N intermediate PRanges.
	// Can also break 1 PRange into 2!!
	//
	// Simple algorithm, easy to follow!

	SC_PatCRangePointer	CRangeP = (SC_PatCRangePointer)SC_MPFirstPatP->Data[1];
	Uns32			SlotsUsed, I;
	Uns32			ZapI, ZapCount;
	Int16			BegIsIn, EndIsIn;
	SC_PRangePointer	RP;
	SC_ArrDataPointer	ADP;

	// Nothing to do... No CRange or no overlap with it.
	if ((CRangeP == NULL) || (R < CRangeP->Range.Beg) || (CRangeP->Range.End < L))
	    return;
	    
	// Full overlap... will wipe Range out completely!
	if ((L <= CRangeP->Range.Beg) && (CRangeP->Range.End <= R)) {
	    CRangeP->Range.Beg = (Uns32)-1L;
	    CRangeP->Range.End = 0L;
	    CRangeP->SlotsUsed = 0L;
	    return;
	}

	// Adjust Range...Must trim Beg or End of Range!
	if ((L <= CRangeP->Range.Beg) && (CRangeP->Range.Beg <= R))
	    CRangeP->Range.Beg = R+1;
	if ((L <= CRangeP->Range.End) && (CRangeP->Range.End <= R))
	    CRangeP->Range.End = L-1;

	ADP = CRangeP->ADP;
	SlotsUsed = CRangeP->SlotsUsed;
	ZapCount = 0;
	RP = (SC_PRangePointer)&ADP->Data[0];
	for (I = 0; I < SlotsUsed; I++, RP++) {
	    if (R < RP->Beg) break;	// Gone past it!
	
	    // Breaking a pre-existing PRange into two!
	    if ((RP->Beg < L) && (R < RP->End))
		goto AddOne;

	    // Trim individual PRange *OR* mark them for deletion
	    if ((BegIsIn = ((L <= RP->Beg) && (RP->Beg <= R))))
		RP->Beg = R+1;		// May be zapped later!
	    if ((EndIsIn = ((L <= RP->End) && (RP->End <= R))))
		RP->End = L-1;		// May be zapped later!

	    if (BegIsIn && EndIsIn) {	// Yeah, should be zapped!
		if (! ZapCount++)	// ZapI will be FIRST in series
		    ZapI = I;
	    }
	}

	if (ZapCount) {
	    memmove(&ADP->Data[ZapI], &ADP->Data[ZapI+ZapCount], (SlotsUsed - (ZapI+ZapCount)) * sizeof(Uns64));
	    CRangeP->SlotsUsed = SlotsUsed - ZapCount;
	}
	return;

    AddOne:
        {
	    Uns32	SlotCount = ADP->SlotCount;
	
	    if (SlotsUsed == SlotCount) {	// Grow
		SlotCount += SlotCount;		// Double the size
		ADP = SC_ArrResizeData(VMP, ADP, SlotCount, 0);
		CRangeP->ADP = ADP;		// In case it moved (often)
		RP = (SC_PRangePointer)&ADP->Data[I];
	    }

	    // Move one slot down from I to the end... RP gets duped!
	    memmove(RP+1, RP, (SlotsUsed - I) * sizeof(Uns64));	    
	    CRangeP->SlotsUsed = SlotsUsed + 1;
	    RP->End = L-1;
	    (RP+1)->Beg = R+1;
	}
    }

    // SC_MPCharAddToCRange will add chars from L to R (inclusive unicode)
    // to the CRange.
    //
    // Adding L-R may result in the following:
    // -- Makes one big solid PRange... full overlap (N -> 1)
    // -- Adds to Beg or End of an existing PRange (N -> N)
    // -- Abuts 2 PRanges... merging them into one! (N -> N-1)
    // -- Overlaps N intermediate PRanges... merging all into one! (N -> N-M)
    // -- Introduces a new PRange in the middle of a larger gap. (N -> N+1)
    //
    // Complex algorithm, somewhat *HARD* to follow... CAUTION!!

    void SC_MPCharAddToCRange(SC_VMPointer VMP, Uns32 L, Uns32 R)
    {
	SC_PatCRangePointer	CRangeP = (SC_PatCRangePointer)SC_MPFirstPatP->Data[1];
	Uns32			SlotCount, SlotsUsed, I;
	Uns32			LIndex, RIndex;
	Int16			LBefore, RAfter;
	Int32			Delta;		// Change in number of ranges
	SC_ArrDataPointer	ADP;
	SC_PRangePointer	RP;

	// Create a CRangeP if have none.
	if (CRangeP == NULL) {
	    SC_MPFirstPatP->Data[1] = CRangeP = (SC_PatCRangePointer)SC_MPAuxPatNew(VMP, SC_PAT_CRANGE);
	    SC_ArrAllocNewData(VMP, &CRangeP->ADP, SC_PATCRANGESLOTMIN, 1);
	    CRangeP->SlotsUsed = 0LL;
	    CRangeP->Range.Beg = (Uns32)-1L;	// Greatest possible value.
	}
	
	ADP = CRangeP->ADP;
	RP = (SC_PRangePointer)&ADP->Data[0];

	if ((LBefore = (L < CRangeP->Range.Beg)))	// New BEG
	    CRangeP->Range.Beg = L;
	if ((RAfter = (CRangeP->Range.End < R)))	// New END
	    CRangeP->Range.End = R;
	// Check for a full overlap... easy to do here!
	if (LBefore && RAfter) {
	    RP->Beg = L, RP->End = R;
	    CRangeP->SlotsUsed = 1;
	    return;
	}

	SlotCount = ADP->SlotCount;
	SlotsUsed = CRangeP->SlotsUsed;
	LBefore = RAfter = 0;
	LIndex = RIndex = (Uns32)-1L;		// Impossible values
	for (I = 0; I < SlotsUsed; I++, RP++) {
	    if (LIndex == (Uns32)-1L) {
		if (L < RP->Beg)
		    LIndex = I, LBefore = 1;
		else if (L <= RP->End + 1)	// +1 avoids abutting PRanges
		    LIndex = I, LBefore = 0;
	    }

	    if (RIndex == (Uns32)-1L) {
		if (R <= RP->Beg - 1) {		// -1 avoids abutting PRanges
		    RIndex = I, RAfter = 1;	// After Prev one
		    break;
		} else if (R < RP->End) {
		    RIndex = I, RAfter = 0;
		    break;
		}
	    }
	}
	
	if (LIndex == (Uns32)-1L) {
	    LIndex = RIndex = I;		// Before all the slots
	    LBefore = RAfter = 1;
	} else if (RIndex == (Uns32)-1L) {
	    RIndex = I;				// After all the slots
	    RAfter = 1;
	}
	Delta = LIndex - RIndex;		// Change to number of PRanges in array
	if (RAfter)				// Adjust Delta up or down 1
	    Delta += (LBefore) ? 1 : -1;

	// Delta ranges from 1 to -N
	if (Delta > 0) {
	    // Delta is 1 only if new PRange is added... no overlap!
	    if (SlotsUsed == SlotCount) {	// Grow
		SlotCount += SlotCount;		// Double the size
		ADP = SC_ArrResizeData(VMP, ADP, SlotCount, 0);
		CRangeP->ADP = ADP;		// In case it moved (often)
	    }

	    // Move one slot down from LIndex to the end...
	    RP = (SC_PRangePointer)&ADP->Data[LIndex];
	    memmove(RP+1, RP, (SlotsUsed - LIndex) * sizeof(Uns64));
	    SlotsUsed += 1;
	    RP->Beg = L;
	    RP->End = R;

	} else {
	    // Delta is 0..-N.  New PRange overlaps 1 or more existing ones.  So expand LIndex range.
	    RP = (SC_PRangePointer)&ADP->Data[LIndex];
	    if (LBefore) RP->Beg = L;
	    RP->End = (RAfter)
		    ? R							// Expand range to R
		    : (*(SC_PRangePointer)&ADP->Data[RIndex]).End;	// Expand range to include RIndex range
		
	    if (Delta < 0) {
		// Remove PRanges that were overlapped/bridged.
		LIndex += 1;			// Move DEST
		RIndex = LIndex - Delta;	// Move SOURCE
		if (RIndex < SlotsUsed)		// Anything to move?
		    memmove(&ADP->Data[LIndex], &ADP->Data[RIndex], (SlotsUsed - RIndex) * sizeof(Uns64));
		SlotsUsed += Delta;
	    }
	}

	CRangeP->SlotsUsed = SlotsUsed;
    }

    // SC_MPCharUnionPat is called to add an existing CHAR Pat to the one being built.
    // OtherPatP is an SC_PAT_CHAR first node!
    
    void SC_MPCharUnionPat(SC_VMPointer VMP, SC_PatPointer OtherPatP, Uns8 IsNeg)
    {
	SC_PatCMapPointer	MapPatP;
	SC_PatCRangePointer	RangePatP;
	
	// Union in the CMap... if there is one!
	if ((MapPatP = (SC_PatCMapPointer)OtherPatP->Data[0]))
	    SC_MPCharUnionCMap(VMP, MapPatP, IsNeg);

	// Union in the CRange... if there is one!
	if ((RangePatP = (SC_PatCRangePointer)OtherPatP->Data[1])) {
	    SC_ArrDataPointer	ADP = RangePatP->ADP;
	    SC_PRangePointer	CurRangeP = (SC_PRangePointer)&ADP->Data[0];
	    SC_PRangePointer	EndRangeP = CurRangeP + RangePatP->SlotsUsed;

	    if (IsNeg) {
		while (CurRangeP < EndRangeP) {
		    SC_MPCharDelFromCRange(VMP, CurRangeP->Beg, CurRangeP->End);
		    CurRangeP += 1;
		}

	    } else {
		while (CurRangeP < EndRangeP) {
		    SC_MPCharAddToCRange(VMP, CurRangeP->Beg, CurRangeP->End);
		    CurRangeP += 1;
		}
	    }
	}
    }

    // SC_MPCharUTF8toUni converts a multi-byte UTF8 char (CP) to its Uns32 Unicode.
    // Will return the number of bytes in the UTF8 char: 1 to 4.
    
    Uns8	SC_MPCharUTF8ToUni(char *CP, Uns32 *UP)
    {
	char	C = *CP++;
	Uns32	U;

	if ((Uns8)C < 192) {
	    *UP = (Uns8)C;
	    return 1;
	}
	
	if ((Uns8)C < 224) {	// 2 bytes
	    U = C & 0x1F;
	    U = (U << 6) + (*CP & 0x3F);
	    *UP = U;
	    return 2;
	}

	if ((Uns8)C < 240) {	// 3 bytes
	    U = C & 0x0F;
	    U = (U << 6) + (*CP++ & 0x3F);
	    U = (U << 6) + (*CP & 0x3F);
	    *UP = U;
	    return 3;
	}

	// 4 bytes
	U = C & 0x07;
	U = (U << 6) + (*CP++ & 0x3F);
	U = (U << 6) + (*CP++ & 0x3F);	
	U = (U << 6) + (*CP & 0x3F);
	*UP = U;
	return 4;
    }

    // SC_MPCharUnionStr will process a Str char specification for the CHAR Pat.
    //
    // Goes through all the chars in the String... handles UTF-8 too.
    // Ranges are given as "x-z"... use '-' as first char if NOT range!
    // Can handle strings of the form "abcd-ghij-l" etc.
    
    void SC_MPCharUnionStr(SC_VMPointer VMP, SC_StrDataPointer SDP, Uns8 IsNeg)
    {
	Uns32		CharLen;			// 1-4!
	char *		SP = SDP->Data;
	char *		LastP = SP + SDP->StrLen;
	Uns32		LChar, RChar;			// Assuming range

	while (SP < LastP) {
	    LChar = 0;
	    SP += SC_MPCharUTF8ToUni(SP, &LChar);	// Could be a ASCII too!	    
	    RChar = LChar;

	    if ((*SP == '-') && (++SP < LastP)) {
		CharLen = SC_MPCharUTF8ToUni(SP, &RChar);
		// Ignore '-' if Range is backwards!
		if (RChar > LChar)
		    SP += CharLen;
		else
		    RChar = LChar;
	    }

	    // Have the LChar or range LChar-RChar.
	    if (LChar < 0x0080) {
		if (RChar < 0x0080)
		    SC_MPCharAddToCMap(VMP, (char)(Uns8)LChar, (char)(Uns8)RChar, IsNeg);
		else {
		    // Crosses from 1-byte to N-byte UTF-8
		    SC_MPCharAddToCMap(VMP, (Uns8)LChar, 0x007F, IsNeg);
		    if (IsNeg)
			SC_MPCharDelFromCRange(VMP, 0x0080, RChar);
		    else
			SC_MPCharAddToCRange(VMP, 0x0080, RChar);
		}
	    } else {
		if (IsNeg)
		    SC_MPCharDelFromCRange(VMP, LChar, RChar);
		else
		    SC_MPCharAddToCRange(VMP, LChar, RChar);
	    }
	}
    }

    // SC_MPAuxAddChar adds DataP (Str or Pat) to the CHAR Pat being built.

    Int16	SC_MPAuxAddChar(SC_VMPointer VMP, void * DataP, Uns8 OpFlag)
    {
	Uns8	IsNeg = OpFlag & SC_PAT_NEGFLAG;
    
	if ((*(SC_VarVPointer)DataP).Type == SC_ENTRY_STR) {
	    SC_MPCharUnionStr(VMP, (*(SC_StrHPointer)DataP).StrDataP, IsNeg);
	} else if ((*(SC_VarVPointer)DataP).Type == SC_ENTRY_PAT) {
	    SC_MPCharUnionPat(VMP, (SC_PatPointer)DataP, IsNeg);
	} else
	    return 0;

	return 1;
    }

Int16	SC_SysMakePatChar(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	StackEltP;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    // Make the new first (SC_PAT_CHAR) Pat... reserve the 2 Data
    // for CMap and CRange!
    SC_MPLastPatP = SC_MPFirstPatP = SC_MPAuxPatNew(VMP, SC_PAT_CHAR);
    SC_MPFirstPatP->Flags = SC_PAT_DATA0P | SC_PAT_DATA1P;
    SC_MPNextDataI = 0;		// Irrelevant here!

    StackEltP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    if (! SC_MPAuxGetPatArgs(VMP, StackEltP, SC_PatStrArr[SC_PAT_CHAR], SC_PAT_CHAR, InCount, 1)) {
	SC_MPAuxPatZap(VMP);	// Drops refs, RefCount/GC will delete it!
	return SC_RUN_BADARG;
    }

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_PTR, SC_MPFirstPatP);
}

// SC_SysMakePatCGo will create a new GO Pat.
// It will simply move the matching process to a new location, whether
// ABS or REL, H (CHARS) or V (Lines).
//
// GO Pat:	Must start with a NUMB/FUNC (where to go).
//		Default is REL
//
//		Numb/Func		Required
//        	#Flag			Abs/Rel H/V
//		#S_POS PVarName		Stashes OldPos/NewPos (NewPos/OldPos if going back)
//		#S_STR PVarName		Stashes STR (OldPos to NewPos) from Buffer
//		#S_CNT PVarName		Stashes movement amount (NewPos - OldPos)
//		#S_ORD PVarName		Stashes 0
//		#S_LEN1 PVarName	Stashes Len1 (NewPos - OldPos (reverse if necessary))
//		#S_NAME Str		Names this pattern
//		...

Int16	SC_SysMakePatGo(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	StackEltP;
    Int64		Val = 0L;
    Uns8		OpFlag = SC_PAT_NOFLAG;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount < 1) {
	SC_RunErrorInfoStr = "Pat.Make Go: Missing arg.";
	return SC_RUN_BADARG;
    }

    StackEltP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    if (StackEltP->ValueType == SC_VAL_INT)
	Val = (Int64)StackEltP->ValueP;
    else if ((StackEltP->ValueType == SC_VAL_PTR) &&
	       ((Val = (Int64)StackEltP->ValueP)) &&	// Assign
	       ((*(SC_VarVPointer)Val).Type == SC_CHUNK_CLOS)) {
	OpFlag = SC_PAT_DATA0P;
	SC_VARINCREF((SC_VarVPointer)Val);
    } else {
	SC_RunErrorInfoStr = "Pat.Make Go: Arg 1 must be INT or FUNC.";
	return SC_RUN_BADARG;
    }

    SC_MPLastPatP = SC_MPFirstPatP = SC_MPAuxPatNew(VMP, SC_PAT_GO);
    SC_MPFirstPatP->Flags = OpFlag;
    SC_MPFirstPatP->Data[0] = (void *)Val;
    SC_MPNextDataI = 1;		// Will never get it

    if (! SC_MPAuxGetPatArgs(VMP, StackEltP+1, SC_PatStrArr[SC_PAT_GO], SC_PAT_GO, InCount-1, 2)) {
	SC_MPAuxPatZap(VMP);	// Drops refs, RefCount/GC will delete it.
	SC_RunErrorInfoStr = "Pat.Make Go: Bad args.";
	return SC_RUN_BADARG;
    }

    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_PTR, SC_MPFirstPatP);    
}


// SC_SysMakePatAt will create a new AT Pat.
// It will succeed if the Match process is AT the given location.
//
// AT Pat:	Must start with NUMB/FUNC (location spec).
//
//		Numb/Func		Required
//					  Pos	   --		(H Abs) Pos from buffer 0 (End if Neg)
//					  Lines	   V		(V Abs) Lines from buffer 0 (End if neg)
//					  Pos	   Rel		(H Rel) Pos from buffer FirstPos (LastPos if Neg)
//					  Lines    V Rel	(V Rel) Lines from buffer FirstPos (from LastPos if Neg)
//		#Flag			H/V Abs/Rel
//					**DOES NOT** stash

Int16	SC_SysMakePatAt(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;
    Uns8		TotCount = (InOutCount >> 8) + InCount;
    SC_VarVPointer	StackEltP;
    Int64		Val;
    Uns8		OpFlag = SC_PAT_NOFLAG;

    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    // Only takes 1 INT arg... followed by optional flags
    if (InCount < 1) {
	SC_RunErrorInfoStr = "Pat.Make At: Missing arg.";
	return SC_RUN_BADARG;
    }

    StackEltP = SC_RUNSTACK_GETNTH(VMP, TotCount);
    if (StackEltP->ValueType == SC_VAL_INT)
	Val = (Int64)StackEltP->ValueP;
    else if ((StackEltP->ValueType == SC_VAL_PTR) &&
	       ((Val = (Int64)StackEltP->ValueP)) &&	// Assign
	       ((*(SC_VarVPointer)Val).Type == SC_CHUNK_CLOS)) {
	OpFlag = SC_PAT_DATA0P;
	SC_VARINCREF((SC_VarVPointer)Val);
    } else {
	SC_RunErrorInfoStr = "Pat.Make At: Arg 1 must be INT or FUNC.";
	return SC_RUN_BADARG;
    }

    SC_MPLastPatP = SC_MPFirstPatP = SC_MPAuxPatNew(VMP, SC_PAT_AT);
    SC_MPFirstPatP->Flags = OpFlag;
    SC_MPFirstPatP->Data[0] = (void *)Val;
    SC_MPNextDataI = 1;		// Will never get it

    if (! SC_MPAuxGetPatArgs(VMP, StackEltP+1, SC_PatStrArr[SC_PAT_AT], SC_PAT_AT, InCount-1, 2)) {
	SC_MPAuxPatZap(VMP);	// Drops refs, RefCount/GC will delete it.
	SC_RunErrorInfoStr = "Pat.Make At: Bad args.";
	return SC_RUN_BADARG;
    }
    
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_PTR, SC_MPFirstPatP);
}

// ****************************************
// PRINT PATTERNS!
//
// SC_PatPrint... SC_PPAuxMain dispatches on the Pat Op and has various functions
// to print specialized patterns.

    typedef enum {
	SC_PPFA_InSen	= 0x00,
	SC_PPFA_Pos,
	SC_PPFA_Str,
	SC_PPFA_Self,
	SC_PPFA_Max,
	SC_PPFA_Abs,
	SC_PPFA_H,
	SC_PPFA_Res,

	SC_PPFA_COUNT
    } SC_PPFA_I;

    typedef struct _SC_PPFlag {
	Uns8	Mask;
	char *	FStrP;
	char *  TStrP;
    } SC_PPFlag, * SC_PPFlagPointer;

    SC_PPFlag	SC_PPFArr[] = {{SC_PAT_SENFLAG, "InSen", "Sen"},	// SC_PPFA_InSen
			       {SC_PAT_NEGFLAG, "Pos", "Neg"},		// SC_PPFA_Pos
			       {SC_PAT_DYNFLAG, "Str", "Dyn"},		// SC_PPFA_Str
			       {SC_PAT_CONSFLAG, "Self", "Cons"},	// SC_PPFA_Self
			       {SC_PAT_MINFLAG, "Max", "Min"},		// SC_PPFA_Max
			       {SC_PAT_ABSFLAG, "Rel", "Abs"},		// SC_PPFA_Abs
			       {SC_PAT_VFLAG, "H", "V"},		// SC_PPFA_H
			       {SC_PAT_NRESFLAG, "Res", "NoRes"}	// SC_PPFA_Res
			      };

    void	SC_PPColumn(SC_VMPointer VMP, Uns16 Level, Int16 IsNext, Uns8 Lead)
    {
	Uns16		L = 0;
	SC_PrintFuncP	OutPFP = VMP->OutPrintFP;

	OutPFP("%*s", Lead, "");
	while (L++ < Level)
	    OutPFP("    | ");
	if (IsNext)
	    OutPFP("   ");
    }

    void	SC_PPAuxMain(SC_VMPointer VMP, SC_PatPointer PatP, Uns16 Level, Uns8 MainPatOp, Uns8 Lead);    

    void	SC_PPAuxFlags(SC_VMPointer VMP, Uns8 Op, Uns8 Flags)
    {

	SC_PrintFuncP	OutPFP = VMP->OutPrintFP;
	
	// Get Flag String...
	#define		SC_GFS(_I) ((SC_PPFArr[_I].Mask & Flags) ? SC_PPFArr[_I].TStrP : SC_PPFArr[_I].FStrP)
	
	    switch (Op) {
		case SC_PAT_SEQ:
		    OutPFP(" <%s %s %s>\n",
			     SC_GFS(SC_PPFA_InSen), SC_GFS(SC_PPFA_Pos), SC_GFS(SC_PPFA_Str));
		    break;

		case SC_PAT_ALT:
		    OutPFP(" <%s %s %s %s>\n",
			     SC_GFS(SC_PPFA_InSen), SC_GFS(SC_PPFA_Pos),
			     SC_GFS(SC_PPFA_Str), SC_GFS(SC_PPFA_Self));
		    break;

		case SC_PAT_SPAN:
		case SC_PAT_REP:
		    OutPFP(" <%s %s %s %s %s>\n",
			     SC_GFS(SC_PPFA_InSen), SC_GFS(SC_PPFA_Pos), SC_GFS(SC_PPFA_Str),
			     SC_GFS(SC_PPFA_Max), SC_GFS(SC_PPFA_Self));
		    break;

		case SC_PAT_CHAR:
		    OutPFP(" <%s>\n", SC_GFS(SC_PPFA_Pos));
		    break;

		case SC_PAT_GO:
		    OutPFP(" <%s %s>\n", SC_GFS(SC_PPFA_H), SC_GFS(SC_PPFA_Abs));
		    break;

		case SC_PAT_AT:
		    OutPFP(" <%s %s>\n", SC_GFS(SC_PPFA_H), SC_GFS(SC_PPFA_Abs));
		    break;
	    }
	    
	#undef SC_GFS
    }
    
    void	SC_PPAuxBody(SC_VMPointer VMP, SC_PatPointer PatP, Uns16 Level, Int16 IsNext, Int16 DoData0, Uns8 Lead)
    {
	#define	_STRLEN		48
    
	SC_PatPointer		NextP;
	Int16			I = 0;
	Uns8			Mask = SC_PAT_DATA0P;
	SC_PrintFuncP		OutPFP = VMP->OutPrintFP;
	char			S[_STRLEN];

	while (I < 2) {
	    if (DoData0 && (PatP->Flags & Mask) && PatP->Data[I]) {
		if ((*(SC_VarVPointer)PatP->Data[I]).Type == SC_ENTRY_PAT) {
		    SC_PPColumn(VMP, Level, IsNext, Lead);
		    OutPFP("Data%d:[--Pat--]\n", I);
		    SC_PPAuxMain(VMP, (SC_PatPointer)PatP->Data[I], Level+1, 0, Lead);
		    
		} else if ((*(SC_StrHPointer)PatP->Data[I]).Type == SC_ENTRY_STR) {
		    SC_PPColumn(VMP, Level, IsNext, Lead);
		    SC_StrStuffStr(S, (*(SC_StrHPointer)PatP->Data[I]).StrDataP->Data, _STRLEN);
		    OutPFP("Data%d:\"%s\"\n", I, S);

		} else if ((*(SC_VarVPointer)PatP->Data[I]).Type == SC_CHUNK_CLOS) {
		    SC_PPColumn(VMP, Level, IsNext, Lead);
		    OutPFP("Data%d:", I);
		        SC_SysWriteVP(VMP, PatP->Data[I], OutPFP);
		    OutPFP("\n");
		}
	    }

	    DoData0 = 1, I++;
	    Mask = SC_PAT_DATA1P;
	}

	if (! IsNext) {
	    NextP = PatP->NextP;
	    while (NextP) {
		SC_PPAuxMain(VMP, NextP, Level, PatP->PatOp, Lead);
		NextP = NextP->NextP;
	    }
	}

	#undef _STRLEN
    }

    // Draws two lines, Line 0 is from 0 to 63 and line 1 if from 64 to 127.
    // Draws * for chars below 33 (ctrl chars cannot be drawn) and 127 (Del).
    // If '-' itself is in the set, it is drawn as an '*'!!
    //
    // NOTE:  "A-Z" in Char pat means ALL CHARS from A to Z.
    //	      "-AZ" means 3 individual chars, not a range.
    void SC_PPCMap(SC_VMPointer VMP, Uns8 * DataP, Uns16 Level, Uns8 Lead)
    {
	char	S[64];
	char *	SP;
	Uns8 *	DP;
	Uns8	Mask, I, J;
	char	C;
	Uns8	Line;

	C = 0, DP = DataP;
	for (Line = 0; Line < 2; Line++) {
	    SP = S;
	    for (I = 0; I < 8; I++, DP++) {
		Mask = 0x80;
		for (J = 0; J < 8; J++, SP++, C++) {
		    if ((C < 33) || (C == '-') || (C == 127))
			*SP = (*DP & Mask) ? '*' : '-';
		    else
			*SP = (*DP & Mask) ? C : '-';
		    Mask >>= 1;
		}
	    }
	    SC_PPColumn(VMP, Level, 0, Lead);
	    VMP->OutPrintFP("   [%.*s]\n", 64, S);
	}
    }

    // Prints "i-j (0x00a1 - 0x00a5) m (0x00a9)..."
    // Requires 11 chars for a singleton and 22 chars for a range.
    //
    // NOTE:  UNICODE is stored, *NOT* UTF-8 !!
    void SC_PPCRange(SC_VMPointer VMP, SC_PatCRangePointer CRP , Uns16 Level, Uns8 Lead)
    {
	SC_ArrDataPointer	ADP = CRP->ADP;
	Uns32			SlotCount = CRP->SlotsUsed;
	Uns32			From, To;
	char			FromS[4], ToS[4];
	Uns16			FromLen, ToLen;
	Int16			SLen;
	Uns32			Index;
	Uns32 *			DP;
	SC_PrintFuncP		OutPFP = VMP->OutPrintFP;
    
	#define	SC_PPCRANGE_LINELEN	80
	#define	SC_PPCRANGE_SINGLEN	11
	#define	SC_PPCRANGE_PAIRLEN	22

	    Index = 0, DP = (Uns32 *)&ADP->Data[0], SLen = 0;
	    while (Index < SlotCount) {
		From = *DP++, To = *DP++;
		if (From == To) {			// Singleton

		    if (SLen > SC_PPCRANGE_LINELEN - SC_PPCRANGE_SINGLEN)
			SLen = 0, OutPFP("\n");
		    if (SLen) OutPFP(" ");
		    else SC_PPColumn(VMP, Level, 0, Lead), OutPFP("   ");

		    SLen += SC_PPCRANGE_SINGLEN;
		    FromLen = SC_UtilWriteUniChar(From, FromS);
		    OutPFP("%.*s (0x%04x)", FromLen, FromS, From);

		} else {				// Range

		    if (SLen > SC_PPCRANGE_LINELEN - SC_PPCRANGE_PAIRLEN)
			SLen = 0, OutPFP("\n");
		    if (SLen) OutPFP(" ");
		    else SC_PPColumn(VMP, Level, 0, Lead), OutPFP("   ");

		    SLen += SC_PPCRANGE_PAIRLEN;
		    FromLen = SC_UtilWriteUniChar(From, FromS);
		    ToLen = SC_UtilWriteUniChar(To, ToS);
		    OutPFP("%.*s-%.*s (0x%04x - 0x%04x)", FromLen, FromS, ToLen, ToS, From, To);
		}
		Index += 1;
	    }
	    OutPFP("\n");

	#undef	SC_PPCRANGE_LINELEN
	#undef	SC_PPCRANGE_SINGLEN
	#undef	SC_PPCRANGE_PAIRLEN
    }

    void	SC_PPAuxMain(SC_VMPointer VMP, SC_PatPointer PatP, Uns16 Level, Uns8 MainPatOp, Uns8 Lead)
    {
	SC_PrintFuncP	OutPFP = VMP->OutPrintFP;

	(MainPatOp == 0xFF) ? OutPFP(" ") : SC_PPColumn(VMP, Level, 0, Lead);

	if (PatP->PatOp < SC_PATCOUNT) OutPFP("%s ", SC_PatStrArr[PatP->PatOp]);
	switch (PatP->PatOp) {
	    case SC_PAT_SEQ:
	    case SC_PAT_ALT:
	    case SC_PAT_SPAN:
		    SC_SysWriteVP(VMP, (SC_VarVPointer)PatP, OutPFP);
		    SC_PPAuxFlags(VMP, PatP->PatOp, PatP->Flags);		
		    SC_PPAuxBody(VMP, PatP, Level, 0, 1, Lead);
		break;

	    case SC_PAT_REP:
		SC_SysWriteVP(VMP, (SC_VarVPointer)PatP, OutPFP);
		SC_PPAuxFlags(VMP, SC_PAT_REP, PatP->Flags);
		if (! PatP->Flags & SC_PAT_DATA0P) {
		    SC_PPColumn(VMP, Level, 0, Lead);
		    OutPFP("Data0:(Min) %ld\n", (Uns64)PatP->Data[0]);
		}
		if (! (PatP->Flags & SC_PAT_DATA1P)) {
		    SC_PPColumn(VMP, Level, 0, Lead);
		    OutPFP("Data1:(Max) %ld\n", (Uns64)PatP->Data[1]);
		}
		SC_PPAuxBody(VMP, PatP, Level, 0, 1, Lead);	// If had FUNC for Min/Max
		break;

	    case SC_PAT_CHAR:
		 SC_SysWriteVP(VMP, (SC_VarVPointer)PatP, OutPFP);
		if (PatP->Data[0] && PatP->Data[1])
		    OutPFP("ASCII + Unicode/UTF-8");
		else {
		    if (PatP->Data[0]) OutPFP(" ASCII");
		    if (PatP->Data[1]) OutPFP(" Unicode/UTF-8");
		}
		SC_PPAuxFlags(VMP, SC_PAT_CHAR, PatP->Flags);				
		SC_PPAuxBody(VMP, PatP, Level, 0, 1, Lead);
		break;

	    case SC_PAT_GO:
		SC_SysWriteVP(VMP, (SC_VarVPointer)PatP, OutPFP);
		SC_PPAuxFlags(VMP, SC_PAT_GO, PatP->Flags);
		if (! (PatP->Flags & SC_PAT_DATA0P)) {
		    SC_PPColumn(VMP, Level, 0, Lead);
		    OutPFP("Data0:%ld\n", PatP->Data[0]);
	        }
		SC_PPAuxBody(VMP, PatP, Level, 0, 1, Lead);	// If FUNC in Data[0]
		break;

	    case SC_PAT_AT:
		SC_SysWriteVP(VMP, (SC_VarVPointer)PatP, OutPFP);
		SC_PPAuxFlags(VMP, SC_PAT_AT, PatP->Flags);
		if (! (PatP->Flags & SC_PAT_DATA0P)) {
		    SC_PPColumn(VMP, Level, 0, Lead);
		    OutPFP("Data0:%ld\n", PatP->Data[0]);
		}
		SC_PPAuxBody(VMP, PatP, Level, 0, 1, Lead);	// If FUNC in Data[0]
		break;

	    case SC_PAT_EXTR:
		OutPFP(" + Extr ");
		SC_SysWriteVP(VMP, (SC_VarVPointer)PatP, OutPFP);
		SC_PPAuxFlags(VMP, MainPatOp, PatP->Flags);
		SC_PPAuxBody(VMP, PatP, Level, 1, 1, Lead);
		break;

	    case SC_PAT_CMAP:
		OutPFP("CMap ");
		SC_SysWriteVP(VMP, (SC_VarVPointer)PatP, OutPFP);
		OutPFP("\n");
		SC_PPCMap(VMP, (*(SC_PatCMapPointer)PatP).Data, Level, Lead);
		break;
	    
	    case SC_PAT_CRANGE:
		OutPFP("CRange ");
		SC_SysWriteVP(VMP, (SC_VarVPointer)PatP, OutPFP);
		OutPFP("Unicode:(0x%04x - 0x%04x)\n", (*(SC_PatCRangePointer)PatP).Range.Beg,
							      (*(SC_PatCRangePointer)PatP).Range.End);
		SC_PPCRange(VMP, (SC_PatCRangePointer)PatP, Level, Lead);
		break;

	    case SC_PAT_STASH_STRS:
		OutPFP(" + StashStrs ");
		SC_SysWriteVP(VMP, (SC_VarVPointer)PatP, OutPFP);
		OutPFP("\n");
		SC_PPAuxBody(VMP, PatP, Level, 1, 1, Lead);
		break;

	    case SC_PAT_STASH_NUMBS:
		OutPFP(" + StashNums ");
		SC_SysWriteVP(VMP, (SC_VarVPointer)PatP, OutPFP);
		OutPFP("\n");
		SC_PPAuxBody(VMP, PatP, Level, 1, 1, Lead);
		break;

	    case SC_PAT_STASH_LENS:
		OutPFP(" + StashLens ");
		SC_SysWriteVP(VMP, (SC_VarVPointer)PatP, OutPFP);
		OutPFP("\n");
		SC_PPAuxBody(VMP, PatP, Level, 1, 1, Lead);
		break;

	    case SC_PAT_STASH_LABEL:
		OutPFP(" + StashLabel ");
		SC_SysWriteVP(VMP, (SC_VarVPointer)PatP, OutPFP);
		OutPFP("\n");
		SC_PPAuxBody(VMP, PatP, Level, 1, 1, Lead);
		break;
	}
    }


void	SC_PatPrint(SC_VMPointer VMP, SC_PatPointer PatP, Uns8 Alone, Uns8 Lead)
{
    if (Alone) VMP->OutPrintFP("\n");
    SC_PPAuxMain(VMP, PatP, 0, 0xFF, Lead);
}

char *	SC_PatGetLabel(SC_PatPointer PP)
{
    PP = PP->NextP;
    while (PP) {
	if (PP->PatOp < SC_PAT_STASH_STRS) break;		
	if (PP->PatOp == SC_PAT_STASH_LABEL)
	    return (*(SC_StrHPointer)PP->Data[0]).StrDataP->Data;
	PP = PP->NextP;
    }

    return NULL;
}

// ****************************************
// MATCH PATTERNS

    // Set SC_DEBUG_DISP_MATCH to show every match step!
    // Can generate MANY lines...
    #if SC_DEBUG_DISP_MATCH
	void		SC_MA_PL(Uns32	L)			// Print Level
	{
	    printf("    ");
	    while (L--) printf("| ");
	}

	void		SC_MA_PRINT_PAT(SC_PatPointer PP, char * StrP)
	{
	    char *	NameP = SC_PatGetLabel(PP);

	    if (NameP)
		printf("Pat:%s \"%s\" From:%d\n", StrP, NameP, SC_SMP_Pos);
	    else
		printf("Pat:%s From:%d\n", StrP, SC_SMP_Pos);
	}

	#define		SC_MATCH_ANNOUNCE			printf("\nBegin Matching... From:%d\n", SC_SMP_Pos)
	#define		SC_MATCH_CONCLUDE(_R)			printf("\nMatch %s... Steps:%d Recursions:%d\n",			\
									(_R) ? "Succeeded" : "Failed", SC_SMP_StepCount, SC_SMP_MaxConsL+1);
	#define		SC_MATCHPAT_ANNOUNCE(_PP, _P, _L)	{SC_MA_PL(_L); SC_MA_PRINT_PAT(_PP, _P);}
	
	#define		SC_MATCHSTASHRANGE_ANNOUNCE(_KP, _V, _L)								\
			if (_KP) {SC_MA_PL(_L);											\
			    printf("|-- Stash Range in \"%s\" [%ld--%ld]\n",							\
				  (*(SC_StrHPointer)_KP).StrDataP->Data, _V & 0x00FFFFFFFF, _V >> 32);}

	// _S is (Int64)SC_StrHPointer
	#define		SC_MATCHSTASHSTR_ANNOUNCE(_KP, _S, _L)									\
			if (_KP) {SC_MA_PL(_L);											\
			    if ((*(SC_StrHPointer)_S).StrDataP->StrLen < 24)							\
				printf("|-- Stash Str in \"%s\" [%s]\n", (*(SC_StrHPointer)_KP).StrDataP->Data,			\
					(*(SC_StrHPointer)_S).StrDataP->Data);							\
			    else												\
				printf("|-- Stash Str in \"%s\" [%.*s...]\n",							\
				       (*(SC_StrHPointer)_KP).StrDataP->Data, 20, (*(SC_StrHPointer)_S).StrDataP->Data);}
			 

	#define		SC_MATCHSTASH_ANNOUNCE(_S, _KP, _V, _L)									\
			if (_KP) {SC_MA_PL(_L);											\
			    printf("|-- Stash %s in \"%s\" [%ld]\n", _S, (*(SC_StrHPointer)_KP).StrDataP->Data, _V);}
	
    #else
	#define		SC_MATCH_ANNOUNCE				// Nothing
	#define		SC_MATCH_CONCLUDE(_R)				// Nothing
	#define		SC_MATCHPAT_ANNOUNCE(_PP, _P, _L)		// Nothing
	#define		SC_MATCHSTASHRANGE_ANNOUNCE(_KP, _V, _L)	// Nothing
	#define		SC_MATCHSTASHSTR_ANNOUNCE(_KP, _S, _L)		// Nothing
	#define		SC_MATCHSTASH_ANNOUNCE(_S, _KP, _V, _L)		// Nothing
    #endif

    #ifdef SC_DEBUG_DISP_RSTACK
    
	void		SC_SMPAuxRStackPrint(void)
	{
	    Uns32		I = 0;
	    SC_PResPointer	PResP = (SC_PResPointer)SC_SMP_RStackP->ADP->Data;
	    char *		NameP;
	
	    printf("\n\nRStack:\n");
	    while (I < SC_SMP_RStackP->TopI) {
		printf("%03d %4s:%d [%d - %d]", I, SC_PatStrArr[PResP->PatP->PatOp], PResP->Numb, PResP->FromPos, PResP->ToPos);
		if ((NameP = SC_PatGetLabel(PResP->PatP)))	// Assign
		    printf(" \"%s\"\n", NameP);
		else
		    printf("\n");

		++I, ++PResP;
	    }
	}

	// Caller must make sure there *IS* an SC_SMP_RStackP !!
	#define		SC_MATCH_RSTACKPRINT()			SC_SMPAuxRStackPrint()
	
    #else
	#define		SC_MATCH_RSTACKPRINT(_R)			// Nothing
    #endif

    // Set SC_DEBUG_DISP_MSTACK to show the MStack (Matching Stack) at every step.
    #if SC_DEBUG_DISP_MSTACK

	#define	SC_SMPAUXMS_MAXI	9
	#define	SC_SMPAUXRS_MAXI	9	
	#define	SC_SMPAUXMS_SHOWRSTACK	1
	
	Int16	SC_SMPAUXMS_NewLine =	0;		// Only 1 "****FAIL" per MStack line!
	
	void	SC_SMPAuxMStackLine(SC_MStackPointer MSP)
	{
	    Uns32		I;
	    SC_PMatchPointer	PMP;
	    char *		SP;
	    Int16		DoOneLine = 1;
	    Uns32		Count;

	    // Print StepCount (Do a full print every 5 steps)
	    printf("\n%05d", SC_SMP_StepCount);
	    DoOneLine = (SC_SMP_StepCount % 5) != 0;

	    // Indent for MStack L (Recursion Level)
	    if ((I = MSP->ConsL) > 15) I = 16;
	    printf(" %.*sL%02d: ", I, "               >", MSP->ConsL + 1);

	    // PMatches on MStack
	    I = 0;
	    PMP = (SC_PMatchPointer)MSP->ADP->Data;

	    if (DoOneLine) {
		if (MSP->TopI > SC_SMPAUXMS_MAXI) {
		    while (I < MSP->TopI - (SC_SMPAUXMS_MAXI - 2)) PMP++, I++;
		    printf("[+%d..]", I);
		}
	    }

	    Count = 0;
	    while (I < MSP->TopI) {
		if (PMP->MainPatP == NULL) {
		    // CONS Placeholder PMatchRecord
		    // CurPos   --> CONS Level
		    // StartPos --> PMatch to do next (-1 if done)
		    printf("[C:%d->%d]", PMP->CurPos, PMP->StartPos);
		    
		} else {
		    char  SepC = (PMP->State & SC_PMATCH_SRCSTATE) ? '*' : ':';
		    // Real PMatchRecord
		    if ((SP = SC_PatGetLabel(PMP->MainPatP)))	// Assign
			printf("[\"%s\"%c%d", SP, SepC, PMP->Rep);
		    else
			printf("[%s%c%d", SC_PatStrArr[PMP->MainPatP->PatOp], SepC, PMP->Rep);

		    if (PMP->RLastI)
			printf(":%d-%d", PMP->RTopI, PMP->RLastI);

		    if (PMP->CRetI > -1) {
			if (PMP->CRetI == 0)
			    printf("<X");
			else
			    printf("<%d", PMP->CRetI - 1);
		    }

		    if (PMP->CSrcI > -1)
			printf("|%d]", PMP->CSrcI);
		    else
			printf("]");
		}

		Count++, I++, PMP++;
		if ((Count > SC_SMPAUXMS_MAXI) && (I < MSP->TopI)) {
		    if ((Count = MSP->ConsL) > 15) Count = 16;
		    printf("\n         %.*s  ", Count, "               >");
		    Count = 0;
		}
	    }

	    // finish with Pos
	    printf("->%d", SC_SMP_Pos);

	    SC_SMPAUXMS_NewLine = 1;
	}

	void	SC_SMPAuxRStackLine(SC_MStackPointer MSP, char * ActStr)
	{
	    Uns32		I;

	    // 1 to print the RStack, 0 to skip... RStack is in Op/Relative form!
	    if (SC_SMPAUXMS_SHOWRSTACK && SC_SMP_RStackP && SC_SMP_RStackP->TopI) {

		SC_PResPointer	PResP = (SC_PResPointer)SC_SMP_RStackP->ADP->Data;
		char *		NameP;

		// Indent for MStack L (Recursion Level)
		if ((I = MSP->ConsL) > 15) I = 16;
		printf("\n        %.*s  ", I, "               >");

		I = 1;
		printf(" R(%s):", ActStr);
		if (SC_SMP_RStackP->TopI > SC_SMPAUXRS_MAXI) {
		    while (I < SC_SMP_RStackP->TopI - (SC_SMPAUXRS_MAXI - 2)) PResP++, I++;
		    printf("[+%d..]", I-1);
		}

		while (I <= SC_SMP_RStackP->TopI) {

		    printf(" %s:%d", SC_PatStrArr[PResP->PatP->PatOp], PResP->Numb);
		    // This one draws the From/To... a little harder to read.
		    // printf("%s:%d[%d-%d] ", SC_PatStrArr[PResP->PatP->PatOp], PResP->Numb, PResP->FromPos, PResP->ToPos);
		    if ((NameP = SC_PatGetLabel(PResP->PatP)))	// Assign
			printf("\"%s\"", NameP);

		    ++I, ++PResP;
		}
	    }	    


	}

	void	SC_SMPAuxMStackFail(SC_MStackPointer MSP)
	{
	    if (SC_SMPAUXMS_NewLine)
		printf(" ***** FAIL");
	    SC_SMPAUXMS_NewLine = 0;
	}

	#define		SC_MSTACK_START()		printf("Starting Match at %d...\n", SC_SMP_Pos);
	#define		SC_MSTACK_LINE(_M)		SC_SMP_StepCount++, SC_SMPAuxMStackLine(_M)
	#define		SC_RSTACK_LINE(_M, _S)		SC_SMPAuxRStackLine(_M, _S)
	#define		SC_MSTACK_FAIL(_M)		SC_SMPAuxMStackFail(_M)
	#define		SC_MSTACK_FINAL(_M,_R)		printf("\n\nMStack(Res:%ld)... Steps:%d  Levels:%d\n",		\
								_R, SC_SMP_StepCount, SC_SMP_MaxConsL+1);	
    #else
	#define		SC_MSTACK_START()
	#define		SC_MSTACK_LINE(_M)		SC_SMP_StepCount++
	#define		SC_RSTACK_LINE(_M, _S)
	#define		SC_MSTACK_FAIL(_M)
	#define		SC_MSTACK_FINAL(_M,_R)
    #endif

    void	SC_SMPAuxDictStash(SC_VarVPointer KeyP, SC_VarVPointer ValueP, Uns8 KeyType, Uns8 ValType)
    {
	SC_PairHPointer		PairP;

	if ((PairP = SC_DictGetPair(SC_SMP_PDictP, KeyP, KeyType))) {
	    if ((PairP->ValueP != ValueP) || (PairP->ValType != ValType)) {
		// Existing Pair... but wrong Value... put in new Value!
		SC_VARDEREFCOND(SC_SMP_VMP, (PairP->ValType == SC_VAL_PTR), PairP->ValueP);
		PairP->ValType = ValType;
		PairP->ValueP = ValueP;
		// Assume new ValueP already has a +1 RefCount!
	    }

	} else {
	    // Build a new Pair
	    PairP = SC_BigEntryNew(SC_SMP_VMP);
	    PairP->KeyType = KeyType;
	    PairP->KeyP = KeyP;
	    SC_VARREFCOND((KeyType == SC_VAL_PTR), KeyP);
	    
	    PairP->ValType = ValType;
	    PairP->ValueP = ValueP;
	    // Assume new ValueP already has a +1 RefCount!
	    SC_DictStashNewPair(SC_SMP_VMP, SC_SMP_PDictP, PairP);
	}
    }

    Int16	SC_SMPAuxString(SC_VarVPointer VP, Int16 InSen)
    {
	char *	StrP = (*(SC_StrHPointer)VP).StrDataP->Data;
	Uns32	StrLen = (*(SC_StrHPointer)VP).StrDataP->StrLen;

	if ((SC_SMP_Pos + StrLen < SC_SMP_LastPos) &&
    	    (ED_BufferMatchFromPos(SC_SMP_BufP, SC_SMP_Pos, StrP, (Int32)StrLen, InSen))) {
	    SC_SMP_Pos += StrLen;
	    return 1;
	}

	return 0;
    }

    // CRangePatP may be NULL!
    Int16	SC_MPUCharInCRange(SC_PatCRangePointer CRangePatP, Uns32 U)
    {
	SC_PRangePointer	RP, EndRP;

	if (CRangePatP && ((CRangePatP->Range.Beg <= U) || (U <= CRangePatP->Range.End))) {
	    RP = (SC_PRangePointer)CRangePatP->ADP->Data;
	    EndRP = RP + CRangePatP->SlotsUsed;

	    while (++RP < EndRP) {
		if (U < RP->Beg)
		    return 0;
		if (U <= RP->End)
		    return 1;
	    }
	}

	return 0;
    }


    void	SC_SMPAuxMStackAlloc(SC_MStackPointer MSP)
    {
	MSP->TopI = 0;
	MSP->ConsL = 0;

	MSP->MaxI = SC_SMP_MSTACK_INITBYTES / sizeof(SC_PMatchRecord);
	SC_ArrAllocNewData(SC_SMP_VMP, &MSP->ADP, SC_SMP_MSTACK_INITBYTES / sizeof(void *), 0);
    }

    SC_RStackPointer	SC_SMPAuxRStackAlloc(SC_RStackPointer RSP)
    {
	RSP->TopI = 0;
	RSP->MaxI = SC_SMP_RSTACK_INITBYTES / sizeof(SC_PResRecord);
	SC_ArrAllocNewData(SC_SMP_VMP, &RSP->ADP, SC_SMP_RSTACK_INITBYTES / sizeof(void *), 0);

	return RSP;
    }

    void	SC_SMPAuxMStackGrow(SC_MStackPointer MSP)
    {
	Uns32		SlotCount;

	SlotCount = (MSP->ADP->BlockLen + SC_SMP_MSTACK_EXTRABYTES) / sizeof(void *);
	MSP->ADP = SC_ArrResizeData(SC_SMP_VMP, MSP->ADP, SlotCount, 0);
	MSP->MaxI = SlotCount / SC_SMP_MSTACK_SLOTSPER;
    }

    void	SC_SMPAuxRStackGrow(void)
    {
	Uns32		SlotCount;

	SlotCount = (SC_SMP_RStackP->ADP->BlockLen + SC_SMP_RSTACK_EXTRABYTES) / sizeof(void *);
	SC_SMP_RStackP->ADP = SC_ArrResizeData(SC_SMP_VMP, SC_SMP_RStackP->ADP, SlotCount, 0);
	SC_SMP_RStackP->MaxI = SlotCount / SC_SMP_RSTACK_SLOTSPER;
    }

    void	SC_SMPAuxMStackPush(SC_MStackPointer MSP, SC_PatPointer MainPatP, SC_PatPointer CurPatP, Uns32 StartPos, Uns16 PatL)
    {
	SC_PMatchPointer	PMP = (SC_PMatchPointer)(MSP->ADP->Data) + MSP->TopI++;

	PMP->MainPatP = MainPatP;
	PMP->CurPatP = CurPatP;
	PMP->StartPos = StartPos;
	PMP->CurPos = 0L;
	PMP->MinRep = PMP->MaxRep = PMP->Rep = 0L;
	PMP->SRep = -1L;
	PMP->RTopI = PMP->RLastI = PMP->RTempI = 0L;
	PMP->CRetI = PMP->CSrcI = -1L;
	PMP->PatL = PatL;
	PMP->I = 0;
	PMP->State = SC_PMATCH_INITSTATE;

	if (MSP->TopI >= MSP->MaxI)
	    SC_SMPAuxMStackGrow(MSP);
    }

    void	SC_SMPAuxRStackPush(SC_PMatchPointer PMP)
    {
	SC_PResPointer		PRP;
	Uns32			CurTopI;

	// NOTE:  FIRST PMP does not do CONS... there is nothing before it!!
	//	  But can still backtrack if MAX, so only increase SkipPos here.
	if ((PMP->State & SC_PMATCH_FIRSTSTATE) && (SC_SMP_Pos > SC_SMP_SkipPos))
	    SC_SMP_SkipPos = SC_SMP_Pos;

	CurTopI = 0;
	if (SC_SMP_RStackP && !(PMP->MainPatP->Flags & SC_PAT_NRESFLAG)) {
	    CurTopI = SC_SMP_RStackP->TopI++;
	    PRP = (SC_PResPointer)(SC_SMP_RStackP->ADP->Data) + CurTopI;

	    PRP->PatP = PMP->MainPatP;
	    PRP->FromPos = PMP->StartPos;
	    PRP->ToPos = SC_SMP_Pos;
	    PRP->Numb = PMP->Rep;
	    PRP->Res = 0;
	    PRP->Res1 = 0;
	    PRP->Res2 = 0;

	    if (SC_SMP_RStackP->TopI == SC_SMP_RStackP->MaxI)
		SC_SMPAuxRStackGrow();
	}
    }

    // Delete (ToI - FromI) elts starting from FromI.
    // Check that (FromI < ToI) before calling.
    // Caller checks for SC_SMP_RStackP!!
    //
    // NOTE:  Stored Indices *WILL* have to change!!
    void	SC_SMPAuxRStackDel(SC_PMatchPointer PMP, Uns32 FromI, Uns32 ToI)
    {
	SC_PResPointer	PRP;

	PRP = (SC_PResPointer)SC_SMP_RStackP->ADP->Data;
	memmove(PRP+FromI, PRP+ToI, sizeof(SC_PResRecord) * (SC_SMP_RStackP->TopI - ToI));
	SC_SMP_RStackP->TopI -= ToI - FromI;
    }

    static inline Uns32 SC_SMPAuxRStackGetTop(void)
    {
        return (SC_SMP_RStackP) ? SC_SMP_RStackP->TopI : 0;
    }

    static inline void SC_SMPAuxRStackSetTop(Uns32 TopI)
    {
	if (SC_SMP_RStackP) SC_SMP_RStackP->TopI = TopI;
    }

    void	SC_SMPAuxRStackZap(void)
    {
	SC_SlabZapItem(SC_SMP_VMP->ArrDataSP, SC_SMP_RStackP->ADP, SC_SMP_RStackP->ADP->BlockLen);
	SC_SMP_RStackP = NULL;
    }


    static inline SC_PMatchPointer SC_SMPAuxMStackGetTop(SC_MStackPointer MSP)
    {
	return (SC_PMatchPointer)MSP->ADP->Data + MSP->TopI - 1;
    }

    static inline void SC_SMPAuxMStackSetTop(SC_MStackPointer MSP, Uns32 I)
    {
	MSP->TopI = 0;
    }

    // Only call if (MStackTopI > 0)!!
    static inline void	SC_SMPAuxMStackPop(SC_MStackPointer MSP)
    {
	MSP->TopI -= 1;
    }

    void	SC_SMPAuxMStackZap(SC_MStackPointer MSP)
    {
	SC_SlabZapItem(SC_SMP_VMP->ArrDataSP, MSP->ADP, MSP->ADP->BlockLen);
	MSP->ADP = NULL;
    }

    // NOTE:  Generally check PatP->Flags (could be MAIN *or* EXTR)... but a few requires MainFlags!

    SC_PatPointer	SC_SMPAuxGetDynPat(void * DataP)
    {
	SC_PairHPointer		PairP;

	if (DataP && ((PairP = SC_DictGetPair(SC_SMP_PDictP, (SC_VarVPointer)DataP, SC_VAL_PTR))))	// Assign
	    return (SC_PatPointer)PairP->ValueP;

	return NULL;
    }

    SC_PatPointer SC_SMPAuxAdvancePat(SC_PMatchPointer PMP)
    {
	// Only 2 data entries, Index 0 and 1.
	PMP->I += 1;
	if ((PMP->I == 1) && (PMP->CurPatP->Data[1] == NULL))
	    PMP->I = 2;

	if (PMP->I == 2) {
	    PMP->I = 0;
	    // Get next record... loop over any not SC_PAT_EXTR
	    // (STASH Pats come right after Main, but before EXTR)
	    while ((PMP->CurPatP = PMP->CurPatP->NextP))	// Assign
		if (PMP->CurPatP->PatOp == SC_PAT_EXTR)
		    break;
	}

	return PMP->CurPatP;
    }

    // CurPatP[I] is the last EltPat?  CurPatP can be MAIN or EXTR.
    //
    // NOTE:  STASH Pats come after MAIN, but before EXTR!
    Int16	SC_SMPAuxIsEltLast(SC_PatPointer CurPatP, Uns8 I)
    {
	SC_PatPointer	NextPatP;

	NextPatP = CurPatP->NextP;
	while (NextPatP) {
	    if (NextPatP->PatOp == SC_PAT_EXTR) return 0;
	    NextPatP = NextPatP->NextP;
	}

	// So in the Main or Last EXTR!  Elt is last if I==1
	// or I==0, but Data[1] is empty.
	return (I || (CurPatP->Data[1] == NULL));
    }

    Int16	SC_SMPAuxPatMatch(SC_MStackPointer MSP);    
    Uns8	SC_SMPAuxPatErrorOut;

    #ifdef DEBUG
	// Check every step!
	#define SC_MATCH_CHECKABORT	{if (ED_DidAbort()) SC_SMPAuxPatErrorOut = SC_RUN_ABORTERR;}
	#define SC_MATCH_PRIMEABORT	// NOTHING!
    #else
	Uns8	SC_MATCH_AbortCount;

	// Check every 256 steps
	#define SC_MATCH_CHECKABORT	{if (! ++SC_MATCH_AbortCount && ED_DidAbort()) SC_SMPAuxPatErrorOut = SC_RUN_ABORTERR;}
	#define SC_MATCH_PRIMEABORT	SC_MATCH_AbortCount = -1;
    #endif

    // Assumes args are on RUNSTACK!
    void SC_SMPAuxRunCallBack(SC_ClosPointer FuncP, Uns16 InOutCount)
    {
	SC_ScriptPointer	ScriptP = SC_SMP_VMP->RunScriptP;
	SC_RunPointer		TopRunP, CurRunP;

	TopRunP = ScriptP->TopRunP;
	CurRunP = ScriptP->CurRunP;
	    ScriptP->TopRunP = ScriptP->CurRunP = NULL;
	    ScriptP->TopRunP = SC_RunMakeRunRecord(SC_SMP_VMP, FuncP, InOutCount, 0);
	    ScriptP->TopRunP->Flags |= SC_RUN_NOTRECOPT;
		SC_RunExecCurFunc(SC_SMP_VMP, 1);
	ScriptP->TopRunP = TopRunP;
	ScriptP->CurRunP = CurRunP;

	// Result will be on RunStack
    }

    // Will set SC_SMPAuxPatErrorOut and exit if problem!
    // KeyP is normally a string (Key to stash data under), but can also
    // be a StashFunc (Closure) which should be called instead.
    //
    // NOTE:  This operation (whether stashing in Dict or calling FuncP)
    //	      may repeat MULTIPLE times in a single match... as a MAX
    //	      pattern hunts for the longest match!
    //
    // KeyPP points to a local var in caller, so this function can set
    // it to NULL... will only do that if KeyP is a func, so caller will
    // not subsequently announce it.
    //
    // StashFunc args are:
    // (StashCode, Val, PatType, PatP, PatDict)()
    // Returns --> Ignored
    //
    // (Previous version returned 0 to fail cur match and -1 to exit entire
    // scan...  Did not seem useful as FuncP is often called N times.)
          
    
    void	SC_SMPAuxDoValStash(SC_PMatchPointer PMP, SC_VarVPointer * KeyPP, Int64 Code, void * ValP, Uns8 ValType)
    {
	SC_VarVPointer	VP;

	// Process StashFunc and Get into KeyP.
	if ((*KeyPP)->Type == SC_CHUNK_CLOS) {

	    // Assemble args on RunStack
		// StashCode
		VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
		VP->ValueType = SC_VAL_INT;
		VP->ValueP = (void *)Code;

		// Val
		VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
		VP->ValueType = ValType;
		VP->ValueP = (void *)ValP;

		// PatType
		VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
		VP->ValueType = SC_VAL_INT;
		VP->ValueP = (void *)(Uns64)PMP->MainPatP->PatOp;

		// PatP
		VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
		VP->ValueType = SC_VAL_PTR;
		VP->ValueP = PMP->MainPatP;
		SC_VARREF(PMP->MainPatP);

		// PatDict
		VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
		VP->ValueType = SC_VAL_PTR;
		VP->ValueP = SC_SMP_PDictP;
		SC_VARREF(SC_SMP_PDictP);

	    // Call StashFunc:  Stash VMP Run Context, Create RunP, Then Call Func!
	    SC_SMPAuxRunCallBack((SC_ClosPointer)*KeyPP, 5);
	    VP = SC_RUNSTACK_POP(SC_SMP_VMP);		// Return val
	    *KeyPP = NULL;

	    // StashFunc --> Returns 0 to mean Terminate match.
	    // Previously, if (VP->ValueType == SC_VAL_INT) return (Int16)(Int64)VP->ValueP;
	    return;
	}

	if ((*KeyPP)->Type == SC_ENTRY_STR) {
	    SC_SMPAuxDictStash(*KeyPP, (SC_VarVPointer)ValP, SC_VAL_PTR, ValType);
	    return;
	}
	
	SC_RunErrorInfoStr = "MatchPat: Cannot stash without Str key.";
	SC_SMPAuxPatErrorOut = SC_RUN_USERERR;
    }

    // Each Key (Data[x]) can be a Str or Stash FUNC that will return Str when called.
    //
    // Checks the PMP->MainPat against what is being stashed.
    //    PosRange	All
    //	  Str		All
    //	  Ord		Only ALT	(Stores 0 for rest)
    //	  Rep		Only REP	(Stores 1 for rest)
    //	  Len1		All
    //	  Len2		Only SPAN	(Stores 0 for rest)
    //
    // NOTE: STASH Pats come right after the Main Pat and before any EXTR on NextP chain.
    
    void	SC_SMPAuxDoStash(SC_PMatchPointer PMP)
    {
	SC_PatPointer		StashPatP;
	SC_VarVPointer		KeyP;
	Int64			Val;
	SC_StrDataPointer	SDP;

	StashPatP = PMP->MainPatP->NextP;
	while (StashPatP && (StashPatP->PatOp >= SC_PAT_STASH_STRS)) {

	    if (StashPatP->PatOp == SC_PAT_STASH_STRS) {
		if ((KeyP = (SC_VarVPointer)StashPatP->Data[0])) {	// Assign
		    // Stash PosRange
		    Val = (SC_SMP_Pos > PMP->StartPos)
			    ? ((Uns64)SC_SMP_Pos << 32) | (Uns64)PMP->StartPos
			    : ((Uns64)PMP->StartPos << 32) | (Uns64)SC_SMP_Pos;
			    
		    SC_SMPAuxDoValStash(PMP, &KeyP, SC_PAT_POSSTASH, (void *)Val, SC_VAL_INT);
		    SC_MATCHSTASHRANGE_ANNOUNCE(KeyP, Val, PMP->PatL);
		}

		if ((KeyP = StashPatP->Data[1])) {
		    // Stash Str (From PMP->StartPos to SC_SMP_Pos
		    SDP = (SC_SMP_Pos > PMP->StartPos)
			    ? SC_StrStashBufferData(SC_SMP_VMP, SC_SMP_BufP, PMP->StartPos, SC_SMP_Pos)
			    : SC_StrStashBufferData(SC_SMP_VMP, SC_SMP_BufP, SC_SMP_Pos, PMP->StartPos);
			    
		    Val = (Int64)SC_StrGetDataEntry(SC_SMP_VMP, SDP);	// Will zap SDP if hashed already!
		    SC_SMPAuxDoValStash(PMP, &KeyP, SC_PAT_STRSTASH, (void *)Val, SC_VAL_PTR);
		    SC_MATCHSTASHSTR_ANNOUNCE(KeyP, Val, PMP->PatL);
		}
		return;

	    } else if (StashPatP->PatOp == SC_PAT_STASH_NUMBS) {
		if ((KeyP = StashPatP->Data[0])) {
		    // Stash Ord
		    Val = (PMP->MainPatP->PatOp == SC_PAT_ALT) ? PMP->Rep : 0LL;
		    SC_SMPAuxDoValStash(PMP, &KeyP, SC_PAT_CNTSTASH, (void *)Val, SC_VAL_INT);
		    SC_MATCHSTASH_ANNOUNCE("Ord", KeyP, Val, PMP->PatL);
		}

		if ((KeyP = StashPatP->Data[1])) {
		    // Stash Count
		    Val = ((PMP->MainPatP->PatOp == SC_PAT_REP) || (PMP->MainPatP->PatOp == SC_PAT_CHAR))
			    ? PMP->Rep : 1LL;
		    SC_SMPAuxDoValStash(PMP, &KeyP, SC_PAT_CNTSTASH, (void *)Val, SC_VAL_INT);
		    SC_MATCHSTASH_ANNOUNCE("Reps", KeyP, Val, PMP->PatL);
		}
		return;

	    } else if (StashPatP->PatOp == SC_PAT_STASH_LENS) {
		if ((KeyP = StashPatP->Data[0])) {

		    switch (PMP->MainPatP->PatOp) {
			case SC_PAT_SEQ:
			case SC_PAT_ALT:
			case SC_PAT_SPAN:
			case SC_PAT_REP:
			    // Get the CHAR Len *NOT* Pos distance
			    Val = (PMP->StartPos < SC_SMP_Pos)
				     ? ED_BufferCountChars(SC_SMP_BufP, PMP->StartPos, SC_SMP_Pos)
				     : ED_BufferCountChars(SC_SMP_BufP, SC_SMP_Pos, PMP->StartPos);
			    break;

			case SC_PAT_CHAR:
			    Val = SC_SMP_Pos - PMP->StartPos;
			    break;
			    
			case SC_PAT_GO:
			    // Len1 for GO is PosLen since it goes N CHARS by definition!
			    Val = (PMP->StartPos < SC_SMP_Pos)
				     ? SC_SMP_Pos - PMP->StartPos
				     : PMP->StartPos - SC_SMP_Pos;
			    break;

			default: Val = 0LL;
		    }
		    SC_SMPAuxDoValStash(PMP, &KeyP, SC_PAT_LEN1STASH, (void *)Val, SC_VAL_INT);
		    SC_MATCHSTASH_ANNOUNCE("Len1", KeyP, Val, PMP->PatL);
		}

		if ((KeyP = StashPatP->Data[1])) {
		    // Stash Space Len (Len2)  (Definitely CHAR Len in PAT_SPAN)
		    Val = (PMP->MainPatP->PatOp == SC_PAT_SPAN) ? PMP->Rep : 0LL;		    
		    SC_SMPAuxDoValStash(PMP, &KeyP, SC_PAT_LEN2STASH, (void *)Val, SC_VAL_INT);
		    SC_MATCHSTASH_ANNOUNCE("Len2", KeyP, Val, PMP->PatL);
		}
		return;
		
	    } else
		break;

	    StashPatP = StashPatP->NextP;
	}
    }
    
    Int32	SC_SMPAuxValueFuncCall(SC_ClosPointer FuncP, SC_PMatchPointer PMP, SC_PatPointer PatP)
    {
	SC_VarVPointer	VP;
	Int32		Value;

	// PatType
	VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
	VP->ValueType = SC_VAL_INT;
	VP->ValueP = (void *)(Uns64)PatP->PatOp;

	// PatP
	VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
	VP->ValueType = SC_VAL_PTR;
	VP->ValueP = PatP;
	SC_VARREF(PatP);

	// PatDict
	VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
	VP->ValueType = SC_VAL_PTR;
	VP->ValueP = SC_SMP_PDictP;
	SC_VARREF(SC_SMP_PDictP);

	// Stash VMP Run Context, Create RunP, Then Call Func!
	SC_SMPAuxRunCallBack(FuncP, 3);

	VP = SC_RUNSTACK_POP(SC_SMP_VMP);

	if (VP->ValueType != SC_VAL_INT) {
	    SC_RunErrorInfoStr = "MatchPat: Pat callback must return int.";
	    SC_SMPAuxPatErrorOut = SC_RUN_USERERR;
	    return 0;
	}

	Value = (Int32)(Int64)VP->ValueP;
	return Value;
    }


    Int16	SC_SMPAuxGetRepLimits(SC_PMatchPointer PMP, SC_PatPointer RepPatP)
    {
	SC_ClosPointer		ClosP;
	
	// Get MIN (From Data[0])
	if (RepPatP->Flags & SC_PAT_DATA0P) {
	    // There must be a script CallBack (ClosP) stashed here!
	    if (((ClosP = (SC_ClosPointer)RepPatP->Data[0])) &&		// Assign
		(ClosP->Type == SC_CHUNK_CLOS)) {
		
		PMP->MinRep = SC_SMPAuxValueFuncCall(ClosP, PMP, RepPatP);
		if (SC_SMPAuxPatErrorOut != SC_RUN_NOERR) return 0;
	    } else {
		SC_RunErrorInfoStr = "MatchPat: Rep MIN must be Int or Func.";
		SC_SMPAuxPatErrorOut = SC_RUN_USERERR;
		return 0;
	    }
	} else
	    PMP->MinRep = (Int32)(Int64)RepPatP->Data[0];

	// Get MAX (from Data[1])
	if (RepPatP->Flags & SC_PAT_DATA1P) {
	    // There must be a script CallBack (ClosP) stashed here!
	    if (((ClosP = (SC_ClosPointer)RepPatP->Data[1])) &&		// Assign
		(ClosP->Type == SC_CHUNK_CLOS)) {

		PMP->MaxRep = SC_SMPAuxValueFuncCall(ClosP, PMP, RepPatP);
		if (SC_SMPAuxPatErrorOut != SC_RUN_NOERR) return 0;
	    } else {
		SC_RunErrorInfoStr = "MatchPat: Rep MAX must be Int or Func.";
		SC_SMPAuxPatErrorOut = SC_RUN_USERERR;
		return 0;
	    }
	} else
	    PMP->MaxRep = (Int32)(Int64)RepPatP->Data[1];

	if ((PMP->MinRep < 0) || (PMP->MaxRep < 0) ||
	    (PMP->MinRep && PMP->MaxRep && (PMP->MinRep > PMP->MaxRep))) {
	    SC_RunErrorInfoStr = "MatchPat: Rep Min/Max (from Func) are bad.";
	    SC_SMPAuxPatErrorOut = SC_RUN_USERERR;
	    return 0;
	}

	return 1;
    }

    // Reads Data[0] int (or User FUNC) to store in PMP->Rep
    Int16	SC_SMPAuxGetValue(SC_PMatchPointer PMP, SC_PatPointer PatP)
    {
	SC_ClosPointer	ClosP;

	if (PatP->Flags & SC_PAT_DATA0P) {
	    // There must be a script CallBack (ClosP) stashed here!
	    if (((ClosP = (SC_ClosPointer)PatP->Data[0])) &&		// Assign
		(ClosP->Type == SC_CHUNK_CLOS)) {
		PMP->Rep = SC_SMPAuxValueFuncCall(ClosP, PMP, PatP);
		if (SC_SMPAuxPatErrorOut != SC_RUN_NOERR) return 0;
	    } else {
		SC_RunErrorInfoStr = "MatchPat: Pat field must be Int or Func.";
		SC_SMPAuxPatErrorOut = SC_RUN_USERERR;
		return 0;
	    }
	} else
	    PMP->Rep = (Int32)(Int64)PatP->Data[0];

	return 1;
    }

    // ResFunc args are: (7 In) + (0 Out)
    // (StackOrd FromPos ToPos Numb PatType PatP PatDict)()

    void SC_SMPAuxRStackFuncCall(void)
    {
	SC_VarVPointer	VP;
	SC_PResPointer	PRP;
	Uns64		I;		// 0-based

	PRP = (SC_PResPointer)SC_SMP_RStackP->ADP->Data;
	for (I = 0; I < SC_SMP_RStackP->TopI; PRP++) {

	    // StackOrd
	    VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);	// Points to top stack elt
	    VP->ValueType = SC_VAL_INT;
	    VP->ValueP = (void *)I++;		// INC here instead of in FOR!

	    // FromPos
	    VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
	    VP->ValueType = SC_VAL_INT;
	    VP->ValueP = (void *)(Int64)PRP->FromPos;

	    // ToPos
	    VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
	    VP->ValueType = SC_VAL_INT;
	    VP->ValueP = (void *)(Int64)PRP->ToPos;

	    // Numb
	    VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
	    VP->ValueType = SC_VAL_INT;
	    VP->ValueP = (void *)(Uns64)PRP->Numb;

	    // PatType
	    VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
	    VP->ValueType = SC_VAL_INT;
	    VP->ValueP = (void *)(Uns64)PRP->PatP->PatOp;

	    // PatP
	    VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
	    VP->ValueType = SC_VAL_PTR;
	    VP->ValueP = PRP->PatP;
	    SC_VARREF(PRP->PatP);
	    
	    // PatDict
	    VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
	    VP->ValueType = SC_VAL_PTR;
	    VP->ValueP = SC_SMP_PDictP;
	    SC_VARREF(SC_SMP_PDictP);

	    // Stash VMP Run Context, Create RunP, Then Call Func!
	    SC_SMPAuxRunCallBack(SC_SMP_ResFuncP, 7);

	    VP = SC_RUNSTACK_POP(SC_SMP_VMP);
	    if (! VP->ValueP) break;		// Abort loop
	}
    }

    // Mode Function Callback
    //
    //	IN Args:  (PatDict)				1 in
    //	Out Args: (NextStartPos MStartPos MEndPos)	3 out
    //
    // This is where matched strings should be replaced/edited.
    // So would be nice to have StartPos/EndPos (for the top level Str/Pat) as args.
    
    Uns8	SC_SSPAuxModeFuncCall(SC_ClosPointer ModeFP, SC_VarVPointer NextStartVP,
				      SC_VarVPointer MStartVP, SC_VarVPointer MEndVP)
    {
	SC_VarVPointer	VP;

	VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);	// PatDict is only IN arg
	VP->ValueType = SC_VAL_PTR;
	VP->ValueP = SC_SMP_PDictP;
	SC_VARREF(SC_SMP_PDictP);

	VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);	// NextStartPos is Out1
	VP->ValueType = SC_VAL_PTR;
	VP->ValueP = NextStartVP;
	SC_VARREF(NextStartVP);	

	VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);	// MStartPos is Out2
	VP->ValueType = SC_VAL_PTR;
	VP->ValueP = MStartVP;
	SC_VARREF(MStartVP);	

	VP = SC_RUNSTACK_PUSH(SC_SMP_VMP);	// MEndPos is Out3
	VP->ValueType = SC_VAL_PTR;
	VP->ValueP = MEndVP;
	SC_VARREF(MEndVP);	

	SC_SMPAuxRunCallBack(ModeFP, (3 << 8) + 1);

	// Get returned value
	VP = SC_RUNSTACK_POP(SC_SMP_VMP);
	if (VP->ValueType == SC_VAL_INT)
	    return (Uns8)(Uns64)VP->ValueP;

	return SC_PMODE_DONE;
    }

    // Design note:
    //
    // Pattern Matching is entirely iterative.  Patterns are recursive in
    // nature, so matching would normally require two forms of recursion: one
    // for simple matching/traversal and another for CONS processing.  But both
    // can be done iteratively with the help of a data stack to hold matching
    // state, the MStack.  CONS "recursion" is differentiated by pushing a
    // special CONS placeholder on the MStack.
    //
    // CONS Placeholder
    //	  MainPatP == NULL  <---- DIFFERENTIATING factor
    //	  StartPos == Index of PMatch to do next (-1 if done)
    //	  CurPos   == CONS Level
    //
    // The goal is for mem usage/"recursion" depth, now done iterarively, to be
    // dictated only by Pat complexity and NOT data length.  That is the case,
    // except when CONS Pats are iterated (in REP or SPAN).
    //
    // A single pattern match can have multiple CONS segments.  But it is still
    // a single match, so once the CONS succeeds, the MStack should be "unwound"
    // and the entire match terminated.  In order to get the proper RStack, the
    // PMatch (segments) must be visited on the way back. This effectively forms
    // a CONS return chain that must be traversed (backwards) in a SUCC return.

    // SC_SMPAuxMStackCONSPush is called from "MStackLoop" in SC_SMPAuxPatMatch
    // when the topmost PMatch on the MStack is a CONS placeholder.  It reaches
    // back into the original (non-copy) part of the MStack and copies another
    // superior PMatch to check.
    //
    // NOTE: TopPMP *MUST* be CONS placeholder and (StartPos > -1) when calling!
    //
    //	     CSrcI are only relevent for CONS return, where the process unwinds
    //	     by returning backwards.  (CSrcI for REP + SPAN are wired directly
    //	     to their Origin, *NOT* daisy chained.
    //

    void	SC_SMPAuxMStackCONSPush(SC_MStackPointer MSP, SC_PMatchPointer TopPMP)
    {
	SC_PMatchPointer	PMPArr = (SC_PMatchPointer)MSP->ADP->Data;
	SC_PMatchPointer	SourceP;
	Uns32			I;
	Int32			SrcI;

	// NOT done with the CONS...
	// Copy/Push SourceP on the stack and dec StartPos.
	SourceP = &PMPArr[(Uns32)TopPMP->StartPos];
	I = MSP->TopI++;
	PMPArr[I] = *SourceP;
	PMPArr[I].CurPos = SC_SMP_Pos;			// Update with latest
	PMPArr[I].State &= ~SC_PMATCH_SUCCSTATE;	// Does NOT transfer!!

	// if copying a LOOP PMatch, just use *ITS* Source, otherwise set CSrcI.
	// (Do NOT want iterating PMatch to be chained.. all should go to ORIG.)
	if ((SourceP->MainPatP->PatOp != SC_PAT_REP) || (SourceP->CSrcI == -1))
	    PMPArr[I].CSrcI = TopPMP->StartPos;

	PMPArr[I].State |= SC_PMATCH_SRCSTATE;	// New Src
	SourceP->State &= ~SC_PMATCH_SRCSTATE;	// Not Src

	// Chain of PMP copies:
	// [PMP*0].....[PMP:1]....[PMP:2]....[PMP*3]
	// ORIGIN      INTER      SourceP    NEW
	//
	// When SourceP failed, ORIGIN got SRC Flag.  Here, NEW (I] gets
	// SRCSTATE set, but it copies SourceP which was already NotSRC.
	// So must go back to ORIGIN and reset its SRCSTATE.
	SrcI = PMPArr[I].CSrcI;
	if (SrcI > -1)
	    PMPArr[SrcI].State &= ~SC_PMATCH_SRCSTATE;
	
	TopPMP->StartPos--;
	// If prev is a CONS placeholder, use its StartPos!
	if (TopPMP->StartPos > -1) {
	    SourceP = &PMPArr[(Uns32)TopPMP->StartPos];
	    if (SourceP->MainPatP == NULL)
		TopPMP->StartPos = SourceP->StartPos;
	}
	
	// Grow MStack?  (May move MSP->ADP)
	if (MSP->TopI >= MSP->MaxI)
	    SC_SMPAuxMStackGrow(MSP);
    }

    // SC_SMPAuxMStackCONSStart will *INITIATE* CONS "recursion".  It will push a CONS
    // Placehoder PMatchRecord on the MStack.  This will then look back at sup (previous
    // on the stack) matches, and put duplicates on TOP.
    //
    // NOTE: Normally, the CONS will be set to start with the previous (I-1) PMatch.  But
    //       if TopPMP is the LAST entry on its container Pat, then CONS should be looking
    //       one *further* back (I-2).  This is because, if the last sub-pat in X matches,
    //       then X itself *will* match, so there is no need to check. (***LastCONS)

    Int16	SC_SMPAuxMStackCONSStart(SC_MStackPointer MSP)
    {
	SC_PMatchPointer	PMPArr = (SC_PMatchPointer)MSP->ADP->Data;
	Int32			I = MSP->TopI - 1;
	SC_PMatchPointer	TopPMP = &PMPArr[I];
	Int32			Steps = (TopPMP->State & SC_PMATCH_LASTSTATE) ? 2 : 1;

	if (I < Steps) return 0;			// No superior for CONS
	I -= Steps;					// Index, where to start

	// Set state to CONSSTATE, for when we return.
	TopPMP->State |= SC_PMATCH_CONSSTATE;

	// Push new CONS placeholder, StartPos is I.
	SC_SMPAuxMStackPush(MSP, NULL, NULL, I, TopPMP->PatL);
	PMPArr = (SC_PMatchPointer)MSP->ADP->Data;	// ADM may have moved!
	TopPMP = &PMPArr[MSP->TopI - 1];		// The new CONS placeholder
	
	TopPMP->CurPos = ++(MSP->ConsL);		// Inc Cons level
	if (PMPArr[I].MainPatP == NULL) 		// StartPos is already a CONS
	    TopPMP->StartPos = PMPArr[I].StartPos;	// Use its StartPos

	if (MSP->ConsL > SC_SMP_MaxConsL)
	    SC_SMP_MaxConsL = MSP->ConsL;

	return 1;
    }

    // SC_SMPAuxMStackCONSReturn is called by a real PMatch (not CONS placeholder) when
    // returning from a SUCC CONS (CONSSTATE) call.  It basically wants to know where to
    // go now, is it done?  Also sets CONS Level (ConsL), since it will change.

    void	SC_SMPAuxMStackCONSReturn(SC_MStackPointer MSP)
    {
	SC_PMatchPointer	PMP = (SC_PMatchPointer)MSP->ADP->Data + MSP->TopI - 1;
	Int16			SetConsL = 0;
	Uns32			I, OldTopI = MSP->TopI;

	// Go to CRetI if we have one.  But possible it is no longer SRC,
	// in which case start from there and search back.
	if (PMP->CRetI > -1) {
	    MSP->TopI = PMP->CRetI;
	    if (MSP->TopI == 0) return;

	    SetConsL = 1;				// May be wrong now, so set it
	    PMP =  (SC_PMatchPointer)MSP->ADP->Data + PMP->CRetI - 1;
	    if (PMP->State & SC_PMATCH_SRCSTATE)
		goto DoneBut;
	}

	MSP->TopI--; PMP--;
	while (MSP->TopI) {
	    // Skip CONS placeholders and NON-SRC PMatch records
	    if (PMP->MainPatP == NULL) {
		MSP->ConsL = PMP->CurPos - 1;		// Going before this one
		SetConsL = 0;				// Adjusted it!
	    } else if (PMP->State & SC_PMATCH_SRCSTATE)
		break;

	    MSP->TopI--, PMP--;
	}

    DoneBut:
	if (SetConsL) {
	    // Only happens if jumping BACK because of CRetI... so go forward
	    // until find a CONS placeholder... then set ConsL!
	    I = MSP->TopI, PMP++;
	    while (PMP->MainPatP && (I < OldTopI))
		I++, PMP++;

	    if (PMP->MainPatP == NULL)
		MSP->ConsL = PMP->CurPos - 1;
	    else
		MSP->ConsL = 0;			// Should never happen!
	}
    }

    // SC_SMPAuxMStackGetRetI is called ONLY in CONS to set CRetI for Pats that
    // SUCC but continue (MAX forms of REP and SPAN).
    //
    // The problem:
    //	  -- In Step Q the Pat succeeds... and records it.
    //	  -- Then, it checks prev PMP X, making a COPY.
    //	  -- If the Pat returns now, it will go BEFORE X as X is no longer SRC.
    //    -- Alas, the Pat does NOT return now!
    //  ... later:
    //    -- Step R, the Pat fails... in the process PMP X becomes SRC again!
    //    -- Pat returns, using its stored SUCC values, but does PMP X again!!!
    //
    //  So by storing CRetI, it will return BEFORE X, when it finally does!!
    //
    // NOTE: Do this ONLY once... even if Pat SUCC multiple times!!
    //
    // This function works similar to CONSReturn, except it just computes the
    // index and does not change the MSP.  It will also not bother with ConsL
    // (CONS Level), because CONSReturn will set it.
    //
    // NOTE: This function returns I+1 becase CRetI is in I+1 format!
    //	     So CRetI is used directly to set MSP->TopI.
    //	     CRetI of -1 means NO CRetI.
    //	     CRetI of 0  means, DONE, nothing more to do
    //       CRetI of 1  means return to very first (Index 0) PMatch record


    Int32	SC_SMPAuxMStackGetRetI(SC_MStackPointer MSP)
    {
	SC_PMatchPointer	PMPArr = (SC_PMatchPointer)MSP->ADP->Data;
	SC_PMatchPointer	PMP;
	Int32			I;

	// *THIS* REP PMatch just succeeded... search the SRC chain back until
	// finding the ORIGIN src.
	//
	// NOTE: CONSPush avoids daisy chained Src for LOOPing PMatch recs.
	//	 So "While" should not loop.

	if (MSP->TopI < 2) return 0;

	// One PMP before this one
	I = MSP->TopI - 2;
	PMP = PMPArr + I;
	
	// If CONS Placeholder or NON-SRC, keep going back.
	while (I > -1) {
	    if ((PMP->MainPatP != NULL) && (PMP->State & SC_PMATCH_SRCSTATE))
		break;
	    I--, PMP--;
	}

	return I + 1;
    }

    // SC_SMPAuxMStackSrcFailed is called when the current SRC PMP fails.
    // This current PMP will get zapped, so restore SRC to its source.
    
    void	SC_SMPAuxMStackSrcFailed(SC_MStackPointer MSP, SC_PMatchPointer PMP)
    {
	// No point removing SRC flag from *THIS* PMP, it will be zapped.
	if ((PMP->State & SC_PMATCH_SRCSTATE) && (PMP->CSrcI > -1)) {
	    PMP = (SC_PMatchPointer)MSP->ADP->Data + PMP->CSrcI;
	    PMP->State |= SC_PMATCH_SRCSTATE;
	}
    }

    Int16	SC_SMPAuxPatMatch(SC_MStackPointer MSP)
    {
	SC_PMatchPointer		TopPMP;
	SC_PatPointer			CurPatP;	// Container Pat (EXTR)
	SC_PatPointer			EltPatP;	// Element (Data) Pat
	Int16				Result;
	Uns8				IsNeg;
	Uns8				InCRet;		// Returning up the MStack

	Result = 1;
	InCRet = 0;

    MStackLoop:
	if (SC_SMPAuxPatErrorOut) return 0;	// Error/Abort out
	if (MSP->TopI == 0) return Result;	// All Done!!
	
	TopPMP = SC_SMPAuxMStackGetTop(MSP);	// Does NOT pop here
	// Is this a CONS placeholder?  (Doing CONS "recursion"!)
	if (TopPMP->MainPatP == NULL) {
	    // StartPos is PMatch Index... -1 means DONE.
	    if (! Result || (TopPMP->StartPos < 0)) {
		// This CONS is done... back to its caller/predecessor!
		MSP->ConsL--;				// 1 Less ConsL (for now)
		SC_SMPAuxMStackPop(MSP);
		InCRet = 1;
		goto MStackLoop;

	    } else {
		// Copy a new PMatch and push on top.
		SC_MSTACK_LINE(MSP);
		SC_SMPAuxMStackCONSPush(MSP, TopPMP);
		TopPMP = SC_SMPAuxMStackGetTop(MSP);	// ADP may have moved!
		InCRet = 0;
	    }
	}

	CurPatP = TopPMP->CurPatP;
	if (TopPMP->State & SC_PMATCH_CONSSTATE) {
	    InCRet = 1;
	    goto GoMatchPat;
	}
	
	if (CurPatP == NULL) {
	    // End of MainPat elts... stash result if required.
	    InCRet = 0;
	    if (Result) {
		SC_SMPAuxDoStash(TopPMP);
		SC_SMPAuxRStackPush(TopPMP);
		SC_RSTACK_LINE(MSP, "+Loop");

	    } else {
		SC_SMPAuxRStackSetTop(TopPMP->RTopI);	// Backing up failed CONS
		SC_RSTACK_LINE(MSP, "-Loop");
		SC_MSTACK_FAIL(MSP);
		SC_SMPAuxMStackSrcFailed(MSP, TopPMP);
	    }

	    SC_SMPAuxMStackPop(MSP);
	    goto MStackLoop;
	}

    GoMatchPat:
	switch (TopPMP->MainPatP->PatOp) {

	    case SC_PAT_SEQ:
		// Stashes Pos + Str + Len1
		// P1STATE:  Going through SEQ Pats one by one **********
		// MP->Rep == Which Elt of sequence
		if (TopPMP->State & SC_PMATCH_P1STATE) {
		    IsNeg = !!(CurPatP->Flags & SC_PAT_NEGFLAG);		// 0 or 1 only
		    TopPMP->Rep += 1;						// Which Elt of the Seq
		    SC_MSTACK_LINE(MSP);		
		    
		    if ((Result = (IsNeg ^ Result)))				// Assign
			goto DoAdvance;						// Next Elt in Pat
		    
		    // Failed... Done!  Should not advance SC_SMP_Pos if Seq failed.
		    SC_MSTACK_FAIL(MSP);
		    SC_SMPAuxMStackSrcFailed(MSP, TopPMP);		    
		    SC_SMP_Pos = TopPMP->StartPos;
		    SC_SMPAuxMStackPop(MSP);
		    goto MStackLoop;
		}

		// Set up SEQ ********** Then process first Elt
		SC_MATCHPAT_ANNOUNCE(TopPMP->MainPatP, SC_PatStrArr[SC_PAT_SEQ], TopPMP->PatL);
		TopPMP->State |= SC_PMATCH_P1STATE | SC_PMATCH_SRCSTATE;
		TopPMP->RTopI = SC_SMPAuxRStackGetTop();		
		SC_MSTACK_LINE(MSP);
		goto ProcessElt;

	    case SC_PAT_ALT:
		// Stashes Pos + Str + Ord + Len1
		// P1STATE:  Going through ALT elts one by one **********
		// MP->Rep == Which Elt (in order)
		//
		// Calls SC_MSTACK_FAIL in DoAdvance... as ALT elts fail in sequence!

		if (TopPMP->State & SC_PMATCH_CONSSTATE) {
		    // Reset CONSSTATE.  Ignore IsNeg, Will not need it after CONSReturn
		    TopPMP->State &= ~SC_PMATCH_CONSSTATE;
		    goto AltCONSReturn;
		}

		if (TopPMP->State & SC_PMATCH_P1STATE) {
		    IsNeg = !!(CurPatP->Flags & SC_PAT_NEGFLAG);		// 0 or 1 only
		    SC_MSTACK_LINE(MSP);
		    TopPMP->CurPos = SC_SMP_Pos;				// CONS may change it
		    
		    if ((Result = (IsNeg ^ Result))) {				// Assign
			// This Elt was successful...
			if (! InCRet && (CurPatP->Flags & SC_PAT_CONSFLAG) && (MSP->TopI > 1)) {
			    SC_SMPAuxDoStash(TopPMP);				// Stash just in case!
			    SC_SMPAuxRStackPush(TopPMP);
			    SC_RSTACK_LINE(MSP, "+Alt");
			    TopPMP->State &= ~SC_PMATCH_SRCSTATE;		// Done, ignore on CONS Ret***
			    // "Recurse" iterate on CONS superiors!
			    if (SC_SMPAuxMStackCONSStart(MSP))
				goto MStackLoop;
			    
			AltCONSReturn:
			    TopPMP = SC_SMPAuxMStackGetTop(MSP);		// In case MSP->Data moved
			    SC_SMP_Pos = TopPMP->CurPos;			    
			    if (Result) {
				SC_SMPAuxMStackCONSReturn(MSP);			// All Done for this level
				InCRet = 1;
				goto MStackLoop;
			    } 
			    if (SC_SMPAuxPatErrorOut) return 0;
			    SC_SMPAuxRStackSetTop(TopPMP->RTopI);		// Reset, it failed!
			    SC_RSTACK_LINE(MSP, "-Alt");
			    TopPMP->State |= SC_PMATCH_SRCSTATE;		// Not Done, for CONS Ret***
			    goto AltGetNextElt;					// Try next Alternative...
			}

			// Succeeded... Done!
			TopPMP->CurPatP = NULL;
			goto MStackLoop;
		    }

		AltGetNextElt:
		    // Try another Elt, previous one failed.
		    SC_SMP_Pos = TopPMP->StartPos;				// Same place again
		    TopPMP->Rep += 1;						// Ordinal #
		    InCRet = 0;
		    SC_MATCH_CHECKABORT;
		    goto DoAdvance;
		}

		// Set up ALT ********** Then process first Elt
		SC_MATCHPAT_ANNOUNCE(TopPMP->MainPatP, SC_PatStrArr[SC_PAT_ALT], TopPMP->PatL);
		TopPMP->State |= SC_PMATCH_P1STATE | SC_PMATCH_SRCSTATE;	// Process it!
		SC_MSTACK_LINE(MSP);
		TopPMP->Rep += 1;						// Ordinal #
		TopPMP->RTopI = SC_SMPAuxRStackGetTop();
		goto ProcessElt;

	    case SC_PAT_SPAN:
		// *CAUTION*!!  Not as bad as REP, but can bite!
		//
		// Stashes Pos + Str + Len1 (Total Len) + Len2 (Rep)
		// View as [FROM] + [SPACE(var len)] + [TO].
		// CONS applies to var len SPACE span, ending in TO pattern.
		//
		// Rep    <-- #Chars of SPACE  (between FROM and TO, **NOT** Pos!)
		// MinRep <-- Last EndPos(SPACE) == StartPos(TO)
		// MaxRep <-- Last succ EndPos(TO) or -1
		// SRep   <-- Last succ Rep (Counts #chars of SPACE)
		//
		// *********** RStack handling is reverse of REP *************
		// RStack handling: RLastI is RTopI from Prev SUCC,
		// Delete RLastI-to-RTopI if new SUCC.
		// InCRet if returning (reverse MStack flow) from CONS.
		//
		// LastCONS:  TO Pat is always LAST in a Span Pat (SC_PMATCH_LASTSTATE).  If the TO
		//	      is CONS there is no need to check the SPAN Pat, as it always (by
		//	      definition) succeeds if the TO part succeeds!
		//
		// SelfCONS:  If a copy (CSrcI > -1) of a P2STATE SPAN Pat is being evaluated, then
		//	      its TO Pat is there too.  Accept the Result and return, the Span will
		//	      SUCC/FAIL as its TO Pat does!
		//
		//	      If a copy (CSrcI > -1) of a P1STATE SPAN Pat is being evaluated, then
		//	      just set Result=1 and return from CONS.  The FROM pat of a CONS SPAN
		//	      should not be CONS!

		if (TopPMP->State & SC_PMATCH_CONSSTATE) {
		    // Reset CONSSTATE.  Ignore IsNeg, Will not need it after CONSReturn!
		    TopPMP->State &= ~SC_PMATCH_CONSSTATE;
		    goto SpanCONSReturn;
		}

		// P2State: Deals with TO pattern **********
		if (TopPMP->State & SC_PMATCH_P2STATE) {
		    Uns8		IsCONS;

		    IsNeg = !!(CurPatP->Flags & SC_PAT_NEGFLAG);	// 0 or 1 only
		    IsCONS = ((TopPMP->MainPatP->Flags & SC_PAT_CONSFLAG) && (MSP->TopI > 1));
		    SC_MSTACK_LINE(MSP);		    
		    TopPMP->CurPos = SC_SMP_Pos;			// CONS may change SC_SMP_Pos!
		    Result = (IsNeg ^ Result);

		    // ***SelfCONS: (Orig is obviously P2STATE!)
		    if (TopPMP->CSrcI > -1L) {
			((SC_PMatchPointer)MSP->ADP->Data)[TopPMP->CSrcI].State |= SC_PMATCH_SRCSTATE;
			SC_SMPAuxMStackPop(MSP);
			goto MStackLoop;
		    }
		    
		    if (Result) {

			// TO Pat matched... Result is 1
			SC_SMPAuxRStackPush(TopPMP);
			SC_RSTACK_LINE(MSP, "+Span");
			SC_SMPAuxDoStash(TopPMP);			// Stash!  (Other Pats may need value!)
			TopPMP->RTempI = SC_SMPAuxRStackGetTop();	// In case CONS fails
			if (IsCONS && ! InCRet) {
			    // "Recurse" iterate on CONS superiors!
			    if (SC_SMPAuxMStackCONSStart(MSP))
				goto MStackLoop;

			SpanCONSReturn:
			    IsCONS = 1;					// Will jump here if SpanCONSReturn!!
			    TopPMP = SC_SMPAuxMStackGetTop(MSP);	// In case MStackP->Data moves!
			    SC_SMP_Pos = TopPMP->CurPos;
			    if (! Result) {
				if (SC_SMPAuxPatErrorOut) return 0;	// ERROR... Express return
				SC_SMPAuxRStackSetTop(TopPMP->RTempI);	// In case CONS added a few!
				SC_RSTACK_LINE(MSP, "-SpanC");				
				goto SpanFail;				// Take the !Result Branch... keep going
			    }
			}

			// Was successful (whether CONS or not)... assume N new elts on RStack!
			// Delete from RLastI to RTopI... 
			if (SC_SMP_RStackP && (TopPMP->SRep != -1L) && (TopPMP->RLastI < TopPMP->RTopI)) {
			    SC_SMPAuxRStackDel(TopPMP, TopPMP->RLastI, TopPMP->RTopI);
			    SC_RSTACK_LINE(MSP, "-SpanS");
			    TopPMP->RTopI = TopPMP->RLastI;		// Index after delete
			}

			// Success... but may have to keep going!
			TopPMP->MaxRep = TopPMP->CurPos;		// Record last SUCCESS match (EndPos of TO)
			TopPMP->SRep = TopPMP->Rep;			// Record last SUCCESS SRep
			TopPMP->RLastI = TopPMP->RTopI;
			TopPMP->RTopI = SC_SMPAuxRStackGetTop();	// End of this succ RStack entries
			if (TopPMP->CRetI == -1)
			    TopPMP->CRetI = SC_SMPAuxMStackGetRetI(MSP);	// Store MStack I for last SRC

			if (! (TopPMP->MainPatP->Flags & SC_PAT_MINFLAG)) {	// Keep going if IsMax...
			    Result = 0;
			    goto SpanFail;
			}
			// Obviously IsMin and SUCCESS
			if (IsCONS) {
			    SC_SMPAuxMStackCONSReturn(MSP);		// All Done for this level
			    InCRet = 1;
			} else
			    SC_SMPAuxMStackPop(MSP);

			SC_MATCH_CHECKABORT;
			goto MStackLoop;
		    }

		SpanFail:
		    SC_SMPAuxRStackSetTop(TopPMP->RTopI);		// Reset, it failed!
		    SC_RSTACK_LINE(MSP, "-SpanF");		    
		    // Either TO Pat did *NOT* match or IsMax... Result is 0
		    if (TopPMP->MinRep < SC_SMP_LastPos) {		// Go again
			// 1 more char in SPACE (could be 1-4 bytes)
			SC_SMP_Pos = TopPMP->MinRep += ED_UtilGetUTF8Len(*ED_PosToPtr(SC_SMP_BufP, TopPMP->MinRep));
			TopPMP->Rep += 1;				// Reps of SPACE
			goto ProcessElt;				// Process TO Pat/Str again!
		    }

		    // Hit the end... no more expanding SPACE
		    if ((! (TopPMP->MainPatP->Flags & SC_PAT_MINFLAG)) && (TopPMP->SRep != (Uns32)-1)) {
			// Had found a previous result!!  Already stashed IFF CONS
			SC_SMP_Pos = TopPMP->MaxRep;
			TopPMP->Rep = TopPMP->SRep;
			
			Result = 1;
			SC_SMPAuxDoStash(TopPMP);			// Stash backtracked values, again.
			if (IsCONS) {
			    SC_SMPAuxMStackCONSReturn(MSP);		// All Done for this level
			    InCRet = 1;
			    SC_MSTACK_LINE(MSP);
			} else 
			    SC_SMPAuxMStackPop(MSP);

		    } else {
			SC_SMPAuxMStackPop(MSP);			// Failed....
			InCRet = 0;
		    }

		    if (! Result) {
			SC_MSTACK_FAIL(MSP);
			SC_SMPAuxMStackSrcFailed(MSP, TopPMP);		    
		    }
		    SC_MATCH_CHECKABORT;
		    goto MStackLoop;
		    
		}   // P2 State

		// P1State: Deals with FROM pattern **********
		if (TopPMP->State & SC_PMATCH_P1STATE) {

		    // ***SelfCONS: (Orig is obviously P1STATE!)...
		    // Bypass the whole CONS and let the "FROM" Pat match on its own!!
		    if (TopPMP->CSrcI > -1L) {
			((SC_PMatchPointer)MSP->ADP->Data)[TopPMP->CSrcI].State |= SC_PMATCH_SRCSTATE;
			Result = 1;
			SC_SMPAuxMStackPop(MSP);
			// And get rid of the CONS placeholder!
			TopPMP = SC_SMPAuxMStackGetTop(MSP);
			if (TopPMP->MainPatP == NULL)
			    SC_SMPAuxMStackPop(MSP);

			goto MStackLoop;
		    }
		
		    IsNeg = !!(CurPatP->Flags & SC_PAT_NEGFLAG);		// 0 or 1 only
		    if ((Result = (IsNeg ^ Result))) {				// Assign
			TopPMP->State |= SC_PMATCH_P2STATE;
			TopPMP->MinRep = SC_SMP_Pos;				// Start TO here (Last SPACE Pos)
			TopPMP->RLastI = TopPMP->RTopI = SC_SMPAuxRStackGetTop();
			Result = 0;						// Until we have a matching TO Pat
			InCRet = 0;
			goto DoAdvance;
		    }
		    // Else... Failed!
		    SC_SMP_Pos = TopPMP->StartPos;
		    SC_SMPAuxMStackPop(MSP);
		    goto MStackLoop;
		}

		// Set up SPAN ********** Then process first Elt
		//
		// Pat has form [FROM (implied space) TO] 		
		// TopPMP->MinRep/MaxRep <-- Start/End of TO Pat (after implied BLANK Pat)
		SC_MATCHPAT_ANNOUNCE(TopPMP->MainPatP, SC_PatStrArr[SC_PAT_SPAN], TopPMP->PatL);
		TopPMP->State |= SC_PMATCH_P1STATE | SC_PMATCH_SRCSTATE;
		TopPMP->SRep = TopPMP->MaxRep = -1L;	// Stashes Last successful EndPos(TO)
		TopPMP->RTopI = SC_SMPAuxRStackGetTop();		
		SC_MSTACK_LINE(MSP);		
		goto ProcessElt;

	    case SC_PAT_REP: {
		Uns8	IsCONS;
	    
		// *NASTY* all around!  (RStack, MStack, MAX mode, Succ/Fail is Local or CONS...)
		//
		// Stashes Pos + Str + Rep + Len1
		// SRep stores end of last success match, or -1.
		// MinRep stores last success Rep (when greater than initial MinRep)
		
		// P1STATE: Does REP match **********
		//
		// NOTE: CONS flag is for the entire REP pattern... so look in MainPatP.  No
		//	 "lattitude" if non-zero MinRep equals MaxRep, so *NO* CONS!  Also *NO* CONS
		//	 if below MinRep! (MinRep/MaxRep may be initially set by a callback FuncP.)
		//
		//	 RStack should logically get a REP entry *AFTER* all the reps are
		//	 done... but this is impossible to know while doing it!  So Res are put on
		//	 RStack as found, then later deleted if superceded!
		//
		//	 The REP itself can have N entries.  first N-1 might succeed and add to the
		//	 RStack, only for last part to fail!  If Selfish (No CONS), MAX will keep
		//	 going while the whole REP succeeds, so each time, del to the last succ
		//	 (wipe the REP).  Under CONS and MAX, the CONS part may also add to the
		//	 RStack, so del all if another CONS supercedes it.
		//
		//	 MAX regime means GO UNTIL FAILURE, does *NOT* mean find the highest REP
		//	 count or use up the most chars!  REP(ALT(Go(5), Go(2))) will stop when
		//	 Go(5) finds a solution.  It is not smart enough to force Go(2) to maximize
		//	 REPs! (REP Cannot reach inside the ALT to coordinate!)
		//
		//	 **** IsCONS handling: MinRep is changed to record a success, so *may* equal
		//	 MaxRep!  But this does no harm, as it WOULD be the last rep and will exit.
		//
		//	 **** Original SUCC: REP (and SPAN) may be MAXimal, so will not stop on
		//	 SUCC.  Instead, they store the SUCC and keep looking for another.  They
		//	 will keep going until they fail, then return the stored (previous) SUCC.
		//	 But the search "context" (SC_SMP_Pos and RStack) will be *different* for
		//	 the original SUCC PMatch and a CONS copy of it coming N steps later.  If
		//	 the CONS copy fails, it should not rely on the ORIGINAL's previous SUCC.
		//	 Instead it should fail and pop PMatch recs, until reaching one that can
		//	 succeed, perhaps the original SUCC PMatch (which could look for more)!
		//	 (SC_PMATCH_SUCCSTATE)
		//
		// Delete RLastI-to-RTopI if new SUCC (***reverse of SPAN***).		

		if (TopPMP->State & SC_PMATCH_CONSSTATE) {
		    // Rest CONSSTATE.  Ignore IsNeg, will not need it after CONSReturn
		    TopPMP->State &= ~SC_PMATCH_CONSSTATE;
		    if (Result) TopPMP->State |= SC_PMATCH_SUCCSTATE;
		    goto RepCONSReturn;
		}
		
		if (TopPMP->State & SC_PMATCH_P1STATE) {

		    SC_MSTACK_LINE(MSP);
		    IsNeg = !!(CurPatP->Flags & SC_PAT_NEGFLAG);	// 0 or 1 only
		    // CONS if (CONSFlag) and (there is a sup PMatch) and (MinRep != MaxRep)!
		    IsCONS = (TopPMP->MainPatP->Flags & SC_PAT_CONSFLAG)
				&& (MSP->TopI > 1)
			        && !(TopPMP->MinRep && (TopPMP->MinRep == TopPMP->MaxRep));
		    Result = (IsNeg ^ Result);

		    if (Result) TopPMP->State |= SC_PMATCH_SUCCSTATE;
		    else goto RepFail;				// Failed mid-rep?
		    CurPatP = SC_SMPAuxAdvancePat(TopPMP);

		RepZero:

		    // Matched all the sub-Pats for THIS Rep?
		    if (CurPatP == NULL) {
			
			if (TopPMP->Rep < TopPMP->MinRep) {
			    // Below min?  Loop quickly, can fail, but cannot succeed yet!!
			    TopPMP->CurPatP = TopPMP->MainPatP;		// Match from first EltPat again
			    TopPMP->I = 1;				// Skip limits (Data 0 and 1)
			    TopPMP->Rep += 1;				// Starting new Rep!
			    goto DoAdvance;
			}

			// Over Max?  See if had previous success.
		        if (TopPMP->MaxRep && (TopPMP->Rep > TopPMP->MaxRep))
			    goto RepFail;

			TopPMP->CurPos = SC_SMP_Pos;			// CONS may change SC_SMP_Pos!
			TopPMP->RTempI = SC_SMPAuxRStackGetTop();	// In case CONS fails
			if (IsCONS && !InCRet) {
			    SC_SMPAuxDoStash(TopPMP);			// Stash just in case!
			    if (SC_SMPAuxMStackCONSStart(MSP))
				goto MStackLoop;

			RepCONSReturn:
			    IsCONS = 1;					// Will jump here if RepCONSReturn!
			    TopPMP = SC_SMPAuxMStackGetTop(MSP);	// In case MStackP->Data moves!
			    SC_SMP_Pos = TopPMP->CurPos;			    
			    if (! Result) {
				if (SC_SMPAuxPatErrorOut) return 0;
				// CONS failed, but SELF succeeds!  Rep Again if not Max limited!
				if (TopPMP->MaxRep && (TopPMP->Rep == TopPMP->MaxRep)) goto RepFail;
				SC_SMPAuxRStackSetTop(TopPMP->RTempI);	// In case CONS added a few!
				SC_RSTACK_LINE(MSP, "-RepC");				
				goto RepAgain;
			    }
			}

			// If there was a previous success, it has now been superceded!  Pull what
			// CONS added, from RTopI (beg) to RLastI (end).
			if (SC_SMP_RStackP && (TopPMP->SRep != -1L) && (TopPMP->RTopI < TopPMP->RLastI)) {
			    SC_SMPAuxRStackDel(TopPMP, TopPMP->RTopI, TopPMP->RLastI);
			    SC_RSTACK_LINE(MSP, "-RepS");
			    // RTemp will be before or after, never IN del range!
			    if (TopPMP->RTempI >= TopPMP->RLastI)	// After del range, decr.
				TopPMP->RTempI -= (TopPMP->RLastI - TopPMP->RTopI);
			}

			// Store SUCC match (RTopI will be after succ REP Pats if SELFISH regime!)
			// MinRep will change here... (NO CONS if MinRep==MaxRep) but will exit if
			// Rep == MaxRep, so no danger.
			TopPMP->SRep = TopPMP->CurPos;
			TopPMP->MinRep = TopPMP->Rep;
			TopPMP->RTopI = TopPMP->RTempI;
			TopPMP->RLastI = SC_SMPAuxRStackGetTop();		// Just after CONS... for next cycle
			if (TopPMP->CRetI == -1)
			    TopPMP->CRetI = SC_SMPAuxMStackGetRetI(MSP);	// Store MStack I+1 for last SRC

			// *DONE* if MIN regime, or hit MaxRep... otherwise Rep again!
			if ((TopPMP->MainPatP->Flags & SC_PAT_MINFLAG) ||
			    ((TopPMP->MaxRep) && (TopPMP->Rep == TopPMP->MaxRep))) {
			    SC_SMP_Pos = TopPMP->CurPos;
			    // if NOT CONS, MStackLoop will SC_SMPAuxRStackPush... so do NOT here!
			    if (IsCONS) {
				SC_SMPAuxRStackPush(TopPMP);
				SC_RSTACK_LINE(MSP, "+Rep");
				SC_SMPAuxMStackCONSReturn(MSP);
				InCRet = 1;
			    }
			    goto MStackLoop;
			}

			SC_MATCH_CHECKABORT;
			goto RepAgain;
		    }
		    
		    // More Elt for this REP Pat to process
		    goto ProcessElt;

		RepFail:
		    // Went as far as we could... but last failed.  REP will end!
		    if (TopPMP->SRep != -1L) {
			// Had a previous success... rewind to it... (Can get here under MAX without CONS!)
			// Only if *THIS* PMatch succeeded, not its src...
			if (TopPMP->State & SC_PMATCH_SUCCSTATE) {
			    Result = 1;					// Success
			    SC_SMP_Pos = TopPMP->SRep;			// Restore last SUCCESSFUL Pos (Includes CONS!)
			    TopPMP->Rep = TopPMP->MinRep;
			    SC_SMPAuxRStackSetTop(TopPMP->RLastI);	// Wipe back to end of last succ (Includes CONS)
			    SC_SMPAuxRStackPush(TopPMP);
			    SC_RSTACK_LINE(MSP, "RepF+P");
			    SC_SMPAuxDoStash(TopPMP);			// Stash backtracked values, again.
			    if (IsCONS) {
				SC_SMPAuxMStackCONSReturn(MSP);
				SC_MSTACK_LINE(MSP);			// 1 more to Help debugging
				InCRet = 1;
			    } else
				SC_SMPAuxMStackPop(MSP);		// Done with this REP Pat.

			} else {
			    // SUCC was due to SRC, but this is a copy, continue unwinding the CONS.
			    Result = 0; InCRet = 1;			// Returning up the chain!
			    // Leaving RStackTop and SC_SMP_Pos untouched ????
			    SC_RSTACK_LINE(MSP, "RepF-O");
			    SC_MSTACK_FAIL(MSP);
			    SC_SMPAuxMStackSrcFailed(MSP, TopPMP);
			    SC_SMPAuxMStackPop(MSP);			// Done with this... propagate Fail
			}

		    } else {
			// No previous success, ... failed completely.
			Result = 0; InCRet = 0;
			SC_SMPAuxRStackSetTop(TopPMP->RTopI);		// Wipe any intermediate Res...
			SC_RSTACK_LINE(MSP, "RepF-P");			
			SC_MSTACK_FAIL(MSP);
			SC_SMPAuxMStackSrcFailed(MSP, TopPMP);		    
			SC_SMP_Pos = TopPMP->StartPos;
			SC_SMPAuxMStackPop(MSP);
		    }
		    SC_MATCH_CHECKABORT;
		    goto MStackLoop;

		RepAgain:
		    SC_SMP_Pos = TopPMP->CurPos;			// May have been changed by CONS!
		    TopPMP->CurPatP = TopPMP->MainPatP;			// Match from first EltPat again
		    TopPMP->I = 1;					// Skip limits (Data 0 and 1)
		    SC_MSTACK_LINE(MSP);
		    TopPMP->Rep += 1;					// Starting new Rep
		    InCRet = 0;
		    goto DoAdvance;
		}

		// Initial REP Pat... gets MinRep/MaxRep ********** Then process first Elt (Pat/Str)
		SC_MATCHPAT_ANNOUNCE(TopPMP->MainPatP, SC_PatStrArr[SC_PAT_REP], TopPMP->PatL);
		if (! SC_SMPAuxGetRepLimits(TopPMP, CurPatP)) return 0;
		SC_MSTACK_LINE(MSP);
		// REP is the only real LOOP Pat... Span acts differently!
		TopPMP->State |= SC_PMATCH_P1STATE | SC_PMATCH_SRCSTATE;
		TopPMP->RTopI = SC_SMPAuxRStackGetTop();
		TopPMP->I = 1;						// Skip limits (Data 0 and 1)
		TopPMP->Rep = 1;					// 1-based Count
		TopPMP->SRep = -1L;					// Starting with no "Prev" success
		
		// If MinRep is 0, then do REP 0... will do *NO* str/pat/func matching!!
		if (TopPMP->MinRep == 0) {
		    TopPMP->Rep = 0;
		    CurPatP = TopPMP->CurPatP = NULL;			// Matches *NOTHING*!!
		    // Ignore IsNeg!  But need IsCONS
		    IsCONS = (TopPMP->MainPatP->Flags & SC_PAT_CONSFLAG) && (MSP->TopI > 1);
		    goto RepZero;
		}
		
		goto DoAdvance;
	    }

	    case SC_PAT_CHAR: {
		    Uns32	U;	// Unicode Char
		    Uns8	L;

		    // Stashes Pos + Str + Len1 + Rep (Rep will store Unicode for char).
		    TopPMP->State |= SC_PMATCH_SRCSTATE;		    
		    SC_MATCHPAT_ANNOUNCE(TopPMP->MainPatP, SC_PatStrArr[SC_PAT_CHAR], TopPMP->PatL);
		    L = SC_MPCharUTF8ToUni(ED_PosToPtr(SC_SMP_BufP, SC_SMP_Pos), &U);
		    TopPMP->Rep = U;
		    if (U < 0x80)
			Result = (CurPatP->Data[0] && ((*(SC_PatCMapPointer)CurPatP->Data[0]).Data[U >> 3] & (0x80 >> (U & 0x07))));
		    else
			Result = SC_MPUCharInCRange((SC_PatCRangePointer)CurPatP->Data[1], U);

		    if (Result)
			SC_SMP_Pos += L;		// Could be 1 to 4 bytes
		    else {
			SC_MSTACK_FAIL(MSP);
			SC_SMPAuxMStackSrcFailed(MSP, TopPMP);
		    }
		    SC_MSTACK_LINE(MSP);
		    TopPMP->CurPatP = NULL;
		    TopPMP->RTopI = SC_SMPAuxRStackGetTop();
		    goto MStackLoop;
		}

	    case SC_PAT_GO: {
		    Uns8	IsRel, IsV;

		    // Stashes Pos + Str + Len1.
		    TopPMP->State |= SC_PMATCH_SRCSTATE;		    
		    SC_MATCHPAT_ANNOUNCE(TopPMP->MainPatP, SC_PatStrArr[SC_PAT_GO], TopPMP->PatL);
		    IsRel = ! (CurPatP->Flags & SC_PAT_ABSFLAG);
		    IsV = CurPatP->Flags & SC_PAT_VFLAG;
		    if (! SC_SMPAuxGetValue(TopPMP, CurPatP)) return 0;
		    SC_MSTACK_LINE(MSP);
		    
		    if (IsV) {
			// Dealing with lines (V)... always to beginning of line.
			SC_SMP_Pos = (IsRel)
				      ? ED_BufferGetNthLinePos(SC_SMP_BufP, SC_SMP_Pos, TopPMP->Rep)
				      : ED_BufferGetNthLinePos(SC_SMP_BufP,
								(TopPMP->Rep < 0) ? SC_SMP_LastPos : SC_SMP_FirstPos,
								TopPMP->Rep);
		    } else {
			// Dealing with Pos (H)... Go +/-Rep *CHARS* forward or back!
			SC_SMP_Pos = (IsRel)
				      ? ED_BufferPosDelta(SC_SMP_BufP, SC_SMP_Pos, TopPMP->Rep)
				      : ED_BufferPosDelta(SC_SMP_BufP,
							  (TopPMP->Rep < 0) ? SC_SMP_LastPos : SC_SMP_FirstPos,
							  TopPMP->Rep);
		    }

		    // Result is 0 if invalid move!!
		    if (! ((Result = ((SC_SMP_FirstPos <= SC_SMP_Pos) && (SC_SMP_Pos <= SC_SMP_LastPos))))) {	// Assign
			SC_SMP_Pos = TopPMP->StartPos;
		    }

		    if (! Result) {
			SC_MSTACK_FAIL(MSP);	// Only signals if Result==0
			SC_SMPAuxMStackSrcFailed(MSP, TopPMP);
		    }
		    TopPMP->CurPatP = NULL;
		    TopPMP->RTopI = SC_SMPAuxRStackGetTop();
		    goto MStackLoop;
		}

	    case SC_PAT_AT: {
		    Uns8	IsRel, IsV;

		    // Stashes nothing... just default values.
		    // NOTE: ABS is relative to Beg/End of Buffer!
		    //	     REL is relative to FirstPos/LastPos of MATCH.
		    TopPMP->State |= SC_PMATCH_SRCSTATE;		    
		    SC_MATCHPAT_ANNOUNCE(TopPMP->MainPatP, SC_PatStrArr[SC_PAT_AT], TopPMP->PatL);
		    IsRel = ! (CurPatP->Flags & SC_PAT_ABSFLAG);
		    IsV = CurPatP->Flags & SC_PAT_VFLAG;
		    if (! SC_SMPAuxGetValue(TopPMP, CurPatP)) return 0;
		    SC_MSTACK_LINE(MSP);
		    
		    if (IsV) {
			// Dealing with lines (V)... always from beginning of line.
			Result = (ED_BufferGetNthLinePos(SC_SMP_BufP, SC_SMP_Pos, 0) == (IsRel)
				  ? ED_BufferGetNthLinePos(SC_SMP_BufP,
							   (TopPMP->Rep < 0) ? SC_SMP_LastPos : SC_SMP_FirstPos,
							   TopPMP->Rep)
				  : ED_BufferGetNthLinePos(SC_SMP_BufP,
							   (TopPMP->Rep < 0) ? ED_BufferGetLastPos(SC_SMP_BufP) : 0,
							   TopPMP->Rep));
		    } else {
			// Dealing with POS (H).
			Result = (IsRel)
				  ? (SC_SMP_Pos == ED_BufferPosDelta(SC_SMP_BufP,
								     (TopPMP->Rep < 0) ? SC_SMP_LastPos : SC_SMP_FirstPos,
								     TopPMP->Rep))
				  : (SC_SMP_Pos == ED_BufferPosDelta(SC_SMP_BufP,
								     (TopPMP->Rep < 0) ? ED_BufferGetLastPos(SC_SMP_BufP) : 0,
								     TopPMP->Rep));
		    }

		    if (! Result) {
			SC_MSTACK_FAIL(MSP);
			SC_SMPAuxMStackSrcFailed(MSP, TopPMP);
		    }
		    TopPMP->CurPatP = NULL;
		    TopPMP->RTopI = SC_SMPAuxRStackGetTop();
		    goto MStackLoop;
		}
	} // Switch (PatOp)

	return Result;

    DoAdvance:
	if (SC_SMPAuxPatErrorOut) return 0;
    
	// Proceed normally *AFTER* the first (re-enterred) TopPMP frame...
	CurPatP = SC_SMPAuxAdvancePat(TopPMP);
	if (CurPatP == NULL) {
	    // End of MainPat elts... stash result if required.
	    InCRet = 0;
	    if (Result) {
		SC_SMPAuxDoStash(TopPMP);
		SC_SMPAuxRStackPush(TopPMP);
		SC_RSTACK_LINE(MSP, "+Adv");
	    } else {
		SC_MSTACK_LINE(MSP);	    
		SC_MSTACK_FAIL(MSP);
		SC_SMPAuxMStackSrcFailed(MSP, TopPMP);		    
	    }

	    SC_SMPAuxMStackPop(MSP);
	    goto MStackLoop;
	    
	} // Else fall through to ProcessElt!

    ProcessElt:
	EltPatP = (SC_PatPointer)CurPatP->Data[TopPMP->I];
	if (EltPatP) {
	    SC_VarVPointer	VarP;

	    InCRet = 0;
	    // DYNamic... so read it from the PDict (SC_SMP_PDictP)!
	    if ((CurPatP->Flags & SC_PAT_DYNFLAG) &&
		    ! ((EltPatP = (SC_PatPointer)SC_SMPAuxGetDynPat(EltPatP)))) {
		SC_RunErrorInfoStr = "MatchPat: Dyn Elt could not be found in PDict.";
		SC_SMPAuxPatErrorOut = SC_RUN_USERERR;
	    }
	
	    if (EltPatP->Type == SC_CHUNK_CLOS) {
		SC_VarVRecord	PosVar;
		
		// Process user FUNC to get EltPatP, Str, or Result (Int)
		// Push SC_SMP_PDictP (In) and SC_SMP_Pos (Out) on Call stack
		//
		// NOTE:  FUNC can *CHANGE* SC_SMP_Pos!!
		VarP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
		VarP->ValueType = SC_VAL_PTR;
		VarP->ValueP = SC_SMP_PDictP;
		SC_VARREF(SC_SMP_PDictP);

		VarP = SC_RUNSTACK_PUSH(SC_SMP_VMP);
		VarP->ValueType = SC_VAL_PTR;
		VarP->ValueP = &PosVar;			// NOT on a slab... *FAKE* VarVRecord!!
		    PosVar.Type = SC_ENTRY_VAR;
		    PosVar.State = SC_NOSTATE;
		    PosVar.Flags = 0;
		    PosVar.ValueType = SC_VAL_INT;
		    PosVar.RefCount = 57;		// Fake RefCount, DO NOT DeRef on returns!
		    PosVar.ValueP = (void *)(Int64)SC_SMP_Pos;

		// Stash VMP Run Context, ceate RunP (1 In, 1 Out args), then Call Func!
		SC_SMPAuxRunCallBack((SC_ClosPointer)EltPatP, (1 << 8) + 1);
		SC_MATCH_CHECKABORT;
		
		// Set VarP to returned value
		VarP = SC_RUNSTACK_POP(SC_SMP_VMP);
		if (VarP->ValueType == SC_VAL_PTR)
		    VarP = (SC_VarVPointer)VarP->ValueP;
		else
		    VarP->Type = SC_ENTRY_VAR;		// Stack entry does not set it!

		// Func can move Pos... Set it and check it!
		SC_SMP_Pos = (Int32)(Int64)PosVar.ValueP;
		if (! ((Result = ((SC_SMP_FirstPos <= SC_SMP_Pos) && (SC_SMP_Pos <= SC_SMP_LastPos))))) {	// Assign
		    SC_SMP_Pos = TopPMP->StartPos;
		    TopPMP->CurPatP = NULL;
		    goto MStackLoop;
		}

	    } else
		VarP = (SC_VarVPointer)EltPatP;

	    // Str, Pat, or Int... can be nothing else!
	    if (VarP->Type == SC_ENTRY_STR) {
		// Match string to data... default is Case INSensitive!
		Result = SC_SMPAuxString(VarP, ! (TopPMP->CurPatP->Flags & SC_PAT_SENFLAG));

	    } else if (VarP->Type == SC_ENTRY_PAT) {
		// New Pat, start at its beginning... Iterate, don't recurse :-)
		SC_SMPAuxMStackPush(MSP, (SC_PatPointer)VarP, (SC_PatPointer)VarP, SC_SMP_Pos, TopPMP->PatL+1);
		// Set LASTSTATE if this is last EltPat... Last of REP does not count.
		// ???? IsEltLast for Pat returned by callback FUNC... should work, but test.
		SC_SAFE_CHECK(TopPMP->MainPatP, "Match: Advancing CONS placeholder!");
		if ((TopPMP->MainPatP->PatOp != SC_PAT_REP) && SC_SMPAuxIsEltLast(CurPatP, TopPMP->I))
		    ((SC_PMatchPointer)(MSP->ADP->Data) + MSP->TopI - 1)->State |= SC_PMATCH_LASTSTATE;

	    } else if ((VarP->Type == SC_ENTRY_VAR) &&
		       (VarP->ValueType == SC_VAL_INT)) {
		// INT, can only come from user FUNC or DYN Match Dict
	        Result = !!VarP->ValueP;		// 0 or 1 only

	    } else {
		SC_RunErrorInfoStr = "MatchPat: Elt (from FUNC or PDict) is not Int (result), Str, or Pat.";
		SC_SMPAuxPatErrorOut = SC_RUN_USERERR;
	    }
	}
	
	goto MStackLoop;
    }


// Pat.match(Buffer Pat/Str Mode/Fn StartPos FirstPos LastPos RFunc)(PatDict MStartPos MEndPos)
//	     REQ^^^ REQ^^^^ opt^^^^ opt^^^^^ opt^^^^^ opt^^^^ opt^^  opt^^^^ opt^^^^^ opt^^^
//
// IN args are (Buffer Pat/Str Mode FirstPos LastPos RFunc)
//
//	Pat/Str may use a string as a shortcut, uses default InSen, Pos, Str flags!
//      Mode: #ONCE  --> Find it once, then exit
//	      #SLIDE --> Find again, StartPos = MStartPos + 1
//	      #SKIP  --> Find again, StartPos = MEndPos
//	      MFunc   --> Call Script function, will return what to do
//			   returns #DONE   --> Stop, match is done.
//			   returns #CUSTOM --> Go again, FUNC will set StartPos
//			   returns #SLIDE  --> Go again, StartPos = MStartPos + 1
//			   returns #SKIP   --> Go again, StartPos = MEndPos
//	StartPos: Pos to start matching from, assumed 0
//		  (If StartPos is 0, it is set to FirstPos)
//	FirstPos: Lowest Pos limit in Buf, assumed 0
//	LastPos:  Highest Pos limit in Buf, assumed ED_BufferGetLastPos
//	RFunc:    Script callback after SUCC complete, on RStack elts
//		  (Only if provided... No RStack if no ResFunc)
//
//	Terminates if CurPos < FirstPos or >= LastPos!
//
//	Takes only a *SINGLE* Pat/String, without any flags or STASH
//	directives.  Use a SEQ if requiring more args/control.
//
// OUT args are (PatDict MStartPos MEndPos)
//	All are optional, may also be given as NULL.
//	PatDict is a dictionary to stash/read DYN Pat variables.
//	MStartPos will get the start Pos of a succ match
//	MEndPos will get the end Pos of a succ match
//
// Pushes 0 (on RunStack) if Fail, N if matching N times (return value to script).
// Returns SC_NO_ERR if terminates with no errors (return value to C caller).

    Uns32	SC_SMP_ReEntGuard = 0;
    void	SC_SMPAuxReEnter(SC_MSavePointer MP)
    {
	if (SC_SMP_ReEntGuard++) {
	    // Being re-enterred... so stash global state in MP!
	    MP->BufP = SC_SMP_BufP;
	    MP->PDictP = SC_SMP_PDictP;
	    MP->ResFuncP = SC_SMP_ResFuncP;
	    MP->RStackP = SC_SMP_RStackP;
	    MP->Pos = SC_SMP_Pos;
	    MP->SkipPos = SC_SMP_SkipPos;
	    MP->FirstPos = SC_SMP_FirstPos;
	    MP->LastPos = SC_SMP_LastPos;
	    MP->MaxConsL = SC_SMP_MaxConsL;
	    MP->StepCount = SC_SMP_StepCount;
	}
    }

    void	SC_SMPAuxReExit(SC_MSavePointer MP)
    {
	if (--SC_SMP_ReEntGuard) {
	    // Existing from re-enterred call... restore global state from MP!
	    SC_SMP_BufP = MP->BufP;
	    SC_SMP_PDictP = MP->PDictP;
	    SC_SMP_ResFuncP = MP->ResFuncP;
	    SC_SMP_RStackP = MP->RStackP;
	    SC_SMP_Pos = MP->Pos;
	    SC_SMP_SkipPos = MP->SkipPos;
	    SC_SMP_FirstPos = MP->FirstPos;
	    SC_SMP_LastPos = MP->LastPos;
	    SC_SMP_MaxConsL = MP->MaxConsL;
	    SC_SMP_StepCount = MP->StepCount;
	}
    }

    #define SC_SMP_RESETCOUNT		1	// Resets MaxConsL and StepCount each iteration
    
Int16	SC_SysMatchPat(SC_VMPointer VMP, Uns16 InOutCount)
{
    Uns8		InCount = InOutCount;		// Just low byte
    Uns8		OutCount = InOutCount >> 8;
    Uns8		TotCount = OutCount + InCount;

    SC_MSaveRecord	MSave;				// Stash state if Re-Entered!
    SC_MStackRecord	MStack;
    SC_RStackRecord	RStack;
    SC_VarVPointer	VarP, NextStartPosVP, MStartPosVP, MEndPosVP, PDictP;
    void *		BufP;
    SC_VarVPointer	ArgP;
    Int64		Res;
    Int64		Count;				// How many SUCC matches in Scan
    Int16		I;
    Int32		LastPos, StartPos;
    Uns8		Mode;
    SC_ClosPointer	ModeFP;

    SC_SMPAuxPatErrorOut = SC_RUN_NOERR;
    SC_MATCH_PRIMEABORT;
    
    SC_SAFE_CHECK(SC_RUNSTACK_HASN(VMP, TotCount), SC_BADRUN_Stack);
    if (InCount < 2) {
	SC_RunErrorInfoStr = "MatchPat: missing required args.";
	return SC_RUN_BADARG;
    }

    I = 1;
    VarP = SC_RUNSTACK_GETNTH(VMP, TotCount);		// First IN

    // Get BufP
    if ((VarP->ValueType != SC_VAL_INT) ||
	! ((BufP = VarP->ValueP)) ||
	! ED_BufferCheckPtr(BufP)) {
	SC_RunErrorInfoStr = "MatchPat: Bad Buffer arg.";
	return SC_RUN_BADARG;				// Not a BufP!
    }
    I++, VarP += 1, InCount--;

    // Get the Pat/Str arg
    if ((VarP->ValueType != SC_VAL_PTR) ||
	! ((ArgP = (SC_VarVPointer)VarP->ValueP)))	// Its NULL!
	    goto ErrorError;				// Will check Type later...
    I++, VarP += 1, InCount--;

    // Set up global match state for recursion and sub-func calls!!
    // Might be Re-Entrant (Script Func from inside Pat being matched)
    // So stash previous global state if there was one!
    SC_SMPAuxReEnter(&MSave);
    
    SC_SMP_VMP = VMP;
    SC_SMP_BufP = BufP;
    SC_SMP_PDictP = NULL;
    SC_SMP_ResFuncP = NULL;
    SC_SMP_RStackP = NULL;
    SC_SMP_Pos = SC_SMP_FirstPos = SC_SMP_LastPos = 0L;
    StartPos = 0L;
    SC_SMP_StepCount = SC_SMP_MaxConsL = 0L;

    Mode = SC_PMODE_ONCE;
    ModeFP = NULL;
    if (InCount) {
	if (VarP->ValueType == SC_VAL_INT) {
	    if ((Uns64)VarP->ValueP >= SC_PMODE_ERROR) goto ErrorError;
	    Mode = (Uns8)(Uns64)VarP->ValueP;

	} else if (VarP->ValueType == SC_VAL_PTR) {
	    ModeFP = (SC_ClosPointer)VarP->ValueP;
	    if (ModeFP->Type != SC_CHUNK_CLOS)
		goto ErrorError;

	} else
	    goto ErrorError;

	I++, InCount--, VarP++;
    }

    // Get optional Mode/Fn StartPos, FirstPos, LastPos.
    while (InCount) {
	if (I == 4) {
	    if (VarP->ValueType != SC_VAL_INT) goto ErrorError;
	    StartPos = SC_SMP_Pos = (Int32)(Int64)VarP->ValueP;
	} else if (I == 5) {
	    if (VarP->ValueType != SC_VAL_INT) goto ErrorError;
	    SC_SMP_FirstPos = (Int32)(Int64)VarP->ValueP;
	} else if (I == 6) {
	    if (VarP->ValueType != SC_VAL_INT) goto ErrorError;
	    SC_SMP_LastPos = (Int32)(Int64)VarP->ValueP;
	} else if (I == 7) {
	    if (VarP->ValueType != SC_VAL_PTR) goto ErrorError;
	    SC_SMP_ResFuncP = (SC_ClosPointer)VarP->ValueP;
	    if (SC_SMP_ResFuncP && (SC_SMP_ResFuncP->Type == SC_CHUNK_CLOS))
		SC_SMP_RStackP = SC_SMPAuxRStackAlloc(&RStack);
	    else
		goto ErrorError;
	} else break;		// Extra nonsense... ignored

	I++, InCount--, VarP++;
    }

    // Check values
    LastPos = ED_BufferGetLastPos(BufP);
    if (SC_SMP_LastPos == 0) SC_SMP_LastPos = LastPos;
    if (SC_SMP_Pos == 0) SC_SMP_Pos = SC_SMP_FirstPos;
    else if ((SC_SMP_LastPos < 0) || (SC_SMP_LastPos > LastPos)) {
	SC_RunErrorInfoStr = "matchPat: Bad To Pos (Arg 6).";
	return SC_RUN_BADARG;
    }

    if ((SC_SMP_FirstPos < 0) || (SC_SMP_FirstPos > SC_SMP_LastPos)) {
	SC_RunErrorInfoStr = "MatchPat: Bad From Pos (Arg 5).";
	return SC_RUN_BADARG;
    }

    if ((SC_SMP_Pos < SC_SMP_FirstPos) || (SC_SMP_Pos > SC_SMP_LastPos)) {
	SC_RunErrorInfoStr = "MatchPat: Bad Match Pos (Arg 4).";
	return SC_RUN_BADARG;
    }

    // Figure out the OUT list
    PDictP = MEndPosVP = MStartPosVP = NextStartPosVP = NULL;
    // NOTE:  OUT args are pass-by-ref... so Stack has Ptr to Var!    
    if (OutCount) {
	// First is PatDict or NULL
	PDictP = SC_RUNSTACK_GETNTH(VMP, OutCount);
	SC_SAFE_CHECK((PDictP->ValueP == 0LL) || (PDictP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	PDictP = (SC_VarVPointer)PDictP->ValueP;		// Could be NULL
	if (OutCount > 1) {
	    MStartPosVP = SC_RUNSTACK_GETNTH(VMP, OutCount - 1);
	    SC_SAFE_CHECK((MStartPosVP->ValueP == 0LL) || (MStartPosVP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	    MStartPosVP = (SC_VarVPointer)MStartPosVP->ValueP;	// Could be NULL
	}
	if (OutCount > 2) {
	    MEndPosVP = SC_RUNSTACK_GETNTH(VMP, OutCount - 2);
	    SC_SAFE_CHECK((MEndPosVP->ValueP == 0LL) || (MEndPosVP->ValueType == SC_VAL_PTR), SC_BADRUN_Type);
	    MEndPosVP = (SC_VarVPointer)MEndPosVP->ValueP;	// Could be NULL
	}
	// Ignore the rest!
    }

    // Make sure variable points to actual Dict
    if (PDictP && (PDictP->ValueType == SC_VAL_PTR)) {
	SC_SMP_PDictP = (SC_DictVPointer)(PDictP->ValueP);
	if (SC_SMP_PDictP->Type != SC_ENTRY_DICT) {
	    SC_VARDEREF(VMP, (SC_VarVPointer)SC_SMP_PDictP);
	    SC_SMP_PDictP = NULL;
	}
    }

    if (SC_SMP_PDictP == NULL)
	SC_SMP_PDictP = SC_DictCreateNewDict(VMP, 0);
    if (PDictP)
	PDictP->ValueType = SC_VAL_PTR, PDictP->ValueP = SC_SMP_PDictP;

    if (ModeFP) {
	// Create NextStartPosVP
	NextStartPosVP = SC_VarEntryNew(SC_SMP_VMP);		// INT by default
	NextStartPosVP->ValueP = (void *)(Int64)StartPos;

	if (MStartPosVP == NULL) MStartPosVP = SC_VarEntryNew(SC_SMP_VMP);
	if (MEndPosVP == NULL) MEndPosVP = SC_VarEntryNew(SC_SMP_VMP);
    }

    // These could be pre-existing (from OUT list) or just created for ModeFN!
    if (MStartPosVP) {
	SC_VARDEREFCOND(VMP, MStartPosVP->ValueType == SC_VAL_PTR, (SC_VarVPointer)MStartPosVP->ValueP);
	MStartPosVP->ValueType = SC_VAL_INT;
	MStartPosVP->ValueP = (void *)(Int64)SC_SMP_Pos;		// Safe initial value.
    }

    if (MEndPosVP) {
	SC_VARDEREFCOND(VMP, MEndPosVP->ValueType == SC_VAL_PTR, (SC_VarVPointer)MEndPosVP->ValueP);
	MEndPosVP->ValueType = SC_VAL_INT;
	MEndPosVP->ValueP = (void *)(Int64)SC_SMP_Pos;		// Safe initial value.
    }

    Count = Res = 0;
    SC_SMPAuxMStackAlloc(&MStack);
    while (1) {
	// Do the Match!
	SC_MATCH_ANNOUNCE;
	if (ArgP->Type == SC_ENTRY_STR) {
	    // SC_SMPAuxString only returns 1 or 0
	    if ((Res = SC_SMPAuxString(ArgP, 1)))		// Default InSen, Pos, Str (Not Dynamic)
		Count++;
	    else
		SC_SMP_Pos++;

	} else if (ArgP->Type == SC_ENTRY_PAT) {
	    SC_SMP_Pos = SC_SMP_SkipPos = StartPos;
	    SC_MSTACK_START();	
		SC_SMPAuxMStackPush(&MStack, (SC_PatPointer)ArgP, (SC_PatPointer)ArgP, StartPos, 0);
		(*(SC_PMatchPointer)MStack.ADP->Data).State |= SC_PMATCH_FIRSTSTATE;
		Res = SC_SMPAuxPatMatch(&MStack);		// Res can be NEGATIVE! Also generate own Error!
		if (Res > 0) {
		    Count++;
		    SC_SMP_Pos = SC_SMP_SkipPos;
		} else
		    SC_SMP_Pos++;
		if (SC_SMP_RStackP) {
		    if (Res > 0) {
			SC_RSTACK_LINE(&MStack, "Fin");
			SC_MATCH_RSTACKPRINT();
			SC_SMPAuxRStackFuncCall();		// CallBack Script ResFuncs
		    }
		    SC_SMPAuxRStackSetTop(0);			// Empty out
		}
	    SC_SMPAuxMStackSetTop(&MStack, 0);
	    if (SC_SMPAuxPatErrorOut) break;	// out of while(1) **********
	    SC_MATCH_CONCLUDE(Res);
	    SC_MSTACK_FINAL(&MStack, Res);

	} else {
	    I = 2;						// So reports bad Pat/Str arg!
	    if (SC_SMP_RStackP) SC_SMPAuxRStackZap();
	    SC_SMPAuxMStackZap(&MStack);
	    goto ErrorError;
	}

	// Put results on OUT list... if any!  (MEndPosP!!)
	if (MStartPosVP)
	    MStartPosVP->ValueP = (void *)(Int64)StartPos;

	if (MEndPosVP)
	    MEndPosVP->ValueP = (void *)(Int64)SC_SMP_Pos;

	if (Res > 0) {
	    if (ModeFP) {
		Mode = SC_SSPAuxModeFuncCall(ModeFP, NextStartPosVP, MStartPosVP, MEndPosVP);

		if ((NextStartPosVP->ValueType != SC_VAL_INT) ||
		    (MStartPosVP->ValueType != SC_VAL_INT) ||
		    (MEndPosVP->ValueType != SC_VAL_INT)) {
		    SC_RunErrorInfoStr = "MatchPat: ModeFN callback set bad values.";
		    SC_SMPAuxPatErrorOut = SC_RUN_BADARG;
		    break;			// Out of while(1) **********
		}

		StartPos = (Int32)(Int64)NextStartPosVP->ValueP;
		SC_SMP_Pos = (Int32)(Int64)MEndPosVP->ValueP;
	    }
	}
	
	SC_MATCH_CHECKABORT;
	if ((Res < 0) || (Mode == SC_PMODE_DONE))		// Don't match again
	    break;

	if (Mode == SC_PMODE_SLIDE)				// Start 1 after prev start
	    SC_SMP_Pos = ++StartPos;
	else if (Mode == SC_PMODE_SKIP)				// Start  where it ended
	    StartPos = SC_SMP_Pos;
	// Otherwise in #CUSTOM mode.

	// Only have NextStartPosVP if there is a ModeFP!
	if (NextStartPosVP) NextStartPosVP->ValueP = (void *)(Int64)StartPos;

	// Terminate loop when exceeding bounds...
	if ((StartPos < SC_SMP_FirstPos) || (StartPos >= SC_SMP_LastPos))
	    break;

	#if SC_SMP_RESETCOUNT
	    SC_SMP_MaxConsL = SC_SMP_StepCount = 0L;
	#endif

	if (SC_SMPAuxPatErrorOut != SC_RUN_NOERR)
	    break;
	
    }	// while (1)


    // AAABBBDDDĀĀĀbbbāāāccc

    // Proper exit...
    SC_VARDEREF(SC_SMP_VMP, (SC_VarVPointer)SC_SMP_PDictP);
    if (NextStartPosVP) SC_VARDEREF(SC_SMP_VMP, NextStartPosVP);
    if (SC_SMP_RStackP) SC_SMPAuxRStackZap();
    SC_SMPAuxMStackZap(&MStack);

    // Cleaned up Refs... exist if error!  **********
    // Re-entrant entry is NOT restored for error exit, as the whole
    // operation will be abandoned and global state will be moot.
    if (SC_SMPAuxPatErrorOut) return SC_SMPAuxPatErrorOut;

    // Doing a non-err exit, restore if re-entrant!
    SC_SMPAuxReExit(&MSave);
    // DeRef and flush *ALL* args... even unwanted ones!
    SC_RUNSTACK_FLUSH_DEREF(VMP, VMP->StackTop - TotCount);
    // Push result value on stack, return error code.
    return SC_SysAuxReturn(VMP, SC_VAL_INT, (void *)Count);
//	Test--ßa ßßßßßßßßßßßßßßßßß--Test
ErrorError:
    // Dispose RStack and MStack before jumping here.
    snprintf(SC_RunInfoS, SC_RUNINFO_SLEN, "ScanPat: Arg %d is bad.", I);
    SC_RunErrorInfoStr = SC_RunInfoS;
    return SC_RUN_BADARG;    
}
