413 lines
10 KiB
D
413 lines
10 KiB
D
/+
|
|
+ device/keyboard.d
|
|
+
|
|
+ Copyright: 2007 Gerald Stocker
|
|
+
|
|
+ This file is part of Twoapple.
|
|
+
|
|
+ Twoapple is free software; you can redistribute it and/or modify
|
|
+ it under the terms of the GNU General Public License as published by
|
|
+ the Free Software Foundation; either version 2 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ Twoapple 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 General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU General Public License
|
|
+ along with Twoapple; if not, write to the Free Software
|
|
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
+/
|
|
|
|
module device.keyboard;
|
|
|
|
import device.base;
|
|
import memory;
|
|
import device.pushbutton;
|
|
|
|
const int KEY_BKSP = 65288; // Backspace (DELETE)
|
|
const int KEY_TAB = 65289; // Tab
|
|
const int KEY_RETURN = 65293; // Enter
|
|
const int KEY_ESCAPE = 65307; // ESC
|
|
const int KEY_LEFT = 65361; // left arrow
|
|
const int KEY_UP = 65362; // up arrow
|
|
const int KEY_RIGHT = 65363; // right arrow
|
|
const int KEY_DOWN = 65364; // down arrow
|
|
const int KEY_LSHIFT = 65505; // left shift
|
|
const int KEY_RSHIFT = 65506; // right sight
|
|
const int KEY_LOS = 65515; // left "windows" (open apple)
|
|
const int KEY_ROS = 65516; // right "windows" (closed apple)
|
|
|
|
const int KEY_LOWER_MIN = 97;
|
|
const int KEY_LOWER_MAX = 122;
|
|
const int KEY_UPPER_MIN = 65;
|
|
const int KEY_UPPER_MAX = 90;
|
|
|
|
// !"#$%&'()*+,-./0123456789:;<=>?@
|
|
const int KEY_SYMBOL_MIN = 32;
|
|
const int KEY_SYMBOL_MAX = 64;
|
|
const int KEY_SYMBOL2_MAX = 126;
|
|
|
|
const int KEY_BRACKETRIGHT = 93;
|
|
const int KEY_CIRCUM = 94;
|
|
|
|
import std.stdio;
|
|
|
|
class Keyboard
|
|
{
|
|
bool keyStrobe;
|
|
bool anyKeyDown;
|
|
ubyte latch;
|
|
bool[int] keysDown;
|
|
Pushbuttons buttons;
|
|
|
|
class RingBuffer
|
|
{
|
|
int[] values;
|
|
int nextRead, nextWrite;
|
|
|
|
this(int len)
|
|
{
|
|
values.length = len;
|
|
clear();
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
nextRead = nextWrite = 0;
|
|
}
|
|
|
|
bool canRead()
|
|
{
|
|
return (nextRead != nextWrite);
|
|
}
|
|
|
|
int read()
|
|
{
|
|
// assert(canRead()); XXX
|
|
int val = values[nextRead];
|
|
nextRead = (nextRead + 1) % values.length;
|
|
return val;
|
|
}
|
|
|
|
void write(int val)
|
|
{
|
|
int next = (nextWrite + 1) % values.length;
|
|
if (next != nextRead)
|
|
{
|
|
values[nextWrite] = val;
|
|
nextWrite = next;
|
|
}
|
|
else
|
|
{
|
|
// XXX Need to clean up press buffer
|
|
// else emulator misbehaves
|
|
writefln("Press buffer full");
|
|
}
|
|
}
|
|
}
|
|
|
|
RingBuffer presses, releases;
|
|
|
|
this()
|
|
{
|
|
// XXX constants
|
|
presses = new RingBuffer(20);
|
|
releases = new RingBuffer(10);
|
|
}
|
|
|
|
void reboot()
|
|
{
|
|
latch = 0;
|
|
keyStrobe = anyKeyDown = false;
|
|
keysDown = keysDown.init;
|
|
presses.clear();
|
|
releases.clear();
|
|
}
|
|
|
|
abstract int appleASCII(int keyval, bool ctrl);
|
|
abstract bool handleSpecialKey(int keyval, bool keyDown);
|
|
|
|
bool handlePress(int keyval, bool ctrl, int keycode)
|
|
{
|
|
int ascii = appleASCII(keyval, ctrl);
|
|
if (ascii < 0)
|
|
{
|
|
if (handleSpecialKey(keyval, true)) return true;
|
|
return false;
|
|
}
|
|
recordKeyPress(ascii, keycode);
|
|
return true;
|
|
}
|
|
|
|
bool handleRelease(int keyval, bool ctrl, int keycode)
|
|
{
|
|
int ascii = appleASCII(keyval, ctrl);
|
|
if (ascii < 0)
|
|
{
|
|
if (handleSpecialKey(keyval, false)) return true;
|
|
return false;
|
|
}
|
|
recordKeyRelease(keycode);
|
|
return true;
|
|
}
|
|
|
|
void processPresses()
|
|
{
|
|
if (!presses.canRead()) return;
|
|
|
|
anyKeyDown = true;
|
|
keyStrobe = true;
|
|
|
|
// assert latch < 0x80; XXX
|
|
latch = presses.read();
|
|
keysDown[presses.read()] = true;
|
|
}
|
|
|
|
void processReleases()
|
|
{
|
|
if (!releases.canRead()) return;
|
|
|
|
int code = releases.read();
|
|
if (code in keysDown) {
|
|
keysDown.remove(code);
|
|
if (keysDown.length == 0)
|
|
{
|
|
anyKeyDown = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void recordKeyPress(int ascii, int code)
|
|
{
|
|
presses.write(ascii);
|
|
presses.write(code);
|
|
}
|
|
|
|
void recordKeyRelease(int code)
|
|
{
|
|
releases.write(code);
|
|
}
|
|
|
|
ubyte delegate() onReadLatch;
|
|
void delegate() onClearStrobe;
|
|
|
|
ubyte readLatch()
|
|
{
|
|
if (onReadLatch !is null)
|
|
{
|
|
keyStrobe = true;
|
|
return onReadLatch() | 0x80;
|
|
}
|
|
|
|
if (keyStrobe)
|
|
{
|
|
return latch | 0x80;
|
|
}
|
|
else
|
|
{
|
|
return latch;
|
|
}
|
|
}
|
|
|
|
ubyte peekLatch()
|
|
{
|
|
return latch;
|
|
}
|
|
|
|
void clearKeystrobe()
|
|
{
|
|
if (keyStrobe)
|
|
{
|
|
keyStrobe = false;
|
|
if (onClearStrobe !is null)
|
|
onClearStrobe();
|
|
}
|
|
}
|
|
|
|
mixin(AbstractInitSwitches());
|
|
}
|
|
|
|
class Keyboard_II : Keyboard
|
|
{
|
|
int appleASCII(int keyval, bool ctrl)
|
|
{
|
|
int ascii = -1;
|
|
if (keyval > 65000)
|
|
{
|
|
switch (keyval)
|
|
{
|
|
case KEY_RETURN:
|
|
ascii = 0x0D;
|
|
break;
|
|
case KEY_ESCAPE:
|
|
ascii = 0x1B;
|
|
break;
|
|
case KEY_LEFT:
|
|
ascii = 0x08;
|
|
break;
|
|
case KEY_RIGHT:
|
|
ascii = 0x15;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (keyval >= KEY_SYMBOL_MIN)
|
|
{
|
|
if (keyval < KEY_SYMBOL_MAX)
|
|
{
|
|
ascii = keyval;
|
|
}
|
|
else if (keyval == KEY_SYMBOL_MAX)
|
|
{
|
|
if (ctrl)
|
|
ascii = 0;
|
|
else
|
|
ascii = keyval;
|
|
}
|
|
else if (keyval >= KEY_UPPER_MIN)
|
|
{
|
|
if (keyval <= KEY_UPPER_MAX)
|
|
{
|
|
if (ctrl)
|
|
ascii = keyval - KEY_UPPER_MIN + 1;
|
|
else
|
|
ascii = keyval;
|
|
}
|
|
else if (keyval == KEY_BRACKETRIGHT ||
|
|
keyval == KEY_CIRCUM)
|
|
{
|
|
ascii = keyval;
|
|
}
|
|
else if (keyval >= KEY_LOWER_MIN)
|
|
{
|
|
if (keyval <= KEY_LOWER_MAX)
|
|
{
|
|
if (ctrl)
|
|
ascii = keyval - KEY_LOWER_MIN + 1;
|
|
// XXX
|
|
// else if lowercase-mod
|
|
else
|
|
ascii = keyval - 32;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ascii;
|
|
}
|
|
|
|
bool handleSpecialKey(int keyval, bool keyDown)
|
|
{
|
|
// XXX check for shift key mod
|
|
return false;
|
|
}
|
|
|
|
mixin(InitSwitches("", [
|
|
mixin(MakeSwitch(
|
|
[0xC000, 0xC001, 0xC002, 0xC003, 0xC004, 0xC005, 0xC006, 0xC007,
|
|
0xC008, 0xC009, 0xC00A, 0xC00B, 0xC00C, 0xC00D, 0xC00E, 0xC00F],
|
|
"R", "readLatch")),
|
|
mixin(MakeSwitch(
|
|
[0xC010, 0xC011, 0xC012, 0xC013, 0xC014, 0xC015, 0xC016, 0xC017,
|
|
0xC018, 0xC019, 0xC01A, 0xC01B, 0xC01C, 0xC01D, 0xC01E, 0xC01F],
|
|
"R0W", "clearKeystrobe"))
|
|
]));
|
|
}
|
|
|
|
class Keyboard_IIe : Keyboard
|
|
{
|
|
int appleASCII(int keyval, bool ctrl)
|
|
{
|
|
int ascii = -1;
|
|
if (keyval > 65000)
|
|
{
|
|
switch (keyval)
|
|
{
|
|
case KEY_RETURN:
|
|
ascii = 0x0D;
|
|
break;
|
|
case KEY_ESCAPE:
|
|
ascii = 0x1B;
|
|
break;
|
|
case KEY_LEFT:
|
|
ascii = 0x08;
|
|
break;
|
|
case KEY_RIGHT:
|
|
ascii = 0x15;
|
|
break;
|
|
case KEY_UP:
|
|
ascii = 0x0B;
|
|
break;
|
|
case KEY_DOWN:
|
|
ascii = 0x0A;
|
|
break;
|
|
case KEY_BKSP:
|
|
ascii = 0x7F;
|
|
break;
|
|
case KEY_TAB:
|
|
ascii = 0x09;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if ((keyval >= KEY_SYMBOL_MIN) && (keyval <= KEY_SYMBOL2_MAX))
|
|
{
|
|
if (ctrl)
|
|
{
|
|
if ((keyval >= KEY_UPPER_MIN) && (keyval <= KEY_UPPER_MAX))
|
|
ascii = keyval - KEY_UPPER_MIN + 1;
|
|
else if ((keyval >= KEY_LOWER_MIN) &&
|
|
(keyval <= KEY_LOWER_MAX))
|
|
ascii = keyval - KEY_LOWER_MIN + 1;
|
|
else
|
|
ascii = keyval;
|
|
}
|
|
else
|
|
{
|
|
ascii = keyval;
|
|
}
|
|
}
|
|
return ascii;
|
|
}
|
|
|
|
bool handleSpecialKey(int keyval, bool keyDown)
|
|
{
|
|
if (keyval == KEY_LOS)
|
|
{
|
|
buttons.update(0, keyDown);
|
|
return true;
|
|
}
|
|
else if (keyval == KEY_ROS)
|
|
{
|
|
buttons.update(1, keyDown);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ubyte readAKD()
|
|
{
|
|
clearKeystrobe();
|
|
return latch | (anyKeyDown ? 0x80 : 0x00);
|
|
}
|
|
|
|
mixin(InitSwitches("", [
|
|
mixin(MakeSwitch(
|
|
[0xC000, 0xC001, 0xC002, 0xC003, 0xC004, 0xC005, 0xC006, 0xC007,
|
|
0xC008, 0xC009, 0xC00A, 0xC00B, 0xC00C, 0xC00D, 0xC00E, 0xC00F],
|
|
"R", "readLatch")),
|
|
mixin(MakeSwitch([0xC010], "R", "readAKD")),
|
|
mixin(MakeSwitch(
|
|
[0xC010, 0xC011, 0xC012, 0xC013, 0xC014, 0xC015, 0xC016, 0xC017,
|
|
0xC018, 0xC019, 0xC01A, 0xC01B, 0xC01C, 0xC01D, 0xC01E, 0xC01F],
|
|
"W", "clearKeystrobe"))
|
|
]));
|
|
}
|
|
|