**作者注:**该文为我自创操作系统的全部源代码。之所以选择用纯汇编语言而非相对高级的语言来实现,则完全是处于一种对计算机硬件的信仰,因为我受到了大名鼎鼎的MenuetOS系统启发,感慨不已,膜拜万千。但是,写汇编语言的过程简直让人痛苦不堪,光要在屏幕上显示一个当前时间都足以让人崩溃。历时数月的工作,在完成了一个粗糙而又简单的文件操作系统之后(最终写入软盘的系统大小仅几K),我便停下来了,因为工作量实在太大了。但是这个痛并快乐的过程,让我收获很多,因为从此之后,其它再难的编程项目都没有比这个更难受的了,也便没有了理由做任何退缩。
;------该程序为自创的简易操作系统,并设计了一个简单的文件系统,支持目录和文件的创建\------
;------删除、复制、移动等简易操作。程序编译之后直接放到软盘便可引导一台裸机启动和运行,本人亲写并验证------
;------程序中注释掉的部分是在开发过程中的调试辅助输出信息。环境:nasm,虚拟机:vmware.------
NUMsector EQU 8 ; 设置读取到的软盘最大扇区编号(18)
NUMheader EQU 0 ; 设置读取到的软盘最大磁头编号(01)
NUMcylind EQU 0 ; 设置读取到的软盘柱面编号
mbrseg equ 7c0h ; 启动扇区存放段地址
loaderseg equ 800h ; 从软盘读取LOADER到内存的段地址
jmp start
welcome db 'Welcome Jiang OS!','$'
fyread db 'Now Floppy Read Loader:','$'
cylind db 'cylind:?? $',0 ; 设置开始读取的柱面编号
header db 'header:?? $',0 ; 设置开始读取的磁头编号
sector db 'sector:?? $',2 ; 设置开始读取的扇区编号;第1扇区是MBR,可以不读
FloppyOK db '---Floppy Read OK','$'
Fyerror db '---Floppy Read Error' ,'$'
Fycontent db 'Floppy Content is:' ,'$'
start:
call showwelcome ;初始化寄存器,打印必要信息
call loader ;执行loader,把现在这张软盘的数据全部读到8000h开始。
jmp loaderseg:0 ;跳转到内核。执行之后CS=loaderseg=800H,IP=0
;内核程序从物理地址8000开始放
showwelcome:
mov ax,mbrseg
mov ds,ax ;为显示各种提示信息做准备
mov ax,loaderseg
mov es,ax ;为读软盘数据到内存做准备,因为读软盘需地址控制---ES:BX
mov si,welcome
call printstr
call newline
ret
loader:
mov si, fyread
call printstr
call newline
call folppyload ;将软盘的数据全部load到内存,从物理地址8000h开始
;mov si, Fycontent
;call printstr
;call showdata ;可以验证一下从软盘读入的kernal程序数据是否正确(二进制)
ret
showdata: mov si,0 ;验证显示从软盘读取到内存的数据
mov ax, 800h
mov es,ax
mov cx,50 ;控制输出的数据长度
nextchar: mov al,[es:si]
mov ah,0eh
int 10h
inc si
loop nextchar
RET
folppyload:
call read1sector
MOV AX,ES
ADD AX,0x0020
MOV ES,AX ;一个扇区占512B=200H,刚好能被整除成完整的段,因此只需改变ES值,无需改变BP即可。
inc byte [sector+11]
cmp byte [sector+11],NUMsector+1
jne folppyload ;读完一个扇区
mov byte [sector+11],1
inc byte [header+11]
cmp byte [header+11],NUMheader+1
jne folppyload ;读完一个磁头
mov byte [header+11],0
inc byte [cylind+11]
cmp byte [cylind+11],NUMcylind+1
jne folppyload ;读完一个柱面
ret
numtoascii: ;将2位数的10进制数分解成ASII码才能正常显示。如柱面56 分解成出口ascii: al:35,ah:36
mov ax,0
mov al,cl ;输入cl
mov bl,10
div bl
add ax,3030h
ret
readinfo: ;显示当前读到哪个扇区、哪个磁头、哪个柱面
mov si,cylind
call printstr
mov si,header
call printstr
mov si,sector
call printstr
ret
read1sector: ;读取一个扇区的通用程序。扇区参数由 sector header cylind控制
mov cl, [sector+11] ;为了能实时显示读到的物理位置
call numtoascii
mov [sector+7],al
mov [sector+8],ah
mov cl,[header+11]
call numtoascii
mov [header+7],al
mov [header+8],ah
mov cl,[cylind+11]
call numtoascii
mov [cylind+7],al
mov [cylind+8],ah
MOV CH,[cylind+11] ; 柱面从0开始读
MOV DH,[header+11] ; 磁头从0开始读
mov cl,[sector+11] ; 扇区从1开始读
call readinfo ;显示软盘读到的物理位置
mov di,0
retry:
MOV AH,02H ; AH=0x02 : AH设置为0x02表示读取磁盘
MOV AL,1 ; 要读取的扇区数
mov BX, 0 ; ES:BX表示读到内存的地址 0x0800*16 + 0 = 0x8000
MOV DL,00H ; 驱动器号,0表示第一个软盘,是的,软盘。。硬盘C:80H C 硬盘D:81H
INT 13H ; 调用BIOS 13号中断,磁盘相关功能
JNC READOK ; 未出错则跳转到READOK,出错的话则会使EFLAGS寄存器的CF位置1
inc di
MOV AH,0x00
MOV DL,0x00 ; A驱动器
INT 0x13 ; 重置驱动器
cmp di, 5 ; 软盘很脆弱,同一扇区如果重读5次都失败就放弃
jne retry
mov si, Fyerror
call printstr
call newline
jmp exitread
READOK: mov si, FloppyOK
call printstr
call newline
exitread:
ret
printstr: ;显示指定的字符串, 以'$'为结束标记
mov al,[si]
cmp al,'$'
je disover
mov ah,0eh
int 10h
inc si
jmp printstr
disover:
ret
newline: ;显示回车换行
mov ah,0eh
mov al,0dh
int 10h
mov al,0ah
int 10h
ret
times 510-($-$$) db 0
db 0x55,0xaa
;-------------------------------------------------------------------------------
;------------------此为扇区分界线,线上为第1扇区,线下为第2扇区-----------------
;-------------------------------------------------------------------------------
jmp kernal
rdataseg equ 1000h ;从硬盘读取出的数据存放段地址 20000h
wdataseg equ 2000h ;写进硬盘的数据存放段地址 800h为内核程序段地址,也即8000h。
hdNUMsector EQU 1 ; 设置读取到的硬盘最大扇区编号 最大:191
hdNUMheader EQU 1 ; 设置读取到的硬盘最大磁头编号 最大:255
hdNUMcylind EQU 0 ; 设置读取到的硬盘柱面编号 最大:198
whdNUMsector EQU 2 ; 设置写到硬盘的最大扇区编号
whdNUMheader EQU 0 ; 设置写到硬盘的最大磁头编号
whdNUMcylind EQU 0 ; 设置写到硬盘的柱面编号
keralmsg db 'Now You Have Comed Kernal!','$'
addrmsg db 'Kernal Data Begins Address:8000h','$'
hdpara db 'Now Hdisk Paras Read:','$'
hdwrite db 'Now Hdisk Write Files:','$'
hdread db 'Now Hdisk Read Files:','$'
pahdcylind db 'cylind:?? $',0 ; 硬盘的柱面参数 0-6。共10bit,只取低8bit进行试验。
pahdheader db 'header:?? $',0 ; 硬盘的磁头参数 0-63
pahdsector db 'sector:?? $',0 ; 硬盘的扇区参数 1-63
pahdOK db '---Hdisk Paras Read OK','$'
pahderror db '---Hdisk Paras Read Error' ,'$'
hdcylind db 'cylind:?? $',0 ; 设置开始读取的柱面编号
hdheader db 'header:?? $',0 ; 设置开始读取的磁头编号
hdsector db 'sector:?? $',1 ; 设置开始读取的扇区编号
hdOK db '---Hdisk Read OK','$'
hderror db '---Hdisk Read Error' ,'$'
hdcontent db 'Hdisk Content is:' ,'$'
whdcylind db 'cylind:?? $',0 ; 设置开始写的柱面编号
whdheader db 'header:?? $',0 ; 设置开始写的磁头编号
whdsector db 'sector:?? $',1 ; 设置开始写的扇区编号
whdOK db '---Hdisk Write OK','$'
whderror db '---Hdisk Write Error' ,'$'
syscome db '------------------------------------------------------',13,10,\
'Now You Have Comed Jiang OS File System,Enjoy Youself!' ,13,10,\
'------------------------------------------------------','$'
pwdinfo db 'C:\>','$'
cdcom db 'cd'
dircom db 'dir'
clscom db 'cls'
formatcom db 'format'
mkdircom db 'mkdir' ;'deldir dir1????...'
deldircom db 'deldir'
inputcom db '?????????????????' ;'mkdir dir1????...'
yescommsg db 'True Command','$'
nocommsg db 'Bad Command','$'
direrror db 'Not Empty Directory,No Permited Delelte','$'
formatmsg db 'Now Format Done,All Datas Lost!','$'
delname db '????????'
bootname db '/???????'
upname db '..??????'
cdname db '????????','$' ;目录名最多8BIT
dirname db '????????','$' ;目录名最多8BIT
filename db '????????'
parentidmsg db 'parentid:??','$'
diridmsg db 'deldirid:??','$'
parentid db -1
dirid db 0
deldirid db 0
subdirid db 0
kernal: mov ax,loaderseg ;跳转到内核之后,全部寄存器启用新的段地址
;ES=CS=800H
mov es,ax
mov ax,loaderseg
sub ax,20h
mov ds,ax ;DS=800H-20H改变段地址之后,需要减去引导扇区的偏移量
mov si, keralmsg
call newline2
call printstr2