Begin the Great Bootloader Swap.

This commit is contained in:
Tyler McGurrin 2025-06-05 20:57:54 -04:00
parent b8ea5e05d2
commit 21b24619f0
31 changed files with 5 additions and 2444 deletions

1
.gitignore vendored
View File

@ -1,4 +1,3 @@
.vscode/
build/
toolchain/
src/bootloader/stage2_old

View File

@ -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

View File

@ -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])

View File

@ -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

View File

@ -1 +0,0 @@
verbose

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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;
}

View File

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

View File

@ -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, &sectors, &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, &sector, &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;
}

View File

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

View File

@ -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

View File

@ -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;
}

View File

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

View 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 = .;
}

View File

@ -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, &sects, &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 (;;);
}

View File

@ -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)

View File

@ -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;
}

View File

@ -1,10 +0,0 @@
/*----------------*\
|Nanite OS |
|Copyright (C) 2024|
|Tyler McGurrin |
\*----------------*/
#pragma once
#include "../../libs/boot/bootparams.h"
void Memory_Detect(MemoryInfo* memoryInfo);

View File

@ -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;
}

View File

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

View File

@ -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))

View File

@ -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");
}

View File

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

View File

@ -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;
}

View File

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

View File

@ -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

View File

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

1
test
View File

@ -1 +0,0 @@
Ok!

View File

@ -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;
}