Shop OBEX P1 Docs P2 Docs Learn Events
automatically convert spin to PASM — Parallax Forums

automatically convert spin to PASM

ersmithersmith Posts: 6,096
edited 2012-08-22 19:51 in Propeller 1
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:
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.spin
and 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.c
The -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

  • HumanoidoHumanoido Posts: 5,770
    edited 2012-08-21 16:22
    How much faster does the pasm conversion run, when compared to the original spin?
  • ersmithersmith Posts: 6,096
    edited 2012-08-21 17:22
    Humanoido wrote: »
    How much faster does the pasm conversion run, when compared to the original spin?

    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.)
  • jmgjmg Posts: 15,183
    edited 2012-08-21 17:47
    ersmith wrote: »
    For quick and dirty projects it may be a good starting point for hand optimization.

    Sounds like a very good idea. Can you include the led.c source, so all the steps are shown ?
  • ersmithersmith Posts: 6,096
    edited 2012-08-21 17:56
    jmg wrote: »
    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:
    #include <propeller.h>
    #include "led.h"
    
    #ifdef __GNUC__
    #define INLINE__ static inline
    #else
    #define INLINE__ static
    #define waitcnt(n) _waitcnt(n)
    #define locknew() _locknew()
    #define lockret(i) _lockret(i)
    #define lockset(i) _lockset(i)
    #define lockclr(i) _lockclr(i)
    #define coginit(id, code, par) _coginit((unsigned)(par)>>2, (unsigned)(code)>>2, id)
    #define cognew(code, par) coginit(0x8, (code), (par))
    #define cogstop(i) _cogstop(i)
    #endif
    INLINE__ int32_t PostFunc__(int32_t *x, int32_t y) { int32_t t = *x; *x = y; return t; }
    #define PostEffect__(X, Y) PostFunc__(&(X), (Y))
    
    static ledSpin thisobj;
    int32_t ledSpin_Start(void)
    {
      int32_t result = 0;
      thisobj.Pause = (80000000 / 2);
      DIRA |= (1<<15);
      OUTA |= (1<<15);
      while (1) {
        waitcnt((CNT + thisobj.Pause));
        OUTA ^= 0x8000;
      }
      return result;
    }
    
    int main() {
      return ledSpin_Start();
    }
    
    
    and here is led.h:
    #ifndef ledSpin_Class_Defined__
    #define ledSpin_Class_Defined__
    
    #include <stdint.h>
    
    #define _clkmode (1032)
    #define _clkfreq (80000000)
    #define Pin (15)
    
    typedef struct ledSpin {
      int32_t    Pause;
      char dummy__;
    } ledSpin;
    
      int32_t    ledSpin_Start(void);
    #endif
    
  • jmgjmg Posts: 15,183
    edited 2012-08-21 18:23
    ersmith wrote: »
    led.c is produced automatically from led.spin by spin2cpp. But for completeness, here is led.c:

    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
    //  led.c auto-created by spin2cpp V0.96 from source led.spin on Date:time
    //  using command line : spin2cpp --ccode --main led.spin
    

    that makes project management easier, and anyone looking at just the .c knows how to re-create it.
  • ersmithersmith Posts: 6,096
    edited 2012-08-21 19:08
    jmg wrote: »
    I would also suggest adding a comment header, to any generated files, roughly like
    //  led.c auto-created by spin2cpp V0.96 from source led.spin on Date:time
    //  using command line : spin2cpp --ccode --main led.spin
    

    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.
  • HumanoidoHumanoido Posts: 5,770
    edited 2012-08-21 20:14
    ersmith wrote:
    The output isn't very good PASM... 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.

    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.
  • varnonvarnon Posts: 184
    edited 2012-08-22 00:23
    Pretty cool. Sounds like it might also be a useful tool for learning PASM. I really don't know what I'm doing with assembly at the moment.
  • kuronekokuroneko Posts: 3,623
    edited 2012-08-22 00:47
    ersmith wrote: »
    The result is, as I said earlier, hardly optimal PASM, but it is valid and works.
    L_LC0
        long    _thisobj
    ...
    _thisobj
        long    0[2]
    
    This may work in GAS but definitely doesn't for normal PASM (in your example L_LC0 contains $29, the cog register index for _thisobj).
  • ersmithersmith Posts: 6,096
    edited 2012-08-22 07:45
    kuroneko wrote: »
    L_LC0
        long    _thisobj
    ...
    _thisobj
        long    0[2]
    
    This may work in GAS but definitely doesn't for normal PASM (in your example L_LC0 contains $29, the cog register index for _thisobj).

    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 :-).
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-08-22 08:00
    You would need to use "long @_thisobj+16" in PASM if this object is the top object. If it's not the top object you would need to use "long @_thisobj" and then fix up all the pointers in the start method with "L_LC0 := @@L_LC0" prior to calling cognew. Of course, if you do the fixup in the start method you would need to ensure that it is only done the first time it is called.
  • MicksterMickster Posts: 2,720
    edited 2012-08-22 13:46
    varnon wrote: »
    Pretty cool. Sounds like it might also be a useful tool for learning PASM. I really don't know what I'm doing with assembly at the moment.

    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.
  • David BetzDavid Betz Posts: 14,516
    edited 2012-08-22 14:04
    Mickster wrote: »
    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.
    As far as I know, PropBasic produces LMM code as does Catalina and PropGCC. However, both Catalina and PropGCC can also generate more compact instructions (CMM) that allow for better code density and hence more code to fit into hub memory. This is similar to the way Spin works by compiling into bytecodes but the mapping to Propeller machine instructions is more direct in the case of CMM.
  • Mike GreenMike Green Posts: 23,101
    edited 2012-08-22 14:12
    AFAIK PropBasic can produce both LMM and plain PASM code.
  • MicksterMickster Posts: 2,720
    edited 2012-08-22 14:19
    Mike Green wrote: »
    AFAIK PropBasic can produce both LMM and plain PASM code.

    Yes, I believe that LMM is a directive if required.

    Mickster
  • David BetzDavid Betz Posts: 14,516
    edited 2012-08-22 18:40
    Mike Green wrote: »
    AFAIK PropBasic can produce both LMM and plain PASM code.
    That's good to know. I guess I should have mentioned that PropGCC can produce PASM code as well. I'm not sure about Catalina.
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-08-22 19:51
    Catalina does not produce COG PASM code.
Sign In or Register to comment.