MOV
指令将逗号前的寄存器中的值修改为逗号后寄存器的值、内存的值或指定的值
语法:
mov [寄存器], [需要放入的值]
示例:
mov ax, 100
将 100 赋值到
ax
寄存器
也可以将一个寄存器中的值赋值到另一个寄存器
mov bx, 100 ; 将100赋值到bx
mov ax, bx ; 将bx中的值赋值到ax中
也可以将内存中的值赋值到寄存器中
mov ax, 2000H ; 将段地址先储存到ax中
mov ds, ax ; 设置段地址
mov bx, 1000H ; 将偏移地址储存到bx中,以便引用
mov ax, [bx] ; 将段地址为2000,偏移地址为1000的内存中的值赋值到ax中
mov ax, ds:[bx] ; 效果同上
mov ax, ds:[1000H] ; 效果同上
在源码中,不可以直接使用
[0]
来表示偏移地址,必须引用其他寄存器中的值,或者使用ds:[0]
这种格式。
idata
可以在程序中使用 idata
更方便的定位到指定的偏移地址
示例:
mov ax, [bx+10]
mov ax, [bx+si+10]
可以在设置偏移地址的方括号中使用运算
PTR
一般在执行计算时,指令后的寄存器会默认指定计算的值是字数据还是字节数据。例如:add al, sp:[0]
和 add ax, sp:[0]
,但是在一些特殊情况下,编译器无从得知需要计算的数是字数据还是字节数据,此时,就需要操作符 ptr
来指定数据类型。
语法:
[指令] [word/byte] ptr [操作]
示例:
inc word ptr sp:[0] ; 以字为单位将指定内存中的数据自加
OFFSET
取得对应标号的偏移地址
示例:
s:mov ax, offset s ; 将标号s所处的偏移地址复制到ax寄存器中
ADD
加法将第二个值和第一个寄存器中的值相加,并放入第一个寄存器中,用法同 mov
。
语法:
add [寄存器], [需要相加的值]
SUB
减法减法运算,用法同 add
指令
示例:
sub ax, 10
INC
自增& DEC
自减自增,将指定的值加一。
语法:
inc [寄存器]
inc [内存地址]
自减,将指定的值减一
语法:
dec [寄存器]
dec [内存地址]
AND
与运算逻辑与指令,将两个值进行逻辑与计算
1 and 1 = 1
0 and 1 = 0
1 and 0 = 0
0 and 0 = 0
语法:
and [寄存器、内存地址], [寄存器、内存地址]
示例:
and ax, bx
OR
或运算逻辑或云端,将两个值进行逻辑或计算
1 or 1 = 1
0 or 1 = 1
1 or 0 = 1
0 or 0 = 0
语法:
or [寄存器、内存地址], [寄存器、内存地址]
示例:
or ax, bx
MUL
乘法对指定数值进行乘法计算
ax
中,乘数默认在 al
中dx
中为高位 ax
中为低位,乘数默认在 ax
中DIV
除法除法指令 division
对寄存器的值进行除法运算。
div
指令后
ax
寄存器中的值会被作为被除数al
中为结果的商ah
中为余数dx
和 ax
中的值会拼接到一起作为一个双字型数据作为被除数,即 (dx)*10000h+(ax)
ax
中为商dx
中为余数示例:
assume cs:code
code segment
start:mov ax, 4d00h
mov dx, 25h ; 设置被除数为 254d00
mov bx, 7d0h ; 设置除数为 7d0
div bx
mov ax, 4c00h
int 21h
code ends
end start
LOOP
使用此指令可以跳转到指定的代码开始循环,若寄存器 cx
的值为0时则继续执行,若不为0时则会跳转到指定的代码进行循环,循环一次完成后会将 cx
中的值减一。
语法:
s:[代码段]
[代码...]
loop s ; 跳转到标号为 s 的代码上继续执行
loop
是否执行是判断cx
中值,所以在使用loop
时一定要将cx
中的值保存处理。
在多层循环的情况下,所有的 loop
指令使用的都是 cx
的值,所以在执行内层循环时需要将 cx
的值进行保存复原操作。
此操作可以使用寄存器和内存来实现,寄存器由于数量有限而且易被覆盖,而独立的内存地址在多个循环嵌套的情况下容易混淆,故使用栈来暂存复原 cx
的值。
格式:
assume cs:code, ss:stack
stack segment
dw 0,0,0,0,0,0,0,0
stack ends
code segment
start:mov ax, stack
mov ss, ax
mov sp, 10h
mov cx, 10 ; 设置外层循环次数
s0:[...do something...]
push cx ; 保存外层循环次数
mov cx, 20 ; 设置内层循环次数
s1:[...do something...]
loop s1
pop cx ; 复原外层循环次数
loop s0
mov ax, 4c00h
int 21h
code ends
JUMP
无条件转移指令jump
为无条件转移,可以只修改 ip
,也可以同时修改 cs
和 ip
跳转需要的两种信息
语法:
jmp [标号]
示例:
assume cs:code
code segment
start:mov ax, 1111h
jmp s
mov ax, 2222h
s:mov ax, 3333h
mov ax, 4c00h
int 21h
code ends
end start
此指令使用的数据位为8位,所以跳转的极限位置为
-128~127
超出此区域会发生错误。使用位移的方式确定跳转的地址时,无论这段代码放置在内存的任何位置,都可以正常执行。
语法:
jmp far ptr [标号]
示例:
assume cs:code
code segment
start:mov ax, 0
mov ax, 11h
jmp far ptr s ; 此跳转指令编译后为 JMP 076E:010B
db 256 dup (0)
s:add ax, 1
mov ax, 4c00h
int 21h
code ends
end start
这种格式的跳转指令是直接改变
cs
和ip
寄存器中的值,从而实现远距离跳转。
直接使用寄存器或内存中储存的内容作为 ip
寄存器的值进行跳转
语法:
mov ax, 0123h
jmp ax
jmp word ptr ds:[12]
jmp dword ptr ds:[12]
段间跳转使用的的是双字型数据,指定
内存单元地址
为ip
的值,内存单元地址+2
的数据为cs
寄存器的值。
JCXZ
条件转移指令根据条件:若寄存器 cx
的值为0,则跳转到指定标号的代码段,若 cx
不为0则继续向下执行。
格式:
jcxz short [标号]
PUSH
将指定的数据压入栈底,sp
寄存器中储存的内容是栈底的指针,值为栈底的偏移地址。
语法:
push [寄存器、内存地址、数据]
示例:
push ds:[bx]
POP
将栈顶的数据出栈到指定的寄存器或内存中
语法:
pop [寄存器、内存地址]
示例:
pop ds:[bx]
在使用栈段进行入栈出栈操作时,一定要先设置好
sp
寄存器的值。
指令语句在源程序汇编时会产生可供计算机执行的指令代码,即目标代码。汇编程序除指令语句外,还需要提供一些指令,用于辅助源程序的汇编。比如指定程序或数据存放的起始地址,为数据分配一段连续的内存单元等。这些指令在汇编时并不生成目标代码,不影响程序执行,因此称之为伪指令。
db
指令定义一个字节长度的数据
db 10,20
dw
指令定义两个字节长度的数据,也称为字型变量
dw 0123h,0234h
dd
指令定义四个字节长度的数据,也称为双字型变量
dd 123,234
DUP
定义多个变量使用 dup
伪指令可以同时定义多个变量
格式:
[db/dw/dp] [需要生成的数量] dup ([值],..)
示例:
dw 3 dup (0) ; 同 dw 0,0,0
dw 3 dup (0,1) ; 同 dw 0,1,0,1,0,1
AX,BX,CX,DX 数据寄存器:
AX (Accumulator):累加寄存器,也称之为累加器
BX (Base):基地址寄存器
CX (Count):计数器寄存器
DX (Data):数据寄存器
以上四个寄存器都可以分为两个8位的寄存器,例如 ax
可以分为 al
,ah
SP 和 BP 指针寄存器:
SI 和 DI 变址寄存器:
- 只有
bx,bp,si,di
可以放在[]
中进行寻址操作。并且只可以存在四种组合bx,si
bx,di
bp,si
bp,di
- 在
[]
寻址时使用bp
并且没有显式给出段地址,段地址默认在ss
中
CS (Code Segment):代码段寄存器;
DS (Data Segment):数据段寄存器;
SS (Stack Segment):堆栈段寄存器;
ES (Extra Segment):附加段寄存器;
CALL
指令用于和 ret
指令配合时间函数调用和返回,用法与 jmp
指令类似实现代码的跳转,但在跳转前会将 cs
和 ip
中的值 push
到栈中以用于程序返回。
语法:
call [标志位]
call [word/dword] ptr [内存地址]
RET
和 RETF
指令用于将 cs
和 ip
寄存器返回到原来的地址,ret
命令会执行 pop ip
而 retf
命令则会执行 pop cs
和 pop ip
两条命令。
语法:
ret
;-------
retf
CALL
和 RET
配合使用示例:
assume cs:code, ss:stack
stack segment
db 16 dup(0) ; 用来存储 cs 和 ip 的值
stack ends
code segment
start:mov ax, stack
mov ss, ax
mov sp, 16
call s ; 调用 s 代码段,实现函数调用
mov ax, 4c00h
int 21h
s:mov ax, 0
add ax, 10h
ret ; 返回到 call 命令后,实现函数返回
code ends
end start
ADC
指令adc
指令用于在计算时将 CF
标志寄存器的值也进行带入运算,可以用来实现更多位数的数据运算,使用格式与 add
相同。
格式:
adc [寄存器], [数值/寄存器/内存]
示例:
adc ax, 10
公式
(ax)+10+(cf)
使用 adc
指令计算 1EF000H
和 201000H
的值:
assume cs:code
code segment
start:
mov ax, 001eh ; 第一个数的高位
mov bx, 0f000h ; 第一个数的低位
add bx, 1000h ; 将低位相加,进位保存到CF中
adc ax, 20h ; 两数高位和CF中的进位相加
; 结果 ax 中为高位 bx 中为低位
mov ax, 4c00h
int 21h
code ends
end start
SBB
指令sbb
指令用法与 adc
指令相同,此指令用户两数相减再减去 cf
标记寄存器中的值。
示例:
sbb ax, bx
公式:
(ax)-(bx)-(cf)
CMP
指令此指令功能相当于减法指令,但是不保存结果只会影响标志寄存器的值。其他指令通过识别被影响的标志寄存器来得知比较结果。
格式:
cmp [操作对象1], [操作对象2]
示例:
mov ax, 8
mov bx, 3
cmp ax, bx
执行后:
ax不变
ZF=0
PF=1
SF=0
CF=0
OF=0
通过此指令可以判断标志寄存器的值来确定两个数的大小关系
a
为值1,b
为值2
ZF
若为1则表示两个数相等ZF
若为0则表示两个数不相等
OF
若为0,没有溢出
SF
为1,表示 a
SF
为0,表示 a>b
OF
为1,有溢出
SF
的含义正好和表达的相反指令 | 含义 | 检测内容 |
---|---|---|
je | 等于转移 | ZF=1 |
jne | 不等于转移 | ZF=0 |
jb | 低于转移 | CF=1 |
jnb | 不低于转移 | CF=0 |
ja | 高于转移 | CF=0&ZF=0 |
jna | 不高于转移 | CF=1orZF=1 |
每次执行指令 SI,DI
寄存器都会根据 DF
标志位进行相应的增减。
((es)*16+di)=((es)*16+si)
DF=0
SI,DI
DF=1
SI,DI
串传送指令和
rep
指令进行配合可以实现循环多次复制,循环条件和loop
指令相同。
MOVSB
字节传送每次 SI,DI
加一,实现正向复制。
示例:
assume cs:code,ds:data
data segment
db 'hello world'
db 11 dup (0)
data ends
code segment
start:
mov ax, data
mov ds, ax
mov si, 0
mov es, ax
mov di, 11
mov cx, 11
cld
rep movsb
mov ax,4c00h
int 21h
code ends
end start
结果:
MOVSW
字传送效果同 movsb
指令,movsw
传送的为字型数据。
DF
标志位设置 DF
标志:
cld ; 将DF设置为0
std ; 将DF设置为1
PUSHF
& POPF
将标志寄存器中的数据进行入栈和出栈,用法同 push
和 pop
指令。