1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-06-25 05:29:31 +00:00

Use relocation data to format instruction operands

This was a relatively lightweight change to confirm the usefulness
of relocation data.  The results were very positive.

The relatively superficial integration of the data into the data
analysis process causes some problems, e.g. the cross-reference table
entries show an offset because the code analyzer's computed operand
offset doesn't match the value of the label.  The feature should be
considered experimental

The feature can be enabled or disabled with a project property.  The
results were sufficiently useful and non-annoying to make the setting
enabled by default.
This commit is contained in:
Andy McFadden 2020-07-03 17:37:04 -07:00
parent 6d7fdff6b5
commit d58b747571
13 changed files with 141 additions and 52 deletions

View File

@ -174,6 +174,7 @@ namespace SourceGen {
/// bytes.</param>
/// <param name="entryFlags">Status flags to use at code entry points.</param>
/// <param name="scriptMan">Extension script manager.</param>
/// <param name="parms">Analysis parameters.</param>
/// <param name="debugLog">Object that receives debug log messages.</param>
public CodeAnalysis(byte[] data, CpuDef cpuDef, Anattrib[] anattribs,
AddressMap addrMap, TypeHint[] hints, StatusFlags[] statusFlagOverrides,

View File

@ -80,6 +80,11 @@ namespace SourceGen {
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="proj">Project to analyze.</param>
/// <param name="anattribs">Anattrib array.</param>
public DataAnalysis(DisasmProject proj, Anattrib[] anattribs) {
mProject = proj;
mAnattribs = anattribs;
@ -156,12 +161,42 @@ namespace SourceGen {
FormatDescriptor.SubType.Address);
continue;
}
// Check for a relocation. It'll be at offset+1 because it's on the operand,
// not the opcode byte.
if (mAnalysisParams.UseRelocData && mProject.RelocList.TryGetValue(offset + 1,
out DisasmProject.RelocData reloc)) {
if (reloc.Value != attr.OperandAddress) {
// The relocation address differs from what the analyzer came up
// with. This may be because of incorrect assumptions about the
// bank (assuming B==K) or because the partial address refers to
// a location outside the file bounds. Whatever the case, if the
// address is different, attr.OperandOffset will also be different.
int relOperandOffset = mProject.AddrMap.AddressToOffset(offset,
reloc.Value);
if (relOperandOffset >= 0 && relOperandOffset != attr.OperandOffset) {
// Determined a different offset. Use that instead.
//Debug.WriteLine("REL +" + offset.ToString("x6") + " " +
// reloc.Value.ToString("x6") + " vs. " +
// attr.OperandAddress.ToString("x6"));
WeakSymbolRef.Part part = WeakSymbolRef.Part.Low;
if (reloc.Shift == -8) {
part = WeakSymbolRef.Part.High;
} else if (reloc.Shift == -16) {
part = WeakSymbolRef.Part.Bank;
}
SetDataTarget(offset, attr.Length, relOperandOffset, part);
continue;
}
}
}
int operandOffset = attr.OperandOffset;
if (operandOffset >= 0) {
// This is an offset reference: a branch or data access instruction whose
// target is inside the file. Create a FormatDescriptor for it, and
// generate a label at the target if one is not already present.
SetDataTarget(offset, attr.Length, operandOffset);
SetDataTarget(offset, attr.Length, operandOffset, WeakSymbolRef.Part.Low);
}
// We advance by a single byte, rather than .Length, in case there's
@ -190,7 +225,8 @@ namespace SourceGen {
}
int operandOffset = mProject.AddrMap.AddressToOffset(offset, address);
if (operandOffset >= 0) {
SetDataTarget(offset, dfd.Length, operandOffset);
SetDataTarget(offset, dfd.Length, operandOffset,
WeakSymbolRef.Part.Low);
}
}
@ -269,7 +305,8 @@ namespace SourceGen {
/// <param name="srcOffset">Offset of instruction or address data.</param>
/// <param name="srcLen">Length of instruction or data item.</param>
/// <param name="targetOffset">Offset of target.</param>
private void SetDataTarget(int srcOffset, int srcLen, int targetOffset) {
private void SetDataTarget(int srcOffset, int srcLen, int targetOffset,
WeakSymbolRef.Part part) {
// NOTE: don't try to cache mAnattribs[targetOffset] -- we may be changing
// targetOffset and/or altering the Anattrib entry, so grabbing a copy of the
// struct may lead to problems.
@ -359,8 +396,7 @@ namespace SourceGen {
", adj=" + (origTargetOffset - targetOffset));
}
mAnattribs[srcOffset].DataDescriptor = FormatDescriptor.Create(srcLen,
new WeakSymbolRef(mAnattribs[targetOffset].Symbol.Label, WeakSymbolRef.Part.Low),
isBigEndian);
new WeakSymbolRef(mAnattribs[targetOffset].Symbol.Label, part), isBigEndian);
}
/// <summary>

View File

@ -122,24 +122,26 @@ namespace SourceGen {
/// <summary>
/// "Cooked" form of relocation data (e.g. OmfReloc). This does not contain the file
/// offset, as that's expected to be used as the dictionary key.
///
/// Will be null unless the project was generated from a relocatable source.
/// </summary>
[Serializable]
public class RelocData {
public byte Width; // width of area written by relocator
public byte Shift; // amount to shift the value
public byte Width; // width of area written by relocator (1-4 bytes)
public sbyte Shift; // amount to shift the value (usually 0 or -16)
public int Value; // value used (unshifted, full width)
public RelocData() { } // for deserialization
public RelocData(byte width, byte shift, int value) {
public RelocData(byte width, sbyte shift, int value) {
Width = width;
Shift = shift;
Value = value;
}
}
public Dictionary<int, RelocData> RelocList { get; set; }
/// <summary>
/// List of relocation data. Will be empty unless file was generated from a
/// relocatable source.
/// </summary>
public Dictionary<int, RelocData> RelocList { get; private set; }
#endregion // data to save & restore
@ -280,6 +282,7 @@ namespace SourceGen {
LvTables = new SortedList<int, LocalVariableTable>();
VisualizationSets = new SortedList<int, VisualizationSet>();
ProjectProps = new ProjectProperties();
RelocList = new Dictionary<int, RelocData>();
SymbolTable = new SymbolTable();
PlatformSyms = new List<PlatformSymbols>();

View File

@ -1282,6 +1282,8 @@ namespace SourceGen {
// This is necessary for 16-bit operands, like "LDA abs" and "PEA val",
// when outside bank zero. The bank is included in the operand address,
// but we don't want to show it here.
// (Some assemblers want the bank to be shown for JSR/JMP, but we don't
// do that here. See the corresponding code in AsmGen.GenCommon)
operandForSymbol &= 0xffff;
}
formattedOperand = mFormatter.FormatHexValue(operandForSymbol, operandLen * 2);

View File

@ -1597,17 +1597,10 @@ namespace SourceGen {
Anattrib attr = mProject.GetAnattrib(line.FileOffset);
FormatDescriptor dfd = attr.DataDescriptor;
// Does this have an operand with an in-file target offset?
// (Resolve it as a numeric reference.)
if (attr.OperandOffset >= 0) {
// Yup, find the line for that offset and jump to it.
if (!testOnly) {
GoToLocation(new NavStack.Location(attr.OperandOffset, 0, false),
GoToMode.JumpToCodeData, true);
}
return true;
} else if (dfd != null && dfd.HasSymbol) {
// Operand has a symbol, do a symbol lookup.
if (dfd != null && dfd.HasSymbol) {
// Operand has a symbol, do a symbol lookup. This is slower than a simple
// jump based on OperandOffset, but if we've incorporated reloc data then
// the jump will be wrong.
if (dfd.SymbolRef.IsVariable) {
if (!testOnly) {
GoToVarDefinition(line.FileOffset, dfd.SymbolRef, true);
@ -1646,6 +1639,14 @@ namespace SourceGen {
Debug.WriteLine("Operand symbol not found: " + dfd.SymbolRef.Label);
}
}
} else if (attr.OperandOffset >= 0) {
// Operand has an in-file target offset. We can resolve it as a numeric reference.
// Find the line for that offset and jump to it.
if (!testOnly) {
GoToLocation(new NavStack.Location(attr.OperandOffset, 0, false),
GoToMode.JumpToCodeData, true);
}
return true;
} else if (attr.IsDataStart || attr.IsInlineDataStart) {
// If it's an Address or Symbol, we can try to resolve
// the value. (Symbols should have been resolved by the
@ -2198,7 +2199,7 @@ namespace SourceGen {
if (!string.IsNullOrEmpty(mProjectPathName)) {
projectDir = Path.GetDirectoryName(mProjectPathName);
}
EditProjectProperties dlg = new EditProjectProperties(mMainWin, mProject.ProjectProps,
EditProjectProperties dlg = new EditProjectProperties(mMainWin, mProject,
projectDir, mFormatter, initialTab);
dlg.ShowDialog();
ProjectProperties newProps = dlg.NewProps;
@ -3201,6 +3202,7 @@ namespace SourceGen {
Debug.Assert(line.FileOffset >= 0);
// Does this have an operand with an in-file target offset?
// TODO: may not work correctly with reloc data?
Anattrib attr = mProject.GetAnattrib(line.FileOffset);
if (attr.OperandOffset >= 0) {
return CodeLineList.FindCodeDataIndexByOffset(attr.OperandOffset);

View File

@ -223,6 +223,7 @@ namespace SourceGen {
public string DefaultTextScanMode { get; set; }
public int MinCharsForString { get; set; }
public bool SeekNearbyTargets { get; set; }
public bool UseRelocData { get; set; }
public bool SmartPlpHandling { get; set; }
public SerAnalysisParameters() { }
@ -231,6 +232,7 @@ namespace SourceGen {
DefaultTextScanMode = src.DefaultTextScanMode.ToString();
MinCharsForString = src.MinCharsForString;
SeekNearbyTargets = src.SeekNearbyTargets;
UseRelocData = src.UseRelocData;
SmartPlpHandling = src.SmartPlpHandling;
}
}
@ -526,13 +528,10 @@ namespace SourceGen {
spf.ProjectProps = new SerProjectProperties(proj.ProjectProps);
if (proj.RelocList != null) {
// The objects can serialize directly, but the Dictionary key can't be an int.
spf.RelocList =
new Dictionary<string, DisasmProject.RelocData>(proj.RelocList.Count);
foreach (KeyValuePair<int, DisasmProject.RelocData> kvp in proj.RelocList) {
spf.RelocList.Add(kvp.Key.ToString(), kvp.Value);
}
// The objects are serializable, but the Dictionary key can't be an int.
spf.RelocList = new Dictionary<string, DisasmProject.RelocData>(proj.RelocList.Count);
foreach (KeyValuePair<int, DisasmProject.RelocData> kvp in proj.RelocList) {
spf.RelocList.Add(kvp.Key.ToString(), kvp.Value);
}
JavaScriptSerializer ser = new JavaScriptSerializer();
@ -610,6 +609,8 @@ namespace SourceGen {
spf.ProjectProps.AnalysisParams.MinCharsForString;
proj.ProjectProps.AnalysisParams.SeekNearbyTargets =
spf.ProjectProps.AnalysisParams.SeekNearbyTargets;
proj.ProjectProps.AnalysisParams.UseRelocData =
spf.ProjectProps.AnalysisParams.UseRelocData;
if (spf._ContentVersion < 2) {
// This was made optional in v1.3. Default it to true for older projects.
proj.ProjectProps.AnalysisParams.SmartPlpHandling = true;
@ -844,7 +845,6 @@ namespace SourceGen {
// Deserialize relocation data. This was added in v1.7.
if (spf.RelocList != null) {
proj.RelocList = new Dictionary<int, DisasmProject.RelocData>();
foreach (KeyValuePair<string, DisasmProject.RelocData> kvp in spf.RelocList) {
if (!ParseValidateKey(kvp.Key, spf.FileDataLength,
Res.Strings.PROJECT_FIELD_RELOC_DATA, report, out int intKey)) {

View File

@ -28,9 +28,10 @@ namespace SourceGen {
/// </summary>
public class ProjectProperties {
//
// NOTE:
// *** NOTE ***
// If you add or modify a member, make sure to update the copy constructor and
// add serialization code to ProjectFile.
// *** NOTE ***
//
/// <summary>
@ -51,6 +52,7 @@ namespace SourceGen {
public TextScanMode DefaultTextScanMode { get; set; }
public int MinCharsForString { get; set; }
public bool SeekNearbyTargets { get; set; }
public bool UseRelocData { get; set; }
public bool SmartPlpHandling { get; set; }
public AnalysisParameters() {
@ -59,6 +61,7 @@ namespace SourceGen {
DefaultTextScanMode = TextScanMode.LowHighAscii;
MinCharsForString = DataAnalysis.DEFAULT_MIN_STRING_LENGTH;
SeekNearbyTargets = true;
UseRelocData = true;
SmartPlpHandling = true;
}
public AnalysisParameters(AnalysisParameters src) {
@ -66,6 +69,7 @@ namespace SourceGen {
DefaultTextScanMode = src.DefaultTextScanMode;
MinCharsForString = src.MinCharsForString;
SeekNearbyTargets = src.SeekNearbyTargets;
UseRelocData = src.UseRelocData;
SmartPlpHandling = src.SmartPlpHandling;
}
}

View File

@ -137,10 +137,12 @@ and 65816 code. The official web site is
<li><a href="tools.html">Tools</a>
<ul>
<li><a href="tools.html#instruction-chart">Instruction Chart</a></li>
<li><a href="tools.html#ascii-chart">ASCII Chart</a></li>
<li><a href="tools.html#hexdump">Hex Dump Viewer</a></li>
<li><a href="tools.html#file-concat">File Concatenator</a></li>
<li><a href="tools.html#ascii-chart">ASCII Chart</a></li>
<li><a href="tools.html#instruction-chart">Instruction Chart</a></li>
<li><a href="tools.html#file-slicer">File Slicer</a></li>
<li><a href="tools.html#omf-converter">OMF Converter</a></li>
</ul></li>
<li><a href="advanced.html">Advanced Topics</a>

View File

@ -254,6 +254,10 @@ unless they match exactly. Note that references into the middle of an
instruction or formatted data area are always adjusted, regardless of
how this is set. This setting has no effect on local variables, and
only enables a 1-byte backward search on project/platform symbols.</p>
<p>The "use relocation data" checkbox is only available if the project
was created from a relocatable source, e.g. by the OMF Converter tool.
If checked, information from the relocation dictionary will be used to
improve automatic operand formatting.</p>
<p>If "smart PLP handling" is checked, the analyzer will try to use
the processor status flags from a nearby <code>PHP</code> when a
<code>PLP</code> is encountered. If not enabled, all flags are set to

View File

@ -13,6 +13,7 @@
<h1>6502bench SourceGen: Tools</h1>
<h2><a name="instruction-chart">Instruction Chart</a></h2>
<p>This opens a window with a summary of all 256 opcodes. The CPU can
be chosen from the pop-up list at the bottom. Undocumented opcodes for
6502/65C02 are shown in italics, and can be excluded from the list
@ -84,6 +85,20 @@ length of the file, respectively.</p>
sliced, allowing you to confirm the placement.</p>
<h2><a name="omf-converter">OMF Converter</a></h2>
<p>This tool allows you to view Apple IIgs Object Module Format
binaries, and convert them for disassembly. OMF files have multiple
segments with relocatable code. The conversion tool loads the OMF
file the same way the GS/OS System Loader would, and creates a
SourceGen project file for it with some basic attributes filled in.</p>
<p>Only Load files may be converted (S16, PIF, etc). Compiler object
files and libraries contain unresolved references and are not supported.</p>
<p>The generated binary file is not in OMF format and will not execute
on an Apple IIgs.</p>
</div>
<div id="footer">

View File

@ -344,7 +344,9 @@ namespace SourceGen.Tools.Omf {
proj.PrepForNew(data, "new_proj");
proj.ApplyChanges(cs, false, out _);
proj.RelocList = mRelocData;
foreach (KeyValuePair<int, DisasmProject.RelocData> kvp in mRelocData) {
proj.RelocList.Add(kvp.Key, kvp.Value);
}
mLoadedData = data;
mNewProject = proj;
@ -410,38 +412,39 @@ namespace SourceGen.Tools.Omf {
Debug.WriteLine("Invalid reloc shift " + omfRel.Shift);
return false;
}
int adjRelocAddr = relocAddr;
if (omfRel.Shift < 0) {
relocAddr >>= -omfRel.Shift;
adjRelocAddr >>= -omfRel.Shift;
} else if (omfRel.Shift > 0) {
relocAddr <<= omfRel.Shift;
adjRelocAddr <<= omfRel.Shift;
}
switch (omfRel.Width) {
case 1:
data[bufOffset + omfRel.Offset] = (byte)(relocAddr);
data[bufOffset + omfRel.Offset] = (byte)(adjRelocAddr);
break;
case 2:
data[bufOffset + omfRel.Offset] = (byte)(relocAddr);
data[bufOffset + omfRel.Offset + 1] = (byte)(relocAddr >> 8);
data[bufOffset + omfRel.Offset] = (byte)(adjRelocAddr);
data[bufOffset + omfRel.Offset + 1] = (byte)(adjRelocAddr >> 8);
break;
case 3:
data[bufOffset + omfRel.Offset] = (byte)(relocAddr);
data[bufOffset + omfRel.Offset + 1] = (byte)(relocAddr >> 8);
data[bufOffset + omfRel.Offset + 2] = (byte)(relocAddr >> 16);
data[bufOffset + omfRel.Offset] = (byte)(adjRelocAddr);
data[bufOffset + omfRel.Offset + 1] = (byte)(adjRelocAddr >> 8);
data[bufOffset + omfRel.Offset + 2] = (byte)(adjRelocAddr >> 16);
break;
case 4:
data[bufOffset + omfRel.Offset] = (byte)(relocAddr);
data[bufOffset + omfRel.Offset + 1] = (byte)(relocAddr >> 8);
data[bufOffset + omfRel.Offset + 2] = (byte)(relocAddr >> 16);
data[bufOffset + omfRel.Offset + 3] = (byte)(relocAddr >> 24);
data[bufOffset + omfRel.Offset] = (byte)(adjRelocAddr);
data[bufOffset + omfRel.Offset + 1] = (byte)(adjRelocAddr >> 8);
data[bufOffset + omfRel.Offset + 2] = (byte)(adjRelocAddr >> 16);
data[bufOffset + omfRel.Offset + 3] = (byte)(adjRelocAddr >> 24);
break;
default:
Debug.WriteLine("Invalid reloc width " + omfRel.Width);
return false;
}
mRelocData.Add(bufOffset + omfRel.Offset,
new DisasmProject.RelocData((byte)omfRel.Width, (byte)omfRel.Shift, relocAddr));
mRelocData.Add(bufOffset + omfRel.Offset, new DisasmProject.RelocData(
(byte)omfRel.Width, (sbyte)omfRel.Shift, relocAddr));
}
return true;

View File

@ -103,6 +103,8 @@ limitations under the License.
IsChecked="{Binding AnalyzeUncategorizedData}"/>
<CheckBox Margin="0,4,0,0" Content="Seek nearby targets"
IsChecked="{Binding SeekNearbyTargets}"/>
<CheckBox Margin="0,4,0,0" Content="Use relocation data (experimental)"
IsChecked="{Binding UseRelocData}" IsEnabled="{Binding IsRelocDataAvailable}"/>
<CheckBox Margin="0,4,0,0" Content="Smart PLP handling"
IsChecked="{Binding SmartPlpHandling}"/>

View File

@ -105,21 +105,23 @@ namespace SourceGen.WpfGui {
/// Constructor. Initial state is configured from an existing ProjectProperties object.
/// </summary>
/// <param name="owner">Parent window.</param>
/// <param name="props">Property holder to clone.</param>
/// <param name="project">Project object.</param>
/// <param name="projectDir">Project directory, if known.</param>
/// <param name="formatter">Text formatter.</param>
/// <param name="initialTab">Tab to open initially. Pass "Unknown" for default.</param>
public EditProjectProperties(Window owner, ProjectProperties props, string projectDir,
public EditProjectProperties(Window owner, DisasmProject project, string projectDir,
Formatter formatter, Tab initialTab) {
InitializeComponent();
Owner = owner;
DataContext = this;
mWorkProps = new ProjectProperties(props);
mWorkProps = new ProjectProperties(project.ProjectProps); // make a work copy
mProjectDir = projectDir;
mFormatter = formatter;
mInitialTab = initialTab;
IsRelocDataAvailable = (project.RelocList.Count > 0);
// Construct arrays used as item sources for combo boxes.
CpuItems = new CpuItem[] {
new CpuItem((string)FindResource("str_6502"), CpuDef.CpuType.Cpu6502),
@ -341,6 +343,19 @@ namespace SourceGen.WpfGui {
IsDirty = true;
}
}
public bool UseRelocData {
get { return mWorkProps.AnalysisParams.UseRelocData; }
set {
mWorkProps.AnalysisParams.UseRelocData = value;
OnPropertyChanged();
IsDirty = true;
}
}
private bool mIsRelocDataAvailable;
public bool IsRelocDataAvailable {
get { return mIsRelocDataAvailable; }
set { mIsRelocDataAvailable = value; OnPropertyChanged(); }
}
public bool SmartPlpHandling {
get { return mWorkProps.AnalysisParams.SmartPlpHandling; }
set {