汇编语言(四) - 程序结构

简介

掌握汇编语言程序设计方法

  • 顺序程序结构
  • 分支程序结构
  • 循环程序结构
    重点
  • 控制转移指令
  • 转移指令的寻址方式

编制汇编语言程序的步骤

分析问题,确定算法

  • 找出合理的算法及适当的数据结构

根据算法画出程序框图

  • 由粗到细把算法逐步地具体化

根据框图编写源程序
上机调试

1.顺序程序结构

无分支、无循环、无转移,按照 程序书写的先后顺序以直线方式 单条顺序执行
控制机制

  • CS:主存中代码段的段基地址
  • EIP:将要执行指令的偏移地址
  • 处理器自动增量EIP

设计要点

  • 如何选择简单有效的算法
  • 如何选择存储单元和工作单元

由于顺序程序结构就是一条线的往下执行,所以很简单,举几个例子也就明白了。
例1.1 自然数求和程序

  • 1+2+3+…+N=?
  • 等差数列求和:(1+N)*N/2
.data 
	num dword 3456 
	sum qword ? 
.code 
	mov eax,num 
	add eax,1 
	mul num 
	shr edx,1 		//无符号数,进行逻辑移位
	rcr eax,1 		//带进位的循环移位,保证edx的最低位移位到eax的最高位
	mov dword ptr sum,eax 
	mov dword ptr sum + 4,edx

例1.2 显示CPU的Vendor信息
CPUID指令:EAX=0,获取CPU的供应商信息,返回结果存储在EBX,EDX,ECX寄存器

include io32.inc 
	.data 
buffer byte 'The processor vendor ID is ',12 dup(0),0 
buffersize = sizeof buffer 
	.code 
start: 
	mov eax,0 
	cpuid 
	mov dword ptr buffer+buffersize - 13,ebx 
	mov dword ptr buffer+buffersize - 9,edx 
	mov dword ptr buffer+buffersize - 5,ecx 
	mov eax,offset buffer 
	call dispmsg 
	exit 0 
	end start

例1.3 复杂的表达式计算
编写程序完成表达式R=((XY+5)+4X)/Z的计算
X,Y,Z 均为DWORD类型的变量
汇编语言(四) - 程序结构_第1张图片

Mov eax,X 
Imul Y 
add eax,5 
Adc edx,0 
Mov ebx,eax 
Mov ecx,edx 
Mov eax,X 
mov esi,4 
imul esi 
add eax,ebx 
Adc edx,ecx 
Idiv Z 
Mov R,eax 
Mov R+4,edx

2.分支程序结构

目标:

  1. 理解分支结构的机器实现
  2. 掌握判断条件的实现与转移指令
  3. 了解分支结构优化方法

要点

  1. 条件的实现
  2. 条件流程控制
  3. 转移寻址和寻址方式

高级语言与汇编语言的if结构对比

高级语言

  • 采用if语句,并根据条件表达式的 结果转向不同的程序分支

汇编语言

无明确的高级逻辑结构硬指令,通 过比较和跳转指令组合实现

  1. 用比较cmp、and或sub操作影 响(修改)状态标志
  2. 用条件转移指令判断标志位, 并产生到新地址的分支(跳转到 新地址)
if (op1==0) 					Cmp op1,0
{								Je L1
 x=1; y=2; 						mov x,0
 }								mov y,0
 else							jmp L2
{							L1:
 x=0;y=0 ;						mov x,1
}								mov y,2
						    L2:

常用的影响状态标志的指令
逻辑指令

  • AND,OR,NOT,XOT,TEST

位运算

  • SHL,SHR,SAL,SAR,ROL,ROR,RCL,RCR

算数运算指令

  • ADD,ADC,SUB,SBB,CMP

CPU状态标志

汇编语言(四) - 程序结构_第2张图片

CMP指令

格式:

  • CMP dest, src

影响标志:

  • 溢出OF,符号SF,零ZF,进位CF,辅助进位AF,奇偶PF

汇编语言(四) - 程序结构_第3张图片

转移指令

流程控制机制:

  • 代码段寄存器CS指示代码段基地址
  • 指令指针寄存器EIP指示将要执行指令的偏移地址
  • 顺序执行,根据指令字节长度增加EIP
  • 分支执行,根据目的地址修改EIP或CS

分类

  • 转移范围:段内、段间
  • 寻址方式:相对、直接、间接
    汇编语言(四) - 程序结构_第4张图片
    1. 转移范围
    段内转移:当前代码段范围内的转移
  • 只改变EIP(偏移地址)
  • 近转移(Near)NEAR32,NEAR16
  • 短转移(Short)转移范围在127~-128字节

段间转移:不同代码段之间的转移

  • 更改CS(段地址)和EIP(偏移地址)
  • 远转移(Far) FAR32 ,FAR16
  • 32位线性地址空间,48位远转移FAR32
  • 实地址存储模型,32位远转移FAR16

2. 指令寻址方式
确定下一条指令的方法,操作数的形式
相对寻址方式(段内转移)

  • 指令代码提供目标地址相对于当前EIP的位移量
  • 操作数(位移量)=目标地址 - 当前EIP
  • 目标地址(新EIP值)= EIP + 位移量

直接寻址方式(段间转移)

  • 指令代码直接提供目标地址
  • 操作数 = 目标地址
  • 新的CS:EIP = 操作数高地址字:操作数低地址双字

间接寻址方式

  • 指令代码指示寄存器或存储单元,目标地址来自寄存器或存储单元
  • 操作数 = reg / mem
  • 目标地址 = [reg] /[mem]

2.1 无条件转移指令JMP

程序无条件改变执行顺序,相当于C/C++的goto
JMP指令的段内寻址方式:

  1. 段内转移、相对寻址
    标号指明目标地址,指令代码包含位移量
    JMP label ;JMP newaddr
    EIP ← EIP+位移量 –
  2. 段内转移、间接寻址
    通用寄存器或主存单元包含目标指令偏移地址
    JMP reg32/reg16 ;JMP ebx
    EIP ←reg32/reg16
    JMP mem32/mem16 ;JMP near ptr [ebx]
    EIP ←[mem32]/[mem16]
  3. 段间转移、直接寻址
    标号提供所在段的段选择器和偏移地址
    JMP label
    EIP = label的偏移地址
    CS=label的段选择器
  4. 段间转移、间接寻址
    32位线性地址空间用3字存储单元包含目标地址
    JMP m48 ;JMP far ptr [ebx]
    EIP = mem48
    CS = mem48+4
    16位实地址用双字存储单元包含目标地址
    JMP mem32 ;JMP far ptr [ebx]
    EIP = mem32
    CS = mem32+2

MASM会根据存储模式等信息自动识别
平展存储模式常用格式

1.相对寻址
– JMP label

  • JMP near ptr label
  • JMP label

2.寄存器间接寻址
– JMP reg32

  • JMP ebx

3.存储器间接寻址
– JMP mem32

  • JMP near ptr [ebx]

JMP相对寻址方式
格式:JMP label                         操作:EIP =EIP+位移量
位移量=label(目标地址)相对于当前EIP的字节数
范围:

  • 近转移(Near) 范围32位有符号数
  • 短转移(Short)范围 8位有符号数
include io32.inc
	.data
	.code
start:
	mov	eax,5
	cmp	eax,0
	jz	L1
	add	ebx,10
	jmp	near ptr L2
L1:
	sub	ebx,10
L2:
	exit 0
	end	start

无条件转移程序

.data
	nvar	dword	?
.code
start:
	jmp		labl1	//相对寻址
	nop
labl1:
	jmp		near ptr labl2
	nop
labl2:
	mov		eax,offset labl3
	jmp		eax		//寄存器间接寻址
	nop
labl3:
	mov		eax,offset labl4
	mov		nvar,eax
	jmp		nvar	//存储器简介寻址
	nop
labl4:
	exit 0
	end start

2.2 条件转移指令

不影响标志,利用标志
当状态标志条件为真时,转移到目标地址;否则,顺序 执行下一条指令
格式: Jcc label
label:目标地址,段内相对寻址

  • 32位IA32处理器, 32位的全偏移量

cc:一个或多个标志位的标志位条件

  • JC, JNC,JZ,JNZ,…
include	io32.inc
.code
start:
	mov		ebx,5
	cmp		eax,0
	jz		L1
	add		ebx,10
	jmp		L2
L1:
	sub		ebx,10
L2:
	exit	0
	end		start

条件转移指令分类
1.单状态标志类
基于特定标志位的值(单标志位)

  • JZ,JC,JP,JO,JS
  • JNZ,JNC,JNP,JNO,JNS

基于相等性( ZF位)

  • JE,JNE

2.组合状态标志类
无符号数的比较(ZF判断相对,CF位判断大小)

  • JA,JB,JNA,JNB,JAE,JBE…

有符号数的比较(ZF判断相对,SF和OF位判断大小)

  • JG,JL,JNG,JNL,…
    汇编语言(四) - 程序结构_第5张图片
    例 个数折半程序
    将某数组分成元素个数相当的两部分.
	Mov		eax,lengthof m1
	shr		eax,1
	jnc		is_even					jc	is_odd
									jmp	is_even
	add		eax,1			Is_odd:
									add	eax,1
Is_even:
	call	dispuid

优化
分支程序是影响程序性能的重要因素之一

Mov		eax,lengthof	m1
shr		eax,1
adc		eax,0
call	dispuid

当数组长度达到最大(eax=0ffffffffh)会怎样?
例 位测试程序
输入参数eax=1的cpuid指令可以获取CPU特性参数
返回参数edx的bit18代表是否支持PSN(Processor Serial Number)功能,1支持,0不支持

include	io32.inc
.data
	yes_msg	byte	'PSN supported:Yes',0
	no_msg	byte	'PSN supported:No',0
.code
start:
	mov		eax,1
	cpuid
	test	edx,040000h
	mov		edx,offset no_msg
	jz		disp
	mov		eax,offset yse_msg
disp:
	call	dispmsg
	exit	0
	end		start

2.3 单分支结构

类似于高级语言的if – then结构语句

  • 当条件满足,发生转移,跳过分支体
  • 条件不满足,顺序向下执行分支体

要点:与if语句相反

  • 转移指令,条件不成立执行分支体
  • if语句,条件成立执行分支体
    汇编语言(四) - 程序结构_第6张图片
    例 求绝对值
    汇编语言(四) - 程序结构_第7张图片
    例 小写字母转大写字母 ‘a’ = 61h ‘A’ = 41h
    汇编语言(四) - 程序结构_第8张图片

2.4 双分支结构

相当于高级语言的if – then - else语句
使用转移指令J(cc)和Jmp实现分支控制
优化为单分支结构

  • 预先执行使用频率较高、不影响判断标志的分支
    例 显示ebx中的最高位
    汇编语言(四) - 程序结构_第9张图片

2.5 多分支程序

分支处理中又有嵌套的分支,具有多个分支走向

  • 利用单分支和双分支结构实现多个分支结构

汇编语言(四) - 程序结构_第10张图片
汇编语言(四) - 程序结构_第11张图片

变量地址表程序

复合表达式的实现

C++语言:					汇编语言:				优化:  																													
if (a > b) && (b>c) 			Mov eax,b				Mov eax,b
{ 								Cmp a,eax				Cmp a,eax
 x=1; 							Ja L1					Jbe next
} 								jmp next				Cmp eax,c
							 L1: 						Jbe next
							 	Cmp eax,c				Mov x,1
								Ja L2 				Next:
								jmp next 
							L2: 
								Mov x,1 
							Next:    

3.循环程序结构

组成部分

  • 循环初始:为开始循环准备必要的条件,如循环次 数、必要的初始值;
  • 循环体:重复执行的程序代码,包括对循环条件的 修改;
  • 循环控制:判断循环条件是否成立,决定是否继续 循环

“先判断、后循环”的循环程序结构

  • 对应C/C++语言的while语句

“先循环、后判断”的循环程序结构

  • 对应C/C++语言的do语句

3.1 循环指令

汇编语言(四) - 程序结构_第12张图片

例 数组求和程序

//循环初始
	mov		ecx,lengthof array				//ECX = 数组元素个数
	xor		eax,eax							//求和初值为0
	mov		ebx,eax							//数组指针为0

//循环体
again:
	add		eax,array[ebx*(type array)]	//求和
	inc		ebx							//指向下一个数组元素

//循环控制
	loop	again
	mov		sum,eax						//保存结果

	call	dispsid						//显示结果

3.2 计数控制循环

通过次数控制循环

  • 利用LOOP指令属于计数控制
  • 常见是“先循环、后判断”循环结构

计数可以减量进行,即减到0结束
计数可以增量进行,即达到规定值结束

例 求最大值程序
汇编语言(四) - 程序结构_第13张图片

3.3 条件控制循环

根据条件决定是否进行循环

  • 需要使用有条件转移指令实现
  • 多见“先判断、后循环”结构

先行判断的条件控制循环程序

  • 很像双分支结构
  • 主要分支需要重复执行多次 (JMP的目标位置是循环开始)
  • 另一个分支用于跳出这个循环

先行循环的条件控制循环程序

  • 类似单分支结构,循环体就是分支体
  • 顺序执行就跳出循环

例 字符数字统计程序

	.data
string	byte 'Do you have fun with Assembly?',0		//以0结尾的字符串

	.code
start:	
	xor 	ebx,ebx		//EBx用来记录字符个数,同事也用于指向字符的指针
again:
	mov		al,string[ebx]
	cmp		al,0		//用指令“test al,al”更好
	jz		done
	inc		ebx			//个数加1
	jmp		again		//继续循环
done:
	mov		eax,ebx		//显示个数
	call	dispuid

	exit	0
	end		start

3.4 多重循环

实际的应用问题

  • 单纯的分支或循环结构
  • 循环体中具有分支结构
  • 分支体中采用循环结构
  • 循环体中嵌套有循环,即形成多重循环结构

如果内外循环之间没有关系

  • 比较容易处理

如果需要传递参数或利用相同的数据

  • 问题比较复杂

例 冒泡法排序程序

	mov ecx,count  	 	;ECX←数组元素个数 
	dec ecx  		 	;元素个数减1为外循环次数 
outlp:	
	mov edx,ecx   		;EDX←内循环次数 
	mov ebx,offset array 
inlp: 
	mov eax,[ebx]  		 ;取前一个元素 
	cmp eax,[ebx+1]  	 ;与后一个元素比较 
	jng next   ;前一个不大于后一个,不交换 
	xchg eax,[ebx+1] 	  ;否则,进行交换 
	mov [ebx],eax 
next: 
	inc ebx   			 ;下一对元素 
	dec edx 
	jnz inlp  			 ;内循环尾 
	loop outlp 			  ;外循环尾

你可能感兴趣的:(汇编语言)