Shop OBEX P1 Docs P2 Docs Learn Events
How do I cognew a Spin binary? — Parallax Forums

How do I cognew a Spin binary?

Dave HeinDave Hein Posts: 6,347
edited 2010-06-15 22:23 in Propeller 1
I want to include a Spin binary in a program, and then run it in another cog.· It seems like the way to do this is to set up the stack with the correct stuff, and then do a cognew that loads the Spin interpreter·from ROM.

Could one of the Spin gurus look at my code and tell me if I'm on the right track, and if so, what would I need to change to actually make it work?

Thanks,
Dave

CON
  SPIN_INTERP_ADDR = $f002  ' NOTE: This should be long-aligned
  OBJECT_BASE      = 0
  OBJECT_VAR_BASE  = 1
  STACK_FRAME      = 2
  RETURN_ADDRESS   = 3
  PARM1_OFFSET     = 2
  PARM2_OFFSET     = 3
 
VAR
  long stack[noparse][[/noparse]20]
  long varspace[noparse][[/noparse]100]
 
PUB main
  InitializeStack(@stack, 2, 4, 1, 2)
  cognew(SPIN_INTERP_ADDR, @stack)
 
PUB InitializeStack(stackptr, numparms, numlocals, parm1, parm2)
  word[noparse][[/noparse]stackptr][noparse][[/noparse]OBJECT_BASE]     := @testcode ' NOTE: Need to add flags
  word[noparse][[/noparse]stackptr][noparse][[/noparse]OBJECT_VAR_BASE] := @varspace
  word[noparse][[/noparse]stackptr][noparse][[/noparse]STACK_FRAME]     := stackptr + 8 + (numparms + numlocals) * 4
  word[noparse][[/noparse]stackptr][noparse][[/noparse]RETURN_ADDRESS]  := 0         ' NOTE: Not sure what this should be
  long[noparse][[/noparse]stackptr][noparse][[/noparse]PARM1_OFFSET]    := parm1
  long[noparse][[/noparse]stackptr][noparse][[/noparse]PARM2_OFFSET]    := parm2
 
DAT
  testcode FILE "test.binary" ' This is a Spin binary

Comments

  • BradCBradC Posts: 2,601
    edited 2010-06-12 01:46
    Here's a spin program I use to load an existing spin binary from eeprom into an arbitrary place in ram, fix up the header and execute it while the existing code is still running. Maybe it'll give you an idea.

    CON
    
    
    
      _clkmode = xtal1 + pll16x
    
      _xinfreq = 5_000_000
    
      LoadBase = $400
    
    
    
    OBJ
    
      i2cObject     : "basic_i2c_driver"
    
    
    
    PUB Go | EAddr, PAddr, Length, i
    
        dira[noparse][[/noparse]7] := 1
    
        i2cObject.Initialize(28)
    
        PAddr := LoadBase
    
        EAddr := 0
    
    
    
        outa[noparse][[/noparse]7] := 1
    
        i2cObject.ReadPage(28, $A0, EAddr, PAddr, 12)
    
        Length := Word[noparse][[/noparse]LoadBase+$A]
    
        i2cObject.ReadPage(28, $A0, EAddr, PAddr, Length)
    
    
    
        ' Fixup
    
        Repeat i from 0 to 4
    
          Word[noparse][[/noparse]LoadBase+6+i<<1] += LoadBase
    
    
    
        Cognew($F004, Loadbase+4)
    
        repeat
    
          outa[noparse][[/noparse]7] := cnt[noparse][[/noparse]23]
    
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "I mean, if I went around sayin' I was an emperor just because some moistened bint had lobbed a scimitar at me they'd put me away!"
  • jazzedjazzed Posts: 11,803
    edited 2010-06-12 01:53
    I believe the Spin interpreter actually starts at $F004. I could be wrong.

    Below is one way to do it.
    You could also load/fix-up in a COG and run the binary with coginit ....

    PRI boot(fname) | ii, sbuf[noparse][[/noparse]100], rc, len, sdpin, spinbin
    
      print(string(CR,"Boot from SD Card pin "))
      dec(byte[noparse][[/noparse]@cfg_sdpins+bd#SDCARD_BASEPIN_BYTE])
    
      if sdopen > -1
        spinbin := loadProgram(fname)
    
      if spinbin
        newline   ' last chance for output
        ' kill all cogs except main interpreter
        repeat ii from 0 to 7
          if ii <> cogid
            cogstop(ii)
        cognew($F004, spinbin+4)
        waitcnt(clkfreq/10+cnt)
        cogstop(cogid)
        repeat
    
    PRI loadError(s,fname)
      print(s)
      puts(fname)
      bsd.pclose
      return 0
    
    PRI loadProgram(fname) | rc,ii,plen,progptr
    
      rc := bsd.popen(fname, "r")
      if rc
        return loadError(@openFail,fname)
       
      repeat ii from 0 to 15
        byte[noparse][[/noparse]@proghdr][noparse][[/noparse]ii] := bsd.pgetc
    
      plen := word[noparse][[/noparse]@proghdr+$A] ' plen at stack base gives enough room for prog
       
      print(string(CR,"File "))
      print(fname)
      print(string(" size "))
      dec(plen)
      newline
    
      if plen > PROGLEN
        return loadError(@memFail,fname)
            
      bsd.pclose
    
      progptr := @heap
      progptr := (progptr + 4) & !3
      
      rc := bsd.popen(fname, "r")
      if rc
        return loadError(@openFail,fname)
    
      rc := bsd.pread(progptr, plen)
      
      if rc =< 0
        return loadError(@readFail,fname)
    
      bsd.pclose
    
      ' Fixup
      Repeat ii from 0 to 4
        Word[noparse][[/noparse]progptr+6+ii<<1] += progptr
    
      ' Clear varspace
      rc := word[noparse][[/noparse]@proghdr+$8]
      Repeat ii from rc to plen step 4
        long[noparse][[/noparse]progptr+ii] := 0
        
      long[noparse][[/noparse]progptr+plen-4] := $fff9ffff
      long[noparse][[/noparse]progptr+plen-8] := $fff9ffff
    
      return progptr
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Propeller Pages: Propeller JVM
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-06-12 12:21
    Thanks for the suggestions.· I'm not sure that will do what I want, but I'll give it a try.

    What I want to do is something like this;

    VAR
      long stack[noparse][[/noparse]20]
     
    PUB main
      cognew(CogOneCode, @stack)
      <The rest of Cog 0 code>
     
    PUB CogOneCode
      <The rest of Cog 1 code>
    

    The difference is that I want to load Cog 1 code from a binary file instead of including the source.· Cog 0 will continue to run while Cog 1 runs.· Basically, I want to trick the Spin interpreter into doing a Spin cognew rather than a PASM cognew.

    Dave
  • BradCBradC Posts: 2,601
    edited 2010-06-12 13:29
    Dave Hein said...
    Thanks for the suggestions. I'm not sure that will do what I want, but I'll give it a try.
    <snip>
    Dave Hein said...

    The difference is that I want to load Cog 1 code from a binary file instead of including the source. Cog 0 will continue to run while Cog 1 runs. Basically, I want to trick the Spin interpreter into doing a Spin cognew rather than a PASM cognew.

    They are loading an unmodified propeller binary and launching the top object in a spin cog while leaving the existing cogs running, which appears to be precisely what you are trying to do.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "I mean, if I went around sayin' I was an emperor just because some moistened bint had lobbed a scimitar at me they'd put me away!"
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-06-13 03:40
    Brad and jazzed,

    I tried out the code and it work great!· I was able to run a spin binary file·in a new cog.

    Thanks,
    Dave
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-06-13 13:40
    One thing that concerns me about loading a spin binary is the stack space.· The spin binary file only contains the code, and it does not include the VAR·bytes or the stack space.· The VAR space can be determined by computing the difference between the VAR start address and the stack start address in the 16-byte program header.· However, there is nothing to indicate how large the stack should be.

    What's the best way to handle the stack size issue?· Maybe additional information can be added to the end of the spin binary file to define the stack size needed.· If it is not included, then an arbitrarily large stack would be allocated, such a 1 or 2 kbytes.

    Dave
  • Bill HenningBill Henning Posts: 6,445
    edited 2010-06-13 13:44
    Perhaps we can get BradC to add a directive to BST:

    CON
    _stacksize = nnn
    Dave Hein said...
    One thing that concerns me about loading a spin binary is the stack space. The spin binary file only contains the code, and it does not include the VAR bytes or the stack space. The VAR space can be determined by computing the difference between the VAR start address and the stack start address in the 16-byte program header. However, there is nothing to indicate how large the stack should be.

    What's the best way to handle the stack size issue? Maybe additional information can be added to the end of the spin binary file to define the stack size needed. If it is not included, then an arbitrarily large stack would be allocated, such a 1 or 2 kbytes.

    Dave
    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    www.mikronauts.com E-mail: mikronauts _at_ gmail _dot_ com
    My products: Morpheus / Mem+ / PropCade / FlexMem / VMCOG / Propteus / Proteus / SerPlug
    and 6.250MHz Crystals to run Propellers at 100MHz & 5.0" OEM TFT VGA LCD modules
    Las - Large model assembler Largos - upcoming nano operating system
  • BradCBradC Posts: 2,601
    edited 2010-06-13 14:27
    Bill Henning said...
    Perhaps we can get BradC to add a directive to BST:

    CON
    _stacksize = nnn

    Where would you store it? (as a long between VAR and Stack ?)
    Even then, it's still guesswork on the part of the original binaries author.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "I mean, if I went around sayin' I was an emperor just because some moistened bint had lobbed a scimitar at me they'd put me away!"
  • BradCBradC Posts: 2,601
    edited 2010-06-13 14:28
    .. additionally, by massaging the header in a slightly different way to the ones listed above, you can put both VAR and Stack wherever you like. Just change DBASE & VBASE prior to launching the interpreter

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "I mean, if I went around sayin' I was an emperor just because some moistened bint had lobbed a scimitar at me they'd put me away!"
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-06-13 18:13
    BradC said...

    Where would you store it? (as a long between VAR and Stack ?)
    Even then, it's still guesswork on the part of the original binaries author.
    Maybe the desired stack size could be defined at the begiinning of the top object's DAT space.· The compiler wouldn't be involved in setting it up.· It could look something like:
    DAT
      long $1ead5afe ' Stack size magic word
      long 500       ' Stack size in longs
     
    

    The loader would know that the stack size in being specified because the first long contains the magic workd $1ead5afe.· If the magic word is not present, then the loader would have to use some reasonable value for the stack size.· Perhaps other information could be included with the stack size, such as whether the program must run as the only app in the system.
    The author should be able to detemine the stack size needed for his program.· He might be able to figure it out from the worst case call depth in the program, or he could run a stack analyzer to determine it.
    Dave
  • Bill HenningBill Henning Posts: 6,445
    edited 2010-06-13 20:41
    I was thinking of the compiler massaging the appropriate entries, based on knowing what the desired _stacksize was [noparse]:)[/noparse]

    But I agree, it can be done externally.

    I now have stripped down and modified PropCMD, leaving 24KB free in the hub, and using unix-style commands.

    I think I can get it to the point were there will be almost 28KB is free in the hub, allowing mailbox-aware spin programs to run without having to reboot... and since cog images won't have to be part of the spin code, it will actually leave more memory for the programs!
    BradC said...
    .. additionally, by massaging the header in a slightly different way to the ones listed above, you can put both VAR and Stack wherever you like. Just change DBASE & VBASE prior to launching the interpreter
    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    www.mikronauts.com E-mail: mikronauts _at_ gmail _dot_ com
    My products: Morpheus / Mem+ / PropCade / FlexMem / VMCOG / Propteus / Proteus / SerPlug
    and 6.250MHz Crystals to run Propellers at 100MHz & 5.0" OEM TFT VGA LCD modules
    Las - Large model assembler Largos - upcoming nano operating system
  • BradCBradC Posts: 2,601
    edited 2010-06-14 01:19
    Bill Henning said...
    I was thinking of the compiler massaging the appropriate entries, based on knowing what the desired _stacksize was [noparse]:)[/noparse]

    Your problem there is while there is a stack start entry, there is nothing to specify where it ends or how big it might get. The stack just goes from DBASE until it hits ROM (and I can tell you weird stuff happens when it bumps into ROM)

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "I mean, if I went around sayin' I was an emperor just because some moistened bint had lobbed a scimitar at me they'd put me away!"
  • Bill HenningBill Henning Posts: 6,445
    edited 2010-06-14 13:55
    Yes, I can clearly see that Weird Stuff (tm) would happen smile.gif
    BradC said...
    Bill Henning said...
    I was thinking of the compiler massaging the appropriate entries, based on knowing what the desired _stacksize was [noparse]:)[/noparse]

    Your problem there is while there is a stack start entry, there is nothing to specify where it ends or how big it might get. The stack just goes from DBASE until it hits ROM (and I can tell you weird stuff happens when it bumps into ROM)
    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    www.mikronauts.com E-mail: mikronauts _at_ gmail _dot_ com
    My products: Morpheus / Mem+ / PropCade / FlexMem / VMCOG / Propteus / Proteus / SerPlug
    and 6.250MHz Crystals to run Propellers at 100MHz & 5.0" OEM TFT VGA LCD modules
    Las - Large model assembler Largos - upcoming nano operating system
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-06-14 16:18
    Normally additional Spin cogs are started up by referencing a method within a larger Spin program as follows:
    VAR
      long stack[noparse][[/noparse]20]
    PUB main
      cognew(Cog1Program, @stack)
      <more cog 0 stuff>
    PUB Cog1Program
      <cog 1 stuff>
    
    

    In this example, the programmer knows (or should know) that a stack of 20 longs is sufficient for the program in cog 1.· There are various techniques for determining the stack usage, and ensuring that enough memory is allocated for the stack.

    When loading a Spin binary file it would be useful to provide the stack size to the loader.· That is why I am suggesting including it in the top object's DAT space.· It doesn't require any changes to the compiler.
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-06-15 04:12
    The loader works great now, but I have a couple of other problems to solve now.· I would like to pass an argument count and a list of pointers to the program.· Is there an easy way to do that?· If not, I'll have to implement a method for passing parameters.

    The other is issue is cleaning up after the program is done.· I need to free up the cog and also free up the memory the program used.· What normally happens when the entry method reaches the end?· Do it automatically free up the cog or does it spin in an infinite loop?

    Dave
  • BradCBradC Posts: 2,601
    edited 2010-06-15 04:16
    Dave Hein said...
    The loader works great now, but I have a couple of other problems to solve now. I would like to pass an argument count and a list of pointers to the program. Is there an easy way to do that? If not, I'll have to implement a method for passing parameters.

    Why not use the first 2 words in the image to do that (they are usually the clock speed). You could then have your image locate the method table and work back from there to find the pointers.
    Dave Hein said...

    The other is issue is cleaning up after the program is done. I need to free up the cog and also free up the memory the program used. What normally happens when the entry method reaches the end? Do it automatically free up the cog or does it spin in an infinite loop?

    No, once the end of the code is reached, the implicit return causes the cog to stop.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "I mean, if I went around sayin' I was an emperor just because some moistened bint had lobbed a scimitar at me they'd put me away!"
  • jazzedjazzed Posts: 11,803
    edited 2010-06-15 04:55
    For passing parameters, can't you just set data using the current stack pointer word[noparse][[/noparse]$e] or @result ?

    Maybe I'm just hallucinating, but would this work?

    pub loader | sp
    ...
      ' Fixup
      Repeat ii from 0 to 4
        word[noparse][[/noparse]progptr+6+ii<<1] += progptr
    
      sp := word[noparse][[/noparse]progptr+$e]
      sp += 2
      word[noparse][[/noparse]sp] := argc
      sp += 2
      word[noparse][[/noparse]sp] := argv
      sp += 2
      word[noparse][[/noparse]sp] := argv
    ...
    
    pub main | sp, argc, argv
      sp := @result
      sp-=2
      argc := word[noparse][[/noparse]sp]
      sp-=2
      argv := word[noparse][[/noparse]sp]
      ...
    
    


    If you can do that, you should also be able to leave the return code in the stack also, but it depends on how you recover after exit. Might have to leave the return code in a predefined location ... I don't know exactly. All pure speculation at this point, so this could be worthless pondering.

    Cheers,
    --Steve

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Propeller Pages: Propeller JVM
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-06-15 13:52
    Thanks for the suggestions on passing parameters.· They both sound good.· Pre-loading the stack sounds very interesting.· I'll give that a try.

    Program cleanup after completion will require a little work.· A well behaved program should close all its files and free any memory it allocated.· The OS will just have to free up the memory that the program was loaded in.· A poorly behaving program may leave a memory mess when it exits.· The OS would have to close all the open files and free up memory, which would require tagging them with the COG ID.· I'll see if I can get the well behaved case working first.

    Dave
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-06-15 22:13
    I got the stack method to work.· I had to use the stack address at $a instead of the one at $e.· The address at $a points to the result value, and the one at $e points to the starting stack·frame for the method.· I also stored the argc and argv parameters as long values.· This allowed the program to access them by defining the first two local stack variables as argc and argv as follows:

    PUB main | argc, argv, i, str
      repeat i from 0 to argc - 1
        str := long[noparse][[/noparse]argv][noparse][[/noparse]i]
        <more code>
    

    Thanks,
    Dave
  • jazzedjazzed Posts: 11,803
    edited 2010-06-15 22:23
    Woo hoo! That looks great! Can't wait to see your code. Any luck with collecting return status?

    Delighted!
    --Steve

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Propeller Pages: Propeller JVM
Sign In or Register to comment.