tags: ASM

写在前面

小结一下最近看的汇编语言王爽(第三版)的寄存器,内存访问与基本汇编指令部分的内容. 其实学习操作系统, 熟悉一些基本的汇编语法即可, 但是要想读懂后面的程序链接等内容还是要好好研究一下汇编的实现细节. 毕竟这是探寻计算机底层的最好的工具.

下面的讨论均基于Intel的8086芯片. 寄存器部分参考​​1​​.

寄存器(register)

分类(共14个)

  • 通用寄存器: ​​AX​​​, ​​BX​​​, ​​CX​​​, ​​DX​​​, 用于存放一般的数据, 每一个寄存器都是16位的(下同), 可以分成高位和低位两种情况(例如​​AX=AH+AL​​​, 这里面的​​AH​​​, ​​AL​​等均为8位寄存器)
  • 段(Segment)寄存器:
  • 代码段寄存器(Code segment register): ​​CS​​, 控制待执行的机器指令.
  • 数据段寄存器(Data segment register): ​​DS​​, 存放要访问的数据的段地址.
  • 栈段寄存器(Stack segment register): ​​SS​​, 存放栈顶地址.
  • 额外段寄存器(Extra segment register): ​​ES ​​, 用于控制显存等信息.
  • 指针(Pointer)寄存器:
  • 指令指针寄存器(instructor pointer register): ​​IP​​​, 指令偏移地址, 与​​CS​​​合用, ​​CS:IP​​指向待执行的下一条指令.
  • 栈指针寄存器(Stack pointer register): ​​SP​​​, 栈的偏移地址, 与​​SS​​​合用, ​​SS:SP​​指向栈顶元素.
  • 栈基指针寄存器(Stack Base pointer register): ​​BP​​,
  • 索引(Index)寄存器:
  • 源索引寄存器(Source index register): ​​SI​​,
  • 目的索引寄存器(Destination index register): ​​DI​​,
  • 程序状态字(Program status word): ​​PSW​​, 执行状态寄存器和程序计数器功能的寄存器​2​​.

寄存器中数据的存储

  • 字节(byte), 一个字节=8个bit(位)
  • 字(word), 一个字=2字节

寻址(物理地址=基址+偏移)

8086CPU(硬件结构仅为16位)有20位地址总线, 所以单次可以传输​​2^20=1,048,576=1MB​​​的数据, 达到1MB寻址能力. 下面的式子展示了为什么16位芯片可以达到20位寻址能力:
汇编语言(王爽)基本指令,寄存器,内存访问部分(chap2,3)小结_汇编

指令寻址(CS:IP)

使用8086CPU的机器, 任意时刻, CPU将从内存的​​CS<<4+IP​​单元开始取指, 执行. 基本步骤:

  1. 从​​CS:IP​​指向的内存单元读取指令, 读取到的指令进入指令缓冲器.
  2. ​IP+=所读取指令的长度​​, 从而指向下一条指令.
  3. 执行指令,转步骤(1), 重复.

不能通过​​mov​​​修改​​CS:IP​​​指向的指令, 可以通过​​jmp​​指令来完成.

栈顶寻址(SS:SP)

基本汇编指令

这里就进入汇编的基本语法部分了, 和高级语言相同, 汇编也是由很多语句构成的, 其中最常用的就是​​mov​​​, 可以类比一下C语言中的赋值运算符​​=​​.

16位汇编中的语句不区分大小写, 这里的语句都以小写形式给出, 但是写汇编源文件的时候

mov(赋值)

格式为:​​mov destination, source​​​, 即​​mov 目的操作数, 源操作数​​, 下面是汇编语句通过高级语言的描述:

数字均为16进制(HEX), 在DEBUG程序中写入指令的时候不需要加数后面的H, 但是在汇编源程序文件中需要加上后缀的H以区分十进制和十六进制.

汇编指令

含义

高级语言描述

​mov ax, 18​

将18送入​​AX​​寄存器

​AX=18​

​mov ah, 18​

将18送入​​AX​​​寄存器的高8位寄存器​​AH​

​AH=18​

​mov ax, bx​

将​​BX​​​寄存器中的数据送入​​AX​​寄存器

​AX=BX​

针对内存访问

通过数据段寄存器​​DS​​​和​​[]​​​操作符完成, 语法:​​mov ax, [0]​​​, 表示将​​DS:0​​​位置的数据送入​​AX​​寄存器.

不能对​​DS​​​/​​CS​​寄存器直接进行赋值, 需要借助通用寄存器中转:

mov ax, 1000
mov ds, ax

但是可以直接从​​DS​​​或​​CS​​​寄存器中读取值并写入​​AX​​寄存器或写入内存单元.

mov ax, cs
mov [0], cs

add,sub(加/减)

可以类比C语言的​​+=,-=​​​操作符, 其格式为: ​​add/sub destination, source​​.

汇编指令

含义

高级语言描述

​add ax, 18​

将​​AX​​​寄存器中的数据加上18后送入​​AX​​寄存器

​AX+=18​

​add ah, 18​

将​​AH​​​寄存器中的数据加上18后送入​​AH​​寄存器

​AH+=18​

​add ax, bx​

将​​AX​​​寄存器中的数据加上​​BX​​​中数据后送入​​AX​​寄存器

​AX+=BX​

需要注意对通用寄存器或者通用寄存器的高位/低位寄存器操作的时候也会存在数据的舍入的情况, 例如:

mov ax, ffff add ax, 1 ; 这里AX 寄存器实际的值为0000, 因为FF+FF=10000, 高位截断 mov al, ff add al, ff ; 这里AL 寄存器实际的值为FE, 因为FF+FF=1FE, 高位截断

针对内存访问

可以执行内存单元\通用寄存器\立即数(字面值)之间的增减, 但是不能执行到段寄存器的增减和两内存单元之间的增减.

add ax, 8
add ax, [0]
add [0], ax
; add [0], [0] ; error
; add ds, ax ; error

jmp(指令跳转)

语法:

  • ​jmp 段地址:偏移地址​​,
  • ​jmp 寄存器名​​​(表示用寄存器中的数据作为待跳转的​​IP​​​值), 可以理解为​​mov IP, ax​​.

push,pop(压栈,出栈)

语法: ​​push AX​​​, 表示将​​AX​​​寄存器的数据压入栈中, ​​pop AX​​​, 将栈顶元素弹出, 写入​​AX​​寄存器.

对内存单元也有类似的操作.

​push​​的执行步骤:

  1. ​SP-=2​​​, ​​SS:SP​​指向当前栈顶前面的内存单元, 以当前栈顶前面的内存单元作为新的栈顶.
  2. 将​​AX​​​中数据送入​​SS:SP​​​指向的内存单元处, ​​SS:SP​​此时指向新的栈顶.

​pop​​正好相反:

  1. 将​​SS:SP​​​指向的内存单元处的数据存入​​AX​​寄存器.
  2. ​SP+=2​​​, ​​SS:SP​​指向当前栈顶下面的内存单元, 以当前栈顶下面的单元作为新的栈顶.

ref


  1. ​x86 Registers (utoronto.ca)​​; ↩︎
  2. ​Program status word - Wikipedia​​; ↩︎