|
一、顺序程序结构
- 程序的静态分析 略
- 程序的动态分析 略
- 实例:读取CMOS RAM数据程序 略
二、分支程序结构
- 无条件转移指令
- 代码段寄存器CS指出代码段的段基地址,指令指针IP给出将要执行的偏移地址
- 转移范围
- 段内转移——只改变IP
- 段内转移也称近转移(NEAR)
- 如果转移范围可以用1字节编码表达,即向地址增大方向转移127字节,向地址减小方向转移128字节,形成短转移(SHORT)
- 段间转移——更改CS和IP
- 也称远转移(FAR)
- 指令寻址方式
- 相对寻址方式
- 提供目标地址相对于当前指令指针IP的位移量
- 目标地址(转移后的IP)=当前IP+位移量
- 直接寻址方式
- 直接提供目标地址
- 目标地址(转移后的CS和IP)=指令操作数
- 间接寻址方式
- 指示寄存器或存储单元
- 目标地址来自寄存器或存储单元,间接获得
- 注:目标地址=目的地址=转移地址
- JMP指令
- 四种类型
- 段内转移,相对寻址
- 段内转移,间接寻址
JMP R16 ;IP=R16 寄存器间接寻址
JMP M16 ;IP=M16 存储器间接寻址
- 段间转移,直接寻址
JMP LABLE ;IP=LABLE的偏移地址,CS=LABLE的段选择器
- 段间转移,间接寻址
- MASM会根据存储模型和目标地址等信息自动识别是哪种转移。
- 汇编程序提供了操作符:
- SHORT
- NEAR PTR
- FAR PTR
- 可以强制转换一个符号、段名或子程序名的类型,形成相应的控制转移。
- 实例:
;eg403.asm
.model small
.stack
.data
nvar dw ?
fvar dd ?
.code
.startup
labl0: jmp labl1 ;段内(短)转移、相对寻址
nop
labl1: jmp near ptr labl2 ;段内(近)转移、相对寻址
nop
labl2: mov ax,offset labl3
jmp ax ;段内转移、寄存器间接寻址
nop
labl3: mov ax,offset labl4
mov nvar,ax
jmp nvar ;段内转移、存储器间接寻址
nop
labl4: jmp far ptr labl5 ;段间转移、直接寻址
nop
labl5: mov ax,offset labl6
mov word ptr fvar,ax
mov dx,seg labl6
mov word ptr fvar+2,dx
jmp fvar ;段间转移、间接寻址
nop
labl6:
.exit
end
- 条件转移指令
- 格式及说明
-
JCC LABEL
;条件满足,发生转移;
;否则顺序执行下条指令
- LABEL表示目标地址,采用段内相对寻址(只能是-128~127之间的短转移)
- 条件转移指令不影响标志,但要利用标志。
- 16种,两类标志判断如下:
-
- 实例!开始疯狂上代码!
- 单分支
-
- 求绝对值
-
;eg408.asm
.model small
.stack
.data
var dw 0b422h ;有符号数据
result dw ? ;保存绝对值
.code
.startup
mov ax,var
cmp ax,0 ;比较AX与0
jge nonneg ;条件满足:AX≥0,转移
neg ax ;条件不满足:AX<0,为负数,需求补得正值
nonneg: mov result,ax ;分支结束,保存结果
.exit
end
-
字母判断程序
-
mov ah,1
int 21h ;输入一个字符,从AL返回值
cmp al,'A' ;与大写字母A比较
jb done ;比大写字母A小,不是大写字母,转移
cmp al,'Z' ;与大写字母Z比较
ja done ;比大写字母Z大,不是大写字母,转移
or al,20h ;转换为小写
mov dl,al
mov ah,2
int 21h ;显示小写字母
done:
- 双分支程序
-
- 显示数据最高位
-
var dw 0b422h ;有符号数据
mov bx,var
shl bx,1 ;BX最高位移入CF标志
jc one ;CF=1,即最高位为1,转移
mov dl,'0' ;CF=0,即最高位为0:DL←'0'
jmp two ;一定要跳过另一个分支体
one: mov dl,'1' ;DL←'1'
two: mov ah,2
int 21h ;显示
可以使用ADC消除分支:
-
XOR DL,DL
mov bx,var
shl bx,1 ;BX最高位移入CF标志
adc dl,'0'
mov ah,2
int 21h ;显示
- 有符号数运算溢出程序
-
;eg411.asm
.model small
.stack
.data
var1 dw 24680
var2 dw -9999
var3 dw ?
okmsg db 'Correct!','$' ;正确信息
errmsg db 'ERROR ! Overflow!','$' ;错误信息
.code
.startup
mov ax,var1
sub ax,var2 ;求差
jo error ;有溢出,转移
mov var3,ax ;无溢出,保存差值
mov dx,offset okmsg ;显示正确
jmp disp
error: mov dx,offset errmsg ;显示错误
disp: mov ah,9
int 21h
.exit
end
- 多分支程序
三、循环程序结构
- 循环指令
- LOOP LABEL
- CX=CX-1 如果CX不等于0,循环到label,否则顺序执行
- JCXZ LABEL
- 目标地址采用相对短转移
- 使用CX作为计数器
- 如果CX=0将循环pow(2,16)次发生错误
- 此时可以使用JCXZ先判断CX是否为零
- 实例:数组求和程序
-
ARRAY DW 136,-138,133,130,-161
SUM DW ?
XOR AX,AX
MOV CX,LENGTHOF ARRAY
MOV BX,OFFSET ARRAY
AGAIN: ADD AX,ARRAY[BX]
ADD BX,TYPE ARRAY
LOOP AGAIN
MOV SUM,AX
- 计数控制循环
- 求最大值程序
-
;eg414.asm
.model small
.stack
.data
array dw -3,0,20,900,587,-632,777,234,-34,-56 ;假设一个数组
count = lengthof array ;数组的元素个数
max dw ? ;存放最大值
.code
.startup
mov cx,count-1 ;元素个数减1是循环次数
mov si,offset array
mov ax,[si] ;取出第一个元素给AX,用于暂存最大值
again: add si,2
cmp ax,[si] ;与下一个数据比较
jge next ;已经是较大值,继续下一个循环比较
mov ax,[si] ;AX取得更大的数据
next: loop again ;计数循环
mov max,ax ;保存最大值
.exit
end
- 简单的加密解密程序
-
-
;eg415.asm
.model small
.stack
.data
key db 234 ;假设的一个密钥
buffer db 'This is a secret.','$' ;待加密的信息(字符串)
count = sizeof buffer-1 ;不处理最后结尾字符
msg1 db 'Encrypted message: ','$'
msg2 db 13,10,'Original messge: ','$'
.code
.startup
mov cx,count ;CX=字符个数,作为循环的次数
xor bx,bx ;BX指向待处理的字符
mov al,key ;AL=密钥
encrypt: xor buffer[bx],al ;异或加密
inc bx ;指向下一个字符
cmp bx,cx
jb encrypt ;没有指向最后字符,继续处理
mov dx,offset msg1 ;显示提示信息
mov ah,9
int 21h
mov dx,offset buffer ;显示加密后的密文
mov ah,9
int 21h
;
xor bx,bx ;BX指向待处理的字符
mov al,key ;AL=密钥
decrypt: xor buffer[bx],al ;异或解密
inc bx
dec cx
jnz decrypt ;等同于指令:loop decrypt
mov dx,offset msg2
mov ah,9
int 21h
mov dx,offset buffer ;显示解密后的明文
mov ah,9
int 21h
.exit
end
- 条件控制循环
- 字符个数统计(统计以0结尾的字符串中的字符个数)
-
;eg416a.asm
include io.inc
.model small
.stack
.data
string db 'Do you have fun with Assembly?',0 ;以0结尾的字符串
.code
.startup
xor bx,bx ;BX用于记录字符个数,同时也用于指向字符的指针
again: mov al,string[bx]
cmp al,0 ;也可以使用指令“test al,al”
jz done
inc bx ;个数加1
jmp again ;继续循环
done: mov ax,bx ;显示个数
call dispuiw
.exit
end
- 斐波那契
-
;eg417a.asm
include io.inc
.model small
.stack
.data
.code
.startup
mov ax,1 ;AX=F(1)=1
call dispuiw ;显示第1个数
call dispcrlf ;回车换行
call dispuiw ;显示第2个数
call dispcrlf ;回车换行
mov bx,ax ;BX=F(2)=1
again: add ax,bx ;AX=F(N)=F(N-2)+F(N-1)
jc done
call dispuiw ;显示一个数
call dispcrlf ;回车换行
xchg ax,bx ;AX=F(N-2),BX=F(N-1)
jmp again
done:
.exit
end
- 多重循环
- 冒泡排序
-
;eg418.asm
.model small
.stack
.data
array dw 587,-632,777,234,-34 ;假设一个数组
count = lengthof array ;数组的元素个数
.code
.startup
mov cx,count ;CX←数组元素个数
dec cx ;元素个数减1为外循环次数
outlp: mov dx,cx ;DX←内循环次数
mov bx,offset array
inlp: mov ax,[bx] ;取前一个元素
cmp ax,[bx+1] ;与后一个元素比较
jng next
;前一个不大于后一个元素,则不进行交换
xchg ax,[bx+1] ;否则,进行交换
mov [bx],ax
next: inc bx ;下一对元素
dec dx
jnz inlp ;内循环尾
loop outlp ;外循环尾
.exit
end
- 字符剔除(剔除空格
-
;eg419.asm
.model small
.stack
.data
string db 'Let us have a try !',0dh,0ah,'$' ;以“$”结尾的字符串
.code
.startup
mov dx,offset string ;显示处理前的字符串
mov ah,9
int 21h
mov si,offset string
outlp: cmp byte ptr [si],'$' ;外循环,先判断后循环
jz done ;为“$”结束
again: cmp byte ptr [si],' ' ;检测是否是空格
jnz next ;不是空格继续循环
mov di,si ;是空格,进入剔除空格分支
inlp: inc di ;该分支是循环程序
mov al,[di] ;前移一个位置
mov [di-1],al
cmp byte ptr [di],'$' ;内循环,先循环后判断
jnz inlp ;内循环结束处
jmp again ;再次判断是否为空格(处理连续空格情况)
next: inc si ;继续对后续字符进行判断处理
jmp outlp ;外循环结束处
done: mov dx,offset string ;显示处理后的字符串
mov ah,9
int 21h
.exit
end
- 串操作指令
- 以字节、字和双字为单位多个数据存放在连续的主存区形成数据串,(也就是数组)
- 特殊的寻址方式:
- 源操作数用寄存器SI间接寻址,默认在数据段DS中,即DS:[SI],允许段超越
- 目的操作数用寄存器DI间接寻址,默认在附加段ES中,即ES:[DI],不允许段超越
- 每执行一次串操作源指针SI和目的指针DI自动±1或±2
- CLD设置方向标志DF=0 指针增加
- STD设置方向标志DF=1 指针减小
- 串传送
- 传送 MOVS
- MOVSB ES:[DI] ← DS:[SI] SI++,DI++
- MOVSW ES:[DI] ← DS:[SI] SI+=2,DI+=2
- 存储 STOS
- STOSB ES:[DI]←AL DI++
- STOSW ES:[DI]←AX DI+=
- 读取 LODS
- AL←DS:[SI] SI++
- AX←DS:[SI] SI+=2
- 重复REP 计数器也是CX
四、习题
4.1(2)数据的直接寻址和指令的直接寻址有什么区别?
数据的直接寻址直接给偏移地址(变量名/有效地址)
指令的直接寻址使用段内转移相对量(标号)
(5)什么是奇偶校验?
包括校验位在内的数据为为“1”的个数为奇数是奇校验。
(6)助记符JZ和JE为什么表达同一条指令?
JZ为两个数相减差值为零,JE为两个数相等。
(9)如果循环体的代码量远超过128字节,还能用LOOP指令实现计数控制循环吗?
短转移前后不能超过128字节。
(10)什么是“先循环、后判断”循环结构?
先执行一遍循环体。
4.2 判断题
(2)指令的相对寻址都是段内转移。
对。
(4)JMP指令对应高级语言的GOTO语句,所以不能使用。
错,可以使用。
(6)JA和JG指令的条件都是“大于”,所以是同一个指令的两个助记符。
错。JA判断无符号数JG判断有符号数。
(7)JC和JB指令的判断条件都是CF=1,所以是同一条指令。
错。JC前有可能是移位,JB必须是有减法操作才能判断。
(10)若CX=0,则LOOP指令和JCXZ指令都发生转移。
对,而且LOOP会执行pow(2,16)次。
4.3填空题
(2)MASM给短转移、近转移、和远转移定义的类型名依次是 SHORT 、 NEAR 、 FAR 。
(3)假设BX=1256H,字变量TABLE的偏移地址是20A1H,数据段偏移地址32F7H处存放3280H,执行指令“JMP BX"后IP= 1256H ,执行指令”JMP TABLE[BX]“后IP= 3280H 。
(4)"CMP AX,3721H"指令之后是JZ指令,发生转移的条件是AX= 3271H ,此时ZF= 1 。
(5)执行""SHR BX,1"指令后,JNC发生转移,说明BX的D0= 0 。
(6)在DX等于0时转移,可以使用指令“CMP DX, 0 ",也可以使用"TEST DX, FFFFH "构成条件,然后使用JE指令实现转移。
4.4 已知VAR1、VAR2、VAR3和VAR4是16位有符号整数,用汇编语言程序片段实现如下C语句:
VAR4=(VAR1*6)/(VAR2-7)+VAR3
;用汇编语言实现var4=(var1*6)/(var2-7)+var3
.model small
.stack
.data
var1 dw 10
var2 dw 9
var3 dw 15
var4 dw ?
.code
.startup
mov ax,var1
mov bx,6
mul bx
mov cx,var2
sub cx,7
div cx
add ax,var3
mov var4,ax
add var4,30h
mov dx,var4
mov ah,9
int 21h
.exit
end
4.7 为了验证例4-3程序的执行路径,可以在每个标号前后增加显示一个数字的功能,是的程序运行后显示数码123456.
;eg403.asm
.model small
.stack
.data
nvar dw ?
fvar dd ?
.code
.startup
labl0:
jmp labl1 ;段内(短)转移、相对寻址
nop
labl1:
mov ah,2
mov dl,'1'
int 21h
jmp near ptr labl2 ;段内(近)转移、相对寻址
nop
labl2:
mov ah,2
mov dl,'2'
int 21h
mov ax,offset labl3
jmp ax ;段内转移、寄存器间接寻址
nop
labl3:
mov ah,2
mov dl,'3'
int 21h
mov ax,offset labl4
mov nvar,ax
jmp nvar ;段内转移、存储器间接寻址
nop
labl4:
mov ah,2
mov dl,'4'
int 21h
jmp far ptr labl5 ;段间转移、直接寻址
nop
labl5:
mov ah,2
mov dl,'5'
int 21h
mov ax,offset labl6
mov word ptr fvar,ax
mov dx,seg labl6
mov word ptr fvar+2,dx
jmp fvar ;段间转移、间接寻址
nop
labl6:
mov ah,2
mov dl,'6'
int 21h
.exit
end
4.13 8086处理器的指令CWD将AX符号扩展到DX。假若没有该指令,编程实现该指令功能。
;编程实现将ax符号扩展到dx
.model small
.stack
.data
.code
.startup
;第一种方法:用符号扩展的含义
mov ax,45h ;初始化ax
test ax,ax
js one
mov dx,0
jmp over1
one:
mov dx,0ffffh
over1:
nop ;空操作,第一种方法结束
;第二种方法:用移位实现
mov ax,0f045h ;初始化ax
mov bx,ax ;为了不改变ax的值,将ax值赋值给bx
shl bx,1 ;取ax的最高位到cf
rcr dx,1 ;将cf的值移入dx最高位
mov cl,15
sar dx,cl ;dx算数右移15,dx所有位变成ax的符号位
.exit
end
4.15 编程:先提示输入数字"INPUT NUMBER:0~9",然后再下一行显示输入的数字,结束;如果不是键入了0~9数字,就提示错误'ERROR',继续等待输入数字。
;输入数字
.model small
.stack
.data
errormsg db 'Error!',0dh,0ah,'$' ;9个信息
msg db 'Input number(1~9): $' ;提示输入字符串
crlf db 0dh,0ah,'$' ;回车换行字符
.code
.startup
again:
mov dx,offset msg
mov ah,9
int 21h ;提示输入
mov ah,1 ;等待按键
int 21h
push ax ;暂时将输入的按键字符保存到堆栈
mov dx,offset crlf ;回车换行
mov ah,9
int 21h
pop ax ;恢复按键字符
cmp al,'1' ;数字 < 1?
jb error
cmp al,'9' ;数字 > 9?
ja error
mov dl,al
mov ah,2
int 21h ;显示数字
jmp done;结束
error:
mov dx,offset errormsg
mov ah,9
int 21h ;提示输入
jmp again
done:
.exit
end
来源:https://www.cnblogs.com/hotaru-klxx/p/15519272.html |