(* Copyright (C) 1992, Digital Equipment Corporation *) (* All rights reserved. *) (* See the file COPYRIGHT for a full description. *) (* *) (* Last modified on Tue Jan 28 10:40:35 PST 1997 by heydon *) INTERFACE JunoByteCode; (* This interface contains the constants which represent each of the bytecodes in the bytecode interpreter. After each bytecode constant is a representation of the number and size of the arguments expected in the bytecode stream. These arguments have the following meanings: | m, n 8-bit unsigned offsets [0..255] | e 8-bit unsigned error code | v 32-bit unsigned index into the global variable_tbl | p 32-bit unsigned index into the global code_tbl | P 32-bit unsigned index into the global ext_code_tbl | s signed 16-bit offset | u unsigned 16-bit offset | r JunoValue.Real number "PUSHL s": Push the local at offset "s" from "fp" onto the stack. A negative index represents a procedure argument; a positive index represents a procedure local variable (or temporary). See the description of the procedure call protocol below for a more detailed description of local variable indices. "POPL s": Pop the top stack value into the local at offset "s" from "fp". "PUSHG v", "POPG v": Push or pop global variables at location "v" in the global "variable_tbl". "INCSP n", "DECSP n": Increment, decrement the stack pointer by "n". "PUSHM3NIL n": Push Modula-3 "NIL" onto the stack "n" times. Modula-3 NIL on the stack corresponds to an uninitialized variable or an unhinted solve variable. "PUSHNIL": Push Juno "NIL" onto the stack. "PUSHNUM r": Push the real Juno value "r" onto the stack. "C_OFF", "C_ON": Set the condition variable to FALSE or TRUE, respectively. "JUMP s": Jump to the location specified by the relative offset "s". The offset is "s" bytes relative to the address of the instruction following the "JUMP". "TJUMP s", "FJUMP s": Jump to the location specified by the relative offset "s" iff the condition variable is TRUE or FALSE, respectively. The offset is "s" bytes relative to the address of the instruction following the "TJUMP" or "FJUMP". "UJUMP s": To be used only on return from a call to a user-defined function or a functional external procedure compiled as an expression. "UJUMP" is like "FJUMP", but it also has the side-effect in the case that the condition bit is FALSE of decrementing the stack pointer (to skip past the single out parameter to the function) and setting the internal "undefined term" address. "CALL p": Call the bytestream procedure at index "p" in the global "code_tbl". It is the caller's responsibility to make space for the OUT parameters on the stack, to then push the INOUT and IN parameters onto the stack (in left-to-right order), and to remove the IN parameters from the stack on return from the procedure. See "Procedure Call Protocol" below. "CALLEXT P": Call the external (Modula-3) procedure at index "P" in the global "ext_code_tbl". The caller's responsibilities are the same as for the "CALL" instruction above. In the event that the procedure failed on its arguments, single a "Failed External Procedure" runtime error. "RET": Return from a bytestream procedure. "ERROR e", "FERROR e": "ERROR" causes a run-time error. The global error code is set to "e", and the machine traps with a "JunoRT.TrapCode.Error". See the "JunoRT" interface for a description of the other fields of the execution result in the event of an error. See the "JunoRTError" interface for the possible error codes. "FERROR" causes a run-time error iff the condition variable is FALSE. "ADD s", "SUBTRACT s", "MULTIPLY s", "DIVIDE s", "DIV_ s", "MOD_ s", "MAX_ s", "MIN_ s", "ATAN s": Perform the specified arithmetic operation on the top two stack entries, where the top entry is the right operand. The result replaces them on the stack unless the result is undefined, in which case the arguments are removed and a branch is made to the specified offset "s". "NEGATE s", "ABS_ s", "FLOOR_ s", "CEILING_ s", "ROUND_ s", "SIN s", "COS s", "LN s", "EXP s": Perform the specified arithmetic operation on the top stack entry, replacing it with the result. If the operation is undefined, the argument is removed from the stack, and a branch is made to the specified offset "s". "REL s": Pop the top two items from the stack and perform a REL on them. If "a" is the top stack item and "(b,c)" is the next item, "a REL (b,c)" is computed and pushed onto the stack. If the types of the arguments are wrong, a branch is performed to offset "s". "CAR s", "CDR s": Replace the top stack entry with its CAR or CDR, respectively. If the operation is undefined, the argument is removed from the stack, and a branch is made to the specified offset "s". "CAR_CDR s": Replace the top stack entry with its CDR and then its CAR (so the new top entry of the stack will be the CAR). If the operation is undefined, the argument is removed from the stack, and a branch is made to the specified offset "s". "CONS": Replace the top two stack entries by a new pair whose CDR was the top stack entry and whose CAR was the second top-most stack entry. No error can occur. "LIST u": Replace the top "u" elements from the stack with an "u"-element list formed from them; the top-most stack element becomes the last element of the resulting list. "CONCAT s": Replace the top two stack entries by their concatenation; the top-most stack element is the right argument. "IS_REAL", "IS_INT", "IS_TEXT", "IS_PAIR": Test the type of the top stack entry without popping it. Set the machine flag "cond" iff that element has the proper type. "EQUAL", "LESS", "AT_MOST": Remove and compare the top two stack entries, where the top entry is the right operand, and set the machine flag "cond" accordingly. If the values have incomparable types, then "cond" is reset. "CONG", "PARA": Pop the top two items from the stack and perform a CONG or PARA, respectively, on them. If the two elements are the segments "(a, b)" and "(c, d)", where "a", "b", "c", and "d" are points, then set the machine flag "cond" iff the two segments are congruent or parallel, respectively. "HOR", "VER": Pop the top two items from the stack and perform a HOR or VER, respectively, on them. If the two elements are the points "(a, b)" and "(c, d)", where "a", "b", "c", and "d" are reals, then set the machine flag "cond" iff the two segments are horizontal or vertical, respectively. "NEWCL p": Pushes a new closure value for the procedure in slot "p" taking 0 arguments. "NEWEXTCL P": Pushes a new closure value for the procedure in slot "P" taking 0 arguments. "CLOSE u, s": Requires that the top "u+1" elements on the stack (from bottom to top) are a closure value "C" and "u" values "v_1, ..., v_u" (pushed in left-to-right order). These "u+1" values are replaced by a new closure formed by extending "C" by the values "v_1, ..., v_u". If "C" is not a closure, then may branch to the code at relative offset "s". "APPLY uOuts, uInOuts, uIns, s": Pop the stack, which is required to contain a closure value. If this value is not a closure, or if the signature of the closure's procedure is incompatible with this call, the instruction branches to the relative offset "s". This bytecode invokes the closure like a procedure call instruction. Hence, before invoking apply, space should have been made for the OUT parameters, and the INOUT and IN parameters should have been pushed on the stack. If the closure on the top of the stack names the procedure "P" and the closure has "k" values, then this bytecode checks that "P" has "uOuts" OUT parameters, "uInOuts" INOUT parameters, and "k + uIns" IN parameters. "APPLY" stores the number of values in the closure in the "clIns" field of the current internal state before it transfers control. If the procedure in the closure is external, then this instruction executes the procedure; if the procedure fails on its argument, a "Failed External Procedure" run-time error is signaled. If the procedure in the closure is internal, this instruction prepares the stack frame for it an branches to its first instruction. "CLDECSP": Decrement the stack pointer by the "clIns" stored in the current internal state. Should follow every "APPLY" instruction. "SOLVE u1, u2, u3, constraints": This bytecode models a procedure call with "u1" INOUT parameters and "u2" IN parameters. Thus, a total of "u1 + u2" parameters are assumed to be on the stack when this bytecode executes. When the bytecode completes, it automatically removes the IN parameters from the stack, but leaves the INOUT parameters on the stack. The machine flag "cond" is set to TRUE iff the solve was successful. "u3" is the total number of constraints to solve for; it must be positive. Each constraint in the "constraints" bytes takes the following form: | n, uX, [uY, [ uZ ]] where "n" represents the type of constraint; and "uX", "uY", and "uZ" are unsigned offsets in the range "[0..u1+u2-1]" in the INOUT and IN parameters on the stack. Offset 0 corresponds to the first INOUT parameter, offset "u1" corresponds to the first IN parameter, and offset "u1+u2-1" corresponds to the last IN parameter at the top of the stack. "uZ" is only present if "n <= 3"; "uY" is present if "n <= 8". The value for "n" must be in the range "ConRange". The equations for the values of "n" are: | (1) x = CONS(y, z) | (2) x = y + z | (3) x = y * z | (4) x = ATAN(y, z) | (5) x = y | (6) x = SIN(y) | (7) x = COS(y) | (8) x = EXP(y) | (9) IS-REAL(x) | (10) IS-TEXT(x) *) (* Procedure Call Protocol It is the caller's responsibility to make space for the OUT parameters before pushing the INOUT and IN parameters on the stack. On return from the procedure call, the stack pointer will be the same as at the time of the call. Hence, it is the caller's responsibility to decrement the stack pointer to skip the IN parameters, which will reside on the top of the stack. The caller can than pop the INOUT and OUT results into local variables. Variable Indices Inside the call, OUT, INOUT, and IN parameters are "local" variables at negative offsets from the frame pointer. Hence, they have negative indices in the frame. The parameters are organized on the stack from bottom to top in the order OUT's, INOUT's, and IN's. If there are a total of "x" IN parameters, "y" INOUT parameters, and "z" OUT parameters, then parameter indices are given by the following table: | IN [-x..-1] | INOUT [-x-y..-x-1] | OUT [-x-y-z..-x-y-1] The order of the parameters on the stack is governed by the following rule: | The parameters have decreasing indices starting with -1 when read from | *right* to *left* in the order they appear in a PROC, PRED, or FUNC | declaration. For example, for the signature: | PROC x, y := (s, t, u)P(a, b, c, d) the variables have the following indices: | x = -9, y = -8, s = -7, t = -6, u = -5, a = -4, b = -3, c = -2, d = -1 The temporary (or projected) variables are also "local" variables, but their indices are strictly positive, starting at 1. Thus, the first temporary variable has index 1. It is the callee's responsibility to make space on the stack for the temporary frame variables on entry to the procedure. This space is "automatically" deallocated on return from the procedure when the stack frame for the procedure is destroyed. Condition Bit There are four types of targets of a procedure call, and on return from each, the machine's internal condition bit is interpretted differently. The four types are: predicates whose bodies are compiled as procedures, functions whose bodies are compiled as procedures, user-defined procedures, and external procedures. Here is the meaning of the condition bit on return from the call in each case: 1. Predicates whose bodies are compiled as procedures The condition bit is true or false as the body of the compiled predicate halted or produced a guard failure. Hence, the bit indicates if the predicate is true or false on the supplied arguments. 2. Functions whose bodies are compiled as procedures The condition bit is true or false as the body of the compiled function halted or produced a guard failure. Hence, the bit indicates if the function is defined or undefined on the supplied arguments. 3. User-defined procedures Since user-defined procedures are total, the condition bit must always be true on return from a call to a user-defined procedure. 4. External procedures The condition bit is true or false as the external procedure succeeded or failed on its arguments. The interpretation of success and failure depends on whether the external procedure was used functionally or not. If used functionally, failure results in an "undefined term" runtime error. If not used functionally, failure results in an "external procedure failed" runtime error. *) CONST (* Stack operations *) PUSHL = 1; (* s *) PUSHG = 2; (* v *) POPL = 3; (* s *) POPG = 4; (* v *) INCSP = 5; (* n *) DECSP = 6; (* n *) PUSHM3NIL= 7; (* n *) PUSHNIL = 8; PUSHNUM = 9; (* r *) (* Condition flag *) C_OFF = 10; C_ON = 11; (* Jumps *) JUMP = 15; (* s *) TJUMP = 16; (* s *) FJUMP = 17; (* s *) UJUMP = 18; (* s *) (* Procedure call *) CALL = 20; (* p *) RET = 21; ERROR = 22; (* e *) FERROR = 23; (* e *) CALLEXT = 24; (* P *) (* Arithmetic *) ADD = 25; (* s *) SUBTRACT = 26; (* s *) MULTIPLY = 27; (* s *) DIVIDE = 28; (* s *) DIV_ = 29; (* s *) MOD_ = 30; (* s *) NEGATE = 31; (* s *) ABS_ = 32; (* s *) FLOOR_ = 33; (* s *) CEILING_ = 34; (* s *) ROUND_ = 35; (* s *) MAX_ = 36; (* s *) MIN_ = 37; (* s *) ATAN = 38; (* s *) SIN = 39; (* s *) COS = 40; (* s *) LN = 41; (* s *) EXP = 42; (* s *) REL = 43; (* s *) (* Cons cells and lists *) CAR = 45; (* s *) CDR = 46; (* s *) CAR_CDR = 47; (* s *) CONS = 48; LIST = 49; (* u *) (* Text *) CONCAT = 50; (* s *) (* Type predicates *) IS_REAL = 55; IS_INT = 56; IS_TEXT = 57; IS_PAIR = 58; (* Comparison predicates *) EQUAL = 60; LESS = 61; AT_MOST = 62; (* Built-in geometry predicates *) CONG = 65; PARA = 66; HOR = 67; VER = 68; (* Closures *) NEWCL = 70; NEWEXTCL = 71; CLOSE = 72; APPLY = 73; CLDECSP = 74; (* Solver *) SOLVE = 75; (* u1, u2, u3, constraints ... *) (* Constraints for SOLVE *) CONS_C = 1; SUM_C = 2; PROD_C = 3; ATAN_C = 4; EQUAL_C = 5; SIN_C = 6; COS_C = 7; EXP_C = 8; REAL_C = 9; TEXT_C = 10; TYPE ConRange = [CONS_C..TEXT_C]; VAR names: ARRAY [PUSHL..SOLVE] OF TEXT; (* "names[i]" is the text version of the bytecode instruction with value "i"; if "i" does not correspond to a bytecode, "names[i]" is "INVALID". The names in the range of this array are the same as the constant names above except that trailing underscore characters in the constant names are not included in the instruction names (e.g. the "FLOOR_" constant name corresponds to the instruction named "FLOOR"), and all other underscores in the constant names are hyphens in the instruction names (e.g., the "IS_INT" constant name corresponds to the instruction named "IS-INT". *) END JunoByteCode.