|
|
View previous topic :: View next topic |
Author |
Message |
kein
Joined: 23 Jul 2007 Posts: 103
|
What is causing Stack underflow on inline assembly |
Posted: Thu Feb 05, 2015 9:16 am |
|
|
Gents,
I'm having stack underflow issue with my code. I've written an inline assembly code as shown below. For some reasons, it generates "stack underflow executing return instruction". When I ran the program, it never goes pass the inline assembly function and never execute the last two functions:"delay_ms(50); output_b(0x00);"
Code: | #include <16F877A.h>
#use delay(crystal=20MHZ)
#FUSES HS,NOWDT,NOLVP,NODEBUG,PUT,BROWNOUT //No Watch Dog Timer
#define Porta 0x05 //clock PIN_A1
void paralIn165(byte buff);
unsigned char rcvReg;
void main()
{
unsigned char cntrlWord;
set_tris_a(0b00000001); //porta are all outputs; pin 1 is input
set_tris_b(0b00000000); //all buits of port is set for output
cntrlWord = 0x04;
rcvReg = 0x00;
output_b(0b00000000);
output_high(PIN_A2);
while(1)
{
//TODO: User Code
delay_ms(5);
output_b(rcvReg);
paralIn165(cntrlWord);
delay_ms(50);
output_b(0x00);
delay_ms(200);
}
}
void paralIn165(int8 buff)
{
unsigned char tmp;
unsigned char counter;
#asm
clrf rcvReg ; clear receive register
movlw 0x08 ;w = 8
movwf counter ;counter = w = 8
bcf porta, 2 ;clear bit number 2
bsf porta, 2 ;set bit number 2
getBit:
movlw 0b00000001 ;
movwf tmp ;save in temp
rrf tmp, f ;rotate bit into carry flag
rlf rcvReg,f ;rotate the received bit into received buffer
decfsz counter,f ;decrement counter skip next instruction if 0
goto shift
return
shift: ;clock in the data
bsf porta, 1
bcf porta, 1
goto getBit ;get another bit
#endasm
} |
I will appreciate your help with this.
Thanks. |
|
|
Jerson
Joined: 31 Jul 2009 Posts: 125 Location: Bombay, India
|
|
Posted: Thu Feb 05, 2015 10:48 am |
|
|
Code: | void paralIn165(int8 buff)
{
unsigned char tmp;
unsigned char counter;
#asm
clrf rcvReg ; clear receive register
movlw 0x08 ;w = 8
movwf counter ;counter = w = 8
bcf porta, 2 ;clear bit number 2
bsf porta, 2 ;set bit number 2
getBit:
movlw 0b00000001 ;
movwf tmp ;save in temp
rrf tmp, f ;rotate bit into carry flag
rlf rcvReg,f ;rotate the received bit into received buffer
decfsz counter,f ;decrement counter skip next instruction if 0
goto shift
;*****return****** problematic
goto endroutine ;; this is added
shift: ;clock in the data
bsf porta, 1
bcf porta, 1
goto getBit ;get another bit
endroutine: ;; this is added
#endasm
}
|
This should fix your problem. I leave it as an exercise for you to understand why. |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
|
Posted: Thu Feb 05, 2015 11:49 pm |
|
|
Thank you so much. I guess return causes stack issues - program returns to random location. |
|
|
gpsmikey
Joined: 16 Nov 2010 Posts: 588 Location: Kirkland, WA
|
|
Posted: Fri Feb 06, 2015 12:30 am |
|
|
When you "call" a subroutine, it pushes the return address on the stack - where is the "call" with your inline assy?
mikey _________________ mikey
-- you can't have too many gadgets or too much disk space !
old engineering saying: 1+1 = 3 for sufficiently large values of 1 or small values of 3 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19538
|
|
Posted: Fri Feb 06, 2015 1:50 am |
|
|
It is more complex than that....
If you have a 'function' like this, and use it from only one location, the compiler will not 'call' it. Instead it'll use a goto. Even from multiple locations, if the function is small, the compiler may 'inline' it (generating different versions each time it is used). So 'assuming' you can 'return' from a function, is always dangerous. It then gets worse, since on processors that have a variable stack (PIC24's etc.), the function may also have data on the stack, not just a return address. Jerson's solution lets the compiler do it's own housekeeping, and is a good way to go. There are ways to force the compiler to 'call' the function, but it's safer really to let it keep track of it's housekeeping itself. |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Fri Feb 06, 2015 2:54 am |
|
|
I've not analysed the assembly code in any detail, but from a quick glance its seems to be essentially some sort of SPI-style bit bashing. it seems likely that the hardware of the PIC might well offer a better, faster and less prone to error solution. If not, then software emulation provided by the compiler might well be available.
So, and seeing that there are redundant tris settings (the compiler manages port direction automatically by default), I have to ask, why the assembler? |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
|
Posted: Fri Feb 06, 2015 6:38 am |
|
|
Thanks guys for your support. On the question of why assembly? My options are very limited as it's a requirement of my school assignment.
Having said that, I'm having problem with MPLABX simulator reading data from PIC16F877A EEPROM. I can write to EEPROM correctly as shown on attached image, but reading always returns value stored at the address 0x00. Why is that?
Code: |
#include <16F877A.h>
#use delay(crystal=20MHZ)
#FUSES HS,NOWDT,NOLVP,NODEBUG,NOPUT,NOBROWNOUT,NOCPD,NOPROTECT //No Watch Dog Timer
#define Porta 5 //clock PIN_A1
#define LdPin 2
#define ClkPin 1
#define CLOCK 3
#define CLEAR 5
#define SER_INP 5
#define BIT7 7
#define buffSize 11
#define STATUS 3 //Status register
#define RP0 5
#define RP1 6
#define INTCON 0x18B //interrupt control register
#define GIE 7 //Global interrupt enable bit
#define EEDATA 0x10C
#define EEADR 0x10D
#define EECON1 0x18C
#define EECON2 0x18D
#define EEPGD 7
#define WREN 2
#define WR 1
#define RD 0
unsigned char rcvReg;
unsigned char arr[buffSize] = {0x37, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x07,0x7E,0x6F};
void main()
{
unsigned char buff[buffSize];
set_tris_a(0b00000001); //porta are all outputs; pin 1 is input
set_tris_b(0b00000000); //all buits of port is set for output
unsigned char i;
for (i = 0; i < buffSize; i++)
{
writeToEEPROM(arr[i], i);
buff[i] = 0x00;
delay_ms(10);
}
for (i = 0x00; i < buffSize; i++)
{
buff[i]= ReadFromEEPROM(i);
}
delay_ms(50);
while(TRUE)
{
//TODO: User Code
}
}
//this function read from EEPROM memory and return value stored at that address
unsigned char ReadFromEEPROM(int8 address)
{
unsigned char temp;
#asm
clrf temp ;clear temp
bsf STATUS, RP1 ;switch to Bank2
bcf STATUS, RP0
movf address, w ;w = address
movwf EEADR ;EADR = address
bsf STATUS, RP0 ;switch to Bank3
bcf EECON1, EEPGD ;point to Data memory
bsf EECON1, RD ;Initiate read from EEPROM
nop
nop
bcf STATUS, RP0 ;switch to Bank2
movf EEDATA, w ;copy data to working register, w
movwf temp ;move to temp ;
#endasm
return temp;
} |
http://postimg.org/image/nk3yvkts3/
|
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
|
Posted: Fri Feb 06, 2015 7:24 pm |
|
|
Guys, I solved it. For those who may be interested, here is the solution.
Code: | unsigned char ReadFromEEPROM(int8 address)
{
unsigned char temp;
#asm
movf address, w ;w = address
bsf STATUS, RP1 ;switch to Bank2
bcf STATUS, RP0
;******move this line up ---> "movf address, w ;w = address"
movwf EEADR ;EADR = address
bsf STATUS, RP0 ;switch to Bank3
bcf EECON1, EEPGD ;point to Data memory
bsf EECON1, RD ;Initiate read from EEPROM
nop
nop
bcf STATUS, RP0 ;switch to Bank2
movf EEDATA, w ;copy data to working register, w
movwf temp ;move to temp ;
#endasm
return temp;
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19538
|
|
Posted: Sat Feb 07, 2015 1:58 am |
|
|
and the reason is that you should have the assembler section marked #ASM ASIS.
As written the compiler will attempt to bank switch for you. So you don't actually need the bank switch instructions. However then because you have changed banks, and it doesn't 'know' the bank has been changed, the wrong bank gets selected. The ASIS statement tells the compiler _you_ are going to control the banks, and to leave them unchanged. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|