首先来看第一条关键汇编指令:rep movw
关于这个movw指令,查了网上很多说法,这里我调查的情况是:在INTEL 80386的数据手册中没有movw指令,只有movsw指令。
按照之前学习的内容,bootsect使用的是intel的as86汇编器编译,as86的mov语法是mov Destination Source;从第47行的mov语法看确实是as86汇编器语法。而movw是GNU as汇编器(AT&T)支持的指令关键字。显然,这里是书中的一个错误。但是,这段程序似乎可以在BOCHS中编译通过,此处是个关于AS86与AT&T汇编器相关知识的一个入口,暂且存疑了。
关于movsw可以参考下面80386手册中的解释:
MOVS是个字符串指令,将ESI指向的字符串元素移动到EDI指向的位置处。movsb/movsw/movsd分别可以对字节、字和双字进行操作。目的段寄存器不能被段覆盖前缀覆盖,源段寄存器可以被覆盖。【段覆盖前缀】。即目的段寄存器只能使用ES,不能使用其他的寄存器代替。源段寄存器是DS,可以被替代为其他的段寄存器。
movs指令和REP前缀一块使用,实现的是内存到内存的块传输。该操作需要初始化ECX、ESI和EDI寄存器。ECX指定了具体的字节、字和双字个数。
第三点是DF标志位,方向标志位,当DF=0时,复制操作的方向是从第一个元素到最后一个元素,步长为+1;当DF=1时,复制操作是反方向进行的,步长为-1。CLD指令会将DF置为0,STD指令将DF置为1。
说明:具体的段覆盖前缀参考下图:
i386有些默认的段寄存器是不能被替换的:
比如上文提到的ES用于目的字符串,SS用于堆栈指令,CS用于指令获取操作等。
此处分割线。
继续向下分析代码:
实地址模式下内存地址0x07C0:0000处的代码已经被成功的复制到内存地址0x9000:0000处。下面再执行后续代码,CPU就直接跳转到0x9000这个段地址处了。jmpi(JMP intersegment)是实地址模式下的段间跳转指令。该指令执行后CS==0x9000,IP==go。因此,后续操作的ds es和ss段皆设置成了0x9000。
这里需要重点说明下此处的堆栈操作,正如上图书中所示,后续牵扯到了出入栈和call调用操作,需要设置堆栈。堆栈是通过SS:SP来确定内存地址的,SS已经被设置为0x9000,那么SP应该设置在哪呢?
答案:根据上篇文章的内存map可知,0x90200开始将会存放大概4个扇区即(4*512字节)的setup可执行代码,加上bootsect的512字节也就是0x90A00,考虑到堆栈是sp指向堆栈顶部,所以SP要指向大于(0x90A00+堆栈大小)地址即可。这里选择了0x9ff00。堆栈大小为0x9ff00-0x90A00=0xF500=61.25K。bootsect在内存中的搬移,以及搬移后设置SS:SP的操作过程就解析到这里,后面一篇文章将继续分享从磁盘2/3/4/5扇区读取setup的过程。