Shop OBEX P1 Docs P2 Docs Learn Events
PASM - Nested Call question — Parallax Forums

PASM - Nested Call question

Chris_DChris_D Posts: 305
edited 2010-03-15 23:15 in Propeller 1
Hi folks,

After reading through the manual about call, I think I understand it but would like a bit of clarification.

It appears that a subroutine cannot call itself (recursive).

Can a subroutine call another subroutine and if so, is there any limit to the depth of called subroutines?

Main...
·· Call Sub1

Main end...


Sub1
·· Call Sub2
Sub1_Ret


Sub2

Sub2_Ret


Chris

Comments

  • Cluso99Cluso99 Posts: 18,069
    edited 2010-03-15 11:11
    Chris: You can nest to any level PROVIDING you do not corrupt the return addresses. This is for you to handle and ensure.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Links to other interesting threads:

    · Home of the MultiBladeProps: TriBlade,·RamBlade,·SixBlade, website
    · Single Board Computer:·3 Propeller ICs·and a·TriBladeProp board (ZiCog Z80 Emulator)
    · Prop Tools under Development or Completed (Index)
    · Emulators: CPUs Z80 etc; Micros Altair etc;· Terminals·VT100 etc; (Index) ZiCog (Z80) , MoCog (6809)·
    · Prop OS: SphinxOS·, PropDos , PropCmd··· Search the Propeller forums·(uses advanced Google search)
    My cruising website is: ·www.bluemagic.biz·· MultiBlade Props: www.cluso.bluemagic.biz
  • heaterheater Posts: 3,370
    edited 2010-03-15 11:24
    You can nest subroutine calls as much as you like. So A can call B can call C can call....

    BUT a subroutine cannot call itself recursively or be a part of a chain of recursive calls.

    So this won't work: A calls A
    and this won't work: A calls B calls C calls A.

    As you may have read the the return address for a CALL is stored in the RET instruction of the called function. So a recursive call would destroy the previous calls return address.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    For me, the past is not over yet.
  • pullmollpullmoll Posts: 817
    edited 2010-03-15 11:36
    Chris_D said...
    Hi folks,
    Can a subroutine call another subroutine and if so, is there any limit to the depth of called subroutines?

    If you really need nested calls, you would have to save the return address yourself on entry into the subroutine and restore it before you hit the return.

    stackptr    long    somewhere_in_hub_RAM
    tmp          long    0
    
    nested_subroutine
               mov tmp, nested_subroutine_ret
               sub  stackptr, #2
               wrword tmp, stackptr
    
               ... do your things and perhaps call nested_subroutine ...
    
               rdword tmp, stackptr
               movs nested_subroutine_ret, tmp
               add stackptr, #2
    nested_subroutine_ret
               ret
    
    



    Note the movs in the exit code. It copies bits 8..0 of the source (tmp) to the source field (also bits 8...0) of the destination without touching the other bits.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    He died at the console of hunger and thirst.
    Next day he was buried. Face down, nine edge first.

    Post Edited (pullmoll) : 3/15/2010 11:43:06 AM GMT
  • MagIO2MagIO2 Posts: 2,243
    edited 2010-03-15 12:57
    @pullmoll:

    Why would you copy nested_subroutine_ret to tmp before writing to HUB-RAM?

    wrword nested_subroutine_ret, stackptr 'is doing the same job

    And what about

    rdword nested_subroutine_ret, stackptr
    movi nested_subroutine_ret, # code for ret

    ? Shouldn't that work as well ... would save a LONG because tmp·is no longer needed then·;o)

    Post Edited (MagIO2) : 3/15/2010 1:02:09 PM GMT
  • kuronekokuroneko Posts: 3,623
    edited 2010-03-15 13:16
    MagIO2 said...
    And what about

    rdword nested_subroutine_ret, stackptr
    movi nested_subroutine_ret, # code for ret

    ? Shouldn't that work as well ... would save a LONG because tmp is no longer needed then ;o)
    Close, but no cigar [noparse]:)[/noparse] rdword clears the hi-word of the instruction and movi isn't enough to resurrect it from being a nop.
  • pullmollpullmoll Posts: 817
    edited 2010-03-15 13:35
    MagIO2 said...
    @pullmoll:

    Why would you copy nested_subroutine_ret to tmp before writing to HUB-RAM?

    wrword nested_subroutine_ret, stackptr 'is doing the same job

    And what about

    rdword nested_subroutine_ret, stackptr
    movi nested_subroutine_ret, # code for ret

    ? Shouldn't that work as well ... would save a LONG because tmp is no longer needed then ;o)

    You know, there is always one last bug in any piece of code.
    Further any piece of code can be made shorter by at least one instruction.
    The logical consequence is that every code consists of one instruction which is wrong.
    smile.gif

    Thanks for the hint. Yes, using tmp isn't necessary. I first had an AND tmp, #$1ff in there until it came to me that the MOVS later on would do the masking anyway, so I removed the AND again.

    Your version of the exit code needs to insert a NOP before the RET, so we are on par again smile.gif

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    He died at the console of hunger and thirst.
    Next day he was buried. Face down, nine edge first.

    Post Edited (pullmoll) : 3/15/2010 1:43:06 PM GMT
  • MagIO2MagIO2 Posts: 2,243
    edited 2010-03-15 13:37
    I already had a fear ... but no propeller manual with me ....
  • Chris_DChris_D Posts: 305
    edited 2010-03-15 21:54
    Hmmm, it is a bit more work than I had hoped but this asm after all so I shouldn't have thought is was all handled by the call statment.· In any case, it really doesn't appear to be all that bad or difficult.· I am pretty sure I have a handle on what has to be done, thanks guys!

    Chris
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-03-15 23:15
    Here's a technique that's a little less "work", once you've got enter and exit in place:

    [b]DAT[/b]
    
    '--------[noparse][[/noparse]* Main Program ]------------------------------------------------------
    
                  [b]jmpret[/b]    recurse,#[b]sub[/b]1           'Use jmpret instead of call
                  [b]jmpret[/b]    recurse,#[b]sub[/b]2           '  for recursive subroutines.
                  '...
    
    '--------[noparse][[/noparse]* Recursive Subroutines ]---------------------------------------------
    
    'Sub1 calls itself and sub2.
    
    [b]sub[/b]1          [b]call[/b]      #enter                  'Stack the return address.
                  '...
                  [b]jmpret[/b]    recurse,#[b]sub[/b]2
                  [b]jmpret[/b]    recurse,#[b]sub[/b]1
                  '...           
                  [b]jmp[/b]       #exit                   'Unstack and return.
    
    'Sub2 calls itself.
    
    [b]sub[/b]2          [b]call[/b]      #enter
                  '...
                  [b]jmpret[/b]    recurse,#[b]sub[/b]2
                  '...
                  [b]jmp[/b]       #exit
    
    '--------[noparse][[/noparse]* Recursive Stack Handling ]------------------------------------------
    
    'Call #enter to stack the return address.
    
    enter         [b]mov[/b]       stack,recurse           'Copy return address to stack.
                  [b]add[/b]       enter,inc_dest          'Increment the stack pointer.
    enter_ret     [b]ret[/b]                               'Return to caller.
    
    'Jmp #exit to unstack and return.
    
    exit          [b]sub[/b]       enter,inc_dest          'Decrement the stack pointer.
                  [b]mov[/b]       acc,enter               'Get the stack pointer...
                  [b]shr[/b]       acc,#9                  '  ...in lower 9 bits.
                  [b]movs[/b]      recurse,acc             'Copy it to jmp address.
                  [b]nop[/b]                               'Nop for pipeline.
    recurse       [b]jmp[/b]       0-0                     'Return to calling routine.
    
    '--------[noparse][[/noparse]* Constants ]---------------------------------------------------------
    
    inc_dest      [b]long[/b]      $200                    'Amount required to increment dest.
    
    '--------[noparse][[/noparse]* Variables ]---------------------------------------------------------
    
    acc           [b]res[/b]       1                       'Scratch register.
    stack         [b]res[/b]       100                     'Stack area.          
    
    
    


    It uses the cog for the stack (assuming you've got the room and don't recurse too deeply) so there's no hub access penalty. The stack pointer is embedded in the first instruction of enter.

    Please remember: You do not need to do this for nested subroutines -- only those which create a recursion loop. Nested subroutines take care of themselves.

    -Phil
Sign In or Register to comment.