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);