In computer programming, a P-code machine or pseudo-code machine is a specification of a cpu whose instructions are expected to be executed in software rather than in hardware (ie, interpreted). This term is applied both generically to all such specifications, although many specifications create their own name (eg, Java uses byte-code and BCPL uses O-code, circa 1966) and to particular specifications (the most famous being UCSD Pascal P-code).
The term p-code first appeared on the scene in the early 1970s. Two early implementations of compilers generating p-code were the Pascal-P compiler in 1973, by Nori, Ammann, Jensen, Hageli, and Jacobi, and the Pascal-S compiler in 1975, by Niklaus Wirth.
Programs that have been translated to P-code are executed (the term interpreted is used to describe this process) by a software program that emulates the behavior of the cpu specification. If there is sufficient commercial interest a hardware implementation of the specification may be built (eg, the Pascal MicroEngine).
Some implementations of the BASIC and of the Pascal use p-codes that are translated by a just-in-time compiler to actual machine instructions. Some articles by Niklaus Wirth mention a m-code variant for Pascal successor Modula-2.
The Business Operating System (BOS) of the 1980s was a cross-platform operating system designed to exclusively run p-code based programs.
The UCSD p-System was a portable machine independent operating system based on p-code.
Some simple instructions:
Insn. Stack Stack Description before after adi i1 i2 i1+i2 add two integers adr r1 r2 r1+r2 add two reals dvi i1 i2 i1/i2 integer division inn i1 s1 b1 set membership; b1 = whether i1 is a member of s1 ldci i1 i1 load integer constant mov a1 a2 move not b1 ~b1 boolean negation
Also present is a constant area, and, below that, the heap growing down towards the stack. The NP register points to the top (lowest used address) of the heap. When EP gets greater than NP, the machine's memory is exhausted.
The fifth register, PC, points at the current instruction in the code area.
EP -> local stack SP -> ... locals ... parameters ... return address (previous PC) previous EP dynamic link (previous MP) static link (MP of surrounding procedure) MP -> function return value
The procedure calling sequence works as follows: The call is introduced with mst n where n specifies the difference in nesting levels (remember that Pascal supports nested procedures). This instruction will mark the stack, i.e. reserve the first five cells of the above stack frame, and initialise previous EP, dynamic, and static link. The caller then computes and pushes any parameters for the procedure, and then issues cup n, p to call a user procedure (n being the number of parameters, p the procedure's address). This will save the PC in the return address cell, and set the procedure's address as the new PC.
User procedures begin with the two instructions ent 1, i ent 2, j The first sets SP to MP + i, the second sets EP to SP + j. So i essentially specifies the space reserved for locals (plus the number of parameters plus 5), and j gives the number of entries needed locally for the stack. Memory exhaustion is checked at this point.
Returning to the caller is accomplished via retC with C giving the return type (i, r, c, b, a as above, and p for no return value). The return value has to be stored in the appropriate cell previously. On all types except p, returning will leave this value on the stack.
Instead of calling a user procedure (cup), standard procedure q can be called with csp q These standard procedures are Pascal procedures like readln() ("csp rln"), sin() ("csp sin"), etc. Peculiarly eof() is a p-Code instruction instead.
This is the code for the machine:
procedure interpret;
const stacksize = 500;
var p,b,t: integer; {program-, base-, topstack-registers}
i: instruction; {instruction register}
s: array * of integer; {datastore}
function base(l: integer): integer;
var b1: integer;
begin b1 := b; {find base l levels down}
while l > 0 do
begin b1 := s*; l := l - 1
end;
base := b1
end {base};
begin writeln(' start pl/0'); t := 0; b := 1; p := 0; s:= 0; s* := 0; repeat i := code*; p := p + 1; with i do case f of lit: begin t := t + 1; s* := a end; opr: case a of {operator} 0: begin {return} t := b - 1; p := s+ 3; b := s+ 2; end; 1: s:= -s[t; 2: begin t := t - 1; s:= s[t + s+ 1 end; 3: begin t := t - 1; s:= s[t - s+ 1 end; 4: begin t := t - 1; s:= s[t * s+ 1 end; 5: begin t := t - 1; s:= s[t div s+ 1 end; 6: s:= ord(odd(s[t)); 8: begin t := t - 1; s:= ord(s[t = s+ 1) end; 9: begin t := t - 1; s:= ord(s[t <> s+ 1) end; 10: begin t := t - 1; s:= ord(s[t < s+ 1) end; 11: begin t := t - 1; s:= ord(s[t >= s+ 1) end; 12: begin t := t - 1; s:= ord(s[t > s+ 1) end; 13: begin t := t - 1; s:= ord(s[t <= s+ 1) end; end; lod: begin t := t + 1; s:= s[base(l) + a end; sto: begin s:= s*); t := t - 1 end; cal: begin {generate new block mark} s+ 1 := base(l); s+ 2 := b; s+ 3 := p; b := t + 1; p := a end; int: t := t + a; jmp: p := a; jpc: begin if s* = 0 then p := a; t := t - 1 end end {with, case} until p = 0; write(' end pl/0'); end {interpret};
This machine was used to run Wirth's PL/0, which was a Pascal subset compiler used to teach compiler development.
Virtualization software | Pascal | Compilers | Programming language implementation
This article is licensed under the GNU Free Documentation License.
It uses material from the
"P-Code machine".
Home Page • arts • business • computers • games • health • hospitals • home • kids & teens • news • physicians • recreation• reference • regional • science • shopping • society • sports • world