Assembly confusion
Graham Stabler
Posts: 2,510
I think I saw this mentioned elsewhere but what the heck is going on here:
I understand add and wrlong but not the use as 0 as a destination, I assume it relates to the label :IPos but I'm not sure how.
I'd also like to know if this is good programming practise or just being fancy [noparse]:)[/noparse]
Graham
:IPos add 0, Diff 'Add to encoder position value wrlong 0, MPosAddr 'Write new position to main memory
I understand add and wrlong but not the use as 0 as a destination, I assume it relates to the label :IPos but I'm not sure how.
I'd also like to know if this is good programming practise or just being fancy [noparse]:)[/noparse]
Graham
Comments
Nico Hattink
Refer to page 415 of the Propeller manual.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Paul Baker
Propeller Applications Engineer
Parallax, Inc.
Could someone explain this specific example rather than the generalities, I don't actually know what an indexed addressing mechanism is and so it is hard to understand what this is trying to achieve and why.
Sorry to act like such a dunce.
Graham
Indexed addressing usually is used for array access. Essentially, a computed value is used for an address. This works already on the Propeller for HUB memory access in that the HUB address is taken from a COG memory location and can be computed in any fashion (for RDxxxx and WRxxxx instructions). Similarly, jump addresses are taken from a COG memory location and can be computed, so you can have a table of instructions (like jumps) and a memory location "addr" that has the address of a table entry in it, then do a "JMP addr". What you don't have is the ability to fetch and set data values (like in an array) using a computed address. For that, you have to actually change the instruction to be executed, particularly the source address or destination address field. There are MOVS and MOVD instructions that make this easier. A simple example:
Post Edited (Mike Green) : 10/8/2006 1:20:18 PM GMT
Mike, this is legal??? One can subtract in a mov command?
So you take the address of the table which is its name, add an offset to access the element you are interested in and by using the command movd write that into the destination part of the command with label yy, replacing the 0-0 that acted as a place holder. When that command is run it has the correct address of the element of the array.
The main thing I wasn't understanding was the self modifying code remarks made earlier, I now understand that movd acts only on the destination and likewise Movs acts on the source. Looking at your code at first I wondered why you didn't just write mov zz,m and then I realized it would just overwrite zz and not use the address it holds.
Graham
p.s. Now I get it Nico's explanation makes perfect sense.
VAR
long rx_head
long rx_tail
long tx_head
long tx_tail
long rx_pin
cognew(@entry,@rx_head)' 'pass address of rx_head: first element of array
entry mov t1,par 'mov address of rx_head to t1
add t1,#4 << 2 'shifting 4 two bits to left yields 16. T1 now holds address of rx_pin: 5th element of array
rdlong t2,t1 'read rx_pin value from that 5th address, into t2
The trick that was not clear to me at first was that to get to the next array element, the cog has to add 4. This is because the hub keeps track of things as bytes, so to get to the next long, you need to advance 4.
Jim C
unless you define your variables you want to pass as Bytes..
consequently if you assigned a word variable you would add 2 for every subsequent entry and use 'rdword' instead.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
Post Edited (Beau Schwabe (Parallax)) : 10/10/2006 8:43:13 PM GMT
That a good segue into a question about the behavior of rdxxxx and wrxxxx instructions with unaligned values.
What does the Propeller do with a RDLONG or WRLONG instruction with a HUB address that's not a multiple of 4?
Similarly, what does it do with a RDWORD or WRWORD instruction with a HUB address that's not a multiple of 2?
Mike
I know that in the Assembly Dispatch program, that I have found very useful for calling Assembly functions from Spin, the following section...
...will not compile if the number of "byte" references do not equate to a multiple of one long. So the IDE might be smart enough to figure out the
relative byte offset if you create a problem. Now, that said, if you call a routine that is positioned within an odd byte offset, then all bets are off.
I'm not sure what you would run into.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
Thanks for the more complete explanation. I had not seen the rdword and wrword commands before, but seeing now how they work, as well as the byte commands, the whole addressing scheme starts to get clear.
Jim C.
It would be nice to have this behavior publically documented. It's clearly something that people are not supposed to do and may not be done the same way in future versions of the Propeller, but it would be nice to know to be able to explain why a program misbehaves in a particular way with this mistake.
Mike
Jim C,
Ok! I am stuck completely. I have a series of Arrays some Byte arrays and some Longs. If I pass the address of my byte array and attempt to locate the value for array index [noparse][[/noparse]0] I find I have to add #2 to the address passed to the assembly program.
VAR
· byte· KBArray[noparse][[/noparse]64]
· Long· CurStopWord1[noparse][[/noparse]16]'
· Long· CurStopWord2[noparse][[/noparse]16]
· Word· OutputWord'
· long· ProcessStop'
· long· cog·························· '· long· StRam
·· long LC
PUB start : okay·
lc:=1
· repeat 16· 'load some logical values into array to find in assembly·routine
··· kbarray[noparse][[/noparse]lc] :=lc
··· lc:=lc+1
· kbarray[noparse][[/noparse]0]:=222' a unique value to locate in assembly routine·
okay := cog := cognew(@entry, @kbarray) + 1
One solution is to define KBArray as long[noparse][[/noparse] 16 ] and to refer to its elements in your Spin code as KBArray.byte[noparse][[/noparse] lc ]. Another solution is to pass the address of a long word that you set to the actual address of KBArray like:
Craig
Now what I need to do is to launch 3 other pure assembly routines. I attempted to do the following to call the 2nd assembly program which was simply created, right or wrong, as a 2nd DAT statement in the same file as the 1st assembly routine. While there was no error generated, it is obvious that the 2nd routine is not running. (for this test I simply flash a unique LED in each routine)
From my spin program:
okay := cog := cognew(@entry, @MasterArray) + 1 'This called routine runs fine as evident by a LED flash routine.
okay := cog := cognew(@OutputDriverCog, @MasterArray) + 1 '** NOTE: the same LED flash routine (with different PIN) does not run
Obviously this is not the correct way to do this so my question is 2 fold.
1) Can a single Spin program call multiple assembly programs? How?
2) Can or should the 2nd assembly routine be located in the original spin file or as a seperate file and what is the best configuration.
NOTE: I am not trying to create multiple instances of an object as all the assembly routines will be doing completely different tasks.
Thanks in advance, Craig
Craig
I have remarked out all the other code in both assembly routines with just the following remaining
DAT
org
entry
mov VarStartAddr,par 'Retrieves RAM starting location for HUB variables
{========= SET PINS TO OUTPUT MODE =======================}
mov tmp,#1
shl tmp,#18
mov pinmask,#1
:PinDirLoop 'set pins to output mode
cmp tmp,pinmask wz, nr 'tmp=loop counter. Set Z flag if values are the same
or dira,pinmask 'Set this pin to output mode
shl pinmask,#1 'Shift bit left one position
if_nz jmp #:PinDirLoop 'If all pins required not set, continue to loop
{
}
:TestLoop
waitcnt time,_off_time ' wait for delay to pass
xor outa ,#2 ' toggle the pin
waitcnt time,_on_time ' wait for delay to pass
xor outa,#2 ' toggle
jmp #:TestLoop
In the 2nd assembly routine I simply added a 2 to the loop labels "TestLoop2" and used xor outa, #1
Just to verify the outputs I swapped the outa, #pin numbers and yes the 1st routine still works.
The compiler only seems to complain about a duplicate decaration.
Anyway, thanks again.
(1) How should the compiler know which part of code is assumed to be put into which COG?
(2) Do you preset TIME correcly for the first WAITCNT ? There is a general misunderstanding of how WAITCNT works. It does NOT wait until TIME + DELTA
@Mike, Beau
(4) Mike asked yesterday what will happen when RDLONG A, B finds an "odd" value in B. IMO Beau did not answer that question; I think however the answer is: It ignores the LSBs
(5) As padding does happen in the DAT section (it has to, as there is no re-allocation as in the VAR section), I think when mixing COG code with BYTE or WORD definitions, there will be no problems
1) Other than 2 or more ORGs I don't know how the compiler determines which part of the code is placed in a given COG. I had assumed a 2nd DAT statement would be the indicator for the compiler. In any event I had used 2 DAT and 2 ORG statements but the problem was I did not use a unique name for the variable which received the PAR from the SPIN routine. I thought the compiler would complain if I had used a variable in both DAT routines but it did not. It only complained when I used the same name in the xxxx RES 1 variable declarations.
2) Both of the assembly routines do have a long delay befor the LEDs start to flash at the start but I knew what caused that but I was not concerned as at this point. I wanted to determine the best way to call multiple assembly routines. The actual routines work fine.
That brings up a point where I am still confused. I can now create and run multiple assembly routines in multiple COGs but only if included in a single SPIN file. While this will work fine, I would like, if possible, to have separate files with Assembly ONLY code and call those routines from a single SPIN program. As of yet my little mind has not seen the light.
It would be much easier to write and edit if each assembly routine were in its own file. I understand how to use Objects in Spin but again, I only wanted a single Spin routine to launch 1 or more pure assembly routines.
I also want to understand the indexing through a table described earlier in this thread. My current application would work slick running a table of 96 longs. It needs to take an index received via a serial port and based on that index value, retrieve the proper bit pattern from a table in as efficient manner as possible.
"...Mike asked yesterday what will happen when RDLONG A, B finds an "odd" value in B. IMO Beau did not answer that question..."
Actually the question was asked on October 10th 2006, and I did reply on that same day... "...if you call a routine that is positioned within an odd byte offset, then all bets are off.
I'm not sure what you would run into."
Now if you define two variables as a long, and then you look past the end of the long declaration, you start dipping into other variables (words or bytes) or actual
program space. This program space actually defines the END of the Propeller program and variable space, but it is NOT blank... If you look at the HEX view after
pressing F8, the terminating double long looks like this.... FF FF F9 FF FF FF F9 FF
example:
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
Post Edited (Beau Schwabe (Parallax)) : 7/9/2007 3:53:56 AM GMT
·
(fragment from end of Keyboard.spin -- lines 432 - 435)
comment, which warns you that this line of code is modified elsewhere in the
assembly code. Look for the other references to napshr, they're the ones that
are actually modifying this line of code.
·
Ex.:
0 highly improbably address value, as the first instruction is located here
0-0 absolutely redundant because 0-0 == 0
#18/16/13 makes no sense, as yields 0; wants to say: "Will be patched with 18 or 16 or 13 respectively"
Edit:
RDLONG .., #0
might also be confusing, though is well commented
Ref. to the manual "....There are two values stored in the initialization area that might be of interest to your program:
a long at $0000 contains the initial master clock frequency, in Hertz, and a byte following it
at $0004 contains the initial value written into the CLK register. These two values can be
read/written using their physical addresses (LONG[noparse][[/noparse]$0] and BYTE[noparse][[/noparse]$4])"
Post Edited (deSilva) : 7/10/2007 12:29:34 PM GMT