!***********************************************************************
!*
!*                     Object file analysis program 
!*
!***********************************************************************
!
!
!***********************************************************************
!*
!*          Constants
!*
!***********************************************************************
!
CONSTANTINTEGER  NO = 0, YES = 1
CONSTANTINTEGER  SSOBJFILETYPE = 1
CONSTANTINTEGER  OUTSTREAM = 80;        ! For all output
CONSTANTBYTEINTEGERARRAY  HEX(0:15) = C 
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
CONSTANTSTRING (1) SNL = "
"
CONSTANTSTRING (8) NONE = "   None"
!
CONSTANTINTEGER  MAXPARMS = 63
CONSTANTSTRING (9)ARRAY  PARMS(0:MAXPARMS) = C 
"","I8","L8","R8",
""(4),
"MAXDICT",""(5),"MINSTACK",""(17),
 "QUOTES","NOLIST","NODIAG","STACK",
"NOCHECK","NOARRAY","NOTRACE","PROFILE",
"IMPS","INHIBIOF","ZERO","XREF",
"LABELS","LET","CODE","ATTR",
"OPT","MAP","DEBUG","FREE",
"DYNAMIC","","EBCDIC","NOLINE",
""(2),"PARMZ","PARMY",
"PARMX","MISMATCH",""(2)
!
CONSTANTINTEGER  MAXLISTNAME = 15
CONSTANTSTRING (60)ARRAY  LISTNAME(0:MAXLISTNAME) = C 
"No of listheads",
"Listhead of procedure entries",
"No of entries and refs",
"No of relocations",
"Listhead of data entries",
"Load address of code (bound files)",
"Load address gla (bound files) / listhead for misc data",
"Listhead of static procedure references",
"Listhead of dynamic procedure references",
"Listhead of data references",
"Load address of initialised stack (bound files)",
"Listhead of single word - code or data - refs",
"Offset of file history",
"Listhead of multiple initialisation requests",
"Listhead of blocks of relocation requests",
"Offset of OMF diagnostic records"
!
CONSTANTINTEGER  MAXAREANAME = 7
CONSTANTSTRING (4)ARRAY  AREANAME(1:MAXAREANAME) = C 
"CODE"," GLA"," PLT"," SST"," UST","ICMN","ISTK"
!
!
!***********************************************************************
!*
!*          Record and array formats
!*
!***********************************************************************
!
RECORDFORMAT  FDF(INTEGER  LINK,DSNUM,BYTEINTEGER  STATUS,ACCESSROUTE,C 
                  VALID ACTION,CUR STATE,BYTEINTEGER  MODE OF USE,C 
                  MODE,FILE ORG,DEV CODE,BYTEINTEGER  REC TYPE,FLAGS,C 
                  LM,RM,INTEGER  ASVAR,AREC,RECSIZE,MINREC,MAXREC,C 
                  MAXSIZE,LASTREC,CONAD,CURREC,CUR,END,TRANSFERS,C 
                  DARECNUM,CURSIZE,DATASTART,STRING (31) IDEN)
RECORDFORMAT  OFMF(INTEGER  N,CODESTART,CODELENGTH,CODEPROP,GLASTART,C 
                   GLALENGTH,GLAPROP,PLTSTART,PLTLENGTH,PLTUSE,C 
                   SSTSTART,SSTLENGTH,SSTPROP,USTSTART,USTLENGTH,C 
                   USTPROP,INITCMNSTART,INITCMNLENGTH,INITCMNPROP,C 
                   INITSTACKSTART,INITSTACKLENGTH,INITSTACKPROP)
RECORDFORMAT  OHF(INTEGER  DATAEND,DATASTART,FILESIZE,FILETYPE,SUM,C 
                  DATETIME,LDA,OFM)
RECORDFORMAT  RF(INTEGER  CONAD,FILETYPE,DATASTART,DATAEND)
!
RECORDFORMAT  LD1F(INTEGER  LINK,LOC,STRING (31) IDEN)
RECORDFORMAT  LD4F(INTEGER  LINK,DISP,L,A,STRING (31) IDEN)
RECORDFORMAT  LD78F(INTEGER  LINK,REFLOC,STRING (31) IDEN)
RECORDFORMAT  LD9F(INTEGER  LINK,REFARRAY,L,STRING (31) IDEN)
RECORDFORMAT  LD11F(INTEGER  LINK,REFLOC,STRING (31) IDEN)
RECORDFORMAT  LD13F(INTEGER  LINK,A,DISP,LEN,REP,ADDR)
RECORDFORMAT  LD14F(INTEGER  LINK,N,TABLESTART)
!
OWNINTEGERARRAYFORMAT  LDATAAF(0:15)
!
!
!***********************************************************************
!*
!*          Subsystem references
!*
!***********************************************************************
!
SYSTEMROUTINESPEC  CONNECT(STRING (31) FILE,INTEGER  MODE,HOLE,C 
                           PROT,RECORD (RF)NAME  R,INTEGERNAME  FLAG)
SYSTEMROUTINESPEC  DEFINE(INTEGER  CHAN,STRING (31) IDEN,C 
                          INTEGERNAME  AFD,FLAG)
SYSTEMSTRINGFUNCTIONSPEC  FAILUREMESSAGE(INTEGER  MESS)
SYSTEMINTEGERFUNCTIONSPEC  IOCP(INTEGER  EP,PARM)
SYSTEMINTEGERMAPSPEC  MAPSSFD(INTEGER  DSNUM)
SYSTEMROUTINESPEC  MOVE(INTEGER  LENGTH,FROM,TO)
EXTERNALINTEGERFUNCTIONSPEC  OUTPOS
SYSTEMINTEGERFUNCTIONSPEC  PARMAP
SYSTEMROUTINESPEC  PHEX(INTEGER  N)
SYSTEMROUTINESPEC  SETFNAME(STRING (63) S)
SYSTEMROUTINESPEC  SETPAR(STRING (255) S)
EXTERNALROUTINESPEC  SET RETURN CODE(INTEGER  I)
SYSTEMSTRINGFUNCTIONSPEC  SPAR(INTEGER  N)
EXTERNALINTEGERFUNCTIONSPEC  UINFI(INTEGER  ENTRY)
SYSTEMSTRING (8)FUNCTIONSPEC  UNPACKDATE(INTEGER  P)
SYSTEMSTRING (8)FUNCTIONSPEC  UNPACKTIME(INTEGER  P)
!
!
!***********************************************************************
!*
!*          Service routines
!*
!***********************************************************************
!
ROUTINE  BOX(STRING (31) S)
! Prints a string enclosed in a box.
STRING (35) BAR
!
BAR = "***********************************"
LENGTH(BAR) = LENGTH(S) + 4
PRINTSTRING(BAR.SNL)
PRINTSTRING("* ".S." *".SNL)
PRINTSTRING(BAR)
NEWLINES(2)
END ;   ! of BOX
!
!
ROUTINE  PRINTPARMS(LONGINTEGER  P)
! Decodes and prints PARM settings.
INTEGER  I,FOUND
!
FOUND = NO
FOR  I = 0,1,MAXPARMS CYCLE 
   IF  P & 1 # 0 THEN  START 
      IF  PARMS(I) # "" THEN  START ;   ! Ignore blank parms
         IF  FOUND = NO THEN  START 
            FOUND = YES
         FINISH  ELSE  PRINTSYMBOL(',')
         PRINTSTRING(PARMS(I))
      FINISH 
   FINISH 
   P = P >> 1
REPEAT 
IF  FOUND = NO THEN  PRINTSTRING("DEFAULTS")
END ;   ! of PRINTPARMS
!
!
ROUTINE  PRINT8PLUS24(INTEGER  N)
! Prints  an  integer  in  the  form 'BASE+DISPLACEMENT', as used by the
! loader.  
INTEGER  I,AREA
!
AREA = N >> 24
IF  0 < AREA <= MAXAREANAME THEN  START 
   PRINTSTRING(AREANAME(AREA))
FINISH  ELSE  START 
   PRINTSYMBOL('@')
   WRITE(AREA,2)
FINISH 
PRINTSYMBOL('+')
FOR  I = 20,-4,0 CYCLE 
   PRINTSYMBOL(HEX((N>>I) & X'F'))
REPEAT 
END ;   ! of PRINT8PLUS24
!
!
ROUTINE  TAB(INTEGER  N)
! Prints spaces up to a specified column.
SPACE WHILE  OUTPOS < N
END ;   ! of TAB
!
!
ROUTINE  OUTI(STRING (255) S,INTEGER  POS,I)
! Prints a string and a decimal integer, aligned to specified columns.
PRINTSTRING(S.":")
SPACE WHILE  OUTPOS < POS - 6
WRITE(I,6)
NEWLINE
END ;   ! of OUTI
!
!
ROUTINE  OUTHI(STRING (255) S,INTEGER  POS,I)
! Prints  a  string  and  a  hexadecimal  integer,  aligned to specified
! columns.  
PRINTSTRING(S.":")
SPACE WHILE  OUTPOS < POS
PHEX(I)
NEWLINE
END ;   ! of OUTHI
!
!
ROUTINE  CLOSESTREAM(INTEGER  CHAN)
! Special  version  of CLOSESTREAM - will work on stream numbers outwith
! the normal range, and will not fail if the stream is not defined.  
INTEGER  FLAG
!
FLAG = IOCP(16,CHAN);                   ! Ignore flag
END ;   ! of CLOSESTREAM
!
!
ROUTINE  CLEARSTREAM(INTEGER  CHAN)
! Clears out a channel definition. Does not give an error if the  stream
! was not defined.  
RECORD (FDF)NAME  F
!
IF  MAPSSFD(CHAN) # 0 THEN  START 
   F == RECORD(MAPSSFD(CHAN))
   IF  F_STATUS = 0 THEN  START 
      F_DSNUM = 0;                      ! Mark descriptor as free
      MAPSSFD(CHAN) = 0;                ! Clear pointer
   FINISH 
FINISH 
END ;   ! of CLEARSTREAM
!
!
INTEGERFUNCTION  LISTCOUNT(INTEGER  LINK,CONAD)
! Counts the number of entries attached to a specified list head.
INTEGER  RES
!
RES = 0
WHILE  LINK # 0 CYCLE 
   RES = RES + 1
   LINK = INTEGER(CONAD+LINK)
REPEAT 
RESULT  = RES
END ;   ! of LISTCOUNT
!
!
!***********************************************************************
!*
!*          O B J A N A L
!*
!***********************************************************************
!
EXTERNALROUTINE  OBJANAL(STRING (255) S)
INTEGERARRAYNAME  LDATA
STRING (31) FILE,OUT
INTEGER  CONAD,LINK,REFARRAY,PAGEWIDTH,AOFM,RRCOUNT,RELAD,NS,TYPE,RECAD
INTEGER  I,J,FLAG,AFD
LONGINTEGER  LONGTYPE
SWITCH  HIST(1:8)
RECORD (RF) RR
RECORD (FDF)NAME  F
RECORD (LD1F)NAME  L1
RECORD (LD4F)NAME  L4
RECORD (LD78F)NAME  L7
RECORD (LD9F)NAME  L9
RECORD (LD11F)NAME  L11
RECORD (LD13F)NAME  L13
RECORD (LD14F)NAME  L14
RECORD (OFMF)NAME  OFM
RECORD (OHF)NAME  H
!
SETPAR(S)
IF  1 # PARMAP # 3 THEN  START 
   FLAG = 263;                          ! Wrong number of parameters
   -> ERR
FINISH 
FILE = SPAR(1)
OUT = SPAR(2)
OUT = ".OUT" IF  OUT = ""
IF  OUT = ".OUT" THEN  START 
   PAGEWIDTH = UINFI(15);               ! Interactive terminal width
   PAGEWIDTH = 72 IF  PAGEWIDTH = 72;   ! Keep to reasonable value
FINISH  ELSE  PAGEWIDTH = 132
DEFINE(OUTSTREAM,OUT,AFD,FLAG)
-> ERR IF  FLAG # 0
F == RECORD(AFD)
F_MAXSIZE = 1024 << 10
SELECTOUTPUT(OUTSTREAM)
!
CONNECT(FILE,1,0,0,RR,FLAG)
-> ERR IF  FLAG # 0
IF  RR_FILETYPE # SSOBJFILETYPE THEN  START 
   SETFNAME(FILE)
   FLAG = 267;                          ! Invalid filetype
   -> ERR
FINISH 
CONAD = RR_CONAD
H == RECORD(CONAD)
LDATA == ARRAY(CONAD+H_LDA,LDATAAF)
AOFM = CONAD + H_OFM
OFM == RECORD(AOFM)
!
PRINTSTRING("File: ".FILE.SNL)
OUTHI("Total size (including header)",35,RR_DATAEND)
OUTHI("Offset of Load Data",35,H_LDA)
OUTHI("Offset of Object File Map",35,H_OFM)
NEWLINE
!
BOX("Load Data")
FOR  I = 0,1,LDATA(0) CYCLE 
   WRITE(I,2)
   SPACES(3)
   PHEX(LDATA(I))
   IF  I <= MAXLISTNAME THEN  START 
      SPACES(3)
      PRINTSTRING(LISTNAME(I))
   FINISH 
   NEWLINE
REPEAT 
NEWLINE
!
BOX("Object File Map")
PRINTSTRING("       Offset      Length      Attributes".SNL.SNL)
FOR  I = 1,1,OFM_N CYCLE 
   J = AOFM + 4 + 12*(I - 1)
   WRITE(I,2)
   SPACES(3)
   PHEX(INTEGER(J))
   SPACES(5)
   PHEX(INTEGER(J+4))
   SPACES(5)
   PHEX(INTEGER(J+8))
   IF  I <= MAXAREANAME THEN  START 
      SPACES(3)
      PRINTSTRING(AREANAME(I))
   FINISH 
   NEWLINE
REPEAT 
NEWLINES(2)
OUTI("Number of procedure entries",50,LISTCOUNT(LDATA(1),CONAD))
OUTI("Number of data entries",50,LISTCOUNT(LDATA(4),CONAD))
OUTI("Number of static procedure references",50,C 
                                             LISTCOUNT(LDATA(7),CONAD))
OUTI("Number of dynamic procedure references",50,C 
                                             LISTCOUNT(LDATA(8),CONAD))
OUTI("Number of data references",50,LISTCOUNT(LDATA(9),CONAD))
OUTI("Number of single word references",50,LISTCOUNT(LDATA(11),CONAD))
OUTI("Number of multiple initialisation blocks",50,C 
                                            LISTCOUNT(LDATA(13),CONAD))
OUTI("Number of relocation request blocks",50,C 
                                            LISTCOUNT(LDATA(14),CONAD))
RRCOUNT = 0
LINK = LDATA(14)
WHILE  LINK # 0 CYCLE 
   L14 == RECORD(CONAD+LINK)
   RRCOUNT = RRCOUNT + L14_N
   LINK = L14_LINK
REPEAT 
OUTI("Total number of relocation requests",50,RRCOUNT)
NEWLINES(3)
!
BOX("Procedure Entries")
IF  LDATA(1) # 0 THEN  START 
   PRINTSTRING(C 
      "Entry Name                   Location of Entry Descriptor")
   NEWLINES(2)
   LINK = LDATA(1)
   WHILE  LINK # 0 CYCLE 
      L1 == RECORD(CONAD+LINK)
      PRINTSTRING(L1_IDEN)
      IF  L1_LOC & X'80000000' # 0 THEN  PRINTSTRING("(Main Entry)")
      TAB(35)
      PRINT8PLUS24(L1_LOC & X'7FFFFFFF')
      NEWLINE
      LINK = L1_LINK
   REPEAT 
FINISH  ELSE  PRINTSTRING(NONE)
NEWLINES(3)
!
BOX("Data Entries")
IF  LDATA(4) # 0 THEN  START 
   PRINTSTRING(C 
      "Entry Name                        Area     Offset   Length")
   NEWLINES(2)
   LINK = LDATA(4)
   WHILE  LINK # 0 CYCLE 
      L4 == RECORD(LINK+CONAD)
      PRINTSTRING(L4_IDEN)
      TAB(35)
      PRINT8PLUS24((L4_A<<24)!L4_DISP)
      WRITE(L4_L,6)
      NEWLINE
      LINK = L4_LINK
   REPEAT 
FINISH  ELSE  PRINTSTRING(NONE)
NEWLINES(3)
!
BOX("Static procedure references")
IF  LDATA(7) # 0 THEN  START 
   PRINTSTRING(C 
      "Name                              Location of Descriptor")
   NEWLINES(2)
   LINK = LDATA(7)
   WHILE  LINK # 0 CYCLE 
      L7 == RECORD(LINK+CONAD)
      PRINTSTRING(L7_IDEN)
      TAB(35)
      PRINT8PLUS24(L7_REFLOC)
      NEWLINE
      LINK = L7_LINK
   REPEAT 
FINISH  ELSE  PRINTSTRING(NONE)
NEWLINES(3)
!
BOX("Dynamic procedure references")
IF  LDATA(8) # 0 THEN  START 
   PRINTSTRING(C 
      "Name                              Location of Descriptor")
   NEWLINES(2)
   LINK = LDATA(8)
   WHILE  LINK # 0 CYCLE 
      L7 == RECORD(LINK+CONAD)
      PRINTSTRING(L7_IDEN)
      TAB(35)
      PRINT8PLUS24(L7_REFLOC)
      NEWLINE
      LINK = L7_LINK
   REPEAT 
FINISH  ELSE  PRINTSTRING(NONE)
NEWLINES(3)
!
BOX("Data references")
IF  LDATA(9) # 0 THEN  START 
   PRINTSTRING(C 
      "Name                               Length    Pointers")
   NEWLINES(2)
   LINK = LDATA(9)
   WHILE  LINK # 0 CYCLE 
      L9 == RECORD(CONAD+LINK)
      PRINTSTRING(L9_IDEN)
      IF  L9_REFARRAY & X'80000000' # 0 THEN  START 
         PRINTSTRING(" (Common Block)")
      FINISH 
      TAB(35)
      WRITE(L9_L,6)
      TAB(45)
      REFARRAY = CONAD + L9_REFARRAY & X'7FFFFFFF'
      FOR  I = REFARRAY + 4,4,REFARRAY + 4*INTEGER(REFARRAY) CYCLE 
         IF  OUTPOS > PAGEWIDTH - 11 THEN  START 
            NEWLINE
            TAB(45)
         FINISH 
         PRINT8PLUS24(INTEGER(I))
         SPACES(2)
      REPEAT 
      NEWLINE
      LINK = L9_LINK
   REPEAT 
FINISH  ELSE  PRINTSTRING(NONE)
NEWLINES(3)
!
BOX("Single word references")
IF  LDATA(11) # 0 THEN  START 
   PRINTSTRING(C 
      "Entry                               Location")
   NEWLINES(2)
   LINK = LDATA(11)
   WHILE  LINK # 0 CYCLE 
      L11 == RECORD(LINK+CONAD)
      PRINTSTRING(L11_IDEN)
      TAB(35)
      PRINT8PLUS24(L11_REFLOC)
      NEWLINE
      LINK = L11_LINK
   REPEAT 
FINISH  ELSE  PRINTSTRING(NONE)
NEWLINES(3)
!
BOX("Multiple initialisation blocks")
IF  LDATA(13) # 0 THEN  START 
   PRINTSTRING(C 
      "Fill area           with byte         from address      Repeat")
   NEWLINES(2)
   LINK = LDATA(13)
   WHILE  LINK # 0 CYCLE 
      L13 == RECORD(LINK+CONAD)
      PRINT8PLUS24((L13_A << 24)!L13_DISP)
      IF  L13_LEN = 1 THEN  START 
         TAB(15)
         PRINTSYMBOL(HEX((L13_ADDR >> 4) & X'F'))
         PRINTSYMBOL(HEX(L13_ADDR & X'F'))
      FINISH  ELSE  START 
         TAB(23)
         PHEX(L13_ADDR)
      FINISH 
      TAB(35)
      PHEX(L13_REP)
      NEWLINE
      LINK = L13_LINK
   REPEAT 
FINISH  ELSE  PRINTSTRING(NONE)
NEWLINES(3)
!
BOX("Relocation Request Blocks")
IF   LDATA(14) # 0 THEN  START 
   PRINTSTRING(C 
  "Relocate INTEGER(AREASTART(1)+OFFSET(1)) by AREASTART(2)+OFFSET(2)")
   NEWLINES(2)
   LINK = LDATA(14)
   WHILE  LINK # 0 CYCLE 
      L14 == RECORD(CONAD+LINK)
      RELAD = ADDR(L14_TABLESTART)
      FOR  I = 1,1,L14_N CYCLE 
         IF  OUTPOS + 21 > PAGEWIDTH THEN  NEWLINE
         PRINT8PLUS24(INTEGER(RELAD));  ! AREALOC
         SPACES(2)
         PRINT8PLUS24(INTEGER(RELAD+4));!BASELOC
         SPACES(6)
         RELAD = RELAD + 8
      REPEAT 
      LINK = L14_LINK
      NEWLINES(2)
   REPEAT 
FINISH  ELSE  PRINTSTRING(NONE)
NEWLINES(3)
!
BOX("File History")
IF  LDATA(12) # 0 THEN  START 
   RECAD = CONAD + LDATA(12)
   NS = 0
   WHILE  BYTEINTEGER(RECAD) # 0 CYCLE 
      TYPE = BYTEINTEGER(RECAD)
      EXIT  UNLESS  1 <= TYPE <= 8
      SPACES(NS) UNLESS  TYPE = 7
      !
      -> HIST(BYTEINTEGER(RECAD))
      !
   HIST(1):                             ! Name of source file
      PRINTSTRING("Source      : ".STRING(RECAD+1))
      -> NEXT
      !
   HIST(2):                             ! PARM setting
      PRINTSTRING("Parms set   : ")
      MOVE(8,RECAD+2,ADDR(LONGTYPE))
      PRINTPARMS(LONGTYPE)
      -> NEXT
      !
   HIST(3):                             ! Start of linked object
      PRINTSTRING("Components  : ")
      NS = NS + 3;                      ! For indentation
      -> NEXT
      !
   HIST(4):                             ! Name of object file
      NEWLINE
      SPACES(NS)
      PRINTSTRING("Object      : ".STRING(RECAD+1))
      -> NEXT
      !
   HIST(5):                             ! Date linked
      MOVE(4,RECAD+2,ADDR(I))
      PRINTSTRING("Linked      : ".UNPACKDATE(I)." at ".UNPACKTIME(I))
      -> NEXT
      !
   HIST(6):                             ! Date compiled
      MOVE(4,RECAD+2,ADDR(I))
      PRINTSTRING("Last altered: ".UNPACKDATE(I)." at ".UNPACKTIME(I))
      -> NEXT
      !
   HIST(7):                             ! End of linked object
      NS = NS - 3
      SPACES(NS)
      PRINTSTRING("END")
      -> NEXT
      !
   HIST(8):                             ! Any text
      I = RECAD + 2;                    ! Start of text
      J = RECAD + 1 + BYTEINTEGER(RECAD + 1)
                                        ! End of text
      WHILE  I <= J CYCLE 
         PRINTSYMBOL(BYTEINTEGER(I))
         IF  BYTEINTEGER(I) = NL THEN  SPACES(NS)
         I = I + 1
      REPEAT 
      -> NEXT
      !
   NEXT:
      NEWLINE
      RECAD = RECAD + 2 + BYTEINTEGER(RECAD+1)
                                        ! Point to next item
   REPEAT 
FINISH  ELSE  PRINTSTRING(NONE)
!
SELECTOUTPUT(0)
CLOSESTREAM(OUTSTREAM)
CLEARSTREAM(OUTSTREAM)
SET RETURN CODE(0)
STOP 
!
ERR:
SELECTOUTPUT(0)
CLOSESTREAM(OUTSTREAM)
CLEARSTREAM(OUTSTREAM)
PRINTSTRING(SNL."OBJANAL fails -".FAILUREMESSAGE(FLAG))
SET RETURN CODE(FLAG)
STOP 
END ;   ! of OBJANAL
! This version deals with LIST11 type references
! Not dealt with currently are LIST13
! (multiple initialisation) and LIST15 (OMF diagnostic records).
!      C. McC.   17/2/81
ENDOFFILE