《操作系统真象还原》第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,本章结束,开心!


参考博客:

posted @   Hell0er  阅读(169)  评论(0编辑  收藏  举报
编辑推荐:
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示