automatically convert spin to PASM
ersmith
Posts: 6,096
After posting my thread about converting spin to C/C++, I noticed a "related thread" about converting spin to PASM. For very simple spin objects that is now possible using a combination of propgcc and spin2cpp. The output isn't very good PASM, of course, but as they say of talking dogs, it's not how well it does it but that it does it at all :-). For quick and dirty projects it may be a good starting point for hand optimization.
For example consider the led toggle program:
The result is, as I said earlier, hardly optimal PASM, but it is valid and works. More complicated sources than LED toggle will probably need some hand massaging to get them to work, though!
For example consider the led toggle program:
CON _clkmode = xtal1+pll16x _clkfreq = 80_000_000 pin = 15 VAR long pause PUB start pause := (_clkfreq/2) DIRA[pin] := 1 ' set as output OUTA[pin] := 1 ' turn it on repeat waitcnt(CNT + pause) !OUTA[pin]That can be converted (via the command line) to C with:
spin2cpp --ccode --main led.spinand then the resulting led.c can be converted to PASM (i.e. back to spin!) with:
propeller-elf-gcc -mspin -Os -S -o new.spin led.cThe -mspin option says to produce PASM rather than GAS output, and add a spin wrapper; -Os option says to optimize for size; -S says to produce assembly output; and -o new.spin gives the output file name.
The result is, as I said earlier, hardly optimal PASM, but it is valid and works. More complicated sources than LED toggle will probably need some hand massaging to get them to work, though!
'' spin code automatically generated by gcc CON _clkmode = xtal1+pll16x _clkfreq = 80_000_000 __clkfreq = 0 '' pointer to clock frequency '' adjust STACKSIZE to how much stack your program needs STACKSIZE = 256 VAR long cog '' cog that was started up by start method long stack[STACKSIZE] '' add parameters here long param '' add any appropriate methods below PUB start stop cog := cognew(@entry, @param) + 1 PUB stop if cog cogstop(cog~ - 1) DAT org entry r0 mov sp,PAR r1 mov r0,sp r2 jmp #_main r3 long 0 r4 long 0 r5 long 0 r6 long 0 r7 long 0 r8 long 0 r9 long 0 r10 long 0 r11 long 0 r12 long 0 r13 long 0 r14 long 0 lr long 0 sp long 0 '.text long 'global variable _ledSpin_Start _ledSpin_Start mov r6, DIRA mov r7, L_LC2 or r6, r7 mov DIRA, r6 mov r6, OUTA mov r5, L_LC0 or r6, r7 mov OUTA, r6 mov r4, r7 wrlong L_LC1, r5 L_L2 mov r7, CNT rdlong r6, r5 add r7, r6 waitcnt r7,#0 mov r6, OUTA xor r6, r4 mov OUTA, r6 jmp #L_L2 long L_LC0 long _thisobj long L_LC1 long 40000000 long L_LC2 long 32768 long 'global variable _main _main sub sp, #4 wrlong lr, sp jmpret lr,#_ledSpin_Start '.data _thisobj long 0[2]
Comments
Good question. I timed it and got 16 cycles/toggle in the new version, versus 976 cycles/toggle for the original. So the converted version is about 61x as fast.
(In my original version of this post I quoted an incorrect figure for the new version, because of a silly typo in my timing routine.)
Sounds like a very good idea. Can you include the led.c source, so all the steps are shown ?
I'm not sure I understand the question. All the steps are shown -- led.c is produced automatically from led.spin by spin2cpp. But for completeness, here is led.c: and here is led.h:
Thanks, that's good, I know it is an automatic step, but it helps to show everyone each step, makes the flow clearer.
I think spin2cpp is yours ?
I would also suggest adding a comment header, to any generated files, roughly like
that makes project management easier, and anyone looking at just the .c knows how to re-create it.
Thanks, that is a good idea. I'll add that to a future version.
ersmith, thank you, this sure got my attention! Making Spin sixty-one times faster is remarkable. I will be curious to see performance on other programs and how they pan out.
It's actually the wrlong instruction that uses L_LC0 that's wrong, but good point. The demo does work, but it scribbles on some memory it shouldn't. Ouch! Good catch.
Definitely this method should not be relied on to produce correct code. It's not really the way propeller-elf-gcc was intended to work (if instead of -mspin we used -mcog and compiled to an ELF file then the data would correctly have been placed in hub memory). But it's a neat trick, I thought :-).
It's threads like this (and many more) that make me wonder why PropBASIC is apparently ignored. All the chit-chat about PropForth and the various C/C++ offerings, etc. Unless I am mistaken, PropBASIC is the only high level language that produces native PASM code....no?
Mickster.
Yes, I believe that LMM is a directive if required.
Mickster