1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-10-02 05:57:59 +00:00

Work around two assembler issues

Most assemblers end local label scope when a global label is
encountered.  cc65 takes this one step further by ending local label
scope when constants or variables are defined.  So, if we have a
variable table with a nonzero number of entries, we want to create
a fake global label at that point to end the scope.

Merlin 32 won't let you write " LDA #',' ".  For some reason the
comma causes an error.  IGenerator now has a "tweak operand format"
interface that lets us fix that.
This commit is contained in:
Andy McFadden 2019-09-20 14:05:17 -07:00
parent 6df874c559
commit b74630dd5b
16 changed files with 174 additions and 18 deletions

View File

@ -320,6 +320,12 @@ namespace SourceGen.AsmGen {
return string.Empty; // indicate original is fine
}
// IGenerator
public FormatDescriptor ModifyInstructionOperandFormat(int offset, FormatDescriptor dfd,
int operand) {
return dfd;
}
// IGenerator
public void UpdateCharacterEncoding(FormatDescriptor dfd) { }

View File

@ -217,6 +217,7 @@ namespace SourceGen.AsmGen {
mLocalizer = new LabelLocalizer(Project);
if (!Settings.GetBool(AppSettings.SRCGEN_DISABLE_LABEL_LOCALIZATION, false)) {
mLocalizer.LocalPrefix = "@";
mLocalizer.QuirkVariablesEndScope = true;
mLocalizer.Analyze();
}
@ -346,6 +347,12 @@ namespace SourceGen.AsmGen {
}
}
// IGenerator
public FormatDescriptor ModifyInstructionOperandFormat(int offset, FormatDescriptor dfd,
int operand) {
return dfd;
}
// IGenerator
public void UpdateCharacterEncoding(FormatDescriptor dfd) { }

View File

@ -367,6 +367,19 @@ namespace SourceGen.AsmGen {
return string.Empty;
}
// IGenerator
public FormatDescriptor ModifyInstructionOperandFormat(int offset, FormatDescriptor dfd,
int operand) {
if (dfd.FormatType == FormatDescriptor.Type.NumericLE && dfd.IsStringOrCharacter &&
(operand & 0x7f) == (byte)',') {
// Merlin throws an error on comma operands, e.g. LDA #','
dfd = FormatDescriptor.Create(dfd.Length,
FormatDescriptor.Type.NumericLE, FormatDescriptor.SubType.None);
}
return dfd;
}
// IGenerator
public void UpdateCharacterEncoding(FormatDescriptor dfd) { }

View File

@ -341,6 +341,12 @@ namespace SourceGen.AsmGen {
return string.Empty; // indicate original is fine
}
// IGenerator
public FormatDescriptor ModifyInstructionOperandFormat(int offset, FormatDescriptor dfd,
int operand) {
return dfd;
}
// IGenerator
public void UpdateCharacterEncoding(FormatDescriptor dfd) {
CharEncoding.Encoding newEnc = PseudoOp.SubTypeToEnc(dfd.FormatSubType);

View File

@ -230,14 +230,17 @@ namespace SourceGen.AsmGen {
// 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) {
FormatDescriptor dfd = gen.ModifyInstructionOperandFormat(offset,
attr.DataDescriptor, operand);
// Format operand as directed.
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,
gen.Localizer.LabelMap, dfd, operand >> 8, 1,
PseudoOp.FormatNumericOpFlags.None);
string opstr2 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
gen.Localizer.LabelMap, attr.DataDescriptor, operand & 0xff, 1,
gen.Localizer.LabelMap, dfd, operand & 0xff, 1,
PseudoOp.FormatNumericOpFlags.None);
if (gen.Quirks.BlockMoveArgsReversed) {
string tmp = opstr1;
@ -248,10 +251,10 @@ namespace SourceGen.AsmGen {
formattedOperand = hash + opstr1 + "," + hash + opstr2;
} else {
if (attr.DataDescriptor.IsStringOrCharacter) {
gen.UpdateCharacterEncoding(attr.DataDescriptor);
gen.UpdateCharacterEncoding(dfd);
}
formattedOperand = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
lvLookup, gen.Localizer.LabelMap, attr.DataDescriptor,
lvLookup, gen.Localizer.LabelMap, dfd,
offset, operandForSymbol, operandLen, opFlags);
}
} else {

View File

@ -95,6 +95,19 @@ namespace SourceGen.AsmGen {
/// null if the op is unsupported or broken and should be emitted as hex.</returns>
string ModifyOpcode(int offset, OpDef op);
/// <summary>
/// Provides an opportunity for the assembler to replace an instruction's format
/// descriptor with another. Only called if the instruction is explicitly formatted
/// (i.e. has a non-null descriptor).
/// </summary>
/// <param name="offset">Instruction offset.</param>
/// <param name="dfd">Existing descriptor.</param>
/// <param name="operand">Operand value.</param>
/// <returns>Replacement format descriptor. If no changes are desired, returns
/// the dfd argument.</returns>
FormatDescriptor ModifyInstructionOperandFormat(int offset, FormatDescriptor dfd,
int operand);
/// <summary>
/// Allows the generator to issue character encoding update instructions for source
/// files with more than one encoding.

View File

@ -130,6 +130,11 @@ namespace SourceGen.AsmGen {
/// </summary>
public string LocalPrefix { get; set; }
/// <summary>
/// Set this if the declaration of a local variable ends the current scope.
/// </summary>
public bool QuirkVariablesEndScope { get; set; }
/// <summary>
/// Project reference.
/// </summary>
@ -230,8 +235,18 @@ namespace SourceGen.AsmGen {
bool first = true;
for (int i = 0; i < mProject.FileDataLength; i++) {
Symbol sym = mProject.GetAnattrib(i).Symbol;
for (int offset = 0; offset < mProject.FileDataLength; offset++) {
Symbol sym = mProject.GetAnattrib(offset).Symbol;
// In cc65, variable declarations end the local label scope. We insert a
// fake global symbol if we counter a table with a nonzero number of entries.
if (QuirkVariablesEndScope &&
mProject.LvTables.TryGetValue(offset, out LocalVariableTable value) &&
value.Count > 0) {
mGlobalFlags[offset] = true;
mGlobalLabels.Add(new OffsetLabel(offset, "!VARTAB!"));
continue;
}
if (sym == null) {
// No label at this offset.
continue;
@ -239,22 +254,22 @@ namespace SourceGen.AsmGen {
if (first || sym.SymbolType != Symbol.Type.LocalOrGlobalAddr) {
first = false;
mGlobalFlags[i] = true;
mGlobalLabels.Add(new OffsetLabel(i, sym.Label));
mGlobalFlags[offset] = true;
mGlobalLabels.Add(new OffsetLabel(offset, sym.Label));
// Don't add to pairs list.
continue;
}
// If nothing actually references this label, the xref set will be empty.
XrefSet xrefs = mProject.GetXrefSet(i);
XrefSet xrefs = mProject.GetXrefSet(offset);
if (xrefs != null) {
foreach (XrefSet.Xref xref in xrefs) {
if (!xref.IsByName) {
continue;
}
mOffsetPairs.Add(new OffsetPair(xref.Offset, i));
mOffsetPairs.Add(new OffsetPair(xref.Offset, offset));
}
}
}

View File

@ -251,6 +251,9 @@ code, but also needs to know how to handle the corner cases.</p>
where the labels are used, the assembler will already have generated them
as absolute values. Width disambiguation must be applied to operands
that wouldn't be ambiguous to a multi-pass assembler.</li>
<li>Assignment of constants and variables (<code>=</code> and
<code>.set</code>) ends local label scope, so the label localizer
has to take variable assignment into account.</li>
<li>The assembler is geared toward generating relocatable code with
multiple segments (it is, after all, an assembler for a C compiler).
A linker configuration script is expected to be provided for anything
@ -269,6 +272,8 @@ code, but also needs to know how to handle the corner cases.</p>
<ul>
<li>PC relative branches don't wrap around at bank boundaries.</li>
<li>For some failures, an exit code of zero is returned.</li>
<li>Immediate operands with a comma (e.g. <code>LDA #','</code>)
cause an error.</li>
<li>Some DP indexed store instructions cause errors if the label isn't
unambiguously DP (e.g. <code>STX $00,X</code> vs.
<code>STX $0000,X</code>). This isn't a problem with project/platform

View File

@ -1,9 +1,13 @@
### 6502bench SourceGen dis65 v1.0 ###
{
"_ContentVersion":2,"FileDataLength":137,"FileDataCrc32":708791740,"ProjectProps":{
"_ContentVersion":2,"FileDataLength":151,"FileDataCrc32":-814797830,"ProjectProps":{
"CpuName":"65816","IncludeUndocumentedInstr":false,"EntryFlags":32702671,"AutoLabelStyle":"Simple","AnalysisParams":{
"AnalyzeUncategorizedData":true,"DefaultTextScanMode":"LowHighAscii","MinCharsForString":4,"SeekNearbyTargets":false,"SmartPlpHandling":false},
"PlatformSymbolFileIdentifiers":[],"ExtensionScriptFileIdentifiers":[],"ProjectSyms":{
"__ENABLE_LABEL_LOCALIZATION":{
"DataDescriptor":{
"Length":1,"Format":"NumericLE","SubFormat":"Decimal","SymbolRef":null},
"Comment":"","Label":"__ENABLE_LABEL_LOCALIZATION","Value":1,"Source":"Project","Type":"Constant"},
"CONST_ONE":{
"DataDescriptor":{
"Length":1,"Format":"NumericLE","SubFormat":"Hex","SymbolRef":null},
@ -44,7 +48,11 @@
"87":{
"Label":"PTR_1","Value":4183,"Source":"User","Type":"LocalOrGlobalAddr"},
"126":{
"Label":"DPCODE","Value":128,"Source":"User","Type":"LocalOrGlobalAddr"}},
"Label":"DPCODE","Value":128,"Source":"User","Type":"LocalOrGlobalAddr"},
"136":{
"Label":"SPLIT1","Value":138,"Source":"User","Type":"LocalOrGlobalAddr"},
"143":{
"Label":"SPLIT2","Value":145,"Source":"User","Type":"LocalOrGlobalAddr"}},
"OperandFormats":{
"8":{
"Length":2,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
@ -52,7 +60,11 @@
"24":{
"Length":2,"Format":"NumericLE","SubFormat":"Decimal","SymbolRef":null},
"26":{
"Length":2,"Format":"NumericLE","SubFormat":"Decimal","SymbolRef":null}},
"Length":2,"Format":"NumericLE","SubFormat":"Decimal","SymbolRef":null},
"136":{
"Length":2,"Format":"NumericLE","SubFormat":"Ascii","SymbolRef":null},
"143":{
"Length":2,"Format":"NumericLE","SubFormat":"Ascii","SymbolRef":null}},
"LvTables":{
"12":{
"Variables":[{
@ -180,4 +192,16 @@
"Variables":[{
"DataDescriptor":{
"Length":1,"Format":"NumericLE","SubFormat":"Hex","SymbolRef":null},
"Comment":"same as org","Label":"DPNOP","Value":128,"Source":"Variable","Type":"ExternalAddr"}],"ClearPrevious":false}}}
"Comment":"same as org","Label":"DPNOP","Value":128,"Source":"Variable","Type":"ExternalAddr"}],"ClearPrevious":false},
"138":{
"Variables":[{
"DataDescriptor":{
"Length":1,"Format":"NumericLE","SubFormat":"Hex","SymbolRef":null},
"Comment":"","Label":"SPLITTER","Value":128,"Source":"Variable","Type":"ExternalAddr"}],"ClearPrevious":false},
"139":{
"Variables":[{
"DataDescriptor":{
"Length":1,"Format":"NumericLE","SubFormat":"Hex","SymbolRef":null},
"Comment":"","Label":"FOO","Value":128,"Source":"Variable","Type":"ExternalAddr"}],"ClearPrevious":false},
"145":{
"Variables":[],"ClearPrevious":false}}}

View File

@ -1,5 +1,7 @@
;Edited to have duplicate labels (PROJ_ZERO, DPCODE).
.cpu "65816"
.enc sg_ascii
.cdef $20,$7e,$20
PROJ_ZERO = $00 ;project addr
PROJ_ONE = $01 ;project addr
CONST_ZERO = $f0 ;project const
@ -66,7 +68,7 @@ PTR .var $22 ;#2
ldx PTR
PTR .var $24 ;#3
ldx PTR
PTR_1 nop
_PTR_1 nop
PTR_A .var $20
ldy PTR_A
PTR_B .var $1f
@ -104,6 +106,13 @@ DPCODE nop
lda DPCODE
lda @wDPCODE
lda @lDPCODE
_SPLIT1 lda #','
SPLITTER .var $80
ldx $1234
beq _SPLIT1
_SPLIT2 lda ','
ldx $5678
beq _SPLIT2
rts
.here

View File

@ -63,7 +63,7 @@ L103C lda #$fe
ldx ]PTR
]PTR equ $24 ;#3
ldx ]PTR
PTR_1 nop
:PTR_1 nop
]PTR_A equ $20
ldy ]PTR_A
]PTR_B equ $1f
@ -101,5 +101,12 @@ DPCODE nop
lda DPCODE
lda: DPCODE
ldal DPCODE
:SPLIT1 lda #$2c
]SPLITTER equ $80
ldx $1234
beq :SPLIT1
:SPLIT2 lda $2c
ldx $5678
beq :SPLIT2
rts

View File

@ -99,7 +99,7 @@ L103C lda #$fe
.CONST0 = $10
.PTR = $24 ;#3
ldx .PTR
PTR_1 nop
@PTR_1 nop
!zone Z000058
.NH0 = $00 ;not hidden
.NH1 = $01 ;not hidden
@ -191,6 +191,36 @@ DPCODE nop
lda+1 DPCODE
lda+2 DPCODE
lda+3 DPCODE
@SPLIT1 lda #','
!zone Z00008a
.NH0 = $00 ;not hidden
.NH1 = $01 ;not hidden
.PTR0 = $10
.CONST0 = $10
.PTR_C = $1d
.PTR_D = $21
.PTR = $24 ;#3
.VAL0 = $30
.VAL14 = $31
.VAL5 = $35
.SPLITTER = $80
ldx $1234
beq @SPLIT1
@SPLIT2 lda ','
!zone Z000091
.NH0 = $00 ;not hidden
.NH1 = $01 ;not hidden
.PTR0 = $10
.CONST0 = $10
.PTR_C = $1d
.PTR_D = $21
.PTR = $24 ;#3
.VAL0 = $30
.VAL14 = $31
.VAL5 = $35
.SPLITTER = $80
ldx $5678
beq @SPLIT2
rts
} ;!pseudopc

View File

@ -106,5 +106,12 @@ DPCODE: nop
lda DPCODE
lda a:DPCODE
lda f:DPCODE
SPLIT1: lda #','
SPLITTER .set $80
ldx $1234
beq SPLIT1
@SPLIT2: lda ','
ldx $5678
beq @SPLIT2
rts

View File

@ -2,7 +2,7 @@
MEMORY {
MAIN: file=%O, start=%S, size=65536;
# MEM000: file=%O, start=$1000, size=126;
# MEM001: file=%O, start=$0080, size=11;
# MEM001: file=%O, start=$0080, size=25;
}
SEGMENTS {
CODE: load=MAIN, type=rw;

View File

@ -3,6 +3,8 @@
;
; Assembler: Merlin 32
; EDIT: add __ENABLE_LABEL_LOCALIZATION to project symbols
org $1000
; Define these as project symbols.
@ -145,4 +147,13 @@ DPCODE nop
lda DPCODE ;should be DPNOP
lda |DPCODE ;should be DPCODE
lda >DPCODE ;should be DPCODE
; Local label test. ca65 v2.18 erases cheap local label scope when it
; encounters a constant or .set.
LOCAL1 lda #$2c ;EDIT: format as ASCII
ldx $1234 ;put variable table here with one arbitrary entry
beq LOCAL1
LOCAL2 lda $2c ;EDIT: format as ASCII
ldx $5678 ;put empty variable table here
beq LOCAL2
rts