diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3294603 --- /dev/null +++ b/Makefile @@ -0,0 +1,74 @@ +ASM=nasm +CC=gcc +CC16=/usr/bin/watcom/binl/wcc +LD16=/usr/bin/watcom/binl/wlink + +SRC_DIR=src +TOOLS_DIR=tools +BUILD_DIR=build + +.PHONY: all floppy_image kernel bootloader clean always tools_fat + +all: floppy_image tools_fat + +# +# Floppy image +# +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 + mcopy -i $(BUILD_DIR)/main_floppy.img $(BUILD_DIR)/stage2.bin "::stage2.bin" + mcopy -i $(BUILD_DIR)/main_floppy.img $(BUILD_DIR)/kernel.bin "::kernel.bin" + mcopy -i $(BUILD_DIR)/main_floppy.img readme.txt "::readme.txt" + +# +# Bootloader +# +# +bootloader: stage1 stage2 + +stage1: $(BUILD_DIR)/stage1.bin + +$(BUILD_DIR)/stage1.bin: always + $(MAKE) -C $(SRC_DIR)/bootloader/stage1 BUILD_DIR=$(abspath $(BUILD_DIR)) + +stage2: $(BUILD_DIR)/stage2.bin + +$(BUILD_DIR)/stage2.bin: always + $(MAKE) -C $(SRC_DIR)/bootloader/stage2 BUILD_DIR=$(abspath $(BUILD_DIR)) + + +# +# Kernel +# +kernel: $(BUILD_DIR)/kernel.bin + +$(BUILD_DIR)/kernel.bin: always + $(MAKE) -C $(SRC_DIR)/kernel BUILD_DIR=$(abspath $(BUILD_DIR)) + +# +# Tools +# +tools_fat: $(BUILD_DIR)/tools/fat +$(BUILD_DIR)/tools/fat: always $(TOOLS_DIR)/fat/fat.c + mkdir -p $(BUILD_DIR)/tools + $(CC) -g -o $(BUILD_DIR)/tools/fat $(TOOLS_DIR)/fat/fat.c + +# +# Always +# +always: + mkdir -p $(BUILD_DIR) + +# +# Clean +# +clean: + $(MAKE) -C $(SRC_DIR)/bootloader/stage1 BUILD_DIR=$(abspath $(BUILD_DIR)) clean + $(MAKE) -C $(SRC_DIR)/bootloader/stage2 BUILD_DIR=$(abspath $(BUILD_DIR)) clean + $(MAKE) -C $(SRC_DIR)/kernel BUILD_DIR=$(abspath $(BUILD_DIR)) clean + rm -rf $(BUILD_DIR)/* + rm -rf $(SRC_DIR)/bootloader/stage2/*.err diff --git a/bochs_config b/bochs_config new file mode 100644 index 0000000..58f1736 --- /dev/null +++ b/bochs_config @@ -0,0 +1,7 @@ +megs: 128 +romimage: file=/usr/share/bochs/BIOS-bochs-legacy, address=0xffff0000 +vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest +floppya: 1_44=build/main_floppy.img, status=inserted +boot: floppy +mouse: enabled=0 +display_library: x, options="gui_debug" diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..0e17b5b --- /dev/null +++ b/build.sh @@ -0,0 +1,52 @@ +#!/bin/bash +echo -------- +echo STARTING +echo -------- +read -p "Do you want to clean old build files? (y/n) " yn + +case $yn in + y ) + + echo ---------------------------; + echo Removing Old Build Files...; + echo ---------------------------; + + sudo make clean; + echo -----; + echo Done!; + echo -----; + ;; + n ) + echo ---------; + echo Proceding; + echo ---------; + ;; +esac +echo ------------ +echo COMPILING OS +echo ------------ + +sudo make -s + +echo --------- +echo Finished! +echo --------- +read -p "Do you want to Start QEMU? (y/n) " yn + +case $yn in + y ) + echo ------------- + echo STARTING QEMU + echo ------------- + sudo qemu-system-i386 -fda build/main_floppy.img + echo -------- + echo Finshed! + echo -------- + + ;; + n ) echo exiting...; + exit;; + * ) echo invalid response; + exit 1;; +esac + diff --git a/build/stage1.bin b/build/stage1.bin new file mode 100644 index 0000000..d57c15d Binary files /dev/null and b/build/stage1.bin differ diff --git a/build/stage2/asm/main.obj b/build/stage2/asm/main.obj new file mode 100644 index 0000000..237e3ff Binary files /dev/null and b/build/stage2/asm/main.obj differ diff --git a/build/stage2/asm/x86.obj b/build/stage2/asm/x86.obj new file mode 100644 index 0000000..ff670e8 Binary files /dev/null and b/build/stage2/asm/x86.obj differ diff --git a/build/stage2/c/main.obj b/build/stage2/c/main.obj new file mode 100644 index 0000000..cea7dd3 Binary files /dev/null and b/build/stage2/c/main.obj differ diff --git a/bx_enh_dbg.ini b/bx_enh_dbg.ini new file mode 100644 index 0000000..e0cd532 --- /dev/null +++ b/bx_enh_dbg.ini @@ -0,0 +1,26 @@ +# bx_enh_dbg_ini +SeeReg[0] = TRUE +SeeReg[1] = TRUE +SeeReg[2] = TRUE +SeeReg[3] = TRUE +SeeReg[4] = FALSE +SeeReg[5] = FALSE +SeeReg[6] = FALSE +SeeReg[7] = FALSE +SingleCPU = FALSE +ShowIOWindows = TRUE +ShowButtons = TRUE +SeeRegColors = TRUE +ignoreNxtT = TRUE +ignSSDisasm = TRUE +UprCase = 0 +DumpInAsciiMode = 3 +isLittleEndian = TRUE +DefaultAsmLines = 512 +DumpWSIndex = 0 +DockOrder = 0x123 +ListWidthPix[0] = 179 +ListWidthPix[1] = 248 +ListWidthPix[2] = 283 +MainWindow = 0, 0, 714, 500 +FontName = Normal diff --git a/debug.sh b/debug.sh new file mode 100644 index 0000000..8963282 --- /dev/null +++ b/debug.sh @@ -0,0 +1,27 @@ +echo ------------ +echo COMPILING OS +echo ------------ + +sudo make + +echo --------- +echo Finished! +echo --------- +read -p "Do you want to Start Bochs? (Y/n) " yn + +case $yn in + y ) + echo ------------- + echo STARTING Bochs + echo ------------- + sudo bochs -f bochs_config + echo -------- + echo Finshed! + echo -------- + + ;; + n ) echo exiting...; + exit;; + * ) echo invalid response; + exit 1;; +esac diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..4dbb2eb --- /dev/null +++ b/readme.txt @@ -0,0 +1,5 @@ +###LAMBDA OS README + +Test README + +OMG i love CHEESE diff --git a/src/bootloader/stage1/Makefile b/src/bootloader/stage1/Makefile new file mode 100644 index 0000000..2bf3755 --- /dev/null +++ b/src/bootloader/stage1/Makefile @@ -0,0 +1,14 @@ +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 diff --git a/src/bootloader/stage1/boot.asm b/src/bootloader/stage1/boot.asm new file mode 100644 index 0000000..b8e7726 --- /dev/null +++ b/src/bootloader/stage1/boot.asm @@ -0,0 +1,383 @@ +;/////////////////////; +;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 floppy_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 kernel.bin + xor bx, bx + mov di, buffer + +.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 stage2_not_found_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 handlers +; + +floppy_error: + mov si, msg_read_failed + call puts + jmp wait_key_and_reboot + +stage2_not_found_error: + mov si, msg_no_stage2 + 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 floppy_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 floppy_error + popa + ret + + +msg_loading: db 'Starting Nanite 0.0.1a...', ENDL, 0 +msg_read_failed: db 'Cant Read Disk!', ENDL, 0 +msg_no_stage2: db 'Cant Find Stage 2!', ENDL, 0 +file_stage2_bin: db 'STAGE2 BIN' +stage2_cluster: dw 0 + +STAGE2_LOAD_SEGMENT equ 0x2000 +STAGE2_LOAD_OFFSET equ 0 + +times 510-($-$$) db 0 +dw 0AA55h + +buffer: + diff --git a/src/bootloader/stage2/Makefile b/src/bootloader/stage2/Makefile new file mode 100644 index 0000000..191024a --- /dev/null +++ b/src/bootloader/stage2/Makefile @@ -0,0 +1,33 @@ +BUILD_DIR?=build/ +ASM?=nasm +ASMFLAGS?=-f obj +CC16?=/usr/bin/watcom/binl/wcc +CFLAGS16?=-4 -d3 -s -wx -ms -zl -zq # -oneatxzh +LD16?=/usr/bin/watcom/binl/wlink + +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)) + +.PHONY: all stage2 clean always + +all: stage2 + +stage2: $(BUILD_DIR)/stage2.bin + +$(BUILD_DIR)/stage2.bin: $(OBJECTS_ASM) $(OBJECTS_C) + $(LD16) NAME $(BUILD_DIR)/stage2.bin FILE \{ $(OBJECTS_ASM) $(OBJECTS_C) \} OPTION MAP=$(BUILD_DIR)/stage2.map @linker.lnk + +$(BUILD_DIR)/stage2/c/%.obj: %.c always + $(CC16) $(CFLAGS16) -fo=$@ $< + +$(BUILD_DIR)/stage2/asm/%.obj: %.asm always + $(ASM) $(ASMFLAGS) -o $@ $< + +always: + mkdir -p $(BUILD_DIR)/stage2/c + mkdir -p $(BUILD_DIR)/stage2/asm + +clean: + rm -f $(BUILD_DIR)/stage2.bin diff --git a/src/bootloader/stage2/disk.h b/src/bootloader/stage2/disk.h new file mode 100644 index 0000000..62faffe --- /dev/null +++ b/src/bootloader/stage2/disk.h @@ -0,0 +1,19 @@ +/*----------------*\ +|Nanite OS | +|Copyright (C) 2024| +|Tyler McGurrin | +\*----------------*/ +#pragma once + +#include "stdint.h" + +typedef struct { + uint8_t id; + uint16_t cylinders; + uint16_t sectors; + uint16_t heads; +}; + +bool DISK_Initialize(DISK* disk, uint8_t driveNumber); +bool DISK_ReadSectors(DISK* disk, uint32_t lba, uint8_t far* dataOut); + diff --git a/src/bootloader/stage2/linker.lnk b/src/bootloader/stage2/linker.lnk new file mode 100644 index 0000000..9e70033 --- /dev/null +++ b/src/bootloader/stage2/linker.lnk @@ -0,0 +1,12 @@ +FORMAT RAW BIN +OPTION QUIET, + NODEFAULTLIBS, + START=entry, + VERBOSE, + OFFSET=0, + STACK=0x200 +ORDER + CLNAME CODE + SEGMENT _ENTRY + SEGMENT _TEXT + CLNAME DATA diff --git a/src/bootloader/stage2/main.asm b/src/bootloader/stage2/main.asm new file mode 100644 index 0000000..8646753 --- /dev/null +++ b/src/bootloader/stage2/main.asm @@ -0,0 +1,28 @@ +;/////////////////////; +;Nanite OS ; +;COPYRIGHT (C) 2024 ; +;Tyler McGurrin ; +;/////////////////////; +bits 16 + +section _ENTRY class=CODE + +extern _cstart_ +global entry + +entry: + cli + ; setup stack + mov ax, ds + mov ss, ax + mov sp, 0 + mov bp, sp + sti + + ; expect boot drive in dl, send it as argument to cstart function + xor dh, dh + push dx + call _cstart_ + + cli + hlt diff --git a/src/bootloader/stage2/main.c b/src/bootloader/stage2/main.c new file mode 100644 index 0000000..f420738 --- /dev/null +++ b/src/bootloader/stage2/main.c @@ -0,0 +1,13 @@ +/*----------------*\ +|Nanite OS | +|Copyright (C) 2024| +|Tyler McGurrin | +\*----------------*/ +#include "stdint.h" +#include "stdio.h" + +void _cdecl cstart_(uint16_t bootDrive) +{ + puts("Going from x86 ASM to C code!\r\n"); + printf("Initialized!\r\n"); +} diff --git a/src/bootloader/stage2/main.err b/src/bootloader/stage2/main.err new file mode 100644 index 0000000..e39ad6c --- /dev/null +++ b/src/bootloader/stage2/main.err @@ -0,0 +1 @@ +main.c(9): Warning! W303: Parameter 'bootDrive' has been defined, but not referenced diff --git a/src/bootloader/stage2/stdint.h b/src/bootloader/stage2/stdint.h new file mode 100644 index 0000000..5b4266b --- /dev/null +++ b/src/bootloader/stage2/stdint.h @@ -0,0 +1,20 @@ +/*----------------*\ +|Nanite OS | +|Copyright (C) 2024| +|Tyler McGurrin | +\*----------------*/ +#pragma once + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed long int int32_t; +typedef unsigned long int uint32_t; +typedef signed long long int int64_t; +typedef unsigned long long int uint64_t; + +typedef uint8_t bool; + +#define true 1 +#define false 0 diff --git a/src/bootloader/stage2/stdio.c b/src/bootloader/stage2/stdio.c new file mode 100644 index 0000000..123e41c --- /dev/null +++ b/src/bootloader/stage2/stdio.c @@ -0,0 +1,222 @@ +/*----------------*\ +|Nanite OS | +|Copyright (C) 2024| +|Tyler McGurrin | +\*----------------*/ +#include "stdio.h" +#include "x86.h" + +void putc(char c) +{ + x86_Video_WriteCharTeletype(c, 0); +} + +void puts(const char* str) +{ + while(*str) + { + putc(*str); + str++; + } +} + +#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 + + +int* printf_number(int* argp, int length, bool sign, int radix); + +void _cdecl printf(const char* fmt, ...) { + int* argp = (int*)&fmt; + int state= PRINTF_STATE_NORMAL; + int length = PRINTF_LENGTH_DEFAULT; + int radix = 10; + bool sign = false; + + argp++; + + while (*fmt) + { + switch (state) + { + case PRINTF_STATE_NORMAL: + switch (*fmt) + { + case '%': state = PRINTF_STATE_NORMAL; + 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 == 'h') + { + 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)*argp); + argp++; + break; + + case 's': puts(*(char***)argp); + argp++; + break; + + case '%': putc('%'); + break; + + case 'd': + case 'i': radix = 10; sign = true; + argp = printf_number(argp, length, sign, radix); + break; + + case 'u': radix = 10; sign = false; + argp = printf_number(argp, length, sign, radix); + break; + + case 'X': + case 'x': + case 'p': radix = 16; sign = false; + argp = printf_number(argp, length, sign, radix); + break; + + case 'o': radix = 8; sign = false; + argp = printf_number(argp, length, sign, radix); + break; + + //ignore invalid specifiers (specs) + default: break; + } + + //reset state + state = PRINTF_STATE_NORMAL; + length = PRINTF_LENGTH_DEFAULT; + radix = 10; + sign = false; + break; + } + + fmt++; + } +} + +const char g_HexChars[] = "0123456789abcdef"; + +int* printf_number(int* argp, int length, bool sign, int radix) +{ + char buffer[32]; + unsigned long long number; + int number_sign = 1; + int pos = 0; + + //process length + switch (length) + { + case PRINTF_LENGTH_SHORT_SHORT: + case PRINTF_LENGTH_SHORT: + case PRINTF_LENGTH_DEFAULT: + if (sign) + { + int n = *argp; + if (n < 0) { + n = -n; + number_sign = -1; + } + number = (unsigned long long)n; + } + else { + number = *(unsigned int*)argp; + } + argp++; + break; + + case PRINTF_LENGTH_LONG: + if (sign) + { + long int n = *(long int*)argp; + if (n < 0) { + n = -n; + number_sign = -1; + } + number = (unsigned long long)n; + } + else { + number = *(unsigned long int*)argp; + } + argp += 2; + break; + + case PRINTF_LENGTH_LONG_LONG: + if (sign) + { + long long int n = *(long long int*)argp; + if (n < 0) { + n = -n; + number_sign = -1; + } + number = (unsigned long long)n; + } + else { + number = *(unsigned long long*)argp; + } + argp += 4; + break; + } + + //number to ASCII conversion + do { + uint32_t rem; + x86_div64_32(number, radix, &number, &rem); + buffer[pos++] = g_HexChars[rem]; + } while (number > 0); + + //add sign + if (sign && number_sign < 0) { + buffer[pos++] = '-'; + } + + //print number in reverse order + while (--pos >= 0) + putc(buffer[pos]); + + return argp; +} diff --git a/src/bootloader/stage2/stdio.err b/src/bootloader/stage2/stdio.err new file mode 100644 index 0000000..b1e31d6 --- /dev/null +++ b/src/bootloader/stage2/stdio.err @@ -0,0 +1,5 @@ +x86.h(22): Error! E1009: Expecting ',' or ';' but found 'x86_Disk_GetDriveParams' +stdio.c(100): Warning! W100: Parameter 1 contains inconsistent levels of indirection +stdio.c(100): Note! I2003: source conversion type is 'char **' +stdio.c(100): Note! I2004: target conversion type is 'char const *' +stdio.c(100): Note! I2002: 'puts' defined in: stdio.c(14) diff --git a/src/bootloader/stage2/stdio.h b/src/bootloader/stage2/stdio.h new file mode 100644 index 0000000..1de4a82 --- /dev/null +++ b/src/bootloader/stage2/stdio.h @@ -0,0 +1,10 @@ +/*----------------*\ +|Nanite OS | +|Copyright (C) 2024| +|Tyler McGurrin | +\*----------------*/ +#pragma once + +void putc(char c); +void puts(const char* str); +void _cdecl printf(const char* fmt, ...); diff --git a/src/bootloader/stage2/x86.asm b/src/bootloader/stage2/x86.asm new file mode 100644 index 0000000..ec02a55 --- /dev/null +++ b/src/bootloader/stage2/x86.asm @@ -0,0 +1,102 @@ +;/////////////////////; +;Nanite OS ; +;COPYRIGHT (C) 2024 ; +;Tyler McGurrin ; +;/////////////////////; +bits 16 + +section _TEXT class=CODE + +; +;void _cdecl x86_div64_32(uint64_t dividend, uint32_t divisor, uint64_t* quotentOut, uint32_t* remainderOut); +; + +global _x86_div64_32 +_x86_div64_32 + ;make new call frame + push bp ;save old call frame + mov bp, sp ;init new call frame + + push bx + + ;divide upper 32 bits + mov eax, [bp + 4] ;eax is upper 32 bits of divedend + mov ecx, [bp + 12] ;ecx is the divisor + xor edx, edx + div ecx ;eax - quot, edx - remainder + + ;store upper 32 bits + mov ebx, [bp + 16] + mov [bx + 4], eax + + ;divide lower 32 bits + mov eax, [bp + 4] ;eax is the lower 32 bits of the dividend + ;edx is old remainder + div ecx + + ;store results + mov [bx], eax + mov bx, [bp + 18] + mov [bx], edx + + pop bx + + ;restore old call frame + mov sp, bp + pop bp + ret + + + + +global _x86_Video_WriteCharTeletype +_x86_Video_WriteCharTeletype: + + push bp + mov bp,sp + + push bx + + mov ah, 0Eh + mov al, [bp + 4] + mov bh, [bp + 6] + + int 10h + + pop bx + + mov sp, bp + pop bp + ret + + +; +;void _cdecl x86_Disk_Reset(uint8_t drive); +; +global _x86_Disk_Reset +_x86_Disk_Reset: + ;make new call frame + push bp ;save old call frame + mov bp, sp ;init new call frame + + + + ;restore old call frame + mov sp, bp + pop bp + ret + + +; +;void _cdecl x86_Disk_Read(uint8_t drive, +; uint16_t cylinder, +; uint16_t head, +; uint16_t sector, +; uint8_t count, +; uint8_t far * dataout); +; +;void _cdelc x86_Disk_GetDriveParams(uint8_t drive, +; uint8_t* driveTypeOut, +; uint16_t* cylindersOut, +; uint16_t* sectorsOut, +; uint16_t* headsOut); diff --git a/src/bootloader/stage2/x86.h b/src/bootloader/stage2/x86.h new file mode 100644 index 0000000..db1366c --- /dev/null +++ b/src/bootloader/stage2/x86.h @@ -0,0 +1,26 @@ +/*----------------*\ +|Nanite OS | +|Copyright (C) 2024| +|Tyler McGurrin | +\*----------------*/ +#pragma once +#include "stdint.h" + +void _cdecl x86_div64_32(uint64_t dividend, uint32_t divisor, uint64_t* quotentOut, uint32_t* remainderOut); + +void _cdecl x86_Video_WriteCharTeletype(char c, uint8_t page); + +void _cdecl x86_Disk_Reset(uint8_t drive); + +void _cdecl x86_Disk_Read(uint8_t drive, + uint16_t cylinder, + uint16_t head, + uint16_t sector, + uint8_t count, + uint8_t far * dataout); + +void _cdelc x86_Disk_GetDriveParams(uint8_t drive, + uint8_t* driveTypeOut, + uint16_t* cylindersOut, + uint16_t* sectorsOut, + uint16_t* headsOut); diff --git a/src/kernel/Makefile b/src/kernel/Makefile new file mode 100644 index 0000000..f47f9fd --- /dev/null +++ b/src/kernel/Makefile @@ -0,0 +1,14 @@ +BUILD_DIR?=build/ +ASM?=nasm + +.PHONY: all kernel clean + +all: kernel + +kernel: $(BUILD_DIR)/kernel.bin + +$(BUILD_DIR)/kernel.bin: + $(ASM) main.asm -f bin -o $(BUILD_DIR)/kernel.bin + +clean: + rm -f $(BUILD_DIR)/kernel.bin diff --git a/src/kernel/main.asm b/src/kernel/main.asm new file mode 100644 index 0000000..eaf316f --- /dev/null +++ b/src/kernel/main.asm @@ -0,0 +1,50 @@ +;/////////////////////; +;Nanite OS ; +;COPYRIGHT (C) 2024 ; +;Tyler McGurrin ; +;/////////////////////; +org 0x0 +bits 16 + + +%define ENDL 0x0D, 0x0A + + +start: + ; print hello world message + mov si, msg_hello + call puts + +.halt: + cli + 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 + +msg_hello: db 'Loading Kernel...', ENDL, 0 diff --git a/src/micron/Makefile b/src/micron/Makefile new file mode 100644 index 0000000..5f466b9 --- /dev/null +++ b/src/micron/Makefile @@ -0,0 +1,2 @@ +CC = gcc +BUILD_DIR = /build/micron \ No newline at end of file diff --git a/src/micron/main b/src/micron/main new file mode 100644 index 0000000..fcc76b4 Binary files /dev/null and b/src/micron/main differ diff --git a/src/micron/main.c b/src/micron/main.c new file mode 100644 index 0000000..5ab31fd --- /dev/null +++ b/src/micron/main.c @@ -0,0 +1,232 @@ +/*----------------*\ +|Nanite OS | +|Copyright (C) 2024| +|Tyler McGurrin | +\*----------------*/ +#include +#include +#include +#include +#include +#include "tinydir.h" + +//Varible Setup here +#define os "Nanite OS" +#define version "0.0.1a" +#define cursor ":\\>" +#define hostname "[NOT IMPLEMENTED YET]" +#define os_logo " _ _ _ _ _____ _____ \n| \\ | | (_) | | _ / ___|\n| \\| | __ _ _ __ _| |_ ___ | | | \\ `--. \n| . ` |/ _` | '_ \\| | __/ _ \\ | | | |`--. \\\n| |\\ | (_| | | | | | || __/ \\ \\_/ /\\__/ /\n\\_| \\_/\\__,_|_| |_|_|\\__\\___| \\___/\\____/ \n" +#define shell_logo " __ __ _ __ __ \n | \\/ (_) ___ _ __ ___ _ __ \\ \\ \\ \\ \n | |\\/| | |/ __| '__/ _ \\| '_ \\(_) \\ \\ \\\n | | | | | (__| | | (_) | | | |_ \\ \\ / /\n |_| |_|_|\\___|_| \\___/|_| |_(_) \\_\\/_/ \n \n" +int invalid_command = 0; +int command_list_length = 10; //does not NEED to match command list's length just helps with slower machines to keep this small and it can get buggy if too large +int history_length = {0}; +int max_files = 200; +char command_list[][15] = {"help","version","clear", "uname", "exit", "dir", "fetch", "time", "exec"}; +char buffer[15][255]; +char program_buffer[30][100]; +char prog_buffer2; +char history[][255]; +char time_buffer[80]; +char exec_cmd_operand_list[15][15] = {"--help", "--nogui"}; +char exec_cmd_dir_buffer[][255]; + + +int main() { + printf("Micron Shell Version " version " Starting...\n"); + + stage2(); + return 0; +} + +int stage2() { + printf("Done Loading Shell!\n"); + printf("Currently Implemtented Commands:\n"); + for(int i = 0; i < command_list_length; i++) { + printf("%s ", &command_list[i]);} + printf("\n"); + cursor_loop(); + return 0; +} + +int cursor_loop() { + printf(cursor); + scanf("%s", &buffer); + //Command detection shiz + if (strcmp(buffer[0],command_list[0]) == 0) {help_cmd();} + else if (strcmp(buffer[0],command_list[1]) == 0) {version_cmd();} + else if (strcmp(buffer[0],command_list[2]) == 0) {clear_cmd();} + else if (strcmp(buffer[0],command_list[3]) == 0) {uname_cmd();} + else if (strcmp(buffer[0],command_list[4]) == 0) {exit_cmd();} + else if (strcmp(buffer[0],command_list[5]) == 0) {dir_cmd();} + else if (strcmp(buffer[0],command_list[6]) == 0) {fetch_cmd();} + else if (strcmp(buffer[0],command_list[7]) == 0) {time_cmd();} + else if (strcmp(buffer[0],command_list[8]) == 0) {exec_cmd();} + else {printf("Mircon: %s: Command not Found!\n", &buffer); clear_buffer();} + cursor_loop(); + return 0; + } + +//Bulit in commands +int help_cmd() { + printf("Built-in Commands:\n"); + for(int i = 0; i < command_list_length; i++) { + printf("%s ", &command_list[i]);} + printf("\n"); + clear_buffer(); + cursor_loop(); + return 0; + } +int version_cmd() { + printf(shell_logo); + printf("Version %s Made by Tyler McGurrin\nCopyright (C) 2024\n", &version); + clear_buffer(); + cursor_loop(); + return 0; + } +int clear_cmd() { + for(int i = 0; i < 2500; i++) { + printf("\n"); + } + clear_buffer(); + cursor_loop(); + return 0; + } +int uname_cmd() { + printf("%s\n", &os); + clear_buffer(); + cursor_loop(); + return 0; +} +int exit_cmd() { + printf("Exiting Micron Shell...\n"); + exit(0); +} +int dir_cmd() { +tinydir_dir dir; +tinydir_open(&dir, "./"); + +while (dir.has_next) +{ + tinydir_file file; + tinydir_readfile(&dir, &file); + + printf("%s", file.name); + if (file.is_dir) + { + printf("/ DIR"); + } + printf("\n"); + + tinydir_next(&dir); +} + +tinydir_close(&dir); +clear_buffer(); +cursor_loop(); +return 0; +} +int fetch_cmd() { +printf("Operating System: %s\nShell: Micron %s\nHostname: %s\n",os,version,hostname); + time_t t = time(NULL); + printf("UTC Time: %s", asctime(gmtime(&t))); + printf("Local Time: %s", asctime(localtime(&t))); +printf(os_logo); +clear_buffer(); +cursor_loop(); +return 0; +} +int time_cmd() { + time_t t = time(NULL); + printf("UTC: %s", asctime(gmtime(&t))); + printf("Local: %s", asctime(localtime(&t))); +clear_buffer(); +cursor_loop(); +return 0; +} +int exec_cmd() { + if (strcmp(buffer[1], exec_cmd_operand_list[0]) == 0) {exec_cmd_help();} + else {exec_cmd_gui(); clear_buffer();} + cursor_loop(); + return 0; +} + + +//Command Handlers + +//Exec Handlers +int exec_cmd_gui() { + printf("\nExec:>"); + scanf("%s", &program_buffer); + if (strcmp(program_buffer[0],command_list[0]) == 0) {help_cmd();} + else if (strcmp(program_buffer[0],command_list[1]) == 0) {version_cmd();} + else if (strcmp(program_buffer[0],command_list[2]) == 0) {clear_cmd();} + else if (strcmp(program_buffer[0],command_list[3]) == 0) {uname_cmd();} + else if (strcmp(program_buffer[0],command_list[4]) == 0) {exit_cmd();} + else if (strcmp(program_buffer[0],command_list[5]) == 0) {dir_cmd();} + else if (strcmp(program_buffer[0],command_list[6]) == 0) {fetch_cmd();} + else if (strcmp(program_buffer[0],command_list[7]) == 0) {time_cmd();} + else {exec_cmd_dir();} + //if (exec_cmd_dir_buffer){printf("Exec: %s: Executible not Found!\n", &buffer); clear_buffer();} + + clear_buffer(); + cursor_loop(); + return 0; +} + +int exec_cmd_help() { + printf("Usage [Operands Like: "); + for(int i = 0; i < command_list_length; i++) { + printf("%s ", &exec_cmd_operand_list[i]);} + printf("]\n"); + clear_buffer(); + cursor_loop(); + return 0; + } +int exec_cmd_dir() { +tinydir_dir dir; +tinydir_open(&dir, "./"); + +while (dir.has_next) +{ + tinydir_file file; + tinydir_readfile(&dir, &file); + + for(int i = 0; i < max_files; i++) { + buffer[i][255] = NULL;} + if (file.is_dir) + { + break; + } + for(int i = 0; i < max_files; i++) { + if (strcmp(program_buffer,file.name) == 0) { + printf("Loading program..."); + strcat(prog_buffer2, "./"); + strcat(prog_buffer2, program_buffer); + prog_load(); + } + else break; + } + + } + printf("Exec: %s: No Executible found!\n", &program_buffer); + tinydir_next(&dir); +tinydir_close(&dir); +clear_buffer(); +cursor_loop(); +return 0; +} + + +//Extra Tools (Not executible via shell) +int clear_buffer() { + + for(int i = 0; i < command_list_length; i++) { + buffer[i][255] = NULL;} + free(prog_buffer2); + cursor_loop(); + return 0; +} +int prog_load() { + system(prog_buffer2); + exit(0); +} \ No newline at end of file diff --git a/src/micron/tinydir.h b/src/micron/tinydir.h new file mode 100644 index 0000000..d7f4668 --- /dev/null +++ b/src/micron/tinydir.h @@ -0,0 +1,848 @@ +/* +Copyright (c) 2013-2021, tinydir authors: +- Cong Xu +- Lautis Sun +- Baudouin Feildel +- Andargor +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef TINYDIR_H +#define TINYDIR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if ((defined _UNICODE) && !(defined UNICODE)) +#define UNICODE +#endif + +#if ((defined UNICODE) && !(defined _UNICODE)) +#define _UNICODE +#endif + +#include +#include +#include +#ifdef _MSC_VER +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# include +# pragma warning(push) +# pragma warning (disable : 4996) +#else +# include +# include +# include +# include +#endif +#ifdef __MINGW32__ +# include +#endif + + +/* types */ + +/* Windows UNICODE wide character support */ +#if defined _MSC_VER || defined __MINGW32__ +# define _tinydir_char_t TCHAR +# define TINYDIR_STRING(s) _TEXT(s) +# define _tinydir_strlen _tcslen +# define _tinydir_strcpy _tcscpy +# define _tinydir_strcat _tcscat +# define _tinydir_strcmp _tcscmp +# define _tinydir_strrchr _tcsrchr +# define _tinydir_strncmp _tcsncmp +#else +# define _tinydir_char_t char +# define TINYDIR_STRING(s) s +# define _tinydir_strlen strlen +# define _tinydir_strcpy strcpy +# define _tinydir_strcat strcat +# define _tinydir_strcmp strcmp +# define _tinydir_strrchr strrchr +# define _tinydir_strncmp strncmp +#endif + +#if (defined _MSC_VER || defined __MINGW32__) +# include +# define _TINYDIR_PATH_MAX MAX_PATH +#elif defined __linux__ +# include +# ifdef PATH_MAX +# define _TINYDIR_PATH_MAX PATH_MAX +# endif +#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +# include +# if defined(BSD) +# include +# ifdef PATH_MAX +# define _TINYDIR_PATH_MAX PATH_MAX +# endif +# endif +#endif + +#ifndef _TINYDIR_PATH_MAX +#define _TINYDIR_PATH_MAX 4096 +#endif + +#ifdef _MSC_VER +/* extra chars for the "\\*" mask */ +# define _TINYDIR_PATH_EXTRA 2 +#else +# define _TINYDIR_PATH_EXTRA 0 +#endif + +#define _TINYDIR_FILENAME_MAX 256 + +#if (defined _MSC_VER || defined __MINGW32__) +#define _TINYDIR_DRIVE_MAX 3 +#endif + +#ifdef _MSC_VER +# define _TINYDIR_FUNC static __inline +#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L +# define _TINYDIR_FUNC static __inline__ +#elif defined(__cplusplus) +# define _TINYDIR_FUNC static inline +#elif defined(__GNUC__) +/* Suppress unused function warning */ +# define _TINYDIR_FUNC __attribute__((unused)) static +#else +# define _TINYDIR_FUNC static +#endif + +#if defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86) +#ifdef _MSC_VER +# define _TINYDIR_CDECL __cdecl +#else +# define _TINYDIR_CDECL __attribute__((cdecl)) +#endif +#else +# define _TINYDIR_CDECL +#endif + +/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */ +#ifdef TINYDIR_USE_READDIR_R + +/* readdir_r is a POSIX-only function, and may not be available under various + * environments/settings, e.g. MinGW. Use readdir fallback */ +#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\ + _POSIX_SOURCE +# define _TINYDIR_HAS_READDIR_R +#endif +#if _POSIX_C_SOURCE >= 200112L +# define _TINYDIR_HAS_FPATHCONF +# include +#endif +#if _BSD_SOURCE || _SVID_SOURCE || \ + (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700) +# define _TINYDIR_HAS_DIRFD +# include +#endif +#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\ + defined _PC_NAME_MAX +# define _TINYDIR_USE_FPATHCONF +#endif +#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\ + !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX) +# define _TINYDIR_USE_READDIR +#endif + +/* Use readdir by default */ +#else +# define _TINYDIR_USE_READDIR +#endif + +/* MINGW32 has two versions of dirent, ASCII and UNICODE*/ +#ifndef _MSC_VER +#if (defined __MINGW32__) && (defined _UNICODE) +#define _TINYDIR_DIR _WDIR +#define _tinydir_dirent _wdirent +#define _tinydir_opendir _wopendir +#define _tinydir_readdir _wreaddir +#define _tinydir_closedir _wclosedir +#else +#define _TINYDIR_DIR DIR +#define _tinydir_dirent dirent +#define _tinydir_opendir opendir +#define _tinydir_readdir readdir +#define _tinydir_closedir closedir +#endif +#endif + +/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */ +#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE) +#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE) +#else +#error "Either define both alloc and free or none of them!" +#endif + +#if !defined(_TINYDIR_MALLOC) + #define _TINYDIR_MALLOC(_size) malloc(_size) + #define _TINYDIR_FREE(_ptr) free(_ptr) +#endif /* !defined(_TINYDIR_MALLOC) */ + +typedef struct tinydir_file +{ + _tinydir_char_t path[_TINYDIR_PATH_MAX]; + _tinydir_char_t name[_TINYDIR_FILENAME_MAX]; + _tinydir_char_t *extension; + int is_dir; + int is_reg; + +#ifndef _MSC_VER +#ifdef __MINGW32__ + struct _stat _s; +#else + struct stat _s; +#endif +#endif +} tinydir_file; + +typedef struct tinydir_dir +{ + _tinydir_char_t path[_TINYDIR_PATH_MAX]; + int has_next; + size_t n_files; + + tinydir_file *_files; +#ifdef _MSC_VER + HANDLE _h; + WIN32_FIND_DATA _f; +#else + _TINYDIR_DIR *_d; + struct _tinydir_dirent *_e; +#ifndef _TINYDIR_USE_READDIR + struct _tinydir_dirent *_ep; +#endif +#endif +} tinydir_dir; + + +/* declarations */ + +_TINYDIR_FUNC +int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path); +_TINYDIR_FUNC +int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path); +_TINYDIR_FUNC +void tinydir_close(tinydir_dir *dir); + +_TINYDIR_FUNC +int tinydir_next(tinydir_dir *dir); +_TINYDIR_FUNC +int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file); +_TINYDIR_FUNC +int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i); +_TINYDIR_FUNC +int tinydir_open_subdir_n(tinydir_dir *dir, size_t i); + +_TINYDIR_FUNC +int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path); +_TINYDIR_FUNC +void _tinydir_get_ext(tinydir_file *file); +_TINYDIR_FUNC +int _TINYDIR_CDECL _tinydir_file_cmp(const void *a, const void *b); +#ifndef _MSC_VER +#ifndef _TINYDIR_USE_READDIR +_TINYDIR_FUNC +size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp); +#endif +#endif + + +/* definitions*/ + +_TINYDIR_FUNC +int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path) +{ +#ifndef _MSC_VER +#ifndef _TINYDIR_USE_READDIR + int error; + int size; /* using int size */ +#endif +#else + _tinydir_char_t path_buf[_TINYDIR_PATH_MAX]; +#endif + _tinydir_char_t *pathp; + + if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0) + { + errno = EINVAL; + return -1; + } + if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) + { + errno = ENAMETOOLONG; + return -1; + } + + /* initialise dir */ + dir->_files = NULL; +#ifdef _MSC_VER + dir->_h = INVALID_HANDLE_VALUE; +#else + dir->_d = NULL; +#ifndef _TINYDIR_USE_READDIR + dir->_ep = NULL; +#endif +#endif + tinydir_close(dir); + + _tinydir_strcpy(dir->path, path); + /* Remove trailing slashes */ + pathp = &dir->path[_tinydir_strlen(dir->path) - 1]; + while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/'))) + { + *pathp = TINYDIR_STRING('\0'); + pathp++; + } +#ifdef _MSC_VER + _tinydir_strcpy(path_buf, dir->path); + _tinydir_strcat(path_buf, TINYDIR_STRING("\\*")); +#if (defined WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP) + dir->_h = FindFirstFileEx(path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0); +#else + dir->_h = FindFirstFile(path_buf, &dir->_f); +#endif + if (dir->_h == INVALID_HANDLE_VALUE) + { + errno = ENOENT; +#else + dir->_d = _tinydir_opendir(path); + if (dir->_d == NULL) + { +#endif + goto bail; + } + + /* read first file */ + dir->has_next = 1; +#ifndef _MSC_VER +#ifdef _TINYDIR_USE_READDIR + dir->_e = _tinydir_readdir(dir->_d); +#else + /* allocate dirent buffer for readdir_r */ + size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */ + if (size == -1) return -1; + dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size); + if (dir->_ep == NULL) return -1; + + error = readdir_r(dir->_d, dir->_ep, &dir->_e); + if (error != 0) return -1; +#endif + if (dir->_e == NULL) + { + dir->has_next = 0; + } +#endif + + return 0; + +bail: + tinydir_close(dir); + return -1; +} + +_TINYDIR_FUNC +int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path) +{ + /* Count the number of files first, to pre-allocate the files array */ + size_t n_files = 0; + if (tinydir_open(dir, path) == -1) + { + return -1; + } + while (dir->has_next) + { + n_files++; + if (tinydir_next(dir) == -1) + { + goto bail; + } + } + tinydir_close(dir); + + if (n_files == 0 || tinydir_open(dir, path) == -1) + { + return -1; + } + + dir->n_files = 0; + dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files); + if (dir->_files == NULL) + { + goto bail; + } + while (dir->has_next) + { + tinydir_file *p_file; + dir->n_files++; + + p_file = &dir->_files[dir->n_files - 1]; + if (tinydir_readfile(dir, p_file) == -1) + { + goto bail; + } + + if (tinydir_next(dir) == -1) + { + goto bail; + } + + /* Just in case the number of files has changed between the first and + second reads, terminate without writing into unallocated memory */ + if (dir->n_files == n_files) + { + break; + } + } + + qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp); + + return 0; + +bail: + tinydir_close(dir); + return -1; +} + +_TINYDIR_FUNC +void tinydir_close(tinydir_dir *dir) +{ + if (dir == NULL) + { + return; + } + + memset(dir->path, 0, sizeof(dir->path)); + dir->has_next = 0; + dir->n_files = 0; + _TINYDIR_FREE(dir->_files); + dir->_files = NULL; +#ifdef _MSC_VER + if (dir->_h != INVALID_HANDLE_VALUE) + { + FindClose(dir->_h); + } + dir->_h = INVALID_HANDLE_VALUE; +#else + if (dir->_d) + { + _tinydir_closedir(dir->_d); + } + dir->_d = NULL; + dir->_e = NULL; +#ifndef _TINYDIR_USE_READDIR + _TINYDIR_FREE(dir->_ep); + dir->_ep = NULL; +#endif +#endif +} + +_TINYDIR_FUNC +int tinydir_next(tinydir_dir *dir) +{ + if (dir == NULL) + { + errno = EINVAL; + return -1; + } + if (!dir->has_next) + { + errno = ENOENT; + return -1; + } + +#ifdef _MSC_VER + if (FindNextFile(dir->_h, &dir->_f) == 0) +#else +#ifdef _TINYDIR_USE_READDIR + dir->_e = _tinydir_readdir(dir->_d); +#else + if (dir->_ep == NULL) + { + return -1; + } + if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0) + { + return -1; + } +#endif + if (dir->_e == NULL) +#endif + { + dir->has_next = 0; +#ifdef _MSC_VER + if (GetLastError() != ERROR_SUCCESS && + GetLastError() != ERROR_NO_MORE_FILES) + { + tinydir_close(dir); + errno = EIO; + return -1; + } +#endif + } + + return 0; +} + +_TINYDIR_FUNC +int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file) +{ + const _tinydir_char_t *filename; + if (dir == NULL || file == NULL) + { + errno = EINVAL; + return -1; + } +#ifdef _MSC_VER + if (dir->_h == INVALID_HANDLE_VALUE) +#else + if (dir->_e == NULL) +#endif + { + errno = ENOENT; + return -1; + } + filename = +#ifdef _MSC_VER + dir->_f.cFileName; +#else + dir->_e->d_name; +#endif + if (_tinydir_strlen(dir->path) + + _tinydir_strlen(filename) + 1 + _TINYDIR_PATH_EXTRA >= + _TINYDIR_PATH_MAX) + { + /* the path for the file will be too long */ + errno = ENAMETOOLONG; + return -1; + } + if (_tinydir_strlen(filename) >= _TINYDIR_FILENAME_MAX) + { + errno = ENAMETOOLONG; + return -1; + } + + _tinydir_strcpy(file->path, dir->path); + if (_tinydir_strcmp(dir->path, TINYDIR_STRING("/")) != 0) + _tinydir_strcat(file->path, TINYDIR_STRING("/")); + _tinydir_strcpy(file->name, filename); + _tinydir_strcat(file->path, filename); +#ifndef _MSC_VER +#ifdef __MINGW32__ + if (_tstat( +#elif (defined _BSD_SOURCE) || (defined _DEFAULT_SOURCE) \ + || ((defined _XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) \ + || ((defined _POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) \ + || ((defined __APPLE__) && (defined __MACH__)) \ + || (defined BSD) + if (lstat( +#else + if (stat( +#endif + file->path, &file->_s) == -1) + { + return -1; + } +#endif + _tinydir_get_ext(file); + + file->is_dir = +#ifdef _MSC_VER + !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); +#else + S_ISDIR(file->_s.st_mode); +#endif + file->is_reg = +#ifdef _MSC_VER + !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || + ( + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) && + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) && +#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) && +#endif +#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) && +#endif + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) && + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY)); +#else + S_ISREG(file->_s.st_mode); +#endif + + return 0; +} + +_TINYDIR_FUNC +int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i) +{ + if (dir == NULL || file == NULL) + { + errno = EINVAL; + return -1; + } + if (i >= dir->n_files) + { + errno = ENOENT; + return -1; + } + + memcpy(file, &dir->_files[i], sizeof(tinydir_file)); + _tinydir_get_ext(file); + + return 0; +} + +_TINYDIR_FUNC +int tinydir_open_subdir_n(tinydir_dir *dir, size_t i) +{ + _tinydir_char_t path[_TINYDIR_PATH_MAX]; + if (dir == NULL) + { + errno = EINVAL; + return -1; + } + if (i >= dir->n_files || !dir->_files[i].is_dir) + { + errno = ENOENT; + return -1; + } + + _tinydir_strcpy(path, dir->_files[i].path); + tinydir_close(dir); + if (tinydir_open_sorted(dir, path) == -1) + { + return -1; + } + + return 0; +} + +/* Open a single file given its path */ +_TINYDIR_FUNC +int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path) +{ + tinydir_dir dir; + int result = 0; + int found = 0; + _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX]; + _tinydir_char_t file_name_buf[_TINYDIR_PATH_MAX]; + _tinydir_char_t *dir_name; + _tinydir_char_t *base_name; +#if (defined _MSC_VER || defined __MINGW32__) + _tinydir_char_t drive_buf[_TINYDIR_PATH_MAX]; + _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX]; +#endif + + if (file == NULL || path == NULL || _tinydir_strlen(path) == 0) + { + errno = EINVAL; + return -1; + } + if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) + { + errno = ENAMETOOLONG; + return -1; + } + + /* Get the parent path */ +#if (defined _MSC_VER || defined __MINGW32__) +#if ((defined _MSC_VER) && (_MSC_VER >= 1400)) + errno = _tsplitpath_s( + path, + drive_buf, _TINYDIR_DRIVE_MAX, + dir_name_buf, _TINYDIR_FILENAME_MAX, + file_name_buf, _TINYDIR_FILENAME_MAX, + ext_buf, _TINYDIR_FILENAME_MAX); +#else + _tsplitpath( + path, + drive_buf, + dir_name_buf, + file_name_buf, + ext_buf); +#endif + + if (errno) + { + return -1; + } + +/* _splitpath_s not work fine with only filename and widechar support */ +#ifdef _UNICODE + if (drive_buf[0] == L'\xFEFE') + drive_buf[0] = '\0'; + if (dir_name_buf[0] == L'\xFEFE') + dir_name_buf[0] = '\0'; +#endif + + /* Emulate the behavior of dirname by returning "." for dir name if it's + empty */ + if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0') + { + _tinydir_strcpy(dir_name_buf, TINYDIR_STRING(".")); + } + /* Concatenate the drive letter and dir name to form full dir name */ + _tinydir_strcat(drive_buf, dir_name_buf); + dir_name = drive_buf; + /* Concatenate the file name and extension to form base name */ + _tinydir_strcat(file_name_buf, ext_buf); + base_name = file_name_buf; +#else + _tinydir_strcpy(dir_name_buf, path); + dir_name = dirname(dir_name_buf); + _tinydir_strcpy(file_name_buf, path); + base_name = basename(file_name_buf); +#endif + + /* Special case: if the path is a root dir, open the parent dir as the file */ +#if (defined _MSC_VER || defined __MINGW32__) + if (_tinydir_strlen(base_name) == 0) +#else + if ((_tinydir_strcmp(base_name, TINYDIR_STRING("/"))) == 0) +#endif + { + memset(file, 0, sizeof * file); + file->is_dir = 1; + file->is_reg = 0; + _tinydir_strcpy(file->path, dir_name); + file->extension = file->path + _tinydir_strlen(file->path); + return 0; + } + + /* Open the parent directory */ + if (tinydir_open(&dir, dir_name) == -1) + { + return -1; + } + + /* Read through the parent directory and look for the file */ + while (dir.has_next) + { + if (tinydir_readfile(&dir, file) == -1) + { + result = -1; + goto bail; + } + if (_tinydir_strcmp(file->name, base_name) == 0) + { + /* File found */ + found = 1; + break; + } + tinydir_next(&dir); + } + if (!found) + { + result = -1; + errno = ENOENT; + } + +bail: + tinydir_close(&dir); + return result; +} + +_TINYDIR_FUNC +void _tinydir_get_ext(tinydir_file *file) +{ + _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.')); + if (period == NULL) + { + file->extension = &(file->name[_tinydir_strlen(file->name)]); + } + else + { + file->extension = period + 1; + } +} + +_TINYDIR_FUNC +int _TINYDIR_CDECL _tinydir_file_cmp(const void *a, const void *b) +{ + const tinydir_file *fa = (const tinydir_file *)a; + const tinydir_file *fb = (const tinydir_file *)b; + if (fa->is_dir != fb->is_dir) + { + return -(fa->is_dir - fb->is_dir); + } + return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX); +} + +#ifndef _MSC_VER +#ifndef _TINYDIR_USE_READDIR +/* +The following authored by Ben Hutchings +from https://womble.decadent.org.uk/readdir_r-advisory.html +*/ +/* Calculate the required buffer size (in bytes) for directory * +* entries read from the given directory handle. Return -1 if this * +* this cannot be done. * +* * +* This code does not trust values of NAME_MAX that are less than * +* 255, since some systems (including at least HP-UX) incorrectly * +* define it to be a smaller value. */ +_TINYDIR_FUNC +size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp) +{ + long name_max; + size_t name_end; + /* parameter may be unused */ + (void)dirp; + +#if defined _TINYDIR_USE_FPATHCONF + name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX); + if (name_max == -1) +#if defined(NAME_MAX) + name_max = (NAME_MAX > 255) ? NAME_MAX : 255; +#else + return (size_t)(-1); +#endif +#elif defined(NAME_MAX) + name_max = (NAME_MAX > 255) ? NAME_MAX : 255; +#else +#error "buffer size for readdir_r cannot be determined" +#endif + name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1; + return (name_end > sizeof(struct _tinydir_dirent) ? + name_end : sizeof(struct _tinydir_dirent)); +} +#endif +#endif + +#ifdef __cplusplus +} +#endif + +# if defined (_MSC_VER) +# pragma warning(pop) +# endif + +#endif diff --git a/tools/fat/fat.c b/tools/fat/fat.c new file mode 100644 index 0000000..8a622ee --- /dev/null +++ b/tools/fat/fat.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include +#include + +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 \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; +} diff --git a/write.sh b/write.sh new file mode 100644 index 0000000..ab54ab6 --- /dev/null +++ b/write.sh @@ -0,0 +1,31 @@ +echo ------------ +echo COMPILING OS +echo ------------ + +sudo make + +echo --------- +echo Finished! +echo --------- +read -p "Do you want to write the IMG to Floppy? (/dev/sdb) (Y/n) " yn + +case $yn in + y ) + echo --------------------- + echo Writing IMG to Floppy + echo --------------------- + + sudo dd if=./build/main_floppy.img of=/dev/sdb + + echo --------- + echo Finished! + echo --------- + + ;; + n ) echo exiting...; + exit;; + * ) echo invalid response; + exit 1;; +esac + +