Please notice that my use of a HUB memory register to exchange information between COGS in my last program posting was disapproved of with some emphasis. (Though how to do just that has just been discussed).
No it has not "just been discussed".
It has been discussed in over a dozen posts in this thread because the idea is a core concept.
Heater:
Please notice that my use of a HUB memory register to exchange information between COGS in my last program posting was disapproved of with some emphasis.
I didn't take any of the above posts as disapproval to use HUB memory to share information between cogs. In fact, according of one of the other threads that's pretty much the main way of doing it.
What I understood disapproval was aimed at was the arbitrary choice of a HUB memory address when there is a mechanism to do this via PAR.
I saw someone call PAR a register only and that over-writing it doesn't work because it's "read-only" or something.
I thought that PAR was a compiler symbol that was evaluated at the time of assembly? I don't see any reason why anything other than the compiler would need to even know about the symbol.
I thought that PAR was a compiler symbol that was evaluated at the time of assembly? I don't see any reason why anything other than the compiler would need to even know about the symbol.
Consider the following fragment:
PUB start(parameter)
return cognew(@entry, parameter)
DAT org 0
entry rdlong :tmp, par
...
:tmp res 1
Ah yes, the value of parameter... which is only known at runtime.
I'm guessing you'd use this kind of idiom for PASM's version of a dispatch table?
If I'm completely 100% wrong btw, I'll continue to point out that I am still a newbie :-) I have exactly 170 lines of code (combined SPIN and ASM to my name).
It's quite simple really. par is one (the first, $1F0) of the 16 special purpose register living in each cog. The parameter passed to coginit/cognew has its bits 15..2 extracted (we only have space for 14bit) and reappears in par[15..2]. All other bits in par are cleared. Whatever you pass down to the cog is up to you. It can be an address and it can be anything else provided it fit's into 14bit (it's basically down to interpretation). For example, you could pass a pin number by reference (rdlong pin, par) or pass the pin number itself shifted by 2 and extract it inside the cog with mov pin, par followed by shr pin, #2.
If par is used in the source slot of an instruction you'll always get the same value for the lifetime of the cog (that's the read-only bit). Using it in the destination slot will access the shadow register which you are free to modify.
So, check my logic for me here - I'm making assumptions.
So, you said that the two least significant bits are always zero'd when you write to PAR. Given the use-case that PAR is frequently used to pass HUB memory references and HUB memory is in longs I'm wanting to jump to a conclusion...
You see where my brain is going? By zeroing the two least significant bits PASM is forcing the value of PAR to always align with addresses which are multiples of 4 - ie longs.
You see where my brain is going? By zeroing the two least significant bits PASM is forcing the value of PAR to always align with addresses which are multiples of 4 - ie longs.
You are correct about the forced 4n alignment. This is also a source of unexplained errors, e.g. if you pass the address of a(n unaligned) byte variable (4n+3) it silently gets clamped to 4n. As for concidence, maybe, maybe not. It seems like a good compromise in that you can cover the whole 64K address range (hub RAM/ROM). And for other alignments you can always rebuild your address (+1/+2/+3) from 4n.
So, check my logic for me here - I'm making assumptions.
...
Coincidence?
Excellent, you got it in only a few posts. Your cargo net is pretty good.
Any long in the 32KB address space is definitely intentional.
Look at the PASM COGINIT instruction in the documentation for more enlightenment.
Table 3-1: Destination Register Fields
Bits Meaning
31:18 14-bit Long address for PAR Register
17:4 14-bit Long address of code to load
3 Start a New COG
2:0 Cog ID (for reusing a specific COG)
The COGNEW variant is best to use unless you have a very good reason for specifying a COG ID.
Excellent, you got it in only a few posts. Your cargo net is pretty good.
Thanks, I appreciate the vote of confidence. My confidence has been kinda dented as I'm stalled on my propeller project. I moved to the hardware for 12 weeks and now I'm back to the software I've forgotten almost everything I've learned.
I am working on a detailed discussion of the use of PAR with extended code samples. I need the feedback of the experts on this one so I get it right for the book. Should be done in a day or two.
'----------------------------------------------
' This is a demonstration of using shared variables
' between SPIN and PASM.
'
con
_clkmode = xtal1 + pll16x
_clkfreq = 80_000_000
obj
fdx : "FullDuplexSerial" ' include fdx object for terminal
var
long command ' this is the command variable
long share[4] ' these are shared variables
pub start | n
fdx.start(31,30,0,115200) ' start serial port
cognew(@pasm, @command) ' start pasm demo
waitcnt(clkfreq+cnt) ' wait for fdx and pasm start
repeat
command := -1 ' send non-zero as the "go" command
repeat while command ' wait until the command is done
fdx.tx($a) ' print a newline
fdx.tx($d)
fdx.str(string("shared values:"))
repeat n from 0 to 3 ' print the values
fdx.tx(" ")
fdx.hex(share[n],8)
waitcnt(clkfreq+cnt) ' don't print too fast
'----------------------------------------------
' this code increments shared array variables
' every time command is non-zero
'
dat org 0
pasm
mov tmp, par ' par is used as the command pointer
add tmp, #4 ' shared array starts 4 bytes after command address
mov ptr1, tmp ' save shared[0] address
add tmp, #4 ' next address
mov ptr2, tmp ' save shared[1] address
add tmp, #4 ' next address
mov ptr3, tmp ' save shared[2] address
add tmp, #4 ' next address
mov ptr4, tmp ' save shared[3] address
mov tmp, #0
:loop
rdlong cmd, par ' wait for user command
tjz cmd, #:loop ' if no command, jump to #:loop
rdlong dat1, ptr1 ' read shared data
rdlong dat2, ptr2
rdlong dat3, ptr3
rdlong dat4, ptr4
add dat1, #1 ' increment data
add dat2, #1
add dat3, #1
add dat4, #1
wrlong dat1, ptr1 ' write shared data
wrlong dat2, ptr2
wrlong dat3, ptr3
wrlong dat4, ptr4
wrlong tmp, par ' let user know we're done
jmp #:loop
cmd res 1 ' the command register
dat1 res 1 ' data temporary registers
dat2 res 1
dat3 res 1
dat4 res 1
ptr1 res 1 ' pointers to shared array
ptr2 res 1
ptr3 res 1
ptr4 res 1
tmp res 1 ' temporary pointer
fit $1ef ' don't let PASM grow beyond $1ef
'
' end of file
'----------------------------------------------
Just for enlightenment and to demonstrate what I meant by "more complicated" here's a more generic "array version" you asked about .... It adds the value of the djnz "len" variable value to the data. If nothing else it shows how to "index" registers in the cog.
I'm sure you'll find a simpler way to present this information at some point. It may be too much for a beginner, but like it or not the concept is a core idea in propeller PASM.
'----------------------------------------------
' This is a demonstration of using shared variables
' between SPIN and PASM.
' This version uses self-modifying code with an array
'
con
_clkmode = xtal1 + pll16x
_clkfreq = 80_000_000
ALEN = 32
ASNT = ALEN-1
obj
fdx : "FullDuplexSerial" ' include fdx object for terminal
var
long command ' this is the command variable
long share[ALEN] ' these are shared variables
pub start | n
fdx.start(31,30,0,115200) ' start serial port
cognew(@pasm, @command) ' start pasm demo
waitcnt(clkfreq+cnt) ' wait for fdx and pasm start
repeat
command := -1 ' send non-zero as the "go" command
repeat while command ' wait until the command is done
repeat n from 0 to ASNT ' print the values (0 based end = ALEN-1)
ifnot n & 7
fdx.tx($a) ' print a newline
fdx.tx($d)
fdx.str(string("shared values:"))
fdx.tx(" ")
fdx.hex(share[n],8)
waitcnt(clkfreq+cnt) ' don't print too fast
'----------------------------------------------
' this code changes shared array variables
' every time command is non-zero
'
dat org 0
pasm
long 0 [8]
mov tmp, par ' par is used as the command pointer
add tmp, #4 ' shared array starts 4 bytes after command address
mov ptr, tmp ' save shared[0] base address
:loop
rdlong cmd, par ' wait for user command
tjz cmd, #:loop ' if no command, jump to #:loop
mov tmp, ptr ' set base address for read from HUB
movd :read, #dat1 ' set start address for write to dat1 array
mov len,#ALEN ' number of items to read from hub
:read
rdlong 0-0, tmp ' read shared data to dat1 array
add tmp, #4 ' next pointer address
add :read, x200 ' next data address
djnz len, #:read ' read the next pointer to dat1 array
movd :incr, #dat1 ' set start address for write to dat1 array
mov len,#ALEN ' number of items to increment
:incr
add 0-0, len ' increment data by len
'add 0-0, #1 ' increment data by 1
add :incr, x200 ' next data address
djnz len, #:incr ' read the next pointer to dat1 array
mov tmp, ptr ' set base address for write to HUB
movd :write,#dat1 ' set start address for read from dat1 array
mov len,#ALEN ' number of items to write back to hub
:write
wrlong 0-0, tmp ' read shared data to dat1 array
add tmp, #4 ' next pointer address
add :write,x200 ' next data address
djnz len, #:write ' read the next pointer to dat1 array
wrlong zero,par ' let user know we're done
jmp #:loop
x200 long $200 ' used to increment destination field
zero long 0
cmd res 1 ' the command register
tmp res 1 ' temporary pointer
ndx res 1 ' increment index to set upper bits of dat array
len res 1 ' array length register
ptr res 1 ' shared array base pointer
dat1 res ALEN ' data temporary registers of size ALEN
fit $1ef ' don't let PASM grow beyond $1ef
'
' end of file
'----------------------------------------------
Wait till I get my progression on line and then give me a critique. I'm working on it pretty much full time so it should be done by tomorrow. Also want to see what other might suggest. Want to explain it so beginners are comfortable with this very necessary PASM/SPIN protocol.
H
When you want to pass an array of numbers with PAR, (lets say an array of 10 variables is needed)
Do you have to set up 10 spaces in the RES area thus
Array res 10
or can this allocation to be the last assignment as
Array res 1
and therefore does it have to be the last assignment in the Cog.
res N doesn't take up space in hub memory. So if you need N longs reserve them. Imagine you put the res 1 at the end of your PASM program and it ends up at location $1EC (4 longs left). How do you fit the other 6 longs in there? Your program will then happily scramble the first 6 SPRs (depending on access mode).
Can you be a bit more specific? What I was trying to say was (in case the question was addressed to me) if res 10 is what you need then use res 10. DON'T do something like res-1-at-the-end and hope it works.
The propeller manuals are not available in Kindle format but you can load the pdfs onto an iPad and then save them as iBooks. They can then be searched very effectively. Very useful for a beginner like me. Particularly so because relevent information is often scattered throught the manual. The search provides phrased results making it even more powerful.
Parallax reported that Kindle format was currently not in the pipeline.
Mine does. But if one wants to make a nice eBook for the Kindle, there is a free tool called Mobipocket Creator that is very easy to use. I am presently translating my acting coach's book, Acting is Living, to Kindle (and other eReaders) using this tool. For Mobipocket your material is a narrow subset of HTML with a few special codes; it's really easy if you've done any HTML coding at all.
I do not have access to a Kindle but I do have the Kindle App on my ipad2.
The app does not allow opening anything other than what has first been downloaded as books from the internet.
Maybe a pdf can be seen as a book somehow. The iPad has a button to convert a pdf to an iBook.
There may be some other tricks I am unaware of.
I have limited computer skills, in some departments, none.
Did not mean to mislead anyone. Apologies.
H
This will be added to, polished and edited before it is published. It will depend on what I learn in the interim. This is the general idea for now.
Chapter XXX
Using the PAR register to share variables between two or more Cogs
Sharing information between Cogs from within the PASM environment is best done with the PAR command. The register is designed specifically for this purpose and is covered on page 283 in the Propeller Manual. This chapter discussed the use of this register in detail.
A single variable shared
If you want to share a variable in the spin environment, it is a simple matter of declaring the variable under the VAR section of the listing, and it will become available to all eight Cogs. This is not true for the PASM environment in that there is no way to declare a global variable directly in this environment. There are two ways to make it possible to create a variable in one Cog and read it another Cog.
One way of doing this is to store the variable at a high memory location in the Hub from any one Cog and to read from that location from any other Cog either in Spin or in PASM. This method will NOT be demonstrated and is not recommended. There are too many problems with doing it this way. I will not even provide a sample listing of how to do this to completely discourage you from using this method. Since it is simple enough to use the PAR register to do the transfer, there is never a good reason to use this method.
The recommended way is to use the PAR register to share one or more variables or constants between the Cogs. PAR is one of the 16 special purpose, dedicated registers in each Cog. When you start a PASM Cog, you can pass it a variable that is available in both the PASM environment of the started Cog and in the general SPIN environment if is it declared under the VAR section. If the passed variable is given the same name in more than one PASM Cog, it will be able to be shared between these PASM cogs as well. The sharing can be for one variable or an array depending on how the shared registers are described.
First let us consider sharing just one value to start with. On startup, the PAR register contains a memory location that points to the location of the shared variable. We store information in the PAR register by putting the information in the memory location.
Consider these few lines of PASM code and comments
VAR
long SharedValue 'Shared variable is designated to be a long
….
….
PUB methodName 'name the method that runs the console display
Cognew(@start,@SharedValue) 'start second Cog at SecondCog in DAT memory
….
….
DAT
….org 0
….
mov mem Par.. 'move the memory location in PAR into MEM
wrlong Value, mem 'Store the value in the memory location
After the last line has been executed in the PASM Cog, the number in “value” will be available in “SharedValue” in all the SPIN Cogs. If another cogs has been started and the same @SharedValue variable passed in them, then all these Cogs could read the variable too.
You can also write-to and read-from the PAR register directly with
wrlong variable PAR 'write variable to PAR register.
and
rdlong variable PAR 'write variable to PAR register.
With the writing and reading taking place in the same cog or separate cogs.
Here we are using the PAR register directly, we are not going through its memory location though that can be done as was demonstrated earlier.
So sharing just one variable is not complicated. The steps we outlines above were as follows
1. Declare the variable global in the SPIN environment
2. Pass the same variable to all necessary PASM methods
3. Write and read longs to the shared variable in SPIN
4. Write and read longs to the PAR variable in the PASM methods.
Let us combine all the above information into a typical listing that you can refer to from time to time to refresh your memory. Consider a program that increments a variable by 1every time through the loops and makes this variable available to all Cogs whether they are written in SPIN or PASM. The initializing, incrementing and writing is done in the PASM cog and the reading and displaying is done in the SPIN Cog.
{{ Program 016 PASM minimum PAR.spin
This program is a minimal implementation of the use of the PAR register.
There are two Cogs in this program, one in SPIN and one in PASM
Cog 1 displays a counter on the console
Cog 2 first sets the variable to 0
it them finds the location of PAR and moves it to MEM
It then stores the variable in that location.
The loop adds 1 to the variable and stores
it back in the location of PAR (as was read into MEM.)
}}
VAR
long sharedVariable
OBJ
fds: "FullDuplexSerial"
PUB FirstCog 'displays values on console
fds.start(31,30,0,115200) 'start console at 115200 for debug output
cognew(@SecondCog, @SharedVariable) 'start the second Cog in PASM
waitcnt(clkfreq/4+cnt) 'wait 1/4 for everything to stabilize.
repeat 'loop
fds.tx($1) 'home to 0,0
fds.dec(SharedVariable) 'display written value
fds.tx(" ") 'space
waitcnt(clkfreq/60+cnt) 'flicker free wait
DAT
org 0 'start at 0 location in the cog
SecondCog 'start point identification
mov variable, zero 'initialize variable to 0
mov MEM, PAR 'find location of PAR, put in MEM
re_do wrlong variable, MEM 'write variable to location MEM
add MEM , #1 'add one to the MEM location
jmp #re_do 'jump back, do it again
zero long 0 'a zero to initialize variable
variable res 1 'variable storage space
MEM res 1 'MEM storage space
Two variables
Next we need to expand the program to transfer two variables between a Cog written in PASM and one written in SPIN. Once we master dealing with two variables, we can extend it to as many variables as we need, RAM space availability will be our only constraint.
As a general rule it is best if we as beginners assume that all our variables are longs. This is particularly so because PASM does not allow us to designate the length of its variables (though it does allow us to write bytes and words). Longs are, by definition, 4 bytes long so they require storage locations that are 4 bytes apart. If we store our first variable starting at location MEM, the next variable will be stored in location MEM+4. Every time we need an address for a fresh variable, we have to add 4 to the last location that we stored a variable in. In this particular case the two locations that we will use will be MEM and MEM+4.
If only a few variables are to be assigned, we can assign individual names and storage locations to them. We do not need an indexed array. In the last exercise we will implement an indexed array but for now we will simply assign two locations for the two variables that we will be using.
The first program needs to be modified to increase the various routines to handle two variables instead of one. Lets take a look at the display routine first. We need to make the following changes:
1. Assign space for two variables in VAR
2. Print out two variables on the console instead of one
VAR
long sharedVariable[2]
OBJ
fds: "FullDuplexSerial"
PUB FirstCog 'displays values on console
fds.start(31,30,0,115200) 'start console at 115200 for debug output
cognew(@SecondCog, @SharedVariable) 'start the second Cog in PASM
waitcnt(clkfreq/4+cnt) 'wait 1/4 for everything to stabilize.
repeat 'loop
fds.tx($1) 'home to 0,0
fds.dec(SharedVariable) 'display first value
fds.tx(" ") 'space
fds.dec(SharedVariable[2]) 'display second value
waitcnt(clkfreq/60+cnt) 'flicker free wait
The PASM code in the second Cog requires a little more attention to handle two variables. Here instead of adding one to the variable each time through the loop we need to call the second variable into active use. We also need to specify two real values for the two variables that we will be manipulating so that we have something to look at to confirm a successful program. Let us select 1111 and 2222 as our two values and define them as longs. These variables are a part of the PASM cog and we want to be able to read them and display them in the SPIN cog. Accordingly we will name the two variables var_one and vat_two and provide each of them with a storage location in the PASM cog. If we run the program and can read them in the SPIN cog we will have succeeded.
Here is the code for the PASM cog.
DAT
org 0 'start at 0 location in the cog
SecondCog 'start point identification
mov MEM1, PAR 'find location of PAR, put in MEM1
mov MEM2, MEM1 'make MEM2=MEM1
add MEM2, #4 'add 4 to MEM2 for next long
re_do wrlong var_one, MEM1 'write variable to location MEM1
wrlong var_two, MEM2 'write variable to location MEM2
jmp #re_do 'jump back, do it again
var_one long 1111 'variable declaration
var_two long 2222 'variable declaration
MEM1 res 1 'first address storage space
MEM2 res 1 'second address storage space
All put together the program is.
{{ Program 017 PASM minimum PAR.spin
This program is a minimal implementation for two variables and PAR.
There are two Cogs in this program, one in SPIN and one in PASM
Cog 1 in SPIN displays the variables on the console
Cog 2 in PASM sets the variables to 1111 and 2222
Finds the location of PAR and moves it to mem1
Then mem2 is set to mem1 +4.
The two variables are transferred to mem1 and mem2
and become available to the SPIN environment
Loop.
}}
VAR
long sharedVariable
OBJ
fds: "FullDuplexSerial"
PUB FirstCog 'displays values on console
fds.start(31,30,0,115200) 'start console at 115200 for debug output
cognew(@SecondCog, @SharedVariable) 'start the second Cog in PASM
waitcnt(clkfreq/60+cnt) 'wait 1/4 for everything to stabilize.
repeat 'loop
fds.tx($1) 'home to 0,0
fds.dec(SharedVariable) 'display first value
fds.tx($d) 'space
fds.dec(SharedVariable[1]) 'display second value
waitcnt(clkfreq/6+cnt) 'flicker free wait
DAT
org 0 'start at 0 location in the cog
SecondCog 'start point identification
mov mem1, PAR 'find location of PAR, put in MEM1
mov mem2, mem1 'make MEM2=MEM1
add mem2, #4 'add 4 to MEME2
re_do wrlong var_one, mem1 'write variable to location MEM1
wrlong var_two, mem2 'write variable to location MEM2
jmp #re_do 'jump back, do it again
var_one long 1111 'variable declaration
var_two long 2222 'variable declaration
mem1 res 1 'first address storage space
mem2 res 1 'second address storage space
Indefinite number of variables
Next we need to understand how to share an indefinite number of variables between a SPIN Cog and a PASM Cog. In order to do this we will need to define an indexing variable and decide on a way to tell the system how many variables are to be shared. An indexing scheme that allows us to access the variables serially will have to be devised also.
In order for the program to be universal let us agree that we need to be able to share between 1 and 10 variables (but the upper limit is determined by how much memory we have left in the Cog after the program is read in).
In the following code, the first cog written in SPIN, displays the 10 variables that are being transferred. The second cog, written in PASM, first find the location of PAR and stores it in mem. It then starts storing the 10 variables in 10 memory locations that are 4 bytes apart. The various variables are incremented and decremented as necessary.
In all these program I tried to make the code as compact and simple as possible as my present expertise allowed.
{{ Program 018 Array from PAR.spin
This program is a minimal implementation for an array and PAR.
There are two Cogs in this program, one in SPIN and one in PASM
Cog 1 in SPIN displays the variables on the console
Cog 2 in PASM sets the variables to 0 and 9
Finds the location of PAR and moves it to mem
Then storage array is set with offsets of +4.
The array variables are transferred to mem array
and become available to the SPIN environment
Loop.
}}
VAR
long shared[12], array, n
OBJ
fds: "FullDuplexSerial"
PUB FirstCog 'displays values on console
fds.start(31,30,0,115200) 'start console at 115200 for debug output
cognew(@SecondCog, @Shared) 'start the second Cog in PASM
waitcnt(clkfreq/4+cnt) 'wait 1/4 for everything to stabilize.
array:=10
n:=0
repeat 'loop
fds.tx($1) 'home to 0,0
repeat 'loop
fds.dec(n) 'display first value
fds.tx(" ") 'space
fds.tx(" ") 'space
fds.dec(Shared[n]) 'display first value
fds.tx(" ") 'space
fds.tx(" ") 'space
fds.tx($d) 'new line
n:=n+1 'counter variable
if n=>array 'decision to reset
n:=0 'reset n
fds.tx($1) 'home
fds.tx($3) 'clear screen
waitcnt(clkfreq/60+cnt) 'flicker free wait
DAT
org 0 'start at 0 location in the cog
SecondCog 'start point identification
:reset mov mem, PAR 'find location of PAR, put in mem
mov cnter, #10 'set counter for 10 variables
mov var_one, #0 'set first variable to 0
:re_do wrlong var_one, mem 'write variable to location (mem+4....)
add var_one, #5 'increment variable value by 5
add mem, #4 'increment memory location by 4 for longs
sub cnter, #1 wz 'subtract from counter and set 0 flag
if_nz jmp #:re_do 'jmp and redo if 0 flag NOT set
jmp #:reset 'jump back, do it again from beginning
cnter res 1 'counter for variables
mem res 10 'ten address storage space
var_one res 1 'variable content
{{ Program 018 Array from PAR.spin
This program is a minimal implementation for an array and PAR.
...
mem res 10 'ten address storage space
var_one res 1 'variable content
In program 18, you don't need "mem res 10" ... "mem res 1" is sufficient.
Your code stores only one COG variable's values to a number of hub addresses.
Here we are using the PAR register directly, we are not going through its memory location though that can be done as was demonstrated earlier.
What does that even mean?
You also use Finds the location of PAR ... a lot. To me par only ever refers to register $1F0 in cog memory. So there is really nothing to find, it's built-in. Unless your PAR means something else. In this case please clarify exactly what you mean.
{{ Program 016 PASM minimum PAR.spin
..
It then stores the variable in that location.
[COLOR="red"]The loop adds 1 to the variable and stores[/COLOR]
}}
...
org 0 'start at 0 location in the cog
SecondCog 'start point identification
mov variable, zero 'initialize variable to 0
mov MEM, PAR 'find location of PAR, put in MEM
re_do wrlong variable, MEM 'write variable to location MEM
[COLOR="red"] add MEM , #1 'add one to the MEM location
[/COLOR] jmp #re_do 'jump back, do it again
zero long 0 'a zero to initialize variable
variable res 1 'variable storage space
MEM res 1 'MEM storage space
You also use Finds the location of PAR ... a lot. To me par only ever refers to register $1F0 in cog memory. So there is really nothing to find, it's built-in. Unless your PAR means something else. In this case please clarify exactly what you mean.
I must be getting confused here. I have thought that the cog architecture was what we called in the bad old core days, memory to memory. daSilva calls them cells, special ones are referred to as registers, but still in all I understand them ALL to be memory.
To b@stardize a line out of Dune, the register is the memory and the memory is the register. Or am I lost in space????
Comments
It has been discussed in over a dozen posts in this thread because the idea is a core concept.
I didn't take any of the above posts as disapproval to use HUB memory to share information between cogs. In fact, according of one of the other threads that's pretty much the main way of doing it.
What I understood disapproval was aimed at was the arbitrary choice of a HUB memory address when there is a mechanism to do this via PAR.
I saw someone call PAR a register only and that over-writing it doesn't work because it's "read-only" or something.
I thought that PAR was a compiler symbol that was evaluated at the time of assembly? I don't see any reason why anything other than the compiler would need to even know about the symbol.
Am I missing something clever here?
I'm guessing you'd use this kind of idiom for PASM's version of a dispatch table?
If I'm completely 100% wrong btw, I'll continue to point out that I am still a newbie :-) I have exactly 170 lines of code (combined SPIN and ASM to my name).
If par is used in the source slot of an instruction you'll always get the same value for the lifetime of the cog (that's the read-only bit). Using it in the destination slot will access the shadow register which you are free to modify.
So, you said that the two least significant bits are always zero'd when you write to PAR. Given the use-case that PAR is frequently used to pass HUB memory references and HUB memory is in longs I'm wanting to jump to a conclusion...
You see where my brain is going? By zeroing the two least significant bits PASM is forcing the value of PAR to always align with addresses which are multiples of 4 - ie longs.
Coincidence?
Any long in the 32KB address space is definitely intentional.
Look at the PASM COGINIT instruction in the documentation for more enlightenment.
The COGNEW variant is best to use unless you have a very good reason for specifying a COG ID.
We had one for a while. The creator of it invested a ton into a nice bot too. Could pull up lots of good info with just a keyword. Gone now.
Anyway, welcome and please just jump in! This forum almost never sleeps. Not quite IRC, but... it's active, and that's better than nothing.
Thanks, I appreciate the vote of confidence. My confidence has been kinda dented as I'm stalled on my propeller project. I moved to the hardware for 12 weeks and now I'm back to the software I've forgotten almost everything I've learned.
Re irc:
I love the infobots on irc - so underrated and underused. You know, there is no reason why we couldn't resurect it :-D
I'll even run an infobot for the service :-)
Thanks guys,
Red
H
Basically, what is the right way to do this?
When you want to pass an array of numbers with PAR, (lets say an array of 10 variables is needed)
Do you have to set up 10 spaces in the RES area thus
Array res 10
or can this allocation to be the last assignment as
Array res 1
and therefore does it have to be the last assignment in the Cog.
I understand that in the SPIN program we would use
VAR
long sharedVariable[10]
to accommodate the data
H
If you say "array res 10", it will be much more complicated to use.
My very first example was one way that saves space ...
obviously it would be better for you and others to present it differently.
Below is a different example to study.
Output of the program on the terminal looks like this:
I'm sure you'll find a simpler way to present this information at some point. It may be too much for a beginner, but like it or not the concept is a core idea in propeller PASM.
Program output:
H
H
My mistake
I thought you indicated that the use of
Array Res 10
Was incorrect
H
The propeller manuals are not available in Kindle format but you can load the pdfs onto an iPad and then save them as iBooks. They can then be searched very effectively. Very useful for a beginner like me. Particularly so because relevent information is often scattered throught the manual. The search provides phrased results making it even more powerful.
Parallax reported that Kindle format was currently not in the pipeline.
Harprit.
Mine does. But if one wants to make a nice eBook for the Kindle, there is a free tool called Mobipocket Creator that is very easy to use. I am presently translating my acting coach's book, Acting is Living, to Kindle (and other eReaders) using this tool. For Mobipocket your material is a narrow subset of HTML with a few special codes; it's really easy if you've done any HTML coding at all.
The app does not allow opening anything other than what has first been downloaded as books from the internet.
Maybe a pdf can be seen as a book somehow. The iPad has a button to convert a pdf to an iBook.
There may be some other tricks I am unaware of.
I have limited computer skills, in some departments, none.
Did not mean to mislead anyone. Apologies.
H
Chapter XXX
Using the PAR register to share variables between two or more Cogs
Sharing information between Cogs from within the PASM environment is best done with the PAR command. The register is designed specifically for this purpose and is covered on page 283 in the Propeller Manual. This chapter discussed the use of this register in detail.
A single variable shared
If you want to share a variable in the spin environment, it is a simple matter of declaring the variable under the VAR section of the listing, and it will become available to all eight Cogs. This is not true for the PASM environment in that there is no way to declare a global variable directly in this environment. There are two ways to make it possible to create a variable in one Cog and read it another Cog.
One way of doing this is to store the variable at a high memory location in the Hub from any one Cog and to read from that location from any other Cog either in Spin or in PASM. This method will NOT be demonstrated and is not recommended. There are too many problems with doing it this way. I will not even provide a sample listing of how to do this to completely discourage you from using this method. Since it is simple enough to use the PAR register to do the transfer, there is never a good reason to use this method.
The recommended way is to use the PAR register to share one or more variables or constants between the Cogs. PAR is one of the 16 special purpose, dedicated registers in each Cog. When you start a PASM Cog, you can pass it a variable that is available in both the PASM environment of the started Cog and in the general SPIN environment if is it declared under the VAR section. If the passed variable is given the same name in more than one PASM Cog, it will be able to be shared between these PASM cogs as well. The sharing can be for one variable or an array depending on how the shared registers are described.
First let us consider sharing just one value to start with. On startup, the PAR register contains a memory location that points to the location of the shared variable. We store information in the PAR register by putting the information in the memory location.
Consider these few lines of PASM code and comments
After the last line has been executed in the PASM Cog, the number in “value” will be available in “SharedValue” in all the SPIN Cogs. If another cogs has been started and the same @SharedValue variable passed in them, then all these Cogs could read the variable too.
You can also write-to and read-from the PAR register directly with and With the writing and reading taking place in the same cog or separate cogs.
Here we are using the PAR register directly, we are not going through its memory location though that can be done as was demonstrated earlier.
So sharing just one variable is not complicated. The steps we outlines above were as follows
1. Declare the variable global in the SPIN environment
2. Pass the same variable to all necessary PASM methods
3. Write and read longs to the shared variable in SPIN
4. Write and read longs to the PAR variable in the PASM methods.
Let us combine all the above information into a typical listing that you can refer to from time to time to refresh your memory. Consider a program that increments a variable by 1every time through the loops and makes this variable available to all Cogs whether they are written in SPIN or PASM. The initializing, incrementing and writing is done in the PASM cog and the reading and displaying is done in the SPIN Cog.
Two variables
Next we need to expand the program to transfer two variables between a Cog written in PASM and one written in SPIN. Once we master dealing with two variables, we can extend it to as many variables as we need, RAM space availability will be our only constraint.
As a general rule it is best if we as beginners assume that all our variables are longs. This is particularly so because PASM does not allow us to designate the length of its variables (though it does allow us to write bytes and words). Longs are, by definition, 4 bytes long so they require storage locations that are 4 bytes apart. If we store our first variable starting at location MEM, the next variable will be stored in location MEM+4. Every time we need an address for a fresh variable, we have to add 4 to the last location that we stored a variable in. In this particular case the two locations that we will use will be MEM and MEM+4.
If only a few variables are to be assigned, we can assign individual names and storage locations to them. We do not need an indexed array. In the last exercise we will implement an indexed array but for now we will simply assign two locations for the two variables that we will be using.
The first program needs to be modified to increase the various routines to handle two variables instead of one. Lets take a look at the display routine first. We need to make the following changes:
1. Assign space for two variables in VAR
2. Print out two variables on the console instead of one
The PASM code in the second Cog requires a little more attention to handle two variables. Here instead of adding one to the variable each time through the loop we need to call the second variable into active use. We also need to specify two real values for the two variables that we will be manipulating so that we have something to look at to confirm a successful program. Let us select 1111 and 2222 as our two values and define them as longs. These variables are a part of the PASM cog and we want to be able to read them and display them in the SPIN cog. Accordingly we will name the two variables var_one and vat_two and provide each of them with a storage location in the PASM cog. If we run the program and can read them in the SPIN cog we will have succeeded.
Here is the code for the PASM cog.
All put together the program is.
Indefinite number of variables
Next we need to understand how to share an indefinite number of variables between a SPIN Cog and a PASM Cog. In order to do this we will need to define an indexing variable and decide on a way to tell the system how many variables are to be shared. An indexing scheme that allows us to access the variables serially will have to be devised also.
In order for the program to be universal let us agree that we need to be able to share between 1 and 10 variables (but the upper limit is determined by how much memory we have left in the Cog after the program is read in).
In the following code, the first cog written in SPIN, displays the 10 variables that are being transferred. The second cog, written in PASM, first find the location of PAR and stores it in mem. It then starts storing the 10 variables in 10 memory locations that are 4 bytes apart. The various variables are incremented and decremented as necessary.
In all these program I tried to make the code as compact and simple as possible as my present expertise allowed.
Harprit
I don't have one either but it prompted me to order a refurbished one ($99.00, no shipping)
Harprit
Your code stores only one COG variable's values to a number of hub addresses.
You also use Finds the location of PAR ... a lot. To me par only ever refers to register $1F0 in cog memory. So there is really nothing to find, it's built-in. Unless your PAR means something else. In this case please clarify exactly what you mean.
I must be getting confused here. I have thought that the cog architecture was what we called in the bad old core days, memory to memory. daSilva calls them cells, special ones are referred to as registers, but still in all I understand them ALL to be memory.
To b@stardize a line out of Dune, the register is the memory and the memory is the register. Or am I lost in space????
Frank