汇编
1. debug指令
-R命令(register)
查看、改变CPU寄存器的内容
r ax 修改AX中的内容
-D命令(display)
查看内存中的内容
-E命令(enter)
改写内存中的内容
-U命令(unassenble反汇编)
将内存中的机器指令翻译成汇编指令
-T命令(trace跟踪)
执行一条机器指令
-A命令(assenble汇编)
以汇编指令的格式在内存中写入一条机器指令
2. mov、add、sub指令
1.mov(传送)
格式:mov ax, bx
注意:
1. 目标操作数与源操作数不能同时为内存操作数 2. 不能直接将立即数传送给段寄存器 3. CX不能以一切形式传送 4. 两个操作数类型必须一致, 字节、字、双字
2.add
格式:add ax, bx
若两数相加结果超过寄存器位数,舍弃超出的高位
eg1溢出:cx = F06F
add cx, cx 原: F06F + F06F = 1E0DE
cx = E0DE 最高位的 1 被舍去
eg2高位: BX = F037
add bh, 37 原: F0 + 37 = 127
BX = 2737
eg3低位: CX = E0DE
add cl, B6 原:DE + B6 = 194
CX = E094 低位计算也不会进位
3.sub
格式: sub op1,op2 ;意为:op1=op1-op2
若两数相减,被减数小于减数,则被减数从最高位的上一位借1
eg1: BX = 1303
sub bx, F
BX = 12F4
eg2借位: BX = 000F
sub bx, 10 原:1000F - 10 = FFFF
BX = FFFF 从高位借1
eg3: BX = 0000
sub bl, E 原:100 - E = 00F2
BX = 00F2 从目标操作数的高位借1
3.mul、div、and、or指令
1. mul(乘法)
(1)两个相乘数:要么都是8位,要么都是16位。
-
若是8位,一个默认放 AL 中,另一个放在 8位reg 或 内存单元
-
若是16位, 一个默认放在 AX 中, 另一个放在 16位reg 或 内存单元
(2)结果:如果是8位乘法,结果默认放在 AX
如果是16位乘法,结果 高位 默认放在 DX , 低位 默认放在 AX 中
格式:mul reg
mul 内存单元
注意:溢出同加法
2. div(除法)
(1)除数:8位 或 16位,在一个reg 或内存单元中
(2)被除数:默认放在 AX 或 DX 和 AX 中
如果除数为8位,被除数则为16位,默认放在 AX 中
如果除数为16位,被除数则为32位, 放在在 DX 和 AX 中,DX放高16位,AX放低16位
(3)结果: 如果除数为8位, 则商放在 AL,余数放在 AH
如果除数为16位,则商放在 AX,余数放在 DX
格式: div reg
div 内存单元
除数8位:AX / BL = AL...AH
除数16位:DXAX / BX = AX...DX
eg132位被除数:DX = F , AX = 4240, BX = 2710
mov dx, F F4240 = 1000000 2710 = 10000 64 = 100
mov ax, 4240 原:F4240 / 2710 = 64
mov bx, 2710
div bx
AX = 64 通过高16位DX, 低16位AX组成一个32位的被除数,商放在AX
eg2有余数: DX = F, AX = 4241, BX = 2710
mov dx, F F4241 = 1000001
mov ax, 4241 原:F4241 / 2710 = 64...1
mov bx, 2710 1000001 / 10000 = 100...1
div bx
AX = 64, DX = 0001
3. and(与)
逻辑与指令,按位进行与运算
mov al, 63
and al, 3B
结果:al = 23
注意:该指令可将操作对象的相应位设为0,其他位不变
将al的第0位设为0的指令为:and al, FE(11111110B)
4. or (或)
逻辑或运算,按位进行或运算
mov al, 63
or al, 3B
结果:al = 7B
注意:该指令可将操作对象的相应位设为1,其他位不变
将al的第0位设为1的指令为:or al, 1(00000001B)
4.shl、shr、inc、dec、xchg
1.shl (左移)
shift left
1.将寄存器或内存单元中的数据向左移位
2.将最后移出的一位写入CF(进位标志)中
3.最低位用0补充
格式:mov al, 48 ;(01001000)
shl al, 1 ;将al中的数据都左移一位
结果: al = 90(10010000), CF = 0
2.shr(右移)
shift right
同shl
3.inc(加一)
值加一,不影响CF
4.dec(减一)
5.nop(空)
空指令,先占位
6.xchg(交换)
交互两操作数的值
格式:xchg ax, bx
7.neg(求补)
运算法一:用零减去操作数,然后结果返回操作数
运算法二:将操作数按位取反后加1
8.退出
格式:mov ax, 4c00H
int 21H
安全退出程序
5.寄存器
AX:累加寄存器 OF:溢出标志(超过机器所能表示的有符号数范围)
BX:基址寄存器 ZF:零标志(运算结果为0,ZF=1)
CX:计数寄存器 CF:进位标志
DX:数据寄存器 AF:辅助进位标志(第3位向第4位进位时位1,否则为0)
SP:堆栈指针寄存器 PF:奇偶标志(计算结果为1个数为偶则为1,否则为0)
BP:基址指针寄存器 DF:方向标志(串处理,DF=1时,每次操作后SI和DI减小,DF=0时增大)
SI:源变址寄存器 SF:符号标志(运算结果为负时SF=1)
DI:目的变址寄存器 IF:中断标志(IF=1,允许CPU响应可屏蔽中断,否则关闭中断)
CS:代码段寄存器 TF:陷阱标志(用于调试单步操作)
DS:数据段寄存器
SS:堆栈段寄存器
ES:附加段寄存器
5.CS:ip代码段寄存器、jmp
CS:IP指示了CPU当前要读取指令的地址
CS 为代码段寄存器,IP 为指令指针寄存器
jmp
功能:用寄存器中的值修改IP
格式:jmp ax ;等同于mov IP, ax 等同但不可以这样用 修改IP
jmp ax:bx ;等同于mov CS, ax mov IP, bx 修改CS:IP
6.ss:sp寄存器、栈的push、pop指令
栈底是高地址
1.push
push指令的完成,需要以下两个步骤
-
SP = SP - 2:因为SS:SP指向前栈顶,既然需要push 数据,自然需要获取新栈顶
-
将 push 后面跟的寄存器中的内容写入刚开辟出的内存中,此时的SS:SP指向新栈顶
入栈时,栈顶从高地址向低地址增长
2.pop
pop指令的完成,需要以下两个步骤
-
将栈顶元素送入pop后面跟的寄存器中
-
SP = SP + 2,SS:SP指向下一个元素
出栈时,栈顶从低地址向高地址增长
ss:sp 指向栈顶元素
7.bp,si、di变址寄存器
1. SI 、DI是变址寄存器
SI 与 DI 是功能与 bx相近的寄存器,不能分成8位寄存器来使用,但是可以直接作为偏移地址(有效地址)
[BX+SI] 与 [BX+DI] 可亦作为偏移地址,但是不可加ax、cx、dx这些寄存器
2. BP寄存器
BP默认的段寄存器是SS,bx默认的段寄存器是DS
8.cmp、adc、sbb、
1. adc(进位加法)
adc 是带进位加法指令,利用 CF 位上记录的进位值
格式:adc 操作数1, 操作数2
功能:操作数1 = 操作数1+操作数2 + CF
常用于32位、48位数的相加,可实现进位
2. sbb(借位减法)
sbb 是带借位减法指令,利用 CF 位上记录的借位值
格式:sbb 操作数1, 操作数2
功能:操作数1 = 操作数1- 操作数2 - CF
利用sbb指令可以对任意大的数据进行减法运算
3. cmp(比较)
cmp是比较指令,功能相当于减法指令,只是不保存结果,但是影响标志寄存器
格式:cmp 操作数1, 操作数2
功能:计算 (操作数1 - 操作数2) 但不保存结果,仅改变标志寄存器
9.源程序
assume cs:codesg
codesg segment
mov ax, 0123H
add bx, 0456H
add ax,bx
add ax,ax
mov ax,4c00H
int 21H
codesg ends
end
10.Loop指令
格式:loop 标号
CPU执行loop指令的时候要进行两步操作:
-
(cx) = (cx) - 1
-
判断 cx 中的值,不为零则转至标号处执行程序,如果为零则向下执行
用loop指令实现循环功能,cx中存放循环次数
assume cs:codesg
codesg segment
mov ax, 2
mov cx, 11 ;循环11次,输入多少,就循环多少次
s:
add ax, ax
loop s
int 21H
codesg ends
end
如果 cx = 0,loop指令会陷入死循环:
因为Loop指令会先减一,即cx = 0 - 1 = FFFF,需要在运行65535次
#include<stdio.h>
int x = 0;
int sum = 0;
for (int i = 1; i <= 100; i++) {
x++;
sum += x;
}
return sum;
上述C语言转换为汇编语言:
assume cs:codesg
codesg segment
mov ax, 0
mov bx, 0
mov cx, 100
s:
inc ax
add bx, ax
loop s
int 21H
codesg ends
end
11.ret、call指令
call ==> func()
ret ==> return
assume cs:codesg
codesg segment
mov ax, 2
mov cx, 11
call s ;调用函数s
int 21H ;若不退出,会执行下一指令,陷入死循环
s:
add ax, ax
loop s
ret ; 封装函数s,等于return
codesg ends
end
1. ret 和 retf
ret 指令用栈中的数据,修改 IP,实现近转移
执行后,进行操作:
(1) IP = SS * 16 + SP
(2) SP = SP + 2
等同于: pop IP
retf指令用栈中的数据,修改 CS 和 IP ,实现远转移
(1) IP = SS * 16 + SP
(2) SP = SP + 2
(3) CS = SS * 16 + SP
(4)SP = SP + 2
等同于: pop IP
pop CS
2.call
格式: call 标号
执行后,进行操作:
(1)将当前的 IP 或 CS 和 IP 压入栈中
(2)转移
3.call far ptr
格式:call far ptr 标号
实现段间转移
12.代码段、数据段、栈段、dup
16进制不能以字母开头
1.在代码段使用数据
assume cs:codesg
codesg segment
dw 123H, 456H, 789H, ABCH
start:
mov ax, 3
mov cx, 11
call s
inc bx
int 21H
s:
add ax,ax
loop s
codesg ends
end start
start 相当于事先把ip跳到第一行代码的有效地址上,避免了定义的数据与执行代码之间的混乱
2.数据、代码、栈放入不同的段
assume cs:code,ds:data,ss:stack
data segment
dw 123H, 456H, 789H, ABCH
data ends
stack segment
db 0, 0, 0, 0, 0, 0;等价于 6 dup(0) 即重复6个
;dup 'hello world' db 3 dup('abc', 'def')
stack ends
code segment
start:
mov ax, 3
mov cx, 11
call s
inc bx
int 21H
s:
add ax,ax
loop s
code ends
end start
dup 的使用格式:
db 重复次数 dup (重复的字节型数据)
dw 重复次数 dup (重复的字型数据)
dd 重复次数 dup (重复的双字型数据)
代码段、数据段、栈段的地址空间的连续的
每个段为避免冲突,至少占用16个字节,(即段地址不同?)
13.offset、jmp
1.操作符offset
功能:取得标号的偏移地址
2.jmp
jmp 为无条件转移指令,可以只修改IP,也可以同时修改CS和IP
1.jmp short 标号
实现段内短转移,对IP修改范围为 -128~127
2.jmp far ptr 标号
实现段间转移,或远转移,修改CS:IP
3.jmp word ptr 内存单元地址(段内地址)
从内存单元地址处开始取一个字,作为转移的目的偏移地址
4.jmp dword ptr 内存单元地址(段间地址)
从内存单元地址处开始取两个字,高地址作为转移的段地址,低地址作为转移的目的偏移地址
14.数组
定义字符串: db ”字符串“
1.arr 数组
assume cs:code,ds:data,ss:stack
data segment
arr dw 12,34 ;定义数组
arr2 db "hello world"
data ends
stack segment
db 10 dup(0)
stack ends
code segment
start:
mov ax, type arr ;type arr 可以查看数组的类型
mov ax, type arr2 ;0001是字节,0002是字
code ends
end start
2.数据标号
代码段内定义:
assume cs:code,ds:data,ss:stack
data segment
data ends
stack segment
db 10 dup(0)
stack ends
code segment
arr dw 12,34 ;定义数组
arr2 db "hello world"
start:
mov ax, arr[2] ;可以通过下标,访问数组,下标增加1,偏移地址增加1字节,不与定义同步
;arr[2] <==> cs:[arr+2]
mov ax, word ptr arr[2] ;从cs:[arr+2]处开始取一个字的内容,放进ax
mov si, offset arr ;可以找到数组的有效/偏移地址
code ends
end start
其他段定义:
assume cs:code,ds:data,ss:stack
data segment
arr db 10H,20H,30H,40H ;定义数组
arr2 db "hello world"
data ends
stack segment
db 10 dup(0)
stack ends
code segment
start:
mov ax, data
mov ds, ax ;没有上面两行,会找不到data;设置ds指向data段
;即 assume处的data与定义了数组的data不同步,导致寻址不同,而找不到定义的数组
;默认访问单元的段地址在ds中,而实际要访问的段为data
mov ax, arr[2] ;等价于
;mov si, offset arr
;mov al, ds:[si+2]
mov ax, type arr2 ;
code ends
end start
15.实战1.0
输出hello world:
assume cs:codesg,ds:data,ss:stack
data segment
string db "hello world",10,'123','$' ;$表示终止符,避免向内存后面继续
;10是换行的ASCII的值,实现换行功能。32-->空格
data ends
stack segment
db 10 dup(0)
stack ends
codesg segment
start:
mov ax, data
mov ds, ax
mov dx, offset string
mov ah, 09H
int 21H
mov ah, 4CH
int 21H
codesg ends
end start
大写转小写:
ASSUME CS:CODE,DS:DATA,SS:STACK
DATA SEGMENT
STR DB "HeLlo woRLd",'$'
DATA ENDS
STACK SEGMENT
DB 10 DUP(0)
STACK ENDS
CODE SEGMENT
START:
MOV AX, DATA
MOV DS, AX
MOV BX, 0
MOV CX, 11
S:
MOV AL, [BX]
CMP AL, 'A'
JB NEXT
CMP AL, 'Z'
JA NEXT
OR AL, 20H
MOV [BX], AL
NEXT:
INC BX
LOOP S
;C语言:
;for(int i=0;i<strlen(str);i++)if(大写)转小写
MOV DX, OFFSET STR
;LEA DX, STR
MOV AH, 09H ;9号功能参数入口为DX
INT 21H
MOV AH, 4CH
INT 21H
code ends
end start
找最大值:
ASSUME CS:CODE, DS:DATA, SS:STACK
DATA SEGMENT
STRING DB 10, 15, 22, 80, 50, 39,'$'
MAX DB 0
DATA ENDS
STACK SEGMENT
DB 10 DUP(0)
STACK ENDS
CODE SEGMENT
START:
MOV AX, DATA
MOV DS, AX
LEA BX, STRING
MOV CX, 5
MOV AL, [BX]
S:
MOV AH, [BX+1]
CMP AL, AH
JA CONTINUE
MOV AL, AH
CONTINUE:
INC BX
LOOP S
MOV [MAX], AL
MOV AH, 4CH
INT 21H
CODE ENDS
END START
参考:《汇编语言》速成指南(全程敲代码)_哔哩哔哩_bilibili