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

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();