【汇编语言】call 和 ret 指令(三) —— 深度解析汇编语言中的批量数据传递与寄存器冲突
文章目录
- 前言
- 1. 批量数据的传递
- 1.1 存在的问题
- 1.2 如何解决这个问题
- 1.3 示例演示
- 1.3.1 问题说明
- 1.3.2 程序实现
- 2. 寄存器冲突问题的引入
- 2.1 问题引入
- 2.2 分析与解决问题
- 2.2.1 字符串定义方式
- 2.2.2 分析子程序功能
- 2.2.3 得到子程序代码
- 2.3 子程序的应用
- 2.3.1 示例1
- 2.3.2 示例2
- 3. 寄存器冲突问题的发现与解决
- 3.1 重看代码
- 3.2 分析与解决问题
- 3.2.1 存在的冲突问题
- 3.2.2 解决方案的探讨
- 3.2.3 给出解决方案。
- 3.2.4 子程序的标准框架
- 3.2.5 改进之前的程序
- 结语
前言
📌
汇编语言是很多相关课程(如数据结构、操作系统、微机原理)的重要基础。但仅仅从课程的角度出发就太片面了,其实学习汇编语言可以深入理解计算机底层工作原理,提升代码效率,尤其在嵌入式系统和性能优化方面有重要作用。此外,它在逆向工程和安全领域不可或缺,帮助分析软件运行机制并增强漏洞修复能力。
本专栏的汇编语言学习章节主要是依据王爽老师的《汇编语言》来写的,和书中一样为了使学习的过程容易展开,我们采用以8086CPU为中央处理器的PC机来进行学习。
1. 批量数据的传递
1.1 存在的问题
前面学习的例程中,子程序 cube 只有一个参数,放在bx中。如果有两个参数,那么可以用两个寄存器来放,可是如果需要传递的数据有3个、4个或更多直至 N个,我们怎样存放呢?
寄存器的数量终究有限,我们不可能简单地用寄存器来存放多个需要传递的数据。对于返回值,也有同样的问题。
1.2 如何解决这个问题
在这种时候,我们将批量数据放到内存中,然后将它们所在内存空间的首地址放在寄存器中,传递给需要的子程序。对于具有批量数据的返回结果,也可用同样的方法。
接下来我们来看下具体的落实……
1.3 示例演示
1.3.1 问题说明
下面看一个例子,
设计一个子程序,功能:将一个全是字母的字符串转化为大写。
这个子程序需要知道两件事,字符串的内容和字符串的长度。
因为字符串中的字母可能很多,所以不便将整个字符串中的所有字母都直接传递给子程序。但是,可以将字符串在内存中的首地址放在寄存器中传递给子程序。因为子程序中要用到循环,我们可以用loop指令,而循环的次数恰恰就是字符串的长度。
出于方便的考虑,可以将字符串的长度放到cx。
capital: and byte ptr [si],11011111b ;将ds:si所指单元中的字母转化为大写inc si ;ds:si 指向下一个单元loop capital ret
1.3.2 程序实现
编程:将data段中的字符串转化为大写。
assume cs:codedata segmentdb 'conversation'
data endscode segmentstart: mov ax,datamov ds,axmov si,0 ;ds:si指向字符串(批量数据)所在空间的首地址mov cx,12 ;cx存放字符串的长度call capitalmov ax,4c00hint 21hcapital:and byte ptr [si],11011111binc siloop capitalretcode endsend start
❗注意:除了寄存器、内存传递参数外,还有一种通用的方法使用栈来传递参数。
2. 寄存器冲突问题的引入
2.1 问题引入
设计一个子程序:功能:将一个全是字母,以0结尾的字符串,转化为大写。
2.2 分析与解决问题
2.2.1 字符串定义方式
程序要处理的字符串以0作为结尾符,这个字符串可以如下定义:
db ‘conversation’,0
2.2.2 分析子程序功能
应用这个子程序 ,字符串的内容后面定要有一个0,标记字符串的结束。
子程序可以依次读取每个字符进行检测,如果不是0,就进行大写的转化,如果是0,就结束处理。
由于可通过检测0而知道是否己经处理完整个字符串 ,所以子程序可以不需要字符串的长度作为参数。
我们可以直接用jcxz来检测0。
2.2.3 得到子程序代码
;说明:将一个全是字母,以0结尾的字符串,转化为大写
;参数:ds:si指向字符串的首地址
;结果:没有返回值
capital:mov cl,[si]mov ch, 0jcxz ok ;如果(cx)=0,结束;如果不是0,处理and byte ptr [si], 11011111b ;将ds:si所指单元中的字母转化为大写inc si ;ds:si 指向下一个单元jmp short capitalok:ret
2.3 子程序的应用
来看一下这个子程序的应用。
2.3.1 示例1
要求:将data段中的字符串转化为大写。
assume cs:code
data segmentdb 'conversation',0
data ends
代码段中的相关程序如下。
mov ax,data
mov ds,ax
mov si,0
call capital
2.3.2 示例2
要求:将data段中字符串全部转化为大写。
assume cs:codedata segmentdb 'word', 0db 'unix', 0db 'wind', 0db 'good', 0
data ends
可以看到,所有字符串的长度都是5(算上结尾符0),使用循环,重复调用子程序capital,完成对4个字符串的处理。
完整的程序如下。
code segment
start: mov ax,datamov ds,axmov bx,0mov cx,4s: mov si,bxcall capitaladd bx,5loop smov ax,4c00hint 21hcapital:mov cl,[si]mov ch, 0jcxz okand byte ptr [si], 11011111binc sijmp short capitalok:retcode endsend start
3. 寄存器冲突问题的发现与解决
3.1 重看代码
上面的这个程序在思想上完全正确,但在细节上却有些错误,把错误找出来。
提示:问题在于cx的使用。
思考后看分析。
3.2 分析与解决问题
3.2.1 存在的冲突问题
问题在于cx的使用,主程序要使用cx记录循环次数,可是子程序中也使用了cx,在执行子程序的时候,cx中保存的循环计数值被改变,使得主程序的循环出错。
从上面的问题中,实际上引出了一个一般化的问题:子程序中使用的寄存器,很可能在主程序中也要使用,造成了寄存器使用上的冲突。
3.2.2 解决方案的探讨
那么如何来避免这种冲突呢?
粗略地看,可以有以下两个方案。
(1)在编写调用子程序的程序时,注意看看子程序中有没有用到会产生冲突的寄存器,如果有,调用者使用别的寄存器;
(2)在编写子程序的时候,不要使用会产生冲突的寄存器。
我们来分析一下上面两个方案的可行性:
(1)这将给调用子程序的程序的编写造成很大的麻烦,因为必须要小心检查所调用的子程序中是否有将产生冲突的寄存器。
比如说,在上面的例子中,我们在编写主程序的循环的时候就得检查子程序中是否用到了bx和cx,因为如果子程序中用到了这两个寄存器就会出现问题。
如果采用这种方案来解决冲突的话,那么在主程序的循环中,就不能使用cx寄存器,因为子程序中已经用到。
(2)这个方案也是不可能实现的,因为编写子程序的时候无法知道将来的调用情况。
可见,我们上面所设想的两个方案都不可行。
我们希望:
-
(1)编写调用子程序的程序的时候不必关心子程序到底使用了哪些寄存器。
-
(2)编写子程序的时候不必关心调用者使用了哪些寄存器。
-
(3)不会发生寄存器冲突。
3.2.3 给出解决方案。
解决这个问题的简捷方法是,在子程序的开始将子程序中所有用到的寄存器中的内容都保存起来,在子程序返回前再恢复。可以用栈来保存寄存器中的内容。
3.2.4 子程序的标准框架
以后,我们编写子程序的标准框架如下:
3.2.5 改进之前的程序
我们改进一下子程序 capital的设计:
capital: push cxpush sichange: mov cl,[si]mov ch, 0jcxz okand byte ptr [si], 11011111binc sijmp short capitalok: pop sipop cxret
要注意寄存器入栈和出栈的顺序。
结语
今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。
也可以点点关注,避免以后找不到我哦!
Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!
相关文章:

【汇编语言】call 和 ret 指令(三) —— 深度解析汇编语言中的批量数据传递与寄存器冲突
文章目录 前言1. 批量数据的传递1.1 存在的问题1.2 如何解决这个问题1.3 示例演示1.3.1 问题说明1.3.2 程序实现 2. 寄存器冲突问题的引入2.1 问题引入2.2 分析与解决问题2.2.1 字符串定义方式2.2.2 分析子程序功能2.2.3 得到子程序代码 2.3 子程序的应用2.3.1 示例12.3.2 示例…...
定义函数合并字符串—超详细讲解
【问题描述】 编写一个函数void str_bin(char str1[ ], char str2[ ]), str1、str2是两个有序字符串(其中字符按ASCII码从小到大排序),将str2合并到字符串str1中,要求合并后的字符串仍是有序的,允许字符重…...

实现 vue3 正整数输入框组件
1.实现代码 components/InputInteger.vue <!-- 正整数输入框 --> <template><el-input v-model"_value" input"onInput" maxlength"9" clearable /> </template><script lang"ts" setup> import { ref …...

Leetcode - 周赛425
目录 一,3364. 最小正和子数组 二, 3365. 重排子字符串以形成目标字符串 三,3366. 最小数组和 四,3367. 移除边之后的权重最大和 一,3364. 最小正和子数组 本题可以直接暴力枚举,代码如下: …...
c++(斗罗大陆2)
我把魂力等级更新到了31级 #include<iostream> #include<conio.h> #include<windows.h> #include<stdlib.h> #include<stdio.h> #include<time.h> #include<string.h> using namespace std; int qs10; int xthl0;//先…...
redis常见数据类型
Redis是一个开源的、内存中的数据结构存储系统,它可以用作数据库、缓存和消息代理,支持多种数据类型。 一、数据类型介绍 String(字符串) Redis中最基本的数据类型。可以存储任何类型的数据,包括字符串、数字和二进制…...
MySQL - 性能优化
使用 Explain 进行分析 Explain 用来分析 SELECT 查询语句,开发人员可以通过分析 Explain 结果来优化查询语句。 比较重要的字段有: select_type : 查询类型,有简单查询、联合查询、子查询等 key : 使用的索引 rows : 扫描的行数 type :…...

Linux进程概念-详细版(一)
目录 进程概念 描述进程-PCB task_struct-PCB的一种 task_struct内容分类 查看进程 通过系统目录查看 通过ps命令查看 通过系统调用获取进程的PID和PPID 通过系统调用创建进程 fork的认识 使用if进行分流 最后的总结 Linux进程状态 运行状态-R 浅度睡眠状态-S 深度睡…...

K8S网络系列--Flannel网络下UDP、VXLAN模式的通信流程机制分析
文章目录 前言一、了解overlay、underlay容器网络二、网络通信1.分类2.网络虚拟设备对2.1、什么是网络虚拟设备对veth pair?2.2、如何查看容器的网卡与主机的哪个veth设备对是成对的关系? 3、vxlan和vtep3.1、vtep3.2、vxlan相关概念 三、Flannel网络模式剖析0、flannel的作用…...
ThreadLocal的设计思考
问题的提出 在Java多线程中,共享变量的读写非常容易出现不可预测的行为,因此对共享变量的访问控制非常重要。因此在多线程编程时,为了保证线程安全,需要进行额外的同步措施。比如典型的操作就是加锁。除了加锁外,另一…...

shell脚本练习(2)
1. 使用case实现成绩优良差的判断 2. for创建20用户 用户前缀由用户输入 用户初始密码由用户输入 例如:test01,test10 3. for ping测试指网段的主机 网段由用户输入,例如用户输入192.168.2 ,则ping 192.168.2.10 --- 192.168.2.2…...

通讯专题4.1——CAN通信之计算机网络与现场总线
从通讯专题4开始,来学习CAN总线的内容。 为了更好的学习CAN,先从计算机网络与现场总线开始了解。 1 计算机网络体系的结构 在我们生活当中,有许多的网络,如交通网(铁路、公路等)、通信网(电信、…...
Harmony NEXT-越过相机读写权限上传图片至项目云存储中
问题成因 在制作用户注册登录界面时想要实现用户头像上传共能,查询API文档,发现有picker和PhotoAccessHelper两个包可以选择使用,但是在使用PhotoAccessHelper包拉起相册并读入所选的照片后将该照片传入云存储中产生报错,需要相册…...
MATLAB基础应用精讲-【数模应用】Retinex图像去雾算法(附MATLAB和python代码实现)
目录 前言 算法原理 图像去雾 数学模型 算法步骤 算法拓展 多尺度Retinex (MSR) 算法 MSR算法的实现细节 McCann Retinex 算法 McCann99 Retinex算法 基于暗通道先验的图像去雾算法 暴力解法——直方图均衡化去雾 基于Retinex理论的图像去雾 基于暗通道先验的单…...
点击A组件跳转到B页面的tab的某一列
1、使用vuex存储点击的数据; 点击A组件里面的button按钮: <div><button click"banli(first)">已办理</button><button click"banli(second)">未办理</button><button click"banli(third)&quo…...

HarmonyOS xml转换JavaScript 常用的几个方法
HarmonyOS 使用 xml转换JavaScript 的好处 易用性: 提供了简洁的API接口,使得XML到JavaScript对象的转换变得简单直接。转换选项的灵活性允许开发者根据实际需求自定义转换结果。 高效性: HarmonyOS对底层运行时环境进行了优化,使…...

Linux笔记---进程:进程等待
1. 进程等待的概念 进程等待是指父进程通过系统调用wait或waitpid来对子进程进行状态检测与回收的功能。 当子进程退出时,如果父进程不读取子进程的退出状态,子进程就会成为僵尸进程,造成内存泄漏的问题。因此,父进程需要调用wa…...

【Linux】匿名管道通信场景——进程池
🔥 个人主页:大耳朵土土垚 🔥 所属专栏:Linux系统编程 这里将会不定期更新有关Linux的内容,欢迎大家点赞,收藏,评论🥳🥳🎉🎉🎉 文章目…...

算法妙妙屋-------1.递归的深邃回响:全排列的奇妙组合
全排列的简要总结 全排列(Permutation)是数学中一个经典的问题,指的是从一组元素中,将所有元素按任意顺序排列形成的所有可能序列。 特点 输入条件: 给定一组互异的元素(通常为数组或字符串)。…...

【maven-6】Maven 生命周期相关命令演示
Maven 是一个广泛使用的项目管理工具,尤其在 Java 项目中。它通过定义一系列的生命周期阶段(Phases)来管理项目的构建过程。理解这些生命周期阶段及其相关命令,对于高效地构建和管理项目至关重要。本文将通过实际演示,…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...