mirror of
https://github.com/KarolS/millfork.git
synced 2024-10-09 13:57:05 +00:00
Multiple improvements and fixes:
– reorganized code for future support of larger zeropage register sets – added stack-allocated variables for Z80 – added many stack-related optimizations for 6502 and Z80 – fixed flow analysis and optimization bugs for Z80 – flow analysis for stack-allocated variables on Z80 – added more optimizations for 6502 and Z80 – fixed IX/IY-indexed operations on Z80 – code cleanup
This commit is contained in:
parent
55b42645be
commit
a00ba49820
@ -5,7 +5,7 @@ import millfork.error.ErrorReporting
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
case class CompilationOptions(platform: Platform, commandLineFlags: Map[CompilationFlag.Value, Boolean], outputFileName: Option[String]) {
|
||||
case class CompilationOptions(platform: Platform, commandLineFlags: Map[CompilationFlag.Value, Boolean], outputFileName: Option[String], zpRegisterSize: Int) {
|
||||
|
||||
import CompilationFlag._
|
||||
import Cpu._
|
||||
@ -21,7 +21,7 @@ case class CompilationOptions(platform: Platform, commandLineFlags: Map[Compilat
|
||||
|
||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.M6502) invalids ++= Set(
|
||||
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
||||
ZeropagePseudoregister, PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, LUnixRelocatableCode, RorWarning)
|
||||
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, LUnixRelocatableCode, RorWarning)
|
||||
|
||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, UseIxForStack)
|
||||
|
||||
@ -30,6 +30,9 @@ case class CompilationOptions(platform: Platform, commandLineFlags: Map[Compilat
|
||||
if (invalids.nonEmpty) {
|
||||
ErrorReporting.error("Invalid flags enabled for the currect CPU family: " + invalids.mkString(", "))
|
||||
}
|
||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.M6502 && zpRegisterSize > 0) {
|
||||
ErrorReporting.error("Invalid flags enabled for the currect CPU family: zp_register" + invalids.mkString(", "))
|
||||
}
|
||||
CpuFamily.forType(platform.cpu) match {
|
||||
case CpuFamily.M6502 =>
|
||||
if (flags(DecimalMode)) {
|
||||
@ -133,7 +136,7 @@ object Cpu extends Enumeration {
|
||||
import CompilationFlag._
|
||||
|
||||
private val mosAlwaysDefaultFlags = Set(
|
||||
VariableOverlap, CompactReturnDispatchParams, ZeropagePseudoregister
|
||||
VariableOverlap, CompactReturnDispatchParams
|
||||
)
|
||||
|
||||
private val i8080AlwaysDefaultFlags = Set(
|
||||
@ -203,7 +206,7 @@ object CompilationFlag extends Enumeration {
|
||||
EmitIllegals, DecimalMode, ReadOnlyArrays,
|
||||
// compilation options for MOS:
|
||||
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
||||
ZeropagePseudoregister, PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator,
|
||||
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator,
|
||||
// compilation options for Z80
|
||||
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, UseIxForStack,
|
||||
// optimization options:
|
||||
@ -236,7 +239,6 @@ object CompilationFlag extends Enumeration {
|
||||
"ipo" -> InterproceduralOptimization,
|
||||
"inline" -> InlineFunctions,
|
||||
"dangerous_optimizations" -> DangerousOptimizations,
|
||||
"zeropage_register" -> ZeropagePseudoregister,
|
||||
"decimal_mode" -> DecimalMode,
|
||||
"ro_arrays" -> ReadOnlyArrays,
|
||||
"ror_warn" -> RorWarning,
|
||||
|
@ -24,6 +24,7 @@ case class Context(inputFileNames: List[String],
|
||||
outputFileName: Option[String] = None,
|
||||
runFileName: Option[String] = None,
|
||||
optimizationLevel: Option[Int] = None,
|
||||
zpRegisterSize: Option[Int] = None,
|
||||
platform: Option[String] = None,
|
||||
outputAssembly: Boolean = false,
|
||||
outputLabels: Boolean = false,
|
||||
@ -73,7 +74,7 @@ object Main {
|
||||
ErrorReporting.info("No platform selected, defaulting to `c64`")
|
||||
"c64"
|
||||
})
|
||||
val options = CompilationOptions(platform, c.flags, c.outputFileName)
|
||||
val options = CompilationOptions(platform, c.flags, c.outputFileName, c.zpRegisterSize.getOrElse(platform.zpRegisterSize))
|
||||
ErrorReporting.debug("Effective flags: ")
|
||||
options.flags.toSeq.sortBy(_._1).foreach{
|
||||
case (f, b) => ErrorReporting.debug(f" $f%-30s : $b%s")
|
||||
@ -155,7 +156,7 @@ object Main {
|
||||
}
|
||||
val callGraph = new StandardCallGraph(program)
|
||||
|
||||
val env = new Environment(None, "")
|
||||
val env = new Environment(None, "", platform.cpuFamily)
|
||||
env.collectDeclarations(program, options)
|
||||
|
||||
val assemblyOptimizations = optLevel match {
|
||||
@ -166,7 +167,7 @@ object Main {
|
||||
val goodExtras = List(
|
||||
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) SixteenOptimizations.AllForEmulation else Nil,
|
||||
if (options.flag(CompilationFlag.EmitNative65816Opcodes)) SixteenOptimizations.AllForNative else Nil,
|
||||
if (options.flag(CompilationFlag.ZeropagePseudoregister)) ZeropageRegisterOptimizations.All else Nil,
|
||||
if (options.zpRegisterSize > 0) ZeropageRegisterOptimizations.All else Nil
|
||||
).flatten
|
||||
val extras = List(
|
||||
if (options.flag(CompilationFlag.EmitIllegals)) UndocumentedOptimizations.All else Nil,
|
||||
@ -206,7 +207,7 @@ object Main {
|
||||
}
|
||||
val callGraph = new StandardCallGraph(program)
|
||||
|
||||
val env = new Environment(None, "")
|
||||
val env = new Environment(None, "", platform.cpuFamily)
|
||||
env.collectDeclarations(program, options)
|
||||
|
||||
val assemblyOptimizations = optLevel match {
|
||||
@ -305,7 +306,7 @@ object Main {
|
||||
c.changeFlag(CompilationFlag.EmitIllegals, v)
|
||||
}.description("Whether should emit illegal (undocumented) NMOS opcodes. Requires -O2 or higher to have an effect.")
|
||||
boolean("-fzp-register", "-fno-zp-register").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.ZeropagePseudoregister, v)
|
||||
c.copy(zpRegisterSize = Some(if (v) 2 else 0)) // TODO
|
||||
}.description("Whether should use 2 bytes of zeropage as a pseudoregister.")
|
||||
boolean("-fjmp-fix", "-fno-jmp-fix").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.PreventJmpIndirectBug, v)
|
||||
|
@ -81,7 +81,10 @@ object OptimizationPresets {
|
||||
AlwaysGoodOptimizations.PointlessStackStashing,
|
||||
AlwaysGoodOptimizations.PointlessStashingForLaterStore,
|
||||
AlwaysGoodOptimizations.PointlessStashingForLaterLoad,
|
||||
AlwaysGoodOptimizations.LoadingOfJustWrittenValue,
|
||||
AlwaysGoodOptimizations.PointlessStackStore,
|
||||
AlwaysGoodOptimizations.RearrangeMath,
|
||||
AlwaysGoodOptimizations.LoadingOfJustWrittenValue,
|
||||
EmptyMemoryStoreRemoval,
|
||||
AlwaysGoodOptimizations.PointlessLoadBeforeReturn,
|
||||
LaterOptimizations.PointessLoadingForShifting,
|
||||
@ -128,6 +131,8 @@ object OptimizationPresets {
|
||||
AlwaysGoodOptimizations.PointlessMathFromFlow,
|
||||
AlwaysGoodOptimizations.PointlessMathFromFlow,
|
||||
AlwaysGoodOptimizations.PointlessMathFromFlow,
|
||||
AlwaysGoodOptimizations.LoadingOfJustWrittenValue,
|
||||
AlwaysGoodOptimizations.PointlessStackStore,
|
||||
AlwaysGoodOptimizations.OptimizeZeroComparisons,
|
||||
AlwaysGoodOptimizations.SimplifiableCondition,
|
||||
AlwaysGoodOptimizations.IncrementingIndexRegistersAfterTransfer,
|
||||
@ -141,6 +146,8 @@ object OptimizationPresets {
|
||||
LaterOptimizations.IncreaseWithLimit,
|
||||
SingleAssignmentVariableOptimization,
|
||||
LocalVariableReadOptimization,
|
||||
AlwaysGoodOptimizations.PointlessStackStore,
|
||||
AlwaysGoodOptimizations.SimplifiableStackOperation,
|
||||
LaterOptimizations.UseBit,
|
||||
)
|
||||
|
||||
@ -167,6 +174,7 @@ object OptimizationPresets {
|
||||
AlwaysGoodOptimizations.IndexComparisonOptimization,
|
||||
AlwaysGoodOptimizations.IndexSequenceOptimization,
|
||||
AlwaysGoodOptimizations.InefficientStashingToRegister,
|
||||
AlwaysGoodOptimizations.LoadingOfJustWrittenValue,
|
||||
AlwaysGoodOptimizations.LoopInvariantRegister,
|
||||
LoopUnrolling.LoopUnrolling,
|
||||
AlwaysGoodOptimizations.MathOperationOnTwoIdenticalMemoryOperands,
|
||||
@ -192,6 +200,7 @@ object OptimizationPresets {
|
||||
AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeReturn,
|
||||
AlwaysGoodOptimizations.PointlessSignCheck,
|
||||
AlwaysGoodOptimizations.PointlessStackStashing,
|
||||
AlwaysGoodOptimizations.PointlessStackStore,
|
||||
AlwaysGoodOptimizations.PointlessStashingForLaterLoad,
|
||||
AlwaysGoodOptimizations.PointlessStashingForLaterStore,
|
||||
AlwaysGoodOptimizations.PointlessStashingToIndexOverShortSafeBranch,
|
||||
|
@ -23,6 +23,7 @@ class Platform(
|
||||
val outputPackager: OutputPackager,
|
||||
val codeAllocators: Map[String, UpwardByteAllocator],
|
||||
val variableAllocators: Map[String, VariableAllocator],
|
||||
val zpRegisterSize: Int,
|
||||
val freeZpPointers: List[Int],
|
||||
val fileExtension: String,
|
||||
val generateBbcMicroInfFile: Boolean,
|
||||
@ -87,6 +88,15 @@ object Platform {
|
||||
}
|
||||
}
|
||||
val startingModules = cs.get(classOf[String], "modules", "").split("[, ]+").filter(_.nonEmpty).toList
|
||||
val zpRegisterSize = cs.get(classOf[String], "zeropage_register", "2").toLowerCase match {
|
||||
case "" | null => if (CpuFamily.forType(cpu) == CpuFamily.M6502) 2 else 0
|
||||
case "true" | "on" | "yes" => 2
|
||||
case "false" | "off" | "no" | "0" => 0
|
||||
case x => x.toInt
|
||||
}
|
||||
if (zpRegisterSize < 0 || zpRegisterSize > 128) {
|
||||
ErrorReporting.error("Invalid zeropage register size: " + zpRegisterSize)
|
||||
}
|
||||
|
||||
val as = conf.getSection("allocation")
|
||||
|
||||
@ -178,6 +188,7 @@ object Platform {
|
||||
new Platform(cpu, flagOverrides, startingModules, outputPackager,
|
||||
codeAllocators.toMap,
|
||||
variableAllocators.toMap,
|
||||
zpRegisterSize,
|
||||
freePointers,
|
||||
if (fileExtension == "" || fileExtension.startsWith(".")) fileExtension else "." + fileExtension,
|
||||
generateBbcMicroInfFile,
|
||||
@ -188,7 +199,7 @@ object Platform {
|
||||
|
||||
def parseNumberOrRange(s:String): Seq[Int] = {
|
||||
if (s.contains("-")) {
|
||||
var segments = s.split("-")
|
||||
val segments = s.split("-")
|
||||
if (segments.length != 2) {
|
||||
ErrorReporting.fatal(s"Invalid range: `$s`")
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ object OpcodeClasses {
|
||||
)
|
||||
|
||||
val ConcernsStackAlways = ChangesStack ++ Set(TSX, TSY, TSC)
|
||||
val ConcernsS = ChangesS ++ Set(TSX, TSY, TSC)
|
||||
val ConcernsSAlways = ChangesS ++ Set(TSX, TSY, TSC)
|
||||
|
||||
val ChangesNAndZ = Set(
|
||||
ADC, AND, ASL, BIT, CMP, CPX, CPY, DEC, DEX, DEY, EOR, INC, INX, INY, LDA,
|
||||
|
@ -31,6 +31,13 @@ object AlwaysGoodOptimizations {
|
||||
(HasOpcode(LDA) & HasImmediate(0) & Elidable) ~
|
||||
(HasOpcode(CLC) & Elidable) ~
|
||||
(HasOpcode(ADC) & Elidable & DoesntMatterWhatItDoesWith(State.C, State.Z, State.V, State.N)) ~~> (code => code(2).copy(opcode = LDA) :: code.drop(3)),
|
||||
(HasOpcode(CLC) & Elidable) ~
|
||||
(HasOpcode(ADC) & MatchImmediate(0) & Elidable) ~
|
||||
(HasOpcode(CLC) & Elidable) ~
|
||||
(HasOpcode(ADC) & MatchImmediate(1) & Elidable & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~> ((code, ctx) => List(
|
||||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.immediate(ADC, (ctx.get[Constant](0) + ctx.get[Constant](1)).quickSimplify),
|
||||
)),
|
||||
)
|
||||
|
||||
val PointlessAccumulatorShifting = new RuleBasedAssemblyOptimization("Pointless accumulator shifting",
|
||||
@ -889,6 +896,91 @@ object AlwaysGoodOptimizations {
|
||||
},
|
||||
)
|
||||
|
||||
val LoadingOfJustWrittenValue = new RuleBasedAssemblyOptimization("Loading of just written value",
|
||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||
|
||||
(HasOpcode(STA) & XContainsStackPointer & 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) |
|
||||
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) =>
|
||||
val oldA = ctx.get[Int](2)
|
||||
val ADDR = ctx.get[Constant](1)
|
||||
val value = code.foldLeft(oldA) { (prev, line) =>
|
||||
line match {
|
||||
case AssemblyLine(INC, AbsoluteX, ADDR, _) => (prev + 1) & 0xff
|
||||
case AssemblyLine(DEC, AbsoluteX, ADDR, _) => (prev - 1) & 0xff
|
||||
case AssemblyLine(ASL, AbsoluteX, ADDR, _) => (prev << 1) & 0xff
|
||||
case AssemblyLine(LSR, AbsoluteX, ADDR, _) => (prev >> 1) & 0xff
|
||||
case _ => prev
|
||||
}
|
||||
}
|
||||
code.init :+ code.last.copy(addrMode = AddrMode.Immediate, parameter = NumericConstant(value, 1))
|
||||
},
|
||||
|
||||
(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))).* ~
|
||||
(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)).* ~
|
||||
(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))
|
||||
},
|
||||
|
||||
)
|
||||
|
||||
val PointlessStackStore = new RuleBasedAssemblyOptimization("Pointless stack store",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
|
||||
// TODO: check if JSR is OK
|
||||
|
||||
(Elidable & HasOpcode(STA) & HasAddrMode(AbsoluteX) & XContainsStackPointer & 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))).* ~
|
||||
HasOpcodeIn(RTS, RTI) ~~> (_.tail),
|
||||
|
||||
(Elidable & HasOpcode(STA) & HasAddrMode(AbsoluteX) & HasParameterWhere(_ match {
|
||||
case NumericConstant(addr, _) => addr >= 0x100 && addr <= 0x1ff
|
||||
case _ => false
|
||||
})) ~
|
||||
(HasOpcode(JSR) | Not(HasOpcodeIn(RTS, RTI)) & Linear & Not(HasAddrMode(AbsoluteX))).* ~
|
||||
HasOpcodeIn(RTS, RTI) ~~> (_.tail),
|
||||
|
||||
(Elidable & HasOpcodeIn(INC, DEC, ASL, LSR, ROR, ROL) & HasAddrMode(AbsoluteX) & HasParameterWhere(_ match {
|
||||
case NumericConstant(addr, _) => addr >= 0x100 && addr <= 0x1ff
|
||||
case _ => false
|
||||
}) & DoesntMatterWhatItDoesWith(State.Z, State.N, State.C)) ~
|
||||
(HasOpcode(JSR) | Not(HasOpcodeIn(RTS, RTI)) & Linear & Not(HasAddrMode(AbsoluteX))).* ~
|
||||
HasOpcodeIn(RTS, RTI) ~~> (_.tail),
|
||||
|
||||
(Elidable & HasOpcodeIn(STA, STA_W) & HasAddrMode(Stack)) ~
|
||||
(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 {
|
||||
case NumericConstant(addr, _) => addr >= 0x100 && addr <= 0x1ff
|
||||
case _ => false
|
||||
})) ~
|
||||
(HasOpcode(JSR) |
|
||||
Linear & XContainsStackPointer & 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),
|
||||
)
|
||||
|
||||
val IdempotentDuplicateRemoval = new RuleBasedAssemblyOptimization("Idempotent duplicate operation",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
HasOpcode(RTS) ~ NoopDiscardsFlags.* ~ (HasOpcode(RTS) ~ Elidable) ~~> (_.take(1)) ::
|
||||
@ -1252,6 +1344,17 @@ object AlwaysGoodOptimizations {
|
||||
(Elidable & HasOpcode(INX)) ~
|
||||
(Elidable & HasOpcode(TXS)) ~
|
||||
(ConcernsA & Not(ConcernsStack) & Linear & DoesntMatterWhatItDoesWith(State.Z, State.N, State.A)) ~~> (code => List(code.last, AssemblyLine.implied(PLA))),
|
||||
|
||||
(Elidable & HasOpcodeIn(PHA, PHX, PHY)).*.captureLength(0) ~
|
||||
(HasOpcode(TSX) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~
|
||||
(Linear & Not(ConcernsS) & Not(ConcernsX) & Not(ChangesStack)).* ~
|
||||
(Elidable & HasOpcode(INX)).*.captureLength(1) ~
|
||||
Where(ctx => ctx.get[Int](0) >= ctx.get[Int](1)) ~
|
||||
(HasOpcode(TXS) & DoesntMatterWhatItDoesWith(State.Z, State.N, State.X)) ~~> {(code, ctx) =>
|
||||
val pushCount = ctx.get[Int](0)
|
||||
val inxCount = ctx.get[Int](1)
|
||||
code.take(pushCount - inxCount) ++ code.drop(pushCount + 1).dropRight(inxCount + 1)
|
||||
},
|
||||
)
|
||||
|
||||
val SimplifiableBitOpsSequence = new RuleBasedAssemblyOptimization("Simplifiable sequence of bit operations",
|
||||
|
@ -90,7 +90,9 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
// case _ =>
|
||||
// }
|
||||
|
||||
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; A7=$a7,A0=$a0,NZ:$src"
|
||||
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; A7=$a7,A0=$a0,NZ:$src" +
|
||||
(if (eqSX) "; S=X"
|
||||
else /* */ " ")
|
||||
|
||||
def aw: Status[Int] = (ah, a) match {
|
||||
case (SingleStatus(h), SingleStatus(l)) => SingleStatus(h.&(0xff).<<(8).+(l&0xff))
|
||||
|
@ -1,6 +1,5 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.CompilationOptions
|
||||
import millfork.assembly.OptimizationContext
|
||||
import millfork.assembly.mos.{AssemblyLine, Opcode, State}
|
||||
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
|
||||
@ -12,6 +11,8 @@ import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
|
||||
class FlowHolder(_statusBefore: () => List[CpuStatus], _importanceAfter: () => List[CpuImportance]) {
|
||||
lazy val statusBefore: List[CpuStatus] = _statusBefore()
|
||||
lazy val importanceAfter: List[CpuImportance] = _importanceAfter()
|
||||
|
||||
def toString(index: Int): String = statusBefore(index).toString ++ " -> " ++ importanceAfter(index).toString
|
||||
}
|
||||
|
||||
case class FlowInfo(holder: FlowHolder, index: Int, _labelUseCountMap: () => Option[Map[String, Int]]) {
|
||||
@ -27,6 +28,8 @@ case class FlowInfo(holder: FlowHolder, index: Int, _labelUseCountMap: () => Opt
|
||||
def isUnimportant(state: State.Value): Boolean = importanceAfter.isUnimportant(state)
|
||||
|
||||
def labelUseCount(label: String): Int = labelUseCountMap.map(_.getOrElse(label, 0)).getOrElse(-1)
|
||||
|
||||
override def toString: String = holder.toString(index)
|
||||
}
|
||||
|
||||
object FlowAnalyzer {
|
||||
|
@ -96,7 +96,7 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
||||
|
||||
private val map = mutable.Map[Int, Any]()
|
||||
|
||||
override def toString: String = map.mkString(", ")
|
||||
override def toString: String = if (map.isEmpty) "<empty context>" else map.mkString(", ")
|
||||
|
||||
def addObject(i: Int, o: Any): Boolean = {
|
||||
if (map.contains(i)) {
|
||||
@ -774,6 +774,13 @@ case object ConcernsX extends TrivialAssemblyLinePattern {
|
||||
OpcodeClasses.ConcernsXAlways(line.opcode) || XAddrModes(line.addrMode)
|
||||
}
|
||||
|
||||
case object ConcernsS extends TrivialAssemblyLinePattern {
|
||||
val SAddrModes = Set(AddrMode.Stack, AddrMode.IndexedSY)
|
||||
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
OpcodeClasses.ConcernsSAlways(line.opcode) || SAddrModes(line.addrMode)
|
||||
}
|
||||
|
||||
case object ConcernsY extends TrivialAssemblyLinePattern {
|
||||
val YAddrModes = Set(AddrMode.AbsoluteY, AddrMode.IndexedSY, AddrMode.IndexedY, AddrMode.LongIndexedY, AddrMode.ZeroPageY)
|
||||
|
||||
|
@ -38,7 +38,7 @@ object SuperOptimizer extends AssemblyOptimization[AssemblyLine] {
|
||||
if (options.flag(CompilationFlag.Emit65CE02Opcodes)) {
|
||||
allOptimizers ++= CE02Optimizations.All
|
||||
}
|
||||
if (options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
if (options.zpRegisterSize > 0) {
|
||||
allOptimizers ++= ZeropageRegisterOptimizations.All
|
||||
}
|
||||
allOptimizers ++= List(
|
||||
|
@ -20,22 +20,56 @@ case class IfFlagSet(flag: ZFlag.Value) extends ZRegisters
|
||||
|
||||
case class IfFlagClear(flag: ZFlag.Value) extends ZRegisters
|
||||
|
||||
case class OneRegister(register: ZRegister.Value) extends ZRegisters
|
||||
case class OneRegister(register: ZRegister.Value) extends ZRegisters {
|
||||
if (register == ZRegister.MEM_IY_D || register == ZRegister.MEM_IX_D) ???
|
||||
}
|
||||
|
||||
case class TwoRegisters(target: ZRegister.Value, source: ZRegister.Value) extends ZRegisters
|
||||
case class TwoRegisters(target: ZRegister.Value, source: ZRegister.Value) extends ZRegisters {
|
||||
if (target == ZRegister.MEM_IY_D || target == ZRegister.MEM_IX_D) ???
|
||||
if (source == ZRegister.MEM_IY_D || source == ZRegister.MEM_IX_D) ???
|
||||
}
|
||||
|
||||
case class OneRegisterOffset(register: ZRegister.Value, offset: Int) extends ZRegisters {
|
||||
if (register != ZRegister.MEM_IY_D && register != ZRegister.MEM_IX_D) ???
|
||||
}
|
||||
|
||||
case class TwoRegistersOffset(target: ZRegister.Value, source: ZRegister.Value, offset: Int) extends ZRegisters {
|
||||
if (target != ZRegister.MEM_IY_D && target != ZRegister.MEM_IX_D && source != ZRegister.MEM_IY_D && source != ZRegister.MEM_IX_D) ???
|
||||
}
|
||||
|
||||
sealed abstract class LocalVariableAddressOperand(val memViaRegister: ZRegister.Value) {
|
||||
def offsetConstant: Constant
|
||||
def offset: Int
|
||||
def oneRegister: ZRegisters
|
||||
def asTargetWithSource(reg: ZRegister.Value): ZRegisters
|
||||
def asSourceWithTarget(reg: ZRegister.Value): ZRegisters
|
||||
}
|
||||
|
||||
case class LocalVariableAddressViaIX(offset: Int) extends LocalVariableAddressOperand(ZRegister.MEM_IX_D) {
|
||||
override def offsetConstant: Constant = NumericConstant(offset, 1)
|
||||
|
||||
override def oneRegister: ZRegisters = OneRegisterOffset(ZRegister.MEM_IX_D, offset)
|
||||
|
||||
override def asTargetWithSource(reg: ZRegister.Value): ZRegisters = TwoRegistersOffset(ZRegister.MEM_IX_D, reg, offset)
|
||||
|
||||
override def asSourceWithTarget(reg: ZRegister.Value): ZRegisters = TwoRegistersOffset(reg, ZRegister.MEM_IX_D, offset)
|
||||
}
|
||||
|
||||
case class LocalVariableAddressViaIY(offset: Int) extends LocalVariableAddressOperand(ZRegister.MEM_IY_D) {
|
||||
override def offsetConstant: Constant = NumericConstant(offset, 1)
|
||||
|
||||
override def oneRegister: ZRegisters = OneRegisterOffset(ZRegister.MEM_IY_D, offset)
|
||||
|
||||
override def asTargetWithSource(reg: ZRegister.Value): ZRegisters = TwoRegistersOffset(ZRegister.MEM_IY_D, reg, offset)
|
||||
|
||||
override def asSourceWithTarget(reg: ZRegister.Value): ZRegisters = TwoRegistersOffset(reg, ZRegister.MEM_IY_D, offset)
|
||||
}
|
||||
|
||||
case object LocalVariableAddressViaHL extends LocalVariableAddressOperand(ZRegister.MEM_HL) {
|
||||
override def offsetConstant: Constant = Constant.Zero
|
||||
override def offset: Int = 0
|
||||
|
||||
override def oneRegister: ZRegisters = OneRegister(ZRegister.MEM_HL)
|
||||
|
||||
override def asTargetWithSource(reg: ZRegister.Value): ZRegisters = TwoRegisters(ZRegister.MEM_HL, reg)
|
||||
|
||||
override def asSourceWithTarget(reg: ZRegister.Value): ZRegisters = TwoRegisters(reg, ZRegister.MEM_HL)
|
||||
}
|
||||
|
||||
object ZLine {
|
||||
@ -63,7 +97,7 @@ object ZLine {
|
||||
|
||||
def register(opcode: ZOpcode.Value, register: ZRegister.Value): ZLine = ZLine(opcode, OneRegister(register), Constant.Zero)
|
||||
|
||||
def register(opcode: ZOpcode.Value, register: LocalVariableAddressOperand): ZLine = ZLine(opcode, OneRegister(register.memViaRegister), register.offsetConstant)
|
||||
def register(opcode: ZOpcode.Value, register: LocalVariableAddressOperand): ZLine = ZLine(opcode, register.oneRegister, Constant.Zero)
|
||||
|
||||
def imm8(opcode: ZOpcode.Value, value: Constant): ZLine = ZLine(opcode, OneRegister(IMM_8), value)
|
||||
|
||||
@ -73,12 +107,16 @@ object ZLine {
|
||||
|
||||
def ld8(target: ZRegister.Value, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegisters(target, source), Constant.Zero)
|
||||
|
||||
def ld8(target: LocalVariableAddressOperand, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegisters(target.memViaRegister, source), target.offsetConstant)
|
||||
def ld8(target: LocalVariableAddressOperand, source: ZRegister.Value): ZLine = ZLine(LD, target.asTargetWithSource(source), Constant.Zero)
|
||||
|
||||
def ld8(target: ZRegister.Value, source: LocalVariableAddressOperand): ZLine = ZLine(LD, TwoRegisters(target, source.memViaRegister), source.offsetConstant)
|
||||
def ld8(target: ZRegister.Value, source: LocalVariableAddressOperand): ZLine = ZLine(LD, source.asSourceWithTarget(target), Constant.Zero)
|
||||
|
||||
def ld16(target: ZRegister.Value, source: ZRegister.Value): ZLine = ZLine(LD_16, TwoRegisters(target, source), Constant.Zero)
|
||||
|
||||
def ldImm8(target: LocalVariableAddressOperand, source: Int): ZLine = ZLine(LD, target.asTargetWithSource(IMM_8), NumericConstant(source & 0xff, 1))
|
||||
|
||||
def ldImm8(target: LocalVariableAddressOperand, source: Constant): ZLine = ZLine(LD, target.asTargetWithSource(IMM_8), source)
|
||||
|
||||
def ldImm8(target: ZRegister.Value, source: Int): ZLine = ZLine(LD, TwoRegisters(target, IMM_8), NumericConstant(source & 0xff, 1))
|
||||
|
||||
def ldImm8(target: ZRegister.Value, source: Constant): ZLine = ZLine(LD, TwoRegisters(target, IMM_8), source)
|
||||
@ -103,9 +141,9 @@ object ZLine {
|
||||
|
||||
def ldAbs16(target: Constant, source: ZRegister.Value): ZLine = ZLine(LD_16, TwoRegisters(MEM_ABS_16, source), target)
|
||||
|
||||
def ldViaIx(target: ZRegister.Value, sourceOffset: Int): ZLine = ZLine(LD, TwoRegisters(target, ZRegister.MEM_IX_D), NumericConstant(sourceOffset, 1))
|
||||
def ldViaIx(target: ZRegister.Value, sourceOffset: Int): ZLine = ZLine(LD, TwoRegistersOffset(target, ZRegister.MEM_IX_D, sourceOffset), Constant.Zero)
|
||||
|
||||
def ldViaIx(targetOffset: Int, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegisters(ZRegister.MEM_IX_D, source), NumericConstant(targetOffset, 1))
|
||||
def ldViaIx(targetOffset: Int, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegistersOffset(ZRegister.MEM_IX_D, source, targetOffset), Constant.Zero)
|
||||
}
|
||||
|
||||
case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Constant, elidable: Boolean = true) extends AbstractCode {
|
||||
@ -141,7 +179,7 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
||||
|
||||
override def isPrintable: Boolean = true
|
||||
|
||||
private def asAssemblyString(r: ZRegister.Value): String = r match {
|
||||
private def asAssemblyString(r: ZRegister.Value, offset: Int = 0): String = r match {
|
||||
case ZRegister.A => "A"
|
||||
case ZRegister.B => "B"
|
||||
case ZRegister.C => "C"
|
||||
@ -166,8 +204,8 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
||||
case ZRegister.MEM_ABS_16 => s"($parameter)"
|
||||
case ZRegister.IMM_8 => s"#$parameter"
|
||||
case ZRegister.IMM_16 => s"#$parameter"
|
||||
case ZRegister.MEM_IX_D => s"(IX,$parameter)"
|
||||
case ZRegister.MEM_IY_D => s"(IY,$parameter)"
|
||||
case ZRegister.MEM_IX_D => s"(IX,$offset)"
|
||||
case ZRegister.MEM_IY_D => s"(IY,$offset)"
|
||||
case ZRegister.MEM_HL => "(HL)"
|
||||
case ZRegister.MEM_BC => "(BC)"
|
||||
case ZRegister.MEM_DE => "(DE)"
|
||||
@ -199,8 +237,8 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
||||
case OneRegister(r) => s" (${asAssemblyString(r)})"
|
||||
}
|
||||
s" $opcode$ps"
|
||||
case o =>
|
||||
val os = o.toString//.stripSuffix("_16")
|
||||
case op =>
|
||||
val os = op.toString//.stripSuffix("_16")
|
||||
val ps = registers match {
|
||||
case NoRegisters => ""
|
||||
case IfFlagSet(ZFlag.P) => " PO"
|
||||
@ -211,6 +249,8 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
||||
case IfFlagClear(f) => s" N$f"
|
||||
case OneRegister(r) => s" ${asAssemblyString(r)}"
|
||||
case TwoRegisters(t, s) => s" ${asAssemblyString(t)},${asAssemblyString(s)}"
|
||||
case TwoRegistersOffset(t, s, o) => s" ${asAssemblyString(t, o)},${asAssemblyString(s, o)}"
|
||||
case OneRegisterOffset(r, o) => s" ${asAssemblyString(r, o)}"
|
||||
}
|
||||
s" $os$ps"
|
||||
}
|
||||
@ -294,6 +334,26 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
||||
}
|
||||
}
|
||||
|
||||
def changesRegisterAndOffset(r: ZRegister.Value, o: Int): Boolean = {
|
||||
import ZOpcode._
|
||||
import ZRegister._
|
||||
r match {
|
||||
case MEM_IX_D | MEM_IY_D =>
|
||||
opcode match {
|
||||
case LD => registers match {
|
||||
case TwoRegistersOffset(s, _, p) => r == s && o == p
|
||||
case _ => false
|
||||
}
|
||||
case INC | DEC | RL | RLC | RR | RRC | SLA | SLL | SRA | SRL => registers match {
|
||||
case OneRegisterOffset(s, p) => r == s && o == p
|
||||
case _ => false
|
||||
}
|
||||
case _ => false // TODO
|
||||
}
|
||||
case _ => changesRegister(r)
|
||||
}
|
||||
}
|
||||
|
||||
def changesRegister(r: ZRegister.Value): Boolean = {
|
||||
import ZOpcode._
|
||||
import ZRegister._
|
||||
@ -304,7 +364,8 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
||||
case IX => changesRegister(IXH) || changesRegister(IXL)
|
||||
case IY => changesRegister(IYH) || changesRegister(IYL)
|
||||
case AF => ???
|
||||
case MEM_ABS_8 | MEM_ABS_16 | IMM_8 | IMM_16 | MEM_DE | MEM_HL | MEM_BC | MEM_IX_D | MEM_IY_D | SP => ???
|
||||
case IMM_8 | IMM_16 => false
|
||||
case MEM_ABS_8 | MEM_ABS_16 | MEM_DE | MEM_HL | MEM_BC | MEM_IX_D | MEM_IY_D | SP => ???
|
||||
case _ =>
|
||||
opcode match {
|
||||
case LD => registers match {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package millfork.assembly.z80.opt
|
||||
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZLine, ZOpcode}
|
||||
import millfork.assembly.z80._
|
||||
import millfork.assembly.z80.ZOpcode._
|
||||
import millfork.env.{Constant, NumericConstant}
|
||||
import millfork.node.ZRegister
|
||||
@ -11,6 +11,13 @@ import millfork.node.ZRegister
|
||||
*/
|
||||
object AlwaysGoodZ80Optimizations {
|
||||
|
||||
def change8BitLoadTarget(line: ZLine, newTarget: ZRegister.Value): ZLine = {
|
||||
line match {
|
||||
case ZLine(LD, TwoRegistersOffset(_, s, o), p, _) => ZLine(LD, TwoRegistersOffset(newTarget, s, o), p)
|
||||
case ZLine(LD, TwoRegisters(_, s), p, _) => ZLine(LD, TwoRegisters(newTarget, s), p)
|
||||
}
|
||||
}
|
||||
|
||||
def for7Registers(f: ZRegister.Value => AssemblyRuleSet) = MultipleAssemblyRules(
|
||||
List(ZRegister.A, ZRegister.B, ZRegister.C, ZRegister.D, ZRegister.E, ZRegister.H, ZRegister.L).map(f))
|
||||
|
||||
@ -26,9 +33,23 @@ object AlwaysGoodZ80Optimizations {
|
||||
(Elidable & IsRegular8BitLoadFrom(register) & MatchRegister(register, 0)) ~~> ((code, ctx) =>
|
||||
code.map(x => x.copy(
|
||||
parameter = NumericConstant(ctx.get[Int](0), 1),
|
||||
registers = x.registers.asInstanceOf[TwoRegisters].copy(source = ZRegister.IMM_8)
|
||||
registers = x.registers match {
|
||||
case TwoRegisters(t, _) => TwoRegisters(t, ZRegister.IMM_8)
|
||||
case TwoRegistersOffset(t@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), _, o) => TwoRegistersOffset(t, ZRegister.IMM_8, o)
|
||||
case TwoRegistersOffset(t, ZRegister.MEM_IX_D | ZRegister.MEM_IY_D, _) => TwoRegisters(t, ZRegister.IMM_8)
|
||||
}
|
||||
)))
|
||||
),
|
||||
(Elidable & MatchSourceIxOffsetOf8BitLoad(0) & MatchValueAtMatchedIxOffset(0, 1)) ~~> ((code, ctx) =>
|
||||
code.map(x => x.copy(
|
||||
parameter = NumericConstant(ctx.get[Int](1), 1),
|
||||
registers = x.registers match {
|
||||
case TwoRegisters(t, _) => TwoRegisters(t, ZRegister.IMM_8)
|
||||
case TwoRegistersOffset(t@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), _, o) => TwoRegistersOffset(t, ZRegister.IMM_8, o)
|
||||
case TwoRegistersOffset(t, ZRegister.MEM_IX_D | ZRegister.MEM_IY_D, _) => TwoRegisters(t, ZRegister.IMM_8)
|
||||
}
|
||||
))
|
||||
),
|
||||
for6Registers(register =>
|
||||
(Elidable & HasRegisterParam(register) & HasOpcodeIn(Set(AND, ADD, ADC, SUB, SBC, XOR, OR, CP)) & MatchRegister(register, 0)) ~~> ((code, ctx) =>
|
||||
code.map(x => x.copy(
|
||||
@ -36,6 +57,12 @@ object AlwaysGoodZ80Optimizations {
|
||||
registers = OneRegister(ZRegister.IMM_8)
|
||||
)))
|
||||
),
|
||||
(Elidable & MatchIxOffset(0) & HasOpcodeIn(Set(AND, ADD, ADC, SUB, SBC, XOR, OR, CP)) & MatchValueAtMatchedIxOffset(0, 1)) ~~> ((code, ctx) =>
|
||||
code.map(x => x.copy(
|
||||
parameter = NumericConstant(ctx.get[Int](1), 1),
|
||||
registers = OneRegister(ZRegister.IMM_8)
|
||||
))
|
||||
),
|
||||
)
|
||||
|
||||
val ReloadingKnownValueFromMemory = new RuleBasedAssemblyOptimization("Reloading known value from memory",
|
||||
@ -88,6 +115,28 @@ object AlwaysGoodZ80Optimizations {
|
||||
(Elidable & Is8BitLoadTo(ZRegister.MEM_BC)) ~
|
||||
(Linear & Not(ConcernsMemory) & Not(Changes(ZRegister.BC))).* ~
|
||||
Is8BitLoadTo(ZRegister.MEM_BC) ~~> (_.tail),
|
||||
|
||||
(Elidable & MatchTargetIxOffsetOf8BitLoad(0) & MatchUnimportantIxOffset(0)) ~~> (_ => Nil),
|
||||
|
||||
for7Registers(register =>
|
||||
(Elidable & Is8BitLoadTo(register) & NoOffset & MatchSourceRegisterAndOffset(1)) ~
|
||||
(Linear & Not(Concerns(register)) & DoesntChangeMatchedRegisterAndOffset(1)).* ~
|
||||
(Elidable & IsRegular8BitLoadFrom(register) & DoesntMatterWhatItDoesWith(register)) ~~> { code =>
|
||||
val last = code.last
|
||||
val head = code.head
|
||||
code.tail.init :+ ZLine(LD, (last.registers, head.registers) match {
|
||||
case (TwoRegisters(t, _), TwoRegisters(_, s)) => TwoRegisters(t, s)
|
||||
case (TwoRegistersOffset(t, _, o), TwoRegisters(_, s)) => TwoRegistersOffset(t, s, o)
|
||||
case (TwoRegisters(t, _), TwoRegistersOffset(_, s, o)) => TwoRegistersOffset(t, s, o)
|
||||
case _ => ???
|
||||
}, head.parameter)
|
||||
}
|
||||
),
|
||||
|
||||
for7Registers(register =>
|
||||
(Elidable & Is8BitLoad(register, register)) ~~> (_ => Nil)
|
||||
),
|
||||
|
||||
)
|
||||
|
||||
private def simplifiable16BitAddWithSplitTarget(targetH: ZRegister.Value, targetL: ZRegister.Value, target: ZRegister.Value, source: ZRegister.Value) = MultipleAssemblyRules(List(
|
||||
@ -137,7 +186,7 @@ object AlwaysGoodZ80Optimizations {
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
for6Registers(register =>
|
||||
(Elidable & HasOpcode(ADD) & MatchRegister(ZRegister.A, 0) & HasRegisterParam(register) & MatchRegister(register, 1) &
|
||||
DoesntMatterWhatItDoesWithFlags) ~~> ((code, ctx) => List(ZLine.ldImm16(ZRegister.A, (ctx.get[Int](0) + ctx.get[Int](1)) & 0xff))),
|
||||
DoesntMatterWhatItDoesWithFlags) ~~> ((code, ctx) => List(ZLine.ldImm8(ZRegister.A, (ctx.get[Int](0) + ctx.get[Int](1)) & 0xff))),
|
||||
),
|
||||
simplifiable16BitAddWithSplitTarget(ZRegister.H, ZRegister.L, ZRegister.HL, ZRegister.BC),
|
||||
simplifiable16BitAddWithSplitTarget(ZRegister.H, ZRegister.L, ZRegister.HL, ZRegister.DE),
|
||||
@ -145,6 +194,29 @@ object AlwaysGoodZ80Optimizations {
|
||||
simplifiable16BitAddWithSplitTarget(ZRegister.IXH, ZRegister.IXL, ZRegister.IX, ZRegister.DE),
|
||||
simplifiable16BitAddWithSplitTarget(ZRegister.IYH, ZRegister.IYL, ZRegister.IY, ZRegister.BC),
|
||||
simplifiable16BitAddWithSplitTarget(ZRegister.IYH, ZRegister.IYL, ZRegister.IY, ZRegister.DE),
|
||||
|
||||
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
|
||||
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L)) ~
|
||||
(Elidable & Is8BitLoadTo(ZRegister.L)) ~
|
||||
(Elidable & Is8BitLoadTo(ZRegister.H)) ~
|
||||
(HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & DoesntMatterWhatItDoesWith(ZRegister.D, ZRegister.E)) ~~> { code =>
|
||||
List(
|
||||
change8BitLoadTarget(code(2), ZRegister.E),
|
||||
change8BitLoadTarget(code(3), ZRegister.D),
|
||||
code.last)
|
||||
},
|
||||
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
|
||||
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L)) ~
|
||||
(Elidable & Is8BitLoadTo(ZRegister.H)) ~
|
||||
(Elidable & Is8BitLoadTo(ZRegister.L)) ~
|
||||
(HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & DoesntMatterWhatItDoesWith(ZRegister.D, ZRegister.E)) ~~> { code =>
|
||||
List(
|
||||
change8BitLoadTarget(code(2), ZRegister.D),
|
||||
change8BitLoadTarget(code(3), ZRegister.E),
|
||||
code.last)
|
||||
},
|
||||
|
||||
(Elidable & HasOpcodeIn(Set(ADD, OR, AND, XOR, SUB)) & Has8BitImmediate(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
|
||||
)
|
||||
|
||||
val FreeHL = new RuleBasedAssemblyOptimization("Free HL",
|
||||
|
@ -1,10 +1,10 @@
|
||||
package millfork.assembly.z80.opt
|
||||
|
||||
import millfork.assembly.opt.{AnyStatus, SingleStatus}
|
||||
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZLine, ZOpcodeClasses}
|
||||
import millfork.assembly.z80._
|
||||
import millfork.env.{Label, MemoryAddressConstant, NormalFunction, NumericConstant}
|
||||
import millfork.node.ZRegister
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.CompilationOptions
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
@ -12,8 +12,6 @@ import millfork.{CompilationFlag, CompilationOptions}
|
||||
object CoarseFlowAnalyzer {
|
||||
|
||||
def analyze(f: NormalFunction, code: List[ZLine], compilationOptions: CompilationOptions): List[CpuStatus] = {
|
||||
val ceFlag = compilationOptions.flag(CompilationFlag.Emit65CE02Opcodes)
|
||||
val cmosFlag = compilationOptions.flag(CompilationFlag.EmitCmosOpcodes)
|
||||
val initialStatus = CpuStatus()
|
||||
val functionStartStatus = CpuStatus()
|
||||
val emptyStatus = CpuStatus()
|
||||
@ -39,8 +37,11 @@ object CoarseFlowAnalyzer {
|
||||
case _ => None
|
||||
}).fold(currentStatus)(_ ~ _)
|
||||
|
||||
case ZLine(CALL | BYTE, _, _, _) =>
|
||||
case ZLine(CALL, _, _, _) =>
|
||||
currentStatus = initialStatus.copy(memIx = currentStatus.memIx)
|
||||
case ZLine(BYTE, _, _, _) =>
|
||||
currentStatus = initialStatus
|
||||
|
||||
case ZLine(ADD, OneRegister(s), _, _) =>
|
||||
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s)) ((m, n) => (m + n) & 0xff),
|
||||
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||
@ -56,10 +57,34 @@ object CoarseFlowAnalyzer {
|
||||
case ZLine(XOR, OneRegister(s), _, _) =>
|
||||
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s)) ((m, n) => (m ^ n) & 0xff),
|
||||
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||
|
||||
case ZLine(ADD, OneRegisterOffset(s, o), _, _) =>
|
||||
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m + n) & 0xff),
|
||||
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||
case ZLine(SUB, OneRegisterOffset(s, o), _, _) =>
|
||||
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m - n) & 0xff),
|
||||
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||
case ZLine(AND, OneRegisterOffset(s, o), _, _) =>
|
||||
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m & n) & 0xff),
|
||||
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||
case ZLine(OR, OneRegisterOffset(s, o), _, _) =>
|
||||
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m | n) & 0xff),
|
||||
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||
case ZLine(XOR, OneRegisterOffset(s, o), _, _) =>
|
||||
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m ^ n) & 0xff),
|
||||
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||
|
||||
case ZLine(CP, _, _, _) =>
|
||||
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||
|
||||
case ZLine(LD, TwoRegisters(t, ZRegister.IMM_8), NumericConstant(value, _), _) =>
|
||||
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt))
|
||||
case ZLine(LD, TwoRegistersOffset(t, ZRegister.IMM_8, o), NumericConstant(value, _), _) =>
|
||||
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt), o)
|
||||
case ZLine(LD | LD_16, TwoRegisters(t, s), _, _) =>
|
||||
currentStatus = currentStatus.setRegister(t, currentStatus.getRegister(s))
|
||||
case ZLine(LD | LD_16, TwoRegistersOffset(t, s, o), _, _) =>
|
||||
currentStatus = currentStatus.setRegister(t, currentStatus.getRegister(s, o), o)
|
||||
case ZLine(ADD_16, TwoRegisters(t, s), _, _) =>
|
||||
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||
.setRegister(t, (currentStatus.getRegister(t) <*> currentStatus.getRegister(s)) ((m, n) => (m + n) & 0xffff))
|
||||
@ -71,6 +96,8 @@ object CoarseFlowAnalyzer {
|
||||
registers match {
|
||||
case OneRegister(r) => currentStatus = currentStatus.setRegister(r, AnyStatus)
|
||||
case TwoRegisters(r1, r2) => currentStatus = currentStatus.setRegister(r1, AnyStatus).setRegister(r2, AnyStatus)
|
||||
case OneRegisterOffset(r1, o) => currentStatus = currentStatus.setRegister(r1, AnyStatus, o)
|
||||
case TwoRegistersOffset(r1, r2, o) => currentStatus = currentStatus.setRegister(r1, AnyStatus, o).setRegister(r2, AnyStatus, o)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
//noinspection RedundantNewCaseClass
|
||||
package millfork.assembly.z80.opt
|
||||
|
||||
import millfork.assembly.mos.State
|
||||
import millfork.assembly.opt._
|
||||
import millfork.assembly.z80.ZFlag
|
||||
import millfork.node.ZRegister
|
||||
@ -21,6 +20,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
ixl: Status[Int] = UnknownStatus,
|
||||
iyh: Status[Int] = UnknownStatus,
|
||||
iyl: Status[Int] = UnknownStatus,
|
||||
memIx: Map[Int, Status[Int]] = Map(),
|
||||
zf: Status[Boolean] = UnknownStatus,
|
||||
nf: Status[Boolean] = UnknownStatus,
|
||||
cf: Status[Boolean] = UnknownStatus,
|
||||
@ -28,7 +28,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
pf: Status[Boolean] = UnknownStatus,
|
||||
hf: Status[Boolean] = UnknownStatus
|
||||
) {
|
||||
def setRegister(target: ZRegister.Value, value: Status[Int]): CpuStatus = target match {
|
||||
def setRegister(target: ZRegister.Value, value: Status[Int], offset: Int = -1): CpuStatus = target match {
|
||||
case ZRegister.IMM_8 => this
|
||||
case ZRegister.IMM_16 => this
|
||||
case ZRegister.A => this.copy(a = value)
|
||||
@ -42,7 +42,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
case ZRegister.IXL => this.copy(ixl = value)
|
||||
case ZRegister.IYH => this.copy(iyh = value)
|
||||
case ZRegister.IYL => this.copy(iyl = value)
|
||||
case ZRegister.MEM_IX_D => this
|
||||
case ZRegister.MEM_IX_D => if (offset < 0) this.copy(memIx = Map()) else this.copy(memIx = this.memIx + (offset -> value))
|
||||
case ZRegister.MEM_IY_D => this
|
||||
case ZRegister.MEM_HL => this
|
||||
case ZRegister.MEM_BC => this
|
||||
@ -62,7 +62,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
|
||||
val mergeBytes: (Int, Int) => Int = (h, l) => (h & 0xff) * 256 + (l & 0xff)
|
||||
|
||||
def getRegister(register: ZRegister.Value): Status[Int] = register match {
|
||||
def getRegister(register: ZRegister.Value, offset: Int = -1): Status[Int] = register match {
|
||||
case ZRegister.A => a
|
||||
case ZRegister.B => b
|
||||
case ZRegister.C => c
|
||||
@ -74,7 +74,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
case ZRegister.IXL => ixl
|
||||
case ZRegister.IYH => iyh
|
||||
case ZRegister.IYL => iyl
|
||||
case ZRegister.MEM_IX_D => AnyStatus
|
||||
case ZRegister.MEM_IX_D => if (offset < 0) AnyStatus else memIx.getOrElse(offset, UnknownStatus)
|
||||
case ZRegister.MEM_IY_D => AnyStatus
|
||||
case ZRegister.MEM_HL => AnyStatus
|
||||
case ZRegister.MEM_BC => AnyStatus
|
||||
@ -114,6 +114,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
ixl = this.ixl ~ that.ixl,
|
||||
iyh = this.iyh ~ that.iyh,
|
||||
iyl = this.iyl ~ that.iyl,
|
||||
memIx = (this.memIx.keySet | that.memIx.keySet).map(k => k -> (this.memIx.getOrElse(k, UnknownStatus) ~ that.memIx.getOrElse(k, UnknownStatus))).toMap,
|
||||
nf = this.nf ~ that.nf,
|
||||
sf = this.sf ~ that.sf,
|
||||
zf = this.zf ~ that.zf,
|
||||
@ -123,6 +124,9 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
)
|
||||
|
||||
|
||||
override def toString: String = {
|
||||
val memRepr = if (memIx.isEmpty) "" else (0 to memIx.keys.max).map(i => memIx.getOrElse(i, UnknownStatus)).mkString("")
|
||||
s"A=$a,B=$b,C=$c,D=$d,E=$e,H=$h,L=$l,IX=$ixh$ixl,Y=$iyh$iyl; Z=$zf,C=$cf,N=$nf,S=$sf,P=$pf,H=$hf; M=" + memRepr.padTo(4, ' ')
|
||||
}
|
||||
|
||||
override def toString: String = s"A=$a,B=$b,C=$c,D=$d,E=$e,H=$h,L=$l,IX=$ixh$ixl,Y=$iyh$iyl; Z=$zf,C=$cf,N=$nf,S=$sf,P=$pf,H=$hf"
|
||||
}
|
@ -11,6 +11,8 @@ import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
|
||||
class FlowHolder(_statusBefore: () => List[CpuStatus], _importanceAfter: () => List[CpuImportance]) {
|
||||
lazy val statusBefore: List[CpuStatus] = _statusBefore()
|
||||
lazy val importanceAfter: List[CpuImportance] = _importanceAfter()
|
||||
|
||||
def toString(index: Int): String = statusBefore(index).toString ++ " -> " ++ importanceAfter(index).toString
|
||||
}
|
||||
|
||||
case class FlowInfo(holder: FlowHolder, index: Int, _labelUseCountMap: () => Option[Map[String, Int]]) {
|
||||
@ -20,6 +22,8 @@ case class FlowInfo(holder: FlowHolder, index: Int, _labelUseCountMap: () => Opt
|
||||
lazy val labelUseCountMap: Option[Map[String, Int]] = _labelUseCountMap()
|
||||
|
||||
def labelUseCount(label: String): Int = labelUseCountMap.map(_.getOrElse(label, 0)).getOrElse(-1)
|
||||
|
||||
override def toString: String = holder.toString(index)
|
||||
}
|
||||
|
||||
object FlowAnalyzer {
|
||||
|
@ -40,6 +40,7 @@ case class CpuImportance(a: Importance = UnknownImportance,
|
||||
ixl: Importance = UnknownImportance,
|
||||
iyh: Importance = UnknownImportance,
|
||||
iyl: Importance = UnknownImportance,
|
||||
memIx: Map[Int, Importance] = Map(),
|
||||
zf: Importance = UnknownImportance,
|
||||
nf: Importance = UnknownImportance,
|
||||
cf: Importance = UnknownImportance,
|
||||
@ -47,7 +48,10 @@ case class CpuImportance(a: Importance = UnknownImportance,
|
||||
pf: Importance = UnknownImportance,
|
||||
hf: Importance = UnknownImportance
|
||||
) {
|
||||
override def toString: String = s"A=$a,B=$b,C=$c,D=$d,E=$e,H=$h,L=$l,IX=$ixh$ixl,Y=$iyh$iyl; Z=$zf,C=$cf,N=$nf,S=$sf,P=$pf,H=$hf"
|
||||
override def toString: String = {
|
||||
val memRepr = if (memIx.isEmpty) "" else (0 to memIx.keys.max).map(i => memIx.getOrElse(i, UnknownImportance)).mkString("")
|
||||
s"A=$a,B=$b,C=$c,D=$d,E=$e,H=$h,L=$l,IX=$ixh$ixl,Y=$iyh$iyl; Z=$zf,C=$cf,N=$nf,S=$sf,P=$pf,H=$hf; M=" ++ memRepr.padTo(4, ' ')
|
||||
}
|
||||
|
||||
def ~(that: CpuImportance) = new CpuImportance(
|
||||
a = this.a ~ that.a,
|
||||
@ -61,6 +65,7 @@ case class CpuImportance(a: Importance = UnknownImportance,
|
||||
ixl = this.ixl ~ that.ixl,
|
||||
iyh = this.iyh ~ that.iyh,
|
||||
iyl = this.iyl ~ that.iyl,
|
||||
memIx = (this.memIx.keySet | that.memIx.keySet).map(k => k -> (this.memIx.getOrElse(k, UnknownImportance) ~ that.memIx.getOrElse(k, UnknownImportance))).toMap,
|
||||
zf = this.zf ~ that.zf,
|
||||
nf = this.nf ~ that.nf,
|
||||
cf = this.cf ~ that.cf,
|
||||
@ -68,7 +73,7 @@ case class CpuImportance(a: Importance = UnknownImportance,
|
||||
hf = this.hf ~ that.hf,
|
||||
)
|
||||
|
||||
def getRegister(register: ZRegister.Value): Importance = register match {
|
||||
def getRegister(register: ZRegister.Value, offset: Int = -1): Importance = register match {
|
||||
case ZRegister.A => a
|
||||
case ZRegister.B => b
|
||||
case ZRegister.C => c
|
||||
@ -80,6 +85,7 @@ case class CpuImportance(a: Importance = UnknownImportance,
|
||||
case ZRegister.IXL => ixl
|
||||
case ZRegister.IYH => iyh
|
||||
case ZRegister.IYL => iyl
|
||||
case ZRegister.MEM_IX_D => if (offset < 0) ??? else memIx.getOrElse(offset, UnknownImportance)
|
||||
case ZRegister.HL => h ~ l
|
||||
case ZRegister.BC => b ~ c
|
||||
case ZRegister.DE => d ~ e
|
||||
@ -96,7 +102,7 @@ case class CpuImportance(a: Importance = UnknownImportance,
|
||||
case ZFlag.N => nf
|
||||
}
|
||||
|
||||
def butReadsRegister(r: ZRegister.Value) = r match {
|
||||
def butReadsRegister(r: ZRegister.Value, offset: Int = -1): CpuImportance = r match {
|
||||
case ZRegister.A => this.copy(a = Important)
|
||||
case ZRegister.AF => this.copy(a = Important, zf = Important, pf = Important, hf = Important, cf = Important, sf = Important)
|
||||
case ZRegister.B => this.copy(b = Important)
|
||||
@ -112,12 +118,13 @@ case class CpuImportance(a: Importance = UnknownImportance,
|
||||
case ZRegister.IXL => this.copy(ixl = Important)
|
||||
case ZRegister.IYH => this.copy(iyh = Important)
|
||||
case ZRegister.IYL => this.copy(iyl = Important)
|
||||
case ZRegister.IX | ZRegister.MEM_IX_D => this.copy(ixh = Important, ixl = Important)
|
||||
case ZRegister.IX => this.copy(ixh = Important, ixl = Important)
|
||||
case ZRegister.MEM_IX_D => this.copy(ixh = Important, ixl = Important, memIx = if (offset < 0) memIx.mapValues(_ => Important) else memIx + (offset -> Important))
|
||||
case ZRegister.IY | ZRegister.MEM_IY_D => this.copy(iyh = Important, iyl = Important)
|
||||
case _ => this
|
||||
}
|
||||
|
||||
def butWritesRegister(r: ZRegister.Value): CpuImportance = r match {
|
||||
def butWritesRegister(r: ZRegister.Value, offset: Int = -1): CpuImportance = r match {
|
||||
case ZRegister.A => this.copy(a = Unimportant)
|
||||
case ZRegister.AF => this.copy(a = Unimportant, zf = Unimportant, pf = Unimportant, hf = Unimportant, cf = Unimportant, sf = Unimportant, nf = Unimportant)
|
||||
case ZRegister.B => this.copy(b = Unimportant)
|
||||
@ -136,8 +143,8 @@ case class CpuImportance(a: Importance = UnknownImportance,
|
||||
case ZRegister.IXL => this.copy(ixl = Unimportant)
|
||||
case ZRegister.IYH => this.copy(iyh = Unimportant)
|
||||
case ZRegister.IYL => this.copy(iyl = Unimportant)
|
||||
case ZRegister.IX => this.copy(ixh = Unimportant, ixl = Unimportant)
|
||||
case ZRegister.MEM_IX_D => this.copy(ixh = Important, ixl = Important)
|
||||
case ZRegister.IX => this.copy(ixh = Unimportant, ixl = Unimportant, memIx = memIx.mapValues(_ => Unimportant))
|
||||
case ZRegister.MEM_IX_D => this.copy(ixh = Important, ixl = Important, memIx = if (offset < 0) Map() else memIx + (offset -> Unimportant))
|
||||
case ZRegister.IY => this.copy(iyh = Important, iyl = Important)
|
||||
case ZRegister.MEM_IY_D => this.copy(iyh = Important, iyl = Important)
|
||||
case _ => this
|
||||
@ -208,22 +215,38 @@ object ReverseFlowAnalyzer {
|
||||
currentImportance = currentImportance.copy(a = Unimportant)
|
||||
case ZLine(DISCARD_F, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(cf = Unimportant, zf= Unimportant, sf = Unimportant , pf = Unimportant, hf = Unimportant)
|
||||
case ZLine(LD, TwoRegistersOffset(t, s, o), _, _) =>
|
||||
currentImportance = currentImportance.butWritesRegister(t, o).butReadsRegister(s, o)
|
||||
case ZLine(LD | LD_16, TwoRegisters(t, s), _, _) =>
|
||||
currentImportance = currentImportance.butWritesRegister(t).butReadsRegister(s)
|
||||
case ZLine(ADD_16, TwoRegisters(t, s), _, _) =>
|
||||
currentImportance = currentImportance.butReadsRegister(t).butReadsRegister(s)
|
||||
|
||||
case ZLine(XOR, OneRegister(ZRegister.A), _, _) =>
|
||||
currentImportance = currentImportance.butWritesRegister(ZRegister.A)
|
||||
case ZLine(OR | AND, OneRegister(ZRegister.A), _, _) =>
|
||||
currentImportance = currentImportance.butReadsRegister(ZRegister.A)
|
||||
|
||||
case ZLine(AND | ADD | SUB | OR | XOR | CP, OneRegister(s), _, _) =>
|
||||
currentImportance = currentImportance.butReadsRegister(ZRegister.A).butReadsRegister(s)
|
||||
case ZLine(ADC | SBC, OneRegister(s), _, _) =>
|
||||
currentImportance = currentImportance.butReadsRegister(ZRegister.A).butReadsRegister(s).butReadsFlag(ZFlag.C)
|
||||
case ZLine(DAA, _, _, _) =>
|
||||
currentImportance = currentImportance.butReadsRegister(ZRegister.A).butReadsFlag(ZFlag.H)
|
||||
|
||||
case ZLine(AND | ADD | SUB | OR | XOR | CP, OneRegisterOffset(s, o), _, _) =>
|
||||
currentImportance = currentImportance.butReadsRegister(ZRegister.A).butReadsRegister(s, o)
|
||||
case ZLine(ADC | SBC, OneRegisterOffset(s, o), _, _) =>
|
||||
currentImportance = currentImportance.butReadsRegister(ZRegister.A).butReadsRegister(s, o).butReadsFlag(ZFlag.C)
|
||||
|
||||
case ZLine(INC | DEC | INC_16 | DEC_16, OneRegister(s), _, _) =>
|
||||
currentImportance = currentImportance.butReadsRegister(s)
|
||||
case ZLine(INC | DEC | INC_16 | DEC_16, OneRegisterOffset(s, o), _, _) =>
|
||||
currentImportance = currentImportance.butReadsRegister(s, o)
|
||||
case ZLine(POP, OneRegister(r), _, _) =>
|
||||
currentImportance = currentImportance.butWritesRegister(r)
|
||||
case ZLine(PUSH, OneRegister(r), _, _) =>
|
||||
currentImportance = currentImportance.butReadsRegister(r)
|
||||
case ZLine(CALL, NoRegisters, _, _) =>
|
||||
currentImportance = finalImportance.copy(memIx = currentImportance.memIx)
|
||||
case _ =>
|
||||
currentImportance = finalImportance // TODO
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package millfork.assembly.z80.opt
|
||||
|
||||
import millfork.CompilationOptions
|
||||
import millfork.assembly.{z80, _}
|
||||
import millfork.assembly.opt.SingleStatus
|
||||
import millfork.assembly._
|
||||
import millfork.assembly.opt.{AnyStatus, SingleStatus}
|
||||
import millfork.assembly.z80._
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
@ -437,6 +437,30 @@ case class MatchRegister(register: ZRegister.Value, i: Int) extends AssemblyLine
|
||||
}
|
||||
}
|
||||
|
||||
case class MatchValueAtIxOffset(offset: Int, i: Int) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
flowInfo.statusBefore.memIx.getOrElse(offset, AnyStatus) match {
|
||||
case SingleStatus(value) => ctx.addObject(i, value)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
case class MatchValueAtMatchedIxOffset(oi: Int, vi: Int) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
|
||||
val offset = ctx.get[Int](oi)
|
||||
flowInfo.statusBefore.memIx.getOrElse(offset, AnyStatus) match {
|
||||
case SingleStatus(value) => ctx.addObject(vi, value)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class MatchImmediate(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
line.registers match {
|
||||
@ -445,6 +469,37 @@ case class MatchImmediate(i: Int) extends AssemblyLinePattern {
|
||||
}
|
||||
}
|
||||
|
||||
case class RegisterAndOffset(register: ZRegister.Value, offset: Int)
|
||||
|
||||
case class MatchSourceRegisterAndOffset(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
line.registers match {
|
||||
case TwoRegisters(_, s) => ctx.addObject(i, RegisterAndOffset(s, 0))
|
||||
case TwoRegistersOffset(_, s, o) => ctx.addObject(i, RegisterAndOffset(s, o))
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
case class MatchTargetRegisterAndOffset(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
line.registers match {
|
||||
case TwoRegisters(t, _) => ctx.addObject(i, RegisterAndOffset(t, 0))
|
||||
case TwoRegistersOffset(t, _, o) => ctx.addObject(i, RegisterAndOffset(t, o))
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
case class DoesntChangeMatchedRegisterAndOffset(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
|
||||
val ro = ctx.get[RegisterAndOffset](i)
|
||||
import ZRegister._
|
||||
ro.register match {
|
||||
case AF | SP => false // ?
|
||||
case MEM_ABS_8 | MEM_ABS_16 | MEM_HL | MEM_DE | MEM_BC => !line.changesMemory
|
||||
case _ => !line.changesRegisterAndOffset(ro.register, ro.offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class MatchParameter(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
@ -619,27 +674,87 @@ case class Is16BitLoad(target:ZRegister.Value, source: ZRegister.Value) extends
|
||||
|
||||
case class IsRegular8BitLoadFrom(source: ZRegister.Value) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean =
|
||||
line.opcode == ZOpcode.LD && line.registers.asInstanceOf[TwoRegisters].source == source && (line.registers.asInstanceOf[TwoRegisters].target match {
|
||||
case ZRegister.I | ZRegister.MEM_ABS_8 | ZRegister.R | ZRegister.MEM_DE | ZRegister.MEM_BC => false
|
||||
case _ => true
|
||||
line.opcode == ZOpcode.LD && (line.registers match {
|
||||
case TwoRegistersOffset(ZRegister.I | ZRegister.MEM_ABS_8 | ZRegister.R | ZRegister.MEM_DE | ZRegister.MEM_BC, _, _) => false
|
||||
case TwoRegisters(ZRegister.I | ZRegister.MEM_ABS_8 | ZRegister.R | ZRegister.MEM_DE | ZRegister.MEM_BC, _) => false
|
||||
case TwoRegistersOffset(t, s, _) => s == source
|
||||
case TwoRegisters(t, s) => s == source
|
||||
})
|
||||
|
||||
override def toString: String = "LD _," + source
|
||||
}
|
||||
|
||||
object NoOffset extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean =
|
||||
line.registers match {
|
||||
case _:TwoRegistersOffset => false
|
||||
case _:OneRegisterOffset => false
|
||||
case _ => true
|
||||
}
|
||||
}
|
||||
|
||||
case class Is8BitLoadTo(target: ZRegister.Value) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean =
|
||||
line.opcode == ZOpcode.LD && line.registers.asInstanceOf[TwoRegisters].target == target && (line.registers.asInstanceOf[TwoRegisters].source match {
|
||||
case ZRegister.I | ZRegister.MEM_ABS_8 | ZRegister.R => false
|
||||
case _ => true
|
||||
line.opcode == ZOpcode.LD && (line.registers match {
|
||||
case TwoRegistersOffset(t, s, _) => target == t
|
||||
case TwoRegisters(t, s) => target == t && (s match {
|
||||
case ZRegister.I | ZRegister.MEM_ABS_8 | ZRegister.R => false
|
||||
case _ => true
|
||||
})
|
||||
})
|
||||
|
||||
override def toString: String = s"LD $target,_"
|
||||
}
|
||||
|
||||
case class MatchSourceIxOffsetOf8BitLoad(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
|
||||
line.opcode == ZOpcode.LD && (line.registers match {
|
||||
case TwoRegistersOffset(_, ZRegister.MEM_IX_D, offset) => ctx.addObject(i, offset)
|
||||
case _ => false
|
||||
})
|
||||
}
|
||||
|
||||
override def toString: String = "LD (IX,!),_"
|
||||
}
|
||||
|
||||
case class MatchIxOffset(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
|
||||
line.registers match {
|
||||
case OneRegisterOffset(ZRegister.MEM_IX_D, offset) => ctx.addObject(i, offset)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
override def toString: String = "LD (IX,!),_"
|
||||
}
|
||||
|
||||
case class MatchTargetIxOffsetOf8BitLoad(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
|
||||
line.opcode == ZOpcode.LD && (line.registers match {
|
||||
case TwoRegistersOffset(ZRegister.MEM_IX_D, _, offset) => ctx.addObject(i, offset)
|
||||
case _ => false
|
||||
})
|
||||
}
|
||||
|
||||
override def toString: String = "LD (IX,!),_"
|
||||
}
|
||||
|
||||
case class MatchUnimportantIxOffset(i: Int) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertBackward(needsFlowInfo)
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
|
||||
val offset = ctx.get[Int](i)
|
||||
flowInfo.importanceAfter.memIx.getOrElse(offset, Unimportant) == Unimportant
|
||||
}
|
||||
}
|
||||
|
||||
case class Is16BitLoadTo(target: ZRegister.Value) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean =
|
||||
line.opcode == ZOpcode.LD_16 && line.registers.asInstanceOf[TwoRegisters].target == target
|
||||
line.opcode == ZOpcode.LD_16 && (line.registers match {
|
||||
case TwoRegistersOffset(t, s, _) => target == t
|
||||
case TwoRegisters(t, s) => target == t
|
||||
})
|
||||
|
||||
override def toString: String = s"LD_16 $target,_"
|
||||
}
|
||||
|
@ -19,13 +19,13 @@ class AbstractExpressionCompiler[T <: AbstractCode] {
|
||||
}
|
||||
|
||||
def callingContext(ctx: CompilationContext, v: MemoryVariable): CompilationContext = {
|
||||
val result = new Environment(Some(ctx.env), "")
|
||||
val result = new Environment(Some(ctx.env), "", ctx.options.platform.cpuFamily)
|
||||
result.registerVariable(VariableDeclarationStatement(v.name, v.typ.name, stack = false, global = false, constant = false, volatile = false, register = false, initialValue = None, address = None, bank = v.declaredBank), ctx.options)
|
||||
ctx.copy(env = result)
|
||||
}
|
||||
|
||||
def getParamMaxSize(ctx: CompilationContext, params: List[Expression]): Int = {
|
||||
params.map { case expr => getExpressionType(ctx, expr).size}.max
|
||||
params.map(expr => getExpressionType(ctx, expr).size).max
|
||||
}
|
||||
|
||||
def getSumSize(ctx: CompilationContext, params: List[(Boolean, Expression)]): Int = {
|
||||
|
@ -15,7 +15,7 @@ case class CompilationContext(env: Environment,
|
||||
breakLabels: Map[String, Label] = Map(),
|
||||
continueLabels: Map[String, Label] = Map()){
|
||||
def withInlinedEnv(environment: Environment, newLabel: String): CompilationContext = {
|
||||
val newEnv = new Environment(Some(env), newLabel)
|
||||
val newEnv = new Environment(Some(env), newLabel, environment.cpuFamily)
|
||||
newEnv.things ++= environment.things
|
||||
copy(env = newEnv)
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ object MosCompiler extends AbstractCompiler[AssemblyLine] {
|
||||
override def compile(ctx: CompilationContext): List[AssemblyLine] = {
|
||||
ctx.env.nameCheck(ctx.function.code)
|
||||
val chunk = MosStatementCompiler.compile(ctx, ctx.function.code)
|
||||
val zpRegisterSize = ctx.options.zpRegisterSize
|
||||
|
||||
val storeParamsFromRegisters = ctx.function.params match {
|
||||
case NormalParamSignature(List(param@MemoryVariable(_, typ, _))) if typ.size == 1 =>
|
||||
@ -29,31 +30,42 @@ object MosCompiler extends AbstractCompiler[AssemblyLine] {
|
||||
case _ => Nil
|
||||
}
|
||||
val phReg =
|
||||
if (ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
if (zpRegisterSize > 0) {
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
List(
|
||||
AssemblyLine.zeropage(LDA, reg),
|
||||
AssemblyLine.implied(PHA),
|
||||
AssemblyLine.zeropage(LDA, reg, 1),
|
||||
AssemblyLine.implied(PHA)
|
||||
)
|
||||
(0 until zpRegisterSize).flatMap { i =>
|
||||
List(
|
||||
AssemblyLine.zeropage(LDA, reg, i),
|
||||
AssemblyLine.implied(PHA))
|
||||
}.toList
|
||||
} else Nil
|
||||
|
||||
val prefix = storeParamsFromRegisters ++ (if (ctx.function.interrupt) {
|
||||
|
||||
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
if (ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
List(
|
||||
if (zpRegisterSize > 0) {
|
||||
val physicalRegisters = List(
|
||||
AssemblyLine.implied(PHB),
|
||||
AssemblyLine.implied(PHD),
|
||||
AssemblyLine.immediate(REP, 0x30),
|
||||
AssemblyLine.implied(PHA_W),
|
||||
AssemblyLine.implied(PHX_W),
|
||||
AssemblyLine.implied(PHY_W),
|
||||
AssemblyLine.implied(PHY_W),
|
||||
AssemblyLine.zeropage(LDA_W, ctx.env.get[VariableInMemory]("__reg")),
|
||||
AssemblyLine.implied(PHA_W),
|
||||
AssemblyLine.immediate(SEP, 0x30))
|
||||
AssemblyLine.implied(PHY_W))
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
val initialBytes = (0 to zpRegisterSize.&(0xfe).-(2) by 2).flatMap{ i=>
|
||||
List(
|
||||
AssemblyLine.zeropage(LDA_W, reg, i),
|
||||
AssemblyLine.implied(PHA_W))
|
||||
}
|
||||
val lastByte = if (zpRegisterSize % 2 != 0){
|
||||
List(
|
||||
AssemblyLine.immediate(SEP, 0x30),
|
||||
AssemblyLine.zeropage(LDA, reg, zpRegisterSize - 1),
|
||||
AssemblyLine.implied(PHA))
|
||||
} else {
|
||||
List(AssemblyLine.immediate(SEP, 0x30))
|
||||
}
|
||||
physicalRegisters ++ initialBytes ++ lastByte
|
||||
} else {
|
||||
List(
|
||||
AssemblyLine.implied(PHB),
|
||||
@ -93,13 +105,22 @@ object MosCompiler extends AbstractCompiler[AssemblyLine] {
|
||||
AssemblyLine.implied(PHA),
|
||||
AssemblyLine.implied(CLD)) ++ phReg
|
||||
}
|
||||
} else if (ctx.function.kernalInterrupt && ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
} else if (ctx.function.kernalInterrupt && zpRegisterSize > 0) {
|
||||
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
List(
|
||||
AssemblyLine.accu16,
|
||||
AssemblyLine.zeropage(LDA_W, ctx.env.get[VariableInMemory]("__reg")),
|
||||
AssemblyLine.implied(PHA_W),
|
||||
AssemblyLine.accu8)
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
val initialBytes = (0 to zpRegisterSize.&(0xfe).-(2) by 2).flatMap { i =>
|
||||
List(
|
||||
AssemblyLine.zeropage(LDA_W, reg, i),
|
||||
AssemblyLine.implied(PHA_W))
|
||||
}.toList
|
||||
val lastByte = if (zpRegisterSize % 2 != 0) {
|
||||
List(AssemblyLine.accu8,
|
||||
AssemblyLine.zeropage(LDA_W, reg, zpRegisterSize - 1),
|
||||
AssemblyLine.implied(PHA_W))
|
||||
} else {
|
||||
List(AssemblyLine.accu8)
|
||||
}
|
||||
AssemblyLine.accu16 :: (initialBytes ++ lastByte)
|
||||
} else phReg
|
||||
} else Nil) ++ stackPointerFixAtBeginning(ctx)
|
||||
val label = AssemblyLine.label(Label(ctx.function.name)).copy(elidable = false)
|
||||
|
@ -136,7 +136,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
|
||||
def prepareWordIndexing(ctx: CompilationContext, pointy: Pointy, indexExpression: Expression): List[AssemblyLine] = {
|
||||
val w = ctx.env.get[Type]("word")
|
||||
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
if (ctx.options.zpRegisterSize < 2) {
|
||||
ErrorReporting.error("16-bit indexing requires a zeropage pseudoregister")
|
||||
return Nil
|
||||
}
|
||||
|
@ -37,32 +37,44 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
||||
val m = ctx.function
|
||||
val b = env.get[Type]("byte")
|
||||
val w = env.get[Type]("word")
|
||||
val plReg =
|
||||
if (ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
val zpRegisterSize = ctx.options.zpRegisterSize
|
||||
lazy val plReg =
|
||||
if (zpRegisterSize > 0) {
|
||||
val reg = env.get[VariableInMemory]("__reg")
|
||||
List(
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.zeropage(STA, reg, 1),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.zeropage(STA, reg)
|
||||
)
|
||||
(zpRegisterSize.-(1) to 0 by (-1)).flatMap{ i=>
|
||||
List(
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.zeropage(STA, reg,i))
|
||||
}.toList
|
||||
} 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))
|
||||
val returnInstructions = if (m.interrupt) {
|
||||
lazy val returnInstructions = if (m.interrupt) {
|
||||
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
if (ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
List(
|
||||
AssemblyLine.immediate(REP, 0x30),
|
||||
AssemblyLine.implied(PLA_W),
|
||||
AssemblyLine.zeropage(STA_W, env.get[VariableInMemory]("__reg")),
|
||||
AssemblyLine.implied(PLY),
|
||||
AssemblyLine.implied(PLX),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.implied(PLD),
|
||||
AssemblyLine.implied(PLB),
|
||||
AssemblyLine.implied(RTI))
|
||||
if (zpRegisterSize > 0) {
|
||||
val reg = env.get[VariableInMemory]("__reg")
|
||||
val lastByte = if (zpRegisterSize % 2 != 0) {
|
||||
List(
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.zeropage(STA, reg, zpRegisterSize - 1),
|
||||
AssemblyLine.immediate(REP, 0x30))
|
||||
} else {
|
||||
List(AssemblyLine.immediate(REP, 0x30))
|
||||
}
|
||||
val remainingBytes = (zpRegisterSize.&(0xfe).-(2) to 0 by (-2)).flatMap { i =>
|
||||
List(
|
||||
AssemblyLine.implied(PLA_W),
|
||||
AssemblyLine.zeropage(STA_W, reg, i))
|
||||
}
|
||||
lastByte ++ remainingBytes ++
|
||||
List(
|
||||
AssemblyLine.implied(PLY),
|
||||
AssemblyLine.implied(PLX),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.implied(PLD),
|
||||
AssemblyLine.implied(PLB),
|
||||
AssemblyLine.implied(RTI))
|
||||
} else {
|
||||
List(
|
||||
AssemblyLine.immediate(REP, 0x30),
|
||||
@ -105,13 +117,24 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
||||
AssemblyLine.implied(RTI))
|
||||
}
|
||||
} else {
|
||||
(if (m.kernalInterrupt && ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
(if (m.kernalInterrupt && zpRegisterSize > 0) {
|
||||
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
List(
|
||||
AssemblyLine.accu16,
|
||||
AssemblyLine.implied(PLA_W),
|
||||
AssemblyLine.zeropage(STA_W, env.get[VariableInMemory]("__reg")),
|
||||
AssemblyLine.accu8)
|
||||
val reg = env.get[VariableInMemory]("__reg")
|
||||
val lastByte = if (zpRegisterSize % 2 != 0) {
|
||||
List(
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.zeropage(STA, reg, zpRegisterSize - 1),
|
||||
AssemblyLine.accu16)
|
||||
} else {
|
||||
List(AssemblyLine.accu16)
|
||||
}
|
||||
val remainingBytes = (zpRegisterSize.&(0xfe).-(2) to 0 by (-2)).flatMap { i =>
|
||||
List(
|
||||
AssemblyLine.implied(PLA_W),
|
||||
AssemblyLine.zeropage(STA_W, reg, i),
|
||||
AssemblyLine.accu8)
|
||||
}
|
||||
lastByte ++ remainingBytes
|
||||
} else plReg
|
||||
} else Nil) ++ (if (m.isFar(ctx.options)) {
|
||||
List(AssemblyLine.implied(RTL))
|
||||
|
@ -38,7 +38,7 @@ object PseudoregisterBuiltIns {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
if (ctx.options.zpRegisterSize < 2) {
|
||||
ErrorReporting.error("Word addition or subtraction requires the zeropage pseudoregister", params.headOption.flatMap(_._2.position))
|
||||
return Nil
|
||||
}
|
||||
@ -57,7 +57,7 @@ object PseudoregisterBuiltIns {
|
||||
}
|
||||
|
||||
def addToReg(ctx: CompilationContext, r: Expression, subtract: Boolean, decimal: Boolean): List[AssemblyLine] = {
|
||||
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
if (ctx.options.zpRegisterSize < 2) {
|
||||
ErrorReporting.error("Word addition or subtraction requires the zeropage pseudoregister", r.position)
|
||||
return Nil
|
||||
}
|
||||
@ -108,7 +108,7 @@ object PseudoregisterBuiltIns {
|
||||
|
||||
|
||||
def compileWordBitOpsToAX(ctx: CompilationContext, params: List[Expression], op: Opcode.Value): List[AssemblyLine] = {
|
||||
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
if (ctx.options.zpRegisterSize < 2) {
|
||||
ErrorReporting.error("Word bit operation requires the zeropage pseudoregister", params.headOption.flatMap(_.position))
|
||||
return Nil
|
||||
}
|
||||
@ -126,7 +126,7 @@ object PseudoregisterBuiltIns {
|
||||
}
|
||||
|
||||
def bitOpReg(ctx: CompilationContext, r: Expression, op: Opcode.Value): List[AssemblyLine] = {
|
||||
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
if (ctx.options.zpRegisterSize < 2) {
|
||||
ErrorReporting.error("Word bit operation requires the zeropage pseudoregister", r.position)
|
||||
return Nil
|
||||
}
|
||||
@ -180,7 +180,7 @@ object PseudoregisterBuiltIns {
|
||||
}
|
||||
|
||||
def compileWordShiftOps(left: Boolean, ctx: CompilationContext, l: Expression, r: Expression): List[AssemblyLine] = {
|
||||
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
if (ctx.options.zpRegisterSize < 2) {
|
||||
ErrorReporting.error("Word shifting requires the zeropage pseudoregister", l.position)
|
||||
return Nil
|
||||
}
|
||||
@ -246,7 +246,7 @@ object PseudoregisterBuiltIns {
|
||||
}
|
||||
|
||||
def compileByteMultiplication(ctx: CompilationContext, param1OrRegister: Option[Expression], param2: Expression, storeInRegLo: Boolean): List[AssemblyLine] = {
|
||||
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
if (ctx.options.zpRegisterSize < 2) {
|
||||
ErrorReporting.error("Variable byte multiplication requires the zeropage pseudoregister", param1OrRegister.flatMap(_.position))
|
||||
return Nil
|
||||
}
|
||||
|
@ -2,9 +2,12 @@ package millfork.compiler.z80
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.z80.ZLine
|
||||
import millfork.compiler.{AbstractCompiler, CompilationContext}
|
||||
import millfork.env.Label
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.ZRegister
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
@ -20,6 +23,26 @@ object Z80Compiler extends AbstractCompiler[ZLine] {
|
||||
val chunk = Z80StatementCompiler.compile(ctx, ctx.function.code)
|
||||
val label = ZLine.label(Label(ctx.function.name)).copy(elidable = false)
|
||||
val prefix = Nil // TODO
|
||||
label :: (prefix ++ chunk)
|
||||
label :: (prefix ++ stackPointerFixAtBeginning(ctx) ++ chunk)
|
||||
}
|
||||
|
||||
def stackPointerFixAtBeginning(ctx: CompilationContext): List[ZLine] = {
|
||||
val m = ctx.function
|
||||
if (m.stackVariablesSize == 0) return Nil
|
||||
if (!ctx.options.flags(CompilationFlag.EmitZ80Opcodes)) {
|
||||
ErrorReporting.error(s"Target CPU does not support stack variables", m.position)
|
||||
return Nil
|
||||
}
|
||||
if (m.stackVariablesSize > 127) {
|
||||
ErrorReporting.error(s"Function ${m.name} has too many local stack variables", m.position)
|
||||
return Nil
|
||||
}
|
||||
import millfork.assembly.z80.ZOpcode._
|
||||
import ZRegister._
|
||||
List(
|
||||
ZLine.register(PUSH, IX),
|
||||
ZLine.ldImm16(IX, 0x10000 - ctx.function.stackVariablesSize.&(1).+(ctx.function.stackVariablesSize)),
|
||||
ZLine.registers(ADD_16, IX, SP),
|
||||
ZLine.ld16(SP, IX))
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,6 @@ import millfork.node.{ZRegister, _}
|
||||
import millfork.assembly.z80.ZOpcode._
|
||||
import millfork.error.ErrorReporting
|
||||
|
||||
import scala.collection.GenTraversableOnce
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
@ -142,6 +140,17 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
case _ => ???
|
||||
}
|
||||
case v: StackVariable =>
|
||||
v.typ.size match {
|
||||
case 0 => ???
|
||||
case 1 => loadByteViaIX(v.baseOffset, target)
|
||||
case 2 => target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.HL =>
|
||||
List(ZLine.ldViaIx(ZRegister.L, v.baseOffset), ZLine.ldViaIx(ZRegister.H, v.baseOffset + 1))
|
||||
}
|
||||
case _ => ???
|
||||
}
|
||||
}
|
||||
case i: IndexedExpression =>
|
||||
calculateAddressToHL(ctx, i) match {
|
||||
@ -316,13 +325,13 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, ADD)
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, ADD, ADC, size, decimal = false)
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, ADD, ADC, size)
|
||||
}
|
||||
case "-=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, SUB)
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, SUB, SBC, size, decimal = false)
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, SUB, SBC, size)
|
||||
}
|
||||
case "+'=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
@ -369,19 +378,19 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, AND)
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, AND, AND, size, decimal = false)
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, AND, AND, size)
|
||||
}
|
||||
case "^=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, XOR)
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, XOR, XOR, size, decimal = false)
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, XOR, XOR, size)
|
||||
}
|
||||
case "|=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, OR)
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, OR, OR, size, decimal = false)
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, OR, OR, size)
|
||||
}
|
||||
case _ =>
|
||||
env.maybeGet[Type](f.functionName) match {
|
||||
@ -509,6 +518,14 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def loadByteViaIX(offset: Int, target: ZExpressionTarget.Value): List[ZLine] = {
|
||||
target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.A => List(ZLine.ldViaIx(ZRegister.A, offset))
|
||||
case ZExpressionTarget.HL => List(ZLine.ldViaIx(ZRegister.A, offset), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0))
|
||||
}
|
||||
}
|
||||
|
||||
def loadByteViaHL(target: ZExpressionTarget.Value): List[ZLine] = {
|
||||
target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
@ -534,6 +551,23 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
prepareA ++ fillUpperBytes
|
||||
}
|
||||
|
||||
def signExtendViaIX(targetOffset: Int, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
||||
if (bytes == 0) return Nil
|
||||
val prepareA = if (signedSource) {
|
||||
val prefix = if (hiRegister == ZRegister.A) Nil else List(ZLine.ld8(ZRegister.A, hiRegister))
|
||||
val label = Z80Compiler.nextLabel("sx")
|
||||
prefix ++ List(
|
||||
ZLine.imm8(OR, 0x7f),
|
||||
ZLine.jump(label, IfFlagSet(ZFlag.S)),
|
||||
ZLine.ldImm8(ZRegister.A, 0),
|
||||
ZLine.label(label))
|
||||
} else {
|
||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||
}
|
||||
val fillUpperBytes = List.tabulate(bytes)(i => ZLine.ldViaIx(targetOffset + i, ZRegister.A))
|
||||
prepareA ++ fillUpperBytes
|
||||
}
|
||||
|
||||
def storeA(targetAddr: Constant, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
@ -542,6 +576,14 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def storeAViaIX(targetOffset: Int, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ldViaIx(targetOffset, ZRegister.A))
|
||||
case n => ZLine.ldViaIx(targetOffset, ZRegister.A) :: signExtendViaIX(targetOffset + 1, ZRegister.A, n - 1, signedSource)
|
||||
}
|
||||
}
|
||||
|
||||
def storeHL(targetAddr: Constant, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
// TODO: LD (nnnn),HL compatibility?
|
||||
targetSize match {
|
||||
@ -552,12 +594,23 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def storeHLViaIX(offset: Int, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
// TODO: LD (nnnn),HL compatibility?
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ldViaIx(offset, ZRegister.L))
|
||||
case 2 => List(ZLine.ldViaIx(offset, ZRegister.L), ZLine.ldViaIx(offset + 1, ZRegister.H))
|
||||
case n => List(ZLine.ldViaIx(offset, ZRegister.L), ZLine.ldViaIx(offset + 1, ZRegister.H)) ++ signExtendViaIX(offset + 2, ZRegister.H, n - 2, signedSource)
|
||||
}
|
||||
}
|
||||
|
||||
def storeA(ctx: CompilationContext, target: LhsExpression, signedSource: Boolean): List[ZLine] = {
|
||||
val env = ctx.env
|
||||
target match {
|
||||
case VariableExpression(vname) =>
|
||||
env.get[Variable](vname) match {
|
||||
case v: VariableInMemory => storeA(v.toAddress, v.typ.size, signedSource)
|
||||
case v: StackVariable => storeAViaIX(v.baseOffset, v.typ.size, signedSource)
|
||||
}
|
||||
case i:IndexedExpression =>
|
||||
calculateAddressToHL(ctx, i) match {
|
||||
@ -577,6 +630,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
case VariableExpression(vname) =>
|
||||
env.get[Variable](vname) match {
|
||||
case v: VariableInMemory => storeHL(v.toAddress, v.typ.size, signedSource)
|
||||
case v: StackVariable => storeHLViaIX(v.baseOffset, v.typ.size, signedSource)
|
||||
}
|
||||
case IndexedExpression(pointyName, indexExpr) =>
|
||||
env.getPointy(pointyName) match {
|
||||
|
@ -21,7 +21,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
||||
val options = ctx.options
|
||||
statement match {
|
||||
case ReturnStatement(None) =>
|
||||
ctx.function.returnType match {
|
||||
fixStackOnReturn(ctx) ++ (ctx.function.returnType match {
|
||||
case _: BooleanType =>
|
||||
List(ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
|
||||
case t => t.size match {
|
||||
@ -34,29 +34,31 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
||||
ErrorReporting.warn("Returning without a value", options, statement.position)
|
||||
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
|
||||
}
|
||||
}
|
||||
})
|
||||
case ReturnStatement(Some(e)) =>
|
||||
ctx.function.returnType match {
|
||||
case t: BooleanType => t.size match {
|
||||
case 0 =>
|
||||
ErrorReporting.error("Cannot return anything from a void function", statement.position)
|
||||
List(ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
|
||||
fixStackOnReturn(ctx) ++
|
||||
List(ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
|
||||
case 1 =>
|
||||
Z80ExpressionCompiler.compileToA(ctx, e) ++
|
||||
Z80ExpressionCompiler.compileToA(ctx, e) ++ fixStackOnReturn(ctx) ++
|
||||
List(ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
|
||||
case 2 =>
|
||||
Z80ExpressionCompiler.compileToHL(ctx, e) ++
|
||||
Z80ExpressionCompiler.compileToHL(ctx, e) ++ fixStackOnReturn(ctx) ++
|
||||
List(ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
|
||||
}
|
||||
case t => t.size match {
|
||||
case 0 =>
|
||||
ErrorReporting.error("Cannot return anything from a void function", statement.position)
|
||||
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
|
||||
fixStackOnReturn(ctx) ++
|
||||
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
|
||||
case 1 =>
|
||||
Z80ExpressionCompiler.compileToA(ctx, e) ++
|
||||
Z80ExpressionCompiler.compileToA(ctx, e) ++ fixStackOnReturn(ctx) ++
|
||||
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
|
||||
case 2 =>
|
||||
Z80ExpressionCompiler.compileToHL(ctx, e) ++
|
||||
Z80ExpressionCompiler.compileToHL(ctx, e) ++ fixStackOnReturn(ctx) ++
|
||||
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
|
||||
}
|
||||
}
|
||||
@ -150,6 +152,26 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
private def fixStackOnReturn(ctx: CompilationContext): List[ZLine] = {
|
||||
if (ctx.function.stackVariablesSize > 0) {
|
||||
import ZRegister._
|
||||
val localVariableArea = ctx.function.stackVariablesSize.&(1).+(ctx.function.stackVariablesSize)
|
||||
if (ctx.function.returnType.size == 2) {
|
||||
List(
|
||||
ZLine.ldImm16(IX, localVariableArea),
|
||||
ZLine.registers(ADD_16, IX, SP),
|
||||
ZLine.ld16(SP, IX),
|
||||
ZLine.register(POP, IX))
|
||||
} else {
|
||||
List(
|
||||
ZLine.ldImm16(HL, localVariableArea),
|
||||
ZLine.registers(ADD_16, HL, SP),
|
||||
ZLine.ld16(SP, HL),
|
||||
ZLine.register(POP, IX))
|
||||
}
|
||||
} else Nil
|
||||
}
|
||||
|
||||
def labelChunk(labelName: String) = List(ZLine.label(Label(labelName)))
|
||||
|
||||
def jmpChunk(label: Label) = List(ZLine.jump(label))
|
||||
|
@ -1,6 +1,6 @@
|
||||
package millfork.compiler.z80
|
||||
|
||||
import millfork.assembly.z80.{TwoRegisters, ZLine, ZOpcode}
|
||||
import millfork.assembly.z80._
|
||||
import millfork.compiler.CompilationContext
|
||||
import millfork.node._
|
||||
import ZOpcode._
|
||||
@ -247,13 +247,14 @@ object ZBuiltIns {
|
||||
}
|
||||
|
||||
def perform8BitInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, opcode: ZOpcode.Value, decimal: Boolean = false): List[ZLine] = {
|
||||
val calculateAddress = lhs match {
|
||||
val (calculateAddress, lv):(List[ZLine], LocalVariableAddressOperand) = lhs match {
|
||||
case VariableExpression(name) =>
|
||||
ctx.env.get[Variable](name) match {
|
||||
case v: VariableInMemory => List(ZLine.ldImm16(ZRegister.HL, v.toAddress))
|
||||
case v: VariableInMemory => List(ZLine.ldImm16(ZRegister.HL, v.toAddress)) -> LocalVariableAddressViaHL
|
||||
case v: StackVariable => Nil -> LocalVariableAddressViaIX(v.baseOffset)
|
||||
case _ => ???
|
||||
}
|
||||
case i: IndexedExpression => Z80ExpressionCompiler.calculateAddressToHL(ctx, i)
|
||||
case i: IndexedExpression => Z80ExpressionCompiler.calculateAddressToHL(ctx, i) -> LocalVariableAddressViaHL
|
||||
}
|
||||
val constantRight = ctx.env.eval(rhs)
|
||||
val calculateChange = Z80ExpressionCompiler.compileToA(ctx, rhs)
|
||||
@ -266,45 +267,45 @@ object ZBuiltIns {
|
||||
opcode match {
|
||||
case ADD if decimal =>
|
||||
setup ++ List(
|
||||
ZLine.register(ADD, ZRegister.MEM_HL),
|
||||
ZLine.register(ADD, lv),
|
||||
ZLine.implied(DAA),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
ZLine.ld8(lv, ZRegister.A))
|
||||
case ADD if !decimal =>
|
||||
constantRight match {
|
||||
case Some(NumericConstant(1, _)) =>
|
||||
calculateAddress :+ ZLine.register(INC, ZRegister.MEM_HL)
|
||||
calculateAddress :+ ZLine.register(INC, lv)
|
||||
case Some(NumericConstant(0xff | -1, _)) =>
|
||||
calculateAddress :+ ZLine.register(DEC, ZRegister.MEM_HL)
|
||||
calculateAddress :+ ZLine.register(DEC, lv)
|
||||
case _ =>
|
||||
setup ++ List(
|
||||
ZLine.register(ADD, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
ZLine.register(ADD, lv),
|
||||
ZLine.ld8(lv, ZRegister.A))
|
||||
}
|
||||
case SUB if decimal =>
|
||||
setup ++ List(
|
||||
ZLine.ld8(ZRegister.E, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.A, lv),
|
||||
ZLine.register(SUB, ZRegister.E),
|
||||
ZLine.implied(DAA),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
ZLine.ld8(lv, ZRegister.A))
|
||||
case SUB if !decimal=>
|
||||
constantRight match {
|
||||
case Some(NumericConstant(1, _)) =>
|
||||
calculateAddress :+ ZLine.register(DEC, ZRegister.MEM_HL)
|
||||
calculateAddress :+ ZLine.register(DEC, lv)
|
||||
case Some(NumericConstant(0xff | -1, _)) =>
|
||||
calculateAddress :+ ZLine.register(INC, ZRegister.MEM_HL)
|
||||
calculateAddress :+ ZLine.register(INC, lv)
|
||||
case _ =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||
setup ++ List(
|
||||
ZLine.implied(NEG),
|
||||
ZLine.register(ADD, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
ZLine.register(ADD, lv),
|
||||
ZLine.ld8(lv, ZRegister.A))
|
||||
} else {
|
||||
setup ++ List(
|
||||
ZLine.implied(CPL),
|
||||
ZLine.register(INC, ZRegister.A),
|
||||
ZLine.register(SUB, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
ZLine.register(SUB, lv),
|
||||
ZLine.ld8(lv, ZRegister.A))
|
||||
}
|
||||
}
|
||||
case XOR =>
|
||||
@ -313,35 +314,35 @@ object ZBuiltIns {
|
||||
calculateAddress
|
||||
case Some(NumericConstant(0xff | -1, _)) =>
|
||||
calculateAddress ++ List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.A, lv),
|
||||
ZLine.implied(CPL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
ZLine.ld8(lv, ZRegister.A))
|
||||
case _ =>
|
||||
setup ++ List(
|
||||
ZLine.register(XOR, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
ZLine.register(XOR, lv),
|
||||
ZLine.ld8(lv, ZRegister.A))
|
||||
}
|
||||
case OR =>
|
||||
constantRight match {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
calculateAddress
|
||||
case Some(NumericConstant(0xff | -1, _)) =>
|
||||
calculateAddress :+ ZLine.ldImm8(ZRegister.MEM_HL, 0xff)
|
||||
calculateAddress :+ ZLine.ldImm8(lv, 0xff)
|
||||
case _ =>
|
||||
setup ++ List(
|
||||
ZLine.register(OR, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
ZLine.register(OR, lv),
|
||||
ZLine.ld8(lv, ZRegister.A))
|
||||
}
|
||||
case AND =>
|
||||
constantRight match {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
calculateAddress :+ ZLine.ldImm8(ZRegister.MEM_HL, 0)
|
||||
calculateAddress :+ ZLine.ldImm8(lv, 0)
|
||||
case Some(NumericConstant(0xff | -1, _)) =>
|
||||
calculateAddress
|
||||
case _ =>
|
||||
setup ++ List(
|
||||
ZLine.register(AND, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
ZLine.register(AND, lv),
|
||||
ZLine.ld8(lv, ZRegister.A))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
src/main/scala/millfork/env/Constant.scala
vendored
2
src/main/scala/millfork/env/Constant.scala
vendored
@ -98,7 +98,7 @@ case class UnexpandedConstant(name: String, requiredSize: Int) extends Constant
|
||||
case class NumericConstant(value: Long, requiredSize: Int) extends Constant {
|
||||
if (requiredSize == 1) {
|
||||
if (value < -128 || value > 255) {
|
||||
throw new IllegalArgumentException("The constant is too big")
|
||||
throw new IllegalArgumentException(s"The constant $value is too big")
|
||||
}
|
||||
}
|
||||
override def isProvablyZero: Boolean = value == 0
|
||||
|
18
src/main/scala/millfork/env/Environment.scala
vendored
18
src/main/scala/millfork/env/Environment.scala
vendored
@ -17,10 +17,13 @@ import scala.collection.mutable
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
//noinspection NotImplementedCode
|
||||
class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
class Environment(val parent: Option[Environment], val prefix: String, val cpuFamily: CpuFamily.Value) {
|
||||
|
||||
|
||||
private var baseStackOffset = 0x101
|
||||
private var baseStackOffset: Int = cpuFamily match {
|
||||
case CpuFamily.M6502 => 0x101
|
||||
case CpuFamily.I80 => 0
|
||||
}
|
||||
|
||||
def genRelativeVariable(constant: Constant, typ: Type, zeropage: Boolean): RelativeVariable = {
|
||||
val variable = RelativeVariable(".rv__" + Environment.relVarId.incrementAndGet().formatted("%06d"), constant, typ, zeropage = zeropage, declaredBank = None /*TODO*/)
|
||||
@ -37,7 +40,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
m.environment.getAllPrefixedThings
|
||||
case _ => Map[String, Thing]()
|
||||
}.fold(things.toMap)(_ ++ _)
|
||||
val e = new Environment(None, "")
|
||||
val e = new Environment(None, "", cpuFamily)
|
||||
e.things.clear()
|
||||
e.things ++= allThings
|
||||
e
|
||||
@ -588,7 +591,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
ErrorReporting.error(s"Non-macro function `$name` cannot have inlinable parameters", stmt.position)
|
||||
}
|
||||
|
||||
val env = new Environment(Some(this), name + "$")
|
||||
val env = new Environment(Some(this), name + "$", cpuFamily)
|
||||
stmt.params.foreach(p => env.registerParameter(p, options))
|
||||
val params = if (stmt.assembly) {
|
||||
AssemblyParamSignature(stmt.params.map {
|
||||
@ -963,7 +966,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
} else {
|
||||
val (v, addr) = stmt.address.fold[(VariableInMemory, Constant)]({
|
||||
val alloc =
|
||||
if (typ.name == "pointer") VariableAllocationMethod.Zeropage
|
||||
if (typ.name == "pointer" || typ.name == "__reg$type") VariableAllocationMethod.Zeropage
|
||||
else if (stmt.global) VariableAllocationMethod.Static
|
||||
else if (stmt.register) VariableAllocationMethod.Register
|
||||
else VariableAllocationMethod.Auto
|
||||
@ -1061,11 +1064,12 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
case a: ArrayDeclarationStatement => registerArray(a, options)
|
||||
case i: ImportStatement => ()
|
||||
}
|
||||
if (options.flag(CompilationFlag.ZeropagePseudoregister) && !things.contains("__reg")) {
|
||||
if (options.zpRegisterSize > 0 && !things.contains("__reg")) {
|
||||
addThing(BasicPlainType("__reg$type", options.zpRegisterSize), None)
|
||||
registerVariable(VariableDeclarationStatement(
|
||||
name = "__reg",
|
||||
bank = None,
|
||||
typ = "pointer",
|
||||
typ = "__reg$type",
|
||||
global = true,
|
||||
stack = false,
|
||||
constant = false,
|
||||
|
@ -10,6 +10,10 @@ import millfork.node._
|
||||
*/
|
||||
object UnusedFunctions extends NodeOptimization {
|
||||
|
||||
private val operatorImplementations: List[(String, Int, String)] = List(
|
||||
("*", 2, "__mul_u8u8u8")
|
||||
)
|
||||
|
||||
override def optimize(nodes: List[Node], options: CompilationOptions): List[Node] = {
|
||||
val panicRequired = options.flags(CompilationFlag.CheckIndexOutOfBounds)
|
||||
val allNormalFunctions = nodes.flatMap {
|
||||
@ -18,8 +22,10 @@ object UnusedFunctions extends NodeOptimization {
|
||||
}.toSet
|
||||
val allCalledFunctions = getAllCalledFunctions(nodes).toSet
|
||||
var unusedFunctions = allNormalFunctions -- allCalledFunctions
|
||||
if (allCalledFunctions.contains("*") && options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
unusedFunctions -= "__mul_u8u8u8"
|
||||
for((op, zp, fun) <- operatorImplementations) {
|
||||
if (allCalledFunctions.contains(op) && options.zpRegisterSize >= zp) {
|
||||
unusedFunctions -= fun
|
||||
}
|
||||
}
|
||||
if (unusedFunctions.nonEmpty) {
|
||||
ErrorReporting.debug("Removing unused functions: " + unusedFunctions.mkString(", "))
|
||||
|
@ -35,18 +35,9 @@ class Z80Assembler(program: Program,
|
||||
case ZRegister.SP => 3
|
||||
}
|
||||
|
||||
private def internalArithmeticIndex(opcode: ZOpcode.Value): Int = {
|
||||
import ZOpcode._
|
||||
opcode match {
|
||||
case ADD => 0x80
|
||||
case ADC => 0x88
|
||||
case SUB => 0x90
|
||||
case SBC => 0x98
|
||||
case AND => 0xa0
|
||||
case XOR => 0xa8
|
||||
case OR => 0xb0
|
||||
case CP => 0xb8
|
||||
}
|
||||
private def prefixByte(reg: ZRegister.Value): Int = reg match {
|
||||
case ZRegister.IX | ZRegister.MEM_IX_D => 0xdd
|
||||
case ZRegister.IY | ZRegister.MEM_IY_D => 0xfd
|
||||
}
|
||||
|
||||
override def emitInstruction(bank: String, options: CompilationOptions, index: Int, instr: ZLine): Int = {
|
||||
@ -77,17 +68,31 @@ class Z80Assembler(program: Program,
|
||||
writeByte(bank, index, 0xed)
|
||||
writeByte(bank, index + 1, edImplieds(op))
|
||||
index + 2
|
||||
case ZLine(ADD_16, TwoRegisters(ZRegister.HL, source), param, _) =>
|
||||
case ZLine(ADD_16, TwoRegisters(ZRegister.HL, source), _, _) =>
|
||||
writeByte(bank, index, 9 + 16 * internalRegisterIndex(source))
|
||||
index + 1
|
||||
case ZLine(ADD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), source), _, _) =>
|
||||
writeByte(bank, index, prefixByte(ix))
|
||||
writeByte(bank, index + 1, 9 + 16 * internalRegisterIndex(source))
|
||||
index + 2
|
||||
case ZLine(SBC_16, TwoRegisters(ZRegister.HL, reg), _, _) =>
|
||||
writeByte(bank, index, 0xed)
|
||||
writeByte(bank, index + 1, 0x42 + 0x10 * internalRegisterIndex(reg))
|
||||
index + 2
|
||||
case ZLine(LD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), ZRegister.IMM_16), param, _) =>
|
||||
writeByte(bank, index, prefixByte(ix))
|
||||
writeByte(bank, index + 1, 0x21)
|
||||
writeWord(bank, index + 2, param)
|
||||
index + 4
|
||||
case ZLine(LD_16, TwoRegisters(target, ZRegister.IMM_16), param, _) =>
|
||||
writeByte(bank, index, 1 + 16 * internalRegisterIndex(target))
|
||||
writeWord(bank, index + 1, param)
|
||||
index + 3
|
||||
case ZLine(LD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), ZRegister.MEM_ABS_16), param, _) =>
|
||||
writeByte(bank, index, prefixByte(ix))
|
||||
writeByte(bank, index + 1, 0x2a)
|
||||
writeWord(bank, index + 2, param)
|
||||
index + 4
|
||||
case ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.MEM_ABS_16), param, _) =>
|
||||
writeByte(bank, index, 0x2a)
|
||||
writeWord(bank, index + 1, param)
|
||||
@ -96,32 +101,27 @@ class Z80Assembler(program: Program,
|
||||
writeByte(bank, index, 0x22)
|
||||
writeWord(bank, index + 1, param)
|
||||
index + 3
|
||||
case ZLine(LD_16, TwoRegisters(ZRegister.SP, ZRegister.HL), _, _) =>
|
||||
writeByte(bank, index, 0xF9)
|
||||
index + 1
|
||||
case ZLine(LD_16, TwoRegisters(ZRegister.SP, ix@(ZRegister.IX | ZRegister.IY)), _, _) =>
|
||||
writeByte(bank, index, prefixByte(ix))
|
||||
writeByte(bank, index + 1, 0xF9)
|
||||
index + 2
|
||||
case ZLine(op, OneRegister(ZRegister.IMM_8), param, _) if immediates.contains(op) =>
|
||||
val o = immediates(op)
|
||||
writeByte(bank, index, o)
|
||||
writeByte(bank, index + 1, param)
|
||||
index + 2
|
||||
case ZLine(op, OneRegister(ZRegister.MEM_IX_D), _, _) if oneRegister.contains(op) =>
|
||||
case ZLine(op, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _, _) if oneRegister.contains(op) =>
|
||||
val o = oneRegister(op)
|
||||
writeByte(bank, index, 0xdd)
|
||||
writeByte(bank, index, prefixByte(ix))
|
||||
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ZRegister.MEM_HL) * o.multiplier)
|
||||
writeByte(bank, index + 2, instr.parameter)
|
||||
writeByte(bank, index + 2, offset)
|
||||
index + 3
|
||||
case ZLine(op, OneRegister(ZRegister.MEM_IY_D), _, _) if oneRegister.contains(op) =>
|
||||
case ZLine(op, OneRegister(ix@(ZRegister.IX | ZRegister.IY)), _, _) if oneRegister.contains(op) =>
|
||||
val o = oneRegister(op)
|
||||
writeByte(bank, index, 0xfd)
|
||||
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ZRegister.MEM_HL) * o.multiplier)
|
||||
writeByte(bank, index + 2, instr.parameter)
|
||||
index + 3
|
||||
case ZLine(op, OneRegister(ZRegister.IX), _, _) if oneRegister.contains(op) =>
|
||||
val o = oneRegister(op)
|
||||
writeByte(bank, index, 0xdd)
|
||||
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ZRegister.HL) * o.multiplier)
|
||||
writeByte(bank, index + 2, instr.parameter)
|
||||
index + 3
|
||||
case ZLine(op, OneRegister(ZRegister.IY), _, _) if oneRegister.contains(op) =>
|
||||
val o = oneRegister(op)
|
||||
writeByte(bank, index, 0xfd)
|
||||
writeByte(bank, index, prefixByte(ix))
|
||||
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ZRegister.HL) * o.multiplier)
|
||||
writeByte(bank, index + 2, instr.parameter)
|
||||
index + 3
|
||||
@ -140,6 +140,12 @@ class Z80Assembler(program: Program,
|
||||
writeByte(bank, index, 6 + 8 * internalRegisterIndex(reg))
|
||||
writeByte(bank, index + 1, instr.parameter)
|
||||
index + 2
|
||||
case TwoRegistersOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), ZRegister.IMM_8, offset) =>
|
||||
writeByte(bank, index, prefixByte(ix))
|
||||
writeByte(bank, index + 1, 0x36)
|
||||
writeByte(bank, index + 2, offset)
|
||||
writeByte(bank, index + 3, instr.parameter)
|
||||
index + 4
|
||||
case TwoRegisters(ZRegister.A, ZRegister.MEM_ABS_8) =>
|
||||
writeByte(bank, index, 0x3a)
|
||||
writeWord(bank, index + 1, instr.parameter)
|
||||
@ -160,25 +166,15 @@ class Z80Assembler(program: Program,
|
||||
case TwoRegisters(ZRegister.A, ZRegister.MEM_DE) =>
|
||||
writeByte(bank, index, 0x1a)
|
||||
index + 1
|
||||
case TwoRegisters(ZRegister.MEM_IX_D, source) =>
|
||||
writeByte(bank, index, 0xdd)
|
||||
case TwoRegistersOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), source, offset) =>
|
||||
writeByte(bank, index, prefixByte(ix))
|
||||
writeByte(bank, index + 1, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(ZRegister.MEM_HL) * 8)
|
||||
writeByte(bank, index + 1, instr.parameter)
|
||||
writeByte(bank, index + 2, offset)
|
||||
index + 3
|
||||
case TwoRegisters(ZRegister.MEM_IY_D, source) =>
|
||||
writeByte(bank, index, 0xfd)
|
||||
writeByte(bank, index + 1, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(ZRegister.MEM_HL) * 8)
|
||||
writeByte(bank, index + 1, instr.parameter)
|
||||
index + 3
|
||||
case TwoRegisters(target, ZRegister.MEM_IX_D) =>
|
||||
writeByte(bank, index, 0xdd)
|
||||
writeByte(bank, index, 0x40 + internalRegisterIndex(ZRegister.MEM_HL) + internalRegisterIndex(target) * 8)
|
||||
writeByte(bank, index + 1, instr.parameter)
|
||||
index + 3
|
||||
case TwoRegisters(target, ZRegister.MEM_IY_D) =>
|
||||
writeByte(bank, index, 0xfd)
|
||||
writeByte(bank, index, 0x40 + internalRegisterIndex(ZRegister.MEM_HL) + internalRegisterIndex(target) * 8)
|
||||
writeByte(bank, index + 1, instr.parameter)
|
||||
case TwoRegistersOffset(target, ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset) =>
|
||||
writeByte(bank, index, prefixByte(ix))
|
||||
writeByte(bank, index + 1, 0x40 + internalRegisterIndex(ZRegister.MEM_HL) + internalRegisterIndex(target) * 8)
|
||||
writeByte(bank, index + 2, offset)
|
||||
index + 3
|
||||
case TwoRegisters(target, source) =>
|
||||
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
|
||||
@ -267,29 +263,29 @@ class Z80Assembler(program: Program,
|
||||
writeWord(bank, index + 1, param)
|
||||
index + 3
|
||||
|
||||
case ZLine(RET, IfFlagClear(ZFlag.Z), param, _) =>
|
||||
case ZLine(RET, IfFlagClear(ZFlag.Z), _, _) =>
|
||||
writeByte(bank, index, 0xc0)
|
||||
index + 1
|
||||
case ZLine(RET, IfFlagClear(ZFlag.C), param, _) =>
|
||||
case ZLine(RET, IfFlagClear(ZFlag.C), _, _) =>
|
||||
writeByte(bank, index, 0xd0)
|
||||
index + 1
|
||||
case ZLine(RET, IfFlagClear(ZFlag.P), param, _) =>
|
||||
case ZLine(RET, IfFlagClear(ZFlag.P), _, _) =>
|
||||
writeByte(bank, index, 0xe0)
|
||||
index + 1
|
||||
case ZLine(RET, IfFlagClear(ZFlag.S), param, _) =>
|
||||
case ZLine(RET, IfFlagClear(ZFlag.S), _, _) =>
|
||||
writeByte(bank, index, 0xf0)
|
||||
index + 1
|
||||
|
||||
case ZLine(RET, IfFlagSet(ZFlag.Z), param, _) =>
|
||||
case ZLine(RET, IfFlagSet(ZFlag.Z), _, _) =>
|
||||
writeByte(bank, index, 0xc8)
|
||||
index + 1
|
||||
case ZLine(RET, IfFlagSet(ZFlag.C), param, _) =>
|
||||
case ZLine(RET, IfFlagSet(ZFlag.C), _, _) =>
|
||||
writeByte(bank, index, 0xd8)
|
||||
index + 1
|
||||
case ZLine(RET, IfFlagSet(ZFlag.P), param, _) =>
|
||||
case ZLine(RET, IfFlagSet(ZFlag.P), _, _) =>
|
||||
writeByte(bank, index, 0xe8)
|
||||
index + 1
|
||||
case ZLine(RET, IfFlagSet(ZFlag.S), param, _) =>
|
||||
case ZLine(RET, IfFlagSet(ZFlag.S), _, _) =>
|
||||
writeByte(bank, index, 0xf8)
|
||||
index + 1
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package millfork.parser
|
||||
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.CompilationOptions
|
||||
import millfork.assembly.mos.AssemblyLine
|
||||
|
||||
/**
|
||||
@ -13,7 +13,7 @@ class MosSourceLoadingQueue(initialFilenames: List[String],
|
||||
override def createParser(filename: String, src: String, parentDir: String): MfParser[AssemblyLine] = MosParser(filename, src, parentDir, options)
|
||||
|
||||
def enqueueStandardModules(): Unit = {
|
||||
if (options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
if (options.zpRegisterSize > 0) {
|
||||
moduleQueue.enqueue(() => parseModule("zp_reg", includePath, Left(None)))
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuCmosBenchmarkRun}
|
||||
import millfork.Cpu
|
||||
import millfork.test.emu.{EmuCmosBenchmarkRun, EmuCrossPlatformBenchmarkRun, EmuZ80BenchmarkRun}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
@ -9,7 +10,7 @@ import org.scalatest.{FunSuite, Matchers}
|
||||
class StackVarSuite extends FunSuite with Matchers {
|
||||
|
||||
test("Basic stack assignment") {
|
||||
EmuCmosBenchmarkRun("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)("""
|
||||
| byte output @$c000
|
||||
| void main () {
|
||||
| stack byte a
|
||||
@ -23,7 +24,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Stack byte addition") {
|
||||
EmuCmosBenchmarkRun("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)("""
|
||||
| byte output @$c000
|
||||
| void main () {
|
||||
| stack byte a
|
||||
@ -40,7 +41,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
""".stripMargin)(_.readWord(0xc000) should equal(0x77))
|
||||
}
|
||||
|
||||
test("Complex expressions involving stack variables") {
|
||||
test("Complex expressions involving stack variables (6502)") {
|
||||
EmuCmosBenchmarkRun("""
|
||||
| byte output @$c000
|
||||
| void main () {
|
||||
@ -54,6 +55,20 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
""".stripMargin)(_.readWord(0xc000) should equal(21))
|
||||
}
|
||||
|
||||
test("Complex expressions involving stack variables (Z80)") {
|
||||
EmuZ80BenchmarkRun("""
|
||||
| byte output @$c000
|
||||
| void main () {
|
||||
| stack byte a
|
||||
| a = 7
|
||||
| output = f(a) + f(a) + f(a)
|
||||
| }
|
||||
| asm byte f(byte a) {
|
||||
| ret
|
||||
| }
|
||||
""".stripMargin)(_.readWord(0xc000) should equal(21))
|
||||
}
|
||||
|
||||
// ERROR: (8:9) Right-hand-side expression is too complex
|
||||
// test("Stack byte subtraction") {
|
||||
// EmuUnoptimizedRun("""
|
||||
@ -74,7 +89,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
// }
|
||||
|
||||
test("Stack word addition") {
|
||||
EmuCmosBenchmarkRun("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| stack word a
|
||||
@ -92,7 +107,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Recursion") {
|
||||
EmuCmosBenchmarkRun("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)("""
|
||||
| array output [6] @$c000
|
||||
| byte fails @$c010
|
||||
| void main () {
|
||||
@ -129,7 +144,7 @@ class StackVarSuite extends FunSuite with Matchers {
|
||||
|
||||
|
||||
test("Indexing") {
|
||||
EmuCmosBenchmarkRun("""
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)("""
|
||||
| array output [200] @$c000
|
||||
| void main () {
|
||||
| stack byte a
|
||||
|
@ -11,6 +11,8 @@ object EmuOptimizedCmosRun extends EmuRun(
|
||||
OptimizationPresets.NodeOpt,
|
||||
OptimizationPresets.AssOpt ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
OptimizationPresets.Good ++
|
||||
OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ OptimizationPresets.Good ++
|
||||
CmosOptimizations.All ++ OptimizationPresets.Good ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
|
@ -11,6 +11,8 @@ object EmuOptimizedInlinedRun extends EmuRun(
|
||||
OptimizationPresets.NodeOpt,
|
||||
OptimizationPresets.AssOpt ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
OptimizationPresets.Good ++
|
||||
OptimizationPresets.Good ++
|
||||
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
|
||||
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
|
@ -12,6 +12,8 @@ object EmuOptimizedRun extends EmuRun(
|
||||
OptimizationPresets.NodeOpt,
|
||||
OptimizationPresets.AssOpt ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
OptimizationPresets.Good ++
|
||||
OptimizationPresets.Good ++
|
||||
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
|
||||
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
|
||||
ZeropageRegisterOptimizations.All ++
|
||||
|
@ -18,6 +18,7 @@ object EmuPlatform {
|
||||
Map("default" -> new VariableAllocator(
|
||||
if (CpuFamily.forType(cpu) == CpuFamily.M6502) pointers else Nil,
|
||||
new AfterCodeByteAllocator(0xff00))),
|
||||
if (CpuFamily.forType(cpu) == CpuFamily.M6502) 2 else 0,
|
||||
pointers,
|
||||
".bin",
|
||||
false,
|
||||
|
@ -17,7 +17,7 @@ import millfork.node.StandardCallGraph
|
||||
import millfork.node.opt.NodeOptimization
|
||||
import millfork.output.{MemoryBank, MosAssembler}
|
||||
import millfork.parser.MosParser
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.{CompilationFlag, CompilationOptions, CpuFamily}
|
||||
import org.scalatest.Matchers
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
@ -104,7 +104,6 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
CompilationFlag.InlineFunctions -> this.inline,
|
||||
CompilationFlag.InterproceduralOptimization -> true,
|
||||
CompilationFlag.CompactReturnDispatchParams -> true,
|
||||
CompilationFlag.ZeropagePseudoregister -> true,
|
||||
CompilationFlag.EmitCmosOpcodes -> millfork.Cpu.CmosCompatible.contains(platform.cpu),
|
||||
CompilationFlag.EmitEmulation65816Opcodes -> (platform.cpu == millfork.Cpu.Sixteen),
|
||||
CompilationFlag.Emit65CE02Opcodes -> (platform.cpu == millfork.Cpu.CE02),
|
||||
@ -112,12 +111,11 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
CompilationFlag.OptimizeForSpeed -> blastProcessing,
|
||||
CompilationFlag.OptimizeForSonicSpeed -> blastProcessing
|
||||
// CompilationFlag.CheckIndexOutOfBounds -> true,
|
||||
), None)
|
||||
), None, 2)
|
||||
ErrorReporting.hasErrors = false
|
||||
ErrorReporting.verbosity = 999
|
||||
var effectiveSource = source
|
||||
if (!source.contains("_panic")) effectiveSource += "\n void _panic(){while(true){}}"
|
||||
if (!source.contains("__reg")) effectiveSource += "\n pointer __reg"
|
||||
if (source.contains("import zp_reg"))
|
||||
effectiveSource += Files.readAllLines(Paths.get("include/zp_reg.mfk"), StandardCharsets.US_ASCII).asScala.mkString("\n", "\n", "")
|
||||
val parserF = MosParser("", effectiveSource, "", options)
|
||||
@ -129,7 +127,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
// prepare
|
||||
val program = nodeOptimizations.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
|
||||
val callGraph = new StandardCallGraph(program)
|
||||
val env = new Environment(None, "")
|
||||
val env = new Environment(None, "", CpuFamily.M6502)
|
||||
env.collectDeclarations(program, options)
|
||||
|
||||
val hasOptimizations = assemblyOptimizations.nonEmpty
|
||||
@ -149,7 +147,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
|
||||
|
||||
// compile
|
||||
val env2 = new Environment(None, "")
|
||||
val env2 = new Environment(None, "", CpuFamily.M6502)
|
||||
env2.collectDeclarations(program, options)
|
||||
val assembler = new MosAssembler(program, env2, platform)
|
||||
val output = assembler.assemble(callGraph, assemblyOptimizations, options)
|
||||
|
@ -12,7 +12,7 @@ import millfork.node.StandardCallGraph
|
||||
import millfork.node.opt.NodeOptimization
|
||||
import millfork.output.{MemoryBank, MosAssembler, Z80Assembler}
|
||||
import millfork.parser.Z80Parser
|
||||
import millfork.CompilationOptions
|
||||
import millfork.{CompilationOptions, CpuFamily}
|
||||
import millfork.compiler.z80.Z80Compiler
|
||||
import org.scalatest.Matchers
|
||||
|
||||
@ -21,8 +21,6 @@ import org.scalatest.Matchers
|
||||
*/
|
||||
class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], assemblyOptimizations: List[AssemblyOptimization[ZLine]]) extends Matchers {
|
||||
|
||||
private val variableLength = Set(0x10, 0x30, 0x50, 0x70, 0x90, 0xb0, 0xd0, 0xf0)
|
||||
|
||||
private val TooManyCycles: Long = 1000000
|
||||
|
||||
def apply2(source: String): (Timings, MemoryBank) = {
|
||||
@ -30,7 +28,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
|
||||
Console.err.flush()
|
||||
println(source)
|
||||
val platform = EmuPlatform.get(cpu)
|
||||
val options = CompilationOptions(platform, millfork.Cpu.defaultFlags(cpu).map(_ -> true).toMap, None)
|
||||
val options = CompilationOptions(platform, millfork.Cpu.defaultFlags(cpu).map(_ -> true).toMap, None, 0)
|
||||
ErrorReporting.hasErrors = false
|
||||
ErrorReporting.verbosity = 999
|
||||
var effectiveSource = source
|
||||
@ -44,7 +42,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
|
||||
// prepare
|
||||
val program = nodeOptimizations.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
|
||||
val callGraph = new StandardCallGraph(program)
|
||||
val env = new Environment(None, "")
|
||||
val env = new Environment(None, "", CpuFamily.I80)
|
||||
env.collectDeclarations(program, options)
|
||||
|
||||
val hasOptimizations = assemblyOptimizations.nonEmpty
|
||||
@ -64,7 +62,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
|
||||
|
||||
|
||||
// compile
|
||||
val env2 = new Environment(None, "")
|
||||
val env2 = new Environment(None, "", CpuFamily.I80)
|
||||
env2.collectDeclarations(program, options)
|
||||
val assembler = new Z80Assembler(program, env2, platform)
|
||||
val output = assembler.assemble(callGraph, assemblyOptimizations, options)
|
||||
|
Loading…
Reference in New Issue
Block a user