MIPSアーキテクチャは、 "分岐遅延スロット" を持っています。
簡略化して考えてみましょう。命令ユニットと命令実行ユニットをフェッチする2つの別々のユニットがあります。
フェッチユニットは、実行ユニットの「1つ先に」実行されます。これにより、ユニットのオーバーラップが可能になります。すなわち、実行ユニットはフェッチと並行して動作することができる。前のサイクルでフェッチされたinstを実行します。
サイクル0では、最初の命令がフェッチされます。サイクル1では、第1の命令が実行され、第2の命令がフェッチされる。サイクル2では2nd instが実行され、3番目の命令がフェッチされます。これは、次のようになります。
cycle fetch exec
0 1 n/a
1 2 1
2 3 2
3 4 3
我々はどのような種類(すなわちjal
)の分岐命令を打つまでは正常に動作します。あなたの例では、7 jal hello
と9 li $a0,0
があります。あなたのCコードは表示されませんでしたが、私はhello
は一つの引数を取り、あなたの実際の呼び出しはそうhello(0)
た、シーケンスはli $a0,0
最もアーチ上jal hello
だろうと思われます。
命令フェッチが "先行"で実行されるため、先読みされた命令の後にのjal
が破棄され、無駄になります。
したがって、mipsには分岐遅延スロットがあります。 の後にブランチは、で、の遅延スロットです。ブランチの前に登場したかのように、は常にです。
だから、論理的に、あなたのプログラムは次のようになります。実際の実行順序はL1、L3、L2
ある
L1: li $a0,0 # first arg to hello
L2: jal hello # call to hello
L3: nop # branch delay slot
コンパイラはこれを最適化し、分岐遅延に便利な命令を入れることができましたスロット:
L1: jal hello # call to hello
L2: li $a0,0 # first arg to hello
実行順序はL2、L1です。ブランチ[takenまたはではなく]の場合、分岐遅延スロットの命令は、最初に実行された場合と同様に、常にが最初に実行されることを覚えておいてください。
したがって、gdb はとなりました。ブレークポイントを正しい場所に置いてください:mainの最初の命令です。しかし、最初の命令がブランチであったため、break
命令を置く正しい場所はブランチの分岐遅延スロットです。
はあなたの例では、jal
は7行目であり、それはライン9
だったため、分岐遅延スロットはUPDATE:
残念ながら、ブレークポイントが誤った位置に設定されています命令に関係なく:jal hello
をli $a0, 1
に置き換えることができ、何も変更されません。
ごめんなさい。 li
は、それが擬似命令であり、実際の命令を1-2個生成できるので、手がかりになったはずです。たとえば、li $a0,0x01020304
は、次のように生成されます。lui $a0,0x0102 ori $a0,$a0,0x0304
ただし、分岐遅延スロットには注意が必要です。 qemu
についてはわかりませんが、mars
やspim
のようないくつかのmipsシミュレータでは、スロットの有効/無効を設定することができます。スロットのデフォルト値はです。オフの場合、スロットは無視できます。それ以外の場合は、各ブランチの後にnop
を追加してください。
コードは "手作業で"書かれており、Cまたは他の言語からコンパイルされていません。
もう一度、申し訳ありません。私は「GCCで組み立てられた」代わりに「GCCでコンパイルされた」を見た。
問題の一部は、gdb
が高水準言語ソースデバッガです。それがその主要な方向です。行番号の概念は、HLL(例えば、C)行番号に向けられている。だから、いくつかの助けを借りずにasm行番号に/からのマッピングが難しいかもしれません。ソースが.s
であっても、それはcc -c -s -o foo.s foo.c ; cc -o foo foo.s
から来る可能性があります。
gdb
は、プログラムが-g
でコンパイルされています。これは、特定のasmディレクティブを追加してデバッグ情報を定義します。それはどのようなものかを確認するには、Cプログラム[あるいはただの.c
ファイルについて]と[クロス]を取る使用して、それをコンパイルし-g
[または-gdwarf-2
]と-s
。出力ファイル.s
を見てください。
あなたは正確に何あなたは行番号があるべきだと思うgdb
を伝えるための場所で同様のディレクティブを追加する必要があるかもしれません。これは、もちろん、手動で行うことができます。しかし、私は与えられた.s
をとり、必要なものを追加するために "メタプログラミング"スクリプトを通してそれを供給することが知られていました。私はデバッグASMへgdb
を使用し、精密な制御を必要とする、私はより良い配向されているいくつかの異なるGDBコマンドを使用してきた時はいつでもそう、これの出力は、gcc
--YMMV
に供給されているものですが、アセンブラをデバッグする。
stepi
の代わりstep
。これは、gdb ががソース行であると考える代わりに、単一のasm命令で処理します。
disassemble main
の代わりlist main
。これは、ソースリストの代わりに実際の指示を出します。またはx/i <address>
。良い例はx/i $pc
です。
<address>
は、ラベルやラベルを使用した簡単な式にすることができます。今
、ビギー:break *<address>
:代わりbreak <function>
またはbreak <line_number>
の、私はアドレスフォームを使用します。
したがって、disassemble main
が最初の命令がアドレス0x00001000
にあることを示した場合、break *0x1000
を実行します。
しかし、それは面倒です。アドレス形式はシンボルを許可します。したがって、あなたはbreak *main
を行うことができます。また、アドレス式も使用できます。break *main+0x4
。私は別のアプローチ
:-)「これら
は、あなたが探しているドロイドですが、」シミュレーションのため
mars
または
spim
の使用を検討することだと思います。これらはGUIベースであり、使用するのがはるかに簡単です(そしてアセンブラが組み込まれています)。あなただけのMIPSアセンブラを習得しようとすると、単純なものをやっている場合は
は、彼らがでスタートするためのより良い選択かもしれません。私が見てきた質問のほとんどは、実際のハードウェア(通常はlinuxの下で起動)でデバッグを行います。
qemu
を使用すると、それほど多くは見られませんでした。したがって、OS要件がない場合は、mars/spim
を試してみる価値があります。私は両方を使いました。私は好きです。mars
あなたのプロジェクトがどれほど大きくなっているかによって、それはまだ一部の答えになるかもしれません(特定の関数を特定してデバッグする)。あなたの豊富な答えをhttp://courses.missouristate.edu/KenVollmar/MARS/
ありがとう:
あなたはそれを試してみることにしたい場合は、ここに火星へのリンクです。 残念ながら、ブレークポイントは命令に関係なく間違った位置に設定されています。 'jal hello'を' li $ a0、1'に置き換えることができ、何も変更されません。 コードは "手作業で"書かれており、Cまたは他の言語からコンパイルされていません。 –
'break * main'へのヒントが鍵でした。 'dissassemble main'はかなり役に立ちます。どうもありがとう。 –