One stack for bit values, measured in 32 bit words.
One stack for object references (capabilities), each composed of a
type and a value. For most types, the value is a pointer to a data
area. Some types store the value directly in the value field.
One stack for execution data (invisible to the running program) that
contains return address and stack bounds info.
The code format is a parse tree with the following nodes:
denotes a stack delta. The operation pushes p objects onto the
object stack and q words onto the bits stack. Negative p or q
indicate that values are popped from the stacks.
o-- ... r s ==> o-- ... t
is a stack diagram. The r and s values are popped from the object
stack and t is pushed back on. o-- represents the bottom of the
object stack, b-- represents the bottom of the bits stack.
... represents a variable number of objects or words. ==> delimits
the stack views before and after the node is evaluated.
Leaf nodes:
SENDR i j <-1, 0>
o-- ... oi oi-1 ... o0 target ==> o-- ... roi roi-1 ... ro0
b-- ... bj bj-1 ... b0 ==> b-- ... rbj rbj-1 ... rb0
pops the target object off the stack. Adjusts the stack
bounds so that the top i objects are accessable on the object
stack, and the top j words on the bits stack. Sets the
current object pointer to the target object, and transfers
control to the target object. On return, the top stack
elements are available as return values.
SEND <-4, 0>
o-- ... oargs bargs selector target ==> o-- ...
arranges for a message to be delivered to target in the
future. The target object will be invoked with oargs, bargs,
and selector on the object stack. oargs should be of type
ObjectArray, and bargs should be of type BitArray.
DISPATCH <0, 0>, or <-1, 0> if successful.
o-- ... selector ==> o-- ... selector
Transfer control to the verb that matches the given selector
from the verb table. If no verb matches, continue execution
with the next node. If a match is found, the selector is
popped from the object stack before entering the verb.
RETURN
if this routine was invoked with a SENDR, transfers control to
just after that SENDR. The stack bounds and current object
value are restored to their values before the SENDR. If this
routine was invoked with a SEND, the stacks are cleared and
another SEND is selected for execution.
OPOP <-1, 0>
o-- ... r ==> o-- ...
discard one object from the top of the object stack.
BPOP <0, -1>
b-- ... i ==> b-- ...
discard one 32 bit word from the top of the bits stack.
ADJUST i j
changes the object stack depth by i elements and the bits
stack depth by j elements. Positive i pushes undef objects.
Positive j pushes zero words. Negative i or j pops values
from the stacks, discarding them.
OPUSH name <1, 0>
o-- ... ==> o-- ... object(name)
push the named object reference onto the object stack.
BPUSH name <0, 1>
b-- ... ==> b-- ... word(name)
push the named word onto the bits stack.
BPUSH i <0, 1>
b-- ... ==> b-- ... i
push the constant 32 bit word onto the bits stack.
ONTH n <1, 0>
o-- ... rn rn-1 ... r0 ==> o-- ... rn rn-1 ... r0 rn
push a copy of the n'th object from the object stack onto the
top of the object stack.
BNTH n <0, 1>
b-- ... in in-1 ... i0 ==> b-- ... in in-1 ... i0 in
push a copy of the n'th 32 bit word from the bits stack onto the
top of the bits stack.
ODUPN n
o-- ... on-1 on-2 ... o0 ==> o-- ... on-1 on-2 ... o0 on-1 on-2 ... o0
duplicate the top n elements of the object stack.
BDUPN n <0, n>
b-- ... bn-1 bn-2 ... b0 ==> b-- ... bn-1 bn-2 ... b0 bn-1 bn-2 ... b0
duplicate the top n words of the bits stack.
OROT i j <0, 0>
o-- ... oi-1 ... o0 ==> o-- ... o(i+j-1)|i ... o(j)|i
where (n)|m means: n mod m
rotate the top i elements of the object stack by j positions.
If j is zero or any multiple of i, the stack remains
unchanged, but an exception would be signalled if the stack
contains less than i elements.
BROT i j <0, 0>
b-- ... bi-1 ... b0 ==> b-- ... b(i+j-1)|i ... b(j)|i
where (n)|m means: n mod m
rotate the top i elements of the bits stack by j positions.
If j is zero or any multiple of i, the stack remains
unchanged, but an exception would be signalled if the stack
contains less than i elements.
DEPTH, MARK and friends?...
OARRAYLOAD <0, -1>
o-- ... array ==> o-- ... array[i]
b-- ... i ==> b-- ...
if array is of type ObjectArray or WritableObjectArray, fetch
the i'th element of array onto the object stack.
OARRAYSTORE <-2, -1>
o-- ... val array ==> o-- ...
b-- ... i ==> b-- ...
if array is of type WritableObjectArray, store val in
array[i].
BARRAYLOAD <-1, 0>
o-- ... array ==> o-- ...
b-- ... i ==> b-- ... array[i]
if array is of type BitArray or WritableBitArray, fetch the
i'th element of array onto the bits stack.
BARRAYSTORE <-1, -2>
o-- ... array ==> o-- ...
b-- ... val i ==> b-- ...
if array is of type WritableBitArray, store val in array[i].
LENGTH <-1, 1>
o-- ... array ==> o-- ...
b-- ... ==> b-- ... length(array)
pushes the number of elements in array onto the bits stack.
// need some way to deal with different size bit array elements.
THIS <1, 0>
o-- ... ==> o-- ... r
push the current object pointer onto the object stack.
NULL <1, 0>
o-- ... ==> o-- ... null
push the null object onto the object stack.
UNDEF <1, 0>
o-- ... ==> o-- ... undef
pushes an undefined object reference onto the object stack.
STRING string <1, 0>
o-- ... ==> o-- ... string
pushes a reference to the given string onto the object stack.
OLOAD n <1, 0>
o-- ... ==> o-- ... r
fetch the object at offset n from the object portion of the
current object and push it onto the stack.
BLOAD n <0, 1>
b-- ... ==> b-- ... i
fetch the word at offset n from the bits portion of the
current object and push it onto the stack.
OSTORE n <-1, 0>
o-- ... r ==> o-- ...
pop the top of the object stack and store it at offset n in
the object portion of the current object.
BSTORE n <0, -1>
b-- ... i ==> b-- ...
pop the top of the bits stack and store it at offset n in
the bits portion of the current object.
TSET n <0, 1>
b-- ... ==> b-- ... i
i = b[n]; b[n] = 1; atomically load the word at offset n in
the bits portion of the current object and set it to 1.
ADD <0, -1>
b-- ... a b ==> b-- ... a+b
pop the bits stack and add it to the new top of the bits
stack.
SUB <0, -1>
b-- ... a b ==> b-- ... a-b
pop the bits stack and subtract it from the new top of the bits
stack.
AND, OR, XOR <0, -1>
b-- ... a b ==> b-- ... a&b
pop the bits stack and perform the given operation on the new
top of the bits stack.
NOT <0, 0>
b-- ... a ==> b-- ... ~a
replace the top of the bits stack with it's bitwise inversion.
EQ, NE, GT..., shifts...
DIV, MUL, MOD, floating point, bignum, modexp...
make number object from bits, make bits from number object.
8 and 16 bit math? 64 bit math?
COMMENT string <0, 0>
does nothing.
OTAGN n name <0, 0>
associates name with the n'th element from the top of the
object stack.
BTAGN n name <0, 0>
associates name with the n'th element from the top of the
bits stack.
THROW
o-- ... exception ==>
initiate exception processing. An exception object is popped
from the object stack. Stack backtrace information is placed
in the exception. Control is transferred to the nearest
enclosing CATCH or ALWAYS.
RETHROW
o-- ... exception ==>
continue exception processing. An exception object is popped
from the object stack. The backtrace information in the
exception remains unaltered. Control is transferred to the
nearest enclosing CATCH or ALWAYS.
BREAK n
transfers to just after the n'th enclosing LOOP. The stacks
are adjusted to the depth on entry to the loop.
CONTINUE n
transfers to the first node of the n'th enclosing LOOP. The
stacks are adjusted to the depth on entry to the loop.
Non-leaf nodes:
BLOCK n nodes...
execute the n nodes in sequence.
IF node node , where node's depths are
pops the bits stack, if it is non-zero execute the first node,
otherwise, execute the second node. Both nodes must have the
same stack deltas.
CONDITIONAL node node node
the first node is evaluated by the loader with empty stacks.
If there is a non-zero word on the top of the bits stack after
this evaluation, the second node is included in the code.
Otherwise, the third node is included. The second and third
nodes must have the same stack deltas: .
LOOP node node node <0, 0>
executes the first node, which must have stack delta <0, 0>.
Executes the second node which must have stack delta <0, 1>.
Pops the bits stack, if non-zero execute the third node,
which must have stack delta <0, 0>. Repeat until a zero is
obtained from the second node. Any of the nodes can be null.
A null second node loops forever.
CATCH node node
execute the first node. If it exits normally, the second node
is not executed. If an exception is thrown from the first
node's processing, the stack depths are adjusted to the
nominal depth of the first node, then the exception is pushed
onto the object stack, then the second node is executed. The
second node must have delta <-1, 0>. If the second node
completes execution without (re)throwing, the exception is
considered to be handled. The stack depth of the catch node
is the same as it's first sub node.
ALWAYS node node
execute the first node. When it exits (for whatever reason),
execute the second node. The stack is adjusted to the nominal
depth of the first node before executing the second node.
Excess elements are popped from the stack, shortages are
filled with zeros on the bits stack and undef on the object
stack. The resulting stack depth is the sum of the two node's
depths. If the second node is entered with an unhandled
exception, that exception is implicitly RETHROWn when the
second node exits.
VERB name o b node
associate the name and the node in the verb table. The object
stack must be at least o elements deep on entry to node, and
the bits stack must be at least b elements deep. An implicit
RETURN is assumed after the end of the node.
Object file format:
Each node consists of a 32 bit integer indicating the node type, an
integer indicating the source code origin of the node, an integer for
each non-node parameter, followed by any sub-nodes. Parameters can be
either 32 bit integers, which are encoded directly in the node, or
they can be strings, in which case a string reference is encoded in
the node. Names are strings which are expected to have a value
associated with them by the loader. If the loader has no knowledge of
the given name, it refers to a zero word value, or an undef object
value.
Things needed:
magic number
version information
options
interpretation of code origin word
line number
character number of start of line
character number of start of symbol
symbol number
name of type
package inclusion info
data area layout
interfaces implemented
types inherited from
code
string table(s?)
cryptohash of source code
cryptohash of object code (including all components up to this one)
compilation date
compiler version info
source code