【汇编】第四章问题与解答

汇编第四章问题与解答

1、伪指令语句与硬指令语句的本质区别是什么?伪指令有什么主要作用?

硬指令语句

能产生目标代码,cpu可以执行,能完成特定功能的语句,它主要由cpu指令组成

伪指令语句

不产生目标代码,它仅仅在汇编过程中告诉汇编程序应如何汇编。

如:告诉汇编程序已经写出的汇编语言源程序有几个段,段名字是什么。

它还可以定义变量,定义过程伪指令是汇编程序在汇编时用的

2、汇编语句有哪4步骤?分别利用什么程序完成?产生什么输出文件?

源程序编辑

源程序汇编

将程序翻译成机器代码,对test.asm文件进行语法检测,如果无错误,则生成test.obj文件

目标文件的连接

将一个或多个目标文件和库文件合成一个可执行文件,将多个.obj文件连接后生成.exe文件

可执行文件的调试

直接运行或者利用调试工具debug text.exe

3、定义源程序

简化段

 .model small    ;定义程序的存储模式,小程序用小型模式
 .stack          ;堆栈段
 .data           ;数据段
     string db 'hello',0dh,0ah,'$'
 .code           ;代码段
 .startup        ;程序开始位置
     mov dx,offset string    ;字符串的首地址给dx
     mov ah,9    ;调用输出功能
     int 21h     ;21中断,返回到dos
 .exit 0
     end

完整段

 stack segment stack
     dw 512 dup(?)
 stack ends
 data segment
     string db 'hello',0dh,0ah,'$'
 data ends
 code segment 'code'
     assume cs:code,ds:data,ss:stack     ;调用assume伪指令,将cs,ds,ss依次指向code,data,stack逻辑段
 start:
     mov ax,data         ;因为assume伪指令不会给ds赋值,所以程序开始,要把data首地址给ds
     mov ds,ax
     
     mov dx,offset string        ;字符串的首地址给dx
     mov ah,9        ;调用输出功能
     int 21h
     mov ax,4c00h    ;返回dos
     int 21h
 code ends
 end start

4、汇编语言的表达式和运算符

符号常量

符号常量是利用一个标识符表达的一个数值

等价EQU伪指令

符号名 equ 数值表达式

符号名 equ <字符串>

  • 同一个符号名只能定义一次数值表达式

  • 这个字符串可以是一条处理器指令

如:

 calldos     equ     <int 21h>
 doswritechar    equ     2

那么原始程序

 mov ah,2
 int 21h

可以变成

 mov ah,doswritechar
 calldos

等号=伪指令

格式:符号名=数值表达式

功能:“=”伪指令给符号定义一个数值

 x=7
 x=x+5

EQU不能重复定义符号名,但‘=’允许重复赋值

高低分离符

取出数值的高半部分或低半部分

HIGH 、LOW 从一个字数值或符号常量中得到高、低字节

 mov ah,high 1234h   ;等价于mov ah,12h

变量及其属性

变量定义伪指令

格式:[变量名] 伪指令初值表

初值表是用逗号分隔的参数,主要由数值常量表达式DUP组成

  1. “?”表示:初值不确定

  2. DUP可以定义重复初值

格式:重复次数 DUP(被重复参数)

定义字节单元DB

功能:定义变量类型为BYTE,给变量分配字节

初值表的每个数据一定是字节量(byte),可以是0-255无符号数-127~+127有符号数,或者是字符常量

如:

 data segment
     x db    'a',-5
       db    2 dup(100),?
     y db    'ABC'
 data ends
对应C 43h 0007h
B 42h 0006h
A 41h 0005h
? - 0004h
100 64h 0003h
100 64h 0002h
-5 FBH 0001h
a 61h 0000h
定义单元DW

功能:定义变量的类型为WORD,给变量分配一个或多个字单元

初值表中的每个数据一定是字,一个字单元可以用于存放任何16位数据;如两个字符0~65535之间的无符号数或-32768~+32768之间的有符号数

data segment
	count	dw	8000h,?,'AB'
	maxint equ 64h
	number dw maxint
	array dw maxint dup(0)
data ends
对应值 存储单元 偏移地址
00h
00h 000Ah
00h
ARRAY 00h 0008h
00h
NUMBER 64h 0006h
‘A’ 41h
‘B’ 42h 0004h
-
- 0002h
80h
COUNT 00h 000h
  • “AB”按“高对高,低对低原则,即:低位字节在前,高位字节在后

  • DW语句定义字符串只能包含一个或两个字符,如果字符多于两个时,必须使用DB语句

DB可以定义“ABCD”,在堆栈中,0000h给A,0001h给B,0002h给C,0003h给D

DW可能定义“AB”,在堆栈中,0000h给B,0001h给A

变量和标号属性

地址操作符
offset操作符

格式:offset 名字/标号

返回名字/标号的偏移地址

seg操作符

格式:seg 名字/标号

返回名字/标号段地址

举例

字节变量ARRAY段地址和偏移地址给DS和BX

mov	ax,seg array
mov	ds,ax
mov bx,offset array		;等价于lea	bx,array
类型操作符
PTR操作符

格式:类型名 ptr 名字/标号

类型名可以是“byte/word/dword/fword/qword/tbyte/struct/record/union

功能:让名字/标号具有指定的类型,可以临时修改名字/标号的类型

mov al,byte ptr w_var		;w_var是一个字变量,byte ptr使其变成字节变量

简化段定义伪指令

.stack堆栈段伪指令

格式:.stack [大小]

stack创建一个堆栈段,段名是stack,默认1KB

.data数据段伪指令

.data

.data ?

用来定义具有初值的变量

.code代码段伪指令

.code [段名]

.code伪指令创建一个代码段,参数是指定代码段段名。

程序开始伪指令

.startup

.exit [返回数码]

返回操作系统

汇编结束伪指令

end[标号]

指示汇编程序到此结束

完整段定义的格式及其伪指令

完整段定义

段名 segment [定位] [组合] [段字] [类别]

……

段名 ends

定位

指定逻辑段在主存储器的边界

byte,word,dword,para,page

段组合

不同逻辑段之间的关系

private,public,stack,common,at

指定段寄存器伪指令

assume 段寄存器:段名 [,段寄存器:段名,……]

用来通知masm用指定段寄存器来寻址对应的逻辑段。

复杂数据类型

结构体类型

定义

结构名 struct

……

结构名 ends

student	struct
	sid	dw	?
	sname	db	‘abc’
	math	db	0
	english	db	0
student ends
结构变量定义

变量名 结构名 <字段初值表>

stu1	student	<1,‘aaa’,85,99>
student	100 dup(<>)	;预留100个结构变量空间
结构变量应用

结构变量名.结构字段名

mov stu1.math,90

宏定义和调用

宏定义

宏名 macro [形参表]

宏定义体

endm

如:

mainbegin	macro
	mov ax,data
	mov ds,ax
endm

宏调用

.code
start:
	mainbegin	;宏调用,直接写名字即可
end start

宏定义中,可以有宏调用,如

dosint21 macro funciton
	mov ah,function		;宏参数
	int 21h
endm
dismsg macro message
	lea dx,message
	dosint21	9		;调用了上个宏
endm

宏参数

shl可以对数据进行左移1位或cl位,但调用不是很方便。可以定义一个宏指令shlext ,[数据],[移动位数]

shlext	macro shloprand,shlnum
	push	cx
	mov 	cl,shlnum
	shl		shloprand,cl
	pop 	cx
endm

如果AX左移6位时,可以采用如下宏指令:

shlext	ax,6
;完整展开如下
push cx
mov cl,06
shl ax,cl
pop cx

除了宏指令,还可以是宏参数

dstring	macro	string
	db	'& string&',0dh,0ah,'$'
endm

5、汇编语言分支程序设计

无条件转移

程序的寻址由CS和IP两个部分完成

格式:

jmp dest ;目的地址/寄存器/存储器地址

jmp reg/m16

根据目的地址相对于转移指令的位置,分为:短转移、段内转移、段间转移

短转移

Short,段内距离-128~127

段内转移

near,只要CS不变,但IP变换

段间转移

far,cs和IP都要变换

举例

jmp		near	ptr		table[bx]		;段内转移,ip=[table+bx][table+bx+1]
jmp		far		ptr		table[bx]		;段间转移,ip=[table+bx][table+bx+1],cs=[table+bx+2][table+bx+3]

同样,也可以根据word/dword来区分

jmp 	word	ptr		[bx]	;段内转移,ip=[bx][bx+1]
jmp		dword	ptr		[bx]	;段间转移,ip=[bx][bx+1],cs=[bx+2][bx+3]

有条件转移

无符号下

je/jz zf=1:若两个数相等或这个数为0则转移

jne/jnz zf=0:若两个数不相等不为0则转移

ja/jnbe cf=0 and zf=0:若高于或不低于等于则转移

jae/jnb cf=0:若高于等于则转移

jb/jnae cf=1 and zf=0:若低于若不高于等于则转移

jbe/jna cf=1:若低于等于则转移

有符号下

je/jz zf=1:若两个数相等或这个数为0则转移

jne/jnz zf=0:若两个数不相等不为0则转移

======以下不同======

jg/jnle sf=of and zf=0:若大于或不小于等于则转移

jge/jnl sf=of:若大于等于或不小于则转移

jl/jnge sf!=of:若小于或不大于等于则转移

jle/jnge sf!=of or zf=1:若小于等于或不大于则转移


分支程序举例

判断AX2+BX+C=0是否有实根,若有则将字节变量TAG置1,否则置0。假设A、B、C均为字节变量,数据范围-128~+127

解:
.model	samll
.stack
.data			;定义各种数据
	_A	db	?
	_B	db	?
	_C	db	?
	TAG	db	?
.code
.startup
	mov 	al,_B
	imul	_B		;乘积结果在ax
	mov		bx,ax	;乘积结果放到bx
	mov 	al,_A	
	imul	_C		;乘积在ax,ax里面是ac
	mov 	cx,4	;设置乘以4
	imul	cx		;现在ax里面是4ac
	cmp		bx,ax	;比较满足条件?
	jae		next	;满足条件就跳转
	mov		tag,0	;否则置0
	jmp		out
next:
	mov 	tag,1	;满足条件就置1
out:
	mov 	ax,4c00h
	int 	21h
.exit	0
end

循环指令

格式:loop label

功能:执行loop指令时,处理器行将CX中计数值减1,再判断如果CX不为0,则转到LOOP指令中的目标语句;否则退出循环。

如果CX初值为0,则循环216,因为先将0-1,得到了216,判断不为0,于是开始循环。

举例

冒泡法排序

.model	small
.stack
.data
	array	db	56h,12h,11h,51h		;随便几个初值
	count	equ	($-array)/type array	;获得数组长度
.code
.startup
	mov		cx,count
	dec		cx			;设置外循环次数,为数组长度
outloop:
	mov		dx,cx		;设置内循环次数,等于当前 cx 大小
	mov		si,offset	array
inloop:
	mov		al,[si]		;当前数给al
	cmp		al,[si+1]	;比较当前数与下一个数
	jbe		next		;如果当前数小于等于下一个数,不用交换
	xchg	al,[si+1]	;当前数大于下一个数,进行交换
	mov		[si],al
next:
	inc		si			;指向下一个数
	dec		dx			;内循环次数减一
	jnz		inloop
	loop	ouloop
.exit	0
end

串操作

串操作处理对象不是一个字节或字,而是连续字节串,这个数据串的长度最长64KB。

要求,以SI为源串操作指针,DI为目标操作指针

串传送指令

格式

movsb

movsw

功能:这两个指令都将DS:SI指向的串元素送到ES:DI,再根据DF的值确定增或减SI与DI的值

不同点

movsb

sb作用于字节

movsw

sw作用于字

串比较指令

格式

cmpSB

cmpSW

功能:这两个指令都把SI指向的元素与DI指向的元素进行比较,通过相减,将影响标志位(AF、CF、OF、PF、SF、ZF),不会影响两个元素

不同点:

cmpSB

是用于字节之间比较,再根据DF的值,将SI/DI增1或减1

cmpSW

用于字之间比较,再根据DF的值1,将SI/DI增2或减2

串扫描指令

格式

scaSB

scaSW

功能:把累加器AL/AX的内容寄存器DI指向的元素进行相减比较,通过相减,将影响标志位(AF、CF、OF、PF、SF、ZF),不会影响两个元素

AL/AX的内容不用变化

scaSB

是用于字节之间扫描,再根据DF的值,将SI/DI增1或减1

scaSW

用于字之间扫描,再根据DF的值1,将SI/DI增2或减2

串装入指令

格式

LoadSB

LoadSW

将寄存器SI所指的字节/字元素装入AL/AX,使用前,必须给SI赋值。SI->->->AL/AX

LoadSB

操作字节

loadSW

操作

串存储指令

格式

stoSW

stoSB

将累加器AL/AX的值装入到DL/DX,再根据DF的值,让DI增加1或2还是减少1或2。AL/AX->->->DI

stoSB

操作字节,让DI增加1或减少1

stoSW

操作,让DI增加2或减少2

重复前缀

格式

rep

repe/repz

repne/repnz

rep无条件重复,直到cx值为0

repe/repz,当指令条件为相等时/结果为0时才重复

repne/repnz,当指令条件不相等/结果不为0才重复

指令 重复前缀 操作数 地址寄存器
movsw/sb rep 源/目标 DS:SI-->ES:DI
cmpsw/sb repe/repz 源/目标 DS:SI-->ES:DI
scasw/sb repe/repz 源/目标 DS:SI-->AL/AX
lodsw/sb 源/目标 DS:SI-->AL/AX
stosw/sb rep 源/目标 AL/AX-->ES:DI

举例

在str1串中查找是否包含字母J,如果有输出Y,没有输出N

datas	segement
	str1	db	'asdfay','$'
	count	equ	$-str1/type str1
datas	ends
codes	segment
	assume	cs:codes,ds:datas
start:
	lea		si,str1
	mov		al,'j'
	mov		cx,count
	repnz	scasb
	jz		find
	mov		dl,"N"
	jmp		out
find:
	mov		dl,"Y"
out:
	mov		ah,02h
	int		21h
	mov		ah,4Ch
	int		21h
codes	ends
end	start

子程序设计

子程序定义

子程序名 proc [near/far]

……

子程序名 endc

子程序调用

段内直接调用

格式:call 子程序名

段内调用时,将ip值压入栈中,堆栈指针SP自动减少2,再将相对偏移量DISP送到ip,从而调用子程序。

段内间接调用

格式:call src ;src为16位寄存器/存储器,其中存入的是子程序地址

段内调用时,将ip值压入栈中,堆栈指针SP自动减少2,再将相对偏移量DISP送到ip,从而调用子程序。

如:

call	word	ptr	[BX+SI+20]		;子程序入口IP<---[bx+si+20]
段间直接调用

格式:dst ;需要基地址cs和偏移量ip

如:

call	2600H:1000H	;子程序入口地址为2500H:1000H

返回指令RET

被调用子程序通过执行RET指令返回调用程序,子程序完成后返回原程序继续执行,一般放到子程序结尾

*(重点)子程序举例

实现回车换行的子程序
fun1	proc
	push	ax
	push	dx
	mov		dl,0dh		;回车控制字0DH
	mov		al,02h
	int		21h
	mov		dl,0ah		;换行控制字0AH
	mov		al,02h
	int		21h
	pop		dx
	pop		ax
	ret
fun1	endp
将AL中8位二进制转换2位16进制
    mov		dh,0
    mov		dl,al
    mov		cl,4
    shr		dl,cl	;把dl循环右移,先处理al的高4位
    add		dl,30h	;添加30,转换成ascii对应的字符
    cmp		dl,39h	;dl与39相比,判断al高4位是数字还是字符
    jbe		display	;如果是数字
	add		dl,7	;如果是字母,需要转换成字母对应的数
					;需要再加7,因为字母9与A相差7
sec_part:
	;处理低4位
	mov		dl,al
	and		dl,0Fh	;将高四位清空,低四位保留
	add		dl,30h	
    cmp		dl,39h	
    jbe		display	
    add		dl,7
	
display:
	add		dh,1		;第一次调用display时,dh不大于1,调用show_dl子程序,并返回到sec_part
	cmp		dh,1		;第二次从sec_part,不论是jbe跳转的(低四位是数字),还是正常走下来的(低四位是字母),到display时,dh大于1,会跳转到out,再调用show_dl子程序,结束
	ja		out
	call	show_dl
	jmp		sec_part
out:
	call	display
	mov		ax,4c00h
	int21h

show_dl proc
	mov		ah,02h
	int		21h
show_dl	endc

寄存器传递参数

将参数存于约定的寄存器中,方法简单易行。

用变量传递参数

主程序与被调用子程序使用同一个变量名访问传递的参数。

.data
	array	db	12h,13h
.code
.startup
	call	fun
.exit
fun	proc
	lea		si,array
fun	endc
end

用堆栈传递参数

在主程序中,将要传递的参数压入栈中

THE END
分享
二维码
< <上一篇
下一篇>>
文章目录
关闭
目 录