next up previous
Next: Bias-Shifting Instructions to Modify Up: Primitive Instructions Previous: Basic Data Stack-Related Instructions


Control-Related Instructions

Each call of callable code $f$ increments $cp$ and results in a new topmost callstack entry. Functions to make and execute functions include:

  1. Instruction def(m,n) defines a new integer function name (1 if it is the first, otherwise the most recent name plus 1) and increments fnp. In the new fns entry we associate with the name: $m$ and $n$, the function's expected numbers of input arguments and return values, and the function's start address $cs[cp].ip + 1$ (right after the address of the currently interpreted token def).

  2. Instruction dof(f) calls $f$: it views $f$ as a function name, looks up $f$'s address and input number $m$ and output number $n$, increments $cp$, lets $cs[cp].base$ point right below the $m$ topmost elements (arguments) in ds (if $m < 0$ then $cs[cp].base=cs[cp-1].base$, that is, all ds contents corresponding to the previous instance are viewed as arguments), sets $cs[cp].out := n$, and sets $cs[cp].ip$ equal to $f$'s address, thus calling $f$.

  3. ret() causes the current function call to return; the sequence of the $n=cs[cp].out$ topmost values on ds is copied down such that it starts in ds right above $ds[cs[cp].base]$, thus replacing the former input arguments; dp is adjusted accordingly, and $cp$ decremented, thus transferring control to the ip of the previous callstack entry (no copying or dp change takes place if $n<0$ -- then we effectively return the entire stack contents above $ds[cs[cp].base]$). Instruction rt0(x) calls ret() if $x \leq 0$ (conditional return).

  4. oldq(n) calls the $n$-th frozen program (either user-defined or frozen by OOPS) stored in $q$ below $a_{frozen}$, assuming (somewhat arbitrarily) zero inputs and outputs.

  5. Instruction jmp1(val, n) sets $cs[cp].ip$ equal to $n$ provided that $val$ exceeds zero (conditional jump, useful for iterative loops); pip(x) sets $cs[cp].ip :=x$ (also useful for defining iterative loops by manipulating the instruction pointer); bsjmp(n) sets current instruction pointer $cs[cp].ip$ equal to the address of $ds[cs[cp].base + n]$, thus interpreting stack contents above $ds[cs[cp].base + n]$ as code to be executed.

  6. bsf(n) uses $cs$ in the usual way to call the code starting at address $ds[cs[cp].base + n]$ (as usual, once the code is executed, we will return to the address of the next instruction right after bsf); exec(n) interprets $n$ as the number of an instruction and executes it.

  7. qot() flips a binary flag quoteflag stored at address $a_{quoteflag}$ on tape as $z(a_{quoteflag})$. The semantics are: code in between two qot's is quoted, not executed. More precisely, instructions appearing between the $m$-th ($m$ odd) and the $m+1$st qot are not executed; instead their instruction numbers are sequentially pushed onto data stack ds. Instruction nop() does nothing and may be used to structure programs.

In the context of instructions such as getq and bsf, let us quote Koopman [23] (reprinted with friendly permission by Philip J. Koopman Jr., 2002):
Another interesting proposal for stack machine program execution was put forth by Tsukamoto (1977). He examined the conflicting virtues and pitfalls of self-modifying code. While self-modifying code can be very efficient, it is almost universally shunned by software professionals as being too risky. Self-modifying code corrupts the contents of a program, so that the programmer cannot count on an instruction generated by the compiler or assembler being correct during the full course of a program run. Tsukamoto's idea allows the use of self-modifying code without the pitfalls. He simply suggests using the run-time stack to store modified program segments for execution. Code can be generated by the application program and executed at run-time, yet does not corrupt the program memory. When the code has been executed, it can be thrown away by simply popping the stack. Neither of these techniques is in common use today, but either one or both of them may eventually find an important application.
Some of the instructions introduced above are almost exactly doing what has been suggested by [71]. Remarkably, they turn out to be quite useful in the experiments (Section 6).


next up previous
Next: Bias-Shifting Instructions to Modify Up: Primitive Instructions Previous: Basic Data Stack-Related Instructions
Juergen Schmidhuber 2004-04-15

Back to OOPS main page