機械語は、CPUの構造に直接語り掛ける文法となっている。 プログラムはどれも計算機の主記憶上で動く。 CPUからみると、主記憶は連番のアドレスが振られた格納場所にすぎないが、 実行中のプログラムが途中で書き換えられてはならないため、 プログラムを格納するセグメントとデータを格納するセグメント、など 複数のものに分ける。
機械語そのものは数値にすぎない。その働きに名前をつけたものが ニーモニックで純粋な機械語はここまで。 プログラミングするために、アドレスや定数に名前をつけて マクロ利用できるような機能を持ったものがアセンブリ言語である。
さらに、生成された機械語をOSが正しく実行するために決められた バイナリデータの置き方、関数(サブルーチン)呼出しの規約を決めたものが ABI(Application Binary Interface)で、 以上を総合してアセンブラプログラミングとなる。
機械語命令に1対1対応するものがインストラクションで、 それに与える引数をオペランドという。インストラクションを 人間の分かる単語に置き換えたものをニーモニックという(例: MOV)。
機械語ではなくアセンブラ処理に必要なマクロなどの擬似命令がある。
擬似命令 | 用途 |
---|---|
EQU | 定数マクロ定義 |
DB, DW, DQ | バイト, ワード、Qワードデータ配置 |
メモリアクセスは 2N 切りの良いところで行なわれる。 2, 4, 8, 16バイト の境界に合わせることが必要。
たとえば0x12を8bit、16bitでそれぞれ表現すると。
0001 0020 | 0000 0000 | 0001 0020
16bitで表現された数値を8bitマシンでも読めるようにしよう
と考えると、上位桁をあとで書くと可能となる。
0001 0020 | 0001 0020 | 0000 0000
変数のように使えるものがレジスタとメモリのみ。
CPUに内蔵された値格納・計算場所で最速。
主記憶。CPUからはアドレスを表すレジスタ経由でアクセスする。 メモリ同士で計算したりすることはできず、メモリにある2つの 値の演算をしたい場合は片方をレジスタに読み込んでから行なう。
AMD64アーキテクチャのレジスタは基本が64ビットで一覧は以下のとおり。
R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 RAX RCX RDX RBX RSP RBP RSI RDI EAX ECX EDX EBX ESP EBP ESI EDI AX CX DX BX SP BP SI DI AL CL DL BL SPL BPL SIL DIL
16ビット時代からの歴史的経緯で最初の8つは各々2行目の名前で利用でき、 そちらが利用されることが多い。3行目は同じレジスタの 下位32ビットだけを使うときの名前、 4行目は同じく下位16ビット、 5行目は同じく下位8ビットを使うときの名前である。
レジスタは数が限られているがレジスタを介さないとできない演算があるの で、現在のレジスタ地をスタックに待避、スタックから復帰する命令がある。
push レジスタ pop レジスタ
とすると汎用レジスタの待避と復帰が行なえ、
pushf popf
とするとフラグレジスタの待避と復帰が行なえる。
アセンブラリスト中、
LABEL:
とコロン後置したものはラベルとして扱われその地点のアドレスが
自動的に入る。このアドレス自体の値は LABEL
で、
アドレスに格納されている値は [LABEL]
で得られる。
section .bss hello db "Hello, world!", 0 hellolen equ $-hello ; $は現在地アドレス section .text mov si, hello ; hello のアドレス mov cl, byte [hello] ; hello番地にあるバイトデータ値
フラグ | 働き |
---|---|
CF(キャリーフラグ) | 算術で繰り上がり/繰り下がりが起きたときに1 |
ZF(ゼロフラグ) | 算術結果がゼロだったときに1 |
SF(サインフラグ) | 算術結果が負だったときに1 |
OF(オーバーフラグ) | 桁溢れのときに1 |
フラグは加減乗除算、インクリメント、デクリメント等 算術演算の起こる命令後に操作される。 現状のフラグを保存したいときは pushf/popf 命令を用いる。
命令 | 働き |
---|---|
jmp | 無条件ジャンプ |
jc, jnc | キャリーが立っていたら(いなかったら)ジャンプ |
jz, jnz | ゼロフラグが立っていたら(いなかったら)ジャンプ |
js, jns | サインフラグが立っていたら(いなかったら)ジャンプ |
jo, jno | オーバーフラグが立っていたら(いなかったら)ジャンプ |
OSの具備している処理は システムコール と呼ばれ、 機能ごとに番号が定義されていて syscall 命令で呼ぶ。 /usr/include/sys/syscall.h に対応表がある。
AMD64アーキテクチャのOSではシステムコール呼出しの引数指定に レジスタを使う。
レジスタ | 代入する値 |
---|---|
EAX | システムコール番号 |
RDI | 第1引数 |
RSI | 第2引数 |
RDX | 第3引数 |
R10 | 第4引数 (一般関数の場合は R10のかわりに RCXを使う) |
R8 | 第5引数 |
R9 | 第6引数 |
RAX | 返却値が入って帰って来る |
objdump -M intel -D objectFile
で逆アセンブルされる。
インストラクション | 意味 |
---|---|
MOV DEST, SRC | DEST に SRC を代入する |
CMP OP1, OP2 | OP1 と OP2 を比較し結果をフラグに反映させる |
OR OP1, OP2 | OP1 と OP2 のビットORを取り結果をOP1に入れフラグに反映させる |
AND OP1, OP2 | OP1 と OP2 のビットANDを取り結果をOP1に入れフラグに反映させる |
XOR OP1, OP2 | OP1 と OP2 のビットXORを取り結果をOP1に入れフラグに反映させる |
ADD OP1, OP2 | OP1 と OP2 を加算し結果をOP1に入れフラグに反映させる |
SUB OP1, OP2 | OP1 から OP2 を減算し結果をOP1に入れフラグに反映させる |
MUL OP1 | AX と OP1 を乗算し結果を (RDX:RAX)の128ビットに入れフラグに反映させる |
DIV OP1 | (RDX:RAX)の128ビットをOP1 で除算し商をAX, 剰余をDXに入れフラグに反映させる |
INC OP1 | OP1を1減らす |
DEC OP1 | OP1を1増やす |
LOOP LABEL | RCXから1減らしゼロでなければ LABELにジャンプする |
LOOP LABEL | RCXから1減らしゼロでなければ LABELにジャンプする |
SYSCALL | OSのシステムコールを呼ぶ(上記参照) |
CALL LABEL | サブルーチン(関数) LABEL を呼ぶ |
RET | サブルーチンから戻る |
CMPやADD/SUB/MUL/DIVなどの演算結果とそれに応じた ジャンプ命令を組み合わせてプログラムを記述する。
これを改良する。テーマをいくつか挙げる。