mirror of
https://github.com/fadden/6502bench.git
synced 2026-04-21 10:16:42 +00:00
Fix Merlin 32 DP op generation
Most assemblers treat '<' and '>' as byte-select operators. Merlin 32 treats them as shift operators, making '<' effectively a no-op. A mask is applied based on the length implied by the opcode or pseudo-op, e.g. "DFB <FOO" will mask off the high bits. This is problematic for instructions like "LDA <FOO", where the choice between absolute and DP addressing depends on the value of "<FOO". If FOO is >= $100, the lack of masking will cause it be treated as absolute. There is currently no other mechanism to force the use of a direct-page opcode. The only recourse is to append "&$ff" to the operand, so the assembler knows that it can be handled as a DP op. The code generator has been updated to add "&$ff" where needed, based on the label's value. Unfortunately the assembler doesn't accept this for certain specific addressing modes; this appears to be a bug. The relevant test cases (2003x-labels-and-symbols) have been updated to exercise these situations. The current test projects side-step the failing assembler behavior by using a DP label instead. These should be changed to correctly exercise the full behavior, with the code generator outputting the instructions as raw hex values to work around bugs. There are some deliberately broken things (like a duplicate label) in the 20030 case that were copied into the 20032 case when it was created. For ease of editing these have been removed from 20032. (issue #170)
This commit is contained in:
+39
-18
@@ -88,6 +88,8 @@ namespace Asm65 {
|
||||
|
||||
/// <summary>String to prefix operand with to force DP addressing.</summary>
|
||||
public string ForceDirectOperandPrefix { get; set; } = string.Empty;
|
||||
/// <summary>String to suffix operand with to force DP addressing.</summary>
|
||||
public string ForceDirectOperandSuffix { get; set; } = string.Empty;
|
||||
/// <summary>String to suffix opcode with to force abs addressing.</summary>
|
||||
public string ForceAbsOpcodeSuffix { get; set; } = string.Empty;
|
||||
/// <summary>String to prefix operand with to force abs addressing.</summary>
|
||||
@@ -172,6 +174,7 @@ namespace Asm65 {
|
||||
BankSelectBackQuote = src.BankSelectBackQuote;
|
||||
|
||||
ForceDirectOperandPrefix = src.ForceDirectOperandPrefix;
|
||||
ForceDirectOperandSuffix = src.ForceDirectOperandSuffix;
|
||||
ForceAbsOpcodeSuffix = src.ForceAbsOpcodeSuffix;
|
||||
ForceAbsOperandPrefix = src.ForceAbsOperandPrefix;
|
||||
ForceDirectOpcodeSuffix = src.ForceDirectOpcodeSuffix;
|
||||
@@ -915,19 +918,23 @@ namespace Asm65 {
|
||||
private string GenerateOperandFormat(OpDef.AddressMode addrMode,
|
||||
OpDef.WidthDisambiguation wdis) {
|
||||
string fmt;
|
||||
string wdisStr = string.Empty;
|
||||
string pfxStr = string.Empty;
|
||||
string dpsfxStr = string.Empty;
|
||||
|
||||
if (wdis == OpDef.WidthDisambiguation.ForceDirect) {
|
||||
if (!string.IsNullOrEmpty(mFormatConfig.ForceDirectOperandPrefix)) {
|
||||
wdisStr = mFormatConfig.ForceDirectOperandPrefix;
|
||||
pfxStr = mFormatConfig.ForceDirectOperandPrefix;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(mFormatConfig.ForceDirectOperandSuffix)) {
|
||||
dpsfxStr = mFormatConfig.ForceDirectOperandSuffix;
|
||||
}
|
||||
} else if (wdis == OpDef.WidthDisambiguation.ForceAbs) {
|
||||
if (!string.IsNullOrEmpty(mFormatConfig.ForceAbsOperandPrefix)) {
|
||||
wdisStr = mFormatConfig.ForceAbsOperandPrefix;
|
||||
pfxStr = mFormatConfig.ForceAbsOperandPrefix;
|
||||
}
|
||||
} else if (wdis == OpDef.WidthDisambiguation.ForceLong) {
|
||||
if (!string.IsNullOrEmpty(mFormatConfig.ForceLongOperandPrefix)) {
|
||||
wdisStr = mFormatConfig.ForceLongOperandPrefix;
|
||||
pfxStr = mFormatConfig.ForceLongOperandPrefix;
|
||||
}
|
||||
} else if (wdis == OpDef.WidthDisambiguation.ForceLongMaybe) {
|
||||
// Don't add a width disambiguator to an operand that is unambiguously long.
|
||||
@@ -940,35 +947,29 @@ namespace Asm65 {
|
||||
case AddressMode.AbsLong:
|
||||
case AddressMode.BlockMove:
|
||||
case AddressMode.StackAbs:
|
||||
case AddressMode.DP:
|
||||
case AddressMode.DPPCRel: // BBR/BBS
|
||||
case AddressMode.DPPCRel: // BBR/BBS (syntax varies)
|
||||
case AddressMode.PCRel:
|
||||
case AddressMode.PCRelLong: // BRL
|
||||
case AddressMode.StackInt: // COP and two-byte BRK
|
||||
case AddressMode.StackPCRelLong: // PER
|
||||
case AddressMode.WDM:
|
||||
fmt = wdisStr + "{0}";
|
||||
fmt = pfxStr + "{0}";
|
||||
break;
|
||||
case AddressMode.AbsIndexX:
|
||||
case AddressMode.AbsIndexXLong:
|
||||
case AddressMode.DPIndexX:
|
||||
fmt = wdisStr + "{0}," + mXregChar;
|
||||
fmt = pfxStr + "{0}," + mXregChar;
|
||||
break;
|
||||
case AddressMode.DPIndexY:
|
||||
case AddressMode.AbsIndexY:
|
||||
fmt = wdisStr + "{0}," + mYregChar;
|
||||
fmt = pfxStr + "{0}," + mYregChar;
|
||||
break;
|
||||
case AddressMode.AbsIndexXInd:
|
||||
case AddressMode.DPIndexXInd:
|
||||
fmt = wdisStr + "({0}," + mXregChar + ")";
|
||||
fmt = pfxStr + "({0}," + mXregChar + ")";
|
||||
break;
|
||||
case AddressMode.AbsInd:
|
||||
case AddressMode.DPInd:
|
||||
case AddressMode.StackDPInd: // PEI
|
||||
fmt = "({0})";
|
||||
break;
|
||||
case AddressMode.AbsIndLong:
|
||||
case AddressMode.DPIndLong:
|
||||
// IIgs monitor uses "()" for AbsIndLong, E&L says "[]". Assemblers
|
||||
// seem to expect the latter.
|
||||
fmt = "[{0}]";
|
||||
@@ -976,11 +977,31 @@ namespace Asm65 {
|
||||
case AddressMode.Acc:
|
||||
fmt = mAccChar;
|
||||
break;
|
||||
case AddressMode.DP:
|
||||
fmt = pfxStr + "{0}" + dpsfxStr;
|
||||
break;
|
||||
case AddressMode.DPIndexX:
|
||||
fmt = pfxStr + "{0}" + dpsfxStr + "," + mXregChar;
|
||||
break;
|
||||
case AddressMode.DPIndexY:
|
||||
fmt = pfxStr + "{0}" + dpsfxStr + "," + mYregChar;
|
||||
break;
|
||||
case AddressMode.DPIndexXInd:
|
||||
fmt = pfxStr + "({0}" + dpsfxStr + "," + mXregChar + ")";
|
||||
break;
|
||||
case AddressMode.DPInd:
|
||||
fmt = "({0}" + dpsfxStr + ")";
|
||||
break;
|
||||
case AddressMode.DPIndLong:
|
||||
// IIgs monitor uses "()" for AbsIndLong, E&L says "[]". Assemblers
|
||||
// seem to expect the latter.
|
||||
fmt = "[{0}" + dpsfxStr + "]";
|
||||
break;
|
||||
case AddressMode.DPIndIndexY:
|
||||
fmt = "({0})," + mYregChar;
|
||||
fmt = "({0}" + dpsfxStr + ")," + mYregChar;
|
||||
break;
|
||||
case AddressMode.DPIndIndexYLong:
|
||||
fmt = "[{0}]," + mYregChar;
|
||||
fmt = "[{0}" + dpsfxStr + "]," + mYregChar;
|
||||
break;
|
||||
case AddressMode.Imm:
|
||||
case AddressMode.ImmLongA:
|
||||
@@ -1021,7 +1042,7 @@ namespace Asm65 {
|
||||
/// <returns>Formatted string.</returns>
|
||||
public string FormatOperand(OpDef op, string contents, OpDef.WidthDisambiguation wdis) {
|
||||
Debug.Assert(((int)op.AddrMode & 0xff) == (int) op.AddrMode);
|
||||
int key = (int) op.AddrMode | ((int)wdis << 8);
|
||||
int key = (int) op.AddrMode | ((int)wdis << 8); // form unique cache key
|
||||
|
||||
if (!mOperandFormats.TryGetValue(key, out string format)) {
|
||||
format = mOperandFormats[key] = GenerateOperandFormat(op.AddrMode, wdis);
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@ namespace Asm65 {
|
||||
/// </summary>
|
||||
public enum WidthDisambiguation : byte {
|
||||
None = 0,
|
||||
ForceDirect, // only needed for forward DP label refs in single-pass assemblers
|
||||
ForceDirect, // mainly needed for forward DP label refs in single-pass assemblers
|
||||
ForceAbs,
|
||||
ForceLong,
|
||||
ForceLongMaybe // add opcode suffix but not operand prefix
|
||||
|
||||
@@ -178,6 +178,7 @@ namespace SourceGen.AsmGen {
|
||||
|
||||
Quirks.NoPcRelBankWrap = true;
|
||||
Quirks.TracksSepRepNotEmu = true;
|
||||
Quirks.ByteSelectionIsShift = true;
|
||||
|
||||
mWorkDirectory = workDirectory;
|
||||
mFileNameBase = fileNameBase;
|
||||
@@ -200,6 +201,7 @@ namespace SourceGen.AsmGen {
|
||||
config.ForceAbsOpcodeSuffix = ":";
|
||||
config.ForceLongOpcodeSuffix = "l";
|
||||
config.ForceDirectOperandPrefix = string.Empty;
|
||||
config.ForceDirectOperandSuffix = "&$ff";
|
||||
config.ForceAbsOperandPrefix = string.Empty;
|
||||
config.ForceLongOperandPrefix = string.Empty;
|
||||
config.LocalVariableLabelPrefix = "]";
|
||||
@@ -841,9 +843,10 @@ namespace SourceGen.AsmGen {
|
||||
}
|
||||
|
||||
// Stdout: "C:\Src\WorkBench\Merlin32.exe v 1.0, (c) Brutal Deluxe ..."
|
||||
// "C:\Src\WorkBench\Merlin32.exe v 1.2 beta 1, (c) Brutal Deluxe ..."
|
||||
// "... Merlin32.exe v 1.2 beta 1, (c) Brutal Deluxe ..."
|
||||
// "... Merlin32_119.exe v1.1.9, (c) Brutal Deluxe ..."
|
||||
// Other platforms may not have the ".exe". Start at first occurrence of " v ".
|
||||
private static string sVersionPattern = @" v (\d.\d)( [^,]+)?,";
|
||||
private static string sVersionPattern = @" v *(\d.\d)(.\d)?( [^,]+)?,";
|
||||
private static Regex sVersionRegex = new Regex(sVersionPattern);
|
||||
|
||||
// IAssembler
|
||||
|
||||
@@ -240,12 +240,13 @@ namespace SourceGen.AsmGen {
|
||||
if (op.IsWidthPotentiallyAmbiguous) {
|
||||
wdis = OpDef.GetWidthDisambiguation(instrLen, operand);
|
||||
}
|
||||
// Some DP addressing modes are ambiguous to one-pass assemblers.
|
||||
if (gen.Quirks.SinglePassAssembler && wdis == OpDef.WidthDisambiguation.None &&
|
||||
(op.AddrMode == OpDef.AddressMode.DP ||
|
||||
op.AddrMode == OpDef.AddressMode.DPIndexX) ||
|
||||
op.AddrMode == OpDef.AddressMode.DPIndexY) {
|
||||
op.AddrMode == OpDef.AddressMode.DPIndexX ||
|
||||
op.AddrMode == OpDef.AddressMode.DPIndexY)) {
|
||||
// Could be a forward reference to a direct-page label. For ACME, we don't
|
||||
// care if it's forward or not.
|
||||
// care if it's forward or not, only that it's referencing a user label.
|
||||
if ((gen.Quirks.SinglePassNoLabelCorrection && IsLabelReference(gen, offset)) ||
|
||||
IsForwardLabelReference(gen, offset)) {
|
||||
wdis = OpDef.WidthDisambiguation.ForceDirect;
|
||||
@@ -343,6 +344,18 @@ namespace SourceGen.AsmGen {
|
||||
formattedOperand = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
|
||||
lvLookup, gen.Localizer.LabelMap, dfd,
|
||||
offset, operandForSymbol, operandLen, opFlags);
|
||||
// Handle special case for DP args with non-DP labels.
|
||||
if (gen.Quirks.ByteSelectionIsShift && wdis == OpDef.WidthDisambiguation.None &&
|
||||
op.IsDirectPageInstruction && dfd.SymbolRef != null) {
|
||||
// The '<' operator is effectively a no-op, so "LDA <LABEL" won't be a DP
|
||||
// instruction unless LABEL is < $100. We need to add an explicit mask in
|
||||
// that case.
|
||||
Debug.Assert(operand < 0x100); // actual operand in code is DP
|
||||
if (proj.SymbolTable.TryGetNonVariableValue(dfd.SymbolRef.Label,
|
||||
out Symbol sym) && sym.Value > 0xff) {
|
||||
wdis = OpDef.WidthDisambiguation.ForceDirect;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Show operand value in hex.
|
||||
|
||||
@@ -280,6 +280,12 @@ namespace SourceGen.AsmGen {
|
||||
/// track the emulation bit?
|
||||
/// </summary>
|
||||
public bool TracksSepRepNotEmu { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Do the byte selection operators ('<', '>', '^') act as shifts that don't
|
||||
/// reduce the value to 8 bits?
|
||||
/// </summary>
|
||||
public bool ByteSelectionIsShift { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Binary file not shown.
@@ -1,8 +1,8 @@
|
||||
### 6502bench SourceGen dis65 v1.0 ###
|
||||
{
|
||||
"_ContentVersion":3,
|
||||
"FileDataLength":452,
|
||||
"FileDataCrc32":-1349239236,
|
||||
"FileDataLength":460,
|
||||
"FileDataCrc32":-736313654,
|
||||
"ProjectProps":{
|
||||
"CpuName":"6502",
|
||||
"IncludeUndocumentedInstr":false,
|
||||
@@ -602,7 +602,37 @@
|
||||
"SubFormat":"Symbol",
|
||||
"SymbolRef":{
|
||||
"Label":"BMI",
|
||||
"Part":"Low"}}},
|
||||
"Part":"Low"}},
|
||||
|
||||
"451":{
|
||||
"Length":2,
|
||||
"Format":"NumericLE",
|
||||
"SubFormat":"Symbol",
|
||||
"SymbolRef":{
|
||||
"Label":"targ",
|
||||
"Part":"Low"}},
|
||||
"453":{
|
||||
"Length":2,
|
||||
"Format":"NumericLE",
|
||||
"SubFormat":"Symbol",
|
||||
"SymbolRef":{
|
||||
"Label":"targ",
|
||||
"Part":"Low"}},
|
||||
"455":{
|
||||
"Length":2,
|
||||
"Format":"NumericLE",
|
||||
"SubFormat":"Symbol",
|
||||
"SymbolRef":{
|
||||
"Label":"targ",
|
||||
"Part":"Low"}},
|
||||
"457":{
|
||||
"Length":2,
|
||||
"Format":"NumericLE",
|
||||
"SubFormat":"Symbol",
|
||||
"SymbolRef":{
|
||||
"Label":"targ",
|
||||
"Part":"Low"}}
|
||||
},
|
||||
|
||||
"LvTables":{
|
||||
},
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -109,6 +109,10 @@ calls jsr L1015
|
||||
.text "he quick brown fox jumps over the lazy dogs."
|
||||
|
||||
L1160 adc #BMI1
|
||||
lda <targ-1
|
||||
lda <targ+1
|
||||
lda <targ-1,x
|
||||
lda (<targ+4),y
|
||||
rts
|
||||
|
||||
.here
|
||||
|
||||
@@ -104,6 +104,10 @@ calls jsr L1015
|
||||
!text "he quick brown fox jumps over the lazy dogs."
|
||||
|
||||
L1160 adc #BMI1
|
||||
lda+1 <targ-1
|
||||
lda+1 <targ+1
|
||||
lda+1 <targ-1,x
|
||||
lda (<targ+4),y
|
||||
rts
|
||||
|
||||
}
|
||||
|
||||
@@ -107,5 +107,9 @@ calls: jsr L1015
|
||||
.byte "he quick brown fox jumps over the lazy dogs."
|
||||
|
||||
L1160: adc #BMI1
|
||||
lda <targ-1
|
||||
lda <targ+1
|
||||
lda <targ-1,x
|
||||
lda (<targ+4),y
|
||||
rts
|
||||
|
||||
|
||||
@@ -103,5 +103,9 @@ calls jsr L1015
|
||||
asc 'he quick brown fox jumps over the lazy dogs.'
|
||||
|
||||
L1160 adc #BMI
|
||||
lda <targ-1&$ff
|
||||
lda <targ+1&$ff
|
||||
lda <targ-1&$ff,x
|
||||
lda (<targ+4&$ff),y
|
||||
rts
|
||||
|
||||
|
||||
@@ -170,9 +170,9 @@ _L103D jmp _targ+1
|
||||
|
||||
_L1040 jml _targ-1
|
||||
|
||||
jml _targ
|
||||
_L1044 jml _targ
|
||||
|
||||
jml _targ+1
|
||||
_L1048 jml _targ+1
|
||||
|
||||
_calls jsr _L1016
|
||||
jsr _L1028
|
||||
@@ -185,8 +185,8 @@ _calls jsr _L1016
|
||||
jsr _L103A
|
||||
jsr _L103D
|
||||
jsr _L1040
|
||||
jsr $1044
|
||||
jsr $1048
|
||||
jsr _L1044
|
||||
jsr _L1048
|
||||
brl _L118E
|
||||
|
||||
_bulk .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f ;bulky
|
||||
@@ -209,6 +209,23 @@ _L118E lda #<thirty2+2
|
||||
lda #(thirty2 & $ffff)+3
|
||||
lda #((thirty2 >> 8) & $ffff)+4
|
||||
lda #thirty2 >> 16
|
||||
before nop
|
||||
lda before
|
||||
lda <before
|
||||
lda <before+1
|
||||
lda (<before+2)
|
||||
lda (<before+3),y
|
||||
lda [<before+4],y
|
||||
lda [<before+5]
|
||||
lda <before+6,x
|
||||
lda (zip+7,x)
|
||||
lda before-$10f8,y
|
||||
lda <after+6
|
||||
lda (<after+7),y
|
||||
after ldx <after+8,y
|
||||
ldy <after+9,x
|
||||
pei (zip+4)
|
||||
nop
|
||||
rts
|
||||
|
||||
.here
|
||||
|
||||
@@ -22,5 +22,6 @@
|
||||
!hex 7420746865206c6162656c20616e6420636f6d6d656e74206f6e6c7920617070
|
||||
!hex 656172206f6e20746865206669727374206c696e652e20205468652071756963
|
||||
!hex 6b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920
|
||||
!hex 646f67732ea97aa959a934c230a97b56a95a34a9341260
|
||||
!hex 646f67732ea97aa959a934c230a97b56a95a34a93412eaad9f11a59fa5a0b2a1
|
||||
!hex b1a2b7a3a7a4b5a5a1d4b9a700a5c0b1c1b6c2b4c3d4d1ea60
|
||||
}
|
||||
|
||||
@@ -167,9 +167,9 @@ start: clc
|
||||
|
||||
@L1040: jml @targ-1
|
||||
|
||||
jml @targ
|
||||
@L1044: jml @targ
|
||||
|
||||
jml @targ+1
|
||||
@L1048: jml @targ+1
|
||||
|
||||
@calls: jsr @L1016
|
||||
jsr @L1028
|
||||
@@ -182,8 +182,8 @@ start: clc
|
||||
jsr @L103A
|
||||
jsr @L103D
|
||||
jsr @L1040
|
||||
jsr $1044
|
||||
jsr $1048
|
||||
jsr @L1044
|
||||
jsr @L1048
|
||||
brl @L118E
|
||||
|
||||
@bulk: .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f ;bulky
|
||||
@@ -206,5 +206,22 @@ start: clc
|
||||
lda #thirty2 & $ffff +3
|
||||
lda #thirty2 >> 8 & $ffff +4
|
||||
lda #thirty2 >> 16
|
||||
before: nop
|
||||
lda before
|
||||
lda <before
|
||||
lda <before+1
|
||||
lda (<before+2)
|
||||
lda (<before+3),y
|
||||
lda [<before+4],y
|
||||
lda [<before+5]
|
||||
lda <before+6,x
|
||||
lda (zip+7,x)
|
||||
lda before-$10f8,y
|
||||
lda z:<after+6
|
||||
lda (<after+7),y
|
||||
after: ldx <after+8,y
|
||||
ldy <after+9,x
|
||||
pei (zip+4)
|
||||
nop
|
||||
rts
|
||||
|
||||
|
||||
@@ -162,9 +162,9 @@ start clc
|
||||
|
||||
:L1040 jml :targ-1
|
||||
|
||||
jml :targ
|
||||
:L1044 jml :targ
|
||||
|
||||
jml :targ+1
|
||||
:L1048 jml :targ+1
|
||||
|
||||
:calls jsr :L1016
|
||||
jsr :L1028
|
||||
@@ -177,8 +177,8 @@ start clc
|
||||
jsr :L103A
|
||||
jsr :L103D
|
||||
jsr :L1040
|
||||
jsr $1044
|
||||
jsr $1048
|
||||
jsr :L1044
|
||||
jsr :L1048
|
||||
brl :L118E
|
||||
|
||||
:bulk hex 808182838485868788898a8b8c8d8e8f808182838485868788898a8b8c8d8e8f ;bulky
|
||||
@@ -197,5 +197,22 @@ start clc
|
||||
lda #thirty2+3
|
||||
lda #>thirty2+$400
|
||||
lda #^thirty2
|
||||
before nop
|
||||
lda before
|
||||
lda <before&$ff
|
||||
lda <before+1&$ff
|
||||
dfb $b2,$a1
|
||||
lda (<before+3&$ff),y
|
||||
lda [<before+4&$ff],y
|
||||
dfb $a7,$a4
|
||||
lda <before+6&$ff,x
|
||||
lda (zip+7,x)
|
||||
lda before-$10f8,y
|
||||
lda <after+6&$ff
|
||||
lda (<after+7&$ff),y
|
||||
after ldx <after+8&$ff,y
|
||||
ldy <after+9&$ff,x
|
||||
pei (zip+4)
|
||||
nop
|
||||
rts
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ L1000 lda CodeWrap+255
|
||||
lda $4000
|
||||
lda $4001
|
||||
lda BankWrap+8
|
||||
lda <BankWrap-$ffe8
|
||||
lda <BankWrap-$ffe8&$ff
|
||||
nop
|
||||
lda ReadOnly
|
||||
lda ReadOnly+1
|
||||
|
||||
@@ -131,4 +131,8 @@ next
|
||||
|
||||
:skiphex
|
||||
adc #$30 ;set to BMI to test constant vs. instr
|
||||
lda <target0&$ff
|
||||
lda <target2&$ff
|
||||
lda <target0&$ff,x
|
||||
lda (<target2+3&$ff),y
|
||||
rts
|
||||
|
||||
@@ -228,5 +228,28 @@ t4c jml target2
|
||||
lda #>thirty2 + 1024
|
||||
lda #^thirty2
|
||||
|
||||
; test direct-page access with a non-DP label, forward and backward, with adjustments
|
||||
before nop
|
||||
|
||||
lda before
|
||||
lda <before&$ff
|
||||
|
||||
lda <before+1&$ff
|
||||
lda (<before+2&$ff)
|
||||
lda (<before+3&$ff),y
|
||||
lda [<before+4&$ff],y
|
||||
lda [<before+5&$ff]
|
||||
lda <before+6&$ff,x
|
||||
lda (<zip+7&$ff,x) ;all versions of Merlin 32 fail
|
||||
lda <before+8&$ff,y
|
||||
|
||||
lda <after&$ff
|
||||
lda (<after+1&$ff),y
|
||||
ldx <after+2&$ff,y
|
||||
ldy <after+3&$ff,x
|
||||
pei (<zip+4&$ff)
|
||||
|
||||
after nop
|
||||
|
||||
rts
|
||||
|
||||
|
||||
@@ -330,8 +330,11 @@ target assembler doesn't handle it.</p>
|
||||
<a href="https://www.brutaldeluxe.fr/products/crossdevtools/merlin/">[web site]</a>
|
||||
<a href="https://github.com/apple2accumulator/merlin32/issues">[bug tracker]</a>
|
||||
</p>
|
||||
<p>The history is somewhat complicated, as there are two different versions
|
||||
of Merlin v1.1, updated by different authors. These are referred to as
|
||||
the "official" and "forked" versions.</p>
|
||||
|
||||
<p>Bugs:</p>
|
||||
<p>Bugs (present in v1.0; unclear if/when these have been fixed):</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>
|
||||
@@ -357,6 +360,8 @@ target assembler doesn't handle it.</p>
|
||||
<li>The byte selection operators ('<', '>', '^') are actually
|
||||
word-selection operators, yielding 16-bit values when wide registers
|
||||
are enabled on the 65816.</li>
|
||||
<li>It's not possible to force direct-page addressing with a modifier.
|
||||
"&$ff" must be added to the operand.</li>
|
||||
<li>Values loaded into registers are implicitly mod 256 or 65536. There
|
||||
is no need to explicitly mask an expression.</li>
|
||||
<li>The assembler tracks register widths when it sees SEP/REP instructions,
|
||||
|
||||
Reference in New Issue
Block a user