情報処理工学

NAGASAKA Yasunori

2006/04/10

http://edu.isc.chubu.ac.jp/naga/index.html で公開中

1   はじめに
 1.1   目標
 1.2   コンピュータに関する代表的な資格

2   コンピュータで扱うデータ
 2.1   データの種類と 2 進数
 2.2   文字データ
 2.3   構造を持ったデータ
  2.3.1   ベクトル
  2.3.2   行列
  2.3.3   レコード
 2.4   数値データ
  2.4.1   2 進数, 16 進数, 2 の補数
  2.4.2   固定小数点形式
  2.4.3   浮動小数点形式
  2.4.4   IEEE754, 浮動小数点形式の国際標準規格
  2.4.5   練習問題
  2.4.6   2 進化 10 進数
  2.4.7   練習問題

3   コンピュータのハードウェア
 3.1   コンピュータを構成する装置
  3.1.1   練習問題
 3.2   コンピュータに関連する単位
  3.2.1   練習問題
 3.3   様々なコンピュータ

4   コンピュータのソフトウェア
 4.1   プログラミング言語
 4.2   言語プロセッサ
 4.3   オペレーティングシステム, OS
 4.4   コンピュータの使用形態

5   CPU、マイクロプロセッサの機能
 5.1   機械語命令とデータサイズ
 5.2   CPU の内部構成
  5.2.1   CPU 内各部の機能
  5.2.2   レジスタの役割による分類
 5.3   CPU の動作原理
  5.3.1   機械語プログラムの例
  5.3.2   メモリ上のプログラムとデータの配置
  5.3.3   プログラム実行時の CPU とメモリの通信
 5.4   主記憶、メモリ
 5.5   アドレシング

6   アセンブリ言語 CASL II
 6.1   仮想 16 ビット CPU COMET II の仕様
 6.2   COMET II の命令
  6.2.1   データ転送
  6.2.2   算術演算
  6.2.3   論理演算
  6.2.4   比較
  6.2.5   桁移動
  6.2.6   分岐
  6.2.7   スタック操作
  6.2.8   サブルーチンコール、リターン
  6.2.9   練習問題
  6.2.10   練習問題
 6.3   条件判断の記述方法
  6.3.1   練習問題
 6.4   スタック
  6.4.1   スタックの使用例
  6.4.2   練習問題
 6.5   CASL II によるプログラミング
  6.5.1   アセンブリ言語でプログラムを作る時の考え方
  6.5.2   CASL II のプログラムの書き方
  6.5.3   プログラムの例
  6.5.4   練習問題



1   はじめに


1.1   目標

ハードウェア、ソフトウェア、データの表現法、 ネットワークなどのコンピュータに関連する様々な事柄を学ぶことによって、 コンピュータシステム全般の理解を深める。

1.2   コンピュータに関する代表的な資格

  経済産業省が監督する「情報処理技術者試験」がよく知られており、権威もある。 様々な試験区分があるが、 基本情報技術者試験及び初級システムアドミニストレータ試験が最も合格しやすい。 最初はこのどちらかの試験の合格が目標となる。

  この講義では、過去に情報処理技術者試験で出題された問題を時々演習として解いてみる。





2   コンピュータで扱うデータ


2.1   データの種類と 2 進数

  論理値、数値、文字などがコンピュータ上ではデータとして処理される。 コンピュータ内部ではすべて数値 (2 進数) として表現される。

  2 進数の 1 桁を 1 ビット(bit)と呼ぶ。 コンピュータで 2 進数を扱う場合、 8 ビットのデータを一固まりのデータとすることが多い。 8 ビットを 1 バイト(Byte)と呼ぶ。 2 進数はバイトが単位として使われることが多い。



2.2   文字データ

  コンピュータで文字を表すデータを考える場合、 文字集合と文字コードという二つの概念が必要となる。 文字集合とは、処理または表現の対象として、 どの文字を採用してどの文字を採用しないかということを規定する。 文字コードとは、一つ一つの文字に対してどのような数値を割り当てるかという対応関係を表す。 以下の日本語の文字の表現で出てくるように、一つの文字集合に対して、 複数の異なった文字コードが存在することもある。

  文字は、アルファベット、数字、記号などは 1 バイトで表現される。代表的 なコード体系として、ASCIIコードがある。平仮名、漢 字など日本語で使われる文字は 2 バイトの数値で表現される。日本語の文字を表す コード体系は JIS コード、Shift JIS コード、EUC コード、Unicode などがある。 以下でそれぞれの文字コードの特徴を簡単に示す。

  JIS X 0208 に含まれる文字 6879 個中、漢字は 6355 個で、 使用頻度により第一水準 2965 個と第二水準 3390 個のグループに分けられている。

  文字コードに関する詳しい解説が http://www.kanzaki.com/docs/jcode.html にて公開されている。



2.3   構造を持ったデータ


2.3.1   ベクトル

  数学でよく使われるデータ形式で、コンピュータ上でシミュレーション、数値 計算を行う場合には同じ形式で使われる。n 次元のベクトルは、n 個の数値を 並べて一まとまりのデータとして扱う。

  一般的な表記法は、 (x1, x2, x3, ... , xn) となる。 コンピュータ上では 1 次元の配列として表現される。 例として C 言語では int a[10]; といった表現になる。



2.3.2   行列

  数学でよく使われるデータ形式で、コンピュータ上でも広く使われる。 m × n 行列は、m × n 個の数値を縦に m 個、横に n 個になるように 2 次元状に 並べて一まとまりのデータとして扱う。

  一般的な表記法は以下のようになる。
x11 x12 x13 ... x1n

x21 x22 x23 ... x2n

x31 x32 x33 ... x3n

...

xm1 xm2 xm3 ... xmn



  コンピュータ上では 2 次元の配列として表現される。 例として C 言語では int a[10][10]; といった表現になる。



2.3.3   レコード

  ベクトルと行列は数学的な概念で、そこに含まれるデータは数値のみである。 数値に限らず、文字列や論理値なども対象として、 コンピュータ上であるまとまった概念を表現するために、 様々な属性のデータを集めたものをレコードと呼ぶ。


 例 一人の人間を表現するためのレコード

属性 : 値 : データの種類 
----------------------------------
名前 : 中部太郎 : 文字列 
性別 : 男性 : 数値、論理値 
身長 : 175cm : 実数 
体重 : 65kg : 実数 
年齢 : 20 歳 : 整数 
住所 : 春日井市松本町 1 番地 : 文字列 
電話番号 : 0568-51-1111 : 数値、文字列 
趣味 : 読書、音楽観賞、テニス、スキー : 文字列の配列  
  コンピュータ上ではこのような複雑な構造を持ったデータをプログラムで表現 して処理できるように、専用の仕組みが用意されることが多い。 例として C 言語では 構造体として表現される。 上記の人間を表現するレコードを C 言語の構造体として表現すると以下のようになる。

struct human {
	char	name[20];
	int	sex;
	float	height;
	float	weight;
	int	age;
	char	address[100];
	int	tel[20];
	char	hobby[10][10];
};
  コンピュータで何らかの情報処理をしようとする場合、単純な数値や文字列だ けでは適切に処理対象のデータを表現できないことが多い。ベクトル、行列、 レコードなどの構造化されたデータが必要となる。




2.4   数値データ


2.4.1   2 進数, 16 進数, 2 の補数

  次の計算をしなさい。 わからない問題がある場合は、1 年生の時に学んだ数の表現の部分をよく復習すること。

  1. (101000101110110)2 を 10 進数に変換する。

  2. (1234.123)5 を 10 進数に変換する。

  3. (101000101110110)2 を 16 進数に変換する。

  4. (101000101110110)2 を 16 桁の 2 の補数に変換する。

  5. (53.6875)10 を 2 進数に変換する。

  6. (123.45)10 を 5 進数に変換する。


  コンピュータ上で数値を扱う場合、その表現法は大きく分けて 固定小数点形式浮動小数点形式の 2 種類がある。


2.4.2   固定小数点形式

  小数点の位置を常に一定の位置に仮定して数を表現する方法である。 場合によって小数点の位置が変化することがなく、 常に固定されているのでこの名前がついている。 次の例は 16 bit の固定小数点形式で整数を表現した場合の例である。 S, # は 2 進数 1 桁を表す。

 S###############. (最上位ビット S は符号を表す)

  固定小数点形式で数を表現する場合、 事前に特別な取り決めがなければ、 上の例のように通常は右端の位置に小数点があるものとされる。 別の言い方をすると、コンピュータで一般的に使用されている整数の表現は、 数の右端に小数点の位置を固定した固定小数点形式であるといえる。 整数を表現する場合、負数の表現は 2 の補数が用いられることが多い。

  ただし、小数点の位置はプログラムを作成する者が用途に応じて決められるので、 別の位置にすることもできる。例として、小数点以下は 0.25 の精度で表現すれば十分な場合、 小数点以下を 2 進数で 2 桁だけ用意した固定小数点形式を使えば良いといえる。 このような取り決めをこのプログラムやデータを使う者すべての間で事前にしておけば問題ない。 ただし取り決めを知らない第三者にとっては、このデータは無意味となる。



2.4.3   浮動小数点形式

  浮動小数点形式は、その名前のとおり小数点の位置が場合によって異なる表現である。 コンピュータで小数点以下の部分を含む実数を表現する場合、ほとんどの場合固定小数点形式ではなく、 浮動小数点形式が使用される。 実数を仮数部 M, 指数部 E の組合せで表現する方法である。 以下ではまず 10 進数を表現する浮動小数点形式により、その基本的な考え方を示す。 その後コンピュータで実際に使用されている 2 進数による浮動小数点形式を示す。


 10 進数の場合の例

  (0.000391)10 を表現したいとする。 この時浮動小数点形式では、もとの数を 0.391 × 10-3 と変形する。ここで 0.391 を仮数、10-3 を指数と呼ぶ。 変形後は 391 と -3 を記憶する。 もとの数は 0.MMMMM × 10EEEEE の M, E に 391, -3 を代入することで再現できる。

  浮動小数点形式は次のような場合に有効である。 (3910000000000000)10 や (0.0000000000391)10 といった数を表現したい場合、 固定小数点形式では十数桁の精度が必要である。 一方、0.391 × 10n と変形して、 391 と n を記憶しておけば高々 5, 6 桁の精度で十分となる。 このように浮動小数点形式は絶対値が非常に大きくなる場合、 小さくなる場合でも記憶するデータ量がそれほど増えないという特徴がある。


 正規化

  上記の変形で、仮数を 10-1 <= M < 1 にすることを 正規化という。 コンピュータ内部で記憶できる数は有限の桁数しかないため、 仮数部の限られた桁数でなるべく精度良く数を表現するために行う。


 正規化の例

  0.000391 を表現する時に、仮数部が仮に 3 桁だけ使用できるとすると、
0.039 × 10-2 → 039, -2 を記憶するのは ×
0.391 × 10-3 → 391, -3 を記憶するのは ○
もし 039, -2 を記憶すると、 本来表現できるはずの 391 の末尾の 1 が誤った正規化により消えてしまうからである。 現実のコンピュータでは、内部で表現、記憶できる数は必ず有限の桁数であるので、 限られた桁数でなるべく多くの桁を正しく表現するためには、 正しい正規化を行なうことが必要である。



2.4.4   IEEE754, 浮動小数点形式の国際標準規格

  上記 10 進数の場合の表現は、浮動小数点形式の概念を説明するための例であり、 現実のコンピュータでは以下に示す浮動小数点形式の表現が使用される。

  コンピュータで実際に使用されている浮動小数点形式は国際標準規格となっており、 IEEE754 (アイトリプルイー 754)という名前が付けられている。 IEEE754 は 32 bit 版と 64 bit 版があるが、基本的な考え方は同じで仮数部、 指数部に割り当てる bit 数が異なる。以下では 32 bit 版を示す。 次の図で、S, E, M, . はすべて 1 bit を表す。


IEEE754 のビットの割り当て

S ...E.... ...........M...........


 正規化の例

  IEEE754 は先の 10 進数の例とは異なる特有の正規化を行なう。例として、
0.0001011 → 1.011 × 2-4
11011.11 → 1.101111 × 24
-0.0010111 → -1.0111 × 2-3
-1111.011 → -1.111011 × 23
などの例を挙げておく。 ポイントは小数点の左に 1 bit だけ残すことである。 また浮動小数点形式では 2 の補数は関係しないので、 負の数の表現は正規化の段階ではそのままマイナス符号を付けておけばよい。


 暗黙の 1 ビット

  仮数部の最初の 1 (小数点の左の1) は常に一定なので、仮数部 M には直接記録しないようにする。 M としては小数点以下を 23 ビット記録する。 よって記録しているのは 23 ビットであるが、24 ビットの精度がある。


 指数部 E の表現

  指数部 E は符号無し 8 ビットの整数で表現する 2 進数 0 〜 255 から 127 を減算した値で、 -127 〜 +128 を表す。2 の補数を用いないので注意する。 8 ビットの値と E の解釈の対応は以下の通り。


指数部 E の解釈

00000000 -127
00000001 -126
00000010 -125
00000011 -124
途中略 途中略
01111110 -1
01111111 0
10000000 1
10000001 2
10000010 3
10000011 4
途中略 途中略
11111110 127
11111111 128

  (この形式では E の大小関係が 符号無し 8 ビットの 2 進数にそのまま対応するので、 指数部の処理が容易になる。すなわち絶対値の大小が、 そのまま 8 ビットの整数の大小として表現される。 もし 2 の補数を用いたとすると大小関係が途中から反転するため、扱いが難しい。)


 問題

  IEEE754 はこのままでは一つ困った問題を抱えることになるが何か ?


 計算の例

  例として 10 進数の 60.125 なら、
(60.125)10 → 2 進数に変換 →
(111100.001)2 → 正規化 →
1.11100001 × 25 → 指数部、仮数部を求める →
0 10000100 11100001000000000000000 → (指示があれば 16 進で書く) →
0100 0010 0111 0000 1000 0000 0000 0000 → 42708000
正の数なので、符号ビットは 0 になる。
指数部は 5 なので 127 を加えた 132 を 8 bit の整数で表し 10000100 となる。
仮数部は正規化した式の小数点以下 11100001 に 23 桁になるまで 0 を加える。

  別の例として 2 進数の -11100111.01 なら、
(-11100111.01)2 → 正規化 →
-1.110011101 × 27 → 指数部、仮数部を求める →
1 10000110 11001110100000000000000 → (指示があれば 16 進で書く) →
1100 0011 0110 0111 0100 0000 0000 0000 → C3674000
負の数なので、符号ビットは 1 になる。
指数部は 7 なので 127 を加えた 134 を 8 bit の整数で表し 10000110 となる。
仮数部は正規化した式の小数点以下 110011101 に 23 桁になるまで 0 を加える。



2.4.5   練習問題


 (1)

  次の各数を IEEE754 にしたがって表現しなさい。答えは正規化した形と、 浮動小数点形式のビットパターンを 8 桁の 16 進数に変換して示しなさい。

  1. 53.687510

  2. -12710

  3. 10101010102

  4. 0.0000012


 (2)

  次の各数を 2 進数の正規化した形で示しなさい。 また浮動小数点形式のビットパターンを示しなさい。

  1. IEEE754 で表現可能な最大の数

  2. IEEE754 で表現可能な最小の数

  3. IEEE754 で表現可能な正数で絶対値最小の数

  4. IEEE754 で表現可能な負数で絶対値最小の数



2.4.6   2 進化 10 進数

  2 進化 10 進数 (BCD, Binary Coded Decimal) は、2 進数 4 桁の 0000 〜 1001 までを使って 10 進数 1 桁の 0 〜 9 を表現する方法である。 演算を行う場合は通常の 2 進数とは異なり、4 桁毎に特別な桁上がりの処理が必要となる。

  2 進化 10 進数は、その表現法の違いからゾーン形式とパック形式という 2 種類の表現がある。


 ゾーン形式

  ゾーン形式は 1 バイトのデータの下位 4 ビットのみを使って 10 進数 1 桁を表す。 上位 4 ビットの扱いは処理系により異なる。 値の範囲は ####0000 〜 ####1001 となり、0 から 9 までを表す。# は未使用ビットを表す。


 パック形式

  パック形式は 1 バイトを上位、下位の 4 ビットずつに分けて、それぞれで 10 進数 1 桁を表す。 よって 1 バイトで 10 進数 2 桁を表現できる。 上位 4 ビットに表す数がない場合の扱いは処理系により異なる。 00000000 〜 10011001 で 0 から 99 までを表す。


 例

  例としていくつかの数を 2 進化 10 進数のゾーン形式とパック形式で表現してみる。 ここでは使用していないビットはすべて 0 で埋めるものとする。

  7 はゾーン形式では 00000111, パック形式では 00000111 となる。
23 はゾーン形式では 00000010 00000011, パック形式では 00100011 となる。
578 はゾーン形式では 00000101 00000111 00001000, パック形式では 00000101 01111000 となる。
6625 はゾーン形式では 00000110 00000110 00000010 00000101, パック形式では 01100110 00100101 となる。



2.4.7   練習問題

  以下の数をゾーン形式とパック形式の両方で表しなさい。 未使用領域は 0 で埋めるものとする。

  1. 9310

  2. 15010

  3. 634510

  4. 2105010






3   コンピュータのハードウェア


3.1   コンピュータを構成する装置



コンピュータを構成する装置


3.1.1   練習問題

  上の図に関して以下の問いに答えなさい。

  1. Central Processing Unit を日本語 1 単語で表す場合の名称 (〜装置) を答えなさい。

  2. Central Processing Unit の機能を 2 個答えなさい。

  3. Central Processing Unit を日本語 2 単語で表す場合の名称 (〜装置, 〜装置) を答えなさい。(前問の答えに関連がある。)

  4. Input Device を日本語で表す場合の名称を答えなさい。

  5. Input Device に該当する具体的な装置を一つ答えなさい。

  6. Main Memory Unit を日本語で表す場合の名称を答えなさい。

  7. Main Memory Unit は読み出しのみと読み書き可の 2 種類に分けられるが、 それぞれの英語の正式な名称と省略形の名称を答えなさい。

  8. Main Memory Unit で使用される読み書き可能な LSI は、 その仕組みの違いから 2 種類存在するが、 その名称とデータを記憶する仕組みを答えなさい。

  9. Output Device を日本語で表す場合の名称を答えなさい。

  10. Output Device に該当する具体的な装置を一つ答えなさい。

  11. Secondary Memory Unit を日本語で表す場合の名称を答えなさい。

  12. Secondary Memory Unit に該当する具体的な装置を一つ答えなさい。

  13. 次の表の空欄に適当な言葉を書き込みなさい。

    違い

    Main Memory Secondary Memory
    反応速度 ? ?
    記憶容量 ? ?
    電源を切ると ? ?

  14. 「現在のコンピュータは人間があらかじめプログラムを作成し、 実行する時には ? に読み込んでおく必要がある。」 ? に当てはまる適当な言葉を答えなさい。

  15. 「読み込まれたプログラムは、そこから順に 1 命令ずつ読み出して CPU で実行される。」 この文章は現在のコンピュータの動作原理を表しているが、これを ? 方式と呼ぶ。? に当てはまる適当な言葉を答えなさい。

  16. 前問の方式で動作するコンピュータを ? 型コンピュータと呼ぶ。 ? に当てはまる適当な言葉を答えなさい。




3.2   コンピュータに関連する単位


 記憶容量

  記憶容量を表す単位は以下のようなものがあるが、これらの単位は通常の単位と異なり、 10n で表す場合と、1024n で表す場合の 2 種類の解釈があるので注意する。どちらの方式で計算されているかは、 単位を見ただけではわからないので、正確な値が必要な場合は確認する必要がある。


記憶容量の単位

単位 表す内容
1 kB 103 または 1024 バイト
1 MB 106 または 10242 バイト
1 GB 109 または 10243 バイト
1 TB 1012 または 10244 バイト
1 PB 1015 または 10245 バイト
1 EB 1018 または 10246 バイト


 時間

  時間を表す単位は以下のようなものがある。記憶容量とは異なり、 こちらは 10n だけである。1 秒より長い時間を表す場合は、 60 や 24 など不規則な値が単位の変化する区切りとして使用されるが、 1 秒より短い時間はすべて 10n が使用されるので、長さや重さと 同じように考えやすい。


時間の単位

単位 表す内容
1 msec 10-3
1 μsec 10-6
1 nsec 10-9
1 psec 10-12
1 fsec 10-15


3.2.1   練習問題

  1. クロック周波数 2GHz の CPU のサイクルタイムを求めなさい。

  2. 反応時間が 60nsec の半導体メモリと 10msec の HDD は何倍の差があるか。

  3. 40GB の HDD には 1.44MB の FD 何枚分のデータが記録できるか。

  4. 40GB の HDD は何億ビット記録できるか。

  5. 1 人分のデータを 1KB で記録する時、40GB の HDD には何人分入るか。

  6. 光の速度を 30 万km/sec とする時、1nsec にどれだけの距離進むか。




3.3   様々なコンピュータ

  ここでは、現在のコンピュータを価格、用途、性能等をもとにして分類してみる。

  現在は WS, PC の能力が非常に向上しており、 単純な計算速度で比較すると上位のコンピュータとの差が小さくなっている。 CPU の能力は差が小さくなったが、その他の性能、 例えばファイルの入出力の速度や可用性など、 システム全体の性能は大規模なコンピュータと安価なコンピュータでは違いがある。

  WS, PC の能力が向上したため、 従来メインフレームやミニコンで行っていた処理を安価な WS, PC で行わせることをダウンサイジングと呼ぶ。





4   コンピュータのソフトウェア


4.1   プログラミング言語

  プログラミング言語とは、プログラムを作成する(書く)ための専用の言語であり、 コンピュータの動作を指示するプログラムを記述するために人工的に作り出された。 プログラミング言語は様々な用途でこれまでに多数開発されている。

  プログラムは最終的には CPU で実行可能なコードとして表現する必要がある。 人間がプログラムを記述する時の表現の仕方が、 コンピュータの動作に直結した内容を記述する(機械向き)か、 人間の思考方法に近い記述をする(人間向き)かで分類される。 前者を低級言語、後者を高級言語と呼ぶが、この低級/高級という表現は分類を表す名前であり、 低級言語が高級言語より劣っているという意味ではない。 低級言語から高級言語までは言語の記述内容のレベルにより大きく次の 3 段階に分けられる。

for (i = 0; i < 10; i++) {
	sum += i;
}
  機械語、アセンブリ言語、高級言語はそれぞれ多くの種類がある。 機械語は CPU のハードウェア 1 種類につき一つずつある。 機械語は世の中の CPU の種類だけあると言ってもよいだろう。 機械語一つにつきアセンブリ言語が一つある。 高級言語は用途やその言語の開発時の目的などに応じて多数作られてきたが、 CPU のハードウェアには直接関係しない。



4.2   言語プロセッサ

  プログラミング言語で記述されたプログラムを、 実行可能な機械語のプログラムに変換するプログラムを言語プロセッサと呼ぶ。

  コンパイラとインタプリタは高級言語を機械語に変換するという点で似ているが、 プログラムを実行する方法が異なるので使い分けがされる。例として、 あるプログラムを実行する時に、どちらも 11 分かかるとする。



コンパイラとインタプリタ

  コンパイラはプログラム全体を一括して機械語のプログラムに変換する。 この例では 10 分かかるが、その間人間は待っている必要がある。 変換が終了すれば実行は 1 分でできる。 もし実行結果が間違っていた場合、 プログラムを修正して再度機械語に変換する必要があるが、ここでも 10 分かかる。 しかし一旦正しいプログラムが完成すれば、 その後は何回実行しても必要なのは実行に要する時間だけで、 10 回実行すれば 10 分で実行が終る。

  インタプリタはプログラムを先頭から 1 命令ずつ機械語に変換して実行していく。 全体を変換、実行するには 11 分かかるが、 プログラムを実行し始めて 2 分のところで間違いが見つかれば、 そこで中断して修正に移ることができる。開発中で実行、中断、修正を繰り返す場合は、 コンパイラを使用するよりも効率が良い。しかしプログラムが完成した後も実行するには 11 分かかる。 10 回実行すれば 110 分必要となる。実行時間が問題となる場合はインタプリタは向いていない。



4.3   オペレーティングシステム, OS

  オペレーティングシステムは、基本ソフトウェアと呼ばれ、 コンピュータを利用する上でいくつかの重要な機能を持っている。



オペレーティングシステムの役割

  オペレーティングシステムの機能のわかりやすい例として、 マウスを動かした時にマウスカーソルが画面上で移動することや、 キーボードから入力した文字が画面に表示されるといったことがある。 これらは、マウスやキーボードを制御して入力されたデータを読みとるプログラムや、 ディスプレイを制御してデータを画面に表示するというプログラムを OS が備えており、 これらがユーザインタフェースの一環として実行されているからである。

  オペレーティングシステムがなければコンピュータは全く使用できないと言える。



4.4   コンピュータの使用形態

1 台のコンピュータを複数の使用者で利用する場合、以下のような使用形態がある。



5   CPU、マイクロプロセッサの機能


5.1   機械語命令とデータサイズ

  機械語命令の種類として、算術演算、論理演算、順序制御、入出力、データ転送などがある。

  n ビットの CPU という場合、一般にはレジスタ長が n ビットであることを表す。 またデータバスのサイズも n ビットであることが多い。


CPU のレジスタ長と直接扱える数値の範囲

ビット数 扱える数値の範囲
4 24 = 16
8 28 = 256
16 216 = 65536
32 232 = 4.295 × 109
64 264 = (232)2



5.2   CPU の内部構成



内部構成

  ここでは例として代表的な 8bit CPU である Z80 を取り上げる。 Z80 はザイログ社が開発した CPU で、インテル社の 8080 の上位互換の機能を持っている。 Z80, 8080 は 30 年程前に広く使用されていた CPU で、 8 bit CPU の代名詞となっている。

  当時から産業用でも広く使われており組み込みのコントローラとして使用されてきた。 これまでに開発されたソフトウェア資産が膨大であり、 コントローラの能力がそれほど必要でない場面では現在でも広く使用されている。

  プロセッサの基本的な動作原理は 4 bit CPU でも、32 bit CPU でもほとんど同じである。 よって 8 bit CPU の Z80 についての知識でも、 最近の高性能な 32 bit CPU, 64 bit CPU の機能の理解に役立つ。 処理能力を上げるための特別な仕組みがないため、 CPU そのものの機能を理解するための教材としてちょうどよいといえる。


5.2.1   CPU 内各部の機能

  CPU には、先に示した CPU の内部構成の図にあるように様々な要素があり、 相互にデータや信号をやりとりしてプログラムを実行していく。

  なぜ 8 ビット CPU なのに、AB は 16 ビットなのか ?
→ AB が 8 ビットではメモリ容量が少な過ぎる (256 Byte) から。 メモリが 256 Byte では実用的なプログラムは全く記憶できず、 コンピュータとして役に立たない。

  同じ理由で 16 ビット CPU なのに、8086 では AB が 20 ビット、 68000 では AB が 24 ビット に拡張されている。 32 ビット CPU では AB も 32 ビットに拡張されており、 特別な工夫をしなくても 4GB のメモリが使用できる。 通常の用途では現在でも十分な広さのメモリアドレスの空間があるといえる。



5.2.2   レジスタの役割による分類

  Z80 のレジスタはアルファベットの名前が付けられており、 プログラムで処理するデータを記憶するための汎用レジスタとして A,B,C,D,E,H,L がある。 A レジスタだけは下記にあるように多少特別な役割を持っている。 他にも F,I,R などの様々なレジスタがあるが、 これらは用途が決められていてプログラムで自由に使うことはできない。 また IX, IY, PC, SP レジスタはアドレスを処理対象として扱うため 16 bit 長となっている。

  図中、白い部分は 8080 CPU にあるレジスタで、 薄いグレーの部分は Z80 で新たに加えられたレジスタである。 A' から L' までは「裏レジスタ」と呼ばれるレジスタで、8080 CPU との互換性を保ちつつ、 レジスタの少なさを緩和するための工夫である。



レジスタ


(ヒント):リフレッシュとは : DRAM, Dynamic RAM はコンデンサに蓄えた電荷でデータを記録する ため、一定時間アクセスがないと放電により内容が消えてしまう。そのため記憶データを 使わない時でも時々読み出して同じ内容を書き込む操作が必要となる。この操作を リフレッシュと呼ぶ。

  算術演算、論理演算を行なう時はレジスタから値を取り出して ALU で必要な演算をした後、 A または HL レジスタペアに結果が記憶される。 また演算結果を表す様々な値が F レジスタに記憶される。これらの値としては、 結果が 0 になったか、 結果が負数になったか、 桁上がりが発生したか、などがあり、プログラム中の条件判断の時に参照される。



8bit 演算



16bit 演算




5.3   CPU の動作原理



CPU とメモリ

  プログラム記憶方式に基づいて作られている CPU は、 次の手順にしたがってメモリの先頭から順に命令を読み出し、実行していく。 この動作原理は、特殊な CPU の場合を除いて、すべての CPU に共通する。

  1. PC に実行開始番地をセットする。

  2. AB に PC の内容を出力して番地を指定する。

  3. DB から、指定した番地の内容を読み取り、IR にセットする。 これをフェッチと呼ぶ。 PC ← PC + 1

  4. システム制御部により IR の内容が解読される。これをデコード と呼ぶ。

  5. 命令を実行する。

  6. 2 に戻る。

  Z80 の制約として、DB が 8 ビットなので、 メモリからは一度に 1 Byte しか読み込むことができない。 また、Z80 では 1 Bytes 〜 4 Bytes の可変長の機械語命令を採用している。 (機械語命令の例 : 01111000 = 78H : LD A, B B レジスタのデータを A レジスタに転送するという命令)。
このため上記の手順は、読み出しの部分が場合によって複数回に分かれるため、 下記のようになる。

  1. PC に実行開始番地をセットする。

  2. AB に PC の内容を出力して番地を指定する。

  3. DB から指定した番地の内容を読み取り、IR にセットする。 PC ← PC + 1

  4. システム制御部により IR の内容が解読される。 1 バイト命令なら 6 へ進む。

  5. 2 バイト以上の命令については、(2),(3) を必要な回数繰り返す。読み込んだ データは CPU 内部のバッファに保持する。

  6. 命令を実行する。

  7. 2 に戻る。


5.3.1   機械語プログラムの例

  例 (2000) のデータと (2001) のデータを加算して (2002) に格納する機械語のプログラムが 1000 番地から配置されている場合

  ※ (2000) という表現は、機械語プログラムの内容を記述する場合に、 メモリの 2000 番地という意味でしばしば用いられる。 ここでもこのような意味で ( 数値 ) という表現を用いる。 したがって上記のプログラムは、メモリの 2000 番地と 2001 番地のデータの和を 2002 番地に 記録するという動作になる。

  ※ Z80 ではメモリへの入出力、算術論理演算はA レジスタでしか行えない という制約があるため、多くの場合データは A レジスタを介して操作される。

  このプログラムは、次に示す一連の命令の組み合わせとして記述する。

  1. メモリの 2000 番地からデータを A レジスタに読み込む。

  2. そのデータを一旦 B レジスタにコピーする。(A レジスタを空けるため)

  3. メモリの 2001 番地からデータを A レジスタに読み込む。

  4. A レジスタと B レジスタの和を A レジスタに記録する。

  5. A レジスタの内容をメモリの 2002 番地に記録する。

  6. 終了。



5.3.2   メモリ上のプログラムとデータの配置

  このプログラムを機械語で記述してメモリの適切な位置(仮に 1000 番地とする)に記録すると、 次の図に示すようなイメージになる。 この図では左に 0000, 1000, 1001, 1002 などの数値が付けられているが、 横 1 行が一つの記憶場所を表しており、1 バイトの容量がある。



プログラムとデータの配置

  メモリ中のプログラムとデータに関して、次のような特徴がある。



5.3.3   プログラム実行時の CPU とメモリの通信

  上記のプログラムを実行する時には、 次に示すように CPU と メモリ間で様々なデータが送受信される。

1000 → AB   ; LD  A, (2000) ; 2000 番地のデータを A レジスタに読み込む。
IR ← DB     ; 参考までにこの命令の最初の 1 バイトは 3A である。
   ; 命令の最初の 1 バイトを解読すると 3 バイト命令とわかるので残り 2 バイトを読む
1001 → AB
バッファ ← DB (00)
1002 → AB
バッファ ← DB (20)
2000 → AB
A ← DB      ; (Q.1)

1003 → AB   ; LD  B, A ; A レジスタの内容を B レジスタにコピーする。
IR ← DB
B ← A       ; (Q.2)

1004 → AB   ; LD  A, (2001) ; 2001 番地のデータを A レジスタに読み込む。
IR ← DB
   ; 同様に (1005), (1006) から 01, 20 を読む
2001 → AB     
A ← DB      ; (Q.3)

1007 → AB   ; ADD  A, B ; A レジスタと B レジスタの内容の和を求め A レジスタに書きこむ。
IR ← DB
A ← A + B   ; (Q.4)

1008 → AB   ; LD (2002), A ; A レジスタの内容を 2002 番地に書きこむ。
IR ← DB
   ; 同様に (1009), (100A) から 02, 20 を読む
2002 → AB
A → DB      ; (Q.5)

100B → AB   ; HALT ; プログラムの実行を停止する。
IR ← DB
   ; 終了



5.4   主記憶、メモリ

  メモリには場所を指定するために一定の記憶容量毎に固有の番地(アドレス)が割り当てられている。 番地の割り当て方には以下の 2 種類がある。

  ワードとは、CPU が一度に扱える自然なデータの大きさで、 32 bit CPU であれば 32 bit が 1 ワードとなる。 32 bit CPU はデータバスも通常 32 bit あるので、 メモリの番地は 32 bit (4 Byte) に一つ割り当てれば問題はないが、 慣例として現在でもほとんどすべてバイトマシンが採用されている。

  32 ビット CPU などビット数の大きな CPU を採用していて、 かつバイトマシン形式の番地割当を採用している一部のコンピュータでは、 4 の整数倍の値 (0, 4, 8, 12, ...) から始まるメモリの番地しか指定できないという制約がある。 その場合、メモリは 1 バイト毎に 1 個番地の値が存在するが、4 の整数倍以外の 途中の値を指定するとメモリアドレスに関する特有のエラー (Alignment Error) になる。

  具体的な例として、上記の制約があるコンピュータでは、 図中のグレイの部分のように 4 の整数倍の値の番地から始まる連続した 4 バイトのアクセスは許される。 もし 2 番地から始まる連続した 4 バイトを読みだしたい場合、 この制約がなければ番地の値として 2 を指定すれば 1 回のメモリアクセスで完了するが、 制約がある場合は 0 番地からと 4 番地からの 2 回に分けてデータを読み出して、 その後で必要部分を取り出して合成する必要があり効率が悪い。



メモリアクセス上の制限

  このような問題もあるが、その一方で 指定可能な番地の値に制約を設けることによって、 メモリアクセスに関連するデータバス、アドレスバスの回路設計が、 必要以上に複雑になるのを避けられるというメリットもある。



5.5   アドレシング

  CPU が処理対象のデータをどこから取ってくるか、 場所を指定することをアドレシングという。 以下のような種類がある。 下の図では、グレイの部分のデータが参照したいデータを表している。

  参考までに、少し前に出てきた内容の CPU の動作原理、 CPU の動作例で示した機械語プログラムの例では、 処理の一部にメモリアクセスを含んでいた。 その部分の機械語命令の一部分に、参照するメモリの番地が直接含まれていたが、 この場合のアドレシングは直接番地方式を使用していたといえる。

  1. 直接番地方式 : 番地部の値そのものがメモリの番地を示す


    直接番地方式

  2. 間接番地方式 : 番地部で示される番地のメモリの値が実際にデータが 入っている場所の番地を示す。


    間接番地方式

  3. 相対番地方式 : 番地部の値と PC の和がメモリの番地を示す。 番地部は PC からの偏位、ずれを表す。


    相対番地方式

  4. インデックス修飾方式 : 番地部の値とインデックスレジスタの和が メモリの番地を表す。


    インデックス修飾方式

  5. 即値方式 : 番地部の値をそのままデータとする方式。


    即値方式

  6. レジスタ指定方式 : レジスタ番号を指定してそのレジスタの 値をデータとする。


    レジスタ指定方式





6   アセンブリ言語 CASL II


6.1   仮想 16 ビット CPU COMET II の仕様


COMET II の命令の構成

  OP  .   GR  .   ADR  .   XR   .

  OP をオペコード(動作の内容「どうする」を表す部分)、 GR, ADR, XR をオペランド(動作の対象「何を」を表す部分)と呼ぶ。 オペランドは最大で 3 個指定する。同じ種類のオペランドを複数個指定することはない。 すなわち、1 命令の中に GR を 2 回指定したり、ADR を2 回指定することはない。

  命令の書き方は以下の 4 種類がある。命令により必要とされるオペランドが異なる。 [, XR] は省略可能で、命令を記述する時に書くか書かないかを決められる。

	OP	GR, ADR [, XR]
	OP	ADR [, XR]
	OP	GR
	OP


6.2   COMET II の命令


6.2.1   データ転送

  データ転送を行なう命令は LD (Load), ST (Store), LAD (Load Address) の 3 種類がある。 LD はメモリからレジスタへの転送、ST はレジスタからメモリへの転送、 LAD は数値をレジスタに転送する。レジスタ間のデータ転送は LD 命令で行なえる。 LD, ST はオペランドの書き方は同じであるが転送の向きが逆、 また LD, LAD は見た目がよく似ているが機能が異なるので注意する。 LD は番地部で指定した値がメモリのアドレスを表し、メモリの指定した場所からデータを取り出す。 LAD は番地部で指定した値そのものをレジスタに書き込む。 以下は具体的な記述の例である。



6.2.2   算術演算

  算術演算の命令は、算術加算 ADDA (Add Arithmetic), 論理加算 ADDL (Add Logical), 算術減算 SUBA (Subtract Arithmetic), 論理減算 SUBL (Subtract Logical) の 4 種類がある。 算術加算/減算は計算対象の数を2 の補数として扱う。 論理加算/減算は計算対象の数を符号無し整数として扱う。 使えるアドレシングは LD 命令と同じで、レジスタ同士の演算か、 レジスタとメモリ内の数値の演算となる。結果は最初に書いたレジスタに入る。 注意する点として、レジスタと直接数値の演算は指定できない。 以下は具体的な記述の例である。



6.2.3   論理演算

  論理演算は 2 個の数に対して指定した論理演算を施す。論理積 AND, 論理和 OR, 排他的論理和 XOR がある。使えるアドレシングは算術演算と同じである。 以下は具体的な記述の例である。



6.2.4   比較

  比較命令は、2 個の数に対して CPU 内部で減算を行なう。 ただし、その結果はどこにも保存されず捨てられる。 比較命令の結果はフラグレジスタに反映され、2 個の数の大小関係を調べることができる。 算術比較 CPA (Compare Arithmetic) と、論理比較 CPL (Compare Logical) がある。 CPA は 2 個の数を 2 の補数として扱う。 CPL は 2 個の数を符号無し整数として扱う。 したがって同じビットパターンの数を比較しても、CPA と CPL では結果が異なる場合がある。 使えるアドレシングは算術演算と同じである。 以下は具体的な記述の例である。



6.2.5   桁移動

  桁移動(シフト)命令は、指定したレジスタの値を、 番地部(インデックスレジスタを含む)で指定したビット数分だけ、右または左にずらす。 シフト命令には算術シフトと論理シフトがあり、ずらした後の空白の埋め方が異なる。 ずらした結果押し出されたビットは OF (Overflow Flag) に入る。 左にずらして右端のビットが空白になる場合は 0 で埋められる。 右にずらして左端のビットが空白になる場合は、 算術シフトの場合は符号ビットと同じ値で埋められ、 論理シフトの場合は 0 で埋められる。 以下は実行例である。

GR0 = 1111000011110000 の場合、
SLA      GR0, 2 を実行すると、
GR0 = 1100001111000000
SRA      GR0, 2 を実行すると、
GR0 = 1111110000111100
SLL      GR0, 2 を実行すると、
GR0 = 1100001111000000
SRL      GR0, 2 を実行すると、
GR0 = 0011110000111100
になる。
  シフト命令の副次的な効果として、左に算術シフトすると 2, 4, 8, ... 倍、 右に算術シフトすると 1/2, 1/4, 1/8, ... 倍することができる。

GR0 = 0000000000001111 (15) の場合、
SLA      GR0, 2 を実行すると、
GR0 = 0000000000111100 (60)
SRA      GR0, 1 を実行すると、
GR0 = 0000000000000111 (7)
になる。
  以下は具体的な記述の例である。



6.2.6   分岐

  分岐命令は、次の命令ではなくて指定した番地の命令を実行する (指定した番地の命令にジャンプする)命令である。 それを指定した条件が満たされたときに実行する条件付ジャンプと、 条件を指定しないで常に実行する無条件ジャンプがある。 条件付ジャンプが実行されない場合は次の命令が実行される。 アドレシングはジャンプ先の番地(インデックスレジスタも使用可能)を指定する

  演算命令や比較命令と条件付ジャンプ命令を組み合わせることで、 条件判断を行ない、プログラムの処理を場合分けすることができる。

  命令の種類は、 演算結果について FR (フラグレジスタ) が正数を表す場合にジャンプする正分岐 JPL (Jump On Plus), 負数を表す場合にジャンプする負分岐 JMI (Jump On Minus), 演算結果が 0 でない場合にジャンプする非零分岐 JNZ (Jump On Non Zero), 演算結果が 0 である場合にジャンプする零分岐 JZE (Jump On Zero), 演算結果がオーバフローした場合、 または OF (Overflow Flag) がセットされている場合にジャンプするオーバフロー分岐 JOV (Jump On Overflow), 無条件でジャンプする無条件分岐 JUMP がある。



6.2.7   スタック操作

  スタックとは保存しておきたい値を一時的にメモリの特定の場所に記憶させておく仕組みである。 スタックの詳細は後述する。 CASL II ではスタックを操作する命令として PUSH と POP が用意されている。 PUSH は数値(インデックスレジスタも使用可能)をスタックに退避する。 POP はスタック最上部の値をレジスタに復帰する。 以下は記述例である。



6.2.8   サブルーチンコール、リターン

  サブルーチンコール命令 CALL は JUMP と似ており、指定した番地の命令を次に実行する。 JUMP との違いは、CALL 命令の次の命令の番地をスタックに退避してから JUMP する点であり、 後に RET を実行した時に CALL を実行したもとの場所に戻ってこられるようになっている。 サブルーチンとは、 プログラム中でメインの処理から一時離れて別の処理をさせたい時に使う仕組みであり、 プログラムをメインとサブの処理に分けて構造化することができる。 RET はサブルーチンから復帰するための命令であり、スタックから戻り番地を取り出して その場所の命令を次に実行する。 以下は記述例である。



6.2.9   練習問題

  次の機能を実現する命令を書きなさい。 1 命令で実現できない場合は複数の命令を組み合わせて実現しなさい。 (####) はメモリの #### 番地の内容を表す。 演算命令で指定がない場合は算術演算、算術比較とする。

  1. GR1 ← GR2

  2. GR1 ← (1000)

  3. GR1 ← (1000 + GR2)

  4. GR1 ← (GR2)

  5. (1000) ← GR1

  6. (1000 + GR2) ← GR1

  7. (GR2) ← GR1

  8. GR1 ← 1000

  9. GR1 ← GR1 + 1000

  10. GR1 ← GR1 + GR2 ; (論理加算)

  11. GR1 ← GR1 + (1000)

  12. GR1 ← GR1 + (1000 + GR2) ; (論理加算)

  13. GR1 ← GR1 - GR2

  14. GR1 ← GR1 - (1000)

  15. GR1 ← GR1 - (1000 + GR2) ; (論理減算)

  16. GR1 を GR2 と比較、符号付

  17. GR1 を (GR2) と比較、符号付

  18. GR1 を (1000) と比較、符号付

  19. GR1 を GR2 と比較、符号無

  20. GR1 を (GR2) と比較、符号無

  21. GR1 を (1000 + GR2) と比較、符号無

  22. GR1 を 2bit、左、算術シフト

  23. GR1 を GR2 で表される bit 数、右、算術シフト

  24. GR1 を 4bit、左、論理シフト

  25. GR1 を 3bit、右、論理シフト

  26. 演算結果が 0 でなければ 1000 番地にジャンプ

  27. 演算結果が 0 なら 1000 番地にジャンプ

  28. 演算結果 >= 0 なら 1000 + GR2 番地にジャンプ

  29. 演算結果 < 0 なら GR2 番地にジャンプ

  30. 1000 + GR2 番地にジャンプ

  31. GR1 をスタックに退避する

  32. GR1 + 10 をスタックに退避する

  33. GR1 にスタックから値を戻す

  34. 1000 番地のサブルーチンへジャンプ

  35. サブルーチンから戻る



6.2.10   練習問題

  GR1 が #A0F5 (1010000011110101B), GR2 が 1 のとき、 次のシフト命令を実行した時の GR1 の値を答えなさい。

  1. SLA GR1, 1, GR2

  2. SRA GR1, 2

  3. SLL GR1, 2, GR2

  4. SRL GR1, 0, GR2




6.3   条件判断の記述方法

  アセンブリ言語で条件判断の処理を記述したい場合は、 比較命令と条件付ジャンプ命令を組み合わせることで実現できる。 以下では具体的な命令の組み合わせ方、記述の仕方を示す。

  例として GR1, GR2 を調べて、GR1 == GR2 なら 1000 番地からの命令、 そうでなければ (GR2 != GR1 なら) 2000 番地からの命令を実行する。 (※ == は等しい、!= は等しくないことを表す)

	CPA	GR1, GR2	; GR1 と GR2 を比較する
	JZE	1000		; GR1 == GR2 なら 1000 番地に行く
	JUMP	2000		; そうでなければ 2000 番地に行く

ちょっとひねった書き方だが、条件判断を逆にして次のように書くこともできる。

	CPA	GR1, GR2	; GR1 と GR2 を比較する
	JNZ	2000		; GR2 != GR1 なら 2000 番地に行く
	JUMP	1000		; そうでなければ 1000 番地に行く
  次の例として GR1, GR2 の大小関係を調べて、GR1 < GR2 なら 1000 番地からの命令、 そうでなければ (GR2 <= GR1 なら) 2000 番地からの命令を実行する。

	CPA	GR1, GR2	; GR1 と GR2 を比較する
	JMI	1000		; GR1 < GR2 なら 1000 番地に行く
	JUMP	2000		; そうでなければ 2000 番地に行く

条件判断を逆にして、次のように書くこともできる。

	CPA	GR1, GR2	; GR1 と GR2 を比較する
	JPL	2000		; GR2 <= GR1 なら 2000 番地に行く
	JUMP	1000		; そうでなければ 1000 番地に行く
  気をつける点としては、JPL には二つの値が等しい場合も含まれるので注意が必要である。 逆に、JMI には二つの値が等しい場合は含まれない。 例として GR1, GR2 の大小関係を調べて、GR2 < GR1 なら 1000 番地からの命令、 そうでなければ (GR1 <= GR2 なら) 2000 番地からの命令を実行する。 これを次のように書くのは間違いである。

	CPA	GR1, GR2	; GR1 と GR2 を比較する
	JPL	1000		; GR2 <= GR1 なら 1000 番地に行く
	JUMP	2000		; そうでなければ 2000 番地に行く
  この場合、GR1 と GR2 が等しい場合も 1000 番地にジャンプしてしまうので正しくない。 JPL を使いたい場合、正しくは次のように書く。こうすれば等しい場合は先に 2000 番地に ジャンプするので問題ない。

	CPA	GR1, GR2	; GR1 と GR2 を比較する
	JZE	2000		; GR1 == GR2 なら先に 2000 番地に行く
	JPL	1000		; GR2 < GR1 なら 1000 番地に行く
	JUMP	2000		; そうでなければ 2000 番地に行く
  または、次のように比較命令のオペランドを逆にすることで命令を減らすことができる。

	CPA	GR2, GR1	; GR2 と GR1 を比較する
	JPL	2000		; GR1 <= GR2 なら 2000 番地に行く
	JUMP	1000		; そうでなければ 1000 番地に行く

JMI を使うと次のようになる。

	CPA	GR2, GR1	; GR2 と GR1 を比較する
	JMI	1000		; GR2 < GR1 なら 1000 番地に行く
	JUMP	2000		; そうでなければ 2000 番地に行く

6.3.1   練習問題

  次の条件判断を実現する命令列を示しなさい。 条件に合わなければ L2 にジャンプするようにしなさい。 L1, L2 はある特定の番地を表すものとし、解答中ではそのまま書きなさい。 演算命令で指定がない場合は算術演算、算術比較とする。

  1. GR1 と GR2 を比較して GR1 < GR2 なら L1 にジャンプする。

  2. GR1 と GR2 を比較して GR1 <= GR2 なら L1 にジャンプする。

  3. GR1 と GR2 を比較して GR1 == GR2 なら L1 にジャンプする。

  4. GR1 と GR2 を比較して GR1 != GR2 なら L1 にジャンプする。

  5. GR1 と GR2 を比較して GR2 < GR1 なら L1 にジャンプする。

  6. GR1 と GR2 を比較して GR2 <= GR1 なら L1 にジャンプする。

  7. 符号付数として GR1 + GR2 を計算した時、あふれがあれば L1 にジャンプする。

  8. 符号無数として GR1 + GR2 を計算した時、あふれがあれば L1 にジャンプする。

  9. GR1 を 1 ビット右に算術シフトした時、はみ出したビットが 1 なら L1 にジャンプする。

  10. GR1 を 1 ビット左に論理シフトした時、はみ出したビットが 0 なら L1 にジャンプする。




6.4   スタック

  スタックとは、レジスタを別の用途に使いたい時に、 現在のレジスタの値を一時的にメモリに退避するための仕組みである。

  スタックを使う時には、使い始める前に SP (スタックポインタ) を初期化する必要がある。 SP はスタックの領域をどこまで使ったか管理するためのアドレス (=最後に使った領域を指す) を保持する。

  スタックの領域はアドレスの大きい方から使われ始めて、 使用量が増えるにしたがってアドレスの小さい方に増えていく。



スタック

  アドレスの値の小さい領域は通常 ROM, OS, ユーザのプログラムが配置されるため、 スタックはアドレスの値の大きい領域に配置されることが多い。


6.4.1   スタックの使用例

  1. 最初の時点で GR0, GR1, GR2 にはそれぞれ 10, 20, 30 が入っているとする。

  2. PUSH 0, GR0 を実行すると、 GR0 の値が SP の指す上の場所のスタックに書き込まれる。

  3. その後続けて PUSH 0, GR1, PUSH 0, GR2 を実行すると各値が記録される。 SP はスタックが消費されるのにつれて、 アドレスの値の小さい方を指し示すように変更される。

  4. この時点で各レジスタの値はスタックに記録されているので、 他の用途にレジスタを使用して値が変化してしまっても、 後から復活させることができる。

  5. レジスタの値を戻す時には PUSH を実行した順番と逆に POP を実行する。 POP GR2 を実行するとスタックの最後に使った領域から値が取り出されて GR2 に値が代入される。 SP は値を取り出した領域の前の領域を指すように内容が変更される。

  6. その後続けて POP GR1, POP GR0 を実行すると各レジスタに値が代入されてスタックは元の状態に戻る。



6.4.2   練習問題

  各レジスタの初期値が GR0=10, GR1=20, GR2=30 となっている状態で、 次のスタック操作を行なった後に各レジスタの値がどうなるか答えなさい。

(1)	PUSH	0, GR0
   	PUSH	0, GR1
	PUSH	0, GR2
	POP	GR0
	POP	GR1
	POP	GR2	の一連の操作を ★ とする。★ を 2 回繰り返す。

(2) ★ を 3 回繰り返す。

(3)	PUSH    0, GR0	(4)	PUSH    0, GR0
	PUSH    0, GR1		PUSH    0, GR1
	PUSH    0, GR2		POP     GR2
	PUSH    0, GR0		POP     GR1
	POP     GR2		POP     GR0
	POP     GR1
	POP     GR0



6.5   CASL II によるプログラミング


6.5.1   アセンブリ言語でプログラムを作る時の考え方

  1. どのレジスタをどういう用途に使うか決める

  2. 必要なデータをレジスタに読み込む

  3. 計算をする、結果がレジスタに入る

  4. レジスタにある結果をメモリに書き込む

  以下は注意すべき点である。



6.5.2   CASL II のプログラムの書き方

  以下はプログラムの書き方を示すためのサンプルプログラムである。

EX0	START			; 最初に必ずラベルと、疑似命令 START を書く ---(1)
	LD	GR0, A		; A 番地のデータを GR0 に入れる ---(2)
	ADDA	GR0, B		; B 番地のデータを GR0 に加算する
	ST	GR0, C		; GR0 を C 番地に記録する
	RET			; 実行部分の最後には RET を書く ---(3)

A	DC	5		; データを置く領域 ---(4)
B	DC	3		; データを置く領域
C	DS	1		; 答を置く領域、初期値は不定 ---(5)
	END			; プログラムの最後には疑似命令 END を書く ---(6)
  (1), (3), (6) について。 CASL II のプログラムは 3 個の命令、疑似命令 START, RET, END を、 次に示すように必ず所定の位置に書く必要がある。

ラベル	START
	…
	プログラムの実行部分
	…
	RET
	…
	データの定義
	…
	END
  プログラムの先頭には、そのプログラムを表すラベルと疑似命令 START, プログラムの実行部分の後に命令 RET, 通常は実行部分に続けてデータの領域が置かれるが、その後(プログラム全体の最後)に 疑似命令 END を書く。 疑似命令とは通常の命令のように CPU で実行されるものではなく、 アセンブラに必要な指示を与えるための記述である。 この場合、START, END はアセンブラにプログラムのはじめと終りを伝える。

  COMET II のプログラムは OS から CALL されて実行されるので、 RET の実行後は OS の管理下に戻ることになる。 したがって RET がないと適切なタイミングで OS の管理下に戻ることができず、 暴走した状態となるので RET は必ず必要である。

  (2) について。 上記のプログラムで EX0, A, B, C は「ラベル」と呼ばれる記述法である。 ラベルとはプログラム中で命令やデータの番地を参照する必要がある場合に、 番地の値を直接書く代わりに使われる表現で、番地を表す仮の名前といった意味合いを持つ。 例として LD GR0, A ではラベル A が付いている番地からデータ 5 が読み込まれるが、 この番地はアセンブラで機械語のプログラムに変換した後、実行時にメモリに読み込むまで確定しない。 このような値が確定しない番地については、仮にラベルを付けておくことで、 ラベルの名前を使ってプログラムが記述できるようになる。

  (4), (5) について。 データの定義は DC, DS という 2 種類の疑似命令で行なう。 DC はその場所に 1 ワードの領域を確保して、指定した値が初期値として代入される。 DS はその場所に必要な大きさの領域を確保する。 DS に指定した値は領域のワード数と解釈される。 DS 10 と指定したら 10 ワードの大きさの領域となる。 DS には初期化の機能はないので領域にどのような値が入るかはわからない。 DC と DS の使い分けは、初期化が必要なら DC、 複数ワードの領域が必要で初期化が不要なら DS となる。



6.5.3   プログラムの例


  1. A と B の大きい方を C に記録する

EX1	START
	LD	GR0, A		; A 番地のデータを GR0 に入れる
	CPA	GR0, B		; B 番地のデータを GR0 と比較
	JPL	L1		; B <= A なら L1 に行く
	LD	GR0, B		; A < B の時は B 番地のデータを GR0 に
L1	ST	GR0, C		; GR0 を C 番地に記録する
	RET

A	DC	5		; データを置く領域
B	DC	3		; データを置く領域
C	DS	1		; 答を置く領域
	END
解説
EX1 : プログラムの先頭にはこのようにラベルを付ける
START : プログラムの先頭には START を書く
L1 : ジャンプする先にラベルを付けると、番地を指定する代わりに使える
RET : 実行部分の最後には RET を書く
A,B,C : データにもラベルを付けると参照するのが楽
DC : 1 ワードの領域を確保し、指定した定数で初期化する
DS : 指定した定数分のワードの領域を確保する、初期化はなし
END : プログラムの最後には END を書く

  2. A × B を求める (A + A + A + ... + A, B 回 A を足す)

EX2	START
	LAD	GR0, 0		; GR0 を 0 にする、答が入る
	LAD	GR1, 0		; GR1 を 0 にする、足した回数を数える
	JUMP	L2		; B=0 でも正しく動作するように L2 に行く
L1	ADDA	GR0, A		; GR0 に A の内容を足す
	LAD	GR1, 1, GR1	; 回数カウンタの GR1 に 1 を足す
L2	CPA	GR1, B		; GR1 が B になったか比較する
	JNZ	L1		; まだ B になってなければ L1 から繰り返し
	ST	GR0, C		; 答を C に記録する
	RET

A       DC      5               ; データを置く領域
B       DC      3               ; データを置く領域
C       DS      1               ; 答を置く領域
        END

  3. A × 10 を求める (シフト命令を使う)

EX3	START
	LD	GR0, A		; GR0 に A 番地のデータを読み込む
	SLA	GR0, 1		; 左に 1 bit シフト、× 2 になる
	LD	GR1, GR0	; その結果を一旦 GR1 に記録する
	SLA	GR0, 2		; 左に 2 bit シフトで × 4、全体では × 8
	ADDA	GR0, GR1	; 8 倍の結果にさっきの 2 倍の結果を足す
	ST	GR0, B		; 10 倍の結果を B に記録する
	RET

A       DC      5               ; データを置く領域
B       DS      1               ; 答を置く領域、初期値は決まっていない
        END

  4. A 回の繰り返しのプログラムの考え方

EX4	START
	LAD	GR1, 0		; 回数をカウントする GR1 を 0 にする
				; GR0 はこの用途には使用不可、インデックス
				; レジスタに指定できないため
	JUMP	L2		; 繰り返し 0 回でも対応できるよう L2 へ
L1				; 
				; 実際はここに繰り返したい内容を書く
				;
	LAD	GR1, 1, GR1	; カウンタ GR1 を +1 する
L2	CPA	GR1, A		; A 回繰り返したか回数をチェックする
	JNZ	L1		; まだ A 回になってなければ L1 から繰り返す
	RET

A	DC	10
	END

  5. A 番地から並んでいる B 個のデータの和を求める

EX5	START
	LAD	GR0, 0		; 答を入れる GR0 を 0 にする
	LAD     GR1, 0          ; 回数をカウントする GR1 を 0 にする
	JUMP	L2              ; 繰り返し 0 回でも対応できるよう L2 へ
L1	ADDA	GR0, A, GR1	; GR0 に (A + GR1) の内容を足す
	LAD	GR1, 1, GR1	; GR1 を +1 する
L2	CPA	GR1, B		; GR1 が B になったか調べる
	JNZ	L1		; なってなければ L1 から繰り返す
	ST	GR0, C		; 答えを C に記録する
	RET

A	DC	3		; ここから足す対象のデータが並んでいる
	DC	5
	DC	7
	DC	2
B	DC	4		; 足す個数が 4 個なので 4 を書く
C	DS	1		; 答えを記録する領域
	END

  6. A 番地から並んでいる B 個のデータを昇順(小さい順)に並べ換える

EX6	START
	LD	GR4, B		: B-1 を GR4 に書き込む
	LAD	GR4, -1, GR4	: 
	LAD	GR2, 0		; 外側のループ i
L1	LAD	GR3, 1, GR2	; 内側のループ j = i + 1
L2	LD	GR0, A, GR2	; (A + GR2) を GR0 に読み込む
	CPA	GR0, A, GR3	; それを (A + GR3) と比較する
	JMI	L3		; (A + GR2) < (A + GR3) なら L3
	LD	GR1, A, GR3	; ここからの 3 行で
	ST	GR0, A, GR3	; (A + GR2) と (A + GR3) を
	ST	GR1, A, GR2	; 入れ換えている
L3	LAD	GR3, 1, GR3	; 内側のループ j を +1 する
	CPA	GR3, B		; B になったか調べる
	JMI	L2		; なってなければ L2 から繰り返し
	LAD	GR2, 1, GR2	; 外側のループ i を +1 する
	CPA	GR2, GR4	; B-1 になったか調べる
	JMI	L1		; なってなければ L1 から繰り返し
	RET

A	DC	3		; 並べ換える対象のデータの先頭
	DC	5
	DC	7
	DC	6
B	DC	4		; 個数が 4 個なので 4 を書く
	END
  このプログラムは以下のプログラムとほぼ同じ内容になる

void sort(void)
{
    int		A[B], tmp;

    for (i = 0; i < B - 1; i++) {
	for (j = i + 1; j < B; j++) {
	    if (A[j] <= A[i]) {
		tmp = A[j]; A[j] = A[i]; A[i] = tmp;
	    }
	}
    }
}


6.5.4   練習問題

  次の機能を実現するプログラムをアセンブリ言語で作成しなさい。 A, B, C, D は必要に応じてデータ領域を確保しなさい。 演算命令で指定がない場合は算術演算、算術比較とする。

  1. A + B, A - B をそれぞれ C, D に記録する

  2. A * 3 を C に記録する

  3. A, B の小さな方の値を C に記録する

  4. A == B なら 0 を、A != B なら 0 以外の値を C に記録する

  5. A の 0/1 を反転して C に記録する (排他的論理和を使う)

  6. A の 2 の補数を求めて C に記録する

  7. A * 7 を C に記録する

  8. 1 + 2 + 3 + ... + A を求めて C に記録する

  9. A, B, C の最小値を D に記録する

  10. A 番地からの B 個のデータを降順(大きい順)に並べ換える