Shop OBEX P1 Docs P2 Docs Learn Events
Does PASM offer an indexed or indirect JMP or CALL? — Parallax Forums

Does PASM offer an indexed or indirect JMP or CALL?

K2K2 Posts: 693
edited 2013-04-11 17:54 in Propeller 1
I need to jump to one of 16 routines, depending on the state of four bits. If I read the manual right, the s and d fields of JMP and CALL instructions are calculated at assembly time. Can MOVS and MOVD still be used on them to do indexed jumps or calls? Can someone steer me to an example of indexed or indirect execution?

Comments

  • kwinnkwinn Posts: 8,697
    edited 2013-04-10 12:44
    You need to use MOVS/MOVD for that. No indirect jump instructions. On my iPad so I can not show an example but it is very simple. Put a label in front of the jump instruction you want to modify and then use a MOVS/MOVD with that label in the destination.
  • Mike GreenMike Green Posts: 23,101
    edited 2013-04-10 12:58
    Yes, you can use MOVS and MOVD on a JMP or JMPRET (call) instruction. Remember that the destination field is (for the JMPRET) really the address of a return (JMP) instruction whose destination field is modified by the JMPRET to have the return address in it. In other words, you have to understand what you're doing if you're going to modify a JMPRET (call) instruction. For a JMP, the destination field isn't used, so you modify just the source field (with MOVS). See this
            movs  myMove,#@table   ' base address of table
            add   myMove,index   ' add index (0 to n-1)
            nop   ' needed because of pipelining
    myMove  movs  myJmp,0-0   ' indirecting
            nop   ' needed because of pipelining
    myJmp   jmp   #0-0   ' now go to destination
    table   long   @entry1, @entry1, @entry2, @entry3   ' plus 12 more entry points
    
    You can use other instructions instead of the NOPs just as long as there's a 1 instruction delay after changing myMove and myJmp.
  • K2K2 Posts: 693
    edited 2013-04-10 13:01
    Thanks, kwinn. So in the case of CALL, do I also need to use MOVS and/or MOVD on the CALL_RET instruction?

    Edit: Oops. Looked like our posts crossed, Mike. Looks like you've just answered my follow-up question, too. Thank you!
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-04-10 13:45
    Actually, you can avoid the movs stuff entirely, since PASM does support indirect jmps (i.e. those without the # in the target address. Here's an example:
            mov   target,#table     'Init target address with table[0].
            add   target,index      'Add value in index.
            jmp   target            'Jump to address in target.
    
    table   jmp   #loc0             'Jump table.
            jmp   #loc1
            ...
            jmp   #loc14
            jmp   #loc15
    
    target  res   1
    index   res   1
    

    -Phil
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-04-10 14:26
    Here's another example that takes advantage of indirect addressing:
            movs  myjmp,#table     'Init target address with table[0].
            add   myjmp,index      'Add value in index.
            nop
    myjmp   jmp   0-0              'Jump to address from table.
    
    table   long  loc0, loc1, ... , loc14, loc15 
    
    index   res   1
    

    -Phil
  • K2K2 Posts: 693
    edited 2013-04-10 15:20
    @Phil: The approach you illustrate in Post #5 is ideally suited to the task at hand. I want to transfer execution between groups of routines in an interleaving pattern. Maintaining a target register for each group will provide this capability. That's how I implemented real-time FIR filters on the SX. I'm delighted that a similar arrangement is possible on the Prop.
  • kuronekokuroneko Posts: 3,623
    edited 2013-04-10 16:49
    Minor optimisation for #6 (based on [thread=143351]this puzzle[/thread]):
    add     myjmp,index      'Add value in index.
            nop
    myjmp   jmpret  $, $+1           'Jump to address from table.
    
    table   long    loc0, loc1, ... , loc14, loc15 
    
    index   res     1
    
    index may be folded into the pipeline slot (nop).
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-04-10 18:58
    Well, yeah. there's the optimum way and the way you'd prefer to explain to your grandmother. :) 'Clever, nonetheless!

    -Phil
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2013-04-10 20:39
    Phil, imagine, have you met Marco's grandmother?
  • K2K2 Posts: 693
    edited 2013-04-10 20:53
    I'm pretty sure I'm the grandmother this time.

    BTW, grandma is having a problem. For the life of me I can't understand why this isn't working. It's based on Phil's #5 post. Somehow I screwed up his perfectly great idea.
    CON
    
      _clkmode  = xtal1 + pll16x
      _xinfreq  = 5_000_000
    
    VAR
    
      long  transfer
    
    OBJ
    
      ser    : "Parallax Serial Terminal"
    
    PUB start
    
      ser.Start(115200)   
      transfer := 0
      cognew(@exp, @transfer)
      repeat
        ser.Dec(transfer)
    
    DAT
                  org       0
                  mov       index, #0
    exp           mov       target, #table     'Init target address with table[0].
                  add       target, index      'Add value in index.
                  jmp       target            'Jump to address in target.
    
    table         jmp       #loc0             'Jump table.
                  jmp       #loc1
                  jmp       #loc2
                  jmp       #loc3
    '********************************************
    loc0          mov       index, #1
                  add       count, #1
                  jmp       #exp
    '********************************************              
    loc1          mov       index, #2
                  jmp       #exp
    '********************************************              
    loc2          mov       index, #3
                  wrlong    count, par
                  jmp       #exp
    '********************************************              
    loc3          mov       index, #0
                  jmp       #exp
    '********************************************
    count         res       1
    target        res       1
    index         res       1
                                 
    
  • Mike GreenMike Green Posts: 23,101
    edited 2013-04-10 20:56
    You're not initializing count. It's not set to zero or anything specific
  • K2K2 Posts: 693
    edited 2013-04-10 21:14
    That is true. But it is being use for diagnostic purposes only, so I figured that whatever state it started up in, it would just increment from there.

    The actual busted code is much bigger. I wrote this pedacito just to pare down the problem to its essentials..
  • kuronekokuroneko Posts: 3,623
    edited 2013-04-10 21:29
    K2 wrote: »
      cognew(@exp, @transfer)
    
    DAT
                  org       0
                  mov       index, #0
    exp           mov       target, #table     'Init target address with table[0].
    
    Does this look odd to you?
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-04-10 21:30
    K2,

    It looks like you added a line ahead of exp, so index never gets initialized.

    -Phil
  • K2K2 Posts: 693
    edited 2013-04-10 23:17
    Good grief. Thanks guys. Granny is really off her oats tonight.

    Marko, that's a fascinating optimization you used. In the morning I'm going to look into it further.

    The Prop is so unusual that I've been timid to wander too far from the reservation. But it's not like something is going to blow up. Probably.

    Anyway, you are all great examples of digging in and figuring things out, a trait I'd love to possess.
  • K2K2 Posts: 693
    edited 2013-04-11 09:51
    Straying off the reservation has proven unhealthy yet again. A few more details were folded into the code sample, to get closer to what I need the Propeller to do, and the wheels fell off. :( If this is the same sort of stupid mistake as last time, I'll go sit in a corner and play with my Lincoln Logs.

    Based on all the excellent info from yesterday, I figured I could store the address of a routine in a long (named LOC_RET in this case) and later jump to that long (without #) and it would take me to the routine. Maybe it is doing that, but it seems certain from the diagnostic link (Prop Term) that something goes akimbo very quickly. (Initializing 'count' helped a lot in that regard. :) )
    CON
            _clkmode  = xtal1 + pll16x
            _xinfreq  = 5_000_000
            
    VAR
            long  transfer
    
    OBJ
            ser: "Parallax Serial Terminal"
    
    PUB s
            ser.Start(115200)   
            transfer := 0
            cognew(@code0, @transfer)
            repeat
              ser.Dec(transfer)
              
    DAT           org       0
    
    '******** TYPE ONE ROUTINES **************
                                            '*
    code0         mov       target, #table  '*
                  mov       index, #3       '*
                  add       target, index   '*
                  '< do stuff here >        '*
                  mov       loc_ret, #ret0  '*
                  jmp       target          '*
    ret0          '< more code here >       '*
    '*****************************************              
    code1         mov       target, #table  '*
                  mov       index, #2       '*
                  add       target, index   '*
                  '< do stuff here >        '*
                  mov       loc_ret, #ret1  '*
                  jmp       target          '*
    ret1          '< more code here >       '*
    '*****************************************              
    code2         mov       target, #table  '*
                  mov       index, #1       '*
                  add       target, index   '*
                  '< do stuff here >        '*
                  mov       loc_ret, #ret2  '*
                  jmp       target          '*
    ret2          '< more code here >       '*
    '*****************************************              
    code3         mov       target, #table  '*
                  mov       index, #0       '*
                  add       target, index   '*
                  '< do stuff here >        '*
                  mov       loc_ret, #ret3  '*
                  jmp       target          '*
    ret3          jmp       code0           '*
    '*****************************************
    
    '******** TYPE TWO ROUTINES **************    
    sub0          add       count, #1       '*
                  '< do more stuff here >   '*
                  jmp       loc_ret         '*
    '*****************************************              
    sub1          '< do stuff here >        '*
                  jmp       loc_ret         '*
    '*****************************************  
    sub2          wrlong    count, par      '*        
                  '< do more stuff here >   '*
                  jmp       loc_ret         '*
    '*****************************************
    sub3          '< do stuff here >        '*
                  jmp       loc_ret         '*
    '*****************************************
        
    table         jmp       #sub0            
                  jmp       #sub1
                  jmp       #sub2
                  jmp       #sub3
    
    count         long      1
    target        res       1
    index         res       1
    loc_ret       res       1
                                 
    
  • JonnyMacJonnyMac Posts: 9,107
    edited 2013-04-11 10:25
    I prefer the great-grandmotherly route of code-so-obvious-even-JonnyMac-can-understand-it.
    getcmd                  rdlong  t1, par                 wz      ' check for command
            if_z            jmp     #getcmd
            
                            min     t1, #0                          ' limit range
                            max     t1, #4                          ' commands + 1
                            add     t1, #jmptable                   ' prep for jump
                            jmp     t1                              ' do it
                                                                     
    jmptable                jmp     #cmdexit                         
    do_cmd1                 jmp     #cmd1                            
    do_cmd2                 jmp     #cmd2                            
    do_cmd3                 jmp     #cmd3                            
                                                                     
    cmdexit                 mov     t1, #0                          ' clear old command
                            wrlong  t1, par
                            jmp     #getcmd
    
    
                            
    ' handler for cmd == 1
    
    cmd1                    ' code here
                            ' -- write result to p1pntr
    
                            jmp     #cmdexit
    


    This is taken from a template I created for PASM code that accepts a command from another cog. It's simple and it always seems to work. I'm an actor. I like simple. ;)
  • K2K2 Posts: 693
    edited 2013-04-11 10:35
    This is a fascinating piece of code. I like the use of min and max...less cryptic than AND #$f. You are also more efficient in reusing code. It will take me a few minutes, though, to get my head around using this approach to thread execution between two blocks of routines in the same cog. In other words, you're a few steps ahead of me. I've got to catch up.

    BTW, Thank you!!
  • K2K2 Posts: 693
    edited 2013-04-11 12:56
    Following Jon's great great grandmotherly approach, the following test was assembled. Pleased to report it works great well! Thanks again to all for your expert guidance and cracking code samples.
    CON
            _clkmode  = xtal1 + pll16x
            _xinfreq  = 5_000_000
            
    VAR
            long  transfer
    
    OBJ
            ser: "Parallax Serial Terminal"
    
    PUB s
            ser.Start(115200)   
            transfer := 0
            cognew(@goA, @transfer)
            repeat
              ser.Dec(transfer)                     'just to show activity
              
    DAT                     org     0
            
    goA                     mov     A, indexA
                            add     A, #tableA                   
                            jmp     A                            
    
    goX                     mov     X, indexX
                            add     X, #tableX
                            jmp     X                        
                                                                     
    tableA                  jmp     #abc0                         
                            jmp     #abc1                            
                            jmp     #abc2                            
                            jmp     #abc3
    
    
    tableX                  jmp     #xyz0
                            jmp     #xyz1
                            jmp     #xyz2
                            jmp     #xyz3
    
    
    abc0                    wrlong  count, par      'diagnostic
                            add     indexA, #1
                            jmp     #goX
    abc1                    add     indexA, #1
                            jmp     #goX
    abc2                    add     indexA, #1
                            jmp     #goX
    abc3                    mov     indexA, #0
                            jmp     #goX
    
    xyz0                    add     count, #1       'diagnostic
                            add     indexX, #1                     
                            jmp     #goA
    xyz1                    add     indexX, #1
                            jmp     #goA
    xyz2                    add     indexX, #1
                            jmp     #goA
    xyz3                    mov     indexX, #0
                            jmp     #goA
                                                                     
    count                   long    0                    
    indexA                  long    0
    indexX                  long    0
    A                       res
    X                       res
    
  • ElectrodudeElectrodude Posts: 1,658
    edited 2013-04-11 15:27
    Why does jmp seem to always have a number sign and have its i bit set? What if I say "jmp address" with no number sign and so no i bit? Shouldn't it do an indirect jump to the location contained in register "address"? The Prop Manual says i can be anything for jmp, jmpret in the PASM reference table and my understanding of jmp is that the only difference between ret and jmp/jmpret is that the i bit is always set for ret and the call/jmpret sets the s of the ret.

    electrodude
  • kuronekokuroneko Posts: 3,623
    edited 2013-04-11 16:19
    K2 wrote: »
    DAT           org       0
    
    '******** TYPE ONE ROUTINES **************
                                            '*
    code0         mov       target, #table  '*
    ...
    code3         mov       target, #table  '*
                  mov       index, #0       '*
                  add       target, index   '*
                  '< do stuff here >        '*
                  mov       loc_ret, #ret3  '*
                  jmp       target          '*
    [COLOR="#FF8C00"]ret3          jmp       code0[/COLOR]           '*
    '*****************************************
    
    '******** TYPE TWO ROUTINES **************    
    sub0          add       count, #1       '*
                  '< do more stuff here >   '*
                  jmp       loc_ret         '*
    '*****************************************
        
    table         jmp       #sub0            
                  jmp       #sub1
                  jmp       #sub2
                  jmp       #sub3
    
    You get what you asked for. In code3 you prime the return address with ret3. When you get there you do an indirect jump to code0 which ends up at table[0] which is in fact sub0. Increment count jump to ret3 again, stuck. Did you mean jmp #code0?
  • K2K2 Posts: 693
    edited 2013-04-11 17:36
    Kuroneko: You are exactly right, of course. Your ability to follow the cruft of others and find their mistakes is legendary. Lincoln Logs it is.

    Perhaps if I hadn't been delving into the dangerous territory of JMPs w/o #, I wouldn't have made such a blunder, but who knows? I'll have to ponder that question while I'm assembling miniature log cabins this evening.

    Still, the latest version (post 20?) works really well and is less cumbersome than my previous efforts, so perhaps it was worth a few dumb mistakes to get to that point. I've folded the great grandmother approach into the full code (302 lines of asm) and it spins like a top. Now that the needed functionality has been achieved I'm free to tweak it to death. :)
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2013-04-11 17:54
    Electrodude,

    "i" stands for "immediate," not "indirect." When the bit is set (i.e. JMP #target), the jump location is in the source field of the instruction. When the bit is cleared (i.e. JMP target), the contents of the target location, pointed to by the source field, are used for the jump address.

    -Phil
Sign In or Register to comment.