Shop OBEX P1 Docs P2 Docs Learn Events
Propeller Assembly for beginners - Page 12 — Parallax Forums

Propeller Assembly for beginners

191012141529

Comments

  • jazzedjazzed Posts: 11,803
    edited 2011-08-18 17:11
    Harprit wrote: »
    Please notice that my use of a HUB memory register to exchange information between COGS in my last program posting was disapproved of with some emphasis. (Though how to do just that has just been discussed).
    No it has not "just been discussed".
    It has been discussed in over a dozen posts in this thread because the idea is a core concept.
  • __red____red__ Posts: 470
    edited 2011-08-18 18:35
    Harprit wrote: »
    Heater:
    Please notice that my use of a HUB memory register to exchange information between COGS in my last program posting was disapproved of with some emphasis.

    I didn't take any of the above posts as disapproval to use HUB memory to share information between cogs. In fact, according of one of the other threads that's pretty much the main way of doing it.

    What I understood disapproval was aimed at was the arbitrary choice of a HUB memory address when there is a mechanism to do this via PAR.

    I saw someone call PAR a register only and that over-writing it doesn't work because it's "read-only" or something.

    I thought that PAR was a compiler symbol that was evaluated at the time of assembly? I don't see any reason why anything other than the compiler would need to even know about the symbol.

    Am I missing something clever here?
  • kuronekokuroneko Posts: 3,623
    edited 2011-08-18 18:53
    __red__ wrote: »
    I thought that PAR was a compiler symbol that was evaluated at the time of assembly? I don't see any reason why anything other than the compiler would need to even know about the symbol.
    Consider the following fragment:
    PUB start(parameter)
    
      return cognew(@entry, parameter)
    
    DAT             org     0
    
    entry           rdlong  :tmp, par
                    ...
    
    :tmp            res     1
    
    Which value would you give par at assembly time?
  • __red____red__ Posts: 470
    edited 2011-08-18 19:06
    Ah yes, the value of parameter... which is only known at runtime.

    I'm guessing you'd use this kind of idiom for PASM's version of a dispatch table?

    If I'm completely 100% wrong btw, I'll continue to point out that I am still a newbie :-) I have exactly 170 lines of code (combined SPIN and ASM to my name).
  • kuronekokuroneko Posts: 3,623
    edited 2011-08-18 19:15
    It's quite simple really. par is one (the first, $1F0) of the 16 special purpose register living in each cog. The parameter passed to coginit/cognew has its bits 15..2 extracted (we only have space for 14bit) and reappears in par[15..2]. All other bits in par are cleared. Whatever you pass down to the cog is up to you. It can be an address and it can be anything else provided it fit's into 14bit (it's basically down to interpretation). For example, you could pass a pin number by reference (rdlong pin, par) or pass the pin number itself shifted by 2 and extract it inside the cog with mov pin, par followed by shr pin, #2.

    If par is used in the source slot of an instruction you'll always get the same value for the lifetime of the cog (that's the read-only bit). Using it in the destination slot will access the shadow register which you are free to modify.
  • __red____red__ Posts: 470
    edited 2011-08-18 19:47
    So, check my logic for me here - I'm making assumptions.

    So, you said that the two least significant bits are always zero'd when you write to PAR. Given the use-case that PAR is frequently used to pass HUB memory references and HUB memory is in longs I'm wanting to jump to a conclusion...

    You see where my brain is going? By zeroing the two least significant bits PASM is forcing the value of PAR to always align with addresses which are multiples of 4 - ie longs.

    Coincidence?
  • __red____red__ Posts: 470
    edited 2011-08-18 19:48
    PS: I wish there was an active irc channel for prop-heads. I'd learn so much.
  • kuronekokuroneko Posts: 3,623
    edited 2011-08-18 19:56
    __red__ wrote: »
    You see where my brain is going? By zeroing the two least significant bits PASM is forcing the value of PAR to always align with addresses which are multiples of 4 - ie longs.
    You are correct about the forced 4n alignment. This is also a source of unexplained errors, e.g. if you pass the address of a(n unaligned) byte variable (4n+3) it silently gets clamped to 4n. As for concidence, maybe, maybe not. It seems like a good compromise in that you can cover the whole 64K address range (hub RAM/ROM). And for other alignments you can always rebuild your address (+1/+2/+3) from 4n.
  • jazzedjazzed Posts: 11,803
    edited 2011-08-18 20:01
    __red__ wrote: »
    So, check my logic for me here - I'm making assumptions.
    ...
    Coincidence?
    Excellent, you got it in only a few posts. Your cargo net is pretty good.

    Any long in the 32KB address space is definitely intentional.
    Look at the PASM COGINIT instruction in the documentation for more enlightenment.
    Table 3-1: Destination Register Fields
    
    Bits     Meaning
    31:18    14-bit Long address for PAR Register
    17:4     14-bit Long address of code to load
    3        Start a New COG
    2:0      Cog ID (for reusing a specific COG)
    
    The COGNEW variant is best to use unless you have a very good reason for specifying a COG ID.
  • potatoheadpotatohead Posts: 10,261
    edited 2011-08-18 20:09
    Hey Red, me too.

    We had one for a while. The creator of it invested a ton into a nice bot too. Could pull up lots of good info with just a keyword. Gone now.

    Anyway, welcome and please just jump in! This forum almost never sleeps. Not quite IRC, but... it's active, and that's better than nothing.
  • __red____red__ Posts: 470
    edited 2011-08-18 20:26
    jazzed wrote: »
    Excellent, you got it in only a few posts. Your cargo net is pretty good.

    Thanks, I appreciate the vote of confidence. My confidence has been kinda dented as I'm stalled on my propeller project. I moved to the hardware for 12 weeks and now I'm back to the software I've forgotten almost everything I've learned.

    Re irc:
    potatohead wrote: »
    Hey Red, me too.

    We had one for a while. The creator of it invested a ton into a nice bot too. Could pull up lots of good info with just a keyword. Gone now.

    Anyway, welcome and please just jump in! This forum almost never sleeps. Not quite IRC, but... it's active, and that's better than nothing.

    I love the infobots on irc - so underrated and underused. You know, there is no reason why we couldn't resurect it :-D

    I'll even run an infobot for the service :-)

    Thanks guys,



    Red
  • HarpritHarprit Posts: 539
    edited 2011-08-19 05:28
    I am working on a detailed discussion of the use of PAR with extended code samples. I need the feedback of the experts on this one so I get it right for the book. Should be done in a day or two.

    H
  • HarpritHarprit Posts: 539
    edited 2011-08-19 08:38
    Question

    Basically, what is the right way to do this?

    When you want to pass an array of numbers with PAR, (lets say an array of 10 variables is needed)
    Do you have to set up 10 spaces in the RES area thus

    Array res 10

    or can this allocation to be the last assignment as

    Array res 1

    and therefore does it have to be the last assignment in the Cog.

    I understand that in the SPIN program we would use

    VAR
    long sharedVariable[10]

    to accommodate the data

    H
  • jazzedjazzed Posts: 11,803
    edited 2011-08-19 09:35
    Harprit wrote: »
    Basically, what is the right way to do this?
    There are different right ways.

    If you say "array res 10", it will be much more complicated to use.

    My very first example was one way that saves space ...
    obviously it would be better for you and others to present it differently.

    Below is a different example to study.

    Output of the program on the terminal looks like this:
    shared values: 00000001 00000001 00000001 00000001
    shared values: 00000002 00000002 00000002 00000002
    shared values: 00000003 00000003 00000003 00000003
    shared values: 00000004 00000004 00000004 00000004
    shared values: 00000005 00000005 00000005 00000005
    shared values: 00000006 00000006 00000006 00000006
    
    '----------------------------------------------
    ' This is a demonstration of using shared variables
    ' between SPIN and PASM.
    '
    con
        _clkmode = xtal1 + pll16x
        _clkfreq = 80_000_000
    
    obj
        fdx : "FullDuplexSerial"    ' include fdx object for terminal
    
    var
        long    command             ' this is the command variable
        long    share[4]            ' these are shared variables
    
    pub start | n
    
        fdx.start(31,30,0,115200)   ' start serial port
        cognew(@pasm, @command)     ' start pasm demo
        waitcnt(clkfreq+cnt)        ' wait for fdx and pasm start
    
        repeat
    
            command := -1           ' send non-zero as the "go" command
            repeat while command    ' wait until the command is done
    
            fdx.tx($a)              ' print a newline
            fdx.tx($d)
            fdx.str(string("shared values:"))
    
            repeat n from 0 to 3    ' print the values
                fdx.tx(" ")
                fdx.hex(share[n],8)
    
            waitcnt(clkfreq+cnt)    ' don't print too fast
    
    
    '----------------------------------------------
    ' this code increments shared array variables
    ' every time command is non-zero
    '
    dat org 0
    pasm
                mov     tmp, par        ' par is used as the command pointer
    
                add     tmp, #4         ' shared array starts 4 bytes after command address
                mov     ptr1, tmp       ' save shared[0] address
                add     tmp, #4         ' next address
                mov     ptr2, tmp       ' save shared[1] address
                add     tmp, #4         ' next address
                mov     ptr3, tmp       ' save shared[2] address
                add     tmp, #4         ' next address
                mov     ptr4, tmp       ' save shared[3] address
                mov     tmp, #0
    
    :loop
                rdlong  cmd, par        ' wait for user command
                tjz     cmd, #:loop     ' if no command, jump to #:loop
    
                rdlong  dat1, ptr1      ' read shared data
                rdlong  dat2, ptr2
                rdlong  dat3, ptr3
                rdlong  dat4, ptr4
    
                add     dat1, #1        ' increment data
                add     dat2, #1
                add     dat3, #1
                add     dat4, #1
    
                wrlong  dat1, ptr1      ' write shared data
                wrlong  dat2, ptr2
                wrlong  dat3, ptr3
                wrlong  dat4, ptr4
    
                wrlong  tmp, par        ' let user know we're done
                jmp     #:loop
    
    
    cmd         res 1                   ' the command register
    
    dat1        res 1                   ' data temporary registers
    dat2        res 1
    dat3        res 1
    dat4        res 1
    
    ptr1        res 1                   ' pointers to shared array
    ptr2        res 1
    ptr3        res 1
    ptr4        res 1
    
    tmp         res 1                   ' temporary pointer
    
                fit $1ef                ' don't let PASM grow beyond $1ef
    
    '
    ' end of file
    '----------------------------------------------
    
    
  • jazzedjazzed Posts: 11,803
    edited 2011-08-19 12:09
    Just for enlightenment and to demonstrate what I meant by "more complicated" here's a more generic "array version" you asked about .... It adds the value of the djnz "len" variable value to the data. If nothing else it shows how to "index" registers in the cog.

    I'm sure you'll find a simpler way to present this information at some point. It may be too much for a beginner, but like it or not the concept is a core idea in propeller PASM.
    '----------------------------------------------
    ' This is a demonstration of using shared variables
    ' between SPIN and PASM.
    ' This version uses self-modifying code with an array
    '
    con
        _clkmode = xtal1 + pll16x
        _clkfreq = 80_000_000
    
        ALEN     = 32
        ASNT     = ALEN-1
    
    obj
        fdx : "FullDuplexSerial"    ' include fdx object for terminal
    
    var
        long    command             ' this is the command variable
        long    share[ALEN]         ' these are shared variables
    
    pub start | n
    
        fdx.start(31,30,0,115200)   ' start serial port
        cognew(@pasm, @command)     ' start pasm demo
        waitcnt(clkfreq+cnt)        ' wait for fdx and pasm start
    
        repeat
            command := -1           ' send non-zero as the "go" command
            repeat while command    ' wait until the command is done
    
            repeat n from 0 to ASNT ' print the values (0 based end = ALEN-1)
                ifnot n & 7
                    fdx.tx($a)      ' print a newline
                    fdx.tx($d)
                    fdx.str(string("shared values:"))
                fdx.tx(" ")
                fdx.hex(share[n],8)
    
            waitcnt(clkfreq+cnt)    ' don't print too fast
    
    
    '----------------------------------------------
    ' this code changes shared array variables
    ' every time command is non-zero
    '
    dat org 0
    pasm
    long 0 [8]
                mov     tmp, par        ' par is used as the command pointer
    
                add     tmp, #4         ' shared array starts 4 bytes after command address
                mov     ptr, tmp        ' save shared[0] base address
    
    
    :loop
                rdlong  cmd, par        ' wait for user command
                tjz     cmd, #:loop     ' if no command, jump to #:loop
    
                mov     tmp, ptr        ' set base address for read from HUB
                movd    :read, #dat1    ' set start address for write to dat1 array
                mov     len,#ALEN       ' number of items to read from hub
    :read
                rdlong  0-0, tmp        ' read shared data to dat1 array
                add     tmp, #4         ' next pointer address
                add     :read, x200     ' next data address
                djnz    len, #:read     ' read the next pointer to dat1 array
    
                movd    :incr, #dat1    ' set start address for write to dat1 array
                mov     len,#ALEN       ' number of items to increment
    :incr
                add     0-0, len        ' increment data by len
                'add     0-0, #1         ' increment data by 1
                add     :incr, x200     ' next data address
                djnz    len, #:incr     ' read the next pointer to dat1 array
    
                mov     tmp, ptr        ' set base address for write to HUB
                movd    :write,#dat1    ' set start address for read from dat1 array
                mov     len,#ALEN       ' number of items to write back to hub
    :write
                wrlong  0-0, tmp        ' read shared data to dat1 array
                add     tmp, #4         ' next pointer address
                add     :write,x200     ' next data address
                djnz    len, #:write    ' read the next pointer to dat1 array
    
                wrlong  zero,par        ' let user know we're done
                jmp     #:loop
    
    x200        long    $200            ' used to increment destination field
    zero        long    0
    
    cmd         res 1                   ' the command register
    tmp         res 1                   ' temporary pointer
    
    ndx         res 1                   ' increment index to set upper bits of dat array
    len         res 1                   ' array length register
    ptr         res 1                   ' shared array base pointer
    
    dat1        res ALEN                ' data temporary registers of size ALEN
    
    
                fit $1ef                ' don't let PASM grow beyond $1ef
    
    '
    ' end of file
    '----------------------------------------------
    

    Program output:
    shared values: 00000020 0000001F 0000001E 0000001D 0000001C 0000001B 0000001A 00000019
    shared values: 00000018 00000017 00000016 00000015 00000014 00000013 00000012 00000011
    shared values: 00000010 0000000F 0000000E 0000000D 0000000C 0000000B 0000000A 00000009
    shared values: 00000008 00000007 00000006 00000005 00000004 00000003 00000002 00000001
    shared values: 00000040 0000003E 0000003C 0000003A 00000038 00000036 00000034 00000032
    shared values: 00000030 0000002E 0000002C 0000002A 00000028 00000026 00000024 00000022
    shared values: 00000020 0000001E 0000001C 0000001A 00000018 00000016 00000014 00000012
    shared values: 00000010 0000000E 0000000C 0000000A 00000008 00000006 00000004 00000002
    shared values: 00000060 0000005D 0000005A 00000057 00000054 00000051 0000004E 0000004B
    shared values: 00000048 00000045 00000042 0000003F 0000003C 00000039 00000036 00000033
    shared values: 00000030 0000002D 0000002A 00000027 00000024 00000021 0000001E 0000001B
    shared values: 00000018 00000015 00000012 0000000F 0000000C 00000009 00000006 00000003
    
  • HarpritHarprit Posts: 539
    edited 2011-08-19 12:18
    Wait till I get my progression on line and then give me a critique. I'm working on it pretty much full time so it should be done by tomorrow. Also want to see what other might suggest. Want to explain it so beginners are comfortable with this very necessary PASM/SPIN protocol.
    H
  • kuronekokuroneko Posts: 3,623
    edited 2011-08-19 18:09
    Harprit wrote: »
    When you want to pass an array of numbers with PAR, (lets say an array of 10 variables is needed)
    Do you have to set up 10 spaces in the RES area thus

    Array res 10

    or can this allocation to be the last assignment as

    Array res 1

    and therefore does it have to be the last assignment in the Cog.
    res N doesn't take up space in hub memory. So if you need N longs reserve them. Imagine you put the res 1 at the end of your PASM program and it ends up at location $1EC (4 longs left). How do you fit the other 6 longs in there? Your program will then happily scramble the first 6 SPRs (depending on access mode).
  • HarpritHarprit Posts: 539
    edited 2011-08-20 03:27
    How do i do that for a PASM cog
    H
  • kuronekokuroneko Posts: 3,623
    edited 2011-08-20 03:47
    Harprit wrote: »
    How do i do that for a PASM cog
    Can you be a bit more specific? What I was trying to say was (in case the question was addressed to me) if res 10 is what you need then use res 10. DON'T do something like res-1-at-the-end and hope it works.
  • HarpritHarprit Posts: 539
    edited 2011-08-20 04:31
    K

    My mistake
    I thought you indicated that the use of

    Array Res 10

    Was incorrect

    H
  • HarpritHarprit Posts: 539
    edited 2011-08-20 07:54
    An interesting and useful discovery.

    The propeller manuals are not available in Kindle format but you can load the pdfs onto an iPad and then save them as iBooks. They can then be searched very effectively. Very useful for a beginner like me. Particularly so because relevent information is often scattered throught the manual. The search provides phrased results making it even more powerful.

    Parallax reported that Kindle format was currently not in the pipeline.

    Harprit.
  • potatoheadpotatohead Posts: 10,261
    edited 2011-08-20 09:13
    Kindles can't open PDF files?
  • JonnyMacJonnyMac Posts: 9,159
    edited 2011-08-20 09:33
    Kindles can't open PDF files?

    Mine does. But if one wants to make a nice eBook for the Kindle, there is a free tool called Mobipocket Creator that is very easy to use. I am presently translating my acting coach's book, Acting is Living, to Kindle (and other eReaders) using this tool. For Mobipocket your material is a narrow subset of HTML with a few special codes; it's really easy if you've done any HTML coding at all.
  • HarpritHarprit Posts: 539
    edited 2011-08-20 09:56
    I do not have access to a Kindle but I do have the Kindle App on my ipad2.
    The app does not allow opening anything other than what has first been downloaded as books from the internet.
    Maybe a pdf can be seen as a book somehow. The iPad has a button to convert a pdf to an iBook.
    There may be some other tricks I am unaware of.
    I have limited computer skills, in some departments, none.
    Did not mean to mislead anyone. Apologies.
    H
  • potatoheadpotatohead Posts: 10,261
    edited 2011-08-20 10:09
    No worries. I was just wondering. I don't have a Kindle, but I think I want one. And we got a nice tip from JonnyMac out of it anyway.
  • HarpritHarprit Posts: 539
    edited 2011-08-20 11:25
    This will be added to, polished and edited before it is published. It will depend on what I learn in the interim. This is the general idea for now.

    Chapter XXX
    Using the PAR register to share variables between two or more Cogs


    Sharing information between Cogs from within the PASM environment is best done with the PAR command. The register is designed specifically for this purpose and is covered on page 283 in the Propeller Manual. This chapter discussed the use of this register in detail.

    A single variable shared

    If you want to share a variable in the spin environment, it is a simple matter of declaring the variable under the VAR section of the listing, and it will become available to all eight Cogs. This is not true for the PASM environment in that there is no way to declare a global variable directly in this environment. There are two ways to make it possible to create a variable in one Cog and read it another Cog.

    One way of doing this is to store the variable at a high memory location in the Hub from any one Cog and to read from that location from any other Cog either in Spin or in PASM. This method will NOT be demonstrated and is not recommended. There are too many problems with doing it this way. I will not even provide a sample listing of how to do this to completely discourage you from using this method. Since it is simple enough to use the PAR register to do the transfer, there is never a good reason to use this method.

    The recommended way is to use the PAR register to share one or more variables or constants between the Cogs. PAR is one of the 16 special purpose, dedicated registers in each Cog. When you start a PASM Cog, you can pass it a variable that is available in both the PASM environment of the started Cog and in the general SPIN environment if is it declared under the VAR section. If the passed variable is given the same name in more than one PASM Cog, it will be able to be shared between these PASM cogs as well. The sharing can be for one variable or an array depending on how the shared registers are described.

    First let us consider sharing just one value to start with. On startup, the PAR register contains a memory location that points to the location of the shared variable. We store information in the PAR register by putting the information in the memory location.

    Consider these few lines of PASM code and comments
    VAR
    long SharedValue                  'Shared variable is designated to be a long
    ….
    ….
    PUB methodName                    'name the method that runs the console display
    Cognew(@start,@SharedValue)       'start second Cog at SecondCog in DAT memory
    ….
    ….
    
    
    DAT
    ….org       0
    ….
           mov          mem          Par..          'move the memory location in PAR into MEM
           wrlong       Value,       mem            'Store the value in the memory location
    
    

    After the last line has been executed in the PASM Cog, the number in “value” will be available in “SharedValue” in all the SPIN Cogs. If another cogs has been started and the same @SharedValue variable passed in them, then all these Cogs could read the variable too.

    You can also write-to and read-from the PAR register directly with
            wrlong    variable     PAR     'write variable to PAR register.
    
    and
            rdlong    variable     PAR     'write variable to PAR register.
    
    With the writing and reading taking place in the same cog or separate cogs.

    Here we are using the PAR register directly, we are not going through its memory location though that can be done as was demonstrated earlier.

    So sharing just one variable is not complicated. The steps we outlines above were as follows

    1. Declare the variable global in the SPIN environment
    2. Pass the same variable to all necessary PASM methods
    3. Write and read longs to the shared variable in SPIN
    4. Write and read longs to the PAR variable in the PASM methods.

    Let us combine all the above information into a typical listing that you can refer to from time to time to refresh your memory. Consider a program that increments a variable by 1every time through the loops and makes this variable available to all Cogs whether they are written in SPIN or PASM. The initializing, incrementing and writing is done in the PASM cog and the reading and displaying is done in the SPIN Cog.
    {{ Program 016 PASM minimum PAR.spin
      This program is a minimal implementation of the use of the PAR register.
     
      There are two Cogs in this program, one in SPIN and one in PASM
      Cog 1 displays a counter on the console
      Cog 2 first sets the variable to 0
      it them finds the location of PAR and moves it to MEM
      It then stores the variable in that location. 
      The loop adds 1 to the variable and stores
      it back in the location of PAR (as was read into MEM.)
    }}
    VAR
    long sharedVariable
    
    OBJ
      fds: "FullDuplexSerial"
      
    PUB FirstCog                          'displays values on console
      fds.start(31,30,0,115200)           'start console at 115200 for debug output
      cognew(@SecondCog, @SharedVariable) 'start the second Cog in PASM
      waitcnt(clkfreq/4+cnt)              'wait 1/4 for everything to stabilize.
      repeat                              'loop
        fds.tx($1)                        'home to 0,0
        fds.dec(SharedVariable)           'display written value
        fds.tx(" ")                       'space
        waitcnt(clkfreq/60+cnt)           'flicker free wait
     
    DAT
        org    0                          'start at 0 location in the cog
    SecondCog                             'start point identification
          mov      variable,   zero       'initialize variable to 0
          mov      MEM,        PAR        'find location of PAR, put in MEM      
    re_do wrlong   variable,   MEM        'write variable to location MEM
          add      MEM     ,   #1         'add one to the MEM location
          jmp      #re_do                 'jump back, do it again
     
    zero      long      0                 'a zero to initialize variable
    variable  res       1                 'variable storage space
    MEM       res       1                 'MEM storage space
    


    Two variables

    Next we need to expand the program to transfer two variables between a Cog written in PASM and one written in SPIN. Once we master dealing with two variables, we can extend it to as many variables as we need, RAM space availability will be our only constraint.

    As a general rule it is best if we as beginners assume that all our variables are longs. This is particularly so because PASM does not allow us to designate the length of its variables (though it does allow us to write bytes and words). Longs are, by definition, 4 bytes long so they require storage locations that are 4 bytes apart. If we store our first variable starting at location MEM, the next variable will be stored in location MEM+4. Every time we need an address for a fresh variable, we have to add 4 to the last location that we stored a variable in. In this particular case the two locations that we will use will be MEM and MEM+4.

    If only a few variables are to be assigned, we can assign individual names and storage locations to them. We do not need an indexed array. In the last exercise we will implement an indexed array but for now we will simply assign two locations for the two variables that we will be using.

    The first program needs to be modified to increase the various routines to handle two variables instead of one. Lets take a look at the display routine first. We need to make the following changes:

    1. Assign space for two variables in VAR
    2. Print out two variables on the console instead of one
    VAR
    long sharedVariable[2]
    
    OBJ
      fds: "FullDuplexSerial"
      
    PUB FirstCog                          'displays values on console
      fds.start(31,30,0,115200)           'start console at 115200 for debug output
      cognew(@SecondCog, @SharedVariable) 'start the second Cog in PASM
      waitcnt(clkfreq/4+cnt)              'wait 1/4 for everything to stabilize.
      repeat                              'loop
        fds.tx($1)                        'home to 0,0
        fds.dec(SharedVariable)           'display first value
        fds.tx(" ")                       'space
        fds.dec(SharedVariable[2])        'display second value
        waitcnt(clkfreq/60+cnt)           'flicker free wait
    

    The PASM code in the second Cog requires a little more attention to handle two variables. Here instead of adding one to the variable each time through the loop we need to call the second variable into active use. We also need to specify two real values for the two variables that we will be manipulating so that we have something to look at to confirm a successful program. Let us select 1111 and 2222 as our two values and define them as longs. These variables are a part of the PASM cog and we want to be able to read them and display them in the SPIN cog. Accordingly we will name the two variables var_one and vat_two and provide each of them with a storage location in the PASM cog. If we run the program and can read them in the SPIN cog we will have succeeded.

    Here is the code for the PASM cog.
    DAT
        org    0                          'start at 0 location in the cog
    SecondCog                             'start point identification
          mov      MEM1,       PAR        'find location of PAR, put in MEM1
          mov      MEM2,       MEM1       'make MEM2=MEM1
          add      MEM2,       #4         'add 4 to MEM2 for next long
    re_do wrlong   var_one,   MEM1        'write variable to location MEM1
          wrlong   var_two,   MEM2        'write variable to location MEM2
          jmp      #re_do                 'jump back, do it again
     
    var_one   long      1111        'variable declaration
    var_two   long      2222        'variable declaration
    
    MEM1      res       1           'first address storage space
    MEM2      res       1           'second address storage space
    

    All put together the program is.
    {{ Program 017 PASM minimum PAR.spin
      This program is a minimal implementation for two variables and PAR.
     
      There are two Cogs in this program, one in SPIN and one in PASM
      Cog 1 in SPIN displays the variables on the console
      Cog 2 in PASM sets the variables to 1111 and 2222
        Finds the location of PAR and moves it to mem1
        Then mem2 is set to mem1 +4. 
        The two variables are transferred to mem1 and mem2
        and become available to the SPIN environment
        Loop.
    }}
    VAR
    long sharedVariable
    
    OBJ
      fds: "FullDuplexSerial"
      
    PUB FirstCog                          'displays values on console
      fds.start(31,30,0,115200)           'start console at 115200 for debug output
      cognew(@SecondCog, @SharedVariable) 'start the second Cog in PASM
      waitcnt(clkfreq/60+cnt)             'wait 1/4 for everything to stabilize.
      repeat                              'loop
        fds.tx($1)                        'home to 0,0
        fds.dec(SharedVariable)           'display first value
        fds.tx($d)                        'space
        fds.dec(SharedVariable[1])        'display second value
        waitcnt(clkfreq/6+cnt)            'flicker free wait
        
    DAT
        org    0                          'start at 0 location in the cog
    SecondCog                             'start point identification
          mov      mem1,       PAR        'find location of PAR, put in MEM1
          mov      mem2,       mem1       'make MEM2=MEM1
          add      mem2,       #4         'add 4 to MEME2
    re_do wrlong   var_one,   mem1        'write variable to location MEM1
          wrlong   var_two,   mem2        'write variable to location MEM2
          jmp      #re_do                 'jump back, do it again
     
    var_one   long      1111        'variable declaration
    var_two   long      2222        'variable declaration
    
    mem1      res       1           'first address storage space
    mem2      res       1           'second address storage space
    


    Indefinite number of variables

    Next we need to understand how to share an indefinite number of variables between a SPIN Cog and a PASM Cog. In order to do this we will need to define an indexing variable and decide on a way to tell the system how many variables are to be shared. An indexing scheme that allows us to access the variables serially will have to be devised also.

    In order for the program to be universal let us agree that we need to be able to share between 1 and 10 variables (but the upper limit is determined by how much memory we have left in the Cog after the program is read in).

    In the following code, the first cog written in SPIN, displays the 10 variables that are being transferred. The second cog, written in PASM, first find the location of PAR and stores it in mem. It then starts storing the 10 variables in 10 memory locations that are 4 bytes apart. The various variables are incremented and decremented as necessary.

    In all these program I tried to make the code as compact and simple as possible as my present expertise allowed.
    {{ Program 018 Array from PAR.spin
      This program is a minimal implementation for an array and PAR.
     
      There are two Cogs in this program, one in SPIN and one in PASM
      Cog 1 in SPIN displays the variables on the console
      Cog 2 in PASM sets the variables to 0 and 9
        Finds the location of PAR and moves it to mem
        Then storage array is set with offsets of +4. 
        The array variables are transferred to mem array
        and become available to the SPIN environment
        Loop.
    }}
    VAR
    long shared[12], array, n
    
    OBJ
      fds: "FullDuplexSerial"
      
    PUB FirstCog                          'displays values on console
      fds.start(31,30,0,115200)           'start console at 115200 for debug output
      cognew(@SecondCog, @Shared)         'start the second Cog in PASM
      waitcnt(clkfreq/4+cnt)              'wait 1/4 for everything to stabilize.
      array:=10
      n:=0
      repeat                              'loop
        fds.tx($1)                        'home to 0,0
          repeat                          'loop
            fds.dec(n)                    'display first value 
            fds.tx(" ")                   'space
            fds.tx(" ")                   'space   
            fds.dec(Shared[n])            'display first value 
            fds.tx(" ")                   'space
            fds.tx(" ")                   'space   
            fds.tx($d)                    'new line
            n:=n+1                        'counter variable
            if n=>array                   'decision to reset
               n:=0                       'reset n
               fds.tx($1)                 'home
               fds.tx($3)                 'clear screen
          waitcnt(clkfreq/60+cnt)         'flicker free wait
        
    DAT
                 org      0                     'start at 0 location in the cog
    SecondCog                                   'start point identification
    :reset       mov      mem,        PAR       'find location of PAR, put in mem
                 mov      cnter,      #10       'set counter for 10 variables
                 mov      var_one,    #0        'set first variable to 0
    :re_do       wrlong   var_one,    mem       'write variable to location (mem+4....)
                 add      var_one,    #5        'increment variable value by 5
                 add      mem,        #4        'increment memory location by 4 for longs
                 sub      cnter,      #1   wz   'subtract from counter and set 0 flag
          if_nz  jmp      #:re_do               'jmp and redo if 0 flag NOT set
                 jmp      #:reset               'jump back, do it again from beginning
     
    cnter    res    1           'counter for variables
    mem      res    10           'ten address storage space
    var_one  res    1           'variable content
    

    Harprit
  • HarpritHarprit Posts: 539
    edited 2011-08-20 11:34
    potatohead wrote: »
    No worries. I was just wondering. I don't have a Kindle, but I think I want one. And we got a nice tip from JonnyMac out of it anyway.

    I don't have one either but it prompted me to order a refurbished one ($99.00, no shipping)
    Harprit
  • jazzedjazzed Posts: 11,803
    edited 2011-08-20 12:06
    Harprit wrote: »
    {{ Program 018 Array from PAR.spin
      This program is a minimal implementation for an array and PAR.
    ...
    mem      res    10           'ten address storage space
    var_one  res    1           'variable content
    
    In program 18, you don't need "mem res 10" ... "mem res 1" is sufficient.
    Your code stores only one COG variable's values to a number of hub addresses.
  • kuronekokuroneko Posts: 3,623
    edited 2011-08-20 18:18
    Harprit wrote: »
    Here we are using the PAR register directly, we are not going through its memory location though that can be done as was demonstrated earlier.
    What does that even mean?

    You also use Finds the location of PAR ... a lot. To me par only ever refers to register $1F0 in cog memory. So there is really nothing to find, it's built-in. Unless your PAR means something else. In this case please clarify exactly what you mean.
    Harprit wrote: »
    {{ Program 016 PASM minimum PAR.spin
      ..
      It then stores the variable in that location. 
      [COLOR="red"]The loop adds 1 to the variable and stores[/COLOR]
    }}
          ...
        org    0                          'start at 0 location in the cog
    SecondCog                             'start point identification
          mov      variable,   zero       'initialize variable to 0
          mov      MEM,        PAR        'find location of PAR, put in MEM      
    re_do wrlong   variable,   MEM        'write variable to location MEM
    [COLOR="red"]      add      MEM     ,   #1         'add one to the MEM location
    [/COLOR]      jmp      #re_do                 'jump back, do it again
     
    zero      long      0                 'a zero to initialize variable
    variable  res       1                 'variable storage space
    MEM       res       1                 'MEM storage space
    
  • frank freedmanfrank freedman Posts: 1,983
    edited 2011-08-20 21:28
    kuroneko wrote: »
    What does that even mean?

    You also use Finds the location of PAR ... a lot. To me par only ever refers to register $1F0 in cog memory. So there is really nothing to find, it's built-in. Unless your PAR means something else. In this case please clarify exactly what you mean.

    I must be getting confused here. I have thought that the cog architecture was what we called in the bad old core days, memory to memory. daSilva calls them cells, special ones are referred to as registers, but still in all I understand them ALL to be memory.

    To b@stardize a line out of Dune, the register is the memory and the memory is the register. Or am I lost in space????

    Frank
Sign In or Register to comment.