Here's a subroutine you can use to see how many free cogs remain. It saves having to chase down the accounting by hand when objects are deeply nested.
PUB free_cogs : free | i, cog[noparse][[/noparse]8], jmp_0
jmp_0 := %010111_0001_1111_000000000_000000000
repeat while (cog[noparse][[/noparse]free] := cognew(@jmp_0, 0)) => 0
free++
if free
repeat i from 0 to free - 1
cogstop(cog[i])
return free
It works by starting as many new cogs as it can, keeping a count, and then stopping them. The cogs that it starts are all assembly cogs with a single instruction: a jump to location zero (i.e. an endless loop).
Okay, I have been quite active on the forum today, so this is it before I go for a bikeride with the lady...
I have created an object for Phils Cog_Counter subroutine. BTW, it is one clever little subroutine. I have also included an example program showing it in use. Both are attached...
This one had me scratching my head for a while
currSpeed -= 20 #> cmdSpeed
With currSpeed := 1750 and cmdSpeed := 1500 the result of the line above is to set currSpeed to 250, not 1730 as expected
The line looks so natural, but assignment is last in precedence so it really is
currSpeed -= (20 #> cmdSpeed)
the answer is
currSpeed := currSpeed - 20 #> cmdSpeed
Perhaps this should have been obvious, but, this problem had me thinking carefully for several evenings! I have a program running using all 8 cogs, and I was performing floating point math in several of them. I had sporadic math failures. It finally occured to me that there was nothing that was saving the context of one cog while servicing the other. So, simultaneous access to the floating point library was causing intermittent problems due to these resource conflicts. When it finally dawned on me tonight, I was able to bracket my transactions with LockSet/LockCLR, and this resolved my problem.
The general issue to be aware of is that you might have a COG that provides a service to your other cogs (such as the float lib) and you have to make sure that your cogs access these functions using safe and discreet transactions that serialize access to the shared resource. It's a classic problem. I'm surprised I didn't think of it sooner. But, that's how it goes!
I hope this saves someone a little debugging time in the future.
This took me three days to figure out. It's always the little stuff...
When declaring a variable array, for instance:
long array
remember that it is an array of three variables, not 4, as I kept thinking... see below;
[b]long[/b] array[noparse][[/noparse]3]
.
.
.
text.out(array[noparse][[/noparse]0])
text.out(array[noparse][[/noparse]1])
text.out(array[noparse][[/noparse]2])
text.out(array[noparse][[/noparse]3]) <- This one doesn't exist in this array!!!
Three days... I'm not sure if this could be remedied in any way, other than not being a block-head! Anyway, I fixed it and got 178kHz sampling from my MCP3001 at 3.3V (read the specs... I didn't think it possible). I'll post those results elsewhere.
-Parsko
EDIT:
PS- Phil, your code editor ROCKS!!!! I'll be using that again sometime! Thanks for the help!
Try adding a space inside the subscript. Without it, the forum software thinks you want to change the font size. You can also use [ instead of the [noparse][[/noparse]. Or, you can try pasting it into the edit box here, clicking Format, and pasting the result into the forum.
-Phil
Post Edited (Phil Pilgrim (PhiPi)) : 11/29/2006 5:50:32 PM GMT
This causes Array1 to have the same pointer value as Array2, so making
changes to Array1 would be the same as making any changes to Array2 and vise versa.
Also, in your original example...
[b]long[/b] array[noparse][[/noparse]3]
...If you place another variable on the end like this...
[b]long[/b] array[noparse][[/noparse]3], testvar
...Then assigning a value to 'testvar' will fill the position of array[noparse][[/noparse]3] in your example.
The reason, is when you declare a variable, the number within the [noparse]/noparse represents the number of
addresses you want to reserve, so Array[noparse][[/noparse]0] is address #1, Array[noparse][[/noparse]1] is address #2, and Array[noparse][[/noparse]2] is address #3.
When you use a variable that has been declared, the number within the [noparse]/noparse represents the index
added to the variable address name.· So Array[noparse][[/noparse]3] would represent the 4th address which overflows
the space or number of addresses that you have reserved for Array.· As a result, the next variable
'testvar' is picked up as if it were reserved within Array.
Another thing you can do based on the same principle mentioned above...
[b]long[/b] Index[noparse][[/noparse]0], Varaible1, Variable2, Variable3
.
.
.
text.out(Index[noparse][[/noparse]0]) ' Will display Variable1
text.out(Index[noparse][[/noparse]1]) ' Will display Variable2
text.out(Index[noparse][[/noparse]2]) ' Will display Variable3
·...Likewise, assigning values to Varaible1, Variable2, Variable3 will effectively·place them in the 'Index' array even though the Index array has a size of Zero.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ Beau Schwabe
IC Layout Engineer
Parallax, Inc.
Much better said than I, thank you. It was one of those AHA moments. Over a few days... Method 1 can write to the variable, method 2 can write to the variable, I can write sequentially to the variable, but I can't increment to the next variable. Every time I try the TV driver goes crazy !AHA! Turns out the next item in memory was the first variable of the TV driver......which is col. But, it didn't seem to have any effect on the color, just black garbage. I could be wrong about that though.
This works (well, it compiles; I can't guarantee it *works*)
but you do
pub start(a) | b[noparse][[/noparse]8193]
you get an error:
Limit of 4096 local variables exceeded.
So I'm not sure if the "8000" will fail at runtime in some odd way (that is, the limit is 4096 but it's not enforced there), or if
the error message is wrong (the real limit is all of memory), or what is going on.
Here's another trick. It's something that took me a while to find out, but is obvious in retrospect.
The immediate value in assembly is a 9-bit value that is not sign extended. So you can initialize
a register with a value from 0..511 using
MOV dst,#value
But what if you want to initialize a register with -1 or some other negative value? Up until now I've
been using
MOV dst,negone
...
negone long -1
But you don't need to do this because NEG has separate source and destinations.
You can use
NEG dst,#1
for instance, to initialize the destination register to -1. With this trick, you can
initialize any register to an immediate value from -511 through 511.
The assembler instruction min returns the largest value.
The assembler instruction max returns the smallest value.
Guys, what did you smoke before you thought this out?
Else I have to say I really love the machine architecture and the orthogonality of the instruction set. I have not seen anything as beautiful as this since the PDP-10.
Thank you guys! Well done!
If I could wish, I'd like two things. The first is assembler macros, and the second is to separate the machine instructions from the assembler directives in the documentation. It's confusing.
I've often wondered the same thing about min and max. It's the same way in pBASIC. Yet, there is some logic to it if you think of these operators as limiters rather than selectors: i.e. max sets an upper limit, and min sets a lower limit.
Yes, I do understand the semantics. It is just that it breaks the conventions for min and max. If reversing the well accepted meaning, why not instead use some other mnemonics, like floor and ceil or similar? Well, it is like it is and it is really not a big issue, but still it can be worth mention.
Don't fall into the trap of using the differential modes of the counters when you really want only a single ended output. If both apin and bpin point to the same pin, they will cancel out. The Propeller OR arbitration logic will make the pin high continuously, because in differential mode either apin or bpin is high. I have seen several cases where a method allows for both an apin and a bpin as parameters, and where the method itself uses differential mode. A user might be tempted to make both pins the same in order to get single ended output. Methods should default to single ended mode when apin=bpin.
only sets the z flag if the z flag was already set *and* the new result is zero.
That is, it does not set the z flag if the z flag was clear.
This makes sense in its use for extended arithmetic, but I think this should be
boldfaced in the manual or something, as I really expected it to always set the
z flag based only on its result.
I believe this also applies to SUBX and SUBSX, ADDSX, and CMPX and CMPSX.
Looking at the instruction summary table, I see that it is noted there in the Z
column.
This is something that tripped me up at one point too. PHSA/B is one of those special registers, in that it has a shadow register associated with it. This ordinarily is not apparent since it is a READ/WRITE register and when writing to it's location (via it being specified in the destination field of an instruction) it works correctly and when reading from it's location (via it being specified in the source field of an instruction) also works correctly.
Where the shadow register comes into play is when you read from the register when it's specified as the destination. This happens when you use WR(LONG/WORD/BYTE). What gets read in this situation is the last value you wrote to the register (in your case you likely didn't write anything to PHSA so it's value is 0). So any modification done by the counters does not affect this shadow register's value.
The way around this is to copy the contents of PHSA to a temporary register as you have shown in your second example before using WR(LONG/WORD/BYTE).
I've found that CASE sometimes requires more stack space that the equivelent list of IF..ELSE statements.· I actually had a CASE statement reset the Propellor and/or lock it up after hitting ctrl-F10. Going from stack[noparse][[/noparse]10] to stack[noparse][[/noparse]20] fixed it right away. I suppose if I figured out the stack space required it would help instead of just throwing numbers at it.
Hi Rich,
First of all: Whoo Hoo! 100 posts.
Back to the issue: The following code should be equivalent but apparently I was just on the edge of using all the space in stack[noparse][[/noparse]10] and the CASE just wouldn't work for me but the IF..THEN would. Adding extra stack space fixed it right up.
PRI check_PW1(pw)
{had to add more stack space to get rid of 'resets'}
dira[noparse][[/noparse]17]~~ 'Phase lock LED output
case pw
0..50 : !outa[noparse][[/noparse]17] 'too low : led blink
51..300 : outa[noparse][[/noparse]17]~~ 'ok : led on
301..600 : !outa[noparse][[/noparse]17] 'off sync: led blink
other : outa[noparse][[/noparse]17]~ 'too wide : led off
PRI check_PW2(pw)
{ Takes less stack than 'check_PW1' }
dira[noparse][[/noparse]17]~~ 'Phase lock LED output
if pw =< 50
!outa[noparse][[/noparse]17] 'too low : led blink
if pw > 51 and pw =< 300
outa[noparse][[/noparse]17]~~ 'ok : led on
if pw > 300 and pw =< 600
!outa[noparse][[/noparse]17] 'off sync: led blink
if pw =>600
outa[noparse][[/noparse]17]~ 'too wide : led off
I think the manual states that the CASE is the prefered method. This is probably·because of readability, not that it takes up less stack space.
I do most of my Propeller programming in assembler, because I need the speed. My application is a controller for a pipe organ, and it is using 7 of the 8 cogs, and I have to move data between the cogs quite a bit via hub memory. The trap which has bit me several times, and which I don't recall seeing mentioned much is that addresses in cog memory are in WORDs, while addresses in hub memory are in BYTEs. This means that an assembler loop which is transferring an array of longs to/from hub memory must increment the hub memory address by 4 and the cog memory address by 1. As an old mainframe guy, where all addresses are in bytes, no matter where they are, this was very counter intuitive. I realize that the 9 bit address fields make this necessary, but I do have to remind myself every time....
As previously mentioned, RES doesn't actually reserve space (if it's followed by additional code and/or data).· But I also discovered that ORG doesn't reserve space either.· So
· ORG $000
Start JMP #init
' code and data here · ORG $100
Init·COGID count
doesn't actually put Init OBJ Start+$100.· And if the code & data after Start runs over $100 longs there's no warning either!
One minor trap (for those of us who spend far too much time programming to the bare metal) is the sense of the Carry condition code for SUB/CMP (at least) is backwards from the 6502 (and probably other processors). So C=1 indicates an unsigned underflow occurred.· (And C=0 if the values are equal.) ·
I think it's the PIC and, by extension, the SX that got it backwards. Every other processor I've programmed (which set does not include the 6502, BTW) does it like the Propeller does it: the flag is a carry-borrow, instead of a carry-/borrow. The PIC-family micros must implement subtraction as a negate-and-add, instead of a true subtract. At least it helps to think about it that way. This difference cost me days of head-scratching awhile back when I had to modify an old PIC program, since I'd become reconditioned to think of the carry flag as an exception for both addition and subtraction. I still believe that to be the good and natural order of things: when the carry flag is set, some additional attention may have to be paid to the result.
Comments
Steve
PT&T has been updated with two new tricks and one new trap.
-Phil
It works by starting as many new cogs as it can, keeping a count, and then stopping them. The cogs that it starts are all assembly cogs with a single instruction: a jump to location zero (i.e. an endless loop).
-Phil
I have created an object for Phils Cog_Counter subroutine. BTW, it is one clever little subroutine. I have also included an example program showing it in use. Both are attached...
-Parsko
Here is link to a possible Assembly "trap" that could potentially hang someone up.
Assembly Trap
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
currSpeed -= 20 #> cmdSpeed
With currSpeed := 1750 and cmdSpeed := 1500 the result of the line above is to set currSpeed to 250, not 1730 as expected
The line looks so natural, but assignment is last in precedence so it really is
currSpeed -= (20 #> cmdSpeed)
the answer is
currSpeed := currSpeed - 20 #> cmdSpeed
http://forums.parallax.com/showthread.php?p=606446
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
The general issue to be aware of is that you might have a COG that provides a service to your other cogs (such as the float lib) and you have to make sure that your cogs access these functions using safe and discreet transactions that serialize access to the shared resource. It's a classic problem. I'm surprised I didn't think of it sooner. But, that's how it goes!
I hope this saves someone a little debugging time in the future.
Joel-
When declaring a variable array, for instance:
long array
remember that it is an array of three variables, not 4, as I kept thinking... see below;
Three days... I'm not sure if this could be remedied in any way, other than not being a block-head! Anyway, I fixed it and got 178kHz sampling from my MCP3001 at 3.3V (read the specs... I didn't think it possible). I'll post those results elsewhere.
-Parsko
EDIT:
PS- Phil, your code editor ROCKS!!!! I'll be using that again sometime! Thanks for the help!
Post Edited (parsko) : 11/29/2006 9:10:34 AM GMT
Try adding a space inside the subscript. Without it, the forum software thinks you want to change the font size. You can also use [ instead of the [noparse][[/noparse]. Or, you can try pasting it into the edit box here, clicking Format, and pasting the result into the forum.
-Phil
Post Edited (Phil Pilgrim (PhiPi)) : 11/29/2006 5:50:32 PM GMT
You can confirm this by...
...and noting the number of longs used after pressing F8.
You can also use this method to 'alias' variables.· For example:
This causes Array1 to have the same pointer value as Array2, so making
changes to Array1 would be the same as making any changes to Array2 and vise versa.
Also, in your original example...
...If you place another variable on the end like this...
...Then assigning a value to 'testvar' will fill the position of array[noparse][[/noparse]3] in your example.
The reason, is when you declare a variable, the number within the [noparse]/noparse represents the number of
addresses you want to reserve, so Array[noparse][[/noparse]0] is address #1, Array[noparse][[/noparse]1] is address #2, and Array[noparse][[/noparse]2] is address #3.
When you use a variable that has been declared, the number within the [noparse]/noparse represents the index
added to the variable address name.· So Array[noparse][[/noparse]3] would represent the 4th address which overflows
the space or number of addresses that you have reserved for Array.· As a result, the next variable
'testvar' is picked up as if it were reserved within Array.
Another thing you can do based on the same principle mentioned above...
·...Likewise, assigning values to Varaible1, Variable2, Variable3 will effectively·place them in the 'Index' array even though the Index array has a size of Zero.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
Much better said than I, thank you. It was one of those AHA moments. Over a few days... Method 1 can write to the variable, method 2 can write to the variable, I can write sequentially to the variable, but I can't increment to the next variable. Every time I try the TV driver goes crazy !AHA! Turns out the next item in memory was the first variable of the TV driver......which is col. But, it didn't seem to have any effect on the color, just black garbage. I could be wrong about that though.
Keep learnin' every day.
-Luke
BTW- Ik houve F8 and F10!!!
If you do
pub start(a) | b[noparse][[/noparse]8000]
This works (well, it compiles; I can't guarantee it *works*)
but you do
pub start(a) | b[noparse][[/noparse]8193]
you get an error:
Limit of 4096 local variables exceeded.
So I'm not sure if the "8000" will fail at runtime in some odd way (that is, the limit is 4096 but it's not enforced there), or if
the error message is wrong (the real limit is all of memory), or what is going on.
The immediate value in assembly is a 9-bit value that is not sign extended. So you can initialize
a register with a value from 0..511 using
MOV dst,#value
But what if you want to initialize a register with -1 or some other negative value? Up until now I've
been using
MOV dst,negone
...
negone long -1
But you don't need to do this because NEG has separate source and destinations.
You can use
NEG dst,#1
for instance, to initialize the destination register to -1. With this trick, you can
initialize any register to an immediate value from -511 through 511.
The assembler instruction min returns the largest value.
The assembler instruction max returns the smallest value.
Guys, what did you smoke before you thought this out?
Else I have to say I really love the machine architecture and the orthogonality of the instruction set. I have not seen anything as beautiful as this since the PDP-10.
Thank you guys! Well done!
If I could wish, I'd like two things. The first is assembler macros, and the second is to separate the machine instructions from the assembler directives in the documentation. It's confusing.
-- Mikael Kuisma
I've often wondered the same thing about min and max. It's the same way in pBASIC. Yet, there is some logic to it if you think of these operators as limiters rather than selectors: i.e. max sets an upper limit, and min sets a lower limit.
-Phil
Yes, I do understand the semantics. It is just that it breaks the conventions for min and max. If reversing the well accepted meaning, why not instead use some other mnemonics, like floor and ceil or similar? Well, it is like it is and it is really not a big issue, but still it can be worth mention.
- Mikael
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com
addx reg,reg wz
only sets the z flag if the z flag was already set *and* the new result is zero.
That is, it does not set the z flag if the z flag was clear.
This makes sense in its use for extended arithmetic, but I think this should be
boldfaced in the manual or something, as I really expected it to always set the
z flag based only on its result.
I believe this also applies to SUBX and SUBSX, ADDSX, and CMPX and CMPSX.
Looking at the instruction summary table, I see that it is noted there in the Z
column.
The manual states on page 159 that symbols must be 32 characters or less.
But if you try to use symbols that are 31 or 32 characters long, the IDE
complains that symbols must be 30 characters or less.
wrlong phsa,variablepointer
return nothing and
mov variable,phsa
wrlong variable,variablepointer
returns phsa?? (not sure if I got address pointer right for wrlong this I always get them mixed up
Where the shadow register comes into play is when you read from the register when it's specified as the destination. This happens when you use WR(LONG/WORD/BYTE). What gets read in this situation is the last value you wrote to the register (in your case you likely didn't write anything to PHSA so it's value is 0). So any modification done by the counters does not affect this shadow register's value.
The way around this is to copy the contents of PHSA to a temporary register as you have shown in your second example before using WR(LONG/WORD/BYTE).
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Paul Baker
Propeller Applications Engineer
Parallax, Inc.
I've found that CASE sometimes requires more stack space that the equivelent list of IF..ELSE statements.· I actually had a CASE statement reset the Propellor and/or lock it up after hitting ctrl-F10. Going from stack[noparse][[/noparse]10] to stack[noparse][[/noparse]20] fixed it right away. I suppose if I figured out the stack space required it would help instead of just throwing numbers at it.
Paul
I missed your post. (I wish that the sticky threads would rotate the most recent posts to the top of the list like the unsticky ones do.)
Isn't your result exactly the opposite of what is described (somewhere... I can't find it now) in the User Manual?
Could you post a complete example?
Thanks,
Rich
First of all: Whoo Hoo! 100 posts.
Back to the issue: The following code should be equivalent but apparently I was just on the edge of using all the space in stack[noparse][[/noparse]10] and the CASE just wouldn't work for me but the IF..THEN would. Adding extra stack space fixed it right up.
I think the manual states that the CASE is the prefered method. This is probably·because of readability, not that it takes up less stack space.
· ORG $000
Start JMP #init
' code and data here
· ORG $100
Init·COGID count
doesn't actually put Init OBJ Start+$100.· And if the code & data after Start runs over $100 longs there's no warning either!
Post Edited (ericball) : 5/28/2007 4:53:08 PM GMT
·
-Phil
I'm having a hard time seeing why it shouldn't be
The code ends with
but wouldn't it work just as well and be faster to say
·