6. アセンブラ
最終更新日 2015年4月8日
アセンブリ言語とは、機械語の命令と1対1に対応させることができる言語であり、CPUの種類 により異なる。そこで本実験では、情報処理技術者試験でも利用されているアセンブリ言語 CASLⅡを用いて、日頃使っているC言語のプログラムがどのような機械語に変換されているか、
例題を通して学ぶ。CASLⅡは仮想計算機COMETIIのための言語である。
まず予習として以下のサイトをみて、仮想計算機COMETII のレジスタの個数や種類、簡単なプロ グラムの書き方などを確認しておくこと。
http://masudahp.web.fc2.com/casl2/casl2010.html
レポート課題は例題を少し発展させたものになっている。まずは例題をしっかり理解した上で課 題に取り組みなさい。
例題では、C言語のプログラム例とそれに対応するCASLⅡのプログラムを示す。
シミュレーターCASL2000( http://www5a.biglobe.ne.jp/~teamcasl/ )上で実行させ、プログ ラムの挙動を理解せよ。新宿校舎の演習室のPCにはこのソフトがインストールされている。
例題1 Hello World!
これをCASLⅡで書くと以下のようになる。
; 「Hello, World!」の文字列を出力するプログラム MAIN START
OUT BUF,LEN ; 文字列を出力
RET ; 実行を終了 BUF DC 'Hello, World!' ; 文字列 LEN DC 13 ; 長さ END
/*「Hello, World!」の文字列を出力するプログラム */
int main(){
printf(“Hello, World!”);
return(0);
}
CASLⅡでは命令を1行ずつ ラベル、命令、引数の順に記述する。ラベルはメモリ上の場所を指 定するために利用する。同じ行で;より後ろの部分はコメントになる。
下図のようにCASL2000のソースエディタにCASLIIのデータを入力し、CASLからアセンブルを 選択せよ。するとCOMETシミュレータとアセンブラ出力という二つのウィンドウが現れる。
COMETシミュレータは現在実行中の場所、レジスタの内容などが確認でき、アセンブラ出力から は機械語とアセンブリ言語の対応が示される。
• START命令、END命令はプログラムの開始場所と終わりを定義する。
• DCはデータ領域を確保して初期値を入れる命令、
• OUTは文字列を出力する命令である。第一引数に文字列が格納されている先頭のアドレス(ラ ベル)を指定し、第二引数で文字列の長さが格納されているアドレスを指定する。
• RETはリターン命令で呼び出し元のプログラムへ戻る命令である。DC命令が指定している部分 はデータが格納される所であり、これがないと下のデータ領域まで実行を続けてしまう。
CASLIIの命令はアセンブラ命令、マクロ命令、機械語の命令(COMETIIの命令)からなる。
アセンブラ命令はアセンブラの制御に利用し、アセンブラ出力には直接は現れない。
START命令、END命令、DC命令はそれぞれアセンブラ命令であり、これらの命令はアセンブラ 出力を見ると、機械語には翻訳されていないことがわかる。
マクロ命令は複数の機械語の命令に置き換えられる命令である。よく利用される機能を実現する ため、特定のマクロ命令が用意されている。このプログラム例では、OUT命令がマクロ命令の一 つとなっている。
CASL2000で実行するためには、COMETシミュレータを選択し、上の「COMET」メニュー から「通常実行」を選択すると、生成された機械語を仮想計算機COMETIIの上で実行する。メモ リーダンプを選択すると、実行後のメモリーの状態を表示する。
変数の値を確認するためには、アセンブラ出力からメモリのどこに保存されているかアドレスを 確認し、メモリーダンプによってそこのアドレスに入っている値を確認する必要がある。
例題2 変数、定数、加算
次に変数と定数、加算などの命令について見てみる。C言語で以下のようなプログラムがあったと する。
/*変数と加算 */
int main(){
int X;
X=3+4;
return(0);
}
CASLⅡでは以下のプログラムに対応する。
(通常のCPUでは、関数内の変数はスタック上に確保するが、CASLⅡではスタックポインタを 明示的に操作しにくいので、このレポートでは変数をレジスタに置くか、もしくはメモリの中で スタックとは別に確保している)
• DCは定数の記録領域作成の命令で、
• DSは変数の領域確保の命令である。DSの引数は1語(16ビット)を単位として何ワード分確 保するかを示す。
これらのDCやDSはアセンブラ命令で、命令部分は実行されない。他の命令からアドレスを経由し て参照される。
• LD命令は第二引数に指定したアドレスのデータを第一引数のレジスタに格納する。
• ADDA命令は第一引数に指定したレジスタに、第二引数の指定しているデータを算術加算する。
• ST命令は第一引数に指定したレジスタの値を第二引数に指定したアドレスに書き込む。
変数はそれぞれDS命令でその領域用の箱が用意される。また、Hello Worldでもこの例題でもわ かるが、定数はメモリ上にその値として確保され、参照される。
例題3 最大値の探索
配列に5個整数のデータがあり、変数maxに最大値を入れるプログラムでC言語では以下のよう な形とする。
; 変数、定数、加算のプログラム MAIN START
LD GR1,A ; GR1=3
ADDA GR1,B ; GR1=GR1+B
ST GR1,X ; C=GR1
RET ; 実行を終了
A DC 3 ; 定数3
B DC 4 ; 定数4 X DS 1 ; 変数X END
これをCASLIIで記述すると次のようになる。
• LAD(Load ADdress)命令は第二引数のデータをアドレスとして第一引数のレジスタに入れる。
第二引数に値を指定することも可能で、その場合はレジスタの内容をその引数の値に代入される。
/* 最大値の探索 */
int main(){
int data[]={10,15,8,20,7};
int max, counter;
counter=0;
max=data[0];
do{
counter=counter+1;
if(max<data[counter]) max=data[counter];
}while(counter<4) return(0);
}
; 最大値の探索 MAIN START
LAD GR2,0 ; GR2=0
ST GR2,COUNTER ;COUNTER=0
LD GR1,KOSUU ; GR1=4
LD GR0,DATA ;GR0=data[0]
ST GR0,MAX ;max=GR0
LOOP LAD GR2,1,GR2 ;GR2=GR2+1 LD GR0,DATA,GR2 ;GR0=data[GR2]
CPA GR0,MAX ;比較:GR0 - MAXを計算
JMI SKIP; ;Jump on minus
ST GR0,MAX ;MAX=GR0
SKIP CPA GR1,GR2 ;比較:GR1-GR2を計算
JPL LOOP ;Jump on plus
ST GR2,COUNTER ;COUNTERへデータの書き戻し
RET ; 実行を終了
KOSUU DC 4 ; 定数4. 0番目から数えた個数
DATA DC 10,15,8,20,7 ; 定数の配列 MAX DS 1 ; 変数max COUNTER DS 1 ; 変数counter END
• LAD命令で三つ引数があるときは第三引数と第二引数を足したものを第一引数に入れる。
• LD命令は第三引数があるときは、その値分の語(16ビット)を第二引数のアドレスに足し込 んで、それに対応するアドレスからデータを取り出す。
• CPA命令は第一引数から第二引数を引き、その結果の符号を使って条件分岐に利用する。
• JMI(Jump Minus)命令があると、その前のCPA命令での差の符号がマイナスのとき、
JPL(Jump Plus)命令のときは符号が+のとき(0より大きい)に指定のラベル(アドレス)に ジャンプする。JZE(Jump on Zero)とJNZ(Jump on non-zero)もあり、CPAの差の結果がゼ ロになるかどうかで分岐させることもできる。
C言語のソースにはwhile文とif文があるが、そのため2カ所CPAがあり、その後、条件によりジャ ンプ命令がある。
例題4 関数化
最大値を探索するプログラム(例題3)を関数化したプログラムを考える。まずはC言語版をし
めす。
次ページにCASLIIの例を示す。
関数の引数はここでは、レジスタにのせて渡すこととする。
このプログラムの場合、0から数えたときのデータの個数(KOSUU), 配列DATAのアドレス、変 数maxのアドレスの三つのデータをsaidai関数に渡す必要がある。これらをレジスタGR1, GR2, GR3にのせて渡している例を示す。
/* 最大値の探索 */
void saidai(int kosuu, int data[], int *max){
int counter=0;
*max=data[0];
do{
counter=counter+1;
if(*max<data[counter]) *max=data[counter];
}while(counter<kosuu);
return;
}
int main(){
int data[]={10,15,8,20,7};
int max, kosuu=4;
saidai(kosuu, data, &max);
return(0);
}
関数内では、さらにGR4, GR5も変数として利用するため、GR1-GR5までスタックに格納し、関 数終了後にスタックから回復する処理をしている。これにより、レジスタの値を関数を呼び出し 前(CALL命令が実行される前)と同じ値に戻すことができる。
; 最大値の探索
MAIN START ; main関数スタート
LD GR1,KOSUU ; GR1=4
LAD GR2,DATA ; GR2=address of DATA LAD GR3,MAX ; GR3=address of MAX
CALL SAIDAI ;
RET ;
KOSUU DC 4 ; 定数4. 0番目から数えた個数
DATA DC 10,15,8,20,7 ; 定数の配列 MAX DS 1 ; 変数max
END ; main関数終了
SAIDAI START
PUSH 0,GR1 ;KOSUU
PUSH 0,GR2 ;&data[0]
PUSH 0,GR3 ;&max
PUSH 0,GR4 ;counter
PUSH 0,GR5 ;配列データの格納のため利用
LAD GR4,0 ; GR4=0
LD GR5,0,GR2 ; GR5=data[0]
ST GR5,0,GR3 ; max=GR5 LOOP LAD GR2,1,GR2 ; data=data+1
LAD GR4,1,GR4 ; counter=counter+1 LD GR5,0,GR2 ; GR5=*data
CPA GR5,0,GR3 ; 比較:GR5 - MAXを計算 JMI SKIP ; Jump on minus
ST GR5,0,GR3 ; MAX=GR5
SKIP CPA GR1,GR4 ; 比較:KOSUU-counterを計算
JPL LOOP ; Jump on plus
POP GR5 ;PUSHした順番と逆順でPOP命令を
POP GR4 ;実行する。
POP GR3 ;これによりレジスタの前の値を
POP GR2 ;復元している。
POP GR1 ;
RET ; 実行を終了 END
• PUSH命令は、アドレスを記録することもできるため、指標となるアドレスから少しずらしたア ドレスを格納することができる。
PUSH 0,GR1 は0+GR1の値をスタック上に保存する。
• POP GR1はプッシュされている値をスタックから取り出し、GR1に入れる命令である。
• LD GR1,address,GR2 はaddressからGR2分だけずらしたアドレスにあるのデータをGR1に 入れる。LD GR5,0,GR2 はGR2に入っているアドレスからデータを取り出し、GR5に格納する。
• ST GR5,0,GR3 はGR5に入っているデータをアドレス(0+GR3)へ書き込む。
• CPA GR5,0,GR3 はGR5からアドレス(0+GR3)に入っているデータを引き、符号フラグを セットする。これはあとの条件分岐に利用される。
これまでの例題で説明した命令を一覧にする。必要に応じて、対応する例題の説明を見よ。また レジュメの最初で紹介したサイトなど、ネット上に説明があるのでそれも参考にせよ。
START 例題1
END 例題1
RET 例題1
OUT 例題1
DC 第一引数 例題1,2
DS 第一引数 例題2
LD 引数1 引数2 例題2
ADDA 引数1 引数2 例題2
ST 引数1 引数2 例題2
LAD 引数1 引数2 引数3 例題3
LAD 引数1 引数2 例題3
CPA 引数1 引数2 例題3
JMI 引数1 JPL 引数1 JNZ 引数1 JZE 引数1
例題3
POP 引数1 例題4
CPA 引数1 引数2 引数3 例題4
PUSH 引数1 引数2 例題4
LD 引数1 引数2 引数3 例題3,4
ST 引数1 引数2 引数3 例題4
レポート課題
各グループに課題を用意した。それぞれのグループに対応する課題を考え、また時間の許す限り発 展課題についてもレポートにまとめよ。
レポートには、各課題のC言語のプログラムとCASLIIのプログラムを含め、さらに、CASL2 000上での実行結果を画面キャプチャなどを行い、正しい結果が出ていることを示せ。プログ ラムの処理の流れ、CとCASLIIコードの対応も説明せよ。
課題の目的:
・ 機械語には一般的にどのような機能の命令が用意されているか学ぶ。
・ C言語のプログラムがどのようなアセンブリ言語に変換されるか、自分で変換することを通じ て理解する。
表紙交付基準:
・ 各自が作成したCとCASLIIコードが正しい結果を出力している ・ それぞれのコードの処理の内容、対応を説明できる
他の人が作成した課題の答えのコードをコピーすることは当然、不正行為に該当する。必ず自分 で考えながらコードを作成すること。不正行為には厳正に対処する。
Aグループ課題レポート
C言語での関数の形としては
void sum(int kosuu, int data[], int* sum)
をイメージし、合計値を求める関数をC言語のプログラムとCASLIIで作成せよ。
例題4と同様に
kosuuは配列のサイズ-1の値
dataには配列の先頭アドレスを示し sumには合計を記録する変数のアドレス が入っているとする。
GR1からGR3に上記の3つの引数のデータをそれぞれのせて、合計値を求める関数を作成し、そ れを呼び出すプログラム(メインプログラム)も含めて作成せよ。
メインプログラムでは、以下の二つの配列に対して、それぞれ合計値を求める処理を記述せよ。
{10,15,8,30,7}
{10,20,30,40,50、60}
答えはどこかの変数に記録されるようにし、メモリーダンプにより内容が確認できるようにせよ
Bグループ課題レポート
例題4では整数の配列の中で、最大値を探索した。この課題では、それに加えて、同じ値の最大 値の個数も返すような関数を作成せよ。
void saidai̲num(int kosuu, int data[], int *max, int *max̲num)
例題と同様に
kosuuは配列のサイズ-1の値 dataには配列の先頭アドレス
maxには最大値を記録する変数のアドレス max̲numにはその最大値の個数
が入っているとする。
メインプログラムでは、以下の二つの配列に対して、それぞれ最大値(max)とその個数(max̲num) を求める処理を記述せよ。
{10,15,30,15,30,30,7}
{10,10,10,10,10,10}
答えはどこかの変数に記録されるようにし、メモリーダンプにより内容が確認できるようにせよ
Cグループ課題レポート
例題4では最大値のみを返すプログラムになっている。ここに最大値が見つかった配列内の場所も 返すようにプログラムを変更せよ。
void saidai̲loc(int kosuu, int data[], int *max, int *loc)
例題と同様に
kosuuは配列のサイズ-1の値 dataには配列の先頭アドレス
maxには最大値を記録する変数のアドレス locにはその最大値が入っていた配列の添え字 が入っているとする。
メインプログラムでは、以下の二つの配列に対して、それぞれ最大値とその場所を求める処理を 関数呼び出しにより、記述せよ。最大値を持つ要素が複数あった場合、どれか一つの場所を示せ ばよい。
{10,15,30,15,30,40,7}
{70,10,10,10,10,10}
答えはどこかの変数に記録されるようにし、メモリーダンプにより内容が確認できるようにせよ
発展課題
課題内容は当日公表する。
再実験日課題
例題4では配列の全要素にアクセスし最大値を返すプログラムを作成した。本課題では、2個の 整数x,yを受け取り、等しいか大きい方の値をmに入れて返すmax2関数を用意し、それを利用す ることで、配列の最大値を求めるsaidai関数を作成せよ
void max2(int x, int y, int *m)
void saidai(int kosuu, int data[], int *max)
メインプログラムでは、以下の二つの配列に対して、saidai関数を呼び出し、正しく動作している か、確認せよ。
{35,15,30,15,30,40,41}
{70,10,20,30,40,50}