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