メモリ配置領域指定(__near/__far)


関数,変数の宣言時に,__near/__far型修飾子を追加することにより,関数,変数の配置場所を明示的に指定することができます。

備考 1.

__nearも__farも付けない宣言は,メモリ・モデルにより決定するデフォルトの__nearおよび__farに従います。

備考 2.

文中では,near,far,__near,__far は,それぞれ次の意味を持ちます。

文字列

意味

near

near 領域

RAMデータ,およびROMデータに対しては0x0F0000〜0x0FFFFF のアドレス範囲

関数に対しては0x000000〜0x00FFFFのアドレス範囲

far

far 領域

RAM データ,ROM データ,関数の全てに対して,0x000000〜0x0FFFFF のアドレス範囲

__near

near領域を意味する型修飾子

__far

far領域を意味する型修飾子

[機能]

-

型修飾子に__nearと__farを追加します。変数および関数の宣言時に,__nearおよび__farを明示することにより,コンパイラに配置先を指示します。

-

__nearおよび__farは,修飾した先の変数および関数が,以下の領域に配置されていることを意味します。

型修飾子

関数

データ

__near

0x000000〜0x00FFFF

0x0F0000〜0x0FFFFF

__far

0x000000〜0x0FFFFF

0x000000〜0x0FFFFF

図 4.15

near,farとメモリ・イメージ

 

-

ソースコードに明示的に記述する__nearおよび__farは,オプションによる設定よりも優先されます。

-

nearを指すポインタ(2 バイト),およびfarを指すポインタ(4 バイト)の内部表現は,ポインタ型を参照してください。

-

nearを指すポインタからfar を指すポインタへの拡張は,以下の拡張を基本とします。

ポインタ

nearからfarへの拡張方法

関数ポインタ

下位から3バイト目は0x00 とします。最上位1 バイトは不定です。

変数ポインタ

下位から3バイト目は0x0fとします。最上位1 バイトは不定です。

 

整数型などを含むキャスト全般の詳細については,キャストを参照してください。

-

voidへのポインタは,変数ポインタとして扱います。

-

空ポインタ定数(NULL)の内部表現は,「関数ポインタ」,「変数ポインタ」ともにゼロとします。
farの変数ポインタは下位から3バイト目が0x0f となりますが,NULLに限り0x000000(最上位1バイトは不定)とします。

-

__nearおよび__farと配置セクション

 

関数

初期値なし変数

初期値あり

非const変数

初期値ありconst 変数および文字列データ

__near

.text

.bss

.data

.const

__far

.textf

.bssf

.dataf

.constf

-

宣言

-

宣言を右から左に見て,「変数,関数,*」から次の「*,(,宣言の左端」の間に__near/__farがあれば,それを「変数,関数,*」の__near/__far属性とします。
もし,何もなければ「変数,関数,*」の持つデフォルトの__near/__far属性にします。
__near/__farは,上記の順序を変えない範囲で,ほかの宣言子と順序を変えることができます。

例えばmedium modelの場合は,以下のとおりとなります。



宣言

意味

int x;
int __near x;               //int型の変数xは
                            //nearセクションに配置
int func();
int __far func ();          //int型を返す関数funcは
                            //farセクションに配置
int* x;
int __near * __near x;      //nearを指すポインタxは
                            //nearセクションに配置
int* func ();
int __near * __far func (); //nearポインタを返す関数funcは
                            //farセクションに配置
int (*fp)();
int (__far * __near fp)();  //far関数ポインタfpは
                            //nearセクションに配置
int __far * func ();
int __far * __far func();   //farポインタを返す関数funcは
                            //farセクションに配置
int (__far * fp)();
int (__far * __near fp)();  //far関数ポインタfpは
                            //nearセクションに配置

-

変数宣言に対する__near と__farの記述位置,および意味は次のとおりとします。
__nearおよび__farは型修飾子であり,constおよびvolatileと同じ位置に書くことができます。__nearおよび__farの作用対象も,constおよびvolatileと同じです。

int __far i;                    //iはfarセクション(.bssf)に出力
                                //sizeof(&i)は4
__near int j;                   //jはnearセクション(.bss)に出力
                                //sizeof(&j)は2
int __far * __near p;           //pはnearセクション(.bss)に出力
                                //sizeof(&p)は2
                                //pはfarセクションに存在するintオブジェクトを指す
                                //sizeof(p)は4
void (__far * __near fp)( );    //fpはnearセクション(.bss)に出力
                                //sizeof(&fp)は2
                                //fpはfarセクションに存在する関数を指す
                                //sizeof(fp)は4

-

関数宣言に対する__nearと__farの記述位置,及び意味は次のとおりとします。
__nearおよび__farは型修飾子であり,const及びvolatileと同じ位置に書くことができます。__nearおよび__farの作用対象も,const及びvolatileと同じです。

void (__far func1)( );  //func1はfarセクション(.textf)に出力
                        //sizeof(&func1)は4
void __far func2( );    //func2はfarセクション(.textf)に出力
                        //sizeof(&func2)は4

-

1つの宣言に__nearおよび__far型修飾子を複数回指定した場合は,エラーとなります。

int __near __far i;     //エラー
int __near __near i;    //エラー
int __far __far i;      //エラー

-

同じ変数,関数の宣言において,__nearおよび__farが混在する場合は,エラーとなります。

__near int i;
__near int i;           //OK
 
__near int i;
__far int i;            //コンパイル・エラー
 
__near const int i;
       const int i;     //デフォルトがnearの場合に限りOK
                        //デフォルトがfarの場合はコンパイル・エラー
 
       const int i;
__near const int i;     //デフォルトがnearの場合に限りOK
                        //デフォルトがfarの場合はコンパイル・エラー

-

キーワードおよび#pragmaとの関係
__nearおよび__farを,__saddr,__callt,#pragma callt,#pragma near,#pragma far,#pragma interrupt,および#pragma interrupt_brkと同時に指定した場合の動作は,各キーワードおよび4.2.3#pragma指令を参照してください。

-

キャスト

nearポインタおよびfarポインタを含むキャストは,次のとおりとします。

-

変数ポインタの場合

near*からfar*への変換は,下位から3バイト目に0x0fを追加します。

ただし,NULLのみnear*,far*ともに0であるため,下位から3バイト目に0x00を追加します。

整数型とポインタ型の変換は,値の維持を基本とします。

 

変換先

_Bool

char

short

int

near*

far*

long

long long

変換元

_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>

intに拡張してから,値を維持してnear*に変換

<b>

longに拡張してから,下位3バイトのみ値を維持してfar*に変換
(理由:最上位1バイトは不定のため,変換の保証なし)

<c>

値を維持して変換。変換先の型がfar*の場合は,型サイズを3バイトとして上位を切り捨て
(理由:最上位1バイトは不定のため,変換の保証なし)

<d>

NULLの場合は0,NULL以外は1に変換

<e>

変換先の型サイズに当てはまるように上位を切り捨てる。残ったバイトの値を維持して変換。変換先の型がfar*の場合は,型サイズを3バイトとして上位を切り捨て
(理由:最上位1バイトは不定のため,変換の保証なし)

<f>

NULLの場合は下位から3バイト目に0x00を,NULL以外は下位から3バイト目に0x0Fを設定
(理由:最上位1バイトは不定のため,変換の保証なし)

<g>

farポインタに拡張<f>してから,上位バイト(farポインタの不定部分を含む)をゼロで拡張

<h>

上位バイト(farポインタの不定部分を含む)をゼロで拡張

long型の場合は上位1バイト,long long型の場合は上位5バイトとなります。

 

-

関数ポインタの場合

near*からfar*への変換は,下位から3バイト目に0x00を追加します。

far*からnear*への変換は,上位2バイトを切り捨てます。

ポインタからポインタへの変換以外は,変数ポインタと同じとします。

-

変数ポインタと関数ポインタ間の変換

-

オプション-strict_std使用時は,変数ポインタと関数ポインタの型変換はエラーとなります。暗黙変換だけでなく,明示的型変換もエラーとなります。

-

オプション-strict_std不使用時は,変数ポインタと関数ポインタの型変換は警告を出して変換します。
near/farが同一の場合は,サイズが同一であるため,型を変えるだけで操作はしません。
farからnearへの変換は,上位2バイトを破棄して切り縮めます。
nearからfarへの変換は,同じ型のままnearからfarに拡張した後に,型を変換します。


-

キャストの例

-

ポインタ間の代入

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;    //o_fpの上位/下位2バイト=(0xnn00, 0x0000) or (0xnn0f, o_np)
    f_fp = f_np;    //f_fpの上位/下位2バイト=(0xnn00, f_np)
}

-

整数定数から“変数ポインタ”への変換

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

-

整数定数から“関数ポインタ”への変換

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
}

-

変数から“関数ポインタ”への変換

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(符号なし2バイト)
}

-

変数からポインタへの変換

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)
}

-

ポインタから変数への変換

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;  //o_npの下位1バイト
        uc = o_fp;  //o_fpの下位1バイト
        uc = f_np;  //f_npの下位1バイト
        uc = f_fp;  //f_fpの下位1バイト
 
        us = o_np;  //o_npのビット・パターン維持
        us = o_fp;  //o_fpの下位2バイト
        us = f_np;  //f_npのビット・パターン維持
        us = f_fp;  //f_fpの下位2バイト
 
        ul = o_np;  //(0x0000,o_np)or(0x000f,o_np)
        ul = o_fp;  //(0x00,o_fpの下位3バイト,o_fpの下位2バイト)
        ul = f_np;  //(0x0000,f_np)
        ul = f_fp;  //(0x00,f_fpの下位3バイト,f_fpの下位2バイト)
 
        sc = o_np;  //o_npの下位1バイト
        sc = o_fp;  //o_fpの下位1バイト
        sc = f_np;  //f_npの下位1バイト
        sc = f_fp;  //f_fpの下位1バイト
 
        ss = o_np;  //o_npのビット・パターン維持
        ss = o_fp;  //o_fpの下位2バイト
        ss = f_np;  //f_npのビット・パターン維持
        ss = f_fp;  //f_fpの下位2バイト
 
        sl = o_np;  //(0x0000,o_np)or(0x000f,o_np)
        sl = o_fp;  //(0x00,o_fpの下位3バイト,o_fpの下位2バイト)
        sl = f_np;  //(0x0000,f_np)
        sl = f_fp;  //(0x00,f_fpの下位3バイト,f_fpの下位2バイト)
}

-

ポインタ演算

-

farポインタの加算は下位2バイトで行ないます。上位は変化しません。

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

-

farポインタの減算は下位2バイトで行ないます。上位は変化しません。

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

-

nearポインタとfarポインタ間の減算は,右項の型を左項の型にキャストしてから減算します。

__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
}

-

nearポインタと整数の演算結果は,nearポインタとします。

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

-

ポインタ比較

-

オプション-strict_std指定時は,ポインタと整数間で直接比較できません。しかし,オプション-strict_std非指定時は,警告を出して直接比較を可能とします。
警告時の比較は,整数型をポインタ型に合わせて比較します。整数型からポインタ型への変換は,キャストの項目に書いた規則に従います。

-

オプション-strict_std指定時は,変数ポインタと関数ポインタで直接比較できません。しかし,オプション-strict_std非指定時は,警告を出して直接比較を可能とします。
警告時の比較は,次のように機能します。

<a>

右辺の型を,左辺の型にキャストします。

<b>

両辺の型のnearおよびfarが異なる場合は,nearポインタからfarポインタへのキャストが発生します。

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

-

farポインタの等値演算(==,!=)は,下位3バイトで行ないます。最上位の1バイトは演算結果に影響しません。

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

-

farポインタの関係演算は,下位2バイトのみで比較します。上位の2バイトは演算結果に影響しません。
上位バイトを含めて比較する場合は,unsigned longにキャストしてから比較する必要があります。

-

ptrdiff_tの型

nearポインタ,farポインタの演算に関わらず常にsigned int型(2バイト)とします。

[方法]

-

関数,変数の宣言時に,__near/__far型修飾子を追加します。

[使用例]

__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はint型で,near領域に配置

(2)

i2はint型で,far領域に配置

(3)

p1は“far領域にあるint型”を指す4バイト型の変数で,変数自身はnear領域に配置

(4)

p2は「near領域にある,“far領域にあるint型”を指す4バイト型」を指す2バイト型の変数で,変数自身はfar領域に配置

(5)

func1は“int型”を返す関数で,関数自身はfar領域に配置

(6)

func2は「“far領域にあるint型”を指す4バイト型」を返す関数で,関数自身はnear領域に配置

(7)

fp1は「“int型”を返すnear領域にある関数」を指す2バイト型の変数で,変数自身はfar領域に配置

(8)

fp2は“「“far領域にあるint型”を指す4バイト型」を返すnear領域にある関数”を指す2バイト型の変数で,変数自身はnear領域に配置

(9)

fp3は“「“near領域にあるint型”を指す2バイト型」を返すfar領域にある関数”を指す4バイト型の変数で,変数自身はnear領域に配置

(10)

fp4は“「“near領域にあるint型”を指す2バイト型」を返すnear領域にある関数”を指す2バイト型の変数で,変数自身はfar領域に配置