0x5C0B
add r12,r11
针对这个特定的处理器,这意味着r11 = r11 + r12。因此,我将文本add r12, r11放入文本文件中,并使用汇编程序(一种编译/汇编汇编语言的程序)将其汇编成某种二进制形式。与任何编程语言一样,有时您会创建对象文件,然后将它们链接在一起,有时您可以直接转换为二进制。而且,二进制文件有许多形式,包括ASCII和二进制形式,这是另一个讨论的话题。
现在,在汇编语言中,你能做什么是指令集之外的?它们有什么不同?首先,您可以有宏:
.macro add3 arg1, arg2, arg3
add \arg1,\arg3
add \arg2,\arg3
.endm
.text
add3 r10,r11,r12
宏与内联函数类似,它们不是被调用的函数,而是在行内生成代码。举个例子,就像C语言中的宏一样。因此,您可以使用宏来节省一些打字,也可以使用宏来抽象化您想要反复执行的某些操作,并希望能够在一个地方进行更改,而不必触及每个实例。上面的示例基本上生成以下内容:
add r10,r12
add r11,r12
指令集和汇编语言之间的另一个区别是伪指令。例如,对于这个特定的指令集,没有弹出(pop)指令来从堆栈中弹出元素,但你可以在代码中使用pop指令以节省输入,我将解释为什么。
pop r12
mov @r1+,r12
无论是pop还是mov指令的操作码都为0x413C。
另一个指令集和汇编器之间的差异的例子是切换指令集时可能出现的情况:
ldr r0,=bob
在这个汇编语言中,它的意思是将bob的地址加载到寄存器0中,但是并没有相应的指令可以实现,汇编器会将其转换成一段手动编写的汇编代码:
ldr r0,ZZ123
...
ZZ123: .word bob
实质上,从该指令可到达的地方(不在执行路径中),创建一个单词,链接器将使用bob的地址来填充它。汇编器或链接器同样会对ldr指令进行编码,以生成相对于pc的ldr指令。
这导致了指令集与汇编语言之间的一系列差异。
call fun
机器码并不知道fun是什么或者在哪里能找到它。对于这个指令集而言,它有很多种寻址方式(请注意,我故意避免使用具体的指令集名称,因为这不是本讨论中的重点),汇编程序或链接器会根据情况选择(取决于fun函数相对于该指令的位置)如何编码这条指令。
如果fun函数距离调用指令40字节,汇编程序可能会将该指令编码为pc相对模式,类似于call pc+36(减去4是因为pc在执行时比指令提前了一个周期,并且这是一条4字节的指令)。
或者汇编程序可能不知道fun在哪里或者是什么,留给链接器来处理,在这种情况下,链接器可能会把函数的绝对地址放在类似于call #0xD00D的位置。
对于加载和存储操作也是一样,有些指令集有近程和远程的pc相对寻址方式,有些则采用绝对地址等等。你也可以不用考虑这些,直接说
mov bob,r1
汇编器或链接器,或两者的组合将负责处理剩余部分。
请注意,对于某些指令集,汇编器和链接器可能会在一个程序中同时进行。现在,我们已经习惯了将代码编译成对象并链接对象的模式,但并非所有汇编器都遵循这种模式。
以下是汇编语言可以使用一些快捷方式的例子:
hang: b hang
b .
b 2f
1:
b 1b
b 1f
1:
b 1b
2:
跳转指令:b hang意为跳转至标记hang处,本质上是一个自身跳转,也就是无限循环。在这种汇编语言中,b .表示自身跳转,相当于无限循环,但我不需要发明标签、输入标签名称并进行跳转,这是一个快捷方式。另一个快捷方式是使用数字,例如b 1b表示向后跳转到编号为1的标签处,汇编器会在当前指令之前或之上查找标签号为1的位置。b 1f则不是自身跳转,而是向前跳转1个位置,这对这个汇编器来说是完全有效的代码。它会在代码行下面或后面寻找标签号为1的位置。在这个汇编器中,你可以疯狂地重复使用数字1来进行简单短暂的分支,这样可以省去发明标签名称的麻烦。第二个b 1b将跳转至第二个1,并且是一个自身跳转。
重要的是要理解,创建处理器的公司定义了指令集以及处理器解码和执行的位和字节的机器代码或操作码等术语。通常,该公司会发布一份包含指令的汇编语言文档,即其语法。该公司通常会提供一个汇编程序来编译/组装该汇编语言,使用该语法。但这并不意味着在地球上任何其他选择为该指令集编写汇编器的人都必须使用该语法。这在x86指令集中非常明显。同样,任何伪指令如上面提到的pop或者宏语法或其他快捷方式如b 1b等都必须在不同的汇编器之间得到认可。但往往是没有的,例如ARM就是如此,通用注释符号;在GNU汇编器中无法工作,必须使用@代替。ARM汇编器确实使用;(请注意,我将我的ARM汇编器写成;@以使其具有可移植性)。使用GNU工具时更糟糕,例如您可以将C语言事物(如#define和/ * comment * /)放入汇编器中,并使用C编译器而不是汇编器来编译它,这也可以工作。我倾向于尽可能纯粹,以实现最大的可移植性,但自然您可以选择使用工具提供的任何功能。
指令集合(set)是处理器可以执行的所有指令组成的,而汇编语言(programming language)则使用这些指令来编写程序。
换句话说,指令集只是CPU能理解的一组字节,但你不能使用它们来做任何有用的事情(就像指令是字母一样),而汇编语言是一种让你组合这些指令(或字母)以制作程序(类似于演讲)的语言。
gas
和nasm
)。 (举手,谁还记得foo.L后缀?) - geekosaur计算机(更精确地说是处理器)只能进行计算,即执行算术和逻辑操作。
单个算术或逻辑操作称为指令。
所有指令的集合称为该计算机(更精确地说是处理器)的指令集。
指令集可以在处理器中硬连线实现,也可以使用一种称为微码的技术实现。
计算机只有拥有一种它理解的语言,才能进行编程。二进制代码不是计算机的语言。基于二进制代码的指令集是计算机的语言。
语言其实就是一份书面规范。第一个被设计成纸上规划的语言是机器语言。它在计算机中的实现只能通过硬件(或最新技术的微码)来实现。这种实现被称为指令集。所有其他语言都将在机器语言之上设计。
由于我们在日常生活中大多使用字母表达,所以机器语言很难操作。因此,决定在机器语言之上引入一个助记符语言,称为汇编语言。 汇编语言的实现被命名为汇编器。
[您可能会想知道第一个汇编器是如何编写的。第一个汇编器可能或可能不是用机器语言编写的。为了简单起见,我不再提及引导程序的概念]
总结:
汇编语言通过汇编器转换为指令集。它们是同一枚硬币的两个不同面,之间存在一层抽象或助记码。 机器语言是处理器指令集的“位编码”,而汇编语言是处理器指令集的“符号编码”。
要比较,只需看:
hello.c(C程序)
hello.asm2bin(对象文件表:直接映射助记符和二进制指令)
hello.asm2bin_exe(二进制文件表:链接后的更多映射)
您将在这些文件中看到一行“部分的反汇编..”由于汇编程序所做的是从汇编语言汇编ISA指令(位模式),因此我们在这里首先看到ISA指令,然后对其进行反汇编以获得助记符。
所有文件都在此链接[下载并打开]
https://www.dropbox.com/sh/v2moak4ztvs5vb7/AABRTxl7KQlqU2EkkMkKssqYa?dl=0
在Linux中,您可以使用vim、emacs打开这些文件。