diff --git a/SourceGen/SourceGen.csproj b/SourceGen/SourceGen.csproj
index a5bbf86..adf408f 100644
--- a/SourceGen/SourceGen.csproj
+++ b/SourceGen/SourceGen.csproj
@@ -86,6 +86,7 @@
+
OmfSegmentViewer.xaml
diff --git a/SourceGen/Tools/Omf/OmfFile.cs b/SourceGen/Tools/Omf/OmfFile.cs
index deab4c3..2c5ff34 100644
--- a/SourceGen/Tools/Omf/OmfFile.cs
+++ b/SourceGen/Tools/Omf/OmfFile.cs
@@ -13,6 +13,7 @@
* 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;
@@ -24,14 +25,18 @@ namespace SourceGen.Tools.Omf {
///
/// OMF files are a series of segments. There is no file header or identifying information.
/// In some cases the length is expected to be a multiple of 512 bytes, in others it isn't.
+ /// Each segment is comprised of a header, followed by a series of records.
///
/// There's no structural limitation on mixing and matching segments, whether different
/// versions or different types. The file format provides a structure in which various
/// things may be stored, but does not provide a way to tell an observer what is contained
- /// within (the ProDOS file type is supposed to do that).
+ /// within (the ProDOS file type is supposed to do that). A given file may be a Load
+ /// file (handled by the System Loader), Object file (fed to a linker), Library file
+ /// (also fed to a linker), or Run-Time Library (used by both the linker and the loader).
///
/// References:
- /// - (OMF "v0" is documented in an Orca/M manual?)
+ /// - IIgs Orca/M 2.0 manual. Appendix B documents OMF v0, v1, and v2.1 Load files.
+ /// (This is included with Opus ][.)
/// - "Apple IIgs Programmer's Workshop Reference". Chapter 7, page 228, describes
/// OMF v1.0 and v2.0.
/// - "Apple IIgs GS/OS Reference, for GS/OS System Software Version 5.0 and later".
@@ -50,22 +55,6 @@ namespace SourceGen.Tools.Omf {
public const int MIN_FILE_SIZE = OmfSegment.MIN_HEADER_V0;
public const int MAX_FILE_SIZE = (1 << 24) - 1; // cap at 16MB
- // TODO:
- // - has an overall file type (load, object, RTL)
- // - determine with a prioritized series of "could this be ____" checks
- // - holds list of OmfSegment
- // - has a list of warnings and errors that arose during parsing
- // - holds on to byte[] with data
- // OmfSegment:
- // - header (common data, plus name/value dict with version-specific fields for display)
- // - ref back to OmfFile for byte[] access?
- // - list of OmfRecord
- // - file-type-specific stuff can be generated and cached in second pass, e.g.
- // generate a full relocation dictionary for load files (can't do this until we
- // know the overall file type, which we can't know until all segments have been
- // processed a bit)
-
- private byte[] mFileData;
///
/// Overall file contents, determined by analysis.
@@ -81,6 +70,7 @@ namespace SourceGen.Tools.Omf {
}
public FileKind OmfFileKind { get; private set; }
+ private byte[] mFileData;
private bool mIsDamaged;
private List mSegmentList = new List();
@@ -102,13 +92,13 @@ namespace SourceGen.Tools.Omf {
OmfFileKind = FileKind.Unknown;
}
- public void Analyze() {
- OmfSegment.ParseResult result = DoAnalyze(false);
+ public void Analyze(Formatter formatter) {
+ OmfSegment.ParseResult result = DoAnalyze(formatter, false);
if (result == OmfSegment.ParseResult.IsLibrary ||
result == OmfSegment.ParseResult.Failure) {
// Failed; try again as a library.
List firstFail = new List(MessageList);
- result = DoAnalyze(true);
+ result = DoAnalyze(formatter, true);
if (result == OmfSegment.ParseResult.Failure) {
// Failed both ways. Report the failures from the non-library attempt.
MessageList = firstFail;
@@ -116,7 +106,7 @@ namespace SourceGen.Tools.Omf {
}
}
- private OmfSegment.ParseResult DoAnalyze(bool parseAsLibrary) {
+ private OmfSegment.ParseResult DoAnalyze(Formatter formatter, bool parseAsLibrary) {
bool first = true;
int offset = 0;
int len = mFileData.Length;
@@ -124,8 +114,15 @@ namespace SourceGen.Tools.Omf {
List msgs = new List();
while (len > 0) {
- OmfSegment.ParseResult result = OmfSegment.ParseSegment(mFileData, offset,
+ OmfSegment.ParseResult result = OmfSegment.ParseHeader(mFileData, offset,
parseAsLibrary, msgs, out OmfSegment seg);
+ if (result == OmfSegment.ParseResult.Success) {
+ if (!seg.ParseBody(formatter, msgs)) {
+ OmfSegment.AddErrorMsg(msgs, offset, "parsing of segment " +
+ seg.SegNum + " '" + seg.SegName + "' incomplete");
+ //result = OmfSegment.ParseResult.Failure;
+ }
+ }
MessageList.Clear();
foreach (string str in msgs) {
@@ -149,6 +146,7 @@ namespace SourceGen.Tools.Omf {
first = false;
Debug.Assert(seg.FileLength > 0);
+
mSegmentList.Add(seg);
offset += seg.FileLength;
len -= seg.FileLength;
diff --git a/SourceGen/Tools/Omf/OmfRecord.cs b/SourceGen/Tools/Omf/OmfRecord.cs
new file mode 100644
index 0000000..f66f98a
--- /dev/null
+++ b/SourceGen/Tools/Omf/OmfRecord.cs
@@ -0,0 +1,505 @@
+/*
+ * 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 System.Security.Cryptography;
+using System.Text;
+using Asm65;
+using CommonUtil;
+
+namespace SourceGen.Tools.Omf {
+ ///
+ /// Apple IIgs OMF record.
+ ///
+ public class OmfRecord {
+ private const int NUMLEN = 4; // defined by NUMLEN field in header; always 4 for IIgs
+
+ ///
+ /// Total length, in bytes, of this record.
+ ///
+ public int Length { get; private set; }
+
+ ///
+ /// Opcode.
+ ///
+ public Opcode Op { get; private set; }
+
+ ///
+ /// Opcode, in human-readable form.
+ ///
+ public string OpName { get; private set; }
+
+ ///
+ /// Value, in human-readable form.
+ ///
+ public string Value { get; private set; }
+
+ ///
+ /// Opcode byte definition.
+ ///
+ ///
+ /// Nearly all of the 256 possible values are assigned. 5 are unused, 5 are reserved.
+ ///
+ public enum Opcode : byte {
+ END = 0x00, // all
+ CONST_start = 0x01, // object
+ CONST_end = 0xdf, // object
+ ALIGN = 0xe0, // object
+ ORG = 0xe1, // object
+ RELOC = 0xe2, // load
+ INTERSEG = 0xe3, // load
+ USING = 0xe4, // object
+ STRONG = 0xe5, // object
+ GLOBAL = 0xe6, // object
+ GEQU = 0xe7, // object
+ MEM = 0xe8, // object ("not needed or supported on the Apple IIgs")
+ unused_e9 = 0xe9,
+ unused_ea = 0xea,
+ EXPR = 0xeb, // object
+ ZEXPR = 0xec, // object
+ BEXPR = 0xed, // object
+ RELEXPR = 0xee, // object
+ LOCAL = 0xef, // object
+ EQU = 0xf0, // object
+ DS = 0xf1, // all
+ LCONST = 0xf2, // all
+ LEXPR = 0xf3, // object
+ ENTRY = 0xf4, // RTL
+ cRELOC = 0xf5, // load
+ cINTERSEG = 0xf6, // load
+ SUPER = 0xf7, // load
+ unused_f8 = 0xf8,
+ unused_f9 = 0xf9,
+ unused_fa = 0xfa,
+ General = 0xfb, // reserved
+ Experimental1 = 0xfc, // reserved
+ Experimental2 = 0xfd, // reserved
+ Experimental3 = 0xfe, // reserved
+ Experimental4 = 0xff, // reserved
+ }
+
+
+ private OmfRecord() { }
+
+ ///
+ /// Creates a new OmfRecord instance from the data at the specified offset.
+ ///
+ ///
+ /// This does not catch segment boundary overruns, unless they happen to overrun
+ /// the buffer entirely. The caller should either pass in a buffer that holds the
+ /// exact segment data, or check the return value for excess length.
+ ///
+ /// Data to analyze.
+ /// Offset of start of record.
+ /// OMF segment version number.
+ /// Label length, defined in OMF segment header.
+ /// Output message holder.
+ /// New record instance.
+ /// True on success.
+ public static bool ParseRecord(byte[] data, int offset,
+ OmfSegment.SegmentVersion version, int labLen, Formatter formatter,
+ List msgs, out OmfRecord omfRec) {
+ omfRec = new OmfRecord();
+ try {
+ return omfRec.DoParseRecord(data, offset, version, labLen, formatter, msgs);
+ } catch (IndexOutOfRangeException ioore) {
+ OmfSegment.AddErrorMsg(msgs, offset, "buffer overrun while parsing record");
+ Debug.WriteLine("Exception thrown decoding record: " + ioore.Message);
+ return false;
+ }
+ }
+
+ ///
+ /// Parses OMF record data.
+ ///
+ /// Data to analyze.
+ /// Offset of start of record.
+ /// OMF segment version number.
+ /// Label length, defined in OMF segment header.
+ /// Output message holder.
+ /// Parse result code.
+ private bool DoParseRecord(byte[] data, int offset,
+ OmfSegment.SegmentVersion version, int labLen, Formatter formatter,
+ List msgs) {
+ int len = 1; // 1 byte for the opcode
+
+ Opcode opcode = Op = (Opcode)data[offset++];
+ OpName = opcode.ToString();
+ Value = string.Empty;
+
+ if (opcode >= Opcode.CONST_start && opcode <= Opcode.CONST_end) {
+ // length determined by opcode value
+ int count = (int)opcode;
+ len += count;
+ OpName = "CONST";
+ Value = count + " bytes of data";
+ } else {
+ switch (opcode) {
+ case Opcode.END:
+ break;
+ case Opcode.ALIGN: {
+ int val = GetNum(data, ref offset, ref len);
+ Value = formatter.FormatHexValue(val, 6);
+ }
+ break;
+ case Opcode.ORG: {
+ int val = GetNum(data, ref offset, ref len);
+ Value = "loc " + formatter.FormatAdjustment(val);
+ }
+ break;
+ case Opcode.RELOC: {
+ len += 1 + 1 + 4 + 4; // 10
+ int width = data[offset];
+ int operandOff = RawData.GetWord(data, offset + 2, 4, false);
+ Value = width + " bytes @" + formatter.FormatHexValue(operandOff, 4);
+ }
+ break;
+ case Opcode.INTERSEG: {
+ len += 1 + 1 + 4 + 2 + 2 + 4; // 14
+ int width = data[offset];
+ int operandOff = RawData.GetWord(data, offset + 2, 4, false);
+ int segNum = RawData.GetWord(data, offset + 8, 2, false);
+ Value = width + " bytes @" + formatter.FormatHexValue(operandOff, 4) +
+ " (seg " + segNum + ")";
+ }
+ break;
+ case Opcode.USING:
+ case Opcode.STRONG: {
+ string label = GetLabel(data, ref offset, ref len, labLen);
+ Value = "'" + label + "'";
+ }
+ break;
+ case Opcode.GLOBAL:
+ case Opcode.LOCAL: {
+ string label = GetLabel(data, ref offset, ref len, labLen);
+ int bytes;
+ byte type;
+ byte priv = 0;
+ if (version == OmfSegment.SegmentVersion.v0_0) {
+ bytes = data[offset];
+ type = data[offset + 1];
+ offset += 2;
+ len += 2;
+ } else if (version == OmfSegment.SegmentVersion.v1_0) {
+ bytes = data[offset];
+ type = data[offset + 1];
+ priv = data[offset + 2];
+ offset += 3;
+ len += 3;
+ } else {
+ bytes = RawData.GetWord(data, offset, 2, false);
+ type = data[offset + 2];
+ priv = data[offset + 3];
+ offset += 4;
+ len += 4;
+ }
+ Value = (char)type + " '" + label + "' " +
+ formatter.FormatHexValue(bytes, 4) +
+ ((priv == 0) ? "" : " private");
+ }
+ break;
+ case Opcode.GEQU:
+ case Opcode.EQU: {
+ string label = GetLabel(data, ref offset, ref len, labLen);
+ int bytes;
+ byte type;
+ byte priv = 0;
+ if (version == OmfSegment.SegmentVersion.v0_0) {
+ bytes = data[offset];
+ type = data[offset + 1];
+ offset += 2;
+ len += 2;
+ } else if (version == OmfSegment.SegmentVersion.v1_0) {
+ bytes = data[offset];
+ type = data[offset + 1];
+ priv = data[offset + 2];
+ offset += 3;
+ len += 3;
+ } else {
+ bytes = RawData.GetWord(data, offset, 2, false);
+ type = data[offset + 2];
+ priv = data[offset + 3];
+ offset += 4;
+ len += 4;
+ }
+ string expr = GetExpression(data, ref offset, ref len, labLen,
+ formatter, msgs);
+ Value = (char)type + " '" + label + "' " +
+ formatter.FormatHexValue(bytes, 4) +
+ ((priv == 0) ? "" : " private") + " = " + expr;
+ }
+ break;
+ case Opcode.MEM: {
+ int addr1 = GetNum(data, ref offset, ref len);
+ int addr2 = GetNum(data, ref offset, ref len);
+ Value = formatter.FormatHexValue(addr1, 4) + ", " +
+ formatter.FormatHexValue(addr2, 4);
+ }
+ break;
+ case Opcode.EXPR:
+ case Opcode.ZEXPR:
+ case Opcode.BEXPR:
+ case Opcode.LEXPR: {
+ int cap = data[offset++];
+ len++;
+ string expr = GetExpression(data, ref offset, ref len, labLen,
+ formatter, msgs);
+ Value = "(" + cap + ") " + expr;
+ }
+ break;
+ case Opcode.RELEXPR: {
+ int cap = data[offset++];
+ len++;
+ int rel = GetNum(data, ref offset, ref len);
+ string expr = GetExpression(data, ref offset, ref len, labLen,
+ formatter, msgs);
+ Value = "(" + cap + ") " + formatter.FormatAdjustment(rel) + " " + expr;
+ }
+ break;
+ case Opcode.DS: {
+ int count = GetNum(data, ref offset, ref len);
+ Value = count + " bytes of $00";
+ }
+ break;
+ case Opcode.LCONST: {
+ int count = GetNum(data, ref offset, ref len);
+ len += count;
+ Value = count + " bytes of data";
+ }
+ break;
+ case Opcode.cRELOC: {
+ len += 1 + 1 + 2 + 2; // 6
+ int width = data[offset];
+ int operandOff = RawData.GetWord(data, offset + 2, 2, false);
+ Value = width + " bytes @" + formatter.FormatHexValue(operandOff, 4);
+ }
+ break;
+ case Opcode.cINTERSEG: {
+ len += 1 + 1 + 2 + 1 + 2; // 7
+ int width = data[offset];
+ int operandOff = RawData.GetWord(data, offset + 2, 2, false);
+ int segNum = data[offset + 4];
+ Value = width + " bytes @" + formatter.FormatHexValue(operandOff, 4) +
+ " (seg " + segNum + ")";
+ }
+ break;
+ case Opcode.SUPER: {
+ int count = GetNum(data, ref offset, ref len);
+ len += count;
+ Value = (count - 1) + " bytes, type=" +
+ formatter.FormatHexValue(data[offset + NUMLEN], 2);
+ }
+ break;
+ case Opcode.General:
+ case Opcode.Experimental1:
+ case Opcode.Experimental2:
+ case Opcode.Experimental3:
+ case Opcode.Experimental4: {
+ OmfSegment.AddInfoMsg(msgs, offset, "found unusual record type " +
+ formatter.FormatHexValue((int)opcode, 2));
+ int count = GetNum(data, ref offset, ref len);
+ len += count;
+ }
+ break;
+ case Opcode.unused_e9:
+ case Opcode.unused_ea:
+ case Opcode.unused_f8:
+ case Opcode.unused_f9:
+ case Opcode.unused_fa:
+ // These are undefined, can't be parsed.
+ default:
+ Debug.Assert(false);
+ return false;
+ }
+ }
+ Length = len;
+ //Debug.WriteLine("REC +" + (offset-1).ToString("x6") + " " + this);
+
+ return true;
+ }
+
+ private static int GetNum(byte[] data, ref int offset, ref int len) {
+ int val = RawData.GetWord(data, offset, NUMLEN, false);
+ offset += NUMLEN;
+ len += NUMLEN;
+ return val;
+ }
+
+ private static string GetLabel(byte[] data, ref int offset, ref int len, int labLen) {
+ if (labLen == 0) {
+ labLen = data[offset++];
+ len++;
+ }
+ string str = Encoding.ASCII.GetString(data, offset, labLen).Trim();
+ offset += labLen;
+ len += labLen;
+ return str;
+ }
+
+
+ ///
+ /// Expression operations.
+ ///
+ private enum ExprOp : byte {
+ End = 0x00,
+ Addition = 0x01,
+ Subtraction = 0x02,
+ Multiplication = 0x03,
+ Division = 0x04,
+ IntegerRemainder = 0x05,
+ UnaryNegation = 0x06,
+ BitShift = 0x07,
+ AND = 0x08,
+ OR = 0x09,
+ EOR = 0x0a,
+ NOT = 0x0b,
+ LessThenEqualTo = 0x0c,
+ GreaterThanEqualTo = 0x0d,
+ NotEqual = 0x0e,
+ LessThan = 0x0f,
+ GreaterThan = 0x10,
+ EqualTo = 0x11,
+ BitAND = 0x12,
+ BitOR = 0x13,
+ BitEOR = 0x14,
+ BitNOT = 0x15,
+
+ PushLocation = 0x80,
+ PushConstant = 0x81,
+ PushLabelWeak = 0x82,
+ PushLabelValue = 0x83,
+ PushLabelLength = 0x84,
+ PushLabelType = 0x85,
+ PushLabelCount = 0x86,
+ PushRelOffset = 0x87,
+ }
+
+ private static readonly string[] ExprStrs = new string[] {
+ string.Empty, // 0x00 End
+ "+", // 0x01 Addition
+ "-", // 0x02 Subtraction
+ "*", // 0x03 Multiplication
+ "/", // 0x04 Division
+ "%", // 0x05 Integer Remainder
+ "neg", // 0x06 Unary Negation
+ "shift", // 0x07 Bit Shift
+ "&&", // 0x08 AND
+ "||", // 0x09 OR
+ "^^", // 0x0a EOR
+ "!", // 0x0b NOT
+ "<=", // 0x0c LE
+ ">=", // 0x0d GE
+ "!=", // 0x0e NE
+ "<", // 0x0f LT
+ ">", // 0x10 GT
+ "==", // 0x11 EQ
+ "&", // 0x12 Bit AND
+ "|", // 0x13 Bit OR
+ "^", // 0x14 Bit EOR
+ "~", // 0x15 Bit NOT
+ };
+
+ private static string GetExpression(byte[] data, ref int offset, ref int len, int labLen,
+ Formatter formatter, List msgs) {
+ StringBuilder sb = new StringBuilder();
+
+ bool done = false;
+ while (!done) {
+ byte operVal = data[offset++];
+ len++;
+
+ // Generate an operand string, if appropriate.
+ if (operVal > 0 && operVal < ExprStrs.Length) {
+ sb.Append(' ');
+ sb.Append(ExprStrs[operVal]);
+ } else {
+ ExprOp oper = (ExprOp)operVal;
+ switch (oper) {
+ case ExprOp.End:
+ done = true;
+ break;
+ case ExprOp.PushLocation:
+ sb.Append(" [loc]");
+ break;
+ case ExprOp.PushConstant: {
+ int val = GetNum(data, ref offset, ref len);
+ sb.Append(' ');
+ sb.Append(formatter.FormatHexValue(val, 4));
+ }
+ break;
+ case ExprOp.PushLabelWeak: {
+ string label = GetLabel(data, ref offset, ref len, labLen);
+ sb.Append(" weak:'");
+ sb.Append(label);
+ sb.Append("'");
+ }
+ break;
+ case ExprOp.PushLabelValue: {
+ string label = GetLabel(data, ref offset, ref len, labLen);
+ sb.Append(" '");
+ sb.Append(label);
+ sb.Append("'");
+ }
+ break;
+ case ExprOp.PushLabelLength: {
+ string label = GetLabel(data, ref offset, ref len, labLen);
+ sb.Append(" len:'");
+ sb.Append(label);
+ sb.Append("'");
+ }
+ break;
+ case ExprOp.PushLabelType: {
+ string label = GetLabel(data, ref offset, ref len, labLen);
+ sb.Append(" typ:'");
+ sb.Append(label);
+ sb.Append("'");
+ }
+ break;
+ case ExprOp.PushLabelCount: {
+ string label = GetLabel(data, ref offset, ref len, labLen);
+ sb.Append(" cnt:'");
+ sb.Append(label);
+ sb.Append("'");
+ }
+ break;
+ case ExprOp.PushRelOffset: {
+ int adj = GetNum(data, ref offset, ref len);
+ sb.Append(" rel:");
+ sb.Append(formatter.FormatAdjustment(adj));
+ }
+ break;
+ default:
+ OmfSegment.AddErrorMsg(msgs, offset,
+ "Found unexpected expression operator " +
+ formatter.FormatHexValue((int)oper, 2));
+ sb.Append("???");
+ break;
+ }
+ }
+ }
+
+ if (sb.Length > 0) {
+ sb.Remove(0, 1); // remove leading space
+ }
+ return sb.ToString();
+ }
+
+ public override string ToString() {
+ return Length + " " + OpName + " " + Value;
+ }
+ }
+}
diff --git a/SourceGen/Tools/Omf/OmfSegment.cs b/SourceGen/Tools/Omf/OmfSegment.cs
index 147437a..aa696ec 100644
--- a/SourceGen/Tools/Omf/OmfSegment.cs
+++ b/SourceGen/Tools/Omf/OmfSegment.cs
@@ -15,13 +15,10 @@
*/
using System;
using System.Collections.Generic;
-using System.Collections.Specialized;
using System.Diagnostics;
-using System.Linq;
using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Input;
+using Asm65;
using CommonUtil;
namespace SourceGen.Tools.Omf {
@@ -42,7 +39,7 @@ namespace SourceGen.Tools.Omf {
///
/// Most IIgs binaries are v1.0 or v2.0.
///
- /// You'd hope that parsing a segment would be unambiguous, but that is not the case.
+ /// You'd hope that parsing segments would be unambiguous, but that is not the case.
/// From the same reference:
///
/// "In Version 1.0, [the first] field is described as follows. For object files
@@ -59,7 +56,7 @@ namespace SourceGen.Tools.Omf {
///
/// Documentation bugs:
/// - GS/OS ref: table F-2 says "blockCount" where it should say "SEGNAME", and shows the
- /// offset of tempOrg as $2a (should be $2c).
+ /// offset of "tempOrg" as $2a (should be $2c).
/// - GS/OS ref: appendix F refers to a "REVISION" field, which does not seem to exist.
///
public class OmfSegment {
@@ -70,7 +67,7 @@ namespace SourceGen.Tools.Omf {
public const int MIN_HEADER_V1 = MIN_HEADER_V0 + 8 + LOAD_NAME_LEN;
// v2.0: Updated IIgs OMF format. Removes LCBANK, redefines KIND, and embraces BYTECNT.
public const int MIN_HEADER_V2 = MIN_HEADER_V1 + 4;
- // v2.1: adds tempORG and a couple of attribute flags. No "min" constant needed.
+ // v2.1: adds TEMPORG and a couple of attribute flags. No "min" constant needed.
// Length of LOADNAME field.
private const int LOAD_NAME_LEN = 10;
@@ -122,15 +119,17 @@ namespace SourceGen.Tools.Omf {
Dynamic = 0x8000 //
}
+ private byte[] mFileData;
+
//
- // Header fields.
+ // Header fields and header-derived values.
//
public int FileOffset { get; private set; }
public int FileLength { get; private set; } // from BLKCNT or BYTECNT
+
public int ResSpc { get; private set; }
public int Length { get; private set; }
- public int Type { get; private set; }
public int LabLen { get; private set; }
public SegmentVersion Version { get; private set; }
public int BankSize { get; private set; }
@@ -141,7 +140,8 @@ namespace SourceGen.Tools.Omf {
public int LcBank { get; private set; } // v1.0 only
public int SegNum { get; private set; }
public int Entry { get; private set; }
- public int TempOrg { get; private set; } // v2.1 only
+ public int DispData { get; private set; }
+ public int TempOrg { get; private set; } // v2.1; only used by MPW IIgs
public string LoadName { get; private set; } // unused in load segments
public string SegName { get; private set; }
@@ -155,6 +155,10 @@ namespace SourceGen.Tools.Omf {
// "The BANKSIZE and align restrictions are enforced by the linker, and violations
// of them are unlikely in a load file."
+ public List Records = new List();
+
+
+ // Constructor is private; use ParseHeader() to create an instance.
private OmfSegment() { }
public enum ParseResult {
@@ -165,7 +169,7 @@ namespace SourceGen.Tools.Omf {
}
///
- /// Parses an OMF segment header.
+ /// Parses an OMF segment header. If successful, a new OmfSegment object is created.
///
/// File data.
/// Offset at which to start parsing.
@@ -174,7 +178,7 @@ namespace SourceGen.Tools.Omf {
/// Notes and errors generated by the parser.
/// Completed object, or null on failure.
/// Result code.
- public static ParseResult ParseSegment(byte[] data, int offset, bool parseAsLibrary,
+ public static ParseResult ParseHeader(byte[] data, int offset, bool parseAsLibrary,
List msgs, out OmfSegment segResult) {
segResult = null;
@@ -188,6 +192,7 @@ namespace SourceGen.Tools.Omf {
}
OmfSegment newSeg = new OmfSegment();
+ newSeg.mFileData = data;
newSeg.FileOffset = offset;
// Start with the version number. The meaning of everything else depends on this.
@@ -229,20 +234,20 @@ namespace SourceGen.Tools.Omf {
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, dispData;
+ int dispName;
if (newSeg.Version == SegmentVersion.v0_0) {
dispName = 0x24;
if (newSeg.LabLen == 0) {
- dispData = dispName + data[offset + dispName];
+ newSeg.DispData = dispName + data[offset + dispName];
} else {
- dispData = dispName + LOAD_NAME_LEN;
+ newSeg.DispData = dispName + LOAD_NAME_LEN;
}
} else {
newSeg.LcBank = data[offset + 0x21];
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);
- dispData = RawData.GetWord(data, offset + 0x2a, 2, false);
+ newSeg.DispData = RawData.GetWord(data, offset + 0x2a, 2, false);
}
// The only way to detect a v2.1 segment is by checking DISPNAME.
@@ -349,8 +354,9 @@ namespace SourceGen.Tools.Omf {
expectedDispName + ", segLen=" + segLen + ")");
return ParseResult.Failure;
}
- if (dispData < expectedDispName + LOAD_NAME_LEN || dispData > (segLen - 1)) {
- AddErrorMsg(msgs, offset, "invalid DISPDATA " + dispData + " (expected " +
+ if (newSeg.DispData < expectedDispName + LOAD_NAME_LEN ||
+ newSeg.DispData > (segLen - 1)) {
+ AddErrorMsg(msgs, offset, "invalid DISPDATA " + newSeg.DispData + " (expected " +
(expectedDispName + LOAD_NAME_LEN) + ", segLen=" + segLen + ")");
return ParseResult.Failure;
}
@@ -463,9 +469,9 @@ namespace SourceGen.Tools.Omf {
newSeg.AddRaw("SEGNUM", newSeg.SegNum, 2, string.Empty);
newSeg.AddRaw("ENTRY", newSeg.Entry, 4, string.Empty);
newSeg.AddRaw("DISPNAME", dispName, 2, string.Empty);
- newSeg.AddRaw("DISPDATA", dispData, 2, string.Empty);
+ newSeg.AddRaw("DISPDATA", newSeg.DispData, 2, string.Empty);
if (newSeg.Version >= SegmentVersion.v2_1) {
- newSeg.AddRaw("tempORG", newSeg.TempOrg, 4, string.Empty);
+ newSeg.AddRaw("TEMPORG", newSeg.TempOrg, 4, string.Empty);
}
newSeg.AddRaw("LOADNAME", loadName, 10, string.Empty);
}
@@ -475,6 +481,38 @@ namespace SourceGen.Tools.Omf {
return ParseResult.Success;
}
+ public bool ParseBody(Formatter formatter, List msgs) {
+ int offset = FileOffset + DispData;
+ while (true) {
+ bool result = OmfRecord.ParseRecord(mFileData, offset, Version, LabLen,
+ formatter, msgs, out OmfRecord omfRec);
+ if (!result) {
+ // Parsing failure. Bail out.
+ return false;
+ }
+ if (offset + omfRec.Length > FileOffset + FileLength) {
+ // Overrun.
+ AddErrorMsg(msgs, offset, "record ran off end of file (" + omfRec + ")");
+ return false;
+ }
+
+ 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.
+ 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)) {
+ AddInfoMsg(msgs, offset, "found " + remaining + " bytes past END record");
+ }
+ return true;
+ }
+
+ Records.Add(omfRec);
+ offset += omfRec.Length;
+ }
+ }
+
//
// Helper functions.
//
@@ -485,10 +523,10 @@ namespace SourceGen.Tools.Omf {
}
RawValues.Add(new NameValueNote(name, value, width, note));
}
- private static void AddInfoMsg(List msgs, int offset, string msg) {
+ public static void AddInfoMsg(List msgs, int offset, string msg) {
msgs.Add("Note (+" + offset.ToString("x6") + "): " + msg);
}
- private static void AddErrorMsg(List msgs, int offset, string msg) {
+ public static void AddErrorMsg(List msgs, int offset, string msg) {
msgs.Add("Error (+" + offset.ToString("x6") + "): " + msg);
}
diff --git a/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml b/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml
index c9c59f0..ee59e74 100644
--- a/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml
+++ b/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml
@@ -30,6 +30,7 @@ limitations under the License.
File offset {0}, length {1} ({2})
+ Records ({0}):
@@ -44,7 +45,7 @@ limitations under the License.
-
+
-
+
-
-
-
+
+
+
diff --git a/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml.cs b/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml.cs
index 7bfe3a6..c541e92 100644
--- a/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml.cs
+++ b/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml.cs
@@ -26,7 +26,7 @@ namespace SourceGen.Tools.Omf.WpfGui {
/// Apple IIgs OMF segment viewer.
///
public partial class OmfSegmentViewer : Window, INotifyPropertyChanged {
- private OmfFile mOmfFile;
+ //private OmfFile mOmfFile;
private OmfSegment mOmfSeg;
private Formatter mFormatter;
@@ -36,10 +36,16 @@ namespace SourceGen.Tools.Omf.WpfGui {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
- private string mFileOffsetLen;
- public string FileOffsetLen {
- get { return mFileOffsetLen; }
- set { mFileOffsetLen = value; OnPropertyChanged(); }
+ private string mFileOffsetLenStr;
+ public string FileOffsetLenStr {
+ get { return mFileOffsetLenStr; }
+ set { mFileOffsetLenStr = value; OnPropertyChanged(); }
+ }
+
+ private string mRecordHeaderStr;
+ public string RecordHeaderStr {
+ get { return mRecordHeaderStr; }
+ set { mRecordHeaderStr = value; OnPropertyChanged(); }
}
public class HeaderItem {
@@ -55,12 +61,7 @@ namespace SourceGen.Tools.Omf.WpfGui {
}
public List HeaderItems { get; private set; } = new List();
- public class RecordItem {
- public string Type { get; private set; }
- public string Len { get; private set; }
- public string Value { get; private set; }
- }
- public List RecordItems { get; private set; } = new List();
+ public List RecordItems { get; private set; }
public class RelocItem {
public string Offset { get; private set; }
@@ -84,17 +85,22 @@ namespace SourceGen.Tools.Omf.WpfGui {
Owner = owner;
DataContext = this;
- mOmfFile = omfFile;
+ //mOmfFile = omfFile;
mOmfSeg = omfSeg;
mFormatter = formatter;
string fmt = (string)FindResource("str_FileOffsetLenFmt");
- FileOffsetLen = string.Format(fmt,
+ FileOffsetLenStr = string.Format(fmt,
mFormatter.FormatOffset24(omfSeg.FileOffset),
omfSeg.FileLength,
mFormatter.FormatHexValue(omfSeg.FileLength, 4));
GenerateHeaderItems();
+
+ RecordItems = omfSeg.Records;
+
+ fmt = (string)FindResource("str_RecordHeaderFmt");
+ RecordHeaderStr = string.Format(fmt, RecordItems.Count);
}
private void GenerateHeaderItems() {
diff --git a/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml b/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml
index fe34d0b..fd60f4c 100644
--- a/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml
+++ b/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml
@@ -75,7 +75,7 @@ limitations under the License.
-
+
diff --git a/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs b/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs
index ba6f167..7740562 100644
--- a/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs
+++ b/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs
@@ -105,7 +105,7 @@ namespace SourceGen.Tools.Omf.WpfGui {
mFormatter = formatter;
mOmfFile = new OmfFile(data);
- mOmfFile.Analyze();
+ mOmfFile.Analyze(mFormatter);
foreach (OmfSegment omfSeg in mOmfFile.SegmentList) {
SegmentListItems.Add(new SegmentListItem(omfSeg));