routine or constant name search

8.44 Machine Level Access

8.44.0.1 Marchine Level Access Summary

Warning: Some of these routines require a knowledge of machine-level programming. You could crash your system!

These routines, along with peek, poke and call, let you access all of the features of your computer. You can read and write to any allowed memory location, and you can create and execute machine code subroutines.

If you are manipulating 32-bit addresses or values, remember to use variables declared as atom. The integer type only goes up to 31 bits.

If you choose to call machine_proc or machine_func directly (to save a bit of overhead) you *must* pass valid arguments or Euphoria could crash.

Some example programs to look at:
  • demo/callmach.ex -- calling a machine language routine

8.44.0.2 peek_longs

include std/machine.e
namespace machine
public function peek_longs(object x)

@nodoc

8.44.0.3 MAP_ANONYMOUS

include std/machine.e
namespace machine
export constant MAP_ANONYMOUS

8.44.0.4 MAP_FAILED

include std/machine.e
namespace machine
export constant MAP_FAILED

8.44.1 Safe Mode

8.44.1.1 Safe Mode Summary

During the development of your application, you can define the word SAFE to cause machine.e to use alternative memory functions. These functions are slower but help in the debugging stages. In general, SAFE mode should not be enabled during production phases but only for development phases.

To define the word SAFE run your application with the -D SAFE command line option, or add to the top of your main file:

with define safe

before the first appearance of include std/machine.e

The implementation of the Machine Level Access routines used are controled with the define word SAFE. The use of SAFE switches the routines included here to use debugging versions which will allow you to catch all kinds of bugs that might otherwise may not always crash your program where in the line your program is written. There may be bugs that are invisible until you port the program they are in to another platform. There has been no bench marking for how much of a speed penalty there is using SAFE.

You can take advantage of SAFE debugging by:

  • If necessary, call register_block(address, length, memory_protection) to add additional "external" blocks of memory to the safe_address_list. These are blocks of memory that are safe to use but which you did not acquire through Euphoria's allocate, allocate_data, allocate_code or allocate_protect, allocate_string, allocate_wstring. Call unregister_block(address) when you want to prevent further access to an external block. When SAFE is not enabled these functions will do nothing and will be converted into nothing by the inline code in the front-end.
  • You will be notified if memory that you haven't allocated is accessed, or if memory is freed twice, or if memory is used in the wrong way. Your application will can be ready for D.E.P. enabled systems even if the system you test on doesn't have D.E.P..
  • If a bug is caught, you will hear some "beep" sounds. Press Enter to clear the screen and see the error message. There will be a descriptive crash message and a traceback in ex.err so you can find the statement that is making the illegal memory access.

8.44.1.2 check_calls

Define block checking policy.

include std/machine.e
public integer check_calls
Comments:

If this integer is 1, (the default), check all blocks for edge corruption after each Executable Memory, call, c_proc or c_func. To save time, your program can turn off this checking by setting check_calls to 0.

8.44.1.3 edges_only

include std/machine.e
public integer edges_only

Determine whether to flag accesses to remote memory areas.

Comments:

If this integer is 1 (the default under Windows), only check for references to the leader or trailer areas just outside each registered block, and don't complain about addresses that are far out of bounds (it's probably a legitimate block from another source)

For a stronger check, set this to 0 if your program will never read/write an unregistered block of memory.

On Windows people often use unregistered blocks. Please do not be one of them.

8.44.1.4 check_all_blocks

include std/machine.e
check_all_blocks()

Scans the list of registered blocks for any corruption.

Comments:

safe.e maintains a list of acquired memory blocks. Those gained through allocate are automatically included. Any other block, for debugging purposes, must be registered by register_block and unregistered by unregister_block.

The list is scanned and, if any block shows signs of corruption, it is displayed on the screen and the program terminates. Otherwise, nothing happens.

Unless SAFE is defined, this routine does nothing. It is there to make switching between debugged and normal version of your program easier.

See Also:

register_block, unregister_block

8.44.1.5 register_block

include std/machine.e
procedure register_block(machine_addr block_addr, positive_int block_len, 
            valid_memory_protection_constant memory_protection = PAGE_READ_WRITE )

Adds a block of memory to the list of safe blocks maintained by safe.e (the debug version of memory.e). The block starts at address a. The length of the block is i bytes.

Parameters:
  1. block_addr : an atom, the start address of the block
  2. block_len : an integer, the size of the block.
  3. protection : a constant integer, of the memory protection constants found in machine.e, that describes what access we have to the memory.
Comments:

In memory.e, this procedure does nothing. It is there to simplify switching between the normal and debug version of the library.

This routine is only meant to be used for debugging purposes. safe.e tracks the blocks of memory that your program is allowed to peek, poke, mem_copy etc. These are normally just the blocks that you have allocated using Euphoria's allocate routine, and which you have not yet freed using Euphoria's free. In some cases, you may acquire additional, external, blocks of memory, perhaps as a result of calling a C routine.

If you are debugging your program using safe.e, you must register these external blocks of memory or safe.e will prevent you from accessing them. When you are finished using an external block you can unregister it using unregister_block.

Example 1:
atom addr

addr = c_func(x, {})
register_block(addr, 5)
poke(addr, "ABCDE")
unregister_block(addr)
See Also:

unregister_block, Safe Mode

8.44.1.6 unregister_block

include std/machine.e
public procedure unregister_block(machine_addr block_addr)

removes a block of memory from the list of safe blocks maintained by safe.e (the debug version of memory.e).

Parameters:
  1. block_addr : an atom, the start address of the block
Comments:

In memory.e, this procedure does nothing. It is there to simplify switching between the normal and debug version of the library.

This routine is only meant to be used for debugging purposes. Use it to unregister blocks of memory that you have previously registered using register_block. By unregistering a block, you remove it from the list of safe blocks maintained by safe.e. This prevents your program from performing any further reads or writes of memory within the block.

See register_block for further comments and an example.

See Also:

register_block, Safe Mode

8.44.2 Data Execute Mode and Data Execute Protection

Data Execute Mode makes data that will be returned from allocate executable. On some systems you will not be allowed to run code in memory returned from allocate unless this mode has been enabled. This restriction is called Data Execute Protection or D.E.P.. When writing software you should use allocate_code or allocate_protect to get memory for execution. This is more efficient and more secure than using Data Execute mode. Because many hacker exploits of software use data buffers and then trick software into running this data, Data Execute Protection stops an entire class of exploits.

If you get a Data Execute Protection Exception from running software, it means that D.E.P. could have thwarted an attack! Your application crashes and your computer wasn't infected. However, many people will decide that they want to disable D.E.P. because they know that they call memory returned by allocate or perhaps they are simply careless.

8.44.3 Type Sorted Function List

8.44.3.1 Executable Memory

Executable Memory is the way to run code on the stack in a completly portable way.

Use the following Routines:

Use allocate_code to allocate some executable machine-code, call to call the code, and free_code to free the machine-code.

8.44.3.2 Using Data Bytes

In C, bytes are called 'char' or 'BOOL' or 'boolean'. They sometimes are used for very small numbers but mostly, they are used in C-strings. See Using Strings.

Use allocate_data to allocate data and return an address. Use poke to save atoms or sequences to at an address. Use peeks or peek to read from an address. Use mem_set and mem_copy to set and copy sections of memory. Use free to free or use delete if you enabled cleanup in allocate_data.

8.44.3.3 Using Data Words

Words are 16-bit integers and are big enough to hold most integers in common use as far as whole numbers go. So they often are used to hold numbers. In C, they are declared as WORD or short.

Use allocate_data to allocate data and return its address. Use poke2 to write to the data at an address. Use peek2 or peek2s to read from an address. Use free to free or use delete if you enabled cleanup in allocate_data.

8.44.3.4 Using Data Double Words

Double words are 32-bit integers. In C, they are typically declared as int, or long (on Windows and other 32-bit architectures), or DWORD. They are big enough to hold pointers to other values in memory on 32-bit architectures.

Use allocate_data to allocate data and return its address. Use poke4 to write to the data at an address. Use peek4 or peek4s to read from an address. Use free to free or use delete if you enabled cleanup in allocate_data.

8.44.3.5 Using Data Quad Words

Quad words are 64-bit integers. In C, they are typically declared as long long int, or long int (on 64-bit architectures other than Windows). They are big enough to hold pointers to other values in memory on 64-bit architectures.

Be careful:

Recall that atoms in EUPHORIA (32 bit) have 15 digits of accuracy. Integral Values whose absolute value exceeds 253 might not be representable exactly.

Use allocate_data to allocate data and return its address. Use poke8 to write to the data at an address. Use peek8u or peek8s to read from an address. Use free to free or use delete if you enabled cleanup in allocate_data.

8.44.3.6 Using Pointers

A Euphoria atom should be used to store pointer values. On 32-bit architectures, pointers may be larger than a Euphoria integer. On 64-bit architectures, a Euphoria integer is large enough to hold pointer values, since current 64-bit architectures use only a 48-bit memory space

To portably peek and poke pointers, you should use peek_pointer and poke_pointer. These routines automatically detect the architecture and use the correct size for a pointer.

8.44.3.7 Using Long Integers

When interfacing with C code, some data will be defined as long or long int. This data type can be tricky to use in a portable manner, due to the way that different architectures and operating systems define it.

On all 32-bit architectures on which Euphoria runs, a long int is defined as 32-bits. On 64-bit Windows, a long int is also 32-bits. However, on other 64-bit operating systems, a long int is defined as 64-bits.

To portably peek and poke long int data, you should use peek_longs, peek_longu and poke_long. You can also use sizeof( C_LONG ) to determine the size (in bytes) of a native long int.

8.44.3.8 Using Strings

You can create legal ANSI and 16-bit UNICODE Strings with these routines. In C, strings are often declared as some pointer to a character: char * or wchar *.

Microsoft Windows uses 8-bit ANSI and 16-bit UNICODE in its routines.

Use allocate_string or allocate_wstring to allocate a string pointer. Use peek_string, peek_wstring, peek4, to read from memory byte strings, word strings and double word strings repsectively. Use poke, poke2, or poke4 to write to memory byte strings, word strings and double word strings. Use free to free or use delete if you enabled cleanup in allocate_data.

8.44.3.9 Using Pointer Arrays

Use allocate_string_pointer_array to allocate a string array from a sequence of strings. Use allocate_pointer_array to allocate and then write to an array for pointers . Use free_pointer_array to deallocate or use delete if you enabled cleanup in allocate_data.

8.44.4 Memory Allocation

8.44.4.1 allocate

include std/machine.e
namespace machine
public function allocate(memory :positive_int n, types :boolean cleanup = 0)

This does the same as allocate_data but allows the DATA_EXECUTE defined word to cause it to return executable memory.

See Also:

allocate_data, allocate_code, free

8.44.4.2 allocate_data

include std/machine.e
namespace machine
public function allocate_data(memory :positive_int n, types :boolean cleanup = 0)

Allocate a contiguous block of data memory.

Parameters:
  1. n : a positive integer, the size of the requested block.
  2. cleanup : an integer, if non-zero, then the returned pointer will be automatically freed when its reference count drops to zero, or when passed as a parameter to delete.
Returns:

An atom, the address of the allocated memory or 0 if the memory can't be allocated. NOTE you must use either an atom or object to receive the returned value as sometimes the returned memory address is too larger for an integer to hold.

Comments:
  • Since allocate acquires memory from the system, it is your responsiblity to return that memory when your application is done with it. There are two ways to do that - automatically or manually.
    • Automatically - If the cleanup parameter is non-zero, then the memory is returned when the variable that receives the address goes out of scope and is not referenced by anything else. Alternatively you can force it be released by calling the delete function.
    • Manually - If the cleanup parameter is zero, then you must call the free function at some point in your program to release the memory back to the system.
  • When your program terminates, the operating system will reclaim all memory that your applicaiton acquired anyway.
  • An address returned by this function shouldn't be passed to call. For that purpose you should use allocate_code instead.
  • The address returned will be at least 8-byte aligned.
Example 1:
buffer = allocate(100)
for i = 0 to 99 do
    poke(buffer+i, 0)
end for
See Also:

Using Data Bytes, Using Data Words, Using Data Double Words, Using Strings, allocate_code, free

8.44.4.3 allocate_pointer_array

include std/machine.e
namespace machine
public function allocate_pointer_array(sequence pointers, types :boolean cleanup = 0)

Allocate a NULL terminated pointer array.

Parameters:
  1. pointers : a sequence of pointers to add to the pointer array.
  2. cleanup : an integer, if non-zero, then the returned pointer will be automatically freed when its reference count drops to zero, or when passed as a parameter to delete
Comments:

This function adds the NULL terminator.

Example 1:
atom pa
pa = allocate_pointer_array({ allocate_string("1"), allocate_string("2") })
See Also:

Using Pointer Arrays, allocate_string_pointer_array, free_pointer_array

8.44.4.4 free_pointer_array

include std/machine.e
namespace machine
public procedure free_pointer_array(atom pointers_array)

Free a NULL terminated pointers array.

Parameters:
  1. pointers_array : memory address of where the NULL terminated array exists at.
Comments:

This is for NULL terminated lists, such as allocated by allocate_pointer_array. Do not call free_pointer_array for a pointer that was allocated to be cleaned up automatically. Instead, use delete.

See Also:

allocate_pointer_array, allocate_string_pointer_array

8.44.4.5 allocate_string_pointer_array

include std/machine.e
namespace machine
public function allocate_string_pointer_array(object string_list, types :boolean cleanup = 0)

Allocate a C-style null-terminated array of strings in memory

Parameters:
  1. string_list : sequence of strings to store in RAM.
  2. cleanup : an integer, if non-zero, then the returned pointer will be automatically freed when its reference count drops to zero, or when passed as a parameter to delete
Returns:

An atom, the address of the memory block where the string pointer array was stored.

Example 1:
atom p = allocate_string_pointer_array({ "One", "Two", "Three" })
-- Same as C: char *p = { "One", "Two", "Three", NULL };
See Also:

Using Pointer Arrays, free_pointer_array

8.44.4.6 allocate_wstring

include std/machine.e
namespace machine
public function allocate_wstring(sequence s, types :boolean cleanup = 0)

Create a C-style null-terminated wchar_t string in memory

Parameters:
  1. s : a unicode (utf16) string
Returns:

An atom, the address of the allocated string, or 0 on failure.

See Also:

Using Strings, allocate_string

8.44.5 Reading from Memory

8.44.5.1 peek

<built-in> function peek(object addr_n_length)

fetches a byte, or some bytes, from an address in memory.

Parameters:
  1. addr_n_length : an object, either of
    • an atom addr -- to fetch one byte at addr, or
    • a pair {addr,len} -- to fetch len bytes at addr
Returns:

An object, either an integer if the input was a single address, or a sequence of integers if a sequence was passed. In both cases, integers returned are bytes, in the range 0..255.

Errors:

Peeking in memory you don't own may be blocked by the OS, and cause a machine exception. If you use the define safe these routines will catch these problems with a EUPHORIA error.

When supplying a {address, count} sequence, the count must not be negative.

Comments:

Since addresses are 32-bit numbers on 32-bit architectures, they can be larger than the largest value of type integer (31-bits). Variables that hold an address should therefore be declared as atoms.

It is faster to read several bytes at once using the second form of peek than it is to read one byte at a time in a loop. The returned sequence has the length you asked for on input.

Remember that peek takes just one argument, which in the second form is actually a 2-element sequence.

Example 1:
-- The following are equivalent:
-- first way
s = {peek(100), peek(101), peek(102), peek(103)}

-- second way
s = peek({100, 4})
See Also:

Using Data Bytes, poke, peeks, peek4u, allocate, free, peek2u

8.44.5.2 peeks

<built-in> function peeks(object addr_n_length)

fetches a byte, or some bytes, from an address in memory.

Parameters:
  1. addr_n_length : an object, either of
    • an atom addr : to fetch one byte at addr, or
    • a pair {addr,len} : to fetch len bytes at addr
Returns:

An object, either an integer if the input was a single address, or a sequence of integers if a sequence was passed. In both cases, integers returned are bytes, in the range -128..127.

Errors:

Peeking in memory you do not own may be blocked by the OS, and cause a machine exception. If you use the define safe these routines will catch these problems with a Euphoria error.

When supplying a {address, count} sequence, the count must not be negative.

Comments:

Since addresses are 32-bit numbers on 32-bit architectures, they can be larger than the largest value of type integer (31-bits). Variables that hold an address should therefore be declared as atoms.

It is faster to read several bytes at once using the second form of peek than it is to read one byte at a time in a loop. The returned sequence has the length you asked for on input.

Remember that peeks takes just one argument, which in the second form is actually a 2-element sequence.

Example 1:
-- The following are equivalent:
-- first way
s = {peeks(100), peek(101), peek(102), peek(103)}

-- second way
s = peeks({100, 4})
See Also:

Using Data Bytes, poke, peek4s, allocate, free, peek2s, peek

8.44.5.3 peek2s

<built-in> function peek2s(object addr_n_length)

Fetches a signed word, or some signed words , from an address in memory.

Parameters:
  1. addr_n_length : an object, either of
    • an atom addr -- to fetch one word at addr, or
    • a pair { addr, len}, to fetch len words at addr
Returns:

An object, either an integer if the input was a single address, or a sequence of integers if a sequence was passed. In both cases, integers returned are double words, in the range -32768..32767.

Errors:

Peeking in memory you don't own may be blocked by the OS, and cause a machine exception. If you use the define safe these routines will catch these problems with a EUPHORIA error.

When supplying a {address, count} sequence, the count must not be negative.

Comments:

Since addresses are 32-bit numbers on 32-bit architectures, they can be larger than the largest value of type integer (31-bits). Variables that hold an address should therefore be declared as atoms.

It is faster to read several words at once using the second form of peek than it is to read one word at a time in a loop. The returned sequence has the length you asked for on input.

Remember that peek2s takes just one argument, which in the second form is actually a 2-element sequence.

The only difference between peek2s and peek2u is how words with the highest bit set are returned. peek2s assumes them to be negative, while peek2u just assumes them to be large and positive.

Example 1:
-- The following are equivalent:
-- first way
s = {peek2s(100), peek2s(102), peek2s(104), peek2s(106)}

-- second way
s = peek2s({100, 4})
See Also:

Using Data Words, poke2, peeks, peek4s, allocate, free peek2u

8.44.5.4 peek2u

<built-in> function peek2u(object addr_n_length)

fetches an unsigned word, or some unsigned words, from an address in memory.

Parameters:
  1. addr_n_length : an object, either of
    • an atom addr -- to fetch one double word at addr, or
    • a pair {addr,len} -- to fetch len double words at addr
Returns:

An object, either an integer if the input was a single address, or a sequence of integers if a sequence was passed. In both cases, integers returned are words, in the range 0..65535.

Errors:

Peeking in memory you do not own may be blocked by the OS, and cause a machine exception. If you use the define safe these routines will catch these problems with a Euphoria error.

When supplying a {address, count} sequence, the count must not be negative.

Comments:

Since addresses are 32-bit numbers on 32-bit architectures, they can be larger than the largest value of type integer (31-bits). Variables that hold an address should therefore be declared as atoms.

It is faster to read several words at once using the second form of peek than it is to read one word at a time in a loop. The returned sequence has the length you asked for on input.

Remember that peek2u takes just one argument, which in the second form is actually a 2-element sequence.

The only difference between peek2s and peek2u is how words with the highest bit set are returned. peek2s assumes them to be negative, while peek2u just assumes them to be large and positive.

Example 1:
-- The following are equivalent:
-- first way
Get 4 2-byte numbers starting address 100.
s = {peek2u(100), peek2u(102), peek2u(104), peek2u(106)}

-- second way
Get 4 2-byte numbers starting address 100.
s = peek2u({100, 4})
See Also:

Using Data Words, poke2, peek, peek2s, allocate, free peek4u

8.44.5.5 peek4s

<built-in> function peek4s(object addr_n_length)

fetches a signed double words, or some signed double words, from an address in memory.

Parameters:
  1. addr_n_length : an object, either of
    • an atom addr -- to fetch one double word at addr, or
    • a pair { addr, len } -- to fetch len double words at addr
Returns:

An object, either an atom if the input was a single address, or a sequence of atoms if a sequence was passed. In both cases, atoms returned are double words, in the range -(231)..231-1.

Errors:

Peeking in memory you don't own may be blocked by the OS, and cause a machine exception. If you use the define safe these routines will catch these problems with a Euphoria error.

When supplying a {address, count} sequence, the count must not be negative.

Comments:

On 32-bit machines, the information provided by peek8 will be an approximation due to the way numbers are stored. If you want to peek in a lossless way use peek4u or peek4s

Since addresses are 32-bit numbers on 32-bit architectures, they can be larger than the largest value of type integer (31-bits). Variables that hold an address should therefore be declared as atoms.

It is faster to read several double words at once using the second form of peek than it is to read one double word at a time in a loop. The returned sequence has the length you asked for on input.

Remember that peek4s takes just one argument, which in the second form is actually a 2-element sequence.

The only difference between peek4s and peek4u is how double words with the highest bit set are returned. peek4s assumes them to be negative, while peek4u just assumes them to be large and positive.

Example 1:
-- The following are equivalent:
-- first way
s = {peek4s(100), peek4s(104), peek4s(108), peek4s(112)}

-- second way
s = peek4s({100, 4})
See Also:

Using Data Double Words, poke4, peeks, peek4u, allocate, free, peek2s

8.44.5.6 peek8s

<built-in> function peek8s(object addr_n_length)

fetches a signed quad words, or some signed quad words, from an address in memory.

Parameters:
  1. addr_n_length : an object, either of
    • an atom addr -- to fetch one double word at addr, or
    • a pair { addr, len } -- to fetch len quad words at addr
Returns:

An object, either an atom if the input was a single address, or a sequence of atoms if a sequence was passed. In both cases, atoms returned are quad words, in the range -power(2,63)..power(2,63)-1.

Errors:

Peeking in memory you don't own may be blocked by the OS, and cause a machine exception. If you use the define safe these routines will catch these problems with a Euphoria error.

When supplying a {address, count} sequence, the count must not be negative.

Comments:

On 32-bit machines, the information provided by peek8s will be an approximation due to the limitations on atoms. If you want to peek in a lossless way use peek4u or peek4s

Since addresses are 32-bit numbers on 32-bit architectures, they can be larger than the largest value of type integer (31-bits). Variables that hold an address should therefore be declared as atoms.

It is faster to read several quad words at once using the second form of peek than it is to read one quad word at a time in a loop. The returned sequence has the length you asked for on input.

Remember that peek8s takes just one argument, which in the second form is actually a 2-element sequence.

The only difference between peek8s and peek8u is how quad words with the highest bit set are returned. peek4s assumes them to be negative, while peek4u just assumes them to be large and positive.

Example 1:
-- The following are equivalent:
-- first way
s = {peek8s(100), peek8s(108), peek8s(116), peek8s(124)}

-- second way
s = peek8s({100, 4})
See Also:

Using Data Double Words, poke4, peeks, peek4u, allocate, free, peek2s

8.44.5.7 peek4u

<built-in> function peek4u(object addr_n_length)

fetches an unsigned double word, or some unsigned double words, from an address in memory.

Parameters:
  1. addr_n_length : an object, either of
    • an atom addr -- to fetch one double word at addr, or
    • a pair {addr,len} -- to fetch len double words at addr
Returns:

An object, either an atom if the input was a single address, or a sequence of atoms if a sequence was passed. In both cases, atoms returned are double words, in the range 0..232-1.

Errors:

Peeking in memory you do not own may be blocked by the OS, and cause a machine exception. If you use the define safe these routines will catch these problems with a Euphoria error.

When supplying a {address, count} sequence, the count must not be negative.

Comments:

Since addresses are 32-bit numbers on 32-bit architectures, they can be larger than the largest value of type integer (31-bits). Variables that hold an address should therefore be declared as atoms.

It is faster to read several double words at once using the second form of peek than it is to read one double word at a time in a loop. The returned sequence has the length you asked for on input.

Remember that peek4u takes just one argument, which in the second form is actually a 2-element sequence.

The only difference between peek4s and peek4u is how double words with the highest bit set are returned. peek4s assumes them to be negative, while peek4u just assumes them to be large and positive.

Example 1:
-- The following are equivalent:
-- first way
s = {peek4u(100), peek4u(104), peek4u(108), peek4u(112)}

-- second way
s = peek4u({100, 4})
See Also:

Using Data Double Words, poke4, peek, peek4s, allocate, free, peek2u

8.44.5.8 peek8u

<built-in> function peek8u(object addr_n_length)

fetches an unsigned quad word, or some unsigned quad words, from an address in memory.

Parameters:
  1. addr_n_length : an object, either of
    • an atom addr -- to fetch one double word at addr, or
    • a pair {addr,len} -- to fetch len double words at addr
Returns:

An object, either an atom if the input was a single address, or a sequence of atoms if a sequence was passed. In both cases, atoms returned are quad words, in the range 0..power(2,64)-1.

Errors:

Peeking in memory you do not own may be blocked by the OS, and cause a machine exception. If you use the define safe these routines will catch these problems with a Euphoria error.

When supplying a {address, count} sequence, the count must not be negative.

Comments:

On 32-bit machines, the information provided by peek8u will be an approximation due to the limitations on atoms. If you want to peek in a lossless way use peek4u or peek4s

Since addresses are 32-bit numbers on 32-bit architectures, they can be larger than the largest value of type integer (31-bits). Variables that hold an address should therefore be declared as atoms.

It is faster to read several quad words at once using the second form of peek than it is to read one quad word at a time in a loop. The returned sequence has the length you asked for on input.

Remember that peek8u takes just one argument, which in the second form is actually a 2-element sequence.

The only difference between peek8s and peek8u is how quad words with the highest bit set are returned. peek8s assumes them to be negative, while peek8u just assumes them to be large and positive.

Example 1:
-- The following are equivalent:
--first way
s = {peek8u(100), peek8u(108), peek8u(116), peek8u(124)}

-- second way
s = peek8u({100, 4})
See Also:

Using Data Double Words, poke4, peek, peek4s, allocate, free, peek2u

8.44.5.9 peek_longu

<built-in> function peek_longu(object addr_n_length)

fetches an unsigned integer, or some unsigned integers, from an address in memory.

Parameters:
  1. addr_n_length : an object, either of
    • an atom addr -- to fetch one double word at addr, or
    • a pair {addr,len} -- to fetch len double words at addr
Returns:

An object, either an atom if the input was a single address, or a sequence of atoms if a sequence was passed. In both cases, atoms returned are based on the native size of a "long int." On Windows and all other 32-bit architectures, the number will be in the range 0..power(2,32)-1. On other 64-bit architectures, the number will be in the range of 0..power(2,64)-1.

Errors:

Peeking in memory you do not own may be blocked by the OS, and cause a machine exception. If you use the define safe these routines will catch these problems with a Euphoria error.

When supplying a {address, count} sequence, the count must not be negative.

Comments:

Since addresses are 32-bit numbers on 32-bit architectures, they can be larger than the largest value of type integer (31-bits). Variables that hold an address should therefore be declared as atoms.

It is faster to read several integers at once using the second form of peek than it is to read one integer at a time in a loop. The returned sequence has the length you asked for on input.

Remember that peek_longu takes just one argument, which in the second form is actually a 2-element sequence.

The only difference between peek_longs and peek_longu is how double words with the highest bit set are returned. peek4s assumes them to be negative, while peek_longu just assumes them to be large and positive.

Example 1:
-- The following are equivalent (on a 32-bit architecture, or Windows):
-- first way
s = {peek_longu(100), peek4u(104), peek4u(108), peek4u(112)}

-- second way
s = peek_longu({100, 4})
See Also:

Using Data Double Words, poke4, peek, peek4s, allocate, free, peek2u, peek2s, peek8u, peek8s, peek_longs, poke_long

8.44.5.10 peek_string

<built-in> function peek_string(atom addr)

reads an ASCII string in RAM, starting from a supplied address.

Parameters:
  1. addr : an atom, the address at which to start reading.
Returns:

A sequence, of bytes, the string that could be read.

Errors:

Further, peeking in memory that does not belong to your process is something the operating system could prevent, and you'd crash with a machine level exception.

Comments:

An ASCII string is any sequence of bytes and ends with a 0 byte. If you peek_string at some place where there is no string, you will get a sequence of garbage.

See Also:

Using Strings, peek, peek_wstring, allocate_string

8.44.5.11 peek_pointer

<built-in> function peek_pointer(object addr_n_length)

8.44.5.12 peek_wstring

include std/machine.e
namespace machine
public function peek_wstring(atom addr)

returns a unicode (utf16) string that are stored at machine address a.

Parameters:
  1. addr : an atom, the address of the string in memory
Returns:

The string, at the memory position. The terminator is the null word (two bytes equal to 0).

See Also:

Using Strings, peek_string

8.44.6 Writing to Memory

8.44.6.1 poke

<built-in> procedure poke(atom addr, object x)

stores one or more bytes, starting at a memory location.

Parameters:
  1. addr : an atom, the address at which to store
  2. x : an object, either a byte or a non empty sequence of bytes.
Errors:

Poking in memory you do not own may be blocked by the OS, and cause a machine exception. The -D SAFE option will make poke catch this sort of issues.

Comments:

The lower 8-bits of each byte value (such as remainder(x, 256)) is actually stored in memory.

It is faster to write several bytes at once by poking a sequence of values, than it is to write one byte at a time in a loop.

Writing to the screen memory with poke can be much faster than using puts or printf, but the programming is more difficult. In most cases the speed is not needed. For example, the Euphoria editor, ed, never uses poke.

Example 1:
a = allocate(100)   -- allocate 100 bytes in memory

-- poke one byte at a time:
poke(a, 97)
poke(a+1, 98)
poke(a+2, 99)

-- poke 3 bytes at once:
poke(a, {97, 98, 99})
Example 2:

demo/callmach.ex

See Also:

Using Data Bytes, peek, peeks, poke4, allocate, free, poke2, mem_copy, mem_set

8.44.6.2 poke2

<built-in> procedure poke2(atom addr, object x)

stores one or more words, starting at a memory location.

Parameters:
  1. addr : an atom, the address at which to store
  2. x : an object, either a word or a non empty sequence of words.
Errors:

Poking in memory you do not own may be blocked by the OS, and cause a machine exception. If you use the define safe these routines will catch these problems with a Euphoria error.

Comments:

There is no point in having poke2s or poke2u. For example, both 32768 and -32768 are stored as #F000 when stored as words. It is up to whoever reads the value to figure it out.

It is faster to write several words at once by poking a sequence of values, than it is to write one words at a time in a loop.

Writing to the screen memory with poke2 can be much faster than using puts or printf, but the programming is more difficult. In most cases the speed is not needed. For example, the Euphoria editor, ed, never uses poke2.

The 2-byte values to be stored can be negative or positive. You can read them back with either peek2s or peek2u. Actually, only remainder(x,65536) is being stored.

Example 1:
a = allocate(100)   -- allocate 100 bytes in memory

-- poke one 2-byte value at a time:
poke2(a, 12345)
poke2(a+2, #FF00)
poke2(a+4, -12345)

-- poke 3 2-byte values at once:
poke2(a, {12345, #FF00, -12345})
See Also:

Using Data Words, peek2s, peek2u, poke, poke4, allocate, free

8.44.6.3 poke4

<built-in> procedure poke4(atom addr, object x)

stores one or more double words, starting at a memory location.

Parameters:
  1. addr : an atom, the address at which to store
  2. x : an object, either a double word or a non empty sequence of double words.
Errors:

Poking in memory you do not own may be blocked by the OS, and cause a machine exception. If you use the define safe these routines will catch these problems with a Euphoria error.

Comments:

There is no point in having poke4s or poke4u. For example, both +231 and -(231) are stored as #F0000000. It is up to whoever reads the value to figure it out.

It is faster to write several double words at once by poking a sequence of values, than it is to write one double words at a time in a loop.

Writing to the screen memory with poke4 can be much faster than using puts or printf, but the programming is more difficult. In most cases the speed is not needed. For example, the Euphoria editor, ed, never uses poke4.

The 4-byte values to be stored can be negative or positive. You can read them back with either peek4s or peek4u. However, the results are unpredictable if you want to store values with a fractional part or a magnitude greater than 232, even though Euphoria represents them all as atoms.

Example 1:
a = allocate(100)   -- allocate 100 bytes in memory

-- poke one 4-byte value at a time:
poke4(a, 9712345)
poke4(a+4, #FF00FF00)
poke4(a+8, -12345)

-- poke 3 4-byte values at once:
poke4(a, {9712345, #FF00FF00, -12345})
See Also:

Using Data Double Words, peek4s, peek4u, poke, poke2, allocate, free, call

8.44.6.4 poke8

<built-in> procedure poke8(atom addr, object x)

stores one or more quad words, starting at a memory location.

Parameters:
  1. addr : an atom, the address at which to store
  2. x : an object, either a quad word or a non empty sequence of double words.
Errors:

Poking in memory you do not own may be blocked by the OS, and cause a machine exception. If you use the define safe these routines will catch these problems with a Euphoria error.

Comments:

On 32-bit machines, the number you supply to poke8 will already be an approximate value before it can reach the memory. If you want to poke exact values way use poke4u or poke4s

There is no point in having poke8s or poke8u. For example, both +power(2,63) and -power(2,63) are stored as #F000000000000000. It is up to whoever reads the value to figure it out.

It is faster to write several quad words at once by poking a sequence of values, than it is to write one quad words at a time in a loop.

The 8-byte values to be stored can be negative or positive. You can read them back with either peek8s or peek8u. However, the results are unpredictable if you want to store values with a fractional part or a magnitude greater than 264, even though Euphoria represents them all as atoms.

Example 1:
a = allocate(100)   -- allocate 100 bytes in memory

-- poke one 8-byte value at a time:
poke8(a, 9712345)
poke8(a+8, #FF00FF00)
poke8(a+16, -12345)

-- poke 3 8-byte values at once:
poke8(a, {9712345, #FF00FF00, -12345})
See Also:

Using Data Quad Words, peek4s, peek4u, poke, poke2, allocate, free, call

8.44.6.5 poke_long

<built-in> procedure poke_long(atom addr, object x)

stores one or more integers, starting at a memory location.

Parameters:
  1. addr : an atom, the address at which to store
  2. x : an object, either an integer or a non empty sequence of double words.
Errors:

Poking in memory you do not own may be blocked by the OS, and cause a machine exception. If you use the define safe these routines will catch these problems with a Euphoria error.

Comments:

There is no point in having poke_longs or poke_longu. For example, both +power(2,31) and -power(2,31) are stored as #F0000000 on a 32-bit architecture. It is up to whoever reads the value to figure it out.

On all Windows and other 32-bit operating systems, the poke_long uses 4-byte integers. On 64-bit architectures using operating systems other than Windows, poke_long uses 8-byte integers.

It is faster to write several integers at once by poking a sequence of values, than it is to write one double words at a time in a loop.

The 4-byte (or 8-byte) values to be stored can be negative or positive. You can read them back with either peek_longs or peek_longu. However, the results are unpredictable if you want to store values with a fractional part or a magnitude greater than the size of a native long int, even though Euphoria represents them all as atoms.

Example 1:
a = allocate(100)   -- allocate 100 bytes in memory

-- poke one 4-byte value at a time (on Windows or other 32-bit operating system):
poke_long(a, 9712345)
poke_long(a+4, #FF00FF00)
poke_long(a+8, -12345)

-- poke 3 long int values at once:
poke_long(a, {9712345, #FF00FF00, -12345})
See Also:

Using Data Double Words, peek4s, peek4u, poke, poke2, allocate, free, call

8.44.6.6 poke_pointer

<built-in> procedure poke_pointer(atom addr, object x)

stores one or more pointers, starting at a memory location.

Parameters:
  1. addr : an atom, the address at which to store
  2. x : an object, either an integer or a non empty sequence of pointers.
Errors:

Poking in memory you do not own may be blocked by the OS, and cause a machine exception. If you use the define safe these routines will catch these problems with a Euphoria error.

Comments:

There is no point in having poke_pointers or poke_pointersu. For example, both +power(2,31) and -power(2,31) are stored as #F0000000 on a 32-bit architecture. It is up to whoever reads the value to figure it out.

On all 32-bit operating systems, the poke_pointer uses 4-byte integers. On 64-bit architectures using operating systems, poke_pointer uses 8-byte integers.

It is faster to write several pointers at once by poking a sequence of values, than it is to write one double words at a time in a loop.

The 4-byte (or 8-byte) values to be stored can be negative or positive. You can read them back with either peek_pointer or any other peek function of the correctsize. However, the results are unpredictable if you want to store values with a fractional part or a magnitude greater than the size of a native pointer, even though Euphoria represents them all as atoms.

Example 1:
a = allocate(100)   -- allocate 100 bytes in memory

-- poke one 4-byte value at a time (on a 32-bit operating system):
poke_pointer(a, 9712345)
poke_pointer(a+4, #FF00FF00)
poke_pointer(a+8, -12345)

-- poke 3 long int values at once:
poke_pointer(a, {9712345, #FF00FF00, -12345})
See Also:

Using Data Double Words, peek4s, peek4u, peek8u, peek8s, peek_pointer poke, poke2, allocate, free, call

8.44.6.7 poke_string

include std/machine.e
namespace machine
public function poke_string(atom buffaddr, integer buffsize, sequence s)

Stores a C-style null-terminated ANSI string in memory

Parameters:
  1. buffaddr: an atom, the RAM address to to the string at.
  2. buffsize: an integer, the number of bytes available, starting from buffaddr.
  3. s : a sequence, the string to store at address buffaddr.
Comments:
  • This does not allocate an RAM. You must supply the preallocated area.
  • This can only be used on ANSI strings. It cannot be used for double-byte strings.
  • If s is not a string, nothing is stored and a zero is returned.
Returns:

An atom. If this is zero, then nothing was stored, otherwise it is the address of the first byte after the stored string.

Example 1:
atom title

title = allocate(1000)
if poke_string(title, 1000, "The Wizard of Oz") then
    -- successful
else
    -- failed
end if
See Also:

Using Strings, allocate, allocate_string

8.44.6.8 poke_wstring

include std/machine.e
namespace machine
public function poke_wstring(atom buffaddr, integer buffsize, sequence s)

stores a C-style null-terminated Double-Byte string in memory.

Parameters:
  1. buffaddr: an atom, the RAM address to to the string at.
  2. buffsize: an integer, the number of bytes available, starting from buffaddr.
  3. s : a sequence, the string to store at address buffaddr.
Comments:
  • This does not allocate an RAM. You must supply the preallocated area.
  • This uses two bytes per string character. Note that buffsize is the number of bytes available in the buffer and not the number of characters available.
  • If s is not a double-byte string, nothing is stored and a zero is returned.
Returns:

An atom. If this is zero, then nothing was stored, otherwise it is the address of the first byte after the stored string.

Example 1:
atom title

title = allocate(1000)
if poke_wstring(title, 1000, "The Wizard of Oz") then
    -- successful
else
    -- failed
end if
See Also:

Using Strings, allocate, allocate_wstring

8.44.7 Memory Manipulation

8.44.7.1 mem_copy

<built-in> procedure mem_copy(atom destination, atom origin, integer len)

copies a block of memory from an address to another.

Parameters:
  1. destination : an atom, the address at which data is to be copied
  2. origin : an atom, the address from which data is to be copied
  3. len : an integer, how many bytes are to be copied.
Comments:

The bytes of memory will be copied correctly even if the block of memory at destination overlaps with the block of memory at origin.

mem_copy(destination, origin, len) is equivalent to: poke(destination, peek({origin, len})) but is much faster.

Example 1:
dest = allocate(50)
src = allocate(100)
poke(src, {1,2,3,4,5,6,7,8,9})
mem_copy(dest, src, 9)
See Also:

Using Data Bytes, mem_set, peek, poke, allocate, free

8.44.7.2 mem_set

<built-in> procedure mem_set(atom destination, integer byte_value, integer how_many))

sets a contiguous range of memory locations to a single value.

Parameters:
  1. destination : an atom, the address starting the range to set.
  2. byte_value : an integer, the value to copy at all addresses in the range.
  3. how_many : an integer, how many bytes are to be set.
Comments:

The low order 8 bits of byte_value are actually stored in each byte. mem_set(destination, byte_value, how_many) is equivalent to: poke(destination, repeat(byte_value, how_many)) but is much faster.

Example 1:
destination = allocate(1000)
mem_set(destination, ' ', 1000)
-- 1000 consecutive bytes in memory will be set to 32
-- (the ASCII code for ' ')
See Also:

Using Data Bytes, peek, poke, allocate, free, mem_copy

8.44.8 Calling Into Memory

8.44.8.1 call

<built-in> procedure call(atom addr)

calls a machine language routine which was stored in memory prior.

Parameters:
  1. addr : an atom, the address at which to transfer execution control.
Comments:

The machine code routine must execute a RET instruction #C3 to return control to Euphoria. The routine should save and restore any registers that it uses.

You can allocate a block of memory for the routine and then poke in the bytes of machine code using allocate_code. You might allocate other blocks of memory for data and parameters that the machine code can operate on using allocate. The addresses of these blocks could be part of the machine code.

If your machine code uses the stack, use c_proc instead of call.

Example 1:

demo/callmach.ex

See Also:

Executable Memory, allocate_code, free_code, c_proc, define_c_proc

8.44.9 Allocating and Writing to memory:

8.44.9.1 allocate_code

include std/machine.e
namespace machine
public function allocate_code(object data, memconst :valid_wordsize wordsize = 1)

allocates and copies data into executable memory.

Parameters:
  1. a_sequence_of_machine_code : is the machine code to be put into memory to be later called with call
  2. the word length : of the said code. You can specify your code as 1-byte, 2-byte or 4-byte chunks if you wish. If your machine code is byte code specify 1. The default is 1.
Returns:

An address, The function returns the address in memory of the code, that can be safely executed whether DEP is enabled or not or 0 if it fails. On the other hand, if you try to execute a code address returned by allocate with DEP enabled the program will receive a machine exception.

Comments:

Use this for the machine code you want to run in memory. The copying is done for you and when the routine returns the memory may not be readable or writeable but it is guaranteed to be executable. If you want to also write to this memory after the machine code has been copied you should use allocate_protect instead and you should read about having memory executable and writeable at the same time is a bad idea. You mustn't use free on memory returned from this function. You may instead use free_code but since you will probably need the code throughout the life of your program's process this normally is not necessary. If you want to put only data in the memory to be read and written use allocate.

See Also:

Executable Memory, allocate, free_code, allocate_protect

8.44.9.2 allocate_string

include std/machine.e
namespace machine
public function allocate_string(sequence s, types :boolean cleanup = 0)

Allocate a C-style null-terminated string in memory

Parameters:
  1. s : a sequence, the string to store in RAM.
  2. cleanup : an integer, if non-zero, then the returned pointer will be automatically freed when its reference count drops to zero, or when passed as a parameter to delete.
Returns:

An atom, the address of the memory block where the string was stored, or 0 on failure.

Comments:

Only the 8 lowest bits of each atom in s is stored. Use allocate_wstring for storing double byte encoded strings.

There is no allocate_string_low function. However, you could easily craft one by adapting the code for allocate_string.

Since allocate_string allocates memory, you are responsible to free the block when done with it if cleanup is zero. If cleanup is non-zero, then the memory can be freed by calling delete, or when the pointer's reference count drops to zero.

Example 1:
atom title

title = allocate_string("The Wizard of Oz")
See Also:

Using Strings, allocate, allocate_wstring

8.44.9.3 allocate_protect

include std/machine.e
namespace machine
public function allocate_protect(object data, memconst :valid_wordsize wordsize = 1,
        valid_memory_protection_constant protection)

Allocates and copies data into memory and gives it protection using Standard Library Memory Protection Constants or Microsoft Windows Memory Protection Constants. The user may only pass in one of these constants. If you only wish to execute a sequence as machine code use allocate_code. If you only want to read and write data into memory use allocate.

See MSDN: Microsoft's Memory Protection Constants

Parameters:
  1. data : is the machine code to be put into memory.
  2. wordsize : is the size each element of data will take in memory. Are they 1-byte, 2-bytes, 4-bytes or 8-bytes long? Specify here. The default is 1.
  3. protection : is the particular Windows protection.
Returns:

An address, The function returns the address to the required memory or 0 if it fails. This function is guaranteed to return memory on the 8 byte boundary. It also guarantees that the memory returned with at least the protection given (but you may get more).

If you want to call allocate_protect( data, PAGE_READWRITE ), you can use allocate instead. It is more efficient and simpler.

If you want to call allocate_protect( data, PAGE_EXECUTE ), you can use allocate_code instead. It is simpler.

You must not use free on memory returned from this function, instead use free_code.

See Also:

Executable Memory

8.44.10 Memory Disposal

8.44.10.1 free

include std/machine.e
namespace machine
public procedure free(object addr)

frees up a previously allocated block of memory.

Parameters:
  1. addr, either a single atom or a sequence of atoms; these are addresses of a blocks to free.
Comments:
  • Use free to return blocks of memory the during execution. This will reduce the chance of running out of memory or getting into excessive virtual memory swapping to disk.
  • Do not reference a block of memory that has been freed.
  • When your program terminates, all allocated memory will be returned to the system.
  • addr must have been allocated previously using allocate. You cannot use it to relinquish part of a block. Instead, you have to allocate a block of the new size, copy useful contents from old block there and then free the old block.
  • If the memory was allocated and automatic cleanup was specified, then do not call free directly. Instead, use delete.
  • An addr of zero is simply ignored.
Example 1:

demo/callmach.ex

See Also:

Using Data Bytes, Using Data Words, Using Data Double Words, Using Strings, allocate_data, free_code

8.44.10.2 free_code

include std/machine.e
public procedure free_code( atom addr, integer size, valid_wordsize wordsize = 1 )

frees up allocated code memory.

Parameters:
  1. addr : must be an address returned by allocate_code or allocate_protect. Do not pass memory returned from allocate here!
  2. size : is the length of the sequence passed to alllocate_code or the size you specified when you called allocate_protect.
  3. wordsize: valid_wordsize default = 1
Comments:

Chances are you will not need to call this function because code allocations are typically public scope operations that you want to have available until your process exits.

See Also: Executable Memory, allocate_code, free

8.44.11 Automatic Resource Management

Euphoria objects are automatically garbage collected when they are no longer referenced anywhere. Euphoria also provides the ability to manage resources associated with euphoria objects. These resources could be open file handles, allocated memory, or other euphoria objects. There are two built-in routines for managing these external resources.

8.44.11.1 delete_routine

<built-in> function delete_routine( object x, integer rid )

associates a routine for cleaning up after a Euphoria object.

Comments:

delete_routine associates a euphoria object with a routine id meant to clean up any allocated resources. It always returns an atom (double) or a sequence, depending on what was passed (integers are promoted to atoms).

The routine specified by delete_routine should be a procedure that takes a single parameter, being the object to be cleaned up after. Objects are cleaned up under one of two circumstances. The first is if it's called as a parameter to delete. After the call, the association with the delete routine is removed.

The second way for the delete routine to be called is when its reference count is reduced to 0. Before its memory is freed, the delete routine is called. A default delete will be used if the cleanup parameter to one of the allocate routines is true.

delete_routine may be called multiple times for the same object. In this case, the routines are called in reverse order compared to how they were associated.

8.44.11.2 delete

<built-in> procedure delete( object x )

calls the cleanup routines associated with the object, and removes the association with those routines.

Comments:

The cleanup routines associated with the object are called in reverse order than they were added. If the object is an integer, or if no cleanup routines are associated with the object, then nothing happens.

After the cleanup routines are called, the value of the object is unchanged, though the cleanup routine will no longer be associated with the object.

8.44.12 Types and Constants

8.44.12.1 std_library_address

include std/machine.e
namespace machine
public type std_library_address(object addr)

an address returned from allocate or allocate_protect or allocate_code or the value 0.

Returns:

An integer, The type will return 1 if the parameter, an address, was returned from one of these Machine Level functions (and has not yet been freeed)

Comments:

This type is equivalent to atom unless SAFE is defined. Only values that satisfy this type may be passed into free or free_code.

8.44.12.2 valid_memory_protection_constant

include std/machine.e
public type valid_memory_protection_constant(object a)

protection constants type

8.44.12.3 machine_addr

include std/machine.e
public type machine_addr(object a)

a 32-bit non-null machine address

8.44.12.4 safe_address

include std/machine.e
public function safe_address(machine_addr start, natural len, 
              positive_int action )

action is some bitwise-or combination of the following constants: A_READ, A_WRITE and A_EXECUTE.

Returns:

When Safe Mode is turned on, this returns true iff it is ok to perform action all addresses from start to start+len-1.

When Safe Mode is not turned on, this always returns true.

Comments:

This is used mostly inside the safe library itself to check whenever you call Machine Level Access Functions or Procedures. It should only be used for debugging purposes.

8.44.12.5 ADDRESS_LENGTH

include std/machine.e
namespace machine
public constant ADDRESS_LENGTH

The number of bytes required to hold a pointer.

8.44.12.6 PAGE_SIZE

include std/machine.e
namespace machine
public constant PAGE_SIZE

The operating system's memory page length in bytes.

8.45 Indirect Routine Calling

8.45.1 Accessing Euphoria coded routines

8.45.1.1 routine_id

<built-in> function routine_id(sequence routine_name)

returns an integer id number for a user-defined Euphoria procedure or function.

Parameters:
  1. routine_name : a string, the name of the procedure or function.
Returns:

An integer, known as a routine id, -1 if the named routine can't be found, else zero or more.

Errors:

routine_name should not exceed 1,024 characters.

Comments:

The id number can be passed to call_proc or call_func, to indirectly call the routine named by routine_name. This id depends on the internal process of parsing your code, not on routine_name.

The routine named routine_name must be visible (that is callable) at the place where routine_id is used to get the id number. If it is not, -1 is returned.

Indirect calls to the routine can appear earlier in the program than the definition of the routine, but the id number can only be obtained in code that comes after the definition of the routine - see example 2 below.

Once obtained, a valid routine id can be used at any place in the program to call a routine indirectly via call_proc or call_func, including at places where the routine is no longer in scope.

Some typical uses of routine_id are:

  1. Creating a subroutine that takes another routine as a parameter. (See Example 2 below)
  2. Using a sequence of routine id's to make a case (switch) statement. Using the switch statement is more efficient.
  3. Setting up an Object-Oriented system.
  4. Getting a routine id so you can pass it to call_back. (See Platform-Specific Issues)
  5. Getting a routine id so you can pass it to task_create. (See Multitasking in Euphoria)
  6. Calling a routine that is defined later in a program. This is no longer needed from v4.0 onward.

Note that C routines, callable by Euphoria, also have ids, but they cannot be used where routine ids are, because of the different type checking and other technical issues.

See Also:

define_c_proc and define_c_func

Example 1:
procedure foo()
    puts(1, "Hello World\n")
end procedure

integer foo_num
foo_num = routine_id("foo")

call_proc(foo_num, {})  -- same as calling foo()
Example 2:
function apply_to_all(sequence s, integer f)
    -- apply a function to all elements of a sequence
    sequence result
    result = {}
    for i = 1 to length(s) do
        -- we can call add1() here although it comes later in the program
        result = append(result, call_func(f, {s[i]}))
    end for
    return result
end function

function add1(atom x)
    return x + 1
end function

-- add1() is visible here, so we can ask for its routine id
? apply_to_all({1, 2, 3}, routine_id("add1"))
-- displays {2,3,4}
See Also:

call_proc, call_func, call_back, define_c_func, define_c_proc, task_create, Platform-Specific Issues, Indirect routine calling

8.45.1.2 call_func

<built-in> function call_func(integer id, sequence args={})

calls the user-defined Euphoria function by routine id.

Parameters:
  1. id : an integer, the routine id of the function to call
  2. args : a sequence, the parameters to pass to the function.
Returns:

The value, the called function returns.

Errors:

If id is negative or otherwise unknown, an error occurs.

If the length of args is not the number of parameters the function takes, an error occurs.

Comments:

id must be a valid routine id returned by routine_id.

args must be a sequence of argument values of length n, where n is the number of arguments required by the called function. Defaulted parameters currently cannot be synthesized while making a indirect call.

If the function with id id does not take any arguments then args should be {}.

Example 1:

Take a look at the sample program called demo/csort.ex

See Also:

call_proc, routine_id, c_func

8.45.1.3 call_proc

<built-in> procedure call_proc(integer id, sequence args={})

calls a user-defined Euphoria procedure by routine id.

Parameters:
  1. id : an integer, the routine id of the procedure to call
  2. args : a sequence, the parameters to pass to the function.
Errors:

If id is negative or otherwise unknown, an error occurs.

If the length of args is not the number of parameters the function takes, an error occurs.

Comments:

id must be a valid routine id returned by routine_id.

args must be a sequence of argument values of length n, where n is the number of arguments required by the called procedure. Defaulted parameters currently cannot be synthesized while making a indirect call.

If the procedure with id id does not take any arguments then args should be {}.

Example 1:
public integer foo_id

procedure x()
    call_proc(foo_id, {1, "Hello World\n"})
end procedure

procedure foo(integer a, sequence s)
    puts(a, s)
end procedure

foo_id = routine_id("foo")

x()
See Also:

call_func, routine_id, c_proc

8.45.2 Accessing Euphoria Internals

8.45.2.1 machine_func

<built-in> function machine_func(integer machine_id, object args={})

performs a machine-specific operation that returns a value.

Returns:

Depends on the called internal facility.

Comments:

This function us mainly used by the standard library files to implement machine dependent operations such as graphics and sound effects. This routine should normally be called indirectly via one of the library routines in a Euphoria include file. User programs normally do not need to call machine_func.

A direct call might cause a machine exception if done incorrectly.

See Also:

machine_proc

8.45.2.2 machine_proc

<built-in> procedure machine_proc(integer machine_id, object args={})

perform a machine-specific operation that does not return a value.

Comments:

This procedure us mainly used by the standard library files to implement machine dependent operations such as graphics and sound effects. This routine should normally be called indirectly via one of the library routines in a Euphoria include file. User programs normally do not need to call machine_proc.

A direct call might cause a machine exception if done incorrectly.

See Also:

machine_func