英文:
Assembly code throwing a stack overflow error and I can't find why
问题
我在为课程项目工作,必须在汇编中编写某些内容,我正在使用 PIC 16F887。我知道它具有 8 级堆栈,但我找不到为什么/在哪里超出堆栈的原因。
代码看起来像这样:
targ_temp EQU 0x20
CONT1 EQU 0x20
CONT2 EQU 0x21
TTEMPU EQU 0x22
TTEMPD EQU 0X23
aux EQU 0X24
FLAGS EQU 0X25
TEMPU EQU 0X26
TEMPD EQU 0X27
PESO EQU 0X28
CONT EQU 0X30
;**********************************************************************
ORG 0x000
goto main
ORG 0x004
BTFSC INTCON,2
CALL TIMEROVERFLOW
BTFSC INTCON,1
CALL SENSORVASO
BTFSC INTCON,0
CALL LEERTECLADO
BTFSC PIR1,6
CALL CONVERSOR
;BTFSC PIR1,5
;CALL EUSARTRECIEVE
;BTFSC PIR1,4
;CALL EUSARTTRANSMIT
// 清除标志位
retfie ; 从中断返回
main
// 设置
GOTO $
LEERTECLADO ; 轮询键盘
call DELAY
movlw 0x0F
iorwf PORTC,1
MOVLW 0XFF ; 我用这个来检查最后是否实际按下按钮,或者是否释放/按下了其他我没有映射的按键
BCF PORTC,3
BTFSS PORTB,7 ; <- 这是堆栈溢出的地方
movlw 0x1
BTFSS PORTB,6
movlw 0x2
BTFSS PORTB,5
movlw 0x3
BTFSS PORTB,4
goto tempres
clrf PORTC
bsf PORTC,3
bcf PORTC,2
BTFSS PORTB,7
movlw 0x4
BTFSS PORTB,6
movlw 0x5
BTFSS PORTB,5
movlw 0x6
BTFSS PORTB,4
goto tempset
clrf PORTC
BSF PORTC,2
BCF PORTC,1
BTFSS PORTB,7
movlw 0x7
BTFSS PORTB,6
movlw 0x8
BTFSS PORTB,5
movlw 0x9
clrf PORTC
BSF PORTC,1
BCF PORTC,0
BTFSS PORTB,6
movlw 0x0
BCF PORTC,3
BCF PORTC,2
BCF PORTC,1
BCF PORTC,0
addlw 0x1 ; 在这里我检查 w 是否溢出,如果溢出,意味着没有按下按钮
btfsc STATUS,2 ;
goto term ;
sublw 0x1 ; 如果没有溢出,我将它恢复到原始值
movwf aux
movf TTEMPU,0
movwf TTEMPD
movf aux,0
movwf TTEMPU
goto term
tempset
movlw 0X26
movwf FSR
BCF PORTA,7
BSF PORTA,6
BCF STATUS,7
BCF STATUS,6
goto term
tempres
clrf TTEMPU
clrf TTEMPD
MOVLW 0x22
movwf FSR
BCF PORTA,7
BSF PORTA,6
BCF STATUS,7
BCF STATUS,6
term
return
CONVERSOR
BTFSC FLAGS,6
GOTO SERV
CLRF TEMPD
CLRF TEMPU
MOVLW .25
adec
SUBWF ADRESH,1
INCF TEMPD,1
BTFSC STATUS,0
GOTO over
BTFSS STATUS,2
GOTO adec
GOTO tempter
over
DECF TEMPD,1
ADDWF ADRESH,1
MOVLW .10
SUBWF TEMPD,0
BTFSC STATUS,2
GOTO cien
MOVLW .3
aun
SUBWF ADRESH,1
INCF TEMPU,1
BTFSC STATUS,0
GOTO overun
BTFSS STATUS,2
GOTO aun
GOTO tempter
overun
DECF TEMPU,1
GOTO tempter
cien
MOVLW .10
MOVWF TEMPD
CLRF TEMPU
BCF PORTA,4
GOTO FINADC
tempter
MOVF TEMPD,0
SUBWF TTEMPD,0
BTFSC STATUS,0
GOTO apagar
BTFSS STATUS,2
GOTO mant
MOVF TEMPU,0
SUBWF TTEMPU,0
BTFSC STATUS,0
GOTO apagar
BTFSS STATUS,2
GOTO apagar
mant
BSF PORTA,4
BCF FLAGS,7
GOTO FINADC
apagar
BCF PORTA,4
BSF FLAGS,7
GOTO FINADC
SERV
MOVLW PESO
SUBWF ADRESH,0
BTFSC STATUS,0
GOTO VLLENO
BTFSS STATUS,2
GOTO VLLENO
GOTO FINADC
VLLENO
BCF FLAGS,6
BCF PORTA,5
BCF ADCON0,3
INCF CONT,1
MOVFW CONT
BANKSEL TXREG
MOVWF TXREG
BSF TXSTA,5
BANKSEL CONT
FINADC
BSF ADCON0,1
RETURN
SENSORVASO
call DELAY
BTFSS FLAGS,7
GOTO ENDVASO
BSF PORTA,5
BSF FLAGS,6
BSF ADCON0,3
ENDVASO
return
TIMEROVERFLOW
btfsc PORTA,7
goto inver
incf FSR,1
movf INDF,0
call TABLA
movwf PORTD
BCF PORTA,6
BSF PORTA,7
goto termi
<details>
<summary>英文:</summary>
I'm working on a project for a course and I have to program something in Assembly, I'm working with the PIC 16F887, I know it has a 8 level stack and I can't find why/where I'm going over it.
The code looks something like this:
targ_temp EQU 0x20
CONT1 EQU 0x20
CONT2 EQU 0x21
TTEMPU EQU 0x22
TTEMPD EQU 0X23
aux EQU 0X24
FLAGS EQU 0X25
TEMPU EQU 0X26
TEMPD EQU 0X27
PESO EQU 0X28
CONT EQU 0X30
;**********************************************************************
ORG 0x000
goto main
ORG 0x004
BTFSC INTCON,2
CALL TIMEROVERFLOW
BTFSC INTCON,1
CALL SENSORVASO
BTFSC INTCON,0
CALL LEERTECLADO
BTFSC PIR1,6
CALL CONVERSOR
;BTFSC PIR1,5
;CALL EUSARTRECIEVE
;BTFSC PIR1,4
;CALL EUSARTTRANSMIT
// clearing flags
retfie ; return from interrupt
main
//setup
GOTO $
LEERTECLADO ;polling the keypad
call DELAY
movlw 0x0F
iorwf PORTC,1
MOVLW 0XFF ;I use this to check at the end if a button is actually pressed, or if it was let go/pressed some other key that I'm not mapping
BCF PORTC,3
BTFSS PORTB,7 ;<- this is where the stack overflow originates from
movlw 0x1
BTFSS PORTB,6
movlw 0x2
BTFSS PORTB,5
movlw 0x3
BTFSS PORTB,4
goto tempres
clrf PORTC
bsf PORTC,3
bcf PORTC,2
BTFSS PORTB,7
movlw 0x4
BTFSS PORTB,6
movlw 0x5
BTFSS PORTB,5
movlw 0x6
BTFSS PORTB,4
goto tempset
clrf PORTC
BSF PORTC,2
BCF PORTC,1
BTFSS PORTB,7
movlw 0x7
BTFSS PORTB,6
movlw 0x8
BTFSS PORTB,5
movlw 0x9
clrf PORTC
BSF PORTC,1
BCF PORTC,0
BTFSS PORTB,6
movlw 0x0
BCF PORTC,3
BCF PORTC,2
BCF PORTC,1
BCF PORTC,0
addlw 0x1 ;here I check if w overflows, if it does it means no buttons were pressed
btfsc STATUS,2 ;
goto term ;
sublw 0x1 ;if it didn't overflow I bring it back to the original value
movwf aux
movf TTEMPU,0
movwf TTEMPD
movf aux,0
movwf TTEMPU
goto term
tempset movlw 0X26
movwf FSR
BCF PORTA,7
BSF PORTA,6
BCF STATUS,7
BCF STATUS,6
goto term
tempres clrf TTEMPU
clrf TTEMPD
MOVLW 0x22
movwf FSR
BCF PORTA,7
BSF PORTA,6
BCF STATUS,7
BCF STATUS,6
term return
CONVERSOR
BTFSC FLAGS,6
GOTO SERV
CLRF TEMPD
CLRF TEMPU
MOVLW .25
adec SUBWF ADRESH,1
INCF TEMPD,1
BTFSC STATUS,0
GOTO over
BTFSS STATUS,2
GOTO adec
GOTO tempter
over DECF TEMPD,1
ADDWF ADRESH,1
MOVLW .10
SUBWF TEMPD,0
BTFSC STATUS,2
GOTO cien
MOVLW .3
aun SUBWF ADRESH,1
INCF TEMPU,1
BTFSC STATUS,0
GOTO overun
BTFSS STATUS,2
GOTO aun
GOTO tempter
overun DECF TEMPU,1
GOTO tempter
cien MOVLW .10
MOVWF TEMPD
CLRF TEMPU
BCF PORTA,4
GOTO FINADC
tempter MOVF TEMPD,0
SUBWF TTEMPD,0
BTFSC STATUS,0
GOTO apagar
BTFSS STATUS,2
GOTO mant
MOVF TEMPU,0
SUBWF TTEMPU,0
BTFSC STATUS,0
GOTO apagar
BTFSS STATUS,2
GOTO apagar
mant BSF PORTA,4
BCF FLAGS,7
GOTO FINADC
apagar BCF PORTA,4
BSF FLAGS,7
GOTO FINADC
SERV
MOVLW PESO
SUBWF ADRESH,0
BTFSC STATUS,0
GOTO VLLENO
BTFSS STATUS,2
GOTO VLLENO
GOTO FINADC
VLLENO BCF FLAGS,6
BCF PORTA,5
BCF ADCON0,3
INCF CONT,1
MOVFW CONT
BANKSEL TXREG
MOVWF TXREG
BSF TXSTA,5
BANKSEL CONT
FINADC BSF ADCON0,1
RETURN
SENSORVASO
call DELAY
BTFSS FLAGS,7
GOTO ENDVASO
BSF PORTA,5
BSF FLAGS,6
BSF ADCON0,3
ENDVASO return
TIMEROVERFLOW
btfsc PORTA,7
goto inver
incf FSR,1
movf INDF,0
call TABLA
movwf PORTD
BCF PORTA,6
BSF PORTA,7
goto termi
inver
DECF FSR,1
movf INDF,0
call TABLA
movwf PORTD
BCF PORTA,7
BSF PORTA,6
termi
return
TABLA
ADDWF PCL,F
RETLW b'11111100' ;devuelve 0
RETLW b'01100000' ;1
RETLW b'11011010' ;2
RETLW b'11110010' ;3
RETLW b'01100110' ;4
RETLW b'10110110' ;5
RETLW b'10111110' ;6
RETLW b'11100000' ;7
RETLW b'11111110' ;8
RETLW b'11110110' ;9
RETLW b'11111010' ;.0
DELAY ;delay routine mainly for debouncing
movlw .3
movwf CONT2
ext
movlw .250
movwf CONT1
int
decfsz CONT1,1
goto int
decfsz CONT2,1
goto ext
return
END
Please excuse the tags in spanish, I get a stack overflow warning when simulating on proteus and touching the button that connects RB7 and RC3, also implemented it on a breadboard and had (I believe) the same thing happens, meaning that from what I could see the displays stopped switching and only one would get stuck with the same value displayed regardless of what I did.
I was using proteus to check everything was working as intending before moving it to the board until I came across this. The exact error it launches is: [PIC16 CORE] PC=0x00E9. Stack overflow executing CALL instruction. [U1]
I tried debugging on MPLAB X using the asyncronous stimulus, I followed the code line by line and it worked as intended. One thing I read on another forum as a response to someone having a similar issue was that the value added in tabla (adding it to pc) could be negative and it could go to a different call and overflow that way, but I can't see that happening here since it's a piece of code on a interrupt, so it can't be cut short, and it either places a value between 0-9 or it returns. I'll leave a chunk of code that I don't really think is necessary but maybe there's something there I'm not seeing. I'm at a loss.
</details>
# 答案1
**得分**: 2
堆栈溢出发生在`TIMEROVERFLOW`函数/例程中,我不能证明为什么会发生,但是使用指针和间接寻址会发送错误的W寄存器值来调用TABLA,这导致程序计数器(PC)跳转到不应该的位置,尽管我在设置中将值初始化为0。我用一个检查我尝试显示的内容的标志替换了这个例程,而不是依赖指针,现在不再出现堆栈溢出。
<details>
<summary>英文:</summary>
The Stack Overflow happened in the `TIMEROVERFLOW` function/routine, I can't attest to why, but using the pointer and indirect addressing was sending a wrong W reg value calling TABLA, which led to the pc going places it shouldn't, even though I initialized the values as 0 in the setup. I replaced the routine with a flag that checks what I'm trying to show instead of relying on the pointer and I'm not getting the stack overflow anymore.
</details>
# 答案2
**得分**: 1
是的,它确实*看起来*你只使用了三个级别(中断本身,调用特定处理函数,以及其中一些调用延迟的函数)<sup>(1)</sup>。
我唯一能建议的是,你首先找出`0x00e9`处的代码片段,这是堆栈溢出错误消息中列出的地址。这可能会提供一些线索。
然后,也许在`TABLA`的开头放置一些保护代码,以便它只为特定可接受的值执行加法操作。如果不是这样,就执行其他操作,比如选择默认值或进入像你的`main: goto $`那样的另一个无限循环。希望你可以通过地址总线上永不变化的PC来捕获它。
在我看来,这似乎是在你的正常(三级)路径之外调用的唯一可能性。例如,`addw pcl, f` *可能*最终会有效地跳回到中断例程中的某个位置,再次调用其中一个处理函数。多做几次,嘣,你的堆栈就耗尽了。
根据你的仿真器有多好,你可以跟踪程序计数器的每个值,并查看它的去向。
---
<sup>(1)</sup> 电路调试器可能会添加另一个级别,但仍然只有四个,远远在`16F877`的八级限制内。
<details>
<summary>英文:</summary>
Yes, it certainly *looks* like you only ever use three levels (the interrupt itself, the call to the specific handler function, and a call from some of them to delay)<sup>(1)</sup>.
All I can suggest is that you first figure out which piece of code is at `0x00e9`, the address listed in the stack overflow error message. That may provide a clue.
Then perhaps put some protection code at the start of `TABLA` so that it only does the add for specific acceptable value. If not, do something else, such as choose a default value or enter another infinite loop like your `main: goto $` one. Hopefully you can then pick it up by virtue of a never changing PC on the address bus.
That seems to me to be the only vector for calling outside of your normal (three-level) pathways. For example, the `addw pcl, f` *may* end up effectively jumping back to somewhere in the interrupt routine and once again calling one of the handler functions. Do that a couple of times and, bang, there's your stack exhausted.
Depending on how good your emulators are, you may be able to track every single value of the program counter and see where it's going.
---
<sup>(1)</sup> The in-circuit debugger may add another level but that's *still* only four, well within the limit of eight for the `16F877`.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论