Skip to content

Latest commit

 

History

History
2577 lines (2221 loc) · 114 KB

pint.pas.org

File metadata and controls

2577 lines (2221 loc) · 114 KB

p5 virtual machine

compiler flags

(*$c+,t-,d-,l-*)

preamble

{*******************************************************************************
*                                                                              *
*                            Portable Pascal compiler                          *
*                            ************************                          *
*                                                                              *
*                                  Pascal P5                                   *
*                                                                              *
*                                  ETH May 76                                  *
*                                                                              *
*  Authors:                                                                    *
*                                                                              *
*     Urs Ammann                                                               *
*     Kesav Nori                                                               *
*     Christian Jacobi                                                         *
*     K. Jensen                                                                *
*     N. Wirth                                                                 *
*                                                                              *
*  Address:                                                                    *
*                                                                              *
*     Institut Fuer Informatik                                                 *
*     Eidg. Technische Hochschule                                              *
*     CH-8096 Zuerich                                                          *
*                                                                              *
*   This code is fully documented in the book                                  *
*         "Pascal Implementation"                                              *
*    by Steven Pemberton and Martin Daniels                                    *
*  published by Ellis Horwood, Chichester, UK                                  *
*          ISBN: 0-13-653-0311                                                 *
*        (also available in Japanese)                                          *
*                                                                              *
*  Steven Pemberton, CWI/AA,                                                   *
*  Kruislaan 413, 1098 SJ Amsterdam, NL                                        *
*  [email protected]                                                     *
*                                                                              *
*  Adaption from P4 to P5 by:                                                  *
*                                                                              *
*     Scott A. Moore                                                           *
*     [email protected]                                                      *
*                                                                              *
*  Note for the implementation.                                                *
*  ===========================                                                 *
*  This interpreter is written for the case where all the fundamental types    *
*  take one storage unit.                                                      *
*                                                                              *
*  In an actual implementation, the handling of the sp pointer has to take     *
*  into account the fact that the types may have lengths different from one:   *
*  in push and pop operations the sp has to be increased and decreased not     *
*  by 1, but by a number depending on the type concerned.                      *
*                                                                              *
*  However, where the number of units of storage has been computed by the      *
*  compiler, the value must not be corrected, since the lengths of the types   *
*  involved have already been taken into account.                              *
*                                                                              *
*  P5 is an extended version of P4 with the following goals:                   *
*                                                                              *
*  1. The remaining unimplemented functions of Pascal are implemented, so that *
*     P5 is no longer a "subset" of full Pascal. This was done because it is   *
*     no longer necessary to produce a minimum size implementation, and it     *
*     allows any standard program to be used with P5.                          *
*                                                                              *
*  2. The P5 compiler is brought up to ISO 7185 level 0 standards, both in the *
*     language it compiles for, and the language it is implemented in.         *
*                                                                              *
*  3. The internal storage efficiency is increased. For example, character     *
*     strings no longer take as much space per character as integers and other *
*     data. Sets are placed in their own space so that the minimum stack size  *
*     not determined by set size.                                              *
*                                                                              *
*  4. The remaining limitations and dependencies on the CDC 6000 version are   *
*     removed. For example, the instruction store no longer is packed 2        *
*     instructions to a 60 bit word.                                           *
*                                                                              *
*  5. General clean up. Longstanding bugs and issues are addressed. Constants  *
*     that were buried in the source (magic numbers) were made constants. The  *
*     type 'alpha' (specific to CDC 6000) was replaced with idstr, etc.        *
*                                                                              *
*  The idea of P5 is to obtain a compiler that is ISO 7185 compliant, can      *
*  compile itself, can compile any reasonable standard program, and is         *
*  efficient enough to be used as a normal compiler for some certain uses.     *
*  Finally, it can serve as a starting implementation for native compilers.    *
*                                                                              *
*  P5 machine instructions added:                                              *
*                                                                              *
*  rnd    round:   expects a float on stack, performs round() and places the   *
*                  result back on the stack as an integer.                     *
*                                                                              *
*  pck ln pack:    Expects a packed array on stack top, followed by the        *
*                  starting subscript, then the unpacked array. The parameter  *
*                  contains the length of packed array in elements. Performs   *
*                  pack(upa, ss, pa) and removes all from stack. The starting  *
*                  subscript is zero based and scaled to the element size.     *
*                                                                              *
*  upk ln pack:    Expects the starting subscript on stack top, followed by the*
*                  unpacked array, then the packed array. The parameter        *
*                  contains the length of packed array in elements. Performs   *
*                  unpack(pa, upa, ss) and removes all from stack. The starting*
*                  subscript is zero based and scaled to the element size.     *
*                                                                              *
*  rgs    set rng: Expects a set range specification on stack, with the last   *
*                  value on the top, and the first value next. The two values  *
*                  are replaced with a set with all of the values between and  *
*                  including the first and last values.                        *
*                                                                              *
*  fbv             Validates a file buffer variable. Expects a file address on *
*                  stack. The buffer is "validated" for lazy I/O, which means  *
*                  that if the associated file is in read mode, the delayed    *
*                  read to the buffer variable occurs. The file address remains*
*                  on the stack.                                               *
*                                                                              *
*  ipj v l ip jmp: Interprocedure jump. Contains the level of the target       *
*                  procedure, and the label to jump to. The stack is adjusted  *
*                  to remove all nested procedures/functions, then the label is*
*                  unconditionally jumped to.                                   *      *
*                                                                              *
*  cip p           Call indirect procedure/function. The top of stack has the  *
*                  address of a mp/address pair pushed by lpa. The dl of the   *
*                  current mark is replaced by the mp, and the address replaces*
*                  the current pc. The mp/ad address is removed from stack.    *
*                                                                              *
*  lpa p l q       Load procedure address. The current mark pointer is loaded  *
*                  onto the stack, followed by the target procedure or function*
*                  address. This puts enough information on the stack to call  *
*                  it with the callers environment.                            *
*                                                                              *
*  lip p q         load procedure function address. Loads a mark/address pair  *
*                  for a procedure or function parameter onto the stack. Used  *
*                  to pass a procedure or function parameter to another        *
*                  procedure or function.                                      *
*                                                                              *
*  efb    eof:     Find eof for binary file. The top of stack is a logical file*
*                  number. The eof boolean vale replaces it.                   *
*                                                                              *
*  fvb             Expects the length of the file buffer on stack, and the file*
*                  address under that. The buffer is "validated" for lazy I/O, *
*                  which means that if the associated file is in read mode, the*
*                  delayed read to the buffer variable occurs. The buffer      *
*                  length is removed only.                                     *
*                                                                              *
*  dmp q           Subtracts the value from the stack top. Used to dump the top*
*                  of the stack.                                               *
*                                                                              *
*  swp q           Pulls the second on stack to the top, swapping the top to   *
*                  elements. The size of the second on stack is specified, but *
*                  the top of the on stack is implied as a pointer.            *
*                                                                              *
*  tjp q           Expects a boolean on stack. Jumps to the address if the     *
*                  value is true. Removes the value from the stack.            *
*                                                                              *
*  P5 machine built in procedures/functions added:                             *
*                                                                              *
*  pag    page:    Expects a logical file number on stack top. Performs page().*
*                                                                              *
*  rsf    reset:   Expects a logical file number on stack top. Performs        *
*                  reset().                                                    *
*                                                                              *
*  rwf    rewrite: Expects a logical file number on stack top. Performs        *
*                  reset().                                                    *
*                                                                              *
*  wrb    write:   Expects a field number on stack top, followed by a boolean  *
*                  to print, then the logical file number. The boolean is      *
*                  output as per ISO 7185.                                     *
*                                                                              *
*  rgs    set rng: Expects a set range specification on stack, with the last   *
*                  value on the top, and the first value next. The two values  *
*                  are replaced with a set with all of the values between and  *
*                  including the first and last values.                        *
*                                                                              *
*  wrf    write:   Expects a fraction number on on stack top, followed by a    *
*                  field number, then a real to print, then the file address.  *
*                  The  real is output in r:f:f (fraction) format. All but the *
*                  file are removed from stack.                                *
*                                                                              *
*  wbf    write:   Expects the length of the type to write, followed by the    *
*                  variable address to write from, then the file address to    *
*                  write to. Writes binary store to the file.                  *
*                                                                              *
*  wbi    write:   Expects a file address on stack top, followed by an integer.*
*                  Writes the integer to the file in binary format.            *
*                                                                              *
*  wbr    write:   Expects a file address on stack top, followed by a real.    *
*                  Writes the real to the file in binary format.               *
*                                                                              *
*  wbc    write:   Expects a file address on stack top, followed by a          *
*                  character. Writes the character to the file in binary       *
*                  format.                                                     *
*                                                                              *
*  wbb    write:   Expects a file address on stack top, followed by a boolean. *
*                  Writes the boolean to the file in binary format.            *
*                                                                              *
*  rbf    read:    Expects a file address on stack top, followed by the length *
*                  of the type to read, then the variable address to read      *
*                  from. Reads binary store from the file.                     *
*                                                                              *
*  rsb    reset:   Expects a logical file number on stack top. Performs        *
*                  reset() and sets the file to binary mode read.              *
*                                                                              *
*  rwb    rewrite: Expects a logical file number on stack top. Performs        *
*                  rewrite() and sets the file to binary mode write.           *
*                                                                              *
*  gbf    get:     Get file binary. Expects the length of a file element on    *
*                  stack top, followed by a pointer to the file. The next file *
*                  element is loaded to the file buffer.                       *
*                                                                              *
*  pbf    put:     Put file binary. Expects the length of a file element on    *
*                  stack top, followed by a pointer to the file. Writes the    *
*                  file buffer to thr file.                                    *
*                                                                              *
*  Note that the previous version of P4 added some type specified instructions *
*  that used to be unified, typeless instructions.                             *
*                                                                              *
*  P5 errors added:                                                            *
*                                                                              *
*  182 identifier too long                                                     *
*  183 For index variable must be local to this block                          *
*  184 Interprocedure goto does not reference outter block of destination      *
*                                                                              *
*  P5 instructions modified:                                                   *
*                                                                              *
*  lca'string'       '                                                         *
*                                                                              *
*  was changed to                                                              *
*                                                                              *
*  lca 'string'''                                                              *
*                                                                              *
*  That is, lca has a space before the opening quote, no longer pads to the    *
*  right, and represents single quotes with a quote image. pint converts quote *
*  images back to single quotes, and pads out strings to their full length.    *
*                                                                              *
*  In addition, the way files work was extensively modified. Original P5 could *
*  not represent files as fully expressed variables, such as within an array   *
*  or record, and were effectively treated as constants. To treat them as true *
*  variable accesses, the stacking order of the file in all file subroutines   *
*  was changed so that the file is on the bottom. This matches the source      *
*  order of the file in write(f, ...) or read(f, ...). Also, the file          *
*  operations now leave the file on the stack for the duration of a write or   *
*  read, then dump them using a specific new instruction "dmp". This allows    *
*  multiparameter writes and reads to be effectively a chain of single         *
*  operations using one file reference. Finally, files were tied to the type   *
*  ending 'a', because files are now full variable references.                 *
*                                                                              *
*  New layout of memory in store:                                              *
*                                                                              *
*     maxstr -> ---------------------                                          *
*               | Constants         |                                          *
*         cp -> ---------------------                                          *
*               | Dynamic variables |                                          *
*         np -> ---------------------                                          *
*               | Free space        |                                          *
*         sp -> ---------------------                                          *
*               | Stack             |                                          *
*               ---------------------                                          *
*               | Code              |                                          *
*               ---------------------                                          *
*                                                                              *
* The constants are loaded upside down from the top of memory. The heap grows  *
* down, the stack grows up, and when they cross, it is an overflow error.      *
*                                                                              *
*******************************************************************************}

main listing

program pcode(input,output,prd,prr);

label 1;

const

      {

      Program object sizes and characteristics, sync with pint. These define
      the machine specific characteristics of the target. 
      
      This configuration is for a 32 bit machine as follows:

      integer               32  bits
      real                  64  bits
      char                  8   bits
      boolean               8   bits
      set                   256 bits
      pointers              32  bits
      marks                 32  bits
      File logical number   8   bits

      Both endian types are supported. There is no alignment needed, but you
      may wish to use alignment to tune the runtime speed.

      The machine characteristics dependent on byte accessable machines. This
      table is all you should need to adapt to any byte addressable machine.

      }

      intsize     =        4;  { size of integer }
      intal       =        4;  { alignment of integer }
      realsize    =        8;  { size of real }
      realal      =        4;  { alignment of real }
      charsize    =        1;  { size of char }
      charal      =        1;  { alignment of char }
      charmax     =        1;
      boolsize    =        1;  { size of boolean }
      boolal      =        1;  { alignment of boolean }
      ptrsize     =        4;  { size of pointer }
      adrsize     =        4;  { size of address }
      adral       =        4;  { alignment of address }
      setsize     =       32;  { size of set }
      setal       =        1;  { alignment of set }
      filesize    =        1;  { required runtime space for file (lfn) }
      fileidsize  =        1;  { size of the lfn only }
      stackal     =        4;  { alignment of stack }
      stackelsize =        4;  { stack element size }
      maxsize     =       32;  { this is the largest type that can be on the stack }
      { Heap alignment should be either the natural word alignment of the 
        machine, or the largest object needing alignment that will be allocated.
        It can also be used to enforce minimum block allocation policy. }
      heapal      =        4;  { alignment for each heap arena }
      sethigh     =      255;  { Sets are 256 values }
      setlow      =        0;
      ordmaxchar  =      255;  { Characters are 8 bit ISO/IEC 8859-1 }
      ordminchar  =        0;
      maxresult   = realsize;  { maximum size of function result }
      marksize    =       32;  { maxresult+6*ptrsize }
      { Value of nil is 1 because this allows checks for pointers that were
        initialized, which would be zero (since we clear all space to zero).
        In the new unified code/data space scheme, 0 and 1 are always invalid
        addresses, since the startup code is at least that long. }
      nilval      =        1;  { value of 'nil' }

      { end of pcom and pint common parameters }

      { internal constants }

      { !!! Need to use the small size memory to self compile, otherwise, by 
        definition, pint cannot fit into its own memory. }
      {elide}maxstr      = 16777215;{noelide}  { maximum size of addressing for program/var }
      {remove maxstr     =  2000000; remove}  { maximum size of addressing for program/var }
      maxdigh     = 6;       { number of digits in hex representation of maxstr }
      maxdigd     = 8;       { number of digits in decimal representation of maxstr }

      codemax     = maxstr;  { set size of code store to maximum possible }
      pcmax       = codemax; { set size of pc as same }
      begincode   = 9{2+6+1};{ beginning of code, offset by program preamble:

                                  2: mst
                                  6: cup
                                  1: stp

                             }
      maxlabel = 5000;       { total possible labels in intermediate }
      resspc   = 0;          { reserve space in heap (if you want) }

      { the header files have a logical no. followed by a buffer var }
      inputoff    = 32;      { 'input' file address }
      outputoff   = 34;      { 'output' file address }
      prdoff      = 36;      { 'prd' file address }
      prroff      = 38;      { 'prr' file address }

      { assigned logical channels for header files }
      inputfn    = 1;        { 'input' file no. }
      outputfn   = 2;        { 'output' file no. }
      prdfn      = 3;        { 'prd' file no. }
      prrfn      = 4;        { 'prr' file no. }

      { Mark element offsets 

        Mark format is:

        0:  Function return value, 64 bits, enables a full real result.
        8:  Static link.
        12: Dynamic link.
        16: Saved EP from previous frame.
        20: Stack bottom after locals allocate. Used for interprocdural gotos.
        24: EP from current frame. Used for interprocedural gotos.
        28: Return address

      }
      markfv     = 0;         { function value }
      marksl     = 8;         { static link }
      markdl     = 12;        { dynamic link }
      markep     = 16;        { (old) maximum frame size }
      marksb     = 20;        { stack bottom }
      market     = 24;        { current ep }
      markra     = 28;        { return address }

      stringlgth  = 1000;    { longest string length we can buffer }
      maxsp       = 36;      { number of predefined procedures/functions }
      maxins      = 255;     { maximum instruction code, 0-255 or byte }
      maxfil      = 100;     { maximum number of general (temp) files }
      maxalfa     = 10;      { maximum number of characters in alfa type }
      ujplen      = 5;       { length of ujp instruction (used for case jumps) }

      { debug flags: turn these on for various dumps and traces }

      dodmpins    = false;    { dump instructions after assembly }
      dodmplab    = false;    { dump label definitions }
      dodmpsto    = false;    { dump storage area specs }
      dotrcrot    = false;    { trace routine executions }
      dotrcins    = false;    { trace instruction executions }
      dopmd       = false;    { perform post-mortem dump on error }
      dosrclin    = true;     { add source line sets to code }
      dotrcsrc    = false;    { trace source line executions (requires dosrclin) }
      dodmpspc    = false;    { dump heap space after execution }
      dorecycl    = true;     { obey heap space recycle requests }
      { We can perform limited checking for attempts to access freed heap
        blocks, but only if we don't recycle them, because this moves the header
        information around. It is "limited" because there is nothing to prevent
        the program from holding the address of a data item within the block
        past a dispose. }                      
      dochkrpt    = false;    { check reuse of freed entry (automatically 
                                invokes dorecycl = false }

      { version numbers }
    
      majorver   = 1; { major version number }
      minorver   = 2; { minor version number }

type

type
      { These equates define the instruction layout. I have choosen a 32 bit
        layout for the instructions defined by (4 bit) digit:

           byte 0:   Instruction code
           byte 1:   P parameter
           byte 2-5: Q parameter

        This means that there are 256 instructions, 256 procedure levels,
        and 2gb of total addressing. This could be 4gb if we get rid of the
        need for negatives. }
      lvltyp      = 0..255;     { procedure/function level }
      instyp      = 0..maxins;  { instruction }
      address     = -maxstr..maxstr; { address }

      datatype    = (undef,int,reel,bool,sett,adr,mark,car);
      beta        = packed array[1..25] of char; (*error message*)
      settype     = set of setlow..sethigh;
      alfainx     = 1..maxalfa; { index for alfa type }
      alfa        = packed array[alfainx] of char;
      byte        = 0..255; { 8-bit byte }
      bytfil      = packed file of byte; { untyped file of bytes }
      fileno      = 0..maxfil; { logical file number }

variables

var   pc          : address;   (*program address register*)
      pctop       : address;   { top of code store }
      op : instyp; p : lvltyp; q : address;  (*instruction register*)
      q1: address; { extra parameter }
      store       : packed array [0..maxstr] of byte; { complete program storage }
      cp          : address;  (* pointer to next free constant position *)
      mp,sp,np,ep : address;  (* address registers *)
      (*mp  points to beginning of a data segment
        sp  points to top of the stack
        ep  points to the maximum extent of the stack
        np  points to top of the dynamically allocated area*)

      interpreting: boolean;

      { !!! remove this next statement for self compile }
      {elide}prd,prr     : text;{noelide}(*prd for read only, prr for write only *)

      instr       : array[instyp] of alfa; (* mnemonic instruction codes *)
      sptable     : array[0..maxsp] of alfa; (*standard functions and procedures*)
      insp        : array[instyp] of boolean; { instruction includes a p parameter }
      insq        : array[instyp] of 0..16; { length of q parameter }
      srclin      : integer; { current source line executing }

      filtable    : array [1..maxfil] of text; { general (temp) text file holders }
      { general (temp) binary file holders }
      bfiltable   : array [1..maxfil] of bytfil;
      { file state holding }
      filstate    : array [1..maxfil] of (fclosed, fread, fwrite);
      { file buffer full status }
      filbuff     : array [1..maxfil] of boolean;

      (*locally used for interpreting one instruction*)
      ad,ad1      : address;
      b           : boolean;
      i,j,i1,i2   : integer;
      c           : char;
      i3, i4      : integer;
      pa          : integer;
      r1, r2      : real;
      b1, b2      : boolean;
      s1, s2      : settype;
      c1, c2      : char;
      a1, a2, a3  : address;
      fn          : fileno;
      pcs         : address;

(*--------------------------------------------------------------------*)
{ Accessor functions

  These translate store variables to internal, and convert to and from store BAM
  formats.

  The acessors are fairly machine independent, they rely here on the machine
  being byte addressable. The endian format is inherent to the machine.

  The exception are the get/put int8,16,32,64 and 128 bit routines, which are
  dependent on the endian mode of the machine.

}

function getint(a: address): integer;

var r: record case boolean of

          true:  (i: integer);
          false: (b: packed array [1..intsize] of byte);

       end;
    i: 1..intsize;

begin 

   for i := 1 to intsize do r.b[i] := store[a+i-1];

   getint := r.i 

end;

procedure putint(a: address; x: integer); 

var r: record case boolean of

          true:  (i: integer);
          false: (b: packed array [1..intsize] of byte);

       end;
    i: 1..intsize;

begin 

   r.i := x;
   for i := 1 to intsize do store[a+i-1] := r.b[i]

end;

function getrel(a: address): real;

var r: record case boolean of

          true:  (r: real);
          false: (b: packed array [1..realsize] of byte);

       end;
    i: 1..realsize;

begin

   for i := 1 to realsize do r.b[i] := store[a+i-1];
   getrel := r.r 

end;

procedure putrel(a: address; f: real);

var r: record case boolean of

          true:  (r: real);
          false: (b: packed array [1..realsize] of byte);

       end;
    i: 1..realsize;

begin

   r.r := f;
   for i := 1 to realsize do store[a+i-1] := r.b[i]

end;

function getbol(a: address): boolean;

var b: boolean;

begin

   if store[a] = 0 then b := false else b := true;
   getbol := b

end;

procedure putbol(a: address; b: boolean);

begin

   store[a] := ord(b)

end;

procedure getset(a: address; var s: settype);

var r: record case boolean of

          true:  (s: settype);
          false: (b: packed array [1..setsize] of byte);

       end;
    i: 1..setsize;

begin

   for i := 1 to setsize do r.b[i] := store[a+i-1];
   s := r.s 

end;

procedure putset(a: address; s: settype);

var r: record case boolean of

          true:  (s: settype);
          false: (b: packed array [1..setsize] of byte);

       end;
    i: 1..setsize;

begin

   r.s := s;
   for i := 1 to setsize do store[a+i-1] := r.b[i]

end;

function getchr(a: address): char;

begin

   getchr := chr(store[a])

end;

procedure putchr(a: address; c: char);

begin

   store[a] := ord(c)

end;

function getadr(a: address): address;

var r: record case boolean of

          true:  (a: address);
          false: (b: packed array [1..adrsize] of byte);

       end;
    i: 1..adrsize;

begin

   for i := 1 to adrsize do r.b[i] := store[a+i-1];
   getadr := r.a 

end;

procedure putadr(a: address; ad: address);

var r: record case boolean of

          true:  (a: address);
          false: (b: packed array [1..adrsize] of byte);

       end;
    i: 1..adrsize;

begin

   r.a := ad;
   for i := 1 to adrsize do store[a+i-1] := r.b[i]

end;

{ Swap pointer on top with second on stack. The size of the second is given. }

procedure swpstk(l: address);

var sb: packed array [1..maxsize] of byte;
    p:  address;
    i:  1..maxsize;

begin

   { get the top pointer }
   p := getadr(sp-adrsize);
   { load up the second on stack }
   for i := 1 to l do sb[i] := store[sp-adrsize-l+i-1];
   putadr(sp-adrsize-l, p); { place pointer at bottom }
   for i := 1 to l do store[sp-l+i-1] := sb[i] { place second as new top }

end;

{ end of accessor functions
(*--------------------------------------------------------------------*)

{ Push/pop

  These routines handle both the data type, and their lengths on the stack.

}

procedure popint(var i: integer); begin sp := sp-intsize; i := getint(sp) end;
procedure pshint(i: integer); begin putint(sp, i); sp := sp+intsize end;
procedure poprel(var r: real); begin sp := sp-realsize; r := getrel(sp) end;
procedure pshrel(r: real); begin putrel(sp, r); sp := sp+realsize; end;
procedure popset(var s: settype); begin sp := sp-setsize; getset(sp, s) end;
procedure pshset(s: settype); begin putset(sp, s); sp := sp+setsize end;
procedure popadr(var a: address); begin sp := sp-adrsize; a := getadr(sp) end;
procedure pshadr(a: address); begin putadr(sp, a); sp := sp+adrsize end;

{ print in hex (carefull, it chops high digits freely!) }

procedure wrthex(v: integer; { value } f: integer { field });
var p,i,d: integer;
begin
   p := 1;
   for i := 1 to f-1 do p := p*16;
   while p > 0 do begin
      d := v div p mod 16; { extract digit }
      if d < 10 then write(chr(d+ord('0')))
      else write(chr(d-10+ord('A')));
      p := p div 16
   end
end;

{ list single instruction at address }

procedure lstins(var ad: address);

var ads: address;
    op: instyp; p : lvltyp; q : address;  (*instruction register*)

begin

   { fetch instruction from byte store }
   ads := ad;
   op := store[ad]; ad := ad+1;
   if insp[op] then begin p := store[ad]; ad := ad+1 end;
   if insq[op] > 0 then begin 

      case insq[op] of

         1:        q := store[ad];
         intsize:  q := getint(ad);

      end;
      ad := ad+insq[op] 

   end;
   write(': ');
   wrthex(op, 2);
   write(' ', instr[op]:10, '  ');
   if insp[op] then begin

      wrthex(p, 2);
      if insq[op] > 0 then begin write(','); wrthex(q, maxdigh) end

   end else if insq[op] > 0 then begin write('   '); wrthex(q, maxdigh) end

end; 

{ dump contents of instruction memory }

procedure dmpins;

var i:  address;

begin

   writeln;
   writeln('Contents of instruction memory');
   writeln;
   writeln('  Addr  Opc Ins          P       Q');
   writeln('----------------------------------');
   i := 0;
   while i < pctop do begin

      wrthex(i, maxdigh);
      lstins(i);
      writeln

   end;
   writeln

end; 

{ align address, upwards }

procedure alignu(algn: address; var flc: address);
  var l: integer;
begin
  l := flc-1;
  flc := l + algn  -  (algn+l) mod algn
end (*align*);

{ align address, downwards }

procedure alignd(algn: address; var flc: address);
  var l: integer;
begin
  l := flc+1;
  flc := l - algn  +  (algn-l) mod algn
end (*align*);

lookup tables

instruction mnemonics

(*--------------------------------------------------------------------*)

{ load intermediate file }

procedure load;
   type  labelst  = (entered,defined); (*label situation*)
         labelrg  = 0..maxlabel;       (*label range*)
         labelrec = record
                          val: address;
                           st: labelst
                    end;
   var  word : array[alfainx] of char; ch  : char;
        labeltab: array[labelrg] of labelrec;
        labelvalue: address;
        iline: integer; { line number of intermediate file }

   procedure init;
      var i: integer;
   begin for i := 0 to maxins do instr[i] := '          ';
         {

           Notes: 

           1. Instructions marked with "*" are for internal use only.
              The "*" mark both shows in the listing, and also prevents
              their use in the intermediate file, since only alpha
              characters are allowed as opcode labels.

           2. "---" entries are no longer used, but left here to keep the
              original instruction numbers from P4. They could be safely
              assigned to other instructions if the space is needed.

         }
         instr[   0]:='lodi      '; insp[   0] := true;  insq[   0] := intsize;     
         instr[   1]:='ldoi      '; insp[   1] := false; insq[   1] := intsize;
         instr[   2]:='stri      '; insp[   2] := true;  insq[   2] := intsize;     
         instr[   3]:='sroi      '; insp[   3] := false; insq[   3] := intsize;
         instr[   4]:='lda       '; insp[   4] := true;  insq[   4] := intsize;     
         instr[   5]:='lao       '; insp[   5] := false; insq[   5] := intsize;
         instr[   6]:='stoi      '; insp[   6] := false; insq[   6] := 0;
         instr[   7]:='ldc       '; insp[   7] := false; insq[   7] := intsize;
         instr[   8]:='---       '; insp[   8] := false; insq[   8] := 0;
         instr[   9]:='indi      '; insp[   9] := false; insq[   9] := intsize;
         instr[  10]:='inci      '; insp[  10] := false; insq[  10] := intsize;
         instr[  11]:='mst       '; insp[  11] := true;  insq[  11] := 0;
         instr[  12]:='cup       '; insp[  12] := true;  insq[  12] := intsize;
         instr[  13]:='ents      '; insp[  13] := false; insq[  13] := intsize;
         instr[  14]:='retp      '; insp[  14] := false; insq[  14] := 0;
         instr[  15]:='csp       '; insp[  15] := false; insq[  15] := intsize;
         instr[  16]:='ixa       '; insp[  16] := false; insq[  16] := intsize;
         instr[  17]:='equa      '; insp[  17] := false; insq[  17] := 0;
         instr[  18]:='neqa      '; insp[  18] := false; insq[  18] := 0;
         instr[  19]:='geqa      '; insp[  19] := false; insq[  19] := 0;
         instr[  20]:='grta      '; insp[  20] := false; insq[  20] := 0;
         instr[  21]:='leqa      '; insp[  21] := false; insq[  21] := 0;
         instr[  22]:='lesa      '; insp[  22] := false; insq[  22] := 0;
         instr[  23]:='ujp       '; insp[  23] := false; insq[  23] := intsize;
         instr[  24]:='fjp       '; insp[  24] := false; insq[  24] := intsize;
         instr[  25]:='xjp       '; insp[  25] := false; insq[  25] := intsize;
         instr[  26]:='chki      '; insp[  26] := false; insq[  26] := intsize;
         instr[  27]:='eof       '; insp[  27] := false; insq[  27] := 0;
         instr[  28]:='adi       '; insp[  28] := false; insq[  28] := 0;
         instr[  29]:='adr       '; insp[  29] := false; insq[  29] := 0;
         instr[  30]:='sbi       '; insp[  30] := false; insq[  30] := 0;
         instr[  31]:='sbr       '; insp[  31] := false; insq[  31] := 0;
         instr[  32]:='sgs       '; insp[  32] := false; insq[  32] := 0;
         instr[  33]:='flt       '; insp[  33] := false; insq[  33] := 0;
         instr[  34]:='flo       '; insp[  34] := false; insq[  34] := 0;
         instr[  35]:='trc       '; insp[  35] := false; insq[  35] := 0;
         instr[  36]:='ngi       '; insp[  36] := false; insq[  36] := 0;
         instr[  37]:='ngr       '; insp[  37] := false; insq[  37] := 0;
         instr[  38]:='sqi       '; insp[  38] := false; insq[  38] := 0;
         instr[  39]:='sqr       '; insp[  39] := false; insq[  39] := 0;
         instr[  40]:='abi       '; insp[  40] := false; insq[  40] := 0;
         instr[  41]:='abr       '; insp[  41] := false; insq[  41] := 0;
         instr[  42]:='not       '; insp[  42] := false; insq[  42] := 0;
         instr[  43]:='and       '; insp[  43] := false; insq[  43] := 0;
         instr[  44]:='ior       '; insp[  44] := false; insq[  44] := 0;
         instr[  45]:='dif       '; insp[  45] := false; insq[  45] := 0;
         instr[  46]:='int       '; insp[  46] := false; insq[  46] := 0;
         instr[  47]:='uni       '; insp[  47] := false; insq[  47] := 0;
         instr[  48]:='inn       '; insp[  48] := false; insq[  48] := 0;
         instr[  49]:='mod       '; insp[  49] := false; insq[  49] := 0;
         instr[  50]:='odd       '; insp[  50] := false; insq[  50] := 0;
         instr[  51]:='mpi       '; insp[  51] := false; insq[  51] := 0;
         instr[  52]:='mpr       '; insp[  52] := false; insq[  52] := 0;
         instr[  53]:='dvi       '; insp[  53] := false; insq[  53] := 0;
         instr[  54]:='dvr       '; insp[  54] := false; insq[  54] := 0;
         instr[  55]:='mov       '; insp[  55] := false; insq[  55] := intsize;
         instr[  56]:='lca       '; insp[  56] := false; insq[  56] := intsize;
         instr[  57]:='deci      '; insp[  57] := false; insq[  57] := intsize;
         instr[  58]:='stp       '; insp[  58] := false; insq[  58] := 0;
         instr[  59]:='ordi      '; insp[  59] := false; insq[  59] := 0;
         instr[  60]:='chr       '; insp[  60] := false; insq[  60] := 0;
         instr[  61]:='ujc       '; insp[  61] := false; insq[  61] := intsize;
         instr[  62]:='rnd       '; insp[  62] := false; insq[  62] := 0;
         instr[  63]:='pck       '; insp[  63] := false; insq[  63] := intsize;
         instr[  64]:='upk       '; insp[  64] := false; insq[  64] := intsize;
         instr[  65]:='ldoa      '; insp[  65] := false; insq[  65] := intsize;
         instr[  66]:='ldor      '; insp[  66] := false; insq[  66] := intsize;
         instr[  67]:='ldos      '; insp[  67] := false; insq[  67] := intsize;
         instr[  68]:='ldob      '; insp[  68] := false; insq[  68] := intsize;
         instr[  69]:='ldoc      '; insp[  69] := false; insq[  69] := intsize;
         instr[  70]:='stra      '; insp[  70] := true;  insq[  70] := intsize;
         instr[  71]:='strr      '; insp[  71] := true;  insq[  71] := intsize;
         instr[  72]:='strs      '; insp[  72] := true;  insq[  72] := intsize;
         instr[  73]:='strb      '; insp[  73] := true;  insq[  73] := intsize;
         instr[  74]:='strc      '; insp[  74] := true;  insq[  74] := intsize;
         instr[  75]:='sroa      '; insp[  75] := false; insq[  75] := intsize;
         instr[  76]:='sror      '; insp[  76] := false; insq[  76] := intsize;
         instr[  77]:='sros      '; insp[  77] := false; insq[  77] := intsize;
         instr[  78]:='srob      '; insp[  78] := false; insq[  78] := intsize;
         instr[  79]:='sroc      '; insp[  79] := false; insq[  79] := intsize;
         instr[  80]:='stoa      '; insp[  80] := false; insq[  80] := 0;
         instr[  81]:='stor      '; insp[  81] := false; insq[  81] := 0;
         instr[  82]:='stos      '; insp[  82] := false; insq[  82] := 0;
         instr[  83]:='stob      '; insp[  83] := false; insq[  83] := 0;
         instr[  84]:='stoc      '; insp[  84] := false; insq[  84] := 0;
         instr[  85]:='inda      '; insp[  85] := false; insq[  85] := intsize;
         instr[  86]:='indr      '; insp[  86] := false; insq[  86] := intsize;
         instr[  87]:='inds      '; insp[  87] := false; insq[  87] := intsize;
         instr[  88]:='indb      '; insp[  88] := false; insq[  88] := intsize;
         instr[  89]:='indc      '; insp[  89] := false; insq[  89] := intsize;
         instr[  90]:='inca      '; insp[  90] := false; insq[  90] := intsize;
         instr[  91]:='incr      '; insp[  91] := false; insq[  91] := intsize;
         instr[  92]:='incs      '; insp[  92] := false; insq[  92] := intsize;
         instr[  93]:='incb      '; insp[  93] := false; insq[  93] := intsize;
         instr[  94]:='incc      '; insp[  94] := false; insq[  94] := intsize;
         instr[  95]:='chka      '; insp[  95] := false; insq[  95] := intsize;
         instr[  96]:='chkr      '; insp[  96] := false; insq[  96] := intsize;
         instr[  97]:='chks      '; insp[  97] := false; insq[  97] := intsize;
         instr[  98]:='chkb      '; insp[  98] := false; insq[  98] := intsize;
         instr[  99]:='chkc      '; insp[  99] := false; insq[  99] := intsize;
         instr[ 100]:='deca      '; insp[ 100] := false; insq[ 100] := intsize;
         instr[ 101]:='decr      '; insp[ 101] := false; insq[ 101] := intsize;
         instr[ 102]:='decs      '; insp[ 102] := false; insq[ 102] := intsize;
         instr[ 103]:='decb      '; insp[ 103] := false; insq[ 103] := intsize;
         instr[ 104]:='decc      '; insp[ 104] := false; insq[ 104] := intsize;
         instr[ 105]:='loda      '; insp[ 105] := true;  insq[ 105] := intsize;
         instr[ 106]:='lodr      '; insp[ 106] := true;  insq[ 106] := intsize;
         instr[ 107]:='lods      '; insp[ 107] := true;  insq[ 107] := intsize;
         instr[ 108]:='lodb      '; insp[ 108] := true;  insq[ 108] := intsize;
         instr[ 109]:='lodc      '; insp[ 109] := true;  insq[ 109] := intsize;
         instr[ 110]:='rgs       '; insp[ 110] := false; insq[ 110] := 0;
         instr[ 111]:='fbv       '; insp[ 111] := false; insq[ 111] := 0;
         instr[ 112]:='ipj       '; insp[ 112] := true;  insq[ 112] := intsize;
         instr[ 113]:='cip       '; insp[ 113] := true;  insq[ 113] := 0;
         instr[ 114]:='lpa       '; insp[ 114] := true;  insq[ 114] := intsize;
         instr[ 115]:='efb       '; insp[ 115] := false; insq[ 115] := 0;
         instr[ 116]:='fvb       '; insp[ 116] := false; insq[ 116] := 0;
         instr[ 117]:='dmp       '; insp[ 117] := false; insq[ 117] := intsize;
         instr[ 118]:='swp       '; insp[ 118] := false; insq[ 118] := intsize;
         instr[ 119]:='tjp       '; insp[ 119] := false; insq[ 119] := intsize;
         instr[ 120]:='lip       '; insp[ 120] := true;  insq[ 120] := intsize;
         instr[ 121]:='---       '; insp[ 121] := false; insq[ 121] := 0;
         instr[ 122]:='---       '; insp[ 122] := false; insq[ 122] := 0;
         instr[ 123]:='ldci      '; insp[ 123] := false; insq[ 123] := intsize;
         instr[ 124]:='ldcr      '; insp[ 124] := false; insq[ 124] := intsize;
         instr[ 125]:='ldcn      '; insp[ 125] := false; insq[ 125] := 0;
         instr[ 126]:='ldcb      '; insp[ 126] := false; insq[ 126] := boolsize;
         instr[ 127]:='ldcc      '; insp[ 127] := false; insq[ 127] := charsize;
         instr[ 128]:='reti      '; insp[ 128] := false; insq[ 128] := 0;
         instr[ 129]:='retr      '; insp[ 129] := false; insq[ 129] := 0;
         instr[ 130]:='retc      '; insp[ 130] := false; insq[ 130] := 0;
         instr[ 131]:='retb      '; insp[ 131] := false; insq[ 131] := 0;
         instr[ 132]:='reta      '; insp[ 132] := false; insq[ 132] := 0;
         instr[ 133]:='ordr      '; insp[ 133] := false; insq[ 133] := 0;
         instr[ 134]:='ordb      '; insp[ 134] := false; insq[ 134] := 0;
         instr[ 135]:='ords      '; insp[ 135] := false; insq[ 135] := 0;
         instr[ 136]:='ordc      '; insp[ 136] := false; insq[ 136] := 0;
         instr[ 137]:='equi      '; insp[ 137] := false; insq[ 137] := 0;
         instr[ 138]:='equr      '; insp[ 138] := false; insq[ 138] := 0;
         instr[ 139]:='equb      '; insp[ 139] := false; insq[ 139] := 0;
         instr[ 140]:='equs      '; insp[ 140] := false; insq[ 140] := 0;
         instr[ 141]:='equc      '; insp[ 141] := false; insq[ 141] := 0;
         instr[ 142]:='equm      '; insp[ 142] := false; insq[ 142] := intsize;
         instr[ 143]:='neqi      '; insp[ 143] := false; insq[ 143] := 0;
         instr[ 144]:='neqr      '; insp[ 144] := false; insq[ 144] := 0;
         instr[ 145]:='neqb      '; insp[ 145] := false; insq[ 145] := 0;
         instr[ 146]:='neqs      '; insp[ 146] := false; insq[ 146] := 0;
         instr[ 147]:='neqc      '; insp[ 147] := false; insq[ 147] := 0;
         instr[ 148]:='neqm      '; insp[ 148] := false; insq[ 148] := intsize;
         instr[ 149]:='geqi      '; insp[ 149] := false; insq[ 149] := 0;
         instr[ 150]:='geqr      '; insp[ 150] := false; insq[ 150] := 0;
         instr[ 151]:='geqb      '; insp[ 151] := false; insq[ 151] := 0;
         instr[ 152]:='geqs      '; insp[ 152] := false; insq[ 152] := 0;
         instr[ 153]:='geqc      '; insp[ 153] := false; insq[ 153] := 0;
         instr[ 154]:='geqm      '; insp[ 154] := false; insq[ 154] := intsize;
         instr[ 155]:='grti      '; insp[ 155] := false; insq[ 155] := 0;
         instr[ 156]:='grtr      '; insp[ 156] := false; insq[ 156] := 0;
         instr[ 157]:='grtb      '; insp[ 157] := false; insq[ 157] := 0;
         instr[ 158]:='grts      '; insp[ 158] := false; insq[ 158] := 0;
         instr[ 159]:='grtc      '; insp[ 159] := false; insq[ 159] := 0;
         instr[ 160]:='grtm      '; insp[ 160] := false; insq[ 160] := intsize;
         instr[ 161]:='leqi      '; insp[ 161] := false; insq[ 161] := 0;
         instr[ 162]:='leqr      '; insp[ 162] := false; insq[ 162] := 0;
         instr[ 163]:='leqb      '; insp[ 163] := false; insq[ 163] := 0;
         instr[ 164]:='leqs      '; insp[ 164] := false; insq[ 164] := 0;
         instr[ 165]:='leqc      '; insp[ 165] := false; insq[ 165] := 0;
         instr[ 166]:='leqm      '; insp[ 166] := false; insq[ 166] := intsize;
         instr[ 167]:='lesi      '; insp[ 167] := false; insq[ 167] := 0;
         instr[ 168]:='lesr      '; insp[ 168] := false; insq[ 168] := 0;
         instr[ 169]:='lesb      '; insp[ 169] := false; insq[ 169] := 0;
         instr[ 170]:='less      '; insp[ 170] := false; insq[ 170] := 0;
         instr[ 171]:='lesc      '; insp[ 171] := false; insq[ 171] := 0;
         instr[ 172]:='lesm      '; insp[ 172] := false; insq[ 172] := intsize;
         instr[ 173]:='ente      '; insp[ 173] := false; insq[ 173] := intsize;
         instr[ 174]:='mrkl*     '; insp[ 174] := false; insq[ 174] := intsize;

system procedures

{ sav (mark) and rst (release) were removed }
sptable[  0]:='get       ';     sptable[  1]:='put       ';
sptable[  2]:='---       ';     sptable[  3]:='rln       ';
sptable[  4]:='new       ';     sptable[  5]:='wln       ';
sptable[  6]:='wrs       ';     sptable[  7]:='eln       ';
sptable[  8]:='wri       ';     sptable[  9]:='wrr       ';
sptable[ 10]:='wrc       ';     sptable[ 11]:='rdi       ';
sptable[ 12]:='rdr       ';     sptable[ 13]:='rdc       ';
sptable[ 14]:='sin       ';     sptable[ 15]:='cos       ';
sptable[ 16]:='exp       ';     sptable[ 17]:='log       ';
sptable[ 18]:='sqt       ';     sptable[ 19]:='atn       ';
sptable[ 20]:='---       ';     sptable[ 21]:='pag       ';
sptable[ 22]:='rsf       ';     sptable[ 23]:='rwf       ';
sptable[ 24]:='wrb       ';     sptable[ 25]:='wrf       ';
sptable[ 26]:='dsp       ';     sptable[ 27]:='wbf       ';
sptable[ 28]:='wbi       ';     sptable[ 29]:='wbr       ';
sptable[ 30]:='wbc       ';     sptable[ 31]:='wbb       ';
sptable[ 32]:='rbf       ';     sptable[ 33]:='rsb       ';
sptable[ 34]:='rwb       ';     sptable[ 35]:='gbf       ';
sptable[ 36]:='pbf       ';

pc := begincode; cp := maxstr; { set constants pointer to top of storage } for i:= 1 to 10 do word[i]:= ’ ‘; for i:= 0 to maxlabel do with labeltab[i] do begin val:=-1; st:= entered end; { initalize file state } for i := 1 to maxfil do filstate[i] := fclosed;

{ !!! remove this next statement for self compile } assign(prd, paramstr(1)); {elide}reset(prd);{noelide}

iline := 1 { set 1st line of intermediate } end;(init)

procedure errorl(string: beta); (error in loading) begin writeln; writeln(‘*** Program load error: [‘, iline:1, ‘] ‘, string); goto 1 end; (errorl)

procedure dmplabs; { dump label table }

var i: labelrg;

begin

writeln; writeln(‘Label table’); writeln; for i := 1 to maxlabel do if labeltab[i].val <> -1 then begin

write(‘Label: ‘, i:5, ’ value: ‘, labeltab[i].val, ’ ‘); if labeltab[i].st = entered then writeln(‘Entered’) else writeln(‘Defined’)

end; writeln

end;

procedure update(x: labelrg); (when a label definition lx is found) var curr,succ,ad: address; (resp. current element and successor element of a list of future references) endlist: boolean; op: instyp; q : address; (instruction register) begin if labeltab[x].st=defined then errorl(‘duplicated label ‘) else begin if labeltab[x].val<>-1 then (forward reference(s)) begin curr:= labeltab[x].val; endlist:= false; while not endlist do begin ad := curr; op := store[ad]; { get instruction } q := getadr(ad+1+ord(insp[op])); succ:= q; { get target address from that } q:= labelvalue; { place new target address } ad := curr; putadr(ad+1+ord(insp[op]), q); if succ=-1 then endlist:= true else curr:= succ end end; labeltab[x].st := defined; labeltab[x].val:= labelvalue; end end;(update)

tokenizer

procedure getnxt; { get next character }
begin
   ch := ' ';
   if not eoln(prd) then read(prd,ch)
end;

procedure skpspc; { skip spaces }
begin
  while (ch = ' ') and not eoln(prd) do getnxt
end;

procedure getlin; { get next line }
begin
  readln(prd);
  iline := iline+1 { next intermediate line }
end;

procedure assemble; forward;

procedure generate;(*generate segment of code*)
   var x: integer; (* label number *)
       again: boolean;
begin
   again := true;
   while again do
         begin if eof(prd) then errorl('unexpected eof on input  ');
               getnxt;(* first character of line*)
               if not (ch in ['i', 'l', 'q', ' ', '!', ':']) then 
                 errorl('unexpected line start    ');
               case ch of
                    'i': getlin;
                    'l': begin read(prd,x);
                               getnxt;
                               if ch='=' then read(prd,labelvalue)
                                         else labelvalue:= pc;
                               update(x); getlin
                         end;
                    'q': begin again := false; getlin end;
                    ' ': begin getnxt; assemble end;
                    ':': begin { source line }

                            read(prd,x); { get source line number }
                            if dosrclin then begin 

                               { pass source line register instruction } 
                               store[pc] := 174; pc := pc+1;
                               putint(pc, x); pc := pc+intsize

                            end;
                            { skip the rest of the line, which would be the
                              contents of the source line if included }
                            while not eoln(prd) do
                               read(prd, c); { get next character }
                            getlin { source line }

                         end
               end;
         end
end; (*generate*)

assembler

procedure assemble; (*translate symbolic code into machine code and store*)
   var name :alfa; r :real; s :settype;
       i,x,s1,lb,ub,l:integer; c: char;
       str: packed array [1..stringlgth] of char; { buffer for string constants }
       t: integer; { [sam] temp for compiler bug }

   procedure lookup(x: labelrg); (* search in label table*)
   begin case labeltab[x].st of
             entered: begin q := labeltab[x].val;
                        labeltab[x].val := pc
                      end;
             defined: q:= labeltab[x].val
         end(*case label..*)
   end;(*lookup*)

   procedure labelsearch;
      var x: labelrg;
   begin while (ch<>'l') and not eoln(prd) do read(prd,ch);
         read(prd,x); lookup(x)
   end;(*labelsearch*)

   procedure getname;
   var i: alfainx;
   begin 
     if eof(prd) then errorl('unexpected eof on input  ');
     for i := 1 to maxalfa do word[i] := ' ';
     i := 1; { set 1st character of word }
     while ch in ['a'..'z'] do begin
       if i = maxalfa then errorl('Opcode label is too long ');
       word[i] := ch;
       i := i+1; ch := ' ';
       if not eoln(prd) then read(prd,ch); { next character }
     end;
     pack(word,1,name) 
   end; (*getname*)

   procedure storeop;
   begin
     if pc+1 > cp then errorl('Program code overflow    ');
     store[pc] := op; pc := pc+1
   end;

   procedure storep;
   begin
     if pc+1 > cp then errorl('Program code overflow    ');
     store[pc] := p; pc := pc+1
   end;

   procedure storeq;
   begin
     if pc+adrsize > cp then errorl('Program code overflow    ');
      putadr(pc, q); pc := pc+adrsize
   end;

   procedure storeq1;
   begin
     if pc+adrsize > cp then errorl('Program code overflow    ');
      putadr(pc, q1); pc := pc+adrsize
   end;

begin  p := 0;  q := 0;  op := 0;
   getname;
   { note this search removes the top instruction from use }
   while (instr[op]<>name) and (op < maxins) do op := op+1;
   if op = maxins then errorl('illegal instruction      ');

   case op of  (* get parameters p,q *)

       (*lod,str,lda,lip*)
       0, 105, 106, 107, 108, 109,
       2, 70, 71, 72, 73, 74,4,120: begin read(prd,p,q); storeop; storep; 
                                          storeq
                                    end;

       { [sam] There is a compiler bug with reads to restricted range 
         variables in IP Pascal here. }
       12(*cup*): begin read(prd,t{p}); p := t; labelsearch; storeop; 
                        storep; storeq
                  end;

       11,113(*mst,cip*): begin read(prd,p); storeop; storep end;

       { equm,neqm,geqm,grtm,leqm,lesm take a parameter }
       142, 148, 154, 160, 166, 172,

       (*lao,ixa,mov,dmp,swp*)
       5,16,55,117,118,

       (*ldo,sro,ind,inc,dec*)
       1, 65, 66, 67, 68, 69,
       3, 75, 76, 77, 78, 79,
       9, 85, 86, 87, 88, 89,
       10, 90, 91, 92, 93, 94,
       57, 100, 101, 102, 103, 104: begin read(prd,q); storeop; storeq end;

       (*pck,upk*)
       63, 64: begin read(prd,q); read(prd,q1); storeop; storeq; storeq1 end;

       (*ujp,fjp,xjp,lpa,tjp*)
       23,24,25,119,

       (*ents,ente*)
       13, 173: begin labelsearch; storeop; storeq end;

       (*ipj,lpa*)
       112,114: begin read(prd,p); labelsearch; storeop; storep; storeq end;

       15 (*csp*): begin skpspc; getname;
                        while name<>sptable[q] do 
                        begin q := q+1; if q > maxsp then 
                              errorl('std proc/func not found  ')
                        end;
                        storeop; storeq
                   end;

       7, 123, 124, 125, 126, 127 (*ldc*): begin case op of  (*get q*)
                        123: begin read(prd,i); storeop; 
                                   if pc+intsize > cp then 
                                      errorl('Program code overflow    ');
                                   putint(pc, i); pc := pc+intsize
                             end;

                        124: begin read(prd,r); 
                                   cp := cp-realsize;
                                   alignd(realal, cp);
                                   if cp <= 0 then 
                                      errorl('constant table overflow  ');
                                   putrel(cp, r); q := cp;
                                   storeop; storeq
                             end;

                        125: storeop; (*p,q = 0*)

                        126: begin read(prd,q); storeop; 
                                   if pc+1 > cp then 
                                     errorl('Program code overflow    ');
                                   putbol(pc, q <> 0); pc := pc+1 end;

                        127: begin
                               skpspc;
                               if ch <> '''' then
                                 errorl('illegal character        ');
                               getnxt;  c := ch;
                               getnxt;
                               if ch <> '''' then
                                 errorl('illegal character        ');
                               storeop; 
                               if pc+1 > cp then 
                                 errorl('Program code overflow    ');
                               putchr(pc, c); pc := pc+1
                             end;
                        7: begin skpspc; 
                                if ch <> '(' then errorl('ldc() expected           ');
                                s := [ ];  getnxt;
                                while ch<>')' do
                                begin read(prd,s1); getnxt; s := s + [s1]
                                end;
                                cp := cp-setsize;
                                alignd(setal, cp);
                                if cp <= 0 then
                                   errorl('constant table overflow  ');
                                putset(cp, s);
                                q := cp;
                                storeop; storeq
                             end
                        end (*case*)
                  end;

        26, 95, 96, 97, 98, 99 (*chk*): begin
                      read(prd,lb,ub);
                      if op = 95 then q := lb
                      else
                      begin
                        cp := cp-intsize; 
                        alignd(intal, cp);
                        if cp <= 0 then errorl('constant table overflow  ');
                        putint(cp, ub);
                        cp := cp-intsize; 
                        alignd(intal, cp);
                        if cp <= 0 then errorl('constant table overflow  ');
                        putint(cp, lb); q := cp
                      end;
                      storeop; storeq
                    end;

        56 (*lca*): begin read(prd,l); skpspc;
                      for i := 1 to stringlgth do str[i] := ' ';
                      if ch <> '''' then errorl('bad string format        ');
                      i := 0;
                      repeat
                        if eoln(prd) then errorl('unterminated string      ');
                        getnxt;
                        c := ch; if (ch = '''') and (prd^ = '''') then begin
                          getnxt; c := ' '
                        end;
                        if c <> '''' then begin
                          if i >= stringlgth then errorl('string overflow          ');
                          str[i+1] := ch; { accumulate string }
                          i := i+1
                        end
                      until c = '''';
                      { place in storage }
                      cp := cp-l;
                      if cp <= 0 then errorl('constant table overflow  ');
                      q := cp;
                      for x := 1 to l do putchr(q+x-1, str[x]);
                      { this should have worked, the for loop is faulty 
                        because the calculation for end is done after the i
                        set
                      for i := 0 to i-1 do putchr(q+i, str[i+1]);
                      }
                      storeop; storeq
                    end;

       14, 128, 129, 130, 131, 132, (*ret*)

       { equ,neq,geq,grt,leq,les with no parameter }
       17, 137, 138, 139, 140, 141,
       18, 143, 144, 145, 146, 147,
       19, 149, 150, 151, 152, 153,
       20, 155, 156, 157, 158, 159,
       21, 161, 162, 163, 164, 165,
       22, 167, 168, 169, 170, 171,

       59, 133, 134, 135, 136, (*ord*)

       6, 80, 81, 82, 83, 84, (*sto*)

       { eof,adi,adr,sbi,sbr,sgs,flt,flo,trc,ngi,ngr,sqi,sqr,abi,abr,not,and,
         ior,dif,int,uni,inn,mod,odd,mpi,mpr,dvi,dvr,stp,chr,rnd,rgs,fbv, 
         fvb }
       27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
       48,49,50,51,52,53,54,58,60,62,110,111,
       115, 116: storeop;

                   (*ujc must have same length as ujp, so we output a dummy
                     q argument*)
       61 (*ujc*): begin storeop; q := 0; storeq end

   end; (*case*)

   getlin { next intermediate line }

end; (*assemble*)

main code for load

begin (*load*)

   init;
   generate;
   pctop := pc; { save top of code store }
   pc := 0;
   generate;
   alignu(stackal, pctop); { align the end of code for stack bottom }
   alignd(heapal, cp); { align the start of cp for heap top }

   if dodmpsto then begin { dump storage overview }

      writeln;
      writeln('Storage areas occupied');
      writeln;
      write('Program     '); wrthex(0, maxdigh); write('-'); wrthex(pctop-1, maxdigh);
      writeln(' (',pctop:maxdigd,')');
      write('Stack/Heap  '); wrthex(pctop, maxdigh); write('-'); wrthex(cp-1, maxdigh); 
      writeln(' (',cp-pctop+1:maxdigd,')');
      write('Constants   '); wrthex(cp, maxdigh); write('-'); wrthex(maxstr, maxdigh); 
      writeln(' (',maxstr-(cp):maxdigd,')');
      writeln

   end;
   if dodmpins then dmpins; { Debug: dump instructions from store }
   if dodmplab then dmplabs { Debug: dump label definitions }

end; (*load*)
* pmd : print memory dump?

(*------------------------------------------------------------------------*)

procedure pmd;
   var s :integer; i: integer;

   procedure pt;
   begin if i = 0 then begin wrthex(s, maxdigh); write(': ') end;
      wrthex(store[s], 2); write(' ');
      s := s - 1;
      i := i + 1;
      if i = 16 then
         begin writeln(output); i := 0 end;
   end; (*pt*)

begin
   if dopmd then begin
      writeln;
      write('pc = '); wrthex(pc-1, maxdigh);
      write(' op = ',op:3);
      write(' sp = '); wrthex(sp, maxdigh);
      write(' mp = '); wrthex(mp, maxdigh);
      write(' np = '); wrthex(np, maxdigh);
      write(' cp = '); wrthex(cp, maxdigh);
      writeln; 
      write('------------------------------------------------------------');
      writeln('-------------');

      writeln;
      writeln('Stack');
      writeln;
      s := sp; i := 0;
      while s>=pctop do pt;
      writeln;
      writeln;
      writeln('Constants');
      writeln;
      s := maxstr; i := 0;
      while s>=cp do pt;
      writeln;
      writeln;
      writeln('Heap');
      writeln;
      s := cp-1; i := 0;
      while s>=np do pt;
      writeln;
   end
end; (*pmd*)

error handling, misc routines

procedure errori(string: beta);
begin writeln; write('*** Runtime error');
      if srclin > 0 then write(' [', srclin:1, ']');
      writeln(': ', string);
      pmd; goto 1
end;(*errori*)

function base(ld :integer):address;
   var ad :address;
begin ad := mp;
   while ld>0 do begin ad := getadr(ad+marksl); ld := ld-1 end;
   base := ad
end; (*base*)

procedure compare;
(*comparing is only correct if result by comparing integers will be*)
begin
  popadr(a2);
  popadr(a1);
  i := 0; b := true;
  while b and (i<>q) do
    if store[a1+i] = store[a2+i] then i := i+1
    else b := false
end; (*compare*)

procedure valfil(fa: address); { attach file to file entry }
var i,ff: integer;
begin
   if store[fa] = 0 then begin { no file }
     if fa = pctop+inputoff then ff := inputfn
     else if fa = pctop+outputoff then ff := outputfn
     else if fa = pctop+prdoff then ff := prdfn
     else if fa = pctop+prroff then ff := prrfn
     else begin
       i := 5; { start search after the header files }
       ff := 0; 
       while i <= maxfil do begin 
         if filstate[i] = fclosed then begin ff := i; i := maxfil end;
         i := i+1 
       end;
       if ff = 0 then errori('To many files            ');
     end;
     store[fa] := ff
   end
end;

procedure valfilwm(fa: address); { validate file write mode }
begin
   valfil(fa); { validate file address }
   if filstate[store[fa]] <> fwrite then errori('File not in write mode   ')
end;

procedure valfilrm(fa: address); { validate file read mode }
begin
   valfil(fa); { validate file address }
   if filstate[store[fa]] <> fread then errori('File not in read mode    ')
end;

procedure getop; { get opcode }

begin

   op := store[pc]; pc := pc+1

end;

procedure getp; { get p parameter }

begin

   p := store[pc]; pc := pc+1

end;

procedure getq; { get q parameter }

begin

   q := getadr(pc); pc := pc+adrsize

end;

procedure getq1; { get q1 parameter }

begin

   q1 := getadr(pc); pc := pc+adrsize

end;

the heap

{

   Blocks in the heap are dead simple. The block begins with a length, including
   the length itself. If the length is positive, the block is free. If negative,
   the block is allocated. This means that AddressOfBLock+abs(lengthOfBlock) is
   address of the next block, and RequestedSize <= LengthOfBLock+adrsize is a
   reasonable test for if a free block fits the requested size, since it will
   never be true of occupied blocks.

}

{ report all space in heap }

procedure repspc;
var l, ad: address;
begin
   writeln;
   writeln('Heap space breakdown');
   writeln;
   ad := np; { index the bottom of heap }
   while ad < cp do begin
      l := getadr(ad); { get next block length }
      write('addr: '); wrthex(ad, maxdigh); write(': ', abs(l):6, ': '); 
      if l >= 0 then writeln('free') else writeln('alloc');
      ad := ad+abs(l)
   end
end;

{ find free block using length }

procedure fndfre(len: address; var blk: address);
var l, b: address;
begin
  b := 0; { set no block found }
  blk := np; { set to bottom of heap active }
  while blk < cp do begin { search blocks in heap }
     l := getadr(blk); { get length }
     if l >= len+adrsize then begin b := blk; blk := cp end { found }
     else blk := blk+abs(l) { go next block }
  end;
  if b > 0 then begin { block was found }
     putadr(b, -(len+adrsize)); { allocate block }
     blk := b+adrsize; { set base address }
     if l > len+adrsize+adrsize+resspc then begin 
        { If there is enough room for the block, header, and another header,
          then a reserve factor if desired. }
        b := b+len+adrsize; { go to top of allocated block }
        putadr(b, l-(len+adrsize)) { set length of stub space }
     end
  end else blk := 0 { set no block found }
end;

{ coalesce space in heap }

procedure cscspc;
var done: boolean;
    ad, ad1, l, l1: address;
begin
   { first, colapse all free blocks at the heap bottom }
   done := false;
   while not done and (np < cp) do begin 
      l := getadr(np); { get header length }
      if l >= 0 then np := np+getadr(np) { free, skip block }
      else done := true { end }
   end;
   { now, walk up and collapse adjacent free blocks }
   ad := np; { index bottom }
   while ad < cp do begin
      l := getadr(ad); { get header length }
      if l >= 0 then begin { free }
         ad1 := ad+l; { index next block }
         if ad1 < cp then begin { not against end }
            l1 := getadr(ad1); { get length next }
            if l1 >=0 then 
               putadr(ad, l+l1) { both blocks are free, combine the blocks }
            else ad := ad+l+abs(l1) { skip both blocks }
         end else ad := ad+l+abs(l1) { skip both blocks }
      end else ad := ad+abs(l) { skip this block }
   end
end;

{ allocate space in heap }

procedure newspc(len: address; var blk: address);
var ad,ad1: address;
begin
  fndfre(len, blk); { try finding an existing free block }
  if blk = 0 then begin { allocate from heap bottom }
     ad := np-(len+adrsize); { find new heap bottom }
     ad1 := ad; { save address }
     alignd(heapal, ad); { align to arena }
     len := len+(ad1-ad); { adjust length upwards for alignment }
     if ad <= ep then errori('store overflow           ');
     np:= ad;
     putadr(ad, -(len+adrsize)); { allocate block }
     blk := ad+adrsize { index start of block }
  end
end; 

{ dispose of space in heap }

procedure dspspc(len, blk: address);
var ad: address;
begin
   len := len; { shut up compiler check }
   if blk = 0 then errori('dispose uninit pointer   ')
   else if blk = nilval then errori('Dispose nil pointer      ')
   else if (blk < np) or (blk >= cp) then errori('bad pointer value        ');
   ad := blk-adrsize; { index header }
   if getadr(ad) >= 0 then errori('block already freed      ');
   if dorecycl and not dochkrpt then begin { obey recycling requests }
      putadr(ad, abs(getadr(ad))); { set block free }
      cscspc { coalesce free space }
   end
end;

{ check pointer indexes free entry }

function isfree(blk: address): boolean;
begin
   isfree := getadr(blk-adrsize) >= 0
end;

system procedure implementation

{ system routine call}

procedure callsp;
   var line: boolean;
       i, j, w, l, f: integer;
       c: char;
       b: boolean;
       ad,ad1: address;
       r: real;
       fn: fileno;

   procedure readi(var f:text; var i: integer);
   begin if eof(f) then errori('End of file              ');
         read(f,i);
   end;(*readi*)

   procedure readr(var f: text; var r: real);
   begin if eof(f) then errori('End of file              ');
         read(f,r);
   end;(*readr*)

   procedure readc(var f: text; var c: char);
   begin if eof(f) then errori('End of file              ');
         read(f,c);
   end;(*readc*)

   procedure writestr(var f: text; ad: address; w: integer; l: integer);
      var i: integer;
   begin (* l and w are numbers of characters *)
         if w>l then for i:=1 to w-l do write(f,' ')
                else l := w;
         for i := 0 to l-1 do write(f, getchr(ad+i))
   end;(*writestr*)

   procedure getfile(var f: text);
   begin if eof(f) then errori('End of file              ');
         get(f);
   end;(*getfile*)

   procedure putfile(var f: text; var ad: address);
   begin f^:= getchr(ad+fileidsize); put(f)
   end;(*putfile*)

begin (*callsp*)
      if q > maxsp then errori('invalid std proc/func    ');
        
      { trace routine executions }
      if dotrcrot then writeln(pc:6, '/', sp:6, '-> ', q:2);
      
      case q of
           0 (*get*): begin popadr(ad); valfil(ad); fn := store[ad];
                           if fn <= prrfn then case fn of
                              inputfn: getfile(input);
                              outputfn: errori('get on output file       ');
                              prdfn: getfile(prd);
                              prrfn: errori('get on prr file          ')
                           end else begin
                                if filstate[fn] <> fread then
                                   errori('File not in read mode    ');
                                getfile(filtable[fn])
                           end
                      end;
           1 (*put*): begin popadr(ad); valfil(ad); fn := store[ad];
                           if fn <= prrfn then case fn of
                              inputfn: errori('put on read file         ');
                              outputfn: putfile(output, ad);
                              prdfn: errori('put on prd file          ');
                              prrfn: putfile(prr, ad)
                           end else begin
                                if filstate[fn] <> fwrite then
                                   errori('File not in write mode   ');
                                putfile(filtable[fn], ad)
                           end
                      end;
           { unused placeholder for "release" }
           2 (*rst*): errori('invalid std proc/func    ');
           3 (*rln*): begin popadr(ad); pshadr(ad); valfil(ad); fn := store[ad];
                           if fn <= prrfn then case fn of
                              inputfn: begin
                                 if eof(input) then errori('End of file              ');
                                 readln(input)
                              end;
                              outputfn: errori('readln on output file    ');
                              prdfn: begin
                                 if eof(prd) then errori('End of file              ');
                                 readln(prd)
                              end;
                              prrfn: errori('readln on prr file       ')
                           end else begin
                                if filstate[fn] <> fread then
                                   errori('File not in read mode    ');
                                if eof(filtable[fn]) then errori('End of file              ');
                                readln(filtable[fn])
                           end
                      end;
           4 (*new*): begin popadr(ad1); newspc(ad1, ad);
                      (*top of stack gives the length in units of storage *)
                            popadr(ad1); putadr(ad1, ad)
                      end;
           5 (*wln*): begin popadr(ad); pshadr(ad); valfil(ad); fn := store[ad];
                           if fn <= prrfn then case fn of
                              inputfn: errori('writeln on input file    ');
                              outputfn: writeln(output);
                              prdfn: errori('writeln on prd file      ');
                              prrfn: writeln(prr)
                           end else begin
                                if filstate[fn] <> fwrite then
                                   errori('File not in write mode   ');
                                writeln(filtable[fn])
                           end
                      end;
           6 (*wrs*): begin popint(l); popint(w); popadr(ad1); 
                           popadr(ad); pshadr(ad); valfil(ad); fn := store[ad];
                           if fn <= prrfn then case fn of
                              inputfn: errori('write on input file      ');
                              outputfn: writestr(output, ad1, w, l);
                              prdfn: errori('write on prd file        ');
                              prrfn: writestr(prr, ad1, w, l)
                           end else begin
                                if filstate[fn] <> fwrite then
                                   errori('File not in write mode   ');
                                writestr(filtable[fn], ad1, w, l)
                           end;
                      end;
           7 (*eln*): begin popadr(ad); valfil(ad); fn := store[ad];
                           if fn <= prrfn then case fn of
                                 inputfn: line:= eoln(input);
                                 outputfn: errori('eoln output file         ');
                                 prdfn: line:=eoln(prd);
                                 prrfn: errori('eoln on prr file         ')
                              end
                           else begin
                                if filstate[fn] <> fread then 
                                   errori('File not in read mode    ');
                                line:=eoln(filtable[fn])
                           end;
                           pshint(ord(line))
                      end;
           8 (*wri*): begin popint(w); popint(i); popadr(ad); pshadr(ad); 
                            valfil(ad); fn := store[ad];
                           if fn <= prrfn then case fn of
                                 inputfn: errori('write on input file      ');
                                 outputfn: write(output, i:w);
                                 prdfn: errori('write on prd file        ');
                                 prrfn: write(prr, i:w)
                              end
                           else begin
                                if filstate[fn] <> fwrite then
                                   errori('File not in write mode   ');
                                write(filtable[fn], i:w)
                           end
                      end;
           9 (*wrr*): begin popint(w); poprel(r); popadr(ad); pshadr(ad); 
                            valfil(ad); fn := store[ad];
                           if fn <= prrfn then case fn of
                                 inputfn: errori('write on input file      ');
                                 outputfn: write(output, r: w);
                                 prdfn: errori('write on prd file        ');
                                 prrfn: write(prr, r:w)
                              end
                           else begin
                                if filstate[fn] <> fwrite then
                                   errori('File not in write mode   ');
                                write(filtable[fn], r:w)
                           end;
                      end;
           10(*wrc*): begin popint(w); popint(i); c := chr(i); popadr(ad); 
                            pshadr(ad); valfil(ad); fn := store[ad];
                           if fn <= prrfn then case fn of
                                 inputfn: errori('write on input file      ');
                                 outputfn: write(output, c:w);
                                 prdfn: errori('write on prd file        ');
                                 prrfn: write(prr, c:w)
                              end
                           else begin
                                if filstate[fn] <> fwrite then
                                   errori('File not in write mode   ');
                                write(filtable[fn], c:w)
                           end
                      end;
           11(*rdi*): begin popadr(ad1); popadr(ad); pshadr(ad); valfil(ad); 
                            fn := store[ad];
                           if fn <= prrfn then case fn of
                                 inputfn: begin readi(input, i); putint(ad1, i) end;
                                 outputfn: errori('read on output file      ');
                                 prdfn: begin readi(prd, i); putint(ad1, i) end;
                                 prrfn: errori('read on prr file         ')
                              end
                           else begin
                                if filstate[fn] <> fread then
                                   errori('File not in read mode    ');
                                readi(filtable[fn], i);
                                putint(ad1, i) 
                           end
                      end;
           12(*rdr*): begin popadr(ad1); popadr(ad); pshadr(ad); valfil(ad); 
                            fn := store[ad];
                           if fn <= prrfn then case fn of
                                 inputfn: begin readr(input, r); putrel(ad1, r) end;
                                 outputfn: errori('read on output file      ');
                                 prdfn: begin readr(prd, r); putrel(ad1, r) end;
                                 prrfn: errori('read on prr file         ')
                              end
                           else begin
                                if filstate[fn] <> fread then
                                   errori('File not in read mode    ');
                                readr(filtable[fn], r);
                                putrel(ad1, r)
                           end
                      end;
           13(*rdc*): begin popadr(ad1); popadr(ad); pshadr(ad); valfil(ad); 
                            fn := store[ad];
                           if fn <= prrfn then case fn of
                                 inputfn: begin readc(input, c); putchr(ad1, c) end;
                                 outputfn: errori('read on output file      ');
                                 prdfn: begin readc(prd, c); putchr(ad1, c) end;
                                 prrfn: errori('read on prr file         ')
                              end
                           else begin
                                if filstate[fn] <> fread then
                                   errori('File not in read mode    ');
                                readc(filtable[fn], c);
                                putchr(ad1, c)
                           end
                      end;
           14(*sin*): begin poprel(r1); pshrel(sin(r1)) end;
           15(*cos*): begin poprel(r1); pshrel(cos(r1)) end;
           16(*exp*): begin poprel(r1); pshrel(exp(r1)) end;
           17(*log*): begin poprel(r1); pshrel(ln(r1)) end;
           18(*sqt*): begin poprel(r1); pshrel(sqrt(r1)) end;
           19(*atn*): begin poprel(r1); pshrel(arctan(r1)) end;
           { placeholder for "mark" }
           20(*sav*): errori('invalid std proc/func    ');
           21(*pag*): begin popadr(ad); valfil(ad); fn := store[ad];
                           if fn <= prrfn then case fn of
                                inputfn: errori('page on read file        ');
                                outputfn: page(output);
                                prdfn: errori('page on prd file         ');
                                prrfn: page(prr)
                              end
                           else begin
                                if filstate[fn] <> fwrite then
                                   errori('File not in write mode   ');
                                page(filtable[fn])
                           end
                      end;
           22(*rsf*): begin popadr(ad); valfil(ad); fn := store[ad];
                           if fn <= prrfn then case fn of
                                inputfn: errori('reset on input file      ');
                                outputfn: errori('reset on output file     ');
                                prdfn: reset(prd);
                                prrfn: errori('reset on prr file        ')
                              end 
                           else begin
                                filstate[fn] := fread;
                                reset(filtable[fn]);
                           end
                      end;
           23(*rwf*): begin popadr(ad); valfil(ad); fn := store[ad];
                           if fn <= prrfn then case fn of
                                inputfn: errori('rewrite on input file    ');
                                outputfn: errori('rewrite on output file   ');
                                prdfn: errori('rewrite on prd file      ');
                                prrfn: rewrite(prr)
                              end 
                           else begin
                                filstate[fn] := fwrite;
                                rewrite(filtable[fn]);
                           end
                      end;
           24(*wrb*): begin popint(w); popint(i); b := i <> 0; popadr(ad); 
                            pshadr(ad); valfil(ad); fn := store[ad];
                           if fn <= prrfn then case fn of
                                 inputfn: errori('write on input file      ');
                                 outputfn: write(output, b:w);
                                 prdfn: errori('write on prd file        ');
                                 prrfn: write(prr, b:w)
                              end 
                           else begin
                                if filstate[fn] <> fwrite then
                                   errori('File not in write mode   ');
                                write(filtable[fn], b:w)
                           end
                      end;
           25(*wrf*): begin popint(f); popint(w); poprel(r); popadr(ad); pshadr(ad);
                            valfil(ad); fn := store[ad];
                           if fn <= prrfn then case fn of
                                 inputfn: errori('write on input file      ');
                                 outputfn: write(output, r:w:f);
                                 prdfn: errori('write on prd file        ');
                                 prrfn: write(prr, r:w:f)
                              end 
                           else begin
                                if filstate[fn] <> fwrite then
                                   errori('File not in write mode   ');
                                write(filtable[fn], r:w:f) 
                           end
                      end;
           26(*dsp*): begin
                           popadr(ad1); popadr(ad); dspspc(ad1, getadr(ad))
                      end;
           27(*wbf*): begin popint(l); popadr(ad1); popadr(ad);
                           valfilwm(ad); fn := store[ad];
                           for i := 1 to l do begin
                              write(bfiltable[fn], store[ad1]);
                              ad1 := ad1+1
                           end
                      end;
           28(*wbi*): begin popint(i); popadr(ad); pshadr(ad); pshint(i);
                            valfilwm(ad); fn := store[ad];
                            for i := 1 to intsize do
                               write(bfiltable[fn], store[sp-intsize+i-1]);
                            popint(i)
                      end;
           29(*wbr*): begin poprel(r); popadr(ad); pshadr(ad); pshrel(r); 
                            valfilwm(ad); fn := store[ad];
                            for i := 1 to realsize do 
                               write(bfiltable[fn], store[sp-realsize+i-1]);
                            poprel(r)
                      end;
           30(*wbc*): begin popint(i); c := chr(i); popadr(ad); pshadr(ad); pshint(i); 
                            valfilwm(ad); fn := store[ad];
                            for i := 1 to charsize do
                               write(bfiltable[fn], store[sp-intsize+i-1]);
                            popint(i)
                      end;
           31(*wbb*): begin popint(i); popadr(ad); pshadr(ad); pshint(i); 
                            valfilwm(ad); fn := store[ad];
                            for i := 1 to boolsize do
                               write(bfiltable[fn], store[sp-intsize+i-1]);
                            popint(i)
                      end;
           32(*rbf*): begin popint(l); popadr(ad1); popadr(ad); pshadr(ad);
                            valfilrm(ad); fn := store[ad];
                            if filbuff[fn] then { buffer data exists }
                            for i := 1 to l do
                              store[ad1+i-1] := store[ad+fileidsize+i-1]
                            else begin
                              if eof(bfiltable[fn]) then errori('End of file              ');
                              for i := 1 to l do begin
                                read(bfiltable[fn], store[ad1]);
                                ad1 := ad1+1
                              end
                            end
                      end;
           33(*rsb*): begin popadr(ad); valfil(ad); fn := store[ad];
                           filstate[fn] := fread;
                           reset(bfiltable[fn]);
                           filbuff[fn] := false
                      end;
           34(*rwb*): begin popadr(ad); valfil(ad); fn := store[ad];
                           filstate[fn] := fwrite;
                           rewrite(bfiltable[fn]);
                           filbuff[fn] := false
                      end;
           35(*gbf*): begin popint(i); popadr(ad); valfilrm(ad); fn := store[ad];
                           if filbuff[fn] then filbuff[fn] := false
                           else
                             for j := 1 to i do
                                read(bfiltable[fn], store[ad+fileidsize+j-1])
                      end;
           36(*pbf*): begin popint(i); popadr(ad); valfilwm(ad); fn := store[ad];
                           for j := 1 to i do
                              write(bfiltable[fn], store[ad+fileidsize+j-1]);
                      end;
      end;(*case q*)
end;(*callsp*)

main code

begin (* main *)

  writeln('P5 Pascal interpreter vs. ', majorver:1, '.', minorver:1);
  writeln;

  { !!! remove this next statement for self compile }
  assign(prr, paramstr(2));
  {elide}rewrite(prr);{noelide}

  writeln('Assembling/loading program');
  load; (* assembles and stores code *)
  pc := 0; sp := pctop; mp := pctop; np := cp; ep := 5; srclin := 0;

  interpreting := true;

  writeln('Running program');
  writeln;
  while interpreting do
  begin

    { fetch instruction from byte store }
    pcs := pc; { save starting pc }
    getop;

    (*execute*)

    { trace executed instructions }
    if dotrcins then begin 

       wrthex(pcs, maxdigh);
       write('/');
       wrthex(sp, maxdigh);
       lstins(pcs);
       writeln

    end;

opcode table

  case op of

        0   (*lodi*): begin getp; getq; pshint(getint(base(p) + q)) end;
        105 (*loda*): begin getp; getq; pshadr(getadr(base(p) + q)) end;
        106 (*lodr*): begin getp; getq; pshrel(getrel(base(p) + q)) end;
        107 (*lods*): begin getp; getq; getset(base(p) + q, s1); pshset(s1) end;
        108 (*lodb*): begin getp; getq; pshint(ord(getbol(base(p) + q))) end;
        109 (*lodc*): begin getp; getq; pshint(ord(getchr(base(p) + q))) end;

        1  (*ldoi*): begin getq; pshint(getint(pctop+q)) end;
        65 (*ldoa*): begin getq; pshadr(getadr(pctop+q)) end;
        66 (*ldor*): begin getq; pshrel(getrel(pctop+q)) end;
        67 (*ldos*): begin getq; getset(pctop+q, s1); pshset(s1) end;
        68 (*ldob*): begin getq; pshint(ord(getbol(pctop+q))) end;
        69 (*ldoc*): begin getq; pshint(ord(getchr(pctop+q))) end;

        2  (*stri*): begin getp; getq; popint(i); putint(base(p)+q, i) end;
        70 (*stra*): begin getp; getq; popadr(ad); putadr(base(p)+q, ad) end;
        71 (*strr*): begin getp; getq; poprel(r1); putrel(base(p)+q, r1) end;
        72 (*strs*): begin getp; getq; popset(s1); putset(base(p)+q, s1) end;
        73 (*strb*): begin getp; getq; popint(i1); b1 := i1 <> 0; 
                           putbol(base(p)+q, b1) end;
        74 (*strc*): begin getp; getq; popint(i1); c1 := chr(i1);
                           putchr(base(p)+q, c1) end;

        3  (*sroi*): begin getq; popint(i); putint(pctop+q, i); end;
        75 (*sroa*): begin getq; popadr(ad); putadr(pctop+q, ad); end;
        76 (*sror*): begin getq; poprel(r1); putrel(pctop+q, r1); end;
        77 (*sros*): begin getq; popset(s1); putset(pctop+q, s1); end;
        78 (*srob*): begin getq; popint(i1); b1 := i1 <> 0; putbol(pctop+q, b1); end;
        79 (*sroc*): begin getq; popint(i1); c1 := chr(i1); putchr(pctop+q, c1); end;

        4 (*lda*): begin getp; getq; pshadr(base(p)+q) end;
        5 (*lao*): begin getq; pshadr(pctop+q) end;

        6  (*stoi*): begin popint(i); popadr(ad); putint(ad, i) end;
        80 (*stoa*): begin popadr(ad1); popadr(ad); putadr(ad, ad1) end;
        81 (*stor*): begin poprel(r1); popadr(ad); putrel(ad, r1) end;
        82 (*stos*): begin popset(s1); popadr(ad); putset(ad, s1) end;
        83 (*stob*): begin popint(i1); b1 := i1 <> 0; popadr(ad); 
                           putbol(ad, b1) end;
        84 (*stoc*): begin popint(i1); c1 := chr(i1); popadr(ad); 
                           putchr(ad, c1) end;

        127 (*ldcc*): begin pshint(ord(getchr(pc))); pc := pc+1 end;
        126 (*ldcb*): begin pshint(ord(getbol(pc))); pc := pc+1 end;
        123 (*ldci*): begin i := getint(pc); pc := pc+intsize; pshint(i) end;
        125 (*ldcn*): pshadr(nilval) (* load nil *) ;
        124 (*ldcr*): begin getq; pshrel(getrel(q)) end;
        7   (*ldc*): begin getq; getset(q, s1); pshset(s1) end;

        9  (*indi*): begin getq; popadr(ad); pshint(getint(ad+q)) end;
        85 (*inda*): begin getq; popadr(ad); ad1 := getadr(ad+q); pshadr(ad1) end;
        86 (*indr*): begin getq; popadr(ad); pshrel(getrel(ad+q)) end;
        87 (*inds*): begin getq; popadr(ad); getset(ad+q, s1); pshset(s1) end;
        88 (*indb*): begin getq; popadr(ad); pshint(ord(getbol(ad+q))) end;
        89 (*indc*): begin getq; popadr(ad); pshint(ord(getchr(ad+q))) end;

        93 (*incb*),
        94 (*incc*),
        10 (*inci*): begin getq; popint(i1); pshint(i1+q) end;
        90 (*inca*): begin getq; popadr(a1); pshadr(a1+q) end;
        91 (*incr*),
        92 (*incs*): errori('Instruction error        ');

        11 (*mst*): begin (*p=level of calling procedure minus level of called
                            procedure + 1;  set dl and sl, increment sp*)
                     (* then length of this element is
                        max(intsize,realsize,boolsize,charsize,ptrsize *)
                     getp;
                     ad := sp; { save mark base }
                     sp := sp+marksize; { allocate mark }
                     putadr(ad+marksl, base(p)); { sl }
                     (* the length of this element is ptrsize *)
                     putadr(ad+markdl, mp); { dl }
                     (* idem *)
                     putadr(ad+markep, ep); { ep }
                     (* idem *)
                    end;

        12 (*cup*): begin (*p=no of locations for parameters, q=entry point*)
                     getp; getq;
                     mp := sp-(p+marksize); { mp to base of mark }
                     putadr(mp+markra, pc); { place ra }
                     pc := q
                    end;

        13 (*ents*): begin getq; ad := mp + q; (*q = length of dataseg*)
                        if ad >= np then errori('store overflow           ');
                        { clear allocated memory }
                        while sp < ad do begin store[sp] := 0; sp := sp+1 end;
                        putadr(mp+marksb, sp) { set bottom of stack }
                     end;

        173 (*ente*): begin getq; ep := sp+q;
                        if ep >= np then errori('store overflow           ');
                        putadr(mp+market, ep) { place current ep }
                      end;
                      (*q = max space required on stack*)

        14  (*retp*): begin
                       sp := mp;
                       pc := getadr(mp+markra); { get ra }
                       ep := getadr(mp+markep); { get old ep }
                       mp := getadr(mp+markdl)  { get dl }
                     end;
        { For characters and booleans, need to clean 8 bit results because
          only the lower 8 bits were stored to. }
        130 (*retc*): begin
                       putint(mp, ord(getchr(mp)));
                       sp := mp+intsize; { set stack above function result }
                       pc := getadr(mp+markra);
                       ep := getadr(mp+markep);
                       mp := getadr(mp+markdl)
                     end;
        131 (*retb*): begin
                       putint(mp, ord(getbol(mp)));
                       sp := mp+intsize; { set stack above function result }
                       pc := getadr(mp+markra);
                       ep := getadr(mp+markep);
                       mp := getadr(mp+markdl)
                     end;
        128 (*reti*): begin
                       sp := mp+intsize; { set stack above function result }
                       pc := getadr(mp+markra);
                       ep := getadr(mp+markep);
                       mp := getadr(mp+markdl)
                     end;
        129 (*retr*): begin
                       sp := mp+realsize; { set stack above function result }
                       pc := getadr(mp+markra);
                       ep := getadr(mp+markep);
                       mp := getadr(mp+markdl)
                     end;
        132  (*reta*): begin
                       sp := mp+adrsize; { set stack above function result }
                       pc := getadr(mp+markra);
                       ep := getadr(mp+markep);
                       mp := getadr(mp+markdl)
                     end;

        15 (*csp*): begin getq; callsp end;

        16 (*ixa*): begin getq; popint(i); popadr(a1); pshadr(q*i+a1) end;

        17  { equa }: begin popadr(a2); popadr(a1); pshint(ord(a1=a2)) end;
        139 { equb },
        141 { equc },
        137 { equi }: begin popint(i2); popint(i1); pshint(ord(i1=i2)) end;
        138 { equr }: begin poprel(r2); poprel(r1); pshint(ord(r1=r2)) end;
        140 { equs }: begin popset(s2); popset(s1); pshint(ord(s1=s2)) end;
        142 { equm }: begin getq; compare; pshint(ord(b)) end;

        18  { neqa }: begin popadr(a2); popadr(a1); pshint(ord(a1<>a2)) end;
        145 { neqb },
        147 { neqc },
        143 { neqi }: begin popint(i2); popint(i1); pshint(ord(i1<>i2)) end;
        144 { neqr }: begin poprel(r2); poprel(r1); pshint(ord(r1<>r2)) end;
        146 { neqs }: begin popset(s2); popset(s1); pshint(ord(s1<>s2)) end;
        148 { neqm }: begin getq; compare; pshint(ord(not b)) end;

        19  { geqa }: errori('<,<=,>,>= for address    ');
        151 { geqb },
        153 { geqc },
        149 { geqi }: begin popint(i2); popint(i1); pshint(ord(i1>=i2)) end;
        150 { geqr }: begin poprel(r2); poprel(r1); pshint(ord(r1>=r2)) end;
        152 { geqs }: begin popset(s2); popset(s1); pshint(ord(s1>=s2)) end;
        154 { geqm }: begin getq; compare; pshint(ord(b or (store[a1+i] >= store[a2+i]))) end;

        20  { grta }: errori('<,<=,>,>= for address    ');
        157 { grtb },
        159 { grtc },
        155 { grti }: begin popint(i2); popint(i1); pshint(ord(i1>i2)) end;
        156 { grtr }: begin poprel(r2); poprel(r1); pshint(ord(r1>r2)) end;
        158 { grts }: errori('set inclusion            ');
        160 { grtm }: begin getq; compare; pshint(ord(not b and (store[a1+i] > store[a2+i]))) end;

        21  { leqa }: errori('<,<=,>,>= for address    ');
        163 { leqb },
        165 { leqc },
        161 { leqi }: begin popint(i2); popint(i1); pshint(ord(i1<=i2)) end;
        162 { leqr }: begin poprel(r2); poprel(r1); pshint(ord(r1<=r2)) end;
        164 { leqs }: begin popset(s2); popset(s1); pshint(ord(s1<=s2)) end;
        166 { leqm }: begin getq; compare; pshint(ord(b or (store[a1+i] <= store[a2+i]))) end;

        22  { lesa }: errori('<,<=,>,>= for address    ');
        169 { lesb },
        171 { lesc },
        167 { lesi }: begin popint(i2); popint(i1); pshint(ord(i1<i2)) end;
        168 { lesr }: begin poprel(r2); poprel(r1); pshint(ord(r1<r2)) end;
        170 { less }: errori('set inclusion            ');
        172 { lesm }: begin getq; compare; pshint(ord(not b and (store[a1+i] < store[a2+i]))) end;

        23 (*ujp*): begin getq; pc := q end;
        24 (*fjp*): begin getq; popint(i); if i = 0 then pc := q end;
        25 (*xjp*): begin getq; popint(i1); pc := i1*ujplen+q end;

        95 (*chka*): begin getq; popadr(a1); pshadr(a1); 
                           {     0 = assign pointer including nil
                             Not 0 = assign pointer from heap address }
                           if a1 = 0 then  
                              { if zero, but not nil, it's never been assigned }
                              errori('uninitialized pointer    ')
                           else if (q <> 0) and (a1 = nilval) then
                              { q <> 0 means deref, and it was nil 
                                (which is not zero) }
                              errori('Dereference of nil ptr   ')
                           else if ((a1 < np) or (a1 >= cp)) and 
                                   (a1 <> nilval) then
                              { outside heap space (which could have 
                                contracted!) }
                              errori('bad pointer value        ')
                           else if dochkrpt and (a1 <> nilval) then begin
                              { perform use of freed space check }
                              if isfree(a1) then
                                 { attempt to dereference or assign a freed 
                                   block }
                                 errori('Ptr used after dispose op')
                           end
                     end;
        96 (*chkr*),
        97 (*chks*): errori('Instruction error        ');
        98 (*chkb*),
        99 (*chkc*),
        26 (*chki*): begin getq; popint(i1); pshint(i1); 
                      if (i1 < getint(q)) or (i1 > getint(q+intsize)) then
                      errori('value out of range       ')
                    end;

        27 (*eof*): begin popadr(ad); valfil(ad); fn := store[ad];
                          if fn <= prrfn then case fn of
                             inputfn: pshint(ord(eof(input)));
                             prdfn: pshint(ord(eof(prd)));
                             outputfn,
                             prrfn: errori('eof test on output file  ')
                          end else begin 
                             if filstate[fn] = fwrite then pshint(ord(true))
                             else if filstate[fn] = fread then 
                                pshint(ord(eof(filtable[fn]) and not filbuff[fn]))
                             else errori('file is not open         ')
                          end
                    end;

        28 (*adi*): begin popint(i2); popint(i1); pshint(i1+i2) end;
        29 (*adr*): begin poprel(r2); poprel(r1); pshrel(r1+r2) end;
        30 (*sbi*): begin popint(i2); popint(i1); pshint(i1-i2) end;
        31 (*sbr*): begin poprel(r2); poprel(r1); pshrel(r1-r2) end;
        32 (*sgs*): begin popint(i1); pshset([i1]); end;
        33 (*flt*): begin popint(i1); pshrel(i1) end;

        { note that flo implies the tos is float as well }
        34 (*flo*): begin poprel(r1); popint(i1); pshrel(i1); pshrel(r1) end;

        35 (*trc*): begin poprel(r1); pshint(trunc(r1)) end;
        36 (*ngi*): begin popint(i1); pshint(-i1) end;
        37 (*ngr*): begin poprel(r1); pshrel(-r1) end;
        38 (*sqi*): begin popint(i1); pshint(sqr(i1)) end;
        39 (*sqr*): begin poprel(r1); pshrel(sqr(r1)) end;
        40 (*abi*): begin popint(i1); pshint(abs(i1)) end;
        41 (*abr*): begin poprel(r1); pshrel(abs(r1)) end;
        42 (*not*): begin popint(i1); b1 := i1 <> 0; pshint(ord(not b1)) end;
        43 (*and*): begin popint(i2); b2 := i2 <> 0; 
                          popint(i1); b1 := i1 <> 0; 
                          pshint(ord(b1 and b2)) end;
        44 (*ior*): begin popint(i2); b2 := i2 <> 0; 
                          popint(i1); b1 := i1 <> 0; 
                          pshint(ord(b1 or b2)) end;
        45 (*dif*): begin popset(s2); popset(s1); pshset(s1-s2) end;
        46 (*int*): begin popset(s2); popset(s1); pshset(s1*s2) end;
        47 (*uni*): begin popset(s2); popset(s1); pshset(s1+s2) end;
        48 (*inn*): begin popset(s1); popint(i1); pshint(ord(i1 in s1)) end;
        49 (*mod*): begin popint(i2); popint(i1); pshint(i1 mod i2) end;
        50 (*odd*): begin popint(i1); pshint(ord(odd(i1))) end;
        51 (*mpi*): begin popint(i2); popint(i1); pshint(i1*i2) end;
        52 (*mpr*): begin poprel(r2); poprel(r1); pshrel(r1*r2) end;
        53 (*dvi*): begin popint(i2); popint(i1); 
                          if i2 = 0 then errori('Zero divide              ');
                          pshint(i1 div i2) end;
        54 (*dvr*): begin poprel(r2); poprel(r1); 
                          if r2 = 0.0 then errori('Zero divide              ');
                          pshrel(r1/r2) end;
        55 (*mov*): begin getq; popint(i2); popint(i1);
                     for i3 := 0 to q-1 do store[i1+i3] := store[i2+i3]
                     (* q is a number of storage units *)
                    end;
        56 (*lca*): begin getq; pshadr(q) end;

        103 (*decb*),
        104 (*decc*),
        57  (*deci*): begin getq; popint(i1); pshint(i1-q) end;
        100 (*deca*),
        101 (*decr*),
        102 (*decs*): errori('Instruction error        ');

        58 (*stp*): interpreting := false;

        134 (*ordb*),
        136 (*ordc*),
        59  (*ordi*): ; { ord is a no-op }
        133 (*ordr*),
        135 (*ords*): errori('Instruction error        ');

        60 (*chr*): ; { chr is a no-op }

        61 (*ujc*): errori('case - error             ');
        62 (*rnd*): begin poprel(r1); pshint(round(r1)) end;

        63 (*pck*): begin getq; getq1; popadr(a3); popadr(a2); popadr(a1);
                     if a2+q > q1 then errori('pack elements out of bnds');
                     for i4 := 0 to q-1 do begin
                        store[a3+i4] := store[a1+a2];
                        a2 := a2+1
                     end
                   end;
        64 (*upk*): begin getq; getq1; popadr(a3); popadr(a2); popadr(a1);
                     if a3+q > q1 then errori('unpack elem out of bnds  ');
                     for i4 := 0 to q-1 do begin
                        store[a2+a3] := store[a1+i4];
                        a3 := a3+1
                     end
                   end;

        110 (*rgs*): begin popint(i2); popint(i1); pshset([i1..i2]) end;
        111 (*fbv*): begin popadr(ad); pshadr(ad); valfil(ad); fn := store[ad];
                     if fn = inputfn then putchr(ad+fileidsize, input^)
                     else if fn = prdfn then putchr(ad+fileidsize, prd^)
                     else begin
                       if filstate[fn] = fread then
                         putchr(ad+fileidsize, filtable[fn]^)
                     end
                   end;
        112 (*ipj*): begin getp; getq; pc := q;
                     mp := base(p); { index the mark to restore }
                     { restore marks until we reach the destination level }
                     sp := getadr(mp+marksb); { get the stack bottom }
                     ep := getadr(mp+market) { get the mark ep }
                   end;
        113 (*cip*): begin getp; popadr(ad);
                    mp := sp-(p+marksize);
                    { replace next link mp with the one for the target }
                    putadr(mp+marksl, getadr(ad));
                    putadr(mp+markra, pc);
                    pc := getadr(ad+1*ptrsize)
                  end;
        114 (*lpa*): begin getp; getq; { place procedure address on stack }
                    pshadr(base(p));
                    pshadr(q)
                  end;
        115 (*efb*): begin
                    popadr(ad); pshadr(ad); valfilrm(ad); fn := store[ad];
                    { eof is file eof, and buffer not full }
                    pshint(ord(eof(bfiltable[fn]) and not filbuff[fn]))
                   end;
        116 (*fvb*): begin popint(i); popadr(ad); pshadr(ad); valfil(ad);
                    fn := store[ad];
                    { load buffer only if in read mode, and buffer is empty }
                    if (filstate[fn] = fread) and not filbuff[fn] then begin 
                      for j := 1 to i do
                        read(bfiltable[fn], store[ad+fileidsize+j-1]);
                      filbuff[fn] := true
                    end
                  end;
        117 (*dmp*): begin getq; sp := sp-q end; { remove top of stack }

        118 (*swp*): begin getq; swpstk(q) end;

        119 (*tjp*): begin getq; popint(i); if i <> 0 then pc := q end;

        120 (*lip*): begin getp; getq; ad := base(p) + q;
                      i := getadr(ad); a1 := getadr(ad+1*ptrsize);
                      pshadr(i); pshadr(a1)
                    end;

        174 (*mrkl*): begin getq; srclin := q; 
                            if dotrcsrc then 
                              writeln('Source line executed: ', q:1)
                      end;

        { illegal instructions }
        8,   121, 122, 175, 176, 177, 178,
        180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
        190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
        200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
        210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
        220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
        230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
        240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
        250, 251, 252, 253, 254, 
        255: errori('illegal instruction      ');

  end
end; (*while interpreting*)

end of program

  { perform heap dump if requested }
  if dodmpspc then repspc;

  1 : { abort run }

  writeln;
  writeln('program complete');

end.