51汇编和c语言实现 两字节有符号数相乘

本文介绍了一种使用汇编语言和C语言实现的多位数乘法程序设计方案。该方案首先从外部RAM中读取乘数和被乘数,通过循环计算并存储结果。汇编部分利用DPTR指针进行地址操作,并通过判断正负数来确定最终结果的符号。C语言实现则使用指针变量完成类似的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题:
(1)从外部扩展RAM中读取被乘数和乘数,乘数存放于0100H处开始,总共3个乘数,每个乘数是3字节的有符号数。被乘数存放于0200H处开始,总共3个被乘数,每个被乘数是3字节的有符号数。
(2)把结果储存到内部存储区,从40H开始的地址处。

思路:
汇编实现
1.用DPTR双指针分别操作乘数和被乘数,R0存放的地址,存放结果,R2=3循环次数。
2.先进行符号位的判断,如果为负数需要变成正数,在这里我编写了一个函数可以把负数变成正数,就可以分别把乘数和被乘数放入函数中判定。
3.同时我设置了一个标志位R1,地址为0052H。若判定为负数,则加一,这样若两个数都为负数或者都为正数则末位为零,若其中一个为正数另一个为负数则末位为一。
4.编写两字节无符号数相乘函数,送入变成正数的两字节数函数使之相乘。
5.最后根据R1所在地址的数字末位为一还是零来确定最后结果的符号数。
6.循环三次,得到三个乘数与三个被乘数乘积的结果。
c语言实现详见代码
汇编代码

sfr AUXR =0x8e;
ORG 0000H
LJMP main
ORG 100H	;定义主程序入口,主程序代码首地址
main: 
;;;;;;;;;;;;;初始化,将被乘数与乘数输入到特定地址;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;    
init:;初始化,将被乘数与乘数输入到特定地址
	ORL AUXR,#00000010B ;选择片外扩展ram
	;存放三位乘数
	mov dptr,#0100h
	mov a,#044h
	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
	mov dptr,#0101h
	mov a,#055h
	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
	mov dptr,#0102h
	mov a,#-055h
	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
	mov dptr,#0103h
	mov a,#044h
	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
	mov dptr,#0104h
	mov a,#033h
	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
	mov dptr,#0105h
	mov a,#022h
	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中

	;存放三位被乘数
	mov dptr,#0200h
	mov a,#-78h
	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
	mov dptr,#0201h
	mov a,#078h
	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
	mov dptr,#0202h
	mov a,#-012h
	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
	mov dptr,#0203h
	mov a,#012h
	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
	mov dptr,#0204h
	mov a,#044h
	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
	mov dptr,#0205h
	mov a,#056h
	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
;;;;;;;;;;;;;;;;初始化双dptr指针的首地址和存放结果的首地址;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	AUXR1 equ 0xA2;等值指令在以后可以用auxr1代替0xA2
	ORL AUXR1,#00000001B;切换数据指针
	MOV DPTR,#0100H	 ;初始化DPTR1首地址
	INC AUXR1;		   
	MOV DPTR,#0200H	;初始化DPTR0首地址	  
	MOV A,#0043H	;初始化结果的低位
	MOV R0,A     ;将保存结果的最低位地址传递给R0
	MOV R2,#03;循环次数
	MOV R1,#0052H    ;存储一个标志位,表示最后的正负
;;;;;;;;;;;判断正负并且把负数变成正数储存到原来的位置;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LOOP:		
;;;;;;;;;;;;;判断乘数的正负并把负数变为正数;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	INC AUXR1;		   ;DPPTR1
	MOVX A, @DPTR
	MOV R6,A;   将最高位传入函数中判断正负
	LCALL NEG;
;;;;;;;;;;;;;;;;;;;;;;;;判断被乘数的正负并把负数变为正数;;;;;;;;;;;;;;;;;;;;;;;;
	INC AUXR1;		   ;DPTR0
	MOVX A, @DPTR
	MOV R6,A;将最高位传入函数中判断正负
	LCALL NEG;

;;;;;;;;;;;;;将片外ram数据传送给r6r7,r4r5相乘;;;;;;;;;;;;;;;;;;;;;;;;
    INC AUXR1;		   ;DPTR1

; ;;;;;   将两个字节的数字放入R6R7中;;	 ;;;;;;
	MOVX A,@DPTR;	;DPTR1
	MOV R6,A;		;取高字节
	INC DPTR;		;地址加一切换地址取低字节
	MOVX A,@DPTR;
	MOV R7,A;  取低字节  
 ;;;;将两个字节的数字放入R4R5中	;;	 ;;;;;
	INC AUXR1;切换dptr指针 dptr0
	MOVX A,@DPTR;
	MOV R4,A;
	INC DPTR;dptr存放的地址加一
	MOVX A,@DPTR;
	MOV R5,A;    
	LCALL  MUL2BYTE	  ;调用两字节相乘函数

;;;;;;;;;;;;;;;确定最后的符号;;;;;;;;;;;;;;;;
	 MOV A,@R1;
	 JB ACC.0 ,NEGTIVE;若低位为1则转移说明为负数,调用函数把结果变为负数
INIT1:
;;;;;;;;初始化下一次循环的地址;;;;;;;;;;;;;;;;;;;;;
	 INC DPTR;DPTR0,将dptr指针加一为下一次循环做准备
	 INC AUXR1;
	 INC DPTR;DPTR1
	 INC R0;
	 INC R0
	 INC R0
	 INC R0
	 INC R0;
	 INC R0
	 INC R0
	 ;结果储存首地址更新
     DJNZ R2,LOOP;	乘数未取完则跳转到loop	,否则结束
 	 LJMP END1;结束
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;两字节相乘函数  测试数据0FFFh*0FFFH,0123H*0132H,7548H*4568H,01H*01H,00H*00H
MUL2BYTE:		 
	;乘数低位与被乘数低位相乘
	MOV A,R7
	MOV B,R5
	MUL AB
	ADDC A,@R0;
	MOV @R0,A;将低位存储
	DEC R0	 ;R0中存放的地址减一
	MOV A,B;
	ADDC A,@R0;
	MOV @R0,A;将高位储存	
	;乘数高位与被乘数低位相乘
	MOV A,R6   ;
	MOV B,R5   ;
	MUL AB	   ;
	ADDC A,@R0  ; 本次的低位与上一位高位相加
	MOV @R0,A  ; 结果保存到特定位置
	DEC R0	 ;R0中存放的地址减一
	MOV A,B
	ADDC A,@R0  ;若有进位加上进位
	MOV @R0,A;将高位储存
	;乘数低位与被乘数高位相乘
	MOV A,R7	 ;
	MOV B,R4	 ;
	MUL AB		 ;
	INC R0	     ;
	ADD A,@R0    ;
	MOV @R0,A	 ;	
	MOV A,B      ;
	ADDC A,#0    ;
	DEC R0
	ADD A,@R0;	加上上一位高位,这里不需要ADDC因为进位信号上面已经加入
	MOV @R0,A;
	DEC R0;
	MOV A,#0;
	ADDC A,@R0;
	MOV @R0,A;
	INC R0;  在这里最高位加上进位信号,这是因为在下面操作A寄存器的时候进位信号可能会被清除
	;乘数高位与被乘数低位相乘
	MOV A,R6	 ;
	MOV B,R4	 ;
	MUL AB		 ;
	ADD A,@R0; 本次相乘低位与上次相乘高位相加
	MOV @R0,A
	MOV A,B
	DEC R0  ;
	ADDC A,@R0;
	MOV @R0,A;
	CLR C;
	RET
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;判断数字的正负并且把负数改成整数
NEG:
	CLR C;			   cy清零
	MOV A,R6 ;
	JB ACC.7,CHANGE;	JB以直接寻址的位转移指令,如果bit==1则转移 ,否则顺序执行
	RET
;将负数改成正数,因为51中储存负数是用补码进行存储的因此需要转变为原码,减一取反
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
CHANGE:
	INC @R1  ;
	MOV A,R6 ;
	SUBB A,#01H ;
	MOV R6,A  ;  减一

	MOV A,R6
	CPL A    ;
	MOV R6,A ;
	;取反
	MOV A,R6;
	MOVX @DPTR,A;将数据传送回原来的地址
	RET		  

;;;;;;;;判断为负数加负号(将高位变成1);;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;		
NEGTIVE:
	MOV A,@R0;
	ORL A,#10000000B
	MOV @R0,A
	INC @R1;  将标志位位清除为0;
	LJMP  INIT1;因为在这里不是用lcall调用的函数因此需要用LJMP返回去。	
END1:
END

c语言代码

#include <STC12C5A60S2.H>
//定义三个乘数的地址
xdata int a1 _at_  0x0100;
xdata int a2 _at_  0x0102;
xdata int a3 _at_  0x0104;
//定义三个被乘数的地址
xdata int b1 _at_  0x0200;
xdata int b2 _at_  0x0202;
xdata int b3 _at_  0x0204;
main()
{
	int i;	
	int xdata *a=0x100;	 //定义指针变量访问乘数
	int xdata *b=0x200;	 //定义指针变量访问被乘数
	long data  *c=0x40;	 //	定义指针变量访问结果,
	//因为int*int有可能超多int的值域因此定义long类型
	a1=0x1122; a2=-0x7fff; a3=-0x7fff;  //为乘数赋值 
	//这里因为最高一位为符号位因此乘数范围为-0x7fff~0x7fff
	b1=0x4455; b2=0x7fff; b3=-0x7fff;  //被乘数赋值
	for(i=0;i<3;i++)
	{
		*c=(long)(*a)*(long)(*b);	//乘数与被乘数相乘,
		//这里用了强制类型转化,都变成long类型相乘
		c++;
		a++;
		b++; //乘完之后各地址自增,按照自己数据长度自增;
		//比如b,c一次自增两个字节,c自增四个字节;
	}
	return 0;

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值