call 和 ret 指令
body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;} th{border: 1px solid gray; padding: 4px; background-color: #DDD;} td{border: 1px solid gray; padding: 4px;} tr:nth-child(2n){background-color: #f8f8f8;}
; CPU 执行 ret 指令 1、(IP)=((SS)*16+(SP)) 2、(sp)=(sp)+2 相当于: pop IP |
; CPU 执行 retf 指令 1、(IP)=((SS)*16+(SP)) 2、(SP)=(SP)+2 3、(CS)=((SS)*16+(SP)) 4、(SP)=(SP)+2 相当于: pop IP pop CS |
;程序执行 ret 指令后, (IP)=0,CS:IP指向代码段第一条指令
assume cs:code
stack segment
db 16 dup (0)
stack ends
code segment
mov ax , 4c00h
int 21h
start: mov ax , stack
mov ss , ax
mov sp , 16
mov ax , 0
push ax
mov bx , 0
ret ; ip 被设置为0,指令转移到mov ax , 4c00
code ends
end start
assume cs:code
code segment
mov ax , 4c00h
int 21h
start:
mov ax , 0
push ax
mov bx , 0
ret
code ends
end start ; 一样的效果 |
;程序执行 reft 后,CS:IP 指向代码段第一条指令
assume cs:code
stack segment
db 16 dup (0)
stack ends
code segment
mov ax , 4c00h
int 21h
start: mov ax , stack
mov ss , ax
mov sp , 16
mov ax , 0
push cs
push ax
mov bx , 0
retf
code ends
end start
assume cs:code
code segment
mov ax , 4c00h
int 21h
start:
mov ax , 0
push cs
push ax
mov bx , 0
retf
code ends
end start |
CPU 执行这种指令进行如下操作
1、(SP)=(SP)-2 ((SS)*16+(SP))=(IP)
2、(IP)=(IP)+16 位位移
CPU 执行 “call 标号”时,相当于: push IP jmp near ptr 标号 |
内存地址 | 机器码 | 汇编指令 |
076A:0 076A:3 076A:6 076A:7 |
B8 00 00 E8 01 00 40 58 |
mov ax , 0 call s ; 执行时 call 0007 inc ax s: pop ax |
当 IP=3的时候指向 call s ;执行这条指令的时候 IP 偏移到 6,也就是下一条指令;call s == push IP jmp near ptr 标号 此时栈中只有一个数据6,最后pop ax , 所以ax=6; |
转移的目的地址在指令中的 call 指令
“call far ptr 标号” 实现的是段间转移。 1、(SP)=(SP)-2 ((SS)*16+(SP))=(CS) (SP)=(SP)-2 ((SS)*16+(SP))=(IP) |
2、 (CS)=标号所在段的段地址 (IP)=标号在段中的偏移地址 相当于进行: push CS push IP jmp far ptr 标号 |
内存地址 | 机器码 | 汇编指令 |
076A:0 076A:3 076A:8 076A:9 076A:A |
B8 00 00 9A 09 00 6A 07 40 58 5B |
mov ax , 0
call far ptr s ; call 076A:0009
inc ax
s: pop ax
pop bx
|
执行call far ptr s 的时候,cpu把下一条指令的位置 CS:IP =076A:0008 压入栈中保存,然后计算到标号 s 的偏移,转到标号处执行;此时出栈 ax=IP=0008,bx=CS=076A; |
指令格式:call 16 位寄存器 功能:(SP)=(SP)-2 ((SS)*16+(SP))=(IP) (IP)=(16位寄存器) |
相当于进行: push IP jmp 16位寄存器 |
内存地址 | 机器码 | 汇编指令 |
076A:0 076A:3 076A:5 076A:6 076A:8 076A : B |
B8 00 00 FF D0 40 8B EC 03 46 00 58 |
mov ax , 6
call ax
inc ax
s: mov bp , sp
add ax , [bp] ; ax=000B , 这里取的是ss:bp内存里的值,因为sp==bp,所以是堆栈顶数据5,5+6=B
pop ax ; ax=0005
|
call ax ; 指令执行时当前 IP 偏移到 inc ax 即:IP=5;进栈;指令跳转到IP=6处执行,ax=6+栈顶元素=000B;出栈,ax =5 |
1、call word ptr 内存单元地址 相当于: push IP jmp word ptr 内存单元地址 mov sp , 10h
mov ax , 0123h
mov ds:[0] , ax
call word ptr ds:[0] ;执行这条指令,IP进栈,栈中存放000D
执行后:(IP)=0123H,(SP)=0EH
|
2、call dword ptr 内存单元地址 相当于: push CS push IP jmp dword ptr 内存单元地址 mov sp , 10h
mov ax , 0123h
mov ds:[0] , ax
mov word ptr ds:[2] , 0
call dword ptr ds:[0]
执行后:(CS)=0 , (IP)=0123h,(sp)=0Ch |
assume cs:codesg
codesg segment
start: mov ax , 1
mov cx , 3
call s ; 栈中进栈(IP),执行到这行指令的时候,IP指向mov bx , ax
mov bx , ax ;(bx)=8Z
mov ax , 4c00h
int 21h
s: add ax , ax
loop s
ret ;从栈中取一个数当做ip(刚好指向mov bx , ax)
codesg ends
end
|
1、两个相乘的数:两个数相乘要么都是8位,要么都是16位。如果都是8位,一个默认放在 AL 中,另外一个放在 8 位寄存器或内存字节单元中;如果是16位,一个默认放在 AX 中,另外一个放在16位寄存器或内存字单元中。 | 2、结果:如果是8位乘法,结果默认放在 AX 中;如果是16位乘法,结果默认高位放在 DX ,低位在 AX 中存放。 |
指令格式: mul reg mul 内存单元 |
内存单元可以用不同的寻址方式给出,eg: mul byte ptr ds:[0] 含义:(ax)=(al)*((ds)*16+0) mul word ptr [bx+si+8] 含义:(ax)=(ax)((ds)*16+(bx)+(si)+8) ; 结果的低16位 (dx)=(ax)((ds)*16+(bx)+(si)+8) ; 结果的高16位 |
; 计算 100*10 ; 两个数都小于255,可以做8位乘法 mov al , 100 mov bl , 10 mul bl 结果:(ax)=1000(03E8H) |
; 计算 100*10000
; 10000大于255,必须做16位乘法
mov ax , 100
mov bx , 10000
mul bx
结果:(ax)=4240H , (dx)=000FH ; F4240H=1000000
|
assume cs:codesg , ds:datasg
datasg segment
dw 1,2,3,4,5,6,7,8 ;(16个字节(00))
dd 0,0,0,0,0,0,0,0 ;(32个字节(00) )
datasg ends
codesg segment
start: mov ax , datasg
mov ds , ax
mov si , 0 ;偏移0指向第一个数1
mov di , 16 ;偏移16指向存放结果内存单元的首地址
mov cx , 8 ; 8个数,进行8次循环
s: mov bx , [si]
call cube
mov [di] , ax ;把结果的低16位放到低位两个字节(00 01)
mov [di].2 , dx ;把结果的高16位放到高位的两个字节(02 03)
add si , 2 ;偏移2个单位取到下一个数2
; ds:si 指向下一个 word 单元
add di , 4 ;偏移到下一个存储结果的内存地址(04 05 06 07)
; ds:di 指向下一个 dword 单元
loop s
mov ax , 4c00h
int 21h
cube: mov ax , bx ; 对拿到的数进行立方运算
mul bx
mul bx
ret
codesg ends
end start
|
// 程序初始状态
// 程序运行两次,也就是得出1的三次方(00 00 00 01)和2的三次方(00 00 00 08) // 改写程序的初始第一个值为9 9的三次方等于 2D9 , 低16位存放2D9 (D9 02), 高16位存放0(00 00) //第一个数是9运行一次循环的内存情况 //第一个数是100(64H),100^3=F4240H,低位4240H放在低16位(40 42),F放在高16位(0F 00) |
assume cs:codesg , ds:datasg
datasg segment
db 'conversation'
datasg ends
codesg segment
start: mov ax , datasg
mov ds , ax
mov si , 0 ; ds:si指向字符串(批量数据)所在空间的首地址
mov cx , 12 ; cx 存放字符串的长度
call capital
mov ax , 4c00h
int 21h
capital:and byte ptr [si] , 11011111b
inc si
loop capital
ret
codesg ends
end start
|
除了用寄存器传递参数外,还有一种通用的方法就是用栈来传递参数。 |
;改进上面的程序,字符串后面加一个0表示结束,就可以不用cx assume cs:codesg , ds:datasg
datasg segment
db 'conversation' , 0
datasg ends
codesg segment
start: mov ax , datasg
mov ds , ax
mov si , 0
call capital
mov ax , 4c00h
int 21h
capital:mov cl , [si] ; 当读到最后一个的时候为 0
mov ch , 0
jcxz ok ; 读到最后 (cx)=0
and byte ptr [si] , 11011111b
inc si
jmp short capital
ok: ret ; 出栈,设置cs:ip指向 mov ax , 4c00h
codesg ends
end start
|
|
;编写程序将 data 段中的字符串全部转化为大写 assume cs:code , ds:data
data segment
db 'word' , 0
db 'unix' , 0
db 'wind' , 0
db 'good' , 0
data ends
code segment
start: mov ax, data
mov ds , ax
mov bx , 0
mov cx , 4 ; 设置循环次数4
s: mov si , bx
call capital ; 调用子程序处理字符串
add bx , 5 ; 偏移量加 5 ,处理下一个字符串
loop s
mov ax , 4c00h
int 21h
capital: push cx ; 执行子程序防止有冲突寄存器,先压栈保存主程序寄存器值
push si
change: mov cl , [si]
mov ch , 0
jcxz ok ; 判断(cx)是否等于0,如果等于0表示读完一个字符串
and byte ptr [si] , 11011111b
inc si
jmp short change
ok: pop si ; 结束了一次子程序的调用,恢复主程序的相应寄存器的值
pop cx
ret ; 返回call下一行指令,执行下一次循环
code ends
end start
|
call 和 ret 指令的更多相关文章
- 汇编语言---call和ret指令
汇编语言--call和ret指令 call和ret指令 call和ret指令都是转移指令,它们都修改IP,或同时修改CS和IP. 它们经常被共同用来实现子程序的设计. ret和retf ret指令用栈 ...
- 汇编学习笔记(7)call和ret指令
ret和retf CPU执行ret指令时进行以下两步操作: (IP)=((ss)*16+(sp)) (sp)=(sp)+2 这相当于pop IP CPU执行retf指令时进行以下四步操作: (IP)= ...
- [汇编学习笔记][第十章 CALL和RET指令]
第十章 CALL和RET指令 call和ret指令都是转移指令,它们都修改CS和IP.经常被共同用于实现子程序的设计.这一章,我们讲解call和ret指令的原理 10.1 ret和retf ret指令 ...
- 汇编-10.0-CALL和RET指令
call和ret指令都是转移指令,他们都是修改IP,或同时修改CS和IP.它们常被共同用来实现子程序设计. 1.ret和retf ret指令用栈中的数据,修改IP的内容,从而实现近转移: retf指令 ...
- 第十章 Call 和 Ret 指令
引言 想想程序之间的加载返回过程. call 和 ret 指令都是转移指令,它们都修改 IP,或同时修改 CS 和 IP. call 和 ret 经常被共同用来实现自程序的设计. 这一章,我们讲解 c ...
- 汇编语言笔记 CALL和RET指令
转载地址:http://www.cnblogs.com/dennisOne ☞模块化程序设计 模块化程序设计 汇编语言通过call和ret指令实现了模块化程序设计.可以实现多个相互联系.功能独立的子程 ...
- 自己总结:汇编CALL和RET指令
ret指令,相当于 pop IP:修改IP的内容,从而实现近转移 retf指令,相当于 pop IP pop CS:修改CS和IP的内容,从而实现远转移 -------------- CPU执行cal ...
- call和ret指令
call和ret都是用来修改ip或cs:ip,可以用来实现子程序的设计: 1.ret和retf ret ->修改ip的内容,从而实现近转移: retf ->同时修改cs和i ...
- 王爽汇编第十章,call和ret指令
目录 王爽汇编第十章,call和ret指令 call和ret指令概述: ret和retf ret指令 retf指令 call 和 ret 的配合使用 call指令详解 call原理 call指令所有写 ...
随机推荐
- Netty入门之客户端与服务端通信(二)
Netty入门之客户端与服务端通信(二) 一.简介 在上一篇博文中笔者写了关于Netty入门级的Hello World程序.书接上回,本博文是关于客户端与服务端的通信,感觉也没什么好说的了,直接上代码 ...
- swift4.0 正则表达式判断手机号
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Menlo; color: #ffffff; background-color: #282b3 ...
- linux socket编程:简易客户端与服务端
什么是socket? socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来 ...
- Nginx 反向代理获取真实IP问题
一.前言 前文 Nginx 解决WebApi跨域二次请求以及Vue单页面问题 当中虽然解决了跨域问题带来的二次请求,但也产生了一个新的问题,就是如果需要获取用户IP的时候,获取的IP地址总是本机地址. ...
- 关于javascript代码优化的8点建议
前面的话 本文将详细介绍JS编程风格的几个要点 松耦合 当修改一个组件而不需要更改其他组件时,就做到了松耦合 1.将JS从CSS中抽离:不要使用CSS表达式 //不好的做法 .box{width: e ...
- Python进阶内容(二)--- 装饰器
谈装饰器前,需要明白一件事,Python 中的函数和 Java.C++不太一样,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数,例如: def foo(): print(" ...
- 8086cpu中的标志寄存器与比较指令
在8086CPU中有一个特殊的寄存器--标志寄存器,该寄存器不同于其他寄存器,普通寄存器是用来存放数据的读取整个寄存器具有一定的含义,但是标志寄存器是每一位都有固定的含义,记录在运算中产生的信息,标志 ...
- 迭代器中next()的用法
>>> g = (x ** 2 for x in range(10)) >>> next(g) 0 >>> next(g) 1 >>& ...
- flask-session组件
简介 flask-session是flask框架的session组件,由于原来flask内置session使用签名cookie保存,该组件则将支持session保存到多个地方,如: redis mem ...
- Vue 组件之 Router
Vue 组件之 Router Vue 开发单页应用的时候,免不了使用Vue组件.在单页应用上如何进行组件切换? 结构如下图所示: 主页面包含Foo组件与Bar组件,在主页面中可以进行Foo与 Bar的 ...