/** * encoding: UTF-8 * This file is a part of JaC64 - a Java C64 Emulator * Main Developer: Joakim Eriksson (Dreamfabric.com) * Contact: joakime@sics.se * Web: http://www.dreamfabric.com/c64 * --------------------------------------------------- * * */ package com.dreamfabric.jac64; import com.dreamfabric.c64utils.*; import java.awt.event.*; import java.util.*; /** * The keyboard and joystick emulation * * * Created: Wed Jun 14 17:39:15 2006 * * @author Joakim Eriksson, joakime@sics.se * @version 1.0 */ public class Keyboard { public static final int IO_OFFSET = CPU.IO_OFFSET; public static final int AUTO_SHIFT = 1024; public static final int AUTO_CTRL = 2048; public static final int AUTO_COMMODORE = 4096; public static final int MIN_AUTO = AUTO_SHIFT; public static final int STICK_UPDOWN = 1 | 2; public static final int STICK_LEFTRIGHT = 4 | 8; public static final int STICK_UP = 1; public static final int STICK_DOWN = 2; public static final int STICK_LEFT = 4; public static final int STICK_RIGHT = 8; public static final int STICK_FIRE = 16; public static final char[][] KEYMAPS = new char[][] { { 's','v', // Keymap for language "sv" ';', 'ö', '\'','ä', '[','å', '`', '§', '\\', (char) 222, '/', '-', ']', (char) 135, '-', (char) KeyEvent.VK_PLUS, '=', (char) 129 }, { 'd','e', // Keymap for language "de" ';', 'ö', '\'','ä', '[','ü', '`', '^', '\\', (char) 520, '/', '-', ']', (char) 521, '-', (char) 223, '=', (char) 129 } }; boolean extendedKeyboardEmulation = false; boolean stickExits = false; int joystick1 = 255; int bval = 255; boolean lastUp = false; boolean lastLeft = false; private int joy1 = 0xff; private int joy2 = 0xff; int stick = IO_OFFSET + 0xdc00; // stick 0; private int keyPressed = 0; private int lastKey; private C64Script c64script; private ArrayList hotkeyScript; int keyrow[] = new int[8]; int keycol[] = new int[8]; // Some keys have high value (+ => 521 for example) int keytable[][] = new int[1024][3]; // For handling autoshifts... int keyShift = 0; // Needs to add some "special keys" - that are handled slightly // different => key up / key right = key dwn + shift & key left + shift // + some ordinary keys that is hard to get on a normal non c64 keyboard // needs to be mapped (+-/*?, etc) // The C64 key-table - key to map, then row + col + auto "shift" / flag int keytableDef[][] = new int[][] { {'A', 1, 2, 0 }, {'B', 3, 4, 0 }, {'C', 2, 4, 0 }, {'D', 2, 2, 0 }, {'E', 1, 6, 0 }, {'F', 2, 5, 0 }, {'G', 3, 2, 0 }, {'H', 3, 5, 0 }, {'I', 4, 1, 0 }, {'J', 4, 2, 0 }, {'K', 4, 5, 0 }, {'L', 5, 2, 0 }, {'M', 4, 4, 0 }, {'N', 4, 7, 0 }, {'O', 4, 6, 0 }, {'P', 5, 1, 0 }, {'Q', 7, 6, 0 }, {'R', 2, 1, 0 }, {'S', 1, 5, 0 }, {'T', 2, 6, 0 }, {'U', 3, 6, 0 }, {'V', 3, 7, 0 }, {'W', 1, 1, 0 }, {'X', 2, 7, 0 }, {'Y', 3, 1, 0 }, {'Z', 1, 4, 0 }, {'0', 4, 3, 0 }, {'1', 7, 0, 0 }, {'2', 7, 3, 0 }, {'3', 1, 0, 0 }, {'4', 1, 3, 0 }, {'5', 2, 0, 0 }, {'6', 2, 3, 0 }, {'7', 3, 0, 0 }, {'8', 3, 3, 0 }, {'9', 4, 0, 0 }, {'\n', 0, 1, 0 }, {KeyEvent.VK_ENTER, 0, 1, 0 }, {' ', 7, 4, 0 }, {',', 5, 7, 0 }, {'.', 5, 4, 0 }, // = on the \ pos. {'\\', 6, 5, 0}, // This is actually colon on the C64 ;-) {';', 5, 5, 0 }, {'-', 5, 0, 0 }, // Actually + on the C64... (- => +) // {'-', 5, 3, 0 }, {'=', 5, 3, 0 }, // Actually - on the C64... (= => -) {'`', 7, 1, 0 }, // This is semi-colon on the C64 {'\'', 6, 2, 0 }, {KeyEvent.VK_SUBTRACT, 5, 3, 0 }, {'[', 5, 6, 0 }, {']', 6, 1, 0 }, {KeyEvent.VK_ESCAPE, 7, 1, 0}, {'/', 6, 7, 0 }, {KeyEvent.VK_DIVIDE, 6, 7, 0}, // will be mapped to an auto shift / {'?', 6, 7, AUTO_SHIFT}, {KeyEvent.VK_DELETE, 0, 0, 0 }, {KeyEvent.VK_BACK_SPACE, 0, 0, 0 }, // LEFT SHIFT {KeyEvent.VK_SHIFT, 1, 7, 0 }, // RIGHT SHIFT {KeyEvent.VK_CAPS_LOCK, 6, 4, 0 }, // Break {19, 7, 7, 0 }, {KeyEvent.VK_ESCAPE, 7, 7, 0 }, // Enter + CTRL {'\r', 0, 1, 0 }, // Commodore key on the control keys {KeyEvent.VK_CONTROL, 7, 5, KeyEvent.KEY_LOCATION_LEFT}, {KeyEvent.VK_ENTER, 0, 1, 0 }, // DOWN & RIGHT {KeyEvent.VK_DOWN, 0, 7, 0 }, {KeyEvent.VK_UP, 0, 7, AUTO_SHIFT }, {KeyEvent.VK_RIGHT, 0, 2, 0 }, {KeyEvent.VK_LEFT, 0, 2, AUTO_SHIFT }, // Function keys {KeyEvent.VK_F1, 0, 4, 0 }, {KeyEvent.VK_F3, 0, 5, 0 }, {KeyEvent.VK_F5, 0, 6, 0 }, {KeyEvent.VK_F7, 0, 3, 0 }, {KeyEvent.VK_HOME, 6, 3, 0 }, {KeyEvent.VK_END, 6, 6, 0}, {KeyEvent.VK_INSERT, 6, 0, 0}, {KeyEvent.VK_TAB, 7, 2, 0} }; private int restoreKey = KeyEvent.VK_PAGE_UP; public static final int USER_UP = 0; public static final int USER_DOWN = 1; public static final int USER_LEFT = 2; public static final int USER_RIGHT = 3; public static final int USER_FIRE = 4; private static final int[] ARROWS = new int[] { KeyEvent.VK_UP, 0, KeyEvent.VK_DOWN, 0, KeyEvent.VK_LEFT, 0, KeyEvent.VK_RIGHT, 0, KeyEvent.VK_CONTROL, KeyEvent.KEY_LOCATION_RIGHT}; private int[] userDefinedStick = ARROWS; private CIA cia; private C64Screen screen; private void remap(char key, char newkey) { for (int i = 0, n = keytableDef.length; i < n; i++) { if (keytableDef[i][0] == key) { keytableDef[i][0] = newkey; System.out.println("Remapped: " + (char) key + " => " + (char) newkey + " key " + (int) key + " => " + (int) newkey); return; } } } private boolean doMap(String ltarget) { for (int i = 0, n = KEYMAPS.length; i < n; i++) { char[] map = KEYMAPS[i]; String lang = "" + map[0] + map[1]; System.out.println("Checking map for: " + lang); if (ltarget.equals(lang)) { System.out.println("Found map - mapping..."); for (int j = 2, m = map.length; j < m; j += 2) { remap(map[j], map[j + 1]); } // Finished! return true; } } return false; } /** * Creates a new <code>Keyboard</code> instance. * */ public Keyboard(C64Screen screen, CIA cia, int[] memory) { Locale locale = Locale.getDefault(); System.out.println("Locale: " + locale); String lang = locale.getLanguage(); if (!doMap(lang)) { System.out.println("Could not find map for keyboard: " + lang); } this.cia = cia; this.screen = screen; for (int i = 0; i < keytable.length; i++) { keytable[i][0] = -1; } for (int i = 0; i < keytableDef.length; i++) { int key = keytableDef[i][0]; keytable[key][0] = keytableDef[i][1]; keytable[key][1] = keytableDef[i][2]; keytable[key][2] = keytableDef[i][3]; } reset(); } public void registerHotKey(int key, int modflag, String script, Object o) { if (hotkeyScript == null) { c64script = new C64Script(); hotkeyScript = new ArrayList(); } hotkeyScript.add(new Object[] {script, o, new int[] {key, modflag}}); } public void setStick(boolean one) { if (one) stick = IO_OFFSET + 0xdc01; else stick = IO_OFFSET + 0xdc00; } private int getUserStick(int key, int location) { if (userDefinedStick != null) { for (int i = 0, n = userDefinedStick.length; i < n; i += 2) { if (key == userDefinedStick[i]) { int cmpLoc = userDefinedStick[i + 1]; if (cmpLoc == 0 || location == userDefinedStick[i + 1]) { // System.out.println("Joystick emulation trigger"); return i / 2; } } } } return -1; } // # Chars in char buffer - 0xc6 - need not to be set here... public void keyPressed(KeyEvent event) { // System.out.println("Key pressed..." + event.getKeyCode()); // System.out.println("Char pressed..." + event.getKeyChar() + " = " + // (int) event.getKeyChar()); char chr = event.getKeyChar(); int key = event.getKeyCode(); int location = event.getKeyLocation(); if (hotkeyScript != null) { int mod = event.getModifiersEx(); // System.out.println("HotKey: " + key + " mod: " + mod); for (int i = 0, n = hotkeyScript.size(); i < n; i++) { Object[] hk = (Object[]) hotkeyScript.get(i); int[] keys = (int[]) hk[2]; // System.out.println("Cmp " + keys[0] + " mod: " + keys[1]); if (keys[0] == key && ((mod & keys[1]) == keys[1])) { c64script.interpretCall((String) hk[0], hk[1]); } } } if (key == 0) { System.out.println("KeyZero ???"); key = (int) Character.toLowerCase(chr); } if (key == restoreKey) { screen.restoreKey(true); } if (key != lastKey) { keyPressed++; } lastKey = key; int usr = getUserStick(key, location); if (extendedKeyboardEmulation) usr = -1; if (usr == -1) { usr = getNormalStick(key); } switch (usr) { // CHANGE TO STOCK1LEFT = true; (or similar!) case USER_UP: joystick1 = joystick1 & (255 - STICK_UP); lastUp = true; updateJoystick(); if (stickExits) { return; } break; case USER_DOWN: joystick1 = joystick1 & (255 - STICK_DOWN); lastUp = false; updateJoystick(); if (stickExits) { return; } break; case USER_LEFT: joystick1 = joystick1 & (255 - STICK_LEFT); lastLeft = true; updateJoystick(); if (stickExits) { return; } break; case USER_RIGHT: joystick1 = joystick1 & (255 - STICK_RIGHT); lastLeft = false; updateJoystick(); if (stickExits) { return; } break; case USER_FIRE: joystick1 = joystick1 & (255 - STICK_FIRE); updateJoystick(); if (stickExits) { return; } break; } // Change the joystick with F10! switch (key) { case KeyEvent.VK_F10: setStick(stick == IO_OFFSET + 0xdc00); break; case KeyEvent.VK_F9: System.out.println("F9"); extendedKeyboardEmulation = !extendedKeyboardEmulation; stickExits = !extendedKeyboardEmulation; break; case KeyEvent.VK_F11: // Toggle colour set? break; } if (extendedKeyboardEmulation) { // Auto shift handling! if ((keytable[key][2] & AUTO_SHIFT) != 0) { keyShift++; if (keyShift == 1) { // AutoShift on if first shift! - otherwize not?! handleKeyPress(KeyEvent.VK_SHIFT, location); } // Also increase shift is shift is pressed } else if (key == KeyEvent.VK_SHIFT) { keyShift++; } handleKeyPress(key, location); } else { // No autoshift keys!!! if (keytable[key][2] < MIN_AUTO) { handleKeyPress(key, location); } } } public void keyReleased(KeyEvent event) { int key = event.getKeyCode(); char chr = event.getKeyChar(); int location = event.getKeyLocation(); if (key == 0) { System.out.println("KeyZero???"); key = (int) Character.toLowerCase(chr); } if (key == restoreKey) { screen.restoreKey(false); } keyPressed--; lastKey = 0; if (keyPressed < 0) keyPressed = 0; int usr = getUserStick(key, location); if (usr == -1) usr = getNormalStick(key); switch (usr) { case USER_UP: joystick1 = joystick1 | STICK_UP; updateJoystick(); if (stickExits) { return; } break; case USER_DOWN: joystick1 = joystick1 | STICK_DOWN; updateJoystick(); if (stickExits) { return; } break; case USER_LEFT: joystick1 = joystick1 | STICK_LEFT; updateJoystick(); if (stickExits) { return; } break; case USER_RIGHT: joystick1 = joystick1 | STICK_RIGHT; updateJoystick(); if (stickExits) { return; } break; case USER_FIRE: joystick1 = joystick1 | STICK_FIRE; updateJoystick(); if (stickExits) { return; } break; } if (extendedKeyboardEmulation) { // Auto shift handling! if ((keytable[key][2] & AUTO_SHIFT) != 0) { keyShift--; if (keyShift == 0) { // AutoShift on if first shift! - otherwize not?! handleKeyRelease(KeyEvent.VK_SHIFT, location); } // Also decrease shift is shift is released } else if (key == KeyEvent.VK_SHIFT) { keyShift--; // Should not remove shift if autoshift was on? if (keyShift > 0) return; } handleKeyRelease(key, location); } else { if (keytable[key][2] < MIN_AUTO) { handleKeyRelease(key, location); } } } private int getNormalStick(int key) { switch (key) { case KeyEvent.VK_NUMPAD8 : return USER_UP; case KeyEvent.VK_NUMPAD2 : case KeyEvent.VK_NUMPAD5 : return USER_DOWN; case KeyEvent.VK_NUMPAD4 : return USER_LEFT; case KeyEvent.VK_NUMPAD6 : return USER_RIGHT; case KeyEvent.VK_NUMPAD0: return USER_FIRE; } return -1; } private void handleKeyPress(int key, int location) { int maprow = keytable[key][0]; int mapcol = keytable[key][1]; if (maprow != -1 && mapcol != -1) { // System.out.println("Row: " + maprow + " Col: " + mapcol); keyrow[maprow] = keyrow[maprow] & (255 - (1 << keytable[key][1])); keycol[mapcol] = keycol[mapcol] & (255 - (1 << keytable[key][0])); } } private void handleKeyRelease(int key, int location) { int maprow = keytable[key][0]; int mapcol = keytable[key][1]; if (maprow != -1 && mapcol != -1) { keyrow[maprow] = keyrow[maprow] | (1 << keytable[key][1]); keycol[mapcol] = keycol[mapcol] | (1 << keytable[key][0]); } } int readDC00(int pc) { // 0xdc00 - why does this not work when it is "correctly" implemented? // DC00 a'la VICE which seems to work on StarPost!? int val = 0xff; int tmp = pc < 0x10000 ? joy1 : 0xff; int mask = (cia.prb | ~(cia.ddrb)) & tmp; for (int m = 0x1, i = 0; i < 8; m <<= 1, i++) { if ((mask & m) == 0) val &= keycol[i]; } tmp = pc < 0x10000 ? joy2 : 0xff; // System.out.println("Read keyboard via dc00: => " + ((val & (cia.pra | ~(cia.ddra))) & tmp)); return (val & (cia.pra | ~(cia.ddra))) & tmp; } void setButtonval(int bval) { this.bval = bval; updateJoystick(); } int readDC01(int pc) { // A'la VICE! // System.out.println("PC: " + Integer.toHexString(pc)); int val = 0xff; int tmp = pc < 0x10000 ? (joy2 & bval) : 0xff; int mask = (cia.pra | ~(cia.ddra)) & tmp; for (int m = 0x1, i = 0; i < 8; m <<= 1, i++) { if ((mask & m) == 0) val &= keyrow[i]; } tmp = pc < 0x10000 ? (joy1 & bval) : 0xff; // System.out.println("Read keyboard via dc00: " + val + " => " + ((val & (cia.prb | ~(cia.ddrb))) & tmp)); return (val & (cia.prb | ~(cia.ddrb))) & tmp; } /* Updates $DC00/$DC01 based on recent joystick activity. */ private void updateJoystick() { int jst = joystick1 & bval; // both up and down? if ((jst & STICK_UPDOWN) == 0) { jst = (jst | STICK_UPDOWN) & (0xff - (lastUp ? STICK_UP : STICK_DOWN)); } // both left and rigth? if ((jst & STICK_LEFTRIGHT) == 0) { jst = (jst | STICK_LEFTRIGHT) & (0xff - (lastLeft ? STICK_LEFT : STICK_RIGHT)); } joy2 = stick == 0xdc00 + IO_OFFSET ? 0xff : jst; joy1 = stick == 0xdc00 + IO_OFFSET ? jst : 0xff; } public void reset() { lastKey = 0; keyPressed = 0; keyShift = 0; joystick1 = 255; for (int i = 0; i < 8; i++) { keyrow[i] = 255; keycol[i] = 255; } } }