1
0
mirror of https://github.com/irmen/ksim65.git synced 2024-06-15 04:29:31 +00:00

added Load button. began to implement c64 CIAs to provide keyboard matrix.

This commit is contained in:
Irmen de Jong 2019-09-24 01:22:54 +02:00
parent bb4819c6d0
commit 52f6823c0e
7 changed files with 167 additions and 14 deletions

View File

@ -0,0 +1,96 @@
package razorvine.c64emu
import razorvine.ksim65.components.Address
import razorvine.ksim65.components.MemMappedComponent
import razorvine.ksim65.components.UByte
/**
* Minimal simulation of the MOS 6526 CIA chip.
* Depending on what CIA it is (1 or 2), some registers do different things on the C64.
*/
class Cia(val number: Int, startAddress: Address, endAddress: Address): MemMappedComponent(startAddress, endAddress) {
private var ramBuffer = Array<UByte>(endAddress - startAddress + 1) { 0x00 }
private var pra = 0xff
init {
require(endAddress - startAddress + 1 == 256) { "cia requires exactly 256 memory bytes (16*16 mirrored)" }
}
override fun clock() {
// TODO: TOD timer, timer A and B countdowns, IRQ triggering.
}
override fun reset() {
// TODO: reset TOD timer, timer A and B
}
override fun get(address: Address): UByte {
val register = (address - startAddress) and 15
if(number==1) {
return if (register == 0x01) {
// PRB data port B (if bit is cleared in PRA, contains keys pressed in that column of the matrix)
println("read PRB, pra=${pra.toString(2)}")
when(pra) {
0b00000000 -> {
// check if any keys are pressed at all (by checking all columns at once)
// TODO zero if there's any key pressed, 0xff otherwise
0xff
}
0b11111110 -> {
// read column 0
0xff
}
0b11111101 -> {
// read column 1
0xff
}
0b11111011 -> {
0b11011111.toShort() // 'F'
}
0b11110111 -> {
// read column 3
0xff
}
0b11101111 -> {
// read column 4
0xff
}
0b11011111 -> {
// read column 5
0xff
}
0b10111111 -> {
// read column 6
0xff
}
0b01111111 -> {
// read column 7
0xff
}
else -> {
// invalid column selection
0xff
}
}
}
else ramBuffer[register]
}
// CIA #2 is not emulated yet
return ramBuffer[register]
}
override fun set(address: Address, data: UByte) {
val register = (address - startAddress) and 15
ramBuffer[register] = data
if(number==1) {
when (register) {
0x00 -> {
// PRA data port A (select keyboard matrix column)
pra = data.toInt()
}
}
}
// CIA #2 is not emulated yet
}
}

View File

@ -93,7 +93,7 @@ private class BitmapScreenPanel(val chargenData: ByteArray, val ram: MemoryCompo
val screen = 0x0400
val colors = 0xd800
val shifted = (ram[0xd018].toInt() and 0b00000010) != 0
g2d.background = ScreenDefs.colorPalette[ram[0xd021].toInt()]
g2d.background = ScreenDefs.colorPalette[ram[0xd021].toInt() and 15]
g2d.clearRect(0, 0, ScreenDefs.SCREEN_WIDTH, ScreenDefs.SCREEN_HEIGHT)
for(y in 0 until ScreenDefs.SCREEN_HEIGHT_CHARS) {
for(x in 0 until ScreenDefs.SCREEN_WIDTH_CHARS) {
@ -236,7 +236,7 @@ class MainC64Window(title: String, chargenData: ByteArray, val ram: MemoryCompon
* This avoids having to deal with the 'real' keyboard matrix,
* but it can't map keys like RUN/STOP and RESTORE properly.
*
* TODO: replace this by the real keyboard matrix including RUN/STOP and RESTORE handling.
* TODO: replace this by the real keyboard matrix.
*/
private fun keyEventToPetscii(ke: KeyEvent): UByte {
if(ke.isActionKey) {

View File

@ -9,15 +9,20 @@ import razorvine.ksim65.components.UByte
* It only has some logic to keep track of the raster line
*/
class VicII(startAddress: Address, endAddress: Address): MemMappedComponent(startAddress, endAddress) {
private var ramBuffer = Array<UByte>(endAddress - startAddress + 1) { 0 }
private var ramBuffer = Array<UByte>(endAddress - startAddress + 1) { 0xff }
private var rasterIrqLine = 0
var currentRasterLine = 1
private var totalClocks = 0L
private var scanlineClocks = 0
private var interruptStatusRegisterD019 = 0
init {
require(endAddress - startAddress + 1 == 0x400) { "vic-II requires exactly 1024 memory bytes (64*16 mirrored)" }
}
override fun clock() {
totalClocks++
if(totalClocks % 63L == 0L) {
scanlineClocks++
if(scanlineClocks == 63) {
scanlineClocks = 0
currentRasterLine++
if(currentRasterLine >= 312)
currentRasterLine = 0
@ -32,7 +37,6 @@ class VicII(startAddress: Address, endAddress: Address): MemMappedComponent(star
override fun reset() {
rasterIrqLine = 0
currentRasterLine = 1
totalClocks = 0L
interruptStatusRegisterD019 = 0
}
@ -49,13 +53,15 @@ class VicII(startAddress: Address, endAddress: Address): MemMappedComponent(star
override fun set(address: Address, data: UByte) {
val register = (address - startAddress) and 63
ramBuffer[register] = data
when(register) {
0x11 -> {
val rasterHigh = (data.toInt() ushr 7) shl 8
rasterIrqLine = (rasterIrqLine and 0x00ff) or rasterHigh
if(register<47) {
ramBuffer[register] = data
when (register) {
0x11 -> {
val rasterHigh = (data.toInt() ushr 7) shl 8
rasterIrqLine = (rasterIrqLine and 0x00ff) or rasterHigh
}
0x12 -> rasterIrqLine = (rasterIrqLine and 0xff00) or data.toInt()
}
0x12 -> rasterIrqLine = (rasterIrqLine and 0xff00) or data.toInt()
}
}
}

View File

@ -24,9 +24,12 @@ class C64Machine(title: String) : IVirtualMachine {
override val cpu = Cpu6502(false)
val ram = Ram(0x0000, 0xffff)
val vic = VicII(0xd000, 0xd3ff)
val cia1 = Cia(1, 0xdc00, 0xdcff)
val cia2 = Cia(2, 0xdd00, 0xddff)
val basicRom = Rom(0xa000, 0xbfff).also { it.load(basicData) }
val kernalRom = Rom(0xe000, 0xffff).also { it.load(kernalData) }
// TODO: implement the two CIAs to add timer and joystick support, and the keyboard matrix.
// TODO: implement something so that RND(0) actually works in basic.
private val debugWindow = DebugWindow(this)
private val hostDisplay = MainC64Window(title, chargenData, ram)
@ -40,6 +43,8 @@ class C64Machine(title: String) : IVirtualMachine {
bus += basicRom
bus += kernalRom
bus += vic
bus += cia1
bus += cia2
bus += ram
bus += cpu
bus.reset()
@ -57,6 +62,12 @@ class C64Machine(title: String) : IVirtualMachine {
}
}
override fun loadFileInRam(file: File, loadAddress: Address?) {
if(file.extension=="prg" && loadAddress==null)
ram.loadPrg(file.inputStream())
else
ram.load(file.readBytes(), loadAddress!!)
}
override fun getZeroAndStackPages(): Array<UByte> = ram.getPages(0, 2)

View File

@ -9,6 +9,8 @@ import javax.swing.event.MouseInputListener
import razorvine.ksim65.IHostInterface
import razorvine.ksim65.IVirtualMachine
import java.awt.event.*
import java.io.File
import java.lang.Integer.parseInt
import java.util.*
import javax.swing.*
import javax.swing.Timer
@ -215,12 +217,13 @@ class DebugWindow(private val vm: IVirtualMachine) : JFrame("debugger"), ActionL
val buttonPanel = JPanel(FlowLayout())
buttonPanel.border = BorderFactory.createTitledBorder("Control")
val loadBt = JButton("Load program").also { it.actionCommand = "load" }
val resetBt = JButton("Reset").also { it.actionCommand = "reset" }
val stepBt = JButton("Step").also { it.actionCommand = "step" }
val irqBt = JButton("IRQ").also { it.actionCommand = "irq" }
val nmiBt = JButton("NMI").also { it.actionCommand = "nmi" }
val quitBt = JButton("Quit").also { it.actionCommand = "quit" }
listOf(resetBt, irqBt, nmiBt, pauseBt, stepBt, quitBt).forEach {
listOf(loadBt, resetBt, irqBt, nmiBt, pauseBt, stepBt, quitBt).forEach {
it.addActionListener(this)
buttonPanel.add(it)
}
@ -245,6 +248,32 @@ class DebugWindow(private val vm: IVirtualMachine) : JFrame("debugger"), ActionL
override fun actionPerformed(e: ActionEvent) {
when(e.actionCommand) {
"load" -> {
val chooser = JFileChooser()
chooser.dialogTitle = "Choose binary program or .prg to load"
chooser.currentDirectory = File(".")
chooser.isMultiSelectionEnabled = false
val result = chooser.showOpenDialog(this)
if(result==JFileChooser.APPROVE_OPTION) {
if(chooser.selectedFile.extension=="prg") {
vm.loadFileInRam(chooser.selectedFile, null)
} else {
val addressStr = JOptionPane.showInputDialog(
this,
"The selected file isn't a .prg.\nSpecify memory load address (hexadecimal) manually.",
"Load address",
JOptionPane.QUESTION_MESSAGE,
null,
null,
"$"
) as String
val loadAddress = parseInt(addressStr.removePrefix("$"), 16)
vm.loadFileInRam(chooser.selectedFile, loadAddress)
}
}
}
"reset" -> {
vm.bus.reset()
updateCpu(vm.cpu, vm.bus)

View File

@ -7,6 +7,7 @@ import razorvine.ksim65.IVirtualMachine
import razorvine.ksim65.Version
import razorvine.ksim65.components.*
import razorvine.ksim65.components.Timer
import java.io.File
import javax.swing.ImageIcon
/**
@ -53,6 +54,13 @@ class VirtualMachine(title: String) : IVirtualMachine {
override fun getZeroAndStackPages(): Array<UByte> = ram.getPages(0, 2)
override fun loadFileInRam(file: File, loadAddress: Address?) {
if(file.extension=="prg" && loadAddress==null)
ram.loadPrg(file.inputStream())
else
ram.load(file.readBytes(), loadAddress!!)
}
override fun pause(paused: Boolean) {
this.paused = paused
}

View File

@ -1,11 +1,14 @@
package razorvine.ksim65
import razorvine.ksim65.components.Address
import razorvine.ksim65.components.UByte
import java.io.File
interface IVirtualMachine {
fun step()
fun pause(paused: Boolean)
fun getZeroAndStackPages(): Array<UByte>
fun loadFileInRam(file: File, loadAddress: Address?)
val cpu: Cpu6502
val bus: Bus