!!%include "Sysinc:com.inc"
{Include file for compiler communication}
%recordformat impcomfm(%integer statements, flags, code, gla, diags, perm,
                        %string(31) file,
                        %string(63) Option)
%externalrecord(impcomfm)%spec IMPCOM
!!%endoffile
!   **************************************************************
!   *                                                            *
!   *          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
