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 parsing of records from OMF segment bodies.  These are displayed
in the segment viewer window.
This commit is contained in:
Andy McFadden 2020-06-27 18:06:26 -07:00
parent d1526e5f25
commit fa500a2a49
8 changed files with 614 additions and 65 deletions

View File

@ -86,6 +86,7 @@
</Compile>
<Compile Include="Tools\ApplesoftToHtml.cs" />
<Compile Include="Tools\Omf\OmfFile.cs" />
<Compile Include="Tools\Omf\OmfRecord.cs" />
<Compile Include="Tools\Omf\OmfSegment.cs" />
<Compile Include="Tools\Omf\WpfGui\OmfSegmentViewer.xaml.cs">
<DependentUpon>OmfSegmentViewer.xaml</DependentUpon>

View File

@ -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 {
/// <remarks>
/// 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;
/// <summary>
/// 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<OmfSegment> mSegmentList = new List<OmfSegment>();
@ -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<string> firstFail = new List<string>(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<string> msgs = new List<string>();
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;

View File

@ -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 {
/// <summary>
/// Apple IIgs OMF record.
/// </summary>
public class OmfRecord {
private const int NUMLEN = 4; // defined by NUMLEN field in header; always 4 for IIgs
/// <summary>
/// Total length, in bytes, of this record.
/// </summary>
public int Length { get; private set; }
/// <summary>
/// Opcode.
/// </summary>
public Opcode Op { get; private set; }
/// <summary>
/// Opcode, in human-readable form.
/// </summary>
public string OpName { get; private set; }
/// <summary>
/// Value, in human-readable form.
/// </summary>
public string Value { get; private set; }
/// <summary>
/// Opcode byte definition.
/// </summary>
/// <remarks>
/// Nearly all of the 256 possible values are assigned. 5 are unused, 5 are reserved.
/// </remarks>
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() { }
/// <summary>
/// Creates a new OmfRecord instance from the data at the specified offset.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="data">Data to analyze.</param>
/// <param name="offset">Offset of start of record.</param>
/// <param name="version">OMF segment version number.</param>
/// <param name="labLen">Label length, defined in OMF segment header.</param>
/// <param name="msgs">Output message holder.</param>
/// <param name="omfRec">New record instance.</param>
/// <returns>True on success.</returns>
public static bool ParseRecord(byte[] data, int offset,
OmfSegment.SegmentVersion version, int labLen, Formatter formatter,
List<string> 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;
}
}
/// <summary>
/// Parses OMF record data.
/// </summary>
/// <param name="data">Data to analyze.</param>
/// <param name="offset">Offset of start of record.</param>
/// <param name="version">OMF segment version number.</param>
/// <param name="labLen">Label length, defined in OMF segment header.</param>
/// <param name="msgs">Output message holder.</param>
/// <returns>Parse result code.</returns>
private bool DoParseRecord(byte[] data, int offset,
OmfSegment.SegmentVersion version, int labLen, Formatter formatter,
List<string> 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;
}
/// <summary>
/// Expression operations.
/// </summary>
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<string> 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;
}
}
}

View File

@ -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.
/// </remarks>
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<OmfRecord> Records = new List<OmfRecord>();
// Constructor is private; use ParseHeader() to create an instance.
private OmfSegment() { }
public enum ParseResult {
@ -165,7 +169,7 @@ namespace SourceGen.Tools.Omf {
}
/// <summary>
/// Parses an OMF segment header.
/// Parses an OMF segment header. If successful, a new OmfSegment object is created.
/// </summary>
/// <param name="data">File data.</param>
/// <param name="offset">Offset at which to start parsing.</param>
@ -174,7 +178,7 @@ namespace SourceGen.Tools.Omf {
/// <param name="msgs">Notes and errors generated by the parser.</param>
/// <param name="segResult">Completed object, or null on failure.</param>
/// <returns>Result code.</returns>
public static ParseResult ParseSegment(byte[] data, int offset, bool parseAsLibrary,
public static ParseResult ParseHeader(byte[] data, int offset, bool parseAsLibrary,
List<string> 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<string> 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<string> msgs, int offset, string msg) {
public static void AddInfoMsg(List<string> msgs, int offset, string msg) {
msgs.Add("Note (+" + offset.ToString("x6") + "): " + msg);
}
private static void AddErrorMsg(List<string> msgs, int offset, string msg) {
public static void AddErrorMsg(List<string> msgs, int offset, string msg) {
msgs.Add("Error (+" + offset.ToString("x6") + "): " + msg);
}

View File

@ -30,6 +30,7 @@ limitations under the License.
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
<system:String x:Key="str_FileOffsetLenFmt">File offset {0}, length {1} ({2})</system:String>
<system:String x:Key="str_RecordHeaderFmt">Records ({0}):</system:String>
</Window.Resources>
<Grid Margin="8">
@ -44,7 +45,7 @@ limitations under the License.
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding FileOffsetLen, FallbackValue=File Offset / Length}" Margin="0,8,0,0"/>
<TextBlock Grid.Row="0" Text="{Binding FileOffsetLenStr, FallbackValue=File Offset / Length}" Margin="0,8,0,0"/>
<TextBlock Grid.Row="1" Text="Header fields:" Margin="0,8,0,0"/>
<DataGrid Name="headerList" Grid.Row="2" Height="130" Margin="0,8,0,0"
@ -73,7 +74,7 @@ limitations under the License.
</DataGrid.Columns>
</DataGrid>
<TextBlock Grid.Row="3" Text="Records {N}:" Margin="0,8,0,0"/>
<TextBlock Grid.Row="3" Text="{Binding RecordHeaderStr, FallbackValue=Records (123):}" Margin="0,8,0,0"/>
<DataGrid Name="recordList" Grid.Row="4" Height="130" Margin="0,8,0,0"
IsReadOnly="True"
ItemsSource="{Binding RecordItems}"
@ -94,9 +95,9 @@ limitations under the License.
Color="{x:Static SystemColors.HighlightTextColor}"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="Type" Width="100" Binding="{Binding Type}"/>
<DataGridTextColumn Header="Len" Width="40" Binding="{Binding Len}"/>
<DataGridTextColumn Header="Value" Width="300" Binding="{Binding Value}"/>
<DataGridTextColumn Header="OpName" Width="80" Binding="{Binding OpName}"/>
<DataGridTextColumn Header="Length" Width="80" Binding="{Binding Length}"/>
<DataGridTextColumn Header="Value" Width="380" Binding="{Binding Value}"/>
</DataGrid.Columns>
</DataGrid>

View File

@ -26,7 +26,7 @@ namespace SourceGen.Tools.Omf.WpfGui {
/// Apple IIgs OMF segment viewer.
/// </summary>
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<HeaderItem> HeaderItems { get; private set; } = new List<HeaderItem>();
public class RecordItem {
public string Type { get; private set; }
public string Len { get; private set; }
public string Value { get; private set; }
}
public List<RecordItem> RecordItems { get; private set; } = new List<RecordItem>();
public List<OmfRecord> 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() {

View File

@ -75,7 +75,7 @@ limitations under the License.
</DataGrid.Columns>
</DataGrid>
<TextBlock Grid.Row="3" Text="Notes and errors:" Margin="0,8,0,0"/>
<TextBlock Grid.Row="3" Text="Notes and error messages:" Margin="0,8,0,0"/>
<TextBox Grid.Row="4" Margin="0,4,0,0" Height="60"
Text="{Binding MessageStrings}"
IsReadOnly="True" VerticalScrollBarVisibility="Auto">

View File

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