mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-11 12:29:46 +00:00
Fix stack variables on 8080 and LR35902
This commit is contained in:
parent
d0f64f2cee
commit
629691dfb3
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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 _ => ???
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user