•
実際にアセンブラを読んでみよう– 方法はいくつかあるが、今回は最後に生成された実行 形式ファイルを逆アセンブルしたものを確認する
•
手順– 先程、作成したフォルダ「4.1」をコピーして「4.2」
フォルダを作成
– lib.cファイルに関数を追記(P.155 リスト4.13)
– main.cに関数呼び出しを追記(P.155 リスト4.14)
– make を実行
– Cygwinで以下のコマンドを実行
$ /usr/local/h8300-elf/bin/objdump –d kzload.elf
逆アセンブルの結果
•
0000010c <_main>:– main関数の先頭
•
000004f8 <_func>:– func()関数の先頭
•
00000162 jsr @0x4f8:24– func()の呼び出し箇所だろうと想像できる
ニーモニックの細かい文法は気にせず、想像で読 んでいくことがコツ
逆アセンブルの結果
•
0000015a: mov.w #0x2, r1– r1レジスタに2を代入
•
0000015e: mov.w #0x1, r0– r0レジスタに1を代入
•
H8はR0~R7という16ビットレジスタを8個持っ ている。拡張レジスタとしてE0~E7という16 ビットレジスタがあり、これらを組み合わせて ER0~ER7という32ビットレジスタとして利用 することができる(long型整数やポインタ)main()関数部
•
79 01 00 02 mov.w #0x2, r1– 「79」はオペコード、後ろ3バイトがオペランドで、
1バイト目は値の格納先レジスタ、後は代入値 – R1に0x0002という値を代入
•
ビッグ・エンディアンとリトル・エンディアン– 「0x0002」を格納するとき、ビッグ・エンディアン
は「0x00」「0x02」、リトル・エンディアンは「0x02」、
「0x00」とひっくり返して格納する
– CPUによって異なり、H8はビッグ・エンディアン。
Pentium系CPUはリトル・エンディアン。近年の多く はビッグ・エンディアン。
main()関数部
•
イミディエイト値(即値)– オペランドにレジスタに代入する「0x0002」という値 が、そのまま記述
– 命令内に直接記述される数値
func()関数部
•
mov.l er6, @-er7– H8ではER7がスタック・ポインタとして利用される – スタック・ポインタを減算してスタック4バイト領域 を確保してスタック・ポインタの指す先にER6の値を 格納(ストア)する
– ER6が上書きされてしまうので、ER6の値(内容)を スタックに退避している
– スタックを獲得するのにER7の値を減算している。ア ドレス値が少なくなる方向に伸びること(下方伸長)
– 「@er7」レジスタの値をアドレス値として、アドレ スが指す先のメモリの意味(レジスタ間接)
→メモリ参照の方法をアドレッシング・モード
func()関数部
•
アドレッシング・モードH8は8種類のアドレッシング・モードを持つ No 、 アドレッシングモード、 記号
(1) レジスタ直接 Rn
(2) レジスタ間接 @ERn
(3) ディスプレースメント付きレジスタ間接 @(d:16, ERn)/@
(d:24, ERn)
(4) ポストインクリメントレジスタ間接@ERn+
プリデクリメントレジスタ間接@-ERn
(5) 絶対アドレス @aa:8/@aa:16/@aa:24
(6) イミディエイト #xx:8/#xx:16/#xx:32
(7) プログラムカウンタ相対 @(d:8,PC) /@(d:16, PC)
(8) メモリ間接 @@aa:8
func()関数部
•
アドレッシング・モード– 「@-er7」レジスタの加減算とメモリ・アクセスを1 命令で同時に行う
– スタック操作に2命令が利用されると、その命令の間 で割り込みが入りスタック操作された時に、スタック の整合性が取れなく可能性がある
– スタック操作は1命令で行える必要がある
func()関数部
•
mov.l er7,er6– スタック・ポインタであるER7の値をER6にコピーし ている
– ER6はフレーム・ポインタと呼ばれる使われ方をして おり、スタック・フレームの先頭を指している
– ER6もER7も、ポインタとして利用されるため、アド レスを保持する必要があるので、32ビットレジスタ が利用される。
func()関数部
•
subs #4, er7– スタック・ポインタであるER7をさらに4バイトだけ 減算して、4バイトの領域を確保
– func()の内部で利用している自然変数「c」の領域 – 「#4」の4は定数値を表すイミディエイト値(即値)
func()関数部
•
add.w r1,r0– R0とR1の値を加算して、R0に代入 – 「a +b 」の処理(1 + 2)をしている
•
mov.w r0, @(0xfffe:16, er6)– 加算結果はR0に格納されているが、自動変数「c」
はスタック上に存在
– フレーム・ポインタを減算してアドレスを計算して、
そこに加算結果を代入
func()関数部
•
mov.w @(0xfffe:16, er6), r0– スタック上に格納されている変数「c」の値をr0に代 入することでモリチの準備をし、R0を関数の戻り値 とする
– 元々、R0に「c」の値が入っていたからムダな処理 – 「c」をvolatile定義したため、最適化処理が行われ
ていない
– 「加算して、結果をスタックに保存して、戻り値を準 備する」という一連の処理が最適化されていない
func()関数部
•
adds #4, er7– スタック・ポインタを4バイト加算することで、自動 変数「c」の為に確保していたスタックを開放
•
mov.l @er7+, er6– スタック上に対比されていたER6の値をER6に読み込 んで(ロード)、ER6を元に戻す
– ロード後にスタック・ポインタであるER7は4バイト 加算
•
rts– スタック上から戻り先アドレスを取得し、ジャンプし て関数の呼び出し元に戻る