RET function returning to beginning of code instead of CALL point

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am new to assembly, but am working on a recursive factorial subassembly.

 

It must use the stack only to store values between iterations, and upon completion push the factorial onto the stack to POP after returning to CALL.

 

Below is my code, which works, except it returns to 'RJMP main' instead of returning to the 'CALL' after finishing.

 

Suggestions?

.def n = R16
.def result = R17
.def tmp =r20

.org 0x0000 ; next instruction will be written to address 0x0000
            ; (the location of the reset vector)
rjmp main   ; set reset vector to point to the main code entry point

main:       ; jump here on reset

; initialize the stack (RAMEND = 0x10FF by default for the ATmega128A)
ldi R16, HIGH(RAMEND)
out SPH, R16
ldi R16, low(RAMEND)
out SPL, R16
LDI n, 5; load a value into n
PUSH n ; push n on the stack
CALL factN ; calculate the factorial of n
POP result ; pop n! from the stack, save in result
here: RJMP here ; loop forever

factN:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Recursive Subroutine to Calculate Factorial of n
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
push n  ; push n onto the stack
pop n   ; pop n off the stack
TST R0  ; test if R0 is <= 0
BRNE notZero ; if RO is >0 jump to nonZero
ldi tmp,1 ; load 1 into tmp
mul tmp,tmp ; multiply tmp by itself to populate R0 with 1
push R0 ; push R0 onto the stack
notZero:
pop R0  ; pop R0
mul R0, n ; mulitply R0 by n
dec n ; decrement n
push n ; push n onto stack
push R0 ; push r0 onto stack
CPI n, 1 ; does n == 1
BRNE factN ;loop factN until n == 1
ret ; return to CALL

         

.def n = R16
.def result = R17
.def tmp = r20

.org 0x0000 ; next instruction will be written to address 0x0000
            ; (the location of the reset vector)
rjmp main   ; set reset vector to point to the main code entry point

main:       ; jump here on reset

; initialize the stack (RAMEND = 0x10FF by default for the ATmega128A)
ldi R16, HIGH(RAMEND)
out SPH, R16
ldi R16, low(RAMEND)
out SPL, R16
LDI n, 5        ; load a value into n
PUSH n          ; push n on the stack
CALL factN      ; calculate the factorial of n
POP result      ; pop n! from the stack, save in result
here: RJMP here ; loop forever

factN:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Recursive Subroutine to Caclulate the Factorial of n
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
push n	    ; push n onto the stack
pop n	    ; pop n off the stack
TST R0	    ; test if R0 is <= 0
BRNE notZero; if RO is >0 jump to nonZero
ldi tmp,1   ; load 1 into tmp
mul tmp,tmp ; multiply tmp by itself to populate R0 with 1
push R0     ; push R0 onto the stack
notZero:
pop R0	    ; pop R0
mul R0, n   ; mulitply R0 by n
dec n       ; decrement n
push n      ; push n onto stack
push R0     ; push r0 onto stack
CPI n, 1    ; does n == 1
BRNE factN  ; loop factN until n == 1
ret         ; return to CALL
.def n = R16
.def result = R17
.def tmp =r20

.org 0x0000 ; next instruction will be written to address 0x0000
            ; (the location of the reset vector)
rjmp main   ; set reset vector to point to the main code entry point

main:       ; jump here on reset

; initialize the stack (RAMEND = 0x10FF by default for the ATmega128A)
ldi R16, HIGH(RAMEND)
out SPH, R16
ldi R16, low(RAMEND)
out SPL, R16
LDI n, 5; load a value into n
PUSH n ; push n on the stack
CALL factN ; calculate the factorial of n
POP result ; pop n! from the stack, save in result
here: RJMP here ; loop forever

factN:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Recursive Subroutine to Calculate Factorial of n
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
push n  ; push n onto the stack
pop n   ; pop n off the stack
TST R0  ; test if R0 is <= 0
BRNE notZero ; if RO is >0 jump to nonZero
ldi tmp,1 ; load 1 into tmp
mul tmp,tmp ; multiply tmp by itself to populate R0 with 1
push R0 ; push R0 onto the stack
notZero:
pop R0  ; pop R0
mul R0, n ; mulitply R0 by n
dec n ; decrement n
push n ; push n onto stack
push R0 ; push r0 onto stack
CPI n, 1 ; does n == 1
BRNE factN ;loop factN until n == 1
ret ; return to CALL
Last Edited: Mon. Apr 20, 2020 - 10:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry for 3 copies of the code. I tried to remove the ones not in the code box, but they don't show up when I hit edit.

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

I didn't scan your code closely, but from your description there's a chance you're over-running the stack (very easy to do with recursive programming on a small micro!).  Either that or you're not matching your PUSHes with your POPs.  Run the code in the simulator and see what the stack is doing on each function call, and on the final crescendo of return statements.

Last Edited: Mon. Apr 20, 2020 - 09:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sildeon wrote:
recursive

 

surprise

 

 

 

Suggestions?

.def n = R16
.def result = R17
.def tmp =r20

.org 0x0000 ; next instruction will be written to address 0x0000
            ; (the location of the reset vector)
rjmp main   ; set reset vector to point to the main code entry point

main:       ; jump here on reset

; initialize the stack (RAMEND = 0x10FF by default for the ATmega128A)
ldi R16, HIGH(RAMEND)
out SPH, R16
ldi R16, low(RAMEND)
out SPL, R16
LDI n, 5; load a value into n
PUSH n ; push n on the stack
CALL factN ; calculate the factorial of n
POP result ; pop n! from the stack, save in result
here: RJMP here ; loop forever

factN:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Recursive Subroutine to Calculate Factorial of n
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
push n  ; push n onto the stack
pop n   ; pop n off the stack
TST R0  ; test if R0 is <= 0
BRNE notZero ; if RO is >0 jump to nonZero
ldi tmp,1 ; load 1 into tmp
mul tmp,tmp ; multiply tmp by itself to populate R0 with 1
push R0 ; push R0 onto the stack
notZero:
pop R0  ; pop R0
mul R0, n ; mulitply R0 by n
dec n ; decrement n
push n ; push n onto stack
push R0 ; push r0 onto stack
CPI n, 1 ; does n == 1
BRNE factN ;loop factN until n == 1
ret ; return to CALL

Don't do that!!

 

As  kk6gm suggested, recursion is risky at the best of times - especially so on a small micro with very limited RAM and, thus, stack space.

Any recursive algorithm can be re-written in a non-recursive form.

 

In this case, consider:

.def result = R17

So you are only accounting for an 8-bit result.

 

The largest factorial that will fit in 8 bits is 5!

 

Therefore you code only needs to consider 7 different inputs.

 

So the simplest solution is a small lookup table.

 

 

 

EDIT

 

The phantom text seems to have hit my post, too!

 

frown

 

EDIT 2

 

I thought there was a way to see the raw HTML - can't find it now.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Mon. Apr 20, 2020 - 10:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Interesting bug with the forum, I also tried to edit the post but it only shows the code in the code box and not the other 2??? surprise

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

The phantom text doesn't show up in the quote box either, but it does show up on my screen.

 

Now, it's hard to tell with all the jumping and branching around, but I strongly suspect you have a bonus 'push' in your loop that doesn't have a respective 'pop'.

 

This is an AVR.  You can use more than three registers!   S.

 

Edited to add PS - It's a fundamental part of the AVR's design that a smashed stack into unintialized RAM causes a RESET.  S.

Last Edited: Tue. Apr 21, 2020 - 05:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

I thought there was a way to see the raw HTML - can't find it now.

Used to be a way by disabling Javascript for the site, but that stopped working a few years ago after a site update.

 

I can still get it to show me on my mobile, mostly because it's an ancient version of Chrome.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]