[从零学习汇编语言] - 模块化程序设计

2023-10-31


前言

点赞再看,养成习惯!


回顾

还记得我们之前讲过的ret指令搭配call指令实现的子程序模板吗?忘记了没关系,我们可以再看一遍:

assume cs:code
code segment
main : 
      call sub1 ; 调用子程序sub1 ,也可以说是方法1
	  ...
	  mov ax,4c00h
	  int 21h
sub1: 
     ...
	 ret       ;返回主程序
code ends 
end main 

这个模板记住哦,等下要用的。


一、 模块化程序设计

1.1 子程序

如果屏幕前的你有其它语言的基础,那么子程序这个概念对于你来说是很好理解的。它就像是其它语言里的method,是为了处理主程序中某一块特定业务而存在的。这部分子程序通常都有以下的特点:

  1. 通常是可复用的,允许多个主程序重复使用的逻辑程序块。
  2. 一般如果某段特定逻辑需要向主程序隐藏实现细节,也可以使用子程序。
  3. 具有一定的独立性,相较于其他代码,更偏向于完成默写特定逻辑。

而子程序这个概念在不同的计算机语言中也会被称为不同的名称,比如函数程序方法等等都是属于子程序的一种。


1.2 子程序的参数和返回值

当我们知晓了子程序是为主程序处理特定业务场景的职责后,新的问题就接踵而至。由于子程序的代码段并没有功能主程序的数据段,因此我们需要为子程序提供特定的参数(形参),并且在子程序运算结束后还要将结果返回给主程序(返回值)。那么在这个前提下,我们接下来需要探讨的问题就很简单了,我们如何为子程序提供参数并存储返回值?
比如: 我们设计一个子程序用以计算N的三次方
思路剖析:

  1. 第一个关注点就是我们如何将N传递给子程序
  2. 第二个关注点是我们需要设计一个N的次方的逻辑代码
  3. 第三个关注点就是如何为主程序提供结果

那么这里面我们首先去做可以解决的问题:计算N的三次方的结果。 我们知道N的三次方就是将N自乘三次,很巧的是我们在上一章节曾经讲过如何进行乘法,那么这里我们先将这部分的代码设计出来。

assume cs:code
code segment
     mov ax,n
	 mov cx,3
cobe: mul ax
       loop cobe
code ends
end 

接下来我们需要解决的问题就是参数传递的问题了,首先按照我们已有的知识体系推断,数据一定会被存放进某个内存单元或寄存器,首先我们来考虑一下参数放到哪里合适呢?比如当前我们的需求只有一个参数,如果放到内存中也是要将内存地址放入寄存器中,这样就不如直接放到寄存器中简易。而可以存放通用数据的寄存器有四种:ax bx cx dx,其中cx又是状态寄存器,ax,dx要参与乘法运算,想来用bx寄存器是最合适的了。而返回值由于我们需要的就是乘法运算的结果,因此使用ax,dx即可。接下来我们只需要补充完整整个程序:

assume cs:code
code segment
	main:   mov bx,n ; n代表变量
	        call cube
	cube:   mov ax, bx 
	        mul ax
			mul ax
			ret 
code ends 
end 		

好,让我们来验证下我们的程序。让我们来定义一个data段,data段分为两组,第一组负责提供参数,第二组负责保存结果

assume cs:code
data segment
     dw 0,1,2,3,4,5,6,7
	 dd 8 dup(0)
data ends 

code segment
     main :   mov ax,data
	          mov ds,ax
			  mov si,0  ; 指向第一组内存单元 即变量
			  mov di,16 ; 指向第二组内存单元 即返回值
			  mov cx,8
 getAndSave:  mov bx,[si]
              call cube ; 进行立方运算
			  mov [di],ax
			  mov [di].2 ,dx
			  add si,2
			  add di,4
			  loop getAndSave
			  
			  mov ax,4c00h ;中断语句
			  int 21h

       cube:  mov ax,bx
	          mul ax
			  mul ax
			  ret
code ends
end main

结果验证:

在这里插入图片描述


1.3 批量数据的传递

那如果我们需要传递的参数不止一个呢?比如有3个?这个时候显然我们再想将参数放入到寄存器中是不合适的。在这种情况下,我们需要做的是将参数放入到内存中,然后将它们所在的内存空间的首地址放在寄存器中,传递给需要的子程序,对于具有批量数据的返回结果也可如此(用栈空间也可以)。让我们验证下:
设计一个程序,将一个全是字母的字符串转化为大写
这里我们需要关注两件事情:

  1. 字符串的内容和字符串的长度。这是因为我们需要告知子程序他们需要在内存中处理的数据的大小。假设我们设计的程序一次处理一个字符长度的字母,那我们完全可以使用loop循环进行大小写转换。这样的话我们可以将字符串长度存放到CX寄存器中。
  2. 还记得如何转换大小写么?如果忘记的同学请复习下逻辑运算章节

程序:

assume cs:code
data segment
    db 'conversation'
data ends

code segment
	 main:
			mov ax,data
			mov ds,ax
			mov si,0
			mov cx,12 ; 字符串长度为12
			call uppercase
			mov ax,4c00h
			int 21h
uppercase:  
			and byte ptr [si] ,11011111b ; 这里看不懂的同学请复习下逻辑运算章节
			inc si 
			loop uppercase
			ret 
code ends 
end main 

验证结果:

在这里插入图片描述


1.4 寄存器的冲突处理

我们先通过业务场景模拟一下冲突场面。现在有几段字符串,均是以0结尾,请将这些字符串转化为大写。
让我们先看下数据段:

assume cs:code
data segment
     db 'hello',0
	 db 'world',0
data ends

由于数据段都会以0结尾,因此我们不再需要将长度作为判断标准,只需要判断0即可。我们以此设计程序:

assume cs:code
data segment
     db 'hello',0
	 db 'world',0
data ends

code segment
     main: 
			mov ax,data
			mov ds,ax
			mov si,0
			call upperCase
			mov ax,4c00h
			int 21h
			
upperCase:  and byte ptr [si] ,11011111b
            inc si
			mov ch,0 ; 高位赋0,低位赋原有值 ,若原有值位0则自动中断循环
			mov cl,si
			loop upperCase
			ret 
code ends 
end main

但是到这里还是有个问题,我们的程序只能够处理完第一个字符串,但是第二个字符串还是没有办法处理。这是为什么呢?其实主要还是由于我们两个需求都太过依赖cx寄存器。有咩有什么改进的办法呢?其实也很简单,我们只需要将我们冲突的寄存器的值在冲突覆盖前保存下来,然后再冲突结束后再恢复即可。

assume cs:code
data segment
     db 'hello',0
	 db 'world',0
data ends

stack segment
    db 16 dup(0)
stack ends 

code segment
     main: 
			mov ax,data
			mov ds,ax
			mov si,0
			mov ax,stack
			mov ss,ax
			mov sp,16
			mov cx ,2
   change: 		
			push cx 
			call upperCase
			pop cx 
			loop change
			mov ax,4c00h
			int 21h
			
upperCase:  and byte ptr [si] ,11011111b
            inc si
			mov ch,0
			mov cl,[si]
			loop upperCase   
			ret 
code ends 
end main

这样对吗?还是不对,因为loop在判断cx = 0的时候会将cx-1 = FFFF ,此时不等于0,程序会进入死循环。我们该怎么办呢?很简单,用我们之前学到过的判断指令jcxz即可。

assume cs:code
data segment
     db 'hello',0
	 db 'world',0
data ends

stack segment
    db 16 dup(0)
stack ends 

code segment
     main: 
			mov ax,data
			mov ds,ax
			mov si,0
			mov ax,stack
			mov ss,ax
			mov sp,16
			mov cx ,2
   change: 		
			push cx 
			call upperCase
			pop cx 
			loop change
			mov ax,4c00h
			int 21h
			
upperCase:  and byte ptr [si] ,11011111b
            inc si
			mov ch,0
			mov cl,[si]
			jcxz return
			loop upperCase   
   return:	ret 
code ends 
end main

验证结果:
在这里插入图片描述

结语

今天的内容就到此结束了,有疑问的小伙伴欢迎评论区留言或者私信博主,博主会在第一时间为你解答。

码字不易,感到有收获的小伙伴记得要关注博主一键三连,不要当白嫖怪哦~
如果大家有什么意见和建议请评论区留言或私聊博主,博主会第一时间反馈的哦。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[从零学习汇编语言] - 模块化程序设计 的相关文章

  • dd大牛的《背包九讲》

    P01 01背包问题 题目 有N件物品和一个容量为V的背包 第i件物品的费用是c i 价值是w i 求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量 且价值总和最大 基本思路 这是最基础的背包问题 特点是 每种物品仅有一件 可以选
  • 【HCNP路由交换学习指南】学习笔记丨第07章 BGP

    07 BGP BGP 的基本概念 BGP 对等体关系类型 IBGP 水平分割原则 路由黑洞问题及 BGP 同步规则 路由通告 Router ID 报文类型及格式 Open 报文 Update 报文 Keepalive 报文 Notifica
  • PaddleOCR使用笔记之模型训练

    目录 简介 模型训练 步骤一 文本检测模型 detection 1 准备训练数据集 2 下载预训练模型 模型介绍 下载预训练模型 3 开始训练 断点训练 4 模型评估 5 模型测试 6 训练模型转inference模型 步骤二 文本识别模型
  • RabbitMQ保证消息的一致性解决方案

    RabbitMQ保证消息的一致性 一 采用confirm消息确认机制及return返回机制 确保消息发送成功 二 将队列以及消息设置持久化 保证rabbitmq突然宕机消息仍然存在 三 手动确认接收消息方式 消息处理失败拒收重回队列 1 y
  • 后端响应是否成功、信息、操作码响应前端及异常处理

    异常处理流程 1 自定义异常类型 2 自定义错误代码及错误信息 3 对于可预知的异常由程序员在代码中主动抛出 由SpringMVC统一捕获 可预知异常是程序员在代码中手动抛出本系统定义的特定异常类型 由于是程序员抛出的异常 通常异常信息比较

随机推荐

  • java反射(从认识到应用)-黑马笔记

    此文章是观看黑马雷哥关于Java反射的视频做的笔记 如有错请多指教 一 认识反射 反射在JavaAPI中的详解 说白了 反射就是 加载类 并允许以编程方式解剖类中的各种成分 成员变量 方法 构造器等 如图 二 反射还学什么 加载类 获取类的
  • 做题

    在一个文件中定义一个全局变量 n 主函数 main 在另一个文件中定义函数 fn1 在 main 中对 n 赋值 再调用 fn1 在 fn1 中也对 n 赋值 显示 n 最后的值 include using namespace std in
  • style对象和less/scss互相转换,驼峰转中横线,支持嵌套转换

    不知道有没有小伙伴在维护或重构前端项目时修改样式的时候遇到js style和less scss需要互相转换的问题 本人找网上没有比较完善的转换工具 要么嵌套不支持 要么兼容不好 于是自己写了一个 请大家参考 emotion style In
  • CSDN英雄大会之 SOA技术观感

    5号假装英雄去北京参加了CSDN技术英雄大会 见到了很多一直想见的同行高手还有编辑记者 这里就不一一列举了 只是从SOA中间件开发角度列一下相关的内容 1 IBM如下划分SOA与构件 SOA4类关键构件 基础构件WAS MQ 流程构件WPS
  • 设置一个FreemarkerExceptionHandler捕获freemarker页面上的异常

    在Freemarker页面中如果使用 userName 并且userName为空 那么Freemarker页面就会崩掉 需要设置默认值 userName 来避免对象为空的错误 同理 user userName 也应该写成这样 user us
  • WaveOut播放声音死锁问题原因

    1 首先我们复习下造成死锁的几个充要条件 1 互斥 互斥资源 只能被一个进程使用 2 不剥夺 非抢占式调度 不能强行抢用其他进程资源 3 请求和保持 占有着资源不释放 同时申请其他资源 4 环路等待 没什么可说的 在WaveOutReset
  • java.lang.IncompatibleClassChangeError: Found interface org.elasticsearch.common.bytes.BytesReferenc

    项目场景 再学谷粒商城时 练习elasticsearch时出现一下错误 问题描述 原因分析 提示 出现java lang IncompatibleClassChangeError Found interface org elasticsea
  • redis 由浅入深之 redis.conf配置文件

    是否以后台进程运行 默认为no 如果需要以后台进程运行则改为yes daemonize no 如果以后台进程运行的话 就需要指定pid 你可以在此自定义redis pid文件的位置 pidfile var run redis pid 接受连
  • 2021-04-09

    C 实现XN 2图灵机模拟 算法分析 代码实现 算法分析 1 首先需要完成数的进制转换 将二进制数转化成十进制数 如何转化呢 这里采用除二取余的方法 这种方法就是不断得到余数 放进数组arr 最后除以颠倒一下就行了 2 转换成拓展二进制编码
  • Leetcode刷题-522最长特殊序列II

    题目描述 给定字符串列表 strs 返回它们中最长的特殊序列 如果最长特殊序列不存在 返回 1 最长特殊序列定义 该序列为某字符串独有的最长子序列 即不能是其他字符串的子序列 s 的子序列可以通过删去字符串 s 中的某些字符实现 来源 力扣
  • 灰色预测原理及实例(附代码)

    灰色预测 引言 古人说 凡事预则立 不预则废 办任何事情之前 必须先调查研究 摸清情况 深思熟虑 有科学的预见 周密的计划 这样才能达到预期的成功 所谓预测 就是人们根据可获得的历史和现实数据 资料 运用一定的科学方法和手段 对人类社会 政
  • CSS文本设置超出2行显示省略号

    需求 如图所示 当居住地址字段太长超过2行时就显示 1 强制一行so easy div class line style 内容部分内容部分内容 div line white space nowrap 强制一行显示 overflow hidd
  • 快速入门

    之前正运动技术与大家分享了 运动控制器的固件升级 ZBasic程序开发 ZPLC程序开发 与触摸屏通讯和输入 输出IO的应用 运动控制器数据与存储的应用 运动控制器ZCAN EtherCAT总线的使用 示波器的应用 多任务运行的特点 运动控
  • 【Linux】echo命令用法详解

    作者 柒号华仔 个人主页 欢迎访问我的主页 个人信条 星光不问赶路人 岁月不负有心人 个人方向 专注于5G领域 同时兼顾其他网络协议 编解码协议 C C linux等 感兴趣的小伙伴可以关注我 一起交流 目录 1 echo命令介绍 2 命令
  • 【Linux】——makefile

    文章目录 1 什么是makefile 2 makefile优势与make命令 3 makefile文件内容 3 1makefile文件的基础写法 3 2 makefile文件的优化 1 什么是makefile 在一个大型工程中 工程中的源文
  • 构建Python,Raspberry Pi和PostgreSQL数据服务器

    视频教程 PostgreSQL初学者 学习Raspberry Pi Linux初学者指南 在Raspberry Pi安装和设置PostgreSQL 前提 下载Raspbian 格式化SD卡 使用Disk Utility在macOS中格式化S
  • 自制番茄钟脚本

    Shell脚本 bin bash i 0 if 1 then t 25 60 else t 5 60 fi while i lt t do sleep 1 i 1 m t i 60 s t m 60 i echo en m s r done
  • parsing XML document from class path resource [applicationContext.xml]…

    方法 1 可能是缺失了resource文件 项目右键 Build Path Configure Build Path Java Build Path source Add Folder 将Resource添加应用 2 可能缺失了spring
  • SqliLab_Mysql_Injection详解_字符型注入(五)_时间盲注(8~10)

    文章目录 1 SqliLab Mysql Injection详解 字符型注入 五 1 1 SQL注入 时间盲注 1 1 1 原理 1 1 2 常用的函数 2 SqliLab关卡 包含8 9 10 图片占据空间太大 payload具体返回情况
  • [从零学习汇编语言] - 模块化程序设计

    文章目录 前言 回顾 一 模块化程序设计 1 1 子程序 1 2 子程序的参数和返回值 1 3 批量数据的传递 1 4 寄存器的冲突处理 结语 前言 点赞再看 养成习惯 回顾 还记得我们之前讲过的ret指令搭配call指令实现的子程序模板吗