汇编基础(一)

一.认识汇编语言

要认识汇编语言,还得从编程语言的发展说起,语言有以下几种分类,其发展都是为了让我们更容易去操纵计算机:

  • 机器语言:由0和1组成。
  • 汇编语言:用符号代替了0和1,比机器语言更便于阅读和记忆。
  • 高级语言:Objective-C/Java/C++等,更接近人类语言,方便程序员的使用。

举个例子🌰:
以赋值a=b为例(计算上的操作就是将寄存器BX的内容送入寄存器AX):

可以看到我们写的代码经过编译转换成计算机能够知道的指令,从而让计算机进行相应的操作。汇编语言与机器语言一一对应,每一条机器指令都有与之对应的汇编指令。汇编语言可以通过编译得到机器语言,机器语言可以通过反汇编得到汇编语言。高级语言可以通过编译得到汇编语言\机器语言,但汇编语言\机器语言几乎不可能还原成高级语言。

汇编语言有如下特点:
① 可以直接访问、控制各种硬件设备,比如存储器、CPU等,能最大限度地发挥硬件的功能。但是知识点过多,开发者需要对CPU等硬件结构有所了解,所以不易于编写、调试、维护。
② 能够不受编译器的限制,对生成的二进制代码进行完全的控制。
③ 目标代码简短,占用内存少,执行速度快。可能写的源文件比较大(相对高级语言比较啰嗦),但是编译后的目标文件、可执行文件都小得多。

以c = a + b为例:(对比汇编语言和C++语言)

④ 汇编指令是机器指令的助记符,同机器指令一一对应。每一种CPU都有自己的机器指令集\汇编指令集,所以汇编语言不具备可移植性。

也是因为汇编语言的这些特点,让学习它有了重要的用途:

  • 理解代码的本质,为编写高效代码打下基础。
  • 理解整个计算机系统的最佳起点和最有效途径。
  • 编写驱动程序、操作系统,对性能要求极高的程序或者代码片段,可与高级语言混合使用(内联汇编)
  • 软件安全、病毒分析与防治

iPhone里面用到的是ARM汇编:

  • armv6:iPhone, iPhone2, iPhone3G, 第一代、第二代 iPod Touch
  • armv7:3GS,4, 4S,iPad, iPad2, iPad3, iPad mini, iPod Touch 3G, iPod Touch4
  • armv7s:5, 5C, iPad4
  • arm64:5S 及以后 iPhoneX , iPad Air, iPad mini2以后

二. 计算机硬件结构基础与汇编

要想学好汇编语言,首先要对CPU等硬件结构有一定的了解。最为关键的是需要了解CPU和内存,我们遇到的绝大部分指令都是跟内存、CPU有关的。

App的启动过程如下:

接下来我们具体讲讲数据(指定)的交互,其中有这么几个重要的部件需要了解:
① 总线
② 内存
③ 寄存器

① 总线

每一个CPU芯片都有许多管脚,这些管脚和总线相连,CPU通过总线跟外部器件进行交互
总线有三种类型:地址总线、数据总线和控制总线。

以CPU从内存的3号单元读取数据为例:

地址总线负责传地址(寻址),它的宽度决定了CPU的寻址能力。比如8086的地址总线是20(20根线),寻址能力是1M(2的20次方)(2的10次方就进1个单位,B → KB → M)。
数据总线负责传数据,它的宽度决定了CPU的单次数据传输量,也就是数据传输速度。比如从内存中读取1024字节的数据(1024B),8086(16位,即每次传2字节)至少要读(传输)512次,80386(32位,即每次传4字节)至少要读256次。 → 我们常说的32位和64位CPU指的就是这个
控制总线负责传控制命令,它的宽度决定了CPU对其他器件的控制类型的多少。

例子🌰:8088的数据总线宽度是8(8条线,一条只能传0和1两种讯号,),8086的数据总线宽度是16,分别向内存中写入89D8H(这边4个16进制,需要16位,所以8080只能先传一半)

常见的数据宽度:
位(Bit): 1个位就是1个二进制位0或1。
字节(Byte): 1个字节由8个Bit组成(8位)。内存中的最小单元Byte。
字(Word): 1个字由2个字节组成(16位),这2个字节分别称为高字节和低字节。
进制占字节数
2进制:一个2进制占1位,1/8个字节。
8进制:一个8进制占3位,3/8个字节。
16进制:一个16进制占4位,1/2个字节。

8088和8086传输数据.png
② 内存

内存(Memory)也被称为内存储器,其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。只要计算机在运行中,CPU就会把需要运算的数据调到内存中进行运算,当运算完成后CPU再将结果传送出来,内存的运行也决定了计算机的稳定运行。

计算机中有各类存储器,它们的逻辑连接情况如下:


内存地址范围(空间大小)受CPU地址总线宽度的限制。比如8086地址总线宽度是20,可以定位2的20次方不同的内存单元,所以内存地址范围为0x00000~0xFFFFF(1个16位占4),内存空间大小为1M。

各类存储器地址肯定是不一样的,他们又不一定是在一块,所以我们把它想象成一块虚拟的逻辑存储器。如下图:


每段内存地址都有特定的用途:


CPU访问内存单元时,要给出内存单元的地址。8086有20位地址总线,可以传送20位的地址,1M的寻址能力(从首地址到尾地址为1M)。但它又是16位结构的CPU,它内部能够一次性处理、传输、暂时存储的地址为16位。如果将地址从内部简单地发出,那么它只能送出16位的地址,表现出来的寻址能力只有64KB。
所以,8086采用一种在内部用2个16位地址(段地址和偏移地址)合成的方法来生成1个20位的物理地址。8086是用“起始地址(段地址×16(16位就是进一格,后面补0)) + 偏移地址 = 物理地址”的方式给出物理地址。

8086的寻址方式.png

为了开发方便,我们可以采取分段的方法来管理内存,比如:
地址10000H - 100FFH的内存单元组成一个段,该段的起始地址为10000H,段地址为1000H,大小为100H。地址10000H - 1007FH、10080H - 100FFH的内存单元组成2个段,它们的起始地址为:10000H和10080H,段地址为1000H和1008H,大小都为80H。
偏移地址为16位,变化范围0-FFFFH,仅用偏移地址来寻址最多可寻64KB个内存单元(寻址能力为64KB,所以一个段的长度最大为64KB)。比如给定段地址1000H,用偏移地址寻址,CPU的寻址范围为:10000H - 1FFFFH。

③ 寄存器

总线和内存算是CPU外面的东西,接下来我们要说最重要的部分——CPU。
CPU主要是解释计算机指令以及处理计算机软件中的数据。它主要由三大部件构成(其内部部件之间由总线相连):


CPU组成.png

对程序员来说,CPU中最主要部件是寄存器,可以通过改变寄存器的内容来实现对CPU的控制。
不同的CPU,寄存器的个数、结构是不相同的。比如:8086是16位结构的CPU,有14个16位的寄存器(每个寄存器可以存放2个字节)。


8086CPU内部的寄存器.png

以通用寄存器为例
AX、BX、CX、DX这4个寄存器通常用来存放一般性的数据,称为通用寄存器(有时也有特定用途)。通常,CPU会先将内存中的数据存储到通用寄存器中,然后再对通用寄存器中的数据进行运算。

假设内存中有块红色内存空间的值是3,现在想把它的值加1,并将结果存储到蓝色内存空间。会有三个步骤:
① CPU首先会将红色内存空间的值放到AX寄存器中:mov ax,红色内存空间
② 然后让AX寄存器与1相加:add ax,1
③ 最后将值赋值给内存空间:mov 蓝色内存空间,ax

像AX、BX、CX、DX这4个寄存器都是16位的,意味着它们能存16个0或1,可以存两个字节(byte),一个字(word)。上一代8086的寄存器都是8位的,为了保证兼容, AX、BX、CX、DX都可分为2个独立的8位寄存器来使用。

在汇编的数据存储中,有2个比较常用的单位:
字节(byte):1个字节由8bit组成,可以存储在8位寄存器中。
字(word):1个字由2个字节组成,这2个字节分别称为字的高字节和低字节。
比如上图中,数据20000(4E20H,0100111000100000B),高字节的值是78,低字节的值是32。1个字可以存在1个16位寄存器中,这个字的高字节、低字节分别存储在这个寄存器的高8位寄存器(AH)、低8位寄存器(AL)中。

已知,8086在访问内存时要由相关部件提供内存单元的段地址和偏移地址,送入地址加法器合成物理地址。那是什么部件提供段地址?

段地址在8086的段寄存器中存放,8086有4个段寄存器:CS、DS、SS、ES,当CPU需要访问内存时由这4个段寄存器提供内存单元的段地址。
CS (Code Segment):代码段寄存器
DS (Data Segment):数据段寄存器
SS (Stack Segment):堆栈段寄存器
ES (Extra Segment):附加段寄存器

CS(代码段寄存器)

CS为代码段寄存器,IP为指令指针寄存器,它们指示了CPU当前要读取指令的地址。任意时刻,8086CPU都会将CS:IP指向的指令作为下一条需要取出执行的指令。

CS寄存器.png

步骤如下:
① CS、IP中的内容送入地址加法器,生成物理地址20000H。
② 地址加法器将物理地址送入输入输出控制电路。
③ 输入输出控制电路将物理地址20000H送上地址总线。
④ 从内存20000H单元开始存放的机器指令(B8 23 01)通过数据总线被送入CPU。(为什么是3个呢?应该是这三个组成一个完整的机器指令,有的是两个组成的)
⑤ 输入输出控制电路将指令(B8 23 01)送入指令缓冲器。
读取一条指令后,IP中的值自动增加,以使CPU可以读取下一条指令。因当前读入的指令(B8 23 01)为三个字节,所以IP中的值加3。此时,CS:IP 指向内存单元2000:0003。
⑥ 执行控制器执行指令(B8 23 01),即mov ax 0123H。
⑦ 指令被执行后AX中的内容为0123H。
此时,CPU将从内存单元2000:0003处读取指令。
⑧ CPU从内存20003H处读取指令(BB 03 30)入指令缓存器,IP中的值加3。
后面指令以此类推。。。

在8086CPU启动或复位后,CS:IP 被设置为FFFFH:0000H,所以FFFF0H单元中的指令是开机后的第一条指令。

CPU从何处执行指令是由CS、IP中的内容决定的,我们可以通过改变CS、IP的内容来控制CPU执行目标指令。

jmp指令(跳转)

8086提供了一个mov指令(传送指令),可以用来修改大部分寄存器的值,比如mov ax,10、mov bx,20、mov cx,30、mov dx,40。但是,mov指令不能用于设置CS、IP的值,8086没有提供这样的功能。
8086提供了另外的指令来修改CS、IP的值,这些指令统称为转移指令,最简单的是jmp指令。

jmp 段地址:偏移地址 ➡️ 用指令中给出的段地址修改CS,偏移地址修改IP
jmp 直接值➡️用直接值修改IP
jmp 某一合法寄存器➡️用寄存器中的值修改IP

例子🌰:
jmp 2AE3:3,执行后CS = 2AE3H,IP = 0003H,CPU将从2AE33H处读取指令。

DS(地址段寄存器)

CPU要读写一个内存单元时,必须要先给出这个内存单元的地址,在8086中,内存地址由段地址和偏移地址组成,8086中有一个DS段寄存器,通常用来存放要访问数据的段地址。
举个例子🌰:

mov bx,1000H
mov dx,bx
mov al,[0]

上面3条指令的作用将10000H(1000:0)中的内存数据赋值到al寄存器中。mov al,[address]的意思将DS:address中的内存数据赋值到al寄存器中,由于al是8位寄存器,所以是将一个字节的数据赋值给al寄存器(由于8086不支持将数据直接送入段寄存器中,mov ds,1000H是错误的)。

上面的例子中是赋值给al(8位),如果是赋值给ax呢(16位)?那就是字型数据的传递(2个字节,16位)。
在内存中,一个地址放一个字节的数据(8位,两个16进制,如34),传递字型意味着要2个内存单元。那是从低内存开始或者从高内存开始,这就涉及到大小端的概念。

  • 大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中(高低\低高)
  • 小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中(高高\低低)

比如我们常见的x86就是小端模式,ARM小端大端都可以。

大小端.png

举个例子🌰:

mov指令(赋值)

mov 寄存器,数据 // 比如mov ax,8
mov 寄存器,寄存器 // 比如mov ax,bx
mov 寄存器,内存单元 // 比如mov ax,[0]
mov 内存单元,寄存器 // 比如mov [0],ax
mov 段寄存器,寄存器 // 比如mov ds,ax
mov 寄存器,段寄存器 // 比如mov ax,ds
mov 内存单元,内存单元 //这个是错误的

add和sub指令

add 寄存器,数据 // 比如add ax,8
add 寄存器,寄存器 // 比如add ax,bx
add 寄存器,内存单元 // 比如add ax,[0]
add 内存单元,寄存器 // 比如add [0],ax

sub 寄存器,数据 // 比如sub ax,8
sub 寄存器,寄存器 // 比如sub ax,bx
sub 寄存器,内存单元 // 比如sub ax,[0]
sub 内存单元,寄存器 // 比如sub [0],ax

栈(栈段)

栈:是一种具有特殊的访问方式的存储空间(后进先出)
8086会将CS作为代码段的段地址,将CS:IP指向的指令作为下一条需要取出执行的指令。
8086会将DS作为数据段的段地址,mov ax,[address]就是取出DS:address的内存数据放到ax寄存器中。
8086会将SS作为栈段的段地址,任意时刻,SS:SP指向栈顶元素。
8086提供了PUSH(入栈)和POP (出栈)指令来操作栈段的数据
比如push ax是将ax的数据入栈,pop ax是将栈顶的数据送入ax。
栈空时,SS:SP指向栈空间最高地址单元的下一个单元(就是栈的下面,栈外了)。

push.png
pop.png

push和pop指令
在8086中,push、pop操作的数据都是2个字节的,也就是以字为单位的。

push 寄存器 // 将一个寄存器中的数据入栈
pop 寄存器 //用一个寄存器接收出栈的数据
push 段寄存器 // 将一个段寄存器中的数据入栈
pop 段寄存器 //用一个段寄存器接收出栈的数据
push 内存单元 // 将一个内存单元处的字(也就是开始的两个内存单元)入栈
pop 内存单元 //用一个内存单元接收出栈的数据

举个例子🌰:

mov ax,1000H
mov dx,ax //dx = 1000H
push [0] //将1000:0处的字压入栈中
pop [2] //出栈的数据送入1000:2处

段的总结

我们可以用一个段存放数据,将它定义为“数据段”
我们可以用一个段存放代码,将它定义为“代码段”
我们可以用一个段当作栈,将它定义为“栈段”

对于数据段,将它的段地址放在DS中,用mov、add、sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当做数据来访问;
对于代码端,将它的段地址放在CS中,将段中第一条指令的偏移地址放在IP中,这样CPU就将执行我们定义的代码段中的指令;
对于栈段,将它的段地址放在SS中,将栈顶单元的偏移地址放在SP中,这样CPU在需要进行栈操作的时候,比如执行push、pop指令等,就将我们定义的栈段当成栈空间来用。

三.完整的汇编程序

完整的汇编程序包含汇编指令和伪指令。
汇编指令如mov、add、sub等,有对应的机器指令,可以被编译为机器指令,最终被CPU执行。伪指令如assume、 segment、ends、end等,没有对应的机器指令,由编译器解析,最终不被CPU执行。

// 声明一下code段是cs段、代码段
assume cs:code
// segment和ends的作用是定义一个段,code是我们取的段名
code segment
    mov ax, 1122h
    mov bx, 3344h
    add ax, bx     
    
    // 正常退出程序
    mov ax, 4c00h
    int 21h    
code ends 

// 编译器遇到end时,就结束对源程序的编译
end 

使用汇编语言编写一个完整的程序,步骤大致如下:
编写源代码 → 编译、链接 → 调试、运行

源程序->可执行文件.png

在完整的汇编程序中,我们会看到常见的几种语法。

  • 中断int
  • 循环loop
  • call和ret指令

中断int
可以通过指令int n产生中断,n是中断码,内存中有一张中断向量表,用来存放中断码对应中断处理程序的入口地址。
CPU在接收到中断信号后,暂停当前正在执行的程序,跳转到中断码对应的中断向量表地址处,去执行中断处理程序。

常见中断:

  • int 10h用于执行BIOS中断
  • int 3是“断点中断”,用于调试程序
  • int 21h用于执行DOS系统功能调用,AH寄存器存储功能号

循环loop
loop指令和cx配合使用,用于循环执行重复的操作,类似于高级语言中的for、while循环。
loop指令的执行流程,让cx的值减一,判断cx的值。

       mov ax, 2h
       mov cx, 5    // 5为循环次数
s:     add ax, ax   // s为标号
       loop  s      // 循环执行标号的程序,即 add ax, ax 

call和ret指令
call是将下一条指令的偏移地址入栈后,转到标号处执行指令。

call 标号

ret是将栈顶的值出栈,赋值给ip。

四.栈帧

栈帧就是一个函数执行的环境,包括:参数、局部变量、返回地址等。
每个函数都有自己对应的栈帧,用来保存(或者说保护)自己的数据。

栈帧的介绍.png
栈帧的详情1.png
栈帧的详情2.png

以下是关于函数执行过程,栈帧的变化(对应的图片如下):

  1. push 参数
  2. push 函数的返回地址
  3. push bp (保留bp之前的值,方便以后恢复)
  4. mov bp, sp (保留sp之前的值,方便以后恢复)
  5. sub sp,空间大小 (分配空间给局部变量)
  6. 保护可能要用到的寄存器
  7. 使用CC(int 3)填充局部变量的空间
  8. --------执行业务逻辑--------
  9. 恢复寄存器之前的值
  10. mov sp, bp (恢复sp之前的值)
  11. pop bp (恢复bp之前的值)
  12. ret (将函数的返回地址出栈,执行下一条指令)
  13. 恢复栈平衡 (add sp,参数所占的空间)
栈帧.jpeg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • 语言的发展 机器语言: 由0和1组成的机器指令. 如:0101 0001 1101 0110汇编语言(assemb...
    hfzhangzhang阅读 281评论 0 0
  • 汇编语言的发展 机器语言 由0和1组成的机器指令. 加:0100 0000 减:0100 1000 乘:1111 ...
    宵衣旰食阅读 608评论 1 0
  • 机器语言:由0和1组成的机器指令 高级语言:更接近人类的语言如 oc,swift,c... 一条汇编指令和一条机器...
    领悟12138阅读 431评论 0 0
  • 引言 什么是汇编语言?答:汇编语言是计算机语言,通俗来讲就是人类与计算机(CPU)交流的桥梁,计算机不认识人类的语...
    struggle3g阅读 1,923评论 1 6
  • 王爽汇编全书知识点大纲 第一章 基础知识 机器语言 汇编语言的产生 汇编语言的组成 存储器 cpu对存储器的读写 ...
    2c3ba901516f阅读 2,400评论 0 1