1
0
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:
Karol Stasiak 2018-06-17 02:01:35 +02:00
parent 780bfa3428
commit 0f453e2d2c
81 changed files with 4118 additions and 856 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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._
/**

View File

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

View File

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

View File

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

View File

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

View File

@ -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._
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

View File

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

View File

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

View 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"
}

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,6 @@
package millfork.compiler
import millfork.assembly.AbstractCode
import millfork.compiler.mos.CompilationContext
/**
* @author Karol Stasiak

View File

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

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

View File

@ -1,4 +1,4 @@
package millfork.compiler.mos
package millfork.compiler
/**
* @author Karol Stasiak

View File

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

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

View File

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

View File

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

View File

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

View File

@ -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._
/**

View File

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

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

View File

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

View File

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

View File

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

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

View 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) => ???
}
}
}

View 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]) = ???
}

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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 {
}

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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))
???
}
}
}