Я пишу довольно основную программу в блоке PIC18. Это требует, чтобы я записал подпрограмму для умножения двух 16-разрядных чисел. Это - то, что я имею прямо сейчас:
;***********************************************************************
; mul_16bit: subroutine that multiplies two 16 bit numbers stored in
; addresses mul_16ptr1, mul_16ptr1+1 and mul_16ptr2,mul_16ptr2+1 and
; returns the 32-bit result in addresses mul_16res1 to mul_16res1+3
;***********************************************************************
mul_16bit:
movf mul_16ptr2, W ;multiply the lower bytes
mulwf mul_16ptr1, W
movff PRODH, mul_16res+1
movff PRODL, mul_16res
movf mul_16ptr2+1, W ;multiply upper bytes
mulwf mul_16ptr1+1, W
movff PRODH, mul_16res+3
movff PRODL, mul_16res+2
movf mul_16ptr2, W ;multiply lower byte of num2
mulwf mul_16ptr1+1, W ; and upper byte of num1
movf PRODL, W
addwf mul_16res+1, F
movf PRODH, W
addwfc mul_16res+2, F
movlw 0 ; add carry
addwfc mul_16res+3, F
movf mul_16ptr2+1, W ;multiply upper byte
;of num1 and lower
mulwf mul_16ptr1, W ; byte of num2
movf PRODL, W ;add the result to mul_16res
addwf mul_16res+1, F ;...
movf PRODH, W ;...
addwfc mul_16res+2, F ;...
movlw 0 ; add carry
addwfc mul_16res+3, F
return
Путем у меня есть записанный, прямо сейчас то, что это умножает числа, сохраненные в зарегистрированном, упомянутом в первом комментарии, и хранит их в 4 регистрах в комментарии. Это работает хорошо, если я только должен сделать это умножение несколько раз, т.е. Я могу просто сказать что-то как:
mul_16ptr1 set 0x45
mul_16ptr2 set 0x47
mul_16res set 0x50
call mul_16bit
Умножиться 0x45
и 0x47
и сохраните его в 0x50
. Проблема состоит в том, когда я должен назвать это несколько раз на различных данных, потому что ассемблер не позволит мне "установить" любой из указателей дважды. Я попытался использовать косвенный доступ (т.е. использовать LFSR1, LFSR2 и LFSR0 для хранения множимых и результата), но затем я просто вхожу в огромную путаницу POSTINC0, и т.д. должен там так или иначе сделать эту вещь вызова функции более хорошей?
Функции под PIC18 обычно используют специальные входные переменные, такие как RegA, RegB и RegR. Поэтому там объявлены:
RegA res 2 ;16bit var
ResB res 2 ;16bit var
ResR res 4 ;32bit var
Вызов подобной функции выглядит так:
;Constants declaration
OperandA set 1234
OperandB set 7777
;
;
;Prepare calling operand A
movlw low OperandA
movwf RegA
movlw high OperandA
movwf RegA + 1
;Prepare calling operand B
movlw low OperandB
movwf RegB + 0
movlw high OperandB
movwf RegB + 1
;Function call
call MullAB_16bit
;Result is in RegR
Да, язык ассемблера PIC делает многие вещи неоправданно сложными.
Я предполагаю, что вы делаете это в рамках обучения - в противном случае вы бы либо использовали библиотеку базовых математических функций, такую как у Роджера Фрауда или Фр. Томаса МакГахи, либо, возможно, перешли бы на язык более высокого уровня, где все вышеперечисленное можно заменить на "*" (BASIC, C, Pyastra, JAL, Forth и т.д.).
Соглашение о вызове, которое демонстрирует GJ, является чрезвычайно распространенным, особенно в коде, перенесенном с PIC16, который имел только один регистр FSR и не имел регистров "PLUSW".
Поскольку в PIC18 есть регистры "PLUSWx", можно использовать различные более приятные соглашения о вызовах. Есть ли способ подправить это немного больше, чтобы получить "re-entrant" код, который R. Reese рекомендует?
#include<18f4550>
OperandA res 2
OperandB res 2
Product res 4
clock_ticks res 2
useconds_per_clock_tick res 2
total_time res 4
; example of the "call" part of a possible 3-pointer calling convention.
; Public domain.
; To multiply by some number in Flash or EEPROM,
; first copy them (perhaps using TBLPTR/TABLAT)
; into some convenient temporary Operand buffer in RAM.
; Then:
; WARNING: untested code.
; put pointer to first (least-significant) byte of 16-bit operand A into FSR2
BANKSEL FSR0
lfsr2 OperandA
; put pointer to first (least-significant) byte of 16-bit operand B into FSR1
lfsr1 OperandB
; put pointer to first (least-significant) byte of 32-bit product into FSR0
lfsr0 Product
;Function call
call mul16x16bit
;Result is in Product
; example of calling the same subroutine with different arguments.
BANKSEL FSR0
lfsr2 clock_ticks
lfsr1 useconds_per_clock_tick
lfsr0 total_time
call mul16x16bit
; result is in total_time.
return
;***********************************************************************
; mull16x16bit: subroutine that multiplies two 16 bit numbers
; pointed to by the pointer FSR2, FSR2+1, FSR3, FSR3+1, and
; returns the 32-bit result in addresses pointed to by
; FSR0 to FSR0+3.
;***********************************************************************
; example of a function using a possible 3-pointer calling convention
; WARNING: untested code
; The pointers to operands are: FSR2, FSR1
; The pointer to the result is: FSR0.
; Mostly identical to code in the Microchip PIC18F2550 datasheet, page 98
; Public domain.
RESULT res 4 // temporary 4 byte register
TEMP EQU RESULT // temporary 1 byte register
mul_16bit:
movlw 1 ; multiply upper bytes
movff PLUSW2, TEMP
movf PLUSW1, W
mulwf TEMP
movff PRODH, RESULT+3
movff PRODL, RESULT+2
movf INDF2, W ;multiply the lower bytes
mulwf INDF1, W
movff PRODH, RESULT+1
movff PRODL, RESULT+0
movlw 1 ; multiply the high byte of num2
movf PLUSW2
mulwf INDF1 ; and the low byte of num1
movf PRODL, W
addwf RESULT+1, F
movf PRODH, W
addwfc RESULT+2, F
movlw 0 ; add carry
addwfc RESULT+3, F
movlw 1 ; multiply the high byte of num1
movf PLUSW1
mulwf INDF2 ; and the low byte of num2
movf PRODL, W
addwf RESULT+1, F
movf PRODH, W
addwfc RESULT+2, F
movlw 0 ; add carry
addwfc RESULT+3, F
movff RESULT+0, POSTINC0 ; copy result to destination where FSR points.
movff RESULT+1, POSTINC0
movff RESULT+2, POSTINC0
movff RESULT+3, POSTINC0
movlw 4
subwf FSR0 ; restore original value of FSR0.
return