汇编语言
1、基础知识
| (1)汇编指令是机器指令的助记符,同机器指令一一对应 |
| (2)每一种 CPU 都有自己的汇编指令集 |
| (3)CPU 可以直接使用的信息在存储器中存放 |
| (4)在存储器中指令和数据没有任何区别,都是二进制信息 |
| (5)存储单元从零开始顺序编号 |
| (6)一个存储单元可以存储 8 个bit,即 8 位二进制数 |
| (7)1 Byte = 8 bit、1 KB = 1024 B、1 MB = 1024 KB、1 GB = 1024 MB |
| (8)每一个 CPU 芯片都有许多管脚,这些管脚和总线相连,也可以说,这些管脚引出总线 |
| 一个 CPU 可以引出 3 种总线的宽度标志了这个 CPU 的不同方面的性能 |
| 地址总线(AB)的宽度决定了 CPU 的寻址能力 |
| 数据总线(DB)的宽度决定了 CPU 与其他器件进行数据传送时的一次数据传送量 |
| 控制总线(CB)的宽度决定了 CPU 对系统中其他器件的控制能力 |
| |
| 内存地址空间 |
| 最终运行程序的是 CPU,我们用汇编语言编程的时候,必须要从 CPU 的角度考虑问题 |
| 对 CPU 来讲,系统中的所有存储器中的存储单元都处于一个统一的逻辑存储器中,它的容量受 CPU 寻址能力的限制 |
| 这个逻辑存储器即是我们所说的内存地址空间 |
| 对于初学者,这个概念比较抽象,我们在后续的课程中将通过一些编程实践,来增加感性认识 |

2、寄存器
| (1)段地址在 8086CPU 的段寄存器中存放,当 8086CPU 要访问内存时,由段寄存器提供内存单元的段地址 |
| 8086CPU 有 4 个段寄存器,其中 CS 用来存放指令的段地址 |
| (2)CS 存放指令的段地址,IP 存放指令的偏移地址,8086 机中,任意时刻,CPU 将 CS:IP 指向的内容当作指令执行 |
| (3)8086CPU 的工作过程 |
| 1、从 CS:IP 指向的内存单元读取指令,读取的指令进入指令绥冲器 |
| 2、IP = IP + 所读取指令的额长度,从而指向下一条指令 |
| 3、执行指令(转到步骤 1,重复这个过程) |
| (4)8086CPU 提供转移指令修改 CS、IP 的内容 |
| 这本书基于 8086 讲寄存器,事实上 8088 也是一样的,都是 16 位 |
| 在这里我用 80386,也叫 i386,它是 32 位的 |
| |
| EAX、EBX、ECX、EDX、ESI、EDI、EBP、ESP |
| CS、DS、SS、ES、FS、GS |
| EIP、EFLAGS |
| |
| b 字节 8 |
| w 字 16 |
| l 双字 32 |
| q 四字 64 |





3、内存访问
| (1)字在内存中存储时,要用两个地址连续的内存单元来存放,字的低位字节存放在低地址单元中,高位字节存放在高地址单元中 |
| (2)用 mov 指令访间内存单元,可以在 mov 指令中只给出单元的偏移地址,此时,段地址默认在 DS 寄存器中 |
| (3)[address]表示一个偏移地址为 address 的内存单元 |
| (4)在内存和寄存器之间传送字型数据时,高地址单元和高 8 位寄存器、低地址单元和低 8 位寄存器相对应 |
| (5)mov、add、sub 是具有两个操作对象的指令,jmp 是具有一个操作对象的指令 |
| |
| 8086 的入栈和出栈操作都是以字为单位进行的 |
| (1)8086CPU 提供了栈操作机制 |
| 在 SS、SP 中存放栈顶的段地址和偏移地址,提供入栈和出栈指令,它们根据 SS:SP 指示的地址,按照栈的方式访问内存单元 |
| (2)push 指令的执行步骤 |
| 1、SP = SP - 2 |
| 2、向 SS:SP 指向的字单元中送入数据 |
| (3)pop 指令的执行步骤 |
| 1、从 SS:SP 指向的字单元中读取数据 |
| 2、SP = SP + 2 |
| (4)任意时刻,SS:SP 指向栈顶元素 |
| (S)8086CPU 只记录栈顶,栈空间的大小我们要自己管理 |
| (6)用栈来暂存以后需要恢复的寄存器的内容时,寄存器出栈的顺序要和入栈的顺序相反 |
| (7)push、pop 实质上是一种内存传送指令,注意它们的灵活应用 |
| |
| 段的综述 |
| 我们可以将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元,这完全是我们自己的安排 |
| 我们可以用一个段存放数据,将它定义为 “数据段” |
| 我们可以用一个段存放代码,将它定义为 “代码段” |
| 我们可以用一个段当作栈,将它定义为 “栈段” |
| 我们可以这样安排,但若要让 CPU 按照我们的安排来访问这些段,就要 |
| 1、对于数据段,将它的段地址放在 DS 中,用 mov、add、sub 等访问内存单元的指令时,CPU 就将我们定义的数据段中的内容当作数据来访问 |
| 2、对于代码段,将它的段地址放在 CS 中,将段中第一条指令的偏移地址放在 IP 中,这样 CPU 就将执行我们定义的代码段中的指令 |
| 3、对于栈段,将它的段地址放在 SS 中,将栈顶单元的偏移地址放在 SP 中 |
| 这样 CPU 在需要进行栈操作的时候,比如执行 push、pop 指令等,就将我们定义的栈段当作栈空间来用 |
| 可见,不管我们如何安排 |
| CPU 将内存中的某段内容当作代码,是因 CS:IP 指向了那里 |
| CPU 将某段内存当作栈,是因为 SS:SP 指向了那里 |
| 我们一定要清楚,什么是我们的安排,以及如何让 CPU 按我们的安排行事 |
| 一段内存,可以既是代码的存储空间,又是数据的存储空间,还可以是栈空间,也可以什么也不是 |
| 关键在于 CPU 中寄存器的设置,即 CS:IP、SS:SP、DS 的指向 |
4、推荐学习
4.1、Intel 开发手册
Intel 开发手册
| 卷一和卷三需要细看,卷二和卷四用来当字典用 |
| |
| Volume 1:基础架构 |
| Chapter 3 基本执行环境 |
| Chapter 6 过程调用、中断和异常 |
| |
| Volume 3:系统编程指南 |
| Chapter 2 系统架构概述 |
| Chapter 3 保护模式内存管理 |
| Chapter 4 分页 |
| Chapter 5 保护 |
| Chapter 6 中断和异常处理 |
| Chapter 7 任务管理 |
| Chapter 8 多处理器管理 |
| Chapter 9 处理器管理和初始化 |
| Chapter 10 高级可编程中断控制器(APIC) |
| Chapter 11 内存缓存控制 |
4.2、Linux 内核完全注释
Linux 内核完全注释 V5.0
| 建议阅读《Linux 内核完全注释 V5.0》第四章 80X86 保护模式及其编程 |
| 对学习汇编、C、操作系统有很大帮助 |
5、cmpxchg
| CAS:lock; cmpxchg xxx, xxx; |
| cmpxchg 内存中的值,修改后的新值(AL、AX、EAX、RAX 中为期望值) |
| 1、若内存中的值 == 期望值:内存中的值 = 修改后的新值,ZF = 1 |
| 2、若内存中的值 != 期望值:期望值 = 内存中的值,ZF = 0 |
| |
| 保证线程安全的两种方式:1、上锁 2、无锁(单线程处理某个事情) |
| cmpxchg cx, dx |
| |
| ax = 2300H |
| cx = 2300H |
| dx = 2400H |
| |
| cx == ax |
| cx = dx = 2400H, ZF = 1 |
| cmpxchg cx, dx |
| |
| ax = 2500H |
| cx = 2300H |
| dx = 2400H |
| |
| cx != ax |
| ax = 2300H, ZF = 0 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步