twoapple-reboot/src/device/keyboard.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"))
]));
}