Shop OBEX P1 Docs P2 Docs Learn Events
Passing a Long in Prop Assembly — Parallax Forums

Passing a Long in Prop Assembly

ellipserellipser Posts: 43
edited 2014-07-19 14:46 in Propeller 1
I've read the pertinent manual sections repeatedly, and have done a search on the forum, but I still can't get this to work.

The intention of the code is something very simple, to take a variable from Hub RAM, do something to it, and then return it to Hub RAM. In this case, I'm just taking the baby-step of just adding 1 to a variable from Hub RAM, but all I get returned is the ADDR of the variable. I need some way to de-reference the MEM variable.

Any suggestions?

I'm using the PASD debugger, so I can see what it's doing. That part of the code I've deleted because it's just so much noise.

Basically the code should take X0, add one to it, and then put it back in the X0 location in main memory. Instead, it puts the address value of X0 and adds one to it and then puts that into main hub RAM.

It's the whole issue of pointers/dereferencing, just like in C++.
CON
   
  _clkmode = xtal1 + pll16x
  _xinfreq = 5_000_000

VAR
  long x0

OBJ
 
   
PUB TestMessages

  repeat
    x0  :=5
  
     cognew(@entry, @x0) 'Launch assy, pass Shared addr
     Repeat
      waitcnt(clkfreq + cnt)
  

DAT
              org     0
entry      
              mov Mem, PAR 'Retrieve shared memory addr and put it in Mem
              mov ValReg, #0   'Clear value in ValReg
              mov ValReg, Mem       ' Move Mem to ValReg.  This is the mistake, I want the value held in Mem, not the address of Mem   
              add ValReg, #1
              wrlong ValReg, Mem 'Move ValReg value to Shared
:endless       jmp #:endless
              
Mem          res 1
ValReg       res 1

Comments

  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-07-11 08:12
    When you move PAR into Mem, you're storing the address which had been passed to PAR.

    To read the value at the address, "5" in this case, you need to use "rdlong ValReg, Mem"

    Delete your second and third "mov" commands and use the "rdlong" command instead.

    BTW, JonnyMac has several good SpinZone articles on using PASM. The light bulb finally went on for me while working through one of his examples. There's a link to his articles in post #3 of my index (see signature).
  • ellipserellipser Posts: 43
    edited 2014-07-11 08:42
    Beautiful Duane, it worked perfectly. Thank you very much.

    Corrected code for future reference:
    VAR   long x0  
    
    OBJ       
    
    PUB TestMessages    
    
               repeat     
                  x0  :=5         
                  cognew(@entry, @x0) 'Launch assy, pass Shared addr      
                  repeat       
                  waitcnt(clkfreq + cnt)     
    
    DAT               
    
    org     0 
    
    entry     mov Mem, PAR                         'Retrieve shared memory addr and put it in Mem
              rdlong ValReg, Mem               
              add ValReg, #1               
              wrlong ValReg, Mem                  'Move ValReg value to Shared 
    
    :endless       jmp #:endless                
    
    Mem          res 1 
    ValReg       res 1
    

    Maybe it'd be nice to see something a bit more elaborate in the Prop manua under PAR, distinguishing between pointers and contents for this operation. All I saw was this, which leaves out that important line:
    VAR
    long Shared 'Shared variable (Spin & Assy)
    
    
    PUB Main | Temp
                     cognew(@Process, @Shared) 'Launch assy, pass Shared addr
                     repeat
                     <do something with Shared vars>
    DAT
                      org 0
    Process     mov Mem, PAR 'Retrieve shared memory addr :loop 
                      <do something>
                      wrlong ValReg, Mem 'Move ValReg value to Shared
                      jmp #:loop
    
    
    Mem res 1
    ValReg res 1
    
  • JonnyMacJonnyMac Posts: 9,105
    edited 2014-07-11 09:58
    Here's a bit of code that I uses as a template for simple pasm cogs that manipulate values. Note that it allows you to send a (non-zero) command value that dictates the manipulation

    I tag this onto a spin program for development, then trim and move to its own file if what I do is useful as a stand-alone object.
    var
    
      long  cog
    
      long  cmd
      long  value
      
    
    pri start 
    
      stop                                                          ' stop if running
    
      cog := cognew(@entry, @cmd) + 1                               ' lauch the cog
    
      return cog                                                    ' return load status
        
    
    pri stop 
    
      if (cog)                                                      ' if running
        cogstop(cog - 1)                                            '  stop
        cog := 0                                                    '  mark stopped
    
      longfill(@cmd, 0, 2)
    
    
    pri run_command(c, v, wait)
    
      repeat while (cmd)                                           ' let previous command finish
    
      value := v                                                    ' setup value (do this first!)
      cmd   := c                                                    ' enable pasm cog
    
      if (wait)
        repeat while (cmd)
    
      return value
      
    
    dat { pasm code }
    
                            org     0
    
    entry                   mov     r1, #0
                            wrlong  r1, par                         ' clear cmd
    
    get_cmd                 rdlong  r1, par                 wz      ' look for command
            if_z            jmp     #get_cmd                        ' if 0, try again
    
                            mov     r2, par                         ' r2 = hub address of cmd
                            add     r2, #4                          ' r2 = hub address of value
                            rdlong  r2, r2                          ' r2 = value
    
                            cmp     r1, #1                  wz      ' cmd == 1?
            if_e            jmp     #cmd1
    
                            cmp     r1, #2                  wz
            if_e            jmp     #cmd2
    
                            cmp     r1, #3                  wz
            if_e            jmp     #cmd3
    
    bad_cmd                 jmp     #entry                  
    
    
    cmd1                    ' do something with value in r2
                            shl     r2, #1                          ' multiply by 2
                            jmp     #cmd_exit
    
                            
    cmd2                    ' do something with value in r2
                            sar     r2, #1                          ' signed divide by 2
                            jmp     #cmd_exit
    
                            
    cmd3                    ' do something with value in r2
                            adds    r2, #3                          ' add 3
                            jmp     #cmd_exit
                            
    
    cmd_exit                mov     r1, par                         ' point to cmd
                            add     r1, #4                          ' advance pointer to value
                            wrlong  r2, r1                          ' write r2 to value
                            jmp     #entry
    
    ' --------------------------------------------------------------------------------------------------
    
    r1                      res     1
    r2                      res     1
    
                            fit     496
    
  • ellipserellipser Posts: 43
    edited 2014-07-11 14:56
    Thanks Johnny Mac.

    I'm going to try that out.

    Link: http://classic.parallax.com/downloads/propeller-spin-zone-articles-and-code

    BTW, it seems that the PASM takes a #4 jump in memory to just go one more memory location, huh? Intuition would tell me it would be just as simple as adding 1, but apparently you have to add 4 to move one.
  • HarpritHarprit Posts: 539
    edited 2014-07-13 06:17
    Each long takes up 4 bytes
    H
  • JonnyMacJonnyMac Posts: 9,105
    edited 2014-07-13 07:59
    PASM sees the hub as an array of bytes, so moving from one long to the next requires the addition of 4.

    Note that PASM sees the cog as an array of longs, so you would use 1 when advancing an address within the cog. Here's an example from an LED driver I wrote.
    dat { single-shot ws2812 driver }
    
                            org     0
    
    ws2812ss                rdlong  r1, par                         ' hub address of parameters -> r1
                            movd    :read, #resettix                ' location of cog parameters -> :read(dest)
                            mov     r2, #6                          ' get 6 parameters
    :read                   rdlong  0-0, r1                         ' copy parameter from hub to cog
                            add     r1, #4                          ' next hub element
                            add     :read, INC_DEST                 ' next cog element                         
                            djnz    r2, #:read                      ' done?
    


    The value in INC_DEST is 1 shifted left by nine bits to put it into the destination field of the instruction (INC_DEST is for increment destination).
  • ellipserellipser Posts: 43
    edited 2014-07-18 12:12
    You mean I can have the hub look into the cog ram? Maybe that's what I need to do, and use a bit to do the job of a traffic cop, like a semaphore or flag.

    What I'm looking for is a trick to basically get the timing right. I'm having the assembly program do the Bresenham algorithm and then send the results to the hub, but only at the right time, after each calculation. The problem is that the timing isn't right. I want to do it sort of like a DataReady bit in a serial protocol, where the cog tells the the hub another piece of data is ready, and to output the data to Parallax Serial Terminal. The Brensenham is lightning fast, so I wanted to the cog to say, "Data is ready" and the hub to say "I'm done outputting the data, it's OK to proceed with another calculation." In this sample, I eliminated all the irrelevant stuff. Instead of a Bresenham, I'm just adding 5 to a counter and uploading it to the hub.

    I'd like to use assembly in the Hub, but I know that's not permitted.
    CON
       
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    VAR
      long Cog, TestVar       
      long x0
      long TxControl
      long x_value_from_cog
    OBJ
       
     pst: "Parallax Serial Terminal"
    ' dbg   :       "PASDebug"                '<---- Add for Debugger 
    
       
    PUB TestMessages
    
      ''Send test messages to Parallax Serial Terminal.
     
    pst.Start(57_600)
    
    
    x0  :=5
    TxControl:=0    'When TxControl is 0, sending data to PST is disabled
    
    cognew(@entry, @x0) 'Launch assy, pass Shared addr  
    
        repeat                        'Blocking function, waiting for TxControl to
           if TxControl==0            'equal 1.  When it is 1, do the output to 
              next                    'the terminal
           
           Pst.NewLine
           pst.Str(String(pst#NL, "Inside SpinLoop"))
           pst.Dec(x_value_from_cog)
           TxControl :=0       'Tell the cog it's done with output to PST, and proceed
    
    DAT
                  org     0
    entry
          
                  mov X0Pntr, PAR        'Retrieve shared memory addr and put it in X0Pntr
                  rdlong X0cog, X0Pntr   'Retrieve X0
    
    loop          add X0cog, #10          'just something simple like adding 10 to a number
                  cmp X0cog, #120 wc   'the real code would have the Bresenham here.
         if_nc    jmp done
    
                  mov X0Pntr, PAR           'Get to memory location of x_value_tx
                  add X0Pntr, #8
                  mov Output_X, X0cog       'and move Output_X into it
                  wrlong  Output_X , X0Pntr
              
    Update_Data_Flag
    
                   mov X0Pntr, PAR           'Get to memory location of TxControl
                   add X0Pntr, #4            'and set it to 1, so that Spin section knows 
                   mov temp, #1              'that data is ready 
                   wrlong temp, X0Pntr         
    
    Wait_Until_Main_Done
    
                   rdlong temp, X0Pntr          
                   
                                            'delay so that MainCog has time to send data to PST
                  rdlong Delay, #0           'Get clock frequency
                  shr Delay, #2              'Divide by 4
                  mov Time, cnt              'Get current time
                  add Time, Delay            'Adjust by 1/4 second
                  waitcnt Time, Delay        'Wait for 1/4 second
    
                  cmp temp, #1  wz
        if_nz     jmp Wait_Until_Main_Done
    
    
                   jmp  loop 
    done           jmp done
                  
    X0Pntr         res 1      'X0Pntr points to MainCogRam memory
    X0cog          res 1
    temp           res 1
    Delay          res 1
    Time           res 1
    Output_X        res 1
    
  • AribaAriba Posts: 2,690
    edited 2014-07-18 13:04
    All your jumps in PASM miss the # before the label. As you have it now it is a indirect jump. To jump to a label you need to write:
    label
               ...
               jmp #label
    
    Then there is something wrong with the wait for the Flag from Spin: You set the TxControl to 1 in PASM and wait then until it is 1. So you don't wait - you need to wait until Spin sets it to zero. Then you don't need the 250ms delay anymore.

    Andy
  • ellipserellipser Posts: 43
    edited 2014-07-18 15:01
    Ariba wrote: »
    All your jumps in PASM miss the # before the label. As you have it now it is a indirect jump. To jump to a label you need to write:
    label
               ...
               jmp #label
    
    Then there is something wrong with the wait for the Flag from Spin: You set the TxControl to 1 in PASM and wait then until it is 1. So you don't wait - you need to wait until Spin sets it to zero. Then you don't need the 250ms delay anymore.

    Andy

    OK, I'll try that tonight. I had a weird error where the compiler said basically that the label doesn't exist, but it did. I took out the : or the # (can't remember) and then it compiled OK.

    I was trying various options with the delay, etc. Assembly is very, very fast, so I put that delay in. Thanks, btw.
  • ellipserellipser Posts: 43
    edited 2014-07-19 11:26
    Thanks, I made the modifications to the code, and it does what I want at this point. I've included the code below just in case someone searches the forum at later date in need of functionality.
      ' Send test messages to Parallax Serial Terminal.
      ' to show how a cog can get data from the MAIN hub,
      ' do something to it using lightning-fast assembly,
      ' and then send it back up to the MAIN hub to be used as needed.
    
    
    CON
       
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    VAR
      long Cog, TestVar       'Don't change the order of the variables.  Pointers in use!
      long x0
      long TxControl
      long x_value_from_cog
    OBJ
       
     pst: "Parallax Serial Terminal"
    
       
    PUB TestMessages
                                                                
    pst.Start(57_600)
    
    
    x0  :=5
    TxControl:=0    'When TxControl is 0, sending data to PST is disabled
    
    cognew(@entry, @x0) 'Launch assy, pass Shared addr  
    
        repeat                        'Blocking function, waiting for TxControl to
           if TxControl==0            'equal 1.  When it is 1, do the output to 
              next                    'the terminal
           
           Pst.NewLine
           pst.Str(String(pst#NL, "Inside SpinLoop"))
           pst.Dec(x_value_from_cog)
           TxControl :=0            'set it back equal to 0 to tell the cog assembly OK to cont.'
    
    DAT
                  org     0
    entry
          
                  mov X0Pntr, PAR        'Retrieve shared memory addr and put it in X0Pntr
                  rdlong X0cog, X0Pntr   'Retrieve X0 using the pointer
    
    loop          add X0cog, #10          'Do something pretty useless, just to do something in 
                  cmp X0cog, #120 wc      'CogRAM
           if_nc  jmp #done
    
                  mov X0Pntr, PAR           'Get to memory location of x_value_tx
                  add X0Pntr, #8
                  mov Output_X, X0cog       'and move Output_X into it
                  wrlong  Output_X , X0Pntr
              
    Update_Data_Flag
    
                   mov X0Pntr, PAR           'Get to memory location of TxControl
                   add X0Pntr, #4            'and set it to 1, so that Spin section knows 
                   mov temp, #1              'that data is ready 
                   wrlong temp, X0Pntr         
    
    Wait_Until_Main_Done
    
                   rdlong temp, X0Pntr
    
                   cmp temp, #1  wz
        if_z       jmp #Wait_Until_Main_Done
    
    
                   jmp  #loop 
    done           jmp  #done
                  
    X0Pntr         res 1      'X0Pntr points to MainCogRam memory
    X0cog          res 1
    temp           res 1
    Output_X        res 1
    

    BTW, what does "0-0" do in Johnny Mac's code? I'm not sure if that's a number, or a calculation, or maybe just trying to say, "put your variable here".
    rdlong  0-0, r1                         ' copy parameter from hub to cog
    
  • kwinnkwinn Posts: 8,697
    edited 2014-07-19 14:46
    ellipser wrote: »
    Thanks, ...........
    BTW, what does "0-0" do in Johnny Mac's code? I'm not sure if that's a number, or a calculation, or maybe just trying to say, "put your variable here".

    rdlong 0-0, r1 ' copy parameter from hub to cog

    The 0-0 indicates that the data there is modified by some other code in the program.
Sign In or Register to comment.