Shop OBEX P1 Docs P2 Docs Learn Events
Assembly Code Examples for the Beginner - Page 3 — Parallax Forums

Assembly Code Examples for the Beginner

13

Comments

  • hippyhippy Posts: 1,981
    edited 2008-10-07 01:10
    Untested, 18x4 ...

          ' lhs == 18-bit value
          ' rhs == 4-bit value
          ' acc := lhs * rhs
    
          mov acc,#0
          shr rhs,#1   WC
    IF_C  add acc,lhs
          shl lhs,#1
          shr rhs,#1   WC
    IF_C  add acc,lhs
          shl lhs,#1
          shr rhs,#1   WC
    IF_C  add acc,lhs
          shl lhs,#1
          shr rhs,#1   WC
    IF_C  add acc,lhs
    
    
    
  • darkxceeddarkxceed Posts: 34
    edited 2008-10-08 16:59
    Ok now for some non proccesor calculation.

    Is it true that when I want to play an audio 16[url=mailto:16bit@22.05khz]bit@22.05khz[/url]·signal, I only have (20000000/(16*22050))=56.56clock cycles thus I only may use about 14 instructions(most instructions cost 4 cycles)?

    The thing that I want to make a synthesizer and·would love to use 16bit 44.1khz to playback my calculations.
    But that would really limit me in assembly instructions.

    Edit:
    I seem to have made a mistake about the calc.
    It would have been 20000000/22050=907cycles about 226cycles which would be enough I gess, and then shift the 16bits in the audio dac.
    maybe even at 44.1khz...

    But for now...
    I want to use one cog for wave generation and then put it trought a filter algoritm.
    I then have to see if I could sync the 2 cogs.
    first cog will generate the first 16bit of the wave, the second will have to wait till the first cog calculated the waveform.
    I thought of writing·a bit in the hub mem, "0" would be wait for waveform calc to finish which will be '1" if finished, second cog will make it '0' once it is read.
    The first cog sees a '0' and know it has to calc·the next 16bit value of the wave form.

    Is this a good approach or not?

    Post Edited (darkxceed) : 10/8/2008 5:24:40 PM GMT
  • hippyhippy Posts: 1,981
    edited 2008-10-08 17:27
    For t=1/f, 44.1kHz equates to just over 22675ns, so that's how many nanoseconds you have between each update of the output value. At 80MHz, cycle time is 12.5nS, instruction time 50nS, so 22675/50 = 453 instructions between updates.

    PS : It's much better if you start a new topic for questions not related to an existing thread.
  • darkxceeddarkxceed Posts: 34
    edited 2008-10-08 17:50
    OK you are right about topic.

    But isn't it true that a cog operates at 20Mhz and not 80Mhz?

    your calculations is almost the same as me btw 80000000/44100/4=453, so it take one cycle of the 20Mhz clock to execute 1 instruction.


    Post Edited (darkxceed) : 10/8/2008 5:57:52 PM GMT
  • BaggersBaggers Posts: 3,019
    edited 2008-10-08 18:03
    darkxceed, the cog operates at 20mips with 80Mhz ( 5Mhz clock * 16PLL ) which is 80Million / 4 clocks per instruction ( with some exceptions hub-ops etc.) = 20Mips [noparse]:)[/noparse]

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    http://www.propgfx.co.uk/forum/·home of the PropGFX Lite

    ·
  • darkxceeddarkxceed Posts: 34
    edited 2008-10-08 18:23
    I·feel like a fool, you are right, 20mips and not Mhz.

    Well this adds up all well!

    thanx
  • yllawwallyyllawwally Posts: 23
    edited 2008-10-10 20:42
    How do you access the pins in assembly.· I want to continuosly read in pin p2, until it's low. Then I want to read pins 16 to 23.
  • Mike GreenMike Green Posts: 23,101
    edited 2008-10-10 21:32
    INA is a register / memory location just like any other. You need to use INA as a source field. Typically, you'd have a bit mask in some location which corresponds to the pin(s) you want, then use a test instruction to set one of the conditional flags (zero or carry) or you'd copy INA to a location and shift and mask the bits you want there. For example:
    
                     test   bit2mask,INA   wc   ' At this point, the carry flag is set to the state of pin 2
    
    bit2mask   long   |< 2
    
                     mov  temp,INA
                     shr    temp,#16               ' Shift bit 16 to the position of bit 0
                     and   temp,#$FF              ' Mask off least significant 8 bits
    
    temp         long  0
    
  • BaggersBaggers Posts: 3,019
    edited 2008-10-10 21:32
    yllawwally, making sure you haven't got the bits set in DIRA as output, ie leave them 0's
    .
    .
    .
    waitpne PIN2,PIN2 'waits for INA anded with PIN2 to not equal PIN2 ( assuming only one bit is set in PIN2 otherwise a low on any of the input pins in PIN2 would pass this check )
    mov datain,INA ' get the data from the port.
    shr datain,#16 ' I'm assuming you want the byte, so shift the bits right 16 times
    and datain,#255
    .
    .
    .
    PIN2 long 1<<2

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    http://www.propgfx.co.uk/forum/·home of the PropGFX Lite

    ·
  • gambrinogambrino Posts: 28
    edited 2008-11-09 17:53
    Hello , i would like to ask if someone changed the code in the library Synth.Spin (which is in Spin language) , to propeller assembly language .
    Is it possible or it's very complicated ?
    Thank you in advance
  • CopperCopper Posts: 48
    edited 2013-01-20 14:24
    I was looking through:
    Assembly Code Examples for the Beginner: SPI Engine Demo
    and I have a really simple question.
    DAT           org'  
    ' SPI Engine - main loop
    '
    loop          rdlong  t1,par          wz                'wait for command
            if_z  jmp     #loop
                  movd    :arg,#arg0                        'get 5 arguments ; arg0 to arg4
                  mov     t2,t1                             '    &#9474;
                  mov     t3,#5                             '&#61626;&#9472;&#9472;&#9472;&#9496; 
    :arg          rdlong  arg0,t2
                  add     :arg,d0
                  add     t2,#4
                  djnz    t3,#:arg
                  mov     address,t1                        'preserve address location for passing
                                                            'variables back to Spin language.
                  wrlong  zero,par                          'zero command to signify command received
                  ror     t1,#16+2                          'lookup command address
                  add     t1,#jumps
                  movs    :table,t1
                  rol     t1,#2
                  shl     t1,#3
    :table        mov     t2,0
                  shr     t2,t1
                  and     t2,#$FF
                  jmp     t2                                'jump to command
    jumps         byte    0                                 '0
                  byte    SHIFTOUT_                         '1
                  byte    SHIFTIN_                          '2
                  byte    NotUsed_                          '3
    NotUsed_      jmp     #loop
    

    what is the "movd" command doing?
    I read the command explanation out of the manual, and I understand movd copies the value in the source location to the destination field (bits [17..9]) of the destination location; but I don't understand what it's doing here. Can you fill the destination field of PASM instructions with a movd operation? and if that is what it's being used for here, why wouldn't you just use arg0, why would you use movd and #arg0?

    Also, I understand when you use the # symbol in front of a value, you are indicating that rather than a memory location, you are using a 9 bit literal. But what does it mean when there is a # symbol in front of a memory address?

    I've spent a good while trying to figure this out myself, and I don't think I'm going to.
    So I really would appreciate an explanation. Thanks.
  • kuronekokuroneko Posts: 3,623
    edited 2013-01-20 20:12
    Copper wrote: »
    DAT           org'  
    ' SPI Engine - main loop
    '
    [COLOR="#FFA500"]loop[/COLOR]          rdlong  t1,par          wz                'wait for command
            if_z  jmp     #loop
                  [COLOR="blue"]movd    :arg,#arg0[/COLOR]                        'get 5 arguments ; arg0 to arg4
                  mov     t2,t1                             '    &#9474;
                  mov     t3,#5                             '&#61626;&#9472;&#9472;&#9472;&#9496; 
    :arg          rdlong  arg0,t2
                  [COLOR="blue"]add     :arg,d0[/COLOR]
                  add     t2,#4
                  djnz    t3,#:arg
                  mov     address,t1                        'preserve address location for passing
    
    I read the command explanation out of the manual, and I understand movd copies the value in the source location to the destination field (bits [17..9]) of the destination location; but I don't understand what it's doing here. Can you fill the destination field of PASM instructions with a movd operation? and if that is what it's being used for here, why wouldn't you just use arg0, why would you use movd and #arg0?
    In the example above the insn at :arg is modified from two different places and the whole parameter fetch sequence happens to be repeated (new/next command). Hope that's enough to get you down the right path here ...
    Copper wrote: »
    Also, I understand when you use the # symbol in front of a value, you are indicating that rather than a memory location, you are using a 9 bit literal. But what does it mean when there is a # symbol in front of a memory address?
    What are the values ending up in a and b? Then think about what the effect would be on an insn when used with movd in the context of parameter setup.
    DAT             org     0
    
    entry           mov     a, location             ' $000, a = ?
                    mov     b, #location            ' $001, b = ?
    
                    waitpeq $, #0                   ' $002
    
    location        long    42                      ' $003
    a               res     1                       ' $004
    b               res     1                       ' $005
    
  • CopperCopper Posts: 48
    edited 2013-01-21 15:26
    My adventure continues...

    I had a discussion with a friend that I think cleared some of this up for me.
    The short answers to my short questions:

    Q: Can you modify a later instruction using the "movd" command?
    A: Yes. It makes sense, but the only applications for the "movd" command described in the manual are both configuring special registers (either the video or counter modules), and I was having a hard time testing the theory.

    Q: What does it mean when you put a literal symbol in front of a label rather than a value?
    A: If you put a # symbol in front of a a label for either an initialized or un-initialized variable, you are telling the propeller to use the memory location that label refers to, as a value; rather than the value contained there-in. In other words, if you have a label "Number" at memory location 4, and the value in "Number" is 18, then "#Number" equals 4, rather than 18.

    If any of that is wrong please show no mercy.



    So to test my understanding I wrote a small block of assembly code, but am not getting the results I expected.
    CON
    
      _clkmode=     xtal1+pll16x
      _xinfreq=     5_000_000
      
    PUB main
    
    
        cognew(@start, 4)
    
    
    DAT
    
    
                  ORG 0
    start         mov       n1,     #0              wz 'set Z to 1
    :read         rdlong    n1,     par                'read par into n1
                  add       :read,  #4                 'add the literal decimal value 4 into :read
    
    
    n1Lights      muxz     dira,   n1                  'set bits in dira corresponding with the high bits in n1 to Z
                  muxz     outa,   n1                  'set bits in outa corresponding with the high bits in n1 to Z
    
    
    Ldelay        mov       time, cnt                   'time:=cnt
                  add       time, Ldelay                'add delay into time
                  waitcnt   time, #0                    'wait for count to equal time
    
    
    
    
    _Ldelay  long  500_000
                  
    n1      res 1
    
    
    time    res 1
    


    I was hoping to see P3 light up (using P0-P31 as a binary display for the value in n1).

    I'm using the Gear: Parallax Propeller Emulator (which can be found here) and it's built in dir display,
    and this is what I'm getting:
    dira.PNG
    (shows both dira and dirb with dira on top)
    which I'm pretty sure reads P0, P1, P2, P3, P5, P6, P9, P10, P11, P13, P20 as high, and everything else as low.

    So what am I missing here?
    Thanks for your help.
    137 x 40 - 480B
  • kuronekokuroneko Posts: 3,623
    edited 2013-01-21 16:13
    Copper wrote: »
    I was hoping to see P3 light up (using P0-P31 as a binary display for the value in n1).
    At least dira looks correct ($00102E6F). Why is that? You set par to 4. In the PASM section you do a rdlong from said address (loading a long from hub RAM address #4) which gives you - among other things - the clkmode ($6F) and the binary checksum ($2E). Where did you think you'd get the mask from which would light up P3 (%1000)?

    Edit: Just realised that you mentioned dira/dirb rather than dira/outa (still too early here).

    For non-special-register movd usage check [post=1108886]this example[/post].
  • CopperCopper Posts: 48
    edited 2013-01-23 14:13
    I'm still confused about literals.

    In the propeller manual, it clearly says you can use nine bit literal values in place of a memory location.

    Here's an example from the manual:
    add  X, #25           'Add 25 to X    
    add  X, Y             'Add Y to X 
    X  long  50 
    Y  long  10
    
    and the description:
    "the result of the first ADD instruction is 75 (i.e.: X + 25 → 50 + 25 = 75) and that value, 75, is stored back in the X register. Similarly, the result of the second ADD instruction is 85
    (i.e.: X + Y → 75 + 10 = 85) and so X is set to 85. "

    So to answer Kuroneko, I thought I was adding 4+4=8, ie 000000_00000000_00000000_00001000

    Really I'm just trying to understand what's going on in this block of code (from Beau Schwabe's SPI engine)
    loop          rdlong  t1,par          wz                'wait for command        if_z  jmp     #loop
                  movd    :arg,#arg0                        'get 5 arguments ; arg0 to arg4
                  mov     t2,t1                             '    &#9474;
                  mov     t3,#5                             '&#61626;&#9472;&#9472;&#9472;&#9496; 
    :arg          rdlong  arg0,t2
                  add     :arg,d0
                  add     t2,#4
                  djnz    t3,#:arg
                  mov     address,t1
    
  • kuronekokuroneko Posts: 3,623
    edited 2013-01-23 16:25
    Copper wrote: »
    So to answer Kuroneko, I thought I was adding 4+4=8, ie 000000_00000000_00000000_00001000
    I assume you mean this bit of code:
    start         mov       n1,     #0              wz 'set Z to 1
    :read         [COLOR="#FFA500"]rdlong[/COLOR]    n1,     par                'read par into n1
                  add       :read,  #4                 'add the literal decimal value 4 into :read
    
    What happens is that - after setting the Z flag - you read from hub RAM location #4 (par == 4) which gives you $00102E6F in n1 (basically the wrong insn here). After that you increase the content of :read by 4 (not n1 or par). Location :read contains an insn (rdlong n1, par) which is encoded as $08BC13F0. Adding 4 to it results in $08BC13F4 (rdlong n1, outa). One way of arriving at 8 would have been:
    start         mov       n1,     #0              wz 'set Z to 1
    :read         mov       n1,     par                'move par into n1
                  add       n1,     #4                 'add the literal decimal value 4 into n1
    

    Copper wrote: »
    Really I'm just trying to understand what's going on in this block of code (from Beau Schwabe's SPI engine)
    The objective here is to read 5 parameters from hub RAM. Unrolled it would look like this:
    rdlong  arg0, t2
            add     t2, #4
            rdlong  arg1, t2
            add     t2, #4
            rdlong  arg2, t2
            add     t2, #4
            rdlong  arg3, t2
            add     t2, #4
            rdlong  arg4, t2
    
    Doing it like this is OK for a small number of parameters (or [thread=135075]if you want it fast[/thread]). To keep the code size down it's sometimes done in a loop.
    {1}     loop          rdlong  t1,par wz         'wait for command                                     
    {2}             if_z  jmp     #loop                                                                   
    
    {3}                   movd    :arg,#arg0        'get 5 arguments ; arg0 to arg4                       
    {4}                   mov     t2,t1             '    &#9474;                                
    {5}                   mov     t3,#5             '&#61626;&#9472;&#9472;&#9472;&#9496;                                
    
    {6}     :arg          rdlong  arg0,t2                                                                 
    {7}                   add     :arg,d0                                                                 
    {8}                   add     t2,#4                                                                   
    {9}                   djnz    t3,#:arg                                                                
    
    1. read the command (par is the hub address of the command)
    2. if said command is zero try again (until non-zero)
    3. reset parameter fetch insn to rdlong arg0, t2
    4. take copy of command (hub address in lower bits)
    5. initialise loop counter (5 arguments)
    6. read first argument into arg0
    7. modify parameter fetch insn to point to next cog address (arg1)
    8. advance hub address
    9. read next argument (insn at :arg has now been modified/updated)
    A note re: point 7, what happens here is that the destination field (insn[17..9]) is incremented by one, IOW starting with rdlong arg0, t2 you'll effectively end up with rdlong arg0+1, t2 which is the same as rdlong arg1, t2 in our case. After 5 loop cycles said insn has been incremented by 5*d0. Because of this we need point 3 which resets the insn's destination field to point to arg0 when the next command is handled.
  • CopperCopper Posts: 48
    edited 2013-01-24 13:35
    Thank you very much for your excellent response. I'm going to have to study it as soon as I get some time.
    Glancing through it, I basically didn't understand how to do a single thing correctly there. PASM is weird.
    I need to re-examine how "values" are passed to PASM sub-routines.
    At any rate, again thanks for the response. I've got some work to do.
  • RumpleRumple Posts: 38
    edited 2013-02-09 08:07
    I thought I'd add to this thread rather than start a new one...

    I'm still grappling with some fundamental concepts of ASM. I'm trying to imagine the framework of an ASM routine that shares data with a Spin routine. Here's what I don't get:

    I have two bytes and 12 words in hub memory that need to be manipulated by a cog running ASM. How do I get the addresses of said bytes and words into the ASM routine with just one long parameter variable? Or tell Spin where they are if they have to be defined and put in the hub with ASM?

    Help me, Obi Wans...
  • JonnyMacJonnyMac Posts: 9,107
    edited 2013-02-09 08:41
    You can only pass one address via par so what most of us do is pass the starting address of a block of longs; within this block you can store the addresses (use @ to populate) of your byte and word arrays.

    Note: It is not a good idea to count on placement of variables in a list when you want multiple addresses -- better to manually stuff and pass the addresses as part of a parameters list.

    Here's a partial listing:
    var
    
      long  cog
    
      long  param1
      long  param2
    
      word  warray[12]
      byte  barray[2]
      
        
    pub start
    
      stop
    
      param1 := @warray                                             ' get hub address of arrays
      param2 := @barray
      
      cog := cognew(@entry, @param1) + 1
    
      return cog
    
    
    pub stop
    
      if (cog)
        cogstop(cog - 1)
        cog := 0
    
    
    dat
    
                            org     0
    
    entry                   mov     t1, par                         ' get address of params
                            rdlong  wpntr, t1                       ' get hub addr of w's
                            add     t1, #4                          ' next long
                            rdlong  bpntr, t1                       ' get hub addr of b's
    


    This sets the cog variables wpntr and bpntr to the addresses of the respective arrays. You can use these values (updated with an index) with wrxxxx and rdxxxx to access the arrays.

    Here's how you might create cog subroutines to read/write those arrays:
    readw                   mov     t1, wpntr                       ' point to w array
                            shl     idx, #1                         ' x2 for words
                            add     t1, idx                         ' add index
                            rdlong  wval, t1                        ' read warray[idx] into wval
    readw_ret               ret
    
    
    writew                  mov     t1, wpntr                       ' point to w array
                            shl     idx, #1                         ' x2 for words
                            add     t1, idx                         ' add index
                            wrlong  wval, t1                        ' write wval to warray[idx]
    writew_ret              ret
    
    
    readb                   mov     t1, bpntr                       ' point to b array
                            add     t1, idx                         ' add index
                            rdlong  bval, t1                        ' read barray[idx] into bval
    readb_ret               ret
                            
    
    writeb                  mov     t1, bpntr                       ' point to b array
                            add     t1, idx                         ' add index
                            wrlong  bval, t1                        ' write bval to barray[idx]
    writeb_ret              ret
    


    Remember that the cog sees the hub as a giant array of bytes. What this means, then, is that when you're reading/writing a word, you must multiply the index value by 2 (2 bytes per long word). This is added into the base address of the array which gives you the correct hub address for the word you want to access.
  • RumpleRumple Posts: 38
    edited 2013-02-09 09:12
    Aha! Thanks!
  • CopperCopper Posts: 48
    edited 2013-02-09 13:38
    So I'm trying to write a PASM Prime Number Generator to generate the 1000th prime number.

    First I wrote one in SPIN, (01_SPIN_PrimeNumberGenerator.spin)
    It takes 4 minutes and 44 seconds to run.

    Now I'm trying to replicate the process in PASM.
    This is what I have so far:
    {PASM 1000th prime number generator   ***Time to execute:???}CON
    
    
      _clkmode=     xtal1+pll16x
      _xinfreq=     5_000_000
    
    
    
    
    OBJ
    
    
      pst: "Parallax Serial Terminal"
      
    VAR
    
    
      long _Prime 
    
    
    PUB main
    
    
      pst.Start(57600)
      waitcnt(clkfreq*2+cnt)
    
    
      cognew(@start, @_Prime)
          
      repeat while _Prime==0
        pst.str(string(16, "Calculating..."))
        
      pst.str(string(13, "1000th Prime Number == "))
      pst.dec(_Prime)
        
    
    
    DAT
                  org 0
    start         add   candidate, #2                             'generate "next" candidate (all odd numbers)
                  mov   divisor,  candidate                       'copy candidate to divisor
    dd            sub   divisor,  #2                              'generate "next" divisor (all odd numbers < candidate)
                  sub   divisor,  #1      nr, wz                  'check if divisor has reached one
            if_z  jmp   #prime                                    'if divisor==1 (Z==1) then jump to #prime                
    divide        mov   checked,  check   wz                      'check if (candidate//divisor)==0                         
            if_z  jmp   #start                                    'if check==0 (Z==1) then move on to next candidate        
                  jmp   #dd                                       'if check<>0 (Z==0) then move on to next divisor
    prime         add   prime_cnt, #1                             'count candidate as prime
                  sub   prime_cnt, grand nr, wz                   'check if prime_cnt==1000
            if_z  jmp   #end                                      'if prime_cnt==1000 (Z==1) then jump to #end
                  jmp   #start                                    'if prime_cnt<>1000 (Z==0) then move on to next candidate
    end           wrlong candidate, PAR                           'write candidate (1000th prime number) to address at PAR (_Prime)
                  
    '************************************************************
    
    
    candidate      long      7
    prime_cnt     long      4
    check         long      (candidate//divisor)
    grand         long      1000
    
    
    divisor       res       1
    quotient      res       1
    checked       res       1
    

    Unfortunately, instead of the expected 7919, It returns 1999.

    My first suspicion is that I don't quite understand how to use the flags properly.

    Also, I'm not certain I can have a variable ("check") equal an operation between
    two other variables (candidate//divisor) without doing more than just putting candidate//divisor in the value field.

    As always I would appreciate any advice.
    Thanks.
  • Mark_TMark_T Posts: 1,981
    edited 2013-02-09 14:00
    JonnyMac wrote: »
    Remember that the cog sees the hub as a giant array of bytes. What this means, then, is that when you're reading/writing a word, you must multiply the index value by 2 (2 bytes per long).

    Little slip there - 2 bytes per word is what was meant! And for longs the factor is 4 (or a shift-left of 2 bits).
  • Mark_TMark_T Posts: 1,981
    edited 2013-02-09 14:03
    Copper wrote: »
    So I'm trying to write a PASM Prime Number Generator to generate the 1000th prime number.

    First I wrote one in SPIN, (01_SPIN_PrimeNumberGenerator.spin)
    It takes 4 minutes and 44 seconds to run.

    Now I'm trying to replicate the process in PASM.


    Unfortunately, instead of the expected 7919, It returns 1999.

    My first suspicion is that I don't quite understand how to use the flags properly.

    You haven't implemented the divisibility test so every odd number is believed to succeed, so you stop on the 1000'th odd number.
  • CopperCopper Posts: 48
    edited 2013-02-10 10:33
    I guess I'm confused as to how to do that.
    In my code I have one variable named check initialized to (candidate//divisor)
    and at instruction "divide" I copy the value from check into checked and "wz"
    where Z==1 would mean I had evenly divided divisor into candidate, thus candidate is not prime.
  • kuronekokuroneko Posts: 3,623
    edited 2013-02-10 15:32
    Copper wrote: »
    In my code I have one variable named check initialized to (candidate//divisor)
    This is a compile time value which is calculated as the remainder from dividing the cog address of candidate ($00D) by the cog address of divisor ($011). IOW it will stay $00D for the duration of your run and you will always get a result of 7+(1000-4)*2 = 1999.

    Division & Co have to be done the hard way in PASM if you want runtime results. Check appendix B of the manual. You should also terminate your program properly after the wrlong (with an endless loop/wait or cogstop). Otherwise the cog continues to execute what's there.
  • CopperCopper Posts: 48
    edited 2013-02-10 17:47
    Bummer. I thought the value was determined when the value was read (not that I can think of how that would happen).
    Thanks for the help.

    Update:

    Integrated the division code from appendix b of the manual (thanks Kuroneko) and it works.
    16.5ish seconds to execute; and that's with a 2 second handshake period for the serial terminal. So 14.5ish seconds to generate the prime number.
    01_PASM_PrimeNumberGenerator.spin


    Update2:

    So now I'm trying to pass the starting address of an array of longs like JonnyMac described earlier in the thread.
    I think I understand the general goal: to set a variable equal to the starting address of an array of "longs" and then in assembly, read each long into it's own variable by starting with the address in PAR then adding an Index value to that address such that you arrive at the next long (which I think is idx:=4). I have had a hell of a time wrapping my head around the idea of memory addresses, but I feel like I'm close.
    {PASM 1000th prime number generator   ***Time to execute:???}CON
    
    
      _clkmode=     xtal1+pll16x
      _xinfreq=     5_000_000
    
    
    
    
    OBJ
    
    
      pst: "Parallax Serial Terminal"
      
    VAR
    
    
      long array_start
      long _PASM_PARS[2]
                       
    PUB main 
    
    
      pst.Start(57600)
      waitcnt(clkfreq*2+cnt)
    
    
      array_start:=@_PASM_PARS                                        
      _PASM_PARS[1]:=(cognew(@Pre, @array_start)+1)
      pst.str(string(16, "Calculating..."))
          
      repeat while _PASM_PARS[0]==0
        waitcnt(clkfreq/100+cnt)
        
      pst.str(string(13, "1000th Prime Number == "))
      pst.dec(_PASM_PARS[0])
        
    
    
    DAT
                  org 0
    Pre             mov     Larray,PAR                                'get address of array_start
                    mov     array_addr,Larray                         'copy address of array_start
                    mov     idx,#1                                    'set index to 1
                    shl     idx,#2                                    'set index to 4    
                    add     array_addr,idx                            'add index to array start to get address of next location in array
                    rdlong  cog_id,array_addr                         'read from main memory 
    '*************************************************************************************************************************************                
    start           add     candidate,#2                              'generate "next" candidate (all odd numbers)
                    mov     divisor,candidate                         'copy candidate to divisor
    dd              sub     divisor,#2                                'generate "next" divisor (all odd numbers < candidate)
                    sub     divisor,#1                nr,wz           'check if divisor has reached one
            if_z    jmp     #prime                                    'if divisor==1 (Z==1) then jump to #prime                
    check           jmp     #divide                                   'jmp to #divide                   
    checked         mov     dend,rem                  nr,wz           'flag z if rem==0
            if_z    jmp     #start                                    'if check==0 (Z==1) then move on to next candidate        
                    jmp     #dd                                       'if check<>0 (Z==0) then move on to next divisor
    prime           add     prime_cnt,#1                              'count candidate as prime
                    sub     prime_cnt,grand           nr, wz          'check if prime_cnt==1000
            if_z    jmp     #end                                      'if prime_cnt==1000 (Z==1) then jump to #end
                    jmp     #start                                    'if prime_cnt<>1000 (Z==0) then move on to next candidate
    end             wrlong  candidate,Larray                          'write candidate (1000th prime number) to address at PAR (_Prime)
                    'cogstop cog_id                                   'stop cog(cog_id)
    dead            nop
                    jmp   #dead 
    '*************************************************************************************************************************************
    
    
    divide          mov     dor, divisor                              'copy divisor into dor
                    mov     dend, candidate                           'copy den into candidate
                    shl     dor,#15                                   'get dor into dor[30..15] 
                                    
                    cmpsub  dend,dor       wc                         'dend =< dor? Subtract it, quotient bit in c 
                    rcl     dend,#1                                   'rotate c into quotient, shift dividend
                    cmpsub  dend,dor       wc                         '
                    rcl     dend,#1                                   'repeat x16
                    cmpsub  dend,dor       wc                         '
                    rcl     dend,#1                                   '
                    cmpsub  dend,dor       wc                         '
                    rcl     dend,#1                                   '
                    cmpsub  dend,dor       wc                         ' 
                    rcl     dend,#1                                   '
                    cmpsub  dend,dor       wc                         '
                    rcl     dend,#1                                   '
                    cmpsub  dend,dor       wc                         ' 
                    rcl     dend,#1                                   '
                    cmpsub  dend,dor       wc                         ' 
                    rcl     dend,#1                                   '
                    cmpsub  dend,dor       wc                         ' 
                    rcl     dend,#1                                   '
                    cmpsub  dend,dor       wc                         '
                    rcl     dend,#1                                   '
                    cmpsub  dend,dor       wc                         '
                    rcl     dend,#1                                   '
                    cmpsub  dend,dor       wc                         '
                    rcl     dend,#1                                   '
                    cmpsub  dend,dor       wc                         ' 
                    rcl     dend,#1                                   '  
                    cmpsub  dend,dor       wc                         '
                    rcl     dend,#1                                   '
                    cmpsub  dend,dor       wc                         '
                    rcl     dend,#1                                   '
                    cmpsub  dend,dor       wc                         '   
                    rcl     dend,#1                                   '
                                                                      'quotient in dend[15..0], remainder in dend[31..16]  
                    mov     rem,dend                                  'copy dend to rem                
                    shr     rem,#15                                   'shift out quotient
    ret_checked     jmp     #checked                                  'return to checked 
                                                                    
                  
    '*************************************************************************************************************************************
    
    
    candidate     long      7
    prime_cnt     long      4
    grand         long      1000
    
    
    divisor       res       1
    dor           res       1
    dend          res       1
    rem           res       1
    Larray        res       1
    array_addr    res       1
    idx           res       1
    cog_id        res       1
    
    Other than adding the Pre routine, all I changed was the wrlong from "wrlong candidate,PAR" to "wrlong candidate,Larray" where "Larray" theoretically == @_PASM_PARS[0]
    The other variable in the array is the cog_id of the PASM cog so I can use a cogstop command at end; rather than an endless loop. But I'm not using it right now.
    My point being I'm probably missing something in the Pre routine.

    Thanks for your help.
  • jmpotheringjmpothering Posts: 6
    edited 2013-10-05 08:17
    in reference to Beau's post #8, why are the mux commands preferred and not the less obscure "or" command?

    for example:

    mov pin_mask, #1
    shl pin_mask, pin
    or outa, pin_mask 'preset pin high
    or dira, pin_mask 'set pin to output
  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2013-10-05 08:46
    We were all learning the language at the time and that was my personal preferred method.... now I think OR to set the pin and ANDN to clear the pin is the more accepted method, but it probably depends on the circumstance.
  • jmpotheringjmpothering Posts: 6
    edited 2013-10-05 09:48
    thank you. i just wanted to be sure i was not missing any advantages to "mux".
  • Sir GawainSir Gawain Posts: 32
    edited 2014-01-12 19:14
    It seems to me that a potential advantage to the MUX method is that it deals with the current pin/s in the mind of the programmer and wont interfere should there be some other pins that other part of the code has set purposely. Why AND or XOR the whole INA or DIRA unless you really want to? It makes no difference on these simple and short code examples, but on more sophisticated pin sets it is better to have code applying only what ought to be applied... n'est pas?
Sign In or Register to comment.