P-code机

在计算机科学中，P-code机（英语：P-code machine）是一种被设计来运行P-code的虚拟机。P-code是一种被设计来运行在虚拟CPU上的汇编语言，即是我们现代所称Bytecode的前身。P-code机这个词可用于形容所有这类机器（例如Java虚拟机和MATLAB预编译的代码），或者特指最有名的P-code机，来自于Pascal语言，特别是UCSD Pascal实现。
虽然这个概念在1966左右年就已首次实现（于BCPL的O-code与Euler语言的P - a code），但P-code这个词直到70年代初才首次出现。 1973年Nori, Ammann, Jensen, Hageli和Jacobi编写的Pascal-P编译器 和1975年尼克劳斯·维尔特写的Pascal-S编译器是早期的两个生成P-code的编译器。
P-code可以是一种与特定硬件平台无关的中间码，一种虚拟机器码。程序源代码会先被转换成P-code；转换成P-code的程序，之后会由一个软件来进行直译。这个软件可以模拟出一个假想的CPU来读取p-code，之后将p-code转换成实体机器码来运行。但如果有足够的商业利益，可能可以实现做出该规格CPU的硬件实现（例如Pascal MicroEngine和Java处理器）。

架构

如很多其他p-code机一样，UCSD p-Machine是一个堆栈结构机器，这意味着大多数指令从堆栈中获取它们的操作数，并将结果放回堆栈上面。因此，“add”指令将堆栈最顶部的两个元素替换成它们的和。有几条指令就取一个参数。像Pascal一样，p-code是强类型语言，原生支持boolean (b), character (c), integer (i), real (r), set (s)和pointer (a)类型。
一些简单的指令：

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

调用约定

栈帧是这样的：

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
程序调用序列的工作方式如下：下面指令引入调用

 mst n
其中 n 指定嵌套级别的差异（记得Pascal支持过程嵌套）。这个指令会标记这个堆栈，即在上述栈帧中保留起始地5个格子，并初始化前面的EP、动态链接和静态链接。

范例机器

尼克劳斯·维尔特在他1976年出的书《算法+数据结构=程序》中详述了一个简单的P-code机。这个机器有3个寄存器——一个程序计数器 p，一个基寄存器 b，和一个栈顶寄存器 t。一共有8个指令，其中一个（opr）有多种形式。
这是机器的Pascal代码：
const
levmax=3;
amax=2047; 
type 
fct=(lit,opr,lod,sto,cal,int,jmp,jpc);
instruction=packed record 
f:fct;
l:0..levmax;
a:0..amax;
end;procedure interpret;

  const stacksize = 500;

  var
    p, b, t: integer; {program-, base-, topstack-registers}
    i: instruction; {instruction register}
    s: array [1..stacksize] 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[b1];
      l := l - 1
    end;
    base := b1
  end {base};begin
  writeln(' start pl/0');
  t := 0; b := 1; p := 0;
  s[1] := 0; s[2] := 0; s[3] := 0;
  repeat
    i := code[p]; p := p + 1;
    with i do
      case f of
        lit: begin t := t + 1; s[t] := a end;
        opr: 
          case a of {operator}
            0: 
              begin {return}
                t := b - 1; p := s[t + 3]; b := s[t + 2];
              end;
            1: s[t] := -s[t];
            2: begin t := t - 1; s[t] := s[t] + s[t + 1] end;
            3: begin t := t - 1; s[t] := s[t] - s[t + 1] end;
            4: begin t := t - 1; s[t] := s[t] * s[t + 1] end;
            5: begin t := t - 1; s[t] := s[t] div s[t + 1] end;
            6: s[t] := ord(odd(s[t]));
            8: begin t := t - 1; s[t] := ord(s[t] = s[t + 1]) end;
            9: begin t := t - 1; s[t] := ord(s[t] <> s[t + 1]) end;
            10: begin t := t - 1; s[t] := ord(s[t] < s[t + 1]) end;
            11: begin t := t - 1; s[t] := ord(s[t] >= s[t + 1]) end;
            12: begin t := t - 1; s[t] := ord(s[t] > s[t + 1]) end;
            13: begin t := t - 1; s[t] := ord(s[t] <= s[t + 1]) end;
          end;
        lod: begin t := t + 1; s[t] := s[base(l) + a] end;
        sto: begin s[base(l)+a] := s[t]; writeln(s[t]); t := t - 1 end;
        cal: 
          begin {generate new block mark}
            s[t + 1] := base(l); s[t + 2] := b; s[t + 3] := p;
            b := t + 1; p := a
          end;
        int: t := t + a;
        jmp: p := a;
        jpc: begin if s[t] = 0 then p := a; t := t - 1 end
      end {with, case}
  until p = 1;
  writeln(' end pl/0');end {interpret};
这个机器是用来运行维尔特的PL/0的，一个为教学开发的Pascal子集编译器。