汇编入门简单,深入难
使用8086架构进行学习,本章节如果没有学过计算机组成原理将可能有点难以理解,由于我学过了,所以记笔记的时候我会加上计组的知识来解释,看不懂直接跳过解释即可。

计算机如何工作


简单来说就是我们的应用或者使用高级语言编程最终都会变成一条条的由01组成的计算机指令,那CPU要执行指令就要先使用总线系统控制或者说操控某几根线导通一条路出来,让我们要取的指令有一条路可以走到CPU中,这就是去存储器中拿数据的一个大概过程。
希望与外部设备进行交互或者通讯,我们计算机需要的是一个接口,接口连接上外部设备,并且与该计算机设计的接口规范一致就能够接上,我们使用汇编的指令中就是通过一种监听方式监听接口,在汇编中叫做 端口,但是这个端口需要注意和web中的端口进行区别。
文言一心对两种端口的解释:"端口"是指网络通信中的端口号,而在汇编语言中,"端口"是指硬件接口中的端口地址。

可能会产生的疑惑:
总线系统是什么?
首先总线这一个概念是在计组中详细介绍的,在汇编中只需要知道汇编的指令具体要实现对应的操作都是总线控制比如Lmov ax , 7 它就知道控制将7送进ax寄存器中。

还有一个要注意的就是:不用纠结于我们的汇编能不能或者到底怎样处理指令的,我解释一下,但是可以不看,这里用作我自己复习笔记

首先我们的CPU控制总线会根据节拍和阶段发出取指信号,PC程序计数器拿出指令的地址,然后到存储器中取出指令,给到IR指令寄存器,然后将指令送进CPU,CPU里面又分为硬布线或者微程序两种设计,不管哪一种,最后CPU会发出一个控制信号,这个信号能够完成对应的取过来的那一条指令的所有微操作,比如一条指令可能mov ax, 7的同时我们PC程序计数器还会指向下一条指令,所以我们需要知道这个信号能够完成这条指令即可,如此循环…
还有一个问题就是:不用担心或者说只是我自己担心而已,我以前一直觉着高级语言怎么能够让CPU执行这么复杂的东西,我连有的代码都看不懂!?
当然这个担心也是多余的,反过来推,CPU只执行01组合的数据指令,那我们在这之前可能是对应我们的汇编语言,汇编语言在转机器码的时候(机器码就是01组合的数据指令),这会有一个叫做汇编器的东西将其转换成机器码,OK截止到汇编,继续逆向推,继续往上就是高级语言了,C语言,那C语言如何转汇编,这就很简单了,那就是使用我们的编译器,(编译器里面怎么转就不深究了),那这就全部都解释清楚了,既然C语言能够转汇编,汇编转机器码,最后给到CPU,所以CPU根本不知道你在干嘛,只要给到正确的指令给他执行即可。同时这个也解释了我们计算机生态圈如此强大的由来,我们的底层可能就很少很少的指令,但是我们一步一步往上也就是说机器码可以编程编出汇编语言,那我们汇编就可以做程序,再用汇编编了一个C语言,那我们就可以用C语言写更牛b的程序,慢慢的我们windows就出现了(狗头保命)。

存储器

8086架构中

  • 存储器都是以byte为编程单位
  • 每一个byte都有一个地址,所以要取(连续的地址的)下一个数据的时候就要将地址+1,这里的+1的单位是byte,所以说我们上面讲存储器都是以byte字节为单位解释通了。
  • 地址用无符号整数表示(那自然是),汇编中使用16进制表示
    这个怎么说呢,最好是用16进制,其实你传数据的时候也可以使用二进制或者十进制,但是他在debug过程中显示都是用16进制,所以就默认了我们都用16进制显示吧。
  • 我们有一个word单位的数据的时候,或者说我们要取出word单位的数据的时候,用word中第一个byte地址作为整个数据的地址,而不是说两个地址(因为上面提到byte为单位)
    解释:这里是因为我们汇编中有三种常用的单位,byte最基本,word为两个byte,dword为两个word即4个byte,但是他们的地址都是最低的byte地址作为这个数据的地址,比如说我要取出dword的数据,那么多怎么取,我们只需要给出首地址即可将整个段取出来(这取决于我们要操作的操作数单位和寄存器个数和寄存器位数)

逻辑地址到物理地址

首先这个我认为是解决我们汇编过程中,可能有的人总是担心这么操作计算机对应的机器码数据会不会出事啊?
在以前的DOS系统可能会,但是现在不会,现在能够操作的都是在实模式下进行的,给你修改的肯定可以修改, 但是不给你修改的早就保护起来了, 真的想修改也修改不了, 因为有的就刻录在硬件中,除非你重新买一份选择自己写程序。

  • 逻辑地址:16bit的段地址:16bit的偏移地址
    这个地址是每一个程序(进程)都自己有的一段连续的空间,换句话说就是我们编程人员看到的都是连续的地址,但是真正的空间是很少或者说不会是连续的,真正的物理地址学过计组或者操作系统的都知道是离散的,那问题来了,我们这样做的目的就是通过逻辑地址映射到物理地址这个过程计算机帮我们做了,所以我们只需要操作简单的那一边,也就是连续的地址这边,我们就有很大的灵活性,同时也为计算机的安全做了一层保护。
    逻辑地址在8086中:16bit的段地址:16bit的偏移地址

    • 段地址:在一个程序中,我们还可以继续将其进行分段,每一个段表示一个地址空间,一个段能够表示的空间为64kb(稍后解释)。
    • 偏移地址:偏移地址就是在这个段中我们相对于这个段的偏移量。

讲完物理地址就知道为何一个段占64kb

  • 物理地址:20bit的真实物理地址
  • 逻辑地址转物理地址
    16bit的段地址 × 十进制的16 + 偏移地址 = 物理地址

下面用计组和操作系统的知识解释为什么看不懂就直接记住即可:
首先逻辑地址上面解释过了,是因为编程方便和计算机安全,这是其次,公式的由来是因为我们的8086架构中物理地址有20位,也就是可以说8086架构中CPU的地址总线就是物理地址的位数。(可能有的疑问:那万一地址超过了20位bit怎么办,我不可以分两次取地址吗,答案是不可以,8086中物理地址只能一次性取)。回到正题,由于我们地址线是20位,所以我们段地址乘以16就是二进制往左移4位,换句话说就是十六进制的段地址往左移了一位,因为四位二进制对应一位十六进制,如此一来,我们段地址编程了20位,那就取到了段地址的首地址,还需要加上偏移地址才是我们真正要拿的地址。至此解释完毕。那么下图就能看得懂了。

访问两次内存的意思是使用你这个字内容低地址来找到第一个byte,但是访存访问了两次,并不是说我们访问word需要两个地址。
@@@@@@@@@@@@@@@@@@@@@@
下面给出一个我们老师ppt上的对段地址的理解:

  • 回到最后一个问题:为什么一个段能够存64KB?
    因为我们一个段中假设首地址为0,那么偏移地址最大也就全F,所以我们范围就是0000H~FFFFH,
    我们8086中单位又是byte,
    所以一共有64K * B = 64KB

寄存器

CPU中有两个重要的部件,ALU和控制器,ALU用来计算,控制器用来接收指令后解释并发出对应指令操作的信号去工作,然后这其中还有很多的寄存器,在8086中的寄存器相比现代CPU中来说算少了。

数据寄存器:

  • AX、BX、CX、DX
    这种寄存器需要知道的是他们都可以分开两半,一半高位一半低位寄存器(这是为了兼容以前最早一批寄存器),同时也为我们汇编提供了很大的方便。例子:AX可以分为AH,AL,H表示high高位,L表示lower低位,同理BX可以分为BH,BL,以此类推。

指针寄存器:

  • SP、BP、SI、DI
    这种寄存器一般是用于偏移地址,但是也可以用来存储数据,但是但是!,SP不能用来存数据,他是栈指针,在这里挑明了SP只能是栈指针地址,我们编写程序都是这么规范的,BP可以存但是我们一般不这么用,一般是用来存放栈底指针,BP可以理解为base ptr。SI和DI这两个一般都是用来标识偏移地址的,S源地址,D目的地址,一般都是这样使用。(迫不得已要存数据的话建议只用SI和DI)

段寄存器

  • CS、DS、SS、ES
    CS是指令地址寄存器,也就是说是表示我们要执行那一条指令的地址的段地址,他是搭配IP寄存器使用的,CS:IP组合起来就是一个逻辑地址,对应的地址就是我们下一条要执行的指令的地址(为什么是下一条?因为我们执行完一条指令后,PC程序计数器会自增到下一条指令的地址,具体自增多少看你指令的大小)。DS是数据段寄存器,表示这个段内都是存放数据的,SS 是栈段,stack segment ,表示这个段都是用来存放栈数据的,ES,extend扩展段,可以用来存放临时数据或者可以当作我们编程中的temp 变量。
    以上所有段寄存器都是可以通过汇编代码修改,不明白为啥其他人总是说段寄存器不可修改,其实对于一个有修养的汇编程序员来说只要不是写病毒,对于修改段寄存器都是十分小心的过程。

控制寄存器

  • IP、FLAGS
    IP就是指令地址的偏移地址,FLAGS是标志寄存器,标志寄存器肯定是用来标记的,不同的是他是用寄存器里面的每一比特0或者1来表示一种状态。比如:有一个位为1的意思是该数字式负数,那0就是表示该数字式正数了。

AX、BX、CX、DX,8086架构中只有数据寄存器可以分为高位寄存器和低位寄存器。并且高位低位分开操作的时候互不干扰,因为他们本质就是两个8位寄存器,只不过为了兼容,我们把两个组合成一个16位的ax寄存器。

数据寄存器使用细节

  • AX一般都是像一个变量一样,在代码里面可能会很频繁的出现,计算后的结果等等一般都是存这里面
  • CX一般是循环次数,可能和很多不同的伪指令搭配使用,比如loop的循环次数就是CX决定的
  • DX这就是用来操作数据的,比如我们计算的后的结果ax计算更高位数的运算的时候就用到DX来存放高位数据,低位数据一般放在AX中。

其他知识点细节

堆栈Stack

先进后出
8086中,栈顶是低地址,栈低是高地址
解释:栈的出现让我们函数调用方便了很多,也是有了栈所以我们参数有多个的时候,比如C语言中函数参数,一般是最右边的参数先入栈,然后出栈的时候都是最左边的参数先出来,这也符合我们人类的思维,使用的参数是左边第一个先用(强行解释一波)
由于栈顶是低地址,栈低是高地址,所以我们在写汇编的时候,开辟一个栈空间的时候,使用的时候记得将SP指向高地址,因为我们栈底或者说我们存数据都是从高地址往低地址存过去。

标志寄存器

在本篇中了解即可

中断

这里有一个伏笔,当我们学到int中断的时候有一个很恶心的地方就是没有告诉你一个int中断号对应的就是我们 中断的 [CSIP地址] 的数据存在0号段中 ,这很恶心,而且后面会告诉你一个可以装入自己编写的中断程序覆盖默认的 中断程序的地址同样是0号段中,但是有的老师没有提到这点,教材上也没有具体的说。
其次有一段空间,IP为0号段中的0200-02ff这范围里面的数据不会因为程序运行过程中被覆盖掉,所以将自己编写的中断程序装入这段空间,之后就可以修改0号段中对应的中段号的地址存的CSIP地址修改为0200这个入口地址就能转到自己写的中断程序执行了。
0号段的偏移地址从0开始,每两个字节表示中断号的入口地址CSIP,比如int 0中断号入口程序就在段的0,1地址,我们拿到这个地址就可以跳转对应的中断程序,中断程序的地址只要修改了自己的地址就不会跳到计算机默认的中断号对应的程序,这是个大坑。
这段话总结来说就是:

  • 0号段开始,用两个word即四个byte存CS和IP,存完后就到下一个中断号码的中断程序CSIP,例如:存完0号中断,0-3这部分空间存0号CSIP,那么接下去4-7空间存1号的CSIP。
  • 中断号地址是0号段,同时0号段中的0200-02ff地址可以理解为不会被覆盖,没用过的,所以我们同样可以将自己写的程序放进这一段空间内作为中断程序。(否则写在其他空间就很有可能会被执行过程中被覆盖掉,这样中断程序就执行不了, 因为被覆盖了)

我还是忍不住补充一下:一定要记住,0200-02ff这一段空间是不会将我们中断号的入口地址覆盖,他是没有用过的,比如我们中断号21他就是偏移地址为十进制21的地址空间存的是中断程序的入口地址,还有很多中断号比如十六进制的7ch,总是记住同样在0号段的0200-02ff这一段是不会用到的就行,并且不会被覆盖,是因为中断程序入口地址被覆盖了那就出大问题了。(当然21也可以写十六进制0x15)


总结:本章就是理解计算机到底如何执行汇编指令的,但是这是笼统的,具体还得学习计组,只需要了解大概即可。汇编如果没有这些知识也是可以学下去的,只不过学起来有点痛苦,我们不知道原理,就好像我一开始作为初学者学习C语言的时候,希望自己在复习的时候能够理解自己写的稀巴烂文章吧…

80x86汇编—80x86架构的更多相关文章

  1. 80x86汇编小站站长简单介绍-2014年08月23日

    [序言] 旧版的"80x86汇编小站站长简单介绍"已经过时了, 因此于2013年10月01日花费1个小时又一次更新和排版一次. [人生格言]  1] 一生都用头脑而不是情绪解决这个 ...

  2. 80x86汇编小站站长简单介绍

    [人生格言] 1] 一生都用头脑而不是情绪解决这个问题 2] 仅仅有偏执狂才会成功 3] 在最困难时都要保持一份幽默感 4] 吾生也有涯,而知也无涯,以有涯随无涯,殆已 [简历] 我的生日: 1981 ...

  3. 清华操作系统实验--80x86汇编基础

    前言 80x86架构里,因为历史原因字是16位的,因此在汇编指令中用后缀-b,-w,-l来表示操作数是字节 字 或是双字 C声明 Intel数据类型 汇编代码后缀 大小(字节) char 字节 b 1 ...

  4. 8086、80x86(IA-32)、64(IA-64)位CPU发展

    众所周知,CPU(中央处理单元)是计算机的核心部分,CPU在单位时间内能一次处理的二进制数的位数叫字长,从386.486直到奔腾系列的CPU都是32位,大多数情况32位计算已经能满足现阶段人们的需要. ...

  5. 学习linux内核时常碰到的汇编指令(1)

     转载:http://blog.sina.com.cn/s/blog_4be6adec01007xvg.html 80X86 汇编指令符号大全 +.-.*./∶算术运算符. &∶宏处理操作符. ...

  6. 1.汇编指令介绍(arm)

    本文作为本人学习过程中的记录及时不时的突发奇想偶记.鄙人菜鸟一只,文中如有错误或疏漏,若读者肯不吝赐教,在下感激零涕.文章一直不断更新中 一.汇编语言 汇编语言是一种应用计算机.微处理器.微控制器或其 ...

  7. 2013-6-2 [转载自CSDN]如何入门Windows系统下驱动开发

    [序言]很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都以英文为主,这样让很多驱动初学者很头疼.本人从事驱动开发时间不长也不短,大概 ...

  8. windows驱动开发推荐书籍

    [作者] 猪头三 个人网站 :http://www.x86asm.com/ [序言] 很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都 ...

  9. 程序员们,AI来了,机会来了,危机也来了

    程序员们,AI来了,机会来了,危机也来了 1.人工智能真的来了 纵观古今,很少有计算机技术能有较长的发展寿命,大部分昙花一现,比如:昔日的DOS.windows3.2.foxpro.delphi.80 ...

  10. 如何正确入门Windows系统下驱动开发领域?

    [作者]猪头三个人网站 :http://www.x86asm.com/ [序言]很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都以英文 ...

随机推荐

  1. #线段树,组合计数,二项式定理#CF266E More Queries to Array

    洛谷传送门 CF266E传送门 分析 首先区间修改区间查询首选线段树 要找突破口,\((i-l+1)^k\)中\(i\)不是定值, 显然得拆开,而且\(k\)很小,根据二项式定理, \[\sum_{i ...

  2. #树链剖分,线段树#洛谷 2146 [NOI2015]软件包管理器

    题目传送门 分析 安装时1到\(x\)路径上都变为1,删除时\(x\)的子树都变为0, 显然可以用树链剖分+线段树实现 代码 #include <cstdio> #include < ...

  3. 深入理解 C++ 语法:从基础知识到高级应用

    C++ 语法 让我们将以下代码分解以更好地理解它: 示例 #include <iostream> using namespace std; int main() { cout <&l ...

  4. Nacos 多个实例的服务调用失败

    在微服务开发阶段,开发人员会频繁启动服务. 这样Nacos上会经常出现一个服务存在多个实例,这是自己和其他同事都启动了同一个服务造成的. 此时使用OpenFeign对该服务进行远程调用,会有很大概率出 ...

  5. Discovery直播 | 移动应用“通行证”——钥匙环,解锁管家式安全出行服务

    用户在登录环节的直接诉求是:别让我等.别让我想.别让我烦.而帐号输入.繁琐验证,以及由此带来的安全风险,总会让很多人望而却步. 如何在简化登录流程的同时保障登录凭证安全?如何帮助用户一键免密登录同一开 ...

  6. MogDB/openGauss 坏块测试-对启动的影响-测试笔记1

    MogDB/openGauss 坏块测试-对启动的影响-测试笔记 1 在 UPDATE 操作提交后,脏块落盘前 kill 掉 mogdb 数据库,然后对 UPDATE 修改的坏进行以下破坏操作,仍然能 ...

  7. Windows Server之KMS

    KMS是什么? KMS全称是Key Management Service,本身就是微软官方提供的一种激活方式,通常用于大型组织客需要激活的客户端数量不固定的情况. 正常情况下,激活Windows系统需 ...

  8. hibernate4升级5带来的一些参数变化

    public String hqlToHibernate5(String hql) { String[] tmp = hql.split(" "); String hqlTmp = ...

  9. 使用 Docker Compose 安装 APISIX

    1.基本概念 APISIX 是 Apache 下的一款云原生的 API 网关,支持全生命周期的 API 管理,在应用中可以作为所有 API 调用的统一入口. APISIX 有一些基础概念如下: Ups ...

  10. 树上点差分的经典应用 LuoguP3258松鼠的新家

    树上点差分的核心就是如何避免重复,即正确的运用差分数组 例如a,b点路径上点权值加1,则把a,b路径找到,并找到其LCA,此时可以把a到根,b到根这两条路径看出两条链,把每条链看出我们熟悉的 顺序差分 ...