Problem with PASM: "Source register/constant cannot exceed $1FF"
turbosupra
Posts: 1,088
All three of these lines of code generate this error. Through searching I saw that some were setting constants, to an up to 31 bit signed value and then using cmp or mov with a pointer to that address. I can't do that with my code, as I'm doing math based on changing variables. How do you handle large equations with variables in PASM, that are larger than 9 bits? For additional information, they have to be done real time, and can't be set in spin before I launch the PASM cog.
Thanks for reading.
Thanks for reading.
cmp ((current/10)*100), ((previous7Avg/10)*110) WZ, WC cmp ((current/10)*100), ((previous7Avg/10)*90) WZ, WC mov toothRpm, (((clk/previous7Avg)*60)/36) WZ, WC
Comments
all constants >511 need to be declared as longs, usually in a group at the end of the pasm program. Those 0-511 can be declared in the code. So you do end up with slightly odd code like:
mov a,one_thousand
Second, you can't do multiplies and divides. Those are going to have to be done with some custom multiply and divide routines. These exist and there are many types, eg one byte times one byte is shorter code than one long times one long. So you have to think about the limits of the values.
Third, sometimes it is better to work with a different scale, eg multiply all your numbers times 256, do the multiply and divide, then at the end, divide by 256. I chose 256 because it is better than, say, 100, because you can multiply/divide numbers like 64,128,256 with bitshifts. It may be worth thinking about all the divisions you have and converting those to powers of two, then scaling the multiplies accordingly. Divisions are harder than multiplies. So you have a multiply there by 60 then a divide by 36. You could do that instead with a multiply by 427 then a divide by 256. The divide by 256 is a shr #8. You can get cunning too about the multiplies by breaking them down into bitshifts and adds. Then you don't necessarily need a multiply subroutine.
Fourth, you can't put things in brackets. All of that has to be expanded out to one single instruction at a time. Before converting spin to pasm, the first thing I tend to do is take all the code in brackets and expand it out to single spin commands. Then convert each of those to pasm.
Also. If you are going to use a number larger than $1FF (Dec ) you need to declare it as a long like I remembered there is no multiply either so look at this too http://forums.parallax.com/showthread.php?132608-PASM-multiply-questions
Don't forget the literal indicator either. *edit*
Beat me to the punch DOC, I hate when my fingers are faster than my brain.
The 32 bits of PASM code is divided up into groups of bits to hold the instruction, distination (address only), other stuff like flags and instruction code and finally the last nine bits holds either an address or a number to use directly as the source (one of the 32 bits tells the Prop which one). Nine bits is enough to hold the address of any location in the cog since its size is 2K (or nine bits). If you're not using an address but an actual number, it has to fit within the 9-bit source section of the PASM line.
I haven't read Pototohead's tutorial but deSilva's covers this. It took me several readings of deSilva tutorial to understand much of it.
When you declare a number larger than 511 like
does the compiler automatically know that particular long is not to be formatted as a typical register with 9 source, 9 destination bits, etc? Does it matter what value you initialize it to, as long as it is larger than 511?
Initialization depends on your needs. You could even initialize it to 0, doesn't matter.
A line of PASM instruction or a declared long just sets the 32 bits of that particular long to a certain value.
When you launch the cog, the cog "brain" (I know there's a better term) will assume the first line is an instruction and try to use it as such and then continue to the next long unless the current long (using it as an instruction) tells it to execute some other long (as with jmp). It doesn't care how you wrote the code (using instructions or just numbers).
http://forums.parallax.com/showthread.php?125188-PASM-self-modifying-code-the-best-method-to-do-this
[/code]
locallabel Mov regnumber,largenumber
[code]
Puts the contents of largenumber in the register regnumber at locallabel (label is optional)
Hopee this helps
Jim
Thank you, that does help.
So at compile time, I can set a long to a value of anything from 0 to 11111111111111111111111111111111 when I initialize it in PASM. And this assignment can change the long from being a general purpose register to an (up to) 32bit numeric value? But at this point, it becomes almost a constant?
How come this can't be done during execution time? It seems like it should be able to be done then as well?
Until I learn how to debug PASM, I can't try to get very cunning with it. Right now my debug is trial and error, which really hurts efficiency.
I may try some of the routines that average joe posted as well, it'd be much nicer if I could just use a method.
Note that this is different from the hub memory which is organized as 32K bytes. These can be treated as longs (4 bytes) or words (2 bytes) or as single bytes. In the case of longs and words, their addresses have to be at appropriate multiples of the size. The RDxxxx and WRxxxx instructions throw away the least significant bit or 2 bits of the hub address to make this so.
I was kind of in a hurry when I wrote that post. After rereading it, I'd be surprised if many people understood it (including those versed in PASM). I'll try to come up with an example.
@Turbo, I'm pretty new to PASM myself but have worked in assembly before. If you can afford a few pins, debugging by leds can be a huge help. When all your leds are used, one way I debug things is declare a space in main memory for passing stuff between spin and asm. This can be done with RDLONG, RDWORD, RDBYTE, and WRLONG... and so on. The easiest example I can think of is Cluso's ramblade driver... the portions in question go This passes 4 variables in, and one variable out.
I thought we already talked about this intermediate values and that these can only have 9 bits because they have to fit into the 9 bit source part of an instruction ... and all values higher than that have to be stored in a long when talking about a PASM-section.
I also thought we already talked about the fact that you can't do calculation in PASM that easy.
What the propeller tool currently tries to do is calculate those values for storage in destination and source part of an instruction. But it can NEVER calculate values stored in these variables, it only uses their address. This sometimes makes sense, but not in your case, as you really want to deal with the content of variables!
Ok .. the point with math is, that the propeller tool has 2 different ways of how to treat / * + - and such. One way is when it's used in a CON - section. In a CON you define values to be represented by a constant name. So, whenever such a name is found in the code later on, it's replaced by exactly this value. If you use calculations in these definitions, the compiler is doing those calculations on compile-time.
For example:
If you use calculations in SPIN, the compiler really generates code out of it, which is executed on runtime.
Also variable names from a DAT-block are locically treated different in SPIN than in PASM. If you use a DAT-variable in SPIN it's actually reading FROM/writing INTO the memory location. If a variable-name is used in PASM, the memory address (a COG-RAM address) is encoded into the destination/source-address-field of the PASM instruction.
The point is now, that in PASM calculations have the same meaning as in the CON-blocks whereas the variable name is actually treated like an address in the PASM section. That's why:
cmp ((current/10)*100), ((previous7Avg/10)*110) WZ, WC
can be a valid code snippet which really can be compiled, but it's NEVER doing what you expect.
((current/10)*100) means: take the address of the variable current, divide it by 10 and multiply the result by 100, which will work if the address of current is between 0 and 51.
I'm not sure what you mean by that first line, but we did talk about those things and your explanations were the only reason I was able to get this far, even if "this far" is no where. I remember you saying math is quite difficult in PASM, and as reluctant as I was to try it, I'm not sure I have a choice given the speed that I apparently need.
I'm still not clear how if everything is a long in cog ram ... then how some longs are defined as registers with different groups of bits blocked together inside of the 32 bit long, and some longs are a 32 bit block as a single grouping, but I'll keep reading. At this point, since I was able to generate the signals exactly with spin methods, I'm just looking for the ability to compile and start experimenting/fixing my code to see if I can get it to work.
When the COG is started, it copies 2K of data (512 longs) from the HUB, into the COG memory space. HUB memory is byte, word, long addressable. COG memory space is only LONG addressable. Once the COG is full, program execution begins at COG address 0, the very first LONG in the COG. This happens no matter what. So if we put real program number values in there, the COG will do something useful. If there are just numbers in there, the COG will just do something.
The only difference between program and data is the programmer intent. A LONG is a LONG. So, if a LONG is going to be a program instruction, it can only hold 9 bits of information, because the rest is needed for instruction purposes. If a LONG is going to be data, it can hold 32 bits of information, because it's not going to be used as an instruction. Instructions might reference other instructions, and that's generally what the 9 bits are for, that and for specifying small values that make sense in the context of a program. Number of times to shift, etc...
When you put complex math into PASM statements as you have done, the compiler evaluates those and attempts to put the result into the memory. Those values go into HUB memory, and are static. The result will be pre-determined prior to your program actually running. Once you fire off a PASM COG, that HUB data gets copied to the COG, and your program starts!
Typical practice is to organize your PASM with your program at the beginning, starting at COG address 0, and to pack in all the values it needs at the end. It all goes in a DAT block. I like to use two DAT statements per PASM program image. The first one is the program, and it's starts with the org 0,and contains the program. I'll type DAT again, to get the color change, then put all my values there. The 'distance' between the start of the program, and the last value needs to be less than the number of LONGS that get copied to the COG, or there will be some values that don't make it!
If you have too many values to stuff into a COG, or need to work with bigger sets of memory, arrays, buffers, etc... then you leave them in the HUB, using the PASM HUB operations to operate on them from PASM. wrbyte, rdbyte, wrlong, rdlong, etc....
The key to remember is in the COG, a long is a long. If you put an instruction in it, there isn't room for big values. If you don't put an instruction in it, there is room for big values. When you use the "#" to combine a value and an instruction, you've only got 9 bits of room for that value.
From there, it's always memory to memory. Take the instruction 'add destination, #source' as opposed to 'add destination, source' The first form has the "#", meaning the number to add to the destination and the result to store in the destination is provided as part of the instruction itself! That's only 9 bits worth. The second form indicates that no value is supplied as part of the add instruction, meaning it comes from one of the other COG LONGS. That's 32 bits worth.
PASM is not a load, store design. It's a memory to memory design, with instructions and data all existing in the same LONG addressable memory space.
My best advice. Take what you have in spin and turn it into a flowchart. Then take that flow chart and start over in PASM one instruction at a time. This is what I did when re-writing my display drivers in PASM. They worked, but have been quite improved since I got a better grasp on the actual display hardware. Start with the basics. Start a cog at an address, and return pass or fail. Then pass some stuff in, return it and check the results.
1. The longs that are part of YOUR program path -> these are decoded by the COG like explained on some other thread, containing instruction, conditional flags, modifiers, destination and source address.
These longs you usually define by using PASM instructions, but as Duane said, with the description given in the propeller manual, you could also set each bit of those longs manually to make it an instruction.
The program path always starts at ORG 0 and some special instruction like JMP, CALL ... are used by your program to define the rest of the program path.
2. The longs that are NOT part of your program path. For the COG themself these longs don't mean anything! YOUR code tells the COG how to use those longs. You can tell the COG to use a long as a 32 bit counter for example: But your code could also use the COG-RAM not being part of the program path to be byte-values: So, for all longs which have a different meaning than a 32bit long, YOU have to tell the COG what to do with these - how to extract the parts.
Hope this makes things a bit clearer ;o)
I'm not sure if this example will help much but I think it's cool so I'll post it.
I wrote a program to output the PASM section as long values. The PASM section cycles through the eight LEDs on a QuickStart board and write the current LED number to a variable in hub RAM.
Writing to hub RAM is one way of debuging PASM.
Here's the code (non-masochistic version):
This is the output from the above code.
The last character, "2" in this case, continues to cycle from 8 to 1, starting again at 8.
This last number is being changed by the PASM code.
I copied the output starting at "entry" down to "long 8" and pasted it between "org" and "fit" of the original program (overwriting the PASM code).
Here's the modified program.
Notice the difference in the PASM section of this program and the top program I posted. While they look very different, they run exactly the same!
Pretty cool!?
My first attempt at posting this was eaten by the forum software so I'm posting this without previewing it. I'm currently editing it and attaching the two programs as Spin files so refresh the screen every so often until this last sentence disappears.
Besides attaching these two files, I was going to bold the PASM section of code from the two programs.
I hope all this code isn't too annoying but I'm posting just the PASM sections again and I want to emphasize these to blocks of code are identical as far as the Propeller is concerned.
Now the same code as above written as longs.
With PASM's add function as used in your short counter program, you have the following "format" for myCntr
–INSTR– ZCRI –CON– –DEST– –SRC–
100000 001i 1111 ddddddddd sssssssss
So how would it store something larger than the destination address (512) if add formats the long in the previously mentioned manner? (–INSTR– ZCRI –CON– –DEST– –SRC–)
If add is preprogrammed to "format" the long register that way, how does that change? Does the literal (#1) tell the add method to ignore it's normal register format and just increment the literal number + the originating value? How can you tell it to just format the long without any instructions, flags, destination or source?
Either way, it's just a number. With the # present, the number is used for the value in the addition directly. Without the # present, the number contained in the instruction is actually the address of the value needed for the addition.
Say there are two add instructions. One at COG address 2, and one at COG address 3. They are:
add total, #45
add total, 45
The label "total" is associated with COG address 10, and that COG address contains the value 5. COG address 45 contains the value 10.
The first add instruction takes the number 45 contained in it's source bits, adds it to the value 5 contained in the COG address 10, associated with the label "total", and writes the sum of that, value 50, into COG address 10, again referenced by label "total" This could be: "add 10, 45" too, it's just that we don't see that because we use labels.
The second add instruction takes the number 45, contained in it's source bits and instead of using it as a value directly, uses it as a COG memory ADDRESS, fetching the value 10, adds it to the value 5 contained in COG address 10, referenced by label "total", writing the sum of that, 15, into COG address 10.
Destination is always an address. Source can be either an address or a value, depending on whether or not # is present in the instruction.
Now, it might be more clear what happens when something bigger than 9 bits gets specified with the # present! There literally is no room for the data to go. All the other bits are spoken for. The correct form for such values is to put a label on them, so the source ADDRESS of that value can be properly specified.
Say everything above is the same, but we want to add $1055. This is too big. So, we put the label "too_big" on a free COG memory address thus:
too_big long 1055
Say that lives at COG memory address 50. Now the add instruction would look like:
add total, too_big
When the prop tool compiles it, the instruction ends up being "add 10, 50", where the 10 is the address referenced by the label "total", and the 50 is the address referenced by the label "too_big". It then sees that COG address 50 contains the value 1055, and it sees that COG memory address 10 contains the value 5, does the add and writes the value 1060 into COG address 10, which is referenced by label "total"
Now, interestingly, one could do the add like this:
add total, #too_big
And it will work too! What happens? The prop tool compiles that to be: add 10, #50, which is an entirely different thing! Now, instead of adding the value at COG address 50, it will just add the ADDRESS, as if it were a VALUE, resulting in the VALUE 55 being written to COG address 10.
Look at the two pictures of the Prop Tool, I've attached. Edit: I've added one more to cover the more common case of just including a value as part of an instruction. (add-03a.jpg)
No, myCntr does not have this format! Only PASM-instructions have this format -> all longs that are on the program path. For all other longs you have free control of the meaning of the 32bits. Easiest case is, that you use them as longs. That's what the counter program is doing! It simply counts up the whole range of a long from 0 to 2^32-1 starting over from 0 again.
But as COG-RAM is very limited, you'll sometimes add code to pack smaller pieces toghether in one long. That's what the second example shows. Using one long for data that fits into a byte is a waste you sometimes can't efford.