mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-11 23:37:15 +00:00
6502: Stack-related things:
– software variable stack – fixes for handling stack variables
This commit is contained in:
parent
db1ce07ed6
commit
c28b71add5
@ -50,7 +50,11 @@ The implementation depends on the target architecture:
|
||||
|
||||
* on 6502, the stack pointer is transferred into the X register and used as a base
|
||||
|
||||
* on 65818, the native stack-based addressing mode is used, similar to 6502, just without clobbering X
|
||||
* on 65816, the native stack-based addressing mode is used, similar to 6502, just without clobbering X
|
||||
|
||||
* alternatively, on both 6502 and 65816, you can use a software-based stack for stack variables:
|
||||
it's implemented as a separate 256-byte memory area plus a single byte for the stack pointer,
|
||||
exclusively for local stack variables; using it can help against stack overflows
|
||||
|
||||
* on 8080 and LR35902, the address is calculated from the stackpointer into the HL register pair
|
||||
|
||||
|
@ -126,6 +126,10 @@ Allow using the IX register for other purposes.
|
||||
Allow using the IY register for other purposes.
|
||||
`.ini` equivalent: `iy_scratch`. Default: no.
|
||||
|
||||
* `-fsoftware-stack`, `-fno-software-stack` –
|
||||
Use a software stack for stack variables.
|
||||
`.ini` equivalent: `software_stack`. Default: no.
|
||||
|
||||
## Optimization options
|
||||
|
||||
* `-O0` – Disable all optimizations.
|
||||
|
@ -80,6 +80,8 @@ Default: the same as `encoding`.
|
||||
|
||||
* `iy_scratch` – allow using the IY register for other purposes, default is `false`
|
||||
|
||||
* `software_stach` – use software stack for stack variables, default is `false`
|
||||
|
||||
* `output_intel_syntax` – use Intel syntax instead of Zilog syntax, default is `true` for Intel 8080 and `false` otherwise
|
||||
|
||||
|
||||
|
@ -60,6 +60,8 @@ The following features are defined based on the chosen CPU and compilation optio
|
||||
|
||||
* `USES_SHADOW_REGISTERS` – 1 if interrupts preserve old registers in the shadow registers, 0 otherwise
|
||||
|
||||
* `USES_SOFTWARE_STACK` – 1 if using software stack for variables, 0 otherwise
|
||||
|
||||
### Commonly used features
|
||||
|
||||
* `WIDESCREEN` – 1 if the horizontal screen resolution, ignoring borders, is greater than 256, 0 otherwise
|
||||
|
@ -34,7 +34,7 @@ case class CompilationOptions(platform: Platform,
|
||||
|
||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.M6502) invalids ++= Set(
|
||||
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
||||
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, LUnixRelocatableCode, RorWarning)
|
||||
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, LUnixRelocatableCode, RorWarning, SoftwareStack)
|
||||
|
||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(
|
||||
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitIntel8080Opcodes, EmitEZ80Opcodes,
|
||||
@ -189,6 +189,7 @@ case class CompilationOptions(platform: Platform,
|
||||
"USES_ZPREG" -> toLong(platform.cpuFamily == CpuFamily.M6502 && zpRegisterSize > 0),
|
||||
"USES_IX_STACK" -> toLong(flag(CompilationFlag.UseIxForStack)),
|
||||
"USES_IY_STACK" -> toLong(flag(CompilationFlag.UseIyForStack)),
|
||||
"USES_SOFTWARE_STACK" -> toLong(flag(CompilationFlag.SoftwareStack)),
|
||||
"USES_SHADOW_REGISTERS" -> toLong(flag(CompilationFlag.UseShadowRegistersForInterrupts)),
|
||||
"ZPREG_SIZE" -> (if (platform.cpuFamily == CpuFamily.M6502) zpRegisterSize.toLong else 0)
|
||||
)
|
||||
@ -306,7 +307,7 @@ object CompilationFlag extends Enumeration {
|
||||
EmitIllegals, DecimalMode, ReadOnlyArrays, LenientTextEncoding, LineNumbersInAssembly,
|
||||
// compilation options for MOS:
|
||||
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
||||
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator,
|
||||
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack,
|
||||
// compilation options for I80
|
||||
EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes,
|
||||
UseShadowRegistersForInterrupts,
|
||||
@ -347,6 +348,7 @@ object CompilationFlag extends Enumeration {
|
||||
"iy_stack" -> UseIyForStack,
|
||||
"ix_scratch" -> UseIxForScratch,
|
||||
"iy_scratch" -> UseIyForScratch,
|
||||
"software_stack" -> SoftwareStack,
|
||||
"use_shadow_registers_for_irq" -> UseShadowRegistersForInterrupts,
|
||||
"output_intel_syntax" -> UseIntelSyntaxForOutput,
|
||||
"input_intel_syntax" -> UseIntelSyntaxForInput,
|
||||
|
@ -403,6 +403,9 @@ object Main {
|
||||
flag("-fno-use-index-for-stack").action { c =>
|
||||
c.changeFlag(CompilationFlag.UseIyForStack, false).changeFlag(CompilationFlag.UseIxForStack, false)
|
||||
}.description("Don't use either IX or IY as base pointer for stack variables (Z80 only)")
|
||||
boolean("-fsoftware-stack", "-fno-software-stack").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.SoftwareStack, v)
|
||||
}.description("Use software stack for stack variables (6502 only)")
|
||||
|
||||
fluff("", "Optimization options:", "")
|
||||
|
||||
|
@ -421,11 +421,7 @@ object AssemblyLine {
|
||||
List(AssemblyLine.zeropage(opcode, v.toAddress + offset))
|
||||
case v: VariableInMemory => List(AssemblyLine.absolute(opcode, v.toAddress + offset))
|
||||
case v: StackVariable =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitEmulation65816Opcodes)) {
|
||||
List(AssemblyLine.stackRelative(opcode, v.baseOffset + offset + ctx.extraStackOffset))
|
||||
} else {
|
||||
List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(opcode, v.baseOffset + offset + ctx.extraStackOffset))
|
||||
}
|
||||
AssemblyLine.tsx(ctx) :+ AssemblyLine.dataStackX(ctx, opcode, v, offset)
|
||||
}
|
||||
}
|
||||
|
||||
@ -460,6 +456,35 @@ object AssemblyLine {
|
||||
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")
|
||||
if (offset == 0x108) {
|
||||
println()
|
||||
}
|
||||
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)
|
||||
|
||||
|
@ -306,7 +306,7 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
(HasOpcode(PHA)) ~
|
||||
(Linear & Not(ChangesA) & Not(ChangesStack) & Not(ChangesMemory)).* ~
|
||||
(Elidable & XContainsStackPointer & HasOpcode(STA) & HasAddrMode(AbsoluteX) & HasParameterWhere(p => p.quickSimplify match {
|
||||
(Elidable & XContainsHardwareStackPointer & HasOpcode(STA) & HasAddrMode(AbsoluteX) & HasParameterWhere(p => p.quickSimplify match {
|
||||
case NumericConstant(n, _) => n == 0x101
|
||||
case _ => false
|
||||
})) ~~> (_.init),
|
||||
@ -423,14 +423,14 @@ object AlwaysGoodOptimizations {
|
||||
(HasOpcodeIn(LDX, TAX, TSX, INX, DEX) & Elidable) ~ (LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ) & Not(HasOpcode(DISCARD_XF))).* ~ HasOpcode(DISCARD_XF) ~~> (_.tail),
|
||||
(HasOpcodeIn(LDY, TAY, INY, DEY) & Elidable) ~ (LinearOrLabel & Not(ConcernsY) & Not(ReadsNOrZ) & Not(HasOpcode(DISCARD_YF))).* ~ HasOpcode(DISCARD_YF) ~~> (_.tail),
|
||||
(HasOpcode(LDX) & Elidable & MatchAddrMode(3) & MatchParameter(4)) ~
|
||||
(LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ) & DoesntChangeMemoryAt(3, 4) & DoesntChangeIndexingInAddrMode(3)).*.capture(1) ~
|
||||
(LinearOrLabel & Not(ConcernsX) & Not(ReadsNOrZ) & DoesntChangeMemoryAtAssumingNonchangingIndices(3, 4) & DoesntChangeIndexingInAddrMode(3)).*.capture(1) ~
|
||||
(HasOpcode(TXA) & Elidable) ~
|
||||
((LinearOrLabel & Not(ConcernsX) & Not(HasOpcode(DISCARD_XF))).* ~
|
||||
HasOpcode(DISCARD_XF)).capture(2) ~~> { (c, ctx) =>
|
||||
ctx.get[List[AssemblyLine]](1) ++ (c.head.copy(opcode = LDA) :: ctx.get[List[AssemblyLine]](2))
|
||||
},
|
||||
(HasOpcode(LDY) & Elidable & MatchAddrMode(3) & MatchParameter(4)) ~
|
||||
(LinearOrLabel & Not(ConcernsY) & Not(ReadsNOrZ) & DoesntChangeMemoryAt(3, 4) & DoesntChangeIndexingInAddrMode(3)).*.capture(1) ~
|
||||
(LinearOrLabel & Not(ConcernsY) & Not(ReadsNOrZ) & DoesntChangeMemoryAtAssumingNonchangingIndices(3, 4) & DoesntChangeIndexingInAddrMode(3)).*.capture(1) ~
|
||||
(HasOpcode(TYA) & Elidable) ~
|
||||
((LinearOrLabel & Not(ConcernsY) & Not(HasOpcode(DISCARD_YF))).* ~
|
||||
HasOpcode(DISCARD_YF)).capture(2) ~~> { (c, ctx) =>
|
||||
@ -606,19 +606,19 @@ object AlwaysGoodOptimizations {
|
||||
},
|
||||
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcode(PHA)) ~
|
||||
(Linear & Not(ConcernsStack) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Linear & Not(ConcernsStack) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAtAssumingNonchangingIndices(0, 1)).* ~
|
||||
(Elidable & HasOpcode(PLA)) ~~> { code =>
|
||||
code.head :: (code.drop(2).init :+ code.head)
|
||||
},
|
||||
(Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcode(PHX)) ~
|
||||
(Linear & Not(ConcernsStack) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Linear & Not(ConcernsStack) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAtAssumingNonchangingIndices(0, 1)).* ~
|
||||
(Elidable & HasOpcode(PLX)) ~~> { code =>
|
||||
code.head :: (code.drop(2).init :+ code.head)
|
||||
},
|
||||
(Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcode(PHY)) ~
|
||||
(Linear & Not(ConcernsStack) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Linear & Not(ConcernsStack) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAtAssumingNonchangingIndices(0, 1)).* ~
|
||||
(Elidable & HasOpcode(PLY)) ~~> { code =>
|
||||
code.head :: (code.drop(2).init :+ code.head)
|
||||
},
|
||||
@ -976,16 +976,16 @@ object AlwaysGoodOptimizations {
|
||||
val LoadingOfJustWrittenValue = jvmFix(new RuleBasedAssemblyOptimization("Loading of just written value",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
|
||||
(HasOpcode(STA) & XContainsStackPointer & HasAddrMode(AbsoluteX) & MatchAddrMode(0) & MatchParameter(1) & MatchA(2) & HasParameterWhere(_ match {
|
||||
(HasOpcode(STA) & XContainsHardwareStackPointer & HasAddrMode(AbsoluteX) & MatchAddrMode(0) & MatchParameter(1) & MatchA(2) & HasParameterWhere(_ match {
|
||||
case NumericConstant(addr, _) => addr >= 0x100 && addr <= 0x1ff
|
||||
case _ => false
|
||||
})) ~
|
||||
(HasOpcode(JSR) |
|
||||
XContainsStackPointer & HasOpcodeIn(INC, DEC, ASL, LSR) & MatchAddrMode(0) & MatchParameter(1) |
|
||||
Linear & XContainsStackPointer & HasAddrMode(AbsoluteX) & Not(MatchParameter(1)) & Not(HasOpcodeIn(OpcodeClasses.AccessesWordInMemory)) |
|
||||
Linear & HasAddrMode(AbsoluteX) & Not(MatchParameter(1) & XContainsStackPointer) & Not(ChangesMemory) |
|
||||
XContainsHardwareStackPointer & HasOpcodeIn(INC, DEC, ASL, LSR) & MatchAddrMode(0) & MatchParameter(1) |
|
||||
Linear & XContainsHardwareStackPointer & HasAddrMode(AbsoluteX) & Not(MatchParameter(1)) & Not(HasOpcodeIn(OpcodeClasses.AccessesWordInMemory)) |
|
||||
Linear & HasAddrMode(AbsoluteX) & Not(MatchParameter(1) & XContainsHardwareStackPointer) & Not(ChangesMemory) |
|
||||
Linear & Not(ChangesS) & DoesntChangeMemoryAt(0, 1) & Not(HasAddrMode(AbsoluteX))).* ~
|
||||
(Elidable & XContainsStackPointer & HasOpcodeIn(LDA, LDX, LDY, ADC, SBC, ORA, EOR, AND, CMP) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
|
||||
(Elidable & XContainsHardwareStackPointer & HasOpcodeIn(LDA, LDX, LDY, ADC, SBC, ORA, EOR, AND, CMP) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
|
||||
val oldA = ctx.get[Int](2)
|
||||
val ADDR = ctx.get[Constant](1)
|
||||
val value = code.foldLeft(oldA) { (prev, line) =>
|
||||
@ -1003,20 +1003,20 @@ object AlwaysGoodOptimizations {
|
||||
(HasOpcode(STA) & HasAddrMode(Stack) & MatchAddrMode(0) & MatchParameter(1) & MatchA(2)) ~
|
||||
(HasOpcode(JSR) |
|
||||
Linear & HasAddrMode(Stack) & Not(MatchParameter(1)) & Not(HasOpcodeIn(OpcodeClasses.AccessesWordInMemory)) |
|
||||
Linear & Not(ChangesS) & DoesntChangeMemoryAt(0, 1) & Not(HasAddrMode(Stack))).* ~
|
||||
Linear & Not(ChangesS) & DoesntChangeMemoryAt(0, 1) & Not(HasAddrMode(Stack)) & Not(MatchAddrMode(0) & MatchParameter(1))).* ~
|
||||
(Elidable & HasOpcodeIn(LDA, LDX, LDY, ADC, SBC, ORA, EOR, AND, CMP) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
|
||||
code.init :+ code.last.copy(addrMode = AddrMode.Immediate, parameter = NumericConstant(ctx.get[Int](2), 1))
|
||||
},
|
||||
|
||||
(HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & MatchA(2)) ~
|
||||
(Linear & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(LinearOrBranch & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAtAssumingNonchangingIndices(0, 1) & Not(MatchAddrMode(0) & MatchParameter(1))).* ~
|
||||
(Elidable & HasOpcodeIn(LDA, LDX, LDY, ADC, SBC, ORA, EOR, AND, CMP) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
|
||||
code.init :+ code.last.copy(addrMode = AddrMode.Immediate, parameter = NumericConstant(ctx.get[Int](2), 1))
|
||||
},
|
||||
|
||||
(HasOpcode(PHA)) ~
|
||||
(Linear & Not(ChangesA) & Not(ChangesStack) & Not(ChangesMemory)).* ~
|
||||
(Elidable & XContainsStackPointer & HasOpcode(LDA) & DoesntMatterWhatItDoesWith(State.N, State.Z) & HasAddrMode(AbsoluteX) & HasParameterWhere(p => p.quickSimplify match {
|
||||
(Elidable & XContainsHardwareStackPointer & HasOpcode(LDA) & DoesntMatterWhatItDoesWith(State.N, State.Z) & HasAddrMode(AbsoluteX) & HasParameterWhere(p => p.quickSimplify match {
|
||||
case NumericConstant(n, _) => n == 0x101
|
||||
case _ => false
|
||||
})) ~~> (_.init),
|
||||
@ -1030,7 +1030,7 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
(HasOpcode(PHA)) ~
|
||||
(Linear & Not(ChangesA) & Not(ChangesStack) & Not(ChangesMemory)).* ~
|
||||
(Elidable & XContainsStackPointer & HasOpcode(LDA) & HasAddrMode(AbsoluteX) & HasParameterWhere(p => p.quickSimplify match {
|
||||
(Elidable & XContainsHardwareStackPointer & HasOpcode(LDA) & HasAddrMode(AbsoluteX) & HasParameterWhere(p => p.quickSimplify match {
|
||||
case NumericConstant(n, _) => n == 0x101
|
||||
case _ => false
|
||||
})) ~~> (code => code.init :+ AssemblyLine.immediate(ORA, 0)),
|
||||
@ -1040,7 +1040,13 @@ object AlwaysGoodOptimizations {
|
||||
(Elidable & HasOpcode(LDA) & HasAddrMode(Stack) & HasParameterWhere(p => p.quickSimplify match {
|
||||
case NumericConstant(n, _) => n == 1
|
||||
case _ => false
|
||||
})) ~~> (code => code.init :+ AssemblyLine.immediate(ORA, 0))
|
||||
})) ~~> (code => code.init :+ AssemblyLine.immediate(ORA, 0)),
|
||||
|
||||
(HasOpcode(LDX) & RefersTo("__sp", 0)
|
||||
& XContainsSoftwareStackPointer & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_ => Nil),
|
||||
|
||||
(HasOpcodeIn(LAX, LDA) & RefersTo("__sp", 0)
|
||||
& XContainsSoftwareStackPointer) ~~> (_ => List(AssemblyLine.implied(TXA))),
|
||||
))
|
||||
|
||||
val PointlessStackStore = new RuleBasedAssemblyOptimization("Pointless stack store",
|
||||
@ -1048,13 +1054,13 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
// TODO: check if JSR is OK
|
||||
|
||||
(Elidable & HasOpcode(STA) & HasAddrMode(AbsoluteX) & XContainsStackPointer & HasParameterWhere(_ match {
|
||||
(Elidable & HasOpcode(STA) & HasAddrMode(AbsoluteX) & XContainsHardwareStackPointer & HasParameterWhere(_ match {
|
||||
case NumericConstant(addr, _) => addr >= 0x100 && addr <= 0x1ff
|
||||
case _ => false
|
||||
}) & MatchParameter(1)) ~
|
||||
(HasOpcode(JSR) |
|
||||
Not(ChangesS) & Not(HasOpcodeIn(RTS, RTI)) & Linear & Not(HasAddrMode(AbsoluteX)) |
|
||||
HasAddrMode(AbsoluteX) & XContainsStackPointer & Not(MatchParameter(1)) & Not(HasOpcodeIn(OpcodeClasses.AccessesWordInMemory))).* ~
|
||||
HasAddrMode(AbsoluteX) & XContainsHardwareStackPointer & Not(MatchParameter(1)) & Not(HasOpcodeIn(OpcodeClasses.AccessesWordInMemory))).* ~
|
||||
HasOpcodeIn(RTS, RTI) ~~> (_.tail),
|
||||
|
||||
(Elidable & HasOpcode(STA) & HasAddrMode(AbsoluteX) & HasParameterWhere(_ match {
|
||||
@ -1075,14 +1081,14 @@ object AlwaysGoodOptimizations {
|
||||
(HasOpcode(JSR) | Linear & Not(HasAddrMode(Stack)) & Not(HasOpcodeIn(RTS, RTI))).* ~
|
||||
HasOpcodeIn(RTS, RTI) ~~> (_.tail),
|
||||
|
||||
(HasOpcode(STA) & XContainsStackPointer & HasAddrMode(AbsoluteX) & MatchAddrMode(0) & MatchParameter(1) & HasParameterWhere(_ match {
|
||||
(HasOpcode(STA) & XContainsHardwareStackPointer & HasAddrMode(AbsoluteX) & MatchAddrMode(0) & MatchParameter(1) & HasParameterWhere(_ match {
|
||||
case NumericConstant(addr, _) => addr >= 0x100 && addr <= 0x1ff
|
||||
case _ => false
|
||||
})) ~
|
||||
(HasOpcode(JSR) |
|
||||
Linear & XContainsStackPointer & HasAddrMode(AbsoluteX) & Not(MatchParameter(1)) & Not(HasOpcodeIn(OpcodeClasses.AccessesWordInMemory)) |
|
||||
Linear & XContainsHardwareStackPointer & HasAddrMode(AbsoluteX) & Not(MatchParameter(1)) & Not(HasOpcodeIn(OpcodeClasses.AccessesWordInMemory)) |
|
||||
Linear & Not(ChangesS) & Not(HasAddrMode(AbsoluteX)) & DoesNotConcernMemoryAt(0, 1)).* ~
|
||||
(Elidable & XContainsStackPointer & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1)) ~~> (_.tail),
|
||||
(Elidable & XContainsHardwareStackPointer & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1)) ~~> (_.tail),
|
||||
)
|
||||
|
||||
val IdempotentDuplicateRemoval = new RuleBasedAssemblyOptimization("Idempotent duplicate operation",
|
||||
|
@ -35,6 +35,7 @@ object CoarseFlowAnalyzer {
|
||||
while (changed) {
|
||||
changed = false
|
||||
var currentStatus: CpuStatus = functionStartStatus
|
||||
var staSpWasLast = false
|
||||
for (i <- codeArray.indices) {
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
@ -43,6 +44,7 @@ object CoarseFlowAnalyzer {
|
||||
changed = true
|
||||
flagArray(i) = currentStatus
|
||||
}
|
||||
var staSpIsNow = false
|
||||
codeArray(i) match {
|
||||
case AssemblyLine0(LABEL, _, MemoryAddressConstant(Label(l))) =>
|
||||
val L = l
|
||||
@ -57,6 +59,7 @@ object CoarseFlowAnalyzer {
|
||||
ah = if (niceFunctionProperties(DoesntChangeAH -> th.name)) currentStatus.ah else AnyStatus,
|
||||
x = if (niceFunctionProperties(DoesntChangeX -> th.name)) currentStatus.x else AnyStatus,
|
||||
eqSX = if (niceFunctionProperties(DoesntChangeX -> th.name)) currentStatus.eqSX else false,
|
||||
eqSpX = if (niceFunctionProperties(DoesntChangeX -> th.name)) currentStatus.eqSpX else false,
|
||||
y = if (niceFunctionProperties(DoesntChangeY -> th.name)) currentStatus.y else AnyStatus,
|
||||
iz = if (niceFunctionProperties(DoesntChangeIZ -> th.name)) currentStatus.iz else AnyStatus,
|
||||
c = if (niceFunctionProperties(DoesntChangeC -> th.name)) currentStatus.c else AnyStatus
|
||||
@ -65,26 +68,44 @@ object CoarseFlowAnalyzer {
|
||||
case AssemblyLine0(JSR | BYTE, _, _) =>
|
||||
currentStatus = initialStatus
|
||||
|
||||
case AssemblyLine0(TAX, _, _) if staSpWasLast =>
|
||||
currentStatus = currentStatus.copy(
|
||||
x = currentStatus.a,
|
||||
eqSX = false,
|
||||
eqSpX = true,
|
||||
n = currentStatus.a.n(),
|
||||
z = currentStatus.a.z(),
|
||||
src = SourceOfNZ.AX)
|
||||
|
||||
case AssemblyLine0(op, Implied, _) if FlowAnalyzerForImplied.hasDefinition(op) =>
|
||||
currentStatus = FlowAnalyzerForImplied.get(op)(currentStatus)
|
||||
|
||||
case AssemblyLine0(op, Immediate | WordImmediate, NumericConstant(nn, _)) if FlowAnalyzerForImmediate.hasDefinition(op) =>
|
||||
currentStatus = FlowAnalyzerForImmediate.get(op)(nn.toInt, currentStatus)
|
||||
|
||||
case AssemblyLine0(STA, _, MemoryAddressConstant(th: Thing))
|
||||
if th.name == "__sp" =>
|
||||
currentStatus = FlowAnalyzerForTheRest.get(STA)(currentStatus, None, true)
|
||||
staSpIsNow = true
|
||||
|
||||
case AssemblyLine0(op, _, MemoryAddressConstant(th: Thing))
|
||||
if th.name == "__sp" && FlowAnalyzerForTheRest.hasDefinition(op) =>
|
||||
currentStatus = FlowAnalyzerForTheRest.get(op)(currentStatus, None, true)
|
||||
|
||||
case AssemblyLine0(op, _, MemoryAddressConstant(th: Thing))
|
||||
if th.name == "__reg" && FlowAnalyzerForTheRest.hasDefinition(op) =>
|
||||
currentStatus = FlowAnalyzerForTheRest.get(op)(currentStatus, Some(0))
|
||||
currentStatus = FlowAnalyzerForTheRest.get(op)(currentStatus, Some(0), false)
|
||||
|
||||
case AssemblyLine0(op, _, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th: Thing), NumericConstant(n, _)))
|
||||
if th.name == "__reg" && FlowAnalyzerForTheRest.hasDefinition(op) =>
|
||||
currentStatus = FlowAnalyzerForTheRest.get(op)(currentStatus, Some(n.toInt))
|
||||
currentStatus = FlowAnalyzerForTheRest.get(op)(currentStatus, Some(n.toInt), false)
|
||||
|
||||
case AssemblyLine0(op, _, _) if FlowAnalyzerForTheRest.hasDefinition(op) =>
|
||||
currentStatus = FlowAnalyzerForTheRest.get(op)(currentStatus, None)
|
||||
currentStatus = FlowAnalyzerForTheRest.get(op)(currentStatus, None, false)
|
||||
|
||||
case AssemblyLine0(opcode, addrMode, _) =>
|
||||
currentStatus = currentStatus.copy(src = AnyStatus)
|
||||
if (OpcodeClasses.ChangesX(opcode)) currentStatus = currentStatus.copy(x = AnyStatus, eqSX = false)
|
||||
if (OpcodeClasses.ChangesX(opcode)) currentStatus = currentStatus.copy(x = AnyStatus, eqSX = false, eqSpX = false)
|
||||
if (OpcodeClasses.ChangesY(opcode)) currentStatus = currentStatus.copy(y = AnyStatus)
|
||||
if (OpcodeClasses.ChangesAAlways(opcode)) currentStatus = currentStatus.copy(a = AnyStatus, a0 = AnyStatus, a7 = AnyStatus)
|
||||
if (addrMode == Implied && OpcodeClasses.ChangesAIfImplied(opcode)) currentStatus = currentStatus.copy(a = AnyStatus, a0 = AnyStatus, a7 = AnyStatus)
|
||||
@ -95,6 +116,7 @@ object CoarseFlowAnalyzer {
|
||||
if (OpcodeClasses.ChangesV(opcode)) currentStatus = currentStatus.copy(v = AnyStatus)
|
||||
if (OpcodeClasses.ChangesStack(opcode) || OpcodeClasses.ChangesS(opcode)) currentStatus = currentStatus.copy(eqSX = false)
|
||||
}
|
||||
staSpWasLast = staSpIsNow
|
||||
}
|
||||
// flagArray.zip(codeArray).foreach{
|
||||
// case (fl, y) => if (y.isPrintable) println(f"$fl%-32s $y%-32s")
|
||||
|
@ -53,6 +53,8 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
r3: Status[Int] = UnknownStatus,
|
||||
src: Status[SourceOfNZ] = UnknownStatus,
|
||||
eqSX: Boolean = false,
|
||||
eqSpA: Boolean = false,
|
||||
eqSpX: Boolean = false,
|
||||
z: Status[Boolean] = UnknownStatus,
|
||||
n: Status[Boolean] = UnknownStatus,
|
||||
c: Status[Boolean] = UnknownStatus,
|
||||
@ -61,6 +63,8 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
m: Status[Boolean] = UnknownStatus,
|
||||
w: Status[Boolean] = UnknownStatus
|
||||
) {
|
||||
def overwriteSp(sp: Boolean): CpuStatus = if (sp && eqSpX) this.copy(eqSpX = false) else this
|
||||
|
||||
// assert(a ne null)
|
||||
// assert(ah ne null)
|
||||
// assert(x ne null)
|
||||
@ -96,7 +100,9 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
|
||||
override def toString: String = s"A=$a,B=$ah,X=$x,Y=$y,Z=$iz; Z=$z,N=$n,C=$c,V=$v,D=$d,M=$m,X=$w; R0=$r0,R1=$r1,R2=$r2,R3=$r3; A7=$a7,A0=$a0,NZ:$src" +
|
||||
(if (eqSX) "; S=X"
|
||||
else /* */ " ")
|
||||
else /*-*/ " ") +
|
||||
(if (eqSpX) "; SP=X"
|
||||
else /*--*/ " ")
|
||||
|
||||
def aw: Status[Int] = (ah, a) match {
|
||||
case (SingleStatus(h), SingleStatus(l)) => SingleStatus(h.&(0xff).<<(8).+(l&0xff))
|
||||
@ -123,6 +129,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
iz = this.iz ~ that.iz,
|
||||
src = this.src ~ that.src,
|
||||
eqSX = this.eqSX && that.eqSX,
|
||||
eqSpX = this.eqSpX && that.eqSpX,
|
||||
z = this.z ~ that.z,
|
||||
n = this.n ~ that.n,
|
||||
c = this.c ~ that.c,
|
||||
|
@ -15,7 +15,7 @@ object FlowAnalyzerForImmediate {
|
||||
if ((nn & 1) != 0) currentStatus = currentStatus.copy(c = Status.SingleFalse)
|
||||
if ((nn & 2) != 0) currentStatus = currentStatus.copy(z = Status.SingleFalse)
|
||||
if ((nn & 8) != 0) currentStatus = currentStatus.copy(d = Status.SingleFalse)
|
||||
if ((nn & 0x10) != 0) currentStatus = currentStatus.copy(w = Status.SingleFalse, eqSX = false)
|
||||
if ((nn & 0x10) != 0) currentStatus = currentStatus.copy(w = Status.SingleFalse, eqSX = false, eqSpX = false)
|
||||
if ((nn & 0x20) != 0) currentStatus = currentStatus.copy(m = Status.SingleFalse)
|
||||
if ((nn & 0x40) != 0) currentStatus = currentStatus.copy(v = Status.SingleFalse)
|
||||
if ((nn & 0x80) != 0) currentStatus = currentStatus.copy(n = Status.SingleFalse)
|
||||
@ -26,7 +26,7 @@ object FlowAnalyzerForImmediate {
|
||||
if ((nn & 1) != 0) currentStatus = currentStatus.copy(c = Status.SingleTrue)
|
||||
if ((nn & 2) != 0) currentStatus = currentStatus.copy(z = Status.SingleTrue)
|
||||
if ((nn & 8) != 0) currentStatus = currentStatus.copy(d = Status.SingleTrue)
|
||||
if ((nn & 0x10) != 0) currentStatus = currentStatus.copy(w = Status.SingleTrue, eqSX = false)
|
||||
if ((nn & 0x10) != 0) currentStatus = currentStatus.copy(w = Status.SingleTrue, eqSX = false, eqSpX = false)
|
||||
if ((nn & 0x20) != 0) currentStatus = currentStatus.copy(m = Status.SingleTrue)
|
||||
if ((nn & 0x40) != 0) currentStatus = currentStatus.copy(v = Status.SingleTrue)
|
||||
if ((nn & 0x80) != 0) currentStatus = currentStatus.copy(n = Status.SingleTrue)
|
||||
@ -34,7 +34,7 @@ object FlowAnalyzerForImmediate {
|
||||
},
|
||||
LDX -> {(nn, currentStatus) =>
|
||||
val n = nn & 0xff
|
||||
currentStatus.nz(n).copy(x = SingleStatus(n), src = SourceOfNZ.X, eqSX = false)
|
||||
currentStatus.nz(n).copy(x = SingleStatus(n), src = SourceOfNZ.X, eqSX = false, eqSpX = false)
|
||||
},
|
||||
LDY -> {(nn, currentStatus) =>
|
||||
val n = nn & 0xff
|
||||
@ -54,7 +54,7 @@ object FlowAnalyzerForImmediate {
|
||||
},
|
||||
LDX_W -> {(nn, currentStatus) =>
|
||||
val n = nn & 0xff
|
||||
currentStatus.nzw(nn).copy(x = SingleStatus(n), src = AnyStatus, eqSX = false)
|
||||
currentStatus.nzw(nn).copy(x = SingleStatus(n), src = AnyStatus, eqSX = false, eqSpX = false)
|
||||
},
|
||||
LDY_W -> {(nn, currentStatus) =>
|
||||
val n = nn & 0xff
|
||||
@ -260,6 +260,7 @@ object FlowAnalyzerForImmediate {
|
||||
z = newX.z(),
|
||||
c = AnyStatus,
|
||||
eqSX = false,
|
||||
eqSpX = false,
|
||||
src = SourceOfNZ.X)
|
||||
}
|
||||
)
|
||||
|
@ -15,7 +15,7 @@ object FlowAnalyzerForImplied {
|
||||
RTI -> identity,
|
||||
SEI -> identity,
|
||||
CLI -> identity,
|
||||
TXS -> (_.copy(eqSX = true)),
|
||||
TXS -> (_.copy(eqSX = true, eqSpX = false)),
|
||||
PHP -> (_.copy(eqSX = false)),
|
||||
PHA -> (_.copy(eqSX = false)),
|
||||
PHA_W -> (_.copy(eqSX = false)),
|
||||
@ -33,12 +33,12 @@ object FlowAnalyzerForImplied {
|
||||
SEC -> (_.copy(c = Status.SingleTrue)),
|
||||
CLV -> (_.copy(v = Status.SingleFalse)),
|
||||
CLA -> (currentStatus => currentStatus.copy(a = Status.SingleZero, src = currentStatus.src.butNotA)),
|
||||
CLX -> (currentStatus => currentStatus.copy(x = Status.SingleZero, src = currentStatus.src.butNotX)),
|
||||
CLX -> (currentStatus => currentStatus.copy(x = Status.SingleZero, src = currentStatus.src.butNotX, eqSX = false, eqSpX = false)),
|
||||
CLY -> (currentStatus => currentStatus.copy(y = Status.SingleZero, src = currentStatus.src.butNotY)),
|
||||
XCE -> (_.copy(c = AnyStatus, m = AnyStatus, w = AnyStatus, x = AnyStatus, y = AnyStatus, eqSX = false)),
|
||||
PLA -> (_.copy(src = SourceOfNZ.A, a = AnyStatus, n = AnyStatus, z = AnyStatus, eqSX = false)),
|
||||
PLX -> (_.copy(src = SourceOfNZ.X, x = AnyStatus, n = AnyStatus, z = AnyStatus, eqSX = false)),
|
||||
PLX_W -> (_.copy(src = SourceOfNZ.X, x = AnyStatus, n = AnyStatus, z = AnyStatus, eqSX = false)),
|
||||
PLX -> (_.copy(src = SourceOfNZ.X, x = AnyStatus, n = AnyStatus, z = AnyStatus, eqSX = false, eqSpX = false)),
|
||||
PLX_W -> (_.copy(src = SourceOfNZ.X, x = AnyStatus, n = AnyStatus, z = AnyStatus, eqSX = false, eqSpX = false)),
|
||||
PLY -> (_.copy(src = SourceOfNZ.Y, y = AnyStatus, n = AnyStatus, z = AnyStatus, eqSX = false)),
|
||||
PLY_W -> (_.copy(src = SourceOfNZ.Y, y = AnyStatus, n = AnyStatus, z = AnyStatus, eqSX = false)),
|
||||
PLZ -> (_.copy(src = SourceOfNZ.Z, iz = AnyStatus, n = AnyStatus, z = AnyStatus, eqSX = false)),
|
||||
@ -57,6 +57,7 @@ object FlowAnalyzerForImplied {
|
||||
z = newX.z(),
|
||||
x = newX,
|
||||
eqSX = false,
|
||||
eqSpX = false,
|
||||
src = SourceOfNZ.X)
|
||||
}),
|
||||
DEX -> (currentStatus => {
|
||||
@ -66,6 +67,7 @@ object FlowAnalyzerForImplied {
|
||||
z = newX.z(),
|
||||
x = newX,
|
||||
eqSX = false,
|
||||
eqSpX = false,
|
||||
src = SourceOfNZ.X)
|
||||
}),
|
||||
INY -> (currentStatus => {
|
||||
@ -137,6 +139,7 @@ object FlowAnalyzerForImplied {
|
||||
z = newX.z().withHiddenHi,
|
||||
x = newX,
|
||||
eqSX = false,
|
||||
eqSpX = false,
|
||||
src = AnyStatus)
|
||||
}),
|
||||
DEX_W -> (currentStatus => {
|
||||
@ -146,6 +149,7 @@ object FlowAnalyzerForImplied {
|
||||
z = newX.z().withHiddenHi,
|
||||
x = newX,
|
||||
eqSX = false,
|
||||
eqSpX = false,
|
||||
src = AnyStatus)
|
||||
}),
|
||||
INY_W -> (currentStatus => {
|
||||
@ -192,6 +196,7 @@ object FlowAnalyzerForImplied {
|
||||
currentStatus.copy(
|
||||
x = currentStatus.a,
|
||||
eqSX = false,
|
||||
eqSpX = false,
|
||||
n = currentStatus.a.n(),
|
||||
z = currentStatus.a.z(),
|
||||
src = SourceOfNZ.AX)
|
||||
@ -232,6 +237,7 @@ object FlowAnalyzerForImplied {
|
||||
currentStatus.copy(
|
||||
x = currentStatus.y,
|
||||
eqSX = false,
|
||||
eqSpX = false,
|
||||
n = currentStatus.y.n(),
|
||||
z = currentStatus.y.z(),
|
||||
src = SourceOfNZ.XY)
|
||||
@ -258,6 +264,7 @@ object FlowAnalyzerForImplied {
|
||||
a0 = currentStatus.x.bit0,
|
||||
a7 = currentStatus.x.bit7,
|
||||
eqSX = false,
|
||||
eqSpX = false,
|
||||
x = currentStatus.a)
|
||||
}),
|
||||
SAY -> (currentStatus => {
|
||||
@ -271,6 +278,7 @@ object FlowAnalyzerForImplied {
|
||||
currentStatus.copy(
|
||||
y = currentStatus.x,
|
||||
eqSX = false,
|
||||
eqSpX = false,
|
||||
x = currentStatus.y)
|
||||
}),
|
||||
ASL -> (currentStatus => {
|
||||
@ -376,6 +384,7 @@ object FlowAnalyzerForImplied {
|
||||
currentStatus.copy(
|
||||
x = AnyStatus,
|
||||
eqSX = true,
|
||||
eqSpX = false,
|
||||
src = SourceOfNZ.X)
|
||||
}),
|
||||
)
|
||||
|
@ -8,35 +8,38 @@ import millfork.assembly.opt.{AnyStatus, SingleStatus, Status}
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object FlowAnalyzerForTheRest {
|
||||
private val map: Map[Opcode.Value, (CpuStatus, Option[Int]) => CpuStatus] = Map(
|
||||
STA -> ((c,zpreg) => c.setReg(zpreg, c.a)),
|
||||
STX -> ((c,zpreg) => c.setReg(zpreg, c.x)),
|
||||
STY -> ((c,zpreg) => c.setReg(zpreg, c.y)),
|
||||
STZ -> ((c,zpreg) => c.setReg(zpreg, c.iz)),
|
||||
SAX -> ((c,zpreg) => c.setReg(zpreg, (c.a <*> c.x)(_ & _))),
|
||||
NOP -> ((x,_) => x),
|
||||
DISCARD_AF -> ((x,_) => x),
|
||||
DISCARD_XF -> ((x,_) => x),
|
||||
DISCARD_YF -> ((x,_) => x),
|
||||
REP -> ((currentStatus, zpreg) => {
|
||||
private val map: Map[Opcode.Value, (CpuStatus, Option[Int], Boolean) => CpuStatus] = Map(
|
||||
STA -> ((c, zpreg, sp) => c.setReg(zpreg, c.a).overwriteSp(sp)),
|
||||
STX -> ((c, zpreg, sp) => {
|
||||
if (sp) c.copy(eqSpX = true)
|
||||
else c.setReg(zpreg, c.x)
|
||||
}),
|
||||
STY -> ((c, zpreg, sp) => c.setReg(zpreg, c.y).overwriteSp(sp)),
|
||||
STZ -> ((c, zpreg, sp) => c.setReg(zpreg, c.iz).overwriteSp(sp)),
|
||||
SAX -> ((c, zpreg, sp) => c.setReg(zpreg, (c.a <*> c.x) (_ & _)).copy(eqSpX = false)),
|
||||
NOP -> ((x, _, _) => x),
|
||||
DISCARD_AF -> ((x, _, _) => x),
|
||||
DISCARD_XF -> ((x, _, _) => x),
|
||||
DISCARD_YF -> ((x, _, _) => x),
|
||||
REP -> ((currentStatus, zpreg, sp) => {
|
||||
currentStatus.copy(c = AnyStatus, d = AnyStatus, n = AnyStatus, z= AnyStatus, v = AnyStatus, m = AnyStatus, w = AnyStatus)
|
||||
}),
|
||||
SEP -> ((currentStatus, zpreg) => {
|
||||
SEP -> ((currentStatus, zpreg, sp) => {
|
||||
currentStatus.copy(c = AnyStatus, d = AnyStatus, n = AnyStatus, z= AnyStatus, v = AnyStatus, m = AnyStatus, w = AnyStatus)
|
||||
}),
|
||||
BCC -> ((currentStatus, zpreg) => {
|
||||
BCC -> ((currentStatus, zpreg, sp) => {
|
||||
currentStatus.copy(c = Status.SingleTrue)
|
||||
}),
|
||||
BCS -> ((currentStatus, zpreg) => {
|
||||
BCS -> ((currentStatus, zpreg, sp) => {
|
||||
currentStatus.copy(c = Status.SingleFalse)
|
||||
}),
|
||||
BVS -> ((currentStatus, zpreg) => {
|
||||
BVS -> ((currentStatus, zpreg, sp) => {
|
||||
currentStatus.copy(v = Status.SingleFalse)
|
||||
}),
|
||||
BVC -> ((currentStatus, zpreg) => {
|
||||
BVC -> ((currentStatus, zpreg, sp) => {
|
||||
currentStatus.copy(v = Status.SingleTrue)
|
||||
}),
|
||||
BMI -> ((c, zpreg) => {
|
||||
BMI -> ((c, zpreg,_) => {
|
||||
var currentStatus = c
|
||||
currentStatus = currentStatus.copy(n = Status.SingleFalse)
|
||||
if (currentStatus.src.isFromA) {
|
||||
@ -44,7 +47,7 @@ object FlowAnalyzerForTheRest {
|
||||
}
|
||||
currentStatus
|
||||
}),
|
||||
BPL -> ((c, zpreg) => {
|
||||
BPL -> ((c, zpreg,_) => {
|
||||
var currentStatus = c
|
||||
currentStatus = currentStatus.copy(n = Status.SingleTrue)
|
||||
if (currentStatus.src.isFromA) {
|
||||
@ -52,10 +55,10 @@ object FlowAnalyzerForTheRest {
|
||||
}
|
||||
currentStatus
|
||||
}),
|
||||
BEQ -> ((currentStatus, zpreg) => {
|
||||
BEQ -> ((currentStatus, zpreg, sp) => {
|
||||
currentStatus.copy(z = Status.SingleFalse)
|
||||
}),
|
||||
BNE -> ((c, zpreg) => {
|
||||
BNE -> ((c, zpreg,_) => {
|
||||
var currentStatus = c
|
||||
currentStatus = currentStatus.copy(z = Status.SingleTrue)
|
||||
if (currentStatus.src.isFromA) {
|
||||
@ -79,15 +82,17 @@ object FlowAnalyzerForTheRest {
|
||||
}
|
||||
currentStatus
|
||||
}),
|
||||
LDX -> ((currentStatus, zpreg) => {
|
||||
LDX -> ((currentStatus, zpreg, sp) => {
|
||||
val newX = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(
|
||||
x = newX,
|
||||
n = newX.n(),
|
||||
z = newX.z(),
|
||||
eqSX = false,
|
||||
eqSpX = sp,
|
||||
src = SourceOfNZ.X)
|
||||
}),
|
||||
LDY -> ((currentStatus, zpreg) => {
|
||||
LDY -> ((currentStatus, zpreg, sp) => {
|
||||
val newY = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(
|
||||
y = newY,
|
||||
@ -95,7 +100,7 @@ object FlowAnalyzerForTheRest {
|
||||
z = newY.z(),
|
||||
src = SourceOfNZ.Y)
|
||||
}),
|
||||
LDA -> ((currentStatus, zpreg) => {
|
||||
LDA -> ((currentStatus, zpreg, sp) => {
|
||||
val newA = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(
|
||||
a = newA,
|
||||
@ -105,7 +110,7 @@ object FlowAnalyzerForTheRest {
|
||||
z = newA.z(),
|
||||
src = SourceOfNZ.A)
|
||||
}),
|
||||
LDZ -> ((currentStatus, zpreg) => {
|
||||
LDZ -> ((currentStatus, zpreg, sp) => {
|
||||
val newZ = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(
|
||||
iz = newZ,
|
||||
@ -113,7 +118,7 @@ object FlowAnalyzerForTheRest {
|
||||
z = newZ.z(),
|
||||
src = SourceOfNZ.Z)
|
||||
}),
|
||||
LAX -> ((currentStatus, zpreg) => {
|
||||
LAX -> ((currentStatus, zpreg, sp) => {
|
||||
val newA = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(
|
||||
x = newA,
|
||||
@ -122,9 +127,11 @@ object FlowAnalyzerForTheRest {
|
||||
a0 = newA.bit0,
|
||||
n = newA.n(),
|
||||
z = newA.z(),
|
||||
eqSX = false,
|
||||
eqSpX = sp,
|
||||
src = SourceOfNZ.AX)
|
||||
}),
|
||||
LDA_W -> ((currentStatus, zpreg) => {
|
||||
LDA_W -> ((currentStatus, zpreg, sp) => {
|
||||
val newA = currentStatus.getReg(zpreg)
|
||||
val newAH = currentStatus.getRegHi(zpreg)
|
||||
currentStatus.copy(
|
||||
@ -136,21 +143,23 @@ object FlowAnalyzerForTheRest {
|
||||
z = AnyStatus,
|
||||
src = SourceOfNZ.AW)
|
||||
}),
|
||||
LDX_W -> ((currentStatus, zpreg) => {
|
||||
LDX_W -> ((currentStatus, zpreg, sp) => {
|
||||
currentStatus.copy(
|
||||
x = AnyStatus,
|
||||
n = AnyStatus,
|
||||
z = AnyStatus,
|
||||
eqSX = false,
|
||||
eqSpX = false,
|
||||
src = AnyStatus)
|
||||
}),
|
||||
LDY_W -> ((currentStatus, zpreg) => {
|
||||
LDY_W -> ((currentStatus, zpreg, sp) => {
|
||||
currentStatus.copy(
|
||||
y = AnyStatus,
|
||||
n = AnyStatus,
|
||||
z = AnyStatus,
|
||||
src = AnyStatus)
|
||||
}),
|
||||
ADC -> ((currentStatus, zpreg) => {
|
||||
ADC -> ((currentStatus, zpreg, sp) => {
|
||||
val r = currentStatus.getReg(zpreg)
|
||||
val newA: Status[Int]= if (currentStatus.d.contains(false)) Status.flatMap3(currentStatus.a, r, currentStatus.c) {
|
||||
case (m, n, false) => SingleStatus((m + n) & 0xff)
|
||||
@ -173,7 +182,7 @@ object FlowAnalyzerForTheRest {
|
||||
n = newA.n(),
|
||||
src = currentStatus.d.flatMap((dec: Boolean) => if (dec) AnyStatus else SourceOfNZ.A))
|
||||
}),
|
||||
SBC -> ((currentStatus, zpreg) => {
|
||||
SBC -> ((currentStatus, zpreg, sp) => {
|
||||
val r = currentStatus.getReg(zpreg)
|
||||
val newA: Status[Int]= if (currentStatus.d.contains(false)) Status.flatMap3(currentStatus.a, r, currentStatus.c) {
|
||||
case (m, n, false) => SingleStatus((m - n + 0xff) & 0xff)
|
||||
@ -192,7 +201,7 @@ object FlowAnalyzerForTheRest {
|
||||
n = newA.n(),
|
||||
src = currentStatus.d.flatMap((dec: Boolean) => if (dec) AnyStatus else SourceOfNZ.A))
|
||||
}),
|
||||
AND -> ((currentStatus, zpreg) => {
|
||||
AND -> ((currentStatus, zpreg, sp) => {
|
||||
val newA: Status[Int] =
|
||||
currentStatus.a.flatMap(v => if ((v & 0xff) == 0) Status.SingleZero else AnyStatus) |
|
||||
(currentStatus.a <*> currentStatus.getReg(zpreg))(_ & _)
|
||||
@ -204,7 +213,7 @@ object FlowAnalyzerForTheRest {
|
||||
z = newA.z(),
|
||||
src = SourceOfNZ.A)
|
||||
}),
|
||||
ORA -> ((currentStatus, zpreg) => {
|
||||
ORA -> ((currentStatus, zpreg, sp) => {
|
||||
val newA: Status[Int] =
|
||||
currentStatus.a.flatMap(v => if ((v & 0xff) == 0xff) Status.SingleFF else AnyStatus) |
|
||||
(currentStatus.a <*> currentStatus.getReg(zpreg))(_ | _)
|
||||
@ -216,7 +225,7 @@ object FlowAnalyzerForTheRest {
|
||||
z = newA.z(),
|
||||
src = SourceOfNZ.A)
|
||||
}),
|
||||
SLO -> ((currentStatus, zpreg) => {
|
||||
SLO -> ((currentStatus, zpreg, sp) => {
|
||||
val newA: Status[Int] = currentStatus.a.flatMap(v => if ((v & 0xff) == 0xff) Status.SingleFF else AnyStatus)
|
||||
currentStatus.copy(
|
||||
c = AnyStatus,
|
||||
@ -227,7 +236,7 @@ object FlowAnalyzerForTheRest {
|
||||
z = newA.z(),
|
||||
src = SourceOfNZ.A).setReg(zpreg, AnyStatus)
|
||||
}),
|
||||
EOR -> ((currentStatus, zpreg) => {
|
||||
EOR -> ((currentStatus, zpreg, sp) => {
|
||||
val newA: Status[Int] = (currentStatus.a <*> currentStatus.getReg(zpreg))(_ ^ _)
|
||||
currentStatus.copy(
|
||||
n = newA.n(),
|
||||
@ -237,7 +246,7 @@ object FlowAnalyzerForTheRest {
|
||||
a0 = newA.bit0,
|
||||
src = SourceOfNZ.A).setReg(zpreg, AnyStatus)
|
||||
}),
|
||||
SRE -> ((currentStatus, zpreg) => {
|
||||
SRE -> ((currentStatus, zpreg, sp) => {
|
||||
currentStatus.copy(
|
||||
c = AnyStatus,
|
||||
n = AnyStatus,
|
||||
@ -245,9 +254,9 @@ object FlowAnalyzerForTheRest {
|
||||
a = AnyStatus,
|
||||
a7 = AnyStatus,
|
||||
a0 = AnyStatus,
|
||||
src = SourceOfNZ.A).setReg(zpreg, AnyStatus)
|
||||
src = SourceOfNZ.A).setReg(zpreg, AnyStatus).overwriteSp(sp)
|
||||
}),
|
||||
RLA -> ((currentStatus, zpreg) => {
|
||||
RLA -> ((currentStatus, zpreg, sp) => {
|
||||
currentStatus.copy(
|
||||
c = AnyStatus,
|
||||
n = AnyStatus,
|
||||
@ -255,9 +264,9 @@ object FlowAnalyzerForTheRest {
|
||||
a = AnyStatus,
|
||||
a7 = AnyStatus,
|
||||
a0 = AnyStatus,
|
||||
src = SourceOfNZ.A).setReg(zpreg, AnyStatus)
|
||||
src = SourceOfNZ.A).setReg(zpreg, AnyStatus).overwriteSp(sp)
|
||||
}),
|
||||
RRA -> ((currentStatus, zpreg) => {
|
||||
RRA -> ((currentStatus, zpreg, sp) => {
|
||||
currentStatus.copy(
|
||||
c = AnyStatus,
|
||||
n = AnyStatus,
|
||||
@ -266,17 +275,17 @@ object FlowAnalyzerForTheRest {
|
||||
a7 = AnyStatus,
|
||||
a0 = AnyStatus,
|
||||
z = AnyStatus,
|
||||
src = currentStatus.d.flatMap(dec => if (dec) AnyStatus else SourceOfNZ.A)).setReg(zpreg, AnyStatus)
|
||||
src = currentStatus.d.flatMap(dec => if (dec) AnyStatus else SourceOfNZ.A)).setReg(zpreg, AnyStatus).overwriteSp(sp)
|
||||
}),
|
||||
DCP -> ((currentStatus, zpreg) => {
|
||||
DCP -> ((currentStatus, zpreg, sp) => {
|
||||
val r = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(
|
||||
c = AnyStatus,
|
||||
n = AnyStatus,
|
||||
z = AnyStatus,
|
||||
src = AnyStatus).setReg(zpreg, r.map(n => (n - 1) & 0xff))
|
||||
src = AnyStatus).setReg(zpreg, r.map(n => (n - 1) & 0xff)).overwriteSp(sp)
|
||||
}),
|
||||
ISC -> ((currentStatus, zpreg) => {
|
||||
ISC -> ((currentStatus, zpreg, sp) => {
|
||||
val r = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(
|
||||
c = AnyStatus,
|
||||
@ -286,61 +295,61 @@ object FlowAnalyzerForTheRest {
|
||||
a7 = AnyStatus,
|
||||
a0 = AnyStatus,
|
||||
z = AnyStatus,
|
||||
src = currentStatus.d.flatMap(dec => if (dec) AnyStatus else SourceOfNZ.A)).setReg(zpreg, r.map(n => (n + 1) & 0xff))
|
||||
src = currentStatus.d.flatMap(dec => if (dec) AnyStatus else SourceOfNZ.A)).setReg(zpreg, r.map(n => (n + 1) & 0xff)).overwriteSp(sp)
|
||||
}),
|
||||
ROL -> ((currentStatus, zpreg) => {
|
||||
ROL -> ((currentStatus, zpreg, sp) => {
|
||||
val r = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(c = r.bit7, n = AnyStatus, z = AnyStatus, src = AnyStatus).setReg(zpreg, currentStatus.c.flatMap { c =>
|
||||
r.map(n => (n << 1) & 0xff | (if (c) 1 else 0))
|
||||
})
|
||||
}).overwriteSp(sp)
|
||||
}),
|
||||
ROR -> ((currentStatus, zpreg) => {
|
||||
ROR -> ((currentStatus, zpreg, sp) => {
|
||||
val r = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(c = r.bit0, n = AnyStatus, z = AnyStatus, src = AnyStatus).setReg(zpreg, currentStatus.c.flatMap { c =>
|
||||
r.map(n => (n >> 1) & 0x7f | (if (c) 0x80 else 0))
|
||||
})
|
||||
}).overwriteSp(sp)
|
||||
}),
|
||||
ASL -> ((currentStatus, zpreg) => {
|
||||
ASL -> ((currentStatus, zpreg, sp) => {
|
||||
val r = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(c = r.bit7, n = AnyStatus, z = AnyStatus, src = AnyStatus).setReg(zpreg, r.map(n => (n << 1) & 0xff))
|
||||
currentStatus.copy(c = r.bit7, n = AnyStatus, z = AnyStatus, src = AnyStatus).setReg(zpreg, r.map(n => (n << 1) & 0xff)).overwriteSp(sp)
|
||||
}),
|
||||
LSR -> ((currentStatus, zpreg) => {
|
||||
LSR -> ((currentStatus, zpreg, sp) => {
|
||||
val r = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(c = r.bit0, n = AnyStatus, z = AnyStatus, src = AnyStatus).setReg(zpreg, r.map(n => (n >> 1) & 0xff))
|
||||
currentStatus.copy(c = r.bit0, n = AnyStatus, z = AnyStatus, src = AnyStatus).setReg(zpreg, r.map(n => (n >> 1) & 0xff)).overwriteSp(sp)
|
||||
}),
|
||||
INC -> ((currentStatus, zpreg) => {
|
||||
INC -> ((currentStatus, zpreg, sp) => {
|
||||
val r = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(n = AnyStatus, z = AnyStatus, src = AnyStatus).setReg(zpreg, r.map(n => (n + 1) & 0xff))
|
||||
currentStatus.copy(n = AnyStatus, z = AnyStatus, src = AnyStatus).setReg(zpreg, r.map(n => (n + 1) & 0xff)).overwriteSp(sp)
|
||||
}),
|
||||
DEC -> ((currentStatus, zpreg) => {
|
||||
DEC -> ((currentStatus, zpreg, sp) => {
|
||||
val r = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(n = AnyStatus, z = AnyStatus, src = AnyStatus).setReg(zpreg, r.map(n => (n - 1) & 0xff))
|
||||
currentStatus.copy(n = AnyStatus, z = AnyStatus, src = AnyStatus).setReg(zpreg, r.map(n => (n - 1) & 0xff)).overwriteSp(sp)
|
||||
}),
|
||||
CMP -> ((currentStatus, zpreg) => {
|
||||
CMP -> ((currentStatus, zpreg, sp) => {
|
||||
val r = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(c = AnyStatus, n = AnyStatus, z = (currentStatus.a <*> r)(_==_), src = if (r.contains(0)) SourceOfNZ.A else AnyStatus)
|
||||
}),
|
||||
CPX -> ((currentStatus, zpreg) => {
|
||||
CPX -> ((currentStatus, zpreg, sp) => {
|
||||
val r = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(c = AnyStatus, n = AnyStatus, z = (currentStatus.x <*> r)(_==_), src = if (r.contains(0)) SourceOfNZ.X else AnyStatus)
|
||||
}),
|
||||
CPY -> ((currentStatus, zpreg) => {
|
||||
CPY -> ((currentStatus, zpreg, sp) => {
|
||||
val r = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(c = AnyStatus, n = AnyStatus, z = (currentStatus.y <*> r)(_==_), src = if (r.contains(0)) SourceOfNZ.Y else AnyStatus)
|
||||
}),
|
||||
CPZ -> ((currentStatus, zpreg) => {
|
||||
CPZ -> ((currentStatus, zpreg, sp) => {
|
||||
val r = currentStatus.getReg(zpreg)
|
||||
currentStatus.copy(c = AnyStatus, n = AnyStatus, z = (currentStatus.iz <*> r)(_==_), src = if (r.contains(0)) SourceOfNZ.Z else AnyStatus)
|
||||
}),
|
||||
BIT -> ((currentStatus, zpreg) => currentStatus.copy(v = AnyStatus, n = AnyStatus, z = AnyStatus, src = AnyStatus)),
|
||||
TRB -> ((currentStatus, zpreg) => currentStatus.copy(z = AnyStatus, src = AnyStatus).setReg(zpreg, AnyStatus)),
|
||||
TSB -> ((currentStatus, zpreg) => currentStatus.copy(z = AnyStatus, src = AnyStatus).setReg(zpreg, AnyStatus)),
|
||||
JMP -> ((_,_) => CpuStatus()),
|
||||
BRA -> ((_,_) => CpuStatus()),
|
||||
BRL -> ((_,_) => CpuStatus()),
|
||||
BIT -> ((currentStatus, zpreg, sp) => currentStatus.copy(v = AnyStatus, n = AnyStatus, z = AnyStatus, src = AnyStatus)),
|
||||
TRB -> ((currentStatus, zpreg, sp) => currentStatus.copy(z = AnyStatus, src = AnyStatus).setReg(zpreg, AnyStatus)),
|
||||
TSB -> ((currentStatus, zpreg, sp) => currentStatus.copy(z = AnyStatus, src = AnyStatus).setReg(zpreg, AnyStatus)),
|
||||
JMP -> ((_,_,_) => CpuStatus()),
|
||||
BRA -> ((_,_,_) => CpuStatus()),
|
||||
BRL -> ((_,_,_) => CpuStatus()),
|
||||
)
|
||||
|
||||
def hasDefinition(opcode: Opcode.Value): Boolean = map.contains(opcode)
|
||||
|
||||
def get(opcode: Opcode.Value): (CpuStatus, Option[Int]) => CpuStatus = map(opcode)
|
||||
def get(opcode: Opcode.Value): (CpuStatus, Option[Int], Boolean) => CpuStatus = map(opcode)
|
||||
}
|
||||
|
@ -36,19 +36,19 @@ object LaterOptimizations {
|
||||
TwoDifferentLoadsWhoseFlagsWillNotBeChecked(LDY, Not(ChangesY), LDA, TYA),
|
||||
|
||||
(HasOpcodeIn(Set(LDA, STA)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesA) & Not(HasOpcode(LDX)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Linear & Not(ChangesA) & Not(HasOpcode(LDX)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAtAssumingNonchangingIndices(0, 1)).* ~
|
||||
(Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1)) ~~> (code => code.init :+ AssemblyLine.implied(TAX)),
|
||||
|
||||
(HasOpcodeIn(Set(LDA, STA)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesA) & Not(HasOpcode(LDY)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Linear & Not(ChangesA) & Not(HasOpcode(LDY)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAtAssumingNonchangingIndices(0, 1)).* ~
|
||||
(Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) ~~> (code => code.init :+ AssemblyLine.implied(TAY)),
|
||||
|
||||
(HasOpcodeIn(Set(LDX, STX)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesX) & Not(HasOpcode(LDA)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Linear & Not(ChangesX) & Not(HasOpcode(LDA)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAtAssumingNonchangingIndices(0, 1)).* ~
|
||||
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~~> (code => code.init :+ AssemblyLine.implied(TXA)),
|
||||
|
||||
(HasOpcodeIn(Set(LDY, STY)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Linear & Not(ChangesY) & Not(HasOpcode(LDA)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
|
||||
(Linear & Not(ChangesY) & Not(HasOpcode(LDA)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAtAssumingNonchangingIndices(0, 1)).* ~
|
||||
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~~> (code => code.init :+ AssemblyLine.implied(TYA)),
|
||||
)
|
||||
|
||||
|
@ -256,7 +256,7 @@ object HelperCheckers {
|
||||
private val badAddrModes = Set(IndexedX, IndexedY, IndexedZ, LongIndexedY, LongIndexedZ, IndexedSY, Indirect, TripleAbsolute, Stack)
|
||||
private val goodAddrModes = Set(Implied, Immediate, WordImmediate, Relative, LongRelative)
|
||||
|
||||
def memoryAccessDoesntOverlap(l1: AssemblyLine, l2: AssemblyLine): Boolean = {
|
||||
def memoryAccessDoesntOverlap(l1: AssemblyLine, l2: AssemblyLine, assumeSameIndices: Boolean = false): Boolean = {
|
||||
val a1 = l1.addrMode
|
||||
val a2 = l2.addrMode
|
||||
if (goodAddrModes(a1) || goodAddrModes(a2)) return true
|
||||
@ -270,14 +270,17 @@ object HelperCheckers {
|
||||
def distinctThings(a: String, b: String): Boolean = {
|
||||
if (a == "__reg") return b != "__reg"
|
||||
if (b == "__reg") return a != "__reg"
|
||||
if (a == "__sp") return b != "__sp"
|
||||
if (b == "__sp") return a != "__sp"
|
||||
a.takeWhile(_ != '.') != b.takeWhile(_ != '.')
|
||||
}
|
||||
|
||||
def handleKnownDistance(distance: Short): Boolean = {
|
||||
// `distance` is the distance between the first byte that can be addressed by l1 (b1) and the first byte that can be addressed by l2 (b2): (b2-b1)
|
||||
val indexingAddrModes = Set(AbsoluteIndexedX, AbsoluteX, ZeroPageX, AbsoluteY, ZeroPageY, LongAbsoluteX)
|
||||
val a1Indexing = indexingAddrModes(a1)
|
||||
val a2Indexing = indexingAddrModes(a2)
|
||||
val indicesCancelOut = assumeSameIndices && a1 == a2
|
||||
val a1Indexing = indexingAddrModes(a1) && !indicesCancelOut
|
||||
val a2Indexing = indexingAddrModes(a2) && !indicesCancelOut
|
||||
(a1Indexing, a2Indexing) match {
|
||||
case (false, false) => distance != 0 && (distance != 1 || !w1) && (distance != -1 || !w2)
|
||||
case (true, false) => distance > 255 || distance < 0 && (distance != 256 || !w1) && (distance != -1 || !w2)
|
||||
@ -624,7 +627,7 @@ case class HasSet(state: State.Value) extends AssemblyLinePattern {
|
||||
flowInfo.hasSet(state)
|
||||
}
|
||||
|
||||
case object XContainsStackPointer extends AssemblyLinePattern {
|
||||
case object XContainsHardwareStackPointer extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||
|
||||
@ -632,6 +635,14 @@ case object XContainsStackPointer extends AssemblyLinePattern {
|
||||
flowInfo.statusBefore.eqSX
|
||||
}
|
||||
|
||||
case object XContainsSoftwareStackPointer extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
|
||||
flowInfo.statusBefore.eqSpX
|
||||
}
|
||||
|
||||
case class HasSourceOfNZ(state: State.Value) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||
@ -1008,6 +1019,23 @@ case class DoesntChangeMemoryAt(addrMode1: Int, param1: Int, opcode: Opcode.Valu
|
||||
}
|
||||
}
|
||||
|
||||
case class DoesntChangeMemoryAtAssumingNonchangingIndices(addrMode1: Int, param1: Int, opcode: Opcode.Value = Opcode.NOP) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
|
||||
import AddrMode._
|
||||
import Opcode._
|
||||
line match {
|
||||
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => !ctx.functionChangesMemory(th.name)
|
||||
case _ =>
|
||||
val p1 = ctx.get[Constant](param1)
|
||||
val a1 = ctx.get[AddrMode.Value](addrMode1)
|
||||
val changesSomeMemory = OpcodeClasses.ChangesMemoryAlways(line.opcode) || line.addrMode != AddrMode.Implied && OpcodeClasses.ChangesMemoryIfNotImplied(line.opcode)
|
||||
// TODO: NOP
|
||||
// this will break if the actual instruction was 16-bit
|
||||
!changesSomeMemory || HelperCheckers.memoryAccessDoesntOverlap(AssemblyLine(opcode, a1, p1), line, assumeSameIndices = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case object ConcernsMemory extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
|
||||
ReadsMemory.matchLineTo(ctx, flowInfo, line) || ChangesMemory.matchLineTo(ctx, flowInfo, line)
|
||||
|
@ -1,6 +1,6 @@
|
||||
package millfork.compiler
|
||||
|
||||
import millfork.env.{Environment, Label, NormalFunction}
|
||||
import millfork.env.{Environment, Label, NormalFunction, NormalParamSignature}
|
||||
import millfork.error.Logger
|
||||
import millfork.node.NiceFunctionProperty
|
||||
import millfork.{CompilationFlag, CompilationOptions, JobContext}
|
||||
@ -42,4 +42,11 @@ case class CompilationContext(env: Environment,
|
||||
def log: Logger = options.log
|
||||
@inline
|
||||
def nextLabel: LabelGenerator = options.nextLabel
|
||||
|
||||
def prologueShouldAvoidA: Boolean = {
|
||||
function.params match {
|
||||
case NormalParamSignature(List(param)) => param.typ.size == 1
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,14 +25,17 @@ object MosCompiler extends AbstractCompiler[AssemblyLine] {
|
||||
case _ => Nil
|
||||
}).map(_.position(ctx.function.position))
|
||||
val phReg =
|
||||
if (zpRegisterSize > 0) {
|
||||
(if (zpRegisterSize > 0) {
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
(0 until zpRegisterSize).flatMap { i =>
|
||||
List(
|
||||
AssemblyLine.zeropage(LDA, reg, i),
|
||||
AssemblyLine.implied(PHA))
|
||||
}.toList.map(_.position(ctx.function.position))
|
||||
} else Nil
|
||||
} else Nil) ++ (
|
||||
if (ctx.options.flag(CompilationFlag.SoftwareStack)) {
|
||||
List(AssemblyLine.absolute(LDA, ctx.env.get[ThingInMemory]("__sp")), AssemblyLine.implied(PHA))
|
||||
} else Nil)
|
||||
|
||||
val prefix = storeParamsFromRegisters ++ (if (ctx.function.interrupt) {
|
||||
|
||||
@ -124,16 +127,36 @@ object MosCompiler extends AbstractCompiler[AssemblyLine] {
|
||||
|
||||
def stackPointerFixAtBeginning(ctx: CompilationContext): List[AssemblyLine] = {
|
||||
val m = ctx.function
|
||||
if (m.stackVariablesSize == 0) return Nil
|
||||
if (ctx.options.flag(CompilationFlag.EmitIllegals)) {
|
||||
if (m.stackVariablesSize > 4)
|
||||
return List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.immediate(LDA, 0xff),
|
||||
AssemblyLine.immediate(SBX, m.stackVariablesSize),
|
||||
AssemblyLine.implied(TXS)).map(_.position(m.position)) // this TXS is fine, it won't appear in 65816 code
|
||||
if (m.stackVariablesSize == 0 && m.name != "main") return Nil
|
||||
if (ctx.options.flag(CompilationFlag.SoftwareStack)) {
|
||||
val stackPointer = ctx.env.get[ThingInMemory]("__sp")
|
||||
if (m.name == "main") {
|
||||
List(
|
||||
AssemblyLine.immediate(LDX, 0xff - m.stackVariablesSize),
|
||||
AssemblyLine.absolute(STX, stackPointer)).map(_.position(m.position))
|
||||
} else if (m.stackVariablesSize < 3 || m.stackVariablesSize == 3 && ctx.prologueShouldAvoidA) {
|
||||
List.fill(m.stackVariablesSize)(AssemblyLine.absolute(DEC, stackPointer)).map(_.position(m.position))
|
||||
} else {
|
||||
List(AssemblyLine.absolute(LDA, stackPointer),
|
||||
AssemblyLine.implied(SEC),
|
||||
AssemblyLine.immediate(SBC, m.stackVariablesSize),
|
||||
AssemblyLine.absolute(STA, stackPointer)).map(_.position(m.position))
|
||||
}
|
||||
} else {
|
||||
if (ctx.options.flag(CompilationFlag.EmitIllegals)) {
|
||||
// TODO
|
||||
if (m.stackVariablesSize > 4 && !ctx.prologueShouldAvoidA)
|
||||
return List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.immediate(LDA, 0xff),
|
||||
AssemblyLine.immediate(SBX, m.stackVariablesSize),
|
||||
AssemblyLine.implied(TXS)).map(_.position(m.position)) // this TXS is fine, it won't appear in 65816 code
|
||||
}
|
||||
if (ctx.prologueShouldAvoidA && ctx.options.flag(CompilationFlag.EmitCmosOpcodes)) {
|
||||
return List.fill(m.stackVariablesSize)(AssemblyLine.implied(PHX)).map(_.position(m.position))
|
||||
}
|
||||
List.fill(m.stackVariablesSize)(AssemblyLine.implied(PHA)).map(_.position(m.position))
|
||||
}
|
||||
List.fill(m.stackVariablesSize)(AssemblyLine.implied(PHA)).map(_.position(m.position))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -57,24 +57,24 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
case StackVariable(_, t, offset) =>
|
||||
t.size match {
|
||||
case 0 => Nil
|
||||
case 1 => List(
|
||||
case 1 => AssemblyLine.tsx(ctx) ++ List(
|
||||
AssemblyLine.immediate(LDA, expr.loByte),
|
||||
AssemblyLine.dataStackX(ctx, STA, offset))
|
||||
case 2 => AssemblyLine.tsx(ctx) ++ List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.immediate(LDA, expr.loByte),
|
||||
AssemblyLine.absoluteX(STA, offset + ctx.extraStackOffset))
|
||||
case 2 => List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.immediate(LDA, expr.loByte),
|
||||
AssemblyLine.absoluteX(STA, offset + ctx.extraStackOffset),
|
||||
AssemblyLine.dataStackX(ctx, STA, offset),
|
||||
AssemblyLine.immediate(LDA, expr.hiByte),
|
||||
AssemblyLine.absoluteX(STA, offset + ctx.extraStackOffset + 1))
|
||||
case s => AssemblyLine.implied(TSX) :: List.tabulate(s)(i => List(
|
||||
AssemblyLine.absoluteX(STA, offset + 1))
|
||||
case s => AssemblyLine.tsx(ctx) ++ List.tabulate(s)(i => List(
|
||||
AssemblyLine.immediate(LDA, expr.subbyte(i)),
|
||||
AssemblyLine.absoluteX(STA, offset + ctx.extraStackOffset + i))).flatten
|
||||
AssemblyLine.dataStackX(ctx, STA, offset + i))).flatten
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def fixTsx(code: List[AssemblyLine]): List[AssemblyLine] = code match {
|
||||
case (access@AssemblyLine0(_, Stack | IndexedSY, p)) :: xs => access.copy(parameter = (p + 1).quickSimplify) :: fixTsx(xs)
|
||||
case (tsx@AssemblyLine0(TSX, _, _)) :: xs => tsx :: AssemblyLine.implied(INX) :: fixTsx(xs)
|
||||
case (txs@AssemblyLine0(TXS, _, _)) :: xs => ???
|
||||
case x :: xs => x :: fixTsx(xs)
|
||||
@ -218,12 +218,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
case 1 =>
|
||||
v match {
|
||||
case mv: VariableInMemory => AssemblyLine.variable(ctx, store, mv)
|
||||
case sv@StackVariable(_, _, offset) =>
|
||||
if (ctx.options.flags(CompilationFlag.EmitEmulation65816Opcodes)) {
|
||||
AssemblyLine.implied(transferToA) :: AssemblyLine.stackRelative(STA, offset + ctx.extraStackOffset) :: Nil
|
||||
} else {
|
||||
AssemblyLine.implied(transferToA) :: AssemblyLine.implied(TSX) :: AssemblyLine.absoluteX(STA, offset + ctx.extraStackOffset) :: Nil
|
||||
}
|
||||
case sv: StackVariable =>
|
||||
AssemblyLine.implied(transferToA) :: (AssemblyLine.tsx(ctx) :+ AssemblyLine.dataStackX(ctx, STA, sv))
|
||||
}
|
||||
case s if s > 1 =>
|
||||
v match {
|
||||
@ -231,11 +227,13 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
AssemblyLine.variable(ctx, store, mv) ++
|
||||
List(AssemblyLine.immediate(LDA, 0)) ++
|
||||
List.tabulate(s - 1)(i => AssemblyLine.variable(ctx, STA, mv, i + 1)).flatten
|
||||
case sv@StackVariable(_, _, offset) =>
|
||||
AssemblyLine.implied(transferToA) ::
|
||||
AssemblyLine.implied(TSX) ::
|
||||
AssemblyLine.absoluteX(STA, offset + ctx.extraStackOffset) ::
|
||||
List.tabulate(s - 1)(i => AssemblyLine.absoluteX(STA, offset + ctx.extraStackOffset + i + 1))
|
||||
case sv: StackVariable =>
|
||||
AssemblyLine.implied(transferToA) :: (
|
||||
AssemblyLine.tsx(ctx) ++
|
||||
List(AssemblyLine.dataStackX(ctx, STA, sv), AssemblyLine.immediate(LDA, 0)) ++
|
||||
List.tabulate(s - 1)(i => AssemblyLine.dataStackX(ctx, STA, sv, i + 1))
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
case IndexedExpression(arrayName, indexExpr) =>
|
||||
@ -440,104 +438,99 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
ctx.log.error(s"Variable `$target.name` is too small", expr.position)
|
||||
Nil
|
||||
} else {
|
||||
val copy = List.tabulate(exprType.size)(i => AssemblyLine.variable(ctx, LDA, source, i) :+ AssemblyLine.absoluteX(STA, target.baseOffset + ctx.extraStackOffset + i))
|
||||
val copy = List.tabulate(exprType.size)(i => AssemblyLine.variable(ctx, LDA, source, i) :+ AssemblyLine.dataStackX(ctx, STA, target, i))
|
||||
val extend = if (exprType.size == target.typ.size) Nil else if (exprType.isSigned) {
|
||||
signExtendA(ctx) ++ List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.absoluteX(STA, target.baseOffset + ctx.extraStackOffset + i + exprType.size))
|
||||
signExtendA(ctx) ++ List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.dataStackX(ctx, STA, target, i + exprType.size))
|
||||
} else {
|
||||
AssemblyLine.immediate(LDA, 0) ::
|
||||
List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.absoluteX(STA, target.baseOffset + ctx.extraStackOffset + i + exprType.size))
|
||||
List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.dataStackX(ctx, STA, target, i + exprType.size))
|
||||
}
|
||||
AssemblyLine.implied(TSX) :: (copy.flatten ++ extend)
|
||||
AssemblyLine.tsx(ctx) ++ copy.flatten ++ extend
|
||||
}
|
||||
}
|
||||
case source@StackVariable(_, sourceType, offset) =>
|
||||
target match {
|
||||
case RegisterVariable(MosRegister.A, _) => List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset))
|
||||
case RegisterVariable(MosRegister.X, _) => List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset), AssemblyLine.implied(TAX))
|
||||
case RegisterVariable(MosRegister.Y, _) => List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDY, offset + ctx.extraStackOffset))
|
||||
case RegisterVariable(MosRegister.A, _) => AssemblyLine.tsx(ctx) :+ AssemblyLine.dataStackX(ctx, LDA, offset)
|
||||
case RegisterVariable(MosRegister.X, _) => AssemblyLine.tsx(ctx) ++ List(AssemblyLine.dataStackX(ctx, LDA, offset), AssemblyLine.implied(TAX))
|
||||
case RegisterVariable(MosRegister.Y, _) => AssemblyLine.tsx(ctx) :+ AssemblyLine.dataStackX(ctx, LDY, offset)
|
||||
case RegisterVariable(MosRegister.AX, _) =>
|
||||
exprType.size match {
|
||||
AssemblyLine.tsx(ctx) ++ (exprType.size match {
|
||||
case 1 => if (exprType.isSigned) {
|
||||
List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset),
|
||||
AssemblyLine.dataStackX(ctx, LDA, offset),
|
||||
AssemblyLine.implied(PHA)) ++ signExtendA(ctx) ++ List(
|
||||
AssemblyLine.implied(TAX),
|
||||
AssemblyLine.implied(PLA))
|
||||
} else List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset),
|
||||
AssemblyLine.dataStackX(ctx, LDA, offset),
|
||||
AssemblyLine.immediate(LDX, 0))
|
||||
case 2 => List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset),
|
||||
AssemblyLine.dataStackX(ctx, LDA, offset),
|
||||
AssemblyLine.implied(PHA),
|
||||
AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset + 1),
|
||||
AssemblyLine.dataStackX(ctx, LDA, offset + 1),
|
||||
AssemblyLine.implied(TAX),
|
||||
AssemblyLine.implied(PLA))
|
||||
}
|
||||
})
|
||||
case RegisterVariable(MosRegister.AY, _) =>
|
||||
exprType.size match {
|
||||
AssemblyLine.tsx(ctx) ++ (exprType.size match {
|
||||
case 1 => if (exprType.isSigned) {
|
||||
val label = ctx.nextLabel("sx")
|
||||
??? // TODO
|
||||
} else {
|
||||
List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset),
|
||||
AssemblyLine.dataStackX(ctx, LDA, offset),
|
||||
AssemblyLine.immediate(LDY, 0))
|
||||
}
|
||||
case 2 => List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset),
|
||||
AssemblyLine.absoluteX(LDY, offset + ctx.extraStackOffset + 1))
|
||||
}
|
||||
AssemblyLine.dataStackX(ctx, LDA, offset),
|
||||
AssemblyLine.dataStackX(ctx, LDY, offset + 1))
|
||||
})
|
||||
case RegisterVariable(MosRegister.XA, _) =>
|
||||
??? // TODO
|
||||
case RegisterVariable(MosRegister.YA, _) =>
|
||||
exprType.size match {
|
||||
AssemblyLine.tsx(ctx) ++ (exprType.size match {
|
||||
case 1 => if (exprType.isSigned) {
|
||||
val label = ctx.nextLabel("sx")
|
||||
??? // TODO
|
||||
} else {
|
||||
List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.absoluteX(LDY, offset + ctx.extraStackOffset),
|
||||
AssemblyLine.dataStackX(ctx, LDY, offset),
|
||||
AssemblyLine.immediate(LDA, 0))
|
||||
}
|
||||
case 2 => List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.absoluteX(LDY, offset + ctx.extraStackOffset),
|
||||
AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset + 1))
|
||||
}
|
||||
AssemblyLine.dataStackX(ctx, LDY, offset),
|
||||
AssemblyLine.dataStackX(ctx, LDA, offset + 1))
|
||||
})
|
||||
case target: VariableInMemory =>
|
||||
if (exprType.size > target.typ.size) {
|
||||
ctx.log.error(s"Variable `$target.name` is too small", expr.position)
|
||||
Nil
|
||||
} else {
|
||||
val copy = List.tabulate(exprType.size)(i => AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset + i) :: AssemblyLine.variable(ctx, STA, target, i))
|
||||
val copy = List.tabulate(exprType.size)(i => AssemblyLine.dataStackX(ctx, LDA, offset + i) :: AssemblyLine.variable(ctx, STA, target, i))
|
||||
val extend = if (exprType.size == target.typ.size) Nil else if (exprType.isSigned) {
|
||||
signExtendA(ctx) ++ List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.variable(ctx, STA, target, i + exprType.size)).flatten
|
||||
} else {
|
||||
AssemblyLine.immediate(LDA, 0) ::
|
||||
List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.variable(ctx, STA, target, i + exprType.size)).flatten
|
||||
}
|
||||
AssemblyLine.implied(TSX) :: (copy.flatten ++ extend)
|
||||
AssemblyLine.tsx(ctx) ++ copy.flatten ++ extend
|
||||
}
|
||||
case target: StackVariable =>
|
||||
if (exprType.size > target.typ.size) {
|
||||
ctx.log.error(s"Variable `$target.name` is too small", expr.position)
|
||||
Nil
|
||||
} else {
|
||||
val copyFromLo = List.tabulate(exprType.size)(i => List(AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset + i), AssemblyLine.absoluteX(STA, target.baseOffset + i)))
|
||||
val copyFromLo = List.tabulate(exprType.size)(i => List(AssemblyLine.dataStackX(ctx, LDA, offset + i), AssemblyLine.dataStackX(ctx, STA, target, i)))
|
||||
val copy = if (shouldCopyFromHiToLo(NumericConstant(source.baseOffset, 2), NumericConstant(target.baseOffset, 2))) copyFromLo.reverse else copyFromLo
|
||||
val extend = if (exprType.size == target.typ.size) Nil else if (exprType.isSigned) {
|
||||
signExtendA(ctx) ++ List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.absoluteX(STA, target.baseOffset + ctx.extraStackOffset + i + exprType.size))
|
||||
signExtendA(ctx) ++ List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.dataStackX(ctx, STA, target, i + exprType.size))
|
||||
} else {
|
||||
AssemblyLine.immediate(LDA, 0) ::
|
||||
List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.absoluteX(STA, target.baseOffset + ctx.extraStackOffset + i + exprType.size))
|
||||
List.tabulate(target.typ.size - exprType.size)(i => AssemblyLine.dataStackX(ctx, STA, target, i + exprType.size))
|
||||
}
|
||||
AssemblyLine.implied(TSX) :: (copy.flatten ++ extend)
|
||||
AssemblyLine.tsx(ctx) ++ copy.flatten ++ extend
|
||||
}
|
||||
}
|
||||
case source@ConstantThing(_, value, _) =>
|
||||
@ -710,8 +703,13 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
ctx.log.error(s"Variable `$target.name` cannot hold a word", expr.position)
|
||||
Nil
|
||||
case 2 =>
|
||||
compile(ctx, l, Some(b -> StackVariable("", b, target.baseOffset + ctx.extraStackOffset)), branches) ++
|
||||
compile(ctx, h, Some(b -> StackVariable("", b, target.baseOffset + ctx.extraStackOffset + 1)), branches)
|
||||
if (ctx.options.flag(CompilationFlag.SoftwareStack)) {
|
||||
compile(ctx, l, Some(b -> StackVariable("", b, target.baseOffset)), branches) ++
|
||||
compile(ctx, h, Some(b -> StackVariable("", b, target.baseOffset + 1)), branches)
|
||||
} else {
|
||||
compile(ctx, l, Some(b -> StackVariable("", b, target.baseOffset + ctx.extraStackOffset)), branches) ++
|
||||
compile(ctx, h, Some(b -> StackVariable("", b, target.baseOffset + ctx.extraStackOffset + 1)), branches)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1257,21 +1255,19 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
case (t, v: StackVariable) => t.size match {
|
||||
case 1 => v.typ.size match {
|
||||
case 1 =>
|
||||
List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(STA, v.baseOffset + ctx.extraStackOffset))
|
||||
AssemblyLine.tsx(ctx) :+ AssemblyLine.dataStackX(ctx, STA, v)
|
||||
case s if s > 1 =>
|
||||
if (t.isSigned) {
|
||||
AssemblyLine.tsx(ctx) ++ (if (t.isSigned) {
|
||||
List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.absoluteX(STA, v.baseOffset + ctx.extraStackOffset)) ++
|
||||
AssemblyLine.dataStackX(ctx, STA, v.baseOffset)) ++
|
||||
signExtendA(ctx) ++
|
||||
List.tabulate(s - 1)(i => AssemblyLine.absoluteX(STA, v.baseOffset + ctx.extraStackOffset + i + 1))
|
||||
List.tabulate(s - 1)(i => AssemblyLine.dataStackX(ctx, STA, v, i + 1))
|
||||
} else {
|
||||
List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.absoluteX(STA, v.baseOffset + ctx.extraStackOffset),
|
||||
AssemblyLine.dataStackX(ctx, STA, v.baseOffset),
|
||||
AssemblyLine.immediate(LDA, 0)) ++
|
||||
List.tabulate(s - 1)(i => AssemblyLine.absoluteX(STA, v.baseOffset + ctx.extraStackOffset + i + 1))
|
||||
}
|
||||
List.tabulate(s - 1)(i => AssemblyLine.dataStackX(ctx, STA, v, i + 1))
|
||||
})
|
||||
}
|
||||
case 2 => v.typ.size match {
|
||||
case 1 =>
|
||||
@ -1280,11 +1276,10 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
case 2 =>
|
||||
List(
|
||||
AssemblyLine.implied(TAY),
|
||||
AssemblyLine.implied(TXA),
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.absoluteX(STA, v.baseOffset + ctx.extraStackOffset + 1),
|
||||
AssemblyLine.implied(TXA)) ++ AssemblyLine.tsx(ctx) ++ List(
|
||||
AssemblyLine.dataStackX(ctx, STA, v, 1),
|
||||
AssemblyLine.implied(TYA),
|
||||
AssemblyLine.absoluteX(STA, v.baseOffset + ctx.extraStackOffset))
|
||||
AssemblyLine.dataStackX(ctx, STA, v))
|
||||
case s if s > 2 => ???
|
||||
}
|
||||
}
|
||||
|
@ -35,14 +35,18 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
||||
val w = env.get[Type]("word")
|
||||
val zpRegisterSize = ctx.options.zpRegisterSize
|
||||
lazy val plReg =
|
||||
if (zpRegisterSize > 0) {
|
||||
(if (ctx.options.flag(CompilationFlag.SoftwareStack)) {
|
||||
List(
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.absolute(STA, ctx.env.get[ThingInMemory]("__sp")))
|
||||
} else Nil) ++ (if (zpRegisterSize > 0) {
|
||||
val reg = env.get[VariableInMemory]("__reg")
|
||||
(zpRegisterSize.-(1) to 0 by (-1)).flatMap{ i=>
|
||||
(zpRegisterSize.-(1) to 0 by (-1)).flatMap { i =>
|
||||
List(
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.zeropage(STA, reg,i))
|
||||
}.toList
|
||||
} else Nil
|
||||
} else Nil)
|
||||
val someRegisterA = Some(b, RegisterVariable(MosRegister.A, b))
|
||||
val someRegisterAX = Some(w, RegisterVariable(MosRegister.AX, w))
|
||||
val someRegisterYA = Some(w, RegisterVariable(MosRegister.YA, w))
|
||||
@ -228,9 +232,9 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
||||
ctx.log.error("Cannot return anything from a void function", statement.position)
|
||||
stackPointerFixBeforeReturn(ctx) ++ returnInstructions
|
||||
case 1 =>
|
||||
MosExpressionCompiler.compile(ctx, e, someRegisterA, NoBranching) ++ stackPointerFixBeforeReturn(ctx) ++ returnInstructions
|
||||
MosExpressionCompiler.compile(ctx, e, someRegisterA, NoBranching) ++ stackPointerFixBeforeReturn(ctx, preserveA = true) ++ returnInstructions
|
||||
case 2 =>
|
||||
MosExpressionCompiler.compile(ctx, e, someRegisterAX, NoBranching) ++ stackPointerFixBeforeReturn(ctx) ++ returnInstructions
|
||||
MosExpressionCompiler.compile(ctx, e, someRegisterAX, NoBranching) ++ stackPointerFixBeforeReturn(ctx, preserveA = true, preserveX = true) ++ returnInstructions
|
||||
case _ =>
|
||||
MosExpressionCompiler.compileAssignment(ctx, e, VariableExpression(ctx.function.name + "`return")) ++
|
||||
stackPointerFixBeforeReturn(ctx) ++ returnInstructions
|
||||
@ -242,10 +246,10 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
||||
ctx.log.error("Cannot return anything from a void function", statement.position)
|
||||
stackPointerFixBeforeReturn(ctx) ++ List(AssemblyLine.discardAF(), AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
|
||||
case 1 =>
|
||||
MosExpressionCompiler.compile(ctx, e, someRegisterA, NoBranching) ++ stackPointerFixBeforeReturn(ctx) ++ List(AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
|
||||
MosExpressionCompiler.compile(ctx, e, someRegisterA, NoBranching) ++ stackPointerFixBeforeReturn(ctx, preserveA = true) ++ List(AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
|
||||
case 2 =>
|
||||
// TODO: ???
|
||||
val stackPointerFix = stackPointerFixBeforeReturn(ctx)
|
||||
val stackPointerFix = stackPointerFixBeforeReturn(ctx, preserveA = true, preserveY = true)
|
||||
if (stackPointerFix.isEmpty) {
|
||||
MosExpressionCompiler.compile(ctx, e, someRegisterAX, NoBranching) ++ List(AssemblyLine.discardYF()) ++ returnInstructions
|
||||
} else {
|
||||
@ -276,38 +280,80 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
||||
}).map(_.positionIfEmpty(statement.position))
|
||||
}
|
||||
|
||||
def stackPointerFixBeforeReturn(ctx: CompilationContext): List[AssemblyLine] = {
|
||||
private def stackPointerFixBeforeReturn(ctx: CompilationContext, preserveA: Boolean = false, preserveX: Boolean = false, preserveY: Boolean = false): List[AssemblyLine] = {
|
||||
val m = ctx.function
|
||||
if (m.stackVariablesSize == 0) return Nil
|
||||
|
||||
if (m.returnType.size == 0 && m.stackVariablesSize <= 2)
|
||||
return List.fill(m.stackVariablesSize)(AssemblyLine.implied(PLA))
|
||||
|
||||
if (ctx.options.flag(CompilationFlag.EmitCmosOpcodes)) {
|
||||
if (m.returnType.size == 1 && m.stackVariablesSize <= 2) {
|
||||
return List.fill(m.stackVariablesSize)(AssemblyLine.implied(PLX))
|
||||
}
|
||||
if (m.returnType.size == 2 && m.stackVariablesSize <= 2) {
|
||||
return List.fill(m.stackVariablesSize)(AssemblyLine.implied(PLY))
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.options.flag(CompilationFlag.EmitIllegals)) {
|
||||
if (m.returnType.size == 0 && m.stackVariablesSize > 4)
|
||||
return List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.immediate(LDA, 0xff),
|
||||
AssemblyLine.immediate(SBX, 256 - m.stackVariablesSize),
|
||||
AssemblyLine.implied(TXS)) // this TXS is fine, it won't appear in 65816 code
|
||||
if (m.returnType.size == 1 && m.stackVariablesSize > 6)
|
||||
return List(
|
||||
AssemblyLine.implied(TAY),
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.immediate(LDA, 0xff),
|
||||
AssemblyLine.immediate(SBX, 256 - m.stackVariablesSize),
|
||||
AssemblyLine.implied(TXS), // this TXS is fine, it won't appear in 65816 code
|
||||
if (m.stackVariablesSize == 0 && m.name != "main") return Nil
|
||||
if (ctx.options.flag(CompilationFlag.SoftwareStack)) {
|
||||
// TODO
|
||||
val stackPointer = ctx.env.get[ThingInMemory]("__sp")
|
||||
if (m.name == "main") {
|
||||
List(
|
||||
AssemblyLine.immediate(LDY, 0xff),
|
||||
AssemblyLine.absolute(STY, stackPointer))
|
||||
} else if (m.stackVariablesSize < 3) {
|
||||
List.fill(m.stackVariablesSize)(AssemblyLine.absolute(INC, stackPointer))
|
||||
} else if (!preserveA) {
|
||||
List(AssemblyLine.absolute(LDA, stackPointer),
|
||||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.immediate(ADC, m.stackVariablesSize),
|
||||
AssemblyLine.absolute(STA, stackPointer))
|
||||
} else if (!preserveY) {
|
||||
List(AssemblyLine.implied(TAY),
|
||||
AssemblyLine.absolute(LDA, stackPointer),
|
||||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.immediate(ADC, m.stackVariablesSize),
|
||||
AssemblyLine.absolute(STA, stackPointer),
|
||||
AssemblyLine.implied(TYA))
|
||||
} else if (!preserveX) {
|
||||
List(AssemblyLine.implied(TAY),
|
||||
AssemblyLine.absolute(LDA, stackPointer),
|
||||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.immediate(ADC, m.stackVariablesSize),
|
||||
AssemblyLine.absolute(STA, stackPointer),
|
||||
AssemblyLine.implied(TYA))
|
||||
} else ???
|
||||
} else {
|
||||
if (!preserveA && m.stackVariablesSize <= 2)
|
||||
return List.fill(m.stackVariablesSize)(AssemblyLine.implied(PLA))
|
||||
|
||||
if (ctx.options.flag(CompilationFlag.EmitCmosOpcodes)) {
|
||||
if (!preserveX && m.stackVariablesSize <= 2) {
|
||||
return List.fill(m.stackVariablesSize)(AssemblyLine.implied(PLX))
|
||||
}
|
||||
if (!preserveY && m.stackVariablesSize <= 2) {
|
||||
return List.fill(m.stackVariablesSize)(AssemblyLine.implied(PLY))
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.options.flag(CompilationFlag.EmitIllegals) && !preserveX) {
|
||||
// TODO
|
||||
if (!preserveA && m.stackVariablesSize > 4)
|
||||
return List(
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.immediate(LDA, 0xff),
|
||||
AssemblyLine.immediate(SBX, 256 - m.stackVariablesSize),
|
||||
AssemblyLine.implied(TXS)) // this TXS is fine, it won't appear in 65816 code
|
||||
if (!preserveY && m.stackVariablesSize > 6)
|
||||
return List(
|
||||
AssemblyLine.implied(TAY),
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.immediate(LDA, 0xff),
|
||||
AssemblyLine.immediate(SBX, 256 - m.stackVariablesSize),
|
||||
AssemblyLine.implied(TXS), // this TXS is fine, it won't appear in 65816 code
|
||||
AssemblyLine.implied(TYA))
|
||||
}
|
||||
if (!preserveX) {
|
||||
AssemblyLine.implied(TSX) :: (List.fill(m.stackVariablesSize)(AssemblyLine.implied(INX)) :+ AssemblyLine.implied(TXS)) // this TXS is fine, it won't appear in 65816 code
|
||||
} else {
|
||||
// TODO: figure out if there's nothing better
|
||||
if (!preserveA) {
|
||||
List.fill(m.stackVariablesSize)(AssemblyLine.implied(PLA))
|
||||
} else if (ctx.options.flag(CompilationFlag.EmitCmosOpcodes) && !preserveY) {
|
||||
List.fill(m.stackVariablesSize)(AssemblyLine.implied(PLY))
|
||||
} else if (!preserveY) {
|
||||
AssemblyLine.implied(TAY) :: (List.fill(m.stackVariablesSize)(AssemblyLine.implied(PLA)) :+ AssemblyLine.implied(TYA))
|
||||
} else ???
|
||||
}
|
||||
}
|
||||
AssemblyLine.implied(TSX) :: (List.fill(m.stackVariablesSize)(AssemblyLine.implied(INX)) :+ AssemblyLine.implied(TXS)) // this TXS is fine, it won't appear in 65816 code
|
||||
}
|
||||
}
|
||||
|
10
src/main/scala/millfork/env/Environment.scala
vendored
10
src/main/scala/millfork/env/Environment.scala
vendored
@ -1,7 +1,7 @@
|
||||
package millfork.env
|
||||
|
||||
import millfork.assembly.BranchingOpcodeMapping
|
||||
import millfork._
|
||||
import millfork.{env, _}
|
||||
import millfork.assembly.mos.Opcode
|
||||
import millfork.assembly.z80.{IfFlagClear, IfFlagSet, ZFlag}
|
||||
import millfork.compiler.LabelGenerator
|
||||
@ -1077,11 +1077,11 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
if (stmt.register && stmt.address.isDefined) log.error(s"`$name` cannot by simultaneously at an address and in a register", position)
|
||||
if (stmt.stack) {
|
||||
val v = StackVariable(prefix + name, typ, this.baseStackOffset)
|
||||
baseStackOffset += typ.size
|
||||
addThing(v, stmt.position)
|
||||
for((suffix, offset, t) <- getSubvariables(typ)) {
|
||||
addThing(StackVariable(prefix + name + suffix, t, baseStackOffset + offset), stmt.position)
|
||||
}
|
||||
baseStackOffset += typ.size
|
||||
} else {
|
||||
val (v, addr) = stmt.address.fold[(VariableInMemory, Constant)]({
|
||||
val alloc =
|
||||
@ -1230,6 +1230,12 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
if (!things.contains("__constant8")) {
|
||||
things("__constant8") = InitializedArray("__constant8", None, List(LiteralExpression(8, 1)), declaredBank = None, b, b, NoAlignment)
|
||||
}
|
||||
if (options.flag(CompilationFlag.SoftwareStack)) {
|
||||
if (!things.contains("__sp")) {
|
||||
things("__sp") = UninitializedMemoryVariable("__sp", b, VariableAllocationMethod.Auto, None, NoAlignment)
|
||||
things("__stack") = UninitializedArray("__stack", 256, None, b, b, WithinPageAlignment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ object UnusedGlobalVariables extends NodeOptimization {
|
||||
}
|
||||
|
||||
def getAllReadVariables(expressions: List[Node]): List[String] = expressions.flatMap {
|
||||
case s: VariableDeclarationStatement => getAllReadVariables(s.address.toList) ++ getAllReadVariables(s.initialValue.toList)
|
||||
case s: VariableDeclarationStatement => getAllReadVariables(s.address.toList) ++ getAllReadVariables(s.initialValue.toList) ++ (if (s.stack) List("__sp", "__stack") else Nil)
|
||||
case s: ArrayDeclarationStatement => getAllReadVariables(s.address.toList) ++ getAllReadVariables(s.elements.toList)
|
||||
case s: ArrayContents => getAllReadVariables(s.getAllExpressions)
|
||||
case s: FunctionDeclarationStatement => getAllReadVariables(s.address.toList) ++ getAllReadVariables(s.statements.getOrElse(Nil))
|
||||
|
@ -1,7 +1,7 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.Cpu
|
||||
import millfork.test.emu.{EmuCmosBenchmarkRun, EmuCrossPlatformBenchmarkRun, EmuZ80BenchmarkRun}
|
||||
import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuOptimizedSoftwareStackRun, EmuSoftwareStackBenchmarkRun, EmuUnoptimizedZ80Run}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
@ -10,7 +10,7 @@ import org.scalatest.{FunSuite, Matchers}
|
||||
class StackVarSuite extends FunSuite with Matchers {
|
||||
|
||||
test("Basic stack assignment") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.StrictMos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| byte output @$c000
|
||||
| void main () {
|
||||
| stack byte a
|
||||
@ -24,7 +24,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Stack byte addition") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.StrictMos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| byte output @$c000
|
||||
| void main () {
|
||||
| stack byte a
|
||||
@ -42,7 +42,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Complex expressions involving stack variables (6502)") {
|
||||
EmuCmosBenchmarkRun("""
|
||||
EmuSoftwareStackBenchmarkRun("""
|
||||
| byte output @$c000
|
||||
| void main () {
|
||||
| stack byte a
|
||||
@ -89,7 +89,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
// }
|
||||
|
||||
test("Stack word addition") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.StrictMos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| stack word a
|
||||
@ -107,7 +107,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Recursion") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.StrictMos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| array output [6] @$c000
|
||||
| byte fails @$c010
|
||||
| void main () {
|
||||
@ -143,7 +143,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Recursion 2") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.StrictMos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| array output [6] @$c000
|
||||
| byte fails @$c010
|
||||
| void main () {
|
||||
@ -175,7 +175,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Complex stack-related stuff") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.StrictMos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| byte output @$c000
|
||||
| array id = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
| void main() {
|
||||
@ -193,7 +193,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
|
||||
|
||||
test("Indexing") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.StrictMos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
|
||||
| array output [200] @$c000
|
||||
| void main () {
|
||||
| stack byte a
|
||||
@ -206,7 +206,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Double array with stack variables") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.StrictMos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||
"""
|
||||
| array output[5]@$c001
|
||||
| array input = [0,1,4,9,16,25,36,49]
|
||||
@ -222,4 +222,33 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
m.readByte(0xc005) should equal (50)
|
||||
}
|
||||
}
|
||||
|
||||
test("Complex large stacks") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.StrictMos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||
// val m = EmuUnoptimizedZ80Run(
|
||||
"""
|
||||
| array output[5]@$c000
|
||||
| noinline byte f(byte y) {
|
||||
| stack int56 param
|
||||
| param = y
|
||||
| param -= 1
|
||||
| if param == 0 {
|
||||
| return 1
|
||||
| }
|
||||
| return f(param.b0) + 1
|
||||
| }
|
||||
| void main () {
|
||||
| output[0] = f(7)
|
||||
| output[1] = f(8)
|
||||
| output[2] = f(9)
|
||||
| output[3] = f(2)
|
||||
| }
|
||||
| void _panic(){while(true){}}
|
||||
""".stripMargin) { m =>
|
||||
m.readByte(0xc000) should equal(7)
|
||||
m.readByte(0xc001) should equal(8)
|
||||
m.readByte(0xc002) should equal(9)
|
||||
m.readByte(0xc003) should equal(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,30 @@ object EmuBenchmarkRun {
|
||||
}
|
||||
}
|
||||
|
||||
object EmuSoftwareStackBenchmarkRun {
|
||||
def apply(source: String)(verifier: MemoryBank => Unit): Unit = {
|
||||
val (Timings(t0, _), m0) = EmuUnoptimizedRun.apply2(source)
|
||||
val (Timings(t1, _), m1) = EmuOptimizedRun.apply2(source)
|
||||
val (Timings(t2, _), m2) = EmuOptimizedInlinedRun.apply2(source)
|
||||
val (Timings(t3, _), m3) = EmuOptimizedSoftwareStackRun.apply2(source)
|
||||
println(f"Before optimization: $t0%7d")
|
||||
println(f"After optimization: $t1%7d")
|
||||
println(f"After inlining: $t2%7d")
|
||||
println(f"After software stack: $t3%7d")
|
||||
println(f"Gain: ${(100L * (t0 - t1) / t0.toDouble).round}%7d%%")
|
||||
println(f"Gain with inlining: ${(100L * (t0 - t2) / t0.toDouble).round}%7d%%")
|
||||
println(f"Gain with SW stack: ${(100L * (t0 - t3) / t0.toDouble).round}%7d%%")
|
||||
println(f"Running 6502 unoptimized")
|
||||
verifier(m0)
|
||||
println(f"Running 6502 optimized")
|
||||
verifier(m1)
|
||||
println(f"Running 6502 optimized inlined")
|
||||
verifier(m2)
|
||||
println(f"Running 6502 optimized with software stack")
|
||||
verifier(m3)
|
||||
}
|
||||
}
|
||||
|
||||
object EmuZ80BenchmarkRun {
|
||||
def apply(source: String)(verifier: MemoryBank => Unit): Unit = {
|
||||
val (Timings(t0, _), m0) = EmuUnoptimizedZ80Run.apply2(source)
|
||||
@ -90,6 +114,9 @@ object EmuCrossPlatformBenchmarkRun {
|
||||
if (platforms.contains(millfork.Cpu.Mos)) {
|
||||
EmuBenchmarkRun.apply(source)(verifier)
|
||||
}
|
||||
if (platforms.contains(millfork.Cpu.StrictMos)) {
|
||||
EmuBenchmarkRun.apply(source)(verifier)
|
||||
}
|
||||
if (platforms.contains(millfork.Cpu.Ricoh)) {
|
||||
verifier(EmuUndocumentedRun.apply(source))
|
||||
}
|
||||
|
@ -43,6 +43,25 @@ object EmuSizeOptimizedRun extends EmuRun(
|
||||
override def optimizeForSize = true
|
||||
}
|
||||
|
||||
object EmuOptimizedSoftwareStackRun extends EmuRun(
|
||||
Cpu.StrictMos,
|
||||
OptimizationPresets.NodeOpt,
|
||||
OptimizationPresets.AssOpt ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
OptimizationPresets.Good ++
|
||||
OptimizationPresets.Good ++
|
||||
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
|
||||
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
OptimizationPresets.Good ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
OptimizationPresets.Good ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
OptimizationPresets.Good ++
|
||||
OptimizationPresets.Good) {
|
||||
override def softwareStack = true
|
||||
}
|
||||
|
||||
|
||||
object EmuOptimizedZ80Run extends EmuZ80Run(Cpu.Z80, OptimizationPresets.NodeOpt, Z80OptimizationPresets.GoodForZ80)
|
||||
|
||||
|
@ -63,6 +63,8 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
|
||||
def optimizeForSize = false
|
||||
|
||||
def softwareStack = false
|
||||
|
||||
private val timingNmos = Array[Int](
|
||||
7, 6, 0, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6,
|
||||
2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,
|
||||
@ -131,6 +133,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
CompilationFlag.InlineFunctions -> this.inline,
|
||||
CompilationFlag.InterproceduralOptimization -> true,
|
||||
CompilationFlag.CompactReturnDispatchParams -> true,
|
||||
CompilationFlag.SoftwareStack -> softwareStack,
|
||||
CompilationFlag.EmitCmosOpcodes -> millfork.Cpu.CmosCompatible.contains(platform.cpu),
|
||||
CompilationFlag.EmitEmulation65816Opcodes -> (platform.cpu == millfork.Cpu.Sixteen),
|
||||
CompilationFlag.Emit65CE02Opcodes -> (platform.cpu == millfork.Cpu.CE02),
|
||||
|
Loading…
x
Reference in New Issue
Block a user