汇编语言程序设计之统计元素个数
- 前言
- 题目描述
- 题目分析
- 参考代码
- 程序使用指令详解
- 六大标志位
- CF——进位标志位
- PF——奇偶标志位
- AF——辅助进位标志位
- ZF——零标志位
- SF——符号标志位
- OF——溢出标志位
- XOR指令
- OR指令
- LEA——取偏移地址指令
- LODSB——串装入指令
- 条件转移指令
- 程序执行过程详解
前言
统计元素个数这样的问题,思路其实很简单,用高级语言来实现的话,几行代码就可以搞定,但是如果要用汇编语言来实现的话,代码量会稍微多一些,但解题思路其实是一样的。
题目描述
在内存数据段处存放有10个8位带符号数,其首地址为MEM,试统计其中正元素、负元素和零元素的个数,并分别将个数存入PLUS、MINUS、ZERO这三个单元中。
题目分析
为了实现上述计数功能:
- 可先将PLUS、MINUS、ZERO这三个单元清零
- 然后将数据段中的数据依次放入AL中
- 利用OR指令让操作数自身相或
- 再用条件转移指令测试该数是正数、负数还是零
- 最后分别在对应的单元中计数。
参考代码
DATA SEGMENT
MEM DB 05H,00H,83H,12H,0FAH,77H,0CDH,9BH, 66H,00H
PLUS DB 1 DUP(?)
MINUS DB 1 DUP(?)
ZERO DB 1 DUP(?)
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START: MOV AX,DATA
MOV DS,AX
XOR AL,AL ;AL清零
MOV PLUS,AL ;PLUS清零
MOV MINUS,AL ;MINUS清零
MOV ZERO,AL ;ZERO清零
LEA SI,MEM ;数据首地址送入SI
MOV CL,10 ;数据长度送入CL
CLD
CHECK: LODSB ;AL←[DS:SI]即取一个数到AL
OR AL,AL ;操作数自身相“或”,AL内容不变,仅影响标志位
JS X1 ;若为负数,即SF=1时转X1
JZ X2 ;若为零,即ZF=0时转X2
INC PLUS ;否则为正,PLUS单元加1
JMP NEXT
X1: INC MINUS ;MINUS单元加1
JMP NEXT
X2: INC ZERO ;ZERO单元加1
NEXT: DEC CL ;数据长度CL加1
JNZ CHECK ;若CL的值不为0即ZF=0则转CHECK
MOV AH,4CH
INT 21H
CODE ENDS
END START
程序使用指令详解
- MOV指令的使用详解
六大标志位
在本程序中,使用OR指令、DEC指令或INC指令时,当条件满足时会使状态标志位发生改变
因此我们需要牢牢记住以下六个状态标志位发生改变的条件
CF——进位标志位
当进行加(减)法运算时,若最高位向前有进(借)位,则CF=1,否则CF=0
PF——奇偶标志位
当运算结果的低8位中1的个数为偶数时PF=1,为奇数时PF=0
AF——辅助进位标志位
在加(减)法操作中,D3向D4有进位(借位)时,AF=1,否则AF=0
DAA指令和DAS指令测试这个标志位,以便在BCD加法或减法之后调整AL中的值
ZF——零标志位
当运算结果为零时,ZF=1,否则ZF=0
SF——符号标志位
当运算结果的最高位为1时SF=1,否则SF=0
OF——溢出标志位
当算数运算的结果超出了带符号数的范围,即溢出时OF=1,否则OF=0
XOR指令
XOR是逻辑运算中的异或指令,其格式为:
XOR OPRD1,OPRD2
两个相同的操作数相异或,能使操作数置零:
XOR AX,AX ;AX 0,使AX清零
OR指令
OR是逻辑运算中的或指令,其格式为:
OR OPRD1,OPRD2
OR指令有下面三种应用:
- 实现两操作数按位相“或”的运算
OR AX,[DI]
- 使某些位不变,某些位置“1”
OR CL,0FH
- 在不改变操作数的 情况下使OF=CF=0
OR AX,AX
LEA——取偏移地址指令
LEA能将存储器操作数的16位偏移地址取出送目标寄存器
当程序中用符号地址表示内存偏移地址时,须使用该指令
指令格式:
LEA REG,MEM
源操作数必须是一个存储器操作数,目标操作数通常是间址寄存器
LODSB——串装入指令
串装入指令有3种格式:
LODS OPRD
LODSB
LODSW
该用于将内存某个区域的数据串依次装入累加器,以便显示或输出到接口,指令执行后会将[DS:SI]里的值送入AX或AL
条件转移指令
程序执行过程详解
进入断点调试:
核心代码从清零3个计数单元开始,首先来看一下初始状态:
要判断的10个值存放在数据段ds里,紧接着的是PLUS、MINUS、ZERO这三个单元,巧的是这三个单元的初始值为0,为了保险起见,我们需要用代码再给他们清零。首先让AL变为0,AL是AX的低八位:
接着把PLUS、MINUS、ZERO这三个单元清零:
下一步是把要比较的数据取出来,用LEA把数据的偏移地址拿到:
这里因为偏移地址就是0000,所以没有变化,但其实已经取到了偏移地址
接着就进入了程序的主要部分CHECK。
通过LODSB指令将数据取到AL中,于此同时si指向下一个数据:
使用OR指令让操作数AL与自身相或,注意看标志位的变化:
操作数05H与自身相或:
- 结果无进位:CF=0
- OR不改变操作数,即结果不为零:ZF=0
- 05H是正数,第一位为0,所以:SF=0
- OR运算无溢出,故:OF=0
- 05H的二进制表示为00000101B,即1的个数为偶数,故:PF=1
- OR运算无进位或借位,故:AF=0
因为SF=1,所以JS的条件不成立,程序往下执行:
因为ZF=0,所以JZ的转移条件不成立,程序继续执行,不跳转,PLUS加1:
此时经过一个无条件转移指令JMP,程序跳转至NEXT处执行:
外循环次数减1,因为CL没有减到0,运算结果不为0故ZF=0,条件成立,程序跳转到CHECK处执行:
如此往复循环,直至10个数全部遍历结束为止