Shop OBEX P1 Docs P2 Docs Learn Events
pass address to assembler routine in another module — Parallax Forums

pass address to assembler routine in another module

CncjerryCncjerry Posts: 64
edited 2012-06-14 11:18 in Propeller 1
I have a main routine that calls a start routine in another module. The start routine (in a separate file) then does a cognew of an assembler routine. This is from bastardized quickstart demo code.

Basically I want the main routine to call the object module with a main memory address that the assembler can read & write. Please refer to the questions in the comments below. This is example code that wont run, but it gets the idea across.

So the main routine does this:
VAR

long results

PUB Start


buttons.start(@results)          ' so this is passing the address of results to the following object file and it works
         

and in the buttons object file
PUB Start(ButRes)                      ' is the address of butres available and is it equal to the address of the long variable results from the caller above ???  

cognew(@entry,@butres)              ' so when I start the assembler routine below is it sending the address of butres defined in the pub statement or the address of results from the caller?
                                             ' I suspect it is the address of butres since the mov of #$02 into butin never gets to results.

DAT
          org 0
entry
          mov    butin, #$02      ' set to 2 for testing
          wrlong butin, par        ' write to butres in the object file or to results in main memory??
                                     ' par should be pointing to the address of butres which should be the address of results as well, no???
          jmp    #entry

butin res 1

Comments

  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-06-12 21:48
    As your program is now it will write 2 to the location of the parameters of your start method not to the location of "results".

    I think your code would write 2 to "results" if you got rid of the "@" symbol in you cognew statement.
    cognew(@entry, butres)
    

    You want the contents of butres not the location of butres passed to your PASM code.

    If you could watch the vaule of "butres" after the cog has been started, you'd see it change from the address of "results" to the value of 2.

    For example. If you changed the start method to:
    PUB Start(butres)
      cognew(@entry,@butres)
      repeat 
        Debug.char(13)
        Debug.dec(butres)
      while butres <> 2
    

    The cog might launch too fast to catch the value of butres before it's changed, but if you add a delay to the beginning of you PASM code, you should be able to catch when "butres" gets written too.
  • CncjerryCncjerry Posts: 64
    edited 2012-06-12 21:51
    I'll try that in a minute, but if I do:

    PUB start(butres)

    butres:=2 ' then you would think that the address would now be set to 2, but I am pretty sure butres as a long will be 2, no?

    cognew(@entry,butres) ' would be inconsistent then
  • CncjerryCncjerry Posts: 64
    edited 2012-06-12 21:54
    I just found some code from a few months ago where they used a routine in the object file to retrieve an object file local variable. This is what I am trying to avoid since several cogs will need to access the data dynamically, all assembler.
  • CncjerryCncjerry Posts: 64
    edited 2012-06-13 06:37
    Just to be clear, I want the location of the main module variable RESULTS passed to the assembler routine.
  • kuronekokuroneko Posts: 3,623
    edited 2012-06-13 06:58
    Cncjerry wrote: »
    Just to be clear, I want the location of the main module variable RESULTS passed to the assembler routine.
    Did you try cognew(@entry, ButRes) (as suggested)?
  • CncjerryCncjerry Posts: 64
    edited 2012-06-13 09:13
    Yes, I tried the cognew(@entry, ButRes) but it didn't work as you would expect. As in my example above, setting butres:=2 and then calling the routine I assume would send a 2, not the address of butres which then should have been pointing to the var results.

    I am going to try moving all these assembler routines back to the main module. It will work then since I wont have to call a start routine in another module.

    It does seem like it should work as suggested since the @results should be the address of the variable results put into butres which then cognew(@entry, ButRes) should send to the assembled routine. But the fact that you can assign a value directly to butres in the start routine means that it is being treated as a resolved litteral from the passed address.
  • CncjerryCncjerry Posts: 64
    edited 2012-06-13 09:27
    Moving the asm routine over to the main program, skipping the start routine in the module, obviously fixes the problem. I had moved the routines to separate modules to avoid naming conflicts. I have a number of masks, variables, etc. that will point to the same area of memory and I wanted them to be named the same (under separate org 0 statements) so I moved them to modules. This single main program that loads the others (opposed to having a module start routine with cognew of the asm) is cleaner with the exception of naming.

    There should be a technique to move addresses between spin modules but it eludes me.

    Thinking about it again, though, I could reserve an upper block of memory with constants that point to the address of the variable in that block. Then by using the constant in the rdlong and wrlong each asm routine would get the variable. I am going to play with this idea. I just need to figure out the way spin does indirect addressing. I think I would also need the address aligned on a long block line.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-06-13 10:01
    Cncjerry wrote: »
    There should be a technique to move addresses between spin modules but it eludes me.

    This is done all the time.

    Would you post your code where "2" isn't written back to "results"?

    How do you know it's not being written there?

    When I was trying to get my head around the way addresses were passed, I'd add a bunch of debug statements that printed both the location of a variable and its contents.

    There are several past threads about this issue. I think there's demo code that illustrates address passing in those threads. I'll see if I can find them and add a link here.

    Edit: I didn't find a thread specifically about passing address to a different object (not that it matters which object an address is passed to, it's still an address in hub RAM). I did find this thread about sharing variables between two PASM cogs.
  • CncjerryCncjerry Posts: 64
    edited 2012-06-13 15:15
    All the threads I saw used a spin routine in the module that would return the variable when called. In my app I have a number of asm routines running somewhat asynchronously processing data.

    What I have been able to do was create an address as below and move data in and out in spin:
         Results:=2    ' preload for testing
         resaddr:= $FF0 ' find an address out far in memory
         long[resaddr] :=1    ' set it to 1
         results:=long[resaddr]  ' move it into a long
    
    ' the follow-on code writes it to an lcd and it was confirmed correct as it should be.
    
    

    So this code works.

    I then did somewhat of the same in assembler:
          wrlong Bt, resadd    ' write a number to a location in hub memory
    
     ' do some processing 
    
        bt        long    $03     ' value for testing
        resadd     long $FF<<4   ' address as in the spin code $FF0
    
    

    So all this works and I skip all the address transfers. I am going to just allocate a block for data up in higher memory and work from there using constants for the addresses.

    I can't figure out how to pass addresses to modules using a spin routine with parameters though I am sure it is possible. If I send a spin routine an address, it looks like the data from the address is loaded into a compiled long variable opposed to the address. I could be wrong but since the parameter variables are not declared in a DAT section, the spin interpreter might treat all parameter declared variables as addresses anyway and do the address to value translation. Can't confirm it without more work, but I played around with it and couldn't get it to work. I was using some code to just write the values to my LCD and was never able to get the modified variable from the assembler routine. Using constants to point to memory addresses will work and I can keep my code in modules and not worry about passing addresses around.

    Making a data block in high memory is more to my liking anyway since I will have a number of variables that each of the ASM routines access.


    Jerry
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-06-13 15:33
    Your original code would pass the address of "results" to your PASM code with the modification I suggested. If you're not seeing a "2" being written to results, then there's a problem with some other part of your program.

    It would help if you posted the complete program so we could point out what you're doing wrong. I think you're making this harder than it needs to be.
  • Mike GreenMike Green Posts: 23,101
    edited 2012-06-13 15:57
    @cncjerry, You're making this all way too complicated. Here's an example:
       _clkfreq = 80_000_000
       _clkmode = xtal1 + pll16x
    
    var long sharedValue
    
    pub main | oldValue, newValue
       sharedValue := -1   ' Initialize sharedValue to -1
       oldValue := startCog(@sharedValue)   ' You could do the COGNEW here, but this illustrates more
       waitcnt(clkfreq/1000 + cnt)   ' Wait 1ms
       newValue := sharedValue
       ' At this point, oldValue should be different from newValue because the cog should have started, changed sharedValue, then quit
    
    pri startCog(parameter)
       cognew(@asmStart,parameter)   ' Note: parameter must be a multiple of 4 (long address) because of the way PAR register works
       return long[parameter]   ' It takes about 100us for the cog to actually start so sharedValue hasn't been changed yet by the cog.
    
    DAT
              org     0
    asmStart  mov     temp,#2   ' OK, set temporary variable to 2
              wrlong  temp,PAR   ' Write the 2 to the hub location passed via COGNEW
              cogid   temp   ' Now stop this cog
              cogstop temp
    
    temp      long      0
    
  • CncjerryCncjerry Posts: 64
    edited 2012-06-13 16:43
    Mike, put the assembler routine and its corresponding start routine in a separate module. Then call the start routine passing an address of hub memory so the asm routine can read/write it. I had no problem getting it to work when it is all in one module as you have above.

    I agree that it should work but for some reason, the address wasn't getting thru. Clearly I am new to the prop chip and asm.

    I don't know, but I think my final solution of just using constants to hold the offsets in hub memory is pretty simple. I then can put each cog asm routine in its own module, start them with a small spin in the same module and read/write all day long without passing any parameters.


    Jerry
  • kuronekokuroneko Posts: 3,623
    edited 2012-06-13 17:24
    Attached is a two-file version which works in the same way as Mike's single file version. Using hardwired hub addresses is just asking for trouble.
    {one.spin}
    
    con
      _clkfreq = 80_000_000
      _clkmode = xtal1 + pll16x
    
    obj
      serial: "FullDuplexSerial"
      helper: "two"
      
    var
      long  sharedValue
    
    pub main | oldValue, newValue
    
      serial.start(31, 30, %0000, 115200)
      waitcnt(clkfreq*3 + cnt)
      
      sharedValue := -1                                     ' Initialize sharedValue to -1
      oldValue := helper.startCog(@sharedValue)             ' You could do the COGNEW here, but this illustrates more
      waitcnt(clkfreq/1000 + cnt)                           ' Wait 1ms
      newValue := sharedValue
      ' At this point, oldValue should be different from newValue because the cog should have started, changed sharedValue, then quit
    
      serial.hex(oldValue, 8)
      serial.tx(32)
      serial.hex(newValue, 8)
      serial.tx(13)
    
    dat
    
    {two.spin}
    
    pub startCog(parameter)
       cognew(@asmStart,parameter)  ' Note: parameter must be a multiple of 4 (long address) because of the way PAR register works
       return long[parameter]       ' It takes about 100us for the cog to actually start so sharedValue hasn't been changed yet by the cog.
    
    DAT
              org     0
    asmStart  mov     temp,#2       ' OK, set temporary variable to 2
              wrlong  temp,PAR      ' Write the 2 to the hub location passed via COGNEW
              cogid   temp          ' Now stop this cog
              cogstop temp
    
    temp      res     1
    
    dat
    
  • CncjerryCncjerry Posts: 64
    edited 2012-06-13 22:22
    what happens If you remove 'return long[parameter]' in the spin code. Is sharedvalue updated when 'wrlong temp, par' is executed?
  • kuronekokuroneko Posts: 3,623
    edited 2012-06-13 23:28
    Cncjerry wrote: »
    what happens If you remove 'return long[parameter]' in the spin code.
    Then the result of the startCog method call will be zero (unless you override it all method calls return zero). It's just in there for convenience. You know the initial value as it's been set just before the call (-1).
    Cncjerry wrote: »
    Is sharedvalue updated when 'wrlong temp, par' is executed?
    Correct. That's the idea here.
  • CncjerryCncjerry Posts: 64
    edited 2012-06-14 08:23
    Well, it all makes sense. Can't figure out why I couldn't get it to work. The code is gone,a victim of editing.

    One last ( read next ) question: is it possible to modify cog memory directly from another cog? Is there a faster way to move data between cogs?

    Even after all this works, I think I am still architecting this code with the allocated high memory addresses. This method seems to fit more with my asm background.
  • CncjerryCncjerry Posts: 64
    edited 2012-06-14 08:35
    I guess the answer to the cog memory question is a resounding NO. Found that one.
  • Mike GreenMike Green Posts: 23,101
    edited 2012-06-14 08:39
    No. There is no connection from a cog's memory to any other cog. If you have some I/O pins available, you can move data via the I/O registers. One cog puts data into OUTA and another cog reads the data via INA. The I/O pins wouldn't be connected to anything although you could hook up some LEDs to monitor what's being transferred. It's possible to transfer data this way serially at speeds on the order of MHz with only two I/O pins, faster if you can use several I/O pins.
  • CncjerryCncjerry Posts: 64
    edited 2012-06-14 11:06
    Mike, this is very creative, thanks.

    Mike Green wrote: »
    No. There is no connection from a cog's memory to any other cog. If you have some I/O pins available, you can move data via the I/O registers. One cog puts data into OUTA and another cog reads the data via INA. The I/O pins wouldn't be connected to anything although you could hook up some LEDs to monitor what's being transferred. It's possible to transfer data this way serially at speeds on the order of MHz with only two I/O pins, faster if you can use several I/O pins.
  • Heater.Heater. Posts: 21,230
    edited 2012-06-14 11:18
    Sharing data through some fixed high memory addresses seems like a really bad idea. How will you know when your code or stack tramples over that area?
    Also you don't need to. You can always get the address of any data area you define in Spin or PASM and pass it around as required.
Sign In or Register to comment.