%external %string(255)%fn cliparam
%string(255) s
%integer strad
  *SWI_16
  *STR_0,strad
  s = ""
  %cycle
    %if BYTE(strad)<=31 %or LENGTH(s)=255 %then %exit
    s = s.BYTE(strad)
    strad = strad+1
  %repeat
  %result = "" %unless s -> (" ").s
  %result = s {Command name stripped off}
%end

%const %integer blocked=0, avail=1

%external %string(255) %fn ItoH(%integer i, pl)
%constbytearray HexByte(0:15) = '0', '1', '2', '3',
                                '4', '5', '6', '7',
                                '8', '9', 'a', 'b',
                                'c', 'd', 'e', 'f'
%integer shift
%string(255) s = ""
  %for shift = 0, 4, pl*4-4 %cycle
    s = HexByte((i>>shift)&15).s
  %repeat
  %result = s
%end

%const %integer -
   Def Inst = 1,            { Instruction }
   Def Byte = 2,            { = byte      }
   Def Char = 4,            { = "chars"   }
   Def Word = 8,            { EQUD word   }
   Def Labelled = 16,       {DATA ...     }
   Def Branch Dest = 32,    {LAB  ...     }
   Def Call Dest = 64,      {PROC ...     }
   Def Access = 128         { B somewhere }

%externalstring(255) %fn  Instr Decode(%integer Pc, N)
   %integer CC, Type, Op, Rn, Rd, Shf, Shift, Imm, Offset, Operand
   %integer X, Y, Z, Printed = 0
   %string(1) Sign
   %switch F(0:7), Fm(0:31)
   %conststring(3)%array Fn(0:15) =  "AND", "EOR", "SUB", "RSB",
                                     "ADD", "ADC", "SBC", "RSC",
                                     "TST", "TEQ", "CMP", "CMN",
                                     "ORR", "MOV", "BIC", "MVN"
   %conststring(2)%array Cond(0:15) = "EQ",  "NE",  "CS",  "CC",
                                      "MI",  "PL",  "VS",  "VC",
                                      "HI",  "LS",  "GE",  "LT",
                                      "GT",  "LE",    "",  "NV"

  %string(255) result string = ""
  %routine Print string(%string(255) s)
    result string = result string.s
  %end
  %routine space
    result string = result string." "
  %end
  %routine spaces(%integer n)
    result string = result string." " %for n = 1,1,n
  %end
  %routine print hex(%integer i, pl)
    result string = result string.itoh(i, pl)
  %end
  %routine print symbol(%integer i)
    result string = result string.i
  %end
  %routine Write(%integer i, pl)
    result string = result string.itos(i, pl)
  %end

   %routine Register(%integer N)
      %if N = 15 %start
         Printstring("PC")
      %else
         Printsymbol('R')
         Write(N, 0)
      %finish
   %end

   %routine F Register(%integer N)
       Printsymbol('F')
       Write(N, 0)
   %end

   %routine Put(%string(255) S)
      Printstring(S)
      Printed = Printed+Length(S)
   %end

   %routine Show Label(%integer N)
      Printstring("L")
      Print Hex(N, 8)
   %end

   %routine Show Hex(%integer N, P)
      Printstring("&")
      Print Hex(N, P)
   %end

   %routine Show Shift(%integer Shift, Reg)
      %integer X, Y, Rs
      %string(3) S
      %switch Sh(0:7)
      Register(Reg)
      %return %if Shift = 0
      Y = Shift>>3
      X = Shift&7
      Space
      ->Sh(X)
Sh(1):  S = "LSL";  ->Sh2
Sh(3):  S = "LSR";  ->Sh2
Sh(5):  S = "ASR";  ->Sh2
Sh(7):  S = "ROR";  ->Sh2
Sh2:    Rs = Shift>>4
        Printstring(S);  Space;  Register(Rs);  %return
Sh(0):  S = "LSL";  ->Sh1
Sh(2):  S = "LSR";  ->Sh1
Sh(4):  S = "ASR";  ->Sh1
Sh(6):  S = "ROR";  S = "" %if Y = 0
Sh1:    %if S = "" %start
           Printstring("RRX")
        %else
           Printstring(S);  Printstring(" #");  Write(Y, 0)
        %finish
   %end

   %routine Show Constreg(%integer N)
      %integer X
      X = N&7
      %if N&8 = 0 %start
         F Register(X)
      %else
         %if X <= 5 %start
            Write(X, 0);  Printstring(".0")
         %else %if X = 6
            Printstring("0.5")
         %else
            Printstring("10.0")
         %finish
      %finish
   %end

   Printed = 0
   CC   = N>>28
   Type = N>>25&7
   Rd   = (N>>12)&15
   Rn   = (N>>16)&15
   ->F(Type)

F(2_000):
F(2_001):   Op = (N>>21)&15
            Shf = (N>>8)&15
            Shift = (N>>4)&255
            Put(Fn(Op));  Put(Cond(CC))
            Put("S") %if N&2_0000 000 0000 1 0000 0000 000000000000 # 0
            Put("P") %if Rd = 15 %and 8 <= Op <= 11
            Spaces(8-Printed)
            Register(Rd) %and Printstring(", ") %unless 8 <= Op <= 11
            Register(Rn) %and Printstring(", ") %if Op # 13 %and Op # 15
            %if N&16_0200 0000 # 0 %start    {Shf Imm}
               X = 2*Shf
               Imm = N&255
               %if X > 8 %start
                  X = X-8
                  Y = (Imm<<24)>>X
               %else
                  Y = (Imm<<(32-X)) ! (Imm>>X)
               %finish
               Printstring("#")
               %if -16_FFFF <= Y <= 16_FFFF %start
                  %if ' ' <= Y <= 126 %start
                     Write(Y, 0)
                     Printstring("; '")
                     Printsymbol(Y)
                     Printsymbol('''')
                  %else
                    Write(Y, 0)
                  %finish
               %else
                 Show Hex(Y, 8)
               %finish
            %else                           {Shift Rm}
               Show Shift(Shift, N&15)
            %finish
            ->Done


F(2_010):
F(2_011):   Type = N>>24&15
            %if N&16_0010 0000 = 0 %then Put("STR") %else Put("LDR")
            Put(Cond(CC))
            Put("B") %if N&16_0040 0000 # 0
            Put("T") %if N&16_0020 0000 # 0 %and Type&1 = 0
            Spaces(8-Printed)
            Register(Rd)
            Printstring(", ")
            Offset = N&4095
            Sign = "";  Sign = "-" %if N&16_0080 0000 = 0
            Printsymbol('[');  Register(Rn)
            %if Type = 2_0100 %start
               Printsymbol(']')
               %if Offset # 0 %start
                  Printstring(", #");  Printstring(Sign);  Show Hex(Offset, 3)
               %finish
            %else %if Type = 2_0101
               %if Offset # 0 %start
                  Printstring(", #");  Printstring(Sign);  Show Hex(Offset, 3)
               %finish
               Printsymbol(']')
               Printsymbol('!') %if N&16_0020 0000 # 0
            %else %if Type = 2_0110
               Printstring("]")
               %if Offset # 0 %start
                  Printstring(", ")
                  Printstring(Sign)
                  Show Shift(Offset>>4, N&15)
               %finish
            %else{%if Type = 2_0111}
               Printsymbol(',')
               Printstring(Sign)
               Show Shift(Offset>>4, N&15)
               Printsymbol(']')
               Printsymbol('!') %if N&16_0020 0000 # 0 %and Offset # 0
            %finish
            ->Done

F(2_100):   %if N&16_0010 0000 = 0 %then Put("STM") %else Put("LDM")
            Put(Cond(CC))
            X = N>>23&3
            %if X&1 # 0 %then Put("I") %else Put("D")
            %if X&2 # 0 %then Put("B") %else Put("A")
            Spaces(8-Printed)
            Register(Rn)
            Printsymbol('!') %if N&16_0020 0000 # 0
            Y = 1
            Z = 1
            Printstring(", {")
            %for X = 0, 1, 15 %cycle
               %if N&Y # 0 %start
                  Printsymbol(',') %if Z = 0
                  Register(X)
                  Z = 0
               %finish
               Y = Y<<1
            %repeat
            Printsymbol('}')
            Printsymbol('^') %if N&16_0040 0000 # 0
            ->Done

F(2_101):   Put("B");  Put("L") %if N&16_0100 0000 # 0
            Put(Cond(CC))
            Spaces(8-Printed)
            X = N&16_00FF FFFF
            X = X!16_FF00 0000 %if X&16_0080 0000 # 0
            Show Label(Pc+(2+X)*4)
            ->Done

F(2_110):   %if N&16_0010 0000 = 0 %then Put("STF") %else Put("LDF")
            Put(Cond(CC))
            %if N&16_0040 0000 = 0 %start
               %if N&16_0000 8000 = 0 %then Put("S") %else Put("D")
            %else
               %if N&16_0000 8000 = 0 %then Put("E") %else Put("P")
            %finish
            Spaces(8-Printed)
            F Register(N>>12&7)
            Printstring(", [")
            Register(N>>16&15)
            X = (N&255)<<2
            %if N&16_0100 0000 = 0 %start
               Printstring("]")
               %if X # 0 %start
                  Printstring(", #")
                  Printsymbol('-') %if N&16_0080 0000 = 0
                  Show Hex(X, 3)
               %finish
            %else
               %if X # 0 %start
                  Printstring(", #")
                  Printsymbol('-') %if N&16_0080 0000 = 0
                  Show Hex(X, 3)
               %finish
               Printstring("]")
            %finish
            Printsymbol('!') %if N&16_0020 0000 # 0
            ->Done

F(2_111):   %if N&16_0100 0000 # 0 %start
               Put("SWI");  Put(Cond(CC));  Spaces(8-Printed)
               Show Hex(N&16_00FF FFFF, 6)
               ->Done
            %finish
            %unless N>>8&15 = 1 %start
              Print string("EQUD    "); Show Hex(N,8)
              -> Done
            %finish

            {this leaves the floating point stuff}

            %if N&16_0000 0010 = 0 %start     {CPDO}
               X = (N>>19)&2_11110 ! (N>>15)&1
               ->Fm(X)
Fm(2_00000):     Put("ADF");  ->FnOP
Fm(2_00010):     Put("MUF");  ->FnOP
Fm(2_00100):     Put("SUF");  ->FnOP
Fm(2_00110):     Put("RSF");  ->FnOP
Fm(2_01000):     Put("DVF");  ->FnOP
Fm(2_01010):     Put("RDF");  ->FnOP
Fm(2_01100):     Put("POW");  ->FnOP
Fm(2_01110):     Put("RPW");  ->FnOP
Fm(2_10000):     Put("RMF");  ->FnOP
Fm(2_10010):     Put("FML");  ->FnOP
Fm(2_10100):     Put("FDV");  ->FnOP
Fm(2_10110):     Put("FRD");  ->FnOP
Fm(2_11000):     Put("POL");  ->FnOP

FnOP:          Put(Cond(CC))
               %if N&16_0008 0000 = 0 %start
                  %if N&16_0000 0080 = 0 %then Put("S") %else Put("D")
               %else
                  %if N&16_0000 0080 = 0 %then Put("E") %else Put("?")
               %finish
               X = N>>5&3
               %if X # 0 %start
                  %if X = 2_01 %then Put("P") %else -
                  %if X = 2_10 %then Put("M") %else Put("Z")
               %finish
               Spaces(8-Printed)
               F Register(N>>12&7)
               Printstring(", ")
               F Register(N>>16&7)
  Op Rest:     Printstring(", ")
               Show ConstReg(N&15)
               ->Done

Fm(2_00001):     Put("MVF");  ->FnOP1
Fm(2_00011):     Put("MNF");  ->FnOP1
Fm(2_00101):     Put("ABS");  ->FnOP1
Fm(2_00111):     Put("RND");  ->FnOP1
Fm(2_01001):     Put("SQT");  ->FnOP1
Fm(2_01011):     Put("LOG");  ->FnOP1
Fm(2_01101):     Put("LGN");  ->FnOP1
Fm(2_01111):     Put("EXP");  ->FnOP1
Fm(2_10001):     Put("SIN");  ->FnOP1
Fm(2_10011):     Put("COS");  ->FnOP1
Fm(2_10101):     Put("TAN");  ->FnOP1
Fm(2_10111):     Put("ASN");  ->FnOP1
Fm(2_11001):     Put("ACS");  ->FnOP1
Fm(2_11011):     Put("ATN");  ->FnOP1

Fm(*):           Put("???")

FnOP1:         Put(Cond(CC))
               %if N&16_0008 0000 = 0 %start
                  %if N&16_0000 0080 = 0 %then Put("S") %else Put("D")
               %else
                  %if N&16_0000 0080 = 0 %then Put("E") %else Put("?")
               %finish
               X = N>>5&3
               %if X # 0 %start
                  %if X = 2_01 %then Put("P") %else -
                  %if X = 2_10 %then Put("M") %else Put("Z")
               %finish
               Spaces(8-Printed)
               F Register(N>>12&7)
               ->Op Rest
            %else %if N&16_0010F000 = 16_0010F000  {CPST}
               X = N>>21&7
               %if X = 2_100 %start
                  Put("CMF")
               %else %if X = 2_101
                  Put("CNF")
               %else %if X = 2_110
                  Put("CMFE")
               %else %if X = 2_111
                  Put("CNFE")
               %else
                  Put("???")
               %finish
               Put(Cond(CC))
               Spaces(8-Printed)
               F Register(N>>16&7)
               ->Op Rest
            %else                       {CPRT}
               X = N>>20&15
               %if X = 0 %start
                  Put("FLT")
               %else %if X = 1
                  Put("FIX")
               %else %if X = 2
                  Put("WFS")
               %else %if X = 3
                  Put("RFS")
               %else %if X = 4
                  Put("WFC")
               %else %if X = 5
                  Put("RFC")
               %else
                  Put("???")
               %finish
               Put(Cond(CC))
               %if N&16_0008 0000 = 0 %start
                  %if N&16_0000 0080 = 0 %then Put("S") %else Put("D")
               %else
                  %if N&16_0000 0080 = 0 %then Put("E") %else Put("?")
               %finish
               Y = N>>5&3
               %if Y # 0 %start
                  %if Y = 2_01 %then Put("P") %else -
                  %if Y = 2_10 %then Put("M") %else Put("Z")
               %finish
               Spaces(8-Printed)
               %if X = 0 %start       {FLT}
                  F Register(N>>16&7)
                  Printstring(", ")
                  Register(N>>12&15)
               %else %if X = 1        {FIX}
                  Register(N>>12&15)
                  Printstring(", ")
                  Show Constreg(N&15)
               %else
                  Register(N>>12&15)
               %finish
            %finish

Done:
  %result = Result string
%end

!###########################################################################

%const %integer Data = 0, Instr = 1, B = 2, BL = 4, Swi = 8

%externalintegerfn Get Simple Type(%integer Pc, N, %integer %name Dest, Access)
%const %integer Always = 14, AddOp=4, MovOp = 1, pcreg = 15
   %integer CC, Type, Op, Rn, Rd, Rs, Shf, Shift, Imm, Offset, Operand
   %integer X, Y, Z
   %switch F(0:7), Fm(0:31)

   Access = Avail

   CC   = N>>28
   Type = N>>25&7
   Rd   = (N>>12)&15
   Rn   = (N>>16)&15
   ->F(Type)

   F(2_000): F(2_001):
   !%begin
     Op = (N>>21)&15
     %if (Op=AddOp %or Op=MovOp) %and Rd=pc reg %and cc=always %start
       Access = blocked
     %finish
     %if op#13 %and op#15 %and %c
         Rd=pc reg %and Rn#pc reg %and cc=always %then access=blocked

     %if N&16_0200 0000 = 0 %start
       Shift = (N>>4)&255
       Rs = Shift >> 4
       %result=Data %if Rs=pc reg %and Shift&1 # 0
     %finish

     %result = Instr
   !%end

   F(2_010): F(2_011):
   !%begin
     %result=Instr
   !%end

   F(2_100):
   !%begin
     %if N&16_0000 ffff = 0 %then %result=Data ;! LDM Rn!,{} for instance
     %if N&16_0010 0000 = 0 %then %result=Instr
     %if N&(1<<15)#0 %and cc=always %then Access=blocked
     %result = Instr
   !%end

   F(2_101):
   !%begin
     Type = B;  Type = BL %if N&16_0100 0000 # 0
     X = N&16_00FF FFFF
     X = X!16_FF00 0000 %if X&16_0080 0000 # 0
     Dest = Pc+(2+X)*4
     %if type=B %and CC=Always %then Access=Blocked
     %result = Type
   !%end

   F(2_110):
   !%begin
     %result = Instr
   !%end

   F(2_111):
   !%begin
     %if N&16_0100 0000 # 0 %start
       Dest = N&16_00FF FFFF
       %result = SWI
     %finish

     %result = Data %if N>>8&15 # 1

     {this leaves the floating point stuff}

     %if N&16_0000 0010 = 0 %start     {CPDO}
       -> Fm((N>>19)&2_11110 ! (N>>15)&1)

       Fm(2_00000): Fm(2_00010): Fm(2_00100): Fm(2_00110):
       Fm(2_01000): Fm(2_01010): Fm(2_01100): Fm(2_01110):
       Fm(2_10000): Fm(2_10010): Fm(2_10100): Fm(2_10110):
       Fm(2_11000):

       Fm(2_00001): Fm(2_00011): Fm(2_00101): Fm(2_00111):
       Fm(2_01001): Fm(2_01011): Fm(2_01101): Fm(2_01111):
       Fm(2_10001): Fm(2_10011): Fm(2_10101): Fm(2_10111):
       Fm(2_11001): Fm(2_11011):

       %if N&16_0008 0000 # 0 %and %c
           N&16_0000 0080 # 0 %then %result=Data
       %result = Instr

       Fm(*):
       %result=Data

     %else %if N&16_0010F000 = 16_0010F000  {CPST}
       X = N>>21&7
       %result = Data %unless 2_100 <= X <= 2_111
       %result = Instr

     %else                       {CPRT}
       X = N>>20&15
       %result = Data %unless 0 <= X <= 5
       %if N&16_0008 0000 # 0 %and %c
           N&16_0000 0080 # 0 %then %result = Data
       %result = Instr
     %finish
   !%end
%end
!###########################################################################

%external %predicate  Valid Instr(%integer PC)
  %integer Type, Dest, Access
  Type = Get Simple type(PC, INTEGER(PC), Dest, Access)
  %if Type#Data %then %true %else %false
%end

%begin
%routine  Decode(%integer from, to, start, end)
%byte %array tag(from:to)

  %routine Text(%string(255) S)
    print string(S)
    newline
  %end

  %string(4) %fn sanitised(%integer is)
    %string (4) s = ""
    %integer i

    i = (is)&255
    i = '.' %unless ' '<=i<='~'; s = s.i

    i = (is >> 8)&255
    i = '.' %unless ' '<=i<='~'; s = s.i

    i = (is >> 16)&255
    i = '.' %unless ' '<=i<='~'; s = s.i

    i = (is >> 24)&255
    i = '.' %unless ' '<=i<='~'; s = s.i

    %result=s
  %end

  %routine Print Decode(%integer from, to)

    %predicate pr(%integer a)
      %true %if ' '<=BYTE(a)<='~'
      %false
    %end

    %string(255) %fn CharStr(%integer a)
      %string(255) guts
          %if tag(a)&Def char#0 %and pr(a) %start
            guts  = """".BYTE(a).""""
          %else
            guts  = itos(BYTE(a), 0)
            guts  = " ".guts %while LENGTH(guts)<3
          %finish
      %result = guts
    %end
    %string(10) Lab
    %string(255) Guts
    %const %integer max guts = 50
    %integer a = from, type, dest, access
    %cycle
      %if tag(a)&Def labelled#0 %start
        %if tag(a)&Def Call Dest#0 %then lab = "P" -
        %else %if tag(a)&Def Branch Dest#0 %then Lab = "L" -
        %else lab = "D"
        Lab = Lab.Itoh(a, 8)." "
      %else
        Lab = "          "
      %finish

      %if tag(a)&Def Inst#0 %start
        guts = Instr decode(a, INTEGER(a))
        Type = Get Simple type(a, INTEGER(a), Dest, Access)
        %if Type&(B!BL)#0 %start
          %unless from <= dest <= to %start
            guts = guts."   ; Outside module???"
          %finish
        %finish
        guts <- guts."                                                "
      %elseif tag(a)&Def Word#0
        guts = "EQUD    &".itoh(INTEGER(a),8)."                       ".
                                "                                     "
      %elseif tag(a)&(Def Byte!Def Char)#0
        guts = "=       "
        %if pr(a) %and pr(a+1) %and pr(a+2) %and pr(a+3) %start
          guts = "=       """.BYTE(a).BYTE(a+1).BYTE(a+2).BYTE(a+3).""""
        %else
          guts = guts.CharStr(a).", "
          guts = guts.CharStr(a+1).", "
          guts = guts.CharStr(a+2).", "
          guts = guts.CharStr(a+3)
        %finish
        guts = guts."                                                "
        LENGTH(guts) = 20
      %else
        ! Unknown - could be data or instr...
        %if Valid Instr(a) %start
          guts = Instr decode(a, INTEGER(a))
          guts <- guts."                                            "
        %else
          guts = "EQUD    &".itoh(INTEGER(a),8)."                      ".
                                   "                                "
        %finish
        LENGTH(guts) = Max guts-6
        guts = guts."; ".sanitised(INTEGER(a))
      %finish
      LENGTH(guts) = max guts
      text(Lab.guts."; ".itoh(a,7).": ".itoh(INTEGER(a),8))
      a = a+4
      %return %if a >= to
    %repeat

  %end

  %string(255) %fn StringAt(%integer Str addr)
    %string (255) Title
    Title = ""
    %cycle
      %if BYTE(Str addr)=0 %then %result=Title
      Title = Title.BYTE(Str addr)
      Str addr = Str addr+1
    %repeat
  %end

  %routine Mark string(%integer str start)
    %return %if BYTE(str start)=0
    tag(str start) = tag(str start)!Def labelled
    %cycle
      tag(str start) = tag(str start)!Def Char
      %if BYTE(str start) = 0 %then tag(Str start)=tag(Str start)! %c
                                                     Def byte %and %return
      str start=str start+1
    %repeat
  %end

  %routine Mark word(%integer ad)
    tag(ad) = tag(ad)!Def word
    tag(ad+1) = Def word
    tag(ad+2) = Def word
    tag(ad+3) = Def word
  %end

  %routine Mark Byte(%integer ad)
    tag(ad) = tag(ad)!Def Byte
  %end

  %routine Mark Jump(%integer ad)
    tag(ad) = tag(ad)!Def Inst!Def Labelled!Def Branch dest
  %end

  %routine Mark Proc(%integer ad)
    tag(ad) = tag(ad)!Def Inst!Def Labelled!Def Call dest
  %end

  %routine %spec Decode Branch Search(%integer Entry, From, To)
  %routine %spec Decode Proc Search(%integer Entry, From, To)

  %routine Decode Search(%integer Entry, from, to)
    %integer Type, Dest, Access=avail
    %return %unless From <= Entry < To %and Entry&3=0
    %return %if tag(Entry)&Def Inst#0 {Already searched}
    %cycle
      %exit %if tag(Entry)&(Def Char!Def Byte!Def Inst)#0 %or Entry >= To
      Type = Get Simple type(Entry, INTEGER(Entry), Dest, Access)
      %if Type=Data %then %start
        ! Somehow missed end of instr seq.
        ! Backtrack and undo?
        %return
      %finish
      tag(Entry) = tag(Entry)!Def Inst
      %if Type&B # 0 %start
        %unless from <= dest < To %start
!!!          tag(Entry) = tag(Entry)&(\Def Inst)
!!!          %return
        %finish
        Decode Branch Search(dest, from, to)
        tag(entry)=tag(Entry)!Def access %and %return %if Access=blocked
        Entry = Entry+4
      %else %if Type&BL # 0
        %unless from <= dest < To %start
!!          tag(Entry) = tag(Entry)&(\Def Inst)
!!          %return
        %finish
        Decode Proc Search(dest, from, to)
        Entry = Entry+4
      %else %if Type&SWI # 0
        %if Dest&(\16_20000)=1 %start
          ! Skip in-line string
          tag(Entry)=tag(Entry)!Def access
          Entry=Entry+4
          Mark String(Entry)
          %while BYTE(Entry)#0 %cycle
            Entry = Entry+1
          %repeat
          Entry = (Entry+4)&(\3)
        %else
          Entry = Entry + 4
        %finish
      %else
        tag(entry)=tag(Entry)!Def access %and %return %if Access=blocked
        Entry = Entry+4
      %finish
    %repeat
  %end

  %routine Decode Branch Search(%integer Entry, From, To)
    %return %unless From <= Entry <= To %and Entry&3=0
    tag(Entry) = tag(Entry)!Def labelled!Def branch dest
    Decode Search(Entry, From, To)
  %end

  %routine Decode Proc Search(%integer Entry, From, To)
    %return %unless From <= Entry <= To %and Entry&3=0
    tag(Entry) = tag(Entry)!Def labelled!Def call dest
    Decode Search(Entry, From, To)
  %end

  %predicate Decode Title String(%integer Str addr, %string(*) %name Title)
    Title = ""
    %cycle
      %if BYTE(Str addr)=0 %then %true
      %unless '!' <= BYTE(Str addr) <= '~' %then %false
      Title = Title.BYTE(Str addr)
      Str addr = Str addr+1
    %repeat
  %end

  %predicate Decode Help String(%integer Str addr, %integer %name Help length)
    Help length = 0
    %cycle
       %if BYTE(Str addr) = 0 %then %true
       %if BYTE(Str addr) >= 127 %then %false
       str addr = Str addr+1; Help length = Help length+1
    %repeat
  %end

  %routine Decode Help Table(%integer Start)
  %record %format  Help info fm(%integer code,
                                %byte min params, flags0, max params, flags 1,
                                %integer syntax, text)
  %string(255) %fn  Params(%integer Low, High)
    %result = "" %if low=High=0
    %result = " (".itos(High,0)." param)" %if low=high=1
    %result = " (".itos(High,0)." params)" %if low=high
    %result = " (".itos(low,0)."-".itos(high,0)." params)" 
  %end

  %record (Help info fm) %name Help info
  %string (255) Help param
    %cycle
      Help param = StringAt(Start)
      Mark string(start)
      %return %if Help param = ""
      Start = (Start + LENGTH(Help param) + 1 + 3) & (\3)
      Help info == RECORD(Start)
      Mark Word(start)
      Mark Byte(start+4); Mark Byte(start+5)
      Mark Byte(start+6); Mark Byte(start+7)
      Mark word(start+8); Mark word(start+12)
      %if Help info_code = 0 %start
        Text("Help on: ".help param.
             Params(Help info_min params,
                    Help info_max params))
      %else
        Decode proc search(from+Help info_code, from, to)
        Text("Entry:   ".help param.
             Params(Help info_min params,
                    Help info_max params))
      %finish
      Mark string(from+Help info_syntax)
      Mark string(from+Help info_text)
      start = start+16
    %repeat
  %end

  %routine Decode SWI Table(%integer Start)
    %cycle
      %cycle
        %exit %if BYTE(Start)=0
        tag(Start)=Def char
        Start=Start+1
      %repeat
      tag(Start)=tag(start)!Def char
      Start=Start+1
    %repeat %until BYTE(start)=0
    tag(start)=tag(start)!Def char
  %end

  %routine Scan branches(%integer from, to)
  %integer address, Type, Dest, Access
    %for address = From, 4, To-4 %cycle
      %if tag(address)!tag(address+1)!tag(address+2)!tag(address+3)=0 %start
        Type = Get Simple type(address, INTEGER(address), Dest, Access)
        %if Type&(B!BL)#0 %start
          %if from < dest <= to %and Access=Blocked %start
            tag(address)=tag(address)!Def Inst
            Decode branch search(Dest, from, to)
          %else
! Leave undecided...  tag(address)=tag(address)!Def word
          %finish
        %finish
      %finish
    %repeat
  %end

  %predicate stringch(%integer i)
    i = BYTE(i)
    %true %if i=0 %or i=10 %or i=13 %or ' '<=i<='~'
    %false
  %end

  %routine Scan strings(%integer from, to)
  %integer a, Type, Dest, Access
    %for a = From, 4, To-4 %cycle
      %if tag(a)!tag(a+1)!tag(a+2)!tag(a+3)=0 %start
        %if stringch(a-4) %and stringch(a-3) %c
        %and stringch(a-2) %and stringch(a-1) %c
        %and stringch(a) %and stringch(a+1) %c
        %and stringch(a+2) %and stringch(a+3) %c
        %and stringch(a+4) %and stringch(a+4) %c
        %and stringch(a+6) %and stringch(a+7) %c
        %start
          %if INTEGER(a)=0 %then tag(a)=tag(a)!Def Word %else %start
            tag(a)=tag(a)!Def Char;tag(a+1)=tag(a+1)!def char
            tag(a+2)=tag(a+2)!Def Char;tag(a+3)=tag(a+3)!def Char
          %finish
          %if BYTE(a+2)=BYTE(a+3)=0 %then tag(a)=tag(a)!Def Word
        %finish
      %finish
    %repeat
  %end

  %routine Scan data(%integer from, to)
  %integer a
    %for a = From, 4, To-4 %cycle
      %if tag(a)!tag(a+1)!tag(a+2)!tag(a+3)=0 %start
        %if 16_ffff0000 <= INTEGER(a) <= 16_0000ffff %start
          tag(a) = Def Word
        %finish
      %finish
    %repeat
  %end

  %routine Confirm(%integer first, last, from, to)
    ! if all the words between first and last can take the
    ! same type as them, set them to that...
  %end

  %routine Patch up alternatives(%integer from, to)
  %integer a, start
    %for a = From, 4, To-4 %cycle
      %if tag(a)!tag(a+1)!tag(a+2)!tag(a+3)=0 %start
        start=a+4
        %while tag(start)!tag(start+1)!tag(start+2)!tag(start+3)=0 %cycle
          start=start+4
        %repeat
        Confirm(a-4, start, from, to)
      %finish
    %repeat
  %end

  %predicate  printable(%integer ad)
    %true %if ' ' <= BYTE(ad) <= '~'
    %false
  %end

  %predicate  all printable(%integer ad)
    %if printable(ad) %and printable(ad+1) %c
    %and printable(ad+2) %and printable(ad+3) %then %true
    %false
  %end

  %routine Undo wrong instructions(%integer From, to)
  %integer Dest, Access, Type, a
    %for a = to-4, -4, from+4 %cycle
      %if tag(a)&Def Char#0 %and tag(a-4)=0 %start
        Type = Get Simple type(a-4, INTEGER(a-4), Dest, Access)
        %if Type&(Instr!BL)#0 %and access#blocked %start
          tag(a-4) = Def Char
        %finish
        %continue
      %elseif tag(a)&def char#0 %and tag(a-4)&Def word#0
        tag(a-4) = tag(a-4)&(\Def Word)!Def char %if all printable(a-4)
      %elseif tag(a)&Def Word#0 %and tag(a-4)=0
        Type = Get Simple type(a-4, INTEGER(a-4), Dest, Access)
        %if Type&(Instr!BL!SWI)#0 %and access#blocked %start
          %if all printable(a-4) %start
            tag(a-4) = Def Char
          %else
            tag(a-4) = Def Word
          %finish
        %finish
        %continue
      %finish
      %if (tag(a)&Def Inst=0 %or (tag(a)=0 %and %not valid instr(a))) %c
       %and (tag(a-4)=0 %or %c
       (tag(a-4)&Def Inst#0 {%and Dubious branch})) %start
        ! if a-4 can drop through then it is not an instr...
        ! Give it DATA?
        Type = Get Simple type(a-4, INTEGER(a-4), Dest, Access)
        %if Type&Data=0 %c
        %and Access#blocked %and tag(a-4)&Def Labelled#0 %start
          tag(a)=Def Word %if tag(a)=0
          tag(a-4)=tag(a-4)&(\Def Inst)!(tag(a)&(Def char!Def Word!Def Byte))
        %finish
      %finish
    %repeat
  %end

  %routine Grab more strings(%integer from, to)
  %integer a
    %for a = from, 4, to-4 %cycle
      %if tag(a) & Def char # 0 %and tag(a+4) & Def word # 0 %start
        %if all printable(a+4) %start
          tag(a+4) = tag(a+4) & (\def word) ! def char
        %finish
      %finish
    %repeat
  %end

  %routine Set any illegal instrs to DATA(%integer from, to)
  %integer a
    %for a = from, 4, to %cycle
      %if tag(a)=0 %and tag(a+1)=0 %and tag(a+2)=0 %and tag(a+3)=0 %start
        %if %not valid instr(a) %then tag(a)=Def Word
      %finish
    %repeat
  %end

  %integer i
  tag(i) = 0 %for i = from, 1, to

  ! Determine type - module or file: Follow entry points as appropriate.
  %record %format Module Header Fm( %c
    %integer Start Code, Init Code, Final Code, Service Handler,
             Title String, Help String, Help table, SWI Chunk,
             SWI Handler, SWI Table, SWI Decode)
  %record (Module Header Fm) %name Module Header

  %integer Code extent = to - from

  Module Header == RECORD(from)

  %if  0 <= Module Header_Init Code  < Code extent %c
  %and 0 <= Module Header_Final Code < Code extent %c
  %and 0 <= Module Header_Service Handler < Code extent %c
  %and 0 <= Module Header_Title String < Code extent %c
  %and 0 <= Module Header_Help String < Code extent %c
  %and 0 <= Module Header_Help Table  < Code extent %c
  %then %start

    ! Good chance of it being a module.

    %string (255) Title

    %if Decode Title String(from+Module Header_Title String, Title) %start
      Mark string(from+Module Header_title string)
      Text("*** Decoding relocatable module '".Title."' ***")
    %else
      Text("*** This appears to be a module but the title string is invalid")
    %finish

    Mark Word(i) %for i = from, 4, from+16_18

    %integer Help Length
    %if Decode Help String(from+Module Header_Help String, Help Length) %start
      Mark string(from+Module Header_Help String)
      ! Help text marked. Possibly also print here too...
    %finish

    %if Module Header_Help Table#0 %start
      Decode Help Table(from+Module Header_Help Table)
      ! Table entries marked appropriately; entry points recursively decoded
    %finish

    %if Module Header_SWI Handler < Code extent %start
      %if Module Header_SWI Handler#0 %c
      %and Module Header_SWI Handler&2_11111100 00000000 00000000 00000011=0 %c
      %then %start
        Mark Word(ADDR(Module Header_SWI Handler))
        Decode Proc Search(from+Module header_SWI Handler, from, to)
      %finish

      ! Only list SWI no's if SWIs valid.
      %if Module Header_SWI Chunk&2_11111111 00000000 00000000 00011111=0 %c
      %then %start
        Mark Word(ADDR(Module Header_SWI Chunk))
        Text("Module handles SWIs from ".
              itoh(Module Header_SWI Chunk,6)." to ".
              itoh(Module Header_SWI Chunk+64-1,6))
      %finish

    %if Module Header_SWI Table < Code extent %start
      %if Module Header_SWI Table#0 %c
      %and Module Header_SWI Table&2_11111100 00000000 00000000 00000011=0 %c
      %then %start
        Mark Word(ADDR(Module Header_SWI Table))
        Decode SWI Table(from+Module Header_SWI Table)
      %finish

    %if Module Header_SWI Decode < Code extent %start
      %if Module Header_SWI Decode#0 %c
      %and Module Header_SWI decode&2_11111100 00000000 00000000 00000011=0 %c
      %then %start
        Mark Word(ADDR(Module Header_SWI Decode))
        Decode Proc Search(from+Module header_SWI Decode, from, to)
      %finish

    %finish;%finish;%finish

    ! do main entry last as it is special...

    %if  0 < Module Header_Start Code < Code extent %start
      Text("Entry point is offset.")
      Decode Proc Search(from+Module header_Start Code, from, to)
    %elseif Module header_start code = 0
      ! No language entry
    %else
      %if Valid Instr(from) %start
        Decode Proc Search(from, from, to)
        ! First word is an instruction, not an offset
      %else
        Text("*** First word not valid instruction ***")
      %finish
    %finish
    Decode Proc Search(from+Module header_Init Code, from, to) %c
      %unless Module header_Init Code=0
    Decode Proc Search(from+Module header_Final Code, from, to) %c
      %unless Module header_Final Code=0
    Decode Proc Search(from+Module header_Service Handler, from, to) %c
      %unless Module header_Service Handler=0
  %else
    ! Probably not a module - treat as a file
    Decode Proc Search(from, from, to)
  %finish

  Scan branches(From, to)
  Scan strings(From, to)
  Scan data(from, to)
  Undo wrong instructions(from, to) {Inst (not jump) followed by Data}
  Grab more strings(from, to)
  Set any illegal instrs to DATA(from, to)
  Undo wrong instructions(from, to) {Iterative process!}
  Patch up alternatives(from, to)   {If DATA, data!inst, DATA then DATA}
  Undo wrong instructions(from, to) {Iterative process!}
  Print decode(start, end)

%end

! Decode (Module/code start, Module/code end, decode start, decode end)
! decode(16_8000, 16_DA50, 16_8000, 16_DA50)
%string(255) s = cli param

  %if s = "MOS" %start
    decode(16_3800000, 16_380837C, 16_3800000, 16_380837C)
  %elseif s = "NFS"
    decode(16_18B2A64, 16_18C9304, 16_18B2A64, 16_18C9304)
  %elseif s = "UtilityModule"
    decode(16_3806428, 16_38195E8, 16_3806428, 16_38195E8)
  %elseif s = "FileSwitch"
    decode(16_38195E8, 16_381EC34, 16_38195E8, 16_381EC34)
  %elseif s = "BASIC"
    decode(16_381EC34, 16_382A5C4, 16_381EC34, 16_382A5C4)
  %elseif s = "ADFS"
    decode(16_382A5C4, 16_38325A4, 16_382A5C4, 16_38325A4)
  %elseif s = "Econet"
    decode(16_38325A4, 16_3834184, 16_38325A4, 16_3834184)
  %elseif s = "NetFS"
    decode(16_3834184, 16_3837F60, 16_3834184, 16_3837F60)
  %elseif s = "WindowManager"
    decode(16_3837F60, 16_383C558, 16_3837F60, 16_383C558)
  %elseif s = "SpriteUtils"
    decode(16_383C558, 16_383CE30, 16_383C558, 16_383CE30)
  %elseif s = "SoundDMA"
    decode(16_383CE30, 16_383D8A8, 16_383CE30, 16_383D8A8)
  %elseif s = "SoundChannels"
    decode(16_383D8A8, 16_383ED58, 16_383D8A8, 16_383ED58)
  %elseif s = "SoundScheduler"
    decode(16_383ED58, 16_383F588, 16_383ED58, 16_383F588)
  %else
    %if s # "" %and s#"-help" %then print string("Error: a") %else print string("A")
    print string(%c
"llowed parameters are:

  NFS
  MOS           UtilityModule FileSwitch ADFS          Econet         NetFS
  WindowManager SpriteUtils   SoundDMA   SoundChannels SoundScheduler

")
  %finish
%endofprogram
