commit b50a84e47be40c0cbd8de2615bf295de1815c4e9 Author: Yatis Date: Sun Jan 10 13:48:14 2021 +0100 use Gint + add terminal device diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8fe90d1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea +build* +*.g1a +*.g3a diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..370dbbb --- /dev/null +++ b/Makefile @@ -0,0 +1,208 @@ +#! /usr/bin/make -f +# Default Makefile for fxSDK add-ins. This file was probably copied there by +# the [fxsdk] program. +#--- + +# +# Configuration +# + +include project.cfg + +# Compiler flags +CFLAGSFX = $(CFLAGS) $(CFLAGS_FX) $(INCLUDE_FX) +CFLAGSCG = $(CFLAGS) $(CFLAGS_CG) $(INCLUDE_CG) + +# Linker flags +LDFLAGSFX := $(LDFLAGS) $(LDFLAGS_FX) +LDFLAGSCG := $(LDFLAGS) $(LDFLAGS_CG) + +# Dependency list generation flags +depflags = -MMD -MT $@ -MF $(@:.o=.d) -MP +# ELF to binary flags +BINFLAGS := -R .bss -R .gint_bss + +# G1A and G3A generation flags +NAME_G1A ?= $(NAME) +NAME_G3A ?= $(NAME) +G1AF := -i "$(ICON_FX)" -n "$(NAME_G1A)" --internal="$(INTERNAL)" +G3AF := -n basic:"$(NAME_G3A)" -i uns:"$(ICON_CG_UNS)" -i sel:"$(ICON_CG_SEL)" + +ifeq "$(TOOLCHAIN_FX)" "" +TOOLCHAIN_FX := sh3eb-elf +endif + +ifeq "$(TOOLCHAIN_CG)" "" +TOOLCHAIN_CG := sh4eb-elf +endif + +# fxconv flags +FXCONVFX := --fx --toolchain=$(TOOLCHAIN_FX) +FXCONVCG := --cg --toolchain=$(TOOLCHAIN_CG) + +# Determine the compiler install and include path +GCC_BASE_FX := $(shell $(TOOLCHAIN_FX)-gcc --print-search-dirs | grep install | sed 's/install: //') +GCC_BASE_CG := $(shell $(TOOLCHAIN_CG)-gcc --print-search-dirs | grep install | sed 's/install: //') +GCC_INCLUDE_FX := $(GCC_BASE_FX)/include +GCC_INCLUDE_CG := $(GCC_BASE_CG)/include + +# +# File listings +# + +NULL := +TARGET := $(subst $(NULL) $(NULL),-,$(NAME)) + +ifeq "$(TARGET_FX)" "" +TARGET_FX := $(TARGET).g1a +endif + +ifeq "$(TARGET_CG)" "" +TARGET_CG := $(TARGET).g3a +endif + +ELF_FX := build-fx/$(shell basename "$(TARGET_FX)" .g1a).elf +BIN_FX := $(ELF_FX:.elf=.bin) + +ELF_CG := build-cg/$(shell basename "$(TARGET_CG)" .g3a).elf +BIN_CG := $(ELF_CG:.elf=.bin) + +# Source files +src := $(wildcard src/*.[csS] \ + src/*/*.[csS] \ + src/*/*/*.[csS] \ + src/*/*/*/*.[csS]) +assets-fx := $(wildcard assets-fx/*/*) +assets-cg := $(wildcard assets-cg/*/*) + +# Object files +obj-fx := $(src:%=build-fx/%.o) \ + $(assets-fx:assets-fx/%=build-fx/assets/%.o) +obj-cg := $(src:%=build-cg/%.o) \ + $(assets-cg:assets-cg/%=build-cg/assets/%.o) + +# Additional dependencies +deps-fx := $(ICON_FX) +deps-cg := $(ICON_CG_UNS) $(ICON_CG_SEL) + +# All targets +all := +ifneq "$(wildcard build-fx)" "" +all += all-fx +endif +ifneq "$(wildcard build-cg)" "" +all += all-cg +endif + +# +# Build rules +# + +all: $(all) + +all-fx: $(TARGET_FX) +all-cg: $(TARGET_CG) + +$(TARGET_FX): $(obj-fx) $(deps-fx) + @ mkdir -p $(dir $@) + $(TOOLCHAIN_FX)-gcc -o $(ELF_FX) $(obj-fx) $(CFLAGSFX) $(LDFLAGSFX) + $(TOOLCHAIN_FX)-objcopy -O binary $(BINFLAGS) $(ELF_FX) $(BIN_FX) + fxg1a $(BIN_FX) -o $@ $(G1AF) + +$(TARGET_CG): $(obj-cg) $(deps-cg) + @ mkdir -p $(dir $@) + $(TOOLCHAIN_CG)-gcc -o $(ELF_CG) $(obj-cg) $(CFLAGSCG) $(LDFLAGSCG) + $(TOOLCHAIN_CG)-objcopy -O binary $(BINFLAGS) $(ELF_CG) $(BIN_CG) + mkg3a $(G3AF) $(BIN_CG) $@ + +# C sources +build-fx/%.c.o: %.c + @ mkdir -p $(dir $@) + $(TOOLCHAIN_FX)-gcc -c $< -o $@ $(CFLAGSFX) $(depflags) +build-cg/%.c.o: %.c + @ mkdir -p $(dir $@) + $(TOOLCHAIN_CG)-gcc -c $< -o $@ $(CFLAGSCG) $(depflags) + +# Assembler sources +build-fx/%.s.o: %.s + @ mkdir -p $(dir $@) + $(TOOLCHAIN_FX)-gcc -c $< -o $@ +build-cg/%.s.o: %.s + @ mkdir -p $(dir $@) + $(TOOLCHAIN_CG)-gcc -c $< -o $@ + +# Preprocessed assembler sources +build-fx/%.S.o: %.S + @ mkdir -p $(dir $@) + $(TOOLCHAIN_FX)-gcc -c $< -o $@ $(INCLUDE_FX) +build-cg/%.S.o: %.S + @ mkdir -p $(dir $@) + $(TOOLCHAIN_CG)-gcc -c $< -o $@ $(INCLUDE_CG) + +# Images +build-fx/assets/img/%.o: assets-fx/img/% + @ mkdir -p $(dir $@) + fxconv --bopti-image $< -o $@ $(FXCONVFX) name:img_$(basename $*) $(IMG.$*) +build-cg/assets/img/%.o: assets-cg/img/% + @ mkdir -p $(dir $@) + fxconv --bopti-image $< -o $@ $(FXCONVCG) name:img_$(basename $*) $(IMG.$*) + +# Fonts +build-fx/assets/fonts/%.o: assets-fx/fonts/% + @ mkdir -p $(dir $@) + fxconv -f $< -o $@ $(FXCONVFX) name:font_$(basename $*) $(FONT.$*) +build-cg/assets/fonts/%.o: assets-cg/fonts/% + @ mkdir -p $(dir $@) + fxconv -f $< -o $@ $(FXCONVCG) name:font_$(basename $*) $(FONT.$*) + +# Binaries +build-fx/assets/bin/%.o: assets-fx/bin/% + @ mkdir -p $(dir $@) + fxconv -b $< -o $@ $(FXCONVFX) name:bin_$(basename $*) $(BIN.$*) +build-cg/assets/bin/%.o: assets-cg/bin/% + @ mkdir -p $(dir $@) + fxconv -b $< -o $@ $(FXCONVCG) name:bin_$(basename $*) $(BIN.$*) + +# Custom conversions +build-fx/assets/%.o: assets-fx/% + @ mkdir -p $(dir $@) + fxconv --custom $< -o $@ $(FXCONVFX) type:$(subst /,,$(dir $*)) name:$(subst /,_,$(basename $*)) +build-cg/assets/%.o: assets-cg/% + @ mkdir -p $(dir $@) + fxconv --custom $< -o $@ $(FXCONVCG) type:$(subst /,,$(dir $*)) name:$(subst /,_,$(basename $*)) + +# +# Cleaning and utilities +# + +# Dependency information +-include $(shell find build* -name *.d 2> /dev/null) +build-fx/%.d: ; +build-cg/%.d: ; +.PRECIOUS: build-fx build-cg build-fx/%.d build-cg/%.d %/ + +clean-fx: + @ rm -rf build-fx/ +clean-cg: + @ rm -rf build-cg/ + +distclean-fx: clean-fx + @ rm -f $(TARGET_FX) +distclean-cg: clean-cg + @ rm -f $(TARGET_CG) + +clean: clean-fx clean-cg + +distclean: distclean-fx distclean-cg + +install-fx: $(TARGET_FX) + p7 send -f $< +install-cg: $(TARGET_CG) + @ while [[ ! -h /dev/Prizm1 ]]; do sleep 0.25; done + @ while ! mount /dev/Prizm1; do sleep 0.25; done + @ rm -f /mnt/prizm/$< + @ cp $< /mnt/prizm + @ umount /dev/Prizm1 + @- eject /dev/Prizm1 + +.PHONY: all all-fx all-cg clean distclean install-fx install-cg diff --git a/assets-cg/icon-cg-sel.png b/assets-cg/icon-cg-sel.png new file mode 100644 index 0000000..7137b50 Binary files /dev/null and b/assets-cg/icon-cg-sel.png differ diff --git a/assets-cg/icon-cg-uns.png b/assets-cg/icon-cg-uns.png new file mode 100644 index 0000000..3c99f62 Binary files /dev/null and b/assets-cg/icon-cg-uns.png differ diff --git a/assets-fx/icon-fx.png b/assets-fx/icon-fx.png new file mode 100644 index 0000000..c92f12a Binary files /dev/null and b/assets-fx/icon-fx.png differ diff --git a/include/fxBoot/parser.h b/include/fxBoot/parser.h new file mode 100644 index 0000000..038c0d8 --- /dev/null +++ b/include/fxBoot/parser.h @@ -0,0 +1,10 @@ +#ifndef __FXBOOT_PARSER_H__ +# define __FXBOOT_PARSER_H__ + +#include +#include + +extern int parser_strtotab(int *argc, char ***argv, char const *str); +extern void parser_strtotab_quit(int *argc, char ***argv); + +#endif /*__FXBOOT_PARSER_H__*/ diff --git a/include/fxBoot/terminal.h b/include/fxBoot/terminal.h new file mode 100644 index 0000000..0476f71 --- /dev/null +++ b/include/fxBoot/terminal.h @@ -0,0 +1,79 @@ +#ifndef __FXBOOT_TERMINAL_H__ +# define __FXBOOT_TERMINAL_H__ + +#include +#include + +#define TERM_PRIVATE_WATERMARK (0xdeadbeef) +#define TERM_RDBUFFER_NBLINE (32) + +/* */ +#ifdef FXCG50 +#define FWIDTH 8 +#define FHEIGHT 9 +#endif +#ifdef FX9860G +#define FWIDTH 5 +#define FHEIGHT 7 +#endif + +/* define offset type */ +typedef unsigned int off_t; + +struct terminal { + /* windows information */ + struct { + unsigned short ws_col; + unsigned short ws_row; + unsigned short ws_xpixel; + unsigned short ws_ypixel; + unsigned short ft_xpixel; + unsigned short ft_ypixel; + } winsize; + + /* cursor information */ + struct { + unsigned short x; + unsigned short y; + } cursor; + + /* buffer information */ + struct { + uint8_t *data; + off_t cursor; + size_t size; + } buffer; + + /* private information */ + struct { + struct { + int id; + } timer; + uint32_t watermark; + struct { + int fg; + int bg; + } color; + } private; +}; + +/* define the terminal */ +extern struct terminal terminal; + +//--- +// User interface +//--- +extern int terminal_open(void); +extern int terminal_write(const char *format, ...); +extern int terminal_read(void *buffer, size_t nb); +extern int terminal_close(void); + +//--- +// Internal interface +//--- +extern void terminal_clear(void); +extern int terminal_cursor_handler(void); +extern void terminal_buffer_display(void); +extern void terminal_buffer_insert(char *buffer, size_t nb); + +#endif /*__FXBOOT_TERMINAL_H__*/ diff --git a/project.cfg b/project.cfg new file mode 100644 index 0000000..67bd3b0 --- /dev/null +++ b/project.cfg @@ -0,0 +1,88 @@ +#--- +# fxSDK project configuration file for fxBoot +#--- + +# Project name, should be at most 8 bytes long. +# (You can also specify NAME_G1A or NAME_G3A to override individually.) +NAME := fxBoot + +# Internal name, should be '@' followed by at most 7 uppercase letters. +# WARNING: If this convention is not followed, the add-in might not appear in +# the main menu of the calculator! +INTERNAL := @FXBOOT + +# Output file name. The default is to take , replace spaces with dashes, +# and add .g1a (or .g3a). You can specify a different folder if you want. +TARGET_FX := +TARGET_CG := + +# fx-9860G icon location +ICON_FX = assets-fx/icon-fx.png +# fx-CG 50 icon locations +ICON_CG_UNS = assets-cg/icon-cg-uns.png +ICON_CG_SEL = assets-cg/icon-cg-sel.png + +#--- +# Toolchain selection +#--- + +# Toolchain for fx9860g. Please see also CFLAGS_FX below. +TOOLCHAIN_FX := sh-elf + +# Toolchain for fxcg50. Please see also CFLAGS_CG below. +TOOLCHAIN_CG := sh-elf + +#--- +# Compiler flags +#--- + +# Base compiler flags for the fxSDK, you usually want to keep these. +CFLAGS := -mb -ffreestanding -nostdlib -fstrict-volatile-bitfields + +# Platform-specific compiler flags. +# <> If you are using sh3eb-elf, use -m3. (You can do this on both FX and CG.) +# <> If you are using sh4eb-elf, use -m4-nofpu. (Not ideal on FX but works.) +# <> If you are using sh4eb-nofpu-elf, then your compiler will likely use the +# FPU and cause problems on the calculator. Consider another configuration. +# <> If you are using an sh-elf with several targets, specify whichever you +# support. I recommend -m3 on FX and -m4-nofpu on CG. +# Please see also TOOLCHAIN_FX and TOOLCHAIN_CG above. +CFLAGS_FX := -D FX9860G -m3 +CFLAGS_CG := -D FXCG50 -m4-nofpu + +# Additional compiler flags, change to your own taste! +CFLAGS += -Wall -Wextra -Wno-missing-field-initializers -Os + +# Include paths. Add one -I option for each folder from which you want to +# be able to include files with #include<>. The Makefile provides a variable +# GCC_INCLUDE_FX/GCC_INCLUDE_CG that represents the default include folder, +# which is useful for some libraries such as OpenLibm. +INCLUDE_FX = -I include +INCLUDE_CG = -I include + +# Libraries. Add one -l option for each library you are using, and also +# suitable -L options if you have library files in custom folders. To use +# fxlib, add libfx.a to the project directory and use "-L . -lfx". +LIBS_FX := +LIBS_CG := + +# Base linker flags for the fxSDK, you usually want to keep these. +LDFLAGS_FX := -T fx9860g.ld -lgint-fx $(LIBS_FX) -lgint-fx -lgcc +LDFLAGS_CG := -T fxcg50.ld -lgint-cg $(LIBS_CG) -lgint-cg -lgcc + +# Additional linker flags, if you need any. +LDFLAGS := + +# Additional platform-specific linker flags. +LDFLAGS_FX += -Wl,-Map=build-fx/map +LDFLAGS_CG += -Wl,-Map=build-cg/map + +#--- +# File conversion parameters +#--- + +# Here you can add fxconv options for each converted file, individually. +# The syntax is ".". For example, to specify the parameters for a +# font named "hexa.png", you might write: +# +# FONT.hexa.png = charset:print grid.size:3x5 grid.padding:1 diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..d6df463 --- /dev/null +++ b/src/main.c @@ -0,0 +1,66 @@ +#include "fxBoot/terminal.h" +#include "fxBoot/parser.h" +#include +#include +#include + +/* internal builtin list */ +struct { + const char *name; + int (*f)(int argc, char **argv); +} cmd_list[] = { + {.name = "ls", NULL}, + {.name = NULL, NULL} +}; + +/* try to find the appropriate command */ +static int (*check_cmd(char *cmd))(int, char**) +{ + for (int i = 0; cmd_list[i].name != NULL; ++i) { + if (strcmp(cmd, cmd_list[i].name) != 0) + continue; + if (cmd_list[i].f == NULL) + terminal_write("command exist but not implemented\n"); + return (cmd_list[i].f); + } + return (NULL); +} + +int main(void) +{ + int (*builtin)(int, char**); + const char *usrline; + char buff[128]; + char **argv; + int argc; + int ret; + + ret = 0; + terminal_open(); + terminal_write("Welcome to fxBoot!\n"); + while (1) { + /* get user command */ + usrline = "/>"; + if (ret != 0) { + usrline = "/[%d]>"; + } + terminal_write(usrline, ret); + if (terminal_read(buff, 128) <= 1) + continue; + + /* parse and try to find the command */ + if (parser_strtotab(&argc, &argv, buff) != 0) { + terminal_write("error when processing \"%s\"", buff); + } + builtin = check_cmd(argv[0]); + if (builtin == NULL) { + terminal_write("command \"%s\" not found...\n", argv[0]); + continue; + } + + /* execute the command and free'd allocated memories */ + ret = builtin(argc, argv); + parser_strtotab_quit(&argc, &argv); + } + return (1); +} diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 0000000..5c6aa79 --- /dev/null +++ b/src/parser.c @@ -0,0 +1,141 @@ +//--- +// fxBoot:parser - Parser used to generate ARGC and ARGV +//--- +#include "fxBoot/parser.h" +#include +#include + +/* define external symbols */ +extern int parser_strtotab(int *argc, char ***argv, char const *str); +extern void parser_strtotab_quit(int *argc, char ***argv); + +/* parser_get_word(): Get the word at the current cursor location. */ +static int parser_get_word(char ***tab, size_t *tab_pos, + char const *str, int *counter) +{ + int i; + + /* skip until separator */ + i = -1; + while (str[++i] != '\0' + && str[i] != '\n' + && str[i] != ' ' + && str[i] != '\t'); + + /* dump the word of needed */ + if (*tab != NULL) { + (*tab)[*tab_pos] = (char*)calloc(i + 1, 1); + if ((*tab)[*tab_pos] == NULL) + return (-1); + memset((*tab)[*tab_pos], 0, i + 1); + strncpy((*tab)[(*tab_pos)++], str, i); + } + + /* update the internal counter */ + (*counter)++; + return (i); +} + +/* parser_get_inibitor() + + This function will get the content of an inhibitor (and check if the + inhibitor characteres are alone or not). */ +static int parser_get_inibitor(char ***tab, size_t *tab_pos, + char const *str, int *counter) +{ + int i; + + /* get the inibitor end */ + i = 0; + while (str[++i] != '\0' && str[i] != '\"'); + if (str[i] != '\"') + return (0); + + /* dump the word if needed */ + if (*tab != NULL) { + (*tab)[*tab_pos] = (char*)calloc(i + 1, 1); + if ((*tab)[*tab_pos] == NULL) + return (-1); + memset((*tab)[*tab_pos], 0, i + 1); + strncpy((*tab)[(*tab_pos)++], str + 1, i - 1); + } + + /* update the internal counter */ + (*counter)++; + return (i + 1); +} + +/* parser_setup_arg() + + This function removes useless spaces, tabs and handle '\"' inhibitor. + Return the number of word(s) stored in "str". */ +static int parser_entry(char ***tab, char const *str) +{ + size_t tab_pos; + int counter; + int sz; + + str--; + sz = 0; + counter = 0; + tab_pos = 0; + while (*(++str) != '\0' && *str != '\n' && sz >= 0) { + if (*str == '\"') { + sz = parser_get_inibitor(tab, &tab_pos, str, &counter); + if (sz > 0) { + str = &str[sz]; + continue; + } + } + if (*str != ' ' && *str != '\t') { + sz = parser_get_word(tab, &tab_pos, str, &counter) - 1; + if (sz > 0) + str = &str[sz]; + } + } + return (counter); +} + +/* parser_strtotab() + Generate word table and indicated the number of word find in the string. */ +int parser_strtotab(int *argc, char ***argv, char const *str) +{ + if (argc == NULL || argv == NULL || str == NULL) + return (-1); + + /* Get the number of word. */ + *argv = NULL; + *argc = parser_entry(argv, str); + if (*argc <= 0) + return (-2); + + *argv = (char **)calloc(sizeof(char *) * (*argc + 1), 1); + if (*argv == NULL) + return (-3); + + /* Dump all word. */ + if (parser_entry(argv, str) != *argc) { + parser_strtotab_quit(argc, argv); + return (-4); + } + (*argv)[*argc] = NULL; + return (0); +} + +/* parser_strtotab_quit(): Free all allocated memory generated by "strtotab()"*/ +void parser_strtotab_quit(int *argc, char ***argv) +{ + if (argc == NULL || argv == NULL) + return; + if (*argv == NULL) { + *argc = 0; + return; + } + while (--(*argc) >= 0) { + if ((*argv)[*argc] != NULL) + free((*argv)[*argc]); + } + free(*argv); + *argv = NULL; + *argc = 0; +} diff --git a/src/terminal/close.c b/src/terminal/close.c new file mode 100644 index 0000000..2d90e24 --- /dev/null +++ b/src/terminal/close.c @@ -0,0 +1,22 @@ +//--- +// fxBoot:terminal:close - Close primitive for the terminal +//--- +#include "fxBoot/terminal.h" +#include +#include + +/* internal symbols */ +extern struct terminal terminal; + +/* terminal_close(): Uninitialize the terminal */ +int terminal_close(void) +{ + if (terminal.private.watermark != TERM_PRIVATE_WATERMARK) + return (-1); + if (terminal.private.timer.id >= 0) + timer_stop(terminal.private.timer.id); + if (terminal.buffer.data != NULL) + free(terminal.buffer.data); + terminal.private.watermark = TERM_PRIVATE_WATERMARK; + return (0); +} diff --git a/src/terminal/open.c b/src/terminal/open.c new file mode 100644 index 0000000..1aecd9f --- /dev/null +++ b/src/terminal/open.c @@ -0,0 +1,45 @@ +//--- +// fxBoot:terminal:open - Open primitive for the terminal +//--- +#include "fxBoot/terminal.h" +#include +#include +#include +#include + +/* internal symbols */ +struct terminal terminal; + +/* terminal_open(): Initialize and open the terminal */ +int terminal_open(void) +{ + if (terminal.private.watermark == TERM_PRIVATE_WATERMARK) + terminal_close(); + memset(&terminal, 0x00, sizeof(struct terminal)); + terminal.private.timer.id = -1; + terminal.winsize.ws_xpixel = DWIDTH; + terminal.winsize.ws_ypixel = DHEIGHT; + terminal.winsize.ft_xpixel = FWIDTH + 1; + terminal.winsize.ft_ypixel = FHEIGHT + 1; + terminal.winsize.ws_col = DWIDTH / terminal.winsize.ft_xpixel; + terminal.winsize.ws_row = DHEIGHT / terminal.winsize.ft_ypixel; + terminal.buffer.size = terminal.winsize.ws_row + * terminal.winsize.ws_col + * TERM_RDBUFFER_NBLINE + * sizeof(uint8_t); + terminal.buffer.data = calloc(terminal.buffer.size, 1); + if (terminal.buffer.data == NULL) { + terminal_close(); + return (-1); + } + terminal.private.timer.id = timer_setup(TIMER_ANY, 250000, + (void*)&terminal_cursor_handler); + if (terminal.private.timer.id < 0) { + terminal_close(); + return (-1); + } + terminal.private.color.bg = C_BLACK; + terminal.private.color.fg = C_WHITE; + terminal.private.watermark = TERM_PRIVATE_WATERMARK; + return (0); +} diff --git a/src/terminal/read.c b/src/terminal/read.c new file mode 100644 index 0000000..06be88b --- /dev/null +++ b/src/terminal/read.c @@ -0,0 +1,246 @@ +//--- +// fxBoot:terminal:read - Read primitive for the terminal device +//--- +#include "fxBoot/terminal.h" +#include +#include +#include +#include + +/* internal structure used to store many information */ +struct { + struct { + unsigned short x; + unsigned short y; + int visible; + off_t saved; + } cursor; + struct { + uint8_t alpha: 1; + uint8_t shift: 1; + uint8_t ctrl: 1; + uint8_t exit: 1; + uint8_t const: 4; + } mode; + struct { + size_t max; + size_t size; + off_t cursor; + char *data; + } buffer; +} term_rdinfo; + +//--- +// +//--- +void term_display_all(void) +{ + /* stop the timer too avoid interrupt-loop */ + if (terminal.private.timer.id >= 0) + timer_pause(terminal.private.timer.id); + + /* mark special characte that the cursor is here */ + if (term_rdinfo.cursor.visible == 1) + term_rdinfo.buffer.data[term_rdinfo.buffer.cursor] |= 0x80; + + /* restore terminal context */ + terminal.cursor.x = term_rdinfo.cursor.x; + terminal.cursor.y = term_rdinfo.cursor.y; + terminal.buffer.cursor = term_rdinfo.cursor.saved; + terminal_buffer_insert(term_rdinfo.buffer.data, + term_rdinfo.buffer.size); + + /* display management */ + dclear(terminal.private.color.bg); + terminal_buffer_display(); + dupdate(); + + /* remove cursor mark */ + term_rdinfo.buffer.data[term_rdinfo.buffer.cursor] &= ~0x80; + + /* restart the timer */ + if (terminal.private.timer.id >= 0) + timer_start(terminal.private.timer.id); +} + +//--- +// Callback function +//--- + +int terminal_cursor_handler(void) +{ + term_rdinfo.cursor.visible ^= 1; + term_display_all(); + return (0); +} + +//--- +// buffer functions +//--- + +/* term_buffer_remove(): Remove character based on current cursor position */ +static void term_buffer_remove(void) +{ + /* check if this action is possible */ + if (term_rdinfo.buffer.cursor == 0) + return; + /* move data if needed */ + if (term_rdinfo.buffer.cursor < term_rdinfo.buffer.size - 1) { + memcpy( + &term_rdinfo.buffer.data[term_rdinfo.buffer.cursor - 1], + &term_rdinfo.buffer.data[term_rdinfo.buffer.cursor], + term_rdinfo.buffer.size - term_rdinfo.buffer.cursor + ); + } + /* force NULL-char and update cursor/size */ + term_rdinfo.buffer.cursor = term_rdinfo.buffer.cursor - 1; + term_rdinfo.buffer.data[--term_rdinfo.buffer.size - 1] = '\0'; +} + +/* term_buffer_insert() - Insert character based on current cursor position */ +static int term_buffer_insert(char n) +{ + /* save space for the "\n\0" (EOL) */ + if (term_rdinfo.buffer.size + 1 >= term_rdinfo.buffer.max) + return (-1); + /* move data if needed */ + if (term_rdinfo.buffer.cursor < term_rdinfo.buffer.size - 1) { + off_t i = term_rdinfo.buffer.size + 1; + while (--i >= term_rdinfo.buffer.cursor) { + term_rdinfo.buffer.data[i] = + term_rdinfo.buffer.data[i - 1]; + } + } + /* insert the character and force NULL-char */ + term_rdinfo.buffer.data[term_rdinfo.buffer.cursor++] = n; + term_rdinfo.buffer.data[++term_rdinfo.buffer.size] = '\0'; + return (0); +} + +//--- +// key handling +//--- + +// TODO +// - F_UP -> history +// - F_DOWN -> history +static int term_key_handle_special(key_event_t key_event) +{ + switch (key_event.key) { + case KEY_SHIFT: term_rdinfo.mode.shift ^= 1; return (1); + case KEY_ALPHA: term_rdinfo.mode.alpha ^= 1; return (1); + case KEY_OPTN: term_rdinfo.mode.ctrl ^= 1; return (1); + case KEY_DOT: term_buffer_insert(' '); return (1); + case KEY_DEL: term_buffer_remove(); return (1); + case KEY_EXE: + /* Add End Of Line character */ + term_rdinfo.buffer.data[term_rdinfo.buffer.size - 1] = '\n'; + term_rdinfo.buffer.data[term_rdinfo.buffer.size] = '\0'; + + /* indicate that the EXE key has been pressed. */ + term_rdinfo.mode.exit = 1; + return (1); + case KEY_LEFT: + if (term_rdinfo.buffer.cursor > 0) + term_rdinfo.buffer.cursor -= 1; + return (1); + case KEY_RIGHT: + if (term_rdinfo.buffer.cursor < term_rdinfo.buffer.size - 1) + term_rdinfo.buffer.cursor += 1; + return (1); + default: + return (0); + } +} + +/* term_buffer_update() - Update the internal buffer with the given key code */ +static int term_key_buffer_update(key_event_t key_event) +{ + static const uint8_t keylist_alpha[] = { + KEY_XOT, KEY_LOG, KEY_LN, KEY_SIN, KEY_COS, KEY_TAN, + KEY_FRAC, KEY_FD, KEY_LEFTP, KEY_RIGHTP, KEY_COMMA, KEY_ARROW, + KEY_7, KEY_8, KEY_9, + KEY_4, KEY_5, KEY_6, KEY_MUL, KEY_DIV, + KEY_1, KEY_2, KEY_3, KEY_PLUS, KEY_MINUS, + KEY_0, 0xff + }; + static const uint8_t keylist_num[] = { + KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, + KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, + KEY_PLUS, KEY_MINUS, KEY_MUL, KEY_DIV, + KEY_LEFTP, KEY_RIGHTP, KEY_COMMA, KEY_POWER, + KEY_DOT, KEY_FD, KEY_ARROW, 0xff + }; + static const char keylist_num_char[] = "0123456789+-x/(),^.|_"; + const uint8_t *keycode_list; + char character; + int i; + + /* Get the appropriate key list. */ + keycode_list = keylist_alpha; + if (term_rdinfo.mode.shift == 1) + keycode_list = keylist_num; + + /* Try to find the pressed key. */ + i = -1; + while (keycode_list[++i] != 0xff && keycode_list[i] != key_event.key); + if (keycode_list[i] != key_event.key) + return (0); + + /* handle mode then update the buffer */ + if (term_rdinfo.mode.shift == 0) { + character = 'a' + i; + if (term_rdinfo.mode.alpha == 1) + character = 'A' + i;; + } else { + character = keylist_num_char[i]; + } + term_buffer_insert(character); + return (1); +} + + + +//--- +// primitive +//--- +int terminal_read(void *buffer, size_t nb) +{ + key_event_t key; + + /* check obvious error */ + if (buffer == NULL || nb == 0) + return (0); + + /* initialize internal data */ + memset(&term_rdinfo, 0x00, sizeof(term_rdinfo)); + memset(buffer, 0x00, nb); + + /* save terminal information */ + term_rdinfo.cursor.saved = terminal.buffer.cursor; + term_rdinfo.cursor.x = terminal.cursor.x; + term_rdinfo.cursor.y = terminal.cursor.y; + term_rdinfo.buffer.data = buffer; + term_rdinfo.buffer.size = 1; + term_rdinfo.buffer.max = nb; + + /* start cursor blink timer */ + if (terminal.private.timer.id >= 0) + timer_start(terminal.private.timer.id); + + /* keyboard handling */ + while (term_rdinfo.mode.exit == 0) { + /* handle pressed keys */ + key = getkey_opt(GETKEY_REP_ALL | GETKEY_MENU, NULL); + if (term_key_handle_special(key) == 0) + term_key_buffer_update(key); + + /* display */ + term_display_all(); + } + + /* stop the timer */ + if (terminal.private.timer.id >= 0) + timer_pause(terminal.private.timer.id); + return (term_rdinfo.buffer.size); +} diff --git a/src/terminal/util.c b/src/terminal/util.c new file mode 100644 index 0000000..b506321 --- /dev/null +++ b/src/terminal/util.c @@ -0,0 +1,166 @@ +//--- +// fxBoot:terminal:util - usefull function for handle the terminal device +//--- +#include "fxBoot/terminal.h" +#include +#include + + +//--- +// Update the internal buffer +//--- +void terminal_buffer_insert(char *buffer, size_t nb) +{ + size_t dump; + void *start; + + /* calculate the "real" number of byte to dump into the buffer */ + dump = nb; + start = &buffer[0]; + if (dump > terminal.buffer.size) { + dump -= terminal.buffer.size; + start = &buffer[nb - dump]; + } + + /* dump the buffer (be carful with the circular effect) */ + if (terminal.buffer.cursor + dump > terminal.buffer.size) { + memcpy( + &terminal.buffer.data[terminal.buffer.cursor], + start, + terminal.buffer.size - terminal.buffer.cursor + ); + dump -= terminal.buffer.size - terminal.buffer.cursor; + terminal.buffer.cursor = 0; + } + memcpy(&terminal.buffer.data[terminal.buffer.cursor], start, dump); + terminal.buffer.cursor += dump; +} + + +//--- +// Display the internal buffer +//--- + +/* terminal_vertical_update() - Update vertical cursor */ +static void terminal_vertical_update(void) +{ + if (terminal.cursor.y + 1 < terminal.winsize.ws_col) { + terminal.cursor.y = terminal.cursor.y + 1; + return; + } +} + +/* earlyterm_horizontal_update() - Update horizotal cursor */ +static int terminal_horizontal_update(void) +{ + terminal.cursor.x = terminal.cursor.x + 1; + if (terminal.cursor.x >= terminal.winsize.ws_col) { + terminal_vertical_update(); + terminal.cursor.x = 0; + return (1); + } + return (0); +} + +/* line_discipline() - Check "special" char */ +static int terminal_line_discipline(char n) +{ + int offset; + + switch (n) { + case '\0': + return (1); + case '\n': + terminal.cursor.x = 0; + terminal_vertical_update(); + return (1); + case '\b': + if (terminal.cursor.x > 0) + terminal.cursor.x = terminal.cursor.x - 1; + return (1); + case '\v': + terminal_vertical_update(); + return (1); + case '\r': + terminal.cursor.x = 0; + return (1); + case '\t': + /* Check if we need a new line or not. */ + offset = terminal.cursor.x - ((terminal.cursor.x / 5) * 5); + offset = 5 - offset; + while (--offset >= 0) + terminal_horizontal_update(); + return (1); + default: + return (0); + } +} + +void terminal_buffer_display(void) +{ + uint8_t *buffer; + uint16_t tmp; + int cursor; + int x; + int y; + int i; + + /* Due to potential special char, we sould find the "real" starting + index for the internal buffer */ + terminal.cursor.x = 0; + terminal.cursor.y = 0; + i = terminal.buffer.cursor - 1; + buffer = &terminal.buffer.data[0]; + while (1) { + /* decrease the cursor and avoid circular effect */ + if (--i < 0) + i = terminal.buffer.size - 1; + + /* check loop condition */ + if (i == (int)terminal.buffer.cursor) + break; + + /* check EOL */ + if (buffer[i] == '\0') { + break; + } + + /* handle the character (only to force update cursors) */ + if (terminal_line_discipline(buffer[i] & 0x7f) == 0) + terminal_horizontal_update(); + if (terminal.cursor.y >= terminal.winsize.ws_row) + break; + } + + /* Display character per character because we need to check special + behaviour (like cariege return, line feed, ...) */ + terminal.cursor.x = 0; + terminal.cursor.y = 0; + while (1) { + /* update the index */ + if (++i >= (int)terminal.buffer.size) + i = 0; + if (i == (int)terminal.buffer.cursor) + break; + + /* get the cursor and remove the potential cursor marker */ + cursor = ((buffer[i] & 0x80) != 0); + buffer[i] &= 0x7f; + + /* display part (character + cursor if needed) */ + x = terminal.cursor.x * terminal.winsize.ft_xpixel; + y = terminal.cursor.y * terminal.winsize.ft_ypixel; + if (terminal_line_discipline(buffer[i]) == 0) { + tmp = buffer[i] << 8; + dtext(x, y, terminal.private.color.fg, (void*)&tmp); + terminal_horizontal_update(); + } + if (cursor != 0) { + dline(x, + y + terminal.winsize.ft_ypixel, + x + terminal.winsize.ft_xpixel - 2, + y + terminal.winsize.ft_ypixel, + terminal.private.color.fg); + } + } +} diff --git a/src/terminal/write.c b/src/terminal/write.c new file mode 100644 index 0000000..d8f535d --- /dev/null +++ b/src/terminal/write.c @@ -0,0 +1,30 @@ +//--- +// fxBoot:terminal:write - Write primitive for terminal device +//--- +#include "fxBoot/terminal.h" +#include +#include +#include +#include + +/* terminal_write() - printf wrapper for the terminal device */ +int terminal_write(const char *format, ...) +{ + char buffer[1024]; + va_list ap; + int nb; + + /* process the format */ + va_start(ap, format); + nb = vsnprintf(buffer, 1024, format, ap); + va_end(ap); + + /* update the internal buffer */ + terminal_buffer_insert(buffer, nb); + + /* display the internal buffer */ + dclear(terminal.private.color.bg); + terminal_buffer_display(); + dupdate(); + return (nb); +}