! 8086 Object Code Loader

%include "inc:util.imp"
%option "-nodiag"

!Object format
%constinteger setcode=1
%constinteger setdata=2
%constinteger setloc=3
%constinteger append=4
%constinteger patch=5
%constinteger def=6
%constinteger sref=7
%constinteger ref=8
%constinteger endmodule=9
%constinteger endfile=10

%externalroutine load86(%string(31)filename,
  %integername codestart,codesize,datastart,datasize)
%integer filestart,fileend,p
%integer codelow=65535,codehigh=0
%integer datalow=65535,datahigh=0

%routine move(%integer bytes,from,to)
%option "-low"
%label no
  *move.l d1,a0
  *move.l d2,a1
  *subq.l #1,d0
  *bmi no
  *swap d0
a:*swap d0
b:*move.b (a0)+,(a1)+
  *dbra d0,b
  *swap d0
  *dbra d0,a
no:
%end

%integerfn enoughfor(%integer x,init)
!Allocate enough space for X bytes rounded up to a multiple of 16,
!to start on a 16-byte boundary
%integer a,i
  x = (x+15)&\15
  a = (heapget(x+16)+15)&\15
  byte(a+i) = init %for i = 0,1,x-1
  %result = a
%end

%integerfn b
  p = p+1; %result = byteinteger(p-1)
%end

%integerfn w
%integer l
  l = b; %result = b<<8+l
%end

%routine pass1
! Determine sizes of code and data segments
! P is point in object file at which scanning is to start
! Results passed via code/data-low/high
%integer codeloc=0,dataloc=0,i
%integername loc,low,high
%switch sw(setcode:endfile)
  loc == codeloc; low == codelow; high == codehigh
  %while p<fileend %cycle
    ->sw(b)
sw(setcode): loc == codeloc; low == codelow; high == codehigh; %continue
sw(setdata): loc == dataloc; low == datalow; high == datahigh; %continue
sw(setloc): loc = w; %continue
sw(append): low = loc %if loc<low
            i = w; loc = loc+i; p = p+i
            high = loc %if loc>high
            %continue
sw(patch):
sw(def):
sw(sref):
sw(ref):    %signal 15,,,"Unlinked object file ".filename
  %repeat
sw(endfile):
sw(endmodule):
  codelow = 0 %and codehigh = 0 %if codelow>codehigh
  datalow = 0 %and datahigh = 0 %if datalow>datahigh
  codesize = codehigh-codelow
  datasize = datahigh-datalow
%end

%routine pass2
! Load information into the code and data segments
! P is point in object file at which scanning is to start
%integer codeloc=0,dataloc=0,i
%integername loc,low,start
%switch sw(setcode:endfile)
  loc == codeloc; low == codelow; start == codestart
  %while p<fileend %cycle
    ->sw(b)
sw(setcode): loc == codeloc; low == codelow; start == codestart; %continue
sw(setdata): loc == dataloc; low == datalow; start == datastart; %continue
sw(setloc): loc = w; %continue
sw(append): i = w
            move(i,p,start+loc-low)
            loc = loc+i; p = p+i; %continue
sw(patch):
sw(def):
sw(sref):
sw(ref):    %signal 15,,,"Unlinked object file ".filename
  %repeat
sw(endfile):
sw(endmodule):
%end

  connectfile(filename,0,filestart,fileend)
  fileend = fileend+filestart
  p = filestart
  pass1
  codestart = enoughfor(codesize,255)
  datastart = enoughfor(datasize,0)
  p = filestart
  pass2
  heapput(filestart)
%end

%begin
%integer codestart,codesize,datastart,datasize
%string(255)infile,outfile=""
  define param("IOB file",infile,pamnodefault)
  define param("ROM file",outfile,pamnewgroup)
  process parameters(cliparam)
  outfile = infile.".rom" %if outfile=""
  infile = infile.".iob"
  load86(infile,codestart,codesize,datastart,datasize)
  phex(datasize); printstring(" bytes of data + ")
  phex(codesize); printstring(" bytes of code"); newline
  openoutput(1,outfile); selectoutput(1)
  datasize = datasize-1 %and-
  printsymbol(byte(datastart)) %and datastart = datastart+1 %while datasize>0
  codesize = codesize-1 %and-
  printsymbol(byte(codestart)) %and codestart = codestart+1 %while codesize>0
%endofprogram
