理解汇编语言

从汇编语言中,可以很好地理解CPU的运行,特别是对计算机整体架构的理解。冯诺依曼最早提出了以存储器为核心的现代电子计算机架构。

 

从编程语言的层次上看,汇编语言属于低级语言。计算机只可以识别(译码)二进制,但是为什么呢?因为计算机的元器件只有高低电平之分,而没有高、中高、中、中低和低之分,如果有这些,那也许计算机可以识别5进制的数了,就像控制灯的开关,只有开和关两个状态,现在某些灯可以调整亮度,可以理解调整电流的大小?而对于计算机的元器件,例如晶体二极管,它只有两个状态。计算机识别0和1,其实是识别电平的高和低两个状态,当然计算机也可以存储高低两个状态。计算机可以识别一个二进制的指令,但二进制指令编写和阅读非常不便,假设现在你有一个二进制的编辑器,让你去根据PE或者ELF文件结构,去编写一个可执行文件,显然不太可能,甚至只编写二进制的指令,也是非常繁琐的工作。指令集是有限的,可以使用助记符进行表示,好处是便于记忆和书写,需要的一个工具仅仅是将一个个助记符和数据等组成的一系列指令,翻译成二进制,这个翻译翻译工作交给了汇编器。汇编语言相对二进制的指令是更高级的语言,而现在(2021年),汇编语言是一种低级语言,有了更高级的编程语言出现,同时,汇编语言是和二进制的指令一一对应的,这让汇编语言看起来并不高级,如同一个人换了一个名字,其实他还是原来的他。现在所说的高级语言,例如C++、Java等,一条指令可能对应几十条,甚至上百条的汇编指令,也就对应那么多的二进制指令,完成从高级语言到汇编语言的工具叫做编译器,译理解为翻译,而这个翻译比汇编器的翻译要复杂得多。打开现代的编译器(对于编译型编程语言),它的选项很多,这些选项大部分是关于编译策略,例如平台相关、指令优化相关。随着CPU架构的优化和升级,指令集的扩张,编译器会更加复杂,从这点也可看出,高级语言的高级所在,再想一想汇编器,工作量可远远比不上编译器。

 

通过上面的解释,可能你会发现,汇编语言并没有那么神秘。我们要把编程语言理解为“工具”,用来操作CPU等计算机部件的工具,无论是高级语言还是汇编语言,只是它们适合的场景不同。所以,再碰到需要讨论哪个语言高级和优越时,大可不必参与。你需要做的只是,理解计算机运行原理,掌握工具,然后解决碰到的计算机相关问题。

 

现代计算机的核心是存储器。指令和数据均存放在内存中,记得在计算机组成原理课上,最经典的一个问题是,你怎么知道从内存中取出的是指令还是数据?实际上,如果把指令也看成数据,更容易理解。只是CPU对指令和数据的处理方式不同,CPU需要对指令进行解释,然后去执行指令,而对数据,则取出它的值参与运算。因此,对于CPU需要具备这样一些功能:取指令、解释指令、执行指令、取数据、写数据等,用专业术语分别叫做:取址、译码、执行、访存和写回。用英文表示为IF(Instruction Fetch)、ID(Instruction Decode)、EX(Execute)、MEM(Memory Access)和WB(Write Back)。对这些术语,你当然可以使用取指令、解释指令和写数据来形象生动地记忆和描述,这没什么不好,实际上,掌握专业术语是与其他专业人士的交流语言,但这不是一定的,面对复杂的系统,在语言描述上简单化,是一件提高理解和效率的重要方式。

 

围绕CPU具备的5级流水线,介绍一些汇编语言寄存器的概念。寄存器,顾名思义,用来寄用存放的机器,但它不能叫做机器,它是CPU中具有特殊功能的变量。寄存器数量是有限的,每个寄存器有自己的功能。例如PC寄存器,PC的含义是program counter,程序计数器,它不是来保存指令的数量的,而是存放着下一条要执行指令的地址,下一条表示当CPU执行完当前的指令后,根据PC的值,在地址为PC的内存处,取一个指令,然后将PC指向下一条要执行的指令。理论上,PC始终保存下一条要执行指令的地址。还有一些寄存器,这里先不提。

 

我们直接从一个汇编指令MOV开始。MOV是move的缩写,移动的意思。它可以将一个寄存器的值移动到另一个寄存器的值,称为复制更合适,它不会清楚“源”的内容。MOV指令也可以将立即数或者内存中的值移动到寄存器中,将寄存器的值移动到内存中。因此,它会有两个参数,源和目的。MOV destination, source,如果用高级编程语言来表示,会是destination=source,同样,先不提MOV指令的约束。MOV用来传输数据,这太重要了,例如写回操作,将寄存器中的值写回到内存中,或者取数据,都会用到MOV指令。ADD和SUB是加和减的指令,它们与MOV指令相同的一点是,都有两个操作数。ADD destination, source,表示destination=destination-source,SUB同理。例如,计算两个寄存器RAX和RBX的和,RAX和RBX是通用寄存器,当然两个数从内存中而来,可以像下面这样写:

MOV RAX, [a]

MOV RBX, [b]

ADD RAX, RBX

MOV RAX, [sum]

假设a、b和sum代表3个内存中的地址,中括号[]中的内容是地址,a、b和sum均表示地址,如何知道取多少字节呢?在汇编语言中,有一个规则,源操作数和目的操作数的长度是相同的,实际上,很容易理解,不可能把8字节长度的数据移动到4字节长度的地方,当然把4字节长度的数据移动到8字节长度的地方,也是不合适的。因此,指令MOV RAX, [a]中,RAX的长度是8,那么CPU会取从地址a开始的8个字节,存放到RAX中。上面的指令可以优化下,MOV指令的两个操作数可以分别是寄存器和内存,当然可以啦,因为取指令、取数据和写回数据,均需要在寄存器和内存之间传递数据。优化下上面的指令:

MOV RAX, [a]

ADD RAX, [b]

MOV RAX, [sum]

那么,还可以再次优化吗,例如MOV [sum], [a] ADD [sum], [b],似乎只使用两条指令就可以完成操作了,答案是不可以,为什么MOV的两个操作数不能同时为内存呢?在stackoverflow上面有个解释:

The answer involves a fuller understanding of RAM. Simply stated, RAM can only be in two states, read mode or write mode. If you wish to copy one byte in ram to another location, you must have a temporary storage area outside of RAM as you switch from read to write.

 

It is certainly possible for the architecture to have such a RAM to RAM instruction, but it would be a high level instruction that in microcode would translate to copying of data from RAM to a register then back to RAM. Alternatively, it could be possible to extend the RAM controller to have such a temporary registerjust for this copying of data, but it wouldnt provide much of a benefit for the added complexity of CPU/Hardware interaction.

 

对于内存,只有读和写两个模式,不能同时进行读和写。举个简单的例子,有一条单向路,同一时间,要么从左向右,要么从右向左,CPU在读和写内存时,通过总线这条单向路。那么能不能建立“两条”总线呢,一条读线,一条写线,不能,因为内存同一时间只能读或者写。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值