汇编语言学习笔记
基础知识
机器语言
机器语言是机器指令的集合.机器指令展开来讲就是一台机器可以正确执行的命令.电子计算机的机器指令是一列二进制数字.计算机将之转变为一列高低电平,以使计算机的电子器件受到驱动,进行运算.
- 因为是以逻辑状态0和1直接命令,不需翻译直接执行,速度快
- 不同的处理器使用不同的机器语言,所以其可移植差
- 只有0和1变化组合,不仅难学、难懂、也不容易维护
- 机器指令能被计算机直接识别
汇编语言
由于机器语言在使用上的不便,因此工程师们便从机器语言中找出规则,而以英文字、数字符号来描述机器语言,使成为有意义的语言,这种语言便称为汇编语言.
汇编语言的主体是汇编指令.汇编指令和机器指令的差别在于指令的表示方法上.汇编指令是机器指令便于记忆的书写格式.
- 这些具有特定功能的符号指令称为指令,例如:ADD代表[相加]、MOV代表[移动]
- 这些指令比起机器语言来说可算是较有意义、容易学习多了,而负责翻译的工具则称之为编译器
- 汇编指令需要通过编译器转为机器指令
- 每种CPU都有自己的汇编指令集
汇编语言的组成
汇编语言发展至今,有以下3类指令组成:
- 汇编指令:机器码的助记符,有对应的机器码
- 伪指令:没有对应的机器码,由编译器执行,计算机并不执行
- 其他符号:如+、-、*、/等,由编译器识别,没有对应的机器码
汇编语言的核心是汇编指令,它决定了汇编语言的特性.
指令和数据
指令和数据是应用上的概念.在内存或磁盘上,指令和数据没有任何区别,都是二进制信息.CPU在工作的时候把有的信息看作指令,有的信息看作数据,为同样的信息赋予了不同的意义.
存储器
存储器:用于存储数据并在需要时提供数据
存储器分类
CPU是计算机的核心部件,它控制整个计算机的运作并进行运算.要想让一个CPU工作,就必须向它提供指令和数据.指令和数据在存储器中存放,也就是我们平时所说的
内存.在一台PC机中内存的作用仅次于CPU.离开了内存.性能再好的CPU也无法工作.磁盘不同于内存,磁盘上的数据或程序如果不读到内存中,就无法被CPU使用.
一台PC机中,装有多个存储器芯片,这些存储器芯片从物理连接上看是独立的、不同的器件.从读写属性上看分为两类:随机存储器(RAM)和只读存储器(ROM).随机存储
器可读可写,但必须带电存储,关机后存储的内容丢失:只读存储器只能读取不能写入,关机后其中的内容不丢失.
- 外部存储器:硬盘、光盘等等
- 内部存储器:
- RAM:内存条(随机存储器,断电消失)
- ROM: BIOS芯片(只读存储器,断电不消失)
这些存储器从功能和连接上又可分为以下几类
随机存储器
用于存放供CPU使用的绝大部分程序和数据,主随机存储器一般由两个位置上的RAM组成,装在主板上RAM和插在扩展插槽上的RAM
装有BIOS(Basic Input/Output System,基本输入/输出系统)的ROM
BIOS是由主板和各类接口卡(如显卡、网卡等)厂商提供的软件系统,可以通过它利用该硬件设备进行最基本的输入输出.在主板和某些接口卡上插有存储相应BIOS的ROM.例如,主板上的ROM中存储着主板的BIOS(通常称为系统BIOS);显卡上的ROM中存储着显卡的BIOS;如果网卡上装有ROM,那其中就可以存储网卡的BIOS接口卡上的RAM
某些接口卡需要对大批量输入、输出数据进行暂时存储,在其上装有RAM.最典型的是显示卡上的RAM,一般称为显存.显示卡随时将显存中的数据向显示器上输出.换句话说,我们将需要显示的内容写入显存,就会出现在显示器上
内存地址空间
上述的那些存储器,在物理上是独立的器件,但是在以下两点上相同:
- 都和CPU的总线相连
- CPU对它们进行读或写的时候都通过控制线发出内存读写命令
这也就是说,CPU在操控它们的时候,把它们都当作内存来对待,把它们总的看作一个由若干存储单元组成的逻辑存储器,这个逻辑存储器就是我们所说的内存地址空间
,我们所面对的是内存地址空间.
所有的物理存储器被看作一个由若干存储单元组成的逻辑存储器,每个物理存储器在这个逻辑存储器中占有一个地址段,即一段地址空间.CPU在这段地址空间中读写数据,实际上就是在相对应的物理存储器中读写数据.
不同的计算机系统的内存地址空间分配情况是不同的
最终运行程序的是CPU,我们用汇编语言编程的时候,必须要从CPU的角度考虑问题.对CPU来讲,系统中的所有存储器中的存储单元都处于一个统一的逻辑存储器中,它的容量受CPU寻址能力的限制.这个逻辑存储器即是我们所说的内存地址空间
存储单位
- 计算机中最小的信息单位是bit(比特),也就是一个二进制位
- 计算机中存储数据的最小单位是Byte(B字节)
- 8个bit组成一个B,也就是一字节
- 还可以用以下单位来计量容量:
- 1024B=1KB (B=Byte)
- 1024KB=1MB
- 1024MB=1GB
- 1024GB=1TB
存储单元
- 存储器被划分成了若干个存储单元,一个存储单元可以存储一个字节,也就是8个二进制位,每个存储单元是从0开始顺序编号
- 对于一个有128个存储单元的存储器:
- 容量为128字节
- 编号就是从0-127
- 每个单元由两部分构成:
- 一个是存储单元的地址,一般用十六进制表示
- 一个是存储单元的内容,一般用十六进制表示
CPU
- 一个典型的CPU由运算器、控制器、寄存器等器件组成,这些器件靠内部总线相连
- 内部总线实现CPU内部各个器件之间的联系
- 外部总线实现CPU和主板上其它器件的联系
- 运算器:对数据进行各种算术运算和逻辑运算,即对数据进行加工处理
- 控制器:完成协调和指挥整个计算机系统的操作
- 寄存器:暂存指令、数据和地址
CPU对存储器读写
CPU对存储器的读写是通过三种总线来完成的,地址总线、数据总线与控制总线,如下图:
CPU要想进行数据的读写,必须和外部器件(标准的说法是芯片)进行下面3类
信息的交互
- 存储单元的地址(地址信息)
- 器件的选择,读或写的命令(控制信息)
- 读或写的数据(数据信息)
总线宽度与CPU性能
- 地址总线的宽度决定了CPU的寻址能力
- 数据总线的宽度决定了CPU与其它器件进行数据传送时的一次数据传送量
- 控制总线宽度决定了CPU对系统中其它器件的控制能力
地址总线
一根导线可以传送的稳定状态只有两种,高电平或是低电平,也就是0或1
一个CPU有N根地址线,则可以说这个CPU的地址总线的宽度为N.这样的CPU最多可以寻找2的N次方个内存单元
示例:
8080、8088、8086、80286、80386是16根、20根、20根、24根、32根,则他们的寻址能力分别为6的地址总线宽度分别64KB、1MB、1MB、16MB 、4GB.
2\^16B=2^16/ 1024=64KB
2\^20B-2^20/ 1024/ 1024=1M
2\^24B=2^24/ 1024/ 1024=16M
2\^32B=2\^32/1024/1024/1024=4G
数据总线
CPU与内存或其他器件之间的数据传送是通过数据总线来进行的.数据总线的宽度决定了CPU和外界的数据传送速度.8根数据总线一次可传送一个 8位二进制数据(即一个字节)16根数据总线一次可传送两个字节.
- 一根数据总线只能传输一个0或1,即1bit
- 示例:
8080、8088、8086、80286、80386的数据总线宽度分别为8根、8根、16根、16根、32根.则它们一次可以传送的数据为:1B、1B、2B、2B、4B
控制总线
CPU对外部器件的控制是通过控制总线来进行的.在这里控制总线是个总称,控制总线是一些不同控制线的集合.有多少根控制总线,就意味着CPU提供了对外部器件的多少种控制.所以,控制总线的宽度决定了CPU对外部器件的控制能力.
内存读或写命令是由几根控制线综合发出的,其中有一根称为“读信号输出”的控制线负责由CPU向外传送读信号,CPU向该控制线上输出低电平表示将要读取数据;有一根称为“写信号输出”的控制线则负责传送写信号.
寄存器
寄存器是CPU内的组成部分.有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址
- 存在于CPU
- 用于存储数据
- 速度快
- 数量有限
- 8086CPU含有14个寄存器,所有的寄存器都是16位(bit)的,可以存放两个字节,即存放一个字(两个字节等于一个字)
- 80386CPU含有16个寄存器,所有的寄存器都是32位的,可以存放四个字节,即存放两个字
在CPU中:
- 运算器进行信息处理
- 寄存器进行信息存储
- 控制器控制各种器件进行工作
- 内部总线连接各种器件,在它们之间进行数据的传送
16位CPU所含有的寄存器有(共14个) :
- 4个数据寄存器(AX、BX、CX和DX)
- 2个变址寄存器(SI和DI) 2个指针寄存器(SP和BP)
- 4个段寄存器(ES、CS、SS、DS)
- 1个指令指针寄存器(IP)1个标志寄存器(Flags)
32位CPU所含有的寄存器有(共16个):
- 4个数据寄存器(EAX、EBX、ECX和EDX)
- 2个变址和指针寄存器(ESI和EDD)2个指针寄存器(ESP和EBP)
- 6个段寄存器(ES、CS、SS、DS、FS和GS)
- 1个指令指针寄存器(EIP)1个标志寄存器(EFlags)
数据寄存器
数据寄存器主要用来保存操作数和运算结果等信息,从而节省读取操作数所需占用总线和访问存储器的时间
32位CPU有4个32位通用寄存器:EAX、EBX、ECX和EDX,对低16位数据的取存,不会影响高16位的数据,这些低16位寄存器分别命名为AX、BX、CX和DX,它和先前的CPU中的寄存器相一致.4个16位寄存器又可分割成8个独立的8位寄存器(AX:ah-al、BX:bh-bl、CX:ch-cl、DX:dh-dl)
由于在8086之前的CPU为8位CPU,为了兼容以前的8位程序,在8086CPU中,每一个数据寄存器都可以当做两个单独的寄存器来使用,由此,每一个16位寄存器可以当做2个独立的8位寄存器来使用
每个寄存器都有自己的名称,可独立存取.程序员可利用数据寄存器的这种”可合可分”的特性,灵活地处理字或字节的信息
AX和al通常称为累加器,用累加器进行的操作可能需要更少时间,累加器可用于乘、除、输入/输出等操作,它们的使用频率很高
BX称为基地址寄存器,它可作为存储器指针来使用
CX称为计数寄存器,在循环和字符串操作时,要用它来控制循环次数;在位操作中,当移多位时,要用cl来指明位移的位数
DX称为数据寄存器,在进行乘、除运算时,它可以为默认的操作数参与运算,也可用于存放I/O的端口地址
- AX、BX、CX、DX这4个寄存器通常用来存放一般性的数据,被称为通用寄存器
注意:除了上面4个数据寄存器以外,其他寄存器均不可以分为两个独立的8位寄存器
寄存器 | 说明 | |
---|---|---|
AX | AL | 累加寄存器(Accumulator):可用于乘、除、输入/输出等操作 |
AH | ||
BX | BL | 基地址寄存器(Base):可作为存储器指针来使用 |
BH | ||
CX | CL | 计数寄存器(Count):在循环和字符串操作时,要用它来控制循环次数;在位操作中,当移多位时,要用CL来指明移位的位数 |
CH | ||
DX | DL | 数据寄存器(Data):在进行乘、除运算时,它可作为默认的操作数参与运算,也可用于存放I/O的端口地址 |
DH |
下图表示16位寄存器AX,可以表示成两个8位寄存器,其中AH表示高位的8位寄存器,
AL表示低位的8位寄存器
注意:
- 在16位CPU中,AX、BX、CX和DX不能存放存储单元的地址
- 在32位CPU中,其32位寄存器EAX、EBX、ECX和EDX不仅可传送数据、暂存数据保存算术逻辑运算结果,而且也可作为指针寄存器,所以,这些32位寄存器更具有通用性
变址寄存器
32位CPU有2个32位通用寄存器ESI和EDI,其低16位对应先前CPU中的SI和DI,对低16位数据的存取,不影响高16位的数据
ESI、EDI、SI和DI称为变址寄存器,它们主要用于存放存储单元在段内的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便
变址寄存器不可分割成8位寄存器,作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果
它们可作一般的存储器指针使用,在字符串操作指令的执行过程中,对它们有特定的要求,而且还具有特殊的功能
指针寄存器
32位CPU有2个32位通用寄存器EBP和ESP,其低16位对应先前CPU中的BP和SP,对低16位数据的存取,不影响高16位的数据
EBP、ESP、BP和SP称为指针寄存器,主要用于存放堆栈内存储单元的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便
指针寄存器不可分割成8位寄存器,作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果
它们主要用于访问堆栈内的存储单元,并且规定:
BP为基指针寄存器,用它可直接存取堆栈中的数据
SP为堆栈指针寄存器,用它只可访问栈顶
段寄存器
段寄存器是根据内存分段的管理模式而设置的.内存单元的物理地址由段寄存器的值和一个偏移量组合而成的,这样可用两个较少位数的值组合成一个可访问较大物理空间的内存地址
32位CPU有6个段寄存器,分别如下:
CS:代码段寄存器 ES:附加段寄存器
DS:数据段寄存器 FS:附加段寄存器
SS:堆栈段寄存器 GS:附件段寄存器
在16位CPU系统中,只有4个段寄存器,所以,程序在任何时刻至多有4个正在使用的段可直接访问,在32位微机系统中,它有6个段寄存器,所以在此环境下开发的程序最多可同时访问6个段
32位CPU有两个不同的工作方式:实方式和保护方式.在每种方式下,段寄存器的作用是不同的,有关规定简单描述如下:
实方式:段寄存器CS、DS、ES和SS与先前CPU中的所对应的段寄存器的含义完全一致,内存单元的逻辑地址仍为”段地址:偏移地址”的形式,为访问某内存段内的数据,必须使用该段寄存器和存储单元的偏移地址
保护方式:在此方式下,情况要复杂得多,装入段寄存器的不再是段值,而是称为”选择子”的某个值
8086CPU有4个段寄存器:
- CS:代码段寄存器(Code Segment)
- DS:数据段寄存器(Data Segment)
- SS:栈段寄存器(Stack Segment)
- ES:附加段寄存器(Extra Segment)
8086机中,任意时刻,CPU将CS:IP指向的内容作为即将执行的指令
指令指针寄存器
32位CPU把指令指针扩展到32位,并记作EIP,EIP的低16位与先前CPU中的IP作用相同
指令指针EIP、IP是存放下次将要执行的指令在代码段的偏移地址,在具有预取指令功能的系统中,下次要执行的指令通常已被预取到指令队列中,除非发生转移情况,所以,在理解它们的功能时不考虑存在指令队列的情况
在实方式下,由于每个段的最大范围为64KB,所以,EIP的高16位肯定都为0,此时,相当于只用其低16位的IP来反映程序中的指令的执行次序
标志寄存器
1.运算结果标志位.一共6个,包括:CF进位标志位、PF奇偶标志位、AF辅助进位标志位、ZF零标志位、SF符号标志位、OF溢出标志位
2.状态控制标志位.一共3个,包括:TF追踪标志位、IF中断允许标志位、DF方向标志位
32位标志寄存器增加的4个标志位:
1.I/O特权标志IOPL
IOPL用两位二进制位来表示,也称为I/O特权级字段,该字段指定了要求执行I/O指令的特权级,如果当前的特权级别在数值上小于等于IOPL的值,那么,该I/O指令可执行,否则将发生一个保护异常
2.嵌套任务标志NT
NT用来控制中断返回指令IRET的执行.具体规定如下:
(1) 当NT=0,用堆栈中保存的值恢复EFlags、CS和EIP,执行常规的中断返回操作
(2) 当NT=1,通过任务转换实现中断返回
3.重启动标志RF
RF用来控制是否接受调试故障.规定:RF=0时,表示接受,否则拒绝
4.虚拟8086方式标志VM
如果VM=1,表示处理机处于虚拟的8086方式下的工作状态,否则,处理机处于一般保护方式下的工作状态
CS:IP
程序的简要运行流程
- 程序加载到内存中
- CPU找到程序即将执行指令的位置,并执行游戏程序
而在这个过程中,需要使用到寄存器(CS:IP)寻址来确定即将执行指令位置
物理地址
通常文件中至少有两个段:
- 代码段:存储程序的指令
- 一般可读、不可写、可执行
- 数据段:存储程序中要用到的数据
- 一般可读、可写、可执行
物理地址=基础地址+偏移地址
基础地址=段地址×10H
CPU可以用不同的段地址和偏移地址形成同一个物理地址
修改CS:IP
- CPU是由CS:IP中的内容决定执行命令
修改:
同时修改CS:IP的内容
jmp 段地址:偏移地址
例如: jmp 2AE3:3→从2AE33H处读取指令
jmp 3:0B16 →从00B46H处读取指令
- 修改IP的内容
- jmp 某一合法寄存器
- 例如: jmp bx;
- 指令执行前: bx=0B16H,CS=2000H,IP=0003H
- 指令执行后: bx=0B16H,CS=2000H, IP=0B16H
- jmp bx;在含义上好似:mov IP,bx
字在寄存器的存储
出于对兼容性的考虑,8086CPU可以一次性处理以下两种尺寸的数据:
- 字节:记为byte,一个字节由8个bit组成,可以存在8位寄存器中
- 字:记为word,一个字由两个字节组成,这两个字节分别称为这个字的高位字节和低位字节
用十六进制来表示数据可以直观地看出这个数据是由哪些8位数据构成的.比如20000写成4E20就可以直观地看出,这个数据是由4E和20两个8位数据构成的,如果AX中存放4E20,则AH里是4E,AL里是20.这种表示方法便于许多问题的直观分析.以后我们多用十六进制来表示一个数据
简单汇编指令
汇编指令 | 控制CPU完成的操作 | 用高级语言的语法描述 |
---|---|---|
mov ax,18 | 将18送入寄存器AX | AX=18 |
mov ah,78 | 将78送入寄存器AH | AH=78 |
add ax,8 | 将寄存器AX中的数值加上8 | AX=AX+8 |
mov ax,bx | 将寄存器BX中的数据送入寄存器AX | AX=BX |
add ax,bx | 将AX和BX中的数值相加,结果存在AX中 | AX=AX+BX |
在汇编源程序中,数据不能以字母开头