一、选择文件系统
通常来说,自制操作系统都是fat12,fat16,ext之类的
经过3周的努力,查阅了各方的资料,终于——失败
看着一堆的db,什么ata,我陷入了沉默
为什么不自制文件系统呢?
说干就干
尽管这是纯汇编操作系统,我还是决定用python构建文件系统
其实python只是为了编译方便...
fs.py
import os
import sys
def create_fs_asm(files):
"""完全保持原样的暴力文件系统实现,仅添加二进制支持"""
asm_content = """; 完全保持原样的暴力文件系统
section .data
global fs_files_count
fs_files_count dd %d
; 文件系统API
global fs_init, fs_list_files, fs_read_file
section .text
fs_init:
ret
fs_list_files:
mov esi, file_names
ret
fs_read_file:
; 直接暴力比较文件名
mov edi, esi
""" % len(files)
for i, filename in enumerate(files):
if not os.path.exists(filename):
continue
base = os.path.splitext(filename)[0]
clean_base = base.replace('.', '_').replace(' ', '_')
asm_content += f"""
; 检查文件: {filename}
mov esi, file_{clean_base}_name
call str_compare
je .found_{i+1}
"""
asm_content += """
; 文件未找到
stc
ret
"""
for i, filename in enumerate(files):
if not os.path.exists(filename):
continue
base = os.path.splitext(filename)[0]
clean_base = base.replace('.', '_').replace(' ', '_')
asm_content += f"""
.found_{i+1}:
mov esi, file_{clean_base}_content_str
clc
ret
"""
asm_content += """
str_compare:
push eax
push esi
push edi
.loop:
mov al, [esi]
cmp al, [edi]
jne .not_equal
test al, al
jz .equal
inc esi
inc edi
jmp .loop
.equal:
xor eax, eax
jmp .done
.not_equal:
or eax, 1
.done:
pop edi
pop esi
pop eax
ret
"""
asm_content += """
section .data
file_names db """
name_list = []
for filename in files:
if not os.path.exists(filename):
continue
name_list.append(f"'{filename} '")
asm_content += ', '.join(name_list) + ',0\n\n'
for filename in files:
if not os.path.exists(filename):
continue
base = os.path.splitext(filename)[0]
clean_base = base.replace('.', '_').replace(' ', '_')
# 二进制模式读取文件
with open(filename, 'rb') as f:
content = f.read()
# 生成转义后的字符串
escaped = []
for byte in content:
if byte == 0: # 处理null字节
escaped.append('\\0')
elif byte == ord('\''): # 处理单引号
escaped.append('\\\'')
elif byte == ord('\\'): # 处理反斜杠
escaped.append('\\\\')
elif 32 <= byte <= 126: # 可打印ASCII
escaped.append(chr(byte))
else: # 其他不可打印字符保持原样
escaped.append(f'\\x{byte:02x}')
asm_content += f"""
; 文件: {filename}
file_{clean_base}_name db '{filename}',0
file_{clean_base}_content_str db '{''.join(escaped)}',0
"""
with open("fs.asm", "w") as f:
f.write(asm_content)
print("Created original brute-force fs.asm")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python fs.py file1 [file2 ...]")
sys.exit(1)
create_fs_asm(sys.argv[1:])
本次的makefile
all:
make bin
make img
make run
bin:
nasm boot.asm -o boot.bin
nasm loader.asm -o loader.bin
#gcc -c -O0 -fno-builtin -m32 -fno-stack-protector -o main.o main.c
nasm -f elf -o a.o a.asm
i686-elf-ld -e _start -o a.run a.o
python fs.py 123.txt qwe.txt a.run
nasm -f elf -o kernel.o kernel.asm
nasm -f elf -o io.o io.asm
nasm -f elf -o shell.o shell.asm
nasm -f elf -o file.o file.asm
nasm -f elf -o fs.o fs.asm
i686-elf-ld -s -Ttext 0x100000 -o kernel.bin kernel.o io.o file.o shell.o fs.o
img : boot.bin loader.bin kernel.bin
#dd if=boot.bin of=a.img bs=512 count=1
#dd if=loader.bin of=a.img bs=512 seek=1 conv=notrunc
#python img.py
edimg imgin:1.img \
wbinimg src:boot.bin len:512 from:0 to:0 \
copy from:loader.bin to:@: \
copy from:kernel.bin to:@: \
imgout:a.img
run : a.img
#qemu-system-i386 -fda a.img
qemu-system-i386 -fda a.img #-hda disk.img -boot a
shell.asm
;shell.asm
[bits 32]
extern scroll_screen
[section .data]
; Shell界面
msg db "[root@Plain]-(/)# ", 0
cmd_buffer times 80 db 0
; 命令定义
cmd_echo db "echo", 0
cmd_help db "help", 0
cmd_ls db "ls", 0
cmd_cat db "cat", 0
cmd_write db "write", 0
cmd_clear db "clear", 0
cmd_time db "time", 0
time_str db "HH:MM:SS", 0
; 帮助信息
help_msg1 db "Available commands:", 0
help_msg2 db " echo <message> - Display message", 0
help_msg3 db " help - Show this help", 0
help_msg4 db " ls - List files", 0
help_msg5 db " cat <file> - Show file content", 0
help_msg6 db " write <file> > <content> - Write to file", 0
help_msg7 db " clear - Clear screen", 0
; 错误和信息消息
not_msg db "Error: Command not found: ", 0
error_msg db "ERROR: Disk operation failed", 0
dir_entry db " [DIR] ", 0
write_success db "Write successful", 0
write_fail db "Write failed", 0
invalid_format_msg db "Invalid write format. Use: write filename > content", 0
[section .text]
extern print_str, put_char, get_key, clear_screen, fs_list_files, fs_files_count, fs_read_file
extern scroll_screen
global shell
shell:
cmp ebx, 25
ja .scroll
mov ecx, 0
mov esi, msg
mov ah, 0x0F
call print_str
; 初始化命令缓冲区
mov edi, cmd_buffer
mov ecx, 18 ; 从第18列开始输入
mov byte [edi], 0 ; 清空缓冲区
.input_loop:
call get_key
test al, al
jz .input_loop
; 处理回车
cmp al, 0x0A
je .execute
; 处理退格
cmp al, 0x08
je .backspace
; 存储并显示字符
mov [edi], al
inc edi
mov ah, 0x0F
call put_char
inc ecx
jmp .input_loop
.backspace:
; 退格处理
cmp edi, cmd_buffer
je .input_loop ; 忽略空退格
dec edi
dec ecx
mov al, ' '
mov ah, 0x0F
call put_char
jmp .input_loop
.scroll:
call scroll_screen
dec ebx
jmp shell
.execute:
; 添加字符串结束符
mov byte [edi], 0
; 检查空命令
mov esi, cmd_buffer
call is_empty
je .empty_cmd
; 跳过前导空格
call skip_spaces
test al, al
jz .empty_cmd
; 检查help命令
mov edi, cmd_help
call cmd_cmp
je .show_help
; 检查echo命令
mov edi, cmd_echo
call cmd_cmp
je .do_echo
; 检查echo命令
mov edi, cmd_ls
call cmd_cmp
je do_ls
mov edi, cmd_time
call cmd_cmp
je do_time
mov edi, cmd_cat
call cmd_cmp
je do_cat
;mov edi, cmd_write
;call cmd_cmp
;je do_write
; 检查clear命令
mov edi, cmd_clear
call cmd_cmp
je .do_clear
; 未知命令处理
inc ebx
mov ecx, 0
mov esi, not_msg
mov ah, 0x0C ; 红色错误信息
call print_str
; 只显示命令部分(第一个空格前的内容)
mov esi, cmd_buffer
call print_command_part
inc ebx
jmp shell
.empty_cmd:
inc ebx
mov ecx, 0
jmp shell
.show_help:
; 显示帮助信息
inc ebx
mov ecx, 0
mov esi, help_msg1
mov ah, 0x0A ; 绿色帮助信息
call print_str
inc ebx
mov ecx, 0
mov esi, help_msg2
call print_str
inc ebx
mov ecx, 0
mov esi, help_msg3
call print_str
inc ebx
mov ecx, 0
mov esi, help_msg4
call print_str
inc ebx
mov ecx, 0
mov esi, help_msg5
call print_str
inc ebx
mov ecx, 0
mov esi, help_msg6
call print_str
inc ebx
mov ecx, 0
mov esi, help_msg7
call print_str
inc ebx
jmp shell
.do_echo:
; 跳过"echo"和后续空格
add esi, 4
call skip_spaces
test al, al
jz .no_args1 ; 无参数情况
; 显示echo参数
inc ebx
mov ecx, 0
mov ah, 0x0F
call print_str
.no_args1:
inc ebx ; 换行
jmp shell
; === clear命令实现 ===
.do_clear:
call clear_screen
mov ebx, 0
mov ecx, 0
jmp shell
; === 辅助函数 ===
; 打印命令部分(第一个空格前的内容)
print_command_part:
pusha
mov ecx, 26 ; 错误信息后位置
.loop:
lodsb
test al, al
jz .done
cmp al, ' '
je .done
mov ah, 0x0F
call put_char
inc ecx
jmp .loop
.done:
popa
ret
; 检查字符串是否为空或只有空格
is_empty:
push esi
.loop:
lodsb
cmp al, ' '
je .loop
test al, al
pop esi
ret
; 跳过字符串中的空格
skip_spaces:
lodsb
cmp al, ' '
je skip_spaces
dec esi ; 回退到第一个非空格字符
ret
; 命令比较函数
cmd_cmp:
pusha
.compare:
mov al, [esi]
mov bl, [edi]
; 检查命令是否结束(空格或字符串结束)
cmp al, ' '
je .check_cmd_end
test al, al
jz .check_cmd_end
; 转换为小写比较
cmp al, 'A'
jb .no_change1
cmp al, 'Z'
ja .no_change1
add al, 0x20
.no_change1:
cmp bl, 'A'
jb .no_change2
cmp bl, 'Z'
ja .no_change2
add bl, 0x20
.no_change2:
cmp al, bl
jne .not_equal
inc esi
inc edi
jmp .compare
.check_cmd_end:
; 检查命令字符串是否也结束了
cmp byte [edi], 0
jne .not_equal
.equal:
popa
xor eax, eax ; ZF=1
ret
.not_equal:
popa
or eax, 1 ; ZF=0
ret
; 显示固定数量的字符
print_nchars:
pusha
mov ah, 0x0F
.loop:
lodsb
call put_char
loop .loop
popa
ret
print_hex:
pushad
mov ecx, 8
.loop:
rol eax, 4
mov ebx, eax
and ebx, 0x0f
mov bl, [hex_chars + ebx]
mov ah, 0x0F
call put_char
loop .loop
popad
ret
do_time:
call get_time
inc ebx ; 换行
mov ecx, 0
mov esi, time_str
mov ah, 0x0F ; 白色文字
call print_str
jmp shell
get_time:
pushad
; 禁用NMI并读取小时
mov al, 0x04 ; 小时寄存器
or al, 0x80 ; 禁用NMI
out 0x70, al
in al, 0x71
call bcd_to_ascii
mov [time_str], dh
mov [time_str+1], dl
; 读取分钟
mov al, 0x02
or al, 0x80
out 0x70, al
in al, 0x71
call bcd_to_ascii
mov [time_str+3], dh
mov [time_str+4], dl
; 读取秒
mov al, 0x00
or al, 0x80
out 0x70, al
in al, 0x71
call bcd_to_ascii
mov [time_str+6], dh
mov [time_str+7], dl
popad
ret
bcd_to_ascii:
; 将AL中的BCD码转换为两个ASCII字符,存储在DH和DL中
mov dh, al
shr dh, 4
add dh, '0'
mov dl, al
and dl, 0x0F
add dl, '0'
ret
; === ls命令实现 ===
do_ls:
call fs_list_files
; 显示文件名
inc ebx
mov ecx, 0
mov ah, 0x0F
call print_str
; 换行
inc ebx
mov ecx, 0
jmp shell
; === cat命令实现 ===
do_cat:
; 跳过"cat"和空格
add esi, 3
call skip_spaces
test al, al
jz .no_filename
; 直接调用文件系统
call fs_read_file
jc .file_not_found
; 显示内容 (esi已指向内容字符串)
inc ebx
mov ecx, 0
mov ah, 0x0F
call print_str ; 直接打印整个内容
inc ebx
jmp shell
.file_not_found:
inc ebx
mov ecx, 0
mov esi, no_file_msg
mov ah, 0x0C
call print_str
; 显示尝试的文件名
mov esi, cmd_buffer+3
mov ah, 0x0F
call print_str
inc ebx
jmp shell
.no_filename:
inc ebx
mov ecx, 0
mov esi, cat_usage_msg
mov ah, 0x0C
call print_str
jmp shell
section .bss
filename_buffer resb 32 ; 存储临时文件名
; === 数据区 ===
[section .data]
no_file_msg db "File not found: ", 0
cat_usage_msg db "Usage: cat <filename>", 0
hex_chars db "0123456789ABCDEF"
一个make直接编译
终于完成了文件系统!
我知道你很高兴,但你先别高兴
我们没有用其他文件系统,这意味着我们无法执行写入操作
只能修改内存里文件位置指针指向的文件内容(就是重启后没有数据)
但我们的目的很简单,只是运行程序
这将在下一节实现