ports/sh: first functional version, can compute in REPL

This commit is contained in:
Lephenixnoir 2022-10-23 23:47:33 +02:00
parent 68f166dae9
commit e33d85b1de
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
14 changed files with 507 additions and 0 deletions

2
ports/sh/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/icon.xcf
/PythonExtra.g3a

44
ports/sh/Makefile Normal file
View File

@ -0,0 +1,44 @@
include ../../py/mkenv.mk
# Use the sh-elf toolchain
CROSS_COMPILE := sh-elf-
include $(TOP)/py/py.mk
include $(TOP)/extmod/extmod.mk
CFLAGS += -m4-nofpu -mb -fstrict-volatile-bitfields -I. -I$(BUILD) -I$(TOP) -DFXCG50 -O2 -Wall -Wextra -Wno-unused-parameter
LIBS += -nostdlib -Wl,--no-warn-rwx-segments -T fxcg50.ld -lm -lgint-cg -lc -lgint-cg -lgcc -Wl,-Map=build/map
# Source files
SRC_C = \
main.c \
console.c \
keymap.c \
mphalport.c \
shared/readline/readline.c \
shared/runtime/gchelper_generic.c \
shared/runtime/pyexec.c \
shared/runtime/stdout_helpers.c \
SRC_QSTR += \
shared/readline/readline.c \
OBJ = $(PY_CORE_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
all: $(BUILD)/firmware.elf PythonExtra.g3a
PythonExtra.g3a: $(BUILD)/firmware.bin icon-uns.png icon-sel.png
fxgxa --g3a -n PythonExtra --icon-uns=icon-uns.png --icon-sel=icon-sel.png $< -o $@
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
$(Q)$(CC:gcc=objcopy) -O binary -R .bss -R .gint_bss $< $@
$(BUILD)/firmware.elf: $(OBJ)
$(ECHO) "LINK $@"
$(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
$(Q)$(SIZE) $@
send: all
fxlink -sw PythonExtra.g3a
include $(TOP)/py/mkrules.mk

110
ports/sh/console.c Normal file
View File

@ -0,0 +1,110 @@
#include <gint/keyboard.h>
#include <gint/display.h>
#include <gint/defs/util.h>
#include <stdlib.h>
#include <string.h>
#include "console.h"
console_t *console_create(int max_lines)
{
console_t *cons = malloc(sizeof *cons);
cons->max_lines = max_lines;
cons->lines = calloc(max_lines, sizeof *cons->lines);
for(int i = 0; i < max_lines; i++)
cons->lines[i] = NULL;
console_new_line(cons);
return cons;
}
void console_new_line(console_t *cons)
{
int n = cons->max_lines;
free(cons->lines[0]);
for(int i = 0; i < n - 1; i++)
cons->lines[i] = cons->lines[i+1];
cons->lines[n - 1] = strdup("");
}
static void console_append_to_last_line(console_t *cons, void const *buf,
int size)
{
int n = cons->max_lines;
char *l = cons->lines[n - 1];
int prev_size = l ? strlen(l) : 0;
int new_size = prev_size + size + 1;
l = realloc(l, new_size);
if(!l)
return;
memcpy(l + prev_size, buf, size);
l[new_size - 1] = 0;
cons->lines[n - 1] = l;
}
void console_append_str(console_t *cons, char const *str)
{
console_append_buf(cons, str, strlen(str));
}
void console_append_buf(console_t *cons, void const *buf, size_t size_)
{
int offset = 0, size = size_;
while(offset < size) {
/* Find the first '\n' (or end of buffer) in the segment */
void const *endline = memchr(buf + offset, '\n', size - offset);
if(endline == NULL)
endline = buf + size;
int line_size = endline - (buf + offset);
console_append_to_last_line(cons, buf + offset, line_size);
offset += line_size;
/* Found a '\n' */
if(offset < size) {
console_new_line(cons);
offset++;
}
}
}
void console_render(int x, int y, console_t *cons, int w, int h)
{
int dy = 13;
for(int i = 0; i < cons->max_lines; i++) {
/* Skip initial unallocated lines */
if(cons->lines[i] == NULL)
continue;
char const *p = cons->lines[i];
char const *endline = p + strlen(p);
while(p < endline) {
if(p[0] == '\n') {
y += dy;
p++;
continue;
}
char const *endscreen = drsize(p, NULL, w, NULL);
char const *endline = strchrnul(p, '\n');
int len = min(endscreen - p, endline - p);
dtext_opt(x, y, C_BLACK, C_NONE, DTEXT_LEFT, DTEXT_TOP, p, len);
y += dy;
p += len + (p[len] == '\n');
}
}
}
void console_destroy(console_t *cons)
{
for(int i = 0; i < cons->max_lines; i++)
free(cons->lines[i]);
free(cons);
}

28
ports/sh/console.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef __PYTHONEXTRA_CONSOLE_H
#define __PYTHONEXTRA_CONSOLE_H
//=== Console data storage ===//
typedef struct
{
/* An array lines[] of size [max_lines], filled from the end (always has a
list of NULL followed by line pointers. */
int max_lines;
char **lines;
} console_t;
console_t *console_create(int max_lines);
void console_new_line(console_t *cons);
void console_append_str(console_t *cons, char const *str);
void console_append_buf(console_t *cons, void const *buf, size_t size);
/* TODO: Expand this function */
void console_render(int x, int y, console_t *cons, int w, int h);
void console_destroy(console_t *cons);
#endif /* __PYTHONEXTRA_CONSOLE_H */

BIN
ports/sh/icon-sel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
ports/sh/icon-uns.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

57
ports/sh/keymap.c Normal file
View File

@ -0,0 +1,57 @@
#include "keymap.h"
#include <gint/keyboard.h>
static int key_id(int keycode)
{
uint col = (keycode & 0x0f) - 1;
uint row = 9 - ((keycode & 0xf0) >> 4);
if(col > 5 || row > 8) return -1;
return 6 * row + col;
}
static uint8_t map_flat[30] = {
0, 0, '(', ')', ',', '=',
'7', '8', '9', 0, 0, 0,
'4', '5', '6', '*', '/', 0,
'1', '2', '3', '+', '-', 0,
'0', '.', 'e', '-', 0, 0,
};
static uint8_t map_alpha[36] = {
'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 0, 0, 0,
'p', 'q', 'r', 's', 't', 0,
'u', 'v', 'w', 'x', 'y', 0,
'z', ' ', '"', 0, 0, 0,
};
uint32_t keymap_translate(int key, bool shift, bool alpha)
{
int id = key_id(key);
if(id < 0) return 0;
if(!shift && !alpha) {
/* The first 4 rows have no useful characters */
return (id < 24) ? 0 : map_flat[id - 24];
}
if(shift && !alpha) {
if(key == KEY_MUL) return '{';
if(key == KEY_DIV) return '}';
if(key == KEY_ADD) return '[';
if(key == KEY_SUB) return ']';
if(key == KEY_DOT) return '=';
if(key == KEY_0) return ':';
if(key == KEY_EXP) return 0x3c0; // 'π'
}
if(!shift && alpha) {
/* The first 3 rows have no useful characters */
return (id < 18) ? 0 : map_alpha[id - 18];
}
if(shift && alpha) {
int c = keymap_translate(key, false, true);
return (c >= 'a' && c <= 'z') ? (c & ~0x20) : c;
}
return 0;
}

9
ports/sh/keymap.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef __PYTHONEXTRA_KEYMAP_H
#define __PYTHONEXTRA_KEYMAP_H
#include <stdint.h>
#include <stdbool.h>
uint32_t keymap_translate(int key, bool shift, bool alpha);
#endif /* __PYTHONEXTRA_KEYMAP_H */

116
ports/sh/main.c Normal file
View File

@ -0,0 +1,116 @@
#include "py/compile.h"
#include "py/gc.h"
#include "py/mperrno.h"
#include "py/stackctrl.h"
#include "py/builtin.h"
#include "shared/runtime/gchelper.h"
#include "shared/runtime/pyexec.h"
#include <gint/display.h>
#include <gint/keyboard.h>
#include <gint/fs.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "console.h"
// Allocate memory for the MicroPython GC heap.
static char heap[32768];
int parse_compile_execute(const void *source, mp_parse_input_kind_t input_kind, mp_uint_t exec_flags);
//=== Console-based standard streams ===//
ssize_t stdouterr_write(void *data, void const *buf, size_t size)
{
console_t *cons = data;
console_append_buf(cons, buf, size);
return size;
}
fs_descriptor_type_t stdouterr_type = {
.read = NULL,
.write = stdouterr_write,
.lseek = NULL,
.close = NULL,
};
//=== Main function ===//
static console_t *cons = NULL;
void pe_draw(void)
{
dclear(C_WHITE);
dtext(1, 1, C_BLACK, "PythonExtra, very much WIP :)");
dline(1, 16, DWIDTH-1, 16, C_BLACK);
console_render(1, 18, cons, DWIDTH-2, -1);
dupdate();
}
void pe_exithandler(void)
{
pe_draw();
drect(DWIDTH-8, 0, DWIDTH-1, 7, C_RED);
dupdate();
getkey();
}
int main(int argc, char **argv)
{
/* Set up standard streams */
cons = console_create(10);
close(STDOUT_FILENO);
close(STDERR_FILENO);
open_generic(&stdouterr_type, cons, STDOUT_FILENO);
open_generic(&stdouterr_type, cons, STDERR_FILENO);
atexit(pe_exithandler);
/* Initialize MicroPython */
mp_stack_ctrl_init();
gc_init(heap, heap + sizeof(heap));
mp_init();
// Start a normal REPL; will exit when ctrl-D is entered on a blank line.
pyexec_friendly_repl();
console_destroy(cons);
// Deinitialise the runtime.
gc_sweep_all();
mp_deinit();
return 0;
}
// Handle uncaught exceptions (should never be reached in a correct C implementation).
void nlr_jump_fail(void *val) {
dclear(C_BLACK);
dtext(2, 2, C_WHITE, "nlr_jump_fail!");
dprint(2, 2, C_WHITE, "val = %p", val);
dupdate();
while(1)
getkey();
}
// Do a garbage collection cycle.
void gc_collect(void) {
gc_collect_start();
gc_helper_collect_regs_and_stack();
gc_collect_end();
}
// There is no filesystem so stat'ing returns nothing.
mp_import_stat_t mp_import_stat(const char *path) {
return MP_IMPORT_STAT_NO_EXIST;
}
mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open);
// There is no filesystem so opening a file raises an exception.
mp_lexer_t *mp_lexer_new_from_file(const char *filename) {
mp_raise_OSError(MP_ENOENT);
}

81
ports/sh/mpconfigport.h Normal file
View File

@ -0,0 +1,81 @@
#include <stdint.h>
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_CORE_FEATURES)
// Other options: BASIC_FEATURES, EXTRA_FEATURES, FULL_FEATURES, EVERYTHING
#define MICROPY_ENABLE_COMPILER (1)
#define MP_ENDIANNESS_BIG (1)
#define MICROPY_QSTR_BYTES_IN_HASH (1)
// #define MICROPY_QSTR_EXTRA_POOL mp_qstr_frozen_const_pool
#define MICROPY_ALLOC_PATH_MAX (256)
#define MICROPY_ALLOC_PARSE_CHUNK_INIT (16)
#define MICROPY_EMIT_X64 (0)
#define MICROPY_EMIT_THUMB (0)
#define MICROPY_EMIT_INLINE_THUMB (0)
#define MICROPY_COMP_MODULE_CONST (0)
#define MICROPY_COMP_CONST (0)
#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (0)
#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (0)
#define MICROPY_MEM_STATS (0)
#define MICROPY_DEBUG_PRINTERS (0)
#define MICROPY_ENABLE_GC (1)
#define MICROPY_GC_ALLOC_THRESHOLD (0)
#define MICROPY_REPL_EVENT_DRIVEN (0)
#define MICROPY_HELPER_REPL (1)
#define MICROPY_HELPER_LEXER_UNIX (0)
#define MICROPY_ENABLE_SOURCE_LINE (1)
#define MICROPY_ENABLE_DOC_STRING (0)
#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED)
#define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (1)
#define MICROPY_PY_ASYNC_AWAIT (0)
#define MICROPY_PY_BUILTINS_BYTEARRAY (1)
#define MICROPY_PY_BUILTINS_MEMORYVIEW (1)
#define MICROPY_PY_BUILTINS_ENUMERATE (1)
#define MICROPY_PY_BUILTINS_FILTER (1)
// #define MICROPY_PY_BUILTINS_FROZENSET (1)
#define MICROPY_PY_BUILTINS_REVERSED (1)
#define MICROPY_PY_BUILTINS_SET (1)
#define MICROPY_PY_BUILTINS_SLICE (1)
#define MICROPY_PY_BUILTINS_PROPERTY (1)
#define MICROPY_PY_BUILTINS_MIN_MAX (1)
#define MICROPY_PY___FILE__ (0)
#define MICROPY_PY_GC (1)
#define MICROPY_PY_ARRAY (1)
#define MICROPY_PY_ATTRTUPLE (1)
#define MICROPY_PY_COLLECTIONS (1)
#define MICROPY_PY_MATH (1)
#define MICROPY_PY_CMATH (1)
#define MICROPY_PY_IO (1)
#define MICROPY_PY_STRUCT (1)
#define MICROPY_PY_SYS (1)
// #define MICROPY_MODULE_FROZEN_MPY (1)
#define MICROPY_CPYTHON_COMPAT (0)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE)
// #define MICROPY_PY_URANDOM (1)
#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1)
#define MP_NEED_LOG2 (1)
/* #define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len)
// extra built in names to add to the global namespace
#define MICROPY_PORT_BUILTINS \
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) },
#define MICROPY_MIN_USE_STDOUT (1) */
// Type definitions for the specific machine.
typedef intptr_t mp_int_t; // must be pointer size
typedef uintptr_t mp_uint_t; // must be pointer size
typedef long mp_off_t;
// We need to provide a declaration/definition of alloca().
#include <alloca.h>
// Define the port's name and hardware.
#define MICROPY_HW_BOARD_NAME "sh7305"
#define MICROPY_HW_MCU_NAME "sh-4a"
#define MP_STATE_PORT MP_STATE_VM

36
ports/sh/mphalport.c Normal file
View File

@ -0,0 +1,36 @@
#include <unistd.h>
#include "py/mpconfig.h"
#include "py/mphal.h"
#include "../../shared/readline/readline.h"
#include "keymap.h"
#include <gint/display.h>
#include <gint/keyboard.h>
// Receive single character, blocking until one is available.
int mp_hal_stdin_rx_chr(void) {
while(1) {
key_event_t ev = getkey();
int key = ev.key;
if(key == KEY_ACON)
return CHAR_CTRL_C;
if(key == KEY_EXE)
return '\r';
if(key == KEY_EXIT)
return CHAR_CTRL_D;
uint32_t code_point = keymap_translate(key, ev.shift, ev.alpha);
if(code_point != 0)
return code_point;
}
}
// Send the string of given length.
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
int r = write(STDOUT_FILENO, str, len);
(void)r;
extern void pe_draw(void);
pe_draw();
}

1
ports/sh/mphalport.h Normal file
View File

@ -0,0 +1 @@
static inline void mp_hal_set_interrupt_char(char c) {}

View File

@ -41,6 +41,8 @@ typedef uintptr_t gc_helper_regs_t[4];
typedef uintptr_t gc_helper_regs_t[10];
#elif defined(__aarch64__)
typedef uintptr_t gc_helper_regs_t[11]; // x19-x29
#elif defined(__sh3__)
typedef uintptr_t gc_helper_regs_t[8];
#endif
#endif

View File

@ -150,6 +150,27 @@ STATIC void gc_helper_get_regs(gc_helper_regs_t arr) {
arr[10] = x29;
}
#elif defined(__sh3__)
STATIC void gc_helper_get_regs(gc_helper_regs_t arr) {
register const long r8 asm ("r8");
register const long r9 asm ("r9");
register const long r10 asm ("r10");
register const long r11 asm ("r11");
register const long r12 asm ("r12");
register const long r13 asm ("r13");
register const long r14 asm ("r14");
register const long r15 asm ("r15");
arr[0] = r8;
arr[1] = r9;
arr[2] = r10;
arr[3] = r11;
arr[4] = r12;
arr[5] = r13;
arr[6] = r14;
arr[7] = r15;
}
#else
#error "Architecture not supported for gc_helper_get_regs. Set MICROPY_GCREGS_SETJMP to use the fallback implementation."