Assembly Code Examples for the Beginner

JavalinJavalin Posts: 892
edited 2014-01-12 - 20:21:15 in Propeller 1
Morning all,

I've just started into Propeller Assembly and I am having some issues with controlling/reading pins.

I can set a pin high using

But how do I set it LOW?· I would assume from SX assembler you'd need a AND of all the pin bits bar pin10 in this case - i.e %111111111111111011111111111 (etc)· Is there an easier way to do this?

or      dira, SPI_ASM_CLK   ' output
or      outa, SPI_ASM_CLK   ' HIGH
....
SPI_ASM_CLK             long |< 10



I assume the same applies to DIRA and setting input(0) and output(1) modes

Then reading pins - I am using the following code - to read INA state:

                                                mov     input,  #0                         '  Zero input  
                        or      input,  INA                        '  Place the state of all the pins into input
                        and     input,  SPI_ASM_DI                 '  Place the state of SDA into input 
 
                        test    input,  SPI_ASM_DI       WC        '  If input AND SDA = 0 &#61627; WZ=1 else = 1 &#61627; WZ=0
                if_c    shl     spiASMData,#1
                if_c    or      spiASMData,#1
                if_nc   shl     spiASMData,#1
.....
 
SPI_ASM_DI  long |< 11

I get values back from it, but wrong ones.

As always grateful for any help!

James


Moderator Edit: By popular request this thread is being made a sticky.

Post Edited By Moderator (Chris Savage (Parallax)) : 8/21/2006 5:19:48 PM GMT
«134

Comments

  • parskoparsko Posts: 501
    edited 2006-08-20 - 11:37:47
    xor outa, SPI_ASM_CLK 'Toggle SPI_ASM_CLK

    You were off by one letter!

    Read the definition of XOR. Basically, only when both xor'd variable are 0's or 1's, you get a zero. When they are opposite, xor 0 with 1, you get a 1. See page 271 in the manual, then write out (like in 3rd grade arithmatic) the 32 bit "outa" and xor it with 32 bit "SPI_ASM_CLK".

    If OUTA was on/high/1
    OUTA
    0000_0101_0011_0111_0000_0110_0110_0001
    SPI_ASM_CLK--0000_0000_0000_0000_0000_0010_0000_0000
    Result-OUTA----0000_0101_0011_0111_0000_0100_0110_0001

    If OUTA was off/low/0
    OUTA
    0000_0101_0011_0111_0000_0100_0110_0001
    SPI_ASM_CLK--0000_0000_0000_0000_0000_0010_0000_0000
    Result-OUTA----0000_0101_0011_0111_0000_0110_0110_0001



    In assy, it is simply the easiest way to toggle a pin state.

    -Parsko

    Post Edited (parsko) : 8/20/2006 11:42:23 AM GMT
  • JavalinJavalin Posts: 892
    edited 2006-08-20 - 12:32:02
    Parsko,

    Doesn't that mean that i would need to know the state of the pin before I try to set it high or low?

    Im not looking to toggle, but to set the pin state.

    Thanks,

    James
  • Mike GreenMike Green Posts: 22,900
    edited 2006-08-20 - 13:08:52
    Use the ANDN instruction with a bit mask to set the pin low (or direction to input) and the OR instruction with the same bit mask to set the pin high (or direction to output). You can use the XOR instruction (with the same bit mask) to toggle the state of the pin.
  • JavalinJavalin Posts: 892
    edited 2006-08-20 - 13:28:11
    Mike,

    Thanks thats what I was after!

    James
  • parskoparsko Posts: 501
    edited 2006-08-20 - 15:40:08
    James,

    Sorry, I thought you just wanted to toggle it. When you set a pin to output, doesn't mean it is high, only that it is an output. Mike, I think they should add something that explicit in the manual, you said it well.

    -Luke
  • JavalinJavalin Posts: 892
    edited 2006-08-20 - 15:49:36
    Agreed, I did look but could'nt see anything in the manual for this. The ANDN was too well hidden!

    A few more ASM examples in the manual would be helpful!

    Thanks for your help!

    James
  • Beau SchwabeBeau Schwabe Posts: 6,404
    edited 2006-08-21 - 04:11:00
    My personal opinion on this matter is to use a data MASK for the PIN you want to affect via the Propeller MUX commands.



    Example #1 (Set Pin as a OUTPUT - Preset with LOW)
    
                  mov     t1,             #1        wz      '     Configure Pin
                  shl     t1,             Pin               '          Create Mask with t1   
                  muxz    outa,           t1                '          PreSet DataPin LOW           "0"
                  muxnz   dira,           t1                '          Set DataPin to an OUTPUT     "1"
    
    
    
    
    
    Example #2 (Set Pin as a OUTPUT - Preset with HIGH)
    
                  mov     t1,             #1        wz      '     Configure Pin
                  shl     t1,             Pin               '          Create Mask with t1
                  muxnz   outa,           t1                '          PreSet DataPin HIGH          "1"
                  muxnz   dira,           t1                '          Set DataPin to an OUTPUT     "1"
    
    
    
    
    
    Example #3 (Set Pin as a INPUT )
    
                  mov     t1,             #1        wz      '     Configure Pin
                  shl     t1,             Pin               '          Create Mask with t1
                  muxz    dira,           t1                '          Set DataPin to an INPUT      "0"
    
    





    'Pin' holds a value ranging between 0 and 31 corresponding to the I/O pin you want to affect.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    [url=mailto:bschwabe@parallax.com]Beau Schwabe[/url]

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 8/21/2006 4:19:52 AM GMT


    Beau Schwabe -- Submicron Forensic Engineer
    www.Kit-Start.com - bschwabe@Kit-Start.com ෴෴ www.BScircuitDesigns.com - icbeau@bscircuitdesigns.com ෴෴

  • parskoparsko Posts: 501
    edited 2006-08-21 - 07:25:45
    Beau,

    I feel that it is about time that there was an Assembly Tutorial Sticky containing examples just like this, in the same manner that there is a Spin beginners guide. A logical orgainization of it would be nice, but to have one place to look for assy examples would be more nice. I know I could post a few after my "high speed Shiftin" experiences...

    -Parsko
  • Beau SchwabeBeau Schwabe Posts: 6,404
    edited 2006-08-21 - 17:01:20
    I agree with the "Assembly Tutorial Sticky" .... The next post is to expand on the earlier examlpes I gave and to start an Assembly Sticky

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    [url=mailto:bschwabe@parallax.com]Beau Schwabe[/url]

    IC Layout Engineer
    Parallax, Inc.


    Beau Schwabe -- Submicron Forensic Engineer
    www.Kit-Start.com - bschwabe@Kit-Start.com ෴෴ www.BScircuitDesigns.com - icbeau@bscircuitdesigns.com ෴෴

  • Beau SchwabeBeau Schwabe Posts: 6,404
    edited 2006-08-21 - 17:01:52
    Using a data MASK for I/O control via the Propeller MUX commands:


    {
    Example #1 (Set Pin as a OUTPUT - Preset with LOW)
    }
    [b]PUB[/b] start
        [b]cognew[/b](@entry, 0)
    
    [b]DAT[/b]
    
    entry         [b]org[/b]
    
                  [b]mov[/b]     t1,             #1        [b]wz[/b]      '     Configure Pin
                  [b]shl[/b]     t1,             Pin               '          Create Mask with t1  
                  [b]muxz[/b]    [b]outa[/b],           t1                '          PreSet Pin LOW           "0"
                  [b]muxnz[/b]   [b]dira[/b],           t1                '          Set Pin to an OUTPUT     "1"
                                
    Pin           [b]long[/b]      1                               'I/O Pin
    t1            [b]res[/b]       1                               'Pin Mask
    
    
    
    
    
    { 
    Example #2 (Set Pin as a OUTPUT - Preset with HIGH)
    }
    [b]PUB[/b] start
        [b]cognew[/b](@entry, 0)
    
    [b]DAT[/b]
    
    entry         [b]org[/b]
    
                  [b]mov[/b]     t1,             #1        [b]wz[/b]      '     Configure Pin
                  [b]shl[/b]     t1,             Pin               '          Create Mask with t1
                  [b]muxnz[/b]   [b]outa[/b],           t1                '          PreSet Pin HIGH          "1"
                  [b]muxnz[/b]   [b]dira[/b],           t1                '          Set Pin to an OUTPUT     "1"
                                
    Pin           [b]long[/b]      1                               'I/O Pin
    t1            [b]res[/b]       1                               'Pin Mask
    
    
    
    
    { 
    Example #3 (Set Pin as a INPUT )
    }
    [b]PUB[/b] start
        [b]cognew[/b](@entry, 0)
    
    [b]DAT[/b]
    
    entry         [b]org[/b]
    
                  [b]mov[/b]     t1,             #1        [b]wz[/b]      '     Configure Pin
                  [b]shl[/b]     t1,             Pin               '          Create Mask with t1
                  [b]muxz[/b]    [b]dira[/b],           t1                '          Set Pin to an INPUT      "0"
                                
    Pin           [b]long[/b]      1                               'I/O Pin
    t1            [b]res[/b]       1                               'Pin Mask
    
    
    
    
    
    { 
    'Pin' holds a value ranging between 0 and 31 corresponding to the I/O pin you want to affect.
     
    What I have been doing recently is to preserve the "mask" associated to each pin that I am using...
    in this [b]case[/b] 't1'.  By preserving the "mask" you can use other commands to conviently read or write
    to a specific bit.
    }
    
    
    
    
    
    { 
    Example #4 (Set P0 as an INPUT [b]and[/b] Set P16 an OUTPUT ; Show status of P0 on P16)
    }
    [b]PUB[/b] start
        [b]cognew[/b](@entry, 0)
    
    [b]DAT[/b]
    
    entry         [b]org[/b]
    
    Initialize
                  [b]mov[/b]       t1,           #1        [b]wz[/b]      '     Configure Output pin
                  [b]shl[/b]       t1,           #16               '          Create mask with t1      - #P16 LED              
                  [b]muxz[/b]      [b]outa[/b],         t1                '          Preset Output Pin LOW    "0"
                  [b]muxnz[/b]     [b]dira[/b],         t1                '          Set pin as Output        "1"
    
                  [b]mov[/b]       t2,           #1        [b]wz[/b]      '     Configure Input pin
                  [b]shl[/b]       t2,           #0                '          Create mask with t2      - #P0 I/O             
                  [b]muxz[/b]      [b]dira[/b],         t2                '          Set pin as Input         "0"
    Loop
                  [b]test[/b]      t2,           [b]ina[/b]        [b]wc[/b]     '     Read Input pin via 't2' mask
                  [b]muxc[/b]      [b]outa[/b],         t1                '     Write Output pin via 't1' mask
                  [b]jmp[/b]       #Loop
                                
    
    t1            [b]res[/b]       1                               'Output Mask
    t2            [b]res[/b]       1                               'Input Mask
    
    
    
    
    
    {
    Example #5 (Simple ShiftIn routine)
    }
    [b]PUB[/b] start
        [b]cognew[/b](@entry, 0)
    
    [b]DAT[/b]
    
    entry         [b]org[/b]
    
    Initialize
                  [b]mov[/b]       t1,           #1        [b]wz[/b]      '     Configure Output pin
                  [b]shl[/b]       t1,           #1                '          Create mask with t1      - #P1 Clock               
                  [b]muxz[/b]      [b]outa[/b],         t1                '          Preset Output Pin LOW    "0"
                  [b]muxnz[/b]     [b]dira[/b],         t1                '          Set pin as Output        "1"
    
                  [b]mov[/b]       t2,           #1        [b]wz[/b]      '     Configure Input pin
                  [b]shl[/b]       t2,           #0                '          Create mask with t2      - #P0 Data              
                  [b]muxz[/b]      [b]dira[/b],         t2                '          Set pin as Input         "0"
    
                  [b]mov[/b]       t3,           #8                '     Set number of bits to 8
    
    Loop          [b]mov[/b]       t1,           #0      [b]wz[/b],[b]nr[/b]     '     Clock Pin
                  [b]muxz[/b]      [b]outa[/b],         t1                '          Set ClockPin HIGH
                  [b]muxnz[/b]     [b]outa[/b],         t1                '          Set ClockPin LOW
                 
                                                            '     Shift Bits in 
                  [b]test[/b]      t2,           [b]ina[/b]     [b]wc[/b]        '          Read Data Bit into 'C' flag
                  [b]rcl[/b]       t4,           #1                '          rotate "C" flag into return value
    
                  [b]djnz[/b]      t3,           #Loop             '      Decrement t3 ; jump if not Zero
                                
    
    t1            [b]res[/b]       1                               'Clock Mask
    t2            [b]res[/b]       1                               'Data Mask         
    t3            [b]res[/b]       1                               'Number of Bits
    t4            [b]res[/b]       1                               'Received Data
    
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    [url=mailto:bschwabe@parallax.com]Beau Schwabe[/url]

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 8/21/2006 7:21:59 PM GMT


    Beau Schwabe -- Submicron Forensic Engineer
    www.Kit-Start.com - bschwabe@Kit-Start.com ෴෴ www.BScircuitDesigns.com - icbeau@bscircuitdesigns.com ෴෴

  • JavalinJavalin Posts: 892
    edited 2006-08-21 - 17:06:29
    Beau,

    Thanks for the post! Very useful!

    James
  • parskoparsko Posts: 501
    edited 2006-08-21 - 18:50:12
    Beau,

    Please include the minimum SPIN code. Your example is not so obvious to the beginners that other stuff is necessary. I've got an easy one that might be more ahead of what you suggested to start. It will toggle a pin endlessly...

    con
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    pub Toggle_Main
      cognew (@Toggle, 0)
    dat
                            org       0
    Toggle
                  mov     dira,   Pin                'Set Pin to output
                  mov     Time,   cnt                'Place the value of cnt into Time
                  add     Time,   #9                 'Add 9 to time
    :loop
                  waitcnt Time,   Delay              'Set Pin high
                  xor     outa,   Pin                'Toggle Pin
                  waitcnt Time,   Delay              'Set Pin Low
                  xor     outa,   Pin                'Toggle Pin
                  jmp     #:loop
    
    Pin     long            |< 1
    Delay   long            40_000_000
    Time                    res 1
    
    



    The waitcnt command KILLED me for what seemed to be weeks before the idea.gif went off.

    Short of what is happening to waitcnt goes like this, if one were to run through the program line for line:

    con
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    pub Toggle_Main
      cognew (@Toggle, 0)
    dat
                            org       0
    Toggle     'This will indicate cnt=0
                mov     dira,   Pin              'cnt=0-3   Set Pin to output
                mov     Time,   cnt            'cnt=4-7   Place the value of cnt into Time
                add     Time,   #9             'cnt=8-11   Add 9 to time
    :loop
                waitcnt Time,   Delay       'cnt=12-15   wait until the system counter (cnt) is equal to Time
                                                     'then add the value of Delay to Time or Time=Time+Delay or Time=15+40_000_000
                xor     outa,   Pin             'cnt=40_000_016-40_000_019   Toggle Pin
                waitcnt Time,   Delay       'cnt=40_000_020-40_000_023   wait until cnt equals Time
                xor     outa,   Pin             'cnt=80_000_024-80_000_027 or cnt=24-27 because the counter wraps   Toggle Pin
                jmp     #:loop                 'cnt=28-31   Jump to :loop
    
    Pin     long            |< 1
    Delay   long            40_000_000
    Time                    res 1
    
    



    The important note is that you must set the time value of waitcnt before waiting for it. The system will wait until cnt = Time. If your value for Time is less than the system counter at the time the waitcnt command is executed, the COG will pause until CNT wraps around, or about 53 seconds at 80Mhz. So, it sits there waiting, then adds Delay to Time. This is the value that will be used in the NEXT waitcnt command (aka the value used at cnt=40_000_020 above).

    Gotta go set the table, more to come...

    -Parsko
  • Beau SchwabeBeau Schwabe Posts: 6,404
    edited 2006-08-21 - 18:59:20
    Assembly Code Examples for the Beginner: SPI Engine Demo

    This Assembly demo illustrates:

    1) a Simple SPI Engine (Serial Periphial Interface)
    2) Setting up an "Assembly Function" method to be used within Spin
    3) A method for passing variables back-n-forth between Spin and Assembly

    [b]CON[/b]
        [b]_clkmode[/b] = [b]xtal[/b]1 + [b]pll[/b]16x                           
        [b]_xinfreq[/b] = 5_000_000
    
        #0,MSBPRE,LSBPRE,MSBPOST,LSBPOST                                            'Used for SHIFTIN routines
        #4,LSBFIRST,MSBFIRST                                                        'Used for SHIFTOUT routines
        #0,Dpin,Cpin,SLpin,#8,Bits                                                  'Set Dpin,Cpin,SLpin and Bit constant
        
    [b]VAR[/b]     [b]long[/b]          DataValue
    
    [b]OBJ[/b]     SPI     : "SPI Engine"
    
    [b]PUB[/b] start
    {
                            Once called [b]from[/b] Spin, SHIFTIN [b]or[/b] SHIFTOUT remains running in its own COG.
                            [b]If[/b] SHIFTIN [b]or[/b] SHIFTOUT are called with 'Bits' set to Zero, then the COG will shut
                            down.  Another way to shut the COG down is to [b]call[/b] 'stop' from Spin.
    }          
    '------------------------------------------------------------------------------------------------------------------------------
        DataValue := %10001011                                                      'DEBUG - Test value
    
        SPI.SHIFTOUT(Dpin, Cpin, MSBFIRST, Bits, DataValue)                         'Send SHIFTOUT DataVAlue
        
                                                                                    'DEBUG - Tested with 74HCT164
                                                                                    '        for MSBFIRST and LSBFIRST functions
    '------------------------------------------------------------------------------------------------------------------------------
        [b]dira[/b][noparse][[/noparse]23..16&#093; := %11111111                                                   'DEBUG - Make ALL LED's outputs
    
        [b]dira[/b][noparse][[/noparse]SLpin&#093; := %1                                                           'Set Shift / Load pin as an output
        [b]repeat[/b]
          [b]outa[/b][noparse][[/noparse]SLpin&#093; := %1                                                         'Set Shift / Load pin in shift mode "1"
          DataValue := SPI.SHIFTIN(Dpin, Cpin, MSBPRE, Bits)                        'Get SHIFTIN DataValue
          [b]outa[/b][noparse][[/noparse]SLpin&#093; := %0                                                         'Set Shift / Load pin in load mode "0"
          
          [b]outa[/b][noparse][[/noparse]23..16&#093; := DataValue                                                 'DEBUG - Lightup the LED's corresponding to the data
                                                                                    '        LED23 = MSB
                                                                                    '        LED16 = LSB
    '------------------------------------------------------------------------------------------------------------------------------
    {{
                                                                                    
    Notes:
    Consecutive SHIFTIN/SHIFTOUT updates through Spin at 80 MHz is about 14kHz (Bit rate about 112k) 
    
    Consecutive SHIFTIN/SHIFTOUT updates through Assembly at 80 MHz is about 310kHz (Bit rate about 2.5M)
    
    }}
    
    
    



    {
                                    ********************************************
                                                     SPI Engine             V1.1    
                                    ********************************************
                                          coded by Beau Schwabe (Parallax)
                                    ********************************************
    Revision History:
             V1.0   - original program
             
             V1.1   - fixed problem with SHIFTOUT MSBFIRST option
                    - fixed argument allocation in the SPI Engines main loop
    }
    [b]CON[/b]
      #1,_SHIFTOUT,_SHIFTIN
    [b]VAR[/b]
        [b]long[/b]     cog, command, Flag
    [b]PUB[/b] SHIFTOUT(Dpin, Cpin, Mode, Bits, Value)             'Once called from Spin, SHIFTOUT remains running in its own COG.
        [b]if[/b] Flag == 0                                        'If SHIFTOUT is called with 'Bits' set to Zero, then the COG will shut
           start                                            'down.  Another way to shut the COG down is to call 'stop' from Spin.
        setcommand(_SHIFTOUT, @Dpin)
    [b]PUB[/b] SHIFTIN(Dpin, Cpin, Mode, Bits)|Value               'Once called from Spin, SHIFTIN remains running in its own COG.
        [b]if[/b] Flag == 0                                        'If SHIFTIN is called with 'Bits' set to Zero, then the COG will shut
           start                                            'down.  Another way to shut the COG down is to call 'stop' from Spin.
        setcommand(_SHIFTIN, @Dpin)
        [b]result[/b] := Value
    '------------------------------------------------------------------------------------------------------------------------------
    [b]PUB[/b] start : okay
    '' Start SPI Engine - starts a cog
    '' returns false if no cog available
        stop
        Flag := 1
        okay := cog := [b]cognew[/b](@loop, @command) + 1
    [b]PUB[/b] stop
    '' Stop SPI Engine - frees a cog
        Flag := 0
        [b]if[/b] cog
           [b]cogstop[/b](cog~ - 1)
        command~
    [b]PRI[/b] setcommand(cmd, argptr)
        command := cmd << 16 + argptr                       'write command and pointer
        [b]repeat[/b] [b]while[/b] command                                'wait for command to be cleared, signifying receipt
    '################################################################################################################
    [b]DAT[/b]           [b]org[/b]
    '  
    ' SPI Engine - main loop
    '
    loop          [b]rdlong[/b]  t1,[b]par[/b]          [b]wz[/b]                'wait for command
            [b]if_z[/b]  [b]jmp[/b]     #loop
                  [b]movd[/b]    :arg,#arg0                        'get 5 arguments ; arg0 to arg4
                  [b]mov[/b]     t2,t1                             '    &#9474;
                  [b]mov[/b]     t3,#5                             '&#61626;&#9472;&#9472;&#9472;&#9496; 
    :arg          [b]rdlong[/b]  arg0,t2
                  [b]add[/b]     :arg,d0
                  [b]add[/b]     t2,#4
                  [b]djnz[/b]    t3,#:arg
                  [b]mov[/b]     address,t1                        'preserve address location for passing
                                                            'variables back to Spin language.
                  [b]wrlong[/b]  zero,[b]par[/b]                          'zero command to signify command received
                  [b]ror[/b]     t1,#16+2                          'lookup command address
                  [b]add[/b]     t1,#jumps
                  [b]movs[/b]    :table,t1
                  [b]rol[/b]     t1,#2
                  [b]shl[/b]     t1,#3
    :table        [b]mov[/b]     t2,0
                  [b]shr[/b]     t2,t1
                  [b]and[/b]     t2,#$FF
                  [b]jmp[/b]     t2                                'jump to command
    jumps         [b]byte[/b]    0                                 '0
                  [b]byte[/b]    SHIFTOUT_                         '1
                  [b]byte[/b]    SHIFTIN_                          '2
                  [b]byte[/b]    NotUsed_                          '3
    NotUsed_      [b]jmp[/b]     #loop
    '################################################################################################################
    SHIFTOUT_                                               'SHIFTOUT Entry
                  [b]mov[/b]     t4,             arg3      [b]wz[/b]      '     Load number of data bits
        [b]if_z[/b]      [b]jmp[/b]     #Done                             '     '0' number of Bits = Done
                  [b]mov[/b]     t1,             #1        [b]wz[/b]      '     Configure DataPin
                  [b]shl[/b]     t1,             arg0
                  [b]muxz[/b]    [b]outa[/b],           t1                '          PreSet DataPin LOW
                  [b]muxnz[/b]   [b]dira[/b],           t1                '          Set DataPin to an OUTPUT
                  [b]mov[/b]     t2,             #1        [b]wz[/b]      '     Configure ClockPin
                  [b]shl[/b]     t2,             arg1
                  [b]muxz[/b]    [b]outa[/b],           t2                '          PreSet ClockPin LOW
                  [b]muxnz[/b]   [b]dira[/b],           t2                '          Set ClockPin to an OUTPUT
                  [b]sub[/b]     LSBFIRST,       arg2    [b]wz[/b],[b]nr[/b]     '     Detect LSBFIRST mode for SHIFTOUT
        [b]if_z[/b]      [b]jmp[/b]     #LSBFIRST_
                  [b]sub[/b]     MSBFIRST,       arg2    [b]wz[/b],[b]nr[/b]     '     Detect MSBFIRST mode for SHIFTOUT
        [b]if_z[/b]      [b]jmp[/b]     #MSBFIRST_             
                  [b]jmp[/b]     #loop                             '     Go wait for next command
    '------------------------------------------------------------------------------------------------------------------------------
    SHIFTIN_                                                'SHIFTIN Entry
                  [b]mov[/b]     t4,             arg3      [b]wz[/b]      '     Load number of data bits
        [b]if_z[/b]      [b]jmp[/b]     #Done                             '     '0' number of Bits = Done
                  [b]mov[/b]     t1,             #1        [b]wz[/b]      '     Configure DataPin
                  [b]shl[/b]     t1,             arg0
                  [b]muxz[/b]    [b]dira[/b],           t1                '          Set DataPin to an INPUT
                  [b]mov[/b]     t2,             #1        [b]wz[/b]      '     Configure ClockPin
                  [b]shl[/b]     t2,             arg1
                  [b]muxz[/b]    [b]outa[/b],           t2                '          PreSet ClockPin LOW
                  [b]muxnz[/b]   [b]dira[/b],           t2                '          Set ClockPin to an OUTPUT
                  [b]sub[/b]     MSBPRE,         arg2    [b]wz[/b],[b]nr[/b]     '     Detect MSBPRE mode for SHIFTIN
        [b]if_z[/b]      [b]jmp[/b]     #MSBPRE_
                  [b]sub[/b]     LSBPRE,         arg2    [b]wz[/b],[b]nr[/b]     '     Detect LSBPRE mode for SHIFTIN
        [b]if_z[/b]      [b]jmp[/b]     #LSBPRE_
                  [b]sub[/b]     MSBPOST,        arg2    [b]wz[/b],[b]nr[/b]     '     Detect MSBPOST mode for SHIFTIN
        [b]if_z[/b]      [b]jmp[/b]     #MSBPOST_
                  [b]sub[/b]     LSBPOST,        arg2    [b]wz[/b],[b]nr[/b]     '     Detect LSBPOST mode for SHIFTIN
        [b]if_z[/b]      [b]jmp[/b]     #LSBPOST_
                  [b]jmp[/b]     #loop                             '     Go wait for next command
    '------------------------------------------------------------------------------------------------------------------------------              
    MSBPRE_                                                 '     Receive Data MSBPRE
    MSBPRE_Sin    [b]test[/b]    t1,             [b]ina[/b]     [b]wc[/b]        '          Read Data Bit into 'C' flag
                  [b]rcl[/b]     t3,             #1                '          rotate "C" flag into return value
                  [b]call[/b]    #Clock                            '          Send clock pulse
                  [b]djnz[/b]    t4,             #MSBPRE_Sin       '          Decrement t4 ; jump if not Zero
                  [b]jmp[/b]     #Update_SHIFTIN                   '     Pass received data to SHIFTIN receive variable
    '------------------------------------------------------------------------------------------------------------------------------              
    LSBPRE_                                                 '     Receive Data LSBPRE
    LSBPRE_Sin    [b]test[/b]    t1,             [b]ina[/b]       [b]wc[/b]      '          Read Data Bit into 'C' flag
                  [b]rcr[/b]     t3,             #1                '          rotate "C" flag into return value
                  [b]call[/b]    #Clock                            '          Send clock pulse
                  [b]djnz[/b]    t4,             #LSBPRE_Sin       '     Decrement t4 ; jump if not Zero
                  [b]mov[/b]     t4,             #32               '     For LSB shift data right 32 - #Bits when done
                  [b]sub[/b]     t4,             arg3
                  [b]shr[/b]     t3,             t4
                  [b]jmp[/b]     #Update_SHIFTIN                   '     Pass received data to SHIFTIN receive variable
    '------------------------------------------------------------------------------------------------------------------------------
    MSBPOST_                                                '     Receive Data MSBPOST
    MSBPOST_Sin   [b]call[/b]    #Clock                            '          Send clock pulse
                  [b]test[/b]    t1,             [b]ina[/b]     [b]wc[/b]        '          Read Data Bit into 'C' flag
                  [b]rcl[/b]     t3,             #1                '          rotate "C" flag into return value
                  [b]djnz[/b]    t4,             #MSBPOST_Sin      '          Decrement t4 ; jump if not Zero
                  [b]jmp[/b]     #Update_SHIFTIN                   '     Pass received data to SHIFTIN receive variable
    '------------------------------------------------------------------------------------------------------------------------------
    LSBPOST_                                                '     Receive Data LSBPOST
    LSBPOST_Sin   [b]call[/b]    #Clock                            '          Send clock pulse
                  [b]test[/b]    t1,             [b]ina[/b]       [b]wc[/b]      '          Read Data Bit into 'C' flag
                  [b]rcr[/b]     t3,             #1                '          rotate "C" flag into return value
                  [b]djnz[/b]    t4,             #LSBPOST_Sin      '          Decrement t4 ; jump if not Zero
                  [b]mov[/b]     t4,             #32               '     For LSB shift data right 32 - #Bits when done
                  [b]sub[/b]     t4,             arg3
                  [b]shr[/b]     t3,             t4
                  [b]jmp[/b]     #Update_SHIFTIN                   '     Pass received data to SHIFTIN receive variable
    '------------------------------------------------------------------------------------------------------------------------------
    LSBFIRST_                                               '     Send Data LSBFIRST
                  [b]mov[/b]     t3,             arg4              '          Load t3 with DataValue
    LSB_Sout      [b]test[/b]    t3,             #1      [b]wc[/b]       '          Test LSB of DataValue
                  [b]muxc[/b]    [b]outa[/b],           t1                '          Set DataBit HIGH or LOW
                  [b]shr[/b]     t3,             #1                '          Prepare for next DataBit
                  [b]call[/b]    #Clock                            '          Send clock pulse
                  [b]djnz[/b]    t4,             #LSB_Sout         '          Decrement t4 ; jump if not Zero
                  [b]mov[/b]     t3,             #0      [b]wz[/b]        '          Force DataBit LOW
                  [b]muxnz[/b]   [b]outa[/b],           t1
                  [b]jmp[/b]     #loop                             '     Go wait for next command
    '------------------------------------------------------------------------------------------------------------------------------
    MSBFIRST_                                               '     Send Data MSBFIRST
                  [b]mov[/b]     t3,             arg4              '          Load t3 with DataValue
                  [b]mov[/b]     t5,             #%1               '          Create MSB mask     ;     load t5 with "1"
                  [b]shl[/b]     t5,             arg3              '          Shift "1" N number of bits to the left.
                  [b]shr[/b]     t5,             #1                '          Shifting the number of bits left actually puts
                                                            '          us one more place to the left than we want. To
                                                            '          compensate we'll shift one position right.              
    MSB_Sout      [b]test[/b]    t3,             t5      [b]wc[/b]        '          Test MSB of DataValue
                  [b]muxc[/b]    [b]outa[/b],           t1                '          Set DataBit HIGH or LOW
                  [b]shr[/b]     t5,             #1                '          Prepare for next DataBit
                  [b]call[/b]    #Clock                            '          Send clock pulse
                  [b]djnz[/b]    t4,             #MSB_Sout         '          Decrement t4 ; jump if not Zero
                  [b]mov[/b]     t3,             #0      [b]wz[/b]        '          Force DataBit LOW
                  [b]muxnz[/b]   [b]outa[/b],           t1
                  
                  [b]jmp[/b]     #loop                             '     Go wait for next command
    '------------------------------------------------------------------------------------------------------------------------------
    Update_SHIFTIN
                  [b]mov[/b]     t1,             address           '     Write data back to Arg4
                  [b]add[/b]     t1,             #16               '          Arg0 = #0 ; Arg1 = #4 ; Arg2 = #8 ; Arg3 = #12 ; Arg4 = #16
                  [b]wrlong[/b]  t3,             t1
                  [b]jmp[/b]     #loop                             '     Go wait for next command
    '------------------------------------------------------------------------------------------------------------------------------
    Clock
                  [b]mov[/b]     t2,             #0      [b]wz[/b],[b]nr[/b]     '     Clock Pin
                  [b]muxz[/b]    [b]outa[/b],           t2                '          Set ClockPin HIGH
                  [b]muxnz[/b]   [b]outa[/b],           t2                '          Set ClockPin LOW
    Clock_ret     [b]ret[/b]                                       '          return
    '------------------------------------------------------------------------------------------------------------------------------
    Done                                                    '     Shut COG down
                  [b]mov[/b]     t2,             #0                '          Preset temp variable to Zero
                  [b]mov[/b]     t1,             [b]par[/b]               '          Read the address of the first perimeter
                  [b]add[/b]     t1,             #4                '          Add offset for the second perimeter ; The 'Flag' variable
                  [b]wrlong[/b]  t2,             t1                '          Reset the 'Flag' variable to Zero
                  [b]CogID[/b]   t1                                '          Read CogID
                  [b]COGSTOP[/b] t1                                '          Stop this Cog!
    '------------------------------------------------------------------------------------------------------------------------------
    {
    ########################### Defined data ###########################
    }
    zero                    [b]long[/b]    0                       'constants
    d0                      [b]long[/b]    $200
    
    MSBPRE                  [b]long[/b]    $0                      '          Applies to SHIFTIN
    LSBPRE                  [b]long[/b]    $1                      '          Applies to SHIFTIN
    MSBPOST                 [b]long[/b]    $2                      '          Applies to SHIFTIN
    LSBPOST                 [b]long[/b]    $3                      '          Applies to SHIFTIN
    LSBFIRST                [b]long[/b]    $4                      '          Applies to SHIFTOUT
    MSBFIRST                [b]long[/b]    $5                      '          Applies to SHIFTOUT
    {
    ########################### Undefined data ###########################
    }
                                                            'temp variables
    t1                      [b]res[/b]     1                       '     Used for DataPin mask     and     COG shutdown 
    t2                      [b]res[/b]     1                       '     Used for CLockPin mask    and     COG shutdown
    t3                      [b]res[/b]     1                       '     Used to hold DataValue SHIFTIN/SHIFTOUT
    t4                      [b]res[/b]     1                       '     Used to hold # of Bits
    t5                      [b]res[/b]     1                       '     Used for temporary data mask
    address                 [b]res[/b]     1                       '     Used to hold return address of first Argument passed
    
    arg0                    [b]res[/b]     1                       'arguments passed to/from high-level Spin
    arg1                    [b]res[/b]     1
    arg2                    [b]res[/b]     1
    arg3                    [b]res[/b]     1
    arg4                    [b]res[/b]     1
    
    
    







    Edit: updated SPI engine....
    fixed problem with SHIFTOUT MSBFIRST option
    fixed argument allocation in the SPI Engines main loop

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    [url=mailto:bschwabe@parallax.com]Beau Schwabe[/url]

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 8/24/2006 4:40:19 AM GMT




    Beau Schwabe -- Submicron Forensic Engineer
    www.Kit-Start.com - bschwabe@Kit-Start.com ෴෴ www.BScircuitDesigns.com - icbeau@bscircuitdesigns.com ෴෴

  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 22,361
    edited 2006-08-21 - 20:07:00
    Beau (and others),

    I think we need to get this copyright thing straightened out. A lot of Propeller code origniating from Parallax (like the SPI Engine above) includes a copyright notice, but no licensing terms. The rules for the Propeller Object Exchange state "no copyrights", which implies public domain (and which is the reason I've never posted anything there). I think a copyright is important, along with a reasonable license for sharing the code. I prefer the General Public License (GPL) for this, and any significant code that I've originated and posted in various forum threads contains those terms of use.

    The reasons for having a copyright are several:

    1. It allows one to control the attributions that appear in the code, yielding a provenance than can be traced back to the originator.

    2. It prevents someone else from copyiing the code and claiming it as their own, as could happen with code in the public domain.

    3. It allows one to set restrictions on how the code may be used and distributed, keeping it "free", for example, as the GPL does.

    But if copyrighted code is meant to be shared, it needs to say so, even though that intent in the context of the forum might seem clear. Once a program is copied from the forum context, any implied consent disappears. My vote would be to standardize on the GPL for shared Propeller code and to remove the "no copyrights" restriction from the object exchange in favor of that license.

    I don't mean to stir up a hornet's nest here, but there are presently some inconsistencies and ambiguities that leave me a little uneasy.

    Thanks,
    Phil
    “Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away. -Antoine de Saint-Exupery
  • Beau SchwabeBeau Schwabe Posts: 6,404
    edited 2006-08-21 - 20:21:14
    Phil,

    You bring up a good point. I will find out what the proper "Header" should look like in released code from Parallax.
    What I have been using for the Header of objects that I have been posting originates from objects such as 'Keyboard.spin', 'Mouse.spin', etc....

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    [url=mailto:bschwabe@parallax.com]Beau Schwabe[/url]

    IC Layout Engineer
    Parallax, Inc.


    Beau Schwabe -- Submicron Forensic Engineer
    www.Kit-Start.com - bschwabe@Kit-Start.com ෴෴ www.BScircuitDesigns.com - icbeau@bscircuitdesigns.com ෴෴

  • Beau SchwabeBeau Schwabe Posts: 6,404
    edited 2006-08-22 - 19:42:28
    It took me a little while to figure this out, so I thought I would pass this along as an Assembler "trap"


    Notice, Marker #1 that starts a cognew at TestMode2...

    Running this test I would expect LED #16 and LED #17 to turn on.
    The actual result is that none of the LED's turn on.


    You could change Marker #1 so that it starts a cognew at TestMode1,
    and then both LED's would turn on. However for illustration purposes,
    TestMode1 happens to do something similar to TestMode2, but it could
    be anything.

    Remarking the 'call' statement on Marker #4 will cause LED #17 to turn
    on as expected and LED #16 will remain off ... as expected. SO why, since
    the section between Marker 5&6 is the same as the section between 7&8, won't
    this program run the way that it is without remarking the 'call' on Marker #4?

    The answer is the way that 'cognew' needs to load your assembly program.
    The proper way is to load or start the cog at the top most assembly code
    position. In this case the line at Marker #1 should read...

    cognew(@TestMode1,0)
    
    



    ...Now at TestMode1 if you want to run or start at TestMode2 then you need a
    'jmp' at TestMode1 that points to TestMode2




    One more thing to watch out for that has to do with the 'call' function mentioned
    in the book is the way that the 'ret' gets encoded into the returning address.
    If the lines between Markers 8&9 looked like this...

    #8
                     ret
       InitializePins_ret
    #9
    
    



    ... Then when 'InitializePins' was actually called, this procedure would not know
    where to return. Instead 'ret' MUST be on the same line as the '{Routine}_ret'
    in order to properly execute.


    attachment.php?attachmentid=42959

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    [url=mailto:bschwabe@parallax.com]Beau Schwabe[/url]

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 8/22/2006 7:51:54 PM GMT
    937 x 639 - 114K


    Beau Schwabe -- Submicron Forensic Engineer
    www.Kit-Start.com - bschwabe@Kit-Start.com ෴෴ www.BScircuitDesigns.com - icbeau@bscircuitdesigns.com ෴෴

  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 22,361
    edited 2006-08-22 - 21:47:41
    Beau,

    You can also pile up return labels in a subroutine with multiple entry points (or even for multiple subroutines, for that matter, if memory is really tight):

    Sub_entry1
                   ...Entry1 stuff...
                   [b]jmp[/b] #Common
    
    Sub_entry2
                   ...Entry2 stuff...
    
    Common
                   ...Common stuff...
    
    Sub_entry1_ret
    Sub_entry2_ret [b]ret[/b]
    
    



    Also, if you need to branch to a subroutine's return point, you don't need to do an immediate jmp. An indirect jmp not only works fine, but saves four clocks:

    Sub
               ...
               [b]jmp[/b]  Sub_ret
               ...
    Sub_ret    [b]ret[/b]
    
    


    The reason this works is that the return address is stored in the src field of the "return" instruction. The indirect jump just picks it up and uses it, instead of going to Sub_ret first.

    -Phil
    “Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away. -Antoine de Saint-Exupery
  • Mike GreenMike Green Posts: 22,900
    edited 2006-08-22 - 22:52:01
    '' Here's a simple example of table lookup in assembly.
    '' This particular example takes a table index in "ptr"
    '' and sets the "data" to the word value from the table.
    '' There's no range checking.  If you want long values,
    '' just eliminate the shifts and masks (*).  You can use
    '' the same kind of logic for byte values.
    
    DAT
                org   0
    test      mov   ptr,#2        ' for an example, get third value
                call  #look            ' note table indices are 0 to n-1
    :stop     jmp   #:stop        ' no checking for out of range
    
    look         mov    data,ptr      ' ptr is a table index (0 to n-1)
                  shr   data,#1       ' divide input value by 2 to get (*)
                  add    data,#table    '   long word index, add table address
                  movs   :inline,data   ' have to use instruction modification
                  nop                        ' need pause here for pipelining
    :inline    mov    data,0-0         ' get long value from table
                  test   ptr,#1   wz     ' do we want odd or even half  (*)
        if_z     and    data,mask       ' if even, take lower 16 bits (*)
        if_nz   shr    data,#16         ' if odd, take upper 16 bits  (*)
    look_ret   ret
    table       word    $0000
                  word    $C0C1
                  word    $C181
                  word    $0140
                  word    $C301
                  word    $03C0
                  word    $0280
    mask        long   $FFFF
    data        res     1
    ptr          res     1
    
    

    Post Edited (Mike Green) : 8/23/2006 3:37:17 PM GMT
  • Beau SchwabeBeau Schwabe Posts: 6,404
    edited 2006-08-30 - 15:35:40
    I had mentioned earlier that I preferred using the Propeller MUX operator to affect an I/O pin.
    I am still with this belief, however it is application dependent. There are certainly other
    times that it is better to use 'or' or 'andn' to affect an I/O bit. The savings can be a few
    clock cycles and an extra programming 'long' ...for large applications this can make a big
    difference. On smaller applications sometimes you just want to get the job done and it doesn't
    really matter one way or the other.

    Typically to affect an I/O pin on a 32-bit register you need to create what's known as a 'mask'
    To set up a mask simply initialize a mask variable to "1" and then shift that variable left by
    the number of bits you wish. For an I/O this translates to the pin you want to access.

    Example1:

                  [b]mov[/b]     PinMask,            #1                '     Set Bit
                  [b]shl[/b]     PinMask,            IOpin                '     Create PinMask
    
    
    




    To define an I/O pin as an input we can use the 'andn' operator AFTER we have created a 'mask'.

    Example2:

                  [b]andn[/b]    [b]dira[/b],            PinMask                '     Make Pin an Input
    
    
    



    By using the 'or' operator instead, we can define the I/O pin as an output.

    Example3:

                  [b]or[/b]      [b]dira[/b],            PinMask                '     Make Pin an Output
    
    
    



    Using the same 'or' operator and directing it toward 'outa' instead of 'dira' we can define the
    pin state as HIGH if we also make the I/O pin an output.

    Example4:

                  [b]or[/b]      [b]outa[/b],            PinMask                '     Make Pin HIGH
    
    
    



    Likewise if we use the 'andn' operator we can define the pin state as being LOW if we also make
    the I/O pin an output.

    Example5:

                  [b]andn[/b]    [b]outa[/b],            PinMask                '     Make Pin LOW
    
    
    




    Suppose we want to affect an I/O pin based on a variable or another I/O pin. One way to do it
    using the 'andn' and 'or' operators is to test the variable or pin with a 'mask' and write to
    the 'z' flag using the 'if_z' and 'if_nz' conditionals.

    Example6:

                  [b]mov[/b]     InPinMask,        #1                '     Set Bit
                  [b]shl[/b]     InPinMask,              InPin                       '     Create PinMask
                  [b]andn[/b]    [b]dira[/b],                     InPinMask                       '     Make Pin an input
    
                  [b]mov[/b]     OutPinMask,        #1                '     Set Bit
                  [b]shl[/b]     OutPinMask,              OutPin                       '     Create PinMask
                  [b]or[/b]      [b]dira[/b],                     OutPinMask                      '     Make Pin an output
    
    
                  [b]test[/b]    InPinMask,                [b]ina[/b]          [b]wz[/b]                 '     Test InpinMask against the I/O port
            [b]if_z[/b]  [b]andn[/b]    [b]outa[/b],                     OutPinMask                      '          If InPin = 0 set OutPin - LOW
            [b]if_nz[/b] [b]or[/b]      [b]outa[/b],                     OutPinMask                      '          If InPin = 1 set OutPin - HIGH
    
    
    




    Another way to accomplish the same task is to use the 'mux' operator.

    Example7:

                  [b]mov[/b]     InPinMask,        #1                '     Set Bit
                  [b]shl[/b]     InPinMask,              InPin                       '     Create PinMask
                  [b]andn[/b]    [b]dira[/b],                     InPinMask                       '     Make Pin an input
    
                  [b]mov[/b]     OutPinMask,        #1                '     Set Bit
                  [b]shl[/b]     OutPinMask,              OutPin                       '     Create PinMask
                  [b]or[/b]      [b]dira[/b],                     OutPinMask                      '     Make Pin an output
    
                  [b]test[/b]    InPinMask,                [b]ina[/b]          [b]wz[/b]                 '     Test InpinMask against the I/O port
                  [b]muxnz[/b]   [b]outa[/b],                     OutPinMask                      '          If InPin = 0 set OutPin - LOW
                                                                                    '          If InPin = 1 set OutPin - HIGH
    
    
    



    Now, this may not seem like a huge amount of savings, but this type of scenario has a way of
    multiplying itself as a program gets larger, and it can make a difference in the long run with
    some applications. Example 8 is a complete Propeller assembly program of Example 7.

    Example8: I/O follower

    [b]PUB[/b] start
        [b]cognew[/b](@entry, 0)
    
    [b]DAT[/b]
    
    entry         [b]org[/b]
    
                  [b]mov[/b]     InPinMask,                #1                              '     Set Bit
                  [b]shl[/b]     InPinMask,                InPin                           '     Create PinMask
                  [b]andn[/b]    [b]dira[/b],                     InPinMask                       '     Make Pin an input                     
    
                  [b]mov[/b]     OutPinMask,               #1                              '     Set Bit
                  [b]shl[/b]     OutPinMask,               OutPin                          '     Create PinMask
                  [b]or[/b]      [b]dira[/b],                     OutPinMask                      '     Make Pin an output
    
    loop          [b]test[/b]    InPinMask,                [b]ina[/b]     [b]wz[/b]                      '     Test Data bit at BitMask position
                  [b]muxnz[/b]   [b]outa[/b],                     OutPinMask                      '          If InPin = 0 set OutPin - LOW
                                                                                    '          If InPin = 1 set OutPin - HIGH
                  [b]jmp[/b]       #loop 
    
    
    InPin         [b]long[/b]      0                            'I/O pin 0
    OutPin        [b]long[/b]      16                            'I/o PIN 16 (DEMO Board LED)
     
    InPinMask     [b]res[/b]       1
    OutPinMask    [b]res[/b]       1
    
    
    





    Example9: MSB - SHIFTIN

    [b]PUB[/b] start
        [b]cognew[/b](@entry, 0)
    
    [b]DAT[/b]
    
    entry         [b]org[/b]
    
    
    Initialize
                  [b]mov[/b]       ClockPinMask,           #1        [b]wz[/b]                    '     Configure Output pin
                  [b]shl[/b]       ClockPinMask,           ClockPin                        '          Create mask with ClockPin               
                  [b]andn[/b]      [b]outa[/b],                   ClockPinMask                    '          Preset Output Pin LOW    "0"
                  [b]or[/b]        [b]dira[/b],                   ClockPinMask                    '          Set pin as Output        "1"
    
                  [b]mov[/b]       DataPinMask,            #1        [b]wz[/b]                    '     Configure Input pin
                  [b]shl[/b]       DataPinMask,            DataPin                         '          Create mask with DataPin              
                  [b]andn[/b]      [b]dira[/b],                   DataPinMask                     '          Set pin as Input         "0"
    
    SHIFTIN       [b]mov[/b]       DataBitMask,            #1                              '     Create BitMask
                  [b]shl[/b]       DataBitMask,            DataBits                        '     Set number of Bits
                  [b]mov[/b]       Data,                   #0                              '     Clear Data
    _ReadNextBit  [b]or[/b]        [b]outa[/b],                   ClockPinMask                    '     Set Clock pin HIGH  - Start Clock
                  [b]test[/b]      DataPinMask,            [b]ina[/b]     [b]wc[/b]                      '     Load "C" with DataPin value
                  [b]muxc[/b]      Data,                   DataBitMask                     '     Move "C" into Data via DataBitMask position
                  [b]andn[/b]      [b]outa[/b],                   ClockPinMask                    '     Set Clock pin LOW   - End Clock
                  [b]shr[/b]       DataBitMask,            #1      [b]wz[/b]                      '     Move BitMask position right by 1           '     
            [b]if_nz[/b] [b]jmp[/b]       #_ReadNextBit                                           '     Jump to '_ReadNextBit' if there are more bits 
    
    
    ClockPin      [b]long[/b]      0                                                       '     Define I/O Clock Pin
    DataPin       [b]long[/b]      1                                                       '     Define I/O Data Pin
    DataBits      [b]long[/b]      7                                                       '     Set number of Data Bits ( N = Bits - 1 = [noparse][[/noparse]0 to 31] )  
     
    ClockPinMask  [b]res[/b]       1                                                       '     ClockPin Mask
    DataPinMask   [b]res[/b]       1                                                       '     DataPin Mask
    DataBitMask   [b]res[/b]       1                                                       '     Data Mask
    Data          [b]res[/b]       1                                                       '     Holds result value from SHIFTIN
    
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    [url=mailto:bschwabe@parallax.com]Beau Schwabe[/url]

    IC Layout Engineer
    Parallax, Inc.


    Beau Schwabe -- Submicron Forensic Engineer
    www.Kit-Start.com - bschwabe@Kit-Start.com ෴෴ www.BScircuitDesigns.com - icbeau@bscircuitdesigns.com ෴෴

  • LucidGuppyLucidGuppy Posts: 32
    edited 2006-11-08 - 03:25:22
    How would I write an assembler routine that would add two numbers and return the result back to the spin object?
  • Beau SchwabeBeau Schwabe Posts: 6,404
    edited 2006-11-08 - 04:38:56
    UPDATE TO THIS POST!!!····· 04 - 21 - 2007

    please see the following thread for an update to this post

    http://forums.parallax.com/showthread.php?p=646675







    LucidGuppy,

    This example will launch a cog just to run the Assembly, then add the two numbers and stop the cog after returning the result.

    [b]PUB[/b] AddNums(Num1,Num2)
        Num1_ := Num1
        Num2_ := Num2
        [b]cognew[/b](@entry, @Num1)
    
    [b]DAT[/b]
                            [b]org[/b]
    entry                   [b]add[/b]     Num1_,Num2_     'Add Num1 to Num2
                            [b]wrlong[/b]  Num1_, [b]par[/b]      'Return result to Num1
                            [b]CogId[/b]   CogNum          'Get COG ID
                            [b]CogStop[/b] CogNum          'Stop this COG
    
    CogNum                  [b]long[/b]    0               'Reserved variables
    Num1_                   [b]long[/b]    0
    Num2_                   [b]long[/b]    0
    
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    [url=mailto:bschwabe@parallax.com]Beau Schwabe[/url]

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 4/21/2007 5:07:01 PM GMT


    Beau Schwabe -- Submicron Forensic Engineer
    www.Kit-Start.com - bschwabe@Kit-Start.com ෴෴ www.BScircuitDesigns.com - icbeau@bscircuitdesigns.com ෴෴

  • LucidGuppyLucidGuppy Posts: 32
    edited 2006-11-08 - 12:32:41
    Thanks Beau,

    I'll give this a try tonight. Seems like a lot of people are way ahead of me and I need to catch up.
  • [Deleted User][Deleted User] Posts: 0
    edited 2006-12-04 - 17:38:48
    Beau ,

    ·Where do the results go ? and how can you "look" at them ?

    Thanks , Brian
  • BeanBean Posts: 7,991
    edited 2006-12-04 - 17:57:52
    Beau,
    How long does it take to start/stop a new cog with an assembly program ?
    If I have a routine that isn't fast enough in spin, I know it would be faster in assembly, but I don't know what the time delay is to launch a new cog.

    Bean.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Cheap used 4-digit LED display with driver IC·www.hc4led.com

    Low power SD Data Logger www.sddatalogger.com
    SX-Video Display Modules www.sxvm.com
    Stuff I'm selling on ebay http://search.ebay.com/_W0QQsassZhittconsultingQQhtZ-1

    "People who are willing to trade their freedom for·security deserve neither and will lose both." Benjamin Franklin
    ·
  • Mike GreenMike Green Posts: 22,900
    edited 2006-12-04 - 17:58:57
    Brian,
    The results (_Num1) are computed in a memory location within the cog's memory space (512 32-bit words). The main memory which
    is used by the SPIN interpreter is separate. To the program running in the cog, the main memory looks a bit like an I/O device with
    special instructions needed to access it. In particular, the "wrlong <cog address>,<hub address>" instruction writes the contents of
    the specified cog location into a main (hub) memory location whose address is in a cog location (in this case, the PAR register). The PAR
    register is a read-only cog memory location that contains a hub memory long-word address specified when the cog was started (by the
    COGINIT/COGNEW instruction).

    You can only "look" at the results of an assembly program by the assembly program itself copying the results into the common hub memory
    for other programs (whether in SPIN or assembly) to examine. You could also send the results to an I/O device by manipulating the I/O pins.
    Mike
  • Beau SchwabeBeau Schwabe Posts: 6,404
    edited 2006-12-05 - 07:02:50
    truckwiz,

    If you treat 'AddNums(Num1,Num2)' as a function, then when you call it with something like...

    Answer := AddNums(Num1,Num2)

    Your result will be returned in the variable 'Answer'

    To 'look' at them, use any of the other objects available to display them to a TV,VGA,SERIAL,LED's(binary form) etc.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    [url=mailto:bschwabe@parallax.com]Beau Schwabe[/url]

    IC Layout Engineer
    Parallax, Inc.


    Beau Schwabe -- Submicron Forensic Engineer
    www.Kit-Start.com - bschwabe@Kit-Start.com ෴෴ www.BScircuitDesigns.com - icbeau@bscircuitdesigns.com ෴෴

  • Beau SchwabeBeau Schwabe Posts: 6,404
    edited 2006-12-05 - 07:59:11
    Bean,

    I've been in Rocklin and Tahoe all of this weekend, and while I was in the Parallax office on Friday and Monday, I was busy going over layout ideas with Chip
    and did not have a chance to do any timing tests with the Propeller that would answer your question.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    [url=mailto:bschwabe@parallax.com]Beau Schwabe[/url]

    IC Layout Engineer
    Parallax, Inc.


    Beau Schwabe -- Submicron Forensic Engineer
    www.Kit-Start.com - bschwabe@Kit-Start.com ෴෴ www.BScircuitDesigns.com - icbeau@bscircuitdesigns.com ෴෴

  • BeanBean Posts: 7,991
    edited 2006-12-05 - 14:03:55
    Beau,
    I didn't actually measure the time, but it's pretty fast (much faster than the same code in spin).

    I was thinking it had to load the cog from EEPROM, but now that I think about it, I guess it just loads from hub memory into cog memory (which is much faster than pulling the data from the EEPROM).

    Bean.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Cheap used 4-digit LED display with driver IC·www.hc4led.com

    Low power SD Data Logger www.sddatalogger.com
    SX-Video Display Modules www.sxvm.com
    Stuff I'm selling on ebay http://search.ebay.com/_W0QQsassZhittconsultingQQhtZ-1

    "People who are willing to trade their freedom for·security deserve neither and will lose both." Benjamin Franklin
    ·
  • parskoparsko Posts: 501
    edited 2006-12-10 - 01:23:45
    This is kind of a trap, I suppose. The following pertinent inputs:
    [b]VAR[/b]
        [b]byte[/b] array[noparse][[/noparse]100]
    [b]PUB[/b]
        [b]cognew[/b](@assembly, @second)
    [b]DAT[/b]
        Assembly
        .
        .
        .
    
    
    



    This code is legal. But any new cognew's that occur, must have a PARameter registered to some value of the array/4. Let me say that another way, it's late. I used "@second[noparse][[/noparse]4];" for my PARameter. I could have easily used "@second[noparse][[/noparse]0];" or "@second[noparse][[/noparse]8];". The cognew requeires the start of a long for it's parameter. So, if you want to use "@second[noparse][[/noparse]2];" it will still start at second[noparse][[/noparse]0]. Subsequently, @second[noparse][[/noparse]6]; will start at @second[noparse][[/noparse]4]; {The less obvious concept I'm pointing out}

    -Parsko

    Post Edited (parsko) : 12/10/2006 1:27:55 AM GMT
  • Mike GreenMike Green Posts: 22,900
    edited 2006-12-10 - 02:29:09
    Because of the way the COGINIT/COGNEW assembly instruction works, there is only a 14 bit field available for initializing the PAR register. Because this value is most likely to be used as the address of a long, it appears in the PAR register shifted left by 2 bits (a multiple of 4). SPIN makes the same assumption and throws away the low order 2 bits of the address supplied. The Propeller Manual says this several times, but it is a trap for the unwary.
Sign In or Register to comment.