NXC - addressOf and pointers

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
tabbycatrobots
Posts: 100
Joined: 27 Dec 2010, 19:10

NXC - addressOf and pointers

Post by tabbycatrobots »

I suspect my question may have been covered but doing a search, I didn't find an answer. I'd like to pass to
a function, an address of a variable, and have the function set the variable to a value. I think I understand
that NXC doesn't support pointers, but I did find addressOf(), which allows me to pass in the address. And
there is the function memcpy (variant dest, variant src, byte num). But, the help says that byte num, i.e.
the number of bytes copied, is ignored. This sounds like it could corrupt memory that I do not want changed.
Also, are dest and src, memory addresses, or variable names? The wording in the help left me suspect. In NXC,
is there a function like memcpy(), where the number of bytes to be copied, can be specified. Thanks for any
help. And if I missed an existing discussion about this, I apologize for taking people's time.
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: NXC - addressOf and pointers

Post by HaWe »

in NXC you can't pass a pointer or an address of a variable to a function or a procedure, but you may pass the variable (sort of) "by reference":

void myFunction (int & myVar) {...}

notice the "&" between variable type and variable name.
It's use is like in C++.
pepijndevos
Posts: 175
Joined: 28 Dec 2011, 13:07
Location: Gelderland, Netherlands
Contact:

Re: NXC - addressOf and pointers

Post by pepijndevos »

So can you pass around "function pointers"? That is, jump to a label or subroutine "by reference".

Something like

Code: Select all

foo:
mov bar foo
jmp bar
Or

Code: Select all

subroutine foo
   add someresult 1 1
   return
ends

mov bar foo
call bar
-- Pepijn
http://studl.es Mindstorms Building Instructions
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: NXC - addressOf and pointers

Post by HaWe »

short answer: no.
long answer: no, because no stack, no heap, no pointers, no pointer functionality on the clump, just faked "reference" functions (actually only temporary copies to global variables).
Although muntoo tried a little around faking pointers by arrays, but IIRC it was only tinkering^^ (no offense, muntoo ;) )
Last edited by HaWe on 10 Jan 2012, 13:52, edited 3 times in total.
pepijndevos
Posts: 175
Joined: 28 Dec 2011, 13:07
Location: Gelderland, Netherlands
Contact:

Re: NXC - addressOf and pointers

Post by pepijndevos »

So then, how are "reference functions" compiled to NBC?
-- Pepijn
http://studl.es Mindstorms Building Instructions
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: NXC - addressOf and pointers

Post by HaWe »

(actually only temporary copies to global variables)
(I wasn't already finished writing my post)
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: NXC - addressOf and pointers

Post by afanofosc »

As was mentioned, code and data are very distinct. Each variable is defined in the dataspace. Each chunk of code is defined in the codespace. Never the twain shall cross. All subroutine calls involve compile-time CLUMP_ID references. A subroutine call requires both a CLUMP_ID (resolved at compile time) and a return address variable specific to the called subroutine. To return from a subroutine you need to use the called subroutine's return address variable.

Code: Select all

subcall FOO, FOO_RET

Code: Select all

subroutine FOO
  subret FOO_RET
ends
The value of FOO is calculated by the compiler. It is not a dataspace variable that you can use with opcodes that manipulate dataspace variables. The value stored at run-time by the firmware VM in FOO_RET is where code execution returns to when the subret opcode is executed in FOO.

As Doc kindly described, NXC fakes reference types by coping by value into a function and then copying by value back out of the function (unless the reference type is const, in which case it skips the copy out). Which also means unlike real references, if you modify the value inside a function and some other routine is executing simultaneously and it reads the value of the variable passed "by reference" into the other routine it will have its old value until the function call returns.

The ability to grab the address (relative) of a variable does allow you to directly modify that variable using IOMapWrite functionality. You would write to the Command module IOMap with an offset that points into the memory buffer where all variables are stored. I think I posted a bit of code that showed how you can do that. But none of the opcodes work with that ability so everything would be really slow and ugly. I have actually started a bit of enhanced firmware code changes that would add support for pointers but it is not ready for prime time. If someone wants to experiment with this at the NBC level it might be possible to do some things and I would be happy to work with such a person outside these forums.

http://bricxcc.sourceforge.net/nbc/nxcd ... le.html#a0

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
tabbycatrobots
Posts: 100
Joined: 27 Dec 2010, 19:10

Re: NXC - addressOf and pointers

Post by tabbycatrobots »

Thank you for the ideas. I'm experimenting with your suggestions using
<
reladdressOf() (I'm using in main to get an address to pass to a function and in the called function)
and
SysIOMapReadByID(read_args); (I'm using in the called function)
SysIOMapWriteByID(write_args); (I'm using in the called function)
>
from ideas shown in ex_reladdressof.nxc. I'm using similar code except have reladdressOf(unsigned long)
rather than reladdressOf(char array). It seems to meet my needs. I'll post my code to this topic after I test
it some more. It may help someone else.
Thanks again.
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: NXC - addressOf and pointers

Post by afanofosc »

You might also want to look at FlattenVar and UnflattenVar along with ByteArrayToStr and StrToByteArray. These functions will help you convert back and forth between an array of bytes and a variable.

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
tabbycatrobots
Posts: 100
Joined: 27 Dec 2010, 19:10

Re: NXC - addressOf and pointers

Post by tabbycatrobots »

Here is my test code. I'm able to pass the addresses of 2 unsigned long variables to a function, that then sets
their values and returns. I display the variable values before and after the function call, and it looks okay.
Next step is to add the code to my dancing robots doing BT comm, and that will be the real test.

Code: Select all

//  File iomap_write_08.nxc
//  Created by: hdrake, TabbyCat Robots      2012Jan12


//  The called function., that writes into addresses passed to it.
bool bt_receive_remote_number(unsigned char queue_id,
                              bool clear_queue,
                              unsigned long msg_addr,
                              unsigned long msg_type_addr);


task main()
{
//  A design decision is that all variables that are passed to a called function,
//  by their address, will be unsigned long.  Two reasons for this.  1.  To
//  reduce the amount of my testing.  2.  To reduces chances of making a mistake
//  through brainfade, e.g.  read_args.Count will always be equal = 4.
//  After control is passed back to the calling function, then the value may be
//  assigned to a different variable type.

//  The following 2 variables are used for passing back values from the called
//  function.
    unsigned long    received_msg;
    unsigned long    received_type;

//  The following 2 addresses are used for the passing.
    unsigned long    received_msg_addr;
    unsigned long    received_type_addr;

//  The following 3 addresses are used for my testing only, not needed in final
//  code.
    unsigned long    msg_data_addr;
    unsigned long    data_type_addr;
    unsigned long    xger_id_addr;

    unsigned short   msg_data;
    unsigned char    data_type;
    unsigned char    xger_id;

    bool bt_receive_status;

    xger_id = 5;

//  For testing assign an arbitrary values to variables, that will be assigned
//  values in the called function.
    received_msg = 0;
    received_type = 0;

//  For testing, assign initial values to variables, that be assigned returned
//  values.
    msg_data = 0x7fed;
    data_type = 31;

    xger_id_addr = reladdressOf(xger_id);
    received_msg_addr = reladdressOf(received_msg);
    received_type_addr = reladdressOf(received_type);
    msg_data_addr = reladdressOf(msg_data);
    data_type_addr = reladdressOf(data_type);

//  Display the starting values.
    TextOut(0,  LCD_LINE1, FormatNum("%x", xger_id_addr));
    TextOut(15, LCD_LINE1, FormatNum("%x", xger_id));
    TextOut(30, LCD_LINE1, FormatNum("%x", received_msg_addr));
    TextOut(45, LCD_LINE1, FormatNum("%x", received_msg));
    TextOut(60, LCD_LINE1, FormatNum("%x", received_type_addr));
    TextOut(75, LCD_LINE1, FormatNum("%x", received_type));

    TextOut( 0, LCD_LINE2, FormatNum("%x", msg_data_addr));
    TextOut(20, LCD_LINE2, FormatNum("%x", msg_data));
    TextOut(48, LCD_LINE2, FormatNum("%x", data_type_addr));
    TextOut(68, LCD_LINE2, FormatNum("%x", data_type));

//  Call a function that will assign values to variables, local to this, the
//  calling function.
    bt_receive_status =  bt_receive_remote_number(xger_id,
                                                  TRUE,
                                                  received_msg_addr,
                                                  received_type_addr);

//  Assign the returned values to variables of different data types.
    msg_data = received_msg;
    data_type = received_type;

//  As part of testing, see if any addresses change.
    received_msg_addr = reladdressOf(received_msg);
    msg_data_addr = reladdressOf(msg_data);
    data_type_addr = reladdressOf(data_type);

//  Display values after the function call, and subsequent assignments.
    TextOut( 0, LCD_LINE6, FormatNum("%x", received_msg_addr));
    TextOut(15, LCD_LINE6, FormatNum("%x", received_msg));

    TextOut( 0, LCD_LINE7, FormatNum("%x", msg_data_addr));
    TextOut(20, LCD_LINE7, FormatNum("%x", msg_data));
    TextOut(48, LCD_LINE7, FormatNum("%x", data_type_addr));
    TextOut(68, LCD_LINE7, FormatNum("%x", data_type));

    until(ButtonPressed(BTNCENTER, TRUE));

}  //  End of  -  task main()


bool bt_receive_remote_number(unsigned char queue_id,
                              bool clear_queue,
                              unsigned long msg_addr,
                              unsigned long msg_type_addr)
{
    bool            bt_op_succeeds;

//  Again, the variable involved in the transfer are unsigned long.  See note
//  in main.
    unsigned long   received_data;
    unsigned long   received_data_type;

    unsigned long   received_data_addr;
    unsigned long   received_data_type_addr;

    IOMapReadByIDType    read_args;
    IOMapWriteByIDType   write_args;

//  In the real code the folowing 3 values would be from a BT operation.
    received_data = 0xba7e;
    received_data_type = 2;
    bt_op_succeeds = TRUE;

    received_data_addr = reladdressOf(received_data);
    received_data_type_addr = reladdressOf(received_data_type);
    
    TextOut( 0, LCD_LINE4, FormatNum("%x", received_data_addr));
    TextOut(20, LCD_LINE4, FormatNum("%x", received_data));
    TextOut(48, LCD_LINE4, FormatNum("%x", received_data_type_addr));
    TextOut(68, LCD_LINE4, FormatNum("%x", received_data_type));

//  Read a value, to be transferred, into the SysIOMap buffer.
    read_args.ModuleID = CommandModuleID;
    read_args.Offset   = CommandOffsetMemoryPool + received_data_addr;
    read_args.Count    = 4;
    SysIOMapReadByID(read_args);

//  Write the value back to the address passed in.  Some magic has occurred,
//  that I don't understand, such that the byte count to be written doesn't
//  need to be specified.  (Did the Count from the read get stored somewhere?)
    write_args.ModuleID = CommandModuleID;
    write_args.Offset   = CommandOffsetMemoryPool + msg_addr;
    write_args.Buffer   = read_args.Buffer;
    SysIOMapWriteByID(write_args);

    read_args.ModuleID = CommandModuleID;
    read_args.Offset   = CommandOffsetMemoryPool + received_data_type_addr;
    read_args.Count    = 4;
    SysIOMapReadByID(read_args);

    write_args.ModuleID = CommandModuleID;
    write_args.Offset   = CommandOffsetMemoryPool + msg_type_addr;
    write_args.Buffer   = read_args.Buffer;
    SysIOMapWriteByID(write_args);

    return bt_op_succeeds;

}  //  End of  -  bt_receive_remote_number()

//  End of  -  File iomap_write_08.nxc

Post Reply

Who is online

Users browsing this forum: Semrush [Bot] and 5 guests