admin 管理员组

文章数量: 887021

boot

在Ubuntu下要安装bin86软件包,只需要在终端中输入命令 sudo apt-get install bin86 即可。

 

下面是我们的代码:

1 entry start
2 start:
3 mov ax,#0xb800
4 mov ds,ax
5 mov byte[ 0],#0x41
6 mov byte[ 1],#0x1f
7 hlt
8

上面这段就是我们需要的as86汇编代码。在linux下将它保存为boot.s文件。

entry start 这一句正如字面意思所言,我们的程序就从start这里开始。

代码的意义等我们讲完windows版本的汇编代码再在步骤二解释,我们先在linux终端中输入命令把代码汇编成机器指令:

as86 -o boot.o boot.s

ld86 -o boot boot.o

 

这样我们就在linux中得到了我们接下来要写入U盘的文件boot,

我们在前面的导言中说了,现阶段编写的代码是用于让BIOS从U盘中读出,然后放入内存中执行的。

 

这个阶段,CPU并没有发挥它最大的威力。

这个一方面是CPU的内存还处于未管理的状态,还可以由我们自己自由使用,还没有确定堆栈的位置等等。

另一方面,更强大,而且也是windows xp等系统和linux系统共同使用的,CPU的保护模式还有待我们启动。

(你可能已经猜到,启动保护模式这部分内容将会出现在未来的几篇教程中。)

而在保护模式启动之前我们处于所谓的实地址模式,这个时候我们总是先设定数据段段寄存器的地址,

然后我们的地址就可以用一个16位的地址来访问内存开头的1M空间了。

这里的gas版本的汇编程序中,.code16这一句声明就是为了让gas了解到这段程序是为实模式编写的(也即是此时还不能使用保护模式下专有的指令)

 

我们把数据段寄存器设置成0xb800,这样,地址0就会指向PC的CPU中用来显示屏幕的一段内存的开头。

写入这些地址,屏幕上就会输出latin-1字符(也就是我们常说的ascii字符的8位拓展)。

在这个CPU的最初的阶段,一般屏幕有80行,25列,而当我们把数据段寄存器赋予值0xb800,

地址0所指向的字节就代表第一行第一个字符,这里是ascii码中的大写字母A,0x41。

地址1现在指向的字节代表了第一行第一个字符的字体颜色和背景颜色,格式与windows命令行解释器的color命令一样。

具体如下:

颜色设置 颜色属性由两个十六进制数字指定 -- 第一个为背景,第二个则为
前景。 每个数字可以为以下任何值之一:

0 = 黑色 8 = 灰色
1 = 蓝色 9 = 淡蓝色
2 = 绿色 A = 淡绿色
3 = 浅绿色 B = 淡浅绿色
4 = 红色 C = 淡红色
5 = 紫色 D = 淡紫色
6 = 黄色 E = 淡黄色
7 = 白色 F = 亮白色

 

这里我们用0x1f这个值,也就是字符是白色的而背景颜色是蓝色的。

 

按我的电脑的情况举例,我的电脑主板使用的是Award公司出产的BIOS,它在运行载入器程序的前一刻显示的是计算机中已经接入的磁盘的列表,

以及一个框显示了USB控制器等各种设备的状态。而且它的文字是白色的而背景是黑色的(注意,也就是颜色属性是0f)。并且可以看出可以容纳80个文字一行共25行。

 

我们的程序就将在BIOS这最后一个画面的基础上输出显示画面。

 

可以看出在这个时候,段0xb800开头的这段内存有80x25x2字节的内存用于设置屏幕的文字。

也就是内存地址160的字节的值就代表第2行第一个字符。

 

在我们上面的汇编程序中,我们把0x41也就是大写字母A的ascii值写入内存地址0,将颜色属性0x1f写入内存地址1。

运行起来之后,我们就会看到屏幕的左上角是一个蓝色底白色字的大写字母A!

 

最后的hlt指令让CPU停住不动,这样我们就可以静静观察屏幕上的变化了~

 

不到10行的程序就是我们将来完善的操作系统的第一个版本,不赖吧~~

 

不过这个时候我们只是把程序的汇编出来了,还没有写入U盘并运行,下面我们就来完成这一步。

教程第三步我们也是分成了linux版和windows版。

可惜的是,无论是linux还是windows,都没有常见程序可以帮我们把文件写入磁盘,所以我们这里还需要写C程序自己实现这个功能。



 

步骤三:将程序写入U盘并运行(linux版)

想要在linux中把数据写入U盘,首先要知道linux中怎表示U盘。

linux中,U盘的表示方法和硬盘是一致的,它们都是 /dev/sd开头,接着用小写字母做标号。

第一块磁盘就是/dev/sda,第二块磁盘就是/dev/sdb

我的电脑上装了2块硬盘,这时候再插上一个U盘,那就是总共3个磁盘了。

这个时候U盘就表示为/dev/sdc

在Ubuntu中,可以在菜单中找一找,有一项叫“磁盘实用工具”的,可以查看U盘和硬盘设备的设备名。

 

再不确定,可以使用linux中的fdisk -l命令,在某些系统中要访问设备可能需要超级权限。

以Ubuntu为例,需要使用sudo超级权限。

输入sudo fdisk -l /dev/sda可以查看第一块磁盘的信息。(-l代表list,列表)

 

插入U盘之后,分别查看/dev/sdb,/dev/sdc等磁盘,通过磁盘的大小和分区情况可以判断哪个是你的U盘。

一般来说命令得出的U盘的分区情况是混乱的,那么对U盘做fdisk命令通常提示不合理的磁盘分区,

这也可以帮助判断哪个设备是U盘。

这个一定要判断正确了,至少你不能把一个总容量几百G或者1T,2T大小的磁盘设备辨认成U盘吧。(这么大U盘,你哪买的?)

 

下面这个C程序把我们前面在linux下用as86处理出的程序写入设备/dev/sdc(如果你的U盘是设备/dev/sdb或是其他的,那么就需要相应修改程序)

 

代码 1 #include <fcntl.h>
2 #include <stdio.h>
3 int main( int argc, char*argv[]){
4 int dev_desc,file_desc;
5 unsigned char buffer[ 512];
6 file_desc = open( " ./boot ",O_RDONLY);
7 if(file_desc ==- 1){
8 perror( " failed to read file boot ");
9 return- 1;
10 }
11 read(file_desc,buffer, 510);
12 close(file_desc);
13 buffer[ 510] = 0x55;
14 buffer[ 511] = 0xaa;
15 dev_desc = open( " /dev/sdc ",O_RDWR);
16 if(dev_desc ==- 1){
17 perror( " failed to open device /dev/sdc ");
18 return- 1;
19 }
20 write(dev_desc,buffer, 512);
21 close(dev_desc);
22 puts( " done. ");
23 return 0
24 }

 

这里我们只是简单的用linux下的api,声明在fcntl.h里的open函数和close函数读取文件,并把内容写入U盘的起始数据。

这里我们并不是直接写入512个字节,而是写510个字节,并填上55和aa这2个字节,才把510字节的文件内容和2字节的结尾标识一起写入U盘。

 

我们把它保存成linux-write-mbr.c,然后编译:

cc -o linux-write-mbr linux-write-mbr.c

插入U盘,然后用超级权限执行我们的linux-write-mbr程序。(其他linux系统可能需要把sudo改成别的什么。)

这是因为访问设备还是需要比较高权限才能做的:-)

sudo ./linux-write-mbr


本文标签: boot