1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-07-13 15:29:01 +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:
Karol Stasiak 2018-07-06 22:45:59 +02:00
parent 55b42645be
commit a00ba49820
39 changed files with 862 additions and 250 deletions

View File

@ -5,7 +5,7 @@ import millfork.error.ErrorReporting
/**
* @author Karol Stasiak
*/
case class CompilationOptions(platform: Platform, commandLineFlags: Map[CompilationFlag.Value, Boolean], outputFileName: Option[String]) {
case class CompilationOptions(platform: Platform, commandLineFlags: Map[CompilationFlag.Value, Boolean], outputFileName: Option[String], zpRegisterSize: Int) {
import CompilationFlag._
import Cpu._
@ -21,7 +21,7 @@ case class CompilationOptions(platform: Platform, commandLineFlags: Map[Compilat
if (CpuFamily.forType(platform.cpu) != CpuFamily.M6502) invalids ++= Set(
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
ZeropagePseudoregister, PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, LUnixRelocatableCode, RorWarning)
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, LUnixRelocatableCode, RorWarning)
if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, UseIxForStack)
@ -30,6 +30,9 @@ case class CompilationOptions(platform: Platform, commandLineFlags: Map[Compilat
if (invalids.nonEmpty) {
ErrorReporting.error("Invalid flags enabled for the currect CPU family: " + invalids.mkString(", "))
}
if (CpuFamily.forType(platform.cpu) != CpuFamily.M6502 && zpRegisterSize > 0) {
ErrorReporting.error("Invalid flags enabled for the currect CPU family: zp_register" + invalids.mkString(", "))
}
CpuFamily.forType(platform.cpu) match {
case CpuFamily.M6502 =>
if (flags(DecimalMode)) {
@ -133,7 +136,7 @@ object Cpu extends Enumeration {
import CompilationFlag._
private val mosAlwaysDefaultFlags = Set(
VariableOverlap, CompactReturnDispatchParams, ZeropagePseudoregister
VariableOverlap, CompactReturnDispatchParams
)
private val i8080AlwaysDefaultFlags = Set(
@ -203,7 +206,7 @@ object CompilationFlag extends Enumeration {
EmitIllegals, DecimalMode, ReadOnlyArrays,
// compilation options for MOS:
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
ZeropagePseudoregister, PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator,
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator,
// compilation options for Z80
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, UseIxForStack,
// optimization options:
@ -236,7 +239,6 @@ object CompilationFlag extends Enumeration {
"ipo" -> InterproceduralOptimization,
"inline" -> InlineFunctions,
"dangerous_optimizations" -> DangerousOptimizations,
"zeropage_register" -> ZeropagePseudoregister,
"decimal_mode" -> DecimalMode,
"ro_arrays" -> ReadOnlyArrays,
"ror_warn" -> RorWarning,

View File

@ -24,6 +24,7 @@ case class Context(inputFileNames: List[String],
outputFileName: Option[String] = None,
runFileName: Option[String] = None,
optimizationLevel: Option[Int] = None,
zpRegisterSize: Option[Int] = None,
platform: Option[String] = None,
outputAssembly: Boolean = false,
outputLabels: Boolean = false,
@ -73,7 +74,7 @@ object Main {
ErrorReporting.info("No platform selected, defaulting to `c64`")
"c64"
})
val options = CompilationOptions(platform, c.flags, c.outputFileName)
val options = CompilationOptions(platform, c.flags, c.outputFileName, c.zpRegisterSize.getOrElse(platform.zpRegisterSize))
ErrorReporting.debug("Effective flags: ")
options.flags.toSeq.sortBy(_._1).foreach{
case (f, b) => ErrorReporting.debug(f" $f%-30s : $b%s")
@ -155,7 +156,7 @@ object Main {
}
val callGraph = new StandardCallGraph(program)
val env = new Environment(None, "")
val env = new Environment(None, "", platform.cpuFamily)
env.collectDeclarations(program, options)
val assemblyOptimizations = optLevel match {
@ -166,7 +167,7 @@ object Main {
val goodExtras = List(
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) SixteenOptimizations.AllForEmulation else Nil,
if (options.flag(CompilationFlag.EmitNative65816Opcodes)) SixteenOptimizations.AllForNative else Nil,
if (options.flag(CompilationFlag.ZeropagePseudoregister)) ZeropageRegisterOptimizations.All else Nil,
if (options.zpRegisterSize > 0) ZeropageRegisterOptimizations.All else Nil
).flatten
val extras = List(
if (options.flag(CompilationFlag.EmitIllegals)) UndocumentedOptimizations.All else Nil,
@ -206,7 +207,7 @@ object Main {
}
val callGraph = new StandardCallGraph(program)
val env = new Environment(None, "")
val env = new Environment(None, "", platform.cpuFamily)
env.collectDeclarations(program, options)
val assemblyOptimizations = optLevel match {
@ -305,7 +306,7 @@ object Main {
c.changeFlag(CompilationFlag.EmitIllegals, v)
}.description("Whether should emit illegal (undocumented) NMOS opcodes. Requires -O2 or higher to have an effect.")
boolean("-fzp-register", "-fno-zp-register").action { (c, v) =>
c.changeFlag(CompilationFlag.ZeropagePseudoregister, v)
c.copy(zpRegisterSize = Some(if (v) 2 else 0)) // TODO
}.description("Whether should use 2 bytes of zeropage as a pseudoregister.")
boolean("-fjmp-fix", "-fno-jmp-fix").action { (c, v) =>
c.changeFlag(CompilationFlag.PreventJmpIndirectBug, v)

View File

@ -81,7 +81,10 @@ object OptimizationPresets {
AlwaysGoodOptimizations.PointlessStackStashing,
AlwaysGoodOptimizations.PointlessStashingForLaterStore,
AlwaysGoodOptimizations.PointlessStashingForLaterLoad,
AlwaysGoodOptimizations.LoadingOfJustWrittenValue,
AlwaysGoodOptimizations.PointlessStackStore,
AlwaysGoodOptimizations.RearrangeMath,
AlwaysGoodOptimizations.LoadingOfJustWrittenValue,
EmptyMemoryStoreRemoval,
AlwaysGoodOptimizations.PointlessLoadBeforeReturn,
LaterOptimizations.PointessLoadingForShifting,
@ -128,6 +131,8 @@ object OptimizationPresets {
AlwaysGoodOptimizations.PointlessMathFromFlow,
AlwaysGoodOptimizations.PointlessMathFromFlow,
AlwaysGoodOptimizations.PointlessMathFromFlow,
AlwaysGoodOptimizations.LoadingOfJustWrittenValue,
AlwaysGoodOptimizations.PointlessStackStore,
AlwaysGoodOptimizations.OptimizeZeroComparisons,
AlwaysGoodOptimizations.SimplifiableCondition,
AlwaysGoodOptimizations.IncrementingIndexRegistersAfterTransfer,
@ -141,6 +146,8 @@ object OptimizationPresets {
LaterOptimizations.IncreaseWithLimit,
SingleAssignmentVariableOptimization,
LocalVariableReadOptimization,
AlwaysGoodOptimizations.PointlessStackStore,
AlwaysGoodOptimizations.SimplifiableStackOperation,
LaterOptimizations.UseBit,
)
@ -167,6 +174,7 @@ object OptimizationPresets {
AlwaysGoodOptimizations.IndexComparisonOptimization,
AlwaysGoodOptimizations.IndexSequenceOptimization,
AlwaysGoodOptimizations.InefficientStashingToRegister,
AlwaysGoodOptimizations.LoadingOfJustWrittenValue,
AlwaysGoodOptimizations.LoopInvariantRegister,
LoopUnrolling.LoopUnrolling,
AlwaysGoodOptimizations.MathOperationOnTwoIdenticalMemoryOperands,
@ -192,6 +200,7 @@ object OptimizationPresets {
AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeReturn,
AlwaysGoodOptimizations.PointlessSignCheck,
AlwaysGoodOptimizations.PointlessStackStashing,
AlwaysGoodOptimizations.PointlessStackStore,
AlwaysGoodOptimizations.PointlessStashingForLaterLoad,
AlwaysGoodOptimizations.PointlessStashingForLaterStore,
AlwaysGoodOptimizations.PointlessStashingToIndexOverShortSafeBranch,

View File

@ -23,6 +23,7 @@ class Platform(
val outputPackager: OutputPackager,
val codeAllocators: Map[String, UpwardByteAllocator],
val variableAllocators: Map[String, VariableAllocator],
val zpRegisterSize: Int,
val freeZpPointers: List[Int],
val fileExtension: String,
val generateBbcMicroInfFile: Boolean,
@ -87,6 +88,15 @@ object Platform {
}
}
val startingModules = cs.get(classOf[String], "modules", "").split("[, ]+").filter(_.nonEmpty).toList
val zpRegisterSize = cs.get(classOf[String], "zeropage_register", "2").toLowerCase match {
case "" | null => if (CpuFamily.forType(cpu) == CpuFamily.M6502) 2 else 0
case "true" | "on" | "yes" => 2
case "false" | "off" | "no" | "0" => 0
case x => x.toInt
}
if (zpRegisterSize < 0 || zpRegisterSize > 128) {
ErrorReporting.error("Invalid zeropage register size: " + zpRegisterSize)
}
val as = conf.getSection("allocation")
@ -178,6 +188,7 @@ object Platform {
new Platform(cpu, flagOverrides, startingModules, outputPackager,
codeAllocators.toMap,
variableAllocators.toMap,
zpRegisterSize,
freePointers,
if (fileExtension == "" || fileExtension.startsWith(".")) fileExtension else "." + fileExtension,
generateBbcMicroInfFile,
@ -188,7 +199,7 @@ object Platform {
def parseNumberOrRange(s:String): Seq[Int] = {
if (s.contains("-")) {
var segments = s.split("-")
val segments = s.split("-")
if (segments.length != 2) {
ErrorReporting.fatal(s"Invalid range: `$s`")
}

View File

@ -229,7 +229,7 @@ object OpcodeClasses {
)
val ConcernsStackAlways = ChangesStack ++ Set(TSX, TSY, TSC)
val ConcernsS = ChangesS ++ Set(TSX, TSY, TSC)
val ConcernsSAlways = ChangesS ++ Set(TSX, TSY, TSC)
val ChangesNAndZ = Set(
ADC, AND, ASL, BIT, CMP, CPX, CPY, DEC, DEX, DEY, EOR, INC, INX, INY, LDA,

View File

@ -31,6 +31,13 @@ object AlwaysGoodOptimizations {
(HasOpcode(LDA) & HasImmediate(0) & Elidable) ~
(HasOpcode(CLC) & Elidable) ~
(HasOpcode(ADC) & Elidable & DoesntMatterWhatItDoesWith(State.C, State.Z, State.V, State.N)) ~~> (code => code(2).copy(opcode = LDA) :: code.drop(3)),
(HasOpcode(CLC) & Elidable) ~
(HasOpcode(ADC) & MatchImmediate(0) & Elidable) ~
(HasOpcode(CLC) & Elidable) ~
(HasOpcode(ADC) & MatchImmediate(1) & Elidable & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~> ((code, ctx) => List(
AssemblyLine.implied(CLC),
AssemblyLine.immediate(ADC, (ctx.get[Constant](0) + ctx.get[Constant](1)).quickSimplify),
)),
)
val PointlessAccumulatorShifting = new RuleBasedAssemblyOptimization("Pointless accumulator shifting",
@ -889,6 +896,91 @@ object AlwaysGoodOptimizations {
},
)
val LoadingOfJustWrittenValue = new RuleBasedAssemblyOptimization("Loading of just written value",
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
(HasOpcode(STA) & XContainsStackPointer & HasAddrMode(AbsoluteX) & MatchAddrMode(0) & MatchParameter(1) & MatchA(2) & HasParameterWhere(_ match {
case NumericConstant(addr, _) => addr >= 0x100 && addr <= 0x1ff
case _ => false
})) ~
(HasOpcode(JSR) |
XContainsStackPointer & HasOpcodeIn(INC, DEC, ASL, LSR) & MatchAddrMode(0) & MatchParameter(1) |
Linear & XContainsStackPointer & HasAddrMode(AbsoluteX) & Not(MatchParameter(1)) & Not(HasOpcodeIn(OpcodeClasses.AccessesWordInMemory)) |
Linear & HasAddrMode(AbsoluteX) & Not(MatchParameter(1) & XContainsStackPointer) & Not(ChangesMemory) |
Linear & Not(ChangesS) & DoesntChangeMemoryAt(0, 1) & Not(HasAddrMode(AbsoluteX))).* ~
(Elidable & XContainsStackPointer & HasOpcodeIn(LDA, LDX, LDY, ADC, SBC, ORA, EOR, AND, CMP) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
val oldA = ctx.get[Int](2)
val ADDR = ctx.get[Constant](1)
val value = code.foldLeft(oldA) { (prev, line) =>
line match {
case AssemblyLine(INC, AbsoluteX, ADDR, _) => (prev + 1) & 0xff
case AssemblyLine(DEC, AbsoluteX, ADDR, _) => (prev - 1) & 0xff
case AssemblyLine(ASL, AbsoluteX, ADDR, _) => (prev << 1) & 0xff
case AssemblyLine(LSR, AbsoluteX, ADDR, _) => (prev >> 1) & 0xff
case _ => prev
}
}
code.init :+ code.last.copy(addrMode = AddrMode.Immediate, parameter = NumericConstant(value, 1))
},
(HasOpcode(STA) & HasAddrMode(Stack) & MatchAddrMode(0) & MatchParameter(1) & MatchA(2)) ~
(HasOpcode(JSR) |
Linear & HasAddrMode(Stack) & Not(MatchParameter(1)) & Not(HasOpcodeIn(OpcodeClasses.AccessesWordInMemory)) |
Linear & Not(ChangesS) & DoesntChangeMemoryAt(0, 1) & Not(HasAddrMode(Stack))).* ~
(Elidable & HasOpcodeIn(LDA, LDX, LDY, ADC, SBC, ORA, EOR, AND, CMP) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
code.init :+ code.last.copy(addrMode = AddrMode.Immediate, parameter = NumericConstant(ctx.get[Int](2), 1))
},
(HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & MatchA(2)) ~
(Linear & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~
(Elidable & HasOpcodeIn(LDA, LDX, LDY, ADC, SBC, ORA, EOR, AND, CMP) & MatchAddrMode(0) & MatchParameter(1)) ~~> { (code, ctx) =>
code.init :+ code.last.copy(addrMode = AddrMode.Immediate, parameter = NumericConstant(ctx.get[Int](2), 1))
},
)
val PointlessStackStore = new RuleBasedAssemblyOptimization("Pointless stack store",
needsFlowInfo = FlowInfoRequirement.BothFlows,
// TODO: check if JSR is OK
(Elidable & HasOpcode(STA) & HasAddrMode(AbsoluteX) & XContainsStackPointer & HasParameterWhere(_ match {
case NumericConstant(addr, _) => addr >= 0x100 && addr <= 0x1ff
case _ => false
}) & MatchParameter(1)) ~
(HasOpcode(JSR) |
Not(ChangesS) & Not(HasOpcodeIn(RTS, RTI)) & Linear & Not(HasAddrMode(AbsoluteX)) |
HasAddrMode(AbsoluteX) & XContainsStackPointer & Not(MatchParameter(1)) & Not(HasOpcodeIn(OpcodeClasses.AccessesWordInMemory))).* ~
HasOpcodeIn(RTS, RTI) ~~> (_.tail),
(Elidable & HasOpcode(STA) & HasAddrMode(AbsoluteX) & HasParameterWhere(_ match {
case NumericConstant(addr, _) => addr >= 0x100 && addr <= 0x1ff
case _ => false
})) ~
(HasOpcode(JSR) | Not(HasOpcodeIn(RTS, RTI)) & Linear & Not(HasAddrMode(AbsoluteX))).* ~
HasOpcodeIn(RTS, RTI) ~~> (_.tail),
(Elidable & HasOpcodeIn(INC, DEC, ASL, LSR, ROR, ROL) & HasAddrMode(AbsoluteX) & HasParameterWhere(_ match {
case NumericConstant(addr, _) => addr >= 0x100 && addr <= 0x1ff
case _ => false
}) & DoesntMatterWhatItDoesWith(State.Z, State.N, State.C)) ~
(HasOpcode(JSR) | Not(HasOpcodeIn(RTS, RTI)) & Linear & Not(HasAddrMode(AbsoluteX))).* ~
HasOpcodeIn(RTS, RTI) ~~> (_.tail),
(Elidable & HasOpcodeIn(STA, STA_W) & HasAddrMode(Stack)) ~
(HasOpcode(JSR) | Linear & Not(HasAddrMode(Stack)) & Not(HasOpcodeIn(RTS, RTI))).* ~
HasOpcodeIn(RTS, RTI) ~~> (_.tail),
(HasOpcode(STA) & XContainsStackPointer & HasAddrMode(AbsoluteX) & MatchAddrMode(0) & MatchParameter(1) & HasParameterWhere(_ match {
case NumericConstant(addr, _) => addr >= 0x100 && addr <= 0x1ff
case _ => false
})) ~
(HasOpcode(JSR) |
Linear & XContainsStackPointer & HasAddrMode(AbsoluteX) & Not(MatchParameter(1)) & Not(HasOpcodeIn(OpcodeClasses.AccessesWordInMemory)) |
Linear & Not(ChangesS) & Not(HasAddrMode(AbsoluteX)) & DoesNotConcernMemoryAt(0, 1)).* ~
(Elidable & XContainsStackPointer & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1)) ~~> (_.tail),
)
val IdempotentDuplicateRemoval = new RuleBasedAssemblyOptimization("Idempotent duplicate operation",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
HasOpcode(RTS) ~ NoopDiscardsFlags.* ~ (HasOpcode(RTS) ~ Elidable) ~~> (_.take(1)) ::
@ -1252,6 +1344,17 @@ object AlwaysGoodOptimizations {
(Elidable & HasOpcode(INX)) ~
(Elidable & HasOpcode(TXS)) ~
(ConcernsA & Not(ConcernsStack) & Linear & DoesntMatterWhatItDoesWith(State.Z, State.N, State.A)) ~~> (code => List(code.last, AssemblyLine.implied(PLA))),
(Elidable & HasOpcodeIn(PHA, PHX, PHY)).*.captureLength(0) ~
(HasOpcode(TSX) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~
(Linear & Not(ConcernsS) & Not(ConcernsX) & Not(ChangesStack)).* ~
(Elidable & HasOpcode(INX)).*.captureLength(1) ~
Where(ctx => ctx.get[Int](0) >= ctx.get[Int](1)) ~
(HasOpcode(TXS) & DoesntMatterWhatItDoesWith(State.Z, State.N, State.X)) ~~> {(code, ctx) =>
val pushCount = ctx.get[Int](0)
val inxCount = ctx.get[Int](1)
code.take(pushCount - inxCount) ++ code.drop(pushCount + 1).dropRight(inxCount + 1)
},
)
val SimplifiableBitOpsSequence = new RuleBasedAssemblyOptimization("Simplifiable sequence of bit operations",

View File

@ -90,7 +90,9 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
// case _ =>
// }
override def toString: String = s"A=$a,B=$ah,X=$x,Y=$y,Z=$iz; Z=$z,N=$n,C=$c,V=$v,D=$d,M=$m,X=$w; A7=$a7,A0=$a0,NZ:$src"
override def toString: String = s"A=$a,B=$ah,X=$x,Y=$y,Z=$iz; Z=$z,N=$n,C=$c,V=$v,D=$d,M=$m,X=$w; A7=$a7,A0=$a0,NZ:$src" +
(if (eqSX) "; S=X"
else /* */ " ")
def aw: Status[Int] = (ah, a) match {
case (SingleStatus(h), SingleStatus(l)) => SingleStatus(h.&(0xff).<<(8).+(l&0xff))

View File

@ -1,6 +1,5 @@
package millfork.assembly.mos.opt
import millfork.CompilationOptions
import millfork.assembly.OptimizationContext
import millfork.assembly.mos.{AssemblyLine, Opcode, State}
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
@ -12,6 +11,8 @@ import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
class FlowHolder(_statusBefore: () => List[CpuStatus], _importanceAfter: () => List[CpuImportance]) {
lazy val statusBefore: List[CpuStatus] = _statusBefore()
lazy val importanceAfter: List[CpuImportance] = _importanceAfter()
def toString(index: Int): String = statusBefore(index).toString ++ " -> " ++ importanceAfter(index).toString
}
case class FlowInfo(holder: FlowHolder, index: Int, _labelUseCountMap: () => Option[Map[String, Int]]) {
@ -27,6 +28,8 @@ case class FlowInfo(holder: FlowHolder, index: Int, _labelUseCountMap: () => Opt
def isUnimportant(state: State.Value): Boolean = importanceAfter.isUnimportant(state)
def labelUseCount(label: String): Int = labelUseCountMap.map(_.getOrElse(label, 0)).getOrElse(-1)
override def toString: String = holder.toString(index)
}
object FlowAnalyzer {

View File

@ -96,7 +96,7 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
private val map = mutable.Map[Int, Any]()
override def toString: String = map.mkString(", ")
override def toString: String = if (map.isEmpty) "<empty context>" else map.mkString(", ")
def addObject(i: Int, o: Any): Boolean = {
if (map.contains(i)) {
@ -774,6 +774,13 @@ case object ConcernsX extends TrivialAssemblyLinePattern {
OpcodeClasses.ConcernsXAlways(line.opcode) || XAddrModes(line.addrMode)
}
case object ConcernsS extends TrivialAssemblyLinePattern {
val SAddrModes = Set(AddrMode.Stack, AddrMode.IndexedSY)
override def apply(line: AssemblyLine): Boolean =
OpcodeClasses.ConcernsSAlways(line.opcode) || SAddrModes(line.addrMode)
}
case object ConcernsY extends TrivialAssemblyLinePattern {
val YAddrModes = Set(AddrMode.AbsoluteY, AddrMode.IndexedSY, AddrMode.IndexedY, AddrMode.LongIndexedY, AddrMode.ZeroPageY)

View File

@ -38,7 +38,7 @@ object SuperOptimizer extends AssemblyOptimization[AssemblyLine] {
if (options.flag(CompilationFlag.Emit65CE02Opcodes)) {
allOptimizers ++= CE02Optimizations.All
}
if (options.flag(CompilationFlag.ZeropagePseudoregister)) {
if (options.zpRegisterSize > 0) {
allOptimizers ++= ZeropageRegisterOptimizations.All
}
allOptimizers ++= List(

View File

@ -20,22 +20,56 @@ case class IfFlagSet(flag: ZFlag.Value) extends ZRegisters
case class IfFlagClear(flag: ZFlag.Value) extends ZRegisters
case class OneRegister(register: ZRegister.Value) extends ZRegisters
case class OneRegister(register: ZRegister.Value) extends ZRegisters {
if (register == ZRegister.MEM_IY_D || register == ZRegister.MEM_IX_D) ???
}
case class TwoRegisters(target: ZRegister.Value, source: ZRegister.Value) extends ZRegisters
case class TwoRegisters(target: ZRegister.Value, source: ZRegister.Value) extends ZRegisters {
if (target == ZRegister.MEM_IY_D || target == ZRegister.MEM_IX_D) ???
if (source == ZRegister.MEM_IY_D || source == ZRegister.MEM_IX_D) ???
}
case class OneRegisterOffset(register: ZRegister.Value, offset: Int) extends ZRegisters {
if (register != ZRegister.MEM_IY_D && register != ZRegister.MEM_IX_D) ???
}
case class TwoRegistersOffset(target: ZRegister.Value, source: ZRegister.Value, offset: Int) extends ZRegisters {
if (target != ZRegister.MEM_IY_D && target != ZRegister.MEM_IX_D && source != ZRegister.MEM_IY_D && source != ZRegister.MEM_IX_D) ???
}
sealed abstract class LocalVariableAddressOperand(val memViaRegister: ZRegister.Value) {
def offsetConstant: Constant
def offset: Int
def oneRegister: ZRegisters
def asTargetWithSource(reg: ZRegister.Value): ZRegisters
def asSourceWithTarget(reg: ZRegister.Value): ZRegisters
}
case class LocalVariableAddressViaIX(offset: Int) extends LocalVariableAddressOperand(ZRegister.MEM_IX_D) {
override def offsetConstant: Constant = NumericConstant(offset, 1)
override def oneRegister: ZRegisters = OneRegisterOffset(ZRegister.MEM_IX_D, offset)
override def asTargetWithSource(reg: ZRegister.Value): ZRegisters = TwoRegistersOffset(ZRegister.MEM_IX_D, reg, offset)
override def asSourceWithTarget(reg: ZRegister.Value): ZRegisters = TwoRegistersOffset(reg, ZRegister.MEM_IX_D, offset)
}
case class LocalVariableAddressViaIY(offset: Int) extends LocalVariableAddressOperand(ZRegister.MEM_IY_D) {
override def offsetConstant: Constant = NumericConstant(offset, 1)
override def oneRegister: ZRegisters = OneRegisterOffset(ZRegister.MEM_IY_D, offset)
override def asTargetWithSource(reg: ZRegister.Value): ZRegisters = TwoRegistersOffset(ZRegister.MEM_IY_D, reg, offset)
override def asSourceWithTarget(reg: ZRegister.Value): ZRegisters = TwoRegistersOffset(reg, ZRegister.MEM_IY_D, offset)
}
case object LocalVariableAddressViaHL extends LocalVariableAddressOperand(ZRegister.MEM_HL) {
override def offsetConstant: Constant = Constant.Zero
override def offset: Int = 0
override def oneRegister: ZRegisters = OneRegister(ZRegister.MEM_HL)
override def asTargetWithSource(reg: ZRegister.Value): ZRegisters = TwoRegisters(ZRegister.MEM_HL, reg)
override def asSourceWithTarget(reg: ZRegister.Value): ZRegisters = TwoRegisters(reg, ZRegister.MEM_HL)
}
object ZLine {
@ -63,7 +97,7 @@ object ZLine {
def register(opcode: ZOpcode.Value, register: ZRegister.Value): ZLine = ZLine(opcode, OneRegister(register), Constant.Zero)
def register(opcode: ZOpcode.Value, register: LocalVariableAddressOperand): ZLine = ZLine(opcode, OneRegister(register.memViaRegister), register.offsetConstant)
def register(opcode: ZOpcode.Value, register: LocalVariableAddressOperand): ZLine = ZLine(opcode, register.oneRegister, Constant.Zero)
def imm8(opcode: ZOpcode.Value, value: Constant): ZLine = ZLine(opcode, OneRegister(IMM_8), value)
@ -73,12 +107,16 @@ object ZLine {
def ld8(target: ZRegister.Value, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegisters(target, source), Constant.Zero)
def ld8(target: LocalVariableAddressOperand, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegisters(target.memViaRegister, source), target.offsetConstant)
def ld8(target: LocalVariableAddressOperand, source: ZRegister.Value): ZLine = ZLine(LD, target.asTargetWithSource(source), Constant.Zero)
def ld8(target: ZRegister.Value, source: LocalVariableAddressOperand): ZLine = ZLine(LD, TwoRegisters(target, source.memViaRegister), source.offsetConstant)
def ld8(target: ZRegister.Value, source: LocalVariableAddressOperand): ZLine = ZLine(LD, source.asSourceWithTarget(target), Constant.Zero)
def ld16(target: ZRegister.Value, source: ZRegister.Value): ZLine = ZLine(LD_16, TwoRegisters(target, source), Constant.Zero)
def ldImm8(target: LocalVariableAddressOperand, source: Int): ZLine = ZLine(LD, target.asTargetWithSource(IMM_8), NumericConstant(source & 0xff, 1))
def ldImm8(target: LocalVariableAddressOperand, source: Constant): ZLine = ZLine(LD, target.asTargetWithSource(IMM_8), source)
def ldImm8(target: ZRegister.Value, source: Int): ZLine = ZLine(LD, TwoRegisters(target, IMM_8), NumericConstant(source & 0xff, 1))
def ldImm8(target: ZRegister.Value, source: Constant): ZLine = ZLine(LD, TwoRegisters(target, IMM_8), source)
@ -103,9 +141,9 @@ object ZLine {
def ldAbs16(target: Constant, source: ZRegister.Value): ZLine = ZLine(LD_16, TwoRegisters(MEM_ABS_16, source), target)
def ldViaIx(target: ZRegister.Value, sourceOffset: Int): ZLine = ZLine(LD, TwoRegisters(target, ZRegister.MEM_IX_D), NumericConstant(sourceOffset, 1))
def ldViaIx(target: ZRegister.Value, sourceOffset: Int): ZLine = ZLine(LD, TwoRegistersOffset(target, ZRegister.MEM_IX_D, sourceOffset), Constant.Zero)
def ldViaIx(targetOffset: Int, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegisters(ZRegister.MEM_IX_D, source), NumericConstant(targetOffset, 1))
def ldViaIx(targetOffset: Int, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegistersOffset(ZRegister.MEM_IX_D, source, targetOffset), Constant.Zero)
}
case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Constant, elidable: Boolean = true) extends AbstractCode {
@ -141,7 +179,7 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
override def isPrintable: Boolean = true
private def asAssemblyString(r: ZRegister.Value): String = r match {
private def asAssemblyString(r: ZRegister.Value, offset: Int = 0): String = r match {
case ZRegister.A => "A"
case ZRegister.B => "B"
case ZRegister.C => "C"
@ -166,8 +204,8 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
case ZRegister.MEM_ABS_16 => s"($parameter)"
case ZRegister.IMM_8 => s"#$parameter"
case ZRegister.IMM_16 => s"#$parameter"
case ZRegister.MEM_IX_D => s"(IX,$parameter)"
case ZRegister.MEM_IY_D => s"(IY,$parameter)"
case ZRegister.MEM_IX_D => s"(IX,$offset)"
case ZRegister.MEM_IY_D => s"(IY,$offset)"
case ZRegister.MEM_HL => "(HL)"
case ZRegister.MEM_BC => "(BC)"
case ZRegister.MEM_DE => "(DE)"
@ -199,8 +237,8 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
case OneRegister(r) => s" (${asAssemblyString(r)})"
}
s" $opcode$ps"
case o =>
val os = o.toString//.stripSuffix("_16")
case op =>
val os = op.toString//.stripSuffix("_16")
val ps = registers match {
case NoRegisters => ""
case IfFlagSet(ZFlag.P) => " PO"
@ -211,6 +249,8 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
case IfFlagClear(f) => s" N$f"
case OneRegister(r) => s" ${asAssemblyString(r)}"
case TwoRegisters(t, s) => s" ${asAssemblyString(t)},${asAssemblyString(s)}"
case TwoRegistersOffset(t, s, o) => s" ${asAssemblyString(t, o)},${asAssemblyString(s, o)}"
case OneRegisterOffset(r, o) => s" ${asAssemblyString(r, o)}"
}
s" $os$ps"
}
@ -294,6 +334,26 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
}
}
def changesRegisterAndOffset(r: ZRegister.Value, o: Int): Boolean = {
import ZOpcode._
import ZRegister._
r match {
case MEM_IX_D | MEM_IY_D =>
opcode match {
case LD => registers match {
case TwoRegistersOffset(s, _, p) => r == s && o == p
case _ => false
}
case INC | DEC | RL | RLC | RR | RRC | SLA | SLL | SRA | SRL => registers match {
case OneRegisterOffset(s, p) => r == s && o == p
case _ => false
}
case _ => false // TODO
}
case _ => changesRegister(r)
}
}
def changesRegister(r: ZRegister.Value): Boolean = {
import ZOpcode._
import ZRegister._
@ -304,7 +364,8 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
case IX => changesRegister(IXH) || changesRegister(IXL)
case IY => changesRegister(IYH) || changesRegister(IYL)
case AF => ???
case MEM_ABS_8 | MEM_ABS_16 | IMM_8 | IMM_16 | MEM_DE | MEM_HL | MEM_BC | MEM_IX_D | MEM_IY_D | SP => ???
case IMM_8 | IMM_16 => false
case MEM_ABS_8 | MEM_ABS_16 | MEM_DE | MEM_HL | MEM_BC | MEM_IX_D | MEM_IY_D | SP => ???
case _ =>
opcode match {
case LD => registers match {

View File

@ -1,7 +1,7 @@
package millfork.assembly.z80.opt
import millfork.assembly.AssemblyOptimization
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZLine, ZOpcode}
import millfork.assembly.z80._
import millfork.assembly.z80.ZOpcode._
import millfork.env.{Constant, NumericConstant}
import millfork.node.ZRegister
@ -11,6 +11,13 @@ import millfork.node.ZRegister
*/
object AlwaysGoodZ80Optimizations {
def change8BitLoadTarget(line: ZLine, newTarget: ZRegister.Value): ZLine = {
line match {
case ZLine(LD, TwoRegistersOffset(_, s, o), p, _) => ZLine(LD, TwoRegistersOffset(newTarget, s, o), p)
case ZLine(LD, TwoRegisters(_, s), p, _) => ZLine(LD, TwoRegisters(newTarget, s), p)
}
}
def for7Registers(f: ZRegister.Value => AssemblyRuleSet) = MultipleAssemblyRules(
List(ZRegister.A, ZRegister.B, ZRegister.C, ZRegister.D, ZRegister.E, ZRegister.H, ZRegister.L).map(f))
@ -26,9 +33,23 @@ object AlwaysGoodZ80Optimizations {
(Elidable & IsRegular8BitLoadFrom(register) & MatchRegister(register, 0)) ~~> ((code, ctx) =>
code.map(x => x.copy(
parameter = NumericConstant(ctx.get[Int](0), 1),
registers = x.registers.asInstanceOf[TwoRegisters].copy(source = ZRegister.IMM_8)
registers = x.registers match {
case TwoRegisters(t, _) => TwoRegisters(t, ZRegister.IMM_8)
case TwoRegistersOffset(t@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), _, o) => TwoRegistersOffset(t, ZRegister.IMM_8, o)
case TwoRegistersOffset(t, ZRegister.MEM_IX_D | ZRegister.MEM_IY_D, _) => TwoRegisters(t, ZRegister.IMM_8)
}
)))
),
(Elidable & MatchSourceIxOffsetOf8BitLoad(0) & MatchValueAtMatchedIxOffset(0, 1)) ~~> ((code, ctx) =>
code.map(x => x.copy(
parameter = NumericConstant(ctx.get[Int](1), 1),
registers = x.registers match {
case TwoRegisters(t, _) => TwoRegisters(t, ZRegister.IMM_8)
case TwoRegistersOffset(t@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), _, o) => TwoRegistersOffset(t, ZRegister.IMM_8, o)
case TwoRegistersOffset(t, ZRegister.MEM_IX_D | ZRegister.MEM_IY_D, _) => TwoRegisters(t, ZRegister.IMM_8)
}
))
),
for6Registers(register =>
(Elidable & HasRegisterParam(register) & HasOpcodeIn(Set(AND, ADD, ADC, SUB, SBC, XOR, OR, CP)) & MatchRegister(register, 0)) ~~> ((code, ctx) =>
code.map(x => x.copy(
@ -36,6 +57,12 @@ object AlwaysGoodZ80Optimizations {
registers = OneRegister(ZRegister.IMM_8)
)))
),
(Elidable & MatchIxOffset(0) & HasOpcodeIn(Set(AND, ADD, ADC, SUB, SBC, XOR, OR, CP)) & MatchValueAtMatchedIxOffset(0, 1)) ~~> ((code, ctx) =>
code.map(x => x.copy(
parameter = NumericConstant(ctx.get[Int](1), 1),
registers = OneRegister(ZRegister.IMM_8)
))
),
)
val ReloadingKnownValueFromMemory = new RuleBasedAssemblyOptimization("Reloading known value from memory",
@ -88,6 +115,28 @@ object AlwaysGoodZ80Optimizations {
(Elidable & Is8BitLoadTo(ZRegister.MEM_BC)) ~
(Linear & Not(ConcernsMemory) & Not(Changes(ZRegister.BC))).* ~
Is8BitLoadTo(ZRegister.MEM_BC) ~~> (_.tail),
(Elidable & MatchTargetIxOffsetOf8BitLoad(0) & MatchUnimportantIxOffset(0)) ~~> (_ => Nil),
for7Registers(register =>
(Elidable & Is8BitLoadTo(register) & NoOffset & MatchSourceRegisterAndOffset(1)) ~
(Linear & Not(Concerns(register)) & DoesntChangeMatchedRegisterAndOffset(1)).* ~
(Elidable & IsRegular8BitLoadFrom(register) & DoesntMatterWhatItDoesWith(register)) ~~> { code =>
val last = code.last
val head = code.head
code.tail.init :+ ZLine(LD, (last.registers, head.registers) match {
case (TwoRegisters(t, _), TwoRegisters(_, s)) => TwoRegisters(t, s)
case (TwoRegistersOffset(t, _, o), TwoRegisters(_, s)) => TwoRegistersOffset(t, s, o)
case (TwoRegisters(t, _), TwoRegistersOffset(_, s, o)) => TwoRegistersOffset(t, s, o)
case _ => ???
}, head.parameter)
}
),
for7Registers(register =>
(Elidable & Is8BitLoad(register, register)) ~~> (_ => Nil)
),
)
private def simplifiable16BitAddWithSplitTarget(targetH: ZRegister.Value, targetL: ZRegister.Value, target: ZRegister.Value, source: ZRegister.Value) = MultipleAssemblyRules(List(
@ -137,7 +186,7 @@ object AlwaysGoodZ80Optimizations {
needsFlowInfo = FlowInfoRequirement.BothFlows,
for6Registers(register =>
(Elidable & HasOpcode(ADD) & MatchRegister(ZRegister.A, 0) & HasRegisterParam(register) & MatchRegister(register, 1) &
DoesntMatterWhatItDoesWithFlags) ~~> ((code, ctx) => List(ZLine.ldImm16(ZRegister.A, (ctx.get[Int](0) + ctx.get[Int](1)) & 0xff))),
DoesntMatterWhatItDoesWithFlags) ~~> ((code, ctx) => List(ZLine.ldImm8(ZRegister.A, (ctx.get[Int](0) + ctx.get[Int](1)) & 0xff))),
),
simplifiable16BitAddWithSplitTarget(ZRegister.H, ZRegister.L, ZRegister.HL, ZRegister.BC),
simplifiable16BitAddWithSplitTarget(ZRegister.H, ZRegister.L, ZRegister.HL, ZRegister.DE),
@ -145,6 +194,29 @@ object AlwaysGoodZ80Optimizations {
simplifiable16BitAddWithSplitTarget(ZRegister.IXH, ZRegister.IXL, ZRegister.IX, ZRegister.DE),
simplifiable16BitAddWithSplitTarget(ZRegister.IYH, ZRegister.IYL, ZRegister.IY, ZRegister.BC),
simplifiable16BitAddWithSplitTarget(ZRegister.IYH, ZRegister.IYL, ZRegister.IY, ZRegister.DE),
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L)) ~
(Elidable & Is8BitLoadTo(ZRegister.L)) ~
(Elidable & Is8BitLoadTo(ZRegister.H)) ~
(HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & DoesntMatterWhatItDoesWith(ZRegister.D, ZRegister.E)) ~~> { code =>
List(
change8BitLoadTarget(code(2), ZRegister.E),
change8BitLoadTarget(code(3), ZRegister.D),
code.last)
},
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L)) ~
(Elidable & Is8BitLoadTo(ZRegister.H)) ~
(Elidable & Is8BitLoadTo(ZRegister.L)) ~
(HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & DoesntMatterWhatItDoesWith(ZRegister.D, ZRegister.E)) ~~> { code =>
List(
change8BitLoadTarget(code(2), ZRegister.D),
change8BitLoadTarget(code(3), ZRegister.E),
code.last)
},
(Elidable & HasOpcodeIn(Set(ADD, OR, AND, XOR, SUB)) & Has8BitImmediate(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
)
val FreeHL = new RuleBasedAssemblyOptimization("Free HL",

View File

@ -1,10 +1,10 @@
package millfork.assembly.z80.opt
import millfork.assembly.opt.{AnyStatus, SingleStatus}
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZLine, ZOpcodeClasses}
import millfork.assembly.z80._
import millfork.env.{Label, MemoryAddressConstant, NormalFunction, NumericConstant}
import millfork.node.ZRegister
import millfork.{CompilationFlag, CompilationOptions}
import millfork.CompilationOptions
/**
* @author Karol Stasiak
@ -12,8 +12,6 @@ import millfork.{CompilationFlag, CompilationOptions}
object CoarseFlowAnalyzer {
def analyze(f: NormalFunction, code: List[ZLine], compilationOptions: CompilationOptions): List[CpuStatus] = {
val ceFlag = compilationOptions.flag(CompilationFlag.Emit65CE02Opcodes)
val cmosFlag = compilationOptions.flag(CompilationFlag.EmitCmosOpcodes)
val initialStatus = CpuStatus()
val functionStartStatus = CpuStatus()
val emptyStatus = CpuStatus()
@ -39,8 +37,11 @@ object CoarseFlowAnalyzer {
case _ => None
}).fold(currentStatus)(_ ~ _)
case ZLine(CALL | BYTE, _, _, _) =>
case ZLine(CALL, _, _, _) =>
currentStatus = initialStatus.copy(memIx = currentStatus.memIx)
case ZLine(BYTE, _, _, _) =>
currentStatus = initialStatus
case ZLine(ADD, OneRegister(s), _, _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s)) ((m, n) => (m + n) & 0xff),
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
@ -56,10 +57,34 @@ object CoarseFlowAnalyzer {
case ZLine(XOR, OneRegister(s), _, _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s)) ((m, n) => (m ^ n) & 0xff),
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(ADD, OneRegisterOffset(s, o), _, _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m + n) & 0xff),
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(SUB, OneRegisterOffset(s, o), _, _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m - n) & 0xff),
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(AND, OneRegisterOffset(s, o), _, _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m & n) & 0xff),
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(OR, OneRegisterOffset(s, o), _, _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m | n) & 0xff),
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(XOR, OneRegisterOffset(s, o), _, _) =>
currentStatus = currentStatus.copy(a = (currentStatus.a <*> currentStatus.getRegister(s, o)) ((m, n) => (m ^ n) & 0xff),
cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(CP, _, _, _) =>
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
case ZLine(LD, TwoRegisters(t, ZRegister.IMM_8), NumericConstant(value, _), _) =>
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt))
case ZLine(LD, TwoRegistersOffset(t, ZRegister.IMM_8, o), NumericConstant(value, _), _) =>
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt), o)
case ZLine(LD | LD_16, TwoRegisters(t, s), _, _) =>
currentStatus = currentStatus.setRegister(t, currentStatus.getRegister(s))
case ZLine(LD | LD_16, TwoRegistersOffset(t, s, o), _, _) =>
currentStatus = currentStatus.setRegister(t, currentStatus.getRegister(s, o), o)
case ZLine(ADD_16, TwoRegisters(t, s), _, _) =>
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
.setRegister(t, (currentStatus.getRegister(t) <*> currentStatus.getRegister(s)) ((m, n) => (m + n) & 0xffff))
@ -71,6 +96,8 @@ object CoarseFlowAnalyzer {
registers match {
case OneRegister(r) => currentStatus = currentStatus.setRegister(r, AnyStatus)
case TwoRegisters(r1, r2) => currentStatus = currentStatus.setRegister(r1, AnyStatus).setRegister(r2, AnyStatus)
case OneRegisterOffset(r1, o) => currentStatus = currentStatus.setRegister(r1, AnyStatus, o)
case TwoRegistersOffset(r1, r2, o) => currentStatus = currentStatus.setRegister(r1, AnyStatus, o).setRegister(r2, AnyStatus, o)
case _ => ()
}
}

View File

@ -1,7 +1,6 @@
//noinspection RedundantNewCaseClass
package millfork.assembly.z80.opt
import millfork.assembly.mos.State
import millfork.assembly.opt._
import millfork.assembly.z80.ZFlag
import millfork.node.ZRegister
@ -21,6 +20,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
ixl: Status[Int] = UnknownStatus,
iyh: Status[Int] = UnknownStatus,
iyl: Status[Int] = UnknownStatus,
memIx: Map[Int, Status[Int]] = Map(),
zf: Status[Boolean] = UnknownStatus,
nf: Status[Boolean] = UnknownStatus,
cf: Status[Boolean] = UnknownStatus,
@ -28,7 +28,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
pf: Status[Boolean] = UnknownStatus,
hf: Status[Boolean] = UnknownStatus
) {
def setRegister(target: ZRegister.Value, value: Status[Int]): CpuStatus = target match {
def setRegister(target: ZRegister.Value, value: Status[Int], offset: Int = -1): CpuStatus = target match {
case ZRegister.IMM_8 => this
case ZRegister.IMM_16 => this
case ZRegister.A => this.copy(a = value)
@ -42,7 +42,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
case ZRegister.IXL => this.copy(ixl = value)
case ZRegister.IYH => this.copy(iyh = value)
case ZRegister.IYL => this.copy(iyl = value)
case ZRegister.MEM_IX_D => this
case ZRegister.MEM_IX_D => if (offset < 0) this.copy(memIx = Map()) else this.copy(memIx = this.memIx + (offset -> value))
case ZRegister.MEM_IY_D => this
case ZRegister.MEM_HL => this
case ZRegister.MEM_BC => this
@ -62,7 +62,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
val mergeBytes: (Int, Int) => Int = (h, l) => (h & 0xff) * 256 + (l & 0xff)
def getRegister(register: ZRegister.Value): Status[Int] = register match {
def getRegister(register: ZRegister.Value, offset: Int = -1): Status[Int] = register match {
case ZRegister.A => a
case ZRegister.B => b
case ZRegister.C => c
@ -74,7 +74,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
case ZRegister.IXL => ixl
case ZRegister.IYH => iyh
case ZRegister.IYL => iyl
case ZRegister.MEM_IX_D => AnyStatus
case ZRegister.MEM_IX_D => if (offset < 0) AnyStatus else memIx.getOrElse(offset, UnknownStatus)
case ZRegister.MEM_IY_D => AnyStatus
case ZRegister.MEM_HL => AnyStatus
case ZRegister.MEM_BC => AnyStatus
@ -114,6 +114,7 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
ixl = this.ixl ~ that.ixl,
iyh = this.iyh ~ that.iyh,
iyl = this.iyl ~ that.iyl,
memIx = (this.memIx.keySet | that.memIx.keySet).map(k => k -> (this.memIx.getOrElse(k, UnknownStatus) ~ that.memIx.getOrElse(k, UnknownStatus))).toMap,
nf = this.nf ~ that.nf,
sf = this.sf ~ that.sf,
zf = this.zf ~ that.zf,
@ -123,6 +124,9 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
)
override def toString: String = {
val memRepr = if (memIx.isEmpty) "" else (0 to memIx.keys.max).map(i => memIx.getOrElse(i, UnknownStatus)).mkString("")
s"A=$a,B=$b,C=$c,D=$d,E=$e,H=$h,L=$l,IX=$ixh$ixl,Y=$iyh$iyl; Z=$zf,C=$cf,N=$nf,S=$sf,P=$pf,H=$hf; M=" + memRepr.padTo(4, ' ')
}
override def toString: String = s"A=$a,B=$b,C=$c,D=$d,E=$e,H=$h,L=$l,IX=$ixh$ixl,Y=$iyh$iyl; Z=$zf,C=$cf,N=$nf,S=$sf,P=$pf,H=$hf"
}

View File

@ -11,6 +11,8 @@ import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
class FlowHolder(_statusBefore: () => List[CpuStatus], _importanceAfter: () => List[CpuImportance]) {
lazy val statusBefore: List[CpuStatus] = _statusBefore()
lazy val importanceAfter: List[CpuImportance] = _importanceAfter()
def toString(index: Int): String = statusBefore(index).toString ++ " -> " ++ importanceAfter(index).toString
}
case class FlowInfo(holder: FlowHolder, index: Int, _labelUseCountMap: () => Option[Map[String, Int]]) {
@ -20,6 +22,8 @@ case class FlowInfo(holder: FlowHolder, index: Int, _labelUseCountMap: () => Opt
lazy val labelUseCountMap: Option[Map[String, Int]] = _labelUseCountMap()
def labelUseCount(label: String): Int = labelUseCountMap.map(_.getOrElse(label, 0)).getOrElse(-1)
override def toString: String = holder.toString(index)
}
object FlowAnalyzer {

View File

@ -40,6 +40,7 @@ case class CpuImportance(a: Importance = UnknownImportance,
ixl: Importance = UnknownImportance,
iyh: Importance = UnknownImportance,
iyl: Importance = UnknownImportance,
memIx: Map[Int, Importance] = Map(),
zf: Importance = UnknownImportance,
nf: Importance = UnknownImportance,
cf: Importance = UnknownImportance,
@ -47,7 +48,10 @@ case class CpuImportance(a: Importance = UnknownImportance,
pf: Importance = UnknownImportance,
hf: Importance = UnknownImportance
) {
override def toString: String = s"A=$a,B=$b,C=$c,D=$d,E=$e,H=$h,L=$l,IX=$ixh$ixl,Y=$iyh$iyl; Z=$zf,C=$cf,N=$nf,S=$sf,P=$pf,H=$hf"
override def toString: String = {
val memRepr = if (memIx.isEmpty) "" else (0 to memIx.keys.max).map(i => memIx.getOrElse(i, UnknownImportance)).mkString("")
s"A=$a,B=$b,C=$c,D=$d,E=$e,H=$h,L=$l,IX=$ixh$ixl,Y=$iyh$iyl; Z=$zf,C=$cf,N=$nf,S=$sf,P=$pf,H=$hf; M=" ++ memRepr.padTo(4, ' ')
}
def ~(that: CpuImportance) = new CpuImportance(
a = this.a ~ that.a,
@ -61,6 +65,7 @@ case class CpuImportance(a: Importance = UnknownImportance,
ixl = this.ixl ~ that.ixl,
iyh = this.iyh ~ that.iyh,
iyl = this.iyl ~ that.iyl,
memIx = (this.memIx.keySet | that.memIx.keySet).map(k => k -> (this.memIx.getOrElse(k, UnknownImportance) ~ that.memIx.getOrElse(k, UnknownImportance))).toMap,
zf = this.zf ~ that.zf,
nf = this.nf ~ that.nf,
cf = this.cf ~ that.cf,
@ -68,7 +73,7 @@ case class CpuImportance(a: Importance = UnknownImportance,
hf = this.hf ~ that.hf,
)
def getRegister(register: ZRegister.Value): Importance = register match {
def getRegister(register: ZRegister.Value, offset: Int = -1): Importance = register match {
case ZRegister.A => a
case ZRegister.B => b
case ZRegister.C => c
@ -80,6 +85,7 @@ case class CpuImportance(a: Importance = UnknownImportance,
case ZRegister.IXL => ixl
case ZRegister.IYH => iyh
case ZRegister.IYL => iyl
case ZRegister.MEM_IX_D => if (offset < 0) ??? else memIx.getOrElse(offset, UnknownImportance)
case ZRegister.HL => h ~ l
case ZRegister.BC => b ~ c
case ZRegister.DE => d ~ e
@ -96,7 +102,7 @@ case class CpuImportance(a: Importance = UnknownImportance,
case ZFlag.N => nf
}
def butReadsRegister(r: ZRegister.Value) = r match {
def butReadsRegister(r: ZRegister.Value, offset: Int = -1): CpuImportance = r match {
case ZRegister.A => this.copy(a = Important)
case ZRegister.AF => this.copy(a = Important, zf = Important, pf = Important, hf = Important, cf = Important, sf = Important)
case ZRegister.B => this.copy(b = Important)
@ -112,12 +118,13 @@ case class CpuImportance(a: Importance = UnknownImportance,
case ZRegister.IXL => this.copy(ixl = Important)
case ZRegister.IYH => this.copy(iyh = Important)
case ZRegister.IYL => this.copy(iyl = Important)
case ZRegister.IX | ZRegister.MEM_IX_D => this.copy(ixh = Important, ixl = Important)
case ZRegister.IX => this.copy(ixh = Important, ixl = Important)
case ZRegister.MEM_IX_D => this.copy(ixh = Important, ixl = Important, memIx = if (offset < 0) memIx.mapValues(_ => Important) else memIx + (offset -> Important))
case ZRegister.IY | ZRegister.MEM_IY_D => this.copy(iyh = Important, iyl = Important)
case _ => this
}
def butWritesRegister(r: ZRegister.Value): CpuImportance = r match {
def butWritesRegister(r: ZRegister.Value, offset: Int = -1): CpuImportance = r match {
case ZRegister.A => this.copy(a = Unimportant)
case ZRegister.AF => this.copy(a = Unimportant, zf = Unimportant, pf = Unimportant, hf = Unimportant, cf = Unimportant, sf = Unimportant, nf = Unimportant)
case ZRegister.B => this.copy(b = Unimportant)
@ -136,8 +143,8 @@ case class CpuImportance(a: Importance = UnknownImportance,
case ZRegister.IXL => this.copy(ixl = Unimportant)
case ZRegister.IYH => this.copy(iyh = Unimportant)
case ZRegister.IYL => this.copy(iyl = Unimportant)
case ZRegister.IX => this.copy(ixh = Unimportant, ixl = Unimportant)
case ZRegister.MEM_IX_D => this.copy(ixh = Important, ixl = Important)
case ZRegister.IX => this.copy(ixh = Unimportant, ixl = Unimportant, memIx = memIx.mapValues(_ => Unimportant))
case ZRegister.MEM_IX_D => this.copy(ixh = Important, ixl = Important, memIx = if (offset < 0) Map() else memIx + (offset -> Unimportant))
case ZRegister.IY => this.copy(iyh = Important, iyl = Important)
case ZRegister.MEM_IY_D => this.copy(iyh = Important, iyl = Important)
case _ => this
@ -208,22 +215,38 @@ object ReverseFlowAnalyzer {
currentImportance = currentImportance.copy(a = Unimportant)
case ZLine(DISCARD_F, _, _, _) =>
currentImportance = currentImportance.copy(cf = Unimportant, zf= Unimportant, sf = Unimportant , pf = Unimportant, hf = Unimportant)
case ZLine(LD, TwoRegistersOffset(t, s, o), _, _) =>
currentImportance = currentImportance.butWritesRegister(t, o).butReadsRegister(s, o)
case ZLine(LD | LD_16, TwoRegisters(t, s), _, _) =>
currentImportance = currentImportance.butWritesRegister(t).butReadsRegister(s)
case ZLine(ADD_16, TwoRegisters(t, s), _, _) =>
currentImportance = currentImportance.butReadsRegister(t).butReadsRegister(s)
case ZLine(XOR, OneRegister(ZRegister.A), _, _) =>
currentImportance = currentImportance.butWritesRegister(ZRegister.A)
case ZLine(OR | AND, OneRegister(ZRegister.A), _, _) =>
currentImportance = currentImportance.butReadsRegister(ZRegister.A)
case ZLine(AND | ADD | SUB | OR | XOR | CP, OneRegister(s), _, _) =>
currentImportance = currentImportance.butReadsRegister(ZRegister.A).butReadsRegister(s)
case ZLine(ADC | SBC, OneRegister(s), _, _) =>
currentImportance = currentImportance.butReadsRegister(ZRegister.A).butReadsRegister(s).butReadsFlag(ZFlag.C)
case ZLine(DAA, _, _, _) =>
currentImportance = currentImportance.butReadsRegister(ZRegister.A).butReadsFlag(ZFlag.H)
case ZLine(AND | ADD | SUB | OR | XOR | CP, OneRegisterOffset(s, o), _, _) =>
currentImportance = currentImportance.butReadsRegister(ZRegister.A).butReadsRegister(s, o)
case ZLine(ADC | SBC, OneRegisterOffset(s, o), _, _) =>
currentImportance = currentImportance.butReadsRegister(ZRegister.A).butReadsRegister(s, o).butReadsFlag(ZFlag.C)
case ZLine(INC | DEC | INC_16 | DEC_16, OneRegister(s), _, _) =>
currentImportance = currentImportance.butReadsRegister(s)
case ZLine(INC | DEC | INC_16 | DEC_16, OneRegisterOffset(s, o), _, _) =>
currentImportance = currentImportance.butReadsRegister(s, o)
case ZLine(POP, OneRegister(r), _, _) =>
currentImportance = currentImportance.butWritesRegister(r)
case ZLine(PUSH, OneRegister(r), _, _) =>
currentImportance = currentImportance.butReadsRegister(r)
case ZLine(CALL, NoRegisters, _, _) =>
currentImportance = finalImportance.copy(memIx = currentImportance.memIx)
case _ =>
currentImportance = finalImportance // TODO
}

View File

@ -1,8 +1,8 @@
package millfork.assembly.z80.opt
import millfork.CompilationOptions
import millfork.assembly.{z80, _}
import millfork.assembly.opt.SingleStatus
import millfork.assembly._
import millfork.assembly.opt.{AnyStatus, SingleStatus}
import millfork.assembly.z80._
import millfork.env._
import millfork.error.ErrorReporting
@ -437,6 +437,30 @@ case class MatchRegister(register: ZRegister.Value, i: Int) extends AssemblyLine
}
}
case class MatchValueAtIxOffset(offset: Int, i: Int) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
flowInfo.statusBefore.memIx.getOrElse(offset, AnyStatus) match {
case SingleStatus(value) => ctx.addObject(i, value)
case _ => false
}
}
case class MatchValueAtMatchedIxOffset(oi: Int, vi: Int) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
val offset = ctx.get[Int](oi)
flowInfo.statusBefore.memIx.getOrElse(offset, AnyStatus) match {
case SingleStatus(value) => ctx.addObject(vi, value)
case _ => false
}
}
}
case class MatchImmediate(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
line.registers match {
@ -445,6 +469,37 @@ case class MatchImmediate(i: Int) extends AssemblyLinePattern {
}
}
case class RegisterAndOffset(register: ZRegister.Value, offset: Int)
case class MatchSourceRegisterAndOffset(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
line.registers match {
case TwoRegisters(_, s) => ctx.addObject(i, RegisterAndOffset(s, 0))
case TwoRegistersOffset(_, s, o) => ctx.addObject(i, RegisterAndOffset(s, o))
case _ => false
}
}
case class MatchTargetRegisterAndOffset(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
line.registers match {
case TwoRegisters(t, _) => ctx.addObject(i, RegisterAndOffset(t, 0))
case TwoRegistersOffset(t, _, o) => ctx.addObject(i, RegisterAndOffset(t, o))
case _ => false
}
}
case class DoesntChangeMatchedRegisterAndOffset(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
val ro = ctx.get[RegisterAndOffset](i)
import ZRegister._
ro.register match {
case AF | SP => false // ?
case MEM_ABS_8 | MEM_ABS_16 | MEM_HL | MEM_DE | MEM_BC => !line.changesMemory
case _ => !line.changesRegisterAndOffset(ro.register, ro.offset)
}
}
}
case class MatchParameter(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
@ -619,27 +674,87 @@ case class Is16BitLoad(target:ZRegister.Value, source: ZRegister.Value) extends
case class IsRegular8BitLoadFrom(source: ZRegister.Value) extends TrivialAssemblyLinePattern {
override def apply(line: ZLine): Boolean =
line.opcode == ZOpcode.LD && line.registers.asInstanceOf[TwoRegisters].source == source && (line.registers.asInstanceOf[TwoRegisters].target match {
case ZRegister.I | ZRegister.MEM_ABS_8 | ZRegister.R | ZRegister.MEM_DE | ZRegister.MEM_BC => false
case _ => true
line.opcode == ZOpcode.LD && (line.registers match {
case TwoRegistersOffset(ZRegister.I | ZRegister.MEM_ABS_8 | ZRegister.R | ZRegister.MEM_DE | ZRegister.MEM_BC, _, _) => false
case TwoRegisters(ZRegister.I | ZRegister.MEM_ABS_8 | ZRegister.R | ZRegister.MEM_DE | ZRegister.MEM_BC, _) => false
case TwoRegistersOffset(t, s, _) => s == source
case TwoRegisters(t, s) => s == source
})
override def toString: String = "LD _," + source
}
object NoOffset extends TrivialAssemblyLinePattern {
override def apply(line: ZLine): Boolean =
line.registers match {
case _:TwoRegistersOffset => false
case _:OneRegisterOffset => false
case _ => true
}
}
case class Is8BitLoadTo(target: ZRegister.Value) extends TrivialAssemblyLinePattern {
override def apply(line: ZLine): Boolean =
line.opcode == ZOpcode.LD && line.registers.asInstanceOf[TwoRegisters].target == target && (line.registers.asInstanceOf[TwoRegisters].source match {
case ZRegister.I | ZRegister.MEM_ABS_8 | ZRegister.R => false
case _ => true
line.opcode == ZOpcode.LD && (line.registers match {
case TwoRegistersOffset(t, s, _) => target == t
case TwoRegisters(t, s) => target == t && (s match {
case ZRegister.I | ZRegister.MEM_ABS_8 | ZRegister.R => false
case _ => true
})
})
override def toString: String = s"LD $target,_"
}
case class MatchSourceIxOffsetOf8BitLoad(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
line.opcode == ZOpcode.LD && (line.registers match {
case TwoRegistersOffset(_, ZRegister.MEM_IX_D, offset) => ctx.addObject(i, offset)
case _ => false
})
}
override def toString: String = "LD (IX,!),_"
}
case class MatchIxOffset(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
line.registers match {
case OneRegisterOffset(ZRegister.MEM_IX_D, offset) => ctx.addObject(i, offset)
case _ => false
}
}
override def toString: String = "LD (IX,!),_"
}
case class MatchTargetIxOffsetOf8BitLoad(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
line.opcode == ZOpcode.LD && (line.registers match {
case TwoRegistersOffset(ZRegister.MEM_IX_D, _, offset) => ctx.addObject(i, offset)
case _ => false
})
}
override def toString: String = "LD (IX,!),_"
}
case class MatchUnimportantIxOffset(i: Int) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertBackward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
val offset = ctx.get[Int](i)
flowInfo.importanceAfter.memIx.getOrElse(offset, Unimportant) == Unimportant
}
}
case class Is16BitLoadTo(target: ZRegister.Value) extends TrivialAssemblyLinePattern {
override def apply(line: ZLine): Boolean =
line.opcode == ZOpcode.LD_16 && line.registers.asInstanceOf[TwoRegisters].target == target
line.opcode == ZOpcode.LD_16 && (line.registers match {
case TwoRegistersOffset(t, s, _) => target == t
case TwoRegisters(t, s) => target == t
})
override def toString: String = s"LD_16 $target,_"
}

View File

@ -19,13 +19,13 @@ class AbstractExpressionCompiler[T <: AbstractCode] {
}
def callingContext(ctx: CompilationContext, v: MemoryVariable): CompilationContext = {
val result = new Environment(Some(ctx.env), "")
val result = new Environment(Some(ctx.env), "", ctx.options.platform.cpuFamily)
result.registerVariable(VariableDeclarationStatement(v.name, v.typ.name, stack = false, global = false, constant = false, volatile = false, register = false, initialValue = None, address = None, bank = v.declaredBank), ctx.options)
ctx.copy(env = result)
}
def getParamMaxSize(ctx: CompilationContext, params: List[Expression]): Int = {
params.map { case expr => getExpressionType(ctx, expr).size}.max
params.map(expr => getExpressionType(ctx, expr).size).max
}
def getSumSize(ctx: CompilationContext, params: List[(Boolean, Expression)]): Int = {

View File

@ -15,7 +15,7 @@ case class CompilationContext(env: Environment,
breakLabels: Map[String, Label] = Map(),
continueLabels: Map[String, Label] = Map()){
def withInlinedEnv(environment: Environment, newLabel: String): CompilationContext = {
val newEnv = new Environment(Some(env), newLabel)
val newEnv = new Environment(Some(env), newLabel, environment.cpuFamily)
newEnv.things ++= environment.things
copy(env = newEnv)
}

View File

@ -22,6 +22,7 @@ object MosCompiler extends AbstractCompiler[AssemblyLine] {
override def compile(ctx: CompilationContext): List[AssemblyLine] = {
ctx.env.nameCheck(ctx.function.code)
val chunk = MosStatementCompiler.compile(ctx, ctx.function.code)
val zpRegisterSize = ctx.options.zpRegisterSize
val storeParamsFromRegisters = ctx.function.params match {
case NormalParamSignature(List(param@MemoryVariable(_, typ, _))) if typ.size == 1 =>
@ -29,31 +30,42 @@ object MosCompiler extends AbstractCompiler[AssemblyLine] {
case _ => Nil
}
val phReg =
if (ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
if (zpRegisterSize > 0) {
val reg = ctx.env.get[VariableInMemory]("__reg")
List(
AssemblyLine.zeropage(LDA, reg),
AssemblyLine.implied(PHA),
AssemblyLine.zeropage(LDA, reg, 1),
AssemblyLine.implied(PHA)
)
(0 until zpRegisterSize).flatMap { i =>
List(
AssemblyLine.zeropage(LDA, reg, i),
AssemblyLine.implied(PHA))
}.toList
} else Nil
val prefix = storeParamsFromRegisters ++ (if (ctx.function.interrupt) {
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
if (ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
List(
if (zpRegisterSize > 0) {
val physicalRegisters = List(
AssemblyLine.implied(PHB),
AssemblyLine.implied(PHD),
AssemblyLine.immediate(REP, 0x30),
AssemblyLine.implied(PHA_W),
AssemblyLine.implied(PHX_W),
AssemblyLine.implied(PHY_W),
AssemblyLine.implied(PHY_W),
AssemblyLine.zeropage(LDA_W, ctx.env.get[VariableInMemory]("__reg")),
AssemblyLine.implied(PHA_W),
AssemblyLine.immediate(SEP, 0x30))
AssemblyLine.implied(PHY_W))
val reg = ctx.env.get[VariableInMemory]("__reg")
val initialBytes = (0 to zpRegisterSize.&(0xfe).-(2) by 2).flatMap{ i=>
List(
AssemblyLine.zeropage(LDA_W, reg, i),
AssemblyLine.implied(PHA_W))
}
val lastByte = if (zpRegisterSize % 2 != 0){
List(
AssemblyLine.immediate(SEP, 0x30),
AssemblyLine.zeropage(LDA, reg, zpRegisterSize - 1),
AssemblyLine.implied(PHA))
} else {
List(AssemblyLine.immediate(SEP, 0x30))
}
physicalRegisters ++ initialBytes ++ lastByte
} else {
List(
AssemblyLine.implied(PHB),
@ -93,13 +105,22 @@ object MosCompiler extends AbstractCompiler[AssemblyLine] {
AssemblyLine.implied(PHA),
AssemblyLine.implied(CLD)) ++ phReg
}
} else if (ctx.function.kernalInterrupt && ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
} else if (ctx.function.kernalInterrupt && zpRegisterSize > 0) {
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
List(
AssemblyLine.accu16,
AssemblyLine.zeropage(LDA_W, ctx.env.get[VariableInMemory]("__reg")),
AssemblyLine.implied(PHA_W),
AssemblyLine.accu8)
val reg = ctx.env.get[VariableInMemory]("__reg")
val initialBytes = (0 to zpRegisterSize.&(0xfe).-(2) by 2).flatMap { i =>
List(
AssemblyLine.zeropage(LDA_W, reg, i),
AssemblyLine.implied(PHA_W))
}.toList
val lastByte = if (zpRegisterSize % 2 != 0) {
List(AssemblyLine.accu8,
AssemblyLine.zeropage(LDA_W, reg, zpRegisterSize - 1),
AssemblyLine.implied(PHA_W))
} else {
List(AssemblyLine.accu8)
}
AssemblyLine.accu16 :: (initialBytes ++ lastByte)
} else phReg
} else Nil) ++ stackPointerFixAtBeginning(ctx)
val label = AssemblyLine.label(Label(ctx.function.name)).copy(elidable = false)

View File

@ -136,7 +136,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
def prepareWordIndexing(ctx: CompilationContext, pointy: Pointy, indexExpression: Expression): List[AssemblyLine] = {
val w = ctx.env.get[Type]("word")
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
if (ctx.options.zpRegisterSize < 2) {
ErrorReporting.error("16-bit indexing requires a zeropage pseudoregister")
return Nil
}

View File

@ -37,32 +37,44 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
val m = ctx.function
val b = env.get[Type]("byte")
val w = env.get[Type]("word")
val plReg =
if (ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
val zpRegisterSize = ctx.options.zpRegisterSize
lazy val plReg =
if (zpRegisterSize > 0) {
val reg = env.get[VariableInMemory]("__reg")
List(
AssemblyLine.implied(PLA),
AssemblyLine.zeropage(STA, reg, 1),
AssemblyLine.implied(PLA),
AssemblyLine.zeropage(STA, reg)
)
(zpRegisterSize.-(1) to 0 by (-1)).flatMap{ i=>
List(
AssemblyLine.implied(PLA),
AssemblyLine.zeropage(STA, reg,i))
}.toList
} else Nil
val someRegisterA = Some(b, RegisterVariable(MosRegister.A, b))
val someRegisterAX = Some(w, RegisterVariable(MosRegister.AX, w))
val someRegisterYA = Some(w, RegisterVariable(MosRegister.YA, w))
val returnInstructions = if (m.interrupt) {
lazy val returnInstructions = if (m.interrupt) {
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
if (ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
List(
AssemblyLine.immediate(REP, 0x30),
AssemblyLine.implied(PLA_W),
AssemblyLine.zeropage(STA_W, env.get[VariableInMemory]("__reg")),
AssemblyLine.implied(PLY),
AssemblyLine.implied(PLX),
AssemblyLine.implied(PLA),
AssemblyLine.implied(PLD),
AssemblyLine.implied(PLB),
AssemblyLine.implied(RTI))
if (zpRegisterSize > 0) {
val reg = env.get[VariableInMemory]("__reg")
val lastByte = if (zpRegisterSize % 2 != 0) {
List(
AssemblyLine.implied(PLA),
AssemblyLine.zeropage(STA, reg, zpRegisterSize - 1),
AssemblyLine.immediate(REP, 0x30))
} else {
List(AssemblyLine.immediate(REP, 0x30))
}
val remainingBytes = (zpRegisterSize.&(0xfe).-(2) to 0 by (-2)).flatMap { i =>
List(
AssemblyLine.implied(PLA_W),
AssemblyLine.zeropage(STA_W, reg, i))
}
lastByte ++ remainingBytes ++
List(
AssemblyLine.implied(PLY),
AssemblyLine.implied(PLX),
AssemblyLine.implied(PLA),
AssemblyLine.implied(PLD),
AssemblyLine.implied(PLB),
AssemblyLine.implied(RTI))
} else {
List(
AssemblyLine.immediate(REP, 0x30),
@ -105,13 +117,24 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
AssemblyLine.implied(RTI))
}
} else {
(if (m.kernalInterrupt && ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
(if (m.kernalInterrupt && zpRegisterSize > 0) {
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
List(
AssemblyLine.accu16,
AssemblyLine.implied(PLA_W),
AssemblyLine.zeropage(STA_W, env.get[VariableInMemory]("__reg")),
AssemblyLine.accu8)
val reg = env.get[VariableInMemory]("__reg")
val lastByte = if (zpRegisterSize % 2 != 0) {
List(
AssemblyLine.implied(PLA),
AssemblyLine.zeropage(STA, reg, zpRegisterSize - 1),
AssemblyLine.accu16)
} else {
List(AssemblyLine.accu16)
}
val remainingBytes = (zpRegisterSize.&(0xfe).-(2) to 0 by (-2)).flatMap { i =>
List(
AssemblyLine.implied(PLA_W),
AssemblyLine.zeropage(STA_W, reg, i),
AssemblyLine.accu8)
}
lastByte ++ remainingBytes
} else plReg
} else Nil) ++ (if (m.isFar(ctx.options)) {
List(AssemblyLine.implied(RTL))

View File

@ -38,7 +38,7 @@ object PseudoregisterBuiltIns {
}
}
}
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
if (ctx.options.zpRegisterSize < 2) {
ErrorReporting.error("Word addition or subtraction requires the zeropage pseudoregister", params.headOption.flatMap(_._2.position))
return Nil
}
@ -57,7 +57,7 @@ object PseudoregisterBuiltIns {
}
def addToReg(ctx: CompilationContext, r: Expression, subtract: Boolean, decimal: Boolean): List[AssemblyLine] = {
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
if (ctx.options.zpRegisterSize < 2) {
ErrorReporting.error("Word addition or subtraction requires the zeropage pseudoregister", r.position)
return Nil
}
@ -108,7 +108,7 @@ object PseudoregisterBuiltIns {
def compileWordBitOpsToAX(ctx: CompilationContext, params: List[Expression], op: Opcode.Value): List[AssemblyLine] = {
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
if (ctx.options.zpRegisterSize < 2) {
ErrorReporting.error("Word bit operation requires the zeropage pseudoregister", params.headOption.flatMap(_.position))
return Nil
}
@ -126,7 +126,7 @@ object PseudoregisterBuiltIns {
}
def bitOpReg(ctx: CompilationContext, r: Expression, op: Opcode.Value): List[AssemblyLine] = {
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
if (ctx.options.zpRegisterSize < 2) {
ErrorReporting.error("Word bit operation requires the zeropage pseudoregister", r.position)
return Nil
}
@ -180,7 +180,7 @@ object PseudoregisterBuiltIns {
}
def compileWordShiftOps(left: Boolean, ctx: CompilationContext, l: Expression, r: Expression): List[AssemblyLine] = {
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
if (ctx.options.zpRegisterSize < 2) {
ErrorReporting.error("Word shifting requires the zeropage pseudoregister", l.position)
return Nil
}
@ -246,7 +246,7 @@ object PseudoregisterBuiltIns {
}
def compileByteMultiplication(ctx: CompilationContext, param1OrRegister: Option[Expression], param2: Expression, storeInRegLo: Boolean): List[AssemblyLine] = {
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
if (ctx.options.zpRegisterSize < 2) {
ErrorReporting.error("Variable byte multiplication requires the zeropage pseudoregister", param1OrRegister.flatMap(_.position))
return Nil
}

View File

@ -2,9 +2,12 @@ package millfork.compiler.z80
import java.util.concurrent.atomic.AtomicLong
import millfork.CompilationFlag
import millfork.assembly.z80.ZLine
import millfork.compiler.{AbstractCompiler, CompilationContext}
import millfork.env.Label
import millfork.error.ErrorReporting
import millfork.node.ZRegister
/**
* @author Karol Stasiak
@ -20,6 +23,26 @@ object Z80Compiler extends AbstractCompiler[ZLine] {
val chunk = Z80StatementCompiler.compile(ctx, ctx.function.code)
val label = ZLine.label(Label(ctx.function.name)).copy(elidable = false)
val prefix = Nil // TODO
label :: (prefix ++ chunk)
label :: (prefix ++ stackPointerFixAtBeginning(ctx) ++ chunk)
}
def stackPointerFixAtBeginning(ctx: CompilationContext): List[ZLine] = {
val m = ctx.function
if (m.stackVariablesSize == 0) return Nil
if (!ctx.options.flags(CompilationFlag.EmitZ80Opcodes)) {
ErrorReporting.error(s"Target CPU does not support stack variables", m.position)
return Nil
}
if (m.stackVariablesSize > 127) {
ErrorReporting.error(s"Function ${m.name} has too many local stack variables", m.position)
return Nil
}
import millfork.assembly.z80.ZOpcode._
import ZRegister._
List(
ZLine.register(PUSH, IX),
ZLine.ldImm16(IX, 0x10000 - ctx.function.stackVariablesSize.&(1).+(ctx.function.stackVariablesSize)),
ZLine.registers(ADD_16, IX, SP),
ZLine.ld16(SP, IX))
}
}

View File

@ -8,8 +8,6 @@ import millfork.node.{ZRegister, _}
import millfork.assembly.z80.ZOpcode._
import millfork.error.ErrorReporting
import scala.collection.GenTraversableOnce
/**
* @author Karol Stasiak
*/
@ -142,6 +140,17 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
}
case _ => ???
}
case v: StackVariable =>
v.typ.size match {
case 0 => ???
case 1 => loadByteViaIX(v.baseOffset, target)
case 2 => target match {
case ZExpressionTarget.NOTHING => Nil
case ZExpressionTarget.HL =>
List(ZLine.ldViaIx(ZRegister.L, v.baseOffset), ZLine.ldViaIx(ZRegister.H, v.baseOffset + 1))
}
case _ => ???
}
}
case i: IndexedExpression =>
calculateAddressToHL(ctx, i) match {
@ -316,13 +325,13 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
val (l, r, size) = assertAssignmentLike(ctx, params)
size match {
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, ADD)
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, ADD, ADC, size, decimal = false)
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, ADD, ADC, size)
}
case "-=" =>
val (l, r, size) = assertAssignmentLike(ctx, params)
size match {
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, SUB)
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, SUB, SBC, size, decimal = false)
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, SUB, SBC, size)
}
case "+'=" =>
val (l, r, size) = assertAssignmentLike(ctx, params)
@ -369,19 +378,19 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
val (l, r, size) = assertAssignmentLike(ctx, params)
size match {
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, AND)
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, AND, AND, size, decimal = false)
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, AND, AND, size)
}
case "^=" =>
val (l, r, size) = assertAssignmentLike(ctx, params)
size match {
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, XOR)
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, XOR, XOR, size, decimal = false)
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, XOR, XOR, size)
}
case "|=" =>
val (l, r, size) = assertAssignmentLike(ctx, params)
size match {
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, OR)
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, OR, OR, size, decimal = false)
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, OR, OR, size)
}
case _ =>
env.maybeGet[Type](f.functionName) match {
@ -509,6 +518,14 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
}
}
def loadByteViaIX(offset: Int, target: ZExpressionTarget.Value): List[ZLine] = {
target match {
case ZExpressionTarget.NOTHING => Nil
case ZExpressionTarget.A => List(ZLine.ldViaIx(ZRegister.A, offset))
case ZExpressionTarget.HL => List(ZLine.ldViaIx(ZRegister.A, offset), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0))
}
}
def loadByteViaHL(target: ZExpressionTarget.Value): List[ZLine] = {
target match {
case ZExpressionTarget.NOTHING => Nil
@ -534,6 +551,23 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
prepareA ++ fillUpperBytes
}
def signExtendViaIX(targetOffset: Int, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
if (bytes == 0) return Nil
val prepareA = if (signedSource) {
val prefix = if (hiRegister == ZRegister.A) Nil else List(ZLine.ld8(ZRegister.A, hiRegister))
val label = Z80Compiler.nextLabel("sx")
prefix ++ List(
ZLine.imm8(OR, 0x7f),
ZLine.jump(label, IfFlagSet(ZFlag.S)),
ZLine.ldImm8(ZRegister.A, 0),
ZLine.label(label))
} else {
List(ZLine.ldImm8(ZRegister.A, 0))
}
val fillUpperBytes = List.tabulate(bytes)(i => ZLine.ldViaIx(targetOffset + i, ZRegister.A))
prepareA ++ fillUpperBytes
}
def storeA(targetAddr: Constant, targetSize: Int, signedSource: Boolean): List[ZLine] = {
targetSize match {
case 0 => Nil
@ -542,6 +576,14 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
}
}
def storeAViaIX(targetOffset: Int, targetSize: Int, signedSource: Boolean): List[ZLine] = {
targetSize match {
case 0 => Nil
case 1 => List(ZLine.ldViaIx(targetOffset, ZRegister.A))
case n => ZLine.ldViaIx(targetOffset, ZRegister.A) :: signExtendViaIX(targetOffset + 1, ZRegister.A, n - 1, signedSource)
}
}
def storeHL(targetAddr: Constant, targetSize: Int, signedSource: Boolean): List[ZLine] = {
// TODO: LD (nnnn),HL compatibility?
targetSize match {
@ -552,12 +594,23 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
}
}
def storeHLViaIX(offset: Int, targetSize: Int, signedSource: Boolean): List[ZLine] = {
// TODO: LD (nnnn),HL compatibility?
targetSize match {
case 0 => Nil
case 1 => List(ZLine.ldViaIx(offset, ZRegister.L))
case 2 => List(ZLine.ldViaIx(offset, ZRegister.L), ZLine.ldViaIx(offset + 1, ZRegister.H))
case n => List(ZLine.ldViaIx(offset, ZRegister.L), ZLine.ldViaIx(offset + 1, ZRegister.H)) ++ signExtendViaIX(offset + 2, ZRegister.H, n - 2, signedSource)
}
}
def storeA(ctx: CompilationContext, target: LhsExpression, signedSource: Boolean): List[ZLine] = {
val env = ctx.env
target match {
case VariableExpression(vname) =>
env.get[Variable](vname) match {
case v: VariableInMemory => storeA(v.toAddress, v.typ.size, signedSource)
case v: StackVariable => storeAViaIX(v.baseOffset, v.typ.size, signedSource)
}
case i:IndexedExpression =>
calculateAddressToHL(ctx, i) match {
@ -577,6 +630,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
case VariableExpression(vname) =>
env.get[Variable](vname) match {
case v: VariableInMemory => storeHL(v.toAddress, v.typ.size, signedSource)
case v: StackVariable => storeHLViaIX(v.baseOffset, v.typ.size, signedSource)
}
case IndexedExpression(pointyName, indexExpr) =>
env.getPointy(pointyName) match {

View File

@ -21,7 +21,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
val options = ctx.options
statement match {
case ReturnStatement(None) =>
ctx.function.returnType match {
fixStackOnReturn(ctx) ++ (ctx.function.returnType match {
case _: BooleanType =>
List(ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
case t => t.size match {
@ -34,29 +34,31 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
ErrorReporting.warn("Returning without a value", options, statement.position)
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
}
}
})
case ReturnStatement(Some(e)) =>
ctx.function.returnType match {
case t: BooleanType => t.size match {
case 0 =>
ErrorReporting.error("Cannot return anything from a void function", statement.position)
List(ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
fixStackOnReturn(ctx) ++
List(ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
case 1 =>
Z80ExpressionCompiler.compileToA(ctx, e) ++
Z80ExpressionCompiler.compileToA(ctx, e) ++ fixStackOnReturn(ctx) ++
List(ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
case 2 =>
Z80ExpressionCompiler.compileToHL(ctx, e) ++
Z80ExpressionCompiler.compileToHL(ctx, e) ++ fixStackOnReturn(ctx) ++
List(ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
}
case t => t.size match {
case 0 =>
ErrorReporting.error("Cannot return anything from a void function", statement.position)
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
fixStackOnReturn(ctx) ++
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
case 1 =>
Z80ExpressionCompiler.compileToA(ctx, e) ++
Z80ExpressionCompiler.compileToA(ctx, e) ++ fixStackOnReturn(ctx) ++
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
case 2 =>
Z80ExpressionCompiler.compileToHL(ctx, e) ++
Z80ExpressionCompiler.compileToHL(ctx, e) ++ fixStackOnReturn(ctx) ++
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
}
}
@ -150,6 +152,26 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
}
}
private def fixStackOnReturn(ctx: CompilationContext): List[ZLine] = {
if (ctx.function.stackVariablesSize > 0) {
import ZRegister._
val localVariableArea = ctx.function.stackVariablesSize.&(1).+(ctx.function.stackVariablesSize)
if (ctx.function.returnType.size == 2) {
List(
ZLine.ldImm16(IX, localVariableArea),
ZLine.registers(ADD_16, IX, SP),
ZLine.ld16(SP, IX),
ZLine.register(POP, IX))
} else {
List(
ZLine.ldImm16(HL, localVariableArea),
ZLine.registers(ADD_16, HL, SP),
ZLine.ld16(SP, HL),
ZLine.register(POP, IX))
}
} else Nil
}
def labelChunk(labelName: String) = List(ZLine.label(Label(labelName)))
def jmpChunk(label: Label) = List(ZLine.jump(label))

View File

@ -1,6 +1,6 @@
package millfork.compiler.z80
import millfork.assembly.z80.{TwoRegisters, ZLine, ZOpcode}
import millfork.assembly.z80._
import millfork.compiler.CompilationContext
import millfork.node._
import ZOpcode._
@ -247,13 +247,14 @@ object ZBuiltIns {
}
def perform8BitInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, opcode: ZOpcode.Value, decimal: Boolean = false): List[ZLine] = {
val calculateAddress = lhs match {
val (calculateAddress, lv):(List[ZLine], LocalVariableAddressOperand) = lhs match {
case VariableExpression(name) =>
ctx.env.get[Variable](name) match {
case v: VariableInMemory => List(ZLine.ldImm16(ZRegister.HL, v.toAddress))
case v: VariableInMemory => List(ZLine.ldImm16(ZRegister.HL, v.toAddress)) -> LocalVariableAddressViaHL
case v: StackVariable => Nil -> LocalVariableAddressViaIX(v.baseOffset)
case _ => ???
}
case i: IndexedExpression => Z80ExpressionCompiler.calculateAddressToHL(ctx, i)
case i: IndexedExpression => Z80ExpressionCompiler.calculateAddressToHL(ctx, i) -> LocalVariableAddressViaHL
}
val constantRight = ctx.env.eval(rhs)
val calculateChange = Z80ExpressionCompiler.compileToA(ctx, rhs)
@ -266,45 +267,45 @@ object ZBuiltIns {
opcode match {
case ADD if decimal =>
setup ++ List(
ZLine.register(ADD, ZRegister.MEM_HL),
ZLine.register(ADD, lv),
ZLine.implied(DAA),
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
ZLine.ld8(lv, ZRegister.A))
case ADD if !decimal =>
constantRight match {
case Some(NumericConstant(1, _)) =>
calculateAddress :+ ZLine.register(INC, ZRegister.MEM_HL)
calculateAddress :+ ZLine.register(INC, lv)
case Some(NumericConstant(0xff | -1, _)) =>
calculateAddress :+ ZLine.register(DEC, ZRegister.MEM_HL)
calculateAddress :+ ZLine.register(DEC, lv)
case _ =>
setup ++ List(
ZLine.register(ADD, ZRegister.MEM_HL),
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
ZLine.register(ADD, lv),
ZLine.ld8(lv, ZRegister.A))
}
case SUB if decimal =>
setup ++ List(
ZLine.ld8(ZRegister.E, ZRegister.A),
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
ZLine.ld8(ZRegister.A, lv),
ZLine.register(SUB, ZRegister.E),
ZLine.implied(DAA),
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
ZLine.ld8(lv, ZRegister.A))
case SUB if !decimal=>
constantRight match {
case Some(NumericConstant(1, _)) =>
calculateAddress :+ ZLine.register(DEC, ZRegister.MEM_HL)
calculateAddress :+ ZLine.register(DEC, lv)
case Some(NumericConstant(0xff | -1, _)) =>
calculateAddress :+ ZLine.register(INC, ZRegister.MEM_HL)
calculateAddress :+ ZLine.register(INC, lv)
case _ =>
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
setup ++ List(
ZLine.implied(NEG),
ZLine.register(ADD, ZRegister.MEM_HL),
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
ZLine.register(ADD, lv),
ZLine.ld8(lv, ZRegister.A))
} else {
setup ++ List(
ZLine.implied(CPL),
ZLine.register(INC, ZRegister.A),
ZLine.register(SUB, ZRegister.MEM_HL),
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
ZLine.register(SUB, lv),
ZLine.ld8(lv, ZRegister.A))
}
}
case XOR =>
@ -313,35 +314,35 @@ object ZBuiltIns {
calculateAddress
case Some(NumericConstant(0xff | -1, _)) =>
calculateAddress ++ List(
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
ZLine.ld8(ZRegister.A, lv),
ZLine.implied(CPL),
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
ZLine.ld8(lv, ZRegister.A))
case _ =>
setup ++ List(
ZLine.register(XOR, ZRegister.MEM_HL),
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
ZLine.register(XOR, lv),
ZLine.ld8(lv, ZRegister.A))
}
case OR =>
constantRight match {
case Some(NumericConstant(0, _)) =>
calculateAddress
case Some(NumericConstant(0xff | -1, _)) =>
calculateAddress :+ ZLine.ldImm8(ZRegister.MEM_HL, 0xff)
calculateAddress :+ ZLine.ldImm8(lv, 0xff)
case _ =>
setup ++ List(
ZLine.register(OR, ZRegister.MEM_HL),
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
ZLine.register(OR, lv),
ZLine.ld8(lv, ZRegister.A))
}
case AND =>
constantRight match {
case Some(NumericConstant(0, _)) =>
calculateAddress :+ ZLine.ldImm8(ZRegister.MEM_HL, 0)
calculateAddress :+ ZLine.ldImm8(lv, 0)
case Some(NumericConstant(0xff | -1, _)) =>
calculateAddress
case _ =>
setup ++ List(
ZLine.register(AND, ZRegister.MEM_HL),
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
ZLine.register(AND, lv),
ZLine.ld8(lv, ZRegister.A))
}
}
}

View File

@ -98,7 +98,7 @@ case class UnexpandedConstant(name: String, requiredSize: Int) extends Constant
case class NumericConstant(value: Long, requiredSize: Int) extends Constant {
if (requiredSize == 1) {
if (value < -128 || value > 255) {
throw new IllegalArgumentException("The constant is too big")
throw new IllegalArgumentException(s"The constant $value is too big")
}
}
override def isProvablyZero: Boolean = value == 0

View File

@ -17,10 +17,13 @@ import scala.collection.mutable
* @author Karol Stasiak
*/
//noinspection NotImplementedCode
class Environment(val parent: Option[Environment], val prefix: String) {
class Environment(val parent: Option[Environment], val prefix: String, val cpuFamily: CpuFamily.Value) {
private var baseStackOffset = 0x101
private var baseStackOffset: Int = cpuFamily match {
case CpuFamily.M6502 => 0x101
case CpuFamily.I80 => 0
}
def genRelativeVariable(constant: Constant, typ: Type, zeropage: Boolean): RelativeVariable = {
val variable = RelativeVariable(".rv__" + Environment.relVarId.incrementAndGet().formatted("%06d"), constant, typ, zeropage = zeropage, declaredBank = None /*TODO*/)
@ -37,7 +40,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
m.environment.getAllPrefixedThings
case _ => Map[String, Thing]()
}.fold(things.toMap)(_ ++ _)
val e = new Environment(None, "")
val e = new Environment(None, "", cpuFamily)
e.things.clear()
e.things ++= allThings
e
@ -588,7 +591,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
ErrorReporting.error(s"Non-macro function `$name` cannot have inlinable parameters", stmt.position)
}
val env = new Environment(Some(this), name + "$")
val env = new Environment(Some(this), name + "$", cpuFamily)
stmt.params.foreach(p => env.registerParameter(p, options))
val params = if (stmt.assembly) {
AssemblyParamSignature(stmt.params.map {
@ -963,7 +966,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
} else {
val (v, addr) = stmt.address.fold[(VariableInMemory, Constant)]({
val alloc =
if (typ.name == "pointer") VariableAllocationMethod.Zeropage
if (typ.name == "pointer" || typ.name == "__reg$type") VariableAllocationMethod.Zeropage
else if (stmt.global) VariableAllocationMethod.Static
else if (stmt.register) VariableAllocationMethod.Register
else VariableAllocationMethod.Auto
@ -1061,11 +1064,12 @@ class Environment(val parent: Option[Environment], val prefix: String) {
case a: ArrayDeclarationStatement => registerArray(a, options)
case i: ImportStatement => ()
}
if (options.flag(CompilationFlag.ZeropagePseudoregister) && !things.contains("__reg")) {
if (options.zpRegisterSize > 0 && !things.contains("__reg")) {
addThing(BasicPlainType("__reg$type", options.zpRegisterSize), None)
registerVariable(VariableDeclarationStatement(
name = "__reg",
bank = None,
typ = "pointer",
typ = "__reg$type",
global = true,
stack = false,
constant = false,

View File

@ -10,6 +10,10 @@ import millfork.node._
*/
object UnusedFunctions extends NodeOptimization {
private val operatorImplementations: List[(String, Int, String)] = List(
("*", 2, "__mul_u8u8u8")
)
override def optimize(nodes: List[Node], options: CompilationOptions): List[Node] = {
val panicRequired = options.flags(CompilationFlag.CheckIndexOutOfBounds)
val allNormalFunctions = nodes.flatMap {
@ -18,8 +22,10 @@ object UnusedFunctions extends NodeOptimization {
}.toSet
val allCalledFunctions = getAllCalledFunctions(nodes).toSet
var unusedFunctions = allNormalFunctions -- allCalledFunctions
if (allCalledFunctions.contains("*") && options.flag(CompilationFlag.ZeropagePseudoregister)) {
unusedFunctions -= "__mul_u8u8u8"
for((op, zp, fun) <- operatorImplementations) {
if (allCalledFunctions.contains(op) && options.zpRegisterSize >= zp) {
unusedFunctions -= fun
}
}
if (unusedFunctions.nonEmpty) {
ErrorReporting.debug("Removing unused functions: " + unusedFunctions.mkString(", "))

View File

@ -35,18 +35,9 @@ class Z80Assembler(program: Program,
case ZRegister.SP => 3
}
private def internalArithmeticIndex(opcode: ZOpcode.Value): Int = {
import ZOpcode._
opcode match {
case ADD => 0x80
case ADC => 0x88
case SUB => 0x90
case SBC => 0x98
case AND => 0xa0
case XOR => 0xa8
case OR => 0xb0
case CP => 0xb8
}
private def prefixByte(reg: ZRegister.Value): Int = reg match {
case ZRegister.IX | ZRegister.MEM_IX_D => 0xdd
case ZRegister.IY | ZRegister.MEM_IY_D => 0xfd
}
override def emitInstruction(bank: String, options: CompilationOptions, index: Int, instr: ZLine): Int = {
@ -77,17 +68,31 @@ class Z80Assembler(program: Program,
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, edImplieds(op))
index + 2
case ZLine(ADD_16, TwoRegisters(ZRegister.HL, source), param, _) =>
case ZLine(ADD_16, TwoRegisters(ZRegister.HL, source), _, _) =>
writeByte(bank, index, 9 + 16 * internalRegisterIndex(source))
index + 1
case ZLine(ADD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), source), _, _) =>
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 9 + 16 * internalRegisterIndex(source))
index + 2
case ZLine(SBC_16, TwoRegisters(ZRegister.HL, reg), _, _) =>
writeByte(bank, index, 0xed)
writeByte(bank, index + 1, 0x42 + 0x10 * internalRegisterIndex(reg))
index + 2
case ZLine(LD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), ZRegister.IMM_16), param, _) =>
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 0x21)
writeWord(bank, index + 2, param)
index + 4
case ZLine(LD_16, TwoRegisters(target, ZRegister.IMM_16), param, _) =>
writeByte(bank, index, 1 + 16 * internalRegisterIndex(target))
writeWord(bank, index + 1, param)
index + 3
case ZLine(LD_16, TwoRegisters(ix@(ZRegister.IX | ZRegister.IY), ZRegister.MEM_ABS_16), param, _) =>
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 0x2a)
writeWord(bank, index + 2, param)
index + 4
case ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.MEM_ABS_16), param, _) =>
writeByte(bank, index, 0x2a)
writeWord(bank, index + 1, param)
@ -96,32 +101,27 @@ class Z80Assembler(program: Program,
writeByte(bank, index, 0x22)
writeWord(bank, index + 1, param)
index + 3
case ZLine(LD_16, TwoRegisters(ZRegister.SP, ZRegister.HL), _, _) =>
writeByte(bank, index, 0xF9)
index + 1
case ZLine(LD_16, TwoRegisters(ZRegister.SP, ix@(ZRegister.IX | ZRegister.IY)), _, _) =>
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 0xF9)
index + 2
case ZLine(op, OneRegister(ZRegister.IMM_8), param, _) if immediates.contains(op) =>
val o = immediates(op)
writeByte(bank, index, o)
writeByte(bank, index + 1, param)
index + 2
case ZLine(op, OneRegister(ZRegister.MEM_IX_D), _, _) if oneRegister.contains(op) =>
case ZLine(op, OneRegisterOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset), _, _) if oneRegister.contains(op) =>
val o = oneRegister(op)
writeByte(bank, index, 0xdd)
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ZRegister.MEM_HL) * o.multiplier)
writeByte(bank, index + 2, instr.parameter)
writeByte(bank, index + 2, offset)
index + 3
case ZLine(op, OneRegister(ZRegister.MEM_IY_D), _, _) if oneRegister.contains(op) =>
case ZLine(op, OneRegister(ix@(ZRegister.IX | ZRegister.IY)), _, _) if oneRegister.contains(op) =>
val o = oneRegister(op)
writeByte(bank, index, 0xfd)
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ZRegister.MEM_HL) * o.multiplier)
writeByte(bank, index + 2, instr.parameter)
index + 3
case ZLine(op, OneRegister(ZRegister.IX), _, _) if oneRegister.contains(op) =>
val o = oneRegister(op)
writeByte(bank, index, 0xdd)
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ZRegister.HL) * o.multiplier)
writeByte(bank, index + 2, instr.parameter)
index + 3
case ZLine(op, OneRegister(ZRegister.IY), _, _) if oneRegister.contains(op) =>
val o = oneRegister(op)
writeByte(bank, index, 0xfd)
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ZRegister.HL) * o.multiplier)
writeByte(bank, index + 2, instr.parameter)
index + 3
@ -140,6 +140,12 @@ class Z80Assembler(program: Program,
writeByte(bank, index, 6 + 8 * internalRegisterIndex(reg))
writeByte(bank, index + 1, instr.parameter)
index + 2
case TwoRegistersOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), ZRegister.IMM_8, offset) =>
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 0x36)
writeByte(bank, index + 2, offset)
writeByte(bank, index + 3, instr.parameter)
index + 4
case TwoRegisters(ZRegister.A, ZRegister.MEM_ABS_8) =>
writeByte(bank, index, 0x3a)
writeWord(bank, index + 1, instr.parameter)
@ -160,25 +166,15 @@ class Z80Assembler(program: Program,
case TwoRegisters(ZRegister.A, ZRegister.MEM_DE) =>
writeByte(bank, index, 0x1a)
index + 1
case TwoRegisters(ZRegister.MEM_IX_D, source) =>
writeByte(bank, index, 0xdd)
case TwoRegistersOffset(ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), source, offset) =>
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(ZRegister.MEM_HL) * 8)
writeByte(bank, index + 1, instr.parameter)
writeByte(bank, index + 2, offset)
index + 3
case TwoRegisters(ZRegister.MEM_IY_D, source) =>
writeByte(bank, index, 0xfd)
writeByte(bank, index + 1, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(ZRegister.MEM_HL) * 8)
writeByte(bank, index + 1, instr.parameter)
index + 3
case TwoRegisters(target, ZRegister.MEM_IX_D) =>
writeByte(bank, index, 0xdd)
writeByte(bank, index, 0x40 + internalRegisterIndex(ZRegister.MEM_HL) + internalRegisterIndex(target) * 8)
writeByte(bank, index + 1, instr.parameter)
index + 3
case TwoRegisters(target, ZRegister.MEM_IY_D) =>
writeByte(bank, index, 0xfd)
writeByte(bank, index, 0x40 + internalRegisterIndex(ZRegister.MEM_HL) + internalRegisterIndex(target) * 8)
writeByte(bank, index + 1, instr.parameter)
case TwoRegistersOffset(target, ix@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), offset) =>
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, 0x40 + internalRegisterIndex(ZRegister.MEM_HL) + internalRegisterIndex(target) * 8)
writeByte(bank, index + 2, offset)
index + 3
case TwoRegisters(target, source) =>
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
@ -267,29 +263,29 @@ class Z80Assembler(program: Program,
writeWord(bank, index + 1, param)
index + 3
case ZLine(RET, IfFlagClear(ZFlag.Z), param, _) =>
case ZLine(RET, IfFlagClear(ZFlag.Z), _, _) =>
writeByte(bank, index, 0xc0)
index + 1
case ZLine(RET, IfFlagClear(ZFlag.C), param, _) =>
case ZLine(RET, IfFlagClear(ZFlag.C), _, _) =>
writeByte(bank, index, 0xd0)
index + 1
case ZLine(RET, IfFlagClear(ZFlag.P), param, _) =>
case ZLine(RET, IfFlagClear(ZFlag.P), _, _) =>
writeByte(bank, index, 0xe0)
index + 1
case ZLine(RET, IfFlagClear(ZFlag.S), param, _) =>
case ZLine(RET, IfFlagClear(ZFlag.S), _, _) =>
writeByte(bank, index, 0xf0)
index + 1
case ZLine(RET, IfFlagSet(ZFlag.Z), param, _) =>
case ZLine(RET, IfFlagSet(ZFlag.Z), _, _) =>
writeByte(bank, index, 0xc8)
index + 1
case ZLine(RET, IfFlagSet(ZFlag.C), param, _) =>
case ZLine(RET, IfFlagSet(ZFlag.C), _, _) =>
writeByte(bank, index, 0xd8)
index + 1
case ZLine(RET, IfFlagSet(ZFlag.P), param, _) =>
case ZLine(RET, IfFlagSet(ZFlag.P), _, _) =>
writeByte(bank, index, 0xe8)
index + 1
case ZLine(RET, IfFlagSet(ZFlag.S), param, _) =>
case ZLine(RET, IfFlagSet(ZFlag.S), _, _) =>
writeByte(bank, index, 0xf8)
index + 1

View File

@ -1,6 +1,6 @@
package millfork.parser
import millfork.{CompilationFlag, CompilationOptions}
import millfork.CompilationOptions
import millfork.assembly.mos.AssemblyLine
/**
@ -13,7 +13,7 @@ class MosSourceLoadingQueue(initialFilenames: List[String],
override def createParser(filename: String, src: String, parentDir: String): MfParser[AssemblyLine] = MosParser(filename, src, parentDir, options)
def enqueueStandardModules(): Unit = {
if (options.flag(CompilationFlag.ZeropagePseudoregister)) {
if (options.zpRegisterSize > 0) {
moduleQueue.enqueue(() => parseModule("zp_reg", includePath, Left(None)))
}
}

View File

@ -1,6 +1,7 @@
package millfork.test
import millfork.test.emu.{EmuBenchmarkRun, EmuCmosBenchmarkRun}
import millfork.Cpu
import millfork.test.emu.{EmuCmosBenchmarkRun, EmuCrossPlatformBenchmarkRun, EmuZ80BenchmarkRun}
import org.scalatest.{FunSuite, Matchers}
/**
@ -9,7 +10,7 @@ import org.scalatest.{FunSuite, Matchers}
class StackVarSuite extends FunSuite with Matchers {
test("Basic stack assignment") {
EmuCmosBenchmarkRun("""
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)("""
| byte output @$c000
| void main () {
| stack byte a
@ -23,7 +24,7 @@ class StackVarSuite extends FunSuite with Matchers {
}
test("Stack byte addition") {
EmuCmosBenchmarkRun("""
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)("""
| byte output @$c000
| void main () {
| stack byte a
@ -40,7 +41,7 @@ class StackVarSuite extends FunSuite with Matchers {
""".stripMargin)(_.readWord(0xc000) should equal(0x77))
}
test("Complex expressions involving stack variables") {
test("Complex expressions involving stack variables (6502)") {
EmuCmosBenchmarkRun("""
| byte output @$c000
| void main () {
@ -54,6 +55,20 @@ class StackVarSuite extends FunSuite with Matchers {
""".stripMargin)(_.readWord(0xc000) should equal(21))
}
test("Complex expressions involving stack variables (Z80)") {
EmuZ80BenchmarkRun("""
| byte output @$c000
| void main () {
| stack byte a
| a = 7
| output = f(a) + f(a) + f(a)
| }
| asm byte f(byte a) {
| ret
| }
""".stripMargin)(_.readWord(0xc000) should equal(21))
}
// ERROR: (8:9) Right-hand-side expression is too complex
// test("Stack byte subtraction") {
// EmuUnoptimizedRun("""
@ -74,7 +89,7 @@ class StackVarSuite extends FunSuite with Matchers {
// }
test("Stack word addition") {
EmuCmosBenchmarkRun("""
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)("""
| word output @$c000
| void main () {
| stack word a
@ -92,7 +107,7 @@ class StackVarSuite extends FunSuite with Matchers {
}
test("Recursion") {
EmuCmosBenchmarkRun("""
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)("""
| array output [6] @$c000
| byte fails @$c010
| void main () {
@ -129,7 +144,7 @@ class StackVarSuite extends FunSuite with Matchers {
test("Indexing") {
EmuCmosBenchmarkRun("""
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)("""
| array output [200] @$c000
| void main () {
| stack byte a

View File

@ -11,6 +11,8 @@ object EmuOptimizedCmosRun extends EmuRun(
OptimizationPresets.NodeOpt,
OptimizationPresets.AssOpt ++
ZeropageRegisterOptimizations.All ++
OptimizationPresets.Good ++
OptimizationPresets.Good ++
CmosOptimizations.All ++ OptimizationPresets.Good ++
CmosOptimizations.All ++ OptimizationPresets.Good ++
ZeropageRegisterOptimizations.All ++

View File

@ -11,6 +11,8 @@ object EmuOptimizedInlinedRun extends EmuRun(
OptimizationPresets.NodeOpt,
OptimizationPresets.AssOpt ++
ZeropageRegisterOptimizations.All ++
OptimizationPresets.Good ++
OptimizationPresets.Good ++
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
ZeropageRegisterOptimizations.All ++

View File

@ -12,6 +12,8 @@ object EmuOptimizedRun extends EmuRun(
OptimizationPresets.NodeOpt,
OptimizationPresets.AssOpt ++
ZeropageRegisterOptimizations.All ++
OptimizationPresets.Good ++
OptimizationPresets.Good ++
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
OptimizationPresets.Good ++ LaterOptimizations.Nmos ++
ZeropageRegisterOptimizations.All ++

View File

@ -18,6 +18,7 @@ object EmuPlatform {
Map("default" -> new VariableAllocator(
if (CpuFamily.forType(cpu) == CpuFamily.M6502) pointers else Nil,
new AfterCodeByteAllocator(0xff00))),
if (CpuFamily.forType(cpu) == CpuFamily.M6502) 2 else 0,
pointers,
".bin",
false,

View File

@ -17,7 +17,7 @@ import millfork.node.StandardCallGraph
import millfork.node.opt.NodeOptimization
import millfork.output.{MemoryBank, MosAssembler}
import millfork.parser.MosParser
import millfork.{CompilationFlag, CompilationOptions}
import millfork.{CompilationFlag, CompilationOptions, CpuFamily}
import org.scalatest.Matchers
import scala.collection.JavaConverters._
@ -104,7 +104,6 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
CompilationFlag.InlineFunctions -> this.inline,
CompilationFlag.InterproceduralOptimization -> true,
CompilationFlag.CompactReturnDispatchParams -> true,
CompilationFlag.ZeropagePseudoregister -> true,
CompilationFlag.EmitCmosOpcodes -> millfork.Cpu.CmosCompatible.contains(platform.cpu),
CompilationFlag.EmitEmulation65816Opcodes -> (platform.cpu == millfork.Cpu.Sixteen),
CompilationFlag.Emit65CE02Opcodes -> (platform.cpu == millfork.Cpu.CE02),
@ -112,12 +111,11 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
CompilationFlag.OptimizeForSpeed -> blastProcessing,
CompilationFlag.OptimizeForSonicSpeed -> blastProcessing
// CompilationFlag.CheckIndexOutOfBounds -> true,
), None)
), None, 2)
ErrorReporting.hasErrors = false
ErrorReporting.verbosity = 999
var effectiveSource = source
if (!source.contains("_panic")) effectiveSource += "\n void _panic(){while(true){}}"
if (!source.contains("__reg")) effectiveSource += "\n pointer __reg"
if (source.contains("import zp_reg"))
effectiveSource += Files.readAllLines(Paths.get("include/zp_reg.mfk"), StandardCharsets.US_ASCII).asScala.mkString("\n", "\n", "")
val parserF = MosParser("", effectiveSource, "", options)
@ -129,7 +127,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
// prepare
val program = nodeOptimizations.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
val callGraph = new StandardCallGraph(program)
val env = new Environment(None, "")
val env = new Environment(None, "", CpuFamily.M6502)
env.collectDeclarations(program, options)
val hasOptimizations = assemblyOptimizations.nonEmpty
@ -149,7 +147,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
// compile
val env2 = new Environment(None, "")
val env2 = new Environment(None, "", CpuFamily.M6502)
env2.collectDeclarations(program, options)
val assembler = new MosAssembler(program, env2, platform)
val output = assembler.assemble(callGraph, assemblyOptimizations, options)

View File

@ -12,7 +12,7 @@ import millfork.node.StandardCallGraph
import millfork.node.opt.NodeOptimization
import millfork.output.{MemoryBank, MosAssembler, Z80Assembler}
import millfork.parser.Z80Parser
import millfork.CompilationOptions
import millfork.{CompilationOptions, CpuFamily}
import millfork.compiler.z80.Z80Compiler
import org.scalatest.Matchers
@ -21,8 +21,6 @@ import org.scalatest.Matchers
*/
class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], assemblyOptimizations: List[AssemblyOptimization[ZLine]]) extends Matchers {
private val variableLength = Set(0x10, 0x30, 0x50, 0x70, 0x90, 0xb0, 0xd0, 0xf0)
private val TooManyCycles: Long = 1000000
def apply2(source: String): (Timings, MemoryBank) = {
@ -30,7 +28,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
Console.err.flush()
println(source)
val platform = EmuPlatform.get(cpu)
val options = CompilationOptions(platform, millfork.Cpu.defaultFlags(cpu).map(_ -> true).toMap, None)
val options = CompilationOptions(platform, millfork.Cpu.defaultFlags(cpu).map(_ -> true).toMap, None, 0)
ErrorReporting.hasErrors = false
ErrorReporting.verbosity = 999
var effectiveSource = source
@ -44,7 +42,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
// prepare
val program = nodeOptimizations.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
val callGraph = new StandardCallGraph(program)
val env = new Environment(None, "")
val env = new Environment(None, "", CpuFamily.I80)
env.collectDeclarations(program, options)
val hasOptimizations = assemblyOptimizations.nonEmpty
@ -64,7 +62,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
// compile
val env2 = new Environment(None, "")
val env2 = new Environment(None, "", CpuFamily.I80)
env2.collectDeclarations(program, options)
val assembler = new Z80Assembler(program, env2, platform)
val output = assembler.assemble(callGraph, assemblyOptimizations, options)