Shop OBEX P1 Docs P2 Docs Learn Events
Problem with PASM: "Source register/constant cannot exceed $1FF" - Page 2 — Parallax Forums

Problem with PASM: "Source register/constant cannot exceed $1FF"

2»

Comments

  • turbosupraturbosupra Posts: 1,088
    edited 2012-04-09 06:10
    Ok,

    Good morning, thank you both, I think I understand now.

    The instruction code formats its longs with –INSTR– ZCRI –CON– –DEST– –SRC– . And with math, up to 511 bits it can do within the actual constraints of its format. Above a value of 511 it requires a pointer to continue these math operations, because the pointer name can fit inside of the constraints of the instruction, while the pointer value (above 511) cannot. That leads me to believe that there is some sort of sandbox, outside of the instruction, where math is actually done that always has a full 32 bits available for computation?

    So let's say I'm trying to still do the following
    cmp     ((current/10)*100), ((previous7Avg/10)*110) WZ, WC
    
    current is 26400 and previous7Avg is 13200


    I'd have to do something like the following (I know there are no mul/div functions, I used those for illustration)
    div current, ten
    mul current, hundred
    
    div previous7Avg, ten
    mul previous7Avg, hundredten
    
    cmp current, previous7Avg WZ, WC
    
    long ten 10
    long hundred 100
    long hundredten 110
    
    

    This way, I'm using addresses and staying away from literals and the constraint of 511, the compiler won't complain and my math should actually work?
  • turbosupraturbosupra Posts: 1,088
    edited 2012-04-09 06:28
    Hi Duane,

    Is the difference between the 2 programs just static vs dynamic values?

    One calculates the numeric values and the other has them hard programmed in?

    The one with just numbers, I can't make sense of how it works :)



    Duane Degn wrote: »
    I wasn't able to edit the above post. The forum software attempted to eat it again.

    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.
  • potatoheadpotatohead Posts: 10,261
    edited 2012-04-09 07:16
    You are getting really close now!

    The instructions are just numbers. In fact, there are just COG memory locations, and they just contain numbers. Some numbers happen to be valid instructions! That is what the compiler / assembler does for us. We find it hard to just remember which numbers to use for instructions. So we use names of instructions and labeles for memory addresses and such, leaving the task of sorting out the numbers to a computer, which is very good at it.

    Most numbers are just numbers however. Duane is just trying to show how "a number" isn't anything special. There are just COG memory locations, and there is a program counter too. When the program counter is pointed at one of those COG memory locations, it's going to take the number there, or the bits, and treat it like an instruction. That is all that happens. One of the things we need to watch for is losing control of the COG. If we know where the program counter will go, the numbers that are instructions that mean something to us will happen. If we make a mistake, and it gets pointed somewhere we don't know is valid instructions, then it just does stuff... generally not useful.

    Yes, you put your bigger values into COG memory locations, so that instructions can point to them and perform operations. That's exactly the workflow you need. Often, it's worth thinking about all the values a person needs, and factor those down to a reasonable set, because the more values one has, the shorter the PASM program can be, unless it somehow overwrites those values with ones that would be meaningful instructions. (nothing is off the table in PASM)
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-04-09 08:00
    turbosupra wrote: »
    Hi Duane,

    Is the difference between the 2 programs just static vs dynamic values?

    One calculates the numeric values and the other has them hard programmed in?

    The one with just numbers, I can't make sense of how it works :)

    While the two programs look different to human eyes, they look identical to the Propeller. They both work exactly the same when loaded into a Prop.

    If I were a true masochist, I'd try to figure out what these numbers were without the Prop telling me (one feature of the program).

    As potatohead explained, instead of typing in a program as a list of numbers, the Prop Tool lets us use nicknames that are easier for humans to read.

    You may notice when two PASM lines are very similar:
                            shl     ledOnMask, firstLedPin
                            shl     ledOnMask, ledsToLight  ' shift to one past last LED
    

    The numeric equivilent of those instructions are also similar.
                            long    7505280[B]15
    [/B]                        long    7505280[B]18[/B]
    

    We know the last nine bits is the source, so it looks like the source "firstLedPin" (the human friendly name) is the sixteenth long in the cog (we start counting with zero so 15 is the 16th long).

    The next line's source is long 18 (19th of cog) with is where "ledsToLight" is stored.

    These four lines:
                           [B] long    16
    [/B]                        long    16711680
                            long    40000000
                           [B] long    8[/B]
    


    Are the same to the Prop as these four lines:
    [B]firstLedPin             long 16
    [/B]ledMask                 long %0000_0000_1111_1111_0000_0000_0000_0000
    waitCycles              long 40_000_000
    [B]ledsToLight             long 8[/B]
    
    I'm sure glad the Prop Tool does this all automatically and we don't have to count memory locations and fill all the bits of a number ourselves.
  • turbosupraturbosupra Posts: 1,088
    edited 2012-04-09 09:15
    Ok, so the numeric values in Duane's code (I would have actually expected a 32bit binary string for this instead) represent what the labels/registers are doing?

    That makes sense theoretically, I'm just still not sure how the magic happens :)
  • potatoheadpotatohead Posts: 10,261
    edited 2012-04-09 10:16
    Yes.

    It's all just numbers. Inside the Prop, there is an ALU (arithmatic logic unit) which does the math and logic operations. There are 32 bits in that thing, plus the carry for larger operations. As the programmer, the only worry is argument size, type and location.

    And he could have done a 32 bit binary string. In fact, you can, and should. That would be a great exercise. Convert the numbers he's got there into binary and insure the program and by definition, the values all match.

    In old, ancient times, when I was first learning assembly language on Apple computers, we had this exact same discussion. The first programs were done just by entering bytes into the RAM. Hex byte values: $A0, $CC, $1E, $45, $23, etc.... Once they got put in, just point the CPU at them, with a "GO" command and see what happens!

    This isn't much different, only we've got the nice PC tools today.

    IMHO, there really aren't registers like we see on other CPUs. Props have internal counters, the ALU, program counter, flags and such. Those are registers, along with the special purpose ones, CNT, INA, OUTA, etc... Typically, "register" refers to a storage entity or control entity within the CPU that isn't addressed like RAM memory storage is. Maybe there is the A register, or R1 - R8, etc... On the Prop, one could think of the JMP instruction more like mov, where the destination is the PC register, instead of a COG memory address. That's really a register in the sense I think you are looking for.

    From there, every address in the COG just contains numbers. It is really a memory to memory design in every other way. One nice thing about this is self-modifying code is easy, one arguably less nice thing is self-modifying code is necessary too.

    I hate to invoke something new here, but you can also put values in the HUB. The workflow would be rdbyte, rdword, rdlong into a COG "register", do the math, then make a decision, then put the result back into the HUB, with wrbyte, wrword, wrlong. In this way, the entire COG is registers, with you able to define as many as you need with simple labels and such. If you have a lot of values, or multiple COGS will operate on them, the HUB is the way to go. I'll stop there though, let's get this sorted first. :)
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-04-09 10:26
    Why do you want to divide by ten and then multiply by 100? This only makes sense if you want to get rid of the last 2 digits in the end-result. Otherwise it would be wiser to simply do a multiply by 10!

    26400 / 10 * 100 = 2640 * 100 = 264000
    26400 * 10 = 264000

    the difference is here - let current be 26423:

    26423 / 10 * 100 = 2642 * 100 = 264200
    26423 * 10 = 264230

    Same question for the other calculation! Doesn't your original calculation add inaccuracy?

    If you only want to multiply by 10 you can hardcode a multiply by 10 instead of having a general loop the multiplies any factors.
    MOV result, current
    SHL result, #2 ' this is equal to *4
    ADD result, current ' this makes it *5
    SHL result, #1 ' here we have * 10

    another add result, current would make it *11 which is what you need in the other formula.
  • tonyp12tonyp12 Posts: 1,951
    edited 2012-04-09 10:43
    COG ram is simply 512 longs of 32bit "bytes/blocks", The Prop does not know the difference between Code and Data.
    You just have to make sure the Program Counter does not walk in to Data territory by having a jump instruction at the end of code block.

    You can point to any cog ram location by just using a 9bit address as cog ram is rather small, where you store a 32bit value to use.
  • turbosupraturbosupra Posts: 1,088
    edited 2012-04-09 10:50
    Ok, my brain is having a dimly lit LED above it, start to illuminate with that last paragraph. The alu sounds like what I was calling a sandbox, so that makes a lot of sense.

    I tried to convert Duane's numbers to binary, but something is missing. It still prints out the same text, but the functionality is wrong because some of the numbers aren't negative. I thought you made the MSB a 1 to represent a negative, but that didn't work? The first block of code is the MSB =1 code, and the second block is with no 32nd bit on the negative numbers.


    DAT
                            org
                            
    entry                   long    %1101000101111111110110000010000
                            long    %11011111010000111101011000010000
                            long    %11011111010000111101001000001111
                            long    %11111111010000111101001111101111
                            long    %1011111010000111101010111101110
                            long    %11011111000000111101100111111111
                            long    %101100101111000010011000001111
                            long    %101100101111000010011000010010
                            long    %101000101111000010011000010101
                            long    %1101000101111111110100000010011
                            long    %1000001111000010101000010100
                            long    %10000111010000111101001111101111
                            long    %1100100101111111110100000010011
                            long    %10011011000000111101010111111011
                            long    %1011100011111000000000000000100
                            long    %10000
                            long    %111111110000000000000000
                            long    %10011000100101101000000000
                            long    %1000
    
    noLongerPasm  byte 0 
    
    DAT
                            org
                            
    entry                   long    %1101000101111111110110000010000
                            long    %1011111010000111101011000010000
                            long    %1011111010000111101001000001111
                            long    %1111111010000111101001111101111
                            long    %1011111010000111101010111101110
                            long    %1011111000000111101100111111111
                            long    %101100101111000010011000001111
                            long    %101100101111000010011000010010
                            long    %101000101111000010011000010101
                            long    %1101000101111111110100000010011
                            long    %1000001111000010101000010100
                            long    %0000111010000111101001111101111
                            long    %1100100101111111110100000010011
                            long    %0011011000000111101010111111011
                            long    %1011100011111000000000000000100
                            long    %10000
                            long    %111111110000000000000000
                            long    %10011000100101101000000000
                            long    %1000
    
    noLongerPasm  byte 0    
    
  • turbosupraturbosupra Posts: 1,088
    edited 2012-04-09 11:02
    The reason I did that in spin is for resolution compensation, IIRC the *110 was causing 32bit roll over with some numbers, at one point I was also using 105/95 as well. I believe it does add a little inaccuracy, that was at the expense of not having to use floats. You may do a facepalm with my most recent topic, but it was a huge stretch for my abilities and I don't even know if the logic flow is any good, I'm learning!

    That multiply by 10 is a pretty sweet trick! :-D

    Since I am using 90 and 110 now, I think you are right, I could get away with 9/10/11 instead of 90/100/110!

    Thanks Mag, I like that binary math trick you did. I guess any multiply is never more then an add of the original value, plus a few SHL commads?

    Could you do:
    MOV result, current
    MOV result2, current
    SHL result, #3 ' this is equal to *8
    SHL result2, #1 ' this is equal to *2
    ADD result, result2
    result is now equal to current * 10?


    MagIO2 wrote: »
    Why do you want to divide by ten and then multiply by 100? This only makes sense if you want to get rid of the last 2 digits in the end-result. Otherwise it would be wiser to simply do a multiply by 10!

    26400 / 10 * 100 = 2640 * 100 = 264000
    26400 * 10 = 264000

    the difference is here - let current be 26423:

    26423 / 10 * 100 = 2642 * 100 = 264200
    26423 * 10 = 264230

    Same question for the other calculation! Doesn't your original calculation add inaccuracy?

    If you only want to multiply by 10 you can hardcode a multiply by 10 instead of having a general loop the multiplies any factors.
    MOV result, current
    SHL result, #2 ' this is equal to *4
    ADD result, current ' this makes it *5
    SHL result, #1 ' here we have * 10

    another add result, current would make it *11 which is what you need in the other formula.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-04-09 11:11
    Okay, now the forum software (or my computer) wont let me use the quote feature.

    Your comment about making the MSB "1" to make a binary negative isn't correct. There's more to it than that but I don't really know what the "more" is.

    I think it would be easier to let the Spin program to the work of printing binary instead of integers.

    Just change:
    Debug.Str(string(13, "The PASM section of code in this program could"))
      Debug.Str(string(13, "be written using the following long values:", 13))
      localPtr := @entry
      Debug.Str(string(13, "entry                   long    ")) ' we still need a label to launch PASM
      Debug.dec(long[localPtr])
      localPtr += 4
      repeat while localPtr < @noLongerPasm
        Debug.Str(string(13, "                        long    ")) 
        Debug.dec(long[localPtr])
        localPtr += 4
    

    To:
    Debug.Str(string(13, "The PASM section of code in this program could"))
      Debug.Str(string(13, "be written using the following long values:", 13))
      localPtr := @entry
      Debug.Str(string(13, "entry                   long   %")) ' we still need a label to launch PASM
      Debug.bin(long[localPtr], 32)
      localPtr += 4
      repeat while localPtr < @noLongerPasm
        Debug.Str(string(13, "                        long   %")) 
        Debug.bin(long[localPtr], 32)
        localPtr += 4
    

    I haven't tested the modification, but I don't see why it wouldn't work.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-04-09 11:19
    Brad, If you still want to convert the integers to binary yourself and you're using the Windows calculator in "programmers" mode, click the button next to "Dword" to limit it to 32 bits.

    When I convert "-1598281232" to binary this way I get "10100000101111000010100111110000". I think this is different than the two binary values you listed (though I'm not absolutely sure it's the same as what the Prop would give).
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-04-09 11:24
    SHL result2, #2 ' this is equal to *2

    this is *4 and NOT *2. SHL is a binary multiplication so to say:
    13 = %00001101
    shift it once makes it
    %00011010 = 26
    shift it twice makes it
    %00110100 = 52

    But yes, a multiplication is simply to know about when to add and when to shift: multiply x by 10 means
    *2 *2 +x *2 = *4 +x *2 = *5 *2 = *10
    multiply x by 9 is
    *2 *2 *2 +x = *8 +x = *9

    It is possible to write a loop which allows to multiply any factors. But if you only have a little amount of constants to multiply with, it might make sense to do the multiplication with a dedicated sequence of shift and add-instructions.
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-04-09 11:34
    Making a binary number a negative number is called a two's complement. So, you invert it (binary) and then add 1

    1 = %0001 -> make it negative: - %0001 = %1110 + 1 = %1111

    This is because this way the math of binary numbers negative and positive is still working:
    -1 + 1 = %1111 + %0001 = %0000 ( on a 4 bit CPU of course ;o)

    actually it would be %10000, but on a 4 bit CPU this MSB would be cut off.
    -1 + 2 = %1111 + %0010 = %0001
  • turbosupraturbosupra Posts: 1,088
    edited 2012-04-09 11:43
    @Mag, ok now I see what went wrong. The converter I used must not have done binary values correctly and when I tried to correct for that I did it incorrectly by not inverting, etc. That bitshift keeps getting me with the syntax, ugh, I will beat it into my brain though so I use the bit place holder number instead of the bit numeric representation.

    That worked Duane, thanks.

    I then copied it to the DAT section and it still worked! :)

    DAT
                            org                        
    
    entry                   long   %01101000101111111110110000010000
                            long   %10100000101111000010100111110000
                            long   %10100000101111000010110111110001
                            long   %10000000101111000010110000010001
                            long   %10100000101111000010101000010010
                            long   %10100000111111000010011000000001
                            long   %00101100101111000010011000001111
                            long   %00101100101111000010011000010010
                            long   %00101000101111000010011000010101
                            long   %01101000101111111110100000010011
                            long   %00001000001111000010101000010100
                            long   %11111000101111000010110000010001
                            long   %01100100101111111110100000010011
                            long   %11100100111111000010101000000101
                            long   %01011100011111000000000000000100
                            long   %00000000000000000000000000010000
                            long   %00000000111111110000000000000000
                            long   %00000010011000100101101000000000
                            long   %00000000000000000000000000001000
    
    noLongerPasm  byte 0  
    
Sign In or Register to comment.