mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-08 18:37:17 +00:00
Start of the Z80 backend
This commit is contained in:
parent
780bfa3428
commit
0f453e2d2c
@ -16,72 +16,117 @@ case class CompilationOptions(platform: Platform, commandLineFlags: Map[Compilat
|
||||
|
||||
def flag(f: CompilationFlag.Value) = flags(f)
|
||||
|
||||
if (flags(DecimalMode)) {
|
||||
if (platform.cpu == Ricoh || platform.cpu == StrictRicoh) {
|
||||
ErrorReporting.warn("Decimal mode enabled for Ricoh architecture", this)
|
||||
{
|
||||
var invalids = Set[CompilationFlag.Value]()
|
||||
|
||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.M6502) invalids ++= Set(
|
||||
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
||||
ZeropagePseudoregister, PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, LUnixRelocatableCode, RorWarning)
|
||||
|
||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, UseIxForStack)
|
||||
|
||||
invalids = invalids.filter(flags)
|
||||
|
||||
if (invalids.nonEmpty) {
|
||||
ErrorReporting.error("Invalid flags enabled for the currect CPU family: " + invalids.mkString(", "))
|
||||
}
|
||||
}
|
||||
if (platform.cpu == Sixteen) {
|
||||
if (flags(LargeCode)) {
|
||||
ErrorReporting.warn("Large code model doesn't work correctly yet", this)
|
||||
}
|
||||
}
|
||||
if (platform.cpu != Sixteen) {
|
||||
if (flags(LargeCode)) {
|
||||
ErrorReporting.error("Cannot use large code model on architectures other than 65816")
|
||||
}
|
||||
if (flags(ReturnWordsViaAccumulator)) {
|
||||
ErrorReporting.error("Cannot return words via accumulator on non-65816 architecture")
|
||||
}
|
||||
if (flags(EmitNative65816Opcodes) || flags(EmitEmulation65816Opcodes)) {
|
||||
ErrorReporting.error("65816 opcodes enabled for non-65816 architecture")
|
||||
}
|
||||
}
|
||||
if (platform.cpu != CE02) {
|
||||
if (flags(Emit65CE02Opcodes)) {
|
||||
ErrorReporting.error("65CE02 opcodes enabled for non-65CE02 architecture")
|
||||
}
|
||||
}
|
||||
if (flags(Emit65CE02Opcodes)) {
|
||||
ErrorReporting.warn("65CE02 opcodes are highly experimental", this)
|
||||
}
|
||||
if (platform.cpu != HuC6280) {
|
||||
if (flags(EmitHudsonOpcodes)) {
|
||||
ErrorReporting.error("HuC6280 opcodes enabled for non-HuC6280 architecture")
|
||||
}
|
||||
}
|
||||
if (!CmosCompatible(platform.cpu)) {
|
||||
if (!flags(PreventJmpIndirectBug)) {
|
||||
ErrorReporting.warn("JMP bug prevention should be enabled for non-CMOS architecture", this)
|
||||
}
|
||||
if (flags(EmitCmosOpcodes)) {
|
||||
ErrorReporting.error("CMOS opcodes enabled for non-CMOS architecture")
|
||||
}
|
||||
}
|
||||
if (flags(EmitIllegals)) {
|
||||
if (CmosCompatible(platform.cpu)) {
|
||||
ErrorReporting.error("Illegal opcodes enabled for architecture that doesn't support them")
|
||||
}
|
||||
if (platform.cpu == StrictRicoh || platform.cpu == StrictMos) {
|
||||
ErrorReporting.warn("Illegal opcodes enabled for strict architecture", this)
|
||||
CpuFamily.forType(platform.cpu) match {
|
||||
case CpuFamily.M6502 =>
|
||||
if (flags(DecimalMode)) {
|
||||
if (platform.cpu == Ricoh || platform.cpu == StrictRicoh) {
|
||||
ErrorReporting.warn("Decimal mode enabled for Ricoh architecture", this)
|
||||
}
|
||||
}
|
||||
if (platform.cpu == Sixteen) {
|
||||
if (flags(LargeCode)) {
|
||||
ErrorReporting.warn("Large code model doesn't work correctly yet", this)
|
||||
}
|
||||
}
|
||||
if (platform.cpu != Sixteen) {
|
||||
if (flags(LargeCode)) {
|
||||
ErrorReporting.error("Cannot use large code model on architectures other than 65816")
|
||||
}
|
||||
if (flags(ReturnWordsViaAccumulator)) {
|
||||
ErrorReporting.error("Cannot return words via accumulator on non-65816 architecture")
|
||||
}
|
||||
if (flags(EmitNative65816Opcodes) || flags(EmitEmulation65816Opcodes)) {
|
||||
ErrorReporting.error("65816 opcodes enabled for non-65816 architecture")
|
||||
}
|
||||
}
|
||||
if (platform.cpu != CE02) {
|
||||
if (flags(Emit65CE02Opcodes)) {
|
||||
ErrorReporting.error("65CE02 opcodes enabled for non-65CE02 architecture")
|
||||
}
|
||||
}
|
||||
if (flags(Emit65CE02Opcodes)) {
|
||||
ErrorReporting.warn("65CE02 opcodes are highly experimental", this)
|
||||
}
|
||||
if (platform.cpu != HuC6280) {
|
||||
if (flags(EmitHudsonOpcodes)) {
|
||||
ErrorReporting.error("HuC6280 opcodes enabled for non-HuC6280 architecture")
|
||||
}
|
||||
}
|
||||
if (!CmosCompatible(platform.cpu)) {
|
||||
if (!flags(PreventJmpIndirectBug)) {
|
||||
ErrorReporting.warn("JMP bug prevention should be enabled for non-CMOS architecture", this)
|
||||
}
|
||||
if (flags(EmitCmosOpcodes)) {
|
||||
ErrorReporting.error("CMOS opcodes enabled for non-CMOS architecture")
|
||||
}
|
||||
}
|
||||
if (flags(EmitIllegals)) {
|
||||
if (CmosCompatible(platform.cpu)) {
|
||||
ErrorReporting.error("Illegal opcodes enabled for architecture that doesn't support them")
|
||||
}
|
||||
if (platform.cpu == StrictRicoh || platform.cpu == StrictMos) {
|
||||
ErrorReporting.warn("Illegal opcodes enabled for strict architecture", this)
|
||||
}
|
||||
}
|
||||
case CpuFamily.I80 =>
|
||||
if (flags(EmitIllegals)) {
|
||||
if (platform.cpu != Z80) {
|
||||
ErrorReporting.error("Illegal opcodes enabled for architecture that doesn't support them")
|
||||
}
|
||||
}
|
||||
if (flags(UseIxForStack)) {
|
||||
if (platform.cpu != Z80) {
|
||||
ErrorReporting.error("IX register enabled for architecture that doesn't support it")
|
||||
}
|
||||
}
|
||||
if (flags(EmitZ80Opcodes)) {
|
||||
if (platform.cpu != Z80) {
|
||||
ErrorReporting.error("Z80 opcodes enabled for architecture that doesn't support them")
|
||||
}
|
||||
}
|
||||
if (flags(EmitSharpOpcodes)) {
|
||||
if (platform.cpu != Sharp) {
|
||||
ErrorReporting.error("Sharp LR35902 opcodes enabled for architecture that doesn't support them")
|
||||
}
|
||||
}
|
||||
if (flags(EmitExtended80Opcodes)) {
|
||||
if (platform.cpu != Sharp && platform.cpu != Z80) {
|
||||
ErrorReporting.error("Extended 8080-like opcodes enabled for architecture that doesn't support them")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object CpuFamily extends Enumeration {
|
||||
val M6502, Z80, M6809, I8086, M65K, ARM = Value
|
||||
val M6502, I80, M6809, I8086, M65K, ARM = Value
|
||||
|
||||
def forType(cpu: Cpu.Value): CpuFamily.Value = {
|
||||
import Cpu._
|
||||
cpu match {
|
||||
case Mos | StrictMos | Ricoh | StrictRicoh | Cmos | HuC6280 | CE02 | Sixteen => M6502
|
||||
case Intel8080 | Sharp | Z80 => I80
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Cpu extends Enumeration {
|
||||
|
||||
val Mos, StrictMos, Ricoh, StrictRicoh, Cmos, HuC6280, CE02, Sixteen = Value
|
||||
val Mos, StrictMos, Ricoh, StrictRicoh, Cmos, HuC6280, CE02, Sixteen, Intel8080, Z80, Sharp = Value
|
||||
|
||||
val CmosCompatible = Set(Cmos, HuC6280, CE02, Sixteen)
|
||||
|
||||
@ -91,6 +136,10 @@ object Cpu extends Enumeration {
|
||||
VariableOverlap, CompactReturnDispatchParams, ZeropagePseudoregister
|
||||
)
|
||||
|
||||
private val i8080AlwaysDefaultFlags = Set(
|
||||
VariableOverlap, CompactReturnDispatchParams
|
||||
)
|
||||
|
||||
def defaultFlags(x: Cpu.Value): Set[CompilationFlag.Value] = x match {
|
||||
case StrictMos =>
|
||||
mosAlwaysDefaultFlags ++ Set(DecimalMode, PreventJmpIndirectBug)
|
||||
@ -108,6 +157,12 @@ object Cpu extends Enumeration {
|
||||
mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, Emit65CE02Opcodes)
|
||||
case Sixteen =>
|
||||
mosAlwaysDefaultFlags ++ Set(DecimalMode, EmitCmosOpcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes, ReturnWordsViaAccumulator)
|
||||
case Intel8080 =>
|
||||
i8080AlwaysDefaultFlags
|
||||
case Z80 =>
|
||||
i8080AlwaysDefaultFlags ++ Set(EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack)
|
||||
case Sharp =>
|
||||
i8080AlwaysDefaultFlags ++ Set(EmitExtended80Opcodes, EmitSharpOpcodes)
|
||||
}
|
||||
|
||||
def fromString(name: String): Cpu.Value = name match {
|
||||
@ -143,9 +198,13 @@ object Cpu extends Enumeration {
|
||||
|
||||
object CompilationFlag extends Enumeration {
|
||||
val
|
||||
// compilation options:
|
||||
EmitIllegals, EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
||||
ZeropagePseudoregister, DecimalMode, ReadOnlyArrays, PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator,
|
||||
// common compilation options:
|
||||
EmitIllegals, DecimalMode, ReadOnlyArrays,
|
||||
// compilation options for MOS:
|
||||
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
||||
ZeropagePseudoregister, PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator,
|
||||
// compilation options for Z80
|
||||
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, UseIxForStack,
|
||||
// optimization options:
|
||||
DangerousOptimizations, InlineFunctions, OptimizeForSize, OptimizeForSpeed, OptimizeForSonicSpeed,
|
||||
// memory allocation options
|
||||
@ -169,6 +228,10 @@ object CompilationFlag extends Enumeration {
|
||||
"emit_cmos" -> EmitCmosOpcodes,
|
||||
"emit_65ce02" -> Emit65CE02Opcodes,
|
||||
"emit_huc6280" -> EmitHudsonOpcodes,
|
||||
"emit_z80" -> EmitZ80Opcodes,
|
||||
"emit_x80" -> EmitExtended80Opcodes,
|
||||
"emit_sharp" -> EmitSharpOpcodes,
|
||||
"ix_stack" -> UseIxForStack,
|
||||
"zeropage_register" -> ZeropagePseudoregister,
|
||||
"decimal_mode" -> DecimalMode,
|
||||
"ro_arrays" -> ReadOnlyArrays,
|
||||
|
@ -0,0 +1,11 @@
|
||||
package millfork.assembly
|
||||
|
||||
import millfork.assembly.mos.Opcode
|
||||
import millfork.assembly.z80.{ZOpcode, ZRegisters}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
case class BranchingOpcodeMapping(mosOpcode: Opcode.Value, z80Flags: ZRegisters) {
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package millfork.assembly
|
||||
package millfork.assembly.mos
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
@ -33,7 +33,7 @@ object AddrMode extends Enumeration {
|
||||
DoesNotExist = Value
|
||||
|
||||
|
||||
def addrModeToMosString(am: AddrMode.Value, argument: String): String = {
|
||||
def addrModeToString(am: AddrMode.Value, argument: String): String = {
|
||||
am match {
|
||||
case Implied => ""
|
||||
case Immediate => "#" + argument
|
@ -1,8 +1,9 @@
|
||||
package millfork.assembly.mos
|
||||
|
||||
import millfork.assembly.{AbstractCode, AddrMode}
|
||||
import millfork.assembly.AbstractCode
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.compiler.mos.{CompilationContext, MosCompiler}
|
||||
import millfork.compiler.CompilationContext
|
||||
import millfork.compiler.mos.MosCompiler
|
||||
import millfork.env._
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
|
||||
@ -562,6 +563,6 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
|
||||
case HuSAX => "SAX"
|
||||
case _ => opcode.toString
|
||||
}
|
||||
s" $op ${AddrMode.addrModeToMosString(addrMode, parameter.toString)}"
|
||||
s" $op ${AddrMode.addrModeToString(addrMode, parameter.toString)}"
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,10 @@ package millfork.assembly.mos.opt
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
import millfork.assembly.AddrMode
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos.OpcodeClasses._
|
||||
import millfork.assembly.mos.{opt, _}
|
||||
import millfork.assembly.mos.{AddrMode, opt, _}
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.env._
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
|
||||
|
||||
import IndexReg._
|
||||
import IndexDirection._
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
|
||||
type IndexReg = IndexReg.Value
|
||||
|
@ -3,7 +3,7 @@ package millfork.assembly.mos.opt
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.mos.{AssemblyLine, Opcode, State}
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.OpcodeClasses._
|
||||
import millfork.env._
|
||||
|
||||
|
@ -3,6 +3,7 @@ package millfork.assembly.mos.opt
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.assembly.mos.AssemblyLine
|
||||
import millfork.assembly.mos.OpcodeClasses
|
||||
import millfork.assembly.opt.AnyStatus
|
||||
import millfork.env.{Label, MemoryAddressConstant, NormalFunction, NumericConstant}
|
||||
|
||||
/**
|
||||
@ -34,7 +35,7 @@ object CoarseFlowAnalyzer {
|
||||
var currentStatus: CpuStatus = functionStartStatus
|
||||
for (i <- codeArray.indices) {
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
if (flagArray(i) != currentStatus) {
|
||||
changed = true
|
||||
flagArray(i) = currentStatus
|
||||
|
@ -1,53 +1,7 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.mos.State
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
|
||||
sealed trait Status[+T] {
|
||||
|
||||
def |[U >: T](value: => Status[U]): Status[U] = this match {
|
||||
case AnyStatus | UnknownStatus => value
|
||||
case x => x
|
||||
}
|
||||
|
||||
def contains[U >: T](value: U): Boolean
|
||||
|
||||
def ~[U >: T](that: Status[U]): Status[U] = {
|
||||
(this, that) match {
|
||||
case (AnyStatus, _) => AnyStatus
|
||||
case (_, AnyStatus) => AnyStatus
|
||||
case (SingleStatus(x), SingleStatus(y)) => if (x == y) SingleStatus(x) else AnyStatus
|
||||
case (SingleStatus(x), UnknownStatus) => SingleStatus(x)
|
||||
case (UnknownStatus, SingleStatus(x)) => SingleStatus(x)
|
||||
case (UnknownStatus, UnknownStatus) => UnknownStatus
|
||||
}
|
||||
}
|
||||
|
||||
def <*>[U, V](that: Status[U])(f: (T,U) => V): Status[V] = (this, that) match {
|
||||
case (SingleStatus(t), SingleStatus(u)) => SingleStatus(f(t, u))
|
||||
case (UnknownStatus, UnknownStatus) => UnknownStatus
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def map[U](f: T => U): Status[U] = this match {
|
||||
case SingleStatus(x) => SingleStatus(f(x))
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def flatMap[U](f: T => Status[U]): Status[U] = this match {
|
||||
case SingleStatus(x) => f(x)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def exists(predicate: T => Boolean): Boolean = this match {
|
||||
case AnyStatus | UnknownStatus => false
|
||||
case SingleStatus(x) => predicate(x)
|
||||
}
|
||||
}
|
||||
import millfork.assembly.opt._
|
||||
|
||||
object SourceOfNZ {
|
||||
val A = SingleStatus(SourceOfNZ(a = true))
|
||||
@ -82,171 +36,9 @@ case class SourceOfNZ(a: Boolean = false, aw: Boolean = false, x: Boolean = fals
|
||||
}
|
||||
}
|
||||
|
||||
object Status {
|
||||
|
||||
val SingleTrue: Status[Boolean] = SingleStatus(true)
|
||||
val SingleFalse: Status[Boolean] = SingleStatus(false)
|
||||
val SingleZero: Status[Int] = SingleStatus(0)
|
||||
val SingleFF: Status[Int] = SingleStatus(0xff)
|
||||
|
||||
@inline
|
||||
private def wrapBool(b: Boolean) = if (b) SingleTrue else SingleFalse
|
||||
|
||||
def flatMap2[T, U, R](a: Status[T], b: Status[U])(f: (T, U) => Status[R]): Status[R] = (a, b) match {
|
||||
case (SingleStatus(t), SingleStatus(u)) => f(t, u)
|
||||
case (UnknownStatus, UnknownStatus) => UnknownStatus
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def flatMap3[T, U, V, R](a: Status[T], b: Status[U], c: Status[V])(f: (T, U, V) => Status[R]): Status[R] = (a, b, c) match {
|
||||
case (SingleStatus(t), SingleStatus(u), SingleStatus(v)) => f(t, u, v)
|
||||
case (UnknownStatus, UnknownStatus, UnknownStatus) => UnknownStatus
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
implicit class BoolStatusOps(val inner: Status[Boolean]) extends AnyVal {
|
||||
def withHiddenHi: Status[Boolean] = inner match {
|
||||
case SingleStatus(false) => inner
|
||||
case _ => AnyStatus
|
||||
}
|
||||
def negate: Status[Boolean] = inner match {
|
||||
case SingleStatus(x) => wrapBool(!x)
|
||||
case x => x
|
||||
}
|
||||
}
|
||||
implicit class SourceOfNZStatusOps(val inner: Status[SourceOfNZ]) extends AnyVal {
|
||||
def isFromA: Boolean = inner match {
|
||||
case SingleStatus(v) => v.a
|
||||
case _ => false
|
||||
}
|
||||
def isFromAW: Boolean = inner match {
|
||||
case SingleStatus(v) => v.aw
|
||||
case _ => false
|
||||
}
|
||||
def isFromX: Boolean = inner match {
|
||||
case SingleStatus(v) => v.x
|
||||
case _ => false
|
||||
}
|
||||
def isFromY: Boolean = inner match {
|
||||
case SingleStatus(v) => v.y
|
||||
case _ => false
|
||||
}
|
||||
def isFromIZ: Boolean = inner match {
|
||||
case SingleStatus(v) => v.iz
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
implicit class IntStatusOps(val inner: Status[Int]) extends AnyVal {
|
||||
|
||||
def bit0: Status[Boolean] = inner match {
|
||||
case SingleStatus(x) => SingleStatus((x & 1) != 0)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def bit7: Status[Boolean] = inner match {
|
||||
case SingleStatus(x) => SingleStatus((x & 0x80) != 0)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def z(f: Int => Int = identity): Status[Boolean] = inner match {
|
||||
case SingleStatus(x) =>
|
||||
val y = f(x) & 0xff
|
||||
wrapBool(y == 0)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def n(f: Int => Int = identity): Status[Boolean] = inner match {
|
||||
case SingleStatus(x) =>
|
||||
val y = f(x) & 0xff
|
||||
wrapBool(y >= 0x80)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def zw(f: Int => Int = identity): Status[Boolean] = inner match {
|
||||
case SingleStatus(x) =>
|
||||
val y = f(x) & 0xffff
|
||||
wrapBool(y == 0)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def nw(f: Int => Int = identity): Status[Boolean] = inner match {
|
||||
case SingleStatus(x) =>
|
||||
val y = f(x) & 0xffff
|
||||
wrapBool(y >= 0x8000)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def lo: Status[Int] = inner match {
|
||||
case SingleStatus(x) => SingleStatus(x & 0xff)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def hi: Status[Int] = inner match {
|
||||
case SingleStatus(x) => SingleStatus(x.&(0xff00).>>(8))
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def adc(value: Int, carry: Status[Boolean], decimal: Status[Boolean]): Status[Int] = inner match {
|
||||
case SingleStatus(x) => decimal match {
|
||||
case SingleStatus(false) => carry match {
|
||||
case SingleStatus(true) => SingleStatus((x + value + 1) & 0xff)
|
||||
case SingleStatus(false) => SingleStatus((x + value) & 0xff)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
case _ => AnyStatus
|
||||
}
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def sbc(value: Int, carry: Status[Boolean], decimal: Status[Boolean]): Status[Int] = inner match {
|
||||
case SingleStatus(x) => decimal match {
|
||||
case SingleStatus(false) => carry match {
|
||||
case SingleStatus(true) => SingleStatus((x - value) & 0xff)
|
||||
case SingleStatus(false) => SingleStatus((x - value - 1) & 0xff)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
case _ => AnyStatus
|
||||
}
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def adc_w(value: Int, carry: Status[Boolean], decimal: Status[Boolean]): Status[Int] = inner match {
|
||||
case SingleStatus(x) => decimal match {
|
||||
case SingleStatus(false) => carry match {
|
||||
case SingleStatus(true) => SingleStatus((x + value + 1) & 0xffff)
|
||||
case SingleStatus(false) => SingleStatus((x + value) & 0xffff)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
case _ => AnyStatus
|
||||
}
|
||||
case _ => AnyStatus
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
case class SingleStatus[T](t: T) extends Status[T] {
|
||||
override def contains[U >: T](value: U): Boolean = t == value
|
||||
|
||||
override def toString: String = t match {
|
||||
case true => "1"
|
||||
case false => "0"
|
||||
case _ => t.toString
|
||||
}
|
||||
}
|
||||
|
||||
case object UnknownStatus extends Status[Nothing] {
|
||||
override def contains[U >: Nothing](value: U) = false
|
||||
|
||||
override def toString: String = "_"
|
||||
}
|
||||
|
||||
case object AnyStatus extends Status[Nothing] {
|
||||
override def contains[U >: Nothing](value: U) = false
|
||||
|
||||
override def toString: String = "#"
|
||||
}
|
||||
//noinspection RedundantNewCaseClass
|
||||
case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
ah: Status[Int] = UnknownStatus,
|
||||
|
@ -3,7 +3,7 @@ package millfork.assembly.mos.opt
|
||||
import millfork.assembly._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import AddrMode._
|
||||
import millfork.env._
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,7 @@ import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.mos.AssemblyLine
|
||||
import millfork.env._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.error.ErrorReporting
|
||||
|
||||
import scala.collection.{immutable, mutable}
|
||||
|
@ -2,6 +2,7 @@ package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.mos.Opcode
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.opt.{AnyStatus, SingleStatus, Status}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
|
@ -2,6 +2,7 @@ package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.mos.Opcode
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.opt.{AnyStatus, Status}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
|
@ -2,6 +2,7 @@ package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.mos.Opcode
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.opt.{AnyStatus, SingleStatus, Status}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
|
@ -3,7 +3,7 @@ package millfork.assembly.mos.opt
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.mos.AssemblyLine
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.env.NumericConstant
|
||||
|
||||
/**
|
||||
|
@ -2,9 +2,8 @@ package millfork.assembly.mos.opt
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
import millfork.assembly.mos.{AssemblyLine, Opcode}
|
||||
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode}
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode
|
||||
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.CompilationOptions
|
||||
|
@ -1,8 +1,7 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.mos.AssemblyLine
|
||||
import millfork.assembly.mos.{AddrMode, AssemblyLine}
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.assembly.AddrMode
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
|
||||
import millfork.error.ErrorReporting
|
||||
|
@ -3,7 +3,7 @@ package millfork.assembly.mos.opt
|
||||
import millfork.assembly._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import AddrMode._
|
||||
import millfork.assembly.mos.OpcodeClasses._
|
||||
import millfork.env.{Constant, NormalFunction, NumericConstant}
|
||||
|
||||
|
@ -3,8 +3,9 @@ package millfork.assembly.mos.opt
|
||||
import millfork.CompilationOptions
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.{AssemblyLine, OpcodeClasses}
|
||||
import millfork.assembly.opt.SingleStatus
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
|
||||
|
@ -6,7 +6,7 @@ import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.assembly.mos.AssemblyLine
|
||||
import millfork.assembly.mos.OpcodeClasses._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.env.{Constant, Label, MemoryAddressConstant}
|
||||
|
||||
/**
|
||||
|
@ -114,7 +114,7 @@ object ReverseFlowAnalyzer {
|
||||
var currentImportance: CpuImportance = finalImportance
|
||||
for (i <- codeArray.indices.reverse) {
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import AddrMode._
|
||||
if (importanceArray(i) != currentImportance) {
|
||||
changed = true
|
||||
importanceArray(i) = currentImportance
|
||||
|
@ -3,6 +3,7 @@ package millfork.assembly.mos.opt
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.assembly._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.assembly.opt.SingleStatus
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
|
||||
|
@ -3,8 +3,8 @@ package millfork.assembly.mos.opt
|
||||
import millfork.{CompilationFlag, CompilationOptions, NonOverlappingIntervals}
|
||||
import millfork.assembly._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.{AssemblyLine, Opcode}
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode}
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.OpcodeClasses._
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.mos.{AssemblyLine, OpcodeClasses, State}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.{CompilationFlag, CompilationOptions, OptimizationPresets}
|
||||
import millfork.assembly.{AddrMode, AssemblyOptimization}
|
||||
import millfork.assembly.mos.{AssemblyLine, Opcode}
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode}
|
||||
import millfork.env.NormalFunction
|
||||
import millfork.error.ErrorReporting
|
||||
|
||||
|
@ -5,7 +5,7 @@ import java.util.concurrent.atomic.AtomicInteger
|
||||
import millfork.assembly._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import AddrMode._
|
||||
import millfork.assembly.mos.OpcodeClasses._
|
||||
import millfork.env.{Constant, NormalFunction, NumericConstant}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.{CompilationFlag, CompilationOptions, NonOverlappingIntervals}
|
||||
import millfork.assembly.{AddrMode, AssemblyOptimization}
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.mos._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import AddrMode._
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.mos.AssemblyLine
|
||||
import millfork.env.{CompoundConstant, Constant, MathOperator}
|
||||
|
215
src/main/scala/millfork/assembly/opt/Status.scala
Normal file
215
src/main/scala/millfork/assembly/opt/Status.scala
Normal file
@ -0,0 +1,215 @@
|
||||
package millfork.assembly.opt
|
||||
|
||||
import millfork.assembly.mos.opt.SourceOfNZ
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
|
||||
sealed trait Status[+T] {
|
||||
|
||||
def |[U >: T](value: => Status[U]): Status[U] = this match {
|
||||
case AnyStatus | UnknownStatus => value
|
||||
case x => x
|
||||
}
|
||||
|
||||
def contains[U >: T](value: U): Boolean
|
||||
|
||||
def ~[U >: T](that: Status[U]): Status[U] = {
|
||||
(this, that) match {
|
||||
case (AnyStatus, _) => AnyStatus
|
||||
case (_, AnyStatus) => AnyStatus
|
||||
case (SingleStatus(x), SingleStatus(y)) => if (x == y) SingleStatus(x) else AnyStatus
|
||||
case (SingleStatus(x), UnknownStatus) => SingleStatus(x)
|
||||
case (UnknownStatus, SingleStatus(x)) => SingleStatus(x)
|
||||
case (UnknownStatus, UnknownStatus) => UnknownStatus
|
||||
}
|
||||
}
|
||||
|
||||
def <*>[U, V](that: Status[U])(f: (T,U) => V): Status[V] = (this, that) match {
|
||||
case (SingleStatus(t), SingleStatus(u)) => SingleStatus(f(t, u))
|
||||
case (UnknownStatus, UnknownStatus) => UnknownStatus
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def map[U](f: T => U): Status[U] = this match {
|
||||
case SingleStatus(x) => SingleStatus(f(x))
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def flatMap[U](f: T => Status[U]): Status[U] = this match {
|
||||
case SingleStatus(x) => f(x)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def exists(predicate: T => Boolean): Boolean = this match {
|
||||
case AnyStatus | UnknownStatus => false
|
||||
case SingleStatus(x) => predicate(x)
|
||||
}
|
||||
}
|
||||
|
||||
case class SingleStatus[T](t: T) extends Status[T] {
|
||||
override def contains[U >: T](value: U): Boolean = t == value
|
||||
|
||||
override def toString: String = t match {
|
||||
case true => "1"
|
||||
case false => "0"
|
||||
case _ => t.toString
|
||||
}
|
||||
}
|
||||
|
||||
case object UnknownStatus extends Status[Nothing] {
|
||||
override def contains[U >: Nothing](value: U) = false
|
||||
|
||||
override def toString: String = "_"
|
||||
}
|
||||
|
||||
case object AnyStatus extends Status[Nothing] {
|
||||
override def contains[U >: Nothing](value: U) = false
|
||||
|
||||
override def toString: String = "#"
|
||||
}
|
||||
|
||||
object Status {
|
||||
|
||||
val SingleTrue: Status[Boolean] = SingleStatus(true)
|
||||
val SingleFalse: Status[Boolean] = SingleStatus(false)
|
||||
val SingleZero: Status[Int] = SingleStatus(0)
|
||||
val SingleFF: Status[Int] = SingleStatus(0xff)
|
||||
|
||||
@inline
|
||||
private def wrapBool(b: Boolean) = if (b) SingleTrue else SingleFalse
|
||||
|
||||
def flatMap2[T, U, R](a: Status[T], b: Status[U])(f: (T, U) => Status[R]): Status[R] = (a, b) match {
|
||||
case (SingleStatus(t), SingleStatus(u)) => f(t, u)
|
||||
case (UnknownStatus, UnknownStatus) => UnknownStatus
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def flatMap3[T, U, V, R](a: Status[T], b: Status[U], c: Status[V])(f: (T, U, V) => Status[R]): Status[R] = (a, b, c) match {
|
||||
case (SingleStatus(t), SingleStatus(u), SingleStatus(v)) => f(t, u, v)
|
||||
case (UnknownStatus, UnknownStatus, UnknownStatus) => UnknownStatus
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
implicit class BoolStatusOps(val inner: Status[Boolean]) extends AnyVal {
|
||||
def withHiddenHi: Status[Boolean] = inner match {
|
||||
case SingleStatus(false) => inner
|
||||
case _ => AnyStatus
|
||||
}
|
||||
def negate: Status[Boolean] = inner match {
|
||||
case SingleStatus(x) => wrapBool(!x)
|
||||
case x => x
|
||||
}
|
||||
}
|
||||
implicit class SourceOfNZStatusOps(val inner: Status[SourceOfNZ]) extends AnyVal {
|
||||
def isFromA: Boolean = inner match {
|
||||
case SingleStatus(v) => v.a
|
||||
case _ => false
|
||||
}
|
||||
def isFromAW: Boolean = inner match {
|
||||
case SingleStatus(v) => v.aw
|
||||
case _ => false
|
||||
}
|
||||
def isFromX: Boolean = inner match {
|
||||
case SingleStatus(v) => v.x
|
||||
case _ => false
|
||||
}
|
||||
def isFromY: Boolean = inner match {
|
||||
case SingleStatus(v) => v.y
|
||||
case _ => false
|
||||
}
|
||||
def isFromIZ: Boolean = inner match {
|
||||
case SingleStatus(v) => v.iz
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
implicit class IntStatusOps(val inner: Status[Int]) extends AnyVal {
|
||||
|
||||
def bit0: Status[Boolean] = inner match {
|
||||
case SingleStatus(x) => SingleStatus((x & 1) != 0)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def bit7: Status[Boolean] = inner match {
|
||||
case SingleStatus(x) => SingleStatus((x & 0x80) != 0)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def z(f: Int => Int = identity): Status[Boolean] = inner match {
|
||||
case SingleStatus(x) =>
|
||||
val y = f(x) & 0xff
|
||||
wrapBool(y == 0)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def n(f: Int => Int = identity): Status[Boolean] = inner match {
|
||||
case SingleStatus(x) =>
|
||||
val y = f(x) & 0xff
|
||||
wrapBool(y >= 0x80)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def zw(f: Int => Int = identity): Status[Boolean] = inner match {
|
||||
case SingleStatus(x) =>
|
||||
val y = f(x) & 0xffff
|
||||
wrapBool(y == 0)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def nw(f: Int => Int = identity): Status[Boolean] = inner match {
|
||||
case SingleStatus(x) =>
|
||||
val y = f(x) & 0xffff
|
||||
wrapBool(y >= 0x8000)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def lo: Status[Int] = inner match {
|
||||
case SingleStatus(x) => SingleStatus(x & 0xff)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def hi: Status[Int] = inner match {
|
||||
case SingleStatus(x) => SingleStatus(x.&(0xff00).>>(8))
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def adc(value: Int, carry: Status[Boolean], decimal: Status[Boolean]): Status[Int] = inner match {
|
||||
case SingleStatus(x) => decimal match {
|
||||
case SingleStatus(false) => carry match {
|
||||
case SingleStatus(true) => SingleStatus((x + value + 1) & 0xff)
|
||||
case SingleStatus(false) => SingleStatus((x + value) & 0xff)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
case _ => AnyStatus
|
||||
}
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def sbc(value: Int, carry: Status[Boolean], decimal: Status[Boolean]): Status[Int] = inner match {
|
||||
case SingleStatus(x) => decimal match {
|
||||
case SingleStatus(false) => carry match {
|
||||
case SingleStatus(true) => SingleStatus((x - value) & 0xff)
|
||||
case SingleStatus(false) => SingleStatus((x - value - 1) & 0xff)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
case _ => AnyStatus
|
||||
}
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def adc_w(value: Int, carry: Status[Boolean], decimal: Status[Boolean]): Status[Int] = inner match {
|
||||
case SingleStatus(x) => decimal match {
|
||||
case SingleStatus(false) => carry match {
|
||||
case SingleStatus(true) => SingleStatus((x + value + 1) & 0xffff)
|
||||
case SingleStatus(false) => SingleStatus((x + value) & 0xffff)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
case _ => AnyStatus
|
||||
}
|
||||
case _ => AnyStatus
|
||||
}
|
||||
}
|
||||
|
||||
}
|
359
src/main/scala/millfork/assembly/z80/ZLine.scala
Normal file
359
src/main/scala/millfork/assembly/z80/ZLine.scala
Normal file
@ -0,0 +1,359 @@
|
||||
package millfork.assembly.z80
|
||||
|
||||
import millfork.assembly.AbstractCode
|
||||
import millfork.env.{Constant, Label, NumericConstant, ThingInMemory}
|
||||
import millfork.node.ZRegister
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
object ZFlag extends Enumeration {
|
||||
val Z, P, C, S, H, N = Value
|
||||
}
|
||||
|
||||
sealed trait ZRegisters
|
||||
|
||||
case object NoRegisters extends ZRegisters
|
||||
|
||||
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 TwoRegisters(target: ZRegister.Value, source: ZRegister.Value) extends ZRegisters
|
||||
|
||||
object ZLine {
|
||||
|
||||
import ZOpcode._
|
||||
import ZRegister._
|
||||
|
||||
def label(label: String): ZLine = ZLine.label(Label(label))
|
||||
|
||||
def label(label: Label): ZLine = ZLine(LABEL, NoRegisters, label.toAddress)
|
||||
|
||||
def jump(label: String): ZLine = ZLine(JP, NoRegisters, Label(label).toAddress)
|
||||
|
||||
def jump(label: Label): ZLine = ZLine(JP, NoRegisters, label.toAddress)
|
||||
|
||||
def jump(label: String, condition: ZRegisters): ZLine = ZLine(JP, condition, Label(label).toAddress)
|
||||
|
||||
def jump(label: Label, condition: ZRegisters): ZLine = ZLine(JP, condition, label.toAddress)
|
||||
|
||||
def implied(opcode: ZOpcode.Value): ZLine = ZLine(opcode, NoRegisters, Constant.Zero)
|
||||
|
||||
def register(opcode: ZOpcode.Value, register: ZRegister.Value): ZLine = ZLine(opcode, OneRegister(register), Constant.Zero)
|
||||
|
||||
def imm8(opcode: ZOpcode.Value, value: Constant): ZLine = ZLine(opcode, OneRegister(IMM_8), value)
|
||||
|
||||
def imm8(opcode: ZOpcode.Value, value: Int): ZLine = ZLine(opcode, OneRegister(IMM_8), NumericConstant(value & 0xff, 1))
|
||||
|
||||
def registers(opcode: ZOpcode.Value, target: ZRegister.Value, source: ZRegister.Value): ZLine = ZLine(opcode, TwoRegisters(target, source), Constant.Zero)
|
||||
|
||||
def ld8(target: ZRegister.Value, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegisters(target, source), Constant.Zero)
|
||||
|
||||
def ld16(target: ZRegister.Value, source: ZRegister.Value): ZLine = ZLine(LD_16, TwoRegisters(target, source), Constant.Zero)
|
||||
|
||||
def ldImm8(target: ZRegister.Value, source: Int): ZLine = ZLine(LD, TwoRegisters(target, IMM_8), NumericConstant(source & 0xff, 1))
|
||||
|
||||
def ldImm8(target: ZRegister.Value, source: Constant): ZLine = ZLine(LD, TwoRegisters(target, IMM_8), source)
|
||||
|
||||
def ldImm16(target: ZRegister.Value, source: Int): ZLine = ZLine(LD_16, TwoRegisters(target, IMM_16), NumericConstant(source & 0xffff, 2))
|
||||
|
||||
def ldImm16(target: ZRegister.Value, source: Constant): ZLine = ZLine(LD_16, TwoRegisters(target, IMM_16), source)
|
||||
|
||||
def ldAbs8(target: ZRegister.Value, source: ThingInMemory): ZLine = ZLine(LD, TwoRegisters(target, MEM_ABS_8), source.toAddress)
|
||||
|
||||
def ldAbs16(target: ZRegister.Value, source: ThingInMemory): ZLine = ZLine(LD_16, TwoRegisters(target, MEM_ABS_16), source.toAddress)
|
||||
|
||||
def ldAbs8(target: ZRegister.Value, source: Constant): ZLine = ZLine(LD, TwoRegisters(target, MEM_ABS_8), source)
|
||||
|
||||
def ldAbs16(target: ZRegister.Value, source: Constant): ZLine = ZLine(LD_16, TwoRegisters(target, MEM_ABS_16), source)
|
||||
|
||||
def ldAbs8(target: ThingInMemory, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegisters(MEM_ABS_8, source), target.toAddress)
|
||||
|
||||
def ldAbs16(target: ThingInMemory, source: ZRegister.Value): ZLine = ZLine(LD_16, TwoRegisters(MEM_ABS_16, source), target.toAddress)
|
||||
|
||||
def ldAbs8(target: Constant, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegisters(MEM_ABS_8, source), target)
|
||||
|
||||
def ldAbs16(target: Constant, source: ZRegister.Value): ZLine = ZLine(LD_16, TwoRegisters(MEM_ABS_16, source), target)
|
||||
}
|
||||
|
||||
case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Constant, elidable: Boolean = true) extends AbstractCode {
|
||||
|
||||
override def sizeInBytes: Int = {
|
||||
import ZOpcode._
|
||||
import ZRegister._
|
||||
val inherent = opcode match {
|
||||
case BYTE => 1
|
||||
case DISCARD_BCDEIX | DISCARD_A | DISCARD_F | DISCARD_HL => 0
|
||||
case JP => registers match {
|
||||
case OneRegister(HL | IX | IY) => 0
|
||||
case _ => 2
|
||||
}
|
||||
case JR => 2
|
||||
case o if ZOpcodeClasses.EdInstructions(o) => 2
|
||||
case o if ZOpcodeClasses.CbInstructions(o) => 2
|
||||
case o if ZOpcodeClasses.CbInstructionsUnlessA(o) => if (registers == OneRegister(ZRegister.A)) 1 else 2
|
||||
case _ => 1 // TODO!!!
|
||||
}
|
||||
val fromParams = registers match {
|
||||
case OneRegister(IX | IXL | IXH | IY | IYH | IYL | IMM_8) => 1
|
||||
case OneRegister(MEM_IX_D | MEM_IY_D | IMM_16 | MEM_ABS_8 | MEM_ABS_16) => 2
|
||||
case TwoRegisters(_, IX | IXL | IXH | IY | IYH | IYL | IMM_8) => 1
|
||||
case TwoRegisters(_, MEM_IX_D | MEM_IY_D | IMM_16 | MEM_ABS_8 | MEM_ABS_16) => 2
|
||||
case TwoRegisters(IX | IXL | IXH | IY | IYH | IYL | IMM_8, _) => 1
|
||||
case TwoRegisters(MEM_IX_D | MEM_IY_D | IMM_16 | MEM_ABS_8 | MEM_ABS_16, _) => 2
|
||||
case _ => 0
|
||||
}
|
||||
inherent + fromParams
|
||||
}
|
||||
|
||||
|
||||
override def isPrintable: Boolean = true
|
||||
|
||||
private def asAssemblyString(r: ZRegister.Value): String = r match {
|
||||
case ZRegister.A => "A"
|
||||
case ZRegister.B => "B"
|
||||
case ZRegister.C => "C"
|
||||
case ZRegister.D => "D"
|
||||
case ZRegister.E => "E"
|
||||
case ZRegister.H => "H"
|
||||
case ZRegister.L => "L"
|
||||
case ZRegister.AF => "AF"
|
||||
case ZRegister.BC => "BC"
|
||||
case ZRegister.DE => "DE"
|
||||
case ZRegister.HL => "HL"
|
||||
case ZRegister.SP => "SP"
|
||||
case ZRegister.IX => "IX"
|
||||
case ZRegister.IY => "IY"
|
||||
case ZRegister.IXH => "IXH"
|
||||
case ZRegister.IYH => "IYH"
|
||||
case ZRegister.IXL => "IXL"
|
||||
case ZRegister.IYL => "IYL"
|
||||
case ZRegister.R => "R"
|
||||
case ZRegister.I => "I"
|
||||
case ZRegister.MEM_ABS_8 => s"($parameter)"
|
||||
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_HL => "(HL)"
|
||||
case ZRegister.MEM_BC => "(BC)"
|
||||
case ZRegister.MEM_DE => "(DE)"
|
||||
}
|
||||
|
||||
override def toString: String = {
|
||||
import ZOpcode._
|
||||
opcode match {
|
||||
case DISCARD_A => " ; DISCARD_A"
|
||||
case DISCARD_HL => " ; DISCARD_HL"
|
||||
case DISCARD_F => " ; DISCARD_F"
|
||||
case DISCARD_BCDEIX => " ; DISCARD_BCDEIX"
|
||||
case BYTE => " !byte " + parameter.toString // TODO: format?
|
||||
case LABEL => parameter.toString + ":"
|
||||
case EX_AF_AF => " EX AF,AF'"
|
||||
case EX_SP => registers match {
|
||||
case OneRegister(r) => s" EX (SP),${asAssemblyString(r)})"
|
||||
case _ => ???
|
||||
}
|
||||
case JP | JR =>
|
||||
val ps = registers match {
|
||||
case NoRegisters => parameter.toString
|
||||
case IfFlagSet(ZFlag.P) => " PO,$parameter"
|
||||
case IfFlagClear(ZFlag.P) => " PE,$parameter"
|
||||
case IfFlagSet(f) => s" $f,$parameter"
|
||||
case IfFlagClear(f) => s" N$f,$parameter"
|
||||
case OneRegister(r) => s" (${asAssemblyString(r)})"
|
||||
}
|
||||
s" $opcode$ps"
|
||||
case o =>
|
||||
val os = o.toString//.stripSuffix("_16")
|
||||
val ps = registers match {
|
||||
case NoRegisters => ""
|
||||
case IfFlagSet(ZFlag.P) => " PO"
|
||||
case IfFlagClear(ZFlag.P) => " PE"
|
||||
case IfFlagSet(f) => s" $f"
|
||||
case IfFlagClear(f) => s" N$f"
|
||||
case OneRegister(r) => s" ${asAssemblyString(r)}"
|
||||
case TwoRegisters(t, s) => s" ${asAssemblyString(t)},${asAssemblyString(s)}"
|
||||
}
|
||||
s" $os$ps"
|
||||
}
|
||||
}
|
||||
|
||||
def readsRegister(r: ZRegister.Value): Boolean = {
|
||||
import ZOpcode._
|
||||
import ZRegister._
|
||||
r match {
|
||||
case HL => readsRegister(L) || readsRegister(H)
|
||||
case BC => readsRegister(B) || readsRegister(C)
|
||||
case DE => readsRegister(D) || readsRegister(E)
|
||||
case IX => readsRegister(IXH) || readsRegister(IXL)
|
||||
case IY => readsRegister(IYH) || readsRegister(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 _ =>
|
||||
opcode match {
|
||||
case LD => (registers match {
|
||||
case TwoRegisters(_, MEM_HL) => r == H || r == L
|
||||
case TwoRegisters(_, MEM_BC) => r == B || r == C
|
||||
case TwoRegisters(_, MEM_DE) => r == D || r == E
|
||||
case TwoRegisters(_, MEM_IX_D) => r == IXH || r == IXL
|
||||
case TwoRegisters(_, MEM_IY_D) => r == IYH || r == IYL
|
||||
case TwoRegisters(_, MEM_ABS_8 | MEM_ABS_16 | IMM_8 | IMM_16) => false
|
||||
case TwoRegisters(_, s) => r == s
|
||||
case _ => false
|
||||
}) || (registers match {
|
||||
case TwoRegisters(MEM_HL, _) => r == H || r == L
|
||||
case TwoRegisters(MEM_BC, _) => r == B || r == C
|
||||
case TwoRegisters(MEM_DE, _) => r == D || r == E
|
||||
case TwoRegisters(MEM_IX_D, _) => r == IXH || r == IXL
|
||||
case TwoRegisters(MEM_IY_D, _) => r == IYH || r == IYL
|
||||
case _ => false
|
||||
})
|
||||
case LD_16 => registers match {
|
||||
case TwoRegisters(_, MEM_ABS_8 | MEM_ABS_16 | IMM_8 | IMM_16) => false
|
||||
case TwoRegisters(_, MEM_HL | HL) => r == H || r == L
|
||||
case TwoRegisters(_, MEM_BC | BC) => r == B || r == C
|
||||
case TwoRegisters(_, MEM_DE | DE) => r == D || r == E
|
||||
case TwoRegisters(_, MEM_IX_D | IX) => r == IXH || r == IXL
|
||||
case TwoRegisters(_, MEM_IY_D | IY) => r == IYH || r == IYL
|
||||
case TwoRegisters(_, s) => r == s
|
||||
case _ => false
|
||||
}
|
||||
case ADD | ADC | OR | XOR | CP | SUB | SBC => registers match {
|
||||
case OneRegister(MEM_HL) => r == H || r == L || r == A
|
||||
case OneRegister(MEM_BC) => r == B || r == C || r == A
|
||||
case OneRegister(MEM_DE) => r == D || r == E || r == A
|
||||
case OneRegister(MEM_IX_D) => r == IXH || r == IXL || r == A
|
||||
case OneRegister(MEM_IY_D) => r == IYH || r == IYL || r == A
|
||||
case OneRegister(IMM_8 | IMM_16) => r == A
|
||||
case OneRegister(s) => r == s || r == A
|
||||
case _ => r == A
|
||||
}
|
||||
case INC | DEC | RL | RLC | RR | RRC | SLA | SLL | SRA | SRL => registers match {
|
||||
case OneRegister(MEM_HL) => r == H || r == L
|
||||
case OneRegister(MEM_BC) => r == B || r == C
|
||||
case OneRegister(MEM_DE) => r == D || r == E
|
||||
case OneRegister(MEM_IX_D) => r == IXH || r == IXL
|
||||
case OneRegister(MEM_IY_D) => r == IYH || r == IYL
|
||||
case OneRegister(s) => r == s
|
||||
case _ => false
|
||||
}
|
||||
case INC_16 | DEC_16 | PUSH => registers match {
|
||||
case OneRegister(HL) => r == H || r == L
|
||||
case OneRegister(BC) => r == B || r == C
|
||||
case OneRegister(DE) => r == D || r == E
|
||||
case OneRegister(IX) => r == IXH || r == IXL
|
||||
case OneRegister(IY) => r == IYH || r == IYL
|
||||
case OneRegister(AF) => r == A
|
||||
case _ => false
|
||||
}
|
||||
case JP | JR | RET | RETI | RETN |
|
||||
POP |
|
||||
DISCARD_A | DISCARD_BCDEIX | DISCARD_HL | DISCARD_F => false
|
||||
case DJNZ => r == B
|
||||
case DAA => r == A
|
||||
case _ => true // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def changesRegister(r: ZRegister.Value): Boolean = {
|
||||
import ZOpcode._
|
||||
import ZRegister._
|
||||
r match {
|
||||
case HL => changesRegister(L) || changesRegister(H)
|
||||
case BC => changesRegister(B) || changesRegister(C)
|
||||
case DE => changesRegister(D) || changesRegister(E)
|
||||
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 _ =>
|
||||
opcode match {
|
||||
case LD => registers match {
|
||||
case TwoRegisters(s, _) => r == s
|
||||
case _ => false
|
||||
}
|
||||
case LD_16 | ADD_16 | SBC_16 | ADC_16 => registers match {
|
||||
case TwoRegisters(HL, _) => r == H || r == L
|
||||
case TwoRegisters(BC, _) => r == B || r == C
|
||||
case TwoRegisters(DE, _) => r == D || r == E
|
||||
case TwoRegisters(IX, _) => r == IXH || r == IXL
|
||||
case TwoRegisters(IY, _) => r == IYH || r == IYL
|
||||
case TwoRegisters(s, _) => r == s
|
||||
case _ => false
|
||||
}
|
||||
case INC | DEC | RL | RLC | RR | RRC | SLA | SLL | SRA | SRL => registers match {
|
||||
case OneRegister(s) => r == s
|
||||
case _ => false
|
||||
}
|
||||
case INC_16 | DEC_16 | POP => registers match {
|
||||
case OneRegister(HL) => r == H || r == L
|
||||
case OneRegister(BC) => r == B || r == C
|
||||
case OneRegister(DE) => r == D || r == E
|
||||
case OneRegister(IX) => r == IXH || r == IXL
|
||||
case OneRegister(IY) => r == IYH || r == IYL
|
||||
case OneRegister(AF) => r == A
|
||||
case _ => false
|
||||
}
|
||||
case JP | JR | RET | RETI | RETN |
|
||||
POP |
|
||||
DISCARD_A | DISCARD_BCDEIX | DISCARD_HL | DISCARD_F => false
|
||||
case ADD | ADC | OR | XOR | SUB | SBC | DAA => r == A
|
||||
case CP => false
|
||||
case DJNZ => r == B
|
||||
case _ => true // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def readsMemory: Boolean = {
|
||||
import ZOpcode._
|
||||
import ZRegister._
|
||||
opcode match {
|
||||
case POP => true
|
||||
case LD | LD_16 => registers match {
|
||||
case TwoRegisters(_, MEM_IX_D | MEM_ABS_16 | MEM_ABS_8 | MEM_DE | MEM_BC | MEM_IY_D | MEM_HL) => true
|
||||
case _ => false
|
||||
}
|
||||
case ADC_16 | ADD_16 | SBC_16 => registers match {
|
||||
case TwoRegisters(_, MEM_IX_D | MEM_ABS_16 | MEM_ABS_8 | MEM_DE | MEM_BC | MEM_IY_D | MEM_HL) => true
|
||||
case TwoRegisters(MEM_IX_D | MEM_ABS_16 | MEM_ABS_8 | MEM_DE | MEM_BC | MEM_IY_D | MEM_HL, _) => true
|
||||
case _ => false
|
||||
}
|
||||
case ADD | ADC | OR | XOR | CP | SUB | SBC | INC | DEC | INC_16 | DEC_16 | RL | RLC | RR | RRC | SLA | SLL | SRA | SRL => registers match {
|
||||
case OneRegister(MEM_IX_D | MEM_ABS_16 | MEM_ABS_8 | MEM_DE | MEM_BC | MEM_IY_D | MEM_HL) => true
|
||||
case _ => false
|
||||
}
|
||||
case JP | JR | RET | RETI | RETN |
|
||||
PUSH | DJNZ | DAA |
|
||||
DISCARD_A | DISCARD_BCDEIX | DISCARD_HL | DISCARD_F => false
|
||||
case _ => true // TODO
|
||||
}
|
||||
}
|
||||
|
||||
def changesMemory: Boolean = {
|
||||
import ZOpcode._
|
||||
import ZRegister._
|
||||
opcode match {
|
||||
case POP => true
|
||||
case LD | LD_16 | ADC_16 | ADD_16 | SBC_16 => registers match {
|
||||
case TwoRegisters(MEM_IX_D | MEM_ABS_16 | MEM_ABS_8 | MEM_DE | MEM_BC | MEM_IY_D | MEM_HL, _) => true
|
||||
case _ => false
|
||||
}
|
||||
case INC | DEC | INC_16 | DEC_16 | RL | RLC | RR | RRC | SLA | SLL | SRA | SRL => registers match {
|
||||
case OneRegister(MEM_IX_D | MEM_ABS_16 | MEM_ABS_8 | MEM_DE | MEM_BC | MEM_IY_D | MEM_HL) => true
|
||||
case _ => false
|
||||
}
|
||||
case JP | JR | RET | RETI | RETN |
|
||||
PUSH | DJNZ | DAA |
|
||||
DISCARD_A | DISCARD_BCDEIX | DISCARD_HL | DISCARD_F => false
|
||||
case _ => true // TODO
|
||||
}
|
||||
}
|
||||
}
|
50
src/main/scala/millfork/assembly/z80/ZOpcode.scala
Normal file
50
src/main/scala/millfork/assembly/z80/ZOpcode.scala
Normal file
@ -0,0 +1,50 @@
|
||||
package millfork.assembly.z80
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object ZOpcode extends Enumeration {
|
||||
val LD,
|
||||
ADD, ADC, SUB, SBC, AND, XOR, OR, CP,
|
||||
INC, DEC,
|
||||
LD_16, ADD_16, ADC_16, SBC_16, INC_16, DEC_16,
|
||||
IN_IMM, IN_C, OUT_IMM, OUT_C,
|
||||
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
||||
LDI, LDIR, LDD, LDDR,
|
||||
CPI, CPIR, CPD, CPDR,
|
||||
SCF, CCF, DAA, CPL, NEG,
|
||||
POP, PUSH,
|
||||
NOP,
|
||||
RLC, RRC, RL, RR, SLA, SRA, SRL, SLL, RLD,
|
||||
BIT, RES, SET,
|
||||
EXX, EX_DE_HL, EX_AF_AF, EX_SP,
|
||||
RST, IM, EI, DI,
|
||||
DJNZ, JP, JR, CALL, RET, RETN, RETI, HALT,
|
||||
DISCARD_A, DISCARD_F, DISCARD_HL, DISCARD_BCDEIX,
|
||||
LABEL, BYTE = Value
|
||||
}
|
||||
|
||||
object ZOpcodeClasses {
|
||||
|
||||
import ZOpcode._
|
||||
|
||||
val EdInstructions = Set(SLA, SRA, SRL, SLL, BIT, RES, SET)
|
||||
val CbInstructions = Set(SLA, SRA, SRL, SLL, BIT, RES, SET)
|
||||
val CbInstructionsUnlessA = Set(RLC, RRC, RL, RR)
|
||||
|
||||
val ChangesBCAlways = Set(
|
||||
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
||||
LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR,
|
||||
EXX, CALL, JR, JP, LABEL, DJNZ)
|
||||
val ChangesHLAlways = Set(
|
||||
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
||||
LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR,
|
||||
EXX, EX_DE_HL, CALL, JR, JP, LABEL)
|
||||
val ChangesDEAlways = Set(
|
||||
LDI, LDIR, LDD, LDDR,
|
||||
EXX, EX_DE_HL, CALL, JR, JP, LABEL)
|
||||
val ChangesOnlyRegister = Set(INC, DEC, INC_16, DEC_16, POP, EX_SP, IN_C, IN_IMM, RL, RR, RLC, RRC, SLA, SRA, SRL, SLL, SET, RES)
|
||||
val ChangesFirstRegister = Set(LD, LD_16, ADD_16, SBC_16)
|
||||
val ChangesAAlways = Set(DAA, ADD, ADC, SUB, SBC, XOR, OR, AND)
|
||||
val NonLinear = Set(JP, JR, CALL, LABEL, BYTE, EXX, EX_DE_HL, EX_SP, EXX, RET, RETI, RETN, HALT)
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
package millfork.assembly.z80.opt
|
||||
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.z80.{TwoRegisters, ZLine, ZOpcode}
|
||||
import millfork.assembly.z80.ZOpcode._
|
||||
import millfork.env.{Constant, NumericConstant}
|
||||
import millfork.node.ZRegister
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object AlwaysGoodZ80Optimizations {
|
||||
|
||||
def for7Registers(f: ZRegister.Value => AssemblyRuleSet) = MultipleAssemblyRules(
|
||||
List(ZRegister.A, ZRegister.B, ZRegister.C, ZRegister.D, ZRegister.E, ZRegister.H, ZRegister.L).map(f))
|
||||
|
||||
def for5LargeRegisters(f: ZRegister.Value => AssemblyRuleSet) = MultipleAssemblyRules(
|
||||
List(ZRegister.HL, ZRegister.BC, ZRegister.DE, ZRegister.IX, ZRegister.IY).map(f))
|
||||
|
||||
def for6Registers(f: ZRegister.Value => AssemblyRuleSet) = MultipleAssemblyRules(
|
||||
List(ZRegister.B, ZRegister.C, ZRegister.D, ZRegister.E, ZRegister.H, ZRegister.L).map(f))
|
||||
|
||||
val LoadingKnownValueFromAnotherRegister = new RuleBasedAssemblyOptimization("Loading known value from another register",
|
||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||
for7Registers(register =>
|
||||
(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)
|
||||
)))
|
||||
)
|
||||
)
|
||||
|
||||
val ReloadingKnownValueFromMemory = new RuleBasedAssemblyOptimization("Reloading known value from memory",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
for7Registers(register =>
|
||||
Is8BitLoad(ZRegister.MEM_HL, register) ~
|
||||
(Linear & Not(Changes(ZRegister.H)) & Not(Changes(ZRegister.L)) & Not(ChangesMemory) & Not(Changes(register)) & Not(IsRegular8BitLoadFrom(ZRegister.MEM_HL))).* ~
|
||||
(Elidable & Is8BitLoad(register, ZRegister.MEM_HL)) ~~> { code => code.init }
|
||||
),
|
||||
for7Registers(register =>
|
||||
Is8BitLoad(ZRegister.MEM_HL, register) ~
|
||||
(Linear & Not(Changes(ZRegister.H)) & Not(Changes(ZRegister.L)) & Not(ChangesMemory) & Not(Changes(register)) & Not(IsRegular8BitLoadFrom(ZRegister.MEM_HL))).* ~
|
||||
(Elidable & IsRegular8BitLoadFrom(ZRegister.MEM_HL)) ~~> { code =>
|
||||
val last = code.last
|
||||
code.init :+ last.copy(registers = TwoRegisters(last.registers.asInstanceOf[TwoRegisters].target, register))
|
||||
}
|
||||
),
|
||||
(Is8BitLoad(ZRegister.MEM_ABS_8, ZRegister.A) & MatchParameter(0)).captureLine(1) ~
|
||||
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~
|
||||
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8) & MatchParameter(0)) ~~> { code => code.init }
|
||||
)
|
||||
|
||||
val PointlessLoad = new RuleBasedAssemblyOptimization("Pointless load",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
for7Registers(register =>
|
||||
(Elidable & Is8BitLoadTo(register) & DoesntMatterWhatItDoesWith(register)) ~~> (_ => Nil)
|
||||
),
|
||||
for5LargeRegisters(register =>
|
||||
(Elidable & Is16BitLoadTo(register) & DoesntMatterWhatItDoesWith(register)) ~~> (_ => Nil)
|
||||
),
|
||||
for7Registers(register =>
|
||||
(Is8BitLoad(register, ZRegister.IMM_8) & MatchImmediate(0)) ~
|
||||
(Linear & Not(Changes(register))).* ~
|
||||
(Elidable & Is8BitLoad(register, ZRegister.IMM_8) & MatchImmediate(0)) ~~> (_.init)
|
||||
),
|
||||
for5LargeRegisters(register =>
|
||||
(Is16BitLoad(register, ZRegister.IMM_16) & MatchImmediate(0)) ~
|
||||
(Linear & Not(Changes(register))).* ~
|
||||
(Elidable & Is16BitLoad(register, ZRegister.IMM_16) & MatchImmediate(0)) ~~> (_.init)
|
||||
),
|
||||
)
|
||||
|
||||
private def simplifiable16BitAddWithSplitTarget(targetH: ZRegister.Value, targetL: ZRegister.Value, target: ZRegister.Value, source: ZRegister.Value) = MultipleAssemblyRules(List(
|
||||
(Is8BitLoad(targetH, ZRegister.IMM_8) & MatchImmediate(1)) ~
|
||||
(Linear & Not(Changes(target))).* ~
|
||||
(Is8BitLoad(targetL, ZRegister.IMM_8) & MatchImmediate(2)) ~
|
||||
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
|
||||
(Is16BitLoad(source, ZRegister.IMM_16) & MatchImmediate(0)) ~
|
||||
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
|
||||
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(target, source)) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
|
||||
val value = (ctx.get[Constant](0) + ctx.get[Constant](1).asl(8) + ctx.get[Constant](2)).quickSimplify
|
||||
code.init :+ ZLine.ldImm16(target, value)
|
||||
},
|
||||
(Is8BitLoad(targetL, ZRegister.IMM_8) & MatchImmediate(2)) ~
|
||||
(Linear & Not(Changes(target))).* ~
|
||||
(Is8BitLoad(targetH, ZRegister.IMM_8) & MatchImmediate(1)) ~
|
||||
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
|
||||
(Is16BitLoad(source, ZRegister.IMM_16) & MatchImmediate(0)) ~
|
||||
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
|
||||
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(target, source)) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
|
||||
val value = (ctx.get[Constant](0) + ctx.get[Constant](1).asl(8) + ctx.get[Constant](2)).quickSimplify
|
||||
code.init :+ ZLine.ldImm16(target, value)
|
||||
},
|
||||
(Is16BitLoad(source, ZRegister.IMM_16) & MatchImmediate(0)) ~
|
||||
(Linear & Not(Changes(targetL)) & Not(Changes(source))).* ~
|
||||
(Is8BitLoad(targetL, ZRegister.IMM_8) & MatchImmediate(2)) ~
|
||||
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
|
||||
(Is8BitLoad(targetH, ZRegister.IMM_8) & MatchImmediate(1)) ~
|
||||
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
|
||||
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(target, source)) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
|
||||
val value = (ctx.get[Constant](0) + ctx.get[Constant](1).asl(8) + ctx.get[Constant](2)).quickSimplify
|
||||
code.init :+ ZLine.ldImm16(target, value)
|
||||
},
|
||||
(Is16BitLoad(source, ZRegister.IMM_16) & MatchImmediate(0)) ~
|
||||
(Linear & Not(Changes(targetH)) & Not(Changes(source))).* ~
|
||||
(Is8BitLoad(targetH, ZRegister.IMM_8) & MatchImmediate(1)) ~
|
||||
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
|
||||
(Is8BitLoad(targetL, ZRegister.IMM_8) & MatchImmediate(2)) ~
|
||||
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
|
||||
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(target, source)) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
|
||||
val value = (ctx.get[Constant](0) + ctx.get[Constant](1).asl(8) + ctx.get[Constant](2)).quickSimplify
|
||||
code.init :+ ZLine.ldImm16(target, value)
|
||||
},
|
||||
))
|
||||
|
||||
val SimplifiableMaths = new RuleBasedAssemblyOptimization("Simplifiable maths",
|
||||
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))),
|
||||
),
|
||||
simplifiable16BitAddWithSplitTarget(ZRegister.H, ZRegister.L, ZRegister.HL, ZRegister.BC),
|
||||
simplifiable16BitAddWithSplitTarget(ZRegister.H, ZRegister.L, ZRegister.HL, ZRegister.DE),
|
||||
simplifiable16BitAddWithSplitTarget(ZRegister.IXH, ZRegister.IXL, ZRegister.IX, ZRegister.BC),
|
||||
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),
|
||||
)
|
||||
|
||||
val FreeHL = new RuleBasedAssemblyOptimization("Free HL",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.IMM_16)) ~
|
||||
(Elidable & Is8BitLoadTo(ZRegister.MEM_HL) & DoesntMatterWhatItDoesWith(ZRegister.HL, ZRegister.A)) ~~> (code =>
|
||||
List(
|
||||
code(1).copy(registers = TwoRegisters(ZRegister.A, code(1).registers.asInstanceOf[TwoRegisters].source)),
|
||||
code.head.copy(opcode = LD, registers = TwoRegisters(ZRegister.MEM_ABS_8, ZRegister.A)),
|
||||
)),
|
||||
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.IMM_16)) ~
|
||||
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_HL) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
|
||||
List(
|
||||
code.head.copy(opcode = LD, registers = TwoRegisters(ZRegister.A, ZRegister.MEM_ABS_8)),
|
||||
)),
|
||||
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.IMM_16)) ~
|
||||
(Elidable & IsRegular8BitLoadFrom(ZRegister.MEM_HL) & DoesntMatterWhatItDoesWith(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.A)) ~~> (code =>
|
||||
List(
|
||||
code.head.copy(opcode = LD, registers = TwoRegisters(ZRegister.A, ZRegister.MEM_ABS_8)),
|
||||
code(1).copy(registers = TwoRegisters(code(1).registers.asInstanceOf[TwoRegisters].target, ZRegister.A)),
|
||||
)),
|
||||
)
|
||||
|
||||
val All: List[AssemblyOptimization[ZLine]] = List[AssemblyOptimization[ZLine]](
|
||||
FreeHL,
|
||||
LoadingKnownValueFromAnotherRegister,
|
||||
PointlessLoad,
|
||||
ReloadingKnownValueFromMemory,
|
||||
SimplifiableMaths,
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,86 @@
|
||||
package millfork.assembly.z80.opt
|
||||
|
||||
import millfork.assembly.mos.{AssemblyLine, OpcodeClasses}
|
||||
import millfork.assembly.opt.{AnyStatus, SingleStatus}
|
||||
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZLine, ZOpcodeClasses}
|
||||
import millfork.env.{Label, MemoryAddressConstant, NormalFunction, NumericConstant}
|
||||
import millfork.node.ZRegister
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
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()
|
||||
val flagArray = Array.fill[CpuStatus](code.length)(emptyStatus)
|
||||
val codeArray = code.toArray
|
||||
|
||||
var changed = true
|
||||
while (changed) {
|
||||
changed = false
|
||||
var currentStatus: CpuStatus = functionStartStatus
|
||||
for (i <- codeArray.indices) {
|
||||
import millfork.assembly.z80.ZOpcode._
|
||||
if (flagArray(i) != currentStatus) {
|
||||
changed = true
|
||||
flagArray(i) = currentStatus
|
||||
}
|
||||
codeArray(i) match {
|
||||
case ZLine(LABEL, _, MemoryAddressConstant(Label(l)), _) =>
|
||||
val L = l
|
||||
currentStatus = codeArray.indices.flatMap(j => codeArray(j) match {
|
||||
case ZLine(_, _, MemoryAddressConstant(Label(L)), _) => Some(flagArray(j))
|
||||
case _ => None
|
||||
}).fold(currentStatus)(_ ~ _)
|
||||
|
||||
case ZLine(CALL | 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)
|
||||
case ZLine(SUB, 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(AND, 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(OR, 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(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(LD, TwoRegisters(t, ZRegister.IMM_8), NumericConstant(value, _), _) =>
|
||||
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt))
|
||||
case ZLine(LD | LD_16, TwoRegisters(t, s), _, _) =>
|
||||
currentStatus = currentStatus.setRegister(t, currentStatus.getRegister(s))
|
||||
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))
|
||||
case ZLine(opcode, registers, _, _) =>
|
||||
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||
if (ZOpcodeClasses.ChangesAAlways(opcode)) currentStatus = currentStatus.copy(a = AnyStatus)
|
||||
if (ZOpcodeClasses.ChangesBCAlways(opcode)) currentStatus = currentStatus.copy(b = AnyStatus, c = AnyStatus)
|
||||
if (ZOpcodeClasses.ChangesDEAlways(opcode)) currentStatus = currentStatus.copy(d = AnyStatus, e = AnyStatus)
|
||||
registers match {
|
||||
case OneRegister(r) => currentStatus = currentStatus.setRegister(r, AnyStatus)
|
||||
case TwoRegisters(r1, r2) => currentStatus = currentStatus.setRegister(r1, AnyStatus).setRegister(r2, AnyStatus)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
// flagArray.zip(codeArray).foreach{
|
||||
// case (fl, y) => if (y.isPrintable) println(f"$fl%-32s $y%-32s")
|
||||
// }
|
||||
// println("---------------------")
|
||||
}
|
||||
|
||||
flagArray.toList
|
||||
}
|
||||
}
|
126
src/main/scala/millfork/assembly/z80/opt/CpuStatus.scala
Normal file
126
src/main/scala/millfork/assembly/z80/opt/CpuStatus.scala
Normal file
@ -0,0 +1,126 @@
|
||||
//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
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
b: Status[Int] = UnknownStatus,
|
||||
c: Status[Int] = UnknownStatus,
|
||||
d: Status[Int] = UnknownStatus,
|
||||
e: Status[Int] = UnknownStatus,
|
||||
h: Status[Int] = UnknownStatus,
|
||||
l: Status[Int] = UnknownStatus,
|
||||
ixh: Status[Int] = UnknownStatus,
|
||||
ixl: Status[Int] = UnknownStatus,
|
||||
iyh: Status[Int] = UnknownStatus,
|
||||
iyl: Status[Int] = UnknownStatus,
|
||||
zf: Status[Boolean] = UnknownStatus,
|
||||
nf: Status[Boolean] = UnknownStatus,
|
||||
cf: Status[Boolean] = UnknownStatus,
|
||||
sf: Status[Boolean] = UnknownStatus,
|
||||
pf: Status[Boolean] = UnknownStatus,
|
||||
hf: Status[Boolean] = UnknownStatus
|
||||
) {
|
||||
def setRegister(target: ZRegister.Value, value: Status[Int]): CpuStatus = target match {
|
||||
case ZRegister.A => this.copy(a = value)
|
||||
case ZRegister.B => this.copy(b = value)
|
||||
case ZRegister.C => this.copy(c = value)
|
||||
case ZRegister.D => this.copy(d = value)
|
||||
case ZRegister.E => this.copy(e = value)
|
||||
case ZRegister.H => this.copy(h = value)
|
||||
case ZRegister.L => this.copy(l = value)
|
||||
case ZRegister.IXH => this.copy(ixh = value)
|
||||
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_IY_D => this
|
||||
case ZRegister.MEM_HL => this
|
||||
case ZRegister.MEM_BC => this
|
||||
case ZRegister.MEM_DE => this
|
||||
case ZRegister.MEM_ABS_8 => this
|
||||
case ZRegister.MEM_ABS_16 => this
|
||||
case ZRegister.R => this
|
||||
case ZRegister.I => this
|
||||
case ZRegister.SP => this
|
||||
case ZRegister.BC => this.copy(b = value.hi, c = value.lo)
|
||||
case ZRegister.DE => this.copy(d = value.hi, e = value.lo)
|
||||
case ZRegister.HL => this.copy(h = value.hi, l = value.lo)
|
||||
case ZRegister.IX => this.copy(ixh = value.hi, ixl = value.lo)
|
||||
case ZRegister.IY => this.copy(iyh = value.hi, iyl = value.lo)
|
||||
case ZRegister.AF => this.copy(a = value.hi, cf = AnyStatus, zf = AnyStatus, hf = AnyStatus, pf = AnyStatus, sf = AnyStatus)
|
||||
}
|
||||
|
||||
val mergeBytes: (Int, Int) => Int = (h, l) => (h & 0xff) * 256 + (l & 0xff)
|
||||
|
||||
def getRegister(register: ZRegister.Value): Status[Int] = register match {
|
||||
case ZRegister.A => a
|
||||
case ZRegister.B => b
|
||||
case ZRegister.C => c
|
||||
case ZRegister.D => d
|
||||
case ZRegister.E => e
|
||||
case ZRegister.H => h
|
||||
case ZRegister.L => l
|
||||
case ZRegister.IXH => ixh
|
||||
case ZRegister.IXL => ixl
|
||||
case ZRegister.IYH => iyh
|
||||
case ZRegister.IYL => iyl
|
||||
case ZRegister.MEM_IX_D => AnyStatus
|
||||
case ZRegister.MEM_IY_D => AnyStatus
|
||||
case ZRegister.MEM_HL => AnyStatus
|
||||
case ZRegister.MEM_BC => AnyStatus
|
||||
case ZRegister.MEM_DE => AnyStatus
|
||||
case ZRegister.MEM_ABS_8 => AnyStatus
|
||||
case ZRegister.MEM_ABS_16 => AnyStatus
|
||||
case ZRegister.R => AnyStatus
|
||||
case ZRegister.I => AnyStatus
|
||||
case ZRegister.SP => AnyStatus
|
||||
case ZRegister.BC => (b <*> c) (mergeBytes)
|
||||
case ZRegister.DE => (d <*> e) (mergeBytes)
|
||||
case ZRegister.HL => (h <*> l) (mergeBytes)
|
||||
case ZRegister.IX => (ixh <*> ixl) (mergeBytes)
|
||||
case ZRegister.IY => (iyh <*> iyl) (mergeBytes)
|
||||
case ZRegister.AF => AnyStatus
|
||||
case ZRegister.IMM_8 => AnyStatus
|
||||
case ZRegister.IMM_16 => AnyStatus
|
||||
}
|
||||
|
||||
def getFlag(register: ZFlag.Value): Status[Boolean] = register match {
|
||||
case ZFlag.C => cf
|
||||
case ZFlag.H => hf
|
||||
case ZFlag.P => pf
|
||||
case ZFlag.Z => zf
|
||||
case ZFlag.S => sf
|
||||
case ZFlag.N => nf
|
||||
}
|
||||
def ~(that: CpuStatus) = new CpuStatus(
|
||||
a = this.a ~ that.a,
|
||||
b = this.b ~ that.b,
|
||||
c = this.c ~ that.c,
|
||||
d = this.d ~ that.d,
|
||||
e = this.e ~ that.e,
|
||||
h = this.h ~ that.h,
|
||||
l = this.l ~ that.l,
|
||||
ixh = this.ixh ~ that.ixh,
|
||||
ixl = this.ixl ~ that.ixl,
|
||||
iyh = this.iyh ~ that.iyh,
|
||||
iyl = this.iyl ~ that.iyl,
|
||||
nf = this.nf ~ that.nf,
|
||||
sf = this.sf ~ that.sf,
|
||||
zf = this.zf ~ that.zf,
|
||||
cf = this.cf ~ that.cf,
|
||||
pf = this.pf ~ that.pf,
|
||||
hf = this.hf ~ that.hf,
|
||||
)
|
||||
|
||||
|
||||
|
||||
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"
|
||||
}
|
54
src/main/scala/millfork/assembly/z80/opt/FlowAnalyzer.scala
Normal file
54
src/main/scala/millfork/assembly/z80/opt/FlowAnalyzer.scala
Normal file
@ -0,0 +1,54 @@
|
||||
package millfork.assembly.z80.opt
|
||||
|
||||
import millfork.CompilationOptions
|
||||
import millfork.assembly.mos.{AssemblyLine, Opcode, State}
|
||||
import millfork.assembly.z80.ZLine
|
||||
import millfork.env.{Label, MemoryAddressConstant, NormalFunction}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
class FlowHolder(_statusBefore: () => List[CpuStatus], _importanceAfter: () => List[CpuImportance]) {
|
||||
lazy val statusBefore: List[CpuStatus] = _statusBefore()
|
||||
lazy val importanceAfter: List[CpuImportance] = _importanceAfter()
|
||||
}
|
||||
|
||||
case class FlowInfo(holder: FlowHolder, index: Int, _labelUseCountMap: () => Option[Map[String, Int]]) {
|
||||
|
||||
lazy val statusBefore: CpuStatus = holder.statusBefore(index)
|
||||
lazy val importanceAfter = holder.importanceAfter(index)
|
||||
lazy val labelUseCountMap: Option[Map[String, Int]] = _labelUseCountMap()
|
||||
|
||||
def labelUseCount(label: String): Int = labelUseCountMap.map(_.getOrElse(label, 0)).getOrElse(-1)
|
||||
}
|
||||
|
||||
object FlowAnalyzer {
|
||||
|
||||
private val EmptyCpuStatus = CpuStatus()
|
||||
private val EmptyCpuImportance = CpuImportance()
|
||||
|
||||
def analyze(f: NormalFunction, code: List[ZLine], options: CompilationOptions, req: FlowInfoRequirement.Value): List[(FlowInfo, ZLine)] = {
|
||||
val forwardFlow = req match {
|
||||
case FlowInfoRequirement.BothFlows | FlowInfoRequirement.ForwardFlow =>
|
||||
() => CoarseFlowAnalyzer.analyze(f, code, options)
|
||||
case FlowInfoRequirement.BackwardFlow | FlowInfoRequirement.JustLabels | FlowInfoRequirement.NoRequirement =>
|
||||
() => List.fill(code.size)(EmptyCpuStatus)
|
||||
}
|
||||
val reverseFlow = req match {
|
||||
case FlowInfoRequirement.BothFlows | FlowInfoRequirement.BackwardFlow =>
|
||||
() => ReverseFlowAnalyzer.analyze(f, code)
|
||||
case FlowInfoRequirement.ForwardFlow | FlowInfoRequirement.JustLabels | FlowInfoRequirement.NoRequirement =>
|
||||
() => List.fill(code.size)(EmptyCpuImportance)
|
||||
}
|
||||
val labelMap: (() => Option[Map[String, Int]]) = () => req match {
|
||||
case FlowInfoRequirement.NoRequirement => None
|
||||
case _ => Some(code.flatMap {
|
||||
case ZLine(op, _, MemoryAddressConstant(Label(l)), _) if op != Opcode.LABEL => Some(l)
|
||||
case _ => None
|
||||
}.groupBy(identity).mapValues(_.size))
|
||||
}
|
||||
val holder = new FlowHolder(forwardFlow, reverseFlow)
|
||||
code.zipWithIndex.map{ case (line, i) => FlowInfo(holder, i, labelMap) -> line}
|
||||
}
|
||||
}
|
@ -0,0 +1,241 @@
|
||||
package millfork.assembly.z80.opt
|
||||
|
||||
import millfork.assembly.z80._
|
||||
import millfork.env._
|
||||
import millfork.node.ZRegister
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
sealed trait Importance {
|
||||
def ~(that: Importance): Importance = (this, that) match {
|
||||
case (_, Important) | (Important, _) => Important
|
||||
case (_, Unimportant) | (Unimportant, _) => Unimportant
|
||||
case (UnknownImportance, UnknownImportance) => UnknownImportance
|
||||
}
|
||||
}
|
||||
|
||||
case object Important extends Importance {
|
||||
override def toString = "!"
|
||||
}
|
||||
|
||||
case object Unimportant extends Importance {
|
||||
override def toString = "*"
|
||||
}
|
||||
|
||||
case object UnknownImportance extends Importance {
|
||||
override def toString = "?"
|
||||
}
|
||||
|
||||
//noinspection RedundantNewCaseClass
|
||||
case class CpuImportance(a: Importance = UnknownImportance,
|
||||
b: Importance = UnknownImportance,
|
||||
c: Importance = UnknownImportance,
|
||||
d: Importance = UnknownImportance,
|
||||
e: Importance = UnknownImportance,
|
||||
h: Importance = UnknownImportance,
|
||||
l: Importance = UnknownImportance,
|
||||
ixh: Importance = UnknownImportance,
|
||||
ixl: Importance = UnknownImportance,
|
||||
iyh: Importance = UnknownImportance,
|
||||
iyl: Importance = UnknownImportance,
|
||||
zf: Importance = UnknownImportance,
|
||||
nf: Importance = UnknownImportance,
|
||||
cf: Importance = UnknownImportance,
|
||||
sf: 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"
|
||||
|
||||
def ~(that: CpuImportance) = new CpuImportance(
|
||||
a = this.a ~ that.a,
|
||||
b = this.a ~ that.a,
|
||||
c = this.a ~ that.a,
|
||||
d = this.a ~ that.a,
|
||||
e = this.a ~ that.a,
|
||||
h = this.a ~ that.a,
|
||||
l = this.a ~ that.a,
|
||||
ixh = this.ixh ~ that.ixh,
|
||||
ixl = this.ixl ~ that.ixl,
|
||||
iyh = this.iyh ~ that.iyh,
|
||||
iyl = this.iyl ~ that.iyl,
|
||||
zf = this.zf ~ that.zf,
|
||||
nf = this.nf ~ that.nf,
|
||||
cf = this.cf ~ that.cf,
|
||||
pf = this.pf ~ that.pf,
|
||||
hf = this.hf ~ that.hf,
|
||||
)
|
||||
|
||||
def getRegister(register: ZRegister.Value): Importance = register match {
|
||||
case ZRegister.A => a
|
||||
case ZRegister.B => b
|
||||
case ZRegister.C => c
|
||||
case ZRegister.D => d
|
||||
case ZRegister.E => e
|
||||
case ZRegister.H => h
|
||||
case ZRegister.L => l
|
||||
case ZRegister.IXH => ixh
|
||||
case ZRegister.IXL => ixl
|
||||
case ZRegister.IYH => iyh
|
||||
case ZRegister.IYL => iyl
|
||||
case ZRegister.HL => h ~ l
|
||||
case ZRegister.BC => b ~ c
|
||||
case ZRegister.DE => d ~ e
|
||||
case ZRegister.IX => ixh ~ ixl
|
||||
case ZRegister.IY => iyh ~ iyl
|
||||
}
|
||||
|
||||
def getFlag(register: ZFlag.Value): Importance = register match {
|
||||
case ZFlag.C => cf
|
||||
case ZFlag.H => hf
|
||||
case ZFlag.P => pf
|
||||
case ZFlag.Z => zf
|
||||
case ZFlag.S => sf
|
||||
case ZFlag.N => nf
|
||||
}
|
||||
|
||||
def butReadsRegister(r: ZRegister.Value) = 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)
|
||||
case ZRegister.C => this.copy(c = Important)
|
||||
case ZRegister.BC | ZRegister.MEM_BC => this.copy(b = Important, c = Important)
|
||||
case ZRegister.D => this.copy(d = Important)
|
||||
case ZRegister.E => this.copy(e = Important)
|
||||
case ZRegister.DE | ZRegister.MEM_DE => this.copy(d = Important, e = Important)
|
||||
case ZRegister.H => this.copy(h = Important)
|
||||
case ZRegister.L => this.copy(l = Important)
|
||||
case ZRegister.HL | ZRegister.MEM_HL => this.copy(h = Important, l = Important)
|
||||
case ZRegister.IXH => this.copy(ixh = Important)
|
||||
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.IY | ZRegister.MEM_IY_D => this.copy(iyh = Important, iyl = Important)
|
||||
case _ => this
|
||||
}
|
||||
|
||||
def butWritesRegister(r: ZRegister.Value): 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)
|
||||
case ZRegister.C => this.copy(c = Unimportant)
|
||||
case ZRegister.BC => this.copy(b = Unimportant, c = Unimportant)
|
||||
case ZRegister.MEM_BC => this.copy(b = Important, c = Important)
|
||||
case ZRegister.D => this.copy(d = Unimportant)
|
||||
case ZRegister.E => this.copy(e = Unimportant)
|
||||
case ZRegister.DE => this.copy(d = Unimportant, e = Unimportant)
|
||||
case ZRegister.MEM_DE => this.copy(d = Important, e = Important)
|
||||
case ZRegister.H => this.copy(h = Unimportant)
|
||||
case ZRegister.L => this.copy(l = Unimportant)
|
||||
case ZRegister.HL => this.copy(h = Unimportant, l = Unimportant)
|
||||
case ZRegister.MEM_HL => this.copy(h = Important, l = Important)
|
||||
case ZRegister.IXH => this.copy(ixh = Unimportant)
|
||||
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.IY => this.copy(iyh = Important, iyl = Important)
|
||||
case ZRegister.MEM_IY_D => this.copy(iyh = Important, iyl = Important)
|
||||
case _ => this
|
||||
}
|
||||
|
||||
def butReadsFlag(f: ZFlag.Value): CpuImportance = f match {
|
||||
case ZFlag.P => this.copy(pf = Important)
|
||||
case ZFlag.S => this.copy(sf = Important)
|
||||
case ZFlag.C => this.copy(cf = Important)
|
||||
case ZFlag.Z => this.copy(zf = Important)
|
||||
case ZFlag.H => this.copy(hf = Important)
|
||||
case _ => this
|
||||
}
|
||||
|
||||
def butWritesFlag(f: ZFlag.Value): CpuImportance = f match {
|
||||
case ZFlag.P => this.copy(pf = Unimportant)
|
||||
case ZFlag.S => this.copy(sf = Unimportant)
|
||||
case ZFlag.C => this.copy(cf = Unimportant)
|
||||
case ZFlag.Z => this.copy(zf = Unimportant)
|
||||
case ZFlag.H => this.copy(hf = Unimportant)
|
||||
case _ => this
|
||||
}
|
||||
}
|
||||
|
||||
object ReverseFlowAnalyzer {
|
||||
|
||||
//noinspection RedundantNewCaseClass
|
||||
def analyze(f: NormalFunction, code: List[ZLine]): List[CpuImportance] = {
|
||||
val importanceArray = Array.fill[CpuImportance](code.length)(new CpuImportance())
|
||||
val codeArray = code.toArray
|
||||
|
||||
var changed = true
|
||||
val finalImportance = new CpuImportance(
|
||||
a = Important, b = Important, c = Important, d = Important, e = Important, h = Important, l = Important,
|
||||
ixh = Important, ixl = Important, iyh = Important, iyl = Important,
|
||||
zf = Important, cf = Important, sf = Important, pf = Important, hf = Important)
|
||||
changed = true
|
||||
while (changed) {
|
||||
changed = false
|
||||
var currentImportance: CpuImportance = finalImportance
|
||||
for (i <- codeArray.indices.reverse) {
|
||||
import millfork.assembly.z80.ZOpcode._
|
||||
if (importanceArray(i) != currentImportance) {
|
||||
changed = true
|
||||
importanceArray(i) = currentImportance
|
||||
}
|
||||
val currentLine = codeArray(i)
|
||||
currentLine match {
|
||||
case ZLine(DJNZ, _, MemoryAddressConstant(Label(l)), _) =>
|
||||
val L = l
|
||||
val labelIndex = codeArray.indexWhere {
|
||||
case ZLine(LABEL, _, MemoryAddressConstant(Label(L)), _) => true
|
||||
case _ => false
|
||||
}
|
||||
currentImportance = if (labelIndex < 0) finalImportance else (importanceArray(labelIndex) ~ currentImportance).butReadsRegister(ZRegister.B).butReadsFlag(ZFlag.Z)
|
||||
case ZLine(JP | JR, IfFlagSet(_) | IfFlagClear(_), MemoryAddressConstant(Label(l)), _) =>
|
||||
val L = l
|
||||
val labelIndex = codeArray.indexWhere {
|
||||
case ZLine(LABEL, _, MemoryAddressConstant(Label(L)), _) => true
|
||||
case _ => false
|
||||
}
|
||||
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex) ~ currentImportance
|
||||
case ZLine(DISCARD_HL, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(h = Unimportant, l = Unimportant)
|
||||
case ZLine(DISCARD_BCDEIX, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(b = Unimportant, c = Unimportant, d = Unimportant, e = Unimportant, ixh = Unimportant, ixl = Unimportant)
|
||||
case ZLine(DISCARD_A, _, _, _) =>
|
||||
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 | 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, 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(CP, OneRegister(s), _, _) =>
|
||||
currentImportance = currentImportance.butReadsRegister(s)
|
||||
case ZLine(INC | DEC | INC_16 | DEC_16, OneRegister(s), _, _) =>
|
||||
currentImportance = currentImportance.butReadsRegister(s)
|
||||
case _ =>
|
||||
currentImportance = finalImportance // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
// importanceArray.zip(codeArray).foreach{
|
||||
// case (i, y) => if (y.isPrintable) println(f"$y%-32s $i%-32s")
|
||||
// }
|
||||
// println("---------------------")
|
||||
|
||||
importanceArray.toList
|
||||
}
|
||||
}
|
@ -0,0 +1,764 @@
|
||||
package millfork.assembly.z80.opt
|
||||
|
||||
import millfork.CompilationOptions
|
||||
import millfork.assembly.{z80, _}
|
||||
import millfork.assembly.opt.SingleStatus
|
||||
import millfork.assembly.z80._
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.ZRegister
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
object FlowInfoRequirement extends Enumeration {
|
||||
|
||||
val NoRequirement, JustLabels, BothFlows, ForwardFlow, BackwardFlow = Value
|
||||
|
||||
def assertForward(x: FlowInfoRequirement.Value): Unit = x match {
|
||||
case BothFlows | ForwardFlow => ()
|
||||
case NoRequirement | JustLabels | BackwardFlow => ErrorReporting.fatal("Forward flow info required")
|
||||
}
|
||||
|
||||
def assertBackward(x: FlowInfoRequirement.Value): Unit = x match {
|
||||
case BothFlows | BackwardFlow => ()
|
||||
case NoRequirement | JustLabels | ForwardFlow => ErrorReporting.fatal("Backward flow info required")
|
||||
}
|
||||
}
|
||||
|
||||
trait AssemblyRuleSet{
|
||||
def flatten: List[AssemblyRule]
|
||||
}
|
||||
|
||||
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRuleSet*) extends AssemblyOptimization[ZLine]{
|
||||
|
||||
private val actualRules = rules.flatMap(_.flatten)
|
||||
actualRules.foreach(_.pattern.validate(needsFlowInfo))
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[ZLine], options: CompilationOptions): List[ZLine] = {
|
||||
val effectiveCode = code.map(a => a.copy(parameter = a.parameter.quickSimplify))
|
||||
val taggedCode = FlowAnalyzer.analyze(f, effectiveCode, options, needsFlowInfo)
|
||||
optimizeImpl(f, taggedCode, options)
|
||||
}
|
||||
|
||||
def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, ZLine)], options: CompilationOptions): List[ZLine] = {
|
||||
code match {
|
||||
case Nil => Nil
|
||||
case head :: tail =>
|
||||
for ((rule, index) <- actualRules.zipWithIndex) {
|
||||
val ctx = new AssemblyMatchingContext(options)
|
||||
rule.pattern.matchTo(ctx, code) match {
|
||||
case Some(rest: List[(FlowInfo, ZLine)]) =>
|
||||
val matchedChunkToOptimize: List[ZLine] = code.take(code.length - rest.length).map(_._2)
|
||||
val optimizedChunk: List[ZLine] = rule.result(matchedChunkToOptimize, ctx)
|
||||
ErrorReporting.debug(s"Applied $name ($index)")
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
val before = code.head._1.statusBefore
|
||||
val after = code(matchedChunkToOptimize.length - 1)._1.importanceAfter
|
||||
ErrorReporting.trace(s"Before: $before")
|
||||
ErrorReporting.trace(s"After: $after")
|
||||
}
|
||||
matchedChunkToOptimize.filter(_.isPrintable).foreach(l => ErrorReporting.trace(l.toString))
|
||||
ErrorReporting.trace(" ↓")
|
||||
optimizedChunk.filter(_.isPrintable).foreach(l => ErrorReporting.trace(l.toString))
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
return optimizedChunk ++ optimizeImpl(f, rest, options)
|
||||
} else {
|
||||
return optimize(f, optimizedChunk ++ rest.map(_._2), options)
|
||||
}
|
||||
case None => ()
|
||||
}
|
||||
}
|
||||
head._2 :: optimizeImpl(f, tail, options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AssemblyMatchingContext(val compilationOptions: CompilationOptions) {
|
||||
private val map = mutable.Map[Int, Any]()
|
||||
|
||||
override def toString: String = map.mkString(", ")
|
||||
|
||||
def addObject(i: Int, o: Any): Boolean = {
|
||||
if (map.contains(i)) {
|
||||
map(i) == o
|
||||
} else {
|
||||
map(i) = o
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
def dontMatch(i: Int, o: Any): Boolean = {
|
||||
if (map.contains(i)) {
|
||||
map(i) != o
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private def getImpl[T: Manifest](i: Int): AnyRef = {
|
||||
if (!map.contains(i)) return null
|
||||
val t = map(i)
|
||||
val clazz = implicitly[Manifest[T]].runtimeClass match {
|
||||
case java.lang.Integer.TYPE => classOf[java.lang.Integer]
|
||||
case java.lang.Boolean.TYPE => classOf[java.lang.Boolean]
|
||||
// TODO
|
||||
case x => x
|
||||
}
|
||||
if (clazz.isInstance(t)) {
|
||||
t.asInstanceOf[AnyRef]
|
||||
} else {
|
||||
if (i eq null) {
|
||||
ErrorReporting.fatal(s"Value at index $i is null")
|
||||
} else {
|
||||
ErrorReporting.fatal(s"Value at index $i is a ${t.getClass.getSimpleName}, not a ${clazz.getSimpleName}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def get[T: Manifest](i: Int): T = {
|
||||
val v = getImpl[T](i)
|
||||
if (v eq null) {
|
||||
ErrorReporting.fatal(s"Value at index $i is null")
|
||||
}
|
||||
v.asInstanceOf[T]
|
||||
}
|
||||
|
||||
def getOrDefault[T: Manifest](i: Int, defau: T): T = {
|
||||
val v = getImpl[T](i)
|
||||
if (v eq null) {
|
||||
defau
|
||||
} else {
|
||||
v.asInstanceOf[T]
|
||||
}
|
||||
}
|
||||
|
||||
def isExternallyLinearBlock(i: Int): Boolean = {
|
||||
val labels = mutable.Set[String]()
|
||||
val jumps = mutable.Set[String]()
|
||||
import millfork.assembly.z80.ZOpcode._
|
||||
get[List[ZLine]](i).foreach {
|
||||
// JSR and BSR are allowed
|
||||
case ZLine(RET | RST | RETI | RETN, _, _, _) =>
|
||||
return false
|
||||
case ZLine(JP | JR, OneRegister(_), _, _) =>
|
||||
return false
|
||||
case ZLine(JP | JR | DJNZ, _, MemoryAddressConstant(Label(l)), _) =>
|
||||
jumps += l
|
||||
case ZLine(LABEL, _, MemoryAddressConstant(Label(l)), _) =>
|
||||
labels += l
|
||||
case ZLine(JP | JR | DJNZ, _, _, _) =>
|
||||
return false
|
||||
case _ => ()
|
||||
}
|
||||
// if a jump leads inside the block, then it's internal
|
||||
// if a jump leads outside the block, then it's external
|
||||
jumps --= labels
|
||||
jumps.isEmpty
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object HelperCheckers {
|
||||
import ZOpcode._
|
||||
import ZRegister._
|
||||
private def isBad(l: ZLine): Boolean = {
|
||||
l.opcode match {
|
||||
case LD | LD_16 | ADD_16 | ADC_16 | SBC_16 => l.registers match {
|
||||
case TwoRegisters(MEM_HL | MEM_IX_D | MEM_IY_D | MEM_BC | MEM_DE, _) => true
|
||||
case TwoRegisters(_, MEM_HL | MEM_IX_D | MEM_IY_D | MEM_BC | MEM_DE) => true
|
||||
case TwoRegisters(_, _) => false
|
||||
}
|
||||
case ADD | SUB | SBC | ADC | XOR | CP | OR | AND => l.registers match {
|
||||
case OneRegister(MEM_HL | MEM_IX_D | MEM_IY_D | MEM_BC | MEM_DE) => true
|
||||
case OneRegister(_) => false
|
||||
}
|
||||
case POP | PUSH => false
|
||||
case _ => true // TODO
|
||||
}
|
||||
}
|
||||
|
||||
def distinctThings(a: String, b: String): Boolean = {
|
||||
a.takeWhile(_ != '.') != b.takeWhile(_ != '.')
|
||||
}
|
||||
|
||||
def memoryAccessDoesntOverlap(l1: ZLine, l2: ZLine): Boolean = {
|
||||
if (!l1.readsMemory && !l1.changesMemory) return true
|
||||
if (!l2.readsMemory && !l2.changesMemory) return true
|
||||
if (isBad(l1) || isBad(l2)) return false
|
||||
|
||||
(l1.parameter.quickSimplify, l2.parameter.quickSimplify) match {
|
||||
case (NumericConstant(n1, _), NumericConstant(n2, _)) => n1 != n2
|
||||
case (MemoryAddressConstant(_: ThingInMemory), NumericConstant(_, _)) => true // TODO: ???
|
||||
case (NumericConstant(_, _), MemoryAddressConstant(_: ThingInMemory)) => true // TODO: ???
|
||||
case (CompoundConstant(MathOperator.Plus | MathOperator.Minus, MemoryAddressConstant(a: ThingInMemory), NumericConstant(_, _)), NumericConstant(_, _)) => true // TODO: ???
|
||||
case (NumericConstant(_, _), CompoundConstant(MathOperator.Plus | MathOperator.Minus, MemoryAddressConstant(a: ThingInMemory), NumericConstant(_, _))) => true // TODO: ???
|
||||
case (MemoryAddressConstant(a: ThingInMemory), MemoryAddressConstant(b: ThingInMemory)) => distinctThings(a.name, b.name) // TODO: ???
|
||||
case (CompoundConstant(op@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(a: ThingInMemory), NumericConstant(offset, _)),
|
||||
MemoryAddressConstant(b: ThingInMemory)) =>
|
||||
if (a.name == b.name) {
|
||||
offset.abs > 1
|
||||
} else {
|
||||
distinctThings(a.name, b.name) // TODO: ???
|
||||
}
|
||||
case (MemoryAddressConstant(a: ThingInMemory),
|
||||
CompoundConstant(op@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(b: ThingInMemory), NumericConstant(offset, _))) =>
|
||||
if (a.name == b.name) {
|
||||
offset.abs > 1
|
||||
} else {
|
||||
distinctThings(a.name, b.name) // TODO: ???
|
||||
}
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
case class AssemblyRule(pattern: AssemblyPattern, result: (List[ZLine], AssemblyMatchingContext) => List[ZLine]) extends AssemblyRuleSet {
|
||||
override def flatten: List[AssemblyRule] = List(this)
|
||||
}
|
||||
|
||||
case class MultipleAssemblyRules(list: List[AssemblyRuleSet]) extends AssemblyRuleSet {
|
||||
override def flatten: List[AssemblyRule] = list.flatMap(_.flatten)
|
||||
}
|
||||
|
||||
trait AssemblyPattern {
|
||||
|
||||
def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = ()
|
||||
|
||||
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, ZLine)]): Option[List[(FlowInfo, ZLine)]]
|
||||
|
||||
def ~(x: AssemblyPattern) = Concatenation(this, x)
|
||||
|
||||
def ~(x: AssemblyLinePattern) = Concatenation(this, x)
|
||||
|
||||
def ~~>(result: (List[ZLine], AssemblyMatchingContext) => List[ZLine]) = AssemblyRule(this, result)
|
||||
|
||||
def ~~>(result: List[ZLine] => List[ZLine]) = AssemblyRule(this, (code, _) => result(code))
|
||||
|
||||
def capture(i: Int) = Capture(i, this)
|
||||
|
||||
def captureLength(i: Int) = CaptureLength(i, this)
|
||||
|
||||
}
|
||||
|
||||
case class Capture(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
|
||||
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, ZLine)]): Option[List[(FlowInfo, ZLine)]] =
|
||||
for {
|
||||
rest <- pattern.matchTo(ctx, code)
|
||||
} yield {
|
||||
ctx.addObject(i, code.take(code.length - rest.length).map(_._2))
|
||||
rest
|
||||
}
|
||||
|
||||
override def toString: String = s"(?<$i>$pattern)"
|
||||
}
|
||||
|
||||
case class CaptureLine(i: Int, pattern: AssemblyLinePattern) extends AssemblyLinePattern {
|
||||
|
||||
override def toString: String = s"(?<$i>$pattern)"
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
|
||||
pattern.matchLineTo(ctx, flowInfo, line) && ctx.addObject(i, line)
|
||||
}
|
||||
}
|
||||
|
||||
case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
|
||||
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, ZLine)]): Option[List[(FlowInfo, ZLine)]] =
|
||||
for {
|
||||
rest <- pattern.matchTo(ctx, code)
|
||||
} yield {
|
||||
ctx.addObject(i, code.length - rest.length)
|
||||
rest
|
||||
}
|
||||
|
||||
override def toString: String = s"(?<$i>$pattern)"
|
||||
}
|
||||
|
||||
|
||||
case class Where(predicate: (AssemblyMatchingContext => Boolean)) extends AssemblyPattern {
|
||||
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, ZLine)]): Option[List[(FlowInfo, ZLine)]] = {
|
||||
if (predicate(ctx)) Some(code) else None
|
||||
}
|
||||
|
||||
override def toString: String = "Where(...)"
|
||||
}
|
||||
|
||||
case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends AssemblyPattern {
|
||||
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
|
||||
l.validate(needsFlowInfo)
|
||||
r.validate(needsFlowInfo)
|
||||
}
|
||||
|
||||
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, ZLine)]): Option[List[(FlowInfo, ZLine)]] = {
|
||||
for {
|
||||
middle <- l.matchTo(ctx, code)
|
||||
end <- r.matchTo(ctx, middle)
|
||||
} yield end
|
||||
}
|
||||
|
||||
override def toString: String = (l, r) match {
|
||||
case (_: Both, _: Both) => s"($l) · ($r)"
|
||||
case (_, _: Both) => s"$l · ($r)"
|
||||
case (_: Both, _) => s"($l) · $r"
|
||||
case _ => s"$l · $r"
|
||||
}
|
||||
}
|
||||
|
||||
case class Many(rule: AssemblyLinePattern) extends AssemblyPattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
|
||||
rule.validate(needsFlowInfo)
|
||||
}
|
||||
|
||||
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, ZLine)]): Option[List[(FlowInfo, ZLine)]] = {
|
||||
var c = code
|
||||
while (true) {
|
||||
c match {
|
||||
case Nil =>
|
||||
return Some(Nil)
|
||||
case x :: xs =>
|
||||
if (rule.matchLineTo(ctx, x._1, x._2)) {
|
||||
c = xs
|
||||
} else {
|
||||
return Some(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
override def toString: String = s"[$rule]*"
|
||||
}
|
||||
|
||||
case class ManyWhereAtLeastOne(rule: AssemblyLinePattern, atLeastOneIsThis: AssemblyLinePattern) extends AssemblyPattern {
|
||||
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
|
||||
rule.validate(needsFlowInfo)
|
||||
}
|
||||
|
||||
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, ZLine)]): Option[List[(FlowInfo, ZLine)]] = {
|
||||
var c = code
|
||||
var oneFound = false
|
||||
while (true) {
|
||||
c match {
|
||||
case Nil =>
|
||||
return Some(Nil)
|
||||
case x :: xs =>
|
||||
if (atLeastOneIsThis.matchLineTo(ctx, x._1, x._2)) {
|
||||
oneFound = true
|
||||
}
|
||||
if (rule.matchLineTo(ctx, x._1, x._2)) {
|
||||
c = xs
|
||||
} else {
|
||||
if (oneFound) {
|
||||
return Some(c)
|
||||
} else {
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
override def toString: String = s"[∃$atLeastOneIsThis:$rule]*"
|
||||
}
|
||||
|
||||
case class Opt(rule: AssemblyLinePattern) extends AssemblyPattern {
|
||||
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
|
||||
rule.validate(needsFlowInfo)
|
||||
}
|
||||
|
||||
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, ZLine)]): Option[List[(FlowInfo, ZLine)]] = {
|
||||
code match {
|
||||
case Nil =>
|
||||
Some(Nil)
|
||||
case x :: xs =>
|
||||
if (rule.matchLineTo(ctx, x._1, x._2)) {
|
||||
Some(xs)
|
||||
} else {
|
||||
Some(code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override def toString: String = s"[$rule]?"
|
||||
}
|
||||
|
||||
trait AssemblyLinePattern extends AssemblyPattern {
|
||||
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, ZLine)]): Option[List[(FlowInfo, ZLine)]] = code match {
|
||||
case Nil => None
|
||||
case x :: xs => if (matchLineTo(ctx, x._1, x._2)) Some(xs) else None
|
||||
}
|
||||
|
||||
def captureLine(i: Int): AssemblyLinePattern = CaptureLine(i, this)
|
||||
|
||||
def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean
|
||||
|
||||
def unary_! : AssemblyLinePattern = Not(this)
|
||||
|
||||
def ? : AssemblyPattern = Opt(this)
|
||||
|
||||
def * : AssemblyPattern = Many(this)
|
||||
|
||||
def + : AssemblyPattern = this ~ Many(this)
|
||||
|
||||
def |(x: AssemblyLinePattern): AssemblyLinePattern = EitherPattern(this, x)
|
||||
|
||||
def &(x: AssemblyLinePattern): AssemblyLinePattern = Both(this, x)
|
||||
}
|
||||
|
||||
trait TrivialAssemblyLinePattern extends AssemblyLinePattern with (ZLine => Boolean) {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = this (line)
|
||||
}
|
||||
|
||||
case class Match(predicate: (AssemblyMatchingContext => Boolean)) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = predicate(ctx)
|
||||
|
||||
override def toString: String = "Match(...)"
|
||||
}
|
||||
|
||||
//noinspection LanguageFeature
|
||||
object AssemblyLinePattern {
|
||||
implicit def __implicitOpcodeIn(ops: Set[ZOpcode.Value]): AssemblyLinePattern = HasOpcodeIn(ops)
|
||||
}
|
||||
|
||||
case class MatchRegister(register: ZRegister.Value, 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.getRegister(register) match {
|
||||
case SingleStatus(value) => ctx.addObject(i, value)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
case class MatchImmediate(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
line.registers match {
|
||||
case TwoRegisters(_, ZRegister.IMM_16 | ZRegister.IMM_8) | OneRegister(ZRegister.IMM_8 | ZRegister.IMM_16) => ctx.addObject(i, line.parameter.quickSimplify)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
case class MatchParameter(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
line.registers match {
|
||||
case TwoRegisters(_, ZRegister.IMM_16 | ZRegister.IMM_8 | ZRegister.MEM_ABS_8 | ZRegister.MEM_ABS_16) |
|
||||
TwoRegisters(ZRegister.IMM_16 | ZRegister.IMM_8 | ZRegister.MEM_ABS_8 | ZRegister.MEM_ABS_16, _) |
|
||||
OneRegister(ZRegister.IMM_8 | ZRegister.IMM_16 | ZRegister.MEM_ABS_8 | ZRegister.MEM_ABS_16) => ctx.addObject(i, line.parameter.quickSimplify)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
case class HasRegister(register: ZRegister.Value, value: 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.getRegister(register).contains(value)
|
||||
}
|
||||
|
||||
case class DoesntMatterWhatItDoesWith(registers: ZRegister.Value*) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertBackward(needsFlowInfo)
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
registers.forall(r => flowInfo.importanceAfter.getRegister(r) != Important)
|
||||
|
||||
override def toString: String = registers.mkString("[¯\\_(ツ)_/¯:", ",", "]")
|
||||
}
|
||||
|
||||
case object DoesntMatterWhatItDoesWithFlags extends AssemblyLinePattern {
|
||||
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertBackward(needsFlowInfo)
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
ZFlag.values.forall(r => flowInfo.importanceAfter.getFlag(r) != Important)
|
||||
|
||||
override def toString: String = "[¯\\_(ツ)_/¯:F]"
|
||||
}
|
||||
|
||||
case class HasSet(flag: ZFlag.Value) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
flowInfo.statusBefore.getFlag(flag).exists(_ == true)
|
||||
}
|
||||
|
||||
case class HasClear(flag: ZFlag.Value) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
|
||||
FlowInfoRequirement.assertForward(needsFlowInfo)
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
flowInfo.statusBefore.getFlag(flag).exists(_ == true)
|
||||
}
|
||||
|
||||
case object Anything extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean = true
|
||||
}
|
||||
|
||||
case class Not(inner: AssemblyLinePattern) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = inner.validate(needsFlowInfo)
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
!inner.matchLineTo(ctx, flowInfo, line)
|
||||
|
||||
override def toString: String = "¬" + inner
|
||||
}
|
||||
|
||||
case class Both(l: AssemblyLinePattern, r: AssemblyLinePattern) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
|
||||
l.validate(needsFlowInfo)
|
||||
r.validate(needsFlowInfo)
|
||||
}
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
l.matchLineTo(ctx, flowInfo, line) && r.matchLineTo(ctx, flowInfo, line)
|
||||
|
||||
override def toString: String = l + " ∧ " + r
|
||||
}
|
||||
|
||||
case class EitherPattern(l: AssemblyLinePattern, r: AssemblyLinePattern) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
|
||||
l.validate(needsFlowInfo)
|
||||
r.validate(needsFlowInfo)
|
||||
}
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
l.matchLineTo(ctx, flowInfo, line) || r.matchLineTo(ctx, flowInfo, line)
|
||||
|
||||
override def toString: String = s"($l ∨ $r)"
|
||||
}
|
||||
|
||||
case object Elidable extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
line.elidable
|
||||
}
|
||||
|
||||
case object DebugMatching extends AssemblyPattern {
|
||||
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, ZLine)]): Option[List[(FlowInfo, ZLine)]] = {
|
||||
println(ctx)
|
||||
code.foreach(println)
|
||||
Some(code)
|
||||
}
|
||||
}
|
||||
|
||||
case object Linear extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
!ZOpcodeClasses.NonLinear(line.opcode)
|
||||
}
|
||||
|
||||
case object LinearOrLabel extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean =
|
||||
line.opcode == ZOpcode.LABEL || !ZOpcodeClasses.NonLinear(line.opcode)
|
||||
}
|
||||
|
||||
case class Reads(register: ZRegister.Value) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean = line.readsRegister(register)
|
||||
}
|
||||
|
||||
case class Changes(register: ZRegister.Value) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean = line.changesRegister(register)
|
||||
}
|
||||
|
||||
case class Concerns(register: ZRegister.Value) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean = line.readsRegister(register) || line.changesRegister(register)
|
||||
}
|
||||
|
||||
case object ReadsMemory extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean = line.readsMemory
|
||||
}
|
||||
|
||||
case object ChangesMemory extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean = line.changesMemory
|
||||
}
|
||||
|
||||
case class DoesntChangeMemoryAt(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
|
||||
HelperCheckers.memoryAccessDoesntOverlap(ctx.get[ZLine](i), line)
|
||||
}
|
||||
}
|
||||
|
||||
case object ConcernsMemory extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean = line.readsMemory || line.changesMemory
|
||||
}
|
||||
|
||||
case class HasOpcode(op: ZOpcode.Value) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean =
|
||||
line.opcode == op
|
||||
|
||||
override def toString: String = op.toString
|
||||
}
|
||||
|
||||
case class Is8BitLoad(target:ZRegister.Value, source: ZRegister.Value) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean =
|
||||
line.opcode == ZOpcode.LD && line.registers == TwoRegisters(target, source)
|
||||
|
||||
override def toString: String = s"LD $target,$source"
|
||||
}
|
||||
|
||||
case class Is16BitLoad(target:ZRegister.Value, source: ZRegister.Value) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean =
|
||||
line.opcode == ZOpcode.LD_16 && line.registers == TwoRegisters(target, source)
|
||||
|
||||
override def toString: String = s"LD $target,$source"
|
||||
}
|
||||
|
||||
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 => false
|
||||
case _ => true
|
||||
})
|
||||
|
||||
override def toString: String = "LD _," + source
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
override def toString: String = s"LD $target,_"
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
override def toString: String = s"LD_16 $target,_"
|
||||
}
|
||||
|
||||
case class HasRegisterParam(register: ZRegister.Value) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean =
|
||||
line.registers match {
|
||||
case OneRegister(r) => r == register
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
case class HasRegisters(registers: ZRegisters) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean = line.registers == registers
|
||||
}
|
||||
|
||||
case class RefersTo(identifier: String, offset: Int = 999) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean = {
|
||||
line.parameter match {
|
||||
case MemoryAddressConstant(th) =>
|
||||
(offset == 999 || offset == 0) && th.name == identifier
|
||||
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(nn, _)) =>
|
||||
(offset == 999 || offset == nn) && th.name == identifier
|
||||
case CompoundConstant(MathOperator.Plus, NumericConstant(nn, _), MemoryAddressConstant(th)) =>
|
||||
(offset == 999 || offset == nn) && th.name == identifier
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
override def toString: String = s"<$identifier+$offset>"
|
||||
}
|
||||
|
||||
case class CallsAnyOf(identifiers: Set[String]) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean = {
|
||||
line.parameter match {
|
||||
case MemoryAddressConstant(th) => identifiers(th.name)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
override def toString: String = identifiers.mkString("(CALL {", ",", "})")
|
||||
}
|
||||
|
||||
case class CallsAnyExcept(identifiers: Set[String]) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean = {
|
||||
line.parameter match {
|
||||
case MemoryAddressConstant(th) => !identifiers(th.name)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
override def toString: String = identifiers.mkString("(JSR ¬{", ",", "})")
|
||||
}
|
||||
|
||||
case class HasOpcodeIn(ops: Set[ZOpcode.Value]) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean =
|
||||
ops(line.opcode)
|
||||
|
||||
override def toString: String = ops.mkString("{", ",", "}")
|
||||
}
|
||||
|
||||
case class Has8BitImmediate(i: Int) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean = (line.registers match {
|
||||
case TwoRegisters(_, ZRegister.IMM_8) => true
|
||||
case OneRegister(ZRegister.IMM_8) => true
|
||||
case _ => false
|
||||
}) && (line.parameter.quickSimplify match {
|
||||
case NumericConstant(j, _) => (i & 0xff) == (j & 0xff)
|
||||
case _ => false
|
||||
})
|
||||
|
||||
override def toString: String = "#" + i
|
||||
}
|
||||
|
||||
case class HasImmediateWhere(predicate: Int => Boolean) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: ZLine): Boolean =
|
||||
(line.registers match {
|
||||
case TwoRegisters(_, ZRegister.IMM_8) => true
|
||||
case OneRegister(ZRegister.IMM_8) => true
|
||||
case _ => false
|
||||
}) && (line.parameter.quickSimplify match {
|
||||
case NumericConstant(j, _) => predicate(j.toInt & 0xff)
|
||||
case _ => false
|
||||
})
|
||||
}
|
||||
|
||||
case class Before(pattern: AssemblyPattern) extends AssemblyLinePattern {
|
||||
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
|
||||
pattern.validate(needsFlowInfo)
|
||||
}
|
||||
|
||||
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, ZLine)]): Option[List[(FlowInfo, ZLine)]] = code match {
|
||||
case Nil => None
|
||||
case x :: xs => pattern.matchTo(ctx, xs) match {
|
||||
case Some(m) => Some(xs)
|
||||
case None => None
|
||||
}
|
||||
}
|
||||
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = ???
|
||||
}
|
||||
|
||||
case class HasCallerCount(count: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
|
||||
line match {
|
||||
case ZLine(ZOpcode.LABEL, _, MemoryAddressConstant(Label(l)), _) => flowInfo.labelUseCount(l) == count
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
case class MatchElidableCopyOf(i: Int, firstLinePattern: AssemblyLinePattern, lastLinePattern: AssemblyLinePattern) extends AssemblyPattern {
|
||||
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, ZLine)]): Option[List[(FlowInfo, ZLine)]] = {
|
||||
val pattern = ctx.get[List[ZLine]](i)
|
||||
if (code.length < pattern.length) return None
|
||||
val (before, after) = code.splitAt(pattern.length)
|
||||
val lastIndex = code.length - 1
|
||||
for (((a, (f, b)), ix) <- pattern.zip(before).zipWithIndex) {
|
||||
if (!b.elidable) return None
|
||||
if (a.opcode != b.opcode) return None
|
||||
if (a.registers != b.registers) return None
|
||||
if (a.parameter.quickSimplify != b.parameter.quickSimplify) return None
|
||||
if (ix == 0 && !firstLinePattern.matchLineTo(ctx, f, b)) return None
|
||||
if (ix == lastIndex && !lastLinePattern.matchLineTo(ctx, f, b)) return None
|
||||
}
|
||||
Some(after)
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package millfork.assembly.z80.opt
|
||||
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.z80.ZLine
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object Z80OptimizationPresets {
|
||||
val Good: List[AssemblyOptimization[ZLine]] = List.tabulate(15)(_ => AlwaysGoodZ80Optimizations.All).flatten
|
||||
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package millfork.compiler
|
||||
|
||||
import millfork.assembly.AbstractCode
|
||||
import millfork.compiler.mos.CompilationContext
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
|
@ -4,13 +4,98 @@ import millfork.env._
|
||||
import millfork.node._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.assembly.AbstractCode
|
||||
import millfork.compiler.mos.CompilationContext
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
class AbstractExpressionCompiler[T <: AbstractCode] {
|
||||
|
||||
def getExpressionType(ctx: CompilationContext, expr: Expression): Type = AbstractExpressionCompiler.getExpressionType(ctx, expr)
|
||||
|
||||
def lookupFunction(ctx: CompilationContext, f: FunctionCallExpression): MangledFunction = AbstractExpressionCompiler.lookupFunction(ctx, f)
|
||||
|
||||
def assertCompatible(exprType: Type, variableType: Type): Unit = {
|
||||
// TODO
|
||||
}
|
||||
|
||||
def callingContext(ctx: CompilationContext, v: MemoryVariable): CompilationContext = {
|
||||
val result = new Environment(Some(ctx.env), "")
|
||||
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
|
||||
}
|
||||
|
||||
def getSumSize(ctx: CompilationContext, params: List[(Boolean, Expression)]): Int = {
|
||||
params.map { case (_, expr) => getExpressionType(ctx, expr).size}.max
|
||||
}
|
||||
|
||||
def assertAllBytes(msg: String, ctx: CompilationContext, params: List[Expression]): Unit = {
|
||||
if (params.exists { expr => getExpressionType(ctx, expr).size != 1 }) {
|
||||
ErrorReporting.fatal(msg, params.head.position)
|
||||
}
|
||||
}
|
||||
|
||||
def assertBinary(ctx: CompilationContext, params: List[Expression]): (Expression, Expression, Int) = {
|
||||
if (params.length != 2) {
|
||||
ErrorReporting.fatal("sfgdgfsd", None)
|
||||
}
|
||||
(params.head, params(1)) match {
|
||||
case (l: Expression, r: Expression) => (l, r, getExpressionType(ctx, l).size max getExpressionType(ctx, r).size)
|
||||
}
|
||||
}
|
||||
|
||||
def assertComparison(ctx: CompilationContext, params: List[Expression]): (Int, Boolean) = {
|
||||
(params.head, params(1)) match {
|
||||
case (l: Expression, r: Expression) =>
|
||||
val lt = getExpressionType(ctx, l)
|
||||
val rt = getExpressionType(ctx, r)
|
||||
(lt.size max rt.size, lt.isSigned || rt.isSigned)
|
||||
}
|
||||
}
|
||||
|
||||
def assertBool(ctx: CompilationContext, fname: String, params: List[Expression], expectedParamCount: Int): Unit = {
|
||||
if (params.length != expectedParamCount) {
|
||||
ErrorReporting.error("Invalid number of parameters for " + fname, params.headOption.flatMap(_.position))
|
||||
return
|
||||
}
|
||||
params.foreach { param =>
|
||||
if (!getExpressionType(ctx, param).isInstanceOf[BooleanType])
|
||||
ErrorReporting.fatal("Parameter should be boolean", param.position)
|
||||
}
|
||||
}
|
||||
|
||||
def assertBool(ctx: CompilationContext, fname: String, params: List[Expression]): Unit = {
|
||||
if (params.length < 2) {
|
||||
ErrorReporting.error("Invalid number of parameters for " + fname, params.headOption.flatMap(_.position))
|
||||
return
|
||||
}
|
||||
params.foreach { param =>
|
||||
if (!getExpressionType(ctx, param).isInstanceOf[BooleanType])
|
||||
ErrorReporting.fatal("Parameter should be boolean", param.position)
|
||||
}
|
||||
}
|
||||
|
||||
def assertAssignmentLike(ctx: CompilationContext, params: List[Expression]): (LhsExpression, Expression, Int) = {
|
||||
if (params.length != 2) {
|
||||
ErrorReporting.fatal("sfgdgfsd", None)
|
||||
}
|
||||
(params.head, params(1)) match {
|
||||
case (l: LhsExpression, r: Expression) =>
|
||||
val lsize = getExpressionType(ctx, l).size
|
||||
val rsize = getExpressionType(ctx, r).size
|
||||
if (lsize < rsize) {
|
||||
ErrorReporting.error("Left-hand-side expression is of smaller type than the right-hand-side expression", l.position)
|
||||
}
|
||||
(l, r, lsize)
|
||||
case (err: Expression, _) => ErrorReporting.fatal("Invalid left-hand-side expression", err.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object AbstractExpressionCompiler {
|
||||
def getExpressionType(ctx: CompilationContext, expr: Expression): Type = {
|
||||
val env = ctx.env
|
||||
val b = env.get[Type]("byte")
|
||||
|
319
src/main/scala/millfork/compiler/AbstractStatementCompiler.scala
Normal file
319
src/main/scala/millfork/compiler/AbstractStatementCompiler.scala
Normal file
@ -0,0 +1,319 @@
|
||||
package millfork.compiler
|
||||
|
||||
import millfork.CpuFamily
|
||||
import millfork.assembly.{AbstractCode, BranchingOpcodeMapping}
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node._
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
abstract class AbstractStatementCompiler[T <: AbstractCode] {
|
||||
|
||||
def compile(ctx: CompilationContext, statements: List[ExecutableStatement]): List[T]
|
||||
|
||||
def nextLabel(prefix: String): String
|
||||
|
||||
def labelChunk(labelName: String): List[T]
|
||||
|
||||
def jmpChunk(labelName: String): List[T] = jmpChunk(Label(labelName))
|
||||
|
||||
def jmpChunk(label: Label): List[T]
|
||||
|
||||
def branchChunk(opcode: BranchingOpcodeMapping, labelName: String): List[T]
|
||||
|
||||
def compileExpressionForBranching(ctx: CompilationContext, expr: Expression, branching: BranchSpec): List[T]
|
||||
|
||||
def areBlocksLarge(blocks: List[T]*): Boolean
|
||||
|
||||
def compileWhileStatement(ctx: CompilationContext, s: WhileStatement): List[T] = {
|
||||
val start = nextLabel("wh")
|
||||
val middle = nextLabel("he")
|
||||
val inc = nextLabel("fp")
|
||||
val end = nextLabel("ew")
|
||||
val condType = AbstractExpressionCompiler.getExpressionType(ctx, s.condition)
|
||||
val bodyBlock = compile(ctx.addLabels(s.labels, Label(end), Label(inc)), s.body)
|
||||
val incrementBlock = compile(ctx.addLabels(s.labels, Label(end), Label(inc)), s.increment)
|
||||
val largeBodyBlock = areBlocksLarge(bodyBlock, incrementBlock)
|
||||
condType match {
|
||||
case ConstantBooleanType(_, true) =>
|
||||
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||
case ConstantBooleanType(_, false) => Nil
|
||||
case FlagBooleanType(_, jumpIfTrue, jumpIfFalse) =>
|
||||
if (largeBodyBlock) {
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, NoBranching)
|
||||
List(labelChunk(start), conditionBlock, branchChunk(jumpIfTrue, middle), jmpChunk(end), labelChunk(middle), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||
} else {
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, NoBranching)
|
||||
List(jmpChunk(middle), labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, labelChunk(middle), conditionBlock, branchChunk(jumpIfTrue, start), labelChunk(end)).flatten
|
||||
// List(labelChunk(start), conditionBlock, branchChunk(jumpIfFalse, end), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||
}
|
||||
case BuiltInBooleanType =>
|
||||
if (largeBodyBlock) {
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, BranchIfTrue(middle))
|
||||
List(labelChunk(start), conditionBlock, jmpChunk(end), labelChunk(middle), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||
} else {
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, BranchIfTrue(start))
|
||||
List(jmpChunk(middle), labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, labelChunk(middle), conditionBlock, labelChunk(end)).flatten
|
||||
// List(labelChunk(start), conditionBlock, bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||
}
|
||||
case _ =>
|
||||
ErrorReporting.error(s"Illegal type for a condition: `$condType`", s.condition.position)
|
||||
Nil
|
||||
}
|
||||
}
|
||||
|
||||
def compileDoWhileStatement(ctx: CompilationContext, s: DoWhileStatement): List[T] = {
|
||||
val start = nextLabel("do")
|
||||
val inc = nextLabel("fp")
|
||||
val end = nextLabel("od")
|
||||
val condType = AbstractExpressionCompiler.getExpressionType(ctx, s.condition)
|
||||
val bodyBlock = compile(ctx.addLabels(s.labels, Label(end), Label(inc)), s.body)
|
||||
val incrementBlock = compile(ctx.addLabels(s.labels, Label(end), Label(inc)), s.increment)
|
||||
val largeBodyBlock = areBlocksLarge(bodyBlock, incrementBlock)
|
||||
condType match {
|
||||
case ConstantBooleanType(_, true) =>
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, NoBranching)
|
||||
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||
case ConstantBooleanType(_, false) =>
|
||||
List(bodyBlock, labelChunk(inc), incrementBlock, labelChunk(end)).flatten
|
||||
case FlagBooleanType(_, jumpIfTrue, jumpIfFalse) =>
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, NoBranching)
|
||||
if (largeBodyBlock) {
|
||||
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, branchChunk(jumpIfFalse, end), jmpChunk(start), labelChunk(end)).flatten
|
||||
} else {
|
||||
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, branchChunk(jumpIfTrue, start), labelChunk(end)).flatten
|
||||
}
|
||||
case BuiltInBooleanType =>
|
||||
if (largeBodyBlock) {
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, BranchIfFalse(end))
|
||||
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||
} else {
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, BranchIfTrue(start))
|
||||
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, labelChunk(end)).flatten
|
||||
}
|
||||
case _ =>
|
||||
ErrorReporting.error(s"Illegal type for a condition: `$condType`", s.condition.position)
|
||||
Nil
|
||||
}
|
||||
}
|
||||
|
||||
def compileForStatement(ctx: CompilationContext, f: ForStatement): List[T] = {
|
||||
// TODO: check sizes
|
||||
// TODO: special faster cases
|
||||
val vex = VariableExpression(f.variable)
|
||||
val one = LiteralExpression(1, 1)
|
||||
val increment = ExpressionStatement(FunctionCallExpression("+=", List(vex, one)))
|
||||
val decrement = ExpressionStatement(FunctionCallExpression("-=", List(vex, one)))
|
||||
val names = Set("", "for", f.variable)
|
||||
(f.direction, ctx.env.eval(f.start), ctx.env.eval(f.end)) match {
|
||||
|
||||
case (ForDirection.Until | ForDirection.ParallelUntil, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s == e - 1 =>
|
||||
val end = nextLabel("of")
|
||||
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, f.start) :: f.body) ++ labelChunk(end)
|
||||
case (ForDirection.Until | ForDirection.ParallelUntil, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s >= e =>
|
||||
Nil
|
||||
|
||||
case (ForDirection.To | ForDirection.ParallelTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s == e =>
|
||||
val end = nextLabel("of")
|
||||
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, f.start) :: f.body) ++ labelChunk(end)
|
||||
case (ForDirection.To | ForDirection.ParallelTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s > e =>
|
||||
Nil
|
||||
|
||||
case (ForDirection.ParallelUntil, Some(NumericConstant(0, ssize)), Some(NumericConstant(e, _))) if e > 0 =>
|
||||
compile(ctx, List(
|
||||
Assignment(vex, f.end),
|
||||
DoWhileStatement(Nil, decrement :: f.body, FunctionCallExpression("!=", List(vex, f.start)), names)
|
||||
))
|
||||
|
||||
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, esize))) if s == e =>
|
||||
val end = nextLabel("of")
|
||||
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, LiteralExpression(s, ssize)) :: f.body) ++ labelChunk(end)
|
||||
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, esize))) if s < e =>
|
||||
Nil
|
||||
case (ForDirection.DownTo, Some(NumericConstant(s, 1)), Some(NumericConstant(0, _))) if s > 0 =>
|
||||
compile(ctx, List(
|
||||
Assignment(vex, FunctionCallExpression("lo", List(SumExpression(List(false -> f.start, false -> LiteralExpression(1, 2)), decimal = false)))),
|
||||
DoWhileStatement(decrement :: f.body, Nil, FunctionCallExpression("!=", List(vex, f.end)), names)
|
||||
))
|
||||
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(0, _))) if s > 0 =>
|
||||
compile(ctx, List(
|
||||
Assignment(vex, SumExpression(List(false -> f.start, false -> LiteralExpression(1, 1)), decimal = false)),
|
||||
DoWhileStatement(decrement :: f.body, Nil, FunctionCallExpression("!=", List(vex, f.end)), names)
|
||||
))
|
||||
|
||||
|
||||
case (ForDirection.Until | ForDirection.ParallelUntil, _, _) =>
|
||||
compile(ctx, List(
|
||||
Assignment(vex, f.start),
|
||||
WhileStatement(
|
||||
FunctionCallExpression("<", List(vex, f.end)),
|
||||
f.body, List(increment), names),
|
||||
))
|
||||
// case (ForDirection.To | ForDirection.ParallelTo, _, Some(NumericConstant(n, _))) if n > 0 && n < 255 =>
|
||||
// compile(ctx, List(
|
||||
// Assignment(vex, f.start),
|
||||
// WhileStatement(
|
||||
// FunctionCallExpression("<=", List(vex, f.end)),
|
||||
// f.body :+ increment),
|
||||
// ))
|
||||
case (ForDirection.To | ForDirection.ParallelTo, _, _) =>
|
||||
compile(ctx, List(
|
||||
Assignment(vex, f.start),
|
||||
WhileStatement(
|
||||
VariableExpression("true"),
|
||||
f.body,
|
||||
List(IfStatement(
|
||||
FunctionCallExpression("==", List(vex, f.end)),
|
||||
List(BreakStatement(f.variable)),
|
||||
List(increment)
|
||||
)),
|
||||
names),
|
||||
))
|
||||
case (ForDirection.DownTo, _, _) =>
|
||||
compile(ctx, List(
|
||||
Assignment(vex, f.start),
|
||||
IfStatement(
|
||||
FunctionCallExpression(">=", List(vex, f.end)),
|
||||
List(DoWhileStatement(
|
||||
f.body,
|
||||
List(decrement),
|
||||
FunctionCallExpression("!=", List(vex, f.end)),
|
||||
names
|
||||
)),
|
||||
Nil)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
def compileBreakStatement(ctx: CompilationContext, s: BreakStatement) :List[T] = {
|
||||
ctx.breakLabels.get(s.label) match {
|
||||
case None =>
|
||||
if (s.label == "") ErrorReporting.error("`break` outside a loop", s.position)
|
||||
else ErrorReporting.error("Invalid label: " + s.label, s.position)
|
||||
Nil
|
||||
case Some(label) =>
|
||||
jmpChunk(label)
|
||||
}
|
||||
}
|
||||
|
||||
def compileContinueStatement(ctx: CompilationContext, s: ContinueStatement) :List[T] = {
|
||||
ctx.continueLabels.get(s.label) match {
|
||||
case None =>
|
||||
if (s.label == "") ErrorReporting.error("`continue` outside a loop", s.position)
|
||||
else ErrorReporting.error("Invalid label: " + s.label, s.position)
|
||||
Nil
|
||||
case Some(label) =>
|
||||
jmpChunk(label)
|
||||
}
|
||||
}
|
||||
|
||||
def compileIfStatement(ctx: CompilationContext, s: IfStatement): List[T] = {
|
||||
val condType = AbstractExpressionCompiler.getExpressionType(ctx, s.condition)
|
||||
val thenBlock = compile(ctx, s.thenBranch)
|
||||
val elseBlock = compile(ctx, s.elseBranch)
|
||||
val largeThenBlock = areBlocksLarge(thenBlock)
|
||||
val largeElseBlock = areBlocksLarge(elseBlock)
|
||||
condType match {
|
||||
case ConstantBooleanType(_, true) =>
|
||||
compileExpressionForBranching(ctx, s.condition, NoBranching) ++ thenBlock
|
||||
case ConstantBooleanType(_, false) =>
|
||||
compileExpressionForBranching(ctx, s.condition, NoBranching) ++ elseBlock
|
||||
case FlagBooleanType(_, jumpIfTrue, jumpIfFalse) =>
|
||||
(s.thenBranch, s.elseBranch) match {
|
||||
case (Nil, Nil) =>
|
||||
compileExpressionForBranching(ctx, s.condition, NoBranching)
|
||||
case (Nil, _) =>
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, NoBranching)
|
||||
if (largeElseBlock) {
|
||||
val middle = nextLabel("el")
|
||||
val end = nextLabel("fi")
|
||||
List(conditionBlock, branchChunk(jumpIfFalse, middle), jmpChunk(end), labelChunk(middle), elseBlock, labelChunk(end)).flatten
|
||||
} else {
|
||||
val end = nextLabel("fi")
|
||||
List(conditionBlock, branchChunk(jumpIfTrue, end), elseBlock, labelChunk(end)).flatten
|
||||
}
|
||||
case (_, Nil) =>
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, NoBranching)
|
||||
if (largeThenBlock) {
|
||||
val middle = nextLabel("th")
|
||||
val end = nextLabel("fi")
|
||||
List(conditionBlock, branchChunk(jumpIfTrue, middle), jmpChunk(end), labelChunk(middle), thenBlock, labelChunk(end)).flatten
|
||||
} else {
|
||||
val end = nextLabel("fi")
|
||||
List(conditionBlock, branchChunk(jumpIfFalse, end), thenBlock, labelChunk(end)).flatten
|
||||
}
|
||||
case _ =>
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, NoBranching)
|
||||
if (largeThenBlock) {
|
||||
if (largeElseBlock) {
|
||||
val middleT = nextLabel("th")
|
||||
val middleE = nextLabel("el")
|
||||
val end = nextLabel("fi")
|
||||
List(conditionBlock, branchChunk(jumpIfTrue, middleT), jmpChunk(middleE), labelChunk(middleT), thenBlock, jmpChunk(end), labelChunk(middleE), elseBlock, labelChunk(end)).flatten
|
||||
} else {
|
||||
val middle = nextLabel("th")
|
||||
val end = nextLabel("fi")
|
||||
List(conditionBlock, branchChunk(jumpIfTrue, middle), elseBlock, jmpChunk(end), labelChunk(middle), thenBlock, labelChunk(end)).flatten
|
||||
}
|
||||
} else {
|
||||
val middle = nextLabel("el")
|
||||
val end = nextLabel("fi")
|
||||
List(conditionBlock, branchChunk(jumpIfFalse, middle), thenBlock, jmpChunk(end), labelChunk(middle), elseBlock, labelChunk(end)).flatten
|
||||
}
|
||||
}
|
||||
case BuiltInBooleanType =>
|
||||
(s.thenBranch, s.elseBranch) match {
|
||||
case (Nil, Nil) =>
|
||||
compileExpressionForBranching(ctx, s.condition, NoBranching)
|
||||
case (Nil, _) =>
|
||||
if (largeElseBlock) {
|
||||
val middle = nextLabel("el")
|
||||
val end = nextLabel("fi")
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, BranchIfFalse(middle))
|
||||
List(conditionBlock, jmpChunk(end), labelChunk(middle), elseBlock, labelChunk(end)).flatten
|
||||
} else {
|
||||
val end = nextLabel("fi")
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, BranchIfTrue(end))
|
||||
List(conditionBlock, elseBlock, labelChunk(end)).flatten
|
||||
}
|
||||
case (_, Nil) =>
|
||||
if (largeThenBlock) {
|
||||
val middle = nextLabel("th")
|
||||
val end = nextLabel("fi")
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, BranchIfTrue(middle))
|
||||
List(conditionBlock, jmpChunk(end), labelChunk(middle), thenBlock, labelChunk(end)).flatten
|
||||
} else {
|
||||
val end = nextLabel("fi")
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, BranchIfFalse(end))
|
||||
List(conditionBlock, thenBlock, labelChunk(end)).flatten
|
||||
}
|
||||
case _ =>
|
||||
if (largeThenBlock) {
|
||||
if (largeElseBlock) {
|
||||
val middleT = nextLabel("th")
|
||||
val middleE = nextLabel("el")
|
||||
val end = nextLabel("fi")
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, BranchIfTrue(middleT))
|
||||
List(conditionBlock, jmpChunk(middleE), labelChunk(middleT), thenBlock, jmpChunk(end), labelChunk(middleE), elseBlock, labelChunk(end)).flatten
|
||||
} else {
|
||||
val middle = nextLabel("th")
|
||||
val end = nextLabel("fi")
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, BranchIfTrue(middle))
|
||||
List(conditionBlock, elseBlock, jmpChunk(end), labelChunk(middle), thenBlock, labelChunk(end)).flatten
|
||||
}
|
||||
} else {
|
||||
val middle = nextLabel("el")
|
||||
val end = nextLabel("fi")
|
||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, BranchIfFalse(middle))
|
||||
List(conditionBlock, thenBlock, jmpChunk(end), labelChunk(middle), elseBlock, labelChunk(end)).flatten
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
ErrorReporting.error(s"Illegal type for a condition: `$condType`", s.condition.position)
|
||||
Nil
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package millfork.compiler.mos
|
||||
package millfork.compiler
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
@ -1,4 +1,4 @@
|
||||
package millfork.compiler.mos
|
||||
package millfork.compiler
|
||||
|
||||
import millfork.env.{Environment, Label, NormalFunction}
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
@ -12,8 +12,8 @@ case class CompilationContext(env: Environment,
|
||||
options: CompilationOptions,
|
||||
breakLabels: Map[String, Label] = Map(),
|
||||
continueLabels: Map[String, Label] = Map()){
|
||||
def withInlinedEnv(environment: Environment): CompilationContext = {
|
||||
val newEnv = new Environment(Some(env), MosCompiler.nextLabel("en"))
|
||||
def withInlinedEnv(environment: Environment, newLabel: String): CompilationContext = {
|
||||
val newEnv = new Environment(Some(env), newLabel)
|
||||
newEnv.things ++= environment.things
|
||||
copy(env = newEnv)
|
||||
}
|
87
src/main/scala/millfork/compiler/MacroExpander.scala
Normal file
87
src/main/scala/millfork/compiler/MacroExpander.scala
Normal file
@ -0,0 +1,87 @@
|
||||
package millfork.compiler
|
||||
|
||||
import millfork.assembly.AbstractCode
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.{MosRegister, _}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
abstract class MacroExpander[T <: AbstractCode] {
|
||||
|
||||
def nextLabel(prefix: String): String
|
||||
|
||||
def prepareAssemblyParams(ctx: CompilationContext, assParams: List[AssemblyParam], params: List[Expression], code: List[ExecutableStatement]): (List[T], List[ExecutableStatement])
|
||||
|
||||
def replaceVariable(stmt: Statement, paramName: String, target: Expression): Statement = {
|
||||
def f[T <: Expression](e: T) = e.replaceVariable(paramName, target)
|
||||
|
||||
def fx[T <: Expression](e: T) = e.replaceVariable(paramName, target).asInstanceOf[LhsExpression]
|
||||
|
||||
def g[T <: Statement](s: T) = replaceVariable(s, paramName, target)
|
||||
|
||||
def gx[T <: ExecutableStatement](s: T) = replaceVariable(s, paramName, target).asInstanceOf[ExecutableStatement]
|
||||
|
||||
def h(s: String) = if (s == paramName) target.asInstanceOf[VariableExpression].name else s
|
||||
|
||||
stmt match {
|
||||
case RawBytesStatement(contents) => RawBytesStatement(contents.replaceVariable(paramName, target))
|
||||
case ExpressionStatement(e) => ExpressionStatement(e.replaceVariable(paramName, target))
|
||||
case ReturnStatement(e) => ReturnStatement(e.map(f))
|
||||
case ReturnDispatchStatement(i, ps, bs) => ReturnDispatchStatement(i.replaceVariable(paramName, target), ps.map(fx), bs.map {
|
||||
case ReturnDispatchBranch(l, fu, pps) => ReturnDispatchBranch(l, f(fu), pps.map(f))
|
||||
})
|
||||
case WhileStatement(c, b, i, n) => WhileStatement(f(c), b.map(gx), i.map(gx), n)
|
||||
case DoWhileStatement(b, i, c, n) => DoWhileStatement(b.map(gx), i.map(gx), f(c), n)
|
||||
case ForStatement(v, start, end, dir, body) => ForStatement(h(v), f(start), f(end), dir, body.map(gx))
|
||||
case IfStatement(c, t, e) => IfStatement(f(c), t.map(gx), e.map(gx))
|
||||
case s: MosAssemblyStatement => s.copy(expression = f(s.expression))
|
||||
case Assignment(d, s) => Assignment(fx(d), f(s))
|
||||
case BreakStatement(s) => if (s == paramName) BreakStatement(target.toString) else stmt
|
||||
case ContinueStatement(s) => if (s == paramName) ContinueStatement(target.toString) else stmt
|
||||
case _ =>
|
||||
println(stmt)
|
||||
???
|
||||
}
|
||||
}
|
||||
|
||||
def inlineFunction(ctx: CompilationContext, i: MacroFunction, params: List[Expression], position: Option[Position]): (List[T], List[ExecutableStatement]) = {
|
||||
var paramPreparation = List[T]()
|
||||
var actualCode = i.code
|
||||
i.params match {
|
||||
case AssemblyParamSignature(assParams) =>
|
||||
val pair = prepareAssemblyParams(ctx, assParams, params, i.code)
|
||||
paramPreparation = pair._1
|
||||
actualCode = pair._2
|
||||
case NormalParamSignature(normalParams) =>
|
||||
if (params.length != normalParams.length) {
|
||||
ErrorReporting.error(s"Invalid number of params for macro function ${i.name}", position)
|
||||
} else {
|
||||
params.zip(normalParams).foreach {
|
||||
case (v@VariableExpression(_), MemoryVariable(paramName, paramType, _)) =>
|
||||
actualCode = actualCode.map(stmt => replaceVariable(stmt, paramName.stripPrefix(i.environment.prefix), v).asInstanceOf[ExecutableStatement])
|
||||
case (v@IndexedExpression(_, _), MemoryVariable(paramName, paramType, _)) =>
|
||||
actualCode = actualCode.map(stmt => replaceVariable(stmt, paramName.stripPrefix(i.environment.prefix), v).asInstanceOf[ExecutableStatement])
|
||||
case _ =>
|
||||
ErrorReporting.error(s"Parameters to macro functions have to be variables", position)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// fix local labels:
|
||||
// TODO: do it even if the labels are in an inline assembly block inside a Millfork function
|
||||
val localLabels = actualCode.flatMap {
|
||||
case MosAssemblyStatement(LABEL, _, VariableExpression(l), _) => Some(l)
|
||||
case _ => None
|
||||
}.toSet
|
||||
val labelPrefix = nextLabel("il")
|
||||
paramPreparation -> actualCode.map {
|
||||
case s@MosAssemblyStatement(_, _, VariableExpression(v), _) if localLabels(v) =>
|
||||
s.copy(expression = VariableExpression(labelPrefix + v))
|
||||
case s => s
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
package millfork.compiler.mos
|
||||
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.assembly._
|
||||
import millfork.compiler._
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node._
|
||||
|
@ -1,10 +1,11 @@
|
||||
package millfork.compiler.mos
|
||||
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.assembly._
|
||||
import millfork.compiler.{BranchSpec, CompilationContext}
|
||||
import millfork.env.{NumericConstant, RegisterVariable, Type, _}
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.{Expression, MosRegister, _}
|
||||
|
@ -1,108 +0,0 @@
|
||||
package millfork.compiler.mos
|
||||
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.{MosRegister, _}
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object MacroExpander {
|
||||
|
||||
|
||||
def replaceVariable(stmt: Statement, paramName: String, target: Expression): Statement = {
|
||||
def f[T <: Expression](e:T) = e.replaceVariable(paramName, target)
|
||||
def fx[T <: Expression](e:T) = e.replaceVariable(paramName, target).asInstanceOf[LhsExpression]
|
||||
def g[T <: Statement](s:T) = replaceVariable(s, paramName, target)
|
||||
def gx[T <: ExecutableStatement](s:T) = replaceVariable(s, paramName, target).asInstanceOf[ExecutableStatement]
|
||||
def h(s:String) = if (s == paramName) target.asInstanceOf[VariableExpression].name else s
|
||||
stmt match {
|
||||
case RawBytesStatement(contents) => RawBytesStatement(contents.replaceVariable(paramName, target))
|
||||
case ExpressionStatement(e) => ExpressionStatement(e.replaceVariable(paramName, target))
|
||||
case ReturnStatement(e) => ReturnStatement(e.map(f))
|
||||
case ReturnDispatchStatement(i,ps, bs) => ReturnDispatchStatement(i.replaceVariable(paramName, target), ps.map(fx), bs.map{
|
||||
case ReturnDispatchBranch(l, fu, pps) => ReturnDispatchBranch(l, f(fu), pps.map(f))
|
||||
})
|
||||
case WhileStatement(c, b, i, n) => WhileStatement(f(c), b.map(gx), i.map(gx), n)
|
||||
case DoWhileStatement(b, i, c, n) => DoWhileStatement(b.map(gx), i.map(gx), f(c), n)
|
||||
case ForStatement(v, start, end, dir, body) => ForStatement(h(v), f(start), f(end), dir, body.map(gx))
|
||||
case IfStatement(c, t, e) => IfStatement(f(c), t.map(gx), e.map(gx))
|
||||
case s:MosAssemblyStatement => s.copy(expression = f(s.expression))
|
||||
case Assignment(d,s) => Assignment(fx(d), f(s))
|
||||
case BreakStatement(s) => if (s == paramName) BreakStatement(target.toString) else stmt
|
||||
case ContinueStatement(s) => if (s == paramName) ContinueStatement(target.toString) else stmt
|
||||
case _ =>
|
||||
println(stmt)
|
||||
???
|
||||
}
|
||||
}
|
||||
|
||||
def inlineFunction(ctx: CompilationContext, i: MacroFunction, params: List[Expression], position: Option[Position]): (List[AssemblyLine], List[ExecutableStatement]) = {
|
||||
var paramPreparation = List[AssemblyLine]()
|
||||
var actualCode = i.code
|
||||
i.params match {
|
||||
case AssemblyParamSignature(assParams) =>
|
||||
var hadRegisterParam = false
|
||||
assParams.zip(params).foreach {
|
||||
case (AssemblyParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByReference), actualParam) =>
|
||||
actualParam match {
|
||||
case VariableExpression(vname) =>
|
||||
ctx.env.get[ThingInMemory](vname)
|
||||
case l: LhsExpression =>
|
||||
// TODO: ??
|
||||
MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, l)
|
||||
case _ =>
|
||||
ErrorReporting.error("A non-assignable expression was passed to an inlineable function as a `ref` parameter", actualParam.position)
|
||||
}
|
||||
actualCode = actualCode.map {
|
||||
case a@MosAssemblyStatement(_, _, expr, _) =>
|
||||
a.copy(expression = expr.replaceVariable(ph, actualParam))
|
||||
case x => x
|
||||
}
|
||||
case (AssemblyParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByConstant), actualParam) =>
|
||||
ctx.env.eval(actualParam).getOrElse(Constant.error("Non-constant expression was passed to an inlineable function as a `const` parameter", actualParam.position))
|
||||
actualCode = actualCode.map {
|
||||
case a@MosAssemblyStatement(_, _, expr, _) =>
|
||||
a.copy(expression = expr.replaceVariable(ph, actualParam))
|
||||
case x => x
|
||||
}
|
||||
case (AssemblyParam(typ, v@RegisterVariable(register, _), AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
if (hadRegisterParam) {
|
||||
ErrorReporting.error("Only one macro assembly function parameter can be passed via a register", position)
|
||||
}
|
||||
hadRegisterParam = true
|
||||
paramPreparation = MosExpressionCompiler.compile(ctx, actualParam, Some(typ, v), BranchSpec.None)
|
||||
case (AssemblyParam(_, _, AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
???
|
||||
case (_, actualParam) =>
|
||||
}
|
||||
case NormalParamSignature(normalParams) =>
|
||||
if (params.length != normalParams.length) {
|
||||
ErrorReporting.error(s"Invalid number of params for macro function ${i.name}", position)
|
||||
} else {
|
||||
params.zip(normalParams).foreach{
|
||||
case (v@VariableExpression(_), MemoryVariable(paramName, paramType, _)) =>
|
||||
actualCode = actualCode.map(stmt => replaceVariable(stmt, paramName.stripPrefix(i.environment.prefix), v).asInstanceOf[ExecutableStatement])
|
||||
case (v@IndexedExpression(_, _), MemoryVariable(paramName, paramType, _)) =>
|
||||
actualCode = actualCode.map(stmt => replaceVariable(stmt, paramName.stripPrefix(i.environment.prefix), v).asInstanceOf[ExecutableStatement])
|
||||
case _ =>
|
||||
ErrorReporting.error(s"Parameters to macro functions have to be variables", position)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// fix local labels:
|
||||
// TODO: do it even if the labels are in an inline assembly block inside a Millfork function
|
||||
val localLabels = actualCode.flatMap{
|
||||
case MosAssemblyStatement(LABEL, _, VariableExpression(l), _) => Some(l)
|
||||
case _ => None
|
||||
}.toSet
|
||||
val labelPrefix = MosCompiler.nextLabel("il")
|
||||
paramPreparation -> actualCode.map{
|
||||
case s@MosAssemblyStatement(_, _, VariableExpression(v), _) if localLabels(v) =>
|
||||
s.copy(expression = VariableExpression(labelPrefix + v))
|
||||
case s => s
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import java.util.concurrent.atomic.AtomicLong
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.compiler.AbstractCompiler
|
||||
import millfork.compiler.{AbstractCompiler, CompilationContext}
|
||||
import millfork.env._
|
||||
|
||||
/**
|
||||
|
@ -1,10 +1,10 @@
|
||||
package millfork.compiler.mos
|
||||
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.compiler.AbstractExpressionCompiler
|
||||
import millfork.compiler._
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.{MosRegister, _}
|
||||
@ -294,74 +294,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def assertCompatible(exprType: Type, variableType: Type): Unit = {
|
||||
// TODO
|
||||
}
|
||||
|
||||
val noop: List[AssemblyLine] = Nil
|
||||
|
||||
def callingContext(ctx: CompilationContext, v: MemoryVariable): CompilationContext = {
|
||||
val result = new Environment(Some(ctx.env), "")
|
||||
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 assertBinary(ctx: CompilationContext, params: List[Expression]): (Expression, Expression, Int) = {
|
||||
if (params.length != 2) {
|
||||
ErrorReporting.fatal("sfgdgfsd", None)
|
||||
}
|
||||
(params.head, params(1)) match {
|
||||
case (l: Expression, r: Expression) => (l, r, getExpressionType(ctx, l).size max getExpressionType(ctx, r).size)
|
||||
}
|
||||
}
|
||||
|
||||
def assertComparison(ctx: CompilationContext, params: List[Expression]): (Int, Boolean) = {
|
||||
(params.head, params(1)) match {
|
||||
case (l: Expression, r: Expression) =>
|
||||
val lt = getExpressionType(ctx, l)
|
||||
val rt = getExpressionType(ctx, r)
|
||||
(lt.size max rt.size, lt.isSigned || rt.isSigned)
|
||||
}
|
||||
}
|
||||
|
||||
def assertBool(ctx: CompilationContext, fname: String, params: List[Expression], expectedParamCount: Int): Unit = {
|
||||
if (params.length != expectedParamCount) {
|
||||
ErrorReporting.error("Invalid number of parameters for " + fname, params.headOption.flatMap(_.position))
|
||||
return
|
||||
}
|
||||
params.foreach { param =>
|
||||
if (!getExpressionType(ctx, param).isInstanceOf[BooleanType])
|
||||
ErrorReporting.fatal("Parameter should be boolean", param.position)
|
||||
}
|
||||
}
|
||||
|
||||
def assertBool(ctx: CompilationContext, fname: String, params: List[Expression]): Unit = {
|
||||
if (params.length < 2) {
|
||||
ErrorReporting.error("Invalid number of parameters for " + fname, params.headOption.flatMap(_.position))
|
||||
return
|
||||
}
|
||||
params.foreach { param =>
|
||||
if (!getExpressionType(ctx, param).isInstanceOf[BooleanType])
|
||||
ErrorReporting.fatal("Parameter should be boolean", param.position)
|
||||
}
|
||||
}
|
||||
|
||||
def assertAssignmentLike(ctx: CompilationContext, params: List[Expression]): (LhsExpression, Expression, Int) = {
|
||||
if (params.length != 2) {
|
||||
ErrorReporting.fatal("sfgdgfsd", None)
|
||||
}
|
||||
(params.head, params(1)) match {
|
||||
case (l: LhsExpression, r: Expression) =>
|
||||
val lsize = getExpressionType(ctx, l).size
|
||||
val rsize = getExpressionType(ctx, r).size
|
||||
if (lsize < rsize) {
|
||||
ErrorReporting.error("Left-hand-side expression is of smaller type than the right-hand-side expression", l.position)
|
||||
}
|
||||
(l, r, lsize)
|
||||
case (err: Expression, _) => ErrorReporting.fatal("Invalid left-hand-side expression", err.position)
|
||||
}
|
||||
}
|
||||
|
||||
def compile(ctx: CompilationContext, expr: Expression, exprTypeAndVariable: Option[(Type, Variable)], branches: BranchSpec): List[AssemblyLine] = {
|
||||
val env = ctx.env
|
||||
@ -1098,7 +1032,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
}
|
||||
lookupFunction(ctx, f) match {
|
||||
case function: MacroFunction =>
|
||||
val (paramPreparation, statements) = MacroExpander.inlineFunction(ctx, function, params, expr.position)
|
||||
val (paramPreparation, statements) = MosMacroExpander.inlineFunction(ctx, function, params, expr.position)
|
||||
paramPreparation ++ statements.map {
|
||||
case MosAssemblyStatement(opcode, addrMode, expression, elidable) =>
|
||||
val param = env.evalForAsm(expression).getOrElse {
|
||||
@ -1303,20 +1237,6 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
}
|
||||
}
|
||||
|
||||
private def getParamMaxSize(ctx: CompilationContext, params: List[Expression]): Int = {
|
||||
params.map { case expr => getExpressionType(ctx, expr).size}.max
|
||||
}
|
||||
|
||||
private def getSumSize(ctx: CompilationContext, params: List[(Boolean, Expression)]): Int = {
|
||||
params.map { case (_, expr) => getExpressionType(ctx, expr).size}.max
|
||||
}
|
||||
|
||||
private def assertAllBytes(msg: String, ctx: CompilationContext, params: List[Expression]): Unit = {
|
||||
if (params.exists { expr => getExpressionType(ctx, expr).size != 1 }) {
|
||||
ErrorReporting.fatal(msg, params.head.position)
|
||||
}
|
||||
}
|
||||
|
||||
def compileAssignment(ctx: CompilationContext, source: Expression, target: LhsExpression): List[AssemblyLine] = {
|
||||
val env = ctx.env
|
||||
val b = env.get[Type]("byte")
|
||||
|
54
src/main/scala/millfork/compiler/mos/MosMacroExpander.scala
Normal file
54
src/main/scala/millfork/compiler/mos/MosMacroExpander.scala
Normal file
@ -0,0 +1,54 @@
|
||||
package millfork.compiler.mos
|
||||
|
||||
import millfork.assembly.mos.AssemblyLine
|
||||
import millfork.compiler.{BranchSpec, CompilationContext, MacroExpander}
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node._
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object MosMacroExpander extends MacroExpander[AssemblyLine] {
|
||||
override def nextLabel(prefix: String): String = MosCompiler.nextLabel(prefix)
|
||||
|
||||
override def prepareAssemblyParams(ctx: CompilationContext, assParams: List[AssemblyParam], params: List[Expression], code: List[ExecutableStatement]): (List[AssemblyLine], List[ExecutableStatement]) = {
|
||||
var paramPreparation = List[AssemblyLine]()
|
||||
var actualCode = code
|
||||
var hadRegisterParam = false
|
||||
assParams.zip(params).foreach {
|
||||
case (AssemblyParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByReference), actualParam) =>
|
||||
actualParam match {
|
||||
case VariableExpression(vname) =>
|
||||
ctx.env.get[ThingInMemory](vname)
|
||||
case l: LhsExpression =>
|
||||
// TODO: ??
|
||||
MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, l)
|
||||
case _ =>
|
||||
ErrorReporting.error("A non-assignable expression was passed to an inlineable function as a `ref` parameter", actualParam.position)
|
||||
}
|
||||
actualCode = actualCode.map {
|
||||
case a@MosAssemblyStatement(_, _, expr, _) =>
|
||||
a.copy(expression = expr.replaceVariable(ph, actualParam))
|
||||
case x => x
|
||||
}
|
||||
case (AssemblyParam(typ, Placeholder(ph, phType), AssemblyParameterPassingBehaviour.ByConstant), actualParam) =>
|
||||
ctx.env.eval(actualParam).getOrElse(Constant.error("Non-constant expression was passed to an inlineable function as a `const` parameter", actualParam.position))
|
||||
actualCode = actualCode.map {
|
||||
case a@MosAssemblyStatement(_, _, expr, _) =>
|
||||
a.copy(expression = expr.replaceVariable(ph, actualParam))
|
||||
case x => x
|
||||
}
|
||||
case (AssemblyParam(typ, v@RegisterVariable(register, _), AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
if (hadRegisterParam) {
|
||||
ErrorReporting.error("Only one macro assembly function parameter can be passed via a register", actualParam.position)
|
||||
}
|
||||
hadRegisterParam = true
|
||||
paramPreparation = MosExpressionCompiler.compile(ctx, actualParam, Some(typ, v), BranchSpec.None)
|
||||
case (AssemblyParam(_, _, AssemblyParameterPassingBehaviour.Copy), actualParam) =>
|
||||
???
|
||||
case (_, actualParam) =>
|
||||
}
|
||||
paramPreparation -> actualCode
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package millfork.compiler.mos
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.compiler.{BranchSpec, CompilationContext}
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node._
|
||||
@ -12,7 +13,7 @@ import scala.collection.mutable
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object ReturnDispatch {
|
||||
object MosReturnDispatch {
|
||||
|
||||
def compile(ctx: CompilationContext, stmt: ReturnDispatchStatement): List[AssemblyLine] = {
|
||||
if (stmt.branches.isEmpty) {
|
||||
@ -134,7 +135,7 @@ object ReturnDispatch {
|
||||
val useJmpaix = ctx.options.flag(CompilationFlag.EmitCmosOpcodes) && !ctx.options.flag(CompilationFlag.LUnixRelocatableCode) && (actualMax - actualMin) <= 127
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
|
||||
import millfork.assembly.AddrMode._
|
||||
import AddrMode._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
|
||||
val ctxForStoringParams = ctx.neverCheckArrayBounds
|
@ -1,9 +1,11 @@
|
||||
package millfork.compiler.mos
|
||||
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.BranchingOpcodeMapping
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.compiler._
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.{MosRegister, _}
|
||||
@ -11,14 +13,20 @@ import millfork.node.{MosRegister, _}
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object MosStatementCompiler {
|
||||
object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
||||
|
||||
private def labelChunk(labelName: String) = List(AssemblyLine.label(Label(labelName)))
|
||||
def labelChunk(labelName: String) = List(AssemblyLine.label(Label(labelName)))
|
||||
|
||||
private def jmpChunk(labelName: String) = List(AssemblyLine.absolute(JMP, Label(labelName)))
|
||||
def jmpChunk(label: Label) = List(AssemblyLine.absolute(JMP, label))
|
||||
|
||||
private def branchChunk(opcode: Opcode.Value, labelName: String) = List(AssemblyLine.relative(opcode, Label(labelName)))
|
||||
def branchChunk(opcode: BranchingOpcodeMapping, labelName: String) = List(AssemblyLine.relative(opcode.mosOpcode, Label(labelName)))
|
||||
|
||||
def areBlocksLarge(blocks: List[AssemblyLine]*): Boolean = blocks.map(_.map(_.sizeInBytes).sum).sum > 100
|
||||
|
||||
def compileExpressionForBranching(ctx: CompilationContext, expr: Expression, branching: BranchSpec): List[AssemblyLine] = {
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
MosExpressionCompiler.compile(ctx, expr, Some(b, RegisterVariable(MosRegister.A, b)), branching)
|
||||
}
|
||||
|
||||
def compile(ctx: CompilationContext, statements: List[ExecutableStatement]): List[AssemblyLine] = {
|
||||
statements.flatMap(s => compile(ctx, s))
|
||||
@ -145,8 +153,8 @@ object MosStatementCompiler {
|
||||
case ExpressionStatement(e@FunctionCallExpression(name, params)) =>
|
||||
env.lookupFunction(name, params.map(p => MosExpressionCompiler.getExpressionType(ctx, p) -> p)) match {
|
||||
case Some(i: MacroFunction) =>
|
||||
val (paramPreparation, inlinedStatements) = MacroExpander.inlineFunction(ctx, i, params, e.position)
|
||||
paramPreparation ++ compile(ctx.withInlinedEnv(i.environment), inlinedStatements)
|
||||
val (paramPreparation, inlinedStatements) = MosMacroExpander.inlineFunction(ctx, i, params, e.position)
|
||||
paramPreparation ++ compile(ctx.withInlinedEnv(i.environment, MosCompiler.nextLabel("en")), inlinedStatements)
|
||||
case _ =>
|
||||
MosExpressionCompiler.compile(ctx, e, None, NoBranching)
|
||||
}
|
||||
@ -168,15 +176,17 @@ object MosStatementCompiler {
|
||||
stackPointerFixBeforeReturn(ctx) ++
|
||||
List(AssemblyLine.discardAF(), AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
|
||||
case 1 =>
|
||||
ErrorReporting.warn("Returning without a value", ctx.options, statement.position)
|
||||
stackPointerFixBeforeReturn(ctx) ++
|
||||
List(AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
|
||||
case 2 =>
|
||||
ErrorReporting.warn("Returning without a value", ctx.options, statement.position)
|
||||
stackPointerFixBeforeReturn(ctx) ++
|
||||
List(AssemblyLine.discardYF()) ++ returnInstructions
|
||||
}
|
||||
}
|
||||
case s : ReturnDispatchStatement =>
|
||||
ReturnDispatch.compile(ctx, s)
|
||||
MosReturnDispatch.compile(ctx, s)
|
||||
case ReturnStatement(Some(e)) =>
|
||||
m.returnType match {
|
||||
case _: BooleanType =>
|
||||
@ -209,286 +219,18 @@ object MosStatementCompiler {
|
||||
}
|
||||
}
|
||||
}
|
||||
case IfStatement(condition, thenPart, elsePart) =>
|
||||
val condType = MosExpressionCompiler.getExpressionType(ctx, condition)
|
||||
val thenBlock = compile(ctx, thenPart)
|
||||
val elseBlock = compile(ctx, elsePart)
|
||||
val largeThenBlock = thenBlock.map(_.sizeInBytes).sum > 100
|
||||
val largeElseBlock = elseBlock.map(_.sizeInBytes).sum > 100
|
||||
condType match {
|
||||
case ConstantBooleanType(_, true) =>
|
||||
MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching) ++ thenBlock
|
||||
case ConstantBooleanType(_, false) =>
|
||||
MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching) ++ elseBlock
|
||||
case FlagBooleanType(_, jumpIfTrue, jumpIfFalse) =>
|
||||
(thenPart, elsePart) match {
|
||||
case (Nil, Nil) =>
|
||||
MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
||||
case (Nil, _) =>
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
||||
if (largeElseBlock) {
|
||||
val middle = MosCompiler.nextLabel("el")
|
||||
val end = MosCompiler.nextLabel("fi")
|
||||
List(conditionBlock, branchChunk(jumpIfFalse, middle), jmpChunk(end), labelChunk(middle), elseBlock, labelChunk(end)).flatten
|
||||
} else {
|
||||
val end = MosCompiler.nextLabel("fi")
|
||||
List(conditionBlock, branchChunk(jumpIfTrue, end), elseBlock, labelChunk(end)).flatten
|
||||
}
|
||||
case (_, Nil) =>
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
||||
if (largeThenBlock) {
|
||||
val middle = MosCompiler.nextLabel("th")
|
||||
val end = MosCompiler.nextLabel("fi")
|
||||
List(conditionBlock, branchChunk(jumpIfTrue, middle), jmpChunk(end), labelChunk(middle), thenBlock, labelChunk(end)).flatten
|
||||
} else {
|
||||
val end = MosCompiler.nextLabel("fi")
|
||||
List(conditionBlock, branchChunk(jumpIfFalse, end), thenBlock, labelChunk(end)).flatten
|
||||
}
|
||||
case _ =>
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
||||
if (largeThenBlock) {
|
||||
if (largeElseBlock) {
|
||||
val middleT = MosCompiler.nextLabel("th")
|
||||
val middleE = MosCompiler.nextLabel("el")
|
||||
val end = MosCompiler.nextLabel("fi")
|
||||
List(conditionBlock, branchChunk(jumpIfTrue, middleT), jmpChunk(middleE), labelChunk(middleT), thenBlock, jmpChunk(end), labelChunk(middleE), elseBlock, labelChunk(end)).flatten
|
||||
} else {
|
||||
val middle = MosCompiler.nextLabel("th")
|
||||
val end = MosCompiler.nextLabel("fi")
|
||||
List(conditionBlock, branchChunk(jumpIfTrue, middle), elseBlock, jmpChunk(end), labelChunk(middle), thenBlock, labelChunk(end)).flatten
|
||||
}
|
||||
} else {
|
||||
val middle = MosCompiler.nextLabel("el")
|
||||
val end = MosCompiler.nextLabel("fi")
|
||||
List(conditionBlock, branchChunk(jumpIfFalse, middle), thenBlock, jmpChunk(end), labelChunk(middle), elseBlock, labelChunk(end)).flatten
|
||||
}
|
||||
}
|
||||
case BuiltInBooleanType =>
|
||||
(thenPart, elsePart) match {
|
||||
case (Nil, Nil) =>
|
||||
MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
||||
case (Nil, _) =>
|
||||
if (largeElseBlock) {
|
||||
val middle = MosCompiler.nextLabel("el")
|
||||
val end = MosCompiler.nextLabel("fi")
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(middle))
|
||||
List(conditionBlock, jmpChunk(end), labelChunk(middle), elseBlock, labelChunk(end)).flatten
|
||||
} else {
|
||||
val end = MosCompiler.nextLabel("fi")
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(end))
|
||||
List(conditionBlock, elseBlock, labelChunk(end)).flatten
|
||||
}
|
||||
case (_, Nil) =>
|
||||
if (largeThenBlock) {
|
||||
val middle = MosCompiler.nextLabel("th")
|
||||
val end = MosCompiler.nextLabel("fi")
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middle))
|
||||
List(conditionBlock, jmpChunk(end), labelChunk(middle), thenBlock, labelChunk(end)).flatten
|
||||
} else {
|
||||
val end = MosCompiler.nextLabel("fi")
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(end))
|
||||
List(conditionBlock, thenBlock, labelChunk(end)).flatten
|
||||
}
|
||||
case _ =>
|
||||
if (largeThenBlock) {
|
||||
if (largeElseBlock) {
|
||||
val middleT = MosCompiler.nextLabel("th")
|
||||
val middleE = MosCompiler.nextLabel("el")
|
||||
val end = MosCompiler.nextLabel("fi")
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middleT))
|
||||
List(conditionBlock, jmpChunk(middleE), labelChunk(middleT), thenBlock, jmpChunk(end), labelChunk(middleE), elseBlock, labelChunk(end)).flatten
|
||||
} else {
|
||||
val middle = MosCompiler.nextLabel("th")
|
||||
val end = MosCompiler.nextLabel("fi")
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middle))
|
||||
List(conditionBlock, elseBlock, jmpChunk(end), labelChunk(middle), thenBlock, labelChunk(end)).flatten
|
||||
}
|
||||
} else {
|
||||
val middle = MosCompiler.nextLabel("el")
|
||||
val end = MosCompiler.nextLabel("fi")
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(middle))
|
||||
List(conditionBlock, thenBlock, jmpChunk(end), labelChunk(middle), elseBlock, labelChunk(end)).flatten
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
ErrorReporting.error(s"Illegal type for a condition: `$condType`", condition.position)
|
||||
Nil
|
||||
}
|
||||
case WhileStatement(condition, bodyPart, incrementPart, labels) =>
|
||||
val start = MosCompiler.nextLabel("wh")
|
||||
val middle = MosCompiler.nextLabel("he")
|
||||
val inc = MosCompiler.nextLabel("fp")
|
||||
val end = MosCompiler.nextLabel("ew")
|
||||
val condType = MosExpressionCompiler.getExpressionType(ctx, condition)
|
||||
val bodyBlock = compile(ctx.addLabels(labels, Label(end), Label(inc)), bodyPart)
|
||||
val incrementBlock = compile(ctx.addLabels(labels, Label(end), Label(inc)), incrementPart)
|
||||
val largeBodyBlock = bodyBlock.map(_.sizeInBytes).sum + incrementBlock.map(_.sizeInBytes).sum > 100
|
||||
condType match {
|
||||
case ConstantBooleanType(_, true) =>
|
||||
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||
case ConstantBooleanType(_, false) => Nil
|
||||
case FlagBooleanType(_, jumpIfTrue, jumpIfFalse) =>
|
||||
if (largeBodyBlock) {
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
||||
List(labelChunk(start), conditionBlock, branchChunk(jumpIfTrue, middle), jmpChunk(end), labelChunk(middle), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||
} else {
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
||||
List(jmpChunk(middle), labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, labelChunk(middle), conditionBlock, branchChunk(jumpIfTrue, start), labelChunk(end)).flatten
|
||||
// List(labelChunk(start), conditionBlock, branchChunk(jumpIfFalse, end), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||
}
|
||||
case BuiltInBooleanType =>
|
||||
if (largeBodyBlock) {
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(middle))
|
||||
List(labelChunk(start), conditionBlock, jmpChunk(end), labelChunk(middle), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||
} else {
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(start))
|
||||
List(jmpChunk(middle), labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, labelChunk(middle), conditionBlock, labelChunk(end)).flatten
|
||||
// List(labelChunk(start), conditionBlock, bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||
}
|
||||
case _ =>
|
||||
ErrorReporting.error(s"Illegal type for a condition: `$condType`", condition.position)
|
||||
Nil
|
||||
}
|
||||
case DoWhileStatement(bodyPart, incrementPart, condition, labels) =>
|
||||
val start = MosCompiler.nextLabel("do")
|
||||
val inc = MosCompiler.nextLabel("fp")
|
||||
val end = MosCompiler.nextLabel("od")
|
||||
val condType = MosExpressionCompiler.getExpressionType(ctx, condition)
|
||||
val bodyBlock = compile(ctx.addLabels(labels, Label(end), Label(inc)), bodyPart)
|
||||
val incrementBlock = compile(ctx.addLabels(labels, Label(end), Label(inc)), incrementPart)
|
||||
val largeBodyBlock = bodyBlock.map(_.sizeInBytes).sum + incrementBlock.map(_.sizeInBytes).sum > 100
|
||||
condType match {
|
||||
case ConstantBooleanType(_, true) =>
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
||||
List(labelChunk(start),bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||
case ConstantBooleanType(_, false) =>
|
||||
List(bodyBlock, labelChunk(inc), incrementBlock, labelChunk(end)).flatten
|
||||
case FlagBooleanType(_, jumpIfTrue, jumpIfFalse) =>
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, NoBranching)
|
||||
if (largeBodyBlock) {
|
||||
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, branchChunk(jumpIfFalse, end), jmpChunk(start), labelChunk(end)).flatten
|
||||
} else {
|
||||
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, branchChunk(jumpIfTrue, start), labelChunk(end)).flatten
|
||||
}
|
||||
case BuiltInBooleanType =>
|
||||
if (largeBodyBlock) {
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfFalse(end))
|
||||
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||
} else {
|
||||
val conditionBlock = MosExpressionCompiler.compile(ctx, condition, someRegisterA, BranchIfTrue(start))
|
||||
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, conditionBlock, labelChunk(end)).flatten
|
||||
}
|
||||
case _ =>
|
||||
ErrorReporting.error(s"Illegal type for a condition: `$condType`", condition.position)
|
||||
Nil
|
||||
}
|
||||
case f@ForStatement(variable, start, end, direction, body) =>
|
||||
// TODO: check sizes
|
||||
// TODO: special faster cases
|
||||
val vex = VariableExpression(f.variable)
|
||||
val one = LiteralExpression(1, 1)
|
||||
val increment = ExpressionStatement(FunctionCallExpression("+=", List(vex, one)))
|
||||
val decrement = ExpressionStatement(FunctionCallExpression("-=", List(vex, one)))
|
||||
val names = Set("", "for", variable)
|
||||
(direction, env.eval(start), env.eval(end)) match {
|
||||
|
||||
case (ForDirection.Until | ForDirection.ParallelUntil, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s == e - 1 =>
|
||||
val end = MosCompiler.nextLabel("of")
|
||||
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, f.start) :: f.body) ++ labelChunk(end)
|
||||
case (ForDirection.Until | ForDirection.ParallelUntil, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s >= e =>
|
||||
Nil
|
||||
|
||||
case (ForDirection.To | ForDirection.ParallelTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s == e =>
|
||||
val end = MosCompiler.nextLabel("of")
|
||||
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, f.start) :: f.body) ++ labelChunk(end)
|
||||
case (ForDirection.To | ForDirection.ParallelTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s > e =>
|
||||
Nil
|
||||
|
||||
case (ForDirection.ParallelUntil, Some(NumericConstant(0, ssize)), Some(NumericConstant(e, _))) if e > 0 =>
|
||||
compile(ctx, List(
|
||||
Assignment(vex, f.end),
|
||||
DoWhileStatement(Nil, decrement :: f.body, FunctionCallExpression("!=", List(vex, f.start)), names)
|
||||
))
|
||||
|
||||
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, esize))) if s == e =>
|
||||
val end = MosCompiler.nextLabel("of")
|
||||
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, LiteralExpression(s, ssize)) :: f.body) ++ labelChunk(end)
|
||||
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, esize))) if s < e =>
|
||||
Nil
|
||||
case (ForDirection.DownTo, Some(NumericConstant(s, 1)), Some(NumericConstant(0, _))) if s > 0 =>
|
||||
compile(ctx, List(
|
||||
Assignment(vex, FunctionCallExpression("lo", List(SumExpression(List(false -> f.start, false -> LiteralExpression(1, 2)), decimal = false)))),
|
||||
DoWhileStatement(decrement :: f.body, Nil, FunctionCallExpression("!=", List(vex, f.end)), names)
|
||||
))
|
||||
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(0, _))) if s > 0 =>
|
||||
compile(ctx, List(
|
||||
Assignment(vex, SumExpression(List(false -> f.start, false -> LiteralExpression(1, 1)), decimal = false)),
|
||||
DoWhileStatement(decrement :: f.body, Nil, FunctionCallExpression("!=", List(vex, f.end)), names)
|
||||
))
|
||||
|
||||
|
||||
case (ForDirection.Until | ForDirection.ParallelUntil, _, _) =>
|
||||
compile(ctx, List(
|
||||
Assignment(vex, f.start),
|
||||
WhileStatement(
|
||||
FunctionCallExpression("<", List(vex, f.end)),
|
||||
f.body, List(increment), names),
|
||||
))
|
||||
// case (ForDirection.To | ForDirection.ParallelTo, _, Some(NumericConstant(n, _))) if n > 0 && n < 255 =>
|
||||
// compile(ctx, List(
|
||||
// Assignment(vex, f.start),
|
||||
// WhileStatement(
|
||||
// FunctionCallExpression("<=", List(vex, f.end)),
|
||||
// f.body :+ increment),
|
||||
// ))
|
||||
case (ForDirection.To | ForDirection.ParallelTo, _, _) =>
|
||||
compile(ctx, List(
|
||||
Assignment(vex, f.start),
|
||||
WhileStatement(
|
||||
VariableExpression("true"),
|
||||
f.body,
|
||||
List(IfStatement(
|
||||
FunctionCallExpression("==", List(vex, f.end)),
|
||||
List(BreakStatement(variable)),
|
||||
List(increment)
|
||||
)),
|
||||
names),
|
||||
))
|
||||
case (ForDirection.DownTo, _, _) =>
|
||||
compile(ctx, List(
|
||||
Assignment(vex, f.start),
|
||||
IfStatement(
|
||||
FunctionCallExpression(">=", List(vex, f.end)),
|
||||
List(DoWhileStatement(
|
||||
f.body,
|
||||
List(decrement),
|
||||
FunctionCallExpression("!=", List(vex, f.end)),
|
||||
names
|
||||
)),
|
||||
Nil)
|
||||
))
|
||||
}
|
||||
|
||||
case BreakStatement(l) =>
|
||||
ctx.breakLabels.get(l) match {
|
||||
case None =>
|
||||
if (l == "") ErrorReporting.error("`break` outside a loop", statement.position)
|
||||
else ErrorReporting.error("Invalid label: " + l, statement.position)
|
||||
Nil
|
||||
case Some(label) =>
|
||||
List(AssemblyLine.absolute(JMP, label))
|
||||
}
|
||||
|
||||
case ContinueStatement(l) =>
|
||||
ctx.continueLabels.get(l) match {
|
||||
case None =>
|
||||
if (l == "") ErrorReporting.error("`continue` outside a loop", statement.position)
|
||||
else ErrorReporting.error("Invalid label: " + l, statement.position)
|
||||
Nil
|
||||
case Some(label) =>
|
||||
List(AssemblyLine.absolute(JMP, label))
|
||||
}
|
||||
// TODO
|
||||
case s: IfStatement =>
|
||||
compileIfStatement(ctx, s)
|
||||
case s: WhileStatement =>
|
||||
compileWhileStatement(ctx, s)
|
||||
case s: DoWhileStatement =>
|
||||
compileDoWhileStatement(ctx, s)
|
||||
case f:ForStatement =>
|
||||
compileForStatement(ctx,f)
|
||||
case s:BreakStatement =>
|
||||
compileBreakStatement(ctx, s)
|
||||
case s:ContinueStatement =>
|
||||
compileContinueStatement(ctx, s)
|
||||
}
|
||||
}
|
||||
|
||||
@ -527,4 +269,5 @@ object MosStatementCompiler {
|
||||
AssemblyLine.implied(TSX) :: (List.fill(m.stackVariablesSize)(AssemblyLine.implied(INX)) :+ AssemblyLine.implied(TXS)) // this TXS is fine, it won't appear in 65816 code
|
||||
}
|
||||
|
||||
override def nextLabel(prefix: String): String = MosCompiler.nextLabel(prefix)
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
package millfork.compiler.mos
|
||||
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.compiler.{BranchSpec, CompilationContext, NoBranching}
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node._
|
||||
|
25
src/main/scala/millfork/compiler/z80/Z80Compiler.scala
Normal file
25
src/main/scala/millfork/compiler/z80/Z80Compiler.scala
Normal file
@ -0,0 +1,25 @@
|
||||
package millfork.compiler.z80
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
import millfork.assembly.z80.ZLine
|
||||
import millfork.compiler.{AbstractCompiler, CompilationContext}
|
||||
import millfork.env.Label
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object Z80Compiler extends AbstractCompiler[ZLine] {
|
||||
|
||||
private val labelCounter = new AtomicLong
|
||||
|
||||
override def nextLabel(prefix: String): String = "." + prefix + "__" + labelCounter.incrementAndGet().formatted("%05d")
|
||||
|
||||
override def compile(ctx: CompilationContext): List[ZLine] = {
|
||||
ctx.env.nameCheck(ctx.function.code)
|
||||
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)
|
||||
}
|
||||
}
|
468
src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala
Normal file
468
src/main/scala/millfork/compiler/z80/Z80ExpressionCompiler.scala
Normal file
@ -0,0 +1,468 @@
|
||||
package millfork.compiler.z80
|
||||
|
||||
import millfork.assembly.z80._
|
||||
import millfork.compiler._
|
||||
import millfork.env._
|
||||
import millfork.node.{ZRegister, _}
|
||||
import millfork.assembly.z80.ZOpcode._
|
||||
import millfork.error.ErrorReporting
|
||||
|
||||
import scala.collection.GenTraversableOnce
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object ZExpressionTarget extends Enumeration {
|
||||
val A, HL, NOTHING = Value
|
||||
}
|
||||
|
||||
object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
|
||||
def compileToA(ctx: CompilationContext, expression: Expression): List[ZLine] = compile(ctx, expression, ZExpressionTarget.A)
|
||||
|
||||
def compileToHL(ctx: CompilationContext, expression: Expression): List[ZLine] = compile(ctx, expression, ZExpressionTarget.HL)
|
||||
|
||||
def changesBC(line: ZLine): Boolean = {
|
||||
import ZRegister._
|
||||
if (ZOpcodeClasses.ChangesBCAlways(line.opcode)) return true
|
||||
if (ZOpcodeClasses.ChangesFirstRegister(line.opcode)) return line.registers match {
|
||||
case TwoRegisters(B | C | BC, _) => true
|
||||
case _ => false
|
||||
}
|
||||
if (ZOpcodeClasses.ChangesOnlyRegister(line.opcode)) return line.registers match {
|
||||
case OneRegister(B | C | BC) => true
|
||||
case _ => false
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
def changesDE(line: ZLine): Boolean = {
|
||||
import ZRegister._
|
||||
if (ZOpcodeClasses.ChangesDEAlways(line.opcode)) return true
|
||||
if (ZOpcodeClasses.ChangesFirstRegister(line.opcode)) return line.registers match {
|
||||
case TwoRegisters(D | E | DE, _) => true
|
||||
case _ => false
|
||||
}
|
||||
if (ZOpcodeClasses.ChangesOnlyRegister(line.opcode)) return line.registers match {
|
||||
case OneRegister(D | E | DE) => true
|
||||
case _ => false
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
def changesHL(line: ZLine): Boolean = {
|
||||
import ZRegister._
|
||||
if (ZOpcodeClasses.ChangesHLAlways(line.opcode)) return true
|
||||
if (ZOpcodeClasses.ChangesFirstRegister(line.opcode)) return line.registers match {
|
||||
case TwoRegisters(H | L | HL, _) => true
|
||||
case _ => false
|
||||
}
|
||||
if (ZOpcodeClasses.ChangesOnlyRegister(line.opcode)) return line.registers match {
|
||||
case OneRegister(H | L | HL) => true
|
||||
case _ => false
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
def changesA(line: ZLine): Boolean = {
|
||||
import ZRegister._
|
||||
if (ZOpcodeClasses.ChangesAAlways(line.opcode)) return true
|
||||
if (ZOpcodeClasses.ChangesFirstRegister(line.opcode)) return line.registers match {
|
||||
case TwoRegisters(A | AF, _) => true
|
||||
case _ => false
|
||||
}
|
||||
if (ZOpcodeClasses.ChangesOnlyRegister(line.opcode)) return line.registers match {
|
||||
case OneRegister(A | AF) => true
|
||||
case _ => false
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
def stashBCIfChanged(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesBC))
|
||||
ZLine.register(PUSH, ZRegister.BC) :: (lines :+ ZLine.register(POP, ZRegister.BC)) else lines
|
||||
|
||||
def stashDEIfChanged(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesDE))
|
||||
ZLine.register(PUSH, ZRegister.DE) :: (lines :+ ZLine.register(POP, ZRegister.DE)) else lines
|
||||
|
||||
def stashHLIfChanged(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesHL))
|
||||
ZLine.register(PUSH, ZRegister.HL) :: (lines :+ ZLine.register(POP, ZRegister.HL)) else lines
|
||||
|
||||
def targetifyA(target: ZExpressionTarget.Value, lines: List[ZLine]): List[ZLine] = target match {
|
||||
case ZExpressionTarget.NOTHING | ZExpressionTarget.A => lines
|
||||
case ZExpressionTarget.HL => lines ++ List(
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ldImm8(ZRegister.H, 0)
|
||||
)
|
||||
}
|
||||
|
||||
def targetifyHL(target: ZExpressionTarget.Value, lines: List[ZLine]): List[ZLine] = target match {
|
||||
case ZExpressionTarget.NOTHING | ZExpressionTarget.HL => lines
|
||||
case ZExpressionTarget.A => lines :+ ZLine.ld8(ZRegister.A, ZRegister.L)
|
||||
}
|
||||
|
||||
def compile(ctx: CompilationContext, expression: Expression, target: ZExpressionTarget.Value, branches: BranchSpec = BranchSpec.None): List[ZLine] = {
|
||||
val env = ctx.env
|
||||
val b = env.get[Type]("byte")
|
||||
val w = env.get[Type]("word")
|
||||
env.eval(expression) match {
|
||||
case Some(const) =>
|
||||
target match {
|
||||
case ZExpressionTarget.A =>
|
||||
List(ZLine.ldImm8(ZRegister.A, const))
|
||||
case ZExpressionTarget.HL =>
|
||||
List(ZLine.ldImm16(ZRegister.HL, const))
|
||||
case ZExpressionTarget.NOTHING =>
|
||||
Nil // TODO
|
||||
}
|
||||
case None =>
|
||||
expression match {
|
||||
case LiteralExpression(value, _) => ???
|
||||
case VariableExpression(name) =>
|
||||
env.get[Variable](name) match {
|
||||
case v: VariableInMemory =>
|
||||
v.typ.size match {
|
||||
case 0 => ???
|
||||
case 1 => loadByte(v.toAddress, target)
|
||||
case 2 => target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.HL =>
|
||||
List(ZLine.ldAbs16(ZRegister.HL, v))
|
||||
}
|
||||
case _ => ???
|
||||
}
|
||||
}
|
||||
case i: IndexedExpression =>
|
||||
calculateAddressToHL(ctx, i) match {
|
||||
case List(ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr, _)) => loadByte(addr, target)
|
||||
case code => code ++ loadByteViaHL(target)
|
||||
}
|
||||
case SumExpression(params, decimal) =>
|
||||
getParamMaxSize(ctx, params.map(_._2)) match {
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitSum(ctx, params, decimal))
|
||||
case 2 => targetifyHL(target, ZBuiltIns.compile16BitSum(ctx, params, decimal))
|
||||
}
|
||||
case f@FunctionCallExpression(name, params) =>
|
||||
name match {
|
||||
case "not" =>
|
||||
assertBool(ctx, "not", params, 1)
|
||||
compile(ctx, params.head, target, branches.flip)
|
||||
case "hi" | "lo" => ??? // TODO
|
||||
case "nonet" => ??? // TODO
|
||||
case "&&" =>
|
||||
assertBool(ctx, "&&", params)
|
||||
branches match {
|
||||
case BranchIfFalse(_) =>
|
||||
params.flatMap(compile(ctx, _, target, branches))
|
||||
case _ =>
|
||||
val skip = Z80Compiler.nextLabel("an")
|
||||
params.init.flatMap(compile(ctx, _, target, BranchIfFalse(skip))) ++
|
||||
compile(ctx, params.last, target, branches) ++
|
||||
List(ZLine.label(skip))
|
||||
}
|
||||
case "||" =>
|
||||
assertBool(ctx, "||", params)
|
||||
branches match {
|
||||
case BranchIfTrue(_) =>
|
||||
params.flatMap(compile(ctx, _, target, branches))
|
||||
case _ =>
|
||||
val skip = Z80Compiler.nextLabel("or")
|
||||
params.init.flatMap(compile(ctx, _, target, BranchIfTrue(skip))) ++
|
||||
compile(ctx, params.last, target, branches) ++
|
||||
List(ZLine.label(skip))
|
||||
}
|
||||
case "^^" => ???
|
||||
|
||||
case "&" =>
|
||||
getParamMaxSize(ctx, params) match {
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, AND, params))
|
||||
case 2 => targetifyHL(target, ZBuiltIns.compile16BitOperation(ctx, AND, params))
|
||||
}
|
||||
case "*" => ???
|
||||
case "|" =>
|
||||
getParamMaxSize(ctx, params) match {
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, OR, params))
|
||||
case 2 => targetifyHL(target, ZBuiltIns.compile16BitOperation(ctx, OR, params))
|
||||
}
|
||||
case "^" =>
|
||||
getParamMaxSize(ctx, params) match {
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, XOR, params))
|
||||
case 2 => targetifyHL(target, ZBuiltIns.compile16BitOperation(ctx, XOR, params))
|
||||
}
|
||||
case ">>>>" =>
|
||||
val (l, r, 2) = assertBinary(ctx, params)
|
||||
???
|
||||
case "<<" =>
|
||||
val (l, r, size) = assertBinary(ctx, params)
|
||||
???
|
||||
case ">>" =>
|
||||
val (l, r, size) = assertBinary(ctx, params)
|
||||
???
|
||||
case "<<'" =>
|
||||
assertAllBytes("Long shift ops not supported", ctx, params)
|
||||
val (l, r, 1) = assertBinary(ctx, params)
|
||||
???
|
||||
case ">>'" =>
|
||||
assertAllBytes("Long shift ops not supported", ctx, params)
|
||||
val (l, r, 1) = assertBinary(ctx, params)
|
||||
???
|
||||
case "<" =>
|
||||
val (size, signed) = assertComparison(ctx, params)
|
||||
???
|
||||
case ">=" =>
|
||||
val (size, signed) = assertComparison(ctx, params)
|
||||
???
|
||||
case ">" =>
|
||||
val (size, signed) = assertComparison(ctx, params)
|
||||
???
|
||||
case "<=" =>
|
||||
val (size, signed) = assertComparison(ctx, params)
|
||||
???
|
||||
case "==" =>
|
||||
val size = params.map(p => getExpressionType(ctx, p).size).max
|
||||
???
|
||||
case "!=" =>
|
||||
val (l, r, size) = assertBinary(ctx, params)
|
||||
???
|
||||
case "+=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, ADD)
|
||||
case _ => ???
|
||||
}
|
||||
case "-=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, SUB)
|
||||
case _ => ???
|
||||
}
|
||||
case "+'=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, ADD, decimal = true)
|
||||
case _ => ???
|
||||
}
|
||||
case "-'=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, SUB, decimal = true)
|
||||
case _ => ???
|
||||
}
|
||||
Nil
|
||||
case "<<=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
???
|
||||
case ">>=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
???
|
||||
case "<<'=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
???
|
||||
case ">>'=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
???
|
||||
case "*=" =>
|
||||
assertAllBytes("Long multiplication not supported", ctx, params)
|
||||
val (l, r, 1) = assertAssignmentLike(ctx, params)
|
||||
???
|
||||
case "*'=" =>
|
||||
assertAllBytes("Long multiplication not supported", ctx, params)
|
||||
val (l, r, 1) = assertAssignmentLike(ctx, params)
|
||||
???
|
||||
case "&=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, AND)
|
||||
case _ => ???
|
||||
}
|
||||
case "^=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, XOR)
|
||||
case _ => ???
|
||||
}
|
||||
case "|=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, OR)
|
||||
case _ => ???
|
||||
}
|
||||
case _ =>
|
||||
env.maybeGet[Type](f.functionName) match {
|
||||
case Some(typ) =>
|
||||
var failed = false
|
||||
if (typ.name == "pointer") {
|
||||
ErrorReporting.error("Cannot cast into pointer")
|
||||
failed = true
|
||||
}
|
||||
if (params.length != 1) {
|
||||
ErrorReporting.error("Type casting should have exactly one argument")
|
||||
failed = true
|
||||
}
|
||||
val sourceType = getExpressionType(ctx, params.head)
|
||||
if (typ.size != sourceType.size) {
|
||||
ErrorReporting.error("Cannot cast a type to a type of different size")
|
||||
failed = true
|
||||
}
|
||||
return sourceType.size match {
|
||||
case 1 => targetifyA(target, compileToA(ctx, params.head))
|
||||
case 2 => targetifyHL(target, compileToHL(ctx, params.head))
|
||||
case _ => ???
|
||||
}
|
||||
case None =>
|
||||
// fallthrough to the lookup below
|
||||
}
|
||||
lookupFunction(ctx, f) match {
|
||||
case function: MacroFunction =>
|
||||
val (paramPreparation, statements) = Z80MacroExpander.inlineFunction(ctx, function, params, expression.position)
|
||||
paramPreparation ++ statements.map {
|
||||
case _ => ???
|
||||
}
|
||||
case function: EmptyFunction =>
|
||||
??? // TODO: type conversion?
|
||||
case function: FunctionInMemory =>
|
||||
function match {
|
||||
case nf: NormalFunction =>
|
||||
if (nf.interrupt) {
|
||||
ErrorReporting.error(s"Calling an interrupt function `${f.functionName}`", expression.position)
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
val result = function.params match {
|
||||
case AssemblyParamSignature(paramConvs) =>
|
||||
???
|
||||
case NormalParamSignature(paramVars) =>
|
||||
params.zip(paramVars).flatMap {
|
||||
case (paramExpr, paramVar) =>
|
||||
val callCtx = callingContext(ctx, paramVar)
|
||||
paramVar.typ.size match {
|
||||
case 1 =>
|
||||
compileToA(ctx, paramExpr) ++ storeA(callCtx, VariableExpression(paramVar.name), paramVar.typ.isSigned)
|
||||
case 2 =>
|
||||
compileToHL(ctx, paramExpr) ++ storeHL(callCtx, VariableExpression(paramVar.name), paramVar.typ.isSigned)
|
||||
case _ => ???
|
||||
}
|
||||
} ++ List(ZLine(CALL, NoRegisters, function.toAddress))
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def calculateAddressToHL(ctx: CompilationContext, i: IndexedExpression): List[ZLine] = {
|
||||
val env = ctx.env
|
||||
env.getPointy(i.name) match {
|
||||
case ConstantPointy(baseAddr, _) =>
|
||||
env.evalVariableAndConstantSubParts(i.index) match {
|
||||
case (None, offset) => List(ZLine.ldImm16(ZRegister.HL, (baseAddr + offset).quickSimplify))
|
||||
case (Some(index), offset) =>
|
||||
List(ZLine.ldImm16(ZRegister.BC, (baseAddr + offset).quickSimplify)) ++
|
||||
stashBCIfChanged(compileToHL(ctx, index)) ++
|
||||
List(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
}
|
||||
case VariablePointy(varAddr) =>
|
||||
compileToHL(ctx, i.index) ++
|
||||
loadBCFromHL ++
|
||||
List(
|
||||
ZLine.ldAbs16(ZRegister.HL, varAddr),
|
||||
ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
}
|
||||
}
|
||||
|
||||
private val loadBCFromHL = List(
|
||||
ZLine.ld8(ZRegister.B, ZRegister.H),
|
||||
ZLine.ld8(ZRegister.C, ZRegister.L)
|
||||
)
|
||||
|
||||
def loadByte(sourceAddr: Constant, target: ZExpressionTarget.Value): List[ZLine] = {
|
||||
target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.A => List(ZLine.ldAbs8(ZRegister.A, sourceAddr))
|
||||
case ZExpressionTarget.HL => List(ZLine.ldAbs8(ZRegister.A, sourceAddr), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0))
|
||||
}
|
||||
}
|
||||
|
||||
def loadByteViaHL(target: ZExpressionTarget.Value): List[ZLine] = {
|
||||
target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.A => List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL))
|
||||
case ZExpressionTarget.HL => List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0))
|
||||
}
|
||||
}
|
||||
|
||||
def signExtend(targetAddr: Constant, 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.ldAbs8((targetAddr + i).quickSimplify, ZRegister.A))
|
||||
prepareA ++ fillUpperBytes
|
||||
}
|
||||
|
||||
def storeA(targetAddr: Constant, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ldAbs8(targetAddr, ZRegister.A))
|
||||
case n => ZLine.ldAbs8(targetAddr, ZRegister.A) :: signExtend(targetAddr + 1, ZRegister.A, n - 1, signedSource)
|
||||
}
|
||||
}
|
||||
|
||||
def storeHL(targetAddr: Constant, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
// TODO: LD (nnnn),HL compatibility?
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ld8(ZRegister.A, ZRegister.L), ZLine.ldAbs8(targetAddr, ZRegister.A))
|
||||
case 2 => List(ZLine.ldAbs16(targetAddr, ZRegister.HL))
|
||||
case n => ZLine.ldAbs16(targetAddr, ZRegister.HL) :: signExtend(targetAddr + 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 i:IndexedExpression =>
|
||||
calculateAddressToHL(ctx, i) match {
|
||||
case List(ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr, _)) => storeA(addr, 1, signedSource)
|
||||
case code => if (code.exists(changesA)) {
|
||||
List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++ stashDEIfChanged(code) :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.E)
|
||||
} else code :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)
|
||||
}
|
||||
//TODO
|
||||
case SeparateBytesExpression(hi, lo) => ???
|
||||
}
|
||||
}
|
||||
|
||||
def storeHL(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 => storeHL(v.toAddress, v.typ.size, signedSource)
|
||||
}
|
||||
case IndexedExpression(pointyName, indexExpr) =>
|
||||
env.getPointy(pointyName) match {
|
||||
case p: ConstantPointy =>
|
||||
env.evalVariableAndConstantSubParts(indexExpr) match {
|
||||
case (None, offset) => ZLine.ld8(ZRegister.A, ZRegister.L) :: storeA((p.value + offset).quickSimplify, 1, signedSource)
|
||||
}
|
||||
}
|
||||
//TODO
|
||||
case SeparateBytesExpression(hi, lo) => ???
|
||||
}
|
||||
}
|
||||
}
|
15
src/main/scala/millfork/compiler/z80/Z80MacroExpander.scala
Normal file
15
src/main/scala/millfork/compiler/z80/Z80MacroExpander.scala
Normal file
@ -0,0 +1,15 @@
|
||||
package millfork.compiler.z80
|
||||
|
||||
import millfork.assembly.z80.ZLine
|
||||
import millfork.compiler.{CompilationContext, MacroExpander}
|
||||
import millfork.env.AssemblyParam
|
||||
import millfork.node.{ExecutableStatement, Expression}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object Z80MacroExpander extends MacroExpander[ZLine] {
|
||||
override def nextLabel(prefix: String): String = Z80Compiler.nextLabel(prefix)
|
||||
|
||||
override def prepareAssemblyParams(ctx: CompilationContext, assParams: List[AssemblyParam], params: List[Expression], code: List[ExecutableStatement]): (List[ZLine], List[ExecutableStatement]) = ???
|
||||
}
|
108
src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala
Normal file
108
src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala
Normal file
@ -0,0 +1,108 @@
|
||||
package millfork.compiler.z80
|
||||
|
||||
import millfork.assembly.BranchingOpcodeMapping
|
||||
import millfork.assembly.z80.ZLine
|
||||
import millfork.compiler.{AbstractExpressionCompiler, AbstractStatementCompiler, BranchSpec, CompilationContext}
|
||||
import millfork.env.{BooleanType, ConstantBooleanType, Label, MacroFunction}
|
||||
import millfork.node._
|
||||
import millfork.assembly.z80.ZOpcode._
|
||||
import millfork.error.ErrorReporting
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
||||
|
||||
def compile(ctx: CompilationContext, statements: List[ExecutableStatement]): List[ZLine] = {
|
||||
statements.flatMap(s => compile(ctx, s))
|
||||
}
|
||||
|
||||
def compile(ctx: CompilationContext, statement: ExecutableStatement): List[ZLine] = {
|
||||
val options = ctx.options
|
||||
statement match {
|
||||
case ReturnStatement(None) =>
|
||||
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 {
|
||||
case 0 =>
|
||||
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
|
||||
case 1 =>
|
||||
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 2 =>
|
||||
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))
|
||||
case 1 =>
|
||||
Z80ExpressionCompiler.compileToA(ctx, e) ++
|
||||
List(ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
|
||||
case 2 =>
|
||||
Z80ExpressionCompiler.compileToHL(ctx, e) ++
|
||||
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))
|
||||
case 1 =>
|
||||
Z80ExpressionCompiler.compileToA(ctx, e) ++
|
||||
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
|
||||
case 2 =>
|
||||
Z80ExpressionCompiler.compileToHL(ctx, e) ++
|
||||
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_BCDEIX), ZLine.implied(RET))
|
||||
}
|
||||
}
|
||||
case Assignment(destination, source) =>
|
||||
val sourceType = AbstractExpressionCompiler.getExpressionType(ctx, source)
|
||||
sourceType.size match {
|
||||
case 0 => ???
|
||||
case 1 => Z80ExpressionCompiler.compileToA(ctx, source) ++ Z80ExpressionCompiler.storeA(ctx, destination, sourceType.isSigned)
|
||||
case 2 => Z80ExpressionCompiler.compileToHL(ctx, source) ++ Z80ExpressionCompiler.storeHL(ctx, destination, sourceType.isSigned)
|
||||
case _ => ??? // large object copy
|
||||
}
|
||||
case s: IfStatement =>
|
||||
compileIfStatement(ctx, s)
|
||||
case s: WhileStatement =>
|
||||
compileWhileStatement(ctx, s)
|
||||
case s: DoWhileStatement =>
|
||||
compileDoWhileStatement(ctx, s)
|
||||
case f:ForStatement =>
|
||||
compileForStatement(ctx,f)
|
||||
case s:BreakStatement =>
|
||||
compileBreakStatement(ctx, s)
|
||||
case s:ContinueStatement =>
|
||||
compileContinueStatement(ctx, s)
|
||||
case ExpressionStatement(e@FunctionCallExpression(name, params)) =>
|
||||
ctx.env.lookupFunction(name, params.map(p => Z80ExpressionCompiler.getExpressionType(ctx, p) -> p)) match {
|
||||
case Some(i: MacroFunction) =>
|
||||
val (paramPreparation, inlinedStatements) = Z80MacroExpander.inlineFunction(ctx, i, params, e.position)
|
||||
paramPreparation ++ compile(ctx.withInlinedEnv(i.environment, Z80Compiler.nextLabel("en")), inlinedStatements)
|
||||
case _ =>
|
||||
Z80ExpressionCompiler.compile(ctx, e, ZExpressionTarget.NOTHING)
|
||||
}
|
||||
case ExpressionStatement(e) =>
|
||||
Z80ExpressionCompiler.compile(ctx, e, ZExpressionTarget.NOTHING)
|
||||
}
|
||||
}
|
||||
|
||||
def labelChunk(labelName: String) = List(ZLine.label(Label(labelName)))
|
||||
|
||||
def jmpChunk(label: Label) = List(ZLine.jump(label))
|
||||
|
||||
def branchChunk(opcode: BranchingOpcodeMapping, labelName: String) = List(ZLine.jump(Label(labelName), opcode.z80Flags))
|
||||
|
||||
def areBlocksLarge(blocks: List[ZLine]*): Boolean = false
|
||||
|
||||
override def nextLabel(prefix: String): String = Z80Compiler.nextLabel(prefix)
|
||||
|
||||
override def compileExpressionForBranching(ctx: CompilationContext, expr: Expression, branching: BranchSpec): List[ZLine] =
|
||||
Z80ExpressionCompiler.compile(ctx, expr, ZExpressionTarget.NOTHING, branching)
|
||||
}
|
347
src/main/scala/millfork/compiler/z80/ZBuiltIns.scala
Normal file
347
src/main/scala/millfork/compiler/z80/ZBuiltIns.scala
Normal file
@ -0,0 +1,347 @@
|
||||
package millfork.compiler.z80
|
||||
|
||||
import millfork.assembly.z80.{ZLine, ZOpcode}
|
||||
import millfork.compiler.CompilationContext
|
||||
import millfork.node._
|
||||
import ZOpcode._
|
||||
import millfork.CompilationFlag
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object ZBuiltIns {
|
||||
|
||||
def compile8BitOperation(ctx: CompilationContext, op: ZOpcode.Value, params: List[Expression]): List[ZLine] = {
|
||||
var const = op match {
|
||||
case AND => NumericConstant(0xff, 1)
|
||||
case OR | XOR => Constant.Zero
|
||||
}
|
||||
var hasConst = false
|
||||
var result = mutable.ListBuffer[ZLine]()
|
||||
params.foreach(expr => ctx.env.eval(expr) match {
|
||||
case None =>
|
||||
if (result.isEmpty) {
|
||||
result ++= Z80ExpressionCompiler.compileToA(ctx, expr)
|
||||
} else {
|
||||
result += ZLine.ld8(ZRegister.E, ZRegister.A)
|
||||
result ++= Z80ExpressionCompiler.stashDEIfChanged(Z80ExpressionCompiler.compileToA(ctx, expr))
|
||||
result += ZLine.register(op, ZRegister.E)
|
||||
}
|
||||
case Some(c) =>
|
||||
hasConst = true
|
||||
const = op match {
|
||||
case AND => CompoundConstant(MathOperator.And, const, c).quickSimplify
|
||||
case OR => CompoundConstant(MathOperator.Or, const, c).quickSimplify
|
||||
case XOR => CompoundConstant(MathOperator.Exor, const, c).quickSimplify
|
||||
}
|
||||
})
|
||||
if (hasConst) {
|
||||
result += ZLine.imm8(op, const)
|
||||
}
|
||||
result.toList
|
||||
}
|
||||
|
||||
def compile16BitOperation(ctx: CompilationContext, op: ZOpcode.Value, params: List[Expression]): List[ZLine] = {
|
||||
var const = op match {
|
||||
case AND => NumericConstant(0xffff, 1)
|
||||
case OR | XOR => Constant.Zero
|
||||
}
|
||||
var hasConst = false
|
||||
var result = mutable.ListBuffer[ZLine]()
|
||||
params.foreach(expr => ctx.env.eval(expr) match {
|
||||
case None =>
|
||||
if (result.isEmpty) {
|
||||
result ++= Z80ExpressionCompiler.compileToHL(ctx, expr)
|
||||
} else {
|
||||
result += ZLine.ld8(ZRegister.E, ZRegister.L)
|
||||
result += ZLine.ld8(ZRegister.D, ZRegister.H)
|
||||
result ++= Z80ExpressionCompiler.stashDEIfChanged(Z80ExpressionCompiler.compileToHL(ctx, expr))
|
||||
result ++= List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
ZLine.register(op, ZRegister.E),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.H),
|
||||
ZLine.register(op, ZRegister.D),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.A)
|
||||
)
|
||||
}
|
||||
case Some(c) =>
|
||||
hasConst = true
|
||||
const = op match {
|
||||
case AND => CompoundConstant(MathOperator.And, const, c).quickSimplify
|
||||
case OR => CompoundConstant(MathOperator.Or, const, c).quickSimplify
|
||||
case XOR => CompoundConstant(MathOperator.Exor, const, c).quickSimplify
|
||||
}
|
||||
})
|
||||
if (hasConst) {
|
||||
result ++= List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
ZLine.imm8(op, const.loByte),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.H),
|
||||
ZLine.imm8(op, const.hiByte),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.A)
|
||||
)
|
||||
}
|
||||
result.toList
|
||||
}
|
||||
|
||||
def compile8BitSum(ctx: CompilationContext, params: List[(Boolean, Expression)], decimal: Boolean): List[ZLine] = {
|
||||
var const = Constant.Zero
|
||||
var hasConst = false
|
||||
var result = mutable.ListBuffer[ZLine]()
|
||||
params.foreach {
|
||||
case (false, expr) =>
|
||||
ctx.env.eval(expr) match {
|
||||
case None =>
|
||||
if (result.isEmpty) {
|
||||
result ++= Z80ExpressionCompiler.compileToA(ctx, expr)
|
||||
} else {
|
||||
result += ZLine.ld8(ZRegister.E, ZRegister.A)
|
||||
result ++= Z80ExpressionCompiler.stashDEIfChanged(Z80ExpressionCompiler.compileToA(ctx, expr))
|
||||
result += ZLine.register(ADD, ZRegister.E)
|
||||
if (decimal) {
|
||||
result += ZLine.implied(DAA)
|
||||
}
|
||||
}
|
||||
case Some(c) =>
|
||||
hasConst = true
|
||||
if (decimal) {
|
||||
const = CompoundConstant(MathOperator.DecimalPlus, const, c).quickSimplify
|
||||
} else {
|
||||
const = (const + c).quickSimplify
|
||||
}
|
||||
}
|
||||
case (true, expr) =>
|
||||
ctx.env.eval(expr) match {
|
||||
case None =>
|
||||
if (result.isEmpty) {
|
||||
result ++= Z80ExpressionCompiler.compileToA(ctx, expr)
|
||||
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||
result += ZLine.implied(NEG)
|
||||
} else {
|
||||
result += ZLine.implied(CPL)
|
||||
result += ZLine.register(INC, ZRegister.A)
|
||||
}
|
||||
} else {
|
||||
// TODO: optimize
|
||||
result += ZLine.ld8(ZRegister.D, ZRegister.A)
|
||||
result ++= Z80ExpressionCompiler.stashDEIfChanged(Z80ExpressionCompiler.compileToA(ctx, expr))
|
||||
result += ZLine.ld8(ZRegister.E, ZRegister.A)
|
||||
result += ZLine.ld8(ZRegister.A, ZRegister.D)
|
||||
result += ZLine.register(SUB, ZRegister.E)
|
||||
if (decimal) {
|
||||
result += ZLine.implied(DAA)
|
||||
}
|
||||
}
|
||||
case Some(c) =>
|
||||
hasConst = true
|
||||
if (decimal) {
|
||||
const = CompoundConstant(MathOperator.DecimalMinus, const, c).quickSimplify
|
||||
} else {
|
||||
const = (const - c).quickSimplify
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasConst) {
|
||||
result += ZLine.imm8(AND, const.loByte)
|
||||
}
|
||||
result.toList
|
||||
}
|
||||
|
||||
def compile16BitSum(ctx: CompilationContext, params: List[(Boolean, Expression)], decimal: Boolean): List[ZLine] = {
|
||||
var const = Constant.Zero
|
||||
var hasConst = false
|
||||
var result = mutable.ListBuffer[ZLine]()
|
||||
if (decimal) {
|
||||
params.foreach {
|
||||
case (false, expr) =>
|
||||
ctx.env.eval(expr) match {
|
||||
case None =>
|
||||
if (result.isEmpty) {
|
||||
result ++= Z80ExpressionCompiler.compileToHL(ctx, expr)
|
||||
} else {
|
||||
result += ZLine.ld8(ZRegister.E, ZRegister.L)
|
||||
result += ZLine.ld8(ZRegister.D, ZRegister.H)
|
||||
result ++= Z80ExpressionCompiler.stashDEIfChanged(Z80ExpressionCompiler.compileToHL(ctx, expr))
|
||||
result += ZLine.ld8(ZRegister.A, ZRegister.L)
|
||||
result += ZLine.registers(ADD, ZRegister.A, ZRegister.E)
|
||||
result += ZLine.implied(DAA)
|
||||
result += ZLine.ld8(ZRegister.L, ZRegister.A)
|
||||
result += ZLine.ld8(ZRegister.A, ZRegister.H)
|
||||
result += ZLine.registers(ADC, ZRegister.A, ZRegister.D)
|
||||
result += ZLine.implied(DAA)
|
||||
result += ZLine.ld8(ZRegister.H, ZRegister.A)
|
||||
}
|
||||
case Some(c) =>
|
||||
hasConst = true
|
||||
const = CompoundConstant(MathOperator.DecimalPlus, const, c).quickSimplify
|
||||
}
|
||||
case (true, expr) =>
|
||||
ctx.env.eval(expr) match {
|
||||
case None =>
|
||||
ErrorReporting.error("Decimal subtraction not supported on Intel 8080-like CPUs.", expr.position)
|
||||
case Some(c) =>
|
||||
hasConst = true
|
||||
const = CompoundConstant(MathOperator.DecimalMinus, const, c).quickSimplify
|
||||
}
|
||||
}
|
||||
} else {
|
||||
params.foreach {
|
||||
case (false, expr) =>
|
||||
ctx.env.eval(expr) match {
|
||||
case None =>
|
||||
if (result.isEmpty) {
|
||||
result ++= Z80ExpressionCompiler.compileToHL(ctx, expr)
|
||||
} else {
|
||||
result += ZLine.ld8(ZRegister.E, ZRegister.L)
|
||||
result += ZLine.ld8(ZRegister.D, ZRegister.H)
|
||||
result ++= Z80ExpressionCompiler.stashDEIfChanged(Z80ExpressionCompiler.compileToHL(ctx, expr))
|
||||
result += ZLine.registers(ADD_16, ZRegister.HL, ZRegister.DE)
|
||||
}
|
||||
case Some(c) =>
|
||||
hasConst = true
|
||||
const = (const + c).quickSimplify
|
||||
}
|
||||
case (true, expr) =>
|
||||
ctx.env.eval(expr) match {
|
||||
case None =>
|
||||
if (result.isEmpty) {
|
||||
???
|
||||
} else {
|
||||
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||
// TODO: optimize
|
||||
result += ZLine.ld8(ZRegister.D, ZRegister.H)
|
||||
result += ZLine.ld8(ZRegister.E, ZRegister.L)
|
||||
result ++= Z80ExpressionCompiler.stashDEIfChanged(Z80ExpressionCompiler.compileToHL(ctx, expr))
|
||||
result += ZLine.ld8(ZRegister.B, ZRegister.H)
|
||||
result += ZLine.ld8(ZRegister.C, ZRegister.L)
|
||||
result += ZLine.ld8(ZRegister.H, ZRegister.D)
|
||||
result += ZLine.ld8(ZRegister.L, ZRegister.E)
|
||||
/// TODO: carry?
|
||||
result += ZLine.registers(SBC_16, ZRegister.HL, ZRegister.BC)
|
||||
} else {
|
||||
???
|
||||
}
|
||||
}
|
||||
case Some(c) =>
|
||||
hasConst = true
|
||||
const = (const - c).quickSimplify
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasConst) {
|
||||
result ++= List(
|
||||
ZLine.ldImm8(ZRegister.D, const.hiByte),
|
||||
ZLine.ldImm8(ZRegister.E, const.loByte),
|
||||
ZLine.registers(ADD_16, ZRegister.HL, ZRegister.DE)
|
||||
)
|
||||
}
|
||||
result.toList
|
||||
}
|
||||
|
||||
def perform8BitInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, opcode: ZOpcode.Value, decimal: Boolean = false): List[ZLine] = {
|
||||
val calculateAddress = lhs match {
|
||||
case VariableExpression(name) =>
|
||||
ctx.env.get[Variable](name) match {
|
||||
case v: VariableInMemory => List(ZLine.ldImm16(ZRegister.HL, v.toAddress))
|
||||
case _ => ???
|
||||
}
|
||||
case i: IndexedExpression => Z80ExpressionCompiler.calculateAddressToHL(ctx, i)
|
||||
}
|
||||
val constantRight = ctx.env.eval(rhs)
|
||||
val calculateChange = Z80ExpressionCompiler.compileToA(ctx, rhs)
|
||||
val setup = (calculateChange.exists(Z80ExpressionCompiler.changesHL), calculateAddress.exists(Z80ExpressionCompiler.changesA)) match {
|
||||
case (false, false) => calculateChange ++ calculateAddress
|
||||
case (true, false) => calculateChange ++ calculateAddress
|
||||
case (false, true) => calculateAddress ++ calculateChange
|
||||
case (true, true) => calculateAddress ++ List(ZLine.register(PUSH, ZRegister.HL)) ++ calculateChange ++ List(ZLine.register(POP, ZRegister.HL))
|
||||
}
|
||||
opcode match {
|
||||
case ADD if decimal =>
|
||||
setup ++ List(
|
||||
ZLine.register(ADD, ZRegister.MEM_HL),
|
||||
ZLine.implied(DAA),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
case ADD if !decimal =>
|
||||
constantRight match {
|
||||
case Some(NumericConstant(1, _)) =>
|
||||
calculateAddress :+ ZLine.register(INC, ZRegister.MEM_HL)
|
||||
case Some(NumericConstant(0xff | -1, _)) =>
|
||||
calculateAddress :+ ZLine.register(DEC, ZRegister.MEM_HL)
|
||||
case _ =>
|
||||
setup ++ List(
|
||||
ZLine.register(ADD, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
}
|
||||
case SUB if decimal =>
|
||||
setup ++ List(
|
||||
ZLine.ld8(ZRegister.E, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.register(SUB, ZRegister.E),
|
||||
ZLine.implied(DAA),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
case SUB if !decimal=>
|
||||
constantRight match {
|
||||
case Some(NumericConstant(1, _)) =>
|
||||
calculateAddress :+ ZLine.register(DEC, ZRegister.MEM_HL)
|
||||
case Some(NumericConstant(0xff | -1, _)) =>
|
||||
calculateAddress :+ ZLine.register(INC, ZRegister.MEM_HL)
|
||||
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))
|
||||
} else {
|
||||
setup ++ List(
|
||||
ZLine.implied(CPL),
|
||||
ZLine.register(INC, ZRegister.A),
|
||||
ZLine.register(SUB, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
}
|
||||
}
|
||||
case XOR =>
|
||||
constantRight match {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
calculateAddress
|
||||
case Some(NumericConstant(0xff | -1, _)) =>
|
||||
calculateAddress ++ List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.implied(CPL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
case _ =>
|
||||
setup ++ List(
|
||||
ZLine.register(XOR, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
}
|
||||
case XOR =>
|
||||
constantRight match {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
calculateAddress
|
||||
case Some(NumericConstant(0xff | -1, _)) =>
|
||||
calculateAddress :+ ZLine.ldImm8(ZRegister.MEM_HL, 0xff)
|
||||
case _ =>
|
||||
setup ++ List(
|
||||
ZLine.register(XOR, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
}
|
||||
case AND =>
|
||||
constantRight match {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
calculateAddress :+ ZLine.ldImm8(ZRegister.MEM_HL, 0)
|
||||
case Some(NumericConstant(0xff | -1, _)) =>
|
||||
calculateAddress
|
||||
case _ =>
|
||||
setup ++ List(
|
||||
ZLine.register(AND, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
54
src/main/scala/millfork/env/Environment.scala
vendored
54
src/main/scala/millfork/env/Environment.scala
vendored
@ -2,8 +2,10 @@ package millfork.env
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.assembly.BranchingOpcodeMapping
|
||||
import millfork.{CompilationFlag, CompilationOptions, CpuFamily}
|
||||
import millfork.assembly.mos.Opcode
|
||||
import millfork.assembly.z80.{IfFlagClear, IfFlagSet, ZFlag}
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node._
|
||||
import millfork.output.{CompiledMemory, VariableAllocator}
|
||||
@ -265,14 +267,38 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
addThing(ConstantThing("true", NumericConstant(0, 0), trueType), None)
|
||||
addThing(ConstantThing("false", NumericConstant(0, 0), falseType), None)
|
||||
addThing(ConstantThing("__zeropage_usage", UnexpandedConstant("__zeropage_usage", 1), b), None)
|
||||
addThing(FlagBooleanType("set_carry", Opcode.BCS, Opcode.BCC), None)
|
||||
addThing(FlagBooleanType("clear_carry", Opcode.BCC, Opcode.BCS), None)
|
||||
addThing(FlagBooleanType("set_overflow", Opcode.BVS, Opcode.BVC), None)
|
||||
addThing(FlagBooleanType("clear_overflow", Opcode.BVC, Opcode.BVS), None)
|
||||
addThing(FlagBooleanType("set_zero", Opcode.BEQ, Opcode.BNE), None)
|
||||
addThing(FlagBooleanType("clear_zero", Opcode.BNE, Opcode.BEQ), None)
|
||||
addThing(FlagBooleanType("set_negative", Opcode.BMI, Opcode.BPL), None)
|
||||
addThing(FlagBooleanType("clear_negative", Opcode.BPL, Opcode.BMI), None)
|
||||
addThing(FlagBooleanType("set_carry",
|
||||
BranchingOpcodeMapping(Opcode.BCS, IfFlagSet(ZFlag.C)),
|
||||
BranchingOpcodeMapping(Opcode.BCC, IfFlagClear(ZFlag.C))),
|
||||
None)
|
||||
addThing(FlagBooleanType("clear_carry",
|
||||
BranchingOpcodeMapping(Opcode.BCC, IfFlagClear(ZFlag.C)),
|
||||
BranchingOpcodeMapping(Opcode.BCS, IfFlagSet(ZFlag.C))),
|
||||
None)
|
||||
addThing(FlagBooleanType("set_overflow",
|
||||
BranchingOpcodeMapping(Opcode.BVS, IfFlagSet(ZFlag.P)),
|
||||
BranchingOpcodeMapping(Opcode.BVC, IfFlagClear(ZFlag.P))),
|
||||
None)
|
||||
addThing(FlagBooleanType("clear_overflow",
|
||||
BranchingOpcodeMapping(Opcode.BVC, IfFlagClear(ZFlag.P)),
|
||||
BranchingOpcodeMapping(Opcode.BVS, IfFlagSet(ZFlag.P))),
|
||||
None)
|
||||
addThing(FlagBooleanType("set_zero",
|
||||
BranchingOpcodeMapping(Opcode.BEQ, IfFlagSet(ZFlag.Z)),
|
||||
BranchingOpcodeMapping(Opcode.BNE, IfFlagClear(ZFlag.Z))),
|
||||
None)
|
||||
addThing(FlagBooleanType("clear_zero",
|
||||
BranchingOpcodeMapping(Opcode.BNE, IfFlagClear(ZFlag.Z)),
|
||||
BranchingOpcodeMapping(Opcode.BEQ, IfFlagSet(ZFlag.Z))),
|
||||
None)
|
||||
addThing(FlagBooleanType("set_negative",
|
||||
BranchingOpcodeMapping(Opcode.BMI, IfFlagSet(ZFlag.S)),
|
||||
BranchingOpcodeMapping(Opcode.BPL, IfFlagClear(ZFlag.S))),
|
||||
None)
|
||||
addThing(FlagBooleanType("clear_negative",
|
||||
BranchingOpcodeMapping(Opcode.BPL, IfFlagClear(ZFlag.S)),
|
||||
BranchingOpcodeMapping(Opcode.BMI, IfFlagSet(ZFlag.S))),
|
||||
None)
|
||||
}
|
||||
|
||||
def assertNotDefined(name: String, position: Option[Position]): Unit = {
|
||||
@ -523,7 +549,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
pd.assemblyParamPassingConvention match {
|
||||
case ByVariable(vn) =>
|
||||
AssemblyParam(typ, env.get[MemoryVariable](vn), AssemblyParameterPassingBehaviour.Copy)
|
||||
case ByRegister(reg) =>
|
||||
case ByMosRegister(reg) =>
|
||||
AssemblyParam(typ, RegisterVariable(reg, typ), AssemblyParameterPassingBehaviour.Copy)
|
||||
case ByConstant(vn) =>
|
||||
AssemblyParam(typ, Placeholder(vn, typ), AssemblyParameterPassingBehaviour.ByConstant)
|
||||
@ -658,7 +684,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
addThing(RelativeVariable(v.name + ".b0", addr, b, zeropage = zp, None), stmt.position)
|
||||
case _ =>
|
||||
}
|
||||
case ByRegister(_) => ()
|
||||
case ByMosRegister(_) => ()
|
||||
case ByConstant(name) =>
|
||||
val v = ConstantThing(prefix + name, UnexpandedConstant(prefix + name, typ.size), typ)
|
||||
addThing(v, stmt.position)
|
||||
@ -996,8 +1022,10 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
initialValue = None,
|
||||
address = None), options)
|
||||
}
|
||||
if (!things.contains("__constant8")) {
|
||||
things("__constant8") = InitializedArray("__constant8", None, List(LiteralExpression(8, 1)), declaredBank = None)
|
||||
if (CpuFamily.forType(options.platform.cpu) == CpuFamily.M6502) {
|
||||
if (!things.contains("__constant8")) {
|
||||
things("__constant8") = InitializedArray("__constant8", None, List(LiteralExpression(8, 1)), declaredBank = None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
13
src/main/scala/millfork/env/Thing.scala
vendored
13
src/main/scala/millfork/env/Thing.scala
vendored
@ -1,6 +1,7 @@
|
||||
package millfork.env
|
||||
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.assembly.BranchingOpcodeMapping
|
||||
import millfork.{CompilationFlag, CompilationOptions, CpuFamily}
|
||||
import millfork.assembly.mos.Opcode
|
||||
import millfork.node._
|
||||
|
||||
@ -66,7 +67,7 @@ sealed trait BooleanType extends Type {
|
||||
|
||||
case class ConstantBooleanType(name: String, value: Boolean) extends BooleanType
|
||||
|
||||
case class FlagBooleanType(name: String, jumpIfTrue: Opcode.Value, jumpIfFalse: Opcode.Value) extends BooleanType
|
||||
case class FlagBooleanType(name: String, jumpIfTrue: BranchingOpcodeMapping, jumpIfFalse: BranchingOpcodeMapping) extends BooleanType
|
||||
|
||||
case object BuiltInBooleanType extends BooleanType {
|
||||
override def name = "bool$"
|
||||
@ -294,7 +295,13 @@ sealed trait ParamPassingConvention {
|
||||
def inNonInlinedOnly: Boolean
|
||||
}
|
||||
|
||||
case class ByRegister(register: MosRegister.Value) extends ParamPassingConvention {
|
||||
case class ByMosRegister(register: MosRegister.Value) extends ParamPassingConvention {
|
||||
override def inInlinedOnly = false
|
||||
|
||||
override def inNonInlinedOnly = false
|
||||
}
|
||||
|
||||
case class ByZRegister(register: ZRegister.Value) extends ParamPassingConvention {
|
||||
override def inInlinedOnly = false
|
||||
|
||||
override def inNonInlinedOnly = false
|
||||
|
@ -1,7 +1,6 @@
|
||||
package millfork.node
|
||||
|
||||
import millfork.assembly.AddrMode
|
||||
import millfork.assembly.mos.Opcode
|
||||
import millfork.assembly.mos.{AddrMode, Opcode}
|
||||
import millfork.env.{Constant, ParamPassingConvention}
|
||||
|
||||
case class Position(filename: String, line: Int, column: Int, cursor: Int)
|
||||
@ -69,6 +68,26 @@ object MosRegister extends Enumeration {
|
||||
val A, X, Y, AX, AY, YA, XA, XY, YX, AW = Value
|
||||
}
|
||||
|
||||
object ZRegister extends Enumeration {
|
||||
|
||||
val A, B, C, D, E, H, L, AF, BC, HL, DE, SP, IXH, IXL, IYH, IYL, IX, IY, R, I, MEM_HL, MEM_BC, MEM_DE, MEM_IX_D, MEM_IY_D, MEM_ABS_8, MEM_ABS_16, IMM_8, IMM_16 = Value
|
||||
|
||||
def size(reg: Value) = reg match {
|
||||
case AF | BC | DE | HL | IX | IY | IMM_16 => 2
|
||||
case A | B | C | D | E | H | L | IXH | IXL | IYH | IYL | R | I | IMM_8 => 1
|
||||
}
|
||||
|
||||
def matchingImm(target: ZRegister.Value): ZRegister.Value = size(target) match {
|
||||
case 1 => IMM_8
|
||||
case 2 => IMM_16
|
||||
}
|
||||
|
||||
def matchingMemAbs(target: ZRegister.Value): ZRegister.Value = size(target) match {
|
||||
case 1 => MEM_ABS_8
|
||||
case 2 => MEM_ABS_16
|
||||
}
|
||||
}
|
||||
|
||||
//case class Indexing(child: Expression, register: Register.Value) extends Expression
|
||||
|
||||
case class VariableExpression(name: String) extends LhsExpression {
|
||||
|
@ -1,12 +1,11 @@
|
||||
package millfork.output
|
||||
|
||||
import millfork.assembly._
|
||||
import millfork.compiler.AbstractCompiler
|
||||
import millfork.compiler.{AbstractCompiler, CompilationContext}
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.{CallGraph, Program}
|
||||
import millfork._
|
||||
import millfork.compiler.mos.CompilationContext
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
|
@ -6,7 +6,7 @@ import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.Program
|
||||
import millfork._
|
||||
import millfork.assembly.mos.{AssemblyLine, Opcode}
|
||||
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode}
|
||||
import millfork.compiler.mos.MosCompiler
|
||||
|
||||
import scala.collection.mutable
|
||||
@ -28,7 +28,7 @@ class MosAssembler(program: Program,
|
||||
}
|
||||
|
||||
override def emitInstruction(bank: String, options: CompilationOptions, index: Int, instr: AssemblyLine): Int = {
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
instr match {
|
||||
case AssemblyLine(BYTE, RawByte, c, _) =>
|
||||
|
@ -1,8 +1,7 @@
|
||||
package millfork.output
|
||||
|
||||
import millfork.assembly.AddrMode
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.assembly.mos.{AddrMode, _}
|
||||
import millfork.compiler.AbstractCompiler
|
||||
import millfork.env._
|
||||
import millfork.node._
|
||||
|
24
src/main/scala/millfork/output/Z80Assembler.scala
Normal file
24
src/main/scala/millfork/output/Z80Assembler.scala
Normal file
@ -0,0 +1,24 @@
|
||||
package millfork.output
|
||||
|
||||
import millfork.{CompilationOptions, Platform}
|
||||
import millfork.assembly.z80.ZLine
|
||||
import millfork.compiler.z80.Z80Compiler
|
||||
import millfork.env.{Environment, NormalFunction}
|
||||
import millfork.node.Program
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
class Z80Assembler(program: Program,
|
||||
rootEnv: Environment,
|
||||
platform: Platform) extends AbstractAssembler[ZLine](program, rootEnv, platform, Z80InliningCalculator, Z80Compiler) {
|
||||
override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[ZLine]): List[ZLine] = code
|
||||
|
||||
override def emitInstruction(bank: String, options: CompilationOptions, index: Int, instr: ZLine): Int = {
|
||||
// TODO
|
||||
index
|
||||
}
|
||||
}
|
||||
object Z80Assembler {
|
||||
|
||||
}
|
16
src/main/scala/millfork/output/Z80InliningCalculator.scala
Normal file
16
src/main/scala/millfork/output/Z80InliningCalculator.scala
Normal file
@ -0,0 +1,16 @@
|
||||
package millfork.output
|
||||
|
||||
import millfork.assembly.z80.ZLine
|
||||
import millfork.compiler.AbstractCompiler
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object Z80InliningCalculator extends AbstractInliningCalculator[ZLine] {
|
||||
|
||||
// TODO
|
||||
|
||||
override def codeForInlining(fname: String, functionsAlreadyKnownToBeNonInlineable: Set[String], code: List[ZLine]): Option[List[ZLine]] = None
|
||||
|
||||
override def inline(code: List[ZLine], inlinedFunctions: Map[String, List[ZLine]], compiler: AbstractCompiler[ZLine]): List[ZLine] = code
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
package millfork.parser
|
||||
|
||||
import fastparse.all._
|
||||
import millfork.assembly.AddrMode
|
||||
import millfork.assembly.mos.Opcode
|
||||
import millfork.assembly.mos.AssemblyLine
|
||||
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode}
|
||||
import millfork.env._
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node._
|
||||
@ -76,15 +74,15 @@ case class MosParser(filename: String, input: String, currentDirectory: String,
|
||||
|
||||
|
||||
val appcSimple: P[ParamPassingConvention] = P("xy" | "yx" | "ax" | "ay" | "xa" | "ya" | "stack" | "a" | "x" | "y").!.map {
|
||||
case "xy" => ByRegister(MosRegister.XY)
|
||||
case "yx" => ByRegister(MosRegister.YX)
|
||||
case "ax" => ByRegister(MosRegister.AX)
|
||||
case "ay" => ByRegister(MosRegister.AY)
|
||||
case "xa" => ByRegister(MosRegister.XA)
|
||||
case "ya" => ByRegister(MosRegister.YA)
|
||||
case "a" => ByRegister(MosRegister.A)
|
||||
case "x" => ByRegister(MosRegister.X)
|
||||
case "y" => ByRegister(MosRegister.Y)
|
||||
case "xy" => ByMosRegister(MosRegister.XY)
|
||||
case "yx" => ByMosRegister(MosRegister.YX)
|
||||
case "ax" => ByMosRegister(MosRegister.AX)
|
||||
case "ay" => ByMosRegister(MosRegister.AY)
|
||||
case "xa" => ByMosRegister(MosRegister.XA)
|
||||
case "ya" => ByMosRegister(MosRegister.YA)
|
||||
case "a" => ByMosRegister(MosRegister.A)
|
||||
case "x" => ByMosRegister(MosRegister.X)
|
||||
case "y" => ByMosRegister(MosRegister.Y)
|
||||
case x => ErrorReporting.fatal(s"Unknown assembly parameter passing convention: `$x`")
|
||||
}
|
||||
|
||||
|
17
src/main/scala/millfork/parser/Z80Parser.scala
Normal file
17
src/main/scala/millfork/parser/Z80Parser.scala
Normal file
@ -0,0 +1,17 @@
|
||||
package millfork.parser
|
||||
|
||||
import fastparse.all
|
||||
import millfork.CompilationOptions
|
||||
import millfork.assembly.z80.ZLine
|
||||
import millfork.node.{ExecutableStatement, ParameterDeclaration, Position, Statement}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
case class Z80Parser(filename: String, input: String, currentDirectory: String, options: CompilationOptions) extends MfParser[ZLine](filename, input, currentDirectory, options) {
|
||||
override def asmParamDefinition: all.P[ParameterDeclaration] = ???
|
||||
|
||||
override def asmStatement: all.P[ExecutableStatement] = ???
|
||||
|
||||
override def validateAsmFunctionBody(p: Position, flags: Set[String], name: String, statements: Option[List[Statement]]): Unit = ???
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.{Cpu, OptimizationPresets}
|
||||
import millfork.{Cpu, CpuFamily, OptimizationPresets}
|
||||
import millfork.assembly.mos.opt.{AlwaysGoodOptimizations, DangerousOptimizations}
|
||||
import millfork.test.emu._
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
@ -100,7 +100,7 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Array simple read") {
|
||||
EmuBenchmarkRun(
|
||||
EmuCrossPlatformBenchmarkRun(CpuFamily.M6502, CpuFamily.I80)(
|
||||
"""
|
||||
| byte output @$c000
|
||||
| array a[7]
|
||||
@ -114,7 +114,7 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Array simple read 2") {
|
||||
EmuBenchmarkRun(
|
||||
EmuCrossPlatformBenchmarkRun(CpuFamily.M6502, CpuFamily.I80)(
|
||||
"""
|
||||
| word output @$c000
|
||||
| array a[7]
|
||||
@ -132,7 +132,7 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Pointers") {
|
||||
EmuBenchmarkRun(
|
||||
EmuCrossPlatformBenchmarkRun(CpuFamily.M6502, CpuFamily.I80)(
|
||||
"""
|
||||
| byte output
|
||||
| pointer a
|
||||
@ -157,7 +157,7 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Pointer indexing test") {
|
||||
EmuBenchmarkRun(
|
||||
EmuCrossPlatformBenchmarkRun(CpuFamily.M6502, CpuFamily.I80)(
|
||||
"""
|
||||
| array output [4] @$c000
|
||||
| pointer a
|
||||
@ -174,19 +174,19 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Syntax") {
|
||||
EmuUnoptimizedRun(
|
||||
EmuUnoptimizedCrossPlatformRun(CpuFamily.M6502, CpuFamily.I80)(
|
||||
"""
|
||||
| array a = [1, 2, 3]
|
||||
| array b = "text" ascii
|
||||
| array c = ["text" ascii, 5]
|
||||
| void main () {
|
||||
| }
|
||||
""".stripMargin)
|
||||
""".stripMargin){m => ()}
|
||||
|
||||
}
|
||||
|
||||
test("Negative subindex") {
|
||||
val m = EmuUnoptimizedRun(
|
||||
EmuUnoptimizedCrossPlatformRun(CpuFamily.M6502, CpuFamily.I80)(
|
||||
"""
|
||||
|
|
||||
| array output [$fff] @$c000
|
||||
@ -197,10 +197,10 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
| output[1 - i] = 5
|
||||
| }
|
||||
| noinline byte one() {return 1}
|
||||
""".stripMargin)
|
||||
m.readByte(0xc100) should equal(55)
|
||||
m.readByte(0xc000) should equal(5)
|
||||
|
||||
""".stripMargin) { m =>
|
||||
m.readByte(0xc100) should equal(55)
|
||||
m.readByte(0xc000) should equal(5)
|
||||
}
|
||||
}
|
||||
|
||||
test("Word subindex 1") {
|
||||
@ -224,7 +224,7 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Word subindex 2") {
|
||||
EmuBenchmarkRun(
|
||||
EmuCrossPlatformBenchmarkRun(CpuFamily.M6502, CpuFamily.I80)(
|
||||
"""
|
||||
|
|
||||
| array output [$fff] @$c000
|
||||
|
@ -1,6 +1,7 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.test.emu.EmuUnoptimizedRun
|
||||
import millfork.CpuFamily
|
||||
import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedRun}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
@ -8,12 +9,14 @@ import org.scalatest.{FunSuite, Matchers}
|
||||
*/
|
||||
class BasicSymonTest extends FunSuite with Matchers {
|
||||
test("Empty test") {
|
||||
EmuUnoptimizedRun(
|
||||
EmuUnoptimizedCrossPlatformRun(CpuFamily.M6502, CpuFamily.I80)(
|
||||
"""
|
||||
| void main () {
|
||||
|
|
||||
| }
|
||||
""".stripMargin)
|
||||
""".stripMargin) {
|
||||
m => ()
|
||||
}
|
||||
}
|
||||
|
||||
test("Panic test") {
|
||||
@ -81,13 +84,15 @@ class BasicSymonTest extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Byte assignment") {
|
||||
EmuUnoptimizedRun(
|
||||
EmuUnoptimizedCrossPlatformRun(CpuFamily.M6502, CpuFamily.I80)(
|
||||
"""
|
||||
| byte output @$c000
|
||||
| void main () {
|
||||
| output = (1)
|
||||
| }
|
||||
""".stripMargin).readByte(0xc000) should equal(1)
|
||||
""".stripMargin) { m =>
|
||||
m.readByte(0xc000) should equal(1)
|
||||
}
|
||||
}
|
||||
|
||||
test("Preallocated variables") {
|
||||
@ -101,20 +106,21 @@ class BasicSymonTest extends FunSuite with Matchers {
|
||||
| output[1] = number
|
||||
| }
|
||||
""".stripMargin)
|
||||
m.readByte(0xc000) should equal(4)
|
||||
m.readByte(0xc001) should equal(5)
|
||||
m.readByte(0xc000) should equal(4)
|
||||
m.readByte(0xc001) should equal(5)
|
||||
}
|
||||
|
||||
test("Preallocated variables 2") {
|
||||
val m = EmuUnoptimizedRun(
|
||||
EmuUnoptimizedCrossPlatformRun(CpuFamily.M6502, CpuFamily.I80)(
|
||||
"""
|
||||
| word output @$c000
|
||||
| word number = 344
|
||||
| void main () {
|
||||
| output = number
|
||||
| }
|
||||
""".stripMargin)
|
||||
""".stripMargin) { m =>
|
||||
m.readWord(0xc000) should equal(344)
|
||||
}
|
||||
}
|
||||
|
||||
test("Else if") {
|
||||
@ -135,12 +141,12 @@ class BasicSymonTest extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Segment syntax") {
|
||||
EmuUnoptimizedRun(
|
||||
EmuUnoptimizedCrossPlatformRun(CpuFamily.M6502, CpuFamily.I80)(
|
||||
"""
|
||||
| segment(default)byte output @$c000
|
||||
| segment(default)array x[3]
|
||||
| segment(default)void main () {
|
||||
| }
|
||||
""".stripMargin)
|
||||
""".stripMargin){ m => () }
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuOptimizedRun, EmuUnoptimizedRun}
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuUnoptimizedRun}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,7 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuUltraBenchmarkRun}
|
||||
import millfork.CpuFamily
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuCrossPlatformBenchmarkRun, EmuUltraBenchmarkRun}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
@ -80,7 +81,7 @@ class ByteMathSuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Parameter order") {
|
||||
EmuBenchmarkRun(
|
||||
EmuCrossPlatformBenchmarkRun(CpuFamily.M6502, CpuFamily.I80)(
|
||||
"""
|
||||
| byte output @$c000
|
||||
| array arr[6]
|
||||
|
@ -1,6 +1,7 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.test.emu.EmuBenchmarkRun
|
||||
import millfork.CpuFamily
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuCrossPlatformBenchmarkRun}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
@ -9,7 +10,7 @@ import org.scalatest.{FunSuite, Matchers}
|
||||
class MacroSuite extends FunSuite with Matchers {
|
||||
|
||||
test("Most basic test") {
|
||||
EmuBenchmarkRun(
|
||||
EmuCrossPlatformBenchmarkRun(CpuFamily.M6502, CpuFamily.I80)(
|
||||
"""
|
||||
| macro void run(byte x) {
|
||||
| output = x
|
||||
|
@ -1,6 +1,7 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuUnoptimizedRun}
|
||||
import millfork.CpuFamily
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuUnoptimizedCrossPlatformRun, EmuUnoptimizedRun}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
@ -9,14 +10,16 @@ import org.scalatest.{FunSuite, Matchers}
|
||||
class SignExtensionSuite extends FunSuite with Matchers {
|
||||
|
||||
test("Sbyte to Word") {
|
||||
EmuUnoptimizedRun("""
|
||||
EmuUnoptimizedCrossPlatformRun(CpuFamily.M6502, CpuFamily.I80)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| sbyte b
|
||||
| b = -1
|
||||
| output = b
|
||||
| }
|
||||
""".stripMargin).readWord(0xc000) should equal(0xffff)
|
||||
""".stripMargin){m =>
|
||||
m.readWord(0xc000) should equal(0xffff)
|
||||
}
|
||||
}
|
||||
test("Sbyte to Word 2") {
|
||||
EmuUnoptimizedRun("""
|
||||
|
@ -1,6 +1,7 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.test.emu.EmuBenchmarkRun
|
||||
import millfork.CpuFamily
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuCrossPlatformBenchmarkRun}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
@ -9,7 +10,7 @@ import org.scalatest.{FunSuite, Matchers}
|
||||
class TypeSuite extends FunSuite with Matchers {
|
||||
|
||||
test("Word to word") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuCrossPlatformBenchmarkRun(CpuFamily.M6502, CpuFamily.I80)("""
|
||||
| word output @$c000
|
||||
| void main () {
|
||||
| output = word(0x203)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package millfork.test.emu
|
||||
|
||||
import millfork.CpuFamily
|
||||
import millfork.output.MemoryBank
|
||||
|
||||
/**
|
||||
@ -23,3 +24,15 @@ object EmuBenchmarkRun {
|
||||
verifier(m2)
|
||||
}
|
||||
}
|
||||
|
||||
object EmuCrossPlatformBenchmarkRun {
|
||||
def apply(platforms: CpuFamily.Value*)(source: String)(verifier: MemoryBank => Unit): Unit = {
|
||||
if (platforms.contains(CpuFamily.M6502)) {
|
||||
EmuBenchmarkRun.apply(source)(verifier)
|
||||
}
|
||||
if (platforms.contains(CpuFamily.I80)) {
|
||||
EmuUnoptimizedZ80Run.apply2(source)
|
||||
EmuOptimizedZ80Run.apply2(source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package millfork.test.emu
|
||||
|
||||
import millfork.assembly.mos.opt.{LaterOptimizations, ZeropageRegisterOptimizations}
|
||||
import millfork.assembly.z80.opt.{AlwaysGoodZ80Optimizations, Z80OptimizationPresets}
|
||||
import millfork.{Cpu, OptimizationPresets}
|
||||
|
||||
/**
|
||||
@ -18,3 +19,4 @@ object EmuOptimizedRun extends EmuRun(
|
||||
|
||||
|
||||
|
||||
object EmuOptimizedZ80Run extends EmuZ80Run(Cpu.Z80, OptimizationPresets.NodeOpt, Z80OptimizationPresets.Good)
|
@ -9,7 +9,8 @@ import com.loomcom.symon.{Bus, Cpu, CpuState}
|
||||
import fastparse.core.Parsed.{Failure, Success}
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.mos.AssemblyLine
|
||||
import millfork.compiler.mos.{CompilationContext, MosCompiler}
|
||||
import millfork.compiler.CompilationContext
|
||||
import millfork.compiler.mos.MosCompiler
|
||||
import millfork.env.{Environment, InitializedArray, InitializedMemoryVariable, NormalFunction}
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.StandardCallGraph
|
||||
|
@ -0,0 +1,18 @@
|
||||
package millfork.test.emu
|
||||
|
||||
import millfork.CpuFamily
|
||||
import millfork.output.MemoryBank
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object EmuUnoptimizedCrossPlatformRun {
|
||||
def apply(platforms: CpuFamily.Value*)(source: String)(verifier: MemoryBank => Unit): Unit = {
|
||||
val (_, mm) = if (platforms.contains(CpuFamily.M6502)) EmuUnoptimizedRun.apply2(source) else Timings(-1, -1) -> null
|
||||
val (_, mz) = if (platforms.contains(CpuFamily.I80)) EmuUnoptimizedZ80Run.apply2(source) else Timings(-1, -1) -> null
|
||||
if (platforms.contains(CpuFamily.M6502)) {
|
||||
println(f"Running MOS")
|
||||
verifier(mm)
|
||||
}
|
||||
}
|
||||
}
|
@ -6,4 +6,6 @@ import millfork.Cpu
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object EmuUnoptimizedRun extends EmuRun(Cpu.StrictMos, Nil, Nil)
|
||||
object EmuUnoptimizedRun extends EmuRun(Cpu.StrictMos, Nil, Nil)
|
||||
|
||||
object EmuUnoptimizedZ80Run extends EmuZ80Run(Cpu.Z80, Nil, Nil)
|
105
src/test/scala/millfork/test/emu/EmuZ80Run.scala
Normal file
105
src/test/scala/millfork/test/emu/EmuZ80Run.scala
Normal file
@ -0,0 +1,105 @@
|
||||
package millfork.test.emu
|
||||
|
||||
import fastparse.core.Parsed.{Failure, Success}
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.z80.ZLine
|
||||
import millfork.compiler.CompilationContext
|
||||
import millfork.compiler.mos.MosCompiler
|
||||
import millfork.env.{Environment, InitializedArray, InitializedMemoryVariable, NormalFunction}
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.StandardCallGraph
|
||||
import millfork.node.opt.NodeOptimization
|
||||
import millfork.output.{MemoryBank, MosAssembler, Z80Assembler}
|
||||
import millfork.parser.Z80Parser
|
||||
import millfork.CompilationOptions
|
||||
import millfork.compiler.z80.Z80Compiler
|
||||
import org.scalatest.Matchers
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
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) = {
|
||||
Console.out.flush()
|
||||
Console.err.flush()
|
||||
println(source)
|
||||
val platform = EmuPlatform.get(cpu)
|
||||
val options = CompilationOptions(platform, millfork.Cpu.defaultFlags(cpu).map(_ -> true).toMap)
|
||||
ErrorReporting.hasErrors = false
|
||||
ErrorReporting.verbosity = 999
|
||||
var effectiveSource = source
|
||||
if (!source.contains("_panic")) effectiveSource += "\n void _panic(){while(true){}}"
|
||||
val parserF = Z80Parser("", effectiveSource, "", options)
|
||||
parserF.toAst match {
|
||||
case Success(unoptimized, _) =>
|
||||
ErrorReporting.assertNoErrors("Parse failed")
|
||||
|
||||
|
||||
// prepare
|
||||
val program = nodeOptimizations.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
|
||||
val callGraph = new StandardCallGraph(program)
|
||||
val env = new Environment(None, "")
|
||||
env.collectDeclarations(program, options)
|
||||
|
||||
val hasOptimizations = assemblyOptimizations.nonEmpty
|
||||
var unoptimizedSize = 0L
|
||||
// print unoptimized asm
|
||||
env.allPreallocatables.foreach {
|
||||
case f: NormalFunction =>
|
||||
val unoptimized = Z80Compiler.compile(CompilationContext(f.environment, f, 0, options))
|
||||
unoptimizedSize += unoptimized.map(_.sizeInBytes).sum
|
||||
case d: InitializedArray =>
|
||||
unoptimizedSize += d.contents.length
|
||||
case d: InitializedMemoryVariable =>
|
||||
unoptimizedSize += d.typ.size
|
||||
}
|
||||
|
||||
ErrorReporting.assertNoErrors("Compile failed")
|
||||
|
||||
|
||||
// compile
|
||||
val env2 = new Environment(None, "")
|
||||
env2.collectDeclarations(program, options)
|
||||
val assembler = new Z80Assembler(program, env2, platform)
|
||||
val output = assembler.assemble(callGraph, assemblyOptimizations, options)
|
||||
println(";;; compiled: -----------------")
|
||||
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println)
|
||||
println(";;; ---------------------------")
|
||||
assembler.labelMap.foreach { case (l, addr) => println(f"$l%-15s $$$addr%04x") }
|
||||
|
||||
val optimizedSize = assembler.mem.banks("default").initialized.count(identity).toLong
|
||||
if (unoptimizedSize == optimizedSize) {
|
||||
println(f"Size: $unoptimizedSize%5d B")
|
||||
} else {
|
||||
println(f"Unoptimized size: $unoptimizedSize%5d B")
|
||||
println(f"Optimized size: $optimizedSize%5d B")
|
||||
println(f"Gain: ${(100L * (unoptimizedSize - optimizedSize) / unoptimizedSize.toDouble).round}%5d%%")
|
||||
}
|
||||
|
||||
ErrorReporting.assertNoErrors("Code generation failed")
|
||||
|
||||
val memoryBank = assembler.mem.banks("default")
|
||||
if (source.contains("return [")) {
|
||||
for (_ <- 0 until 10; i <- 0xfffe.to(0, -1)) {
|
||||
if (memoryBank.readable(i)) memoryBank.readable(i + 1) = true
|
||||
}
|
||||
}
|
||||
platform.cpu match {
|
||||
case _ =>
|
||||
Timings(-1, -1) -> memoryBank
|
||||
}
|
||||
case f: Failure[_, _] =>
|
||||
println(f)
|
||||
println(f.extra.toString)
|
||||
println(f.lastParser.toString)
|
||||
ErrorReporting.error("Syntax error", Some(parserF.lastPosition))
|
||||
???
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user