Specifying memory allocation area (__near /__far)


An allocating place of the function and a variable can be designated specifically by adding the __near or __far type qualifier when a function or variable declared.

Remark 1.

A declaration without __near or __far is handled according to the default __near and __far determined by the memory model.

Remark 2.

near, far, __near, and __far have the following meanings here.

Character String

Meaning

near

near area

Address range 0x0F0000 to 0x0FFFFF for RAM data and ROM data.

Address range 0x000000 to 0x00FFFF for functions.

far

far area

Address range 0x000000 to 0x0FFFFF for all RAM data, ROM data, and functions.

__near

Type qualifier indicating the near area.

__far

Type qualifier indicating the far area.

[Function]

-

__near and __far are added as type qualifiers. Explicitly specifying __near or __far for a variable or function declaration gives the compiler a direction regarding the allocation area.

-

__near and __far indicate that the variables and functions qualified by them are allocated to the following areas.

Type Qualifier

Function

Data

__near

0x000000 to 0x00FFFF

0x0F0000 to 0x0FFFFF

__far

0x000000 to 0x0FFFFF

0x000000 to 0x0FFFFF

Figure 4.15

Memory Image for near and far

 

-

Explicit __near and __far specified in the source code take priority over the related option settings.

-

For the internal representation of the pointer (2 bytes) pointing to the near area and that (4 bytes) pointing to the far area, see the "Pointer type" description.

-

Extension from a pointer pointing to the near area to a pointer pointing to the far area is basically done as follows.

Pointer

Extension from near to far

Function pointer

The third byte from the least significant is set to 0x00. The most significant byte is undefined.

Variable pointer

The third byte from the least significant is set to 0x0f. The most significant byte is undefined.

 

For details of general cast specifications including integer types, see the "Cast" description.

-

A pointer to void is handled as a variable pointer.

-

The internal representation of a null pointer constant (NULL) is 0, both for a function pointer and a variable pointer.
In a variable pointer pointing to far, the third byte from the least significant is set to 0x0f, but only in NULL, the pointer is set to 0x000000 (the most significant byte is undefined).

-

Allocation sections for __near and __far

 

Function

Uninitialized Variable

Initialized Non-const Variable

Initialized const Variable and Character String Data

__near

.text

.bss

.data

.const

__far

.textf

.bssf

.dataf

.constf

-

Declaration

-

A declaration is checked from the right to the left, and when __near or __far is found between a "variable, function, or * " and the next " *, (, or left end of declaration", it is set to the __near or __far attribute for the "variable, function, or *".
If neither __near nor __far is found, the default __near or __far attribute for the "variable, function, or * " is used.
The order of __near/__far and other declarators can be changed as long as the above order is observed.

The following shows examples in the medium model.



Declaration

Meaning

int x;
int __near x;           //int type variable x is 
                        //allocated to the near section
int func();
int __far func ();      //Function func that returns a 
                        //int type is allocated to the 
                        //far section
int* x;
int __near * __near x;  //Pointer x pointing to near is
                        //allocated to the near section
int* func ();
int __near * __far func (); //Function func that returns 
                            //a near pointer is allocated
                            //to the far section
int (*fp)();
int (__far * __near fp)();  //far function pointer fp is
                            //allocated to the near 
                            //section
int __far * func ();
int __far * __far func();   //Function func that returns 
                            //a far pointer is allocated 
                            //to the far section
int (__far * fp)();
int (__far * __near fp)();  //far function pointer fp is 
                            //allocated to the near 
                            //section

-

The following shows the locations of __near and __far specifications for variable declarations and their meaning.
__near and __far are type qualifiers and can be written at the same locations as const and volatile. The objects to be affected by __near and __far are also the same as those of const and volatile.

int __far i;                    //i is output to the far section (.bssf)
                                //sizeof(&i) is 4
__near int j;                   //j is output to the near section (.bss)
                                //sizeof(&j) is 2
int __far * __near p;           //p is output to the near section (.bss)
                                //sizeof(&p) is 2
                                //p points to an int object in the far section
                                //sizeof(p) is 4
void (__far * __near fp)( );    //fp is output to the near section (.bss)
                                //sizeof(&fp) is 2
                                //fp points to a function in the far section
                                //sizeof(fp) is 4

-

The following shows the locations of __near and __far specifications for function declarations and their meaning.
__near and __far are type qualifiers and can be written at the same locations as const and volatile. The objects to be affected by __near and __far are the same as those of const and volatile.

void (__far func1)( );  //func1 is output to the far section (.textf)
                        //sizeof(&func1) is 4
void __far func2( );    //func2 is output to the far section (.textf)
                        //sizeof(&func2) is 4

-

If multiple __near or __far qualifiers are specified in a single declaration, an error will occur.

int __near __far i;     //Error
int __near __near i;    //Error
int __far __far i;      //Error

-

If both __near and __far are specified for declaration of a single variable or function, an error will occur.

__near int i;
__near int i;           //OK
 
__near int i;
__far int i;            //Compilation error
 
__near const int i;
       const int i;     //OK only when the default setting is near
                        //Compilation error when the default is far
 
       const int i;
__near const int i;     //OK only when the default setting is near
                        //Compilation error when the default is far

-

Relationship with keywords and #pragma
For the operation when __near or __far is specified together with __saddr, #pragma interrupt, or #pragma interrupt_brk, see the description of each keyword and "4.2.2 #pragma directive".

-

Cast

Cast involving a near pointer or a far pointer is handled as follows.

-

Variable pointer

For conversion from near* to far*, 0x0f is set in the third byte from the least significant.

Note that only for NULL, both near* and far* are 0 and 0x00 is set in the third byte from the least significant.

For conversion between an integer type and a pointer type, the value is kept unchanged in principle.

 

Converted to

_Bool

char

short

int

near*

far*

long

long long

Converted from

_Bool

char

-

-

-

<a>

<b>

-

-

short

int

-

-

-

<c>

<b>

-

-

near*

<d>

<e>

<c>

-

<f>

<g>

<g>

far*

<d>

<e>

<e>

<e>

-

<h>

<h>

long

-

-

-

<e>

<c>

-

-

long long

-

-

-

<e>

<e>

-

-

<a>

After being extended to int, this type is converted to near* with the value unchanged.

<b>

After being extended to long, this type is converted to far* with only the value of the lower-order three bytes unchanged.
(This is because the most significant byte is undefined and its value is not guaranteed.)

<c>

This type is converted with the value unchanged. When it is converted to far*, the size of the type is set to three bytes and the upper-order bytes are truncated.
(This is because the most significant byte is undefined and its value is not guaranteed.)

<d>

A NULL pointer is converted to 0. Other pointers are converted to 1.

<e>

The upper-order bytes are truncated to fit within the size of the target type of conversion. The type is converted with the value of the remaining bytes unchanged. When the type is converted to far*, the size of the type is set to three bytes and the upper-order bytes are truncated.
(This is because the most significant byte is undefined and its value is not guaranteed.)

<f>

For a NULL pointer, the third byte from the least significant is set to 0x00. For other pointers, the byte is set to 0x0F.
(This is because the most significant byte is undefined and its value is not guaranteed.)

<g>

After being extended to a far pointer <f>, the upper-order bytes Note (including the undefined byte in the far pointer) are extended with zero.

<h>

The upper-order bytes Note (including the undefined byte in the far pointer) are extended with zero.

Note

This is applied to the most significant byte for conversion to the long type or the upper-order five bytes for conversion to the long long type.

 

-

Function pointer

For conversion from near* to far*, 0x00 is set in the third byte from the least significant.

For conversion from far* to near*, the upper-order 2 bytes are truncated.

Conversion is done in the same way as variable pointers except for conversion between pointers.

-

Conversion between a variable pointer and a function pointer

-

When the -ansi option is specified, type conversion between a variable pointer and a function pointer will cause an error. Note that explicit type conversion generates an error as well as implicit conversion.

-

When the -ansi option is not used, conversion between a variable pointer and a function pointer is done with a warning being output.
When the near or far specification is the same between the variable pointer and the function pointer, only the type is changed and no other processing is done because the size is the same before and after conversion.
For conversion from far to near, the upper-order 2 bytes are discarded and truncated.
For conversion from near to far, the pointer is extended from near to far with the type unchanged, and then the type is converted.


-

Example of cast

-

Assignment between pointers

char __near* o_np;
char __far* o_fp;
 
typedef void(FT)(void);
 
__near FT* f_np;
__far FT* f_fp;
 
void func(){
    o_fp = o_np;  //Upper and lower-order 2 bytes of o_fp =(0xnn00, 0x0000)
                  //or (0xnn0f, o_np)
    f_fp = f_np;  //Upper and lower-order 2 bytes of f_fp =(0xnn00, f_np)
}

-

Conversion from an integer constant to "a variable pointer"

(char __near*)(char)0x12;           //0x0012
(char __near*)0x34;                 //0x0034
(char __far*)0x56;                  //0x00000056
(char __far*)(char __near*)0x78;    //0x000f0078

-

Conversion from an integer constant to "a function pointer"

typedef void(FT)(void);
 
void func1(){
  (__far FT*)0x34;                //0x00000034
  (__far FT*)(char __near*)0x56;  //0x000f0056
                                  //(char __near*)->(char __far*)->(__far FT*)
  (__far FT*)(char __far*)0x78;   //0x00000078
  (__far FT*)(__near FT*)0xab;    //0x000000ab
  (__far FT*)(__far FT*)0xcd;     //0x000000cd
}

-

Conversion from a variable to "a function pointer"

typedef void(FT)(void);
 
void func2(__near FT* f_np, unsigned int i, unsigned char ch){
        (__far FT*)f_np;        //0xnn00, f_np
        (__far FT*)i;           //0xnn00, i
        (__far FT*)ch;          //0xnn00, ch (Unsigned 2 bytes)
}

-

Conversion from a variable to a pointer

signed char sc;
signed short ss;
signed long sl;
 
unsigned char uc;
unsigned short us;
unsigned long ul;
 
void func3(){
        (char __near*)uc;       //0x00, uc
        (char __near*)sc;       //(0x00 or 0xff), sc
 
        (char __far*)uc;        //nn, 0x00, 0x00, uc
        (char __far*)us;        //nn, 0x00, us(1), us(0)
        (char __far*)ul;        //nn, ul(2), ul(1), ul(0)
 
        (char __far*)sc;        //nn, (0x0000 or 0xffff), sc
        (char __far*)ss;        //nn, (0x00 or 0xff), ss(1), ss(0)
        (char __far*)sl;        //nn, sl(2), sl(1), sl(0)
 
        (__far FT*)uc;          //nn, 0x00, 0x00, uc
        (__far FT*)us;          //nn, 0x00, us(1), us(0)
        (__far FT*)ul;          //nn, ul(2), ul(1), ul(0)
 
        (__far FT*)sc;          //nn, (0x0000 or 0xffff), sc
        (__far FT*)ss;          //nn, (0x00 or 0xff), ss(1), ss(0)
        (__far FT*)sl;          //nn, sl(2), sl(1), sl(0)
}

-

Conversion from a pointer to a variable

char __near* o_np;
char __far* o_fp;
 
typedef void(FT)(void);
 
__near FT* f_np;
__far FT* f_fp;
 
signed char sc;
signed short ss;
signed long sl;
 
unsigned char uc;
unsigned short us;
unsigned long ul;
 
void func(){
        uc = o_np;      //Least significant byte o_np
        uc = o_fp;      //Least significant byte o_fp
        uc = f_np;      //Least significant byte f_np
        uc = f_fp;      //Least significant byte f_fp
 
        us = o_np;      //Bit pattern of o_np is retained
        us = o_fp;      //Lower-order 2 bytes of o_fp
        us = f_np;      //Bit pattern of f_np is retained
        us = f_fp;      //Lower-order 2 bytes of f_fp
 
        ul = o_np;      //(0x0000,o_np)or(0x000f,o_np)
        ul = o_fp;      //(0x00,lower-order three bytes of o_fp, 
                        //lower-order 2 bytes of o_fp)
        ul = f_np;      //(0x0000,f_np)
        ul = f_fp;      //(0x00,lower-order three bytes of of_fp, 
                        //lower-order 2 bytes of f_fp)
 
        sc = o_np;      //Least significant byte o_np
        sc = o_fp;      //Least significant byte o_fp
        sc = f_np;      //Least significant byte f_np
        sc = f_fp;      //Least significant byte f_fp
 
        ss = o_np;      //Bit pattern of o_np is retained
        ss = o_fp;      //Lower-order 2 bytes of o_fp
        ss = f_np;      //Bit pattern of f_np is retained
        ss = f_fp;      //Lower-order 2 bytes of f_fp
 
        sl = o_np;      //(0x0000,o_np)or(0x000f,o_np)
        sl = o_fp;      //(0x00,lower-order three bytes of oo_fp, 
                        //lower-order 2 bytes of o_fp)
        sl = f_np;      //(0x0000,f_np)
        sl = f_fp;      //(0x00,lower-order three bytes of of_fp, 
                        //lower-order 2 bytes of f_fp)
}

-

Pointer operation

-

Addition to a far pointer is done only in the lower-order 2 bytes. The upper-order bytes are not changed.

char __far* ptr = (char __far*)0x5ffff + 1;     //0x00050000

-

Subtraction to a far pointer is done only in the lower-order 2 bytes. The upper-order bytes are not changed.

char __far* ptr = (char __far*)0x050002 - 3;    //0x05ffff

-

For subtraction between a near pointer and a far pointer, the type of the right term is cast to the type of the left term before subtraction.

__near int i;
__far int j;
 
void func( )
{
        &j - &i;        //OK (&j) - ((int __far *)(&i))
        &i - &j;        //OK (&i) - ((int __near *)(&j))
 
        &j - (__far int*)&i; //OK
}

-

A near pointer is returned as the result of operation between a near pointer and an integer.

int b = ((char __near *)0xffff+1 == (char __near *)0);  //=1 (True)

-

Comparison with pointers

-

When the -ansi option is specified, a pointer cannot be directly compared with an integer. However, when the -ansi option is not specified, direct comparison is done with a warning being output.
In the case when a warning is output, the integer type is processed to match the pointer type before comparison. Conversion from an integer type to a pointer type conforms to the rules shown in the "Cast" description.

-

When the -ansi option is specified, a variable pointer cannot be directly compared with a function pointer. However, when the -ansi option is not specified, direct comparison is done with a warning being output.
In the case when a warning is output, comparison is processed as follows.

<a>

The type of the right side is cast to the type of the left side.

<b>

When the near or far specification does not match between both sides, the near pointer is cast to the far pointer.

obj_near_ptr  == func_near_ptr  ->   obj_near_ptr == (obj *)func_near_ptr
func_near_ptr == obj_near_ptr   ->   func_near_ptr == (func *)obj_near_ptr
obj_near_ptr  == func_far_ptr
                      ->   (obj __far *)obj_near_ptr == (obj __far *)func_far_ptr
func_f_ptr == obj_n_ptr         ->   func_f_ptr == (func _far *)obj_n_ptr
func_n_ptr == obj_f_ptr
                      ->   (func __far *)func_n_ptr == (func __far *)obj_f_ptr
obj_f_ptr == func_n_ptr         ->   obj_f_ptr == (obj __far *)func_n_ptr

-

Equality operation (== or !=) for a far pointer is done only in the lower-order three bytes. The most significant byte does not affect the operation result.

if((char __far*)0x1105ffffUL == (char __far*)0x05ffffUL)
        OK();
else
        NG();

-

Relational operation for a far pointer is done only in the lower-order 2 bytes. The upper-order 2 bytes do not affect the operation result.
If comparison is to be performed including the upper-order bytes, they should be cast to unsigned long before comparison.

-

Type of ptrdiff_t

This type is always set to signed int (2-byte) regardless of the operation of near and far pointers.

[Usage]

-

The __near or __far type qualifier is added to a function or variable declared.

[Example]

__near int i1;                          (1)
__far int i2;                           (2)
__far int *__near p1;                   (3)
__far int *__near *__far p2;            (4)
__far int func1( );                     (5)
__far int *__near func2 ( );            (6)
int (__near *__far fp1 ) ( );           (7)
__far int * (__near *__near fp2 ) ( );  (8)
__near int * (__far *__near fp3 ) ( );  (9)
__near int * (__near *__far fp4 ) ( );  (10)

(1)

i1 has an int type and is allocated to the near area.

(2)

i2 has an int type and is allocated to the far area.

(3)

p1 is a 4-byte type variable that points to "an int type in the far area", and the variable itself is allocated to the near area.

(4)

p2 is a 2-byte type variable that points to [a 4-byte type in the near area, which points to "an int type in the far area"], and the variable itself is allocated to the far area.

(5)

func1 is a function that returns "an int type", and the function itself is allocated to the far area.

(6)

func2 is a function that returns [a 4-byte type that points to "an int type in the far area", and the function itself is allocated to the near area.

(7)

fp1 is a 2-byte type variable that points to [a function in the near area, which returns "an int type"], and the variable itself is allocated to the far area.

(8)

fp2 is a 2-byte type variable that points to "a function in the near area, which returns [a 4-byte type that points to "an int type in the far area"]", and the variable itself is allocated to the near area.

(9)

fp3 is a 4-byte type variable that points to "a function in the far area, which returns [a 2-byte type that points to "an int type in the near area"]", and the variable itself is allocated to the near area.

(10)

fp4 is a 2-byte type variable that points to "a function in the near area, which returns [a 2-byte type that points to "an int type in the near area"]", and the variable itself is allocated to the far area.