forked from Lephenixnoir/gint
189 lines
4.4 KiB
C
189 lines
4.4 KiB
C
//---
|
|
// gint:keysc - The SH7305 and I/O Key Scan Interfaces
|
|
//---
|
|
|
|
#include <gint/drivers.h>
|
|
#include <gint/gint.h>
|
|
#include <gint/timer.h>
|
|
#include <gint/keyboard.h>
|
|
|
|
#include <core/mpu.h>
|
|
#include <defs/attributes.h>
|
|
|
|
//---
|
|
// Keyboard buffer
|
|
//---
|
|
|
|
/* The driver's internal state. At each step of time, this file compares the
|
|
internal state with the hardware state and generates events accordingly.
|
|
Events can be seen as a delta-encoding of the state of the keyboard over
|
|
time, and this buffer must be the sum of all events. This means that if an
|
|
event cannot be generated, f.i. because the buffer is full, this state must
|
|
*not* be updated. */
|
|
GDATA volatile uint8_t state[12] = { 0 };
|
|
|
|
/* A driver event, which is a change in a full row, not a key. */
|
|
typedef struct
|
|
{
|
|
uint time :12; /* Locally unique time identifier */
|
|
uint row :4; /* Row number */
|
|
uint old :8; /* Key status for the old row */
|
|
uint new :8; /* Key status for the new row */
|
|
|
|
} driver_event_t;
|
|
|
|
/* The keyboard event buffer. This is a circular list defined by [buffer_start]
|
|
and [buffer_end]. To avoid an ambiguity when start = end, the buffer is not
|
|
allowed to be full (at least one free cell must be remaining). */
|
|
GBSS static driver_event_t buffer[KEYBOARD_QUEUE_SIZE];
|
|
/* Buffer bounds */
|
|
GDATA static int buffer_start = 0;
|
|
GDATA static int buffer_end = 0;
|
|
|
|
/* Current time, in keyboard-scanning ticks */
|
|
GDATA int time = 0;
|
|
GDATA int full_release = 2;
|
|
|
|
/* buffer_push() - add an event in the keyboard buffer
|
|
Returns non-zero if the event cannot be pushed. */
|
|
static int buffer_push(driver_event_t ev)
|
|
{
|
|
int next = (buffer_end + 1) % KEYBOARD_QUEUE_SIZE;
|
|
if(next == buffer_start) return 1;
|
|
|
|
buffer[buffer_end] = ev;
|
|
buffer_end = next;
|
|
return 0;
|
|
}
|
|
|
|
/* buffer_poll() - generate key events from the buffer
|
|
Sets [ev] and returns zero on success, otherwise non-zero. */
|
|
static int buffer_poll(driver_event_t *ev)
|
|
{
|
|
if(buffer_start == buffer_end) return 1;
|
|
|
|
*ev = buffer[buffer_start];
|
|
buffer_start = (buffer_start + 1) % KEYBOARD_QUEUE_SIZE;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* keysc_frame() - generate a round of interrupts for the current frame */
|
|
void keysc_frame(void)
|
|
{
|
|
ALIGNED(2) uint8_t scan[12] = { 0 };
|
|
time++;
|
|
|
|
/* First scan the key matrix: from I/O ports on SH3, KEYSC on SH4 */
|
|
if(isSH3()) iokbd_scan(&scan);
|
|
else
|
|
{
|
|
volatile uint16_t *KEYSC = (void *)0xa44b0000;
|
|
uint16_t *array = (void *)&scan;
|
|
|
|
for(int i = 0; i < 6; i++) array[i] = KEYSC[i];
|
|
}
|
|
|
|
for(int row = 0; row < 12; row++)
|
|
{
|
|
/* Compare new data with the internal state. */
|
|
int old = state[row];
|
|
int new = scan[row];
|
|
if(old == new && !new) continue;
|
|
|
|
driver_event_t ev = {
|
|
.time = time,
|
|
.row = row,
|
|
.old = old,
|
|
.new = new,
|
|
};
|
|
|
|
/* Update internal status if the event could be pushed */
|
|
if(!buffer_push(ev)) state[row] = new;
|
|
}
|
|
}
|
|
|
|
/* key_poll() - poll the next keyboard event from the buffer */
|
|
key_event_t key_poll(void)
|
|
{
|
|
/* Every time a driver event is unqueued, its key events are generated
|
|
and put in this small buffer */
|
|
static key_event_t events[8];
|
|
/* Number of pending events in the previous buffer */
|
|
static int events_pending = 0;
|
|
|
|
/* If there are pending events, use them */
|
|
if(events_pending > 0) return events[--events_pending];
|
|
|
|
/* Unqueue something from the driver buffer, if possible */
|
|
driver_event_t ev;
|
|
if(buffer_poll(&ev)) return (key_event_t){ .type = KEYEV_NONE };
|
|
|
|
/* Generate new key events */
|
|
int old = ev.old << 1;
|
|
int new = ev.new;
|
|
|
|
for(int code = ((ev.row ^ 1) << 4) | 0x7; code & 0x7; code--)
|
|
{
|
|
int kind = (old & 2) | (new & 1);
|
|
old >>= 1;
|
|
new >>= 1;
|
|
|
|
if(!kind) continue;
|
|
|
|
key_event_t keyev = {
|
|
.time = ev.time,
|
|
.type = kind,
|
|
.key = code,
|
|
};
|
|
events[events_pending++] = keyev;
|
|
}
|
|
|
|
/* Return the first of these generated events */
|
|
return events[--events_pending];
|
|
}
|
|
|
|
//---
|
|
// Driver initialization
|
|
//---
|
|
|
|
static int callback(UNUSED void *arg)
|
|
{
|
|
keysc_frame();
|
|
return 0;
|
|
}
|
|
|
|
/* init() - setup the support timer */
|
|
static void init(void)
|
|
{
|
|
int tid = isSH3() ? 4 : 7;
|
|
|
|
/* 32768 Hz divided by 16 is 2048 */
|
|
/* TODO: Use I/O port scanning on SH3 */
|
|
timer_setup(tid, 2048, 0, callback, NULL);
|
|
timer_start(tid);
|
|
}
|
|
|
|
/* unload() - stop the support timer */
|
|
static void unload(void)
|
|
{
|
|
int tid = isSH3() ? 4 : 7;
|
|
timer_stop(tid);
|
|
}
|
|
|
|
//---
|
|
// Driver structure definition
|
|
//---
|
|
|
|
gint_driver_t drv_keysc = {
|
|
.name = "KEYSC",
|
|
.init = init,
|
|
.unload = unload,
|
|
.ctx_size = 0,
|
|
.sys_ctx = NULL,
|
|
.ctx_save = NULL,
|
|
.ctx_restore = NULL,
|
|
};
|
|
|
|
GINT_DECLARE_DRIVER(4, drv_keysc);
|