Skip to content

Instantly share code, notes, and snippets.

@r-lyeh-archived
Last active February 23, 2022 00:01
Show Gist options
  • Save r-lyeh-archived/0af42b2788bb75219061 to your computer and use it in GitHub Desktop.
Save r-lyeh-archived/0af42b2788bb75219061 to your computer and use it in GitHub Desktop.
pcode interpreter
/*
P-code for PL/0 machine
[ref] https://en.wikipedia.org/wiki/P-code_machine
[ref] http://blackmesatech.com/2011/12/pl0/pl0.xhtml
The PL/0 virtual machine was originally specified by Nicklaus Wirth in his book
Algorithms + Data Structures = Programs; it's used as the target machine for a
PL/0 compiler.
Just as PL/0 is simpler than full Pascal, so also the virtual machine used by
the PL/0 compiler is simpler than the p-code machines used by various Pascal
compilers (e.g. Pascal-P from ETH Zürich and UCSD Pascal's P-System). Students
learning about p-code and virtual machines may find the PL/0 p-code easier
to grasp than the p-code systems used by full Pascal compilers.
# General
The machine has four registers and two storage areas. The two storage areas are:
- stack
- a stack used as a datastore for mutable data. Variables, information on function invocations, and temporary data are all placed on the stack. All values are integers.
- code area
- an immutable array of instructions; all instructions have the same format
The registers are:
P: program counter: points to an instruction in the program area
T: stack-top register: points to the current top of the stack
B: base address: points to the base address in the stack for the current invocation of a given procedure
I: instruction register: contains the currently executing instruction
Integer is the only datatype.
There is no input or output; a useful convention is for programs to leave their results in a pre-arranged location on the stack.
*/
enum fct {
lit,opr,lod,sto,cal,Int,jmp,jpc
} ;
struct opcode {
int f:3; // {fct}
int l:2; // [0..3(levmax)]
int a:11; // [0..2047(amax)]
};
enum { stacksize = 500 };
int p, b, t; //{program-, base-, topstack-registers}
int i; //{instruction register}
int s[stacksize]; //[1..stacksize];
int base(int l) {
int b1 = b; // {find base l levels down}
while( l > 0 ) {
b1 = s[b1];
l--;
}
return b1;
}
#include <stdio.h>
int main() {
struct opcode code[] = {
//f l a
{ jmp, 1, 1 },
{ Int, 1, 5 },
{ lit, 1, 0 },
{ sto, 1, 3 },
{ lit, 1, 1 },
{ sto, 1, 4 },
{ lod, 1, 3 },
{ lit, 1, 16 },
{ opr, 1, 9 },
{ jpc, 1, 19 },
{ lod, 1, 3 },
{ lit, 1, 1 },
{ opr, 1, 2 },
{ sto, 1, 3 },
{ lod, 1, 4 },
{ lod, 1, 3 },
{ opr, 1, 4 },
{ sto, 1, 4 },
{ jmp, 1, 6 },
{ opr, 1, 0 },
};
puts(" start pl/0");
t = 0; b = 1; p = 0;
s[1] = s[2] = s[3] = 0;
do {
printf("[%d]\n", p );
struct opcode i = code[p]; p = p + 1;
switch(i.f) { default:
break; case lit: { t++; s[t] = i.a; }
break; case opr:
switch(i.a) { default:
break; case 0:
{ // {return}
t = b - 1; p = s[t + 3]; b = s[t + 2];
}
break; case 1: s[t] = -s[t];
break; case 2: t--; s[t] = s[t] + s[t + 1];
break; case 3: t--; s[t] = s[t] - s[t + 1];
break; case 4: t--; s[t] = s[t] * s[t + 1];
break; case 5: t--; s[t] = s[t] / s[t + 1];
break; case 6: s[t] = (s[t] & 1); //ord(odd(s[t]));
break; case 8: t--; s[t] = (s[t] == s[t + 1]);
break; case 9: t--; s[t] = (s[t] != s[t + 1]);
break; case 10: t--; s[t] = (s[t] < s[t + 1]);
break; case 11: t--; s[t] = (s[t] >= s[t + 1]);
break; case 12: t--; s[t] = (s[t] > s[t + 1]);
break; case 13: t--; s[t] = (s[t] <= s[t + 1]);
}
break; case lod: t++; s[t] = s[base(i.l) + i.a];
break; case sto: s[base(i.l)+i.a] = s[t]; printf("%d\n",s[t]); t--;
break; case cal:
{ //{generate new block mark}
s[t + 1] = base(i.l); s[t + 2] = b; s[t + 3] = p;
b = t + 1; p = i.a;
}
break; case Int: t = t + i.a;
break; case jmp: p = i.a;
break; case jpc: if( s[t] == 0) { p = i.a; t = t - 1; }
}
} while( p != 1 ); //< (sizeof(code)/sizeof(struct opcode)) );
puts(" end pl/0");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment