Assembly Code Examples for the Beginner
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?
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:
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
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  WZ=1 else = 1  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
Comments
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
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
Thanks thats what I was after!
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
A few more ASM examples in the manual would be helpful!
Thanks for your help!
James
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.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
Post Edited (Beau Schwabe (Parallax)) : 8/21/2006 4:19:52 AM GMT
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 Schwabe
IC Layout Engineer
Parallax, Inc.
{ 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
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
Post Edited (Beau Schwabe (Parallax)) : 8/21/2006 7:21:59 PM GMT
Thanks for the post! Very useful!
James
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
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
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] := %11111111 'DEBUG - Make ALL LED's outputs [b]dira[/b][noparse][[/noparse]SLpin] := %1 'Set Shift / Load pin as an output [b]repeat[/b] [b]outa[/b][noparse][[/noparse]SLpin] := %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] := %0 'Set Shift / Load pin in load mode "0" [b]outa[/b][noparse][[/noparse]23..16] := 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 ' │ [b]mov[/b] t3,#5 '───┘ :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
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
Post Edited (Beau Schwabe (Parallax)) : 8/24/2006 4:40:19 AM GMT
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
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....
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
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.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
Post Edited (Beau Schwabe (Parallax)) : 8/22/2006 7:51:54 PM GMT
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
'' 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
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
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
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
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
Post Edited (Beau Schwabe (Parallax)) : 4/21/2007 5:07:01 PM GMT
I'll give this a try tonight. Seems like a lot of people are way ahead of me and I need to catch up.
·Where do the results go ? and how can you "look" at them ?
Thanks , Brian
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
·
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
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.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
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.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
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
·
[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