アセンブラ指令 - 2024年度 システムプログラミング
アセンブラ指令
はじめに
アセンブリ言語では,通常の la
や add
といった命令と違って,
.text
や .byte
のようにドットで始まる命令が見受けられます.
これらの意味と用途について解説します.
教科書には,A.2節「アセンブラ」の「追加機能」, B.10節「MIPS R2000 のアセンブリ言語」の「アセンブラの構文規則」 で触れられています.しかし,若干難解な部分があるため,本文書にて補足をしています. 教科書も合わせて参照してください.
アセンブラ指令の意義
アセンブラの仕事は,ユーザが書いたアセンブリコードを CPU が実行すべき数値の列に変換して,メモリに配置していくことです. アセンブラがメモリに配置するのは,CPUが実行する命令だけではなく, 実行中に使用されるデータも含まれます.例えば,
printf("Hello");
をアセンブラで変換すると,
(1) printf を呼出す機械語命令列 (2) "Hello" の文字列に対応する数値の列
の2つがメモリ中に用意されることが想像できるでしょう. アセンブラプログラミングにおいて,前者を一般的に「テキスト」, 後者を「データ」と大別して呼びます. 「テキスト」といっても,文字ではなくCPU にとっての命令列(数の列)を指すことに注意して下さい.
では,上の Hello のC言語に相当するアセンブリ言語を見てみましょう.
1: .data 2: .align 2 3: msg: 4: .byte 72, 101, 108, 108, 111, 0 5: 6: .text 7: .align 2 8: main: 9: la $a0, msg 10: jal printf
4行目に書いてある 72, 101, 108 … は,Hello の文字コード(ASCIIコード)に対応する数値です.
このように, .byte
を使うと,
プログラム実行前にメモリ中にデータをあらかじめ配置できます.
アセンブラには, .byte
以外にも,
CPU が直接実行する訳ではないけれども,
プログラム実行に不可欠なデータの用意や,
メモリ中におけるプログラムの配置をコントロールする指令が各種用意されています.
これらのことをここでは,アセンブラ指令と呼びます.
このアセンブラ指令は,まるでCPUの命令のようにプログラム中に書けるので,
擬似命令と呼ばれることもあります.
しかし,MIPS プログラミングにおいて,「擬似命令」は,別の意味を持っているので,
ここではそれと区別するために,アセンブラ指令と呼ぶことにします.
よく使うアセンブラ指令
以下では,本講義でよく使用されるアセンブラ指令について解説します.
.byte b1, …, bn
b1〜bn
で示すバイト(8ビット)列をメモリに順番に配置します.
先の Helloの例のように使用します.
通常,C言語の char
型の数値と1対1で対応していると考えることができます.
前述の
.byte 72, 101, 108, 108, 111, 0
は,
.byte 'H', 'e', 'l', 'l', 'o', 0
のようにも記述できます.なぜなら,'H' は C言語と同じく 72 と同等に扱われるからです.
.word w1, …, wn
w1,...,wn
で示す32ビットの数値をメモリに順番に配置します.
通常,C言語の int型の数値と1対1で対応していると考えることができます.
.byte 0, 0, 0, 0
と書くことと
.word 0
と書くことは同等です.
.ascii str
str
をメモリに配置します.
これによって,C言語の文字列のように簡便な表現が可能になります.
前述の
.byte 72, 101, 108, 108, 111, 0
は,
.ascii "Hello\000"
と書けます.
.asciiz str
末尾に 0 が自動的に配置されることを除けば, .ascii
と同じです.
C言語の文字列を表現するときには末尾を 0 にする必要があるので,便利です.
つまり,
.ascii "Hello\000"
と書く代わりに
.asciiz "Hello"
と書けます.
.asciiで末尾にヌル文字を付ける記法は spim では時々おかしな動作をします.可能な場合は .asciiz を使うのを勧めます.
.space n
メモリ中に n バイトの領域をあらかじめ空けておきます. これによって配列のような大きなデータを保存する領域を確保できます.例えば,
array: .space 400
で,400バイト分のデータ(100個の int の配列)を用意して,
その先頭を指すアドレスを array
で参照できます.
つまり,C言語の
static int array[100];
にあたります.
int が 32ビットで,かつ static
(静的にはじめから領域が確保されている)
配列宣言のことです.
.comm symbol, n
.space
と同じで,メモリを確保します.
.data symbol: .space n
と書いたことと同じです.
.data
については,次に説明します.
.data と .text
これらの2つは,メモリ中の何処にデータやテキストを配置するかを制御するためのアセンブラ指令です. 教科書A.5節「主記憶領域の使用法」に示すように本講義で使用する SPIM では,
テキストセグメント (0x00400000〜) データセグメント (0x10000000〜)
のようなセグメント分割を行っています.
つまり,プログラム (テキスト)は, 0x00400000
番地から,
データは, 0x10000000
番地からメモリ中に並べて行くようになっています.
テキストとデータは,最終的にどちらも数値になって並びますから, アセンブラにとって,どれをどちらに配置するかは, プログラムを書いた人の指令に頼るしかありません.
そこで,もう分かると思いますが,
.text
は,以下の記述をテキストセグメントに配置せよとの指令で,
対して .data
は,データセグメントにデータを配置することを示しています.
アセンブラ命令列の前に .text
指令,
.byte 72, 101, 108, 108, 111, 0
の
前には, .data
が書かれていることで分かると思います.
1: .data 2: .align 2 3: msg: 4: .byte 72, 101, 108, 108, 111, 0 5: 6: .text 7: .align 2 8: main: 9: la $a0, msg 10: jal printf
このように,データとテキストを意識して区別するのは何故でしょう. いくつかの理由があります.
- テキストは,通常,動作中に書き変えたりしないので, データセグメントと違って,ROM (Read Only Memory) 上に配置することができる.
- 異なるプロセスで同じプログラムを実行する場合, テキストは同一なので,仮想記憶を使った共有ができる. しかし,データ部は,プロセス毎に異なるので,別の実メモリを必要とする.
詳細はオペレーティングシステムの講義に譲りますが, このようにテキストとデータを別の領域に置いて管理することが普通です. その他,メモリの保護の問題やアセンブリ言語の読み書きのしやすさ (データとそれを使うテキストが隣接してあるほうが分かりやすい)も関係します.
使用するCコンパイラによっては,.rdata
(読み出し専用データ)や
.kdata
(カーネルが使用するデータ)
といった .data
をさらに区別するアセンブラ指令もありますが,
本講義の範囲では,同等に考えて差し支えありません.
.align n
.align 2
とは,何でしょうか.以下の例を見てください.
1: .data 2: .ascii "ABC" 3: .word 0x11223344
これは,メモリにどのような数値列を配置するでしょうか. 例えば,以下のようになります.1行に4バイトづつ,'A' の ASCII コードは0x41 であることに気をつけてください.
0x10000000: 41 42 43 11 0x10000004: 22 33 44 ??
実際には,11 22 33 44
の並び順は逆かもしれませんが,
ここではそれは問題ではありません.
ここで,MIPS は 32ビットアーキテクチャであるということを思い出してください.
CPU が 32ビットの数値 (ここでは 0x11223344
) を効率よく一度に読むためには,
32bit つまり 4バイト境界のアドレスにデータが整列(align) している必要があります.
ここでは 1個の word
が被害を被るだけのように見えますが,
これより後に配置されるデータは,すべて半端な境界の上に並ぶことになります.
したがって,理想的には,
0x10000000: 41 42 43 00 (最後の 00 は使わない) 0x10000004: 11 22 33 44
となる必要があります.このためには,たとえば,
1: .data 2: .ascii "ABC" 3: .byte 0x00 4: .word 0x11223344
と書けばうまくいきそうです.
このような間を埋める .byte 00
のことをパディング(padding)といいます.
しかしながら,このパディングが何バイト必要になるかを一々計算するのは面倒です.
.ascii
で指定している文字が 1文字増えたら?…そこで,
自動的にそのパディング量を計算して隙間を空けてくれるのが, .align
です.
.align n
は 2の n
乗の境界上になるまで隙間を空けてくれる
(丁度の場合は何もしない)という便利な指令です.
1: .data 2: .ascii "ABC" 3: .align 2 4: .word 0x11223344
これでうまくいきました.
.text
や .data
の直後によく .align 2
と書いているのは,
.data
や .text
がプログラム中に複数現われた場合に,
前の領域が綺麗に4バイト境界で終わっていないことを仮定した予防です.
ちなみに,仮想記憶が絡んでくると,
仮想記憶のページ境界にデータを配置した方が効率のいい場合があります.
そのような場合は,仮想記憶のページサイズ,
たとえば4KB境界になるように, .align 12
とすることもあります.
本演習では,仮想記憶は扱いません.