指令集和汇编语言是相同的吗?

31

我想知道指令集汇编语言是不是同一回事?

如果不是,它们有什么区别和关系?

谢谢和问候!


请注意,“汇编语言”通常也被称为“汇编器语言”,后者是由汇编器理解的语言。 </爱挑剔> - nmichaels
@nmichaels:谢谢!通过“</pedantry>”,你指的是什么? - Tim
4
这是一个打断我吹毛求疵的标签,意味着我有点小题大做了。其实只是个无趣的玩笑,请继续阅读下去。 - nmichaels
7个回答

32
我想每个人都给了您相同的答案。指令集是处理器可以执行或理解的所有指令(如数学中的集合)。汇编语言是一种编程语言。
让我试着用一些问题为基础举例说明。我将使用各种处理器的代码来跳跃演示。
指令、操作码、二进制或机器语言,无论您想用哪个术语来表示加载到处理器中以进行解码和执行的位/字节。例如:
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

没有出现pop的原因是寻址模式足够灵活,可以从源寄存器中读取地址并将值放入目标寄存器,并将源寄存器按照一个字增加。在这个指令集的汇编语言中,对应的是:
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编译器而不是汇编器来编译它,这也可以工作。我倾向于尽可能纯粹,以实现最大的可移植性,但自然您可以选择使用工具提供的任何功能。


这个答案给了我人生中最好的“恍然大悟”时刻... :) 谢谢。 - aderchox

12

指令集合(set)是处理器可以执行的所有指令组成的,而汇编语言(programming language)则使用这些指令来编写程序。
换句话说,指令集只是CPU能理解的一组字节,但你不能使用它们来做任何有用的事情(就像指令是字母一样),而汇编语言是一种让你组合这些指令(或字母)以制作程序(类似于演讲)的语言。


@BlackBear:谢谢!那么所有指令集和使用这些指令的编程语言之间有什么区别? - Tim
@Tim:我添加了一些链接,可能会帮助你理解。 - BlackBear
@BlackBear:作为单独的概念,我理解什么是集合,也大致了解编程语言的含义。但就像我之前问的那样,指令集和使用指令的编程语言之间有什么区别呢? - Tim
@Tim:我尽力用我所能的最好方式解释了(我的英语不太好)。 - BlackBear
汇编语言的特点在于你所写的每一行代码都对应着一条机器指令。与编译器不同,汇编器只是将符号代码转换为二进制机器代码。 - Bo Persson
这里缺少一个区分,即对于给定的指令集,可能会有多种汇编语言。x86在多年来已经有过几种不同的汇编语言语法,其中一些是特定于特定操作系统的(例如比较gasnasm)。 (举手,谁还记得foo.L后缀?) - geekosaur

4

计算机(更精确地说是处理器)只能进行计算,即执行算术和逻辑操作。

单个算术或逻辑操作称为指令

所有指令的集合称为该计算机(更精确地说是处理器)的指令集

指令集可以在处理器中硬连线实现,也可以使用一种称为微码的技术实现。

计算机只有拥有一种它理解的语言,才能进行编程。二进制代码不是计算机的语言。基于二进制代码的指令集是计算机的语言。

语言其实就是一份书面规范。第一个被设计成纸上规划的语言是机器语言。它在计算机中的实现只能通过硬件(或最新技术的微码)来实现。这种实现被称为指令集。所有其他语言都将在机器语言之上设计。

由于我们在日常生活中大多使用字母表达,所以机器语言很难操作。因此,决定在机器语言之上引入一个助记符语言,称为汇编语言。 汇编语言的实现被命名为汇编器。

[您可能会想知道第一个汇编器是如何编写的。第一个汇编器可能或可能不是用机器语言编写的。为了简单起见,我不再提及引导程序的概念]

总结:

汇编语言通过汇编器转换为指令集。它们是同一枚硬币的两个不同面,之间存在一层抽象或助记码。 机器语言是处理器指令集的“位编码”,而汇编语言是处理器指令集的“符号编码”。


3
汇编语言会包括指令助记符,但通常还会添加许多其他内容,例如:
  1. 定义数据的方法
  2. 定义名称的方法(例如函数名称)
编辑:指令本身将被编码为二进制以供CPU读取。助记符是指令的名称。例如,在汇编语言中,我可能会写“mov ax, 1”。对应的指令(在x86的情况下)将编码为B8 00000001(十六进制)。
定义数据、宏、函数名称等不是实际的指令。宏(类似于C中的宏等)允许您在汇编过程中定义名称。它可能(通常会)导致生成一些指令,但这些指令与宏定义本身是分开的。就像在C中定义一些数据一样,这通常会导致目标文件中记录某些空间的名称X,但不直接生成任何指令。

谢谢!(1) 您介意为指令和助记符添加一些示例吗?(2) 因此,定义数据、宏和函数不是指令? - Tim
顺便提一下,俄罗斯IT行业有一个很好的术语“автокод”(“autocode”),其本质上是指“汇编语言,没有宏和其他功能,只有简单的命令及其助记符和参数”。我不知道它是否在英语中使用,但似乎TS想到了类似的东西。 - Netch

2
汇编语言不仅仅是指令集的超集,它还是一种生成目标文件、符号、调试信息和链接的方式,即使在此级别上也可以进行一些最小化的结构化编程。(在其他答案/评论的基础上建立)
  • 目标文件布局。例如,代码、数据、只读、调试和动态链接等部分。常见的“org”指令告诉汇编器指令/数据的位置。
  • 预处理。这包括宏(内联扩展、重复)和有时候的结构化编程(结构体布局、为寄存器定义别名)。
  • 数据定义。可以整体包含文件,或者按字节/字为单位定义,例如".byte"、“.word”、“.dw”,具体取决于体系结构。
大多数C编译器都会生成汇编代码,然后将其传递给汇编器创建目标文件。如果你使用带有“-S”标志运行gcc,则会看到大多数上述内容正在被使用。如果您打开了调试(' -g ')并启用了任何动态链接(这些天默认启用),您将看到大量汇编代码不仅仅是指令。

2
一切都采用分层架构,具有“严格(大部分时间)和定义良好的接口”。从硬件开始,一直到处理器,中间有许多层级。我所说的层级是指我们从“物理->设备(电子)->模拟(放大器)->门电路->数字电路->微体系结构->架构(ISA,处理器)”开始。但是,从处理器开始,它有两个部分(大多数嵌入式系统都有):硬件和软件。软件部分称为ISA(指令集架构),其中包含一个受支持处理器的所有指令。这意味着ISA仅绑定于一个处理器(如x86)。“为什么需要ISA?”同样如我之前所说,它是严格和定义良好的接口。处理器不能运行超出ISA范围的任何指令[严格],但想要使用该处理器的任何人都可以使用ISA中的这些命令来完成工作[定义良好的接口]。现在来看汇编语言、C语言、汇编器、编译器...
层次化架构,你知道我们在硬件中使用它(分层架构)来为您实现一个处理器。您可以阅读更多关于为什么使用这种分层架构。它使得逐步解决大问题变得容易。
同样,在这里我们想要什么?我们的目标是什么?
我们希望用户(程序员)能够轻松地使用这个处理器。
现在看看程序员的困难。
程序员能记住处理器中所有以二进制格式表示的指令吗?而且处理器可能会从英特尔转换到IBM(现在不是版本特定)。
因此,我们也有分层架构[不固定]。
1)汇编器-编译器
2)汇编器
汇编器也是一个具有两个接口的层。编译器也是如此。
例如:您在C语言中编写代码。处理器无法理解此代码。它只能理解以ISA中定义的二进制格式编写的内容。但是用ISA中的指令编写(维护|修改)程序很困难。
1) 用户使用C语言编写代码。这段代码可以被C编译器理解,因为用户只能使用C语言中给定的语法。这意味着C编译器在一端为用户提供了一个标准和明确定义的接口。在另一端,它可以直接使用ISA指令或另一个称为“汇编器”的接口。
2) 如果您正在使用汇编器,则编译器将把所有C代码转换成汇编器提供的语法。汇编语言是一个明确定义的接口,任何人都可以使用它来编写汇编语言程序。在另一端,汇编器将其所有语法(助记符|指令,在ISA中不存在)转换为ISA中的二进制代码指令。
这里有一些翻译的例子:
- 在C中= hello.c - 在汇编代码中= hello.s - 在目标代码中= hello.obj(未进行链接:更多信息)
在这个文件中,有一行“机器:Advanced Micro Devices X86-64”,它提供了有关处理器的信息,因此我们使用ISA和汇编器。C程序员不知道这一点,他可以自由地使用C语言编写代码。这就是“明确定义的接口”的好处。
  • 在机器码中 = hello.binary(链接后:更多信息)

要比较,只需看:

  • hello.c(C程序)

  • hello.asm2bin(对象文件表:直接映射助记符和二进制指令)

  • hello.asm2bin_exe(二进制文件表:链接后的更多映射)

    您将在这些文件中看到一行“部分的反汇编..”由于汇编程序所做的是从汇编语言汇编ISA指令(位模式),因此我们在这里首先看到ISA指令,然后对其进行反汇编以获得助记符。

所有文件都在此链接[下载并打开]

https://www.dropbox.com/sh/v2moak4ztvs5vb7/AABRTxl7KQlqU2EkkMkKssqYa?dl=0

在Linux中,您可以使用vim、emacs打开这些文件。
在Windows中,只需使用vim或右键单击文件后选择文本编辑器并使用“打开->从中选择程序...”选项即可。

你写的意思是C编译器在一端为用户提供标准和明确定义的接口。在另一端,它可以直接使用ISA指令或另一个称为“汇编器”的接口。要点:汇编器和编译器执行相同的工作,但根据定义,任何能够直接将助记符代码转换为二进制的软件都将被称为汇编器而不是编译器……你怎么看? - Bubba Yakoza
您的评论是正确的:“任何将助记符代码直接转换为二进制的软件都被称为汇编器”。但我说的是,在C编程中用户不使用那些助记符,他们使用C语法,所以我说C语法(编译器)=助记符(汇编器)=二进制......由于ISA对于一个处理器而言是固定的,因此应该有不同的编译器用于不同的语言才能在同一处理器上运行程序。 - AnuragChauhan

1
当您查看您在问题中链接的汇编语言的维基百科文章时,下面有一个示例,显示汇编语言指令和相应的目标代码。两者都是同一事物的不同表示形式:处理器指令集中的指令。但只有标题为“指令(AT&T语法)”的列包含汇编语言。
希望这能让它更清晰明了。

谢谢!因此,指令集中的指令可以有两种表示方式:目标代码和汇编语言?我是否正确理解指令集和汇编语言之间的关系是后者只是前者的一种表示形式? - Tim
@Tim:你的意思是后者只是第一个的一种表示形式,对吗?更准确地说,我会说特定的汇编语言包含处理器指令的表示形式,但它通常还包含一些有用的东西,如果你要编写真正的汇编程序(请参见Jerry Coffin的答案)。如果你想了解更多,请尝试自己编写一个汇编语言程序,然后你可以回答自己大部分的问题。 - Doc Brown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接