Shop OBEX P1 Docs P2 Docs Learn Events
Passing multiple variables to asm — Parallax Forums

Passing multiple variables to asm

James LongJames Long Posts: 1,181
edited 2009-04-27 07:33 in Propeller 1
Ok, someone else's turn. Mike has had enough for a week or two.

Say I want to pass three variables to asm. One of those variables is a very long byte array. How is this done?

I have read the tutorials about passing one variable, but can't find anything about passing a byte array.

Also with this much data, I'm worried about getting it all mixed up.

Any help would be appreciated.

James L

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer

Lil Brother SMT Assembly Services
«13

Comments

  • Ziggy252Ziggy252 Posts: 32
    edited 2009-04-21 00:47
    Just pass the _address_ of the first variable in PAR. Then, use RDBYTE/RDWORD/RDLONG to retrieve the data at that and successive addresses.
  • James LongJames Long Posts: 1,181
    edited 2009-04-21 01:14
    Ziggy252 said...
    Just pass the _address_ of the first variable in PAR. Then, use RDBYTE/RDWORD/RDLONG to retrieve the data at that and successive addresses.

    Ziggy,

    How do I know if the Variables are in successive order?

    This is where I am confused.

    Will they come in the order of how the cognew statement is configured?

    One variable seems pretty easy, but I don't understand how a person knows how every other variable is ordered.

    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer

    Lil Brother SMT Assembly Services
  • JonnyMacJonnyMac Posts: 9,194
    edited 2009-04-21 01:23
    James,

    Longs are laid into memory in the order you define them, so if you stick with longs your PASM code can pick them up by knowing the address of the first. Let's say you have two longs and an array of longs that you want to share with PASM.

    long  param1
    long  param2
    long  param3[noparse][[/noparse]8]
    


    You PASM code might look something like this:

    DAT
    
                            org     0
    
    asm                     mov     tmp1, par                       ' start of structure
                            mov     p1Addr, tmp1                    ' save param1 address
                            add     tmp1, #4                        ' point to param2 address
                            mov     p2Addr, tmp1                    ' save it
                            add     tmp1, #4                        ' point to param3[noparse][[/noparse]0] address
                            mov     p3Addr, tmp1                    ' save it
    


    To start the Assembly section you pass pointers to the Assembly code and the param1 variable:

    cognew(@asm, @param1)
    


    One the address of the first (param1) is known the others are easily calculated.
  • James LongJames Long Posts: 1,181
    edited 2009-04-21 01:30
    JonnyMac said...
    James,

    Longs are laid into memory in the order you define them, so if you stick with longs your PASM code can pick them up by knowing the address of the first. Let's say you have two longs and an array of longs that you want to share with PASM.

    long  param1
    long  param2
    long  param3[noparse][[/noparse]8]
    


    You PASM code might look something like this:

    DAT
    
                            org     0
    
    asm                     mov     tmp1, par                       ' start of structure
                            mov     p1Addr, tmp1                    ' save param1 address
                            add     tmp1, #4                        ' point to param2 address
                            mov     p2Addr, tmp1                    ' save it
                            add     tmp1, #4                        ' point to param3[noparse][[/noparse]0] address
                            mov     p3Addr, tmp1                    ' save it
    


    To start the Assembly section you pass pointers to the Assembly code and the param1 variable:

    cognew(@asm, @param1)
    


    One the address of the first (param1) is known the others are easily calculated.

    Ah, I see. What if the variables are bytes.

    If I declare them all in order, they should be in memory in order as well I'm assuming. I can't remember off the top of my head if they are all bytes. I believe they are.

    I don't remember reading that anywhere.

    I'll give that a shot.

    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer

    Lil Brother SMT Assembly Services
  • Cluso99Cluso99 Posts: 18,069
    edited 2009-04-21 01:45
    If they are bytes, then you just add #1 instead of #4 to the pointer address and use rdbyte/wrbyte. For words, add #2 and use rdword/wrword - but needs to be on even boundary.
    long param1
    word param2[noparse][[/noparse]10]
    byte param2[noparse][[/noparse]10]

    Effectively you can access all the above using rdbyte/wrbyte by getting par and adding #1. Just remember the ordering is not as you might expect "little endian".

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Links to other interesting threads:

    · Home of the MultiBladeProps: TriBladeProp, SixBladeProp, website (Multiple propeller pcbs)
    · Single Board Computer:·3 Propeller ICs·and a·TriBladeProp board (ZiCog Z80 Emulator)
    · Prop Tools under Development or Completed (Index)
    · Emulators: Micros eg Altair, and Terminals eg VT100 (Index)
    · Search the Propeller forums (via Google)
    My cruising website is: ·www.bluemagic.biz·· MultiBladeProp is: www.bluemagic.biz/cluso.htm
  • JonnyMacJonnyMac Posts: 9,194
    edited 2009-04-21 02:24
    James,

    See the attached demo: it uses two longs and an array of bytes and demonstrates the Spin/Asm interface.
  • ericballericball Posts: 774
    edited 2009-04-21 02:42
    There is also no reason the SPIN code can't copy the paramters to labelled LONGs before invoking COGNEW. I prefer this to large parameter blocks which the cog code then has to read in. The only danger is if the start function is executed by overlapping calls.
    PUB start | i
      cogwait := 60_000 + CNT
      repeat i from 1 to 7
        COGNEW( @cogstart, @cogstart )
      COGINIT( COGID, @cogstart, @cogstart )
    
    DAT
                        ORG     0
    cogstart            COGID   cogstart
                        WAITCNT cogwait, #0
    { other PASM code }
    cogwait             LONG    0
    
    


    The PASM code figures out a CNT value which all the cogs will wait on and then stores that value into cogwait. As long as cogwait is part of the 498 LONGs which are copied to the COG, then the value is accessible to the COG code. Note: the 60_000 values is sufficient for this example but is not optimized.

    Don't forget that PAR is designed to be an address of a LONG in HUB RAM as it is only 14 bits.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Composite NTSC sprite driver: http://forums.parallax.com/showthread.php?p=800114
  • KyeKye Posts: 2,200
    edited 2009-04-21 03:16
    Hoping that bytes will be in the same place you ordered them in is an invation to a head ache. Becareful with passing the first address of some ordered variables.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Nyamekye,
  • dwelvedwelve Posts: 21
    edited 2009-04-21 04:50
    Someone correct me if I'm wrong, but If I recall correctly, LONG-length variables are placed first in memory, WORD-length variables are placed after the LONGs, and BYTE-length variables are placed last in memory...

    That is, if you have:

    LONG myLong1
    LONG myLong2
    BYTE myByte1
    WORD myWord1
    LONG myLong3
    BYTE myByte2
    WORD myWord2

    .... you will have in memory:
    myLong1, myLong2, myLong3, myWord1, myWord2, myByte1, myByte2...

    ... in that order. Kind of annoying since I don't think any of this is plainly stated in any official (or even unofficial) documentation...

    ... so you can't reference myByte1 by offsetting the base address of myLong1 by 8, because there are other LONGs and WORDs that are magically sandwiched in there with absolutely no hint of where they are... unless you keep track of it yourself and/or are careful when you declare the variables and know where they are. Because of this memory re-ordering magic, you can't have quasi data structures in memory that hold different data types in a particular order that is continuous in memory (unless you are tricky about it). So you'll just have to take note or care of your variable declarations!

    Hope this helps and keeps you away from a day's worth of headaches!
  • nohabnohab Posts: 96
    edited 2009-04-21 08:23
    Somewhere I've read the same, and it make sense because the order long-word-byte saves memory when the variables are located at correct boundaries.
  • Cluso99Cluso99 Posts: 18,069
    edited 2009-04-21 08:28
    BradC or mpark ? Do the variables long, word and byte get re-ordered?? (see post 2 above)

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Links to other interesting threads:

    · Home of the MultiBladeProps: TriBladeProp, SixBladeProp, website (Multiple propeller pcbs)
    · Single Board Computer:·3 Propeller ICs·and a·TriBladeProp board (ZiCog Z80 Emulator)
    · Prop Tools under Development or Completed (Index)
    · Emulators: Micros eg Altair, and Terminals eg VT100 (Index)
    · Search the Propeller forums (via Google)
    My cruising website is: ·www.bluemagic.biz·· MultiBladeProp is: www.bluemagic.biz/cluso.htm
  • AleAle Posts: 2,363
    edited 2009-04-21 10:51
    I just tested in BST, they get reordered. So all longs in the defined order are put first, later words and then bytes:

    VAR
    
      long a, b, c
      word d, e
      byte b1
      long l3, l4, l5
      word f
      byte b2
    
    PUB start
    
    
      l3 := a + b + c
      l4 := d + e + f + b * b2
    
    



    *** Produces :

    
    
    |===========================================================================|
    Objects : -
    test
    
    Object Address : 0010 : Object Name : test
    
    Binary Image Information :
    PBASE : 0010
    VBASE : 0030
    DBASE : 0058
    PCURR : 0018
    DCURR : 005C
    |===========================================================================|
    |===========================================================================|
    Object test
    Object Base is 0010
    |===========================================================================|
    Object Constants
    |===========================================================================|
    |===========================================================================|
    |===========================================================================|
    VBASE Global Variables
    |===========================================================================|
    VBASE : 0000 LONG Size 0004 Variable a
    VBASE : 0004 LONG Size 0004 Variable b
    VBASE : 0008 LONG Size 0004 Variable c
    VBASE : 000C LONG Size 0004 Variable l3
    VBASE : 0010 LONG Size 0004 Variable l4
    VBASE : 0014 LONG Size 0004 Variable l5
    VBASE : 0018 WORD Size 0002 Variable d
    VBASE : 001A WORD Size 0002 Variable e
    VBASE : 001C WORD Size 0002 Variable f
    VBASE : 001E BYTE Size 0001 Variable b1
    VBASE : 001F BYTE Size 0001 Variable b2
    |===========================================================================|
    |===========================================================================|
    Spin Block start with 0 Parameters and 0 Extra Stack Longs. Method 1
    PUB start
    
    Local Parameter DBASE:0000 - Result
    |===========================================================================|
    12                        l3 := a + b + c
    Addr : 0018:             40  : Variable Operation Global Offset - 0 Read
    Addr : 0019:             44  : Variable Operation Global Offset - 1 Read
    Addr : 001A:             EC  : Math Op +     
    Addr : 001B:             48  : Variable Operation Global Offset - 2 Read
    Addr : 001C:             EC  : Math Op +     
    Addr : 001D:             4D  : Variable Operation Global Offset - 3 Write
    13                        l4 := d + e + f + b * b2
    Addr : 001E:          A8 18  : Memory Op Word VBASE + READ Address = 0018
    Addr : 0020:          A8 1A  : Memory Op Word VBASE + READ Address = 001A
    Addr : 0022:             EC  : Math Op +     
    Addr : 0023:          A8 1C  : Memory Op Word VBASE + READ Address = 001C
    Addr : 0025:             EC  : Math Op +     
    Addr : 0026:             44  : Variable Operation Global Offset - 1 Read
    Addr : 0027:          88 1F  : Memory Op Byte VBASE + READ Address = 001F
    Addr : 0029:             F4  : Math Op *     
    Addr : 002A:             EC  : Math Op +     
    Addr : 002B:             51  : Variable Operation Global Offset - 4 Write
    Addr : 002C:             32  : Return        
    
    
    
  • MagIO2MagIO2 Posts: 2,243
    edited 2009-04-21 12:56
    Bad news for passing a parameter block with different types as variables. But a dat block will hopefully keep the order of longs, words and bytes. Maybe in some cases you can use a dat-block instead.
    Otherwise pointers to the different parameter types can be used.

    var
    long ppointers[noparse][[/noparse] 3 ]
    long a, b, c
    word d,e
    byte b1
    long l3, l4, l5
    word f
    byte b2
    
    pub
    ppointers[noparse][[/noparse] 0 ]:=@l3
    ppointers[noparse][[/noparse] 1 ]:=@d
    ppointers[noparse][[/noparse] 2 ]:=@b2
    cognew( blabla, @ppointers )
    

  • ericballericball Posts: 774
    edited 2009-04-21 14:59
    VAR reorders, DAT keeps order, both will pad so WORDs start on WORD boundaries, and LONGs start on LONG boundaries.

    Note: HUB addresses can only be resolved at run time. So don't try to embed "ptr WORD @data" 'cause it won't work.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Composite NTSC sprite driver: http://forums.parallax.com/showthread.php?p=800114
  • James LongJames Long Posts: 1,181
    edited 2009-04-21 16:45
    MagIO2 said...
    Bad news for passing a parameter block with different types as variables. But a dat block will hopefully keep the order of longs, words and bytes. Maybe in some cases you can use a dat-block instead.
    Otherwise pointers to the different parameter types can be used.

    :=@d
    ppointers[noparse][[/noparse] 2 ]:=@b2
    cognew( blabla, @ppointers )</FONT></FONT></FONT>
    [noparse][[/noparse]/code]</FONT></FONT></FONT>

    Ah, Now there is an answer that is useful.

    I wanted to ask about this, but didn't know how to go about asking the question.

    So I can take the known address, and make a pointer to the first location. That will suffice to what I need to do.

    I need to compare 2 variables (check equality) and when they are equal, I need to perform one of two possible instructions based on the third variable.

    With the pointers addressed as above, this procedure will be much easier.

    Thanks Mag,

    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer

    Lil Brother SMT Assembly Services
  • MagIO2MagIO2 Posts: 2,243
    edited 2009-04-21 18:42
    Hi James,

    but keep in mind, what I wrote is only necessary if you have different types of variables. If you only have one type it's enough to pass the adress of the first variable. SPIN compiler is only reordering typewise. The order of the variables within the same type is not changed.
  • James LongJames Long Posts: 1,181
    edited 2009-04-21 19:51
    Well, I have finished a partial version of what I think will work.

    I want criticism of the code. Please tell me any problems you guys see with this.

    This code is designed for a 128 byte array.

    The passing of parameters and addresses is still a uneasy task for me to perform. I'm not sure if I'll ever get to understand it.

    
    {{ Object created to compare two variables and pass back a number based on where the maching variable falls within a byte array.}}
           
    VAR
    
      byte  note1, cuelist_addr1
    
    PUB start(note,cuelist_addr)
    
      cognew(@entry, @note)                                 '** NOTE: we send the cog the address to the hub ram variables used **  
    
    PUB LEDdrivercall(note,cuelist_addr)                           ' Used by spin programs to set parameters
          
    note1         := @note                                ' Put note in HUB ram
    cuelist_addr1 := @cuelist_addr                        ' Put the cutelist pointer in HUB ram
    
    DAT
    
    entry
                          ' When the cog starts par is initialized with the address passed in the coginit command
                          ' in this case it was passed @note
    
                            org     0
                            mov     ptr,par                  ' load parameter pointer into ptr (points to global variables)
                                                             ' ptr is just a copy and keeps par unchanged for later.                        
                            rdbyte  note_val,ptr             ' load first parameter into sch_para (cog ram)
                            add     ptr,#1                   ' Increment address to next global variable, the address is in bytes so
                                                             ' it needs 1 adding to get to the next byte          
                            rdbyte  list,ptr                 ' get the first byte of the byte array (cuelist)
    
    :loop                   andn    list,sch_para           wz
                  if_z      jmp     #:output_on
                            add     count,#%0_0000_0001
                            andn    count,#%0_0111_1111     wz
                  if_z      jmp     entry           
                            add     ptr,#1                  'move to the next byte (of the array) and test it
                            rdbyte  list,ptr
                            jmp     #:loop
    
    :output_on              movd    par,count
    entry_ret
                        
    
                           ' These variables are in cog ram!                 
    ' Uninitialized data
    
    val_test             byte     0
    outputval            byte     0
    count                byte     0
    list                 byte     0
    note_val             byte     0
    ptr                  byte     0
    
                            fit
    
    



    Edit, had to add in some code on increment through the byte array.

    2nd Edit. changed "test" to "andn". I think it will work this way.


    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer

    Lil Brother SMT Assembly Services

    Post Edited (James Long) : 4/21/2009 8:27:15 PM GMT
  • James LongJames Long Posts: 1,181
    edited 2009-04-21 20:09
    well, I have already found a problem. I need to test with ANDN, and I'm not sure how to do that at this point.


    Hmmmm,

    Well back to the drawing (programming) board.


    I have changed the above listed code to andn for the testing of the variables

    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer

    Lil Brother SMT Assembly Services

    Post Edited (James Long) : 4/21/2009 8:25:44 PM GMT
  • MagIO2MagIO2 Posts: 2,243
    edited 2009-04-21 20:33
    Bad idea to pass the PASM code a adress of a parameter of a SPIN function. note is parameter of start(). What does that mean?
    start( 10, @somevar ) is used in whatever SPIN program. 10 is put on the stack, the adress of somevar is put on the adress and start is executed. Now start can access the 10 by using the parameter name note and the adress of somevar by parameter name cuelist_addr.
    You then pass the adress of note to a PASM program. But the PASM program needs some time to load while the COG running your start returns and cleans up the stack. Usually an interpreter would not remove the values on the stack by overwriting with zeros, but it's very likely that you call another function which also has parameters. So, at the time you access the note in PASM it's real content is not necessarily the 10.

    But I think you meant:
    pub start(note, cuelist_addr)
       note1:=note
       cuelist_addr1:=cuelist_addr
       cognew( @entry, @note1 )
    

    But please be aware, you pass the adress of note1 to the PASM. Incrementing this by 1 it'll point to cuelist_addr and not to the cuelist! By the way ... cuelist_addr at least should be word size, since HUB RAM adresses have 16 bit. Then we again have the problem of reorder.

    So, you could pack both values in one 32 bit variable.
    var
       long note_cuelist_addr
     
    pub start(note, cuelist_addr)
       note_cuelist_addr.byte[noparse][[/noparse]0] := note
       note_cuelist_addr.word[noparse][[/noparse]1] := cuelist_addr
       cognew( @entry, @note_cuelist_addr )
     
    dat
       org
       rdbyte note_val, par         ' first get the value of note
       add    cue_ptr, par          ' calculate the adress where to find the ADRESS of cuelist
       rdword cuelist_ptr, cue_ptr  ' now we can read the cuelist_addr stored inside of note_cuelist_addr
     
       rdbyte byte, cuelist_ptr     ' now you have the first byte of the cuelist in byte
     
    cue_ptr     long  2
    cuelist_ptr res 1
    byte        res 1
     
    
  • MagIO2MagIO2 Posts: 2,243
    edited 2009-04-21 20:37
    In PASM you only have longs. So working with bytes as you do it in your code won't work. That's why the propeller is called a 32 bit microcontroller.
  • James LongJames Long Posts: 1,181
    edited 2009-04-21 20:44
    MagIO2 said...
    In PASM you only have longs. So working with bytes as you do it in your code won't work. That's why the propeller is called a 32 bit microcontroller.

    So the variables in the PASM must be:

    "whatever" long 0


    I get it.

    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer

    Lil Brother SMT Assembly Services
  • James LongJames Long Posts: 1,181
    edited 2009-04-21 20:50
    MagIO2 said...
    Bad idea to pass the PASM code a adress of a parameter of a SPIN function. note is parameter of start(). What does that mean?
    start( 10, @somevar ) is used in whatever SPIN program. 10 is put on the stack, the adress of somevar is put on the adress and start is executed. Now start can access the 10 by using the parameter name note and the adress of somevar by parameter name cuelist_addr.
    You then pass the adress of note to a PASM program. But the PASM program needs some time to load while the COG running your start returns and cleans up the stack. Usually an interpreter would not remove the values on the stack by overwriting with zeros, but it's very likely that you call another function which also has parameters. So, at the time you access the note in PASM it's real content is not necessarily the 10.


    But I think you meant:



    pub start(note, cuelist_addr) 
    
       note1:=note 
    
       cuelist_addr1:=cuelist_addr 
    
       cognew( @entry, @note1 ) 
    
    



    But please be aware, you pass the adress of note1 to the PASM. Incrementing this by 1 it'll point to cuelist_addr and not to the cuelist! By the way ... cuelist_addr at least should be word size, since HUB RAM adresses have 16 bit. Then we again have the problem of reorder.



    So, you could pack both values in one 32 bit variable.



    var 
    
       long note_cuelist_addr 
    
      
    
    pub start(note, cuelist_addr) 
    
       note_cuelist_addr.byte[noparse][[/noparse]0] := note 
    
       note_cuelist_addr.word := cuelist_addr 
    
       cognew( @entry, @note_cuelist_addr ) 
    
      
    
    dat 
    
       org 
    
       rdbyte note_val, par         ' first get the value of note 
    
       add    cue_ptr, par          ' calculate the adress where to find the ADRESS of cuelist 
    
       rdword cuelist_ptr, cue_ptr  ' now we can read the cuelist_addr stored inside of note_cuelist_addr 
    
      
    
       rdbyte byte, cuelist_ptr     ' now you have the first byte of the cuelist in byte 
    
      
    
    cue_ptr     long  2 
    
    cuelist_ptr res 1 
    
    byte        res 1 
    
      
    
    


    Mag,

    I'm going to need to read that a few times. I'm not sure that makes sense.

    I'll post back, when I actually decide how to ask the question I have.

    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer

    Lil Brother SMT Assembly Services
  • MagIO2MagIO2 Posts: 2,243
    edited 2009-04-21 21:03
    No, it's not a must. But you need to know what you're doin, if you define it differently.
    dat
          org
     
          mov     x, val_tes            ' this will move all 4 bytes into x
          add     count, #1             ' will add ... hmmm ... i guess to list or val_test - depends on how the order is, big or little endian
                                        ' currently don't know it, would have to try it
          add     count, # $0010        ' would add 1 to count leaving the rest untouched unless you did not reach $ff (if MSB comes first 
                                        ' otherwise reordering would help -> val_test, count, outputval, list)
                                        ' but be aware, #$0020 would not work as only 9 bits are allowed as immediate
     
          shr     x, #8                 ' this way you can shift x to the LSBs of x and remove the rest (outputval and val_test)
          and     x, #$ff
     
    x                    long     0
    val_test             byte     0
    outputval            byte     0
    count                byte     0
    list                 byte     0
    
    
  • James LongJames Long Posts: 1,181
    edited 2009-04-21 21:52
    MagIO2 said...
    No, it's not a must. But you need to know what you're doin, if you define it differently.

    Mag,

    I definitely do not know what I'm doing.

    I have this hang up with passing parameters in Spin, so passing them into PASM will definitely be a problem.

    The top object collects all the data I'm using for a result. This is where I always have my pitfall.

    I am trying to learn, but the hang ups still occur when I try to pass parameters.

    For example, the last post you made would be a problem. I do not see how you would do action on the items, if the top object is constantly changing them. The timing is specific, so there would need to be a call (Pub method) to initiate the comparison.

    With a Pub having parameters, does the initial command to start that Pub need to have the same? Ah, the basis for my hang up.

    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer

    Lil Brother SMT Assembly Services
  • James LongJames Long Posts: 1,181
    edited 2009-04-21 22:11
    Mag,

    I now see your point about the pointer. Because it is a pointer (or address) I can not read what I need from it, only it's value, which I do not want. Passing the addresses from a previous Spin method is something I'll just need to work out.

    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer

    Lil Brother SMT Assembly Services
  • James LongJames Long Posts: 1,181
    edited 2009-04-22 01:43
    Ok, I have totally backed up and rewritten the code.

    I think now I understand the process.

    If someone would, please critique the latest revision.

    
    {{ Object created to compare two variables and pass back a number based on where the maching variable falls within a byte array.}}
    
    CON
    
      _clkmode        = xtal1 + pll16x
      _xinfreq        = 5_000_000
           
    VAR
    
      long  note_cuelist_addr
    
    PUB start
    
      cognew(@entry, @note_cuelist_addr)                    '** NOTE: we send the cog the address to the hub ram variables used **  
    
    PUB LEDdrivercall(note_addr,cuelist_addr)               ' Used by spin programs to set parameters
          
    byte[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse]0] := @note_addr                ' Put note in HUB ram
    long[noparse][[/noparse]note_cuelist_addr] := @cuelist_addr             ' Put the cutelist pointer in HUB ram
    
    DAT
    
    entry
                          ' When the cog starts par is initialized with the address passed in the coginit command
                          ' in this case it was passed @note_cuelist_addr
    
                            org     0
                            mov     ptr,par                  ' load parameter pointer into ptr (points to global variables)
                                                             ' ptr is just a copy and keeps par unchanged for later.                       
                            rdword  note_val,ptr             ' load first parameter into note_val (cog ram)
                            add     cue_ptr,ptr              ' calculating cue_ptr location
                            rdword  list_ptr,cue_ptr         ' Making list_ptr point to cuelist address
                            rdbyte  list,list_ptr            ' get the first byte of the byte array (cuelist)
    
    :loop                   andn    list,note_val           wz                      ' Test (using and-not) a byte of cuelist with the value of note_val
                  if_z      jmp     #:output_on                                     ' if zero go to a jump
                            add     count,#%0_0000_0001                             ' Increment count
                            andn    count,#%0_0111_1111     wz                      ' compare count with 127
                  if_z      jmp     #:output_on                                     ' if count = 127 jump
                            add     list_ptr,#1             ' Move to the next byte (of the array cuelist)
                            rdbyte  list,list_ptr           ' Read the next byte of cuelist
                            jmp     #:loop                  ' Jump back to the beginning of the loop
    
    :output_on              wrlong  par,count               ' Output the count value
    entry_ret
                        
    
                           ' These variables are in cog ram!                 
    ' Uninitialized data
    
    ptr                  long     0 
    count                long     0
    cue_ptr              long     0
    list_ptr             long     0
    list                 long     0
    note_val             long     0
    
    
                            fit
    
    



    Just as a note: the other bracket is there in the code(for note_cuelist_addr), it just doesn't show on the forum.

    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer

    Lil Brother SMT Assembly Services
  • MagIO2MagIO2 Posts: 2,243
    edited 2009-04-22 07:02
    {{ Object created to compare two variables and pass back a number based on where the maching variable falls within a byte array.}}
    
    CON
    
      _clkmode        = xtal1 + pll16x
      _xinfreq        = 5_000_000
           
    VAR
    
      long  note_cuelist_addr
    
    PUB start
      [color=red]' you did not initialize note_cuelist_addr, but the PASM immediately reads the content.[/color]
    [color=#ff0000]  ' So in your case you need to call LEDdrivercall first and then you can call start. Possible, but unusual for a driver.[/color]
    [color=#ff0000]  ' Usual is to give the start function all it needs to setup the driver. See FullDuplexSerial, TV ... whatever[/color]
      cognew(@entry, @note_cuelist_addr)                    '** NOTE: we send the cog the address to the hub ram variables used **  
    
    PUB LEDdrivercall(note_addr,cuelist_addr)               ' Used by spin programs to set parameters
          
    [color=orange]byte[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse]0] := @note_addr                ' Put note in HUB ram[/color]
    [color=orange]   ' 1. now you work with an adress here as well, so the type should be word [/color]
    [color=#ffa500]   ' 2. you call the parameter note_addr, so LEDdrivercall( @a_note, @a_cuelist) would be the way you use it[/color]
    [color=#ffa500]   ' @note_addr is wrong, because this will be a pointer to the stack adress of the parameter instead of the adress of the variable[/color]
    [color=orange]   '-> word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse]0] := note_addr
    [/color][color=red]long[noparse][[/noparse]note_cuelist_addr][/color][color=red] := @cuelist_addr             ' Put the cutelist pointer in HUB ram[/color]
    [color=red]   ' this will overwrite the note part of note_cuelist_addr as well[/color]
    [color=red]   ' -> word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse]1] := cuelist_addr
    [/color]
    DAT
    
    entry
                          ' When the cog starts par is initialized with the address passed in the coginit command
                          ' in this case it was passed @note_cuelist_addr
    
                            org     0
                            mov     ptr,par                  ' load parameter pointer into ptr (points to global variables)
                                                             ' ptr is just a copy and keeps par unchanged for later.                       
                            [color=orange]rdword  note_val,ptr             ' load first parameter into note_val (cog ram)[/color]
    [color=orange]                        ' remember, what you put into the first word of note_cuelist_addr is a pointer to the note and not the value of the note![/color]
    [color=orange]                        ' -> rdword note_ptr, ptr       [/color]
    [color=orange]                        '    rdbyte note_val, note_ptr[/color]
    [color=orange]                        ' this is needed to get the value of the variable
    [/color]                        add     cue_ptr,ptr              ' calculating cue_ptr location
                            rdword  list_ptr,cue_ptr         ' Making list_ptr point to cuelist address
    :loop                   rdbyte  list,list_ptr            ' get the first byte of the byte array (cuelist)
    
    [color=red][s]:loop[/s]                   andn    list,note_val           wz                      ' Test (using and-not) a byte of cuelist with the value of note_val[/color]
    [color=red]                        ' this is not equal to a compare instruction! It's result will be zero in many cases where the values are not equal, because[/color]
    [color=red]                        ' andn simply deletes each bit in list which is set in note_val. But if the bit already was zero you have the same result.[/color]
    [color=#ff0000]                        ' for example:[/color]
    [color=#ff0000]                        ' list     note_val  result[/color]
    [color=#ff0000]                        ' %000000, %000101   %000000[/color]
    [color=#ff0000]                        ' %000001, %000101   %000000[/color]
    [color=#ff0000]                        ' %000100, %000101   %000000[/color]
    [color=red]                        ' %000101, %000101   %000000[/color]
    [color=red]                        ' so, note_val %000101 will "match" with all these list values. First comes first serves.
    [/color]              if_z      jmp     #:output_on                                     ' if zero go to a jump
                            add     count,#%0_0000_0001                             ' Increment count
    [color=orange]                        andn    count,#%0_0111_1111     wz                      ' compare count with 127[/color]
    [color=orange]                        ' again, this is not a compare! you delete the bits in count which are 1 in your immediate. So, this will end the loop immediately[/color]
    [color=orange]                        ' for comparison we have the cmp instruction
    [/color]              if_z      jmp     #:output_on                                     ' if count = 127 jump
                            add     list_ptr,#1             ' Move to the next byte (of the array cuelist)
                            [s]rdbyte  list,list_ptr           ' Read the next byte of cuelist[/s]
                            jmp     #:loop                  ' Jump back to the beginning of the loop
    
    [color=purple]:output_on              wrlong  par,count               ' Output the count value[/color]
    [color=purple]                        ' wrong order! wrlong does work differently than all other instructions. You first give the source and then the adress![/color]
    [color=purple]                        ' -> wrlong count, par
    [/color]entry_ret
                            [color=red]' and what do you do here? That's unpredictable, as it depends on the values in the following longs. It's very unlikely that[/color]
    [color=#ff0000]                        ' you find the opcode of the right cogstop instruction here. Please remember, the COG RAM is read completely even if your[/color]
    [color=#ff0000]                        ' PASM only needs 30 longs. So, what's behind the PASM code is also loaded. So, either you should have an outer loop here or[/color]
    [color=#ff0000]                        ' you should stop the COG. Funny things could happen if you don't do that. There might be the opcode jumping back in the[/color]
    [color=#ff0000]                        ' middle of your PASM .. Or there could be an opcode stopping COG 0.[/color]
    [color=#ff0000]                        ' To end an PASM program you can do a[/color]
    [color=#ff0000]                        ' COGID  $0[/color]
    [color=#ff0000]                        ' COGSTOP $0[/color]
     
    [color=#ff0000]                        ' What you did up to now is not what you would call a driver. A driver is running in background, waiting for commands. So, you would[/color]
    [color=#ff0000]                        ' have the following code-structure[/color]
    [color=#ff0000]                        ' 1. take the parameters (note_addr and cueList_addr)[/color]
    [color=#ff0000]                        ' 2. read note_value[/color]
    [color=#ff0000]                        ' 3. if note_value = 0, go to 2.[/color]
    [color=#ff0000]                        ' 4. loop through the list until value has been found.[/color]
    [color=#ff0000]                        ' 5. store the index in HUB RAM ( don't use note_addr for that, because then the driver starts again immediately)[/color]
    [color=#ff0000]                        ' 6. set the note to 0 (SPIN then can see if the driver is done)[/color]
    [color=#ff0000]                        ' 7. jump to 2.[/color]
                            
                           ' These variables are in cog ram!                 
    [color=orange]' Uninitialized data[/color]
    [color=orange]' wrong comment, the data below is initialized with 0![/color]
    [color=orange]' res would be uninitialized data
    [/color]
    ptr                  long     0 
    count                long     0
    [color=red]cue_ptr              long     0[/color]
    [color=red]' it's important to initialize this with 2, because of the "add cue_ptr, par". 0+par = par. The resulting pointer would then also point to[/color]
    [color=red]' note_addr and not to cuelist_addr.
    [/color]list_ptr             long     0
    list                 long     0
    note_val             long     0
    
    
                            fit
    

    Have fun digesting this ;o)

    By the way ... what you do here looks like implementing the lookdownz in PASM. Do you expect it to be so much faster?

    Post Edited (MagIO2) : 4/22/2009 7:17:10 AM GMT
  • James LongJames Long Posts: 1,181
    edited 2009-04-22 07:50
    Wow,

    You are right, it will take me some time to get all that through my head.

    Your right of course. i used to being able to "call" a method when I want a result, and with a cog running all the time, the results can be rubbish.

    You are right about the purpose. I was trying to make a lookdown event in pasm. I thought it was a good first project. Not sure if it would be much faster, but it is a good learning experiment.

    You are definitely a patient person, because dealing with all of this is annoying, but I'll get it. You may have to beat it into my skull with the manual, but I will get it.

    Ok, so the first words (notice words) of the variables addressed with be the address of the material (yet again I missed that). I must use that information to move the pointer to the respective address.

    Could I do " andn list,note_val wz
    if_nz jmp #wherever

    I wanted the values to match perfectly, but may have used the wrong "z".

    Ok, I read your list, and will try again to write this. I think this is a good exercise, even if you want to beat me after looking at it.

    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer

    Lil Brother SMT Assembly Services
  • MagIO2MagIO2 Posts: 2,243
    edited 2009-04-22 08:44
    I already mentioned it in my code-comments: For checking for a perfect match you would use the cmp instruction:
    cmp list, note_val WZ
    if_z jmp #value_match

    Ok, lesson 1 learned ;o) :
    A driver does not work like a function call in SPIN. The cognew does not wait until the PASM code is done with it's job. And it even does not wait until the PASM code has been loaded into COG RAM. That's why you should never (for newbies [noparse]:o[/noparse]) pass parameter adresses to the PASM because parameters are located on the stack and that might be used otherwise meanwhile. If you need the result in your 'calling' method, you have to synchronize. For drivers you reserve some memory in HUB RAM - for example a variable, some variables, an array .. - which is then used as communication buffer. Both, the PASM and the SPIN code need to know the structure of this buffer. For example: 1 long for passing a command, 1 long for returning a value, several longs for data.
    Simple communication protocol is (as already scetched in the code comments of my previous post):
    1. PASM waits until command <>0
    2. So, as long as SPIN does not write a command into that part of the buffer, nothing happens
    3. Depending on the value in command it can do different things ( in your case you might switch between lookdown and lookdownz )
    4. When PASM is done, it copys the result to the return-part of the buffer and sets the command buffer to zero again.
    This prevents PASM code running again immediately and it can be used to synchronize the SPIN code ( repeat while command )
  • James LongJames Long Posts: 1,181
    edited 2009-04-22 19:49
    MagIO2 said...
    I already mentioned it in my code-comments: For checking for a perfect match you would use the cmp instruction:
    cmp list, note_val WZ
    if_z jmp #value_match

    Ok, lesson 1 learned ;o) :
    A driver does not work like a function call in SPIN. The cognew does not wait until the PASM code is done with it's job. And it even does not wait until the PASM code has been loaded into COG RAM. That's why you should never (for newbies [noparse]:o[/noparse]) pass parameter adresses to the PASM because parameters are located on the stack and that might be used otherwise meanwhile. If you need the result in your 'calling' method, you have to synchronize. For drivers you reserve some memory in HUB RAM - for example a variable, some variables, an array .. - which is then used as communication buffer. Both, the PASM and the SPIN code need to know the structure of this buffer. For example: 1 long for passing a command, 1 long for returning a value, several longs for data.
    Simple communication protocol is (as already scetched in the code comments of my previous post):
    1. PASM waits until command <>0
    2. So, as long as SPIN does not write a command into that part of the buffer, nothing happens
    3. Depending on the value in command it can do different things ( in your case you might switch between lookdown and lookdownz )
    4. When PASM is done, it copys the result to the return-part of the buffer and sets the command buffer to zero again.
    This prevents PASM code running again immediately and it can be used to synchronize the SPIN code ( repeat while command )

    Yet again, I haven't given up.

    I have yet again totally changed the structure. I know you love that.

    Try this and see if it is any better:

    I edited for the return value pointer, it was wrong, maybe I fixed it this time.

    VAR
    
      long  note_cuelist_addr, output_num
       
    
    PUB start(note,cuelist_addr)
    
      word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse]0] := @note                     ' Pointer for note value
      word[noparse][[/noparse]note_cuelist_addr] := @output_num               ' Pointer for value returned
      long[noparse][[/noparse]note_cuelist_addr] := @cuelist_addr             ' Put the cutelist pointer in HUB ram
      
      cognew(@entry, @note_cuelist_addr)            
    
    PUB LEDdrivercall                                         ' Used to get the value back from the operation
         
    return output_num
    
    DAT
                           org      0
    
                          ' When the cog starts par is initialized with the address passed in the coginit command
                          ' in this case it was passed @note_cuelist_addr.
    entry                   mov     ptr1,par                 ' load parameter pointer into ptr (points to global variables)
                                                             ' ptr is just a copy and keeps par unchanged for later.
                            mov     ptr2,par                 ' We need a second copy of the pointer for cuelist                      
                    
    :start                  rdword  note_ptr,ptr1            ' Read the note pointer address from pointer1
                            add     ptr1,note_ptr            ' move pointer1 to the note data
                            rdbyte  note_val,ptr1   wz       ' Read the note value
                  if_z      jmp     #:start                  ' If the note value is zero start again                                                         
                            add     ptr2,#4                  ' Move pointer2 to the address of cuelist   
                            rdlong  list_ptr,ptr2            ' Read cuelist address
                            add     ptr2,list_ptr            ' Move the pointer 2 to cuelist data
    
    :loop                   rdbyte  list,ptr2                ' get the first byte of the byte array (cuelist)
                            cmp     list,note_val   wz       ' Compare a byte of cuelist with note_val
                  if_z      jmp     #:output_on              ' if zero go to a jump
                            add     count,#%0_0000_0001      ' Increment count
                            cmp     count,#%0_0111_1111 wz   ' compare count with 127
                  if_z      jmp     #:output_on              ' if count = 127 jump
                            add     ptr2,#1                  ' Move pointer2 to the next byte (of the array cuelist)
                            jmp     #:loop                   ' Jump back to the beginning of the loop
    
    :output_on              add     ptr1,#2                  ' Move ptr1 to the return value space
                            rdword  count_ptr,ptr1           ' Read the count pointer address
                            add     ptr1,count_ptr           ' move the pointer to the counter data space
                            wrlong  count, ptr1              ' Output the count value
                            mov     note_val,#0
                            jmp     #entry                   ' not sure about this jump, should it be to :start?
                        
    
                           ' These variables are in cog ram!
    
    ptr1                    long    0
    ptr2                    long    0 
    count                   long    0
    count_ptr               word    0
    cue_ptr                 long    0
    list_ptr                long    0
    list                    long    0
    note_val                long    0
    note_ptr                word    0
    test_num                long    0
    
    
                            fit 496
    
    



    James

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer

    Lil Brother SMT Assembly Services

    Post Edited (James Long) : 4/22/2009 8:07:34 PM GMT
Sign In or Register to comment.