Shop OBEX P1 Docs P2 Docs Learn Events
How is this PASM 'ADD' code actually working when it shouldn't? — Parallax Forums

How is this PASM 'ADD' code actually working when it shouldn't?

cbmeekscbmeeks Posts: 634
edited 2015-12-02 19:05 in Propeller 1
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:
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

  • potatoheadpotatohead Posts: 10,261
    edited 2015-12-02 19:19
    Look at the movs instruction just below. It's writing the value of R1 into the source value of the instruction at label :C0.

    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:
    
                  movs      :C0, #5+PALETTE_PAPER
                  nop
    :C0           mov       COLOR_BORDER, 5+PALETTE_PAPER       ' Load the border color from the paper palette
    
    

    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.

  • AHHH!!!!

    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.

  • cbmeeks wrote: »
    AHHH!!!!

    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.
  • tonyp12tonyp12 Posts: 1,951
    edited 2015-12-02 20:37
    You could also do it in a other way, as showing two ways may make it clearer.
                  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
                  movs      :C0, #PALETTE_PAPER   ' reset pointer to first spot in table
                  add       :C0, R1
                  nop
    :C0           mov       COLOR_BORDER, 0       ' Load the border color from the paper palette
    



  • That is a good example.
  • Can one arrange the movs to eliminate the nop?
                  mov       R0, PAR
                  add       R0, #8                ' THIRD parameter
                  rdlong    R1, R0                ' R1 := Border color
                  movs      :C0, #PALETTE_PAPER   ' reset pointer to first spot in table
                  and       R1, #$0F              ' Border color is limited from 0 to 15
                  add       :C0, R1
    :C0           mov       COLOR_BORDER, 0       ' Load the border color from the paper palette
    
  • The add will be too late. Normal use is to put an instruction intended for a later operation there.
  • Ah, that's right. I was placing the 'and' in the delay slot, totally overlooking the fact that the 'add' is also modifying.
  • potatoheadpotatohead Posts: 10,261
    edited 2015-12-03 16:13
    Indeed it is :)

    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.

Sign In or Register to comment.