用AT&T 汇编实现 第一个bootloader
作者 斯人 | 发布于 2014 年 2 月 25 日
BIOS 操作系统

最近在学习LINUX操作系统,虽然一直在使用linux系统,但是 知其然知其所以然,而且也希望自己将来能够

转向LINUX操作系统开发的工作,今年年初就开始学习LINUX实现原理,我现在参照的源码是linux 0.11,教材

是 《LINUX内核的设计艺术》和《Linux内核完全注释》,两本书各有优缺点,我都是两本对照这一起看,当

然,评论这两本书也不是本文的内容,主题是通过AT&T汇编语言实现自己第一个bootloader程序。

这两本书阅读之后,感觉虽然讲的比较细,但是并没有完全掌握书里的内容,也没有完全理解,所以决定 模

仿 linux0.11源码 一步步重新实现Linux操作系统底层,希望能够加深理解。

那第一个当然是bootloader了,前面已经讲过BIOS启动原理,本文就只谈bootloader启动相关的内容。

我们都知道 BIOS通电后会从磁盘设备第一个扇区读取512字节的内容到0x0000:0x07c0处并跳转到这里执行,

而且第512KB的内容必须是0xAA55,这是规定,因为BIOS启动时没有办法动态指定要加载的Bootloader地

址,所以只能强制制约,要想用BIOS启动,那你的启动项就要放到第一个扇区中。

本文的目的很简单,就是要让bios加载自己写的bootloader,并且输出一句“Hello ,My Firset OS”,目的越简

单,越能对BIOS加载流程有更清楚的认识,以免被其他杂项干扰,找不到重点。
AT&T代码如下:

BOOTSEG =0x07c0
.code16
.global _start

_start:
        movw    $BOOTSEG        ,%ax
        movw    %ax             ,%ds
        movw    %ax             ,%es

        movw    $bootMsg        ,%bp    #输出的字符位置
        movw    $bootMsgLen     ,%cx    #输出的字符长度
        movw    $0x1301         ,%ax    #中断功能号
        movw    $0xac           ,%bx    #显示属性 :背景及字体颜色bh:背景颜色,bl:字体颜色
        movb    $0              ,%dl    #显示到列的位置,X
        movb    $0x14           ,%dh    #显示到0x14行,也就是20+1行 ,Y
        int $0x10
call loop
loop:
        jmp loop

bootMsg:
        .string "Hello ,My First OS!"
bootMsgLen=. -bootMsg
.org    510
.word 0xaa55

我们先看下效果:
编译指令:

As -o boot.o boot.s
Objcopy -O binary boot.o Image

上面会生成Image镜像文件,然后把Image镜像文件配置到bochs里,
效果如下图:

最下面有一句话,绿色背景,红色字体,颜色设置也比较简单。
下面来详细描述一下代码流程。
BIOS通电–将磁盘第一个扇区512字节copy到内存0x0000:0x07c0处,并将 CS寄存器设置为0x0000,IP设置为

0x07c0,因为现在CPU处于实模式下,所以 CPU下一条将要执行的指令是CS:IP 既 0x0000:0x07c0,这样就能

执行到我们写的bootloader了。

读取磁盘的状态如图:

_start segment前三句是设置ds(源寄存器地址),es (目的寄存器),通过
Movw $BOOTSEG,%ax 传给%ax寄存器,并通过%ax寄存器传给%ds和%es,当然
这段代码也直接可以 用movw %cs,%ax替代,因为%cs指向代码段地址也就是0x7c00处。

下面的指令就是 调用BIOS的中断功能号输出msg:

movw    $bootMsg        ,%bp    #输出的字符位置
        movw    $bootMsgLen     ,%cx    #输出的字符长度
        movw    $0x1301         ,%ax    #中断功能号
        movw    $0xac           ,%bx    #显示属性 :背景及字体颜色bh:背景颜色,bl:字体颜色
        movb    $0              ,%dl    #显示到列的位置,X
        movb    $0x14           ,%dh    #显示到0x14行,也就是立即数$20行 ,Y
        int $0x10  

Int $0x10是调用中断,
前面设置各寄存器的值
$0x1301 是显示器输出功能号

可以理解为
Print(%ax,%bx,%cx,%dx,%bp)
能且只能这么理解,因为print是C的标准IO输出,现在我们的bootloader不能支持任何C标准,只是为了便于

理解。

call loop
loop:
        jmp loop

是一个死循环。

.org    510
.word  $0xaa55

.org是一个伪指令,意思是告诉编译器,将org后面的指令放到org指定的偏移地址后面,也可以理解为用0将

内存填充到510地址。

将.word的数据放到510的位置,那上面说了,启动扇区必须是512字节,那510在加上0xaa55这两个字节,是

不是就是512字节了?^.^

是不是很简单?

下一篇将模拟 linux 启动后的第二步,将bootloader自身代码移动到0x9000处继续执行,以便更容易规划内

存。

原文出处:http://www.imsiren.com/archives/917