简介:本篇旨在对汇编的基础知识进行汇总,汇总的都是要点(简明扼要),目前还在更新中。
第一章 基础知识
1.1 存储单元
一个存储单元存储的信息量以bit为单位(最小)。而转换关系中8 bit=1 bytes(B)。而一个字=两个字节
在微型计算机存储器的存储单元中,一个单元可以存1 B(即8个进制位)。而一个存储器有128个单元,则可以存储128 B。
1.2 CPU对存储器的读写
要读写,则应该与三类信息进行交互:
- 地址信息
- 控制信息
- 数据信息
而逻辑上又分为3类总线,分别传输信息:
- 地址总线(指出内存中的信息放在那里(自下而上读01))
- 控制总线(若要传输的数据多于一次性传输的宽度,则分开来多传几次)
- 数据总线
1.2.1 影响性能的因素:
宽度(线的条数):它决定了部件的能力(速度,控制能力等)。8086为16条,8088为8条
若CPU的寻址能力为8KB,则总线宽度:8*1024/1 = $2^13$ (注意单位是B,13则为总线宽度)
第二章 寄存器
2.1 通用寄存器
有AX BX CX DX四种,它们分为高位和低位以兼容以前的程序,如(AX = AH(高) + AL(低))
要注意的是:若只用AL进行运算,若超出其范围,超出的数据不会到AH当中!
2.2 CPU给出物理地址的方法
段地址(SA):只是为了内存的管理,并不是真分段。16位地址分段,一段64KB
偏移地址(EA):16位地址最多64KB
公式:物理地址=段地址*16+偏移地址
2.3 段寄存器
有CS DS SS ES四种。CS常存储段地址。它们提供了一下功能:
- CS:CPU要执行指令的地址。相当于命令指针
- DS:读取内存的地址(返回数据)。相当于数据指针
- SS:存储内存中的栈的顶。相当于栈顶指针
2.3.1 CS和IP
IP是存储基于CS的偏移量。修改的流程:
一般流程:读取指令 -> 修改 IP+=命令长度 -> 执行指令(可能会涉及修改CS:IP) -> 读取指令…
8086PC启动时在FFFF0H单元中读取指令执行
2.4 疑问
1. 为什么偏移地址只能在64Kb内?
一个16位的CPU,只能有1111 1111 1111 1111,代表了4个16进制数,所以能表示2**16 bit的数据,恰好,就是64Kb。
2.暂留
第三章 寄存器(内存访问)
3.1 内存中字的存储
字单元的概念:即存放一个字型数据的内存单元,由两个地址连续的内存单元(一个8位)组成。高放高位,低放低位。
这里解释一下: 若有单元[0]->2e,[1]->33,那么我们读[0]的字型数据为:332eH(高放高位)
3.2 DS和[address]
“[…]”表示一个内存单元,表示在段地址下的偏移量。如10000H = 1000:0 = [0]
在masm汇编编辑器中,如果单纯写mov ax,[0],那么他会把[0]当做0来解释,即指令变成:mov ax,0
而若用bx作为中转,那是可以的!如:1
2mov bx,0
mov ax,[bx]这个是可以把[bx]当成段地址,返回ds:bx下的数据的!
而如果偏要用[idata]的话,那么就必须加上ds:前缀,如:1
mov ax,ds:[0]
而对于Debug编译器的话,ds是自动加上的,用
1
mov ax,[0]
即可,如果使用[]的话,而且他(段寄存器)是不能直接用mov命令改变的,必须通过其他的寄存器中转才能改变。
3.3 字的传送(mov指令)
在传送字之后, DS和[address]也会跟着变,如1000:0,若传入一个fffeH,则address会从0->2,此时,[0]存有feH,[1]存有ffH,[2]为空。
3.4 mov、add、sub指令
主要是说说置零问题,与机械码长度问题,稍微提一下,以后会细讲。
举例子:
1 | sub bx, bx |
两个都是可以将bx寄存器置零的指令,但是sub的机械码长度为2,而mov为3,用sub更好。
另外说说jmp指令,他是转移指令,具有一个操作对象的指令,而上面的具有两个。
3.5 栈
他是一个FILO结构(先入后出),而且是从大变小(地址)。
3.5.1 push和pop
执行push ax命令,会把ax中的数据传入ss:sp指向的地址空间。此时sp-=2
执行pop ax命令,会把ss:sp指向的地址空间中的数据传给ax。此时sp+=2
执行之后的指针变化:
- push先令sp-=2,然后将ax中的数据放进去。称为出栈。
- pop是先取出数据放到ax中,再sp+=2。称为入栈。
3.5.2 数据传输
如8086cpu的入栈和出栈都是以字为单位进行的。
3.5.3 栈指针
上面已经用到了:段寄存器:寄存器存储(ss:sp)
sp是指偏移地址,ss:sp始终指向栈顶。
3.6 栈顶越界问题
栈溢出问题都是大问题,如果栈顶越界了,那么执行push和pop会受影响吗?当然不会!直接刚,写!写!写!出!出!出!管你呢。
所以就会有可能读了其他程序的数据,或者修改了(覆盖)其他程序的数据(直接崩溃的说)
执行情况(取自《汇编语言第二版》王爽著)
3.7 问题
1.程序与数据有区别吗?
可以说有,也可以说没有。由于指令都是0和1组成的,只是用不同功能的段地址读出来的不一样,如,CS和DS都指向10000H,那么,CS会将里头的数据转化为机械码,执行指令,而DS就直接读数据,作为单纯的数据使用。
2.空栈如(10000H~1000FH)此时ss=1000H,那么sp是多少?
因为是自下而上(自大到小),所以sp=0010H。
换一种方式看,若存储了一个数据,则一个存到000EH,一个存到000FH,此时的栈指针在栈顶,当然指向000EH了,此时是已经让sp-=2了,那么回溯一下,不就有sp=0010H了。
- 好了,问题来了,如果将(10000H~1FFFFH)作为空间呢?
答案:sp=0000H,因为,不能变成10000H(笑)
3.栈会溢出,那么,为什么不设置栈大小
emmm,可惜8086cpu就是没有,所以变成自己注意。
4.只有mov可以完成传输数据吗?
push也可以哦,只要把sp定位为ds:[address]那就好了哦。直接写进去。
第四章 第一个程序
4.1 一个源程序从写出到执行的过程
执行过程包括:写程序->编译连接成可执行文件->执行。
可执行文件文件中包括以下两部分:
- 程序和数据
- 相关的描述:程序多大,占用多少运行空间
4.2 源程序
4.2.1 伪指令
也就是只能被编译器识别的指令,如assume、segment、end等,这是没有机械码的。
一般的伪指令使用如下:
1 | assume cs:code #声明(假设)cs:段名(标号) |
4.2.2 程序返回
现在考虑,如何让上一个程序把CPU控制权交给写一个程序,这时候就要使用程序返回
其代码如下:
1 | mov ax, 4c00H |
4.3 程序生成流程
编程 -> 1.asm -> 编译 -> 1.obj -> 连接 -> 1.exe -> 加载 -> 内存中的程序 -> 运行
注:加载就是将程序加载到内存,执行就是在内存中执行程序的指令
4.4 怎么装载程序?
操作系统是一个模块组成庞大的系统。任何通用的系统都要提供一个称为shell的程序,用户使用这个程序来操作计算机系统进行工作。
如DOS中有一个程序command.com,这个程序称为:命令解释器,也就是DOS中的SHELL
(其实跟我们的cmd.exe差不多)
先运行shell,然后在加载1.exe可执行程序的时候,cmd会自动把cpu的控制权交给1.exe,执行完成再返回cmd。
4.4 展示EXE的加载过程
第五章 [BX]和loop指令
5.1 [BX]
这个之前就讲过:其实就是把BX中的值作为ds的偏移地址EA,然后可以用这个来访问特定地址下的数据。
如ds:1000H,bx=0001H,那么[BX]就是指ds*16+bx下的值。
5.2 loop
直接上程序:
1 | assume cs:eloop |
解释:
- loop是按照cx当中的值进行对有标号的s下的命令进行循环的。当cx不等于0,则执行循环,这个程序实际上是实现128*36的程序。
- s实际上是IP值,指向s标记的地址,所以loop s实际上是给IP赋值:IP=s,然后就可以通过cs:ip来执行指令了
注意:汇编程序中,数据不能以字母开头,所以要在前面加个0。比如:9138h可以写成9138h,而A000h就必须写成0A000h
5.3 loop和[bx]的联合运用
例如来一个如同C++语言中的:
1 | int dx = 0; |
那么汇编语言就有:
1 | assume cs:code |
好了,现在来说说这段代码里面有什么需要注意的点:
- cx寄存器一般用作loop循环的判断条件,即循环的粗次数。
- ax寄存器的初始化有两步,是,因为一个字符型数据是8位这样的话转化为16位就得让高8位的值赋为0。
- dx寄存器是常用的累加寄存器。
- inc指令是让后面的值加一的指令,相当于C++语言中的‘++’自增。
- int指令:
由int 指令引发的中断是一种重要的内中断。
格式: int n //相当于引发一个n号中断的过程,最终功能和call指令相似,都是调用一段程序。- 取中断类型码n
- 标志寄存器入栈,并IF=0,TF=0 //TF=0使得避免中断程序执行过程中引发单步中断
- CS,IP寄存器入栈
- IP=(n*4) , CS=(n*4 + 2)
原文链接
5.4 段前缀与其使用
- 能够显示地指令内存单元的段地址的,如:”ds:”、”cs:”、”ss:”、”es:”。这就是段前缀。
- 默认的[x]是指:(dx*16)+x,而如果要用其他的段寄存器,则需要加上它的段前缀。
第六章 包含多个段的程序
由于软件是由数据和程序组成的,数据当然也是程序里面必不可少的部分,所以会将代码分为多段进行,如数据段、代码段等。
6.1 在代码段中使用数据
汇编程序若没有特殊的说明(或者注明在哪里开始),则默认从头开始!
所以,一旦你的程序头部有数据,则将会把头部的数据翻译为机器码执行。(冤)
那么解决方式是:在开始执行代码的地方加上 start:
1 | assume cs:code |
6.2 将数据、代码、栈放入不同的段
上面说了,代码从哪里开始就在哪里加上start,结束就用end start(其他标识也行)
好,记住,那是代码,别把接下来说的混淆了。
不同的段可以放不同的东西,例如数据,代码等等。
只要记住将段名end了再定义其他段。也只有定义了,才能使用这个段
1 | assume cs:b,ds:a,ss:c |
第七章 更灵活的定位内存地址的方法
之前的[0]和[bx]均可以起到定位的作用,现在有更灵活的定位方式:
7.1 and和or指令
and是有零变零,而or是有一变一
也可以想象成and是乘法,or是加法
7.2 以字符型给出数据
凡是以’…’形式给出数据的,都被译为是字符型数据,这样,编译器会将里面的数据变成ASCII码值。具体实例:
1 | db 'unIX' |
这里被译为
1 | db 75H,6EH,49H,58H |
而大小写转换的方法有两种:
- 基于比较的,在ASCII码中,大写字母+20H=小写字母
- 基于二进制数的,在ASCII表达字符时,若第五位为0,则为大写字母;若第五位为1则为小写字母(这里可以用or或者and来进行转换)
7.3 以[bx+idata]的方式寻址
现在以例题进行解析,将第一组字符串变成大写,第二组变成小写
1 | assume cs:codesg,ds:datasg |
- 原来的思想:取[bx]中的字符,再对其进行and 11011111b操作,再放回去,然后将bx自增,再重新···,而变成小写就or 00100000b。(这里要执行两次循环啊,一个变大写,一个变小写)
- 然而我们可以使用以下思想,更加便捷,就是[bx+idata]:思想和前面的一样,但是,不用执行两次循环,只要一次就够了:这个其实,学过高级语言都知道这方法,只不过,没想到汇编也能做吧,汇编语言也不是那么死板的。
1
2
3
4
5
6
7
8
9
10
11
12
13mov ax,datasg
mov ds, ax
mov bx,0
mov cx,5
s:mov al,[bx] ;定位第一个字符串的第bx个字符
and al,11011111b ;执行完变大工作
mov [bx],al ;放回去
mov al[5+bx] ;定位第二个字符串的第bx个字符(因为一个字符串只有5个字符)
or al,00100000b ;执行变小工作
mov [5+bx],al ;放回去
inc bx ;自增
loop s
7.4 si与di寄存器和多重循环
这个只要知道是16位寄存器,功能和bx差不多,可以用作[bx+si+idata]和[bx+di+idata]就可以了。其中,idata是常量,其他的为变量。这个结构可以执行二重循环,相当于处理二维数组。
需要注意的是二重循环的实现:
- 问题一:cx寄存器是用作循环的判定条件的,那么二重循环对于没有两个cx的我们来说,如何是好?
将之前的cx用其他寄存器保存起来啊!- 问题二:二重循环可以那样做,那么多重循环呢!?你只有有限个寄存器。
利用内存空间!开辟一段dw 0,让后指向它,放进去就完事了。- 问题三:那这个方法的前提是我得知道有多少个循环,很麻烦啊,换个简单的。
- 在需要暂存数据的时候我们都应该使用栈*
- 在需要暂存数据的时候我们都应该使用栈*
- 在需要暂存数据的时候我们都应该使用栈*
按顺序push,然后逆向按顺序pop
第八章 数据处理的两个基本问题
这两个问题是:
- 处理的数据在什么地方
- 要处理的数据有多长
8.1 bx、si、di和bp
这几个都用过,主要说说bp:这个只要在[…]里使用了它,没有段地址声明,那么默认是ss
8.2 机器指令处理的数据在什么地方
在指令执行前,所要处理的数据可以在三个地方:
- CPU内部
- 内存
- 端口
下面举例子:
上图还表明了汇编语言中数据位置的表达 - 立即数(idata):如上图的最后一个例子,是直接赋值的
- 寄存器:如上图的第二个例子,使用寄存器名。
- 段地址:如果是[bx]则默认段地址是ds,如果是[bp]则默认段地址为ss,当然可以显性给出。
8.3 指令处理的数据长度
这个得看具体例子。
- 如果是ax,bx,那么这个就是字操作
- 如说是al,bl,那么就是字节操作
- 在没有寄存器名存在的情况下,用操作符X ptr指明内存单元的长度,X在汇编中可以为word或byte(且必须显式声明)。
1 | mov word ptr ds:[0],1 ;字 |
- 有些指令默认了访问的是字还是字节,如push默认进行字操作,而用[…]的是字单元操作(一个8位)。
8.4 div指令
div是除法指令,使用div做除法的时候应注意以下问题.
- 除数:有8位和16位两种,在一个reg或内存单元中。
- 被除数:默认放在AX或DX和AX中
- 如果除数为8位,被除数则为16位,默认在AX中存放;
- 如果除数为16位,被除数则为32位,在DX和AX中存放,DX存放高16位,AX存放低16位。
- 结果:
- 如果除数为8位,则AL存储除法操作的商,AH存储除法操作的余数:
- 如果除数为16位,则AX存储除法操作的商,DX存储除法操作的余数。
格式如下:
1 | div reg |
8.5 实例
利用除法指令计算100001/100
分析:
- 由于100001大于整数最大值(16位)65535,不能用ax寄存器存放,所以只能用dx和ax两个寄存器联合存放,共32位
- 而出书100小于255(8位),但是因为被除数是32位,所以除数应该用16位的来存放除数100
所以有程序:
1 | mov dx,1 |
执行后,(ax)=03E8H(即10000),(dx)=1(余数为1)。
8.6 伪指令dd
之前有学过用db和dw定义字节型数据和字型数据。dd是用来定义dword(double word,双字)型数据的。
- db 占1个字节
- dw 占1个字(即两个字节)
- db 占2个字(即四个字节)
8.7 dup
dup是一个操作符,在汇编语言中与dw,db,dd一样,都是由编译器识别处理的符号,且与它们一同配合使用,用来进行数据重复。
如:
1 | db 3 dup (0) |
第九章 转移指令的原理
可以修改IP 或同事修改CS和IP的指令统称为转移指令统称为转移指令。如jmp、jno等,即可以控制CPU指令内存中某处代码的指令。
具体分类有:
- 无条件转移指令(jmp)
- 条件转移指令(jno)
- 循环指令(loop)
- 过程
- 中断
还有其他的分类:
只修改IP:段内转移(jmp ax)
同时修改CS和IP:段间转移(jmp 1000:0)
短转移
近转移
9.1 操作符offset
offset是编译器处理符号,功能:取得标号的额编译地址。
如:
1 | start: mov ax, offset start ;相当于mov ax,0 |
解析:第一条指令mov … 是三个字节,所以s段的offset为3
9.2 jmp指令和依据译为进行转移的jmp指令
9.2.1 jmp short 标号(转移到标号出执行指令)
1. short标号下,它属于段内短转移,对IP的修改范围为-128~127。
9.2.2 立即数在机器码中的表示
在一般的汇编指令当中,汇编指令的idata(立即数),不论他是表示一个数据还是内存单元的偏移地址,都会在相应的机器指令中出现,因为CPU执行的是机器指令,他必须处理这些数据或地址。
1 | mov ax,0123h B8 23 01 |
9.2.3 jmp转移的机制(是怎么实现转移的)
1 | start: mov ax,0 |
- 首先我们应该知道,jmp指令是2个字节,如代码中的jmp机械码是:EB 03 。这样的机械码,没有包含s段的地址!所以将jmp指令写入内存之后,IP=IP+2=0008h。
- 但是,执行完jmp后,IP=000Bh。因为执行JMP,就是执行IP=IP+(EB之后的偏移量)=IP+3=000Bh。
- 所以jmp执行的是给IP加偏移地址!。
所以 jmp short 标号 的功能为:(ip)=(ip)+8位位移
9.2.4 继续上面的,8位位移是怎么得出来的?
- 8位位移=标号处的地址 - jmp指令后的第一个字节的地址.
- short指明此处的位移为8位位移;
- 8位位移的范围为-128-127,用补码表示
- 8位位移由编译程序在编译时算出。
还有一种和”jmp short标号”功能相近的指令格式:
jmp near ptr标号
它实现的是段内近转移。功能为:(IP)=(LP)+16位位移。
(1) 16位位移=标号处的地址 - jmp指令后的第一个字节的地址:
(2) near ptr指明此处的位移为16位位移,进行的是段内近转移:
(3) 16位位移的范围为一32768~32767,用补码表示:
(4) 16位位移由编译程序在编译时算出。
9.2.5 jmp far ptr 标号(段间转移/远转移)
是不是所有的jmp机械码都没有段数据呢?当然不是!小标题就说到了:far ptr,这个指令用标号的段地址和偏移地址修改CS和IP
如:
解释:“OB 01 BD 0B”是目的地之在指令中的存储顺序:
高地址:“BD 0B”是段地址 0BBDH ;
低地址:“0B 01”是偏移地址 010BH ;
9.2.6 转移地址在寄存器的jmp指令(这就相当于总结了,上面废话有点多)
- 若只是一个字,则只是偏移地址(直接EB ??完事)
- 若是两个字,那么一个是段,一个是偏移地址。
9.3 jcxz指令
该指令为有条件转移指令,所有的有条件转移指令都是短指令,即上面9.2.6说的1个字,只有位移(偏移地址),对ip的修改范围是:-128~127.
指令格式:jcxz 标号
如果(cx)=0,则转移到标号这
9.4 loop指令
也是短转移指令,pass
9.5 编译器对转移位移超界的检测
编译的时候就会出错!因为,这超界,编译器可识别不了。这不存在的玩意儿。
第十章 CALL和RET指令
10.1 ret和retf
它们都是用栈中的指令,而ret是改变ip,而retf是改变cs:ip,实现远转移。
而正是因为它们是取栈中的指令,所以要和push 和pop结合起来,而ret或者retf执行完后,相当于执行了指令pop
如:
1 | mov ax stack ;在前面声明的stack数据段,共16B |
这里就相当于执行了刚放进去的机器码。
10.2 call指令
- 将打那个钱的IP或CS:IP压入栈中
- 转移
注意:call指令不能执行短转移,其实现方法与jmp指令相同
10.2.1 依据位移进行转移的call指令
call 标号(将当期那的ip压入栈后,转到标号处执行指令)
指令执行时会带有以下操作:
1 | 1. (sp)=(sp)-2 ;压栈 |
这个位移之前也说过了,不多讲。
看到上面的代码我们也能联想到jmp的执行了,相当于:
1 | push IP |
举个例子!
1 | 内存地址 机器码 汇编指令 |
也许你会想到:我没有push哪来的pop!?
- 在call之时已经有了push ip,此时的ip=提取call s之后指向下一个执行机器码(inc ax)的ip指针,也就是ip=6。
- 而当call转移到s,ip就变为call之后的ip。
- 此时再pop ax,也就是ax=6。
- 因为执行完s,没有回调指令pop ip。。。所以不会执行inc ax
10.2.2 转移地址在指令中的call指令
这个也就是段间转移了,是相对于上头说的段内转移
1 | call far ptr 标号 |
此时会执行cs和ip双压栈
1 | 1. (sp)=(sp)-2 ;压栈 |
举个例子:
1 | 1000:0 b8 00 00 mov ax,0 |
ax是多少呢?
- call指令执行之后,stack中有cs和ip两个数据,CS在底部,IP在顶部。所以第一次pop ax,ax=ip=8
- 执行add后ax=16即10H
- 再执行pop,bx=1000H
- add指令执行后ax=ax+bx=1010H
10.2.3 转移指令在寄存器中的call指令
call 16位reg
功能:
1 | push IP |
这个跟10.2.1是不是很像?段内转移。例子:call ax(当ax=6,则转到cs:6处执行)
10.2.4 转移指令在内存中的call指令
call word ptr 内存单元地址
功能:
1 | push IP |
其实跟10.2.3没啥两样。
但是还有一种双字型:
call dword ptr 内存单元地址
功能:
1 | push CS |
举个例子就明白了
1 | mov sp,10h |
执行后:(CS)=0, (IP)=0123H, (sp)=0CH.
10.3 call与ret的配合使用(敲黑板!)
- call之时压ip到栈中!
- call完之后,执行转移后的指令
- 通过ret取出栈中的地址,执行转移操作!即回调作用!
- 回调完成就可以执行call之后的代码了!
分析例子:1
2
3
4
5
6
7
8
9
10
11
12
13assume cs:code
code segment
start: mov ax,1
mov cx,3
call s
mov bx,ax ;(b)=?
mov ax,4c00h
int 21h
s: add ax,ax
loop s
ret
code ends
end start我们来看一下CPU执行这个程序的主要过程。
(1)CPU将call s指令的机器码读入,IP指向了call s后的指令mov bx,ax.然后CPU执行call s指令,将当前的IP值(指令mov bx,ax的偏移地址)压栈,并将IP的值改变为标号s处的偏移地址:
(2) CPU从标号s处开始执行指令,loop循环完毕后,(ax)=8;
(3) CPU将ret指令的机器码读入,IP指向了ret指令后的内存单元,然后CPU执行ret指令,从栈中弹出一个值(即call s先前压入的mov bx,ax指令的偏移地址)送入IP中。则CS:IP指向指令mov bx,ax;
(4) CPU从mov bx,ax开始执行指令,直至完成。
10.4 mul乘法指令
之前说了div除法指令,现在说乘法指令。
10.4.1 乘法规则
这里跟div的除数和被除数不一样的是,乘法的两个数,要么都是8位要么都是16位!
- 乘法的两个数
- 如果是8位,一个放在AL中,一个放在8位的reg或内存字节单元中。
- 如果是16位,一个放在AX中,一个放在16位的reg或内存字节单元中。
- 乘法的结果
- 如果是8位乘法,结果放在AX中
- 如果是16位乘法,高位放DX,低位放AX
10.4.2 指令格式:
1 | mul reg |
格式说明:
因为有一个放在了al(以8位为例)
1 | mov al 1 |
这个就是用2*1了~
结果是ax=2(当然,我赋值的是十进制嘛)
10.4.3 用mul寻址
- mul byte ptr ds:[0]
含义:ax=al(ds16+0) - mul word ptr [bx+si+8]
含义:dx=
10.5 模块化程序设计
10.5.1 参数和结果的传递问题
也就是说,传进来的参数是啥,返回值是个啥。
- 传入的参数:
这个有很多,有用栈传递的,也有直接用内存单元或者寄存器。 - 返回值:
这个可以存在寄存器中返回跟传入参数的方法很像
而模块化设计,其实就是相当于c语言中的函数,把一个段中的代码当做一个函数执行。
10.5.2 寄存器冲突问题
冲突问题是什么呢?例如如果上面的模块使用的loop,即使用了寄存器cx,而call的模块也适用了loop,即也使用了cx,那么两者就将共用cx,肯定会出错!
解决办法有:
- 让别的调用者调用其他寄存器(这个很难实现,你也不知道他会用到什么寄存器)
- 不要使用会冲突的寄存器(这个不可能实现,只是说说理想罢了)
- 用栈,压进去,保存,call完在出栈!(这个OK哦)
10.6 课后
下面的程序执行后,ax和bx中的数值为多少?
1 | assume cs:codesg |
实验10-1 编写子程序 之 显示字符串
在这次实验中,我们将要编写3个子程序,通过它们来认识几个常见的问题和掌握解决这些问题的方法.同前面的所有实验一样,这个实验是必须独立完成的,在后面的课程中,将要用到这个实验中编写的3个子程序。
显示字符串
问题
显示字符串是现实工作中经常要用到的功能,应该编写一个通用的子程序来实现这个功
能.我们应该提供灵活的调用接口,使调用者可以决定显示的位置(行、列)、内容和颜色。
子程序描述
名称:show str
功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
参数:(dh)=行号(取值范围。-24), (dl)=列号(取值范围0-79),
(cl)=颜色,ds:si指向字符串的首地址
返回:无
应用举例:在屏幕的8行3列,用绿色显示data段中的字符串。
提示
- 子程序的入口参数是屏幕上的行号和列号,注意在子程序内部要将它们转化为显存中的地址,首先要分析一下屏幕上的行列位置和显存地址的对应关系:
- 注意保存子程序中用到的相关寄存器:
- 这个子程序的内部处理和显存的结构密切相关.但是向外提供了与显存结构无关的接口。通过调用这个子程序,进行字符串的显示时可以不必了解显存的结构,为编程提供了方便。在实验中,注意体会这种设计思想。
- 其实这玩意,就是抢占了本来应该在屏幕中输出的系统信息。那么设置行列号的,就是占的信息的位置。
点击查看参考代码
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61assume cs:code,ds:data
data segment
db 'Welcome to masm!',0
data ends
code segment
start:
mov dh,8 ;行号
mov dl,3 ;列号
mov cl,07h ;白色字
mov ax,data
mov ds,ax
mov si,0 ;循环加入字
call show_str
mov ax,4c00h
int 21h
show_str:
push cx ;保存用到的寄存器
push si
push es
push di
push bx
mov ax,0b800h
mov es,ax
mov al,0a0h ;一行的总列数160字节
dec dh ;行号减1,因为是从0开始的
mul dh ;计算行开始偏移地址
mov bx,ax
mov al,2
mul dl ;计算列
sub ax,2 ;列也是从0开始,而且一个字符占两个字节
add bx,ax ;求出开始位置
mov di,0
mov al,cl
mov ch,0 ;高8位为0
s:
mov cl,ds:[si] ;判断是否到了字符结束
jcxz ok
mov es:[bx+di],cl
mov es:[bx+di+1],al
inc si
add di,2
jmp short s
ok:
pop bx
pop di
pop es
pop si
pop cx
ret
code ends
end start
解决除法溢出问题
问题:
其实就是如果商超过了应该存储商的寄存器的大小,应该怎么办。emmm可以用大一点的,用dword
第十一章 标志寄存器
这是一种特殊的寄存器,一共16位:
- 用来存储相关指令的执行结果
- 用来为CPU执行相关指令提供行为依据
- 用来控制CPU的相关工作方式
其分布如下:
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
* | * | * | * | OF | DF | IF | TF | SF | ZF | * | AF | * | PF | * | CF |
其中*表示都没用:1 3 5 12 13 14 15在8086CPU中都没有使用。
注意:对标志位有影响的指令有:add,sub,mul,div,inc,or,and等,而没有影响的有:mov,push,pop等,它们大多数为传送指令。
11.1 标志表
名称 | 代号 | 作用 | 备注 |
---|---|---|---|
零标志 | ZF | 判断结果是否为零 | 若为0则ZF=1,否则ZF=0 |
奇偶标志 | PF | 判断结果中1的个数是奇是偶 | 若1的个数为奇数则PF=0,否则为1 |
符号标志 | SF | 判断结果是正是负 | 若为正数则SF=0,否则为1 |
进位标志位 | CF | 在进行无符号运算时记录是否有向最高位进位或者借位 | 那是假想的最高位 |
溢出标志 | OF | 判断是否超过容器所能装的最大数 | 如al=98+99=197,197>127溢出,实际结果:-59 |
方向标志 | DF | 控制每次操作后si、di的增减 | df=0 每次si,di增加;df=1 每次si,di减少。经常用作字符串的传送 |
11.2 与标志有关的指令
11.2.1 与CF相关的:adc,sdd指令
- adc:带进位加法。如:adc al,8h == al+8h+CF
- sdd:带借位减法。如:sdd al,8h == al-8h-CF
11.2.2 与CF和ZF有关的:cmp指令(比较指令)
指令格式:cmp s1,s2
举个例子:cmp ax,bx
比对结果如下:
情况 | 结果分析 | 结果 |
---|---|---|
(ax)=(bx) | 则(ax)-(bx)=0 | zf=1; |
(ax)≠(bx) | 则(ax)-(bx)≠0 | zf=0; |
(ax)<(bx) | 则(ax卜(bx)将产生借位 | cf=1; |
(ax)≥(bx) | 则(ax)-(bx)不必借位 | cf=0; |
(ax)>(bx) | 则(ax)-(bx)既不必借位,结果又不为0 | cf=0并且zf=0; |
(ax)≤(bx) | 则(ax)-(bx)既可能借位,结果可能为0 | cf=1或zf=1. |
11.2.3 与CMP有关的比较结果条件转移指令
指令 | 含义 | 简记 | 符号 | 监测的相关标志位 |
---|---|---|---|---|
je | 等于则转移 | equal | = | zf=1 |
jne | 不等于则转移 | not equal | ≠ | zf=0 |
jb | 低于则转移 | below | < | cf=1 |
jnb | 不低于则转移 | not below | ≥ | cf=0 |
ja | 高于则转移 | above | > | cf=0且zf=0 |
jna | 不高于则转移 | not above | ≤ | cf=1或zf=1 |
注意,这个一般是与cmp配合使用,如同call和ret一样,但是没有说一定要配合使用。
11.2.4 与DF有关指令
指令 | 功能 | 解释 |
---|---|---|
movsb | mov es:[di],byte ptr ds:[si] | 从ds中取字符放到es指定的位置中,单位为字节,会令di和si同时±1 |
movsw | mov es:[di],word ptr ds:[si] | 从ds中取字符放到es指定的位置中,单位为字,会令di和si同时±2 |
cld | 设置df=0,正向 | 令di,si向增方向 |
std | 设置df=1,逆向 | 令di,si向减方向 |
rep | 重复指令,根据CX当中的值,与LOOP相似 | 配合movsb使用 |
例子:
1 | 我们来看下面的两个程序。 |
11.3 标志位有关的问题
11.3.1 单纯就符号标志位就可以判断结果的正负吗?(即判断相减的两个数的大小)
当然不是!
当1-2=-1<0,则SF为1,表示负数,则前一个数比后一个数大
当34-(-96)=82H时,82H是-126的补码,所以SF=1,但是前一个数比后一个数大?当然不。
为什么呢?因为发生了溢出。
- 也就是说,保存结果的寄存器不足以表示那么大的数。
- 而34-(-96)=130明显超过127(假设保存结果的是ah)。
- 那么结果就会表示为-126(130-127=3,-128+3-1=-126)
- 而此时,SF=1,同时,OF=1。
- 所以要看结果的正负,要结合SF和OF的值.
总结:
标志 | 结果 | 备注 |
---|---|---|
SF=1,OF=0 | 负 | 略 |
SF=1,OF=1 | 正 | 因为溢出导致实际结果为负,那么逻辑上必定为正 |
SF=0,OF=1 | 负 | 因为溢出导致实际结果为正,那么逻辑上必定为负 |
SF=0,OF=0 | 正 | 略 |
11.4 pushf和popf
因为标志寄存器共16位,所以pushf就直接将16位当做寄存器放入栈中,popf是出栈。
注意,它们不需要加对象参数,入栈和出栈的对象都是标志寄存器!
第十二章 内中断
也就是CPU不再往下执行下去。而CPU内部的四种中断信息是:
- 除法错误,如div指令产生的除法溢出(中断类型码:0)
- 单步执行(中断类型码:1)
- 执行into指令(中断类型码:4)
- 执行int指令(指令格式:int n,其中n是中断类型码,是字节型立即数)
12.1 中断一条龙(中断程序,中断向量表,中断过程)
中断过程:
- 取得中断类型码(即中断向量表中的中断程序位置)
- 标志位入栈(因为执行中断过程要改变标志寄存器的值)
- 设置寄存器的TF和IF的值为0
- CS内容入栈
- IP内容入栈(结束中断程序回调的时候用,和call差不多)
- 从内存地址为中断类型码*4和中断类型码*4+2的两个字单元中读取中断处理程序的入口地址设置IP和CS。(用来取得中断程序在内存中的位置)
其实就是:
- get N
- pushf
- TF=0, IF=0
- push CS
- push IP
- (IP)=(N*4), (CS)=(N/4+2)
- 执行程序!
12.2 中断处理程序,安装,中断向量等
这个只是稍微述说,额,因为我不太能用到这个,作为了解而已。
中断处理程序就如同除法溢出程序一样(输出‘…overflow’),如何处理后续的事物做中断处理,然后返回系统程序。
而如果想要自己编一个中断处理程序,首先
- 把中断码想好,把程序要放的地方想好,*放入中断向量表
- 编程序,安装(写到相应位置)
- 测试
这里当然要用到之前学的几乎所有内容(包括ret,movsb等)
12.3 单步中断和TF,DF标志
说到这里,你看标题就知道,TF肯定与单步中断有关,具体有什么关系呢?
首先说说单步中断,也就是不停止(退出)运行的程序,却能返回其实时的寄存器信息(也就是还在运行,只不过暂时停止了)。
而TF就是实现单步中断的重要标志,当TF=1时将引发单步中断,所以要想中断就先置TF=1。
这就是为什么中断执行(刚开始)就要把TF置0,就是为了防止步步中断!
12.4 int指令
int指令引发的内中断很重要。虽然其过程与12.1所说的别无二致。
接下来就深入理解理解int、iret和栈:
12.4.1 int、iret和栈
第十三章 汇编语言重点知识总结
13.1寄存器与存储器
- 寄存器功能
- 寄存器的一般用途和专用用途
- CS:IP 控制程序执行流程
- SS:SP 提供堆栈栈顶单元地址
- DS:BX(SI,DI) 提供数据段内单元地址
- SS:BP 提供堆栈内单元地址
- ES:BX(SI,DI) 提供附加段内单元地址
- AX,CX,BX 和CX 寄存器多用于运算和暂存中间计算结果,但又专用于某些指令(查阅指令
表)。 - PSW 程序状态字寄存器只能通过专用指令(LAHF, SAHF)和堆栈(PUSHF,POPF)进行存取。
- 存储器分段管理
- 解决了16 位寄存器构成20 位地址的问题
- 便于程序重定位
- 20 位物理地址=段地址* 16 + 偏移地址
- 程序分段组织: 一般由代码段,堆栈段,数据段和附加段组成,不设置堆栈段时则使用系统
内部的堆栈。
- 堆栈
- 堆栈是一种先进后出的数据结构, 数据的存取在栈顶进行, 数据入栈使堆栈向地址减小
的方向扩展。 - 堆栈常用于保存子程序调用和中断响应时的断点以及暂存数据或中间计算结果。
- 堆栈总是以字为单位存取
13.2 指令系统与寻址方式
- 指令系统
- 计算机提供给用户使用的机器指令集称为指令系统,大多数指令为双操作数指令。执行指令
后,一般源操作数不变,目的操作数被计算结果替代。 - 机器指令由CPU 执行,完成某种运算或操作,8086/8088 指令系统中的指令分为6 类: 数据传
送,算术运算,逻辑运算,串操作,控制转移和处理机控制。
- 寻址方式
- 寻址方式确定执行指令时获得操作数地址的方法
- 分为与数据有关的寻址方式(7 种)和与转移地址有关的寻址方式(4)种。
- 与数据有关的寻址方式的一般用途:
(1) 立即数寻址方式–将常量赋给寄存器或存储单元
(2) 直接寻址方式–存取单个变量
(3) 寄存器寻址方式–访问寄存器的速度快于访问存储单元的速度
(4) 寄存器间接寻址方式–访问数组元素
(5) 变址寻址方式
(6) 基址变址寻址方式
(7) 相对基址变址寻址方式
(5),(6),(7)都便于处理数组元素
. 与数据有关的寻址方式中,提供地址的寄存器只能是BX,SI,DI 或BP
. 与转移地址有关的寻址方式的一般用途:
(1) 段内直接寻址–段内直接转移或子程序调用
(2) 段内间接寻址–段内间接转移或子程序调用
(3) 段间直接寻址–段间直接转移或子程序调用
(4) 段间间接寻址–段间间接转移或子程序调用
13.3 汇编程序和汇编语言
- 汇编程序
- 汇编程序是将汇编语言源程序翻译成二进制代码程序的语言处理程序,翻译的过程称为汇
编。
- 汇编语言
- 汇编语言是用指令助记符,各种标识变量,地址,过程等的标识符书写程序的语言, 汇编语言
指令与机器指令一一对应。 - 伪指令,宏指令不是由CPU 执行的指令,而是由汇编程序在汇编期间处理的指令。
- 伪指令指示汇编程序如何完成数据定义,存储空间分配,组织段等工作。
- 宏指令可简化程序并减少程序书写量。
- 条件汇编伪指令的功能是确定是否汇编某段源程序,而不是实现程序分支,对未汇编的程序
将不产生相应的目标代码。 - 结构作为一种数据结构可将一组类型不同但有逻辑关联的数据组织在一起,便于整体处理
数据。 - 记录可用于提高存储单元的利用率,将若干不足一个字节或字且有逻辑关联的信息压缩存
放在一个字节或字中。 - 指令中的表达式在汇编期间计算,并且只能对常量或地址进行计算。
13.4 程序设计基础
- 分支程序设计
- 程序分支由条件转移指令或无条件转移指令实现
- 存放若干目的转移地址或跳转指令的跳转表常用于实现多路分支
- 条件转移指令只能实现偏移量为-128 至+127 字节范围的转移
- 无条件转移指令根据寻址方式可实现短转移(偏移量为-128 至+127 字节),段内转移,段间
转移。
- 循环程序设计
- 可由循环控制指令或条件转移指令组织循环结构
- 内层循环结构必须完全包含在外层循环结构内,并不能发生从循环结构外向循环结构内的
转移。
- 子程序设计
- 子程序中应保护寄存器内容,并正确使用堆栈, 成对执行PUSH 和POP 指令,保证执行RET
指令时堆栈栈顶为返回地址。 - 主程序可通过寄存器,参数表,或堆栈传递参数给子程序
- EXE 文件和COM 文件
- 二者都是可执行文件
- COM 文件源程序的特点是: 第一条可执行指令的起始存放地址必须是100H,不能分段,不用
定义堆栈,所有过程为NEAR 类型,直接用INT 20H 指令返回DOS。
- DOS 功能调用与BIOS 中断调用
- 二者都是完成DOS 系统提供给用户的输入/输出等常用功能,通过执行软中断指令完成一
次软中断服务。 - DOS 功能调用的中断服务程序是操作系统的一部分,存于RAM 中; 而BIOS 中断调用的中
断服务程序存放在ROM 中。
13.5 输入/输出与中断系统
- 输入/输出的方式
- 程序直接I/O 方式: 用IN 和OUT 指令直接在端口级上进行I/O 操作,数据传送方式分为无
条件传送方式和查询传送方式。 - 中断传送方式: 由CPU 响应中断请求完成中断服务。
- DMA 传送方式: 直接在存储器与外设之间传送数据。
- 有关中断的概念
- 中断、中断源、中断请求、中断服务、中断向量、中断向量表、中断响应过程、中断指令、
开中断、关中断、内部中断、外部中断、可屏蔽中断、非屏蔽中断。
- 键盘I/O、显示器I/O 操作
- 键盘的输入操作用BIOS 的16H 中断调用控制,也可直接访问60H 端口(数据端口), 61H 端
口(状态端口)检测键盘的按键操作。 - 对于特殊键(如Shift , Ctrl , Alt , NumLock , ScrollLock 等键)的按动情况,可以直接从来
40:17H 单元取得有关信息。 - 显示器的图形显示可以用BIOS 的10H 中断调用实现,另一种速度更快的方法是直接读写
视频缓冲区。
- 打印机I/O 操作由INT 17H 中断调用实现, 串行通讯口操作由INT 14H 中断调用实现。
CLD Clear the direction flag (set to forward direction)
将方向标志置0,使si 和di 增量,串处理从低地址向高地址处理
13.6 8088 汇编速查手册
13.6.1 数据传输指令
它们在存贮器和寄存器、寄存器和输入输出端口之间传送数据.
- 通用数据传送指令.
指令 | 描述 |
---|---|
MOV | 传送字或字节. |
MOVSX | 先符号扩展,再传送. |
MOVZX | 先零扩展,再传送. |
PUSH | 把字压入堆栈. |
POP | 把字弹出堆栈. |
PUSHA | 把AX,CX,DX,BX,SP,BP,SI,DI 依次压入堆栈. |
POPA | 把DI,SI,BP,SP,BX,DX,CX,AX 依次弹出堆栈. |
PUSHAD | 把EAX,ECX,EDX, EBX,ESP,EBP,ESI,EDI 依次压入堆栈. |
POPAD | 把EDI,ESI,EBP,ESP,EBX,EDX, ECX,EAX 依次弹出堆栈. |
BSWAP | 交换32 位寄存器里字节的顺序 |
XCHG | 交换字或字节.( 至少有一个操作数为寄存器,段寄存器不可作为操作数) |
CMPXCHG | 比较并交换操作数.( 第二个操作数必须为累加器AL/AX/EAX ) |
XADD | 先交换再累加.( 结果在第一个操作数里) |
XLAT | 字节查表转换.── BX 指向一张256 字节的表的起点, AL 为表的索引值(0-255,即0-FFH); 返回AL 为查表结果. ( [BX+AL]->AL ) |
- 输入输出端口传送指令.
指令 | 描述 |
---|---|
IN | I/O 端口输入. ( 语法: IN 累加器, {端口号│DX} ) |
OUT | I/O 端口输出. ( 语法: OUT {端口号│DX},累加器) |
输入输出端口由立即方式指定时, 其范围是0-255; 由寄存器DX 指定时,其范 | |
围是0-65535. |
- 目的地址传送指令.
指令 | 描述 | 例子 |
---|---|---|
LEA | 装入有效地址. | 例: LEA DX,string ;把偏移地址存到DX. |
LDS | 传送目标指针,把指针内容装入DS. | 例: LDS SI,string ;把段地址:偏移地址存到DS:SI. |
LES | 传送目标指针,把指针内容装入ES. | 例: LES DI,string ;把段地址:偏移地址存到ES:DI. |
LFS | 传送目标指针,把指针内容装入FS. | 例: LFS DI,string ;把段地址:偏移地址存到FS:DI. |
LGS | 传送目标指针,把指针内容装入GS. | 例: LGS DI,string ;把段地址:偏移地址存到GS:DI. |
LSS | 传送目标指针,把指针内容装入SS. | 例: LSS DI,string ;把段地址:偏移地址存到SS:DI. |
- 标志传送指令.
指令 | 描述 |
---|---|
LAHF | 标志寄存器传送,把标志装入AH. |
SAHF | 标志寄存器传送,把AH 内容装入标志寄存器. |
PUSHF | 标志入栈. |
POPF | 标志出栈. |
PUSHD | 32 位标志入栈. |
POPD | 32 位标志出栈. |
13.6.2 算术运算指令
指令 | 描述 |
---|---|
ADD | 加法. |
ADC | 带进位加法. |
INC | 加1. |
AAA | 加法的ASCII 码调整. |
DAA | 加法的十进制调整. |
SUB | 减法. |
SBB | 带借位减法. |
DEC | 减1. |
NEC | 求反(以0 减之). |
CMP | 比较.(两操作数作减法,仅修改标志位,不回送结果). |
AAS | 减法的ASCII 码调整. |
DAS | 减法的十进制调整. |
MUL | 无符号乘法. |
IMUL | 整数乘法.以上两条,结果回送AH 和AL(字节运算),或DX 和AX(字运算), |
AAM | 乘法的ASCII 码调整. |
DIV | 无符号除法. |
IDIV | 整数除法.以上两条,结果回送:商回送AL,余数回送AH, (字节运算);或商回送AX,余数回送DX, (字运算). |
AAD | 除法的ASCII 码调整. |
CBW | 字节转换为字. (把AL 中字节的符号扩展到AH 中去) |
CWD | 字转换为双字. (把AX 中的字的符号扩展到DX 中去) |
CWDE | 字转换为双字. (把AX 中的字符号扩展到EAX 中去) |
CDQ | 双字扩展. (把EAX 中的字的符号扩展到EDX 中去) |
13.6.3 逻辑运算指令
指令 | 描述 |
---|---|
AND | 与运算. |
OR | 或运算. |
XOR | 异或运算. |
NOT | 取反. |
TEST | 测试.(两操作数作与运算,仅修改标志位,不回送结果). |
SHL | 逻辑左移. |
SAL | 算术左移.(=SHL) |
SHR | 逻辑右移. |
SAR | 算术右移.(=SHR) |
ROL | 循环左移. |
ROR | 循环右移. |
RCL | 通过进位的循环左移. |
RCR | 通过进位的循环右移. |
以上八种移位指令,其移位次数可达255 次.
移位一次时, 可直接用操作码. 如SHL AX,1.
移位>1 次时, 则由寄存器CL 给出移位次数.
如MOV CL,04
SHL AX,CL
13.6.4 串指令
指令 | 描述 |
---|---|
DS:SI | 源串段寄存器:源串变址. |
ES:DI | 目标串段寄存器:目标串变址. |
CX | 重复次数计数器. |
AL/AX 扫描值. | |
D | 标志0 表示重复操作中SI 和DI 应自动增量; 1 表示应自动减量. |
Z | 标志用来控制扫描或比较操作的结束. |
MOVS | 串传送.( MOVSB 传送字符. MOVSW 传送字. MOVSD 传送双字. ) |
CMPS | 串比较.( CMPSB 比较字符. CMPSW 比较字. ) |
SCAS | 串扫描.把AL 或AX 的内容与目标串作比较,比较结果反映在标志位. |
LODS | 装入串.把源串中的元素(字或字节)逐一装入AL 或AX 中.( LODSB 传送字符. LODSW 传送字. LODSD 传送双字. ) |
STOS | 保存串. 是LODS 的逆过程. |
REP | 当CX/ECX<>0 时重复. |
REPE/REPZ | 当ZF=1 或比较结果相等,且CX/ECX<>0 时重复. |
REPNE/REPNZ | 当ZF=0 或比较结果不相等,且CX/ECX<>0 时重复. |
REPC | 当CF=1 且CX/ECX< >0 时重复. |
REPNC | 当CF=0 且CX/ECX<>0 时重复. |
13.6.5 程序转移指令
无条件转移指令(长转移)
指令 描述 JMP 无条件转移指令 CALL 过程调用 RET/RETF 过程返回. 条件转移指令(短转移,-128 到+127 的距离内)
( 当且仅当(SF XOR OF)=1 时,OP1<OP2 )
指令 | 描述 |
---|---|
JA/JNBE | 不小于或不等于时转移. |
JAE/JNB | 大于或等于转移. |
JB/JNAE | 小于转移. |
JBE/JNA | 小于或等于转移. 以上四条,测试无符号整数运算的结果(标志C 和Z). |
JG/JNLE | 大于转移. |
JGE/JNL | 大于或等于转移. |
JL/JNGE | 小于转移. |
JLE/JNG | 小于或等于转移. 以上四条,测试带符号整数运算的结果(标志S,O 和Z). |
JE/JZ | 等于转移. |
JNE/JNZ | 不等于时转移. |
JC | 有进位时转移. |
JNC | 无进位时转移. |
JNO | 不溢出时转移. |
JNP/JPO | 奇偶性为奇数时转移. |
JNS | 符号位为”0” 时转移. |
JO | 溢出转移. |
JP/JPE | 奇偶性为偶数时转移. |
JS | 符号位为”1” 时转移. |
- 循环控制指令(短转移)
指令 | 描述 |
---|---|
LOOP | CX 不为零时循环. |
LOOPE/LOOPZ | CX 不为零且标志Z=1 时循环. |
LOOPNE/LOOPNZ | CX 不为零且标志Z=0 时循环. |
JCXZ | CX 为零时转移. |
JECXZ | ECX 为零时转移. |
- 中断指令
指令 | 描述 |
---|---|
INT | 中断指令 |
INTO | 溢出中断 |
IRET | 中断返回 |
- 处理器控制指令
指令 | 描述 |
---|---|
HLT | 处理器暂停, 直到出现中断或复位信号才继续. |
WAIT | 当芯片引线TEST 为高电平时使CPU 进入等待状态. |
ESC | 转换到外处理器. |
LOCK | 封锁总线. |
NOP | 空操作. |
STC | 置进位标志位. |
CLC | 清进位标志位. |
CMC | 进位标志取反. |
STD | 置方向标志位. |
CLD | 清方向标志位. |
STI | 置中断允许位. |
CLI | 清中断允许位. |
13.6.6 伪指令
指令 | 描述 |
---|---|
DW | 定义字(2 字节). |
PROC | 定义过程. |
ENDP | 过程结束. |
SEGMENT | 定义段. |
ASSUME | 建立段寄存器寻址. |
ENDS | 段结束. |
END | 程序结束. |