Shop OBEX P1 Docs P2 Docs Learn Events
Small Assembly example please — Parallax Forums

Small Assembly example please

krazyideaskrazyideas Posts: 119
edited 2012-08-09 17:51 in Propeller 1
I'm working on my first assembly program.

So I have figured out how to get waitcnt to work. By work I mean I carefuly went through the example in the prop manual and it makes sense to me. But I can't get waitpeq to work.
I think I get how to do the rest of what I want. Namely grabing the cnt value, subtracting values, and I think I can divide values (we will see on this one), then triggering a output pin

That brings up another question also. How does the dividing of numbers work in Binary? I have noticed that in spin that there are several different numbers that you can divide and still come up with the same answer. For example say 300/20 = 15 but 300/21 = 15 too. does the prop just round it or trunk it or what??

Anyway back to the orginal question

I don't understand how to identifi the Pin to watch or to I dentifi it's state.

I don't really understand the terms "state" or "Mask" in the prop manuel in regards to waitpeq or any other command for that matter.

What I am doing is
waitpeq pin, pin

pin  long |< 15



But no matter how I guess at it, it still don't work.

The goal is to put the waitpeq into the example in the book (I can't remember the page but it's in the introduction to assembly about 320 or something) so that it won't begin triggering pin 16 until a pin goes high.

I small example of how to use waitpeq would be awsome

Thanks

Comments

  • kuronekokuroneko Posts: 3,623
    edited 2012-08-02 01:40
    krazyideas wrote: »
    I don't understand how to identifi the Pin to watch or to I dentifi it's state.

    I don't really understand the terms "state" or "Mask" in the prop manuel in regards to waitpeq or any other command for that matter.

    What I am doing is
    waitpeq pin, pin
    pin  long |< 15
    

    But no matter how I guess at it, it still don't work.
    The generic form of this insn (and its brother) is waitpxx state, mask. Both work on masks (i.e. |< pin, not pin numbers like counters). The function performed by a waitpeq is
    1. grab ina
    2. and it with mask (extract pins/bits of interest)
    3. compare against state
    4. if equal continue with next insn otherwise go back to step 1
    So for your example this would mean we deal with pin 15 (zero-based). The waitpeq extracts said pin and compares it against itself (state == mask). Which means that in order to become true pin 15 must become high:
    ina: %xxxxxxxx_xxxxxxxx_?xxxxxxx_xxxxxxxx
                            AND
     mask: %00000000_00000000_10000000_00000000      ' |< 15
                             V
           %00000000_00000000_?0000000_00000000
                           eq/ne?
    state: %00000000_00000000_10000000_00000000      ' |< 15
    
    HTH
  • krazyideaskrazyideas Posts: 119
    edited 2012-08-02 07:42
    Thanks but I still don't understand.

    Mabe what I should ask is what does ANDed, ORed mean or do.

    From what I understand (uber beginer) my little code should have worked.

    The waitpeq grabed the ina register. Then it checked to see if the state and mask were equil to each other.
    OK so that does not make sense to me. If the waitpeq grabs the ina register all at once why does it have to have a mask?? shouldn't it just wait for the ina reg to match the state?

    I'm not understanding the steps you don't see in the command.

    Is it that waitpeq grabs the ina register.
    The state is the pin or pins to watch
    and the mask is what you want those pin to == ?

    Also do you think it would be easyer for a beginer such as my self to write out the entire registers, masks and states in Binary like in your example to help me see what I'm trying to do as I work with assembly to start with

    Sorry and Thanks
  • ElectrodudeElectrodude Posts: 1,665
    edited 2012-08-02 08:39
    Waitpeq waits until all of the bits of ina set in mask are equal to state.
    eg:
    DAT
    
                  waitpeq   state,  mask
    
    state         long      000000000000000000000000000101
    mask          long      000000000000000000000000001111
    
    would wait for pins 3, 2, 1, and 0 to equal 0, 1, 0, and 1, respectively.
  • krazyideaskrazyideas Posts: 119
    edited 2012-08-02 09:24
    Thanks I get it now.

    waitpeq checks the ina register but is only concerned with the bits set to 1 in the Mask, and waits for those pins set to 1 in the mask to match the state that is in the state part of the expression.

    Mask sets the pins to watch
    state is the states of those pins

    Thanks
  • krazyideaskrazyideas Posts: 119
    edited 2012-08-02 12:11
    Ok so apparently I still don't get it.
    here is what I am doing.
    CON
    _clkmode = xtal1 + pll16x
    _xinfreq = 5_000_000
    
    
    PUB Main
    {Launch cog to toggle P16 endlessly}
    cognew(@Toggle, 0) 'Launch new cog
    
    DAT
    {Toggle P16}
                  ORG 0                             'Begin at Cog RAM addr 0
                  
    Toggle        waitpeq state, #mask              'wait for Pin 3 to be high
    
                  mov dira, Pin                     'Set Pin to output
                  mov Time, cnt                     'Calculate delay time
                  add Time, #$f                     'Set initial delay here
                                
    :loop         waitcnt Time, Delay               'Wait
                  xor outa, Pin                     'Toggle Pin
                  jmp #:loop                        'Loop endlessly
    
    
    state         long %0000_0000_0000_0000_0000_0000_0000_1000    'Wait for Pin 3 to be high
    mask          long %0000_0000_0000_0000_0000_0000_0000_1000    'Monitor Pin 3
                 
    Pin           long |< 16      'Pin number
    Delay         long 60_000     'Clock cycles to delay
    Time res      1               'System Counter Workspace
    

    'The waitpeq only works on pin 3 (%0000_0000_0000_0000_0000_0000_0000_1000)
    'if I enter %0000_0000_0000_0000_0000_0000_0000_0001 in both the state and mask var's it wont work.
    'It won't work on pins 1,2,4,5,6,7 either
    'Why will it only work on pin 3
    'I have nothing but a scope hooked up to pin 16 so I can see what it is doing.

    'I am lost.
  • kwinnkwinn Posts: 8,697
    edited 2012-08-02 12:37
    It sounds like your pins may be floating. When pins are set to input they are "floating" (have high input impedance) so they may be high or low at any point. Try putting a resistor from the pin to +3.3V and use a switch or push button connected to ground to make it low.
  • kuronekokuroneko Posts: 3,623
    edited 2012-08-02 16:08
    If you want mask to have any effect you should use waitpeq state, mask (without #). As it is now you always use a mask of 8 (%1000) which is the register location of mask.
    PASM: waitpeq state, mask
    SPIN: repeat until state == (mask & ina)
    
    PASM: waitpne state, mask
    SPIN: repeat until state <> (mask & ina)
    
    Note that due to the way this is all evaluated you may end up with cases where waitpxx never exits or never waits (independent of external signals), e.g.
    waitpeq $, #0    ' never exits, $ represents the location of the waitpxx instruction, e.g. current PC
    waitpne $, #0    ' never waits
    
  • krazyideaskrazyideas Posts: 119
    edited 2012-08-03 00:09
    Ok I got it working properly now.
    Thanks a ton guys.

    So next question. Dividing, and timesing. Not as easy as I thought.

    I thought I could just use Mov like this
    Mov mark, (take / split)
    
    take long 5000
    split long 300
    Mark res 1
    

    But no chance I guess.
    I have heard of useing addtion to multiply some how but I do not recall how that works.
    What I really want to do is divide.

    Can someone point me down the right road.

    Thanks again
  • msrobotsmsrobots Posts: 3,709
    edited 2012-08-03 00:20
    Well dividing in PASM is not that easy.

    Chip did the following in the Spin-Interpreter...KYE posted this not long ago and I kept it ...
    ' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    '                       Unsigned Divide
    ' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    LNDivide                mov     LNDivideQuotient,       #0                           ' Setup to divide.
                            mov     LNDivideBuffer,         #0                           '
                            mov     LNDivideCounter,        #32                          '
    
                            cmp     LNDivideDivsor,         #0 wz                        ' Clear if dividing by zero.
    if_z                    mov     LNDivideDividend,       #0                           '
    if_z                    jmp     #LNDivide_ret                                        '
                         
    LNDivideLoopPre         shr     LNDivideDivsor,         #1 wc, wz                    ' Align divisor MSB and count size.
                            rcr     LNDivideBuffer,         #1                           '
    if_nz                   djnz    LNDivideCounter,        #LNDivideLoopPre             '
                                                      
    LNDivideLoopPost        cmpsub  LNDivideDividend,       LNDivideBuffer wc            ' Preform division.
                            rcl     LNDivideQuotient,       #1                           '
                            shr     LNDivideBuffer,         #1                           '
                            djnz    LNDivideCounter,        #LNDivideLoopPost            '
                            
    LNDivide_ret            ret                                                          ' Return. Remainder in dividend on exit.
    
    ' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    '                       Unsigned Multiply                            
    ' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    LNMultiply              mov     LNMultiplyProduct,      #0                           ' Clear product.
    
    LNMultiplyLoop          shr     LNMultiplyMultiplicand, #1 wc                        ' Preform multiplication.
    if_c                    add     LNMultiplyProduct,      LNMultiplyMultiplier         '
                            shl     LNMultiplyMultiplier,   #1                           '     
                            tjnz    LNMultiplyMultiplicand, #LNMultiplyLoop              '  
          
    LNMultiply_ret          ret                                                          ' Return.
    
    ' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    '                       Data
    ' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     
    ' //////////////////////Run Time Variables/////////////////////////////////////////////////////////////////////////////////////
    
    LNDivideBuffer          res     1
    LNDivideCounter         res     1
    LNDivideDividend        res     1
    LNDivideDivsor          res     1
    LNDivideQuotient        res     1
    
    LNMultiplyMultiplicand  res     1
    LNMultiplyMultiplier    res     1
    LNMultiplyProduct       res     1
    

    Enjoy!

    Mike
  • krazyideaskrazyideas Posts: 119
    edited 2012-08-03 23:04
    Thanks a ton. I got that all working now thanks to the TONS of help you guys have givein.

    On to the next question.

    I have a loop going on and in the middle of the loop I need to check to see if a Pin is high. If it is then I need to jump to another loop but if it is not then it needs to just pass by the jump and continue the first loop again and again till the Pin goes High.

    So after studying I understand this.
    -In order to use an IF statment you have to use the Z and C flags. They are what the condition must look at.
    -Each command writes to the Z or C flag depending on the command and if you tell it to write to the flag or not.

    I think I understand that part,(said that before and I was very wrong, but ya gotta start somewhere)

    So the real question at present is how would I check a single bit in the ina register that coralates to the pin I want lets call it pin 20?????????
    ina reads the whole register at once right? so how do I look at pin 20 alone.

    I was thinking mabe useing the AND command somehow but it didn't work in my head.

    All I'm pretty sure about is that I need to cmp a mask and Pin 20 which if equle will WZ then if z then jump to other loop
             cmp PIN 20, Mask  WZ
    ifz     jmp other loop
    
    

    Am I barking up the right tree??

    Thanks
  • kuronekokuroneko Posts: 3,623
    edited 2012-08-03 23:08
    It's close enough. Issue here is that cmp looks at the whole register again. Your idea using and (or one of its relatives) is the right way.
    test    mask, ina wz        ' test [COLOR="silver"]is the same as[/COLOR] and nr [COLOR="silver"](we update the flags and keep mask alive)[/COLOR]
    if_nz   jmp     #away               ' [COLOR="silver"]if pin is high (!Z) leave[/COLOR]
    
    
    mask    long    |< 20
    
    Note, for this kind of test keep ina in the source slot of the insn. test ina, mask wz is not the same.
  • krazyideaskrazyideas Posts: 119
    edited 2012-08-03 23:39
    So do I understand this.

    mask is set to 20 via |< 20 which is the same thinga as %0000_0000_0001_0000_0000_0000_0000_0000 right.

    Then the test command looks at ina but because of the mask is only looking at pin 20 Right?

    So if Mask and ina == each other (both == 1) it writes the Z flag to be 1, otherwise the Z flag remains 0 and it continues on with the next command.

    If so then why did you use if_nz (if not Z) then jmp. Or is it that I would have
    if_nz jmp to one place and
    if_z jmp to the other place ?

    Thanks
  • kuronekokuroneko Posts: 3,623
    edited 2012-08-03 23:52
    test takes both operands and performs a bitwise AND operation (but doesn't actually write to the destination register). So for all bits except bit 20 the result will be 0 (A & 0 == 0). Bit 20 is the only one which can change depending on ina[20], so we get result[20] := ina[20] & 1 which is the same as result[20] := ina[20].

    Now, the wz effect for and/test is that it's set when the result is 0 and cleared when it is not (zero). Which means for a high pin 20 we get a non-zero result (!Z) and for a low pin a zero result (Z). This is not to be confused with the cmp behaviour where the zero flag is determined by the difference between both operands (A == B is Z, A != B is !Z), e.g.
    mov     tmp, ina              mov     tmp, ina
            and     tmp, mask             and     tmp, mask             mov     tmp, ina
            cmp     tmp, mask wz          cmp     tmp, #0 wz            and     tmp, mask wz          test    mask, ina wz
    if_z    jmp     #away         if_nz   jmp     #away         if_nz   jmp     #away         if_nz   jmp     #away
    
    
    mask    long    |< 20
    
  • Mark_TMark_T Posts: 1,981
    edited 2012-08-04 01:41
    Note that with TEST (or AND) you can examine more than one pin at once, but only detemine if they are all zero or not all zero. WAITPEQ and WAITPNE are more powerful in that they can test a group of pins for a specific pattern that isn't all zeroes.
    :wait         mov     t, ina
                  and     t, mask
                  cmp     t, state  wz
            if_z  jmp     #:wait
    
    is equivalent to waitpne, except its slower to respond (could miss short pulses), uses an extra register t and consumes power for that cog rather than shutting it down while waiting.
  • krazyideaskrazyideas Posts: 119
    edited 2012-08-08 09:22
    Ok next question

    So I need too pause the assembly program while I do a bunch of stuff in spin that is dependant on the information gained by the assembly code and then start the assembly code again.

    The easyest way of doing that in Spin us just calling a method. So the question is can I call a spin method from a assembly program and have it do the same thing, just jump back to what called the method and continue from there? or do I need to write the assembly program so it skip the puse loop until a flag is set thenl run an endless loop until a hub var is == to a value then jump out of the pause loop.

    Any thoughts on a simple way of doing this?

    Thanks
  • User NameUser Name Posts: 1,451
    edited 2012-08-08 19:56
    If you are not specifically trying to save power, then it is very common for a PASM COG to endlessly check a HUB location (via RDLONG) and resume operation when it finds that the value stored at that location has changed, perhaps in some predetermined fashion.
  • krazyideaskrazyideas Posts: 119
    edited 2012-08-09 09:36
    Thanks.

    I just wasn't sure if there was some super easy thing that I just didn't know about that would do what I wanted. It happens often with me so I thought I would ask this time.

    Thanks a ton
  • kwinnkwinn Posts: 8,697
    edited 2012-08-09 17:51
    If you do want to save power you can use one of the wait instructions in the PASM program loop that checks the hub location. Spin is considerably slower than PASM so the PASM program may loop hundreds or thousands of times without the wait.
Sign In or Register to comment.