mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
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:
parent
22b99e051d
commit
2ca2809904
68 changed files with 2192 additions and 359 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
3
Makefile
3
Makefile
|
|
@ -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
|
||||
|
|
|
|||
BIN
boredos.iso
BIN
boredos.iso
Binary file not shown.
BIN
build/about.o
BIN
build/about.o
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/cmd.o
BIN
build/cmd.o
Binary file not shown.
Binary file not shown.
BIN
build/disk_manager.o
Normal file
BIN
build/disk_manager.o
Normal file
Binary file not shown.
BIN
build/dns.o
BIN
build/dns.o
Binary file not shown.
BIN
build/e1000.o
BIN
build/e1000.o
Binary file not shown.
BIN
build/editor.o
BIN
build/editor.o
Binary file not shown.
BIN
build/explorer.o
BIN
build/explorer.o
Binary file not shown.
BIN
build/fat32.o
BIN
build/fat32.o
Binary file not shown.
BIN
build/graphics.o
BIN
build/graphics.o
Binary file not shown.
BIN
build/http.o
BIN
build/http.o
Binary file not shown.
BIN
build/icmp.o
BIN
build/icmp.o
Binary file not shown.
BIN
build/idt.o
BIN
build/idt.o
Binary file not shown.
Binary file not shown.
BIN
build/main.o
BIN
build/main.o
Binary file not shown.
BIN
build/markdown.o
BIN
build/markdown.o
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
build/network.o
BIN
build/network.o
Binary file not shown.
BIN
build/notepad.o
BIN
build/notepad.o
Binary file not shown.
BIN
build/paint.o
BIN
build/paint.o
Binary file not shown.
BIN
build/pci.o
BIN
build/pci.o
Binary file not shown.
BIN
build/platform.o
BIN
build/platform.o
Binary file not shown.
BIN
build/ps2.o
BIN
build/ps2.o
Binary file not shown.
BIN
build/rtc.o
BIN
build/rtc.o
Binary file not shown.
BIN
build/tcp.o
BIN
build/tcp.o
Binary file not shown.
BIN
build/vm.o
BIN
build/vm.o
Binary file not shown.
BIN
build/wm.o
BIN
build/wm.o
Binary file not shown.
BIN
disk.img
Normal file
BIN
disk.img
Normal file
Binary file not shown.
Binary file not shown.
|
|
@ -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);
|
||||
|
|
|
|||
546
src/kernel/cmd.c
546
src/kernel/cmd.c
|
|
@ -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
38
src/kernel/disk.h
Normal 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
301
src/kernel/disk_manager.c
Normal 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");
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
1487
src/kernel/fat32.c
1487
src/kernel/fat32.c
File diff suppressed because it is too large
Load diff
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue