Ihave been playing around with the P1 for a few years now, programming it exclusively in C. I decided to finally take a look at Spin, mostly on a whim. The first thing that stood out was that when you called cognew to start a process in a separate cog, you are able to have arguments. When programming in C, using SimpleIDE and the SimpleTools.h library, the cognew does not allow your functions to take any arguments.
One of the first bits of example code in either language is a multicore version of the blinking light "Hello World" program. The C version has two separate blink functions with the delay times hard coded. The Spin version has the blink method take several arguments, including pin number, and the counter ticks for the delay. So the same method can be used for both cogs. There seems to be no way to do this in C or C++.
I never understood why the C version of cognew does not allow your functions to take arguments. I thought it must be using function pointers, which allow you to pass arguments to the function being pointed to. Seeing the cognew in Spin makes me even more confused as to why the C version is that way.
The first parameter is the address of the code to execute (that's in a DAT block), and the second is the address of the hub variable that serves as the "mailbox" (this is called par in the PASM code). We can access other hub variables offset from par.
There are still just two arguments: the first one is the method to run (along with its arguments, if any), and a pointer to stack space. In this second case we're actually starting another Spin interpreter cog and we have to point at the starting method (which can have parameters). For a manually-launched Spin interpreter, we have to provide a stack (array of longs).
You need to use cogstart, instead. I believe cognew resembles the Spin counterpart to start a cog code. The Spin compiler can understand if you are specifiying a Spin method or Cog code and act accordingly, in C is not possible (unless I'm missing something) because it can't know if the argument is a function or a cog code (it is always an address...).
In C it is also a bit more complicated than Spin, because the argument is a pointer (to void), you can't specify the parameters list, so you need to be aware of this and pass the variables accordingly.
@macca Thanks for the help! Your answer is more along this line of what I was thinking. It looks like cog_run is the function I was thinking of in C. So in Spin it is cognew and in C it is cog_run(). Sorry for the confusion. The description of cog_run() is below.
The example you shared uses arduino IDE names such as pinMode and digitalWrite. I am using SimpleIDE with the simpletools.h header. The C reference does not seem to include cogstart, but I looked at the source code for the cog_run() function and it calls cogstart. I changed the functions in your example to match the names in simpletools.h
I looked up the source code for cog_run() and it calls the function cogstart(). Unfortunately, only cog_run() is documented in the simpletools.h doxygen HTML page included in the SimpleIDE Learn folder. I will have to dig a little bit see if I can find more information.
I didn't like the simplicity of Simple very much and made my own C++ HAL for the Propeller many years ago. You may find PropWare::Runnable interesting for your use case: -develop/classPropWare_1_1Runnable.xhtml
It may be that installing all of PropWare on your system isn't worth the hassle if you're happy with your current toolset, but you could certainly pull specific pieces out of PropWare that interest you.
I want to use P2 as a spacecraft simulator student project for an engineering class. However, I don't have time to teach spin2. The point of the class is the spacecraft systems, not the programming required to tie it all together.
Edit: I've looked into propware and libarduino, but I couldn't get them to work with C in flexprop/P2. At the moment I'm really just stuck trying to get I2C working with some arduino libraries (INA219, for example).
Programmers can certainly succumb to "challenge accepted" moments, and PASM is one of those. Don't worry, though,I've taken pains to break down every line of the Assembly in this tutorial and try to explain it in simple terms.
You'll notice terms here like "instruction" and "mnemonic", instructions are basically just numbers which tell thePropeller chip exactly what it should be doing at any one moment. Because they're long, 32-bit, numbers nobodycould be expected to remember what number correlates to what action against what register or RAM address... so we use mnemonics.
A mnemonic is simply a much more memorable name for a particular assembly instruction, register or modifier. It convertswhat would be a completely incomprehensible number into something slightly less daunting without stripping youof the power to tell the microprocessor exactly what it should be doing.
Like all languages, the canonical way to get started is with a simple and easy blink example. Let's have a lookat one in PASM. What we'll actually see is a small SPIN program loading up a new Cog with the PASM we want to run.All the code you run on a Propeller will start with a Cog running SPIN.
Whew! That's a lot to take in. While at first glance the PASM code may look like incomprehensible nonsense,it's actually a lot easier to understand than it looks. Assembly doesn't have many of the constructs we takefor granted in most high level programming languages, and that means it doesn't have those complexities either.
Most of these Assembly commands are simply moving a number from one place to another, or performing an operationupon two memory locations. Once you get a simple Blink program running, it becomes easier to experiment with howother instructions affect, for example, your Pin value.
This is because PASM is wholly different to SPIN in how it operates, and is much more explicit about how memory isused. SPIN needs a stack to store various snippets of information as it goes about its business, and what it storesis completely hidden from the programmer, it's a high-level language.
The second parameter in this instance becomes the read-only parameter passed into the new Cog. You can specify asingle number, a complicated 32-bits long bitfield, or just give it the memory address for a handful of parametersstored in the Hub. This is all far too advanced for our first blink, though, so we'll stick with 0.
All PASM is stored within the DAT section of its parent SPIN program. This is the easiest place to store PASM sourcewhich everyone can see and modify, but it's not the only way. You could use an array of longs to storecompiled PASM instructions, or you could create PASM instructions on the fly ( please don't! for your own sanity! ).
The blink part is a label, this is a little note to the compiler telling it that we want to keep track of thispoint in the program so we can find it later. This label can be passed into cognew to tell it where to locate our PASM. It can also be jumped to in PASM itself, you'll see this later.
Next comes the actual instruction: mov dira, Pin. The mov part is a simple mnemonic, a text name for a numericalinstruction that the Propeller can understand. If you hadn't guessed, it means "move" although it actually copies.
The rest of the instruction is made up of our destination, dira which is the address ( not the value ) of theDIRA register, the very same one we use in SPIN. And, finally, the address of our source Pin which is a 32bit integer containing the pin mask we want to toggle.
Next up is a Hub access instruction. Propeller Assembly includes special instructions for accessing Hub memory.This particular one, rdlong, reads a long ( a 32bit integer ) into its target from a source memory location.
In this instance Delay is our target, a long we've reserved for our use, and the source is #0 which is aliteral memory address. So, we're loading Delay from Hub memory location 0. This just so happens to be where thePropeller keeps the CLKFREQ value, which is the number of clock ticks in a second. Using this, we can create a 1secdelay later.
The waitcnt instruction in PASM will wait until the cnt register equals the value we give it. We're passingit the value of Time for its first wait, if this value simply equalled the system counter then the very act ofcalling waitcnt, which itself takes time, will cause us to miss the value we're looking for.
Imagine I gave you a piece of paper with the number 30 on it, and told you at exactly 30 seconds pastto wait until the clock next shows 30 seconds. By the time I've finished telling you what to do it's going to beat least 32 seconds past- so you'll wait nearly a whole minute instead of the 30 seconds I wanted.
The :loop prefix on this line is another label. Using ':' to prefix a label is a handy way of making itobvious that it's a label, and not a variable, register address or instruction mnemonic. We've labelled thisline because, in order to loop, we want to jump back to it later.
res is a directive, not an instruction, this means it's evaluated by the compiler and never run as PASM. Uponevaluation it just leaves gaps in the Cog memory and gives them the friendly names, symbols, which we can referto them by in our code.
This final directive isn't essential, specially when we're dealing with such small amounts of PASM code, but it'suseful to remember. fit is another instruction to the compiler, it simply asks "does all the PASM we've writtenactually fit into Cog RAM?"
Well, that was an explanation marathon. Go and cool off your brain for a minute, and then get ready to compileand run your code. For this example you can use the same layout we used in the SPIN blink example:
3a8082e126