Begin the Great Bootloader Swap.
This commit is contained in:
parent
b8ea5e05d2
commit
21b24619f0
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,3 @@
|
||||
.vscode/
|
||||
build/
|
||||
toolchain/
|
||||
src/bootloader/stage2_old
|
||||
toolchain/
|
||||
34
Makefile
34
Makefile
@ -18,33 +18,11 @@ floppy_image: $(BUILD_DIR)/main_floppy.img
|
||||
$(BUILD_DIR)/main_floppy.img: bootloader kernel
|
||||
dd if=/dev/zero of=$(BUILD_DIR)/main_floppy.img bs=512 count=2880
|
||||
mkfs.fat -F 12 -n "NANITE" $(BUILD_DIR)/main_floppy.img
|
||||
dd if=$(BUILD_DIR)/stage1.bin of=$(BUILD_DIR)/main_floppy.img conv=notrunc
|
||||
mmd -i $(BUILD_DIR)/main_floppy.img "::boot"
|
||||
mmd -i $(BUILD_DIR)/main_floppy.img "::misc"
|
||||
mmd -i $(BUILD_DIR)/main_floppy.img "::misc/src"
|
||||
mcopy -v -i $(BUILD_DIR)/main_floppy.img $(BUILD_DIR)/kernel.bin "::boot"
|
||||
mcopy -v -i $(BUILD_DIR)/main_floppy.img $(BUILD_DIR)/nboot.bin "::nboot.bin"
|
||||
mcopy -v -i $(BUILD_DIR)/main_floppy.img test "::boot"
|
||||
mcopy -v -i $(BUILD_DIR)/main_floppy.img kparams "::boot"
|
||||
mcopy -s -i $(BUILD_DIR)/main_floppy.img src/* "::misc/src"
|
||||
|
||||
#
|
||||
# Bootloader
|
||||
#
|
||||
#
|
||||
bootloader: stage1 stage2
|
||||
|
||||
stage1: $(BUILD_DIR)/stage1.bin
|
||||
|
||||
$(BUILD_DIR)/stage1.bin: always
|
||||
$(MAKE) -C src/bootloader/stage1 BUILD_DIR=$(abspath $(BUILD_DIR))
|
||||
|
||||
stage2: $(BUILD_DIR)/stage2.bin
|
||||
|
||||
$(BUILD_DIR)/stage2.bin: always
|
||||
$(MAKE) -C src/bootloader/stage2 BUILD_DIR=$(abspath $(BUILD_DIR))
|
||||
|
||||
|
||||
#
|
||||
# Kernel
|
||||
#
|
||||
@ -53,21 +31,11 @@ kernel: $(BUILD_DIR)/kernel.bin
|
||||
$(BUILD_DIR)/kernel.bin: always
|
||||
$(MAKE) -C src/kernel BUILD_DIR=$(abspath $(BUILD_DIR))
|
||||
|
||||
#
|
||||
# Tools
|
||||
#
|
||||
tools_fat: $(BUILD_DIR)/tools/fat
|
||||
$(BUILD_DIR)/tools/fat: always tools/fat/fat.c
|
||||
mkdir -p $(BUILD_DIR)/tools
|
||||
$(CC) -g -o $(BUILD_DIR)/tools/fat tools/fat/fat.c
|
||||
|
||||
#
|
||||
# Always
|
||||
#
|
||||
always:
|
||||
mkdir -p $(BUILD_DIR)
|
||||
# echo Version $(oldnum)
|
||||
# sed -i '9i s/#define VERSION "RD-//#define VERSION "RD-$(newnum)/"' src/libs/version.h
|
||||
|
||||
|
||||
|
||||
@ -75,8 +43,6 @@ always:
|
||||
# Clean
|
||||
#
|
||||
clean:
|
||||
$(MAKE) -C src/bootloader/stage1 BUILD_DIR=$(abspath $(BUILD_DIR)) clean
|
||||
$(MAKE) -C src/bootloader/stage2 BUILD_DIR=$(abspath $(BUILD_DIR)) clean
|
||||
$(MAKE) -C src/kernel BUILD_DIR=$(abspath $(BUILD_DIR)) clean
|
||||
rm -rf $(BUILD_DIR)/*
|
||||
rm -rf src/bootloader/stage2/*.err
|
||||
|
||||
@ -14,7 +14,8 @@ Good luck figuring out the spagetti code i write... (sorry not sorry ;D)
|
||||
Designed for older computers such as a Pentium (i586) Class Machine. I would recomend atleast a Pentium 2 Class System or higher however.
|
||||
|
||||
## Features
|
||||
- Bootloader
|
||||
- Support for the GRUB Bootloader
|
||||
- Custom Theme for GRUB (Coming Soon!)
|
||||
- Basic Memory Paging
|
||||
- Support for Floppy Disk Drives (FDDs)
|
||||
- Basic Serial (RS-232) Support
|
||||
@ -33,6 +34,7 @@ If you wanted to write it to a floppy disk you can use `write.sh` tho be careful
|
||||
- make
|
||||
- gcc (or really any C compiler)
|
||||
- NASM
|
||||
- GRUB
|
||||
|
||||
## How is Testing Done
|
||||
Testing is mostly done with QEMU These days, but I do sometimes pull out my Dell Latitude D610 to test on (for anyone wondering its completely maxed out. [2GB of ram Pentium M @ 2.23GHz])
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Kernel Parameters
|
||||
|
||||
Kernel Parameters are edited via editing the `kernelparams` file in the root of the git tree/hdd.
|
||||
Basically just enter it into grub, just like you would for something like linux.
|
||||
They should be seperated via commas **WITHOUT ANY SPACES!**
|
||||
|
||||
## List of Parameters
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
BUILD_DIR?=build/
|
||||
ASM?=nasm
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: stage1
|
||||
|
||||
stage1: $(BUILD_DIR)/stage1.bin
|
||||
|
||||
$(BUILD_DIR)/stage1.bin:
|
||||
$(ASM) boot.asm -f bin -o $(BUILD_DIR)/stage1.bin
|
||||
|
||||
clean:
|
||||
rm -f $(BUILD_DIR)/stage1.bin
|
||||
@ -1,397 +0,0 @@
|
||||
;/////////////////////;
|
||||
;Nanite OS ;
|
||||
;COPYRIGHT (C) 2024 ;
|
||||
;Tyler McGurrin ;
|
||||
;/////////////////////;
|
||||
org 0x7C00
|
||||
bits 16
|
||||
|
||||
|
||||
%define ENDL 0x0D, 0x0A
|
||||
|
||||
|
||||
;
|
||||
; FAT12 header
|
||||
;
|
||||
jmp short start
|
||||
nop
|
||||
|
||||
bdb_oem: db 'MSWIN4.1' ; 8 bytes
|
||||
bdb_bytes_per_sector: dw 512
|
||||
bdb_sectors_per_cluster: db 1
|
||||
bdb_reserved_sectors: dw 1
|
||||
bdb_fat_count: db 2
|
||||
bdb_dir_entries_count: dw 0E0h
|
||||
bdb_total_sectors: dw 2880 ; 2880 * 512 = 1.44MB
|
||||
bdb_media_descriptor_type: db 0F0h ; F0 = 3.5" floppy disk
|
||||
bdb_sectors_per_fat: dw 9 ; 9 sectors/fat
|
||||
bdb_sectors_per_track: dw 18
|
||||
bdb_heads: dw 2
|
||||
bdb_hidden_sectors: dd 0
|
||||
bdb_large_sector_count: dd 0
|
||||
|
||||
; extended boot record
|
||||
ebr_drive_number: db 0 ; 0x00 floppy, 0x80 hdd, useless
|
||||
db 0 ; reserved
|
||||
ebr_signature: db 29h
|
||||
ebr_volume_id: db 12h, 34h, 56h, 78h ; serial number, value doesn't matter
|
||||
ebr_volume_label: db 'NANITE ' ; 11 bytes, padded with spaces
|
||||
ebr_system_id: db 'FAT12 ' ; 8 bytes
|
||||
|
||||
;
|
||||
; Code goes here
|
||||
;
|
||||
|
||||
start:
|
||||
; setup data segments
|
||||
mov ax, 0 ; can't set ds/es directly
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
|
||||
; setup stack
|
||||
mov ss, ax
|
||||
mov sp, 0x7C00 ; stack grows downwards from where we are loaded in memory
|
||||
|
||||
; some BIOSes might start us at 07C0:0000 instead of 0000:7C00, make sure we are in the
|
||||
; expected location
|
||||
push es
|
||||
push word .after
|
||||
retf
|
||||
|
||||
.after:
|
||||
|
||||
; read something from floppy disk
|
||||
; BIOS should set DL to drive number
|
||||
mov [ebr_drive_number], dl
|
||||
|
||||
; show loading message
|
||||
mov si, msg_loading
|
||||
call puts
|
||||
|
||||
; read drive parameters (sectors per track and head count),
|
||||
; instead of relying on data on formatted disk
|
||||
push es
|
||||
mov ah, 08h
|
||||
int 13h
|
||||
jc error
|
||||
pop es
|
||||
|
||||
and cl, 0x3F ; remove top 2 bits
|
||||
xor ch, ch
|
||||
mov [bdb_sectors_per_track], cx ; sector count
|
||||
|
||||
inc dh
|
||||
mov [bdb_heads], dh ; head count
|
||||
|
||||
; compute LBA of root directory = reserved + fats * sectors_per_fat
|
||||
; note: this section can be hardcoded
|
||||
mov ax, [bdb_sectors_per_fat]
|
||||
mov bl, [bdb_fat_count]
|
||||
xor bh, bh
|
||||
mul bx ; ax = (fats * sectors_per_fat)
|
||||
add ax, [bdb_reserved_sectors] ; ax = LBA of root directory
|
||||
push ax
|
||||
|
||||
; compute size of root directory = (32 * number_of_entries) / bytes_per_sector
|
||||
mov ax, [bdb_dir_entries_count]
|
||||
shl ax, 5 ; ax *= 32
|
||||
xor dx, dx ; dx = 0
|
||||
div word [bdb_bytes_per_sector] ; number of sectors we need to read
|
||||
|
||||
test dx, dx ; if dx != 0, add 1
|
||||
jz .root_dir_after
|
||||
inc ax ; division remainder != 0, add 1
|
||||
; this means we have a sector only partially filled with entries
|
||||
.root_dir_after:
|
||||
|
||||
; read root directory
|
||||
mov cl, al ; cl = number of sectors to read = size of root directory
|
||||
pop ax ; ax = LBA of root directory
|
||||
mov dl, [ebr_drive_number] ; dl = drive number (we saved it previously)
|
||||
mov bx, buffer ; es:bx = buffer
|
||||
call disk_read
|
||||
|
||||
; search for stage2
|
||||
xor bx, bx
|
||||
mov di, buffer
|
||||
|
||||
; .search_boot_dir:
|
||||
; mov si, boot_dir
|
||||
; mov cx, 11 ; compare up to 11 characters
|
||||
; push di
|
||||
; repe cmpsb
|
||||
; pop di
|
||||
; je .found_boot_dir
|
||||
|
||||
; add di, 32
|
||||
; inc bx
|
||||
; cmp bx, [bdb_dir_entries_count]
|
||||
; jl .search_boot_dir
|
||||
|
||||
; ; kernel not found
|
||||
; jmp error
|
||||
|
||||
; .found_boot_dir:
|
||||
; hlt
|
||||
|
||||
.search_stage2:
|
||||
mov si, file_stage2_bin
|
||||
mov cx, 11 ; compare up to 11 characters
|
||||
push di
|
||||
repe cmpsb
|
||||
pop di
|
||||
je .found_stage2
|
||||
|
||||
add di, 32
|
||||
inc bx
|
||||
cmp bx, [bdb_dir_entries_count]
|
||||
jl .search_stage2
|
||||
|
||||
; kernel not found
|
||||
jmp error
|
||||
|
||||
.found_stage2:
|
||||
|
||||
; di should have the address to the entry
|
||||
mov ax, [di + 26] ; first logical cluster field (offset 26)
|
||||
mov [stage2_cluster], ax
|
||||
|
||||
; load FAT from disk into memory
|
||||
mov ax, [bdb_reserved_sectors]
|
||||
mov bx, buffer
|
||||
mov cl, [bdb_sectors_per_fat]
|
||||
mov dl, [ebr_drive_number]
|
||||
call disk_read
|
||||
|
||||
; read kernel and process FAT chain
|
||||
mov bx, STAGE2_LOAD_SEGMENT
|
||||
mov es, bx
|
||||
mov bx, STAGE2_LOAD_OFFSET
|
||||
|
||||
.load_stage2_loop:
|
||||
|
||||
; Read next cluster
|
||||
mov ax, [stage2_cluster]
|
||||
|
||||
; not nice :( hardcoded value
|
||||
add ax, 31 ; first cluster = (kernel_cluster - 2) * sectors_per_cluster + start_sector
|
||||
; start sector = reserved + fats + root directory size = 1 + 18 + 134 = 33
|
||||
mov cl, 1
|
||||
mov dl, [ebr_drive_number]
|
||||
call disk_read
|
||||
|
||||
add bx, [bdb_bytes_per_sector]
|
||||
|
||||
; compute location of next cluster
|
||||
mov ax, [stage2_cluster]
|
||||
mov cx, 3
|
||||
mul cx
|
||||
mov cx, 2
|
||||
div cx ; ax = index of entry in FAT, dx = cluster mod 2
|
||||
|
||||
mov si, buffer
|
||||
add si, ax
|
||||
mov ax, [ds:si] ; read entry from FAT table at index ax
|
||||
|
||||
or dx, dx
|
||||
jz .even
|
||||
|
||||
.odd:
|
||||
shr ax, 4
|
||||
jmp .next_cluster_after
|
||||
|
||||
.even:
|
||||
and ax, 0x0FFF
|
||||
|
||||
.next_cluster_after:
|
||||
cmp ax, 0x0FF8 ; end of chain
|
||||
jae .read_finish
|
||||
|
||||
mov [stage2_cluster], ax
|
||||
jmp .load_stage2_loop
|
||||
|
||||
.read_finish:
|
||||
|
||||
; jump to our kernel
|
||||
mov dl, [ebr_drive_number] ; boot device in dl
|
||||
|
||||
mov ax, STAGE2_LOAD_SEGMENT ; set segment registers
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
|
||||
jmp STAGE2_LOAD_SEGMENT:STAGE2_LOAD_OFFSET
|
||||
|
||||
jmp wait_key_and_reboot ; should never happen
|
||||
|
||||
cli ; disable interrupts, this way CPU can't get out of "halt" state
|
||||
hlt
|
||||
|
||||
|
||||
;
|
||||
; Error handler (i needed more room.)
|
||||
;
|
||||
|
||||
error:
|
||||
mov si, msg_error
|
||||
call puts
|
||||
jmp wait_key_and_reboot
|
||||
|
||||
wait_key_and_reboot:
|
||||
mov ah, 0
|
||||
int 16h ; wait for keypress
|
||||
jmp 0FFFFh:0 ; jump to beginning of BIOS, should reboot
|
||||
|
||||
.halt:
|
||||
cli ; disable interrupts, this way CPU can't get out of "halt" state
|
||||
hlt
|
||||
|
||||
|
||||
;
|
||||
; Prints a string to the screen
|
||||
; Params:
|
||||
; - ds:si points to string
|
||||
;
|
||||
puts:
|
||||
; save registers we will modify
|
||||
push si
|
||||
push ax
|
||||
push bx
|
||||
|
||||
.loop:
|
||||
lodsb ; loads next character in al
|
||||
or al, al ; verify if next character is null?
|
||||
jz .done
|
||||
|
||||
mov ah, 0x0E ; call bios interrupt
|
||||
mov bh, 0 ; set page number to 0
|
||||
int 0x10
|
||||
|
||||
jmp .loop
|
||||
|
||||
.done:
|
||||
pop bx
|
||||
pop ax
|
||||
pop si
|
||||
ret
|
||||
|
||||
;
|
||||
; Disk routines
|
||||
;
|
||||
|
||||
;
|
||||
; Converts an LBA address to a CHS address
|
||||
; Parameters:
|
||||
; - ax: LBA address
|
||||
; Returns:
|
||||
; - cx [bits 0-5]: sector number
|
||||
; - cx [bits 6-15]: cylinder
|
||||
; - dh: head
|
||||
;
|
||||
|
||||
lba_to_chs:
|
||||
|
||||
push ax
|
||||
push dx
|
||||
|
||||
xor dx, dx ; dx = 0
|
||||
div word [bdb_sectors_per_track] ; ax = LBA / SectorsPerTrack
|
||||
; dx = LBA % SectorsPerTrack
|
||||
|
||||
inc dx ; dx = (LBA % SectorsPerTrack + 1) = sector
|
||||
mov cx, dx ; cx = sector
|
||||
|
||||
xor dx, dx ; dx = 0
|
||||
div word [bdb_heads] ; ax = (LBA / SectorsPerTrack) / Heads = cylinder
|
||||
; dx = (LBA / SectorsPerTrack) % Heads = head
|
||||
mov dh, dl ; dh = head
|
||||
mov ch, al ; ch = cylinder (lower 8 bits)
|
||||
shl ah, 6
|
||||
or cl, ah ; put upper 2 bits of cylinder in CL
|
||||
|
||||
pop ax
|
||||
mov dl, al ; restore DL
|
||||
pop ax
|
||||
ret
|
||||
|
||||
|
||||
;
|
||||
; Reads sectors from a disk
|
||||
; Parameters:
|
||||
; - ax: LBA address
|
||||
; - cl: number of sectors to read (up to 128)
|
||||
; - dl: drive number
|
||||
; - es:bx: memory address where to store read data
|
||||
;
|
||||
disk_read:
|
||||
|
||||
push ax ; save registers we will modify
|
||||
push bx
|
||||
push cx
|
||||
push dx
|
||||
push di
|
||||
|
||||
push cx ; temporarily save CL (number of sectors to read)
|
||||
call lba_to_chs ; compute CHS
|
||||
pop ax ; AL = number of sectors to read
|
||||
|
||||
mov ah, 02h
|
||||
mov di, 3 ; retry count
|
||||
|
||||
.retry:
|
||||
pusha ; save all registers, we don't know what bios modifies
|
||||
stc ; set carry flag, some BIOS'es don't set it
|
||||
int 13h ; carry flag cleared = success
|
||||
jnc .done ; jump if carry not set
|
||||
|
||||
; read failed
|
||||
popa
|
||||
call disk_reset
|
||||
|
||||
dec di
|
||||
test di, di
|
||||
jnz .retry
|
||||
|
||||
.fail:
|
||||
; all attempts are exhausted
|
||||
jmp error
|
||||
|
||||
.done:
|
||||
popa
|
||||
|
||||
pop di
|
||||
pop dx
|
||||
pop cx
|
||||
pop bx
|
||||
pop ax ; restore registers modified
|
||||
ret
|
||||
|
||||
|
||||
;
|
||||
; Resets disk controller
|
||||
; Parameters:
|
||||
; dl: drive number
|
||||
;
|
||||
disk_reset:
|
||||
pusha
|
||||
mov ah, 0
|
||||
stc
|
||||
int 13h
|
||||
jc error
|
||||
popa
|
||||
ret
|
||||
|
||||
|
||||
msg_loading: db 'Loading NBOOT...', ENDL, 0
|
||||
msg_error: db 'ERR!', ENDL, 0
|
||||
file_stage2_bin: db 'NBOOT BIN'
|
||||
boot_dir: db 'BOOT '
|
||||
stage2_cluster: dw 0
|
||||
|
||||
STAGE2_LOAD_SEGMENT equ 0x0
|
||||
STAGE2_LOAD_OFFSET equ 0x500
|
||||
|
||||
times 510-($-$$) db 0
|
||||
dw 0AA55h
|
||||
|
||||
buffer:
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
TARGET_ASMFLAGS += -f elf
|
||||
TARGET_CFLAGS += -ffreestanding -nostdlib
|
||||
TARGET_LIBS += -lgcc
|
||||
TARGET_LINKFLAGS += -T linker.ld -nostdlib
|
||||
|
||||
SOURCES_C=$(wildcard *.c)
|
||||
SOURCES_ASM=$(wildcard *.asm)
|
||||
OBJECTS_C=$(patsubst %.c, $(BUILD_DIR)/stage2/c/%.obj, $(SOURCES_C))
|
||||
OBJECTS_ASM=$(patsubst %.asm, $(BUILD_DIR)/stage2/asm/%.obj, $(SOURCES_ASM))
|
||||
HEADERS_C= $(wildcard ../../libs/*.h) \
|
||||
$(wildcard ../../libs/*/*.h)
|
||||
|
||||
.PHONY: all stage2 clean always
|
||||
|
||||
all: stage2
|
||||
|
||||
stage2: $(BUILD_DIR)/nboot.bin
|
||||
|
||||
$(BUILD_DIR)/nboot.bin: $(OBJECTS_ASM) $(OBJECTS_C)
|
||||
$(TARGET_LD) $(TARGET_LINKFLAGS) -Wl,-Map=$(BUILD_DIR)/stage2.map -o $@ $^ $(TARGET_LIBS)
|
||||
|
||||
$(BUILD_DIR)/stage2/c/%.obj: %.c always $(HEADERS_C)
|
||||
$(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(BUILD_DIR)/stage2/asm/%.obj: %.asm always
|
||||
$(TARGET_ASM) $(TARGET_ASMFLAGS) -o $@ $<
|
||||
|
||||
always:
|
||||
mkdir -p $(BUILD_DIR)/stage2/c
|
||||
mkdir -p $(BUILD_DIR)/stage2/asm
|
||||
|
||||
clean:
|
||||
rm -f $(BUILD_DIR)/nboot.bin
|
||||
@ -1,14 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#include "ctype.h"
|
||||
|
||||
bool islower(char chr) {
|
||||
return chr >= 'a' && chr <= 'z';
|
||||
}
|
||||
|
||||
char toupper(char chr) {
|
||||
return islower(chr) ? (chr - 'a' + 'A') : chr;
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool islower(char chr);
|
||||
char toupper(char chr);
|
||||
@ -1,52 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#include "disk.h"
|
||||
#include "x86.h"
|
||||
#include "stdio.h"
|
||||
|
||||
bool DISK_Initialize(DISK* disk, uint8_t driveNumber) {
|
||||
uint8_t driveType;
|
||||
uint16_t cylinders, sectors, heads;
|
||||
|
||||
if (!x86_Disk_GetDriveParams(disk->id, &driveType, &cylinders, §ors, &heads))
|
||||
return false;
|
||||
|
||||
disk->id = driveNumber;
|
||||
disk->cylinders = cylinders;
|
||||
disk->heads = heads;
|
||||
disk->sectors = sectors;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DISK_LBA2CHS(DISK* disk, uint32_t lba, uint16_t* cylinderOut, uint16_t* sectorOut, uint16_t* headOut) {
|
||||
// sector = (LBA % sectors per track + 1)
|
||||
*sectorOut = lba % disk->sectors +1;
|
||||
|
||||
// cylinder = (LBA / sects per track / heads)
|
||||
*cylinderOut = (lba / disk->sectors) / disk->heads;
|
||||
|
||||
// head = (LBA / sects per track % heads)
|
||||
*headOut = (lba / disk->sectors) % disk->heads;
|
||||
|
||||
// printf("LBA2CHS: lba=%u sect=%u cyl=%u head=%u disk_sectors=%u disk_heads=%u\n", lba, *sectorOut, *cylinderOut, *headOut, disk->sectors, disk->heads);
|
||||
}
|
||||
|
||||
bool DISK_ReadSectors(DISK* disk, uint32_t lba, uint8_t sectors, void* dataOut) {
|
||||
uint16_t cylinder, sector, head;
|
||||
|
||||
DISK_LBA2CHS(disk, lba, &cylinder, §or, &head);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (x86_Disk_Read(disk->id, cylinder, sector, head, sectors, dataOut))
|
||||
return true;
|
||||
|
||||
x86_Disk_Reset(disk->id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#pragma once
|
||||
|
||||
#include "stdint.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
uint8_t id;
|
||||
uint16_t cylinders;
|
||||
uint16_t sectors;
|
||||
uint16_t heads;
|
||||
} DISK;
|
||||
|
||||
bool DISK_Initialize(DISK* disk, uint8_t driveNumber);
|
||||
bool DISK_ReadSectors(DISK* disk, uint32_t lba, uint8_t sectors, void* lowerDataOut);
|
||||
|
||||
@ -1,175 +0,0 @@
|
||||
;/////////////////////;
|
||||
;Nanite OS ;
|
||||
;COPYRIGHT (C) 2024 ;
|
||||
;Tyler McGurrin ;
|
||||
;/////////////////////;
|
||||
bits 16
|
||||
|
||||
section .entry
|
||||
|
||||
extern __bss_start
|
||||
extern __end
|
||||
|
||||
extern start
|
||||
global entry
|
||||
|
||||
entry:
|
||||
cli
|
||||
|
||||
; save boot drive
|
||||
mov [g_BootDrive], dl
|
||||
|
||||
; setup stack
|
||||
mov ax, ds
|
||||
mov ss, ax
|
||||
mov sp, 0xFFF0
|
||||
mov bp, sp
|
||||
|
||||
; switch to PMODE
|
||||
call EnableA20
|
||||
call LoadGDT
|
||||
|
||||
; set PMODE enable flag in CR0
|
||||
mov eax, CR0
|
||||
or al, 1
|
||||
mov cr0, eax
|
||||
|
||||
; far jmp into PMODE
|
||||
jmp dword 08h:.pmode
|
||||
|
||||
.pmode:
|
||||
; we are now in protected mode!
|
||||
[bits 32]
|
||||
|
||||
; 6 - setup segment registers
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov ss, ax
|
||||
mov al, 0
|
||||
cld
|
||||
rep stosb
|
||||
|
||||
; clear BSS (uninit data)
|
||||
mov edi, __bss_start
|
||||
mov ecx, __end
|
||||
sub ecx, edi
|
||||
|
||||
|
||||
xor edx, edx
|
||||
mov dl, [g_BootDrive]
|
||||
push edx
|
||||
call start
|
||||
|
||||
cli
|
||||
hlt
|
||||
|
||||
|
||||
|
||||
EnableA20:
|
||||
[bits 16]
|
||||
; disable keyboard
|
||||
call A20WaitInput
|
||||
mov al, KbdControllerDisableKeyboard
|
||||
out KbdControllerCommandPort, al
|
||||
|
||||
; read control output port
|
||||
call A20WaitInput
|
||||
mov al, KbdControllerReadCtrlOutputPort
|
||||
out KbdControllerCommandPort, al
|
||||
|
||||
call A20WaitOutput
|
||||
in al, KbdControllerDataPort
|
||||
push eax
|
||||
|
||||
; write control output port
|
||||
call A20WaitInput
|
||||
mov al, KbdControllerWriteCtrlOutputPort
|
||||
out KbdControllerCommandPort, al
|
||||
|
||||
call A20WaitInput
|
||||
pop eax
|
||||
or al, 2 ; bit 2 = A20 bit
|
||||
out KbdControllerDataPort, al
|
||||
|
||||
; enable keyboard
|
||||
call A20WaitInput
|
||||
mov al, KbdControllerEnableKeyboard
|
||||
out KbdControllerCommandPort, al
|
||||
|
||||
call A20WaitInput
|
||||
ret
|
||||
|
||||
|
||||
A20WaitInput:
|
||||
[bits 16]
|
||||
; wait until status bit 2 (input buffer) is 0
|
||||
; by reading from command port, we read status byte
|
||||
in al, KbdControllerCommandPort
|
||||
test al, 2
|
||||
jnz A20WaitInput
|
||||
ret
|
||||
|
||||
A20WaitOutput:
|
||||
[bits 16]
|
||||
; wait until status bit 1 (output buffer) is 1 so it can be read
|
||||
in al, KbdControllerCommandPort
|
||||
test al, 1
|
||||
jz A20WaitOutput
|
||||
ret
|
||||
|
||||
|
||||
LoadGDT:
|
||||
[bits 16]
|
||||
lgdt [g_GDTDesc]
|
||||
ret
|
||||
|
||||
|
||||
|
||||
KbdControllerDataPort equ 0x60
|
||||
KbdControllerCommandPort equ 0x64
|
||||
KbdControllerDisableKeyboard equ 0xAD
|
||||
KbdControllerEnableKeyboard equ 0xAE
|
||||
KbdControllerReadCtrlOutputPort equ 0xD0
|
||||
KbdControllerWriteCtrlOutputPort equ 0xD1
|
||||
|
||||
ScreenBuffer equ 0xB8000
|
||||
|
||||
g_GDT: ; NULL descriptor
|
||||
dq 0
|
||||
|
||||
; 32-bit code segment
|
||||
dw 0FFFFh ; limit (bits 0-15) = 0xFFFFF for full 32-bit range
|
||||
dw 0 ; base (bits 0-15) = 0x0
|
||||
db 0 ; base (bits 16-23)
|
||||
db 10011010b ; access (present, ring 0, code segment, executable, direction 0, readable)
|
||||
db 11001111b ; granularity (4k pages, 32-bit pmode) + limit (bits 16-19)
|
||||
db 0 ; base high
|
||||
|
||||
; 32-bit data segment
|
||||
dw 0FFFFh ; limit (bits 0-15) = 0xFFFFF for full 32-bit range
|
||||
dw 0 ; base (bits 0-15) = 0x0
|
||||
db 0 ; base (bits 16-23)
|
||||
db 10010010b ; access (present, ring 0, data segment, executable, direction 0, writable)
|
||||
db 11001111b ; granularity (4k pages, 32-bit pmode) + limit (bits 16-19)
|
||||
db 0 ; base high
|
||||
|
||||
; 16-bit code segment
|
||||
dw 0FFFFh ; limit (bits 0-15) = 0xFFFFF
|
||||
dw 0 ; base (bits 0-15) = 0x0
|
||||
db 0 ; base (bits 16-23)
|
||||
db 10011010b ; access (present, ring 0, code segment, executable, direction 0, readable)
|
||||
db 00001111b ; granularity (1b pages, 16-bit pmode) + limit (bits 16-19)
|
||||
db 0 ; base high
|
||||
|
||||
; 16-bit data segment
|
||||
dw 0FFFFh ; limit (bits 0-15) = 0xFFFFF
|
||||
dw 0 ; base (bits 0-15) = 0x0
|
||||
db 0 ; base (bits 16-23)
|
||||
db 10010010b ; access (present, ring 0, data segment, executable, direction 0, writable)
|
||||
db 00001111b ; granularity (1b pages, 16-bit pmode) + limit (bits 16-19)
|
||||
db 0 ; base high
|
||||
|
||||
g_GDTDesc: dw g_GDTDesc - g_GDT - 1 ; limit = size of GDT
|
||||
dd g_GDT ; address of GDT
|
||||
|
||||
g_BootDrive: db 0
|
||||
@ -1,377 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#include "fat.h"
|
||||
#include "stdio.h"
|
||||
#include "memdefs.h"
|
||||
#include "string.h"
|
||||
#include "memory.h"
|
||||
#include "ctype.h"
|
||||
#include <stddef.h>
|
||||
#include "minmax.h"
|
||||
|
||||
#define SECTOR_SIZE 512
|
||||
#define MAX_PATH_SIZE 256
|
||||
#define MAX_FILE_HANDLES 10
|
||||
#define ROOT_DIRECTORY_HANDLE -1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t BootJumpInstruction[3];
|
||||
uint8_t OemIdentifier[8];
|
||||
uint16_t BytesPerSector;
|
||||
uint8_t SectorsPerCluster;
|
||||
uint16_t ReservedSectors;
|
||||
uint8_t FatCount;
|
||||
uint16_t DirEntryCount;
|
||||
uint16_t TotalSectors;
|
||||
uint8_t MediaDescriptorType;
|
||||
uint16_t SectorsPerFat;
|
||||
uint16_t SectorsPerTrack;
|
||||
uint16_t Heads;
|
||||
uint32_t HiddenSectors;
|
||||
uint32_t LargeSectorCount;
|
||||
|
||||
// extended boot record
|
||||
uint8_t DriveNumber;
|
||||
uint8_t _Reserved;
|
||||
uint8_t Signature;
|
||||
uint32_t VolumeId; // serial number, value doesn't matter
|
||||
uint8_t VolumeLabel[11]; // 11 bytes, padded with spaces
|
||||
uint8_t SystemId[8];
|
||||
|
||||
// ... we don't care about code ... huh?
|
||||
|
||||
} __attribute__((packed)) FAT_BootSector;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t Buffer[SECTOR_SIZE];
|
||||
FAT_File Public;
|
||||
bool Opened;
|
||||
uint32_t FirstCluster;
|
||||
uint32_t CurrentCluster;
|
||||
uint32_t CurrentSectorInCluster;
|
||||
|
||||
} FAT_FileData;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union
|
||||
{
|
||||
FAT_BootSector BootSector;
|
||||
uint8_t BootSectorBytes[SECTOR_SIZE];
|
||||
} BS;
|
||||
|
||||
FAT_FileData RootDirectory;
|
||||
|
||||
FAT_FileData OpenedFiles[MAX_FILE_HANDLES];
|
||||
|
||||
} FAT_Data;
|
||||
|
||||
static FAT_Data* g_Data;
|
||||
static uint8_t* g_Fat = NULL;
|
||||
static uint32_t g_DataSectionLba;
|
||||
|
||||
|
||||
bool FAT_ReadBootSector(DISK* disk)
|
||||
{
|
||||
return DISK_ReadSectors(disk, 0, 1, g_Data->BS.BootSectorBytes);
|
||||
}
|
||||
|
||||
bool FAT_ReadFat(DISK* disk)
|
||||
{
|
||||
return DISK_ReadSectors(disk, g_Data->BS.BootSector.ReservedSectors, g_Data->BS.BootSector.SectorsPerFat, g_Fat);
|
||||
}
|
||||
|
||||
bool FAT_Initialize(DISK* disk)
|
||||
{
|
||||
g_Data = (FAT_Data*)MEMORY_FAT_ADDR;
|
||||
|
||||
// read boot sector
|
||||
if (!FAT_ReadBootSector(disk))
|
||||
{
|
||||
printf("FAT: read boot sector failed\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// read FAT
|
||||
g_Fat = (uint8_t*)g_Data + sizeof(FAT_Data);
|
||||
uint32_t fatSize = g_Data->BS.BootSector.BytesPerSector * g_Data->BS.BootSector.SectorsPerFat;
|
||||
if (sizeof(FAT_Data) + fatSize >= MEMORY_FAT_SIZE)
|
||||
{
|
||||
printf("FAT: not enough memory to read FAT! Required %lu, only have %u\r\n", sizeof(FAT_Data) + fatSize, MEMORY_FAT_SIZE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FAT_ReadFat(disk))
|
||||
{
|
||||
printf("FAT: read FAT failed\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// open root directory file
|
||||
uint32_t rootDirLba = g_Data->BS.BootSector.ReservedSectors + g_Data->BS.BootSector.SectorsPerFat * g_Data->BS.BootSector.FatCount;
|
||||
uint32_t rootDirSize = sizeof(FAT_DirectoryEntry) * g_Data->BS.BootSector.DirEntryCount;
|
||||
|
||||
g_Data->RootDirectory.Public.Handle = ROOT_DIRECTORY_HANDLE;
|
||||
g_Data->RootDirectory.Public.IsDirectory = true;
|
||||
g_Data->RootDirectory.Public.Position = 0;
|
||||
g_Data->RootDirectory.Public.Size = sizeof(FAT_DirectoryEntry) * g_Data->BS.BootSector.DirEntryCount;
|
||||
g_Data->RootDirectory.Opened = true;
|
||||
g_Data->RootDirectory.FirstCluster = rootDirLba;
|
||||
g_Data->RootDirectory.CurrentCluster = rootDirLba;
|
||||
g_Data->RootDirectory.CurrentSectorInCluster = 0;
|
||||
|
||||
if (!DISK_ReadSectors(disk, rootDirLba, 1, g_Data->RootDirectory.Buffer))
|
||||
{
|
||||
printf("FAT: read root directory failed\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// calculate data section
|
||||
uint32_t rootDirSectors = (rootDirSize + g_Data->BS.BootSector.BytesPerSector - 1) / g_Data->BS.BootSector.BytesPerSector;
|
||||
g_DataSectionLba = rootDirLba + rootDirSectors;
|
||||
|
||||
// reset opened files
|
||||
for (int i = 0; i < MAX_FILE_HANDLES; i++)
|
||||
g_Data->OpenedFiles[i].Opened = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t FAT_ClusterToLba(uint32_t cluster)
|
||||
{
|
||||
return g_DataSectionLba + (cluster - 2) * g_Data->BS.BootSector.SectorsPerCluster;
|
||||
}
|
||||
|
||||
FAT_File* FAT_OpenEntry(DISK* disk, FAT_DirectoryEntry* entry)
|
||||
{
|
||||
// find empty handle
|
||||
int handle = -1;
|
||||
for (int i = 0; i < MAX_FILE_HANDLES && handle < 0; i++)
|
||||
{
|
||||
if (!g_Data->OpenedFiles[i].Opened)
|
||||
handle = i;
|
||||
}
|
||||
|
||||
// out of handles
|
||||
if (handle < 0)
|
||||
{
|
||||
printf("FAT: out of file handles\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// setup vars
|
||||
FAT_FileData* fd = &g_Data->OpenedFiles[handle];
|
||||
fd->Public.Handle = handle;
|
||||
fd->Public.IsDirectory = (entry->Attributes & FAT_ATTRIBUTE_DIRECTORY) != 0;
|
||||
fd->Public.Position = 0;
|
||||
fd->Public.Size = entry->Size;
|
||||
fd->FirstCluster = entry->FirstClusterLow + ((uint32_t)entry->FirstClusterHigh << 16);
|
||||
fd->CurrentCluster = fd->FirstCluster;
|
||||
fd->CurrentSectorInCluster = 0;
|
||||
|
||||
if (!DISK_ReadSectors(disk, FAT_ClusterToLba(fd->CurrentCluster), 1, fd->Buffer))
|
||||
{
|
||||
printf("FAT: open entry failed - read error cluster=%u lba=%u\n", fd->CurrentCluster, FAT_ClusterToLba(fd->CurrentCluster));
|
||||
for (int i = 0; i < 11; i++)
|
||||
printf("%c", entry->Name[i]);
|
||||
printf("\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fd->Opened = true;
|
||||
return &fd->Public;
|
||||
}
|
||||
|
||||
uint32_t FAT_NextCluster(uint32_t currentCluster)
|
||||
{
|
||||
uint32_t fatIndex = currentCluster * 3 / 2;
|
||||
|
||||
if (currentCluster % 2 == 0)
|
||||
return (*(uint16_t*)(g_Fat + fatIndex)) & 0x0FFF;
|
||||
else
|
||||
return (*(uint16_t*)(g_Fat + fatIndex)) >> 4;
|
||||
}
|
||||
|
||||
uint32_t FAT_Read(DISK* disk, FAT_File* file, uint32_t byteCount, void* dataOut)
|
||||
{
|
||||
// get file data
|
||||
FAT_FileData* fd = (file->Handle == ROOT_DIRECTORY_HANDLE)
|
||||
? &g_Data->RootDirectory
|
||||
: &g_Data->OpenedFiles[file->Handle];
|
||||
|
||||
uint8_t* u8DataOut = (uint8_t*)dataOut;
|
||||
|
||||
// don't read past the end of the file
|
||||
if (!fd->Public.IsDirectory || (fd->Public.IsDirectory && fd->Public.Size != 0))
|
||||
byteCount = min(byteCount, fd->Public.Size - fd->Public.Position);
|
||||
|
||||
while (byteCount > 0)
|
||||
{
|
||||
uint32_t leftInBuffer = SECTOR_SIZE - (fd->Public.Position % SECTOR_SIZE);
|
||||
uint32_t take = min(byteCount, leftInBuffer);
|
||||
|
||||
memcpy(u8DataOut, fd->Buffer + fd->Public.Position % SECTOR_SIZE, take);
|
||||
u8DataOut += take;
|
||||
fd->Public.Position += take;
|
||||
byteCount -= take;
|
||||
|
||||
// printf("leftInBuffer=%lu take=%lu\r\n", leftInBuffer, take);
|
||||
// See if we need to read more data
|
||||
if (leftInBuffer == take)
|
||||
{
|
||||
// Special handling for root directory
|
||||
if (fd->Public.Handle == ROOT_DIRECTORY_HANDLE)
|
||||
{
|
||||
++fd->CurrentCluster;
|
||||
|
||||
// read next sector
|
||||
if (!DISK_ReadSectors(disk, fd->CurrentCluster, 1, fd->Buffer))
|
||||
{
|
||||
printf("FAT: read error!\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// calculate next cluster & sector to read
|
||||
if (++fd->CurrentSectorInCluster >= g_Data->BS.BootSector.SectorsPerCluster)
|
||||
{
|
||||
fd->CurrentSectorInCluster = 0;
|
||||
fd->CurrentCluster = FAT_NextCluster(fd->CurrentCluster);
|
||||
}
|
||||
|
||||
if (fd->CurrentCluster >= 0xFF8)
|
||||
{
|
||||
// Mark end of file
|
||||
fd->Public.Size = fd->Public.Position;
|
||||
break;
|
||||
}
|
||||
|
||||
// read next sector
|
||||
if (!DISK_ReadSectors(disk, FAT_ClusterToLba(fd->CurrentCluster) + fd->CurrentSectorInCluster, 1, fd->Buffer))
|
||||
{
|
||||
printf("FAT: read error!\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return u8DataOut - (uint8_t*)dataOut;
|
||||
}
|
||||
|
||||
bool FAT_ReadEntry(DISK* disk, FAT_File* file, FAT_DirectoryEntry* dirEntry)
|
||||
{
|
||||
return FAT_Read(disk, file, sizeof(FAT_DirectoryEntry), dirEntry) == sizeof(FAT_DirectoryEntry);
|
||||
}
|
||||
|
||||
void FAT_Close(FAT_File* file)
|
||||
{
|
||||
if (file->Handle == ROOT_DIRECTORY_HANDLE)
|
||||
{
|
||||
file->Position = 0;
|
||||
g_Data->RootDirectory.CurrentCluster = g_Data->RootDirectory.FirstCluster;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Data->OpenedFiles[file->Handle].Opened = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FAT_FindFile(DISK* disk, FAT_File* file, const char* name, FAT_DirectoryEntry* entryOut)
|
||||
{
|
||||
char fatName[12];
|
||||
FAT_DirectoryEntry entry;
|
||||
|
||||
// convert from name to fat name
|
||||
memset(fatName, ' ', sizeof(fatName));
|
||||
fatName[11] = '\0';
|
||||
|
||||
const char* ext = strchr(name, '.');
|
||||
if (ext == NULL)
|
||||
ext = name + 11;
|
||||
|
||||
for (int i = 0; i < 8 && name[i] && name + i < ext; i++)
|
||||
fatName[i] = toupper(name[i]);
|
||||
|
||||
if (ext != name + 11)
|
||||
{
|
||||
for (int i = 0; i < 3 && ext[i + 1]; i++)
|
||||
fatName[i + 8] = toupper(ext[i + 1]);
|
||||
}
|
||||
|
||||
while (FAT_ReadEntry(disk, file, &entry))
|
||||
{
|
||||
if (memcmp(fatName, entry.Name, 11) == 0)
|
||||
{
|
||||
*entryOut = entry;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FAT_File* FAT_Open(DISK* disk, const char* path)
|
||||
{
|
||||
char name[MAX_PATH_SIZE];
|
||||
|
||||
// ignore leading slash
|
||||
if (path[0] == '/')
|
||||
path++;
|
||||
|
||||
FAT_File* current = &g_Data->RootDirectory.Public;
|
||||
|
||||
while (*path) {
|
||||
// extract next file name from path
|
||||
bool isLast = false;
|
||||
const char* delim = strchr(path, '/');
|
||||
printf("DELIM: %s\r\n", delim);
|
||||
if (delim != NULL)
|
||||
{
|
||||
memcpy(name, path, delim - path);
|
||||
name[delim - path + 1] = '\0';
|
||||
path = delim + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned len = strlen(path);
|
||||
memcpy(name, path, len);
|
||||
name[len + 1] = '\0';
|
||||
path += len;
|
||||
isLast = true;
|
||||
}
|
||||
|
||||
// find directory entry in current directory
|
||||
FAT_DirectoryEntry entry;
|
||||
if (FAT_FindFile(disk, current, name, &entry))
|
||||
{
|
||||
FAT_Close(current);
|
||||
|
||||
// check if directory
|
||||
if (!isLast && entry.Attributes & FAT_ATTRIBUTE_DIRECTORY == 0)
|
||||
{
|
||||
printf("FAT: %s not a directory\r\n", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// open new directory entry
|
||||
current = FAT_OpenEntry(disk, &entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
FAT_Close(current);
|
||||
|
||||
printf("FAT: %s not found\r\n", name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include "disk.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t Name[11];
|
||||
uint8_t Attributes;
|
||||
uint8_t _Reserved;
|
||||
uint8_t CreatedTimeTenths;
|
||||
uint16_t CreatedTime;
|
||||
uint16_t CreatedDate;
|
||||
uint16_t AccessedDate;
|
||||
uint16_t FirstClusterHigh;
|
||||
uint16_t ModifiedTime;
|
||||
uint16_t ModifiedDate;
|
||||
uint16_t FirstClusterLow;
|
||||
uint32_t Size;
|
||||
|
||||
} __attribute__((packed)) FAT_DirectoryEntry;
|
||||
|
||||
|
||||
typedef struct {
|
||||
int Handle;
|
||||
bool IsDirectory;
|
||||
uint32_t Position;
|
||||
uint32_t Size;
|
||||
} FAT_File;
|
||||
|
||||
enum FAT_Attributes {
|
||||
FAT_ATTRIBUTE_READ_ONLY = 0x01,
|
||||
FAT_ATTRIBUTE_HIDDEN = 0x02,
|
||||
FAT_ATTRIBUTE_SYSTEM = 0x04,
|
||||
FAT_ATTRIBUTE_VOLUME_ID = 0x08,
|
||||
FAT_ATTRIBUTE_DIRECTORY = 0x10,
|
||||
FAT_ATTRIBUTE_ARCHIVE = 0x20,
|
||||
FAT_ATTRIBUTE_LFN = FAT_ATTRIBUTE_READ_ONLY | FAT_ATTRIBUTE_HIDDEN | FAT_ATTRIBUTE_SYSTEM | FAT_ATTRIBUTE_VOLUME_ID
|
||||
};
|
||||
|
||||
|
||||
bool FAT_Initialize(DISK* disk);
|
||||
FAT_File* FAT_Open(DISK* disk, const char* path);
|
||||
uint32_t FAT_Read(DISK* disk, FAT_File* file, uint32_t byteCount, void* dataOut);
|
||||
bool FAT_ReadEntry(DISK* disk, FAT_File* file, FAT_DirectoryEntry* dirEntry);
|
||||
void FAT_Close(FAT_File* file);
|
||||
@ -1,15 +0,0 @@
|
||||
ENTRY(entry)
|
||||
OUTPUT_FORMAT("binary")
|
||||
phys = 0x00000500;
|
||||
|
||||
SECTIONS {
|
||||
. = phys;
|
||||
|
||||
.entry : { __entry_start = .; *(.entry) }
|
||||
.text : { __text_start = .; *(.text) }
|
||||
.data : { __data_start = .; *(.data) }
|
||||
.rodata : { __rodata_start = .; *(.rodata) }
|
||||
.bss : { __bss_start = .; *(.bss) }
|
||||
|
||||
__end = .;
|
||||
}
|
||||
@ -1,135 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "stdio.h"
|
||||
#include "x86.h"
|
||||
#include "disk.h"
|
||||
#include "fat.h"
|
||||
#include "string.h"
|
||||
#include "memdefs.h"
|
||||
#include "memory.h"
|
||||
#include "memdetect.h"
|
||||
#include "../../libs/version.h"
|
||||
#include "../../libs/boot/bootparams.h"
|
||||
|
||||
|
||||
|
||||
uint8_t* KernelLoadBuffer = (uint8_t*)MEMORY_LOAD_KERNEL;
|
||||
uint8_t* Kernel = (uint8_t*)MEMORY_KERNEL_ADDR;
|
||||
BootParams g_BootParams;
|
||||
|
||||
|
||||
typedef void (*KernelStart)(BootParams* bootParams);
|
||||
|
||||
void* g_data = (void*)0x20000;
|
||||
|
||||
void puts_realmode(const char* str) {
|
||||
while (*str) {
|
||||
x86_realmode_putc(*str);
|
||||
++str;
|
||||
}
|
||||
}
|
||||
|
||||
void __attribute__((cdecl)) start(uint16_t bootDrive) {
|
||||
// Clear screen and Print Startup logo
|
||||
clrscr();
|
||||
printf("%s", BOOTLOGO);
|
||||
printf("The Nano Loader %s\n------------------------------\n", VERSION);
|
||||
|
||||
// Test Real Mode
|
||||
printf("Testing Real Mode...");
|
||||
puts_realmode(" ");
|
||||
printf("Ok!\n");
|
||||
|
||||
// get drive params (Test for Disk driver)
|
||||
printf("Getting Drive Params...");
|
||||
uint8_t driveType;
|
||||
uint16_t cyls, sects, heads;
|
||||
x86_Disk_GetDriveParams(bootDrive, &driveType, &cyls, §s, &heads);
|
||||
printf("Done!\n");
|
||||
printf("> Drive Type: %u\n> Cylinders: %u\n> Sectors: %u\n> Heads: %u\n", driveType, cyls, sects, heads);
|
||||
|
||||
// init disk
|
||||
printf("Initializing Disk...");
|
||||
DISK disk;
|
||||
if (!DISK_Initialize(&disk, bootDrive)) {
|
||||
printf("Failed!\nDisk Initialization Error!\n");
|
||||
goto end;
|
||||
}
|
||||
printf("Done!\n");
|
||||
DISK_ReadSectors(&disk, 0, 1, g_data);
|
||||
// print_buffer("> Boot Sector Data: ", g_data, 512);
|
||||
|
||||
printf("Initializing FAT Driver...");
|
||||
if (!FAT_Initialize(&disk)) {
|
||||
printf("Failed!\nDisk Initialization Error!\n");
|
||||
goto end;
|
||||
}
|
||||
printf("Done!\n");
|
||||
|
||||
// test fat driver
|
||||
printf("Testing FAT Driver...");
|
||||
// read test.txt
|
||||
FAT_File* ft = FAT_Open(&disk, "/test.x");
|
||||
char buffer[100];
|
||||
uint32_t testread;
|
||||
while ((testread = FAT_Read(&disk, ft, sizeof(buffer), buffer)))
|
||||
{
|
||||
for (uint32_t i = 0; i < testread; i++)
|
||||
{
|
||||
if (buffer[i] == '\n')
|
||||
putc('\r');
|
||||
putc(buffer[i]);
|
||||
}
|
||||
}
|
||||
FAT_Close(ft);
|
||||
|
||||
printf("Detecting Memory...");
|
||||
// prep boot params
|
||||
g_BootParams.BootDevice = bootDrive;
|
||||
Memory_Detect(&g_BootParams.Memory);
|
||||
printf("Done!\n");
|
||||
// kernel params...
|
||||
FAT_File* kp = FAT_Open(&disk, "/kparams.x");
|
||||
char* kparams;
|
||||
uint32_t kernelparams;
|
||||
char* buffer2[100];
|
||||
while ((kernelparams = FAT_Read(&disk, kp, sizeof(buffer2), buffer2)))
|
||||
{
|
||||
for (uint32_t i = 0; i < kernelparams; i++)
|
||||
{
|
||||
strcpy(kparams, buffer2[i]);
|
||||
}
|
||||
}
|
||||
FAT_Close(kp);
|
||||
printf("Kernel Params: %s\n", kparams);
|
||||
g_BootParams.KernelParams = kparams;
|
||||
|
||||
// load kernel from disk
|
||||
printf("Loading Kernel...");
|
||||
FAT_File* fd = FAT_Open(&disk, "/kernel.bin"); // move to /boot later????? (TM)
|
||||
uint32_t read;
|
||||
uint8_t* kernelBuffer = Kernel;
|
||||
while ((read = FAT_Read(&disk, fd, MEMORY_LOAD_SIZE, KernelLoadBuffer)))
|
||||
{
|
||||
memcpy(kernelBuffer, KernelLoadBuffer, read);
|
||||
kernelBuffer += read;
|
||||
}
|
||||
FAT_Close(fd);
|
||||
|
||||
// execute kernel
|
||||
KernelStart kernelStart = (KernelStart)Kernel;
|
||||
kernelStart(&g_BootParams);
|
||||
|
||||
|
||||
|
||||
|
||||
end:
|
||||
for (;;);
|
||||
}
|
||||
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#pragma once
|
||||
|
||||
// 0x00000000 - 0x000003FF - interrupt vector table
|
||||
// 0x00000400 - 0x000004FF - BIOS data area
|
||||
|
||||
#define MEMORY_MIN 0x00000500
|
||||
#define MEMORY_MAX 0x00080000
|
||||
|
||||
// 0x00000500 - 0x00010500 - FAT driver
|
||||
#define MEMORY_FAT_ADDR ((void*)0x20000)
|
||||
#define MEMORY_FAT_SIZE 0x00010000
|
||||
|
||||
#define MEMORY_LOAD_KERNEL ((void*)0x30000)
|
||||
#define MEMORY_LOAD_SIZE 0x00010000
|
||||
|
||||
// 0x00020000 - 0x00030000 - stage2
|
||||
|
||||
// 0x00030000 - 0x00080000 - free
|
||||
|
||||
// 0x00080000 - 0x0009FFFF - Extended BIOS data area
|
||||
// 0x000A0000 - 0x000C7FFF - Video
|
||||
// 0x000C8000 - 0x000FFFFF - BIOS
|
||||
|
||||
#define MEMORY_KERNEL_ADDR ((void*)0x100000)
|
||||
@ -1,40 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2025|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#include "x86.h"
|
||||
#include "stdio.h"
|
||||
#include "../../libs/boot/bootparams.h"
|
||||
|
||||
#define MAX_REGIONS 256
|
||||
|
||||
MemoryRegion g_MemRegions[MAX_REGIONS];
|
||||
int g_MemRegionCount;
|
||||
|
||||
void Memory_Detect(MemoryInfo* memoryInfo)
|
||||
{
|
||||
E820MemoryBlock block;
|
||||
uint32_t continuation = 0;
|
||||
int ret;
|
||||
|
||||
g_MemRegionCount = 0;
|
||||
ret = x86_E820GetNextBlock(&block, &continuation);
|
||||
|
||||
while (ret > 0 && continuation != 0)
|
||||
{
|
||||
g_MemRegions[g_MemRegionCount].Begin = block.Base;
|
||||
g_MemRegions[g_MemRegionCount].Length = block.Length;
|
||||
g_MemRegions[g_MemRegionCount].Type = block.Type;
|
||||
g_MemRegions[g_MemRegionCount].ACPI = block.ACPI;
|
||||
++g_MemRegionCount;
|
||||
|
||||
// printf("E820: base=0x%llx length=0x%llx type=0x%x\n", block.Base, block.Length, block.Type);
|
||||
// MAKE IT SHUT THE FUCK UP
|
||||
ret = x86_E820GetNextBlock(&block, &continuation);
|
||||
}
|
||||
|
||||
// fill meminfo structure
|
||||
memoryInfo->RegionCount = g_MemRegionCount;
|
||||
memoryInfo->Regions = g_MemRegions;
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#pragma once
|
||||
|
||||
#include "../../libs/boot/bootparams.h"
|
||||
|
||||
void Memory_Detect(MemoryInfo* memoryInfo);
|
||||
@ -1,36 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#include "memory.h"
|
||||
|
||||
void* memcpy(void* dst, const void* src, uint16_t num) {
|
||||
uint8_t* u8Dst = (uint8_t *)dst;
|
||||
const uint8_t* u8Src = (const uint8_t *)src;
|
||||
|
||||
for (uint16_t i = 0; i < num; i++)
|
||||
u8Dst[i] = u8Src[i];
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
void* memset (void * ptr, int value, uint16_t num) {
|
||||
uint8_t* u8Ptr = (uint8_t *)ptr;
|
||||
|
||||
for (uint16_t i = 0; i < num; i++)
|
||||
u8Ptr[i] = (uint8_t)value;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
int memcmp(const void* ptr1, const void * ptr2, uint16_t num) {
|
||||
const uint8_t* u8Ptr1 = (const uint8_t *)ptr1;
|
||||
const uint8_t* u8Ptr2 = (const uint8_t *)ptr2;
|
||||
|
||||
for (uint16_t i = 0; i < num; i++)
|
||||
if (u8Ptr1[i] != u8Ptr2[i])
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
void* memcpy(void* dst, const void* src, uint16_t num);
|
||||
void* memset(void* ptr, int value, uint16_t num);
|
||||
int memcmp(const void* ptr1, const void * ptr2, uint16_t num);
|
||||
@ -1,9 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#pragma once
|
||||
|
||||
#define min(a,b) ((a) < (b) ? (a) : (b))
|
||||
#define max(a,b) ((a) > (b) ? (a) : (b))
|
||||
@ -1,318 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#include "stdio.h"
|
||||
#include "x86.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
const unsigned SCREEN_WIDTH = 80;
|
||||
const unsigned SCREEN_HEIGHT = 25;
|
||||
const uint8_t DEFAULT_COLOUR = 0x7;
|
||||
|
||||
uint8_t* g_ScreenBuffer = (uint8_t*)0xB8000;
|
||||
int g_ScreenX = 0, g_ScreenY = 0;
|
||||
|
||||
void putchr(int x, int y, char c)
|
||||
{
|
||||
g_ScreenBuffer[2 * (y * SCREEN_WIDTH + x)] = c;
|
||||
}
|
||||
|
||||
void putcolour(int x, int y, uint8_t colour)
|
||||
{
|
||||
g_ScreenBuffer[2 * (y * SCREEN_WIDTH + x) + 1] = colour;
|
||||
}
|
||||
|
||||
char getchr(int x, int y)
|
||||
{
|
||||
return g_ScreenBuffer[2 * (y * SCREEN_WIDTH + x)];
|
||||
}
|
||||
|
||||
uint8_t getcolour(int x, int y)
|
||||
{
|
||||
return g_ScreenBuffer[2 * (y * SCREEN_WIDTH + x) + 1];
|
||||
}
|
||||
|
||||
void setcursor(int x, int y)
|
||||
{
|
||||
int pos = y * SCREEN_WIDTH + x;
|
||||
|
||||
x86_outb(0x3D4, 0x0F);
|
||||
x86_outb(0x3D5, (uint8_t)(pos & 0xFF));
|
||||
x86_outb(0x3D4, 0x0E);
|
||||
x86_outb(0x3D5, (uint8_t)((pos >> 8) & 0xFF));
|
||||
}
|
||||
|
||||
void clrscr()
|
||||
{
|
||||
for (int y = 0; y < SCREEN_HEIGHT; y++)
|
||||
for (int x = 0; x < SCREEN_WIDTH; x++)
|
||||
{
|
||||
putchr(x, y, '\0');
|
||||
putcolour(x, y, DEFAULT_COLOUR);
|
||||
}
|
||||
g_ScreenX =0; g_ScreenY = 0;
|
||||
setcursor(g_ScreenX, g_ScreenY);
|
||||
}
|
||||
|
||||
void scrollback(int lines)
|
||||
{
|
||||
for (int y = lines; y < SCREEN_HEIGHT; y++)
|
||||
for (int x = 0; x < SCREEN_WIDTH; x++)
|
||||
{
|
||||
putchr(x, y - lines, getchr(x, y));
|
||||
putcolour(x, y - lines, getcolour(x, y));
|
||||
}
|
||||
|
||||
for (int y= SCREEN_HEIGHT - lines; y < SCREEN_HEIGHT; y++)
|
||||
for (int x = 0; x < SCREEN_WIDTH; x++)
|
||||
{
|
||||
putchr(x, y - lines, getchr(x, y));
|
||||
putcolour(x, y - lines, getcolour(x, y));
|
||||
}
|
||||
g_ScreenY -= lines;
|
||||
}
|
||||
|
||||
void putc(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
g_ScreenX = 0;
|
||||
g_ScreenY++;
|
||||
break;
|
||||
case '\t':
|
||||
for (int i = 0; i < 4 - (g_ScreenX % 4); i++)
|
||||
putc(' ');
|
||||
break;
|
||||
case '\r':
|
||||
g_ScreenX = 0;
|
||||
break;
|
||||
default:
|
||||
putchr(g_ScreenX, g_ScreenY, c);
|
||||
g_ScreenX++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (g_ScreenX >= SCREEN_WIDTH)
|
||||
{
|
||||
g_ScreenY++;
|
||||
g_ScreenX = 0;
|
||||
}
|
||||
if (g_ScreenY >= SCREEN_HEIGHT)
|
||||
scrollback(1);
|
||||
|
||||
setcursor(g_ScreenX, g_ScreenY);
|
||||
|
||||
}
|
||||
|
||||
void puts(const char* str)
|
||||
{
|
||||
while(*str)
|
||||
{
|
||||
putc(*str);
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
const char g_HexChars[] = "0123456789abcdef";
|
||||
|
||||
|
||||
void printf_unsigned(unsigned long long number, int radix)
|
||||
{
|
||||
char buffer[32];
|
||||
int pos = 0;
|
||||
|
||||
//number to ASCII conversion
|
||||
do {
|
||||
unsigned long long rem = number % radix;
|
||||
number /= radix;
|
||||
buffer[pos++] = g_HexChars[rem];
|
||||
} while (number > 0);
|
||||
|
||||
//print number in reverse order
|
||||
while (--pos >= 0)
|
||||
putc(buffer[pos]);
|
||||
}
|
||||
|
||||
void printf_signed(long long number, int radix)
|
||||
{
|
||||
if (number < 0)
|
||||
{
|
||||
putc('-');
|
||||
printf_unsigned(-number, radix);
|
||||
}
|
||||
else printf_unsigned(number, radix);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define PRINTF_STATE_NORMAL 0
|
||||
#define PRINTF_STATE_LENGTH 1
|
||||
#define PRINTF_STATE_LENGTH_SHORT 2
|
||||
#define PRINTF_STATE_LENGTH_LONG 3
|
||||
#define PRINTF_STATE_SPEC 4
|
||||
|
||||
#define PRINTF_LENGTH_DEFAULT 0
|
||||
#define PRINTF_LENGTH_SHORT_SHORT 1
|
||||
#define PRINTF_LENGTH_SHORT 2
|
||||
#define PRINTF_LENGTH_LONG 3
|
||||
#define PRINTF_LENGTH_LONG_LONG 4
|
||||
|
||||
void printf(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
int state = PRINTF_STATE_NORMAL;
|
||||
int length = PRINTF_LENGTH_DEFAULT;
|
||||
int radix = 10;
|
||||
bool sign = false;
|
||||
bool number = false;
|
||||
|
||||
while (*fmt)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PRINTF_STATE_NORMAL:
|
||||
switch (*fmt)
|
||||
{
|
||||
case '%': state = PRINTF_STATE_LENGTH;
|
||||
break;
|
||||
default: putc(*fmt);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case PRINTF_STATE_LENGTH:
|
||||
switch (*fmt)
|
||||
{
|
||||
case 'h': length = PRINTF_LENGTH_SHORT;
|
||||
state = PRINTF_STATE_LENGTH_SHORT;
|
||||
break;
|
||||
case 'l': length = PRINTF_LENGTH_LONG;
|
||||
state = PRINTF_STATE_LENGTH_LONG;
|
||||
break;
|
||||
default: goto PRINTF_STATE_SPEC_;
|
||||
}
|
||||
break;
|
||||
|
||||
case PRINTF_STATE_LENGTH_SHORT:
|
||||
if (*fmt == 'h')
|
||||
{
|
||||
length = PRINTF_LENGTH_SHORT_SHORT;
|
||||
state = PRINTF_STATE_SPEC;
|
||||
}
|
||||
else goto PRINTF_STATE_SPEC_;
|
||||
break;
|
||||
|
||||
case PRINTF_STATE_LENGTH_LONG:
|
||||
if (*fmt == 'l')
|
||||
{
|
||||
length = PRINTF_LENGTH_LONG_LONG;
|
||||
state = PRINTF_STATE_SPEC;
|
||||
}
|
||||
else goto PRINTF_STATE_SPEC_;
|
||||
break;
|
||||
|
||||
case PRINTF_STATE_SPEC:
|
||||
PRINTF_STATE_SPEC_:
|
||||
switch (*fmt)
|
||||
{
|
||||
case 'c': putc((char)va_arg(args, int));
|
||||
break;
|
||||
|
||||
case 's':
|
||||
puts(va_arg(args, const char*));
|
||||
break;
|
||||
|
||||
case '%': putc('%');
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
case 'i': radix = 10; sign = true; number = true;
|
||||
break;
|
||||
|
||||
case 'u': radix = 10; sign = false; number = true;
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
case 'x':
|
||||
case 'p': radix = 16; sign = false; number = true;
|
||||
break;
|
||||
|
||||
case 'o': radix = 8; sign = false; number = true;
|
||||
break;
|
||||
|
||||
// ignore invalid spec
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (number)
|
||||
{
|
||||
if (sign)
|
||||
{
|
||||
switch (length)
|
||||
{
|
||||
case PRINTF_LENGTH_SHORT_SHORT:
|
||||
case PRINTF_LENGTH_SHORT:
|
||||
case PRINTF_LENGTH_DEFAULT: printf_signed(va_arg(args, int), radix);
|
||||
break;
|
||||
|
||||
case PRINTF_LENGTH_LONG: printf_signed(va_arg(args, long), radix);
|
||||
break;
|
||||
|
||||
case PRINTF_LENGTH_LONG_LONG: printf_signed(va_arg(args, long long), radix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (length)
|
||||
{
|
||||
case PRINTF_LENGTH_SHORT_SHORT:
|
||||
case PRINTF_LENGTH_SHORT:
|
||||
case PRINTF_LENGTH_DEFAULT: printf_unsigned(va_arg(args, unsigned int), radix);
|
||||
break;
|
||||
|
||||
case PRINTF_LENGTH_LONG: printf_unsigned(va_arg(args, unsigned long), radix);
|
||||
break;
|
||||
|
||||
case PRINTF_LENGTH_LONG_LONG: printf_unsigned(va_arg(args, unsigned long long), radix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset state
|
||||
state = PRINTF_STATE_NORMAL;
|
||||
length = PRINTF_LENGTH_DEFAULT;
|
||||
radix = 10;
|
||||
sign = false;
|
||||
number = false;
|
||||
break;
|
||||
}
|
||||
|
||||
fmt++;
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void print_buffer(const char* msg, const void* buffer, uint32_t count)
|
||||
{
|
||||
const uint8_t* u8Buffer = (const uint8_t*)buffer;
|
||||
|
||||
puts(msg);
|
||||
for (uint16_t i = 0; i < count; i++)
|
||||
{
|
||||
putc(g_HexChars[u8Buffer[i] >> 4]);
|
||||
putc(g_HexChars[u8Buffer[i] & 0xF]);
|
||||
}
|
||||
puts("\n");
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
void clrscr();
|
||||
void putc(char c);
|
||||
void puts(const char* str);
|
||||
void printf(const char* fmt, ...);
|
||||
void print_buffer(const char* msg, const void* buffer, uint32_t count);
|
||||
@ -1,50 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#include "string.h"
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
const char* strchr(const char* str, char chr) {
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
while (*str) {
|
||||
if (*str == chr)
|
||||
return str;
|
||||
|
||||
++str;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* strcpy(char* dst, const char* src) {
|
||||
char* origDst = dst;
|
||||
|
||||
if (dst == NULL)
|
||||
return NULL;
|
||||
|
||||
if (src == NULL) {
|
||||
*dst = '\0';
|
||||
return dst;
|
||||
}
|
||||
while (*src) {
|
||||
*dst = *src;
|
||||
++src;
|
||||
++dst;
|
||||
}
|
||||
|
||||
*dst = '\0';
|
||||
return origDst;
|
||||
}
|
||||
|
||||
unsigned strlen(const char* str) {
|
||||
unsigned len = 0;
|
||||
while (*str) {
|
||||
++len; ++str;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#pragma once
|
||||
|
||||
const char* strchr(const char* str, char chr);
|
||||
char* strcpy(char* dst, const char* src);
|
||||
unsigned strlen(const char* str);
|
||||
@ -1,350 +0,0 @@
|
||||
;/////////////////////;
|
||||
;Nanite OS ;
|
||||
;COPYRIGHT (C) 2024 ;
|
||||
;Tyler McGurrin ;
|
||||
;/////////////////////;
|
||||
%macro x86_EnterRealMode 0
|
||||
[bits 32]
|
||||
jmp word 18h:.pmode16 ; 1 - jump to 16-bit protected mode segment
|
||||
|
||||
.pmode16:
|
||||
[bits 16]
|
||||
; 2 - disable protected mode bit in cr0
|
||||
mov eax, cr0
|
||||
and al, ~1
|
||||
mov cr0, eax
|
||||
|
||||
; 3 - jump to real mode
|
||||
jmp word 00h:.rmode
|
||||
|
||||
.rmode:
|
||||
; 4 - setup segments
|
||||
mov ax, 0
|
||||
mov ds, ax
|
||||
mov ss, ax
|
||||
|
||||
; 5 - enable interrupts
|
||||
sti
|
||||
|
||||
%endmacro
|
||||
|
||||
%macro x86_EnterProtectedMode 0
|
||||
cli
|
||||
; set PMODE enable flag in CR0
|
||||
mov eax, CR0
|
||||
or al, 1
|
||||
mov cr0, eax
|
||||
|
||||
; far jmp into PMODE
|
||||
jmp dword 08h:.pmode
|
||||
|
||||
.pmode:
|
||||
; we are now in protected mode!
|
||||
[bits 32]
|
||||
|
||||
; 6 - setup segment registers
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov ss, ax
|
||||
|
||||
|
||||
%endmacro
|
||||
|
||||
; Convert linear ADDR to seg:offset addr
|
||||
; Args:
|
||||
; 1 - linear addr
|
||||
; 2 - (OUT) target seg (ex. ES)
|
||||
; 3 - target 32 bit reg to use (ex. EAX)
|
||||
; 4 - target lower 16 bit half of 3 (ex. AX)
|
||||
|
||||
%macro LinearToSegOffset 4
|
||||
|
||||
mov %3, %1 ;linear addr to EAX
|
||||
shr %3, 4
|
||||
mov %2, %4
|
||||
mov %3, %1 ;linear addr to EAX
|
||||
and %3, 0xF
|
||||
|
||||
%endmacro
|
||||
|
||||
|
||||
global x86_outb
|
||||
x86_outb:
|
||||
[bits 32]
|
||||
mov dx, [esp + 4]
|
||||
mov al, [esp + 8]
|
||||
out dx, al
|
||||
ret
|
||||
|
||||
global x86_inb
|
||||
x86_inb:
|
||||
[bits 32]
|
||||
mov dx, [esp + 4]
|
||||
xor eax, eax
|
||||
in al, dx
|
||||
ret
|
||||
|
||||
;
|
||||
; bool _cdecl x86_Disk_GetDriveParams(uint8_t drive,
|
||||
; uint8_t* driveTypeOut,
|
||||
; uint16_t* cylindersOut,
|
||||
; uint16_t* sectorsOut,
|
||||
; uint16_t* headsOut);
|
||||
;
|
||||
global x86_Disk_GetDriveParams
|
||||
x86_Disk_GetDriveParams:
|
||||
[bits 32]
|
||||
|
||||
; make new call frame
|
||||
push ebp ; save old call frame
|
||||
mov ebp, esp ; initialize new call frame
|
||||
|
||||
x86_EnterRealMode
|
||||
|
||||
[bits 16]
|
||||
|
||||
; save regs
|
||||
push es
|
||||
push bx
|
||||
push esi
|
||||
push di
|
||||
|
||||
; call int13h
|
||||
mov dl, [bp + 8] ; dl - disk drive
|
||||
mov ah, 08h
|
||||
mov di, 0 ; es:di - 0000:0000
|
||||
mov es, di
|
||||
stc
|
||||
int 13h
|
||||
|
||||
; out params
|
||||
mov eax, 1
|
||||
sbb eax, 0
|
||||
|
||||
; drive type from bl
|
||||
LinearToSegOffset [bp + 12], es, esi, si
|
||||
mov [es:si], bl
|
||||
|
||||
; cylinders
|
||||
mov bl, ch ; cylinders - lower bits in ch
|
||||
mov bh, cl ; cylinders - upper bits in cl (6-7)
|
||||
shr bh, 6
|
||||
inc bx
|
||||
|
||||
LinearToSegOffset [bp + 16], es, esi, si
|
||||
mov [es:si], bx
|
||||
|
||||
; sectors
|
||||
xor ch, ch ; sectors - lower 5 bits in cl
|
||||
and cl, 3Fh
|
||||
|
||||
LinearToSegOffset [bp + 20], es, esi, si
|
||||
mov [es:si], cx
|
||||
|
||||
; heads
|
||||
mov cl, dh ; heads - dh
|
||||
inc cx
|
||||
|
||||
LinearToSegOffset [bp + 24], es, esi, si
|
||||
mov [es:si], cx
|
||||
|
||||
; restore regs
|
||||
pop di
|
||||
pop esi
|
||||
pop bx
|
||||
pop es
|
||||
|
||||
; return
|
||||
|
||||
push eax
|
||||
|
||||
x86_EnterProtectedMode
|
||||
|
||||
[bits 32]
|
||||
|
||||
pop eax
|
||||
|
||||
; restore old call frame
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
|
||||
global x86_Disk_Reset
|
||||
x86_Disk_Reset:
|
||||
|
||||
; make new call frame
|
||||
push ebp ; save old call frame
|
||||
mov ebp, esp ; initialize new call frame
|
||||
|
||||
x86_EnterRealMode
|
||||
|
||||
mov ah, 0
|
||||
mov dl, [bp + 8] ; dl - drive
|
||||
stc
|
||||
int 13h
|
||||
|
||||
mov eax, 1
|
||||
sbb eax, 0 ; 1 on success, 0 on fail
|
||||
|
||||
push eax
|
||||
|
||||
x86_EnterProtectedMode
|
||||
|
||||
pop eax
|
||||
|
||||
; restore old call frame
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
|
||||
;
|
||||
; bool _cdecl x86_Disk_Read(uint8_t drive,
|
||||
; uint16_t cylinder,
|
||||
; uint16_t sector,
|
||||
; uint16_t head,
|
||||
; uint8_t count,
|
||||
; void far * dataOut);
|
||||
;
|
||||
global x86_Disk_Read
|
||||
x86_Disk_Read:
|
||||
|
||||
; make new call frame
|
||||
push ebp ; save old call frame
|
||||
mov ebp, esp ; initialize new call frame
|
||||
|
||||
x86_EnterRealMode
|
||||
|
||||
; save modified regs
|
||||
push ebx
|
||||
push es
|
||||
|
||||
; setup args
|
||||
mov dl, [bp + 8] ; dl - drive
|
||||
|
||||
mov ch, [bp + 12] ; ch - cylinder (lower 8 bits)
|
||||
mov cl, [bp + 13] ; cl - cylinder to bits 6-7
|
||||
shl cl, 6
|
||||
|
||||
mov al, [bp + 16] ; cl - sector to bits 0-5
|
||||
and al, 3Fh
|
||||
or cl, al
|
||||
|
||||
mov dh, [bp + 20] ; dh - head
|
||||
|
||||
mov al, [bp + 24] ; al - count
|
||||
|
||||
LinearToSegOffset [bp + 28], es, ebx, bx
|
||||
|
||||
; call int13h
|
||||
mov ah, 02h
|
||||
stc
|
||||
int 13h
|
||||
|
||||
; set return value
|
||||
mov eax, 1
|
||||
sbb eax, 0 ; 1 on success, 0 on fail
|
||||
|
||||
; restore regs
|
||||
pop es
|
||||
pop ebx
|
||||
|
||||
push eax
|
||||
|
||||
x86_EnterProtectedMode
|
||||
|
||||
pop eax
|
||||
|
||||
; restore old call frame
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
|
||||
|
||||
; Testing Function
|
||||
global x86_realmode_putc
|
||||
x86_realmode_putc:
|
||||
; setup stack frame
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
|
||||
x86_EnterRealMode
|
||||
|
||||
mov al, [bp + 8]
|
||||
mov ah, 0xe
|
||||
int 10h
|
||||
|
||||
x86_EnterProtectedMode
|
||||
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
|
||||
;
|
||||
; int ASMCALL x86_E820GetNextBlock(E820MemoryBlock* block, uint32_t* continuationId);
|
||||
;
|
||||
E820Signature equ 0x534D4150
|
||||
|
||||
global x86_E820GetNextBlock
|
||||
x86_E820GetNextBlock:
|
||||
|
||||
; make new call frame
|
||||
push ebp ; save old call frame
|
||||
mov ebp, esp ; initialize new call frame
|
||||
|
||||
x86_EnterRealMode
|
||||
|
||||
; save modified regs
|
||||
push ebx
|
||||
push ecx
|
||||
push edx
|
||||
push esi
|
||||
push edi
|
||||
push ds
|
||||
push es
|
||||
|
||||
; setup params
|
||||
LinearToSegOffset [bp + 8], es, edi, di ; es:di pointer to structure
|
||||
|
||||
LinearToSegOffset [bp + 12], ds, esi, si ; ebx - pointer to continuationId
|
||||
mov ebx, ds:[si]
|
||||
|
||||
mov eax, 0xE820 ; eax - function
|
||||
mov edx, E820Signature ; edx - signature
|
||||
mov ecx, 24 ; ecx - size of structure
|
||||
|
||||
; call interrupt
|
||||
int 0x15
|
||||
|
||||
; test results
|
||||
cmp eax, E820Signature
|
||||
jne .Error
|
||||
|
||||
.IfSuccedeed:
|
||||
mov eax, ecx ; return size
|
||||
mov ds:[si], ebx ; fill continuation parameter
|
||||
jmp .EndIf
|
||||
|
||||
.Error:
|
||||
mov eax, -1
|
||||
|
||||
.EndIf:
|
||||
|
||||
; restore regs
|
||||
pop es
|
||||
pop ds
|
||||
pop edi
|
||||
pop esi
|
||||
pop edx
|
||||
pop ecx
|
||||
pop ebx
|
||||
|
||||
push eax
|
||||
|
||||
x86_EnterProtectedMode
|
||||
|
||||
pop eax
|
||||
|
||||
; restore old call frame
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
@ -1,52 +0,0 @@
|
||||
/*----------------*\
|
||||
|Nanite OS |
|
||||
|Copyright (C) 2024|
|
||||
|Tyler McGurrin |
|
||||
\*----------------*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define ASMCALL __attribute__((cdecl))
|
||||
|
||||
|
||||
void ASMCALL x86_outb(uint16_t port, uint8_t value);
|
||||
uint8_t ASMCALL x86_inb(uint16_t port);
|
||||
|
||||
void ASMCALL x86_realmode_putc(char c);
|
||||
|
||||
bool ASMCALL x86_Disk_GetDriveParams(uint8_t drive,
|
||||
uint8_t* driveTypeOut,
|
||||
uint16_t* cylindersOut,
|
||||
uint16_t* sectorsOut,
|
||||
uint16_t* headsOut);
|
||||
|
||||
bool ASMCALL x86_Disk_Reset(uint8_t drive);
|
||||
|
||||
bool ASMCALL x86_Disk_Read(uint8_t drive,
|
||||
uint16_t cylinder,
|
||||
uint16_t sector,
|
||||
uint16_t head,
|
||||
uint8_t count,
|
||||
void* lowerDataOut);
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint64_t Base;
|
||||
uint64_t Length;
|
||||
uint32_t Type;
|
||||
uint32_t ACPI;
|
||||
|
||||
} E820MemoryBlock;
|
||||
|
||||
enum E820MemoryBlockType {
|
||||
|
||||
E820_USABLE = 1,
|
||||
E820_RESERVED = 2,
|
||||
E820_ACPI_RECLAIMABLE = 3,
|
||||
E820_ACPI_NVS = 4,
|
||||
E820_BAD_MEMORY = 5,
|
||||
};
|
||||
|
||||
int ASMCALL x86_E820GetNextBlock(E820MemoryBlock* block, uint32_t* continuationId);
|
||||
183
tools/fat/fat.c
183
tools/fat/fat.c
@ -1,183 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
// typedef uint8_t bool;
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t BootJumpInstruction[3];
|
||||
uint8_t OemIdentifier[8];
|
||||
uint16_t BytesPerSector;
|
||||
uint8_t SectorsPerCluster;
|
||||
uint16_t ReservedSectors;
|
||||
uint8_t FatCount;
|
||||
uint16_t DirEntryCount;
|
||||
uint16_t TotalSectors;
|
||||
uint8_t MediaDescriptorType;
|
||||
uint16_t SectorsPerFat;
|
||||
uint16_t SectorsPerTrack;
|
||||
uint16_t Heads;
|
||||
uint32_t HiddenSectors;
|
||||
uint32_t LargeSectorCount;
|
||||
|
||||
//Extended Boot Record
|
||||
uint8_t DriveNumber;
|
||||
uint8_t _Reserved;
|
||||
uint8_t Signature;
|
||||
uint32_t VolumeId; //Serial Can be whatever
|
||||
uint8_t VolumeLabel[11]; //11 Bytes MUST Be padded with spaces!
|
||||
uint8_t SystemId[8]; //8 Bytes padded with spaces (use MSWIN4.1 for best compatibility!)
|
||||
|
||||
} __attribute__((packed)) BootSector;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t Name[11];
|
||||
uint8_t Attributes;
|
||||
uint8_t _Reserved;
|
||||
uint8_t CreatedTimeTenths;
|
||||
uint16_t CreatedTime;
|
||||
uint16_t CreatedDate;
|
||||
uint16_t AccessedDate;
|
||||
uint16_t FirstClusterHigh;
|
||||
uint16_t ModifiedTime;
|
||||
uint16_t ModifiedDate;
|
||||
uint16_t FirstClusterLow;
|
||||
uint32_t Size;
|
||||
|
||||
} __attribute__((packed)) DirectoryEntry;
|
||||
|
||||
BootSector g_BootSector;
|
||||
uint8_t* g_Fat = NULL;
|
||||
DirectoryEntry* g_RootDirectory = NULL;
|
||||
uint32_t g_RootDirectoryEnd;
|
||||
|
||||
bool readBootSector(FILE* disk)
|
||||
{
|
||||
return fread(&g_BootSector, sizeof(g_BootSector), 1, disk) > 0;
|
||||
}
|
||||
|
||||
bool readSectors(FILE* disk, uint32_t lba, uint32_t count, void* bufferout)
|
||||
{
|
||||
bool ok = true;
|
||||
ok = ok && (fseek(disk, lba * g_BootSector.BytesPerSector, SEEK_SET) == 0);
|
||||
ok = ok && (fread(bufferout, g_BootSector.BytesPerSector, count, disk) == count);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool readFat(FILE* disk)
|
||||
{
|
||||
g_Fat = (uint8_t*) malloc(g_BootSector.SectorsPerFat * g_BootSector.BytesPerSector);
|
||||
return readSectors(disk, g_BootSector.ReservedSectors, g_BootSector.SectorsPerFat, g_Fat);
|
||||
}
|
||||
|
||||
bool readRootDirectory(FILE* disk)
|
||||
{
|
||||
uint32_t lba = g_BootSector.ReservedSectors + g_BootSector.SectorsPerFat * g_BootSector.FatCount;
|
||||
uint32_t size = sizeof(DirectoryEntry) * g_BootSector.DirEntryCount;
|
||||
uint32_t sector = (size / g_BootSector.BytesPerSector);
|
||||
if (size % g_BootSector.BytesPerSector > 0)
|
||||
sector++;
|
||||
|
||||
g_RootDirectoryEnd = lba + sector;
|
||||
g_RootDirectory = (DirectoryEntry*) malloc(sector * g_BootSector.BytesPerSector);
|
||||
return readSectors(disk, lba, sector, g_RootDirectory);
|
||||
}
|
||||
|
||||
DirectoryEntry* findFile(const char* name)
|
||||
{
|
||||
for (uint32_t i = 0; i < g_BootSector.DirEntryCount; i++)
|
||||
{
|
||||
if (memcmp(name, g_RootDirectory[i].Name, 11) == 0)
|
||||
return &g_RootDirectory[i];
|
||||
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool readFile(DirectoryEntry* fileEntry, FILE* disk, uint8_t* outputBuffer)
|
||||
{
|
||||
bool ok = true;
|
||||
uint16_t currentCluster = fileEntry->FirstClusterLow;
|
||||
|
||||
do {
|
||||
uint32_t lba = g_RootDirectoryEnd + (currentCluster - 2) * g_BootSector.SectorsPerCluster;
|
||||
ok = ok && readSectors(disk, lba, g_BootSector.SectorsPerCluster, outputBuffer);
|
||||
outputBuffer += g_BootSector.SectorsPerCluster * g_BootSector.BytesPerSector;
|
||||
|
||||
uint32_t fatIndex = currentCluster * 3 / 2;
|
||||
if (currentCluster % 2 == 0)
|
||||
currentCluster = (*(uint16_t*)(g_Fat + fatIndex)) & 0x0FFF;
|
||||
else
|
||||
currentCluster = (*(uint16_t*)(g_Fat + fatIndex)) >> 4;
|
||||
|
||||
} while (ok && currentCluster < 0x0FF8);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 3){
|
||||
printf("Syntax: %s <disk image> <file name>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE* disk = fopen(argv[1], "rb");
|
||||
if (!disk) {
|
||||
fprintf(stderr, "Cannot open disk image %s!\n", argv[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!readBootSector(disk)) {
|
||||
fprintf(stderr, "Could not read boot sector!\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (!readFat(disk)) {
|
||||
fprintf(stderr, "Could not read FAT!\n");
|
||||
free(g_Fat);
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (!readRootDirectory(disk)) {
|
||||
fprintf(stderr, "Could not read root!\n");
|
||||
free(g_Fat);
|
||||
free(g_RootDirectory);
|
||||
return -4;
|
||||
}
|
||||
|
||||
DirectoryEntry* fileEntry = findFile(argv[2]);
|
||||
if (!fileEntry) {
|
||||
fprintf(stderr, "Could not locate file %s!\n", argv[2]);
|
||||
free(g_Fat);
|
||||
free(g_RootDirectory);
|
||||
return -5;
|
||||
}
|
||||
|
||||
uint8_t* buffer = (uint8_t*) malloc(fileEntry->Size + g_BootSector.BytesPerSector);
|
||||
if (!readFile(fileEntry, disk, buffer)) {
|
||||
fprintf(stderr, "Could not read file %s!\n", argv[2]);
|
||||
free(g_Fat);
|
||||
free(g_RootDirectory);
|
||||
free(buffer);
|
||||
return -5;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < fileEntry->Size; i++)
|
||||
{
|
||||
if (isprint(buffer[i])) fputc(buffer[i], stdout);
|
||||
//else printf("<%02x>", buffer[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
free(g_Fat);
|
||||
free(g_RootDirectory);
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user