From 4fa4faa278bf417e3a6b921a7e135e53e78a3bea Mon Sep 17 00:00:00 2001 From: Stephen Crane Date: Thu, 22 Aug 2024 15:58:45 +0100 Subject: [PATCH] Keyboards (#26) * ps/2 and hardware serial keyboards * serial keyboard * add ps2_kbd shim * hardware_run() and hardware_debug_cpu() * ... * ... * updates * refactoring * refactoring * run instructions --- CPU.h | 2 +- hardware.cpp | 39 +++++++++++++++---- hardware.h | 13 +++++-- hw_serial_kbd.cpp | 28 ++++++++++++++ hw_serial_kbd.h | 20 ++++++++++ keyboard.h | 18 --------- ps2_raw_kbd.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++ ps2_raw_kbd.h | 36 ++++++++++++++++++ ps2_serial_kbd.cpp | 40 +++++++++++++++++++ ps2_serial_kbd.h | 11 ++++++ ps2drv.cpp | 95 ---------------------------------------------- ps2drv.h | 70 ---------------------------------- r65emu.h | 6 ++- serial_kbd.h | 20 ++++++++++ 14 files changed, 290 insertions(+), 196 deletions(-) create mode 100644 hw_serial_kbd.cpp create mode 100644 hw_serial_kbd.h delete mode 100644 keyboard.h create mode 100644 ps2_raw_kbd.cpp create mode 100644 ps2_raw_kbd.h create mode 100644 ps2_serial_kbd.cpp create mode 100644 ps2_serial_kbd.h delete mode 100644 ps2drv.cpp delete mode 100644 ps2drv.h create mode 100644 serial_kbd.h diff --git a/CPU.h b/CPU.h index c17c957..6c4258d 100644 --- a/CPU.h +++ b/CPU.h @@ -16,7 +16,7 @@ public: virtual void run(unsigned instructions) =0; virtual void reset() =0; virtual void raise(uint8_t level) =0; - virtual char *status(char *buf, size_t n, bool hdr) =0; + virtual char *status(char *buf, size_t n, bool hdr = false) =0; virtual void checkpoint(Stream &s) = 0; virtual void restore(Stream &s) = 0; diff --git a/hardware.cpp b/hardware.cpp index 4fccbca..69cfe9a 100644 --- a/hardware.cpp +++ b/hardware.cpp @@ -1,3 +1,4 @@ +#include #include #include #include "hardware.h" @@ -26,11 +27,6 @@ spiram sram(SPIRAM_SIZE); Memory memory; -#if defined(USE_PS2_KBD) && !defined(USE_OWN_KBD) -#include "ps2drv.h" -PS2Driver ps2; -#endif - static CPU *_cpu; bool hardware_reset() { @@ -75,8 +71,8 @@ void hardware_init(CPU &cpu) { _cpu = &cpu; memory.begin(); -#if defined(USE_PS2_KBD) && !defined(USE_OWN_KBD) - ps2.begin(PS2_KBD_DATA, PS2_KBD_IRQ); +#if defined(DEBUGGING) || defined(CPU_DEBUG) + Serial.begin(TERMINAL_SPEED); #endif #if defined(TFT_BACKLIGHT) @@ -94,6 +90,35 @@ void hardware_init(CPU &cpu) { #endif } +#if defined(CPU_DEBUG) +bool cpu_debug = CPU_DEBUG; +#endif + +bool hardware_debug_cpu() { +#if defined(CPU_DEBUG) + cpu_debug = !cpu_debug; + return cpu_debug; +#else + return false; +#endif +} + +bool hardware_run(unsigned instructions) { + +#if defined(CPU_DEBUG) + if (cpu_debug) { + char buf[256]; + Serial.println(_cpu->status(buf, sizeof(buf))); + _cpu->run(1); + } else + _cpu->run(instructions); +#else + _cpu->run(instructions); +#endif + + return !_cpu->halted(); +} + #if !defined(NO_CHECKPOINT) void hardware_checkpoint(Stream &s) { unsigned ds = 0; diff --git a/hardware.h b/hardware.h index 13e09b2..1f75f3d 100644 --- a/hardware.h +++ b/hardware.h @@ -10,14 +10,21 @@ #include "hw/user.h" #endif +#if !defined(CPU_INSTRUCTIONS) +#define CPU_INSTRUCTIONS 1000 +#endif + +#if !defined(TERMINAL_SPEED) +#define TERMINAL_SPEED 115200 +#endif + bool hardware_reset(); void hardware_init(class CPU &); void hardware_checkpoint(class Stream &); void hardware_restore(class Stream &); +bool hardware_run(unsigned instructions = CPU_INSTRUCTIONS); +bool hardware_debug_cpu(); -#if defined(__PS2DRV_H__) && defined(USE_PS2_KBD) -extern class PS2Driver ps2; -#endif #if defined(__SPIRAM_H__) && defined(USE_SPIRAM) extern class spiram sram; #endif diff --git a/hw_serial_kbd.cpp b/hw_serial_kbd.cpp new file mode 100644 index 0000000..22d6d88 --- /dev/null +++ b/hw_serial_kbd.cpp @@ -0,0 +1,28 @@ +#include +#include "hardware.h" + +#if defined(HW_SERIAL_KBD) +#include "serial_kbd.h" +#include "hw_serial_kbd.h" + +int hw_serial_kbd::read() { + + if (!_serial.available()) + return -1; + + int k = _serial.read(); + if ((k >= 0x0e) && (k <= 0x19)) { + fnkey(k - 0x0d); + return -1; + } + return k; +} + +bool hw_serial_kbd::available() { + return _serial.available(); +} + +void hw_serial_kbd::reset() { + _serial.begin(TERMINAL_SPEED); +} +#endif diff --git a/hw_serial_kbd.h b/hw_serial_kbd.h new file mode 100644 index 0000000..d07fca1 --- /dev/null +++ b/hw_serial_kbd.h @@ -0,0 +1,20 @@ +#ifndef __HW_SERIAL_KBD_H__ +#define __HW_SERIAL_KBD_H__ + +/* + * A serial keyboard based around the hardware serial port ("Serial") + * 12 Function keys: ^N .. ^Y inclusive + */ +class hw_serial_kbd: public serial_kbd { +public: + hw_serial_kbd(HardwareSerial &serial): _serial(serial) {} + + int read(); + bool available(); + void reset(); + +private: + HardwareSerial &_serial; +}; + +#endif diff --git a/keyboard.h b/keyboard.h deleted file mode 100644 index f7ab9ed..0000000 --- a/keyboard.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __KEYBOARD_H__ -#define __KEYBOARD_H__ - -class Keyboard { -public: - virtual void up(uint8_t) = 0; - virtual void down(uint8_t) = 0; - virtual void reset() = 0; - - static inline bool isshift(uint8_t scan) { - return scan == 0x12 || scan == 0x59; - } - static inline bool isctrl(uint8_t scan) { - return scan == 0x14; - } -}; - -#endif diff --git a/ps2_raw_kbd.cpp b/ps2_raw_kbd.cpp new file mode 100644 index 0000000..f485622 --- /dev/null +++ b/ps2_raw_kbd.cpp @@ -0,0 +1,88 @@ +#include +#include "hardware.h" + +#if defined(USE_PS2_KBD) +#include +#include "ps2_raw_kbd.h" + +static PS2KeyRaw keyboard; + +bool ps2_raw_kbd::available() { + return keyboard.available(); +} + +#define PS2_F1 0x05 +#define PS2_F2 0x06 +#define PS2_F3 0x04 +#define PS2_F4 0x0C +#define PS2_F5 0x03 +#define PS2_F6 0x0B +#define PS2_F7 0x83 +#define PS2_F8 0x0A +#define PS2_F9 0x01 +#define PS2_F10 0x09 +#define PS2_F11 0x78 +#define PS2_F12 0x07 + +static uint8_t fn(uint8_t key) { + switch(key) { + case PS2_F1: return 1; + case PS2_F2: return 2; + case PS2_F3: return 3; + case PS2_F4: return 4; + case PS2_F5: return 5; + case PS2_F6: return 6; + case PS2_F7: return 7; + case PS2_F8: return 8; + case PS2_F9: return 9; + case PS2_F10: return 01; + case PS2_F11: return 11; + case PS2_F12: return 12; + } + return 0; +} + +static bool brk = false; + +uint16_t ps2_raw_kbd::read() { + if (!available()) + return 0; + + int s = keyboard.read(); + if (s < 0) + return 0; + + uint8_t k = (s & 0xff); + if (k == 0xf0) { + brk = true; + return 0; + } + + uint8_t f = fn(k); + if (f >= 1 && brk) { + fnkey(f); + brk = false; + return 0; + } + uint16_t r = brk? (0x8000 | k): k; + brk = false; + return r; +} + +void ps2_raw_kbd::reset() { + keyboard.begin(PS2_KBD_DATA, PS2_KBD_IRQ); + _m.reset(); +} + +void ps2_raw_kbd::poll() { + if (available()) { + uint16_t scan = read(); + uint8_t k = (scan & 0xff); + if (scan & 0x8000) + _m.up(k); + else + _m.down(k); + } +} + +#endif diff --git a/ps2_raw_kbd.h b/ps2_raw_kbd.h new file mode 100644 index 0000000..ed65343 --- /dev/null +++ b/ps2_raw_kbd.h @@ -0,0 +1,36 @@ +#if !defined(__PS2_KBD_H__) +#define __PS2_KBD_H__ + +class matrix_keyboard { +public: + virtual void up(uint8_t) = 0; + virtual void down(uint8_t) = 0; + virtual void reset() {} +}; + +inline bool is_ps2_shift(uint16_t scan) { return scan == 0x12 || scan == 0x59; } + +inline bool is_ps2_ctrl(uint16_t scan) { return scan == 0x14; } + +typedef void (*fnkey_handler)(uint8_t); + +class ps2_raw_kbd { +public: + ps2_raw_kbd(matrix_keyboard &m): _m(m) {} + + void register_fnkey_handler(fnkey_handler f) { _f = f; } + void poll(); + void reset(); + +protected: + void fnkey(uint8_t k) { if (_f) _f(k); } + +private: + uint16_t read(); + bool available(); + + fnkey_handler _f; + matrix_keyboard &_m; +}; + +#endif diff --git a/ps2_serial_kbd.cpp b/ps2_serial_kbd.cpp new file mode 100644 index 0000000..64c958d --- /dev/null +++ b/ps2_serial_kbd.cpp @@ -0,0 +1,40 @@ +#include +#include "hardware.h" + +#if defined(PS2_SERIAL_KBD) +#include +#include +#include "serial_kbd.h" +#include "ps2_serial_kbd.h" + +PS2KeyAdvanced keyboard; +PS2KeyMap keymap; + +void ps2_serial_kbd::reset() { + keyboard.begin(PS2_KBD_DATA, PS2_KBD_IRQ); + keyboard.setNoBreak(1); + keymap.selectMap(PS2_SERIAL_KBD); +} + +bool ps2_serial_kbd::available() { + return keyboard.available(); +} + +int ps2_serial_kbd::read() { + + if (!keyboard.available()) + return -1; + + uint16_t key = keyboard.read(); + if (key & PS2_FUNCTION) { + uint8_t k = key & 0xff; + if ((k >= PS2_KEY_F1) && (k <= PS2_KEY_F12)) { + fnkey(k - 0x60); + return -1; + } + } + + uint16_t code = keymap.remapKey(key); + return code == 0? -1: (code & 0xff); +} +#endif diff --git a/ps2_serial_kbd.h b/ps2_serial_kbd.h new file mode 100644 index 0000000..3872973 --- /dev/null +++ b/ps2_serial_kbd.h @@ -0,0 +1,11 @@ +#ifndef __PS2_SERIAL_KBD_H__ +#define __PS2_SERIAL_KBD_H__ + +class ps2_serial_kbd: public serial_kbd { +public: + int read(); + bool available(); + void reset(); +}; + +#endif diff --git a/ps2drv.cpp b/ps2drv.cpp deleted file mode 100644 index e18dca9..0000000 --- a/ps2drv.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include -#include "hardware.h" - -#if defined(USE_PS2_KBD) -#include "ps2drv.h" - -#if !defined(KBD_BUFFER) -#define KBD_BUFFER 16 -#endif - -static volatile uint8_t buffer[KBD_BUFFER]; -static volatile uint8_t head, tail; -static uint8_t DataPin; - -// The ISR for the external interrupt -#if defined(ESP32) || defined(ESP8266) -IRAM_ATTR -#endif -void ps2interrupt_handler(void) -{ - static uint8_t bitcount=0; - static uint8_t incoming=0; - static uint32_t prev_ms=0; - uint32_t now_ms; - uint8_t n, val; - - val = digitalRead(DataPin); - now_ms = millis(); - if (now_ms - prev_ms > 250) { - bitcount = 0; - incoming = 0; - } - prev_ms = now_ms; - n = bitcount - 1; - if (n <= 7) { - incoming |= (val << n); - } - bitcount++; - if (bitcount == 11) { - uint8_t i = head + 1; - if (i == KBD_BUFFER) i = 0; - if (i != tail) { - buffer[i] = incoming; - head = i; - } - bitcount = 0; - incoming = 0; - } -} - -bool PS2Driver::available() { - if (head == tail) - return false; - - uint8_t i = tail+1; - if (i == KBD_BUFFER) i = 0; - if (buffer[i] == 0xf0) - return i != head; - return true; -} - -unsigned PS2Driver::read2() { - if (head == tail) - return 0; - - uint8_t i = tail+1; - if (i == KBD_BUFFER) i = 0; - tail = i; - if (buffer[i] != 0xf0) - return buffer[i]; - return 0xf000 | read2(); -} - -unsigned PS2Driver::peek() { - if (head == tail) - return 0; - - uint8_t i = tail+1; - if (i == KBD_BUFFER) i = 0; - if (buffer[i] == 0xf0) { - if (++i == KBD_BUFFER) i = 0; - return 0xf000 | buffer[i]; - } - return buffer[i]; -} - -void PS2Driver::begin(uint8_t data_pin, uint8_t irq_pin) -{ - DataPin = data_pin; - pinMode(irq_pin, INPUT_PULLUP); - pinMode(data_pin, INPUT_PULLUP); - attachInterrupt(irq_pin, ps2interrupt_handler, FALLING); - head = tail = 0; -} -#endif diff --git a/ps2drv.h b/ps2drv.h deleted file mode 100644 index bbe2d1e..0000000 --- a/ps2drv.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef __PS2DRV_H__ -#define __PS2DRV_H__ - -class PS2Driver -{ -public: - PS2Driver() {} - - /** - * Starts the keyboard "service" by registering the external interrupt. - * setting the pin modes correctly and driving those needed to high. - * The propably best place to call this method is in the setup routine. - */ - void begin(uint8_t dataPin, uint8_t irq_pin); - - /** - * Returns true if there is a char to be read, false if not. - */ - bool available(); - - /** - * Returns the scancode last received from the keyboard. - * If there is no char available, 0 is returned. - */ - unsigned peek(); - - unsigned read2(); -}; - -#define is_down(scan) ((scan) < 0x100) - -#undef PS2_F1 -#undef PS2_F2 -#undef PS2_F3 -#undef PS2_F4 -#undef PS2_F5 -#undef PS2_F6 -#undef PS2_F7 -#undef PS2_F8 -#undef PS2_F9 -#undef PS2_F10 -#undef PS2_F11 -#undef PS2_F12 - -#define PS2_F1 0x05 -#define PS2_F2 0x06 -#define PS2_F3 0x04 -#define PS2_F4 0x0C -#define PS2_F5 0x03 -#define PS2_F6 0x0B -#define PS2_F7 0x83 -#define PS2_F8 0x0A -#define PS2_F9 0x01 -#define PS2_F10 0x09 -#define PS2_F11 0x78 -#define PS2_F12 0x07 - -#define PS2_KP_DOT 0x71 -#define PS2_KP_0 0x70 -#define PS2_KP_1 0x69 -#define PS2_KP_2 0x72 -#define PS2_KP_3 0x7a -#define PS2_KP_4 0x6b -#define PS2_KP_5 0x73 -#define PS2_KP_6 0x74 -#define PS2_KP_7 0x6c -#define PS2_KP_8 0x75 -#define PS2_KP_9 0x7d - -#endif diff --git a/r65emu.h b/r65emu.h index c2e50d2..6ee76b6 100644 --- a/r65emu.h +++ b/r65emu.h @@ -6,9 +6,7 @@ #include "ram.h" #include "spiram.h" #include "prom.h" -#include "ps2drv.h" #include "display.h" -#include "keyboard.h" #include "serialio.h" #include "filer.h" #include "flash_filer.h" @@ -17,5 +15,9 @@ #include "timed.h" #include "hardware.h" #include "sound_dac.h" +#include "ps2_raw_kbd.h" +#include "serial_kbd.h" +#include "ps2_serial_kbd.h" +#include "hw_serial_kbd.h" #endif diff --git a/serial_kbd.h b/serial_kbd.h new file mode 100644 index 0000000..214ca3c --- /dev/null +++ b/serial_kbd.h @@ -0,0 +1,20 @@ +#if !defined(__SERIAL_KBD_H__) +#define __SERIAL_KBD_H__ + +typedef void (*fnkey_handler)(uint8_t); + +class serial_kbd { +public: + virtual int read() = 0; + virtual bool available() = 0; + virtual void reset() {} + virtual void register_fnkey_handler(fnkey_handler f) { _f = f; } + +protected: + void fnkey(uint8_t k) { if (_f) _f(k); } + +private: + fnkey_handler _f; +}; + +#endif