粉丝9323获赞2.9万

今天我们要学的会编指令是 sub。 在 微软的 ms m 语法和 intel 写法中, sub 指令用于执行减法运算。它的基本格式是 sub 后跟两个操作数,第一个是目标操作数, 第二个是原操作数。这条指令的功能是从目标操作数中减去原操作数的值,然后将结果存储回目标操作数的值,然后将结果存储回目标操作数的值,然后将结果存储回 x, 就是将寄存器 rex 的 当前值减去寄存器 r x 的 值,差值保留在 rex 寄存器里。 操作数可以是寄存器内存地址或立即数。比如 sub e x, 十表示把 e x 寄存器的值减去十,结果存回 e x。 而 sub d word p r m e x 则是从内存位置 mem 所存储的双字中减去 ex 的 值。更新内存内容执行萨布指令时,处理器会根据运算结果更新标志。寄存器中的多个标志位包括零标志 判断结果是否为零,符号标志判断结果是否为负信为标志用于无符号数溢出的情况和溢出标志用于有符号数溢出的情况。这些标志未在程序的条件分支和逻辑判断中非常有用。 通过萨布指令,我们可以在汇编程序中实现基本的算数减法处理数据计算和流程控制需求。

欢迎回来,今天我们来深入探讨一下 intel musm 语法格式下的会编语言。跳转指令。如果你正在学习底层编程,或者对处理器内部的工作机制充满好奇,那么这一部分内容 绝对不容错过。我们会从最基础的无条件跳转讲起,逐步扩展到那些依赖于标志寄存器中各个标志位的条件跳转指令,帮助你理清它们之间的关系,以及在实际编程中 如何正确使用它们。首先,我们简单回顾一下标志寄存器,也就是大家常说的 e flex, 它是一个位集合,每个位代表一个特定的状态标志, 这些标志会随着算数逻辑运算指令的执行结果而自动更新。在条件跳转中,我们最常用到的标志位包括进位标志 c f, 零标志 z f、 符号标志 a f, 溢出标志 off, 还有基友标志 p f。 例如,当你执行一条比较指令 c m p 时,它实际上会被丢弃,但却会更新这些标志位, 从而为后续的条件跳转提供判断依据。接下来,我们正式进入跳转指令的世界。先来看最简单的 gmp, 它是无条件跳转,也就是说,只要执行到 gmp 指令,处理器就会直接跳转到指定 的目标地址继续执行,不会进行任何条件判断。它的格式通常是 gmp 目标地址, 这个目标可以是一个标号,也可以是寄存器或内存中存放的地址。现在让我们重点关注条件跳转指令,它们通常跟在一个 比较指令之后,根据比较结果所设置的标志位来决定是否跳转。第一类是基于零标志 z f 的 指令 g 和 j z 其实是同一条指令的不同写法。 g 是 jump, if equal 的 缩写, j 是 jump, if 0, 它们检查 z f 是 否为一。如果 z f 等于一,说明上一次比较的两个数相等 或者运算结果为零,于是就跳转。反过来, j 和 j z 也是同一条指令,它们检查 z f 是 否为零, 如果不相等或者不为零,就跳转。接下来是和进位标志 cf 相关的指令。 jc 用于检测 cf 是 否等于一,如果有进位或界位发生就跳转。 jnc 则相反,检测 cf 是 否等于零,无进位时才跳转。 在无符号数比较中, cf 是 非常重要的。比如 j b 全称是 jump, if below, 用于判断无符号数是否低于它实际上就是 j c, 因为低于就相当于产生了界位。 类似的, j a 是 jump, if above or equal, 它检查 cf 是 否为零,也就是无进位,表示无符号数高于或等于。 而 j b 则是 jump, if below or equal, 他 检查 cf 等于一或者 cf 等于一,表示低于或等于。亚则是 jump, if above, 他 要求 cf 等于零,并且 cf 等于零,表示无符号数高于。再来看基于溢出标志 off 和符号标志 i f 的 指令,这类指令主要用于有 符号数的比较。 jo 检测 off 是 否为一,即上次运算是否发生了溢出。 gel 则检测 off 是 否为零。无溢出 j s 检测 f 是 否为一,也就是结果为负数。 j n f 检测 s f 是 否为零, 结果非负。现在组合标志来判断大小关系。对于有符号数, j l 是 jump, if less, 它检测 s f 是 否不等于 of, 如果小于就跳转。 j g 是 jumpif greater or equal, 它检测 i f 是 否等于 of, 表示大于等于 gl。 一 是 jump, if less or equal, 它检测 z f 等于一或者 s f 不 等于 of。 g e 是 jump, if greater, 它检测 z f 等于零,并且 s f 等于 off。 此外,还有基于基偶 标志 p f 的 指令, j p 或 j p e。 检测 p f 是 否为一,表示结果中一的个数为偶数。 j n p 或 j p o, 检测 p f 是 否为零, 表示基数。可能你还听说过 g x z 和 g c x z, 它们不是基于标志位,而是基于 c x 或 e c x 寄存器的值。当寄存器为零时,跳转用于循环控制中。在使用这些指令时, 有一个重要的细节需要留意。条件跳转指令的跳转范围通常有限,在时模式下,一般是当前指令前后一二八字节以内。 如果目标距离太远,你可能需要借助无条件 gmp 来中转。最后总结一下,所有的条件跳转本质上都是对标志位的测试。当你写好一条 cmp 指令之后,处理器就会默默记录下 比较结果对应的标志位,然后你只需选择合适的跳转指令即可。例如比较两个有符号数,如果想在第一个数大于第二个数时跳转,就用 j g。 比较无符号数时, 则用 y。 理解和记住这些指令对应的标志位组合能让你更自如地阅读和边写,会编代码。好了, 今天关于 masm 语法下跳转指令的分享就到这里,希望对你有所帮助。如果你觉得内容有用,别忘了点赞和订阅,我们下期再见!

会编语言一刷而过,第一个知识点,基础知识,我们为什么要学习会编语言呢?首先我们要知道,会编语言在这个行业中发挥着不可替代的作用, 因为它的效率非常的高,不管是运行效率还是开发效率,所以我们学习汇编语言是非常有必要的。 会编语言,他是直接在硬件之上工作的编程语言,首先我们要了解硬件系统的结构,然后才能有效的应用会编语言,在智障中,我们会对硬件系统结构的问题进行一部分的探索。 好,我们先来看第一节机器语言,机器语言就是机器指令的集合,机器指令是什么呢?它是一台机器,一台计算机可以正确执行的命令,那么对于这些命令来讲,都是用二进 之术进行表示的,这个就是机器语言,这个呢就是汇编语言。 好,我们来看会编语言的产生,会编指令是机器便于记忆的书写格式,这是什么意思呢?会编指令它所表达的语意和机器指令是一样的, 但是会编指令在表示方法上,它是便于人记忆的,所以它仅仅是在一种书写格式上叫做一个改进。因此呢,我们说会编指令呢,它是机器指令的速记符, 这个助剂符对我们人这个角度来讲是便于我们记忆的寄存器,简单来讲就是 cpu 中 可以储存数据的器件,一个 cpu 中可以有很多个计存器。好,我们来看会编语言的组成,它是由会编指令为指令其他符号组成的, 由程序员将会编指令输给传译器,然后传译器将这些会编指令转成机器码,然后再传给计算机,这样程序就可以运行了。 我们来看存储器,它是 cpu, 也是计算机的最核心部件,它控制着整个计算机的运行,要想让一个 cpu 工作,就必须向它提供指令,还有数据 指令和数据在存储器中存放,也就是平时所说的内存。指令和数据 它需要从外存读到内存里面,就由 cpu 和这个内存进行打交道。我们还知道数据和指令都是二进制信息,那么对这些二进制信息,现在我们就带来一个问题,如果单纯地给出这一个二进制串, 我们说它是数据还是指令呢?那的确数据和指令它都是用二进制来表示的。其实对这个问题的回答呢,就是这样的,它可以是数据,也可以是指令, 要看它是数据还是指令,就要看你 c p、 u 如何去用它。好,接下来我们来看存储单元, 存储器被划分为若干个存储单元,每个存储单元都是从零开始顺序编号, c 语言中的数组也是这样编号的。对于大容量的存组器,一般含有以下单位进行计量,容量,我们要把这些记住。 cpu 对纯速器的读写。计算机中有专门连接和 cpu 和其他芯片的导线,我们统称为总线。 在计算机里面,总线分为数据总线、地址总线、控制总线这三类。 我们先来看地址总线,如果说想到内存的某一个地方 去获取数据,我们就需要用到地址总线,地址总线上能传传送多少个不同的信息, cpu 就可以对多少个纯属单元进行巡址。好, 我们来看数据总线,它是 cpu 与内存或其他器件之间的数据传传送,通过数据总线来进行,数据总线的宽度决定了 cpu 与外界的数据传送速度。好,我们来看控制总线, cpu 对外部器件的控制都是通过控制总线来进行的, 我们来总结一下,一、会编指令是机器指令的助计符,同机器指令一一对应。二、每一种 cpu 都有自己的会编指令级。三、 cpu 可以直接使用的信息在储存器中存放,也就是内存。四、在储存储器中,指令和数据没有区别,都是。二、禁止信息,它可以 作为指令,也可以作为数据。五、存储单元都是从零开始顺序编号的, c 语言中的数组也是这样的。六、一个存储单元可以存储八, 八个 beat, 也就是八位。还有这些计量单位,我们要记住。八、这些总线地址总线,数据总线,控制总线,它们的宽度都分别决定了 cpu 的不同能力。好,这节就讲到这里,感谢你的倾听,再见!


汇编语言是第二代计算机语言,是用一些容易理解和记忆的缩写单词来代替一些特定的指令,例如用 add 代表加法操作指令, sub 代表减法操作指令以及 in 代表增加一 deck 代表减去一 move 代表变量传递等等。通过这种方法,人们很容易去阅读已经完成的程序, 或者理解程序正在执行的功能,对现有程序的 bug 修复以及运营维护都变得更加简单方便。但计算机的硬件不认识字母符号,这时候就需要一个专门的程序 把这些字母变成计算机能够识别的二进制数或机器语言。因为会编语言只是将机器语言做了简单翻译,所以并没有根本上解决机器语言的特定性。所以会编语言和机器自身的编程环境 息息相关,推广和移植很难,但是还是保持了机器语言优秀的执行效率,因为它的可阅读性和简变性。会编语言到现在依然是常用的编程语言之一, 只是不像其他大多数的程序设计语言一样被广泛用于程序设计。在今天的实际应用中,它通常被应用在底层及硬件操作和高要求的程序优化的场合,例如驱动程序、嵌入式操作系统和实时运行程序都需要会编语言。

今天我们要学的是 cmp 指令,这个指令在汇编语言里用来比较两个操作数, 它不会修改操作数的值,而是根据比较结果来设置处理器的标志位。这些标志位后续可以用于条件跳转,让程序能做出不同的决策。在用法上, cmp 指令需要两个参数写位, c、 m、 p 目标圆,其中目标操作数可以是寄存器或内存地址 圆,操作数可以是寄存器内存地址或立即数。例如 c m rex, r x 就是 把寄存器 rex 的 值和寄存器 r x 的 值进行比较。比较时实际上执行减法操作,但结果不保存, 只更新标志位。假设 rex 中是十, r x 中是五,执行后,标志位中的零标志会被清除,表示不相等,符号标志和进位标志也会相应设置。这样你之后就能用 g 或 j、 n、 e 这样的条件跳转指令来检查标志位,并控制程序流程。理解 c、 n、 p 指令是掌握会编条件逻辑的关键一步。每天积累一个指令,慢慢你就会发现编辑底层代码变得更得心应手了。


前几天雷军写的代码火了,我们先来看一下雷军写的代码什么水平。雷军曾经说,代码不仅要整洁,逻辑也要无懈可击,自己的写的代码一定就是说达到规范的这种标准, 这个代码写的是会编的语言,会编通常是程序员一个基本功的体现啊。这附近也说了,雷军写的代码很牛逼啊。 雷军的代码不仅完整,而且考虑的很周全,写代码就像写教科书一样,甚至还为了可读性做了专门的优化,这样雷军写出来的代码可读性和维护性非常的强。雷军在国家会议中心演讲的时候讲了很多啊,我总结了几条,给大家分享一下。第一个啊, 遇到问题不要瞎琢磨,百分之九十九的问题都是有答案的,模仿就行了,对不对?别人能搞出来,你也可以搞出来。第二个,知识他不是 是曲线型的,他是网状的,成体系的,所以我们在刷短视频的时候,你可以你以为可以学到很多东西,但是也只是刚入门去接触这个东西, 知识和知识之间他是有非常多的关联的,学技术也一样,你没必要非啃一本书,我啃,从头啃到尾,这个问题我解决不了,就不啃下去,这样肯定是不行的,你在不懂的地方是可以跳过去的,后面你再搞其他地方,你可能就懂了。 你刚开始看设计模式的时候非常的枯燥,那么你当你看到了史鹏认,你再结合一下设计模式就就容易懂了。前面的东西看不懂,跳过去就行了,不会影响到后面学习的,后面学会了,你再看看前面的就懂了。 第三个,很多大目标他其实不难实现,想出来混的好,你先出来,你不出来你怎么混啊? 所以你想成为架构师,先把哈喽味的给写出来。第四个,每一次的蜕变都是技术的提高,思维的提高,认知的提高。他分享了很多,我,我也记不全了。总结了一张表,大家可以看一下,点赞收藏,看一下,一定会对你有帮助的。


今天我们想跟大家一起讨论一下程序到底是怎么运行的。没错,通过了解会编语言和机器码之间的关系,我们可以知道高级语言是怎么一步步被转化成 cpu 能够识别的指令的。没错没错, 其实这些内容都是程序运行的最底层的原理了。对,那我们就直接开始吧。首先我们要讨论的话题是会编语言和本地代码之间的一一对应关系。嗯? 为什么说它们之间是一一对应的?就是每一条汇编指令都会有唯一的一串二金制的机器码来对应对,就它们之间是一个直接映射的关系。所以说汇编语言其实就是机器码的另一种写法,是吗?汇编语言就是用一些容易记住的英文缩写,比如 move 啊, add 的 这些来代表操作码, 然后它其实就是机器码的符号化表示哦,那这样的话程序员就可以读和写了,它跟硬件的操作是非常非常接近的, 同时它也让机器码变得有意义。那汇编语言到底在高级语言和机器码之间扮演一个什么样的角色呢?它其实是一个很关键的桥梁, 就是它既可以看到硬件底层的一些细节,同时它又比机器码要易懂。没错,所以理解汇编语言会帮助我们真正的明白程序在计算机里面是怎么一步一步的变成汇编代码的。 首先我们来看一下这个 c 语言的代码它都干了些什么?这个 c 程序其实很简单,它就是定义了一个函数叫 add num, 然后它里面就是把两个整数加起来返回哦,然后在 mean 函数里面呢, 就调用了这个 add num 函数,把一二三个四五十六传进去,然后把结果存在一个变量 c 里面。哦,那这段 c 语言里面的 main 函数, 它对应的这些汇编代码,每一句都在干什么?你能给我们解释一下吗?当然可以,首先 push e b p 就是 把原来的基址指贞保存起来,然后目 e b p esp 是 用当前的站指针来设置新的机制指针,这样就建立了一个新的战争。战争建好了之后,接下来这些步骤又是干什么的呢? subesp 四,就是给局部变量 c 分 配空间,然后两个 pose 就是 把参数四五十六和一二三压占,嗯,扣就会跳到 atnum 这个函数去执行, 同时把返回地址压占。哦,然后 add e s p 八是清理刚才压入的两个参数,最后目以 e p p 减四, e x 是 把函数的返回值存到局部变量 c 里面。明白了,那我们再来看一下 add num 这个函数,它本身在汇编层面是怎么实现的? 它是怎么用汇编指令来完成两个数相加并且返回结果的呢?其实这个函数一开始也是 push 一 p p 和某一 b p s p 这两句也是在建立战争哦,然后某一 a x e p p 加八是把第一个参数放到 ex 寄存器里面, i 的 ex e p p 加十二是把第二个参数也加到 ex 里面,所以结果就存在 ex 里面了。那它最后是怎么结束的呢? 结束的时候就一句 pop a p p 恢复原来的基值指征,然后 red 零就可以返回了,返回值默认就是放在 ex 里面。嗯,所以整个函数就很干净利落,参数的获取、计算、返回都是通过计算器来做的。 好的,那我们接下来要聊的一个话题是会编语言的一些基础。嗯,就是伪制令和它的语法格式。那什么是伪制令?它在会编程序里面到底扮演一个什么样的角色?伪制令其实就是专门给会编器看的一些命令,它不会被翻译成机器码,也不会被 cpu 执行。 哦,它主要是用来告诉汇编器怎么去组织和安排我们这个程序的结构,还有数据的位置。那能不能给我们举几个例子,就是常见的为指令都有哪些,然后它都干了些什么事?比如说像 dump, segment 和下划线 text 就是 用来划分代码段的,然后 pros 和 e n d p 是 用来定义一个过程,也就是相当于高级语言里面的函数。嗯,还有 end 就 会告诉汇编器, 我们的原文件到这里就结束了,后面就不用再处理了。原来如此,那会编指令的语法结构一般都是什么样的?能不能结合几个例子给我们讲一讲每一部分都在干什么?好的,它的格式一般就是操作码加上操作数, 操作码就是说这个指令具体要干什么事情,比如说目就是移动数据, add 就是 做加法,然后 push 就是 把数据压到站里面。嗯,操作数就是说这个指令要对谁进行操作,有的指令不需要操作数,有的需要一个或者两个操作数,能不能给我们举几个真实的指令?然后我们来看一看它的操作码和操作数分别是什么?行啊, 比如说木, e x a b p 加八,这个木就是操作码, e x 和 e b p 加八就是两个操作数,意思就是把 e b p 加八这个地址里面的值移动到 e x 里面。 嗯,再比如 add e s p 八,就是把 e s p 的 值加上八,还有 write 这种就不需要操作数,它就是直接让函数返回。然后我们就要讲到一个新的话题了,就是 cpu 里面的寄存器,它到底是干什么用的? 对,以及它在整个计算机系统里面处于一个什么样的地位?寄存器其实就是 cpu 内部的一些存储单元,它的读写速度是非常非常快的,比内存要快很多很多。对, 所以它一般是用来临时存放我们正在运算的数据,还有一些地址。哎,这么说的话,寄存器是不是就相当于 cpu 自己的一个小 workspace? 你 可以这么理解,就是我们把数据从内存里面拿出来,放到寄存器里面进行各种运算,运算完了之后再写回到内存。 嗯,但是寄存器的数量是非常有限的,比如说 x 八六架构,只有十几个通用寄存器,所以它是一个非常非常宝贵的资源。哦,那你能不能给我们介绍一下,就是常用的寄存器都有哪些?然后它们都各自是用来干什么的?可以啊,比如说 ex, 它叫累加寄存器, 它就是专门用来做算数运算的,同时它也会默认存放函数的返回值。嗯,然后 e b p 是 扩展基值,指向寄存器, 它会指向当前战争的底部,用来访问函数的参数和局部变量。这些寄存器感觉都还挺各司其职的。哈,那还有其他的吗?当然有啦, esp 是 扩展站,指真器存,它永远都指向站顶,用来管理站的压入和弹出。 e c x 是 计数寄存器,经常在循环里面做计数器,然后还有 e b x、 e d x 啊,这些都是通用寄存器,可以用来存数据或者地址,就看程序员怎么用了。了解了,那我们再来讲一下函数调用的时候,占到底是怎么参与工作的? 嗯,它在这个过程当中到底起了哪些关键的作用?占其实就是一个按照后进先出原则来管理数据的一个内存区域。对,在函数调用的时候,它会有几个非常重要的功能。 首先他会把函数的参数一个一个的压到站里面,嗯,然后 call 指令会把返回地址压到站里面,这样当函数执行完之后就知道要回到哪里去了。除了传递参数和保存返回地址之外,站还有什么其他的作用吗?当然有了, 就是在函数调用之前,还会把一些寄存器的值压到站里面,这样就可以保证在函数执行的过程当中,这些寄存器的值不会被破坏,然后函数返回之前再把这些值弹出来恢复现场。 嗯,而且函数内部的局部变量也是在站上面分配空间的,所以当函数返回的时候,这些局部变量就会自动被回收了。 ok, 那 我们来具体的说一下,这个函数调用的过程当中,站上面到底都发生了哪些变化。其实整个过程可以分成四步,第一步就是参数入站, 把参数按照从右到左的顺序一个一个压到站里面,嗯,第二步就是 call 指令会把返回地址压到站里面,然后调转到被调用函数的地址去执行。然后呢?函数体里面是怎么使用站的? 函数体一开始的时候,会先把老的 e d p 压到站里面保存起来,然后把 esp 的 值付给 e d p, 这样就建立了一个新的战争 哦,接下来就可以在这个战争上面为局部变量分配空间了。嗯,等到函数执行完了之后, write 指令会把返回地址弹出来,然后寄存器和站都会恢复到调用之前的状态, 程序就可以继续往下执行了。明白了,那我们再来讨论一下大局变量和局部变量在内存里面是怎么存放的?嗯,它们的存放位置,生命周期还有作用范围到底有什么区别?全剧变量一般是放在数据段或者 b s s 段里面,它的生命周期是贯穿整个程序运行的, 就是程序结束了之后才会被操作系统回收,然后它的作用范围通常是整个程序。嗯,但是局部变量就不一样了,它是在站上面分配空间的, 它的生命周期就只限于函数调用期间,函数返回了之后,这个局部变量就被销毁了哦,然后它的作用范围就只有函数内部可以访问。原来是这样, 那程序的流程控制就是循环和分支在汇编里面到底是怎么实现的?其实核心就是靠标志寄存器和跳转指令,比如说 c m p 指令,它会比较两个操作数的大小,然后根据结果去设置标志寄存器,但是它不会去改变操作数本身。 嗯,跳转指令有很多种,比如说 g, g, n, e, gl 啊这些,它们会根据标志寄存器里面的状态来决定要不要跳转到一个指定的地址去执行。 所以说条件分支和循环结构都是靠这些标志和跳转来完成的嘛。比如说 ifelse 这种条件分支,就是用 cmp 先比较,然后根据结果用 j 或者 je 这些指令直接跳到对应的代码块去执行。 嗯,那循环的话也是类似的,只不过是它会在循环体的末尾去做这个条件判断,如果成立的话,就用 gmp 或者其他的跳转指令跳回到循环的开头,对,形成一个闭环。好的,那我们再来回顾一下今天讲的这些核心知识点。 嗯,第一个问题就是汇编语言和本地代码到底是一个什么样的关系?它们其实就是一一对应的。嗯,汇编语言其实就是把本地机器码用注记存器, 它到底有什么作用?寄存器其实就是 cpu 内部的一些高速的存储单元,它就相当于运算的工作台。嗯,它可以用来暂时存放数据和运算的结果。那函数调用的时候,站到底是用来干什么的?站的作用可大了,它可以用来传递参数,保存返回地址, 保存寄存器的状态。嗯,还可以给局部变量分配空间。全局变量和局部变量它们在存储的方式上到底有什么差别? 全局变量是放在数据段里面的,它的生命周期是贯穿整个程序的。嗯,但是局部变量是在站里面,它只有在函数调用的时候才会存在。 最后一个问题啊,就是程序里面的循环和分支到底是怎么用?会编实现的实现方式就是用 c m p 指令先设置标志位,然后配合 g m p、 j j e 这些跳转指令来控制程序的执行顺序。好的,那我们今天把程序运行的底层逻辑算是捋了一遍。嗯, 从会编到机器码,再到函数调用和流程控制,基本把程序是怎么跑起来的这个问题给讲透了。没错,那今天的内容就到这里了,拜拜。拜拜。

网页上的文字只能看不能复制咋办?教你一招,让你为所欲为安排。很多网页在复制文字的时候会提示 不是有钱人无法复制。其实你只需要在网页前面输入 iad 冒号,按下回车,就可以直接进入阅读模式,这个时候就可以直接复制粘贴文字啦。