Propeller Assembly Q&A
Paul Baker
Posts: 6,351
Sure, the equivalent assembly code for this would look something like:SailerMan said...
Thanks... Why don't you move this section of the tread to another post.
In any acount My problem lies in the fact that I have a hard time thinking low level.
Let's start out really small.
·······Pub Main|Index,Count Repeat Index From 0 to 10 Count+=1
····
loop add Count, #1 'add 1 to the value of count djnz Index, #loop 'decrement Index, if Index is not equal to 0 jump to loop, else continue ... Index long 11 Count long 0
Note that Index is set to 11, thats because your Spin loop executes 11 times (0 + 1-10). If you needed to go through this loop more than once, you would have to re-initialize Index by performing "mov Index, #11" before entering loop. The # you see in the code means it's an immediate value, IOW it is the value itself, if for instance you said "add Count, 1" instead, it would take the value in address 1 of the cog's memory and add that to Count. The # works for any value between 0 and 511, because there are only nine bits that can be stored for an immediate value.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Paul Baker
Propeller Applications Engineer
Parallax, Inc.
Comments
[/code][/size]
In Order For the Above Code to run, What other code must I add? I am not at home so I can't test. Would the below work?
Why use·Count Long·0?· Does·Count Res·1 Do the Same Thing?
You Said,· "· If you needed to go through this loop more than once, you would have to re-initialize Index by performing "mov Index, #11" "
[/code][/size]
Would this work?·
Why is there a "#" in front of The Loop label?
Thanks for your help,
Eric
·
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
at the top too)
The only issue is the cog will execute this without telling the external world what it's doing, but it will constantly be counting up in Count (because Count carries its value into the next set of loops, "mov Count, #0" will reset it's value).
The # is in front of Loop because all labels are resolved into an address, using the # tells it to use the address itself, otherwise it will grab the value in Loop and jump to that value, not the address. In this case it will grab the lowest nine bits at address 0 (which is what Label resolves to), located there is "add Count, #1", the lowest nine bits of that instruction is the source register or #1, so it will jump to address 1 not 0 (to "djnz Index, #loop" not "Loop add Count, #1" like we want).
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Paul Baker
Propeller Applications Engineer
Parallax, Inc.
Now let's take this a little further.
I want to send count to the outside world via Pins 0-7 as a binary led Arrangment. What would be the basic (Long)·way of doing it.
Thanks,
Eric
To write the value out to pins 0-7 you would do something like this:
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Paul Baker
Propeller Applications Engineer
Parallax, Inc.
Post Edited (Paul Baker (Parallax)) : 12/16/2006 5:07:50 AM GMT
Thanks for this...OK I'm understanding a little.
How about a simple "IF-THEN-ELSE" condition.
If A=B Then
Equal=Equal+1
Else
NotEqual=NotEqual+1
What is the difference between
Variable Long 100
And
Variable Res 1·, and then later Mov Variable,#100
Regards,
Eric
Note the above is suitable for small If/Then statements.
For complicated statement, you should use jumps like
When you use a "long 100", the value "100" is compiled into your code
as the initial value of "Variable". If your program changes it, it is changed
until the program is reloaded. If you use the "Res 1" and "Mov Variable,#100",
the value is undefined (probably zero) until the "Mov" instruction is executed.
If the "Mov" is in your initialization, "Variable" gets initialized during initialization.
(The first is static, the second is dynamic and depends on the flow of your program).
Usually, the first form is used when Variable is actually a constant for your program
or if the program will always be reloaded from RAM to COG for initialization.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Who says you have to have knowledge to use it?
I've killed a fly with my bare mind.
Post Edited (CJ) : 12/17/2006 4:21:16 PM GMT
Is the ":" used for in this case.
Thanks for all of this information. It is really helping to understand.
The ":" is used for a local label. These are just like normal labels, but can be redefined. Essentially, the space in your program between regular labels is a "local label space". Any local label defined there is different from the same local label defined between some other regular labels. They're intended for the case where you need a label say for a jump, but that label isn't needed elsewhere and the name itself isn't particularly important. (page 348 in the manual).
CJ, Thanks... A second way of doing it.
good examples of the readability features used in the syntactical setup
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Who says you have to have knowledge to use it?
I've killed a fly with my bare mind.
There are several things I expected to be able to do, but don't see explicit instructions for. I assume there are tricks to build these operations out of other operations instead.
1. How do I invert all the bits in a register? I expected it would be coded "not Mask". (Mask is the name of my register.) But there is no "not" instruction. I ended up having to use "xor Mask, #$FF".
2. How do I explicitly set/clear the Z flag? I've been using the Z flag to hold the return values from my functions which return booleans. It's very convenient because I can end the function with a "cmp" and pick right up after the function call with an "if_z", and I don't have to worry about creating a temporary register to hold the value. But now I've run into a function which has more complicated logic, and I can't narrow that down to a single cmp. So I want to explicitly code "setz" in one part of the function, and "clrz" in another, to return true or false. Except I can't because there is no setz! I thought about "mov reg, #0 NR, WZ", but it doesn't seem very elegant. Is there a better way to set the Z flag?
1. That's the only good way to NOT a register. But of course you need to use
xor Reg, all_ones
...
all_ones long -1 ' -1 = all ones
if the reg contents are larger than one byte.
2. The mov 0, #0 nr,wz and mov 0, #1 nr, wz is a good way to set/clear the Z flag. I haven't used it, I usually write cmp 0,0 wz to set Z and cmp all_ones, #0 wz to clear it (the all_ones would be the notmask). Neither of these is elegant, but it isn't the number one priority when writing in ASM anyway.
Also, to set and clear the carry flag: test all_ones, #0 wc to clear, test all_ones, #1 wc to set it.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Paul Baker
Propeller Applications Engineer
Parallax, Inc.
Jim C
mov reg, #0 NR, WZ
and be done with it?
I've enjoyed this thread too. For me, the prop assembly makes some hard things fairly easy. I do often get tripped up on easy things though!
The smaller number of actual opcodes does make hunting for solutions easier though. IMHO, this is a good thing. Having written assembly on some CPU's with very large numbers of opcodes, I find this refreshing. At first, it seemed really limited, but now it's really making some sense. Blowing 32 bits on just a set to a flag, is gonna happen dedicated opcode or not right? In that mindset, the above is perfectly reasonable.
James L
I really do like Propeller assembly. I grew up on 8086 assembly - literally I grew up on it, I spent every day after school for years in high school hacking assembly on a DOS computer - but I find the Propeller assembly much more orthogonal than a CISC machine like the 8086. By orthogonal I mean that the pieces of the instructions in Propeller assembly can be mixed and matched to create a much richer set of operations. On a CISC machine the instructions are all different sizes and you can't combine pieces of them, but there are also little 1-byte instructions for doing things like setting/clearing flags. So sometimes I go to do something on the Propeller and go "Smile! There's no instruction for that!" but it really doesn't matter in the long run; it's just a new set of tricks to learn - I had to go through the same process of learning tricks to make up for things the 8086 doesn't do, which I can tell you is a lot more frustrating than Propeller assembly!
Actually Propeller assembly is not frustrating at all. Though it is a new thing to learn, the instructions pretty much do what I think they are going to do without weird side effects. When it does surprise me it is usually a good surprise. Some examples:
When I looked up what the "movs" instruction does (move to source reg), I suddenly realized: instead of self modifying code being a dirty hack, this processor has op-codes with direct support for self-modifying code! Wicked!
Then I got an error message where it said it wanted me to label my "ret" statement in a subroutine I wrote. WTF? So I looked it up in the Propeller Manual, and found out that the Propeller uses self-modifying code to do function calls without a stack! Wow!
cmp zero, zero wz, wc ' z=1, c=0
cmp neg1, zero wz, wc ' z=0, c=1
cmp zero, neg1 wz, wc ' z=0, c=0
add neg1, #1 nr, wz, wc ' z=1, c=1
zero long 0
neg1 long -1 ' all ones
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com
But the Propeller is completely different from both of those. For the Propeller it's more like having 512 registers and your program is stored *in* the registers as well! So you can do things in Propeller assembly that are just mind boggling.
Well let me just say this is a BAD idea. I've just spent the last few hours trying to debug my code and the way it's acting made no sense whatsoever.
Finally I read in the manual that the CALL instruction clears the Z flag!!! So no matter what I did to set the Z flag, I was always returning false!
1. I have already read through the descriptions and satisfied myself that there is no facility for shared cog memory for passing data from one cog without going through the Hub. Is this right?
2. Is anyone building an encryption object? I could do that. There actually is enough memory available in the HUB to do good video stuff... but it would have to be encrypted on the way in and the way out. I was thinking of just running the video through a smart differential amplifier, for which there are probably better uses.
Thanks
Rich
There is no such thing as shared cog memory. The only way to pass data from one cog to another without going through hub memory is to use a shared I/O pin. If you have an I/O pin you're not using for anything, you can transmit data serially with one cog and "listen" to the I/O pin with another cog.
I have read many books and datasheets about processors and uCs, but none of them are useful to learn assembler or better put, the way assembler works. An exception is a book titled:
"Assembler for the 8086 by John Socha and Peter Norton" From 1986. (Those guys if you have been in this for long enough, nc anybody ?)
Besides is an old book and for the 8086 and thus DOS related, it is very good at explaining how the assembler language/processor works.
After some time, just reading a datasheet gives me enough to start with a new instruction set. (May be that is why I have problems with OO programming ).
I'd recommend it to anybody how wants to learn asm.
I got a Spanish translation, so It should exist in more than 2 languages.
Question: does the Call and Jmpret behavior (stores the pc + 1 return address at RetinstAddr) happen at compile, load or execution? My expectation now is that it happens
at execution (thereby letting multiple callers to a routine).
As I understand Call, the assembler looks for a label with "_ret" and modifies the Jmp code there at compile time. So this question: can you modify the "wrong" opcode? That is, not have a Ret instruction at the _ret label? Or is will the assembler enforce good coding habits?
Anyway, the storing of the return address happens at execution time (it has to ... think about it).
Call and return are "macros" in that they're shortcuts that are handled by the assembler for convenience. The Call is really just a JmpRet and the Return is just a Jmp. When the assembler sees a "Call #Routine", it produces a "JmpRet Routine_ret,#Routine". When the assembler sees a "Ret", it produces a "Jmp #0". The assembler does no checking of what's at the "Routine_ret" label. It's normally not the job of an assembler to enforce good coding habits. It might be useful as an option for an assembler to issue warning messages, but this assembler/compiler has no provision for warning messages.