110gc -- DataScope Graphics Component

ライブラリリファレンス - gcHeap

gcAppをまず参照してください。


gcHeapクラス

gcHeapで始まる一連の関数群は、ヒープ中へのメモリ確保を扱います。 gcHeapだけでは扱いにくいので、このgcHeapクラスを取り囲んで gcObjクラスがあり、通常はこのgcObj関数群を用いて、 メモリの確保や開放を行ないます。


gcHeap* gcHeapInit(void* addr, long size);

与えられたメモリ領域をヒープエリアとして初期化します。 初期化後、gcHeap*が返されます。

初期化時に渡されるアドレスが奇数の場合は、word界にアライメントを取りますので、 渡したアドレスと、gcHeap*の値は必ずしも一致しません。

ヒープメモリエリアは最低でも512バイトの長さは確保してください。


void gcHeapRegister(gcHeap* heap);

作成したヒープをアプリケーションコンテキストに登録します。 最大2つまで登録可能であり、古いものから自動的に登録削除されます。 また、以前登録してあるヒープと領域が重なっている場合は、 古いヒープの登録は自動的に削除されます。

ちょっと意味がわかりにくいのですが、 gcHeapInitによるヒープ領域の作成はいくつでも可能です。 ただし、その場合は、gcHeap*を引数に取る、 gcHeap関数群を使う必要があります。 例えば、ヒープ中に確保したメモリ(gcObj*)の開放は、 その確保したgcHeap*を引数に持った、 gcHeapFree関数で行なう必要があります。
もちろんそれでも構わないのですが、確保したメモリ(gcObj*)を、 どのヒープから割り当てたものか覚えておく必要があります。 それでは面倒なので、各gcHeap関数群には対応するgcObj関数群があり、 この場合は、確保したメモリ(gcObj*)ポインタを渡すだけで済むようになっています。 gcHeapFree関数はgcHeap*とgcObj*の2つの引数が必要ですが、 gcObjFree関数の場合にはgcObj*だけで済むようになっています。

この自動仕訳作業を行なうために、gcHeapRegister関数で、 作成したヒープを登録しておく必要があります。 この登録は最大2つまで行なうことが可能です。 ワークデータ領域に作成する切替キーでなくなってしまうヒープと、 アプリ固有データ領域に作成する切替キーでも保持されるヒープの、 2つのヒープの登録に利用できます。

通常のゲームなどでは、ワークデータ領域に作成するヒープだけで十分なので、 以下のようにして領域を確保します。

#pragma section area UserWork
byte heap_work[2000]; //ワークエリアのヒープ
UserStart() {
  ...
  gcAppInit(0);
  workheap= gcHeapInit((void*)heap_work, sizeof(heap_work));
  gcHeapRegister(workheap); //ワークヒープ登録
  gcHeapDefault(workheap, 0); //ワークヒープをデフォルトヒープへ
  ...
あるいは、ワークエリアのデータ領域は0x412100〜0x416fffですので、
UserStart() {
  ...
  gcAppInit(0);
  workheap= gcHeapInit((void*)0x415000, 0x2000);
  gcHeapRegister(workheap); //ワークヒープ登録
  gcHeapDefault(workheap, 0); //ワークヒープをデフォルトヒープへ
  ...
といった指定でもかまいません。

切替キーでも破壊されないヒープを持ちたい場合には、

#pragma section area UserWork
byte heap_work[2000]; //ワークエリアのヒープ
#pragma section area f_area
byte heap_app[1000]; //アプリ固有データ領域内のヒープ
gcHeap* appheap= 0;  //固有ヒープ
UserStart() {
  ...
  gcAppInit(0);
  workheap= gcHeapInit((void*)heap_work, sizeof(heap_work));
  if(appheap==0) {
    appheap= gcHeapInit((void*)heap_app, sizeof(heap_app));
  };
  gcHeapRegister(workheap); //ワークヒープ登録
  gcHeapRegister(appheap);  //固有ヒープ登録
  gcHeapDefault(workheap, 0); //ワークヒープをデフォルトヒープへ
  ...
のようにします。この場合の、f_areaからのヒープ領域は、 アドレス指定ではなく必ず配列で確保しておきます。 そうでないと、アプリケーションで使っているアプリ固有データ領域の長さが AppInfoセクションに正確に反映されなくなります。

(注) 切替キーは、ヒープ操作中などの任意のタイミングで実行されてしまうため、 実際には切替キーを越えてヒープを安全に保持することは困難です。


void gcHeapDefault(gcHeap* heap, short mode);

指定されたheapをデフォルトのヒープとして アプリケーションコンテキストに保存します。 modeには、gcObjAllocなどで使うアロケートモードを指定します。 ここで指定されたモードと、関数で指定されたモードとのORが、 実際のgcHeapAllocの割り付けモードに用いられますが、 通常、0を指定します。 heapとして0を指定した場合には、デフォルトヒープは変更されず、 アロケートモードのみ変更されます。

この関数も、上記のgcHeapRegisterと同様、gcObjAllocからgcHeapAllocへの つなぎのための登録用関数です。 例えば、描画領域(gcRgn*)の演算ルーチンであるgcRgnAnd関数は、 演算結果をヒープから確保した領域で返します。 その場合に用いられるヒープ領域が、ここで指定したデフォルトヒープになります。


gcObj* gcHeapAlloc(gcHeap* heap, long size, short mode);

指定したヒープからメモリ領域を確保し、そのハンドルを返します。 modeには次のフラグをOR指定します。デフォルトは0です。
#define gcHeap_HEAD 1
見つかった空きブロックの前方から領域を確保します。
#define gcHeap_ALL 2
見つかった空きブロックの全てを確保します。 実際に確保されるサイズは、引数で指定されたsize以上になります。
#define gcHeap_LOWEST 4
割り付け可能な空きブロックのうち、 一番低位アドレスのブロックから確保します。
#define gcHeap_NOCOMPACT 8
割り付け時にヒープのコンパクト化を行ないません。
返されるgcObj*自体はメモリブロックのアドレスそのものではありません。 gcObjは次のようなハンドルです。
typedef struct {
  gcObj_v* v; //予約、未使用
  void* d; //確保したメモリブロックのアドレス
} gcObj;
実際に確保したメモリブロックのアドレスは、(gcObj*)->dになります。 このようなダブルポインタ構成になっているのは、 確保したメモリ領域が、必要に応じて移動させられるからです。 つまり、(gcObj*)->dの値は、ヒープのコンパクト化に伴い、ヒープの中を移動します。

(gcObj*)->dのアドレスのうち上位31〜24ビットの内容は、 gcHeapクラスによって内部的に使用されていますので、 比較対象とする時は注意してください。 DS110で用いられているTLCS900 CPUは、アドレスバス24ビットです。
【戻り値】0 ☆メモリ不足
その他正常


short gcHeapRealloc(gcHeap* heap, gcObj* obj, long size, short mode);

確保したメモリの大きさを変えます。大きくも小さくもなります。 modeはgcHeapAllocのものに加えて、次のものが指定できます。
#define gcHeap_NOMOVE 16
再割当て時に、当該メモリブロックを移動しません。 つまり、obj->dの値は変化しません。 このフラグが指定していない場合、 そのメモリブロックがgcHeapLock関数でロック状態にある場合でも移動します。
【戻り値】-9 ☆内部エラー
-1 ☆メモリ不足
1正常、当該メモリの移動なし
2正常、当該メモリブロックは移動

void gcHeapFree(gcHeap* heap, gcObj* obj);

確保したメモリブロックとそのハンドルを開放します。 gcHeapLinkDataで確保したハンドルも開放できます。


long gcHeapSize(gcHeap* dummy, gcObj* obj);

確保してあるメモリのサイズを返します。 この場合、gcHeap*引数は使用しないので、ダミーです。


void gcHeapCompact(gcHeap* heap);

ヒープをコンパクト化します。 移動可能なブロックを、なるべく高位のアドレスに詰め込みます。


gcObj* gcHeapLinkData(gcHeap* heap, void* data, short mode);

ヒープ外のメモリブロックに対するハンドルを確保し、それを返します。 modeは、ハンドルテーブルブロックの確保に用いられますが、通常0です。

gcHeapAllocで確保するメモリは、ヒープ内のメモリブロックです。 しかし、EEPROM中に確保した固定データを示すgcObj*が必要になる場合があります。 その場合に、このgcHeapLinkDataを用いると、指定したヒープから、 ハンドル用のエリアだけ確保して、そのハンドルを返します。 ここで戻されるgcObj*の、(gcObj*)->dは、引数として与えたアドレスになります。 (上位31〜24ビットを除く)

但し、このようにして確保したgcObj*は、本物のメモリブロックではないので、 gcHeapFree以外の関数を呼び出すことはできません。注意してください。
【戻り値】0 ☆メモリ不足
その他正常


gcObj* gcHeapDup(gcHeap* heap, gcObj* obj, short mode);

指定したメモリブロックを複製してそのハンドルを返します。 このコピー元となるメモリブロックは、どのヒープにあるメモリブロックでも かまいません。但し、gcHeapLinkDataで確保したものはだめです。
【戻り値】0 ☆メモリ不足
その他正常

void gcHeapLock(gcHeap* dummy, gcObj* obj, short flag);

指定したメモリをロックし、gcHeapCompactとかで移動しないようにします。 gcHeap*引数は使用しないのでダミーです。 flagにはロック方向を示します。
1
ロックカウントを1つ増やします。
-1
ロックカウントを1つ減らします。負にはなりません。
0
ロックカウントを強制的に0にし、移動可能なメモリブロックにします。
ロックカウントはgcHeapAllocで確保した時点で0であり、この場合には、その確保した メモリブロックは、移動可能になっています。 ロックカウントが1より大きくなると、そのメモリブロックは移動不可能になり、 (gcObj*)->dの値は、そのメモリブロックでは変化しなくなります。

メモリブロックのロックは、ヒープの断片化をもたらしますので注意してください。 一般に、ロックされているブロックがないヒープの場合は、 ヒープ全体のうち80%程度までメモリ領域を確保することができますが、 ロックされているブロックがある場合には、 ヒープ全体のうち60%程度しかメモリ領域を確保できません。

gcHeapRealloc関数の引数として指定されたメモリブロックは、 たとえそれが移動不可能の場合でも、 gcHeap_NOMOVEが指定されない限り、移動します。


void __adecl gcHeapMemMove(void* to, void* from, long len);

memmove関数と全く同様です。 gcHeapクラスとは関係のない、単なるメモリのコピー関数ですが、 他に良い場所もないので、ここに押し込んであります。