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;
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
{ "Fill", "!fill" },
{ "Dense", "!hex" },
//Junk
{ "Align", "!align" },
{ "StrGeneric", "!text" }, // can use !xor for high ASCII
//StrReverse
//StrNullTerm
@ -406,6 +408,25 @@ namespace SourceGen.AsmGen {
opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr);
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.StringReverse:
case FormatDescriptor.Type.StringNullTerm:

View File

@ -115,6 +115,7 @@ namespace SourceGen.AsmGen {
//DefineBigData4
{ "Fill", ".res" },
//Dense // no equivalent, use .byte with comma-separated args
//Junk
{ "StrGeneric", ".byte" },
//StrReverse
{ "StrNullTerm", ".asciiz" },
@ -434,6 +435,22 @@ namespace SourceGen.AsmGen {
opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr);
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.StringReverse:
case FormatDescriptor.Type.StringNullTerm:

View File

@ -106,6 +106,8 @@ namespace SourceGen.AsmGen {
//DefineBigData4
{ "Fill", "ds" },
{ "Dense", "hex" },
//Junk
//Align
{ "StrGeneric", "asc" },
{ "StrReverse", "rev" },
//StrNullTerm
@ -269,6 +271,24 @@ namespace SourceGen.AsmGen {
opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr);
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.StringReverse:
case FormatDescriptor.Type.StringNullTerm:

View File

@ -132,6 +132,8 @@ namespace SourceGen.AsmGen {
//DefineBigData4
{ "Fill", ".fill" },
//Dense // no equivalent, use .byte with comma-separated args
//Junk
{ "Align", ".align" },
{ "StrGeneric", ".text" },
//StrReverse
{ "StrNullTerm", ".null" },
@ -463,6 +465,25 @@ namespace SourceGen.AsmGen {
opcodeStr = operandStr = null;
OutputDenseHex(offset, length, labelStr, commentStr);
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.StringReverse:
case FormatDescriptor.Type.StringNullTerm:

View File

@ -422,5 +422,32 @@ namespace SourceGen.AsmGen {
// 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)
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>
@ -88,8 +89,26 @@ namespace SourceGen {
// Dense; no sub-types
// Fill; default is non-ignore
Ignore // TODO(someday): use this for "don't care" sections
// Fill; no sub-types
// 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).
@ -169,6 +188,7 @@ namespace SourceGen {
Debug.Assert(length > 0);
Debug.Assert(length <= MAX_NUMERIC_LEN || !IsNumeric);
Debug.Assert(fmt != Type.Default || length == 1);
Debug.Assert(subFmt == SubType.None || (fmt != Type.Junk) ^ IsJunkSubType(subFmt));
Length = length;
FormatType = fmt;
@ -200,8 +220,12 @@ namespace SourceGen {
/// <param name="length">Length, in bytes.</param>
/// <param name="fmt">Format 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) {
if (subFmt != SubType.None && !((fmt != Type.Junk) ^ IsJunkSubType(subFmt))) {
return null;
}
DebugCreateCount++;
DebugPrefabCount++;
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>
/// Generates a string describing the format, suitable for use in the UI.
/// </summary>
@ -432,6 +491,9 @@ namespace SourceGen {
case Type.Fill:
retstr += "Fill";
break;
case Type.Junk:
retstr += "Unaligned junk";
break;
default:
// strings handled earlier
retstr += "???";
@ -469,6 +531,24 @@ namespace SourceGen {
case SubType.C64Screen:
retstr += "Numeric, C64 Screen";
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:
retstr += "???";

View File

@ -1988,10 +1988,9 @@ namespace SourceGen {
TypedRangeSet.Tuple firstOffset = iter.Current;
mProject.OperandFormats.TryGetValue(firstOffset.Value, out FormatDescriptor dfd);
EditDataOperand dlg = new EditDataOperand(mMainWin, mProject.FileData,
mProject.SymbolTable, mOutputFormatter, trs, dfd);
dlg.ShowDialog();
if (dlg.DialogResult == true) {
EditDataOperand dlg =
new EditDataOperand(mMainWin, mProject, mOutputFormatter, trs, dfd);
if (dlg.ShowDialog() == true) {
// Merge the changes into the OperandFormats list. We need to remove all
// FormatDescriptors that overlap the selected region. We don't need to
// 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,
out FormatDescriptor dfd)) {
report.Add(FileLoadItem.Type.Warning,
string.Format(Res.Strings.ERR_BAD_FD_FMT, intKey));
continue;
}
// 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);
dfd = FormatDescriptor.Create(sfd.Length, format, subFormat);
return true;
return dfd != null;
}
try {
@ -842,7 +844,6 @@ namespace SourceGen {
subFormat = (FormatDescriptor.SubType)Enum.Parse(
typeof(FormatDescriptor.SubType), sfd.SubFormat);
}
} catch (ArgumentException) {
report.Add(FileLoadItem.Type.Warning, Res.Strings.ERR_BAD_FD_FORMAT +
": " + sfd.Format + "/" + sfd.SubFormat);
@ -865,7 +866,7 @@ namespace SourceGen {
new WeakSymbolRef(sfd.SymbolRef.Label, part),
format == FormatDescriptor.Type.NumericBE);
}
return true;
return dfd != null;
}
/// <summary>

View File

@ -78,6 +78,8 @@ namespace SourceGen {
public string DefineBigData4 { get; private set; }
public string Fill { 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 StrReverse { get; private set; }
public string StrLen8 { get; private set; }
@ -125,6 +127,8 @@ namespace SourceGen {
a.DefineBigData4 == b.DefineBigData4 &&
a.Fill == b.Fill &&
a.Dense == b.Dense &&
a.Junk == b.Junk &&
a.Align == b.Align &&
a.StrGeneric == b.StrGeneric &&
a.StrReverse == b.StrReverse &&
a.StrLen8 == b.StrLen8 &&
@ -234,6 +238,8 @@ namespace SourceGen {
{ "DefineBigData4", ".dbd4" },
{ "Fill", ".fill" },
{ "Dense", ".bulk" },
{ "Junk", ".junk" },
{ "Align", ".align" },
{ "StrGeneric", ".str" },
{ "StrReverse", ".rstr" },
@ -265,6 +271,7 @@ namespace SourceGen {
case FormatDescriptor.Type.NumericLE:
case FormatDescriptor.Type.NumericBE:
case FormatDescriptor.Type.Fill:
case FormatDescriptor.Type.Junk:
return 1;
case FormatDescriptor.Type.Dense: {
// no delimiter, two output bytes per input byte
@ -345,6 +352,17 @@ namespace SourceGen {
po.Opcode = opNames.Fill;
po.Operand = length + "," + formatter.FormatHexValue(data[offset], 2);
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: {
int maxPerLine = MAX_OPERAND_LEN / 2;
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.
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
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>
<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>
<li>.EQ - defines a symbol's value. These are generated automatically
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
(65816 only). Note this doesn't change the actual width, just tells
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>
<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
little-endian value.</li>
<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.
Useful for chunks of graphics data.</li>
<li>.FILL - a series of identical bytes. The operand

View File

@ -1,8 +1,8 @@
### 6502bench SourceGen dis65 v1.0 ###
{
"FileDataLength":631,"FileDataCrc32":-1812792607,"ProjectProps":{
"CpuName":"6502","IncludeUndocumentedInstr":false,"EntryFlags":13566159,"AnalysisParams":{
"AnalyzeUncategorizedData":true,"MinCharsForString":4},
"_ContentVersion":2,"FileDataLength":1024,"FileDataCrc32":-1387500320,"ProjectProps":{
"CpuName":"6502","IncludeUndocumentedInstr":false,"TwoByteBrk":false,"EntryFlags":13566159,"AutoLabelStyle":"Simple","AnalysisParams":{
"AnalyzeUncategorizedData":true,"DefaultTextScanMode":"LowHighAscii","MinCharsForString":4,"SeekNearbyTargets":false,"SmartPlpHandling":true},
"PlatformSymbolFileIdentifiers":[],"ExtensionScriptFileIdentifiers":[],"ProjectSyms":{
}},
"AddressMap":[{
@ -10,10 +10,10 @@
"Low":0,"High":0,"Hint":"Code"}],"StatusFlagOverrides":{
},
"Comments":{
"566":"comment"},
"566":"comment","882":"incorrect alignment"},
"LongComments":{
"-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":{
},
"UserLabels":{
@ -53,4 +53,20 @@
"555":{
"Length":10,"Format":"Dense","SubFormat":"None","SymbolRef":null},
"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"
* = $1000
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 $ff,$ee,$dd,$cc,$bb,$aa,$99,$88,$77,$66,$55,$44,$33,$22,$11,$00
.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
rts
@ -28,3 +29,14 @@
LABEL hex 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff ;comment
hex 00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100
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
* = $1000
rts
@ -29,3 +30,14 @@
LABEL !hex 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff ;comment
!hex 00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100
!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"
; .segment "SEG000"
.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 $ff,$ee,$dd,$cc,$bb,$aa,$99,$88,$77,$66,$55,$44,$33,$22,$11,$00
.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
MEMORY {
MAIN: file=%O, start=%S, size=65536;
# MEM000: file=%O, start=$1000, size=631;
# MEM000: file=%O, start=$1000, size=1024;
}
SEGMENTS {
CODE: load=MAIN, type=rw;

View File

@ -15,26 +15,38 @@
dfb $11,$22,$33 ;.dbd3
dfb $11,$22,$33,$44 ;.dbd4
ds 2
ds 2 ;.fill
dfb $80
ds 3
ds 3 ;.fill
dfb $80
ds 4
ds 4 ;.fill
dfb $80
ds 5
ds 5 ;.fill
dfb $80
ds 256
ds 256 ;.fill
dfb $80
ds 257,$cc
ds 257,$cc ;.fill
hex 11
hex 11 ;.bulk
dfb $80
hex 11223344556677889900
hex 11223344556677889900 ;.bulk
dfb $80
hex 00112233445566778899aabbccddeeff
hex 00112233445566778899aabbccddeeff
hex 00112233445566778899aabbccddeeff ;4 lines .bulk
hex 00112233445566778899aabbccddeeff ;add a comment
hex 00112233445566778899aabbccddeeff
hex ffeeddccbbaa99887766554433221100
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="*"/>
</Grid.RowDefinitions>
<!-- first row -->
@ -673,63 +674,77 @@ limitations under the License.
FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- 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="two bytes:" HorizontalAlignment="Right"/>
</StackPanel>
<TextBox Name="defineBigData2TextBox" Grid.Column="1" 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"
<TextBox Name="defineBigData2TextBox" Grid.Column="3" Grid.Row="2"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- 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"/>
<TextBox Name="strGenericTextBox" Grid.Column="1" Grid.Row="3"
<TextBox Name="fillTextBox" Grid.Column="1" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
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"/>
<TextBox Name="strReverseTextBox" Grid.Column="3" Grid.Row="3"
<TextBox Name="denseTextBox" Grid.Column="3" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
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"/>
<TextBox Name="strNullTermTextBox" Grid.Column="5" Grid.Row="3"
<TextBox Name="junkTextBox" Grid.Column="5" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
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"/>
<TextBox Name="strDciTextBox" Grid.Column="7" Grid.Row="3"
<TextBox Name="alignTextBox" Grid.Column="7" Grid.Row="3"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>
<!-- 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"/>
<TextBox Name="strLen8TextBox" Grid.Column="1" Grid.Row="4"
<TextBox Name="strGenericTextBox" Grid.Column="1" Grid.Row="4"
VerticalAlignment="Center" Margin="{StaticResource TBS}"
Text=".placeho" MaxLength="12"
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"/>
<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}"
Text=".placeho" MaxLength="12"
FontFamily="{StaticResource GeneralMonoFont}"/>

View File

@ -1099,6 +1099,8 @@ namespace SourceGen.WpfGui {
new TextBoxPropertyMap(defineBigData2TextBox, "DefineBigData2"),
new TextBoxPropertyMap(fillTextBox, "Fill"),
new TextBoxPropertyMap(denseTextBox, "Dense"),
new TextBoxPropertyMap(junkTextBox, "Junk"),
new TextBoxPropertyMap(alignTextBox, "Align"),
new TextBoxPropertyMap(strGenericTextBox, "StrGeneric"),
new TextBoxPropertyMap(strReverseTextBox, "StrReverse"),
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_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_AlignmentNone">arbitrary boundary</system:String>
<system:String x:Key="str_AlignmentItemFmt">{0}-byte boundary ({1})</system:String>
</Window.Resources>
<StackPanel Margin="8">
@ -125,8 +128,14 @@ limitations under the License.
<Rectangle HorizontalAlignment="Stretch" Fill="LightGray" Height="2"/>
<RadioButton Name="radioDenseHex" GroupName="Main" Content="Densely-_packed bytes" Margin="0,4,0,0"
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"/>
<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"/>
<Rectangle HorizontalAlignment="Stretch" Fill="LightGray" Height="2"/>

View File

@ -74,6 +74,11 @@ namespace SourceGen.WpfGui {
/// </summary>
private SymbolTable mSymbolTable;
/// <summary>
/// Map of offsets to addresses.
/// </summary>
private AddressMap mAddrMap;
/// <summary>
/// Formatter to use when displaying addresses and hex values.
/// </summary>
@ -100,6 +105,17 @@ namespace SourceGen.WpfGui {
}
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
public event PropertyChangedEventHandler PropertyChanged;
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) {
InitializeComponent();
Owner = owner;
DataContext = this;
mFileData = fileData;
mSymbolTable = symbolTable;
mFileData = project.FileData;
mSymbolTable = project.SymbolTable;
mAddrMap = project.AddrMap;
mFormatter = formatter;
mSelection = trs;
mFirstFormatDescriptor = firstDesc;
@ -129,6 +146,40 @@ namespace SourceGen.WpfGui {
new StringEncodingItem(Res.Strings.SCAN_C64_SCREEN_CODE,
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) {
@ -212,7 +263,8 @@ namespace SourceGen.WpfGui {
stringEncodingComboBox.SelectedItem = choice;
}
private void StringEncodingComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
private void StringEncodingComboBox_SelectionChanged(object sender,
SelectionChangedEventArgs e) {
if (!IsLoaded) {
return;
}
@ -287,6 +339,9 @@ namespace SourceGen.WpfGui {
symbolEntryTextBox.Focus();
}
// Disable the alignment pop-up unless Junk is selected.
junkAlignComboBox.IsEnabled = (radioJunk.IsChecked == true);
bool isOk = true;
if (radioSimpleDataSymbolic.IsChecked == true) {
// Just check for correct format. References to non-existent labels are allowed.
@ -302,6 +357,71 @@ namespace SourceGen.WpfGui {
#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>
/// Analyzes the selection to see which data formatting options are suitable.
/// Disables radio buttons and updates labels.
@ -733,6 +853,9 @@ namespace SourceGen.WpfGui {
case FormatDescriptor.Type.Fill:
preferredFormat = radioFill;
break;
case FormatDescriptor.Type.Junk:
preferredFormat = radioJunk;
break;
default:
// Should not be here.
Debug.Assert(false);
@ -875,6 +998,10 @@ namespace SourceGen.WpfGui {
type = FormatDescriptor.Type.Dense;
} else if (radioFill.IsChecked == true) {
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) {
type = FormatDescriptor.Type.StringGeneric;
subType = charSubType;