最初的问题:
我们中的一群人(电子工程专业的学生——英国)最近在我们自己的时代开始着手对 PIC16F84A 微控制器进行编程。需要将两个 8 位数字相乘,每个数字都没有已知的最小值/最大值。一位同学提出了以下想法。
multiply_numbers:
; Takes numbers in Num1 and Num2, and returns product in OutH:OutL
clrf OutH ; clear all non-input variables
clrf OutL
mult_loop
bcf STATUS,c ; clear carry bit
movfw Num2
addwf OutL ; add Num2 to OutL
btfsc STATUS,c ; check carry bit
incf OutH ; if set, increment OutH
decfsz Num1 ; decrement Num1
goto mult_loop ; if Num1 is not zero, repeat loop
return ; else return
我觉得这虽然在代码行方面很短,但对于较大的数字可能需要相对较长的时间来执行。我自己做了一些思考,开始沿着一条路线将一个数字向右移动,另一个向左移动,并在此过程中将左移的数字添加到输出中一定次数以到达最终答案。我做得不太对,但后来在 SO 上偶然发现了这个问题,这让我想到了将输入数字之一表示为:
N = a_0 + a_1*2 + a_2*2^2 + a_3*2^3 + ... + a_7*2^7
从那个出发点,我想出了这种方法,将两个 8 位数字相乘以获得 16 位输出(存储在两个 8 位寄存器中)。
multiply_numbers:
; Takes numbers in Num1 and Num2L, and returns product in OutH:OutL
clrf Num2H ; clear all non-input variables
clrf OutL
clrf OutH
mult_loop
btfsc Num1,0 ; test LSB of Num1
call add_num16 ; if set, add Num2H:Num2L to OutH:OutL
call shift_left ; shift Num2H:Num2L left (multiply by 2)
rrf Num1,f ; shift Num1 right
clrw ; clear working register (0x00)
bcf STATUS,z ; clear zero bit (3) of the STATUS register
addwf Num1,w ; add 0x00 to Num1
btfss STATUS,z ; if Num1 is zero, then exit loop
goto mult_loop ; else, continue with another iteration
return
add_num16
movfw Num2H
addwf OutH,f ; add Num2H to OutH
bcf STATUS,c ; clear carry bit (0) of the STATUS register
movfw Num2L
addwf OutL,f ; add Num2L to OutL
btfsc STATUS,c ; check carry bit
incf OutH,f ; increment OutH if set (OutL overflowed)
return
shift_left
bcf STATUS,c ; clear carry bit
rlf Num2L,f ; rotate Num2L left (carry -> LSB, MSB -> carry)
rlf Num2H,f ; rotate Num2H left, using carry bit from Num2L
return
我认为第二个示例在大多数情况下更快,仅仅是因为循环最多只能重复 8 次,而不是最多 256 次。
我对它们的相对速度/效率的假设是否正确?第二个代码块是否真的按照我的意图运行(是否有任何我错过的潜在问题)?最后,是否可以使用尚未采用的技术进一步优化这种乘法?
先感谢您。
PS 所有变量/寄存器都已正确定义了它们自己的地址。大量的代码注释是因为我们正在尝试编译一组例程,我们可以在将来引用这些例程,并且仍然一眼就知道发生了什么以及为什么。
PPS 这个问题与编程这张照片的个人/爱好兴趣有关,与任何当前的课程作业等无关。只是为了减轻你可能有的任何怀疑!
微控制器:PIC16F84A
开发环境:MPLABX IDE v1.10
编译器:mpasm (v5.43)
编辑#1:
- 不是测试 Num1 的 LSB 并将左移的 Num2H:Num2L 添加到 OutH:OutL,而是测试 Num1 的 MSB 并将 Num2 添加到左移的 OutH:OutL。
- 使 'shift_left' 内联而不是调用的子函数。
- 展开“mult_loop”以优化第 8 次迭代。
方法2改进:
multiply_numbers:
; Takes numbers in Num1 and Num2, and returns product in OutH:OutL
clrf OutL ; clear all non-input variables
clrf OutH
; 1st iteration
btfsc Num1,7 ; test MSB of Num1
call add_num8 ; if set, add Num2 to OutH:OutL
bcf STATUS,c ; clear carry bit
rlf OutL,f ; rotate OutL left (carry -> LSB, MSB -> carry)
rlf OutH,f ; rotate OutH left, using carry bit from OutL
rlf Num1,f ; shift Num1 left
; 2nd iteration
btfsc Num1,7
call add_num8
bcf STATUS,c
rlf OutL,f
rlf OutH,f
rlf Num1,f
; 3rd iteration
btfsc Num1,7
call add_num8
bcf STATUS,c
rlf OutL,f
rlf OutH,f
rlf Num1,f
; 4th iteration
btfsc Num1,7
call add_num8
bcf STATUS,c
rlf OutL,f
rlf OutH,f
rlf Num1,f
; 5th iteration
btfsc Num1,7
call add_num8
bcf STATUS,c
rlf OutL,f
rlf OutH,f
rlf Num1,f
; 6th iteration
btfsc Num1,7
call add_num8
bcf STATUS,c
rlf OutL,f
rlf OutH,f
rlf Num1,f
; 7th iteration
btfsc Num1,7
call add_num8
bcf STATUS,c
rlf OutL,f
rlf OutH,f
rlf Num1,f
; 8th iteration
btfss Num1,7 ; test MSB of Num1
return ; if not set, then return. else...
add_num8
bcf STATUS,c ; clear carry bit (0) of the STATUS register
movfw Num2
addwf OutL,f ; add Num2L to OutL
btfsc STATUS,c ; check carry bit
incf OutH,f ; increment OutH if set (OutL overflowed)
return