《操作系统真象还原》第3章
本章内容多为汇编语言和硬件的相关知识,就不一一抄录了。但是在此处本系统和xv6有一点不同,xv6会将MBR和Bootloader合并为bootblock程序(由bootasm.S和bootmain.c编译链接而成),而本系统是分开的。因此记录一下MBR和Bootloader的作用:
“我们的MBR受限于512字节的大小,在那么小的空间中,没法为内核准备好环境,更没法将内核成功加载到内存中并运行。所以我们要在另一个程序中完成初始化环境及内核的任务,这个程序我们称之为loader,即加载器。”
“新款MBR的使命就是负责从硬盘上把loader加载到内存,并将接力棒交给它。”
步骤:
1.直接操作显卡
2.使用硬盘
1.直接操作显卡
修改mbr.S为:
1 SECTION MBR vstart=0x7c00 ;起始地址编译为0x7c00
2 mov ax,cs ; 因为是jmp 0:0x7c00跳转到MBR的,故cs此时为0。ds、es、ss、fs等sreg只能用通用寄存器赋值,本例采用ax赋值
3 mov ds,ax
4 mov es,ax
5 mov ss,ax
6 mov fs,ax
7 mov sp,0x7c00 ; 初始化栈指针
8 mov ax,0xb800 ; 0xb800为文本显示起始区
9 mov gs,ax ; gs = ax 充当段基址的作用
10
11 ;ah = 0x06,al = 0x00 想要调用int 0x06的BIOS提供的中断对应的函数,即向上移动即完成清屏功能
12 ;cx,dx 分别存储左上角与右下角的左边,详情看int 0x06函数调用
13 mov ax,0x600
14 mov bx,0x700
15 mov cx,0
16 mov dx,0x184f
17
18 ;调用BIOS中断,实现清屏
19 int 0x10
20
21 ;新增功能:直接操作显存部分
22 ;预设输出"Hell0er."
23
24 mov byte [gs:0x00],'H' ;低位字节储存ASCII字符,小端储存内存顺序相反。用关键词byte指定操作数所占空间,因为[gs:0x00]和'H'所占空间均为不定的,所以需要自己指定空间大小
25 mov byte [gs:0x01],0xA4 ;背景储存在第二个字节,含字符与背景属性。A表示绿色背景闪烁,4表示前景色为红色
26
27 mov byte [gs:0x02],'e'
28 mov byte [gs:0x03],0xA4
29
30 mov byte [gs:0x04],'l'
31 mov byte [gs:0x05],0xA4
32
33 mov byte [gs:0x06],'l'
34 mov byte [gs:0x07],0xA4
35
36 mov byte [gs:0x08],'0'
37 mov byte [gs:0x09],0xA4
38
39 mov byte [gs:0x0A],'e'
40 mov byte [gs:0x0B],0xA4
41
42 mov byte [gs:0x0C],'r'
43 mov byte [gs:0x0D],0xA4
44
45 mov byte [gs:0x0E],'.'
46 mov byte [gs:0x0F],0xA4
47
48 jmp $ ; 执行死循环
49
50 times 510-($-$$) db 0 ; 将512B的剩余部分填充为0
51 db 0x55,0xaa ; 魔数
可以简单归为三步:初始化、清屏、输出。
保存后还是执行老步骤:
nasm -o mbr.bin mbr.S
dd if=mbr.bin of=hd60M.img bs=512 count=1 conv=notrunc
bin/bochs -f bochsrc.disk
记得再键入c,然后就会出现
没问题,字符闪烁。
在我尝试魔改时出现了有趣的现象,分享一下:
可以尝试将字符前景色和背景色进行修改;然后当cx、dx设置的不为整个屏幕时,只会清屏一部分。
2.使用硬盘
修改mbr.S为:
1 %include "boot.inc"
2 SECTION MBR vstart=0x7c00 ;起始地址编译为0x7c00
3 mov ax,cs ; 因为是jmp 0:0x7c00跳转到MBR的,故cs此时为0。ds、es、ss、fs等sreg只能用通用寄存器赋值,本例采用ax赋值
4 mov ds,ax
5 mov es,ax
6 mov ss,ax
7 mov fs,ax
8 mov sp,0x7c00 ; 初始化栈指针
9 mov ax,0xb800 ; 0xb800为文本显示起始区
10 mov gs,ax ; gs = ax 充当段基址的作用
11
12 ;ah = 0x06,al = 0x00 想要调用int 0x06的BIOS提供的中断对应的函数,即向上移动即完成清屏功能
13 ;cx,dx 分别存储左上角与右下角的左边,详情看int 0x06函数调用
14 mov ax,0x600
15 mov bx,0x700
16 mov cx,0
17 mov dx,0x184f
18
19 ;调用BIOS中断,实现清屏
20 int 0x10
21
22 ;新增功能:直接操作显存部分
23 ;预设输出"Hell0er."
24
25 mov byte [gs:0x00],'H' ;低位字节储存ASCII字符,小端储存内存顺序相反。用关键词byte指定操作数所占空间,因为[gs:0x00]和'H'所占空间均为不定的,所以需要自己指定空>间大小
26 mov byte [gs:0x01],0xA4 ;背景储存在第二个字节,含字符与背景属性。A表示绿色背景闪烁,4表示前景色为红色
27
28 mov byte [gs:0x02],'e'
29 mov byte [gs:0x03],0xA4
30
31 mov byte [gs:0x04],'l'
32 mov byte [gs:0x05],0xA4
33
34 mov byte [gs:0x06],'l'
35 mov byte [gs:0x07],0xA4
36
37 mov byte [gs:0x08],'0'
38 mov byte [gs:0x09],0xA4
39
40 mov byte [gs:0x0A],'e'
41 mov byte [gs:0x0B],0xA4
42
43 mov byte [gs:0x0C],'r'
44 mov byte [gs:0x0D],0xA4
45
46 mov byte [gs:0x0E],'.'
47 mov byte [gs:0x0F],0xA4
48
49 mov eax,LOADER_START_SECTOR ; 起始扇区lba地址
50 mov bx,LOADER_BASE_ADDR ; 写入的地址
51 mov cx,1 ; 待读入的扇区数
52 call rd_disk_m_16 ; 以下读取程序的起始部分(一个扇区)
53
54 jmp LOADER_BASE_ADDR
55
56 ;-----------------------------------
57 ;功能:读取硬盘n个扇区
58 rd_disk_m_16:
59 ;-----------------------------------
60 ; eax=LBA 扇区号
61 ; bx=将数据写入的内存地址
62 ; cx=读入的扇区数
63 mov esi,eax ;备份eax
64 mov di,cx ;备份cx
65 ;读写硬盘:
66 ;第1步:设置要读取的扇区数
67 mov dx,0x1f2
68 mov al,cl
69 out dx,al ;读取的扇区数
70 mov eax,esi ;恢复ax
71
72 ;第2步:将LBA地址存入0x1f3~0x1f6
73 ;LBA地址7~0位写入端口0x1f3
74 mov dx,0x1f3
75 out dx,al
76
77 ;LBA地址15~8位写入端口0x1f4
78 mov cl,8
79 shr eax,cl
80 mov dx,0x1f4
81 out dx,al
82
83 ;LBA地址23~16位写入端口0x1f5
84 shr eax,cl
85 mov dx,0x1f5
86 out dx,al
87
88 shr eax,cl
89 and al,0x0f ;lba第24~27位
90 or al,0xe0 ;设置7~4位为1110,表示lba模式
91 mov dx,0x1f6
92 out dx,al
93
94 ;第3步:向0x1f7端口写入读命令,0x20
95 mov dx,0x1f7
96 mov al,0x20
97 out dx,al
98
99 ;第4步:检测硬盘状态
100 .not_ready:
101 ;同一端口,写时表示写入命令字,读时表示读入硬盘状态
102 nop
103 in al,dx
104 and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙
105 cmp al,0x08
106 jnz .not_ready ;若未准备好,继续等
107
108 ;第5步:从0x1f0端口读数据
109 mov ax,di
110 mov dx,256
111 mul dx
112 mov cx,ax ;di为要读取的扇区数
113 mov dx,0x1f0
114 .go_on_read:
115 in ax,dx
116 mov [bx],ax
117 add bx,2
118 loop .go_on_read
119 ret
120
121 times 510-($-$$) db 0 ; 将512B的剩余部分填充为0
122 db 0x55,0xaa ; 魔数
如果学过《微机原理与接口技术》这门课,那么对于各种读写端口的操作应该是比较熟悉的。
主要的是第58行起的rd_disk_m_16函数。正如我们老师所说,我倒觉得不必记住端口号和命令字每一位的含义,因为一定会忘记的,只要理解代码,能够根据data sheet把程序编写出来就好。
考虑到引用了boot.inc头文件,因此肯定要创建它。
先建个文件夹吧:
mkdir include
然后在该文件夹中编写boot.inc:
;----------------- loader 和 kernel -----------------
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
回到bochs下,继续老步骤:
nasm -I include/ -o mbr.bin mbr.S
dd if=/home/zbb/bochs/mbr.bin of=/home/zbb/bochs/hd60M.img bs=512 count=1 conv=notrunc
最后再编写一个简单的loader.S来试试能否跳转读取:
1 %include "boot.inc"
2 SECTION LOADER vstart=LOADER_BASE_ADDR
3 mov byte [gs:0x00],'H'
4 mov byte [gs:0x01],0xA4
5
6 mov byte [gs:0x02],'E'
7 mov byte [gs:0x03],0xA4
8
9 mov byte [gs:0x04],'L'
10 mov byte [gs:0x05],0xA4
11
12 mov byte [gs:0x06],'L'
13 mov byte [gs:0x07],0xA4
14
15 mov byte [gs:0x08],'O'
16 mov byte [gs:0x09],0xA4
17
18 mov byte [gs:0x0A],' '
19 mov byte [gs:0x0B],0xA4
20
21 mov byte [gs:0x0C],'L'
22 mov byte [gs:0x0D],0xA4
23
24 mov byte [gs:0x0E],'O'
25 mov byte [gs:0x0F],0xA4
26
27 mov byte [gs:0x10],'A'
28 mov byte [gs:0x11],0xA4
29
30 mov byte [gs:0x12],'D'
31 mov byte [gs:0x13],0xA4
32
33 mov byte [gs:0x14],'E'
34 mov byte [gs:0x15],0xA4
35
36 mov byte [gs:0x16],'R'
37 mov byte [gs:0x17],0xA4
38
39 mov byte [gs:0x18],'.'
40 mov byte [gs:0x19],0xA4
41
42 jmp $
下一步,不用说你应该也知道了:
nasm -I include/ -o loader.bin loader.S
dd if=/home/zbb/bochs/loader.bin of=/home/zbb/bochs/hd60M.img bs=512 count=1 seek=2 conv=notrunc
此时,bochs文件夹下所含文件如下:
当然还要运行一下bochs:
bin/bochs -f bochsrc.disk
成功闪烁:
理论上,如果你能够成功完成第一步实验,那么这第二步实验也是能顺利实现的。如果出现了问题,那只有三种可能:
- .S的程序编写有误
- 控制命令有误(如没有修改路径等)
- 或者,当它显示booting时,你需要对你的机器有些耐心。。。
ok,本章结束,开心!
参考博客:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库