1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-11-29 10:50:28 +00:00

Implement local variables for ACME

Unlike 64tass and Merlin, which allow you to redefine symbols, ACME
uses "zones" that provide scope for local variables.  This means
that, at the point of a local variable table definition, we have to
start a new zone and output the full set of active symbols, not just
the newly-defined ones.  (If you set the "clear previous" flag in
the LvTable there's no difference.)

We could do a bit better by only outputting the symbols that are
actually used within the zone, similar to what we do for global
project/platform symbols, but that's a bunch of work for questionable
benefit.
This commit is contained in:
Andy McFadden 2019-09-01 10:55:19 -07:00
parent c698048001
commit d542a809f8
8 changed files with 218 additions and 95 deletions

View File

@ -188,6 +188,7 @@ namespace SourceGen.AsmGen {
config.mForceDirectOperandPrefix = string.Empty;
config.mForceAbsOperandPrefix = string.Empty;
config.mForceLongOperandPrefix = string.Empty;
config.mLocalVariableLablePrefix = ".";
config.mEndOfLineCommentDelimiter = ";";
config.mFullLineCommentDelimiterBase = ";";
config.mBoxLineCommentDelimiter = ";";
@ -472,8 +473,18 @@ namespace SourceGen.AsmGen {
}
// IGenerator
public void OutputVarDirective(string name, string valueStr, string comment) {
OutputEquDirective(name, valueStr, comment);
public void OutputLocalVariableTable(int offset, List<DefSymbol> newDefs,
LocalVariableTable allDefs) {
OutputLine(string.Empty, "!zone", "Z" + offset.ToString("x6"), string.Empty);
for (int i = 0; i < allDefs.Count; i++) {
DefSymbol defSym = allDefs[i];
string valueStr = PseudoOp.FormatNumericOperand(SourceFormatter,
Project.SymbolTable, null, defSym.DataDescriptor, defSym.Value, 1,
PseudoOp.FormatNumericOpFlags.None);
OutputEquDirective(SourceFormatter.FormatVariableLabel(defSym.Label),
valueStr, defSym.Comment);
}
}
// IGenerator

View File

@ -164,6 +164,8 @@ namespace SourceGen.AsmGen {
// Special handling for forward references to zero-page labels is required.
Quirks.SinglePassAssembler = true;
Quirks.NoRedefinableSymbols = true;
mWorkDirectory = workDirectory;
mFileNameBase = fileNameBase;
Settings = settings;
@ -506,8 +508,16 @@ namespace SourceGen.AsmGen {
}
// IGenerator
public void OutputVarDirective(string name, string valueStr, string comment) {
OutputEquDirective(name, valueStr, comment);
public void OutputLocalVariableTable(int offset, List<DefSymbol> newDefs,
LocalVariableTable allDefs) {
foreach (DefSymbol defSym in newDefs) {
// Use an operand length of 1 so values are shown as concisely as possible.
string valueStr = PseudoOp.FormatNumericOperand(SourceFormatter,
Project.SymbolTable, null, defSym.DataDescriptor, defSym.Value, 1,
PseudoOp.FormatNumericOpFlags.None);
OutputEquDirective(SourceFormatter.FormatVariableLabel(defSym.Label),
valueStr, defSym.Comment);
}
}
// IGenerator

View File

@ -134,7 +134,6 @@ namespace SourceGen.AsmGen {
Project = project;
Quirks = new AssemblerQuirks();
Quirks.HasRedefinableSymbols = true;
Quirks.NoPcRelBankWrap = true;
Quirks.TracksSepRepNotEmu = true;
@ -401,10 +400,16 @@ namespace SourceGen.AsmGen {
}
// IGenerator
public void OutputVarDirective(string name, string valueStr, string comment) {
OutputLine(SourceFormatter.FormatVariableLabel(name),
SourceFormatter.FormatPseudoOp(sDataOpNames.VarDirective),
valueStr, SourceFormatter.FormatEolComment(comment));
public void OutputLocalVariableTable(int offset, List<DefSymbol> newDefs,
LocalVariableTable allDefs) {
foreach (DefSymbol defSym in newDefs) {
string valueStr = PseudoOp.FormatNumericOperand(SourceFormatter,
Project.SymbolTable, null, defSym.DataDescriptor, defSym.Value, 1,
PseudoOp.FormatNumericOpFlags.None);
OutputLine(SourceFormatter.FormatVariableLabel(defSym.Label),
SourceFormatter.FormatPseudoOp(sDataOpNames.VarDirective),
valueStr, SourceFormatter.FormatEolComment(defSym.Comment));
}
}
// IGenerator

View File

@ -160,7 +160,6 @@ namespace SourceGen.AsmGen {
Project = project;
Quirks = new AssemblerQuirks();
Quirks.HasRedefinableSymbols = true;
Quirks.StackIntOperandIsImmediate = true;
mWorkDirectory = workDirectory;
@ -538,9 +537,16 @@ namespace SourceGen.AsmGen {
}
// IGenerator
public void OutputVarDirective(string name, string valueStr, string comment) {
OutputLine(name, SourceFormatter.FormatPseudoOp(sDataOpNames.VarDirective),
valueStr, SourceFormatter.FormatEolComment(comment));
public void OutputLocalVariableTable(int offset, List<DefSymbol> newDefs,
LocalVariableTable allDefs) {
foreach (DefSymbol defSym in newDefs) {
string valueStr = PseudoOp.FormatNumericOperand(SourceFormatter,
Project.SymbolTable, null, defSym.DataDescriptor, defSym.Value, 1,
PseudoOp.FormatNumericOpFlags.None);
OutputLine(SourceFormatter.FormatVariableLabel(defSym.Label),
SourceFormatter.FormatPseudoOp(sDataOpNames.VarDirective),
valueStr, SourceFormatter.FormatEolComment(defSym.Comment));
}
}
// IGenerator

View File

@ -40,7 +40,7 @@ namespace SourceGen.AsmGen {
bool doAddCycles = gen.Settings.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, false);
LocalVariableLookup lvLookup = new LocalVariableLookup(proj.LvTables, proj,
!gen.Quirks.HasRedefinableSymbols);
gen.Quirks.NoRedefinableSymbols);
GenerateHeader(gen, sw);
@ -74,7 +74,9 @@ namespace SourceGen.AsmGen {
List<DefSymbol> lvars = lvLookup.GetVariablesDefinedAtOffset(offset);
if (lvars != null) {
GenerateLocalVariables(gen, sw, lvars);
// table defined here
gen.OutputLocalVariableTable(offset, lvars,
lvLookup.GetMergedTableAtOffset(offset));
}
if (attr.IsInstructionStart) {
@ -160,20 +162,6 @@ namespace SourceGen.AsmGen {
}
}
private static void GenerateLocalVariables(IGenerator gen, StreamWriter sw,
List<DefSymbol> vars) {
foreach (DefSymbol defSym in vars) {
DisasmProject proj = gen.Project;
Formatter formatter = gen.SourceFormatter;
// Use an operand length of 1 so values are shown as concisely as possible.
string valueStr = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
null, defSym.DataDescriptor, defSym.Value, 1,
PseudoOp.FormatNumericOpFlags.None);
gen.OutputVarDirective(defSym.Label, valueStr, defSym.Comment);
}
}
private static void GenerateInstruction(IGenerator gen, StreamWriter sw,
LocalVariableLookup lvLookup, int offset, int instrBytes, bool doAddCycles) {
DisasmProject proj = gen.Project;

View File

@ -137,12 +137,14 @@ namespace SourceGen.AsmGen {
void OutputEquDirective(string name, string valueStr, string comment);
/// <summary>
/// Outputs a variable definition directive. The numeric value is already formatted.
/// Outputs a series of local variable definitions.
/// </summary>
/// <param name="name">Symbol label.</param>
/// <param name="valueStr">Formatted value.</param>
/// <param name="comment">End-of-line comment.</param>
void OutputVarDirective(string name, string valueStr, string comment);
/// <param name="offset">Offset at which table is defined.</param>
/// <param name="newDefs">New definitions, i.e. just the variables that were defined
/// at this offset.</param>
/// <param name="allDefs">All variable definitions that are active at this point.</param>
void OutputLocalVariableTable(int offset, List<DefSymbol> newDefs,
LocalVariableTable allDefs);
/// <summary>
/// Outputs a code origin directive.
@ -200,7 +202,7 @@ namespace SourceGen.AsmGen {
/// Does the assembler support a type of label whose value can be redefined to
/// act as a local variable?
/// </summary>
public bool HasRedefinableSymbols { get; set; }
public bool NoRedefinableSymbols { get; set; }
/// <summary>
/// Is the assembler unable to generate relative branches that wrap around banks?

View File

@ -49,6 +49,20 @@ namespace SourceGen {
///
/// The BaseLabel does not change, but Label is updated by MakeUnique.
/// </summary>
/// <remarks>
/// LvLookup is run multiple times, and can be restarted in the middle of a run. It's
/// essential that UniqueLabel behaves deterministically. For this to happen, the
/// contents of SymbolTable can't change in a way that affects the outcome unless it
/// also causes us to redo the uniquification. This mostly means that we have to be
/// very careful about creating duplicate symbols, so that we don't get halfway through
/// the analysis pass and invalidate our previous work. It's best to leave
/// uniquification disabled until we're generating assembly source code.
///
/// The issues also make it hard to do the uniquification once, rather than every time we
/// walk the code. Not all symbol changes cause a re-analysis (e.g. renaming a user
/// label does not), and we don't want to fill the symbol table with the uniquified
/// names because it could block user labels that would otherwise be valid.
/// </remarks>
private class UniqueLabel {
public string BaseLabel { get; private set; }
public string Label { get; private set; }
@ -87,7 +101,7 @@ namespace SourceGen {
/// </summary>
/// <remarks>
/// It's hard to do this as part of uniquification because the remapped base name ends
/// up in the symbol table, and the uniqifier isn't able to tell that the entry in the
/// up in the symbol table, and the uniquifier isn't able to tell that the entry in the
/// symbol table is itself. The logic is simpler if we just rename the label before
/// the uniquifier ever sees it.
/// </remarks>

View File

@ -13,28 +13,42 @@ CONST_ZERO = $f0 ;project const
ldx $04
lda CONST_ZERO,S
sta $f1,S
VAR_ZERO = $00
VAR_TWO = $02
VAR_THREE = $03
CONST_ZERO_VAR = $f0
ldy VAR_ZERO
lda (VAR_ZERO+1),y
sta VAR_THREE
!zone Z00000c
.VAR_ZERO = $00
.VAR_TWO = $02
.VAR_THREE = $03
.CONST_ZERO_VAR = $f0
ldy .VAR_ZERO
lda (.VAR_ZERO+1),y
sta .VAR_THREE
ldx $04
lda CONST_ZERO_VAR,S
lda .CONST_ZERO_VAR,S
sta $f1,S
eor 0
ora 240,S
PROJ_ZERO_DUP1 = $10 ;clash with project symbol
DPCODE_DUP1 = $80 ;clash with user label
lda VAR_ZERO
lda VAR_ZERO+1
lda VAR_TWO
lda VAR_THREE
!zone Z00001c
.VAR_ZERO = $00
.VAR_TWO = $02
.VAR_THREE = $03
.PROJ_ZERO_DUP1 = $10 ;clash with project symbol
.DPCODE_DUP1 = $80 ;clash with user label
.CONST_ZERO_VAR = $f0
lda .VAR_ZERO
lda .VAR_ZERO+1
lda .VAR_TWO
!zone Z000022
.VAR_ZERO = $00
.VAR_TWO = $02
.VAR_THREE = $03
.PROJ_ZERO_DUP1 = $10 ;clash with project symbol
.DPCODE_DUP1 = $80 ;clash with user label
.CONST_ZERO_VAR = $f0
lda .VAR_THREE
lda $04
lda PROJ_ZERO_DUP1
lda .PROJ_ZERO_DUP1
lda $11
lda+1 DPCODE
!zone Z00002c
ldx PROJ_ZERO
ldx PROJ_ONE
ldx $02
@ -43,58 +57,131 @@ DPCODE_DUP1 = $80 ;clash with user label
ldy PROJ_ONE
ldy $02
!byte $2c
NH0 = $00 ;not hidden
NH1 = $01 ;not hidden
!zone Z00003c
.NH0 = $00 ;not hidden
.NH1 = $01 ;not hidden
L103C lda #$fe
beq L103C
ldy NH0
ldy NH1
ldy .NH0
ldy .NH1
ldy $02
nop
PTR0 = $10
CONST0 = $10
lda PTR0
ldx PTR0+1
!zone Z000047
.NH0 = $00 ;not hidden
.NH1 = $01 ;not hidden
.PTR0 = $10
.CONST0 = $10
lda .PTR0
ldx .PTR0+1
ldy $12
lda (CONST0,S),y
sta (CONST0+3,S),y
lda (.CONST0,S),y
sta (.CONST0+3,S),y
;Test name redefinition. This is mostly of interest for assemblers without
;redefinable variables, but also of interest to the cross-reference window.
PTR = $20 ;#1
ldx PTR
PTR_3 = $22 ;#2
ldx PTR_3
PTR_4 = $24 ;#3
ldx PTR_4
!zone Z000051
.NH0 = $00 ;not hidden
.NH1 = $01 ;not hidden
.PTR0 = $10
.CONST0 = $10
.PTR = $20 ;#1
ldx .PTR
!zone Z000053
.NH0 = $00 ;not hidden
.NH1 = $01 ;not hidden
.PTR0 = $10
.CONST0 = $10
.PTR = $22 ;#2
ldx .PTR
!zone Z000055
.NH0 = $00 ;not hidden
.NH1 = $01 ;not hidden
.PTR0 = $10
.CONST0 = $10
.PTR = $24 ;#3
ldx .PTR
PTR_1 nop
PTR_A = $20
ldy PTR_A
PTR_B = $1f
ldy PTR_B+1
PTR_C = $1d
ldy PTR_C+3
PTR_D = $21
ldy PTR_C+3
VAL0 = $30
VAL1 = $31
VAL2 = $32
VAL3 = $33
VAL4 = $34
VAL5 = $35
and VAL0
and VAL1
and VAL2
and VAL3
and VAL4
and VAL5
VAL14 = $31
and VAL0
and VAL14
and VAL14+1
and VAL14+2
and VAL14+3
and VAL5
DPNOP = $80 ;same as org
!zone Z000058
.NH0 = $00 ;not hidden
.NH1 = $01 ;not hidden
.PTR0 = $10
.CONST0 = $10
.PTR_A = $20
.PTR = $24 ;#3
ldy .PTR_A
!zone Z00005a
.NH0 = $00 ;not hidden
.NH1 = $01 ;not hidden
.PTR0 = $10
.CONST0 = $10
.PTR_B = $1f
.PTR = $24 ;#3
ldy .PTR_B+1
!zone Z00005c
.NH0 = $00 ;not hidden
.NH1 = $01 ;not hidden
.PTR0 = $10
.CONST0 = $10
.PTR_C = $1d
.PTR = $24 ;#3
ldy .PTR_C+3
!zone Z00005e
.NH0 = $00 ;not hidden
.NH1 = $01 ;not hidden
.PTR0 = $10
.CONST0 = $10
.PTR_C = $1d
.PTR_D = $21
.PTR = $24 ;#3
ldy .PTR_C+3
!zone Z000060
.NH0 = $00 ;not hidden
.NH1 = $01 ;not hidden
.PTR0 = $10
.CONST0 = $10
.PTR_C = $1d
.PTR_D = $21
.PTR = $24 ;#3
.VAL0 = $30
.VAL1 = $31
.VAL2 = $32
.VAL3 = $33
.VAL4 = $34
.VAL5 = $35
and .VAL0
and .VAL1
and .VAL2
and .VAL3
and .VAL4
and .VAL5
!zone Z00006c
.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
and .VAL0
and .VAL14
and .VAL14+1
and .VAL14+2
and .VAL14+3
and .VAL5
!zone Z000078
.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
.DPNOP = $80 ;same as org
lda+1 DPCODE
jsr DPCODE
rts