《汇编语言》- 读书笔记 - 实验 10 编写子程序

news/发布时间2024/5/19 8:22:51

《汇编语言》- 读书笔记 - 实验 10 编写子程序

  • 1. 显示字符串
    • 问题
    • 子程序描述 show_str
    • 提示
    • 结果
    • 演示
  • 2. 解决除法溢出的问题
    • 问题
    • 子程序描述 divdw
    • 提示
    • 结果
    • 演示
  • 3. 数值显示
    • 问题
    • 子程序描述 dtoc
    • 提示
    • 结果
    • 演示

在这次实验中,我们将要编写3个子程序,通过它们来认识几个常见的问题和掌握解决这些问题的方法。同前面的所有实验一样,这个实验是必须独立完成的,在后面的课程中,将要用到这个实验中编写的3个子程序。

1. 显示字符串

问题

显示字符串是个常用功能,写个子程序来实现此功能。子程序要支持参数:显示的位置(行、列)、内容和颜色。

子程序描述 show_str

名称:show_str
功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
参数:(dh) = 行号(取值范围 0~24),
(dl) = 列号(取值范围 0~79),
(cl) = 颜色,ds:si 指向字符串的首地址
返回:
应用举例:在屏幕的83列,用绿色显示data段中的字符串

提示

  1. 子程序的入口参数是屏幕上的行号和列号,注意在子程序内部要将它们转化为显存中的地址,首先要分析一下屏幕上的行列位置和显存地址的对应关系;
  2. 注意保存子程序中用到的相关寄存器;
  3. 这个子程序的内部处理和显存的结构密切相关,但是向外提供了与显存结构无关的接口。通过调用这个子程序,进行字符串的显示时可以不必了解显存的结构,为编程提供了方便。在实验中,注意体会这种设计思想。

结果

assume cs:code
data segmentdb 'Welcome to masm!',0
data endscode segmentstart:	mov dh,8		;8 行mov dl,3		;3 列mov cl,2		; 颜色mov ax,data		; 设置数据段mov ds,axmov si,0		; 指向字符串首地址call show_str	; 调用子程序mov ax,4c00h	; 退出int 21h
; ------------------- 子程序 -------------------
show_str:				push si				; 缓存寄存器push dipush espush dxpush cxpush bxpush axmov ax,0B800h	; 设置显存段地址mov es,axmov al,160		; 先算行偏移dec dh				; 行号从 0 开始,所以这里要先减1mul dh				; 行数 x 160算出行偏移mov bx,ax			; 行偏移先存到 bxmov al,2		; 再算列偏移dec dl				; 列号从 0 开始,所以这里要先减1mul dladd bx,ax		;+= 算出目标字符串(显存)的开始位置mov dl,cl 		; 字符属性换个地方存。cx下面要用来循环mov ch,0		; cx高位清空(后面我们只改 cl,就方便 jcxz 判断了)mov di,0		; 指向目标字符串首字符sloop:	mov cl,ds:[si]		; 取字符mov es:[bx+di],cl	; 字符写入显存mov es:[bx+di+1],dl ; 字符属性写入显存jcxz ok				; 如果为 0 字符结束,跳出循环inc si				; si 递增,源字符串指向下一字符add di,2			; di += 2,目标字符串指向下一字符jmp short sloopok:	pop ax			; 还原寄存器pop bxpop cxpop dxpop espop dipop siret
; ------------------- 子程序 -------------------
code ends
end start

演示

在这里插入图片描述

2. 解决除法溢出的问题

问题

div指令执行除法时:
8位除法的AL = 商AH = 余数
16位除法则分别用AXDX存储余数
但若结果超出ALAX的最大范围,该如何处理?

比如:
8位除法:FFFF ÷ 1al 中容纳不下商 FFFFh
16位除法:10000h ÷ 1ax 中容纳不下商 1 0000h

为解决这一问题,我们设计一个子程序来专门处理它。

子程序描述 divdw

名称:divdw
功能:进行不会产生溢出的除法运算,被除数dword 型,除数word 型,结果dword 型。
参数:(ax) = dword被除数 16 位,
(dx) = dword被除数 16 位,
(cx) = 除数
返回:(ax) = 结果的低16位
(dx) = 结果的高16位
(cx) = 余数
应用举例:计算:1000000/10(F4240H/0AH)
结果:(dx)=0001H, (ax)=86A0H, (cx)=0

提示

书中给出的公式:

公式X/N = int(H/N) * 65536 + [ rem(H/N) * 65536 + L ] / N
X被除数,范围:[0,FFFFFFFF]
N除数,范围:[0,FFFF]
HX 高 16 位,范围:[0,FFFF]
LX 低 16 位,范围:[0,FFFF]
int()描述性运算符,取商,比如,int(38/10)=3
rem()描述性运算符,取余数,比如,rem(38/10)=8

这个公式将可能产生溢出的除法运算:XN,转变为多个不会产生溢出的除法运算。公式中,等号右边的所有除法运算都可以用 div 指令来做,肯定不会导致除法溢出。
(关于这个公式的推导,有兴趣的读者请参看附注5。)

结果

assume cs:code
code segmentstart:	mov ax,4240h	; dword 被除数的低 16位mov dx,000Fh	; dword 被除数的高 16位mov cx,0Ah		; word  除数call divdw		; 调用子程序 dword 类型除法mov ax,4c00h	; 退出int 21h
; ------------------- 子程序 -------------------
divdw:push si		; 缓存寄存器push bxmov si,ax	; 因为要用到 ax,所以被除数的低 16位,临时放在 simov ax,dx	; 先算高 16 位mov dx,0		;160div cx			; 除数16位,则被除数32位。商ax,余dxmov bx,ax		; 临时保存高16位商mov ax,si	; 后算低 16;16位的余数留在 dx不用动。相当于:余数 * 10000 +16位div cx			; 除数16位,则被除数32位。商ax,余dxmov cx,dx	; 得到余数mov dx,bx	; 拿回临时保存的高16位商pop bx		; 还原寄存器	pop siret
; ------------------- 子程序 -------------------
code ends
end start

演示

在这里插入图片描述

3. 数值显示

问题

编程,将 data 段中的数据以十进制的形式显示出来。

data seqmentdw 123,12666,1,8,3,38
data ends

但是数据在内存中都是二进制信息,
比如 12666 在内存保存的是二进制的0011 0001 0111 1010等于16进制317A

可见,要将数据用十进制形式显示到屏幕上,要进行两步工作:

  1. 将用二进制信息存储的数据转变为十进制形式的字符串。(我们需要设计一个子程序)
  2. 显示十进制形式的字符串。(这个我们可以前面设计 好的 show_str

子程序描述 dtoc

名称:dtoc
功能:word 型数据转变为表示十进制数的字符串,字符串以0为结尾符。
参数:(ax) = word 型数据
ds:si 指向字符串的首地址
返回:
应用举例编程,将数据12666十进制的形式在屏幕的8行3列,用绿色显示出来。
在显示时我们调用本次实验中的第一个子程序 show str

提示

  1. 十进制数码字符对应的ASCII码 = 十进制数码值 + 30H
    12666 对应:
12666
31H32H36H36H36H
  1. 要求得 12666 的每一位是多少,可以用除法迭代取数:
    在这里插入图片描述
  2. 借助 jcxz 指令,用是否为 0 作为循环结束的条件。
  3. 按这个顺序取余得到的是 66621 最后我们还要把它翻转过来才是 12666

结果

assume cs:code
data segmentdb 10 dup (0)
data endscode segmentstart:	mov bx,data		; 设置段地址mov ds,bx; 调用子程序 dtoc 将 word 转 10 进制数字mov ax,12666		; word 型数据mov si,0			; 循环变量从 0 开始call dtoc		; 调用子程序 show_str 在屏幕 83列用绿色显示mov dh,8mov dl,3mov cl,2call show_strmov ax,4c00h	; 退出int 21h
; ------------------- 子程序 dtoc -------------------
dtoc:push si		; 缓存寄存器push dipush bx; 用遍历取余的方式拿到 ascii 拿到的结果是倒的; 比如 12666 遍历取余拿到的是 66621 所以先丢栈里,方便下一步翻转mov bx,10s:	mov dx,0	; 除数 bx 是 16 位,则被除数是32位。高16位 dx 没有数据要补 0div bx		; 除完后 商在ax下次继续用,dx为余数add dx,30H	; 将余数转 ascii push dx		; 入栈备用(此时高8位是无意义的,之后我们只要取低8位用即可 )inc si		; 循环变量 +1mov cx,ax	; 取商来判断是否为 0jcxz s_ok		; 如果商为0跳出循环jmp s			; 否则继续循环s_ok:; 目前栈里是 66621 顺序不是我们想要的,遍历一下出栈到 ds:[0]... 就正了mov di,0r:	pop axmov [di],al	; 写入目标内存地址。第 di 个字符(我们只取低8位有效值)inc didec simov cx,si	; 出栈到第 si 个jcxz r_ok		; 如果 si 为 0 表示出完,结束循环jmp r			; 否则继续出栈r_ok:	pop bx		; 还原寄存器pop dipop siret
; ------------------- 子程序 dtoc -------------------
; ------------------- 子程序 show_str -------------------
show_str:				push si				; 缓存寄存器push dipush espush dxpush cxpush bxpush axmov ax,0B800h	; 设置显存段地址mov es,axmov al,160		; 先算行偏移dec dh				; 行号从 0 开始,所以这里要先减1mul dh				; 行数 x 160算出行偏移mov bx,ax			; 行偏移先存到 bxmov al,2		; 再算列偏移dec dl				; 列号从 0 开始,所以这里要先减1mul dladd bx,ax		;+= 算出目标字符串(显存)的开始位置mov dl,cl 		; 字符属性换个地方存。cx下面要用来循环mov ch,0		; cx高位清空(后面我们只改 cl,就方便 jcxz 判断了)mov di,0		; 指向目标字符串首字符sloop:	mov cl,ds:[si]		; 取字符mov es:[bx+di],cl	; 字符写入显存mov es:[bx+di+1],dl ; 字符属性写入显存jcxz ok				; 如果为 0 字符结束,跳出循环inc si				; si 递增,源字符串指向下一字符add di,2			; di += 2,目标字符串指向下一字符jmp short sloopok:	pop ax			; 还原寄存器pop bxpop cxpop dxpop espop dipop siret
; ------------------- 子程序 show_str -------------------
code ends
end start

演示

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.bcls.cn/hjBH/1058.shtml

如若内容造成侵权/违法违规/事实不符,请联系编程老四网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

【MySQL】多表关系的基本学习

🌈个人主页: Aileen_0v0 🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​💫个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-3oES1ZdkKIklfKzq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

【GO语言卵细胞级别教程】05.项目创建和函数讲解

感谢!点点赞和评论呀!我将继续更新 目录: 感谢!点点赞和评论呀!我将继续更新0.创建项目1.函数的引入2.注意事项3.详细介绍3.1 形参介绍 4.导入包4.1 基本知识4.2 注意事项 5.init函数6.匿名函数 0.创建项目 创建目录 …

以太坊 Dencun 升级与潜在机会

撰文:Biteye 核心贡献者 Fishery Isla 文章来源Techub News专栏作者,搜Tehub News下载查看更多Web3资讯。 以太坊网络升级 Dencun 测试网版本在 2024 年 1 月 17 日上线了 Goerli 测试网,1 月 30 日成功上线了 Sepolia 测试网,D…

拼夕夕 拼多多关键词恢复供应,欢迎骚扰

API接口(Application Programming Interface)是一种定义了软件组件之间交互的规范。它允许不同的软件系统之间进行通信和数据交换,使得开发者可以利用已有的功能和服务来构建自己的应用程序。 API接口可以分为不同的类型,包括Web…

[计算机网络]---UDP协议

前言 作者:小蜗牛向前冲 名言:我可以接受失败,但我不能接受放弃 如果觉的博主的文章还不错的话,还请点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、端口号…

《剑指 Offer》专项突破版 - 面试题 47 : 二叉树剪枝(C++ 实现)

题目链接:LCR 047. 二叉树剪枝 - 力扣(LeetCode) 题目: 一棵二叉树的所有节点的值要么是 0 要么是 1,请剪除该二叉树中所有节点的值全都是 0 的子树。例如,在剪除下图 (a) 中二叉树中所有节点值都为 0 的…

多模态基础---BERT

1. BERT简介 BERT用于将一个输入的句子转换为word_embedding,本质上是多个Transformer的Encoder堆叠在一起。 其中单个Transformer Encoder结构如下: BERT-Base采用了12个Transformer Encoder。 BERT-large采用了24个Transformer Encoder。 2. BERT的…

集群聊天项目

不懂的一些东西 (const TcpConnectionPtr&)作为形参啥意思:接收一个常量引用,函数内部不允许修改该指针所指向的对象。 客户端与服务器如何联系? 优势 1.网络层与业务层分离:通过网络层传来的id,设计一个map存储id以及对印的业务处理器,处理器bind绑定处理函数,…

【PyQt】12-滑块、计数控件

文章目录 前言一、滑块控件 QSlider运行结果 二、计数器控件 QSpinBox运行结果 总结 前言 1、滑块控件 2、计数控件 一、滑块控件 QSlider #Author :susocool #Creattime:2024/2/15 #FileName:28-滑块控件 #Description: 通过滑块选择字体大小 import sys from PyQ…

基于java的企业校园招聘平台的设计与实现

分享一个自己的毕业设计,想要获取源码的同学加V:qq2056908377 链接:https://pan.baidu.com/s/1It0CnXUvc9KVr1kDcHWvEw 提取码:1234 摘要: 摘要:本毕业设计旨在设计和实现一个企业校园招聘平台&#xf…

MIT-BEVFusion系列九--CUDA-BEVFusion部署2 create_core之参数设置

目录 加载命令行参数main 函数中的 create_core图像归一化参数体素化参数稀疏卷积网络参数真实世界几何空间参数 (雷达坐标系下体素网格的参数)解码后边界框的参数构建 bevfusion::Core 存储推理时需要的参数 本章开始,我们将一起看CUDA-BEVFusion的代码流程&#x…

微信小程序view设置margin-top和margin-button无效的解决方案

微信小程序view设置margin-top和margin-button无效的解决方案 问题发现解决过程解决问题正确办法综上所述,position、overflow、border属性都可以解决这个问题 制作不易,感谢三联,谢谢大家了 问题发现 当我新建一个微信小程序项目时&#xf…

适配器模式:接口转换的艺术,让不匹配成为过去式

适配器模式(Adapter Pattern)是一种结构型设计模式,它通过将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以一起工作。在Java中,适配器模式可以通过实现一个适配器类来实现两个不兼容接口之间的转…

Android Studio安装SDK失败解决办法

Android Studio安装SDK失败解决办法 安装SDK时界面会显示安装的连接,同时在你选择的安装SDK的文件夹里面会生成一些目录和文件,在你选择放SDK的目录下有一个叫做.temp的文件夹,里面放的就是下载的临时文件。 .temp内部的文件夹里面能看到下…

平时积累的FPGA知识点(7)

平时在FPGA群聊等积累的FPGA知识点,第七期: 11 描述扇出的xilinx官方文档是? 解释:ug949 12 在BD中如何指定某个IP用global,其他的用OOC模式?因为某个模块引用的IP带着XPM,综合不了 解释&am…

DS:栈和队列的相互实现

创作不易,感谢友友们三连!! 一、前言 栈和队列的相互实现是用两个栈去实现队列或者是用两个队列去实现栈,这样其实是把问题复杂化的,实际中没有什么应用价值,但是通过他们的相互实现可以让我们更加深入地理…

ChatGPT绘图指南:DALL.E3玩法大全(一)

一、 DALLE.3 模型介绍 1、什么是 DALLE.3 模型? DALLE-3模型,是一种由OpenAI研发的技术,它是一种先进的生成模型,可以将文字描述转化为清晰的图片。这种模型的名称"DALLE"实际上是"Deep Auto-regressive Latent …

unity学习(26)——客户端与服务器合力完成注册功能(8)json编解码问题,大结局

服务器端发送的内容如下: 客户端所接受的内容如下: 是一样的,不是传输问题,少了一个解码的过程,之前那个addMessage函数应该是不能解码的! 具体解析一下数据包的内容:上边的是成功的&#xff0…

OpenCV边缘检测与视频读写

原理 OpenCV中的边缘检测原理主要基于图像梯度的计算,包括一阶梯度和二阶梯度。 一阶梯度:它反映了图像亮度变化的速度。Sobel算法就是一种以一阶梯度为基础的边缘检测算法。它通过计算图像在水平和垂直方向上的梯度来检测边缘。这种方法简单有效&…

python 与 neo4j 交互(py2neo 使用)

参考自:neo4j的python.py2neo操作入门 官方文档:The Py2neo Handbook — py2neo 2021.1 安装:pip install py2neo -i https://pypi.tuna.tsinghua.edu.cn/simple 1 节点 / 关系 / 属性 / 路径 节点(Node)和关系(relationship)是构成图的基础…
推荐文章