-- 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