Add disk driver

This commit is contained in:
2025-11-21 22:57:51 +01:00
parent 18092face9
commit 304dda0adf
25 changed files with 1523 additions and 15 deletions

View File

@@ -1,3 +1,69 @@
./scripts/build.sh ./scripts/build.sh
qemu-system-i386 -serial stdio -kernel "build/out/kernel" # Create disk image if it doesn't exist
if [ ! -f hdd.img ]; then
echo "Creating disk image..."
qemu-img create -f raw hdd.img 10M
# Create a proper MBR partition table with one FAT32 partition
# Using fdisk commands
(
echo o # Create new DOS partition table
echo n # New partition
echo p # Primary partition
echo 1 # Partition number 1
echo # Default first sector
echo # Default last sector (use all space)
echo t # Change partition type
echo c # FAT32 LBA
echo a # Make bootable
echo w # Write changes
) | fdisk hdd.img >/dev/null 2>&1
# Format the partition as FAT32
# First, get the partition offset using sfdisk
OFFSET=$(sfdisk -l hdd.img 2>/dev/null | grep 'hdd.img1' | awk '{print $2}')
# Check if OFFSET is empty or non-numeric and set default if needed
if [ -z "$OFFSET" ] || ! [[ "$OFFSET" =~ ^[0-9]+$ ]]; then
OFFSET=2048 # Default first sector for most partition tables
fi
# Create FAT32 filesystem on the partition
echo "Formatting partition as FAT32..."
mkfs.vfat -F 32 -n "TESTDISK" --offset $OFFSET hdd.img
echo "Disk image created with FAT32 partition"
fi
# Get the partition offset for file operations
OFFSET=$(sfdisk -l hdd.img 2>/dev/null | grep 'hdd.img1' | awk '{print $2}')
if [ -z "$OFFSET" ] || ! [[ "$OFFSET" =~ ^[0-9]+$ ]]; then
OFFSET=2048
fi
# Copy test-hdd contents into the disk image
if [ -d "test-hdd" ]; then
echo "Copying test-hdd contents to disk image..."
# Use mcopy to copy files to the FAT32 partition
MTOOLS_SKIP_CHECK=1 mcopy -i hdd.img@@$((OFFSET * 512)) -s test-hdd/* :: || {
echo "Warning: mcopy failed, retrying individual files..."
for file in test-hdd/*; do
if [ -f "$file" ]; then
MTOOLS_SKIP_CHECK=1 mcopy -i hdd.img@@$((OFFSET * 512)) "$file" :: && echo "Copied $(basename "$file")"
fi
done
}
fi
# Create CD-ROM image if it doesn't exist
if [ ! -f cdrom.iso ]; then
mkdir -p /tmp/cdrom_content
echo "Test CD-ROM" > /tmp/cdrom_content/readme.txt
genisoimage -o cdrom.iso -V "TESTCD" -r -J /tmp/cdrom_content 2>/dev/null || touch cdrom.iso
fi
qemu-system-i386 -serial stdio -kernel "build/out/kernel" \
-drive file=hdd.img,if=ide,format=raw,index=0,media=disk \
-drive file=cdrom.iso,if=ide,format=raw,index=1,media=cdrom

View File

@@ -116,6 +116,33 @@ duk_ret_t native_dword_out(duk_context *ctx)
return 0; return 0;
} }
duk_ret_t native_word_in(duk_context *ctx)
{
uint16_t port = (uint16_t)duk_to_uint32(ctx, 0);
uint16_t result;
__asm__ volatile("inw %1, %0"
: "=a"(result)
: "Nd"(port));
duk_push_uint(ctx, (duk_uint_t)result);
return 1;
}
duk_ret_t native_word_out(duk_context *ctx)
{
uint16_t port = (uint16_t)duk_to_uint32(ctx, 0);
// Get the value to write (second argument)
uint16_t value = (uint16_t)duk_to_uint32(ctx, 1);
__asm__ volatile("outw %0, %1"
:
: "a"(value), "Nd"(port));
return 0;
}
// Global context for IRQ handlers // Global context for IRQ handlers
duk_context *global_ctx = NULL; duk_context *global_ctx = NULL;
@@ -229,6 +256,14 @@ void kmain()
duk_push_c_function(ctx, native_dword_out, 2); duk_push_c_function(ctx, native_dword_out, 2);
duk_put_global_string(ctx, "$dwordout"); duk_put_global_string(ctx, "$dwordout");
// Register native dword in function
duk_push_c_function(ctx, native_word_in, 1);
duk_put_global_string(ctx, "$wordin");
// Register native dword out function
duk_push_c_function(ctx, native_word_out, 2);
duk_put_global_string(ctx, "$wordout");
// Register native IRQ registration function // Register native IRQ registration function
duk_push_c_function(ctx, native_irq_register, 2); duk_push_c_function(ctx, native_irq_register, 2);
duk_put_global_string(ctx, "$irqregister"); duk_put_global_string(ctx, "$irqregister");

View File

@@ -26,15 +26,15 @@ SECTIONS
*(COMMON) *(COMMON)
} }
/* Stack grows downward from high memory - place before heap */
. = ALIGN(4096);
__stack_bottom = .;
. = . + 0x100000; /* 1MB stack */
__stack_top = .;
/* Heap for malloc */ /* Heap for malloc */
. = ALIGN(4096); . = ALIGN(4096);
__heap_start = .; __heap_start = .;
. = . + 0x100000; /* 1MB heap */ . = . + 0x10000000; /* 256MB heap */
__heap_end = .; __heap_end = .;
/* Stack grows downward from high memory */
. = ALIGN(4096);
__stack_bottom = .;
. = . + 0x10000; /* 64KB stack */
__stack_top = .;
} }

View File

@@ -1,4 +1,5 @@
export const Keycode = { export const Keycode = {
Enter: 28,
Q: 16, Q: 16,
W: 17, W: 17,
E: 18, E: 18,
@@ -25,9 +26,12 @@ export const Keycode = {
B: 48, B: 48,
N: 49, N: 49,
M: 50, M: 50,
".": 52,
"/": 53,
}; };
export const RKeycode = { export const RKeycode = {
28: "Enter",
16: "Q", 16: "Q",
17: "W", 17: "W",
18: "E", 18: "E",
@@ -54,4 +58,6 @@ export const RKeycode = {
48: "B", 48: "B",
49: "N", 49: "N",
50: "M", 50: "M",
52: ".",
53: "/",
}; };

View File

@@ -0,0 +1,365 @@
import { Logger } from "../../../lib/libstd/logger/logger.kmod";
import { bytein, byteout } from "../../../lib/libts/byte";
import { dwordin } from "../../../lib/libts/dword";
import { wordin } from "../../../lib/libts/word";
import type { AtaDriveDescriptor } from "../../../types/dev/ata/drive";
import type { BlockCache } from "../../../types/utils/block_cache";
import {
blockCache_block,
blockCache_create,
blockCache_init,
} from "../../utils/block_cache";
const KDRIVER_DEV_ATA_PRIMARY = 0x1f0;
const KDRIVER_DEV_ATA_SECONDARY = 0x170;
const KDRIVER_DEV_ATA_PRIMARY_CTL = 0x3f6;
const KDRIVER_DEV_ATA_SECONDARY_CTL = 0x376;
const KDRIVER_DEV_ATA_CTL_nIEN = 0x02;
const KDRIVER_DEV_ATA_MASTER_BIT = 0;
const KDRIVER_DEV_ATA_SLAVE_BIT = 1;
const KDRIVER_DEV_ATA_DRV_HEAD = 6;
const KDRIVER_DEV_ATA_NSECTOR = 2;
const KDRIVER_DEV_ATA_SECTOR = 3;
const KDRIVER_DEV_ATA_LCYL = 4;
const KDRIVER_DEV_ATA_HCYL = 5;
const KDRIVER_DEV_ATA_STATUS = 7;
const KDRIVER_DEV_ATA_COMMAND = 7;
const KDRIVER_DEV_ATA_IDENTIFY = 0xec;
const KDRIVER_DEV_ATA_READ_SECTORS = 0x20;
const KDRIVER_DEV_ATA_STATUS_ERROR = 0x01;
const KDRIVER_DEV_ATA_STATUS_BSY = 0x80;
const KDRIVER_DEV_ATA_STATUS_DRQ = 0x08;
const KDRIVER_DEV_ATA_DATA = 0;
const KDRIVER_DEV_ATA_BLOCK_SIZE = 512;
let drives: AtaDriveDescriptor[] = [];
let cache = blockCache_create();
export function kdriver_dev_ata_init(): void {}
export function kdriver_dev_ata_400ns_delay(controller: number) {
bytein(controller + KDRIVER_DEV_ATA_STATUS);
bytein(controller + KDRIVER_DEV_ATA_STATUS);
bytein(controller + KDRIVER_DEV_ATA_STATUS);
bytein(controller + KDRIVER_DEV_ATA_STATUS);
bytein(controller + KDRIVER_DEV_ATA_STATUS);
bytein(controller + KDRIVER_DEV_ATA_STATUS);
bytein(controller + KDRIVER_DEV_ATA_STATUS);
bytein(controller + KDRIVER_DEV_ATA_STATUS);
bytein(controller + KDRIVER_DEV_ATA_STATUS);
}
function ide_string(info: number[], start: number, size: number): string {
const buffer = new Array(50);
const t = info.slice(start);
for (let i = 0; i < size; ++i) {
const wordIndex = Math.floor(i / 2);
const isHighByte = i % 2 === 1;
buffer[i] = isHighByte ? (t[wordIndex]! >> 8) & 0xff : t[wordIndex]! & 0xff;
}
for (let i = 0; i < size; i += 2) {
const c = buffer[i];
buffer[i] = buffer[i + 1];
buffer[i + 1] = c;
}
let end = size - 1;
while (true) {
const c = buffer[end];
if (c > 32 && c < 127) {
break;
}
if (end == 0) {
break;
}
--end;
}
//buffer[end + 1] = 0;
let target = "";
for (let i = 0; i <= end; i++) {
target += String.fromCharCode(buffer[i]);
}
return target;
}
export function kdriver_dev_ata_identify(drive: AtaDriveDescriptor) {
byteout(
drive.controller + KDRIVER_DEV_ATA_DRV_HEAD,
0xa0 | (drive.slave << 4)
);
kdriver_dev_ata_400ns_delay(drive.controller);
const floatingBus = bytein(drive.controller + KDRIVER_DEV_ATA_STATUS);
if (floatingBus == 0xff) {
return;
}
byteout(drive.controller + KDRIVER_DEV_ATA_COMMAND, KDRIVER_DEV_ATA_IDENTIFY);
kdriver_dev_ata_400ns_delay(drive.controller);
if (bytein(drive.controller + KDRIVER_DEV_ATA_STATUS) == 0) return;
let notAta = false;
let iterations = 0;
while (1) {
const status = bytein(drive.controller + KDRIVER_DEV_ATA_STATUS);
iterations++;
if (status & KDRIVER_DEV_ATA_STATUS_ERROR) {
notAta = true;
break;
}
if (
!(status & KDRIVER_DEV_ATA_STATUS_BSY) &&
status & KDRIVER_DEV_ATA_STATUS_DRQ
)
break;
if (iterations > 100000) {
return;
}
}
drive.atapi = false;
if (notAta) {
const cl = bytein(drive.controller + KDRIVER_DEV_ATA_LCYL);
const ch = bytein(drive.controller + KDRIVER_DEV_ATA_HCYL);
if (cl == 0x14 && ch == 0xeb) drive.atapi = true;
else if (cl == 0x69 && ch == 0x96) drive.atapi = true;
else return;
byteout(drive.controller + KDRIVER_DEV_ATA_COMMAND, 0xa1);
kdriver_dev_ata_400ns_delay(drive.controller);
}
drive.present = true;
const info = new Array(256);
for (let i = 0; i < 256; i++) {
info[i] = wordin(drive.controller + KDRIVER_DEV_ATA_DATA);
}
drive.model = ide_string(info, 27, 40);
drive.serial = ide_string(info, 10, 20);
drive.firmware = ide_string(info, 23, 8);
const sectors = info[0] + 114;
drive.size = sectors * 4096;
Logger.log(
"[ATA] Found drive " +
drive.model +
" (" +
drive.serial +
") FW: " +
drive.firmware +
" Size: " +
drive.size
);
}
export function kdriver_dev_ata_numberOfDrives(): number {
return 4;
}
export function kdriver_dev_ata_getDrive(n: number) {
return drives[n]!;
}
export function kdriver_dev_ata_detectDisks(): void {
blockCache_init(cache, KDRIVER_DEV_ATA_BLOCK_SIZE, 256);
drives = new Array<AtaDriveDescriptor>(4);
drives[0] = {
controller: KDRIVER_DEV_ATA_PRIMARY,
drive: 0xe0,
present: false,
slave: KDRIVER_DEV_ATA_MASTER_BIT,
atapi: false,
model: "",
serial: "",
firmware: "",
size: 0,
};
drives[1] = {
controller: KDRIVER_DEV_ATA_PRIMARY,
drive: 0xf0,
present: false,
slave: KDRIVER_DEV_ATA_SLAVE_BIT,
atapi: false,
model: "",
serial: "",
firmware: "",
size: 0,
};
drives[2] = {
controller: KDRIVER_DEV_ATA_SECONDARY,
drive: 0xe0,
present: false,
slave: KDRIVER_DEV_ATA_MASTER_BIT,
atapi: false,
model: "",
serial: "",
firmware: "",
size: 0,
};
drives[3] = {
controller: KDRIVER_DEV_ATA_SECONDARY,
drive: 0xf0,
present: false,
slave: KDRIVER_DEV_ATA_SLAVE_BIT,
atapi: false,
model: "",
serial: "",
firmware: "",
size: 0,
};
byteout(KDRIVER_DEV_ATA_PRIMARY_CTL, KDRIVER_DEV_ATA_CTL_nIEN);
byteout(KDRIVER_DEV_ATA_SECONDARY_CTL, KDRIVER_DEV_ATA_CTL_nIEN);
for (let i = 0; i < 4; i++) {
const drive = drives[i]!;
kdriver_dev_ata_identify(drive);
}
byteout(KDRIVER_DEV_ATA_PRIMARY_CTL, 0);
byteout(KDRIVER_DEV_ATA_SECONDARY_CTL, 0);
// TODO: INTERRUPTS
}
export function kdriver_dev_ata_readSectors(
descriptor: AtaDriveDescriptor,
start: number,
count: number,
read: number
) {
let buffer: number[] = [];
for (let i = 0; i < count; ++i) {
const sectorNum = start + i;
const valid = { valid: false };
const block = blockCache_block(
cache,
(descriptor.controller << 8) + descriptor.drive,
sectorNum,
valid
);
if (!valid.valid) {
// Need to actually read from disk
byteout(
descriptor.controller + KDRIVER_DEV_ATA_DRV_HEAD,
0xe0 | (descriptor.slave << 4) | ((sectorNum >> 24) & 0x0f)
);
byteout(descriptor.controller + KDRIVER_DEV_ATA_NSECTOR, 1);
byteout(descriptor.controller + KDRIVER_DEV_ATA_SECTOR, sectorNum & 0xff);
byteout(
descriptor.controller + KDRIVER_DEV_ATA_LCYL,
(sectorNum >> 8) & 0xff
);
byteout(
descriptor.controller + KDRIVER_DEV_ATA_HCYL,
(sectorNum >> 16) & 0xff
);
byteout(
descriptor.controller + KDRIVER_DEV_ATA_COMMAND,
KDRIVER_DEV_ATA_READ_SECTORS
);
// Wait for drive to be ready
let timeout = 0;
while (timeout < 100000) {
const status = bytein(descriptor.controller + KDRIVER_DEV_ATA_STATUS);
if (
!(status & KDRIVER_DEV_ATA_STATUS_BSY) &&
status & KDRIVER_DEV_ATA_STATUS_DRQ
) {
break;
}
timeout++;
}
// Read 256 words (512 bytes)
for (let j = 0; j < 256; j++) {
const word = wordin(descriptor.controller + KDRIVER_DEV_ATA_DATA);
block[j * 2] = word & 0xff;
block[j * 2 + 1] = (word >> 8) & 0xff;
}
}
// Copy the block to the output buffer
for (let n = 0; n < block.length; n++) {
buffer.push(block[n]!);
}
read += 512;
}
return buffer;
}
export function kdriver_dev_ata_fsDriver_read(
data: AtaDriveDescriptor,
count: number,
offset: number
) {
if (count % KDRIVER_DEV_ATA_BLOCK_SIZE != 0) throw new Error("Invalid count");
if (offset % KDRIVER_DEV_ATA_BLOCK_SIZE != 0)
throw new Error("Invalid offset");
const sectors = count / KDRIVER_DEV_ATA_BLOCK_SIZE;
const start = offset / KDRIVER_DEV_ATA_BLOCK_SIZE;
const buffer = kdriver_dev_ata_readSectors(data, start, sectors, 0);
return buffer;
}
export function kdriver_dev_ata_partition_fsDriver_read(
data: any,
count: number,
offset: number
) {
if (count % KDRIVER_DEV_ATA_BLOCK_SIZE != 0) throw new Error("Invalid count");
if (offset % KDRIVER_DEV_ATA_BLOCK_SIZE != 0)
throw new Error("Invalid offset");
const partition = data;
const disk = partition.disk as { descriptor: AtaDriveDescriptor };
const sectors = count / KDRIVER_DEV_ATA_BLOCK_SIZE;
const start = partition.lbaStart + offset / KDRIVER_DEV_ATA_BLOCK_SIZE;
const buffer = kdriver_dev_ata_readSectors(
disk.descriptor,
start,
sectors,
0
);
return buffer;
}
export const kdriver_dev_ata_fsDriver = {
read: kdriver_dev_ata_fsDriver_read,
};
export const kdriver_dev_ata_partition_fsDriver = {
read: kdriver_dev_ata_partition_fsDriver_read,
};

View File

@@ -11,6 +11,8 @@ export function kdriver_dev_keyboard_handleInterrupt(): void {
| keyof typeof Keycode | keyof typeof Keycode
| undefined; | undefined;
//Logger.log(String(scancode));
if (key == undefined) return; if (key == undefined) return;
kmod_terminal_input_handleKeyboardInput(key); kmod_terminal_input_handleKeyboardInput(key);

View File

@@ -0,0 +1,151 @@
import { Path } from "../../lib/libstd/path";
import type { AtaDriveDescriptor } from "../../types/dev/ata/drive";
import type { DeviceType } from "../../types/dev/device";
import type {
KFilesystemDevice,
KFilesystemDirectReadResponse,
KFilesystemDriver,
KFilesystemEntity,
KFilesystemMemdata,
KFilesystemMemdev,
KFilesystemStat,
} from "../../types/filesystem/fs";
const devfs_data: KFilesystemMemdev = [];
export function devfs_driver(): KFilesystemDriver {
return {
id: "devfs",
init: devfs_init,
listDir(path: string): (KFilesystemEntity | KFilesystemDevice)[] | null {
return devfs_listDir(path);
},
readFile(path: string): unknown {
throw new Error("Cannot read files in devfs.");
},
writeFile(path: string, content: unknown): void {
throw new Error("Cannot write files in devfs.");
},
createFile(path: string, content: unknown): void {
throw new Error("Cannot create files in devfs.");
},
mkdir(path: string): void {
throw new Error("Cannot make directories in devfs.");
},
stat(path: string): KFilesystemStat | null {
return devfs_statEntity(path);
},
};
}
export function devfs_init(): void {
devfs_data.push({
name: "root",
path: "/",
type: "folder",
size: 0,
contents: null,
});
}
export function devfs_getEntity(
path: string
): KFilesystemDevice | KFilesystemEntity | null {
for (let i = 0; i < devfs_data.length; i++) {
const entity = devfs_data[i]!;
if (entity.path === path) return entity;
}
return null;
}
export function devfs_statEntity(path: string): KFilesystemStat | null {
const entity = devfs_getEntity(path);
if (!entity) return null;
return {
name: entity.name,
type: entity.type,
size: entity.size,
};
}
export function devfs_mkdir(path: string): void {
if (devfs_getEntity(path)) return;
const parent = Path.dirname(path);
if (!devfs_getEntity(parent)) return;
devfs_data.push({
name: Path.filename(path),
path,
type: "folder",
size: 0,
contents: null,
});
}
export function devfs_listDir(
path: string
): (KFilesystemEntity | KFilesystemDevice)[] | null {
const entity = devfs_getEntity(path);
if (!entity) return null;
if (entity.type !== "folder") return null;
const contents: (KFilesystemEntity | KFilesystemDevice)[] = [];
for (let i = 0; i < devfs_data.length; i++) {
const e = devfs_data[i]!;
if (Path.dirname(e.path) === path) {
contents.push(e);
}
}
return contents;
}
export function devfs_registerDevice(
path: string,
type: DeviceType,
driver: unknown,
data: unknown
) {
if (devfs_getEntity(path)) return;
const parent = Path.dirname(path);
if (!devfs_getEntity(parent)) return;
devfs_data.push({
name: Path.filename(path),
path,
type: "file",
size: 0,
contents: "",
dtype: type,
driver: driver,
data: data,
});
}
export function devfs_getDevice(path: string): KFilesystemDevice | null {
const e = devfs_getEntity(path);
if (!e) return null;
if (!("driver" in e)) return null;
return e;
}
export function devfs_directRead(
path: string,
count: number,
offset: number
): KFilesystemDirectReadResponse | null {
const device = devfs_getDevice(path);
if (!device) return null;
//return (device.driver as any).read(device.data, count, offset);
return [];
}

View File

@@ -0,0 +1,367 @@
import { Logger } from "../../lib/libstd/logger/logger.kmod";
import type {
KFilesystemDriver,
KFilesystemEntity,
KFilesystemStat,
} from "../../types/filesystem/fs";
interface Fat32BootSector {
bytesPerSector: number;
sectorsPerCluster: number;
reservedSectors: number;
numberOfFATs: number;
sectorsPerFAT: number;
rootCluster: number;
totalSectors: number;
}
interface Fat32State {
driver: any;
data: any;
boot: Fat32BootSector;
fatStart: number;
dataStart: number;
}
interface DirectoryEntry {
name: string;
extension: string;
attributes: number;
firstCluster: number;
fileSize: number;
}
const ATTR_READ_ONLY = 0x01;
const ATTR_HIDDEN = 0x02;
const ATTR_SYSTEM = 0x04;
const ATTR_VOLUME_ID = 0x08;
const ATTR_DIRECTORY = 0x10;
const ATTR_ARCHIVE = 0x20;
const ATTR_LONG_NAME =
ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID;
export function fat32_driver(driver: any, data: any): KFilesystemDriver {
const state: Fat32State = {
driver,
data,
boot: {} as Fat32BootSector,
fatStart: 0,
dataStart: 0,
};
return {
id: "fat32",
init() {
return fat32_init(state);
},
listDir(path: string) {
return fat32_listDir(state, path);
},
readFile(path: string) {
return fat32_readFile(state, path);
},
writeFile(path: string, content: unknown) {
throw new Error("FAT32 write not implemented");
},
createFile(path: string, content: unknown) {
throw new Error("FAT32 createFile not implemented");
},
mkdir(path: string) {
throw new Error("FAT32 mkdir not implemented");
},
stat(path: string) {
return fat32_stat(state, path);
},
directRead(path: string, count: number, offset: number) {
return fat32_directRead(state, path, count, offset);
},
};
}
function fat32_init(state: Fat32State): void {
const bootSector = state.driver.read(state.data, 512, 0);
state.boot = {
bytesPerSector: bootSector[11] | (bootSector[12] << 8),
sectorsPerCluster: bootSector[13],
reservedSectors: bootSector[14] | (bootSector[15] << 8),
numberOfFATs: bootSector[16],
sectorsPerFAT:
bootSector[36] |
(bootSector[37] << 8) |
(bootSector[38] << 16) |
(bootSector[39] << 24),
rootCluster:
bootSector[44] |
(bootSector[45] << 8) |
(bootSector[46] << 16) |
(bootSector[47] << 24),
totalSectors:
bootSector[32] |
(bootSector[33] << 8) |
(bootSector[34] << 16) |
(bootSector[35] << 24),
};
state.fatStart = state.boot.reservedSectors;
state.dataStart =
state.boot.reservedSectors +
state.boot.numberOfFATs * state.boot.sectorsPerFAT;
}
function fat32_clusterToSector(state: Fat32State, cluster: number): number {
return state.dataStart + (cluster - 2) * state.boot.sectorsPerCluster;
}
function fat32_readCluster(state: Fat32State, cluster: number): number[] {
const sector = fat32_clusterToSector(state, cluster);
const size = state.boot.sectorsPerCluster * state.boot.bytesPerSector;
return state.driver.read(
state.data,
size,
sector * state.boot.bytesPerSector
);
}
function fat32_getNextCluster(state: Fat32State, cluster: number): number {
const fatOffset = cluster * 4;
const fatSector =
state.fatStart + Math.floor(fatOffset / state.boot.bytesPerSector);
const entryOffset = fatOffset % state.boot.bytesPerSector;
const sector = state.driver.read(
state.data,
state.boot.bytesPerSector,
fatSector * state.boot.bytesPerSector
);
const nextCluster =
sector[entryOffset] |
(sector[entryOffset + 1] << 8) |
(sector[entryOffset + 2] << 16) |
(sector[entryOffset + 3] << 24);
return nextCluster & 0x0fffffff;
}
function fat32_parseDirectoryEntry(
data: number[],
offset: number
): DirectoryEntry | null {
if (data[offset] === 0x00) return null; // End of directory
if (data[offset] === 0xe5) return null; // Deleted entry
const attr = data[offset + 11];
if ((attr & ATTR_LONG_NAME) === ATTR_LONG_NAME) return null; // LFN entry
let name = "";
for (let i = 0; i < 8; i++) {
const c = data[offset + i];
if (c !== 0x20) name += String.fromCharCode(c);
}
let extension = "";
for (let i = 0; i < 3; i++) {
const c = data[offset + 8 + i];
if (c !== 0x20) extension += String.fromCharCode(c);
}
const firstClusterLow = data[offset + 26] | (data[offset + 27] << 8);
const firstClusterHigh = data[offset + 20] | (data[offset + 21] << 8);
const firstCluster = (firstClusterHigh << 16) | firstClusterLow;
const fileSize =
data[offset + 28] |
(data[offset + 29] << 8) |
(data[offset + 30] << 16) |
(data[offset + 31] << 24);
return {
name: name.trim(),
extension: extension.trim(),
attributes: attr,
firstCluster,
fileSize,
};
}
function fat32_listDir(
state: Fat32State,
path: string
): KFilesystemEntity[] | null {
let cluster: number | undefined;
if (path === "/") cluster = state.boot.rootCluster;
else {
const entry = fat32_findEntry(state, path);
if (!entry) {
return null;
}
cluster = entry.firstCluster;
}
if (!cluster) {
return null;
}
const entries: KFilesystemEntity[] = [];
let currentCluster = cluster;
while (currentCluster < 0x0ffffff8) {
const clusterData = fat32_readCluster(state, currentCluster);
for (let i = 0; i < clusterData.length; i += 32) {
const entry = fat32_parseDirectoryEntry(clusterData, i);
if (!entry) continue;
// Skip volume label entries
if ((entry.attributes & ATTR_VOLUME_ID) !== 0) continue;
const fullName = entry.extension
? entry.name + "." + entry.extension
: entry.name;
const isDir = (entry.attributes & ATTR_DIRECTORY) !== 0;
entries.push({
name: fullName,
path: path === "/" ? "/" + fullName : path + "/" + fullName,
type: isDir ? "folder" : "file",
size: entry.fileSize,
contents: null,
});
}
currentCluster = fat32_getNextCluster(state, currentCluster);
}
return entries;
}
function fat32_findEntry(
state: Fat32State,
path: string
): DirectoryEntry | null {
const parts = path.split("/").filter(function (p) {
return p.length > 0;
});
if (parts.length === 0) return null;
let currentCluster = state.boot.rootCluster;
for (let i = 0; i < parts.length; i++) {
const targetName = parts[i]!.toUpperCase();
let found = false;
while (currentCluster < 0x0ffffff8) {
const clusterData = fat32_readCluster(state, currentCluster);
for (let j = 0; j < clusterData.length; j += 32) {
const entry = fat32_parseDirectoryEntry(clusterData, j);
if (!entry) continue;
const entryName = entry.extension
? entry.name + "." + entry.extension
: entry.name;
if (entryName.toUpperCase() === targetName) {
if (i === parts.length - 1) return entry;
currentCluster = entry.firstCluster;
found = true;
break;
}
}
if (found) break;
currentCluster = fat32_getNextCluster(state, currentCluster);
}
if (!found) return null;
}
return null;
}
function fat32_readFile(state: Fat32State, path: string): number[] {
const entry = fat32_findEntry(state, path);
if (!entry || (entry.attributes & ATTR_DIRECTORY) !== 0) {
throw new Error("File not found or is a directory");
}
const data: number[] = [];
let currentCluster = entry.firstCluster;
let remaining = entry.fileSize;
while (currentCluster < 0x0ffffff8 && remaining > 0) {
const clusterData = fat32_readCluster(state, currentCluster);
const bytesToCopy = Math.min(remaining, clusterData.length);
for (let i = 0; i < bytesToCopy; i++) {
data.push(clusterData[i]!);
}
remaining -= bytesToCopy;
currentCluster = fat32_getNextCluster(state, currentCluster);
}
return data;
}
function fat32_stat(state: Fat32State, path: string): KFilesystemStat | null {
if (path === "/") {
return { name: "/", type: "folder", size: 0 };
}
const entry = fat32_findEntry(state, path);
if (!entry) return null;
const fullName = entry.extension
? entry.name + "." + entry.extension
: entry.name;
return {
name: fullName,
type: (entry.attributes & ATTR_DIRECTORY) !== 0 ? "folder" : "file",
size: entry.fileSize,
};
}
function fat32_directRead(
state: Fat32State,
path: string,
count: number,
offset: number
): number[] {
const entry = fat32_findEntry(state, path);
if (!entry || (entry.attributes & ATTR_DIRECTORY) !== 0) {
throw new Error("File not found or is a directory");
}
const clusterSize = state.boot.sectorsPerCluster * state.boot.bytesPerSector;
let currentCluster = entry.firstCluster;
let currentOffset = 0;
const data: number[] = [];
// Skip to the cluster containing the offset
while (offset >= currentOffset + clusterSize && currentCluster < 0x0ffffff8) {
currentOffset += clusterSize;
currentCluster = fat32_getNextCluster(state, currentCluster);
}
let remaining = count;
const startOffsetInCluster = offset - currentOffset;
while (remaining > 0 && currentCluster < 0x0ffffff8) {
const clusterData = fat32_readCluster(state, currentCluster);
const start = data.length === 0 ? startOffsetInCluster : 0;
const bytesToCopy = Math.min(remaining, clusterData.length - start);
for (let i = start; i < start + bytesToCopy; i++) {
data.push(clusterData[i]!);
}
remaining -= bytesToCopy;
currentCluster = fat32_getNextCluster(state, currentCluster);
}
return data;
}

View File

@@ -2,18 +2,23 @@ import { Logger } from "../lib/libstd/logger/logger.kmod";
import { charc } from "../lib/libts/byte"; import { charc } from "../lib/libts/byte";
import { padStart } from "../lib/libts/string"; import { padStart } from "../lib/libts/string";
import { getDate } from "../lib/sys/date"; import { getDate } from "../lib/sys/date";
import { kdriver_dev_ata_detectDisks } from "./drivers/dev/ata";
import { kdriver_dev_pci_detectDevices } from "./drivers/dev/pci"; import { kdriver_dev_pci_detectDevices } from "./drivers/dev/pci";
import { import {
kdriver_etc_serial_read, kdriver_etc_serial_read,
kdriver_etc_serial_transmit, kdriver_etc_serial_transmit,
} from "./drivers/etc/serial"; } from "./drivers/etc/serial";
import { devfs_driver, devfs_getDevice } from "./filesystem/devfs";
import { fat32_driver } from "./filesystem/fat32";
import { sysfs_driver, sysfs_readFile } from "./filesystem/sysfs"; import { sysfs_driver, sysfs_readFile } from "./filesystem/sysfs";
import { kmod_disks_detectDisks } from "./modules/disks/disks.kmod";
import { import {
kmod_drivers_init, kmod_drivers_init,
kmod_drivers_register, kmod_drivers_register,
} from "./modules/drivers/drivers.kmod"; } from "./modules/drivers/drivers.kmod";
import { import {
kmod_filesystem_init, kmod_filesystem_init,
kmod_filesystem_listDir,
kmod_filesystem_mount, kmod_filesystem_mount,
kmod_filesystem_readFile, kmod_filesystem_readFile,
} from "./modules/filesystem/filesystem.kmod"; } from "./modules/filesystem/filesystem.kmod";
@@ -21,6 +26,7 @@ import {
kmod_graphics_vga_clear, kmod_graphics_vga_clear,
kmod_graphics_vga_init, kmod_graphics_vga_init,
kmod_graphics_vga_pushLine, kmod_graphics_vga_pushLine,
kmod_graphics_vga_setLastLine,
} from "./modules/graphics/graphics.kmod"; } from "./modules/graphics/graphics.kmod";
import { import {
kmod_terminal_input_init, kmod_terminal_input_init,
@@ -33,9 +39,13 @@ export function kmain() {
kmod_drivers_register(); kmod_drivers_register();
Logger.log("[Kernel] Drivers initialized."); Logger.log("[Kernel] Drivers initialized.");
Logger.log("[Kernel] Initializing VGA module...");
kmod_graphics_vga_init();
Logger.log("[Kernel] Initializing filesystem module..."); Logger.log("[Kernel] Initializing filesystem module...");
kmod_filesystem_init(); kmod_filesystem_init();
kmod_filesystem_mount("/sys", sysfs_driver); kmod_filesystem_mount("/sys", sysfs_driver);
kmod_filesystem_mount("/dev", devfs_driver);
Logger.log("[Kernel] Initializing terminal module..."); Logger.log("[Kernel] Initializing terminal module...");
kmod_terminal_input_init(); kmod_terminal_input_init();
@@ -43,11 +53,45 @@ export function kmain() {
Logger.log("[Kernel] Initializing PCI devices..."); Logger.log("[Kernel] Initializing PCI devices...");
kdriver_dev_pci_detectDevices(); kdriver_dev_pci_detectDevices();
Logger.log("[Kernel] Initializing VGA module..."); Logger.log("[Kernel] Initializing drives...");
kmod_graphics_vga_init(); kmod_disks_detectDisks();
kmod_graphics_vga_pushLine("[Kernel] Kernel initialized successfully."); kmod_graphics_vga_pushLine("[Kernel] Kernel initialized successfully.");
kmod_terminal_input_onKeyboardInput(function (keycode) { const dev = devfs_getDevice("/hda1");
kmod_graphics_vga_pushLine(keycode); if (!dev) throw new Error("Drive not found");
kmod_filesystem_mount("/home", function () {
return fat32_driver(dev.driver, dev.data);
});
let input = "";
let running = false;
kmod_graphics_vga_setLastLine("> ");
kmod_terminal_input_onKeyboardInput(function (code) {
if (running) return;
if (code == "Enter") {
running = true;
kmod_graphics_vga_pushLine("Starting " + input);
const f = kmod_filesystem_readFile(input);
if (!f) return;
let app = "";
for (let i = 0; i < f.length; i++) {
app += String.fromCharCode(f[i]!);
}
// EXTREMELY DANGEROUS -- REPLACE WITH SANDBOXING
eval("const console = { log: Logger.log } ;" + app);
Logger.log("DONE");
running = false;
} else {
input += code.toLowerCase();
kmod_graphics_vga_setLastLine("> " + input);
}
}); });
} }

View File

@@ -0,0 +1,163 @@
import { Logger } from "../../../lib/libstd/logger/logger.kmod";
import { Path } from "../../../lib/libstd/path";
import { cchar, charc } from "../../../lib/libts/byte";
import { DeviceType } from "../../../types/dev/device";
import { DiskType, type Disk } from "../../../types/filesystem/disk";
import {
kdriver_dev_ata_detectDisks,
kdriver_dev_ata_fsDriver,
kdriver_dev_ata_getDrive,
kdriver_dev_ata_numberOfDrives,
kdriver_dev_ata_partition_fsDriver,
kdriver_dev_ata_readSectors,
} from "../../drivers/dev/ata";
import { devfs_registerDevice } from "../../filesystem/devfs";
import { sysfs_mkdir, sysfs_writeFile } from "../../filesystem/sysfs";
let disks: Disk[] = [];
export function kmod_disks_detectDisks() {
kdriver_dev_ata_detectDisks();
let cdrom = charc("a");
let disk = charc("a");
let numOfDisks = 0;
for (let i = 0; i < kdriver_dev_ata_numberOfDrives(); ++i) {
const descriptor = kdriver_dev_ata_getDrive(i);
if (!descriptor.present) continue;
let name = "";
if (descriptor.atapi) {
name = "cd" + cchar(cdrom);
cdrom++;
disks.push({
dnum: numOfDisks,
name: name,
descriptor: descriptor,
type: DiskType.ATAPI,
});
devfs_registerDevice(
"/" + name,
DeviceType.BlockDevice,
null,
descriptor
);
} else {
name = "hd" + cchar(disk);
disk++;
disks.push({
dnum: numOfDisks,
name: name,
descriptor: descriptor,
type: DiskType.ATA,
});
devfs_registerDevice(
"/" + name,
DeviceType.BlockDevice,
kdriver_dev_ata_fsDriver,
descriptor
);
let part = charc("1");
const partitions = kmod_disks_getPartitions(disks[numOfDisks]!);
for (let n = 0; n < partitions.length; n++) {
const partition = partitions[n];
let part_name = name + cchar(part);
part++;
(partition as any).disk = disks[numOfDisks]!;
devfs_registerDevice(
"/" + part_name,
DeviceType.BlockDevice,
kdriver_dev_ata_partition_fsDriver,
partition
);
}
}
const p = Path.join("/ata", name);
sysfs_mkdir(p);
sysfs_writeFile(Path.join(p, "model"), descriptor.model);
sysfs_writeFile(Path.join(p, "serial"), descriptor.serial);
sysfs_writeFile(Path.join(p, "firmware"), descriptor.firmware);
numOfDisks++;
}
}
export function kmod_disks_getPartitions(disk: Disk) {
const sectors = kdriver_dev_ata_readSectors(disk.descriptor, 0, 1, 0);
const signature = (sectors[511]! << 8) | sectors[510]!;
if (signature !== 0xaa55) {
throw new Error("Invalid MBR signature");
}
const partitions = [];
// Parse partition table (starts at offset 446, 4 entries of 16 bytes each)
for (let i = 0; i < 4; i++) {
const offset = 446 + i * 16;
const status = sectors[offset]!;
const partitionType = sectors[offset + 4]!;
// LBA of first sector (little-endian)
const lbaStart =
sectors[offset + 8]! |
(sectors[offset + 9]! << 8) |
(sectors[offset + 10]! << 16) |
(sectors[offset + 11]! << 24);
// Number of sectors (little-endian)
const sectorCount =
sectors[offset + 12]! |
(sectors[offset + 13]! << 8) |
(sectors[offset + 14]! << 16) |
(sectors[offset + 15]! << 24);
// Skip empty partition entries
if (partitionType === 0 || sectorCount === 0) {
continue;
}
partitions.push({
index: i,
bootable: status === 0x80,
type: partitionType,
lbaStart: lbaStart,
sectors: sectorCount,
size: sectorCount * 512,
});
Logger.log(
"[Disk] Partition " +
i +
": Type=0x" +
partitionType.toString(16) +
" " +
"LBA=" +
lbaStart +
" Sectors=" +
sectorCount +
" " +
"Bootable=" +
(status === 0x80)
);
}
return partitions;
}

View File

@@ -1,3 +1,4 @@
import { kdriver_dev_ata_init } from "../../drivers/dev/ata";
import { kdriver_dev_keyboard_init } from "../../drivers/dev/keyboard"; import { kdriver_dev_keyboard_init } from "../../drivers/dev/keyboard";
import { kdriver_dev_pci_init } from "../../drivers/dev/pci"; import { kdriver_dev_pci_init } from "../../drivers/dev/pci";
import { kdriver_etc_serial_init } from "../../drivers/etc/serial"; import { kdriver_etc_serial_init } from "../../drivers/etc/serial";
@@ -6,6 +7,7 @@ const drivers = [
kdriver_etc_serial_init, kdriver_etc_serial_init,
kdriver_dev_pci_init, kdriver_dev_pci_init,
kdriver_dev_keyboard_init, kdriver_dev_keyboard_init,
kdriver_dev_ata_init,
]; ];
export function kmod_drivers_init(): void {} export function kmod_drivers_init(): void {}

View File

@@ -1,4 +1,5 @@
import { Logger } from "../../../lib/libstd/logger/logger.kmod"; import { Logger } from "../../../lib/libstd/logger/logger.kmod";
import { Path } from "../../../lib/libstd/path";
import type { import type {
KFilesystemDriver, KFilesystemDriver,
KFilesystemEntity, KFilesystemEntity,
@@ -48,24 +49,42 @@ export function kmod_filesystem_stripMountpoint(
return p.slice(mount.mountpoint.length) || "/"; return p.slice(mount.mountpoint.length) || "/";
} }
export function kmod_filesystem_lsMountAsFsEntity() {
let out: KFilesystemEntity[] = [];
for (let i = 0; i < kmod_filesystem_data.length; i++) {
const mount = kmod_filesystem_data[i]!;
out.push({
name: Path.filename(mount.mountpoint),
path: mount.mountpoint,
type: "folder",
size: 0,
contents: 0,
});
}
return out;
}
export function kmod_filesystem_listDir( export function kmod_filesystem_listDir(
path: string path: string
): KFilesystemEntity[] | null { ): KFilesystemEntity[] | null {
const mount = kmod_filesystem_findMount(path); const mount = kmod_filesystem_findMount(path);
if (!mount) return null; if (!mount) return kmod_filesystem_lsMountAsFsEntity();
const strippedPath = kmod_filesystem_stripMountpoint(mount, path); const strippedPath = kmod_filesystem_stripMountpoint(mount, path);
return mount.driver.listDir(strippedPath); return mount.driver.listDir(strippedPath);
} }
export function kmod_filesystem_readFile(path: string): unknown { export function kmod_filesystem_readFile(path: string): number[] | null {
const mount = kmod_filesystem_findMount(path); const mount = kmod_filesystem_findMount(path);
if (!mount) return null; if (!mount) return null;
const strippedPath = kmod_filesystem_stripMountpoint(mount, path); const strippedPath = kmod_filesystem_stripMountpoint(mount, path);
return mount.driver.readFile(strippedPath); return mount.driver.readFile(strippedPath) as number[];
} }
export function kmod_filesystem_writeFile( export function kmod_filesystem_writeFile(
@@ -109,3 +128,9 @@ export function kmod_filesystem_stat(path: string): KFilesystemStat | null {
return mount.driver.stat(strippedPath); return mount.driver.stat(strippedPath);
} }
export function kmod_filesystem_directRead(
path: string,
count: number,
offset: number
) {}

View File

@@ -6,12 +6,17 @@ import {
} from "./config"; } from "./config";
let kmod_graphics_vga_lineBuf: string[] = []; let kmod_graphics_vga_lineBuf: string[] = [];
let init = false;
export function kmod_graphics_vga_init() { export function kmod_graphics_vga_init() {
init = true;
kmod_graphics_vga_clear(); kmod_graphics_vga_clear();
} }
export function kmod_graphics_vga_pushLine(line: string) { export function kmod_graphics_vga_pushLine(line: string) {
if (!init) return;
kmod_graphics_vga_lineBuf.push(line); kmod_graphics_vga_lineBuf.push(line);
if (kmod_graphics_vga_lineBuf.length > CFG_KMOD_GRAPHICS_VGA_HEIGHT) { if (kmod_graphics_vga_lineBuf.length > CFG_KMOD_GRAPHICS_VGA_HEIGHT) {
@@ -21,7 +26,21 @@ export function kmod_graphics_vga_pushLine(line: string) {
kmod_graphics_vga_printLines(); kmod_graphics_vga_printLines();
} }
export function kmod_graphics_vga_setLastLine(line: string) {
if (!init) return;
if (kmod_graphics_vga_lineBuf.length === 0) {
kmod_graphics_vga_lineBuf.push(line);
} else {
kmod_graphics_vga_lineBuf[kmod_graphics_vga_lineBuf.length - 1] = line;
}
kmod_graphics_vga_printLines();
}
export function kmod_graphics_vga_printLines() { export function kmod_graphics_vga_printLines() {
if (!init) return;
for (let y = 0; y < CFG_KMOD_GRAPHICS_VGA_HEIGHT; y++) { for (let y = 0; y < CFG_KMOD_GRAPHICS_VGA_HEIGHT; y++) {
const line = kmod_graphics_vga_lineBuf[y] || ""; const line = kmod_graphics_vga_lineBuf[y] || "";
@@ -49,6 +68,8 @@ export function kmod_graphics_vga_getColorAddr(x: number, y: number): number {
} }
export function kmod_graphics_vga_clear() { export function kmod_graphics_vga_clear() {
if (!init) return;
kmod_graphics_vga_lineBuf = []; kmod_graphics_vga_lineBuf = [];
for (let y = 0; y < CFG_KMOD_GRAPHICS_VGA_HEIGHT; y++) { for (let y = 0; y < CFG_KMOD_GRAPHICS_VGA_HEIGHT; y++) {
@@ -64,6 +85,8 @@ export function kmod_graphics_vga_writeChar(
char: string, char: string,
color: number color: number
) { ) {
if (!init) return;
const charAddr = kmod_graphics_vga_getCharAddr(x, y); const charAddr = kmod_graphics_vga_getCharAddr(x, y);
const colorAddr = kmod_graphics_vga_getColorAddr(x, y); const colorAddr = kmod_graphics_vga_getColorAddr(x, y);

View File

@@ -0,0 +1,192 @@
interface Block {
key: number;
hashNext: Block | null;
freeNext: Block | null;
freePrev: Block | null;
payload: number[]; // Array instead of Uint8Array
}
interface BlockCacheState {
payloadSize: number;
blocks: number;
hashTable: (Block | null)[];
blocksMemory: Block[];
front: Block | null;
rear: Block | null;
}
export function blockCache_create(): BlockCacheState {
return {
payloadSize: 0,
blocks: 0,
hashTable: [],
blocksMemory: [],
front: null,
rear: null,
};
}
export function blockCache_init(
state: BlockCacheState,
payloadSize: number,
blocks: number
): void {
state.payloadSize = payloadSize;
state.blocks = blocks;
// Initialize hash table (2x the number of blocks)
var hashTableSize = blocks * 2;
state.hashTable = [];
for (var i = 0; i < hashTableSize; i++) {
state.hashTable.push(null);
}
// Allocate blocks in memory
state.blocksMemory = [];
var previous: Block | null = null;
for (var i = 0; i < blocks; i++) {
var payload: number[] = [];
for (var j = 0; j < payloadSize; j++) {
payload.push(0);
}
var block: Block = {
key: 0,
hashNext: null,
freeNext: null,
freePrev: previous,
payload: payload,
};
if (previous) {
previous.freeNext = block;
}
state.blocksMemory.push(block);
previous = block;
}
state.front = state.blocksMemory[0];
state.rear = previous;
}
export function blockCache_blockIfPresent(
state: BlockCacheState,
deviceOrKey: number,
sector?: number
): number[] | null {
var origKey =
sector !== undefined ? deviceOrKey * 65536 + sector : deviceOrKey;
var key = origKey % (state.blocks * 2);
if (state.hashTable[key]) {
var entry = state.hashTable[key];
while (entry) {
if (entry.key === origKey) {
return entry.payload;
}
entry = entry.hashNext;
}
}
return null;
}
export function blockCache_block(
state: BlockCacheState,
deviceOrKey: number,
sectorOrValid: number | { valid: boolean },
valid?: { valid: boolean }
): number[] {
var origKey: number;
var validRef: { valid: boolean };
if (valid !== undefined) {
// block(state, device, sector, valid)
origKey = deviceOrKey * 65536 + (sectorOrValid as number);
validRef = valid;
} else {
// block(state, key, valid)
origKey = deviceOrKey;
validRef = sectorOrValid as { valid: boolean };
}
var key = origKey % (state.blocks * 2);
// Try to get from hash table
var direct = blockCache_blockIfPresent(state, origKey);
if (direct) {
validRef.valid = true;
return direct;
}
// Allocate new block from front of free list
validRef.valid = false;
if (!state.front) {
throw new Error("No free blocks available");
}
var blk = state.front;
// Remove block from previous hash table entry if it was used
if (blk.key) {
var previousKey = blk.key % (state.blocks * 2);
if (state.hashTable[previousKey] === blk) {
state.hashTable[previousKey] = blk.hashNext;
} else {
var entry = state.hashTable[previousKey];
var found = false;
while (entry && entry.hashNext) {
if (entry.hashNext === blk) {
entry.hashNext = blk.hashNext;
found = true;
break;
}
entry = entry.hashNext;
}
if (!found) {
throw new Error("The hash table chain did not contain the used block");
}
}
}
// Insert block into hash table
blk.key = origKey;
if (!state.hashTable[key]) {
blk.hashNext = null;
state.hashTable[key] = blk;
} else {
// Insert at end of chain
var entry = state.hashTable[key]!;
while (entry.hashNext) {
entry = entry.hashNext;
}
entry.hashNext = blk;
blk.hashNext = null;
}
// Move block to rear of free list
state.front = state.front.freeNext;
if (state.front) {
state.front.freePrev = null;
}
if (state.rear) {
state.rear.freeNext = blk;
}
blk.freePrev = state.rear;
blk.freeNext = null;
state.rear = blk;
return blk.payload;
}

View File

@@ -1,8 +1,11 @@
import { charc } from "../../libts/byte"; import { charc } from "../../libts/byte";
import { kdriver_etc_serial_transmit } from "../../../kernel/drivers/etc/serial"; import { kdriver_etc_serial_transmit } from "../../../kernel/drivers/etc/serial";
import { kmod_graphics_vga_pushLine } from "../../../kernel/modules/graphics/vga";
export const Logger = { export const Logger = {
log(message: string) { log(message: string) {
kmod_graphics_vga_pushLine(message);
for (let i = 0; i < message.length; i++) { for (let i = 0; i < message.length; i++) {
kdriver_etc_serial_transmit(charc(message[i]!)); kdriver_etc_serial_transmit(charc(message[i]!));
} }

View File

@@ -9,3 +9,7 @@ export function byteout(port: number, value: number): void {
export function charc(char: string): number { export function charc(char: string): number {
return char.charCodeAt(0); return char.charCodeAt(0);
} }
export function cchar(code: number): string {
return String.fromCharCode(code)[0]!;
}

View File

@@ -0,0 +1,7 @@
export function wordin(w: number): number {
return $wordin(w);
}
export function wordout(w: number, value: number): void {
$wordout(w, value);
}

View File

@@ -5,6 +5,8 @@ declare function $bytein(port: number): number;
declare function $byteout(port: number, value: number): void; declare function $byteout(port: number, value: number): void;
declare function $dwordin(port: number): number; declare function $dwordin(port: number): number;
declare function $dwordout(port: number, value: number): void; declare function $dwordout(port: number, value: number): void;
declare function $wordin(port: number): number;
declare function $wordout(port: number, value: number): void;
declare const $irqregister: ( declare const $irqregister: (
irq: number, irq: number,
handler: (irqNum: number) => void handler: (irqNum: number) => void

View File

@@ -0,0 +1,11 @@
export type AtaDriveDescriptor = {
controller: number;
drive: number;
present: boolean;
slave: number;
atapi: boolean;
model: string;
serial: string;
firmware: string;
size: number;
};

View File

@@ -0,0 +1,4 @@
export enum DeviceType {
BlockDevice,
CharDevice,
}

View File

@@ -0,0 +1,13 @@
import type { AtaDriveDescriptor } from "../dev/ata/drive";
export type Disk = {
dnum: number;
type: DiskType;
descriptor: AtaDriveDescriptor;
name: string;
};
export enum DiskType {
ATA,
ATAPI,
}

View File

@@ -1,3 +1,5 @@
import type { DeviceType } from "../dev/device";
export type KFilesystemMountdata = KFilesystemMount[]; export type KFilesystemMountdata = KFilesystemMount[];
export type KFilesystemMount = { export type KFilesystemMount = {
@@ -5,6 +7,8 @@ export type KFilesystemMount = {
driver: KFilesystemDriver; driver: KFilesystemDriver;
}; };
export type KFilesystemDirectReadResponse = number[];
export type KFilesystemDriver = { export type KFilesystemDriver = {
id: string; id: string;
init: () => void; init: () => void;
@@ -14,6 +18,11 @@ export type KFilesystemDriver = {
createFile(path: string, content: unknown): void; createFile(path: string, content: unknown): void;
mkdir(path: string): void; mkdir(path: string): void;
stat(path: string): KFilesystemStat | null; stat(path: string): KFilesystemStat | null;
directRead?(
path: string,
count: number,
offset: number
): KFilesystemDirectReadResponse;
}; };
export type KFilesystemMemdata = KFilesystemEntity[]; export type KFilesystemMemdata = KFilesystemEntity[];
@@ -31,3 +40,11 @@ export type KFilesystemStat = {
type: "file" | "folder"; type: "file" | "folder";
size: number; size: number;
}; };
export type KFilesystemDevice = KFilesystemEntity & {
dtype: DeviceType;
driver: unknown;
data: unknown;
};
export type KFilesystemMemdev = (KFilesystemDevice | KFilesystemEntity)[];

View File

@@ -0,0 +1,4 @@
export type BlockCache = {
payload_size: number;
blocks: number;
};

1
test-hdd/hello.txt Normal file
View File

@@ -0,0 +1 @@
It works!

1
test-hdd/myapp.js Normal file
View File

@@ -0,0 +1 @@
console.log("Hello from myApp");