assembly 在16F84A上使用PIC组件时出现堆栈下溢问题

wz8daaqr  于 2022-11-30  发布在  其他
关注(0)|答案(1)|浏览(122)

我有一个课程的小项目,要求我们使用PIC汇编语言生成PWM信号。为了简化,我将高时间设置为5ms,低时间设置为15ms,这样我就可以多次调用同一个延迟子例程。多次调用此延迟似乎会导致堆栈下溢问题。
我真的不知道我可以尝试解决这个问题,因为我对编程非常新鲜。我已经尝试在这个网站上搜索,以及一般,但还没有能够找到一个解决方案。我正在使用MPLab 9.82以及。
代码如下:

list        p=16F84A      
#include    <p16F84A.inc>

    __CONFIG _CP_OFF & _XT_OSC & _PWRTE_ON & _WDT_OFF ;turn off watchdog timer

org 0x00 ; program starts at 0x00

counter equ 4Fh ; create a counter at position 4Fh in RAM

    BSF STATUS, RP0 ; select bank 1
    BCF TRISB, D'1' ; set port B pin 1 to output
    BCF STATUS, RP0 ; select bank 0

    goto main

main

    BSF PORTB,1 ; turn port B pin 1 on
    call delay_5ms ; calls sub routine for 5ms delay
    BCF PORTB,1 ; turn port B pin 1 off
    call delay_5ms ; calls sub routine for 5ms delay
    call delay_5ms ; calls sub routine for 5ms delay
    call delay_5ms ; calls sub routine for 5ms delay

delay_5ms 

    movlw D'200' ; put decimal number 200 into working register
    movwf counter ; move 200 from working register into counter

lp  nop ; no operation. just take 1 instruction
    nop  ; 1 instruction
    decfsz counter ; 1 instruction and decreases counter by 1
    goto lp ; 2 instructions (goto takes 2 instructions)
    return

end

mplab 9.82 Simulator code

kcwpcxri

kcwpcxri1#

正如Hans Passant所指出的,main需要有某种方法来防止fallthrough。为了详细解释这里发生的事情,让我们来看一个C函数:

void doNothing()
{
    asm("nop");
}

虽然不是最好的例子,但我需要一个没有return语句的函数。至少,在C语言中没有return语句,但在汇编语言中,这个函数(假设它没有被内联)看起来像这样:

doNothing:
nop
return

用C编写的函数将始终编译为带有 *return指令 * 的汇编函数(除非编译器内联该函数),即使您的函数没有C return语句。
为什么需要这样做?因为ASM标签不存在。
程序集源文件中带标签的代码行只是对特定内存地址的引用。

main:
   nop
   nop
   nop
   goto main

goto main只是一个硬件抽象,意思是“后藤whatever memory address main碰巧是什么。分支是相对的还是绝对的,这无关紧要--汇编程序为您进行计算,并将goto main中的main替换为所需的偏移量,以使分支将您带到所需的位置。然而,当创建可执行文件时,main:(注意后面的冒号,我指的是标签本身)没有了。这导致了汇编语言中的一些怪癖,而这些怪癖在其他语言中不会发生:

  • 首先,我可以有多个指向同一指令的标签,这样做不会增加可执行文件的大小。
foo:
bar:
baz:
nop

第二,也是最重要的一点,没有什么可以阻止执行通过标签。

main

    BSF PORTB,1 ; turn port B pin 1 on
    call delay_5ms ; calls sub routine for 5ms delay
    BCF PORTB,1 ; turn port B pin 1 off
    call delay_5ms ; calls sub routine for 5ms delay
    call delay_5ms ; calls sub routine for 5ms delay
    call delay_5ms ; calls sub routine for 5ms delay

delay_5ms
    movlw D'200' ; put decimal number 200 into working register

完全一样

main

    BSF PORTB,1 ; turn port B pin 1 on
    call delay_5ms ; calls sub routine for 5ms delay
    BCF PORTB,1 ; turn port B pin 1 off
    call delay_5ms ; calls sub routine for 5ms delay
    call delay_5ms ; calls sub routine for 5ms delay
    call delay_5ms ; calls sub routine for 5ms delay
    movlw D'200' ; put decimal number 200 into working register

也就是说,如果你想执行某个函数固定的次数,而没有循环的开销,你可以使用fallthrough。

foo:
;execute "bar" four times

call bar
call bar
call bar

bar:
   nop
   return

相关问题