上次我们学习到开机后会修改段寄存器ds为 0x07c0,方便之后利用这个段基址进行内存寻址。
这次我们主要学习以下几行代码:
.equ INITSEG, 0x9000
mov $INITSEG, %ax #将es段寄存器设置为0x900
mov %ax, %es
mov $256, %cx
sub %si, %si
sub %di, %di
rep
movsw
第一行先 定义常量INITSEG 为0x9000。
然后取出INITSEG的值 给到通用寄存器ax。
接着把ax给到了 段寄存器es。这时候es里的值就是0x9000了。
然后把立即数 256 给到了 通用寄存器 cx(一般cx 用作计数器)。
接下来2个 sub 指令,就是把 si 和 di都清零了。
现在ds, es, cx, si, di都有值了。
ds = 0x07c0
es = 0x9000
cx = 256
si = 0
di = 0
关键寄存器就变成了这样。
接下来的指令:
rep movsw
这里rep 是重复的意思,movsw是复制一个字。就是不断地复制一个字。
这里扩展一下:
MOVS 是串指令里的串传送:
一般来说 cx里有值,就意味要有循环了。
循环的次数:就是cx的值256。退出条件就是cx == 0
从哪儿复制到哪儿:从ds:si 复制到es:di,也就是0x7c00 复制到0x90000。
一次复制2个字节,复制256次 正好是复制了512个字节!也就是说将内存地址 0x7c00 处开始往后的 512 字节的数据,复制到 0x90000 处开始的后面 512 字节的地方
也就是这里的第二步:
接下来是一个跳转指令:
ljmp $INITSEG, $go #段间跳转,这里INITSEG指出跳转到的段地址,解释了cs的值为0x9000
go: mov %cs, %ax #将ds,es,ss都设置成移动后代码所在的段处(0x9000)
mov %ax, %ds
作者那里是8086的指令里是jmpi,我看的这个是AT&T指令,这里是ljmp。
反正意思都是段间跳转。这里由于INITSEG是0x9000 所以就是要跳转到0x9000:go的地址。段基址要左移4位,所以最后要跳转到0x90000 + go的内存地址。
这个go: 在这里是一个标签(label),编译成机器码的时候会变成一个值,就是段内偏移地址。
这时候,假设偏移量是0x10,其实就是这一行代码
mov %cs, %ax
所在的内存地址就是 0x90000 + 0x10 = 0x90010 CPU最终就会跳转到0x90010处。
下一次,我们一起看看go里面又发生了哪些”精彩“?