Spin to C/C++ converter version 0.98
ersmith
Posts: 6,094
I've just released version 0.98 of spin2cpp (see http://code.google.com/p/spin2cpp). This version has a better preprocessor (it now understands #include, #undef, #elseifdef, and #elseifndef), more consistent error reporting, and further improvements to the Catalina C output. Catalina can now compile spin2cpp output for the unmodified FullDuplexSerial object and the various floating point objects.
Bug reports are very welcome. I think spin2cpp is getting pretty mature now (particularly when used with PropGCC) and should be able to handle most objects that are found in practice. If you have some Spin code that spin2cpp doesn't work on, please let me know and I'll try to fix it.
Eric
Bug reports are very welcome. I think spin2cpp is getting pretty mature now (particularly when used with PropGCC) and should be able to handle most objects that are found in practice. If you have some Spin code that spin2cpp doesn't work on, please let me know and I'll try to fix it.
Eric
Comments
For the beginners, this works in the Command Prompt window, if you are using Windows. You have to have a good understanding of how to set up your paths, if you will be using the program as is. I did the Hello.spin conversion to see how it works, make sure you have all the necessary files in the dir that you will be compiling the example. After viewing the hello.cpp file, I noticed there is a lot of stuff going on under the hood of the spin2cpp program. The hello.cpp file also shows how objects are being used, which is a good learning experience.
Since I use SimpleIDE, a lot, I tried to get the resulting code to compile within the SimpleIDE program. Sorry to say that I did not get it to work. The following errors showed up: I thought I had all the necessary files in the dir for SimpleIDE to work with. I did not try the command line version of PropGCC to achieve the same thing. Once I figure out how to get this to work with SimpleIDE I may try to do a conversion of some of the larger Spin programs that I have. Again, a very good teaching tool, so far.
Ray
You will have to add the FullDuplexSerial.cpp, or whatever it is called, to the project.
Or on the command line add FullDuplexSerial.cpp to the end of that compilation command.
The problem is that we have to add all of the files that spin2cpp produces, not just the top level "hello.cpp". In your example I think you need FullDuplexSerial.cpp to be added to the SimpleIDE project. You can get a list of these files on the command line by adding the "--files" option to spin2cpp.
I added some preliminary spin2cpp support to SimpleIDE a while back, but Steve has had a whole lot of more important things to work on and he hasn't been able to get to integrating that into the main SimpleIDE release yet.
In the meantime, an even easier way to do the complete conversion is to use the --elf option of spin2cpp. On the command line type: and spin2cpp will automatically invoke propeller-elf-gcc for you with the "-O" option and produce the output in hello.elf. In order for this to work propeller-elf-gcc will have to be on the Windows PATH, which I think it should be in the default SimpleIDE installation.
Eric
Ray
spin2cpp just translates Spin to C++ directly. The original Hello.spin calls FullDuplexSerial for I/O, and so that's what the translated Hello.cpp does. There is no real equivalent to cout/cin in Spin -- the language has no standard input/output facilities, nor really a standard library.
Eric
Wow, Eric! I'm seriously impressed!
Your example works a treat. Even more impressive is that a native C version of Fibo compiles down to a code size only a few hundred bytes smaller than the version you automatically generate from Spin! **
The native C version also runs faster (around 25% when using CMM) but to be honest I'm not exactly sure why that should be - the fibo function is so trivial I would have expected them to be about the same no matter how the function is generated. I'll investigate further when I get some time.
But you are definitely onto something here. Yet another potential avenue to help Spin users migrate to C!
Ross.
** Actual code sizes (compiled with Catalina 3.7 using -Dprintf=t_printf to prevent Catalina pulling in the stdio I/O functions): native C = 1192 bytes, C generated from Spin to Spin2cpp = 1760 bytes.
I suspect the speed issue is because spin2cpp generates some redundant initializations -- for example in the generated Fibo program there's a: even though "result" is never actually used. I'll admit to some laziness there -- I was counting on the compiler to eliminate dead code for such cases, but I should probably do a pass to eliminate obviously dead code from the generated C++ before writing it out.
Cheers,
Eric
In such a small function, that would probably be enough to do it - LCC does not eliminate unused variables. This particular case would be easy to detect and I could add a check that would do so - I'll look into doing that when I have time.
SimpleIDE Spin mode : 384 bytes
Propeller Tool : 407 bytes
SimpleIDE CPP mode : 861 bytes (This is the size that spin2cpp produced)
I was expecting that the file size of the PropGCC C++ would be larger, but the real surprise was that the file size that the Propeller tool produced was bigger than what SimpleIDE in Spin mode produced. I am guessing that since SimpleIDE uses some program from BST, that may have something to do with it, not sure what.
I guess probably the next experiment would be is to clean up the .cpp result by inserting the PASM code as inline, and see if that reduces the .cpp code size, and by how much. Although, I did notice this in the .cpp program: This kind of looks like inline asm (binary equivalent), but I am not sure.
Ray
On my PropGCC wishlist is the ability to have inline PASM. I wonder if it would be possible to have a preprocessor that scans the CPP file and converts the PASM to binary, and then it's compatible?
spin2cpp can actually do this, since it supports inline C code. If you have a comment that starts with {++ then it's passed through as verbatim C code.
For example, you could do an LED toggle program like this:
The result after spin2cpp --ccode led2.c is reproduced below. I see that there is still some cleanup possible -- the #defines and thisobj declarations aren't necessary if {++!nospin} was given -- but overall it's not too bad, I think.
(Ok, I think I see it supports GNU inline assembly, but not native PASM)
This looks like a nice way around that though.
Right -- the GCC inline assembly uses the GAS assembler, which is just enough different from PASM that it's a pain to use existing PASM code. But if you're writing code from scratch the GAS inline assembly is quite powerful -- it has ways to interface with the C code directly so you can pass parameters to the assembly and even have the compiler optimizer interact (in very constrained ways) with the assembly.
I see the spin2cpp inline C code as a convenient way to convert existing PASM, or to provide drivers that can compile either to C or to Spin.
Eric
Nice work. Inline pasm is going to be a great addition. I like the way you can now output C or C++ - that means you can use Catalina or GCC.
I've been working my way through C tutorials and sometimes the easiest answer is to run some Spin through your program and see what comes out!
I was wondering if you have some thoughts about structures in C? One very useful thing in any language is to return multiple things from a subroutine. In Spin and early versions of Basic you declare a global variable but in C I understand the better solution is to get pointers to the variables and then pass the pointer, and 'unwrap' that inside the function. But I think structures can make that simpler - eg you can have an integer, and a long, and a string array, and you can bundle that up into a structure and pass the structure and then modify one or more of the values inside the function.
I'm not entirely sure if a structure can work in Spin though, as I think the compiler would go and put the longs all in one group and the bytes for an array in a different group and they may not be contiguous. But maybe you can force things to stay in groups - perhaps with a big DAT section? Or maybe it does not matter that they are not contiguous in hub ram - all that matters is that as an abstract group they all stay together?
Thinking it through, maybe Spin has more difficulty with structures than C. I guess thinking in reverse, is there anything in Spin2C that outputs a structure?
But, you can do structures in Spin with some work...
In VisualSpin, I have structures to represent forms and buttons and text and such...
Alignment is a major issue though if you want to have byte and word types...
Here's a "form" structure with bytes and words in it. The words are pointers to strings...
If everything is the same type, like this button structure, then it's a lot easier:
C is a lot nicer because I could do things like buttonMsgBoxOK.left=5 to change the position of the button...
In Spin, I have to hard code it byte[@buttonMsgBoxOK+3]:=5
Right. Traditionally in C you group related items together in a structure, and then pass a pointer to the structure to functions (a pointer because it is faster; if you pass the structure instead then it has to be copied into a temporary space on the stack, which might be OK if you need to modify the structure inside the function and preserve it in the caller, but is usually more overhead than necessary).
Spin really doesn't have structures. I guess the closest thing would be an array of longs, with each one representing a distinct item. You could also consider all the variables inside an object as belonging to a structure, since different instances of the object can have different values of the variables.
spin2cpp outputs all the variables of an object together as a structure (or class, for C++ output, but a class is just a fancy structure :-)). This allows us to guarantee the layout of the variables in memory, since otherwise the C compiler is allowed to re-arrange where the variables are in memory. A lot of Spin programs make assumptions about how things are stored in memory and on the stack, so we have to carefully arrange things in the output C code to make them work.
Eric
It's coming. I had already started the spin-side branch (v0-8-x) and it was very before you sent me the v0-7-x diffs. Certain other things are in the way now, but they should be resolved in a few weeks so that I can focus on more important things like spin2c/cpp support.
What memory model ? COG ?
As I recall SimpleIDE only counts program size to be consistent with BSTC.
They are very close. GCC users expect GAS syntax.
-Tor
I ran it with all three Memory Models: COG, LMM, XMMC. The LED flashed three to four times faster in these modes, the Spin mode(s) were much slower, LED flashing. The significance of this, is the spin2cpp conversion missing something, or is SimpleIDE CPP mode missing something, or is this just a weird coincedence?
To bad there are no more PASM examples that could be run to double check the conversion method. I think something like flashing two LEDs at different rates, running on two different COGS, in PASM might be something of interest. Anybody have some code for that kind of example? The QuickStart board would be my choice.
Ray
The PASM conversion can be tested by doing spin2cpp --dat, which just compiles the DAT section, and comparing the output to bstc's"bstc -c". So far all the examples I've tried have been identical, but there's always the chance of a bug somewhere.
Eric
Yes, using spin2cpp to compile the PASM driver code would probably be an option. It might be nice to replace bstc in the propgcc tree -- not that bstc isn't a great tool (it certainly is!) but it would be kind of nice to use platform independent, C only tools to build the C compiler.
Eric
Ray
Spin code:
spin2cpp conversion code:
General question here - under the hood with things like printf and serial drivers and sd drivers, are the drivers for GCC written in C or Spin? Is there any spin at all?
Restating what you are saying, if there is any spin, then everything can be converted to C. Maybe the only spin left is a tiny loader to get things started?
The library drivers (and functions like printf) are all written in C. What's left in spin is indeed a tiny (16 byte) loader at the start of programs, which is actually hand crafted spin byte codes; and some board interfaces files and XMM memory drivers used by the propeller-load loader. Those are the drivers David was referring to.
Eric
"Unknown identifier used in function call" and lists waitpne, and waitpeq as the culprits. These are listed as legal commands in the manual, and it looks like they are being used correctly in the program. Any ideas?
Ray