Assembley_language
1.第一章基础知识
1.1.机器语言
机器语言就是机器指令的集合,机器指令展开来讲就是一台机器可以正确执行的命令
1.2.汇编语言的产生
汇编指令和机器指令的差别在于指令的表示方法上,汇编指令是机器指令便于记忆的书写格式
操作 :寄存器BX内容送到AX中
机器指令:1000100111011000
汇编指令:mov ax,bx
因为计算机只能读懂机器指令,需要使用编译器将汇编指令编译成机器指令

1.3.存储器
就是我们平常所说的内存
1.4.存储单元
存储器会被划分若干个存储单元
1.5.CPU对存储器的读写
通过地址总线,数据总线,控制总线对存储芯片中的数据进行读写
1.6.主板
每一台PC中,都有一个主板,通过地址总线,数据总线,控制总线将核心器件和主要器件进行相连
1.7.接口卡
计算机系统中,所有可用程序控制其工作的设备,必须收到CPU控制,CPU对外部设备都不能直接控制,比如显示器,音响,打印机等,直接控制这些设备进行工作的是插在扩展插槽上的接口卡,扩展插槽通过总线和CPU相连,接口卡也通过总线和CPU进行相连。CPU通过总线向接口卡发送命令,接口卡根据CPU的命令控制外设进行工作
1.8.各类存储器芯片
一个PC中装有多个存储器芯片,从读写属性中分为
- RAM(随机存储器):必须带电存储,关机后存储的内容丢失
- 第一种:存放CPU和绝大部分程序和数据,主随机存储器一般由两个位置上的RAM组成,装在主板上的RAM和装在扩展槽上的RAM
- 第二种:接口卡上的RAM:某些接口需要对大批量数据,数据数据进行暂时存储,在其上安装RAM,最典型的就是显卡上的RAM,称之为显存,将要写入的内容写入显存中,显示卡随时将显存的数据在显示器上输出
- ROM(只读存储器):只能读取,不能写入,关机之后内容不丢失
- 装有BIOS(Basic Input/Output System,基本输入/输出系统)的ROM,BIOS 是主板和各类接口卡(如显卡,网卡等)厂商提供的软件系统,可以通过它利用该硬件设备进行最基本的输入和输出。例如:主板的ROM中存储的主板的BIOS(系统BIOS),显卡上存储着显卡的BIOS,网卡上安装网卡的BIOS

1.9.内存地址空间
上述的那些存储器,在物理上是独立的器件,但是一些两点比较相同
- 都和CPU总线进行相连
- CPU对它进行读写时候都是通过控制线发出内存读写命令

在上图中,所有的物理存储器被看作一个有若干个存储单元组成的逻辑存储器,每个物理存储器在这个逻辑存储器中占有一个地址段,即一段地址空间,CPU在在这段地址空间读写数据,实际上就是在相对应的物理存储器上读写数据
1.10.总结
总结:
- 汇编指令是机器指令的助记符,同机器指令一一对应
- 每一种CPU都有自己的汇编指令集
- CPU可以直接使用的信息在存储器中存放
- 在存储器中指令和数据没有任何区别,都是二进制信息
- 存储单元从零开始顺序编号
- 一个存储单元可以存储8个bit,即8位二进制数
- 1Byte=8bit 1KB=1024B 1MB=1024KB 1GB=1024MB 类比:2^10 1KB 2^20 1MB 2^30 1GB
- 每一个CPU芯片都有许多管脚,这些管脚和总线进行相连,也可以说,这些管脚引出总线。CPU引出3种总线的宽度标志这个CPU的不同方面的性能
- 地址总线宽度决定了CPU的寻址能力
- 数据总线的宽度决定了CPU和其它器件进行数据传送时的一次数据传送量
- 控制总线宽度决定了CPU对系统中器件的控制能力
- 内存地址空间:使用汇编语言编程时候,必须从CPU的角度考虑问题,对CPU来说,系统中的所有存储器中的存储单元都处于一个统一的逻辑存储器中,它的容量受CPU的寻址能力限制,这个逻辑CPU就是我们所说的内存地址空间
习题:

(1)1个CPU的寻址能力为8KB,那么它的地址总线的宽度为 13位。
答:8 KB=8*1024 Byte=2^3^*2^10^=2^13^ Byte 所以13位
(2)1KB的存储器有 1024 个存储单元,存储单元的编号从 0 到 1023 。
答:一个存储字长为8 bit,1 KB=1024 Byte=1024 * 8 bit 有1024 存储单元
(3)1KB的存储器可以存储 8192(2^13) 个bit, 1024个Byte。
答:1 KB=1024 Byte=1024 * 8 bit
(4)1GB是 1073741824 (2^30) 个Byte、1MB是 1048576(2^20) 个Byte、1KB是 **1024(2^10)**个Byte。
(5)8080、8088、80296、80386的地址总线宽度分别为16根、20根、24根、32根,则它们的寻址能力分别为: 64 (KB)、 1 (MB)、 16 (MB)、 4 (GB)。
答:16根:2^16=2^10 * 2^6 2^10是1024就是1KB 然后乘2^6 =64 就是64KB,后面一次类推
(6)8080、8088、8086、80286、80386的数据总线宽度分别为8根、8根、16根、16根、32根。则它们一次可以传送的数据为: 1 (B)、 1 (B)、 2 (B)、 2 (B)、 4 (B)。
答:8根就是8bit=1B ,后面一次类推
(7)从内存中读取1024字节的数据,8086至少要读 512 次,80386至少要读 256 次。
答:8086 数据总线宽度为16 1024 * 8 /16 = 512 80386数据总线宽度为32 1024 * 8 /32 = 256
(8)在存储器中,数据和程序以 二进制 形式存放。
2.寄存器
一个CPU由运算器,控制器,寄存器等器件构成,这些器件靠内部总线进行相连。简单的说CPU中:
- 运算器进行信息处理
- 寄存器进行信息存储
- 控制器控制各种器件进行工作
- 内部总线连接各个器件,在他们之间进行各种数据的传送
寄存器是CPU中程序员可以用指令读写的部件,程序员可以通过改变各种寄存器的内容实现对CPU的控制
8086CPU由14个寄存器:AX,BX,CX,DX,SI,DI,SP,BP,IP,CS,SS,DS,ES,PSW
2.1.通用寄存器
所有的寄存器都是16位,可以存放两个字节。AX,BX,CX,DX 这4个通用寄存器,每一个寄存器可以分为两个可独立使用的8位寄存器来使用
- AX 可以分为AH和AL,累加器;可以与DX组合成为EAX<=>DX:AX
- CX可以分为CH和CL,计数器
- DX可以分为DH和DL,累加器扩展
- BX可以分为BH和BL,基址;可以和CX组合成为EBX<=>CX:BX
- BP基指针
- SI源变址
- DI目的变址
- SP堆栈指针
2.2.物理地址
CPU访问内存单元,要给出内存单元的地址。没一个内存单元在这个空间中都有一个唯一的地址,我们将这个唯一的地址称之为
物理地址
2.3.8086CPU给出物理地址的方法

- CPU中的相关部件提供两个16位地址,一个称之为段地址,一个称之为偏移地址
- 段地址和偏移地址通过内部总线送入一个称之为地址加法器的部件
- 地址加法器将两个16位地址合成一个20位的物理地址
- 地址加法器通过内部总线将20位物理地址送入到输入和输出控制电路
- 输入和输出控制电路将20 位物理地址送上地址总线
- 20位物理地址被地址总线传送到存储器
地址加法器采用:
物理地址=段地址*16+偏移地址[这里的16是十进制]
物理地址=段地址*(10)H+偏移地址[这里的10是十六进制]
2.4.段的概念
在编程时可以根据需要,将若干地址连续的内存单元看作一个段。偏移地址位16位,16位地址的最大寻址能力位64KB,所以要给段的长度最大位64KB
2.5.段寄存器
段寄存器:CS、DS、SS、ES
CS:代码段寄存器
DS:数据段寄存器
ES:附加段寄存器
SS:堆栈段寄存器
IP:指令指针寄存器
2.6.CS和IP
假设CS中的内容位M,IP中内容位N,8086CPU将从内存M✖16+N单元开始,读取一条指令并执行
CPU将CS:IP指向内存单元中的内容看作指令
2.7.总结
- CS 存放指令的段地址,IP存放指令的偏移地址,任意时刻,CPU将CS:IP指向内容当作指令的执行
- CPU工作的流程
- 从CS:IP指向内存单元读取指令,读取的指令进入指令的缓冲器
- IP指向下一条指令
- 执行指令

2.8.Debug
- R命令查看、改变CPU寄存器的内容
- D命令查看内存中的内容
- E命令改写内存中的内容
- U命令将内存中的机器指令翻译成汇编指令
- T命令执行一条机器指令s
- A命令已汇编指令的格式在内存中写入一条机器指令
DEBUG调试命令中的标志表示
标志 名称 标志为1 标志为0 备注 OF 溢出标志(Overflow flag) OF=1|是(OV) 否(NV) 运算结果超出机器用补码所能表示的范围,则为溢出 DF 方向标志(Direction flag) DF=1|递减(DN) 递增(UP) DF为串操作指定规定增减方向 TF 陷阱标志(Trap flag) TF=1|产生中断 未产生中断 TF供调试指令程序使用 IF 中断标志(Interrupt flag) IF=1|允许(EI) 关闭(DI) 可用开中断指令STI和关中断指令CLI设置IF的状态 SF 符号标志(Sign flag) SF=1|负(NG) 正(PL) 运算结果的正负 ZF 零标志(Zero flag) ZF=1|是(ZR) 否(NZ) 运算结果全为0则ZF置1,否则置0 AF 辅助进位标志(Auxiliary carry flag) AF=1|有(AC) 无(NA) 进行算数运算时,低半字节向高半字节产生进位(加法)或借位(减法)则AF=1,否则为0 PF 奇偶标志(Parity flag) PF=1|偶(PE) 奇(PO) 操作结果中低八位中含1的个数的奇偶性,对应1的个数为偶数则PF=1; CF 进位标志(Carry flag) CF=1|是(CY) 否(NC) 算数运算时,最高位产生进位或借位,则CF=1
3.寄存器(内存访问)
3.1.内存中的字存储
CPU中用16位寄存器存储一个字,高8位存放高位字节,低8位存放低位字节。内存单元是字节单元,一个单元存放一个字节,一个字需要使用两个连续的内存单元进行存放,这个字的低位字节存放在低地址单元,高位字节存放在高地址单元。双字需要使用四个连续的内存单元进行存放

问题:
- 0地址单元存放的字节型数据是多少?20 H
- 0地址单元存放的字型数据是多少?4E20 H
- 2地址单元存放的字节型数据是多少?12 H
- 2地址单元存放的字型数是多少?0012 H
- 1地址单元存放的字型数据是多少?124E H
- 0地址单元存放的双字型数据是多少?00124E20 H
3.2.DS和[address]
CPU 要读写一个内存单元的时候,必须给出这个内存单元的地址。内存单元的地址由段地址和偏移地址组成。DS寄存器通常需要存放访问数据的段地址
1 | mov bx,1000H |
3.3.字的传送
CPU是16位结构,有16根数据线,可以一次性传送16位数据,就是一个字

3.4.数据段

总结
- 字在内存中存储,使用两个地址连续的内存单元存放,低位字节存放在低地址单元,高位字节存放在高地址单元
- 双字在内存中存储,使用四个地址连续的内存单元存放,即两个字型数据的存储单元
- 使用mov指令访问内存单元,可以在mov指令中只给出单元的偏移地址,此时段地址默认在DS寄存器中
- [address]表示一个偏移地址位address的内存单元,例如[2]表示偏移地址为2的内存单元
- 在内存和寄存器之间传动数据的时候,高地址单元和高8位寄存器相对应,低地址单元和低8位寄存器相对应
3.5.栈
- 入栈:就是将一个新的元素放到栈顶,栈顶指针减小;
- 出栈:就是从栈顶取出一个新的元素,栈顶指针增大;
- 栈顶的元素总是LIFO(Last In First Out):“先进后出”;
3.6.CPU提供栈机制
基本命令就是PUSH和POP,push ax 表示将寄存器ax中的数据送入栈中,pop ax 表示从栈顶中取出数据送入到ax,CPU入栈和出栈操作都是以字为单位进行的。
段寄存器SS和SP,栈顶的段地址存放在SS中,偏移地址存放在SP中,任意时刻SS:SP指向栈顶元素

3.7.栈顶超解问题
CPU不会保证我们对栈的操作不会超界。CPU只知道栈顶(SS:SP)在何处,不知道安排的栈的空间由多大。所以编程的时候操心栈顶超界的问题。
3.8.栈的总结
- 在ss,sp中存放栈顶的段地址和偏移地址:提供入栈和出栈的指令,他们根据SS:SP指示的地址,按照栈的方式访问内存单元
- push指令的执行步骤:①sp=sp-2 ②向ss:sp指向的字单元中送入数据
- pop指令的执行步骤:①从ss:sp指向的字单元中读取数据②sp=sp+2
- 任意时刻,ss:sp指向栈顶元素
- CPU只记录栈顶,栈空间的大小由我们自己管理
- 用栈来暂存以后需要恢复的寄存器的内容时,寄存器出栈的顺序要和入栈的相反
- push,pop实质是一种内存传送指令,注意他们灵活使用
总结:栈是一种非常重要的机制,一定要深入理解,灵活掌握
3.9.段的综述
我们可以将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元
我们用一个段存放数据,可以定义为 数据段
我们用一个段存放代码,可以定义为 代码段
我们用一个段当作栈,可以定义为 栈段
数据段:将段地址存放在DS中
代码段:将它们的段地址存放在CS中,将段中的第一条指令的偏移地址存放在IP中,这样CPU就将执行我们定义的代码段中的指令
栈段:段地址存放在SS中,将栈顶单元的偏移地址存放在SP中
4.第一个程序
4.1.一个源程序从写出到执行的过程
- 第一步:编写汇编程序
- 第二步:对源程序进行编译连接
- 第三步:执行可执行文件中的程序
4.2.源程序
汇编语言中包含两种指令:汇编指令和 伪指令
- 汇编指令:有对应机器码的指令,最终被CPU执行
- 伪指令:由编译器进行执行,进行相关的编译动作
伪指令:

源程序中的程序
将源程序文件中所有内容称之为源程序,然后通过编译连接后转变为机器码,存储在可执行文件中

标号
一个标号指代了一个地址,作为一个段的名称,这个段的名称最终会编译一个段的段地址。

1 | mov ax,4c00H |
5.[BX]和loop指令
5.1.[bx]
mov ax,[bx]
说明:bx中存放的数据作为一个偏移地址EA,段地址SA默认在DS中,将SA:EA处的数据送到ax中
mov [bx],ax
说明:bx中存放的数据作为要给偏移地址EA,段地址默认在DS中,将ax中的数据送入到内存中
5.2.Loop指令
就是循环指令,它会减少CX的值并检查是否为0,如果不为0则跳转回标签继续循环。
5.3.Loop指令和[bx]使用

这段汇编语言代码主要实现了一个简单的功能,即计算从内存中某个地址开始的12个字节数据之和,并最终通过DOS中断调用返回到操作系统。
下面是详细的分析和解释:
段定义:
assume cs:code命令告诉汇编器代码将位于名为**code**的代码段中。code segment和code ends之间的内容定义了实际的代码段。初始化寄存器:
- **
mov ax,0fffH**将十六进制数0fffH送入AX寄存器。通常,这是为了设置DS(数据段)寄存器以指向内存中的某个数据段,但此处的值似乎用于演示目的,实际上可能会导致访问内存错误,因为0fffH对于大多数程序来说不是一个有效的段地址。mov ds,ax将AX的内容送入DS寄存器,设定数据段的基地址。mov bx,0初始化BX为0,BX将作为偏移地址来访问内存中的数据。mov dx,0初始化DX为0,DX将用来累加数据之和。mov cx,12初始化CX为12,CX将作为循环计数器,控制循环执行12次。循环求和:
s:标签定义了一个循环开始的位置。mov al,[bx]将BX寄存器指向的内存单元中的字节数据加载到AL寄存器中。mov ah,0清零AH寄存器,确保AL中的字节数据在后续操作中被视为无符号数。add dx,ax将AX寄存器的内容(此时为AL中的数据加上AH中的0)加到DX中,累计总和。inc bx将BX的值增加1,以便下一次循环读取下一个字节。loop s是一个循环指令,它会减少CX的值并检查是否为0,如果不为0则跳转回标签s继续循环。退出程序:
mov ax,4C00H将4C00H送入AX寄存器,这是DOS中断21H的功能号,用于请求程序终止并返回到操作系统,其中4CH表示退出程序,00H是返回给操作系统的错误码,通常表示正常退出。int 21H执行DOS中断调用,结束程序。注意:此代码中的DS段寄存器设置为0fffH,这在实际应用中很可能会导致访问冲突或错误,除非程序在特定的内存布局中运行,该布局中0fffH是合法且预期的数据段起始地址。在常规的实模式DOS程序中,数据段地址应由程序加载时正确设置,通常不会直接设置为这么高的地址。
5.4.段前缀
mov ax,ds:[bx]:将一个内存单元的内容送入ax中,这个内存单元存放两个字节,偏移地址在bx中,段地址在ds中mov ax,cs:[bx],mov ax,es:[bx],mov ax,ss:[0],mov ax,cs:[0]
这些出现在访问内存单元的指令中,用于显示的指明内存单元的段地址的ds:,cs:,ss:,es:,我们称之为段前缀
1 | 段寄存器:CS、DS、ES、SS |
5.5.一段安全的段空间
0:200~0:2ff的256字节空间,这个段空间是安全的
6.包含多个段的程序
6.1.在代码段中使用数据

**dw含义:**全称define word,在这里定义了8个字型数据,它们所占的内存空间大小为16字节
程序中的指令需要对这8个数据进行累加,8个数据放在哪里呢?
答:程序在运行的时候CS中存放代码段的的段地址。dw定义的数据处于代码段的最开始,所以偏移地址为0,这8个数据在代码段的偏移0,2,4,6,8,A,C,E处。
ends start:指明程序的入口,被转化为一个入口地址
这段汇编语言代码的功能是累加一组双字节(word)数据,并存储结果在寄存器AX中。下面是详细的代码分析:
1 assume cs:code这一行声明段寄存器CS指向名为
code的段。
1
2 code segment
dw 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h定义了一个名为
code的段,在这个段中存放了8个双字节(word)的数据:0123h, 0456h, … , 0987h。这些数据以双字节为单位连续存储在内存中,每两个数据之间默认间隔一个字节地址(因为每个数据占2字节)。
1
2
3 start: mov bx,0
mov ax,0
mov cx,8
start:标记了程序的起始执行点。mov bx,0初始化基址寄存器BX为0,BX将用来作为数据数组的指针。mov ax,0将累加和的寄存器AX清零,用于存放最终的累加结果。mov cx,8初始化计数寄存器CX为8,表示循环将执行8次,对应于数据数组中的8个元素。
1
2
3
4 s:
add ax,[bx]
add bx,2
loop s这是一个循环结构,用以累加数据段中的所有数据:
s:是循环的标签。add ax,[bx]将BX指向的内存单元中的数据(当前元素)加到AX中,实现累加操作。add bx,2每次循环后,BX增加2,这是因为每个数据项是双字节(word),即2个字节,所以要跳过当前数据到下一个数据。loop s是循环控制指令,它会自动将CX的值减1,如果CX不为0,则跳转回标签s继续执行循环,直到CX减到0为止,此时循环结束。
1
2 mov ax,4c00h
int 21h最后,使用DOS中断21h的服务号4Ch来结束程序,返回操作系统,其中AX寄存器的低字节00h表示程序正常退出。
1
2 code ends
end start
code ends表示code段定义结束。end start指定程序的入口点为start,同时告知汇编器程序汇编结束。综上所述,这段代码的作用是从内存中预先设定的一组双字节数据(0123h至0987h)开始,将这些数据累加起来,并将累加的结果存储在AX寄存器中。
6.2.在代码段中使用栈

对于sp的理解:
第一步:看计算机系统内存:
SA:10: 23 12 56 04 ……….09 16个字节
SA:20:0…………………………..0 对应 第二个dw:00000000
SA:30:0…………………………..0 对应 第二个dw:00000000
这段汇编代码完成了一个特定的任务,即通过堆栈在代码段中移动一个16字节数据块。具体分析如下:
1
2
3
4 assume code
code segment
dw 0123h,0456h,0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h
dw 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0首先,定义了两组双字节(word)数据。前8个是非零数据,后16个初始化为0。
1
2
3 start: mov ax,cs
mov ss,ax
mov sp,30h这部分代码设置了堆栈段(SS)与代码段(CS)相同,并将堆栈指针(SP)设置为30h,意味着堆栈从地址CS:30h开始向下增长,预留了一定的空间用于存储数据。
1
2
3
4
5 mov bx,0
mov cx,8
s: push cs:[bx]
add bx,2
loop s这里开始一个循环,将前8个非零数据通过
push指令压入堆栈。cs:[bx]访问的是代码段中的数据,从偏移量BX开始的双字节。每次循环,BX增加2,指向下一个数据,总共循环8次,将所有非零数据压栈。
1
2
3
4
5 mov bx,0
mov cx,8
s0: pop cs:[bx]
add bx,2
loop s0接着,进行第二个循环
s0,这次是从堆栈中弹出数据并存回到代码段中原来初始化为0的部分。同样循环8次,每次pop后,BX增加2,确保数据被正确地放置到代码段的对应位置。
1
2 mov ax,4c00h
int 21h最后,使用DOS中断21h的服务号4Ch来结束程序,返回操作系统,其中AX寄存器的低字节00h表示程序正常退出。
1
2 code ends
end标记代码段结束,并告知汇编器程序的结尾。
总结来说,这段代码通过堆栈实现了代码段内部的数据搬移,具体是从一段非零数据移到另一段初始为0的数据区域,展示了汇编语言中堆栈操作和循环控制的应用。
6.3.将数据、代码、栈放入到不同的段

对于sp的理解
c 等于开辟了一块空间,在这段空间中
SA:0:00000000
SA:10:000000000
然后就sp 就指向了20h
这段汇编代码演示了如何在不同的段之间移动数据,具体是从数据段A到堆栈段C,然后又从堆栈段C返回到另一个位置在数据段A中。以下是详细分析:
1 assume cs:b,ds:a,ss:c这一行指定了代码段寄存器CS指向段b,数据段寄存器DS指向段a,堆栈段寄存器SS指向段c。
1
2
3 a segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
a ends段a定义了8个双字节(word)的数据,这是原始数据集合。
1
2
3 c segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
c ends段c定义了16个双字节的空数据空间,准备用于临时存储从段a中移动过来的数据。
1
2
3
4 b segment
d: mov ax,c
mov ss,ax
mov sp,20h
- 这段代码位于代码段b中,从标号d开始执行。
- 首先,将段c的段地址加载到AX寄存器中,然后将AX的内容赋给SS,设置堆栈段指向段c。
- 接着,设置堆栈指针SP为20h,这意味着堆栈将从段c的地址20h处开始向下增长。
1
2
3
4
5
6
7 mov ax,a
mov ds,ax
mov bx,0
mov cx,8
s: push [bx]
add bx,2
loop s
- 将段a的段地址加载到AX,再将AX赋给DS,使得数据段寄存器指向段a。
- 初始化BX为0,CX为8,用于后续循环操作。
- 进入循环s,将段a中由BX指向的数据通过
push指令压入堆栈,之后BX加2指向下一个数据,使用loop s循环直到CX减到0。
1
2
3
4
5 mov bx,0
mov cx,8
s0: pop [bx]
add bx,2
loop s0
- 重置BX为0,CX为8,开始另一个循环s0。
- 在此循环中,从堆栈中弹出数据到段a中新的位置(之前清零的16个位置的前8个),BX每次加2指向下一个目标地址,直至循环结束。
1
2 mov ax,4c00h
int 21h
- 最后,设置AX为4C00h,调用DOS中断21h来结束程序。
1
2 b ends
end d
- 标记代码段b的结束,并告知汇编器程序的起始标签为d。
综上所述,这段代码首先设置好段寄存器以指向正确的段,然后通过堆栈机制,将段a中前8个双字节数据复制到段c中暂存,随后立即从段c中将这些数据弹出并覆盖段a中另一块预先清零的区域,完成了一次数据在段内的“转移”操作,并最终结束程序。
7.更灵活的定位内存地址的方法
7.1.and 和 or 指令、xor和not指令
and 指令:只有全为1,才为1
mov al,01100011B
and al, 00111011B
or 指令:只用全为0,才为0
or al, 00111011B
xor cx,cx ;CX清零,同时清除进位标志
AND AX,AX ;自身相与值不变,该语句常用于清进位标志
AND AL,0FH ;分离出AL中低四位,高四位被清0
AND AL,0F0FH ;分离出AL中高四位,低四位被清0
XOR DL,80H ;DL中最高位置1
总结
- NOT指令对标志位没有影响
- 执行除NOT指令外的逻辑指令后,OF和CF两个标志都被清0,而AF的状态不确定,其他标志反应操作结果的状态。
- TEST指令对两个操作数进行“与”操作,但是不保留“与”的结果,只是通过标志状态的判断,得出测试结果。
7.2.位移和循环位移
非循环移位指令:
SAL —— 算术左移 —— 最高位移入标志状态位CF,最低位补0
SAR —— 算术右移 —— 最低位移入CF,最高位不变,最高位原来是1,右移过后最高位就补1
比如说:将10000000算术右移7位,应该变成11111111,而逻辑右移7位,则不考虑符号位,变为00000001
SHL —— 逻辑左移 —— 最高位移入标志状态位CF,最低位补0
SHR —— 逻辑右移 —— 最低位移入CF,最高位补0
循环移位指令:
ROL —— 循环左移 —— 不带进位位的循环左移,最高位移入CF和最低位
ROR —— 循环右移 —— 不带进位位的循环右移,最低位移入CF和最高位
RCL —— 带进位的循环左移 —— 带进位位的循环左移,最高位移入CF,原来的CF进入最低位
RCR —— 带进位的循环右移 —— 带进位位的循环右移,最低位移入CF,原来的CF进入最高位
说明:
- CF的值总是最后一次被移入的值
- 位移指令影响标志位CF,OF,SF,ZF。而循环位移指令仅影响CF和OF位。
- 对OF位的影响:在移动1位的情况下,如果位移后的操作数是最高位改变了,OF就置1,否则置0.若位移次数大于1,那么OF 不确定。
1、算术左移、逻辑左移
算术左移和逻辑左移一样都是右边补0
[例] 00101011
算术左移一位:01010110
逻辑左移一位:010101102、逻辑右移:将二进制数整体右移,左边补0
[例]10101101逻辑右移一位为01010110
3、算术右移:符号位要一起移动,并且在左边补上符号位
[例]11100算术右移一位为11110(符号位1跟着一起移动并且左边补了1)
7.3.以字符的形式给出数据

1 assume cs:code,ds:data这一行定义了代码段(CS)和数据段(DS)的段寄存器分别指向名为
code和data的段。
1
2
3
4 data segment
db "unIX"
db "foRK"
data ends
data segment定义了数据段的开始。db "unIX"和db "foRK"分别定义了两个字符串常量,每个字符串包含四个字符,存储在数据段中。”unIX” 和 “foRK” 会按顺序占据内存中的位置。data ends标志数据段的结束。
1 >end start这一行指示汇编器程序的结束,并且指定程序的起始地址为
start标签所在的位置。总结:这个程序主要做了两件事情:定义了包含两个字符串的数据段,并在代码段中执行了一些基本的寄存器操作,最后通过调用 DOS 中断21h的4C号功能来结束程序。字符串”unIX”和”foRK”虽然被定义,但在程序中并没有被使用,它们的存在更多是为了演示数据段的定义。
7.4. [bx+idata]
[bx+idata]表示一个内存单元,它的偏移地址为(bx)+(itda)
7.5.SI和DI
si 和 di 是与bx 功能相近的寄存器,si 和di 不能够分成两个8为寄存器来使用,下面是3组指令实现了相同的功能

7.6.[bx + si] 和 [bx + di]
7.7.[bx + si + idata] 和 [bx + di + idata]
8.数据处理的两个基本问题
定义了两个描述符号:
- 通用寄存器reg包含了:ax,bx,cx,dx,(ah,al,bh,bl,ch,cl,dh,dl),sp,bp,si,di
- 段寄存器sreg包含了:ds,ss,cs,es
8.1.bx,si,di 和bp[基址变址寻址]
只有BX,SI,DI和BP这4 个寄存器可以用于[…]中进行内存单元寻址

在[…]中,这4个寄存器可以单个出现,或者只能以4中组合出现:bx 和 si,bx 和 di,bp 和si,bp和di,比如下面指令是正确的:

只要在[…]中使用寄存器bp,而指令中没有显性的给出段地址,段地址就默认在SS中,比如下面的指令

8.2.汇编语言中数据位置的表达
立即数:对于直接包含在机器指令中的数据(执行前在CPU的指令缓冲器中),在汇编语言中称之为立即数,在汇编指令中直接给出

寄存器:要处理的数据在寄存器中

8.3.段地址和偏移地址

8.4.寻址方式

- 用BX,DX,SI做间址寄存器寻找操作数时,隐含规定段基址由DS提供
- 当BP做间址寄存器寻找操作数时,隐含规定段基址由SS提供
8.5.指令要处理的数据有多长
使用操作符X ptr 指明内存单元的长度,X 在汇编中可以指定为word 或者 byte

8.6.算术运算类指令
ADD加法指令
格式:ADD DEST, SCR
功能:将源操作数SCR和目的操作数DEST相加,结果存入DEST中
1 | ADD AX,X |
ADC带进位加指令
格式:ADC DEST, SCR
功能:将源操作数SCR和目的操作数DEST以及进位标志CF位相加,结果存入DEST中
1 | ADC BX,9F88H |
XADD交换加法指令
格式:XADD DEST, SCR
功能:将源操作数SCR和目的操作数DEST相加,结果存入DEST中,原来的目的操作数DEST放入源操作数SRC中
DEST+SCR—>DEST, 原DEST—>SCR
INC自增指令
格式:INC DEST
功能:将目的操作数DEST自身增加1,结果存入DEST中,即DEST<-DEST+1
1 | INC SI |
注:INC指令对CF进位标志位无影响
SUB减法指令
格式:SUB DEST, SRC
功能:将目的操作数DEST内容减去源操作数SRC内容,结果输入DEST中,即DEST<-DEST-SRC
1 | SUB EAX,EBX ;CF=1 EAX=00000034H EBX=00000052H |
SBB带借位减法
格式:SBB DEST, SRC
功能:将目的操作数DEST内容减去源操作数SRC内容及CF位,结果输入DEST中,即DEST<-DEST-SRC-CF
说明:SBB指令主要用于大于16位的多精度的减法,把低位部分相减的借位引入高位部分的减法中
1 | SBB AX, BX;CF=1 AX=4037H BX=2342H |
DEC自减指令
格式:DEC DEST
格式:将目的操作数DEST自身减1,结果存入DEST中,即DEST<-DEST-1
1 | DEC SI |
CMP比较指令
格式:CMP DEST, SRC
功能:目的操作数DEST减去源操作数SRC,即DEST-SRC
说明:CMP比较指令将两个操作数相减,但相减的结果并不保留,两个操作数都保留原值不变,只是将相减的结果的特征反应在各个状态标志位上。
执行CMP指令后,如果标志位ZF=1,说明被比较的两个数相等;
两个数的大小查看CF标志位,如果标志位CF=1,说明出现借位,SRC更大;
1
2
3 CMP AX,BX ;AX-BX
JE NEXT ;JE表示如果两个数相等,即AX=BX,则跳转至标号NEXT
;JE[Jump Equal]
条件 大于 小于 等于 带符号数 G L E 无符号数 A B E
NEG取补指令
格式:NEG DEST
功能:零0减去目的操作数DEST,结果存入目的操作数DEST,即DEST<—0-DEST
说明:NEG指令是求操作数的负数,即改变操作数的符号,这对带符号数即为求其补码。NEG对标志位的影响:如果被取补的操作数非0,NEG操作后,CF置1,否则CF=0;
MUL无符号数乘法指令
格式:MUL SRC
功能:若SRC为字节长度,则AX<—AL*SRC;若SRC为字长度,则DX:AX<—AX*SRC[EAX<—AX*SRC]
说明:乘法指令格式中只出现源操作数SRC,根据操作数的类型决定是8位乘法还是16位乘法。SRC不能是立即数(立即数无类型属性),目的操作数(被乘数)隐含约定为累加器AL(8位乘)或AX(16位乘),运算结果约定在AX(8位乘法的积)或EAX(16位乘法的积)
若乘积的高半部全为0,则CF=OF=0
若乘积的高半部不全为0,则CF=OF=1
IMUL带符号数乘法指令
格式:IMUL SRC
功能:若SRC为字节长度,则AX<—AL*SRC;若SRC为字长度,则DX:AX<—AX*SRC[EAX<—AX*SRC]
说明: 乘法指令只影响进位标志位CF和溢出标志位OF。对于MUL,如果乘积的高半部(8位乘时为AH,16位乘时为DX)为零0,CF=0,OF=0;否则CF=OF=1(表示AH或DX中有乘积的有效数字)。若积的高半部是低半部的符号扩展,则CF=OF=0,否则CF=OF=1;
DIV无符号数除法指令和IDIV带符号数除法指令
格式:DIV SRC;IDIV SRC
div 是除法指令,使用div 做除法的时候注意以下问题
**要求被除数默认存放在ax,或者dx和ax中,而且要求被除数的位数,必须是除数的2倍**,比如说,除数位8位,被除数就为16位。为什么被除数的位数一定要为除数的2倍呢?
因为CPU只会做加法运算,把其它一切的算法都转换成加法,比如说,除数 就先转换加法,例如36/6 当CPU看到这个运算时,就会这样想,需要多少个6 相加才能得到36呢,然后CPU就从1个6 ,2个6………..这样一种算下去,终于最后发现原来是6个6啊,从这我们可以看出,CPU是不断的用除数相加,知道找到结果为止,这就出现问题了,如果被除数不是除数位数的2倍,再相加的过程中,就可能超出除数的位数所能表达的最大数值,从而越界,如果能保证 被除数是除数的2倍 ,这问题就能解决了,例如:8 /5 5+ 5 =10 超出所能表达的位数,08 /5 就可以解决这个问题
- 除数:有8位和16位两种,在一个REG或者内存单元中
- 被除数:默认放在AX或者 DX和AX中,如果除数为8位,被除数为16位,默认在AX中存放;如果除数为16位,被除数为32位,在DX和AX中存放,DX存高16位,AX存低16位
- 商:如果除数为8位,AL存储除法操作的商,AH存储除法操作的余数;如果除数为16位,AX存储除法操作的商,DX存储除法操作的余数
CWB字扩展指令
格式:CWB
功能:对AL中的带符号数进行符号扩展。若AL<0,AH =0FFH,否则AH=0.
扩展指令 解释 CWB AL符号扩展到AH中每一位,
8.7.伪指令dd
db 和 dw 定义字节型数据和字型数据,dd 用来定义dword(double world,双字)型数据
dup


9.转义指令
9.1.操作符 offset
功能:取得标号的偏移地址

上图的案例:取得标号start和s的偏移地址为0和3
9.2.根据位移进行转移的jmp指令
jmp short 标号(转到标号处执行指令)

9.3.转移的目的地址在指令中的jmp指令

将跳转到CS:IP所在的地址
9.4.转移指令在寄存器中的jmp指令
指令格式:jmp 16 位 reg
功能:(IP)=(16位reg)
9.5.转移指令在内存中jmp指令
jmp word ptr 内存单元地址(段内转移)
1 | mov ax,0123H |
jmp dword ptr 内存单元地址(段间转移)
1 | mov ax,0123H |
9.6.jcxz指令
当(cx)不等于0时,什么也不做,程序向下执行
当cx==0,jmp short 标号;
1 | 补全编程,利用jcxz指令,实现在内存2000H段中查找第一个值为0的字节,找到后,将它的偏移地址存贮在dx中。 |
9.7.loop指令
loop指令位循环指令,为短转移,相当于
(cx)–
if ((cx)≠0) jmp short 标号;
1 | ;************************************************************ |
10.CALL和RET指令
10.1.ret和retf指令
CPU执行ret指令,相当于pop IP
CPU执行retf指令,相当于pop IP 和 pop CS
监测点:
1 | 补全程序,实现从内存1000:0000处开始执行指令。 |
10.2.根据位移进行转移的call指令
CPU执行call 标号,相当于进行
- push IP
- jmp near ptr 标号
检测点
1 | # 下面程序执行后,ax中的数值是多少? |
10.3.转移目的地址在指令中的call指令
call far ptr 标号:实现段间转移。相当于进行如下操作
push CS
push IP
jmp far ptr 标号
监测点
1 | 下面程序执行后,ax中的数值为多少? |
10.4.转移地址在寄存器中的call指令
指令格式:call 16位 reg
功能:
- push IP
- jmp 16 位 reg
检测点
1 | 这儿用到了bp,除了之前这样用过bp外 [bx+bp] ,还会在栈中用到。 |
10.5.转移地址在内存中的call指令
call word ptr 内存单元地址,相当于进行:
push IP
jmp word ptr 内存单元地址
实例:
1 | mov sp,10h, |
call dword ptr 内存单元地址,相当于进行:
push CS
push IP
jmp dwod ptr 内存单元地址
实例:
1 | mov sp,10h |
检测点

参考书籍
- Title: Assembley_language
- Author: 敬请T期待
- Created at : 2024-06-09 16:29:27
- Updated at : 2024-11-17 14:02:01
- Link: https://kingwempity.github.io/2024/06/09/Assembley-language/
- License: This work is licensed under CC BY-NC-SA 4.0.