Shop OBEX P1 Docs P2 Docs Learn Events
Calling Spin code from Assembly — Parallax Forums

Calling Spin code from Assembly

Kevin L.Kevin L. Posts: 10
edited 2006-07-26 09:46 in Propeller 1
How do I call a Spin procedure from within Assembly code?

Comments

  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2006-07-25 22:12
    Here is an example...

    Edit: Updated see below...

    [s][b]CON[/b]
    
      [b]_CLKMODE[/b] = [b]XTAL[/b]1 + [b]PLL[/b]16X
      [b]_XINFREQ[/b] = 5_000_000
    
      LEDPin        = 16
      
    [b]PUB[/b] MainLoop
        'cognew(CallSpinFromAsm,LEDTest1)    <----Pass address of Spin program here
        [b]cognew[/b](CallSpinFromAsm,LEDTest2)
    
    [b]PUB[/b] LEDTest1
        [b]Dira[/b][noparse][[/noparse]LEDPin] := 1
        [b]Outa[/b][noparse][[/noparse]LEDPin] := 1
        [b]repeat[/b] 
         
    [b]PUB[/b] LEDTest2
        [b]Dira[/b][noparse][[/noparse]LEDPin] := 1
        [b]repeat[/b]
          [b]Outa[/b][noparse][[/noparse]LEDPin] := 1 - [b]Ina[/b][noparse][[/noparse]LEDPin]
    
    [b]DAT[/b]
                            [b]org[/b]
    CallSpinFromAsm
                  'PG 366
                  'Table 5-1:     Destination Register Fields
            
                  'Bits 31:18     14-bit long address for PAR register
                  'Bits 17:4      14-bit long address of code to load
                  'Bit   3        Indicates a NEW cog
                  'Bit   2:0      Cog ID
                            [b]rdlong[/b]          Destination,            [b]par[/b]     'Pass address location of Spin program
                            [b]shl[/b]             Destination,            #4      'Shift 14-bit Long address left by 4-bits
                            [b]add[/b]             Destination,            $F      'Set Bits 0-3 High ; CogID = 7 New = 1
            
                            [b]COGINIT[/b]         Destination
    
    Destination             [b]res[/b]       1[/s]
    
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 7/26/2006 7:36:08 AM GMT
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-25 23:10
    Beau,
    Are you sure that's correct? Chip posted a very different short assembly routine that mostly made sure the clock was set according to what's specified in the SPIN program, but had a very different COGINIT operand that pointed to the SPIN interpreter in ROM so it gets loaded into the COG to begin interpreting the SPIN program in RAM from the beginning. Would you please explain how your example works because the assembly piece doesn't make sense from what I've gleaned from the manual and forum threads.
    Mike
  • cgraceycgracey Posts: 14,206
    edited 2006-07-26 01:04
    Yeah, unfortunately, it's more complicated than Beau's example. You must set up some word pointers, have some stack allocated and initialized, and then launch the interpreter pointing to the word pointers. I really like Beau's approach, though. That could be a goal for the next chip.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔


    Chip Gracey
    Parallax, Inc.
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2006-07-26 05:38
    Mike,

    You are right.

    After discussing with Chip on the phone this evening what was really happening with the code that I posted, it seems that there is no easy way
    to do this.

    From a learning perspective, here is what my code IS doing that makes it "appear" to function properly....

    [b]cognew[/b](CallSpinFromAsm,LEDTest1)
    
    '          OR
    
    [b]cognew[/b](CallSpinFromAsm,LEDTest2)
    
    



    This line never actually runs the Assembly code pointed to at 'CallSpinFromAsm'. Because the syntax for 'cognew' used in this way....

    [i]COGNEW(AsmAddress, Parameter)[/i]
    
    



    ...COGNEW evaluates the 'Parameter' argument as 'LEDTest1' or 'LEDTest2' essentially looking at it as a function awaiting a value to
    be returned so that it can be passed on to the Assembly program.
    In doing so, 'LEDTest1' or 'LEDTest2' get launched but never return to the "COGNEW" command which originally called it.

    To help prove this, the original program could have looked like this, and it still would have appeared to function properly...

    [b]CON[/b]
    
      [b]_CLKMODE[/b] = [b]XTAL[/b]1 + [b]PLL[/b]16X
      [b]_XINFREQ[/b] = 5_000_000
    
      LEDPin        = 16
      
    [b]PUB[/b] MainLoop
        'cognew(CallSpinFromAsm,LEDTest1)    <----Pass address of Spin program here
        [b]cognew[/b](CallSpinFromAsm,LEDTest2)
    
    [b]PUB[/b] LEDTest1
        [b]Dira[/b][noparse][[/noparse]LEDPin] := 1
        [b]Outa[/b][noparse][[/noparse]LEDPin] := 1
        [b]repeat[/b] 
         
    [b]PUB[/b] LEDTest2
        [b]Dira[/b][noparse][[/noparse]LEDPin] := 1
        [b]repeat[/b]
          [b]Outa[/b][noparse][[/noparse]LEDPin] := 1 - [b]Ina[/b][noparse][[/noparse]LEDPin]
    
    [b]DAT[/b]
                            [b]org[/b]
    CallSpinFromAsm
    
    
    



    Notice that there is nothing in the Assembly section other than a label? The code could be further equated to...

    [b]CON[/b]
    
      [b]_CLKMODE[/b] = [b]XTAL[/b]1 + [b]PLL[/b]16X
      [b]_XINFREQ[/b] = 5_000_000
    
      LEDPin        = 16
      
    [b]PUB[/b] MainLoop
        'LEDTest1
        LEDTest2
    
    [b]PUB[/b] LEDTest1
        [b]Dira[/b][noparse][[/noparse]LEDPin] := 1
        [b]Outa[/b][noparse][[/noparse]LEDPin] := 1
        [b]repeat[/b] 
         
    [b]PUB[/b] LEDTest2
        [b]Dira[/b][noparse][[/noparse]LEDPin] := 1
        [b]repeat[/b]
          [b]Outa[/b][noparse][[/noparse]LEDPin] := 1 - [b]Ina[/b][noparse][[/noparse]LEDPin]
    
    
    



    ...Once again proving that there was no Assembly code that was actually executed.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2006-07-26 07:30
    If you loosen your definition of "call" slightly, you can call a Spin routine from assembly by using a dispatch table. This is the same technique used to call assembly routines from Spin, but in reverse. Here's an example:

    [b]CON[/b]
    
      [b]_clkmode[/b]      = [b]xtal[/b]1 + [b]pll[/b]16x
      [b]_xinfreq[/b]      = 5_000_000
      
      call_out      = 1
      call_str      = 2
      call_dec      = 3
      call_hex      = 4
    
    [b]VAR[/b]
    
      [b]long[/b]  args[noparse][[/noparse]3&#093;
    
    [b]OBJ[/b]
    
      pr :  "tv_text"
    
    [b]PUB[/b] dispatcher | cmd
    
      pr.start(12)
      [b]cognew[/b](@asm_code, @args)
    
      [b]repeat[/b]
        [b]repeat[/b] [b]until[/b] cmd := args[noparse][[/noparse]0&#093;
        [b]case[/b] cmd
          call_out: args[noparse][[/noparse]1&#093; := pr.out(args[noparse][[/noparse]1&#093;)
          call_str: args[noparse][[/noparse]1&#093; := pr.[b]str[/b](args[noparse][[/noparse]1&#093;)
          call_dec: args[noparse][[/noparse]1&#093; := pr.dec(args[noparse][[/noparse]1&#093;)
          call_hex: args[noparse][[/noparse]1&#093; := pr.hex(args[noparse][[/noparse]1&#093;, args[noparse][[/noparse]2&#093;)
        args[noparse][[/noparse]0&#093;~
    
    [b]DAT[/b]
    
                  [b]org[/b]       0
                  
    asm_code      [b]mov[/b]       command,[b]par[/b]
                  [b]mov[/b]       arg1,[b]par[/b]
                  [b]add[/b]       arg1,#4
                  [b]mov[/b]       arg2,[b]par[/b]
                  [b]add[/b]       arg2,#8
                  [b]mov[/b]       counter,#0
    
    main_loop     [b]wrlong[/b]    HOME,arg1
                  [b]call[/b]      #pr_out
                  [b]wrlong[/b]    counter,arg1
                  [b]call[/b]      #pr_dec
                  [b]add[/b]       counter,#1
                  [b]jmp[/b]       #main_loop
    
    pr_out        [b]wrlong[/b]    _call_out,command
                  [b]jmp[/b]       #wait_return
    
    pr_dec        [b]wrlong[/b]    _call_dec,command
    
    wait_return   [b]rdlong[/b]    tst,command [b]wz[/b]
            [b]if_nz[/b] [b]jmp[/b]       #wait_return
    
    pr_out_ret
    pr_dec_ret    [b]ret[/b]              
    
    _call_out     [b]long[/b]      call_out
    _call_dec     [b]long[/b]      call_dec
    HOME          [b]long[/b]      1
    counter       [b]res[/b]       1
    command       [b]res[/b]       1
    arg1          [b]res[/b]       1
    arg2          [b]res[/b]       1
    tst           [b]res[/b]       1
    
    
    


    The dispatch loop just waits for the command to become non-zero, then calls the associated subroutine with the arguments passed by the assembly routine and returns the return value in the first argument. (Return values are not used in the example.) Finally, the command is cleared to signal the assembly routine that it's done.

    Notice that I did not call the pr.str routine in the example. To do so requires the hub address of a string, and this would require passing the address of a more complicated data structure in par.

    -Phil
  • ESPESP Posts: 25
    edited 2006-07-26 09:46
    Hi All,

    any way to change the forum width? Some of these threads are hard to read [noparse]:)[/noparse]

    Robin
Sign In or Register to comment.