Please help with Stack Operations

Go To Last Post
116 posts / 0 new

Pages

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
ADIW has nothing to do with the SP. Where in the documentation of ADIW do you see that?
Cliff provided an example where SP is loaded into Y (see above post).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Doesnt Y=SP? And its adiw Y. Thats where i got it from

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
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).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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.

Regards

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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.

P.s this isnt for my assignment but just interested to know.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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?

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.
sys_init:
	ldi	zh,high(in_1)	;msb SRAM
	ldi	zl,low(in_1)	;lsb SRAM
	ldi	xh,0x00		;msb EEPROM
	ldi	xl,low(in_1)+16	;lsb EEPROM
sys_prom:
	rcall	prom_read		;read EEPROM
	st	z+,data		;save data to SRAM
	adiw	x,1			;inc EEPROM adress
	cpi	zl,low(sys_end)	;until beyond SYSTEM
	brne	sys_prom		;read EEPROM

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.

foo:			.byte 1	;define foo
lds   r16,foo
inc   r16
sts   foo,r16

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.

send_cmd:
	lpm	data,z+		;get CMD CHAR
	tst	data			;if NULL
	breq	cmd_ok		;CMD DONE
	rcall	uart_tx		;else send CMD CHAR
	rjmp	send_cmd		;fetch CMD CHAR

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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
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.
This was new to me too.
Suppose you never get too old to learn.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What this thread needs is pictures..

Second entry googling for "AVR stack pointer"
http://www.avr-tutorials.com/gen...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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.

How confusing.

Last Edited: Tue. Apr 3, 2012 - 11:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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..."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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! :?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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..."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

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)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

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..."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
.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

Last Edited: Tue. Apr 3, 2012 - 12:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

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?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
which seems to tell me that Cliff screwed up again and got the CP the wrong way round!

Just in case the Op doesn't yet know:
After the excution of this instruction sequence,

LDI R0, Cliff
CP  R0, clawson

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..."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That joke was close to being too subtle for me! ;-)

Of course I'm a C programmer so:

if (Cliff==clawson) {
  printf("you are Cliff Lawson and I claim my £5");
}

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... )

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

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:

0000 0010 dddd rrrr   muls    d,d		1
0000 0011 0ddd 0rrr   mulsu   a,a		1
0000 0011 0ddd 1rrr   fmul    a,a		1
0000 0011 1ddd 0rrr   fmuls   a,a		1
0000 0011 1ddd 1rrr   fmulsu  a,a		1
0000 01rd dddd rrrr   cpc     r,r		1
0000 10rd dddd rrrr   sbc     r,r		1
0000 11rd dddd rrrr   add     r,r		1
        =             lsl     r

0001 00rd dddd rrrr   cpse    r,r		1
0001 01rd dddd rrrr   cp      r,r		1
0001 10rd dddd rrrr   sub     r,r		1
0001 11rd dddd rrrr   adc     r,r		1
        =             rol     r

0010 00rd dddd rrrr   and     r,r		1
        =             tst     r
0010 01rd dddd rrrr   eor     r,r		1
        =             clr     r
0010 10rd dddd rrrr   or      r,r		1
0010 11rd dddd rrrr   mov     r,r		1

0011 KKKK dddd KKKK   cpi     d,M		1

0100 KKKK dddd KKKK   sbci    d,M		1

0101 KKKK dddd KKKK   subi    d,M		1

0110 KKKK dddd KKKK   ori     d,M		1
        =             sbr     d,M

0111 KKKK dddd KKKK   andi    d,M		1
        =             cbr     d,n

100! 000d dddd ee-+   ld      r,e		1
100! 001r rrrr ee-+   st      e,r		1
10o0 oo0d dddd booo   ldd     r,b		1
10o0 oo1r rrrr booo   std     b,r		1

1001 000d dddd 0000   lds     r,i		2
1001 000d dddd 010+   lpm     r,z		1
1001 000d dddd 011+   elpm    r,z		1
1001 000r rrrr 1111   pop     r			1
1001 001d dddd 0000   sts     i,r		2
1001 001r rrrr 1111   push    r			1
1001 0100 0000 1001   ijmp				1
1001 0100 0001 1001   eijmp				1
1001 0100 0SSS 1000   bset    S			1
        =             sec
        =             sez
        =             sen
        =             sev
        =             ses
        =             seh
        =             set
        =             sei
1001 0100 1SSS 1000   bclr    S			1
        =             clc
        =             clz
        =             cln
        =             clv
        =             cls
        =             clh
        =             clt
        =             cli
1001 0101 0000 1000   ret				1
1001 0101 0000 1001   icall				1
1001 0101 0001 1000   reti				1
1001 0101 0001 1001   eicall			1
1001 0101 1000 1000   sleep				1
1001 0101 1001 1000   break				1
1001 0101 1010 1000   wdr				1
1001 0101 1100 1000   lpm     ?			1
1001 0101 1101 1000   elpm    ?			1
1001 0101 1110 1000   spm				1
1001 010h hhhh 110h   jmp     h			2
1001 010h hhhh 111h   call    h			2
1001 010r rrrr 0000   com     r			1
1001 010r rrrr 0001   neg     r			1
1001 010r rrrr 0010   swap    r			1
1001 010r rrrr 0011   inc     r			1
1001 010r rrrr 0101   asr     r			1
1001 010r rrrr 0110   lsr     r			1
1001 010r rrrr 0111   ror     r			1
1001 010r rrrr 1010   dec     r			1
1001 0110 KKdd KKKK   adiw    w,K		1
1001 0111 KKdd KKKK   sbiw    w,K		1
1001 1000 pppp psss   cbi     p,s		1
1001 1001 pppp psss   sbic    p,s		1
1001 1010 pppp psss   sbi     p,s		1
1001 1011 pppp psss   sbis    p,s		1
1001 11rd dddd rrrr   mul     r,r		1

1011 0PPd dddd PPPP   in      r,P		1
1011 1PPr rrrr PPPP   out     P,r		1

1100 LLLL LLLL LLLL   rjmp    L			1

1101 LLLL LLLL LLLL   rcall   L			1

1110 KKKK dddd KKKK   ldi     d,M		1
	    =			  ser     d (K=FF)

1111 00ll llll lsss   brbs    s,l		1
        =             brcs    l
        =             brlo    l
        =             breq    l
        =             brmi    l
        =             brvs    l
        =             brlt    l
        =             brhs    l
        =             brts    l
        =             brie    l
1111 01ll llll lsss   brbc    s,l		1
        =             brcc    l
        =             brsh    l
        =             brne    l
        =             brpl    l
        =             brvc    l
        =             brge    l
        =             brhc    l
        =             brtc    l
        =             brid    l
1111 100d dddd 0sss   bld     r,s		1
1111 101d dddd 0sss   bst     r,s		1
1111 110r rrrr 0sss   sbrc    r,s		1
1111 111r rrrr 0sss   sbrs    r,s		1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That 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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

It is confusing for Disassemblers.

And simulators ;-) (hence my list above).

Atmel do seem to make a thing of:

Page 1 of just about any AVR datasheet wrote:
• Advanced RISC Architecture
– 130 Powerful Instructions – Most Single-clock Cycle Execution

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)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sinedo,

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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I still don't see the point with LDD/STD,

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)?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
sinedo,

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.

3:        byte hanoi(byte n){ 
+0000001A:   3082        CPI       R24,0x02       Compare with immediate

+0000001B:   F020        BRCS      PC+0x05        Branch if carry set

7:            return ( 2*hanoi(n-1) + 1 ); 
+0000001C:   5081        SUBI      R24,0x01       Subtract immediate

+0000001D:   DFFC        RCALL     PC-0x0003      Relative call subroutine

+0000001E:   0F88        LSL       R24            Logical Shift Left

+0000001F:   5F8F        SUBI      R24,0xFF       Subtract immediate

8:        }
+00000020:   9508        RET                      Subroutine return

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 =)

Last Edited: Tue. Apr 3, 2012 - 02:20 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

By 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

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Because 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?

.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 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
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.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

'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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
'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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lennart wrote:
Quote:
'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.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
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.

I know which I prefer. You know your teacher.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I 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 =)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
.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.

Can someone comment on the code please

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You lost the return address here:

  POP TEMP 
  POP TEMP 

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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

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!)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
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..."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

which complicates the picture a bit.

return address = 2 plates, simple ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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..."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Great minds think alike! Cliff just beat me to the punch.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do we say "SNAP!" now? ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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

Last Edited: Wed. Apr 4, 2012 - 11:56 AM

Pages