From 463a0cc5616e1ebf28ce945a72bd559bdc552a18 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Sun, 28 Jun 2020 20:50:41 -0700 Subject: [PATCH] Progress toward OMF file handling Added generation of the relocation dictionary and constant body for segments in Load files. Also, don't reject files with v1 segments (whose length is specified as a block count) just because the EOF isn't a multiple of 512 bytes. Some executables don't pad out the last block. Various tweaks to output formatting. --- SourceGen/SourceGen.csproj | 1 + SourceGen/Tools/Omf/OmfFile.cs | 56 +++- SourceGen/Tools/Omf/OmfRecord.cs | 19 +- SourceGen/Tools/Omf/OmfReloc.cs | 266 ++++++++++++++++++ SourceGen/Tools/Omf/OmfSegment.cs | 124 ++++++-- .../Tools/Omf/WpfGui/OmfSegmentViewer.xaml | 15 +- .../Tools/Omf/WpfGui/OmfSegmentViewer.xaml.cs | 46 ++- SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs | 5 +- 8 files changed, 485 insertions(+), 47 deletions(-) create mode 100644 SourceGen/Tools/Omf/OmfReloc.cs diff --git a/SourceGen/SourceGen.csproj b/SourceGen/SourceGen.csproj index adf408f..0d668b6 100644 --- a/SourceGen/SourceGen.csproj +++ b/SourceGen/SourceGen.csproj @@ -87,6 +87,7 @@ + OmfSegmentViewer.xaml diff --git a/SourceGen/Tools/Omf/OmfFile.cs b/SourceGen/Tools/Omf/OmfFile.cs index a439ddc..afe3b0e 100644 --- a/SourceGen/Tools/Omf/OmfFile.cs +++ b/SourceGen/Tools/Omf/OmfFile.cs @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -using Asm65; using System; using System.Collections.Generic; using System.Diagnostics; +using Asm65; + namespace SourceGen.Tools.Omf { /// /// Apple IIgs OMF file. @@ -70,14 +71,19 @@ namespace SourceGen.Tools.Omf { } public FileKind OmfFileKind { get; private set; } + /// + /// File data. Files must be loaded completely into memory. + /// private byte[] mFileData; - private bool mIsDamaged; - private List mSegmentList = new List(); - public List SegmentList { - get { return mSegmentList; } - } + /// + /// List of segments. + /// + public List SegmentList { get; private set; } = new List(); + /// + /// List of strings to show in the message window. + /// public List MessageList { get; private set; } = new List(); @@ -110,6 +116,10 @@ namespace SourceGen.Tools.Omf { OmfFileKind = DetermineFileKind(); Debug.WriteLine("File kind is " + OmfFileKind); + + if (OmfFileKind == FileKind.Load) { + GenerateRelocDicts(); + } } /// @@ -156,13 +166,13 @@ namespace SourceGen.Tools.Omf { Debug.Assert(seg.FileLength > 0); - mSegmentList.Add(seg); + SegmentList.Add(seg); offset += seg.FileLength; len -= seg.FileLength; Debug.Assert(len >= 0); } - Debug.WriteLine("Num segments = " + mSegmentList.Count); + Debug.WriteLine("Num segments = " + SegmentList.Count); return OmfSegment.ParseResult.Success; } @@ -170,7 +180,7 @@ namespace SourceGen.Tools.Omf { /// Analyzes the file to determine the file kind. /// private FileKind DetermineFileKind() { - if (mSegmentList.Count == 0) { + if (SegmentList.Count == 0) { // Couldn't find a single valid segment, this is not OMF. return FileKind.Foreign; } @@ -179,8 +189,8 @@ namespace SourceGen.Tools.Omf { // * 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. + // * Object files have Code, Data, and DP/Stack 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 @@ -190,17 +200,18 @@ namespace SourceGen.Tools.Omf { bool maybeObject = true; bool maybeLibrary = true; - foreach (OmfSegment omfSeg in mSegmentList) { + foreach (OmfSegment omfSeg in SegmentList) { switch (omfSeg.Kind) { case OmfSegment.SegmentKind.Code: case OmfSegment.SegmentKind.Data: + case OmfSegment.SegmentKind.DpStack: // Allowed anywhere. break; case OmfSegment.SegmentKind.JumpTable: case OmfSegment.SegmentKind.PathName: case OmfSegment.SegmentKind.Init: case OmfSegment.SegmentKind.AbsoluteBank: - case OmfSegment.SegmentKind.DpStack: + // Only allowed in Load. maybeObject = maybeLibrary = false; break; case OmfSegment.SegmentKind.LibraryDict: @@ -219,7 +230,7 @@ namespace SourceGen.Tools.Omf { // if (maybeLoad) { - foreach (OmfSegment omfSeg in mSegmentList) { + foreach (OmfSegment omfSeg in SegmentList) { if (!omfSeg.CheckRecords_LoadFile()) { maybeLoad = false; break; @@ -230,7 +241,7 @@ namespace SourceGen.Tools.Omf { } } if (maybeLibrary || maybeObject) { - foreach (OmfSegment omfSeg in mSegmentList) { + foreach (OmfSegment omfSeg in SegmentList) { if (!omfSeg.CheckRecords_ObjectOrLib()) { maybeLibrary = maybeObject = false; break; @@ -245,5 +256,20 @@ namespace SourceGen.Tools.Omf { return FileKind.Indeterminate; } + + /// + /// Generates relocation dictionaries for all code/data/init segments. + /// + private void GenerateRelocDicts() { + Debug.Assert(OmfFileKind == FileKind.Load); + + foreach (OmfSegment omfSeg in SegmentList) { + if (omfSeg.Kind == OmfSegment.SegmentKind.Code || + omfSeg.Kind == OmfSegment.SegmentKind.Data || + omfSeg.Kind == OmfSegment.SegmentKind.Init) { + omfSeg.GenerateRelocDict(); + } + } + } } } diff --git a/SourceGen/Tools/Omf/OmfRecord.cs b/SourceGen/Tools/Omf/OmfRecord.cs index f66f98a..6d74495 100644 --- a/SourceGen/Tools/Omf/OmfRecord.cs +++ b/SourceGen/Tools/Omf/OmfRecord.cs @@ -16,8 +16,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Security.Cryptography; using System.Text; + using Asm65; using CommonUtil; @@ -28,6 +28,11 @@ namespace SourceGen.Tools.Omf { public class OmfRecord { private const int NUMLEN = 4; // defined by NUMLEN field in header; always 4 for IIgs + /// + /// Offset of record start within file. + /// + public int FileOffset { get; private set; } + /// /// Total length, in bytes, of this record. /// @@ -114,6 +119,7 @@ namespace SourceGen.Tools.Omf { OmfSegment.SegmentVersion version, int labLen, Formatter formatter, List msgs, out OmfRecord omfRec) { omfRec = new OmfRecord(); + omfRec.FileOffset = offset; try { return omfRec.DoParseRecord(data, offset, version, labLen, formatter, msgs); } catch (IndexOutOfRangeException ioore) { @@ -299,9 +305,16 @@ namespace SourceGen.Tools.Omf { break; case Opcode.SUPER: { int count = GetNum(data, ref offset, ref len); - len += count; + int type = data[offset]; + len += count; // count includes type byte Value = (count - 1) + " bytes, type=" + - formatter.FormatHexValue(data[offset + NUMLEN], 2); + formatter.FormatHexValue(type, 2); + + if (type > 37) { + OmfSegment.AddErrorMsg(msgs, offset, + "found SUPER record with bogus type=$" + type.ToString("x2")); + // the length field allows us to skip it, so keep going + } } break; case Opcode.General: diff --git a/SourceGen/Tools/Omf/OmfReloc.cs b/SourceGen/Tools/Omf/OmfReloc.cs new file mode 100644 index 0000000..112de28 --- /dev/null +++ b/SourceGen/Tools/Omf/OmfReloc.cs @@ -0,0 +1,266 @@ +/* + * Copyright 2020 faddenSoft + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using CommonUtil; + +namespace SourceGen.Tools.Omf { + /// + /// Apple IIgs OMF relocation entry. These are generated from OMF relocation records. + /// Note that SUPER records may generate multiple instances of these. + /// + public class OmfReloc { + /// + /// Number of bytes to be relocated. + /// + public int Width { get; private set; } + + /// + /// Number of bits to shift the result before storing. Positive values shift left, + /// negative values shift right (unsigned). + /// + public int Shift { get; private set; } + + /// + /// Offset within segment of bytes to rewrite. + /// + public int Offset { get; private set; } + + /// + /// Relative offset of the thing being referenced. The segment's address in memory + /// is added to this to yield the final value. + /// + public int RelOffset { get; private set; } + + /// + /// File number of target segment. Normally 1. Will be -1 for intra-segment relocs. + /// + public int FileNum { get; private set; } + + /// + /// Segment number of target segment. Will be -1 for intra-segment relocs. + /// + public int SegNum { get; private set; } + + /// + /// Set to the SUPER type, or -1 if this record was not from a SUPER. + /// + public int SuperType { get; private set; } + + + /// + /// Constructor for RELOC/cRELOC. + /// + /// Width in bytes of relocation. + /// Bit-shift amount. + /// Offset within segment of relocation. + /// Relative offset of relocation reference. + /// True if generated from SUPER. + private OmfReloc(int width, int shift, int offset, int relOffset, int superType) { + FileNum = SegNum = -1; + Width = width; + Shift = shift; + Offset = offset; + RelOffset = relOffset; + SuperType = superType; + } + + /// + /// Constructor for INTERSEG/cINTERSEG. + /// + /// Width in bytes of relocation. + /// Bit-shift amount. + /// Offset within segment of relocation. + /// Relative offset of relocation reference. + /// File number. + /// Segment number. + /// True if generated from SUPER. + private OmfReloc(int width, int shift, int offset, int relOffset, int fileNum, + int segNum, int superType) { + Width = width; + Shift = shift; + Offset = offset; + RelOffset = relOffset; + FileNum = fileNum; + SegNum = segNum; + SuperType = superType; + } + + /// + /// Adds one or more OmfReloc instances to the list to represent the provided record. + /// + /// Segment that contains the record. + /// Record to add. + /// File data. + /// List of relocations. New entries will be appended. + /// True on success. + public static bool GenerateRelocs(OmfSegment omfSeg, OmfRecord omfRec, byte[] data, + List relocs) { + try { + return DoGenerateRelocs(omfSeg, omfRec, data, relocs); + } catch (IndexOutOfRangeException ioore) { + Debug.WriteLine("Caught IOORE during reloc gen (" + omfRec.Op + "): " + + ioore.Message); + return false; + } + } + + /// + /// Adds one or more OmfReloc instances to the list to represent the provided record. + /// + private static bool DoGenerateRelocs(OmfSegment omfSeg, OmfRecord omfRec, byte[] data, + List relocs) { + int offset = omfRec.FileOffset; + Debug.Assert(data[offset] == (int)omfRec.Op); + switch (omfRec.Op) { + case OmfRecord.Opcode.RELOC: { + byte width = data[offset + 1]; + sbyte shift = (sbyte)data[offset + 2]; + int off = RawData.GetWord(data, offset + 3, 4, false); + int relOff = RawData.GetWord(data, offset + 7, 4, false); + relocs.Add(new OmfReloc(width, shift, off, relOff, -1)); + } + break; + case OmfRecord.Opcode.cRELOC: { + byte width = data[offset + 1]; + sbyte shift = (sbyte)data[offset + 2]; + int off = RawData.GetWord(data, offset + 3, 2, false); + int relOff = RawData.GetWord(data, offset + 5, 2, false); + relocs.Add(new OmfReloc(width, shift, off, relOff, -1)); + } + break; + case OmfRecord.Opcode.INTERSEG: { + byte width = data[offset + 1]; + sbyte shift = (sbyte)data[offset + 2]; + int off = RawData.GetWord(data, offset + 3, 4, false); + int fileNum = RawData.GetWord(data, offset + 7, 2, false); + int segNum = RawData.GetWord(data, offset + 9, 2, false); + int relOff = RawData.GetWord(data, offset + 11, 4, false); + relocs.Add(new OmfReloc(width, shift, off, relOff, fileNum, segNum, -1)); + } + break; + case OmfRecord.Opcode.cINTERSEG: { + byte width = data[offset + 1]; + sbyte shift = (sbyte)data[offset + 2]; + int off = RawData.GetWord(data, offset + 3, 2, false); + int fileNum = 1; + int segNum = data[offset + 5]; + int relOff = RawData.GetWord(data, offset + 6, 2, false); + relocs.Add(new OmfReloc(width, shift, off, relOff, fileNum, segNum, -1)); + } + break; + case OmfRecord.Opcode.SUPER: + if (!GenerateRelocForSuper(omfSeg, omfRec, data, relocs)) { + return false; + } + break; + default: + Debug.Assert(false); + return false; + } + + return true; + } + + /// + /// Generates a series of relocation items for a SUPER record. + /// + private static bool GenerateRelocForSuper(OmfSegment omfSeg, OmfRecord omfRec, byte[] data, + List relocs) { + int offset = omfRec.FileOffset; + int remaining = RawData.GetWord(data, offset + 1, 4, false); + int type = data[offset + 5]; + offset += 6; // we've consumed 6 bytes + remaining--; // ...but only 1 counts against the SUPER length + + int width, shift, fileNum, segNum; + bool needSegNum = false; + + if (type == 0) { + // SUPER RELOC2 + width = 2; + shift = 0; + fileNum = segNum = -1; + } else if (type == 1) { + // SUPER RELOC3 + width = 3; + shift = 0; + fileNum = segNum = -1; + } else if (type >= 2 && type <= 13) { + // SUPER INTERSEG1 - SUPER INTERSEG12 + width = 3; + shift = 0; + fileNum = type - 1; + segNum = -100; + needSegNum = true; + } else if (type >= 14 && type <= 25) { + // SUPER INTERSEG13 - SUPER INTERSEG24 + width = 2; + shift = 0; + fileNum = 1; + segNum = type - 13; + } else if (type >= 26 && type <= 37) { + // SUPER INTERSEG25 - SUPER INTERSEG36 + width = 2; + shift = -16; // right shift + fileNum = 1; + segNum = type - 25; + } else { + return false; + } + + int page = 0; + while (remaining > 0) { + int patchCount = data[offset++]; + remaining--; + + if ((patchCount & 0x80) != 0) { + // high bit set, this is a skip-count + page += (patchCount & 0x7f); + continue; + } + patchCount++; // zero means one patch + while (patchCount-- != 0) { + int patchOff = data[offset++]; + remaining--; + + int relocOff = page * 256 + patchOff; + + byte[] constData = omfSeg.GetConstData(); + int relocRelOff = RawData.GetWord(constData, relocOff, 2, false); + if (needSegNum) { + segNum = constData[relocOff + 2]; + } + + relocs.Add(new OmfReloc(width, shift, relocOff, relocRelOff, + fileNum, segNum, type)); + } + + page++; + } + + if (remaining < 0) { + Debug.WriteLine("Ran off end of SUPER record"); + Debug.Assert(false); + return false; + } + + return true; + } + } +} diff --git a/SourceGen/Tools/Omf/OmfSegment.cs b/SourceGen/Tools/Omf/OmfSegment.cs index aa51225..b2576df 100644 --- a/SourceGen/Tools/Omf/OmfSegment.cs +++ b/SourceGen/Tools/Omf/OmfSegment.cs @@ -72,6 +72,8 @@ namespace SourceGen.Tools.Omf { // Length of LOADNAME field. private const int LOAD_NAME_LEN = 10; + private const int DISK_BLOCK_SIZE = 512; + public class NameValueNote { public string Name { get; private set; } public object Value { get; private set; } @@ -132,7 +134,8 @@ namespace SourceGen.Tools.Omf { // public int FileOffset { get; private set; } - public int FileLength { get; private set; } // from BLKCNT or BYTECNT + public int RawFileLength { get; private set; } // from BLKCNT or BYTECNT + public int FileLength { get; private set; } // last block may be short public int ResSpc { get; private set; } public int Length { get; private set; } @@ -166,6 +169,11 @@ namespace SourceGen.Tools.Omf { /// public List Records = new List(); + /// + /// Relocation list, for segments in Load files. + /// + public List Relocs = new List(); + // Constructor is private; use ParseHeader() to create an instance. private OmfSegment() { } @@ -321,9 +329,11 @@ namespace SourceGen.Tools.Omf { // We've got the basic pieces. Handle the block-vs-byte debacle. int segLen; + bool asBlocks = false; if (newSeg.Version == SegmentVersion.v0_0) { // Always block count. - segLen = blkByteCnt * 512; + segLen = blkByteCnt * DISK_BLOCK_SIZE; + asBlocks = true; } else if (newSeg.Version >= SegmentVersion.v2_0) { // Always byte count. segLen = blkByteCnt; @@ -332,17 +342,14 @@ namespace SourceGen.Tools.Omf { // files by checking for a nonzero SegNum field, but there's no reliable way // to tell the difference between Object and Library while looking at a segment // in isolation. - // - // I have found a couple of examples (e.g. BRIDGE.S16 in Davex v1.23, SYSTEM:START - // on an old Paintworks GS disk) where the file's length is shy of a multiple - // of 512, so we ought to handle that. if (parseAsLibrary) { segLen = blkByteCnt; } else { - segLen = blkByteCnt * 512; + segLen = blkByteCnt * DISK_BLOCK_SIZE; + asBlocks = true; } } - newSeg.FileLength = segLen; + newSeg.RawFileLength = newSeg.FileLength = segLen; // // Perform validity checks. If any of these fail, we're probably reading something @@ -359,11 +366,21 @@ namespace SourceGen.Tools.Omf { return ParseResult.Failure; } if (offset + segLen > data.Length) { - // Segment is longer than the file. (This can happen easily in a static lib if - // we're not parsing it as such.) - AddErrorMsg(msgs, offset, "segment file length exceeds EOF (segLen=" + segLen + - ", remaining=" + (data.Length - offset) + ")"); - return ParseResult.Failure; + if (asBlocks && offset + segLen - data.Length < DISK_BLOCK_SIZE) { + // I have found a few examples (e.g. BRIDGE.S16 in Davex v1.23, SYSTEM:START + // on an old Paintworks GS disk) where the file's length doesn't fill out + // the last block in the file. If we continue, and the segment actually + // does pass EOF, we'll fail while reading the records. + AddInfoMsg(msgs, offset, + "file EOF is not a multiple of 512; last segment may be truncated"); + newSeg.FileLength = data.Length - offset; + } else { + // Segment is longer than the file. (This can happen easily in a static lib if + // we're not parsing it as such.) + AddErrorMsg(msgs, offset, "segment file length exceeds EOF (segLen=" + segLen + + ", remaining=" + (data.Length - offset) + ")"); + return ParseResult.Failure; + } } if (dispName < expectedDispName || dispName > (segLen - LOAD_NAME_LEN)) { AddErrorMsg(msgs, offset, "invalid DISPNAME " + dispName + " (expected " + @@ -456,7 +473,8 @@ namespace SourceGen.Tools.Omf { } else { newSeg.AddRaw("undefined", data[offset + 0x0c], 1, string.Empty); } - newSeg.AddRaw("LABLEN", newSeg.LabLen, 1, string.Empty); + newSeg.AddRaw("LABLEN", newSeg.LabLen, 1, + (newSeg.LabLen == 0 ? "variable length" : "fixed length")); newSeg.AddRaw("NUMLEN", numLen, 1, "must be 4"); newSeg.AddRaw("VERSION", data[offset + 0x0f], 1, VersionToString(newSeg.Version)); newSeg.AddRaw("BANKSIZE", newSeg.BankSize, 4, string.Empty); @@ -474,8 +492,16 @@ namespace SourceGen.Tools.Omf { string.Empty); } newSeg.AddRaw("ORG", newSeg.Org, 4, (newSeg.Org != 0 ? "" : "relocatable")); - newSeg.AddRaw("ALIGN", newSeg.Align, 4, - "expecting 0, $0100, or $010000"); + // alignment is rounded up to page/bank + string alignStr; + if (newSeg.Align == 0) { + alignStr = "no alignment"; + } else if (newSeg.Align <= 0x0100) { + alignStr = "align to page"; + } else { + alignStr = "align to bank"; + } + newSeg.AddRaw("ALIGN", newSeg.Align, 4, alignStr); newSeg.AddRaw("NUMSEX", numSex, 1, "must be 0"); if (newSeg.Version == SegmentVersion.v1_0) { newSeg.AddRaw("LCBANK", newSeg.LcBank, 1, string.Empty); @@ -507,7 +533,7 @@ namespace SourceGen.Tools.Omf { // Parsing failure. Bail out. return false; } - if (offset + omfRec.Length > FileOffset + FileLength) { + if (offset + omfRec.Length > FileOffset + RawFileLength) { // Overrun. AddErrorMsg(msgs, offset, "record ran off end of file (" + omfRec + ")"); return false; @@ -520,7 +546,8 @@ namespace SourceGen.Tools.Omf { int remaining = (FileOffset + FileLength) - (offset + omfRec.Length); Debug.Assert(remaining >= 0); Debug.WriteLine("END record found, remaining space=" + remaining); - if (remaining >= 512 || (Version >= SegmentVersion.v2_0 && remaining != 0)) { + if (remaining >= DISK_BLOCK_SIZE || + (Version >= SegmentVersion.v2_0 && remaining != 0)) { AddInfoMsg(msgs, offset, "found " + remaining + " bytes past END record"); } return true; @@ -580,6 +607,67 @@ namespace SourceGen.Tools.Omf { return true; } + public void GenerateRelocDict() { + Debug.Assert(CheckRecords_LoadFile()); + + 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: + OmfReloc.GenerateRelocs(this, omfRec, mFileData, Relocs); + break; + default: + break; + } + } + } + + private byte[] mConstData; + + /// + /// Returns a reference to the unpacked constant data from the body of a Load segment + /// (i.e. the LCONST/DS part). + /// + public byte[] GetConstData() { + if (mConstData != null) { + return mConstData; + } + + // We haven't generated this yet; do it now. Start by determining the length. + int totalLen = 0; + foreach (OmfRecord omfRec in Records) { + if (omfRec.Op != OmfRecord.Opcode.LCONST && omfRec.Op != OmfRecord.Opcode.DS) { + break; + } + // safe to assume NUMLEN=4, NUMSEX=0 + totalLen += RawData.GetWord(mFileData, omfRec.FileOffset + 1, 4, false); + } + + byte[] data = new byte[totalLen]; + + int bufOffset = 0; + foreach (OmfRecord omfRec in Records) { + if (omfRec.Op == OmfRecord.Opcode.DS) { + int len = RawData.GetWord(mFileData, omfRec.FileOffset + 1, 4, false); + bufOffset += len; // new buffers are zero-filled + } else if (omfRec.Op == OmfRecord.Opcode.LCONST) { + int len = RawData.GetWord(mFileData, omfRec.FileOffset + 1, 4, false); + Array.Copy(mFileData, omfRec.FileOffset + 5, data, bufOffset, len); + bufOffset += len; + } else { + break; + } + } + + Debug.Assert(bufOffset == totalLen); + Debug.WriteLine("Generated " + totalLen + " bytes of LCONST/DS data for " + this); + mConstData = data; + return data; + } + // // Helper functions. // diff --git a/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml b/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml index 9dcc0d7..e876528 100644 --- a/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml +++ b/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml @@ -101,8 +101,10 @@ limitations under the License. - + - - - - + + + + + + + diff --git a/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml.cs b/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml.cs index c541e92..d4e9186 100644 --- a/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml.cs +++ b/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml.cs @@ -63,13 +63,46 @@ namespace SourceGen.Tools.Omf.WpfGui { public List RecordItems { get; private set; } + /// + /// Formatted version of OmfReloc object. + /// public class RelocItem { - public string Offset { get; private set; } - public string Reference { get; private set; } public string Width { get; private set; } public string Shift { get; private set; } + public string Offset { get; private set; } + public string RelOffset { get; private set; } + public string FileNum { get; private set; } + public string SegNum { get; private set; } + public string SuperType { get; private set; } + + public OmfReloc RelocRef { get; private set; } + + public RelocItem(OmfReloc reloc, Formatter formatter) { + RelocRef = reloc; + + Width = reloc.Width.ToString(); + if (reloc.Shift == 0) { + Shift = string.Empty; + } else if (reloc.Shift < 0) { + Shift = ">> " + -reloc.Shift; + } else { + Shift = "<< " + reloc.Shift; + } + Offset = formatter.FormatHexValue(reloc.Offset, 4); + RelOffset = formatter.FormatHexValue(reloc.RelOffset, 4); + FileNum = reloc.FileNum < 0 ? string.Empty : reloc.FileNum.ToString(); + SegNum = reloc.SegNum < 0 ? string.Empty : reloc.SegNum.ToString(); + if (reloc.SuperType < 0) { + SuperType = string.Empty; + } else if (reloc.SuperType <= 1) { + SuperType = "RELOC" + (reloc.SuperType + 2); + } else { + SuperType = "INTERSEG" + (reloc.SuperType - 1); + } + } } - public List RelocItems { get; private set; } = new List(); + public List RelocItems { get; private set; } + public bool HasRelocs { get { return RelocItems.Count != 0; } } /// @@ -98,6 +131,13 @@ namespace SourceGen.Tools.Omf.WpfGui { GenerateHeaderItems(); RecordItems = omfSeg.Records; + RelocItems = new List(omfSeg.Relocs.Count); + foreach (OmfReloc omfRel in omfSeg.Relocs) { + RelocItems.Add(new RelocItem(omfRel, formatter)); + } + RelocItems.Sort(delegate (RelocItem a, RelocItem b) { + return a.RelocRef.Offset - b.RelocRef.Offset; + }); fmt = (string)FindResource("str_RecordHeaderFmt"); RecordHeaderStr = string.Format(fmt, RecordItems.Count); diff --git a/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs b/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs index 509be14..6bc983f 100644 --- a/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs +++ b/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs @@ -29,7 +29,6 @@ namespace SourceGen.Tools.Omf.WpfGui { /// Apple IIgs OMF file viewer. /// public partial class OmfViewer : Window, INotifyPropertyChanged { - private byte[] mFileData; private OmfFile mOmfFile; private Formatter mFormatter; @@ -63,7 +62,7 @@ namespace SourceGen.Tools.Omf.WpfGui { get { return OmfSeg.Length; } } public int FileLength { - get { return OmfSeg.FileLength; } + get { return OmfSeg.RawFileLength; } } public SegmentListItem(OmfSegment omfSeg) { @@ -101,13 +100,13 @@ namespace SourceGen.Tools.Omf.WpfGui { /// Parent window. /// Path to file on disk. Only used for display. /// File contents. + /// Text formatter. public OmfViewer(Window owner, string pathName, byte[] data, Formatter formatter) { InitializeComponent(); Owner = owner; DataContext = this; PathName = pathName; - mFileData = data; mFormatter = formatter; mOmfFile = new OmfFile(data);