登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

明月凉

再不记录,就忘了

 
 
 

日志

 
 

汇编—王爽_3  

2011-09-13 21:48:47|  分类: 计算机 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

call  和 ret  的配合,实现子程序。

assume cs:code
code segment
start: mov ax,1
 mov cx,3
 call s
 mov bx,ax
 mov ax,4c00h
 int 21h
s: add ax,ax
 loop s
 ret

code ends
end start

执行到call时,call将IP的指向转移,本来是指向它的下一条语句(mov bx,ax)的,现在转向了add ax,ax。

遇到 call 时,CPU 先将 IP 入栈,所以执行完后,要用 ret让 IP 出栈。

这里的 s 子程序执行完后,ret,IP重新指到了mov bx,ax。

 

mul

前面学过div除尘指令了。

mul是乘法,两个数同时为8位或16位。

8位的,一个默认在AH中,另一个在8位寄存器或内存字节单元中,16位的,一个在默认AX中,另一个在16位寄存器或内存字单元中。

结果:两个8位的结果放在AX中,两个16位的高位在DX中,低位在AX中。

计算100*10000,得用16位。

mov ax,100

mov bx,10000

mul bx

 

参数和传递问题

一涉及到子程序,自然引发参数的问题。

显然可以用寄存器来暂时存储。

 assume cs:code

 data segment
 dw 1,2,3,4,5,6,7,8
 dd 0,0,0,0,0,0,0,0   
 data ends

 stack segment
 dw 8 dup (0)      
 stack ends

 code segment
 start:mov ax,data
 mov ds,ax
 mov si,0
 mov di,16
 mov cx,8
s: mov bx,[si]
 call cube
 mov [di],ax
 mov [di+2],dx
 add si,2
 add di,4
 loop s

 mov ax,4c00h
 int 21h

 cube: mov ax,bx
 mul bx
 mul bx
 ret

code ends
end start   

乘法运算后的结果在AX和DX中,所以将AX,DX的值分别放进定义好的内存区。

dd是4个字节。运行结果:

汇编—王爽_3 - 某丶丶人 - 某丶丶人
 
 
但是,当参数数目多时,这样做是行不通的,哪儿那么多的寄存器给你用?
所以得用内存单元才行。
 
 
在使用寄存器时,可能存在冲突,主程序中的寄存器可能会在子程序中被改变。
在子程序中开始时将寄存器入栈,在子程序返回前再出栈。
assume cs:code
data segment
 db 'word',0
 db 'unix',0
 db 'wind',0
 db 'good',0
data ends
stack segment
 dw 8 dup (0)      
stack ends
code segment
start:mov ax,data
 mov ds,ax
 mov bx,0 
 mov cx,4
s: mov si,bx
 call capital
 add bx,5
 loop s
 mov ax,4c00h
 int 21h
capital:push cx
 push si
change:mov cl,[si]
 mov ch,0
 jcxz ok 
 and byte ptr [si],11011111b
 inc si
 jmp short change
ok: pop si
 pop cx
 ret
code ends
由于change子程序中对CX进行了改变,所以要先将它入栈才不影响主程序的循环。
汇编—王爽_3 - 某丶丶人 - 某丶丶人
 
 
 
 
 
 
标志寄存器
flag寄存器用来存储相关指令的执行结果,为CUP的执行提供行为依据,控制CUP的工作方式。
flag是16位,和其它寄存器不一样的是它的每一位都有专门的含义,代表了一种信息。
第1,3,5,12,13,14,15位在8086中没有用。其它的每一位都有特殊含义。
 
ZF标志位,第6位,是零标志位。
它记录相关指令执行后,其结果是不是为0。
影响它的一般都是进行运算的指令。
 
PF标志位,第2位,是奇偶标志位。
它记录相关指令执行后,其结果的二进制位中1的个数是不是为偶数。
 
SF标志位,它不是影魔!第7位,是符号标志位。
它记录相关指令执行后,其结果是否为负。
 
CF,第0位,进位标志位。
在进行无符号运算时,记录进位或借位。
 
OF,第11位,溢出标志位。
记录有符号运算是否溢出。
 这里先说到这,因为cmp要影响这些标志位。后面还有DF等标志位。
 
adc指令:
带进位的加法,利用了CF上的进位值。
用法:adc   操作数1,操作数2
相当于:操作数1 = 操作数1 + 操作数2 + CF
也就是多加了一个CF位的值。
mov ax,2
 mov bx,1
 sub bx,ax
 adc ax,1
执行后,AX=4,sub这句使CF=1,然后adc这句就多加了一个1。
 
sbb指令:
带借位的减法,利用了CF位上的借位值。
 
 
cmp:比较指令,相当于做减法,它会影响标志位,其它的指令通过识别被它影响的标志位来得到结果。
条件转移指令一般都和cmp指令配合使用,根据标志位来决定是不是要跳转。
对有符号数和无符号数,cmp指令影响的标志位有不同之处。
所以转移指令也会因为有符号和无符号而去识别不同的位。
根据无符号数的比较结果而转移的指令:
je------等于则转移--------检测ZF
jne----不等于则转移------检测ZF
jb------小于则转移---------检测CF
jnb-----不小于则转移-------检测CF
ja-----不高于则转移-------检测CF,CF=0且ZF=0
jna-----不高于则转移-------检测CF,CF=1且ZF=1
j=jump,e=equal,n=not,b=below,a:above
 
比如说je,它表现出来的“相等则转移”是通过cmp来体现的,内部其实是“ZF=1则转移”。
所以,就算je前面没有cmp,它还是可能转移,主要看ZF的值了。
 
我们在使用的时候,也就是只考虑它们配合使用体现出来的这种逻辑含义,不必关心它内部是检测什么位或是设置什么位。
就像IF语句一样。
 
 
DF标志位
第10位,方向标志位,用在串处理指令中。
DF=0时,每次操作后si,di递增。
DF=1时,每次操作后si,di递减。
 
串传送指令movsb:
它相当于:mov es:[di],byte  ptr  ds:[si]。
然后呢,si,di就跟据DF来变化。
它将ds:si指向内存单元中的字节送入es:di中。
 
传送一个字就是:movsw。
它相当于:mov es:[di],word  ptr  ds:[si]。
 
movsb和movsw只是串传送操作中的一步。
它可以后rep 配合使用,rep  movsb。
rep 根据CX的值来重复后面的movsb指令。相当于
s:movsb
   loop s
 
我们必须要能去控制DF,才能让程序按我们的想法走。
cld指令可以将DF置0。
std   指令可以将DF置1。
 
一个例子:
assume cs:code
data segment
       db 'Welcome to masm!'
 db 16 dup (0)
data ends
stack segment
      
stack ends
code segment
start: mov ax,data
 mov ds,ax
 mov si,0
 mov es,ax
 mov di,16
 mov cx,16
 cld
 rep movsb
 mov ax,4c00h
 int 21h
code ends
end start
我们要做的,就是把data段的字符串复制到它后面的空间中去。
程序中,要知道:
1,传送的原始位置:ds:si-------data:0
2,传送的目的位置:es:di-------data:16
3,传送的长度:cx-------16
4,传送的方向:DF--------要使它递增,使DF=0。
汇编—王爽_3 - 某丶丶人 - 某丶丶人
 
  评论这张
 
阅读(280)| 评论(0)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018