自制DOS系统XYZDOS v0.1a源代码
DOS,即磁盘操作系统(DiskOperatingSystem),是早期个人计算机上的一类操作系统。从1981年MS-DOS1.0直到1995年MS-DOS 6.22的15年间,DOS作为微软公司在个人计算机上使用的一个操作系统载体,推出了多个版本。DOS在IBM PC 兼容机市场中占有举足轻重的地位。可以直接操纵管理硬盘的文件,以DOS的形式运行。
什么是DOS
DOS,即磁盘操作系统(Disk Operating System),是早期个人计算机上的一类操作系统。
从1981年MS-DOS1.0直到1995年MS-DOS 6.22的15年间,DOS作为微软公司在个人计算机上使用的一个操作系统载体,推出了多个版本。DOS在IBM PC 兼容机市场中占有举足轻重的地位。可以直接操纵管理硬盘的文件,以DOS的形式运行。
名称由来
磁盘操作系统是早期个人计算机上的一类操作系统。从1981年直到1995年的15年间,DOS在IBM PC兼容机市场中占有举足轻重的地位。而且,若是把部分以DOS为基础的Microsoft Windows版本,如Windows 95、Windows 98、Windows 98 SE和Windows Me等都算进去的话,那么其商业寿命至少可以算到2000年.
家族包括86-DOS、MS-DOS、PC-DOS、DR-DOS、FreeDOS、PTS-DOS、ROM-DOS、JM-OS等,其中以MS-DOS最为著名。虽然这些系统常被简称为"DOS",但几乎没有系统单纯以"DOS"命名(只有一个毫无关连的20世纪60年代IBM大型主机操作系统以此命名)。此外,有几个和DOS无关、在非x86的微计算机系统上运行的磁盘操作系统在名称中也有 “DOS” 字眼,而且在专门讨论该机器的场合中也会简称为"DOS"(例如:AmigaDOS、AMSDOS、ANDOS、AppleDOS、Atari DOS、Commodore DOS、CSI-DOS、ProDOS、TRS-DOS等),但这些系统和DOS可执行文件以及MS-DOS API并不兼容。
发展简史
DOS是1979年由微软公司为IBM个人电脑开发的MS-DOS,它是一个单用户单任务操作系统。后来DOS的概念也包括了其它公司生产的与MS-DOS兼容的系统,如PC-DOS、DR-DOS, 以及一些其它相对不太出名的DOS兼容产品。它们在1985年到1995年及其后的一段时间内占据操作系统的统治地位,最著名和广泛使用的DOS系统从1981年直到1995年的15年间微软在推出Windows 95之后,宣布MS-DOS不再单独发布新版本。不过FreeDOS等与MS-DOS兼容的DOS则在继续发展着。
从1981年直到1995年的15年间,磁盘操作系统在IBM PC兼容机市场中占有举足轻重的地位。而且,若是把部分以DOS为基础的Microsoft Windows版本,如Windows 95、Windows 98和Windows Me等都算进去的话,那么其商业寿命至少可以算到2000年。微软的所有后续版本中,磁盘操作系统仍然被保留着。
虽然大多数用户将1981年最早发表的术语“PC”与IBM PC或IBM PC兼容机等同看待, 但是实际上个人计算机在1970年已经开始使用。DOS是IBM PC及其兼容机用的操作系统(OS)。在DOS之前,一些微机为其操作系统使用内部BASIC编程语言,而另外一些使用Digital Research公司开发的CP/M操作系统(此公司提供了DR-DOS)。微软公司于1970年后开发了MS-DOS,当时,该公司为不同微机开发了像BASIC这类编程语言;1980年IBM正在设计最早的IBM PC并请微软为其新型机开发多个编程语言。这次CP/M作为选择的操作系统并成为IBM新机种的操作系统。在微软为IBM PC设计语言的过程中,尽管微软公司改进了IBM PC的语言设计和应用程序设计,但是他们认识到开发PC操作系统才是其努力方向。然而,由于没有足够的时间从零开始开发一个操作系统。为解决这个问题,微软购买了一个用于8086处理器的操作系统86-DOS,86-DOS操作系统是西雅图电脑产品公司(Seattle Computer Products, SCP)的一名24岁的程序员蒂姆·帕特森(Tim Paterson)花费了四个月时间编的,该操作系统与最早的IBM PC中的8086处理器非常相似。给每台IBM电脑安装一个DOS系统,微软就收取200美金的商业运作模式,造就了微软的初期辉煌。
发展历程
随着电脑硬件的不断发展,从Windows 95到Windows Me中,MS-DOS的核心依然存在,只是加上Windows当作系统的图形界面,直到纯32 位版本的Windows系统(从Windows NT开始,其中就包含了 Windows 2000、Windows XP、Windows 2003、Windows Vista、Windows 7、Windows 8和Windows 10)。由此可见dos的生命力极强。例如系统还原和安装都可能需要DOS,DOS用起来也很方便。
DOS是个非常实用的操作系统,因此,它深深受到国内外人们的普遍喜爱,一直拥有数以亿计的用户。但是,自从1995年以来,Microsoft公司出于商业利益等方面的原因,决定抛弃手中的MS-DOS。当时,DOS已有许多种,除MS-DOS以外,还有PC-DOS,DR-DOS,FreeDOS等,但是由于受到Microsoft公司的挤压,故使用的人数不多,而大多数用户都在使用MS-DOS。由于Microsoft彻底放弃了MS-DOS,而转以开发Windows,引起了广大DOS使用者和爱好者的不满,而且,大家通常所使用的Windows 9x也只是DOS下的一个大型程序罢了,如果没有DOS的支持,它根本无法运行。但也由此可见DOS的功能其实是非常强大的,以及DOS软件拥有巨大的开发潜能,因为像Windows那样强大的DOS增强程序都可以编出来。
比较
Windows是一个多任务的操作环境,但它对程序编写的约束和限制比DOS高得多,因而许多程序员出于对Windows的不满及对DOS开发的价值和潜力的深刻认识,继续开发DOS软件。由于多年的经验的积累,他们的软件开发水平越来越高,因此他们开发出的DOS软件的质量也越来越高。为了开发更多的软件并且增加开发的效率,他们设计了一个又一个DOS软件的开发工具。而用这些开发工具的人也越来越多,并在国外迅速地流传开来,详情可见一篇叫作“DJGPP程序与保护模式”的文章(在“DOS文章”栏目中)。一个尤为重要的是,正如上文所提到,虽然MS-DOS已经不再继续发展了,但并不意味着整个DOS也不再继续发展了。MS-DOS的最高版本是8.0(包含在Windows ME中),随后Microsoft开始开发基于Windows NT架构的Windows,才成了真正独立的操作系统。除了MS-DOS以外,其它的DOS也在发展着。仍在不断发展和更新中的DOS有FreeDOS、PTS-DOS、ROM-DOS等,这些DOS的功能都十分强大,往往超过MS-DOS,而且FreeDOS还是完全免费且自由开放的。因此,程序员们完全可以为它们开发新的DOS软件,而不必依赖于MS-DOS。这大概是90年代末DOS的情况。
现状
随后,一项叫作“开放源代码运动”的活动兴起了。很多程序员和使用者出于对微软的Windows横行霸道的不满,离开了Windows阵营。取而代之的是兴起了对其它操作系统软件的开发,如DOS、Linux和UNIX。这一下设计和开发DOS软件的人迅速增加,并纷纷组成了开发团体,以开发新的DOS和其它非Windows的操作系统的软件,详情可见一篇叫作“DOS和Linux近年来的发展比较”的文章。由于开发者的增多,原先的DOS软件开发器也开始了进一步的更新,而且支持FAT32和长文件名。再加上这次开发软件的有许多是程序员中的精英,所以开发出来的DOS软件的质量自然是今非昔比了。比如,由于Allegro等编程库的出现,在DOS下实现MP3等音乐的播放对他们来说已是轻而易举的了。以这些软件为代表的DOS软件和原来的DOS软件,如DOSAMP,GDS Viewer等的质量相比有着本质的提高。原来认为非常强大的SEA看图软件,也只是个一般强大的软件罢了。另一个很重要的变化在于对FAT32及长文件名的支持。DJGPP程序已经实现了在LFN API存在的情况下对长文件名的完美支持。总之,这些新的DOS软件的整体水平的提高是很显著的。为了挑战Windows的界面,程序员们(经常是集体合作)开发了一个又一个仿Windows的界面增强程序,著名的有Seal,Qube,WinDOS等等。还有人开发出了内核为32位的DOS操作系统,如FreeDOS 32,事实证明,这些程序的编写水平是很高的。这些软件都在不断地开发中,所取得的成绩是有目共睹的。而且它们最显著的特点就是自由开放的发展,如上面所提到的SEAL、FreeDOS 32等等都是遵守著名的GNU GPL协议的自由软件。
自制DOS代码
目前还是实模式,只有一个文件,实现了文件系统和读盘写盘,但是一部分电脑不兼容(用实机装不上)
; XYZDOS version 1.00a
;By Robert Ryan
NUMsector EQU 8 ; 设置读取到的软盘最大扇区编号(18)
NUMheader EQU 0 ; 设置读取到的软盘最大磁头编号(01)
NUMcylind EQU 0 ; 设置读取到的软盘柱面编号
mbr equ 7c0h ; 启动扇区存放段地址
loader equ 800h ; 从软盘读取LOADER到内存的段地址
jmp start
welcome db 'Welcome XYZDOS!','$'
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:' ,'$'
start:
call showwelcome ;初始化寄存器,打印必要信息
call loader ;执行loader,把现在这张软盘的数据全部读到8000h开始。
jmp loader:0 ;跳转到内核。执行之后CS=loader=800H,IP=0
;内核程序从物理地址8000开始放
showwelcome:
mov ax,mbr
mov ds,ax ;为显示各种提示信息做准备
mov ax,loader
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:' ,'$'
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 XYZDOS 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 'TrueCommand.','$'
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,loader ;跳转到内核之后,全部寄存器启用新的段地址
;ES=CS=800H
mov es,ax
mov ax,loader
sub ax,20h
mov ds,ax ;DS=800H-20H改变段地址之后,需要减去引导扇区的偏移量
mov si, keralmsg
call newline2
call printstr2
call newline2
mov si, addrmsg
call printstr2
call newline2
call newline2
filesystem: call newline2
mov si, pwdinfo
call printstr2
mov si, 0
usrinput:
mov ah,0
int 16h ;从键盘读字符 ah=扫描码 al=字符码
mov ah,0eh ;把键盘输入的字符显示出来
int 10h
cmp al, 0dh ;回车作为输入结束标记
je inputover
mov [inputcom+si],al
inc si
jmp usrinput
inputover: call commanddeal
jmp filesystem
showcsseg: call newline2
mov dx, cs
mov al,dh
add al,30h
mov ah,0eh
int 10h
mov al,dl
add al,30h
mov ah,0eh
int 10h
call newline2
ret
commanddeal:
mov si,0
mov cx,dircom-cdcom
nextcom3char:mov ah, [cdcom+si]
mov al, [inputcom+si]
cmp ah, al
jne nextcom3
inc si
loop nextcom3char
jmp cd ;输入的是cd命令
nextcom3: mov si,0
mov cx,clscom-dircom
nextcom2char:mov ah, [dircom+si]
mov al, [inputcom+si]
cmp ah, al
jne nextcom2
inc si
loop nextcom2char
jmp dir ;输入的是 dir命令
nextcom2: mov si,0
mov cx,formatcom-clscom
nextcom1char:mov ah, [clscom+si]
mov al, [inputcom+si]
cmp ah, al
jne nextcom1
inc si
loop nextcom1char
jmp cls ;输入的是 cls命令
nextcom1: mov si,0
mov cx,mkdircom-formatcom
nextcomchar:mov ah, [formatcom+si]
mov al, [inputcom+si]
cmp ah, al
jne nextcom
inc si
loop nextcomchar
jmp format ;输入的是 format命令
nextcom: mov si,0
mov cx,deldircom-mkdircom
nextcomch: mov ah, [mkdircom+si]
mov al, [inputcom+si]
cmp ah, al
jne nextcom4
inc si
loop nextcomch
jmp makedir ;输入的是mkdir命令
nextcom4: mov si,0
mov cx,inputcom-deldircom
nextcomch4: mov ah, [deldircom+si]
mov al, [inputcom+si]
cmp ah, al
jne badcom
inc si
loop nextcomch4
jmp deldir ;输入的是deldir命令
cd:
call cddeal
jmp comdealover
dir:
call dirdeal
jmp comdealover
cls:
call clsdeal
jmp comdealover
format:
call formatdeal
jmp comdealover
makedir:
call makedirdeal
jmp comdealover
deldir:
call deldirdeal
jmp comdealover
badcom: call newline2 ;否则输入的非法命令
mov si, nocommsg
call printstr2
call newline2
comdealover: mov si,0 ;命令处理完成之后,清空用户输入命令的缓冲区数据
mov cx,yescommsg-inputcom
nextinputcom:mov byte [inputcom+si],'?'
inc si
loop nextinputcom
mov si,0 ;命令处理完成之后,清空用户输入dir命令目标文件夹的缓冲区数据
mov cx,dirname-cdname
dec cx ;多了一位'$'
nextinputcom2:mov byte [cdname+si],'?'
inc si
loop nextinputcom2
mov si,0 ;命令处理完成之后,清空用户输入cd命令标文件夹的缓冲区数据
mov cx,filename-dirname
dec cx ;多了一位'$'
nextinputcom3:mov byte [dirname+si],'?'
inc si
loop nextinputcom3
ret
mov si,0 ;命令处理完成之后,清空用户输入deldir命令标文件夹的缓冲区数据
mov cx,bootname-delname
dec cx ;多了一位'$'
nextinputcom4:mov byte [delname+si],'?'
inc si
loop nextinputcom4
ret
cddeal: call getcdname ;根据用户命令获取目录文件名cdname,并存入缓冲区
;mov si, cdname
;call printstr2
;call newline2
mov si,0
mov cx,8
goonboot: mov dh, [cdname+si] ;用户输入
cmp [bootname+si],dh ;标准输入
jne cdcomnext1
inc si
loop goonboot
mov byte [parentid],-1 ;用户输入的是:cd /
call dirdeal ;cd到目标目录后,需要显示子目录
jmp cddealover
cdcomnext1: mov si,0
mov cx,8
goonup: mov dh, [cdname+si] ;用户输入
cmp [upname+si],dh ;标准输入
jne cdcomnext2
inc si
loop goonup
call cdupcomdeal ;用户输入的是:cd ..
call dirdeal ;cd到目标目录后,需要显示子目录
jmp cddealover
cdcomnext2: call commoncddeal ;用户输入的是普通:cd dir1;遍历目录区,求出目标目录的newparentid
call dirdeal ;cd到目标目录后,需要显示子目录
cddealover: ret
cdupcomdeal: call readdirdata
mov si,0
cdupcom : ;cmp byte [es:si+1],'?' ;遍历结束标志
cmp si,2000
je cdupcomdealover
mov dh,[parentid]
cmp byte [es:si], dh ;找出目录区中dirid=当前parentid的目录
jne cdupcomnext
mov dh, [es:si+1]
mov [parentid], dh ;将找到目录的parentid赋值给当前parentid
jmp cdupcomdealover ;一旦找到上层目录就结束
cdupcomnext:add si,10
jmp cdupcom
cdupcomdealover:
ret
ret
getcdname: mov di,0
mov si, 2 ; cd标准命令的长度
cdnamenext:mov al, [inputcom+si+1]
cmp al, '?' ;用户输入文件名的结束位置
je cdnameover
mov [cdname+di],al
inc si
inc di
jmp cdnamenext
cdnameover: ret
commoncddeal: ;cd普通目录
call readdirdata
mov si,0
cdparentid: cmp si,2000 ;遍历结束标志
je cdparentidover
mov dh,[parentid]
cmp byte [es:si+1], dh ;找出目录区中匹配当前parentid的目录
jne cdnextparentid
call ifdestdir
cdnextparentid: add si,10
jmp cdparentid
cdparentidover:
ret
ifdestdir: mov di,si
mov cx,8
mov bx,0
destdir: mov ah, [cdname+bx] ;用户输入的目标目录
mov al, [es:di+2] ;硬盘中目录区的目录
cmp ah, al
jne destdirover
inc di
inc bx
loop destdir
mov dh,[es:si] ;用户cd的目录和目标目录完全匹配
mov [parentid],dh ;将dirid的值赋到parentid,完成cd目录切换
;call showparentid
destdirover: ret
showparentid:
mov cl, [parentid] ;为了能实时显示parentid
call numtoascii2
mov [parentidmsg+9],al
mov [parentidmsg+10],ah
call newline2
mov si, parentidmsg
call printstr2
call newline2
ret
readdirdata: ;读取目录数据结构区数据,在第12-15扇区
mov byte [hdsector+11],12 ;目录数据结构区在第12-15扇区
mov byte [hdheader+11],0
mov byte [hdcylind+11],0
mov ax,rdataseg
mov es,ax
dirhdiskread:
call read1sector2 ;把目录数据区的扇区全部读出来
MOV AX,ES
ADD AX,0x0020
MOV ES,AX ;一个扇区占512B=200H,刚好能被整除成完整的段,因此只需改变ES值,无需改变BP即可。
inc byte [hdsector+11]
cmp byte [hdsector+11],16
jne dirhdiskread
mov ax,rdataseg
mov es,ax
;call newline2
;call showdata2
;call newline2
ret
dirdeal: call readdirdata
mov si,0
goonparentid: cmp si,2000 ;遍历结束标志
je parentidover
mov dh,[parentid]
cmp byte [es:si+1], dh
jne nextparentid
call showdirname
nextparentid: add si,10
jmp goonparentid
parentidover: ret
showdirname: call newline2
mov di, si
nextdirname: mov al,[es:di+2] ;显示目录名称, 以'?'为结束标记
cmp al,'?'
je showdirnameover
mov ah,0eh
int 10h
inc di
jmp nextdirname
showdirnameover:;call newline2
ret
clsdeal: ;清屏
mov ah,00h
mov al,03h ;80*25标准彩色文本
int 10h
ret
getdelname: mov di,0
mov si, inputcom-deldircom ; 排除掉 mkdir标准命令的长度
delnamenextc:mov al, [inputcom+si+1]
cmp al, '?' ;用户输入文件名的结束位置
je getdelnameover
mov [delname+di],al
inc si
inc di
jmp delnamenextc
getdelnameover: ret
deldirdeal: call getdelname ;根据用户命令获取目录文件名dirname,并存入缓冲区
;mov si, dirname
;call printstr2
;call newline2
call deldestdir ;删除目标目录
ret
deldestdir: call posdeldir ;定位目标目录,出口为deldirid
call readdirdata
mov si,0
godeldestdir: ;还需要判断是否空目录执行删除操作
cmp si,2000 ;遍历结束标志
je deldestdirall
mov dh,[deldirid]
cmp byte [es:si+1], dh ;找出目录区中匹配当前parentid的目录为子目录
je notemptydir ;一旦发现有子目录
add si,10
jmp godeldestdir
notemptydir: ;call newline2
mov si,direrror
call printstr2
call newline2
jmp deldestdirover
deldestdirall: call deldestdirdeal ;遍历完说明是空目录,可以执行删除操作
deldestdirover: ret
deldestdirdeal:
mov ah,0
mov al,[deldirid]
call delonedirdata ;删除一个目录,以al为目录ID参数
mov ah,0
mov al,[deldirid]
call delonedirno ;清除一个目录编号占用标记,以al为目录ID参数
;call showdeldirid
ret
delonedirno: push ax ;把参数带进函数
mov byte [hdsector+11],1 ;从目录文件控制信息扇区(第1扇区)读取目录编号占用标记
mov byte [hdheader+11],0
mov byte [hdcylind+11],0
mov ax,rdataseg ;8000开始存放程序,10000开始存放硬盘读进的数据
mov es,ax
call read1sector2
;call newline2
;call numshow2
;call newline2
;mov ah,0
;mov al,[deldirid] ;要删除的目录编号
pop ax ;把调用此函数的参数带进来
mov bl,8
div bl ;假如目录编号为1,1/8,商al=0,余数ah=1
mov ch,0
mov cl,ah ;用余数定位目录编号di占用标记在字节内的位置
inc cl
mov dl,0111_1111b
rol dl,cl ;循环右移余数+1次,把0移到了需要的位置
mov bh,0
mov bl, al ;用商定位目录编号id占用标记所在的字节放在BX
and byte [es:bx],dl ;更新占用标记1bit 相当于置0
mov byte [whdsector+11],1 ;往目录文件控制信息扇区(第1扇区)写目录编号占用标记
mov byte [whdheader+11],0
mov byte [whdcylind+11],0
mov ax,rdataseg ;把先前读进来的数据又写回去(只有1个字节的里面的1 bit有更新)
mov es,ax
call write1sector2
mov byte [dirid],0 ;最后必须把dirid置0,为创建新目录命令mkdir做好准备。
; call read1sector2
; call newline2
; call numshow2
; call newline2
; call newline2
ret
delonedirdata:
;mov ah,0
;mov al,[deldirid] ;要删除的目录编号
mov bl,50 ;目录编号/50,用商找到目录编号所在扇区
div bl ;假如目录编号为1,1/50,商al=0,余数ah=1
push ax ;把定位数据保存下来 ,商al=0,余数ah=1
push ax
push ax
add al, 12 ;al为目录编号所在扇区 12为目录编号大区扇区基础计数
mov byte [hdsector+11],al ;
mov byte [hdheader+11],0
mov byte [hdcylind+11],0
mov ax,rdataseg ;8000开始存放程序,10000开始存放硬盘读进的数据
mov es,ax
call read1sector2
pop ax ;目录编号/50,用余数*10,找到目录所在扇区内的偏移位置
mov al,ah ;ah为余数
mov ah,0
mov bl,10
mul bl
mov bx, ax
mov cx, 10 ;一个目录10B
delonedir: mov byte [es:bx], '?' ;从硬盘上清空目录数据区的数据
inc bx
loop delonedir
writedeldir:
pop ax
add al, 12 ;al为目录编号所在扇区 12为目录编号大区扇区计数
mov byte [whdsector+11],al ;往目录编号所在扇区写目录编号数据
mov byte [whdheader+11],0
mov byte [whdcylind+11],0
mov ax,rdataseg ;把先前读进来的数据又写回去(只有10个字节有更新)
mov es,ax
call write1sector2
pop ax
add al, 12 ;al为目录编号所在扇区 12为目录编号大区扇区计数
mov byte [hdsector+11],al ;从硬盘读出目录编号所在扇区 验证数据
mov byte [hdheader+11],0
mov byte [hdcylind+11],0
;call read1sector2
;call newline2
;call showdata2
;call newline2
ret
posdeldir:
call readdirdata
mov si,0
goposdeldir: ;cmp byte [es:si+1],'?'
cmp si,2000 ;遍历结束标志
je posdeldirover
mov dh,[parentid]
cmp byte [es:si+1], dh ;找出目录区中匹配当前parentid的目录
jne posdeldirnext
call ifdeldestdir
posdeldirnext: add si,10
jmp goposdeldir
posdeldirover:
ret
ifdeldestdir:
mov di,si
mov cx,8
mov bx,0
goifdeldest: mov ah, [delname+bx] ;用户输入的目标目录
mov al, [es:di+2] ;硬盘中目录区的目录
cmp ah, al
jne ifdeldestover
inc di
inc bx
loop goifdeldest
mov dh, [es:si]
mov [deldirid],dh ;找到要删除目录的dirid,先保存起来
ifdeldestover:
ret
showdeldirid:
mov cl, [deldirid]
call numtoascii2
mov [diridmsg+9],al
mov [diridmsg+10],ah
call newline2
mov si, diridmsg
call printstr2
call newline2
ret
makedirdeal: call getdirname ;根据用户命令获取目录文件名dirname,并存入缓冲区
;mov si, dirname
;call printstr2
;call newline2
call newdirid ;遍历目录编号占用标记,确定新目录编号dirid,并更新目录占用标记
call writedir ;把新建目录写进目录数据结构
;call showparentid
ret
writedir: mov ah,0
mov al,[dirid] ;目录编号
mov bl,50 ;目录编号/50,用商找到目录编号所在扇区
div bl ;假如目录编号为1,1/50,商al=0,余数ah=1
push ax ;把定位数据保存下来 ,商al=0,余数ah=1
push ax
push ax
add al, 12 ;al为目录编号所在扇区 12为目录编号大区扇区计数
mov byte [hdsector+11],al ;从硬盘读出目录编号所在扇区
mov byte [hdheader+11],0
mov byte [hdcylind+11],0
mov ax,rdataseg ;8000开始存放程序,10000开始存放硬盘读进的数据
mov es,ax
call read1sector2
pop ax ;目录编号/50,用余数*10,找到目录所在扇区内的偏移位置
mov al,ah ;ah为余数
mov ah,0
mov bl,10
mul bl
mov bx, ax
mov dh, [dirid]
mov [es:bx], dh
inc bx
mov dh, [parentid]
mov [es:bx],dh
inc bx
mov si,0
writedirname:
mov dl,[dirname+si] ;写目录名
cmp dl,'?'
je overwritedir
mov [es:bx],dl
inc si
inc bx
jmp writedirname
overwritedir:
pop ax
add al, 12 ;al为目录编号所在扇区 12为目录编号大区扇区计数
mov byte [whdsector+11],al ;往目录编号所在扇区写目录编号数据
mov byte [whdheader+11],0
mov byte [whdcylind+11],0
mov ax,rdataseg ;把先前读进来的数据又写回去(只有10个字节有更新)
mov es,ax
call write1sector2
pop ax
add al, 12 ;al为目录编号所在扇区 12为目录编号大区扇区计数
mov byte [hdsector+11],al ;从硬盘读出目录编号所在扇区 验证数据
mov byte [hdheader+11],0
mov byte [hdcylind+11],0
;call read1sector2
; call newline2
;call showdata2
;call newline2
ret
newdirid:
mov byte [hdsector+11],1 ;从目录文件控制信息扇区(第1扇区)读取目录编号占用标记
mov byte [hdheader+11],0
mov byte [hdcylind+11],0
mov ax,rdataseg ;8000开始存放程序,10000开始存放硬盘读进的数据
mov es,ax
call read1sector2
;call newline2
;call numshow2
;call newline2
nextdirno:
mov ah,0
mov al,[dirid] ;起始目录编号
mov bl,8
div bl ;假如目录编号为1,1/8,商al=0,余数ah=1
push ax ;把定位数据保存下来 ,商al=0,余数ah=1
mov bh,0
mov bl, al ;先用商定位目录编号id占用标记所在的字节放在BX
mov dh,[es:bx]
mov ch,0
mov cl,ah ;再用余数定位目录编号di占用标记在字节内的位置
inc cl ;右移余数+1次
shr dh,cl ;右移1位,0位将移到CF
jnc nozhan
inc byte [dirid] ;占用了则下一个目录编号
pop ax ;同时需要POP出AX,因为在当前dirid已经被占用的情况下,后面会走不到POP命令。
;而POP命令和PUSH命令必须成对使用,因此这里做一个无效执行。就因为少了这行代码引起程序乱飞,导致我耽误了2周时间!
jmp nextdirno ;注意这里有一个死循环,直到找到一个未占用的目录编号为止
nozhan: mov al,[dirid] ;转换成ASCII码显示输出验证
; add al,30h
;mov ah,0eh
; int 10h
;call newline2
;还要将新的目录编号占用标记打成占用
pop ax ;还原定位数据
mov ch,0
mov cl,ah ;再用余数定位目录编号di占用标记在字节内的位置
inc cl
mov dl,1000_0000b
rol dl,cl ;循环右移余数+1次,把1移到了需要的位置
mov bh,0
mov bl, al ;商定位目录编号di占用标记所在的字节
or byte [es:bx],dl ;更新占用标记1bit
mov byte [whdsector+11],1 ;往目录文件控制信息扇区(第1扇区)写目录编号占用标记
mov byte [whdheader+11],0
mov byte [whdcylind+11],0
mov ax,rdataseg ;把先前读进来的数据又写回去(只有1个字节的里面的1 bit有更新)
mov es,ax
call write1sector2
;call read1sector2
;call newline2
;call numshow2
;call newline2
;call newline2
ret
numshow2:
mov si,0 ;验证显示从软盘读取到内存的数据
mov cx,80 ;控制输出的数据长度
numnext2: mov al,[es:si]
add al,30h ;转换成ASCII码输出
mov ah,0eh
int 10h
inc si
loop numnext2
ret
getdirname: mov di,0
mov si, deldircom- mkdircom ; 排除掉 mkdir标准命令的长度
namenextchar:mov al, [inputcom+si+1]
cmp al, '?' ;用户输入文件名的结束位置
je dirnameover
mov [dirname+di],al
inc si
inc di
jmp namenextchar
dirnameover: ret
formatdeal: call controlclear ;目录文件控制信息数据结构区清0
call sectorbusycl ;扇区占用标记区数据清0
call dirstrclear ;目录数据结构区数据清0
call filestrclear ;文件数据结构区数据清0
call newline2
mov si, formatmsg
call printstr2
call newline2
ret
controlclear: mov byte [whdsector+11],1 ;目录文件控制信息数据结构区在第1扇区
mov byte [whdheader+11],0
mov byte [whdcylind+11],0
mov ax,wdataseg ;8000开始存放程序,10000开始存放要写硬盘的数据
mov es,ax
call writereadydata ; 1个扇区在写数据之前,都要在内存es:bx的地方先准备好数据
call write1sector2 ;写1个扇区,全写0
;call numshow2 ;显示扇区数据(原始数据为数字非ASCII码)
;call newline2
ret
sectorbusycl: mov byte [whdsector+11],2 ;目录文件控制信息数据结构区在第2-11扇区
mov byte [whdheader+11],0
mov byte [whdcylind+11],0
mov ax,wdataseg ;8000开始存放程序,10000开始存放要写硬盘的数据
mov es,ax
w10sectors: call writereadydata ;每个扇区在写数据之前,都要在内存es:bx的地方先准备好数据
call write1sector2
inc byte [whdsector+11]
cmp byte [whdsector+11],12
jne w10sectors
;call numshow2 ;显示扇区数据(原始数据为数字非ASCII码)
;call newline2
ret
dirstrclear: mov byte [whdsector+11],12 ;目录数据结构区在第12-15扇区
mov byte [whdheader+11],0
mov byte [whdcylind+11],0
mov ax,wdataseg ;8000开始存放程序,10000开始存放要写硬盘的数据
mov es,ax
f10sectors: call writereadydata? ; 1个扇区在写数据之前,都要在内存es:bx的地方先准备好数据
call write1sector2 ;写1个扇区,全写0
inc byte [whdsector+11]
cmp byte [whdsector+11],16
jne f10sectors
;call showdata2 ;显示扇区数据(原始数据为数字非ASCII码)
;call newline2
ret
filestrclear:
mov byte [whdsector+11],16 ;目录文件控制信息数据结构区在第16-25扇区
mov byte [whdheader+11],0
mov byte [whdcylind+11],0
mov ax,wdataseg ;8000开始存放程序,10000开始存放要写硬盘的数据
mov es,ax
file10sectors: call writereadydata ;每个扇区在写数据之前,都要在内存es:bx的地方先准备好数据
call write1sector2
inc byte [whdsector+11]
cmp byte [whdsector+11],26
jne file10sectors
;call numshow2 ;显示扇区数据(原始数据为数字非ASCII码)
;call newline2
ret
readhdpara: ;读取硬盘的扇区数、磁头数、柱面数等参数
mov di,0
paretry:
MOV AH,08H
MOV DL,80H ; 驱动器号,0表示第一个软盘,是的,软盘。。硬盘C:80H C 硬盘D:81H
INT 13H
JNC paREADOK ; 未出错则跳转到READOK,出错的话则会使EFLAGS寄存器的CF位置1
inc di
MOV AH,0x00
MOV DL,0x80 ; A驱动器
INT 0x13 ; 重置驱动器
cmp di, 5 ; 软盘很脆弱,同一扇区如果重读5次都失败就放弃
jne paretry
mov si, pahderror
call printstr2
call newline2
jmp paexitread
paREADOK: mov si, pahdOK
call printstr2
call newline2
;以下为显示出读出的磁盘参数
and cl, 00111111b
mov [pahdsector+11],cl ;CL的位5-0=扇区数
mov cl, [pahdsector+11]
call numtoascii2
mov [pahdsector+7],al
mov [pahdsector+8],ah
mov [pahdheader+11],dh
mov cl,[pahdheader+11] ;DH=磁头数 DL=驱动器数
call numtoascii2
mov [pahdheader+7],al
mov [pahdheader+8],ah
mov [pahdcylind+11],ch
mov cl,[pahdcylind+11] ;CH=柱面数的低8位 CL的位7-6=柱面数的高2位,
call numtoascii2
mov [pahdcylind+7],al
mov [pahdcylind+8],ah
call pareadinfo ;显示读到的硬盘参数
paexitread: ret
pareadinfo: ;显示当前读到哪个扇区、哪个磁头、哪个柱面
mov si,pahdcylind
call printstr2
mov si,pahdheader
call printstr2
mov si,pahdsector
call printstr2
ret
writereadydata: ;准备好写入硬盘的数据
MOV bx,0
nextrebit:
mov byte [ES:BX],0 ;全部初始化成0
INC bx
cmp bx,512
jne nextrebit
ret
writereadydata?: ;准备好写入硬盘的数据
MOV bx,0
nextrebit?:
mov byte [ES:BX],'?' ;全部初始化成'?'
INC bx
cmp bx,512
jne nextrebit?
ret
writetestdata: ;测试写入硬盘的数据
MOV bx,0
nextbit:
mov byte [ES:BX],'T'
INC bx
cmp bx,512
jne nextbit
mov ax,es
add ax,20h
mov es,ax
mov bx,0
nextbit2 : mov byte [ES:BX],'Y'
inc bx
cmp bx,512
jne nextbit2
ret
showdata2:
mov si,0 ;验证显示从软盘读取到内存的数据
mov cx,80 ;控制输出的数据长度
nextchar2: mov al,[es:si]
mov ah,0eh
int 10h
inc si
loop nextchar2
RET
hdiskwrite:
call write1sector2
MOV AX,ES
ADD AX,0x0020
MOV ES,AX ;一个扇区占512B=200H,刚好能被整除成完整的段,因此只需改变ES值,无需改变BP即可。
inc byte [whdsector+11]
cmp byte [whdsector+11],whdNUMsector+1
jne hdiskwrite ;写完一个扇区
mov byte [whdsector+11],1
inc byte [whdheader+11]
cmp byte [whdheader+11],whdNUMheader+1
jne hdiskwrite ;写完一个磁头
mov byte [whdheader+11],0
inc byte [whdcylind+11]
cmp byte [whdcylind+11],whdNUMcylind+1
jne hdiskwrite ;写完一个柱面
ret
hdiskread:
call read1sector2
MOV AX,ES
ADD AX,0x0020
MOV ES,AX ;一个扇区占512B=200H,刚好能被整除成完整的段,因此只需改变ES值,无需改变BP即可。
inc byte [hdsector+11]
cmp byte [hdsector+11],hdNUMsector+1
jne hdiskread ;读完一个扇区
mov byte [hdsector+11],1
inc byte [hdheader+11]
cmp byte [hdheader+11],hdNUMheader+1
jne hdiskread ;读完一个磁头
mov byte [hdheader+11],0
inc byte [hdcylind+11]
cmp byte [hdcylind+11],hdNUMcylind+1
jne hdiskread ;读完一个柱面
ret
newline2: ;显示回车换行
mov ah,0eh
mov al,0dh
int 10h
mov al,0ah
int 10h
ret
printstr2: ;显示指定的字符串, 以'$'为结束标记
mov al,[si]
cmp al,'$'
je disover2
mov ah,0eh
int 10h
inc si
jmp printstr2
disover2:
ret
write1sector2: ;读取一个扇区的通用程序。扇区参数由 sector header cylind控制
mov cl, [whdsector+11] ;为了能实时显示读到的物理位置
call numtoascii2
mov [whdsector+7],al
mov [whdsector+8],ah
mov cl,[whdheader+11]
call numtoascii2
mov [whdheader+7],al
mov [whdheader+8],ah
mov cl,[whdcylind+11]
call numtoascii2
mov [whdcylind+7],al
mov [whdcylind+8],ah
MOV CH,[whdcylind+11]
MOV DH,[whdheader+11]
mov cl,[whdsector+11]
;call writeinfo2 ;显示写到的物理位置
mov di,0
wretry2:
MOV AH,03H ; AH=0x03 : AH设置为0x02表示写磁盘
MOV AL,1 ; 要写的扇区数
mov BX, 0 ; ES:BX表示取数据从内存的地址 0x1000*16 + 0 = 0x10000
MOV DL,80H ; 驱动器号,0表示第一个软盘,是的,软盘。。硬盘C:80H C 硬盘D:81H
INT 13H ; 调用BIOS 13号中断,磁盘相关功能
JNC writeOK2 ; 未出错则跳转到writeOK2,出错的话则会使EFLAGS寄存器的CF位置1
inc di
MOV AH,0x00
MOV DL,0x80 ; A驱动器
INT 0x13 ; 重置驱动器
cmp di, 5 ; 软盘很脆弱,同一扇区如果重读5次都失败就放弃
jne wretry2
mov si, whderror
call printstr2
call newline2
jmp exitwrite2
writeOK2:
;mov si, whdOK
;call printstr2
call newline2
exitwrite2:
ret
read1sector2: ;读取一个扇区的通用程序。扇区参数由 sector header cylind控制
mov cl, [hdsector+11] ;为了能实时显示读到的物理位置
call numtoascii2
mov [hdsector+7],al
mov [hdsector+8],ah
mov cl,[hdheader+11]
call numtoascii2
mov [hdheader+7],al
mov [hdheader+8],ah
mov cl,[hdcylind+11]
call numtoascii2
mov [hdcylind+7],al
mov [hdcylind+8],ah
MOV CH,[hdcylind+11] ; 柱面从0开始读
MOV DH,[hdheader+11] ; 磁头从0开始读
mov cl,[hdsector+11] ; 扇区从1开始读
;call readinfo2 ;显示软盘读到的物理位置
mov di,0
retry2:
MOV AH,02H ; AH=0x02 : AH设置为0x02表示读取磁盘
MOV AL,1 ; 要读取的扇区数
mov BX, 0 ; ES:BX表示读到内存的地址 0x0800*16 + 0 = 0x8000
MOV DL,80H ; 驱动器号,0表示第一个软盘,是的,软盘。。硬盘C:80H C 硬盘D:81H
INT 13H ; 调用BIOS 13号中断,磁盘相关功能
JNC READOK2 ; 未出错则跳转到READOK,出错的话则会使EFLAGS寄存器的CF位置1
inc di
MOV AH,0x00
MOV DL,0x80 ; A驱动器
INT 0x13 ; 重置驱动器
cmp di, 5 ; 软盘很脆弱,同一扇区如果重读5次都失败就放弃
jne retry2
mov si, hderror
call printstr2
call newline2
jmp exitread2
READOK2: ;mov si, hdOK
;call printstr2
call newline2
exitread2:
ret
numtoascii2: ;将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
writeinfo2: ;显示当前读到哪个扇区、哪个磁头、哪个柱面
mov si,whdcylind
call printstr2
mov si,whdheader
call printstr2
mov si,whdsector
call printstr2
ret
readinfo2: ;显示当前读到哪个扇区、哪个磁头、哪个柱面
mov si,hdcylind
call printstr2
mov si,hdheader
call printstr2
mov si,hdsector
call printstr2
ret
以后可能会出正式版(v1.0)
更多推荐
所有评论(0)