1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-05-31 22:41:37 +00:00

Add junk/align directives

Sometimes there's a bunch of junk in the binary that isn't used for
anything.  Often it's there to make things line up at the start of
a page boundary.

This adds a ".junk" directive that tells the disassembler that it
can safely disregard the contents of a region.  If the region ends
on a power-of-two boundary, an alignment value can be specified.

The assembly source generators will output an alignment directive
when possible, a .fill directive when appropriate, and a .dense
directive when all else fails.  Because we're required to regenerate
the original data file, it's not always possible to avoid generating
a hex dump.
This commit is contained in:
Andy McFadden 2019-10-18 20:28:02 -07:00
parent f31b7f5822
commit cd23580cc5
25 changed files with 638 additions and 66 deletions

View File

@ -52,5 +52,29 @@ namespace Asm65 {
target |= addr & 0x7fff0000; target |= addr & 0x7fff0000;
return target; return target;
} }
/// <summary>
/// Determines whether a range of bytes is composed of a single value. If so, the
/// value is returned.
/// </summary>
/// <param name="data">Bytes to examine.</param>
/// <param name="offset">Start offset.</param>
/// <param name="length">Number of bytes. Must be greater than zero.</param>
/// <returns>The value found, or -1 if multiple values were found.</returns>
public static int CheckRangeHoldsSingleValue(byte[] data, int offset, int length) {
Debug.Assert(data != null);
Debug.Assert(offset >= 0 && offset < data.Length);
Debug.Assert(length >= 0 && offset + length <= data.Length);
if (length < 0) {
return -1;
}
byte testVal = data[offset++];
while (--length > 0) {
if (data[offset++] != testVal) {
return -1;
}
}
return testVal;
}
} }
} }

94
CommonUtil/BitTwiddle.cs Normal file
View File

@ -0,0 +1,94 @@
/*
* Copyright 2019 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;
namespace CommonUtil {
public class BitTwiddle {
/// <summary>
/// Returns the argument, rounded up to the next highest power of 2. If the argument
/// is an exact power of two, it is returned unmodified.
/// </summary>
public static int RoundUpPowerOf2(int val) {
val--; // handle exact power of 2 case; works correctly for val=0
return NextHighestPowerOf2(val);
}
/// <summary>
/// Returns the first power of 2 value that is higher than val. If the argument is
/// an exact power of two, the next power of 2 is returned.
/// </summary>
/// <remarks>
/// Classic bit-twiddling approach. I can't find a "count leading zeroes" function
/// in C# that turns into a CPU instruction; if we had that, we could just use 1<<N.
/// </remarks>
public static int NextHighestPowerOf2(int val) {
val |= val >> 1; // "smear" bits across integer
val |= val >> 2;
val |= val >> 4;
val |= val >> 8;
val |= val >> 16;
return val + 1;
}
/// <summary>
/// Returns an integer in which the only bit set is the least-significant set bit in
/// the argument.
/// </summary>
/// <remarks>
/// If you pass in 10110100, this will return 00000100.
///
/// Two's complement negation inverts and adds one, so 01100 --> 10011+1 --> 10100. The
/// only set bit they have in common is the one we want.
/// </remarks>
public static int IsolateLeastSignificantOne(int val) {
return val & -val;
}
/// <summary>
/// Returns the number of bits that are set in the argument.
/// </summary>
/// <remarks>
/// If you pass in 10110100, this will return 4.
///
/// This comes from http://aggregate.org/MAGIC/#Population%20Count%20(Ones%20Count) .
/// </remarks>
public static int CountOneBits(int val) {
// 32-bit recursive reduction using SWAR...
// but first step is mapping 2-bit values
// into sum of 2 1-bit values in sneaky way
val -= ((val >> 1) & 0x55555555);
val = (((val >> 2) & 0x33333333) + (val & 0x33333333));
val = (((val >> 4) + val) & 0x0f0f0f0f);
val += (val >> 8);
val += (val >> 16);
return (val & 0x0000003f);
}
/// <summary>
/// Returns the number of trailing zero bits in the argument.
/// </summary>
/// <remarks>
/// If you pass in 10110100, this will return 2.
///
/// Also from http://aggregate.org/MAGIC/ .
/// </remarks>
public static int CountTrailingZeroes(int val) {
// Lazy: reduce to least-significant 1 bit, subtract one to clear that bit and set
// all the bits to the right of it, then just count the 1s.
return CountOneBits(IsolateLeastSignificantOne(val) - 1);
}
}
}

View File

@ -118,6 +118,8 @@ namespace SourceGen.AsmGen {
//DefineBigData4 //DefineBigData4
{ "Fill", "!fill" }, { "Fill", "!fill" },
{ "Dense", "!hex" }, { "Dense", "!hex" },
//Junk
{ "Align", "!align" },
{ "StrGeneric", "!text" }, // can use !xor for high ASCII { "StrGeneric", "!text" }, // can use !xor for high ASCII
//StrReverse //StrReverse
//StrNullTerm //StrNullTerm
@ -406,6 +408,25 @@ namespace SourceGen.AsmGen {
opcodeStr = operandStr = null; opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr); OutputDenseHex(offset, length, labelStr, commentStr);
break; break;
case FormatDescriptor.Type.Junk:
int fillVal = Helper.CheckRangeHoldsSingleValue(data, offset, length);
if (fillVal >= 0 && GenCommon.CheckJunkAlign(offset, dfd, Project.AddrMap)) {
// !align ANDVALUE, EQUALVALUE [, FILLVALUE]
opcodeStr = sDataOpNames.Align;
int alignVal = 1 << FormatDescriptor.AlignmentToPower(dfd.FormatSubType);
operandStr = (alignVal - 1).ToString() +
",0," + formatter.FormatHexValue(fillVal, 2);
} else if (fillVal >= 0) {
// treat same as Fill
opcodeStr = sDataOpNames.Fill;
operandStr = length + "," + formatter.FormatHexValue(fillVal, 2);
} else {
// treat same as Dense
multiLine = true;
opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr);
}
break;
case FormatDescriptor.Type.StringGeneric: case FormatDescriptor.Type.StringGeneric:
case FormatDescriptor.Type.StringReverse: case FormatDescriptor.Type.StringReverse:
case FormatDescriptor.Type.StringNullTerm: case FormatDescriptor.Type.StringNullTerm:

View File

@ -115,6 +115,7 @@ namespace SourceGen.AsmGen {
//DefineBigData4 //DefineBigData4
{ "Fill", ".res" }, { "Fill", ".res" },
//Dense // no equivalent, use .byte with comma-separated args //Dense // no equivalent, use .byte with comma-separated args
//Junk
{ "StrGeneric", ".byte" }, { "StrGeneric", ".byte" },
//StrReverse //StrReverse
{ "StrNullTerm", ".asciiz" }, { "StrNullTerm", ".asciiz" },
@ -434,6 +435,22 @@ namespace SourceGen.AsmGen {
opcodeStr = operandStr = null; opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr); OutputDenseHex(offset, length, labelStr, commentStr);
break; break;
case FormatDescriptor.Type.Junk:
// The ca65 .align directive has a dependency on the alignment of the
// segment as a whole. We're not currently declaring multiple segments,
// so we can't use .align without generating complaints.
int fillVal = Helper.CheckRangeHoldsSingleValue(data, offset, length);
if (fillVal >= 0) {
// treat same as Fill
opcodeStr = sDataOpNames.Fill;
operandStr = length + "," + formatter.FormatHexValue(fillVal, 2);
} else {
// treat same as Dense
multiLine = true;
opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr);
}
break;
case FormatDescriptor.Type.StringGeneric: case FormatDescriptor.Type.StringGeneric:
case FormatDescriptor.Type.StringReverse: case FormatDescriptor.Type.StringReverse:
case FormatDescriptor.Type.StringNullTerm: case FormatDescriptor.Type.StringNullTerm:

View File

@ -106,6 +106,8 @@ namespace SourceGen.AsmGen {
//DefineBigData4 //DefineBigData4
{ "Fill", "ds" }, { "Fill", "ds" },
{ "Dense", "hex" }, { "Dense", "hex" },
//Junk
//Align
{ "StrGeneric", "asc" }, { "StrGeneric", "asc" },
{ "StrReverse", "rev" }, { "StrReverse", "rev" },
//StrNullTerm //StrNullTerm
@ -269,6 +271,24 @@ namespace SourceGen.AsmGen {
opcodeStr = operandStr = null; opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr); OutputDenseHex(offset, length, labelStr, commentStr);
break; break;
case FormatDescriptor.Type.Junk:
int fillVal = Helper.CheckRangeHoldsSingleValue(data, offset, length);
if (fillVal >= 0) {
opcodeStr = sDataOpNames.Fill;
if (dfd.FormatSubType == FormatDescriptor.SubType.Align256 &&
GenCommon.CheckJunkAlign(offset, dfd, Project.AddrMap)) {
// special syntax for page alignment
operandStr = "\\," + formatter.FormatHexValue(fillVal, 2);
} else {
operandStr = length + "," + formatter.FormatHexValue(fillVal, 2);
}
} else {
// treat same as Dense
multiLine = true;
opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr);
}
break;
case FormatDescriptor.Type.StringGeneric: case FormatDescriptor.Type.StringGeneric:
case FormatDescriptor.Type.StringReverse: case FormatDescriptor.Type.StringReverse:
case FormatDescriptor.Type.StringNullTerm: case FormatDescriptor.Type.StringNullTerm:

View File

@ -132,6 +132,8 @@ namespace SourceGen.AsmGen {
//DefineBigData4 //DefineBigData4
{ "Fill", ".fill" }, { "Fill", ".fill" },
//Dense // no equivalent, use .byte with comma-separated args //Dense // no equivalent, use .byte with comma-separated args
//Junk
{ "Align", ".align" },
{ "StrGeneric", ".text" }, { "StrGeneric", ".text" },
//StrReverse //StrReverse
{ "StrNullTerm", ".null" }, { "StrNullTerm", ".null" },
@ -463,6 +465,25 @@ namespace SourceGen.AsmGen {
opcodeStr = operandStr = null; opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr); OutputDenseHex(offset, length, labelStr, commentStr);
break; break;
case FormatDescriptor.Type.Junk:
int fillVal = Helper.CheckRangeHoldsSingleValue(data, offset, length);
if (fillVal >= 0 && GenCommon.CheckJunkAlign(offset, dfd, Project.AddrMap)) {
// .align <expression>[, <fill>]
opcodeStr = sDataOpNames.Align;
int alignVal = 1 << FormatDescriptor.AlignmentToPower(dfd.FormatSubType);
operandStr = alignVal.ToString() +
"," + formatter.FormatHexValue(fillVal, 2);
} else if (fillVal >= 0) {
// treat same as Fill
opcodeStr = sDataOpNames.Fill;
operandStr = length + "," + formatter.FormatHexValue(fillVal, 2);
} else {
// treat same as Dense
multiLine = true;
opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr);
}
break;
case FormatDescriptor.Type.StringGeneric: case FormatDescriptor.Type.StringGeneric:
case FormatDescriptor.Type.StringReverse: case FormatDescriptor.Type.StringReverse:
case FormatDescriptor.Type.StringNullTerm: case FormatDescriptor.Type.StringNullTerm:

View File

@ -422,5 +422,32 @@ namespace SourceGen.AsmGen {
// Ditto for the local variable prefix. // Ditto for the local variable prefix.
} }
/// <summary>
/// Checks to see if the junk alignment directive is compatible with the actual
/// address. This is used to screen out alignment values that no longer match up
/// with the actual addresses.
/// </summary>
/// <param name="offset">File offset of directive.</param>
/// <param name="dfd">Format descriptor.</param>
/// <param name="addrMap">Offset to address map.</param>
/// <returns>True if the .junk alignment directive is correct.</returns>
public static bool CheckJunkAlign(int offset, FormatDescriptor dfd,
CommonUtil.AddressMap addrMap) {
Debug.Assert(dfd.FormatType == FormatDescriptor.Type.Junk);
if (dfd.FormatSubType == FormatDescriptor.SubType.None) {
return true;
}
// Just check the address. Shouldn't need to check the length.
int lastOffset = offset + dfd.Length - 1;
int alignToAddr = addrMap.OffsetToAddress(lastOffset) + 1;
int alignPwr = FormatDescriptor.AlignmentToPower(dfd.FormatSubType);
int alignMask = alignPwr - 1;
bool result = (alignToAddr & alignMask) == 0;
//Debug.WriteLine(dfd.FormatSubType + " at +" + offset.ToString("x6") +
// "(" + alignToAddr.ToString("x4") + "): " + result);
return result;
}
} }
} }

View File

@ -60,7 +60,8 @@ namespace SourceGen {
StringDci, // string terminated by flipped high bit (Dextral Char Inverted) StringDci, // string terminated by flipped high bit (Dextral Char Inverted)
Dense, // raw data, represented as compactly as possible Dense, // raw data, represented as compactly as possible
Fill // fill memory with a value Fill, // fill memory with a value
Junk // contents of memory are not interesting
} }
/// <summary> /// <summary>
@ -88,8 +89,26 @@ namespace SourceGen {
// Dense; no sub-types // Dense; no sub-types
// Fill; default is non-ignore // Fill; no sub-types
Ignore // TODO(someday): use this for "don't care" sections
// Junk; data may exist for alignment purposes. Sub-type indicates boundary.
// (SubType=None indicates no alignment)
Align2, // must be consecutive ascending powers of 2
Align4,
Align8,
Align16,
Align32,
Align64,
Align128,
Align256,
Align512,
Align1024,
Align2048,
Align4096,
Align8192,
Align16384,
Align32768,
Align65536
} }
// Maximum length of a NumericLE/BE item (32-bit value or 4-byte instruction). // Maximum length of a NumericLE/BE item (32-bit value or 4-byte instruction).
@ -169,6 +188,7 @@ namespace SourceGen {
Debug.Assert(length > 0); Debug.Assert(length > 0);
Debug.Assert(length <= MAX_NUMERIC_LEN || !IsNumeric); Debug.Assert(length <= MAX_NUMERIC_LEN || !IsNumeric);
Debug.Assert(fmt != Type.Default || length == 1); Debug.Assert(fmt != Type.Default || length == 1);
Debug.Assert(subFmt == SubType.None || (fmt != Type.Junk) ^ IsJunkSubType(subFmt));
Length = length; Length = length;
FormatType = fmt; FormatType = fmt;
@ -200,8 +220,12 @@ namespace SourceGen {
/// <param name="length">Length, in bytes.</param> /// <param name="length">Length, in bytes.</param>
/// <param name="fmt">Format type.</param> /// <param name="fmt">Format type.</param>
/// <param name="subFmt">Format sub-type.</param> /// <param name="subFmt">Format sub-type.</param>
/// <returns>New or pre-allocated descriptor.</returns> /// <returns>New or pre-allocated descriptor, or null if the arguments are
/// invalid.</returns>
public static FormatDescriptor Create(int length, Type fmt, SubType subFmt) { public static FormatDescriptor Create(int length, Type fmt, SubType subFmt) {
if (subFmt != SubType.None && !((fmt != Type.Junk) ^ IsJunkSubType(subFmt))) {
return null;
}
DebugCreateCount++; DebugCreateCount++;
DebugPrefabCount++; DebugPrefabCount++;
if (length == 1) { if (length == 1) {
@ -364,6 +388,41 @@ namespace SourceGen {
} }
} }
/// <summary>
/// Returns true if the sub-type is exclusively for use with the Junk type. Notably,
/// returns false for SubType.None.
/// </summary>
private static bool IsJunkSubType(SubType subType) {
return ((int)subType >= (int)SubType.Align2 &&
(int)subType <= (int)SubType.Align65536);
}
/// <summary>
/// Converts a power of 2 value to the corresponding alignment sub-type.
/// </summary>
/// <param name="pwr">Power of 2.</param>
/// <returns>The matching sub-type, or None if nothing matches.</returns>
public static SubType PowerToAlignment(int pwr) {
if (pwr < 1 || pwr > 16) {
return SubType.None;
}
// pwr==1 --> 2^1 --> Align2
return (SubType)((int)SubType.Align2 - 1 + pwr);
}
/// <summary>
/// Converts an alignment sub-type to the corresponding power of 2.
/// </summary>
/// <param name="align">Alignment value.</param>
/// <returns>The matching power of 2, or -1 if the sub-type isn't valid.</returns>
public static int AlignmentToPower(SubType align) {
Debug.Assert(IsJunkSubType(align));
if ((int)align < (int)SubType.Align2 || (int)align > (int)SubType.Align65536) {
return -1;
}
return (int)align - (int)SubType.Align2 + 1;
}
/// <summary> /// <summary>
/// Generates a string describing the format, suitable for use in the UI. /// Generates a string describing the format, suitable for use in the UI.
/// </summary> /// </summary>
@ -432,6 +491,9 @@ namespace SourceGen {
case Type.Fill: case Type.Fill:
retstr += "Fill"; retstr += "Fill";
break; break;
case Type.Junk:
retstr += "Unaligned junk";
break;
default: default:
// strings handled earlier // strings handled earlier
retstr += "???"; retstr += "???";
@ -469,6 +531,24 @@ namespace SourceGen {
case SubType.C64Screen: case SubType.C64Screen:
retstr += "Numeric, C64 Screen"; retstr += "Numeric, C64 Screen";
break; break;
case SubType.Align2:
case SubType.Align4:
case SubType.Align8:
case SubType.Align16:
case SubType.Align32:
case SubType.Align64:
case SubType.Align128:
case SubType.Align256:
case SubType.Align512:
case SubType.Align1024:
case SubType.Align2048:
case SubType.Align4096:
case SubType.Align8192:
case SubType.Align16384:
case SubType.Align32768:
case SubType.Align65536:
retstr += "Alignment to " + (1 << AlignmentToPower(FormatSubType));
break;
default: default:
retstr += "???"; retstr += "???";

View File

@ -1988,10 +1988,9 @@ namespace SourceGen {
TypedRangeSet.Tuple firstOffset = iter.Current; TypedRangeSet.Tuple firstOffset = iter.Current;
mProject.OperandFormats.TryGetValue(firstOffset.Value, out FormatDescriptor dfd); mProject.OperandFormats.TryGetValue(firstOffset.Value, out FormatDescriptor dfd);
EditDataOperand dlg = new EditDataOperand(mMainWin, mProject.FileData, EditDataOperand dlg =
mProject.SymbolTable, mOutputFormatter, trs, dfd); new EditDataOperand(mMainWin, mProject, mOutputFormatter, trs, dfd);
dlg.ShowDialog(); if (dlg.ShowDialog() == true) {
if (dlg.DialogResult == true) {
// Merge the changes into the OperandFormats list. We need to remove all // Merge the changes into the OperandFormats list. We need to remove all
// FormatDescriptors that overlap the selected region. We don't need to // FormatDescriptors that overlap the selected region. We don't need to
// pass the selection set in, because the dlg.Results list spans the exact // pass the selection set in, because the dlg.Results list spans the exact

View File

@ -680,6 +680,8 @@ namespace SourceGen {
if (!CreateFormatDescriptor(kvp.Value, spf._ContentVersion, report, if (!CreateFormatDescriptor(kvp.Value, spf._ContentVersion, report,
out FormatDescriptor dfd)) { out FormatDescriptor dfd)) {
report.Add(FileLoadItem.Type.Warning,
string.Format(Res.Strings.ERR_BAD_FD_FMT, intKey));
continue; continue;
} }
// Extra validation: make sure dfd doesn't run off end. // Extra validation: make sure dfd doesn't run off end.
@ -827,7 +829,7 @@ namespace SourceGen {
} }
Debug.WriteLine("Found v1 string, fmt=" + format + ", sub=" + subFormat); Debug.WriteLine("Found v1 string, fmt=" + format + ", sub=" + subFormat);
dfd = FormatDescriptor.Create(sfd.Length, format, subFormat); dfd = FormatDescriptor.Create(sfd.Length, format, subFormat);
return true; return dfd != null;
} }
try { try {
@ -842,7 +844,6 @@ namespace SourceGen {
subFormat = (FormatDescriptor.SubType)Enum.Parse( subFormat = (FormatDescriptor.SubType)Enum.Parse(
typeof(FormatDescriptor.SubType), sfd.SubFormat); typeof(FormatDescriptor.SubType), sfd.SubFormat);
} }
} catch (ArgumentException) { } catch (ArgumentException) {
report.Add(FileLoadItem.Type.Warning, Res.Strings.ERR_BAD_FD_FORMAT + report.Add(FileLoadItem.Type.Warning, Res.Strings.ERR_BAD_FD_FORMAT +
": " + sfd.Format + "/" + sfd.SubFormat); ": " + sfd.Format + "/" + sfd.SubFormat);
@ -865,7 +866,7 @@ namespace SourceGen {
new WeakSymbolRef(sfd.SymbolRef.Label, part), new WeakSymbolRef(sfd.SymbolRef.Label, part),
format == FormatDescriptor.Type.NumericBE); format == FormatDescriptor.Type.NumericBE);
} }
return true; return dfd != null;
} }
/// <summary> /// <summary>

View File

@ -78,6 +78,8 @@ namespace SourceGen {
public string DefineBigData4 { get; private set; } public string DefineBigData4 { get; private set; }
public string Fill { get; private set; } public string Fill { get; private set; }
public string Dense { get; private set; } public string Dense { get; private set; }
public string Junk { get; private set; }
public string Align { get; private set; }
public string StrGeneric { get; private set; } public string StrGeneric { get; private set; }
public string StrReverse { get; private set; } public string StrReverse { get; private set; }
public string StrLen8 { get; private set; } public string StrLen8 { get; private set; }
@ -125,6 +127,8 @@ namespace SourceGen {
a.DefineBigData4 == b.DefineBigData4 && a.DefineBigData4 == b.DefineBigData4 &&
a.Fill == b.Fill && a.Fill == b.Fill &&
a.Dense == b.Dense && a.Dense == b.Dense &&
a.Junk == b.Junk &&
a.Align == b.Align &&
a.StrGeneric == b.StrGeneric && a.StrGeneric == b.StrGeneric &&
a.StrReverse == b.StrReverse && a.StrReverse == b.StrReverse &&
a.StrLen8 == b.StrLen8 && a.StrLen8 == b.StrLen8 &&
@ -234,6 +238,8 @@ namespace SourceGen {
{ "DefineBigData4", ".dbd4" }, { "DefineBigData4", ".dbd4" },
{ "Fill", ".fill" }, { "Fill", ".fill" },
{ "Dense", ".bulk" }, { "Dense", ".bulk" },
{ "Junk", ".junk" },
{ "Align", ".align" },
{ "StrGeneric", ".str" }, { "StrGeneric", ".str" },
{ "StrReverse", ".rstr" }, { "StrReverse", ".rstr" },
@ -265,6 +271,7 @@ namespace SourceGen {
case FormatDescriptor.Type.NumericLE: case FormatDescriptor.Type.NumericLE:
case FormatDescriptor.Type.NumericBE: case FormatDescriptor.Type.NumericBE:
case FormatDescriptor.Type.Fill: case FormatDescriptor.Type.Fill:
case FormatDescriptor.Type.Junk:
return 1; return 1;
case FormatDescriptor.Type.Dense: { case FormatDescriptor.Type.Dense: {
// no delimiter, two output bytes per input byte // no delimiter, two output bytes per input byte
@ -345,6 +352,17 @@ namespace SourceGen {
po.Opcode = opNames.Fill; po.Opcode = opNames.Fill;
po.Operand = length + "," + formatter.FormatHexValue(data[offset], 2); po.Operand = length + "," + formatter.FormatHexValue(data[offset], 2);
break; break;
case FormatDescriptor.Type.Junk:
if (dfd.FormatSubType != FormatDescriptor.SubType.None) {
po.Opcode = opNames.Align;
int alignPow = FormatDescriptor.AlignmentToPower(dfd.FormatSubType);
po.Operand = formatter.FormatHexValue(1 << alignPow, 2) +
" (" + length.ToString() + " bytes)";
} else {
po.Opcode = opNames.Junk;
po.Operand = length.ToString();
}
break;
case FormatDescriptor.Type.Dense: { case FormatDescriptor.Type.Dense: {
int maxPerLine = MAX_OPERAND_LEN / 2; int maxPerLine = MAX_OPERAND_LEN / 2;
offset += subIndex * maxPerLine; offset += subIndex * maxPerLine;

View File

@ -174,7 +174,12 @@ the data file, each address will be assigned a label.</p>
<p>The "Bulk Data" items can represent large chunks of data compactly. <p>The "Bulk Data" items can represent large chunks of data compactly.
The "fill" option is only available if all selected bytes have the The "fill" option is only available if all selected bytes have the
same value.</p> same value.
If a region of bytes is irrelevant, perhaps used only as padding, you
can mark it as "junk". If it appears to be adding bytes to reach a
power-of-two address boundary, you can designate it as an alignment
directive. If you have multiple regions selected, only options that
work for all regions will be shown.</p>
<p>The "String" items are enabled or disabled depending on whether the <p>The "String" items are enabled or disabled depending on whether the
data you have selected is in the appropriate format. For example, data you have selected is in the appropriate format. For example,

View File

@ -808,7 +808,13 @@ absolute long load, and because it can make 65816 code easier to read.</p>
<h2><a name="pseudo-ops">Data and Directive Pseudo-Opcodes</a></h2> <h2><a name="pseudo-ops">Data and Directive Pseudo-Opcodes</a></h2>
<p>There are only four assembler directives that appear in the code list:</p> <p>The on-screen code list shows assembler directives that are similar
to what the various cross-assemblers provide. The actual directives
generated for a given assembler may match exactly or be totally different.
The idea is to represent the concept behind the directive, then let the
code generator figure out the implementation details.</p>
<p>There are six assembler directives that appear in the code list:</p>
<ul> <ul>
<li>.EQ - defines a symbol's value. These are generated automatically <li>.EQ - defines a symbol's value. These are generated automatically
when an operand that matches a platform or project symbol is found.</li> when an operand that matches a platform or project symbol is found.</li>
@ -818,6 +824,14 @@ absolute long load, and because it can make 65816 code easier to read.</p>
<li>.RWID - specifies the width of the accumulator and index registers <li>.RWID - specifies the width of the accumulator and index registers
(65816 only). Note this doesn't change the actual width, just tells (65816 only). Note this doesn't change the actual width, just tells
the assembler that the width has changed.</li> the assembler that the width has changed.</li>
<li>.JUNK - indicates that the data in a range of bytes is irrelevant.
(When generating sources, this will become .FILL or .BULK
depending on the contents of the memory region and the assembler's
capabilities.)</li>
<li>.ALIGN - a special case of .JUNK that indicates the irrelevant
bytes exist to force alignment to a memory boundary (usually a
256-byte page). Depending on the memory contents, it may be possible
to output this as an assembler-specific alignment directive.</li>
</ul> </ul>
<p>Every data item is represented by a pseudo-op. Some of them may <p>Every data item is represented by a pseudo-op. Some of them may
@ -826,7 +840,9 @@ represent hundreds of bytes and span multiple lines.</p>
<li>.DD1, .DD2, .DD3, .DD4 - basic "define data" op. A 1-4 byte <li>.DD1, .DD2, .DD3, .DD4 - basic "define data" op. A 1-4 byte
little-endian value.</li> little-endian value.</li>
<li>.DBD2, .DBD3, .DBD4 - "define big-endian data". 2-4 bytes of <li>.DBD2, .DBD3, .DBD4 - "define big-endian data". 2-4 bytes of
big-endian data.</li> big-endian data. (The 3- and 4-byte versions are not currently
available in the UI, since they're very unusual and few assemblers
support them.)</li>
<li>.BULK - data packed in as compact a form as the assembler allows. <li>.BULK - data packed in as compact a form as the assembler allows.
Useful for chunks of graphics data.</li> Useful for chunks of graphics data.</li>
<li>.FILL - a series of identical bytes. The operand <li>.FILL - a series of identical bytes. The operand

View File

@ -1,8 +1,8 @@
### 6502bench SourceGen dis65 v1.0 ### ### 6502bench SourceGen dis65 v1.0 ###
{ {
"FileDataLength":631,"FileDataCrc32":-1812792607,"ProjectProps":{ "_ContentVersion":2,"FileDataLength":1024,"FileDataCrc32":-1387500320,"ProjectProps":{
"CpuName":"6502","IncludeUndocumentedInstr":false,"EntryFlags":13566159,"AnalysisParams":{ "CpuName":"6502","IncludeUndocumentedInstr":false,"TwoByteBrk":false,"EntryFlags":13566159,"AutoLabelStyle":"Simple","AnalysisParams":{
"AnalyzeUncategorizedData":true,"MinCharsForString":4}, "AnalyzeUncategorizedData":true,"DefaultTextScanMode":"LowHighAscii","MinCharsForString":4,"SeekNearbyTargets":false,"SmartPlpHandling":true},
"PlatformSymbolFileIdentifiers":[],"ExtensionScriptFileIdentifiers":[],"ProjectSyms":{ "PlatformSymbolFileIdentifiers":[],"ExtensionScriptFileIdentifiers":[],"ProjectSyms":{
}}, }},
"AddressMap":[{ "AddressMap":[{
@ -10,10 +10,10 @@
"Low":0,"High":0,"Hint":"Code"}],"StatusFlagOverrides":{ "Low":0,"High":0,"Hint":"Code"}],"StatusFlagOverrides":{
}, },
"Comments":{ "Comments":{
"566":"comment"}, "566":"comment","882":"incorrect alignment"},
"LongComments":{ "LongComments":{
"-2147483647":{ "-2147483647":{
"Text":"Project file was edited to get all big-endian data types.","BoxMode":false,"MaxWidth":80}}, "Text":"Project file was edited to get all big-endian data types, and to have an incorrect .junk alignment directive.","BoxMode":false,"MaxWidth":80,"BackgroundColor":0}},
"Notes":{ "Notes":{
}, },
"UserLabels":{ "UserLabels":{
@ -53,4 +53,20 @@
"555":{ "555":{
"Length":10,"Format":"Dense","SubFormat":"None","SymbolRef":null}, "Length":10,"Format":"Dense","SubFormat":"None","SymbolRef":null},
"566":{ "566":{
"Length":64,"Format":"Dense","SubFormat":"None","SymbolRef":null}}} "Length":64,"Format":"Dense","SubFormat":"None","SymbolRef":null},
"631":{
"Length":137,"Format":"Junk","SubFormat":"Align256","SymbolRef":null},
"769":{
"Length":63,"Format":"Junk","SubFormat":"Align64","SymbolRef":null},
"833":{
"Length":31,"Format":"Junk","SubFormat":"Align32","SymbolRef":null},
"864":{
"Length":8,"Format":"Junk","SubFormat":"None","SymbolRef":null},
"873":{
"Length":8,"Format":"Junk","SubFormat":"None","SymbolRef":null},
"882":{
"Length":2,"Format":"Junk","SubFormat":"Align128","SymbolRef":null},
"884":{
"Length":140,"Format":"Junk","SubFormat":"Align256","SymbolRef":null}},
"LvTables":{
}}

View File

@ -1,4 +1,5 @@
;Project file was edited to get all big-endian data types. ;Project file was edited to get all big-endian data types, and to have an
;incorrect .junk alignment directive.
.cpu "6502" .cpu "6502"
* = $1000 * = $1000
rts rts
@ -31,3 +32,14 @@ LABEL .byte $00,$11,$22,$33,$44,$55,$66,$77,$88,$99,$aa,$bb,$cc,$dd,$ee,$ff
.byte $00,$11,$22,$33,$44,$55,$66,$77,$88,$99,$aa,$bb,$cc,$dd,$ee,$ff .byte $00,$11,$22,$33,$44,$55,$66,$77,$88,$99,$aa,$bb,$cc,$dd,$ee,$ff
.byte $ff,$ee,$dd,$cc,$bb,$aa,$99,$88,$77,$66,$55,$44,$33,$22,$11,$00 .byte $ff,$ee,$dd,$cc,$bb,$aa,$99,$88,$77,$66,$55,$44,$33,$22,$11,$00
.byte $80 .byte $80
.align 256,$aa
.byte $81
.align 64,$00
.byte $81
.align 32,$ab
.byte $00,$00,$00,$00,$00,$00,$00,$01
.byte $81
.byte $10,$00,$00,$00,$00,$00,$00,$00
.byte $81
.fill 2,$dd ;incorrect alignment
.align 256,$00

View File

@ -1,4 +1,5 @@
;Project file was edited to get all big-endian data types. ;Project file was edited to get all big-endian data types, and to have an
;incorrect .junk alignment directive.
org $1000 org $1000
rts rts
@ -28,3 +29,14 @@
LABEL hex 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff ;comment LABEL hex 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff ;comment
hex 00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100 hex 00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100
dfb $80 dfb $80
ds \,$aa
dfb $81
ds 63,$00
dfb $81
ds 31,$ab
hex 0000000000000001
dfb $81
hex 1000000000000000
dfb $81
ds 2,$dd ;incorrect alignment
ds \,$00

View File

@ -1,4 +1,5 @@
;Project file was edited to get all big-endian data types. ;Project file was edited to get all big-endian data types, and to have an
;incorrect .junk alignment directive.
!cpu 6502 !cpu 6502
* = $1000 * = $1000
rts rts
@ -29,3 +30,14 @@
LABEL !hex 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff ;comment LABEL !hex 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff ;comment
!hex 00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100 !hex 00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100
!byte $80 !byte $80
!align 255,0,$aa
!byte $81
!align 63,0,$00
!byte $81
!align 31,0,$ab
!hex 0000000000000001
!byte $81
!hex 1000000000000000
!byte $81
!fill 2,$dd ;incorrect alignment
!align 255,0,$00

View File

@ -1,4 +1,5 @@
;Project file was edited to get all big-endian data types. ;Project file was edited to get all big-endian data types, and to have an
;incorrect .junk alignment directive.
.setcpu "6502" .setcpu "6502"
; .segment "SEG000" ; .segment "SEG000"
.org $1000 .org $1000
@ -32,3 +33,14 @@ LABEL: .byte $00,$11,$22,$33,$44,$55,$66,$77,$88,$99,$aa,$bb,$cc,$dd,$ee,$ff
.byte $00,$11,$22,$33,$44,$55,$66,$77,$88,$99,$aa,$bb,$cc,$dd,$ee,$ff .byte $00,$11,$22,$33,$44,$55,$66,$77,$88,$99,$aa,$bb,$cc,$dd,$ee,$ff
.byte $ff,$ee,$dd,$cc,$bb,$aa,$99,$88,$77,$66,$55,$44,$33,$22,$11,$00 .byte $ff,$ee,$dd,$cc,$bb,$aa,$99,$88,$77,$66,$55,$44,$33,$22,$11,$00
.byte $80 .byte $80
.res 137,$aa
.byte $81
.res 63,$00
.byte $81
.res 31,$ab
.byte $00,$00,$00,$00,$00,$00,$00,$01
.byte $81
.byte $10,$00,$00,$00,$00,$00,$00,$00
.byte $81
.res 2,$dd ;incorrect alignment
.res 140,$00

View File

@ -1,7 +1,7 @@
# 6502bench SourceGen generated linker script for 2004-numeric-types # 6502bench SourceGen generated linker script for 2004-numeric-types
MEMORY { MEMORY {
MAIN: file=%O, start=%S, size=65536; MAIN: file=%O, start=%S, size=65536;
# MEM000: file=%O, start=$1000, size=631; # MEM000: file=%O, start=$1000, size=1024;
} }
SEGMENTS { SEGMENTS {
CODE: load=MAIN, type=rw; CODE: load=MAIN, type=rw;

View File

@ -15,26 +15,38 @@
dfb $11,$22,$33 ;.dbd3 dfb $11,$22,$33 ;.dbd3
dfb $11,$22,$33,$44 ;.dbd4 dfb $11,$22,$33,$44 ;.dbd4
ds 2 ds 2 ;.fill
dfb $80 dfb $80
ds 3 ds 3 ;.fill
dfb $80 dfb $80
ds 4 ds 4 ;.fill
dfb $80 dfb $80
ds 5 ds 5 ;.fill
dfb $80 dfb $80
ds 256 ds 256 ;.fill
dfb $80 dfb $80
ds 257,$cc ds 257,$cc ;.fill
hex 11 hex 11 ;.bulk
dfb $80 dfb $80
hex 11223344556677889900 hex 11223344556677889900 ;.bulk
dfb $80 dfb $80
hex 00112233445566778899aabbccddeeff hex 00112233445566778899aabbccddeeff ;4 lines .bulk
hex 00112233445566778899aabbccddeeff hex 00112233445566778899aabbccddeeff ;add a comment
hex 00112233445566778899aabbccddeeff hex 00112233445566778899aabbccddeeff
hex ffeeddccbbaa99887766554433221100 hex ffeeddccbbaa99887766554433221100
dfb $80 dfb $80
; align to 256-byte boundary
ds \,$aa ;.junk, align 256
dfb $81
ds 63,$00 ;.junk, align 64
dfb $81
ds 31,$ab ;.junk, align 32
hex 0000000000000001 ;.junk (should become .dense)
dfb $81
hex 1000000000000000 ;.junk (should become .dense)
dfb $81
hex dddd ;EDIT FILE: give this a bogus alignment
ds \,$00 ;.junk, align 256

View File

@ -616,6 +616,7 @@ limitations under the License.
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- first row --> <!-- first row -->
@ -673,63 +674,77 @@ limitations under the License.
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- third row --> <!-- third row -->
<StackPanel Grid.Column="0" Grid.Row="2" VerticalAlignment="Center"> <StackPanel Grid.Column="2" Grid.Row="2" VerticalAlignment="Center">
<TextBlock Text="Big-endian data," HorizontalAlignment="Right"/> <TextBlock Text="Big-endian data," HorizontalAlignment="Right"/>
<TextBlock Text="two bytes:" HorizontalAlignment="Right"/> <TextBlock Text="two bytes:" HorizontalAlignment="Right"/>
</StackPanel> </StackPanel>
<TextBox Name="defineBigData2TextBox" Grid.Column="1" Grid.Row="2" <TextBox Name="defineBigData2TextBox" Grid.Column="3" Grid.Row="2"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="4" Grid.Row="2" Text="Fill:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="fillTextBox" Grid.Column="5" Grid.Row="2"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="6" Grid.Row="2" Text="Bulk data:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="denseTextBox" Grid.Column="7" Grid.Row="2"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- fourth row --> <!-- fourth row -->
<TextBlock Grid.Column="0" Grid.Row="3" Text="Generic str:" <TextBlock Grid.Column="0" Grid.Row="3" Text="Fill:"
HorizontalAlignment="Right" VerticalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strGenericTextBox" Grid.Column="1" Grid.Row="3" <TextBox Name="fillTextBox" Grid.Column="1" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="2" Grid.Row="3" Text="Reverse str:" <TextBlock Grid.Column="2" Grid.Row="3" Text="Bulk data:"
HorizontalAlignment="Right" VerticalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strReverseTextBox" Grid.Column="3" Grid.Row="3" <TextBox Name="denseTextBox" Grid.Column="3" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="4" Grid.Row="3" Text="Null-term str:" <TextBlock Grid.Column="4" Grid.Row="3" Text="Junk:"
HorizontalAlignment="Right" VerticalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strNullTermTextBox" Grid.Column="5" Grid.Row="3" <TextBox Name="junkTextBox" Grid.Column="5" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="6" Grid.Row="3" Text="DCI str:" <TextBlock Grid.Column="6" Grid.Row="3" Text="Align:"
HorizontalAlignment="Right" VerticalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strDciTextBox" Grid.Column="7" Grid.Row="3" <TextBox Name="alignTextBox" Grid.Column="7" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- fifth row --> <!-- fifth row -->
<TextBlock Grid.Column="0" Grid.Row="4" Text="1-byte len str:" <TextBlock Grid.Column="0" Grid.Row="4" Text="Generic str:"
HorizontalAlignment="Right" VerticalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strLen8TextBox" Grid.Column="1" Grid.Row="4" <TextBox Name="strGenericTextBox" Grid.Column="1" Grid.Row="4"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="2" Grid.Row="4" Text="2-byte len str:" <TextBlock Grid.Column="2" Grid.Row="4" Text="Reverse str:"
HorizontalAlignment="Right" VerticalAlignment="Center"/> HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strLen16TextBox" Grid.Column="3" Grid.Row="4" <TextBox Name="strReverseTextBox" Grid.Column="3" Grid.Row="4"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="4" Grid.Row="4" Text="Null-term str:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strNullTermTextBox" Grid.Column="5" Grid.Row="4"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="6" Grid.Row="4" Text="DCI str:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strDciTextBox" Grid.Column="7" Grid.Row="4"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- sixth row -->
<TextBlock Grid.Column="0" Grid.Row="5" Text="1-byte len str:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strLen8TextBox" Grid.Column="1" Grid.Row="5"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<TextBlock Grid.Column="2" Grid.Row="5" Text="2-byte len str:"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Name="strLen16TextBox" Grid.Column="3" Grid.Row="5"
VerticalAlignment="Center" Margin="{StaticResource TBS}" VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12" Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>

View File

@ -1099,6 +1099,8 @@ namespace SourceGen.WpfGui {
new TextBoxPropertyMap(defineBigData2TextBox, "DefineBigData2"), new TextBoxPropertyMap(defineBigData2TextBox, "DefineBigData2"),
new TextBoxPropertyMap(fillTextBox, "Fill"), new TextBoxPropertyMap(fillTextBox, "Fill"),
new TextBoxPropertyMap(denseTextBox, "Dense"), new TextBoxPropertyMap(denseTextBox, "Dense"),
new TextBoxPropertyMap(junkTextBox, "Junk"),
new TextBoxPropertyMap(alignTextBox, "Align"),
new TextBoxPropertyMap(strGenericTextBox, "StrGeneric"), new TextBoxPropertyMap(strGenericTextBox, "StrGeneric"),
new TextBoxPropertyMap(strReverseTextBox, "StrReverse"), new TextBoxPropertyMap(strReverseTextBox, "StrReverse"),
new TextBoxPropertyMap(strLen8TextBox, "StrLen8"), new TextBoxPropertyMap(strLen8TextBox, "StrLen8"),

View File

@ -39,6 +39,9 @@ limitations under the License.
<system:String x:Key="str_StringLen8">Strings prefixed with 8-bit length ({0})</system:String> <system:String x:Key="str_StringLen8">Strings prefixed with 8-bit length ({0})</system:String>
<system:String x:Key="str_StringLen16">Strings prefixed with 16-bit length ({0})</system:String> <system:String x:Key="str_StringLen16">Strings prefixed with 16-bit length ({0})</system:String>
<system:String x:Key="str_StringDci">Dextral character inverted ({0})</system:String> <system:String x:Key="str_StringDci">Dextral character inverted ({0})</system:String>
<system:String x:Key="str_AlignmentNone">arbitrary boundary</system:String>
<system:String x:Key="str_AlignmentItemFmt">{0}-byte boundary ({1})</system:String>
</Window.Resources> </Window.Resources>
<StackPanel Margin="8"> <StackPanel Margin="8">
@ -125,8 +128,14 @@ limitations under the License.
<Rectangle HorizontalAlignment="Stretch" Fill="LightGray" Height="2"/> <Rectangle HorizontalAlignment="Stretch" Fill="LightGray" Height="2"/>
<RadioButton Name="radioDenseHex" GroupName="Main" Content="Densely-_packed bytes" Margin="0,4,0,0" <RadioButton Name="radioDenseHex" GroupName="Main" Content="Densely-_packed bytes" Margin="0,4,0,0"
Checked="MainGroup_CheckedChanged"/> Checked="MainGroup_CheckedChanged"/>
<RadioButton Name="radioFill" GroupName="Main" Content="_Fill with value" Margin="0,4,0,0" <RadioButton Name="radioFill" GroupName="Main" Content="Area _filled with value" Margin="0,4,0,0"
Checked="MainGroup_CheckedChanged"/> Checked="MainGroup_CheckedChanged"/>
<StackPanel Orientation="Horizontal">
<RadioButton Name="radioJunk" GroupName="Main" Content="_Junk bytes, end aligned to" Margin="0,4,0,0"
Checked="MainGroup_CheckedChanged"/>
<ComboBox Name="junkAlignComboBox" Margin="8,-1,0,0" Width="200"
ItemsSource="{Binding JunkAlignmentItems}" DisplayMemberPath="Description"/>
</StackPanel>
<TextBlock Text="String" Margin="0,12,0,0"/> <TextBlock Text="String" Margin="0,12,0,0"/>
<Rectangle HorizontalAlignment="Stretch" Fill="LightGray" Height="2"/> <Rectangle HorizontalAlignment="Stretch" Fill="LightGray" Height="2"/>

View File

@ -74,6 +74,11 @@ namespace SourceGen.WpfGui {
/// </summary> /// </summary>
private SymbolTable mSymbolTable; private SymbolTable mSymbolTable;
/// <summary>
/// Map of offsets to addresses.
/// </summary>
private AddressMap mAddrMap;
/// <summary> /// <summary>
/// Formatter to use when displaying addresses and hex values. /// Formatter to use when displaying addresses and hex values.
/// </summary> /// </summary>
@ -100,6 +105,17 @@ namespace SourceGen.WpfGui {
} }
public StringEncodingItem[] StringEncodingItems { get; private set; } public StringEncodingItem[] StringEncodingItems { get; private set; }
public class JunkAlignmentItem {
public string Description { get; private set; }
public FormatDescriptor.SubType FormatSubType { get; private set; }
public JunkAlignmentItem(string descr, FormatDescriptor.SubType subFmt) {
Description = descr;
FormatSubType = subFmt;
}
}
public List<JunkAlignmentItem> JunkAlignmentItems { get; private set; }
// INotifyPropertyChanged implementation // INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "") { private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
@ -107,14 +123,15 @@ namespace SourceGen.WpfGui {
} }
public EditDataOperand(Window owner, byte[] fileData, SymbolTable symbolTable, public EditDataOperand(Window owner, DisasmProject project,
Asm65.Formatter formatter, TypedRangeSet trs, FormatDescriptor firstDesc) { Asm65.Formatter formatter, TypedRangeSet trs, FormatDescriptor firstDesc) {
InitializeComponent(); InitializeComponent();
Owner = owner; Owner = owner;
DataContext = this; DataContext = this;
mFileData = fileData; mFileData = project.FileData;
mSymbolTable = symbolTable; mSymbolTable = project.SymbolTable;
mAddrMap = project.AddrMap;
mFormatter = formatter; mFormatter = formatter;
mSelection = trs; mSelection = trs;
mFirstFormatDescriptor = firstDesc; mFirstFormatDescriptor = firstDesc;
@ -129,6 +146,40 @@ namespace SourceGen.WpfGui {
new StringEncodingItem(Res.Strings.SCAN_C64_SCREEN_CODE, new StringEncodingItem(Res.Strings.SCAN_C64_SCREEN_CODE,
TextScanMode.C64ScreenCode), TextScanMode.C64ScreenCode),
}; };
GetMinMaxAlignment(out FormatDescriptor.SubType min, out FormatDescriptor.SubType max);
//Debug.WriteLine("ALIGN: min=" + min + " max=" + max);
Debug.Assert(min == FormatDescriptor.SubType.None ^ // both or neither are None
max != FormatDescriptor.SubType.None);
int junkSel = 0;
string noAlign = (string)FindResource("str_AlignmentNone");
string alignFmt = (string)FindResource("str_AlignmentItemFmt");
JunkAlignmentItems = new List<JunkAlignmentItem>();
JunkAlignmentItems.Add(new JunkAlignmentItem(noAlign, FormatDescriptor.SubType.None));
if (min != FormatDescriptor.SubType.None) {
int index = 1;
// We assume the enum values are consecutive and ascending.
FormatDescriptor.SubType end = (FormatDescriptor.SubType)(((int)max) + 1);
while (min != end) {
int pwr = FormatDescriptor.AlignmentToPower(min);
string endStr = mFormatter.FormatHexValue(1 << pwr, 4);
JunkAlignmentItems.Add(new JunkAlignmentItem(
string.Format(alignFmt, 1 << pwr, endStr), min));
// See if this matches previous value.
if (mFirstFormatDescriptor != null &&
mFirstFormatDescriptor.FormatType == FormatDescriptor.Type.Junk &&
mFirstFormatDescriptor.FormatSubType == min) {
junkSel = index;
}
// Advance.
min = (FormatDescriptor.SubType)(((int)min) + 1);
index++;
}
}
junkAlignComboBox.SelectedIndex = junkSel;
} }
private void Window_Loaded(object sender, RoutedEventArgs e) { private void Window_Loaded(object sender, RoutedEventArgs e) {
@ -212,7 +263,8 @@ namespace SourceGen.WpfGui {
stringEncodingComboBox.SelectedItem = choice; stringEncodingComboBox.SelectedItem = choice;
} }
private void StringEncodingComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { private void StringEncodingComboBox_SelectionChanged(object sender,
SelectionChangedEventArgs e) {
if (!IsLoaded) { if (!IsLoaded) {
return; return;
} }
@ -287,6 +339,9 @@ namespace SourceGen.WpfGui {
symbolEntryTextBox.Focus(); symbolEntryTextBox.Focus();
} }
// Disable the alignment pop-up unless Junk is selected.
junkAlignComboBox.IsEnabled = (radioJunk.IsChecked == true);
bool isOk = true; bool isOk = true;
if (radioSimpleDataSymbolic.IsChecked == true) { if (radioSimpleDataSymbolic.IsChecked == true) {
// Just check for correct format. References to non-existent labels are allowed. // Just check for correct format. References to non-existent labels are allowed.
@ -302,6 +357,71 @@ namespace SourceGen.WpfGui {
#region Setup #region Setup
/// <summary>
/// Determines the minimum and maximum alignment values, based on the sizes of the
/// regions and the address they end on.
/// </summary>
/// <param name="min">Minimum allowed format, or None.</param>
/// <param name="max">Maximum allowed format, or None.</param>
private void GetMinMaxAlignment(out FormatDescriptor.SubType min,
out FormatDescriptor.SubType max) {
min = max = FormatDescriptor.SubType.None;
int maxLenPow = -1;
int minAlignPow = 65535;
IEnumerator<TypedRangeSet.TypedRange> iter = mSelection.RangeListIterator;
while (iter.MoveNext()) {
TypedRangeSet.TypedRange rng = iter.Current;
int length = rng.High - rng.Low + 1;
Debug.Assert(length > 0);
// The goal is to find an instruction that fills an entire region with zeroes
// or junk bytes for the sole purpose of ending at a specific boundary.
//
// If we have a 100-byte region that ends at address $103f (inclusive), it
// can't be the result of an assembler alignment directive. "align $40" would
// have stopped at $1000, "align $80" would have continued on to $107f.
//
// Alignment of junk whose last byte $103f could be due to Align2, Align4 (1-3
// bytes at $103d/e/f), Align8 (1-7 bytes at $1039-f), and so on, up to Align64.
// The size of the buffer determines the minimum value, the end address
// determines the maximum.
//
// Bear in mind that assembler alignment directives will do nothing if the
// address is already aligned: Align256 at $1000 generates no output. So we
// cannot use Align8 on a buffer of length 8.
// Count the trailing 1 bits in the address. This gets us the power of 2
// alignment value. Note alignPow will be zero if the last byte is stored at
// an even address.
int endAddress = mAddrMap.OffsetToAddress(rng.High) & 0x0000ffff;
int alignPow = BitTwiddle.CountTrailingZeroes(~endAddress);
// Round length up to next highest power of 2, and compute Log2(). Unfortunately
// .NET Standard 2.0 doesn't have Math.Log2(). Note we want the next-highest
// even if it's already a power of 2.
int lenRound = BitTwiddle.NextHighestPowerOf2(length);
int lenPow = BitTwiddle.CountTrailingZeroes(lenRound);
Debug.Assert(lenPow > 0); // length==1 -> lenRound=2 --> lenPow=1
// Want the biggest minimum value and the smallest maximum value.
if (maxLenPow < lenPow) {
maxLenPow = lenPow;
}
if (minAlignPow > alignPow) {
minAlignPow = alignPow;
}
if (maxLenPow > minAlignPow) {
return;
}
}
min = FormatDescriptor.PowerToAlignment(maxLenPow);
max = FormatDescriptor.PowerToAlignment(minAlignPow);
}
/// <summary> /// <summary>
/// Analyzes the selection to see which data formatting options are suitable. /// Analyzes the selection to see which data formatting options are suitable.
/// Disables radio buttons and updates labels. /// Disables radio buttons and updates labels.
@ -733,6 +853,9 @@ namespace SourceGen.WpfGui {
case FormatDescriptor.Type.Fill: case FormatDescriptor.Type.Fill:
preferredFormat = radioFill; preferredFormat = radioFill;
break; break;
case FormatDescriptor.Type.Junk:
preferredFormat = radioJunk;
break;
default: default:
// Should not be here. // Should not be here.
Debug.Assert(false); Debug.Assert(false);
@ -875,6 +998,10 @@ namespace SourceGen.WpfGui {
type = FormatDescriptor.Type.Dense; type = FormatDescriptor.Type.Dense;
} else if (radioFill.IsChecked == true) { } else if (radioFill.IsChecked == true) {
type = FormatDescriptor.Type.Fill; type = FormatDescriptor.Type.Fill;
} else if (radioJunk.IsChecked == true) {
type = FormatDescriptor.Type.Junk;
JunkAlignmentItem comboItem = (JunkAlignmentItem)junkAlignComboBox.SelectedItem;
subType = comboItem.FormatSubType;
} else if (radioStringMixed.IsChecked == true) { } else if (radioStringMixed.IsChecked == true) {
type = FormatDescriptor.Type.StringGeneric; type = FormatDescriptor.Type.StringGeneric;
subType = charSubType; subType = charSubType;