8.42 Machine Level Access

8.42.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.42.1 Safe Mode

8.42.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 the're 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.42.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.42.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 don't be one of them.

8.42.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.42.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 )

Description: Add 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.42.1.6 unregister_block

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

Remove 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.42.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.42.3 Type Sorted Function List

8.42.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.42.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.42.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.42.3.4 Using Data Double Words

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

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.42.3.5 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.42.3.6 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.42.4 Memory Allocation

8.42.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.42.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.
Return:

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.42.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.42.4.4 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.42.4.5 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.42.5 Reading from Memory

8.42.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, 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:
-- method 1
s = {peek(100), peek(101), peek(102), peek(103)}

-- method 2
s = peek({100, 4})
See Also:

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

8.42.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 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, 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:
-- method 1
s = {peeks(100), peek(101), peek(102), peek(103)}

-- method 2
s = peeks({100, 4})
See Also:

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

8.42.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, 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:
-- method 1
s = {peek2s(100), peek2s(102), peek2s(104), peek2s(106)}

-- method 2
s = peek2s({100, 4})
See Also:

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

8.42.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:

Peek() 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, 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:
-- method 1
Get 4 2-byte numbers starting address 100.
s = {peek2u(100), peek2u(102), peek2u(104), peek2u(106)}

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

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

8.42.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:

Since addresses are 32-bit numbers, 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:
-- method 1
s = {peek4s(100), peek4s(104), peek4s(108), peek4s(112)}

-- method 2
s = peek4s({100, 4})
See Also:

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

8.42.5.6 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:

Peek() 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, 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:
-- method 1
s = {peek4u(100), peek4u(104), peek4u(108), peek4u(112)}

-- method 2
s = peek4u({100, 4})
See Also:

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

8.42.5.7 peek_string

<built-in> function peek_string(atom addr)

Read 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, peek() memory that doesn't 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.42.5.8 peek_wstring

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

Return 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.42.6 Writing to Memory

8.42.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:

Poke() in memory you don't 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, i.e. 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.42.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:

Poke() 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.

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's 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:
poke4(a, {12345, #FF00, -12345})
See Also:

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

8.42.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:

Poke() 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.

Comments:

There is no point in having poke4s() or poke4u(). For example, both +231 and -(231) are stored as #F0000000. It's 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.42.6.4 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.42.6.5 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.42.7 Memory Manipulation

8.42.7.1 mem_copy

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

Copy 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.42.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.42.8 Calling Into Memory

8.42.8.1 call

<built-in> procedure call(atom addr)

Call 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.42.9 Allocating and Writing to memory:

8.42.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.
Return Value:

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.42.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.42.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 or 4-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.42.10 Memory Disposal

8.42.10.1 free

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

Free 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.42.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.42.10.3 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:

Using Pointer Arrays, allocate_pointer_array, allocate_string_pointer_array

8.42.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.42.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.42.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.42.12 Types and Constants

8.42.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.

Return Value:

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.42.12.2 valid_memory_protection_constant

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

protection constants type

8.42.12.3 page_aligned_address

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

page aligned address type

8.42.12.4 machine_addr

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

a 32-bit non-null machine address

8.42.12.5 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.

Return Value:

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.

Comment: 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.42.12.6 ADDRESS_LENGTH

include std/machine.e
namespace machine
public constant ADDRESS_LENGTH

The number of bytes required to hold a pointer.

8.42.12.7 PAGE_SIZE

include std/machine.e
namespace machine
public constant PAGE_SIZE

The operating system's memory page length in bytes.

8.43 Indirect Routine Calling

8.43.1 Accessing Euphoria coded routines

8.43.1.1 routine_id

<built-in> function routine_id(sequence routine_name)

Return 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, i.e. 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()/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 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.43.1.2 call_func

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

Call 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.43.1.3 call_proc

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

Call 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.43.2 Accessing Euphoria internals

8.43.2.1 machine_func

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

Perform 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.43.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