基础知识
机器语言
- 机器语言是机器指令的集合
- 机器指令是一台机器可以正确执行的命令
- 机器指令由一串二进制数表示
汇编语言
- 汇编语言的主体是汇编指令
- 汇编指令是机器指令便于记忆的书写格式
- 汇编指令是机器指令的助记符
汇编语言的组成
- 汇编指令:机器码的助记符,有对应的机器码
- 伪指令::没有对应的机器码,由编译器执行,计算器并不执行
- 其他符号:如 + 、- 、* 、/ 等,由编译器识别,没有对应的机器码
汇编语言的种类
- 8086汇编(8086处理器是16bit的CPU)
- Win32汇编
- Win64汇编
- AT&T汇编(Mac、iOS模拟器)
- ARM汇编(嵌入式、iOS真机)
汇编无法得到高级语言,因为不同高级语言在汇编上是相同的
总线
总线是一根根导线的集合
每一个CPU新片都有许多管脚,这些管脚和总线相连,CPU通过总线和外部器件进行交互。
总线的分类
地址总线:寻址,找到地址对应的存储空间
地址总线决定了CPU的寻址能力,8086地址总线宽度是20,所以它的寻址能力是1M $(2^{20})$
寻址能力的计算:首先明白总线就是导线,导线能够传递的是电信号,电信号分为两种:高电平信号、低电平信号,高电平信号即 1,低电平信号即 0。假如总线总线的宽度是 3 ,那么 3 根导线高电平为 1 ,低电平为 0 ,它们最大能够传递的值只有 $2^3$ 种:000,001,010,011,100,101,110,111。
数据总线:传递,CPU和内存之间传递具体数据
数据总线决定了CPU单次数据的传送量,也就是数据传送的速度。8086的数据总线宽度是16,所以单次最大能够传递2个字节的数据。
单次数据传送量的计算:数据总线的宽度是16,同地址线一样,16根线代表16位0或1,即16位二进制数据,一次最多能够传送16个二进制位。一个字节是8位,16位即2个字节。所以8086单次能够传递的最大数据量就是2个字节。
8088的数据总线宽度是8,8086的数据总线宽度是16,分别向内存中写入89D8H时(89D8H即16进制的89D8,汇编语言中末尾加H代码16进制)。一个16进制代表4个二进制位,两个16进制代表8个二进制位即1个字节,四个16进制即2个字节。因为8088数据线宽度是8,一次只能传递一个字节,所以8088传递89D8H需要传2次,第一次传D8,第二次传89。而8086只需要一次就能够将89D8传递完成。
控制总线:控制,告诉内存需要进行读还是写操作
控制总线决定了CPU的控制能力,代表CPU有多少种控制能力。
CPU从内存中读取数据的步骤
- CPU通过 地址线 找到需要读取数据的地址
- 通过 控制线 告诉内存进行读取操作
- 内存通过 数据线 返回数据给CPU
CPU 如何控制外设
CPU通过总线向接口卡发送命令,接口卡根据命令控制外设工作
内存
所有的内存单元都有唯一的地址,这个地址叫做物理地址。
8086CPU的地址总线是20根,那么它能够访问的内存空间的地址值范围即 0x00000 - 0xFFFFF(上面已经说明过,一个16进制位=4个二进制位),通过这个范围可以定位 $2^{20}$ 个不同的内存单元,所以8006的内存空间大小为1M。

- 0x00000 - 0x9FFFF:主存储空间,可读可写
- 0xA0000 - 0xBFFFF:显存地址空间,数据显示在显示器上
- 0xC0000 - 0xFFFFF:ROM,只读
8086的寻址方式
上面提到8086的地址总线宽度为20,寻址能力为1M,但是实际上8086是一个16位架构的CPU,它内部能够一次性处理、传输、暂存的数据只有16位。这就意味这8086实际上只能够直接送出16的地址,但是它的地址总线宽度又是20位,意味这这样就有4位是无法使用的,它的实际寻址能力只能够是64KB。那么它是如何做到实现1M的寻址能力呢,具体步骤如下:
- CPU中的相关部件提供两个16的地址,一个成为段地址,一个成为偏移地址。
- 段地址和偏移地址通过内部总线送入地址加法器。
- 地址加法器将两个16位地址合成一个20位的物理地址。
- 地址加法器通过内部总线将20位物理地址送入输入输出控制电路。
- 输入输出控制电路将20位物理地址送入地址总线。
- 20位的物理地址被地址总线送到内存。
字节与字
汇编语言没有数据类型的概念,它是直接操作内存的,汇编语言的数据存储单位有两个:
- 字节:byte,1个字节由8bit组成,可以存储在8位寄存器中。
- 字:word,1个字由2个字节组成,这两个字节分别成高字节和低字节。
寄存器
寄存器是CPU非常重要的部件,可以通过改变寄存器的值来实现对程序的控制。不同CPU的寄存器个数和结构一般都不相同,下面是8086CPU寄存器的结构,8086CPU有14个寄存器,所有寄存器都是16位的。
字在寄存器中的存储
在CPU中,16位寄存器存储一个字,高八位存储高位字节,低八位存储地位字节
在内存中,字的地位字节存放在低地址单元,高位字节存放在高地址单元
数据寄存器
数据寄存器由AX(Accumulator Register)、BX(Base Address Register)、CX(Count Register)、DX(Data Register)组成,虽然上图里边每个每一个寄存器都分成了两块,但它依然是一个寄存器。
由于8086之前的CPU是8位的架构,所以8086为了兼容8位的程序,每个16位数据寄存器都可以当作两个单独的8位寄存器来使用。AX寄存器可以分成两个独立的8位寄存器,高8位为AH,低8位为AL。BX、CX、DX同理。
除了四个数据寄存器之外,其它的寄存器均不可以分为两个独立的8位寄存器。独立的意思是:当AH和AL做为8位寄存器使用时,可以看作它们是互不相关的,形式上可以看作两个完全独立的寄存器。
既然数据寄存器可以当作两个独立的寄存器,那么它们既可以用整个寄存器的16位存放一个数据,也可以高8位和低8位分别存放一个数据共存放两个数组。
AX(accumulate)
AX 作为累加器用,所以它是算术运算的主要寄存器。在乘、除等指令中指定用来存放操作数。
另外,所有的I/O指令都使用这一寄存器与外部设备传送信息。
BX(base)
BX 在计算存储器地址时,它经常用作基址寄存器。
CX(count)
CX 常用来保存计数值,如在移位指令、循环和串处理指令中用作隐含的计数器。
DX(data)
DX 一般在作双字长运算时把DX和AX组合在一起存放一个双字长数,DX用来存放高位字。
此外,对某些I/O操作,DX可用来存放I/O的端口地址。
段寄存器(Segment Register)
前面关于8086的寻址方式里边提到,8086需要16位的段地址和偏移地址合成20位地址,其中的段地址就由段寄存器提供。段寄存器一共有四个,每个段寄存器的作用都不相同。
CS 代码段寄存器(Code Segment Register)
CS和IP配合使用,它们指示了CPU当前要读取指令的地址。任何时候,8086CPU都会将CS:IP指向的指令做为下一条需要取出执行的指令。
指令执行的过程:
- 从CS:IP指向的代码段内存单元读取指令,读取的指令进入指令缓冲器。
- IP = IP+读取指令的长度,进而可以读取下一条指令。
- 返回步骤1。
在内存或者磁盘上中,指令和数据没有任何区别,都是二进制信息。 CPU在工作时,有时候把信息当作指令,有时候看作数据,同样的信息赋予不同的意义。
CPU根据什么将内存中的数据信息当作指令? 通过CS:IP指向的内存单元内容看作指令。
DS 数据段寄存器(Data Segment Register)
DS是用来操作内存时提供段地址的,假如需要将内存中10000H 存入1122H,直接这样写是不可以的:因为汇编语言又如下要求:
- 不能直接给内存地址赋值,必须通过DS:[偏移地址]指向内存。
- 不能直接给DS赋值,需要通过寄存器中转
正确做法是:- mov ax,1000H
- mov ds,ax
- mov [0H],1122H
复制代码 SS 堆栈段寄存器(Stack Segment Register)
配合SP使用,SS:SP指向栈顶元素
其他寄存器
SI 源变址寄存器(Source Index Register)
一般与DS联用,用来确定数据段中某一单元的地址
与自动增量和自动减量的功能,方便用于变址
DI 目的变址寄存器(Destination Index Register)
与SI相同
BP 基址指针寄存器(Base Pointer Register)
可以作为堆栈区中的一个基地址以便访问堆栈中的信息
8086专用寄存器
IP 指令指针寄存器(Instruction Pointer Register)
它用来存放代码段中的偏移地址。在程序运行的过程中,它始终指向下一条指令的首地址,它与段寄存器CS联用确定下一条指令的物理地址。当这一地址送到存储器后,控制器可以取得下一条要执行的指令,而控制器一旦取得这条指令就马上修改IP的内容,使它指向下一条指令的首地址。
可见,计算机就是用IP寄存器来控制指令序列的执行流程的,因此IP寄存器是计算机中很重要的一个控制寄存器。
SP 堆栈指针寄存器(Stack Pointer Register)
存放栈顶的偏移地址
FLAGS 标志寄存器 / PSW 程序状态寄存器(Program Status Word Register)
存放条件码标志,控制标志和系统标志
条件码标志
条件码标志用来记录程序中运行结果的状态信息,它们是根据有关指令的运行结果由CPU自动设置的。由于这些状态信息往往作为后续条件转移指令的转移控制条件,所以称为条件码。它包括以下6位:
- OF 溢出标志(overflow flag)
- 在进行有符号数运算过程中,如操作数超出了机器能表示的范围称为溢出。
- 有溢出,OF = 1 OV
- 无溢出,OF = 0 NV
- SF 符号标志(sign flag)
- 将结果视为有符号数
- 记录运算结果的符号(符号位)
- 符号位为1时(负数),SF = 1 NG
- 符号位为0时(非负),SF = 0 PL
- ZF 零标志(zero flag)
- 指令的运算结果是否是0
- 结果是 0 ,ZF = 1 ZR
- 结果不是0,ZF = 0 NZ
- CF 进位标志(carry flag)
- 进行无符号数运算时从最高有效位产生了进位值,或从更高位产生了借位值
- 有进位或借位,CF = 1 CY
- 无进位或借位,CF = 0 NC
- PF 奇偶标志(parity flag)
- 用来为机器中传送信息时可能产生的代码出错情况提供检验条件。
- 记录指令执行后结果所有二进制位中 1 的个数
- 1的个数为偶数,PF = 1 PE
- 1的个数为奇数,PF = 0 PO
- AF 辅助进位标志(auxiliary carry flag)
控制标志
控制标志位为方向标志(direction flag,DF),在串处理指令中控制处理信息的方向用。
- 当DF位为1时,每次操作后使变址寄存器SI和DI减小,这样就使串处理从高地址向低地址方向处理。
- 当DF位为0时,则使SI和DI增大,使串处理从低地址向高地址方向处理
系统标志
系统标志位可以用于I/O、可屏蔽中断、程序调试、任务切换和系统工作方式等的控制。一般应用程序不必关心或修改这些位的状态,只有系统程序员或需要编制低层I/O 设备控制等程序时才需要访问其中的有关位。
- 陷阱标志(trap flag,TF),用于调试时的单步方式操作。当TF位为1时,每条指令执行完后产生陷阱,由系统控制计算机;当TF位为0时,CPU正常工作,不产生陷阱。
- 中断标志(interrupt flag,IF),当IF位为1时,允许CPU响应可屏蔽中断请求,否则关闭中断。
- I/O特权级(I/O privilege level,.IOPI),在保护模式下,用于控制对I/O地址空间的访问。

规范与约定
[...]
(汇编语法规定)表示一个内存单元
一个内存单元的描述:
(...)
(为了学习方便规定)表示一个内存单元或寄存器中的内容
idata
表示常量,某个字符
访问内存
为了访问内存,我们可以使用这四个寄存器:BX,SI,DI,BP
我们可以使用如下方式访问对应地址:

除了BP寄存器,其他寄存器默认均使用DS寄存器,BP使用SS寄存器

BP不能和BX一起作为偏移地址,SI不能和DI一起作为偏移地址
段寄存器中的值成为 段地址 ,通用寄存器中的值称为 偏移地址
类型
为了告诉编译器数据类型,需要使用以下前缀:
byte ptr
字节
word ptr
字
MOV 指令
mov 指令将第二个操作数(源)复制到第一个操作数上(目标)
- 源操作数可以是立即数,通用寄存器或内存地址
- 目标操作数可以是通用寄存器或内存地址
- 两个操作数必须是同一数据类型
MOV 指令支持以下指令形式:
- MOV REG,memory
- MOV memory,REG
- MOV REG,REG
- MOV memory,immediate
- MOV REG,immediate
REG:AX,BX,CX,DX,AH,AL,BH,BL,CH,CL,DH,DL,DI,SI,BP,SP
memory:[BX],[BX+SI+7],variable
immediate: 5, -24, 3Fh, 10001101b
对于段寄存器只有以下指令可以使用:
- MOV SREG (CS) ,memory
- MOV memory,SREG
- MOV,REG,SREG
- MOV SREG,REG
SREG: DS, ES, SS, and only as second operand: CS.- ; 假设内存10000H原始值: 1122H
- ; 8086是小端模式,高字节放在高地址,低字节放在低地址
- ; 1000:0000 22
- ; 1000:0001 11
- ; 准备修改10000H位置的值
- mov ax, 1000H
- mov ds, ax
- ; 1000:0000 66
- ; 1000:0001 11
- ; 修改后10000H: 1166H
- mov [0], 66h
- ; 1000:0000 66
- ; 1000:0001 11
- ; 修改后10000H: 1166H
- mov byte ptr [0], 66h
- ; 1000:0000 66
- ; 1000:0001 00
- ; 修改后10000H: 0066H
- mov word ptr [0], 66h
复制代码 寻址方式
当数据存放在内存中的时候,我们可以用多种方式来给定这个内存单元的偏移地址,这种定位内存单元的方法一般称为寻址方式。
数据有关的寻址方式
隐含寻址
隐含寻址就是指令中不指明操作数,但隐含在操作码中。如乘法指令(MUL src)
立即数寻址(immediate addressing)
操作数直接包含在指令中,紧跟在操作码之后的寻址方式称为立即寻址方式,把该操作数称为立即数。
- 可以是8位数或16位数
- 常用来给寄存器赋值,不执行总线周期,速度快
- 只能用于源操作数
例:
MOV AL,2CH
MOV AX,2C40H
寄存器寻址
操作数包含在CPU内的某个寄存器中,指令直接给出寄存器名。
- 对16位的操作数,寄存器可以是:AX,BX,CX,DX和SI,DI,SP,BP
对8位的操作数,寄存器可以是:AL,BL,CL,DL和AH,BH,CH,DH
- 源操作数和目的操作数都可用
- 不执行总线周期,执行速度快
例:
INC CX
MOV AX,BX
存储器寻址
除以上三种寻址方式外,以下各种寻址方式的操作数都在存储器中,其操作数称为存储器操作数。
由于80X86对内存采用分段管理,因此由以下寻址方式得到的只是有效地址(简写为EA-effective address,在IBM PC中就是操作数地址的偏移量部分)。
有效地址可以由以下四种成分组成:
- 位移量(displacement)是存放在指令中的一个8位、16位或32位的数,它是一个地址。
- 基址(base)是存放在基址寄存器中的内容。通常用来指向数据段中数组或字符串的首地址。
- 变址(index)是存放在变址寄存器中的内容。通常用来访问数组中的某个元素或字符串中的某个字符。
EA = 基址 + 变址 + 位移量
直接寻址
操作数的有效地址直接包含在指令中的寻址方式。
有效地址存放在代码段的指令操作码之后,但操作数本身在存储器中,所以必须先求出操作数的物理地址。这种寻址方式常用于存取简单变量。
- 如果没有指定段超越前缀默认操作数在数据段
- 编译后的程序与debug中表现不同,会将[idata]直接转换为idata,因此需要加上段前缀
例:
MOV AL, [1400H]
由于在汇编语言中用符号表示地址,所以指令“MOV AL,VAR”中的源操作数寻址方式是直接寻址,有时也写做“MOV AL,[VAR]”
寄存器间接寻址
操作数的有效地址在基址寄存器BX、BP或变址寄存器SI、DI中,而操作数在存储器中的寻址方式
- 能用作间址寄存器的寄存器:BX, BP, SI, DI
- BP为间址寄存器时,操作数在堆栈段中,其余为数据段
例:
MOV AX, [DI]
寄存器相对寻址
也称为直接变址寻址方式。操作数的有效地址是一个基址(BX、BP)或变址(SI、DI)寄存器的内容和指令中给定的一个位移量(disp)之和。有效地址由2部分组成。
- EA = (基址/变址寄存器) + 0/8/16位位移量
- 段地址对应BX/SI/DI寄存器默认是DS,对应BP寄存器默认是SS,可用段超越前缀改变。
例:
MOV AX, [DI+0100H]
基址变址寻址
操作数的有效地址是一个基址寄存器(BX、BP)和一个变址寄存器(SI、DI)的内容之和。缺省使用段寄存器的情况由基址寄存器决定。
- EA=BX/BP+SI/DI
- 段基址对应BX基址寄存器默认是DS,对应BP基址寄存器默认是SS,可用段超越前缀改变。
例:
MOV AX, [BX] [SI] ← → MOV AX, DS: [BX+SI]
相对基址变址寻址
操作数的有效地址是一个基址和一个变址寄存器的内容和指令中给定的一个位移量之和。有效地址由三部分组成。缺省使用段寄存器的情况由基址寄存器决定。
- EA=BX/BP+SI/DI+8/16位位移量
- 段地址对应BX基址寄存器默认是DS,对应BP基址寄存器默认是SS,可用段超越前缀改变。
例:
MOV AX, 06H[BX+SI] ← → MOV AX, DS:[BX+SI+06H]
与转移地址有关的寻址方式
这种寻址方式用来确定转移指令及CALL指令的专项地址

非考点,略
变量
变量是一种内存地址
8086的汇编器支持两种类型的变量:BYTE 和 WORD
声明一个变量的方式如下- name DB value ;DB 表示字节 Define Byte
- name DW value ;DW 表示单字 Define Word
- name DD value ;DD 表示双字 Define Double(Word)
- ; name 可以是任何字符和/或数字的组合,但是它必须以一个字符开始
- ; 可以声明一个未命名的变量(变量会拥有一个地址,但是没有名字)
- ;value 可以是任何数字或者是 '?' 表示未初始化
复制代码 数组
数组可以被视为一系列变量,下面是一些数组声明的例子:- a DB 48h, 65h, 6Ch, 6Ch, 6Fh, 00h
- b DB 'Hello', 0
复制代码
你可以通过方括号来访问数组中的任何元素你还可以使用任何内存索引寄存器 BX,SI,DI,BP 来访问其中的元素DUP
如果需要声明一个一个大数组,可以使用DUP操作符
number DUP (value(s))
number —— 需要声明的复制(任何常数)
value —— 将要复制的值
如果你想要声明一个比255更大或比-128更小的值,你可以使用DW取代DB。注意:DW 不能被用来声明字符串
获得变量的地址(LEA,OFFSET)
你可以通过两种方式获得变量的地址:LEA(Load Effective Address)指令和它的替代品 OFFSET 操作符
LEA 同时还能让你获得索引变量的地址。
LEA和OFFSET最后都会被编译为同一种机器语言:
MOV BX,num
num是一个16进制的偏移量
注意:只有以下寄存器可以被用在方括号内(作为内存指针):BX,SI,DI,BP
LEA
<ul>格式:LEA REG,SRC
操作:(REG) |