1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-08-12 11:29:20 +00:00

6502: Stack-related things:

– software variable stack
– fixes for handling stack variables
This commit is contained in:
Karol Stasiak 2018-12-14 22:01:52 +01:00
parent db1ce07ed6
commit c28b71add5
25 changed files with 527 additions and 248 deletions

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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:", "")

View File

@ -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)

View File

@ -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",

View File

@ -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")

View File

@ -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,

View File

@ -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)
}
)

View File

@ -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)
}),
)

View File

@ -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)
}

View File

@ -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)),
)

View File

@ -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)

View File

@ -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
}
}
}

View File

@ -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))
}
}

View File

@ -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 => ???
}
}

View File

@ -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
}
}

View File

@ -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)
}
}
}
}

View File

@ -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))

View File

@ -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)
}
}
}

View File

@ -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))
}

View File

@ -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)

View File

@ -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),