前回はスタックに1つしかデータを格納しませんでした。 今回は複数のデータを格納します。
スタックの状態を表現するために、ここでは、スタックが右に伸びるように要素を列挙します。 まず、空のスタックは {} と書きます。 次に、このスタックに4をプッシュしますと、スタックの要素は {4} になります。 さらに5をプッシュし、6をプッシュしますと、スタックの要素は {4, 5, 6} になります。 そして、ポップしますと6が取り出せ、スタックの要素は {4, 5} になります。
Java仮想マシンには四則演算の機能があります。 この演算は、スタックの右端のデータに対して行われます。
例えば、9 - 6 を計算したい場合、9と6をスタックにプッシュしてから引き算の命令を実行します。 すると、計算された9と6はポップされ、計算結果3がプッシュされます。 スタックの要素を書きますと、{9, 6} のときに引き算の命令を実行しますと、{3} になります。
スタックを用いた演算には、十分な計算能力があります。 ただし、普通の筆算とは異なる、独特の考え方をします。 例として、20, 30, 40の合計を2通りの方法で計算します。
第1の方法は、すべての数をスタックにプッシュしてから足し算を続けるものです。 20をプッシュし、30をプッシュし、40をプッシュしますと、スタックの要素は {20, 30, 40} となります。 ここで足し算を行いますと {20, 70} となり、もう一度足し算をしますと {90} となります。 最後にポップしますと、計算結果90が取り出せます。
第2の方法は、スタックへのプッシュと足し算を交互に行うものです。 まず、スタックに20をプッシュしておきます。 30をプッシュしますと、スタックの要素は {20, 30} となり、足し算をしますと {50} となります。 40をプッシュしますと {50, 40} となり、足し算をしますと {90} となります。 最後にポップしますと、計算結果90が取り出せます。
プログラムは命令の系列ですが、命令を16進数で書き表しても、人間にとっては分かりにくいものです。 少しでも分かりやすくするために、アセンブリ言語が考え出されました。 アセンブリ言語 ( assembly language ) とは、命令に覚えやすい文字列(英単語など)を対応させた言語です。 特に、オペコードに対応する文字列を ニーモニック・コード ( mnemonic code ) と呼びます。
例えば、Java仮想マシンの命令
1009
は、アセンブリ言語では
bipush 9
となります。
bipush
は、Byte Integer PUSHという意味のニーモニック・コードです。
アセンブリ言語を機械語に置き換えることを、 アセンブル ( assemble ) と言います。 アセンブルを行うソフトウェアを、 アセンブラ ( assembler ) と呼びます。 逆に、機械語をアセンブリ言語に置き換えることを、 逆アセンブル ( disassemble ) と言います。
学内のパソコンには、Java仮想マシンのアセンブラはないようです。 しかし、逆アセンブルならできます。 コマンドjavap -c filename でファイル filename .classが逆アセンブルされ、アセンブリ言語が出力されます。 この機能を使えば、分かりにくい機械語を読む必要がなくなります。 プログラムを逆アセンブルして、分かりやすくなったアセンブリ言語を読めばよいのです。
今日扱う命令は次の通りです。
ニーモニック | 命令長 | 第1バイト | 第2バイト | 命令 |
---|---|---|---|---|
nop | 1バイト | 0x00 | 何もしない。 | |
iconst_0 | 1バイト | 0x03 | 整数0をスタックにプッシュする。 | |
iconst_1 | 1バイト | 0x04 | 整数1をスタックにプッシュする。 | |
bipush | 2バイト | 0x10 | byte | 整数byteをスタックにプッシュする。 |
iload | 2バイト | 0x15 | index | 第index変数の値をスタックにプッシュする。 |
iload_1 | 1バイト | 0x1b | 第1変数の値をスタックにプッシュする。 | |
iload_2 | 1バイト | 0x1c | 第2変数の値をスタックにプッシュする。 | |
istore | 2バイト | 0x36 | index | スタックからポップした値を第index変数に格納する。 |
istore_1 | 1バイト | 0x3c | スタックからポップした値を第1変数に格納する。 | |
istore_2 | 1バイト | 0x3d | スタックからポップした値を第2変数に格納する。 | |
iadd | 1バイト | 0x60 | 整数の足し算を行う。 | |
isub | 1バイト | 0x64 | 整数の引き算を行う。 | |
imul | 1バイト | 0x68 | 整数の掛け算を行う。 | |
idiv | 1バイト | 0x6c | 整数の割り算を行う。 | |
irem | 1バイト | 0x70 | 整数の剰余算を行う。 |
今日の演習は次のJavaプログラムから始めます。
/* 1*/ class Ex5 { /* 2*/ public static void main (String[] args) { /* 3*/ int x; /* 4*/ int y = 6; /* 5*/ x = 9 - y; /* 6*/ System.out.println(x); /* 7*/ } /* 8*/ }
b04a001@AsiaA1:~/comp2l% javac Ex5.java b04a001@AsiaA1:~/comp2l% java Ex5 3 b04a001@AsiaA1:~/comp2l%
バイナリ・ファイルEx5.classを開きますと、下から4行目にプログラム
1006 3d10 091c 643c
が見えます。
このプログラムは、上記の4行目と5行目に対応します。
00000150: 0010 1006 3d10 091c 643c b200 021b b600 ....=...d<......
このプログラムを逆アセンブルします。
b04a001@AsiaA1:~/comp2l% javap -c Ex5 ... 0: bipush 6 2: istore_2 3: bipush 9 5: iload_2 6: isub 7: istore_1 ... b04a001@AsiaA1:~/comp2l%
このアセンブリ言語を読んでみます。
命令
bipush 6
でプッシュされ、スタックの要素は {6} になります。
命令
istore_2
でポップされ、スタックの要素は {} になり、第2変数の値は6になります。
命令
bipush 9
でプッシュされ、スタックの要素は {9} になります。
命令
iload_2
で第2変数の値がプッシュされ、スタックの要素は {9, 6} になります。
命令
isub
で計算 9 - 6 = 3 が行われ、スタックの要素は {3} になります。
命令
istore_1
でポップされ、スタックの要素は {} になり、第1変数の値は3になります。
したがって、第1変数の値3が出力されます。
例題1.
プログラム
1007 3d1c 1003 6c3c
を実行すると何が出力されるかを答えてください。
出力だけではなく、プログラムの動作についても説明してください。
バイナリ・ファイルEx5.classのプログラムの部分を置き換え、javaコマンドを実行すると、答えが確認できます。
解答する前に、バイナリ・ファイルEx5.classを次のように書き換え、逆アセンブルします。
00000150: 0010 1007 3d1c 1003 6c3c b200 021b b600 ....=...l<......
b04a001@AsiaA1:~/comp2l% javap -c Ex5 ... 0: bipush 7 2: istore_2 3: iload_2 4: bipush 3 6: idiv 7: istore_1 ... b04a001@AsiaA1:~/comp2l%
解答例1.
命令
bipush 7
でスタックの要素は {7} になる。
命令
istore_2
でスタックの要素は {} になり、第2変数の値は7になる。
命令
iload_2
でスタックの要素は {7} になる。
命令
bipush 3
でスタックの要素は {7, 3} になる。
命令
idiv
で割り算 7 / 3 = 2 が行われ、スタックの要素は {2} になる。
命令
istore_1
でスタックの要素は {} になり、第1変数の値は2になる。
したがって、第1変数の値2が出力される。
確認のため、javaコマンドを実行します。
b04a001@AsiaA1:~/comp2l% java Ex5 2 b04a001@AsiaA1:~/comp2l%
次のプログラムを実行すると何が出力されるかを答えてください。 出力だけではなく、プログラムの動作についても説明してください。 バイナリ・ファイルEx5.classのプログラムの部分を置き換え、javaコマンドを実行すると、答えが確認できます。
1008 1002 6c3c 0000
1009 3d1c 1c68 3c00
0404 6004 6004 603c
今日の演習5の答案をメールで提出してください。 メールの差出人は学内のアドレス(b04a001@twcu.ac.jpなど)とし、メールの宛先はkonishi@twcu.ac.jpとします。 メールの本文には、学生番号、氏名、科目名、授業日(10月28日)を明記してください。