汇编语言总复习(上)
本文将结合老师说的考试范围与PPT,对所涉及的知识点作个人总结,面向考试复习(高星,yyds)
顺序有点乱,如果有时间会再调一下。
一、子程序与主程序
(一)什么是子程序
把功能相对独立的程序段单独编写和调试,作为一个相对独立的模块供程序使用,就形成子程序
子程序可以实现源程序的模块化,可简化源程序结构,可以提高编程效率
参数传递是子程序设计的重点和难点
子程序的调用和返回是由指令CALL和RET(return)来完成的
•CALL指令分成4种类型(类似JMP)
CALL label ;段内调用、相对寻址
CALL r16/m16 ;段内调用、间接寻址
CALL far ptr label ;段间调用、直接寻址
CALL far ptr mem ;段间调用、间接寻址
返回时,RET直接从当前栈顶取内容作为返回地址。因此要保证RET指令执行前堆栈栈顶的内容刚好是返回的地址。
例:子程序的常见格式(实现回车换行的子程序):
注意:
- 对简化段定义格式,在tiny和small下,过程(PROC)的默认属性为near;对完整段定义格式下,过程的默认属性为near。
- 进行过程设计时,需要注意寄存器的保护和恢复,即开始时将要修改的寄存器内容压入栈,结束后再逆序弹出。
(二)如何进行参数传递
主程序在调用子程序时,通常需要提供一些数据,即入口参数(输入参数)
子程序执行结束后要返回主程序数据,即出口参数(输出参数)
参数的形式分为两种
① 数据本身(传值)②数据的地址(传址)
下面介绍如何传递参数
1.寄存器传递参数(共享寄存器)
把参数存于约定的寄存器中,可以传值,也可以传址。
上图的子程序将和存在了al中,返回后再将其mov到result中,实现了参数传递。
注:子程序对带有入口参数的寄存器可以保护,也可以不保护;上图未保护
子程序对带有出口参数的寄存器不能保护和恢复
2.变量传递参数(共享变量)
主程序和子程序直接采用同一个变量名共享同一个变量。
类比于C++中的加引用,直接改变了变量的值。
上图的子程序直接将al中的值mov到了result,随后返回到主程序。
3.堆栈传递参数(共享堆栈)
主程序将子程序的入口参数压入堆栈,子程序从堆栈中取出参数
子程序将出口参数压入堆栈,主程序弹出堆栈取得它们
前方高能
入口参数为数组的偏移地址和数组的元素个数,通过压入堆栈进行传递。
程序在保护bp后,将bp置为栈顶,并通过偏移位置来分别寻址获得压入的偏移地址和元素参数。最后按反序弹出栈内值和址。
上述程序堆栈的使用情况如下图
由于主程序压入了2个参数,使用了堆栈区的4字节,故采用上述办法平衡堆栈。
注:由于寄存器的保护也存于堆栈中,所以在子程序退出时要特别注意pop的顺序。
二、缓冲区
(一)如何定义缓冲区
例
该例表示在内存中申请一个缓冲区为83个字节,首地址给BUF。
缓冲区分为三个部分
第一个字节内放的是事先填入最多欲接收的字符个数(包括回车字符,可以是1~255),在例中为81,表示申请的存放数据的缓冲区的字节数为81个。
第二个字节 ‘ ?‘表示的是实际存放的字节个数(不包括回车)
就是说,你放入2个字节的数据,“?”变成2,放10个字节的数据,变成10);
DB表示的是分配一个或多个字节;
输入的数据(字符串)从第三个字节开始存放,存放至第82个字节,第81个字节存放回车符(0DH),0DH作为输入数据的结束。
DUP(0)表示的是存放数据的81个字节初始值全为0,即为:81 0 0 0 0······(第82个字节)0 ODH。
注:实际输入的字符数多于定义数时,多出的字符丢掉,且响铃
(二)如何使用缓冲区
下例实现了字符串的输入
三、串操作指令
串操作指令的操作对象是以字(W)为单位的字串,或是以字节(B)为单位的字节串
串操作指令采用了特殊的寻址方式
- 源操作数用寄存器SI间接寻址,默认在数据段DS中,即DS:[DI]
- 目的操作数用寄存器DI间接寻址,默认在附加段ES中,即ES:[SI]
- 每执行一次串操作,SI和DI将自动修改
- 源数据串可以段跨越,目的串不可。
注:由于要自动修改SI与DI,所以用户需要在进行串操作之前改变方向标志DF先确定增加或减小的方向
即:执行CLD(DF=0,主存地址增大,较为常用)或执行STD(DF=1,主存地址减小)
串操作经出配合重复前缀指令,通过计数器CX控制重复执行串操作指令的次数。
(一)串传送指令
①串传送 MOVSB/W(move string)
把字节从主存的源地址传送至目标地址
MOVSB 字节串传送:ES:[DI]←DS:[SI]
②串存储 STOSB/W(store string)
把AL或AX数据传送至目标地址
STOSB 字节串存储:ES:[DI]←AL(AL传字节)
STOSW 字串存储: ES:[DI]←AX(AX传字)
③串读取 LODSB/W(load string)
把指定主存单元的数据传送给AL或AX
LODSB 字节串读取: AL←DS:[SI]
LODSW 字串读取: AX←DS:[SI]
④REP重复前缀指令
REP ;每执行一次串指令,CX减1直到CX=0,重复执行结束
即:当数据串没有结束(CX≠0),则继续传送
(二)串检测指令
①串比较指令CMPS(compare string)
将主存中的源操作数减去至目的操作数,以便设置标志,进而比较两操作数之间的关系
CMPSB 字节串比较:DS:[SI]-ES:[DI]
②串扫描SCAS(scan string)
将AL/AX减去至目的操作数,以便设置标志,进而比较AL/AX与操作数之间的关系
SCASB 字节串扫描:AL-ES:[DI]
SCASW 字串扫描: AX-ES:[DI]
③REPZ/REPE重复前缀指令
每执行一次串指令,CX减1并判断ZF是否为0,
只要CX=0或ZF=0,重复执行结束。
即:当数据串没有结束(CX≠0),并且串相等(ZF=1),则继续比较
④REPNZ/REPNE重复前缀指令
每执行一次串指令,CX减1并判断ZF是否为0,
只要CX=0或ZF=0,重复执行结束。
即:当数据串没有结束(CX≠0),并且串不相等(ZF=0),则继续比较
REPNZ和LOOP的区别:
REPNZ是先执行一次指令,之后将CX减一,判断CX是否为0。
而LOOP是先将CX减一,然后判断CX是否为0,不为0则继续执行指令。
例:
在上例中,指令repz cmpsb结束有两种情况
第一种:ZF=0,即出现不相等的字符
第二种:CX=0,即比较完了所有字符
注意,在这种情况下,如果ZF=0,则说明最后一个字符不相等,ZF=1才能说明两个字符串相同。
所以,重复比较结束后,需要通过jnz指令 来判断zf位,若其=0,则字符串不相等。
总流程图如下
下例为SCAS在字符串中查找空格字符的用法
四、常用DOS命令
调用DOS功能一般方法如下:
1、在AH寄存器中设置系统功能调用号,说明选择的功能
2、在指定寄存器中设置入口参数。
3、用中断调用指令 INT 21H 执行功能调用。
(一)字符输出(02H号)
入口参数:DL=字符的ASCII码
注:该功能可以识别响铃字符(07H),回车(0DH),换行(0AH)。
(二)字符串输出(09H号)
入口参数:DS:DX=欲显示字符串在主存中的首地址。
注:顺序为先回车(0DH),再换行(0AH),且字符串应以’$’(24H)结束。
(三)字符输入(01H号)
出口参数:AL=字符的ASCII码
调用此功能时,若无键按下,则会一直等待,直到按键后才读取该键值
(四)字符串输入(0AH号)
入口参数:DS:DX=缓冲区首地址
关键要定义好缓冲区
五、8086的机器代码格式
机器码我都不怎么懂,直接搬PPT了= =
例1:
MOV AX,BX ;机器代码是 89 D8(16进制)
第1个字节89是操作码(含w=1表示16位操作)
第2个字节D8(11 011 000)是 “mod reg r/m”
reg=011表示目的操作数为BX
mod=11时,为寄存器寻址方式。
mod=11和r/m=000表示源操作数为AX
例2:
MOV AL,[BX+SI+6] ;机器代码是 8A 40(16进制) 06
前一个字节8A是操作码(含w=0表示8位操作)
中间一个字节40(01 000 000)(将16进制转为2进制,然后按2 3 3拆开)是 “mod reg r/m”字节
reg=000表示目的操作数为AL
mod=01时,为带有8位位移量的存储器寻址方式
mod=01和r/m=000表示源操作数为[BX+SI+D8]
最后一个字节就是8位位移量[D8]=06
六、常用的ASCII码
回车:0DH 换行:0AH 响铃:07H
空格:20H
数字0~9:30H~39H
大写字母A~Z:41H~5AH
小写字母a~z:61H~7AH
七、BCD码
Binary Coded Decimal(二进制编码的十进制数),即一个十进制数位(0-9)在计算机中用4位二进制编码表示。常用的BCD码是8421BCD码,即用4位二进制编码的低19个编码表示0-9这十个数字,如图。
例:0100 1001 0111 1000.0001 0100 1001 十进制真值:4978.149(上图对应)
00111010B=3AH F2H=11110010B
10H=00010000B=16D ABH=10101011B=171D
压缩BCD码:一个字节表达两位BCD码 10000111B(87H)
非压缩BCD码:一个字节表达一位BCD码(低4位表达数值,高4位常设置为0)
00001000 00000111B(0807H)
八、变量的定义
(一)符号常数
利用一个标识符表达的一个数值
例:
注:在5.x版本中,字符串是用引号括起来的
例:calldos equ "int 21h"
EQU用于数值等价时不能重复定义符号名,但”=“允许有重复赋值
X=7 X EQU 7 都是正确的
X=X+5 但 X EQU X+5是错误的
(二)变量定义伪指令
变量定义(Define)伪指令为变量申请固定长度的存储空间,并可同时将相应的存储单元初始化
其汇编格式为:变量名 伪指令 初值表
变量名:
- 变量名为用户自定义标识符,表示初值表首元素的逻辑地址;用这个符号表示地址,常称为符号地址
- 变量名可以没有,此时无符号地址。
初值表:
初值表是用逗号分隔的参数
主要由数值常数、表达式或?、DUP组成
?——表示初值不确定,即未赋初值
DUP——表示重复初值
DUP的格式为:重复次数 DUP(重复参数)
(三)定义实例
1.字节单元定义实例
DB伪指令用于分配一个或多个字节单元。初值表中每个数据一定是字节量(Byte),存放8位数据。
2.字单元定义实例
DW伪指令用于分配一个或多个字单元。初值表中每个数据一定是字量(Word),存放16位数据。
3.字变量和字常量的应用(重要)
即:字变量在程序中当作mem,即取其偏移地址元素的数值。
结语
只花了一天的时间去复习整理汇编语言,希望别挂科就好
后面看到这篇的人一定要好好学汇编啊。
张海英你没有心(确信)