This file is here only for inspection to work out what the
intermediate codes mean.  It is not being developed.

include "Sysinc:com.inc"
!   **************************************************************
!   *                                                            *
!   *          PERKIN-ELMER 32-bit series IMP compiler           *
!   *               Intermediate-code Assembler                  *
!   *                                                            *
!   *                 (General Service Version)                  *
!   *                                                            *
!   *          Interactive Datasystems (Edinburgh) Ltd.          *
!   *                 32, Upper Gilmore Place,                   *
!   *                    Edinburgh  EH3 9NJ                      *
!   *                                                            *
!   *                 Copyright (c) MAY 1st. 1980                *
!   *                     All Rights Reserved                    *
!   *                                                            *
!   **************************************************************





! Known faults:

! Outstanding:
!      %longreal is currently treated as %real
!      statically sized arrays within stack frame
!
! Optimisations:  (intended complete list)
!      integer constant folding                          *done*
!      real folding: integer/integer, integer^integer    *done*
!      literals in shareable code segment                *done*
!      special treatment of null string                  *done*
!      special treatment of simple append (S = S.T)      *done*
!      special treatment of S=S.tostring(x)              *done*
!      suppression of redundant capacity checks          *done*
!      suppression of redundant unassigned checks        *done*
!      register usage                                    *done*
!      register environments                             *done*
!      k*2 -> k+k, k^2 -> k*k, k^^2 -> k*k               *done*
!      Pass last param = string or record %value as %name then
!         copy within routine *done* (for strings only)
!      Detect and omit redundant array bound checks

!                   ABORT CODES
!                   ===========
! code    routine             reason
!
! ?? x:   assemble:  faulty intermediate code operation 'x'

! ADMP:   adump:  constant record ?
! AM00:   assemble:  static block nesting > 5 levels
! AM01:   assemble:  ('A') unknown constant type
! AM05:   assemble:  ('u', 'q')  unspecified length in ++, --
! AM10:   assemble:  ('_')  switch label outwith declared vector
! AM15:   assemble:  ('B')  intermediate code faulty at %repeat tag
! AM25:   assemble:  ('F')  user label out of range
! AM30:   assemble:  (':')  user label out of range
! AM35:   assemble:  ('d')  wrong no. of dimensions specified
! AM40:   assemble:  ('d')  %const/%own array inside out
! AM45:   assemble:  ('~')  faulty intermediate code in alternate record format

! AM50:   assemble:  ('}')  symbol table overflow (inserting formal parameter specs)
! AM55:   assemble:  ('}') (OUT:)  %record format > 64k bytes
! AMAP:   amap:  impossible form
! ARF1:   array ref:  no. of subscripts doesn't match declaration
! ASS1:   assign:  not at least two items on operand stack
! ASS2:   assign:  general %name not a %name
! ASS3:   assign:  record length undefined in 'record = record'
! CLM1:   claim:  reg > fr14
! CLMD:   assemble:  ('O')  registers still claimed at line flag
! COP1:   cop:  exponent overflow in folding integer^integer
! COP2:   cop:  inappropriate operator
! DMP1:   select literal area:  literal area already selected
! DMP2:   select code area:  code area already selected
! DMP3:   lit byte:  literal area not selected
! DMP4:   claim literal:  literal area currently selected
! DMP5:   external link:  non-existent reference type
! DROP:   drop:  descriptor not in use
! DSC1:   descriptor:  operand stack overflow
! DSC2:   descriptor:  descriptor free-list empty
! DSC3:   descriptor:  link-block ('using') free-list empty
! DFV1:   define var:  symbol table overflow (inserting record element name)
! DFV2:   define var:  symbol table overflow (inserting non-format item)
! FOR1:   compile for:  too many nested %for...%cycle .....%repeat pairs
! HAZ1:   hazard:  attempt to hazard a constant
! HAZ2:   hazard:   a use is still outstanding
! HDR1:   header:  %string parameter in %begin ?
!  LD1:   load:  ADDRESS failed to simplify non-trivial address mode
!  LD2:   load:  real variable/integer register
!  LD3:   load:  inappropriate type
!  LD4:   load:  not/neg implemented in operate
!  LD5:   load:  load floating variable into 'any' ?
!  LD6:   load:  real operand with and/or/xor
!  LD7:   load:  real 'neg' implemented in operate
!  LD8:   load:  not a floating register
!  LD9:   load:  real exponent ?
! LIT?:   assemble:  ('O')  literal area still selected at line flag
! NLBL:   new label:  no free labels
! PICK:   pickup (in LOAD) incompatible uses of a register
! POPL:   pop lhs:  operand stack is already empty
! REL1:   release:  reg > fr14
! REL2:   release:  reg not claimed
! RXD1:   rxd:  no immediate form of instruction
! RXD2:   rxd:  faulty register specification
! RXD3:   rxd:  faulty register specification
! RXD4:   rxd:  non-elementary operand type supplied to 'RXD'
! RXD5:   rxd:  displacement not aligned on 'type' boundary
! SETB:   set both:  not at least two items on operand stack
! STK?:   assemble:  ('O')  operand stack not empty at line flag.
! TAG?:   block mark:  more than 32767 third pass tags generated. See c('_'):
! USNG:   assemble:  ('O')  'using' list not empty at line flag.
! VMAP:   vmap:  impossible form
! VSTK:   vstack:  variable no (symbol table index) out of bounds







! ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  == 
! Known Faults:
!      READ SYMBOL is not implemented properly
!      external linkage dumping is too indiscriminate
!      general name parameter types don't match old subsystem
! ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  ==  == 
!*****************************************************************************
!
! Options:     (enabled when control bit = 1)
!
!       1: Capacity check on all store operations
!          Overflow check on integer multiply
!       2: Unassigned check on %string, %integer, %real & %longreal operands
!       4: Array bound checking
!          Checks for integrity of %for construction
!      16: Assorted extra checks:
!                              complete arithmetic overflow checking (*not yet*)
!      32: Permit removal of ALL diagnostic code and optimisations which are
!          not 100% safe.
!               Diagnostic code removed: 
!                                    Unassigned check on P in R string parameter
!                                    Stack limit check
!               Risky optimisations:
!                                    Remembering pointers over an assignment via
!                                    another pointer.  Aliasing might JUST occur.
!      64: Enable trace option
!     128: No register optimisation: primarily for suppressing compiler faults
!
! NOTE:
!          Switch references are always checked
!          Stack overflow is checked unless 'TRUSTED' is specified.
!
!******************************************************************************
!         N.B.  The bit positions in CONTROL corresponding to 256 and 512
!               are reserved to control the dumping of diagnostic tables
!               by PASS 3.
!
!               OPT is set (implicitly) by disabling all explicitly settable
!               checks (bits 1,2,4,8,16)
!
!               TRUSTED disables all checks and also sets the '32' bit.
!******************************************************************************





begin;             !    7/32 DIAGNOSTIC ASSEMBLER
!SIZE CONSTANTS
constinteger  max vars = 800
constinteger  max labels = 80
constinteger  max depth = 16
constinteger  max stack = 25
constinteger  max labdef = 7999
constinteger  max refs = 2000
constinteger SetLen = 32           {bytes per set}
constinteger  max prim = 23
constinteger  max cycle = 30
constinteger  max temps = 60
constinteger max use = 20;               !limit for klist
constinteger max envirs = 5;             !Environments
constinteger max knowledge = max use*(max envirs+1)
conststring(3)  program ep = "%GO";      ! Main program external name
conststring(1)  system prefix = "$";     ! prefixed to %system routine idents
conststring(6)  trace routine = "$TRACE";    ! external called by trace option
conststring(10)  read sym fn = "#READSYMFN";    ! linkage name of "read symbol" perm
constinteger    ident len = 19;           ! Significant chars in internal idents
constinteger    extern len = 12;         ! Max. length of names in diags/link

!Input/output streams
constinteger  in=1
constinteger  report=0, direct=1, object=2


! Language mask bits (generally =0 for 'obvious' or IMP interpretation)
! Note that each bit controls compilation of a particular source level
! abstraction and each first pass can select any convenient combination
! of options.
constinteger    UNUSED            = 1,      {currently not used - IMP pass1!!}
               non IMP for         = 2    {exit on >= final value (zero trip)}

!CONTROL BITS
constinteger  check capacity=1
constinteger  check unass=2
constinteger  check array=4,  check for = 4
constinteger  check extra=16
constinteger  trusted=32
constinteger  trace=64
constinteger  suppress=128
constinteger  check bits = check capacity+check unass+check array+check extra

constinteger  bit15 = -32768;           !  halfword sign-bit

!SPECIAL ADDRESSES
constinteger  unass = 12;            ! unassigned pattern at unass(code)
constinteger  init gla = 12;         ! first usable displacement into gla
constinteger  init lit = 8;         ! first literal ends at  -INITLIT(CODE)

! Derived constants
constinteger  for lab base = 8000;    ! = MAX LABDEF+1


!REGISTERS
constinteger  R0 = 1
constinteger  R1 = 2;      ! Fn/map result, @final string result
constinteger            p3 = R1;     ! SPECIAL STRING PARAMETER
constinteger  R2 = 3
constinteger  R3 = 4
constinteger  R4 = 5
constinteger  R5 = 6,            p2 = R5
constinteger  R6 = 7,            p1 = R6
constinteger  R7 = 8,            wsp = R7
constinteger  R8 = 9,    base1 = R8
constinteger  R9 = 10,   base2 = R9
constinteger  R10 = 11,  base3 = R10
constinteger  R11 = 12,  base4 = R11
constinteger  R12 = 13,  base5 = R12;    ! (unassigned pattern for levels 1:4)
constinteger  R13 = 14,            gla = R13
constinteger  R14 = 15,            code = R14
constinteger  R15 = 16,            link = R15
constinteger  FR0 = 17;        ! FN RESULT
constinteger  FR2 = 18
constinteger  FR4 = 19
constinteger  FR6 = 20
constinteger  FR8 = 21
constinteger  FR10 = 22
constinteger  FR12 = 23
constinteger  FR14 = 24
!PSEUDO REGISTERS
constinteger  any = 25
constinteger  anyf = 26
constbyteintegerarray  actual(0:fr14) = 0,
      0,   1,   2,   3,   4,   5,   6,    7,
!     R0   R1   R2   R3   R4   P2   P1   WSP

      8,   9,  10,  11,  12,  13,   14,   15,
!     R8   R9  R10  R11  R12  GLA  CODE  LINK

      0,   2,   4,   6,   8,   10,   12,   14
!    FR0  FR2  FR4  FR6  FR8  FR10  FR12  FR14
constbyteintegerarray  breg(-1:5) =
      0,  0,  base1,  base2,  base3,  base4,  base5
!DATA FORMS
       ! EXTERNAL
constinteger  recordformat = 4
constinteger  switch = 6
constinteger  array = 11
constinteger  arrayname = 12
constinteger  namearray = 13
constinteger  namearrayname = 14
      ! INTERNAL
constinteger  constant = 0
constinteger  v in r = 1
constinteger  av in r = 2
constinteger  a in r = 3
constinteger  v in s = 4
constinteger  av in s = 5
constinteger  a in s = 6
constinteger  v in rec = 7
constinteger  av in rec = 8
constinteger  a in rec = 9
constinteger  pgm label = 14
!!N.B. FORM=15 denotes %record format
! Flag bits used in conjunction with form:
constinteger  quick conc   = 1,      {optimise:  S = S.tostring(symbol) }
               P in R       = 2,      {parameter-in-register}
               prim bit     = 4,      {primitive known to compiler}
               assigned     = 8,      {assigned and known to be}
               proc bit     = 16,     {routine/fn/map/predicate}
               abit         = 32,     {array by value}
               anbit        = 64,     {array by name}
               label bit    = 128     {data is an address}
constinteger  array bits = abit ! anbit;       ! for convenience

! All arrays are in fact treated as by name (i.e. with a dope vector) and
! the ABIT bit is misused to indicate that an array is a candidate for
! subscript scaling by the use of 'multiply halfword'
constinteger  cheap array bit = abit

! 'FLAG' byte of 'xform':

      !=======================================================================!
      !  label      AN      A   proc   assigned     prim   P in R    ------   !
      !   bit       bit    bit   bit                bit                       !
      ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - !
      !   128       64     32    16        8         4       2         1      !
      !=======================================================================!
!                                              :                              !
!                                              :    D I M E N S I O N S       !
!                                              :       (if %array)            !
!                                              :______________________________!
! N.B.
!      In 'varfm' the 4,2,1 bits are used to hold the number of dimensions
!      when the table entry represents an array object.  This field is
!      unpacked into the 'DIM' field of 'stackfm' by 'VSTACK'

!LABEL CONSTANTS
constinteger  define new = 0
constinteger  redefine old = 1
constinteger  internal tag = -1;    ! N.B. This must be <0 and others >=0
!DATA TYPES
constinteger  integers = 1
constinteger  short = 2
constinteger  byte = 3
constinteger  general = 4
constinteger  strings = 5
constinteger  records = 6
constinteger  reals = 7
constinteger  reall = 7;    ! (SET TO 8 FOR LONG REALS)
!Figurative data types used internally (reduce to INTEGERS)
!!  %constinteger  pointer = -1
!!  %constinteger  in store const = -2
!LENGTHS
constinteger  single=4;      ! bytes in single precision %real
constinteger  double = single*(reall-reals+1)
constinteger  align = 3;       ! Basic alignment mask
constinteger  reglen=4;            !  no. of bytes in GP register
constinteger  basic frame = (link-p2+1)*reglen
!OWN INFO
constinteger  own = 1
constinteger  con = 2
constinteger  external = 3
constinteger  system = 4
constinteger  dynamic = 5
constinteger  primrt = 6
constinteger  permrt = 7


! Constants used to define sizes of various objects known to pass 3
constinteger  short ident = 6;    ! characters: related to 'extern len' !!
constinteger  basic vdiag = 4;    ! halfwords:  no. req'd for 'short ident'
constinteger  extra vdiag = 2;    ! halfwords:  basic+extra == extern len




! Define type codes known externally (to pass 3 and user):
constbyteintegerarray  gen map(integers:reals+1) =
      1,      6,      5,      0,      3,      4,      2,      8
! integer  short   byte   general  string  record  reals   reall

!PERM ROUTINES
!  ** UNASSIGNED CHECK as a special at 0(code) **
constinteger  asschk=1

constinteger  iexp=2
constinteger  fexp=3;      ! floating exponent
constinteger  smove=4
constinteger  sjam=5
constinteger  sconc=6
constinteger  sresln=7;    !  conditional resolution
constinteger  sresv=8;     !  check SRESLN succeeded
constinteger  scomp=9
constinteger  frac part = 10;   !  IMP 'frac pt' function
constinteger  sfcap=11;    !  string capacity exceeded
constinteger  substr = 12;     !  substring
constinteger  aref1=13;    !  1-D with checks
constinteger  aref2=14;    !  2-D with checks
constinteger  aref3=15;    !  n-D with checks
constinteger  aref4=16;    !  2-D without checks
constinteger  set dv=17;   !  set dope vector
constinteger  alloc=18;     !  claim array space
constinteger  swjump=19
constinteger  signal=20
constinteger  mulchk=21;    ! check for 32-bit result from integer multiply
constinteger  cap16=22;    !  check for 16-bit signed overflow
constinteger  cap8=23;     !    . . .    8-bit unsigned   . .
constinteger  fchk1=24;    !  %for loop parameter check
constinteger  fchk2=25;    !  check for %for loop counter fiddling
constinteger  pentc=26;     !  checked procedure entry
constinteger  rcopy=27;    !  record copy
constinteger  rzero=28;    !  clear record
constinteger  vschk=29;     !  variable shift parameter check
constinteger  smovopt=30;    ! fast unchecked string move (also see P in R string)
constinteger  chmap=31;    !  IMP 'charno' %map
constinteger  freesp=32;    !  IMP 'free space' function
constinteger  int fn=33;    !  IMP 'int' function
constinteger  rcomp = 34     {record compare}
constinteger set comp = 35,
              set union = 36,
              set difference = 37,
              set intersection = 38

constbytearray Set ops(1:3) = Set union, Set Difference, Set intersection

constinteger  iocp = 49
constinteger  enter trace = 50

!OPERATIONS
!            logical   => both <=  code generator
constinteger  not = 1,             lw = 1
constinteger  neg = 2,             st = 2
constinteger            add = 3
constinteger            sub = 4
constinteger            mul = 5
constinteger            div = 6
constinteger  conc = 7,            cmp = 7
constinteger            and = 8
constinteger            or  = 9
constinteger            xor = 10
constinteger            lsh = 11
constinteger            rsh = 12
constinteger           mult16 = 13
constinteger  rem = 14
constinteger  exp = 15
constinteger  rexp = 16
constinteger  rdiv = 17

!CODE GENERATOR TABLES
!  mask bits in 'op index' array
constinteger  fw imm=2048, hw imm=4096, sf imm=8192, inv imm=16384
constinteger  fw rr=256, fw rx=512, short rx=1024, byte rx=32768
constinteger  fp base = rem-1
constshortintegerarray op index(1:40) =
      x'FF01',;  ! LW: load - 1 + all formats
      x'8609',;  ! ST: store - 9 + full/half word + byte
      x'7F11',;  ! ADD: add  - 17 + all but byte
      x'7F18',;  ! SUB: subtract - 24 + all but byte
      x'031F',;  ! MUL: multiply - 31 + full word formats
      x'0321',;  ! DIV: divide - 33 + full word formats
      x'1F23',;  ! CMP: compare - 35 + all but byte and short immediate
      x'1F28',;  ! AND: and - 40 +              .....
      x'1F2D',;  ! OR:  or - 45 +              ......
      x'1F32',;  ! XOR: xor - 52 +            ........
      x'3037',;  ! LSH: left shift - 55 + halfword and shortform
      x'303D',;  ! RSH: right shift - 61 + halfword and shortform
      x'0775',;  ! MULT16: 117 + rr + rx + short rx
! ** floating point formats **
      x'0F43',;  ! LW: load - single and double
      x'0A47',;  ! ST: store - store reference formats only
      x'0F4B',;  ! ADD: add - 75 + all formats
      x'0F4F',;  ! SUB: subtract - 79 + ...
      x'0F53',;  ! MUL: multiply - 83 + ...
      x'0F57',;  ! DIV: divide - 87 + ...
      x'0F5B',;  ! CMP: compare - 91 + ...
! ** specials **
   x'0364', ;                 ! JMP
   x'0366', ;                 ! BAL
   x'0267', ;                 ! LA
   x'0268', ;                 ! LM
   x'0269', ;                 ! STM
   x'016B', ;                 ! FLR
   x'016C', ;                 ! FXR
   x'1F5F', ;                 ! CLW
   x'1069', ;                 ! SRA
   x'016E', ;                 ! CHVR
   x'016F', ;                 ! LBR
   x'040A', ;                 ! STH
   x'026F', ;                 ! SVC
   x'0270', ;                 ! LME
   x'0271', ;                 ! STME
   x'1870', ;                 ! TEST  (TI/THI)
   x'0476', ;                 ! LHL (short rx only)
   x'0678', ;                 ! AM (fullword and short rx only)
   x'027A', ;                 ! TBT
   x'027B'  ;                 ! SBT


! Each halfword below is treated as 4 groups of 4 bits  [a:b:c:d] with the
! following significance.
! a,b:  8-bit machine op-code
! c:    special function bits
!                     1:  this operation sets condition code other than relative
!                                                                     to zero.
!                     2:  invert order of operands to provide for example
!                               STR x,y => LR y,x
!                     4:  this operation doesn't affect condition code
! d:    mask to check alignment of displacement required by this instruction.

constshortintegerarray  op code(1:124) =
x'0800', x'5803', x'4801', x'F800', x'C800', x'2400', x'2500', x'D350',
!   LR       L        LH       LI      LHI      LIS      LCS      LB
x'0820', x'5053', x'4051',     0,       0,       0,       0,   x'D250',
!   STR      ST      STH       --       --       --       --      STB
x'0A00', x'5A03', x'4A01', x'FA00', x'CA00', x'2600', x'2700',
!   AR       A        AH       AI      AHI      AIS      SIS
x'0B00', x'5B03', x'4B01', x'FB00', x'CB00', x'2700', x'2600',
!   SR       S        SH       SI      SHI      SIS      AIS
x'1C50', x'5C53',
!   MR       M
x'1D50', x'5D53',
!   DR       D
x'0910', x'5913', x'4911', x'F910', x'C910',
!   CR       C        CH       CI      CHI
x'0400', x'5403', x'4401', x'F400', x'C400',
!   NR       N        NH       NI      NHI    
x'0600', x'5603', x'4601', x'F600', x'C600',
!   OR       O        OH       OI      OHI
x'0700', x'5703', x'4701', x'F700', x'C700',
!   XR       X        XH       XI      XHI
    0,       0,       0,       0,   x'ED00',  x'1100',
!   --       --       --       --      SLL      SLLS   
    0,       0,       0,       0,   x'EC00',  x'1000',
!   --       --       --       --      SRL      SRLS   
x'2800', x'6803', x'3800', x'7803',
!  LER       LE      LDR       LD
x'2820', x'6053',     0,   x'7053',
! STER      STE      --       STD
x'2A00', x'6A03', x'3A00', x'7A03',
!  AER       AE      ADR       AD
x'2B00', x'6B03', x'3B00', x'7B03',
!  SER       SE      SDR       SD
x'2C00', x'6C03', x'3C00', x'7C03',
!  MER       ME      MDR       MD
x'2D00', x'6D03', x'3D00', x'7D03',
!  DER       DE      DDR       DD
x'2910', x'6913', x'3910', x'7913',
!  CER       CE      CDR       CD 

! Special purpose entries.
x'0510', x'5513', x'4511', x'F510', x'C510',
!  CLR      CL      CLH      CLI      CLHI
x'0300', x'4300',
! BFCR     BFC
x'0100', x'4100',
! BALR     BAL
         x'E650',
!           LA   
         x'D150',
!           LM
         x'D050',
!          STM
x'2F00',
! FLR
x'2E00',
! FXR
                            x'EE10',
!                             SRA
x'1200',
! CHVR  
x'9350',
! LBR
         x'E110',
!          SVC
         x'7250',
!          LME
         x'7150',
!          STME
                           x'F310', x'C310',
!                            TI       THI
x'0C50', x'4C51', x'4C51',
! MHR     M(H)      MH
                  x'7301',
!                   LHL
         x'5113', x'6111',
!          AM      AHM
         x'7401', x'7501'
!          TBT      SBT

!Non-uniform operations for special situations
constinteger  jmp=21, always=r0;    !  RR(JMP,always,LINK)
constinteger  bal=22;       ! Branch-and-link
constinteger  la =23;       ! Load Address (RX format)
constinteger  lm=24, stm=25;        ! Load/Store Multiple
!   ** FLR below is really 25-fpbase **
constinteger  flr=26-fpbase, fxr=27;       ! Float/Fix (RR format only)
constinteger  clw=28;       ! Compare Logical  (same formats as AND)
constinteger  sra=29;       ! Shift Right Arithmetic (HW IMM only)
constinteger  chvr=30;      ! Convert to halfword value (RR only)
constinteger  lbr=31;       ! Load Byte Register (RR only)
constinteger  sth=32;       ! Store half-word (Short RX only)
constinteger  svc=33;       ! Supervisor call (RX format)
constinteger  lme=34-fpbase;       ! Used in conjunction with SVC
constinteger  stme=35-fpbase;      !     .... ditto ....
constinteger  test=36;      !  test halfword? immediate
constinteger  LHL=37;       ! load unsigned halfword (for switch)
constinteger  AM=38;         ! add-to-memory (see ASSIGN)
constinteger TBT = 39, SBT = 40


constbytearray Inverted(16:21) =
      0,  1,  3,  2,  5,  4
   {  =   #   <   >   <=  >= }

!ASSORTED FUNNY CONSTANTS
constinteger  jump=12;    ! logical condition code == unconditional jump
constinteger  not equal=1;  !       . . .             branch not equal
constinteger  less than=2,     greater than=3
constinteger  less or equal=4, greater or equal=5

!CODES USED IN OUTPUT FOR 3RD. PASS
constinteger  tag def = 1
constinteger  r ref = 2;    ! Routine/fn/map/predicate reference
constinteger  p ref = 3;    ! Prim reference
constinteger  sw ref = 4;   ! Switch reference
constinteger  j ref = 5;    ! Jump reference
constinteger  c ref = 6;    ! Conditional (jump) reference
constinteger  code item = 7
constinteger  gla  item = 8
constinteger  line flag = 9
constinteger  line reset = 10
constinteger  var diag = 11
constinteger  code area  = 12
constinteger  lit area  = 13
constinteger  lit  org  = 14
constinteger  frame patch = 15
constinteger  block start = 16
constinteger  block end = 17
constinteger  prog end = 18
constinteger  c rel    = 19
constinteger  g rel    = 20
constinteger  extern   = 21
!          (external references)
constinteger  data ref = 4,  data defn = 5
constinteger   ep  ref = 6,   ep  defn = 7


recordformat  varfm(integer  disp, c
                     shortinteger  format,extra,length,header,  c
                     (shortinteger  xform or byteinteger  flag,form),
                     byteinteger   base,type)
record(varfm)array  var(0:max vars)
record(varfm)name   decvar
record(varfm)name   fp, ap
ownrecord(varfm)     begin = 0

recordformat  stackfm(integer  disp,
                     shortinteger  format, extra, length, header, rt,
                     shortinteger  var no, type,
                     (shortinteger  xform or byteinteger flag,form),
                     (short xbase or byte index, base),
                     byte dim, oper,
                     record(stackfm)name  link)
record(stackfm)array  stak(0:max stack)
record(stackfm)name   desc asl

recordformat sptfm(record(stackfm)name  v)
record(sptfm)array  stacked(1:max depth)

! elements of USING list
recordformat  dfm(record(stackfm)name  d,  record(dfm)name  link)
record(dfm)array  dlist(0:max stack)
record(dfm)name   dasl
record(dfm)        using

! for compiling %for/%repeat pairs
recordformat  cyclefm(integer  cv disp, fv disp,  c
                       shortinteger  lab, shadow, initial, cv form, c
                       byteinteger   reg, cv type, cv base,  c
                                      fv base, temp base)
record(cyclefm)array  for stk(0:max cycle)
record(cyclefm)name   for;         !  points to currently active level
owninteger  for stp=0

! mechanism to minimise no of temporaries allocated
shortintegerarray  temps(1:max temps)
owninteger  temp base = 0, next temp = 0,  new temp = 0

! list terminator
constrecord(*)name  null == (0)

recordformat  labelfm(shortinteger  id, tag)
record(labelfm)array  labels(1:max labels)

ownintegerarray  activity(0:fr14) = 0(*)
owninteger  claimed = 0


ownshortinteger  control = check bits & (¬check extra)
ownshortinteger  diagnose = 0
      !   1:  trace calls on descriptor stack handling primitives
      !   2:  . . . . . . . . . LOAD
      !   4:  . . . . . . . . . ASSIGN
      !   8:  . . . . . . . optimisation routines and display generated code
      !  16:  dump 'knowledge' list every time CHEAPEN is called with '8' bit on

owninteger  level = -1
owninteger  main ep = 0;              ! non-zero if compiling main program
owninteger  unassigned rtn = 0;       ! non-zero if unassigned check routine pr in

integer     j,k,len,n,val,aparm,opr
owninteger  ca = 0;                ! CODE ADDRESS
owninteger  ga = init gla;         ! GLA ADDRESS
owninteger  lita = 0;    ! Literal address: current address
owninteger  litmax=0;    !                : limit of area claimed so far
owninteger  diag1 = 0;             ! DIAG TABLES 1
owninteger  diag2 = 0;             ! DIAG TABLES 2
owninteger  cc ca=0, cc reg=0;     ! to remember condition code

integer     sym, next;             ! CODE SYMBOL, NEXT SYMBOL
integer     vlb,vub;               ! VECTOR LOWER/UPPER BOUND
integer     Allocate;               ! Flag for array(#0) or arrayformat (=0)
integer     Falign                 {alignment of internal formats}
owninteger  current line = 0;      ! SOURCE LINE NUMBER
owninteger  last line = 0
owninteger  stp = 0;               ! STACK POINTER
integer     data size;             ! CURRENT DATA ITEM SIZE
owninteger  frame = 0;             ! LOCAL STACK FRAME EXTENT
owninteger  extra frame = 0;       ! ALLOW EXTRA FRAME FOR STATIC ARRAYS
integer     parms;                 ! START OF PARAMETER STACK
integer     local;                 ! LOCAL BASE REGISTER
owninteger  invert = 0, swopped = 0;    ! CONDITION INVERSION FLAGS
owninteger  uncond jump = 0;       ! ADDRESS OF CODE HOLE
owninteger  gtype = 0;             ! 0=RECORDS, 1=PROCEDURE
owninteger  gmode = 0;             ! NON-ZERO INSIDE PARAMETER LISTS
integer     decl;                  ! LAST-DEFINED DESCRIPTOR
ownshortinteger  language mask = 0;    ! selects language specific options
integer cheap reg;                 !Preferred register after ADDRESS
integer  otype, owntype, ownform, spec, frozen, potype
integer  diag type=0, diag form=0, diag size=0;    ! external form/type/size 
longreal    rvalue
!! Initialised to suppress critical unassigned check when compiling itself
owninteger  ownval = 0, mantissa = 0;   ! *order critical*
integer  oarea
integer  dim,dv
integer     wdisp, pdisp, gdisp

owninteger  block no = 0;        ! Ordered by block head
owninteger  defns=0, specs=0, relocations=0, var diags=0
owninteger  total ca = 0
owninteger  last ca = -1;       ! Used by 'set line'
owninteger  trace flag = 0;     ! controls calling of DUMP TRACE routine

integer     jtag;                  ! Set by 'JUMP TO'
ownstring(ident len)  external id = "", alias = "", block name = ""
ownstring(ident len)  internal id = ""
owninteger  faulty=0
owninteger  null string = 0

byteintegername   cslen
byteintegerarray  current string(0:255)

! Register optimisation scratch pad

owninteger Last Gpr = 1,
            Last Fpr = Fr0,
            Last EO  = 1

recordformat  kfm(record(kfm)name  link, array,
                  integer  disp,
                  shortinteger reg,
                  byteinteger  type, form, base, ktype)
record(kfm)array  knowledge(1:max knowledge)
ownrecord(kfm)name   klist == (0), kasl == (0)
integer known regs = 0;       ! bit mask: must contain at least ANYF+1 bits
integer in use = 0;           !counter to limit active uses

!Environment control

recordformat envfm(integer label, in use, known, record(kfm)name link)
record(envfm)array envir(1:max envirs)
owninteger envp = 0


! Code generation routine specs
routinespec  rr(integer  op,r1,r2)
routinespec  rx(integer  op,r1,base,disp)
routinespec  rxi(integer  op,r1,base,disp)
routinespec  rxd(integer  op,r1,record(stackfm)name  v)
routinespec set line

!                                                             >> SHOW <<
routine  show(record(stackfm)name  v)
   write(v_varno,2);  print symbol(':')
   write(v_type,3);  write(v_form,2);  write(v_flag,2)
   write(v_base,3);  write(v_disp,5)
   write(v_length,3)
   write(v_extra,3);  write(v_format,3)
   write(v_header,3);  write(v_dim,3)
   if v_oper # 0 start
      write(v_oper,2);  newline
      print string("         +")
      show(v_link)
   else
      newline
   finish
end

!                                                            >> ABORT <<
routine  abort(integer  code)
record(dfm)name  dd
integer  j
   select output(report)
   print string("*Compiler error '")
   print symbol( (code>>j)&255 ) for j = 24,-8,0
   print string("' at line");  write(current line,1)
   newline
   print string("Please seek assistance!!");  newline
   if stp # 0 start
      print string("STACK:");  newline
      show(stacked(j)_v) for j = 1,1,stp
   finish
   unless using_link == null start
      print string("USING:");  newline
      dd == using_link
      cycle
         show(dd_d)
         dd == dd_link;  exit if dd == null
      repeat
   finish
   select output(object)
   signal 15,15;          ! %IF diagnose < 0
end;           !  abort

!                                                             >> WARN <<
routine  warn(integer  n)
switch  w(1:8)
   select output(report)
   print string("*WARNING: line")
   write(current line, 1);  print string(": ")
   -> w(n)
w(1): print string("division by zero");  -> at
w(2): print string("Illegal FOR");  -> at
w(3): print string("Non-local control variable?");  -> at
w(4): print string("Invalid parameter for READ SYMBOL");  -> at
w(5): print string("String constant too long");  -> at
w(6): print string("No. of shifts outwith 0..31");  -> at
w(7): print string("Illegal constant exponent");  -> at
w(8): print string("Numerical constant too big");  -> at
at: newline
   select output(object)
end

!                                                          >> MONITOR <<
routine  monitor(record(stackfm)name  v, string(15) text)
   select output(report)
   print string(text);  print symbol(':')
   spaces(9-length(text))
   show(v)
   select output(object)
end

!                                                         >> FLOATING <<
predicate  floating(record(stackfm)name v)
! check descriptor for floating point quantity
   true if (v_type >= reals and v_type # 255) or (v_oper # 0  c
                                     and v_link_type >= reals)
   true if v_oper >= rexp
   false
end

!                                                             >> ZERO <<
predicate  zero(record(stackfm)name  v)
! CHECK DESCRIPTOR FOR (INTEGER) ZERO
   false if v_disp # 0 or v_base # 0 or constant # v_form # AV in S
   false if v_oper # 0
   true
end

!                                                            >> CONST <<
predicate  const(record(stackfm)name  v)
! CHECK DESCRIPTOR FOR CONSTANT (INTEGER) VALUE
   false unless v_form = constant and v_oper = 0
   false if v_type > byte
   true
end

integerfn Min Record Size(record(stackfm)name A, B)
   integer N, M
   N = A_Format;  N = Var(N)_Length&x'FFFF' if N # 0
   M = B_Format;  M = Var(M)_Length&x'FFFF' if M # 0
   N = M if N = 0 or (M # 0 and M < N)
   result = N if N > 0
   Abort(m'Rec0')
end
!                                                             >> SAME <<
integerfn POWER(integer n)
   integer j, ref
   ref = 1
   for j = 1, 1, 14 cycle
      ref = ref<<1
      if ref >= n start
         if ref = n then result = j else result = -1
      finish
   repeat
   result = -1
end

predicate  same(record(stackfm)name  v,w)
! Test whether or not V and W describe the same object.
   true if v_disp = w_disp and v_base = w_base      c
         and v_type = w_type and v_form = w_form and v_extra = w_extra
   false
end

!                                                      >> IN FREE REG <<
predicate in free reg(record(stackfm)name  v)
!  TRUE if v is in a useable register
   false unless v_form = v in r and activity(v_base) <= 1
   true
end

!                                                             >> TEMP <<
integerfn  temp
! Allocate a temporary 4 bytes long
integer t
   if next temp = new temp start;              ! no spare temps outstanding
      t = (frame+3)&(¬3)
      frame = t+4
      result = t if new temp = max temps;      ! temp buffer overflow
      new temp = new temp + 1
      temps(new temp) = t
   finish
   next temp = next temp + 1
   result = temps(next temp)&x'FFFF'
end

!                                                              >> TAG <<
integerfn  tag
   integer s1, s2
   s1 = next
   readsymbol(s2)
   readsymbol(next)
   result = s1<<8!s2
end

!                                                            >> GET D <<
routine  get d
   longreal p
   integer  i, n
   real ten,one
   n = 10 ; rvalue = n    {initial base}
   n = 1; one = n
   n = tag;  read symbol(next);    ! Skip comma
BASE:
   ten = rvalue
   rvalue = 0
   cycle
      sym = next;  read symbol(next)
      exit if sym = '.'
      n = n-1
      -> power if sym = '@'
      -> base  if sym = '_'
      sym = sym-'A'+'0'+10 if sym >= 'A'
      rvalue = rvalue*ten+(sym-'0')
      -> SIGN if n = 0
   repeat
   p = one
   cycle
      n = n-1;  -> SIGN if n = 0
      sym = next;  read symbol(next)
      -> POWER if sym = '@'
      sym = sym-'A'+'0'+10 if sym >= 'A'
      p = p/ten
      rvalue = rvalue + (sym-'0')*p
   repeat
POWER:
   n = tag
   n = n ! 16_FFFF0000 if n&16_8000 # 0
   rvalue = rvalue * (ten^n)
SIGN:               ! sign of whole value
   if next = 'U' start
      read symbol(next)
      rvalue = -rvalue
   finish
end

!                                                          >> RELEASE <<
routine  release(integer  reg)
! Hazard the value in a register
   abort(m'REL1') if reg > fr14
   return if reg = 0 or activity(reg) < 0;     ! LOCKED
   activity(reg) = activity(reg)-1
   abort(m'REL2') if activity(reg) < 0
   claimed = claimed - 1
end

!                                                            >> CLAIM <<
routine  claim(integer  reg)
! Cherish the value in a register
   abort(m'CLM1') if reg > fr14
   return if reg = 0 or activity(reg) < 0
   activity(reg) = activity(reg)+1
   claimed = claimed+1
end

routinespec  forget reg(integer  mask)
routinespec  forget all
routinespec forget var(record(stackfm)name v)
!                                                           >> HAZARD <<
routine  hazard(integer  reg)
! Protect the value in register REG by storing in a temporary.
integer  n, t, tot
record(dfm)name  p
record(stackfm)  u
         routine  mod(record(stackfm)name  v)
            switch  sw(0:a in rec)
            v_base = local
            n = n-1
            -> sw(v_form)
         sw(a in rec):
         sw(av in rec):
         sw(v in rec):
            if tot = 1 start
               claim(reg);  rx(lw,reg,reg,v_extra)
               u_type = integers
               v_extra = t
               -> OUT2
            finish
         sw(constant): abort(m'HAZ1')
         sw(v in s):   if v_disp = 0 start
                          v_disp = t;  v_form = a in s;  ->out1
                       finish
         sw(a in s):
         sw(av in s):
      !  change (X in S) to (X in REC)
            v_form = v_form + 3;  v_extra = t;  -> OUT1
         sw(v in r): v_form = v in s;  v_disp = t
            v_type = u_type
         OUT1:
            v_flag = v_flag ! assigned
         OUT2:
         end

   n = activity(reg);  return if n <= 0;    ! NOT IN USE OR CLAIMED
   tot = n
   claimed = claimed - n
   activity(reg) = 0
   t = temp;      ! ** needs a parameter to deal with 8-byte reals **
   u_type = integers
   u_type = reals if FR0 <= reg <= FR14
   p == using_link
   cycle
      exit if p == null
      mod(p_d) if p_d_base = reg
      p == p_link
   repeat
   u_xbase = local;  u_disp = t
   u_xform = V in S ! (assigned << 8)
   rxd(st,reg,u)
   forget var(u)
   abort(m'HAZ2') if n # 0;       ! USE STILL OUTSTANDING
end

!                                                       >> HAZARD ALL <<
routine  hazard all
integer  j
   forget reg(-1) 
   if claimed # 0 start;              ! at least one register claimed
      hazard(j) for j = r0,1,fr14
   finish
end

! REGISTER OPTIMISATION ROUTINES
constinteger  register contents = 1

   routine Reset Optimisation Data
      integer J
      Last Gpr = 1
      Last Fpr = Fr0
      Last EO  = 1
      Envp    = 0
      Known Regs = -1
      In Use = 0
      Kasl == Null
      Klist == Null
      for j = 1,1,max knowledge cycle
         knowledge(j)_Link == Kasl
         Kasl == knowledge(j)
      repeat
      for j = 1,1,max envirs cycle
         Envir(j)_Label = 0
         Envir(j)_Link == Null
      repeat
   end

!                                                            >> DUMP OPT LIST <<
routine  dump opt list
   record(kfm)name  p
   select output(report)
   p == klist
   if p == null start
      print string("*opt list empty")
      newline
   else
      cycle
         write(p_type,1);  write(p_form,1)
         write(p_disp,3);  print symbol('(')
         write(p_base,-1);  print string(") =")
         write(p_reg,1);  newline
         p == p_link
      repeat until p == null
   finish
   select output(object)
end;          ! dump opt list

!                                                                  >> K ENTRY <<
record(kfm)map  k entry(record(stackfm)name  v, integer  fuzz)
   record(kfm)name  p,q
   fuzz = ¬fuzz
   p == k list
   q == null
   while not p == null cycle
      -> FOUND if (p_disp!!v_disp)&fuzz = 0 and p_base = v_base
      q == p
      p == p_link
   repeat
   result == null;       ! failure
FOUND:
   if not q == null start;            ! promote if not first item already
      q_link == p_link
      p_link == klist
      klist == p
   finish
   result == klist
end;       ! k entry

!                                                                >> NEW KCELL <<
record(kfm)map  new kcell
   record(kfm)name  p, q
   integer  n
   if kasl == null or in use >= max use start;               ! no free cells left
      ! In extremis so reclaim last item from KLIST.
      p == klist;  q == null
      n = max use
      cycle
         n = n-1
         exit if p_link == null
         q == p
         p == p_link
      repeat
      abort(m'OPT1') if n # 0
      q_link == null;      ! truncate KLIST
      p_link == kasl;  kasl == p;         !give on back
      in use = in use-1
   finish
   p == kasl;  kasl == kasl_link
   in use = in use+1;  abort(m'Opt3') if in use > max use
   p = 0
   result == p
end;       ! new kcell

!                                                                >> ASSOCIATE <<
routine  associate(record(stackfm)name  v, integer  reg)
   record(kfm)name  p
   return if reg = R0 or V_Base = Reg
   p == k entry(v,0)
   if p == null start;        ! new entry
      p == new kcell
      p_link == klist
      klist == p
   else;       ! re-use this cell
      forget reg(1<<p_reg)
   finish
   p_reg = reg
   p_base = v_base
   p_disp = v_disp
   p_type = v_type
   p_form = v_form
   p_ktype = register contents
   known regs = known regs ! (1<<reg)
   known regs = known regs ! (1<<p_base) if activity(p_base) >= 0;    ! unlocked ?
end;       ! associate

!                                                                  >> CHEAPEN <<
routine  cheapen(record(stackfm)name  v, integer  mode)
!! modes:  >= 0:  looking for value
!           < 1:  looking for address
   record(kfm)name  p
   integer  reg, form, type
   form = v_form;  type = v_type
   p == k entry(v,0)
   return if p == null
   v_flag = v_flag ! assigned if p_form = V in S;       ! it's at least assigned
   return if p_reg = 0;                       !*psr* Nothing known
   cheap reg = p_reg if form # AinS and p_type = Type
   return if mode < 0 and form = V in S;      !  V in S on left-hand side
   reg = p_reg
   if form = A in S and p_type = integers and p_form = V in S start
      release(v_base);  claim(reg)
      v_base = reg;  v_disp = 0;  v_Xform = V in S  {changed to Xform - PSR}
      cheapen(v,mode)
   else
      return if p_type # type or p_form # form
      release(v_base);  claim(reg)
      v_base = reg;  v_disp = 0;  v_Xform = V in R {changed to Xform - PSR}
   finish
   if diagnose < 0 start
      monitor(v, "CHEAPENED")
      dump opt list if diagnose & 16 # 0
   finish
end;          ! cheapen

!!! * * * * * This needs to be a bit brighter * * * * *
!                                                               >> FORGET VAR <<
routine  forget var(record(stackfm)name  v)
   record(kfm)name  p
!!!!!   p == k entry(v, align)
!!!!!   forget reg(1<<p_reg) %unless p == null

   cycle
      p == k entry(V, align)
      return if p == null
      p_base = anyf+1            {invalid entry}
   repeat

end;       ! forget var

!                                                               >> FORGET REG <<
routine  forget reg(integer  reg mask)
   record(kfm)name  p
   return if known regs & reg mask = 0;     ! for speed:  nothing to do
   reg mask = reg mask & (¬1);      ! R0 = 1 not 0
   known regs = known regs & (¬reg mask)
   p == klist
   if reg mask < 0 start;           ! forget the lot
      while not p == null cycle
         p_base = anyf+1 if regmask & (1<<p_base) # 0;    ! invalidate entry
         p_reg = 0
         p == p_link
      repeat
   else;                            ! selective forget
      while not p == null cycle
         p_base = anyf+1 if reg mask & (1<<p_base) # 0;    ! invalidate entry
         p_reg = 0 if reg mask & (1<<p_reg) # 0;        ! forget reg association
         p == p_link
      repeat
   finish
   ! Clean up any old kcells which can be recovered easily
   while klist ## null and klist_base = anyf+1 cycle
      p == klist;  klist == klist_link
      p_link == kasl
      kasl == p
      in use = in use-1
   repeat
   abort(m'Use?') if in use < 0
end;          ! forget reg

!                                                               >> FORGET ALL <<
routine  forget all
   record(kfm)name  p
   if not klist == null start
      p == klist
      cycle
         in use = in use-1
         exit if p_link == null
         p == p_link
      repeat
      p_link == kasl
      kasl == klist
      klist == null
   finish
   abort(m'Fall') unless in use = 0
   known regs = 0
end;       ! forget all
!environment control

   record(envfm)map environment(integer label)
      record(envfm)name E
      integer j
      if label > 0 start
         for j = 1,1,max envirs cycle
            E == envir(j)
            result == E if E_label = label
         repeat
      finish
      result == null
   end
   record(envfm)map new env(record(envfm)name E)
      record(kfm)name K
      if E == null start
         envp = envp+1;  envp = 1 if envp > max envirs
         e == envir(envp)
      finish
      k == E_link
      unless k == null start
         k == k_link while k_link ## null
         k_link == kasl
         kasl == E_link
      finish
      E_in use = 0
      E_label = 0
      E_link == null
      result == E
   end
   record(kfm)map Ecopy(record(kfm)name L)
      record(kfm)name K
      result == null if l == null
      abort(m'Ecop') if kasl == null
      k == kasl;  kasl == k_link
      k = l
      k_link == Ecopy(l_link)
      result == k
   end
   routine restore environment(integer label)
      record(envfm)name E
      record(envfm) temp
      temp_link == klist
      e == new env(temp)            {release current environment}
      E == environment(label)
      if E == null start
         klist == null
         known regs = 0
         in use = 0
      else
         klist == Ecopy(E_link)
         known regs = E_known
         in use = E_in use
      finish
   end
   routine remember environment(integer label)
      record(envfm)name E
      return if label <= 0
      E == environment(label)
      E == new env(E)
      E_label = label
      E_known = known regs
      E_in use = in use
      E_link == Ecopy(klist)
   end
   routine merge environment(integer label)
      record(ENVFM)name e
      record(kfm)name K, end, X
      record(kfm) khead
      routine MERGE(record(kfm)name K)
         record(kfm)name p
         p == klist
         while p ## null cycle
            if p_disp = k_disp and
                p_reg  = k_reg  and
                p_base = k_base and
                p_form = k_form and
                p_type = k_type and
                p_ktype= k_ktype  start
                  {*****Beware when array opt is put in***}
                end_link == k
                end == k
                E_known = E_known ! (1<<p_reg) ! (1<<p_base)
                E_in use = E_in use+1
                return
            finish
            p == p_link
         repeat
         k_link == kasl;  kasl == k
      end
      E == environment(label)
      if E ## null start
         k == E_link
         e_link == null
         e_in use = 0
         e_known = 0
         khead_link == null;  end == khead
         while k ## null cycle
            x == k_link
            merge(k)
            k == x
         repeat
         end_link == null
         e_link == khead_link
      finish
   end

!                                                              >> GPR <<
integerfn  gpr
! Get a general (integer) register
constinteger nregs=8
constbyteintegerarray  pref(1:nregs) =
         P1, P2, R4, R9, R10, R11, R3, R12
integer  r,j,mask
   mask = known regs
   cycle
      for j = 1,1,nregs cycle
         Last Gpr = Last Gpr-1;  Last Gpr = nregs if Last Gpr = 0
         r = pref(Last Gpr)
         result = r if activity(r) = 0 and mask & (1<<r) = 0
      repeat
      exit if mask = 0
      mask = 0
   repeat
   hazard(R4)
   result = R4
end

!                                                    >> EVEN/ODD PAIR <<
integerfn even odd pair
! Get an even/odd (integer) register pair
! the odd register is returned
! registers are hazarded here
constinteger  regs = 3
constbyteintegerarray  even(1:regs) = r2, r10, r4
integer j,r,mask
   mask = known regs
   cycle
      for j = 1,1,regs cycle
         Last EO = Last EO-1;  Last EO = regs if Last EO = 0
         r = even(Last EO)
         result = r+1 if activity(r) = 0 and activity(r+1) = 0 c
                                                      and mask & (3<<r) = 0
      repeat
      exit if mask = 0
      mask = 0
   repeat
   hazard(r2);  hazard(r3);  result = r3
end

!                                                              >> FPR <<
integerfn  fpr
! get a floating point register
integer  j,mask
   mask = known regs
   cycle
      for j = fr0,1,fr14 cycle
         Last Fpr = Last Fpr-1;  Last Fpr = fr14 if Last Fpr = fr0-1
         result = Last Fpr if activity(Last Fpr) = 0
      repeat
      exit if mask = 0
      mask = 0
   repeat
   hazard(fr0)
   result = fr0
end

!OBJECT FILE HANDLING ROUTINES
!                                                              >> PUT <<
routine put(integer n)
   print symbol(n>>8);  print symbol(n&255)
end

!                                              >> SELECT LITERAL AREA <<
routine  select literal area
integer  k
   print symbol(lit area)
   abort(m'DMP1') if ca < 0
   k = lita;  lita = ca;  ca = k
end

!                                                 >> SELECT CODE AREA <<
routine  select code area
integer  k
   abort(m'DMP2') if ca > 0
   k = lita;  lita = ca;  ca = k
   print symbol(code area)
end

   routine  phex(integer  n)
   integer  j,k
      spaces(2)
      for j = 12,-4,0 cycle
         k = (n>>j)&15
         if k <= 9 then k = k+'0' else k = k-10+'A'
         print symbol(k)
      repeat
   end

!                                                         >> DUMP TAG <<
routine  dump tag(integer  tag, type)
conststring(7)array  s(tag def:c ref) =
      " defn", " r ref", " p ref", " sw ref", " j ref", " c ref"
   select output(report)
   print symbol('*');  write(ca,-3) 
   print string(s(type))
   write(tag,1) 
   newline
   select output(object)
end;        !   dump tag

!                                                             >> DUMP <<
routine  dump(integer  p,val)
integer  k
   select output(report)
   if p = m'CA' start
      print string("CA ");  k = ca
   else
      print string("GA ");  k = ga
   finish
   write(k-2,-3);  print symbol(':')
   phex(val)
   newline
   select output(object)
end;             !  dump

!                                                            >> CPUT <<
routine  cput(integer  n)
! Output one halfword to code area
   print symbol(code item)
   print symbol(n>>8);  print symbol(n&255)
   ca = ca+2
   dump(m'CA',n) if diagnose < 0
end

!                                                            >> GPUT <<
routine  gput(integer  n)
! Output one halfword to gla area
   print symbol(gla item)
   print symbol(n>>8);  print symbol(n&255)
   ga = ga+2
   dump(m'GA',n) if diagnose < 0
end

!                                                            >> CWORD <<
routine  cword(integer  n)
   cput(n>>16);  cput(n)
end

!                                                            >> LIT BYTE <<
routine  lit byte(integer  n)
owninteger  v=0,f=0
   f = ¬f
   if f=0 start
      ca = ca+1;  cput(v<<8 + n&255)
   else
      v = n;  ca = ca-1
   finish
   abort(m'DMP3') unless ca <= 0
end

!                                                            >> GWORD <<
routine  gword(integer  n)
   gput(n>>16);  gput(n&x'FFFF')
end

!                                                         >> GWORD REL <<
routine  gword rel(integer  n)
   ! Word in GLA modified at load-time by gla base address - used to relocate
   ! %ownarray headers.
   gput(n>>16);  gput(n&x'FFFF')
   print symbol(g rel);  relocations = relocations + 1
end;          ! gword rel

!                                                              >> GWORD CREL <<
routine  gword crel(integer  n)
   ! Word in GLA modified at load-time by code base address - used to relocate
   ! %constarray headers
   gput(n>>16);  gput(n&x'FFFF')
   print symbol(c rel);  relocations = relocations+1
end;             ! GWORD CREL

!                                                            >> GBYTE <<
routine  gbyte(integer  n)
owninteger  v=0, f=0
   f = ¬f
   if f = 0 start
      ga = ga-1;  gput(v<<8 + n&255)
   else
      v = n;  ga = ga+1
   finish
end

!                                                             >> GFIX <<
routine  gfix(integer align)
   gbyte(0) while ga&align # 0
end

!                                                       >> DEFINE TAG <<
routine  define tag(integer ref)
integer  k
   select output(direct)
   print symbol(tag def)
   print symbol(ref>>8);  print symbol(ref&255)
   k = ca>>1;               ! ******* Halfword units
   print symbol(k>>8);   print symbol(k&255)
   select output(object)
   dump tag(ref,tag def) if diagnose < 0
end;       ! define tag

!                                                 >> DEFINE REFERENCE <<
routine  define reference(integer  ref, type)
integer  k
   set line if current line # last line
   select output(direct)
   print symbol(type)
   print symbol(ref>>8);  print symbol(ref&255)
   k = ca>>1;               ! ******** Halfword units
   print symbol(k>>8);   print symbol(k&255)
   select output(object)
   dump tag(ref,type) if diagnose < 0
   print symbol(type)
   if type = r ref start
      print symbol(ref>>8);  print symbol(ref&255)
   finish
end;            ! define reference

!                                                    >> CLAIM LITERAL <<
routine  claim literal(integer  size,align)
integer  k
   abort(m'DMP4') if ca < 0 or litmax > 0
   if lita&1 # 0 start;         ! odd no. of bytes
      select literal area
      lit byte(0)
      select code area
   finish
   litmax = -((-litmax+size+align)&(¬align))
   lita = litmax
   k = (-lita)>>1
   print symbol(lit org);  put(k);         ! Tell pass 3
end;           !  claim literal

!                                                         >> SET LINE <<
routine  set line
   integer  flag
      return if ca < 0;           !in literal area
      if current line-last line # 1 then flag = line reset c
                                                   else flag = line flag
      select output(direct)
      print symbol(flag);  put(current line) if flag = line reset
      if diagnose < 0 start
         select output(report)
         print string("-->line");  write(current line,1)
         newline
      finish
      select output(object)
      print symbol(flag);  put(current line) if flag = line reset
      last line = current line;  last ca = ca
end;          ! set line

!                                                         >> DESCRIBE <<
routine  describe(integer  base,disp, string(ident len)name  xsym)
! Generate a full description of the variable specified by (base,disp)
! Assumes that DIAG SIZE, DIAG TYPE, DIAG FORM, OTYPE are appropriately set.
integer  size,type
integer  j,k
constbyteintegerarray  compressed type(0:13) =
      1,      2,     3,       4,    5,  0(3),    6,    0(4),  7
!  integer  real  string  record   byte         short      long real
   length(xsym) = extern len if length(xsym) > extern len
    print symbol(length(xsym))
    print symbol(charno(xsym,j)) for j = 1,1,length(xsym);   ! name
    return if base < 0
    size = diag size;  type = diag type
    size = 1 if diag type >= 3 or diag form > 2 or size = 0
    type = 1 if diag type <= 0
    k = (size-1) << 2 + (type-1)
    j = 0
    j = x'80' if Otype # 0 and Spec # 0    {external data spec}
    print symbol(otype)
    print symbol( compressed type(k) << 4 ! DIAG FORM ! J)
    j = actual(base)<<20 + disp&x'000FFFFF'
    print symbol(j>>16);  print symbol(j>>8);  print symbol(j)
end;          !  describe

!                                                        >> SET DIAG <<
routine  set diag(integer  base,disp)
! Implicit parameters:     DIAG TYPE   DIAG FORM  DIAG SIZE  OTYPE
   var diags = var diags + basic vdiag
   var diags = var diags + extra vdiag if length(internal id) > short ident
   print symbol(var diag);  describe(base,disp,internal id)
end

!                                                >> EXTERNAL LINK <<
routine  external link(integer  ref type,data size,addr)
   !Note that ADDR is ignored when defining procedure entry points
   !     it is assumed that the link is set IMMEDIATELY before the entry point.
   integer  k
   abort(m'DMP5') unless data ref <= ref type <= ep defn
   if ref type&1 # 0 then defns = defns+1 else specs = specs+1
   print symbol(extern)
   print symbol(ref type)
   put(data size//2);      ! Halfwords for pass3
   k = gla;  k = code if ref type = ep defn
   describe(k,addr//2,external id)
   if ref type&1 = 0 start;                 !a spec
      gword(0);  gword(0)
      if ref type = ep ref then gword(0) else gword(-1)
   finish
end

!                                                      >> CLOSE FILES <<
routine  close files
   select output(direct)
   print symbol(prog end)
   put(total ca>>1);  put((-litmax)>>1);  put(ga>>1);   ! Halfword units
   put(defns);  put(specs);  put(relocations)
   print symbol(0);      ! to prevent potential trouble with binary 4 = EOF
   close output
   select output(object);  print symbol(prog end) 
   close output
end;     !  close files

! code generation routines
!                                                                        >>  RXD  <<
! = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
!                  B a s i c   C o d e  G e n e r a t o r
!
routine  RXD(integer  op, r1, record(stackfm)name  v)
integer  index, mask, code, format
integer  type, form, base, disp, x
integer  k, old ca
record(stackfm)  u
constbyteintegerarray  type index(integers:reals+1) =
         1,      2,     7,       0(3),          1,      0
!      integer  short   byte                   reals  reall
   old ca = ca
   type = v_type;  form = v_form
   base = v_base;  disp = v_disp;  x = v_index
   op = op + fp base if r1 >= FR0
   mask = op index(op);  index = mask&255
   set line if last line # current line

   if form = constant or form = AV in S start;         ! RXI
      abort(m'RXDx') if x # 0
      if disp = 0 and base # 0 and LSH # op # RSH and op # SRA start
         ! optimise:  LHI x,0(y)    =>  LR x,y
         ! remove:    LHI x,0(x)
         if r1 # base or op # LW start
            u_xbase = base;  u_disp = 0;   ! **** u_disp otherwise unassigned ****
            u_form = V in R;  rxd(op,r1,u)
            return
         finish
         code = x'10';             !(psr) preserve CC at end - see later
      else if op = LW and r1 = base and 15 >= disp >= -15
         ! LHI x, 15(x)   =>  AIS x,15
         ! LHI x,-15(x)   =>  SIS x,15
         op = add
         if disp < 0 start
            op = sub;  disp = -disp
         finish
         u_form = constant;  u_xbase = 0;  u_disp = disp;  rxd(op,r1,u)
         release(base)
         return
      else if op = LSH and disp = 1 and base = 0
         ! SLLS x,1   =>  AR x,x
         claim(r1)
         u_xbase = r1;  u_disp = 0
         u_form = V in R;  rxd(add,r1,u);  return
      else;                                     ! general case (RXI)
         abort(m'RXD1') if mask&FWIMM = 0 and  LSH # op # RSH and op # SRA
         index = index + 3;            ! fullword immediate
         format = 0
         if 15>=disp>=-15 and base=0 and (SF IMM+INV IMM)&mask # 0 start
            if disp >= 0 start
               format = 2
            else
               format = 3;  disp = -disp
            finish
         else if 32767 >= disp >= -32768
            format = 1
         finish
         code = op code(index + format)
         if format >= 2 start
            cput(code&x'FF00' + actual(r1)<<4 + disp)
         else
            cput(code&x'FF00' + actual(r1)<<4 + actual(base))
            cput(disp>>16) if format = 0;    ! fullword immediate ?
            cput(disp)
         finish
      finish
   else if form = V in R;                    !   register-register operation
      abort(m'RXD2') if r1 = 0 or base = 0 or x # 0
      code = op code(index);      ! ** N.B.  op code(index + 0) really ......
      if code&x'20' # 0 start;        ! STR => LR etc.
         k = r1;  r1 = base;  base = k
      finish
      cput(code&x'FF00' + actual(r1)<<4 + actual(base))
   else;                                     ! RX (integer,real,short,byte)
      abort(m'RXD3') if r1 = 0 or base = R0
      format = type index(type);  abort(m'RXD4') if format = 0
      code = op code(index + format)
      abort(m'RXD5') if (code&15)&disp # 0
      cput(code&x'FF00' + actual(r1)<<4 + actual(base))
      unless 0 <= disp <= 16383 and x = 0 start
         cput(x'4000' + actual(x)<<8 + (disp>>16)&255)
      finish
      cput(disp)
   finish
   release(base) if base > 0;       !(PSR)
   release(x) if x # 0
   if code&x'40' # 0 start;        ! leaves cond code completely unchanged
      cc ca = cc ca + ca - old ca 
   else if code&x'10' = 0;        ! cond code relative to zero ?
      cc ca = ca;  cc reg = r1
   finish
end;                ! of 'RXD'
! = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

!                                                                        >>  RR  <<
routine  rr(integer  op,r1,r2)
   record(stackfm)  v
   v_xbase = r2;  v_disp = 0
   v_type = integers;  v_type = reals if r1 >= FR0
   v_form = V in R;  rxd(op, r1, v)
end

!                                                                     >>  RXI  <<
routine  rxi(integer  op, r1, base, disp)
   record(stackfm)  v
   v_xbase = base;  v_disp = disp
   v_type = integers
   v_form = constant;  rxd(op, r1, v)
end

!                                                                     >>  RX  <<
routine  rx(integer  op, r1, base, disp)
   record(stackfm)  v
   v_xbase = base;  v_disp = disp
   v_type = integers;  v_type = reals if r1 >= FR0
   v_form = V in S;  rxd(op, r1, v)
end

!                                                                  >> SKIP <<
routine  skip(integer  half words, condition)
! Plant a short forward jump to skip over unwanted code sequence:
!      skips forward the number of halfwords specified NOT COUNTING the
!      code dumped to effect the skip
! Must be used with care as it doesn't account automatically for register
! contents.
   constshortintegerarray  jump(0:5) =
      x'2330',   x'2130',   x'2110',   x'2120',   x'2320',   x'2310'
      !  BES        BNES       BMS        BPS        BNPS       BNMS
   Condition = Inverted(Condition) if Condition&16 # 0
   abort(m'SKP1') unless 0 < halfwords <= 14 and 0 <= condition <= 5
   cput( jump(condition) ! (halfwords + 1) )
end

!                                                            >> MACHINE CODE <<
routine machine code
   constinteger branch = 1
   constinteger rr = 2
   constinteger indexed = 4
   constinteger ri1 = 8
   constinteger ri2 = 16
!*delete*
constinteger mc entries = 204
constintegerarray mcop(1:mc entries) =
    x'00000041', x'00000042', x'00000043', x'00000044', x'0000004C',
!        A            B            C            D            L      
    x'0000004D', x'0000004E', x'0000004F', x'00000053', x'00000058',
!        M            N            O            S            X      
    x'00001004', x'00001005', x'00001008', x'00001009', x'0000100C',
!        AD           AE           AH           AI           AL     
    x'0000100D', x'00001012', x'00001084', x'00001085', x'00001088',
!        AM           AR           CD           CE           CH     
    x'00001089', x'0000108C', x'00001092', x'000010C3', x'000010C5',
!        CI           CL           CR           BC           BE     
    x'000010C7', x'000010CC', x'000010CD', x'000010CF', x'000010D0',
!        BG           BL           BM           BO           BP     
    x'000010D2', x'000010DA', x'00001144', x'00001145', x'00001148',
!        BR           BZ           DD           DE           DH     
    x'00001152', x'00001304', x'00001305', x'00001308', x'00001312',
!        DR           MD           ME           MH           MR     
    x'00001341', x'00001342', x'00001344', x'00001345', x'00001348',
!        LA           LB           LD           LE           LH     
    x'00001349', x'0000134D', x'00001352', x'00001383', x'00001388',
!        LI           LM           LR           OC           OH     
    x'00001389', x'00001392', x'000013C8', x'000013C9', x'000013D2',
!        OI           OR           NH           NI           NR     
    x'00001484', x'00001485', x'00001488', x'00001489', x'00001492',
!        SD           SE           SH           SI           SR     
    x'00001493', x'00001494', x'000014C2', x'000014C4', x'000014C8',
!        SS           ST           RB           RD           RH     
    x'00001549', x'00001553', x'00001582', x'00001584', x'00001588',
!        TI           TS           WB           WD           WH     
    x'00001648', x'00001649', x'00001652', x'000400CC', x'00040112',
!        XH           XI           XR          ABL          AER     
    x'00040152', x'00040213', x'00040249', x'0004024D', x'0004054C',
!       ADR          AIS          AHI          AHM          ATL     
    x'000420D4', x'00042112', x'00042152', x'00042249', x'00042342',
!       CBT          CER          CDR          CHI          CLB     
    x'00042348', x'00042349', x'00042352', x'0004300C', x'00043092',
!       CLH          CLI          CLR          BAL          BCR     
    x'00043112', x'00043185', x'00043192', x'000431C3', x'00043312',
!       BER          BGE          BGR          BFC          BMR     
    x'00043345', x'00043352', x'00043392', x'000433C3', x'000433C5',
!       BLE          BLR          BOR          BNC          BNE     
    x'000433CC', x'000433CD', x'000433CF', x'000433D0', x'000433DA',
!       BNL          BNM          BNO          BNP          BNZ     
    x'00043452', x'00043543', x'00043648', x'000436D2', x'00045112',
!       BPR          BTC          BXH          BZR          DER     
    x'00045152', x'00045252', x'00047352', x'00047652', x'0004C112',
!       DDR          DHR          FLR          FXR          MER     
    x'0004C152', x'0004C252', x'0004D093', x'0004D0D2', x'0004D112',
!       MDR          MHR          LCS          LBR          LER     
    x'0004D152', x'0004D213', x'0004D249', x'0004D24C', x'0004D304',
!       LDR          LIS          LHI          LHL          LMD     
    x'0004D305', x'0004E092', x'0004E249', x'0004F249', x'0004F390',
!       LME          OCR          OHI          NHI          NOP     
    x'00052090', x'000520D4', x'00052112', x'00052152', x'00052213',
!       SCP          SBT          SER          SDR          SIS     
    x'00052249', x'00052341', x'00052492', x'000524C1', x'000524CC',
!       SHI          SLA          SSR          SRA          SRL     
    x'000524D2', x'00052542', x'00052544', x'00052545', x'00052548',
!       SRR          STB          STD          STE          STH     
    x'0005254D', x'000525C3', x'000530CC', x'000530D2', x'000530D4',
!       STM          SVC          RBL          RBR          RBT     
    x'00053152', x'00053252', x'0005334C', x'000534CC', x'0005354C',
!       RDR          RHR          RLL          RRL          RTL     
    x'000550D4', x'00055249', x'000560D2', x'00056152', x'00056252',
!       TBT          THI          WBR          WDR          WHR     
    x'00059249', x'010895D2', x'0108D249', x'010C0352', x'010C6112',
!       XHI          CHVR         CLHI         BALR         BGER    
    x'010C7092', x'010C7092', x'010C70D3', x'010C71D3', x'010CD112',
!       BFCR         BFCR         BFBS         BFFS         BLER    
    x'010CF092', x'010CF112', x'010CF312', x'010CF352', x'010CF392',
!       BNCR         BNER         BNMR         BNLR         BNOR    
    x'010CF452', x'010CF6D2', x'010D5092', x'010D50D3', x'010D51D3',
!       BNPR         BNZR         BTCR         BTBS         BTFS    
    x'010D9345', x'01111492', x'011190D2', x'01119252', x'011CD152',
!       BXLE         EPSR         EXBR         EXHR         FLDR    
    x'011D9152', x'01351497', x'013CE452', x'014883D4', x'0148D241',
!       FXDR         LPSW         NOPR         SINT         SLHA    
    x'0148D24C', x'0148D353', x'01493241', x'0149324C', x'01493353',
!       SLHL         SLLS         SRHA         SRHL         SRLS    
    x'014950D2', x'01495304', x'01495305', x'424C3C72', x'424C3C76',
!       STBR         STMD         STME        CRC12        CRC16    
    x'4D452592', x'52349353', x'524C9353', x'55340545'
!      LPSWR        SLHLS        SRHLS        TLATE    
constshortintegerarray opflags(1:mc entries) =
    x'5A04', x'4305', x'5904', x'5D04', x'5804', x'5C04', x'5404', x'5604',
!      A        B        C        D        L        M        N        O    
    x'5B04', x'5704', x'7A04', x'6A04', x'4A04', x'FA10', x'D501', x'5104',
!      S        X        AD       AE       AH       AI       AL       AM   
    x'0A02', x'7904', x'6904', x'4904', x'F910', x'5504', x'0902', x'4285',
!      AR       CD       CE       CH       CI       CL       CR       BC   
    x'4335', x'4225', x'4285', x'4215', x'4245', x'4225', x'0303', x'4335',
!      BE       BG       BL       BM       BO       BP       BR       BZ   
    x'7D04', x'6D04', x'4D04', x'1D02', x'7C04', x'6C04', x'4C04', x'1C02',
!      DD       DE       DH       DR       MD       ME       MH       MR   
    x'E604', x'D304', x'7804', x'6804', x'4804', x'F810', x'D104', x'0802',
!      LA       LB       LD       LE       LH       LI       LM       LR   
    x'DE04', x'4604', x'F610', x'0602', x'4404', x'F410', x'0402', x'7B04',
!      OC       OH       OI       OR       NH       NI       NR       SD   
    x'6B04', x'4B04', x'FB10', x'0B02', x'DD04', x'5004', x'D704', x'DB04',
!      SE       SH       SI       SR       SS       ST       RB       RD   
    x'D904', x'F310', x'E004', x'D604', x'DA04', x'D804', x'4704', x'F710',
!      RH       TI       TS       WB       WD       WH       XH       XI   
    x'0702', x'6504', x'2A02', x'3A02', x'2602', x'CA08', x'6104', x'6404',
!      XR      ABL      AER      ADR      AIS      AHI      AHM      ATL   
    x'7704', x'2902', x'3902', x'C908', x'D404', x'4504', x'F510', x'0502',
!     CBT      CER      CDR      CHI      CLB      CLH      CLI      CLR   
    x'4104', x'0281', x'0333', x'4315', x'0223', x'4304', x'0213', x'4325',
!     BAL      BCR      BER      BGE      BGR      BFC      BMR      BLE   
    x'0283', x'0243', x'4385', x'4235', x'4385', x'4315', x'4345', x'4325',
!     BLR      BOR      BNC      BNE      BNL      BNM      BNO      BNP   
    x'4235', x'0223', x'4204', x'C004', x'0333', x'2D02', x'3D02', x'0D02',
!     BNZ      BPR      BTC      BXH      BZR      DER      DDR      DHR   
    x'2F02', x'2E02', x'2C02', x'3C02', x'0C02', x'2502', x'9302', x'2802',
!     FLR      FXR      MER      MDR      MHR      LCS      LBR      LER   
    x'3802', x'2402', x'C808', x'7304', x'7F04', x'7204', x'9E02', x'C608',
!     LDR      LIS      LHI      LHL      LMD      LME      OCR      OHI   
    x'C408', x'4205', x'E304', x'7504', x'2B02', x'3B02', x'2702', x'CB08',
!     NHI      NOP      SCP      SBT      SER      SDR      SIS      SHI   
    x'EF00', x'9D02', x'EE00', x'EC00', x'ED00', x'D204', x'7004', x'6004',
!     SLA      SSR      SRA      SRL      SRR      STB      STD      STE   
    x'4004', x'D004', x'E104', x'6704', x'9702', x'7604', x'9B02', x'9902',
!     STH      STM      SVC      RBL      RBR      RBT      RDR      RHR   
    x'EB00', x'EA00', x'6604', x'7404', x'C308', x'9602', x'9A02', x'9802',
!     RLL      RRL      RTL      TBT      THI      WBR      WDR      WHR   
    x'C708', x'1202', x'C508', x'0102', x'0313', x'0302', x'1302', x'2202',
!     XHI      CHVR     CLHI     BALR     BGER     BFCR     BFCR     BFBS  
    x'2302', x'0323', x'0383', x'0233', x'0313', x'0383', x'0343', x'0323',
!     BFFS     BLER     BNCR     BNER     BNMR     BNLR     BNOR     BNPR  
    x'0233', x'0202', x'2002', x'2102', x'C104', x'9502', x'9402', x'3402',
!     BNZR     BTCR     BTBS     BTFS     BXLE     EPSR     EXBR     EXHR  
    x'3F02', x'3E02', x'C205', x'0203', x'E201', x'CF08', x'CD08', x'1102',
!     FLDR     FXDR     LPSW     NOPR     SINT     SLHA     SLHL     SLLS  
    x'CE08', x'CC08', x'1002', x'9202', x'7E04', x'7104', x'5E04', x'5F04',
!     SRHA     SRHL     SRLS     STBR     STMD     STME    CRC12    CRC16  
    x'1801', x'9102', x'9002', x'E704'
!    LPSWR    SLHLS    SRHLS    TLATE  

!*end*
string(7) opcode
record(varfm)name  v
   integer op, base, disp, index, flags, p, n, reg
   routine mc error(string(255) s)
      selectoutput(0)
      printsymbol('*')
      write(current line, 3)
      space
      printstring(opcode)
      printstring(": ")
      printstring(s)
      newline
      selectoutput(object)
      while sym # ';' cycle
         sym = next;  readsymbol(next)
      repeat
   end
   routine get opcode
      opcode = "";  op = 0
      cycle
         sym = next;  readsymbol(next)
         exit if sym = '_'
         if length(opcode) # 6 start
            op = op<<6!!sym
            opcode = opcode.tostring(sym)
         finish
      repeat
      sym = next;  readsymbol(next)
   end
   integerfn find opcode
      integer high, low, p
      high = mc entries;  low = 1
      while high >= low cycle
         p = (high+low)>>1
         result = p if mcop(p) = op
         if mcop(p) > op then high = p-1 else low = p+1
      repeat
      mc error("unknown operation")
      result = 0
   end
   predicate value(integername n)
      n = 0
      false unless '0' <= sym <= '7'
      cycle
         n = n<<3!(sym-'0')
         sym = next;  readsymbol(next)
         true unless '0' <= sym <= '7'
      repeat
   end
   predicate register(integername r)
      false unless value(r)
      false unless 0 <= r <= 15
      true
   end
   predicate deal with plus minus
      integer sign, n
      true unless sym = '+' or sym = '-'
      sign = sym;  sym = next;  readsymbol(next)
      unless value(n) start
         mc error("invalid offset")
         false
      finish
      n = -n if sign = '-'
      disp = disp+n
      true
   end

   base = -1;  index = -1;  disp = 0
   get opcode
   p = find opcode;  return if p = 0
   flags = opflags(p)

   if flags&branch # 0 start
      reg = flags>>4&15;               !cond-code
   else unless register(reg)
      mc error("register 1?");  return
   else if sym # ','
      mc error("comma missing");  return
   else
      sym = next;  readsymbol(next)
   finish
   if flags&rr # 0 start
      unless register(base) start
         mc error("register 2?");  return
      finish
   else if sym = ' ';                    !named operand
      n = tag
      sym = next;  readsymbol(next)
      v == var(n)
      disp = v_disp
      if v_form = pgm label start;     !%label
         define reference(disp&X'FFF', r ref);   !make it look like a routine
         disp = 0;  Base = Code
         return unless deal with plus minus
      else
         base = actual(v_base) unless v_base = 0
         return unless deal with plus minus
         if sym = '(' start
            ->ix if base > 0
            ->ib
         finish
      finish
   else
      if sym = '-' and deal with plus minus start
         !only needs the side-effect of deal with ..
      else unless value(disp)
         mc error("displacement?");  return
      finish
      return unless deal with plus minus
      if sym = '(' start
ib:      sym = next;  readsymbol(next)
         unless register(base) start
            mc error("base register?");  return
         finish
         if sym = ',' start
ix:         sym = next;  readsymbol(next)
            if flags&indexed = 0 start
               mc error("no double indexed form");  return
            finish
            unless register(index) start
               mc error("index register?");  return
            finish
         finish
         if sym # ')' start
            mc error(") missing");  return
         finish
         sym = next;  readsymbol(next)
      finish
   finish
   if sym # ';' start
      mc error("form?");  return
   finish

   base = 0 if base < 0
   index = 0 if index < 0
   base = index and index = 0 if base = 0 and index # 0

   cput(flags&x'FF00'+reg<<4+base)
   if flags&ri1 # 0 start
      cput(disp&x'FFFF')
   else if flags&ri2 # 0 and flags&branch = 0
      cput(disp>>16);  cput(disp&x'FFFF')
   else if flags&rr = 0
      if disp>>14 # 0 or index > 0 start
         mc error("no RX3 form") and return if flags&indexed = 0
         cput(x'4000'+index<<8+disp>>16&x'FF')
      finish
      cput(disp&x'FFFF')
   finish
end

!                                                  >> SET DOPE VECTOR <<
routine  set dope vector
integer  t
   t = vub-vlb+1
   claim literal(4*reglen,3)
   select literal area
   dv = ca
   cword(1)
   cword(vlb);  cword(vub)
   cword(data size)
   select code area
   vub = t*data size;  vlb = vlb*data size
end

!                                                             >> PERM <<
routine  perm(integer  n)
constinteger  g0=1, g1=2, g2=4, g3=8, g4=16, g5=32, g6=64;   ! General Registers
constinteger f0=128, f2=256;                                 ! Floating Registers
constinteger  prot = (-1)<<15;                      ! protect stack around call
constshortintegerarray  rmap(0:8) = R0, R1, R2, R3, R4, P2, P1, FR0, FR2
integer  k,r,h
!
! **** N.B.  The following table must match the properties of the perm
!            routines in use.
constinteger perm routines = 50
constshortintegerarray  hazard reg(1:perm routines) =
      0,                                      ; !    1: ASSCHK
      G0+G3,                                  ; !    2: IEXP
      G0+F0+F2,                               ; !    3: REXP
      G0+G3+G4,                               ; !    4: SMOVE
      G0+G3+G5,                               ; !    4: SJAM
      G0+G1+G2+G3+G4+G5,                      ; !    6: SCONC
      G0+G5+G6,                               ; !    7: SRESLN
      0,                                      ; !    8: SRESV
      G0+G3+G4+G5,                            ; !    9: SCOMP
      F0+F2,                                  ; !   10: FRAC PT
      0,                                      ; !   11: SFCAP
      G0+G1+G2+G3+prot,                       ; !   12: SUBSTR
      G0+G1+G2,                               ; !   13: AREF1
      G0+G1+G4,                               ; !   14: AREF2
      G0+G1+G4+G5,                            ; !   15: AREF3
      G0+G1+G4,                               ; !   16: AREF4
      G0+G1+G2+G3,                            ; !   17: SETDV
      G0+G5,                                  ; !   18: ALLOC
      0,                                      ; !   19: SWJMP
      0,                                      ; !   20: SIGNAL
      0,                                      ; !   21: MULCHK
      0,                                      ; !   22: CAP16
      0,                                      ; !   23: CAP8
      G0+G1,                                  ; !   24: FCHK1
      0,                                      ; !   25: FCHK2
      G2+G3+G4,                               ; !   26: PENTC
      G0+G3,                                  ; !   27: RCOPY
      G0+G3,                                  ; !   28: RZERO
      0,                                      ; !   29: VSCHK
      G0+G3+G4,                               ; !   30: SMOVOPT
      G0+G1,                                  ; !   31: CHMAP
      G1,                                     ; !   32: FREESP
      G1+F2,                                  ; !   33: INT
      G0+G3+G4+G5,                            ; !   34: RECORD COMPARE
      G0+G1+G2+G3+G4+G5+G6,                   ; !   35: SET COMPARE
      G0+G3,                                  ; !   36: SET UNION
      G0+G3,                                  ; !   37: SET DIFFERENCE
      G0+G3,                                  ; !   38: SET INTERSECTION
      0,                                      ; !   39:
      0,                                      ; !   40:
      0,                                      ; !   41:
      0,                                      ; !   42:
      0,                                      ; !   43:
      0,                                      ; !   44:
      0,                                      ; !   45:
      0,                                      ; !   46:
      0,                                      ; !   47:
      0,                                      ; !   48:
      G0+G1+G2+G3+G4+G5+G6+F0+F2,             ; !   49: IOCP
      G0+G1+G2+G3+G4+G5+G6+F0+F2              ; !   50: ENTER TRACE

   h = hazard reg(n);                     ! property mask for nth. perm routine
   if claimed # 0 start;        ! maybe something to do .. perhaps
      k = h&x'7FFF';                  ! register mask
      r = 0
      while k # 0 cycle
         hazard(rmap(r)) if k&1 # 0
         k = k>>1
         r = r+1
      repeat
   finish

   ! forget all registers which are at risk
   r = ( ((h&(F0+F2)) << (FR0-P1))  !  (h&127) ) << 1;   ! ** N.B. P1 == G6
   forget reg(r)

   rxi(ADD,wsp,0,wdisp) if h < 0 and wdisp # 0
   define reference(n&255,p ref)
   rx(bal,link,code,n&255)
   if h < 0 start
      rxi(SUB,wsp,0,wdisp) if wdisp # 0
      wdisp = wdisp + basic frame + 256;       ! protect it
   finish
end

!                                                      >> DUMP TRACE <<
routine  DUMP TRACE
   if current line # last line start
      trace flag = 0
      perm(enter trace);  cput(current line)
   finish
end

!                                                         >> ASSEMBLE <<
! AMODE:
!  -2: alternate record format
!  -1: record format
!   0: procedure
!   1: %spec
!   2: initial call

routine Assemble(integer  amode, labs, names)
   switch c(33:127), Pc('A':'Z')
   recordformat  evfm(integer  low, high, events, label)
   record(evfm)  event = 0
   record(varfm)name  v
   record(varfm)name  gvar             {procedure var}
   record(stackfm)name  lhs, rhs, x
   integer  old frame, old extra frame, old jump
   integer  old temp base, old next temp
   integer true frame base, putative frame base, max frame, alt first, alt align=0
   integer  old var diags
   integer  gstart                      {first descriptor at this level}
   integer  label start                 {first label for this level}
   owninteger  free tag   = 0
   integer  max local     = 0
   integer  max parm      = 0
   integer  min parm      = 0           {P1, P2 parameter registers used ?}
   integer  mark assigned = 1           {mark VAR table entries as 'assigned' if # 0}
   integer  Closed        = Assigned    {assume it can't return}
   integer  Return Label  = 0           {label on return code}
   integer  px            = 0
   integer  proc ca       = ca
   integer  sw list       = 0
   integer  last a        = -1
   integer  line size     = 0
   integer  block index
   integer  j, k, t

   routinespec compile to string(record(stackfm)name v)
   routinespec  pop lhs
   routinespec  lrd(record(stackfm)name  v, integer  reg)
   routinespec  load(record(stackfm)name  v, integer  reg)
   routinespec  assign(integer  assop)
   routinespec  array ref(integer  mode)
   routinespec  operate(integer  n)
   routinespec  compare(record(stackfm)name  l,r, integer next)
   routinespec test zero(record(stackfm)name v)
   routinespec  header(record(varfm)name  v)
   routinespec  block mark(integer mark)
   integerfnspec  new tag

   old jump = uncond jump;  uncond jump = -1
   old var diags = var diags;  var diags = 0
   label start = labs
   old frame = frame;  old extra frame = extra frame;  extra frame = 0
   old temp base = temp base;  old next temp = next temp
   temp base = new temp;  Next Temp = New Temp
forget the lot:
   pdisp = 0;  wdisp = 0;  gdisp = -1;  event_events = 0
    abort(m'-1 ?') unless gdisp = -1;    !************????????????
   gvar == decvar;  gstart = names
   if amode >= 0 start;              ! NOT A RECORDFORMAT
      frame = basic frame;  ca = 0
      level = level+1;  abort(m'AM00') if level > 5 and spec = 0
      local = breg(level)
      activity(local) = -1
      gdisp = (p1-p2)*reglen
      Reset Optimisation Data if Spec = 0
      if amode = 0 start;          ! procedure, proc. parameter, %begin block
         block no = block no + 1;  block index = block no
         block mark(block start)
         if sym = 'H' start;                  ! %BEGIN block
            gdisp = -1
            if level = 1 start;        ! Initial %begin ?
               external id = program ep;      ! linkage to program entry
               otype = external;  potype = otype
            finish
            header(gvar)
         finish
      finish
   else
      if amode = -1 start;              ! record format
         gvar_extra = parms
         frame = 0
      finish
      true frame base = frame
      putative frame base = (frame+align)&(¬align)
      frame = putative frame base
      max frame = frame
      alt first = parms-1;       ! note start of this alternative list
   finish


!                                                       >> BLOCK MARK <<
routine  block mark(integer mark)
integer  k, limit
   k = direct
   cycle
      select output(k)
      print symbol(mark)
      if mark = block start start
         print symbol(block index)
         if k = object start;        ! procedure head diagnostics
            put(current line);  last line = -15;  !force a reset
            describe(-1,0,block name);      ! internal name for procedure
         finish
      else if k = direct;         ! %and mark = block end (by implication)
         abort(m'TAG?') if free tag > 32767;    ! too many pass3 tags
         put(ca>>1);            ! code size for this block (half words)
         put(var diags);         ! var. diags local to this block
         frame = (frame+align)&(¬align)
         k = frame + extra frame;      ! include in-frame array space
         if control & trusted = 0 start
            ! using checked (perm) entry sequence
            k = k>>2;            ! to full word units
            limit = 65535;       ! treated as unsigned 16 bits by perm
         else
            ! in-line entry sequence
            limit = 32767;       ! must be positive 2's complement (byte units)
         finish
         abort(m'FRM?') unless 0 < k <= limit
         put(k)
         print symbol(actual(local));   ! current display register
         put(event_events);             ! events-trapped mask
         put(event_label);                  ! Event block ep
         put(event_low);                    ! Event block %finish
         total ca = total ca + ca
      finish
      exit if k = object
      k = object
   repeat
   last ca = -1
end;            ! block mark

!                                                  >> SET FRAME PATCH <<
routine  set frame patch
   if diagnose < 0 start
      select output(report)
      print string("  Block index");  write(block index,1)
      newline
      select output(object)
   finish
   print symbol(frame patch)
end;          ! set frame patch

!                                                       >> DEFINE VAR <<
routine  define var
   integer  type, form, tf, size, format, s, new, round, dimension
   integer  ignore;          ! ** used to control dumping of diags **
   record(stackfm)  temp
   integer  k
!!!*** N.B. On machines with the PDP-11/VAX perversion relating to the order
!            of register bytes in store, the following table will have to
!            be changed as will a few constants in this routine.
!            The relevant piece of record format to consider is:
!               (%shortinteger xform %or %byteinteger  flag,form)
!            which must have the effect of mapping 'flag' onto the
!            more significant byte of 'xform'.
   constshortintegerarray  fmap(0:18) = 0,
      V in S,                           {simple variable}
      A in S,                           {name: pointer variable}
      pgm label,                        {label ** SPECIAL **}
      15,                               {record format **SPECIAL**}
      0,                                   {unused}
      0,                                {switch}
      proc bit<<8 + 0,                  {routine}
      proc bit<<8 + V in R,             {function}
      proc bit<<8 + V in S,             {map}
      proc bit<<8 + 5,                  {predicate}
      abit<<8 + V in S,                 {array}
      anbit<<8 + V in S,                {array name}
      (abit+label bit)<<8 + V in S,     {name array}
      (anbit+label bit)<<8 + V in S,    {name array name}
   ! external manifestations of array forms
      abit<<8 + V in REC,               {external array}
      anbit<<8 + V in REC,              {external array name}
      (abit+label bit)<<8 + V in REC,   {external name array}
      (anbit+label bit)<<8 + V in REC   {external name array name}
   constbyteintegerarray  vsize(0:8) = 0,4,2,1,8,0,0,4,8
   owninteger  prim no = 0
   ignore = 0
   ignore = 1 if amode < 0 or amode = 1;     ! no diags for specs of any kind!!
   internal id = "";  new = 0;  round = align
   decl = tag
   if decl = 0 start;           ! RECORD FORMAT ELEMENT NAME
      parms = parms-1;  abort(m'DFV1') if parms <= names
      decvar == var(parms)
      decvar = 0;    !(psr)
   else
      abort(m'DFV2') if decl >= parms
      decvar == var(decl)
      if decl > names start
         names = decl;  new = 1
         decvar = 0
      finish
   finish
   cycle
      sym = next;  read symbol(next);  exit if sym = ','
      if length(internal id) # extern len start
         internal id = internal id.to string(sym)
      finish
   repeat
   ignore = 1 if internal id = ""
   tf = tag;  read symbol(next)
   type = tf>>4;  form = tf&15
   size = tag;  read symbol(next)
   diag type = type;  diag form = form;  diag size = size
   if type = integers and size # 1 start;       ! INTEGER
      type = byte  and round = 0 if size = 2
      type = short and round = 1 if size = 3
      size = vsize(type)
   else if type = 2;                                          ! REAL
      type = reals
      ! *** for 8/32, 'round = 3' below should be changed to 'round = 7'
!?????????????      type = reall %and round = 3 %if size = 4;               ! LONG REAL
      size = vsize(type)
   else if type = 4;                               ! record
      type = records
      format = size
      decvar_format = format;  size = var(format)_length&x'FFFF' if format <= names
   else if type = 3;                   !  string
      type = strings
      round = 0
      decvar_length = size
      size = size + 1
   else
      size = vsize(type)
   finish
   decvar_length = size if type # strings
   decvar_type = type;  decvar_xform = fmap(form)
   otype = tag
   spec = (otype>>3)&1;  dimension = otype>>8&255;  otype = otype&7
   if otype # 0 start;        ! Set external linkage name if appropriate
      if otype >= external start
         if alias # "" start
            external id = alias
         else if otype = system
            external id <- system prefix.internal id
         else
            external id = internal id
         finish
         otype = external if otype <= dynamic      {external, system, dynamic}
      finish
   finish
   alias = ""
   if 7 <= form <= 10 start;          ! PROCEDURE
      gtype = spec
      if otype # 0 and spec # 0 start;        ! external spec
         if otype = primrt start
            primno = primno + 1
            decvar_flag = decvar_flag ! prim bit
            decvar_header = prim no;   ! *** THIS NEEDS FIXING ***
            return if prim no # 2;        ! not READ SYMBOL
            otype = external;  external id = read sym fn;   ! see "CALL"
         finish
         gfix(align)
         decvar_disp = ga;  decvar_base = gla
         external link(ep ref, 0, ga)
         return
      finish
      if gmode = 0 start;                ! NOT A PARAMETER
         potype = otype
         if new # 0 start;               ! NEW NAME
            decvar_disp = new tag;       ! Procedure ID
         finish
         block name = internal id if spec = 0
         return
      finish
      ignore = 1
      otype = 0;  size = 4;  data size = 4;  ! procedure parameter
   else
      data size = size
      if form # 1 start
         Round = Align
         if type = 0 start;                    ! General %name
            ignore = 1
            decvar_extra = gmode;        ! FOR LABELS
            type = general;  size = 8
            decvar_type = general
         else if form = array or form = name array 
            ignore = 1
            size = 0
            data size = reglen if form = name array
         else if form = array name or form = name array name 
            ignore = 1
            size = 2*reglen;  round = align;         ! array header
            decvar_header = -1
            abort(m'DFV3') unless 0 < dimension <= 7
            decvar_flag = decvar_flag ! dimension;   ! 'dim' in low order 3 bits
         else
            size = 4;                       ! integer (etc) %name
         finish
      finish
   finish

   if otype # 0 start;        ! OWN DATA
      if otype = con start;        ! CONST INTEGER ETC.
         data size = 0 if type=strings and form=1;      ! use actual size
         if form = 2 or form = arrayname or form = namearrayname start
            otype = 0;        ! Treat as special later
         else
            ignore = 1;      ! no diags for named constants
         finish
      else
         gfix(round)
         set diag(gla,ga) if ignore # 1
      finish
      own type = type;  own form = form
      own type = integers and data size = 4 if form = 2
      decvar_header = -1
      if spec = 0 start
         if form = array or form = name array start
            own form = array;         ! to simplify subsequent test at 'A'
            decvar_flag = decvar_flag&(¬array bits)!(anbit!1);   ! 1-D %name
            ! mark as candidate for MHR subscript scaling if bounds do not
            ! exceed -32768 <= x <= 32767 and  data size <= 32767
            decvar_flag = decvar_flag ! cheap array bit if   c
                              0 < decvar_length <= 32767 and c
                              -32768 <= vlb {<= 32767}  and  c
                              {-32768 <=} vub <= 32767
                              {Note: vlb <= vub-1}
            gfix(align)
            set dope vector;      ! N.B.  changes vlb, vub
            if otype # con start
               decvar_disp = ga;  decvar_base = gla
               gword rel(ga+8-vlb);        ! @A(0)          (in gla area)
               gword crel(dv);              ! @dope vector   (in code area)
            else;           !  %const ...... %array
               claim literal(vub,align);  ! no header this time (it's in GLA)
               select literal area
               decvar_disp = ga;  decvar_base = gla
               gword crel(ca-vlb)
               gword crel(dv)
               select code area
            finish
            external link(data defn,0,ga-8) if otype = external
         finish
      else
               {to RECORD variant with 1-dim bit set, if nesc}
         decvar_xform = (decvar_xform+3) ! (assigned<<8)
         decvar_xform = decvar_Xform!1<<8 if Form >= Array
         decvar_base = gla;  decvar_disp = 0;  decvar_extra = ga+8
         external link(data ref,0,ga)
      finish
      return
   finish

   if form = 3 start;           !%label
      decvar_disp = new tag
      return
   finish

   if form = switch start
      decvar_extra = vlb;  decvar_length = vub-vlb+1
      decvar_format = free tag + 1;       ! base tag
      claim literal((vub-vlb+1+2)*2,1)
      decvar_base = code;  decvar_disp = litmax
      select literal area
      cput(vlb);  cput(vub);            ! switch bounds
      for s = vlb,1,vub cycle
         free tag = free tag + 1
         define reference(free tag,sw ref)
         cput(free tag)
      repeat
      select code area
      return
   finish

   if form = record format start
      if gmode # 0 start
         frame = decvar_length if decvar_length > frame
      else
         gtype = -1;  spec = -1
      finish
      return
   finish

   decvar_base = local
   if gdisp >= 0 and decvar_flag & array bits = 0   c
                  and ( (decvar_form = a in s and decvar_type # general)    c
                    or (decvar_form = v in s and decvar_type <= byte) ) start
      decvar_disp = gdisp;  gdisp = gdisp - reglen
      decvar_disp = decvar_disp + decvar_type if    c
                        decvar_form = v in s and short <= decvar_type <= byte
      decvar_flag = decvar_flag ! P in R;      ! Parameter in Register
      min parm = min parm + 1;                 ! for use by 'HEADER'
      if control & suppress = 0 start
         temp_form = decvar_form;  temp_type = decvar_type
         temp_xbase = decvar_base;  temp_disp = decvar_disp
         if temp_form = A in S start
            temp_form = V in S;  temp_type = integers
         finish
         associate(temp, p1 - (min parm-1))
      finish
   else
      frame = (frame+round)&(¬round)
      max local = frame
      decvar_disp = frame
      frame = frame + size
      alt align = alt align ! round
   finish
   set diag(local,decvar_disp) if ignore = 0
end;         !    define var

!                                                               >> CHECKABLE <<
predicate  checkable(record(stackfm)name  v)
   ! Presumes test on 'CONTROL&CHECK UNASS' in line for speed
   ! Note that a string temporary (v_type = 0) yields FALSE
   false if v_form = constant or v_form = AV in S
   false if v_flag & assigned # 0
   true if v_type = integers or v_type = strings or v_type >= reals
   false
end;             ! checkable

!                                                       >> DESCRIPTOR <<
! N.B.  Note that the record zero operation is used, among
!       other things, to set the link field to NULL. This
!       equivalence between binary zero and NULL links must
!       be maintained.
record(stackfm)map  descriptor
record(dfm)name  d
record(stackfm)name  v
   stp = stp+1;  abort(m'DSC1') if stp > max depth

   v == desc asl;  abort(m'DSC2') if v == null
   d == dasl;  abort(m'DSC3') if d == null
   desc asl == v_link;  v = 0
   dasl == d_link;  d_link == using_link;  using_link == d
   d_d == v
   result == v
end

!                                                             >> DROP <<
routine  drop(record(stackfm)name  descriptor)
record(dfm)name  p,q
   p == using
   cycle
      q == p_link
      abort(m'DROP') if q == null
      exit if q_d == descriptor
      p == q
   repeat
   p_link == q_link
   q_link == dasl;  dasl == q
   descriptor_link == desc asl;  desc asl == descriptor
end

!                                                           >> VSTACK <<
routine  vstack(integer  var no)
record(varfm)name  w
   abort(m'VSTK') unless 0 <= var no <= max vars
   w == var(varno)
   lhs == descriptor
   stacked(stp)_v == lhs
   lhs_base = w_base
   lhs_disp = w_disp
   lhs_format = w_format
   lhs_extra = w_extra
   lhs_type = w_type
   lhs_length = w_length
   lhs_header = w_header
   lhs_link == null
   lhs_type = w_type;  lhs_xform = w_xform
   lhs_dim = w_flag&7;      ! in case it's an array
   lhs_varno = varno
   monitor(lhs, "V stack") if diagnose&1 # 0
end

!                                                           >> SSTACK <<
routine  sstack(record(stackfm)name  v)
record(stackfm)name  t
   t == descriptor;  t = v
   stacked(stp)_v == t
   monitor(t, "S STACK") if diagnose&1 # 0
end

!                                                          >> C STACK <<
routine  c stack(integer  n)
   rhs == descriptor
   rhs_base = 0
   rhs_disp = n
   rhs_type = integers
   rhs_form = constant
   stacked(stp)_v == rhs
   monitor(rhs, "C stack") if diagnose&1 # 0
end

!                                                           >> C LOAD <<
routine  cload(integer  value, reg)
   c stack(value)
   pop lhs
   lrd(lhs,reg)
end

!                                                             >> SSET <<
routine  sset(integer  base, disp, xform, extra)
   rhs == descriptor
   rhs_base = base
   rhs_disp = disp
   rhs_type = integers
   rhs_xform = xform
   rhs_extra = extra
   rhs_link == null
   stacked(stp)_v == rhs
   monitor(rhs, "SSET") if diagnose&1 # 0
end

!                                                          >> SET LHS <<
routine  set lhs
   lhs == stacked(stp)_v
   monitor(lhs, "SET LHS") if diagnose&1 # 0
end

!                                                         >> SET BOTH <<
routine  set both
   abort(m'SETB') if stp <= 1
   lhs == stacked(stp-1)_v
   rhs == stacked(stp)_v
   if diagnose&1 # 0 start
      monitor(lhs, "BOTH LHS")
      monitor(rhs, "BOTH RHS")
   finish
end

!                                                          >> POP LHS <<
routine  pop lhs
   abort(m'POPL') if stp <= 0
   lhs == stacked(stp)_v
   stp = stp-1
   monitor(lhs, "POP LHS") if diagnose&1 # 0
end

!                                                         >> POP DROP <<
routine  pop drop
   pop lhs
   monitor(lhs, "POP DROP") if diagnose&1 # 0
   drop(lhs)
end

!STRING PROCESSING
!                                                      >> DUMP STRING <<
routine  dump string(integer  max)
integer  j
   if max = 0 start;         ! DUMP AS MUCH AS NEEDED
      max = cslen+1
   else;                     ! DUMP NO MORE THAN MAX
      if cslen+1 > max start
         ! String constant too long - warn and truncate
         if cslen # x'80' or current string(1) # x'80' start
            warn(5);  current string(0) = max-1
         finish
      finish
   finish
   if otype = con start
      select literal area
      lit byte(current string(j)) for j = 0,1,max-1
      select code area
   else;         !  %own
      gbyte(current string(j)) for j = 0,1,max-1
   finish
end

!                                                       >> GET STRING <<
routine  get string
   integer  l
   l = next;                  !length
   cslen = 0
   while l > 0 cycle
      l = l-1
      read symbol(next)
      cslen = (cslen+1)&255;  current string(cslen) = next
   repeat
   readsymbol(next)
   if next # 'A' and next # '$' start
      if next = '.' and cslen = 1 and
                            control&(check capacity!check unass) = 0 start
         cstack(current string(1))
         Rhs_Flag = Rhs_Flag!Quick Conc
         return
      finish
      cstack(0);  rhs_type = strings
      otype = con;        ! anonymous %const
      rhs_base = code;  rhs_xform = VinS!(assigned<<8);  rhs_format = cslen+1
      if cslen # 0 or null string = 0 start
         claim literal(cslen+1,1);   ! Alignment req'd for buffer flushing
         rhs_disp = lita;  dump string(0)
         null string = rhs_disp if null string = 0 = cslen
      else
         rhs_disp = null string
      finish
   else
      cstack(0);         ! explicit string initialisation
   finish
end

!                                                    >> REAL CONSTANT <<
integerfn  real constant(integer  force)
owninteger  last = 0, next = 0
integer  j,k
ownintegerarray  val(0:31) = 0(32)
ownshortintegerarray  index(0:31) = 0(32)
   k = integer(addr(rvalue))
   if otype # con start
      gfix(3);  gword(k)
      result = ga-4
   finish
! deal with %const anonymous or not
   if force = 0 start
      j = last
      cycle
         -> FOUND if val(last) = k
         last = (last+1)&31
         exit if last = j
      repeat
      claim literal(single,single-1);      ! anonymous value not in cache
   finish
   next = (next+1)&31;  last = next
   select literal area
   val(last) = k;  index(last) = ca
   cword(k)
   select code area
FOUND:
   result = index(last)
end;                   ! real constant

!LABEL PROCESSING
!                                                          >> NEW TAG <<
integerfn  new tag
   free tag = free tag + 1
   result = free tag
end

!                                                        >> NEW LABEL <<
record(labelfm)map  new label
   labs = labs+1;  abort(m'NLBL') if labs > max labels
   result == labels(labs)
end

!                                                             >> FIND <<
record(labelfm)map  find(integer  label)
integer  lp
record(labelfm)name  l
   lp = labs
   while lp # label start cycle
      l == labels(lp)
      result == l if l_id = label
      lp = lp-1
   repeat
   result == null
end

!                                                     >> DEFINE LABEL <<
routine  define label(integer  label)
   integer ltag, new
   record(labelfm)name  l
   record(envfm)name E
   cc ca = 0               {must forget condition code}
   new = 0
   return if label = 0;        ! JUMP AROUND PROCEDURE
   if label < 0 start
      ltag = -label
      new = 1
   else
      l == find(label)
      if l == null start
         l == new label
         l_id = label;  l_tag = new tag
         new = 1
      else
         if l_tag < 0 and label >= 0 start
            l_tag = new tag
            new = 1
         finish
      finish
      l_tag = l_tag ! bit15
      ltag = l_tag
   finish
   if new # 0 start
      e == environment(label)
      e_label = 0 if e ## null
   finish
   define tag(ltag & x'7FFF')
   merge environment(label) if uncond jump # ca
   restore environment(label)
   if trace flag # 0 start
      dump trace if next # ':' and next # 'L'
   finish
   uncond jump = 0;            ! YOU CAN GET HERE !
   mark assigned = 0;      ! can't be sure any more
end;        ! define label

!                                                          >> JUMP TO <<
routine  jump to(integer  label, cond, def)
record(labelfm)name  lab
integer  ref
   invert = 0
   Cond = Inverted(Cond) if Cond&16 # 0
   if def >= 0 start;           ! Compiler defined label
      return if label = 0;          ! jump round routine
      if label < 0 start
         j tag = -label
      else
         lab == find(label)
         if lab == null start
            lab == new label
            lab_id = label;  lab_tag = new tag
            remember environment(label)
         else if lab_tag < 0 and def = redefine old
            lab_tag = new tag
            remember environment(label)
         else
            merge environment(label) if lab_tag > 0
         finish
         j tag = lab_tag&x'7FFF'
      finish
   else;                         ! Tag internal to pass 2
      jtag = label;      ! *** N.B. This is %not a pass1-visible label ***
   finish
   if cond = jump then ref = j ref else ref = c ref
   define reference(j tag,ref)
   cput(jtag<<4 + cond&15);  cc ca = cc ca + 2;   ! these two bytes can't change CC
   if cond = jump start
      uncond jump = ca;      ! no way past here
      trace flag = control&trace if next = ':';    ! to catch 'else', 'repeat' etc
   else
      trace flag = control&trace;   ! maybe trace flow on next line
   finish
   mark assigned = 0
end;             ! jump to

!                                                            >> FLOAT <<
routine  float(record(stackfm)name  v, integer r)
! Convert 'v' into floating point form
integer  k
!!!!!%longreal  x
   r = fpr if r = anyf
   if const(v) start
      if v_disp = 0 start
         hazard(r);  claim(r)
         rr(sub,r,r);  claim(r)
         v_type = reall;  v_form = v in r;  v_base = r
      else
         rvalue = v_disp;       ! ** IMPLICIT FLOATING **
         otype = con;  k = real constant(0)
         v_xform = (assigned<<8) ! V in S;  v_type = reals
         v_base = code;  v_disp = k
      finish
   else
      load(v,any)
      rr(flr,r,v_base);  claim(r)
      v_form = v in r;  v_type = reall
      v_base = r
   finish
end

!                                                              >> LRD <<
routine  lrd(record(stackfm)name  v, integer  reg)
! load, release and drop
   load(v,reg)
   release(v_base)
   drop(v)
end

!                                                           >> QUICK LOAD <<
routine QUICK LOAD(integer reg, form, base, disp)
   record(stackfm) v
   v = 0
   v_type = integers;  v_form = form
   v_base = base;      v_disp = disp
   load(v, reg)
end

!                                                           >> REDUCE <<
routine  reduce(record(stackfm)name  v)
integer  type, xform, disp, base
   xform = v_xform - 3;         ! X in REC => X in S
   type = v_type
   disp = v_disp;  base = v_base
   v_disp = v_extra;  v_type = integers;  v_form = v in s
   load(v,any)
   v_type = type;  v_xform = xform & (¬(assigned<<8))
   v_disp = disp
end

!                                                             >> AMAP <<
routine  amap(record(stackfm)name  v)
! convert V into a descriptor for the address of V
integer f
constshortintegerarray  map(0:15) =
   -1, -2, -3, -4, av in s, -5, v in s, av in rec, -6, v in rec,
   -7, -8, -9, -10, -11 {PGM LABEL}, -12 {record format}
   f = map(v_form)
   if f < 0 start
      abort(m'AMAP') unless v_form = pgm label
      ! Deal with ADDR(pgm label)
      f = gpr;  forget reg(1<<f)
      define reference(v_disp&x'FFF',r ref)
      rx(LA,f,code,0)
      v_type = integers;  v_xform = VinR
      v_base = f; v_disp = 0
      claim(f)
      return
   finish
   if (f = VinREC or f = AVinREC) and v_disp = 0 start     {eliminate redundant LOAD}
      if f = VinREC then f = AinS else f = VinS
      v_disp = v_extra
   finish
   v_type = integers;  v_form = f
end

!                                                                 >> AMAPS <<
routine  amaps(record(stackfm)name  v)
integer  t,l
   t = v_type;  l = v_length
   amap(v)
   return if t # strings;        ! put length in top byte
   reduce(v) if v_form >= V in REC
   load(v,any) if v_form = V in S or v_Form = AinS
   !! It must be:   const, V in R or AV in S
   v_disp = v_disp + l<<24
   v_form = AV in S
end

!                                                             >> VMAP <<
routine  vmap(record(stackfm)name  v)
   ! The inverse of AMAP:  i.e. vmap(amap(x)) => x
   integer  mod, f, t
   constshortintegerarray  map(0:8) =
         v in s, v in s, -1, -2, a in s, v in s, -3, a in rec, v in rec
   mod = 0
   if v_oper # 0 start
      if (v_oper=add or v_oper=sub) and const(v_link) start
         mod = v_link_disp 
         mod = -mod if v_oper = sub
         v_oper = 0;  drop(v_link)
      finish
      load(v,any)
   else if v_form = a in s or v_form = a in rec
      T = V_Type
      Amap(V)
      load(v,any)
      V_Type = T;  V_Form = VinS
   finish
   f = map(v_form);  abort(m'VMAP') if f < 0
   v_form = f
   v_disp = v_disp + mod
end;             !  v map

!                                                          >> ADDRESS <<
routine  address(record(stackfm)name  v, integer  mode)
! convert V into a form in which it is directly addressable
! MODE parameter specifies what type of result is required.
!            >= 0 : a value (RHS)
!             < 0 : a name  (LHS)
! Further, if MODE > 0, the value is taken to specify the target register
! for any LOAD which may be generated.
integer  type, form, reg, d, cr
ownrecord(stackfm)name  last == (0);     ! ***** null actually ****** UGH 
   monitor(v, "ADDRESS") if diagnose&2 # 0
   reg = mode
   if reg <= 0 start
      reg = any
      reg = anyf if v_type >= reals or (v_oper # 0 and floating(v))
   finish
   cr = reg;               !*psr*
   if v_oper # 0 start;           ! compound object
      if v_oper = ADD and const(v_link) and v_type <= BYTE start
         d = v_link_disp;  drop(v_link)
         v_oper = 0
         load(v,reg)
         v_disp = d;  v_form = AV in S
      else
         load(v,reg)
      finish
      ->SET CR
   finish
   form = v_form;  type = v_type
   if form >= V in REC start
      reduce(v);  form = v_form
   finish
   if control & suppress = 0 start
      cheap reg = cr
      cheapen(v,mode)
      cr = cheap reg
      form = v_form
   finish
   ->SET CR if form = V in R or form = constant
   if form = AV in S start
      if v_base = 0 start
         v_form = constant
      else if v_disp = 0
         v_form = V in R
      finish
      ->SET CR
   finish
   if form = A in S start
      v_form = V in S;  v_type = integers
      load(v,any)
      v_type = type;  v_xform = (v_flag&(¬assigned))<<8 ! V in S;  v_disp = 0
      form = V in S
   finish
   if not last == v start;         ! *** FRIG: to prevent mutually recursive loop
      last == v
      if mode >= 0 and ((control&check unass#0 and v_type#strings  c
                                 and checkable(v)) or v_type = byte) start
         load(v,reg)
      finish
      last == null
   finish

SET CR:

   cheap reg = cr
end;             ! address

!                                                             >> LOAD <<
routine  load(record(stackfm)name  v, integer  r)
! load the entity described by V into register R

record(stackfm)name  w
switch  f(constant:a in rec), iop(not:rdiv), rop(not:rdiv)
record(stackfm)  z
record(stackfm)name  temp rhs
integer  op, d, type, temp, n, uflag

      constbyteintegerarray  twin(R0:R15) =
               R1,R0, R3,R2, R5,R4, R7,R6, R9,R8, R11,R10, R13,R12, R15,R14


   routine PICKUP(record(stackfm)name V)
      integer  old
      load(v, r)
      if R = Any or R = AnyF start
         old = R;   R = V_Base
         return if Activity(R) = 1 or (Activity(R)=2 and W_Base = R)
         if old = Any then R = Gpr else R = Fpr
         Load(V, R)
      else
         abort(m'Pick') if activity(r) # 1
      finish
   end

   monitor(v, "LOAD") if diagnose&2 # 0
   -> realv if floating(v) or fr0 <= r <= fr14 or r = anyf
   op = v_oper;  v_oper = 0
   if op # 0 start
      w == v_link;  {address(w,0)};   ! records reduced here
      load(w,any) if w_base = r # v_base;    ! *** FRIG: to avoid problem
                                          ! with HAZARD and e.g.  -> sw( -A(j) )
      -> iop(op)
   finish
   amap(v) if v_type = 0 or v_type = strings or v_type = records
   address(v,r)
   if r = any start
      return if v_form = VinR
      if v_form = AV in S and activity(v_base) = 1 and -15 <= v_disp <= 15 start
         r = v_base
      else
         r = gpr
      finish
   else
      if v_base = r start
         if activity(r) > 1 start      {protect other uses}
            release(r);  v_base = 0
            hazard(r)
            claim(r);    v_base = r
         finish
      else
         hazard(r)
      finish
   finish
   -> f(v_form)

f(av in rec):
f(a in rec):
f(v in rec):
f(A in S):
   abort(m'LD1');         ! These forms should have been simplified by ADDRESS

f(av in s):
f(constant):
   abort(m'LD2') if v_type >= reals
   rxi(lw,r,v_base,v_disp)
   forget reg(1<<r);  associate(v,r) if r # v_base;    ! e.g.LHI 12,1(12)
CSETI:
   v_type = integers
CSET:
   v_form = v in r
   v_base = r;  v_disp = 0
   claim(r)
   return

f(v in r):
   return if v_base = r
   rr(lw,r,v_base);  forget reg(1<<r) 
   v_base = r
   claim(r)
   return

f(v in s):
   uflag = control & check unass
   if integers < v_type < reals start
      abort(m'LD3') if short # v_type # byte
      uflag = 0
   else
      uflag = 0 if v_Flag&assigned # 0 or not checkable(v) or v_Type = 255
   finish
   if V_Type = 255 start
      V_Type = Short
      Rxd(LHL, r, v)
      Forget reg(1<<r)
   else
      rxd(lw,r,v)
      forget reg(1<<r);  associate(v,r)
   finish
   if uflag # 0 start
      if v_type < reals start
         v_type = integers
         if level # 5 start
            rr(clw,r,r12)
         else
            rx(clw,r,code,unass)
         finish
      else
         v_type = reall
         rx(cmp,r,code,unass)
      finish
      rr(bal,link,code)
      v_flag = v_flag & (¬assigned);   ! only one level remembered (1 bit !!)
   finish
   -> CSET

! integer operations
iop(and):
   if control&check unass = 0 and w_form = constant start
      address(v, r)
      if w_disp = x'FFFF' start
         if v_form = VinS and (v_type = integers or v_type = short) start
            v_disp = v_disp+2 if v_type = integers
LOADL:
            v_type = 255
            drop(w)
            Load(V, R)
            return
         finish
      else if w_disp = 255
         drop(w)
         if v_form = VinR start
            r = gpr if r = any
            rr(LBR, r, v_base)
            ->CSETI
         finish
         if v_type = integers start
            v_disp = v_disp+3
         else if v_type = short
            v_disp = v_disp+1
         finish
         v_type = byte
         load(v, r)
         return
      finish
   finish
   {** Drops through **}
iop(add):
iop(sub):
iop(or):
iop(xor):
   pickup(v)            {sets R}
   address(w, 0)        {**Moved down one line**}
   rxd(op,r,w)
   -> end op

iop(rsh):
   if control&check unass = 0 and w_form = constant and w_disp = 16 start
      address(v, r)
      ->LOADL if v_form = VinS and v_type = integers
   finish
iop(lsh):
   if w_form # constant and control&check capacity # 0 start
      load(w,r2);  perm(vschk)
   finish
   pickup(v)               {sets R}
   if w_form = constant start
      warn(6) unless 0 <= w_disp <= 8*reglen-1
   else;          !  variable shift
      load(w,any) if w_form # V in R
      w_disp = 0
   finish
   rxi(op,v_base,w_base,w_disp)
   -> end op

! these operations are changed immediately into binary subtracts
! and should themselves never appear in LOAD
!         -x   =>   0 - x
!         ¬x   =>  -1 - x   (assumes 2's complement)
iop(not):
iop(neg):
   abort(m'LD4')

iop(div):
   if w_form = constant start
      n = power(w_disp)
      if n > 0 start
         Pickup(V)         {make sure it's in the correct register}
         Test Zero(v);  r = v_base
         claim(r)
         d = 1;  d = 2 if n > 4;    ! 1 or 2 halfwords
         skip(d, greater or equal)
         rxi(ADD, v_base, 0, ¬((-1)<<n))
         rxi(SRA, v_base, 0, n)
         ->END OP
      finish
   finish
   {** Drops through **}

! *** N.B. ***
!   The multiply routine below is not intended for use in array subscript
!   calculation as it will include an overflow check.  Currently all in-line
!   subscript scaling uses shift or 'multiply halfword' instructions.
iop(mul):
iop(rem):
   if r = any start
      n = 0;  n = 1 if op = MUL
      if in free reg(v) and actual(v_base)&1 = n      c
                         and activity(twin(v_base)) = 0 start
         temp = v_base
         temp = twin(temp) if op # MUL
      else
         temp = even odd pair
      finish
   else
      if actual(r)&1 # 0 and activity(twin(r)) = 0 start
         temp = r
      else
         temp = even odd pair
      finish
   finish
   n = twin(temp)
   claim(n);  load(v,temp)
   release(n);  hazard(n);  claim(n)
   d = op
   if op # MUL start
      rr(lw,n,temp);  claim(temp)
      rxi(sra,n,0,31);        ! propagate sign
      d = div
   finish
   forget reg( (2+1)<<n );               ! forget N,TEMP (adjacent)
   address(w, 0)
      ! Note complication below because machine op-code only caters
      ! for the cases INTEGER*INTEGER, INTEGER//INTEGER,  rem(INTEGER,INTEGER)
      ! Short, byte and constant multipliers must therefore be preloaded
      ! into a register
   load(w,any) if w_form = constant or w_form = AV in S or w_type # integers
   rxd(d,n,w)
   release(n)
   if op = MUL start
      if control & check capacity # 0 start;     ! overflow check
         if n # R0 start
            claim(n);  rr(LW,r0,n)
         finish
         perm(mulchk)
      finish
   else if op = rem
      ! Interested in remainder not dividend
      claim(n);  release(temp)
      d = temp;  temp = n;  n = d
   finish
   v_base = temp;  v_disp = 0;  v_form = v in r
   load(v,r) if temp # r
   -> end op

! Special multiply routine used for array subscript scaling where all values
! involved are in range: -32768 <= x <= +32767
iop(mult16):
   if r = v_base or v_type = byte start
      load(v,any)
   else
      address(v,0)
      load(v,any) if v_form # V in S and v_form # V in R and v_form # constant
   finish
   v_disp = v_disp+reglen//2 if v_type = integers;    !  ** halfword instruction!! **
   pickup(w);                       ! scale factor (data size) - & sets R
   rxd(mult16,r,v)
   v_base = r;  v_disp = 0;  v_xform = V in R
   -> end op

iop(exp):
   load(v,r3);   load(w,r2)
   release(r3);  release(r2)
   perm(iexp)
   claim(r1);  v_base = r1
   -> end op

iop(conc):
   address(v, r)
   if v_type # 0 start
      pdisp = basic frame if pdisp = 0
      ! N.B.  Must %not corrupt LHS/RHS in LOAD
      temp rhs == rhs
      sset(wsp,pdisp,V in S,0);  rhs_type = strings;  rhs_length = 255
      rhs == temp rhs
      sstack(v);  v_Base = 0
      assign(1)
      claim(r2)
      v_type = strings;  v_form = VinS
      v_base = r2;       v_disp = 0
      v_length = 255;           ! it's a temporary now
      pdisp = pdisp + 256;      ! ... so protect it
   finish
   if w_flag & quick conc # 0 start;        ! S = S.tostring(sym)
      z = v;  claim(z_base);  z_type = byte
      load(z, any)
      rxi(LW, z_base, z_base, 1);  claim(z_base)              {length+1}
      load(w, any)                                            {character}
      v_index = z_base;  v_type = byte;  v_form = VinS
      rxd(ST, w_base, v);  release(w_base)
      claim(v_base);  v_index = 0
      rxd(ST, z_base, v)
   else
      load(v, r2)
      load(w,r1);  release(r1);  release(r2)
      n = v_length;  n = 255 if n = 0
      perm(sconc);  cput(n)
      v_form = VinS
   finish
   claim(v_base)
   v_type = 0
   if r # any and r # 0 start;          ! not from OPERATE
      load(v,r);  v_type = 0;  v_form = v in s
   finish
   drop(w)
   return         {Note: nothing to forget}

! floating operations
REALV:
   abort(m'LD5') if r = any;      ! should be floating register
   op = v_oper;  v_oper = 0
   if op # 0 start
      w == v_link
      -> rop(op)

rop(not):
rop(lsh):
rop(rsh):
rop(and):
rop(or):
rop(xor):
rop(conc):
rop(mult16):
      abort(m'LD6');              !  inappropriate operator

rop(rdiv):
      op = div
rop(div):
rop(add):
rop(sub):
rop(mul):
      if w_type < reals start
         float(w, anyf)
         if w_form = V in R # v_form and v_type >= reals    c
                                           and (op = add or op = mul) start
            z = v;  v = w;  w = z;      ! interchange
         finish
      finish
      Pickup(v);  r = v_base
      Address(W, 0)
      rxd(op,r,w)
      -> end op

rop(neg):
      abort(m'LD7');               ! should have been modified by OPERATE

   finish
   float(v, r) if v_type < reals
   address(v, r)                  {AFTER float to prevent optimising constants}
                                   {e.g. I=0;  R=0}
   if v_form = v in r start
      return if r = anyf or v_base = r
      hazard(r);  rr(lw,r,v_base)
      v_base = r;  claim(r)
      return
   finish
   if r = anyf start
      r = fpr
   else
      hazard(r) unless r = v_base
   finish
   abort(m'LD8') unless fr0 <= r <= fr14
   -> f(v_form)

rop(rexp):
   abort(m'LD9') if w_type >= reals
   load(v,fr2);  load(w,r1)
   release(fr2);  release(r1)
   perm(fexp);         ! floating exponent
   claim(fr0);  v_base = fr0

END OP:
   V_Type = Integers if V_Type <= Byte
   forget reg(1<<v_base) 
   drop(w)
end;             ! load

!                                                              >> COP <<
routine  cop(integer  op, record(stackfm)name  lh,rh)
! perform a compile-time operation
constinteger  fp tens=70;       ! max powers of ten available in floating point
integer  l,r
switch  s(1:rdiv)
   integerfn  p10(integer  n);        ! approximate powers of ten in 'n'
   integer  value, power
      value = 1;  power = 0
      cycle
         result = power if value >= n
         value = value*10
         power = power+1
         abort(m'COP1') if power > 100
      repeat
   end
   l = lh_disp;  r = rh_disp
   -> s(op)
s(NEG):
s(NOT):
s(CONC): abort(m'COP2')

s(ADD):  l = l+r;   -> EXIT
s(SUB):  l = l-r;   -> EXIT
s(OR):   l = l!R;   -> EXIT
s(AND):  l = l&r;   -> EXIT
s(XOR):  l = l!!R;  -> EXIT
s(LSH):  l = l<<r;  -> EXIT
s(MUL):  l = l*r;   -> EXIT
s(MULT16):  l = l*r;   -> EXIT
s(RSH):  l = l>>r;  -> EXIT
s(EXP):  l = l^^r;  -> EXIT
s(DIV):  warn(1) and r = 1 if r = 0
         l = l//r;  -> EXIT
s(REM):  warn(1) and r = 1 if r = 0
         l = l-l//r*r;  -> EXIT
s(REXP):
         warn(7) and r = 0 if p10(|l|) * r > fp tens
         rvalue = l^r;     !  **** implicit floating ****
         -> REAL
s(RDIV):
         warn(1) and r = 1 if r = 0
         rvalue = l/r;      !  **** implicit floating ****
REAL:
   otype = con;  l = real constant(0)
   lh_base = code
   lh_type = reall;  lh_form = V in S
EXIT:
   lh_disp = l
end
!                                                          >> OPERATE <<
routine  operate(integer  oper)
! perform the operation OPER on the top two elements of the stack.
!   (single element for unary operators)
record(stackfm)name  lh,rh,with
integer  key,lcon,rcon,wcon,lop
constbyteintegerarray  transitive(add:rdiv) =
         0,0,1,15(2),1(3),15(2),1,15(4)
constbyteintegerarray  commutative(add:rdiv) =
         1,0,1,0,0,1(3),0(2),1,0(4)
constshortintegerarray  nop value(add:rdiv) =
         0,0,1(2),0,-1,0(4),1,1(4)
   routine  pickup(record(stackfm)name  v)
      if floating(v) then load(v,anyf) else load(v,any)
   end

   stp = stp-1
   lcon = 0;  rcon = 0;  wcon = 0
   lh == stacked(stp)_v
   if const(lh) start
      lcon = 1
   else if lh_type # strings and lh_type # 0
      address(lh, 0) if lh_oper = 0
   finish
   rh == stacked(stp+1)_v
   if const(rh) start
      rcon = 1
      if oper = sub start
         oper = add;  rh_disp = -rh_disp
      finish
   finish
   if lh_oper # 0 start
      lop = lh_oper
      with == lh_link
      wcon = 1 if const(with)
      if wcon&rcon # 0 start;       !!  fold
         key = transitive(oper)!transitive(lop)
         if key = 0 or (key = 1 and oper = lop) start
            with_disp = -with_disp and lop = add if lop = sub
            cop(oper,rh,with);  drop(with)
            lh_link == rh
            lh_oper = lop
            -> STRIP NOP
         finish
      finish
      pickup(lh)
   finish
   if rcon # 0 start
      if lcon#0 or (oper=ADD and lh_type=INTEGERS and
                                 (lh_form=VinR or lh_form=AVinS)) start
         lh_form = AV in S if lh_form = VinR
         cop(oper,lh,rh);  drop(rh)
         return
      finish
   finish
   if rh_oper # 0 start
      pickup(rh)
   else if rcon # 0 and rh_disp = 2
      ! treat *2 (real & integer) and ^2, ^^2 specially
      if oper = mul or oper = exp or oper = rexp start
         if oper = mul then oper = add else oper = mul
         rh = lh;  rcon = 0;  claim(rh_base)
      finish
   finish
   if commutative(oper) # 0 and  ( lcon # 0      c
            or  ( lh_form # VinR and rh_form = VinR and
                                       activity(rh_base) >= 0 ) ) start
      rh_link == lh
      stacked(stp)_v == rh
      rh_oper = oper
      ! keep various items valid for use at STRIP NOP:
      with == rh;  rh == lh;  lh == with
      rcon = lcon
   else
      lh_oper = oper;  lh_link == rh
   finish
STRIP NOP:
   if rcon # 0 start
      if rh_disp = nop value(oper) start
         lh_oper = 0;  drop(rh)
      else if oper = MUL and control&check capacity = 0
         key = power(rh_disp)
         if key > 0 start
            lh_oper = lsh;  rh_disp = key
         finish
      finish
   finish
end;         !     operate

!                                                           >> ASSIGN <<
routine  assign(integer  assop)
! ASSOP =   -1:  parameter assignment
!            0:  == assignment
!            1:  =  assignment
!            2:  <- assignment
!            3:  Unchecked string move - either for speed or P in R
constbyteintegerarray  string move(-1:3) =    SMOVE, 0, SMOVE, SJAM, SMOVOPT
record(stackfm)name  lh,rh,x
record(stackfm)  temp
integer  n,p,t,op,insert,form,lhdisp
   insert = 0
   abort(m'ASS1') if stp < 2
   rh == stacked(stp)_v
   lh == stacked(stp-1)_v
   form = lh_form;        ! to avoid the ravages of amap, load etc
   if diagnose&4 # 0 start
      monitor(lh, "ASS LH")
      monitor(rh, "ASS RH")
   finish
   
   if assop < 0 start;             ! Parameter
      if lh_flag & prim bit # 0 start;       ! Special - prim routine
         temp = lh;  lh = rh;  rh = temp
         p disp = 0
         return
      finish
      lh_extra = lh_extra - 1
      vstack(lh_extra);  lh == stacked(stp)_v
      form = lh_form;  lh disp = lh_disp;   ! preserve original values
      assop = 0 if lh_form # v in s
      if lh_flag & p in r = 0 start;     ! not an in-register parameter
         p disp = lh_disp + lh_length
         p disp = p disp+1 if lh_type = strings and Form = VinS
         lh_disp = lh_disp + wdisp;   ! adjust for nested calls
      finish
      if lh_flag & proc bit # 0 start;         ! Procedure parameter
         assop = 1
         lh_type = integers;  lh_form = v in s
         rh_type = integers;  rh_form = av in s
         if rh_base # 0 and rh_base # gla start;       !  param already
            rh_form = v in s
         else if rh_base = gla;        ! non-local external
            rh_disp = rh_disp-5*reglen;  !dummy environment
         else;                           ! local routine
            p = (frame+3)&(¬3)
            frame = p+8*reglen
            t = rh_disp;                     !proc tag
            rh_disp = p;  rh_base = local
            define reference(t, r ref)
            rx(la, link, code, 0)
            rx(stm, r8, local, p)
         finish
      finish
   finish
   stp = stp-2

   if rh_flag & array bits # 0 start;         ! Arrayname
      p disp = lh_disp + 2*reglen
      hazard(r0)
      address(lh,-1);  address(rh,-1)
      if rh_header = -1 start;                ! Simple case
         rx(lw,r0,rh_base,rh_disp);          ! @A(0)
      else;                                    ! Array-in-record
         rxi(lw,r0,rh_base,rh_disp)
         rx(add,r0,gla,rh_header)
         rh_disp = rh_header;  rh_base = GLA
      finish
      if lh_type = strings and lh_length = 0 start;      ! %string(*)%arrayname
         RXI(ADD,r0,0,rh_length<<24);         ! length in top byte
      finish
      forget reg(1<<r0)
      rx(st,r0,lh_base,lh_disp)
      claim(rh_base);  rx(lw,r0,rh_base,rh_disp+reglen)
      claim(lh_base);  rx(st,r0,lh_base,lh_disp+reglen)
      drop(lh);  drop(rh)
      return
   finish

   if lh_type = general start;       ! general %name parameter
      abort(m'ASS2') unless assop = 0
      if rh_type = general start
         amap(lh);  address(lh,-1)
         amap(rh);  address(rh,-1)
         hazard(r0)
         rx(lw,r0,rh_base,rh_disp)
         rx(st,r0,lh_base,lh_disp)
         claim(lh_base);  claim(rh_base)
         rx(lw,r0,rh_base,rh_disp+reglen)
         rx(st,r0,lh_base,lh_disp+reglen)
         drop(lh);  drop(rh)
         return
      finish
      t = rh_type
      rh_flag = rh_flag ! assigned;   ! pointer proper may never be used !!!!!
      n = rh_length; n = n+1 if t = strings;    ! logical => physical length
      amaps(rh);  lrd(rh,any);  p = rh_base
      rx(st,p,lh_base,lh_disp)
      claim(lh_base)
      cload((n<<4) + genmap(t),p)
      rx(st,p,lh_base,lh_disp+reglen)
      drop(lh)
      return
   finish

   if assop = 0 start;          ! ==
      amap(lh);      ! destination
      if lh_length = 0 then amaps(rh) else amap(rh);      ! %string(*)%name ?
   finish

   if Lh_Type = Records start
      n = Min Record Size(Lh, Rh)
      if rh_Form # Constant start
         lrd(rh,r1);        ! source area
         op = rcopy;        ! copy record
      else
         drop(rh)
         op = rzero;        ! clear record
      finish
      lrd(lh,r2);          ! destination area
      cload(n>>2,R3);         ! R3 = no. of WORDS to copy/zero
      perm(op)
      return
   finish

   if lh_type = strings and lh_flag & p in r = 0 start
      if assop > 0 and rh_format = 1 start;       ! null string as zero byte ?
         drop(rh)
         lh_type = byte;  sstack(lh);  drop(lh)
         cstack(0);  assign(assop)
         return
      finish
      p = lh_length
      if assop # 2 and same(lh,rh) start;               ! S = S  or S = S.T
         if rh_oper = 0 start
            drop(lh);  drop(rh);      ! S = S
         else
            rh_length = p
            release(lh_base);  drop(lh)
            rh_type = 0{;  address(rh,-1)};  lrd(rh,0)   {0 = special for CONC}
         finish
      else if Control&Trusted # 0   and
                assop # 2             and
                Rh_Oper # 0           and
                not Same(Lh, Rh)     and
                not Same(Lh, Rh_Link)
         x == Rh_Link;  Rh_Oper = 0
         Load(Lh, R2);  Lrd(Rh, R1)
         Perm(String Move(3))
         Lh_Form = VinS;  Lh_Type = 0
         Lh_Oper = Conc;  Lh_Link == X
         Lrd(Lh, 0)
      else;          ! general case

         rh_flag = rh_flag&(¬quick conc);      ! quicky not possible after all.

         ! use fast string move if 'trusted' or capacity exceeded is impossible
         ! and unassigned is not requested or impossible.
         if assop # 2 start;                 ! not jam transfer
            assop = 3 if control & trusted # 0    c
                      or ( p >= rh_length   c
                          and (control&check unass = 0 or not checkable(rh)) )
         finish
         if rh_oper = 0 start;     !simple, so protect lhs first: s(j)=t
            lrd(lh, r2);  lrd(rh, r1)
         else;                     !simplify rh first: s = t.u
            lrd(rh, r1);  lrd(lh, r2)
         finish
         perm( string move(assop) )
         cput(p) if assop # 3;          ! max. length of destination for check ?
      finish
      return
   finish

   if lh_flag & p in r # 0 start
      p = p1;  p = p2 if lh disp < reglen;  p = p3 if lh_type = strings
      load(rh,p) if rh_oper # 0
      drop(lh)
      lh == stacked(stp)_v
      rh_oper = p;           ! target register
      rh_link == lh_link;  lh_link == rh
      address(lh, -1)
   else
      ! Test for case where add-to-memory can profitably be used.
      ! Note that the effective no-op of self-assignment can be detected easily
      address(lh, -1)
      if control & check bits = 0 and rh_type <= short C
                  and (rh_oper = 0 or rh_oper = ADD) and same(lh,rh)  start
         if rh_oper = 0 start;              ! assignment-to-self
            release(rh_base);  drop(rh)
            release(lh_base);  drop(lh)
            return
         finish
         ! General case: add-to-memory
         x == rh;  rh == rh_link
         release(x_base);  drop(x)
         load(rh,any)
         address(lh,-1);  rxd(AM,rh_base,lh)
         forget var(lh)
         release(rh_base);  drop(rh);  drop(lh)
         return
      finish
      ! test for assignment of small constants to %short and %byte
      t = rh_type
      if rh_base = 0 and rh_form = constant and rh_Oper = 0 start
         if -32768 <= rh_disp <= 32767 start
            if rh_disp&(¬255) = 0 start
               t = byte
            else
               t = short
            finish
         finish
      finish
      ! . . . then suppress capacity check if LH is real or length RH is
      ! not greater than length LH.
      n = assop
      assop = 2 if lh_type > byte or lh_type <= t

      if Lh_Form = VinR start      {special by PSR - is it safe????}
         Load(Rh, Lh_Base)
         Assop = 2
      else
         p = cheap reg                            {preferred register}
         float(rh, p) if lh_type >= reals and not floating(rh)
                  {Float here to prevent optimising the integer value}
         address(rh, p)                           {see where it is}
         load(rh, p) unless rh_form = VinR
         p = rh_base
         address(lh, -1) unless lh_form = VinS
         rxd(ST, p, lh)

         if control & suppress = 0 start
            t = activity(lh_base)
            if t < 0 or control&trusted # 0 start
               forget var(lh)
               if mark assigned # 0 and lh_base = local start;      ! set 'assigned' ?
                  var(lh_varno)_flag = var(lh_varno)_flag ! assigned
               finish
            else
               forget all
            finish
            lh_flag = lh_flag ! (rh_flag & assigned)
            associate(lh,p) if n # 2;             ! not jam transfer
         finish
         release(p)
      finish
      drop(lh);  drop(rh)
   finish
   if assop = 1 and control&check capacity # 0 start
      if lh_type = short start
         claim(p);  rr(chvr,p,p)
         perm(cap16);       ! Test for 16-bit overflow
      else
         rxi(TEST,p,0,¬255);                ! should give zero result
         perm(cap8);                        ! Test for 8-bit overflow
      finish
   finish
end;             !  assign

!                                                      >> LOAD PARAMS <<
routine load params(record(stackfm)name v)
! called at c('E') to load in-register parameters set by ASSIGN above
integer reg
record(stackfm)name  next
   return if v == null
   reg = v_oper
   v_oper = 0
   next == v_link
   load(v,reg)
   load params(next)
   release(reg)
   drop(v)
end;          ! load params

!                                                        >> ARRAY REF <<
routine  array ref(integer mode)
   ! Array references are by perm call except in the case of unchecked 1-D arrays
   ! which either:
   ! (i) have a data size which is an integral power of 2, not greater than 16384.
   !           or:
   ! (ii) have data size <= 32768 and constant bounds -32768 <= x <= 32767.
   integer  flags, p, type, base, assbit
   integer  mult, shift;       ! ** PRESUMED SET BY 'UNCHECKED REF'
   record(stackfm)name  temp

   predicate  special case
      shift = power(mult)
      true if shift >= 0 or flags & cheap array bit # 0 or Base = 0
      false
   end;          ! special case

   routine  unchecked ref
      integer  header, length, format, extra
      header = lhs_header;  length = lhs_length;  format = lhs_format
      extra = 0
      if rhs_oper = ADD or (rhs_oper = 0 and
                             (rhs_form = AV in S or rhs_form = Constant)) start
         if rhs_oper = ADD start
            if rhs_link_form = constant or rhs_link_form = AV in S start
               extra = rhs_link_disp
               if rhs_link_form = AV in S start;     ! => VinR + const (see below)
                  rhs_link_form = VinR
                  rhs_link_disp = 0
               else;                   ! simple constant
                  rhs_oper = 0
                  drop(rhs_link)
               finish
            finish
         else;                           ! AV in S  (treat as VinR + constant)
            extra = rhs_disp;  rhs_disp = 0
            rhs_form = VinR if rhs_form = AVinS
         finish
         extra = extra * mult
      finish
      if shift >= 0 start
         cstack(shift);  operate(lsh)
      else
         cstack(mult);  operate(mult16)
      finish
      lhs_type = integers;    ! address calculation
      if lhs_header >= 0 start;     ! array-in-record
         amap(lhs);          ! address of record containing array
         sset(gla,lhs_header,v in s,0)
         operate(add)
      finish
      operate(add)
      set lhs
      !                                         ! ***** F R I G *****
      !      load(lhs,any) %if lhs_oper = 0;   ! Force load: zero subscript folded out
      !                                         ! ***** F R I G *****
      !      vmap(lhs)
      !      lhs_disp = lhs_disp + extra
      !      lhs_type = type
      !      lhs_format = format;  lhs_length = length
      !      lhs_xform = assbit ! V in S
      !      lhs_form = A in S %if flags & label bit # 0

      if Extra # 0 start
         Cstack(Extra);  Operate(Add)
         Set Lhs
      finish
      Vmap(Lhs)
      Vmap(Lhs) if Flags&Label Bit # 0            {namearray}
      Lhs_Type   = Type
      Lhs_Format = Format
      Lhs_Length = Length
      Lhs_Xform  = Lhs_Form!Assbit
   end

   if mode # 0 start;     ! multi-dimensional:  ingest non-terminal subscripts
      set both;  stp = stp-1
      load(rhs,any) if rhs_oper # 0
      rhs_link == lhs_link
      lhs_link == rhs
      lhs_oper = lhs_oper+1
      return
   finish

   set both
   abort(m'ARF1') if lhs_oper+1 # lhs_dim;       ! No. of subscripts ?

   flags = lhs_flag;                           ! protect from ravages of AMAP
   lhs_flag = lhs_flag &  (¬(label bit + array bits))

   base = lhs_base;  type = lhs_type;  assbit = lhs_xform & (assigned<<8)

   if (control & check array = 0 and lhs_oper = 0) or
                                       Base = 0  start;    ! unchecked 1-D
      mult = lhs_length
      mult = mult+1 if lhs_type = strings
      mult = 4 if Flags&Label Bit # 0;             !namearray
      if special case start;        ! sets 'shift' as a side-effect
         unchecked ref;  return
      finish
   finish

   stp = stp-1
   if lhs_oper = 0 start;        ! 1-D
      load(rhs,r1);  drop(rhs)
      p = aref1
   else if lhs_oper = 1;         ! 2-D
      load(lhs_link,r1);  drop(lhs_link)
      load(rhs,r2);       drop(rhs)
      p = aref2
      p = aref4 if control & check array = 0
   else;                         ! 3-D or more
      load(rhs, any) if rhs_oper # 0         {**psr**}
      rhs_link == lhs_link;   ! tack on last subscript
      for p = pdisp,reglen,pdisp+(lhs_oper-1)*reglen cycle
         temp == rhs_link
         lrd(rhs,any)                  {**psr**}
         rx(ST,rhs_base,wsp,p)         {**psr**}
         rhs == temp
      repeat
      load(rhs,r1);            ! r1 = first subscript
      rhs_form = AV in S;  rhs_base = wsp;  rhs_disp = pdisp
      load(rhs,r2);            ! r2 = addr(subscript list)
      drop(rhs)
      p = aref3
   finish
   lhs_oper = 0
   amap(lhs)
   if lhs_header >= 0 start;           ! array-within-record
      sset(gla,lhs_header,av in s,0)
      load(rhs,r3);  drop(rhs);  stp = stp-1
   else
      load(lhs,r3)
   finish
   release(r1);   release(r3)
   release(r2) unless p = aref1
   perm(p)
   claim(r1)
   if lhs_header >= 0 start;           ! array-in-record
      sset(lhs_base,lhs_disp,lhs_form,lhs_extra);   ! address of record
      lhs_base = r1;  lhs_disp = 0;  lhs_form = VinR;    ! array component
      lhs_type = integers;            ! an address to be amapped
      operate(add);                     ! record address + array component
   else
      lhs_base = r1;  lhs_disp = 0
   finish
   vmap(lhs)
   lhs_type = type
   lhs_xform = assbit ! V in S
   lhs_form = A in S if flags & label bit # 0
end;        !  array ref

!                                                        >> TEST ZERO <<
routine  test zero(record(stackfm)name  v)
record(stackfm)name w
integer  cr
   cr = any
   cr = anyf if floating(v)
   if v_oper = AND and sym = '?' and const(v_link) start
      ! if x & const = 0 . . . . . . .
      w == v_link;  v_oper = 0
      load(v,cr)
      rxd(TEST,v_base,w)
      drop(w);  release(v_base)
   else
      load(v,cr)
      if ca # cc ca or cc reg # v_base start
         rr(lw,v_base,v_base)
      else
         release(v_base)
      finish
   finish
end;       ! test zero

routine Compare Records(record(stackfm)name L, R, integer N)
   Amap(l);  Load(l, R1)
   Amap(r);  Load(r, R2)
   Cload(n, R3);  Set Both           {***beware of CLOAD and Lhs}
   Release(R1);  Release(R2)
   Perm(Rcomp)
end

!                                                    >> COMPARE REALS <<
routine  compare reals(record(stackfm)name  l,r)
   load(l,anyf)
   address(r,0)
   float(r, anyf) unless floating(r)
   rxd(cmp,l_base,r)
   release(l_base)
end;         ! compare reals

!                                                  >> COMPARE STRINGS <<
routine  compare strings(record(stackfm)name  l,r)
record(stackfm)name  temp
   if l_base = code and l_disp = null string start
      temp == r;  r == l; l == temp
      invert = invert !! 16
   finish
   if r_base = code and r_disp = null string start
      load(l,any) if l_oper # 0
      l_type = byte
      test zero(l)
   else
      load(r,r2) if r_oper # 0
      load(l,r1);  load(r,r2)
      release(r1);  release(r2)
      perm(scomp)
   finish
   l_type = strings;  l_form = v in s
   p disp = 0
end;        ! compare strings

!                                                          >> COMPARE <<
routine  compare(record(stackfm)name  l,r, integer next)
   swopped = 0
   if l_type = 0 or l_type = strings start
      compare strings(l,r);  return
   finish
   if zero(r) start
      test zero(l);  return
   finish
   if zero(l) start
      test zero(r);  invert = invert !! 16
      return
   finish
   if floating(l) or floating(r) start
      compare reals(l,r);  return
   finish
   if L_Type = Records start
      Compare Records(L, R, Min Record Size(L, R))
      return
   finish
   address(l,0);  load(l,any)
   address(r,0)
   if '=' # next # '#' start
      rxd(cmp,l_base,r)
   else
      rxd(clw,l_base,r)
   finish
   release(l_base)
end;         ! compare

!                                                          >> RESOLVE <<
routine  resolve(integer  flag)
!S -> A.(B).C
record(stackfm)name  s,a,b,c
integer  p,q
   cstack(0) if flag&1 = 0;          ! C missing
   pop lhs;  c == lhs
   pop lhs;  b == lhs
   cstack(0) if flag&2 = 0;          ! A missing
   pop lhs;  a == lhs
   pop lhs;  s == lhs
   load(s,r3);  load(a,r2);  load(b,r1);  load(c,r4)
   p = a_length;   !!!!!  p = 255 %if p = 0
   q = c_length;   !!!!!  q = 255 %if q = 0
   release(r3);  drop(s)
   release(r2);  drop(a)
   release(r1);  drop(b)
   release(r4);  drop(c)
   perm(sresln);  cput( (p<<8) + (q&255) );          ! conditional resolution
   if flag&4 = 0 start;            ! unconditional
      perm(sresv);                 ! verify it succeeded
   finish
end;            ! resolve

!                                                           >> HEADER <<
routine  header(record(varfm)name  v)
   frame = basic frame if frame < basic frame
   define tag(v_disp&x'FFF')
   rx(stm, p2+2-minparm, wsp, (2-minparm)*reglen)
   if potype >= external start

      !===== the order of the next two statements is critical =====

            external link(ep defn,0,0)
            rx(st,link,wsp,(link-p2)*reglen)

      if control&unass # 0 and unassigned rtn = 0 start
         ! Force inclusion of unassigned check routine if not already present
         unassigned rtn = 1
         select output(direct)
         print symbol(p ref);  put(asschk);  put(0)
         select output(object)
      finish
   finish
   rr(lw,local,wsp)
   if v_header # 0 start;              ! special string parameter (P in R)
      abort(m'HDR1') if v == begin
      sset(local,v_header,V in S,0);  rhs_type = strings
      sset(p3,0,VinR,0)
      claim(P3);            ! parameter nominally at 0(P3)
      assign(3);            ! SMOVOPT
      if control&trusted # 0 start;     ! suppress check if 'TRUSTED'
         v_header = 0
      else
         Cput(x'0812')                  {preserve R2 for later}
      finish
   finish
   frame = (frame+align)&(¬align);   ! ensure non-parameter locals are aligned
   if control & trusted = 0 start;           ! stack overflow check
      perm(pentc);       ! checked procedure entry   *** mustn't corrupt R2 ***
      cput(frame);       ! parameter size
      cput(0);           ! padding:-  gets overwritten
   else
      rxi(add,wsp,0,basic frame);      ! 2nd. halfword gets overwritten
   finish
   set frame patch;      ! Total size
   ! Use base reg(5) to hold unassigned pattern ( except at level 5 !! )
   if level # 5 and control & check unass # 0 start
      rx(lw,base5,code,unass)
      activity(base5) = -1;             !  lock it
   finish
   if v_header # 0 start;        ! check P in R string
      Cput(x'0821')               {*LR_2,1}
      perm(smove)
      cput(ap_length);         ! 'AP' set at '}'
      v_header = 0
   finish
   if control&trace # 0 start;        ! trace option enabled
      if v == begin and potype >= external start;      ! main program %begin
         external id = trace routine
         external link(ep ref,0,ga)
         perm(enter trace);  cput(0);      ! initialise user-supplied routine
      finish
      trace flag = control&trace
   finish
   event = 0
end;       ! header

!                                                           >> RETURN <<
routine  return
   return if uncond jump = ca;            !  can't get here ?
   if Return Label # 0 start
      Jump To(Return Label, Jump, Define New)
   else
      Return Label = x'7001'              {something positive and unique}
      Define Label(Return Label)
      rx(lm,wsp,local,(wsp-p2)*reglen)
      rr(jmp,always,link)
   finish
   uncond jump = ca
   Closed = 0            {can get back now}
end;           ! return

routine  compile to string(record(stackfm)name  v)
   {Delay if possible so S = S.tostring(k) can be optimised in LOAD}
   if next = '.' and control&(check capacity!check unass) = 0 start
      v_flag = v_flag ! quick conc
      return
   else if const(v)
      current string(0) = 1;  current string(1) = v_disp&255
      claim literal(2,0);  otype = con;  dump string(0)
      v_base = code;  v_disp = litmax
   else
      load(v,any)
      frame = (frame+1)&(¬1)
      rr(lbr,R0,v_base)
      rxi(add,R0,0,1<<8)
      rx(sth,R0,local,frame)
      v_base = local;  v_disp = frame;  frame = frame+2
   finish
   v_type = strings;  v_xform = VinS ! (assigned<<8);  v_length = 1
end
!                                                             >> CALL <<
routine  call(record(stackfm)name  v)
switch  b(1:max prim)
!  1 = rem
!  2 = read symbol
!  3 = float
!  4 = to string
!  5 = substring
!  6 = free space
!  7 = SVC;      ! *** MOUSES specific ***
!  8 = addr
!  9 = integer
! 10 = short integer
! 11 = byte integer
! 12 = string
! 13 = record
! 14 = real
! 15 = long real
! 16 = length
! 17 = charno
! 18 = int
! 19 = int pt
! 20 = IOCP;      ! *** temporary ***
! 21 = type of;   ( type of general name parameter )
! 22 = size of;   ( physical length in bytes )
! 23 = frac pt;    ! *** replaces IOCP above in the fullness of time ***
   constbyteintegerarray  new type(9:17) =
         integers, short, byte, strings, records, reals, reall, byte, byte
   constbytearray New Size(integers:reall) = 4,2,1,8,255,0,4
   integer  t,l,p

   if v_flag & prim bit # 0 start;           !  built-in primitive
      l = 0;  t = v_header;  sym = 0;      ! 'sym=0' used as flag elsewhere
      if t = 2 start;        ! 'read symbol'
         v_flag = v_flag & (¬prim bit)
         v_header = 0;      ! otherwise looks like "P in R" in "HEADER" q.v.
      else
         drop(v)
      finish
      set lhs
      -> b(t)
b(1):                             ! REM
      operate(rem);  return
b(2):                             ! READ SYMBOL
      call(v)
      sset(r1,0,VinR,0)
      if lhs_type = records or lhs_type = general start
         warn(4)
         ! *** subsequently, force a call on external routine form of
         !     read symbol and leave it to generate the error
         ! *** FRIG ***
            cload(5,r0); cload(5,r1); perm(signal)
            set lhs;  lhs_type = byte;   ! to prevent compiler failing
         ! *** FRIG ***
      finish
      claim(rhs_base)
      compile to string(rhs) if lhs_type = strings
      assign(1)
      return
b(3):                             ! FLOAT
      float(lhs, anyf);  return
b(4):                             ! TO STRING
      compile to string(lhs)
      return
b(8):                             ! ADDR
      t = Lhs_Type
      amap(lhs)
      if T = Strings and Lhs_Form # AVinS and Lhs_Form # AVinRec start
         Load(Lhs, Any)
         Lhs_Form = VinS
         Forget Reg(1<<Lhs_Base)
         Rxd(LA, Lhs_Base, Lhs);  Claim(Lhs_Base)
         Lhs_Form = VinR
      finish
      return

b(16):                            ! LENGTH
      cstack(0)
b(17):                            ! CHARNO
      set both
      amap(lhs)
      if control&check array = 0   c
                  or (const(rhs) and t-16 <= rhs_disp <= lhs_length) start
         operate(add);  set lhs;  !LHS&RHS reversed in operate??
      else
         load(lhs,r1);  load(rhs,r2)
         drop(rhs);  stp = stp-1
         release(r1);  release(r2)
!!**** charno(s,j) where S is %String(*)%name won't work:  change  perm as well
         perm(chmap);  cput(lhs_length & 255)
         claim(r1)
         set lhs;  lhs_base = r1;  lhs_disp = 0;  lhs_xform = VinR
      finish
      -> map it
b(12):                            ! STRING
!!!!!      l = 255
b(9):b(10):b(11):                 ! INTEGER, SHORT, BYTE
b(13):                            ! RECORD
b(14):b(15):                      ! REAL, LONG REAL
map it:
      vmap(lhs);  lhs_type = new type(t)
      lhs_length = new size(Lhs_Type)
      return
b(19):                             ! INT PT
      load(lhs,anyf)
      p = gpr;  hazard(p)
      rr(fxr,p,lhs_base);  claim(p)
      lhs_base = p;  lhs_type = integers;  lhs_xform = VinR
      return
b(18):                              ! INT
      p = intfn;         ! perm routine
      t = integers;      ! resulting type
      l = R1;            ! result register
      -> PERM1823
b(23):                              ! FRAC PT
      p = frac part
      t = reall
      l = FR2
PERM1823:
      load(lhs,fr2);  release(fr2)
      perm(p)
      claim(l);            ! result register
      set lhs
      lhs_base = l;  lhs_xform = VinR;  lhs_type = t
      return
b(5):                               ! substring(S,from,to)
      load(lhs,r3);  drop(lhs);  stp = stp-1
      set both;  stp = stp-2
      load(lhs,r1);  load(rhs,r2);  drop(lhs);  drop(rhs)
      release(r1);  release(r2);  release(r3)
      perm(substr);  claim(r1)
      sset(r1,0,v in s,0);  rhs_type = strings
      return
b(21):                              ! type of(..)
b(22):                              ! size of(..)
      if lhs_type # general start;        ! type explicitly specified
         if t = 21 start;        ! type of
            p = gen map(lhs_type)
         else
            p = lhs_length;  p = p+1 if lhs_type = strings
         finish
         release(lhs_base)
         lhs_type = integers;  lhs_form = constant
         lhs_base = 0;  lhs_disp = p
      else
         lhs_disp = lhs_disp + reglen;      ! reference property-word
         lhs_xform = (assigned<<8) ! V in S;  lhs_type = integers
         if t = 21 start;           ! type of
            cstack(15);  operate(and)
         else;                      ! size of
            cstack(4);  operate(rsh)
         finish
      finish
      return
b(6):                               !  free space
      perm(freesp);  claim(r1)
      sset(r1,0,VinR,0)
      return
b(7):                              ! SVC (MOUSES SPECIFIC)
      hazard(p) for p = fr0,1,fr14
      set both;  stp = stp-2
      load(lhs,any) unless const(lhs);
      address(rhs,-1)
      rx(lme,fr0,rhs_base,rhs_disp);  claim(rhs_base)
      rx(svc,r0,lhs_base,lhs_disp)
      rx(stme,fr0,rhs_base,rhs_disp)
      drop(lhs);  drop(rhs)
      forget reg(-1)
      return
b(20):                              ! IOCP    *** temporary ***
      load(lhs,r4);            ! required function
      release(r4);  drop(lhs);  stp = stp-1
      perm(iocp)
      return

   finish
   !   -- normal routine calls --
   wdisp = (wdisp+align)&(¬align);      !keep WSP aligned
   hazard all if V_Flag&Assigned = 0         {beware - it returns}
   if v_base # 0 start;               ! non-local
      if v_base # gla start
         t = new tag;  define reference(t, r ref)
         rx(la, link, code, 0)
      finish
      rx(STM,p2,wsp,wdisp)
      rxi(ADD,wsp,0,wdisp) if wdisp # 0
      if v_base = gla start;          ! external
         rx(lm,gla,gla,v_disp)
         rr(bal, link, link)
      else;                           ! procedure-as-parameter
         quick load(r2, VinS, v_base, v_disp);  forget reg(1<<R2)
         rx(lm,r8,r2,0)
         rx(bal, link, link, 4);      !skip initial STM
         define tag(t)
      finish
   else;                              ! local routine
      rxi(ADD,wsp,0,wdisp) if wdisp # 0;       ! protect stacked parameters ?
      define reference(v_disp&x'FFF',r ref)
      rx(bal,link,code,0)
      rxi(SUB,wsp,0,wdisp) if wdisp # 0;       ! reset protection
      Uncond Jump = Ca if V_Flag&Assigned # 0      {it doesn't return}
   finish
   wdisp = v_header;  p disp = v_rt
   drop(v) if v_type = 0;             ! not function or map
end;             !  call

!                                                     >> COMPILER OP <<
!***** RE=ORGANISE  'call', 'prim' and this routine *****
routine  compiler op(integer  n)
   record(stackfm)name  p
   p == descriptor;  stp = stp-1 {DESCRIPTOR increments it!!}
   p_flag= prim bit;  p_header = n
   abort(m'CMOP') unless 0 < n <= max prim
   call(p)
end

!                                                      >> COMPILE FOR <<
routine  compile for
record(stackfm)name  cv, iv, inc, fv
integer  lab, safe, n, reg, shadow
   routine  stab(record(stackfm)name  v,  integer  type)
   integer  t,r
      return if const(v)
      load(v,any);  r = v_base
      t = temp
      v_base = local;  v_disp = t
      v_type = type;  v_xform = (assigned<<8) ! V in S
      rx(ST,r,local,t);  release(r)
      associate(v,r) if control & suppress = 0
   end
   routine  set(record(stackfm)name  v,integer  reg)
   record(stackfm)name  r
      sstack(v);  r == stacked(stp)_v
      lrd(r,reg)
      stp = stp-1
   end
   cv == stacked(stp-3)_v
   inc == stacked(stp-2)_v
   fv == stacked(stp-1)_v
   lab = tag
   abort(m'FOR1') if for stp = max cycle
   for stp = for stp + 1;  for == for stk(for stp)
   n = next temp;                        ! remember current point in temp stack
   shadow = -1;  shadow = temp if control & check for # 0
   stab(fv,integers);  stab(inc,integers)
   for_temp base = temp base
   if n # next temp start;               ! protect shadow, FV, INC ?
      temp base = new temp
   finish
   safe = 0
   sstack(inc);  operate(sub)
   iv == stacked(stp)_v;         ! iv = iv - inc
   if cv_form # v in s or activity(cv_base) >= 0 start
      n = cv_type
      amap(cv)
      stab(cv, n)
      cv_form = a in s
   finish
   stp = stp-4
   if const(fv) and const(iv) and const(inc) start
      if inc_disp # 0 start
         n = fv_disp-iv_disp
         if n !! inc_disp >= 0 and (n//inc_disp)*inc_disp = n start
            safe = 1
         finish
      finish
      if safe = 0 start
         warn(2);            !  constant faulty %for parameters
      else
         safe = fv_disp - iv_disp;           ! null cycle ?
      finish
   finish
   reg = iv_base
   if reg <= r2 start
      reg = gpr                              {**cannot return r0,r1,r2}
   else
      reg = any
   finish
   load(iv,reg);  reg = iv_base
   if safe = 0 and control & check for # 0 start
      set(iv,r0);  claim(reg)
      set(fv,r1);  set(inc,r2)
      perm(fchk1);            ! Check %for parameters before entry
   finish
   if safe = 0 start;     ! non-constant or null cycle
      sstack(cv);  sstack(iv);  assign(1);  claim(reg)
      for_initial = for lab base + for stp
      jump to(for_initial,jump,redefine old)
   finish
   define label(lab);  trace flag = control&trace
   sstack(cv)
   sstack(iv);  sstack(inc);  operate(add);        ! CV + INC
   drop(iv);  drop(inc)
   set lhs;  load(lhs,reg);   ! to make sure ASSIGN doesn't use wrong register
   assign(1)
   rx(st,reg,local,shadow) if shadow >= 0
   for_lab = lab;  for_reg = reg;  for_shadow = shadow
   for_cvbase = cv_base;  for_cvdisp = cv_disp 
   for_cvtype = cv_type;  for_cvform = cv_xform
   for_fvbase = fv_base;  for_fvdisp = fv_disp
   drop(cv);  drop(fv)
end;             ! for


   cycle;             !  --- main loop ---
      sym = next;  read symbol(next)
      -> c(sym)

c('l'): language mask = tag;  continue;       ! Select language dependent options

c('O'):
      abort(m'STK?') if stp # 0
      abort(m'USNG') unless using_link == null
      abort(m'CLMD') if claimed # 0
      abort(m'LIT?') if ca < 0;    ! 'select code/literal area' misused
      wdisp = 0;  p disp = 0
      next temp = temp base
      current line = tag
      if control&trace # 0 start
         if next = ':' or next = 'L' start
            trace flag = 1
         else if trace flag # 0
            dump trace
         finish
      finish
      continue

c('$'): define var;  continue

c('b'):
      pop drop;  vub = lhs_disp
      pop drop;  vlb = lhs_disp
      continue

         routine  adump
            switch c(integers:8),g(integers:8);   ! 8 =REALS+1 !!!!!
            constintegerarray  low(integers:8) =   0,-32768,   0, 0(*)
            constintegerarray  high(integers:8) = 0, 65535, 255, 0(*)
            integer  j
               if high(owntype) # 0 and control & check capacity # 0 start
                  warn(8) unless low(owntype) <= ownval <= high(owntype)
               finish
               -> g(owntype) if otype # con
               select literal area if strings # owntype < reals
               -> c(owntype)
            g(integers):      gword(ownval);           return
            c(integers):      cword(ownval);     -> exit
            c(reals):c(8):
            g(reals):g(8):    j = real constant(1);    return
            g(byte):          gbyte(ownval);           return
            c(byte):          lit byte(ownval);  -> exit
            g(short):         gput(ownval);            return
            c(short):         cput(ownval);      -> exit
            c(strings):
            g(strings):       dump string(data size);  return
            g(records):       gput(0) for j = 1,1,data size>>1
                              return
            c(records):       abort(m'ADMP')
         exit:                select code area
         end;        ! adump
c('A'):
      aparm = tag
      if stp # 0 start
         decvar_flag = decvar_flag ! assigned;      ! explicit initialisation
         pop drop
         if own type >= reals start
            rvalue = lhs_disp if lhs_type < reals
            ownval = integer(addr(rvalue))
            mantissa = integer(addr(rvalue)+4)
         else
            ownval = lhs_disp;      ! a string
         finish
      else;             ! initialise to UNASSIGNED pattern
         if own type = byte start
            own val = x'80'
         else if own type = short
            own val = x'FFFF8080'
         else if owntype # strings
            ownval = x'80808080';  mantissa = x'80808080'
         else
            cslen = x'80';  current string(1) = x'80'
         finish
      finish
      if own form = array or own form = name array start
         adump for j = 1,1,aparm
      else
         if otype = 0 start
            decvar_flag = decvar_flag & (¬assigned);   ! %const .... %name
            decvar_disp = ownval;  decvar_base = 0
            ! %CONSTINTEGERNAME    ->   INTEGER
            ! A in S -> V in S, A in REC -> VinREC
            if Decvar_Form = VinS start
               Decvar_Form = Constant
            else
               Set Diag(0, Ownval) if Decvar_Form = AinS
               decvar_form = decvar_form + (v in s - a in s)
            finish
         else
            decvar_base = gla;  decvar_disp = ga
            if otype >= external start
               decvar_flag = decvar_flag & (¬assigned)
               external link(data defn,data size,ga)
            else if otype = con ;        ! %const
               if decvar_type = strings start
                  claim literal(cslen+1,1)
                  j = litmax;  dump string(0)
               else if decvar_type >= reals
                  j = real constant(0);   ! ** N.B.  %fn + side-effect **
               else
                  abort(m'AM01')
               finish
               decvar_base = code;  decvar_disp = j
               continue
            finish
            adump
         finish
      finish
      continue

c(''''): get string;  continue
c('G'):  get string
         alias = ""
         for j = 1, 1, cslen cycle
            alias = alias.tostring(current string(j))
         repeat
         pop drop
         continue
c('N'):  cstack(tag<<16!tag);  continue
c('D'):
      get d
      cstack(0) and continue if rvalue = 0
      continue if next = 'A'
      otype = con;          !  anonymous %const
      j = real constant(0);    !  N.B. ** %fn + side-effect **
      sset(code,j,v in s,0);  rhs_type = reals
      continue

c('n'):
      j = tag;  set lhs
      vstack( var(lhs_format)_extra - j )
      set both;  stp = stp-1
      if rhs_form # 15 start;         !  not record format
         if lhs_form = v in s or lhs_form = VinRec start
            rhs_disp = lhs_disp + rhs_disp
            lhs_xform = lhs_form - v in s + rhs_xform
         else
            if lhs_form = a in rec start
               lhs_form = VinRec;  lhs_type = integers
               load(lhs,any)
               lhs_xform = rhs_xform
            else
               if lhs_form <= VinR start
                  lhs_xform = rhs_xform;    ! ????
               else
                  lhs_extra = lhs_disp
                  lhs_xform = rhs_xform+3
               finish
            finish
         finish
         lhs_disp = rhs_disp
         lhs_type = rhs_type
         lhs_rt = rhs_rt
         lhs_header = rhs_header
      finish
      lhs_length = rhs_length;  lhs_format = rhs_format
      lhs_dim = rhs_dim
      drop(rhs)
      continue

c('@'):
      vstack(tag)
      if lhs_flag & proc bit # 0 and next # 'p'   c
                                              and lhs_flag&prim bit = 0 start
         lhs_rt = p disp
         lhs_header = wdisp
         w disp = (p disp+align)&(¬align)
         p disp = 0
      finish
      continue
c('E'):
      pop lhs;  x == lhs
      load params(x_link)
      call(x)
      if x_type # 0 and sym # 0 start;         ! fn/map - SYM=0:  see 'CALL'!?!?!?!?
         sstack(x);  drop(x);  set lhs
         if lhs_type >= reals then opr = fr0 else opr = r1
         lhs_base = opr;  lhs_disp = 0
         claim(opr)
         if lhs_form = VinR   c
                        and (lhs_type = strings or lhs_type = records) start
            lhs_base = R1;  lhs_form = V in S
            if next # 'S' and next # 'p' and next # '?' start
               if lhs_type = strings start
                  n = 256;  lhs_length = 255
               else
                  n = var(lhs_format)_length
               finish
               pdisp = basic frame if pdisp = 0
               lhs_base = wsp;  lhs_disp = pdisp
               sstack(lhs);             ! a copy for 'ASSIGN' below
               sset(r1,0,V in S,0);     ! N.B. sets RHS implicitly
               if lhs_type = strings start
                  rhs_type = strings
               else
                  rhs_type = records;  rhs_format = lhs_format
               finish
               assign(1)
               pdisp = pdisp + n;      ! protect stacked temporary
               lhs_type = 0 if lhs_type = strings
            finish
         finish
      finish
      continue

c('M'):
c('V'):
      set lhs
      opr = r1
      if sym = 'V' start
         if gvar_type >= reals start
            opr = FR0
         else if gvar_type = records and zero(lhs);      ! recordfn result = 0
            lhs_type = records;  lhs_form = V in S
            lhs_base = wsp;      lhs_disp = 0
            lhs_format = gvar_format
            sstack(lhs);            ! duplicate
            cstack(0)
            assign(1);               ! construct zero record
            set lhs
         finish
      else
         amaps(lhs)
      finish
      lrd(lhs,opr);  stp = stp-1
      if sym = 'V'  start
         if gvar_type = strings and gvar_length > 0 start
            cload(gvar_length,r0)
            perm(sfcap);        ! string function capacity
         else if control & check capacity # 0
            if gvar_type = short start
               claim(opr);  rr(CHVR,opr,opr)
               perm(cap16)
            else if gvar_type = byte
               rxi(TEST,opr,0,¬255)
               perm(cap8)
            finish
         finish
      finish
c('R'):
      return
      continue
c('K'):                     ! %false
      k = 0;  -> true false
c('T'):                     ! %true
      k = -1
true false:
      cload(k,r1)
      return
      continue

c('a'):  array ref(0);   continue
c('i'):  array ref(1);   continue
c('.'):  operate(conc);  continue
c('+'):  operate(add);   continue
c('¬'):  k = -1;  -> not neg;         ! NOT
c('U'):  k = 0;                       ! NEG
not neg:
         pop lhs;  cstack(k);  sstack(lhs);  drop(lhs)
c('-'):  operate(sub);   continue
c('!'):  operate(or);    continue
c('%'):  operate(xor);   continue
c('&'):  operate(and);   continue
c('['):  operate(lsh);   continue
c(']'):  operate(rsh);   continue
c('*'):  operate(mul);   continue
c('/'):  operate(div);   continue
c('Q'):  operate(rdiv);  continue
c('X'):  operate(exp);   continue
c('x'):  operate(rexp);  continue

c('v'):
      set lhs
      if floating(lhs) start
         load(lhs,anyf);  opr = fpr
      else
         load(lhs,any);  opr = gpr
      finish
      pop lhs
      k = lhs_base
      load(lhs,opr)
      n = new tag
      jump to(n,greater or equal,internal tag)
      cstack(0);  sstack(lhs);  drop(lhs)
      operate(sub)
      set lhs;  load(lhs,k)
      define tag(n)
      continue

c('j'):  assign(2);   continue
c('S'):  assign(1);   continue
c('Z'):  assign(0);   continue
c('p'):  assign(-1);  continue

c('u'):            !++
c('q'):            !--
         if sym = 'u' then k = add else k = sub
         set both
         t = lhs_type;  j = lhs_length
                        j = j+1 if t = strings
         amap(lhs)
         abort(m'AM05') if j = 0
         if j = 2 start
            cstack(1);  j = lsh
         else if j = 4
            cstack(2);  j = lsh
         else
            cstack(j);  j = mul
         finish
         operate(j)
         operate(k)
         set lhs
         vmap(lhs);  lhs_type = t
         continue

c('='):
c('k'):  opr = 0;   -> cond
c('#'):
c('t'):  opr = 1;   -> cond
c('<'):  opr = 2;   -> cond
c('>'):  opr = 3;   -> cond
c('('):  opr = 4;   -> cond
c(')'):  opr = 5;   -> cond
cond:
      val = tag
      jump to(val,opr+invert,redefine old);  invert = 0
      continue

c('C'):
      set both
      t = lhs_type
      amap(lhs);  amap(rhs)
      if t = strings and (lhs_form = V in S or lhs_form = VinREC c
                        or rhs_form = V in S or rhs_form = VinREC) start
         operate(xor)
         cstack(8);  operate(lsh)
         cstack(0)
      finish
c('?'):
      set both
      compare(lhs,rhs, next);  stp = stp-2
      drop(lhs);  drop(rhs)
      continue
c('"'):
      set both;  invert = 16
      compare(rhs,lhs, next)
      stp = stp-1;  lhs = rhs;  drop(rhs)
      claim(lhs_base)
      continue

c('r'):  resolve(tag);  continue
c('_'):
      uncond jump = 0;  mark assigned = 0
      forget all 
      v == var(tag);  pop drop
      j = lhs_disp - v_extra;      !  this label - lower bound
      abort(m'AM10') unless 0 <= j < v_length;     ! within vector ?
      define tag((v_format + j)!x'8000');   ! N.B. marked as a switch defn.
      continue
c('W'):
      v == var(tag)
      if control & trusted = 0 start;           ! checked switch via PERM
         pop lhs
         lrd(lhs,r1)
         rxi(lw,r2,code,v_disp)
         perm(swjump)
      else
         cstack(1);  operate(lsh);         ! subscript X 2
         pop lhs
         k = v_disp+2*2-v_extra*2
         if const(lhs) start
            k = k+lhs_disp
            j = 0
         else
            load(lhs, Any)
            j = Lhs_Base
         finish
         lhs_base = code;  lhs_index = j;  lhs_disp = k
         lhs_type = short;  lhs_form = V in S
         rxd(LHL,r1,lhs);  claim(r1)
         drop(lhs)
         rr(ADD,r1,r1);  claim(r1)
         rr(ADD,r1,code)
         rr(JMP,always,r1)
      finish
      uncond jump = ca
      continue
c('B'):
      val = tag
      if val # for_lab start;        ! not %for .... %repeat 
         jump to(val,jump,define new)
      else
         sset(for_cvbase,for_cvdisp,for_cvform,0)
         hazard(for_reg)
         pop lhs
         lhs_type = for_cvtype;  lhs_flag = lhs_flag ! assigned
         lrd(lhs,for_reg)
         if for_shadow >= 0 start
            rx(clw,for_reg,local,for_shadow)
            perm(fchk2)
         finish
         if for_initial # 0 start
            define label(for_initial);  for_initial = 0
         finish
         if for_fvbase = 0 start;         !  constant
            if for_fvdisp = 0 start;        ! zero
               claim(for_reg);  rr(lw,for_reg,for_reg)
            else
               rxi(clw,for_reg,for_fvbase,for_fvdisp)
            finish
         else
            rx(clw,for_reg,for_fvbase,for_fvdisp)
         finish
         jump to(val,not equal,define new)
         abort(m'AM15') if next # ':'
         read symbol(next);  define label(tag)
         if for_shadow >= 0 start
            if level # 5 start
               rx(st,r12,local,for_shadow)
            else
               rx(lw,for_reg,code,unass)
               rx(st,for_reg,local,for_shadow)
               forget reg(1<<for_reg)
            finish
         finish
         temp base = for_temp base;         ! unprotect shadow, FV, INC
         for stp = for stp-1;  abort(m'AM20') if for stp < 0
         for == for stk(for stp)
      finish
      continue
c('F'):
      val = tag;  abort(m'AM25') if val >= for lab base
      jump to(val,jump,redefine old)
      continue

         integerfn user label(integer lab)
            record(varfm)name v
            if lab > names start
               names = lab
               v == var(lab)
               v = 0
               v_form = pgm label
               v_disp = new tag
               result = -v_disp
            finish
            result = -var(lab)_disp
         end
c('J'):
      jump to(user label(tag),jump,define new)
      continue
c('L'):
      define label(user label(tag))
      continue
c(':'):
      j = tag;  abort(m'AM30') if j >= for lab base
      define label(j);  continue
c('f'):
      compile for;  continue
c('w'): mark assigned = 0;  machine code;  forget reg(-1);  continue
c('P'):
      pop drop;  cput(lhs_disp);  forget reg(-1)
      continue
c('y'):                         ! %diagnose n
      j = tag
      diagnose = 0
      if (j>>14)&3 = 2 start;           ! it's for pass 2
         diagnose = j&x'3FFF'
         diagnose = diagnose ! ((-1)<<15) if diagnose&4 # 0;    ! only for speed
      else
         !***** should pass onto next pass ******
      finish
      continue
c('z'):
      control = tag;  continue
c('m'):
      j = -1;  -> SIGNAL EVENT
c('s'):
      if control&trace # 0 start
         perm(enter trace);  cput(0);     ! close off user-supplied routine
      finish
      j = 0;  -> SIGNAL EVENT
c('e'):
      j = tag
SIGNAL EVENT:
      cstack(0) while stp < 2
      pop lhs;  lrd(lhs,r2)
      pop lhs;  lrd(lhs,r1)
      cload(j,R0)
      perm(signal)
      uncond jump = ca
      continue
c('o'):
      event_events = tag;        !  events trapped
      read symbol(next);  k = tag
      j = (frame+align)&(¬align);  frame = j+reglen
      rx(ST,wsp,local,j);            ! for use below
      jump to(k,jump,redefine old);  event_low = j tag;   ! skip event body
      forget all
      event_label = new tag
      define tag(event_label);                ! entry point
      rx(LW,wsp,local,j)
      continue
c('h'):   ! compiler op(n)
      compiler op(tag)
      continue
c('g'):    !array formats
c('d'):
      ! array allocation and dope vector dumping.
      Allocate = Sym-'g'         {0=format}
      ! GMODE:  =0 -> simple array,  # 0 -> array-in-record
      !       When OPT is specified, in-line code is dumped to
      !      allocate 1-D constant-bounded arrays
      dim = tag;  abort(m'AM35') unless 0 < dim <= 7
      read symbol(next);  n = tag
      if gmode = 0 then names = names-n else parms = parms+n
      set both
      dv = 0;                     ! used as a flag subsequently
      t = -1
      ! **** The test for OPT must come out once this optimisation is
      !      implemented correctly ****
      if control&checkbits=0 and dim = 1 and lhs_disp <= rhs_disp+1 start
         if const(rhs) and const(lhs) start
            t = 0;               ! candidate for cheap allocation at least
            if 0 < data size <= 32767 c
                  and -32768 <= lhs_disp <= 32767 c
                  and -32768 <= rhs_disp <= 32767 start
               dim = dim ! cheap array bit;               ! stuffed in below
            finish
         finish
      finish
      if gmode # 0 or t >= 0 start
         vlb = lhs_disp;  vub = rhs_disp
         abort(m'AM40') if vlb > vub+1;    ! null array, A(1:0) allowed
         set dope vector
         stp = stp-2;  drop(lhs);  drop(rhs)
         if gmode = 0 start;              ! constant-bounded 1-D simple array
            quick load(R4, AVinS, code, dv);  release(R4)
            vub = (vub+align) & (¬align);    ! to preserve stack-front alignment
         finish
      else
         frame = (frame+align)&(¬align);  k = frame;  Frame = Frame+Reglen
         quick load(R4, AVinS, local, K);  release(R4)
         stp = 0
         for j = 1,1,dim<<1 cycle;     ! N.B. not changed above on this path
            stp = stp+1;  set lhs
            claim(r4);  lrd(lhs,any)
            rx(st,lhs_base,r4,frame-k);  frame = frame+reglen
            frame = frame+reglen if j&1 = 0;   ! LEAVE HOLE FOR MULTIPLIER
         repeat
         perm(set dv)
         cput(dim);  cput(data size)
         stp = 0
      finish
      if dv = 0 start
         quick load(R2, AVinS, local, frame)
         release(R2)
         forget reg(1<<r2)
      finish
      for j = 1,1,n cycle
         if gmode = 0 start
            names = names+1;  decvar == var(names)
         else
            parms = parms-1;  decvar == var(parms)
         finish
         decvar_disp = frame
         decvar_flag = decvar_flag ! dim;      ! may also set 'cheap array bit'
         if gmode = 0 start;        !  array not in record
            decvar_header = -1;  decvar_base = local
            decvar_flag = decvar_flag ! anbit;          ! force arrayname
            if dv = 0 start
               if Allocate # 0 start
                  perm(alloc);         ! general method of allocation
               else
                  RX(ST, R4, Local, Frame+Reglen)
                  RX(ST, R1, Local, Frame+0)
               finish
            else
               ! dope-vector was dumped statically above
               ! Note that the data area for each array is allocated within
               ! the high address end of the static frame startingat the top
               ! and working downwards.  Pass3 patches in the displacement req'd
               ! from 'local'
               rx(ST,r4,local,frame+reglen);        ! @DV
               if Allocate # 0 start
                  rxi(LW,r0,wsp,-vlb);     ! @A(0) for Jth array
                  rxi(ADD,wsp,0,vub);            !****temp fix****
               else
                  rxi(LW,R0,0,-vlb)
               finish
               rx(ST,r0,local,frame+0);             ! plug into header
            finish
            frame = frame + 2*reglen;  ! 2-word header
         else;           ! array-in-record
            gfix(align);                     !*****psr*****
            decvar_header = ga;  decvar_base = 0
            gword(-vlb);              ! relative to start of array
            gword crel(dv);           ! relative to code base
            frame = frame+vub if Allocate # 0
         finish
      repeat
      continue

c('^'):   {Set Format}
      Set Lhs
      Lhs_Type   = Records
      Lhs_Format = Tag
      continue


         routine Temp Set
            Frame = (Frame+Align)&(¬Align)
            Sset(Local, Frame, VinS, 0)
            Rhs_Type = Records
            Rhs_Format = Max Vars
            Frame = Frame+SetLen
         end

c('I'):   {ESCAPE for Pascal etc.}
      sym = next;  readsymbol(next)
      ->Pc(Sym) if 'A' <= Sym <= 'Z'
      Abort(M'I  ?' - ' '<<8 +Sym<<8)
Pc('A'):   {Compare}
Pc('D'):   {Compare records}
Pc('K'):   {Test set membership}
         cload(0, R6);  claim(R6)
         set both
         j = next;  readsymbol(next)
         if Sym = 'A' start
            if j <= 1 then k = '=' else k = '<'
            compare(lhs, rhs, k)
         else if Sym = 'K'
            Load(Lhs, Any);  Address(Rhs, -1)
            Rhs_Type = Integers
            Rxd(Tbt, Lhs_Base, Rhs)
            Release(Lhs_Base)
         else
            k = next;  readsymbol(next)
            Compare Records(Lhs, Rhs, K)
         finish
         stp = stp-2;  drop(lhs);  drop(rhs)
         skip(1, j+invert);                  ! short forward jump
         Invert = 0
         rxi(ADD, R6, 0, 1);        ! reduces to halfword: AIS R6,1
         forget reg(1<<r6);  CC CA = 0
         sset(R6, 0, VinR, 0)
         continue

Pc('B'):   {Create space}
         sym = next;  readsymbol(next)
         ownform = array;  Owntype = byte
         Claim Literal(Sym, Align)
         Select Literal Area
         Decvar_Disp = Ca;  Decvar_Base = Code
         Select Code Area
         continue

Pc('C'):
         ! 'refer to' FORTRAN parameter in CALL
         ! If parameter is not a simple variable, then store into a
         ! temporary to make it so.  Either way, convert to descriptor for
         ! address of simple variable for final parameter
         set lhs
         unless lhs_form = VinS and lhs_oper = 0 start
            t = any;  t = anyf if floating(lhs)
            load(lhs,t)
            hazard(lhs_base);      ! force into store temporary
         finish
         continue

Pc('I'):   {Add to set}
         Set Both
         Lrd(Rhs, Any)
         Address(Lhs, -1)
         Lhs_Type = Integers
         Rxd(Sbt, Rhs_base, Lhs);  Claim(Lhs_Base)
         Lhs_Type = Records
         Stp = Stp-1
         continue

Pc('G'):   {Make set null}
Pc('H'):   {assign set}
         k = Tag if Sym = 'H'
         if Next # 'S' start
            Temp Set
            Sstack(Rhs)
            Rhs_Type = 0         {show it's a temporary}
         else
            Readsymbol(Next)
         finish
         if Sym = 'G' then Cstack(0) else Vstack(k)
         Assign(1)
         continue

Pc('J'):   {Compare sets}
         Set Both
         Lrd(Lhs, R1)
         Lrd(Rhs, R2)
         Perm(Set Comp)
         if Next <= 1 start    {#, =}
            cput(0)
         else if Next = 3     {<=}
            cput(2)
         else    {Next = 2}    {>=}
            cput(1)
         finish
         Stp = Stp-2
         Sset(R6, 0, VinR, 0);  Claim(R6)
         Rxi(Xor, R6, 0, 1) if Next = 0 {#}
         Readsymbol(Next)
         continue

Pc('L'):   {Set operation}
         Pop Lhs;  X == Lhs
         Set Lhs
         if Lhs_Type # 0 start      {needs to be made temporary}
            Pop Lhs
            Temp Set
            Sstack(Lhs);  Drop(Lhs)
            Assign(1)
            Sset(Local, Frame-SetLen, VinS, 0)
            Rhs_Type = 0
            Set Lhs
         finish
         Load(Lhs, R1)
         Lrd(X, R2)
         Perm(Set Ops(next));  Readsymbol(Next)
         continue

Pc('S'):   {Swop top of stack elements}
         Set Both
         Stacked(Stp-1)_V == RHS
         Stacked(Stp)_V   == LHS
         continue

Pc('N'):   {check not NIL}
         if Control&Check Unass # 0 start
            Set Lhs;  Test Zero(Lhs);  Claim(Lhs_Base)
            RR(Bal, Link, Code)
         finish
         continue

Pc('W'):   {Stack WSP}
         Cstack(0)
         Rhs_Form = VinR
         Rhs_Base = Wsp
         continue

c('~'):                  ! alternate record format
      sym = next;  read symbol(next)
      if sym = 'A' start;              ! alt start
         decvar == gvar
         assemble(-2,labs,names)
         Alt Align = Alt Align!Falign
      else if sym = 'B';               ! alt end
         -> OUT
      else
         abort(m'AM45') if sym # 'C';          ! faulty intermediate code
         max frame = frame if frame > max frame
         frame = putative frame base
      finish
      continue
c('{'):
      gmode = -1
      assemble(gtype,labs,names)
      continue
c('}'):
      gmode = 0
      -> OUT if amode < 0;          ! end of %record %format defn.
      -> OUT if gvar_flag & primbit # 0;    ! prim routine reference
      if names > gstart start
         gvar_extra = parms
         for j = gstart+1,1,names cycle
            ap == var(j)
            parms = parms-1;  fp == var(parms)
            fp = ap;  fp_base = wsp
            ap_flag = ap_flag & (¬p in r) ! assigned if ap_flag&array bits = 0
         repeat
         abort(m'AM50') if parms < names
         if ap_type = strings and ap_xform & (array bits<<8 + 255) = V in S c
                                                    and ap_base # 0 start
            gvar_header = ap_disp
            fp_flag = fp_flag ! P in R;     ! mark as 'in-register' param
         finish
      finish
      gdisp = -1;         ! so locals are properly placed
      max parm = frame;   !start of local space
      -> OUT if amode # 0
      header(gvar)
      continue
c('H'):
      decvar == begin;  decvar_disp = new tag
      otype = 0;  spec = 0;  potype = 0
      if level # 0 start;    ! not outermost %begin
         cstack(decvar_disp)
         pop lhs;  lhs_type = 0;  call(lhs)
      finish
      block name = "BLOCK";         ! Fix up diagnostic name for "%begin" block
      assemble(0,labs,names)
      continue

   repeat;            ! --- end of main loop ---

   ! To catch the sinners!!
C(*):
      abort(m'?? '<<8 ! sym)

   routine ALIGN ALTERNATIVES
   ! Routine to fix up alternate record definitions - implicit parameters in:
   !  true frame base, putative frame base, max frame, alt first, alt align
      integer  n, mod, j
      record(varfm)name  v
      Falign = Alt Align
      n = putative frame base - true frame base
      return if n = 0 or alt align = align;      ! no padding or fullword req'd
      if alt align = 0 start;                    ! byte alignment possible
         mod = n
      else;                                      ! at least %short req'd
         return if n = 1;     ! can't move it back
         mod = 2;             ! n = 2,3
      finish
      ! now strip out extra unnecessary alignment
      for j = parms,1,alt first cycle
         v == var(j)
         v_disp = v_disp - mod
      repeat
      max frame = max frame - mod
   end;          ! align alternatives

c(';'):
   if level # 0 start
      if uncond jump # ca or (gvar_type = 0 and control&trusted = 0) start
         if control&trace # 0 and level = 1 start
            perm(enter trace); cput(0);      ! close down user-supplied routine
         finish
         return
      finish
   else;       ! level 0:  flush literals and gla
      gbyte(0) if ga&1 # 0
      claim literal(0,0) if lita&1 # 0
   finish
   Gvar_Flag = Gvar_Flag!Closed
   block mark(block end)
   Reset Optimisation Data
OUT:
   if amode >= 0 start;           ! end of declarative block
      activity(local) = 0 unless local = base5;   ! release old base register
      level = level-1;  local = breg(level)
   else;                         ! end of record format defn
      align alternatives
      frame = max frame if max frame > frame
      if amode = -2 start;                    ! end of alternative only
         old frame = frame
      else
         frame = (frame+align)&(¬align);   ! **** temporary ****
         abort(m'AM55') unless frame>>16 = 0      {only 16 bits worth}
         gvar_length <- frame
      finish
   finish
   frame = old frame;  extra frame = old extra frame
   uncond jump = old jump;  ca = proc ca
   var diags = old var diags
   new temp = temp base
   next temp = old next temp
   temp base = old temp base
   last line = -15

end;            !  assemble


!              -------- it all starts here ---------

   control = IMPCOM_flags & 255;         ! set compilation options
   control = control & (¬check bits) if control & trusted # 0;  ! force OPT

   select input(in)
   select output(object) 
   print symbol(init gla>>1);          ! Initial GLA allocation
   print symbol(init lit>>1);          ! specify literal area available to pass 3
   claim literal(init lit,align);      ! set literal base and initialise pass 3

   var(0) = 0;              !  for %RECORD(*) . . . . .
   var(max vars)_Length = SetLen       {for sets}
   parms = max vars
   cslen == current string(0)
   activity(wsp) = -1;  activity(code) = -1;  activity(0) = -1
   activity(gla) = -1;  activity(link) = -1

   for j = 0,1,max stack-1 cycle
      stak(j)_link == stak(j+1)
      dlist(j)_link == dlist(j+1)
   repeat
   stak(max stack)_link == null
   dlist(max stack)_link == null
   desc asl == stak(0);  dasl == dlist(0)
   using_link == null

   for stk(j) = 0 for j = 0,1,max cycle
   for stp = 0;  for == for stk(0)

   read symbol(next);          !  Prime SYM/NEXT pair
   Spec = 0
   decvar == begin
   assemble(2,0,0)
   close files
   signal 15,3 if faulty # 0
endofprogram