faq/asm
の編集
http://hrb.osask.jp/wiki/?faq/asm
[
リロード
|
差分
|
単語検索
|
一覧
] [
編集
|
バックアップ
|
添付
]
-- 雛形とするページ --
A
Akkie
Athlon64X2
Clover
DAsoran
Falcon
FormatRule
FrontPage
Genesis
Help
I
InterWikiName
InterWikiSandBox
InterWikiテクニカル
Jormungand
K
Kebo
Kor_Lee_Hee_Rak
Leaf
Linux
Linux/wako_memo
MOIZ99
MW
MenuBar
OSC
PG_MANA
ReadersOS
RecentDeleted
SKYDASH
SandBox
Sero
Sigle
Source
Triangle_Ld.
Zxcvbnm
advance
advance/CPU
advance/FDC
advance/FPU
advance/NotHariMain
advance/QEMUVGA
advance/RTC
advance/blike
advance/cpu_reset
advance/driver
advance/driver/01
advance/driver/02
advance/families
advance/filesystem
advance/fwrite
advance/hddboot
advance/he86
advance/hints
advance/ipl
advance/kernel
advance/keycode
advance/osselect
advance/smaller1
advance/startup
advnace/smaller2
anzy
aotatsu
banbi-
bluedwarf
bo
bugs
challengers
cybozulabsyouth11
deskmanta
esb02b
faq
faq/advance
faq/asm
faq/c00-03
faq/c04-07
faq/c08-15
faq/c16-23
faq/c24-31
faq/make
faq/others
faq/qemu
guide
guide03
guide05
guide07
hikarupsp
imp_log/0000
imp_log/0001
imp_log/0002
imp_log/0003
impressions
index
k
killer_elf
kota
lea
lea/10_memory
lea/4_color
lea/idea
lea/terms
links
logs
logs/osa_hrb/comments0000
logs/osa_hrb/rumors0000
masa
members
message
mistakes
moge32
moppoi5168
notice
osdevjp
populars
prog_index
projects
q_and_a
q_and_a_2
qa_log/0000
qa_log/0001
qa_log/0002
qa_log/0003
qa_log/0004
qa_log/0005
qa_log/0006
qa_log/0007
qa_log/0008
qa_log/0009
quark
rankings
rule
sakamoto
sasaki
spc09
spcc_30min_os
tatsu
tools
tools/bim2hrb
tools/bin2obj
tools/cc1
tools/edimg
tools/gas2nask
tools/makefont
tools/nask
tools/obj2bim
tools/sjisconv
uchan
uho
updates
violations
wako
white
win64-bit
x
ytakano
ヘルプ
リックス
質問します
整形ルール
本は買ったぞ!持ってるぞ!
練習用ページ
* アセンブラと機械語の対応が分からないよ、でも知りたいよ!のときに読むページ -(by [[K]], 2006.04.19) -いろいろQ&Aが集まったと思うので、ここにまとめておこうと思います。 -ここで答えが見つからなければ、[[q_and_a]]へ! *** 前書き -OSを作るという立場では、それぞれのアセンブラ命令がどんな機械語になるかなんてほとんど重要ではありません。それに、naskが出力する.lstファイルを見れば、この命令はこんなふうに翻訳されるんだーってことも簡単に確認できます。それで満足するのが(=あまりその方面には深追いしないのが)この本の想定読者だったので、それ以上の詳しいことは本文中には書いてきませんでした。 -しかしもっと知りたいと思うことが悪いわけじゃありません。そこで、ここにそういう人がそれなりに満足できるような説明を書いておくことにします。なお、ここにあるのは全体のうちのごく一部ですが、これくらいが分かればあとは.lstの結果と類推でなんとなく分かるはずです。 *** パラメータのない命令 |CLI|HLT|IRETD|NOP|RET|RETF|STI| |FA|F4|CF|90|C3|CB|FB| *** レジスタ番号表 -reg8 |AL|CL|DL|BL|AH|CH|DH|BH| |0|1|2|3|4|5|6|7| -reg16 |AX|CX|DX|BX|SP|BP|SI|DI| |0|1|2|3|4|5|6|7| -reg32 |EAX|ECX|EDX|EBX|ESP|EBP|ESI|EDI| |0|1|2|3|4|5|6|7| -sreg |ES|CS|SS|DS|FS|GS| |0|1|2|3|4|5| *** 一般の命令 -CALL 定数 ([BITS 32]のとき) | E8 | EIPに足す値(4バイト) | --たとえば0x1234番地の関数の呼び出す場合、 E8 34 12 00 00 になるわけではない。E8の後に書かれる数値は、EIPに足し算するべき値である。たとえば、このCALL命令が0x2345番地にあるならCALL命令を読み取った直後のEIPの値は0x234aになっているはずなので、機械語は E8 EA EE FF FF になる(0xffffeeeaは-4374を意味していて、 0x234a + (-4374) = 0x1234 であるから)。 ~ ~ -INT 定数 | CD | 定数(1バイト) | --INT 3 に限っては、CCという1バイト命令でもよい(もちろん CD 03 でもいい)。 ~ ~ -JMP 定数 ([BITS 32]のとき) | E9 | EIPに足す値(4バイト) | --EIPに足す値が-128〜+127の範囲のときは、 | EB | EIPに足す値(1バイト) | という形式も使える。 ~ ~ -MOV reg8,定数 | B0 + レジスタ番号 | 定数(1バイト) | ~ -MOV reg16,定数 ([BITS 16]のとき) | B8 + レジスタ番号 | 定数(2バイト) | ~ -MOV reg32,定数 ([BITS 32]のとき) | B8 + レジスタ番号 | 定数(4バイト) | ~ -MOV reg16,sreg([BITS 16]のとき) | 8C | C0 + セグメントレジスタ番号 * 8 + レジスタ番号| ~ -MOV sreg,reg16 | 8E | C0 + セグメントレジスタ番号 * 8 + レジスタ番号| --ちなみに MOV sreg,定数 という機械語はない ~ ~ -POP reg32 | 58 + レジスタ番号 | ~ -PUSH reg32 | 50 + レジスタ番号 | ~ *** 補足 -[BITS 32]のときに MOV AX,0x1234 を翻訳するとどうなるの?・・・頭に66が追加されます。というかこの程度のことは自分でちょっとプログラムを書いて、naskの.lstを見て自分で突き止めましょう。そうすれば力がつきますよ。 --66は BITS 16/32 を次の1命令に限って逆にする機械語です。 *** なぜ? -COLOR(#0000ff){どうしてMOV命令は代入したいレジスタによっていろいろな機械語になってしまうんだろう?} | B0 | レジスタ番号 | 定数 | COLOR(#0000ff){みたいにすれば分かりやすくて良さそうなのに。} --よい質問です。実はそのような機械語もあります。たとえば8ビットレジスタへの代入なら、 | C6 | C0 + レジスタ番号 | 定数(1バイト) | というのがありますし、32ビットレジスタへの代入なら、 | C7 | C0 + レジスタ番号 | 定数(4バイト) | というのがあるのです。確かにこの形式のほうが人間には分かりやすいとはいえるでしょう。 --しかしこの書き方だとMOV命令は3バイト命令や6バイト命令になってしまいます。MOV命令はとてもよく使う命令なので、もし短く書ける形式があるのなら、そちらを使うほうがプログラムが短くなって、動作が速くなります(註:キャッシュヒット率が上がるので)。それでアセンブラは基本的に短く書ける形式のほうの機械語しか出力しないようになっています。 --同様にPUSHやPOPも2バイト形式がありますが、やはり同じ理由で1バイト形式を使うのが普通です。 -COLOR(#0000ff){どうしてINT命令は INT 3 だけ1バイト形式が用意されているんだろう? INT 3 ってそんなによく使う命令なのかなあ。だとしたらなんに使うの?} --INT 3 は例外の割り込み番号の一つで、ブレークポイント割り込みです。これは何かというと、今ここにバグがありそうな機械語のプログラムがあって、特に0x1234番地の命令を実行するときが怪しいとしましょう。だからそのときのレジスタの値を画面に表示させてチェックしようと考えました。そんなとき、0x1234にJMP命令とかCALL命令を置いて、レジスタ表示ルーチンを呼び出せばいいでしょう。ああしかし、実は0x1234番地の命令は1バイト命令(たとえばRETとか)で、その次の0x1235番地からはテストに必要なデータが入っているんですよ。だからもし0x1234番地に5バイト命令のCALLなんて書いてしまったら、テスト結果がおかしくなってしまいます。普通のINT命令なら2バイトで済みますが、それでも長すぎます。 --もちろんアセンブラのソースが手元にあればそこに命令を追加してアセンブルしなおせばいいのかもしれませんが、命令を挿入すると番地がずれて結果が変わることもたまにはありますし、アセンブラのソースはないけど、とにかくバグを見つけて報告しなければいけない、というむごい状況もたまにはあるのです。そんなときに1バイトで関数を呼び出す機能があれば、ものすごく重宝なのです。 INT 3 はそのときのための割り込み命令です。だからよく使うわけでもないのに、1バイト形式が特別に用意されているわけです。 --そんなわけで、「お、1バイトでかけるINT命令があるのか。じゃあこれをAPIの呼び出しに使ったら、アプリケーションはきっと短くなるぞ。APIを呼び出すためのINTはアプリケーションの中に何度も何度も出てくるから、これは結構効果がありそうだ。」なんて考えちゃダメなんです。そんなことしたら、バグを見つけるための INT 3 とAPIの INT 3 が区別できなくなるので、事実上、バグを見つけるための INT 3 をあきらめなきゃいけないことになり、「なんだよこのOSはデバッグのために INT 3 を使うことはできないのかよ、不便だなあ。」って言われちゃうことになるかもしれないわけです。 -COLOR(#0000ff){どうしてCALL命令やJMP命令では、EIPに足し算するような機械語なんだろう? 分かりにくいし、CPUは代入の代わりに内部で計算しなきゃいけなくなるから、かえって疲れちゃう気がするんだけど。} --まずJMP命令から。JMP命令はgotoに相当する命令ですが、for (;;) などでも使われます。いずれにしても、たいていの場合、関数の中の別の部分への移動がほとんどなので、行き先は100バイト前とか、123バイト後など、現在地からそれほど遠くないことが多いです。そこでインテルのおじさんたちは、飛びたい番地ではなくEIPに足したい数を機械語では書くことにして、しかも距離が短いときは足したい数を signed char で(つまり1バイトで)書いていいことにしました。これにより、距離が近い多くのJMP命令は2バイト命令になって、機械語は短くなりました。 --距離が遠いものはEIPに足す値を4バイトで書かないといけないので5バイト命令になりますが、そのときも値は行きたい番地ではなくEIPに足す値で指定します。これは同じJMP命令なのに足したり代入したりというややこしい回路にするよりも、全部足し算で統一したほうがラクだったのでしょう。 --CALL命令はJMP命令とほとんど同じで、違うのはEIPを更新する前に PUSH EIP を実行するかどうかだけなので、JMP命令と同じ仕様にしたのだと思います。しかしCALLでは2バイト形式はありません。5バイト命令だけです。 --このように実際の番地を指定するのではなく、EIPへの加算量で指定する方法を「相対ジャンプ」といいます。 --なお、far-CALLやfar-JMP命令ではどちらもEIPへの足し算ではなく、EIPとCSへの代入になっています。これはその名のとおり遠くへのCALL/JMPなので、1バイト形式を作る意味がなく、それゆえに単純な代入形式だけになったのでしょう。このような普通の指定方法のことを「絶対ジャンプ」といいます。 * こめんと欄 -オペコードの頭に0x66がつく話はもうちょい解説してあげるべきではないでしょうか。とは言え私もOperand override prefixの詳しい仕様がどこにあるかしらないのですが…(はじめて読む486にもさっぱり書いていない) -- ''永田'' SIZE(10){2016-12-23 (金) 17:09:18} #comment
タイムスタンプを変更しない
* アセンブラと機械語の対応が分からないよ、でも知りたいよ!のときに読むページ -(by [[K]], 2006.04.19) -いろいろQ&Aが集まったと思うので、ここにまとめておこうと思います。 -ここで答えが見つからなければ、[[q_and_a]]へ! *** 前書き -OSを作るという立場では、それぞれのアセンブラ命令がどんな機械語になるかなんてほとんど重要ではありません。それに、naskが出力する.lstファイルを見れば、この命令はこんなふうに翻訳されるんだーってことも簡単に確認できます。それで満足するのが(=あまりその方面には深追いしないのが)この本の想定読者だったので、それ以上の詳しいことは本文中には書いてきませんでした。 -しかしもっと知りたいと思うことが悪いわけじゃありません。そこで、ここにそういう人がそれなりに満足できるような説明を書いておくことにします。なお、ここにあるのは全体のうちのごく一部ですが、これくらいが分かればあとは.lstの結果と類推でなんとなく分かるはずです。 *** パラメータのない命令 |CLI|HLT|IRETD|NOP|RET|RETF|STI| |FA|F4|CF|90|C3|CB|FB| *** レジスタ番号表 -reg8 |AL|CL|DL|BL|AH|CH|DH|BH| |0|1|2|3|4|5|6|7| -reg16 |AX|CX|DX|BX|SP|BP|SI|DI| |0|1|2|3|4|5|6|7| -reg32 |EAX|ECX|EDX|EBX|ESP|EBP|ESI|EDI| |0|1|2|3|4|5|6|7| -sreg |ES|CS|SS|DS|FS|GS| |0|1|2|3|4|5| *** 一般の命令 -CALL 定数 ([BITS 32]のとき) | E8 | EIPに足す値(4バイト) | --たとえば0x1234番地の関数の呼び出す場合、 E8 34 12 00 00 になるわけではない。E8の後に書かれる数値は、EIPに足し算するべき値である。たとえば、このCALL命令が0x2345番地にあるならCALL命令を読み取った直後のEIPの値は0x234aになっているはずなので、機械語は E8 EA EE FF FF になる(0xffffeeeaは-4374を意味していて、 0x234a + (-4374) = 0x1234 であるから)。 ~ ~ -INT 定数 | CD | 定数(1バイト) | --INT 3 に限っては、CCという1バイト命令でもよい(もちろん CD 03 でもいい)。 ~ ~ -JMP 定数 ([BITS 32]のとき) | E9 | EIPに足す値(4バイト) | --EIPに足す値が-128〜+127の範囲のときは、 | EB | EIPに足す値(1バイト) | という形式も使える。 ~ ~ -MOV reg8,定数 | B0 + レジスタ番号 | 定数(1バイト) | ~ -MOV reg16,定数 ([BITS 16]のとき) | B8 + レジスタ番号 | 定数(2バイト) | ~ -MOV reg32,定数 ([BITS 32]のとき) | B8 + レジスタ番号 | 定数(4バイト) | ~ -MOV reg16,sreg([BITS 16]のとき) | 8C | C0 + セグメントレジスタ番号 * 8 + レジスタ番号| ~ -MOV sreg,reg16 | 8E | C0 + セグメントレジスタ番号 * 8 + レジスタ番号| --ちなみに MOV sreg,定数 という機械語はない ~ ~ -POP reg32 | 58 + レジスタ番号 | ~ -PUSH reg32 | 50 + レジスタ番号 | ~ *** 補足 -[BITS 32]のときに MOV AX,0x1234 を翻訳するとどうなるの?・・・頭に66が追加されます。というかこの程度のことは自分でちょっとプログラムを書いて、naskの.lstを見て自分で突き止めましょう。そうすれば力がつきますよ。 --66は BITS 16/32 を次の1命令に限って逆にする機械語です。 *** なぜ? -COLOR(#0000ff){どうしてMOV命令は代入したいレジスタによっていろいろな機械語になってしまうんだろう?} | B0 | レジスタ番号 | 定数 | COLOR(#0000ff){みたいにすれば分かりやすくて良さそうなのに。} --よい質問です。実はそのような機械語もあります。たとえば8ビットレジスタへの代入なら、 | C6 | C0 + レジスタ番号 | 定数(1バイト) | というのがありますし、32ビットレジスタへの代入なら、 | C7 | C0 + レジスタ番号 | 定数(4バイト) | というのがあるのです。確かにこの形式のほうが人間には分かりやすいとはいえるでしょう。 --しかしこの書き方だとMOV命令は3バイト命令や6バイト命令になってしまいます。MOV命令はとてもよく使う命令なので、もし短く書ける形式があるのなら、そちらを使うほうがプログラムが短くなって、動作が速くなります(註:キャッシュヒット率が上がるので)。それでアセンブラは基本的に短く書ける形式のほうの機械語しか出力しないようになっています。 --同様にPUSHやPOPも2バイト形式がありますが、やはり同じ理由で1バイト形式を使うのが普通です。 -COLOR(#0000ff){どうしてINT命令は INT 3 だけ1バイト形式が用意されているんだろう? INT 3 ってそんなによく使う命令なのかなあ。だとしたらなんに使うの?} --INT 3 は例外の割り込み番号の一つで、ブレークポイント割り込みです。これは何かというと、今ここにバグがありそうな機械語のプログラムがあって、特に0x1234番地の命令を実行するときが怪しいとしましょう。だからそのときのレジスタの値を画面に表示させてチェックしようと考えました。そんなとき、0x1234にJMP命令とかCALL命令を置いて、レジスタ表示ルーチンを呼び出せばいいでしょう。ああしかし、実は0x1234番地の命令は1バイト命令(たとえばRETとか)で、その次の0x1235番地からはテストに必要なデータが入っているんですよ。だからもし0x1234番地に5バイト命令のCALLなんて書いてしまったら、テスト結果がおかしくなってしまいます。普通のINT命令なら2バイトで済みますが、それでも長すぎます。 --もちろんアセンブラのソースが手元にあればそこに命令を追加してアセンブルしなおせばいいのかもしれませんが、命令を挿入すると番地がずれて結果が変わることもたまにはありますし、アセンブラのソースはないけど、とにかくバグを見つけて報告しなければいけない、というむごい状況もたまにはあるのです。そんなときに1バイトで関数を呼び出す機能があれば、ものすごく重宝なのです。 INT 3 はそのときのための割り込み命令です。だからよく使うわけでもないのに、1バイト形式が特別に用意されているわけです。 --そんなわけで、「お、1バイトでかけるINT命令があるのか。じゃあこれをAPIの呼び出しに使ったら、アプリケーションはきっと短くなるぞ。APIを呼び出すためのINTはアプリケーションの中に何度も何度も出てくるから、これは結構効果がありそうだ。」なんて考えちゃダメなんです。そんなことしたら、バグを見つけるための INT 3 とAPIの INT 3 が区別できなくなるので、事実上、バグを見つけるための INT 3 をあきらめなきゃいけないことになり、「なんだよこのOSはデバッグのために INT 3 を使うことはできないのかよ、不便だなあ。」って言われちゃうことになるかもしれないわけです。 -COLOR(#0000ff){どうしてCALL命令やJMP命令では、EIPに足し算するような機械語なんだろう? 分かりにくいし、CPUは代入の代わりに内部で計算しなきゃいけなくなるから、かえって疲れちゃう気がするんだけど。} --まずJMP命令から。JMP命令はgotoに相当する命令ですが、for (;;) などでも使われます。いずれにしても、たいていの場合、関数の中の別の部分への移動がほとんどなので、行き先は100バイト前とか、123バイト後など、現在地からそれほど遠くないことが多いです。そこでインテルのおじさんたちは、飛びたい番地ではなくEIPに足したい数を機械語では書くことにして、しかも距離が短いときは足したい数を signed char で(つまり1バイトで)書いていいことにしました。これにより、距離が近い多くのJMP命令は2バイト命令になって、機械語は短くなりました。 --距離が遠いものはEIPに足す値を4バイトで書かないといけないので5バイト命令になりますが、そのときも値は行きたい番地ではなくEIPに足す値で指定します。これは同じJMP命令なのに足したり代入したりというややこしい回路にするよりも、全部足し算で統一したほうがラクだったのでしょう。 --CALL命令はJMP命令とほとんど同じで、違うのはEIPを更新する前に PUSH EIP を実行するかどうかだけなので、JMP命令と同じ仕様にしたのだと思います。しかしCALLでは2バイト形式はありません。5バイト命令だけです。 --このように実際の番地を指定するのではなく、EIPへの加算量で指定する方法を「相対ジャンプ」といいます。 --なお、far-CALLやfar-JMP命令ではどちらもEIPへの足し算ではなく、EIPとCSへの代入になっています。これはその名のとおり遠くへのCALL/JMPなので、1バイト形式を作る意味がなく、それゆえに単純な代入形式だけになったのでしょう。このような普通の指定方法のことを「絶対ジャンプ」といいます。 * こめんと欄 -オペコードの頭に0x66がつく話はもうちょい解説してあげるべきではないでしょうか。とは言え私もOperand override prefixの詳しい仕様がどこにあるかしらないのですが…(はじめて読む486にもさっぱり書いていない) -- ''永田'' SIZE(10){2016-12-23 (金) 17:09:18} #comment
テキスト整形のルールを表示する