]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: kconsole overhaul -- bugs fixed + readline + scrollback + extended keyboard
authorTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 07:22:15 +0000 (04:22 -0300)
committerTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 07:22:15 +0000 (04:22 -0300)
Bug fixes:
1. dmesg pollution from mem/ls/cat: added klog_set_suppress() flag
   in console.c. kconsole wraps command execution with suppression
   so interactive output doesn't contaminate the kernel log buffer.

2. UTF-8 em dash rendering as garbage on VGA: replaced all UTF-8
   em dashes in output strings (main.c, init.c) with ASCII '--'.
   VGA text mode only supports CP437, not UTF-8.

3. PANIC message appearing in dmesg: changed from kprintf() to
   console_write() so the emergency banner goes to screen/serial
   but NOT to the kernel log ring buffer.

Features:
1. VGA scrollback buffer (200 lines): lines that scroll off the
   top of the screen are saved in a circular buffer. Shift+PgUp
   scrolls back half-page, Shift+PgDn scrolls forward. Any new
   output auto-returns to live view (like Linux).

2. Command history (16 entries): Up/Down arrow keys navigate
   through previous commands. Duplicate suppression. Current
   line is saved/restored when navigating.

3. Full line editing: Left/Right arrows move cursor, Home/End
   jump to start/end, Delete key, insert-at-cursor mode.
   Emacs keybindings: Ctrl-A (home), Ctrl-E (end), Ctrl-U
   (kill line), Ctrl-K (kill to end).

4. Extended keyboard driver: HAL x86 keyboard now tracks shift
   state, handles 0xE0 extended scancodes, emits VT100 escape
   sequences for arrow keys/Home/End/PgUp/PgDn/Delete. Shift
   scancode map added for uppercase letters and symbols.
   Shift+PgUp/PgDn calls vga_scroll_back/fwd directly.

5. Serial input works with readline: VT100 sequences from
   terminal emulators are parsed by the same escape state
   machine, so -serial stdio now supports full line editing.

Build: clean, cppcheck: clean, smoke: 19/19 pass

include/console.h
include/vga_console.h
src/drivers/vga_console.c
src/hal/x86/keyboard.c
src/kernel/console.c
src/kernel/init.c
src/kernel/kconsole.c
src/kernel/main.c

index de201f27449e163d8161e1c85e6a9766a7edb063..7829470895f81e9010f064460b43cd8b935e84d6 100644 (file)
@@ -18,6 +18,7 @@ void kprintf(const char* fmt, ...);
 
 int kgetc(void);
 
+void klog_set_suppress(int suppress);
 size_t klog_read(char* out, size_t out_size);
 
 #endif
index 544c215eab61b782afec1e87595b766b58f0d335..c4d372892e202ebd9c667f5e7e2ba603405f0e82 100644 (file)
@@ -8,5 +8,7 @@ void vga_put_char(char c);
 void vga_print(const char* str);
 void vga_set_color(uint8_t fg, uint8_t bg);
 void vga_clear(void);
+void vga_scroll_back(void);
+void vga_scroll_fwd(void);
 
 #endif
index 69ae7162d91e2ea550ae38856d982a0d2ec0e676..fe1f0800ac068dd08f02492ee826938eba9d20f3 100644 (file)
@@ -5,8 +5,8 @@
 #include "spinlock.h"
 
 static volatile uint16_t* VGA_BUFFER = 0;
-static const int VGA_WIDTH = 80;
-static const int VGA_HEIGHT = 25;
+#define VGA_WIDTH  80
+#define VGA_HEIGHT 25
 
 static int term_col = 0;
 static int term_row = 0;
@@ -14,7 +14,28 @@ static uint8_t term_color = 0x0F; // White on Black
 
 static spinlock_t vga_lock = {0};
 
+/* --- Scrollback buffer --- */
+#define SB_LINES 200
+static uint16_t sb_buf[SB_LINES * VGA_WIDTH];
+static int sb_head  = 0;  /* next write line (circular) */
+static int sb_count = 0;  /* stored lines (max SB_LINES) */
+
+static int view_offset = 0; /* 0 = live view, >0 = scrolled back N lines */
+static uint16_t live_buf[VGA_HEIGHT * VGA_WIDTH]; /* saved live VGA when scrolled */
+
+static void vga_update_hw_cursor(void) {
+    hal_video_set_cursor(term_row, term_col);
+}
+
 static void vga_scroll(void) {
+    /* Save row 0 (about to be lost) into scrollback ring */
+    for (int x = 0; x < VGA_WIDTH; x++) {
+        sb_buf[sb_head * VGA_WIDTH + x] = VGA_BUFFER[x];
+    }
+    sb_head = (sb_head + 1) % SB_LINES;
+    if (sb_count < SB_LINES) sb_count++;
+
+    /* Shift visible content up */
     for (int y = 1; y < VGA_HEIGHT; y++) {
         for (int x = 0; x < VGA_WIDTH; x++) {
             VGA_BUFFER[(y - 1) * VGA_WIDTH + x] = VGA_BUFFER[y * VGA_WIDTH + x];
@@ -26,13 +47,57 @@ static void vga_scroll(void) {
     term_row = VGA_HEIGHT - 1;
 }
 
-static void vga_update_hw_cursor(void) {
-    hal_video_set_cursor(term_row, term_col);
+/* Restore live view if currently scrolled back */
+static void vga_unscroll(void) {
+    if (view_offset > 0) {
+        for (int i = 0; i < VGA_HEIGHT * VGA_WIDTH; i++) {
+            VGA_BUFFER[i] = live_buf[i];
+        }
+        view_offset = 0;
+    }
+}
+
+/* Render scrollback + live content at current view_offset */
+static void render_scrollback_view(void) {
+    for (int y = 0; y < VGA_HEIGHT; y++) {
+        /*
+         * line_from_end: how far this row is from the bottom of live content.
+         * 0..VGA_HEIGHT-1 = live rows, VGA_HEIGHT+ = scrollback
+         */
+        int line_from_end = view_offset + (VGA_HEIGHT - 1 - y);
+
+        if (line_from_end < VGA_HEIGHT) {
+            /* Live content */
+            int live_row = VGA_HEIGHT - 1 - line_from_end;
+            for (int x = 0; x < VGA_WIDTH; x++) {
+                VGA_BUFFER[y * VGA_WIDTH + x] = live_buf[live_row * VGA_WIDTH + x];
+            }
+        } else {
+            /* Scrollback: sb_idx 0 = most recent scrolled-off line */
+            int sb_idx = line_from_end - VGA_HEIGHT;
+            if (sb_idx < sb_count) {
+                int buf_line = (sb_head - 1 - sb_idx + SB_LINES) % SB_LINES;
+                for (int x = 0; x < VGA_WIDTH; x++) {
+                    VGA_BUFFER[y * VGA_WIDTH + x] = sb_buf[buf_line * VGA_WIDTH + x];
+                }
+            } else {
+                /* Beyond scrollback — blank */
+                for (int x = 0; x < VGA_WIDTH; x++) {
+                    VGA_BUFFER[y * VGA_WIDTH + x] = (uint16_t)' ' | (uint16_t)term_color << 8;
+                }
+            }
+        }
+    }
+    /* Hide cursor when scrolled back */
+    hal_video_set_cursor(VGA_HEIGHT, 0);
 }
 
 static void vga_put_char_unlocked(char c) {
     if (!VGA_BUFFER) return;
 
+    /* Any new output auto-returns to live view */
+    vga_unscroll();
+
     switch (c) {
     case '\n':
         term_col = 0;
@@ -131,14 +196,60 @@ void vga_clear(void) {
         return;
     }
 
-    for (int y = 0; y < VGA_HEIGHT; y++) {
-        for (int x = 0; x < VGA_WIDTH; x++) {
-            VGA_BUFFER[y * VGA_WIDTH + x] = (uint16_t)' ' | (uint16_t)term_color << 8;
-        }
+    for (int i = 0; i < VGA_HEIGHT * VGA_WIDTH; i++) {
+        VGA_BUFFER[i] = (uint16_t)' ' | (uint16_t)term_color << 8;
     }
     term_col = 0;
     term_row = 0;
+    view_offset = 0;
+    sb_count = 0;
+    sb_head = 0;
     vga_update_hw_cursor();
 
     spin_unlock_irqrestore(&vga_lock, flags);
 }
+
+void vga_scroll_back(void) {
+    uintptr_t flags = spin_lock_irqsave(&vga_lock);
+
+    if (!VGA_BUFFER || sb_count == 0) {
+        spin_unlock_irqrestore(&vga_lock, flags);
+        return;
+    }
+
+    if (view_offset == 0) {
+        /* First scroll back — save current live screen */
+        for (int i = 0; i < VGA_HEIGHT * VGA_WIDTH; i++) {
+            live_buf[i] = VGA_BUFFER[i];
+        }
+    }
+
+    view_offset += VGA_HEIGHT / 2;
+    if (view_offset > sb_count) view_offset = sb_count;
+
+    render_scrollback_view();
+    spin_unlock_irqrestore(&vga_lock, flags);
+}
+
+void vga_scroll_fwd(void) {
+    uintptr_t flags = spin_lock_irqsave(&vga_lock);
+
+    if (!VGA_BUFFER || view_offset == 0) {
+        spin_unlock_irqrestore(&vga_lock, flags);
+        return;
+    }
+
+    if (view_offset <= VGA_HEIGHT / 2) {
+        /* Return to live view */
+        for (int i = 0; i < VGA_HEIGHT * VGA_WIDTH; i++) {
+            VGA_BUFFER[i] = live_buf[i];
+        }
+        view_offset = 0;
+        vga_update_hw_cursor();
+    } else {
+        view_offset -= VGA_HEIGHT / 2;
+        render_scrollback_view();
+    }
+
+    spin_unlock_irqrestore(&vga_lock, flags);
+}
index 546e1eb525b1910853e0be73bbb9d015a735fc53..11fbe4cefa43959c01618a4f5cb5bd844a664553 100644 (file)
@@ -3,10 +3,17 @@
 #if defined(__i386__)
 #include "arch/x86/idt.h"
 #include "io.h"
+#include "vga_console.h"
 
 static hal_keyboard_char_cb_t g_cb = 0;
 static hal_keyboard_scan_cb_t g_scan_cb = 0;
 
+/* Modifier state */
+static volatile int shift_held = 0;
+
+/* Extended scancode state (0xE0 prefix) */
+static volatile int e0_prefix = 0;
+
 static char scancode_map[128] = {
     0,  27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
     '\t',
@@ -45,25 +52,140 @@ static char scancode_map[128] = {
     0,
 };
 
+static char scancode_map_shift[128] = {
+    0,  27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',
+    '\t',
+    'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
+    0,
+    'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',   0,
+    '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',   0,
+    '*',
+    0,
+    ' ',
+    0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    -1,
+    0,
+    0,
+    0,
+    '+',
+    0,
+    0,
+    0,
+    0,
+    0,
+    0, 0, 0,
+    0,
+    0,
+    0,
+};
+
+/* Emit a VT100 escape sequence through the char callback */
+static void emit_escape_seq(const char* seq) {
+    if (!g_cb) return;
+    for (int i = 0; seq[i]; i++) {
+        g_cb(seq[i]);
+    }
+}
+
+/* PS/2 scan set 1 scancodes */
+#define SC_LSHIFT_PRESS  0x2A
+#define SC_RSHIFT_PRESS  0x36
+#define SC_LSHIFT_REL    0xAA
+#define SC_RSHIFT_REL    0xB6
+
+/* Extended (0xE0-prefixed) scancodes */
+#define SC_E0_UP    0x48
+#define SC_E0_DOWN  0x50
+#define SC_E0_LEFT  0x4B
+#define SC_E0_RIGHT 0x4D
+#define SC_E0_HOME  0x47
+#define SC_E0_END   0x4F
+#define SC_E0_PGUP  0x49
+#define SC_E0_PGDN  0x51
+#define SC_E0_DEL   0x53
+
+static void handle_extended_press(uint8_t sc) {
+    switch (sc) {
+    case SC_E0_UP:    emit_escape_seq("\033[A"); break;
+    case SC_E0_DOWN:  emit_escape_seq("\033[B"); break;
+    case SC_E0_RIGHT: emit_escape_seq("\033[C"); break;
+    case SC_E0_LEFT:  emit_escape_seq("\033[D"); break;
+    case SC_E0_HOME:  emit_escape_seq("\033[H"); break;
+    case SC_E0_END:   emit_escape_seq("\033[F"); break;
+    case SC_E0_PGUP:
+        if (shift_held) {
+            vga_scroll_back();
+        } else {
+            emit_escape_seq("\033[5~");
+        }
+        break;
+    case SC_E0_PGDN:
+        if (shift_held) {
+            vga_scroll_fwd();
+        } else {
+            emit_escape_seq("\033[6~");
+        }
+        break;
+    case SC_E0_DEL:   emit_escape_seq("\033[3~"); break;
+    default: break;
+    }
+}
+
 static void kbd_irq(struct registers* regs) {
     (void)regs;
 
     uint8_t status = inb(0x64);
-    if (status & 0x01) {
-        uint8_t scancode = inb(0x60);
+    if (!(status & 0x01)) return;
 
-        /* Raw scancode callback (key press and release) */
-        if (g_scan_cb) {
-            g_scan_cb(scancode);
-        }
+    uint8_t scancode = inb(0x60);
+
+    /* Raw scancode callback (key press and release) */
+    if (g_scan_cb) {
+        g_scan_cb(scancode);
+    }
+
+    /* 0xE0 prefix: next byte is an extended scancode */
+    if (scancode == 0xE0) {
+        e0_prefix = 1;
+        return;
+    }
 
+    if (e0_prefix) {
+        e0_prefix = 0;
         if (!(scancode & 0x80)) {
-            if (scancode < 128) {
-                char c = scancode_map[scancode];
-                if (c != 0 && g_cb) {
-                    g_cb(c);
-                }
-            }
+            handle_extended_press(scancode);
+        }
+        return;
+    }
+
+    /* Track shift state */
+    if (scancode == SC_LSHIFT_PRESS || scancode == SC_RSHIFT_PRESS) {
+        shift_held = 1;
+        return;
+    }
+    if (scancode == SC_LSHIFT_REL || scancode == SC_RSHIFT_REL) {
+        shift_held = 0;
+        return;
+    }
+
+    /* Ignore key releases for normal keys */
+    if (scancode & 0x80) return;
+
+    if (scancode < 128) {
+        char c = shift_held ? scancode_map_shift[scancode] : scancode_map[scancode];
+        if (c != 0 && g_cb) {
+            g_cb(c);
         }
     }
 }
index 283e0817ac4beac453c01d2368a98541e85dc0fe..10219b4f75d5754ce86feb1093d1c117b7858317 100644 (file)
@@ -21,6 +21,7 @@ static char klog_buf[KLOG_BUF_SIZE];
 static size_t klog_head = 0;   // next write position
 static size_t klog_count = 0;  // total bytes stored (capped at KLOG_BUF_SIZE)
 static spinlock_t klog_lock = {0};
+static volatile int klog_suppress_flag = 0;
 
 static void klog_append(const char* s, size_t len) {
     for (size_t i = 0; i < len; i++) {
@@ -222,7 +223,7 @@ void kprintf(const char* fmt, ...) {
     int len = kvsnprintf(buf, sizeof(buf), fmt, ap);
     va_end(ap);
 
-    if (len > 0) {
+    if (len > 0 && !klog_suppress_flag) {
         size_t slen = (size_t)len;
         if (slen >= sizeof(buf)) slen = sizeof(buf) - 1;
 
@@ -234,6 +235,10 @@ void kprintf(const char* fmt, ...) {
     console_write(buf);
 }
 
+void klog_set_suppress(int suppress) {
+    klog_suppress_flag = suppress ? 1 : 0;
+}
+
 int kgetc(void) {
     for (;;) {
         char c = 0;
index 0c7bcc3c418a8e581d10c2ca350d394f1de2598d..f2c5562d06a938735e2a4bc21a165f70fc00caa6 100644 (file)
@@ -123,7 +123,7 @@ int init_start(const struct boot_info* bi) {
     }
 
     if (!fs_root) {
-        kprintf("[INIT] No root filesystem  cannot start userspace.\n");
+        kprintf("[INIT] No root filesystem -- cannot start userspace.\n");
         return -1;
     }
 
index 7cbc440fe8e3ff9fb581a1004de1fcb8d0a84861..03a301ff774803007f821d5228661222069cc667 100644 (file)
 
 #define KCMD_MAX 128
 
+/* ---- Output helpers ---- */
+
 static void kc_puts(const char* s) {
     console_write(s);
 }
 
+static void kc_putc(char c) {
+    console_put_char(c);
+}
+
+/* ---- Command history ---- */
+
+#define HIST_MAX 16
+static char hist_buf[HIST_MAX][KCMD_MAX];
+static int hist_head  = 0;  /* next write slot */
+static int hist_count = 0;  /* entries stored */
+
+static void hist_add(const char* line) {
+    if (line[0] == '\0') return;
+    /* Don't add duplicates of the last command */
+    if (hist_count > 0) {
+        int prev = (hist_head - 1 + HIST_MAX) % HIST_MAX;
+        if (strcmp(hist_buf[prev], line) == 0) return;
+    }
+    strncpy(hist_buf[hist_head], line, KCMD_MAX - 1);
+    hist_buf[hist_head][KCMD_MAX - 1] = '\0';
+    hist_head = (hist_head + 1) % HIST_MAX;
+    if (hist_count < HIST_MAX) hist_count++;
+}
+
+static const char* hist_get(int idx) {
+    /* idx 0 = most recent, 1 = second most recent, etc. */
+    if (idx < 0 || idx >= hist_count) return 0;
+    int slot = (hist_head - 1 - idx + HIST_MAX) % HIST_MAX;
+    return hist_buf[slot];
+}
+
+/* ---- Line editing helpers ---- */
+
+static void kc_cursor_left(int n) {
+    for (int i = 0; i < n; i++) kc_putc('\b');
+}
+
+static void kc_erase_line(char* buf, int* len, int* cur) {
+    kc_cursor_left(*cur);
+    for (int i = 0; i < *len; i++) kc_putc(' ');
+    kc_cursor_left(*len);
+    *len = 0;
+    *cur = 0;
+    buf[0] = '\0';
+}
+
+static void kc_replace_line(char* buf, int* len, int* cur, const char* text) {
+    kc_erase_line(buf, len, cur);
+    int nlen = (int)strlen(text);
+    if (nlen >= KCMD_MAX) nlen = KCMD_MAX - 1;
+    memcpy(buf, text, (size_t)nlen);
+    buf[nlen] = '\0';
+    *len = nlen;
+    *cur = nlen;
+    for (int i = 0; i < nlen; i++) kc_putc(buf[i]);
+}
+
+/* ---- Readline with VT100 escape parsing ---- */
+
+static int kc_readline(char* buf, int maxlen) {
+    int len = 0;
+    int cur = 0;
+    int hist_nav = -1;    /* -1 = editing current, 0+ = history index */
+    char saved[KCMD_MAX]; /* saved current line when navigating history */
+    saved[0] = '\0';
+
+    enum { ST_NORMAL, ST_ESC, ST_CSI } state = ST_NORMAL;
+    char csi_buf[8];
+    int csi_len = 0;
+
+    buf[0] = '\0';
+
+    for (;;) {
+        int ch = kgetc();
+        if (ch < 0) continue;
+
+        if (state == ST_ESC) {
+            if (ch == '[') {
+                state = ST_CSI;
+                csi_len = 0;
+            } else {
+                state = ST_NORMAL;
+            }
+            continue;
+        }
+
+        if (state == ST_CSI) {
+            if (ch >= '0' && ch <= '9' && csi_len < 7) {
+                csi_buf[csi_len++] = (char)ch;
+                continue;
+            }
+            csi_buf[csi_len] = '\0';
+            state = ST_NORMAL;
+
+            switch (ch) {
+            case 'A': /* Up arrow — previous history */
+                if (hist_nav + 1 < hist_count) {
+                    if (hist_nav == -1) {
+                        memcpy(saved, buf, (size_t)(len + 1));
+                    }
+                    hist_nav++;
+                    kc_replace_line(buf, &len, &cur, hist_get(hist_nav));
+                }
+                break;
+            case 'B': /* Down arrow — next history / restore */
+                if (hist_nav > 0) {
+                    hist_nav--;
+                    kc_replace_line(buf, &len, &cur, hist_get(hist_nav));
+                } else if (hist_nav == 0) {
+                    hist_nav = -1;
+                    kc_replace_line(buf, &len, &cur, saved);
+                }
+                break;
+            case 'C': /* Right arrow */
+                if (cur < len) {
+                    kc_putc(buf[cur]);
+                    cur++;
+                }
+                break;
+            case 'D': /* Left arrow */
+                if (cur > 0) {
+                    cur--;
+                    kc_putc('\b');
+                }
+                break;
+            case 'H': /* Home */
+                kc_cursor_left(cur);
+                cur = 0;
+                break;
+            case 'F': /* End */
+                for (int i = cur; i < len; i++) kc_putc(buf[i]);
+                cur = len;
+                break;
+            case '~': { /* Delete (CSI 3 ~) */
+                int param = 0;
+                for (int i = 0; i < csi_len; i++)
+                    param = param * 10 + (csi_buf[i] - '0');
+                if (param == 3 && cur < len) {
+                    memmove(&buf[cur], &buf[cur + 1], (size_t)(len - cur - 1));
+                    len--;
+                    buf[len] = '\0';
+                    for (int i = cur; i < len; i++) kc_putc(buf[i]);
+                    kc_putc(' ');
+                    kc_cursor_left(len - cur + 1);
+                }
+                break;
+            }
+            default:
+                break;
+            }
+            continue;
+        }
+
+        /* ST_NORMAL */
+        if (ch == 0x1B) {
+            state = ST_ESC;
+            continue;
+        }
+
+        if (ch == '\n' || ch == '\r') {
+            kc_putc('\n');
+            buf[len] = '\0';
+            return len;
+        }
+
+        if (ch == '\b' || ch == 127) {
+            if (cur > 0) {
+                cur--;
+                memmove(&buf[cur], &buf[cur + 1], (size_t)(len - cur - 1));
+                len--;
+                buf[len] = '\0';
+                kc_putc('\b');
+                for (int i = cur; i < len; i++) kc_putc(buf[i]);
+                kc_putc(' ');
+                kc_cursor_left(len - cur + 1);
+            }
+            continue;
+        }
+
+        /* Ctrl-A = Home, Ctrl-E = End, Ctrl-U = kill line, Ctrl-K = kill to end */
+        if (ch == 0x01) { /* Ctrl-A */
+            kc_cursor_left(cur);
+            cur = 0;
+            continue;
+        }
+        if (ch == 0x05) { /* Ctrl-E */
+            for (int i = cur; i < len; i++) kc_putc(buf[i]);
+            cur = len;
+            continue;
+        }
+        if (ch == 0x15) { /* Ctrl-U: kill whole line */
+            kc_erase_line(buf, &len, &cur);
+            continue;
+        }
+        if (ch == 0x0B) { /* Ctrl-K: kill to end of line */
+            for (int i = cur; i < len; i++) kc_putc(' ');
+            kc_cursor_left(len - cur);
+            len = cur;
+            buf[len] = '\0';
+            continue;
+        }
+
+        if (ch < ' ' || ch > '~') continue;
+
+        /* Insert printable character at cursor */
+        if (len >= maxlen - 1) continue;
+        memmove(&buf[cur + 1], &buf[cur], (size_t)(len - cur));
+        buf[cur] = (char)ch;
+        len++;
+        buf[len] = '\0';
+        for (int i = cur; i < len; i++) kc_putc(buf[i]);
+        cur++;
+        kc_cursor_left(len - cur);
+    }
+}
+
+/* ---- Commands ---- */
+
 static void kconsole_help(void) {
     kc_puts("kconsole commands:\n");
     kc_puts("  help        - Show this list\n");
@@ -123,6 +343,8 @@ static void kconsole_exec(const char* cmd) {
     }
 }
 
+/* ---- Main entry ---- */
+
 void kconsole_enter(void) {
     keyboard_set_callback(0);
 
@@ -130,37 +352,17 @@ void kconsole_enter(void) {
     kc_puts("Type 'help' for available commands.\n");
 
     char line[KCMD_MAX];
-    int pos = 0;
 
     for (;;) {
         kc_puts("kconsole> ");
+        kc_readline(line, KCMD_MAX);
 
-        pos = 0;
-        while (pos < KCMD_MAX - 1) {
-            int ch = kgetc();
-            if (ch < 0) continue;
-
-            if (ch == '\n' || ch == '\r') {
-                console_write("\n");
-                break;
-            }
-
-            if (ch == '\b' || ch == 127) {
-                if (pos > 0) {
-                    pos--;
-                    console_write("\b \b");
-                }
-                continue;
-            }
-
-            if (ch < ' ' || ch > '~') continue;
-
-            line[pos++] = (char)ch;
-            char echo[2] = { (char)ch, 0 };
-            console_write(echo);
+        if (line[0] != '\0') {
+            hist_add(line);
         }
-        line[pos] = '\0';
 
+        klog_set_suppress(1);
         kconsole_exec(line);
+        klog_set_suppress(0);
     }
 }
index 94ddf11aa30cce5e701d2ddb39badb4c359e1803..8a9cafcacc4d22d290654c380901fe82ade3ebdf 100644 (file)
@@ -82,8 +82,8 @@ void kernel_main(const struct boot_info* bi) {
     int init_ret = init_start(bi);
     
     if (init_ret < 0) {
-        kprintf("\n[PANIC] Userspace init failed — dropping to emergency console.\n");
-        kprintf("        (type 'help' for commands, 'reboot' to restart)\n\n");
+        console_write("\n[PANIC] Userspace init failed -- dropping to emergency console.\n");
+        console_write("        (type 'help' for commands, 'reboot' to restart)\n\n");
         kconsole_enter();
     }