We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
I am attempting to do something that seemed simple, but it is turning into a mess. I want to write a stack underflow monitor in main that both checks the stack pointer to insure it is at the bottom, and checks a byte under the stack bottom for a pattern (to better insure that the stack never went under when it was outside of main).
I've come to find that you cannot compile the following statement:
unsigned char * data StackPtr _at_ 0x81; (because it is an sfr address)
It seems like this should be the same thing as: sfr SP = 0x81; ..but with the added benefit of being able to dereference StackPtr to get at what it is pointing at.
The plan was to enter main at power-up, get the address SP is pointing at, stuff a pattern in there like 0xAA, then increment the SP. Later, when it returns to main, the test verfies the SP is still pointing at the SP++ address, and the original SP pattern is still 0xAA.
Assembler seems my only way out.
Just use SP by name. I'm not at a system with C51 installed at the moment, but something like:
unsigned char start_SP = SP; *(unsigned char*)SP = 0xAA; ++SP; /* Do stuff */ if ((SP - 1) != start_SP) { /* SP isn't pointing where it should be. */ } else if (*(unsigned char*)(SP - 1) != 0xAA) { /* SP underflowed or something else happened. */ }
Assembler seems like the obvious way to do this - trying to do it in 'C' is just asking for unnecessary complications, surely?!
I should have mentioned that I need to try and maintain MISRA-C compliance.
unsigned char start_SP = SP; MAIN.C(91): error C247: non-address/-constant initializer
But the typecasting inspired another means.
void main(void) { *(uint8_t data *)SP = 0xAAu; SP++; ... if((SP != &stackbot[0]) || (*(uint8_t data *)(SP-1) != 0xAAu)) { //fail } }
'stackbot' I stuck in the startup file and defined it in the header file as:
extern uint8_t const idata stackbot[];
I needed to put the 'data' keyword in there because the compiler must have thought it was generic pointer, and stuffed all sorts of useless instructions in there.
It compiles and runs like it should. The thing I can't figure out is what is causing the compiler to put in extra instructions...
267: if((SP != &stackbot[0]) || (*(uint8_t data *)(SP-1) != 0xAAu)) C:0x0264 AF81 MOV R7,SP(0x81) C:0x0266 7E00 MOV R6,#xFPage(0x00) C:0x0268 747E MOV A,#STACKBOT(0x7E) C:0x026A B5070D CJNE A,0x07,C:027A C:0x026D EE MOV A,R6 C:0x026E B40009 CJNE A,#xFPage(0x00),C:027A C:0x0271 E581 MOV A,SP(0x81) C:0x0273 14 DEC A C:0x0274 F8 MOV R0,A C:0x0275 E6 MOV A,@R0 C:0x0276 64AA XRL A,#EMI0CN(0xAA) C:0x0278 6007 JZ C:0281
I'm using the compact memory model
Where's your sense of adventure! :)
There's "adventure" - and then there's just plain making life difficult!
if((SP != (uint8_t)&stackbot[0]) || (*(uint8_t data *)(SP-1) != 0xAAu))
That fixed it! Thanks for the help.
I know what you mean. The guys here in the office all said to write it in assembler. So this was a challenge more than anything.
I also enjoy bending the compiler to my will. You learn a lot about how it works.
..now if I could only get lint to not have a cow. I need to tell it that the sfr is not a 3-byte pointer. *eye roll*
I screwed this up badly. I should have incremented SP, then put in the 0xAA pattern. Then the SP-1 down below should have simply been 'SP'.
The stack pointer always increments before something gets pushed onto the stack.
(I inadvertently stuffed 0xAA into a variable, instead of onto the actual stack bottom) Someday someone will also want to do this, and it would be easier if it were posted correctly.