mirror of
https://github.com/fadden/6502bench.git
synced 2024-11-29 10:50:28 +00:00
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.
This commit is contained in:
parent
0702882c8c
commit
463a0cc561
@ -87,6 +87,7 @@
|
||||
<Compile Include="Tools\ApplesoftToHtml.cs" />
|
||||
<Compile Include="Tools\Omf\OmfFile.cs" />
|
||||
<Compile Include="Tools\Omf\OmfRecord.cs" />
|
||||
<Compile Include="Tools\Omf\OmfReloc.cs" />
|
||||
<Compile Include="Tools\Omf\OmfSegment.cs" />
|
||||
<Compile Include="Tools\Omf\WpfGui\OmfSegmentViewer.xaml.cs">
|
||||
<DependentUpon>OmfSegmentViewer.xaml</DependentUpon>
|
||||
|
@ -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 {
|
||||
/// <summary>
|
||||
/// Apple IIgs OMF file.
|
||||
@ -70,14 +71,19 @@ namespace SourceGen.Tools.Omf {
|
||||
}
|
||||
public FileKind OmfFileKind { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// File data. Files must be loaded completely into memory.
|
||||
/// </summary>
|
||||
private byte[] mFileData;
|
||||
private bool mIsDamaged;
|
||||
|
||||
private List<OmfSegment> mSegmentList = new List<OmfSegment>();
|
||||
public List<OmfSegment> SegmentList {
|
||||
get { return mSegmentList; }
|
||||
}
|
||||
/// <summary>
|
||||
/// List of segments.
|
||||
/// </summary>
|
||||
public List<OmfSegment> SegmentList { get; private set; } = new List<OmfSegment>();
|
||||
|
||||
/// <summary>
|
||||
/// List of strings to show in the message window.
|
||||
/// </summary>
|
||||
public List<string> MessageList { get; private set; } = new List<string>();
|
||||
|
||||
|
||||
@ -110,6 +116,10 @@ namespace SourceGen.Tools.Omf {
|
||||
|
||||
OmfFileKind = DetermineFileKind();
|
||||
Debug.WriteLine("File kind is " + OmfFileKind);
|
||||
|
||||
if (OmfFileKind == FileKind.Load) {
|
||||
GenerateRelocDicts();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates relocation dictionaries for all code/data/init segments.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
/// <summary>
|
||||
/// Offset of record start within file.
|
||||
/// </summary>
|
||||
public int FileOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total length, in bytes, of this record.
|
||||
/// </summary>
|
||||
@ -114,6 +119,7 @@ namespace SourceGen.Tools.Omf {
|
||||
OmfSegment.SegmentVersion version, int labLen, Formatter formatter,
|
||||
List<string> 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:
|
||||
|
266
SourceGen/Tools/Omf/OmfReloc.cs
Normal file
266
SourceGen/Tools/Omf/OmfReloc.cs
Normal file
@ -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 {
|
||||
/// <summary>
|
||||
/// Apple IIgs OMF relocation entry. These are generated from OMF relocation records.
|
||||
/// Note that SUPER records may generate multiple instances of these.
|
||||
/// </summary>
|
||||
public class OmfReloc {
|
||||
/// <summary>
|
||||
/// Number of bytes to be relocated.
|
||||
/// </summary>
|
||||
public int Width { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of bits to shift the result before storing. Positive values shift left,
|
||||
/// negative values shift right (unsigned).
|
||||
/// </summary>
|
||||
public int Shift { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Offset within segment of bytes to rewrite.
|
||||
/// </summary>
|
||||
public int Offset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Relative offset of the thing being referenced. The segment's address in memory
|
||||
/// is added to this to yield the final value.
|
||||
/// </summary>
|
||||
public int RelOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// File number of target segment. Normally 1. Will be -1 for intra-segment relocs.
|
||||
/// </summary>
|
||||
public int FileNum { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Segment number of target segment. Will be -1 for intra-segment relocs.
|
||||
/// </summary>
|
||||
public int SegNum { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to the SUPER type, or -1 if this record was not from a SUPER.
|
||||
/// </summary>
|
||||
public int SuperType { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for RELOC/cRELOC.
|
||||
/// </summary>
|
||||
/// <param name="width">Width in bytes of relocation.</param>
|
||||
/// <param name="shift">Bit-shift amount.</param>
|
||||
/// <param name="offset">Offset within segment of relocation.</param>
|
||||
/// <param name="relOffset">Relative offset of relocation reference.</param>
|
||||
/// <param name="fromSuper">True if generated from SUPER.</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for INTERSEG/cINTERSEG.
|
||||
/// </summary>
|
||||
/// <param name="width">Width in bytes of relocation.</param>
|
||||
/// <param name="shift">Bit-shift amount.</param>
|
||||
/// <param name="offset">Offset within segment of relocation.</param>
|
||||
/// <param name="relOffset">Relative offset of relocation reference.</param>
|
||||
/// <param name="fileNum">File number.</param>
|
||||
/// <param name="segNum">Segment number.</param>
|
||||
/// <param name="fromSuper">True if generated from SUPER.</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds one or more OmfReloc instances to the list to represent the provided record.
|
||||
/// </summary>
|
||||
/// <param name="omfSeg">Segment that contains the record.</param>
|
||||
/// <param name="omfRec">Record to add.</param>
|
||||
/// <param name="data">File data.</param>
|
||||
/// <param name="relocs">List of relocations. New entries will be appended.</param>
|
||||
/// <returns>True on success.</returns>
|
||||
public static bool GenerateRelocs(OmfSegment omfSeg, OmfRecord omfRec, byte[] data,
|
||||
List<OmfReloc> relocs) {
|
||||
try {
|
||||
return DoGenerateRelocs(omfSeg, omfRec, data, relocs);
|
||||
} catch (IndexOutOfRangeException ioore) {
|
||||
Debug.WriteLine("Caught IOORE during reloc gen (" + omfRec.Op + "): " +
|
||||
ioore.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds one or more OmfReloc instances to the list to represent the provided record.
|
||||
/// </summary>
|
||||
private static bool DoGenerateRelocs(OmfSegment omfSeg, OmfRecord omfRec, byte[] data,
|
||||
List<OmfReloc> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a series of relocation items for a SUPER record.
|
||||
/// </summary>
|
||||
private static bool GenerateRelocForSuper(OmfSegment omfSeg, OmfRecord omfRec, byte[] data,
|
||||
List<OmfReloc> 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
/// </summary>
|
||||
public List<OmfRecord> Records = new List<OmfRecord>();
|
||||
|
||||
/// <summary>
|
||||
/// Relocation list, for segments in Load files.
|
||||
/// </summary>
|
||||
public List<OmfReloc> Relocs = new List<OmfReloc>();
|
||||
|
||||
|
||||
// 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,12 +366,22 @@ namespace SourceGen.Tools.Omf {
|
||||
return ParseResult.Failure;
|
||||
}
|
||||
if (offset + segLen > data.Length) {
|
||||
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 " +
|
||||
expectedDispName + ", segLen=" + segLen + ")");
|
||||
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a reference to the unpacked constant data from the body of a Load segment
|
||||
/// (i.e. the LCONST/DS part).
|
||||
/// </summary>
|
||||
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.
|
||||
//
|
||||
|
@ -101,8 +101,10 @@ limitations under the License.
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<TextBlock Grid.Row="5" Text="Relocation table (for Load file segment):" Margin="0,8,0,0"/>
|
||||
<TextBlock Grid.Row="5" Text="Relocation dictionary:" Margin="0,8,0,0"
|
||||
Visibility="{Binding HasRelocs, Converter={StaticResource BoolToVis}}"/>
|
||||
<DataGrid Name="relocList" Grid.Row="6" Height="145" Margin="0,8,0,0"
|
||||
Visibility="{Binding HasRelocs, Converter={StaticResource BoolToVis}}"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding RelocItems}"
|
||||
FontFamily="{StaticResource GeneralMonoFont}"
|
||||
@ -122,10 +124,13 @@ limitations under the License.
|
||||
Color="{x:Static SystemColors.HighlightTextColor}"/>
|
||||
</DataGrid.Resources>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Offset" Width="100" Binding="{Binding Offset}"/>
|
||||
<DataGridTextColumn Header="Reference" Width="120" Binding="{Binding Reference}"/>
|
||||
<DataGridTextColumn Header="Width" Width="100" Binding="{Binding Width}"/>
|
||||
<DataGridTextColumn Header="Shift" Width="280" Binding="{Binding Shift}"/>
|
||||
<DataGridTextColumn Header="Bytes" Width="50" Binding="{Binding Width}"/>
|
||||
<DataGridTextColumn Header="Offset" Width="70" Binding="{Binding Offset}"/>
|
||||
<DataGridTextColumn Header="Rel Off" Width="70" Binding="{Binding RelOffset}"/>
|
||||
<DataGridTextColumn Header="Shift" Width="50" Binding="{Binding Shift}"/>
|
||||
<DataGridTextColumn Header="File Num" Width="70" Binding="{Binding FileNum}"/>
|
||||
<DataGridTextColumn Header="Seg Num" Width="70" Binding="{Binding SegNum}"/>
|
||||
<DataGridTextColumn Header="Super Type" Width="80" Binding="{Binding SuperType}"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
|
@ -63,13 +63,46 @@ namespace SourceGen.Tools.Omf.WpfGui {
|
||||
|
||||
public List<OmfRecord> RecordItems { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Formatted version of OmfReloc object.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
public List<RelocItem> RelocItems { get; private set; } = new List<RelocItem>();
|
||||
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<RelocItem> RelocItems { get; private set; }
|
||||
public bool HasRelocs { get { return RelocItems.Count != 0; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
@ -98,6 +131,13 @@ namespace SourceGen.Tools.Omf.WpfGui {
|
||||
GenerateHeaderItems();
|
||||
|
||||
RecordItems = omfSeg.Records;
|
||||
RelocItems = new List<RelocItem>(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);
|
||||
|
@ -29,7 +29,6 @@ namespace SourceGen.Tools.Omf.WpfGui {
|
||||
/// Apple IIgs OMF file viewer.
|
||||
/// </summary>
|
||||
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 {
|
||||
/// <param name="owner">Parent window.</param>
|
||||
/// <param name="pathName">Path to file on disk. Only used for display.</param>
|
||||
/// <param name="data">File contents.</param>
|
||||
/// <param name="formatter">Text formatter.</param>
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user