在x86或amd64上使用汇编指令时,程序员可以使用"Intel“(即nasm编译器)或"AT&T”(即gas编译器)汇编语法。"Intel“语法在Windows上更流行,但"AT&T”在UNIX(-like)系统上更流行。
但是Intel和AMD的手册,也就是由芯片创建者创建的手册,都使用了"Intel“语法。
我想知道,设计"AT&T“语法背后的原始想法是什么?从处理器的创建者所使用的表示法中浮动的好处是什么?
发布于 2017-02-15 21:06:29
UNIX很长一段时间是在PDP-11上开发的,PDP-11是DEC的一种16位计算机,它有一个相当简单的指令集。几乎每条指令都有两个操作数,每个操作数可以有以下八种寻址方式中的一种,这里用宏16汇编语言显示:
0n Rn register
1n (Rn) deferred
2n (Rn)+ autoincrement
3n @(Rn)+ autoincrement deferred
4n -(Rn) autodecrement
5n @-(Rn) autodecrement deferred
6n X(Rn) index
7n @X(Rn) index deferred通过巧妙地重用R7上的一些寻址模式,可以对即时地址和直接地址进行编码,程序计数器:
27 #imm immediate
37 @#imm absolute
67 addr relative
77 @addr relative deferred由于UNIX驱动程序使用@和#作为控制字符,因此用$代替#,用*代替@。
PDP11指令字中的第一个操作数指的是源操作数,而第二个操作数指的是目的地。这反映在汇编语言的操作数顺序上,即源,然后是目标。例如,操作码
011273参考说明
mov (R2),R3它将R2指向的单词移动到R3。
此语法适用于8086 CPU及其寻址模式:
mr0 X(bx,si) bx + si indexed
mr1 X(bx,di) bx + di indexed
mr2 X(bp,si) bp + si indexed
mr3 X(bp,di) bp + di indexed
mr4 X(si) si indexed
mr5 X(di) di indexed
mr6 X(bp) bp indexed
mr7 X(bx) bx indexed
3rR R register
0r6 addr direct其中,如果没有索引,则m为0;如果有单字节索引,则m为1;如果有2字节索引,则m为2;如果使用寄存器而不是内存操作数,则m为3。如果存在两个操作数,则另一个操作数始终是一个寄存器,并以r数字编码。否则,r将对操作码的另外三位进行编码。
在这种寻址方案中,立即数是不可能的,所有采用立即数的指令都会在其操作码中对该事实进行编码。中间件拼写为$imm,就像在PDP-11语法中一样。
虽然英特尔的汇编程序一直使用dst, src操作数排序,但没有特别令人信服的理由来适应这种约定,UNIX汇编程序被编写为使用PDP11中已知的src, dst操作数排序。
他们在8087浮点指令的实现中对这种顺序进行了一些不一致,可能是因为Intel给了非交换浮点指令的两个可能的方向不同的助记符,这与AT&T语法使用的操作数顺序不匹配。
PDP11指令jmp (跳转)和jsr (跳转到子例程)跳转到其操作数的地址。因此,jmp foo将跳转到foo,而jmp *foo将跳转到存储在变量foo中的地址,类似于lea在8086中的工作方式。
X86的jmp和call指令的语法被设计成好像这些指令在PDP11上的工作方式一样,这就是为什么jmp foo跳转到foo,jmp *foo跳转到地址foo处的值,即使8086实际上没有延迟寻址。这具有在语法上区分直接跳转和间接跳转的优势和便利,而不需要为每个直接跳转目标使用$前缀,但在逻辑上没有太多意义。
语法已扩展为使用冒号指定段前缀:
seg:addr在引入80386时,该方案采用四部分通用寻址模式来适应其新的SIB寻址模式:
disp(base,index,scale)其中,disp是位移,base是基址寄存器,index是索引寄存器,scale是1、2、4或8,用于将索引寄存器缩放这些数值之一。这与英特尔语法相同:
[disp+base+index*scale]PDP-11的另一个显著特点是,大多数指令都是以字节和字的形式提供的。您使用的是操作码的b或w后缀,这将直接切换操作码的第一位:
010001 movw r0,r1
110001 movb r0,r1这也适用于AT&T语法,因为大多数8086指令实际上也可用于字节模式和字模式。后来,80386和AMD K6引入了32位指令(为long添加了后缀l )和64位指令(为quad添加了后缀q )。
最后但并非最不重要的一点是,最初的约定是在C语言符号前面加上下划线(就像在Windows上仍然做的那样),这样您就可以从寄存器ax中区分名为ax的C函数。当Unix系统实验室开发ELF二进制格式时,他们决定摆脱这种修饰。由于无法区分直接地址和寄存器,因此每个寄存器都添加了%前缀:
mov direct,%eax # move memory at direct to %eax这就是我们如何得到今天的AT&T语法。
发布于 2017-05-29 21:49:40
汇编语言是由汇编语言定义的,汇编语言是分析汇编语言的软件。唯一的“标准”是机器代码,它必须与处理器匹配,但是如果你让100个程序员给他们机器代码标准(没有任何汇编语言提示),你最终会得到1到100种不同的汇编语言。这对于该处理器的所有用例(裸机、操作系统、应用程序工作)都能很好地工作,只要它们构成一个与工具链相适应的完整工具即可。
为了指令集的创建者,机器代码的最佳利益,创建一个描述指令集的文档和一个汇编程序,这是你需要的第一个工具。他们可以把它外包出去,也可以自己做,这两种方式都无关紧要,但是有一个带有语法的汇编程序和一个机器代码文档,它使用汇编程序的语法来连接两者之间的点,这将为任何可能对该处理器感兴趣的人提供一个起点。就像英特尔和8086/88一样。但这并不意味着masm和tasm与intels汇编程序完全兼容。即使每条指令的语法匹配,每条指令的语法只是汇编语言的一部分,还有许多非指令类型的语法,指令,宏语言等。这是从DOS端开始的,有UNIX端,因此AT&T。gnu人在当时是unix端,所以他们使用AT&T语法或派生的语法是非常合理的,因为他们在移植时通常会把汇编语言搞得一团糟。也许有一个例外。
nasm和其他一些类似的工具试图延续masm语法,因为masm是一个封闭源代码的Microsoft工具(就像tasm和Borland C的任何东西一样,如果不是tasm的话)。这些代码现在可能是开源的,但没有必要,从头开始编写代码比尝试移植代码更容易,我假设是用现代编译器构建的,而且nasm已经存在。
“为什么”这个问题就像是问我为什么你选择了今天早上或某一天你选择的那双袜子。你的袜子可能对世界其他地方没有那么大的影响,但这个问题同样无关紧要和/或无法回答。答案部分地回到了要求100个程序员为相同的机器代码定义制作汇编程序的问题上。这些程序员中的一些人可能对汇编语言很有经验,可能会选择在他们以前使用过的语言的镜像中创建一种汇编语言,这意味着他们中的几个人会制作出看起来彼此非常相似的汇编语言。但他们之前使用的一个或多个可能不同,所以会有类似但仍然不同的组。再过30年,问问这100个人中的每一个,为什么他们还在alive...Like,问我为什么你选择在你30年前写的程序中声明一个变量,就像你这么做的方式。
https://stackoverflow.com/questions/42244028
复制相似问题