%begin; !TOBIN: Convert Motorola object to binary

! Addresses are assumed to start at relative zero and
! to be monotonic increasing

%include "inc:util.imp"

%routine tobin
%integer sym,checksum,loc,pos,reclen,type

  %routine stop(%string(255)s)
    selectoutput(0); printstring(s)
    newline; %stop
  %end

  %routine phex(%integer x)
    phex(x>>4) %if x>>4#0
    x=x&15; x=x+7 %if x>9
    printsymbol(x+'0')
  %end

  %routine scan; !Hunt to start of next record of type 1 or 2
    %cycle
      readsymbol(sym) %until sym>' '
      stop("Illegal record delimiter") %unless sym='S'
      readsymbol(type)
      %returnif type='1' %or type='2'
      %exitif type='9'
      stop("Illegal record type") %unless type='0'
      readsymbol(sym) %until sym<' '
    %repeat
    selectoutput(0); phex(loc); stop("")
  %end

  %integerfn nibble
    readsymbol(sym); sym=sym-7 %if sym>='A'
    stop("Invalid hex digit") %unless '0'<=sym<='?'
    %result=sym-'0'
  %end

  %integerfn byte
  %integer b
    b=nibble<<4; b=b+nibble
    checksum=checksum+b; reclen=reclen-1; %result=b
  %end

  %integerfn word
  %integer w
    w=byte<<8; %result=w+byte
  %end

  pos=-1
  %cycle
    scan
    checksum=0; reclen = 0; reclen=byte-1
    stop("Record too short") %if reclen<3
    sym=byte %if type='2'; !Ignore ms byte of 3-byte address
    loc=word
    pos=loc %if pos=-1
    %while pos<loc %cycle
      pos=pos+1; printsymbol(255)
    %repeat
    %cycle
      printsymbol(byte); loc=loc+1; pos=loc
    %repeatuntil reclen<=0
    sym=byte
    stop("Checksum error") %if (checksum+1)&255#0
  %repeat
%end

%string(255)parm,in,out

  %onevent 3,4,9 %start
    selectoutput(0)
    printstring(event_message); newline
    %stop
  %finish

  parm = cliparam
  in = parm %and out = parm %unless parm -> in.("/").out
  selectinput(1); openinput(1,in)
  selectoutput(1); openoutput(1,out)
  tobin

%endofprogram
