1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-05-29 04:41:30 +00:00

Added Z80 emulator for testing

This commit is contained in:
Karol Stasiak 2018-07-01 22:28:27 +02:00
parent dc3425f64e
commit 4d00cb4db9
6 changed files with 90 additions and 6 deletions

View File

@ -12,6 +12,8 @@ libraryDependencies += "org.apache.commons" % "commons-configuration2" % "2.2"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.4" % "test"
libraryDependencies += "com.codingrodent.microprocessor" % "Z80Processor" % "2.0.2" % "test"
// these two are not in Maven Central or any other public repo
// get them from the following links or just build millfork without tests:
// https://github.com/sethm/symon/tree/71905fdb1998ee4f142260879504bc46cf27648f

View File

@ -0,0 +1,10 @@
package millfork.test.emu
import com.codingrodent.microprocessor.IBaseDevice
/**
* @author Karol Stasiak
*/
object DummyIO extends IBaseDevice {
}

View File

@ -25,14 +25,27 @@ object EmuBenchmarkRun {
}
}
object EmuI80BenchmarkRun {
def apply(source: String)(verifier: MemoryBank => Unit) = {
val (Timings(t0, _), m0) = EmuUnoptimizedZ80Run.apply2(source)
val (Timings(t1, _), m1) = EmuOptimizedZ80Run.apply2(source)
println(f"Before optimization: $t0%7d")
println(f"After optimization: $t1%7d")
println(f"Gain: ${(100L * (t0 - t1) / t0.toDouble).round}%7d%%")
println(f"Running unoptimized")
verifier(m0)
println(f"Running optimized")
verifier(m1)
}
}
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)
EmuI80BenchmarkRun.apply(source)(verifier)
}
}
}

View File

@ -14,5 +14,9 @@ object EmuUnoptimizedCrossPlatformRun {
println(f"Running MOS")
verifier(mm)
}
if (platforms.contains(CpuFamily.I80)) {
println(f"Running Z80")
verifier(mz)
}
}
}

View File

@ -1,5 +1,6 @@
package millfork.test.emu
import com.codingrodent.microprocessor.Z80.{CPUConstants, Z80Core}
import fastparse.core.Parsed.{Failure, Success}
import millfork.assembly.AssemblyOptimization
import millfork.assembly.z80.ZLine
@ -84,12 +85,34 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
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
}
(0x1f0 until 0x200).foreach(i => memoryBank.readable(i) = true)
(0xff00 to 0xffff).foreach{i =>
memoryBank.readable(i) = true
memoryBank.writeable(i) = true
}
// CALL $0200
// HALT
memoryBank.output(0x1f0) = 0xCD.toByte
memoryBank.output(0x1f1) = 0
memoryBank.output(0x1f2) = 2
memoryBank.output(0x1f3) = 0x76.toByte
(0x200 until 0x2000).takeWhile(memoryBank.occupied(_)).map(memoryBank.output).grouped(16).map(_.map(i => f"$i%02x").mkString(" ")).foreach(ErrorReporting.debug(_))
platform.cpu match {
case millfork.Cpu.Z80 =>
val cpu = new Z80Core(Z80Memory(memoryBank), DummyIO)
cpu.reset()
cpu.setProgramCounter(0x1f0)
cpu.resetTStates()
while (!cpu.getHalt) {
cpu.executeOneInstruction()
dump(cpu)
cpu.getTStates should be < TooManyCycles
}
val tStates = cpu.getTStates
Timings(tStates, tStates) -> memoryBank
case _ =>
Timings(-1, -1) -> memoryBank
}
@ -102,4 +125,12 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
}
}
def dump(cpu: Z80Core): Unit = {
val a = cpu.getRegisterValue(CPUConstants.RegisterNames.A)
val bc = cpu.getRegisterValue(CPUConstants.RegisterNames.A)
val de = cpu.getRegisterValue(CPUConstants.RegisterNames.A)
val hl = cpu.getRegisterValue(CPUConstants.RegisterNames.A)
println(f"A=$a%02x,BC=$bc%04x,DE=$de%04x,HL=$hl%04x")
}
}

View File

@ -0,0 +1,24 @@
package millfork.test.emu
import com.codingrodent.microprocessor.IMemory
import millfork.output.MemoryBank
/**
* @author Karol Stasiak
*/
case class Z80Memory(memoryBank: MemoryBank) extends IMemory {
override def readByte(address: Int): Int = memoryBank.readByte(address)
override def readWord(address: Int): Int = memoryBank.readWord(address)
override def writeByte(address: Int, data: Int): Unit = {
// if (!memoryBank.writeable(address)) throw new RuntimeException("Can't write to $" + address.toHexString)
memoryBank.output(address) = data.toByte
}
override def writeWord(address: Int, data: Int): Unit = {
writeByte(address, data)
writeByte(address + 1, data >> 8)
}
}