Storage | Registers | Instruction |
C - code S - stack |
IR - instruction register
T - stack top pointer PC - program counter AR - activation record pointer |
OpCode, arg_1, arg_2 |
Instruction | Operands | Comments |
LIT | 0,N | Load literal value N onto stack: T := T+ 1; S(T) := N |
OPR | 0,0 | Return (from subroutine call) |
0,1 | Negate: S(T) := -1*S(T) | |
0,2 | Add: S(T-1) := S(T-1) + S(T); T := T-1 | |
0,3 | Subtract: S(T-1) := S(T-1) - S(T); T := T-1 | |
0,4 | Multiply: S(T-1) := S(T-1) * S(T); T := T-1 | |
0,5 | Divide: S(T-1) := S(T-1) / S(T); T := T-1 | |
0,6 | undefined | |
0,7 | Mod: S(T-1) := S(T-1) mod S(T); T := T-1 | |
0,8 | Equal: S(T-1) := if S(T-1) = S(T) then 1 else 0; T:= T-1 | |
0,9 | Not equal: S(T-1) := if S(T-1) <> S(T) then 1 else 0; T:= T-1 | |
0,10 | Less than: S(T-1) := if S(T-1) < S(T) then 1 else 0; T:= T-1 | |
0,11 | Greater than or equal: S(T-1) := if S(T-1) >= S(T) then 1 else 0; T:= T-1 | |
0,12 | Greater than: S(T-1) := if S(T-1) > S(T) then 1 else 0; T:= T-1 | |
0,13 | Less than or equal: S(T-1) := if S(T-1) <= S(T) then 1 else 0; T:= T-1 | |
0,14 | Or: S(T-1) := if(S(T-1) + S(T) > 1 then 1 else 0; T := T-1 | |
0,15 | And: S(T-1) := S(T-1)*S(T); T := T-1 | |
0,16 | Not: S(T) := if S(T) = 0 then 1 else 0 | |
0,19 | Increment: S(T) := S(T)+1 | |
0,20 | Decrement: S(T) := S(T)-1 | |
0,21 | Copy: S(T+1):= S(T); T := T+1 | |
Data Access Operations | ||
LOD | L,N | Load value of variable at level offset L, base offset N in stack onto top of stack T:= T + 1; S(T):= S(f(L,AR)+N)+3 |
LOD | 255,0 | Load byte from memory address which is on top of stack onto top of stack: S(T) := S(S(T)) |
LODX | L,D | Indexed load: S(T) := S(f(L,AR)+D+S(T) |
STO | L,N | Store value on top of stack into variable location at level offset L, base offset N in stack: S(f(L,AR)+N+3):= S(T); T:= T - 1 |
STO | 255,0 | Store: S(S(T-1)) := S(T); T:=T-2 |
STOX | L,D | Indexed store: POP index, POP A, store A at (base of level offset L)+D+index |
Control Operations | ||
CAL | L, N | call PROC or FUNC at S-code location N declared at level
offset L:
S(T+1):= f(ld,AR); {Static Link} S(T+2):= AR; {Dynamic Link} S(T+3):= P; {Return Address} AR:= T+1; {Activation Record} PC:= N; {Program Counter} T:= T+3 {Stack Top} |
JMP | 0, N | JUMP: P := N; |
JPC | C, N | JUMP: if S(T) = C then P:= N; T:= T-1 |
CSP | 0, 0 | CHARACTER Input: T := T+1, S(T) := input; |
0, 1 | CHARACTER Output: Output := S(T); T := T-1; | |
0, 2 | INTEGER Input: T := T+1; S(T) := input | |
0, 3 | INTEGER Output: Output := S(T); T := T-1 | |
0, 8 | STRING Output: L := S(T); T := T-1; FOR I := 1 to L DO BEGIN Output := S(T); T := T-1 END |
Where the static level difference between the current procedure and
the called procedure is ld.
os is the offset within the activation record, ld is the static level
difference between the
current activation record and the activation record in which the value
is to be stored and
f(ld,a) = if i=0 then a else f(i-1,S(a))
execution-loop: I := C(P); P := P+1; interpret(I); if P > 0 -> execution-loop
/* OPERATIONS: Internal Representation */ enum code_ops { HALT, STORE, JMP_FALSE, GOTO, DATA, LD_INT, LD_VAR, READ_INT, WRITE_INT, LT, EQ, GT, ADD, SUB, MULT, DIV, PWR }; /* OPERATIONS: External Representation */ char *op_name[] = {"halt", "store", "jmp_false", "goto", "data", "ld_int", "ld_var", "in_int", "out_int", "lt", "eq", "gt", "add", "sub", "mult", "div", "pwr" }; struct instruction { enum code_ops op; int arg; };Memory is separtated into two segments, a code segment and a run-time data and expression stack.
void fetch_execute_cycle() { do { /* Fetch */ ir = code[pc++]; /* Decode & Execute */ switch (ir.op) { case HALT : printf( "halt\n" ); break; case READ_INT : printf( "Input: " ); scanf( "%ld", &stack[ar+ir.arg] ); break; case WRITE_INT : printf( "Output: %d\n", stack[top--] ); break; case STORE : stack[ir.arg] = stack[top--]; break; case JMP_FALSE : if ( stack[top--] == 0 ) pc = ir.arg; break; case GOTO : pc = ir.arg; break; case DATA : top = top + ir.arg; break; case LD_INT : stack[++top] = ir.arg; break; case LD_VAR : stack[++top] = stack[ar+ir.arg]; break; case LT : if ( stack[top-1] < stack[top] ) stack[--top] = 1; else stack[--top] = 0; break; case EQ : if ( stack[top-1] == stack[top] ) stack[--top] = 1; else stack[--top] = 0; break; case GT : if ( stack[top-1] > stack[top] ) stack[--top] = 1; else stack[--top] = 0; top--; break; case ADD : stack[top-1] = stack[top-1] + stack[top]; top--; break; case SUB : stack[top-1] = stack[top-1] - stack[top]; top--; break; case MULT : stack[top-1] = stack[top-1] * stack[top]; top--; break; case DIV : stack[top-1] = stack[top-1] / stack[top]; top--; break; case PWR : stack[top-1] = stack[top-1] * stack[top]; top--; break; default : printf( "%sInternal Error: Memory Dump\n" ); break; } } while (ir.op != HALT); }
*This is an adaptation of: Niklaus Wirth, Algorithms + Data Structures = Programs Prentice-Hall, Englewood Cliffs, N.J., 1976.
See also Wilhelm and Maurer, Compiler Design Addison-Wesley 1995 pp. 7-60.