-- IntelHexImpl6.mesa
-- Last Modified: Stewart  January 5, 1982  12:20 PM
-- Converts an Intel absolute loader format file into
--commands to an output stream.

DIRECTORY
  Ascii USING [CR],
  Inline,
  IntelHex6;

IntelHexImpl6: PROGRAM
IMPORTS Inline
EXPORTS IntelHex6 =
  BEGIN

  -- signal raised on unexpected end of input file
  iEOF: SIGNAL = CODE;

  -- signal raised on checksum error in input file
  ChecksumError: SIGNAL = CODE;

  BitOp: TYPE = PROC [a, b: UNSPECIFIED] RETURNS [UNSPECIFIED];

  And: BitOp = INLINE { RETURN[Inline.BITAND[a,b]]; };
  Shift: BitOp = INLINE { RETURN[Inline.BITSHIFT[a,b]]; };


  -- sum of bytes, contribution to checksum
  WordCk: PROC [c: CARDINAL] RETURNS [CARDINAL] = {
    v: Inline.BytePair ← LOOPHOLE[c];
    RETURN [v.low+v.high];
    };

  -- The checksum is the value making sum of checked bytes equal zero.
  CkSum: PROC [c: CARDINAL] RETURNS [CARDINAL] = {
    v: INTEGER ← And[c, 377B];
    v ← -v;
    v ← And[v, 377B];
    RETURN [LOOPHOLE[v, CARDINAL]];
    };

ProcessFile: PUBLIC PROC [in: IntelHex6.IStream, out: IntelHex6.OStream, errs: IntelHex6.PutCharProc]
      RETURNS [success: BOOLEAN] = {

  PutS: PROC [s: STRING] = {
    FOR i: CARDINAL IN [0..s.length) DO errs[s[i]]; ENDLOOP;
    };

  PutL: PROC [s: STRING] = { PutS[s]; PutCR[]; };
  
  PutHexNum: PROC [x: CARDINAL] = {
    Dig: PROC [y: CARDINAL] RETURNS [CHARACTER] = {
      y ← And[y, 017B];
      IF y < 10 THEN RETURN[y+'0]
      ELSE RETURN[y-10+'A];
      };
    errs[Dig[Shift[x, -12]]];
    errs[Dig[Shift[x, -8]]];
    errs[Dig[Shift[x, -4]]];
    errs[Dig[x]];
    };

  PutCR: PROC = { errs[Ascii.CR]; };
  
  {
  ENABLE {
    iEOF => {
      PutL["Unexpected end of input file."];
      GOTO ReturnFalse;
      };
    ChecksumError => {
      PutL["Checksum error in input file."];
      GOTO ReturnFalse;
      };
    };

  inCkecksum: CARDINAL;  -- checksum of input record
  rectype, length, count, frame, bytepos: CARDINAL;

  -- read a character, adding to input checksum
  CkChar: PROC RETURNS[v: CARDINAL] = {
    IF in.endof[] THEN SIGNAL iEOF;
    v ← in.get[];
    inCkecksum ← inCkecksum+v;
    };

  -- read a word, adding to input checksum
  CkWord: PROC RETURNS [CARDINAL] = {
    v: Inline.BytePair;
    IF in.endof[] THEN SIGNAL iEOF;
    v.high ← in.get[];
    v.low ← in.get[];
    inCkecksum ← inCkecksum+v.high + v.low;
    RETURN[LOOPHOLE[v, CARDINAL]];
    };

  CheckChecksum: PROC = {
    [] ← CkChar[];
    IF And[inCkecksum, 377B]#0 THEN SIGNAL ChecksumError;
    };

  -- Write a data record after every byte filling a paragraph
  -- global variables for data address
  gda: LONG CARDINAL;

  -- Set a new data address.
  NewDataAddress: PROC [frame, bytepos: CARDINAL] = {
    gda ← Shift[frame, 4] + bytepos;
    };

  DataByte: PROC [d: CARDINAL] = {
    out.DataByte[adr: gda, d: d];
    gda ← gda+1;
    };

  -- handle physical iterated data record
  -- the input file must be in the right position on entry and is
  --left at the end of the iterated block on exit
  DumpRepeated: PROC [length: CARDINAL] RETURNS [CARDINAL] = {
    rptcount, blockcount, nlength: CARDINAL;
    pos: LONG CARDINAL;
    rptcount ← CkWord[];
    blockcount ← CkWord[];
    length ← length-4;
    IF blockcount = 0 THEN {
      count: CARDINAL ← CkChar[];
      nlength ← length - count - 1;
      pos ← in.GetPosition[];
      THROUGH [0..rptcount) DO
	in.SetPosition[pos];
	THROUGH[0..count) DO DataByte[CkChar[]]; ENDLOOP;
	ENDLOOP;
      }
    ELSE {
      pos ← in.GetPosition[];
      THROUGH [0..rptcount) DO
	nlength ← length;
	in.SetPosition[pos];
	THROUGH[0..blockcount) DO
	  nlength ← DumpRepeated[nlength];
	  ENDLOOP;
	ENDLOOP;
      };
    RETURN [nlength];
    };

  -- Read each record, writing corresponding output record(s).
    DO
      inCkecksum ← 0;
      rectype ← CkChar[];
      SELECT rectype FROM
	200B, 202B => {
	  length ← CkWord[];
	  count ← CkChar[];
	  length ← length - count - 1;
	  THROUGH [0..count) DO [] ← CkChar[]; ENDLOOP;
	  CheckChecksum[];
	  };
	212B => {
	  modtype: CARDINAL;
	  length ← CkWord[];
	  modtype ← CkChar[];
	  SELECT Shift[modtype, -6] FROM
	    0 => out.PutEndRecord[];
	    1, 3 => {
	      frame ← CkWord[];
	      bytepos ← CkWord[];
	      out.PutStartRecord[frame: frame, bytepos: bytepos];
	      out.PutEndRecord[];
	      };
	    ENDCASE => {
	      PutS["Invalid end record attributes: "];
	      PutHexNum[modtype];
	      PutCR[];
	      };
	  CheckChecksum[];
	  EXIT; -- end of module, stop here
	  };
	204B => {
	  length ← CkWord[];
	  frame ← CkWord[];
	  bytepos ← CkChar[];
	  length ← length - 4;
	  NewDataAddress[frame: frame, bytepos: bytepos];
	  THROUGH [0..length) DO DataByte[CkChar[]]; ENDLOOP;
	  CheckChecksum[];
	  };
	206B => {
	  filepos: LONG CARDINAL;
	  length ← CkWord[];
	  frame ← CkWord[];
	  bytepos ← CkChar[];
	  length ← length - 4;
	  filepos ← in.GetPosition[];
	  NewDataAddress[frame: frame, bytepos: bytepos];
	  -- Check checksum up front, due to complexity of
	  --iterated block structure and danger of double counting.
	  THROUGH [0..length) DO [] ← CkChar[]; ENDLOOP;
	  CheckChecksum[];
	  in.SetPosition[filepos];
	  length ← DumpRepeated[length];
	  -- length should be 0
	  IF length#0 THEN {
	    PutL["End PIData, non zero length."];
	    GOTO ReturnFalse;
	    };
	  -- must eat checksum character
	  [] ← CkChar[];
	  };
	ENDCASE => {
	  PutS["Unknown record type: "];
	  PutHexNum[rectype];
	  PutCR[];
	  length ← CkWord[];
	  THROUGH [0..length-1) DO [] ← CkChar[]; ENDLOOP;
	  CheckChecksum[];
	  };
      ENDLOOP;

  RETURN[TRUE];
  };
  EXITS
  ReturnFalse => RETURN[FALSE];
  };  -- end of ProcessFile

  END.
October 4, 1981  3:06 PM, LStewart; Created
December 23, 1981  3:31 PM, LStewart; Suppress duplicate address records
 1-Jan-82 21:05:41, L. Stewart, Rewrite with Defs file