PIC 汇编函数调用

发布于 2024-09-05 22:09:40 字数 2216 浏览 6 评论 0 原文

我正在用 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

0x450x47 相乘并将其存储在 0x50 中。问题是当我需要对不同的数据多次调用它时,因为汇编器不会让我“设置”任何指针两次。我尝试过使用间接访问(即使用 LFSR1、LFSR2 和 LFSR0 来存储被乘数和结果),但后来我陷入了 POSTINC0 等的混乱之中。有没有办法让这个函数调用变得更好?

I'm writing a pretty basic program in PIC18 assembly. It requires that I write a subroutine to multiply two 16-bit numbers. This is what I have right now:

;***********************************************************************
; 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

The way I have it written right now is that it multiplies numbers stored in the registered mentioned in the first comment and stores them in the 4 registers in the comment. This works well if I only need to do this multiplication once or twice, i.e. I can just say something like:

mul_16ptr1   set    0x45
mul_16ptr2   set    0x47
mul_16res    set    0x50
call         mul_16bit

To multiply 0x45 and 0x47 and store it in 0x50. The problem is when I need to call this more than once on different data, because the assembler won't let me "set" any of the pointers twice. I've tried using indirect access (i.e. using LFSR1, LFSR2, and LFSR0 to store the multiplicands and result) but then I just get in a huge mess of POSTINC0's etc. Is there anyway to make this function calling thing nicer?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

雾里花 2024-09-12 22:09:40

PIC18 下的函数通常使用专用输入变量,例如 RegA、RegB 和 RegR。
所以有声明:

RegA res 2    ;16bit var
ResB res 2    ;16bit var
ResR res 4    ;32bit var

调用suchlike函数看起来像:

;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

Functions under PIC18 normaly use dedicated input variables such as RegA, RegB and RegR.
So there are declarated:

RegA res 2    ;16bit var
ResB res 2    ;16bit var
ResR res 4    ;32bit var

Calling suchlike function looks like:

;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
甜是你 2024-09-12 22:09:40

是的,PIC 汇编语言使许多事情变得不必要地复杂。

我假设您将这样做作为学习体验的一部分 - 否则您将使用 基本数学函数库,例如 Roger Froud 或 Fr. 的函数库。 Thomas McGahee,或者也许切换到一种更高级的语言,其中上述所有语言都可以用“*”替换(BASIC、C、Pyastra、JAL、Forth 等)。

GJ 演示的调用约定非常常见,特别是在从 PIC16 移植的代码中,该代码只有一个 FSR 寄存器并且没有“PLUSW”寄存器。

由于 PIC18 具有“PLUSWx”寄存器,因此可以使用各种更好的调用约定。
有没有办法稍微调整一下以获得 R.里斯推荐?

#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

Yes, PIC assembly language makes many things unnecessarily complicated.

I'm assuming you are doing this as part of a learning experience -- otherwise you would either use a basic math function library such as the one by Roger Froud or Fr. Thomas McGahee, or perhaps switch to a higher-level language where all of the above can be replaced by "*" (BASIC, C, Pyastra, JAL, Forth, etc.).

The calling convention GJ demonstrates is extremely common, especially in code ported from PIC16 which only had one FSR register and no "PLUSW" registers.

Since the PIC18 has the "PLUSWx" registers, it's possible to use a variety of nicer calling conventions.
Is there a way to tweak this a little more to get the "re-entrant" code that R. Reese recommends?

#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
思念绕指尖 2024-09-12 22:09:40

您能否安排一些事情,以便它们在 FSR0-FSR2 指向您的操作数和结果寄存器时表现得更加明智? EG

  movf   POSTINC0,w,c
  mulwf  POSTINC1,c     ; Op0L*Op1L (now both point at MSB)
  movff  PRODL,POSTINC2 ; Result0
  movff  PRODH,INDF2    ; Result1
  mulwf  POSTDEC1,c     ; Op0L*Op1H (now 0 points at MSB 1 at LSB)
  movf   PRODL,w,c
  addwf  POSTINC2,f,c   ; Result1 (now points at Result2)
  movlw  0
  addwfc PRODH,w,c
  movwf  POSTDEC2,c     ; Result2 (now points at Result1)
  movf   INDF0,w,c      ; Op0H
  mulwf  POSTINC1,c     ; Op1L
  movf   PRODL,w,c
  addwf  POSTINC2,f,c   ; Result1
  movf   PRODH,w,c
  addwfc POSTINC2,f,c   ; Result2 (carry may be outstanding)
  clrf   INDF2,f,c      ; Result3
  rlcf   POSTDEC2,f,c   ; Store carry
  movf   INDF0,w,c      ; Op0H
  mulwf  POSTINC1,c     ; Op1H
  movf   PRODL,w,c
  addwf  POSTINC2,f,c
  movf   PRODH,w,c
  addwfc INDF2,f,c

LFSR 比手动移动大量数据更便宜。

Can you arrange things so that they'll behave sensibly with FSR0-FSR2 pointing at your operand and result registers? E.G.

  movf   POSTINC0,w,c
  mulwf  POSTINC1,c     ; Op0L*Op1L (now both point at MSB)
  movff  PRODL,POSTINC2 ; Result0
  movff  PRODH,INDF2    ; Result1
  mulwf  POSTDEC1,c     ; Op0L*Op1H (now 0 points at MSB 1 at LSB)
  movf   PRODL,w,c
  addwf  POSTINC2,f,c   ; Result1 (now points at Result2)
  movlw  0
  addwfc PRODH,w,c
  movwf  POSTDEC2,c     ; Result2 (now points at Result1)
  movf   INDF0,w,c      ; Op0H
  mulwf  POSTINC1,c     ; Op1L
  movf   PRODL,w,c
  addwf  POSTINC2,f,c   ; Result1
  movf   PRODH,w,c
  addwfc POSTINC2,f,c   ; Result2 (carry may be outstanding)
  clrf   INDF2,f,c      ; Result3
  rlcf   POSTDEC2,f,c   ; Store carry
  movf   INDF0,w,c      ; Op0H
  mulwf  POSTINC1,c     ; Op1H
  movf   PRODL,w,c
  addwf  POSTINC2,f,c
  movf   PRODH,w,c
  addwfc INDF2,f,c

LFSR is cheaper than manually moving around large amounts of data.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文