Initital Commit
This commit is contained in:
parent
cb8480c234
commit
803c228073
74
Makefile
Normal file
74
Makefile
Normal file
@ -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
|
||||||
7
bochs_config
Normal file
7
bochs_config
Normal file
@ -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"
|
||||||
52
build.sh
Normal file
52
build.sh
Normal file
@ -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
|
||||||
|
|
||||||
BIN
build/stage1.bin
Normal file
BIN
build/stage1.bin
Normal file
Binary file not shown.
BIN
build/stage2/asm/main.obj
Normal file
BIN
build/stage2/asm/main.obj
Normal file
Binary file not shown.
BIN
build/stage2/asm/x86.obj
Normal file
BIN
build/stage2/asm/x86.obj
Normal file
Binary file not shown.
BIN
build/stage2/c/main.obj
Normal file
BIN
build/stage2/c/main.obj
Normal file
Binary file not shown.
26
bx_enh_dbg.ini
Normal file
26
bx_enh_dbg.ini
Normal file
@ -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
|
||||||
27
debug.sh
Normal file
27
debug.sh
Normal file
@ -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
|
||||||
5
readme.txt
Normal file
5
readme.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
###LAMBDA OS README
|
||||||
|
|
||||||
|
Test README
|
||||||
|
|
||||||
|
OMG i love CHEESE
|
||||||
14
src/bootloader/stage1/Makefile
Normal file
14
src/bootloader/stage1/Makefile
Normal file
@ -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
|
||||||
383
src/bootloader/stage1/boot.asm
Normal file
383
src/bootloader/stage1/boot.asm
Normal file
@ -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:
|
||||||
|
|
||||||
33
src/bootloader/stage2/Makefile
Normal file
33
src/bootloader/stage2/Makefile
Normal file
@ -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
|
||||||
19
src/bootloader/stage2/disk.h
Normal file
19
src/bootloader/stage2/disk.h
Normal file
@ -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);
|
||||||
|
|
||||||
12
src/bootloader/stage2/linker.lnk
Normal file
12
src/bootloader/stage2/linker.lnk
Normal file
@ -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
|
||||||
28
src/bootloader/stage2/main.asm
Normal file
28
src/bootloader/stage2/main.asm
Normal file
@ -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
|
||||||
13
src/bootloader/stage2/main.c
Normal file
13
src/bootloader/stage2/main.c
Normal file
@ -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");
|
||||||
|
}
|
||||||
1
src/bootloader/stage2/main.err
Normal file
1
src/bootloader/stage2/main.err
Normal file
@ -0,0 +1 @@
|
|||||||
|
main.c(9): Warning! W303: Parameter 'bootDrive' has been defined, but not referenced
|
||||||
20
src/bootloader/stage2/stdint.h
Normal file
20
src/bootloader/stage2/stdint.h
Normal file
@ -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
|
||||||
222
src/bootloader/stage2/stdio.c
Normal file
222
src/bootloader/stage2/stdio.c
Normal file
@ -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;
|
||||||
|
}
|
||||||
5
src/bootloader/stage2/stdio.err
Normal file
5
src/bootloader/stage2/stdio.err
Normal file
@ -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)
|
||||||
10
src/bootloader/stage2/stdio.h
Normal file
10
src/bootloader/stage2/stdio.h
Normal file
@ -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, ...);
|
||||||
102
src/bootloader/stage2/x86.asm
Normal file
102
src/bootloader/stage2/x86.asm
Normal file
@ -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);
|
||||||
26
src/bootloader/stage2/x86.h
Normal file
26
src/bootloader/stage2/x86.h
Normal file
@ -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);
|
||||||
14
src/kernel/Makefile
Normal file
14
src/kernel/Makefile
Normal file
@ -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
|
||||||
50
src/kernel/main.asm
Normal file
50
src/kernel/main.asm
Normal file
@ -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
|
||||||
2
src/micron/Makefile
Normal file
2
src/micron/Makefile
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
CC = gcc
|
||||||
|
BUILD_DIR = /build/micron
|
||||||
BIN
src/micron/main
Normal file
BIN
src/micron/main
Normal file
Binary file not shown.
232
src/micron/main.c
Normal file
232
src/micron/main.c
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
/*----------------*\
|
||||||
|
|Nanite OS |
|
||||||
|
|Copyright (C) 2024|
|
||||||
|
|Tyler McGurrin |
|
||||||
|
\*----------------*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
848
src/micron/tinydir.h
Normal file
848
src/micron/tinydir.h
Normal file
@ -0,0 +1,848 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2013-2021, tinydir authors:
|
||||||
|
- Cong Xu
|
||||||
|
- Lautis Sun
|
||||||
|
- Baudouin Feildel
|
||||||
|
- Andargor <andargor@yahoo.com>
|
||||||
|
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 <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# endif
|
||||||
|
# include <windows.h>
|
||||||
|
# include <tchar.h>
|
||||||
|
# pragma warning(push)
|
||||||
|
# pragma warning (disable : 4996)
|
||||||
|
#else
|
||||||
|
# include <dirent.h>
|
||||||
|
# include <libgen.h>
|
||||||
|
# include <sys/stat.h>
|
||||||
|
# include <stddef.h>
|
||||||
|
#endif
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
# include <tchar.h>
|
||||||
|
#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 <windows.h>
|
||||||
|
# define _TINYDIR_PATH_MAX MAX_PATH
|
||||||
|
#elif defined __linux__
|
||||||
|
# include <limits.h>
|
||||||
|
# ifdef PATH_MAX
|
||||||
|
# define _TINYDIR_PATH_MAX PATH_MAX
|
||||||
|
# endif
|
||||||
|
#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
||||||
|
# include <sys/param.h>
|
||||||
|
# if defined(BSD)
|
||||||
|
# include <limits.h>
|
||||||
|
# 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 <unistd.h>
|
||||||
|
#endif
|
||||||
|
#if _BSD_SOURCE || _SVID_SOURCE || \
|
||||||
|
(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
|
||||||
|
# define _TINYDIR_HAS_DIRFD
|
||||||
|
# include <sys/types.h>
|
||||||
|
#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 <ben@decadent.org.uk>
|
||||||
|
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
|
||||||
183
tools/fat/fat.c
Normal file
183
tools/fat/fat.c
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
31
write.sh
Normal file
31
write.sh
Normal file
@ -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
|
||||||
|
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user