Difference between "@" and "#" in PASM?
ags
Posts: 386
Looking at some code examples, I see patterns like:
which makes sense: take the the location (address) of the register represented by the symbol "src" and use it as a immediate value (as compared to the contents of the register represented by the symbol "src") and move that to the register (address) represented by the symbol "dest". The end result is the register represented by the symbol "dest" will contain the location (address) of the register represented by the symbol "src".
However, I've also seen (and used) a construct like this:
This says: take the address of the register represented by "src" and move it's contents (since there is no "#" specifying immediate mode for the move instruction) to the register (location) represented by the symbol "dest".
However, I"ve used the second style in code that works (with problems) like I would expect the first style (immediate mode) and I can't belive that it would ever work if I was understanding what's going on here - but it does (apparently). If "#" causes the "i" bit of the instruction/word to be set meaning that the src value is not a register address but an immediate numerical value, I can't reconcile that with the meaning of the "@" modifyer. In PASM, isn't the "@" always implied?l Meaning, the value inserted into the the src or dest fields of the instruction is always the location represented by the symbol provided as an operand(s) to the instruction, and for the src location, using "#" designates that it is a literal, immediate value?
Thanks.
label mov dest, #src src res 1 dest res 1
which makes sense: take the the location (address) of the register represented by the symbol "src" and use it as a immediate value (as compared to the contents of the register represented by the symbol "src") and move that to the register (address) represented by the symbol "dest". The end result is the register represented by the symbol "dest" will contain the location (address) of the register represented by the symbol "src".
However, I've also seen (and used) a construct like this:
label mov dest, @src src res 1 dest res 1
This says: take the address of the register represented by "src" and move it's contents (since there is no "#" specifying immediate mode for the move instruction) to the register (location) represented by the symbol "dest".
However, I"ve used the second style in code that works (with problems) like I would expect the first style (immediate mode) and I can't belive that it would ever work if I was understanding what's going on here - but it does (apparently). If "#" causes the "i" bit of the instruction/word to be set meaning that the src value is not a register address but an immediate numerical value, I can't reconcile that with the meaning of the "@" modifyer. In PASM, isn't the "@" always implied?l Meaning, the value inserted into the the src or dest fields of the instruction is always the location represented by the symbol provided as an operand(s) to the instruction, and for the src location, using "#" designates that it is a literal, immediate value?
Thanks.
Comments
Certainly not wrong but rather confusing, never used it myself in PASM context.
1) how did you arrive at $0C as the address for the src register? If it is the second line of the DAT section, why woudln't that be $01 (given that is PASM, cog memory is addressed in long word (32 bit) chunks? Each instruction (instr, zrci, cond, dest, src) requires just 32 bits.
2) the burning question is still why the assembler would recognize the "@src" as an immediate value, as compared to the address of a register in which the value to be used is contained?
It doesn't, for that you'd want #@src Anyway, @ only resolves hub (related) addresses so you'll get one. Incidentally, can you point at the code you say is using it? Would be interesting to see as to why it's done like this.
In the example above, "label" has a value of 0, "src" is 1 and "dest" is 2. If the object's method table uses 8 bytes then the values of "@label", "@src" and "@dest" will be $8, $C and $10 respectively. In general, the relative object address is not useful in PASM unless you know the object's start address.
The top object always starts at $10 (unless it's manually moved to another location). I have done something like "mov addr,#@src+$10" to get the absolute hub address of "src" into the cog variable "addr". Note that this only works if "src" is located in the top object and its absolute address is less than 512. For larger addresses I do something like this: Once again, this only works if "src" is in the top object and the code hasn't been relocated to another location.
Dave
Surely you don't mean this. The label name by itself refers to the value of the long located there, not the address of it.
I am referring to the values associated with the label, and not the value at the location that the label refers to. The following statements use the values of the label: The value stored at the location addressed by "label" is 123, but the value of "label" when used as a constant is its hub address, and the value of "@label" is its byte offset within the object. The symbols "label" and "@label" can be use like any other constants, such as in an expression like "label*4 + 3".
Shirley Dave, I'm slow today. How is this different than what I said?
@kuroneko, I hadn't noticed that they were defined with res statements. Thanks for pointing that out.
(1)
mov someDestLabel, @someSrcLabel
and this:
(2)
mov someDestLabel, someSrcLabel
and this:
(3)
mov someDestLabel, #someSrcLabel
If I'm understanding the comments here, the difference between the 1st and 2nd examples above is that the first will resolve to the offset, in bytes, of the "someSrcLabel" register, and move the contents of that location into the "someDestLabel" register location. The second will do the same thing, but the offset to "someSrcLabel" will be in terms of long words (1/4 the value in the 1st case) and move the contents of that location into the "someDestLabel" register location. The 3rd example (which is what I meant to do) will directly load the location (address) of the "someSrcLabel" register, in long words, into the "someDestLabel" register location.
In other words, the difference between the 1st and 2nd examples is loading the value from one location (offset) in COG RAM vs. a location/4. The 3rd example doesn't deal with contents of a register location, but the address/offset/location (in long words - limited to 0-511) directly (immediate mode).
If this is correct, it's a miracle that my code worked at all. I'm including it to help the discussion, but it will probably have little value as a snippet. The full code listing is too long. My intention was to define an "array" of long word values in COG RAM, and then iterate through each long (to get a single bit value). I hard-coded a limit of 32 longs as the array to be used. It seems that what was actually happening was that by using "@" instead of "#" (to set the immediate mode instruction flag), the value stored in the location I intended to use "cogArray" (value = 32) was multiplied by 4 (byte offset, not long word location) which is 128 and is beyond the extent of the DAT block that I defined. So, I ended up using different registers than expected, but they were (luckily) stil able to be pressed into service without a problem here. (When storing the values I was loading from HUB RAM, I used the same technique (mov someLocation, @cogArray) to load the value from HUB RAM, so if I did it incorrectly both times, then it could work (accessing the same location) but if any other part of the code used those locations then things would go very bad very quickly.
Thanks for the help.
Example 1: here outa is loaded from register $002, outb from register $010.
Example 2: here outa is loaded from register $022, outb (still) from register $010.
There is no direct relationship between @label and label (especially not simply dividing by 4).
This is quite a bit different than I originally thought.
I'm going to suggest that for this discussion we not include the use of @ in SPIN code. That seems to operate much as I expected ("address of" as in C/C++) and it doesn't seem to be very tricky - if used in a PUB block (which implies SPIN code)
I'm also going to suggest that in reality, there are very few meaningful uses for the @ operator in a DAT block (implying PASM code). At least I can't figure out any use for it. It would be an unlikely coincidence if it actually provided a value that is related to the intended use - for anyone other than a very, very advanced user - and in general, it is likely to be a mistake to use the @ operator in a DAT block.
However, I am still learning, even if indirectly, from the discussion, and would like to continue. What I see is that the value calculated at compile time (@a) will be the address, in bytes, of memory in HUB RAM represented by the symbol "a", for that DAT block as it exists in HUB RAM. However, the DAT block will be copied into COG RAM, and executed in COG RAM - where register addresssing is in longs, the absolute location of the DAT contents are guaranteed to not be the same as in HUB RAM, and the compile-time value of @a is unchanged. Is this correct?
I have some strong opinions about the utility of the existing documentation (the Propeller Manual v1.1) that I'll share separately so as not to make this even more lengthy.
The meaning of the @ operator depends on whether it is used in a DAT section or a PUB/PRI section. In a DAT section it produces the object byte offset of the label. In a PUB/PRI section it produces the absolute address.
This is documented in the Prop manual, but it is a bit confusing.
I'm going to ammend my previous statement, and make it more concrete (and perhaps incorrect - please correct me if so):
There is no meaningful use for the "@" operator operating on a PASM instruction operand (source or destination).
Using the "@" operator in a DAT block containing constant values does provide some utility (e.g. tables) - but must be dealt with carefully by using the SPIN-only "@@" operator to resolve the value from object-relative to absolute.
Using the "@" operator in a PUB/PRI block works as expected, without risk or special attention (other than remembering it's an address not the value at an address).
Does anyone disagree with this (maybe not-so) bold statement?
Again, let me be clear on this: this is not mean to be argumentative, and you have demonstrated that this technique *could* be used. I'm also not asserting that I'm an expert by any means. I'll gladly admit that I do not have years of experience with the Properller, and I am certain, based on other threads I've participated in, there are many contributors to this forum with much more knowledge than I have. However, my thinking is that it actually never *would* be used, other than as an academic exercise. Am I still missing something?
Thanks for the replies.
Well, I for one use in many projects, exactly the technique Dave Hein demonstrated, so there is your real world example. Todate I have written most of my code in assembler, and one of the things I do is store many lists in hubram because space in cogram is just too precious. These lists can be text lists, or pseudocode lists that a specialized interpreter in a cog reads and executes, and several other applications.
In fact in one significant approach I store many small (a dozen or two) short assembler driver sequences such as UART, I2C, OneWire etc. in hubram that any cog can dynamically download for execution, and when finished get flushed. This way, because program sections are not permanently resident, much larger programs can be executed in a cog than otherwise, yet at full assembler speed. The swapping in of these drivers is only 10 to 20-ish microseconds.... the length of a single typical SPIN instruction.
So, There is a practical use for @ in assembler....
By the way, for convenience I park all these DAT lists in the top object, so that the $10 offset remains constant.
Cheers,
Peter (pjv)
See it as one odd way of getting a hub reference. Personally I communicate stuff like that through par which is clean. The +$10 method will immediatly fail if the object is used as a child object.
... and if you know the address of one variable, the @ operator is useful to calculate the addresses of other variables (not more than 512 bytes away):
Andy
@Dave Hein: Thanks for your patience and help with this. I do think I understand now, and it is much different than I thought. I am still dumbfounded that my code worked at all.
Having said all that, I now find myself wanting to validate my understaning of how the 6 blocks are arranged in hub RAM, (and how DAT blocks are loaded into cog RAM). I read the bit about "org" in the manual, and it doesn't clearly describe how it would be used other than "org 0". I'm not clear how the technique described here to reference data that is too big to fit into cog RAM could work; if it's too big to fit into cog RAM, then how can it be in a DAT block? Isn't the DAT block copied to cog RAM upon initialization?
Is there somewhere that I can read all about this? I know it must be pretty basic stuff.
Thanks again.
I'm with you! I've been so occupied with making cogs do one thing that it never occurred to me to do what pjv outlined. The sudden awareness of all the additional possibilities is blowing my mind. Maybe this is why Cluso, Heater, and others are so enamored with Hub RAM, and are constantly pining for more. It never seemed that useful to me except as a temporary code repository between EEPROM and COG RAM. Like Edwin Starr once said, "Hub - What is it good for? Absolutely nothing!" Or maybe he didn't say that.
Now that's funny. I think we may be conteporaries - and many others may be as bewildered by that comment as I have recently been with the nuances uncovered by this thread...
So I've come back to this thread for a refresh of my understanding, as I'm in the midst of a nasty debugging effort (and have run out of other ideas) and I found this to be a very helpful discussion the first time around. After the nth re-read, either I still don't understand this 100% correctly or there are some mistakes (or clarifications). Would you agree with these edits?
...and when used in a variable expression (i.e. SPIN PUB|PRI block), "label" refers to the contents of the hub memory location that label refers to.
It confused me so much I never use it ouside of Spin and pass all addresses in via par, which as kuroneko says, is clean.
Whish I had a Prop here to try it out on again.
1) When the compiler is evaluating @ (e.g. when initializing data, which of course means in a DAT block) the compiler has to figure out the value (at compile time). The compiler can't know the absolute address since it depends on where the object is located when the entire application is loaded into hub memory, so it can only supply an object-relative address.
2) When the SPIN interpreter is evaluating @ (e.g. in a variable expression, which means in a PUB|PRI block) the SPIN interpreter does have all the information needed to determine the absolute address in hub ram (runtime evaluation). This is why I am now tending to think of these as two different operators: one is a compiler "directive" (sort of), resolved at compile time; the other is actually a SPIN operator, resolved at run time (presumably) by executing some SPIN byte code to determine both the offset of the object in the application and the offset of the symbol within the object. As another way of thinking, it's almost as though the compiler evaluates @ during compile time in exactly the same way (in DAT and PUB|PRI blocks), and in addition inserts an "@@" SPIN (runtime) operator as well when in a PUB|PRI block.
This may not be the way others think of this, and I'm not saying it's necessarily the best way to think of it. But for the purpose of internalizing an accurate understanding of what is happening (in each use case), if anyone sees anything incorrect about this model of how @ works, please correct me.
PS - not that I'm an expert. I still haven't debugged my problem...
There are three different cases aren't there?
1) Knowing the offset of something within a module when the module is compiled. At that point the compiler does not know where the module is goiing to be in HUB so an offset is the best it can do.
2) Knowing the actual HUB address of something when modules are linked together. Now the linker knows where things will be.
3) Knowing the address of something that can move. The address of local variables and parameters can change depending an from where a method is called and the resulting stack depth.
The fisrt to can all be done statically ay build time. The last can only be done at run time.
But it seems the @s in Spin are fixed up when linking is done but the @'s in DAT are not, hence @someVar can be different in each case.
I was trying to think of a reason why all "@" (DAT and PUB|PRI) couldn't be resolved at "link" time. I was wondering if there was some cyclic problem that could occur (like the value of some constant (DAT) impacting the size of the OBJ itself, thus modifying the location of the OBJ and the value of the "@" when resolved) that would prevent "link"-time resolution, but couldn't come up with a realistic example of this.