著者:xzp21stメール: [email protected] ハード著者、著者とソースを明記してください
キーワード:keilc51、シミュレーションスタックリエントラント関数呼び出し、パラメータの受け渡し、C XBP、C ADDXBP?
要約:この論文ではkeilc51の希望に詳細に再入国するkeilc51機能とアナログスタックと実装の原則を、簡単な手順で大規模なメモリモデルリエントラント関数で呼び出しプロセスkeilc51を分析するために、いくつかの概念を説明し、 51シリーズのいくつかの助けを提供初心者RTOSを移植。
再入可能関数(リエントラント機能)とアナログ・スタック(スタックのシミュレーション)について
「リエントラント関数は、時間の経過後、再入可能関数はいつでも中断することができる。データの破損の恐れなしに複数のタスクを起動することができ、実行することができ、対応するデータが失われることはない。」(抜粋埋め込みからRTOSのuC / OS-II)
これらの概念を理解する前に、我々は最初のkeilc51の話をしなければならない「オーバーレイ技術。」(この技術を使用する理由は、付録友達を説明参照します)
(1)グローバル変数は、(拡張外部メモリの場合を考慮せずに)ローカルRAM領域に格納されます。
(2)コンパイル及びリンクされた場合、すなわち、ローカル変数の位置決めが完了しています。
(3)関数呼び出し、カバーすることができ、その後、ローカル変数領域との間に直接または間接的な関係が存在しない場合。
それは、Keil社のC51環境では、これらの理由のためである(例えば、シミュレートされたスタックを追加するなど)対処しなければ、純粋な関数はリエントラントではありません。例えば:
空TaskA(void *型PD)
{
int型;
//他の変数を定義します
行う{
//実際のユーザーのタスク処理コード
}一方、(1)
}
空TaskB(void *型PD)
{
int型B;
//他の変数を定義します
行う{
FUNC();
//他の実用的なユーザーのタスクの処理コード
}一方、(1)
}
空FUNC()
{
int型のC;
//他の変数を定義します
//関数処理コード
}
上記のコードでは、それらのローカル変数aおよびbは、すなわち上書き互いに、となるように、直接的または間接的な呼び出しとはTaskA TaskBが存在しない、彼らは両方とも、特定の同一のRAM空間を配置することができます。TaskAは、いくつかの時間のために実行しているときにこのように、TaskBはCPUと実行を制御し得るとき、それはBを変更することがあり、変更されています。aおよびbは、CPUの制御を取り戻すTaskAその結果、同一のRAM空間を参照するので、値が変更され、間違った動作をさせるプログラムは、その逆回し。一方、FUNC()を呼び出すTaskBと直接的な関係を有しているため、そのローカル変数は、Bとそれぞれ、C覆われていないが、ローカル変数C FUNC保証が形成されていないことができないか、ローカル変数TaskA他のタスクをカバーすることができます関係。
我々は容易に決定することができる上記の分析によるとTaskA TaskBこれら二つの関数は(もちろん、FUNCもリエントラントの)リエントラントではありません。それでは、どの関数リエントラント機能になるには?リエントラントとして定義された機能拡張キーワードを使用してのC51コンパイラオプションは、関数があれば一緒にファンクションキーは再入することができた後のように、再入可能関数として定義する必要があります。
ストレージ割り当て方法とパラメータと非リエントラント異なる関数のローカル変数を渡し、コンパイラがリエントラントスタック機能(スタックするためのシステムまたはハードウェア・スタックに対して)のためにC51アナログを生成、シミュレーションパラメータスタックを完了するためにローカル変数を転送して保存します。?グローバル変数をシミュレートするC_IBP ,? C_PBPとスタック?C_XBPは、スタックポインタ(SPシステムスタックのスタックポインタ)として、これらの変数は、データ・アドレス・スペースで定義され、そしてstartup.a51ファイルで初期化することができます。コンパイル時に使用するメモリモデルは、アナログは、スタック領域(IDATA)または(XDATA又はPDATA)、外部メモリ内に配置することができます。表1に示すように:
ストレージモード |
スタックポインタ |
スタック領域 |
小さい |
?C_IBP(1バイト) |
間接内部データ・メモリ・アクセス(IDATA)、スタック領域の256バイトの最大 |
コンパクト |
?C_PBP(1バイト) |
外部データ・メモリ(PDATA)をアドレッシングページング、スタック領域は、256バイトの最大値であります |
大 |
?C_XBP(2バイト) |
外部データ・メモリ(XDATA)、64Kスタック領域の最大 |
表1
注:(また、従来のハードウェアスタック又はスタックとしても知られている)システムスタック51台のコンピュータは、常に内部データ・メモリ(SP 8ビットレジスタのみ内部点)に位置し、かつ下位アドレスからのタイプ(「アップ成長」されています高アドレス)、およびスタックがシミュレートされた「成長ダウン」タイプです。
<! - [場合はsupportLists!] - > 2、<! - [endifの] - > [リエントラント機能パラメータ転送プロセス分析
分析に入る前に、ときに、関数呼び出しのパラメータが渡された方法についてのC51簡単にトーク。簡単に説明すると、主パラメータでR1〜R7が通過するレジスタ、およびコールパラメータまたは全くレジスタを使用していないが、利用可能なコンパイル制御コマンド「NOREGPARMS」である場合、送信パラメータは、固定されたメモリ領域にメモリを発生します選択されたパターンのメモリアドレス空間がコンパイルさに依存するセグメント領域と呼ばれるパラメータを渡します。表2に示すようにMCU 51は、三つのパラメータにワーキングレジスタ転送を使用します。
パラメータの受け渡し |
文字、1バイトのポインタ |
int型、2バイトのポインタ |
長い、フロート |
一般的なポインタ |
最初の引数 |
R7 |
R6、R7 |
R4〜R7 |
R 1、R 2、R 3 |
二番目のパラメータ |
R5 |
R4、R5 |
R4〜R7 |
R 1、R 2、R 3 |
三番目のパラメータ |
R3 |
R2、R3 |
ノー |
R 1、R 2、R 3 |
表II
ここでは2つの例を示します。
func1の(int型A): "" R6、R7に渡される最初の引数、です。
関数func2(int型B、INT C、INT * D): "B" はR6、R7であり通過、 "C" R4が経過し、R5であり、 "* Dは、" 中、R1、R2、R3に渡されます。
レジスタがここに渡すか、どのような方法を言わなかった関数の戻り値については、我々は、関連する文書や書籍をC51見ることができます。
まあ、我々は次のように簡単なプログラムコードは解剖し始めました。
int型の楽しい(char型A、char型のB、Cのchar、char型のD)リエントラント//分析を簡素化するために、型パラメータがcharあります。
{
int型のJ1、J2;
J1 = A + B + C + D。
J2 = J1 + 10。
J2返します。
}
メイン()
{
私はint型。
I =楽しい(1,2,3,4);
}
手順は、アセンブリ言語に翻訳C51は、それがどのように見えるかである見るために私と一緒にここでは、前置きなしに、簡単です。
メイン()
{
私はint型。
I =楽しい(1,2,3,4);
MOV DPTR、#は0xFFFF;スタックポインタCをシミュレート?0xFFFFの+ 1のXBPは最初のポイント
LCALL C ADDXBP(C:00A6);?コールC?ADDXBPサブルーチンは、アナログスタックポインタCを調整しますか?XBP
; 0xFFFFのへのポイント
MOV A、#0×04; NOアナログに直接、第四パラメータスタック圧力を利用できるレジスタ
MOVX @ DPTR、A。
MOV R3、#0×03であり; R 3は、3つのパラメータを透過し、表2を参照
MOV R5、#0×02; R5は、2つのパラメータを通過し、表2を参照
MOV R7は、#0x01の; R7は、パラメータを通過し、表2参照します
LCALLの楽しみ(C:0003);関数呼び出しの楽しみ
MOV DPTR、#C_STARTUP(0000)は、R6、R7によって楽しい関数の戻り値(int型)をバックに渡されます
;および0000とは0x0001で、外部メモリに格納されたデータ
(int型は2バイトです)
MOV A、R6
MOVX @ DPTR、A
INC DPTR
MOV A、R7
MOVX @ DPTR、A
}
RET;メインリターン
説明:アナログ初期スタックポインタはstartup.a51には0xFFFF + 1に初期化され、およびアセンブリコードは、上記のパラメータは右から左に走査されるから分かります。
そして、見た目の楽しさを取る:(アセンブリコード)は、一部をスキップすることができ、患者のことと見、非常に長いです
C:0003
MOV DPTR、#は0xFFFF
?LCALL C ADDXBP(C:00A6);アナログスタックポインタCを調整?XBP = C?XBP-1
MOV A、R3
DPTR、A @ MOVX、R3の値(パラメータ3)は、アナログスタックに押し込まれます
MOV DPTR、#は0xFFFF
?LCALL C ADDXBP(C:00A6);アナログスタックポインタCを調整?XBP = C?XBP-1
MOV A、R5
DPTR、A @ MOVX; R5は、値(パラメータ2)は、アナログスタックに押されています
MOV DPTR、#は0xFFFF
?LCALL C ADDXBP(C:00A6);アナログスタックポインタCを調整?XBP = C?XBP-1
MOV A、R7
MOVX @ DPTR、Aは、R7(パラメータ1)の値は、アナログスタックに押し込まれます
MOV DPTR、#0xFFFC
?LCALL C ADDXBP(C:00A6);アナログスタックポインタCを調整し続けますか?XBP = C?XBP-4、2を入れて
;準備でローカルint型の変数
J1 = A + B + C + D。
MOV DPTR、#0x0005
LCALL C XBPOFF(C:00CA);?Cを通して?シミュレーションのスタックを指すようにDPTR XBP調整値
;この時点でAパラメータDPTR = 0xFFFFの
;注:C XBPOFFは、Cを変更しません?XBPの値
MOVX A、@ DPTR
MOV R7、A; 1パラメータ除去
MOV A、R7
......
。。。。。。;省略、パラメータ2、3テイクパラメータを完了するのに要するパラメータを採取し、4を加算しました
......
MOV DPH(0x83の),? C_XBP(0x08の)
MOV DPL(0x82と)、0x09の;? 0x09のは、+ 1 C_XBPあります
MOV A、R6
MOVX @ DPTR、A
INC DPTR
MOV A、R7
MOVX @ DPTR、A;シミュレーション結果J1プレススタック
J2 = J1 + 10。
......
......
。。。。。。; J2 = J1 + 10を完成省略し、演算結果をアナログスタックJ1に押し込まれます
J2返します。
MOV DPH(0x83の),? C_XBP(0x08の)
MOV DPL(0x82と)、0x09の
INC DPTR
INC DPTR
MOVX A、@ DPTR
MOV R6、A
INC DPTR
MOVX A、@ DPTR
MOV R7、A、アナログR6からスタックに入れJ2、R7
}
MOV DPTR、#?C_XBP(0x0008で)
?LCALL C ADDXBP(C:00A6);リターンに楽しい、0xffffのにC_XBPポイントして、シミュレートされたスタックをリリース
RIGHT
説明:以下のアナログスタック構造
パラメータ4 |
パラメータ3 |
パラメータ2 |
パラメータ1 |
J1下位バイト |
J1上位バイト |
J2下位バイト |
J2上位バイト |
次に、二つの主要サブ機能C_ADDXBPとC_XBPOFF
C ADDXBP?:
MOV A、0x09の、0x09のはC_XBPあります
A、DPLを追加し(0x82と)最初の完全なRETの前:C_XBP + DPTR
MOV DPL(0x82と)、A
MOV A、?C_XBP(0x08の)
ADDC A、DPH(0x83の)
MOV DPH(0x83の)、A
CJNE A、C_XBP(0x08に)、C:?00B9
MOV 0x09の、DPL(0x82と)
RIGHT
C:00B9
JBC EA(0xA8.7)、C:00C2;オープン破りますか?オープン(クリア)、それを先送りし、Cジャンプ:00C2
MOV 0x09の、DPL(0x82と);中断すでに閉じ、セキュリティ、次のアクションが中断されることはありません、新しいです
;アナログスタックポインタ割り当てC_XBP
MOV?C_XBP(0x08の)、
RIGHT
C:00C2
MOV 0x09の、DPL(0x82と)
MOV?C_XBP(0x08の)、
SETB EA(0xA8.7);開口ブレーク
RIGHT
?C XBPOFF :;この関数は自明、完全DPTR = C_XBP + DPTRあります
MOV A、0x09の
ADD A、DPL(0x82と)
MOV DPL(0x82と)、A
MOV A、?C_XBP(0x08の)
ADDC A、DPH(0x83の)
MOV DPH(0x83の)、A
RIGHT
最後に、最後のハイライトの友人~~~最後に来て
アナログスタックはC_XBPが最初0xFFFFの+ 1に等しく、下方に成長し、次の文を見
MOV DPTR、#は0xFFFF
LCALL C ADDXBP?(C:00A6)
(0xFFFFの+ 1)+ = 0xFFFFの0xFFFFの
すなわちC_XBP -1;
ルック
MOV DPTR、#0xFFFEという
LCALL C ADDXBP?(C:00A6)
すなわちC_XBP-2
ルック
MOV DPTR、#0xFFFEという
LCALL C ADDXBP?(C:00A6)
それC_XBP-3
...
実際には、これは次のとおりです。に相当マイナス1プラス0xffffと、比較的0xFFFEという2を加算および減算、プラスマイナス4 0xfffd同等。。。。。。なぜ、あなたはそれを言ったことはなかっただろう:)
結論:
研究の数日後、最後に要約レポートを書きましたが、それの彼らの達成のビットと考え、あなたとの問題、および一般的な進捗状況を議論することを望んで、避けられないと間違っています。
参考文献:
1、徐Aijun、鵬Xiuhua「SCM C51windows高水準言語のプログラミング環境とのアプリケーション」2001年電子工業プレス
図2は、鵬は、リアルタイムオペレーティングシステム51構成のマイクロコントローラを明るい赤。
付録:
、一般的に特別な注意を払っていない再入国問題の機能(例えば、このようなARMなどPC、など)他の環境での問題。のように長いあなたは、通常の状況下で静的変数、または静的変数へのポインタを使用しないよう、機能が自然に再入可能ですA。
C51が、同じではありません、あなたはあなたの関数を設計していない場合は特に、それはリエントラントではありません。
通常のCコンパイラ(またはより正確にポイントを前記:一般的なプロセッサに基づいてCコンパイラ)、この差の原因があり、その機能スタックに位置し、そしてC51に配置されているローカル変数これは、セグメント(データ)によって覆われていてもよいです。
次のようにC51がそうする理由については、一部の人々は言わないように、メモリを節約するために、実際には、単に節約メモリ上の理由ではないことができそう。
関数は別の関数func1のの関数func2を呼び出す場合1)、次いで、関数func1が、func2のローカル変数が単にメモリ.C51の同じ部分ではないか、またはそれらに異なるRAMを割り当てる。これは、スタックの使用と比較され、メモリに保存できません。
彼らは別の呼び出しにあるのでので、func1の場合は2)、func2のは、コールチェーンに、C51は、そのローカル変数が同じメモリアドレスを共有するように、分析でカバーすることができません。しかし、これは、スタックを使用するよりもメモリ保存されませんチェーンに、関数のいずれかが実行されている場合、その後、他のは、必ずしもその寿命の関数ではない、それはまた、システムに戻さスタックのリリースによって占領されています。
(ローカルに保存されているオーバーレイ変数としてC51の使用による)本当の理由は以下のとおりです。
命令51は、変数としてスタックを使用するコストがあまりにも高価なものにする相対(インデックス)を対処する有効な命令、ではありません。
一般的には、店舗の変数にスタックを使用することです:
関数を入力し、スタック領域は、可変収納スペースとして、期間保持、異なる変数にアクセスするためにオフセットを追加することによって、この空間のアドレスにベースアドレスレジスタポイントとして使用することができます。
例えば:MOV EAX、[EBP + 14]; X86命令
LDR R0、[R12、#14]、ARM命令
これは、この問題に良い解決策になることができ。
51、そのようなAN命令が存在しません。
*ローカル変数の実際、2 51または変数インデックスアドレッシング命令はなく、スタック、このような機会へのアクセスのために、あります。
MOVC A、@ A + DPTR
A + PC @ MOVC A、
だから、C51は、特別なキーワードがあります:リエントラント関数は、再入国の問題を解決するために使用しました