home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 5 Edit
/
05-Edit.zip
/
me34src.zip
/
me3
/
mc
/
doc
/
mm2.doc
Wrap
Text File
|
1995-01-14
|
29KB
|
961 lines
========================================================================
== The Mutt2 Machine Craig Durland 88, 10/91 ==
========================================================================
Here is info on embedding the Mutt2 machine in a application and the
Mutt2 machine itself.
The Mutt2 Machine is a virtual machine ment to be an embedded language.
It is written in C for ease of porting.
Copyright 1991 Craig Durland
Distributed under the terms of the GNU General Public License.
Distributed "as is", without warranties of any kind, but comments,
suggestions and bug reports are welcome.
========================================================================
== Embedding the Mutt2 Machine ==
========================================================================
Data Types:
maddr
uint8
Example data structures:
block :
typedef struct
{
char *name; /* Name of the block (munged file name) */
uint8 *global_vars; /* Where the global vars are in this block */
maddr code; /* The start of the code in this block */
void *global_object_table;
int num_global_objects;
} CodeBlock;
Programs:
typedef struct /* program address table */
{
maddr addr; /* address of routine */
short int block; /* link to programs code block. 0 => dead */
} Pgm;
To embed the Mutt2 Machine in your C program, write the following
routines:
Function: FILE *MMopen_code_file(char *file_name)
Description:
Open a Mutt code (.mco) file so that it can be loaded by the Mutt
Machine.
Input:
name: Name of code file to open. The extension is ".mco". May have
a leading path.
Returns:
FILE : Pointer to the opened code file.
NULL : Couldn't find the file.
Notes:
You might want to parse a PATH environment variable.
Function: maddr MMpgm_addr(int pgm_id)
Description:
Return the address of the Mutt routine that has pgm_id. Also update
MMglobal_vars, and MMglobal_object_table to point to the proper
areas.
Input:
pgm_id : The id of a Mutt program.
Returns:
Address of the start of the Mutt program code.
Munges:
MMglobal_vars
MMglobal_object_table
Notes:
You only need to update the globals if they will change.
Function: int MMpgm_lookup(char *pgm_name)
Description:
Return the program id of the program that is named pgm_name.
Input:
pgm_name:
Returns:
Program id.
Notes:
Function: void MMblock_name(char *buf, *file_name)
Description:
Create a block name (using file_name if you want) and put it into buf.
Input:
buf:
file_name:
Munges:
buf
Notes:
Function: int MMadd_block(
char *block_name;
maddr code;
uint8 *global_vars;
void *global_object_table;
int num_global_objects)
Description:
Save some information that the Mutt Machine might need to use later.
Input:
block_name: A name returned by MMblock_name. Useful to check to see
if a block is being reloaded.
code: Pointer to the Mutt code. MMpgm_addr() wants this.
global_vars: Pointer to the block-global non-object variables. Also
used by MMpgm_addr().
global_object_table: Pointer to the block-global objects. Used by
MMpgm_addr().
num_global_objects: Number of block-global objects. Used when
calling MMfree_block().
Returns:
A block id that be used in MMadd_pgm(). You need block ids so you can
cross index the program table and the block table. You save to save
memory by keeping a (program-name, block-id) tuple and using the
block table in MMpgm_addr().
Notes:
Function: int MMadd_pgm(char *pgm_name; int block_id; maddr code_addr)
Description:
Add a program to your program table. This so MMpgm_addr() can get the
info.
Input:
pgm_name: Pointer the name of the program. You only need to save a
copy of the pointer, the name itself is in the code block name table.
block_id: Returned by MMadd_block.
code_addr: Start address of the code in the program. Used by
MMpgm_addr().
Returns:
TRUE: Everything went as expected.
FALSE: Ran out of memory or some other real bad thing.
Notes:
Function: void MMset_hooks()
Description:
Hooks for Mutt programs. When certain events occur in the
application, you might want to call a hook so your application can
be extended in useful ways.
Notes:
Called from MM after every Mutt code block is loaded.
Call this at start up to make sure all hooks are
properly initialized to no hook.
Function: void MMbitch(char *message)
Description:
A fatal error has occurred when running a Mutt program. Inform the
user and bail out of the Mutt Machine (usually by calling
MMabort_pgm()).
Input:
message: Text that has some information about what happened. You
might want to add some verbiage saying that the program is aborting.
Notes:
Only the Mutt Machine aborts. The application should be OK (it is up
to the application to decide).
Function: void MMmoan(char *message)
Description:
Inform the user that the Mutt program has errored.
Input:
message: Text that has some information about what happened.
Notes:
This is used for those times when MMabort_pgm() can't be called.
Function: void MMmsg(char *message)
Description:
Give the user a message. Used by MSG: (msg "hoho") will result in
MMmsg("hoho");
Input:
message: Text for the user.
Notes:
Function: void MMask(char *prompt, *buf)
Description:
Ask the user for a response. Used by ASK: (ask "Press a key ") will
result in MMask("Press a key ", &buf);
Input:
prompt:
buf: where to put the reply.
Munges:
buf
Notes:
Function: int MMaux_fcn(char *aux_fcn_name)
Description:
Run a aux function by name. Aux functions are the functions that the
application defines and MM knows nothing about. For example, say
the application is a spread sheet, you have C code that sums a
column and you want the Mutt name for this to be "sum-column". Now,
when a Mutt program does a (sum-column n), MM looks around, can't
find a program named "sum-column", thinks it might be part of the
application and calls MMaux_fcn("sum-column"). Your C code can then
access the Mutt stack (see MMpull_nth_arg()) and sum that column.
Input:
Returns:
TRUE: aux_fcn_name is real and I did it.
FALSE: aux_fcn_name is not something I know about.
Notes:
Function: void MMxtoken(int token)
Description:
Execute a token defined by the application. If you compile a Mutt
program with a token file ("mc2 -t<token-file>"), MM will call
MMxtoken(token). Using the MMaux_fcn() example, if you created a
token file with the entry "123 sum-column" in it, used that file to
compile a Mutt program with (sum-column 3) in it, then, when MM
executes that line, MMxtoken(123) will be called. You then take 123
and figure out you need to call sum_column(3).
Input:
token:
Notes:
MMxtoken() is short hand for MMaux_fcn().
Its assumed that token file is correct - you might want to do some
token checking if this is a bad assumption (it usually is).
Token files can really reduce the (static) space used by a Mutt
program and speed things up (because it gets rid of string lookups).
Token is usually an index into a array of function pointers or a case
in a switch statement.
Function: void MMgc_external_objects()
Description:
Garbage collect all mortal (temporary) objects that may have been
created by Mutt programs and not freed by them.
This is typically called (by the Mutt Machine) after a pgm has been
run or aborted to clean up stuff the programmer forgot to or was
unable to (as in the case where the pgm was aborted).
See also: Section on garbage collection.
Internal Routines of External Interest
-------- -------- -- -------- --------
Routines Mutt extension writers are interested in:
Function: int MMpull_nth_arg(vsitem *val; int n)
Description:
Pull the nth arg out of the stack frame.
This routine is used to get parameters off the stack. For example, if
you are writing the C code for "foo" and it is called like so: (foo
123), then when your foo code is called, you can
MMpull_nth_arg(&RV,0) and RV will be a number with value 123.
Input:
val: Pointer to a var (vsitem). Arg will be stashed there. Usually
&RV.
n: The arg you want to pull. 0 is the first and numargs is 1+ the
last (not that it helps you - you have to use (nargs) or
MMpull_nth_arg() until it returns false.
Output:
val: vsitem is filled in with pointers to nth stack arg. If it is an
object string, it points to the contents of the string.
Returns:
TRUE: Got to the arg
FALSE: n if out range (less than 0 or greater than the number of
args)
Notes:
See also: MMnext_arg().
Function: int MMnext_arg(char *buf)
Description:
Get the next arg in the stack frame, convert it to a string and store
it in a buffer.
Ment for stuff that wants a bunch of ascii info from something and
does the conversions itself (like (ask)). Use this routine when
writing a routine that can get info from either a user or Mutt
program.
Input:
buf: Pointer to a area to store the ascii form of the var in.
Returns:
FALSE: No more args
TRUE: all OK
Munges:
TV
Function: void MMset_ask_frame()
Description:
???
This is ment for self contained opcodes (like ask).
Notes:
Don't use this if you turn around and call MM().
Set MMask_pgm to TRUE after you do the ask.
Function: void MMreset_ask_frame()
Description:
Notes:
Function: int MMgonna_ask_pgm()
Description:
Returns:
Notes:
Function: char *MMvtoa(vsitem *val)
Description:
Input:
Returns:
Notes:
Function: void MMconcat()
Description:
Returns:
Munges:
TV
Notes:
Function: mmaddr MMload_code(char *fname; int f)
Description:
Load a compiled code file and run the code at the entry point.
Input:
Returns:
Notes:
Function: void MMfree_block(
maddr code; Object *block_object_table[]; int objects_in_table)
Description:
Input:
Returns:
Notes:
Function: int MMpgm_running()
Description:
Is a pgm running?
Returns:
FALSE: no pgm running.
n : n pgms are running (interrupts, (load) can cause more than one
pgm to be running at the same time.
Function: int MMrun_pgm(int n)
Description:
Heres where the outside world fires off a Mutt pgm.
Input:
n :
Returns:
FALSE if pgm aborts.
Notes:
If MMrun_pgm() gets called recursively and then aborts, everything is
aborted.
Function: void MMopen_frame(stkframe *mark)
Description:
Input:
Notes:
Function: void MMpush_arg(vsitem *RV)
Description:
Input:
Notes:
Function: void MMclose_frame(stkframe *mark)
Description:
Input:
Notes:
Function: int MMrun_pgm_with_args(int n; stkframe *mark)
Description:
Input:
Returns:
Notes:
See also: MMrun_pgm()
Function: int MMload(char *fname; int f)
Description:
Load a code file (block) and run the MAIN code.
Input:
fname: Name of the file that contains the code. The application
knows how to interpret this (see MMopen_code_file()).
f: TRUE: Complain if can't open fname.
Output:
TRUE: Block loaded and MAIN code ran to completion.
FALSE: Block didn't load, MAIN didn't run or something else failed.
A message was probably issued.
Function: int MMinitialize()
Description:
Initialize the Mutt Machine. This is called by the application ONCE so
MM can set things up.
Call this AFTER everything else in your application has been initialized
(this might call back into the application).
Returns:
TRUE: Everything went as expected.
FALSE: Mutt Machine can't be initialized, don't use it!
Function: void MMabort_pgm(int dump_level)
Description:
Input:
Notes:
Variables of Interest
--------- -- --------
Variable: char result[]
Description:
Initial value: ""
Notes:
Variable: int MMask_pgm
Description:
Initial value: FALSE
Notes:
Variable: uint8 *MMglobal_vars
Description:
Initial value: NULL
Notes:
Variable: Object *MMglobal_object_table[]
Description:
Initial value: NULL
Notes:
========================================================================
== Mutt2 Machine Porting Notes ==
========================================================================
========================================================================
== Mutt2 Machine Data Types ==
========================================================================
C typedef What it is
- ------- ---- -- --
num8 8 bit unsigned int
num16 16 bit signed int
num32 32 bit signed int
addr num16
offset Offset from start of var storage (num16)
type 8 bit type coding
vpop: pop val stack
vpush(n): push n on val stack
strings are C-strings ie text with a terminating \0. len is num8.
dString: Dynamic string. Grows to fit whatever is placed in it.
(opcode operands) : instruction stream in memory.
GVars : global variable memory
External tokens !!!???
********************************************************************
========================================================================
== Mutt2 Machine Objects and Garbage Collection ==
========================================================================
Most Mutt data is is a fixed size (such as numbers) and can be kept on
the stack or in fixed size data storage areas. Strings and lists are
dynamic and can grow or shrink at the whim of a program. These data
types have to be handled differently from the fixed sized data. For
these types, I use an object manager manipulate the objects and
garbage collection to reclaim "dead" objects.
Garbage collect all mortal (temporary) objects that may have been
created by Mutt programs and not freed by them.
This is typically called (by the Mutt Machine) after a pgm has been run
or aborted to clean up stuff the programmer forgot to or was unable to
(as in the case where the pgm was aborted).
Problems with this method of GC:
I won't notice some dependences between Mutt objects and external
objects. For example, if a global Mutt object creates external
objects, the block GC code will have to be smart enough to free
the external objects. Which ain't goona happen until I add OOP
support and pass the burden back to the programmer (by having
destructors that can be called from the block GC code).
MMgc_external_objects() can only be called when the root pgm has
been terminated (and control returns from MM to the application)
to ensure objects aren't freed while they are still in use. This
means I can't GC when low on memory in hopes of getting some
memory back. This might be a real problem for an application
that just fires off MM and doesn't expect it to return.
Since this might be called quite a lot, it might be expensive to
look though all objects to try and find dead ones (especially if
the Mutt programmer was good and cleaned things up). On the
other hand, it is probably called from a wait-for-keyboard loop
and thats when we have time to burn.
Pros:
This style of GC works well with applications like ME2 - the user
won't see dead buffers and ME2 won't waste time updating dead
marks.
If you have a real GC, you can ignore this call.
Object tables ala Smalltalk
GC is used because too expensive to free objects at the end of each
function.
Garbage Collection for Embedded Systems
------- ---------- --- -------- -------
Need GC because:
If a program tanks, all the objects allocated so far won't be freed.
Less for the programmer to worry about or forget.
Programmers are bound to forget a few frees and never notice.
Mutt strings.
Domains:
Internal Mutt:
dStrings.
OOP: Probably don't need gc - constructors and destructors can
handle the normal cases. GC is needed for when the program is
aborted.
External Mutt:
Objects can be allocated that Mutt will never know how to deal with.
For example: Bags, Marks, Buffers.
Constraints:
Don't want to slow down programs.
Solutions:
Internal:
Pool strings so can gc on abort or program termination. Handle like
external objects (or OOP):
{
(string foo bar)
(foo "this is a test")
(done)
(bar foo)
}
generates code:
{
(int foo bar)
(foo (create-string)) (bar (create-string))
(copy-to-string foo "this is a test") ;; (foo "this is a test")
(goto gc-strings) ;; (done)
(copy-to-string bar (get-string foo)) ;; (bar foo)
(label gc-strings)
(free-string foo bar)
(done)
}
External:
When external objects are allocated, they are flagged Mortal or
Immortal. When a program stops running or aborts,
MMgc_foreign_objects() is called. The external "process" can then
sweep the object lists and remove all objects marked Mortal.
Pros:
Easy. Doesn't slow program execution.
Cons:
Not real GC. If the programmer is lazy and doesn't free stuff, it
could be while before the resources are freed (programs that
repeatedly call a routine that creates objects).
========================================================================
== Mutt2 Machine Code File Layout ==
========================================================================
What the MC2 compiler puts into a .mco file:
<Header><Code><Program_Name_Table><String_Table><Program_Address_Table>
Header (13 bytes)
Offset Bytes Name
------ ----- ----
0 2 Entry Point
2 2 Code size
4 2 Name table offset
6 2 Number of non-hidden programs contained in this file.
8 2 Bytes of global variable space needed.
10 1 Magic number.
11 2 Number of global objects.
Entry Point: the is a offset from code start.
Code Size: Size (in bytes) of Code, Program-Name-Table and
String-Table. Does not include the Program-Address-Table.
Name table offset: Offset (from the start of the code) of the name
table.
...
!!!
Code
Mutt Machine code.
Program_Name_Table (starts at code + name_table (from header))
A bunch of C strings (text\0text\0 ...). The header contains the
number of these strings.
String_Table
All the string constants used by the programs in this file. C
strings. Dumped in reverse order ie the string at offset 0 is the
last string in this table.
See (set-RV-to-string).
!!!
Program_Address_Table
A bunch of addresses, 1 per program name. You can create
(program_name, program_address) tuples with this data. With that
info, you can run any program given its name.
Notes:
Malloc() code_size + global_space + global_object_table so
String_Table is is the correct place.
========================================================================
== Mutt2 Machine Memory Layout ==
========================================================================
Memory Layouts
<Code><Program-Names><String-Table><Global-Vars><Global-Object-Table>
^ 0 ^ GVars
op stack
var stack (local variables stored in here)
========================================================================
== Mutt2 Machine Registers ==
========================================================================
Registers: RV (return value), TV (temp value). tagged types.
========================================================================
== Mutt2 Machine Stacks ==
========================================================================
Stacks: opcode stack, arg stack
Notes on stack frames
- A stack frame consists of all the data for a pgm
- A stack frame looks like:
*--| vsitem args[]; byte flotsam[]; byte vars[]; |--jetsam--| frame |--
| ^abase ^vbase
^varstack
LVars = varstack + vbase == the start of the local vars.
args are args to a pgm, fcn, etc and are typed.
flotsam: strings and other variable sized stuff attached to a arg.
vars: the local vars. Untyped and "packed".
jetsam: stack usage while a pgm is running.
- Flow
Find out going to be running a pgm:
a stack frame is opened.
args pushed on the frame. flotsam placed after the args.
Pgm is run:
local vars are allocated (must happen before any code is run
or the stack frame could be munged.
frame is closed.
as pgm runs, jetsam accumulates.
Pgm terminates:
frame is removed by "popping" ie the previous frame is restored
(jetsam and all).
Notes on the OP stack
!!!
- opush
========================================================================
== Mutt2 Machine Instruction Set ==
========================================================================
NAME ACTION OPCODE
---- ------ ------
(add) RV = vpop + RV ADD
(sub) RV = vpop - RV SUB
(mul) RV = vpop * RV MUL
(div) RV = vpop / RV DIV
(<) RV = vpop < RV LT
(<=) RV = vpop <= RV LTE
(arg) RV = RVth arg ARG
(ask-user) ASKUSER
Force query of user on next (ask).
(cmp) CMP
Input: RV, TV (top of stack == vpop)
Result: RV (boolean: (RV == TV))
Notes: No type checking. Its up to the compiler to make sure
that RV and TV have the same type.
(convert-to) CONVERT_TO
Input: type (number, on stack), RV (anything)
Result: RV (type)
Example: See mutt2.doc
(do-OP) eval(opop) DOOP ???!!!???
Input: top of op stack
Result:
case OPADDRESS:
case OPTOKEN:
case OPNAME:
case OPXTOKEN:
(done) DONE
Return (exit) from the current routine.
Result:
Restore previous stack frame and continue execution.
If no previous stack frame, ???!!!???
(halt) Stop MM HALT
Stop all pgms no matter how deeply nested or recursed MM is.
(stop) Kinder, gentler halt STOP
Return from MM(). Only need this if trying to make MM non
recursive. Not generated by the compiler. The initial
stackframe (set in init_stacks()) will need to set pc to point
to some code that contains { STOP, STOP }. Thus when the last
stack frame is popped, stop will be executed and MM() will
return to the caller. Need 2 stops because the pc may or may
not (I'm not sure about this) be incremented.
(dup) DUP
Duplicate the top of the val stack.
TV = vpop, vpush(TV), vpush(TV)
(jmp addr) pc = addr JMP
(jmp-FALSE addr) JMPFALSE
Input: RV (BOOLEAN)
Result: if (RV == FALSE) pc = addr
Notes: No type checking. Its up to the compiler.
(jmp-TRUE addr) JMPTRUE
Input: RV (BOOLEAN)
Result: if (RV == TRUE) pc = addr
Notes: No type checking. Its up to the compiler.
(length-of) LEN_OF
Input: RV (anything)
Result: RV (NUMBER)
Notes:
length-of string is strlen(string).
length-of list is number of elements in the list.
length-of anything else is 0.
(nargs) NARGS
Result: RV (NUMBER: number of args passed to this function)
(not) NOT
Input: RV (BOOLEAN)
Result: RV = !RV
Notes: No type checking. Its up to the compiler.
(pop) discard ToS POP
(push-args) PUSHARGS
Input: RV (NUMBER: first arg to push)
Result:
Push arg (n, n+1, ..., all args) from the current stack frame
are pushed onto the next stack frame. This is used to quickly
duplicate the stack so you can pass the arg list to another
function.
(push-name name_offset) PUSHNAME
Input: name_offset (num16: offset of name in string table)
Result:
char *ptr = &GVars[-name_offset];
opush(ptr)
(push-token token) PUSHTOKEN
input: token (num16)
Result: opush(token)
!!!what is a token?????
(push-address address) PUSHADDR
input: address (addr)
Result: opush(addr)
(fcn-addr type [token | address]) FADDR ???!!!???
input:
type (num8: type of function call: OPTOKEN, OPXTOKEN,
OPADDRESS or OPNAME)
case type:
OPTOKEN (num16: token)
OPXTOKEN: (num16: token)
OPADDRESS: (addr: fcn address)
OPNAME: (RV: STRING: fcn name), not type checked
result: RV contains a function address. To call the function,
(push-RV) and (do-OP).
(shove-RV) SHOVERV
Input: RV
Result: RV pushed onto the val stack (vpush(RV)).
(push-RV) PUSHRV ???!!!???
Input: RV
Result:
case RV.type
STRING: If text is stored in RV, allocate space (in the
current stack frame) and copy text. Then [in all cases]
(shove-RV).
OSTRING: Get pointer to text, shove that ???!!!
default: (shove-RV)
case OPTOKEN, OPXTOKEN:
case OPADDRESS:
case OPNAME:
(set-RV-to num8) RVNUM8
Input: number (num8)
Result: RV (NUMBER)
(set-RV-to num16) RVNUM16
Input: number (num16)
Result: RV (NUMBER)
(set-RV-to num32) RVNUM32
Input: number (num32)
Result: RV (NUMBER)
(set-RV-to-string offset) RVSTR
String starts offset bytes in front of GVars.
Input: offset (num16)
Result: RV = (STRING *)&GVars[-offset] == address of string
(set-RV-to Bool) RVBOOL
Input: Bool (num8)
Result: RV (BOOLEAN: Bool)
(set-RV-to-VOID) RV = VOID RVVOID
(typecheck num8) TYPECHECK
!!!???? strings, ostrings
Input: type (num8)
Result:
noop if RV has type type.
pgm abort if RV has wrong type, message printed.
(get-local-var type offset) GETLVAR
Get the nth var out of the current stack frame
Input:
type (num8)
offset (num16). what is offset!!!???
Result:
Type is:
STRING:
object_pointer = local_object_table[offset]
get_object(object_pointer, RV.string)
RV = object
LIST:
other: RV = *(type *)&LVars[offset]
(get-global-var type offset) GETGVAR
Get the nth global var
Input:
type (num8)
offset (num16). what is offset!!!???
Result:
Type is:
STRING:
!!! if string, n = *(int16 *)&GVars[offset], RV = get_dString(n)
other: RV = *(type *)&GVars[offset]
(set-local-var type offset) SETLVAR
Set the nth var in the current stack frame.
Input:
type (num8)
offset (num16). what is offset!!!???
RV: value
Result:
Type is:
STRING:
object_pointer = local_object_table[offset]
set_object(object_pointer, RV.string)
LIST:
other: *(type *)&LVars[offset] = RV
Notes:
No type checking is done. The compiler does it if needed.
Don't have to worry about NUMBER, INT16, etc mismatches.
(set-global-var type offset) SETGVAR
Set the nth global var.
Input:
type (num8)
offset (num16). what is offset!!!???
RV: value
Result:
Type is:
STRING:
!!! if string, n = *(int16 *)&GVars[offset], set_dString(n,RV)
other: GVars[offset] = RV
Notes:
No type checking is done. The compiler does it if needed.
Don't have to worry about NUMBER, INT16, etc mismatches.
(get-var-relative type) GETRVAR
Input:
type (num8)
RV (address: base address, GVars or LVars)
vpop (NUMBER: offset from base of variable)
Result:
Type is:
STRING:
other: RV = *(type *)&MEM[base + offset]
Note: Use global-vars or local-vars to get the base address.
(set-var-relative type) SETRVAR
Input:
type (num8)
RV (type: variable)
vpop1 (address: base address, GVars or LVars)
vpop2 (NUMBER: offset from base of variable)
Result:
Type is:
STRING:
other: *(type *)&MEM[base + offset] = RV
Notes:
No type checking is done. The compiler does it if needed.
Don't have to worry about NUMBER, INT16, etc mismatches.
Use global-vars or local-vars to get the base address.
(global-vars offset) RVGBASE
Input: offset (num16)
Result: RV = (Blob: GVars + offset)
(local-vars offset) RVLBASE
Input: offset (num16)
Result: RV = (Blob: LVars + offset)
(lalloc num16) LALLOC
Allocate n bytes in the current stack frame
Input: bytes to allocate (num16)
Result: RV = (Blob: LVars + offset)
Note: (lalloc 0) is a no-op.
(create-object global/local object-type offset) CREATE_OBJ
Allocate objects.
Input:
global/local (num8: 1 (global), 0 (local))
object-type (num8: STRING, LIST)
offset (num16: where in the object table to save object pointer)
Result:
None. RV might be mashed.
Errors: No memory.
Notes:
When creating local objects, must be created in same order as
offset.
There is no instruction to free an object - that can only be
done by garbage collection (ie don't worry, be happy).
========================================================================
== Mutt2 Machine Extended Instruction Set ==
========================================================================
Why not regular instructions: Need a stack frame
Internal tokens !!!???
Internal tokens are opcodes that need a stack frame:
- so pgm can use vars and stuff
- do vararg stuff
- ???
See push-token, OPTOKEN
NAME ACTION OPCODE
---- ------ ------
(ask) ASK
If args, return the next one else ask user
Input: prompt list on stack (all types)
Result: RV (STRING)
(msg) MSG
If args, concatenate them and print them out.
(concat) CONCAT
object routines
********************************************************************