从0x7c00到0x90000

从0x7c00到0x90000

前两行代码就是设置代码段寄存器为0x07c0,接着往下看

1
2
3
4
5
6
7
8
9
10
_start:
mov ax,#0x07c0
mov ds,ax
mov ax,#0x9000
mov es,ax
mov cx,#256
sub si,si
sub di,di
rep
movw
ASM

同前两行代码一样,接下来三行是将es寄存器设置为0x9000,cx寄存器设置为256

关于sub指令很简单,sub a,b表示a=a-b所以下面两行就是将si寄存器和di寄存器清空。现在寄存器的值为:

1
2
3
4
5
ds=0x07c0
es=0x9000
cx=256
si=0
di=0
SHELL

CPU 寄存器如图所示:

有了这些寄存器的值就可以执行下面的指令了:

1
2
rep
movw
ASM

rep代表重负执行下一条指令,movw表示拷贝一个字(两个字节即16位)

movw指令会从数据段:源变址拷贝一个字到附加段:目的变址rep指令会使下一条指令重复执行cx(计数寄存器)次

所以这两条指令就是从0x7c00拷贝512字节到0x90000,也就是把启动区从0x7c00移动到了0x90000

跳转到go

现在我们已经把启动区拷贝到0x90000了,需要跳转到对应的位置继续执行:

1
2
	jmpi	go,0x9000
go: mov ax,cs
ASM

jmpi指令就跳转指令,因为前面的指令都执行过了,所以直接跳转到下一条指令对应的位置(0x9000:go),即0x90000+go

为什么是0x90000

我们知道因为历史原因,启动区会被BIOS拷贝到0x7c00,那么为什么自己还要拷贝到0x90000呢?这是因为THE LINUX/I386 BOOT PROTOCOL规定的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
        |                        |
0A0000 +------------------------+
| Reserved for BIOS | Do not use. Reserved for BIOS EBDA.
09A000 +------------------------+
| Command line |
| Stack/heap | For use by the kernel real-mode code.
098000 +------------------------+
| Kernel setup | The kernel real-mode code.
090200 +------------------------+
| Kernel boot sector | The kernel legacy boot sector.
090000 +------------------------+
| Protected-mode kernel | The bulk of the kernel image.
010000 +------------------------+
| Boot loader | <- Boot sector entry point 0000:7C00
001000 +------------------------+
| Reserved for MBR/BIOS |
000800 +------------------------+
| Typically used by MBR |
000600 +------------------------+
| BIOS use only |
000000 +------------------------+
COQ

总结

这部分代码只做了一件事:把启动区(MBR)从硬盘中先拷贝到0x7c00,再拷贝到0x90000.


从0x7c00到0x90000
https://carl-5535.github.io/2023/12/21/Linux0.11/2)从0x7c00到0x90000/
作者
Carl Chen
发布于
2023年12月21日
许可协议