diff --git a/c64testprgs/joytest.prg b/c64testprgs/joytest.prg
new file mode 100644
index 0000000..aa1a273
Binary files /dev/null and b/c64testprgs/joytest.prg differ
diff --git a/free-c64-roms/basic b/free-c64-roms/basic
new file mode 100644
index 0000000..7828da6
Binary files /dev/null and b/free-c64-roms/basic differ
diff --git a/free-c64-roms/chargen b/free-c64-roms/chargen
new file mode 100644
index 0000000..11cbd8c
Binary files /dev/null and b/free-c64-roms/chargen differ
diff --git a/free-c64-roms/kernal b/free-c64-roms/kernal
new file mode 100644
index 0000000..0df4af2
Binary files /dev/null and b/free-c64-roms/kernal differ
diff --git a/free-c64-roms/readme.txt b/free-c64-roms/readme.txt
new file mode 100644
index 0000000..031c2fa
--- /dev/null
+++ b/free-c64-roms/readme.txt
@@ -0,0 +1,24 @@
+Free/open source roms for the C64
+
+See https://github.com/MEGA65/open-roms
+
+The following copyright notices apply to the entirety of this package,
+including each source file, unless otherwise noted in each file or directory.
+
+ Copyright Paul Gardner-Stephen, 2019.
+ Copyright Roman Standzikowski (FeralChil64), 2019-2020.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see .
+
+
diff --git a/src/main/kotlin/razorvine/c64emu/Cia.kt b/src/main/kotlin/razorvine/c64emu/Cia.kt
index a948315..6eca49d 100644
--- a/src/main/kotlin/razorvine/c64emu/Cia.kt
+++ b/src/main/kotlin/razorvine/c64emu/Cia.kt
@@ -9,12 +9,20 @@ import java.awt.event.KeyEvent
/**
* 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.
- * This implementation provides a working keyboard matrix, TOD clock, and the essentials of the timer A and B.
+ * This implementation provides a working keyboard matrix, joystick in port#2 (cia 1),
+ * time of day clock, and the essentials of the timer A and B.
*/
class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu: Cpu6502) : MemMappedComponent(startAddress, endAddress) {
private var ramBuffer = Array(endAddress-startAddress+1) { 0 }
private var regPRA = 0xff
+ // joystick in port 2 configuration (only works on cia#1)
+ private var joy2up = false
+ private var joy2down = false
+ private var joy2left = false
+ private var joy2right = false
+ private var joy2fire = false
+
class TimeOfDay {
private var updatedAt = 0L
private var startedAt = 0L
@@ -125,6 +133,11 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu:
timerAset = 0
timerBactual = 0
timerBset = 0
+ joy2up = false
+ joy2down = false
+ joy2left = false
+ joy2right = false
+ joy2fire = false
}
override operator fun get(offset: Int): UByte {
@@ -139,71 +152,85 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu:
}
val register = offset and 15
- if (number == 1 && register == 0x01) {
- // register 1 on CIA#1 is the keyboard data port
- // if bit is cleared in PRA, contains keys pressed in that column of the matrix
- return when (regPRA) {
- 0b00000000 -> {
- // check if any keys are pressed at all (by checking all columns at once)
- if (hostKeyPresses.isEmpty()) 0xff.toShort() else 0x00.toShort()
- }
- 0b11111110 -> {
- // read column 0
- scanColumn(HostKeyPress(KeyEvent.VK_DOWN), HostKeyPress(KeyEvent.VK_F5), HostKeyPress(KeyEvent.VK_F3),
- HostKeyPress(KeyEvent.VK_F1), HostKeyPress(KeyEvent.VK_F7), HostKeyPress(KeyEvent.VK_RIGHT),
- HostKeyPress(KeyEvent.VK_ENTER), HostKeyPress(KeyEvent.VK_BACK_SPACE))
- }
- 0b11111101 -> {
- // read column 1
- scanColumn(HostKeyPress(KeyEvent.VK_SHIFT), // left shift
- HostKeyPress(KeyEvent.VK_E), HostKeyPress(KeyEvent.VK_S), HostKeyPress(KeyEvent.VK_Z),
- HostKeyPress(KeyEvent.VK_4), HostKeyPress(KeyEvent.VK_A), HostKeyPress(KeyEvent.VK_W),
- HostKeyPress(KeyEvent.VK_3))
- }
- 0b11111011 -> {
- // read column 2
- scanColumn(HostKeyPress(KeyEvent.VK_X), HostKeyPress(KeyEvent.VK_T), HostKeyPress(KeyEvent.VK_F),
- HostKeyPress(KeyEvent.VK_C), HostKeyPress(KeyEvent.VK_6), HostKeyPress(KeyEvent.VK_D),
- HostKeyPress(KeyEvent.VK_R), HostKeyPress(KeyEvent.VK_5))
- }
- 0b11110111 -> {
- // read column 3
- scanColumn(HostKeyPress(KeyEvent.VK_V), HostKeyPress(KeyEvent.VK_U), HostKeyPress(KeyEvent.VK_H),
- HostKeyPress(KeyEvent.VK_B), HostKeyPress(KeyEvent.VK_8), HostKeyPress(KeyEvent.VK_G),
- HostKeyPress(KeyEvent.VK_Y), HostKeyPress(KeyEvent.VK_7))
- }
- 0b11101111 -> {
- // read column 4
- scanColumn(HostKeyPress(KeyEvent.VK_N), HostKeyPress(KeyEvent.VK_O), HostKeyPress(KeyEvent.VK_K),
- HostKeyPress(KeyEvent.VK_M), HostKeyPress(KeyEvent.VK_0), HostKeyPress(KeyEvent.VK_J),
- HostKeyPress(KeyEvent.VK_I), HostKeyPress(KeyEvent.VK_9))
- }
- 0b11011111 -> {
- // read column 5
- scanColumn(HostKeyPress(KeyEvent.VK_COMMA), HostKeyPress(KeyEvent.VK_AT), HostKeyPress(KeyEvent.VK_COLON),
- HostKeyPress(KeyEvent.VK_PERIOD), HostKeyPress(KeyEvent.VK_MINUS), HostKeyPress(KeyEvent.VK_L),
- HostKeyPress(KeyEvent.VK_P), HostKeyPress(KeyEvent.VK_PLUS))
- }
- 0b10111111 -> {
- // read column 6
- scanColumn(HostKeyPress(KeyEvent.VK_SLASH), HostKeyPress(KeyEvent.VK_CIRCUMFLEX), HostKeyPress(KeyEvent.VK_EQUALS),
- HostKeyPress(KeyEvent.VK_SHIFT, rightSide = true), // right shift
- HostKeyPress(KeyEvent.VK_HOME), HostKeyPress(KeyEvent.VK_SEMICOLON), HostKeyPress(KeyEvent.VK_ASTERISK),
- HostKeyPress(KeyEvent.VK_DEAD_TILDE) // pound sign
- )
- }
- 0b01111111 -> {
- // read column 7
- scanColumn(HostKeyPress(KeyEvent.VK_ESCAPE), HostKeyPress(KeyEvent.VK_Q), HostKeyPress(KeyEvent.VK_ALT),
- HostKeyPress(KeyEvent.VK_SPACE), HostKeyPress(KeyEvent.VK_2), HostKeyPress(KeyEvent.VK_CONTROL),
- HostKeyPress(KeyEvent.VK_BACK_QUOTE), HostKeyPress(KeyEvent.VK_1))
- }
- else -> {
- // invalid column selection
- 0xff
+
+ if(number==1) {
+ // first CIA has keyboard matrix
+ if(register==0x00) {
+ // reading $dc00 is joystick in port #2
+ return (0b01100000
+ or (if(joy2up) 0 else 0b00000001)
+ or (if(joy2down) 0 else 0b00000010)
+ or (if(joy2left) 0 else 0b00000100)
+ or (if(joy2right) 0 else 0b00001000)
+ or (if(joy2fire) 0 else 0b00010000)).toShort()
+ } else if(register==0x01) {
+ // register 1 on CIA#1 is the keyboard data port (and joystick #1...)
+ // if bit is cleared in PRA, contains keys pressed in that column of the matrix
+ // NOTE: we do not emulate a joystick in port #1 as this conflicts with the keyboard.
+ // just use the joystick in port #2 for now...
+ return when (regPRA) {
+ 0b00000000 -> {
+ // check if any keys are pressed at all (by checking all columns at once)
+ if (hostKeyPresses.isEmpty()) 0xff.toShort() else 0x00.toShort()
+ }
+ 0b11111110 -> {
+ // read column 0
+ scanColumn(HostKeyPress(KeyEvent.VK_DOWN), HostKeyPress(KeyEvent.VK_F5), HostKeyPress(KeyEvent.VK_F3),
+ HostKeyPress(KeyEvent.VK_F1), HostKeyPress(KeyEvent.VK_F7), HostKeyPress(KeyEvent.VK_RIGHT),
+ HostKeyPress(KeyEvent.VK_ENTER), HostKeyPress(KeyEvent.VK_BACK_SPACE))
+ }
+ 0b11111101 -> {
+ // read column 1
+ scanColumn(HostKeyPress(KeyEvent.VK_SHIFT), // left shift
+ HostKeyPress(KeyEvent.VK_E), HostKeyPress(KeyEvent.VK_S), HostKeyPress(KeyEvent.VK_Z),
+ HostKeyPress(KeyEvent.VK_4), HostKeyPress(KeyEvent.VK_A), HostKeyPress(KeyEvent.VK_W),
+ HostKeyPress(KeyEvent.VK_3))
+ }
+ 0b11111011 -> {
+ // read column 2
+ scanColumn(HostKeyPress(KeyEvent.VK_X), HostKeyPress(KeyEvent.VK_T), HostKeyPress(KeyEvent.VK_F),
+ HostKeyPress(KeyEvent.VK_C), HostKeyPress(KeyEvent.VK_6), HostKeyPress(KeyEvent.VK_D),
+ HostKeyPress(KeyEvent.VK_R), HostKeyPress(KeyEvent.VK_5))
+ }
+ 0b11110111 -> {
+ // read column 3
+ scanColumn(HostKeyPress(KeyEvent.VK_V), HostKeyPress(KeyEvent.VK_U), HostKeyPress(KeyEvent.VK_H),
+ HostKeyPress(KeyEvent.VK_B), HostKeyPress(KeyEvent.VK_8), HostKeyPress(KeyEvent.VK_G),
+ HostKeyPress(KeyEvent.VK_Y), HostKeyPress(KeyEvent.VK_7))
+ }
+ 0b11101111 -> {
+ // read column 4
+ scanColumn(HostKeyPress(KeyEvent.VK_N), HostKeyPress(KeyEvent.VK_O), HostKeyPress(KeyEvent.VK_K),
+ HostKeyPress(KeyEvent.VK_M), HostKeyPress(KeyEvent.VK_0), HostKeyPress(KeyEvent.VK_J),
+ HostKeyPress(KeyEvent.VK_I), HostKeyPress(KeyEvent.VK_9))
+ }
+ 0b11011111 -> {
+ // read column 5
+ scanColumn(HostKeyPress(KeyEvent.VK_COMMA), HostKeyPress(KeyEvent.VK_AT), HostKeyPress(KeyEvent.VK_COLON),
+ HostKeyPress(KeyEvent.VK_PERIOD), HostKeyPress(KeyEvent.VK_MINUS), HostKeyPress(KeyEvent.VK_L),
+ HostKeyPress(KeyEvent.VK_P), HostKeyPress(KeyEvent.VK_PLUS))
+ }
+ 0b10111111 -> {
+ // read column 6
+ scanColumn(HostKeyPress(KeyEvent.VK_SLASH), HostKeyPress(KeyEvent.VK_CIRCUMFLEX), HostKeyPress(KeyEvent.VK_EQUALS),
+ HostKeyPress(KeyEvent.VK_SHIFT, rightSide = true), // right shift
+ HostKeyPress(KeyEvent.VK_HOME), HostKeyPress(KeyEvent.VK_SEMICOLON), HostKeyPress(KeyEvent.VK_ASTERISK),
+ HostKeyPress(KeyEvent.VK_DEAD_TILDE) // pound sign
+ )
+ }
+ 0b01111111 -> {
+ // read column 7
+ scanColumn(HostKeyPress(KeyEvent.VK_ESCAPE), HostKeyPress(KeyEvent.VK_Q), HostKeyPress(KeyEvent.VK_ALT),
+ HostKeyPress(KeyEvent.VK_SPACE), HostKeyPress(KeyEvent.VK_2), HostKeyPress(KeyEvent.VK_CONTROL),
+ HostKeyPress(KeyEvent.VK_BACK_QUOTE), HostKeyPress(KeyEvent.VK_1))
+ }
+ else -> {
+ // invalid column selection
+ 0xff
+ }
}
}
- } else ramBuffer[register]
+ }
return when (register) {
0x04 -> (timerAactual and 0xff).toShort()
@@ -224,19 +251,6 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu:
}
}
- private fun toBCD(data: Int): UByte {
- val tens = data/10
- val ones = data-tens*10
- return ((tens shl 4) or ones).toShort()
- }
-
- private fun fromBCD(bcd: UByte): Int {
- val ibcd = bcd.toInt()
- val tens = ibcd ushr 4
- val ones = ibcd and 0x0f
- return tens*10+ones
- }
-
override operator fun set(offset: Int, data: UByte) {
val register = offset and 15
if (number == 1 && register == 0x00) {
@@ -296,6 +310,19 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu:
}
}
+ private fun toBCD(data: Int): UByte {
+ val tens = data/10
+ val ones = data-tens*10
+ return ((tens shl 4) or ones).toShort()
+ }
+
+ private fun fromBCD(bcd: UByte): Int {
+ val ibcd = bcd.toInt()
+ val tens = ibcd ushr 4
+ val ones = ibcd and 0x0f
+ return tens*10+ones
+ }
+
fun hostKeyPressed(event: KeyEvent) {
val rightSide = event.keyLocation == KeyEvent.KEY_LOCATION_RIGHT
val numpad = event.keyLocation == KeyEvent.KEY_LOCATION_NUMPAD
@@ -374,4 +401,15 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu:
}
}
}
+
+ fun setJoystick2(up: Boolean, down: Boolean, left: Boolean, right: Boolean, fire: Boolean) {
+ if(number!=1)
+ throw NotImplementedError("joystick port 2 is connected to cia#1")
+
+ joy2up = up
+ joy2down = down
+ joy2left = left
+ joy2right = right
+ joy2fire = fire
+ }
}
diff --git a/src/main/kotlin/razorvine/c64emu/GUI.kt b/src/main/kotlin/razorvine/c64emu/GUI.kt
index f43583b..cfaccdf 100644
--- a/src/main/kotlin/razorvine/c64emu/GUI.kt
+++ b/src/main/kotlin/razorvine/c64emu/GUI.kt
@@ -82,16 +82,50 @@ class MainC64Window(title: String, chargen: Rom, val ram: MemoryComponent, val c
// keyboard events:
override fun keyTyped(event: KeyEvent) {}
+ private var joy2up = false
+ private var joy2down = false
+ private var joy2left = false
+ private var joy2right = false
+ private var joy2fire = false
+
override fun keyPressed(event: KeyEvent) {
// '\' is mapped as RESTORE, this causes a NMI on the cpu
if (event.keyChar == '\\') {
cpu.nmiAsserted = true
} else {
- keypressCia.hostKeyPressed(event)
+ if(event.keyLocation==KeyEvent.KEY_LOCATION_NUMPAD) {
+ // numpad is joystick #2
+ if (event.keyChar in "789") joy2up = true
+ if (event.keyChar in "123") joy2down = true
+ if (event.keyChar in "741") joy2left = true
+ if (event.keyChar in "963") joy2right = true
+ if (event.keyChar in "05\n") joy2fire = true
+ keypressCia.setJoystick2(joy2up, joy2down, joy2left, joy2right, joy2fire)
+ } else {
+ keypressCia.hostKeyPressed(event)
+ }
}
}
override fun keyReleased(event: KeyEvent) {
- keypressCia.hostKeyPressed(event)
+ if(event.keyLocation==KeyEvent.KEY_LOCATION_NUMPAD) {
+ // numpad is joystick #2
+ if (event.keyChar in "789") joy2up = false
+ if (event.keyChar in "123") joy2down = false
+ if (event.keyChar in "741") joy2left = false
+ if (event.keyChar in "963") joy2right = false
+ if (event.keyChar in "05\n") joy2fire = false
+ keypressCia.setJoystick2(joy2up, joy2down, joy2left, joy2right, joy2fire)
+ } else {
+ keypressCia.hostKeyPressed(event)
+ }
+ }
+
+ fun reset() {
+ joy2up = false
+ joy2down = false
+ joy2left = false
+ joy2right = false
+ joy2fire = false
}
}
diff --git a/src/main/kotlin/razorvine/c64emu/c64Main.kt b/src/main/kotlin/razorvine/c64emu/c64Main.kt
index 8dbb402..f1f78cc 100644
--- a/src/main/kotlin/razorvine/c64emu/c64Main.kt
+++ b/src/main/kotlin/razorvine/c64emu/c64Main.kt
@@ -216,6 +216,11 @@ class C64Machine(title: String) : IVirtualMachine {
while (cpu.instrCycles > 0) bus.clock()
}
+ override fun reset() {
+ bus.reset()
+ hostDisplay.reset()
+ }
+
override fun executeMonitorCommand(command: String) = monitor.command(command)
fun start() {
diff --git a/src/main/kotlin/razorvine/examplemachines/DebugWindow.kt b/src/main/kotlin/razorvine/examplemachines/DebugWindow.kt
index c50f293..43ab6d1 100644
--- a/src/main/kotlin/razorvine/examplemachines/DebugWindow.kt
+++ b/src/main/kotlin/razorvine/examplemachines/DebugWindow.kt
@@ -182,6 +182,7 @@ class DebugWindow(private val vm: IVirtualMachine) : JFrame("Debugger - ksim65 v
}
"reset" -> {
+ vm.reset()
vm.bus.reset()
updateCpu(vm.cpu, vm.bus)
}
diff --git a/src/main/kotlin/razorvine/examplemachines/GUI.kt b/src/main/kotlin/razorvine/examplemachines/GUI.kt
index f845107..9c07468 100644
--- a/src/main/kotlin/razorvine/examplemachines/GUI.kt
+++ b/src/main/kotlin/razorvine/examplemachines/GUI.kt
@@ -242,4 +242,8 @@ class MainWindow(title: String) : JFrame(title), KeyListener, MouseInputListener
else keyboardBuffer.pop()
}
+ fun reset() {
+ // when the reset button is pressed
+ }
+
}
diff --git a/src/main/kotlin/razorvine/examplemachines/machineMain.kt b/src/main/kotlin/razorvine/examplemachines/machineMain.kt
index 84de2cc..a4f204e 100644
--- a/src/main/kotlin/razorvine/examplemachines/machineMain.kt
+++ b/src/main/kotlin/razorvine/examplemachines/machineMain.kt
@@ -65,6 +65,11 @@ class VirtualMachine(title: String) : IVirtualMachine {
while (cpu.instrCycles > 0) bus.clock()
}
+ override fun reset() {
+ bus.reset()
+ hostDisplay.reset()
+ }
+
override fun executeMonitorCommand(command: String) = monitor.command(command)
fun start() {
diff --git a/src/main/kotlin/razorvine/ksim65/IVirtualMachine.kt b/src/main/kotlin/razorvine/ksim65/IVirtualMachine.kt
index 10177c0..4f71347 100644
--- a/src/main/kotlin/razorvine/ksim65/IVirtualMachine.kt
+++ b/src/main/kotlin/razorvine/ksim65/IVirtualMachine.kt
@@ -7,6 +7,7 @@ import java.io.File
interface IVirtualMachine {
fun step()
fun pause(paused: Boolean)
+ fun reset()
fun getZeroAndStackPages(): Array
fun loadFileInRam(file: File, loadAddress: Address?)
diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties
index d01ef93..af5f173 100644
--- a/src/main/resources/version.properties
+++ b/src/main/resources/version.properties
@@ -1 +1 @@
-version=1.8
+version=1.9-dev