Shop Learn
Moving From Spin1 to Spin2 — Parallax Forums

Moving From Spin1 to Spin2

JonnyMacJonnyMac Posts: 7,419
edited 2020-11-10 18:22 in Propeller 2
In another thread I saw a person with P1 experience wishing for a guide to moving code to the P2. I'm not suggesting that is is that guide, but I'd like it be a place where we can share what we've learned moving from Spin on the P1 to the Spin on the P2.

Please...
-- keep the tips focused on Spin1 to Spin2 (there are plenty of places to discuss BASIC, C, Forth, etc.)
-- when possible, show the Spin1 version and the equivalent Spin2 version
-- please restrict examples to that which will run in the official Parallax tools, PNut and Propeller Tool (some compilers have extensions that aren't back-compatible with Parallax tools)

I've been porting my P1 objects to the P2 for the past couple months, so I'll share my mental list and the things I've learned here.

Comments

  • Tip: Identify the device type in your listing.

    Now... this hasn't been implemented yet, but it will be. No doubt those of you using Propeller Tool have written P2 code only to have the compiler complain because it defaults to the P1. For the moment the fix is to save the file with a .spin2 extension. In the future, though, Parallax will implement a feature used in the BASIC Stamp editor that allows the same tool to deal with the differences between the Stamp 1 and the Stamp 2, and even extensions in PBASIC 2.

    This is my programming header; note the comment with {$P2} in it. This modelled after the way the BASIC Stamp editor works. Down the line, we should be able to compile and check code before saving.
    '' =================================================================================================
    ''
    ''   File....... untitled.spin2
    ''   Purpose....
    ''   Author..... Jon "JonnyMac" McPhalen
    ''               Copyright (c) 2020 Jon McPhalen
    ''               -- see below for terms of use
    ''   E-mail..... jon.mcphalen@gmail.com
    ''   Started....
    ''   Updated.... dd MMM yyyy
    ''
    ''   {$P2}
    ''
    '' =================================================================================================
    
  • Spin1 to Spin2: Add empty parenthesis to any methods that do not use parameters.

    Spin1
    pub main
                                                                     
      setup                                                          
                                                                     
      wait_for_terminal(true)
                    
      term.str(string("Hello, World!", 13, 13))            
                                                                     
      repeat                                                         
        waitcnt(0)
    

    Spin2
    pub main()
    
      setup()
    
      wait_for_terminal(true)
    
      term.str(string("Hello, World!", 13, 13))
    
      repeat
        waitct(0)
    

    Note that neither my main nor setup methods use parameters, but in Spin2 must have empty parenthesis.
  • JonnyMacJonnyMac Posts: 7,419
    edited 2020-11-10 18:22
    Spin1 to Spin2: New Spin2 keywords handle essential IO.

    Spin1
      outa[LED] := 1                                                ' activate LED
      dira[LED] := 1
    
    The initial configuration of an IO pin requires setting the outa (output register) and dira (direction register) bits.

    Spin2
      pinhigh(LED)
    

    Both languages accommodate multiple contiguous pins.

    Spin1
      outa[MSB..LSB] := %1010
      dira[MSB..LSB] := %1111
    

    Spin2
      pinwrite(LSB addpins (MSB-LSB), %1010
    

    Warning: In the P2, the group created by addpins cannot span the pin 31 / pin 32 boundary.

    P2 IO keywords:
    -- pinhigh(n), pinh(n)
    -- pinlow(n), pinl(n)
    -- pintoggle(n), pint(n)
    -- pinfloat(n), pinf(n)
    -- pinwrite(n, value), pinw(n, value)
    -- value := pinread(n), value := pinr(n)

    Consult the official P2 documentation for keyword details.
    -- https://docs.google.com/document/d/16qVkmA6Co5fUNKJHF6pBfGfDupuRwDtf-wyieh_fbqw/edit#heading=h.1h0sz9w9bl25
  • JonnyMacJonnyMac Posts: 7,419
    edited 2020-11-10 18:24
    Spin1 to Spin2: Using the system counter to control loops requires changes in keywords.

    P1 cnt becomes getct() in the P2.
    P1 waitcnt() becomes waitct() in the P2.

    Spin1
      dira[LED] := 1                                                ' make LED an output
    
      t := cnt                                                      ' sync to system counter
      repeat
        waitcnt(t += clkfreq / 10)                                  ' wait 1/10th second
        !outa[LED]                                                  ' toggle LED
    

    Spin2
      t := getct()
      repeat
        waitct(t += clkfreq / 10)
        pintoggle(LED)
    
  • JonnyMacJonnyMac Posts: 7,419
    edited 2020-11-10 18:25
    Spin1 to Spin2: Methods that return values must have those values declared in the P2

    Spin
    pub add_numbers(n1, n2)
    
      return n1+n2
    

    Spin2
    pub add_numbers(n1, n2) : result
    
      return n1+n2
    

    Note 1: The advantage of having to declare return values allows the P2 to return multiple values from the same method.

    Spin2
    pub add_sub(n1, n2) : sum2, dif2
    
      return n1+n2, n1-n2
    

    Note 2: The calling code does not have to accept multiple return values. Use the underscore character in the assignment expression to ignore a specific result.

    Spin2
      theSum, _ := add_sub(3, 5)
    

    Note 3: The P2 can use return without declaring a variable if no value is returned -- you can use an empty return as an early escape mechanism.

    Spin2
    pub blink(pin, count, ms)
    
      if (count < 0)
        return 
    
      repeat count
        pinhigh(pin)
        waitms(ms)
        pinlow(pin)
        waitms(ms)
    
  • wow, very nice

    thank you Jon

    Mike
  • Great start to the topic!
  • JonnyMacJonnyMac Posts: 7,419
    edited 2020-11-10 05:02
    Spin1 to Spin2: Some operators have changed, and some of them have changed behavior.
    Spin Operators
    
    a  New/changed operator in P2
    b  Behavioral change in P2
    
    P2              P1              Description
    --------------------------------------------------------------------------------
    ++ (pre)        ++              Pre-increment
    -- (pre)        --              Pre-decrement
    ?? (pre)        ?      ab       XORO32, iterate and return pseudo-random
    
    ++ (post)       ++              Post-increment
    -- (post)       --              Post-decrement
    !! (post)                       Post-logical NOT
    !  (post)                       Post-bitwise NOT
    \  (post)                       Post-set
    ~  (post)       ~               Post-set to 0
    ~~ (post)       ~~              Post-set to -1
    
    !               !               Bitwise NOT, 1's complement
    -               -               Negation, 2's complement
    abs             ||     a        Absolute value
    encod           >|     ab       Encode MSB, 31..0
    decod           |<     a        Decode, 1 << (x & $1F)
    bmask                           Bitmask, (2 << (x & $1F)) - 1
    ones                            Count ones
    sqrt                            Square root of unsigned x
    qlog                            Unsigned to logarithm
    qexp                            Logarithm to unsigned
    
    >>              >>              Shift right, insert 0's
    <<              <<              Shift left, insert 0's
    sar             ~>     a        Shift right, insert MSB's
    ror             ->     a        Rotate right
    rol             <-     a        Rotate left
    rev             ><     a        Reverse y LSBs of x and zero-extend
    zerox                           Zero-extend above bit y
    signx           ~, ~~  ab       Sign-extend from bit y
    
    &               &               Bitwise AND
    ^               ^               Bitwise XOR
    |               |               Bitwise OR
    
    *               *               Signed multiply
    /               /               Signed divide, return quotient
    +/                              Unsigned divide, return quotient
    //              //              Signed divide, return remainder
    +//                             Unsigned divide, return remainder
    sca                             Unsigned scale (x * y) >> 32
    scas            **     a        Signed scale (x * y) >> 30
    frac                            Unsigned fraction {x, 32'b0} / y
    
    +               +               Add
    -               -               Subtract
    
    #>              #>              Ensure x => y, signed
    <#              <#              Ensure x <= y, signed
    
    addbits                         Make bitfield, (x & $1F) | (y & $1F) << 5
    addpins                         Make pinfield, (x & $3F) | (y & $1F) << 6
    
    <               <               Signed less than                (returns 0 or -1)
    +<                              Unsigned less than              (returns 0 or -1)
    <=              =<     a        Signed less than or equal       (returns 0 or -1)
    +<=                             Unsigned less than or equal     (returns 0 or -1)
    ==              ==              Equal                           (returns 0 or -1)
    <>              <>              Not equal                       (returns 0 or -1)
    >=              =>     a        Signed greater than or equal    (returns 0 or -1)
    +>=                             Unsigned greater than or equal  (returns 0 or -1)
    >               >               Signed greater than             (returns 0 or -1)
    +>                              Unsigned greater than           (returns 0 or -1)
    <=>                             Signed comparison          (<,=,> returns -1,0,1)
    
    !!, not         not    a        Logical NOT  (x == 0,            returns 0 or -1)
    &&, and         and    a        Logical AND  (x <> 0 AND y <> 0, returns 0 or -1)
    ^^, xor         xor    a        Logical XOR  (x <> 0 XOR y <> 0, returns 0 or -1)
    ||, or          or     a        Logical OR   (x <> 0 OR  y <> 0, returns 0 or -1)
    
    ? :                             If x <> 0 then choose y, else choose z
    
    :=              :=     a        Set var(s) to x
                                    P2: v1,v2,... := x,y,... ' set v1 to x, v2 to y, etc. '_' = ignore
    
    
    Complex math functions
    ---------------------------------------------------------------------------------------------------
    x, y := rotxy(x,y,t)            Rotate cartesian (x,y) by t and assign resultant (x,y)
    x, t := xypol(x,y)              Convert cartesian (x,y) to polar and assign resultant (r,t)
    x, y := polxy(r,t)              Convert polar (r,t) to cartesian and assign resultant (x,y)
    
  • Spin1 to Spin2: The syntax of starting a new cog has changed

    Spin 1 (start PASM cog)
      cog := cognew(@fdsuart, @rxhead) + 1
    

    Spin 2 (start PASM cog)
      cog := coginit(COGEXEC_NEW, @uart_mgr, @rxp) + 1
    
    In the P2, coginit replaces cognew for PASM cogs. The new first parameter selects the cog; using COGEXEC_NEW will select the next available cog as was the case with cognew.

    Note: The P2 is able to run PASM code within a cog or from the hub; see the Spin2 documentation for details of coginit().


    Spin 1 (start Spin cog)
      bcog := cognew(background, @bcstack) + 1
    

    Spin 2 (start Spin cog)
      cog := cogspin(NEWCOG, play_notes(pin, p_tune, bpm, mode), @stack) + 1
    
    In the P2, cogspin replaces cognew for Spin cogs.
  • JonnyMac wrote: »
    Spin1 to Spin2: Using the system counter to control loops requires changes in keywords.

    P1 cnt becomes getct() in the P2.
    P1 waitcnt() becomes waitct() in the P2.

    Spin1
      dira[LED] := 1                                                ' make LED an output
    
      t := cnt                                                      ' sync to system counter
      repeat
        waitcnt(t += clkfreq / 10)                                  ' wait 1/10th second
        !outa[LED]                                                  ' toggle LED
    

    Spin2
      t := getct()
      repeat
        waitct(t += clkfreq / 10)
        pintoggle(LED)
    

    Jon, I started to learn Spin2 and trying out examples from Spin 1 I found out that apparently there is an important difference in the way bits are accessed and referenced in the code.

    In Spin 1:
    dira[LED]
    
    ...
    
    !outa[LED]
    

    But in Spin 2 the corresponding syntax would be
    dira.[LED]   'or dirb.[LED] if LED points to I/O's 32-63
    
    ...
    
    outa.[LED]!  'or outb.[LED]! if LED points to I/O's 32-63
    
  • JonnyMacJonnyMac Posts: 7,419
    edited 2020-11-16 03:11
    Jon, I started to learn Spin2 and trying out examples from Spin 1 I found out that apparently there is an important difference in the way bits are accessed...
    You're right, but may have missed my point a bit on this topic. My goal is to be functionally equivalent, not do a line-by-line translation of Spin1 to Spin2.

    In the P1, we only have one output register (outa) and its associated direction register (dira), but in the P2 we have double (outa and outb, dira and dirb), but each is only 32 bits -- while our pint #s are from 0 to 63. In my mind, programs should be clear, so it's best to avoid direct use of these registers for simple IO manipulation.

    Interestingly, this actually works in the P2 -- even those the dirb and outb are 32-bit registers:
    con
     
        LED = 56
    
    pub main()
    
      dirb.[LED] := 1
    
      repeat
        waitms(250)
        outb.[LED]!
    
    It works because the interpreter limits the pin number to five bits, so that corrects the value for dirb/outb. Still, this code is easily broken. If you want to toggle a pin in the range of 0..31, you have to change the pin constant and update the code for dira and outa (as you pointed out in your code comments).

    This is why I suggest the built-in pin methods available in Spin2 for basic IO control. We don't have those in the P1, so we're forced to use the direction and output registers. For simplicity of updates, and for clarity for ourselves and others, this little blinker is best (in my opinion) as
    con
     
        LED = 56
    
      repeat
        waitms(250)
        pintoggle(LED)
    
    It's simple, self explanatory, and works with any IO pin with a single edit of the LED constant.

    When moving from P1 to P2, strive to work alike, even if line-by-line you don't look alike. Spin2 is blessed with more power and features, often simplifying code we wrote for the P1.

    Follow-up: Even PASM2 is simpler in this regard. There are single instructions to drive a pin high, low, or make it float, and will accept a pin number from 0 to 63 -- the microcode for each instruction handles the specifics of which register group to use. This makes programs cleaner and easier to write, read, and debug.

    Follow-up #2: I will sometimes allow less-than-clear code when it runs a lot faster. For grins, I used the Propeller to measure the duration of the two mechanisms discussed here. The results are interesting.
      outb.[LED]!     ' 104 clock ticks
      pintoggle(LED)  '  32 clock ticks
    
    I was surprised, but happy. The pintoggle() instruction is 3x faster, while being easier to read and maintain.
  • 💯 Agree with you Jon,

    I totally understand the new pin methods are way better for readability and makes cleaner code, and frankly I find it (Spin 2) better to learn from scratch than Spin 1.

    I wrote that reply because as a :confused: newbie, I am using as a reference a book I have had for about 6 years and I was a bit confused as to why I couldnt make the simple examples (in Spin1) work in Spin 2 using the dir and out instructions used by the book. Fortunately I came across this thread :smiley: and have cleared so far most of my confusion while reading the Google Docs on Spin2 vs the book I have.

    I am just trying to learn and this forum has proven to be great! Btw, thank you for your breakout board!!! I can't believe I have a signed Jon McPhalen board to learn P2!!!! :smile:

    Perhaps in the near future this thread could be the basis for an updated book on Propeller?
  • I've been coding the Propeller since before it was available to the public (I worked for Parallax back then), so I have a LOT of code to translate. The reason I started this thread was to help those going through what I've been doing for the past six months, and to exchange ideas as I still stumble from time-to-time. Yes, there are frustrating days. I've written a few hundred thousand lines of P1 code (a lot of it deployed in commercial apps) and sometimes moving it to the P2 requires new ways of thinking. One of my clients is a laser tag company. Interestingly, doing IR output on the P2 is a lot easier, but IR input on the P2 takes more work due to losing the counters in the cogs. There may be a smart pin mode I can use for that, but I haven't sussed that out yet.

    If you haven't seen it, I wrote an article for Nuts & Volts about the P2 (note that this was early on and there have been a couple changes).
    -- https://www.nutsvolts.com/magazine/article/an-introduction-to-the-parallax-propeller-2

    I also did an early-adopter seminar which is about the Propeller family, with a bit of focus on the P2:
    --

    Thank Parallax for the breakout board -- they did all the hard work. It was something I wanted, so I "sketched" the idea with a PCB program and put it up in the forums. Luckily, my practical development needs are similar to others and the idea kind of took off. My signature on the bottom is a bit of a goof because I am not a famous actor (though I am a member of SAG/AFTRA). I guess if do get famous it will be a good story to tell -- how many actors have their signature on an electronic development board?

    I'm sure the future holds lots of books about the Propeller family. The first step is to let Parallax get the P2 manual out (the P1 manual is really good, I trust the P2 manual will follow suit).
  • Cluso99Cluso99 Posts: 17,474
    edited 2020-11-16 03:59
    Jon,
    You have to become famous so that these boards become collectors items like guitars, clothes, etc. Collectors items usually surge in value when the person passes away, but I expect I'll be long gone before you!
    Have you seen the value of the Apple 1 boards? Ouch! Jobs is gone but Woz is still here.
  • AribaAriba Posts: 2,440
    edited 2020-11-16 04:01
    JonnyMac wrote: »
    ...
    Interestingly, this actually works in the P2 -- even those the dirb and outb are 32-bit registers:
    con
     
        LED = 56
    
    pub main()
    
      dirb.[LED] := 1
    
      repeat
        waitms(250)
        outb.[LED]!
    
    It works because the interpreter limits the pin number to five bits, so that corrects the value for dirb/outb. Still, this code is easily broken. If you want to toggle a pin in the range of 0..31, you have to change the pin constant and update the code for dira and outa (as you pointed out in your code comments).
    ...

    Actually this toggles the LED on pin 56, but it also toggles pin 57. The bits higher than 5 are used to define additional bits (=pins in this case), so dirb.[56] means the same as dirb.[24 addbits 1].

    Just another reason to use the built in pin functions, like pinw / pinwrite or pint / pintoggle.

    Andy
  • Excellent! Thank you for the links!!!
    I guess if do get famous it will be a good story to tell -- how many actors have their signature on an electronic development board?

    Not many I hope!!! That will make my board even more valuable!!! :wink:
  • Actually this toggles the LED on pin 56, but it also toggles pin 57. The bits higher than 5 are used to define additional bits (=pins in this case), so dirb.[56] means the same as dirb.[24 addbits 1].
    Thank you, Andy -- great catch. I didn't see that when I ran the program because pin 57 was defined as an input in this case. In another case, that could have become a problem.
  • JonnyMacJonnyMac Posts: 7,419
    edited 2020-11-16 14:52
    Following up on Andy's eagle-eyed post.

    This syntax..
      outb.[pin]!
    
    ... treats the pin parameter as a bit field. A bit field is a 10-bit value (%00000_00000) with lower five bits (0..31) defining the base bit, and the upper five bits defining the number of bits used to extend the group (0..31). In the case of having the LED constant set to 56, that bit field becomes %00001_11000. What this does is create a mask that starts at bit 24 and extends by one to bit 25. Applying this mask to outb affects IO pins 56 and 57, which correspond to bits 24 and 25 of the outb register. As Andy pointed out, it's equivalent to 24 addbits 1.

    This syntax...
      pintoggle(pin)
    
    ... treats the pin parameter as a pin field. A pin field is an 11-bit value (%00000_000000) with the lower six bits (0..63) defining the base pin, and the upper five bits defining the number of pins used to extend the group. In our example, pin 56 fits into six bits which leaves the upper portion of the pin field 0; so we only affect one pin.

    See pages 6 and 7 of the Spin2 documentation for more details and examples.
    -- https://docs.google.com/document/d/16qVkmA6Co5fUNKJHF6pBfGfDupuRwDtf-wyieh_fbqw/
Sign In or Register to comment.