问题:
(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;
}