# Copyright (c) 1979 by Allen Akin and Tom Chappell. # These programs were developed by Allen Akin and Tom Chappell # for use with the Software Tools Subsystem at the Georgia # Institute of Technology. General permission is granted to # copy and use this software product, provided that this # notice is included in full with each copy. # lk --- link sspl object files define(DEBUG, #) include PRIMOS_KEYS include "as6800_def.r.i" define(MAXFD,128) # run-time modes define(INCLUDEMODE, 0) define(LIBMODE, 1) define(NAMELISTMODE, 2) # machines define(NO_MACHINE,0) define(MOTOROLA_6800,1) define(INTEL_8080,2) include "lk_com.r.i" integer open_segment, segment_useful, unresolved integer junk call initialize # create output file, etc. if (open_segment (junk) ~= OK) call error ("Nothing to link.") call link call next_segment while (open_segment (junk) ~= EOF) { if (Mode == INCLUDEMODE | segment_useful (junk) == YES) call link call next_segment } call cleanup stop end # chainback --- resolve word-length forward references subroutine chainback (addr, val, type) integer addr, val include "lk_com.r.i" integer p, next p = addr while (p ~= LAMBDA) { call putrel (type, p) call xseek (p, Outfile) call getword (next, Outfile) call xseek (p, Outfile) call putword (val, Outfile) p = next } return end # cleanup --- terminate process of assembly subroutine cleanup include "lk_com.r.i" integer length, ctoa integer i, j, maplen call seek (1, Outfile) # put in Outfile length call putword (Seg_start (Outfile), Outfile) call xseek (Seg_start (Outfile), Outfile) # relocation map: call putbyte (RMAPCODE, Outfile) maplen = (Seg_start (Outfile) + 7) / 8 call putword (maplen, Outfile) for (i = 1; i <= maplen; i = i + 1) call putbyte (Rmap (i), Outfile) # symbol table entries: for (i = 1; i <= Symtop; i = i + 1) { call putbyte (SYMBOLCODE, Outfile) call putword (length (Mem (Sym_sym (i))) + 5, Outfile) call putword (Sym_typ (i), Outfile) call putword (Sym_val (i), Outfile) for (j = Sym_sym (i); Mem (j) ~= EOS; j = j + 1) call putbyte (ctoa (Mem (j)), Outfile) call putbyte (0, Outfile) # end-of-string } call close (Infile) call close (Outfile) return end # compare --- compare two strings, return -1 if <, 0 if =, 1 if > integer function compare (str1, str2) character str1 (ARB), str2 (ARB) integer i for (i = 1; str1 (i) == str2 (i); i = i + 1) if (str1 (i) == EOS) { compare = 0 return } if (str1 (i) > str2 (i)) compare = 1 else compare = -1 return end # connect --- connect chain1 and chain2; with chain1 in front subroutine connect (chain1, chain2) integer chain1, chain2 include "lk_com.r.i" integer p, nextp if (chain1 == LAMBDA) chain1 = chain2 else { p = chain1 repeat { call xseek (p, Outfile) call getword (nextp, Outfile) if (nextp == LAMBDA) break p = nextp } call xseek (p, Outfile) call putword (chain2, Outfile) } return end # copy_text --- copy text section from Infile to Outfile integer function copy_text (junk) integer junk include "lk_com.r.i" integer getbyte, getword integer b, len, typ if (getbyte (typ, Infile) ~= EOF) { copy_text = getword (len, Infile) for ( ; len ~= 0; len = len - 1) { call getbyte (b, Infile) call putbyte (b, Outfile) } return } call error ("Unexpected EOF in text section.") return end # ctoa --- convert Prime characters to real ASCII integer function ctoa (c) character c ctoa = rt (c, 7) return end # enter --- enter symbol, type, and value in symbol table subroutine enter (sym, type, val) character sym (ARB) integer type, val include "lk_com.r.i" integer equal pointer sdup integer i for (i = 1; i <= Symtop; i = i + 1) if (equal (sym, Mem (Sym_sym (i))) == YES) call error ("symbol redefined.") if (Symtop >= MAXSYMTOP) call error ("too many symbols --- link stopped.") Symtop = Symtop + 1 Sym_sym (Symtop) = sdup (sym) Sym_typ (Symtop) = type Sym_val (Symtop) = val return end # fix_chain --- relocate chain starting at addr subroutine fix_chain (addr) integer addr include "lk_com.r.i" integer p, nextp if (addr ~= LAMBDA) { p = addr repeat { call xseek (p, Outfile) call getword (nextp, Outfile) if (nextp == LAMBDA) break call xseek (p, Outfile) p = nextp + Seg_start (Outfile) call putword (p, Outfile) } } return end # getbyte --- read one byte from file integer function getbyte (b, fd) integer b, fd integer junk, rc integer mapfd call prwf$$ (KREAD, mapfd (fd), loc (b), 1, intl (0), junk, rc) if (rc == 0) getbyte = b else { b = EOF getbyte = EOF } return end # getnext --- get next argument, either from STDIN or arg list integer function getnext (arg) character arg (ARB) integer getarg, getlin integer count, len data count /0/ if (getarg (1, arg, MAXARG) ~= EOF) { count = count + 1 getnext = getarg (count, arg, MAXARG) } else { len = getlin (arg, STDIN, MAXARG) if (len <= 1) getnext = EOF else { arg (len) = EOS getnext = len } } return end # getword --- read word from file integer function getword (w, fd) integer w, fd integer getbyte include "lk_com.r.i" integer hi, lo if (Machine == INTEL_8080) { call getbyte (lo, fd) call getbyte (hi, fd) } else { call getbyte (hi, fd) call getbyte (lo, fd) } w = or (ls (hi, 8), lo) getword = w return end # get_symbol --- collect symbol info from Infile integer function get_symbol (sym, type, val) character sym (ARB) integer type, val include "lk_com.r.i" integer getbyte, getword integer i, len, stringend get_symbol = getword (len, Infile) # return length of symbol entry call getword (type, Infile) call getword (val, Infile) i = 1 for (len = len - 4; len > 1; len = len - 1) { call getbyte (sym (i), Infile) sym (i) = and (sym (i), :177) i = i + 1 } sym (i) = EOS if (getbyte (stringend, Infile) == EOF || stringend ~= 0) call error ("Can't happen: symbol section garbled.") return end # initialize --- set up everything subroutine initialize include "lk_com.r.i" integer create, next_file integer i, junk string outfile_name "l.out" Symtop = 0 call dsinit (MEMSIZE) Mode = INCLUDEMODE Machine = NO_MACHINE First_time = YES Outfile = create (outfile_name, READWRITE) if (Outfile == ERR) call error ("Can't create output file.") Seg_start (Outfile) = 0 call putbyte (TEXTCODE, Outfile) for (i = 2; i <= HEADER_LENGTH; i = i + 1) call putbyte (0, Outfile) if (next_file (junk) == EOF) call error ("Usage: lk -(6800 | 8080) {[-(i | l | n)] file}.") else if (Machine == NO_MACHINE) call error ("Usage: lk -(6800 | 8080) {[-(i | l | n)] file}.") return end # link --- link in current block with object file subroutine link include "lk_com.r.i" integer copy_text, lookup, get_symbol, getbyte character newsym_sym (MAXLINE) integer b, i, junk, l, newsym_typ, newsym_val integer text_size, len, posn call seek (Seg_start (Infile), Infile) if (Mode == NAMELISTMODE) { posn = Seg_start (Infile) for (i = 1; i <= 2; i = i + 1) { call getbyte (b, Infile) call getword (len, Infile) posn = posn + 3 + len call seek (posn, Infile) } while (getbyte (b, Infile) == SYMBOLCODE) { call get_symbol (newsym_sym, newsym_typ, newsym_val) if (newsym_typ ~= EXTERNAL) { # new symbol defined newsym_typ = ABSOLUTE l = lookup (newsym_sym) # look for old symbol of same name if (l == ERR) next else if (Sym_typ (l) == EXTERNAL) { call chainback (Sym_val (l), newsym_val, newsym_typ) Sym_typ (l) = newsym_typ Sym_val (l) = newsym_val } } } } else { text_size = copy_text (junk) call update_rbits (text_size) # update relocation bits; correct text while (getbyte (b, Infile) == SYMBOLCODE) { call get_symbol (newsym_sym, newsym_typ, newsym_val) if (newsym_typ == RELOCATABLE || newsym_typ == EXTERNAL & newsym_val ~= LAMBDA) newsym_val = newsym_val + Seg_start (Outfile) l = lookup (newsym_sym) # look for old symbol of same name if (newsym_typ ~= EXTERNAL) { # new symbol defined if (l == ERR) # first time we've seen it call enter (newsym_sym, newsym_typ, newsym_val) else if (Sym_typ (l) == EXTERNAL) { # old symbol external call chainback (Sym_val (l), newsym_val, newsym_typ) Sym_typ (l) = newsym_typ Sym_val (l) = newsym_val } else call print (ERROUT, "Doubly defined: *s*n.", Mem (Sym_sym (l)) ) } else { # new symbol is external call fix_chain (newsym_val) if (l == ERR) # first time we've seen it call enter (newsym_sym, newsym_typ, newsym_val) else if (Sym_typ (l) ~= EXTERNAL) # old symbol defined call chainback (newsym_val, Sym_val (l), Sym_typ (l)) else { # old symbol external call connect (newsym_val, Sym_val (l)) Sym_val (l) = newsym_val } } } Seg_start (Outfile) = Seg_start (Outfile) + text_size } call xseek (Seg_start (Outfile), Outfile) return end # lookup --- return location of symbol in the symbol table, or ERR integer function lookup (sym) character sym (ARB) include "lk_com.r.i" integer equal integer i lookup = ERR # guilty until proven innocent for (i = 1; i <= Symtop; i = i + 1) { if (equal (sym, Mem (Sym_sym (i))) == YES) { if (Sym_typ (i) == ALIAS) lookup = Sym_val (i) else lookup = i break } } return end # next_file --- process arguments; get next file name & open it integer function next_file (junk) integer junk include "lk_com.r.i" integer equal, getnext, open character arg (MAXLINE) string m6800_flag "-6800" string i8080_flag "-8080" string incflag "-i" string libflag "-l" string nameflag "-n" repeat { if (getnext (arg) == EOF) { next_file = EOF return } else if (equal (arg, m6800_flag) == YES) { Machine = MOTOROLA_6800 next } else if (equal (arg, i8080_flag) == YES) { Machine = INTEL_8080 next } else if (equal (arg, incflag) == YES) { Mode = INCLUDEMODE next } else if (equal (arg, libflag) == YES) { Mode = LIBMODE next } else if (equal (arg, nameflag) == YES) { Mode = NAMELISTMODE next } else break } Infile = open (arg, READ) if (Infile == ERR) call cant (arg) DEBUG call print(ERROUT, "open: *s*n.", arg) Seg_start (Infile) = 0 next_file = OK return end # next_segment --- seek to end of object segment; set Seg_start (Infile) subroutine next_segment include "lk_com.r.i" integer getbyte, getword integer len, posn, type posn = Seg_start (Infile) call seek (posn, Infile) if (getbyte (type, Infile) == EOF) call error ("Can't happen: no segment.") repeat { call getword (len, Infile) # skip this part posn = posn + 3 + len call seek (posn, Infile) if (getbyte (type, Infile) == EOF) break # Seg_start is wrong, but open_segment fixes it if (type == TEXTCODE) { # start of segment we want call seek (posn, Infile) # get back to start Seg_start (Infile) = posn break } } return end # open_segment --- open next object segment and verify integer function open_segment (junk) integer junk include "lk_com.r.i" integer getbyte, next_file integer type while (getbyte (type, Infile) == EOF) { call close (Infile) if (next_file (junk) == EOF) { open_segment = EOF return } } if (type ~= TEXTCODE) call error ("Format error in link segment.") open_segment = OK return end # putbyte --- write byte out to file subroutine putbyte (b, fd) integer b, fd integer w, junk integer mapfd w = rt (b, 8) call prwf$$ (KWRIT, mapfd (fd), loc (w), 1, intl (0), junk, junk) return end # putrel --- set a bit in the relocation bit map subroutine putrel (reloc, address) integer reloc, address include "lk_com.r.i" integer word, mask word = address / 8 + 1 mask = ls (1, 7 - mod (address, 8)) if (reloc == RELOCATABLE) Rmap (word) = or (Rmap (word), mask) else Rmap (word) = and (Rmap (word), not (mask)) return end # putword --- put word out on file subroutine putword (w, fd) integer w, fd include "lk_com.r.i" if (Machine == INTEL_8080) { call putbyte (rt (w, 8), fd) call putbyte (rs (w, 8), fd) } else { call putbyte (rs (w, 8), fd) call putbyte (rt (w, 8), fd) } return end # reloc --- relocate word at addr in Outfile (add offset to word at addr) subroutine reloc (addr, offset) integer addr, offset include "lk_com.r.i" integer w call xseek (addr, Outfile) call getword (w, Outfile) w = w + offset call xseek (addr, Outfile) call putword (w, Outfile) return end # sdup --- duplicate string in dynamic memory pointer function sdup (str) character str (ARB) include "lk_com.r.i" integer p integer length, dsget p = dsget (length (str) + 1) call scopy (str, 1, Mem, p) sdup = p return end # seek --- seek to given position in fd subroutine seek (posn, fd) integer posn, fd integer junk integer mapfd call prwf$$ (KPOSN + KPREA, mapfd (fd), loc (0), 0, intl (posn), junk, junk) return end # segment_useful --- return YES if block contains symbols we need integer function segment_useful (junk) integer junk include "lk_com.r.i" integer getbyte, getword, get_symbol, lookup integer l, len, newsym_typ, newsym_val, posn, type character newsym_sym (MAXLINE) posn = Seg_start (Infile) call seek (posn, Infile) if (getbyte (type, Infile) == EOF) call error ("Can't happen: no segment.") repeat { if (type ~= SYMBOLCODE) { # skip call getword (len, Infile) posn = posn + 3 + len call seek (posn, Infile) } else { len = get_symbol (newsym_sym, newsym_typ, newsym_val) posn = posn + 3 + len if (newsym_typ ~= EXTERNAL) { l = lookup (newsym_sym) if (l ~= ERR) if (Sym_typ (l) == EXTERNAL) { segment_useful = YES return } } } if (getbyte (type, Infile) == EOF) break } until (type == TEXTCODE) segment_useful = NO return end # update_rbits --- handle relocation segment; correct text subroutine update_rbits (text_size) integer getbyte, getword include "lk_com.r.i" integer text_count, i, len, place_count, rbyte, text_size, typ if (getbyte (typ, Infile) ~= EOF) { call getword (len, Infile) text_count = text_size place_count = Seg_start (Outfile) for ( ; len > 0; len = len - 1) { call getbyte (rbyte, Infile) for (i = 7; i >= 0 & text_count > 0; i = i - 1) { if (and (rbyte, ls (1, i)) ~= 0) { call putrel (RELOCATABLE, place_count) call reloc (place_count, Seg_start (Outfile)) } else call putrel (ABSOLUTE, place_count) # not relocatable place_count = place_count + 1 text_count = text_count - 1 } } return } call error ("Unexpected EOF in relocation section.") return end # unresolved --- return YES if there are still some unresolved externals integer function unresolved (junk) integer junk include "lk_com.r.i" integer i unresolved = NO # assume the best for (i = 1; i <= Symtop; i = i + 1) if (Sym_typ (i) == EXTERNAL) { unresolved = YES break } return end # xseek --- seek in file fd, skipping header subroutine xseek (posn, fd) integer posn include "lk_com.r.i" call seek (posn + HEADER_LENGTH, fd) return end