-- MBImpl6.mesa
-- Last Modified: Stewart   7-Jan-82 18:19:40
-- Converts an Intel absolute loader format file into
--An MB format file for Prom blowing.
-- ObjToMB infile outfile -[eob] -s romsize(dec) -a romaddress(hex)
-- e: even bytes only, o: odd bytes only, b: both
-- for even and odd, the address within the rom is divided by 2.

DIRECTORY
  ComParse USING [Close, FreeString, Get, Open],
  Inline,
  IntelHex6,
  Storage USING [Words, FreeWords],
  StreamDefs,
  String,
  WF;

MBImpl6: PROGRAM
IMPORTS ComParse, Inline, IntelHex6, Storage, StreamDefs, String, WF =
  BEGIN
  iName: STRING ← NIL;
  oName: STRING ← NIL;
  token: STRING ← NIL;
  iS: StreamDefs.DiskHandle ← NIL;
  oS: StreamDefs.DiskHandle ← NIL;
  Mode: TYPE = {both, even, odd};
  mode: Mode;

  Lose: SIGNAL = CODE;

  myIStream: IntelHex6.IStream ← [
    endof: myEndOf,
    get: myGetByte,
    GetPosition: myGetPosition,
    SetPosition: mySetPosition
    ];

  myOStream: IntelHex6.OStream ← [
    PutEndRecord: myPutEndRecord,
    PutStartRecord: myPutStartRecord,
    DataByte: myDataByte
    ];

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

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

  WordOp: TYPE = PROC [a: UNSPECIFIED] RETURNS [UNSPECIFIED];

  Low: WordOp = INLINE { RETURN[Inline.LowHalf[a]]; };
  High: WordOp = INLINE { RETURN[Inline.HighHalf[a]]; };

  -- 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]];
    };

  -- Write an end record.
  myPutEndRecord: IntelHex6.PutEndRecordProc = {
    NULL;
    };

  -- Write a start record.
  myPutStartRecord: IntelHex6.PutStartRecordProc = {
    NULL;
    };

  InitLocalStorage: PROC = {
    maxadr ← 0;
    };

  GetHex: PROC [s: STRING] RETURNS [CARDINAL] = {
    c: CARDINAL ← 0;
      neg: BOOLEAN ← FALSE;
    FOR i: CARDINAL IN [0..s.length) DO
      SELECT s[i] FROM
        '- => neg ← TRUE;
        IN ['0..'9] => c ← (c*16)+(s[i]-'0);
        IN ['a..'f] => c ← (c*16)+(s[i]-'a)+10;
        IN ['A..'F] => c ← (c*16)+(s[i]-'A)+10;
        ENDCASE;
      ENDLOOP;
    IF neg THEN c ← 0-c;
    RETURN[c];
    };

  -- Write a data record after every byte filling a paragraph
  --Write a new extended address record first thing and
  --thereafter when the bank changes.
  Byte: TYPE = [0..377B];
  data: DESCRIPTOR FOR PACKED ARRAY [0..8192) OF Byte;
  maxadr, rombase, romsize: CARDINAL;

  myDataByte: IntelHex6.DataByteProc = {
    locadr: CARDINAL;
    IF adr<rombase THEN RETURN;
    adr ← adr-rombase;
    SELECT mode FROM
      even => {
        IF And[Low[adr], 1]  # 0 THEN RETURN;
        adr ← adr/2;  -- right shift
        };
      odd => {
        IF And[Low[adr], 1]  = 0 THEN RETURN;
        adr ← adr/2;  -- right shift
        };
      ENDCASE;
    IF High[adr]#0 THEN {
      WF.WF0[" Address outside 64K!*n"];
      SIGNAL Lose;
      };
    IF adr >= (rombase+romsize) THEN RETURN;
    locadr ← Low[adr];
    IF locadr > maxadr THEN maxadr ← locadr;
    data[locadr] ← d;
    };

  OByte: PROC [x: CARDINAL] = INLINE { oS.put[oS, x]; };
  OWord: PROC [x: CARDINAL] = INLINE {
    oS.put[oS, Inline.HighByte[x]];
    oS.put[oS, Inline.LowByte[x]];
    };

  MakeMBFile: PROC = {
    OWord[4];  -- boilerplate
    OWord[1];  -- memorynumber
    OWord[8];  -- memorywidth in bits
    FOR i: CARDINAL IN [0..oName.length) DO
      OByte[oName[i]-0C];
      ENDLOOP;
    OByte[0];  -- ASCIZ end of string
    -- pad to word boundary
    IF And[oName.length, 1] = 0 THEN OByte[0];
    OWord[2];  -- set current memory and location
    OWord[1];  -- memorynumber again
    OWord[0];  -- location
    FOR i: CARDINAL IN [0..maxadr] DO
      OWord[1];  -- memory data word
      OWord[0];  -- source line number (not used)
      OWord[Shift[data[i], 8]]; -- left justified
      ENDLOOP;
    OWord[0];  -- end of file
    };

  myEndOf: IntelHex6.EndOfProc = { RETURN[iS.endof[iS]]; };
  myGetByte: IntelHex6.GetByteProc = { RETURN[iS.get[iS]]; };
  myGetPosition: IntelHex6.GetPositionProc = { RETURN[StreamDefs.GetPosition[iS]]; };
  mySetPosition: IntelHex6.SetPositionProc = { StreamDefs.SetPosition[iS, p]; };

  myPutC: IntelHex6.PutCharProc = { WF.WFC[c]; };
  
  -- Mainline code

  {
  CLState: TYPE = { in, out, done };
  cls: CLState ← in;
  data ← DESCRIPTOR[Storage.Words[4096], 8192];
  ComParse.Open[];
  mode ← both;
  rombase ← 0;
  romsize ← 2048;
  DO
    token ← ComParse.Get[];
    IF token = NIL THEN EXIT;
    IF token[0]#'- THEN SELECT cls FROM
      in => {
        iName ← token;
        cls ← out;
        LOOP;
        };
      out => {
        oName ← token;
        cls ← done;
        LOOP;
        };
      ENDCASE => {
        WF.WF0[" syntax errors*n"];
        GOTO Cleanup;
        };
      IF token.length<2 THEN {
        WF.WF0[" Bad switch*n"];
        GOTO Cleanup;
        };
      SELECT token[1] FROM
        'e => { mode ← even; WF.WF0[" even mode*n"]; };
        'o => { mode ← odd; WF.WF0[" odd mode*n"]; };
        'b => { mode ← both; WF.WF0[" both mode*n"]; };
        's => {
          ComParse.FreeString[token];
          token ← ComParse.Get[];
          romsize ← String.StringToDecimal[token];
          WF.WF1[" rom size = %d bytes*n", romsize];
          IF romsize > 8192 THEN {
            WF.WF0[" Max rom capacity 8192*n"];
            GOTO Cleanup;
            };
          };
        'a => {
          ComParse.FreeString[token];
          token ← ComParse.Get[];
          rombase ← GetHex[token];
          WF.WF1[" rom address = %x*n", rombase];
          };
        ENDCASE;
      ComParse.FreeString[token];
      ENDLOOP;
  IF iName = NIL OR oName=NIL THEN {
    WF.WF0["Usage: ObjToMB filename outputfilename*n"L];
    GOTO Cleanup;
    };
  iS ← StreamDefs.NewByteStream[iName, StreamDefs.Read !
    StreamDefs.FileNameError => {
      WF.WF1["ObjToMB %s: no such file*n"L, iName];
      GOTO Cleanup;
      };];
  oS ← StreamDefs.NewByteStream[oName, StreamDefs.Write + StreamDefs.Append];
  iS.reset[iS];
  oS.reset[oS];
  InitLocalStorage[];
  IF NOT IntelHex6.ProcessFile[in: myIStream, out: myOStream, errs: myPutC ! Lose => GOTO Cleanup] THEN WF.WF0["  Failed*n"];
  MakeMBFile[];
  GOTO Cleanup;
  EXITS
  Cleanup => {
    ComParse.Close[];
    IF iS#NIL THEN iS.destroy[iS];
    IF oS#NIL THEN oS.destroy[oS];
    IF oName#NIL THEN ComParse.FreeString[oName];
    IF iName#NIL THEN ComParse.FreeString[iName];
    Storage.FreeWords[BASE[data]];
    };
  };

  END.
October 4, 1981  3:06 PM, LStewart; Created
December 23, 1981  3:31 PM, LStewart; Suppress duplicate address records
 2-Jan-82 11:45:27, LStewart; Use IntelHex6
January 7, 1982  11:22 AM, LStewart; Fewer extended address records
January 7, 1982  5:52 PM, LStewart; MB files