diff --git a/usr/src/sp1/common/Makefile b/usr/src/sp1/common/Makefile index 7d029ec..b0738f0 100644 --- a/usr/src/sp1/common/Makefile +++ b/usr/src/sp1/common/Makefile @@ -10,13 +10,20 @@ # CFILES = $(shell find os/ -name "*.c") +CFILES += $(shell find io/ -name "*.c") +CFILES += $(shell find ../ext -name "*.c") +CFILES += $(shell find ../lib -name "*.c") + OFILES = $(CFILES:.c=.o) +DFILES = $(CFILES:.c=.d) CC = ../../../../$(SYS_CC) CFLAGS = \ $(SYS_CFLAGS) \ -I../head \ + -I../../head \ + -I../ext/flanterm/src/ \ -D_KERNEL \ -MMD \ -DPRINTF_DISABLE_SUPPORT_PTRDIFF_T \ @@ -25,5 +32,6 @@ CFLAGS = \ .PHONY: all all: $(OFILES) +-include $(DFILES) %.o: %.c $(CC) -c $< $(CFLAGS) -o $@ diff --git a/usr/src/sp1/common/io/cons/cons.c b/usr/src/sp1/common/io/cons/cons.c new file mode 100644 index 0000000..630b149 --- /dev/null +++ b/usr/src/sp1/common/io/cons/cons.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2026, Mirocom Laboratories + * All rights reserved. + * + * The following sources are CONFIDENTIAL and PROPRIETARY + * property of Mirocom Laboratories. Unauthorized copying, + * use, distrubution or modification of this file, in whole + * and in part, is strictly prohibited without the prior written + * consent from Mirocom Laboratories. + */ + +#include +#include +#include "flanterm.h" +#include "flanterm_backends/fb.h" + +#define FRAMEBUFFER fb_resp->framebuffers[0] + +static struct flanterm_context *ft_ctx = NULL; +static struct limine_framebuffer_response *fb_resp = NULL; +static struct limine_framebuffer_request fb_req = { + .id = LIMINE_FRAMEBUFFER_REQUEST, + .revision = 0 +}; + +void +cons_init(struct cons_attr *attr) +{ + if (attr == NULL) { + return; + } + + fb_resp = fb_req.response; + ft_ctx = flanterm_fb_init( + NULL, + NULL, + FRAMEBUFFER->address, + FRAMEBUFFER->width, + FRAMEBUFFER->height, + FRAMEBUFFER->pitch, + FRAMEBUFFER->red_mask_size, + FRAMEBUFFER->red_mask_shift, + FRAMEBUFFER->green_mask_size, + FRAMEBUFFER->green_mask_shift, + FRAMEBUFFER->blue_mask_size, + FRAMEBUFFER->blue_mask_shift, + NULL, + NULL, + NULL, + &attr->bg, + &attr->fg, + NULL, + NULL, + NULL, + 0, 0, 0, + 0, 0, 0, 0 + ); +} + +void +cons_write(const char *str, size_t len) +{ + if (fb_resp == NULL) { + return; + } + + if (str == NULL || len == 0) { + return; + } + + flanterm_write(ft_ctx, str, len); +} diff --git a/usr/src/sp1/common/os/main.c b/usr/src/sp1/common/os/main.c index f24f343..864833b 100644 --- a/usr/src/sp1/common/os/main.c +++ b/usr/src/sp1/common/os/main.c @@ -9,7 +9,18 @@ * consent from Mirocom Laboratories. */ +#include +#include + +/* Root console attribute */ +struct cons_attr cons_attr = { + .fg = 0x808080, + .bg = 0x000000 +}; + void main(void) { + /* Initialize the console */ + cons_init(&cons_attr); } diff --git a/usr/src/sp1/ext/flanterm/.gitignore b/usr/src/sp1/ext/flanterm/.gitignore new file mode 100644 index 0000000..cc62e43 --- /dev/null +++ b/usr/src/sp1/ext/flanterm/.gitignore @@ -0,0 +1,2 @@ +*.d +*.o \ No newline at end of file diff --git a/usr/src/sp1/ext/flanterm/LICENSE b/usr/src/sp1/ext/flanterm/LICENSE new file mode 100644 index 0000000..e18db68 --- /dev/null +++ b/usr/src/sp1/ext/flanterm/LICENSE @@ -0,0 +1,22 @@ +Copyright (C) 2022-2026 Mintsuki and contributors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/usr/src/sp1/ext/flanterm/README.md b/usr/src/sp1/ext/flanterm/README.md new file mode 100644 index 0000000..f392efc --- /dev/null +++ b/usr/src/sp1/ext/flanterm/README.md @@ -0,0 +1,43 @@ +# Flanterm + +Flanterm is a fast and reasonably complete terminal emulator with support for +multiple output backends. Included is a fast framebuffer backend. + +### Quick usage + +To quickly set up and use a framebuffer Flanterm instance, it is possible to +use the `flanterm_fb_init()` function as such: +```c +#include +#include + +struct flanterm_context *ft_ctx = flanterm_fb_init( + NULL, + NULL, + framebuffer_ptr, width, height, pitch, + red_mask_size, red_mask_shift, + green_mask_size, green_mask_shift, + blue_mask_size, blue_mask_shift, + NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, 0, 0, 1, + 0, 0, + 0 + ); +``` +Where `framebuffer_ptr, width, height, pitch` and `{red,green,blue}_mask_{size,shift}` +represent the corresponding info about the framebuffer to use for this given instance. + +The meaning of the other arguments can be found in `flanterm_backends/fb.h`. + +To then print to the terminal instance, simply use the `flanterm_write()` +function on the given instance. For example: +```c +#include + +const char msg[] = "Hello world\n"; + +flanterm_write(ft_ctx, msg, sizeof(msg)); +``` diff --git a/usr/src/sp1/ext/flanterm/src/flanterm.c b/usr/src/sp1/ext/flanterm/src/flanterm.c new file mode 100644 index 0000000..b53e2d6 --- /dev/null +++ b/usr/src/sp1/ext/flanterm/src/flanterm.c @@ -0,0 +1,1424 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* Copyright (C) 2022-2026 Mintsuki and contributors. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __cplusplus +#error "Please do not compile Flanterm as C++ code! Flanterm should be compiled as C99 or newer." +#endif + +#ifndef __STDC_VERSION__ +#error "Flanterm must be compiled as C99 or newer." +#endif + +#include + +#ifndef FLANTERM_IN_FLANTERM +#define FLANTERM_IN_FLANTERM +#endif + +#include "flanterm.h" + +// Tries to implement this standard for terminfo +// https://man7.org/linux/man-pages/man4/console_codes.4.html + +static const uint32_t col256[] = { + 0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, + 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af, + 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, + 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, + 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, + 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, + 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f, + 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, + 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, + 0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, + 0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af, + 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, + 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, + 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, + 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, + 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, + 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, + 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, + 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, + 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, + 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, + 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, + 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, + 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, + 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, + 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, + 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, + 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, + 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, + 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee +}; + +#define CHARSET_DEFAULT 0 +#define CHARSET_DEC_SPECIAL 1 + +void flanterm_context_reinit(struct flanterm_context *ctx) { + ctx->tab_size = 8; + ctx->autoflush = true; + ctx->cursor_enabled = true; + ctx->scroll_enabled = true; + ctx->control_sequence = false; + ctx->escape = false; + ctx->osc = false; + ctx->osc_escape = false; + ctx->rrr = false; + ctx->discard_next = false; + ctx->bold = false; + ctx->bg_bold = false; + ctx->reverse_video = false; + ctx->dec_private = false; + ctx->insert_mode = false; + ctx->csi_unhandled = false; + ctx->unicode_remaining = 0; + ctx->g_select = 0; + ctx->charsets[0] = CHARSET_DEFAULT; + ctx->charsets[1] = CHARSET_DEC_SPECIAL; + ctx->current_charset = 0; + ctx->escape_offset = 0; + ctx->esc_values_i = 0; + ctx->saved_cursor_x = 0; + ctx->saved_cursor_y = 0; + ctx->current_primary = (size_t)-1; + ctx->current_bg = (size_t)-1; + ctx->scroll_top_margin = 0; + ctx->scroll_bottom_margin = ctx->rows; + ctx->oob_output = FLANTERM_OOB_OUTPUT_ONLCR; +} + +static void flanterm_putchar(struct flanterm_context *ctx, uint8_t c); + +void flanterm_write(struct flanterm_context *ctx, const char *buf, size_t count) { + for (size_t i = 0; i < count; i++) { + flanterm_putchar(ctx, buf[i]); + } + + if (ctx->autoflush) { + ctx->double_buffer_flush(ctx); + } +} + +static void sgr(struct flanterm_context *ctx) { + size_t i = 0; + + if (!ctx->esc_values_i) + goto def; + + for (; i < ctx->esc_values_i; i++) { + size_t offset; + + if (ctx->esc_values[i] == 0) { +def: + if (ctx->reverse_video) { + ctx->reverse_video = false; + ctx->swap_palette(ctx); + } + ctx->bold = false; + ctx->bg_bold = false; + ctx->current_primary = (size_t)-1; + ctx->current_bg = (size_t)-1; + ctx->set_text_bg_default(ctx); + ctx->set_text_fg_default(ctx); + continue; + } + + else if (ctx->esc_values[i] == 1) { + ctx->bold = true; + if (ctx->current_primary != (size_t)-1) { + if (!ctx->reverse_video) { + ctx->set_text_fg_bright(ctx, ctx->current_primary); + } else { + ctx->set_text_bg_bright(ctx, ctx->current_primary); + } + } else { + if (!ctx->reverse_video) { + ctx->set_text_fg_default_bright(ctx); + } else { + ctx->set_text_bg_default_bright(ctx); + } + } + continue; + } + + else if (ctx->esc_values[i] == 5) { + ctx->bg_bold = true; + if (ctx->current_bg != (size_t)-1) { + if (!ctx->reverse_video) { + ctx->set_text_bg_bright(ctx, ctx->current_bg); + } else { + ctx->set_text_fg_bright(ctx, ctx->current_bg); + } + } else { + if (!ctx->reverse_video) { + ctx->set_text_bg_default_bright(ctx); + } else { + ctx->set_text_fg_default_bright(ctx); + } + } + continue; + } + + else if (ctx->esc_values[i] == 22) { + ctx->bold = false; + if (ctx->current_primary != (size_t)-1) { + if (!ctx->reverse_video) { + ctx->set_text_fg(ctx, ctx->current_primary); + } else { + ctx->set_text_bg(ctx, ctx->current_primary); + } + } else { + if (!ctx->reverse_video) { + ctx->set_text_fg_default(ctx); + } else { + ctx->set_text_bg_default(ctx); + } + } + continue; + } + + else if (ctx->esc_values[i] == 25) { + ctx->bg_bold = false; + if (ctx->current_bg != (size_t)-1) { + if (!ctx->reverse_video) { + ctx->set_text_bg(ctx, ctx->current_bg); + } else { + ctx->set_text_fg(ctx, ctx->current_bg); + } + } else { + if (!ctx->reverse_video) { + ctx->set_text_bg_default(ctx); + } else { + ctx->set_text_fg_default(ctx); + } + } + continue; + } + + else if (ctx->esc_values[i] >= 30 && ctx->esc_values[i] <= 37) { + offset = 30; + ctx->current_primary = ctx->esc_values[i] - offset; + + if (ctx->reverse_video) { + goto set_bg; + } + +set_fg: + if ((ctx->bold && !ctx->reverse_video) + || (ctx->bg_bold && ctx->reverse_video)) { + ctx->set_text_fg_bright(ctx, ctx->esc_values[i] - offset); + } else { + ctx->set_text_fg(ctx, ctx->esc_values[i] - offset); + } + continue; + } + + else if (ctx->esc_values[i] >= 40 && ctx->esc_values[i] <= 47) { + offset = 40; + ctx->current_bg = ctx->esc_values[i] - offset; + + if (ctx->reverse_video) { + goto set_fg; + } + +set_bg: + if ((ctx->bold && ctx->reverse_video) + || (ctx->bg_bold && !ctx->reverse_video)) { + ctx->set_text_bg_bright(ctx, ctx->esc_values[i] - offset); + } else { + ctx->set_text_bg(ctx, ctx->esc_values[i] - offset); + } + continue; + } + + else if (ctx->esc_values[i] >= 90 && ctx->esc_values[i] <= 97) { + offset = 90; + ctx->current_primary = ctx->esc_values[i] - offset; + + if (ctx->reverse_video) { + goto set_bg_bright; + } + +set_fg_bright: + ctx->set_text_fg_bright(ctx, ctx->esc_values[i] - offset); + continue; + } + + else if (ctx->esc_values[i] >= 100 && ctx->esc_values[i] <= 107) { + offset = 100; + ctx->current_bg = ctx->esc_values[i] - offset; + + if (ctx->reverse_video) { + goto set_fg_bright; + } + +set_bg_bright: + ctx->set_text_bg_bright(ctx, ctx->esc_values[i] - offset); + continue; + } + + else if (ctx->esc_values[i] == 39) { + ctx->current_primary = (size_t)-1; + + if (ctx->reverse_video) { + ctx->swap_palette(ctx); + } + + if (!ctx->bold) { + ctx->set_text_fg_default(ctx); + } else { + ctx->set_text_fg_default_bright(ctx); + } + + if (ctx->reverse_video) { + ctx->swap_palette(ctx); + } + + continue; + } + + else if (ctx->esc_values[i] == 49) { + ctx->current_bg = (size_t)-1; + + if (ctx->reverse_video) { + ctx->swap_palette(ctx); + } + + if (!ctx->bg_bold) { + ctx->set_text_bg_default(ctx); + } else { + ctx->set_text_bg_default_bright(ctx); + } + + if (ctx->reverse_video) { + ctx->swap_palette(ctx); + } + + continue; + } + + else if (ctx->esc_values[i] == 7) { + if (!ctx->reverse_video) { + ctx->reverse_video = true; + ctx->swap_palette(ctx); + } + continue; + } + + else if (ctx->esc_values[i] == 27) { + if (ctx->reverse_video) { + ctx->reverse_video = false; + ctx->swap_palette(ctx); + } + continue; + } + + // 256/RGB + else if (ctx->esc_values[i] == 38 || ctx->esc_values[i] == 48) { + bool fg = ctx->esc_values[i] == 38; + + i++; + if (i >= ctx->esc_values_i) { + break; + } + + switch (ctx->esc_values[i]) { + case 2: { // RGB + if (i + 3 >= ctx->esc_values_i) { + goto out; + } + + uint32_t rgb_value = 0; + + rgb_value |= ctx->esc_values[i + 1] << 16; + rgb_value |= ctx->esc_values[i + 2] << 8; + rgb_value |= ctx->esc_values[i + 3]; + + i += 3; + + (fg ? ctx->set_text_fg_rgb : ctx->set_text_bg_rgb)(ctx, rgb_value); + + break; + } + case 5: { // 256 colors + if (i + 1 >= ctx->esc_values_i) { + goto out; + } + + uint32_t col = ctx->esc_values[i + 1]; + + i++; + + if (col < 8) { + (fg ? ctx->set_text_fg : ctx->set_text_bg)(ctx, col); + } else if (col < 16) { + (fg ? ctx->set_text_fg_bright : ctx->set_text_bg_bright)(ctx, col - 8); + } else if (col < 256) { + uint32_t rgb_value = col256[col - 16]; + (fg ? ctx->set_text_fg_rgb : ctx->set_text_bg_rgb)(ctx, rgb_value); + } + + break; + } + default: continue; + } + } + } + +out:; +} + +static void dec_private_parse(struct flanterm_context *ctx, uint8_t c) { + ctx->dec_private = false; + + if (ctx->esc_values_i == 0) { + return; + } + + bool set; + + switch (c) { + case 'h': + set = true; break; + case 'l': + set = false; break; + default: + return; + } + + switch (ctx->esc_values[0]) { + case 25: { + if (set) { + ctx->cursor_enabled = true; + } else { + ctx->cursor_enabled = false; + } + return; + } + } + + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_DEC, ctx->esc_values_i, (uintptr_t)ctx->esc_values, c); + } +} + +static void linux_private_parse(struct flanterm_context *ctx) { + if (ctx->esc_values_i == 0) { + return; + } + + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_LINUX, ctx->esc_values_i, (uintptr_t)ctx->esc_values, 0); + } +} + +static void mode_toggle(struct flanterm_context *ctx, uint8_t c) { + if (ctx->esc_values_i == 0) { + return; + } + + bool set; + + switch (c) { + case 'h': + set = true; break; + case 'l': + set = false; break; + default: + return; + } + + switch (ctx->esc_values[0]) { + case 4: + ctx->insert_mode = set; return; + } + + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_MODE, ctx->esc_values_i, (uintptr_t)ctx->esc_values, c); + } +} + +static bool osc_parse(struct flanterm_context *ctx, uint8_t c) { + // ESC \ terminates an OSC sequence cleanly + // but if ESC is followed by non-\, report failure from osc_parse and + // try parsing the character as another escape code + if (ctx->osc_escape) { + if (c == '\\') { + ctx->osc = false; + ctx->osc_escape = false; + ctx->escape = false; + return true; + } else { + ctx->osc_escape = false; + ctx->osc = false; + // escape stays true here + return false; + } + } + switch (c) { + case 0x1b: + ctx->osc_escape = true; + break; + // BEL is the other terminator + case '\a': + ctx->osc_escape = false; + ctx->osc = false; + ctx->escape = false; + break; + default: + break; + } + return true; +} + +static void control_sequence_parse(struct flanterm_context *ctx, uint8_t c) { + if (ctx->escape_offset == 2) { + switch (c) { + case '[': + ctx->discard_next = true; + goto cleanup; + case '?': + ctx->dec_private = true; + return; + } + } + + if (c >= '0' && c <= '9') { + if (ctx->esc_values_i == FLANTERM_MAX_ESC_VALUES) { + return; + } + ctx->rrr = true; + ctx->esc_values[ctx->esc_values_i] *= 10; + ctx->esc_values[ctx->esc_values_i] += c - '0'; + return; + } + + if (ctx->rrr == true) { + ctx->esc_values_i++; + ctx->rrr = false; + if (c == ';') + return; + } else if (c == ';') { + if (ctx->esc_values_i == FLANTERM_MAX_ESC_VALUES) { + return; + } + ctx->esc_values[ctx->esc_values_i] = 0; + ctx->esc_values_i++; + return; + } + + size_t esc_default; + switch (c) { + case 'J': case 'K': case 'q': + esc_default = 0; break; + default: + esc_default = 1; break; + } + + for (size_t i = ctx->esc_values_i; i < FLANTERM_MAX_ESC_VALUES; i++) { + ctx->esc_values[i] = esc_default; + } + + if (ctx->dec_private == true) { + dec_private_parse(ctx, c); + goto cleanup; + } + + bool r = ctx->scroll_enabled; + ctx->scroll_enabled = false; + size_t x, y; + ctx->get_cursor_pos(ctx, &x, &y); + + // CSI sequences are terminated by a byte in [0x40,0x7E] + // so skip all bytes until the terminator byte + if (ctx->csi_unhandled) { + if (c >= 0x40 && c <= 0x7E) { + ctx->csi_unhandled = false; + goto cleanup; + } + return; + } + + switch (c) { + // Got ESC in the middle of an escape sequence, start a new one + case 0x1B: + return; + case 'F': + x = 0; + // FALLTHRU + case 'A': { + if (ctx->esc_values[0] > y) + ctx->esc_values[0] = y; + size_t orig_y = y; + size_t dest_y = y - ctx->esc_values[0]; + bool will_be_in_scroll_region = false; + if ((ctx->scroll_top_margin >= dest_y && ctx->scroll_top_margin <= orig_y) + || (ctx->scroll_bottom_margin >= dest_y && ctx->scroll_bottom_margin <= orig_y)) { + will_be_in_scroll_region = true; + } + if (will_be_in_scroll_region && dest_y < ctx->scroll_top_margin) { + dest_y = ctx->scroll_top_margin; + } + ctx->set_cursor_pos(ctx, x, dest_y); + break; + } + case 'E': + x = 0; + // FALLTHRU + case 'e': + case 'B': { + if (y + ctx->esc_values[0] > ctx->rows - 1) + ctx->esc_values[0] = (ctx->rows - 1) - y; + size_t orig_y = y; + size_t dest_y = y + ctx->esc_values[0]; + bool will_be_in_scroll_region = false; + if ((ctx->scroll_top_margin >= orig_y && ctx->scroll_top_margin <= dest_y) + || (ctx->scroll_bottom_margin >= orig_y && ctx->scroll_bottom_margin <= dest_y)) { + will_be_in_scroll_region = true; + } + if (will_be_in_scroll_region && dest_y >= ctx->scroll_bottom_margin) { + dest_y = ctx->scroll_bottom_margin - 1; + } + ctx->set_cursor_pos(ctx, x, dest_y); + break; + } + case 'a': + case 'C': + if (x + ctx->esc_values[0] > ctx->cols - 1) + ctx->esc_values[0] = (ctx->cols - 1) - x; + ctx->set_cursor_pos(ctx, x + ctx->esc_values[0], y); + break; + case 'D': + if (ctx->esc_values[0] > x) + ctx->esc_values[0] = x; + ctx->set_cursor_pos(ctx, x - ctx->esc_values[0], y); + break; + case 'c': + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_PRIVATE_ID, 0, 0, 0); + } + break; + case 'd': + ctx->esc_values[0] -= 1; + if (ctx->esc_values[0] >= ctx->rows) + ctx->esc_values[0] = ctx->rows - 1; + ctx->set_cursor_pos(ctx, x, ctx->esc_values[0]); + break; + case 'G': + case '`': + ctx->esc_values[0] -= 1; + if (ctx->esc_values[0] >= ctx->cols) + ctx->esc_values[0] = ctx->cols - 1; + ctx->set_cursor_pos(ctx, ctx->esc_values[0], y); + break; + case 'H': + case 'f': + if (ctx->esc_values[0] != 0) { + ctx->esc_values[0]--; + } + if (ctx->esc_values[1] != 0) { + ctx->esc_values[1]--; + } + if (ctx->esc_values[1] >= ctx->cols) + ctx->esc_values[1] = ctx->cols - 1; + if (ctx->esc_values[0] >= ctx->rows) + ctx->esc_values[0] = ctx->rows - 1; + ctx->set_cursor_pos(ctx, ctx->esc_values[1], ctx->esc_values[0]); + break; + case 'M': { + size_t count = ctx->esc_values[0] > ctx->rows ? ctx->rows : ctx->esc_values[0]; + for (size_t i = 0; i < count; i++) { + ctx->scroll(ctx); + } + break; + } + case 'L': { + size_t old_scroll_top_margin = ctx->scroll_top_margin; + ctx->scroll_top_margin = y; + size_t count = ctx->esc_values[0] > ctx->rows ? ctx->rows : ctx->esc_values[0]; + for (size_t i = 0; i < count; i++) { + ctx->revscroll(ctx); + } + ctx->scroll_top_margin = old_scroll_top_margin; + break; + } + case 'n': + switch (ctx->esc_values[0]) { + case 5: + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_STATUS_REPORT, 0, 0, 0); + } + break; + case 6: + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_POS_REPORT, x + 1, y + 1, 0); + } + break; + } + break; + case 'q': + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_KBD_LEDS, ctx->esc_values[0], 0, 0); + } + break; + case 'J': + switch (ctx->esc_values[0]) { + case 0: { + size_t rows_remaining = ctx->rows - (y + 1); + size_t cols_diff = ctx->cols - (x + 1); + size_t to_clear = rows_remaining * ctx->cols + cols_diff + 1; + for (size_t i = 0; i < to_clear; i++) { + ctx->raw_putchar(ctx, ' '); + } + ctx->set_cursor_pos(ctx, x, y); + break; + } + case 1: { + ctx->set_cursor_pos(ctx, 0, 0); + bool b = false; + for (size_t yc = 0; yc < ctx->rows; yc++) { + for (size_t xc = 0; xc < ctx->cols; xc++) { + ctx->raw_putchar(ctx, ' '); + if (xc == x && yc == y) { + ctx->set_cursor_pos(ctx, x, y); + b = true; + break; + } + } + if (b == true) + break; + } + break; + } + case 2: + case 3: + ctx->clear(ctx, false); + break; + } + break; + case '@': + for (size_t i = ctx->cols - 1; ; i--) { + ctx->move_character(ctx, i + ctx->esc_values[0], y, i, y); + ctx->set_cursor_pos(ctx, i, y); + ctx->raw_putchar(ctx, ' '); + if (i == x) { + break; + } + } + ctx->set_cursor_pos(ctx, x, y); + break; + case 'P': + for (size_t i = x + ctx->esc_values[0]; i < ctx->cols; i++) + ctx->move_character(ctx, i - ctx->esc_values[0], y, i, y); + ctx->set_cursor_pos(ctx, ctx->cols - ctx->esc_values[0], y); + // FALLTHRU + case 'X': { + size_t count = ctx->esc_values[0] > ctx->cols ? ctx->cols : ctx->esc_values[0]; + for (size_t i = 0; i < count; i++) + ctx->raw_putchar(ctx, ' '); + ctx->set_cursor_pos(ctx, x, y); + break; + } + case 'm': + sgr(ctx); + break; + case 's': + ctx->get_cursor_pos(ctx, &ctx->saved_cursor_x, &ctx->saved_cursor_y); + break; + case 'u': + ctx->set_cursor_pos(ctx, ctx->saved_cursor_x, ctx->saved_cursor_y); + break; + case 'K': + switch (ctx->esc_values[0]) { + case 0: { + for (size_t i = x; i < ctx->cols; i++) + ctx->raw_putchar(ctx, ' '); + ctx->set_cursor_pos(ctx, x, y); + break; + } + case 1: { + ctx->set_cursor_pos(ctx, 0, y); + for (size_t i = 0; i < x; i++) + ctx->raw_putchar(ctx, ' '); + break; + } + case 2: { + ctx->set_cursor_pos(ctx, 0, y); + for (size_t i = 0; i < ctx->cols; i++) + ctx->raw_putchar(ctx, ' '); + ctx->set_cursor_pos(ctx, x, y); + break; + } + } + break; + case 'r': + if (ctx->esc_values[0] == 0) { + ctx->esc_values[0] = 1; + } + if (ctx->esc_values[1] == 0) { + ctx->esc_values[1] = 1; + } + ctx->scroll_top_margin = 0; + ctx->scroll_bottom_margin = ctx->rows; + if (ctx->esc_values_i > 0) { + ctx->scroll_top_margin = ctx->esc_values[0] - 1; + } + if (ctx->esc_values_i > 1) { + ctx->scroll_bottom_margin = ctx->esc_values[1]; + } + if (ctx->scroll_top_margin >= ctx->rows + || ctx->scroll_bottom_margin > ctx->rows + || ctx->scroll_top_margin >= (ctx->scroll_bottom_margin - 1)) { + ctx->scroll_top_margin = 0; + ctx->scroll_bottom_margin = ctx->rows; + } + ctx->set_cursor_pos(ctx, 0, 0); + break; + case 'l': + case 'h': + mode_toggle(ctx, c); + break; + case ']': + linux_private_parse(ctx); + break; + default: + ctx->csi_unhandled = true; + return; + } + + ctx->scroll_enabled = r; + +cleanup: + ctx->control_sequence = false; + ctx->escape = false; +} + +static void restore_state(struct flanterm_context *ctx) { + ctx->bold = ctx->saved_state_bold; + ctx->bg_bold = ctx->saved_state_bg_bold; + ctx->reverse_video = ctx->saved_state_reverse_video; + ctx->current_charset = ctx->saved_state_current_charset; + ctx->current_primary = ctx->saved_state_current_primary; + ctx->current_bg = ctx->saved_state_current_bg; + + ctx->restore_state(ctx); +} + +static void save_state(struct flanterm_context *ctx) { + ctx->save_state(ctx); + + ctx->saved_state_bold = ctx->bold; + ctx->saved_state_bg_bold = ctx->bg_bold; + ctx->saved_state_reverse_video = ctx->reverse_video; + ctx->saved_state_current_charset = ctx->current_charset; + ctx->saved_state_current_primary = ctx->current_primary; + ctx->saved_state_current_bg = ctx->current_bg; +} + +static void escape_parse(struct flanterm_context *ctx, uint8_t c) { + ctx->escape_offset++; + + if (ctx->osc == true) { + // ESC \ is one of the two possible terminators of OSC sequences, + // so osc_parse consumes ESC. + // If it is then followed by \ it cleans correctly, + // otherwise it returns false, and it tries parsing it as another escape sequence + if (osc_parse(ctx, c)) { + return; + } + } + + if (ctx->control_sequence == true) { + control_sequence_parse(ctx, c); + return; + } + + size_t x, y; + ctx->get_cursor_pos(ctx, &x, &y); + + switch (c) { + case ']': + ctx->osc_escape = false; + ctx->osc = true; + return; + case '[': + for (size_t i = 0; i < FLANTERM_MAX_ESC_VALUES; i++) + ctx->esc_values[i] = 0; + ctx->esc_values_i = 0; + ctx->rrr = false; + ctx->csi_unhandled = false; + ctx->control_sequence = true; + return; + case '7': + save_state(ctx); + break; + case '8': + restore_state(ctx); + break; + case 'c': + flanterm_context_reinit(ctx); + ctx->clear(ctx, true); + break; + case 'D': + if (y == ctx->scroll_bottom_margin - 1) { + ctx->scroll(ctx); + ctx->set_cursor_pos(ctx, x, y); + } else { + ctx->set_cursor_pos(ctx, x, y + 1); + } + break; + case 'E': + if (y == ctx->scroll_bottom_margin - 1) { + ctx->scroll(ctx); + ctx->set_cursor_pos(ctx, 0, y); + } else { + ctx->set_cursor_pos(ctx, 0, y + 1); + } + break; + case 'M': + // "Reverse linefeed" + if (y == ctx->scroll_top_margin) { + ctx->revscroll(ctx); + ctx->set_cursor_pos(ctx, 0, y); + } else { + ctx->set_cursor_pos(ctx, 0, y - 1); + } + break; + case 'Z': + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_PRIVATE_ID, 0, 0, 0); + } + break; + case '(': + case ')': + ctx->g_select = c - '\''; + break; + } + + ctx->escape = false; +} + +static bool dec_special_print(struct flanterm_context *ctx, uint8_t c) { +#define FLANTERM_DEC_SPCL_PRN(C) ctx->raw_putchar(ctx, (C)); return true; + switch (c) { + case '`': FLANTERM_DEC_SPCL_PRN(0x04) + case '0': FLANTERM_DEC_SPCL_PRN(0xdb) + case '-': FLANTERM_DEC_SPCL_PRN(0x18) + case ',': FLANTERM_DEC_SPCL_PRN(0x1b) + case '.': FLANTERM_DEC_SPCL_PRN(0x19) + case 'a': FLANTERM_DEC_SPCL_PRN(0xb1) + case 'f': FLANTERM_DEC_SPCL_PRN(0xf8) + case 'g': FLANTERM_DEC_SPCL_PRN(0xf1) + case 'h': FLANTERM_DEC_SPCL_PRN(0xb0) + case 'j': FLANTERM_DEC_SPCL_PRN(0xd9) + case 'k': FLANTERM_DEC_SPCL_PRN(0xbf) + case 'l': FLANTERM_DEC_SPCL_PRN(0xda) + case 'm': FLANTERM_DEC_SPCL_PRN(0xc0) + case 'n': FLANTERM_DEC_SPCL_PRN(0xc5) + case 'q': FLANTERM_DEC_SPCL_PRN(0xc4) + case 's': FLANTERM_DEC_SPCL_PRN(0x5f) + case 't': FLANTERM_DEC_SPCL_PRN(0xc3) + case 'u': FLANTERM_DEC_SPCL_PRN(0xb4) + case 'v': FLANTERM_DEC_SPCL_PRN(0xc1) + case 'w': FLANTERM_DEC_SPCL_PRN(0xc2) + case 'x': FLANTERM_DEC_SPCL_PRN(0xb3) + case 'y': FLANTERM_DEC_SPCL_PRN(0xf3) + case 'z': FLANTERM_DEC_SPCL_PRN(0xf2) + case '~': FLANTERM_DEC_SPCL_PRN(0xfa) + case '_': FLANTERM_DEC_SPCL_PRN(0xff) + case '+': FLANTERM_DEC_SPCL_PRN(0x1a) + case '{': FLANTERM_DEC_SPCL_PRN(0xe3) + case '}': FLANTERM_DEC_SPCL_PRN(0x9c) + } +#undef FLANTERM_DEC_SPCL_PRN + + return false; +} + +// Following wcwidth related code inherited from: +// https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + +struct interval { + uint32_t first; + uint32_t last; +}; + +/* auxiliary function for binary search in interval table */ +static int bisearch(uint32_t ucs, const struct interval *table, int max) { + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + +int mk_wcwidth(uint32_t ucs) { + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ + static const struct interval combining[] = { + { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, + { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, + { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, + { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, + { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, + { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, + { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, + { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, + { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, + { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, + { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, + { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, + { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, + { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, + { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, + { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, + { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, + { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, + { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, + { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, + { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, + { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, + { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, + { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, + { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, + { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, + { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, + { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, + { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, + { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, + { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, + { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, + { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, + { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, + { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, + { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, + { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, + { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, + { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, + { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, + { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, + { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, + { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, + { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, + { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, + { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, + { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, + { 0xE0100, 0xE01EF } + }; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) + return 1; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, combining, + sizeof(combining) / sizeof(struct interval) - 1)) + return 0; + + /* if we arrive here, ucs is not a combining or C0/C1 control character */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + ucs == 0x2329 || ucs == 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2fffd) || + (ucs >= 0x30000 && ucs <= 0x3fffd))); +} + +// End of https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c inherited code + +static int unicode_to_cp437(uint64_t code_point) { + switch (code_point) { + case 0x263a: return 1; + case 0x263b: return 2; + case 0x2665: return 3; + case 0x2666: return 4; + case 0x2663: return 5; + case 0x2660: return 6; + case 0x2022: return 7; + case 0x25d8: return 8; + case 0x25cb: return 9; + case 0x25d9: return 10; + case 0x2642: return 11; + case 0x2640: return 12; + case 0x266a: return 13; + case 0x266b: return 14; + case 0x263c: return 15; + case 0x25ba: return 16; + case 0x25c4: return 17; + case 0x2195: return 18; + case 0x203c: return 19; + case 0x00b6: return 20; + case 0x00a7: return 21; + case 0x25ac: return 22; + case 0x21a8: return 23; + case 0x2191: return 24; + case 0x2193: return 25; + case 0x2192: return 26; + case 0x2190: return 27; + case 0x221f: return 28; + case 0x2194: return 29; + case 0x25b2: return 30; + case 0x25bc: return 31; + + case 0x2302: return 127; + case 0x00c7: return 128; + case 0x00fc: return 129; + case 0x00e9: return 130; + case 0x00e2: return 131; + case 0x00e4: return 132; + case 0x00e0: return 133; + case 0x00e5: return 134; + case 0x00e7: return 135; + case 0x00ea: return 136; + case 0x00eb: return 137; + case 0x00e8: return 138; + case 0x00ef: return 139; + case 0x00ee: return 140; + case 0x00ec: return 141; + case 0x00c4: return 142; + case 0x00c5: return 143; + case 0x00c9: return 144; + case 0x00e6: return 145; + case 0x00c6: return 146; + case 0x00f4: return 147; + case 0x00f6: return 148; + case 0x00f2: return 149; + case 0x00fb: return 150; + case 0x00f9: return 151; + case 0x00ff: return 152; + case 0x00d6: return 153; + case 0x00dc: return 154; + case 0x00a2: return 155; + case 0x00a3: return 156; + case 0x00a5: return 157; + case 0x20a7: return 158; + case 0x0192: return 159; + case 0x00e1: return 160; + case 0x00ed: return 161; + case 0x00f3: return 162; + case 0x00fa: return 163; + case 0x00f1: return 164; + case 0x00d1: return 165; + case 0x00aa: return 166; + case 0x00ba: return 167; + case 0x00bf: return 168; + case 0x2310: return 169; + case 0x00ac: return 170; + case 0x00bd: return 171; + case 0x00bc: return 172; + case 0x00a1: return 173; + case 0x00ab: return 174; + case 0x00bb: return 175; + case 0x2591: return 176; + case 0x2592: return 177; + case 0x2593: return 178; + case 0x2502: return 179; + case 0x2524: return 180; + case 0x2561: return 181; + case 0x2562: return 182; + case 0x2556: return 183; + case 0x2555: return 184; + case 0x2563: return 185; + case 0x2551: return 186; + case 0x2557: return 187; + case 0x255d: return 188; + case 0x255c: return 189; + case 0x255b: return 190; + case 0x2510: return 191; + case 0x2514: return 192; + case 0x2534: return 193; + case 0x252c: return 194; + case 0x251c: return 195; + case 0x2500: return 196; + case 0x253c: return 197; + case 0x255e: return 198; + case 0x255f: return 199; + case 0x255a: return 200; + case 0x2554: return 201; + case 0x2569: return 202; + case 0x2566: return 203; + case 0x2560: return 204; + case 0x2550: return 205; + case 0x256c: return 206; + case 0x2567: return 207; + case 0x2568: return 208; + case 0x2564: return 209; + case 0x2565: return 210; + case 0x2559: return 211; + case 0x2558: return 212; + case 0x2552: return 213; + case 0x2553: return 214; + case 0x256b: return 215; + case 0x256a: return 216; + case 0x2518: return 217; + case 0x250c: return 218; + case 0x2588: return 219; + case 0x2584: return 220; + case 0x258c: return 221; + case 0x2590: return 222; + case 0x2580: return 223; + case 0x03b1: return 224; + case 0x00df: return 225; + case 0x0393: return 226; + case 0x03c0: return 227; + case 0x03a3: return 228; + case 0x03c3: return 229; + case 0x00b5: return 230; + case 0x03c4: return 231; + case 0x03a6: return 232; + case 0x0398: return 233; + case 0x03a9: return 234; + case 0x03b4: return 235; + case 0x221e: return 236; + case 0x03c6: return 237; + case 0x03b5: return 238; + case 0x2229: return 239; + case 0x2261: return 240; + case 0x00b1: return 241; + case 0x2265: return 242; + case 0x2264: return 243; + case 0x2320: return 244; + case 0x2321: return 245; + case 0x00f7: return 246; + case 0x2248: return 247; + case 0x00b0: return 248; + case 0x2219: return 249; + case 0x00b7: return 250; + case 0x221a: return 251; + case 0x207f: return 252; + case 0x00b2: return 253; + case 0x25a0: return 254; + } + + return -1; +} + +static void flanterm_putchar(struct flanterm_context *ctx, uint8_t c) { + if (ctx->discard_next || (c == 0x18 || c == 0x1a)) { + ctx->discard_next = false; + ctx->escape = false; + ctx->control_sequence = false; + ctx->unicode_remaining = 0; + ctx->osc = false; + ctx->osc_escape = false; + ctx->g_select = 0; + return; + } + + if (ctx->unicode_remaining != 0) { + if ((c & 0xc0) != 0x80) { + ctx->unicode_remaining = 0; + goto unicode_error; + } + + ctx->unicode_remaining--; + ctx->code_point |= (uint64_t)(c & 0x3f) << (6 * ctx->unicode_remaining); + if (ctx->unicode_remaining != 0) { + return; + } + + int cc = unicode_to_cp437(ctx->code_point); + + if (cc == -1) { + size_t replacement_width = (size_t)mk_wcwidth(ctx->code_point); + if (replacement_width > 0) { + ctx->raw_putchar(ctx, 0xfe); + } + for (size_t i = 1; i < replacement_width; i++) { + ctx->raw_putchar(ctx, ' '); + } + } else { + ctx->raw_putchar(ctx, cc); + } + return; + } + +unicode_error: + if (c >= 0xc0 && c <= 0xf7) { + if (c >= 0xc0 && c <= 0xdf) { + ctx->unicode_remaining = 1; + ctx->code_point = (uint64_t)(c & 0x1f) << 6; + } else if (c >= 0xe0 && c <= 0xef) { + ctx->unicode_remaining = 2; + ctx->code_point = (uint64_t)(c & 0x0f) << (6 * 2); + } else if (c >= 0xf0 && c <= 0xf7) { + ctx->unicode_remaining = 3; + ctx->code_point = (uint64_t)(c & 0x07) << (6 * 3); + } + return; + } + + if (ctx->escape == true) { + escape_parse(ctx, c); + return; + } + + if (ctx->g_select) { + ctx->g_select--; + switch (c) { + case 'B': + ctx->charsets[ctx->g_select] = CHARSET_DEFAULT; break; + case '0': + ctx->charsets[ctx->g_select] = CHARSET_DEC_SPECIAL; break; + } + ctx->g_select = 0; + return; + } + + size_t x, y; + ctx->get_cursor_pos(ctx, &x, &y); + + switch (c) { + case 0x00: + case 0x7f: + return; + case 0x1b: + ctx->escape_offset = 0; + ctx->escape = true; + return; + case '\t': + if ((x / ctx->tab_size + 1) >= ctx->cols) { + ctx->set_cursor_pos(ctx, ctx->cols - 1, y); + return; + } + ctx->set_cursor_pos(ctx, (x / ctx->tab_size + 1) * ctx->tab_size, y); + return; + case 0x0b: + case 0x0c: + case '\n': + if (y == ctx->scroll_bottom_margin - 1) { + ctx->scroll(ctx); + ctx->set_cursor_pos(ctx, (ctx->oob_output & FLANTERM_OOB_OUTPUT_ONLCR) ? 0 : x, y); + } else { + ctx->set_cursor_pos(ctx, (ctx->oob_output & FLANTERM_OOB_OUTPUT_ONLCR) ? 0 : x, y + 1); + } + return; + case '\b': + ctx->set_cursor_pos(ctx, x - 1, y); + return; + case '\r': + ctx->set_cursor_pos(ctx, 0, y); + return; + case '\a': + // The bell is handled by the kernel + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_BELL, 0, 0, 0); + } + return; + case 14: + // Move to G1 set + ctx->current_charset = 1; + return; + case 15: + // Move to G0 set + ctx->current_charset = 0; + return; + } + + if (ctx->insert_mode == true) { + for (size_t i = ctx->cols - 1; ; i--) { + ctx->move_character(ctx, i + 1, y, i, y); + if (i == x) { + break; + } + } + } + + // Translate character set + switch (ctx->charsets[ctx->current_charset]) { + case CHARSET_DEFAULT: + break; + case CHARSET_DEC_SPECIAL: + if (dec_special_print(ctx, c)) { + return; + } + break; + } + + if (c >= 0x20 && c <= 0x7e) { + ctx->raw_putchar(ctx, c); + } else { + ctx->raw_putchar(ctx, 0xfe); + } +} + +void flanterm_flush(struct flanterm_context *ctx) { + ctx->double_buffer_flush(ctx); +} + +void flanterm_full_refresh(struct flanterm_context *ctx) { + ctx->full_refresh(ctx); +} + +void flanterm_deinit(struct flanterm_context *ctx, void (*_free)(void *, size_t)) { + ctx->deinit(ctx, _free); +} + +void flanterm_get_dimensions(struct flanterm_context *ctx, size_t *cols, size_t *rows) { + *cols = ctx->cols; + *rows = ctx->rows; +} + +void flanterm_set_autoflush(struct flanterm_context *ctx, bool state) { + ctx->autoflush = state; +} + +void flanterm_set_callback(struct flanterm_context *ctx, void (*callback)(struct flanterm_context *, uint64_t, uint64_t, uint64_t, uint64_t)) { + ctx->callback = callback; +} + +uint64_t flanterm_get_oob_output(struct flanterm_context *ctx) { + return ctx->oob_output; +} + +void flanterm_set_oob_output(struct flanterm_context *ctx, uint64_t oob_output) { + ctx->oob_output = oob_output; +} diff --git a/usr/src/sp1/ext/flanterm/src/flanterm.h b/usr/src/sp1/ext/flanterm/src/flanterm.h new file mode 100644 index 0000000..64dd672 --- /dev/null +++ b/usr/src/sp1/ext/flanterm/src/flanterm.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* Copyright (C) 2022-2026 Mintsuki and contributors. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLANTERM_H +#define FLANTERM_H 1 + +#include +#include + +#ifndef _HAVE_bool +#define _HAVE_bool 1 +typedef _Bool bool; + +#define true 1 +#define false 0 +#endif /* !_HAVE_bool */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define FLANTERM_CB_DEC 10 +#define FLANTERM_CB_BELL 20 +#define FLANTERM_CB_PRIVATE_ID 30 +#define FLANTERM_CB_STATUS_REPORT 40 +#define FLANTERM_CB_POS_REPORT 50 +#define FLANTERM_CB_KBD_LEDS 60 +#define FLANTERM_CB_MODE 70 +#define FLANTERM_CB_LINUX 80 + +#define FLANTERM_OOB_OUTPUT_OCRNL (1 << 0) +#define FLANTERM_OOB_OUTPUT_OFDEL (1 << 1) +#define FLANTERM_OOB_OUTPUT_OFILL (1 << 2) +#define FLANTERM_OOB_OUTPUT_OLCUC (1 << 3) +#define FLANTERM_OOB_OUTPUT_ONLCR (1 << 4) +#define FLANTERM_OOB_OUTPUT_ONLRET (1 << 5) +#define FLANTERM_OOB_OUTPUT_ONOCR (1 << 6) +#define FLANTERM_OOB_OUTPUT_OPOST (1 << 7) + +#ifdef FLANTERM_IN_FLANTERM + +#include "flanterm_private.h" + +#else + +struct flanterm_context; + +#endif + +void flanterm_write(struct flanterm_context *ctx, const char *buf, size_t count); +void flanterm_flush(struct flanterm_context *ctx); +void flanterm_full_refresh(struct flanterm_context *ctx); +void flanterm_deinit(struct flanterm_context *ctx, void (*_free)(void *ptr, size_t size)); + +void flanterm_get_dimensions(struct flanterm_context *ctx, size_t *cols, size_t *rows); +void flanterm_set_autoflush(struct flanterm_context *ctx, bool state); +void flanterm_set_callback(struct flanterm_context *ctx, void (*callback)(struct flanterm_context *, uint64_t, uint64_t, uint64_t, uint64_t)); +uint64_t flanterm_get_oob_output(struct flanterm_context *ctx); +void flanterm_set_oob_output(struct flanterm_context *ctx, uint64_t oob_output); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr/src/sp1/ext/flanterm/src/flanterm_backends/fb.c b/usr/src/sp1/ext/flanterm/src/flanterm_backends/fb.c new file mode 100644 index 0000000..9106764 --- /dev/null +++ b/usr/src/sp1/ext/flanterm/src/flanterm_backends/fb.c @@ -0,0 +1,1412 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* Copyright (C) 2022-2026 Mintsuki and contributors. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __cplusplus +#error "Please do not compile Flanterm as C++ code! Flanterm should be compiled as C99 or newer." +#endif + +#ifndef __STDC_VERSION__ +#error "Flanterm must be compiled as C99 or newer." +#endif + +#if defined(_MSC_VER) +#define ALWAYS_INLINE __forceinline +#elif defined(__GNUC__) || defined(__clang__) +#define ALWAYS_INLINE __attribute__((always_inline)) inline +#else +#define ALWAYS_INLINE inline +#endif + +#include + +#ifndef FLANTERM_IN_FLANTERM +#define FLANTERM_IN_FLANTERM +#endif + +#include "../flanterm.h" +#include "fb.h" + +void *memset(void *, int, size_t); +void *memcpy(void *, const void *, size_t); + +#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC + +#ifndef FLANTERM_FB_BUMP_ALLOC_POOL_SIZE +#define FLANTERM_FB_BUMP_ALLOC_POOL_SIZE 873000 + +#define FLANTERM_FB_WIDTH_LIMIT 1920 +#define FLANTERM_FB_HEIGHT_LIMIT 1200 +#endif + +static uint8_t bump_alloc_pool[FLANTERM_FB_BUMP_ALLOC_POOL_SIZE]; +static size_t bump_alloc_ptr = 0; + +static void *bump_alloc(size_t s) { + static bool base_offset_added = false; + if (!base_offset_added) { + if ((uintptr_t)bump_alloc_pool & 0xf) { + bump_alloc_ptr += 0x10 - ((uintptr_t)bump_alloc_pool & 0xf); + } + base_offset_added = true; + } + + if ((s & 0xf) != 0) { + s += 0x10; + s &= ~(size_t)0xf; + } + + size_t next_ptr = bump_alloc_ptr + s; + if (next_ptr > FLANTERM_FB_BUMP_ALLOC_POOL_SIZE) { + return NULL; + } + void *ret = &bump_alloc_pool[bump_alloc_ptr]; + bump_alloc_ptr = next_ptr; + return ret; +} + +static bool bump_allocated_instance = false; + +#endif + +// Builtin font originally taken from: +// https://github.com/viler-int10h/vga-text-mode-fonts/raw/master/FONTS/PC-OTHER/TOSH-SAT.F16 +static const uint8_t builtin_font[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x81, 0x81, 0xa5, 0xa5, 0x81, + 0x81, 0xa5, 0x99, 0x81, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x3c, 0x7e, 0xff, + 0xff, 0xdb, 0xdb, 0xff, 0xff, 0xdb, 0xe7, 0xff, 0x7e, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, + 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x3c, 0x3c, 0xdb, 0xff, 0xff, 0xdb, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0xff, 0x66, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, + 0x78, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0x84, 0x84, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, + 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x1e, + 0x0e, 0x1e, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0xfc, 0x30, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x18, 0x1c, 0x1e, 0x16, 0x12, + 0x10, 0x10, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x38, 0x2c, + 0x26, 0x32, 0x3a, 0x2e, 0x26, 0x22, 0x62, 0xe2, 0xc6, 0x0e, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xfe, + 0xf8, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x06, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb, + 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, + 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, + 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, 0x30, 0xfc, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0xfc, 0x78, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x0c, 0xfe, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0xfe, 0x60, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, + 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xff, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, + 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x78, 0x78, 0x78, 0x78, 0x30, 0x30, 0x30, 0x00, 0x30, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, + 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc0, 0xc0, 0x7c, 0x06, 0x06, 0xc6, 0x7c, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x0c, 0x0c, 0x18, 0x38, + 0x30, 0x60, 0x60, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, + 0x6c, 0x38, 0x30, 0x76, 0xde, 0xcc, 0xcc, 0xde, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x38, 0xfe, 0x38, 0x6c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, + 0x0c, 0x0c, 0x18, 0x38, 0x30, 0x60, 0x60, 0xc0, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, + 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0x06, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, + 0xfe, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, + 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, + 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x60, + 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x00, 0x30, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde, + 0xde, 0xde, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, + 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xcc, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, + 0xc0, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, + 0xee, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xe6, 0xe6, 0xf6, 0xde, 0xce, 0xce, 0xc6, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xc6, + 0xc6, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xf6, 0xda, + 0x6c, 0x06, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, + 0xd8, 0xcc, 0xcc, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, + 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, + 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x38, + 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xc0, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, + 0x60, 0x60, 0x30, 0x38, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x06, 0x06, + 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, + 0xc0, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xe6, 0xdc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x76, 0xce, 0xc6, + 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xce, 0xc6, + 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0xc0, 0xc0, + 0xc0, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x1e, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0xc0, 0xc0, + 0xc0, 0xc6, 0xcc, 0xd8, 0xf0, 0xf0, 0xd8, 0xcc, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, + 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc6, + 0xc6, 0xc6, 0xe6, 0xdc, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x06, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, + 0x70, 0x1c, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, + 0x30, 0xfe, 0x30, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x30, 0x30, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x30, + 0x30, 0x30, 0x30, 0x1c, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x6c, + 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x18, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, + 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, + 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0x06, 0x06, + 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, + 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6, + 0x7c, 0x18, 0x0c, 0x38, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, + 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, + 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, + 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, + 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x36, 0x36, + 0x76, 0xde, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x3c, + 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, + 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, + 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, + 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x30, 0x78, 0xcc, 0xc0, 0xc0, 0xcc, 0x78, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x60, 0x60, 0x60, 0xf8, 0x60, 0x60, 0x60, 0xe6, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0xfc, + 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xcc, + 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, + 0x18, 0xd8, 0x70, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0x06, 0x06, + 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, + 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, + 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x78, 0xd8, 0xd8, 0x6c, 0x00, 0xfc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, + 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc2, 0xc6, 0xcc, 0xd8, 0x30, 0x60, 0xdc, 0x86, 0x0c, + 0x18, 0x3e, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc2, 0xc6, 0xcc, 0xd8, 0x30, + 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, + 0x00, 0x30, 0x30, 0x30, 0x78, 0x78, 0x78, 0x78, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, + 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x88, 0x22, 0x88, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, + 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, + 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, + 0x36, 0xf6, 0xf6, 0x06, 0x06, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x06, + 0x06, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0xf6, 0xf6, 0x06, 0x06, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, + 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, + 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x37, 0x37, 0x30, 0x30, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x30, 0x30, 0x37, 0x37, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0xf7, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xf7, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x30, 0x30, 0x37, 0x37, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, + 0x36, 0xf7, 0xf7, 0x00, 0x00, 0xf7, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, + 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0xff, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0xff, 0xff, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, + 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x76, 0xd6, 0xdc, 0xc8, 0xc8, 0xdc, 0xd6, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xd8, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0xfe, 0x24, 0x24, 0x24, 0x24, 0x66, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xfe, 0xc2, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc2, 0xfe, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xc8, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x76, 0x6c, 0x60, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0xfc, 0x98, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x30, 0x30, 0x78, 0xcc, 0xcc, + 0xcc, 0x78, 0x30, 0x30, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, + 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, + 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0x60, 0x30, 0x78, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x76, 0xbb, 0x99, 0x99, 0xdd, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x06, 0x3c, 0x6c, 0xce, 0xd6, 0xd6, 0xe6, 0x6c, 0x78, + 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x60, 0xc0, 0xc0, 0xfe, + 0xc0, 0xc0, 0x60, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0xfc, + 0x30, 0x30, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, 0x36, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, + 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, + 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c, 0x00, 0x00, + 0x00, 0xd8, 0xec, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x0c, 0x18, 0x30, 0x60, 0x7c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static ALWAYS_INLINE uint32_t convert_colour(struct flanterm_context *_ctx, uint32_t colour) { + struct flanterm_fb_context *ctx = (void *)_ctx; + uint32_t r = (colour >> 16) & 0xff; + uint32_t g = (colour >> 8) & 0xff; + uint32_t b = colour & 0xff; + return (r << ctx->red_mask_shift) | (g << ctx->green_mask_shift) | (b << ctx->blue_mask_shift); +} + +static void flanterm_fb_save_state(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + ctx->saved_state_text_fg = ctx->text_fg; + ctx->saved_state_text_bg = ctx->text_bg; + ctx->saved_state_cursor_x = ctx->cursor_x; + ctx->saved_state_cursor_y = ctx->cursor_y; +} + +static void flanterm_fb_restore_state(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + ctx->text_fg = ctx->saved_state_text_fg; + ctx->text_bg = ctx->saved_state_text_bg; + ctx->cursor_x = ctx->saved_state_cursor_x; + ctx->cursor_y = ctx->saved_state_cursor_y; +} + +static void flanterm_fb_swap_palette(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + uint32_t tmp = ctx->text_bg; + ctx->text_bg = ctx->text_fg; + ctx->text_fg = tmp; +} + +static void plot_char_scaled_canvas(struct flanterm_context *_ctx, struct flanterm_fb_char *c, size_t x, size_t y) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (x >= _ctx->cols || y >= _ctx->rows) { + return; + } + + x = ctx->offset_x + x * ctx->glyph_width; + y = ctx->offset_y + y * ctx->glyph_height; + + bool *glyph = &ctx->font_bool[c->c * ctx->font_height * ctx->font_width]; + + volatile uint32_t *dest; + int outer_stride, inner_stride; + + switch (ctx->rotation) { + default: + case FLANTERM_FB_ROTATE_0: + dest = ctx->framebuffer + x + y * (ctx->pitch / 4); + outer_stride = ctx->pitch / 4; + inner_stride = 1; + break; + case FLANTERM_FB_ROTATE_90: + dest = ctx->framebuffer + (ctx->height - 1 - y) + x * (ctx->pitch / 4); + outer_stride = -1; + inner_stride = ctx->pitch / 4; + break; + case FLANTERM_FB_ROTATE_180: + dest = ctx->framebuffer + (ctx->width - 1 - x) + (ctx->height - 1 - y) * (ctx->pitch / 4); + outer_stride = -(ctx->pitch / 4); + inner_stride = -1; + break; + case FLANTERM_FB_ROTATE_270: + dest = ctx->framebuffer + y + (ctx->width - 1 - x) * (ctx->pitch / 4); + outer_stride = 1; + inner_stride = -(ctx->pitch / 4); + break; + } + + // naming: fx,fy for font coordinates, gx,gy for glyph coordinates + for (size_t gy = 0; gy < ctx->glyph_height; gy++) { + uint8_t fy = gy / ctx->font_scale_y; + volatile uint32_t *fb_line = dest; + uint32_t *canvas_line = ctx->canvas + x + (y + gy) * ctx->width; + bool *glyph_pointer = glyph + (fy * ctx->font_width); + for (size_t fx = 0; fx < ctx->font_width; fx++) { + for (size_t i = 0; i < ctx->font_scale_x; i++) { + size_t gx = ctx->font_scale_x * fx + i; + uint32_t bg = c->bg == 0xffffffff ? canvas_line[gx] : c->bg; + uint32_t fg = c->fg == 0xffffffff ? canvas_line[gx] : c->fg; + *fb_line = *glyph_pointer ? fg : bg; + fb_line += inner_stride; + } + glyph_pointer++; + } + dest += outer_stride; + } +} + +static void plot_char_scaled_uncanvas(struct flanterm_context *_ctx, struct flanterm_fb_char *c, size_t x, size_t y) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (x >= _ctx->cols || y >= _ctx->rows) { + return; + } + + uint32_t default_bg = ctx->default_bg; + + uint32_t bg = c->bg == 0xffffffff ? default_bg : c->bg; + uint32_t fg = c->fg == 0xffffffff ? default_bg : c->fg; + + x = ctx->offset_x + x * ctx->glyph_width; + y = ctx->offset_y + y * ctx->glyph_height; + + bool *glyph = &ctx->font_bool[c->c * ctx->font_height * ctx->font_width]; + + volatile uint32_t *dest; + int outer_stride, inner_stride; + + switch (ctx->rotation) { + default: + case FLANTERM_FB_ROTATE_0: + dest = ctx->framebuffer + x + y * (ctx->pitch / 4); + outer_stride = ctx->pitch / 4; + inner_stride = 1; + break; + case FLANTERM_FB_ROTATE_90: + dest = ctx->framebuffer + (ctx->height - 1 - y) + x * (ctx->pitch / 4); + outer_stride = -1; + inner_stride = ctx->pitch / 4; + break; + case FLANTERM_FB_ROTATE_180: + dest = ctx->framebuffer + (ctx->width - 1 - x) + (ctx->height - 1 - y) * (ctx->pitch / 4); + outer_stride = -(ctx->pitch / 4); + inner_stride = -1; + break; + case FLANTERM_FB_ROTATE_270: + dest = ctx->framebuffer + y + (ctx->width - 1 - x) * (ctx->pitch / 4); + outer_stride = 1; + inner_stride = -(ctx->pitch / 4); + break; + } + + // naming: fx,fy for font coordinates, gx,gy for glyph coordinates + for (size_t gy = 0; gy < ctx->glyph_height; gy++) { + uint8_t fy = gy / ctx->font_scale_y; + volatile uint32_t *fb_line = dest; + bool *glyph_pointer = glyph + (fy * ctx->font_width); + for (size_t fx = 0; fx < ctx->font_width; fx++) { + for (size_t i = 0; i < ctx->font_scale_x; i++) { + *fb_line = *glyph_pointer ? fg : bg; + fb_line += inner_stride; + } + glyph_pointer++; + } + dest += outer_stride; + } +} + +static void plot_char_unscaled_canvas(struct flanterm_context *_ctx, struct flanterm_fb_char *c, size_t x, size_t y) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (x >= _ctx->cols || y >= _ctx->rows) { + return; + } + + x = ctx->offset_x + x * ctx->glyph_width; + y = ctx->offset_y + y * ctx->glyph_height; + + bool *glyph = &ctx->font_bool[c->c * ctx->font_height * ctx->font_width]; + + volatile uint32_t *dest; + int outer_stride, inner_stride; + + switch (ctx->rotation) { + default: + case FLANTERM_FB_ROTATE_0: + dest = ctx->framebuffer + x + y * (ctx->pitch / 4); + outer_stride = ctx->pitch / 4; + inner_stride = 1; + break; + case FLANTERM_FB_ROTATE_90: + dest = ctx->framebuffer + (ctx->height - 1 - y) + x * (ctx->pitch / 4); + outer_stride = -1; + inner_stride = ctx->pitch / 4; + break; + case FLANTERM_FB_ROTATE_180: + dest = ctx->framebuffer + (ctx->width - 1 - x) + (ctx->height - 1 - y) * (ctx->pitch / 4); + outer_stride = -(ctx->pitch / 4); + inner_stride = -1; + break; + case FLANTERM_FB_ROTATE_270: + dest = ctx->framebuffer + y + (ctx->width - 1 - x) * (ctx->pitch / 4); + outer_stride = 1; + inner_stride = -(ctx->pitch / 4); + break; + } + + // naming: fx,fy for font coordinates, gx,gy for glyph coordinates + for (size_t gy = 0; gy < ctx->glyph_height; gy++) { + volatile uint32_t *fb_line = dest; + uint32_t *canvas_line = ctx->canvas + x + (y + gy) * ctx->width; + bool *glyph_pointer = glyph + (gy * ctx->font_width); + for (size_t fx = 0; fx < ctx->font_width; fx++) { + uint32_t bg = c->bg == 0xffffffff ? canvas_line[fx] : c->bg; + uint32_t fg = c->fg == 0xffffffff ? canvas_line[fx] : c->fg; + *fb_line = *(glyph_pointer++) ? fg : bg; + fb_line += inner_stride; + } + dest += outer_stride; + } +} + +static void plot_char_unscaled_uncanvas(struct flanterm_context *_ctx, struct flanterm_fb_char *c, size_t x, size_t y) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (x >= _ctx->cols || y >= _ctx->rows) { + return; + } + + uint32_t default_bg = ctx->default_bg; + + uint32_t bg = c->bg == 0xffffffff ? default_bg : c->bg; + uint32_t fg = c->fg == 0xffffffff ? default_bg : c->fg; + + x = ctx->offset_x + x * ctx->glyph_width; + y = ctx->offset_y + y * ctx->glyph_height; + + bool *glyph = &ctx->font_bool[c->c * ctx->font_height * ctx->font_width]; + + volatile uint32_t *dest; + int outer_stride, inner_stride; + + switch (ctx->rotation) { + default: + case FLANTERM_FB_ROTATE_0: + dest = ctx->framebuffer + x + y * (ctx->pitch / 4); + outer_stride = ctx->pitch / 4; + inner_stride = 1; + break; + case FLANTERM_FB_ROTATE_90: + dest = ctx->framebuffer + (ctx->height - 1 - y) + x * (ctx->pitch / 4); + outer_stride = -1; + inner_stride = ctx->pitch / 4; + break; + case FLANTERM_FB_ROTATE_180: + dest = ctx->framebuffer + (ctx->width - 1 - x) + (ctx->height - 1 - y) * (ctx->pitch / 4); + outer_stride = -(ctx->pitch / 4); + inner_stride = -1; + break; + case FLANTERM_FB_ROTATE_270: + dest = ctx->framebuffer + y + (ctx->width - 1 - x) * (ctx->pitch / 4); + outer_stride = 1; + inner_stride = -(ctx->pitch / 4); + break; + } + + // naming: fx,fy for font coordinates, gx,gy for glyph coordinates + for (size_t gy = 0; gy < ctx->glyph_height; gy++) { + volatile uint32_t *fb_line = dest; + bool *glyph_pointer = glyph + (gy * ctx->font_width); + for (size_t fx = 0; fx < ctx->font_width; fx++) { + *fb_line = *(glyph_pointer++) ? fg : bg; + fb_line += inner_stride; + } + dest += outer_stride; + } +} + +static inline bool compare_char(struct flanterm_fb_char *a, struct flanterm_fb_char *b) { + return !(a->c != b->c || a->bg != b->bg || a->fg != b->fg); +} + +static void push_to_queue(struct flanterm_context *_ctx, struct flanterm_fb_char *c, size_t x, size_t y) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (x >= _ctx->cols || y >= _ctx->rows) { + return; + } + + size_t i = y * _ctx->cols + x; + + struct flanterm_fb_queue_item *q = ctx->map[i]; + + if (q == NULL) { + if (compare_char(&ctx->grid[i], c)) { + return; + } + q = &ctx->queue[ctx->queue_i++]; + q->x = x; + q->y = y; + ctx->map[i] = q; + } + + q->c = *c; +} + +static void flanterm_fb_revscroll(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + for (size_t i = (_ctx->scroll_bottom_margin - 1) * _ctx->cols - 1; + i >= _ctx->scroll_top_margin * _ctx->cols; i--) { + if (i == (size_t)-1) { + break; + } + struct flanterm_fb_char *c; + struct flanterm_fb_queue_item *q = ctx->map[i]; + if (q != NULL) { + c = &q->c; + } else { + c = &ctx->grid[i]; + } + push_to_queue(_ctx, c, (i + _ctx->cols) % _ctx->cols, (i + _ctx->cols) / _ctx->cols); + } + + // Clear the first line of the screen. + struct flanterm_fb_char empty; + empty.c = ' '; + empty.fg = ctx->text_fg; + empty.bg = ctx->text_bg; + for (size_t i = 0; i < _ctx->cols; i++) { + push_to_queue(_ctx, &empty, i, _ctx->scroll_top_margin); + } +} + +static void flanterm_fb_scroll(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + for (size_t i = (_ctx->scroll_top_margin + 1) * _ctx->cols; + i < _ctx->scroll_bottom_margin * _ctx->cols; i++) { + struct flanterm_fb_char *c; + struct flanterm_fb_queue_item *q = ctx->map[i]; + if (q != NULL) { + c = &q->c; + } else { + c = &ctx->grid[i]; + } + push_to_queue(_ctx, c, (i - _ctx->cols) % _ctx->cols, (i - _ctx->cols) / _ctx->cols); + } + + // Clear the last line of the screen. + struct flanterm_fb_char empty; + empty.c = ' '; + empty.fg = ctx->text_fg; + empty.bg = ctx->text_bg; + for (size_t i = 0; i < _ctx->cols; i++) { + push_to_queue(_ctx, &empty, i, _ctx->scroll_bottom_margin - 1); + } +} + +static void flanterm_fb_clear(struct flanterm_context *_ctx, bool move) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + struct flanterm_fb_char empty; + empty.c = ' '; + empty.fg = ctx->text_fg; + empty.bg = ctx->text_bg; + for (size_t i = 0; i < _ctx->rows * _ctx->cols; i++) { + push_to_queue(_ctx, &empty, i % _ctx->cols, i / _ctx->cols); + } + + if (move) { + ctx->cursor_x = 0; + ctx->cursor_y = 0; + } +} + +static void flanterm_fb_set_cursor_pos(struct flanterm_context *_ctx, size_t x, size_t y) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (x >= _ctx->cols) { + if ((int)x < 0) { + x = 0; + } else { + x = _ctx->cols - 1; + } + } + if (y >= _ctx->rows) { + if ((int)y < 0) { + y = 0; + } else { + y = _ctx->rows - 1; + } + } + ctx->cursor_x = x; + ctx->cursor_y = y; +} + +static void flanterm_fb_get_cursor_pos(struct flanterm_context *_ctx, size_t *x, size_t *y) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + *x = ctx->cursor_x >= _ctx->cols ? _ctx->cols - 1 : ctx->cursor_x; + *y = ctx->cursor_y >= _ctx->rows ? _ctx->rows - 1 : ctx->cursor_y; +} + +static void flanterm_fb_move_character(struct flanterm_context *_ctx, size_t new_x, size_t new_y, size_t old_x, size_t old_y) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (old_x >= _ctx->cols || old_y >= _ctx->rows + || new_x >= _ctx->cols || new_y >= _ctx->rows) { + return; + } + + size_t i = old_x + old_y * _ctx->cols; + + struct flanterm_fb_char *c; + struct flanterm_fb_queue_item *q = ctx->map[i]; + if (q != NULL) { + c = &q->c; + } else { + c = &ctx->grid[i]; + } + + push_to_queue(_ctx, c, new_x, new_y); +} + +static void flanterm_fb_set_text_fg(struct flanterm_context *_ctx, size_t fg) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_fg = ctx->ansi_colours[fg]; +} + +static void flanterm_fb_set_text_bg(struct flanterm_context *_ctx, size_t bg) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_bg = ctx->ansi_colours[bg]; +} + +static void flanterm_fb_set_text_fg_bright(struct flanterm_context *_ctx, size_t fg) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_fg = ctx->ansi_bright_colours[fg]; +} + +static void flanterm_fb_set_text_bg_bright(struct flanterm_context *_ctx, size_t bg) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_bg = ctx->ansi_bright_colours[bg]; +} + +static void flanterm_fb_set_text_fg_rgb(struct flanterm_context *_ctx, uint32_t fg) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_fg = convert_colour(_ctx, fg); +} + +static void flanterm_fb_set_text_bg_rgb(struct flanterm_context *_ctx, uint32_t bg) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_bg = convert_colour(_ctx, bg); +} + +static void flanterm_fb_set_text_fg_default(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_fg = ctx->default_fg; +} + +static void flanterm_fb_set_text_bg_default(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_bg = 0xffffffff; +} + +static void flanterm_fb_set_text_fg_default_bright(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_fg = ctx->default_fg_bright; +} + +static void flanterm_fb_set_text_bg_default_bright(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_bg = ctx->default_bg_bright; +} + +static void draw_cursor(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (ctx->cursor_x >= _ctx->cols || ctx->cursor_y >= _ctx->rows) { + return; + } + + size_t i = ctx->cursor_x + ctx->cursor_y * _ctx->cols; + + struct flanterm_fb_char c; + struct flanterm_fb_queue_item *q = ctx->map[i]; + if (q != NULL) { + c = q->c; + } else { + c = ctx->grid[i]; + } + uint32_t tmp = c.fg; + c.fg = c.bg; + c.bg = tmp; + ctx->plot_char(_ctx, &c, ctx->cursor_x, ctx->cursor_y); + if (q != NULL) { + ctx->grid[i] = q->c; + ctx->map[i] = NULL; + } +} + +static void flanterm_fb_double_buffer_flush(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (_ctx->cursor_enabled) { + draw_cursor(_ctx); + } + + for (size_t i = 0; i < ctx->queue_i; i++) { + struct flanterm_fb_queue_item *q = &ctx->queue[i]; + size_t offset = q->y * _ctx->cols + q->x; + if (ctx->map[offset] == NULL) { + continue; + } + ctx->plot_char(_ctx, &q->c, q->x, q->y); + ctx->grid[offset] = q->c; + ctx->map[offset] = NULL; + } + + if ((ctx->old_cursor_x != ctx->cursor_x || ctx->old_cursor_y != ctx->cursor_y) || _ctx->cursor_enabled == false) { + if (ctx->old_cursor_x < _ctx->cols && ctx->old_cursor_y < _ctx->rows) { + ctx->plot_char(_ctx, &ctx->grid[ctx->old_cursor_x + ctx->old_cursor_y * _ctx->cols], ctx->old_cursor_x, ctx->old_cursor_y); + } + } + + ctx->old_cursor_x = ctx->cursor_x; + ctx->old_cursor_y = ctx->cursor_y; + + ctx->queue_i = 0; + + if (ctx->flush_callback) { + ctx->flush_callback(ctx->framebuffer, ctx->pitch * ctx->height); + } +} + +static void flanterm_fb_raw_putchar(struct flanterm_context *_ctx, uint8_t c) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (ctx->cursor_x >= _ctx->cols && (ctx->cursor_y < _ctx->scroll_bottom_margin - 1 || _ctx->scroll_enabled)) { + ctx->cursor_x = 0; + ctx->cursor_y++; + if (ctx->cursor_y == _ctx->scroll_bottom_margin) { + ctx->cursor_y--; + flanterm_fb_scroll(_ctx); + } + if (ctx->cursor_y >= _ctx->cols) { + ctx->cursor_y = _ctx->cols - 1; + } + } + + struct flanterm_fb_char ch; + ch.c = c; + ch.fg = ctx->text_fg; + ch.bg = ctx->text_bg; + push_to_queue(_ctx, &ch, ctx->cursor_x++, ctx->cursor_y); +} + +static void flanterm_fb_full_refresh(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + uint32_t default_bg = ctx->default_bg; + + for (size_t y = 0; y < ctx->height; y++) { + for (size_t x = 0; x < ctx->width; x++) { + size_t px, py; + switch (ctx->rotation) { + default: + case FLANTERM_FB_ROTATE_0: + px = x; py = y; + break; + case FLANTERM_FB_ROTATE_90: + px = ctx->height - 1 - y; py = x; + break; + case FLANTERM_FB_ROTATE_180: + px = ctx->width - 1 - x; py = ctx->height - 1 - y; + break; + case FLANTERM_FB_ROTATE_270: + px = y; py = ctx->width - 1 - x; + break; + } + + if (ctx->canvas != NULL) { + ctx->framebuffer[py * (ctx->pitch / sizeof(uint32_t)) + px] = ctx->canvas[y * ctx->width + x]; + } else { + ctx->framebuffer[py * (ctx->pitch / sizeof(uint32_t)) + px] = default_bg; + } + } + } + + for (size_t i = 0; i < (size_t)_ctx->rows * _ctx->cols; i++) { + size_t x = i % _ctx->cols; + size_t y = i / _ctx->cols; + + ctx->plot_char(_ctx, &ctx->grid[i], x, y); + } + + if (_ctx->cursor_enabled) { + draw_cursor(_ctx); + } + + if (ctx->flush_callback) { + ctx->flush_callback(ctx->framebuffer, ctx->pitch * ctx->height); + } +} + +static void flanterm_fb_deinit(struct flanterm_context *_ctx, void (*_free)(void *, size_t)) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (_free == NULL) { +#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC + if (bump_allocated_instance == true) { + bump_alloc_ptr = 0; + bump_allocated_instance = false; + } +#endif + return; + } + + _free(ctx->font_bits, ctx->font_bits_size); + _free(ctx->font_bool, ctx->font_bool_size); + _free(ctx->grid, ctx->grid_size); + _free(ctx->queue, ctx->queue_size); + _free(ctx->map, ctx->map_size); + + if (ctx->canvas != NULL) { + _free(ctx->canvas, ctx->canvas_size); + } + + _free(ctx, sizeof(struct flanterm_fb_context)); +} + +struct flanterm_context *flanterm_fb_init( + void *(*_malloc)(size_t), + void (*_free)(void *, size_t), + uint32_t *framebuffer, size_t width, size_t height, size_t pitch, + uint8_t red_mask_size, uint8_t red_mask_shift, + uint8_t green_mask_size, uint8_t green_mask_shift, + uint8_t blue_mask_size, uint8_t blue_mask_shift, + uint32_t *canvas, + uint32_t *ansi_colours, uint32_t *ansi_bright_colours, + uint32_t *default_bg, uint32_t *default_fg, + uint32_t *default_bg_bright, uint32_t *default_fg_bright, + void *font, size_t font_width, size_t font_height, size_t font_spacing, + size_t font_scale_x, size_t font_scale_y, + size_t margin, + int rotation +) { + if (rotation == FLANTERM_FB_ROTATE_90 || rotation == FLANTERM_FB_ROTATE_270) { + size_t tmp = width; + width = height; + height = tmp; + } + + if (font_scale_x == 0 || font_scale_y == 0) { + font_scale_x = 1; + font_scale_y = 1; + if (width >= (1920 + 1920 / 3) && height >= (1080 + 1080 / 3)) { + font_scale_x = 2; + font_scale_y = 2; + } + if (width >= (3840 + 3840 / 3) && height >= (2160 + 2160 / 3)) { + font_scale_x = 4; + font_scale_y = 4; + } + } + + if (red_mask_size < 8 || red_mask_size != green_mask_size || red_mask_size != blue_mask_size) { + return NULL; + } + + if (_malloc == NULL) { +#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC + if (bump_allocated_instance == true) { + return NULL; + } + _malloc = bump_alloc; + // Limit terminal size if needed + if (width > FLANTERM_FB_WIDTH_LIMIT || height > FLANTERM_FB_HEIGHT_LIMIT) { + size_t width_limit = width > FLANTERM_FB_WIDTH_LIMIT ? FLANTERM_FB_WIDTH_LIMIT : width; + size_t height_limit = height > FLANTERM_FB_HEIGHT_LIMIT ? FLANTERM_FB_HEIGHT_LIMIT : height; + + framebuffer = (uint32_t *)((uintptr_t)framebuffer + ((((height / 2) - (height_limit / 2)) * pitch) + (((width / 2) - (width_limit / 2)) * 4))); + + width = width_limit; + height = height_limit; + } + + // Force disable canvas + canvas = NULL; +#else + return NULL; +#endif + } + + struct flanterm_fb_context *ctx = NULL; + ctx = _malloc(sizeof(struct flanterm_fb_context)); + if (ctx == NULL) { + goto fail; + } + + struct flanterm_context *_ctx = (void *)ctx; + memset(ctx, 0, sizeof(struct flanterm_fb_context)); + + ctx->red_mask_size = red_mask_size; + ctx->red_mask_shift = red_mask_shift + (red_mask_size - 8); + ctx->green_mask_size = green_mask_size; + ctx->green_mask_shift = green_mask_shift + (green_mask_size - 8); + ctx->blue_mask_size = blue_mask_size; + ctx->blue_mask_shift = blue_mask_shift + (blue_mask_size - 8); + + if (ansi_colours != NULL) { + for (size_t i = 0; i < 8; i++) { + ctx->ansi_colours[i] = convert_colour(_ctx, ansi_colours[i]); + } + } else { + ctx->ansi_colours[0] = convert_colour(_ctx, 0x00000000); // black + ctx->ansi_colours[1] = convert_colour(_ctx, 0x00aa0000); // red + ctx->ansi_colours[2] = convert_colour(_ctx, 0x0000aa00); // green + ctx->ansi_colours[3] = convert_colour(_ctx, 0x00aa5500); // brown + ctx->ansi_colours[4] = convert_colour(_ctx, 0x000000aa); // blue + ctx->ansi_colours[5] = convert_colour(_ctx, 0x00aa00aa); // magenta + ctx->ansi_colours[6] = convert_colour(_ctx, 0x0000aaaa); // cyan + ctx->ansi_colours[7] = convert_colour(_ctx, 0x00aaaaaa); // grey + } + + if (ansi_bright_colours != NULL) { + for (size_t i = 0; i < 8; i++) { + ctx->ansi_bright_colours[i] = convert_colour(_ctx, ansi_bright_colours[i]); + } + } else { + ctx->ansi_bright_colours[0] = convert_colour(_ctx, 0x00555555); // black + ctx->ansi_bright_colours[1] = convert_colour(_ctx, 0x00ff5555); // red + ctx->ansi_bright_colours[2] = convert_colour(_ctx, 0x0055ff55); // green + ctx->ansi_bright_colours[3] = convert_colour(_ctx, 0x00ffff55); // brown + ctx->ansi_bright_colours[4] = convert_colour(_ctx, 0x005555ff); // blue + ctx->ansi_bright_colours[5] = convert_colour(_ctx, 0x00ff55ff); // magenta + ctx->ansi_bright_colours[6] = convert_colour(_ctx, 0x0055ffff); // cyan + ctx->ansi_bright_colours[7] = convert_colour(_ctx, 0x00ffffff); // grey + } + + if (default_bg != NULL) { + ctx->default_bg = convert_colour(_ctx, *default_bg); + } else { + ctx->default_bg = 0x00000000; // background (black) + } + + if (default_fg != NULL) { + ctx->default_fg = convert_colour(_ctx, *default_fg); + } else { + ctx->default_fg = convert_colour(_ctx, 0x00aaaaaa); // foreground (grey) + } + + if (default_bg_bright != NULL) { + ctx->default_bg_bright = convert_colour(_ctx, *default_bg_bright); + } else { + ctx->default_bg_bright = convert_colour(_ctx, 0x00555555); // background (black) + } + + if (default_fg_bright != NULL) { + ctx->default_fg_bright = convert_colour(_ctx, *default_fg_bright); + } else { + ctx->default_fg_bright = convert_colour(_ctx, 0x00ffffff); // foreground (grey) + } + + ctx->text_fg = ctx->default_fg; + ctx->text_bg = 0xffffffff; + + ctx->rotation = rotation; + + ctx->framebuffer = (void *)framebuffer; + ctx->width = width; + ctx->height = height; + ctx->pitch = pitch; + +#define FONT_BYTES ((font_width * font_height * FLANTERM_FB_FONT_GLYPHS) / 8) + + if (font != NULL) { + ctx->font_width = font_width; + ctx->font_height = font_height; + ctx->font_bits_size = FONT_BYTES; + ctx->font_bits = _malloc(ctx->font_bits_size); + if (ctx->font_bits == NULL) { + goto fail; + } + memcpy(ctx->font_bits, font, ctx->font_bits_size); + } else { + ctx->font_width = font_width = 8; + ctx->font_height = font_height = 16; + ctx->font_bits_size = FONT_BYTES; + font_spacing = 1; + ctx->font_bits = _malloc(ctx->font_bits_size); + if (ctx->font_bits == NULL) { + goto fail; + } + memcpy(ctx->font_bits, builtin_font, ctx->font_bits_size); + } + +#undef FONT_BYTES + + ctx->font_width += font_spacing; + + ctx->font_bool_size = FLANTERM_FB_FONT_GLYPHS * font_height * ctx->font_width * sizeof(bool); + ctx->font_bool = _malloc(ctx->font_bool_size); + if (ctx->font_bool == NULL) { + goto fail; + } + + for (size_t i = 0; i < FLANTERM_FB_FONT_GLYPHS; i++) { + uint8_t *glyph = &ctx->font_bits[i * font_height]; + + for (size_t y = 0; y < font_height; y++) { + // NOTE: the characters in VGA fonts are always one byte wide. + // 9 dot wide fonts have 8 dots and one empty column, except + // characters 0xC0-0xDF replicate column 9. + for (size_t x = 0; x < 8; x++) { + size_t offset = i * font_height * ctx->font_width + y * ctx->font_width + x; + + if ((glyph[y] & (0x80 >> x))) { + ctx->font_bool[offset] = true; + } else { + ctx->font_bool[offset] = false; + } + } + // fill columns above 8 like VGA Line Graphics Mode does + for (size_t x = 8; x < ctx->font_width; x++) { + size_t offset = i * font_height * ctx->font_width + y * ctx->font_width + x; + + if (i >= 0xc0 && i <= 0xdf) { + ctx->font_bool[offset] = (glyph[y] & 1); + } else { + ctx->font_bool[offset] = false; + } + } + } + } + + ctx->font_scale_x = font_scale_x; + ctx->font_scale_y = font_scale_y; + + ctx->glyph_width = ctx->font_width * font_scale_x; + ctx->glyph_height = font_height * font_scale_y; + + _ctx->cols = (ctx->width - margin * 2) / ctx->glyph_width; + _ctx->rows = (ctx->height - margin * 2) / ctx->glyph_height; + + ctx->offset_x = margin + ((ctx->width - margin * 2) % ctx->glyph_width) / 2; + ctx->offset_y = margin + ((ctx->height - margin * 2) % ctx->glyph_height) / 2; + + ctx->grid_size = _ctx->rows * _ctx->cols * sizeof(struct flanterm_fb_char); + ctx->grid = _malloc(ctx->grid_size); + if (ctx->grid == NULL) { + goto fail; + } + for (size_t i = 0; i < _ctx->rows * _ctx->cols; i++) { + ctx->grid[i].c = ' '; + ctx->grid[i].fg = ctx->text_fg; + ctx->grid[i].bg = ctx->text_bg; + } + + ctx->queue_size = _ctx->rows * _ctx->cols * sizeof(struct flanterm_fb_queue_item); + ctx->queue = _malloc(ctx->queue_size); + if (ctx->queue == NULL) { + goto fail; + } + ctx->queue_i = 0; + memset(ctx->queue, 0, ctx->queue_size); + + ctx->map_size = _ctx->rows * _ctx->cols * sizeof(struct flanterm_fb_queue_item *); + ctx->map = _malloc(ctx->map_size); + if (ctx->map == NULL) { + goto fail; + } + memset(ctx->map, 0, ctx->map_size); + + if (canvas != NULL) { + ctx->canvas_size = ctx->width * ctx->height * sizeof(uint32_t); + ctx->canvas = _malloc(ctx->canvas_size); + if (ctx->canvas == NULL) { + goto fail; + } + for (size_t i = 0; i < ctx->width * ctx->height; i++) { + ctx->canvas[i] = convert_colour(_ctx, canvas[i]); + } + } + + if (font_scale_x == 1 && font_scale_y == 1) { + if (canvas == NULL) { + ctx->plot_char = plot_char_unscaled_uncanvas; + } else { + ctx->plot_char = plot_char_unscaled_canvas; + } + } else { + if (canvas == NULL) { + ctx->plot_char = plot_char_scaled_uncanvas; + } else { + ctx->plot_char = plot_char_scaled_canvas; + } + } + + _ctx->raw_putchar = flanterm_fb_raw_putchar; + _ctx->clear = flanterm_fb_clear; + _ctx->set_cursor_pos = flanterm_fb_set_cursor_pos; + _ctx->get_cursor_pos = flanterm_fb_get_cursor_pos; + _ctx->set_text_fg = flanterm_fb_set_text_fg; + _ctx->set_text_bg = flanterm_fb_set_text_bg; + _ctx->set_text_fg_bright = flanterm_fb_set_text_fg_bright; + _ctx->set_text_bg_bright = flanterm_fb_set_text_bg_bright; + _ctx->set_text_fg_rgb = flanterm_fb_set_text_fg_rgb; + _ctx->set_text_bg_rgb = flanterm_fb_set_text_bg_rgb; + _ctx->set_text_fg_default = flanterm_fb_set_text_fg_default; + _ctx->set_text_bg_default = flanterm_fb_set_text_bg_default; + _ctx->set_text_fg_default_bright = flanterm_fb_set_text_fg_default_bright; + _ctx->set_text_bg_default_bright = flanterm_fb_set_text_bg_default_bright; + _ctx->move_character = flanterm_fb_move_character; + _ctx->scroll = flanterm_fb_scroll; + _ctx->revscroll = flanterm_fb_revscroll; + _ctx->swap_palette = flanterm_fb_swap_palette; + _ctx->save_state = flanterm_fb_save_state; + _ctx->restore_state = flanterm_fb_restore_state; + _ctx->double_buffer_flush = flanterm_fb_double_buffer_flush; + _ctx->full_refresh = flanterm_fb_full_refresh; + _ctx->deinit = flanterm_fb_deinit; + + flanterm_context_reinit(_ctx); + flanterm_fb_full_refresh(_ctx); + +#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC + if (_malloc == bump_alloc) { + bump_allocated_instance = true; + } +#endif + + return _ctx; + +fail: + if (ctx == NULL) { + return NULL; + } + +#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC + if (_malloc == bump_alloc) { + bump_alloc_ptr = 0; + return NULL; + } +#endif + + if (_free == NULL) { + return NULL; + } + + if (ctx->canvas != NULL) { + _free(ctx->canvas, ctx->canvas_size); + } + if (ctx->map != NULL) { + _free(ctx->map, ctx->map_size); + } + if (ctx->queue != NULL) { + _free(ctx->queue, ctx->queue_size); + } + if (ctx->grid != NULL) { + _free(ctx->grid, ctx->grid_size); + } + if (ctx->font_bool != NULL) { + _free(ctx->font_bool, ctx->font_bool_size); + } + if (ctx->font_bits != NULL) { + _free(ctx->font_bits, ctx->font_bits_size); + } + if (ctx != NULL) { + _free(ctx, sizeof(struct flanterm_fb_context)); + } + + return NULL; +} + +void flanterm_fb_set_flush_callback(struct flanterm_context *_ctx, void (*flush_callback)(volatile void *address, size_t length)) { + struct flanterm_fb_context *ctx = (void *)_ctx; + ctx->flush_callback = flush_callback; +} diff --git a/usr/src/sp1/ext/flanterm/src/flanterm_backends/fb.h b/usr/src/sp1/ext/flanterm/src/flanterm_backends/fb.h new file mode 100644 index 0000000..aa99219 --- /dev/null +++ b/usr/src/sp1/ext/flanterm/src/flanterm_backends/fb.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* Copyright (C) 2022-2026 Mintsuki and contributors. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLANTERM_FB_H +#define FLANTERM_FB_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../flanterm.h" + +#ifdef FLANTERM_IN_FLANTERM + +#include "fb_private.h" + +#endif + +#define FLANTERM_FB_ROTATE_0 0 +#define FLANTERM_FB_ROTATE_90 1 +#define FLANTERM_FB_ROTATE_180 2 +#define FLANTERM_FB_ROTATE_270 3 + +struct flanterm_context *flanterm_fb_init( + /* If _malloc and _free are nulled, use the bump allocated instance (1 use only). */ + void *(*_malloc)(size_t size), + void (*_free)(void *ptr, size_t size), + uint32_t *framebuffer, size_t width, size_t height, size_t pitch, + uint8_t red_mask_size, uint8_t red_mask_shift, + uint8_t green_mask_size, uint8_t green_mask_shift, + uint8_t blue_mask_size, uint8_t blue_mask_shift, + uint32_t *canvas, /* If nulled, no canvas. */ + uint32_t *ansi_colours, uint32_t *ansi_bright_colours, /* If nulled, default. */ + uint32_t *default_bg, uint32_t *default_fg, /* If nulled, default. */ + uint32_t *default_bg_bright, uint32_t *default_fg_bright, /* If nulled, default. */ + /* If font is null, use default font and font_width and font_height ignored. */ + void *font, size_t font_width, size_t font_height, size_t font_spacing, + /* If scale_x and scale_y are 0, automatically scale font based on resolution. */ + size_t font_scale_x, size_t font_scale_y, + size_t margin, + /* One of FLANTERM_FB_ROTATE_* values. */ + int rotation +); + +void flanterm_fb_set_flush_callback(struct flanterm_context *ctx, void (*flush_callback)(volatile void *address, size_t length)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr/src/sp1/ext/flanterm/src/flanterm_backends/fb_private.h b/usr/src/sp1/ext/flanterm/src/flanterm_backends/fb_private.h new file mode 100644 index 0000000..af47815 --- /dev/null +++ b/usr/src/sp1/ext/flanterm/src/flanterm_backends/fb_private.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* Copyright (C) 2022-2026 Mintsuki and contributors. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLANTERM_FB_PRIVATE_H +#define FLANTERM_FB_PRIVATE_H 1 + +#ifndef FLANTERM_IN_FLANTERM +#error "Do not use fb_private.h. Use interfaces defined in fb.h only." +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FLANTERM_FB_FONT_GLYPHS 256 + +struct flanterm_fb_char { + uint32_t c; + uint32_t fg; + uint32_t bg; +}; + +struct flanterm_fb_queue_item { + size_t x, y; + struct flanterm_fb_char c; +}; + +struct flanterm_fb_context { + struct flanterm_context term; + + void (*plot_char)(struct flanterm_context *ctx, struct flanterm_fb_char *c, size_t x, size_t y); + void (*flush_callback)(volatile void *address, size_t length); + + size_t font_width; + size_t font_height; + size_t glyph_width; + size_t glyph_height; + + size_t font_scale_x; + size_t font_scale_y; + + size_t offset_x, offset_y; + + volatile uint32_t *framebuffer; + size_t pitch; + size_t width; + size_t height; + size_t bpp; + + uint8_t red_mask_size, red_mask_shift; + uint8_t green_mask_size, green_mask_shift; + uint8_t blue_mask_size, blue_mask_shift; + + int rotation; + + size_t font_bits_size; + uint8_t *font_bits; + size_t font_bool_size; + bool *font_bool; + + uint32_t ansi_colours[8]; + uint32_t ansi_bright_colours[8]; + uint32_t default_fg, default_bg; + uint32_t default_fg_bright, default_bg_bright; + + size_t canvas_size; + uint32_t *canvas; + + size_t grid_size; + size_t queue_size; + size_t map_size; + + struct flanterm_fb_char *grid; + + struct flanterm_fb_queue_item *queue; + size_t queue_i; + + struct flanterm_fb_queue_item **map; + + uint32_t text_fg; + uint32_t text_bg; + size_t cursor_x; + size_t cursor_y; + + uint32_t saved_state_text_fg; + uint32_t saved_state_text_bg; + size_t saved_state_cursor_x; + size_t saved_state_cursor_y; + + size_t old_cursor_x; + size_t old_cursor_y; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr/src/sp1/ext/flanterm/src/flanterm_private.h b/usr/src/sp1/ext/flanterm/src/flanterm_private.h new file mode 100644 index 0000000..d26f020 --- /dev/null +++ b/usr/src/sp1/ext/flanterm/src/flanterm_private.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* Copyright (C) 2022-2026 Mintsuki and contributors. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLANTERM_PRIVATE_H +#define FLANTERM_PRIVATE_H 1 + +#ifndef FLANTERM_IN_FLANTERM +#error "Do not use flanterm_private.h. Use interfaces defined in flanterm.h only." +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FLANTERM_MAX_ESC_VALUES 16 + +struct flanterm_context { + /* internal use */ + + size_t tab_size; + bool autoflush; + bool cursor_enabled; + bool scroll_enabled; + bool control_sequence; + bool escape; + bool osc; + bool osc_escape; + bool rrr; + bool discard_next; + bool bold; + bool bg_bold; + bool reverse_video; + bool dec_private; + bool insert_mode; + bool csi_unhandled; + uint64_t code_point; + size_t unicode_remaining; + uint8_t g_select; + uint8_t charsets[2]; + size_t current_charset; + size_t escape_offset; + size_t esc_values_i; + size_t saved_cursor_x; + size_t saved_cursor_y; + size_t current_primary; + size_t current_bg; + size_t scroll_top_margin; + size_t scroll_bottom_margin; + uint32_t esc_values[FLANTERM_MAX_ESC_VALUES]; + uint64_t oob_output; + bool saved_state_bold; + bool saved_state_bg_bold; + bool saved_state_reverse_video; + size_t saved_state_current_charset; + size_t saved_state_current_primary; + size_t saved_state_current_bg; + + /* to be set by backend */ + + size_t rows, cols; + + void (*raw_putchar)(struct flanterm_context *, uint8_t c); + void (*clear)(struct flanterm_context *, bool move); + void (*set_cursor_pos)(struct flanterm_context *, size_t x, size_t y); + void (*get_cursor_pos)(struct flanterm_context *, size_t *x, size_t *y); + void (*set_text_fg)(struct flanterm_context *, size_t fg); + void (*set_text_bg)(struct flanterm_context *, size_t bg); + void (*set_text_fg_bright)(struct flanterm_context *, size_t fg); + void (*set_text_bg_bright)(struct flanterm_context *, size_t bg); + void (*set_text_fg_rgb)(struct flanterm_context *, uint32_t fg); + void (*set_text_bg_rgb)(struct flanterm_context *, uint32_t bg); + void (*set_text_fg_default)(struct flanterm_context *); + void (*set_text_bg_default)(struct flanterm_context *); + void (*set_text_fg_default_bright)(struct flanterm_context *); + void (*set_text_bg_default_bright)(struct flanterm_context *); + void (*move_character)(struct flanterm_context *, size_t new_x, size_t new_y, size_t old_x, size_t old_y); + void (*scroll)(struct flanterm_context *); + void (*revscroll)(struct flanterm_context *); + void (*swap_palette)(struct flanterm_context *); + void (*save_state)(struct flanterm_context *); + void (*restore_state)(struct flanterm_context *); + void (*double_buffer_flush)(struct flanterm_context *); + void (*full_refresh)(struct flanterm_context *); + void (*deinit)(struct flanterm_context *, void (*)(void *, size_t)); + + /* to be set by client */ + + void (*callback)(struct flanterm_context *, uint64_t, uint64_t, uint64_t, uint64_t); +}; + +void flanterm_context_reinit(struct flanterm_context *ctx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr/src/sp1/ext/printf.c b/usr/src/sp1/ext/printf.c new file mode 100644 index 0000000..7139136 --- /dev/null +++ b/usr/src/sp1/ext/printf.c @@ -0,0 +1,919 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#ifndef _HAVE_bool +#define _HAVE_bool 1 +typedef _Bool bool; + +#define true 1 +#define false 0 +#endif /* !_HAVE_bool */ + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + + +// output function type +typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); + + +// wrapper (used as buffer) for output function type +typedef struct { + void (*fct)(char character, void* arg); + void* arg; +} out_fct_wrap_type; + + +// internal buffer output +static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) { + ((char*)buffer)[idx] = character; + } +} + + +// internal null output +static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)character; (void)buffer; (void)idx; (void)maxlen; +} + + +// internal _putchar wrapper +static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)buffer; (void)idx; (void)maxlen; + if (character) { + _putchar(character); + } +} + + +// internal output function wrapper +static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)idx; (void)maxlen; + if (character) { + // buffer is the output fct pointer + ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); + } +} + + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char* str, size_t maxsize) +{ + const char* s; + for (s = str; *s && maxsize--; ++s); + return (unsigned int)(s - str); +} + + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char** str) +{ + unsigned int i = 0U; + while (_is_digit(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for (size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + len--; + if (len && (base == 16U)) { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'x'; + } + else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'X'; + } + else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) { + frac = 0; + ++whole; + } + } + else if (diff < 0.5) { + } + else if ((frac == 0U) || (frac & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) { + if ((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } + else { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } else { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do { + switch (*format) { + case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; + case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; + case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; + case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; + case '#': flags |= FLAGS_HASH; format++; n = 1U; break; + default : n = 0U; break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) { + width = _atoi(&format); + } + else if (*format == '*') { + const int w = va_arg(va, int); + if (w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) { + precision = _atoi(&format); + } + else if (*format == '*') { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) { + case 'l' : + flags |= FLAGS_LONG; + format++; + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h' : + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't' : + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j' : + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z' : + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default : + break; + } + + // evaluate specifier + switch (*format) { + case 'd' : + case 'i' : + case 'u' : + case 'x' : + case 'X' : + case 'o' : + case 'b' : { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') { + base = 16U; + } + else if (*format == 'o') { + base = 8U; + } + else if (*format == 'b') { + base = 2U; + } + else { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) { + // signed + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + else { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } + else { + // unsigned + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } + else { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f' : + case 'F' : + if (*format == 'F') flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c' : { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's' : { + const char* p = va_arg(va, char*); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p' : { + width = sizeof(void*) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags); + } + else { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%' : + out('%', buffer, idx++, maxlen); + format++; + break; + + default : + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char* format, ...) +{ + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + + +int sprintf_(char* buffer, const char* format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + + +int snprintf_(char* buffer, size_t count, const char* format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + + +int vprintf_(const char* format, va_list va) +{ + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); +} + + +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + + +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) +{ + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = { out, arg }; + const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + va_end(va); + return ret; +} diff --git a/usr/src/sp1/head/io/cons/cons.h b/usr/src/sp1/head/io/cons/cons.h new file mode 100644 index 0000000..4a3e222 --- /dev/null +++ b/usr/src/sp1/head/io/cons/cons.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Mirocom Laboratories + * All rights reserved. + * + * The following sources are CONFIDENTIAL and PROPRIETARY + * property of Mirocom Laboratories. Unauthorized copying, + * use, distrubution or modification of this file, in whole + * and in part, is strictly prohibited without the prior written + * consent from Mirocom Laboratories. + */ + +#ifndef _CONS_CONS_H_ +#define _CONS_CONS_H_ 1 + +#include + +/* + * Represents console attributes + * + * @fg: Console foreground + * @bg: Console background + */ +struct cons_attr { + uint32_t fg; + uint32_t bg; +}; + +/* + * Initialize the boot console + * + * @attr: Console attributes + */ +void cons_init(struct cons_attr *attr); + +/* + * Write to the boot console + * + * @str: string to write + * @len: length of string to write + */ +void cons_write(const char *str, size_t len); + +#endif /* !_CONS_CONS_H_ */ diff --git a/usr/src/sp1/head/lib/_printf.h b/usr/src/sp1/head/lib/_printf.h new file mode 100644 index 0000000..e4abac3 --- /dev/null +++ b/usr/src/sp1/head/lib/_printf.h @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Output a character to a custom device like UART, used by the printf() function + * This function is declared here only. You have to write your custom implementation somewhere + * \param character Character to output + */ +void _putchar(char character); + + +/** + * Tiny printf implementation + * You have to implement _putchar if you use printf() + * To avoid conflicts with the regular printf() API it is overridden by macro defines + * and internal underscore-appended functions like printf_() are used + * \param format A string that specifies the format of the output + * \return The number of characters that are written into the array, not counting the terminating null character + */ +#define printf printf_ +int printf_(const char* format, ...); + + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! + * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! + * \param format A string that specifies the format of the output + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define sprintf sprintf_ +int sprintf_(char* buffer, const char* format, ...); + + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + * If the formatted string is truncated the buffer size (count) is returned + */ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ +int snprintf_(char* buffer, size_t count, const char* format, ...); +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); + + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define vprintf vprintf_ +int vprintf_(const char* format, va_list va); + + +/** + * printf with output function + * You may use this as dynamic alternative to printf() with its fixed _putchar() output + * \param out An output function which takes one character and an argument pointer + * \param arg An argument pointer for user data passed to output function + * \param format A string that specifies the format of the output + * \return The number of characters that are sent to the output function, not counting the terminating null character + */ +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); + + +#ifdef __cplusplus +} +#endif + + +#endif // _PRINTF_H_ diff --git a/usr/src/sp1/head/lib/printf.h b/usr/src/sp1/head/lib/printf.h new file mode 100644 index 0000000..bcefe8c --- /dev/null +++ b/usr/src/sp1/head/lib/printf.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2026, Mirocom Laboratories + * All rights reserved. + * + * The following sources are CONFIDENTIAL and PROPRIETARY + * property of Mirocom Laboratories. Unauthorized copying, + * use, distrubution or modification of this file, in whole + * and in part, is strictly prohibited without the prior written + * consent from Mirocom Laboratories. + */ + +#ifndef _LIB_PRINTF_H_ +#define _LIB_PRINTF_H_ 1 + +#include + +/* Aliases to follow style convention */ +#define Printf printf +#define FmtBuf snprintf +#define VFmtBuf vsnprintf + +#endif /* !_LIB_PRINTF_H_ */ diff --git a/usr/src/sp1/lib/_printf.c b/usr/src/sp1/lib/_printf.c new file mode 100644 index 0000000..e4397d8 --- /dev/null +++ b/usr/src/sp1/lib/_printf.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2026, Mirocom Laboratories + * All rights reserved. + * + * The following sources are CONFIDENTIAL and PROPRIETARY + * property of Mirocom Laboratories. Unauthorized copying, + * use, distrubution or modification of this file, in whole + * and in part, is strictly prohibited without the prior written + * consent from Mirocom Laboratories. + */ + +#include +#include + +void +_putchar(char c) +{ + cons_write(&c, 1); +}