欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

汇编语言导论Introduction To Assembly

程序员文章站 2024-03-23 22:14:40
...

语言

高级语言:

  • 非常具有人类可读性
  • 一行可以做很多事
  • 可快速编写以及调试
  • 非常适合企业软件

汇编语言:

  • 对CPU的要求很少
  • 大量控制
  • 执行速度非常快
  • 只需要很少的运行时支持
  • 非常适合嵌入式(embedded)/微型(tiny)系统

关于汇编的关键知识:

  • 指令易于编写和理解
  • 你需要很多个指令
  • 一些东西你必须自己动手去编写
    • 循环,基于块的流控制
  • 变量与高级语言的有所不同

变量

在高级语言中,当你看到 int a;

这意味着:分配空间给a,并允许我用a去引用这个空间。

这个空间可能是栈或者堆。

这是汇编语言通常无法提供的高级抽象。

关于我们如何存储字符串,数组,链表:

我们在寄存器中存储一个指向一个地址的指针,并使用它作为引用内存的基础。

寄存器(Registers)

每个CPU有一个小数量的(通常是8-16个)硬件“变量”。在机器内部,这些都有编号。在汇编中,我们将经常看到它们以相同的方式编号:例如R0 - R15。

在x86/x64中,你将会看到一些带有名字(rax,rbx,rcx)和数字(r8-r15)的寄存器。

你可以在你的程序中利用这些寄存器当做你的变量。可将其想象成你的C语言程序有一个全局(global)的变量。

int r0,r1,r2,r3,r4,r5,r6,r7;

指令格式

汇编看起来与基于c的语言有所不同,它没有终止符(terminators)(;),没有大括号({}),没有缩进。通常所有东西在屏幕上都以一条直线的形式写下来。

add R1,R2,R3
mult R7,R2,R6
branchifequal $1234

这些在汇编语言中都十分常见。

寻址(Addressing)

你也许会注意到$1234, 没有了用大括号括起来的块({}),我们就需要一些方法去指明跳转到哪里。在汇编语言中,我们会指明跳转的地址(address)。

$1234是一个绝对地址的例子--只能去到这里。

branchifequal -100
branchifequal 124

这些是相对地址的例子--向后走100个字节(bytes)和向前走124个字节(bytes)。

标签(Labels)

一些组译机assemblers(一个用来将汇编语言转化为机器语言(machine language)的程序)有能力允许你去创建标签。一个标签就是一行代码的名字。这个组译机作为汇编处理的一部分将会为你计算它的距离。

beginningOfLoop:add R1,R2,R3
...
branchifequal beginningOfLoop

函数调用

跳转(jump)就像C或者Java中的goto--它不会处理参数或者返回值。它是一个单程旅行。

几乎在所有汇编语言中函数都可以被调用。

call someFunction
someFunction:add R1 R2 R3
return

函数参数与返回值

这里有几种不同的机制用于参数和返回值:

一种机制就是一直使用寄存器。 R0能够被作为第一个参数,R1作为第二个参数等等。(在这种机制中,R0可以被一直作为返回值)

缺点: 一个函数可以调用任意数量的其他函数。如果函数a调用函数b,同时函数b调用函数c,调用函数a的代码如何确定寄存器的状态?那就意味着产生函数调用的代码需要去假设寄存器已经产生了变化。

比如你借给朋友还有1/2的油量剩余的车子,那么他们还给你的时候还剩下了多少呢?

另一种方法就是: 如果我们都同意R15指向栈呢(换句话说,存储在R15内的值是一个指向内存的指针)。一个调用者可以将参数push到栈中。被调用的函数可以干它自己的事,然后将参数pop出来再把返回值push到栈中,然后return。这个调用者加下来就可以pop这个返回值并且知道(因为在这个方法中,这是所有函数都遵守的规则)这个栈已经变成了函数调用之前的样子。汇编语言导论Introduction To Assembly

 

 

缺点: 通常,访问内存是很慢的。我们将参数写入内存,然后(几乎)立即将它们读入寄存器。 在灵活性(堆栈)和速度(寄存器)之间有一个明显的权衡。

一个混合方式: 如果我们这样来定义这个(参数和返回值的)规则: 当调用一个函数时,R0和R1持有最初的两个参数。其他的参数将会被放入栈中,并将被即刻取出。被调用的函数仅可以使用R0或者R1(或者保存/重新存储其他的寄存器)。R0将会包含返回值。

这意味着调用者只需要保存/避免两个寄存器。

利用操作系统进行汇编

如果有一个操作系统正在运作,那么你将遵循C中相同的规则。

在UNIX或者OSX上,你可以调用 mmap 或者 brk。

在Windows上,你可以调用你VirtualAlloc。

这些方法通常返回一个指针。你可以将这个内存按照你自己的方式分割。这是在汇编中的主题曲--*是压倒一切的。

如果没有操作系统正在运行,你来控制整个机器。硬件厂商应该为你提供一个内存映射(memory map)--可用或不可用的一组范围。例如:汇编语言导论Introduction To Assembly

如果是这种情况,您可以在指定的范围内选择任何您想要的地址。

汇编转换为机器语言

所有汇编/机器语言都有着相同的转换算法。组译机通常不是一个复杂的软件。

通常一定量的指令将都使用一个相同的格式。 接下来我们将看到一些MIPS汇编的转换。

汇编语言导论Introduction To Assembly

 

汇编语言导论Introduction To Assembly

寻址模式

在RISC(reduced instruction set computers 简化指令集计算机)处理器中,内存仅能被一个单独的指令集所访问(类似于“load” 和 “store”)。

在CISC(complex instruction set computers复杂指令集计算机)处理器中,内存(通常)可以被任何指令所访问。这样使得编写汇编程序较为容易,但是使得处理器负担增大。

x86寻址模式的例子:

movl 1, 0x123456  (moves 1 to the long word starting at hex 123456)
movl 1,(%rbx)         (moves 1 to the address stored in register rbx)
movl 1,-12(%rbx)   (moves 1 to the address in rbx minus 12)

这些方法的优缺点: 使用RISC的load/store方法,你必须要去从内存中加载一个值到寄存器中,然后修改这个值,接着将他存回内存中。这样就有三个指令。

使用CISC方法,一个指令就可以更换一个在内存中的值。

CPU做的去修改值的工作基本都是相同的。

类似于“给我一瓶汽水”与“去到便利店,得到汽水,把它拿给我”相比。