Shop OBEX P1 Docs P2 Docs Learn Events
Updated pasm_toggle example — Parallax Forums

Updated pasm_toggle example

mindrobotsmindrobots Posts: 6,506
edited 2011-11-17 16:58 in Propeller 1
11-17-11 EDIT


In keeping with the spirit of Steve's series of Toggle examples, I fixed the pasm_toggle to work as the other examples do while still showing how to start a COG with PASM code and pass variables to it via a mailbox. The variable passing wasn't working in the pasm_toggle example as provided with the latest propgcc demos I have. The attached .zip pasm_toggle_updated.zip contains the code to match the other examples. My mailbox code listed in this post will become another example for 2-way C/PASM data exchange.

This thread as it started and was commented on has some valuable information from Eric that will be included in my documentation and future tutorials.

END EDIT



On my way to the library examples, I got distracted by jazzed and potatohead's discussion about using OBEX PASM routines and a "clean" interface between propgcc and PASM. I went to look at the toggle_pasm examples and (sorry Steve) saw that they really didn't show what I believe was intended. So, I rewrote them in a simple verbose style. I've included the source in the thread as well as attached a complete .zip of the demos/toggle/toggle_pasm directory.

This was developed and tested on a QuickStart as the default LMM memory model.

The C code:
/**
 * @file toggle.c
 * This program demonstrates starting a PASM COG
 * and being able to pass parameters from a C program to a PASM program
 * and from PASM back to C. 
 *
 * C to PASM Mailbox example
 *
 * WARNING: This code makes all IO pins except 30/31 toggle HIGH/LOW. Check if this is OK 
 * for the board you are using.
 *
 *
 * to use:
 * from directory containing source
 * make clean
 * make
 * propeller-load -pn -t -r toggle.elf  (where n is port #)
 *
 * Copyright (c) 2011, Steve Denson, Rick Post
 * MIT Licensed - terms of use below.
 */

#include <stdio.h>
#include <propeller.h>                    // propeller specific definitions

// the STATIC HUB mailbox for communication to PASM routine 
//
static unsigned int delay; // a pointer to this gets passed to the PASM code as the PAR register
static unsigned int pins;
static int loop_cnt;
static int pasm_done;

// C stub function to start the PASM routine
// need to be able to provide the entry point to the PASM
// and a pointer to the STATIC HUB mailbox
// the cognew function in the propeller.c library returns the COG #
//
int start(unsigned int *pinptr)
{
    extern unsigned int binary_toggle_dat_start;
    cognew(&binary_toggle_dat_start, pinptr);
}

void usleep(int t)
{
    if(t < 10)  // very small t values will cause a hang
        return; // don't bother function delay is likely enough
    waitcnt((CLKFREQ/1000000)*t+CNT);
}
// C main function
// LMM model
void main (int argc,  char* argv[])
{
    printf("hello, world!\n");            // let the lead LMM COG say hello
    delay = CLKFREQ>>1;                    // set the delay rate in the STATIC mailbox
                                        // this is actually the duty cycle of the blink 0.5 sec on, 0.5 sec off
    pins = 0x3fFFffff;                     // set the PIN mask into the STATIC mailbox
                                        // light up all pins except 30 & 31 since we don't know board config
    loop_cnt = 20;                        // number of time through the loop (20 toggles, 10 on/off cycles)
    pasm_done = 0;                        // make sure it's zero since we'll sit and wait on it to change in a few lines
    printf ("New COG# %d started.\n",start(&delay)); // start a new COG passing a pointer to the STATIC mailbox structure
    printf ("waiting for semaphore to be set by PASM code.\n");
    while (!pasm_done)
    {
      usleep(10);                        // wait for the PASM code to clear the loop counter
    }    
    printf("goodbyte, world!\n");
    while(1);                            //let the original COG sit and spin
}

/*
    +--------------------------------------------------------------------
      TERMS OF USE: MIT License
    +--------------------------------------------------------------------
    Permission is hereby granted, free of charge, to any person obtaining
    a copy of this software and associated documentation files
    (the "Software"), to deal in the Software without restriction,
    including without limitation the rights to use, copy, modify, merge,
    publish, distribute, sublicense, and/or sell copies of the Software,
    and to permit persons to whom the Software is furnished to do so,
    subject to the following conditions:

    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    +--------------------------------------------------------------------
*/ 

The PASM
{{
toggle.spin
Propgcc - PASM toggle demo

Simple PASM routine to demonstrate interaction between PASM subroutines an PROPGCC main program.
The code running in the C address space to talk to exchange data values with PASM code running
in a COG.

The C program has visibility/access to the mailbox variables as normal C variables.
The PASM program has visibility/access to the mailbox variables through the PAR register
initialized by the COGNEW and hte RDLONG/RDWORD/RDCHAR and WRLONG/WRWORD/WRCHAR instructions.  

C program starts the PASM routine via cognew() function, passing strat address and PAR register value.
PAR register should be the address of a STATIC data area of LONGs in the C program.

For this example:

PAR ->  static unsigned int delay;
        static unsigned int pins;
        static unsigned int loop_cnt;
        static unsigned int pasm_done;

The first three variables are used as input to the PASM routine, the last is used to act as a semaphore
back to the C routine.

This could have been written as more efficient PASM code but for the examples, I was going for maximum clarity at this point.

Copyright (c) 2011, Steve Denson, Rick Post
MIT Licensed. Terms of use below.

}}

pub start(pinptr)                                                  
    cognew(@pasm, pinptr)

dat             org 0

pasm
                mov      mailbox_ptr,par         ' save the pointer to the STATIC parameter (HUB) memory area
                                                 ' the PAR register is initialized by the cognew() function and is a pointer to
                                                 ' the first STATIC int declared in the C code as the mailbox
                                                 ' mailbox_ptr will be changed as the code executes. You can reload
                                                 ' the initial pointer from PAR if you ever need it to point to
                                                 ' the start of the mailbox again
                rdlong   waitdelay, mailbox_ptr  ' read the wait delay from HUB - it is initialized by the C program
                                                 ' in C program: delay = CLKFREQ>>1;
                add      mailbox_ptr,#4          ' point to the next LONG in HUB
                rdlong   pins,mailbox_ptr        ' the caller's PIN mask  as initialized in the C program
                                                 ' in C program: pins = 0x3fffffff;
                add      mailbox_ptr, #4         ' point to the next LONG (4 bytes each)
                rdlong   loopcounter,mailbox_ptr ' set the loop count as provided by the C program
                                                 ' in C program: loop_cnt = 20;
                add      mailbox_ptr, #4         ' point to the next LONG which is the semaphore we are setting when done

                mov      dira, pins              ' set pins provided by C program to OUTPUT
                mov      nextcnt, waitdelay
                add      nextcnt, cnt            ' best to add cnt last
:loop
                xor      outa, pins              ' toggle pins
                waitcnt  nextcnt, waitdelay      ' wait for user specified delay
                djnz     loopcounter,#:loop      ' loop until the C provided counter hits zero
                
                mov      done_flag,#1            ' set the semaphore to one
                wrlong   done_flag, mailbox_ptr  ' and save it back into hub memory via the ptr provided by the C program
                                                 ' in C program: while(!pasm_done) to test for update from PASM
                jmp     #$                       ' to infinity and BEYOND!!

' these do not need to be in any particular order or have particular names. There is no relationship between these
' local copies of the C variable except when you create via the PAR register and HUB instructions
' there is no address resolution or linkage done by propgcc or the loader
'
mailbox_ptr     long    0                       ' working ptr into the HUB area - reload from PAR if needed
pins            long    0                       ' local copy of the user's PIN mask
waitdelay       long    0                       ' local copy of the user's delay
loopcounter     long    0                       ' local copy of the user's loop counter
done_flag       long    0                       ' local copy of the semaphore to return to the C program
nextcnt         long    0                       ' local variable to save target value from waitcnt

{{

MIT Licensed.

+--------------------------------------------------------------------
 TERMS OF USE: MIT License
+--------------------------------------------------------------------
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+--------------------------------------------------------------------
}}

This was really a great learning experience since I haven't really written PASM yet and certainly haven't played with the PAR register. It has lead to a number of questions.

Alpha Tester question: If I remove the usleep(10) from the first while and make it an empty statement, the C program hangs after starting the PASM code and never sees the seamphore change values. As the code currently is, it works fine. Is this a propgcc problem with empty loops? Optimization gone bad? Or am I expecting behavior in an empty loop I shouldn't be getting (programmer error)?

General propgcc questions:

1) Is a STATIC declaration guaranteed to be in HUB on all memory models (except -mcog)? If not, then this needs to be documented and the examples written differently for different memory models.

2) Where does the 'binary_toggle_stat_start' tag come from? I looked lots of places and never saw it get generated but it certainly gets resolved at link time. This needs to be documented for those more ambitious coders with multiple PASM routines.

3) From a coding style perspective, should the mailbox really be a STRUCT? I tried to keep it simple for a SPIN/PASM programmer transitioning to propgcc (I'm not up to talking about propg++ implementations yet).

4) In the toggle.spin program, I'm assuming the two lines of SPIN at the start are just to make the file a valid SPIN program so it will compile.

I'm sure my PASM code can be tightened up but for what it's doing, again, simple was the key (plus it is my first PASM code).

I think this is worth wrapping a tutorial around for our friends transitioning from SPIN/PASM and others learning propgcc.

Sorry it's so long but I thought there might be folks that just wanted to look at the code without having to download and unzip the thing.

Feedback & criticism appreciated - I'm just stumbling along here. :lol:

Comments

  • ersmithersmith Posts: 6,100
    edited 2011-11-16 17:14
    That's a very nicely commented example, Rick -- thanks for sending it!
    mindrobots wrote: »
    Alpha Tester question: If I remove the usleep(10) from the first while and make it an empty statement, the C program hangs after starting the PASM code and never sees the seamphore change values. As the code currently is, it works fine. Is this a propgcc problem with empty loops? Optimization gone bad? Or am I expecting behavior in an empty loop I shouldn't be getting (programmer error)?
    It's programmer error, but a subtle one. The problem is that the compiler doesn't know that the pasm_done variable can be unexpectedly changed, and so it optimized the loop into an endless loop. What it did would be correct if no other cog was running, or if pasm_done wasn't the cog communication variable. We have to add the C volatile keyword to the declaration of pasm_done to tell it that the variable could be changed by something outside of the C code (the PASM code, in this case).
    1) Is a STATIC declaration guaranteed to be in HUB on all memory models (except -mcog)? If not, then this needs to be documented and the examples written differently for different memory models.
    A static declaration is treated the same as any other variable declaration: in -mcog, -mlmm, and -mxmmc it will go in HUB memory, in -mxmm it will go in external memory. To force it in hub memory in -xmm mode you can add __attribute__((section(".hub"))) to it.
    2) Where does the 'binary_toggle_stat_start' tag come from? I looked lots of places and never saw it get generated but it certainly gets resolved at link time. This needs to be documented for those more ambitious coders with multiple PASM routines.
    I believe it comes from the OBJCOPY command that created the toggle_firmware.o (in the Makefile). You're right, this should be better documented.
    3) From a coding style perspective, should the mailbox really be a STRUCT? I tried to keep it simple for a SPIN/PASM programmer transitioning to propgcc (I'm not up to talking about propg++ implementations yet).
    It definitely should be a struct; otherwise the linker might not necessarily keep the variables together in memory. For example, if you add an initialization to one of them that one will end up in the .data section (for initialized data), while the others will be in .bss (for uninitialized data).
    I'm sure my PASM code can be tightened up but for what it's doing, again, simple was the key (plus it is my first PASM code).

    Have you looked at the toggle_gas demo? Using the GNU assembler instead of PASM it's even easier to interface the assembly code with C code.

    Eric
  • mindrobotsmindrobots Posts: 6,506
    edited 2011-11-17 06:43
    Eric,

    Thank you for the excellent clarifications. I have massive gaps in my C knowledge, so it's hard for me to identify "Alpha" problems versus "Rick" problems. Hopefully my value will be found in providing well documented, simple examples and explanation for people transitioning to propgcc from other places.

    I'll fold your suggestions/corrections into my examples. There's a wealth of information that needs to get captured in the documentation and tutorials.

    I did look at toggle-gas and it is much simpler and better integrated. Still, there will be the need for incorporating PASM with "clean" interfaces.

    I'll also make my toggle work in a similar fashion to Steve's other toggle programs so from the blinking light standpoint, they look the same and really do just show the differences of getting the same results from several different implementations.
  • mindrobotsmindrobots Posts: 6,506
    edited 2011-11-17 08:41
    Eric,

    I just finished some testing with VOLATILE.

    If I changed the variable as it is in my original C program:

    static int pasm_done;

    to

    static volatile int pasm_done;


    it still got optimized away (as best I could tell).

    Once I built it into a STRUCT:
    typedef struct 
    {
    unsigned int delay; // a pointer to this gets passed to the PASM code as the PAR register
    unsigned int pins;
    int loop_cnt;
    volatile int pasm_done;
    } mbox_type;
    
    static mbox_type mb;
    

    then it did not get optimized away and the semaphore works as expected with an empty while loop.

    I'm not sure if this is working as you had expected but this is what I've observed in testing. I need to do more testing with a single volatile variable. That should work as you described without having a single variable wrapped inside a structure.


    In digging around more and playing with volatile, I stumbled upon signal.h which started me to thinking about standard support for COG to COG signals and LOCK support. Is signal.h being implemented? LOCK support? (I come from an old multi-processor mainframe background, so my mind often happily wanders off to inter-processor communications and data structure locking.)

    Today, I'm off to gas_toggle to see what I can screw up there!! :lol:
  • ersmithersmith Posts: 6,100
    edited 2011-11-17 09:31
    mindrobots wrote: »
    I just finished some testing with VOLATILE.

    If I changed the variable as it is in my original C program:

    static int pasm_done;

    to

    static volatile int pasm_done;


    it still got optimized away (as best I could tell).
    I don't think the pasm_done reference will be optimized away (at least, it didn't in the tests I did) but some of the other variable references may be :-(. I forgot that "volatile" may be necessary for writes as well as reads, so all of the variables that are used for inter-cog communication should be marked as volatile.

    If you put everything in a struct you can just mark the whole struct as volatile, rather than having to put volatile on each member, which is another benefit of structs.
    In digging around more and playing with volatile, I stumbled upon signal.h which started me to thinking about standard support for COG to COG signals and LOCK support. Is signal.h being implemented? LOCK support? (I come from an old multi-processor mainframe background, so my mind often happily wanders off to inter-processor communications and data structure locking.)

    signal.h is implemented, but cogs can't send signals to other cogs. The only way to send a signal is with the raise function, and it just causes the signal to happen on the current cog.

    Low level LOCK support is provided by __builtin_propeller_lockset, __builtin_propeller_lockclr, __builtin_propeller_locknew, and __builtin_propeller_lockret, which act like the PASM instructions (except instead of setting the carry they return -1 or 0). We're using one lock (stored in the global __C_LOCK) to provide higher level
    inter-cog (or rather inter-thread) communication. See sys/thread.h, and pthread.h in the current repository... I'm not sure if it's made it to the distribution yet.

    In LMM mode pthreads will use as many cogs as are available to run threads. In XMM mode only one cog is used for threads (that's a limitation of the current caching model; we may be able to work around this in the future). If there are more threads than cogs, then threads will have to share cogs.

    The thread model is cooperative, so threads have to call pthread_yield, pthread_mutex_lock, or sleep to give up the processor.

    Eric
  • mindrobotsmindrobots Posts: 6,506
    edited 2011-11-17 10:27
    This looks much better, makes more sense and works fine:
    typedef struct 
    {
    unsigned int delay; // a pointer to this gets passed to the PASM code as the PAR register
    unsigned int pins;
    int loop_cnt;
    int pasm_done;
    } mbox_type;
    
    static volatile mbox_type mb;
    

    I ended up changing the start function to this:
    int start( volatile unsigned int *pinptr)
    {
        extern unsigned int binary_toggle_dat_start;
        cognew(&binary_toggle_dat_start, (void *)pinptr);
    }
    

    to get rid of two warning about variable type mismatches and "volatile" being discarded on the function call.

    You were correct about binary_toggle_dat_start being generated out of objcopy. You can find the name when you dump the symbol table (-t) with objdump. I'll document this so people can find the name on their own PASM code entry points.

    Thanks for all the help! You're a wealth of information!
  • mindrobotsmindrobots Posts: 6,506
    edited 2011-11-17 12:17
    Updated code!

    Based on my discussions with Eric and my original intent to provide a working pasm_toggle that performed as Steve's other toggle demos do, I now have the corrected and updated code. The .zip for this is attached to the first post in this thread as pasm_toggle_updated.zip. The code is listed here in case anyone prefers to see the code without having to download and unzip.

    The C program:
    /**
     * @file toggle.c
     * This program demonstrates starting a PASM COG
     * passing parameters from a C program to a PASM program.
     *
     * C to PASM Mailbox example
     *
     * WARNING: This code makes all IO pins except 30/31 toggle HIGH/LOW. Check if this is OK 
     * for the board you are using.
     *
     *
     * to use:
     * from directory containing source
     * make clean
     * make
     * propeller-load -pn -t -r toggle.elf  (where n is port #)
     *
     * Copyright (c) 2011, Steve Denson, Rick Post
     * MIT Licensed - terms of use below.
     */
    
    #include <stdio.h>
    #include <propeller.h>                    // propeller specific definitions
    
    // the mailbox structure (STRUCT) for communication to PASM routine 
    //
    typedef struct 
    {
    unsigned int pins; // a pointer to this gets passed to the PASM code as the PAR register
    unsigned int wait_time;
    } mbox_type;
    
    // the mailbox is defined as VOLATILE since these variable are being used to communicate with an execution thread
    // outside the scope of this C program (an independent COG running PASM in this case. They are read/write variable
    // whos values could be changed outside the control of this program. VOLATILE also prevent the gcc compiler from 
    // optimizing away these variables if it sees cases where it might optimize and unchanging variable. This will be 
    // made clear in a future example.
    
    static volatile mbox_type mb;
    
    // this constant is used in the whiel loop below to controll the highest flash rate
    #define MIN_GAP 400000
    
    // C stub function to start the PASM routine
    // You provide the entry point to the PASM
    // and a pointer to the STATIC HUB mailbox
    // The cognew function in the propeller.c library returns the COG #
    //
    int start( volatile unsigned int *pinptr)
    {
        extern unsigned int binary_toggle_dat_start;
        cognew(&binary_toggle_dat_start, (void *)pinptr);
    }
    
    // C main function
    // LMM model
    void main (int argc,  char* argv[])
    {
        printf("hello, world!\n");                            // let the lead LMM COG say hello
        mb.wait_time = CLKFREQ;                                // set the delay rate in the STATIC mailbox
                                                            // this is actually the duty cycle of the blink 0.5 sec on, 0.5 sec off
        mb.pins = 0x3fFFffff;                                 // set the PIN mask into the STATIC mailbox
                                                            // light up all pins except 30 & 31 since we don't know board config
                                                            // The PASM code is written to read these variables into local PASM 
                                                            // variable when it initially start up.
        printf ("New COG# %d started.\n",start(&mb.pins));  // start a new COG passing a pointer to the STATIC mailbox structure
        
        while(1) {                                            // start infinite loop to toggel pins at a variable rate
          sleep(2);                                            // change the flash rate every 2 seconds
          mb.wait_time =  mb.wait_time >> 1;                // divide the current wait time by 2 (shift right 1 bit)
                                                              // this changes the C variable in the mailbox
                                                              // the PASM code will only notice the change if it does a RDLONG of the 
                                                              // variable from time to time - the PASM code provide does this through each
                                                              // toggle loop
          if (mb.wait_time < MIN_GAP)                        // don't let it get too fast - can't see the blinks anymore
             mb.wait_time = _clkfreq;                        // start back at a 1 second rate if we got too fast
        }   
    }
    
    /*
        +--------------------------------------------------------------------
          TERMS OF USE: MIT License
        +--------------------------------------------------------------------
        Permission is hereby granted, free of charge, to any person obtaining
        a copy of this software and associated documentation files
        (the "Software"), to deal in the Software without restriction,
        including without limitation the rights to use, copy, modify, merge,
        publish, distribute, sublicense, and/or sell copies of the Software,
        and to permit persons to whom the Software is furnished to do so,
        subject to the following conditions:
    
        The above copyright notice and this permission notice shall be
        included in all copies or substantial portions of the Software.
    
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
        EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
        MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
        IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
        CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
        TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
        SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
        +--------------------------------------------------------------------
    */ 
    

    The PASM:
    {{
    toggle.spin
    Propgcc - PASM toggle demo
    
    Simple PASM routine to demonstrate interaction between PASM subroutines and a PROPGCC main program.
    The code running in the C address space to exchange data values with PASM code running
    in an independent COG.
    
    The C program has visibility/access to the mailbox variables as normal C variables.
    The PASM program has visibility/access to the mailbox variables through the PAR register
    initialized by the COGNEW and hte RDLONG/RDWORD/RDCHAR and WRLONG/WRWORD/WRCHAR instructions.  
    
    C program starts the PASM routine via cognew() function, passing start address and PAR register value.
    PAR register should be the address of a STATIC VOLATILE data area of LONGs in the C program.
    
    For this example:
    
    PAR ->  static volatile unsigned int pins;
            static volatile unsigned int wait_time;
    
    The two C variables are used as input to the PASM routine. The C program can change these after it starts the PASM
    code but the PASM code will only see their values when it reads them from HUB memory. If the program doesn't read them again after
    initially starts, the changes made  by the C code will never be seen.
    
    This could have been written as more efficient PASM code but for the examples, I was going for maximum clarity at this point.
    
    Copyright (c) 2011, Steve Denson, Rick Post
    MIT Licensed. Terms of use below.
    
    }}
    
    pub start(pinptr)                                                  
        cognew(@pasm, pinptr)
    
    dat             org 0
    
    pasm
                    mov      mailbox_ptr,par         ' save the pointer to the STATIC parameter (HUB) memory area
                                                     ' the PAR register is initialized by the cognew() function and is a pointer to
                                                     ' the first STATIC int declared in the C code as the mailbox
                                                     ' mailbox_ptr will be changed as the code executes. You can reload
                                                     ' the initial pointer from PAR if you ever need it to point to
                                                     ' the start of the mailbox again
                    rdlong   pins, mailbox_ptr       ' read the pin mask from HUB - it is initialized by the C program
                                                     ' in the C program, this is initially set as $3FFFFFFF
                    add      mailbox_ptr,#4          ' point to the next LONG in HUB
                    mov      dira, pins              ' set pins as provided by C program to OUTPUT
    :loop           rdlong   waitdelay, mailbox_ptr  ' get the waitdelay from HUB in case the C program changed it
                    mov      nextcnt, waitdelay      ' calculate the wait
                    add      nextcnt, cnt            ' best to add cnt last
                    xor      outa, pins              ' toggle pins
                    waitcnt  nextcnt, waitdelay      ' wait for user specified delay
                    jmp      #:loop                  ' loop until the C provided counter hits zero
    
    
    ' these do not need to be in any particular order or have particular names. There is no relationship between these
    ' local copies of the C variable except when you create via the PAR register and HUB instructions
    ' there is no address resolution or linkage done by propgcc or the loader
    '
    mailbox_ptr     long    0                       ' working ptr into the HUB area - reload from PAR if needed
    pins            long    0                       ' local copy of the user's PIN mask
    waitdelay       long    0                       ' local copy of the user's delay
    nextcnt         long    0                       '
    {{
    
    MIT Licensed.
    
    +--------------------------------------------------------------------
     TERMS OF USE: MIT License
    +--------------------------------------------------------------------
    Permission is hereby granted, free of charge, to any person obtaining
    a copy of this software and associated documentation files
    (the "Software"), to deal in the Software without restriction,
    including without limitation the rights to use, copy, modify, merge,
    publish, distribute, sublicense, and/or sell copies of the Software,
    and to permit persons to whom the Software is furnished to do so,
    subject to the following conditions:
    
    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    +--------------------------------------------------------------------
    }}
    

    Thanks for playing along and being tolerant.

    If this was easy, even I could do it!! :lol:
  • ersmithersmith Posts: 6,100
    edited 2011-11-17 16:58
    Thanks for the code, Rick! I'll update our pasm_toggle example with it.

    There is one small bug: the start function is missing a return statement.
    Also, it might also be better to declare binary_toggle_data_start as an array of unsigned ints,
    since in a way it is really an array of ints (the instructions for the cog) and then you don't have to remember to put the & to take it's address:
    int start(unsigned int *pinptr)
    {
        // The label binary_toggle_dat_start is automatically placed
        // on the cog code from toggle.dat by objcopy (see the Makefile).
        extern unsigned int binary_toggle_dat_start[];
        return cognew(&binary_toggle_dat_start, pinptr);
    }
    

    Eric
Sign In or Register to comment.