Pridana verze v0.1
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento ovladac realizuje textovou konzoli nad VGA pameti na adrese 0xB8000.
|
||||
* Ridi pozici kurzoru, barvy znaku, zpracovani ridicich znaku
|
||||
* a automaticky scroll, kdyz vystup presahne posledni radek obrazovky.
|
||||
*/
|
||||
|
||||
#include "display.h"
|
||||
|
||||
#define VGA_WIDTH 80
|
||||
#define VGA_HEIGHT 25
|
||||
#define VGA_CONTENT_HEIGHT (VGA_HEIGHT - 1)
|
||||
|
||||
static volatile u16 *const vga = (u16 *)0xB8000;
|
||||
static u8 color = 0x0F;
|
||||
static usize row = 0;
|
||||
static usize col = 0;
|
||||
|
||||
static inline void outb(u16 port, u8 value) {
|
||||
__asm__ volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
static void update_hw_cursor(void) {
|
||||
u16 pos = (u16)(row * VGA_WIDTH + col);
|
||||
|
||||
outb(0x3D4, 0x0F);
|
||||
outb(0x3D5, (u8)(pos & 0xFF));
|
||||
outb(0x3D4, 0x0E);
|
||||
outb(0x3D5, (u8)((pos >> 8) & 0xFF));
|
||||
}
|
||||
|
||||
static inline u16 vga_entry(char c, u8 clr) {
|
||||
return (u16)c | ((u16)clr << 8);
|
||||
}
|
||||
|
||||
static void scroll_if_needed(void) {
|
||||
usize r;
|
||||
usize c;
|
||||
|
||||
if (row < VGA_CONTENT_HEIGHT) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (r = 1; r < VGA_CONTENT_HEIGHT; ++r) {
|
||||
for (c = 0; c < VGA_WIDTH; ++c) {
|
||||
vga[(r - 1) * VGA_WIDTH + c] = vga[r * VGA_WIDTH + c];
|
||||
}
|
||||
}
|
||||
|
||||
for (c = 0; c < VGA_WIDTH; ++c) {
|
||||
vga[(VGA_CONTENT_HEIGHT - 1) * VGA_WIDTH + c] = vga_entry(' ', color);
|
||||
}
|
||||
|
||||
row = VGA_CONTENT_HEIGHT - 1;
|
||||
}
|
||||
|
||||
void display_set_color(u8 fg, u8 bg) {
|
||||
color = (u8)((bg << 4) | (fg & 0x0F));
|
||||
}
|
||||
|
||||
void display_clear(void) {
|
||||
usize i;
|
||||
|
||||
for (i = 0; i < VGA_WIDTH * VGA_HEIGHT; ++i) {
|
||||
vga[i] = vga_entry(' ', color);
|
||||
}
|
||||
|
||||
row = 0;
|
||||
col = 0;
|
||||
update_hw_cursor();
|
||||
}
|
||||
|
||||
void display_init(void) {
|
||||
display_set_color(0x0F, 0x00);
|
||||
display_clear();
|
||||
}
|
||||
|
||||
void display_putc(char c) {
|
||||
if (c == '\n') {
|
||||
col = 0;
|
||||
++row;
|
||||
scroll_if_needed();
|
||||
update_hw_cursor();
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == '\r') {
|
||||
col = 0;
|
||||
update_hw_cursor();
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == '\b') {
|
||||
if (col > 0) {
|
||||
--col;
|
||||
} else if (row > 0) {
|
||||
--row;
|
||||
col = VGA_WIDTH - 1;
|
||||
}
|
||||
|
||||
vga[row * VGA_WIDTH + col] = vga_entry(' ', color);
|
||||
update_hw_cursor();
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == '\t') {
|
||||
col = (col + 4) & ~3ULL;
|
||||
if (col >= VGA_WIDTH) {
|
||||
col = 0;
|
||||
++row;
|
||||
scroll_if_needed();
|
||||
}
|
||||
update_hw_cursor();
|
||||
return;
|
||||
}
|
||||
|
||||
vga[row * VGA_WIDTH + col] = vga_entry(c, color);
|
||||
++col;
|
||||
|
||||
if (col >= VGA_WIDTH) {
|
||||
col = 0;
|
||||
++row;
|
||||
scroll_if_needed();
|
||||
}
|
||||
|
||||
update_hw_cursor();
|
||||
}
|
||||
|
||||
void display_write(const char *text) {
|
||||
while (*text) {
|
||||
display_putc(*text++);
|
||||
}
|
||||
}
|
||||
|
||||
void display_get_cursor(usize *out_row, usize *out_col) {
|
||||
if (out_row) {
|
||||
*out_row = row;
|
||||
}
|
||||
if (out_col) {
|
||||
*out_col = col;
|
||||
}
|
||||
}
|
||||
|
||||
void display_set_cursor(usize new_row, usize new_col) {
|
||||
if (new_row >= VGA_HEIGHT) {
|
||||
new_row = VGA_HEIGHT - 1;
|
||||
}
|
||||
if (new_col >= VGA_WIDTH) {
|
||||
new_col = VGA_WIDTH - 1;
|
||||
}
|
||||
|
||||
row = new_row;
|
||||
col = new_col;
|
||||
update_hw_cursor();
|
||||
}
|
||||
|
||||
void display_fill_row(usize target_row, char ch, u8 fg, u8 bg) {
|
||||
usize c;
|
||||
u8 clr;
|
||||
|
||||
if (target_row >= VGA_HEIGHT) {
|
||||
return;
|
||||
}
|
||||
|
||||
clr = (u8)((bg << 4) | (fg & 0x0F));
|
||||
for (c = 0; c < VGA_WIDTH; ++c) {
|
||||
vga[target_row * VGA_WIDTH + c] = vga_entry(ch, clr);
|
||||
}
|
||||
}
|
||||
|
||||
void display_write_at(usize target_row, usize target_col, const char *text, u8 fg, u8 bg) {
|
||||
usize c = target_col;
|
||||
u8 clr;
|
||||
|
||||
if (!text || target_row >= VGA_HEIGHT || target_col >= VGA_WIDTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
clr = (u8)((bg << 4) | (fg & 0x0F));
|
||||
while (*text && c < VGA_WIDTH) {
|
||||
vga[target_row * VGA_WIDTH + c] = vga_entry(*text++, clr);
|
||||
++c;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento soubor implementuje zakladni klavesnicovy vstup pres PS/2 porty.
|
||||
* Preklada scancode na ASCII znaky a poskytuje funkci pro nacitani radku,
|
||||
* kterou pouziva shell pro prijem prikazu od uzivatele.
|
||||
*/
|
||||
|
||||
#include "display.h"
|
||||
#include "drivers.h"
|
||||
|
||||
#define KBD_HISTORY_MAX 32
|
||||
#define KBD_HISTORY_LINE_MAX 128
|
||||
|
||||
static char g_history[KBD_HISTORY_MAX][KBD_HISTORY_LINE_MAX];
|
||||
static int g_history_count = 0;
|
||||
static int g_history_head = 0;
|
||||
static int g_ctrl_down = 0;
|
||||
|
||||
static inline unsigned char inb(unsigned short port) {
|
||||
unsigned char ret;
|
||||
__asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char keymap[128] = {
|
||||
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t',
|
||||
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's',
|
||||
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v',
|
||||
'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ',
|
||||
};
|
||||
|
||||
void keyboard_init(void) {
|
||||
}
|
||||
|
||||
int keyboard_try_read_key(void) {
|
||||
unsigned char sc;
|
||||
int out = -1;
|
||||
|
||||
if ((inb(0x64) & 1) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sc = inb(0x60);
|
||||
|
||||
if (sc == 0xE0) {
|
||||
unsigned char ext;
|
||||
|
||||
if ((inb(0x64) & 1) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ext = inb(0x60);
|
||||
if (ext == 0x1D) {
|
||||
g_ctrl_down = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ext == 0x9D) {
|
||||
g_ctrl_down = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ext & 0x80) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ext == 0x48) {
|
||||
out = ASTER_KEY_UP;
|
||||
return out;
|
||||
}
|
||||
|
||||
if (ext == 0x50) {
|
||||
out = ASTER_KEY_DOWN;
|
||||
return out;
|
||||
}
|
||||
|
||||
if (ext == 0x4B) {
|
||||
out = ASTER_KEY_LEFT;
|
||||
return out;
|
||||
}
|
||||
|
||||
if (ext == 0x4D) {
|
||||
out = ASTER_KEY_RIGHT;
|
||||
return out;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sc == 0x1D) {
|
||||
g_ctrl_down = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sc == 0x9D) {
|
||||
g_ctrl_down = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sc & 0x80) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sc == 0x3B) {
|
||||
out = ASTER_KEY_F1;
|
||||
return out;
|
||||
}
|
||||
|
||||
if (sc == 0x3C) {
|
||||
out = ASTER_KEY_F2;
|
||||
return out;
|
||||
}
|
||||
|
||||
if (sc == 0x1F && g_ctrl_down) {
|
||||
out = 19; /* Ctrl+S */
|
||||
return out;
|
||||
}
|
||||
|
||||
if (sc < 128 && keymap[sc]) {
|
||||
out = keymap[sc];
|
||||
return out;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int keyboard_read_key(void) {
|
||||
for (;;) {
|
||||
int k = keyboard_try_read_key();
|
||||
if (k != -1) {
|
||||
return k;
|
||||
}
|
||||
|
||||
__asm__ volatile ("pause");
|
||||
}
|
||||
}
|
||||
|
||||
static void clear_current_line(int len) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < len; ++j) {
|
||||
display_putc('\b');
|
||||
}
|
||||
}
|
||||
|
||||
static void set_line_from_history(char *buffer, int *len, int max_len, const char *src) {
|
||||
int j = 0;
|
||||
int limit = max_len - 1;
|
||||
|
||||
clear_current_line(*len);
|
||||
while (src[j] && j < limit) {
|
||||
buffer[j] = src[j];
|
||||
display_putc(src[j]);
|
||||
++j;
|
||||
}
|
||||
|
||||
buffer[j] = '\0';
|
||||
*len = j;
|
||||
}
|
||||
|
||||
static void history_push(const char *line) {
|
||||
int i;
|
||||
int slot;
|
||||
|
||||
if (!line || !line[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_history_count > 0) {
|
||||
int last = (g_history_head + KBD_HISTORY_MAX - 1) % KBD_HISTORY_MAX;
|
||||
if (g_history[last][0] != '\0') {
|
||||
i = 0;
|
||||
while (line[i] && g_history[last][i] && line[i] == g_history[last][i]) {
|
||||
++i;
|
||||
}
|
||||
if (line[i] == '\0' && g_history[last][i] == '\0') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
slot = g_history_head;
|
||||
for (i = 0; i < KBD_HISTORY_LINE_MAX - 1 && line[i]; ++i) {
|
||||
g_history[slot][i] = line[i];
|
||||
}
|
||||
g_history[slot][i] = '\0';
|
||||
|
||||
g_history_head = (g_history_head + 1) % KBD_HISTORY_MAX;
|
||||
if (g_history_count < KBD_HISTORY_MAX) {
|
||||
++g_history_count;
|
||||
}
|
||||
}
|
||||
|
||||
int keyboard_readline(char *buffer, int max_len) {
|
||||
int i = 0;
|
||||
int history_nav = -1;
|
||||
|
||||
if (max_len <= 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
int c = keyboard_read_key();
|
||||
|
||||
if (c == ASTER_KEY_F1 || c == ASTER_KEY_F2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == ASTER_KEY_UP) {
|
||||
if (g_history_count == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (history_nav < g_history_count - 1) {
|
||||
++history_nav;
|
||||
}
|
||||
|
||||
{
|
||||
int idx = (g_history_head + KBD_HISTORY_MAX - 1 - history_nav) % KBD_HISTORY_MAX;
|
||||
set_line_from_history(buffer, &i, max_len, g_history[idx]);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == ASTER_KEY_DOWN) {
|
||||
if (g_history_count == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (history_nav > 0) {
|
||||
--history_nav;
|
||||
{
|
||||
int idx = (g_history_head + KBD_HISTORY_MAX - 1 - history_nav) % KBD_HISTORY_MAX;
|
||||
set_line_from_history(buffer, &i, max_len, g_history[idx]);
|
||||
}
|
||||
} else if (history_nav == 0) {
|
||||
history_nav = -1;
|
||||
clear_current_line(i);
|
||||
i = 0;
|
||||
buffer[0] = '\0';
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\n') {
|
||||
display_putc('\n');
|
||||
buffer[i] = '\0';
|
||||
history_push(buffer);
|
||||
return i;
|
||||
}
|
||||
|
||||
if (c == '\b') {
|
||||
if (i > 0) {
|
||||
--i;
|
||||
display_putc('\b');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i < max_len - 1) {
|
||||
buffer[i++] = (char)c;
|
||||
display_putc((char)c);
|
||||
buffer[i] = '\0';
|
||||
history_nav = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,669 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalas
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento modul implementuje AsterFS nad realnym ATA diskem v QEMU.
|
||||
* Data se nahravaji pri startu a po kazde zmene se synchronizuji
|
||||
* do sektoru na druhem IDE disku, aby pretrvala mezi restarty.
|
||||
*/
|
||||
|
||||
#include "storage.h"
|
||||
#include "string.h"
|
||||
|
||||
#define ATA_IO_BASE 0x1F0
|
||||
#define ATA_CTRL_BASE 0x3F6
|
||||
#define ATA_DRIVE_SLAVE 0xF0
|
||||
|
||||
#define ATA_REG_DATA 0
|
||||
#define ATA_REG_SECCOUNT0 2
|
||||
#define ATA_REG_LBA0 3
|
||||
#define ATA_REG_LBA1 4
|
||||
#define ATA_REG_LBA2 5
|
||||
#define ATA_REG_HDDEVSEL 6
|
||||
#define ATA_REG_COMMAND 7
|
||||
#define ATA_REG_STATUS 7
|
||||
|
||||
#define ATA_CMD_READ_PIO 0x20
|
||||
#define ATA_CMD_WRITE_PIO 0x30
|
||||
#define ATA_CMD_CACHE_FLUSH 0xE7
|
||||
|
||||
#define ATA_SR_BSY 0x80
|
||||
#define ATA_SR_DRQ 0x08
|
||||
#define ATA_SR_ERR 0x01
|
||||
|
||||
#define ASTERFS_MAGIC "ASTERFS1"
|
||||
#define ASTERFS_VERSION 1U
|
||||
|
||||
#define ASTERFS_NODE_BYTES (ASTERFS_NAME_LEN + 1 + 2 + 1 + ASTERFS_DATA_LEN)
|
||||
#define ASTERFS_NODE_SECTORS (((ASTERFS_MAX_FILES * ASTERFS_NODE_BYTES) + 511) / 512)
|
||||
#define ASTERFS_DISK_START_LBA 1U
|
||||
|
||||
typedef struct {
|
||||
char magic[8];
|
||||
u32 version;
|
||||
u32 nodes_used;
|
||||
u32 reserved;
|
||||
u8 pad[512 - 8 - 4 - 4 - 4];
|
||||
} asterfs_superblock_t;
|
||||
|
||||
typedef struct {
|
||||
char name[ASTERFS_NAME_LEN];
|
||||
u8 is_dir;
|
||||
u16 size;
|
||||
u8 reserved;
|
||||
u8 data[ASTERFS_DATA_LEN];
|
||||
} asterfs_disk_node_t;
|
||||
|
||||
static asterfs_node_t nodes[ASTERFS_MAX_FILES];
|
||||
static int nodes_used = 0;
|
||||
static int disk_ready = 0;
|
||||
static u8 g_sector_buffer[512];
|
||||
static u8 g_nodes_blob[ASTERFS_MAX_FILES * ASTERFS_NODE_BYTES];
|
||||
|
||||
static inline void outb(unsigned short port, unsigned char value) {
|
||||
__asm__ volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
static inline unsigned char inb(unsigned short port) {
|
||||
unsigned char ret;
|
||||
__asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void outw(unsigned short port, unsigned short value) {
|
||||
__asm__ volatile ("outw %0, %1" : : "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
static inline unsigned short inw(unsigned short port) {
|
||||
unsigned short ret;
|
||||
__asm__ volatile ("inw %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ata_delay_400ns(void) {
|
||||
(void)inb(ATA_CTRL_BASE);
|
||||
(void)inb(ATA_CTRL_BASE);
|
||||
(void)inb(ATA_CTRL_BASE);
|
||||
(void)inb(ATA_CTRL_BASE);
|
||||
}
|
||||
|
||||
static int ata_wait_not_busy(void) {
|
||||
unsigned int timeout = 200000U;
|
||||
while (timeout-- > 0) {
|
||||
unsigned char s = inb(ATA_IO_BASE + ATA_REG_STATUS);
|
||||
if (s == 0x00 || s == 0xFF) {
|
||||
return -1;
|
||||
}
|
||||
if ((s & ATA_SR_BSY) == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ata_wait_drq(void) {
|
||||
unsigned int timeout = 200000U;
|
||||
while (timeout-- > 0) {
|
||||
unsigned char s = inb(ATA_IO_BASE + ATA_REG_STATUS);
|
||||
if (s == 0x00 || s == 0xFF) {
|
||||
return -1;
|
||||
}
|
||||
if (s & ATA_SR_ERR) {
|
||||
return -1;
|
||||
}
|
||||
if ((s & ATA_SR_BSY) == 0 && (s & ATA_SR_DRQ) != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ata_select_drive_lba(u32 lba) {
|
||||
if (ata_wait_not_busy() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
outb(ATA_IO_BASE + ATA_REG_HDDEVSEL, (unsigned char)(ATA_DRIVE_SLAVE | ((lba >> 24) & 0x0F)));
|
||||
ata_delay_400ns();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ata_probe_slave(void) {
|
||||
unsigned char s;
|
||||
|
||||
outb(ATA_IO_BASE + ATA_REG_HDDEVSEL, ATA_DRIVE_SLAVE);
|
||||
ata_delay_400ns();
|
||||
|
||||
s = inb(ATA_IO_BASE + ATA_REG_STATUS);
|
||||
if (s == 0x00 || s == 0xFF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ata_wait_not_busy();
|
||||
}
|
||||
|
||||
static int ata_read_sector(u32 lba, u8 *buf512) {
|
||||
int i;
|
||||
|
||||
if (!buf512) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ata_select_drive_lba(lba) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
outb(ATA_IO_BASE + ATA_REG_SECCOUNT0, 1);
|
||||
outb(ATA_IO_BASE + ATA_REG_LBA0, (unsigned char)(lba & 0xFF));
|
||||
outb(ATA_IO_BASE + ATA_REG_LBA1, (unsigned char)((lba >> 8) & 0xFF));
|
||||
outb(ATA_IO_BASE + ATA_REG_LBA2, (unsigned char)((lba >> 16) & 0xFF));
|
||||
outb(ATA_IO_BASE + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
|
||||
|
||||
if (ata_wait_drq() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < 256; ++i) {
|
||||
unsigned short w = inw(ATA_IO_BASE + ATA_REG_DATA);
|
||||
buf512[i * 2] = (u8)(w & 0xFF);
|
||||
buf512[i * 2 + 1] = (u8)((w >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ata_write_sector(u32 lba, const u8 *buf512) {
|
||||
int i;
|
||||
|
||||
if (!buf512) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ata_select_drive_lba(lba) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
outb(ATA_IO_BASE + ATA_REG_SECCOUNT0, 1);
|
||||
outb(ATA_IO_BASE + ATA_REG_LBA0, (unsigned char)(lba & 0xFF));
|
||||
outb(ATA_IO_BASE + ATA_REG_LBA1, (unsigned char)((lba >> 8) & 0xFF));
|
||||
outb(ATA_IO_BASE + ATA_REG_LBA2, (unsigned char)((lba >> 16) & 0xFF));
|
||||
outb(ATA_IO_BASE + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
|
||||
|
||||
if (ata_wait_drq() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < 256; ++i) {
|
||||
unsigned short w = (unsigned short)buf512[i * 2] | ((unsigned short)buf512[i * 2 + 1] << 8);
|
||||
outw(ATA_IO_BASE + ATA_REG_DATA, w);
|
||||
}
|
||||
|
||||
outb(ATA_IO_BASE + ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH);
|
||||
if (ata_wait_not_busy() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fs_reset_memory(void) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ASTERFS_MAX_FILES; ++i) {
|
||||
nodes[i].name[0] = '\0';
|
||||
nodes[i].is_dir = 0;
|
||||
nodes[i].size = 0;
|
||||
}
|
||||
|
||||
nodes_used = 0;
|
||||
}
|
||||
|
||||
static void fs_encode_nodes(u8 *out) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ASTERFS_MAX_FILES; ++i) {
|
||||
asterfs_disk_node_t d;
|
||||
usize off = (usize)i * ASTERFS_NODE_BYTES;
|
||||
|
||||
aster_memset(&d, 0, sizeof(d));
|
||||
aster_memcpy(d.name, nodes[i].name, ASTERFS_NAME_LEN);
|
||||
d.is_dir = nodes[i].is_dir;
|
||||
d.size = nodes[i].size;
|
||||
aster_memcpy(d.data, nodes[i].data, ASTERFS_DATA_LEN);
|
||||
|
||||
aster_memcpy(out + off, &d, ASTERFS_NODE_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
static void fs_decode_nodes(const u8 *in) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ASTERFS_MAX_FILES; ++i) {
|
||||
asterfs_disk_node_t d;
|
||||
usize off = (usize)i * ASTERFS_NODE_BYTES;
|
||||
|
||||
aster_memcpy(&d, in + off, ASTERFS_NODE_BYTES);
|
||||
|
||||
aster_memset(nodes[i].name, 0, ASTERFS_NAME_LEN);
|
||||
aster_memcpy(nodes[i].name, d.name, ASTERFS_NAME_LEN);
|
||||
nodes[i].is_dir = d.is_dir;
|
||||
nodes[i].size = d.size;
|
||||
if (nodes[i].size > ASTERFS_DATA_LEN) {
|
||||
nodes[i].size = ASTERFS_DATA_LEN;
|
||||
}
|
||||
aster_memcpy(nodes[i].data, d.data, ASTERFS_DATA_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
static int fs_flush_disk(void) {
|
||||
asterfs_superblock_t super;
|
||||
unsigned int sector;
|
||||
|
||||
if (!disk_ready) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
aster_memset(&super, 0, sizeof(super));
|
||||
aster_memcpy(super.magic, ASTERFS_MAGIC, 8);
|
||||
super.version = ASTERFS_VERSION;
|
||||
super.nodes_used = (u32)nodes_used;
|
||||
|
||||
aster_memset(g_sector_buffer, 0, sizeof(g_sector_buffer));
|
||||
aster_memcpy(g_sector_buffer, &super, sizeof(super));
|
||||
if (ata_write_sector(ASTERFS_DISK_START_LBA, g_sector_buffer) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fs_encode_nodes(g_nodes_blob);
|
||||
for (sector = 0; sector < ASTERFS_NODE_SECTORS; ++sector) {
|
||||
usize off = (usize)sector * 512;
|
||||
aster_memset(g_sector_buffer, 0, sizeof(g_sector_buffer));
|
||||
if (off < sizeof(g_nodes_blob)) {
|
||||
usize left = sizeof(g_nodes_blob) - off;
|
||||
usize chunk = left < 512 ? left : 512;
|
||||
aster_memcpy(g_sector_buffer, g_nodes_blob + off, chunk);
|
||||
}
|
||||
|
||||
if (ata_write_sector(ASTERFS_DISK_START_LBA + 1U + sector, g_sector_buffer) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fs_load_disk(void) {
|
||||
asterfs_superblock_t super;
|
||||
unsigned int sector;
|
||||
|
||||
if (!disk_ready) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ata_read_sector(ASTERFS_DISK_START_LBA, g_sector_buffer) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
aster_memcpy(&super, g_sector_buffer, sizeof(super));
|
||||
|
||||
if (aster_strncmp(super.magic, ASTERFS_MAGIC, 8) != 0 || super.version != ASTERFS_VERSION) {
|
||||
fs_reset_memory();
|
||||
return fs_flush_disk();
|
||||
}
|
||||
|
||||
for (sector = 0; sector < ASTERFS_NODE_SECTORS; ++sector) {
|
||||
usize off = (usize)sector * 512;
|
||||
if (ata_read_sector(ASTERFS_DISK_START_LBA + 1U + sector, g_sector_buffer) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (off < sizeof(g_nodes_blob)) {
|
||||
usize left = sizeof(g_nodes_blob) - off;
|
||||
usize chunk = left < 512 ? left : 512;
|
||||
aster_memcpy(g_nodes_blob + off, g_sector_buffer, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
fs_decode_nodes(g_nodes_blob);
|
||||
if (super.nodes_used > ASTERFS_MAX_FILES) {
|
||||
nodes_used = ASTERFS_MAX_FILES;
|
||||
} else {
|
||||
nodes_used = (int)super.nodes_used;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_root(const char *path) {
|
||||
return path && path[0] == '/' && path[1] == '\0';
|
||||
}
|
||||
|
||||
static int path_parent_exists(const char *path) {
|
||||
int i;
|
||||
int last = -1;
|
||||
char parent[ASTERFS_NAME_LEN];
|
||||
|
||||
if (!path || path[0] != '/') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; path[i] != '\0'; ++i) {
|
||||
if (path[i] == '/') {
|
||||
last = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (last <= 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((usize)last >= ASTERFS_NAME_LEN) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
aster_memcpy(parent, path, (usize)last);
|
||||
parent[last] = '\0';
|
||||
|
||||
for (i = 0; i < nodes_used; ++i) {
|
||||
if (nodes[i].is_dir && aster_strcmp(nodes[i].name, parent) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int path_is_child(const char *parent, const char *path, const char **leaf_start) {
|
||||
usize parent_len;
|
||||
const char *rest;
|
||||
const char *p;
|
||||
|
||||
if (!parent || !path || path[0] != '/') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (is_root(parent)) {
|
||||
if (path[0] != '/' || path[1] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rest = path + 1;
|
||||
p = rest;
|
||||
while (*p && *p != '/') {
|
||||
++p;
|
||||
}
|
||||
|
||||
if (*p != '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*leaf_start = rest;
|
||||
return 1;
|
||||
}
|
||||
|
||||
parent_len = aster_strlen(parent);
|
||||
if (aster_strncmp(path, parent, parent_len) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (path[parent_len] != '/') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rest = path + parent_len + 1;
|
||||
if (*rest == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = rest;
|
||||
while (*p && *p != '/') {
|
||||
++p;
|
||||
}
|
||||
|
||||
if (*p != '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*leaf_start = rest;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void storage_init(void) {
|
||||
asterfs_init();
|
||||
}
|
||||
|
||||
void asterfs_init(void) {
|
||||
fs_reset_memory();
|
||||
|
||||
disk_ready = (ata_probe_slave() == 0);
|
||||
if (!disk_ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fs_load_disk() != 0) {
|
||||
fs_reset_memory();
|
||||
(void)fs_flush_disk();
|
||||
}
|
||||
}
|
||||
|
||||
static int find_node(const char *name) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nodes_used; ++i) {
|
||||
if (aster_strcmp(nodes[i].name, name) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int asterfs_create_file(const char *name) {
|
||||
usize len;
|
||||
|
||||
if (!name || name[0] != '/' || nodes_used >= ASTERFS_MAX_FILES) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!path_parent_exists(name)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (find_node(name) >= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = aster_strlen(name);
|
||||
if (len >= ASTERFS_NAME_LEN) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
aster_memset(nodes[nodes_used].name, 0, ASTERFS_NAME_LEN);
|
||||
aster_memcpy(nodes[nodes_used].name, name, len);
|
||||
nodes[nodes_used].size = 0;
|
||||
nodes[nodes_used].is_dir = 0;
|
||||
++nodes_used;
|
||||
(void)fs_flush_disk();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asterfs_create_dir(const char *name) {
|
||||
usize len;
|
||||
|
||||
if (!name || name[0] != '/' || is_root(name) || nodes_used >= ASTERFS_MAX_FILES) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!path_parent_exists(name)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (find_node(name) >= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = aster_strlen(name);
|
||||
if (len >= ASTERFS_NAME_LEN) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
aster_memset(nodes[nodes_used].name, 0, ASTERFS_NAME_LEN);
|
||||
aster_memcpy(nodes[nodes_used].name, name, len);
|
||||
nodes[nodes_used].size = 0;
|
||||
nodes[nodes_used].is_dir = 1;
|
||||
++nodes_used;
|
||||
(void)fs_flush_disk();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asterfs_remove_file(const char *name) {
|
||||
int idx;
|
||||
int i;
|
||||
|
||||
idx = find_node(name);
|
||||
if (idx < 0 || nodes[idx].is_dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = idx; i < nodes_used - 1; ++i) {
|
||||
nodes[i] = nodes[i + 1];
|
||||
}
|
||||
|
||||
--nodes_used;
|
||||
(void)fs_flush_disk();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asterfs_remove_dir(const char *name) {
|
||||
int idx;
|
||||
int i;
|
||||
const char *leaf;
|
||||
|
||||
if (is_root(name)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
idx = find_node(name);
|
||||
if (idx < 0 || !nodes[idx].is_dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < nodes_used; ++i) {
|
||||
if (i == idx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (path_is_child(name, nodes[i].name, &leaf)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = idx; i < nodes_used - 1; ++i) {
|
||||
nodes[i] = nodes[i + 1];
|
||||
}
|
||||
|
||||
--nodes_used;
|
||||
(void)fs_flush_disk();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asterfs_write_file(const char *name, const u8 *data, u16 len) {
|
||||
int idx;
|
||||
|
||||
if (!name || !data || name[0] != '/') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
idx = find_node(name);
|
||||
if (idx < 0) {
|
||||
if (asterfs_create_file(name) != 0) {
|
||||
return -1;
|
||||
}
|
||||
idx = find_node(name);
|
||||
}
|
||||
|
||||
if (idx < 0 || nodes[idx].is_dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len > ASTERFS_DATA_LEN) {
|
||||
len = ASTERFS_DATA_LEN;
|
||||
}
|
||||
|
||||
aster_memset(nodes[idx].data, 0, ASTERFS_DATA_LEN);
|
||||
aster_memcpy(nodes[idx].data, data, len);
|
||||
nodes[idx].size = len;
|
||||
(void)fs_flush_disk();
|
||||
return len;
|
||||
}
|
||||
|
||||
int asterfs_read_file(const char *name, u8 *out, u16 max_len) {
|
||||
int idx;
|
||||
u16 len;
|
||||
|
||||
if (!name || !out || name[0] != '/') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
idx = find_node(name);
|
||||
if (idx < 0 || nodes[idx].is_dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = nodes[idx].size;
|
||||
if (len > max_len) {
|
||||
len = max_len;
|
||||
}
|
||||
|
||||
aster_memcpy(out, nodes[idx].data, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
void asterfs_list(void (*cb)(const char *name, u8 is_dir, u16 size)) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nodes_used; ++i) {
|
||||
cb(nodes[i].name, nodes[i].is_dir, nodes[i].size);
|
||||
}
|
||||
}
|
||||
|
||||
int asterfs_get_type(const char *name) {
|
||||
int idx;
|
||||
|
||||
if (is_root(name)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
idx = find_node(name);
|
||||
if (idx < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return nodes[idx].is_dir ? 1 : 0;
|
||||
}
|
||||
|
||||
void asterfs_list_dir(const char *path, void (*cb)(const char *name, u8 is_dir, u16 size)) {
|
||||
int i;
|
||||
const char *leaf = 0;
|
||||
char temp[ASTERFS_NAME_LEN];
|
||||
usize n;
|
||||
|
||||
for (i = 0; i < nodes_used; ++i) {
|
||||
if (!path_is_child(path, nodes[i].name, &leaf)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
n = aster_strlen(leaf);
|
||||
if (n >= ASTERFS_NAME_LEN) {
|
||||
n = ASTERFS_NAME_LEN - 1;
|
||||
}
|
||||
|
||||
aster_memcpy(temp, leaf, n);
|
||||
temp[n] = '\0';
|
||||
cb(temp, nodes[i].is_dir, nodes[i].size);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento soubor implementuje ovladac PIT casovace.
|
||||
* Nastavuje frekvenci hardwaroveho casovace, drzi globalni citac tiku
|
||||
* a poskytuje blokujici uspani jadra na zadany pocet milisekund.
|
||||
*/
|
||||
|
||||
#include "timer.h"
|
||||
|
||||
static volatile unsigned long g_ticks = 0;
|
||||
|
||||
static inline unsigned long long read_tsc(void) {
|
||||
unsigned int lo;
|
||||
unsigned int hi;
|
||||
__asm__ volatile ("rdtsc" : "=a"(lo), "=d"(hi));
|
||||
return ((unsigned long long)hi << 32) | (unsigned long long)lo;
|
||||
}
|
||||
|
||||
static int interrupts_enabled(void) {
|
||||
unsigned long long rflags;
|
||||
__asm__ volatile ("pushfq; popq %0" : "=r"(rflags));
|
||||
return (rflags & (1ULL << 9)) != 0;
|
||||
}
|
||||
|
||||
static void busy_delay_ms(unsigned long ms) {
|
||||
unsigned long long start = read_tsc();
|
||||
unsigned long long wait_cycles = (unsigned long long)ms * 1000000ULL;
|
||||
unsigned long long end = start + wait_cycles;
|
||||
|
||||
while (read_tsc() < end) {
|
||||
__asm__ volatile ("pause");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void outb(unsigned short port, unsigned char value) {
|
||||
__asm__ volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
void timer_init(unsigned int hz) {
|
||||
unsigned int divisor;
|
||||
|
||||
if (hz == 0) {
|
||||
hz = 100;
|
||||
}
|
||||
|
||||
divisor = 1193180U / hz;
|
||||
|
||||
outb(0x43, 0x36);
|
||||
outb(0x40, (unsigned char)(divisor & 0xFF));
|
||||
outb(0x40, (unsigned char)((divisor >> 8) & 0xFF));
|
||||
}
|
||||
|
||||
unsigned long timer_ticks(void) {
|
||||
return g_ticks;
|
||||
}
|
||||
|
||||
void timer_tick_advance(void) {
|
||||
++g_ticks;
|
||||
}
|
||||
|
||||
void timer_sleep_ms(unsigned long ms) {
|
||||
if (!interrupts_enabled()) {
|
||||
busy_delay_ms(ms);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long start = timer_ticks();
|
||||
unsigned long wait_ticks = (ms + 9UL) / 10UL;
|
||||
|
||||
if (wait_ticks == 0) {
|
||||
wait_ticks = 1;
|
||||
}
|
||||
|
||||
while ((timer_ticks() - start) < wait_ticks) {
|
||||
__asm__ volatile ("hlt");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user