! FE02 Object module linker
! RWT February 1985 revised June 1985

%include "inc:util.imp"

%begin

!Parameters (via PAM)

%string(21)mainfile="",               {input object file}
           outfile="",                {output object file}
           include="",                {file of exports to be left in}
           exclude="",                {file of exports to be hidden}
           disregard="utils:link.dat" {file of files not to be autoloaded}
%constinteger auxmax=19
%string(21)%array auxfile(1:auxmax)   {additional object input files}
%constinteger debugbit=16_80000000,   {monitor linker operation}
              dynbit  =16_40000000,   {?}
              autobit =16_20000000    {automatically choose input files}
%integer control=0

%routine acquire parameters
%integer i
  auxfile(i) = "" %for i = 1,1,auxmax
  define param("Mainfile",mainfile,pammajor)
  define param("Auxfile".itos(i,0),auxfile(i),0) %for i = 1,1,auxmax
  define param("Outfile",outfile,pamnewgroup)
  define param("Include",include,pamnewgroup)
  define param("Exclude",exclude,0)
  define param("Disregard",disregard,0)
  define boolean params("DEbug,DYnamic,Auto",control,pamnewgroup)
  process parameters(cliparam)
  %stopif mainfile=""
  outfile = mainfile %if outfile=""
%end

! Object module formats

%recordformat obj header f(%byte type,version,%half flags,export,import,
  %integer code,%half reset,main,%integer own,stack,spare1,spare2)

%recordformat obj entry f(%half flags,ptype,%integer type,disp,
                          %string(255)name)

! _FLAGS values

%constinteger present=16_8000,extbit=16_4000,procmask=16_3000,
              system=16_1000,external=16_2000,dynamic=16_3000

! Input Reset Routines must look like this or like the output one
! 1 = place holder for number of data words -1

%constinteger irmax=8
%constshortarray ir(0:irmax)=%c
16_224C,         {     move.l a4,a1
16_41FA,16_000E, {     lea glap,a0
16_303C,      1, {     move.w #1,d0
16_32D8,         {loop move.w (a0)+,(a1)+
16_51C8,     -4, {     dbra d0,loop
16_4E75          {     rts
                 {glap equ *
                 {     end

! Output reset routine will look like this
! 1-4 place holders for data and code sizes

%constinteger ormax=34
%consthalfarray or(0:ormax)=%C
16_45FA,16_0044,         {  lea end,a2          code base
16_41FA,16_0040,         {  lea end,a0
16_D1FC,16_0004,16_0003, {  add.l #$40003,a0    glap base
16_224C,                 {  move.l a4,a1
16_220A,                 {  move.l a2,d1        code base for later
16_2409,                 {  move.l a1,d2        gla base for later
16_203C,16_0002,16_0001, {  move.l #$20001,d0   glap word count
16_6712,                 {  beq.s a
16_4840,                 {  swap d0
16_4840,                 {m swap d0
16_32D8,                 {l move.w (a0)+,(a1)+  copy glap to gla
16_51C8,16_FFFC,         {  dbra d0,l
16_4840,                 {  swap d0
16_51C8,16_FFF4,         {  dbra d0,m
16_224C,                 {  move.l a4,a1        perform relocation
16_3018,                 {a move.w (a0)+,d0
16_6712,                 {  beq.s c             no more
16_0880,16_0000,         {  bclr #0,d0
16_6706,                 {  beq.s b
16_D2C0,                 {  add.w d0,a1
16_D591,                 {  add.l d2,(a1)       data
16_60F0,                 {  bra a
16_D2C0,                 {b add.w d0,a1
16_D391,                 {  add.l d1,(a1)       code
16_60EA,                 {  bra a
16_4E75                  {c rts
                         {end equ *

%constinteger dismax=35
%string(21)%array dis(1:dismax)
%integer dispos=0

%constinteger relocmax=800
%integer relocp=0
%integerarray reloc(1:relocmax)

%recordformat mdf(%record(mdf)%name next,%record(obj header f)%name header,
                  %integer ca,da, {code, data address as connected now}
                           co,do) {code, data offset wrt combined area}

%record(mdf)%name headers==nil
%record(obj header f) output header = 0
%integer codesize=0, datasize=0, exportcount=0, importcount=0

%constinteger ok=1, hopeless=0
%integer situation = ok

%integerfn entrysize(%record(obj entry f)%name e)
%integer n
  n = sizeof(e)-255+length(e_name)
  %result = (n+1)>>1<<1
%end

%routine suck in(%string(*)%name input file)

! Read the module in file INPUTFILE, making sure it is of
! the form expected.  Make an entry for it in the global
! HEADERS list.  Adjust its export displacements.

%owninteger f=0
%integer start,size
%record(mdf)%name md,m
%record(obj header f)%name header
%record(obj entry f)%name entry
%integer export,import,code,p,i
%conststring(31)%array reason(1:6)=%c
  "not an FE02 object module",
  "non-null reset routine",
  "data size does not tally",
  "incompatible reset routine",
  "does not contain exports",
  "relocation table full"

  %on 3,9,15 %start
    %if event_event=15 %start
      printstring(" unsuitable for linking - ")
      printstring(reason(event_sub))
    %else
      printstring(event_message)
      %if event_message="" %start
        printstring("Trouble with ".input file)
        %if event_event=9 %start
          printstring(": End of file reached")
        %else
          printstring(": Event 3 ")
          write(event_sub,0); write(event_extra,1)
        %finish
      %finish
    %finish
    newline
    situation = hopeless
    %return
  %finish

  %returnif input file=""
  toupper(input file)
  input file = input file.".MOB" %unless input file -> (".MOB")
  write(f,0) %and space %and f = f+1 %unless control&debugbit=0
  printstring("Reading ".inputfile)
  connectfile(input file,0,start,size)

  header == record(start)
  md == new(md)
  md_header == header
  %signal 15,1 %unless header_type=16_fe %and header_version=2
  export = start+sizeof(header)
  import = export+header_export
  code = import+header_import
  p = header_reset<<1+code
  %if header_own=0 %start
    header_code = header_code-header_own
    md_da = 0
    %signal 15,2 %unless shortinteger(p)=ir(irmax)
    %signal 15,4 %unless header_reset = 0
  %elseif shortinteger(p)=ir(0)
    header_code = p-code
    %for i = 0,1,irmax %cycle
      %if ir(i)=1 %start
        %signal 15,3 %unless (shortinteger(p)&65535)<<1+2=header_own
      %else
        %signal 15,4 %unless ir(i)=shortinteger(p)
      %finish
      p = p+2
    %repeat
    md_da = p
  %elseif shortinteger(p)=or(0)
    header_code = p-code
    %for i = 0,1,ormax %cycle
      %if or(i)=1 %start
        %signal 15,3 %unless (header_own>>1-1-shortinteger(p))&65535=0
      %elseif or(i)=2
        %signal 15,3 %unless (header_own>>1-1)>>16-shortinteger(p)=0
      %elseif 3#or(i)#4
        %signal 15,4 %unless or(i)=shortinteger(p)
      %finish
      p = p+2
    %repeat
    md_da = p
  %finishelsesignal 15,4
  md_ca = code
  md_do = datasize
  md_co = codesize
  md_next == nil
  %if headers==nil %start          {first module: start list
    headers == md
    output header = header
    output header_import = 0
    output header_export = 0
  %else                            {tag on to end of list
    m == headers
    m == m_next %while m_next##nil
    m_next == md
    %signal 15,5 %if header_export=0
  %finish
  newline
  %unless header_export=0 %start
    p = export
    %cycle
      entry == record(p); %exitif entry_flags&present=0
      toupper(entry_name)
      %if entry_flags&procmask=0 %start {data object
        entry_disp = entry_disp+datasize
      %else                             {code object
        entry_disp = entry_disp+codesize
      %finish
      output header_export = output header_export+entrysize(entry)
      exportcount = exportcount+1
      p = p+entrysize(entry)
      %unless control&debugbit=0 %start
        phex(entry_disp); space; printstring(entry_name); newline
      %finish
    %repeat
  %finish
  %unless control&debugbit=0 %start
    phex(codesize); space; phex(datasize); space
    phex(header_code); space; phex(header_own); newline
  %finish
  codesize = codesize+header_code
  datasize = datasize+header_own
%end

%constinteger zapped = present

%routine satisfy everything

! For each module which has been read in, see if each of its
! imports can be satisfied internally.  If so, do it, otherwise
! note that it is still required in the output module.

%record(mdf)%name md
%record(obj header f)%name h
%record(obj entry f)%name e
%integer f=0,t,p,i,j


  %routine locate(%record(objentryf)%name want,%integername address,gla)

  ! Try to satisfy entry WANT internally.  If successful, set ADDRESS
  ! to the offset of the desired object relative to the start of its
  ! area (code/data), and set GLA to the start of the submodule's GLA.
  ! If unsuccessful, set both to 0.

  %predicate hauled in(%string(*)%name entry)
! Look in the external dictionary for an entry corresponding to
! the ENTRY_NAME.  If found, and the corresponding file can be
! got at, return TRUE.
  %option "-low"
  %recordformat dictf(%integer beg,pos,lim,alt)
  @736(a5)%record(dictf)%name extdict
  @16_1180 %integerfn findname(%string(255)s,%record(dictf)%name d)
  @16_1184 %routine transname(%integer t,%string(255)%name s)
  %integer i,time
  %string(255)file
    i = findname(entry,extdict)
    %if i<=0 %start
      printstring(entry." not found"); newline; %false
    %finish
    transname(integer(i),file)
    toupper(file)
    %for i = 1,1,dispos %cycle
      %falseif dis(i)=file
    %repeat
    time = cputime
    suck in(file)
    time = cputime-time
    t = t+time
    %true
  %end

  %record(objentryf)%name got
  %record(mdf)%name md
  %record(objheaderf)%name h
  %integer pass=1,w,g,p
    %cycle
      md == headers
      %cycle
        h == md_header
        %if h_export#0 %start
          p = addr(h)+sizeof(h)
          %cycle
            got == record(p); %exitif got_flags&present=0
            %if got_name=want_name %start     {name match
              %if got_type=0 %or want_type=0 %c
              %or got_type=want_type %start   {type match
                g = got_flags&procmask; w = want_flags&procmask
                w = external %if w=dynamic
                g = system %if g = external %and h_own=0
                %if g=w %or (g=system %and w=external) %start  {flag match
                  %if got_flags&extbit#0 %start
                    got_flags = got_flags-extbit  {suppress export
                    output header_export = output header_export-entrysize(got)
                    exportcount = exportcount-1
                  %finish
                  i = got_disp; j = md_do; %return
                %finish
              %finish
            %finish
            p = p+entrysize(got)
          %repeat
        %finish
        md == md_next
      %repeatuntil md==nil
      pass = 2 %if control&autobit=0
      %exitif pass=2
      %exitunless hauled in(want_name)
      pass = 2
    %repeat
    address = 0; gla = 0
  %end

  %routine relocate(%integer x)
  ! Add entry X to the relocation table
    %signal 15,6 %if relocp=relocmax
    relocp = relocp+1; reloc(relocp) = x
  %end

  printstring("Linking"); newline
  t = cputime
  situation = hopeless %andreturnif headers==nil

! Process each module's import list

  md == headers
  %cycle
    h == md_header
    %unless control&debugbit=0 %start
      printstring("File"); write(f,2); newline; f = f+1
    %finish
    %unless h_import=0 %start
      p = addr(h)+sizeof(h)+h_export
      %cycle
        e == record(p); %exitif e_flags&present=0
        toupper(e_name)
        locate(e,i,j)
        %if i=0 %start      {leave as import requirement
          output header_import = output header_import+entrysize(e)
          importcount = importcount+1
          e_disp = e_disp+md_do
          %unless control&debugbit=0 %start
            printstring(" Import: "); phex(e_disp)
          %finish
        %else               {satisfy internally
          e_flags = e_flags&procmask
          %if e_flags=0 %start         {data object
            relocate(md_do+e_disp+1)
            integer(md_da+e_disp) = i
            %unless control&debugbit=0 %start
              spaces(9); phex(i)
            %finish
          %elseif e_flags=system
            shortinteger(md_da+e_disp) = 16_4ef9
            relocate(md_do+e_disp+2)
            integer(md_da+e_disp+2) = i
            %unless control&debugbit=0 %start
              phex(i); spaces(9)
            %finish
          %else
            shortinteger(md_da+e_disp) = 16_287c
            relocate(md_do+e_disp+3)
            integer(md_da+e_disp+2) = j
            shortinteger(md_da+e_disp+6) = 16_4ef9
            relocate(md_do+e_disp+8)
            integer(md_da+e_disp+8) = i
            %unless control&debugbit=0 %start
              phex(i); space; phex(j)
            %finish
          %finish
          e_flags = zapped
        %finish
        %unless control&debugbit=0 %start
          space; printstring(e_name); newline
        %finish
        p = p+entrysize(e)
      %repeat
    %finish
    md == md_next
  %repeatuntil md==nil
  t = cputime-t
  printstring("Completed in "); write(t,0); printstring("ms")
  newline
  output header_code = codesize+datasize+ormax<<1+2+relocp<<1+2
  output header_own = datasize
  output header_reset = 0
  %if datasize=0 %start
    %signal 15 %unless shortinteger(headers_ca)=16_4e75 %and relocp=0
    output header_code = output header_code-ormax<<1-4
  %else
    output header_main = output header_main+ormax+1
  %finish
%end

%routine deal with(%string(21)x,%integer inc)
%record(mdf)%name md
%record(obj header f)%name h
%record(obj entry f)%name e
%integer p
  %on 3,9 %start
    printstring(event_message) %and newline %unless event_event=9
    %return
  %finish
  %returnif x=""
  %unless x="*" %start
    %if inc=0 %then prompt("Export:") %else prompt("Import:")
    %if x=":" %then selectinput(0) %elsestart
      openinput(1,x); selectinput(1)
    %finish
    read(x)
  %finish
  %cycle
    md == headers
    %cycle
      h == md_header
      %unless h_export=0 %start
        p = addr(h)+sizeof(h)
        %cycle
          e == record(p); %exitif e_flags&present=0
          %if x="*" %or x=e_name %start
            %if inc#0 %and e_flags&extbit=0 %start
              e_flags = e_flags+extbit
              output header_export = output header_export+entrysize(e)
              exportcount = exportcount+1
            %elseif inc=0 %and e_flags&extbit#0
              e_flags = e_flags-extbit
              output header_export = output header_export-entrysize(e)
              exportcount = exportcount-1
            %finish
          %finish
          p = p+entrysize(e)
        %repeat
      %finish
      md == md_next
    %repeatuntil md==nil
    %exitif x="*"
    read(x)
  %repeatuntil x=":"
%end

%routine spit out(%string(*)%name outfile)
%record(mdf)%name md
%record(objheaderf)%name h
%record(objentryf)%name e
%integer i,j,p

  %routine order(%integer p,q)
  ! Swop reloc(p) and reloc(q) if reloc(p)>reloc(q)
  %integer t
    t = reloc(p)
    %if t>reloc(q) %start
      reloc(p) = reloc(q); reloc(q) = t
    %finish
  %end

  toupper(outfile)
  outfile = outfile.".MOB" %unless outfile -> (".MOB")
  printstring("Writing ".outfile); newline
  openoutput(1,outfile); selectoutput(1)

! Output header

  output header_reset = 0 %unless datasize=0
  output header_export = output header_export+2 %unless output header_export=0
  output header_import = output header_import+2 %unless output header_import=0
  %for i = 0,1,sizeof(output header)-1 %cycle
    printsymbol(byteinteger(addr(output header)+i))
  %repeat

! Output exports

  %unless output header_export=0 %start
    md == headers
    %cycle
      h == md_header
      %unless h_export=0 %start
        p = addr(h)+sizeof(h)
        %cycle
          e == record(p); %exitif e_flags&present=0
          e_disp = e_disp+ormax<<1+2 %unless datasize=0
          %if e_flags&extbit#0 %start
            %for i = 0,1,entrysize(e)-1 %cycle
              printsymbol(byteinteger(addr(e)+i))
            %repeat
          %finish
          p = p+entrysize(e)
        %repeat
      %finish
      md == md_next
    %repeatuntil md==nil
    printsymbol(0); printsymbol(0)
  %finish

! Output import requirements

  %unless output header_import=0 %start
    md == headers
    %cycle
      h == md_header
      %unless h_import=0 %start
        p = addr(h)+sizeof(h)+h_export
        %cycle
          e == record(p); %exitif e_flags&present=0
          %unless e_flags=zapped %start
            %if e_flags&procmask=dynamic %and control&dynbit=0 %start
              e_flags = e_flags!!dynamic!!external
            %finish
            %for i = 0,1,entrysize(e)-1 %cycle
              printsymbol(byteinteger(addr(e)+i))
            %repeat
          %finish
          p = p+entrysize(e)
        %repeat
      %finish
      md == md_next
    %repeatuntil md==nil
    printsymbol(0); printsymbol(0)
  %finish

! Output reset routine

  %unless datasize=0 %start
    %for i = 0,1,ormax %cycle
      j = or(i)
      %if j=1 %start
        j = data size>>1-1
      %elseif j=2
        j = (data size>>1-1)>>16
      %elseif j=3
        j = code size
      %elseif j=4
        j = code size>>16
      %finish
      printsymbol(j>>8); printsymbol(j)
    %repeat
  %finish

! Output code

  md == headers
  %cycle
    j = md_ca; i = md_header_code
    %while i>0 %cycle
      i = i-1; printsymbol(byteinteger(j)); j = j+1
    %repeat
    md == md_next
  %repeatuntil md==nil

! Output data

  md == headers
  %cycle
    j = md_da; i = md_header_own
    %while i>0 %cycle
      i = i-1; printsymbol(byteinteger(j)); j = j+1
    %repeat
    md == md_next
  %repeatuntil md==nil

! Sort and output relocation data

  %if relocp>1 %start
    %for j = relocp,-1,2 %cycle
      %for i = 1,1,j-1 %cycle
        order(i,j)
      %repeat
    %repeat
  %finish
  p = 0
  %for i = 1,1,relocp %cycle
    j = reloc(i)-p; p = reloc(i)>>1<<1
    printsymbol(j>>8); printsymbol(j)
  %repeat
  printsymbol(0); printsymbol(0)
%end

%routine read disfile(%string(21)f)
%string(21)%name s
  %onevent 3,9 %start
    selectinput(0)
    %unless event_event=9 %start
      printstring(event_message); newline
    %finish
    %return
  %finish
  openinput(1,f); selectinput(1)
  %cycle
    s == dis(dispos+1); read(s)
    toupper(s); dispos = dispos+1
  %repeatuntil dispos>dismax
  %signal 3,,,"Too many DISREGARD-files"
%end

!  Main program

%integer i

acquire parameters
read disfile(disregard) %unless control&autobit=0

suck in (main file);       %stopif situation=hopeless
%for i = 1,1,auxmax %cycle
  suck in (aux file(i));   %stopif situation=hopeless
%repeat

satisfy everything;        %stopif situation=hopeless

deal with(include,1) %if include="*"
deal with(exclude,0)
deal with(include,1) %unless include="*"

spit out(outfile)

selectoutput(0)
printstring("Code="); write(codesize,0)
printsymbol('+') %and write(ormax<<1+2,0) %unless datasize=0
printstring(", Data="); write(datasize,0)
printsymbol('+') %and write(relocp<<1+2,0) %unless datasize=0
printstring(" bytes;  Exports="); write(exportcount,0)
printstring(", Imports="); write(importcount,0)
newline

%endofprogram
