How to pass arguments to/from assembly language?

Please, could someone give a short, simple explanation of and a simple piece of code for, how to pass several parameters to an assembly-language routine and how to get several values back from that routine? Examples in this Forum are either too complicated to follow or are just an explanation without an example. Show me how to send three values and how to get two back. Seems like this should be an obvious part of the Propeller manual, but it's not. I need to use assembly language for speed. Folks, many of us are new to assembly language on the Propeller, so simplicity and clarity help more than complexity and obscurity. Thanks. --Jon

Comments

  • 15 Comments sorted by Date Added Votes
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 21,585
    edited February 9 Vote Up0Vote Down
    It's pretty simple. Here are the steps:

    1. When you start the PASM cog, send it the address of a long parameter array. This address will appear in PAR at the assembly end. The first parameter should just be a true/false flag.

    2. At the beginning of the PASM program, program a loop that waits for the first parameter to become non-zero.

    3. When this happens, read the parameters from hub memory, i.e. at PAR + 4, PAR + 8, and PAR + 12.

    4. Do your computations, and store the result at PAR + 4.

    5. Then clear the hub memory at PAR to zero.
    ____

    6. The calling program must first store the parameters in positions 1..3 in the long array.

    7. After that, it must set position 0 to a non-zero value.

    8. Then it just has to wait until position 0 becomes zero again.

    9. At that point, it can read the result from position 1 in the array.

    -Phil
    “Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away. -Antoine de Saint-Exupery
  • JonnyMacJonnyMac Posts: 5,916
    edited February 9 Vote Up0Vote Down
    Here's a dirt-simple example following Phil's outline. Put values into param1 and param2, then a non-zero value into command. The sum will land in param1 and the difference in param2. You know the background process is done when command returns to zero.
    var
    
      long  command
      long  param1
      long  param2
                                                                     
    
    dat
    
                            org     0
    
    entry                   rdlong  hub, par                wz      ' command?
            if_z            jmp     #entry                          ' wait until !0
    
                            mov     hub, par                        ' hub = @command
                            add     hub, #4                         ' hub = @param1
                            rdlong  val1, hub                       ' val1 = param1
                            add     hub, #4                         ' hub = @param2
                            rdlong  val2, hub                       ' val2 = param2
    
                            mov     sum, val1                       ' sum = val1
                            adds    sum, val2                       ' sum = val1 + val2
    
                            mov     dif, val1                       ' dif = val1
                            subs    dif, val2                       ' dif = val1 - val2
    
                            mov     hub, par                        ' hub = @command
                            add     hub, #4                         ' hub = @param1
                            wrlong  sum, hub                        ' param1 = sum
                            add     hub, #4                         ' hub = @param2
                            wrlong  dif, hub                        ' param2 = dif
    
                            mov     val1, #0
                            wrlong  val1, par                       ' command = 0
    
                            jmp     #entry
    
    
    hub                     res     1
    val1                    res     1
    val2                    res     1
    sum                     res     1
    dif                     res     1
    
                            fit     496
    
    Of course, you have to launch the cog -- do that with:
    cognew(@entry, @command)
    
    Jon McPhalen
    Hollywood, CA
    It's Jon or JonnyMac -- please do not call me Jonny.
  • You didn't specify SPIN or C code example.

    I struggled with this for months trying to find useful examples. I can write the code but to get it into the C code was impossible.

    Anyway I figured it out and here is the C code version. I believe C code runs faster than SPIN.
    int main()
    {
      // Add two numbers.
    
      int Total, Parm1, Parm2;
      
      Total = 0;
      Parm1 = 2;
      Parm2 = 2;
      
     __asm__ volatile (
     "    mov %[T], %[P1]   \n\t"  // put parameter one into total
     "    add %[T], %[P2]   \n\t"  // add parameter two to total
          : [T] "+r" (Total)       // declare Total as output and alias it
          : [P1] "r" (Parm1),      // declare Parm1 as input and alias it
            [P2] "r" (Parm2)       // declare Parm2 as input and alias it
          );
          
      print("Total: %d, Parm1: %d, Parm2: %d\n", Total, Parm1, Parm2);
      
      while(1)
      {
        // Add main loop code here.
        
      }  
    }
    

    Thanks for playing.

    Mike

  • Many thanks fellow programmers. Your information and code examples look helpful and easy to understand. Cheers. --Jon
  • Now I need to know how to get OUT of the assembly-language routine and get back to my SPIN code without bad effects. None of the examples I've examined show how to do that in a simple way.

    I wrote a simple assembly-language program to turn on two off-board LEDs at pins P8 and P9. I set the DIRA and OUTA pins properly within the AL program. If I end the AL routine with:

    jmp $%

    the LEDS stay lit because the AL program finishes and remains in an endless loop. It doesn't return to my SPIN code. I have seen examples that use the following instructions to stop execution in the cog:

    cog ID
    cogstop ID

    In this case, the LEDs turn on for 0.5 microseconds and then turn off. I need them to remain on when execution returns to the SPIN code. Again, thanks for any help you can offer. --Jon
  • You don't "return" to your spin code from a cog running PASM. The cog running that spin code can either continue running spin code if it loops continuously or stops if it does not. Same is is true for the PASM code that was launched. Typically a cog running spin code will start one or more cogs running PASM code that perform some function (serial comms, video, etc) that the spin program needs.
    In science there is no authority. There is only experiment.
    Life is unpredictable. Eat dessert first.
  • Thanks, kwinn. OK, so these statements should do the job (ID defined within the ASM section of code):
    cog ID
    cogstop ID
    

    I appreciate your clarification. But why do the statements above cause the I/O pins to change state, but the statement
    jmp $#
    

    does not? That's what baffles me.
    Again, thanks. --Jon


  • Perhaps the over-arching problem here comes down to my interpretation of cog use in PASM. I have treated the cog PASM use much like a subroutine rather than as a separate process that runs and waits for me to send it a command to start operation. I thought I could "call" the cog PASM, run a task and then "return" to the main program. I looked at an example this morning and will try the use of a PASM loop in a cog that waits for a command and then retrieves data, performs its operations, and then waits for another command. Maybe this gets explained somewhere, but I haven't found it yet. Sadly, many of the links Google finds on the Parallax site are broken. --Jon
  • Jon,

    the difference is that when you stop a COG it will reset dira and outa and let pins float again, as opposed to stay active and just jump on itself, doing nothing but keeping alive and keeping the pin states.

    Mike
    I am just another Code Monkey.
    A determined coder can write COBOL programs in any language. -- Author unknown.
    Press any key to continue, any other key to quit

    The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this post are to be interpreted as described in RFC 2119.
  • JonnyMacJonnyMac Posts: 5,916
    edited February 11 Vote Up0Vote Down
    As Kwinn points out, the Spin cog and your assembly cog are both running. This is why Phil suggested the flag approach in the par-assigned variable: it lets you know when the assembly routine is done. Using the trivial example I posted above, you might do something like this if you want to run the assembly and the kill it when it's finished.
      pasmcog := cognew(@entry, @command)                           ' launch pasm cog
      param1  := 2                                                  ' set parameters
      param2  := 3
      command := 1                                                  ' enable pasm cog
      repeat while (command)                                        ' wait for pasm to finish
      cogstop(pasmcog)                                              ' kill pasm cog (if no longer needed)
    
    If you want the pasm cog to maintain the state of IO pins then you must leave it running.

    Another thing you can do is use the flag as a switch for what you want to do in the pasm cog -- this is why I called it command. In the examples above it's a simple true-false flag, but you could easily compare the value in command with a known set and run a routine within the pasm cog on demand. See attached demo; it illustrates the use of pasm-coded subroutines. Since it contains IO control, it must stay running.


    Jon McPhalen
    Hollywood, CA
    It's Jon or JonnyMac -- please do not call me Jonny.
  • I set a flag in my SPIN program and then start a cog that runs a short loop that tests this flag. (Files attached.) When the PASM routine detects the flag, it runs some I/O control software. If at the end of the PASM routine it jumps back to the test routine it again detects the flag, so I end up with an infinite loop. After the PASM code runs I need a way to clear the flag set earlier in the SPIN code. The cog with the PASM can continue to test the flag until the SPIN program sets it again.

    The attached files show my attempt. I need very high-speed serial output with a clock to test other equipment. All I get out is one long (0.5-sec) pulse on the clock like. I have spend days on this code and got nowhere. I'm about to switch to an Atmel or ARM-Cortex and program what I need in C.

    Thanks for any assistance you can provide --Jon

  • twm47099twm47099 Posts: 598
    edited February 12 Vote Up0Vote Down
    In your programs, you should have another flag that is set by the spin program. The spin process would delay (just keep looping) until the PASM completes its action and clears the flag.

    In the PropTool library there is an object SPI_Asm.spin that does that.

    The code below is an excerpt from that object showing how the spin "calls" the Pasm (which was started earlier). Look at the full code from the library and the corresponding demo.
    PUB SHIFTOUT(Dpin, Cpin, Mode, Bits, Value)             ''If SHIFTOUT is called with 'Bits' set to Zero, then the COG will shut
                                                            ''down.  Another way to shut the COG down is to call 'stop' from Spin.
        setcommand(_SHIFTOUT, @Dpin)
    
    PUB SHIFTIN(Dpin, Cpin, Mode, Bits)|Value,Flag          ''If SHIFTIN is called with 'Bits' set to Zero, then the COG will shut
                                                            ''down.  Another way to shut the COG down is to call 'stop' from Spin.
    
        Flag := 1                                           ''Set Flag                                           
        setcommand(_SHIFTIN, @Dpin)
        repeat until Flag == 0                              ''Wait for Flag to clear ... data is ready
        
        Result := Value
    
    
    
    PRI setcommand(cmd, argptr)
        command := cmd << 16 + argptr                       ''write command and pointer
        repeat while command                                ''wait for command to be cleared, signifying receipt
    
    

  • Thanks for that info. --Jon
  • You probably want to setup your pasm cog so that you can pass the pins to it -- hard-coding will limit your designs. The only MAX7219 code I have is in Spin so I did a simple pasm cog (attached, but not tested). Will have to find a display to test. At the moment there is a single function that will write one register using pasm. Once I get it working I'll add a command that will let me write a group of contiguous registers to the device.
    Jon McPhalen
    Hollywood, CA
    It's Jon or JonnyMac -- please do not call me Jonny.
  • The display-info text was left over from an old program. I forgot about it because it scrolled off at the top of my screen as I worked on the code. You can find working programs in my book, free at Parallax:

    https://www.parallax.com/news/2015-02-26/download-new-book-jon-titus-experiments-propeller-quickstart.

    Descriptions start in Experiment 11. Again, thanks for all your help. --Jon
Sign In or Register to comment.