【汇编】第四章问题与解答
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组成
-
“?”表示:初值不确定
-
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
用堆栈传递参数
在主程序中,将要传递的参数压入栈中
共有 0 条评论