1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-07-30 15:29:01 +00:00

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.
This commit is contained in:
Andy McFadden 2020-06-28 11:56:51 -07:00
parent fa500a2a49
commit 0702882c8c
5 changed files with 220 additions and 14 deletions

View File

@ -92,6 +92,9 @@ namespace SourceGen.Tools.Omf {
OmfFileKind = FileKind.Unknown; OmfFileKind = FileKind.Unknown;
} }
/// <summary>
/// Analyzes the contents of an OMF file.
/// </summary>
public void Analyze(Formatter formatter) { public void Analyze(Formatter formatter) {
OmfSegment.ParseResult result = DoAnalyze(formatter, false); OmfSegment.ParseResult result = DoAnalyze(formatter, false);
if (result == OmfSegment.ParseResult.IsLibrary || if (result == OmfSegment.ParseResult.IsLibrary ||
@ -104,8 +107,14 @@ namespace SourceGen.Tools.Omf {
MessageList = firstFail; MessageList = firstFail;
} }
} }
OmfFileKind = DetermineFileKind();
Debug.WriteLine("File kind is " + OmfFileKind);
} }
/// <summary>
/// Analyzes the contents of an OMF file as a library or non-library.
/// </summary>
private OmfSegment.ParseResult DoAnalyze(Formatter formatter, bool parseAsLibrary) { private OmfSegment.ParseResult DoAnalyze(Formatter formatter, bool parseAsLibrary) {
bool first = true; bool first = true;
int offset = 0; int offset = 0;
@ -156,5 +165,85 @@ namespace SourceGen.Tools.Omf {
Debug.WriteLine("Num segments = " + mSegmentList.Count); Debug.WriteLine("Num segments = " + mSegmentList.Count);
return OmfSegment.ParseResult.Success; return OmfSegment.ParseResult.Success;
} }
/// <summary>
/// Analyzes the file to determine the file kind.
/// </summary>
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;
}
} }
} }

View File

@ -91,8 +91,14 @@ namespace SourceGen.Tools.Omf {
/// </summary> /// </summary>
public List<NameValueNote> RawValues = new List<NameValueNote>(); public List<NameValueNote> RawValues = new List<NameValueNote>();
/// <summary>
/// All known OMF versions.
/// </summary>
public enum SegmentVersion { v0_0, v1_0, v2_0, v2_1 } public enum SegmentVersion { v0_0, v1_0, v2_0, v2_1 }
/// <summary>
/// All known segment kinds.
/// </summary>
public enum SegmentKind { public enum SegmentKind {
Code = 0x00, Code = 0x00,
Data = 0x01, Data = 0x01,
@ -155,6 +161,9 @@ namespace SourceGen.Tools.Omf {
// "The BANKSIZE and align restrictions are enforced by the linker, and violations // "The BANKSIZE and align restrictions are enforced by the linker, and violations
// of them are unlikely in a load file." // of them are unlikely in a load file."
/// <summary>
/// Record list, from body of segment.
/// </summary>
public List<OmfRecord> Records = new List<OmfRecord>(); public List<OmfRecord> Records = new List<OmfRecord>();
@ -231,11 +240,12 @@ namespace SourceGen.Tools.Omf {
newSeg.LabLen = data[offset + 0x0d]; newSeg.LabLen = data[offset + 0x0d];
int numLen = data[offset + 0x0e]; int numLen = data[offset + 0x0e];
newSeg.BankSize = RawData.GetWord(data, offset + 0x10, 4, false); newSeg.BankSize = RawData.GetWord(data, offset + 0x10, 4, false);
newSeg.Org = RawData.GetWord(data, offset + 0x18, 4, false); int numSex, dispName;
newSeg.Align = RawData.GetWord(data, offset + 0x1c, 4, false);
int numSex = data[offset + 0x20];
int dispName;
if (newSeg.Version == SegmentVersion.v0_0) { 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; dispName = 0x24;
if (newSeg.LabLen == 0) { if (newSeg.LabLen == 0) {
newSeg.DispData = dispName + data[offset + dispName]; newSeg.DispData = dispName + data[offset + dispName];
@ -243,7 +253,11 @@ namespace SourceGen.Tools.Omf {
newSeg.DispData = dispName + LOAD_NAME_LEN; newSeg.DispData = dispName + LOAD_NAME_LEN;
} }
} else { } 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.SegNum = RawData.GetWord(data, offset + 0x22, 2, false);
newSeg.Entry = RawData.GetWord(data, offset + 0x24, 4, false); newSeg.Entry = RawData.GetWord(data, offset + 0x24, 4, false);
dispName = RawData.GetWord(data, offset + 0x28, 2, 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); 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; int kindByte, kindWord;
if (newSeg.Version <= SegmentVersion.v1_0) { if (newSeg.Version <= SegmentVersion.v1_0) {
kindByte = data[offset + 0x0c]; kindByte = data[offset + 0x0c];
@ -457,8 +473,9 @@ namespace SourceGen.Tools.Omf {
newSeg.AddRaw("undefined", RawData.GetWord(data, offset + 0x14, 4, false), 4, newSeg.AddRaw("undefined", RawData.GetWord(data, offset + 0x14, 4, false), 4,
string.Empty); string.Empty);
} }
newSeg.AddRaw("ORG", newSeg.Org, 4, string.Empty); newSeg.AddRaw("ORG", newSeg.Org, 4, (newSeg.Org != 0 ? "" : "relocatable"));
newSeg.AddRaw("ALIGN", newSeg.Align, 4, string.Empty); newSeg.AddRaw("ALIGN", newSeg.Align, 4,
"expecting 0, $0100, or $010000");
newSeg.AddRaw("NUMSEX", numSex, 1, "must be 0"); newSeg.AddRaw("NUMSEX", numSex, 1, "must be 0");
if (newSeg.Version == SegmentVersion.v1_0) { if (newSeg.Version == SegmentVersion.v1_0) {
newSeg.AddRaw("LCBANK", newSeg.LcBank, 1, string.Empty); newSeg.AddRaw("LCBANK", newSeg.LcBank, 1, string.Empty);
@ -497,8 +514,9 @@ namespace SourceGen.Tools.Omf {
} }
if (omfRec.Op == OmfRecord.Opcode.END) { if (omfRec.Op == OmfRecord.Opcode.END) {
// v0/v1 pad to 512-byte block boundaries, so this is expected there, but v2.x // v0/v1 pad to 512-byte block boundaries, so some slop is expected there,
// should be snug. Doesn't have to be, but might indicate a parsing error. // 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); int remaining = (FileOffset + FileLength) - (offset + omfRec.Length);
Debug.Assert(remaining >= 0); Debug.Assert(remaining >= 0);
Debug.WriteLine("END record found, remaining space=" + remaining); Debug.WriteLine("END record found, remaining space=" + remaining);
@ -513,6 +531,55 @@ namespace SourceGen.Tools.Omf {
} }
} }
/// <summary>
/// Tests to see whether the record collection is congruent with a Load file.
/// </summary>
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;
}
/// <summary>
/// Tests to see whether the record collection is congruent with an Object or Library file.
/// </summary>
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. // Helper functions.
// //

View File

@ -48,7 +48,7 @@ limitations under the License.
<TextBlock Grid.Row="0" Text="{Binding FileOffsetLenStr, FallbackValue=File Offset / Length}" Margin="0,8,0,0"/> <TextBlock Grid.Row="0" Text="{Binding FileOffsetLenStr, FallbackValue=File Offset / Length}" Margin="0,8,0,0"/>
<TextBlock Grid.Row="1" Text="Header fields:" Margin="0,8,0,0"/> <TextBlock Grid.Row="1" Text="Header fields:" Margin="0,8,0,0"/>
<DataGrid Name="headerList" Grid.Row="2" Height="130" Margin="0,8,0,0" <DataGrid Name="headerList" Grid.Row="2" Height="145" Margin="0,8,0,0"
IsReadOnly="True" IsReadOnly="True"
ItemsSource="{Binding HeaderItems}" ItemsSource="{Binding HeaderItems}"
FontFamily="{StaticResource GeneralMonoFont}" FontFamily="{StaticResource GeneralMonoFont}"
@ -75,7 +75,7 @@ limitations under the License.
</DataGrid> </DataGrid>
<TextBlock Grid.Row="3" Text="{Binding RecordHeaderStr, FallbackValue=Records (123):}" Margin="0,8,0,0"/> <TextBlock Grid.Row="3" Text="{Binding RecordHeaderStr, FallbackValue=Records (123):}" Margin="0,8,0,0"/>
<DataGrid Name="recordList" Grid.Row="4" Height="130" Margin="0,8,0,0" <DataGrid Name="recordList" Grid.Row="4" Height="145" Margin="0,8,0,0"
IsReadOnly="True" IsReadOnly="True"
ItemsSource="{Binding RecordItems}" ItemsSource="{Binding RecordItems}"
FontFamily="{StaticResource GeneralMonoFont}" FontFamily="{StaticResource GeneralMonoFont}"
@ -102,7 +102,7 @@ limitations under the License.
</DataGrid> </DataGrid>
<TextBlock Grid.Row="5" Text="Relocation table (for Load file segment):" Margin="0,8,0,0"/> <TextBlock Grid.Row="5" Text="Relocation table (for Load file segment):" Margin="0,8,0,0"/>
<DataGrid Name="relocList" Grid.Row="6" Height="130" Margin="0,8,0,0" <DataGrid Name="relocList" Grid.Row="6" Height="145" Margin="0,8,0,0"
IsReadOnly="True" IsReadOnly="True"
ItemsSource="{Binding RelocItems}" ItemsSource="{Binding RelocItems}"
FontFamily="{StaticResource GeneralMonoFont}" FontFamily="{StaticResource GeneralMonoFont}"

View File

@ -20,6 +20,7 @@ limitations under the License.
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SourceGen.Tools.Omf.WpfGui" xmlns:local="clr-namespace:SourceGen.Tools.Omf.WpfGui"
xmlns:system="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" mc:Ignorable="d"
Title="OMF File Viewer" Title="OMF File Viewer"
SizeToContent="Height" Width="600" ResizeMode="NoResize" SizeToContent="Height" Width="600" ResizeMode="NoResize"
@ -27,6 +28,16 @@ limitations under the License.
<Window.Resources> <Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/> <BooleanToVisibilityConverter x:Key="BoolToVis"/>
<system:String x:Key="str_OmfFileUnknownStr">a ??? file</system:String>
<system:String x:Key="str_OmfFileLoadStr">a Load file</system:String>
<system:String x:Key="str_OmfFileObjectStr">an Object file</system:String>
<system:String x:Key="str_OmfFileLibraryStr">a Library file</system:String>
<system:String x:Key="str_OmfFileIndeterminateStr">a file of indeterminate type</system:String>
<system:String x:Key="str_OmfFileSummaryFmt">This is {0}, with {1} segment</system:String>
<system:String x:Key="str_OmfFileSummaryPlFmt">This is {0}, with {1} segments</system:String>
<system:String x:Key="str_OmfFileNot">This is not an Apple II OMF file</system:String>
</Window.Resources> </Window.Resources>
<Grid Margin="8"> <Grid Margin="8">
@ -44,7 +55,7 @@ limitations under the License.
<TextBox DockPanel.Dock="Left" IsReadOnly="True" Margin="8,0,0,0" Text="{Binding PathName}"/> <TextBox DockPanel.Dock="Left" IsReadOnly="True" Margin="8,0,0,0" Text="{Binding PathName}"/>
</DockPanel> </DockPanel>
<TextBlock Grid.Row="1" Text="File is a {something}, with {N} segments" Margin="0,8,0,0"/> <TextBlock Grid.Row="1" Text="{Binding FileSummaryStr, FallbackValue=This be blah with N segments}" Margin="0,8,0,0"/>
<DataGrid Name="segmentList" Grid.Row="2" Height="210" Margin="0,8,0,0" <DataGrid Name="segmentList" Grid.Row="2" Height="210" Margin="0,8,0,0"
IsReadOnly="True" IsReadOnly="True"

View File

@ -82,6 +82,12 @@ namespace SourceGen.Tools.Omf.WpfGui {
set { mPathName = value; OnPropertyChanged(); } set { mPathName = value; OnPropertyChanged(); }
} }
private string mFileSummaryStr;
public string FileSummaryStr {
get { return mFileSummaryStr; }
set { mFileSummaryStr = value; OnPropertyChanged(); }
}
private string mMessageStrings; private string mMessageStrings;
public string MessageStrings { public string MessageStrings {
get { return mMessageStrings; } get { return mMessageStrings; }
@ -107,6 +113,39 @@ namespace SourceGen.Tools.Omf.WpfGui {
mOmfFile = new OmfFile(data); mOmfFile = new OmfFile(data);
mOmfFile.Analyze(mFormatter); mOmfFile.Analyze(mFormatter);
string summary;
if (mOmfFile.OmfFileKind == OmfFile.FileKind.Foreign) {
summary = (string)FindResource("str_OmfFileNot");
} else {
string fileStr;
switch (mOmfFile.OmfFileKind) {
case OmfFile.FileKind.Indeterminate:
fileStr = (string)FindResource("str_OmfFileIndeterminateStr");
break;
case OmfFile.FileKind.Load:
fileStr = (string)FindResource("str_OmfFileLoadStr");
break;
case OmfFile.FileKind.Object:
fileStr = (string)FindResource("str_OmfFileObjectStr");
break;
case OmfFile.FileKind.Library:
fileStr = (string)FindResource("str_OmfFileLibraryStr");
break;
default:
fileStr = (string)FindResource("str_OmfFileUnknownStr");
break;
}
string fmt;
if (mOmfFile.SegmentList.Count == 1) {
fmt = (string)FindResource("str_OmfFileSummaryFmt");
} else {
fmt = (string)FindResource("str_OmfFileSummaryPlFmt");
}
summary = string.Format(fmt, fileStr, mOmfFile.SegmentList.Count);
}
FileSummaryStr = summary;
foreach (OmfSegment omfSeg in mOmfFile.SegmentList) { foreach (OmfSegment omfSeg in mOmfFile.SegmentList) {
SegmentListItems.Add(new SegmentListItem(omfSeg)); SegmentListItems.Add(new SegmentListItem(omfSeg));
} }