これはスタック オーバーフローに関する私の最初の投稿なので、正しく行っていることを願っています。;-)
TriCore エミュレーターを開発しようとしていますが、命令のオペランドをロードするタイミングを決定できません。TriCore はあまり知られていない種類のマイクロコントローラーかもしれないので、そのアーキテクチャーについて少し説明させてください。
命令には 16 ビットと 32 ビットの 2 種類があります。16 ビットか 32 ビットかは、最後のバイトのビット 0 によって決まります。バイト順はリトル エンディアンであるため、常にメモリの最初のバイトになります。問題ありません。
これらの 2 つの命令タイプには、16 ビット命令用の 14 と 32 ビット命令用の 25 という、いくつかのオペコード形式があります。オペコードは 2 つの別個のオペコード フィールドに分割されますが、16 ビットのもののほとんどにはオペコード フィールドが 1 つしかありません。最初のオペコード フィールドは下位 8 ビットにあります。16ビット命令の命令を直接記述し、32ビット命令の多くでは、オペランドのエンコードを記述しますが、2番目のフィールドは実際の命令を記述します(ただし、もちろん例外があります)。
私の計画は、常にすべてのオペコード フィールドを抽出し (2 番目のフィールドの位置がすべての命令フォーマットで同じではないため、少し面倒です)、関数ポインター テーブルで使用される 16 ビット値にまとめることです。 .
この値に応じて、オペランドを抽出したいと思います。たとえば、32 ビット命令で最初のオペコードが0x8B
の場合、オペランドは 2 つのデータ レジスタと 1 つの 9 ビット定数です。しかし、本当に退屈な例外があります:
ADD.A
and命令のADDSC.A
両方にオペコード 1 == があり0x01
ます。ただしADD.A
、3 つのアドレス レジスタを使用するのに対しADDSC.A
、命令にエンコードされた 2 つのアドレス レジスタ (1 つのデータ レジスタと 1 つのインデックス) を使用します。
最後に私の質問は次のとおりです。命令を実行する前に、このようなアーキテクチャにオペランドをロードすることはまったく可能ですか? それとも、最初に命令関数を呼び出して、そこでオペランドを抽出する方がよいでしょうか?
興味のある方は、命令セットのマニュアルをご覧ください。
ヒントをありがとう!
ところで: 選択する言語は C または C++ です。
(リクエストにより、元の質問に私の考えを挿入しました。)
さて、私は自分が持っているさまざまなオプションについて広範囲に考えましたが、次のことで解決すると思います.
プロセス全体を 2 つの部分に分けます。
- プログラム解析とオペランド抽出
- 実行
最初のステップでは、各命令がロードされ、32 ビットに整列されます。次に、ロードされた命令が一連のビット マスクと比較され、最終的に何を実行する必要があるかだけでなく、オペランドがどのようにロードされるかを示す適切なオペコードが決定されます。アドレス モード固有の関数では、オペランドはポインターにロードされます。命令即値は必要に応じて格納されます。
これは、次のような構造体に要約されます。
struct instruction_t {
int32_t **operands;
int32_t *immediates;
};
これは基本的に各命令に割り当てられます (最大の欠点: メモリ消費量。最悪の場合 (64 ビット ポインター) のような構造体に対して約 40 バイトを計算しました。つまり、通常は 4 メガバイトで、16 ビット命令のみで構成されるプログラムは、最終的に約 80 メガバイトのメモリを占有しますが、実行速度はかなり速くなると思います)。
このアプローチでは、オペランドがどのように読み込まれるかは問題ではないため、各命令を 1 回だけ実装できます。また、同じ命令を同じオペランド セットで実行すると、実際のマシンと同じように動作します。コードの実行は、正しい構造体を選択し、それに応じて命令関数を呼び出すことによって、正しいオペランドのセットをロードすることを意味するだけです。
私は他のアプローチがあることを知っています - 私は特に動的再コンパイルが好きです。しかし、このシステムはさまざまなオンチップ コンポーネントと I/O マップされたレジスタでかなり複雑であり、いずれにせよかなりの量のボイラープレート コードが追加されます。
私のアプローチについてコメントをいただければ幸いです。多分あなたはそれを行うより良い方法を知っていますか?
ありがとうございました!