mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-11 12:29:46 +00:00
Added Z80 emulator for testing
This commit is contained in:
parent
dc3425f64e
commit
4d00cb4db9
@ -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
|
||||
|
10
src/test/scala/millfork/test/emu/DummyIO.scala
Normal file
10
src/test/scala/millfork/test/emu/DummyIO.scala
Normal file
@ -0,0 +1,10 @@
|
||||
package millfork.test.emu
|
||||
|
||||
import com.codingrodent.microprocessor.IBaseDevice
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object DummyIO extends IBaseDevice {
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,5 +14,9 @@ object EmuUnoptimizedCrossPlatformRun {
|
||||
println(f"Running MOS")
|
||||
verifier(mm)
|
||||
}
|
||||
if (platforms.contains(CpuFamily.I80)) {
|
||||
println(f"Running Z80")
|
||||
verifier(mz)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
}
|
||||
|
24
src/test/scala/millfork/test/emu/Z80Memory.scala
Normal file
24
src/test/scala/millfork/test/emu/Z80Memory.scala
Normal 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)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user