V1.51/2.5.0

This update adds support for IDE and SATA drives (that are formatted to fat32) this allows for persistent storage over drives and gives the system some use other than just a showcase :D The user can switch between drives in the explorer by clicking on the dropdown and then clicking any drive letter ( A:, B: .. C:) this list dynamically expands on how many drives there are in the system. In the cmd the user just types A:, B:, C: etc to switch to that drive.
This commit is contained in:
Chris 2026-02-18 15:36:31 +01:00
parent 22b99e051d
commit 2ca2809904
68 changed files with 2192 additions and 359 deletions

BIN
.DS_Store vendored

Binary file not shown.

View file

@ -119,4 +119,5 @@ run: $(ISO_IMAGE)
qemu-system-x86_64 -m 2G -serial stdio -cdrom $(ISO_IMAGE) -boot d \
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
-netdev user,id=net0,hostfwd=udp::12345-:12345 -device e1000,netdev=net0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-drive file=disk.img,format=raw

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
build/disk_manager.o Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
disk.img Normal file

Binary file not shown.

Binary file not shown.

View file

@ -14,8 +14,8 @@ static void about_paint(Window *win) {
// Version info
draw_string(offset_x, offset_y + 105, "BoredOS 'Panda'", COLOR_BLACK);
draw_string(offset_x, offset_y + 120, "BoredOS Version 1.50", COLOR_BLACK);
draw_string(offset_x, offset_y + 135, "Kernel Version 2.4.0", COLOR_BLACK);
draw_string(offset_x, offset_y + 120, "BoredOS Version 1.51", COLOR_BLACK);
draw_string(offset_x, offset_y + 135, "Kernel Version 2.5.0", COLOR_BLACK);
// Copyright
draw_string(offset_x, offset_y + 150, "(C) 2026 boreddevnl.", COLOR_BLACK);

View file

@ -6,6 +6,7 @@
#include "notepad.h"
#include "calculator.h"
#include "fat32.h"
#include "disk.h"
#include "cli_apps/cli_apps.h"
#include "licensewr.h"
#include <stddef.h>
@ -21,7 +22,6 @@
#define CMD_ROWS 41
#define LINE_HEIGHT 10
#define CHAR_WIDTH 8
#define PROMPT "> "
#define COLOR_RED 0xFFFF0000
@ -39,6 +39,12 @@ typedef enum {
MODE_PAGER
} CmdMode;
// CMD Window State (per-window context)
typedef struct {
char current_drive;
char current_dir[256];
} CmdState;
// --- State ---
Window win_cmd;
@ -47,6 +53,7 @@ static CharCell screen_buffer[CMD_ROWS][CMD_COLS];
static int cursor_row = 0;
static int cursor_col = 0;
static uint32_t current_color = COLOR_LTGRAY;
static CmdState *cmd_state = NULL; // Will be set in cmd_init
// Pager State
static CmdMode current_mode = MODE_SHELL;
@ -139,8 +146,18 @@ static void cmd_history_add(const char *cmd) {
if (history_len < HISTORY_MAX) history_len++;
}
static void cmd_print_prompt(void) {
char buf[5];
buf[0] = cmd_state ? cmd_state->current_drive : 'A';
buf[1] = ':';
buf[2] = '>';
buf[3] = ' ';
buf[4] = 0;
cmd_write(buf);
}
static void cmd_clear_line_content(void) {
int prompt_len = cmd_strlen(PROMPT);
int prompt_len = 4; // "A:> "
for (int i = prompt_len; i < CMD_COLS; i++) {
screen_buffer[cursor_row][i].c = ' ';
screen_buffer[cursor_row][i].color = current_color;
@ -327,6 +344,204 @@ void pager_set_mode(void) {
}
// Internal LS command to avoid stack overflow in external module
static void cmd_update_dir(const char *path); // Forward declaration
static void internal_cmd_pwd(char *args) {
(void)args;
if (cmd_state) {
char drive_str[3];
drive_str[0] = cmd_state->current_drive;
drive_str[1] = ':';
drive_str[2] = 0;
cmd_write(drive_str);
cmd_write(cmd_state->current_dir);
} else {
char cwd[256];
fat32_get_current_dir(cwd, sizeof(cwd));
cmd_write(cwd);
}
cmd_write("\n");
}
static void internal_cmd_cd(char *args) {
// Handle cd with proper cmd_state context
if (!args || !args[0]) {
// No argument - show current directory
if (cmd_state) {
char drive_str[3];
drive_str[0] = cmd_state->current_drive;
drive_str[1] = ':';
drive_str[2] = 0;
cmd_write(drive_str);
cmd_write(cmd_state->current_dir);
cmd_write("\n");
} else {
char cwd[256];
fat32_get_current_dir(cwd, sizeof(cwd));
cmd_write("Current directory: ");
cmd_write(cwd);
cmd_write("\n");
}
return;
}
// Parse argument (remove trailing spaces/tabs)
char path[256];
int i = 0;
while (args[i] && args[i] != ' ' && args[i] != '\t') {
path[i] = args[i];
i++;
}
path[i] = 0;
// For cmd_state, we need to build and validate the full path
if (cmd_state) {
// Build full path for validation
char full_path[512] = {0};
if (path[1] == ':') {
// Has drive letter
cmd_strcpy(full_path, path);
} else if (path[0] == '/') {
// Absolute path
full_path[0] = cmd_state->current_drive;
full_path[1] = ':';
int j = 2;
int k = 0;
while (path[k] && j < 509) {
full_path[j++] = path[k++];
}
full_path[j] = 0;
} else {
// Relative path - resolve from current directory
full_path[0] = cmd_state->current_drive;
full_path[1] = ':';
int j = 2;
// Copy current directory
const char *dir = cmd_state->current_dir;
while (*dir && j < 509) {
full_path[j++] = *dir++;
}
// Add separator if needed
if (j > 2 && full_path[j-1] != '/') {
full_path[j++] = '/';
}
// Add path argument
int k = 0;
while (path[k] && j < 509) {
full_path[j++] = path[k++];
}
full_path[j] = 0;
}
// Validate directory exists
if (fat32_is_directory(full_path)) {
// Normalize the path to resolve .. and .
char normalized_path[512];
fat32_normalize_path(full_path, normalized_path);
cmd_update_dir(normalized_path);
cmd_write("Changed to: ");
char drive_str[3];
drive_str[0] = cmd_state->current_drive;
drive_str[1] = ':';
drive_str[2] = 0;
cmd_write(drive_str);
cmd_write(cmd_state->current_dir);
cmd_write("\n");
} else {
cmd_write("Error: Cannot change to directory: ");
cmd_write(path);
cmd_write("\n");
}
} else {
// Fallback to global state if no cmd_state
if (fat32_chdir(path)) {
char cwd[256];
fat32_get_current_dir(cwd, sizeof(cwd));
cmd_write("Changed to: ");
cmd_write(cwd);
cmd_write("\n");
} else {
cmd_write("Error: Cannot change to directory: ");
cmd_write(path);
cmd_write("\n");
}
}
}
static void internal_cmd_txtedit(char *args) {
// Parse the file path argument
char filepath[256];
int i = 0;
// Skip leading whitespace
while (args && args[i] && (args[i] == ' ' || args[i] == '\t')) {
i++;
}
// Extract filepath (args now includes drive prefix from cmd_exec_single)
int j = 0;
while (args && args[i] && args[i] != ' ' && args[i] != '\t' && j < 255) {
filepath[j++] = args[i++];
}
filepath[j] = 0;
// If no filepath provided, show usage
if (j == 0) {
cmd_write("Usage: txtedit <filename>\n");
cmd_write("Example: txtedit myfile.txt\n");
cmd_write(" txtedit /document.txt\n");
return;
}
// Normalize the path (filepath already includes drive from cmd_exec_single)
char normalized_path[256];
fat32_normalize_path(filepath, normalized_path);
// Extract drive from normalized path to set it as current temporarily
char drive = 'A';
if (normalized_path[1] == ':') {
drive = normalized_path[0];
if (drive >= 'a' && drive <= 'z') drive -= 32;
}
// Set global drive temporarily to match file's drive
char saved_drive = fat32_get_current_drive();
fat32_change_drive(drive);
// Open the file in the GUI editor
extern void editor_open_file(const char *filename);
extern Window win_editor;
editor_open_file(normalized_path);
// Restore the drive
fat32_change_drive(saved_drive);
// Make editor window visible and focused, bring to front
extern Window win_explorer;
extern Window win_cmd;
extern Window win_notepad;
extern Window win_calculator;
win_editor.visible = true;
win_editor.focused = true;
// Calculate max z_index to bring window to front
int max_z = 0;
if (win_explorer.z_index > max_z) max_z = win_explorer.z_index;
if (win_cmd.z_index > max_z) max_z = win_cmd.z_index;
if (win_notepad.z_index > max_z) max_z = win_notepad.z_index;
if (win_calculator.z_index > max_z) max_z = win_calculator.z_index;
win_editor.z_index = max_z + 1;
cmd_write("Opening: ");
cmd_write(normalized_path);
cmd_write("\n");
}
static void internal_cmd_ls(char *args) {
char path[256];
if (args && *args) {
@ -386,8 +601,8 @@ static const CommandEntry commands[] = {
{"math", cli_cmd_math},
{"MAN", cli_cmd_man},
{"man", cli_cmd_man},
{"TXTEDIT", cli_cmd_txtedit},
{"txtedit", cli_cmd_txtedit},
{"TXTEDIT", internal_cmd_txtedit},
{"txtedit", internal_cmd_txtedit},
{"UPTIME", cli_cmd_uptime},
{"uptime", cli_cmd_uptime},
{"BEEP", cli_cmd_beep},
@ -405,10 +620,10 @@ static const CommandEntry commands[] = {
{"EXIT", cli_cmd_exit},
{"exit", cli_cmd_exit},
// Filesystem Commands
{"CD", cli_cmd_cd},
{"cd", cli_cmd_cd},
{"PWD", cli_cmd_pwd},
{"pwd", cli_cmd_pwd},
{"CD", internal_cmd_cd},
{"cd", internal_cmd_cd},
{"PWD", internal_cmd_pwd},
{"pwd", internal_cmd_pwd},
{"LS", internal_cmd_ls},
{"ls", internal_cmd_ls},
{"MKDIR", cli_cmd_mkdir},
@ -468,6 +683,88 @@ static const CommandEntry commands[] = {
{NULL, NULL}
};
// Helper to build full path with cmd window's drive context
static void cmd_build_full_path(const char *relative_path, char *full_path) {
if (!cmd_state) {
if (relative_path[0]) {
cmd_strcpy(full_path, relative_path);
} else {
full_path[0] = 'A';
full_path[1] = ':';
full_path[2] = '/';
full_path[3] = 0;
}
return;
}
// If path already has drive letter, use it as-is
if (relative_path && relative_path[1] == ':') {
cmd_strcpy(full_path, relative_path);
return;
}
// Build path with cmd_state's drive and directory
int i = 0;
full_path[i++] = cmd_state->current_drive;
full_path[i++] = ':';
// Add current directory
const char *dir = cmd_state->current_dir;
while (*dir && i < 509) {
full_path[i++] = *dir++;
}
// Add path argument
if (relative_path && relative_path[0]) {
if (i > 2 && full_path[i-1] != '/') {
full_path[i++] = '/';
}
const char *p = relative_path;
while (*p && i < 509) {
full_path[i++] = *p++;
}
}
full_path[i] = 0;
}
// Helper to sync cmd window directory after cd
static void cmd_update_dir(const char *path) {
if (!cmd_state || !path) return;
// Extract drive if provided
const char *p = path;
char drive = cmd_state->current_drive;
if (p[0] && p[1] == ':') {
drive = p[0];
if (drive >= 'a' && drive <= 'z') drive -= 32;
p += 2;
}
// Update drive
cmd_state->current_drive = drive;
// Update directory
if (*p) {
// Remove trailing slashes and copy
int len = 0;
while (p[len]) len++;
while (len > 0 && p[len-1] == '/') len--;
if (len == 0) {
cmd_state->current_dir[0] = '/';
cmd_state->current_dir[1] = 0;
} else {
for (int i = 0; i < len && i < 255; i++) {
cmd_state->current_dir[i] = p[i];
}
cmd_state->current_dir[len] = 0;
}
} else {
cmd_state->current_dir[0] = '/';
cmd_state->current_dir[1] = 0;
}
}
static const char* find_pipe(const char* cmd) {
while (*cmd) {
@ -483,8 +780,42 @@ static void cmd_exec_single(char *cmd) {
while (*cmd == ' ') cmd++;
if (!*cmd) return;
// Check for drive switch (e.g. "A:", "B:")
if (cmd[0] && cmd[1] == ':' && cmd[2] == 0) {
char letter = cmd[0];
if (letter >= 'a' && letter <= 'z') letter -= 32;
// Check if drive exists (don't change global, just check)
if (disk_get_by_letter(letter)) {
// Update cmd window's drive, not global
if (cmd_state) {
cmd_state->current_drive = letter;
cmd_state->current_dir[0] = '/';
cmd_state->current_dir[1] = 0;
}
} else {
cmd_write("Invalid drive.\n");
}
return;
}
if (cmd[0] == '.' && cmd[1] == '/') {
char *filename = cmd + 2;
// Build full path with drive context
char full_exec_path[512];
if (cmd_state && cmd_state->current_drive != 'A') {
full_exec_path[0] = cmd_state->current_drive;
full_exec_path[1] = ':';
int i = 2;
const char *p = filename;
while (*p && i < 509) {
full_exec_path[i++] = *p++;
}
full_exec_path[i] = 0;
filename = full_exec_path;
}
FAT32_FileHandle *fh = fat32_open(filename, "r");
if (fh) {
@ -521,6 +852,168 @@ static void cmd_exec_single(char *cmd) {
args++; // Point to start of args
}
// For file system commands, prepend drive context to path args if on different drive
// Build full path with drive letter for commands that take paths
char full_path_arg[512] = {0};
bool is_cd_command = (cmd_strcmp(cmd, "cd") == 0 || cmd_strcmp(cmd, "CD") == 0);
bool is_ls_command = (cmd_strcmp(cmd, "ls") == 0 || cmd_strcmp(cmd, "LS") == 0);
bool is_echo_command = (cmd_strcmp(cmd, "echo") == 0 || cmd_strcmp(cmd, "ECHO") == 0);
if (cmd_state) {
// Check if this is a command that takes a path argument
bool needs_path = (is_ls_command ||
is_cd_command ||
cmd_strcmp(cmd, "mkdir") == 0 || cmd_strcmp(cmd, "MKDIR") == 0 ||
cmd_strcmp(cmd, "rm") == 0 || cmd_strcmp(cmd, "RM") == 0 ||
cmd_strcmp(cmd, "cat") == 0 || cmd_strcmp(cmd, "CAT") == 0 ||
is_echo_command ||
cmd_strcmp(cmd, "cc") == 0 || cmd_strcmp(cmd, "CC") == 0 ||
cmd_strcmp(cmd, "compc") == 0 || cmd_strcmp(cmd, "COMPC") == 0 ||
cmd_strcmp(cmd, "touch") == 0 || cmd_strcmp(cmd, "TOUCH") == 0 ||
cmd_strcmp(cmd, "cp") == 0 || cmd_strcmp(cmd, "CP") == 0 ||
cmd_strcmp(cmd, "mv") == 0 || cmd_strcmp(cmd, "MV") == 0 ||
cmd_strcmp(cmd, "txtedit") == 0 || cmd_strcmp(cmd, "TXTEDIT") == 0 ||
cmd_strcmp(cmd, "tx") == 0 || cmd_strcmp(cmd, "TX") == 0);
if (needs_path) {
if (args && args[0]) {
// For echo with redirection, we need to prepend drive to redirect target too
if (is_echo_command) {
// Find > or >> and prepend drive to the filename after it
char temp_args[512] = {0};
int i = 0;
int j = 0;
bool in_redirect = false;
while (args[i] && j < 509) {
if (args[i] == '>' && args[i+1] == '>') {
// >> redirection
temp_args[j++] = '>';
temp_args[j++] = '>';
i += 2;
while (args[i] == ' ') { temp_args[j++] = ' '; i++; }
// Prepend drive to filename
if (args[i] && args[i+1] != ':') {
temp_args[j++] = cmd_state->current_drive;
temp_args[j++] = ':';
}
in_redirect = true;
} else if (args[i] == '>' && args[i+1] != '>') {
// > redirection
temp_args[j++] = '>';
i++;
while (args[i] == ' ') { temp_args[j++] = ' '; i++; }
// Prepend drive to filename
if (args[i] && args[i+1] != ':') {
temp_args[j++] = cmd_state->current_drive;
temp_args[j++] = ':';
}
in_redirect = true;
} else {
temp_args[j++] = args[i++];
}
}
temp_args[j] = 0;
cmd_strcpy(full_path_arg, temp_args);
args = full_path_arg;
} else if (cmd_strcmp(cmd, "cat") == 0 || cmd_strcmp(cmd, "CAT") == 0 ||
cmd_strcmp(cmd, "cc") == 0 || cmd_strcmp(cmd, "CC") == 0 ||
cmd_strcmp(cmd, "compc") == 0 || cmd_strcmp(cmd, "COMPC") == 0 ||
cmd_strcmp(cmd, "touch") == 0 || cmd_strcmp(cmd, "TOUCH") == 0 ||
cmd_strcmp(cmd, "cp") == 0 || cmd_strcmp(cmd, "CP") == 0 ||
cmd_strcmp(cmd, "mv") == 0 || cmd_strcmp(cmd, "MV") == 0 ||
cmd_strcmp(cmd, "txtedit") == 0 || cmd_strcmp(cmd, "TXTEDIT") == 0 ||
cmd_strcmp(cmd, "tx") == 0 || cmd_strcmp(cmd, "TX") == 0) {
// For cat, cc, compc, touch, cp, mv, txtedit: prepend drive to file arguments if not already present
if (args[1] == ':') {
// Already has drive letter
cmd_strcpy(full_path_arg, args);
} else {
// Add drive letter
full_path_arg[0] = cmd_state->current_drive;
full_path_arg[1] = ':';
int i = 2;
int j = 0;
while (args[j] && i < 509) {
full_path_arg[i++] = args[j++];
}
full_path_arg[i] = 0;
}
args = full_path_arg;
} else if (is_cd_command) {
// For cd: build full path with drive + current directory + relative path
// Check if args starts with drive letter (e.g., "B:" or "A:")
if (args[1] == ':') {
// Has drive letter, use as-is
cmd_strcpy(full_path_arg, args);
} else if (args[0] == '/') {
// Absolute path, just prepend drive
full_path_arg[0] = cmd_state->current_drive;
full_path_arg[1] = ':';
int i = 2;
int j = 0;
while (args[j] && i < 509) {
full_path_arg[i++] = args[j++];
}
full_path_arg[i] = 0;
} else {
// Relative path - need to build from current directory
int i = 0;
full_path_arg[i++] = cmd_state->current_drive;
full_path_arg[i++] = ':';
// Add current directory
const char *dir = cmd_state->current_dir;
while (*dir && i < 509) {
full_path_arg[i++] = *dir++;
}
// Add separator if current dir doesn't end with /
if (i > 2 && full_path_arg[i-1] != '/') {
full_path_arg[i++] = '/';
}
// Add the relative path argument
int j = 0;
while (args[j] && i < 509) {
full_path_arg[i++] = args[j++];
}
full_path_arg[i] = 0;
}
args = full_path_arg;
} else if (args[1] == ':') {
// Already has drive letter
cmd_strcpy(full_path_arg, args);
args = full_path_arg;
} else {
// Add drive letter
full_path_arg[0] = cmd_state->current_drive;
full_path_arg[1] = ':';
int i = 2;
int j = 0;
while (args[j] && i < 509) {
full_path_arg[i++] = args[j++];
}
full_path_arg[i] = 0;
args = full_path_arg;
}
} else if (is_ls_command || is_cd_command) {
// For ls and cd with no args, pass current directory with drive
full_path_arg[0] = cmd_state->current_drive;
full_path_arg[1] = ':';
int i = 2;
const char *dir = cmd_state->current_dir;
while (*dir && i < 509) {
full_path_arg[i++] = *dir++;
}
full_path_arg[i] = 0;
args = full_path_arg;
}
}
}
// Use command dispatch table
for (int i = 0; commands[i].name != NULL; i++) {
if (cmd_strcmp(cmd, commands[i].name) == 0) {
@ -531,12 +1024,19 @@ static void cmd_exec_single(char *cmd) {
// Check for executable in /Apps/
char app_path[256];
char *p = app_path;
int app_idx = 0;
// Add drive letter if on different drive
if (cmd_state && cmd_state->current_drive != 'A') {
app_path[app_idx++] = cmd_state->current_drive;
app_path[app_idx++] = ':';
}
const char *prefix = "/Apps/";
while (*prefix) *p++ = *prefix++;
while (*prefix) app_path[app_idx++] = *prefix++;
char *c = cmd;
while (*c) *p++ = *c++;
*p = 0;
while (*c && app_idx < 255) app_path[app_idx++] = *c++;
app_path[app_idx] = 0;
FAT32_FileHandle *app_fh = fat32_open(app_path, "r");
if (app_fh) {
@ -847,7 +1347,7 @@ static void cmd_key(Window *target, char c) {
if (c == '\n') { // Enter
char cmd_buf[CMD_COLS + 1];
int len = 0;
int prompt_len = cmd_strlen(PROMPT);
int prompt_len = 4;
for (int i = prompt_len; i < CMD_COLS; i++) {
char ch = screen_buffer[cursor_row][i].c;
@ -864,13 +1364,13 @@ static void cmd_key(Window *target, char c) {
cmd_exec(cmd_buf);
cmd_write(PROMPT);
cmd_print_prompt();
} else if (c == 17) { // UP
if (history_len > 0) {
if (history_pos == -1) {
// Save current line
int len = 0;
int prompt_len = cmd_strlen(PROMPT);
int prompt_len = 4;
for (int i = prompt_len; i < CMD_COLS; i++) {
char ch = screen_buffer[cursor_row][i].c;
if (ch == 0) break;
@ -900,7 +1400,7 @@ static void cmd_key(Window *target, char c) {
}
}
} else if (c == 19) { // LEFT
if (cursor_col > (int)cmd_strlen(PROMPT)) {
if (cursor_col > 4) {
cursor_col--;
}
} else if (c == 20) { // RIGHT
@ -908,7 +1408,7 @@ static void cmd_key(Window *target, char c) {
cursor_col++;
}
} else if (c == '\b') { // Backspace
if (cursor_col > (int)cmd_strlen(PROMPT)) {
if (cursor_col > 4) {
cursor_col--;
screen_buffer[cursor_row][cursor_col].c = ' ';
}
@ -928,7 +1428,7 @@ void cmd_reset(void) {
cmd_write_int(msg_count);
cmd_write(" new message(s) run \"msgrc\" to see your new message(s).\n");
}
cmd_write(PROMPT);
cmd_print_prompt();
}
static void create_test_files(void) {
@ -1164,6 +1664,16 @@ void cmd_init(void) {
win_cmd.handle_click = NULL;
win_cmd.handle_right_click = NULL;
// Initialize cmd state (per-window context)
CmdState *state = (CmdState*)kmalloc(sizeof(CmdState));
if (state) {
state->current_drive = 'A';
state->current_dir[0] = '/';
state->current_dir[1] = 0;
win_cmd.data = state;
cmd_state = state; // Set static pointer
}
cmd_reset();
if (!boot_time_init) {

38
src/kernel/disk.h Normal file
View file

@ -0,0 +1,38 @@
#ifndef DISK_H
#define DISK_H
#include <stdint.h>
#include <stdbool.h>
#define SECTOR_SIZE 512
typedef enum {
DISK_TYPE_RAM,
DISK_TYPE_IDE,
DISK_TYPE_SATA,
DISK_TYPE_USB
} DiskType;
typedef struct Disk {
char letter;
DiskType type;
bool is_fat32;
char name[32];
// Function pointers for driver operations
int (*read_sector)(struct Disk *disk, uint32_t sector, uint8_t *buffer);
int (*write_sector)(struct Disk *disk, uint32_t sector, const uint8_t *buffer);
// Private driver data
void *driver_data;
} Disk;
void disk_manager_init(void);
void disk_manager_scan(void); // Scans for new disks
Disk* disk_get_by_letter(char letter);
char disk_get_next_free_letter(void);
void disk_register(Disk *disk);
int disk_get_count(void);
Disk* disk_get_by_index(int index);
#endif

301
src/kernel/disk_manager.c Normal file
View file

@ -0,0 +1,301 @@
#include "disk.h"
#include "pci.h"
#include "memory_manager.h"
#include "io.h"
#include "wm.h"
#include <stddef.h>
#define MAX_DISKS 26
static Disk *disks[MAX_DISKS];
static int disk_count = 0;
// === ATA Definitions ===
#define ATA_PRIMARY_IO 0x1F0
#define ATA_PRIMARY_CTRL 0x3F6
#define ATA_SECONDARY_IO 0x170
#define ATA_SECONDARY_CTRL 0x376
#define ATA_REG_DATA 0x00
#define ATA_REG_ERROR 0x01
#define ATA_REG_FEATURES 0x01
#define ATA_REG_SEC_COUNT0 0x02
#define ATA_REG_LBA0 0x03
#define ATA_REG_LBA1 0x04
#define ATA_REG_LBA2 0x05
#define ATA_REG_HDDEVSEL 0x06
#define ATA_REG_COMMAND 0x07
#define ATA_REG_STATUS 0x07
#define ATA_CMD_READ_PIO 0x20
#define ATA_CMD_WRITE_PIO 0x30
#define ATA_CMD_IDENTIFY 0xEC
#define ATA_SR_BSY 0x80 // Busy
#define ATA_SR_DRDY 0x40 // Drive ready
#define ATA_SR_DF 0x20 // Drive write fault
#define ATA_SR_DSC 0x10 // Drive seek complete
#define ATA_SR_DRQ 0x08 // Data request ready
#define ATA_SR_CORR 0x04 // Corrected data
#define ATA_SR_IDX 0x02 // Index
#define ATA_SR_ERR 0x01 // Error
typedef struct {
uint16_t port_base;
bool slave;
} ATADriverData;
// === Helpers ===
static void dm_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++;
*dest = 0;
}
void disk_register(Disk *disk);
static int ramdisk_read(Disk *disk, uint32_t sector, uint8_t *buffer) {
(void)disk; (void)sector; (void)buffer;
return 0;
}
static int ramdisk_write(Disk *disk, uint32_t sector, const uint8_t *buffer) {
(void)disk; (void)sector; (void)buffer;
return 0;
}
static void ata_wait_bsy(uint16_t port_base) {
while (inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY);
}
static void ata_wait_drq(uint16_t port_base) {
while (!(inb(port_base + ATA_REG_STATUS) & ATA_SR_DRQ));
}
// Returns 1 if drive exists, 0 otherwise
static int ata_identify(uint16_t port_base, bool slave) {
// Select Drive
outb(port_base + ATA_REG_HDDEVSEL, slave ? 0xB0 : 0xA0);
// Zero out sector count and LBA registers
outb(port_base + ATA_REG_SEC_COUNT0, 0);
outb(port_base + ATA_REG_LBA0, 0);
outb(port_base + ATA_REG_LBA1, 0);
outb(port_base + ATA_REG_LBA2, 0);
// Send Identify command
outb(port_base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
// Check if status is 0 (no drive)
uint8_t status = inb(port_base + ATA_REG_STATUS);
if (status == 0) return 0;
// Wait until BSY clears
while (inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) {
// Simple timeout could be added here
status = inb(port_base + ATA_REG_STATUS);
if (status == 0) return 0; // Check again
}
// Check for error
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) {
return 0; // Error, likely not ATA
}
// Wait for DRQ or ERR
while (!(inb(port_base + ATA_REG_STATUS) & (ATA_SR_DRQ | ATA_SR_ERR)));
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) return 0;
// Read 256 words (512 bytes) of identity data
for (int i = 0; i < 256; i++) {
uint16_t data = inw(port_base + ATA_REG_DATA);
(void)data; // We discard identity data for now, just checking presence
}
return 1;
}
static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) {
ATADriverData *data = (ATADriverData*)disk->driver_data;
uint16_t port_base = data->port_base;
bool slave = data->slave;
ata_wait_bsy(port_base);
// Select drive and send highest 4 bits of LBA
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
outb(port_base + ATA_REG_FEATURES, 0x00);
outb(port_base + ATA_REG_SEC_COUNT0, 1);
outb(port_base + ATA_REG_LBA0, (uint8_t)(lba));
outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8));
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
outb(port_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
ata_wait_bsy(port_base);
ata_wait_drq(port_base);
uint16_t *ptr = (uint16_t*)buffer;
for (int i = 0; i < 256; i++) {
ptr[i] = inw(port_base + ATA_REG_DATA);
}
return 0; // Success
}
static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
ATADriverData *data = (ATADriverData*)disk->driver_data;
uint16_t port_base = data->port_base;
bool slave = data->slave;
ata_wait_bsy(port_base);
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
outb(port_base + ATA_REG_FEATURES, 0x00);
outb(port_base + ATA_REG_SEC_COUNT0, 1);
outb(port_base + ATA_REG_LBA0, (uint8_t)(lba));
outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8));
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
outb(port_base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
ata_wait_bsy(port_base);
ata_wait_drq(port_base);
const uint16_t *ptr = (const uint16_t*)buffer;
for (int i = 0; i < 256; i++) {
outw(port_base + ATA_REG_DATA, ptr[i]);
}
// Flush / Sync
outb(port_base + ATA_REG_COMMAND, 0xE7); // Cache Flush
ata_wait_bsy(port_base);
return 0;
}
char disk_get_next_free_letter(void) {
for (int i = 0; i < MAX_DISKS; i++) {
char letter = 'A' + i;
bool used = false;
for (int j = 0; j < disk_count; j++) {
if (disks[j]->letter == letter) {
used = true;
break;
}
}
if (!used) return letter;
}
return 0; // No free letters
}
void disk_register(Disk *disk) {
if (disk_count >= MAX_DISKS) return;
// Ensure letter is unique
if (disk->letter == 0) {
disk->letter = disk_get_next_free_letter();
}
disks[disk_count++] = disk;
}
void disk_manager_init(void) {
for (int i = 0; i < MAX_DISKS; i++) {
disks[i] = NULL;
}
disk_count = 0;
// Register A: (Ramdisk)
Disk *ramdisk = (Disk*)kmalloc(sizeof(Disk));
ramdisk->letter = 'A';
ramdisk->type = DISK_TYPE_RAM;
ramdisk->is_fat32 = true; // Ramdisk is always formatted
dm_strcpy(ramdisk->name, "RAM");
ramdisk->read_sector = ramdisk_read;
ramdisk->write_sector = ramdisk_write;
ramdisk->driver_data = NULL;
disk_register(ramdisk);
}
Disk* disk_get_by_letter(char letter) {
// Uppercase
if (letter >= 'a' && letter <= 'z') letter -= 32;
for (int i = 0; i < disk_count; i++) {
if (disks[i]->letter == letter) {
return disks[i];
}
}
return NULL;
}
int disk_get_count(void) {
return disk_count;
}
Disk* disk_get_by_index(int index) {
if (index < 0 || index >= disk_count) return NULL;
return disks[index];
}
// Check for FAT32 Signature in MBR/VBR
static bool check_fat32_signature(Disk *disk) {
uint8_t *buffer = (uint8_t*)kmalloc(512);
if (!buffer) return false;
// Read Sector 0
if (disk->read_sector(disk, 0, buffer) != 0) {
kfree(buffer);
return false;
}
// Check boot signature 0x55 0xAA at offset 510
if (buffer[510] != 0x55 || buffer[511] != 0xAA) {
kfree(buffer);
return false;
}
kfree(buffer);
return true;
}
static void try_add_ata_drive(uint16_t port, bool slave, const char *name) {
if (ata_identify(port, slave)) {
Disk *new_disk = (Disk*)kmalloc(sizeof(Disk));
if (!new_disk) return;
ATADriverData *data = (ATADriverData*)kmalloc(sizeof(ATADriverData));
data->port_base = port;
data->slave = slave;
new_disk->letter = 0; // Auto-assign
new_disk->type = DISK_TYPE_IDE;
dm_strcpy(new_disk->name, name);
new_disk->read_sector = ata_read_sector;
new_disk->write_sector = ata_write_sector;
new_disk->driver_data = data;
// Check filesystem
if (check_fat32_signature(new_disk)) {
new_disk->is_fat32 = true;
disk_register(new_disk);
} else {
kfree(data);
kfree(new_disk);
}
}
}
void disk_manager_scan(void) {
// Probe Standard ATA Ports
try_add_ata_drive(ATA_PRIMARY_IO, false, "IDE1");
try_add_ata_drive(ATA_PRIMARY_IO, true, "IDE2");
try_add_ata_drive(ATA_SECONDARY_IO, false, "IDE3");
try_add_ata_drive(ATA_SECONDARY_IO, true, "IDE4");
}

View file

@ -5,7 +5,6 @@
#include <stdbool.h>
#include <stddef.h>
// === Text Editor State ===
Window win_editor;
#define EDITOR_MAX_LINES 128
@ -192,13 +191,13 @@ static void editor_insert_char(char ch) {
EditorLine *prev = &lines[cursor_line - 1];
int merge_point = prev->length;
for (int i = 0; i < line->length; i++) {
if (merge_point + i < EDITOR_MAX_LINE_LEN - 1) {
prev->content[merge_point + i] = line->content[i];
}
int i = 0;
while (i < line->length && (merge_point + i) < EDITOR_MAX_LINE_LEN - 1) {
prev->content[merge_point + i] = line->content[i];
i++;
}
prev->content[merge_point + line->length] = 0;
prev->length = merge_point + line->length;
prev->content[merge_point + i] = 0;
prev->length = merge_point + i;
// Shift lines up
for (int i = cursor_line; i < line_count - 1; i++) {
@ -419,13 +418,6 @@ static void editor_paint(Window *win) {
// === Key Handler ===
static void editor_handle_key(Window *win, char c) {
if (c == 'q' || c == 'Q') {
if (file_modified) {
}
win->visible = false;
return;
}
// Arrow keys - UP
if (c == 17) {
if (cursor_line > 0) {

View file

@ -1,6 +1,7 @@
#include "explorer.h"
#include "graphics.h"
#include "fat32.h"
#include "disk.h"
#include "wm.h"
#include "memory_manager.h"
#include "editor.h"
@ -377,8 +378,15 @@ bool explorer_delete_recursive(const char *path) {
for (int k = i + 1; k < len; k++) filename[j++] = path[k];
filename[j] = 0;
// Extract drive from path
char drive_prefix[3] = "A:";
if (path[0] && path[1] == ':') {
drive_prefix[0] = path[0];
}
char dest_path[256];
explorer_strcpy(dest_path, "/RecycleBin/");
explorer_strcpy(dest_path, drive_prefix);
explorer_strcat(dest_path, "/RecycleBin/");
explorer_strcat(dest_path, filename);
// Save origin
@ -946,11 +954,36 @@ static void explorer_paint(Window *win) {
// Fill background
draw_rect(offset_x, offset_y, win->w - 8, win->h - 28, COLOR_LTGRAY);
// Draw path bar
// Draw Drive Button
char drive_label[8];
// Extract drive from the window's current_path instead of using global current_drive
char current_drv = 'A';
if (state->current_path[0] && state->current_path[1] == ':') {
current_drv = state->current_path[0];
} else if (state->current_path[0] && (state->current_path[0] >= 'A' && state->current_path[0] <= 'Z')) {
current_drv = state->current_path[0];
}
drive_label[0] = '[';
drive_label[1] = ' ';
drive_label[2] = current_drv;
drive_label[3] = ':';
drive_label[4] = ' ';
drive_label[5] = 'v';
drive_label[6] = ' ';
drive_label[7] = ']';
// Button at x+4, y+4, w=60
draw_button(win->x + 4, offset_y + 4, 60, 30, "", false);
draw_string(win->x + 12, offset_y + 12, drive_label, COLOR_BLACK);
// Draw path bar (shifted right)
int path_height = 30;
draw_bevel_rect(offset_x + 4, offset_y + 4, win->w - 16, path_height, true);
draw_string(offset_x + 10, offset_y + 10, "Path", COLOR_BLACK);
draw_string(offset_x + 50, offset_y + 10, state->current_path, COLOR_BLACK);
int path_x = offset_x + 64;
int path_w = win->w - 16 - 64;
draw_bevel_rect(path_x, offset_y + 4, path_w, path_height, true);
draw_string(path_x + 6, offset_y + 10, "Path", COLOR_BLACK);
draw_string(path_x + 46, offset_y + 10, state->current_path, COLOR_BLACK);
// Draw dropdown menu button (right-aligned, before back button)
int dropdown_btn_x = win->x + win->w - 90;
@ -1004,6 +1037,39 @@ static void explorer_paint(Window *win) {
graphics_clear_clipping();
}
// Draw Drive Menu if visible
if (state->drive_menu_visible) {
int menu_x = win->x + 4;
int menu_y = offset_y + 34;
int menu_w = 80;
int count = disk_get_count();
int menu_h = count * 25;
draw_rect(menu_x, menu_y, menu_w, menu_h, COLOR_LTGRAY);
draw_bevel_rect(menu_x, menu_y, menu_w, menu_h, true);
for (int i = 0; i < count; i++) {
Disk *d = disk_get_by_index(i);
if (d) {
char buf[16];
buf[0] = d->letter;
buf[1] = ':';
buf[2] = ' ';
// Copy name truncated
int n = 0; while(d->name[n] && n < 10) { buf[3+n] = d->name[n]; n++; }
buf[3+n] = 0;
// Highlight current
if (d->letter == current_drv) {
draw_rect(menu_x + 2, menu_y + i*25 + 2, menu_w - 4, 21, COLOR_BLUE);
draw_string(menu_x + 5, menu_y + i*25 + 6, buf, COLOR_WHITE);
} else {
draw_string(menu_x + 5, menu_y + i*25 + 6, buf, COLOR_BLACK);
}
}
}
}
// Draw dropdown menu if visible
if (state->dropdown_menu_visible) {
int menu_x = dropdown_btn_x;
@ -1323,6 +1389,35 @@ static void explorer_handle_click(Window *win, int x, int y) {
}
}
// Handle Drive Menu Selection
if (state->drive_menu_visible) {
int menu_x = 4; // Window relative
int menu_y = 58; // 24+34
int menu_w = 80;
int count = disk_get_count();
int menu_h = count * 25;
if (x >= menu_x && x < menu_x + menu_w && y >= menu_y && y < menu_y + menu_h) {
int idx = (y - menu_y) / 25;
Disk *d = disk_get_by_index(idx);
if (d) {
// Do not change global drive, just navigate explorer to it
char path[4];
path[0] = d->letter;
path[1] = ':';
path[2] = '/';
path[3] = 0;
explorer_load_directory(win, path);
}
state->drive_menu_visible = false;
return;
}
// Click outside closes menu
state->drive_menu_visible = false;
return;
}
// Handle dropdown menu clicks
if (state->dropdown_menu_visible) {
int dropdown_btn_x = win->w - 90; // Window-relative
@ -1362,12 +1457,21 @@ static void explorer_handle_click(Window *win, int x, int y) {
}
// x, y are already relative to window (0,0 is top-left of window content area)
// Check Drive Button
int button_y = 28;
if (x >= 4 && x < 64 && y >= button_y && y < button_y + 30) {
state->drive_menu_visible = !state->drive_menu_visible;
state->dropdown_menu_visible = false; // Close other menu
return;
}
// Check dropdown menu button
int button_y = 28; // Position from top of window title bar
if (x >= win->w - 90 && x < win->w - 55 &&
y >= button_y && y < button_y + 30) {
// Dropdown menu button clicked
dropdown_menu_toggle(win);
state->drive_menu_visible = false; // Close other menu
return;
}
@ -1514,10 +1618,7 @@ static void explorer_handle_key(Window *win, char c) {
return;
}
if (c == 'q' || c == 'Q') {
win->visible = false;
return;
}
// Close dropdown menu if open with ESC
if (state->dropdown_menu_visible && c == 27) {
@ -1899,12 +2000,15 @@ Window* explorer_create_window(const char *path) {
state->explorer_scroll_row = 0;
state->dialog_state = DIALOG_NONE;
state->dropdown_menu_visible = false;
state->drive_menu_visible = false;
state->file_context_menu_visible = false;
explorer_wins[explorer_win_count++] = win;
wm_add_window(win);
wm_bring_to_front(win);
explorer_load_directory(win, path);
if (explorer_strcmp(path, "/") == 0) explorer_load_directory(win, "A:/");
else explorer_load_directory(win, path);
return win;
}
@ -1924,13 +2028,14 @@ void explorer_init(void) {
win_explorer.handle_right_click = explorer_handle_right_click;
win_explorer.data = state;
state->drive_menu_visible = false;
explorer_wins[explorer_win_count++] = &win_explorer;
explorer_load_directory(&win_explorer, "/");
explorer_load_directory(&win_explorer, "A:/");
}
void explorer_reset(void) {
ExplorerState *state = (ExplorerState*)win_explorer.data;
// Reset explorer to root directory on close/reopen
explorer_load_directory(&win_explorer, "/");
explorer_load_directory(&win_explorer, "A:/");
win_explorer.focused = false;
state->explorer_scroll_row = 0;
}

View file

@ -43,6 +43,7 @@ typedef struct {
// Dropdown menu state
bool dropdown_menu_visible;
bool drive_menu_visible;
// File context menu state
bool file_context_menu_visible;

File diff suppressed because it is too large Load diff

View file

@ -85,6 +85,7 @@ typedef struct {
bool valid; // Is this handle valid?
uint32_t dir_sector; // Sector containing the directory entry
uint32_t dir_offset; // Offset within that sector
char drive; // Drive letter (A, B, ...)
} FAT32_FileHandle;
// Directory Entry Info (for listing)
@ -123,6 +124,8 @@ int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entr
// Working Directory
bool fat32_chdir(const char *path);
void fat32_get_current_dir(char *buffer, int size);
bool fat32_change_drive(char drive);
char fat32_get_current_drive(void);
// Utilities
void fat32_normalize_path(const char *path, char *normalized);

View file

@ -11,6 +11,12 @@ static inline void outw(uint16_t port, uint16_t val) {
asm volatile ("outw %0, %1" : : "a"(val), "Nd"(port));
}
static inline uint16_t inw(uint16_t port) {
uint16_t ret;
asm volatile ("inw %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static inline uint8_t inb(uint16_t port) {
uint8_t ret;
asm volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));

View file

@ -16,6 +16,7 @@
#include "fat32.h"
#include "memory_manager.h"
#include "paint.h"
#include "disk.h"
// --- State ---
static int mx = 400, my = 300; // Mouse Pos
@ -1760,6 +1761,10 @@ void wm_refresh(void) {
}
void wm_init(void) {
disk_manager_init();
disk_manager_scan();
// Drives are now dynamically managed - only real drives are registered
notepad_init();
cmd_init();
calculator_init();