Shop OBEX P1 Docs P2 Docs Learn Events
byte arrays / strings passing to objects confused — Parallax Forums

byte arrays / strings passing to objects confused

Chip CoxChip Cox Posts: 73
edited 2010-02-21 01:26 in Propeller 1
I always hated type casting in C and now it's here too.· From what I understand when arguements are passed to objects subroutines, they are passed as longs and long arrays.· Furthermore, the variables for the function are all longs and long arrays too.· Even though the calling function may be using bytes and byte arrays and be expecting bytes and byte arrays.· In addition, there seem to be a variety ( at least 2 ) different ways of type casting back to byte.· My question is this, when do I have to cast my variables back to bytes and which form of casting do I use???· Also, it seems the way strings are passed back from functions is through a pointer to a global variable in the object.· What happens if multiple cogs call the same object at the same time.· Also, what happens if you try to nest calls to functions that all pass back strings?· For example· resstr:=str.strpad(str.i2a(buff[noparse][[/noparse]i]),2,"0",LEFT)· Doesn't the return variable get trounced????· I'm getting a headache <grin>.· lol.giffreaked.gifconfused.gif

Comments

  • Dave HeinDave Hein Posts: 6,347
    edited 2010-02-19 03:59
    Actually Spin doesn't use types for the most part.· There is some inconsistency in Spin where variables defined in a VAR section are treated as byte, word or long values, and only longs can be defined within a function.· I don't recall exactly how DAT variables are treated.· Type casting in C can be a pain, but it enforces a structure that prevents common programming mistakes.

    Cogs cannot call functions in another cog (please correct me if I'm wrong about this).· Communication between cogs is done using hub variables.· A cog can cause a function to be performed in another cog by setting a setting a parameter in cog memory.· However, the other cog must examine that variable in a polling loop and execute the proper function based on the value of the parameter.

    I don't understand your example about processing a string.· Can you include a short program that demonstrates the problem?

    Dave
  • Mike GreenMike Green Posts: 23,101
    edited 2010-02-19 04:01
    First of all, there's no casting in Spin. There are only byte addresses that can also be word aligned (divisible by 2) and long aligned (divisible by 4). You can use BYTE[noparse][[/noparse] ] to fetch or store a byte value to any address, WORD[noparse][[/noparse] ] to fetch or store a word value to any address divisible by 2, and LONG[noparse][[/noparse] ] to fetch or store a long value to any address divisible by 4. Strings are simply arrays of bytes, usually terminated by a zero byte. As you have noticed, the address of the string is what's passed around. There are several objects that work by passing back the address of a global variable in the object. You have to be careful about moving the contents of that variable somewhere else if there's going to be another call to the object. Also as you've noticed, if more than one cog uses that object, there has to be something done to prevent one cog from using the variable while another cog expects something to be there in the variable. Usually this involves the use of a lock or each cog could use a different instance of the object (and thus a different variable). Each instance has its own variable, but there's only one copy of the code.
  • Chip CoxChip Cox Posts: 73
    edited 2010-02-19 12:01
    Sorry for being so dense on this. I have not had this much trouble with character strings since my first c class 20+ years ago. All I'm trying to do is take the result from a function and store it in a local variable. Take the following code snipit.

    inbuff[noparse][[/noparse]0]:=9
    inbuff[noparse][[/noparse]1]:=8
    inbuff[noparse][[/noparse]2]:=3
    hld:=str.numbertodecimal(inbuff[noparse][[/noparse]0],2)
    hld[noparse][[/noparse]2]:=str.numbertodecimal(inbuff[noparse][[/noparse]1],2)
    hld[noparse][[/noparse]4]:=0
    debug.str(hld)

    numbertodecimal is in stringEngine from the object exchange. It converts a number to a decimal representation of that number in a string the length of the last arguement ( 2 in this case ), zero padded. Lets just say the results I am getting are varied. In this example debug.str(hld) comes out as 08 08.· Yes there is a space between them ( why )??· To my increasingly feeble mind, I think it has something to do with long vs byte alignment, but I'm not sure anymore.
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-02-19 14:00
    Please post the whole program.· I have no idea how you declared your variables.· I also can't find stringEngine in the object exchange.· What name is is listed under?

    Dave
  • MagIO2MagIO2 Posts: 2,243
    edited 2010-02-19 14:01
    What about having a look into the object that you use?

    VAR
      ...
      byte decimalCharacters[noparse][[/noparse]12]
      ...
    
    PUB numberToDecimal(number, length) '' 5 Stack Longs
      ....
      return @decimalCharacters
    
    

    In SPIN you can ONLY return one long from SPIN functions. Nothing else ... no byte, no word, no array, no string ....
    So, if you need to return more than that, you have to return a pointer to the memory where the result can be found. And that's what numberToDecimal is doing. It prepares the string in it's own buffer and returns the address to that. If you call numberToDecimal·a second time, it will overwrite the previous result.

    So, what you have to do in your code is:
    call numberToDecimal, copy the result to your hld-array @hld[noparse][[/noparse] 0 ], for example using the bytemove instruction.
    call numberToDecimal again and copy the result to hld[noparse][[/noparse] 2 ].

    ·
  • Chip CoxChip Cox Posts: 73
    edited 2010-02-20 04:54
    Ok, I'm still not getting it. Here is my code in the form that is closest to working.

    CON

    _clkmode = xtal1 + pll16x ' use crystal x 16
    _xinfreq = 5_000_000

    OBJ
    Str: "StringEngine"
    Debug: "Extended_FDSerial"

    var
    long stack[noparse][[/noparse]1024]
    byte cog

    PUB main | curtim[noparse][[/noparse]8], hld, inbuff[noparse][[/noparse]40]

    Debug.start(31, 30, 0, 115200)

    waitcnt((clkfreq*5)+cnt)
    inbuff[noparse][[/noparse]0]:= 9 'GPS.rx 'time(1000) ' using hard coded values to test with
    inbuff:= 8 'GPS.rx 'time(1000) ' using hard coded values to test with
    inbuff:= 4 'GPS.rx 'time(1000) ' using hard coded values to test with
    debug.dec(inbuff[noparse][[/noparse]0]) ' prints 9 just like it should
    debug.dec(inbuff) ' prints 8 just like it should
    debug.dec(inbuff) ' prints 4 just like it should
    debug.str(string(13))

    hld:=str.numbertodecimal(inbuff[noparse][[/noparse]0],2) ' I think this puts the address of the result from numbertodecimal in hld
    debug.str(hld) ' this prints 09 like it should
    bytemove(@curtim[noparse][[/noparse]0],@hld,2) ' I think this should move the first 2 bytes of whatever hld points to into the
    ' first 2 bytes of curtim.
    hld:=str.numbertodecimal(inbuff,2)
    debug.str(hld) ' this prints out 08 like it should
    bytemove(@curtim,@hld,2)
    hld:=str.numbertodecimal(inbuff,2)
    debug.str(hld) ' this prints out 04 like it should
    bytemove(@curtim,@hld,2)
    curtim[noparse][[/noparse]6]:=0 ' make sure we have a zero terminated string

    debug.str(string(13))
    debug.str(string("strtime="))
    debug.dec(strsize(curtim[noparse][[/noparse]0])) ' prints out 3
    debug.str(curtim[noparse][[/noparse]0]) ' prints out 04

    interestingly also, there is a space between the 3 and the 04 ( 3 04 ). Why???
  • Chip CoxChip Cox Posts: 73
    edited 2010-02-20 04:55
    sorry about the strange formatting. I have no idea why it does that sometimes...
  • Mike GreenMike Green Posts: 23,101
    edited 2010-02-20 04:59
    The formatting is strange because the forum software interprets things in square brackets ([noparse][[/noparse] ]) as formatting information. It helps to surround your code with [noparse][[/noparse] code ] and [noparse][[/noparse] /code ] brackets (without the extra spaces). It also helps to attach your source file to a message using the Attachment Manager that you find when you click on the Post Reply button.
  • MagIO2MagIO2 Posts: 2,243
    edited 2010-02-20 11:47
    hld:=str.numbertodecimal(inbuff[noparse][[/noparse]0],2) ' I think this puts the address of the result from numbertodecimal in hld
    debug.str(hld) ' this prints 09 like it should
    bytemove(@curtim[noparse][[/noparse]0],@[b][color=red]hld[/color][/b],2) ' I think this should move the first 2 bytes of whatever hld points to into the 
    ' first 2 bytes of curtim.
    hld:=str.numbertodecimal(inbuff,2)
    debug.str(hld) ' this prints out 08 like it should
    bytemove(@curtim,@[b][color=orange]hld[/color][/b],2)
    hld:=str.numbertodecimal(inbuff,2)
    debug.str(hld) ' this prints out 04 like it should
    bytemove(@curtim[b][color=red],@hld[/color][/b],2)
    
    

    hld already contains the address, so you should use hld only - without @.
    bytemove with @hld will copy the address stored in hld to curtim and not the content at this address.
  • MagIO2MagIO2 Posts: 2,243
    edited 2010-02-20 12:08
    By the way ... you're wasting a lot of RAM.

    You define curtim and inbuff in the function. This will use a long for each array-element instead of a byte which is what you need. AND it eats up stack-space. So, whenever your stack gets to small, you will have lot's of trouble and best thing ... you'll never guess where the trouble comes from.
    Put these variables into VAR-section. Then the compiler can warn you if you run out of RAM. And use byte arrays.

    Oh ... now that I mention it ... this is also a bug in your program. bytemove does not care about the datatypes. So it will copy a byte array (the buffer returned by numberToDecimal) into a long array ... depending on what you do with the curtim array that might cause other problems!
  • Chip CoxChip Cox Posts: 73
    edited 2010-02-20 12:54
    Ok, I figured out the problem numbertodecimal was returning 3 bytes with a leading space. So since all my values were single digits and numbertodecimal was zero padding them to 2 characters, with a blank on the front, I kept getting 0's for answers and these strange spaces embedded in the result.
    Thanks for your help..
  • Chip CoxChip Cox Posts: 73
    edited 2010-02-20 13:00
    Sorry I just read your earlier post. I went with the local variables because if I have more than one copy of this running at the same time, I don't want them stepping on each other back in the global areas. And yes, the arrays are way to big. This was just a test program to show the problem I was having. Now that I have it working, I can start to clean up behind myself some. And yes, the long/byte issue has been driving me nuts. The way I am getting around it in the version that works is that instead of treating curtim as an array and using array references to get to it, I'm treating it as a contiguous block of memory and adding offsets to it to position my pointer where I want it.
    Thanks Again.
  • MagIO2MagIO2 Posts: 2,243
    edited 2010-02-20 14:32
    Maybe you can post the result and we can discuss on that ... even if you got it running it's not said that it can't be improved.

    As I said ... if you use local variable you will somewhen step into unpredictable problems where you simply don't know where these come from.

    If you put the buffers into VAR sections and create an array of objects, you can have several instances running the same code, not stepping into each other as well. The good thing is that the compiler can tell you if you run into RAM problems. The stack-version will never complain and simply overwrite what ever is in it's way.
  • Chip CoxChip Cox Posts: 73
    edited 2010-02-20 20:52
    See that's what bothers me. In my experience local variables are good except for things that are truely global like the clock frequence, or the maximum number of seconds in an hour or something like that. Otherwise local variables are better because you don't run the risk of some other routine called somewhere that uses the same variable name as you do trouncing on your values and giving you major headaches. Just imagine if I had a generic counter named "i" that I made a global. I could have any number of routines in counters using I and really screwing up my loops <grin>. Trust me I've done that before. I agree with the propellers you are playing a game trying to balance code reusability with space. It's a fine line. The thing that buggs me most though is that the local variables to a function are all long alligned. It causes problems when you store things in arrays like strings that are going to be read by other functions that expect them to be byte alligned. instr in a long aligned array is not the same place as instr is in a byte alligned array. Drove me nutz for the past 3 days.
  • MagIO2MagIO2 Posts: 2,243
    edited 2010-02-20 22:35
    SPIN is object-based. All functions and variables that belong together should be put together into one object. The only thing that can be seen outside of an object are the public functions. If you have a buffer called "buffer" in one object, you still can have variables named buffer in other objects. There is no problem - no interference. All variable names and dat-labels are LOCAL inside of an object!

    A functions local variables are good for local counters and 'working' variables or 'helper' variables that are only needed inside of one function.

    Ok ... let's talk about the code you posted before:
    long stack[noparse][[/noparse]1024]
    
    

    I don't see any COGNEW, so why do you need a stack variable? The first COG will use the whole free memory as stack-space ... no need to initialize a stack for that one, it's only needed for all additional COGs you start.

    PUB main | curtim[noparse][[/noparse]8], hld, inbuff[noparse][[/noparse]40]
    
    

    hld is fine for me, because it's a temporary variable you use inside of main. But the curtim and inbuff should not be defined as local variable. Put em into the var section and use byte as type. If you have additional functions that need independent buffers then use different·buffer-names.

    Again ... I think having the compiler checking the RAM-usage is worth to get familiar with other concepts of variable usage. When you start overwriting memory because the stack is to small to hold all the local variables then you will need another 3 days ... or more .. what can go wrong in this case is simply unpredictable up to being destructive.


    ·
  • BradCBradC Posts: 2,601
    edited 2010-02-21 01:26
    Chip Cox said...
    The thing that buggs me most though is that the local variables to a function are all long alligned. It causes problems when you store things in arrays like strings that are going to be read by other functions that expect them to be byte alligned.

    Why would it do that? If you are accessing memory in byte sized quantities alignment is a non-issue. It's only word and long you need to worry about your alignment.

    An array of longs is just a pool of bytes, use it however you like.
    PUB Fred | data[noparse][[/noparse]25]
    
    



    This simply allocates a pool of ram. It could be long[noparse][[/noparse]25] / word[noparse][[/noparse]50] or byte[noparse][[/noparse]100].

    Use it as byte[noparse][[/noparse]@data][noparse][[/noparse]index] to get access to it as a byte array.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    You only ever need two tools in life. If it moves and it shouldn't use Duct Tape. If it does not move and it should use WD40.
Sign In or Register to comment.