1
0
mirror of https://github.com/irmen/ksim65.git synced 2024-06-26 02:29:38 +00:00

Custom c64 key mapping to make typing on a modern keyboard tolerable. Connected Restore key (backslash) to NMI.

This commit is contained in:
Irmen de Jong 2019-09-25 22:58:58 +02:00
parent bfdd9aa656
commit 88c1688a41
4 changed files with 274 additions and 114 deletions

View File

@ -15,6 +15,7 @@
<option value="$PROJECT_DIR$" />
</set>
</option>
<option name="useAutoImport" value="true" />
<option name="useQualifiedModuleNames" value="true" />
</GradleProjectSettings>
</option>

View File

@ -13,7 +13,9 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address): MemMappe
private var ramBuffer = Array<UByte>(endAddress - startAddress + 1) { 0x00 }
private var pra = 0xff
private val hostKeyPresses = mutableSetOf<Int>()
private data class HostKeyPress(val code: Int, val rightSide: Boolean, val numpad: Boolean)
private val hostKeyPresses = mutableSetOf<HostKeyPress>()
init {
require(endAddress - startAddress + 1 == 256) { "cia requires exactly 256 memory bytes (16*16 mirrored)" }
@ -24,18 +26,26 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address): MemMappe
}
override fun reset() {
hostKeyPresses.clear()
// TODO: reset TOD timer, timer A and B
}
override fun get(address: Address): UByte {
fun scanColumn(keys: List<HostKeyPress>): UByte {
var bits = 0b10000000
var presses = 0
for (key in keys) {
if (key in hostKeyPresses) presses = presses or bits
bits = bits ushr 1
}
return (presses.inv() and 255).toShort()
}
val register = (address - startAddress) and 15
if (number == 1) {
return if (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
// TODO tweak the keyboard mapping so that keys like '=' can be pressed,
// RESTORE works (this is not a normal key but connected to the cpu's NMI line),
// Cursor up/down/left/right all work and left/right shift are different.
when (pra) {
0b00000000 -> {
// check if any keys are pressed at all (by checking all columns at once)
@ -43,115 +53,138 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address): MemMappe
}
0b11111110 -> {
// read column 0
val presses =
(if (KeyEvent.VK_DOWN in hostKeyPresses) 0b10000000 else 0) or
(if (KeyEvent.VK_F5 in hostKeyPresses) 0b01000000 else 0) or
(if (KeyEvent.VK_F3 in hostKeyPresses) 0b00100000 else 0) or
(if (KeyEvent.VK_F1 in hostKeyPresses) 0b00010000 else 0) or
(if (KeyEvent.VK_F7 in hostKeyPresses) 0b00001000 else 0) or
(if (KeyEvent.VK_RIGHT in hostKeyPresses) 0b00000100 else 0) or
(if (KeyEvent.VK_ENTER in hostKeyPresses) 0b00000010 else 0) or
(if (KeyEvent.VK_BACK_SPACE in hostKeyPresses) 0b00000001 else 0)
(presses.inv() and 255).toShort()
scanColumn(
listOf(
HostKeyPress(KeyEvent.VK_DOWN, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_F5, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_F3, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_F1, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_F7, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_RIGHT, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_ENTER, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_BACK_SPACE, rightSide = false, numpad = false)
)
)
}
0b11111101 -> {
// read column 1
val presses =
(if(KeyEvent.VK_SHIFT in hostKeyPresses) 0b10000000 else 0) or // TODO make it LEFT shift only
(if(KeyEvent.VK_E in hostKeyPresses) 0b01000000 else 0) or
(if(KeyEvent.VK_S in hostKeyPresses) 0b00100000 else 0) or
(if(KeyEvent.VK_Z in hostKeyPresses) 0b00010000 else 0) or
(if(KeyEvent.VK_4 in hostKeyPresses) 0b00001000 else 0) or
(if(KeyEvent.VK_A in hostKeyPresses) 0b00000100 else 0) or
(if(KeyEvent.VK_W in hostKeyPresses) 0b00000010 else 0) or
(if(KeyEvent.VK_3 in hostKeyPresses) 0b00000001 else 0)
(presses.inv() and 255).toShort()
scanColumn(
listOf(
HostKeyPress(KeyEvent.VK_SHIFT, rightSide = false, numpad = false), // left shift
HostKeyPress(KeyEvent.VK_E, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_S, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_Z, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_4, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_A, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_W, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_3, rightSide = false, numpad = false)
)
)
}
0b11111011 -> {
// read column 2
val presses =
(if (KeyEvent.VK_X in hostKeyPresses) 0b10000000 else 0) or
(if (KeyEvent.VK_T in hostKeyPresses) 0b01000000 else 0) or
(if (KeyEvent.VK_F in hostKeyPresses) 0b00100000 else 0) or
(if (KeyEvent.VK_C in hostKeyPresses) 0b00010000 else 0) or
(if (KeyEvent.VK_6 in hostKeyPresses) 0b00001000 else 0) or
(if (KeyEvent.VK_D in hostKeyPresses) 0b00000100 else 0) or
(if (KeyEvent.VK_R in hostKeyPresses) 0b00000010 else 0) or
(if (KeyEvent.VK_5 in hostKeyPresses) 0b00000001 else 0)
(presses.inv() and 255).toShort()
scanColumn(
listOf(
HostKeyPress(KeyEvent.VK_X, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_T, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_F, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_C, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_6, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_D, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_R, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_5, rightSide = false, numpad = false)
)
)
}
0b11110111 -> {
// read column 3
val presses =
(if (KeyEvent.VK_V in hostKeyPresses) 0b10000000 else 0) or
(if (KeyEvent.VK_U in hostKeyPresses) 0b01000000 else 0) or
(if (KeyEvent.VK_H in hostKeyPresses) 0b00100000 else 0) or
(if (KeyEvent.VK_B in hostKeyPresses) 0b00010000 else 0) or
(if (KeyEvent.VK_8 in hostKeyPresses) 0b00001000 else 0) or
(if (KeyEvent.VK_G in hostKeyPresses) 0b00000100 else 0) or
(if (KeyEvent.VK_Y in hostKeyPresses) 0b00000010 else 0) or
(if (KeyEvent.VK_7 in hostKeyPresses) 0b00000001 else 0)
(presses.inv() and 255).toShort()
scanColumn(
listOf(
HostKeyPress(KeyEvent.VK_V, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_U, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_H, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_B, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_8, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_G, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_Y, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_7, rightSide = false, numpad = false)
)
)
}
0b11101111 -> {
// read column 4
val presses =
(if (KeyEvent.VK_N in hostKeyPresses) 0b10000000 else 0) or
(if (KeyEvent.VK_O in hostKeyPresses) 0b01000000 else 0) or
(if (KeyEvent.VK_K in hostKeyPresses) 0b00100000 else 0) or
(if (KeyEvent.VK_M in hostKeyPresses) 0b00010000 else 0) or
(if (KeyEvent.VK_0 in hostKeyPresses) 0b00001000 else 0) or
(if (KeyEvent.VK_J in hostKeyPresses) 0b00000100 else 0) or
(if (KeyEvent.VK_I in hostKeyPresses) 0b00000010 else 0) or
(if (KeyEvent.VK_9 in hostKeyPresses) 0b00000001 else 0)
(presses.inv() and 255).toShort()
scanColumn(
listOf(
HostKeyPress(KeyEvent.VK_N, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_O, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_K, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_M, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_0, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_J, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_I, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_9, rightSide = false, numpad = false)
)
)
}
0b11011111 -> {
// read column 5
val presses =
(if (KeyEvent.VK_COMMA in hostKeyPresses) 0b10000000 else 0) or
(if (KeyEvent.VK_OPEN_BRACKET in hostKeyPresses) 0b01000000 else 0) or // '[' = @
(if (KeyEvent.VK_SEMICOLON in hostKeyPresses) 0b00100000 else 0) or // ';' -> :
(if (KeyEvent.VK_PERIOD in hostKeyPresses) 0b00010000 else 0) or
(if (KeyEvent.VK_MINUS in hostKeyPresses) 0b00001000 else 0) or
(if (KeyEvent.VK_L in hostKeyPresses) 0b00000100 else 0) or
(if (KeyEvent.VK_P in hostKeyPresses) 0b00000010 else 0) or
(if (KeyEvent.VK_PLUS in hostKeyPresses) 0b00000001 else 0)
(presses.inv() and 255).toShort()
scanColumn(
listOf(
HostKeyPress(KeyEvent.VK_COMMA, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_AT, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_COLON, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_PERIOD, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_MINUS, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_L, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_P, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_PLUS, rightSide = false, numpad = false)
)
)
}
0b10111111 -> {
// read column 6
val presses =
(if (KeyEvent.VK_SLASH in hostKeyPresses) 0b10000000 else 0) or
(if (KeyEvent.VK_BACK_SLASH in hostKeyPresses) 0b01000000 else 0) or // '\' -> up arrow
(if (KeyEvent.VK_EQUALS in hostKeyPresses) 0b00100000 else 0) or
(if (KeyEvent.VK_SHIFT in hostKeyPresses) 0b00010000 else 0) or // TODO RIGHT shift only
(if (KeyEvent.VK_HOME in hostKeyPresses) 0b00001000 else 0) or
(if (KeyEvent.VK_QUOTE in hostKeyPresses) 0b00000100 else 0) or // ' -> ;
(if (KeyEvent.VK_CLOSE_BRACKET in hostKeyPresses) 0b00000010 else 0) or // ']' = *
(if (KeyEvent.VK_END in hostKeyPresses) 0b00000001 else 0) // END -> pound key
(presses.inv() and 255).toShort()
scanColumn(
listOf(
HostKeyPress(KeyEvent.VK_SLASH, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_CIRCUMFLEX, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_EQUALS, rightSide = false, numpad = false),
HostKeyPress(
KeyEvent.VK_SHIFT,
rightSide = true,
numpad = false
), // right shift
HostKeyPress(KeyEvent.VK_HOME, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_SEMICOLON, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_ASTERISK, rightSide = false, numpad = false),
HostKeyPress(
KeyEvent.VK_DEAD_TILDE,
rightSide = false,
numpad = false
) // pound sign
)
)
}
0b01111111 -> {
// read column 7
val presses =
(if (KeyEvent.VK_ESCAPE in hostKeyPresses) 0b10000000 else 0) or // esc -> STOP
(if (KeyEvent.VK_Q in hostKeyPresses) 0b01000000 else 0) or
(if (KeyEvent.VK_ALT in hostKeyPresses) 0b00100000 else 0) or // alt -> Commodore
(if (KeyEvent.VK_SPACE in hostKeyPresses) 0b00010000 else 0) or
(if (KeyEvent.VK_2 in hostKeyPresses) 0b00001000 else 0) or
(if (KeyEvent.VK_CONTROL in hostKeyPresses) 0b00000100 else 0) or
(if (KeyEvent.VK_BACK_QUOTE in hostKeyPresses) 0b00000010 else 0) or // '`' -> left arrow
(if (KeyEvent.VK_1 in hostKeyPresses) 0b00000001 else 0)
(presses.inv() and 255).toShort()
scanColumn(
listOf(
HostKeyPress(KeyEvent.VK_ESCAPE, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_Q, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_ALT, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_SPACE, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_2, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_CONTROL, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_BACK_QUOTE, rightSide = false, numpad = false),
HostKeyPress(KeyEvent.VK_1, rightSide = false, numpad = false)
)
)
}
else -> {
// invalid column selection
0xff
}
}
}
else ramBuffer[register]
} else ramBuffer[register]
}
// CIA #2 is not emulated yet
@ -173,9 +206,127 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address): MemMappe
}
fun hostKeyPressed(event: KeyEvent) {
if(event.id==KeyEvent.KEY_PRESSED)
hostKeyPresses.add(event.keyCode)
else if(event.id==KeyEvent.KEY_RELEASED)
hostKeyPresses.remove(event.keyCode)
val rightSide = event.keyLocation == KeyEvent.KEY_LOCATION_RIGHT
val numpad = event.keyLocation == KeyEvent.KEY_LOCATION_NUMPAD
val shift = HostKeyPress(KeyEvent.VK_SHIFT, rightSide = true, numpad = false)
fun register(eventId: Int, vararg keys: HostKeyPress) {
if (eventId == KeyEvent.KEY_PRESSED) keys.forEach { hostKeyPresses.add(it) }
else keys.forEach { hostKeyPresses.remove(it) }
}
fun unregister(keyCode: Int) {
hostKeyPresses.removeAll { it.code == keyCode }
}
// to avoid some 'stuck' keys, if we receive a shift/control/alt RELEASE, we wipe the keyboard buffer
// (this can happen becase we're changing the keycode for some pressed keys below,
// and a released key doesn't always match the pressed keycode anymore then)
if (event.id == KeyEvent.KEY_RELEASED && event.keyCode in listOf(
KeyEvent.VK_SHIFT,
KeyEvent.VK_CONTROL,
KeyEvent.VK_ALT,
KeyEvent.VK_ALT_GRAPH
)
) hostKeyPresses.clear()
// try to remap the keys a bit so a modern PC keyboard maps better to the keys of the C64
when {
event.keyChar == '@' -> {
unregister(KeyEvent.VK_SHIFT)
register(event.id, HostKeyPress(KeyEvent.VK_AT, rightSide = false, numpad = false))
}
event.keyChar == '^' -> {
unregister(KeyEvent.VK_SHIFT)
register(event.id, HostKeyPress(KeyEvent.VK_CIRCUMFLEX, rightSide = false, numpad = false))
}
event.keyChar == '&' -> {
register(event.id, shift, HostKeyPress(KeyEvent.VK_6, rightSide = false, numpad = false))
}
event.keyChar == '*' -> {
unregister(KeyEvent.VK_SHIFT)
register(event.id, HostKeyPress(KeyEvent.VK_ASTERISK, rightSide = false, numpad = false))
}
event.keyChar == '(' -> {
register(event.id, shift, HostKeyPress(KeyEvent.VK_8, rightSide = false, numpad = false))
}
event.keyChar == ')' -> {
register(event.id, shift, HostKeyPress(KeyEvent.VK_9, rightSide = false, numpad = false))
}
event.keyChar == '[' -> {
register(event.id, shift, HostKeyPress(KeyEvent.VK_COLON, rightSide = false, numpad = false))
}
event.keyChar == ']' -> {
register(event.id, shift, HostKeyPress(KeyEvent.VK_SEMICOLON, rightSide = false, numpad = false))
}
event.keyChar == '+' -> {
unregister(KeyEvent.VK_SHIFT)
register(event.id, HostKeyPress(KeyEvent.VK_PLUS, rightSide = false, numpad = false))
}
event.keyChar == '"' -> {
register(event.id, HostKeyPress(KeyEvent.VK_2, rightSide = false, numpad = false))
}
event.keyChar == ':' -> {
unregister(KeyEvent.VK_SHIFT)
register(event.id, HostKeyPress(KeyEvent.VK_COLON, rightSide = false, numpad = false))
}
event.keyChar == '~' -> {
unregister(KeyEvent.VK_SHIFT)
register(event.id, HostKeyPress(KeyEvent.VK_DEAD_TILDE, rightSide = false, numpad = false))
}
event.keyChar == '\'' -> {
register(event.id, shift, HostKeyPress(KeyEvent.VK_7, rightSide = false, numpad = false))
}
else -> when (event.keyCode) {
KeyEvent.VK_CONTROL, KeyEvent.VK_TAB -> {
// both controls and the tab key map to the 'single left control'
register(event.id, HostKeyPress(KeyEvent.VK_CONTROL, rightSide = false, numpad = false))
}
KeyEvent.VK_ALT, KeyEvent.VK_ALT_GRAPH -> {
// both alts map to the 'commodore key' (same left alt)
register(event.id, HostKeyPress(KeyEvent.VK_ALT, rightSide = false, numpad = false))
}
KeyEvent.VK_F2 -> {
// F2 = shift+F1
val func = HostKeyPress(KeyEvent.VK_F1, rightSide = false, numpad = false)
register(event.id, shift, func)
}
KeyEvent.VK_F4 -> {
// F4 = shift+F3
val func = HostKeyPress(KeyEvent.VK_F3, rightSide = false, numpad = false)
register(event.id, shift, func)
}
KeyEvent.VK_F6 -> {
// F6 = shift+F5
val func = HostKeyPress(KeyEvent.VK_F5, rightSide = false, numpad = false)
register(event.id, shift, func)
}
KeyEvent.VK_F8 -> {
// F8 = shift+F7
val func = HostKeyPress(KeyEvent.VK_F7, rightSide = false, numpad = false)
register(event.id, shift, func)
}
KeyEvent.VK_INSERT -> {
// insert = shift+backspace(del)
val backspace = HostKeyPress(KeyEvent.VK_BACK_SPACE, rightSide = false, numpad = false)
register(event.id, shift, backspace)
}
KeyEvent.VK_UP -> {
// up = shift+down
val cursor = HostKeyPress(KeyEvent.VK_DOWN, rightSide = false, numpad = false)
register(event.id, shift, cursor)
}
KeyEvent.VK_LEFT -> {
// left = shift+right
val cursor = HostKeyPress(KeyEvent.VK_RIGHT, rightSide = false, numpad = false)
register(event.id, shift, cursor)
}
else -> {
// just map the key as usual
val hostkey = HostKeyPress(event.keyCode, rightSide, numpad)
register(event.id, hostkey)
}
}
}
}
}

View File

@ -1,5 +1,6 @@
package razorvine.c64emu
import razorvine.ksim65.Cpu6502
import razorvine.ksim65.components.MemoryComponent
import java.awt.*
import java.awt.image.BufferedImage
@ -132,6 +133,7 @@ class MainC64Window(
title: String,
chargenData: ByteArray,
val ram: MemoryComponent,
val cpu: Cpu6502,
val keypressCia: Cia
) : JFrame(title), KeyListener {
private val canvas = BitmapScreenPanel(chargenData, ram)
@ -218,8 +220,14 @@ class MainC64Window(
override fun keyTyped(event: KeyEvent) {}
override fun keyPressed(event: KeyEvent) {
// '\' is mapped as RESTORE, this causes a NMI on the cpu
if (event.keyChar == '\\') {
cpu.nmi()
} else {
keypressCia.hostKeyPressed(event)
}
}
override fun keyReleased(event: KeyEvent) {
keypressCia.hostKeyPressed(event)
}

View File

@ -32,7 +32,7 @@ class C64Machine(title: String) : IVirtualMachine {
// TODO: implement something so that RND(0) actually works in basic. (cia timer?)
private val debugWindow = DebugWindow(this)
private val hostDisplay = MainC64Window(title, chargenData, ram, cia1)
private val hostDisplay = MainC64Window(title, chargenData, ram, cpu, cia1)
private var paused = false
init {