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

Work around cc65 single-pass behavior

The cc65 assembler runs in a single pass, which means forward
address references default to 16 bits.  For zero-page references
we have to add an explicit width disambiguator.  (This is an
unusual situation that only occurs if you have a zero-page .ORG
in the file after code that references it.)

With this change, 2014-label-dp passes, and no other regression
tests were affected.

(issue #40)
This commit is contained in:
Andy McFadden 2018-11-02 15:32:54 -07:00
parent c80be07f73
commit a88c746419
8 changed files with 363 additions and 4 deletions

View File

@ -63,7 +63,8 @@ namespace Asm65 {
public bool mAllowHighAsciiCharConst; // can we do high-ASCII character constants?
// (this might need to be generalized)
public string mForceAbsOpcodeSuffix; // these may be null or empty
public string mForceDirectOperandPrefix; // these may be null or empty
public string mForceAbsOpcodeSuffix;
public string mForceAbsOperandPrefix;
public string mForceLongOpcodeSuffix;
public string mForceLongOperandPrefix;
@ -485,7 +486,9 @@ namespace Asm65 {
/// <returns></returns>
public string FormatMnemonic(string mnemonic, OpDef.WidthDisambiguation wdis) {
string opcodeStr = mnemonic;
if (wdis == OpDef.WidthDisambiguation.ForceAbs) {
if (wdis == OpDef.WidthDisambiguation.ForceDirect) {
// nothing to do for opcode
} else if (wdis == OpDef.WidthDisambiguation.ForceAbs) {
if (!string.IsNullOrEmpty(mFormatConfig.mForceAbsOpcodeSuffix)) {
opcodeStr += mFormatConfig.mForceAbsOpcodeSuffix;
}
@ -507,13 +510,18 @@ namespace Asm65 {
/// Generates an operand format.
/// </summary>
/// <param name="addrMode">Addressing mode.</param>
/// <param name="wdis">Width disambiguation mode.</param>
/// <returns>Format string.</returns>
private string GenerateOperandFormat(OpDef.AddressMode addrMode,
OpDef.WidthDisambiguation wdis) {
string fmt;
string wdisStr = string.Empty;
if (wdis == OpDef.WidthDisambiguation.ForceAbs) {
if (wdis == OpDef.WidthDisambiguation.ForceDirect) {
if (!string.IsNullOrEmpty(mFormatConfig.mForceDirectOperandPrefix)) {
wdisStr = mFormatConfig.mForceDirectOperandPrefix;
}
} else if (wdis == OpDef.WidthDisambiguation.ForceAbs) {
if (!string.IsNullOrEmpty(mFormatConfig.mForceAbsOperandPrefix)) {
wdisStr = mFormatConfig.mForceAbsOperandPrefix;
}

View File

@ -112,9 +112,10 @@ namespace Asm65 {
/// </summary>
public enum WidthDisambiguation : byte {
None = 0,
ForceDirect, // only needed for forward DP label refs in single-pass assemblers
ForceAbs,
ForceLong,
ForceLongMaybe
ForceLongMaybe // add opcode suffix but not operand prefix
// May want an additional item: "force long if operand suffix specified". This
// would let us generate LDAL for assemblers that like to have that made explicit,

View File

@ -164,6 +164,7 @@ namespace SourceGen.AsmGen {
// cc65 v2.17: https://github.com/cc65/cc65/issues/754
Quirks.NoPcRelBankWrap = true;
}
Quirks.SinglePassAssembler = true;
mWorkDirectory = workDirectory;
mFileNameBase = fileNameBase;
@ -182,6 +183,7 @@ namespace SourceGen.AsmGen {
private void SetFormatConfigValues(ref Formatter.FormatConfig config) {
config.mForceAbsOpcodeSuffix = string.Empty;
config.mForceLongOpcodeSuffix = string.Empty;
config.mForceDirectOperandPrefix = "z:"; // zero
config.mForceAbsOperandPrefix = "a:"; // absolute
config.mForceLongOperandPrefix = "f:"; // far
config.mEndOfLineCommentDelimiter = ";";

View File

@ -185,6 +185,7 @@ namespace SourceGen.AsmGen {
private void SetFormatConfigValues(ref Formatter.FormatConfig config) {
config.mForceAbsOpcodeSuffix = ":";
config.mForceLongOpcodeSuffix = "l";
config.mForceDirectOperandPrefix = string.Empty;
config.mForceAbsOperandPrefix = string.Empty;
config.mForceLongOperandPrefix = string.Empty;
config.mEndOfLineCommentDelimiter = ";";

View File

@ -179,6 +179,7 @@ namespace SourceGen.AsmGen {
config.mForceAbsOpcodeSuffix = string.Empty;
config.mForceLongOpcodeSuffix = string.Empty;
config.mForceDirectOperandPrefix = string.Empty;
config.mForceAbsOperandPrefix = "@w"; // word
config.mForceLongOperandPrefix = "@l"; // long
config.mEndOfLineCommentDelimiter = ";";

View File

@ -171,6 +171,15 @@ namespace SourceGen.AsmGen {
if (op.IsWidthPotentiallyAmbiguous) {
wdis = OpDef.GetWidthDisambiguation(instrLen, operand);
}
if (gen.Quirks.SinglePassAssembler && wdis == OpDef.WidthDisambiguation.None &&
(op.AddrMode == OpDef.AddressMode.DP ||
op.AddrMode == OpDef.AddressMode.DPIndexX) ||
op.AddrMode == OpDef.AddressMode.DPIndexY) {
// Could be a forward reference to a direct-page label.
if (IsForwardLabelReference(gen, offset)) {
wdis = OpDef.WidthDisambiguation.ForceDirect;
}
}
string opcodeStr = formatter.FormatOpcode(op, wdis);
@ -298,6 +307,46 @@ namespace SourceGen.AsmGen {
}
}
/// <summary>
/// Determines whether the instruction at the specified offset has an operand that is
/// a forward reference. This only matters for single-pass assemblers.
/// </summary>
/// <param name="gen">Source generator reference.</param>
/// <param name="offset">Offset of instruction opcode.</param>
/// <returns>True if the instruction's operand is a forward reference to a label.</returns>
private static bool IsForwardLabelReference(IGenerator gen, int offset) {
DisasmProject proj = gen.Project;
Debug.Assert(proj.GetAnattrib(offset).IsInstructionStart);
FormatDescriptor dfd = proj.GetAnattrib(offset).DataDescriptor;
if (dfd == null || !dfd.HasSymbol) {
return false;
}
if (!proj.SymbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym)) {
return false;
}
if (!sym.IsInternalLabel) {
return false;
}
// It's an internal label reference. We don't currently have a data structure
// that lets us go from label name to file offset. This situation is sufficiently
// rare that an O(n) approach is acceptable. We may need to fix this someday.
//
// We only want to know if it is defined after the current instruction. This is
// probably being used for a direct-page reference, which is probably at the start
// of the file, so we run from the start to the current instruction.
for (int i = 0; i < offset; i++) {
Anattrib attr = proj.GetAnattrib(i);
if (attr.Symbol != null && attr.Symbol == sym) {
// Found it earlier in file.
return false;
}
}
// Must appear later in file.
return true;
}
/// <summary>
/// Configures some common format config items from the app settings. Uses a
/// passed-in settings object, rather than the global settings.

View File

@ -183,5 +183,10 @@ namespace SourceGen.AsmGen {
/// (Note this affects long-distance BRLs that don't appear to wrap.)
/// </summary>
public bool NoPcRelBankWrap { get; set; }
/// <summary>
/// Is the assembler implemented as a single pass?
/// </summary>
public bool SinglePassAssembler { get; set; }
}
}

View File

@ -0,0 +1,292 @@
;6502bench SourceGen v1.1.0-dev1
.setcpu "65816"
.org $1000
.a8
.i8
sec
xce
jsr L101F
jsr L10AB
jsr L10F2
jsr L1106
jsr L1109
jsr L112C
jsr L11F9
jsr L11FC
nop
nop
nop
.byte $00,$80
L101F: ora (L0080,x)
cop $80
ora $80,S
tsb z:L0080
ora z:L0080
asl z:L0080
ora [L0080]
php
ora #$80
asl A
phd
tsb a:L0086
ora a:L0086
asl a:L0086
ora f:L0089
bpl L1041
L1041: ora (L0080),y
ora (L0080)
ora ($80,S),y
trb z:L0080
ora z:L0080,x
asl z:L0080,x
ora [L0080],y
clc
ora L0086,y
inc A
tcs
trb a:L0086
ora a:L0086,x
asl a:L0086,x
ora f:L0089,x
jsr L0086
and (L0080,x)
jsl L0089
and $80,S
bit z:L0080
and z:L0080
rol z:L0080
and [L0080]
plp
and #$80
rol A
pld
bit a:L0086
and a:L0086
rol a:L0086
and f:L0089
bmi L1089
L1089: and (L0080),y
and (L0080)
and ($80,S),y
bit z:L0080,x
and z:L0080,x
rol z:L0080,x
and [L0080],y
sec
and L0086,y
dec A
tsc
bit a:L0086,x
and a:L0086,x
rol a:L0086,x
and f:L0089,x
rti
L10AB: eor (L0080,x)
.byte $42,$80
eor $80,S
.byte $44,$83,$84
eor z:L0080
lsr z:L0080
eor [L0080]
pha
eor #$80
lsr A
phk
jmp L10C2
L10C2: eor a:L0086
lsr a:L0086
eor f:L0089
bvc L10CE
L10CE: eor (L0080),y
eor (L0080)
eor ($80,S),y
.byte $54,$83,$84
eor z:L0080,x
lsr z:L0080,x
eor [L0080],y
cli
eor L0086,y
phy
tcd
jml L10E7
L10E7: eor a:L0086,x
lsr a:L0086,x
eor f:L0089,x
rts
L10F2: adc (L0080,x)
per $0ff6
adc $80,S
stz z:L0080
adc z:L0080
ror z:L0080
adc [L0080]
pla
adc #$80
ror A
rtl
L1106: jmp (L0086)
L1109: adc a:L0086
ror a:L0086
adc f:L0089
bvs L1115
L1115: adc (L0080),y
adc (L0080)
adc ($80,S),y
stz z:L0080,x
adc z:L0080,x
ror z:L0080,x
adc [L0080],y
sei
adc L0086,y
ply
tdc
jmp (L0086,x)
L112C: adc a:L0086,x
ror a:L0086,x
adc f:L0089,x
bra L1138
L1138: sta (L0080,x)
brl L113D
L113D: sta $80,S
sty z:L0080
sta z:L0080
stx z:L0080
sta [L0080]
dey
bit #$80
txa
phb
sty a:L0086
sta a:L0086
stx a:L0086
sta f:L0089
bcc L115B
L115B: sta (L0080),y
sta (L0080)
sta ($80,S),y
sty z:L0080,x
sta z:L0080,x
stx z:L0080,y
sta [L0080],y
tya
sta L0086,y
txs
txy
stz a:L0086
sta a:L0086,x
stz a:L0086,x
sta f:L0089,x
ldy #$80
lda (L0080,x)
ldx #$80
lda $80,S
ldy z:L0080
lda z:L0080
ldx z:L0080
lda [L0080]
tay
lda #$80
tax
plb
ldy a:L0086
lda a:L0086
ldx a:L0086
lda f:L0089
bcs L11A0
L11A0: lda (L0080),y
lda (L0080)
lda ($80,S),y
ldy z:L0080,x
lda z:L0080,x
ldx z:L0080,y
lda [L0080],y
clv
lda L0086,y
tsx
tyx
ldy a:L0086,x
lda a:L0086,x
ldx a:L0086,y
lda f:L0089,x
cpy #$80
cmp (L0080,x)
rep #$00
cmp $80,S
cpy z:L0080
cmp z:L0080
dec z:L0080
cmp [L0080]
iny
cmp #$80
dex
wai
cpy a:L0086
cmp a:L0086
dec a:L0086
cmp f:L0089
bne L11E5
L11E5: cmp (L0080),y
cmp (L0080)
cmp ($80,S),y
pei (L0080)
cmp z:L0080,x
dec z:L0080,x
cmp [L0080],y
cld
cmp L0086,y
phx
stp
L11F9: jml [L0086]
L11FC: cmp a:L0086,x
dec a:L0086,x
cmp f:L0089,x
cpx #$80
sbc (L0080,x)
sep #$00
sbc $80,S
cpx z:L0080
sbc z:L0080
inc z:L0080
sbc [L0080]
inx
sbc #$80
nop
xba
cpx a:L0086
sbc a:L0086
inc a:L0086
sbc f:L0089
beq L122A
L122A: sbc (L0080),y
sbc (L0080)
sbc ($80,S),y
pea L0086
sbc z:L0080,x
inc z:L0080,x
sbc [L0080],y
sed
sbc L0086,y
plx
xce
jsr (L0086,x)
sbc a:L0086,x
inc a:L0086,x
sbc f:L0089,x
.org $0080
L0080: bit z:L0082
L0082: bit z:L0082
bit L0082
L0086: bit a:L0086
L0089: lda f:L0089