Shop OBEX P1 Docs P2 Docs Learn Events
[RESOLVED] SPIN stub code for learning ASM - Page 2 — Parallax Forums

[RESOLVED] SPIN stub code for learning ASM

2»

Comments

  • R PankauR Pankau Posts: 127
    edited 2007-09-02 17:56
    As promised Mightor here is that bit of code

    CON
      _CLKMODE = XTAL3
      _XINFREQ = 20_000_000
      samples = 7000
      
    var
      long ptr
      long time[noparse][[/noparse]2]
      byte check
      byte lockout
      long cog,IR_Pin, buffer[noparse][[/noparse]7000]  
     
    obj serial : "FullDuplexSerial"
    pub Main : okay
      buf_ptr := @buffer
      IR_Pin := 0
      ptr := 0
      lockout := 0
      serial.start(31,30,0,9600)
      okay := cog := cognew(@SAMPLE, @IR_PIN)     
      
      repeat                                               
          check := serial.rxcheck
          if check <> 255                                 
            ptr := 0
            repeat samples               
              serial.dec(buffer[noparse][[/noparse]ptr])
              ptr++
               
    dat
     
    SAMPLE   org       0
                  
              rdlong        t1,                     par                         
              mov           IR_pin_mask,            #1                          
              shl           IR_pin_mask,            t1                   
              andn          dira,                   IR_pin_mask              
                  
                  'add      t1,                     #4                         
                  'mov      buf_ptr,                t1                           
                  
    wait      mov           t2,                     #0                                                  
              test          IR_pin_mask,           ina     wc                    
              rcl           t2,                     #1                            
              tjnz          t2,                     #wait                        
                  'once data is detected then keep sampling until the buffer is full
              mov           t3,                     samp
              mov           t4,                     cnt
              wrlong        t4,                     buf_ptr                         
                                                                                
              add           buf_ptr,                #4                               
    look      test          IR_pin_mask,            ina    wc              
              mov           t2,                     #0
              rcl           t2,                     #1                           
              xor           t2,                     negate_mask              
              and           t2,                     #1                       
              wrlong        t2,                     buf_ptr                    
              add           buf_ptr,                #4
                  
              mov           Time1,                  cnt                           
              add           Time1,                  Delay
              waitcnt       Time1,                  Delay
                  
              djnz          t3,                     #look
              mov           t4,                     cnt
              wrlong        t4,                     buf_ptr
     
    
    samp          long      6998
    negate_mask   long      $FFFFFFFF
    buf_ptr       long      0
    Delay         long      2000
    t1            res       1
    t2            res       1
    t3            res       1
    t4            res       1
    IR_pin_mask   res       1
    Time1         RES       1
                  FIT       496
    

    So is this a professional forum?·

    This is a hobby for me, no stress, no sarcasm from work, no road-rage from riding my bicycle to work, no yelling at the kids.....Am I in the wrong place?

    I guess I should apologize for my slack-jawed-yokel comments about PAR, I was thinking CNT at the time.·

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    I'd rather have a bottle in front of me than a frontal labotomy
  • MightorMightor Posts: 338
    edited 2007-09-02 18:07
    Thanks Randy [noparse]:)[/noparse]

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-02 20:01
    There is a difference between biting sarcasm and subtle irony, I think Mightor understands quite well smile.gif
  • MightorMightor Posts: 338
    edited 2007-09-02 20:56
    deSilva said...
    There is a difference between biting sarcasm and subtle irony, I think Mightor understands quite well smile.gif
    I think it is a cultural difference, though. I'm from the Netherlands and you're from Germany (as far as I have managed to ascertain) and we tend to use sarcasm and irony (and even a touch of cynicism) in our every day language. I am not sure if that's the same in the US [noparse]:)[/noparse]

    As for irony :P I found a bug in your Machine Language guide. When specifying multiple flags (like WC and WZ) you need put a "," in between each effect. On page 18, example code ex07[noparse][[/noparse]A|B], you forgot these. I came across when I was looking for a way to divide 80_000_000 by 200. The code on page 20 for division won't work for that because the resultant quotient is longer than 16 bits.

    Gr,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-02 21:28
    Thank you Mightor - the routines are limited to 16 bit results and unsigned. I copied them from Parallax.
    To my knowledge no one has published "true" and "signed" 32-bit routines. I think this has to be done by distinguishing the different cases (<16 bit, >16 bit); signed is simple, just the rules from school.

    I have been aware of the "comma" for some time, and I asked myself: "Why does no-one complain?". The error is also in the BNF-Syntax.

    The funny thing is that I have never really used a double flag in my whole Propeller life. You need it for every => or =< check with CMPS or even CMP.
    But for some reason, the knowledge of > or < seemed always sufficient... Or are there luring bugs in my little programs?
  • MightorMightor Posts: 338
    edited 2007-09-03 13:08
    K, after much hacking and sawing, this is the code I've come up with to divide a 32bit long by another. One thing kind of puzzles me though and that's these lines:
                  cmp cpufreq, pwmfreq wc
    IF_C          add _t, #1
    
    


    I am not sure why I need it, but when I leave them out, my result is shifted wrongly. Anyway, here's the code. I'd love some feedback as this is basically my first attempt at binary maths from scratch (pretty much from scratch).
    {{
      Demonstration of binary division in ASM
    
    }}
    DAT
      propArr     Byte "-\|/"
      
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    VAR
      long quotient
      long freq
      long pwm
      
    OBJ
      Debug: "SerialMirror"
    
    PUB start
      Debug.start(31, 30, 0, 57600)
      showWaiting
      pwm := 80000 
      qAddr := @quotient
      fAddr := @freq
    
      cognew(@divide, @pwm)
      waitcnt(clkfreq + cnt)        ' wait a sec for cog to be ready 
    
      Debug.str(string("freq: "))
      Debug.dec(freq)
      Debug.CrLf
    
      Debug.str(string(10,13))
    
      Debug.str(string("quotient: "))
      Debug.CrLf
      Debug.bin(80_000_000/pwm, 32)
      Debug.CrLf
      Debug.bin(quotient, 32)
      Debug.CrLf
      Debug.dec(quotient)
      Debug.str(string(10,13))
      waitcnt(clkfreq/10 + cnt)
      
    PRI showWaiting | i  
      repeat i from 0 to 70
        Debug.tx(propArr[noparse][[/noparse]i//4])
        waitcnt(clkfreq / 15 + cnt)
        Debug.str(string(27, "[noparse][[/noparse]K"))
        Debug.str(string(27, "[noparse][[/noparse]0;0H"))    
    
    DAT
            org    
    divide        rdlong pwmfreq, par               ' PWM frequency   
                  rdlong cpufreq, #0                ' CPU frequency
                  wrlong cpufreq, fAddr
                  
    :shiftloop
                  shl pwmfreq, #1 wc, wz            ' If the bit 31 was 1, this will be in the C
    IF_NZ_AND_NC  add _t, #1 
    IF_NZ_AND_NC  jmp #:shiftloop                   ' If no carry detected or number isn't 0, loop
                                                     
                  rcr pwmfreq, #1                   ' right shift carry back onto number
                               
                  cmp cpufreq, pwmfreq wc
    IF_C          add _t, #1
                  
    :divloop      cmpsub cpufreq, pwmfreq wc        'if y =< x then subtract it, set C
                  rcl _q,#1                         'rotate c into quotient
                  shr pwmfreq, #1
                  djnz _t,#:divloop                 'loop until done
    
                  wrlong _q, qAddr                  ' write our quotient to the HUB
    
    :noploop      nop                               ' just loop forever, keeps the cog alive.
                  jmp #:noploop
    
    _q       long 0
    qAddr    long 0
    cpufreq  long 0
    pwmfreq  long 0
    _t       long 0
    fAddr    long 0
    
    


    Please be gentle...

    Gr,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
  • Graham StablerGraham Stabler Posts: 2,507
    edited 2007-09-03 14:30
    I love a bit of gentle sarcasm (and some not so gentle) but its not the easiest thing to get across in text, combined with some "unusual wording" it can just come across as rudeness. So I'd just be careful all.

    Graham
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-03 17:56
    To simplify the debugging of "arithmetic" routines it is generally helpfull to reduce the wordlength to 2. So you have just the numbers 0,1,2, and 3 to occur (unsigned).

    Now lets consider the following cases:
    A: 2/2
    B: 2/3

    Both should yield 1 eventually

    A:
    A1: Shifting the denominator to the left breaks the first loop immediately, leaving pwm = 2 and _t = 1
    A2: No _t correction
    A3: COMPSUB will leave a carry and the second loop will break, leaving the quotient as 1 -> GOOD!

    B:
    B1 Shifting the denominator to the left stops he first loop immediately, leaving pwm = 3 and _t = 1
    B2: unclear correction of _t as 2<3, leaving _t = 2
    B3: COMPSUB will NOT leave a carry in the first loop run, quotient = 0 -> BAD!
    B3.1: But you get a second chance:
    pwm /= 2
    COMPSUB will leave a carry, quotient = 1 -> GOOD!

    But this is not an EXPLANATION, just what happens...

    Now remember your school math (no sarcasm intended!)
    When you divide - say - 12,345 by 13 - how do you start?
    NOT devide 12 by 13 - poppycock!
    And if you would do that it would require AN ADITIONAL LOOP RUN!
    This is exactly the situation you identify by CMP cpu, pwm WC

    Normally you would start with 123 by 13.
    So to make the algorithm closer to school math I should have coded:
                    cmp cpufreq, pwmfreq wc
    IF_C          shr  pwmfreq, #1
    
    


    Incrementing _t however has the same effect.


    But this is not yet EXACTLY how you do it in school - you do not blow up the denominator up the highest possible value.
    Rather you shift it to a position where it is just somewhat smaller than (or equal to) the counter.

    So it would be more transparent (and even faster!) when using a different loop check in the first loop... Something like
    cmp cpufreq, pwmfreq wc
    
    

    perhaps wink.gif
    Give it a try! But mind the zeroes!

    Post Edited (deSilva) : 9/3/2007 6:58:27 PM GMT
  • MightorMightor Posts: 338
    edited 2007-09-03 19:19
    I went through the Propeller man page and in the section for the CMP command it's a bit unclear what the "WZ" flag does:
    Propeller Manual said...

    CMP (Compare Unsigned) compares the unsigned values of Value1 and Value2. The Z and C
    flags, if written, indicate the relative equal, and greater or lesser relationship between the two.

    If the WZ effect is specified, the Z flag is set (1) if Value1 equals Value2. If the WC effect is
    specified, the C flag is set (1) if Value1 is less than Value2.
    Which is the effect of WZ? Does it set Z when it is "greater or equal" or just when it is equal?

    Gr,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
  • Mike GreenMike Green Posts: 23,101
    edited 2007-09-03 19:35
    Mightor,
    Z is set when the two values are equal. CMP does a 2's complement subtraction and Z is set when the result of the subtraction is zero. C is set when there is a carry (borrow) out of the subtraction. If you look at the definition of CMP in the Propeller manual, you'll see that it's a SUB instruction with no result written (NR).
  • MightorMightor Posts: 338
    edited 2007-09-04 03:51
    Ah I get it now... If you specify WZ and WC and no flags are set after the operation it must've been bigger [noparse]:)[/noparse] Right?

    Gr,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-04 06:11
    There is whole set of mnemonics for it - have a look at the manual p.369 table 5.2
    Condition Instruction      Synonyms
    IF_E  if equal             IF_Z
    IF_NE if not equal         IF_NZ
    IF_A  if above             IF_NC_AND_NZ or IF_NZ_AND_NC
    IF_B  if below             IF_C
    IF_AE if above or equal    IF_NC
    IF_BE if below or equal    IF_C_OR_Z or  IF_Z_OR_C
    


    I am sure I mention this in the tutorial - but I mention so many things, don't I wink.gif

    Post Edited (deSilva) : 9/4/2007 7:21:50 AM GMT
  • MightorMightor Posts: 338
    edited 2007-09-04 06:49
    deSilva,

    I knew about some of those, they're great. I used them in my code before [noparse]:)[/noparse] I was just verifying whether I was right wrg to the flags being set/not set and their meaning. I hadn't noticed the "IF_E" and "IF_A" ones though, they're nice and a lot less cryptic looking than IF_Z and IF_NC_AND_NZ. Thanks for pointing them out. I'm at work now but I'll have a look at tweaking that first loop to make use of CMP in a bit [noparse]:)[/noparse]

    Gr,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
  • MightorMightor Posts: 338
    edited 2007-09-04 18:40
    deSilva,

    I've been trying to figure out how to setup that shiftloop using your CMP suggestion but I really cannot figure it out. I even took my laptop to work so I could hack it during lunch, all to no avail. I'm afraid I must throw in the towel. Could you please show me what you had in mind?

    Thanks,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-04 20:12
    Originally you are shifting to the left up to last possible position.
    The new idea is: Just shift the denominator until it is equal or just a little bit below the counter.

    When you overdo shifting to the left, your check (CMP) will not work as the important high bit is hiding in the carry! You have to take this also into account
  • MightorMightor Posts: 338
    edited 2007-09-05 11:38
    deSilva,

    I knew what I had to do, it's the execution of the plan that's not going quite as planned [noparse]:)[/noparse]
    This is what I have now:
        cmp cpufreq, pwmfreq wc
    IF_BE    add t_, #1
    IF_B    shl pwmfreq, #1 wc
    IF_B    jmp #:shiftloop
    IF_C    rcr pwmfreq, #1
    
    


    But this doesn't work because the "add" modifies C and Z, I can't switch around the add and the shl either. Ugh.

    Please give me some new hints man, I'm really stuck :P

    Gr,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
    | I'm afraid I might be phobophobic.
  • KaioKaio Posts: 253
    edited 2007-09-05 14:51
    On the Propeller the flags are only modified if you specify on a instruction to use execution effects. Your add instruction does not use such an effect. So why you are thinking the flags could be changed by add?
  • MightorMightor Posts: 338
    edited 2007-09-05 16:12
    I didn't know flags could be "inherited" from a previous instruction if the following function could potentially modify the flags too.

    Gr,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
    | I'm afraid I might be phobophobic.
  • KaioKaio Posts: 253
    edited 2007-09-05 16:53
    A flag will be changed only on an instruction if you use the WC or WZ effect unlike other microcontrollers. This is clearly described in the manual for each instruction.

    In your code snippet this occur on the

    "cmp cpufreq, pwmfreq wc"

    and

    "IF_B shl pwmfreq, #1 wc"

    instructions.

    The "IF_B" on the shl instruction depends on the result of the cmp instruction before. The C flag is only changed by the shl instruction if C flag was set before otherwise the shl instruction is treated as NOP.
  • MightorMightor Posts: 338
    edited 2007-09-05 17:26
    Kaio,

    If I managed to absorb and understand all the information that was in the Propeller Manual I probably wouldn't be on this forum asking my questions. [noparse]:)[/noparse] There's a serious amount of information in there to digest. For some reason I thought they were just cleared if the instruction was not a NOP. I thought the WZ and WC flags were there to allow them to be set if you chose to.

    In any case, I managed to figure it out. This is what I have now:
    :shiftloop
                  cmp pwmfreq, cpufreq wc, wz      ' Compare pwmfreq and cpufreq
    IF_A          jmp #:exitshiftloop
    IF_BE         add _t, #1
    IF_B          shl pwmfreq, #1 wc, wz
    IF_NZ_AND_NC  jmp #:shiftloop
    
    :exitshiftloop
    IF_A          rcr pwmfreq, #1
                  wrlong _t, tAddr                 ' For debugging purposes
    
    


    Is the IF_A jump right after the cmp something that would be considered and OK way to get out of the loop or is this a little unaesthetic in ASM? I hope I didn't cause some of the more hardcore ASM programmers to get a little nauseous reading my newbie code.

    Gr,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
    | I'm afraid I might be phobophobic.
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-05 17:33
    This is what a certain deSilva has written some monthes ago:
    said...

    ..... we have to understand how these two flags are set, reset, or left unchanged. Every experienced assembly programmer knows that this can be a nightmare! A small instruction inserted for some reason in a chain of instructions can destroy a clever and efficient algorithm build on a flag. 8080 instructions are extremely clever, in that 8-bit instructions generally influence flags and 16-bit instructions don’t – with exceptions…

    Now there is good news! You can forget all past problems, as a Propeller instruction will only influence a flag, if you tell it so! This is indicated by some “postfix” notation: you write WC (“with carry flag&#8221[noparse];)[/noparse] or WZ (“with zero flag&#8221[noparse];)[/noparse] at the end of the instruction.

    Now you only have to memorize in what situation an instruction CAN set any flag (if it is allowed to do this). Some basic rules are:

    - Moves, arithmetic, and logical instructions change Z whether the result is zero or not.
    - Arithmetic instructions change C according to an “overflow”
    - Logical instructions set C to form an even parity of all bits.
    For the rest of instructions this is more complex smile.gif
  • MightorMightor Posts: 338
    edited 2007-09-05 17:39
    My excuse is that I am neither experienced in ASM nor was I ever an 8080 ASM programmer [noparse]:)[/noparse] I think you even put that passage in your tutorial, yeah, page 15, just checked. When you pasted it like that I instantly recognised it.

    But back to my little piece of new code... Is this what you meant, deSilva?

    Gr,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
    | I'm afraid I might be phobophobic.
  • MightorMightor Posts: 338
    edited 2007-09-08 14:57
    This is my final test program for long division in ASM. Thanks to deSilva for helping me iron out bugs and everyone else who responded.

    Gr,
    Mightor

    {{
      Demonstration of binary division in ASM
    
    }}
    DAT
      propArr     Byte "-\|/"
      
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    VAR
      long quotient
      long pwm
      long t
      
    OBJ
      Debug: "SerialMirror"
    
    PUB start
      Debug.start(31, 30, 0, 57600)
      showWaiting
      pwm := 80_000
      qAddr := @quotient
      tAddr := @t
    
      cognew(@divide, @pwm)
      waitcnt(clkfreq + cnt)        ' wait a sec for cog to be ready
    
      Debug.str(string("freq: "))
      Debug.dec(clkfreq)
      Debug.CrLf
      Debug.str(string("pwm:  "))
      Debug.dec(pwm)
      Debug.CrLf
      Debug.CrLf
                                       
      Debug.str(string("quotient: "))
      Debug.CrLf
      Debug.str(string("SPIN: "))
      Debug.bin(clkfreq/pwm, 32)
      Debug.CrLf
      Debug.str(string("ASM:  "))
      Debug.bin(quotient, 32)
      Debug.CrLf
      Debug.str(string("SPIN: "))
      Debug.dec(clkfreq/pwm)
      Debug.CrLf  
      Debug.str(string("ASM:  "))
      Debug.dec(quotient)
      Debug.CrLf
      Debug.str(string("_t: "))
      Debug.dec(t)
      waitcnt(clkfreq/10 + cnt)
      
    PRI showWaiting | i  
      repeat i from 0 to 70
        Debug.tx(propArr[noparse][[/noparse]i//4])
        waitcnt(clkfreq / 15 + cnt)
        Debug.str(string(27, "[noparse][[/noparse]K"))
        Debug.str(string(27, "[noparse][[/noparse]0;0H"))    
    
    DAT
            org    
    divide        rdlong pwmfreq, par               ' PWM frequency   
                  rdlong cpufreq, #0                ' CPU frequency
    
                  cmp pwmfreq, #0 wz                ' Check for division by 0
    IF_Z          mov _q, #0                        ' Make quotient 0 in this case
    IF_Z          jmp #:divret                      ' Go to exit
    
                  cmp pwmfreq, cpufreq wc, wz       ' Check pwmfreq > cpufreq
    IF_A          mov _q, #0                        ' Make quotient 0 in this case
    IF_A          jmp #:divret                      ' Goto exit
                  
    :shiftloop                                       
                  cmp pwmfreq, cpufreq wc, wz       ' Compare pwmfreq and cpufreq
    IF_A          jmp #:exitshiftloop               ' pwmfreq is greater than cpufreq, so exit
    IF_BE         add _t, #1                        ' increment _t counter
    IF_B          shl pwmfreq, #1 wc, wz            ' shift pwmfreq to left, track C and Z
    IF_NZ_AND_NC  jmp #:shiftloop                   ' if no carry and pwmfreq != 0, keep going
                                                     
    :exitshiftloop                                   
    IF_A          rcr pwmfreq, #1                   ' If pwmfreq was shifted too much, shift carry back 
                  
    :divloop      cmpsub cpufreq, pwmfreq wc        ' if y =< x then subtract it, set C
                  rcl _q,#1                         ' rotate c into quotient
                  shr pwmfreq, #1                   ' Shift pwmfreq to right
                  djnz _t,#:divloop                 ' loop until done
    
    :divret       mov _t, #255                      ' for debugging purposes, to check if we're not 
                  wrlong _t, tAddr                  ' looping infinitely
                  wrlong _q, qAddr                  ' write our quotient to the HUB
    
    :noploop      nop                               ' just loop forever, keeps the cog alive.
                  jmp #:noploop
    
    _q       long 0
    qAddr    long 0
    cpufreq  long 0
    pwmfreq  long 0
    _t       long 0
    tAddr    long 0
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
    | I'm afraid I might be phobophobic.
Sign In or Register to comment.