From 0702882c8ca27d61c745679332726d6623ff6f71 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Sun, 28 Jun 2020 11:56:51 -0700 Subject: [PATCH] Progress toward OMF file handling Added file type determination (Load, Object, Library). Requires screening the segment and record types. Also, fix parsing of v0 headers, which placed ORG and ALIGN in different places. --- SourceGen/Tools/Omf/OmfFile.cs | 89 +++++++++++++++++++ SourceGen/Tools/Omf/OmfSegment.cs | 87 +++++++++++++++--- .../Tools/Omf/WpfGui/OmfSegmentViewer.xaml | 6 +- SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml | 13 ++- SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs | 39 ++++++++ 5 files changed, 220 insertions(+), 14 deletions(-) diff --git a/SourceGen/Tools/Omf/OmfFile.cs b/SourceGen/Tools/Omf/OmfFile.cs index 2c5ff34..a439ddc 100644 --- a/SourceGen/Tools/Omf/OmfFile.cs +++ b/SourceGen/Tools/Omf/OmfFile.cs @@ -92,6 +92,9 @@ namespace SourceGen.Tools.Omf { OmfFileKind = FileKind.Unknown; } + /// + /// Analyzes the contents of an OMF file. + /// public void Analyze(Formatter formatter) { OmfSegment.ParseResult result = DoAnalyze(formatter, false); if (result == OmfSegment.ParseResult.IsLibrary || @@ -104,8 +107,14 @@ namespace SourceGen.Tools.Omf { MessageList = firstFail; } } + + OmfFileKind = DetermineFileKind(); + Debug.WriteLine("File kind is " + OmfFileKind); } + /// + /// Analyzes the contents of an OMF file as a library or non-library. + /// private OmfSegment.ParseResult DoAnalyze(Formatter formatter, bool parseAsLibrary) { bool first = true; int offset = 0; @@ -156,5 +165,85 @@ namespace SourceGen.Tools.Omf { Debug.WriteLine("Num segments = " + mSegmentList.Count); return OmfSegment.ParseResult.Success; } + + /// + /// Analyzes the file to determine the file kind. + /// + private FileKind DetermineFileKind() { + if (mSegmentList.Count == 0) { + // Couldn't find a single valid segment, this is not OMF. + return FileKind.Foreign; + } + + // The rules: + // * Load files may contain any kind of segment except LibraryDict. Their + // segments must be LCONST/DS followed by relocation records + // (INTERSEG, cINTERSEG, RELOC, cRELOC, SUPER). + // * Object files have Code and Data segments, and may not contain relocation + // records or ENTRY. + // * Library files are like Object files, but start with a LibraryDict segment. + // * Run-Time Library files have an initial segment with ENTRY records. (These + // are not supported because I've never actually seen one... the few files I've + // found with the RTL filetype ($B4) did not have any ENTRY records.) + + bool maybeLoad = true; + bool maybeObject = true; + bool maybeLibrary = true; + + foreach (OmfSegment omfSeg in mSegmentList) { + switch (omfSeg.Kind) { + case OmfSegment.SegmentKind.Code: + case OmfSegment.SegmentKind.Data: + // Allowed anywhere. + break; + case OmfSegment.SegmentKind.JumpTable: + case OmfSegment.SegmentKind.PathName: + case OmfSegment.SegmentKind.Init: + case OmfSegment.SegmentKind.AbsoluteBank: + case OmfSegment.SegmentKind.DpStack: + maybeObject = maybeLibrary = false; + break; + case OmfSegment.SegmentKind.LibraryDict: + maybeLoad = false; + maybeObject = false; + break; + default: + Debug.Assert(false, "Unexpected segment kind " + omfSeg.Kind); + break; + } + } + + // + // Initial screening complete. Dig into the segment records to see if they're + // compatible. + // + + if (maybeLoad) { + foreach (OmfSegment omfSeg in mSegmentList) { + if (!omfSeg.CheckRecords_LoadFile()) { + maybeLoad = false; + break; + } + } + if (maybeLoad) { + return FileKind.Load; + } + } + if (maybeLibrary || maybeObject) { + foreach (OmfSegment omfSeg in mSegmentList) { + if (!omfSeg.CheckRecords_ObjectOrLib()) { + maybeLibrary = maybeObject = false; + break; + } + } + if (maybeObject) { + return FileKind.Object; + } else { + return FileKind.Library; + } + } + + return FileKind.Indeterminate; + } } } diff --git a/SourceGen/Tools/Omf/OmfSegment.cs b/SourceGen/Tools/Omf/OmfSegment.cs index aa696ec..aa51225 100644 --- a/SourceGen/Tools/Omf/OmfSegment.cs +++ b/SourceGen/Tools/Omf/OmfSegment.cs @@ -91,8 +91,14 @@ namespace SourceGen.Tools.Omf { /// public List RawValues = new List(); + /// + /// All known OMF versions. + /// public enum SegmentVersion { v0_0, v1_0, v2_0, v2_1 } + /// + /// All known segment kinds. + /// public enum SegmentKind { Code = 0x00, Data = 0x01, @@ -155,6 +161,9 @@ namespace SourceGen.Tools.Omf { // "The BANKSIZE and align restrictions are enforced by the linker, and violations // of them are unlikely in a load file." + /// + /// Record list, from body of segment. + /// public List Records = new List(); @@ -231,11 +240,12 @@ namespace SourceGen.Tools.Omf { newSeg.LabLen = data[offset + 0x0d]; int numLen = data[offset + 0x0e]; newSeg.BankSize = RawData.GetWord(data, offset + 0x10, 4, false); - newSeg.Org = RawData.GetWord(data, offset + 0x18, 4, false); - newSeg.Align = RawData.GetWord(data, offset + 0x1c, 4, false); - int numSex = data[offset + 0x20]; - int dispName; + int numSex, dispName; if (newSeg.Version == SegmentVersion.v0_0) { + newSeg.Org = RawData.GetWord(data, offset + 0x14, 4, false); + newSeg.Align = RawData.GetWord(data, offset + 0x18, 4, false); + numSex = data[offset + 0x1c]; + // 7 unused bytes follow dispName = 0x24; if (newSeg.LabLen == 0) { newSeg.DispData = dispName + data[offset + dispName]; @@ -243,7 +253,11 @@ namespace SourceGen.Tools.Omf { newSeg.DispData = dispName + LOAD_NAME_LEN; } } else { - newSeg.LcBank = data[offset + 0x21]; + newSeg.BankSize = RawData.GetWord(data, offset + 0x10, 4, false); + newSeg.Org = RawData.GetWord(data, offset + 0x18, 4, false); + newSeg.Align = RawData.GetWord(data, offset + 0x1c, 4, false); + numSex = data[offset + 0x20]; + newSeg.LcBank = data[offset + 0x21]; // v1.0 only newSeg.SegNum = RawData.GetWord(data, offset + 0x22, 2, false); newSeg.Entry = RawData.GetWord(data, offset + 0x24, 4, false); dispName = RawData.GetWord(data, offset + 0x28, 2, false); @@ -263,7 +277,9 @@ namespace SourceGen.Tools.Omf { newSeg.TempOrg = RawData.GetWord(data, offset + 0x2c, 4, false); } - // Extract Kind and its attributes. + // Extract Kind and its attributes. The Orca/M 2.0 manual refers to the 1-byte + // field in v0/v1 as "TYPE" and the 2-byte field as "KIND", but we're generally + // following the GS/OS reference nomenclature. int kindByte, kindWord; if (newSeg.Version <= SegmentVersion.v1_0) { kindByte = data[offset + 0x0c]; @@ -457,8 +473,9 @@ namespace SourceGen.Tools.Omf { newSeg.AddRaw("undefined", RawData.GetWord(data, offset + 0x14, 4, false), 4, string.Empty); } - newSeg.AddRaw("ORG", newSeg.Org, 4, string.Empty); - newSeg.AddRaw("ALIGN", newSeg.Align, 4, string.Empty); + newSeg.AddRaw("ORG", newSeg.Org, 4, (newSeg.Org != 0 ? "" : "relocatable")); + newSeg.AddRaw("ALIGN", newSeg.Align, 4, + "expecting 0, $0100, or $010000"); newSeg.AddRaw("NUMSEX", numSex, 1, "must be 0"); if (newSeg.Version == SegmentVersion.v1_0) { newSeg.AddRaw("LCBANK", newSeg.LcBank, 1, string.Empty); @@ -497,8 +514,9 @@ namespace SourceGen.Tools.Omf { } if (omfRec.Op == OmfRecord.Opcode.END) { - // v0/v1 pad to 512-byte block boundaries, so this is expected there, but v2.x - // should be snug. Doesn't have to be, but might indicate a parsing error. + // v0/v1 pad to 512-byte block boundaries, so some slop is expected there, + // but v2.x should be snug. Doesn't have to be, but might indicate a + // bug in the parser. int remaining = (FileOffset + FileLength) - (offset + omfRec.Length); Debug.Assert(remaining >= 0); Debug.WriteLine("END record found, remaining space=" + remaining); @@ -513,6 +531,55 @@ namespace SourceGen.Tools.Omf { } } + /// + /// Tests to see whether the record collection is congruent with a Load file. + /// + public bool CheckRecords_LoadFile() { + bool constSection = true; + foreach (OmfRecord omfRec in Records) { + switch (omfRec.Op) { + case OmfRecord.Opcode.LCONST: + case OmfRecord.Opcode.DS: + if (!constSection) { + Debug.WriteLine("Found LCONST/DS past const section"); + return false; + } + break; + case OmfRecord.Opcode.RELOC: + case OmfRecord.Opcode.cRELOC: + case OmfRecord.Opcode.INTERSEG: + case OmfRecord.Opcode.cINTERSEG: + case OmfRecord.Opcode.SUPER: + constSection = false; + break; + default: + // incompatible record + return false; + } + } + return true; + } + + /// + /// Tests to see whether the record collection is congruent with an Object or Library file. + /// + public bool CheckRecords_ObjectOrLib() { + foreach (OmfRecord omfRec in Records) { + switch (omfRec.Op) { + case OmfRecord.Opcode.RELOC: + case OmfRecord.Opcode.cRELOC: + case OmfRecord.Opcode.INTERSEG: + case OmfRecord.Opcode.cINTERSEG: + case OmfRecord.Opcode.SUPER: + case OmfRecord.Opcode.ENTRY: + return false; + default: + break; + } + } + return true; + } + // // Helper functions. // diff --git a/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml b/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml index ee59e74..9dcc0d7 100644 --- a/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml +++ b/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml @@ -48,7 +48,7 @@ limitations under the License. - - - + + a ??? file + a Load file + an Object file + a Library file + a file of indeterminate type + + This is {0}, with {1} segment + This is {0}, with {1} segments + This is not an Apple II OMF file @@ -44,7 +55,7 @@ limitations under the License. - +