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