Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
|
start [2025/12/06 09:33] jango |
start [2025/12/10 10:09] (aktuell) jango |
||
|---|---|---|---|
| Zeile 1: | Zeile 1: | ||
| - | < | ||
| - | // kernel.c | ||
| - | // Minimal " | ||
| - | // You can split this later into vga.c/ | ||
| - | #include < | ||
| - | |||
| - | // -------- types (if you already have uint8/ | ||
| - | typedef unsigned char uint8; | ||
| - | typedef unsigned short uint16; | ||
| - | typedef unsigned int | ||
| - | |||
| - | // -------- port I/O ---------- | ||
| - | static inline void outb(uint16_t port, uint8_t val) { | ||
| - | asm volatile ("outb %b0, %w1" | ||
| - | : | ||
| - | : " | ||
| - | } | ||
| - | |||
| - | static inline uint8_t inb(uint16_t port) { | ||
| - | uint8_t ret; | ||
| - | asm volatile ("inb %w1, %b0" | ||
| - | : " | ||
| - | : " | ||
| - | return ret; | ||
| - | } | ||
| - | |||
| - | static inline void io_wait(void) { | ||
| - | // 0x80 ist klassisch " | ||
| - | asm volatile ("outb %b0, %w1" : : " | ||
| - | } | ||
| - | |||
| - | // -------- very small utils (no libc) ---------- | ||
| - | static void *memset_(void *dst, int v, size_t n) { | ||
| - | uint8 *p = (uint8*)dst; | ||
| - | while (n--) *p++ = (uint8)v; | ||
| - | return dst; | ||
| - | } | ||
| - | static void *memmove_(void *dst, const void *src, size_t n) { | ||
| - | uint8 *d = (uint8*)dst; | ||
| - | const uint8 *s = (const uint8*)src; | ||
| - | if (d == s || n == 0) return dst; | ||
| - | if (d < s) { while (n--) *d++ = *s++; } | ||
| - | else { d += n; s += n; while (n--) *--d = *--s; } | ||
| - | return dst; | ||
| - | } | ||
| - | static size_t strlen_(const char *s) { | ||
| - | size_t n = 0; | ||
| - | while (s && s[n]) n++; | ||
| - | return n; | ||
| - | } | ||
| - | static int isspace_(char c) { return c==' ' || c==' | ||
| - | static int tolower_(int c) { return (c> | ||
| - | static int strcmpi_(const char *a, const char *b) { | ||
| - | while (*a && *b) { | ||
| - | int ca = tolower_(*a), | ||
| - | if (ca != cb) return ca - cb; | ||
| - | a++; b++; | ||
| - | } | ||
| - | return tolower_(*a) - tolower_(*b); | ||
| - | } | ||
| - | |||
| - | // -------- VGA terminal ---------- | ||
| - | #define VGA_ADDRESS | ||
| - | #define VGA_W 80 | ||
| - | #define VGA_H 25 | ||
| - | #define VGA_CRTC_INDEX | ||
| - | #define VGA_CRTC_DATA | ||
| - | |||
| - | enum vga_color { | ||
| - | BLACK=0, BLUE=1, GREEN=2, CYAN=3, RED=4, MAGENTA=5, BROWN=6, LIGHT_GREY=7, | ||
| - | DARK_GREY=8, | ||
| - | LIGHT_MAGENTA=13, | ||
| - | }; | ||
| - | |||
| - | static volatile uint16 *g_vga = (volatile uint16*)VGA_ADDRESS; | ||
| - | static uint8 g_fg = WHITE, g_bg = BLUE; | ||
| - | static uint16 g_cursor = 0; // 0..(VGA_W*VGA_H-1) | ||
| - | |||
| - | static inline uint16 vga_entry(char ch, uint8 fg, uint8 bg) { | ||
| - | uint8 attr = (uint8)((bg << 4) | (fg & 0x0F)); | ||
| - | return (uint16)((uint16)attr << 8) | (uint8)ch; | ||
| - | } | ||
| - | static void vga_set_cursor(uint16 pos) { | ||
| - | outb(VGA_CRTC_INDEX, | ||
| - | outb(VGA_CRTC_DATA, | ||
| - | outb(VGA_CRTC_INDEX, | ||
| - | outb(VGA_CRTC_DATA, | ||
| - | } | ||
| - | static void term_set_color(uint8 fg, uint8 bg) { g_fg = fg; g_bg = bg; } | ||
| - | |||
| - | static void term_clear(void) { | ||
| - | for (uint32 i=0; i < VGA_W*VGA_H; | ||
| - | g_cursor = 0; | ||
| - | vga_set_cursor(g_cursor); | ||
| - | } | ||
| - | |||
| - | static void term_scroll_if_needed(void) { | ||
| - | uint32 row = g_cursor / VGA_W; | ||
| - | if (row < VGA_H) return; | ||
| - | |||
| - | // scroll up 1 row | ||
| - | memmove_((void*)g_vga, | ||
| - | // clear last row | ||
| - | for (uint32 i=(VGA_H-1)*VGA_W; | ||
| - | g_vga[i] = vga_entry(' | ||
| - | |||
| - | g_cursor = (VGA_H-1)*VGA_W; | ||
| - | } | ||
| - | |||
| - | static void term_putc(char c) { | ||
| - | if (c == ' | ||
| - | g_cursor += VGA_W - (g_cursor % VGA_W); | ||
| - | term_scroll_if_needed(); | ||
| - | vga_set_cursor(g_cursor); | ||
| - | return; | ||
| - | } | ||
| - | if (c == ' | ||
| - | |||
| - | g_vga[g_cursor++] = vga_entry(c, | ||
| - | term_scroll_if_needed(); | ||
| - | vga_set_cursor(g_cursor); | ||
| - | } | ||
| - | static void term_write(const char *s) { | ||
| - | for (size_t i=0; s && s[i]; i++) term_putc(s[i]); | ||
| - | } | ||
| - | |||
| - | // backspace within the current line (won't jump to prev line here) | ||
| - | static void term_backspace(void) { | ||
| - | if (g_cursor == 0) return; | ||
| - | // don't backspace over prompt " > " (simple guard: stop at col 2) | ||
| - | uint16 col = g_cursor % VGA_W; | ||
| - | if (col <= 2) return; | ||
| - | |||
| - | g_cursor--; | ||
| - | g_vga[g_cursor] = vga_entry(' | ||
| - | vga_set_cursor(g_cursor); | ||
| - | } | ||
| - | |||
| - | // -------- Keyboard (polling, robust, no busy-wait bugs) ---------- | ||
| - | #define KBD_DATA | ||
| - | #define KBD_STATUS | ||
| - | |||
| - | static inline uint8 kbd_has_data(void) { return (inb(KBD_STATUS) & 0x01); } | ||
| - | static inline uint8 kbd_read_scancode(void) { return inb(KBD_DATA); | ||
| - | |||
| - | // simple typematic repeat filter (like your idea, but safe) | ||
| - | static uint8 key_down[128]; | ||
| - | static uint8 e0_prefix; | ||
| - | |||
| - | // scancode set 1 maps | ||
| - | static const char map_norm[128] = { | ||
| - | [0x02]=' | ||
| - | [0x0C]=' | ||
| - | [0x10]=' | ||
| - | [0x1A]=' | ||
| - | [0x1E]=' | ||
| - | [0x27]=';', | ||
| - | [0x2B]=' | ||
| - | [0x2C]=' | ||
| - | [0x33]=',', | ||
| - | [0x39]=' | ||
| - | }; | ||
| - | |||
| - | static const char map_shift[128] = { | ||
| - | [0x02]=' | ||
| - | [0x0C]=' | ||
| - | [0x10]=' | ||
| - | [0x1A]=' | ||
| - | [0x1E]=' | ||
| - | [0x27]=':', | ||
| - | [0x2B]=' | ||
| - | [0x2C]=' | ||
| - | [0x33]='<', | ||
| - | [0x39]=' | ||
| - | }; | ||
| - | |||
| - | static uint8 kbd_shift; | ||
| - | static uint8 kbd_caps; | ||
| - | |||
| - | static int kbd_get_scancode_once(uint8 *out_sc) { | ||
| - | if (!kbd_has_data()) return 0; | ||
| - | uint8 sc = kbd_read_scancode(); | ||
| - | |||
| - | if (sc == 0xE0) { e0_prefix = 1; return 0; } | ||
| - | |||
| - | // break (release) | ||
| - | if (sc & 0x80) { | ||
| - | uint8 make = sc & 0x7F; | ||
| - | if (!e0_prefix && make < 128) key_down[make] = 0; | ||
| - | // handle shift release | ||
| - | if (!e0_prefix && (make == 0x2A || make == 0x36)) kbd_shift = 0; | ||
| - | e0_prefix = 0; | ||
| - | return 0; | ||
| - | } | ||
| - | |||
| - | // make (press) - ignore typematic repeats | ||
| - | if (!e0_prefix && sc < 128) { | ||
| - | if (key_down[sc]) return 0; | ||
| - | key_down[sc] = 1; | ||
| - | } | ||
| - | |||
| - | e0_prefix = 0; | ||
| - | *out_sc = sc; | ||
| - | return 1; | ||
| - | } | ||
| - | |||
| - | static char kbd_translate(uint8 sc) { | ||
| - | // modifiers | ||
| - | if (sc == 0x2A || sc == 0x36) { kbd_shift = 1; return 0; } // shift | ||
| - | if (sc == 0x3A) { kbd_caps ^= 1; return 0; } // caps lock | ||
| - | |||
| - | // special keys | ||
| - | if (sc == 0x1C) return ' | ||
| - | if (sc == 0x0E) return ' | ||
| - | if (sc == 0x0F) return ' | ||
| - | |||
| - | char c = kbd_shift ? map_shift[sc] : map_norm[sc]; | ||
| - | if (!c) return 0; | ||
| - | |||
| - | // caps only affects letters (and only if shift isn't producing uppercase already) | ||
| - | if (kbd_caps && !kbd_shift && (c >= ' | ||
| - | return c; | ||
| - | } | ||
| - | |||
| - | // blocking getchar (polling). If you have timer IRQs enabled, you can use hlt to reduce CPU. | ||
| - | static char kbd_getchar(void) { | ||
| - | for (;;) { | ||
| - | uint8 sc; | ||
| - | if (kbd_get_scancode_once(& | ||
| - | char c = kbd_translate(sc); | ||
| - | if (c) return c; | ||
| - | } else { | ||
| - | // reduce busy loop a bit | ||
| - | io_wait(); | ||
| - | // OPTIONAL (only if interrupts are enabled): | ||
| - | // asm volatile(" | ||
| - | } | ||
| - | } | ||
| - | } | ||
| - | |||
| - | // -------- tiny shell ---------- | ||
| - | #define LINE_MAX 128 | ||
| - | |||
| - | static void shell_prompt(void) { | ||
| - | term_putc(' | ||
| - | term_write("> | ||
| - | } | ||
| - | |||
| - | static void cmd_help(void) { | ||
| - | term_write(" | ||
| - | term_write(" | ||
| - | term_write(" | ||
| - | term_write(" | ||
| - | term_write(" | ||
| - | term_write(" | ||
| - | } | ||
| - | |||
| - | static int parse_u8_dec(const char *s, uint8 *out) { | ||
| - | if (!s || !*s) return 0; | ||
| - | uint32 v = 0; | ||
| - | for (; *s; s++) { | ||
| - | if (*s < ' | ||
| - | v = v*10u + (uint32)(*s - ' | ||
| - | if (v > 255u) return 0; | ||
| - | } | ||
| - | *out = (uint8)v; | ||
| - | return 1; | ||
| - | } | ||
| - | |||
| - | static void do_reboot(void) { | ||
| - | // try 8042 reboot pulse | ||
| - | outb(0x64, 0xFE); | ||
| - | // fallback: halt | ||
| - | for (;;) asm volatile(" | ||
| - | } | ||
| - | |||
| - | static void shell_exec(char *line) { | ||
| - | // tokenize in-place | ||
| - | char *argv[8] = {0}; | ||
| - | int argc = 0; | ||
| - | |||
| - | // skip leading spaces | ||
| - | while (*line && isspace_(*line)) line++; | ||
| - | |||
| - | while (*line && argc < 8) { | ||
| - | argv[argc++] = line; | ||
| - | while (*line && !isspace_(*line)) line++; | ||
| - | if (!*line) break; | ||
| - | *line++ = 0; | ||
| - | while (*line && isspace_(*line)) line++; | ||
| - | } | ||
| - | if (argc == 0) return; | ||
| - | |||
| - | if (strcmpi_(argv[0], | ||
| - | cmd_help(); | ||
| - | return; | ||
| - | } | ||
| - | if (strcmpi_(argv[0], | ||
| - | term_clear(); | ||
| - | return; | ||
| - | } | ||
| - | if (strcmpi_(argv[0], | ||
| - | if (argc >= 2) { | ||
| - | term_write(argv[1]); | ||
| - | for (int i=2; i<argc; i++) { term_putc(' | ||
| - | } | ||
| - | term_putc(' | ||
| - | return; | ||
| - | } | ||
| - | if (strcmpi_(argv[0], | ||
| - | uint8 fg, bg; | ||
| - | if (argc == 3 && parse_u8_dec(argv[1], | ||
| - | term_set_color(fg, | ||
| - | term_write(" | ||
| - | } else { | ||
| - | term_write(" | ||
| - | } | ||
| - | return; | ||
| - | } | ||
| - | if (strcmpi_(argv[0], | ||
| - | term_write(" | ||
| - | do_reboot(); | ||
| - | return; | ||
| - | } | ||
| - | |||
| - | term_write(" | ||
| - | term_write(argv[0]); | ||
| - | term_putc(' | ||
| - | } | ||
| - | |||
| - | static void shell_run(void) { | ||
| - | char line[LINE_MAX]; | ||
| - | size_t len = 0; | ||
| - | memset_(line, | ||
| - | |||
| - | term_write(" | ||
| - | term_write(" | ||
| - | term_write(" | ||
| - | term_write("> | ||
| - | |||
| - | for (;;) { | ||
| - | char c = kbd_getchar(); | ||
| - | |||
| - | if (c == ' | ||
| - | if (len > 0) { | ||
| - | len--; | ||
| - | line[len] = 0; | ||
| - | term_backspace(); | ||
| - | } | ||
| - | continue; | ||
| - | } | ||
| - | |||
| - | if (c == ' | ||
| - | term_putc(' | ||
| - | shell_exec(line); | ||
| - | memset_(line, | ||
| - | len = 0; | ||
| - | term_write("> | ||
| - | continue; | ||
| - | } | ||
| - | |||
| - | // printable range: keep it simple | ||
| - | if (c >= 32 && c <= 126) { | ||
| - | if (len < LINE_MAX - 1) { | ||
| - | line[len++] = c; | ||
| - | line[len] = 0; | ||
| - | term_putc(c); | ||
| - | } | ||
| - | } | ||
| - | } | ||
| - | } | ||
| - | |||
| - | // -------- entry ---------- | ||
| - | void kernel_entry(void) { | ||
| - | term_set_color(WHITE, | ||
| - | term_clear(); | ||
| - | shell_run(); | ||
| - | } | ||
| - | </ | ||
| Hallo Besucher! Willkommen in diesem kleinen Wiki rund um IT. Vieles ist noch **unvollständig, | Hallo Besucher! Willkommen in diesem kleinen Wiki rund um IT. Vieles ist noch **unvollständig, | ||