「オペレーティングシステム」の版間の差分

削除された内容 追加された内容
Ef3 (トーク | 投稿記録)
→‎発展的な話題: 2022年現在のx86プロセッサは、x86命令セットをマイクロ命令に分解し、マイクロ命令の組合せによっては複合的なマイクロ命令に合成し実行しています。ところが、lodsbの様な複雑な命令はマイクロ命令に翻訳されず固定的なマイクロコードにより実行されます。このため、かつて命令読込みのオーバーヘッドがなくせるという理由で「最適化」の代表だった複雑な命令を使うことは、現在は実行速度を低下させることになります。
タグ: 2017年版ソースエディター
Ef3 (トーク | 投稿記録)
→‎発展的な話題: cleanup; 「であろう」で始まる嘘を削除
タグ: 2017年版ソースエディター
845 行
 
;コード例 『発展』
:(※ 実際に動きます。 windows7 上の qemu で動作を確認。)
<syntaxhighlight lang="asm">
org 0x7c00
 
mov ah, 0x0e
mov bxsi, aisatugreet
 
loop_top:
kurikaesi:
mov al, [bxsi]
cmp al, 0 ; 0に等しければと比較
int 0x10
je owaridone ; (0に等しければ)owari doneにジャンプしろ
int 0x10
inc si ; 1文字進める
jmp loop_top
 
greet:
add bx, 1 ; 使い終わったので 1文字ぶん、進める
db "Hello aisatugreet", 0x00 ;文字の終わりとして 0x00 を使用した
 
times 510 - ($ - $$ ) db 0
cmp al, 0 ; 0に等しければ
db 0x55 , 0xaa
je owari ; (0に等しければ)owariにジャンプしろ
done:
 
jmp kurikaesi ; (条件に関わらず)kurikaesi を実行しろ
 
; ここまでラベル kurikaesi の内容
 
aisatu:
db "Hello aisatu", 0x00 ;文字の終わりとして 0x00 を使用した
 
 
times 510 - ($ - $$ ) db 0
db 0x55 , 0xaa
 
owari:
</syntaxhighlight>
 
879 ⟶ 871行目:
org は擬似命令であり、これからの書き込みのメモリ位置を指定する命令である。
 
;<code> mov bxsi, aisatugreet </code>
実際、実験してみると、上記コードの冒頭に org 0x7c00 を削除してみて実行しても、文字が出力されない(実験すれば分かる)。qemuの実装はそうなっている。
また、 <code> mov bxsi, aisatugreet </code> は形式的には、あたかもラベルaisatugreetの内容を代入するかのような表現ですが、
 
コード例『基本』ではorg0x7c00が無くても文字出力できたのに、コード例『発展』では0rg0x7c00が無いと文字出力できないのは奇妙かもしれないが、ともかく qemu の実装がそうなっている(そして、おそらく市販のパソコンのBIOSも同様の仕様だろう。
 
;<code> mov bx, aisatu </code>
また、 <code> mov bx, aisatu </code> は形式的には、あたかもラベルaisatuの内容を代入するかのような表現ですが、
 
実際は、単にaisatu ラベルの先頭のメモリを代入するだけです。
 
なお、aisatuラベル先にある <code> db "Hello aisatu", 0x00 </code>も、単に、aisatuラベルの先頭の位置から順番に「H」「e」「l」「l」「o」 (以下略)に対応するアスキーコードを代入しているだけです。
 
 
 
bx とは、単なるbxレジスタ(ベースレジスタ)。siレジスタ(ソースインデックス)で書いてもいい。
 
というか、ソースシンデックスで書くほうが、お行儀がいいだろう。
 
 
本書wikibooksでは単にアルファベット順で「A」の次は「B」なので、ソースインデックスを紹介するのがメンドウなので、BXで代用した。
 
 
また、ラベル(たとえば aisatu ラベル)の中身を定義する前に、<code>mov bx, aisatu </code>のように、先にラベルを代入するなどの指示をする必要がある。
 
C言語になれていると宣言の順序が逆なので奇妙だが、ともかくアセンブラでは、こうである。(おそらくだが、(文法の形式ではコピー・代入だが、)実際はマシン内部では作業用メモリの確保の処理や、それらの複数のメモリ領域どうしの間の関連づけの作業をしているだけなのだろう。(そして、その結果をBIOS作業用メモリのどこかに保存しているのだろう) )
 
 
<code> mov al, [bx] </code>について。
 
仮に、bxに角カッコ<code>[ ] </code>をつけずに <code> mov al, bx </code> と書いても、まずalレジスタとbxレジスタのサイズが合わないのでアセンブル時にエラーになり、「 error: invalid combination of opcode and operands」と表示されるだけである。si(ソースインデックス)レジスタの場合でも同様である。サイズをあわせたいなら「bx」でなく「bl」にする必要がある。
 
かといってbxからblに書き換えて、
<code> mov bl, aisatu </code> とか<code> mov al, bx </code> とか <code> add bl, 1 </code> とか入力して実行しても、出力結果はワケのわからない文字列のあとに
<pre><nowiki>
(※ わけのわからない文字列がここら辺にズラズラと並んでから)0123456789:;<=>ABCDEFGHIJKLMNOPQRSTUVWXYZ[_]^`?@abcdefghijklmnopqrstuvwxyz{ (※ このあと、記号やギリシャ語のアルファベットなどの何かの文字列が並ぶ)
</nowiki></pre>
とか、途中にアスキーコード表の順序どおりのような文字列が表示されるだけである。
 
なぜこうなるかというと、 mov bl, aisatu のあとに <code> mov al, bl </code> をすると、あたかもalに aisatu ラベルを入力するかのように見えますが、そうではなくalに代入されるのは、aisatuラベルの最初のアドレスの番号です。
 
 
なので<code> add bx, 1 </code> で1文字ぶんを進めることができるわけです。
 
私たちは、アドレス番号ではなくて、そのアドレス番号のメモリにあるデータの中身(内容は文字のアスキー)を表示したいわけですので、<code> [ ] </code> でククる必要があります。
 
C言語になれていると、ついつい、何の記号もない「変数っぽい?」何かは(勘違い→)「きっとアドレス番号ではない」と思い勝ちですが、しかしアセンブラではむしろ逆で、movm命令の2番目の引数は、記号のついてないほうがアドレス番号の解釈になります。
 
 
また、代入される側(上記の例では al )は、代入された数値の由来は不明であり、数値の由来がはたしてアドレス番号に由来なのか、あるいは、電卓みたいな加減算をしたいのか、なんにも知りません。
 
たとえば、もし bl レジスタに保管されている(メモリの)アドレス番号が200番だとして、 <code> mov al, bl </code>をしたら、alに200が保存されますが、しかしalはその「200」が何なのか知りません。200円の価格の商品の計算なのか、全校生徒が200人の小学校なのか、アドレス200番なのか、alには、知るよしが無いのです。
 
実際は、単にaisatugreet ラベルの先頭のメモリを代入するだけです。
 
;int 0x10
int 0x10 は、繰返し命令の中で、毎回、使用する必要がある。
 
 
さて、もし上記コード中の int 0x10 の位置を移動して、繰り返しの終了後に移動して「owari」ラベルで int 0x10 をまとめて実行しようとしても、文字出力はされない(実験すれば、そうなる)。
 
 
このことから、int 0x10 は、単にディスプレイ出力をするだけの命令ではなく、文字出力に必要なメモリ処理をしていることが分かる(※ もし、単にディスオプレイの画面後進をするだけなら、あとでマトメて処理してもいいハズになる)。おそらく org 0x7c00 で宣言したメモリ領域以降に、情報を書き込んでいるのだろう。
 
「cmp」とは比較命令で、等しいかどうかを調べて報告する命令で、出力は「真」か「偽」のどちらかであり、等しければ真、等しくなければ偽である。
 
そして、「je」とは条件つきジャンプで、直前の条件判定が「真」なら、指定したラベルにジャンプする命令である。
 
 
 
;その他の例