1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-06-27 18:29:30 +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;
}
/// <summary>
/// Analyzes the contents of an OMF file.
/// </summary>
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);
}
/// <summary>
/// Analyzes the contents of an OMF file as a library or non-library.
/// </summary>
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;
}
/// <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>
public List<NameValueNote> RawValues = new List<NameValueNote>();
/// <summary>
/// All known OMF versions.
/// </summary>
public enum SegmentVersion { v0_0, v1_0, v2_0, v2_1 }
/// <summary>
/// All known segment kinds.
/// </summary>
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."
/// <summary>
/// Record list, from body of segment.
/// </summary>
public List<OmfRecord> Records = new List<OmfRecord>();
@ -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 {
}
}
/// <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.
//

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="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"
ItemsSource="{Binding HeaderItems}"
FontFamily="{StaticResource GeneralMonoFont}"
@ -75,7 +75,7 @@ limitations under the License.
</DataGrid>
<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"
ItemsSource="{Binding RecordItems}"
FontFamily="{StaticResource GeneralMonoFont}"
@ -102,7 +102,7 @@ limitations under the License.
</DataGrid>
<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"
ItemsSource="{Binding RelocItems}"
FontFamily="{StaticResource GeneralMonoFont}"

View File

@ -20,6 +20,7 @@ limitations under the License.
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SourceGen.Tools.Omf.WpfGui"
xmlns:system="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="OMF File Viewer"
SizeToContent="Height" Width="600" ResizeMode="NoResize"
@ -27,6 +28,16 @@ limitations under the License.
<Window.Resources>
<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>
<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}"/>
</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"
IsReadOnly="True"

View File

@ -82,6 +82,12 @@ namespace SourceGen.Tools.Omf.WpfGui {
set { mPathName = value; OnPropertyChanged(); }
}
private string mFileSummaryStr;
public string FileSummaryStr {
get { return mFileSummaryStr; }
set { mFileSummaryStr = value; OnPropertyChanged(); }
}
private string mMessageStrings;
public string MessageStrings {
get { return mMessageStrings; }
@ -107,6 +113,39 @@ namespace SourceGen.Tools.Omf.WpfGui {
mOmfFile = new OmfFile(data);
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) {
SegmentListItems.Add(new SegmentListItem(omfSeg));
}