6

学習目的で、8051 マイクロコントローラー エミュレーターの構築を開始する予定です。C/C++/C# でのプログラミングに慣れています。これはクラスのプロジェクトなどではなく、私の側からの学習イニシアチブです。

私はこれについて議論する非常に多くの質問を見つけました. ただし、実際にコードを書き始める前に、どの領域に焦点を当てる必要があるかを知ることができるように、もう少し細かいレベルでそれを壊したかったのです。

私の最初の要件は次のとおりです。

  1. ユーザーがアセンブリ コードを記述できるテキスト エディター (編集ボックス コントロールを使用可能)

  2. 構文が正しいかどうかを検証する

  3. 実行時にレジスタ値を表示する小さなウィンドウがあります。

  4. ユーザーがプログラムを開始すると、命令はレジスタウィンドウを段階的に更新する必要があります。

GUI 要素よりも、マイクロコントローラーをエミュレートする方法を知りたいと思っています。

私が理解している方法で、さらに分解できます。

  1. 命令のルックアップ テーブル、または使用可能な命令を格納して構文を検証する他の方法が必要です。これを実装する方法があれば、教えてください。

  2. 8051 の各命令をエミュレートするにはどうすればよいですか?

  3. レジスタの場合、タイプに基づいて un/signed 整数を使用し、テーブルを更新できます。

  4. マイクロコントローラは RAM メモリが限られているため、コード長またはメモリ内で実行されているコードをチェックして、バッファ オーバーフローやその他の問題を回避するにはどうすればよいですか。

エミュレーターをゼロから構築する方法を詳しく説明しているオープンソース プロジェクトがいくつかある場合は、感謝します。

4

2 に答える 2

7

少なくともタイトルに関連して、このプロジェクトの範囲について少し不明確だと思います。

エミュレーターはバイナリ コードだけを実行します。エミュレーターには、エディター (開発ツール) もアセンブラー (同上) も含まれていません。構文チェックと変換を行うのはアセンブラーの責任です。そのため、エミュレーターは事前に検証された正当なコードを実行するという比較的簡単な仕事しかできません。

完全な IDE を構築したいようですね。これにより、エディター、アセンブラー、およびエミュレーターに多くの GUI がラップされます。そのステップを最後のステップとして残します。


エミュレータ自体に関する質問については、次のとおりです。

エミュレータの作業メモリとして、最大 (たとえば) 64K バイトの配列を使用できます。プログラムで変数を使用してレジスタをエミュレートします。unsigned char *プログラムカウンターをエミュレートするにはanを使用し、int他のほとんどのものには s を使用します...

操作は非常に簡単です。プログラム カウンターを 0 (または事前に決められたブート位置) から開始し、そのポインターを介して命令をフェッチするループを開始し、命令に関連付けられている操作をレジスターとメモリに適用します。switch単純な実装では、考えられるすべての命令コードを含む巨大なステートメントが中心になります。

前述したように、アセンブラは不正な命令を生成しないため、エミュレータは不正な命令について心配する必要はありません。不正な操作に遭遇した場合、プログラム (つまり、メイン ループ) が停止する可能性があります。

同様に、エミュレーターは範囲、インデックス、またはサイズのオーバーランについて心配する必要はありません...これはアセンブラーの問題でもあり、リンカーがある場合はリンカーの問題でもあります。


更新: SOのここからのいくつかのポインタ:

エミュレーター フレームワーク

于 2009-11-24T09:32:50.563 に答える
6

最近、AVR チップ用のエミュレータをまとめました。これも小さな 8 ビット マイクロコントローラです。ソースは GitHub にgewgill/emulinoとしてあります。最も興味深いファイルは、各 CPU 命令の実装を含むcpu.cです。重要な行は次のとおりですcpu_run()(一部の詳細は省略します)。

while (state == CPU_RUN) {
    u16 instr = Program[PC++];
    Instr[instr](instr);
}

これは、PC レジスタが指すプログラム メモリから 16 ビット ワードをロードし、それを命令ジャンプ テーブル (関数ポインタの 64k 配列である - 実際のテーブルはコンパイル時にスクリプトによって生成される) へのインデックスとして使用します。 )。この関数はdo_XXX()、そのソース ファイル内の関数の 1 つになり、実際の命令を実行する前にさらに命令のデコードを行う場合があります。たとえば、do_ADD()関数:

static void do_ADD(u16 instr)
{
    trace(__FUNCTION__);
    // ------rdddddrrrr
    u16 r = (instr & 0xf) | ((instr >> 5) & 0x10);
    u16 d = ((instr >> 4) & 0x1f);
    u8 x = Data.Reg[d] + Data.Reg[r];
    Data.SREG.H = (((Data.Reg[d] & Data.Reg[r]) | (Data.Reg[r] & ~x) | (~x & Data.Reg[d])) & 0x08) != 0;
    Data.SREG.V = (((Data.Reg[d] & Data.Reg[r] & ~x) | (~Data.Reg[d] & ~Data.Reg[r] & x)) & 0x80) != 0;
    Data.SREG.N = (x & 0x80) != 0;
    Data.SREG.S = Data.SREG.N ^ Data.SREG.V;
    Data.SREG.Z = x == 0;
    Data.SREG.C = (((Data.Reg[d] & Data.Reg[r]) | (Data.Reg[r] & ~x) | (~x & Data.Reg[d])) & 0x80) != 0;
    Data.Reg[d] = x;
    Cycle++;
}

これは実際の加算演算 ( Data.Reg[d] + Data.Reg[r]) を実行し、結果に基づいてさまざまな条件フラグをすべて設定します。

于 2009-11-24T09:35:44.553 に答える