前些天在看kernel的i386的bootsect.S文件,该文件在1.11版本的时候还是用intel汇编的格式写的,不过我又读了2.4.20的 bootsect.S,里头却是AT&T的格式,不知道这个改动是在哪个版本发生的。由于很久没有接触过汇编,再加上bootsect.S中的内 容涉及很多的底层,例如硬盘的读取,int中断等等;让我花了非常多的时间才搞清楚了bootsect.S的工作原理。
AT&T的汇编和Intel的汇编主要是语法格式的区别,然后是指令的区别。AT&T的汇编指令和Intel的汇编指令大体相同,但是在 一些地方有些微的区别。我们先来看AT&T和Intel的格式差别,这些在网上很容易搜到,就当整理记录吧。
AT&T与Intle的汇编格式区别:
1)AT&T汇编语言中源操作数和目的操作数的位置与Intel的语法正好相反。
原本我没有太在意这个,因为在遇到MOV指令的时候,AT&T的格式反而更符合阅读习惯,但是在遇到subw %bx,%ax这个语句的时候,曾经让我困惑了很久,最后才反应过来,源操作数和目的操作数位置相反了;刚开始很不适应,因为sub这些指令我觉得 Intel的格式更符合习惯(还有其他的cmp等等)。
2)前缀。AT&T中所有立即数前缀$:$ 7,寄存器前要加%:%eax(据说后者是gnu的as的特性,而不是AT&T的)
3)后缀。AT&T中所有的操作码多有后缀:”b” “w” “l”,Intel使用的是”byte ptr”、”word ptr”。如movb,movw,movl。分别进行8位(byte)、16位(word)和32位(long)的数据传送。顺道提一下,如果mov后面 没有加后缀,gnu的as程序将根据操作数中的寄存器补上相应的后缀。
4)间接寻址的表示:Intel的间接寻址用”[]”,而AT&T用”()”。
AT&T: movw 4(%bp), %dx
Intel: mov dx,[bp+4]
你还有可能见到以下两种:
AT&T: _var( , %ebx , 4)
Intel: [_var + ebx * 4]
AT&T: _array(%eax, %ebx, 8 )
Intel: [%eax + %ebx * 8 + _array]
上面的两种其实是一样的,只是前者省略了第一个参数而已。
在清楚上面几个差别清楚之后,只要你能看的懂Intel的汇编,看AT&T的基本也没什么问题了。事实上,我是Intel的和AT&T汇 编对照着看的,尤其是在看AT&T的某个地方被“一叶障目”的时候,再看看Intel的汇编很有可能会恍然大悟。
然后就是指令上的一些差别了。AT&T的汇编指令和Intel的基本一致,但是在以下地方需要注意一下:
1)在AT&T汇编指令中,直接远跳转/调用的指令格式是
lcall/ljmp $SECTION,$OFFSET”
同样,远程返回的指令是“lret $STACK-ADJUST”;而在Intel格式中,相应的指令分别为
call/jmp far SECTION:OFFSET
和
ret far STACK-ADJUST
。其实和mov这些指令的后缀差不多,只是lcall是前缀。
2)一些类型转换指令。转换关系如下(均用符号位扩展):
a. cbw cbtw :%al->%ax
b. cwde cwtl :%ax->%eax
c. cwd cwtd :%ax->%dx : %ax
d. cdq cltd :%eax->%edx : %eax
整个bootsect.S并不难看懂,网上有很多的资料。主要是你可能得了解一些关于硬盘的知识,例如硬盘参数表的存放位置、硬盘的读取顺序等;然后是 Intel提供的中断向量:int xxH ;还有就是你的汇编功底了,只要你对微机原理还有稍许印象,再找一个汇编指令手册就行,通常你的微机原理课本就可以胜任了。