ulisp-picocalc/ulisp-extensions.ino

1000 lines
22 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
User Extensions
LispBox uLisp Extension - Version 1.0 - June 2024
Hartmut Grawe - github.com/ersatzmoco - June 2024
edited by hasn0life for Lilygo T-Deck - Jan 2025
updated by picolisper for LilyGo T-Deck and
PicoCalc - April 2025
*/
// Utility functions
uint8_t
dec_to_bcd(uint8_t n)
{
uint8_t bcd = 0;
uint8_t tens = n / 10;
bcd = tens << 4;
tens *= 10;
bcd += (n - tens) & 0x0f;
return bcd;
}
uint8_t
bcd_to_dec(uint8_t n)
{
return ((n>>4) * 10) + (n&0x0f);
}
/*
* STANDARD DEFINITIONS
*
* These definitions should be the same on every platform.
*/
object *
fn_platform(object *args, object *env)
{
(void) args;
(void) env;
#if defined(PLATFORM_PICOCALC)
const char platformName[] = ":picocalc";
#elif defined(TDECK_PERI_POWERON) /* first t-deck define */
const char platformName[] = ":t-deck";
#elif defined(ARDUINO_TEENSY41)
const char platformName[] = ":teensy41";
#else
const char platformName[] = ":unknown";
#endif
return internlong((char *)platformName);
}
object *
fn_bcd_to_dec(object *args, object *env)
{
(void) env;
object *arg = car(args);
int n = checkinteger(arg);
if ((n < 0) || (n > 153)) {
error2("number not in the range [0...#x99].");
}
uint8_t result = bcd_to_dec(static_cast<uint8_t>(n));
return number(static_cast<int>(result));
}
object *
fn_dec_to_bcd(object *args, object *env)
{
(void) env;
object *arg = car(args);
int n = checkinteger(arg);
if ((n < 0) || (n > 99)) {
error2("number not in the range [0...99].");
}
uint8_t result = dec_to_bcd(static_cast<uint8_t>(n));
return number(static_cast<int>(result));
}
object *
fn_now(object *args, object *env)
{
(void) env;
static unsigned long Offset;
unsigned long now = millis()/1000;
int nargs = listlength(args);
// Set time
if (nargs == 3) {
Offset = (unsigned long)((checkinteger(first(args))*60 \
+ checkinteger(second(args)))*60 \
+ checkinteger(third(args)) - now);
} else if (nargs > 0) {
error2(PSTR("wrong number of arguments"));
}
// Return time
unsigned long secs = Offset + now;
object *seconds = number(secs%60);
object *minutes = number((secs/60)%60);
object *hours = number((secs/3600)%24);
return cons(hours, cons(minutes, cons(seconds, NULL)));
}
void
hyperprint(object *form, int lm, pfun_t pfun)
{
if (atom(form)) {
if (isbuiltin(form, NOTHING)) {
printsymbol(form, pfun);
} else {
printobject(form, pfun);
}
} else if (quoted(form)) {
pfun('\'');
hyperprint(car(cdr(form)), lm + 1, pfun);
} else {
lm = lm + PPINDENT;
bool fits = (subwidth(form, PPWIDTH - lm - PPINDENT) >= 0);
int special = 0, extra = 0; bool separate = true;
object *arg = car(form);
if (symbolp(arg) && builtinp(arg->name)) {
uint8_t minmax = getminmax(builtin(arg->name));
if (minmax == 0327 || minmax == 0313) {
special = 2; // defun, setq, setf, defvar
} else if (minmax == 0317 || minmax == 0017 ||
minmax == 0117 || minmax == 0123) {
special = 1;
}
}
while (form != NULL) {
if (atom(form)) {
pfstring(PSTR(" . "), pfun);
printobject(form, pfun);
pfun(')');
return;
} else if (separate) {
pfun('(');
separate = false;
} else if (special) {
pfun(' ');
special--;
} else if (fits) {
pfun(' ');
} else {
pln(pfun);
indent(lm, ' ', pfun);
}
hyperprint(car(form), lm+extra, pfun);
form = cdr(form);
}
pfun(')');
}
}
object *
fn_sym_def(object *args, object *env)
{
(void) env;
object *obj = first(args);
pfun_t pfun = pstreamfun(cdr(args));
#if defined(gfxsupport)
if (pfun == gfxwrite) ppwidth = GFXPPWIDTH;
#endif
object *pair = findvalue(obj, env);
object *var = car(pair);
object *val = cdr(pair);
pln(pfun);
if (consp(val) && symbolp(car(val)) && builtin(car(val)->name) == LAMBDA) {
hyperprint(cons(bsymbol(DEFUN), cons(var, cdr(val))), 0, pfun);
} else {
hyperprint(cons(bsymbol(DEFVAR), cons(var, cons(quote(val), NULL))), 0, pfun);
}
pln(pfun);
ppwidth = PPWIDTH;
return bsymbol(NOTHING);
}
object *
fn_listlibrary2(object *args, object *env)
{
(void) args, (void) env;
object *lst = nil;
GlobalStringIndex = 0;
object *line = read(glibrary);
while (line != NULL) {
builtin_t bname = builtin(first(line)->name);
if (bname == DEFUN || bname == DEFVAR) {
lst = cons(second(line), lst);
}
line = read(glibrary);
}
return lst;
}
object *
fn_lambdap(object *arg, object *env)
{
(void) env;
if (consp(arg)) {
arg = car(arg);
}
if (builtin(arg->name) == LAMBDA) {
return tee;
}
return nil;
}
object *
fn_searchstr(object *args, object *env)
{
(void) env;
int startpos = 0;
object *pattern = first(args);
object *target = second(args);
args = cddr(args);
if (pattern == NULL) {
return number(0);
} else if (target == NULL) {
return nil;
}
if (args != NULL) {
startpos = checkinteger(car(args));
}
if (stringp(pattern) && stringp(target)) {
int l = stringlength(target);
int m = stringlength(pattern);
if (startpos > l) {
error2(indexrange);
}
for (int i = startpos; i <= l-m; i++) {
int j = 0;
while (j < m && nthchar(target, i+j) == nthchar(pattern, j)) {
j++;
}
if (j == m) {
return number(i);
}
}
return nil;
} else {
error2("arguments are not both lists or strings");
}
return nil;
}
object *
fn_searchn(object *args, object *env)
{
(void) env;
int matches = 0;
int last_index = 0;
object *pattern = first(args);
object *target = second(args);
if (cddr(args) != NULL) {
object *num = third(args);
if (integerp(num)) {
matches = num->integer;
}
}
if (pattern == NULL) {
return number(0);
} else if (target == NULL) {
return nil;
} else if (listp(pattern) && listp(target)) {
int l = listlength(target);
int m = listlength(pattern);
for (int i = 0; i <= l-m; i++) {
object *target1 = target;
while (pattern != NULL && eq(car(target1), car(pattern))) {
pattern = cdr(pattern);
target1 = cdr(target1);
}
if (pattern == NULL){
last_index = i;
if (matches-- == 0) {
return number(i);
}
}
pattern = first(args); target = cdr(target);
}
if (last_index > 0) {
return number(last_index);
}
return nil;
} else if (stringp(pattern) && stringp(target)) {
int l = stringlength(target);
int m = stringlength(pattern);
for (int i = 0; i <= l-m; i++) {
int j = 0;
while (j < m && nthchar(target, i+j) == nthchar(pattern, j)) {
j++;
}
if (j == m) {
last_index = i;
if(matches-- == 0){
return number(i);
}
}
}
if (last_index > 0) {
return number(last_index);
}
return nil;
} else {
error2(PSTR("arguments are not both lists or strings"));
}
return nil;
}
// SD card standard library.
#if defined(sdcardsupport)
object *
fn_sd_rename(object *args, object *env)
{
(void) env;
char buffer1[BUFFERSIZE];
char buffer2[BUFFERSIZE];
object *pathFrom = car(args);
if (!stringp(pathFrom)) {
error2("filenames must be strings.");
}
object *pathTo = car(cdr(args));
if (!stringp(pathTo)) {
error2("filenames must be strings.");
}
if (!SD.rename((const char *)MakeFilename(pathFrom, buffer1),
(const char *)MakeFilename(pathTo, buffer2))) {
return nil;
}
return tee;
}
object *
fn_sd_remove(object *args, object *env)
{
(void) env;
char buffer[BUFFERSIZE];
object *arg = car(args);
if (!SD.remove(MakeFilename(arg, buffer))) {
return nil;
}
return tee;
}
object *
fn_sd_existsp(object *args, object *env)
{
(void) env;
char buffer[BUFFERSIZE];
object *arg = car(args);
if (!SD.exists(MakeFilename(arg, buffer))) {
return nil;
}
return tee;
}
/*
(sd-make-dir path)
Create a directory on the SD card.
This will also create any intermediate directories that dont already exists;
e.g. SD.mkdir("a/b/c") will create a, b, and c.
*/
object *
fn_sd_mkdir(object *args, object *env)
{
(void) env;
char buffer[BUFFERSIZE];
object *arg = car(args);
if (SD.mkdir(MakeFilename(arg, buffer))) {
return tee;
}
return nil;
}
/*
(sd-remove-dir path)
Remove a directory from the SD card. The directory must be empty.
*/
object *
fn_sd_rmdir(object *args, object *env)
{
(void) env;
char buffer[BUFFERSIZE];
object *arg = car(args);
if (SD.rmdir(MakeFilename(arg, buffer))) {
return tee;
}
return nil;
}
/*
* (sd-list)
*/
object *
fn_sd_list(object *args, object *env)
{
(void) env;
char *sd_path_buf = NULL;
SDBegin();
File root;
object *result = cons(NULL, NULL);
object *ptr = result;
if (args != NULL) {
object *arg1 = checkstring(first(args));
int len = stringlength(arg1) + 2; //make it longer for the initial slash and the null terminator
sd_path_buf = (char*)malloc(len);
if (sd_path_buf != NULL) {
cstring(arg1, &sd_path_buf[1], len-1);
sd_path_buf[0] = '/'; //really weird way to add a slash at the front...
root = SD.open(sd_path_buf);
}
} else{
root = SD.open("/");
}
while (true) {
File entry = root.openNextFile();
if (!entry) {
break; // no more files
}
object *filename = lispstring((char*)entry.name());
if (entry.isDirectory()) {
cdr(ptr) = cons(filename, NULL);
} else{
cdr(ptr) = cons(cons(filename, number(entry.size())), NULL);
}
ptr = cdr(ptr);
entry.close();
}
if (sd_path_buf != NULL) {
free(sd_path_buf);
}
root.close();
return cdr(result);
}
#endif
/*
* PICOCALC-SPECIFIC FUNCTIONS
*
* Only works on the PicoCalc...
*/
#if defined(PLATFORM_PICOCALC)
char
getkey()
{
PCKeyboard::KeyEvent kevt;
char keypress;
do {
kevt = pc_kbd.keyEvent();
if (kevt.state == PCKeyboard::StatePress) {
if (kevt.key == 6) {
continue;
}
keypress = kevt.key;
break;
}
} while (pc_kbd.keyCount() == 0);
do {
kevt = pc_kbd.keyEvent();
} while (kevt.state != PCKeyboard::StateRelease);
switch (keypress) {
case 177:
keypress = 27;
break;
default:
// do nothing
break;
}
return keypress;
}
object *
fn_get_key(object *args, object *env)
{
(void) args;
(void) env;
return character(getkey());
}
#endif
#if defined(TDECK_PERI_POWERON)
#define touchscreen
#if defined(touchscreen)
#include "TouchDrvGT911.hpp"
TouchDrvGT911 touch;
#endif
#define TDECK_TOUCH_INT 16
#define TDECK_TRACKBALL_UP 3
#define TDECK_TRACKBALL_DOWN 15
#define TDECK_TRACKBALL_LEFT 1
#define TDECK_TRACKBALL_RIGHT 2
#define PLATFORM_TDECK
volatile int ball_val = 0;
// Touchscreen
void
initTouch()
{
#if defined (touchscreen)
pinMode(TDECK_TOUCH_INT, INPUT);
touch.setPins(-1, TDECK_TOUCH_INT);\
//keyboard already initialized the I2C?
if (!touch.begin(Wire1, GT911_SLAVE_ADDRESS_L)) {
while (1) {
Serial.println("Failed to find GT911 - check your wiring!");
delay(1000);
}
}
// Set touch max xy
touch.setMaxCoordinates(320, 240);
// Set swap xy
touch.setSwapXY(true);
// Set mirror xy
touch.setMirrorXY(false, true);
#endif
}
void
ISR_trackball_up()
{
ball_val = 218;
}
void
ISR_trackball_down()
{
ball_val = 217;
}
void
ISR_trackball_left()
{
ball_val = 216;
}
void
ISR_trackball_right ()
{
ball_val = 215;
}
void
inittrackball()
{
pinMode(TDECK_TRACKBALL_UP, INPUT_PULLUP);
pinMode(TDECK_TRACKBALL_DOWN, INPUT_PULLUP);
pinMode(TDECK_TRACKBALL_LEFT, INPUT_PULLUP);
pinMode(TDECK_TRACKBALL_RIGHT, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(TDECK_TRACKBALL_UP), ISR_trackball_up, FALLING);
attachInterrupt(digitalPinToInterrupt(TDECK_TRACKBALL_DOWN), ISR_trackball_down, FALLING);
attachInterrupt(digitalPinToInterrupt(TDECK_TRACKBALL_LEFT), ISR_trackball_left, FALLING);
attachInterrupt(digitalPinToInterrupt(TDECK_TRACKBALL_RIGHT), ISR_trackball_right, FALLING);
}
object *
fn_get_touch_points(object *args, object *env)
{
#if defined(touchscreen)
int16_t x[5], y[5];
uint8_t touched = 0;
object *result = nil;
do {
touched = touch.getPoint(x, y, touch.getSupportTouchPoint());
if (touched > 0) {
//start from the end of the list so we dont have to reverse it
for (int i = touched; i > 0; --i) {
result = cons(cons(number(x[i-1]), number(y[i-1])), result);
}
}
} while(touch.isPressed());
return result;
#else
return nil;
#endif
}
bool
isScreenTouched()
{
bool received_touch = false;
// Clear any previous readings since it buffers those.
do {
int16_t x[5], y[5];
uint8_t touched = touch.getPoint(x, y, touch.getSupportTouchPoint());
} while(touch.isPressed());
// touch.ispressed() will trigger like 5 times if you press it once so
// we have to loop through it and get the touchpoints
do {
int16_t x[5], y[5];
uint8_t touched = touch.getPoint(x, y, touch.getSupportTouchPoint());
if (touched > 0) {
received_touch = true;
}
} while(touch.isPressed());
return received_touch;
}
// T-Deck extras
char
touchKeyModEditor(char temp)
{
#if defined (touchscreen)
/* t-deck / blackberry keyboard missing symbols
missing mapped alt symbol
` k '
~ p @
% $
^ a *
& q #
= o +
< t (
> y )
\ u _
| g /
[ alt-t (
] alt-y )
{ n/a
} n/a
tab space
while holding the touch screen
c --- quit editor and return to REPL
n --- discard current text buffer (i.e. new file)
backspace --- delete line starting at cursor position
trackball left --- move cursor to start of line
trackball right --- move cursor to end of line
^ --- move cursor to beginning of buffer
trackball up / trackball down --- move one page up or down
Fn-h --- help menu
Fn-( --- toggle bracket matching on/off
Fn-) --- check if bracket under the cursor has a matching bracket
in the buffer. If so, they are temporarily highlighted.
(Use when continuous bracket matching is off.)
Fn-b --- bind contents of the text buffer to a symbol of your choice and quit editor
Fn-d --- delete a file on the SD card
Fn-s --- save text buffer to SD card
Fn-l --- load text from SD card into buffer, discarding the present one
Fn-i --- show directory of SD card
*/
if (isScreenTouched()) {
if (temp == 'k') return '`';
else if (temp == 'p') return '~';
else if (temp == '$') return '%';
else if (temp == 'a') return '^';
else if (temp == 'q') return '&';
else if (temp == 'o') return '=';
else if (temp == 't') return '<';
else if (temp == 'y') return '>';
else if (temp == 'u') return '\\';
else if (temp == 'g') return '|';
else if (temp == '(') return '[';
else if (temp == ')') return ']';
else if (temp == ' ') return '\t';
else if (temp == 'c') return (char)17; //quit
else if (temp == 'n') return (char)24; //new
else if (temp == 8) return (char)12; //delete line
else if (temp == '*') return (char)94; //beginning
else if (temp == 'h') return (char)16; //help
else if (temp == 's') return (char)203; //save
else if (temp == 'l') return (char)204; //load
else if (temp == 'd') return (char)202; //delete
else if (temp == 'b') return (char)198; //bind
else if (temp == 'i') return (char)205; //show dir
else if (temp == '1') return (char)194; //toggle bracket
else if (temp == '2') return (char)195; //highlight
}
#else
if (temp == '@') temp = '~';
if (temp == '_') temp = '\\';
#endif
return temp;
}
object *
fn_KeyboardGetKey(object *args, object *env)
{
(void) env, (void) args;
Wire1.requestFrom(0x55, 1);
if (Wire1.available()) {
char temp = Wire1.read();
if ((temp != 0) && (temp !=255)){
temp = touchKeyModEditor(temp);
//Serial.println((int)temp);
return number(temp);
}
}
if (ball_val != 0) {
int temp = ball_val;
ball_val = 0;
if (isScreenTouched()) {
// ((or 1 210) (se:linestart))
// ((or 5 213) (se:lineend))
// (211 (se:prevpage))
// (214 (se:nextpage))
switch(temp){
case 218: temp = 211; break; //up
case 217: temp = 214; break; //down
case 216: temp = 210; break; //left
case 215: temp = 213; break; //right
}
}
return number(temp);
}
return nil;
}
/*
(keyboard-flush)
Discard missing key up/down events.
*/
object *
fn_KeyboardFlush(object *args, object *env)
{
(void) args, (void) env;
return nil;
}
#endif
/*
* SYMBOL NAMES
*
* Define symbol names as const char[] PROGMEM here.
*/
const char stringlambdap[] PROGMEM = "lambdap";
const char stringnow[] PROGMEM = "now";
const char string_sym_def[] PROGMEM = "symdef";
const char stringbcd_to_dec[] PROGMEM = "bcd-to-dec";
const char stringdec_to_bcd[] PROGMEM = "dec-to-bcd";
const char stringlist_library2[] PROGMEM = "list-library2";
const char stringplatform[] PROGMEM = "platform";
const char stringSearchStr[] PROGMEM = "search-str";
const char stringsearchn[] PROGMEM = "searchn";
#if defined(sdcardsupport)
const char stringsd_rename[] PROGMEM = "sd-rename";
const char stringsd_remove[] PROGMEM = "sd-remove";
const char stringsd_existsp[] PROGMEM = "sd-exists-p";
const char stringsd_mkdir[] PROGMEM = "sd-make-dir";
const char stringsd_rmdir[] PROGMEM = "sd-remove-dir";
const char stringsd_dir[] PROGMEM = "sd-list";
#endif
#if defined(PLATFORM_PICOCALC)
const char string_get_key[] PROGMEM = "get-key";
#elif defined(TDECK_PERI_POWERON)
const char string_gettouchpoints[] PROGMEM = "get-touch-points";
const char stringKeyboardGetKey[] PROGMEM = "keyboard-get-key";
const char stringKeyboardFlush[] PROGMEM = "keyboard-flush";
const char stringSearchStr[] PROGMEM = "search-str";
const char stringsearchn[] PROGMEM = "searchn";
#elif defined(TDECK_PERI_POWERON)
const char string_gettouchpoints[] PROGMEM = "get-touch-points";
const char stringKeyboardGetKey[] PROGMEM = "keyboard-get-key";
const char stringKeyboardFlush[] PROGMEM = "keyboard-flush";
#endif
/*
* DOCUMENTATION STRINGS
*
* Define documentation strings as const char[] PROGMEM here.
*/
const char doclambdap[] PROGMEM = "(lambdap x)"
"Returns t if the form passed in is a lambda.";
const char docnow[] PROGMEM = "(now [hh mm ss])\n"
"Sets the current time, or with no arguments returns the current time\n"
"as a list of three integers (hh mm ss).";
const char doc_sym_def[] PROGMEM = "(symbol-def symbol [str])\n"
"Prints the definition of a symbol (variable or function) defined in\n"
"ulisp using the pretty printer."
"If str is specified it prints to the specified stream.\n"
"It returns no value.";
const char doc_bcd_to_dec[] PROGMEM = "(bcd-to-dec n)\n"
"Convert the BCD-encoded number n to decimal.";
const char doc_dec_to_bcd[] PROGMEM = "(dec-to-bcd n)\n"
"BCD encode n.";
const char doc_list_library2[] PROGMEM = "(list-library2)\n"
"Return a list of all symbols in the current Lisp library.";
const char doc_platform[] PROGMEM = "(platform)\n"
"Returns a keyword with the current platform. Supports :picocalc,\n"
":t-deck, and :teensy41. Otherwise, returns :unknown.";
const char docSearchStr[] PROGMEM = "(search pattern target [startpos])\n"
"Returns the index of the first occurrence of pattern in target, or nil if it's not found\n"
"starting from startpos";
const char docsearchn[] PROGMEM = "(searchn pattern target [n])\n"
"Returns the index of the nth occurrence of pattern in target,\n"
"which can be lists or strings, or nil if it's not found.\n"
"if the pattern occured more than once but less than n times, it returns the last occuring index";
// SD card doc strings
#if defined(sdcardsupport)
const char docsd_rename[] PROGMEM = "(sd-rename from to)\n"
"Renames the file named by 'from' to 'to.'";
const char docsd_remove[] PROGMEM = "(sd-remove file)\n"
"Removes the named file from the filesystem. Returns t if the file\n"
"was remove successfully.";
const char docsd_existsp[] PROGMEM = "(sd-exists-p file)\n"
"Returns t if the named file exists.";
const char docsd_dir[] PROGMEM = "(sd-list [directory])\n"
"Returns a list of filenames in the root or named directory.";
const char docsd_mkdir[] PROGMEM = "(sd-make-dir directory)\n"
"Create a directory with the specified name. Returns t if \n"
"successful, otherwise nil.";
const char docsd_rmdir[] PROGMEM = "(sd-remove-dir directory)\n"
"Remove the named directory. Returns t if successful, otherwise nil.";
#endif
// PicoCalc-specific doc strings
#if defined(PLATFORM_PICOCALC)
const char doc_get_key[] PROGMEM = "(get-key)\n"
"Waits for a keypress event, then returns the key.";
// T-Deck-specific doc strings
#elif defined(TDECK_PERI_POWERON)
const char doc_gettouchpoints[] PROGMEM = "(get-touch-points)\n"
"Returns all the points being touched on the screen in a list of x,y pairs or an empty list";
const char docKeyboardGetKey[] PROGMEM = "(keyboard-get-key [pressed])\n"
"Get key last recognized - default: when released, if [pressed] is t: when pressed).";
const char docKeyboardFlush[] PROGMEM = "(keyboard-flush)\n"
"Discard missing key up/down events.";
// End of platform-specific doc strings.
#endif
// Symbol lookup table
const tbl_entry_t lookup_table2[] PROGMEM = {
{ stringlambdap, fn_lambdap, 0211, doclambdap },
{ stringnow, fn_now, 0203, docnow },
{ string_sym_def, fn_sym_def, 0212, doc_sym_def },
{ stringbcd_to_dec, fn_bcd_to_dec, 0211, doc_bcd_to_dec },
{ stringdec_to_bcd, fn_dec_to_bcd, 0211, doc_dec_to_bcd },
{ stringlist_library2, fn_listlibrary2, 0200, doc_list_library2 },
{ stringplatform, fn_platform, 0200, doc_platform },
{ stringSearchStr, fn_searchstr, 0224, docSearchStr },
{ stringsearchn, fn_searchn, 0223, docsearchn },
#if defined(sdcardsupport)
{ stringsd_rename, fn_sd_rename, 0222, docsd_rename },
{ stringsd_remove, fn_sd_remove, 0211, docsd_remove },
{ stringsd_existsp, fn_sd_existsp, 0211, docsd_existsp },
{ stringsd_dir, fn_sd_list, 0201, docsd_dir },
{ stringsd_mkdir, fn_sd_mkdir, 0211, docsd_mkdir },
{ stringsd_rmdir, fn_sd_rmdir, 0211, docsd_rmdir },
#endif
#if defined(PLATFORM_PICOCALC)
{ string_get_key, fn_get_key, 0200, doc_get_key },
#elif defined(TDECK_PERI_POWERON)
{ string_gettouchpoints, fn_get_touch_points, 0200, doc_gettouchpoints },
{ stringKeyboardGetKey, fn_KeyboardGetKey, 0201, docKeyboardGetKey },
{ stringKeyboardFlush, fn_KeyboardFlush, 0200, docKeyboardFlush },
#endif
};
// Table cross-reference functions
tbl_entry_t *tables[] = {lookup_table, lookup_table2};
const unsigned int tablesizes[] = { arraysize(lookup_table), arraysize(lookup_table2) };
const tbl_entry_t *
table(int n)
{
return tables[n];
}
unsigned int
tablesize(int n)
{
return tablesizes[n];
}