当前位置: 首页 > news >正文

汇编指令入门级整理

文章目录

  • 前言
  • 什么是汇编语言
    • 汇编语言产生的原因
    • 汇编与二进制的关系
  • 寄存器
    • 寄存器作用
    • 存取速度比较
    • 寄存器分类
    • 常用寄存器用途
    • 寄存器EAX、AX、AH、AL的关系
  • 汇编语言指令
    • 数据传送指令
    • 算术运算指令
    • 逻辑运算指令
    • 循环控制指令
    • 转移指令
  • linux 和 windows 下汇编的区别
  • 总结

前言

我们大都是被高级语言惯坏了的一代,源源不断的新特性正在逐步添加到各类高级语言之中,汇编作为最接近机器指令的低级语言,已经很少被直接拿来写程序了,不过我还真的遇到了一个,那是之前的一个同事,因为在写代码时遇到了成员函数权限及可见性的问题,导致他无法正确调用想执行的函数,结果他就开始在 C++ 代码里嵌入汇编了,绕过了种种限制终于如愿以偿,但是读代码的我们傻眼了…

因为项目是跨平台的,代码推送的 Linux 上编译的时候他才发现,汇编代码的语法在 Linux 和 Windows 上居然是不一样的,结果他又用一个判断平台的宏定义“完美”的解决了,最终这些代码肯定是重写了啊,因为可读性太差了,最近在学习左值、右值、左引用和右引用的时候,总是有人用程序编译生成的中间汇编代码来解释问题,看得我迷迷糊糊,所以决定熟悉一下简单的汇编指令,边学习边记录,方便今后忘记了可以直接拿来复习。

什么是汇编语言

汇编语言是最接近机器语言的编程语言,引用百科中的一段话解释为:

汇编语言(assembly language)是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符代替机器指令的操作码,用地址符号或标号代替指令或操作数的地址。汇编语言又被称为第二代计算机语言。

汇编语言产生的原因

对于绝大多数人来说,二进制程序是不可读的,当然有能人可以读,比如第一代程序员,但这类人快灭绝了,直接看二进制不容易看出来究竟做了什么事情,比如最简单的加法指令二进制表示为 00000011,如果它混在一大串01字符串中就很难把它找出来,所以汇编语言主要就是为了解决二进制编码的可读性问题。

汇编与二进制的关系

换句话来说,汇编语言就是把给机器看的二进制编码翻译成人话,汇编指令是机器指令的助记符,与机器指令是一一对应的关系,是一种便于阅读和记忆的书写格式。有效地解决了机器指令编写程序难度大的问题,并且使用编译器,可以很方便的把汇编程序转译成机器指令程序,比如之前提到的 00000011 加法指令,对应的汇编指令是 ADD,在调用汇编器时就会把 ADD 翻译成 00000011

寄存器

说到汇编指令不得不提到寄存器,寄存器本身是用来存数据的,因为 CPU 本身只负责逻辑运算,数据需要单独储存在其他的地方,但是对于不熟悉寄存器的人来说会有疑惑,数据不是存在硬盘上吗?或者说数据不是存在内存中吗?这些想法都没错,那么寄存器是用来做什么的呢?

寄存器作用

其实硬盘、内存都是用来存储数据的,但是 CPU 的运算速度远高于内存的读写速度,更不用说从硬盘上取数据了,所以为了避免被拖慢速度影响效率,CPU 都自带一级缓存和二级缓存,一些 CPU 甚至增加了三级缓存,从这些缓存中读写数据要比内存快很多,但是还是无法使用飞速运转的 CPU,所以才会有寄存器的存在。

寄存器不是后来增加的,在最初的计算中就已经设计出来,相比而言,多级缓存出现的更晚一些,通常那些最频繁读写的数据都会被放在寄存器里面,CPU 优先读写寄存器,再通过寄存器、缓存跟内存来交换数据,达到缓冲的目的,因为可以通过名称访问寄存器,这样访问速度是最快的,因此也被称为零级缓存。

存取速度比较

通过上面的叙述我们可以知道存取速度从高到低分别是: 寄存器 > 1级缓存 > 2级缓存 > 3级缓存 > 内存 > 硬盘,关于它们的存取速度,举个例子很容易就能明白了,比如我们做菜(CPU工作)时,取手中(寄存器)正拿着的肉和蔬菜肯定是最快的,如果没有就需要把案板上(1级缓存)处理好的菜拿过来,如果案板上没有就在更远一点的洗菜池(2级缓存)中找一找,还没找到的话就要到冰箱(3级缓存)中看一看了,这时发现家里真没有,那去楼下的菜店(内存)去买点吧,转了一圈发现没有想要的,最后还是开车去农贸市场(硬盘)买吧。

通过上面这个例子应该能明白它们的速度关系了,既然缓存这么快,为什么不用缓存代替内存,或者将2、3级缓存都换成1级缓存呢?这里边有一个成本问题,速度越快对应着价格越高,如果你买过机械硬盘和固态硬盘应该很容易就理解了。

寄存器分类

常用的 x86 CPU 寄存器有8个:EAXEBXECXEDXEDIESIEBPESP,据说现在寄存器总数已经超过100个了,等我找到相关资料再来补充,上面这几个寄存器是最常用的,这些名字也常常出现在汇编的代码中。

我们常说的32位、64位 CPU 是指数据总线的宽度或根数,而寄存器是暂存数据和中间结果的单元,因此寄存器的位数也就是处理数据的长度与数据总线的根数是相同的,所以32位 CPU 对应的寄存器也应该是32位的。

常用寄存器用途

上面提到大8个寄存器都有其特定的用途,我们以32位 CPU 为例简单说明下这些寄存器的作用,整理如下表:

寄存器含义用途包含寄存器
EAX累加(Accumulator)寄存器常用于乘、除法和函数返回值AX(AH、AL)
EBX基址(Base)寄存器常做内存数据的指针, 或者说常以它为基址来访问内存.BX(BH、BL)
ECX计数器(Counter)寄存器常做字符串和循环操作中的计数器CX(CH、CL)
EDX数据(Data)寄存器常用于乘、除法和 I/O 指针DX(DH、DL)
ESI来源索引(Source Index)寄存器常做内存数据指针和源字符串指针SI
EDI目的索引(Destination Index)寄存器常做内存数据指针和目的字符串指针DI
ESP堆栈指针(Stack Point)寄存器只做堆栈的栈顶指针; 不能用于算术运算与数据传送SP
EBP基址指针(Base Point)寄存器只做堆栈指针, 可以访问堆栈内任意地址, 经常用于中转 ESP 中的数据, 也常以它为基址来访问堆栈; 不能用于算术运算与数据传送BP

寄存器EAX、AX、AH、AL的关系

在上面的图标中每个常用寄存器后面还有其他的名字,它们是同一个寄存器不同用法下的不同名字,比如在32位 CPU 上,EAX是32位的寄存器,而AX是EAX的低16位,AH是AX的高8位,而AL是AX的低8位,它们的对照关系如下:

00000000 00000000 00000000 00000000
|===============EAX===============|---4个字节
                  |======AX=======|---2个字节
                  |==AH===|-----------1个字节
                          |===AL==|---1个字节

汇编语言指令

终于说到汇编常用指令了,因为 linuxwindows 下的汇编语法是有些不同的,所以下面我们先通过 windows 下的汇编指令来简单学习一下,后续再来比较两者的不同。

数据传送指令

指令名称示例备注
MOV传送指令MOV dest, src将数据从src移动到dest
PUSH进栈指令PUSH src把源操作数src压入堆栈
POP出栈指令POP desc从栈顶弹出字数据到dest

算术运算指令

指令名称示例备注
ADD加法指令ADD dest, src在dest基础上加src
SUB减法指令SUB dest, src在dest基础上减src
INC加1指令INC dest在dest基础上加1
DEC减1指令DEC dest在dest基础上减1

逻辑运算指令

指令名称示例备注
NOT取反运算指令NOT dest把操作数dest按位取反
AND与运算指令AND dest, src把dest和src进行与运算之后送回dest
OR或运算指令OR dest, src把dest和src进行或运算之后送回dest
XOR异或运算XOR dest, src把dest和src进行异或运算之后送回dest

循环控制指令

指令名称示例备注
LOOP计数循环指令LOOP label使ECX的值减1,当ECX的值不为0的时候跳转至label,否则执行LOOP之后的语句

转移指令

指令名称示例备注
JMP无条件转移指令JMP lable无条件地转移到标号为label的位置
CALL过程调用指令CALL labal直接调用label
JE条件转移指令JE lablezf =1 时跳转到标号为label的位置
JNE条件转移指令JNE lablezf=0 时跳转到标号为label的位置

linux 和 windows 下汇编的区别

前面说到 linuxwindows 下的汇编语法是不同的,其实两种语法的不同和系统不同没有绝对的关系,一般在 linux 上会使用 gcc/g++ 编译器,而在 windows 上会使用微软的 cl 也就是 MSBUILD,所以产生不同的代码是因为编译器不同,gcc 下采用的是AT&T的汇编语法格式,MSBUILD 采用的是Intel汇编语法格式。

差异IntelAT&T
引用寄存器名字eax%eax
赋值操作数顺序mov dest, srcmovl src, dest
寄存器、立即数指令前缀mov ebx, 0xd00dmovl $0xd00d, %ebx
寄存器间接寻址[eax](%eax)
数据类型大小操作码后加后缀字母,“l” 32位,“w” 16位,“b” 8位(mov dx, word ptr [eax])操作数前面加dword ptr, word ptr,byte ptr的格式 (movb %bl %al)

总结

  • 汇编指令是机器指令的助记符,与机器指令是一一对应的
  • AT&T的汇编语法格式和Intel汇编语法格式的是不同的
  • 常用寄存器:EAXEBXECXEDXEDIESIEBPESP
  • 存取速度从高到低分别是: 寄存器 > 1级缓存 > 2级缓存 > 3级缓存 > 内存 > 硬盘
  • 常用的汇编指令:movjejmpcalladdsubincdecandor

如今的每分每秒都是人生,不要总想着将自然发生的事情拖到预定的时刻才进行~

相关文章:

  • 使用c++filt命令还原C++编译后的函数名
  • 配置Beyond Compare 4作为git mergetool来解决git merge命令导致的文件冲突
  • git在回退版本时HEAD~和HEAD^的作用和区别
  • 对称加密、非对称加密、公钥、私钥究竟是个啥?
  • 认证、HTTPS、证书的基本含义
  • 码龄10年工作6年的搬砖小哥,最常访问的学习网站都在这里了
  • C++中的std::lower_bound()和std::upper_bound()函数
  • 根证书的应用和信任基础
  • Shell脚本中获取命令运行结果、特殊变量使用、条件判断等常用操作
  • gdb调试解决找不到源代码的问题
  • GDB调试指北大全
  • 小白眼中的docker究竟是个什么东西
  • GDB调试指北-启动GDB并查看说明信息
  • Redis源码-BFS方式浏览main函数
  • GDB调试指北-启动调试或者附加到进程
  • echarts的各种常用效果展示
  • ES6--对象的扩展
  • k8s如何管理Pod
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • Redis字符串类型内部编码剖析
  • ucore操作系统实验笔记 - 重新理解中断
  • uva 10370 Above Average
  • Vue学习第二天
  • web标准化(下)
  • 程序员该如何有效的找工作?
  • 第十八天-企业应用架构模式-基本模式
  • 规范化安全开发 KOA 手脚架
  • 三分钟教你同步 Visual Studio Code 设置
  • 译自由幺半群
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • ​油烟净化器电源安全,保障健康餐饮生活
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • (1) caustics\
  • (Matlab)使用竞争神经网络实现数据聚类
  • (分布式缓存)Redis持久化
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (原創) 如何使用ISO C++讀寫BMP圖檔? (C/C++) (Image Processing)
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • (转)IOS中获取各种文件的目录路径的方法
  • (转)程序员疫苗:代码注入
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .Net 高效开发之不可错过的实用工具
  • .NET 依赖注入和配置系统
  • .NET简谈互操作(五:基础知识之Dynamic平台调用)
  • :=
  • ?
  • ??eclipse的安装配置问题!??
  • @transaction 提交事务_【读源码】剖析TCCTransaction事务提交实现细节
  • [20160902]rm -rf的惨案.txt
  • [CSS]浮动
  • [FFmpeg学习]从视频中获取图片
  • [iOS]-NSTimer与循环引用的理解
  • [javascript]Tab menu实现