Interactive Spin to Pasm or C converter

Update 8/20/2016: There's a new release 3.1.1 athttps://github.com/totalspectrum/spin2cpp/releases/.
The new version supports Propeller 2 assembly output, and has FCACHE acceleration to speed up LMM code on Propeller 1. This release also includes the "fastspin" front end, which is a command line tool that takes the same arguments as Parallax's "openspin" tool but produces fast (and large) LMM binaries from Spin code, instead of small and compact bytecode as openspin does.

Update 3/29/2016: The spin2cpp_v3.0.0.zip file accidentally had old (v1.94) binaries in it, so the command line tools did not work to produce PASM output. Sorry about that! I've updated to v3.0.1 with the correct binaries. The spinconvert GUI (spincvt.zip) was not affected.

Update 3/25/2016: Many bugs have been fixed thanks to feedback from users here. The current version is at:https://github.com/totalspectrum/spin2cpp/releases/tag/v3.0.1. Most users will only need the spincvt.zip file (that's the Windows GUI). Linux users, or Windows users who prefer a command line tool, can grab spin2cpp.zip. Mac users, I'm afraid, will have to build from source for now.

@, and inline assembly in PASM output mode). Several bugs have been fixed, and there's now an option (-g) for including the original Spin source as comments in the PASM output; that option is enabled by default in the GUI.

Update 3/19/2016: Preview 9 is out now. As far as I know the language support is complete now: all Spin features should be supported for all three output modes (PASM / C / C++). If you find a valid Spin program that won't compile please let me know, as it's probably a bug. I'm sure there are plenty of bugs left. The one caveat is that compiled LMM mode programs are much bigger than the original Spin bytecodes, so some large Spin programs will not fit in memory when compiled with Spin Converter.

Update 3/15/2016: Preview 8 is out now. The biggest change is LMM mode support, so larger Spin programs can be run. Together with various bug fixes and additions, we can now run quite a few interesting programs, including SS_TFT.spin, fft_bench.spin, and fibo.spin. The easiest way to compile is to launch the GUI, select "Make Binary" and "LMM Mode" under options, then load in the .spin file. A .binary file will be produced (it has the same base name as the .spin) which you can then load using propeller-load or PropellerIDE.

Update 3/12/2016: We're up to preview 7 now. It's able to compile the FullDuplexSerial object now, and can run the fibo benchmark (among others). FullDuplexSerial is actually overkill if only output is needed, since the PASM code is more than capable of driving a serial line at 115200, so I've also provided a smaller, faster SimpleSerial that runs in the same cog.

Update 3/4/2016: preview 5 is out. The GUI now has an option to produce .binary files, and we can compile and run real examples (see the examples folder).

Update 2/27/2016: and now preview 3 is available, with an improved GUI and more bug fixes

Update 2/24/2016: preview 2 is available now that fixes a some nasty bugs in preview 1.

=== Some of the information below here is obsolete (at least, the info about how much is done) but kept for reference

I've been working on extending spin2cpp to handle PASM output. That work is still ongoing, but there seems to be a demand for an easy way to convert Spin to Pasm. So I've released a preview version of the converter. For converting to C it's complete (as far as I know), but for PASM output it's still early stages: it'll only handle very simple Spin code: a single object, long variables only, and many builtin functions are not implemented yet. You can get the preview at https://github.com/totalspectrum/spin2cpp/releases/; look for the most recent Spin Converter GUI Preview.

There's a simple GUI. To use it, unzip spincvt.zip and run the program spinconverter.exe. Load up a .spin file, and you'll either see the corresponding .pasm in the right hand window, or else you'll get error messages in the bottom window. If you edit and save the .spin file (the left window) the right window will be regenerated. The output file name is always the same as the input file name but with the extension changed to .pasm (or .c). Under the Options menu you can select PASM, C, or C++ output.

Again, this is a preview and quite incomplete -- for example, OBJ isn't supported in PASM yet. So don't expect to magically convert your complete Spin project to PASM (although for C output you actually may have good luck). I think it will be useful for converting small snippets of code though.

Only windows binaries are included. Linux and Mac users can get the source from the spin2pasm branch of spin2cpp. The GUI is in spinconvert/spinconvert.tcl, and will probably work on any platform but hasn't been tested much.

I was going to add this to my build server too, but I can't find where the GUI binaries are built. Are there special flags or make targets?

Thanks for the offer, but I don't think automatically distributing the GUI will work yet. This time around I built the GUI by hand (using the Windows freewrap program on spinconvert.tcl). At some point I'll try to automate the process, but it's not really ready for prime time yet.

I've published a new (preview) release that fixes some nasty bugs in the first release, including incorrect output for the if_e condition (the first release printed this as "if_eq", which PASM thinks is a label!) and a failure to capture the compiler output in the appropriate window.

.... For converting to C it's pretty complete, but for PASM output it's still early stages: it'll only handle very simple Spin code.
...

There's a simple GUI. To use it, unzip spincvt.zip and run the program spinconverter.exe. Load up a .spin file, and you'll either see the corresponding .pasm in the right hand window, or else you'll get error messages in the bottom window. If you edit and save the .spin file (the left window) the right window will be regenerated..

Very nifty idea.
Is there a short summary of what 'very simple Spin code' or 'pretty complete' means ?
ie what is currently supported, and what is still to do ?

.... For converting to C it's pretty complete, but for PASM output it's still early stages: it'll only handle very simple Spin code.

Very nifty idea.
Is there a short summary of what 'very simple Spin code' or 'pretty complete' means ?
ie what is currently supported, and what is still to do ?

"pretty complete" means that (as far as I know) any valid Spin program can be converted to C or C++ successfully, but error checking is not that great so some invalid Spin programs may not get sensible errors (and will be converted to invalid C code)

"very simple Spin code" is a bit vague, sorry, but basically the PASM output only handles CON, PUB, PRI, and simple VAR (long only, no arrays); OBJ and DAT are not supported. (Ironic, I know, that DAT is not supported yet since it's only a "pass through", but internally spin2cpp actually parses and compiles the DAT and I don't yet have a way to put that back into output PASM).

Definitely supported for PASM output are: if/else, loops (including quit/next), function definition and calls (except recursive), and expression evaluation, including the hardware registers but excluding SPR. Within functions I know that abort, lookup/lookdown, case, string, and built-in functions (longmove, the string functions, etc.) are not supported, with the exception of the waitxxx functions. There are probably some other things missing on the PASM side, that's why I'm calling this a "preview".

I've updated the repo to have a third preview release, fixing some more bugs and with an improved GUI. See the first post in this thread for a link.

Here's an example of using of Spin Converter, with screenshots. In the discussion of Spin equivalent to arduino pulsin@JonnyMac posted some Spin code to give that equivalent. Suppose you want to know what the PASM or C version is. Open up spinconvert.exe. Copy and paste Jon's code into the "Original Spin" window. Save it (using the menu or the ^S command). As soon as you save the converter runs and shows the "Converted Code", in this case PASM, on the right:

We see a lot of repeated lines like:

mov ez_pulse_in_tmp001_, #1
shl ez_pulse_in_tmp001, ez_pulse_in_pin_

This is evaluation of the "decode pin" expression ("|<pin") which happens
repeatedly. A sophisticated optimizing compiler like PropGCC has would
recognize this repeated code and factor it out, but we'll have to do it
by hand. Edit the original spin code to add a variable "mask" which will
hold the decoded pin value. When you save, the code will be reconverted
again:

Every time you save the original Spin file, the converter is re-run to produce fresh output.

Now suppose you want to know what the same thing would look like in C++. Go to the "Options" menu and select C++. The code will be converted again, and you'll see:

The same thing will work for C too, although the C output is a little bit messier at the start (spin2cpp adds some defines to allow the generated code to work both in PropGCC and Catalina).

The error message actually appears in the bottom window -- the alert box should have mentioned this. In the bottom window you would have seen something like:
error: blinky.spin:3: Cannot handle expression yet
So on line 3 there was an operator ( "~~" ) that the PASM converter didn't know how to deal with. (It would have worked fine in C or C++ mode).

I've updated the PASM output routines to deal with postfix ~ and ~~, and I've also made the GUI alert box point to the compiler output window and highlighted errors in red. There's a new release at:

The converter produces the following PASM code. It looks OK to me.
...
EDIT: You do have to add a dummy PUB method to get it to compile. And if you want to run it the PUB method would have to do a cognew(@Blinky1,0).

Thanks for looking at it Dave. The first "and" to DIRA is redundant; I'm going to try to fix that. Otherwise I'm pretty happy with the code generator.

On the subject of compiling, spin2cpp itself (the 3.00-preview version) can actually compile the pasm output file to binary, with no dummy Spin method required (well, a tiny precompiled Spin header that does the cognew is prepended automatically, just like PropGCC does). I've been meaning to add a GUI option for that, but for now do:

bin/spin2cpp --asm blinky1.spin # this is what the GUI does
bin/spin2cpp --dat --binary blinky1.pasm

I was trying to run it interactively pasting code in the left window.
Trying to see what SPIN routines would look like in PASM.

That's what spinconvert.exe is for, and I think you were using it correctly earlier -- you just had a version that didn't understand ~~. The preview-4 version does, try that.

Command line only?

spin2cpp is the tool that spinconvert.exe uses "behind the scenes". It is a command line only tool, which is why spinconvert.exe exists, to make it easier to use.

I'm not sure why you got the usage message you did, but the command line you posted looks very weird with all the exclamation marks in it -- I think something got typed in wrong. Did that line come from the Compiler Output window of spinconvert.exe?

Oh, do some of the paths or files you are using have spaces in them? Perhaps that's the problem, there may be a bug in spinconvert.exe handling files with spaces included in them. I'll try to figure that out.

EDIT: You do have to add a dummy PUB method to get it to compile. And if you want to run it the PUB method would have to do a cognew(@Blinky1,0).

That's a bit more cryptic than I can grok, Dave.

What would the dummy pub look like?

The Prop Tool (and probably other Spin compilers) requires at least one method in a Spin file. So you just need to add the line "pub methodname" to the beginning or end of the Spin file. This will allow it to compile. If you also add cognew(@Blinky1,0) to the method it will run the generated code as well.

J
To handle objects, just a dummy call can be placed in the pasm output. We could then modify it later to use another single output pasm object.

Does that make sense???

Yes, that seems like a pretty reasonable approach. To be honest though the object handling isn't one of the things that's causing me trouble. Right now the 2 things I'm hung up on are DAT section output and handling built-in functions like lockclr and longmove.

The DAT section output is a headache because the DAT section gets parsed and compiled by the "front end" (the spin parser part) and so to the "back end" (the assembly output) it's more or less a binary blob, just as it is for the C back end. I'd ultimately like to output something close to the original DAT, but I think for now I may just punt and output the compiled bytes.

The built-in functions are a problem just because of implementation details -- they're pretty tied in to how I output C code. I can work-around this (I already have for waitcnt/waitpeq/etc.) but I've been trying to find a general solution that, again, would work for any back end, not just the C and assembly ones. But probably I should punt for now.

I don't know how many back ends it's feasible to create. A spin byte code back end would be interesting in a way, but OTOH we already have openspin. A target to create Forth code output would also be kind of cool (Tachyon Forth has a wicked fast bytecode interpreter). But I don't know how much demand there is for a spin2forth program.

Maybe more interesting than different back ends would be different front ends (basic, pascal, etc.). But that's even further away.

If you're trying to convert just a part of a bigger program then you'll end up running the PASM in another COG, which means having to set up communication with it. You'd need some dummy Spin functions on the original side that copy their parameters into memory, and then in the PASM you'd read those values out of memory in the PASM version of the functions. There are some policy decisions here which are beyond the scope of the spin converter, I think.

It looks like you're trying to convert Roger's TFT.spin. That's one of the objects I'm interested in as well, so in a future version spinconverter will probably be able to convert your whole program to PASM, which will make things a lot easier. You'll just have to wait a bit longer!

J
To handle objects, just a dummy call can be placed in the pasm output. We could then modify it later to use another single output pasm object.

Does that make sense???

Yes, that seems like a pretty reasonable approach. To be honest though the object handling isn't one of the things that's causing me trouble. Right now the 2 things I'm hung up on are DAT section output and handling built-in functions like lockclr and longmove.

The DAT section output is a headache because the DAT section gets parsed and compiled by the "front end" (the spin parser part) and so to the "back end" (the assembly output) it's more or less a binary blob, just as it is for the C back end. I'd ultimately like to output something close to the original DAT, but I think for now I may just punt and output the compiled bytes.

The built-in functions are a problem just because of implementation details -- they're pretty tied in to how I output C code. I can work-around this (I already have for waitcnt/waitpeq/etc.) but I've been trying to find a general solution that, again, would work for any back end, not just the C and assembly ones. But probably I should punt for now.

I don't know how many back ends it's feasible to create. A spin byte code back end would be interesting in a way, but OTOH we already have openspin. A target to create Forth code output would also be kind of cool (Tachyon Forth has a wicked fast bytecode interpreter). But I don't know how much demand there is for a spin2forth program.

Maybe more interesting than different back ends would be different front ends (basic, pascal, etc.). But that's even further away.

IMHO a Basic front end would likely be a big benefit. We do have Bean's basic for the prop but I haven't looked at it.

I am curious to know whether an extra pass in the compiler for spin bytecodes could remove some of the push/pop that is done for every type of bytecode. Sometimes the info must also be on the stack, so its a requirement for this pass to have access to the source. I guess there are other compiler requirements first, like some form of macros etc.

I am keen to put some code thru your spin to pasm converter shortly, if only to get some help in converting some spin code (drivers) to pasm for placing in cog(s) rather than running spin.

If you're trying to convert just a part of a bigger program then you'll end up running the PASM in another COG, which means having to set up communication with it. You'd need some dummy Spin functions on the original side that copy their parameters into memory, and then in the PASM you'd read those values out of memory in the PASM version of the functions. There are some policy decisions here which are beyond the scope of the spin converter, I think.

It looks like you're trying to convert Roger's TFT.spin. That's one of the objects I'm interested in as well, so in a future version spinconverter will probably be able to convert your whole program to PASM, which will make things a lot easier. You'll just have to wait a bit longer!

Yeah.
It would take an experienced PASM programmer an hour or two to do it.
But I'm not a PASM guy.

I've been waiting about a year on this, er.
So I'm waiting as fast as I can?

So you just keep plugging along at it.
If you can make it work you will have created something very useful.
Basically it would be a SPIN compiler.
Like I said, something VERY useful.

IMHO a Basic front end would likely be a big benefit. We do have Bean's basic for the prop but I haven't looked at it.

PropBasic produces pretty good code, but partly it does this by really restricting the input (e.g. each line can only have one operator), which has the benefit of "forcing" the programmer to think about what they're doing and write carefully. I'm trying to make spin2pasm be a little more flexible, but it comes at the cost of having to do multiple passes on the code.

I am keen to put some code thru your spin to pasm converter shortly, if only to get some help in converting some spin code (drivers) to pasm for placing in cog(s) rather than running spin.

I'd love to hear your feedback. Fair warning, the converter is still very much alpha code (if not pre-alpha) and so I'm sure you'll find lots of features missing. I hope to have some of these added by next week.