Shop OBEX P1 Docs P2 Docs Learn Events
Spin feature request - Page 2 — Parallax Forums

Spin feature request

2

Comments

  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-01-09 11:51
    @simonl: Thanks, that would be great. I'll post something here in a day or two. smile.gif

    Steven
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-01-09 12:06
    I can confirm that it works. I changed your DOL program to output
    debug messages using MultiCogSerialDebug object as I don't have VGA.
    It shows:
    heap available 3FFA
    objectLength 06A8
    objectStartPtr 1605

    regards peter
  • hippyhippy Posts: 1,981
    edited 2008-01-10 03:15
    @ steven : Finally had a chance to look at your code and it's quite impressive, and DOL quite a powerful tool. Not astoundingly easy to use yet because the PropTool doesn't have the support for dynamic objects and so on, but if the programming effort is put it I think it could yield impressive results. It's certainly good enough to launch whole programs from PropDoS etc.

    When I first suggested just moving the object I never realised it was actually really that simple, just update a dummy object entry to point to the loaded object, call the dummy object which really calls the loaded object and everything else happens automatically ( Edited : Except stopping VAR being dumped in the stack space ). Thanks to Chip there for a position independent implementation of objects and Spin in general.

    I've been exercising my brain trying to think of when the Spin Compiler might generate absolute addressed code or pointers which would affect DOL but haven't so far come up with any scenario.

    You've got far further than I have. My absolute basic minimum implementation is attached.

    Post Edited (hippy) : 1/10/2008 3:23:07 AM GMT
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-01-10 07:04
    @hippy: I like that. It's much neater than all my code smile.gif. Maybe I'll have to make two versions. A full version with everything and a cut down version that that doesn't do calls between objects.

    Steven
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-01-10 10:31
    @Peter: I just figured out what it is. Its a bug in gear. Sorry to cause you trouble. From now on I'll test it on an actual propeller.
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-01-10 11:01
    No update for tonight. I made a whole heap of changes for V_007 and busted something in the process. I will have to go back to V_006 to find out what it is. confused.gif
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-01-11 09:57
    @Steven and Hippy,
    Would it be possible to find the values assigned to an OBJ tag?
    For example:
    My heap_test program has
    OBJ
    · debug: "MultiCogSerialDebug"· 'debugport serial driver
    · mem:·· "heap"················ 'dynamic memory control
    · 'mem2: "heap"
    VAR
    · long atnStack[noparse][[/noparse]10]······· 'stack for monitoring ATN pin
    · byte debugSemID········· 'lock to be used by debug
    · byte heapArray[noparse][[/noparse]HEAPSIZE] 'byte array to serve as heap
    · word block1,block2

    The heap object has
    VAR
    · word heapStart······ '//address of heap
    · word heapEnd········ '//points to closing 0 word

    When I compile I get Program 1073 longs,·Variables 278 longs.
    If I uncomment mem2 in the main program that becomes
    Program 1074 longs, Variables 279 longs.
    Apparently the mem2 tag consumes a long in program area.
    I was thinking, if the value·for mem2 could be retrieved and passed
    to another object,·that has
    OBJ
    · mem3: "heap"
    and I would replace the value for mem3 with the retrieved value of mem2,
    then any reference mem3.??? would actually access mem2.???

    I know this is not really related to dynamically loading·objects, but would the above
    be possible? Or is there more involved?

    regards peter
    ··
  • hippyhippy Posts: 1,981
    edited 2008-01-11 11:58
    @ Peter : Yes that's entirely possible and effectively what I do in my minimal code. For OBJ mem3: "heap" you can obtain a pointer which is effectively @mem3

    I do that by using a named label in a DAT section of what here is "heap.spin". Because of the way the PropTool locates code within hub memory, that label is given the hub address of the long immediately after what I call the 'method pointer table'. By knowing how many methods there are one can simply offset back to the start of the object, ie @mem3.

    From knowing @mem3, one can then find where the entry is in the 'method pointer table', and consequently, because all OBJ entries are sequential, addresses of all other sub-objects can be obtained through that table.

    Getting "heap.spin" to return its object base may ( I haven't tested ) be difficult if it includes sub-objects as the magic label used to get its own address may not end up where desired, however, put that in a dummy object as per my code, and once you have the address of an object ( ie, @dummy ), you can find the address of any other object.

    Once you have @dummy and @mem3, and know where the entry pointing to @dummy is in the 'method pointer table', drop @mem3 in the 'Point to @dummy' entry and any calls to the dummy object then vector off to the mem3 object.
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-01-11 12:15
    A simple dummy object won't do I think. What I meant was to get the value for mem2 in the
    main program, and write that value at the location for mem3 in another object so that when
    using mem3.somefunction it really calls mem2.somefunction. Since mem2 and mem3 are the
    same object type I can use the method names.

    OBJ
    · i: "obj1"
    ··j: "obj2"
    What is required is a function
    PUB getObjectAddr(index):addr
    that returns the address of the object tag.
    For obj1 the index=0, for obj2 the index=1
    value := long[noparse][[/noparse]getObjectAddr(0)]
    obj2.passObjectHandle(value)

    obj2 has
    OBJ
    · k: "obj1"· 'this has index 0
    PUB
    · long[noparse]/noparse]getObjectAddr[noparse][[/noparse]0 := value 'value·that was·passed to obj2

    regards peter
    ·
  • hippyhippy Posts: 1,981
    edited 2008-01-11 12:17
    Hopefully another 'picture worth a thousand words'. This shows how an object is laid out in hub memory by the Prop Tool. Where any sub-object is included isn't indicated because I ahvenet checked. AFAIR, they are placed after the DAT Code and before the Executable Method Code, but I may be wrong.

                    .---------------.
    Object Base --> |    Method     |
                    | Pointer Table |
                    |_______________|
                    |               |
                    |   DAT Code    |
                    |_______________|
                    |               |
                    |  Executable   |
                    |  Method Code  |
                    |               |
                    `---------------'
    
    
    



    The 'method pointer table' in more detail ...

                    .---------------.
    Object Base --> |_______________|----------> To next object in application
                    |_______________|---.        To first PUB method
                    |_______________|---|---.    To next method
                    |               |   |   |
                    |               |   |   |
                    |               |---|---|--> To object base of first sub-object
                    |               |---|---|--> To object base of second sub-object
                    |_______________|   |   |
                    |               |   |   |
                    :   DAT Code    :   |   |
                    |_______________|   |   |
                    |               | <-'   |
                    :   PUB First   :       |
                    |_______________|       |
                    |               | <-----'
                    : Other Methods :
                    |               |
                    `---------------'
    
    
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-01-11 12:25
    Where in your picture are the object tags located, for the objects declared under OBJ

    regards peter
  • hippyhippy Posts: 1,981
    edited 2008-01-11 12:25
    Peter Verkaik said...
    What I meant was to get the value for mem2 in the
    main program, and write that value at the location for mem3 in another object so that when
    using mem3.somefunction it really calls mem2.somefunction. Since mem2 and mem3 are the
    same object type I can use the method names.

    That should work, but if both objects are the same type then the PropTool will AIUI have made the two pointers point to the same hub address anyway; objects are re-entrant so there will only ever be one copy of an object's 'method pointer table' and executable code in hub.

    What differentiates the two objects is where the Variable Base is set when each object is used, each gets its own unique VAR area allocated in hub. To make mem3 exactly the same as mem2 means having to adjust the Variable Base ( not sure how that would be done ) or ensure that mem2 only has its data within a DAT section.

    I suppose one way to look at it in an OOP way is that variables/data in DAT is static, common for all instantiations of that object, variables in VAR are unique for each instantiation - please excuse my OOP terminology if it's wrong.
  • hippyhippy Posts: 1,981
    edited 2008-01-11 12:28
    Peter Verkaik said...
    Where in your picture are the object tags located, for the objects declared under OBJ

    From a top-object ( main program ) the object tags for its sub-objects are the "To base of N object" pointers ( placed after PUB/PRI method pointers ).

    For sub-objects including further sub-objects, same again but within their own 'method pointer table'.
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-01-11 12:30
    I was hoping the 32bit value for an object tag would hold the reference for the
    VAR area used by that specific object, so for·two objects
    OBJ
    · p: "obj"
    · q: "obj"
    the tag values would be different.

    regards peter
  • hippyhippy Posts: 1,981
    edited 2008-01-11 12:59
    Yes, the object tag does hold a different reference for each object; each tag is a long, the first word is the address of the object base ( the same for each in this case ), the second word is the number of bytes to add to Variable Base which would be different.

    Being a value to add is what makes it difficult to make them both point to the same Variable Base. Not a problem here when p and q are in the same object - simply replace the add value for q with that from p, but if p was in one object and q were in a sub-object the Variable Base would have been altered when the object containing q was entered, so one couldn't add the same value to get to the same Variable Base.

    Theoretically the add value could be adjusted for q in the sub-object's table but I don't know if making that two's complement negative would work if it needed to be a subtract rather than add; if Variable Base is held as a long, does adding a two's complement word work ? It may. It may break for the Prop II.

    Then there's the challenge of being able to determine what the add value should from within the sub-object. Calling q from the top object and from a sub-object would allow the Variable Base for each to be determined and it should be possible to determine the fixup needed but it could get quite complicated.

    I'm afraid I can only comment theoretically because I've not tried any of this.
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-01-11 13:23
    I see. Not so simple then. I wonder what the benefits are of using an offset
    to the variable base rather than using the address of the var section directly in the tag value.
    After all, the program·is staticly linked and the methods are referenced via object base
    already.

    regards peter
  • hippyhippy Posts: 1,981
    edited 2008-01-11 14:32
    It is a bit of a mystery.

    One possibility is that on the way into an object the offset can be added and on the way out subtracted again, but that requires keeping a reference to the tag on the stack and it seems easier to just push the previous Variable Base and pop that back when done.

    Thinking about that though, and it would make some sense; if the Object Base is pushed anyway the index to the tag can be pushed as a byte which frees up a byte which can be used to hold the indication of whether the call was trappable or not, should have its result discarded or not, and indicate if it was a local or inter-object call.

    I never fully discovered / understood the stack framing used by the ROM Spin Interpreter when implementing SpinVM. I did it by pushing the Object Base, Variable Base, Stack Pointer and Return PC as words ( simply pop everything back to how it was on return ) and used the unused lsb's of an always long aligned word to hold the calling information. Quite messy but works.

    I've also not worked out why there's a hard cap on 255 methods/sub-objects allowed in each object. The operands for calls could easily allow more, the table can allow more, so there's some unknown but rational explanation to be had elsewhere. If the area on stack for the index is fixed as a byte that could explain it.
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-01-12 00:17
    New version of DOL. Lots of fixes and new features. It can now load more than one object and allows calls between objects. This one does all kinds of calling between objects and DOL. See the version table in DOL. I just want to fix up the aborts a bit and then I will do a version that reads from the SD card. smile.gif

    Steven
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-01-12 00:20
    hippy said...
    I've been exercising my brain trying to think of when the Spin Compiler might generate absolute addressed code or pointers which would affect DOL but haven't so far come up with any scenario.

    I think this will happen if you use the '@@' or '@' operators in a DAT section. Haven't tried it yet though.
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-01-12 00:23
    @simonl: What board, display and crystal do you have so I know what settings need to be made?
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-01-12 00:27
    Peter said...
    I see. Not so simple then. I wonder what the benefits are of using an offset
    to the variable base rather than using the address of the var section directly in the tag value.
    After all, the program is staticly linked and the methods are referenced via object base
    already.

    Just guessing, but I think it has to do with the compiler. The compiler can go through and compile each sub-object individually and then just put in the offsets in the method pointer table. Even if the sub-object has sub-objects it would still work and be pretty simple.

    Steven
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-01-12 00:49
    Steven,
    I compared/assumed it the way the javelin handles things. The javelin uses 16bit tagvalues (when using the
    'new' keyword to create an instance)
    that you can pass to other objects. The tagvalue is the address where the object base address
    and var base address for that specific instance are located. So no matter where you use
    the tagvalue, it always points to the areas for the specific instance.
    In spin·the tagvalue must hold an offset to the var base of the object in which the tagvalue is used.
    That makes it not so simple. Because all is staticly linked, the absolute addresses could have been
    used I am sure. Perhaps the prop II handles it better (an updated interpreter).
    I found an alternative way to pass an object. Rather than copying the tagvalue, I pass
    the properties of an object and call the object initialization routines from the other object.
    http://forums.parallax.com/showthread.php?p=699699· (3rd·post from last)
    I realize that only works nice when the properties are pointers to a datastructure.
    Lets see what DOL can do (I would like to use named methods)

    regards peter
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-01-12 00:59
    @Peter: DOL can't use method names, but, you can define constants like this
    #some number greater than 16
    method1
    method2
    method3
    etc..
    and then just copy it to other objects and use these to call the methods. You will still need a data structure if you have more than one argument. If someone (please parallax, at least fix the spin compiler so that it can call a pre-processor that someone here writes) wrote a pre-processor then we wouldn't need to do this. You could just include an object definition file and the preprocessor would handle all the mucking around.

    Steven
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-01-12 01:15
    Steven,
    I just thought of something. DOL uses a dummy object whose pointers are adjusted
    to reflect the target object, right?
    What if I have an object MyObject and I create a dummyMyObject
    that has all the public and private methods (including arguments)
    but with code removed (just the method names + arguments as in MyObject)
    and use dummyMyObject as the dummy object. Its method table would then
    be identical to the MyObject method table, wouldn't it?
    Could we then use the method names directly?

    BTW, did you upload the latest DOL?

    regards peter
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-01-12 01:32
    Sorry, I've done that twice now. I'm not old enough to be getting Alzheimer's yet. Here it is.

    Peter, it won't work with how DOL is currently set up. To call another object, the callObject method in DOL is called. This then determines which dynamically loaded oject matches the name passed to it (if any, otherwise DOL aborts). It then plays around with the pointers and calls the object. When the object is called, methodSelector takes the method name passed to it and figeres out what to do. Obviously this is going to take more time than a normal object call, but you don't generally use spin when speed is tight any way. smile.gif

    However, if you wanted to, you could include your dummy object, dynamically load the real object and then call DOL to get the location of the real object and then adjust the method pointer table as required. Note though that when you include a dummy object it uses up a long for every PUB, PRI and sub-object in the dummy object plus a byte for every method (needs a return op-code). If you like I can do a demo of this when I get DOL finished.

    Steven
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-01-12 01:48
    Steven,
    I actually had the example in mind that I discussed with Hippy some posts back.
    Since there are offsets involved, I adapted that example.

    What I meant was to get the value for mem2 in the
    main program, and write that value at the location for mem3 in another object so that when
    using mem3.somefunction it really calls mem2.somefunction. Since mem2 and mem3 are the
    same object type I can use the method names.

    OBJ
    · i: "obj1"
    ··j: "obj2"
    What is required is a function
    PUB getObjectAddr(index):addr
    that returns the address of the object tag.
    and a function getVarBase that returns the varbase address
    For obj1 the index=0, for obj2 the index=1
    value := long[noparse][[/noparse]getObjectAddr(0)]
    value.word[noparse][[/noparse]0] += getVarBase 'now the tagvalue holds absolute var address for this instance
    obj2.passObjectHandle(value)

    obj2 has
    OBJ
    · k: "obj1"· 'this has index 0
    PUB
    · value.word[noparse][[/noparse]0] -= getVarBase 'tagvalue holds offset to varbase of obj2
    · long[noparse]/noparse]getObjectAddr[noparse][[/noparse]0 := value 'value·that was·passed to obj2

    I assume here that the low word of the tagvalue is the offset to varbase.
    Would this work? If so, is it also required that both main program and obj2 include
    the routines getObjectAddr and getVarBase?

    regards peter
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-01-12 02:31
    @Peter: If you have a DAT section (must be the first DAT section) like this

    DAT
      endOfObjects long 0
    
    



    then

    offsetToObject:=word[noparse][[/noparse] @endOfObjects-4 ]
    offsetToVAR:=word[noparse][[/noparse] @endOfObjects-2 ]
    



    will give you the offset to the sub-object (from the base of the current object) and the offset for the VARBase (from the current VARbase) of the last sub-object respictively (In your case obj2). To get the addresses of ealier sub-objects, simply subtract 4 for each place further back the object is.

    Now, if you want to use this value in another sub-object it starts getting a little tricky. What you have to do is figure out the offset from the base of the sub-object back to the sub-object that you want to call. And then do it again for the VAR base.

    What do you mean by index? Do you mean the location in the method table or is it just some variable to indicate which method?

    So the code you want is something like this. (haven't tested it yet so be warned)
    'in the main object
    OBJ
     i:"obj1"
     j:"obj2"
    
    DAT
     endOfObjects long 0 ' you can store any value in here. we just need the address
    
    VAR
     long firstVariable 'just the first varable
    PUB start
     j.passObjectHandle(getObjectAddress(1)-getObjectAddress(0),getObjectVARBase(1)-getObjectVARBase(0)
     'then you can call some other method in obj2 and get it to run methods in obj1
     j.run
    
    PUB getObjectAddress(index)
    ' if index==1  'using the absolute addresses
    '  return word[noparse][[/noparse] @endOfObjects-4 ]
    ' elseif index==0
    '  return word[noparse][[/noparse] @endOfObjects-8 ]
    'or if you want the absolute address and not the offset (makes finding the new offset easier)
     if index==1
      return word[noparse][[/noparse] @endOfObjects-4]+$10 'the $10 is where the first object starts
     elseif index==0
      return word[noparse][[/noparse] @endOfObjects-8]+$10
    
    PUB getObjectVARBase(index)
    ' if index==1  using the absolute addresses
    '  return word[noparse][[/noparse] @endOfObjects-4 ]
    ' elseif index==0
    '  return word[noparse][[/noparse] @endOfObjects-8 ]
    'or if you want the absolute address and not the offset
     if index==1
      return @firstVariable+word[noparse][[/noparse] @endOfObjects-4 ]
     elseif index==0
      return @firstVariable+word[noparse][[/noparse] @endOfObjects-8]
    
    
    'in obj2
    OBJ
     d:"dummyObject" 'object that has the same methods as the one you want to call but has all the other code removed
    DAT
     endOfObject long 0 'same as above
    
    PUB passObjectHandle(objectOffset,varOffset)
     word[noparse][[/noparse]@endOfObject-4]:=objectOffset
     word[noparse][[/noparse]@endOfObject-2]:=varOffset
    
    'now you should be able to do something like this and it will (should [img]http://forums.parallax.com/images/smilies/smile.gif[/img] )call obj1
    PUB run
     d.doSomething
     
    'whatever other methods you want
    
    



    Hope this helps

    Steven
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-01-12 04:12
    Steven,
    Thanks for that template.
    I put it all to work and attached some demo code.
    Unfortunately it does not work.
    If you care to take a look, much appreciated.
    I have put in comments to make clear what should happen.

    regards peter
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-01-12 09:55
    Steven,
    I got it working now. Turns out to be simpler than your template suggested.
    I can now use the method names and all indexes are local to files, and
    determined by the order of declared objects under OBJ.
    This means·even if a target object changes (eg. obj2), that does not effect the·calling (eg.·main)

    regards peter
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-01-12 10:26
    @Peter I'm just looking at your code now.

    Here is a new version of DOL. The next one will try to load from an SD card (I've still got to get one and make a reader for it. smile.gif ). Updates for V_009 are
    - fixed up the aborts some though there is still more to do
    - added some calls so that loaded objects can dynamically allocate memory from the heap

    Steven

    And I even rembered to include the file this time smile.gif
Sign In or Register to comment.