mirror of
https://gitlab.com/c3d/db48x.git
synced 2024-09-29 05:36:58 +02:00
Some progress towards having a real editor
Signed-off-by: Christophe de Dinechin <christophe@dinechin.org>
This commit is contained in:
parent
18a0bb3941
commit
884b3d164e
10 changed files with 1179 additions and 1187 deletions
1
Makefile
1
Makefile
|
@ -51,6 +51,7 @@ DECIMAL_SOURCES=$(DECIMAL_SIZES:%=src/decimal-%.cc)
|
|||
# C++ sources
|
||||
CXX_SOURCES += \
|
||||
src/main.cc \
|
||||
src/input.cc \
|
||||
src/menu.cc \
|
||||
src/util.cc \
|
||||
src/settings.cc \
|
||||
|
|
21
sim/dmcp.cpp
21
sim/dmcp.cpp
|
@ -317,6 +317,27 @@ void lcd_switchFont(disp_stat_t * ds, int nr)
|
|||
else
|
||||
fprintf(stderr, "lcd_switchFont not implemented\n");
|
||||
}
|
||||
|
||||
int lcd_charWidth(disp_stat_t * ds, int c)
|
||||
{
|
||||
int width = 0;
|
||||
const line_font_t *f = ds->f;
|
||||
byte first = f->first_char;
|
||||
byte count = f->char_cnt;
|
||||
const uint16_t *offs = f->offs;
|
||||
const uint8_t *data = f->data;
|
||||
uint xspc = ds->xspc;
|
||||
|
||||
c -= first;
|
||||
if (c >= 0 && c < count)
|
||||
{
|
||||
uint off = offs[c];
|
||||
width += data[off + 0] + data[off + 2] + xspc;
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
int lcd_textWidth(disp_stat_t * ds, const char* text)
|
||||
{
|
||||
int width = 0;
|
||||
|
|
|
@ -34,6 +34,7 @@ SOURCES += \
|
|||
../src/menu.cc \
|
||||
../src/main.cc \
|
||||
../src/util.cc \
|
||||
../src/input.cc \
|
||||
../src/settings.cc \
|
||||
../src/object.cc \
|
||||
../src/integer.cc \
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
#define KB_ON 33 //! ON
|
||||
#define KB_ESC 33 //! Exit
|
||||
#define KB_DOT 35 //! Dot
|
||||
#define KB_SPC 36 //! Space
|
||||
#define KB_SPC 37 //! Space
|
||||
#define KB_RUNSTOP 36 //! R/S
|
||||
#define KB_QUESTION 36 //! ?
|
||||
#define KB_SHIFT 28 //! Shift
|
||||
|
|
2
src/id.h
2
src/id.h
|
@ -27,6 +27,8 @@
|
|||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// ****************************************************************************
|
||||
|
||||
ID(object) // Value 0 is reserved for "not implemented"
|
||||
|
||||
ID(string)
|
||||
|
||||
ID(integer)
|
||||
|
|
825
src/input.cc
Normal file
825
src/input.cc
Normal file
|
@ -0,0 +1,825 @@
|
|||
// ****************************************************************************
|
||||
// input.cc DB48X project
|
||||
// ****************************************************************************
|
||||
//
|
||||
// File Description:
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// ****************************************************************************
|
||||
// (C) 2022 Christophe de Dinechin <christophe@dinechin.org>
|
||||
// This software is licensed under the terms outlined in LICENSE.txt
|
||||
// ****************************************************************************
|
||||
// This file is part of DB48X.
|
||||
//
|
||||
// DB48X is free software: you can redistribute it and/or modify
|
||||
// it under the terms outlined in the LICENSE.txt file
|
||||
//
|
||||
// DB48X 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.
|
||||
// ****************************************************************************
|
||||
|
||||
#include "input.h"
|
||||
|
||||
#include "runtime.h"
|
||||
#include "settings.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <dmcp.h>
|
||||
#include <stdio.h>
|
||||
|
||||
enum { LCD_W = LCD_X, LCD_H = LCD_Y };
|
||||
|
||||
// The primary input of the calculator
|
||||
input Input;
|
||||
|
||||
runtime &input::RT = runtime::RT;
|
||||
|
||||
input::input()
|
||||
// ----------------------------------------------------------------------------
|
||||
// Initialize the input
|
||||
// ----------------------------------------------------------------------------
|
||||
: cursor(0), xoffset(0), mode(STACK), last(0),
|
||||
shift(false), xshift(false), alpha(false), lowercase(false),
|
||||
hideMenu(false), down(false), up(false)
|
||||
{}
|
||||
|
||||
|
||||
bool input::key(int key)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Process an input key
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
bool result =
|
||||
handle_shifts(key) ||
|
||||
handle_editing(key) ||
|
||||
handle_alpha(key) ||
|
||||
handle_functions(key);
|
||||
|
||||
if (key && key != KEY_SHIFT)
|
||||
shift = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void input::assign(int key, uint plane, object *code)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Assign an object to a given key
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
if (key >= 1 && key <= NUM_KEYS && plane <= NUM_PLANES)
|
||||
function[plane][key-1] = code;
|
||||
}
|
||||
|
||||
|
||||
object *input::assigned(int key, uint plane)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Assign an object to a given key
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
if (key >= 1 && key <= NUM_KEYS && plane <= NUM_PLANES)
|
||||
return function[plane][key-1];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void input::menus(cstring labels[input::NUM_MENUS],
|
||||
object *function[input::NUM_MENUS])
|
||||
// ----------------------------------------------------------------------------
|
||||
// Assign all menus at once
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
for (int m = 0; m < NUM_MENUS; m++)
|
||||
menu(m, labels[m], function[m]);
|
||||
}
|
||||
|
||||
|
||||
void input::menu(uint menu_id, cstring label, object *fn)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Assign one menu item
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
if (menu_id < NUM_MENUS)
|
||||
{
|
||||
int softkey_id = menu_id % NUM_SOFTKEYS;
|
||||
int key = KEY_F1 + softkey_id;
|
||||
int plane = menu_id / NUM_SOFTKEYS;
|
||||
function[plane][key] = fn;
|
||||
strncpy(menu_label[plane][softkey_id], label, NUM_LABEL_CHARS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void input::draw_menus()
|
||||
// ----------------------------------------------------------------------------
|
||||
// Draw the softkey menus
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
int plane = shift_plane();
|
||||
cstring labels[NUM_SOFTKEYS];
|
||||
for (int k = 0; k < NUM_SOFTKEYS; k++)
|
||||
labels[k] = menu_label[plane][k];
|
||||
lcd_draw_menu_keys(labels);
|
||||
}
|
||||
|
||||
|
||||
void input::draw_annunciators()
|
||||
// ----------------------------------------------------------------------------
|
||||
// Draw the annunciators for Shift, Alpha, etc
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
// Don't clear line (we expect dark background already drawn)
|
||||
if (alpha)
|
||||
{
|
||||
cstring label = lowercase ? "abc" : "ABC";
|
||||
t20->lnfill = false;
|
||||
t20->x = LCD_W - lcd_textWidth(t20, label) - 3;
|
||||
t20->y = lcd_lineHeight(t20) + 2;
|
||||
lcd_puts(t20, label);
|
||||
}
|
||||
if (shift)
|
||||
{
|
||||
const uint ann_height = 12;
|
||||
static const byte ann_right[] =
|
||||
{
|
||||
0xfe, 0x3f, 0xff, 0x7f, 0x9f, 0x7f, 0xcf, 0x7f, 0xe7, 0x7f, 0x03, 0x78,
|
||||
0x03, 0x70, 0xe7, 0x73, 0xcf, 0x73, 0x9f, 0x73, 0xff, 0x73, 0xfe, 0x33
|
||||
};
|
||||
static const byte ann_left[] =
|
||||
{
|
||||
0xfe, 0x3f, 0xff, 0x7f, 0xff, 0x7c, 0xff, 0x79, 0xff, 0x73, 0x0f, 0x60,
|
||||
0x07, 0x60, 0xe7, 0x73, 0xe7, 0x79, 0xe7, 0x7c, 0xe7, 0x7f, 0xe6, 0x3f
|
||||
};
|
||||
const byte *source = xshift ? ann_right : ann_left;
|
||||
int top = lcd_lineHeight(t20) + 2;
|
||||
for (uint r = 0; r < ann_height; r++)
|
||||
{
|
||||
byte *dest = lcd_line_addr(r + top) + 48;
|
||||
dest[0] = *source++;
|
||||
dest[1] = *source++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void input::draw_editor()
|
||||
// ----------------------------------------------------------------------------
|
||||
// Draw the editor
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
// Get the editor area
|
||||
char *ed = RT.editor();
|
||||
size_t len = RT.editing();
|
||||
char *last = ed + len;
|
||||
char *edline = ed;
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
// Count rows and colums
|
||||
int rows = 1; // Number of rows in editor
|
||||
int column = 0; // Current column
|
||||
int cwidth = 0; // Column width
|
||||
int edrow = 0; // Row number of line being edited
|
||||
int edcol = 0; // Column of line being edited
|
||||
int cursx = 0; // Cursor X position
|
||||
|
||||
for (char *p = ed; p <= last; p++)
|
||||
{
|
||||
if (p - ed == (int) cursor)
|
||||
{
|
||||
edrow = rows - 1;
|
||||
edcol = column;
|
||||
edline = p - edcol;
|
||||
cursx = cwidth;
|
||||
}
|
||||
if (p == last)
|
||||
break;
|
||||
|
||||
if (*p == '\n')
|
||||
{
|
||||
rows++;
|
||||
column = 0;
|
||||
cwidth = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
column++;
|
||||
cwidth += lcd_charWidth(fReg, (byte) *p);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we want to move the cursor up or down
|
||||
if (up || down)
|
||||
{
|
||||
int r = 0;
|
||||
int c = 0;
|
||||
int tgt = edrow - up + down;
|
||||
bool done = false;
|
||||
for (char *p = ed; p < last && !done; p++)
|
||||
{
|
||||
if (*p == '\n')
|
||||
{
|
||||
r++;
|
||||
c = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
c++;
|
||||
}
|
||||
if ((r == tgt && c >= edcol) || r > tgt)
|
||||
{
|
||||
cursor = p - ed;
|
||||
edrow = r;
|
||||
edline = p - c;
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
up = false;
|
||||
down = false;
|
||||
}
|
||||
|
||||
// Draw the area that fits on the screen
|
||||
int lineHeight = lcd_lineHeight(fReg);
|
||||
int top = lcd_lineHeight(t20) + 2;
|
||||
int bottom = LCD_H - (hideMenu ? 0 : LCD_MENU_LINES);
|
||||
int availableHeight = bottom - top;
|
||||
int availableRows = availableHeight / lineHeight;
|
||||
char *display = ed;
|
||||
|
||||
if (rows > availableRows)
|
||||
{
|
||||
// Skip rows to show the cursor
|
||||
int skip =
|
||||
edrow < availableRows ? 0 :
|
||||
edrow >= rows - availableRows ? rows - availableRows :
|
||||
edrow - availableRows / 2;
|
||||
for (int r = 0; r < skip; r++)
|
||||
while (*display != '\n')
|
||||
display++;
|
||||
rows = availableRows;
|
||||
}
|
||||
|
||||
// Draw the editor rows
|
||||
int skip = 64;
|
||||
int cursw = t20->f->width;
|
||||
if (xoffset > cursx)
|
||||
xoffset = (cursx > skip) ? cursx - skip : 0;
|
||||
else if (xoffset + LCD_W - cursw < cursx)
|
||||
xoffset = cursx - LCD_W + cursw + skip;
|
||||
|
||||
int y = bottom - rows * lineHeight;
|
||||
int x = -xoffset;
|
||||
fReg->x = x;
|
||||
fReg->y = y;
|
||||
fReg->lnfill = false;
|
||||
fReg->bgfill = false;
|
||||
t20->inv = true;
|
||||
t20->lnfill = false;
|
||||
t20->bgfill = false;
|
||||
|
||||
for (int r = 0; r < rows && display < last; r++)
|
||||
{
|
||||
int drawCursor = display == edline;
|
||||
while (display < last && *display != '\n')
|
||||
{
|
||||
int c = *display++;
|
||||
int cw = lcd_charWidth(fReg, c);
|
||||
if (fReg->x >= 0 && fReg->x + cw < LCD_W)
|
||||
{
|
||||
const char buf[2] = { (char) c, 0 };
|
||||
lcd_writeText(fReg, buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
fReg->x += cw;
|
||||
}
|
||||
}
|
||||
|
||||
if (drawCursor)
|
||||
{
|
||||
char cursorChar =
|
||||
mode == DIRECT ? 'd' :
|
||||
mode == TEXT ? (lowercase ? 'l' : 'c') :
|
||||
mode == PROGRAM ? 'p' :
|
||||
mode == ALGEBRAIC ? 'a' :
|
||||
mode == MATRIX ? 'm' : 'x';
|
||||
char buf[2] = { cursorChar, 0 };
|
||||
lcd_fill_rect(x + cursx, y, 2, lineHeight, 1);
|
||||
t20->x = x + cursx;
|
||||
t20->y = y + (lineHeight - top) / 2 + 1;
|
||||
t20->bgfill = true;
|
||||
lcd_putsR(t20, buf);
|
||||
t20->bgfill = false;
|
||||
}
|
||||
|
||||
fReg->x = x;
|
||||
fReg->y += lineHeight;
|
||||
}
|
||||
|
||||
// Restore display state
|
||||
fReg->inv = false;
|
||||
fReg->lnfill = true;
|
||||
fReg->bgfill = true;
|
||||
t20->inv = false;
|
||||
t20->lnfill = true;
|
||||
t20->bgfill = true;
|
||||
}
|
||||
|
||||
|
||||
void input::draw_error()
|
||||
// ----------------------------------------------------------------------------
|
||||
// Draw the error message if there is one
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
if (cstring err = RT.error())
|
||||
{
|
||||
const int b = 4;
|
||||
lcd_switchFont(fReg, 5);
|
||||
|
||||
int lineHeight = lcd_lineHeight(fReg);
|
||||
int top = lcd_lineHeight(t20) + 10;
|
||||
int height = LCD_H / 3;
|
||||
int width = LCD_W - 20;
|
||||
int x = LCD_W / 2 - width / 2;
|
||||
int y = top;
|
||||
|
||||
lcd_fill_rect(x, y, width, height, 1);
|
||||
lcd_fill_rect(x + b, y + b, width - 2*b, height - 2*b, 0);
|
||||
|
||||
x += 2*b+1;
|
||||
y += 2*b+1;
|
||||
fReg->x = x;
|
||||
fReg->y = y;
|
||||
fReg->lnfill = false;
|
||||
fReg->bgfill = false;
|
||||
if (cstring cmd = RT.command())
|
||||
{
|
||||
lcd_print(fReg, "%s error:", cmd);
|
||||
fReg->y += lineHeight;
|
||||
}
|
||||
lcd_puts(fReg, err);
|
||||
fReg->lnfill = true;
|
||||
fReg->bgfill = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool input::handle_shifts(int key)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Handle status changes in shift keys
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
bool consumed = false;
|
||||
if (key == KEY_SHIFT)
|
||||
{
|
||||
#define SHM(d, a, s) ((d<<2) | (a<<1) | (s<<0))
|
||||
#define SHD(d, a, s) (1 << SHM(d, a, s))
|
||||
bool dshift = last == KEY_SHIFT; // Double shift toggles alpha
|
||||
int plane = SHM(dshift, alpha, shift);
|
||||
const unsigned nextShift =
|
||||
SHD(0, 0, 0) |
|
||||
SHD(0, 1, 0) |
|
||||
SHD(1, 0, 0) |
|
||||
SHD(1, 1, 0);
|
||||
const unsigned nextAlpha =
|
||||
SHD(0, 0, 1) |
|
||||
SHD(0, 1, 0) |
|
||||
SHD(0, 1, 1) |
|
||||
SHD(1, 0, 1);
|
||||
shift = (nextShift & (1 << plane)) != 0;
|
||||
alpha = (nextAlpha & (1 << plane)) != 0;
|
||||
consumed = true;
|
||||
#undef SHM
|
||||
#undef SHD
|
||||
}
|
||||
|
||||
if (key)
|
||||
{
|
||||
last = key;
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
|
||||
bool input::handle_editing(int key)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Some keys always deal with editing
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
bool consumed = false;
|
||||
size_t editing = RT.editing();
|
||||
|
||||
if (editing)
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
case KEY_BSP:
|
||||
if (shift && cursor < editing)
|
||||
{
|
||||
// Shift + Backspace = Delete to right of cursor
|
||||
RT.remove(cursor, 1);
|
||||
}
|
||||
else if (!shift && cursor > 0)
|
||||
{
|
||||
// Backspace = Erase on left of cursor
|
||||
cursor--;
|
||||
RT.remove(cursor, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Limits of line: beep
|
||||
beep(4400, 50);
|
||||
}
|
||||
return true;
|
||||
case KEY_ENTER:
|
||||
{
|
||||
if (shift)
|
||||
{
|
||||
// TODO: Show Alpha menu
|
||||
// For now, shift lowercase
|
||||
lowercase = !lowercase;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Finish editing and parse the result
|
||||
object *obj = object::parse(RT.editor());
|
||||
if (obj)
|
||||
{
|
||||
// We successfully parsed the line
|
||||
RT.clear();
|
||||
shift = false;
|
||||
alpha = false;
|
||||
cursor = 0;
|
||||
xoffset = 0;
|
||||
obj->evaluate();
|
||||
}
|
||||
else
|
||||
{
|
||||
cstring pos = RT.source();
|
||||
cstring ed = RT.editor();
|
||||
if (pos >= ed && pos <= ed + editing)
|
||||
cursor = pos - ed;
|
||||
beep(3300, 100);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case KEY_EXIT:
|
||||
if (shift)
|
||||
{
|
||||
SET_ST(STAT_PGM_END);
|
||||
shift = false;
|
||||
alpha = false;
|
||||
}
|
||||
else if (RT.error())
|
||||
{
|
||||
// Clear error
|
||||
RT.error(nullptr);
|
||||
shift = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clear the editor
|
||||
RT.clear();
|
||||
shift = false;
|
||||
alpha = false;
|
||||
}
|
||||
return true;
|
||||
|
||||
case KEY_UP:
|
||||
if (shift)
|
||||
up = true;
|
||||
else if (cursor > 0)
|
||||
cursor--;
|
||||
else
|
||||
beep(4000, 50);
|
||||
return true;
|
||||
case KEY_DOWN:
|
||||
if (shift)
|
||||
down = true;
|
||||
else if (cursor < editing)
|
||||
cursor++;
|
||||
else
|
||||
beep(4800, 50);
|
||||
return true;
|
||||
case 0:
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
case KEY_BSP:
|
||||
// RT.evaluate(ID_drop);
|
||||
return true;
|
||||
case KEY_ENTER:
|
||||
// RT.evaluate(ID_dup);
|
||||
return true;
|
||||
case KEY_EXIT:
|
||||
if (shift)
|
||||
SET_ST(STAT_PGM_END);
|
||||
shift = false;
|
||||
alpha = false;
|
||||
return true;
|
||||
case KEY_UP:
|
||||
// RT.evaluate(ID_stack);
|
||||
return false;
|
||||
case KEY_DOWN:
|
||||
// Bring the object to the editor
|
||||
return false;
|
||||
case 0:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
|
||||
bool input::handle_alpha(int key)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Handle alphabetic input
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
if (!alpha || !key)
|
||||
return false;
|
||||
|
||||
static const char upper[] =
|
||||
"ABCDEF"
|
||||
"GHIJKL"
|
||||
"_MNO_"
|
||||
"_PQRS"
|
||||
"_TUVW"
|
||||
"_XYZ_"
|
||||
"_:.? ";
|
||||
static const char lower[] =
|
||||
"abcdef"
|
||||
"ghijkl"
|
||||
"_mno_"
|
||||
"_pqrs"
|
||||
"_tuvw"
|
||||
"_xyz-"
|
||||
"_:.? ";
|
||||
static const char shifted[] =
|
||||
"\x85^\x82([{"
|
||||
"\x86%\x87<=>"
|
||||
"_\"'\x98_"
|
||||
"_789\x80"
|
||||
"_456\x81"
|
||||
"_123-"
|
||||
"_;,!+";
|
||||
|
||||
key--;
|
||||
char c = shift ? shifted[key] : lowercase ? lower[key] : upper[key];
|
||||
RT.insert(cursor, c);
|
||||
cursor++;
|
||||
|
||||
// Test delimiters
|
||||
int closing = 0;
|
||||
switch(c)
|
||||
{
|
||||
case '(': closing = ')'; break;
|
||||
case '[': closing = ']'; break;
|
||||
case '{': closing = '}'; break;
|
||||
case ':': closing = ':'; break;
|
||||
case '"': closing = '"'; break;
|
||||
case '\'': closing = '\''; break;
|
||||
}
|
||||
if (closing)
|
||||
RT.insert(cursor, closing);
|
||||
shift = false;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
//
|
||||
// Tables with the default assignments
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
static const byte defaultUnshiftedCommand[2*input::NUM_KEYS] =
|
||||
// ----------------------------------------------------------------------------
|
||||
// RPL code for the commands assigned by default to each key
|
||||
// ----------------------------------------------------------------------------
|
||||
// All the default-assigned commands fit in one or two bytes
|
||||
{
|
||||
#define OP2BYTES(key, id) \
|
||||
(id) < 0x80 ? (id) : ((id) & 0x7F) | 0x80, \
|
||||
(id) < 0x80 ? 0 : ((id) >> 7)
|
||||
|
||||
OP2BYTES(KEY_SIGMA, 0),
|
||||
OP2BYTES(KEY_INV, 0),
|
||||
OP2BYTES(KEY_SQRT, 0),
|
||||
OP2BYTES(KEY_LOG, 0),
|
||||
OP2BYTES(KEY_LN, 0),
|
||||
OP2BYTES(KEY_XEQ, 0),
|
||||
OP2BYTES(KEY_STO, 0),
|
||||
OP2BYTES(KEY_RCL, 0),
|
||||
OP2BYTES(KEY_RDN, 0),
|
||||
OP2BYTES(KEY_SIN, 0),
|
||||
OP2BYTES(KEY_COS, 0),
|
||||
OP2BYTES(KEY_TAN, 0),
|
||||
OP2BYTES(KEY_ENTER, 0),
|
||||
OP2BYTES(KEY_SWAP, 0),
|
||||
OP2BYTES(KEY_CHS, 0),
|
||||
OP2BYTES(KEY_E, 0),
|
||||
OP2BYTES(KEY_BSP, 0),
|
||||
OP2BYTES(KEY_UP, 0),
|
||||
OP2BYTES(KEY_7, 0),
|
||||
OP2BYTES(KEY_8, 0),
|
||||
OP2BYTES(KEY_9, 0),
|
||||
OP2BYTES(KEY_DIV, 0),
|
||||
OP2BYTES(KEY_DOWN, 0),
|
||||
OP2BYTES(KEY_4, 0),
|
||||
OP2BYTES(KEY_5, 0),
|
||||
OP2BYTES(KEY_6, 0),
|
||||
OP2BYTES(KEY_MUL, 0),
|
||||
OP2BYTES(KEY_SHIFT, 0),
|
||||
OP2BYTES(KEY_1, 0),
|
||||
OP2BYTES(KEY_2, 0),
|
||||
OP2BYTES(KEY_3, 0),
|
||||
OP2BYTES(KEY_SUB, 0),
|
||||
OP2BYTES(KEY_EXIT, 0),
|
||||
OP2BYTES(KEY_0, 0),
|
||||
OP2BYTES(KEY_DOT, 0),
|
||||
OP2BYTES(KEY_RUN, 0),
|
||||
OP2BYTES(KEY_ADD, 0),
|
||||
|
||||
OP2BYTES(KEY_F1, 0),
|
||||
OP2BYTES(KEY_F2, 0),
|
||||
OP2BYTES(KEY_F3, 0),
|
||||
OP2BYTES(KEY_F4, 0),
|
||||
OP2BYTES(KEY_F5, 0),
|
||||
OP2BYTES(KEY_F6, 0),
|
||||
|
||||
OP2BYTES(KEY_SCREENSHOT, 0),
|
||||
OP2BYTES(KEY_SH_UP, 0),
|
||||
OP2BYTES(KEY_SH_DOWN, 0),
|
||||
};
|
||||
|
||||
|
||||
static const byte defaultShiftedCommand[2*input::NUM_KEYS] =
|
||||
// ----------------------------------------------------------------------------
|
||||
// RPL code for the commands assigned by default to shifted keys
|
||||
// ----------------------------------------------------------------------------
|
||||
// All the default assigned commands fit in one or two bytes
|
||||
{
|
||||
OP2BYTES(KEY_SIGMA, 0),
|
||||
OP2BYTES(KEY_INV, 0),
|
||||
OP2BYTES(KEY_SQRT, 0),
|
||||
OP2BYTES(KEY_LOG, 0),
|
||||
OP2BYTES(KEY_LN, 0),
|
||||
OP2BYTES(KEY_XEQ, 0),
|
||||
OP2BYTES(KEY_STO, 0),
|
||||
OP2BYTES(KEY_RCL, 0),
|
||||
OP2BYTES(KEY_RDN, 0),
|
||||
OP2BYTES(KEY_SIN, 0),
|
||||
OP2BYTES(KEY_COS, 0),
|
||||
OP2BYTES(KEY_TAN, 0),
|
||||
OP2BYTES(KEY_ENTER, 0),
|
||||
OP2BYTES(KEY_SWAP, 0),
|
||||
OP2BYTES(KEY_CHS, 0),
|
||||
OP2BYTES(KEY_E, 0),
|
||||
OP2BYTES(KEY_BSP, 0),
|
||||
OP2BYTES(KEY_UP, 0),
|
||||
OP2BYTES(KEY_7, 0),
|
||||
OP2BYTES(KEY_8, 0),
|
||||
OP2BYTES(KEY_9, 0),
|
||||
OP2BYTES(KEY_DIV, 0),
|
||||
OP2BYTES(KEY_DOWN, 0),
|
||||
OP2BYTES(KEY_4, 0),
|
||||
OP2BYTES(KEY_5, 0),
|
||||
OP2BYTES(KEY_6, 0),
|
||||
OP2BYTES(KEY_MUL, 0),
|
||||
OP2BYTES(KEY_SHIFT, 0),
|
||||
OP2BYTES(KEY_1, 0),
|
||||
OP2BYTES(KEY_2, 0),
|
||||
OP2BYTES(KEY_3, 0),
|
||||
OP2BYTES(KEY_SUB, 0),
|
||||
OP2BYTES(KEY_EXIT, 0),
|
||||
OP2BYTES(KEY_0, 0),
|
||||
OP2BYTES(KEY_DOT, 0),
|
||||
OP2BYTES(KEY_RUN, 0),
|
||||
OP2BYTES(KEY_ADD, 0),
|
||||
|
||||
OP2BYTES(KEY_F1, 0),
|
||||
OP2BYTES(KEY_F2, 0),
|
||||
OP2BYTES(KEY_F3, 0),
|
||||
OP2BYTES(KEY_F4, 0),
|
||||
OP2BYTES(KEY_F5, 0),
|
||||
OP2BYTES(KEY_F6, 0),
|
||||
|
||||
OP2BYTES(KEY_SCREENSHOT, 0),
|
||||
OP2BYTES(KEY_SH_UP, 0),
|
||||
OP2BYTES(KEY_SH_DOWN, 0),
|
||||
};
|
||||
|
||||
|
||||
static const byte defaultLongShiftedCommand[2*input::NUM_KEYS] =
|
||||
// ----------------------------------------------------------------------------
|
||||
// RPL code for the commands assigned by default to long-shifted keys
|
||||
// ----------------------------------------------------------------------------
|
||||
// All the default assigned commands fit in one or two bytes
|
||||
{
|
||||
OP2BYTES(KEY_SIGMA, 0),
|
||||
OP2BYTES(KEY_INV, 0),
|
||||
OP2BYTES(KEY_SQRT, 0),
|
||||
OP2BYTES(KEY_LOG, 0),
|
||||
OP2BYTES(KEY_LN, 0),
|
||||
OP2BYTES(KEY_XEQ, 0),
|
||||
OP2BYTES(KEY_STO, 0),
|
||||
OP2BYTES(KEY_RCL, 0),
|
||||
OP2BYTES(KEY_RDN, 0),
|
||||
OP2BYTES(KEY_SIN, 0),
|
||||
OP2BYTES(KEY_COS, 0),
|
||||
OP2BYTES(KEY_TAN, 0),
|
||||
OP2BYTES(KEY_ENTER, 0),
|
||||
OP2BYTES(KEY_SWAP, 0),
|
||||
OP2BYTES(KEY_CHS, 0),
|
||||
OP2BYTES(KEY_E, 0),
|
||||
OP2BYTES(KEY_BSP, 0),
|
||||
OP2BYTES(KEY_UP, 0),
|
||||
OP2BYTES(KEY_7, 0),
|
||||
OP2BYTES(KEY_8, 0),
|
||||
OP2BYTES(KEY_9, 0),
|
||||
OP2BYTES(KEY_DIV, 0),
|
||||
OP2BYTES(KEY_DOWN, 0),
|
||||
OP2BYTES(KEY_4, 0),
|
||||
OP2BYTES(KEY_5, 0),
|
||||
OP2BYTES(KEY_6, 0),
|
||||
OP2BYTES(KEY_MUL, 0),
|
||||
OP2BYTES(KEY_SHIFT, 0),
|
||||
OP2BYTES(KEY_1, 0),
|
||||
OP2BYTES(KEY_2, 0),
|
||||
OP2BYTES(KEY_3, 0),
|
||||
OP2BYTES(KEY_SUB, 0),
|
||||
OP2BYTES(KEY_EXIT, 0),
|
||||
OP2BYTES(KEY_0, 0),
|
||||
OP2BYTES(KEY_DOT, 0),
|
||||
OP2BYTES(KEY_RUN, 0),
|
||||
OP2BYTES(KEY_ADD, 0),
|
||||
|
||||
OP2BYTES(KEY_F1, 0),
|
||||
OP2BYTES(KEY_F2, 0),
|
||||
OP2BYTES(KEY_F3, 0),
|
||||
OP2BYTES(KEY_F4, 0),
|
||||
OP2BYTES(KEY_F5, 0),
|
||||
OP2BYTES(KEY_F6, 0),
|
||||
|
||||
OP2BYTES(KEY_SCREENSHOT, 0),
|
||||
OP2BYTES(KEY_SH_UP, 0),
|
||||
OP2BYTES(KEY_SH_DOWN, 0),
|
||||
};
|
||||
|
||||
|
||||
static const byte *const defaultCommand[input::NUM_PLANES] =
|
||||
// ----------------------------------------------------------------------------
|
||||
// Pointers to the default commands
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
defaultUnshiftedCommand,
|
||||
defaultShiftedCommand,
|
||||
defaultLongShiftedCommand,
|
||||
};
|
||||
|
||||
|
||||
bool input::handle_functions(int key)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Check if we have one of the soft menu functions
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
if (!key)
|
||||
return false;
|
||||
int plane = shift_plane();
|
||||
object *obj = function[plane][key-1];
|
||||
if (obj)
|
||||
{
|
||||
obj->evaluate();
|
||||
return true;
|
||||
}
|
||||
const byte *ptr = defaultCommand[plane] + key-1;
|
||||
if (*ptr)
|
||||
{
|
||||
obj = (object *) ptr; // Uh oh! Evaluate bytecode in ROM
|
||||
obj->evaluate();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
115
src/input.h
Normal file
115
src/input.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
#ifndef INPUT_H
|
||||
#define INPUT_H
|
||||
// ****************************************************************************
|
||||
// input.h DB48X project
|
||||
// ****************************************************************************
|
||||
//
|
||||
// File Description:
|
||||
//
|
||||
// Calculator input
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// ****************************************************************************
|
||||
// (C) 2022 Christophe de Dinechin <christophe@dinechin.org>
|
||||
// This software is licensed under the terms outlined in LICENSE.txt
|
||||
// ****************************************************************************
|
||||
// This file is part of DB48X.
|
||||
//
|
||||
// DB48X is free software: you can redistribute it and/or modify
|
||||
// it under the terms outlined in the LICENSE.txt file
|
||||
//
|
||||
// DB48X 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.
|
||||
// ****************************************************************************
|
||||
|
||||
#include <types.h>
|
||||
#include <object.h>
|
||||
|
||||
struct runtime;
|
||||
|
||||
struct input
|
||||
// ----------------------------------------------------------------------------
|
||||
// Calculator input state
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
input();
|
||||
|
||||
enum modes
|
||||
// ------------------------------------------------------------------------
|
||||
// Current input mode
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
STACK, // Showing the stack, not editing
|
||||
DIRECT, // Keys like 'sin' evaluate directly
|
||||
TEXT, // Alphanumeric entry, e.g. in strings
|
||||
PROGRAM, // Keys like 'sin' show as 'sin' in the editor
|
||||
ALGEBRAIC, // Keys like 'sin' show as 'sin()'
|
||||
MATRIX, // Matrix/vector mode
|
||||
};
|
||||
|
||||
enum
|
||||
// ------------------------------------------------------------------------
|
||||
// Dimensioning constants
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
NUM_PLANES = 3, // NONE, Shift and "extended" shift
|
||||
NUM_KEYS = 46, // Including SCREENSHOT, SH_UP and SH_DN
|
||||
NUM_SOFTKEYS = 6, // Number of softkeys
|
||||
NUM_LABEL_CHARS = 10, // Number of characters per menu label
|
||||
NUM_MENUS = NUM_PLANES * NUM_SOFTKEYS,
|
||||
};
|
||||
|
||||
|
||||
bool key(int key);
|
||||
void assign(int key, uint plane, object *code);
|
||||
object * assigned(int key, uint plane);
|
||||
void menus(cstring labels[NUM_MENUS], object *function[NUM_MENUS]);
|
||||
void menu(uint index, cstring labels, object *function);
|
||||
void draw_menus();
|
||||
void draw_annunciators();
|
||||
void draw_editor();
|
||||
void draw_error();
|
||||
|
||||
protected:
|
||||
bool handle_shifts(int key);
|
||||
bool handle_editing(int key);
|
||||
bool handle_alpha(int key);
|
||||
bool handle_functions(int key);
|
||||
|
||||
bool handle_enter(int key);
|
||||
bool handle_backspace(int key);
|
||||
|
||||
uint shift_plane() { return xshift ? 2 : shift ? 1 : 0; }
|
||||
|
||||
|
||||
protected:
|
||||
uint cursor; // Cursor position in buffer
|
||||
int xoffset; // Offset of the cursor
|
||||
modes mode; // Current editing mode
|
||||
int last; // Last key
|
||||
bool shift : 1; // Normal shift active
|
||||
bool xshift : 1; // Extended shift active (simulate Right)
|
||||
bool alpha : 1; // Alpha mode active
|
||||
bool lowercase : 1; // Lowercase
|
||||
bool hideMenu : 1; // Hide the menu
|
||||
bool down : 1; // Move one line down
|
||||
bool up : 1; // Move one line up
|
||||
protected:
|
||||
// Key mappings
|
||||
object *function[NUM_PLANES][NUM_KEYS];
|
||||
char menu_label[NUM_PLANES][NUM_SOFTKEYS][NUM_LABEL_CHARS];
|
||||
static runtime &RT;
|
||||
};
|
||||
|
||||
|
||||
extern input Input;
|
||||
|
||||
|
||||
#endif // INPUT_H
|
1198
src/main.cc
1198
src/main.cc
File diff suppressed because it is too large
Load diff
136
src/object.h
136
src/object.h
|
@ -77,6 +77,41 @@ struct object
|
|||
~object() {}
|
||||
|
||||
|
||||
// ========================================================================
|
||||
//
|
||||
// Object command protocol
|
||||
//
|
||||
// ========================================================================
|
||||
|
||||
enum command
|
||||
// ------------------------------------------------------------------------
|
||||
// The commands that all handlers must deal with
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
EVAL, // Evaluate the object (e.g. push on stack)
|
||||
SIZE, // Compute the size of the object
|
||||
PARSE, // Parse the object
|
||||
RENDER, // Render the object
|
||||
};
|
||||
|
||||
enum result
|
||||
// ------------------------------------------------------------------------
|
||||
// Common return values for handlers
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
OK = 0, // Command ran successfully
|
||||
SKIP = -1, // Command not for this handler, try next
|
||||
ERROR = -2, // Error processing the command
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ========================================================================
|
||||
//
|
||||
// Memory management
|
||||
//
|
||||
// ========================================================================
|
||||
|
||||
static size_t required_memory(id i)
|
||||
// ------------------------------------------------------------------------
|
||||
// Compute the amount of memory required for an object
|
||||
|
@ -85,6 +120,52 @@ struct object
|
|||
return leb128size(i);
|
||||
}
|
||||
|
||||
|
||||
id type()
|
||||
// ------------------------------------------------------------------------
|
||||
// Return the type of the object
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
byte *ptr = (byte *) this;
|
||||
return (id) leb128(ptr);
|
||||
}
|
||||
|
||||
|
||||
size_t size(runtime &rt = RT)
|
||||
// ------------------------------------------------------------------------
|
||||
// Compute the size of the object by calling the handler with SIZE
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
return (size_t) run(rt, SIZE);
|
||||
}
|
||||
|
||||
object *skip(runtime &rt = RT)
|
||||
// ------------------------------------------------------------------------
|
||||
// Return the pointer to the next object in memory by skipping its size
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
return this + size(rt);
|
||||
}
|
||||
|
||||
byte * payload()
|
||||
// ------------------------------------------------------------------------
|
||||
// Return the object's payload, i.e. first byte after ID
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
byte *ptr = (byte *) this;
|
||||
leb128(ptr); // Skip ID
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
void evaluate(runtime &rt = RT)
|
||||
// ------------------------------------------------------------------------
|
||||
// Evaluate an object by calling the handler
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
run(rt, EVAL);
|
||||
}
|
||||
|
||||
static object *parse(cstring beg, cstring *end = nullptr, runtime &rt = RT)
|
||||
// ------------------------------------------------------------------------
|
||||
// Try parsing the object as a top-level temporary
|
||||
|
@ -104,30 +185,6 @@ struct object
|
|||
}
|
||||
|
||||
|
||||
|
||||
enum command
|
||||
// ------------------------------------------------------------------------
|
||||
// The commands that all handlers must deal with
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
EVAL,
|
||||
SIZE,
|
||||
PARSE,
|
||||
RENDER,
|
||||
LAST,
|
||||
};
|
||||
|
||||
enum result
|
||||
// ------------------------------------------------------------------------
|
||||
// Common return values for handlers
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
OK = 0, // Object parsed successfully
|
||||
SKIP = -1, // Not for this handler
|
||||
ERROR = -2, // Error processing the command
|
||||
};
|
||||
|
||||
|
||||
static intptr_t run(runtime &rt, id type, command cmd, void *arg = nullptr)
|
||||
// ------------------------------------------------------------------------
|
||||
// Run a command without an object
|
||||
|
@ -140,43 +197,16 @@ struct object
|
|||
|
||||
intptr_t run(runtime &rt, command cmd, void *arg = nullptr)
|
||||
// ------------------------------------------------------------------------
|
||||
// Run an arbitrary command
|
||||
// Run an arbitrary command on the object
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
byte *ptr = (byte *) this;
|
||||
id type = (id) leb128(ptr);
|
||||
id type = (id) leb128(ptr); // Don't use type() to update payload
|
||||
if (type >= NUM_IDS)
|
||||
return -1;
|
||||
return handler[type](rt, cmd, arg, this, (object *) ptr);
|
||||
}
|
||||
|
||||
size_t size(runtime &rt = RT)
|
||||
// ------------------------------------------------------------------------
|
||||
// Compute the size of the object by calling the handler with SIZE
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
return (size_t) run(rt, SIZE);
|
||||
}
|
||||
|
||||
object *skip(runtime &rt = RT)
|
||||
// ------------------------------------------------------------------------
|
||||
// Return the pointer to the next object in memory by skipping its size
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
return this + size(rt);
|
||||
}
|
||||
|
||||
|
||||
byte * payload()
|
||||
// ------------------------------------------------------------------------
|
||||
// Return the object's payload, i.e. first byte after ID
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
byte *ptr = (byte *) this;
|
||||
leb128(ptr); // Skip ID
|
||||
return ptr;
|
||||
}
|
||||
|
||||
struct parser
|
||||
// ------------------------------------------------------------------------
|
||||
// Arguments to the PARSE command
|
||||
|
|
|
@ -62,6 +62,7 @@ struct runtime
|
|||
runtime(byte *mem = nullptr, size_t size = 0)
|
||||
: Error(nullptr),
|
||||
ErrorSource(nullptr),
|
||||
ErrorCommand(nullptr),
|
||||
Code(nullptr),
|
||||
LowMem(),
|
||||
Globals(),
|
||||
|
@ -205,7 +206,7 @@ struct runtime
|
|||
if (available(len) >= len)
|
||||
{
|
||||
size_t moved = Editing - offset;
|
||||
memmove(editor() + offset, editor() + offset + len, moved);
|
||||
memmove(editor() + offset + len, editor() + offset, moved);
|
||||
memcpy(editor() + offset, data, len);
|
||||
Editing += len;
|
||||
}
|
||||
|
@ -232,7 +233,7 @@ struct runtime
|
|||
if (offset > end)
|
||||
offset = end;
|
||||
len = end - offset;
|
||||
memmove(editor() + offset + len, editor() + offset, len);
|
||||
memmove(editor() + offset, editor() + offset + len, Editing - end);
|
||||
Editing -= len;
|
||||
}
|
||||
|
||||
|
@ -285,8 +286,9 @@ struct runtime
|
|||
// ------------------------------------------------------------------------
|
||||
{
|
||||
if (StackTop >= StackBottom)
|
||||
return error("Cannot replace empty stack");
|
||||
*StackTop = obj;
|
||||
error("Cannot replace empty stack");
|
||||
else
|
||||
*StackTop = obj;
|
||||
}
|
||||
|
||||
object *pop()
|
||||
|
@ -315,8 +317,9 @@ struct runtime
|
|||
// ------------------------------------------------------------------------
|
||||
{
|
||||
if (idx >= depth())
|
||||
return error("Insufficient stack depth");
|
||||
StackTop[idx] = obj;
|
||||
error("Insufficient stack depth");
|
||||
else
|
||||
StackTop[idx] = obj;
|
||||
}
|
||||
|
||||
uint depth()
|
||||
|
@ -341,7 +344,10 @@ struct runtime
|
|||
// ------------------------------------------------------------------------
|
||||
{
|
||||
if (available(sizeof(callee)) < sizeof(callee))
|
||||
return error("Too many recursive calls");
|
||||
{
|
||||
error("Too many recursive calls");
|
||||
return;
|
||||
}
|
||||
StackTop--;
|
||||
StackBottom--;
|
||||
for (object **s = StackBottom; s < StackTop; s++)
|
||||
|
@ -356,7 +362,10 @@ struct runtime
|
|||
// ------------------------------------------------------------------------
|
||||
{
|
||||
if ((byte *) Returns >= (byte *) HighMem)
|
||||
return error("Cannot return without a caller");
|
||||
{
|
||||
error("Cannot return without a caller");
|
||||
return;
|
||||
}
|
||||
Code = *Returns++;
|
||||
StackTop++;
|
||||
StackBottom++;
|
||||
|
@ -450,19 +459,54 @@ struct runtime
|
|||
//
|
||||
// ========================================================================
|
||||
|
||||
void error(cstring message, cstring source = nullptr)
|
||||
runtime &error(cstring message, cstring source = nullptr)
|
||||
// ------------------------------------------------------------------------
|
||||
// Set the error message
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
Error = message;
|
||||
ErrorSource = source;
|
||||
return *this;
|
||||
}
|
||||
|
||||
cstring error()
|
||||
// ------------------------------------------------------------------------
|
||||
// Get the error message
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
return Error;
|
||||
}
|
||||
|
||||
cstring source()
|
||||
// ------------------------------------------------------------------------
|
||||
// Get the pointer to the problem
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
return ErrorSource;
|
||||
}
|
||||
|
||||
runtime &command(cstring cmd)
|
||||
// ------------------------------------------------------------------------
|
||||
// Set the faulting command
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
ErrorCommand = cmd;
|
||||
return *this;
|
||||
}
|
||||
|
||||
cstring command()
|
||||
// ------------------------------------------------------------------------
|
||||
// Get the faulting command if there is one
|
||||
// ------------------------------------------------------------------------
|
||||
{
|
||||
return ErrorCommand;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
protected:
|
||||
cstring Error; // Error message if any
|
||||
cstring ErrorSource; // Source of the error if known
|
||||
cstring ErrorCommand; // Source of the error if known
|
||||
object *Code; // Currently executing code
|
||||
object *LowMem;
|
||||
global *Globals;
|
||||
|
@ -476,6 +520,7 @@ struct runtime
|
|||
// Pointers that are GC-adjusted
|
||||
gcptr *GCSafe;
|
||||
|
||||
public:
|
||||
// The one and only runtime
|
||||
static runtime RT;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue