/* Copyright (c) 2026 Arslaan Pathan This software is licensed under the ARPL. See LICENSE for details. */ #include #include #include #include "font8x8_basic.h" #define KB_DATA_PORT 0x60 #define KB_STATUS_PORT 0x64 #define KB_OBF 0x01 /* check if compiler thinks we are targeting incorrect OS. */ #if defined(__linux__) #error "you are not using a cross-compiler, this is bad. use an elf cross-compiler for ix86 targets, for example, i386-elf-gcc" #endif /* OS only works on 32bit ix86 */ #if !defined(__i386__) #error "this operating system is only supported on ix86 targets. use an elf cross-compiler for ix86 targets, for example, i386-elf-gcc" #endif static int term_col = 0; static int term_row = 0; static uint32_t *g_framebuffer = NULL; static uint32_t g_width = 0; static uint32_t g_height = 0; static uint32_t g_default_color = 0xFFFFFF; static bool kbd_shifted = false; /* what in the dark magic? ok just dont touch one can hope */ struct multiboot_info { uint32_t flags; uint32_t mem_lower; uint32_t mem_upper; uint32_t boot_device; uint32_t cmdline; uint32_t mods_count; uint32_t mods_addr; uint32_t syms[4]; uint32_t mmap_length; uint32_t mmap_addr; uint32_t drives_length; uint32_t drives_addr; uint32_t config_table; uint32_t boot_loader_name; uint32_t apm_table; uint32_t vbe_control_info; uint32_t vbe_mode_info; uint16_t vbe_mode; uint16_t vbe_interface_seg; uint16_t vbe_interface_off; uint16_t vbe_interface_len; uint64_t framebuffer_addr; uint32_t framebuffer_pitch; uint32_t framebuffer_width; uint32_t framebuffer_height; uint8_t framebuffer_bpp; uint8_t framebuffer_type; uint8_t color_info[6]; } __attribute__((packed)); static const char scancode_to_ascii[128] __attribute__((section(".text"))) = { [0x29] = '`', [0x02] = '1', [0x03] = '2', [0x04] = '3', [0x05] = '4', [0x06] = '5', [0x07] = '6', [0x08] = '7', [0x09] = '8', [0x0A] = '9', [0x0B] = '0', [0x0C] = '-', [0x0D] = '=', [0x0E] = '\b', [0x10] = 'q', [0x11] = 'w', [0x12] = 'e', [0x13] = 'r', [0x14] = 't', [0x15] = 'y', [0x16] = 'u', [0x17] = 'i', [0x18] = 'o', [0x19] = 'p', [0x1A] = '[', [0x1B] = ']', [0x1C] = '\n', [0x2B] = '\\', [0x1E] = 'a', [0x1F] = 's', [0x20] = 'd', [0x21] = 'f', [0x22] = 'g', [0x23] = 'h', [0x24] = 'j', [0x25] = 'k', [0x26] = 'l', [0x27] = ';', [0x28] = '\'', [0x2C] = 'z', [0x2D] = 'x', [0x2E] = 'c', [0x2F] = 'v', [0x30] = 'b', [0x31] = 'n', [0x32] = 'm', [0x33] = ',', [0x34] = '.', [0x35] = '/', [0x39] = ' ', }; static const char scancode_to_ascii_shifted[128] __attribute__((section(".text"))) = { [0x29] = '~', [0x02] = '!', [0x03] = '@', [0x04] = '#', [0x05] = '$', [0x06] = '%', [0x07] = '^', [0x08] = '&', [0x09] = '*', [0x0A] = '(', [0x0B] = ')', [0x0C] = '_', [0x0D] = '+', [0x0E] = '\b', [0x10] = 'Q', [0x11] = 'W', [0x12] = 'E', [0x13] = 'R', [0x14] = 'T', [0x15] = 'Y', [0x16] = 'U', [0x17] = 'I', [0x18] = 'O', [0x19] = 'P', [0x1A] = '{', [0x1B] = '}', [0x1C] = '\n', [0x2B] = '|', [0x1E] = 'A', [0x1F] = 'S', [0x20] = 'D', [0x21] = 'F', [0x22] = 'G', [0x23] = 'H', [0x24] = 'J', [0x25] = 'K', [0x26] = 'L', [0x27] = ':', [0x28] = '"', [0x2C] = 'Z', [0x2D] = 'X', [0x2E] = 'C', [0x2F] = 'V', [0x30] = 'B', [0x31] = 'N', [0x32] = 'M', [0x33] = '<', [0x34] = '>', [0x35] = '?', [0x39] = ' ', }; static inline void outb(uint16_t port, uint8_t val) { asm volatile("outb %0, %1" : : "a"(val), "Nd"(port)); } static inline uint8_t inb(uint16_t port) { uint8_t ret; asm volatile("inb %1, %0" : "=a"(ret) : "Nd"(port)); return ret; } uint8_t keyboard_read_scan_code() { while (!(inb(KB_STATUS_PORT) & KB_OBF)); return inb(KB_DATA_PORT); } void draw_char(int x, int y, int c, uint32_t color, uint32_t *framebuffer, uint32_t width) { char *glyph = font8x8_basic[(unsigned char)c]; for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { if (glyph[row] & (1 << col)) { framebuffer[(y + row) * width + (x + col)] = color; } } } } void draw_string(int x, int y, const char *str, uint32_t color, uint32_t *framebuffer, uint32_t width) { while (*str) { draw_char(x, y, *str, color, framebuffer, width); x += 8; str++; } } void term_init(uint32_t *framebuffer, uint32_t width, uint32_t height) { g_framebuffer = framebuffer; g_width = width; g_height = height; term_col = 0; term_row = 0; } void term_set_color(uint32_t color) { g_default_color = color; } void term_printf(const char *str) { while (*str) { if (*str == '\n') { term_col = 0; term_row++; str++; continue; } if ((term_col * 8) >= g_width) { term_col = 0; term_row++; } if ((term_row * 8) >= g_height) { term_row = 0; // just simple wrap around for now, scroll later } draw_char(term_col * 8, term_row * 8, *str, g_default_color, g_framebuffer, g_width); term_col++; str++; } } void shell() { term_set_color(0xFCD24D); term_printf("\n> "); term_set_color(0xFFFFFF); while (1) { uint8_t scancode = keyboard_read_scan_code(); // shift or no shift???? if (scancode == 0x2A) kbd_shifted = true; if (scancode == 0xAA) kbd_shifted = false; // we dont care abt key releases if (scancode & 0x80) continue; char c = kbd_shifted ? scancode_to_ascii_shifted[scancode] : scancode_to_ascii[scancode]; if (c) { if (c == '\n') { // enter, interpret command // we dont know what to do with command yet so new prompt and pretend we did something // we also need to add like handling of the current command, do that soon! term_set_color(0xFCD24D); term_printf("\n> "); term_set_color(0xFFFFFF); } if (c == '\b') { // stupid nut keep trying to use backspace when its not done yet, just add a handler placeholder term_printf("\n---\nyou stupid nut!\n"); term_printf("we dont have backspace yet, stop trying to when testing! get better\n"); term_set_color(0xFCD24D); term_printf("\n> "); term_set_color(0xFFFFFF); } else { term_printf(&c); } } } } void kernel_main(struct multiboot_info *mbi) { // check if framebuffer info is available (bit 12) if (!(mbi->flags & (1 << 12))) { while(1) __asm__("hlt"); } // get the framebuffer uint32_t *framebuffer = (uint32_t*)(uintptr_t)mbi->framebuffer_addr; uint32_t width = mbi->framebuffer_width; uint32_t height = mbi->framebuffer_height; // draw text to the framebuffer term_init(framebuffer, width, height); term_printf("welcome to "); term_set_color(0xFCD24D); term_printf("FrenchToastOS!\n"); term_set_color(0xFFFFFF); term_printf("developed by "); term_set_color(0x967BB6); term_printf("Arslaan Pathan\n"); term_set_color(0xFFFFFF); term_printf("---\n"); term_printf("https://arslaancodes.com\n"); term_printf("\n---\nShell:\n"); shell(); // if theres nothing left to do, halt the cpu or else cooked // our boot.s already does this but better to be safe than sorry while (1) { __asm__ __volatile__ ("hlt"); } }