2019-09-19 19:29:33 +00:00
|
|
|
package razorvine.c64emu
|
|
|
|
|
2019-09-20 20:12:58 +00:00
|
|
|
import razorvine.examplemachines.DebugWindow
|
2019-09-19 19:29:33 +00:00
|
|
|
import kotlin.concurrent.scheduleAtFixedRate
|
|
|
|
import razorvine.ksim65.Bus
|
|
|
|
import razorvine.ksim65.Cpu6502
|
|
|
|
import razorvine.ksim65.IVirtualMachine
|
|
|
|
import razorvine.ksim65.Version
|
|
|
|
import razorvine.ksim65.components.*
|
|
|
|
import java.io.File
|
|
|
|
import java.nio.file.Paths
|
|
|
|
import javax.swing.ImageIcon
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The virtual representation of the Commodore-64
|
|
|
|
*/
|
|
|
|
class C64Machine(title: String) : IVirtualMachine {
|
|
|
|
private val romsPath = Paths.get(expandUser("~/.vice/C64"))
|
2019-09-19 19:41:44 +00:00
|
|
|
private val chargenData = romsPath.resolve("chargen").toFile().readBytes()
|
|
|
|
private val basicData = romsPath.resolve("basic").toFile().readBytes()
|
|
|
|
private val kernalData = romsPath.resolve("kernal").toFile().readBytes()
|
2019-09-19 19:29:33 +00:00
|
|
|
|
|
|
|
override val bus = Bus()
|
|
|
|
override val cpu = Cpu6502(false)
|
|
|
|
val ram = Ram(0x0000, 0xffff)
|
|
|
|
val vic = VicII(0xd000, 0xd3ff)
|
2019-09-23 23:22:54 +00:00
|
|
|
val cia1 = Cia(1, 0xdc00, 0xdcff)
|
|
|
|
val cia2 = Cia(2, 0xdd00, 0xddff)
|
2019-09-19 19:29:33 +00:00
|
|
|
val basicRom = Rom(0xa000, 0xbfff).also { it.load(basicData) }
|
|
|
|
val kernalRom = Rom(0xe000, 0xffff).also { it.load(kernalData) }
|
2019-09-24 19:14:24 +00:00
|
|
|
// TODO: implement the two CIAs to add timer and joystick support.
|
|
|
|
// TODO: implement something so that RND(0) actually works in basic. (cia timer?)
|
2019-09-19 19:29:33 +00:00
|
|
|
|
|
|
|
private val debugWindow = DebugWindow(this)
|
2019-09-24 19:14:24 +00:00
|
|
|
private val hostDisplay = MainC64Window(title, chargenData, ram, cia1)
|
2019-09-20 20:12:58 +00:00
|
|
|
private var paused = false
|
2019-09-19 19:29:33 +00:00
|
|
|
|
|
|
|
init {
|
|
|
|
hostDisplay.iconImage = ImageIcon(javaClass.getResource("/icon.png")).image
|
|
|
|
debugWindow.iconImage = hostDisplay.iconImage
|
|
|
|
debugWindow.setLocation(hostDisplay.location.x+hostDisplay.width, hostDisplay.location.y)
|
|
|
|
|
|
|
|
bus += basicRom
|
|
|
|
bus += kernalRom
|
|
|
|
bus += vic
|
2019-09-23 23:22:54 +00:00
|
|
|
bus += cia1
|
|
|
|
bus += cia2
|
2019-09-19 19:29:33 +00:00
|
|
|
bus += ram
|
|
|
|
bus += cpu
|
|
|
|
bus.reset()
|
|
|
|
|
|
|
|
debugWindow.isVisible = true
|
|
|
|
hostDisplay.isVisible = true
|
|
|
|
hostDisplay.start()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun expandUser(path: String): String {
|
|
|
|
if(path.startsWith("~" + File.separator)) {
|
2019-09-19 19:41:44 +00:00
|
|
|
return System.getProperty("user.home") + path.substring(1)
|
2019-09-19 19:29:33 +00:00
|
|
|
} else {
|
|
|
|
throw UnsupportedOperationException("home dir expansion not implemented for other users")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-23 23:22:54 +00:00
|
|
|
override fun loadFileInRam(file: File, loadAddress: Address?) {
|
|
|
|
if(file.extension=="prg" && loadAddress==null)
|
|
|
|
ram.loadPrg(file.inputStream())
|
|
|
|
else
|
|
|
|
ram.load(file.readBytes(), loadAddress!!)
|
|
|
|
}
|
2019-09-19 19:29:33 +00:00
|
|
|
|
2019-09-21 13:57:14 +00:00
|
|
|
override fun getZeroAndStackPages(): Array<UByte> = ram.getPages(0, 2)
|
|
|
|
|
2019-09-20 20:12:58 +00:00
|
|
|
override fun pause(paused: Boolean) {
|
|
|
|
this.paused = paused
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun step() {
|
|
|
|
// step a single full instruction
|
2019-09-19 19:29:33 +00:00
|
|
|
while (cpu.instrCycles > 0) bus.clock()
|
|
|
|
bus.clock()
|
|
|
|
while (cpu.instrCycles > 0) bus.clock()
|
|
|
|
}
|
|
|
|
|
|
|
|
fun start() {
|
2019-09-21 13:57:14 +00:00
|
|
|
javax.swing.Timer(10) {
|
|
|
|
debugWindow.updateCpu(cpu, bus)
|
|
|
|
}.start()
|
|
|
|
|
|
|
|
java.util.Timer("cpu-clock", true).scheduleAtFixedRate(1, 1) {
|
2019-09-19 19:29:33 +00:00
|
|
|
if(!paused) {
|
|
|
|
repeat(400) {
|
2019-09-20 20:12:58 +00:00
|
|
|
step()
|
2019-09-19 19:29:33 +00:00
|
|
|
if(vic.currentRasterLine == 255) {
|
|
|
|
// we force an irq here ourselves rather than fully emulating the VIC-II's raster IRQ
|
|
|
|
// or the CIA timer IRQ/NMI.
|
|
|
|
cpu.irq()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun main(args: Array<String>) {
|
|
|
|
val machine = C64Machine("virtual Commodore-64 - using KSim65 v${Version.version}")
|
|
|
|
machine.start()
|
|
|
|
}
|