Interactive Spin to Pasm or C converter

ersmithersmith Posts: 2,272
edited August 2016 in Propeller 1 Vote Up0Vote Down
Update 8/20/2016: There's a new release 3.1.1 at
https://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.
«134567

Comments

  • 200 Comments sorted by Date Added Votes
  • This is really cool! :D

    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?
    David
    PropWare: C++ HAL (Hardware Abstraction Layer) for PropGCC; Robust build system using CMake; Integrated Simple Library, libpropeller, and libPropelleruino (Arduino port); Instructions for Eclipse and JetBrain's CLion; Example projects; Doxygen documentation
    CI Server: http://david.zemon.name:8111/?guest=1
  • DavidZemon wrote: »
    This is really cool! :D

    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.

  • Ah, that makes sense. Exciting work nonetheless :)
    David
    PropWare: C++ HAL (Hardware Abstraction Layer) for PropGCC; Robust build system using CMake; Integrated Simple Library, libpropeller, and libPropelleruino (Arduino port); Instructions for Eclipse and JetBrain's CLion; Example projects; Doxygen documentation
    CI Server: http://david.zemon.name:8111/?guest=1
  • Ok, this is a briliant idea. I can use it to help me learn PASM lang.

    Joe
    ~Owner, Designer & Builder Killer K.A.R.R.
    ~Starfleet Captain Joe R.
    Current Commission: USS-Titanic Registry Number NCC-1513
    Launched: Star Date 48509.9 April 10th, 2385
    Galaxy Class, Titanic Sub-Class Starship Max Warp: 15.96
  • 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.
  • ersmith wrote: »
    .... 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 ?


  • jmg wrote: »
    ersmith wrote: »
    .... 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".

  • ersmithersmith Posts: 2,272
    edited February 2016 Vote Up0Vote Down
    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:
    screenshot1.png

    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:
    screenshot2.png

    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:
    screenshot3.png

    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).

    Note that even the comments are preserved in the C and C++ versions.
    984 x 478 - 19K
    984 x 478 - 17K
    984 x 478 - 15K
  • I'm having problems with it, er.

    Compilation failed.
    But why?
    PUB  Blinky1
    
    dira [23]~~
    repeat
       outa[23] := 1
       waitCNT (400+cnt) 
       outa[23] := 0
       waitCNT (1_000_000+cnt) 
     
    
  • Compiles fine using the Propeller Tool.
    In science there is no authority. There is only experiment.
    Life is unpredictable. Eat dessert first.
  • cavelamb wrote: »
    I'm having problems with it, er.

    Compilation failed.
    But why?
    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:

    https://github.com/totalspectrum/spin2cpp/releases/download/spincvt-preview-4/spincvt.zip

    Thanks for the bug report! It's good to have feedback on what kinds of things aren't obvious in the GUI.
  • Dave HeinDave Hein Posts: 5,519
    edited March 2016 Vote Up0Vote Down
    The converter produces the following PASM code. It looks OK to me.
    DAT
    	org	0
    Blinky1
    	and	DIRA, imm_4286578687_
    	or	DIRA, imm_8388608_
    L_001_
    	or	OUTA, imm_8388608_
    	mov	Blinky1_tmp001_, #400
    	add	Blinky1_tmp001_, CNT
    	waitcnt	Blinky1_tmp001_, #0
    	andn	OUTA, imm_8388608_
    	mov	Blinky1_tmp001_, imm_1000000_
    	add	Blinky1_tmp001_, CNT
    	waitcnt	Blinky1_tmp001_, #0
    	jmp	#L_001_
    Blinky1_ret
    	ret
    
    Blinky1_tmp001_
    	long	0
    imm_1000000_
    	long	1000000
    imm_4286578687_
    	long	-8388609
    imm_8388608_
    	long	8388608
    

    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).
  • ersmithersmith Posts: 2,272
    edited March 2016 Vote Up0Vote Down
    Dave Hein wrote: »
    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
    
    Note that compiling from .spin to .binary is a 2 step process: .spin -> .pasm -> .binary.
  • Sounds like you have the P1 version working already.
    Still thinking about extending this to P2?
    Prop Info and Apps: http://www.rayslogic.com/
  • I was trying to run it interactively pasting code in the left window.
    Trying to see what SPIN routines would look like in PASM.

    Command line only?
    ./bin/spin2cpp --noheader --asm -o C:/!Propeller/! C-spin/spincvt/spincvt/B1.pasm C:/!Propeller/! C-spin/spincvt/spincvt/B1.spin
    Spin to C++ converter version 3.00-preview-3
    Usage: C:\!Propeller\! C-spin\spincvt\spincvt\bin\spin2cpp.exe [options] file.spin
    Options:
    --asm: output (user readable) PASM code
    --binary: create binary file for download
    --ccode: output (static) C code instead of C++
    --dat: output binary blob of DAT section only
    --elf: create executable ELF file with propgcc
    --catalina: convert to C and run Catalina on result
    --files: print list of .cpp files to stdout
    --gas: create inline assembly out of DAT area;
    with --dat, create gas .S file from DAT area
    --main: include C++ main() function
    --nopre: do not run preprocessor on the .spin file
    --normalize: normalize case of all identifiers
    -Dname=val: define a preprocessor symbol
    -I dir: add dir to the object search path
    child process exited abnormally
  • cavelambcavelamb Posts: 679
    edited March 2016 Vote Up0Vote Down
    Dave Hein wrote: »
    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).

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

    What would the dummy pub look like?
  • Rayman wrote: »
    Sounds like you have the P1 version working already.
    Still thinking about extending this to P2?

    The P1 version is far from finished -- it still doesn't support objects. But I am making progress, and yes, I am thinking of extending it to the P2.
  • Interesting...

    It has been a L O N G time since I've played around with a disassembler, but that's what this looks like.

  • cavelamb wrote: »
    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.

  • cavelamb wrote: »
    Dave Hein wrote: »
    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).

    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.

  • Just to be able to run a single object thru and get pasm out that can be hand fixed/simplified/etc would be quite an achievement.

    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???
  • Thanks guys.

    @er
    The !'s are path. They sort to the top of an ascii list.

    Well.
    I have PASM output, but I'm not sure I can do much with it.
    It's sure not going to be a plug-n-play swap anyway.

  • Noticed that the CON section simplifies _clkmode etc from the original definition. Not a biggie though.
  • Cluso99 wrote: »
    Just to be able to run a single object thru and get pasm out that can be hand fixed/simplified/etc would be quite an achievement.

    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???

    Well, yes and no.

    Yes, it sounds reasonable, but trying to unroll deeply nested calls - they will all ave to be translated eventually.

    Got this from ersmith's spin2CPP...
    It looks like maybe something workable.

    But in trying to simplify things, I ran into (again) a couple of SPIN/PASM idiosyncrasies.
    Or maybe just my lack of understanding.

    Anyway, still trying...

    DrawHorizontalLine and FillHorizontalLine are two of the innermost functions.
    Speeding these two up should show noticeable improvement.

    Where is LocalRoger when I need him!?!?

    'Spin
    pub drawHorizontalLine(poX, poY, length, color) | i

    setXY(poX,poY)
    TFT_LineOrientation(0)
    if(length+poX>TFT_Max_X)
    length := TFT_Max_X-poX
    sendMultiData(color, length)
    {
    drawHorizontalLine
    mov setXY_poX_, drawHorizontalLine_poX_
    mov setXY_poY_, drawHorizontalLine_poY_
    call #setXY
    mov TFT_LineOrientation_HV_, #0
    call #TFT_LineOrientation
    mov drawHorizontalLine_tmp001_, drawHorizontalLine_length_
    add drawHorizontalLine_tmp001_, drawHorizontalLine_poX_
    cmps drawHorizontalLine_tmp001_, #TFT_Max_X wc,wz

    if_a mov drawHorizontalLine_length_, #TFT_Max_X

    if_a sub drawHorizontalLine_length_, drawHorizontalLine_poX_

    mov sendMultiData_data_, drawHorizontalLine_color_
    mov sendMultiData_qty_, drawHorizontalLine_length_
    call #sendMultiData
    drawHorizontalLine_ret
    ret
  • Problems encountered:

    Passing variables - how to cast variables in the PASM routine.


  • Cluso99 wrote: »
    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.
  • cavelamb wrote: »
    Problems encountered:

    Passing variables - how to cast variables in the PASM routine.

    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!
  • ersmith wrote: »
    Cluso99 wrote: »
    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.
  • cavelambcavelamb Posts: 679
    edited March 2016 Vote Up0Vote Down
    ersmith wrote: »
    cavelamb wrote: »
    Problems encountered:

    Passing variables - how to cast variables in the PASM routine.

    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.



  • Cluso99 wrote: »
    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.

    Thanks,
    Eric
Sign In or Register to comment.