But why do you need to do add one and its done twice?
You need to add 2 to move the SP to the location where your earlier pushed variables are located, because when RCALL/CALL is used a 2-byte return address is loaded onto the stack and will prevent you from directly accessing those variables (using POP).
Maybe I should add that the SP is loaded at the highest SRAM address on init of a program and anything saved to the stack will move SP to a LOWER SRAM address.
Might not be obvious to a newbie that a growing stack chew its way DOWN through SRAM.
clawson and LENART, and others THANK YOU SO MUCH, with some more research I think I get the understanding of LDD and stack operations.
.include "m8515def.inc" ; include the ATMEGA8515(L) definitions file
.def NUM = r16 ; use this register to store ‘num’
.def RESULT = r17 ; use this register to store ‘result’
.def temp = r19
RESET:
LDI r18, high (RAMEND)
OUT SPH, r18
LDI r18, low (RAMEND)
OUT SPL, r18
LDI NUM, $6
PUSH NUM
LDI RESULT,$1
PUSH RESULT
RCALL HANOI ; returns with RESULT
POP RESULT
POP NUM
OUT PORTA, RESULT
OUT PORTB, NUM
LOOP:
RJMP LOOP
HANOI:
in r28, SPL
in r29, SPH
adiw r29:r28,2
LDD NUM, Y+0
LDD RESULT, Y+1
LDI TEMP, $1
FOR_LOOP:
ADD RESULT, RESULT ; "ret *= 2"
INC temp ; i++
CP temp, NUM ; i <= n ?
BRCC FOR_LOOP
SUBI RESULT, 1 ; "ret--"
std Y+0, NUM
std Y+1, RESULT
RET
Here is my other attempt.
I'm pretty sure I implemented the Stack right, but when debugging at the
LDD NUM, Y+0
the Num register becomes 9 instead of 6, why is that.
Also the result at the end comes out as 6, so im pretty sure my loop is wrong.
Can someone give me a tip on the Loop function please.
the Num register becomes 9 instead of 6, why is that.
Cliff screwed up! I should have tested the code rather than just writing it theoretically. The Y adjustment needs to be:
adiw r29:r28,3
That's because SP is pointing at the byte BELOW the two bytes of return address after entry to the function, not at the bottom byte of the return address so the adjustment is 3 not 2.
Quote:
Also is the main difference with LD, ST and LDD and SDD is that with LD, ST you use increments/decrements and with LDD,STD you use displacement?
You do have the PDF that documents the opcodes don't you? Read about LD/LDD there but, yes,. for Y and Z the extra D means "displacement". In fact "LD R24,Y" is really just an encoding of "LDD R24, Y+0" when you look at the bit pattern of the opcode.
Quote:
And what is the main difference in simple Terms between the X, Y and Z pointers.
Nothing. They give you 3 so you can pick whichever you like. Often if you are doing a memory to memory copy you will have one of X,Y,Z pointing at the source and another pointing at the destination then the core of the copy loop becomes:
LD reg, X+
ST Y+, reg
(or whatever from X,Y,Z you chose). X differs slightly from Y and Z in that it does not offer LDD/STD with the +n displacement.
Also is the main difference with LD, ST and LDD and SDD is that with LD, ST you use increments/decrements and with LDD,STD you use displacement?
And what is the main difference in simple Terms between the X, Y and Z pointers.
LD/ST will load/store values in SRAM at a location pointed to by Index registers. LDD/STD is very similar to LD/ST but with an added number of displacement from Index registers.
As Index register you can use X, Y or Z.
X/Y/Z can be post-incremented or pre-decremented which makes them easy to use for loading/storing several bytes in a row. X/Y/Z can be used without +/- to load/store single bytes.
Only difference btw X/Y/Z is that only Z can be used to load data from FLASH(program memory).
Program memory is often used to store data that will not change such as tables or text strings.
To load data from FLASH you need to use LPM instruction.
Z can be post-incremented but not pre-decremented.
To understand what makes you get wrong numbers in your code you should use the simulator included in AVRStudio.
Use single stepping and look at how the registers and stack(at the end of your SRAM) change.
To understand what makes you get wrong numbers in your code you should use the simulator included in AVRStudio.
Use single stepping and look at how the registers and stack(at the end of your SRAM) change.
I think that's how he saw the 9 where there should have been a 6. It's also where I saw the same thing and realised that SP points BELOW the return address not at the base of it after CALL.
What very confusing pictures - a stack pointer that goes "up" as items are added to the stack? On the other hand I suppose it's natural to show ascending addresses going down the page?
I guess that because a "stack" is so called because of the spring loaded stack of plates metaphor what should really be shown is an SP that never moves and a stack of data that itself moves up and down.
Yeah - I didn't say they were good pictures :)
..I just wish forums had a neat way of allowing people to draw pictures to explain things..I'm sure this thread would be only half its length if all these attempts at describing stack operations via words were replaced with some (agreed and useful) pictures.
Cliff, a stack pointer growing upwards (that is: lower addresses are above higher addresses) is something that is quite common when visualized in a picture. Maybe it is originating from many common designs where the stack simply grows from the opposite end of the available address space than the user program/data does. To maximize efficiency using the space in between those 2 areas, they grow towards each other from opposite directions. That's why the stack grows upwards but towards lower addresses.
And who said that the stack pointer moved in the referred picture? It's all a matter of relativity - you can as well state that the observer moved together with the data, but the stack pointer remained at a fixed position :-)
Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."
I guess it's the same old argument as to which way up your draw a memory space diagram?
Do you have 0x0000 at the top of the page or the bottom?
For me it only "feels right" with 0x000 at the bottom of the page yet memory layout diagrams in Atmel datasheets are drawn the other way up ((non BOOTRST)reset/vectors at the top, bootloader at the bottom). An odd form of OCD makes my flesh creep when I see this! :?
But imagine a 4GB address space fully occupied and you want to see what's at the beginning??? Would you happily scroll down thru dozens of pages just to see user code that resides in lower space traditionally?
Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."
But imagine a 4GB address space fully occupied and you want to see what's at the beginning???
OK in your scheme what if you want to check what's at the end? ;-)
(quite a few chips have vectors at the very end of memory)
I'd say: tough luck then! But for a matter of data dynamics it surely is more effective to have that space readily at hand where the suspected user code & data structures reside, not the constant vector table at the very end of a design's address space layout?
But i will happily admit that this little rathole is best discussed in the next pub with a pint of beer at hand.
Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."
.include "m8515def.inc" ; include the ATMEGA8515(L) definitions file
.def NUM = r16 ; use this register to store ‘num’
.def RESULT = r17 ; use this register to store ‘result’
.def temp = r19
RESET:
LDI r18, high (RAMEND)
OUT SPH, r18
LDI r18, low (RAMEND)
OUT SPL, r18
LDI NUM, $6
PUSH NUM
LDI RESULT,$1
PUSH RESULT
RCALL HANOI ; returns with RESULT
POP RESULT
POP NUM
OUT PORTA, RESULT
OUT PORTB, NUM
LOOP:
RJMP LOOP
HANOI:
in r28, SPL
in r29, SPH
adiw r29:r28,3
LDD RESULT, Y+0
LDD NUM, Y+1
LDI TEMP, $1
FOR_LOOP:
ADD RESULT, RESULT ; "ret *= 2"
INC temp ; i++
CP temp, NUM ; i <= n ?
BRCC FOR_LOOP
SUBI RESULT, 1 ; "ret--"
std Y+1, NUM
std Y+0, RESULT
RET
I also got the Ldd wrong, as Result is +0, and Num is +1, as thats the order I pushed them, so thats fixed now.
So my variables now are equal what they should, One thing left is the for Loop, its still not doing what it should for some silly reason
So my variables now are equal what they should, One thing left is the for Loop, I am just using one I was suggested earlier in this thread, but it doesn't work, as Result just comes of as one.
This is where you learn to be a software engineer. As the ADIW,3 thing shows software is not always right when you first implement what you thought was a great idea so the next step is to debug it and find out what's going on.
As you've already found, for code like this that doesn't really care much about hardware inputs and outputs (apart from the very end - you never set DDR by the way!), then the simulator is perfect for stepping through it and finding out what does/doesn't work.
So I load your code into AVR Studio 4, build and run it in the simulator and watch what happens in "HANOI:".
After the first ADD RESULT,RESULT I see 2 in RESULT. "temp" then increments to 2. The CPU flags show C(arry) to be set after the CP temp,NUM because temp=2 and NUM=1 so it does not loop back and ends (including the SUBI) which seems to tell me that Cliff screwed up again and got the CP the wrong way round!
So I corrected it to be:
CP NUM, temp ; i <= n ?
After doing this I get the result 0x3F (63) which I believe is correct?
The lovely new working code.
Thank You Cliff, Much appreciated mate.
.include "m8515def.inc" ; include the ATMEGA8515(L) definitions file
.def NUM = r16 ; use this register to store ‘num’
.def RESULT = r17 ; use this register to store ‘result’
.def TEMP = r19
RESET:
LDI r18, high (RAMEND)
OUT SPH, r18
LDI r18, low (RAMEND)
OUT SPL, r18
LDI NUM, $6
PUSH NUM
LDI RESULT,$1
PUSH RESULT
RCALL HANOI ; returns with RESULT
POP RESULT
POP NUM
OUT PORTA, RESULT
OUT PORTB, NUM
LOOP:
RJMP LOOP
HANOI:
in r28, SPL
in r29, SPH
adiw r29:r28,3
LDD RESULT, Y+0
LDD NUM, Y+1
LDI TEMP, $1
FOR_LOOP:
LSL RESULT ; "ret *= 2"
INC temp ; i++
CP NUM, TEMP ; i <= n ?
BRCC FOR_LOOP
SUBI RESULT, 1 ; "ret--"
std Y+1, NUM
std Y+0, RESULT
RET
Btw I replaced ADD RESULT, RESULT with just LSL command, I forgot about it earlier.
Thank you everyone for explaining it to me, I know it would get frustrating teaching a newbie something something like this =)[/code]
Btw I replaced ADD RESULT, RESULT with just LSL command, I forgot about it earlier.
Guess what (taken from the opcode manual):
LSL Rd: 0000 11dd dddd dddd
ADD Rd,Rr: 0000 11rd dddd rrrr
So when r=d that becomes:
ADD Rd,Rd: 0000 11dd dddd dddd
Time of a game of "spot the difference" between the LSL and the latter form of ADD! ;-)
This is why in the description of LSL the opcode manual says "16-bit Opcode: (see ADD Rd,Rd)".
There are far more opcodes listed in that manual than there are really opcode bit patterns. For another example look up BSET then look at each of SEC, SEH, SEI, SEN, SES, SET, SEV, SEZ in turn. And you probably thought that was really 9 different opcodes ?
And here is a list that shows the ones that are the same - any line with " = " in it is just another name for the bit pattern already listed:
To get bonus points, tidy up the code and add your own comments that describe what happens.
I still don't see the point with LDD/STD, you could have used LD/ST for this if you post-incremented Y and loaded/stored NUM and RESULT in reverse order.
But you'd better do what your teacher told you to make her happy.
In fact the funny thing is that when HANOI: returns and the code does the POP's into RESULT and NUM those registers already hold those values - which rather proves the point about the idiocy of being forced to use the stack!
Perhaps "inside" HANOI: you should use different registers simply to show that the return value *is* then being passed back correctly on the stack (and not the fact that the registers already hold the output anyway when you get back)?
To get bonus points, tidy up the code and add your own comments that describe what happens.
I still don't see the point with LDD/STD, you could have used LD/ST for this if you post-incremented Y and loaded/stored NUM and RESULT in reverse order.
But you'd better do what your teacher told you to make her happy.
Yep I definitely will, and yes she asks for LDD so ill leave it at that, but thanks to this forum I know a lot more then she ever told us about this.
Now just to do the second part.
typedef unsigned char byte; // define 8-bit variable type
int main(void) {
byte result, num = 6;
for(;;) {
// Calculate the result of the Tower of Hanoi
result = hanoi(num);
}
}
byte hanoi(byte n){
if(n <= 1)
return n;
else
return ( 2*hanoi(n-1) + 1 );
}
Using the previous code for the stack parts, first step i did was see what the dissasembler shows me.
Perhaps "inside" HANOI: you should use different registers simply to show that the return value *is* then being passed back correctly on the stack (and not the fact that the registers already hold the output anyway when you get back)?
IIRC part of the assignment was to use as few registers as possible.
Was looking for a way to get rid of TEMP but that would need more PUSH and POP and might just be confusing.
I probably should of started from that instead of running to dissasembler.
As for part one, I have to explain in person to the Tutors that mark us what i did, so regardless I will be showing them step by step what my code does, so they know I understand it, its not just a submit electronically and walk away type of task
Is this all I have to do, from what you experts can see that the c-code specifies?
.include "m8515def.inc" ; include the ATMEGA8515(L) definitions file
.def NUM = r16 ; use this register to store ‘num’
.def RESULT = r17 ; use this register to store ‘result’
.def TEMP = r19
RESET:
LDI r18, high (RAMEND)
OUT SPH, r18
LDI r18, low (RAMEND)
OUT SPL, r18
LDI NUM, $6
PUSH NUM
LDI RESULT,$1
PUSH RESULT
RCALL HANOI ; returns with RESULT
POP RESULT
POP NUM
OUT PORTA, RESULT
OUT PORTB, NUM
LOOP:
RJMP LOOP
HANOI:
in r28, SPL
in r29, SPH
adiw r29:r28,3
LDD RESULT, Y+0
LDD NUM, Y+1
LDI TEMP, $2 ; two because ++ after 2x see below
IF_ST:
;;ELSE
LSL RESULT ; 2*(n-(n-1)
INC RESULT ; ++
INC TEMP ; counter ++
CP NUM, TEMP ;if(n <= TEMP)
BRCC IF_ST
std Y+1, NUM
std Y+0, RESULT
RET
I guess it's the same old argument as to which way up your draw a memory space diagram?
Do you have 0x0000 at the top of the page or the bottom?
I do mine left to right ;) But to me, you start 0 where you start writing on a page, which for all cultures that I can think of is the top of the page (though left or right might change).
Quote:
[In fact the funny thing is that when HANOI: returns and the code does the POP's into RESULT and NUM those registers already hold those values - which rather proves the point about the idiocy of being forced to use the stack!
And I don't see the point of pushing both NUM and RESULT anyways. Since both are the same size, and NUM only goes in and RESULT only goes out, I would simply push NUM, replace the value on the stack with the result, then pop RESULT. No need waste space on the stack for a number that won't change.
I would also not bother comparing NUM against temp. I would grab NUM off the stack and decrement it until it reaches 0 to end the loop.
'Tain't obvious that push and pop are required at all.
At most three registers are required, two with the gnu API.
One or two clobberable registers are all that is necessary to avoid pushes and pops.
The gnu API has more than two.
'Tain't obvious that push and pop are required at all.
No, I think most of us already agreed that we would handle this as you suggest.
Unfortunately the assignment forces him to use stack for passing parameters and as few registers a s possible.
Hopefully this exercise have some value for future understanding of how the stack operations work.
'Tain't obvious that push and pop are required at all.
No, I think most of us already agreed that we would handle this as you suggest.
Unfortunately the assignment forces him to use stack for passing parameters and as few registers a s possible.
Hopefully this exercise have some value for future understanding of how the stack operations work.
Sorry about that.
When I wrote it, I'd forgotten that I was only on the first of five pages.
Had it not drawn a response already, I'd have deleted it.
Is this all I have to do, from what you experts can see that the c-code specifies?
1. You could certainly improve on it if you are anally-retentive.
2. Otherwise you could just add the explanatory comments that are so loathed by students.
.include "m8515def.inc" ; include the ATMEGA8515(L) definitions file
.def NUM = r16 ; use this register to store ‘num’
.def RESULT = r17 ; use this register to store ‘result’
.def TEMP = r19
RESET:
LDI r18, high (RAMEND)
OUT SPH, r18
LDI r18, low (RAMEND)
OUT SPL, r18
LDI NUM, $6
PUSH NUM
LDI RESULT,$1
PUSH RESULT
RCALL HANOI ; returns with RESULT
POP RESULT
POP NUM
OUT PORTA, RESULT ;Displays result in Port A
OUT PORTB, NUM ; Displays original NUM in Port B
LOOP:
RJMP LOOP
HANOI:
in r28, SPL
in r29, SPH
adiw r29:r28,3
LDD RESULT, Y+0
LDD NUM, Y+1
LDI TEMP, $1
CP TEMP, NUM ; i <= n ?
BRCC RETURN
SUBI NUM, 1
std Y+1, NUM
LSL RESULT ; "ret *= 2"
INC RESULT ; i++
std Y+0, RESULT
POP TEMP
POP TEMP
RCALL HANOI
;std Y+1, NUM
;std Y+0, RESULT
RETURN:
RET
This is my beta code, RET doesnt work as it should, as I assume i lost the return a address somewhere in the stack or something like that.
You can't just throw away the return address and expect to get back to where you started. When you call your routine recursively, you need to call it the same way you called it the first time.
Yes I realised thats what I did, but would you overcome the the fact that Ret is only executed at the End of the Recursive function, as I was specifically told to Rcall Hanoi Within Hanoi itself.
the the fact that Ret is only executed at the End of the Recursive function
No, you misunderstand recursion - it's executed (probably 6 times) each time the nested call to the function unwinds.
To be honest I'd be a bit leery myself about handling stack frame parameters and recursion at the same time!
I guess your journey starts with paper and pencil. Sketch out on a page what you expect to be happening on the stack both as you go 6 levels down the chain doing the repeated CALLs and then as you post results and unwind back up the stack.
(I still cannot picture this in my mind just yet!)
I guess your journey starts with paper and pencil. Sketch out on a page what you expect to be happening on the stack both as you go 6 levels down the chain doing the repeated CALLs and then as you post results and unwind back up the stack.
I can only second Cliff's advice: use the paper/pencil method to see what will happen on the stack, and perform all stack manipulations manually with the pen. To simplify this task, forget about all parameter passing via the stack for now and get absolutely familiar how nested calls and RETs and return addresses being pushed onto the stack really work. Once you're familiar with the mechanics of nested calls, and you recognized that every called routine must not modify the stack contents at the beginning of its own invocation, then you may proceed with actually doing parameter passing via the stack in addition.
As for a mental picture: that old-fashioned spring-loaded stack of plates in my view is still the best analogy. What it does *not* provide is that every single plate is the same form of stack object, whereas in your CPU stack you will find return addresses *and* data items on the stack, which complicates the picture a bit.
Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."
No no, i was about to explain the different meaning successive plates could have for the waiters behind you in the queue. So, in the cantina, each plate stands for exactly the same purpose, whereas in calling a subroutine, the callee could find other items on the stack besides the address to finally return to. So, you need a mental concept of an agreement of how exactly the stack does look like when you enter a subroutine. That's what a calling standard will provide. Basically caller and callee simply agree upon the stack contents when the callee enters execution: is there only a return address on top of the stack, or are there additional data items that have been pushed onto the stack just below (beware: this would imply: at higher stack addresses!) the return address? And who is responsible for the removal of those data items (mostly the calling program, but who knows?)?
Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."
I have a bit of a mad idea for OP (perhaps not that mad, several C compilers do it):
Two stacks.
Use the hardware stack simply for CALL/RET then implement a second data stack for the variables. This helps to avoid the mish-mash of interleaved items on the single stack.
To simplify things, why not create your own data stack? Avoids having to make stack frames and indexing around the return address. Then just write functions or macros to push/ pop values.
Well i do understand that Rcall each time puts in the address of the location into the stack and moves the pointer under the 2 bytes of the address, Ret does the opposite it gets the adress out of the stack, pops it out and voila. And yes i know poping twice as i did in my code just discards that address thus, i can not get back to the top, and the ret I used just goes back to ret, as thats the next thing after the last Rcall Hanoi.
I want to try to preserve that first address, and discard all other 2bytes that the Rcall Hanoi within the function then creates. Thats one approach im thinking of, will research further if its possible and how.
Would placing that address into 2 registers, and then storing them back onto the stack right before I do the final RET work, so STD - ing them back? Would I just then need to not do this
in r28, SPL
in r29, SPH
adiw r29:r28,3
and instead
LDD RESULT, Y+3
LDD NUM, Y+4
Is my understanding right that the address of the rcall is stored in Y+0, Y+1?
edit
Just got onto my pc with AVRSTUDIO 4.
Y+3, and Y+4 is not the right values, My understanding is fogged up again. Need to re-read the manual
- Log in or register to post comments
TopDoesnt Y=SP? And its adiw Y. Thats where i got it from
- Log in or register to post comments
Top- Log in or register to post comments
TopMaybe I should add that the SP is loaded at the highest SRAM address on init of a program and anything saved to the stack will move SP to a LOWER SRAM address.
Might not be obvious to a newbie that a growing stack chew its way DOWN through SRAM.
- Log in or register to post comments
Topclawson and LENART, and others THANK YOU SO MUCH, with some more research I think I get the understanding of LDD and stack operations.
Here is my other attempt.
I'm pretty sure I implemented the Stack right, but when debugging at the
the Num register becomes 9 instead of 6, why is that.
Also the result at the end comes out as 6, so im pretty sure my loop is wrong.
Can someone give me a tip on the Loop function please.
Regards
- Log in or register to post comments
TopAlso is the main difference with LD, ST and LDD and SDD is that with LD, ST you use increments/decrements and with LDD,STD you use displacement?
And what is the main difference in simple Terms between the X, Y and Z pointers.
P.s this isnt for my assignment but just interested to know.
- Log in or register to post comments
TopCliff screwed up! I should have tested the code rather than just writing it theoretically. The Y adjustment needs to be:
That's because SP is pointing at the byte BELOW the two bytes of return address after entry to the function, not at the bottom byte of the return address so the adjustment is 3 not 2.
You do have the PDF that documents the opcodes don't you? Read about LD/LDD there but, yes,. for Y and Z the extra D means "displacement". In fact "LD R24,Y" is really just an encoding of "LDD R24, Y+0" when you look at the bit pattern of the opcode.
Nothing. They give you 3 so you can pick whichever you like. Often if you are doing a memory to memory copy you will have one of X,Y,Z pointing at the source and another pointing at the destination then the core of the copy loop becomes:
(or whatever from X,Y,Z you chose). X differs slightly from Y and Z in that it does not offer LDD/STD with the +n displacement.
- Log in or register to post comments
TopAs Index register you can use X, Y or Z.
X/Y/Z can be post-incremented or pre-decremented which makes them easy to use for loading/storing several bytes in a row. X/Y/Z can be used without +/- to load/store single bytes.
Another option is LDS/STS which will load/store bytes at a location earlier defined. Useful since you don't need to set up any Index pointers.
Only difference btw X/Y/Z is that only Z can be used to load data from FLASH(program memory).
Program memory is often used to store data that will not change such as tables or text strings.
To load data from FLASH you need to use LPM instruction.
Z can be post-incremented but not pre-decremented.
To understand what makes you get wrong numbers in your code you should use the simulator included in AVRStudio.
Use single stepping and look at how the registers and stack(at the end of your SRAM) change.
- Log in or register to post comments
TopI think that's how he saw the 9 where there should have been a 6. It's also where I saw the same thing and realised that SP points BELOW the return address not at the base of it after CALL.
- Log in or register to post comments
TopSuppose you never get too old to learn.
- Log in or register to post comments
TopWhat this thread needs is pictures..
Second entry googling for "AVR stack pointer"
http://www.avr-tutorials.com/gen...
- Log in or register to post comments
TopWhat very confusing pictures - a stack pointer that goes "up" as items are added to the stack? On the other hand I suppose it's natural to show ascending addresses going down the page?
I guess that because a "stack" is so called because of the spring loaded stack of plates metaphor what should really be shown is an SP that never moves and a stack of data that itself moves up and down.
How confusing.
- Log in or register to post comments
TopYeah - I didn't say they were good pictures :)
..I just wish forums had a neat way of allowing people to draw pictures to explain things..I'm sure this thread would be only half its length if all these attempts at describing stack operations via words were replaced with some (agreed and useful) pictures.
- Log in or register to post comments
TopCliff, a stack pointer growing upwards (that is: lower addresses are above higher addresses) is something that is quite common when visualized in a picture. Maybe it is originating from many common designs where the stack simply grows from the opposite end of the available address space than the user program/data does. To maximize efficiency using the space in between those 2 areas, they grow towards each other from opposite directions. That's why the stack grows upwards but towards lower addresses.
And who said that the stack pointer moved in the referred picture? It's all a matter of relativity - you can as well state that the observer moved together with the data, but the stack pointer remained at a fixed position :-)
Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."
- Log in or register to post comments
TopI guess it's the same old argument as to which way up your draw a memory space diagram?
Do you have 0x0000 at the top of the page or the bottom?
For me it only "feels right" with 0x000 at the bottom of the page yet memory layout diagrams in Atmel datasheets are drawn the other way up ((non BOOTRST)reset/vectors at the top, bootloader at the bottom). An odd form of OCD makes my flesh creep when I see this! :?
- Log in or register to post comments
TopBut imagine a 4GB address space fully occupied and you want to see what's at the beginning??? Would you happily scroll down thru dozens of pages just to see user code that resides in lower space traditionally?
Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."
- Log in or register to post comments
TopOK in your scheme what if you want to check what's at the end? ;-)
(quite a few chips have vectors at the very end of memory)
- Log in or register to post comments
TopI'd say: tough luck then! But for a matter of data dynamics it surely is more effective to have that space readily at hand where the suspected user code & data structures reside, not the constant vector table at the very end of a design's address space layout?
But i will happily admit that this little rathole is best discussed in the next pub with a pint of beer at hand.
Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."
- Log in or register to post comments
TopI also got the Ldd wrong, as Result is +0, and Num is +1, as thats the order I pushed them, so thats fixed now.
So my variables now are equal what they should, One thing left is the for Loop, its still not doing what it should for some silly reason
- Log in or register to post comments
TopThis is where you learn to be a software engineer. As the ADIW,3 thing shows software is not always right when you first implement what you thought was a great idea so the next step is to debug it and find out what's going on.
As you've already found, for code like this that doesn't really care much about hardware inputs and outputs (apart from the very end - you never set DDR by the way!), then the simulator is perfect for stepping through it and finding out what does/doesn't work.
So I load your code into AVR Studio 4, build and run it in the simulator and watch what happens in "HANOI:".
After the first ADD RESULT,RESULT I see 2 in RESULT. "temp" then increments to 2. The CPU flags show C(arry) to be set after the CP temp,NUM because temp=2 and NUM=1 so it does not loop back and ends (including the SUBI) which seems to tell me that Cliff screwed up again and got the CP the wrong way round!
So I corrected it to be:
After doing this I get the result 0x3F (63) which I believe is correct?
- Log in or register to post comments
TopJust in case the Op doesn't yet know:
After the excution of this instruction sequence,
the Z flag has been set.
Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."
- Log in or register to post comments
TopThat joke was close to being too subtle for me! ;-)
Of course I'm a C programmer so:
And that subtle reference may be lost on anyone who is not from UK and at least 40+ years old ;-) (see: http://en.wikipedia.org/wiki/Lob... see also: http://www.worldofspectrum.org/c... )
- Log in or register to post comments
TopThe lovely new working code.
Thank You Cliff, Much appreciated mate.
Btw I replaced ADD RESULT, RESULT with just LSL command, I forgot about it earlier.
Thank you everyone for explaining it to me, I know it would get frustrating teaching a newbie something something like this =)[/code]
- Log in or register to post comments
TopGuess what (taken from the opcode manual):
So when r=d that becomes:
Time of a game of "spot the difference" between the LSL and the latter form of ADD! ;-)
This is why in the description of LSL the opcode manual says "16-bit Opcode: (see ADD Rd,Rd)".
There are far more opcodes listed in that manual than there are really opcode bit patterns. For another example look up BSET then look at each of SEC, SEH, SEI, SEN, SES, SET, SEV, SEZ in turn. And you probably thought that was really 9 different opcodes ?
And here is a list that shows the ones that are the same - any line with " = " in it is just another name for the bit pattern already listed:
- Log in or register to post comments
TopThat applies to most MCUs. There are several pseudonyms for the same instruction.
It keeps most people happy, most of the time.
It is no trouble for Assemblers. It is confusing for Disassemblers.
David.
- Log in or register to post comments
TopAnd simulators ;-) (hence my list above).
Atmel do seem to make a thing of:
I've always thought it a bit disingenuous of them to make this "130" claim in each datasheet when there aren't 130 different ones ;-)
Even with blank lines inserted my list above is exactly 130 lines long (Notepad++ says).
EDIT: a quick look at mega640/1280/2560 datasheet bumps the number to 135 (above was mega8)
- Log in or register to post comments
Topsinedo,
To get bonus points, tidy up the code and add your own comments that describe what happens.
I still don't see the point with LDD/STD, you could have used LD/ST for this if you post-incremented Y and loaded/stored NUM and RESULT in reverse order.
But you'd better do what your teacher told you to make her happy.
- Log in or register to post comments
TopIn fact the funny thing is that when HANOI: returns and the code does the POP's into RESULT and NUM those registers already hold those values - which rather proves the point about the idiocy of being forced to use the stack!
Perhaps "inside" HANOI: you should use different registers simply to show that the return value *is* then being passed back correctly on the stack (and not the fact that the registers already hold the output anyway when you get back)?
- Log in or register to post comments
TopYep I definitely will, and yes she asks for LDD so ill leave it at that, but thanks to this forum I know a lot more then she ever told us about this.
Now just to do the second part.
Using the previous code for the stack parts, first step i did was see what the dissasembler shows me.
The result does end up 63, I just need to figure out how to put this inside my code.
I think a much better way will be to do a flow chart of what's happening and then step by step input it into assembly.
Thank you everyone for your help once again, ill update on how I go soon, but now its bed time in Sydney =)
- Log in or register to post comments
TopWas looking for a way to get rid of TEMP but that would need more PUSH and POP and might just be confusing.
- Log in or register to post comments
TopBy the way quickly looking at the c code (PART 2), i can see that it begins with n = 1, so quick calculation, i get
2*1 +1 =3
2*3 +1 = 7
2*7 +1 = 15
2*15 +1 = 31
2*31 + 1 =63
I probably should of started from that instead of running to dissasembler.
As for part one, I have to explain in person to the Tutors that mark us what i did, so regardless I will be showing them step by step what my code does, so they know I understand it, its not just a submit electronically and walk away type of task
- Log in or register to post comments
TopBecause im still not asleep, I tried part 2
Is this all I have to do, from what you experts can see that the c-code specifies?
- Log in or register to post comments
TopI would also not bother comparing NUM against temp. I would grab NUM off the stack and decrement it until it reaches 0 to end the loop.
Regards,
Steve A.
The Board helps those that help themselves.
- Log in or register to post comments
Top'Tain't obvious that push and pop are required at all.
At most three registers are required, two with the gnu API.
One or two clobberable registers are all that is necessary to avoid pushes and pops.
The gnu API has more than two.
Iluvatar is the better part of Valar.
- Log in or register to post comments
TopUnfortunately the assignment forces him to use stack for passing parameters and as few registers a s possible.
Hopefully this exercise have some value for future understanding of how the stack operations work.
- Log in or register to post comments
TopWhen I wrote it, I'd forgotten that I was only on the first of five pages.
Had it not drawn a response already, I'd have deleted it.
Iluvatar is the better part of Valar.
- Log in or register to post comments
Top1. You could certainly improve on it if you are anally-retentive.
2. Otherwise you could just add the explanatory comments that are so loathed by students.
I know which I prefer. You know your teacher.
David.
- Log in or register to post comments
TopI talked to my lecturer today, shes fine with Part A, but i realised what is wrong with part B, im still using iteration.
I actually need to Rcall Hanoi within Hanoi.. Duhh =)
- Log in or register to post comments
TopThis is my beta code, RET doesnt work as it should, as I assume i lost the return a address somewhere in the stack or something like that.
Can someone comment on the code please
- Log in or register to post comments
TopYou lost the return address here:
You can't just throw away the return address and expect to get back to where you started. When you call your routine recursively, you need to call it the same way you called it the first time.
Regards,
Steve A.
The Board helps those that help themselves.
- Log in or register to post comments
TopYes I realised thats what I did, but would you overcome the the fact that Ret is only executed at the End of the Recursive function, as I was specifically told to Rcall Hanoi Within Hanoi itself.
- Log in or register to post comments
TopNo, you misunderstand recursion - it's executed (probably 6 times) each time the nested call to the function unwinds.
To be honest I'd be a bit leery myself about handling stack frame parameters and recursion at the same time!
I guess your journey starts with paper and pencil. Sketch out on a page what you expect to be happening on the stack both as you go 6 levels down the chain doing the repeated CALLs and then as you post results and unwind back up the stack.
(I still cannot picture this in my mind just yet!)
- Log in or register to post comments
TopI can only second Cliff's advice: use the paper/pencil method to see what will happen on the stack, and perform all stack manipulations manually with the pen. To simplify this task, forget about all parameter passing via the stack for now and get absolutely familiar how nested calls and RETs and return addresses being pushed onto the stack really work. Once you're familiar with the mechanics of nested calls, and you recognized that every called routine must not modify the stack contents at the beginning of its own invocation, then you may proceed with actually doing parameter passing via the stack in addition.
As for a mental picture: that old-fashioned spring-loaded stack of plates in my view is still the best analogy. What it does *not* provide is that every single plate is the same form of stack object, whereas in your CPU stack you will find return addresses *and* data items on the stack, which complicates the picture a bit.
Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."
- Log in or register to post comments
Topreturn address = 2 plates, simple ;-)
- Log in or register to post comments
TopNo no, i was about to explain the different meaning successive plates could have for the waiters behind you in the queue. So, in the cantina, each plate stands for exactly the same purpose, whereas in calling a subroutine, the callee could find other items on the stack besides the address to finally return to. So, you need a mental concept of an agreement of how exactly the stack does look like when you enter a subroutine. That's what a calling standard will provide. Basically caller and callee simply agree upon the stack contents when the callee enters execution: is there only a return address on top of the stack, or are there additional data items that have been pushed onto the stack just below (beware: this would imply: at higher stack addresses!) the return address? And who is responsible for the removal of those data items (mostly the calling program, but who knows?)?
Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."
- Log in or register to post comments
TopI have a bit of a mad idea for OP (perhaps not that mad, several C compilers do it):
Two stacks.
Use the hardware stack simply for CALL/RET then implement a second data stack for the variables. This helps to avoid the mish-mash of interleaved items on the single stack.
- Log in or register to post comments
TopTo simplify things, why not create your own data stack? Avoids having to make stack frames and indexing around the return address. Then just write functions or macros to push/ pop values.
- Log in or register to post comments
TopGreat minds think alike! Cliff just beat me to the punch.
- Log in or register to post comments
TopDo we say "SNAP!" now? ;-)
- Log in or register to post comments
TopWell i do understand that Rcall each time puts in the address of the location into the stack and moves the pointer under the 2 bytes of the address, Ret does the opposite it gets the adress out of the stack, pops it out and voila. And yes i know poping twice as i did in my code just discards that address thus, i can not get back to the top, and the ret I used just goes back to ret, as thats the next thing after the last Rcall Hanoi.
I want to try to preserve that first address, and discard all other 2bytes that the Rcall Hanoi within the function then creates. Thats one approach im thinking of, will research further if its possible and how.
Would placing that address into 2 registers, and then storing them back onto the stack right before I do the final RET work, so STD - ing them back? Would I just then need to not do this
and instead
Is my understanding right that the address of the rcall is stored in Y+0, Y+1?
edit
Just got onto my pc with AVRSTUDIO 4.
Y+3, and Y+4 is not the right values, My understanding is fogged up again. Need to re-read the manual
- Log in or register to post comments
TopPages