2019-09-23 23:22:54 +00:00
|
|
|
package razorvine.c64emu
|
|
|
|
|
2019-10-08 21:11:18 +00:00
|
|
|
import razorvine.ksim65.Cpu6502
|
2019-09-23 23:22:54 +00:00
|
|
|
import razorvine.ksim65.components.Address
|
|
|
|
import razorvine.ksim65.components.MemMappedComponent
|
|
|
|
import razorvine.ksim65.components.UByte
|
2019-09-24 19:14:24 +00:00
|
|
|
import java.awt.event.KeyEvent
|
2019-09-23 23:22:54 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
2019-09-26 20:59:14 +00:00
|
|
|
* This implementation provides a working keyboard matrix, TOD clock, and the essentials of the timer A and B.
|
2019-09-23 23:22:54 +00:00
|
|
|
*/
|
2019-10-12 10:35:18 +00:00
|
|
|
class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu: Cpu6502) : MemMappedComponent(startAddress, endAddress) {
|
|
|
|
private var ramBuffer = Array<UByte>(endAddress-startAddress+1) { 0 }
|
2019-10-08 21:11:18 +00:00
|
|
|
private var regPRA = 0xff
|
2019-09-23 23:22:54 +00:00
|
|
|
|
2019-09-25 23:31:39 +00:00
|
|
|
class TimeOfDay {
|
|
|
|
private var updatedAt = 0L
|
|
|
|
private var startedAt = 0L
|
|
|
|
private var userStartTime = 0.0
|
|
|
|
private var running = false
|
|
|
|
var hours = 0
|
|
|
|
var minutes = 0
|
|
|
|
var seconds = 0
|
|
|
|
var tenths = 0
|
2019-10-05 20:22:06 +00:00
|
|
|
private var latchedTime = 0L
|
|
|
|
private var latched = false
|
2019-09-25 23:31:39 +00:00
|
|
|
|
|
|
|
fun start() {
|
2019-09-26 18:56:13 +00:00
|
|
|
if (!running) {
|
2019-09-25 23:31:39 +00:00
|
|
|
updatedAt = System.currentTimeMillis()
|
|
|
|
startedAt = updatedAt
|
2019-10-12 10:35:18 +00:00
|
|
|
userStartTime = hours*3600+minutes*60+seconds+tenths/10.0
|
2019-09-25 23:31:39 +00:00
|
|
|
running = true
|
|
|
|
}
|
2019-10-05 20:22:06 +00:00
|
|
|
latch(false)
|
2019-09-25 23:31:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fun stop() {
|
|
|
|
running = false
|
|
|
|
}
|
|
|
|
|
2019-10-05 20:22:06 +00:00
|
|
|
fun latch(latched: Boolean) {
|
|
|
|
this.latched = latched
|
|
|
|
update()
|
|
|
|
}
|
|
|
|
|
2019-09-25 23:31:39 +00:00
|
|
|
fun update() {
|
2019-10-12 10:35:18 +00:00
|
|
|
if (!running || latched) return
|
2019-10-05 20:22:06 +00:00
|
|
|
latchedTime = System.currentTimeMillis()
|
|
|
|
if (updatedAt != latchedTime) {
|
|
|
|
updatedAt = latchedTime
|
2019-10-12 10:35:18 +00:00
|
|
|
var elapsedSeconds = (latchedTime.toDouble()-startedAt)/1000.0+userStartTime
|
2020-02-19 23:01:54 +00:00
|
|
|
if(elapsedSeconds>60*3600)
|
|
|
|
elapsedSeconds=0.0 // TOD counds max 60 hours
|
2019-10-12 10:35:18 +00:00
|
|
|
hours = (elapsedSeconds/3600).toInt()
|
|
|
|
elapsedSeconds -= hours*3600
|
|
|
|
minutes = (elapsedSeconds/60).toInt()
|
|
|
|
elapsedSeconds -= minutes*60
|
2019-09-25 23:31:39 +00:00
|
|
|
seconds = (elapsedSeconds).toInt()
|
|
|
|
elapsedSeconds -= seconds
|
2019-10-12 10:35:18 +00:00
|
|
|
tenths = (elapsedSeconds*10).toInt()
|
2019-09-25 23:31:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-04 18:39:57 +00:00
|
|
|
private var totalCycles = 0
|
2019-09-25 23:31:39 +00:00
|
|
|
private var tod = TimeOfDay()
|
2019-09-26 20:59:14 +00:00
|
|
|
private var timerAset = 0
|
|
|
|
private var timerBset = 0
|
|
|
|
private var timerAactual = 0
|
|
|
|
private var timerBactual = 0
|
2019-10-08 21:11:18 +00:00
|
|
|
private var timerAinterruptEnabled = false
|
|
|
|
private var timerBinterruptEnabled = false
|
2019-09-25 23:31:39 +00:00
|
|
|
|
2019-10-12 10:35:18 +00:00
|
|
|
private data class HostKeyPress(val code: Int, val rightSide: Boolean = false, val numpad: Boolean = false)
|
2019-09-25 20:58:58 +00:00
|
|
|
|
|
|
|
private val hostKeyPresses = mutableSetOf<HostKeyPress>()
|
2019-09-24 19:14:24 +00:00
|
|
|
|
2019-09-23 23:22:54 +00:00
|
|
|
init {
|
2019-10-12 10:35:18 +00:00
|
|
|
require(endAddress-startAddress+1 == 256) { "cia requires exactly 256 memory bytes (16*16 mirrored)" }
|
2019-09-23 23:22:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun clock() {
|
2019-10-04 18:39:57 +00:00
|
|
|
totalCycles++
|
|
|
|
|
2019-10-12 10:35:18 +00:00
|
|
|
if (totalCycles%20000 == 0) {
|
2019-10-04 18:39:57 +00:00
|
|
|
// TOD resolution is 0.1 second, no need to update it in every cycle
|
|
|
|
tod.update()
|
|
|
|
}
|
|
|
|
|
2019-10-12 10:35:18 +00:00
|
|
|
if (ramBuffer[0x0e].toInt() and 1 != 0) {
|
2019-10-05 21:34:43 +00:00
|
|
|
// timer A is enabled, assume system cycles counting for now
|
2019-09-26 20:59:14 +00:00
|
|
|
timerAactual--
|
2019-10-12 10:35:18 +00:00
|
|
|
if (timerAactual == 0 && timerAinterruptEnabled) {
|
2020-02-16 15:01:41 +00:00
|
|
|
if (number == 1) cpu.irqAsserted = true
|
|
|
|
else if (number == 2) cpu.nmiAsserted = true
|
2019-10-08 21:11:18 +00:00
|
|
|
}
|
2019-10-12 10:35:18 +00:00
|
|
|
if (timerAactual < 0) timerAactual = if (ramBuffer[0x0e].toInt() and 0b00001000 != 0) 0 else timerAset
|
2019-09-26 20:59:14 +00:00
|
|
|
}
|
2019-10-12 10:35:18 +00:00
|
|
|
if (ramBuffer[0x0f].toInt() and 1 != 0) {
|
2019-10-05 21:34:43 +00:00
|
|
|
// timer B is enabled
|
2019-10-08 21:11:18 +00:00
|
|
|
val regCRB = ramBuffer[0x0f].toInt()
|
2019-10-12 10:35:18 +00:00
|
|
|
if (regCRB and 0b01000000 != 0) {
|
2019-10-05 21:34:43 +00:00
|
|
|
// timer B counts timer A underruns
|
2019-10-12 10:35:18 +00:00
|
|
|
if (timerAactual == 0) timerBactual--
|
2019-10-05 21:34:43 +00:00
|
|
|
} else {
|
|
|
|
// timer B counts just the system cycles
|
|
|
|
timerBactual--
|
|
|
|
}
|
2019-10-12 10:35:18 +00:00
|
|
|
if (timerBactual == 0 && timerBinterruptEnabled) {
|
2020-02-16 15:01:41 +00:00
|
|
|
if (number == 1) cpu.irqAsserted = true
|
|
|
|
else if (number == 2) cpu.nmiAsserted = true
|
2019-10-08 21:11:18 +00:00
|
|
|
}
|
2019-10-12 10:35:18 +00:00
|
|
|
if (timerBactual < 0) timerBactual = if (regCRB and 0b00001000 != 0) 0 else timerBset
|
2019-09-26 20:59:14 +00:00
|
|
|
}
|
2019-09-23 23:22:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun reset() {
|
2019-09-25 20:58:58 +00:00
|
|
|
hostKeyPresses.clear()
|
2019-09-25 23:31:39 +00:00
|
|
|
tod.stop()
|
2019-09-26 20:59:14 +00:00
|
|
|
ramBuffer.fill(0)
|
|
|
|
timerAactual = 0
|
|
|
|
timerAset = 0
|
|
|
|
timerBactual = 0
|
|
|
|
timerBset = 0
|
2019-09-23 23:22:54 +00:00
|
|
|
}
|
|
|
|
|
2020-02-05 00:02:27 +00:00
|
|
|
override operator fun get(offset: Int): UByte {
|
2019-09-26 20:59:14 +00:00
|
|
|
fun scanColumn(vararg keys: HostKeyPress): UByte {
|
2019-09-25 20:58:58 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2020-02-05 00:02:27 +00:00
|
|
|
val register = offset and 15
|
2019-09-26 20:59:14 +00:00
|
|
|
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
|
2019-10-08 21:11:18 +00:00
|
|
|
return when (regPRA) {
|
2019-09-26 20:59:14 +00:00
|
|
|
0b00000000 -> {
|
|
|
|
// check if any keys are pressed at all (by checking all columns at once)
|
|
|
|
if (hostKeyPresses.isEmpty()) 0xff.toShort() else 0x00.toShort()
|
2019-09-23 23:22:54 +00:00
|
|
|
}
|
2019-09-26 20:59:14 +00:00
|
|
|
0b11111110 -> {
|
|
|
|
// read column 0
|
2019-10-12 10:35:18 +00:00
|
|
|
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))
|
2019-09-26 20:59:14 +00:00
|
|
|
}
|
|
|
|
0b11111101 -> {
|
|
|
|
// read column 1
|
2019-10-12 10:35:18 +00:00
|
|
|
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))
|
2019-09-26 20:59:14 +00:00
|
|
|
}
|
|
|
|
0b11111011 -> {
|
|
|
|
// read column 2
|
2019-10-12 10:35:18 +00:00
|
|
|
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))
|
2019-09-26 20:59:14 +00:00
|
|
|
}
|
|
|
|
0b11110111 -> {
|
|
|
|
// read column 3
|
2019-10-12 10:35:18 +00:00
|
|
|
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))
|
2019-09-26 20:59:14 +00:00
|
|
|
}
|
|
|
|
0b11101111 -> {
|
|
|
|
// read column 4
|
2019-10-12 10:35:18 +00:00
|
|
|
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))
|
2019-09-26 20:59:14 +00:00
|
|
|
}
|
|
|
|
0b11011111 -> {
|
|
|
|
// read column 5
|
2019-10-12 10:35:18 +00:00
|
|
|
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))
|
2019-09-26 20:59:14 +00:00
|
|
|
}
|
|
|
|
0b10111111 -> {
|
|
|
|
// read column 6
|
2019-10-12 10:35:18 +00:00
|
|
|
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
|
2019-09-26 20:59:14 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
0b01111111 -> {
|
|
|
|
// read column 7
|
2019-10-12 10:35:18 +00:00
|
|
|
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))
|
2019-09-26 20:59:14 +00:00
|
|
|
}
|
|
|
|
else -> {
|
|
|
|
// invalid column selection
|
|
|
|
0xff
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else ramBuffer[register]
|
2019-09-25 23:31:39 +00:00
|
|
|
|
|
|
|
return when (register) {
|
2019-09-26 20:59:14 +00:00
|
|
|
0x04 -> (timerAactual and 0xff).toShort()
|
|
|
|
0x05 -> (timerAactual ushr 8).toShort()
|
|
|
|
0x06 -> (timerBactual and 0xff).toShort()
|
|
|
|
0x07 -> (timerBactual ushr 8).toShort()
|
2019-09-25 23:31:39 +00:00
|
|
|
0x08 -> {
|
|
|
|
tod.start()
|
|
|
|
(tod.tenths and 0x0f).toShort()
|
|
|
|
}
|
2019-10-04 18:39:57 +00:00
|
|
|
0x09 -> toBCD(tod.seconds)
|
|
|
|
0x0a -> toBCD(tod.minutes)
|
2019-10-05 20:22:06 +00:00
|
|
|
0x0b -> {
|
|
|
|
tod.latch(true)
|
|
|
|
toBCD(tod.hours)
|
|
|
|
}
|
2019-09-25 23:31:39 +00:00
|
|
|
else -> ramBuffer[register]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun toBCD(data: Int): UByte {
|
2019-10-12 10:35:18 +00:00
|
|
|
val tens = data/10
|
|
|
|
val ones = data-tens*10
|
2019-09-25 23:31:39 +00:00
|
|
|
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
|
2019-10-12 10:35:18 +00:00
|
|
|
return tens*10+ones
|
2019-09-23 23:22:54 +00:00
|
|
|
}
|
|
|
|
|
2020-02-05 00:02:27 +00:00
|
|
|
override operator fun set(offset: Int, data: UByte) {
|
|
|
|
val register = offset and 15
|
2019-09-26 20:59:14 +00:00
|
|
|
if (number == 1 && register == 0x00) {
|
|
|
|
// PRA data port A (select keyboard matrix column)
|
2019-10-08 21:11:18 +00:00
|
|
|
regPRA = data.toInt()
|
2019-09-23 23:22:54 +00:00
|
|
|
}
|
2019-09-25 23:31:39 +00:00
|
|
|
|
2019-10-12 10:35:18 +00:00
|
|
|
if (register != 0x0d) ramBuffer[register] = data
|
2019-10-08 21:11:18 +00:00
|
|
|
|
2019-09-25 23:31:39 +00:00
|
|
|
when (register) {
|
2019-09-26 20:59:14 +00:00
|
|
|
0x04 -> {
|
2019-10-12 10:35:18 +00:00
|
|
|
if (ramBuffer[0x0e].toInt() and 0b10000000 == 0) {
|
2019-09-26 20:59:14 +00:00
|
|
|
timerAset = (timerAset and 0xff00) or data.toInt()
|
|
|
|
timerAactual = timerAset
|
|
|
|
}
|
2019-09-26 18:56:13 +00:00
|
|
|
}
|
2019-09-26 20:59:14 +00:00
|
|
|
0x05 -> {
|
2019-10-12 10:35:18 +00:00
|
|
|
if (ramBuffer[0x0e].toInt() and 0b10000000 == 0) {
|
2019-09-26 20:59:14 +00:00
|
|
|
timerAset = (timerAset and 0x00ff) or (data.toInt() shl 8)
|
|
|
|
timerAactual = timerAset
|
|
|
|
}
|
2019-09-26 18:56:13 +00:00
|
|
|
}
|
2019-09-26 20:59:14 +00:00
|
|
|
0x06 -> {
|
2019-10-12 10:35:18 +00:00
|
|
|
if (ramBuffer[0x0f].toInt() and 0b10000000 == 0) {
|
2019-09-26 20:59:14 +00:00
|
|
|
timerBset = (timerBset and 0xff00) or data.toInt()
|
|
|
|
timerBactual = timerBset
|
|
|
|
}
|
|
|
|
}
|
|
|
|
0x07 -> {
|
2019-10-12 10:35:18 +00:00
|
|
|
if (ramBuffer[0x0f].toInt() and 0b10000000 == 0) {
|
2019-09-26 20:59:14 +00:00
|
|
|
timerBset = (timerBset and 0x00ff) or (data.toInt() shl 8)
|
|
|
|
timerBactual = timerBset
|
|
|
|
}
|
2019-09-26 18:56:13 +00:00
|
|
|
}
|
2019-09-26 20:59:14 +00:00
|
|
|
0x08 -> tod.tenths = data.toInt() and 0x0f
|
|
|
|
0x09 -> tod.seconds = fromBCD(data)
|
|
|
|
0x0a -> tod.minutes = fromBCD(data)
|
2019-09-25 23:31:39 +00:00
|
|
|
0x0b -> {
|
|
|
|
tod.stop()
|
|
|
|
tod.hours = fromBCD(data)
|
|
|
|
}
|
2019-10-08 21:11:18 +00:00
|
|
|
0x0d -> {
|
2019-10-12 10:35:18 +00:00
|
|
|
if (data.toInt() and 0b10000000 != 0) {
|
2019-10-08 21:11:18 +00:00
|
|
|
// set ICR bits
|
|
|
|
val newICR = ramBuffer[0x0d].toInt() or (data.toInt() and 0b01111111)
|
|
|
|
timerAinterruptEnabled = newICR and 1 != 0
|
|
|
|
timerBinterruptEnabled = newICR and 2 != 0
|
|
|
|
ramBuffer[0x0d] = newICR.toShort()
|
|
|
|
} else {
|
|
|
|
// clear ICR bits
|
|
|
|
val newICR = ramBuffer[0x0d].toInt() and (data.toInt() and 0b01111111).inv()
|
|
|
|
timerAinterruptEnabled = newICR and 1 != 0
|
|
|
|
timerBinterruptEnabled = newICR and 2 != 0
|
|
|
|
ramBuffer[0x0d] = newICR.toShort()
|
|
|
|
}
|
|
|
|
}
|
2019-09-25 23:31:39 +00:00
|
|
|
}
|
2019-09-23 23:22:54 +00:00
|
|
|
}
|
2019-09-24 19:14:24 +00:00
|
|
|
|
|
|
|
fun hostKeyPressed(event: KeyEvent) {
|
2019-09-25 20:58:58 +00:00
|
|
|
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
|
2019-10-08 21:11:18 +00:00
|
|
|
// (this can happen because we're changing the key code for some pressed keys below,
|
|
|
|
// and a released key doesn't always match the pressed key code anymore then)
|
2019-10-12 10:35:18 +00:00
|
|
|
if (event.id == KeyEvent.KEY_RELEASED && event.keyCode in listOf(KeyEvent.VK_SHIFT, KeyEvent.VK_CONTROL, KeyEvent.VK_ALT,
|
|
|
|
KeyEvent.VK_ALT_GRAPH)
|
2019-09-25 20:58:58 +00:00
|
|
|
) hostKeyPresses.clear()
|
|
|
|
|
|
|
|
// try to remap the keys a bit so a modern PC keyboard maps better to the keys of the C64
|
2020-01-27 22:44:56 +00:00
|
|
|
when (event.keyChar) {
|
|
|
|
'@' -> {
|
2019-09-25 20:58:58 +00:00
|
|
|
unregister(KeyEvent.VK_SHIFT)
|
2019-09-26 18:56:13 +00:00
|
|
|
register(event.id, HostKeyPress(KeyEvent.VK_AT))
|
2019-09-25 20:58:58 +00:00
|
|
|
}
|
2020-01-27 22:44:56 +00:00
|
|
|
'^' -> {
|
2019-09-25 20:58:58 +00:00
|
|
|
unregister(KeyEvent.VK_SHIFT)
|
2019-09-26 18:56:13 +00:00
|
|
|
register(event.id, HostKeyPress(KeyEvent.VK_CIRCUMFLEX))
|
2019-09-25 20:58:58 +00:00
|
|
|
}
|
2020-01-27 22:44:56 +00:00
|
|
|
'*' -> {
|
2019-09-25 20:58:58 +00:00
|
|
|
unregister(KeyEvent.VK_SHIFT)
|
2019-09-26 18:56:13 +00:00
|
|
|
register(event.id, HostKeyPress(KeyEvent.VK_ASTERISK))
|
2019-09-25 20:58:58 +00:00
|
|
|
}
|
2020-01-27 22:44:56 +00:00
|
|
|
'+' -> {
|
2019-09-25 20:58:58 +00:00
|
|
|
unregister(KeyEvent.VK_SHIFT)
|
2019-09-26 18:56:13 +00:00
|
|
|
register(event.id, HostKeyPress(KeyEvent.VK_PLUS))
|
2019-09-25 20:58:58 +00:00
|
|
|
}
|
2020-01-27 22:44:56 +00:00
|
|
|
':' -> {
|
2019-09-25 20:58:58 +00:00
|
|
|
unregister(KeyEvent.VK_SHIFT)
|
2019-09-26 18:56:13 +00:00
|
|
|
register(event.id, HostKeyPress(KeyEvent.VK_COLON))
|
2019-09-25 20:58:58 +00:00
|
|
|
}
|
2020-01-27 22:44:56 +00:00
|
|
|
'~' -> {
|
2019-09-25 20:58:58 +00:00
|
|
|
unregister(KeyEvent.VK_SHIFT)
|
2019-09-26 18:56:13 +00:00
|
|
|
register(event.id, HostKeyPress(KeyEvent.VK_DEAD_TILDE))
|
2019-09-25 20:58:58 +00:00
|
|
|
}
|
2020-01-27 22:44:56 +00:00
|
|
|
'&' -> register(event.id, shift, HostKeyPress(KeyEvent.VK_6))
|
|
|
|
'(' -> register(event.id, shift, HostKeyPress(KeyEvent.VK_8))
|
|
|
|
')' -> register(event.id, shift, HostKeyPress(KeyEvent.VK_9))
|
|
|
|
'[' -> register(event.id, shift, HostKeyPress(KeyEvent.VK_COLON))
|
|
|
|
']' -> register(event.id, shift, HostKeyPress(KeyEvent.VK_SEMICOLON))
|
|
|
|
'"' -> register(event.id, HostKeyPress(KeyEvent.VK_2))
|
|
|
|
'\'' -> register(event.id, shift, HostKeyPress(KeyEvent.VK_7))
|
|
|
|
else -> // F2 = shift+F1
|
2019-09-26 18:56:13 +00:00
|
|
|
// F4 = shift+F3
|
|
|
|
// F6 = shift+F5
|
|
|
|
// F8 = shift+F7
|
|
|
|
// insert = shift+backspace(del)
|
|
|
|
// up = shift+down
|
|
|
|
// left = shift+right
|
|
|
|
// just map the key as usual
|
|
|
|
// both alts map to the 'commodore key' (same left alt)
|
|
|
|
// both controls and the tab key map to the 'single left control'
|
|
|
|
when (event.keyCode) {
|
|
|
|
KeyEvent.VK_CONTROL, KeyEvent.VK_TAB -> register(event.id, HostKeyPress(KeyEvent.VK_CONTROL))
|
|
|
|
KeyEvent.VK_ALT, KeyEvent.VK_ALT_GRAPH -> register(event.id, HostKeyPress(KeyEvent.VK_ALT))
|
|
|
|
KeyEvent.VK_F2 -> register(event.id, shift, HostKeyPress(KeyEvent.VK_F1))
|
|
|
|
KeyEvent.VK_F4 -> register(event.id, shift, HostKeyPress(KeyEvent.VK_F3))
|
|
|
|
KeyEvent.VK_F6 -> register(event.id, shift, HostKeyPress(KeyEvent.VK_F5))
|
|
|
|
KeyEvent.VK_F8 -> register(event.id, shift, HostKeyPress(KeyEvent.VK_F7))
|
|
|
|
KeyEvent.VK_INSERT -> register(event.id, shift, HostKeyPress(KeyEvent.VK_BACK_SPACE))
|
|
|
|
KeyEvent.VK_UP -> register(event.id, shift, HostKeyPress(KeyEvent.VK_DOWN))
|
|
|
|
KeyEvent.VK_LEFT -> register(event.id, shift, HostKeyPress(KeyEvent.VK_RIGHT))
|
|
|
|
else -> register(event.id, HostKeyPress(event.keyCode, rightSide, numpad))
|
2019-09-25 20:58:58 +00:00
|
|
|
}
|
|
|
|
}
|
2019-09-24 19:14:24 +00:00
|
|
|
}
|
2019-09-23 23:22:54 +00:00
|
|
|
}
|