mirror of
https://github.com/fadden/6502bench.git
synced 2025-01-30 06:32:24 +00:00
Progress toward 64tass expression support
Gave cc65 its own expression generator, as the precedence table seems atypical if not unique. Configured 64tass to use the "simple" expression mode. Added some operations on a 32-bit constant to 2007-labels-and-symbols to exercise the current worst-case expression (shift + AND + add). Tweaked the Merlin expression generator to handle it. (issue #16)
This commit is contained in:
parent
f26a03869a
commit
61914c8f79
@ -54,6 +54,7 @@ namespace Asm65 {
|
||||
public bool mUpperOperandA; // display acc operand in upper case?
|
||||
public bool mUpperOperandS; // display stack operand in upper case?
|
||||
public bool mUpperOperandXY; // display index register operand in upper case?
|
||||
public bool mBankSelectBackQuote; // use '`' rather than '^' for bank select?
|
||||
public bool mAddSpaceLongComment; // insert space after delimiter for long comments?
|
||||
|
||||
// functional changes to assembly output
|
||||
@ -79,7 +80,8 @@ namespace Asm65 {
|
||||
public enum CharConvMode { Unknown = 0, PlainAscii, HighLowAscii };
|
||||
public CharConvMode mHexDumpCharConvMode; // character conversion mode for dumps
|
||||
|
||||
public enum ExpressionMode { Unknown = 0, Simple, Merlin };
|
||||
// Hopefully we don't need a separate mode for every assembler in existence.
|
||||
public enum ExpressionMode { Unknown = 0, Simple, Cc65, Merlin };
|
||||
public ExpressionMode mExpressionMode; // symbol rendering mode
|
||||
|
||||
// Deserialization helper.
|
||||
@ -601,22 +603,6 @@ namespace Asm65 {
|
||||
return fmt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a pseudo-opcode.
|
||||
/// </summary>
|
||||
/// <param name="opstr">Pseudo-op string to format.</param>
|
||||
/// <returns>Formatted string.</returns>
|
||||
public string FormatPseudoOp(string opstr) {
|
||||
if (!mPseudoOpStrings.TryGetValue(opstr, out string result)) {
|
||||
if (mFormatConfig.mUpperPseudoOpcodes) {
|
||||
result = mPseudoOpStrings[opstr] = opstr.ToUpperInvariant();
|
||||
} else {
|
||||
result = mPseudoOpStrings[opstr] = opstr;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats the instruction operand.
|
||||
/// </summary>
|
||||
@ -634,6 +620,22 @@ namespace Asm65 {
|
||||
return string.Format(format, contents);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a pseudo-opcode.
|
||||
/// </summary>
|
||||
/// <param name="opstr">Pseudo-op string to format.</param>
|
||||
/// <returns>Formatted string.</returns>
|
||||
public string FormatPseudoOp(string opstr) {
|
||||
if (!mPseudoOpStrings.TryGetValue(opstr, out string result)) {
|
||||
if (mFormatConfig.mUpperPseudoOpcodes) {
|
||||
result = mPseudoOpStrings[opstr] = opstr.ToUpperInvariant();
|
||||
} else {
|
||||
result = mPseudoOpStrings[opstr] = opstr;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a format string for N hex bytes.
|
||||
/// </summary>
|
||||
|
@ -611,7 +611,7 @@ namespace SourceGen.AppForms {
|
||||
private void shiftAfterAdjustCheckBox_CheckedChanged(object sender, EventArgs e) {
|
||||
string mode = useMerlinExpressions.Checked ?
|
||||
Asm65.Formatter.FormatConfig.ExpressionMode.Merlin.ToString() :
|
||||
Asm65.Formatter.FormatConfig.ExpressionMode.Simple.ToString();
|
||||
Asm65.Formatter.FormatConfig.ExpressionMode.Cc65.ToString();
|
||||
mSettings.SetString(AppSettings.FMT_EXPRESSION_MODE, mode);
|
||||
SetDirty(true);
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ namespace SourceGen.AsmGen {
|
||||
config.mFullLineCommentDelimiterBase = ";";
|
||||
config.mBoxLineCommentDelimiter = ";";
|
||||
config.mAllowHighAsciiCharConst = false;
|
||||
config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Simple;
|
||||
config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Cc65;
|
||||
}
|
||||
|
||||
// IGenerator
|
||||
|
@ -168,6 +168,15 @@ namespace SourceGen.AsmGen {
|
||||
/// Configures the assembler-specific format items.
|
||||
/// </summary>
|
||||
private void SetFormatConfigValues(ref Formatter.FormatConfig config) {
|
||||
// Must be lower case when --case-sensitive is used.
|
||||
config.mUpperOpcodes = false;
|
||||
config.mUpperPseudoOpcodes = false;
|
||||
config.mUpperOperandA = false;
|
||||
config.mUpperOperandS = false;
|
||||
config.mUpperOperandXY = false;
|
||||
|
||||
config.mBankSelectBackQuote = true;
|
||||
|
||||
config.mForceAbsOpcodeSuffix = string.Empty;
|
||||
config.mForceLongOpcodeSuffix = string.Empty;
|
||||
config.mForceAbsOperandPrefix = "@w"; // word
|
||||
@ -176,11 +185,6 @@ namespace SourceGen.AsmGen {
|
||||
config.mFullLineCommentDelimiterBase = ";";
|
||||
config.mBoxLineCommentDelimiter = ";";
|
||||
config.mAllowHighAsciiCharConst = false;
|
||||
config.mUpperOpcodes = false;
|
||||
config.mUpperPseudoOpcodes = false;
|
||||
config.mUpperOperandA = false;
|
||||
config.mUpperOperandS = false;
|
||||
config.mUpperOperandXY = false;
|
||||
config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Simple;
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,8 @@ namespace SourceGen.AsmGen {
|
||||
operandForSymbol = attr.OperandAddress;
|
||||
}
|
||||
|
||||
// Check Length to watch for bogus descriptors (?)
|
||||
// Check Length to watch for bogus descriptors. (ApplyFormatDescriptors() should
|
||||
// now be screening bad descriptors out, so we may not need the Length test.)
|
||||
if (attr.DataDescriptor != null && attr.Length == attr.DataDescriptor.Length) {
|
||||
// Format operand as directed.
|
||||
if (op.AddrMode == OpDef.AddressMode.BlockMove) {
|
||||
|
@ -541,6 +541,10 @@ namespace SourceGen {
|
||||
FormatNumericSymbolSimple(formatter, sym, labelMap,
|
||||
dfd, operandValue, operandLen, isPcRel, sb);
|
||||
break;
|
||||
case Formatter.FormatConfig.ExpressionMode.Cc65:
|
||||
FormatNumericSymbolCc65(formatter, sym, labelMap,
|
||||
dfd, operandValue, operandLen, isPcRel, sb);
|
||||
break;
|
||||
case Formatter.FormatConfig.ExpressionMode.Merlin:
|
||||
FormatNumericSymbolMerlin(formatter, sym, labelMap,
|
||||
dfd, operandValue, operandLen, isPcRel, sb);
|
||||
@ -584,7 +588,11 @@ namespace SourceGen {
|
||||
string selOp;
|
||||
if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank) {
|
||||
symbolValue = (sym.Value >> 16) & 0xff;
|
||||
selOp = "^";
|
||||
if (formatter.Config.mBankSelectBackQuote) {
|
||||
selOp = "`";
|
||||
} else {
|
||||
selOp = "^";
|
||||
}
|
||||
} else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High) {
|
||||
symbolValue = (sym.Value >> 8) & 0xff;
|
||||
selOp = ">";
|
||||
@ -651,16 +659,113 @@ namespace SourceGen {
|
||||
sb.Append(formatter.FormatAdjustment(adjustment));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format the symbol and adjustment using cc65 expression syntax.
|
||||
/// </summary>
|
||||
private static void FormatNumericSymbolCc65(Formatter formatter, Symbol sym,
|
||||
Dictionary<string, string> labelMap, FormatDescriptor dfd,
|
||||
int operandValue, int operandLen, bool isPcRel, 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
|
||||
// and divide.) This means that, if we want to mask off the low 16 bits and add one
|
||||
// to a label, we can write "start & $ffff + 1" rather than "(start & $ffff) + 1".
|
||||
//
|
||||
// This is particularly convenient for PEA, since "PEA (start & $ffff)" looks like
|
||||
// we're trying to use a (non-existent) indirect form of PEA. We can write things
|
||||
// in a simpler way.
|
||||
|
||||
int adjustment, symbolValue;
|
||||
|
||||
string symLabel = sym.Label;
|
||||
if (labelMap != null && labelMap.TryGetValue(symLabel, out string newLabel)) {
|
||||
symLabel = newLabel;
|
||||
}
|
||||
|
||||
if (operandLen == 1) {
|
||||
// Use the byte-selection operator to get the right piece.
|
||||
string selOp;
|
||||
if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank) {
|
||||
symbolValue = (sym.Value >> 16) & 0xff;
|
||||
selOp = "^";
|
||||
} else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High) {
|
||||
symbolValue = (sym.Value >> 8) & 0xff;
|
||||
selOp = ">";
|
||||
} else {
|
||||
symbolValue = sym.Value & 0xff;
|
||||
if (symbolValue == sym.Value) {
|
||||
selOp = string.Empty;
|
||||
} else {
|
||||
selOp = "<";
|
||||
}
|
||||
}
|
||||
sb.Append(selOp);
|
||||
sb.Append(symLabel);
|
||||
|
||||
operandValue &= 0xff;
|
||||
} else if (operandLen <= 4) {
|
||||
// Operands and values should be 8/16/24 bit unsigned quantities. 32-bit
|
||||
// support is really there so you can have a 24-bit pointer in a 32-bit hole.
|
||||
// Might need to adjust this if 32-bit signed quantities become interesting.
|
||||
uint mask = 0xffffffff >> ((4 - operandLen) * 8);
|
||||
string shOp;
|
||||
if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank) {
|
||||
symbolValue = (sym.Value >> 16);
|
||||
shOp = " >> 16";
|
||||
} else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High) {
|
||||
symbolValue = (sym.Value >> 8);
|
||||
shOp = " >> 8";
|
||||
} else {
|
||||
symbolValue = sym.Value;
|
||||
shOp = "";
|
||||
}
|
||||
|
||||
if (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
|
||||
// relevant to our computations.
|
||||
operandValue &= 0xffff;
|
||||
symbolValue &= 0xffff;
|
||||
}
|
||||
|
||||
sb.Append(symLabel);
|
||||
sb.Append(shOp);
|
||||
if (symbolValue > mask) {
|
||||
// Post-shift value won't fit in an operand-size box.
|
||||
symbolValue = (int)(symbolValue & mask);
|
||||
sb.Append(" & ");
|
||||
sb.Append(formatter.FormatHexValue((int)mask, 2));
|
||||
}
|
||||
|
||||
if (sb.Length != symLabel.Length) {
|
||||
sb.Append(' ');
|
||||
}
|
||||
|
||||
operandValue = (int)(operandValue & mask);
|
||||
} else {
|
||||
Debug.Assert(false, "bad numeric len");
|
||||
sb.Append("?????");
|
||||
symbolValue = 0;
|
||||
}
|
||||
|
||||
adjustment = operandValue - symbolValue;
|
||||
|
||||
sb.Append(formatter.FormatAdjustment(adjustment));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format the symbol and adjustment using Merlin expression syntax.
|
||||
/// </summary>
|
||||
private static void FormatNumericSymbolMerlin(Formatter formatter, Symbol sym,
|
||||
Dictionary<string, string> labelMap, FormatDescriptor dfd,
|
||||
int operandValue, int operandLen, bool isPcRel, 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.
|
||||
//
|
||||
// The part-selection operators differ from "simple" in two ways:
|
||||
// (1) They always happen last. If FOO=$10f0, "#>FOO+$18" == $11. (Strangely,
|
||||
// all other operators are evaluated from left to right, with no concept
|
||||
// of operator precedence.)
|
||||
// (1) They always happen last. If FOO=$10f0, "#>FOO+$18" == $11. One of the
|
||||
// few cases where left-to-right evaluation is overridden.
|
||||
// (2) They select words, not bytes. If FOO=$123456, "#>FOO" is $1234. This is
|
||||
// best thought of as a shift operator, rather than byte-selection. For
|
||||
// 8-bit code this doesn't matter.
|
||||
@ -676,13 +781,13 @@ namespace SourceGen {
|
||||
|
||||
// If we add or subtract an adjustment, it will be done on the full value, which
|
||||
// is then shifted to the appropriate part. So we need to left-shift the operand
|
||||
// value to match. We fill in the low bytes with the contes of the symbol, so
|
||||
// value to match. We fill in the low bytes with the contents of the symbol, so
|
||||
// that the adjustment doesn't include unnecessary values. (For example, let
|
||||
// FOO=$10f0, with operand "#>FOO" ($10). We shift the operand to get $1000, then
|
||||
// OR in the low byte to get $10f0, so that when we subtract we get adjustment==0.)
|
||||
int adjOperand, keepLen;
|
||||
if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank) {
|
||||
adjOperand = operandValue << 16 | (sym.Value & 0xffff);
|
||||
adjOperand = operandValue << 16 | (int)(sym.Value & 0xff00ffff);
|
||||
keepLen = 3;
|
||||
} else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High) {
|
||||
adjOperand = (operandValue << 8) | (sym.Value & 0xff);
|
||||
@ -696,7 +801,7 @@ namespace SourceGen {
|
||||
adjustment = adjOperand - sym.Value;
|
||||
if (keepLen == 1) {
|
||||
adjustment %= 256;
|
||||
// Adjust for aesthetics. The assembler implicitly appiles a modulo operation,
|
||||
// Adjust for aesthetics. The assembler implicitly applies a modulo operation,
|
||||
// so we can use the value closest to zero.
|
||||
if (adjustment > 127) {
|
||||
adjustment = -(256 - adjustment) /*% 256*/;
|
||||
|
@ -198,7 +198,7 @@ code, but also needs to know how to handle the corner cases.</p>
|
||||
left to right, with no operator precedence.</li>
|
||||
<li>The byte selection operators ('<', '>', '^') are actually
|
||||
word-selection operators, yielding 16-bit values when wide registers
|
||||
are enabled on the 65816.</p>
|
||||
are enabled on the 65816.</li>
|
||||
<li>The assembler tracks register widths when it sees SEP/REP instructions,
|
||||
but doesn't attempt to track the emulation flag. So if the registers
|
||||
are long when you switch to emulation, incorrect code is generated.
|
||||
|
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
### 6502bench SourceGen dis65 v1.0 ###
|
||||
{
|
||||
"_ContentVersion":1,"FileDataLength":678,"FileDataCrc32":1825804405,"ProjectProps":{
|
||||
"_ContentVersion":1,"FileDataLength":695,"FileDataCrc32":1242388944,"ProjectProps":{
|
||||
"CpuName":"65816","IncludeUndocumentedInstr":false,"EntryFlags":33489103,"AnalysisParams":{
|
||||
"AnalyzeUncategorizedData":true,"MinCharsForString":4,"SeekNearbyTargets":true},
|
||||
"PlatformSymbolFileIdentifiers":["PROJ:2007-labels-and-symbols.sym65","RT:TestData/TestSyms.sym65"],"ExtensionScriptFileIdentifiers":[],"ProjectSyms":{
|
||||
@ -19,7 +19,11 @@
|
||||
"projover":{
|
||||
"DataDescriptor":{
|
||||
"Length":1,"Format":"NumericLE","SubFormat":"Hex","SymbolRef":null},
|
||||
"Comment":"replaces platform symbol","Label":"projover","Value":16384,"Source":"Project","Type":"ExternalAddr"}}},
|
||||
"Comment":"replaces platform symbol","Label":"projover","Value":16384,"Source":"Project","Type":"ExternalAddr"},
|
||||
"thirty2":{
|
||||
"DataDescriptor":{
|
||||
"Length":1,"Format":"NumericLE","SubFormat":"Hex","SymbolRef":null},
|
||||
"Comment":"32-bit constant test","Label":"thirty2","Value":305419896,"Source":"Project","Type":"Constant"}}},
|
||||
"AddressMap":[{
|
||||
"Offset":0,"Addr":74565},
|
||||
{
|
||||
@ -30,7 +34,7 @@
|
||||
"397":"bulky","509":"stringy"},
|
||||
"LongComments":{
|
||||
"-2147483647":{
|
||||
"Text":"Project was edited to add a label in the middle of a dense hex region, and add a duplicate label.","BoxMode":false,"MaxWidth":80}},
|
||||
"Text":"Project was edited to add a label in the middle of a dense hex region, and add a duplicate label.","BoxMode":false,"MaxWidth":80,"BackgroundColor":0}},
|
||||
"Notes":{
|
||||
},
|
||||
"UserLabels":{
|
||||
@ -381,4 +385,22 @@
|
||||
"Length":3,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||
"Label":"badmid","Part":"Low"}},
|
||||
"397":{
|
||||
"Length":112,"Format":"Dense","SubFormat":"None","SymbolRef":null}}}
|
||||
"Length":112,"Format":"Dense","SubFormat":"None","SymbolRef":null},
|
||||
"677":{
|
||||
"Length":2,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||
"Label":"thirty2","Part":"Low"}},
|
||||
"679":{
|
||||
"Length":2,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||
"Label":"thirty2","Part":"High"}},
|
||||
"681":{
|
||||
"Length":2,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||
"Label":"thirty2","Part":"Bank"}},
|
||||
"685":{
|
||||
"Length":3,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||
"Label":"thirty2","Part":"Low"}},
|
||||
"688":{
|
||||
"Length":3,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||
"Label":"thirty2","Part":"High"}},
|
||||
"691":{
|
||||
"Length":3,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||
"Label":"thirty2","Part":"Bank"}}}}
|
||||
|
@ -6,6 +6,7 @@ plataddr equ $3000 ;address only in platform file
|
||||
projalsa equ $3200 ;same val as projalso
|
||||
absh equ $feed
|
||||
biggie equ $123456
|
||||
thirty2 equ $12345678 ;32-bit constant test
|
||||
|
||||
org $012345
|
||||
start clc
|
||||
@ -186,5 +187,13 @@ string asc 'This is a long string. Put a label and comment on it to confir'
|
||||
asc 'm that the label and comment only appear on the first line. T'
|
||||
asc 'he quick brown fox jumps over the lazy dogs.'
|
||||
|
||||
L118E rts
|
||||
L118E lda #<thirty2+2
|
||||
lda #>thirty2+768
|
||||
lda #^thirty2
|
||||
rep #$30
|
||||
mx %00
|
||||
lda #thirty2+3
|
||||
lda #>thirty2+1024
|
||||
lda #^thirty2
|
||||
rts
|
||||
|
||||
|
@ -7,6 +7,7 @@ plataddr = $3000 ;address only in platform file
|
||||
projalsa = $3200 ;same val as projalso
|
||||
absh = $feed
|
||||
biggie = $123456
|
||||
thirty2 = $12345678 ;32-bit constant test
|
||||
|
||||
.org $012345
|
||||
.a8
|
||||
@ -194,5 +195,14 @@ string: .byte "This is a long string. Put a label and comment on it to confir
|
||||
.byte "m that the label and comment only appear on the first line. T"
|
||||
.byte "he quick brown fox jumps over the lazy dogs."
|
||||
|
||||
L118E: rts
|
||||
L118E: lda #<thirty2+2
|
||||
lda #>thirty2+3
|
||||
lda #^thirty2
|
||||
rep #$30
|
||||
.a16
|
||||
.i16
|
||||
lda #thirty2 & $ffff +3
|
||||
lda #thirty2 >> 8 & $ffff +4
|
||||
lda #thirty2 >> 16
|
||||
rts
|
||||
|
||||
|
@ -7,6 +7,7 @@ absl equ $1029
|
||||
absh equ $feed
|
||||
zip equ $cd
|
||||
biggie equ $123456
|
||||
thirty2 equ $12345678
|
||||
|
||||
org $012345
|
||||
|
||||
@ -217,5 +218,15 @@ t4c jml target2
|
||||
|
||||
:skiphex
|
||||
|
||||
; extract bytes from 32-bit value with short regs
|
||||
lda #<thirty2 + 2
|
||||
lda #>thirty2 + 768
|
||||
lda #^thirty2
|
||||
rep #$30
|
||||
mx %00
|
||||
lda #<thirty2 + 3
|
||||
lda #>thirty2 + 1024
|
||||
lda #^thirty2
|
||||
|
||||
rts
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user