-- MakeLoaderFile.mesa -- last edited: -- January 9, 1984 4:46 PM by Taft -- HGM on: October 10, 1980 2:49 AM -- Make D0 boot files from .mb files DIRECTORY AltoFileDefs USING [CFA], ImageDefs USING [MakeImage, StopMesa], InlineDefs USING [BITAND, BITNOT, BITOR, BITSHIFT, BITXOR, LowHalf, HighHalf], IODefs USING [ CR, ReadChar, ReadID, ReadLine, ReadOctal, WriteDecimal, WriteChar, WriteLine, WriteOctal, WriteString], MiscDefs USING [CommandLineCFA], SegmentDefs USING [ Append, FileHandle, FileNameError, GetFileTimes, InsertFile, InvalidFP, Read, Write], StreamDefs USING [ CreateByteStream, DiskHandle, GetIndex, JumpToFA, ModifyIndex, NewByteStream, NewWordStream, SetIndex, StreamError, WriteBlock], String USING [ AppendChar, AppendString, EquivalentString, LowerCase, StringToOctal, WordsForString], Storage USING [Node], TimeDefs USING [PackedTime]; MakeLoaderFile: PROGRAM IMPORTS ImageDefs, InlineDefs, IODefs, MiscDefs, SegmentDefs, StreamDefs, String, Storage = BEGIN -- Central to this program is the array IMem, a model of the instruction memory. -- Commands exist to load IMem from .mb format files, and -- to dump IMem to .bt and .sb format files. IMem: POINTER TO ARRAY IMemRange OF IMemCell; IMemRange: TYPE = [0..4096); IMemCell: TYPE = RECORD [first, second, third: WORD]; nullIMemCell: IMemCell = [0, 1, 0]; -- odd parity PackedInst: TYPE = MACHINE DEPENDENT RECORD [SELECT OVERLAID * FROM inst => [addr: [0..4095], word2: [0..15], word0: WORD, word1: WORD], end => [addr: [0..4095], fill: [0..15], checkSum: WORD, startLoc: WORD], -- addr=4095 words => [a, b, c: WORD], ENDCASE]; miPerSector: CARDINAL = 256/SIZE[PackedInst]; -- number of microinstructions per sector bpCount: CARDINAL _ 0; -- counts BPs encountered ClearIMem: PROCEDURE = BEGIN IMem^ _ ALL[nullIMemCell]; END; Word: TYPE = MACHINE DEPENDENT RECORD [SELECT OVERLAID * FROM bytes => [left, right: [0..255]], word => [word: WORD], ENDCASE]; LoadIMem: PROCEDURE = BEGIN OPEN InlineDefs; MicroIMemory: CARDINAL = 1; MicroRMemory: CARDINAL = 2; MicroMMemory: CARDINAL = 3; MicroZMemory: CARDINAL = 4; NoMemory: CARDINAL = 100; fun: ARRAY [0..16] OF WORD = [ 0,100000B,140000B,160000B,170000B, 174000B,176000B,177000B,177400B,177600B,177700B, 177740B,177760B,177770B,177774B,177776B,177777B]; currentMemory: WORD _ NoMemory; type: Word; -- Interpret Micro/MicroD-style output file -- (see Appendix 3 of Micro section of D0 Microprogrammmer's Manual) DO type _ [word[GetWord[]]]; -- right byte is significant; left is don't care here SELECT type.right FROM 0 => -- end of file EXIT; 1 => -- data word for current location of current memory BEGIN IMemAddr, IMemAddrWord: WORD; temp: IMemCell; SkipWords[1]; -- source line number SELECT currentMemory FROM MicroRMemory, MicroMMemory, MicroZMemory => SkipWords[1]; -- data MicroIMemory => BEGIN temp _ [GetWord[], BITAND[GetWord[], 177776B], GetWord[]]; IMemAddrWord _ GetWord[]; Parity[@temp]; IMemAddr _ BITAND[7777B, IMemAddrWord]; IF (BITAND[IMemAddrWord, 140000B]#0) THEN BEGIN bpCount _ bpCount+1; IODefs.WriteString["BREAKPOINT instr at loc "]; IODefs.WriteOctal[IMemAddr]; IODefs.WriteLine[""] END; IMem[IMemAddr] _ temp END; ENDCASE => IODefs.WriteString[" unknownMemoryType"]; END; 2 => -- switch memories/location BEGIN currentMemory _ GetWord[]; SkipWords[1]; -- currentLocation; overridden by address in type 1 format END; -- 3 => forward reference fixup 4 => -- memory-symbolic name correlation BEGIN SkipWords[2]; -- memory #, width of memory SkipString[] -- symbolic name END; 5 => -- address symbol definition BEGIN SkipWords[2]; -- memory #, value SkipString[] -- symbol name END; 6 => -- reference to undefined symbol BEGIN SkipWords[3]; -- memory #, location, first bit,,last bit SkipString[] -- symbol name END; ENDCASE => IODefs.WriteString["UnexpectedMicroFormat"] ENDLOOP END; Parity: PROCEDURE [t: POINTER TO IMemCell] = BEGIN OPEN InlineDefs; p: CARDINAL; t.second _ BITAND[t.second, 177776B]; p _ BITXOR[t.first, t.second]; p _ BITXOR[p, BITAND[t.third, 170000B]]; p _ BITXOR[p, BITSHIFT[p, -8]]; p _ BITXOR[p, BITSHIFT[p, -4]]; p _ BITXOR[p, BITSHIFT[p, -2]]; p _ BITNOT[BITXOR[p, BITSHIFT[p, -1]]]; t.second _ BITOR[t.second, BITAND[p, 1]] END; Format: TYPE = {soft, ether}; -- See MakeLoaderFile.memo for details softVersionNumber: CARDINAL _ 0; etherVersionNumber: CARDINAL _ 1; DumpIMem: PROCEDURE [format: Format, startLoc: CARDINAL] = BEGIN OPEN InlineDefs; i: CARDINAL; x: IMemCell; px: PackedInst; endMarker: PackedInst _ [end[addr: 7777B, fill: 0, checkSum: , startLoc: startLoc]]; checkSum: WORD _ 0; -- accumulator for soft format check sum FOR i IN IMemRange DO x _ IMem[i]; IF x#nullIMemCell THEN BEGIN px _ [inst[addr: i, word2: BITSHIFT[x.third, -12], word0: x.first, word1: x.second]]; checkSum _ checkSum + px.a + px.b + px.c; PutInst[format: format, inst: px, andFlush: FALSE] END ENDLOOP; -- Put out end marker and flush buffer (if appropriate) endMarker.checkSum _ 0-(checkSum + endMarker.a + endMarker.c); PutInst[format: format, inst: endMarker, andFlush: TRUE] END; -- Input/output routines -- GetX means read from stream -- PutX means write to stream -- InX means read from command file or keyboard -- OutX means write to command file or display -- Input stream infile: STRING = [40]; is: StreamDefs.DiskHandle; -- a word stream GetWord: PROCEDURE RETURNS [WORD] = INLINE BEGIN RETURN[is.get[is]] END; SkipWords: PROCEDURE [count: CARDINAL] = BEGIN THROUGH [0..count) DO [] _ is.get[is] ENDLOOP END; SkipString: PROCEDURE = -- skip Bcpl-style string BEGIN word: Word; DO word _ [word[is.get[is]]]; IF word.left=0 OR word.right=0 THEN EXIT ENDLOOP END; -- Output stream outfile: STRING = [40]; os: StreamDefs.DiskHandle; -- a word stream wordsWritten: CARDINAL _ 0; PutWord: PROCEDURE [w: WORD] = INLINE BEGIN os.put[os, w]; wordsWritten _ wordsWritten+1 END; PutBlock: PROCEDURE [address: POINTER, words: CARDINAL] = BEGIN [] _ StreamDefs.WriteBlock[os, address, words]; wordsWritten _ wordsWritten+words END; PutHeader: PROCEDURE [format: Format, name: STRING] = INLINE BEGIN SELECT format FROM soft => PutWord[softVersionNumber]; ether => BEGIN createTime: TimeDefs.PackedTime _ SegmentDefs.GetFileTimes[os.file].create; destMaxLength: CARDINAL _ name.length + name.length MOD 2; wordsForString: CARDINAL _ String.WordsForString[destMaxLength]; -- The first 5 words must agree with the format of an Alto boot file PutWord[etherVersionNumber]; PutWord[0]; -- or else boot file will get reformated PutWord[0]; PutWord[InlineDefs.HighHalf[createTime]]; -- BCPL format PutWord[InlineDefs.LowHalf[createTime]]; PutWord[name.length]; -- length PutWord[destMaxLength]; -- maxlength PutBlock[@name.text, wordsForString-2]; -- We should probably put a version stamp and our version stamp in here PutPadding[]; END; ENDCASE => ERROR END; PutInst: PROCEDURE [format: Format, inst: PackedInst, andFlush: BOOLEAN] = BEGIN PutWord[inst.a]; PutWord[inst.b]; PutWord[inst.c]; END; PutTrailer: PROCEDURE [format: Format] = BEGIN SELECT format FROM ether => NULL; soft => PutPadding[]; ENDCASE => ERROR END; PutPadding: PROCEDURE = -- make sure file size is multiple of 512 bytes BEGIN wordsOnLastPage: CARDINAL = wordsWritten MOD 256; IF wordsOnLastPage~=0 THEN THROUGH [0..256-wordsOnLastPage) DO PutWord[0] ENDLOOP END; -- Command stream commandFile: BOOLEAN _ FALSE; -- FALSE => read from keyboard isCommand: StreamDefs.DiskHandle; -- a byte stream (also used to read Com.cm) CR: CHARACTER = 15C; Space: CHARACTER = ' ; Tab: CHARACTER = 11C; tstr: STRING _ [200]; tstrLength: CARDINAL; InChar: PROCEDURE RETURNS [ch: CHARACTER] = BEGIN tstr.length _ 0; IF commandFile THEN BEGIN InLine[tstr]; RETURN[ch _ tstr[0]] END ELSE ch _ IODefs.ReadChar[] END; InLine: PROCEDURE [st: STRING] = BEGIN i: CARDINAL; ch: CHARACTER; IF commandFile THEN BEGIN st.length _ i _ 0; UNTIL (ch _ isCommand.get[isCommand])=CR DO String.AppendChar[st,ch]; i _ i+1 ENDLOOP; tstrLength _ st.length _ i; i _ 0; -- skip over comments UNTIL i=st.length OR st[i]=Space OR st[i]=Tab DO i _ i+1 ENDLOOP; st.length _ i END ELSE IODefs.ReadLine[st] END; InString: PROCEDURE [st: STRING] = BEGIN i: CARDINAL; ch: CHARACTER; IF commandFile THEN BEGIN i _ 0; UNTIL (ch _ isCommand.get[isCommand])=CR DO IODefs.WriteChar[ch]; st[i] _ ch; i _ i+1 ENDLOOP; st.length _ i; i _ 0; -- skip over comments UNTIL i=st.length OR st[i]=Space OR st[i]=Tab DO i _ i+1 ENDLOOP; st.length _ i END ELSE IODefs.ReadID[st] END; InOctal: PROCEDURE RETURNS [c: CARDINAL] = BEGIN i: CARDINAL _ 0; st: STRING _ [7]; ch: CHARACTER; IF commandFile THEN BEGIN UNTIL (ch _ LOOPHOLE[isCommand.get[isCommand]])=CR OR ch=Space DO IODefs.WriteChar[ch]; st[i] _ ch; i _ i+1 ENDLOOP; st.length _ i; c _ String.StringToOctal[st] END ELSE c _ IODefs.ReadOctal[] END; InBoolean: PROCEDURE RETURNS [b: BOOLEAN] = BEGIN b _ String.LowerCase[InChar[]]='y; OutLine[IF b THEN "Yes" ELSE "No"] END; InFileName: PROCEDURE [name, ext: STRING] = BEGIN i: CARDINAL; InString[name]; OutLine[""]; FOR i DECREASING IN [0..name.length) DO IF name[i]='. THEN RETURN ENDLOOP; String.AppendString[to: name, from: ext] END; OutString: PROCEDURE [st:STRING] = BEGIN IF TRUE OR NOT commandFile THEN IODefs.WriteString[st] END; OutLine: PROCEDURE [st:STRING] = BEGIN IF TRUE OR NOT commandFile THEN IODefs.WriteLine[st] END; WriteCR: PROCEDURE = BEGIN IODefs.WriteChar[IODefs.CR] END; Resynch: PROCEDURE = BEGIN IODefs.WriteString["Type any key to exit."]; [] _ IODefs.ReadChar[] END; GetToken: PROCEDURE [stream: StreamDefs.DiskHandle, string: STRING] = BEGIN c: CHARACTER; string.length _ 0; DO IF (c_stream.get[stream ! StreamDefs.StreamError => EXIT]) <= Space THEN BEGIN IF string.length # 0 THEN EXIT END ELSE String.AppendChar[string,c]; ENDLOOP; END; TestForMakeImage: PROCEDURE = -- makes image if command line says "/i" -- returns with isCommand set up to read commands BEGIN OPEN SegmentDefs, StreamDefs; cfa: POINTER TO AltoFileDefs.CFA _ MiscDefs.CommandLineCFA[]; cfile: FileHandle _ InsertFile[@cfa.fp,Read]; s: STRING _ [40]; isCommand _ NIL; isCommand _ CreateByteStream[cfile,Read ! InvalidFP => CONTINUE]; IF isCommand # NIL THEN BEGIN JumpToFA[isCommand,@cfa.fa]; WHILE isCommand.get[isCommand ! StreamError => GOTO nocommands] <= Space DO NULL ENDLOOP; SetIndex[isCommand,ModifyIndex[GetIndex[isCommand],-1]]; EXITS nocommands => BEGIN JumpToFA[isCommand,@cfa.fa]; RETURN END; END; GetToken[isCommand, s]; IF String.EquivalentString[s, "/i"L] THEN BEGIN isCommand.destroy[isCommand]; ImageDefs.MakeImage["MakeLoaderFile.image"L]; isCommand _ NewByteStream["Com.Cm"L, Read]; GetToken[isCommand, s]; -- skip image file name END ELSE BEGIN JumpToFA[isCommand,@cfa.fa]; END; END; -- Main code BEGIN format: Format _ ether; appendToCurrentFile: BOOLEAN _ FALSE; str: STRING _ [40]; TestForMakeImage[]; -- sets up isCommand IODefs.WriteLine["MakeLoaderFile of 21-Jun-82 15:51:55"]; -- Check for command file specified in Com.cm GetToken[isCommand, str]; isCommand.destroy[isCommand]; IF str.length#0 THEN BEGIN i: CARDINAL; FOR i IN [0..str.length) DO IF str[i] = '. THEN EXIT; REPEAT FINISHED => String.AppendString[str, ".mlf"]; ENDLOOP; isCommand _ StreamDefs.NewByteStream[name: str, access: SegmentDefs.Read ! SegmentDefs.FileNameError => GOTO nofile]; commandFile _ TRUE; IODefs.WriteString["Commands from "]; IODefs.WriteLine[str] EXITS nofile => NULL END; -- Allocate space for IMem IMem _ Storage.Node[SIZE[IMemCell]*(LAST[IMemRange]+1)]; ClearIMem[]; DO OutString["Command: "]; SELECT String.LowerCase[InChar[]] FROM '* => BEGIN tstr.length _ tstrLength; IODefs.WriteLine[tstr]; END; -- Comment 'r => BEGIN OutString["Read file: "]; InFileName[infile, ".mb"]; is _ StreamDefs.NewWordStream[name: infile, access: SegmentDefs.Read ! SegmentDefs.FileNameError => GOTO nofile]; LoadIMem[]; is.destroy[is] EXITS nofile => BEGIN IODefs.WriteString["Can't find "]; IODefs.WriteLine[infile]; Resynch[]; EXIT; END; END; 'p => BEGIN IODefs.WriteLine["Pad to the end of this page (for making initial boot files)"]; PutPadding[]; END; 's => BEGIN IODefs.WriteLine["(Old fashioned) Soft boot format"]; format _ soft END; 'w => BEGIN startLoc: CARDINAL; OutString["Write"]; IF NOT appendToCurrentFile THEN BEGIN trailer: STRING; SELECT format FROM soft => trailer _ ".sb"; ether => trailer _ ".eb"; ENDCASE => ERROR; OutString[" onto file: "]; InFileName[outfile, trailer]; os _ StreamDefs.NewWordStream[name: outfile, access: SegmentDefs.Write+SegmentDefs.Append]; PutHeader[format, outfile] END ELSE OutLine[""]; OutString["Starting location (octal) : "]; startLoc _ InOctal[]; OutLine[" "]; OutLine["**If you want overlays, answer No to next question**"]; OutString["Close output file afterwords? "]; appendToCurrentFile _ ~InBoolean[]; DumpIMem[format, startLoc]; IODefs.WriteString[outfile]; IODefs.WriteString[" contains "]; IODefs.WriteDecimal[(wordsWritten+255)/256]; IODefs.WriteLine[" pages"]; IF ~appendToCurrentFile THEN BEGIN PutTrailer[format]; os.destroy[os]; wordsWritten _ 0 END; OutString["Clear Control store? "]; IF InBoolean[] THEN ClearIMem[] END; 'q => BEGIN IODefs.WriteLine["Quit"]; IF appendToCurrentFile THEN BEGIN IODefs.WriteLine["Error - last write didn't close output file"]; Resynch[]; END; EXIT END; ENDCASE => OutLine["Commands are: Read, Soft format, Pad to end of page, Write, Quit"] ENDLOOP END; IF commandFile THEN isCommand.destroy[isCommand]; ImageDefs.StopMesa[]; END. LOG Time: June 13, 1978 By: Hankins Action: Created file Time: October 25, 1978 By: Hankins Action: ??? Time: January, 1978 By: Hankins Action: ??? Time: March 12, 1979 4:21 PM By: McJones Action: Converted to Mesa 5; added soft booting Time: May 2, 1979 5:07 PM By: Johnsson Action: run from bcd Time: May 23, 1979 5:19 PM By: Johnsson Action: Breakpoints command Time: June 4, 1979 9:42 AM By: Johnsson Action: fix BP vs. CheckSum Time: December 30, 1979 4:43 PM By: Murray Action: ether format Time: Aug 80 By: Murray Action: add padding option Time: September 23, 1980 7:30 PM By: Murray Action: cleanup+bugfixes Time: September 23, 1980 7:30 PM By: Murray Action: Fixup BoundsCheck glitch in checksum, delete Clear and version commands 21-Jun-82 15:50:49 Taft Add filename to header 7-Jul-82 15:38:42 Taft Make embedded date be same as file creation date January 9, 1984 4:45 PM Taft Add PutBlock; fix bug in padding computation for header