汇编:子程序设计

news2025/7/19 11:41:56

一、 实验要求

实验目的:

  1. 熟练掌握算术运算汇编指令的使用
  2. 熟练掌握子程序设计的基本方法
  3. 熟练掌握程序的调试方法

    

实验内容:

  1. 编程实现两个数:#8888H和#79H的乘除运算
  2. 结合实验1的代码,将加减乘除四则运算写成四个子程序,注意现场保护。

二、 实验设计

 1.整体思路

1DIV:

  • 初始化被除数的高位、低位和除数的寄存器,以及商的高位、低位和余数的寄存器。
  • 设置一个计数器R0,用于循环执行16次,以处理16位的被除数。
  • 进入循环,从被除数的高位开始,将被除数的高位和除数相减,并检查进位标志。
  • 如果进位标志为0,表示没有借位,说明被除数可以整除除数,直接跳转到RESULT标签。
  • 如果进位标志为1,表示有借位,说明被除数不能整除除数,余数增加1,并跳转至ENDING标签。
  • 在RESULT标签中,将被除数的高位和低位分别右环移一位,得到新的被除数。
  • 商的高位增加1。
  • 在ENDING标签中,计数器R0减1,如果计数器不等于0,则继续执行循环。
  • 循环结束后,程序结束。

(2)MUL:

  • 在START标签中,首先从NUM1和NUM2内存地址读取被除数和除数。
  • 使用MUL指令将被除数的低位与除数相乘,结果保存在R6和R5寄存器中,其中R6存储乘法的低位结果,R5存储乘法的高位结果。
  • 接着再次从NUM1和NUM2内存地址读取被除数和除数的高位。
  • 再次使用MUL指令将被除数的低位与除数相乘,结果保存在R4和A寄存器中,其中R4存储乘法的高位结果,A寄存器存储乘法的低位结果。
  • 将累加器A中的值与R5相加,进位标志也参与运算,结果保存在R5中作为中位的值。
  • 最终,R4存储计算结果的高位,R5存储计算结果的中位,R6存储计算结果的低位,即商和余数。

(3)FOUR:

  • 首先定义了两个数据变量NUM1和NUM2,并给它们赋初值。
  • 然后定义了保存现场寄存器的子程序(SAVE),将需要保护的寄存器值依次压入栈中,并为新的返回地址腾出空间。
  • 接着定义了恢复现场寄存器的子程序(RESTORE),将之前保存的寄存器值从栈中弹出,恢复到相应的寄存器中。
  • 进入主程序(START)后,依次调用加法子程序、减法子程序、乘法子程序和除法子程序,并在每一次调用子程序前后分别执行保存和恢复现场寄存器的操作。
  • 程序最后结束。

2.流程图

DIV

MUL

FOUR

            

3.主要模块设计思路及分析

(1)DIV:

  1. 初始化模块:在START标签中,将被除数的高位、低位和除数的寄存器初始化,以及商的高位、低位和余数的寄存器初始化。
  2. 商的计算模块:在RESULT标签中,通过右环移操作更新被除数的高位和低位,同时商的高位增加1。
  3. 循环模块:使用计数器R0和DJNZ指令来控制循环次数,确保循环执行16次。

(2)MUL:

  1. 数据读取模块:使用MOV和MOVC指令从指定内存地址读取乘数1和乘数2的数据,分别存入R0和B寄存器中。
  2. 低位乘法模块:使用MUL指令将R0和B寄存器中的值相乘,结果保存在R6和A寄存器中。
  3. 高位乘法模块:再次使用MUL指令将R0和B寄存器中的值相乘,结果保存在R4和A寄存器中。
  4. 加法模块:使用ADDC指令将A寄存器中的值与R5寄存器中的值相加,进位标志也参与运算,结果保存在R5寄存器中。 

(3)FOUR:  

现场保护子程序(SAVE):

  1. 通过PUSH指令将PSW寄存器、B寄存器、DPL寄存器、DPH寄存器和新的返回地址依次压入栈中。
  2. 将栈指针SP的值移动到累加器A中,并为新的返回地址腾出空间(4个字节)。
  3. 将新的返回地址压入栈中,将栈指针SP的值移回SP寄存器。
  4. 保存现场寄存器的值完成。

现场恢复子程序(RESTORE):

  1. 通过POP指令将之前保存的寄存器值从栈中依次弹出。
  2. 恢复到相应的寄存器中,如数据指针高位DPH、数据指针低位DPL、B寄存器和PSW寄存器。
  3. 现场恢复完成。

加法子程序(ADD_NUMS):

  1. 将NUM1和NUM2的低位相加,结果保存在R0中,在此之前需要设置DPTR指向NUM1。
  2. 将NUM1和NUM2的高位相加,结果保存在R1中。
  3. 加法过程中使用了MOV和ADDC指令实现低位和高位的相加,并且考虑进位标志。
  4. 最终结果保存在R1和R2中。

减法子程序(SUB_NUMS):

  1. 将NUM1和NUM2的低位相减,结果保存在R0中,在此之前需要设置DPTR指向NUM1。
  2. 将NUM1和NUM2的高位相减,结果保存在R1中。
  3. 减法过程中使用了MOV和SUBB指令实现低位和高位的相减,并且考虑借位标志。
  4. 最终结果保存在R1和R2中。

乘法子程序(MUL_NUMS):

  1. 将NUM1和NUM2的低位相乘,结果保存在R0中,在此之前需要设置DPTR指向NUM1。
  2. 将NUM1和NUM2的高位相乘,结果保存在R4和寄存器A中。
  3. 乘法过程中使用了MOV和MUL指令实现低位和高位的相乘。
  4. 最终结果保存在R4、R5和R6中。

除法子程序(DIV_NUMS):

  1. 设置被除数的初始值和除数的值。
  2. 使用R0作为循环计数器,执行16次循环,将一个16 bit的被除数除以一个8 bit的除数,通过右移和减法来实现除法运算。
  3. 商的高位增加1,余数逢低位增加1的情况下进行修正。
  4. 循环结束后,最终结果保存在R0和R1中。

三、实现效果

DIV

MUL

FOUR

      

四、 总结

1.在编写除法运算的时候,第一次编写的时候出现了死循环,最后我的解决方式是通过计算的位数限定了循环的结果,死循环的问题就解决了。

2.在计算乘法运算的时候,前几次结果出现了错误,最后的解决方式是将低位的进位和中位的低位进行相加,结果最后显示正确。

3.通过这次实验,我深刻体会到了汇编语言的高效性和灵活性,以及8051单片机所具有的强大的功能。同时,我也意识到写汇编代码需要耐心和细心,需要对指令和操作数的含义非常清楚。

附录:
(1)DIV:
ORG 0000h
START:
     MOV R1, #88H		 ; 将被除数的高位存入R1
     MOV R2, #88H		 ; 将被除数的低位存入R2
     MOV R3, #79H		 ; 将除数存入R1

     MOV R4, #0          ; 将商的高位定义为0
     MOV R5, #0          ; 将商的低位定义为0
     MOV R6, #0          ; 将余数定义为0

     MOV R0, #16         ; 由于需要将一个16 bit的被除数除以一个8 bit的除数,因此需要执行16次循环,才能将所有的被除数都除尽。
						 ; 并且为了避免出现死循环,在这里设置了一个计数器,用来计算循环共执行16次

LOOP:
     MOV A, R1           ; 用A暂时存放被除数的高位
     SUBB A, R3          ; 用A减除数

     JNC RESULT          ; 如果进位标志为"0",即没有借位的情况下,直接跳转至RESULT
     INC R6              ; 余数增1
     SJMP ENDING

RESULT:
     CLR C               ; 进位标志清"0"
     MOV A, R1           ; 把被除数的高位存放至A
     RRC A               ; 把经过进位标志位的累加器A右环移
     MOV R1, A           ; 把结果存回被除数的高位

     MOV A, R2           ; 把被除数的低位存放至A
     RRC A               ; 把经过进位标志位的累加器A右环移
     MOV R2, A           ; 把结果存回被除数的低位

     INC R4              ; 商的高位增1

ENDING:
     DJNZ R0, LOOP       ; 计数器控制循环

     END

(2)MUL:
ORG 0000h
NUM1: DB 88H,88H         ; data: 88 88
                         ; addr: 0H 1H
NUM2: DB 79H			 ; data: 79
                         ; addr: 2H

START:
      MOV DPTR, #0H		 ; 用于读取数据
      MOV A, #1H
	  MOVC A, @A+DPTR    ; 读取程序储存器地址1H数据88(低位)
	  MOV R0, A          ; 使用R0暂时储存数据
	  MOV A, #2H
	  MOVC A, @A+DPTR    ; 读取程序储存器地址2H数据79
	  MOV B, A
	  MOV A, R0
	  MUL AB           ; 低位相乘
	  MOV R6, B		   ; 将低位保存到R6,作为低位的值
	  MOV R5, A        ; 将高位保存到R5

      MOV A, #0H
	  MOVC A, @A+DPTR    ; 读取程序储存器地址1H数据88(高位)
	  MOV R0, A          ; 使用R0暂时储存数据
	  MOV A, #2H
	  MOVC A, @A+DPTR    ; 读取程序储存器地址2H数据79
	  MOV B, A
	  MOV A, R0
	  MUL AB             ; 低位相乘
	  MOV R4, A			 ; 将寄存器A(高位结果)的值保存到寄存器R4中,作为高位的值
	  MOV A, B			 ; 将寄存器B(低位结果)的值移回累加器A



	  ADDC A, R5		 ; 将累加器A中的值与寄存器R5中的值相加,进位标志也参与运算
	  MOV R5, A			 ; 将累加器A中的结果保存到寄存器R5中,作为中位的值

      END

(3)FOUR:
ORG 0000h
NUM1:	DB 88H, 88H	   ; data: 88 88
					   ; addr: 0H 1H
NUM2:	DB 79H         ; data: 79
					   ; addr: 2H


SAVE:			 ;现场保护子程序,保存现场寄存器的值,并开启中断
    PUSH PSW     ; 将程序状态字PSW压入栈中
    PUSH B       ; 将寄存器B的值压入栈中
    PUSH DPL     ; 将数据指针低位DPL的值压入栈中
    PUSH DPH     ; 将数据指针高位DPH的值压入栈中
    MOV  A,SP    ; 将栈指针SP的值移动到累加器A中
    ADD  A,#04h  ; 为新的返回地址腾出空间(4个字节)
    PUSH ACC     ; 将新的返回地址压入栈中
    MOV  SP,A    ; 将栈指针SP的值移回SP寄存器

RESTORE:		 ; 现场恢复子程序,恢复现场寄存器的值,并关闭中断
    POP  ACC     ; 将返回地址弹出栈中
    MOV  SP,ACC  ; 将返回地址移回栈指针SP中
    POP  DPH     ; 恢复数据指针高位DPH的值
    POP  DPL     ; 恢复数据指针低位DPL的值
    POP  B       ; 恢复寄存器B的值
    POP  PSW     ; 恢复程序状态字PSW的值




ADD_NUMS:		          ; 加法子程序,将NUM1和NUM2的值相加,结果保存在R1和R2中
        MOV DPTR, #0H     ; 加法模块 —— 低位
							   
		MOV A, #1H		  
		MOVC A, @A+DPTR   ; 读取程序存储器地址 2H 数据 77(低位)
		MOV R0, A		  ; 使用 R0 暂时存储数据
		MOV A, #2H
		MOVC A, @A+DPTR   ; 读取程序存储器地址 5H 数据 99(低位)
		ADDC A, R0		  ; 低位相互加
		MOV R0, A
		

		MOV A, #0H		  
		MOVC A, @A+DPTR
		MOV R1, A          ;读取最终的高位结果
		ADDC A, R0
		MOV R2, A          ; 读取最终的低位结果



SUB_NUMS:				  ; 减法子程序,将NUM1和NUM2的值相减,结果保存在R1和R2中
        MOV DPTR, #0H     ; 加法模块 —— 低位
							   
		MOV A, #1H		  
		MOVC A, @A+DPTR   ; 读取程序存储器地址 2H 数据 77(低位)
		MOV R0, A		  ; 使用 R0 暂时存储数据
		MOV A, #2H
		MOVC A, @A+DPTR   ; 读取程序存储器地址 5H 数据 99(低位)
		SUBB A, R0		  ; 低位相互加
		MOV R0, A
		

		MOV A, #0H		  
		MOVC A, @A+DPTR
		MOV R1, A          ;读取最终的高位结果
		SUBB A, R0
		MOV R2, A          ; 读取最终的低位结果


MUL_NUMS:				 ; 乘法子程序,将NUM1和NUM2的值相乘,低位结果保存在R6中,中位结果保存在R5中,高位结果保存在R6中
      MOV DPTR, #0H		 ; 用于读取数据
      MOV A, #1H
	  MOVC A, @A+DPTR    ; 读取程序储存器地址1H数据88(低位)
	  MOV R0, A          ; 使用R0暂时储存数据
	  MOV A, #2H
	  MOVC A, @A+DPTR    ; 读取程序储存器地址2H数据79
	  MOV B, A
	  MOV A, R0
	  MUL AB           ; 低位相乘
	  MOV R6, B		   ; 将低位保存到R6,作为低位的值
	  MOV R5, A        ; 将高位保存到R5

      MOV A, #0H
	  MOVC A, @A+DPTR    ; 读取程序储存器地址1H数据88(高位)
	  MOV R0, A          ; 使用R0暂时储存数据
	  MOV A, #2H
	  MOVC A, @A+DPTR    ; 读取程序储存器地址2H数据79
	  MOV B, A
	  MOV A, R0
	  MUL AB             ; 低位相乘
	  MOV R4, A			 ; 将寄存器A(高位结果)的值保存到寄存器R4中,作为高位的值
	  MOV A, B			 ; 将寄存器B(低位结果)的值移回累加器A

	  ADDC A, R5		 ; 将累加器A中的值与寄存器R5中的值相加,进位标志也参与运算
	  MOV R5, A			 ; 将累加器A中的结果保存到寄存器R5中,作为中位的值




DIV_NUMS:				 ; 除法子程序,将NUM1除以NUM2,商保存在R0中,余数保存在R1中
     MOV R1, #88H		 ; 将被除数的高位存入R1
     MOV R2, #88H		 ; 将被除数的低位存入R2
     MOV R3, #79H		 ; 将除数存入R1

     MOV R4, #0          ; 将商的高位定义为0
     MOV R5, #0          ; 将商的低位定义为0
     MOV R6, #0          ; 将余数定义为0

     MOV R0, #16         ; 由于需要将一个16 bit的被除数除以一个8 bit的除数,因此需要执行16次循环,才能将所有的被除数都除尽。
						 ; 并且为了避免出现死循环,在这里设置了一个计数器,用来计算循环共执行16次

LOOP:
     MOV A, R1           ; 用A暂时存放被除数的高位
     SUBB A, R3          ; 用A减除数

     JNC RESULT          ; 如果进位标志为"0",即没有借位的情况下,直接跳转至RESULT
     INC R6              ; 余数增1
     SJMP ENDING

RESULT:
     CLR C               ; 进位标志清"0"
     MOV A, R1           ; 把被除数的高位存放至A
     RRC A               ; 把经过进位标志位的累加器A右环移
     MOV R1, A           ; 把结果存回被除数的高位

     MOV A, R2           ; 把被除数的低位存放至A
     RRC A               ; 把经过进位标志位的累加器A右环移
     MOV R2, A           ; 把结果存回被除数的低位

     INC R4              ; 商的高位增1

ENDING:
     DJNZ R0, LOOP       ; 计数器控制循环


START:
    CALL SAVE            ; 执行现场保护子程序,保存现场寄存器的值
    CALL ADD_NUMS        ; 执行加法子程序,将NUM1和NUM2的值相加,结果保存在R0中
    CALL SUB_NUMS        ; 执行减法子程序,将NUM1和NUM2的值相减,结果保存在R0中
    CALL MUL_NUMS        ; 执行乘法子程序,将NUM1和NUM2的值相乘,结果保存在R0和R1中
    CALL DIV_NUMS        ; 执行除法子程序,将NUM1除以NUM2,商保存在R0中,余数保存在R1中
    CALL RESTORE         ; 执行现场恢复子程序,恢复现场寄存器的值
    END                  ; 结束

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从概念表达到安全验证:智能驾驶功能迎来系统性规范

随着辅助驾驶事故频发,监管机制正在迅速补位。面对能力表达、使用责任、功能部署等方面的新要求,行业开始重估技术边界与验证能力,数字样机正成为企业合规落地的重要抓手。 2025年以来,围绕智能驾驶功能的争议不断升级。多起因辅…

DeepSeek基于注意力模型的可控图像生成

DeepSeek大模型高性能核心技术与多模态融合开发 - 商品搜索 - 京东 图像的加噪与模型训练 在扩散模型的训练过程中,首先需要对输入的信号进行加噪处理,经典的加噪过程是在图像进行向量化处理后在其中添加正态分布,而正态分布的值也是与时间…

“端 - 边 - 云”三级智能协同平台的理论建构与技术实现

摘要 随着低空经济与智能制造的深度融合,传统集中式云计算架构在实时性、隐私保护和资源效率上的瓶颈日益凸显。本文提出“端 - 边 - 云”三级智能协同平台架构,以“时空 - 资源 - 服务”三维协同理论为核心,构建覆盖终端感知、边缘计算、云端…

AI时代,如何实现人机共舞?

在科技飞速发展的当下,人工智能(AI)已不再是科幻作品中的遥远想象,而是深入渗透到我们生活与工作的方方面面。从智能手机中的语音助手,到金融领域的风险预测模型;从医疗影像的智能诊断,到工业生…

OceanBase 在业务监控系统中的应用实践

本文作者来自于一家总部在宁波的新能源上市公司,公司业务包括光伏新能源产品的研发与产销。 作为年产值达百亿的企业,监控系统是不可或缺的IT管理体系之一,对于确保业务连续性及预警风险非常重要。2022年,公司选择把Zabbix作为企业…

每日Prompt:品牌化键盘键帽

提示词 一个超逼真的3D渲染图,展示了四个机械键盘键帽,排列成紧密的2x2网格,所有键帽相互接触。从等轴测角度观察。一个键帽是透明的,上面用红色印刷着“{just}”字样。另外三个键帽采用颜色:{黑色、紫色和白色}。一个…

超声波传感器模块

欢迎来到 破晓的历程的 博客 ⛺️不负时光,不负己✈️ 文章目录 1.HC-SR04介绍2.HC-SR04原理介绍2.1原理概述3.2原理详解 4驱动代码编写4.1写前思考4.2硬件连线 5.总结hcsr04.hhcsr04.c 1.HC-SR04介绍 超声波传感器有很多种类的型号:HC-SR04、UC-025、…

LeetCode 513 找树左下角的值 LeetCode 112 路径总和 LeetCode106 从中序与后序遍历序列构造二叉树

LeetCode 513 找树左下角的值 迭代法——层序遍历 思路:对树进行层序遍历操作,层序遍历完后,输出树最后一层的第一个节点。 # Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val0, leftNone, r…

『大模型笔记』Langchain作者Harrison Chase专访:环境智能体与全新智能体收件箱

Langchain作者Harrison Chase专访:环境智能体与全新智能体收件箱 文章目录 摘要访谈内容什么环境智能体为什么要探索环境智能体怎么让人类能更方便地和环境智能体互动参考文献摘要 LangChain 的 CEO Harrison Chase 提出了_“环境智能体”(Ambient Agents)的概念,这是一种…

SpringBoot的外部化配置

一、什么是外部化配置 外部化配置是指把应用程序中各种可配置的参数、属性等信息,从代码内部提取出来,放置在外部的配置文件、数据库或配置中心等地方(比如使用.properties、.yml 或.xml 等格式的文件)进行管理。提高应用程序的可…

数字IC后端实现教程 | Early Clock Flow和Useful skew完全不是一个东西

数字后端零基础入门系列 | Innovus零基础LAB学习Day10 Q: Early clock flow和useful skew都是做短某段路径,这两个有什么区别呢,既然这样还用useful skew是不是有点多余了? Useful Skew技术 在不使用useful skew技术,第一级FF到第二级FF的…

MySQL OCP试题解析(3)

试题如图所示: 一、解析 正确选项:D)The backup can be impacted when DDL operations run during the backup(备份期间运行的 DDL 操作可能影响备份) 1. 关键知识点解析: 题目中的命令 mysqlbackup 使用了…

SpringCloud之Gateway基础认识-服务网关

0、Gateway基本知识 Gateway 是在 Spring 生态系统之上构建的 API 网关服务,基于 Spring ,Spring Boot 和 Project Reactor 等技术。 Gateway 旨在提供一种简单而有效的方式来对 API 进行路由,以及提供一些强大的过滤器功能,例如…

STM32-DMA数据转运(8)

目录 一、简介 二、存储器映像 三、DMA框图​编辑 四、DMA基本结构 五、两个数据转运的实例 一、简介 直接存储器存取简称DMA(Direct Memory Access),它是一个数据转运小助手,主要用来协助CPU,完成数据转运的工作…

电机控制储备知识学习(一) 电机驱动的本质分析以及与磁相关的使用场景

目录 电机控制储备知识学习(一)一、电机驱动的本质分析以及与磁相关的使用场景1)电机为什么能够旋转2)电磁原理的学习重要性 二、电磁学理论知识1)磁场基础知识2)反电动势的公式推导 附学习参考网址欢迎大家…

使用 React 实现语音识别并转换功能

在现代 Web 开发中,语音识别技术的应用越来越广泛。它为用户提供了更加便捷、自然的交互方式,例如语音输入、语音指令等。本文将介绍如何使用 React 实现一个简单的语音识别并转换的功能。 功能概述 我们要实现的功能是一个语音识别测试页面&#xff0…

[Git]ssh下用Tortoisegit每次提交都要输密码

问题描述 ssh模式下,用小乌龟提交代码,即使在git服务端存储了公钥,仍然要每次输入密码。 原因分析 小乌龟需要额外配置自己的密钥,才能免除每次输密码。 解决方案 1.配置好ssh密钥 具体方法参考我前一篇文章: […

如何查看项目是否支持最新 Android 16K Page Size 一文汇总

前几天刚聊过 《Google 开始正式强制 Android 适配 16 K Page Size》 之后,被问到最多的问题是「怎么查看项目是否支持 16K Page Size」 ?其实有很多直接的方式,但是最难的是当你的项目有很多依赖时,怎么知道这个「不支持的动态库…

ESP32C3连接wifi

文章目录 🔧 一、ESP32-C3 连接 Wi-Fi 的基本原理(STA 模式)✅ 二、完整代码 注释讲解(适配 ESP32-C3)📌 三、几个关键点解释🔚 四、小结 🔧 一、ESP32-C3 连接 Wi-Fi 的基本原理&a…

机器学习中分类模型的常用评价指标

评价指标是针对模型性能优劣的一个定量指标。 一种评价指标只能反映模型一部分性能,如果选择的评价指标不合理,那么可能会得出错误的结论,故而应该针对具体的数据、模型选取不同的的评价指标。 本文将详细介绍机器学习分类任务的常用评价指…