关于寄存器的疑问

  • 寄存器的名称为什么一个ax会出现rax,eax,ax,ah,al
1
2
3
4
5
回答:是为了确保能够被向下兼容,才有这些名称的。
x86基于原始的8086指令集,寄存器是16为宽
x86 ISA将寄存器扩展为32位
x86-64再将它们进一步扩展为64位。
为了保证向后兼容,新指令集中使用的寄存器是旧寄存器的超集

所以在64位寄存器中就会出现如下图:

寄存器

  • 在介绍汇编指令之前,先要了解寄存器,这样才有利于汇编指令的学习

image-20240415011207826

image-20240415012604712

通用寄存器

数据寄存器

  • 累加寄存器
  • 基数寄存器
  • 计数寄存器
  • 数据寄存器

指针寄存器

  • 堆栈指针
  • 基数指针

变址寄存器

  • 源变址寄存器
  • 目的变址寄存器

专用寄存器

控制寄存器

  • 指令指针寄存器
  • 状态标志寄存器

状态标志寄存器

  • 又称为标志寄存器

image-20240415183455181

  • CF标志(Carry Flag)–第0位——进位标志位
    • 在进行无符号运算时,记录最高有效位向更高位的进位值,或向更高位的借位值
    • 例如:
1
2
3
4
5
6
7
8
mov al,98H
add al,al
执行后:
al=30H,会将溢出值丢弃,但是CF=1

mov al,97H
sub al,98H ;执行后:al=FFH,CF=1,CF记录了借位
sub al,al ;执行后:al=0,CF=0 CF记录了借位

image-20240416144501905

  • PF标志(Parity Flag)–第2位——奇偶标志位
    • 判断计算结果bit位中1的个数是否为偶数,1为偶数个就置1,为奇数个就置0
    • PF标志位是看当前指令执行后的结果
1
2
3
4
例如:
mov al,1
add al,10
执行后,结果为00001011B,1的个数为3(奇数),pf=0
  • AF标志–第4位——
  • ZF标志(Zero Flag)–第6位——零标志位
    • 零标志位,对指令进行操作,计算结果为0,则ZF标志位为1;计算结果不为0,则ZF标志位为0。
    • ZF标志位是看当前指令执行后的结果
1
2
3
4
例如:
mov ax,1
sub ax,1
执行后,结果为0,则zf=1
  • SF标志(Sign Flag)–第7位——符号标志位

    • 符号标志位,指令执行后,结果为负,SF=1;结果为正,SF=0

    • SF标志位是看当前指令执行后的结果,或者看一个整体运算的结果

1
2
例如:

  • TF标志
  • IF标志
  • DF标志
  • OF标志(Overflow Flag)–第11位——溢出标志位

段寄存器

  • 代码段寄存器(CS)
  • 数据段寄存器(DS)
  • 堆栈段寄存器(SS)
  • 附加段寄存器(ES)

汇编指令语句

寻址方式

  • 立即寻址
  • 寄存器寻址
  • 寄存器间接寻址
  • 直接寻址
  • 寄存器相对寻址
1

汇编指令

  • 在学习汇编指令之前先了解一些缩写
    • OPD:表示目的操作数Operand Destination
    • OPS:表示源操作数
    • mem:表示内存单元Memory
    • reg:表示通用寄存器Register
    • seg :表示段寄存器Segment
    • imm :表示立即数Immediate

MOV指令

  • “MOV”是赋值指令,用于数据传送,可以将数据域从一个源位置转移到另一个目的的位置(源位置的数据会复制一份到目标位置,而不是单纯的移动)
1
2
3
4
MOV OPD,OPS # 
MOV AX,8; # 将8赋值给AX,AX=8;
MOV BX,10; # 将10赋值给BX,BX=10;
MOV AX,BX; # 将BX中的10赋值给AX,AX=BX;
  • MOV指令的操作范围
1
2
3
4
MOV reg/mem,imm;       将立即数 赋值给 寄存器/内存单元
MOV seg/reg/mem,reg; 将寄存器 赋值给 段寄存器/寄存器/内存单元
MOV seg/reg,mem; 将内存单元 赋值给 段寄存器/寄存器
MOV reg/mem,seg; 将段寄存器 赋值给 寄存器/内存单元

image-20240416185944709

  • 需要注意
1
2
两内存单元 不能直接MOV,需要通过寄存器 间接MOV

XCHG指令

  • 用于交换两个操作数的数据
1
2
3
4
格式: XCHG OPD,OPS
XCHG reg,mem
XCHG reg,reg
XCHG mem,reg
  • XCHG指令不能使用立即数作为操作数,并且在段寄存器和累加器之间不能进行交换。

XLAT指令

  • 查表指令
  • AL寄存器中的值用作查找表的索引
  • 查找表的基地址由BX寄存器提供
  • XLAT指令将AL中的值替换为查找表中BX+AL位置处的值

PUSH、POP指令

ADD指令

  • 加法操作,将源操作数与目的操作数相加,把结果存储到目的操作数中
1
2
3
4
5
6
7
8
ADD 目的操作数,源操作数
ADD AX,BX
AX = AX+BX

ADD 寄存器,寄存器
ADD 寄存器,内存
ADD 寄存器,立即数
ADD 内存,立即数

ADC指令

  • 带进位位的加法操作指令

  • 将源操作数和目的操作数和CF标志位的值相加,结果存储在目的操作数中

  • 影响的标志位

    • CF(进位标志):如果结果产生了进位或借位,则设置为 1,否则设置为 0。

      ZF(零标志):如果结果为 0,则设置为 1,否则设置为 0。

      SF(符号标志):如果结果的最高有效位为 1,则设置为 1,否则设置为 0。

      OF(溢出标志):如果带符号的操作数产生溢出,则设置为 1,否则设置为 0。

      PF(奇偶标志):如果结果的最低 8 位中有偶数个 1,则设置为 1,否则设置为 0。

      AF(辅助进位标志):如果发生了低 4 位的进位或借位,则设置为 1,否则设置为 0。

1
ADC 目的操作数,源操作数

SUB指令

  • 减法操作:sub ax,bx。

  • ax里面的数据是被减数,bx里面的数据是减数,将结果储存在ax里面

CMP指令

  • CMP 指令用于比较两个操作数。它实际上是通过执行隐式减法(减去源操作数)来设置状态标志(如零标志、符号标志和进位标志),但不存储结果。

  • 操作数的类型

1
2
3
4
5
寄存器、内存操作数、立即数
CMP 寄存器,寄存器;
CMP 寄存器,内存;
CMP 寄存器,立即数;
CMp 内存,立即数;

INC指令

  • 自增指令

LEA指令

  • 取地址指令
  • 能作为地址指针的寄存器有BX、BP、SI、DI

LES指令

  • 取内存地址的内容,准确的来说是将内存中的段偏移地址加载到指定的通用寄存器和ES段寄存器
  • 所以LES目的操作数只能是BX、SI、DI、BP,能配合段寄存器使用取址的寄存器
1
2
3
4
LES BX,[1234H]
将取内存1234H处的4字节
低2字节存到BX寄存器中
高2字节存到ES寄存器中

LDS指令

  • 取内存地址的内容,准确的来说是将内存中的段偏移地址加载到指定的通用寄存器和DS段寄存器
  • 目的操作数的限制同LES指令
1
2
3
4
LDS SI,[1234H]
取内存1234H处的4字节
低2字节存到SI寄存器中
高2字节存到DS寄存器中

OUT指令

  • 从CUP向IO端口写入数据,它将数据从一个寄存器或内存地址输出到指定的IO端口
  • OUT指令的目的操作数只能是DX,
1
2
3
MOV DX, 0x03F8  ; 将0x03F8端口地址加载到DX寄存器
MOV AL, 65 ; 将ASCII码为65的字符('A')加载到AL寄存器
OUT DX, AL ; 将AL寄存器中的数据输出到0x03F8端口

逻辑运算指令

XOR指令

  • 对源操作数和目的操作数做异或操作,并将结果保存在目的操作数中
    • 运算结果为0,ZF置1,反之置0
    • 运算结果最高位为1,则SF置1,反之置0
    • CF、OF始终置0
    • 结果为偶数PF置1,反之置0

OR指令

  • 对源操作数和目的操作数进行或运算,并将结果保存在目的操作数中

AND指令

  • 将源操作数和目的操作数进行与操作,并将结果保存在目的操作数中

NOT指令

  • NOT 指令不会影响任何标志位(如 CF, ZF, SF, OF 等)。

TEST指令

  • 将源操作数和目的操作数进行与操作,并且不保存结果,只影响标志位
  • 影响的标志位
    • ZF(零标志):如果结果为 0,则设置为 1,否则设置为 0。
    • SF(符号标志):如果结果的最高有效位为 1,则设置为 1,否则设置为 0。
    • PF(奇偶标志):如果结果的最低 8 位中有偶数个 1,则设置为 1,否则设置为 0。
    • CF(进位标志):被清除(设置为 0)。
    • OF(溢出标志):被清除(设置为 0)。

移位指令

算术位移指令

SAL/SAR指令

  • SAL算术左移,SAR算术右移指令

逻辑位移指令

SHL/SHR指令

  • SHL逻辑左移,SHR逻辑右移指令

循环位移指令

ROL/ROR指令

RCL/RCR指令

控制转移指令

  • 跳转指令中
    • A:Above
    • B:Below
    • E:Equal
    • G:Greater
    • J :Jump
    • L:Less
    • N:Not
汇编指令 描述
JA 无符号大于则跳转
JNA 无符号不大于则跳转
JAE 无符号大于等于则跳转(同JNB)
JNAE 无符号不大于等于则跳转(同JB)
JB 无符号小于则跳转
JNB 无符号不小于则跳转
JBE 无符号小于等于则跳转(同JNA)
JNBE 无符号不小于等于则跳转(同JA)
有符号跳转
汇编指令 描述
------- ----
JG 有符号大于则跳转
JNG 有符号不大于则跳转
JGE 有符号大于等于则跳转(同JNL)
JNGE 有符号不大于等于则跳转(同JL)
JL 有符号小于则跳转
JNL 有符号不小于则跳转
JLE 有符号小于等于则跳转(同JNG)
JNLE 有符号不小于等于则跳转(同JG)
  • 转移的方式
    • 段内转移,范围在段内-128~+127范围内称为短转移
    • 段间转移
  • JMP指令默认是近转移

JMP指令

  • 无条件转移指令
1
2
3
4
5
6
7
8
9
格式:
JMP LABLE

JMP SHORT LABLE;
JMP NEAR PTR LABLE;
JMP WORD PTR [BX];

JMP FAR PTR LABLE;
JMP FAR PTR MEM;
  • 用指令中给出的段地址修改CS的值,给出的偏移地址修改IP的值
  • 也可以仅修改IP的值
1
2
3
4
5
6
7
8
9
10
11
12
jmp指令修改CS、IP的值
jmp 数据:数据
例如:
jmp 2AE3:3 执行后:CS=2AE3H IP = 0003H
jmp 3:0B16 执行后:CS=0003H IP = 0B16H

jmp指令仅修改IP的值
jmp 某一合法寄存器,将该合法寄存器里面的值赋值给IP
例如:
假设CS=2000H,IP0003H
mov ax,1000H
jmp ax 执行后:CS=2000H,IP=1000H

JCC指令

  • 条件转移语句
  • 该指令是段内短转移指令
  • 条件转移中的条件可以分成三类:判断单个标志位状态、比较无符号数高低、比较有符号数大小
1
2
3
4
格式:
JCC LABLE;


判断单个标志位转态

1
2
3
4
5
JZ/jE和JNZ/JNE
JS和JNS
JO和JNO
JP/JPE和JNP/JPO
JC/JB/JNAE和JNC/JNB/JAE

jz指令

  • ”JZ“是条件跳转指令的一种,全称”Jump if Zero“

    • 即:如果结果为0,则跳转到指定的目标地址

    • 这个指令通常用于控制程序执行流程,根据先前的运算结果判断是否进行跳转。

1
2
3
4
5
6
7
8
9
用法: JZ destination
其中:destination是一个标记或者地址,表示程序在满足条件是要跳转到的目标位置。

例如:
MOV AX,0;
....
关于AX的运算
....
JZ ZeroDetected; # 如果AX的值为零,则跳转到ZeroDetected标签处

比较无符号数高低

  • 指令中的A表示高,B表示低,利用CF确定高低、ZF标志确定相等
1
2
3
4
JB/JNAE/JC
JNB/JAE/JNC
JBE/JNA
JNBE/JA

比较有符号数大小

  • 指令中的G(Greater)表示大,L表示小(Less)
1
2
3
4
JL/JNGE		
JNL/JGE ≥0 则跳转
JLE/JNG ≤0 则跳转
JNLE/JG

其他

  • 测试CX的值为0,则转移的指令
1
2
格式:
JCXZ LABLE

循环指令

LOOP指令

  • 循环控制指令
  • 默认利用CX计数器,属于段内短转移。执行时:
    • CX=CX-1
    • 判断CX是否为0,CX不为0,则跳转;CX为0则不跳转。
    • 执行后续程序
1
2
3
4
格式:
LOOP LABLE
LOOPZ/LOOPE LABLE
LOOPNZ/LOOPE LABLE

LOOPZ/E指令

  • LOOPZ指令
    • 先判断CX是否为0,CX为0则退出循环,不等于0再接下去判断
    • 再判断ZF是否等于0,ZF等于1进入循环,ZF为0退出循环

LOOPNZ /NE指令

子程序调用及返回指令

CALL指令

  • 子程序调用指令CALL,CALL指令执行时会有以下操作
    • 先push rip
    • 再jmp LABLE
    • 段内调用只要把ip压入栈即可,段间调用要把ip、cs压入栈中
  • 段内直接调用、段内间接调用、段间直接调用、段间间接调用
1
2
3
4
5
6
格式:
CALL NEAR PTR LABLE
CALL R16/M16 (R16表示16位寄存器,M16表示)
CALL FAR PTR LABLE
CALL MEM
# 不写伪指令默认段内
  • 段间调用
1
2
3
4
5
6
7
8
call kkk
相当于:
push cs
push ip
先将cs压入栈中,再将ip压入栈中,因为cs表示地址的高位
# call kkk 下一条指令的cs:ip=ABCDH:3416H
# kkk函数的cs:ip=1234H:5678H

image-20240422184112398

RET指令

  • 子程序最后执行的指令,用于返回到父程序中
1
2
3
4
5
6
7
ret 
pop ip
pop cs

ret n
sp=sp+n

示例

1
2
3
4
5
6
7
8
9
10
11
MOV AL,20H
CALL PROC1

PROC1: MOV AH,0
MOV CL,0
DIV CL
OR AL,30H
MOV BH,AL
OR AH,30H
MOV BL,AH
RET

处理器控制指令

NOP指令

  • 空操作指令NOP
  • 常用于延时操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
下面是51单片机的软件延时:
DELAY100MS: ;@12.000MHz
NOP
NOP
NOP
PUSH 30H
PUSH 31H
PUSH 32H
MOV 30H,#4
MOV 31H,#166
MOV 32H,#207
NEXT:
DJNZ 32H,NEXT
DJNZ 31H,NEXT
DJNZ 30H,NEXT
POP 32H
POP 31H
POP 30H
RET

image-20240422190826990

LOCK指令

HLT指令

  • CPU执行到该指令时,会暂时停下来,查看运行结果

ESC指令

  • 交权指令,CPU控制计算机的总线,能够实现处理操作还有其他处理器,如:8087浮点数处理器等。
  • 使用该指令可以将总线的控制权交给其他处理器

WAIT指令

  • 等待指令,当需要引脚条件成立后才会执行,wait就是等待该条件满足

标志操作指令

对进位位操作

  • CLC:清零
  • STC置1
  • CMC取反

其他

  • CLD:DF位置零
  • STD:DF位置1
  • CLI:IF位置零
  • STI:IF位置1

端口操作指令

  • 8086端口和存储器完全分开
1
2
3
4
5
6
7
8
9
输入输出指令IN和OUT
指令格式:
n代表端口号
端口号在0~255时可以使用立即数
端口号大于255,必须将端口先送给DX,再进行IN操作
IN AL,n OUT n,AL
IN AX,n OUT n,AX
IN AX,DX OUT DX,AL
In AL,DX OUT DX,AX

中断指令和中断返回指令

中断向量表

  • 中断向量表存储着中断程序起始位的cs、ip
  • 中断源只允许有256个中断源
  • 中断向量表就需要内存256*4=1024
  • 中断向量表存储在内存的最低位 00000H~003FFH

INT指令

  • INT n(只允许256个中断源)
1
2
例如:
INT 80H; # pwn常用32位中断

其他

  • IRET指令:中断返回指令,中断执行完后返回中断之前的程序

  • INTO指令:

中断指令执行过程

  • 取中断类型号n,n*4作为中断向量表的指针
  • 将标志寄存器内容压入堆栈
  • 保护断点,将当前CS,IP压入堆栈
  • 备份TF标志,并将IF和TF清0,以禁止外部中断
  • 取中断处理器程序对待入口地址送IP、CS

字符串操作指令

串操作数的寻址

  • 源操作数用寄存器SI寻址,默认在数据段DS中,但允许段超越:ES: [SI]。
  • 目的操作数用寄存器DI寻址,默认在附加段ES中,不允许段超越:DS: [DI]。
  • 每执行一次串操作指令,SI和DI都会自动修改:±1(字节串)或±2(字串)。

串传送指令

  • SI所对应的地址内容,送到DI所对应的地址内容,自动完成SI和DI的自增或自减
1
2
3
4
5
6
7
8
9
指令格式:
MOVSB
MOVSW

等价于:
MOV AL,[SI]
MOV [DI],AL
INC SI
INC DI
  • 例如:将数据段DATA1指示的50个字节传送到本数据段指示的DATA2区域。
1
2
3
4
5
6
7
MOV SI,OFFSET DATA1
MOV DI,OFFSET DATA2
MOV CX,50
CLD
AGN: MOVSB
DEC CX
JNZ AGN

串存储指令

1
2
3
指令格式:
STOSB
STOSW
  • 例如:将附加段偏移地址为300H开始的100个存储单元全部清为0。
1
2
3
4
5
6
7
	 MOV AX,0
MOV DI,300H
MOV CX,50;
LCD
LOP: STOSW
DEC CX
JNZ LOP

串读取指令

  • 将SI所对应的地址内容给AL或AX,SI自增1或2
1
2
3
4
指令格式:
LODSB
LODSW

串比较指令

  • 俩串数据比较,[SI]和[DI]比较,[DI]-[SI]结果不回送,影响标志位
1
2
3
指令格式:
CMPSB
CMPSW
  • 例如:比较字符串STR1和STR2,字符串长度为20,如果相等AL置0,不相等AL置0FFH
1
2
3
4
5
6
7
8
9
10
11
12
     MOV SI, OFFSET STR1
MOV DI, OFFSET STR2
MOV CX, 20
CLD
AGN: CMPSB
JNZ NLT
DEC CX
JNZ AGN
MOOV AL,0
JMP EXIT
NLT: MOV AL,0FFH
EXIT:

串扫描指令

  • 从字符串里面去找关键字,实现应把要搜索的关键字放在AL或者AX里面
1
2
3
4
5
SCASB
SCASW

等价于:
CMP AL,[DI]
  • 例如:搜索STR字符串,查找是否有空格,如果有则转去FIN。
1
2
3
4
5
6
7
8
	 MOV DI,OFFSET STR
MOV AL,20H
MOV CX,COUNT
CLD
AGN: SCASB
JZ FIN
DEC CX
JNZ AGN

重复前缀指令

  • 重复前缀分2类,3条指令
    • 第一类:与MOVS、STOS和LODS指令配合的前缀REP,不影响标志位。
    • 第二类:与CMPS和SCAS指令配合的前缀REPZ和REPNZ,影响标志位
1
2
3
4
5
6
指令格式:REP MOVSB/REP STOSW
REPZ CMPS/REPZ SCAS
REPNZ CMPS/REPNZ SCAS
注意:
REP指令使串操作重复CX规定的次数,REPZ、REPNZ使串操作的重复可能提前结束,根据ZF值的变化
重复前缀和循环控制指令的差别:LOOP先做CX减1,后判断;REP先判断,后做CX减1。
  • 例如:比较字符串STR1和STR2,字符串长度为20,如果相等AL置0,不相等AL置0FFH.
1
2
3
4
5
6
7
8
9
10
	 MOV SI, OFFSET STR1
MOV DI, OFFSET STR2
MOV CX,20
CLD
AGN: REPZ CMPSB
JNZ NLT
MOV AL,0
JMP EXIT
NLT: MOV AL,0FFH
EXIT:
  • 例如:用SCASB指令,编程查找字符串STRING中是否包含$字符,若包含,FLAG单元置1;否则FLAG单元置0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
DATA SEGMENT
STRING DB N DUP(?)
N=$-STRING
FLAG DB ?
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,ES:DATA
START: MOV AX,DATA
MOV DS,AX
MOV ES,AX
MOV AL,'$'
MOV DI,OFFSET STRING
MOV CX,N
CLD
REPNZ SCASB
JNZ LAB1
MOV FLAG,1
JMP EXIT
LAB1: MOV FLAG,0
EXIT: INT 21H
CODE ENDS
END START

汇编伪指令

  • 伪指令是在程序汇编期间由汇编程序处理的操作。
1
2
3
4
[名字] 伪指令助记符 参数, 参数...[;注释]
名字:反映伪指令
参数
注释

关于结束的伪指令

END伪指令

  • END伪指令用于指示汇编器文件的结束。所有程序通常在代码段的结尾使用它。它还可以指定程序的入口点(起始地址)
  • 注意:END是标识源程序的结束而不是程序终止运行
1
END start

ENDP伪指令

  • ENDP伪指令用于指示过程(子程序)的结束。它通常与过程的开始伪指令 PROC 一起使用。
1
2
3
MyProcedure PROC
; Procedure code here
MyProcedure ENDP

ENDS伪指令

  • ENDS 伪指令在 x86 汇编语言中用于标志段(segment)的结束。段是内存中的一个连续区域,用于存储代码、数据或堆栈。段的定义从 SEGMENTSEG 伪指令开始,用 ENDS 结束。

ENDM伪指令

  • ENDM标志宏定义的结束。宏(Macro)。
  • 宏定义从 MACRO 开始,用 ENDM 结束。
1
2
3
4
5
6
7
8
MyMacro MACRO param1, param2
; 宏代码
mov ax, param1
mov bx, param2
ENDM

; 使用宏
MyMacro 1, 2

参数

  • 数值型参数
    • 常数
    • 符号常数定义伪指令(EQU、=)

数值表达式

  • 算数运算符
  • 逻辑运算符
  • 关系运算符,注意(真为-1,假为0)
  • 运算符的优先顺序

地址型参数

变量

ORG伪指令

  • 用来设置当前地址计数器的值
1
2
3
格式:ORG N
例如:ORG 30
数据从001EH开始存储

image-20240424084433687

EVEN伪指令

  • 使下一个变量或指令开始于偶数字节地址
1
2
3
格式:EVEN
例如:EVEN
WARY DW 20

ALING对齐伪指令

  • 使下面的内容变量必须从下一个能被NUM整除的地址开始分配
1
2
格式:ALIGN NUM; #NUM必须是2的幂
例如:

特殊运算符

PTR运算符

  • 强制类型运算符,用于给已分配的存储地址赋予另一种属性,仅在本语句有效,不影响原有属性。

THIS运算符

  • 配合**EQU或=**使用,
1
2
3
4
5
6
7
格式: 名字 EQU THIS 类型名
例如:
BARRAY EQU THIS BYTE
WARRAY DW 3344H
......
MOV AL,BARRAY
MOV AX,WARRAY

SHORT运算符

  • 短取代运算符,只用于JMP指令。
1
如: JMP SHORT NEXT

数值返回操作符

  • 这一类操作数不改变操作属性,只送操作数的某一属性值

OFFSET操作符

SEG操作符

TYPE操作符

  • 用于获取某个标识符(通常是变量或数据结构)的类型信息

  • 变量标号类型

1
2
3
4
5
6
7
DB = 1,DW = 2, DD = 4
NEAR = -1,FAR = -2

array DW 1, 2, 3, 4 ; 定义一个包含4个16位元素的数组
byte_var DB 'A' ; 定义一个单字节变量
mov ax, TYPE array ; AX 中将包含 2,因为每个 `DW`(double word) 是 2 个字节
mov bx, TYPE byte_var ; BX 中将包含 1,因为每个 `DB`(byte) 是 1 个字节

LENGTH操作符

汇编宏指令

1
2
3
LEA DX,STR1
MOV AH,9
INT 21H
  • 反复多次使用的代码段用宏指令来定义

宏指令

宏定义

  • 宏指令的使用包括宏定义、宏调用和宏展开
1
2
3
格式:宏定义名  MACRO [形式参数表]
宏定义体
ENDM
  • 示例
1
2
3
4
5
PRINt MACRO STR
LEA DX,STR
MOV AH,9
INT 21H
ENDM

宏调用

1
2
格式:宏指令名	[实参数表]

  • 示例
1
2
3
4
5
6
......
PRINT STR1
......
PRINT STR2
......
PRINT STR3

宏展开

汇编语言格式

  • 指令语句
  • 伪指令语句
  • 宏指令语句

段和模块定义伪指令

段定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
DATA SEGMENT
STRING DB N DUP(?)
N=$-STRING
FLAG DB ?
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,ES:DATA
START: MOV AX,DATA
MOV DS,AX
MOV ES,AX
MOV AL,'$'
MOV DI,OFFSET STRING
MOV CX,N
CLD
REPNZ SCASB
JNZ LAB1
MOV FLAG,1
JMP EXIT
LAB1: MOV FLAG,0
EXIT: INT 21H
CODE ENDS
END START

DEBUG

  • debug是DOS的调试工具,可以用来检查和修改内存和磁盘内容、执行汇编和反汇编等操作

  • 下面具体说明DEBUG的调试命令

A命令

  • 用于汇编语言输入
  • 语法:a [address]
  • address是可选参数,指定从哪个内存地址开始汇编。如果不指定地址,将从当前的CS寄存器取址
1
2
3
4
5
a 100
0A23:0100 mov ax, 0002;输入a 100后,就可以再该位置键入汇编指令
0A23:0103 int 21
0A23:0105

D命令

  • 用于显示内存内容
  • 语法: d [range]
1
2
3
4
5
6
可以直接查看地址
d 0040:0000
0040:0000 00 01 1F 10

也可以用寄存器查看所指地址
d DS:SI

E命令

  • 用于修改内存内容
  • 语法:e [address][data]
  • 按下回车键结束输入
1
e 0040:0000 4D 5A 90 00

F命令

G命令

H命令

I命令

L命令

M命令

N命令

O命令

P命令

Q命令

  • 退出debug调试
  • 语法: q
1
q

R命令

  • 显示或修改寄存器的值
  • 语法:r ax

S命令

T命令

U命令

W命令

系统功能调用

MS-DOS的调用过程

  • 将系统调用号送AN寄存器
  • 在指定的寄存器中设置有关入口参数
  • INT 21H;DOS的中断处理程序,对于不同的系统调用号,能够完成对不同设备的控制

返回DOS的系统调用

  • 调用结束当前正在执行的程序,返回DOS系统。无入口参数。
1
2
MOV AH,4CH
INT 21H

常用DOS系统调用

字符输入-01H

  • AH=01,从键盘输入一个字符。机器会等待键盘输入,输入字符的ASCII码存于AL中。
1
2
MOV AH,01
INT 21H

单字符输出-02H

  • AH=02,要显示的字符存于DL中,即入口参数DL=字符的ASCII
1
2
3
MOV AH,2
MOV DL,'a'
INT 21H ;输出字符

字符串输出-09H

  • AH=09,要输出的字符串的首地址存于DX寄存器中,即入口参数DS:DX=字符串首地址,字符串必须以$(24H)结尾。
1
2
3
MOV AH,09
LEA DX,BUF ;BUF为字符串首地址
INT 21H

字符串输入-0AH

  • AH=0AH,从键盘输入一个字符串。入口参数DS: DX=缓冲区首地址。用回车键表示输入结束;按Ctrl+Break或Ctrl+C键则中止

  • 从键盘输入字符串到DX所指的内存缓冲区

    • 缓冲区第一个单元:用户设定的最多输入的字符数(含回车)
    • 缓冲区第二个单元:用户实际输入的字符数(不含回车)
    • 缓冲区第三个单元:字符开始的地址
1
2
3
4
5
BUFF DB 10H,?,10H(?)
MOV DX,OFFSET BUFF
MOV AH,0AH
INT 21H
键入:0123456789

image-20240506194300121

汇编程序

顺序程序设计

存储单元内容移位

  • 将存储单元A中的内容左移4位,存储单元B中的内容右移2位(移位后的空位为0)

屏蔽与组合

  • 将字类型变量A的高4位和低4位置0,其余各位保持不变。

拆字

  • 将存储单元A中两个压缩BCD数拆成两个非压缩BCD数(高位BCD数放C单元中,低位BCD数放B单元中),然后分别转换为两个ASCII代码。

查表程序设计

加减运算

  • 已知Z=(X+Y)-(W+Z),其中X,Y,Z,W均为字节存储单元,存放的数据均用压缩BCD码表示。编写完成上式的程序段。

乘法运算

  • 将寄存器BL内容进行乘10运算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
MOV AL,10
MUL BL ;该程序需要70个时钟周期

MOV AL,BL
MOV AH,0
SAL AX,1
MOV BX,AX
SAL AX,1
SAL AX,1
ADD AX,BX ;只需15个时钟周期

MOV AL,BL
MOV AH,0
ADD AX,AX
MOV BX,AX
ADD AX,AX
ADD AX,AX
ADD AX,BX ;只需18个时钟周期


混合运算

  • 用顺序结构来编程实现求无符号数S=(x^2+Y^2)/Z的值,将最后结果放入RESULT单元保存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
DATA SEGMENT
X DB 5
Y DB 7
Z DB 2
RESULT DB ?
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA;
MOV AX,DATA
MOV DS,AX
MOV AL,X
MUL X
MOV BX,AX
MOV AL,Y
MUL Y
ADD AX,BX
DIV Z
MOV RESULT,AL
MOV AH,4CH
INT 21H
CODE ENDS
END START

求数的补码与反码

  • 将自变量A转换为补码和反码,分别存入自变量B和C中
1
2
3
4
5
6
7
8
9
10
11
MOV AX,A
NOT AX
MOV B,AX
INC AX
MOV C,AX
还可以
MOV AX,A
NEg AX
MOV C,AX
DEC AX
MOV B,AX

分支程序设计

  • 条件转移指令和无条件转移指令JMP用于实现程序的分支。

输入小写——输出大写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CODE SEGMENT
ASSUME CS:CODE
START: MOV AH,01H
INT 21H
JMP AL,'a'
JB EXIT
CMP AL,'z'
JA EXIT
SUBB AL,20H
MOV DL,AL
MOV AH,02H
INT 21H
EXIT: MOV Ah,4CH
INT 21H
CODE ENDS
END START

多分支程序设计

  • 多分支结构具有若干个条件,每个条件对应一个操作程序。程序必须判断

分段函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
DATA SEGMENT
X DB -10
Y DB ?
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START:MOV AX,DATA
MOV DS,AX
MOV AL,X
CMP AL,0
JGE BIG
MOV BL,-1
JMP EXIT
BIG: JE MIN
MOV BL,1
JMP EXIT
MIN: MOV BL:0
EXIT: MOV Y,BL
MOV AH,4CH
INT 21H
CODE ENDS
END START

循环程序设计

  • 单重循环程序,多重循环程序

统计正数和负数

  • 已知有n个有符号二进制数,存放在以BUF为首地址的字节存储区,试统计其中正数和负数的个数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
DATA SEGMENT
BUF DB -40,78,0,12,-19,41,-34
COUNT EQU $-BUF
PLUS DW ?
MINUS DW ?
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
BEGIN: MOV AX,DATA
MOV DS,AX
MOV BX,0
MOV DX,0
LEA SI,BUF
MOV CX,0
LOP: MOV AL,[SI]
CMP AL,0
JG P1
JE P2
INC DX
JMP P2
P1: INC BX
P2: INC SI
INC CX
CMP CX,COUNT
JL LOP
MOV PLUS,BX
MOV MINIUS,DX
MOV AH,4CH
INT 21H
CODE ENDS
END BEGIN

程序2

  • 编写程序段,求N1和N2两个数组对应的数据之和(数组中的数据个数为M),并将结果存入到新数组N3中。此计算一直进行到两数之和为0或数组结束,并将新数组的长度存放在N4单元中。
1
2
3
4
5
6
7
8
9
10
MOV CX,M
MOV BX,-1
L1: INC BX
MOV AL,N1[BX]
ADD AL,N2[BX]
MOV N3[BX],AL
LOOPNZ L1
JZ L2
INC BX
L2: MOV N4,BX

程序3

  • 试编一个程序实现S=1+2×3+3×4+…+N×(N+1)+…直到N(N+1)项大于100为止
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CODE SEGMENT
ASSUME CS:CODE
STARTL: MOV DX,1
MOV AX,0
MOV BL,2
NEXT: ADD DX,AX
MOV AL,BL
INC BL
MUL BL
CMP AX,100
JBE NEXT
MOV AH,4CH
INT 21H
CODE ENDS
END START

子程序设计

子程序定义

  • 子程序定义格式为:
1
2
3
4
子程序名	PROC [NEAR/FAR]
...... ;子程序体
RET
子程序名 ENDP

保护现场和恢复现场

示例

例题1:编写一个求两个正整数最大公约数的子程序
分析:求两个数的最大公约数可以用辗转相除法:用M除以N,得余数R,若R=0,则N就是所求的最大公约数;若R≠0,则N→M,R→N,继续求解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
GYS PROC
PUSH AX
PUSH BX
PUSH DX
G1:
XOR DX,DX
DIV BX
AND DX,DX
JZ EXIT
MOV AX,BX
MOV BX,DX
JMP G1
EXIT:
MOV CX,BX
POP DX
POP BX
POP AX
RET
GYS ENDP

子程序参数调用

image-20240520192058575

示例

例题1:两个6字节数相加。

1
2
DATA SEGMENT
ADD1 DB 0FEH

应用程序设计