From d1d514c16ab0cf2f4c8d1e21f8f61a7781c95d53 Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Mon, 13 Apr 2026 22:37:39 +0800 Subject: [PATCH] Stage 28 --- cleonos/c/apps/shell/shell_cmd.c | 18 +- cleonos/c/apps/shell/shell_util.c | 9 +- cleonos/c/apps/shell_main.c | 10 +- clks/include/clks/exec.h | 3 +- clks/include/clks/userland.h | 1 + clks/kernel/exec.c | 86 ++++++++- clks/kernel/kmain.c | 9 +- clks/kernel/syscall.c | 41 ++++- clks/kernel/tty.c | 295 ++++++++++++++++++++++++++---- clks/kernel/userland.c | 19 +- docs/README.md | 4 +- docs/stage27.md | 34 ++++ docs/stage28.md | 43 +++++ 13 files changed, 514 insertions(+), 58 deletions(-) create mode 100644 docs/stage27.md create mode 100644 docs/stage28.md diff --git a/cleonos/c/apps/shell/shell_cmd.c b/cleonos/c/apps/shell/shell_cmd.c index 29356be..81cc6ca 100644 --- a/cleonos/c/apps/shell/shell_cmd.c +++ b/cleonos/c/apps/shell/shell_cmd.c @@ -398,12 +398,16 @@ static int ush_cmd_exit(ush_state *sh, const char *arg) { } static int ush_cmd_clear(void) { - u64 i; - - for (i = 0ULL; i < USH_CLEAR_LINES; i++) { - ush_write_char('\n'); - } + ush_write("\x1B[2J\x1B[H"); + return 1; +} +static int ush_cmd_ansi(void) { + ush_writeln("\x1B[1;36mansi color demo\x1B[0m"); + ush_writeln(" \x1B[30mblack\x1B[0m \x1B[31mred\x1B[0m \x1B[32mgreen\x1B[0m \x1B[33myellow\x1B[0m"); + ush_writeln(" \x1B[34mblue\x1B[0m \x1B[35mmagenta\x1B[0m \x1B[36mcyan\x1B[0m \x1B[37mwhite\x1B[0m"); + ush_writeln(" \x1B[90mbright-black\x1B[0m \x1B[91mbright-red\x1B[0m \x1B[92mbright-green\x1B[0m \x1B[93mbright-yellow\x1B[0m"); + ush_writeln(" \x1B[94mbright-blue\x1B[0m \x1B[95mbright-magenta\x1B[0m \x1B[96mbright-cyan\x1B[0m \x1B[97mbright-white\x1B[0m"); return 1; } @@ -874,6 +878,8 @@ void ush_execute_line(ush_state *sh, const char *line) { success = ush_cmd_exit(sh, arg); } else if (ush_streq(cmd, "clear") != 0 || ush_streq(cmd, "cls") != 0) { success = ush_cmd_clear(); + } else if (ush_streq(cmd, "ansi") != 0 || ush_streq(cmd, "color") != 0) { + success = ush_cmd_ansi(); } else if (ush_streq(cmd, "memstat") != 0) { success = ush_cmd_memstat(); } else if (ush_streq(cmd, "fsstat") != 0) { @@ -925,4 +931,4 @@ void ush_execute_line(ush_state *sh, const char *line) { if (known == 0) { sh->cmd_unknown++; } -} \ No newline at end of file +} diff --git a/cleonos/c/apps/shell/shell_util.c b/cleonos/c/apps/shell/shell_util.c index 785c648..e17a56d 100644 --- a/cleonos/c/apps/shell/shell_util.c +++ b/cleonos/c/apps/shell/shell_util.c @@ -246,13 +246,14 @@ void ush_writeln(const char *text) { void ush_prompt(const ush_state *sh) { if (sh == (const ush_state *)0) { - ush_write("cleonos(user)> "); + ush_write("\x1B[96mcleonos\x1B[0m(\x1B[92muser\x1B[0m)> "); return; } - ush_write("cleonos(user:"); + ush_write("\x1B[96mcleonos\x1B[0m(\x1B[92muser\x1B[0m:"); + ush_write("\x1B[93m"); ush_write(sh->cwd); - ush_write(")> "); + ush_write("\x1B[0m)> "); } void ush_write_hex_u64(u64 value) { @@ -272,4 +273,4 @@ void ush_print_kv_hex(const char *label, u64 value) { ush_write(": "); ush_write_hex_u64(value); ush_write_char('\n'); -} \ No newline at end of file +} diff --git a/cleonos/c/apps/shell_main.c b/cleonos/c/apps/shell_main.c index 8a5ddf7..5d21e71 100644 --- a/cleonos/c/apps/shell_main.c +++ b/cleonos/c/apps/shell_main.c @@ -5,10 +5,12 @@ int cleonos_app_main(void) { char line[USH_LINE_MAX]; ush_init_state(&sh); - ush_writeln("[USER][SHELL] interactive framework online"); + ush_writeln("\x1B[92m[USER][SHELL]\x1B[0m interactive framework online"); - if (ush_run_script_file(&sh, "/shell/init.cmd") == 0) { - ush_writeln("[USER][SHELL] /shell/init.cmd missing"); + if (ush_run_script_file(&sh, "/shell/init.cmd") == 0 && + ush_run_script_file(&sh, "/shell/INIT.CMD") == 0 && + ush_run_script_file(&sh, "/SHELL/INIT.CMD") == 0) { + ush_writeln("\x1B[33m[USER][SHELL]\x1B[0m init script not found, continue interactive mode"); } for (;;) { @@ -19,4 +21,4 @@ int cleonos_app_main(void) { return (int)(sh.exit_code & 0x7FFFFFFFULL); } } -} \ No newline at end of file +} diff --git a/clks/include/clks/exec.h b/clks/include/clks/exec.h index 63740f1..6fda6ae 100644 --- a/clks/include/clks/exec.h +++ b/clks/include/clks/exec.h @@ -14,5 +14,6 @@ u64 clks_exec_yield(void); u64 clks_exec_request_count(void); u64 clks_exec_success_count(void); clks_bool clks_exec_is_running(void); +clks_bool clks_exec_current_path_is_user(void); -#endif \ No newline at end of file +#endif diff --git a/clks/include/clks/userland.h b/clks/include/clks/userland.h index d621004..5eff098 100644 --- a/clks/include/clks/userland.h +++ b/clks/include/clks/userland.h @@ -7,6 +7,7 @@ clks_bool clks_userland_init(void); void clks_userland_tick(u64 tick); clks_bool clks_userland_shell_ready(void); clks_bool clks_userland_shell_exec_requested(void); +clks_bool clks_userland_shell_auto_exec_enabled(void); u64 clks_userland_launch_attempts(void); u64 clks_userland_launch_success(void); u64 clks_userland_launch_failures(void); diff --git a/clks/kernel/exec.c b/clks/kernel/exec.c index 5474d2f..6de92cc 100644 --- a/clks/kernel/exec.c +++ b/clks/kernel/exec.c @@ -47,6 +47,69 @@ static clks_bool clks_exec_exit_requested_stack[CLKS_EXEC_MAX_DEPTH]; static u64 clks_exec_exit_status_stack[CLKS_EXEC_MAX_DEPTH]; static u32 clks_exec_pid_stack_depth = 0U; +static clks_bool clks_exec_has_prefix(const char *text, const char *prefix) { + usize i = 0U; + + if (text == CLKS_NULL || prefix == CLKS_NULL) { + return CLKS_FALSE; + } + + while (prefix[i] != '\0') { + if (text[i] != prefix[i]) { + return CLKS_FALSE; + } + + i++; + } + + return CLKS_TRUE; +} + +static clks_bool clks_exec_has_suffix(const char *text, const char *suffix) { + usize text_len; + usize suffix_len; + usize i; + + if (text == CLKS_NULL || suffix == CLKS_NULL) { + return CLKS_FALSE; + } + + text_len = clks_strlen(text); + suffix_len = clks_strlen(suffix); + + if (suffix_len > text_len) { + return CLKS_FALSE; + } + + for (i = 0U; i < suffix_len; i++) { + if (text[text_len - suffix_len + i] != suffix[i]) { + return CLKS_FALSE; + } + } + + return CLKS_TRUE; +} + +static clks_bool clks_exec_path_is_user_program(const char *path) { + if (path == CLKS_NULL || path[0] != '/') { + return CLKS_FALSE; + } + + if (clks_exec_has_prefix(path, "/system/") == CLKS_TRUE) { + return CLKS_FALSE; + } + + if (clks_exec_has_prefix(path, "/driver/") == CLKS_TRUE) { + return CLKS_FALSE; + } + + if (clks_exec_has_prefix(path, "/shell/") == CLKS_TRUE) { + return CLKS_TRUE; + } + + return clks_exec_has_suffix(path, ".elf"); +} + static void clks_exec_copy_path(char *dst, usize dst_size, const char *src) { usize i = 0U; @@ -394,4 +457,25 @@ u64 clks_exec_success_count(void) { clks_bool clks_exec_is_running(void) { return (clks_exec_running_depth > 0U) ? CLKS_TRUE : CLKS_FALSE; -} \ No newline at end of file +} + +clks_bool clks_exec_current_path_is_user(void) { + i32 depth_index; + i32 slot; + const struct clks_exec_proc_record *proc; + + depth_index = clks_exec_current_depth_index(); + + if (depth_index < 0) { + return CLKS_FALSE; + } + + slot = clks_exec_proc_find_slot_by_pid(clks_exec_pid_stack[(u32)depth_index]); + + if (slot < 0) { + return CLKS_FALSE; + } + + proc = &clks_exec_proc_table[(u32)slot]; + return clks_exec_path_is_user_program(proc->path); +} diff --git a/clks/kernel/kmain.c b/clks/kernel/kmain.c index a2070ed..619567f 100644 --- a/clks/kernel/kmain.c +++ b/clks/kernel/kmain.c @@ -99,7 +99,7 @@ void clks_kernel_main(void) { clks_tty_init(); } - clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS Stage25 START"); + clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS Stage28 START"); if (boot_fb == CLKS_NULL) { clks_log(CLKS_LOG_WARN, "VIDEO", "NO FRAMEBUFFER FROM LIMINE"); @@ -224,7 +224,12 @@ void clks_kernel_main(void) { clks_log_hex(CLKS_LOG_INFO, "SYSCALL", "TICKS", syscall_ticks); clks_shell_init(); - clks_log(CLKS_LOG_INFO, "SHELL", "KERNEL SHELL ACTIVE"); + + if (clks_userland_shell_auto_exec_enabled() == CLKS_TRUE) { + clks_log(CLKS_LOG_INFO, "SHELL", "DEFAULT ENTER USER SHELL MODE"); + } else { + clks_log(CLKS_LOG_INFO, "SHELL", "KERNEL SHELL ACTIVE"); + } clks_log_hex(CLKS_LOG_INFO, "TTY", "COUNT", (u64)clks_tty_count()); clks_log_hex(CLKS_LOG_INFO, "TTY", "ACTIVE", (u64)clks_tty_active()); diff --git a/clks/kernel/syscall.c b/clks/kernel/syscall.c index 6e6f2d1..9f60dcc 100644 --- a/clks/kernel/syscall.c +++ b/clks/kernel/syscall.c @@ -19,6 +19,7 @@ #define CLKS_SYSCALL_TTY_MAX_LEN 512U #define CLKS_SYSCALL_FS_IO_MAX_LEN 65536U #define CLKS_SYSCALL_JOURNAL_MAX_LEN 256U +#define CLKS_SYSCALL_USER_TRACE_BUDGET 128ULL struct clks_syscall_frame { u64 rax; @@ -46,6 +47,8 @@ struct clks_syscall_frame { }; static clks_bool clks_syscall_ready = CLKS_FALSE; +static clks_bool clks_syscall_user_trace_active = CLKS_FALSE; +static u64 clks_syscall_user_trace_budget = 0ULL; static clks_bool clks_syscall_copy_user_string(u64 src_addr, char *dst, usize dst_size) { const char *src = (const char *)src_addr; @@ -375,8 +378,43 @@ static u64 clks_syscall_log_journal_read(u64 arg0, u64 arg1, u64 arg2) { return 1ULL; } +static void clks_syscall_trace_user_program(u64 id) { + clks_bool user_program_running = + (clks_exec_is_running() == CLKS_TRUE && clks_exec_current_path_is_user() == CLKS_TRUE) + ? CLKS_TRUE + : CLKS_FALSE; + + if (user_program_running == CLKS_FALSE) { + if (clks_syscall_user_trace_active == CLKS_TRUE) { + clks_log(CLKS_LOG_DEBUG, "SYSCALL", "USER_TRACE_END"); + } + + clks_syscall_user_trace_active = CLKS_FALSE; + clks_syscall_user_trace_budget = 0ULL; + return; + } + + if (clks_syscall_user_trace_active == CLKS_FALSE) { + clks_syscall_user_trace_active = CLKS_TRUE; + clks_syscall_user_trace_budget = CLKS_SYSCALL_USER_TRACE_BUDGET; + clks_log(CLKS_LOG_DEBUG, "SYSCALL", "USER_TRACE_BEGIN"); + clks_log_hex(CLKS_LOG_DEBUG, "SYSCALL", "PID", clks_exec_current_pid()); + } + + if (clks_syscall_user_trace_budget > 0ULL) { + clks_log_hex(CLKS_LOG_DEBUG, "SYSCALL", "USER_ID", id); + clks_syscall_user_trace_budget--; + + if (clks_syscall_user_trace_budget == 0ULL) { + clks_log(CLKS_LOG_DEBUG, "SYSCALL", "USER_TRACE_BUDGET_EXHAUSTED"); + } + } +} + void clks_syscall_init(void) { clks_syscall_ready = CLKS_TRUE; + clks_syscall_user_trace_active = CLKS_FALSE; + clks_syscall_user_trace_budget = 0ULL; clks_log(CLKS_LOG_INFO, "SYSCALL", "INT80 FRAMEWORK ONLINE"); } @@ -389,6 +427,7 @@ u64 clks_syscall_dispatch(void *frame_ptr) { } id = frame->rax; + clks_syscall_trace_user_program(id); switch (id) { case CLKS_SYSCALL_LOG_WRITE: @@ -506,4 +545,4 @@ u64 clks_syscall_invoke_kernel(u64 id, u64 arg0, u64 arg1, u64 arg2) { ); return ret; -} \ No newline at end of file +} diff --git a/clks/kernel/tty.c b/clks/kernel/tty.c index e0bdee3..4800771 100644 --- a/clks/kernel/tty.c +++ b/clks/kernel/tty.c @@ -12,10 +12,24 @@ #define CLKS_TTY_CURSOR_BLINK_INTERVAL_TICKS 5ULL #define CLKS_TTY_BLINK_TICK_UNSET 0xFFFFFFFFFFFFFFFFULL #define CLKS_TTY_DESKTOP_INDEX 1U +#define CLKS_TTY_ANSI_MAX_LEN 31U + +typedef struct clks_tty_ansi_state { + clks_bool in_escape; + clks_bool saw_csi; + clks_bool bold; + u32 len; + char params[CLKS_TTY_ANSI_MAX_LEN + 1U]; +} clks_tty_ansi_state; static char clks_tty_cells[CLKS_TTY_COUNT][CLKS_TTY_MAX_ROWS][CLKS_TTY_MAX_COLS]; +static u32 clks_tty_cell_fg[CLKS_TTY_COUNT][CLKS_TTY_MAX_ROWS][CLKS_TTY_MAX_COLS]; +static u32 clks_tty_cell_bg[CLKS_TTY_COUNT][CLKS_TTY_MAX_ROWS][CLKS_TTY_MAX_COLS]; static u32 clks_tty_cursor_row[CLKS_TTY_COUNT]; static u32 clks_tty_cursor_col[CLKS_TTY_COUNT]; +static u32 clks_tty_current_fg[CLKS_TTY_COUNT]; +static u32 clks_tty_current_bg[CLKS_TTY_COUNT]; +static clks_tty_ansi_state clks_tty_ansi[CLKS_TTY_COUNT]; static u32 clks_tty_rows = 0; static u32 clks_tty_cols = 0; @@ -27,24 +41,71 @@ static clks_bool clks_tty_cursor_visible = CLKS_FALSE; static clks_bool clks_tty_blink_enabled = CLKS_TRUE; static u64 clks_tty_blink_last_tick = CLKS_TTY_BLINK_TICK_UNSET; -static void clks_tty_fill_row(u32 tty_index, u32 row, char ch) { - u32 col; +static u32 clks_tty_ansi_palette(u32 index) { + static const u32 palette[16] = { + 0x00000000U, 0x00CD3131U, 0x000DBC79U, 0x00E5E510U, + 0x002472C8U, 0x00BC3FBCU, 0x0011A8CDU, 0x00E5E5E5U, + 0x00666666U, 0x00F14C4CU, 0x0023D18BU, 0x00F5F543U, + 0x003B8EEAU, 0x00D670D6U, 0x0029B8DBU, 0x00FFFFFFU + }; - for (col = 0; col < clks_tty_cols; col++) { - clks_tty_cells[tty_index][row][col] = ch; + if (index < 16U) { + return palette[index]; } + + return CLKS_TTY_FG; +} + +static void clks_tty_reset_blink_timer(void) { + clks_tty_blink_last_tick = CLKS_TTY_BLINK_TICK_UNSET; } static void clks_tty_draw_cell_with_colors(u32 row, u32 col, char ch, u32 fg, u32 bg) { clks_fb_draw_char(col * clks_tty_cell_width, row * clks_tty_cell_height, ch, fg, bg); } -static void clks_tty_draw_cell(u32 row, u32 col, char ch) { - clks_tty_draw_cell_with_colors(row, col, ch, CLKS_TTY_FG, CLKS_TTY_BG); +static void clks_tty_draw_cell(u32 tty_index, u32 row, u32 col) { + clks_tty_draw_cell_with_colors( + row, + col, + clks_tty_cells[tty_index][row][col], + clks_tty_cell_fg[tty_index][row][col], + clks_tty_cell_bg[tty_index][row][col] + ); } -static void clks_tty_reset_blink_timer(void) { - clks_tty_blink_last_tick = CLKS_TTY_BLINK_TICK_UNSET; +static void clks_tty_reset_color_state(u32 tty_index) { + clks_tty_current_fg[tty_index] = CLKS_TTY_FG; + clks_tty_current_bg[tty_index] = CLKS_TTY_BG; + clks_tty_ansi[tty_index].bold = CLKS_FALSE; +} + +static void clks_tty_reset_ansi_state(u32 tty_index) { + clks_tty_ansi[tty_index].in_escape = CLKS_FALSE; + clks_tty_ansi[tty_index].saw_csi = CLKS_FALSE; + clks_tty_ansi[tty_index].len = 0U; + clks_tty_ansi[tty_index].params[0] = '\0'; +} + +static void clks_tty_fill_row(u32 tty_index, u32 row, char ch) { + u32 col; + + for (col = 0; col < clks_tty_cols; col++) { + clks_tty_cells[tty_index][row][col] = ch; + clks_tty_cell_fg[tty_index][row][col] = CLKS_TTY_FG; + clks_tty_cell_bg[tty_index][row][col] = CLKS_TTY_BG; + } +} + +static void clks_tty_clear_tty(u32 tty_index) { + u32 row; + + for (row = 0; row < clks_tty_rows; row++) { + clks_tty_fill_row(tty_index, row, ' '); + } + + clks_tty_cursor_row[tty_index] = 0U; + clks_tty_cursor_col[tty_index] = 0U; } static void clks_tty_hide_cursor(void) { @@ -59,7 +120,7 @@ static void clks_tty_hide_cursor(void) { col = clks_tty_cursor_col[clks_tty_active_index]; if (row < clks_tty_rows && col < clks_tty_cols) { - clks_tty_draw_cell(row, col, clks_tty_cells[clks_tty_active_index][row][col]); + clks_tty_draw_cell(clks_tty_active_index, row, col); } clks_tty_cursor_visible = CLKS_FALSE; @@ -68,6 +129,9 @@ static void clks_tty_hide_cursor(void) { static void clks_tty_draw_cursor(void) { u32 row; u32 col; + u32 fg; + u32 bg; + char ch; if (clks_tty_is_ready == CLKS_FALSE) { return; @@ -86,14 +150,11 @@ static void clks_tty_draw_cursor(void) { return; } - clks_tty_draw_cell_with_colors( - row, - col, - clks_tty_cells[clks_tty_active_index][row][col], - CLKS_TTY_BG, - CLKS_TTY_FG - ); + ch = clks_tty_cells[clks_tty_active_index][row][col]; + fg = clks_tty_cell_fg[clks_tty_active_index][row][col]; + bg = clks_tty_cell_bg[clks_tty_active_index][row][col]; + clks_tty_draw_cell_with_colors(row, col, ch, bg, fg); clks_tty_cursor_visible = CLKS_TRUE; } @@ -106,7 +167,7 @@ static void clks_tty_redraw_active(void) { for (row = 0; row < clks_tty_rows; row++) { for (col = 0; col < clks_tty_cols; col++) { - clks_tty_draw_cell(row, col, clks_tty_cells[clks_tty_active_index][row][col]); + clks_tty_draw_cell(clks_tty_active_index, row, col); } } @@ -117,14 +178,20 @@ static void clks_tty_scroll_up(u32 tty_index) { u32 row; for (row = 1; row < clks_tty_rows; row++) { + clks_memcpy(clks_tty_cells[tty_index][row - 1U], clks_tty_cells[tty_index][row], clks_tty_cols); clks_memcpy( - clks_tty_cells[tty_index][row - 1], - clks_tty_cells[tty_index][row], - clks_tty_cols + clks_tty_cell_fg[tty_index][row - 1U], + clks_tty_cell_fg[tty_index][row], + (usize)clks_tty_cols * sizeof(u32) + ); + clks_memcpy( + clks_tty_cell_bg[tty_index][row - 1U], + clks_tty_cell_bg[tty_index][row], + (usize)clks_tty_cols * sizeof(u32) ); } - clks_tty_fill_row(tty_index, clks_tty_rows - 1, ' '); + clks_tty_fill_row(tty_index, clks_tty_rows - 1U, ' '); if (tty_index == clks_tty_active_index) { u32 col; @@ -132,16 +199,18 @@ static void clks_tty_scroll_up(u32 tty_index) { clks_fb_scroll_up(clks_tty_cell_height, CLKS_TTY_BG); for (col = 0U; col < clks_tty_cols; col++) { - clks_tty_draw_cell(clks_tty_rows - 1U, col, clks_tty_cells[tty_index][clks_tty_rows - 1U][col]); + clks_tty_draw_cell(tty_index, clks_tty_rows - 1U, col); } } } static void clks_tty_put_visible(u32 tty_index, u32 row, u32 col, char ch) { clks_tty_cells[tty_index][row][col] = ch; + clks_tty_cell_fg[tty_index][row][col] = clks_tty_current_fg[tty_index]; + clks_tty_cell_bg[tty_index][row][col] = clks_tty_current_bg[tty_index]; if (tty_index == clks_tty_active_index) { - clks_tty_draw_cell(row, col, ch); + clks_tty_draw_cell(tty_index, row, col); } } @@ -206,10 +275,163 @@ static void clks_tty_put_char_raw(u32 tty_index, char ch) { } } +static void clks_tty_ansi_apply_sgr_code(u32 tty_index, u32 code) { + if (code == 0U) { + clks_tty_reset_color_state(tty_index); + return; + } + + if (code == 1U) { + clks_tty_ansi[tty_index].bold = CLKS_TRUE; + return; + } + + if (code == 22U) { + clks_tty_ansi[tty_index].bold = CLKS_FALSE; + return; + } + + if (code == 39U) { + clks_tty_current_fg[tty_index] = CLKS_TTY_FG; + return; + } + + if (code == 49U) { + clks_tty_current_bg[tty_index] = CLKS_TTY_BG; + return; + } + + if (code >= 30U && code <= 37U) { + u32 idx = code - 30U; + + if (clks_tty_ansi[tty_index].bold == CLKS_TRUE) { + idx += 8U; + } + + clks_tty_current_fg[tty_index] = clks_tty_ansi_palette(idx); + return; + } + + if (code >= 90U && code <= 97U) { + clks_tty_current_fg[tty_index] = clks_tty_ansi_palette((code - 90U) + 8U); + return; + } + + if (code >= 40U && code <= 47U) { + clks_tty_current_bg[tty_index] = clks_tty_ansi_palette(code - 40U); + return; + } + + if (code >= 100U && code <= 107U) { + clks_tty_current_bg[tty_index] = clks_tty_ansi_palette((code - 100U) + 8U); + return; + } +} + +static void clks_tty_ansi_apply_sgr_params(u32 tty_index, const char *params, u32 len) { + u32 i; + u32 value = 0U; + clks_bool has_digit = CLKS_FALSE; + + if (len == 0U) { + clks_tty_ansi_apply_sgr_code(tty_index, 0U); + return; + } + + for (i = 0U; i <= len; i++) { + char ch = (i < len) ? params[i] : ';'; + + if (ch >= '0' && ch <= '9') { + has_digit = CLKS_TRUE; + value = (value * 10U) + (u32)(ch - '0'); + continue; + } + + if (ch == ';') { + if (has_digit == CLKS_TRUE) { + clks_tty_ansi_apply_sgr_code(tty_index, value); + } else { + clks_tty_ansi_apply_sgr_code(tty_index, 0U); + } + + value = 0U; + has_digit = CLKS_FALSE; + continue; + } + + return; + } +} + +static clks_bool clks_tty_ansi_process_byte(u32 tty_index, char ch) { + clks_tty_ansi_state *state = &clks_tty_ansi[tty_index]; + + if (state->in_escape == CLKS_FALSE) { + if ((u8)ch == 0x1BU) { + state->in_escape = CLKS_TRUE; + state->saw_csi = CLKS_FALSE; + state->len = 0U; + state->params[0] = '\0'; + return CLKS_TRUE; + } + + return CLKS_FALSE; + } + + if (state->saw_csi == CLKS_FALSE) { + if (ch == '[') { + state->saw_csi = CLKS_TRUE; + return CLKS_TRUE; + } + + clks_tty_reset_ansi_state(tty_index); + return CLKS_FALSE; + } + + if ((ch >= '0' && ch <= '9') || ch == ';') { + if (state->len < CLKS_TTY_ANSI_MAX_LEN) { + state->params[state->len++] = ch; + state->params[state->len] = '\0'; + } else { + clks_tty_reset_ansi_state(tty_index); + } + + return CLKS_TRUE; + } + + if (ch == 'm') { + clks_tty_ansi_apply_sgr_params(tty_index, state->params, state->len); + clks_tty_reset_ansi_state(tty_index); + return CLKS_TRUE; + } + + if (ch == 'J') { + if (state->len == 0U || (state->len == 1U && state->params[0] == '2')) { + clks_tty_clear_tty(tty_index); + + if (tty_index == clks_tty_active_index) { + clks_tty_redraw_active(); + } + } + + clks_tty_reset_ansi_state(tty_index); + return CLKS_TRUE; + } + + if (ch == 'H') { + clks_tty_cursor_row[tty_index] = 0U; + clks_tty_cursor_col[tty_index] = 0U; + clks_tty_reset_ansi_state(tty_index); + return CLKS_TRUE; + } + + clks_tty_reset_ansi_state(tty_index); + return CLKS_TRUE; +} + void clks_tty_init(void) { struct clks_framebuffer_info info; u32 tty; - u32 row; if (clks_fb_ready() == CLKS_FALSE) { clks_tty_is_ready = CLKS_FALSE; @@ -244,16 +466,15 @@ void clks_tty_init(void) { return; } - for (tty = 0; tty < CLKS_TTY_COUNT; tty++) { - clks_tty_cursor_row[tty] = 0; - clks_tty_cursor_col[tty] = 0; - - for (row = 0; row < clks_tty_rows; row++) { - clks_tty_fill_row(tty, row, ' '); - } + for (tty = 0U; tty < CLKS_TTY_COUNT; tty++) { + clks_tty_cursor_row[tty] = 0U; + clks_tty_cursor_col[tty] = 0U; + clks_tty_reset_color_state(tty); + clks_tty_reset_ansi_state(tty); + clks_tty_clear_tty(tty); } - clks_tty_active_index = 0; + clks_tty_active_index = 0U; clks_tty_is_ready = CLKS_TRUE; clks_tty_cursor_visible = CLKS_FALSE; clks_tty_blink_enabled = CLKS_TRUE; @@ -271,7 +492,10 @@ void clks_tty_write_char(char ch) { clks_tty_hide_cursor(); tty_index = clks_tty_active_index; - clks_tty_put_char_raw(tty_index, ch); + + if (clks_tty_ansi_process_byte(tty_index, ch) == CLKS_FALSE) { + clks_tty_put_char_raw(tty_index, ch); + } clks_tty_draw_cursor(); clks_tty_reset_blink_timer(); @@ -289,7 +513,10 @@ void clks_tty_write(const char *text) { tty_index = clks_tty_active_index; while (text[i] != '\0') { - clks_tty_put_char_raw(tty_index, text[i]); + if (clks_tty_ansi_process_byte(tty_index, text[i]) == CLKS_FALSE) { + clks_tty_put_char_raw(tty_index, text[i]); + } + i++; } diff --git a/clks/kernel/userland.c b/clks/kernel/userland.c index 6697079..58a2c60 100644 --- a/clks/kernel/userland.c +++ b/clks/kernel/userland.c @@ -14,6 +14,7 @@ static u64 clks_user_launch_attempt_count = 0ULL; static u64 clks_user_launch_success_count = 0ULL; static u64 clks_user_launch_fail_count = 0ULL; static u64 clks_user_last_try_tick = 0ULL; +static clks_bool clks_user_first_try_pending = CLKS_FALSE; static clks_bool clks_userland_probe_elf(const char *path, const char *tag) { const void *image; @@ -83,11 +84,12 @@ clks_bool clks_userland_init(void) { clks_user_shell_ready = CLKS_FALSE; clks_user_shell_exec_requested_flag = CLKS_FALSE; - clks_user_shell_exec_enabled = CLKS_FALSE; + clks_user_shell_exec_enabled = CLKS_TRUE; clks_user_launch_attempt_count = 0ULL; clks_user_launch_success_count = 0ULL; clks_user_launch_fail_count = 0ULL; clks_user_last_try_tick = 0ULL; + clks_user_first_try_pending = CLKS_TRUE; if (clks_userland_probe_elf("/shell/shell.elf", "SHELL ELF READY") == CLKS_FALSE) { return CLKS_FALSE; @@ -105,7 +107,7 @@ clks_bool clks_userland_init(void) { return CLKS_FALSE; } - clks_log(CLKS_LOG_INFO, "USER", "USER SHELL EXEC DISABLED (KERNEL SHELL MODE)"); + clks_log(CLKS_LOG_INFO, "USER", "USER SHELL AUTO EXEC ENABLED"); return CLKS_TRUE; } @@ -116,6 +118,13 @@ void clks_userland_tick(u64 tick) { return; } + if (clks_user_first_try_pending == CLKS_TRUE) { + clks_user_first_try_pending = CLKS_FALSE; + clks_user_last_try_tick = tick; + (void)clks_userland_request_shell_exec(); + return; + } + if (tick - clks_user_last_try_tick < CLKS_USERLAND_RETRY_INTERVAL) { return; } @@ -132,6 +141,10 @@ clks_bool clks_userland_shell_exec_requested(void) { return clks_user_shell_exec_requested_flag; } +clks_bool clks_userland_shell_auto_exec_enabled(void) { + return clks_user_shell_exec_enabled; +} + u64 clks_userland_launch_attempts(void) { return clks_user_launch_attempt_count; } @@ -142,4 +155,4 @@ u64 clks_userland_launch_success(void) { u64 clks_userland_launch_failures(void) { return clks_user_launch_fail_count; -} \ No newline at end of file +} diff --git a/docs/README.md b/docs/README.md index 50de0d1..9ae51c1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -23,11 +23,11 @@ - `stage24.md` - `stage25.md` - `stage26.md` +- `stage27.md` +- `stage28.md` - `syscall.md` (syscall ABI reference) ## Notes - Stage docs use a fixed template: goal, implementation, acceptance criteria, build targets, QEMU command, and debugging notes. - Stages 16~19 are currently not documented in this folder; add them later using the same template to keep history continuous. - - diff --git a/docs/stage27.md b/docs/stage27.md new file mode 100644 index 0000000..0c21823 --- /dev/null +++ b/docs/stage27.md @@ -0,0 +1,34 @@ +# Stage 27 - User Shell/Wine Split and Syscall Expansion + +## Goal +- Expand user shell capability toward kernel-shell parity. +- Extend syscall ABI for richer process/runtime operations. +- Split CLeonOS-Wine implementation into maintainable modules. + +## Implementation +- Added more user-facing commands in user shell around process/runtime control (`spawn`, `wait`, `sleep`, `yield`, `pid`, file ops under `/temp`). +- Extended syscall surface (process and runtime related IDs) and kept kernel/user syscall ID tables aligned. +- Updated user runtime syscall wrappers to cover newly added syscall IDs. +- Refactored Wine codebase from single-file implementation into modular structure for CLI, ELF loader, syscall bridge, and runtime helpers. + +## Acceptance Criteria +- User shell can complete process and temp-file workflows without falling back to kernel shell. +- New syscall IDs are available consistently in: + - `clks/include/clks/syscall.h` + - `cleonos/c/include/cleonos_syscall.h` + - `cleonos/c/src/syscall.c` +- Wine entry remains runnable after split (`python cleonos_wine.py ...`). + +## Build Targets +- `make userapps` +- `make ramdisk` +- `make iso` +- `make run` + +## QEMU Command +- `make run` + +## Debug Notes +- If a user command reports `request failed`, verify syscall ID mapping first. +- If `wait`/`spawn` behavior is wrong, check `clks/kernel/exec.c` process table and pid stack. +- If Wine import fails after split, check package/module import paths and `__init__.py` exports. diff --git a/docs/stage28.md b/docs/stage28.md new file mode 100644 index 0000000..6d0d282 --- /dev/null +++ b/docs/stage28.md @@ -0,0 +1,43 @@ +# Stage 28 - ANSI TTY + Default User Shell Entry + User Syscall Trace + +## Goal +- Add ANSI color control support to TTY. +- Introduce ANSI-colored experience in user shell. +- Default to entering user shell after kernel is ready. +- Add serial log tracing for syscalls while user programs are running. + +## Implementation +- TTY: + - Added ANSI CSI/SGR parsing (`ESC[...m`) for standard foreground/background colors. + - Added support for reset/default color behavior (`0`, `39`, `49`) and bright color range (`90..97`, `100..107`). + - Added per-cell color buffers so scrolling/redraw keeps color attributes. +- User shell: + - Colored prompt and startup text now use ANSI sequences. + - Added `ansi`/`color` command to preview palette. + - `clear` now uses ANSI clear/home sequence. +- Boot behavior: + - Enabled user shell auto-exec by default in userland manager. + - Boot log now reports default user-shell mode when auto-exec is enabled. +- Syscall tracing: + - Added bounded debug trace in `clks/kernel/syscall.c` for syscalls issued while a user-path program is active. + - Trace emits begin/end markers and syscall IDs with budget guard to avoid log flooding. + +## Acceptance Criteria +- User shell prompt/output can display ANSI colors correctly on TTY. +- Boot enters default user-shell flow (auto launch enabled). +- Serial logs include syscall trace lines while user programs execute. +- Stage27 and Stage28 docs are present and indexed in `docs/README.md`. + +## Build Targets +- `make userapps` +- `make ramdisk` +- `make iso` +- `make run` + +## QEMU Command +- `make run` + +## Debug Notes +- If ANSI text shows raw escape chars, verify `clks/kernel/tty.c` ANSI parser path is compiled. +- If user shell does not auto launch, check `clks/kernel/userland.c` auto-exec flags and retry tick flow. +- If syscall trace is missing, confirm `clks_exec_current_path_is_user()` is true during the target run.