How is this PASM 'ADD' code actually working when it shouldn't?
cbmeeks
Posts: 634
I swear, the more I dig into the propeller the more I enjoy it. But it opens up more questions!
Anyway, I'm studying a certain video driver from top to bottom and I came across a section that is actually working, but I don't see how.
Long story short, one COG can pass in a border color while the rendering COG draws it.
Here is the code snippet:
OK, so the user can pass in a border color from 0-15. If they use 0, then the border is black, etc.
What confuses me is the add statement. For example, say we are using border color 0. So, R1 gets loaded with 0 from the rdlong. Then, we mask it with the AND to prevent larger than 15 values. Next, we ADD the LITERAL value of PALETTE_PAPER to that 0. So the result should be $02. $00 + $02 = $02. Well, that is a coincidence because the first value at PALETTE_PAPER is $02 so it's black.
But it seems like any value I pass in is going to be BORDER_COLOR + $02. $0F + $02 = $11, etc. How are the other values even being used if everything is added to the first $02?
What am I missing here?
Thanks!
Anyway, I'm studying a certain video driver from top to bottom and I came across a section that is actually working, but I don't see how.
Long story short, one COG can pass in a border color while the rendering COG draws it.
Here is the code snippet:
DAT ..... COLOR_SYNC long $00 COLOR_BLACK long $02 COLOR_YHUE long $8A COLOR_BORDER long $02 ' Border color ..... PALETTE_PAPER long $00000002, $0000000B, $0000005B, $0000003B, $000000AB, $000000DC, $0000008C, $00000004 long $80000002, $8000000C, $8000005C, $8000003C, $800000AD, $800000DD, $8000008D, $80000005 .... mov R0, PAR add R0, #8 ' THIRD parameter rdlong R1, R0 ' R1 := Border color and R1, #$0F ' Border color is limited from 0 to 15 add R1, #PALETTE_PAPER movs :C0, R1 nop :C0 mov COLOR_BORDER, 0 ' Load the border color from the paper palette
OK, so the user can pass in a border color from 0-15. If they use 0, then the border is black, etc.
What confuses me is the add statement. For example, say we are using border color 0. So, R1 gets loaded with 0 from the rdlong. Then, we mask it with the AND to prevent larger than 15 values. Next, we ADD the LITERAL value of PALETTE_PAPER to that 0. So the result should be $02. $00 + $02 = $02. Well, that is a coincidence because the first value at PALETTE_PAPER is $02 so it's black.
But it seems like any value I pass in is going to be BORDER_COLOR + $02. $0F + $02 = $11, etc. How are the other values even being used if everything is added to the first $02?
What am I missing here?
Thanks!
Comments
This is tricky. A value is used to compute an address, and the literal #PALETTE_PAPER, is the base address as a value. The user parameter gets added to it to form an effective address.
That effective address gets written into the source of instruction :C0, where it's now treated as an address, not a literal.
The dummy value 0, in the source of instruction :C0 will be overwritten with the effective source address from the HUB read, and and add computation above.
This is like taking a value as an address on the 6502, and self-modifying the target instruction to create 16 bit indexes on that chip, which only has 8 bit indexes.
Same sort of thing going on here.
The nop is needed, due to the pipelining of instructions. If the self-modify isn't done in time, the value will arrive late, after the instruction is already in the pipeline, meaning the effective address computed above won't actually get used. Address 0 will.
Here is another way to think about what is happening by just hardcoding the address value:
See how that value gets transformed into an address?
At compile time, 5+PALETTE_PAPER would get evaluated. Say PALETTE_PAPER is $100. The value would be $105. That value gets stuffed into the mov instruction, and since it's literal bit isn't set, $105 becomes an address.
OK, I think I get it.
So, "#PALETTE_PAPER", even though that is a literal, it's also a label so it's using the ADDRESS at that label. So it's not really pulling in $00000002, it's pulling in some register address. So, 75 for example.
Then, it takes the border color (15) and does 75 + 15 = $80000005
Is that correct?
I was treating PALETTE_PAPER as a constant but it isn't.
Yes. Using a label defined in a DAT block from a DAT block always yields its cogram address. Using one from a Spin block yields its value.
#PALETTE_PARSER means use the cogram address of PALETTE_PARSER as a literal value and not an address. Using # tells the assembler to set the immediate source value (I) bit in that instruction, which tells the Prop to use the 9 bit value in the source field of that instruction as a value and not as an address at which it should look for a value.
Now, doing that add can be used in a loop though. You setup the right initial value outside the loop, and the add happens, but it's late and that's OK, because the value there will work first time through. Then, next time through the loop, the late add will get used, while the add to be late again gets used in the future.
Each loop iteration is using the add done from the one prior.
Where there isn't any "after" instruction to get stuffed in there in place of the nop, a pre-setup on a loop can maximize the time in the loop anyway. The cost is a couple more instructions of setup for the loop, but if it loops even a few times, it's usually worth it.