mirror of
https://github.com/KarolS/millfork.git
synced 2024-05-31 18:41:30 +00:00
721 lines
25 KiB
Scala
721 lines
25 KiB
Scala
package millfork.assembly.mos
|
|
|
|
import millfork.assembly.{AbstractCode, Elidability, SourceLine}
|
|
import millfork.assembly.mos.Opcode._
|
|
import millfork.compiler.CompilationContext
|
|
import millfork.compiler.mos.MosCompiler
|
|
import millfork.env._
|
|
import millfork.node.Position
|
|
import millfork.{CompilationFlag, CompilationOptions}
|
|
|
|
//noinspection TypeAnnotation
|
|
object OpcodeClasses {
|
|
|
|
val SingleBit = Set(
|
|
BBR0, BBR1, BBR2, BBR3, BBR4, BBR5, BBR6, BBR7,
|
|
BBS0, BBS1, BBS2, BBS3, BBS4, BBS5, BBS6, BBS7,
|
|
RMB0, RMB1, RMB2, RMB3, RMB4, RMB5, RMB6, RMB7,
|
|
SMB0, SMB1, SMB2, SMB3, SMB4, SMB5, SMB6, SMB7
|
|
)
|
|
val SingleBitBranch = Set(
|
|
BBR0, BBR1, BBR2, BBR3, BBR4, BBR5, BBR6, BBR7,
|
|
BBS0, BBS1, BBS2, BBS3, BBS4, BBS5, BBS6, BBS7
|
|
)
|
|
|
|
val HudsonTransfer = Set(TAI, TIA, TDD, TIN, TII)
|
|
|
|
val ReadsAAlways = Set(
|
|
ADC, AND, BIT, CMP, EOR, ORA, PHA, SBC, STA,
|
|
ADC_W, AND_W, BIT_W, CMP_W, EOR_W, ORA_W, PHA_W, SBC_W, STA_W,
|
|
TAX, TAY,
|
|
SAX, SBX, ANC, DCP, ISC, RRA, RLA, SRE, SLO, LXA, XAA, AHX, TAS,
|
|
TSB, TRB,
|
|
TSB_W, TRB_W,
|
|
TAZ, TAB,
|
|
HuSAX, SAY, TAM,
|
|
TCD, TCS, XBA,
|
|
)
|
|
val ReadsAIfImplied = Set(
|
|
ASL, LSR, ROL, ROR, INC, DEC,
|
|
DEC_W, INC_W, ROL_W, ROR_W, ASL_W, LSR_W,
|
|
)
|
|
val ReadsAHAlways = Set(
|
|
ADC_W, AND_W, BIT_W, CMP_W, EOR_W, ORA_W, PHA_W, SBC_W, STA_W,
|
|
TCD, TCS, XBA,
|
|
TSB_W, TRB_W,
|
|
)
|
|
val ReadsAHIfImplied = Set(
|
|
DEC_W, INC_W, ROL_W, ROR_W, ASL_W, LSR_W,
|
|
)
|
|
val ReadsXAlways = Set(
|
|
CPX, DEX, INX, STX,
|
|
CPX_W, DEX_W, INX_W, STX_W,
|
|
TXA, TXS, SBX,
|
|
PLX, PLX_W,
|
|
XAA, SAX, AHX, SHX, TAS,
|
|
HuSAX, SXY, SET,
|
|
TXY,
|
|
)
|
|
val ReadsYAlways = Set(CPY, DEY, INY, STY, TYA, PLY, SHY, SAY, SXY, TYX)
|
|
val ReadsIZAlways = Set(CPZ, DEZ, INZ, STZ, TZA, PLZ)
|
|
val ReadsM = Set(
|
|
ORA, AND, EOR, ADC, SBC, CMP, LDA, STA,
|
|
ORA_W, AND_W, EOR_W, ADC_W, SBC_W, CMP_W, LDA_W, STA_W,
|
|
STZ, BIT,
|
|
STZ_W, BIT_W,
|
|
PHA, PLA, PHP,
|
|
PHA_W, PLA_W,
|
|
DEC, INC, ROL, ROR, ASL, LSR,
|
|
DEC_W, INC_W, ROL_W, ROR_W, ASL_W, LSR_W,
|
|
TAX, TXA, TAY, TYA)
|
|
val ReadsW = Set(
|
|
LDX, LDY, CPX, CPY, STX, STY, INX, INY, DEX, DEY,
|
|
LDX_W, LDY_W, CPX_W, CPY_W, STX_W, STY_W, INX_W, INY_W, DEX_W, DEY_W,
|
|
PLX, PLY, PHX, PHY, PHP,
|
|
PLX_W, PLY_W, PHX_W, PHY_W,
|
|
TAX, TXA, TAY, TYA, TXY, TYX)
|
|
val ReadsZ = Set(BNE, BEQ, PHP)
|
|
val ReadsN = Set(BMI, BPL, PHP)
|
|
val ReadsNOrZ = ReadsZ ++ ReadsN
|
|
val ReadsV = Set(BVS, BVC, PHP)
|
|
val ReadsD = Set(PHP, ADC, SBC, RRA, ARR, ISC, ADC_W, SBC_W)
|
|
val ReadsC = Set(
|
|
PHP, BCC, BCS,
|
|
ADC, SBC, ROL, ROR,
|
|
ADC_W, SBC_W, ROL_W, ROR_W,
|
|
ALR, ARR, ISC, RLA, RRA, SLO, SRE,
|
|
XCE
|
|
)
|
|
|
|
val ChangesAAlways = Set(
|
|
TXA, TYA, PLA, PLA_W,
|
|
ORA, AND, EOR, ADC, LDA, SBC,
|
|
ORA_W, AND_W, EOR_W, ADC_W, LDA_W, SBC_W,
|
|
SLO, RLA, SRE, RRA, LAX, ISC,
|
|
XAA, ANC, ALR, ARR, LXA, LAS,
|
|
TZA, NEG,
|
|
TMA,
|
|
XBA, TDC,
|
|
HuSAX, SAY,
|
|
)
|
|
|
|
val ChangesAIfImplied = Set(
|
|
ASL, LSR, ROL, ROR, INC, DEC,
|
|
ASL_W, LSR_W, ROL_W, ROR_W, INC_W, DEC_W,
|
|
)
|
|
|
|
val ChangesAHAlways = Set(
|
|
PLA_W,
|
|
ORA_W, AND_W, EOR_W, ADC_W, LDA_W, SBC_W,
|
|
XBA, TDC,
|
|
)
|
|
|
|
val ChangesAHIfImplied = Set(
|
|
ASL, LSR, ROL, ROR, INC, DEC,
|
|
ASL_W, LSR_W, ROL_W, ROR_W, INC_W, DEC_W,
|
|
)
|
|
|
|
val ChangesX = Set(
|
|
DEX, INX, LDX,
|
|
DEX_W, INX_W, LDX_W,
|
|
TAX, TSX,
|
|
SBX, LAX, LXA, LAS,
|
|
PLX, PLX_W,
|
|
TYX, SXY,
|
|
)
|
|
val ChangesY = Set(
|
|
DEY, INY, LDY,
|
|
DEY_W, INY_W, LDY_W,
|
|
TAY,
|
|
PLY, PLY_W,
|
|
TXY, SXY,
|
|
)
|
|
val ChangesIZ = Set(
|
|
DEZ, INZ, TAZ, LDZ,
|
|
)
|
|
val ChangesDirectPageRegister = Set(XCE, PLD, TCD)
|
|
val ChangesDataBankRegister = Set(XCE, PLB, MVN, MVP)
|
|
val ChangesS = Set(
|
|
PHA, PLA,
|
|
PHA_W, PLA_W,
|
|
PHP, PLP, TXS,
|
|
PHX, PHY, PLX, PLY,
|
|
PHX_W, PHY_W, PLX_W, PLY_W,
|
|
TAS, LAS,
|
|
PHZ,
|
|
PHB, PHD, PHK, PLB, PLD, RTL,
|
|
PEA, PEI, PER,
|
|
XCE, TCS, TYS,
|
|
)
|
|
val ChangesMemoryAlways = Set(
|
|
STA, STY, STZ, STX,
|
|
STA_W, STY_W, STZ_W, STX_W,
|
|
TRB, TSB,
|
|
TRB_W, TSB_W,
|
|
SAX, DCP, ISC,
|
|
SLO, RLA, SRE, RRA,
|
|
AHX, SHY, SHX, TAS, LAS,
|
|
TAM, TIN, TII, TIA, TAI, TST, TDD,
|
|
COP,
|
|
CHANGED_MEM,
|
|
)
|
|
val ChangesMemoryIfNotImplied = Set(
|
|
DEC, INC, ASL, ROL, LSR, ROR,
|
|
DEC_W, INC_W, ASL_W, ROL_W, LSR_W, ROR_W,
|
|
)
|
|
val ReadsMemoryIfNotImpliedOrImmediate = Set(
|
|
LDY, CPX, CPY, BIT,
|
|
LDY_W, CPX_W, CPY_W, BIT_W,
|
|
ORA, AND, EOR, ADC, LDA, CMP, SBC,
|
|
ORA_W, AND_W, EOR_W, ADC_W, LDA_W, CMP_W, SBC_W,
|
|
ASL, ROL, LSR, ROR, LDX, DEC, INC,
|
|
ASL_W, ROL_W, LSR_W, ROR_W, LDX_W, DEC_W, INC_W,
|
|
SLO, RLA, SRE, RRA, LAX, DCP, ISC,
|
|
LAS,
|
|
TRB, TSB,
|
|
TRB_W, TSB_W,
|
|
TST, TAM, TII, TAI, TIN, TIA, TDD,
|
|
CHANGED_MEM,
|
|
) ++ SingleBitBranch
|
|
|
|
val AccessesWordInMemory = Set(
|
|
LDA_W, LDX_W, LDY_W,
|
|
STA_W, STX_W, STY_W,
|
|
CMP_W, CPX_W, CPY_W,
|
|
DEC_W, INC_W, ASL_W, ROL_W, LSR_W, ROR_W,
|
|
ORA_W, AND_W, EOR_W, ADC_W, SBC_W,
|
|
TSB_W, TRB_W, BIT_W,
|
|
PHW,
|
|
)
|
|
|
|
val AccessesWordInMemoryAlwaysIfNotImplied = Set(
|
|
)
|
|
|
|
val StoresByte = Set(STA, STX, STY, STZ, SAX)
|
|
val StoresWord = Set(STA_W, STX_W, STY_W, STZ_W)
|
|
|
|
val OverwritesA = Set(
|
|
LDA, PLA,
|
|
LDA_W, PLA_W,
|
|
TXA, TYA,
|
|
LAX, LAS,
|
|
TBA, TZA,
|
|
HuSAX, SAY,
|
|
TDC, TSC,
|
|
)
|
|
val OverwritesAH = Set(
|
|
LDA_W, PLA_W,
|
|
TDC, TSC,
|
|
)
|
|
val OverwritesX = Set(
|
|
TAX, LDX, TSX, PLX,
|
|
LAX, LAS,
|
|
TYX, HuSAX, SXY,
|
|
)
|
|
val OverwritesY = Set(
|
|
TAY, LDY, PLY,
|
|
TSY, TXY, SAY, SXY,
|
|
)
|
|
val OverwritesIZ = Set(
|
|
TAZ, LDZ, PLZ,
|
|
)
|
|
val OverwritesC = Set(CLC, SEC, PLP, XCE)
|
|
val OverwritesD = Set(CLD, SED, PLP)
|
|
val OverwritesI = Set(CLI, SEI, PLP)
|
|
val OverwritesV = Set(CLV, PLP)
|
|
val ConcernsAAlways = ReadsAAlways ++ ChangesAAlways
|
|
val ConcernsAHAlways = ReadsAHAlways ++ ChangesAHAlways
|
|
val ConcernsAIfImplied = ReadsAIfImplied ++ ChangesAIfImplied
|
|
val ConcernsAHIfImplied = ReadsAHIfImplied ++ ChangesAHIfImplied
|
|
val ConcernsXAlways = ReadsXAlways | ChangesX
|
|
val ConcernsYAlways = ReadsYAlways | ChangesY
|
|
val ConcernsIZAlways = ReadsIZAlways | ChangesIZ
|
|
|
|
val ChangesStack = Set(
|
|
PHA, PLA, PHP, PLP,
|
|
PHX, PLX, PHY, PLY,
|
|
PHA_W, PLA_W,
|
|
PHX_W, PLX_W, PHY_W, PLY_W,
|
|
TXS,
|
|
JSR, RTS, RTI,
|
|
TAS, LAS,
|
|
PHW, PHZ, PLZ,
|
|
TYS, TCS,
|
|
RTL, BSR,
|
|
PHB, PHD, PHK, PLB, PLD,
|
|
PEA, PEI, PER,
|
|
XCE,
|
|
)
|
|
|
|
val ConcernsStackAlways = ChangesStack ++ Set(TSX, TSY, TSC)
|
|
val ConcernsSAlways = ChangesS ++ Set(TSX, TSY, TSC)
|
|
|
|
val ChangesNAndZ = Set(
|
|
ADC, AND, ASL, BIT, CMP, CPX, CPY, DEC, DEX, DEY, EOR, INC, INX, INY, LDA,
|
|
ADC_W, AND_W, ASL_W, BIT_W, CMP_W, CPX_W, CPY_W, DEC_W, DEX_W, DEY_W, EOR_W, INC_W, INX_W, INY_W, LDA_W,
|
|
LDX, LDY, LSR, ORA, PLP, ROL, ROR, SBC, TAX, TAY, TXA, TYA,
|
|
LDX_W, LDY_W, LSR_W, ORA_W, ROL_W, ROR_W, SBC_W,
|
|
LAX, SBX, ANC, ALR, ARR, DCP, ISC, RLA, RRA, SLO, SRE, SAX,
|
|
TSB, TRB, // These two do not change N, but lets pretend they do for simplicity
|
|
TSB_W, TRB_W,
|
|
NEG, ASR,
|
|
CPZ, DEZ, INZ, LDZ,
|
|
REP, SEP, // People usually don't use there to change N or Z, but let's assume they do
|
|
)
|
|
val ChangesC = Set(
|
|
PLP, CLC, SEC,
|
|
ADC, ASL, CMP, CPX, CPY, LSR, ROL, ROR, SBC,
|
|
ADC_W, ASL_W, CMP_W, CPX_W, CPY_W, LSR_W, ROL_W, ROR_W, SBC_W,
|
|
SBX, ANC, ALR, ARR, DCP, ISC, RLA, RRA, SLO, SRE,
|
|
CPZ, ASR,
|
|
XCE,
|
|
REP, SEP, // People usually don't use there to change C, but let's assume they do
|
|
)
|
|
val ChangesV = Set(
|
|
ADC, BIT, PLP, SBC,
|
|
ARR, ISC, RRA,
|
|
REP, SEP, // People usually don't use there to change V, but let's assume they do
|
|
)
|
|
|
|
val SupportsAbsoluteX = Set(
|
|
ORA, AND, EOR, ADC, CMP, SBC,
|
|
ORA_W, AND_W, EOR_W, ADC_W, CMP_W, SBC_W,
|
|
ASL, ROL, LSR, ROR, DEC, INC,
|
|
ASL_W, ROL_W, LSR_W, ROR_W, DEC_W, INC_W,
|
|
SLO, RLA, SRE, RRA, DCP, ISC,
|
|
STA, LDA, LDY, STZ,
|
|
STA_W, LDA_W, LDY_W, STZ_W,
|
|
SHY,
|
|
)
|
|
|
|
val SupportsZeroPageX = Set(
|
|
ORA, AND, EOR, ADC, CMP, SBC,
|
|
ORA_W, AND_W, EOR_W, ADC_W, CMP_W, SBC_W,
|
|
ASL, ROL, LSR, ROR, DEC, INC,
|
|
ASL_W, ROL_W, LSR_W, ROR_W, DEC_W, INC_W,
|
|
SLO, RLA, SRE, RRA, DCP, ISC,
|
|
ASR,
|
|
STA, LDA, LDY, STY, STZ,
|
|
STA_W, LDA_W, LDY_W, STY_W, STZ_W,
|
|
)
|
|
|
|
val SupportsAbsoluteY = Set(
|
|
ORA, AND, EOR, ADC, CMP, SBC,
|
|
ORA_W, AND_W, EOR_W, ADC_W, CMP_W, SBC_W,
|
|
SLO, RLA, SRE, RRA, DCP, ISC,
|
|
STA, LDA, LDX,
|
|
STA_W, LDA_W, LDX_W,
|
|
LAX, AHX, SHX, TAS, LAS,
|
|
)
|
|
|
|
val SupportsZeroPageY = Set(
|
|
LDX, STX,
|
|
LDX_W, STX_W,
|
|
SAX, LAX,
|
|
)
|
|
|
|
val SupportsAbsolute = Set(
|
|
ORA, AND, EOR, ADC, STA, LDA, CMP, SBC,
|
|
ORA_W, AND_W, EOR_W, ADC_W, STA_W, LDA_W, CMP_W, SBC_W,
|
|
ASL, ROL, LSR, ROR, STX, LDX, DEC, INC,
|
|
ASL_W, ROL_W, LSR_W, ROR_W, STX_W, LDX_W, DEC_W, INC_W,
|
|
SLO, RLA, SRE, RRA, SAX, LAX, DCP, ISC,
|
|
STY, LDY,
|
|
BIT, JMP, JSR,
|
|
STZ, TRB, TSB,
|
|
LDZ,
|
|
)
|
|
|
|
val SupportsIndexedZ = Set(
|
|
ORA, AND, EOR, ADC, STA, LDA, CMP, SBC,
|
|
ORA_W, AND_W, EOR_W, ADC_W, STA_W, LDA_W, CMP_W, SBC_W,
|
|
)
|
|
|
|
val SupportsLongIndexedZ = Set(
|
|
ORA, AND, EOR, ADC, STA, LDA, CMP, SBC,
|
|
ORA_W, AND_W, EOR_W, ADC_W, STA_W, LDA_W, CMP_W, SBC_W,
|
|
)
|
|
|
|
val ShortConditionalBranching = Set(BEQ, BNE, BMI, BPL, BVC, BVS, BCC, BCS)
|
|
val ShortBranching = ShortConditionalBranching + BRA
|
|
val AllDirectJumps = ShortBranching ++ Set(JMP, BRL)
|
|
val AllLinear = Set(
|
|
ORA, AND, EOR,
|
|
ORA_W, AND_W, EOR_W,
|
|
ADC, SBC, CMP, CPX, CPY,
|
|
ADC_W, SBC_W, CMP_W, CPX_W, CPY_W,
|
|
DEC, DEX, DEY, INC, INX, INY,
|
|
DEC_W, DEX_W, DEY_W, INC_W, INX_W, INY_W,
|
|
ASL, ROL, LSR, ROR,
|
|
ASL_W, ROL_W, LSR_W, ROR_W,
|
|
LDA, STA, LDX, STX, LDY, STY,
|
|
LDA_W, STA_W, LDX_W, STX_W, LDY_W, STY_W,
|
|
TAX, TXA, TAY, TYA, TXS, TSX,
|
|
PLA, PLP, PHA, PHP,
|
|
PLA_W, PHA_W,
|
|
BIT, BIT_W, NOP,
|
|
CLC, SEC, CLD, SED, CLI, SEI, CLV,
|
|
STZ, PHX, PHY, PLX, PLY, TSB, TRB,
|
|
STZ_W, PHX_W, PHY_W, PLX_W, PLY_W, TSB_W, TRB_W,
|
|
SLO, RLA, SRE, RRA, SAX, LAX, DCP, ISC,
|
|
ANC, ALR, ARR, XAA, LXA, SBX, KIL,
|
|
CPZ, LDZ, INZ, DEZ,
|
|
TAZ, TZA, TYS, TSY,
|
|
TBA,
|
|
PLZ, PHZ, PHW,
|
|
CLA, CLX, CLY,
|
|
CSH, CSL,
|
|
TXY, TYX, XBA,
|
|
PHD, PHB, PHK,
|
|
DISCARD_AF, DISCARD_XF, DISCARD_YF, CHANGED_MEM)
|
|
|
|
val NoopDiscardsFlags = Set(DISCARD_AF, DISCARD_XF, DISCARD_YF)
|
|
val DiscardsV = NoopDiscardsFlags | OverwritesV
|
|
val DiscardsC = NoopDiscardsFlags | OverwritesC
|
|
val DiscardsD = OverwritesD
|
|
val DiscardsI = NoopDiscardsFlags | OverwritesI
|
|
|
|
val SupportsZeropage = Set(
|
|
LDA, LDX, LDY, LAX, // LDZ doesn't!
|
|
LDA_W, LDX_W, LDY_W,
|
|
STA, STX, STY, SAX, STZ,
|
|
STA_W, LDX_W, LDY_W,
|
|
BIT,
|
|
CPX, CPY, CPZ,
|
|
ADC, SBC, CMP, AND, ORA, EOR,
|
|
ADC_W, SBC_W, CMP_W, AND_W, ORA_W, EOR_W,
|
|
INC, DEC, ROL, ROR, ASL, LSR,
|
|
INC_W, DEC_W, ROL_W, ROR_W, ASL_W, LSR_W,
|
|
ISC, DCP, RLA, RRA, SLO, SRE,
|
|
TSB, TRB,
|
|
)
|
|
|
|
}
|
|
|
|
object AssemblyLine {
|
|
|
|
val accu8: AssemblyLine = AssemblyLine.immediate(SEP, 0x20)
|
|
val accu16: AssemblyLine = AssemblyLine.immediate(REP, 0x20)
|
|
val index8: AssemblyLine = AssemblyLine.immediate(SEP, 0x10)
|
|
val index16: AssemblyLine = AssemblyLine.immediate(REP, 0x10)
|
|
|
|
def treatment(lines: List[AssemblyLine], state: State.Value): Treatment.Value =
|
|
lines.map(_.treatment(state)).foldLeft(Treatment.Unchanged)(_ ~ _)
|
|
|
|
def label(label: String): AssemblyLine = AssemblyLine.label(Label(label))
|
|
|
|
def label(label: Label): AssemblyLine = AssemblyLine(LABEL, AddrMode.DoesNotExist, label.toAddress)
|
|
|
|
def discardAF() = AssemblyLine(DISCARD_AF, AddrMode.DoesNotExist, Constant.Zero)
|
|
|
|
def discardXF() = AssemblyLine(DISCARD_XF, AddrMode.DoesNotExist, Constant.Zero)
|
|
|
|
def discardYF() = AssemblyLine(DISCARD_YF, AddrMode.DoesNotExist, Constant.Zero)
|
|
|
|
def immediate(opcode: Opcode.Value, value: Long) = AssemblyLine(opcode, AddrMode.Immediate, NumericConstant(value, 1))
|
|
|
|
def immediate(opcode: Opcode.Value, value: Constant) = AssemblyLine(opcode, AddrMode.Immediate, value)
|
|
|
|
def implied(opcode: Opcode.Value) = AssemblyLine(opcode, AddrMode.Implied, Constant.Zero)
|
|
|
|
private val opcodesForNopVariableOperation = Set(STA, SAX, STX, STY, STZ)
|
|
private val opcodesForZeroedVariableOperation = Set(ADC, EOR, ORA, AND, SBC, CMP, CPX, CPY)
|
|
private val opcodesForZeroedOrSignExtendedVariableOperation = Set(LDA, LDX, LDY, LDZ)
|
|
|
|
def variable(ctx: CompilationContext, opcode: Opcode.Value, variable: Variable, offset: Int = 0, preserveA: Boolean = false): List[AssemblyLine] =
|
|
if (offset >= variable.typ.size) {
|
|
if (opcodesForNopVariableOperation(opcode)) {
|
|
Nil
|
|
} else if (opcodesForZeroedVariableOperation(opcode)) {
|
|
if (variable.typ.isSigned) ???
|
|
List(AssemblyLine.immediate(opcode, 0))
|
|
} else if (opcodesForZeroedOrSignExtendedVariableOperation(opcode)) {
|
|
if (variable.typ.isSigned) {
|
|
val label = ctx.nextLabel("sx")
|
|
val loadHiByteToA = AssemblyLine.variable(ctx, LDA, variable, variable.typ.size - 1)
|
|
val signExtend = List(
|
|
AssemblyLine.immediate(ORA, 0x7f),
|
|
AssemblyLine.relative(BMI, label),
|
|
AssemblyLine.immediate(LDA, 0),
|
|
AssemblyLine.label(label))
|
|
if (preserveA) {
|
|
opcode match {
|
|
case LDA => loadHiByteToA ++ signExtend
|
|
case LAX => loadHiByteToA ++ signExtend ++ List(AssemblyLine.implied(TAX))
|
|
case LDX => loadHiByteToA ++ List(AssemblyLine.implied(PHA)) ++ signExtend ++ List(AssemblyLine.implied(TAX), AssemblyLine.implied(PLA))
|
|
case LDY => loadHiByteToA ++ List(AssemblyLine.implied(PHA)) ++ signExtend ++ List(AssemblyLine.implied(TAY), AssemblyLine.implied(PLA))
|
|
case LDZ => loadHiByteToA ++ List(AssemblyLine.implied(PHA)) ++ signExtend ++ List(AssemblyLine.implied(TAZ), AssemblyLine.implied(PLA))
|
|
}
|
|
} else {
|
|
opcode match {
|
|
case LDA => loadHiByteToA ++ signExtend
|
|
case LDX | LAX => loadHiByteToA ++ signExtend ++ List(AssemblyLine.implied(TAX))
|
|
case LDY => loadHiByteToA ++ signExtend ++ List(AssemblyLine.implied(TAY))
|
|
case LDZ => loadHiByteToA ++ signExtend ++ List(AssemblyLine.implied(TAZ))
|
|
}
|
|
}
|
|
} else {
|
|
List(AssemblyLine.immediate(opcode, 0))
|
|
}
|
|
} else {
|
|
???
|
|
}
|
|
} else {
|
|
val elidability = if (variable.isVolatile) Elidability.Volatile else Elidability.Elidable
|
|
variable match {
|
|
case v@MemoryVariable(_, _, VariableAllocationMethod.Zeropage) =>
|
|
List(AssemblyLine.zeropage(opcode, v.toAddress + offset).copy(elidability = elidability))
|
|
case v@RelativeVariable(_, _, _, true, None, _) =>
|
|
List(AssemblyLine.zeropage(opcode, v.toAddress + offset).copy(elidability = elidability))
|
|
case v: VariableInMemory => List(AssemblyLine.absolute(opcode, v.toAddress + offset).copy(elidability = elidability))
|
|
case v: StackVariable =>
|
|
AssemblyLine.tsx(ctx) :+ AssemblyLine.dataStackX(ctx, opcode, v, offset)
|
|
}
|
|
}
|
|
|
|
def zeropage(opcode: Opcode.Value, addr: Constant) =
|
|
AssemblyLine(opcode, AddrMode.ZeroPage, addr)
|
|
|
|
def zeropage(opcode: Opcode.Value, thing: ThingInMemory, offset: Int = 0) =
|
|
AssemblyLine(opcode, AddrMode.ZeroPage, thing.toAddress + offset)
|
|
|
|
def absolute(opcode: Opcode.Value, addr: Constant) =
|
|
AssemblyLine(opcode, AddrMode.Absolute, addr)
|
|
|
|
def absoluteOrLongAbsolute(opcode: Opcode.Value, thing: ThingInMemory, options: CompilationOptions): AssemblyLine =
|
|
if (thing.isFar(options)) AssemblyLine(opcode, AddrMode.LongAbsolute, thing.toAddress)
|
|
else AssemblyLine(opcode, AddrMode.Absolute, thing.toAddress)
|
|
|
|
def absolute(opcode: Opcode.Value, thing: ThingInMemory, offset: Int = 0) =
|
|
AssemblyLine(opcode, AddrMode.Absolute, thing.toAddress + offset)
|
|
|
|
def relative(opcode: Opcode.Value, thing: ThingInMemory, offset: Int = 0) =
|
|
AssemblyLine(opcode, AddrMode.Relative, thing.toAddress + offset)
|
|
|
|
def relative(opcode: Opcode.Value, label: String) =
|
|
AssemblyLine(opcode, AddrMode.Relative, Label(label).toAddress)
|
|
|
|
def absoluteY(opcode: Opcode.Value, addr: Constant) =
|
|
AssemblyLine(opcode, AddrMode.AbsoluteY, addr)
|
|
|
|
def absoluteY(opcode: Opcode.Value, thing: ThingInMemory, offset: Int = 0) =
|
|
AssemblyLine(opcode, AddrMode.AbsoluteY, thing.toAddress + offset)
|
|
|
|
def absoluteX(opcode: Opcode.Value, addr: Int) =
|
|
AssemblyLine(opcode, AddrMode.AbsoluteX, NumericConstant(addr, 2))
|
|
|
|
def dataStackX(ctx: CompilationContext, opcode: Opcode.Value, v: StackVariable): AssemblyLine =
|
|
dataStackX(ctx, opcode, v.baseOffset)
|
|
|
|
def dataStackX(ctx: CompilationContext, opcode: Opcode.Value, v: StackVariable, offset: Int): AssemblyLine =
|
|
dataStackX(ctx, opcode, v.baseOffset + offset)
|
|
|
|
def dataStackX(ctx: CompilationContext, opcode: Opcode.Value, offset: Int): AssemblyLine =
|
|
if (ctx.options.flag(CompilationFlag.SoftwareStack)) {
|
|
val stack = ctx.env.get[ThingInMemory]("__stack")
|
|
AssemblyLine.absoluteX(opcode, stack.toAddress + (offset - 0x100))
|
|
} else if (ctx.options.flag(CompilationFlag.EmitEmulation65816Opcodes)) {
|
|
AssemblyLine.stackRelative(opcode, offset + ctx.extraStackOffset)
|
|
} else {
|
|
AssemblyLine.absoluteX(opcode, offset + ctx.extraStackOffset)
|
|
}
|
|
|
|
def tsx(ctx: CompilationContext): List[AssemblyLine] =
|
|
if (ctx.options.flag(CompilationFlag.SoftwareStack)) {
|
|
val sp = ctx.env.get[ThingInMemory]("__sp")
|
|
List(AssemblyLine.absolute(LDX, sp))
|
|
} else if (ctx.options.flag(CompilationFlag.EmitEmulation65816Opcodes)) {
|
|
Nil
|
|
} else {
|
|
List(AssemblyLine.implied(TSX))
|
|
}
|
|
|
|
def absoluteX(opcode: Opcode.Value, addr: Constant) =
|
|
AssemblyLine(opcode, AddrMode.AbsoluteX, addr)
|
|
|
|
def absoluteX(opcode: Opcode.Value, thing: ThingInMemory, offset: Int = 0) =
|
|
AssemblyLine(opcode, AddrMode.AbsoluteX, thing.toAddress + offset)
|
|
|
|
def indexedY(opcode: Opcode.Value, addr: Int) =
|
|
AssemblyLine(opcode, AddrMode.IndexedY, NumericConstant(addr & 0xff, 1))
|
|
|
|
def indexedY(opcode: Opcode.Value, addr: Constant) =
|
|
AssemblyLine(opcode, AddrMode.IndexedY, addr)
|
|
|
|
def indexedY(opcode: Opcode.Value, thing: ThingInMemory, offset: Int = 0) =
|
|
AssemblyLine(opcode, AddrMode.IndexedY, thing.toAddress + offset)
|
|
|
|
def indexedSY(opcode: Opcode.Value, offset: Int = 0) =
|
|
AssemblyLine(opcode, AddrMode.IndexedSY, NumericConstant(offset & 0xff, 1))
|
|
|
|
def stackRelative(opcode: Opcode.Value, addr: Int) =
|
|
AssemblyLine(opcode, AddrMode.Stack, NumericConstant(addr & 0xff, 1))
|
|
}
|
|
|
|
object AssemblyLine0 {
|
|
@inline
|
|
def unapply(a: AssemblyLine): Some[(Opcode.Value, AddrMode.Value, Constant)] = Some(a.opcode, a.addrMode, a.parameter)
|
|
}
|
|
|
|
case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var parameter: Constant, elidability: Elidability.Value = Elidability.Elidable, source: Option[SourceLine] = None) extends AbstractCode {
|
|
|
|
def applyVolatile(v: Boolean): AssemblyLine = if (v) copy(elidability = Elidability.Volatile) else this
|
|
|
|
def pos(s: Option[SourceLine]): AssemblyLine = if (s.isEmpty || s == source) this else this.copy(source = s)
|
|
|
|
def pos(s1: Option[SourceLine], s2: Option[SourceLine]): AssemblyLine = pos(Seq(s1, s2))
|
|
|
|
def position(s: Option[Position]): AssemblyLine = pos(SourceLine.of(s))
|
|
|
|
def positionIfEmpty(s: Option[Position]): AssemblyLine = if (s.isEmpty || source.isDefined) this else pos(SourceLine.of(s))
|
|
|
|
def pos(s: Seq[Option[SourceLine]]): AssemblyLine = pos(SourceLine.merge(s))
|
|
|
|
def mergePos(s: Seq[Option[SourceLine]]): AssemblyLine = if (s.isEmpty) this else pos(SourceLine.merge(this.source, s))
|
|
|
|
@inline
|
|
def elidable: Boolean = elidability == Elidability.Elidable
|
|
|
|
@inline
|
|
def notFixed: Boolean = elidability != Elidability.Fixed
|
|
|
|
def refersTo(name: String): Boolean = parameter.refersTo(name)
|
|
|
|
import AddrMode._
|
|
import OpcodeClasses._
|
|
import State._
|
|
import Treatment._
|
|
|
|
def reads(state: State.Value): Boolean = state match {
|
|
case A => if (addrMode == Implied) ReadsAIfImplied(opcode) else ReadsAAlways(opcode)
|
|
case AH => if (addrMode == Implied) ReadsAHIfImplied(opcode) else ReadsAHAlways(opcode)
|
|
case X => addrMode == AbsoluteX || addrMode == LongAbsoluteX || addrMode == ZeroPageX || addrMode == IndexedX || ReadsXAlways(opcode)
|
|
case Y => addrMode == AbsoluteY || addrMode == ZeroPageY || addrMode == IndexedY || addrMode == LongIndexedY || ReadsYAlways(opcode)
|
|
case C => ReadsC(opcode)
|
|
case D => ReadsD(opcode)
|
|
case N => ReadsN(opcode)
|
|
case V => ReadsV(opcode)
|
|
case Z => ReadsZ(opcode)
|
|
case IZ => addrMode == IndexedZ || ReadsIZAlways(opcode)
|
|
case M => ReadsM(opcode)
|
|
case W => ReadsW(opcode)
|
|
}
|
|
|
|
def concernsX: Boolean = addrMode == AbsoluteX || addrMode == LongAbsoluteX || addrMode == ZeroPageX || addrMode == IndexedX || ConcernsXAlways(opcode)
|
|
|
|
def concernsY: Boolean = addrMode == AbsoluteY || addrMode == ZeroPageY || addrMode == IndexedY || addrMode == IndexedSY || addrMode == LongIndexedY || ConcernsYAlways(opcode)
|
|
|
|
def treatment(state: State.Value): Treatment.Value = opcode match {
|
|
case LABEL => Unchanged // TODO: ???
|
|
case NOP => Unchanged
|
|
case JSR | JMP | BEQ | BNE | BMI | BPL | BRK | BCC | BVC | BCS | BVS | BSR => Changed
|
|
case CLC => if (state == C) Cleared else Unchanged
|
|
case SEC => if (state == C) Set else Unchanged
|
|
case CLV => if (state == V) Cleared else Unchanged
|
|
case CLD => if (state == D) Cleared else Unchanged
|
|
case SED => if (state == D) Set else Unchanged
|
|
case SEP => parameter match {
|
|
case NumericConstant(n, _) =>
|
|
if (isAffectedBySepRep(state, n)) Set else Unchanged
|
|
case _ => Changed
|
|
}
|
|
case REP => parameter match {
|
|
case NumericConstant(n, _) =>
|
|
if (isAffectedBySepRep(state, n)) Cleared else Unchanged
|
|
case _ => Changed
|
|
}
|
|
case XCE => Changed
|
|
case _ => state match { // TODO: smart detection of constants
|
|
case A =>
|
|
if (ChangesAAlways(opcode) || addrMode == Implied && ChangesAIfImplied(opcode))
|
|
Changed
|
|
else
|
|
Unchanged
|
|
case AH =>
|
|
if (ChangesAHAlways(opcode) || addrMode == Implied && ChangesAHIfImplied(opcode))
|
|
Changed
|
|
else
|
|
Unchanged
|
|
case X => if (ChangesX(opcode)) Changed else Unchanged
|
|
case Y => if (ChangesY(opcode)) Changed else Unchanged
|
|
case IZ => if (ChangesIZ(opcode)) Changed else Unchanged
|
|
case C => if (ChangesC(opcode)) Changed else Unchanged
|
|
case V => if (ChangesV(opcode)) Changed else Unchanged
|
|
case N | Z => if (ChangesNAndZ(opcode)) Changed else Unchanged
|
|
case W | M | D => Unchanged
|
|
}
|
|
}
|
|
|
|
override def sizeInBytes: Int = addrMode match {
|
|
case Implied | RawByte => 1
|
|
case Relative | ZeroPageX | ZeroPage | ZeroPageY | IndexedZ | IndexedX | IndexedY | IndexedSY | Stack | LongIndexedY | LongIndexedZ | Immediate => 2
|
|
case AbsoluteIndexedX | AbsoluteX | Absolute | AbsoluteY | Indirect | LongRelative | WordImmediate | ZeroPageWithRelative | ImmediateWithZeroPage | ImmediateWithZeroPageX => 3
|
|
case LongAbsolute | LongAbsoluteX | LongIndirect | ImmediateWithAbsolute | ImmediateWithAbsoluteX => 4
|
|
case TripleAbsolute => 7
|
|
case DoesNotExist => 0
|
|
}
|
|
|
|
def cost: Int = addrMode match {
|
|
case Implied | RawByte => 1000
|
|
case Relative | Immediate => 2000
|
|
case ZeroPage => 2001
|
|
case Stack | ZeroPageX | ZeroPageY => 2002
|
|
case IndexedX | IndexedY | IndexedZ => 2003
|
|
case IndexedSY | LongIndexedY | LongIndexedZ => 2004
|
|
case WordImmediate => 3000
|
|
case Absolute => 3001
|
|
case AbsoluteX | AbsoluteY | Indirect => 3002
|
|
case AbsoluteIndexedX => 3003
|
|
case ZeroPageWithRelative => 3004
|
|
case ImmediateWithZeroPage => 3005
|
|
case ImmediateWithZeroPageX => 3006
|
|
case LongAbsolute => 4000
|
|
case LongAbsoluteX => 4001
|
|
case LongIndirect => 4002
|
|
case ImmediateWithAbsolute => 4003
|
|
case ImmediateWithAbsoluteX => 4004
|
|
case TripleAbsolute => 7000
|
|
case DoesNotExist => 1
|
|
}
|
|
|
|
def isPrintable: Boolean = true //addrMode != AddrMode.DoesNotExist || opcode == LABEL
|
|
|
|
override def toString: String = {
|
|
val raw = if (opcode == LABEL) {
|
|
parameter.toString + ':'
|
|
} else if (opcode == BYTE) {
|
|
" !byte " + parameter.toString
|
|
} else if (addrMode == TripleAbsolute) {
|
|
parameter match {
|
|
case StructureConstant(_, List(a,b,c)) => s" $opcode $a,$b,$c"
|
|
}
|
|
} else if (addrMode == ZeroPageWithRelative) {
|
|
parameter match {
|
|
case StructureConstant(_, List(a,b)) => s" $opcode $a,$b"
|
|
}
|
|
} else if (addrMode == LongRelative && opcode != BSR) { // BSR on Hudson is always 8-bit short, and on 65CE02 it's always 16-bit
|
|
s" L$opcode ${AddrMode.addrModeToString(addrMode, parameter.toString)}"
|
|
} else if (addrMode == ImmediateWithAbsolute || addrMode == ImmediateWithZeroPage) {
|
|
parameter match {
|
|
case StructureConstant(_, List(a,b)) => s" $opcode #$a,$b"
|
|
}
|
|
} else if (addrMode == ImmediateWithAbsoluteX || addrMode == ImmediateWithZeroPageX) {
|
|
parameter match {
|
|
case StructureConstant(_, List(a,b)) => s" $opcode #$a,$b,X"
|
|
}
|
|
} else if (addrMode == DoesNotExist) {
|
|
s" ; $opcode"
|
|
} else {
|
|
val op = opcode match {
|
|
case HuSAX => "SAX"
|
|
case _ => opcode.toString
|
|
}
|
|
s" $op ${AddrMode.addrModeToString(addrMode, parameter.toString)}"
|
|
}
|
|
source match {
|
|
case Some(SourceLine(_, line)) if line > 0 => f"$raw%-30s \t; @ $line%d"
|
|
case _ => raw
|
|
}
|
|
}
|
|
}
|