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