1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-10-03 20:55:36 +00:00

Fix stack variables on 8080 and LR35902

This commit is contained in:
Karol Stasiak 2019-04-16 12:09:14 +02:00
parent d0f64f2cee
commit 629691dfb3
6 changed files with 108 additions and 25 deletions

View File

@ -173,7 +173,8 @@ object AlwaysGoodI80Optimizations {
for7Registers(register => for7Registers(register =>
(Elidable & Is8BitLoadTo(register) & NoOffset & MatchSourceRegisterAndOffset(1)) ~ (Elidable & Is8BitLoadTo(register) & NoOffset & MatchSourceRegisterAndOffset(1)) ~
(Linear & Not(Concerns(register)) & DoesntChangeMatchedRegisterAndOffset(1)).* ~ (Linear & Not(Concerns(register)) & DoesntChangeMatchedRegisterAndOffset(1)).* ~
(Elidable & IsRegular8BitLoadFrom(register) & DoesntMatterWhatItDoesWith(register)) ~~> { code => (Elidable & IsRegular8BitLoadFrom(register) & DoesntMatterWhatItDoesWith(register) & MatchTargetRegisterAndOffset(2)) ~
Where(ctx => ctx.areCompatibleForLoad(2, 1)) ~~> { code =>
val last = code.last val last = code.last
val head = code.head val head = code.head
code.tail.init :+ ZLine(LD, (last.registers, head.registers) match { code.tail.init :+ ZLine(LD, (last.registers, head.registers) match {

View File

@ -100,7 +100,7 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions) {
def log: Logger = compilationOptions.log def log: Logger = compilationOptions.log
override def toString: String = map.mkString(", ") override def toString: String = if (map.isEmpty) "(empty context)" else map.mkString(", ")
def addObject(i: Int, o: Any): Boolean = { def addObject(i: Int, o: Any): Boolean = {
if (map.contains(i)) { if (map.contains(i)) {
@ -180,6 +180,21 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions) {
jumps.isEmpty jumps.isEmpty
} }
def areCompatibleForLoad(target: Int, source: Int): Boolean = {
val t = get[RegisterAndOffset](target).register
val s = get[RegisterAndOffset](source).register
import ZRegister._
if (t == A || s == A) return true
if (t == MEM_DE || s == MEM_DE || t == MEM_BC || s == MEM_BC) return false
if (t == B || t == C || t == D || t == E) return true
if (s == B || s == C || s == D || s == E) return true
if ((t == IXH || t == IXL) && (s == IXH || s == IXL)) return true
if ((t == IYH || t == IYL) && (s == IYH || s == IYL)) return true
if ((t == H || t == L) && (s == MEM_HL || s == MEM_IX_D || s == MEM_IY_D)) return true
if ((s == H || s == L) && (t == MEM_HL || t == MEM_IX_D || t == MEM_IY_D)) return true
false
}
} }
object HelperCheckers { object HelperCheckers {

View File

@ -1410,8 +1410,37 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
case VariableExpression(vname) => case VariableExpression(vname) =>
env.get[Variable](vname) match { env.get[Variable](vname) match {
case v: Variable => case v: Variable =>
import ZRegister._
val size = v.typ.size val size = v.typ.size
compileByteReads(ctx, source, size, ZExpressionTarget.HL).zip(compileByteStores(ctx, target, size, includeStep = false)).flatMap(t => t._1 ++ t._2) val reads = compileByteReads(ctx, source, size, ZExpressionTarget.HL)
val stores = compileByteStores(ctx, target, size, includeStep = true)
if (stores.exists(_.exists(_.changesRegister(HL)))) {
if (reads.tail.exists(_.exists(_.changesRegister(HL)))) {
// most likely stack-to-stack copy
// use DE as the secondary pointer
val fixedReads = reads.head.init ++ List(ZLine.ld8(E, L), ZLine.ld8(D, H), ZLine.ld8(A, MEM_DE)) :: reads.tail.map(_.map {
case l@ZLine0(LD, TwoRegisters(A, MEM_HL), _) => l.copy(registers = TwoRegisters(A, MEM_DE))
case l@ZLine0(INC_16, OneRegister(HL), _) => l.copy(registers = OneRegister(DE))
case l@ZLine0(DEC_16, OneRegister(HL), _) => l.copy(registers = OneRegister(DE))
case l => l
})
fixedReads.zip(stores).flatMap(t => t._1 ++ t._2)
} else {
val fixedReads = reads.head ++ List(ZLine.ld8(B, H)) :: reads.tail.map(_.map {
case l@ZLine0(LD, TwoRegisters(reg, H), _) => l.copy(registers = TwoRegisters(reg, B))
case l@ZLine0(LD, TwoRegisters(reg, L), _) => l.copy(registers = TwoRegisters(reg, C))
case l => l
})
val fixedStores = stores.map(_.map {
case l@ZLine0(LD, TwoRegisters(H, reg), _) => l.copy(registers = TwoRegisters(B, reg))
case l@ZLine0(LD, TwoRegisters(L, reg), _) => l.copy(registers = TwoRegisters(C, reg))
case l => l
})
fixedReads.zip(fixedStores).flatMap(t => t._1 ++ t._2)
}
} else {
reads.zip(stores).flatMap(t => t._1 ++ t._2)
}
} }
case _ => ??? case _ => ???
} }

View File

@ -1,7 +1,7 @@
package millfork.test package millfork.test
import millfork.Cpu import millfork.Cpu
import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuOptimizedSoftwareStackRun, EmuSoftwareStackBenchmarkRun, EmuUnoptimizedZ80Run} import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuSoftwareStackBenchmarkRun, EmuUnoptimizedCrossPlatformRun}
import org.scalatest.{FunSuite, Matchers} import org.scalatest.{FunSuite, Matchers}
/** /**
@ -69,24 +69,26 @@ class StackVarSuite extends FunSuite with Matchers {
""".stripMargin)(_.readWord(0xc000) should equal(21)) """.stripMargin)(_.readWord(0xc000) should equal(21))
} }
// ERROR: (8:9) Right-hand-side expression is too complex test("Stack byte subtraction") {
// test("Stack byte subtraction") { EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080)(
// EmuUnoptimizedRun(""" """
// | byte output @$c000 | byte output @$c000
// | void main () { | void main () {
// | stack byte a | stack byte a
// | stack byte b | stack byte b
// | b = $77 | b = $77
// | a = $11 | a = $11
// | b -= zzz() | b -= zzz()
// | b -= a | b -= a
// | output = b | output = b
// | } | }
// | byte zzz() { | byte zzz() {
// | return $22 | return $22
// | } | }
// """.stripMargin).readByte(0xc000) should equal(0x44) """.stripMargin) { m =>
// } m.readByte(0xc000) should equal(0x44)
}
}
test("Stack word addition") { test("Stack word addition") {
EmuCrossPlatformBenchmarkRun(Cpu.StrictMos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(""" EmuCrossPlatformBenchmarkRun(Cpu.StrictMos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)("""
@ -251,4 +253,41 @@ class StackVarSuite extends FunSuite with Matchers {
m.readByte(0xc003) should equal(2) m.readByte(0xc003) should equal(2)
} }
} }
test("Large stack storage") {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
"""
| int32 output @$c000
| void main () {
| stack int32 a
| a = f()
| barrier()
| output = a
| }
| noinline int32 f() = 400
| noinline void barrier(){}
""".stripMargin) { m =>
m.readLong(0xc000) should equal(400)
}
}
test("Large stack-to-stack transfer") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
"""
| int32 output @$c000
| void main () {
| stack int32 a
| stack int32 b
| a = f()
| barrier()
| b = a
| barrier()
| output = b
| }
| noinline int32 f() = 400
| noinline void barrier(){}
""".stripMargin) { m =>
m.readLong(0xc000) should equal(400)
}
}
} }

View File

@ -10,8 +10,7 @@ import org.scalatest.{FunSuite, Matchers}
class StructSuite extends FunSuite with Matchers { class StructSuite extends FunSuite with Matchers {
test("Basic struct support") { test("Basic struct support") {
// TODO: 8080 has broken stack operations, fix and uncomment! EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080)("""
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80/*, Cpu.Intel8080*/)("""
| struct point { | struct point {
| byte x | byte x
| byte y | byte y

View File

@ -8,7 +8,7 @@ import millfork.output.MemoryBank
*/ */
object EmuUnoptimizedCrossPlatformRun { object EmuUnoptimizedCrossPlatformRun {
def apply(platforms: Cpu.Value*)(source: String)(verifier: MemoryBank => Unit): Unit = { def apply(platforms: Cpu.Value*)(source: String)(verifier: MemoryBank => Unit): Unit = {
val (_, mm) = if (platforms.contains(Cpu.Mos)) EmuUnoptimizedRun.apply2(source) else Timings(-1, -1) -> null val (_, mm) = if (platforms.contains(Cpu.Mos) || platforms.contains(Cpu.StrictMos)) EmuUnoptimizedRun.apply2(source) else Timings(-1, -1) -> null
val (_, mc) = if (platforms.contains(Cpu.Cmos)) EmuUnoptimizedCmosRun.apply2(source) else Timings(-1, -1) -> null val (_, mc) = if (platforms.contains(Cpu.Cmos)) EmuUnoptimizedCmosRun.apply2(source) else Timings(-1, -1) -> null
val (_, mn) = if (platforms.contains(Cpu.Ricoh)) EmuUnoptimizedRicohRun.apply2(source) else Timings(-1, -1) -> null val (_, mn) = if (platforms.contains(Cpu.Ricoh)) EmuUnoptimizedRicohRun.apply2(source) else Timings(-1, -1) -> null
val (_, mz) = if (platforms.contains(Cpu.Z80)) EmuUnoptimizedZ80Run.apply2(source) else Timings(-1, -1) -> null val (_, mz) = if (platforms.contains(Cpu.Z80)) EmuUnoptimizedZ80Run.apply2(source) else Timings(-1, -1) -> null