1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-07-02 04:29:28 +00:00

Improve the "common" expression formatter

To avoid confusing the assembler, expressions with a leading
parenthesis like "(foo & $ffff) + 1" are prefixed with a "0+".  This
is not necessary if the operand begins with a '#'.

(issue #16)
This commit is contained in:
Andy McFadden 2018-10-26 15:45:39 -07:00
parent 975ae1eb28
commit a8af7e8794
8 changed files with 92 additions and 53 deletions

View File

@ -344,9 +344,17 @@ namespace SourceGen.AppForms {
if (operandLen == 1 && mIsPcRelative) {
operandLen = 2;
}
PseudoOp.FormatNumericOpFlags flags;
if (mIsPcRelative) {
flags = PseudoOp.FormatNumericOpFlags.IsPcRel;
} else if (mShowHashPrefix) {
flags = PseudoOp.FormatNumericOpFlags.HasHashPrefix;
} else {
flags = PseudoOp.FormatNumericOpFlags.None;
}
string str = PseudoOp.FormatNumericOperand(mFormatter,
mProject.SymbolTable, null, dfd,
mOperandValue, operandLen, mIsPcRelative);
mOperandValue, operandLen, flags);
preview.Append(str);
if (sym.SymbolSource == Symbol.Source.Auto) {

View File

@ -364,7 +364,8 @@ namespace SourceGen.AsmGen {
opcodeStr = sDataOpNames.GetDefineData(length);
operand = RawData.GetWord(data, offset, length, false);
operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable,
mLocalizer.LabelMap, dfd, operand, length, false);
mLocalizer.LabelMap, dfd, operand, length,
PseudoOp.FormatNumericOpFlags.None);
break;
case FormatDescriptor.Type.NumericBE:
opcodeStr = sDataOpNames.GetDefineBigData(length);
@ -374,7 +375,8 @@ namespace SourceGen.AsmGen {
} else {
operand = RawData.GetWord(data, offset, length, true);
operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable,
mLocalizer.LabelMap, dfd, operand, length, false);
mLocalizer.LabelMap, dfd, operand, length,
PseudoOp.FormatNumericOpFlags.None);
}
break;
case FormatDescriptor.Type.Fill:

View File

@ -268,7 +268,8 @@ namespace SourceGen.AsmGen {
opcodeStr = sDataOpNames.GetDefineData(length);
operand = RawData.GetWord(data, offset, length, false);
operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable,
mLocalizer.LabelMap, dfd, operand, length, false);
mLocalizer.LabelMap, dfd, operand, length,
PseudoOp.FormatNumericOpFlags.None);
break;
case FormatDescriptor.Type.NumericBE:
opcodeStr = sDataOpNames.GetDefineBigData(length);
@ -278,7 +279,8 @@ namespace SourceGen.AsmGen {
} else {
operand = RawData.GetWord(data, offset, length, true);
operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable,
mLocalizer.LabelMap, dfd, operand, length, false);
mLocalizer.LabelMap, dfd, operand, length,
PseudoOp.FormatNumericOpFlags.None);
}
break;
case FormatDescriptor.Type.Fill:

View File

@ -336,7 +336,8 @@ namespace SourceGen.AsmGen {
opcodeStr = sDataOpNames.GetDefineData(length);
operand = RawData.GetWord(data, offset, length, false);
operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable,
mLocalizer.LabelMap, dfd, operand, length, false);
mLocalizer.LabelMap, dfd, operand, length,
PseudoOp.FormatNumericOpFlags.None);
break;
case FormatDescriptor.Type.NumericBE:
opcodeStr = sDataOpNames.GetDefineBigData(length);
@ -346,7 +347,8 @@ namespace SourceGen.AsmGen {
} else {
operand = RawData.GetWord(data, offset, length, true);
operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable,
mLocalizer.LabelMap, dfd, operand, length, false);
mLocalizer.LabelMap, dfd, operand, length,
PseudoOp.FormatNumericOpFlags.None);
}
break;
case FormatDescriptor.Type.Fill:

View File

@ -141,7 +141,8 @@ namespace SourceGen.AsmGen {
foreach (DefSymbol defSym in proj.ActiveDefSymbolList) {
// Use an operand length of 1 so things are shown as concisely as possible.
string valueStr = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
gen.Localizer.LabelMap, defSym.DataDescriptor, defSym.Value, 1, false);
gen.Localizer.LabelMap, defSym.DataDescriptor, defSym.Value, 1,
PseudoOp.FormatNumericOpFlags.None);
gen.OutputEquDirective(defSym.Label, valueStr, defSym.Comment);
}
@ -176,7 +177,7 @@ namespace SourceGen.AsmGen {
string formattedOperand = null;
int operandLen = instrLen - 1;
bool isPcRel = false;
PseudoOp.FormatNumericOpFlags opFlags = PseudoOp.FormatNumericOpFlags.None;
bool isPcRelBankWrap = false;
// Tweak branch instructions. We want to show the absolute address rather
@ -185,12 +186,16 @@ namespace SourceGen.AsmGen {
if (op.AddrMode == OpDef.AddressMode.PCRel) {
Debug.Assert(attr.OperandAddress >= 0);
operandLen = 2;
isPcRel = true;
opFlags = PseudoOp.FormatNumericOpFlags.IsPcRel;
} else if (op.AddrMode == OpDef.AddressMode.PCRelLong ||
op.AddrMode == OpDef.AddressMode.StackPCRelLong) {
isPcRel = true;
opFlags = PseudoOp.FormatNumericOpFlags.IsPcRel;
} else if (op.AddrMode == OpDef.AddressMode.Imm ||
op.AddrMode == OpDef.AddressMode.ImmLongA ||
op.AddrMode == OpDef.AddressMode.ImmLongXY) {
opFlags = PseudoOp.FormatNumericOpFlags.HasHashPrefix;
}
if (isPcRel) {
if (opFlags == PseudoOp.FormatNumericOpFlags.IsPcRel) {
int branchDist = attr.Address - attr.OperandAddress;
isPcRelBankWrap = branchDist > 32767 || branchDist < -32768;
}
@ -209,9 +214,11 @@ namespace SourceGen.AsmGen {
if (op.AddrMode == OpDef.AddressMode.BlockMove) {
// Special handling for the double-operand block move.
string opstr1 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
gen.Localizer.LabelMap, attr.DataDescriptor, operand >> 8, 1, false);
gen.Localizer.LabelMap, attr.DataDescriptor, operand >> 8, 1,
PseudoOp.FormatNumericOpFlags.None);
string opstr2 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
gen.Localizer.LabelMap, attr.DataDescriptor, operand & 0xff, 1, false);
gen.Localizer.LabelMap, attr.DataDescriptor, operand & 0xff, 1,
PseudoOp.FormatNumericOpFlags.None);
if (gen.Quirks.BlockMoveArgsReversed) {
string tmp = opstr1;
opstr1 = opstr2;
@ -221,7 +228,7 @@ namespace SourceGen.AsmGen {
} else {
formattedOperand = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
gen.Localizer.LabelMap, attr.DataDescriptor,
operandForSymbol, operandLen, isPcRel);
operandForSymbol, operandLen, opFlags);
}
} else {
// Show operand value in hex.

View File

@ -813,7 +813,8 @@ namespace SourceGen {
line = new Line(DefSymOffsetFromIndex(index), 0, Line.Type.EquDirective);
// Use an operand length of 1 so things are shown as concisely as possible.
string valueStr = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
null, defSym.DataDescriptor, defSym.Value, 1, false);
null, defSym.DataDescriptor, defSym.Value, 1,
PseudoOp.FormatNumericOpFlags.None);
string comment = formatter.FormatEolComment(defSym.Comment);
parts = FormattedParts.CreateEquDirective(defSym.Label,
formatter.FormatPseudoOp(opNames.EquDirective),
@ -1099,7 +1100,7 @@ namespace SourceGen {
string formattedOperand = null;
int operandLen = instrLen - 1;
bool isPcRel = false;
PseudoOp.FormatNumericOpFlags opFlags = PseudoOp.FormatNumericOpFlags.None;
// Tweak branch instructions. We want to show the absolute address rather
// than the relative offset (which happens with the OperandAddress assignment
@ -1107,10 +1108,14 @@ namespace SourceGen {
if (op.AddrMode == OpDef.AddressMode.PCRel) {
Debug.Assert(attr.OperandAddress >= 0);
operandLen = 2;
isPcRel = true;
opFlags = PseudoOp.FormatNumericOpFlags.IsPcRel;
} else if (op.AddrMode == OpDef.AddressMode.PCRelLong ||
op.AddrMode == OpDef.AddressMode.StackPCRelLong) {
isPcRel = true;
opFlags = PseudoOp.FormatNumericOpFlags.IsPcRel;
} else if (op.AddrMode == OpDef.AddressMode.Imm ||
op.AddrMode == OpDef.AddressMode.ImmLongA ||
op.AddrMode == OpDef.AddressMode.ImmLongXY) {
opFlags = PseudoOp.FormatNumericOpFlags.HasHashPrefix;
}
// Use the OperandAddress when available. This is important for relative branch
@ -1130,13 +1135,15 @@ namespace SourceGen {
if (op.AddrMode == OpDef.AddressMode.BlockMove) {
// Special handling for the double-operand block move.
string opstr1 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
null, attr.DataDescriptor, operand >> 8, 1, false);
null, attr.DataDescriptor, operand >> 8, 1,
PseudoOp.FormatNumericOpFlags.None);
string opstr2 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
null, attr.DataDescriptor, operand & 0xff, 1, false);
null, attr.DataDescriptor, operand & 0xff, 1,
PseudoOp.FormatNumericOpFlags.None);
formattedOperand = opstr1 + "," + opstr2;
} else {
formattedOperand = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
null, attr.DataDescriptor, operandForSymbol, operandLen, isPcRel);
null, attr.DataDescriptor, operandForSymbol, operandLen, opFlags);
}
} else {
// Show operand value in hex.

View File

@ -289,13 +289,13 @@ namespace SourceGen {
po.Opcode = opNames.GetDefineData(length);
operand = RawData.GetWord(data, offset, length, false);
po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd,
operand, length, false);
operand, length, FormatNumericOpFlags.None);
break;
case FormatDescriptor.Type.NumericBE:
po.Opcode = opNames.GetDefineBigData(length);
operand = RawData.GetWord(data, offset, length, true);
po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd,
operand, length, false);
operand, length, FormatNumericOpFlags.None);
break;
case FormatDescriptor.Type.Fill:
po.Opcode = opNames.Fill;
@ -509,6 +509,15 @@ namespace SourceGen {
}
}
/// <summary>
/// Special formatting flags for the FormatNumericOperand() method.
/// </summary>
public enum FormatNumericOpFlags {
None = 0,
IsPcRel, // opcode is PC relative, e.g. branch or PER
HasHashPrefix, // operand has a leading '#', avoiding ambiguity in some cases
}
/// <summary>
/// Format a numeric operand value according to the specified sub-format.
/// </summary>
@ -521,11 +530,10 @@ namespace SourceGen {
/// out of the code, for relative branches it's a 24-bit absolute address.</param>
/// <param name="operandLen">Length of operand, in bytes. For an instruction, this
/// does not include the opcode byte. For a relative branch, this will be 2.</param>
/// <param name="isPcRel">Set to true if the actual operand is a PC-relative value.
/// These get slightly different treatment.</param>
/// <param name="flags">Special handling.</param>
public static string FormatNumericOperand(Formatter formatter, SymbolTable symbolTable,
Dictionary<string, string> labelMap, FormatDescriptor dfd,
int operandValue, int operandLen, bool isPcRel) {
int operandValue, int operandLen, FormatNumericOpFlags flags) {
Debug.Assert(operandLen > 0);
int hexMinLen = operandLen * 2;
@ -547,15 +555,15 @@ namespace SourceGen {
switch (formatter.ExpressionMode) {
case Formatter.FormatConfig.ExpressionMode.Common:
FormatNumericSymbolCommon(formatter, sym, labelMap,
dfd, operandValue, operandLen, isPcRel, sb);
dfd, operandValue, operandLen, flags, sb);
break;
case Formatter.FormatConfig.ExpressionMode.Cc65:
FormatNumericSymbolCc65(formatter, sym, labelMap,
dfd, operandValue, operandLen, isPcRel, sb);
dfd, operandValue, operandLen, flags, sb);
break;
case Formatter.FormatConfig.ExpressionMode.Merlin:
FormatNumericSymbolMerlin(formatter, sym, labelMap,
dfd, operandValue, operandLen, isPcRel, sb);
dfd, operandValue, operandLen, flags, sb);
break;
default:
Debug.Assert(false, "Unknown expression mode " +
@ -578,7 +586,7 @@ namespace SourceGen {
/// </summary>
private static void FormatNumericSymbolCommon(Formatter formatter, Symbol sym,
Dictionary<string, string> labelMap, FormatDescriptor dfd,
int operandValue, int operandLen, bool isPcRel, StringBuilder sb) {
int operandValue, int operandLen, FormatNumericOpFlags flags, StringBuilder sb) {
// We could have some simple code that generated correct output, shifting and
// masking every time, but that's ugly and annoying. For single-byte ops we can
// just use the byte-select operators, for wider ops we get only as fancy as we
@ -645,7 +653,7 @@ namespace SourceGen {
rightShift = 0;
}
if (isPcRel) {
if (flags == FormatNumericOpFlags.IsPcRel) {
// PC-relative operands are funny, because an 8- or 16-bit value is always
// expanded to 24 bits. We output a 16-bit value that the assembler will
// convert back to 8-bit or 16-bit. In any event, the bank byte is never
@ -670,10 +678,13 @@ namespace SourceGen {
// ((label >> rightShift) & mask) [+ adj]
if (rightShift != 0 || needMask) {
if (flags != FormatNumericOpFlags.HasHashPrefix) {
sb.Append("0+");
}
if (rightShift != 0 && needMask) {
sb.Append("0+((");
sb.Append("((");
} else {
sb.Append("0+(");
sb.Append("(");
}
}
sb.Append(symLabel);
@ -705,7 +716,7 @@ namespace SourceGen {
/// </summary>
private static void FormatNumericSymbolCc65(Formatter formatter, Symbol sym,
Dictionary<string, string> labelMap, FormatDescriptor dfd,
int operandValue, int operandLen, bool isPcRel, StringBuilder sb) {
int operandValue, int operandLen, FormatNumericOpFlags flags, StringBuilder sb) {
// The key difference between cc65 and other assemblers with general expressions
// is that the bitwise shift and AND operators have higher precedence than the
// arithmetic ops like add and subtract. (The bitwise ops are equal to multiply
@ -758,7 +769,7 @@ namespace SourceGen {
shOp = "";
}
if (isPcRel) {
if (flags == FormatNumericOpFlags.IsPcRel) {
// PC-relative operands are funny, because an 8- or 16-bit value is always
// expanded to 24 bits. We output a 16-bit value that the assembler will
// convert back to 8-bit or 16-bit. In any event, the bank byte is never
@ -796,7 +807,7 @@ namespace SourceGen {
/// </summary>
private static void FormatNumericSymbolMerlin(Formatter formatter, Symbol sym,
Dictionary<string, string> labelMap, FormatDescriptor dfd,
int operandValue, int operandLen, bool isPcRel, StringBuilder sb) {
int operandValue, int operandLen, FormatNumericOpFlags flags, StringBuilder sb) {
// Merlin expressions are compatible with the original 8-bit Merlin. They're
// evaluated from left to right, with (almost) no regard for operator precedence.
//

View File

@ -61,21 +61,21 @@ start clc
lda #zip+16
lda #zip+64
lda #absl
lda #0+(absl >> 8)
lda #(absl >> 8)
lda #absl-4096
lda #0+(absl >> 8)-16
lda #0+(absl >> 16)
lda #(absl >> 8)-16
lda #(absl >> 16)
lda #absh
lda #0+(absh >> 8)
lda #(absh >> 8)
lda #absh-61440
lda #0+(absh >> 8)+16
lda #0+(absh >> 16)+1
lda #0+(start & $ffff)
lda #0+(start >> 8)
lda #0+(start >> 16)
lda #0+(biggie & $ffff)
lda #0+(biggie >> 8)
lda #0+(biggie >> 16)
lda #(absh >> 8)+16
lda #(absh >> 16)+1
lda #(start & $ffff)
lda #(start >> 8)
lda #(start >> 16)
lda #(biggie & $ffff)
lda #(biggie >> 8)
lda #(biggie >> 16)
bra skipdata
.byte zip
@ -113,7 +113,7 @@ start clc
.dword 0+(start >> 8)
.dword 0+(start >> 16)
skipdata lda #0+(biggie >> 16)-1
skipdata lda #(biggie >> 16)-1
mvn `biggie,(`biggie)-17
mvp `start,(`start)+17
mvn 18,1
@ -201,9 +201,9 @@ L118E lda #<thirty2+2
rep #$30
.al
.xl
lda #0+(thirty2 & $ffff)+3
lda #0+((thirty2 >> 8) & $ffff)+4
lda #0+(thirty2 >> 16)
lda #(thirty2 & $ffff)+3
lda #((thirty2 >> 8) & $ffff)+4
lda #(thirty2 >> 16)
rts
.here