命令定義


■基本命令セット

マイクロプロセッサELC-1の基本的な命令のみからなる命令セットが用意されている。 この基本命令セットには、市販されている一般的なマイクロプロセッサには共通に含まれているいくつかの命令が欠落しているが、効率に目をつぶればおおよそどのようなプログラムでも記述できるはずである。

■命令定義の書式

ELC-1が実行可能な命令は、以下の書式にしたがって命令定義ファイルに記述する。 命令定義ファイルのファイル名の拡張子は".ops"とすること。 命令定義ファイルによる命令定義には、次の2つの意味がある。
  1. 命令実行のためにプロセッサ内部で行うマイクロ操作をレジスタ転送言語(Register Transfer Language, RTL)によって記述する。
  2. アセンブリ言語プログラムをアセンブラが解釈して2進コードに変換するために必要な情報を定義する。
命令定義ファイルには、複数の命令定義を記述可能であり、各命令の定義は空行(改行のみがあり他には文字がない行)によって区切られる。空行によって区切られた部分を命令定義ブロックと呼ぶ。命令定義例として、命令'LD AC, arg'の命令定義ブロックおよび命令'JMP label'の命令定義を以下に示す。

	LD AC, arg                     ←命令書式
		nbyte 2                    ←nbyte(バイト数指定)
		opcode 10001100            ←opcode(命令コード)
		0: MAR <- PC, PC <- inc    ←実行内容
		1: MDR <- mem
		2: IR <- MDR
		3: MAR <- PC, PC <- inc
		4: MDR <- mem
		5: AC <- MDR

	JMP label
		alias JMP (arg)            ←alias(別名指定)

命令定義ブロックの1行目は命令書式である。命令書式(1行目)は行の先頭から書く。 2行目以降は、先頭に1個以上のタブまたは半角空白を置く。命令書式の記述を1行目とする以外は、2行目以降の記述順序は自由である。
  命令書式
命令書式は、最初の1行に記述し、行の先頭から書く。この命令書式に基づいてアセンブラはプログラム中の命令を識別する。命令書式中の','(カンマ)は、 アセンブラによるアセンブルの際に空白に置換される。したがって、プログラムを見やすくするためにカンマを使用することは自由であるが、カンマの有無を命令区別のために利用することはできない。

'arg'と'label'は特別な語であり、プログラム中の命令の対応する語が即値あるいはラベルである場合に命令パターンがマッチすることを意味する。詳細は「アセンブラの動作」の項を参照のこと。

  nbyte
この命令のバイト数を指定する。命令のオペコードは1バイトであり、 オペランドのない命令ではバイト数は1、オペランドが1バイトの命令では バイト数は2となる。

  opcode
この命令のオペコードを指定する。オペコードは1バイトであり、8ビットの 2進数によって記述する。

  alias
この命令が、他の命令の別名であることを定義する。alias指定がある場合は、 この他のnbyteやopcodeなどの指定はすべて無効となる。

例えば、上の例では、命令'JMP label'は命令'JMP (arg)'の別名であると 定義している。aliasによる別名定義よりも前に、別名の元となる命令 (この場合は'JMP (arg)')の命令定義がなされていなければならない。

  実行内容
プロセッサが命令を実行する際に行うマイクロ操作をRTLによって記述する。 マイクロ操作の記述は、実行条件およびマイクロ操作列からなる。
	実行条件: マイクロ操作列
  1. 実行条件
    実行条件は、実行タイミングならびに状態フラグ条件からなる。
    実行タイミングは、命令実行開始時点からのクロック数によって指定し、 命令実行開始後の最初のクロック数を0とする。
    実行条件は、状態フラグの値によってマイクロ操作を行うか否かを選択する。

    状態フラグ条件がない場合は、実行条件は実行タイミングを表す整数のみと なる。実行タイミングと状態フラグ条件の両方がある場合は、実行タイミング を表す整数の直後に'&'をおき、状態フラグ名を連結する。
    なお、実行タイミングがなく、状態フラグ条件のみの実行条件は禁止する。

    例えば最初のタイミング0で実行するマイクロ操作列の場合は、

    	0: マイクロ操作列
    
    となる。
    タイミング4で状態フラグC=1の場合に実行するマイクロ操作列の場合は、
    	4&C: マイクロ操作列
    
    となる。
    タイミング4で状態フラグZ=1かつ状態フラグC=0の場合に実行するマイクロ 操作列の場合は、
    	4&Z&NC: マイクロ操作列
    
    となる。
    以上のように、状態フラグの値が1の場合に実行するならば状態フラグ名、状態フラグの値が0の場合に実行するならば状態フラグ名の前に否定(not)を意味する'N'を付けたものが状態フラグ条件となる。
    また、複数の状態フラグを検査する場合は、'&'によって連結すればよい。

    なお、以下のように実行タイミングが同じものがある場合は、該当するタイミングにおいてすべてのマイクロ操作が実行される。

    	5: マイクロ操作列1
    	5&Z: マイクロ操作列2
    
    すなわち、タイミング5において状態フラグZ=1ならばマイクロ操作列1とマイクロ操作列2の両方が実行される。タイミング5において状態フラグZ=0ならばマイクロ操作列1だけが実行される。

  2. マイクロ操作列
    マイクロ操作列は、単一あるいは複数のマイクロ操作からなる。複数のマイクロ操作がある場合は、間に','(カンマ)を置く。よってマイクロ操作列は
    	マイクロ操作
    
    あるいは
    	マイクロ操作, マイクロ操作, ...., マイクロ操作
    
    となる。
    後者の形式では、指定されたマイクロ操作は同時に実行される。
    マイクロ操作の記述方法については、「マイクロ操作の記述」の項を参照のこと。
ELC-1における制約
ELC-1では、各命令実行のタイミング0~2は命令フェッチ(メモリからの命令のオペコードの読み出し)である。命令を読み出して命令レジスタIRに代入するまでは、それがどのような命令であるかは不明である。したがって、タイミング0~2では全ての命令において命令フェッチのために共通のマイクロ操作を実行しなければならない。

ELC-1の命令フェッチをマイクロ操作によって表すと以下の通りである。

	0: MAR <- PC, PC <- inc
	1: MDR <- mem
	2: IR <- MDR
すべての命令定義において、タイミング0~2のマイクロ操作をこのようにすること。


■マイクロ操作の記述

データ転送(転送元と転送先のビット数が同じ場合)

レジスタあるいはメモリからレジスタへのデータの転送は
	データ転送先 <- データ転送元
と記述する。データ転送先には PC, B, X, MAR, MDR, MDRH, IR、 データ転送元には、レジスタPC, AC, B, WA, X, MAR, MDR, MDRWまたはmemが指定できる。 memは、レジスタMARの内容をアドレスとする位置のメモリ内容を表す。

なお、ACWAをデータ転送先とする場合については、「ALUを用いた演算」(無処理)の項を参照のこと。

MDR <- AC
 アキュムレータACの値をレジスタMDRに転送する
MAR <- PC
 レジスタPCの値をレジスタMARに転送する
MDRH <- MDR
 レジスタMDRの値をレジスタMDRHに転送する
X <- MDRW
 レジスタMDRWの値をレジスタXに転送する
MDR <- mem
 レジスタMARをアドレスとするメモリ位置の内容をレジスタ MDRに転送する

ELC-1における制約
ELC-1では、メモリの内容をレジスタMDR以外のレジスタに直接転送できない。 これは、メモリのデータ出力がレジスタMDRの入力に接続されているためである。 データ転送マイクロ操作の文法としては、memMDR以外のレジスタに転送するように記述することは可能であるが、ELC-1の仕様としてメモリからのデータ転送は、転送先がMDRである
	MDR <- mem
のみが使用でき、これ以外は禁止する。

ELC-1における制約
ELC-1では、レジスタMDRHの転送元としてレジスタMDRのみが指定できる。

データ転送(転送元と転送先のビット数が異なる場合)

ビット数の異なるレジスタ間のデータの転送は、以下のように記述する。 ("a:", "b:"などは記述する必要はない)
	a: データ転送先16ビットレジスタ <-H データ転送元8ビットレジスタ
	b: データ転送先16ビットレジスタ <-L データ転送元8ビットレジスタ
	c: データ転送先8ビットレジスタ <-H データ転送元16ビットレジスタ
	d: データ転送先8ビットレジスタ <-L データ転送元16ビットレジスタ
	e: データ転送先16ビットレジスタ <- データ転送元8ビットレジスタ
	f: データ転送先8ビットレジスタ <- データ転送元16ビットレジスタ
記述aは、転送元の8ビットデータを転送先16ビットレジスタの上位8ビットに転送する。
記述bは、転送元の8ビットデータを転送先16ビットレジスタの下位8ビットに転送する。
記述cは、転送元データの上位8ビットを転送先レジスタに転送する。
記述dは、転送元データの下位8ビットを転送先レジスタに転送する。
記述eは、記述bと等価である。
記述fは、記述dと等価である。
なお、ACWAをデータ転送先とする場合については、 「ALUを用いた演算」(無処理)の項を参照のこと。

MDR <- X
 レジスタXの下位8ビットをレジスタMDRに転送する
MDR <-L WA
 レジスタWAの下位8ビットをレジスタMDRに転送する(上と同じ)
X <-H AC
 アキュムレータACの値をレジスタXの上位8ビットに転送する

ELC-1における制約
ELC-1では、レジスタMAR, PCに8ビットデータを転送することはできない。

状態フラグの設定

状態フラグに1または0を設定するには
	状態フラグ <- 1(0)
と記述する。状態フラグとしてS, Z, C, Hが指定できる。
C <- 1
 状態フラグC(carry)を1に設定する
H <- 0
 状態フラグH(halt)を0に設定する

レジスタ値のインクリメント、デクリメント

単に値を記憶するだけでなく、現在記憶している値を増加したり減少したりできる機能が備わったレジスタもある。
値のインクリメント(1増加)とデクリメント(1減少)は、データ転送と同様に記述する。 ただし、インクリメントの場合はデータ転送元を'inc'、 デクリメントの場合はデータ転送元を'dec'とする。
PC <- inc
 レジスタPCの値をインクリメントする
X <- dec
 レジスタXの値をデクリメントする

なお、ELC-1では、レジスタPCにはインクリメント機能、レジスタ WAXMARBにはインクリメント機能とデクリメント機能がある。

アキュムレータACに対して同様にインクリメントとデクリメントを指令することができるが、ACの場合はレジスタ(アキュムレータ)の機能ではなく、ALUを用いてインクリメントとデクリメントを行う。 詳細は、ALUを用いた演算の項を参照のこと。 レジスタWAのインクリメント/デクリメントはALUを使用せずに行うので、ALUを用いた(ACに関する)演算とレジスタWAのインクリメント/デクリメントを同時に行うことができる。
なお、WAのインクリメント/デクリメントはALUを使用しないため、フラグが変化しないので注意のこと。

実行タイミングの分岐

各命令の処理内容を表すマイクロ操作には、実行条件として実行タイミングが規定されている。 ELC-1内部にはタイミングレジスタがあり、このタイミングレジスタの値に基づいてマイクロ操作を起動する。 タイミングレジスタは、各命令実行開始時に0に設定され、クロックごとに値が1ずつ増加する。 命令について定義された最後のタイミング条件のマイクロ操作を起動すると、タイミングレジスタの値は0にリセットされる。

タイミングレジスタの値をマイクロ操作によって強制的に変更することで、命令実行手順を変更することができる。タイミングレジスタの値の変更は

	-> 変更値
である。 特別な場合として'-> 0'は、現在の命令実行を中断し、次の命令実行へ進むことを意味する。
以下に、タイミングレジスタの変更の例を示す。
	JC  arg       
		nbyte 2
		opcode 10000100
		0: MAR <- PC, PC <- inc
		1: MDR <- mem
		2: IR <- MDR
		3&NC: -> 0                  ←条件依存タイミングレジスタ変更
		3: MAR <- PC, PC <- inc
		4: MDR <- mem
		5: PC <- MDR
これは状態フラグCの値が1ならば、分岐(プログラムカウンタPCの値の変更)を行う命令である。実行タイミング3では、PCの値をMARに転送し、この命令のオペコードの直後にある分岐先アドレスをメモリから読み出す準備をしている。同時に、実行条件3&NCによって状態フラグCの値を検査し、C=0ならば次の実行タイミングを0にする(3&NC: -> 0)。 これにより、この命令の実行を中断し、直後の命令の命令フェッチへと進む。 すなわち、C=0ならば分岐は行わない。

C=1ならば条件3&NCは偽なので、タイミングレジスタへの0の代入は行われず、タイミングレジスタの次の値は4となる。そして、4以降では分岐先アドレスを実際にメモリから読み出し、PCへ代入することで分岐を行う。すなわち、C=1ならば分岐が行われる。

なお、現在の実行タイミングkにおける'-> (k+1)'、および最後の実行タイミングにおける'-> 0'は省略できる。

メモリの制御

メモリにデータを書き込む場合には、メモリ制御信号WRに値1を出力して、1クロック期間以上待つ。これはマイクロ操作
	WR = 1
によって行う。メモリからデータを読み出す場合には'WR = 0'とする必要があるが、'WR = 0'は省略できる。

ALUを用いた演算

ALUによって実行可能な演算は以下の通りである。 図1に示すように、ALUへの2つの入力のうち、1つは常にアキュムレータACまたはレジスタWAの内容である。 またALUによる処理結果は、必ずACまたはWAに格納される(例外として、処理結果がどこにも格納されない場合がある)。 ALU処理の'無処理'は、ACまたはWAに値をそのままロードする場合に使用する。

なお、ALUへ入力するデータとALU処理結果は、データのビット数とALU処理内容に依存して取り扱われる。詳細は「マイクロプロセッサELC-1の構成要素」(ALU)を参照のこと。

ALUを用いた演算と状態フラグの変化

ALUを用いた演算では、演算結果に応じて状態フラグが設定される。 演算の種類と状態フラグの変化の有無を表1に示す。 表1では、'↑↓'は演算結果に応じて状態フラグがセットまたはリセットされることを表し、'-'は状態フラグが変化しないことを表す。 インクリメントおよびデクリメントでは、状態フラグCが変化しないことに注意。

表1. 演算の種類と変化する状態フラグ
演算の種類SZC
加算
減算
↑↓↑↓↑↓
AND(論理積)
OR(論理和)
XOR(排他的論理和)
↑↓↑↓↑↓
シフト ↑↓↑↓↑↓
インクリメント
デクリメント
↑↓↑↓
ビット反転 ↑↓↑↓
符号反転 ↑↓↑↓
無処理

状態フラグのみの設定

ALUによる演算の結果に応じて状態フラグに値が設定されるが、アキュムレータACまたはWAに演算結果が転送されるので、演算前の結果が失われることがある。 これは、ACの値と他の値を比較して、ACの値が大きければACの値を用いて何らかの処理を行うといった場合に不便である。
そこで、ALUに演算を行わせて状態フラグを設定するが、演算結果をACWAに転送しないようにすることができる。これには、単に演算結果転送先を意味する'AC <-'または'WA <-'を削除すれば良い。
AC - MDR
 アキュムレータACとレジスタMDRの値を比較し、その結果に応じて状態フラグを設定する
AC & MDR
 アキュムレータACとレジスタMDRの値の論理積を求めることで、AC内の特定のビットが1であるか0であるかを調べる

AC - MDRでは、もしもMDRの値が10のとき、ACが10以上ならば引き算の結果は正または0なので、状態フラグSは0になる。 さらにACが10よりも大きければ、状態フラグZも0になる。 ACが10未満ならば引き算の結果は負なので、Sは1になる。
よって、ACの値と比較対象(この例ではMDR)との大小関係により、状態フラグは表2のように設定される。表2では、'・'は0または1のいずれかになることを表している。

表2. 比較における状態フラグの設定
大小関係 SZC
AC>比較対象 000
AC≧比較対象 00
AC=比較対象 010
AC≦比較対象
AC<比較対象 101
AC≠比較対象 0

AC & MDRでは、もしもMDRの値が1のとき、ACのLSB(Least Significant Bit, 最下位ビット)が1ならば論理積は1、LSBが0ならば論理積は0となる。 よってAC & MDRの後で状態フラグZの値が0ならばACのLSBは1、Zの値が1ならばACのLSBは0であることが分かる。
MDRの値を2とすればACの最下位から2ビット目、MDRの値を8とすればACの最下位から4ビット目の検査ができる。 また、MDRの値を6とすればACの最下位から2ビット目と最下位から3ビット目の少なくともいずれかが1である、あるいは両方とも0である、のどちらであるかを検査することができる。


■データ転送のタイミング

レジスタ、状態フラグ、タイミングレジスタへのデータ転送は、クロック信号に同期して行われる。実行タイミングnにおいて実行するように指定されたデータ転送は、実行タイミングnの終わりのクロック信号立上り(値0から値1への変化)の時点で行われる。このクロック信号立上りまでは、レジスタの値は変化しない。したがって、同一実行タイミングにおいて実行される複数のレジスタ間データ転送は、その記述の順序には意味がない。

例えば、命令フェッチのため実行タイミング0で行うマイクロ操作は

	0: MAR <- PC, PC <- inc
と記述されている。これは、現在のプログラムカウンタPCの値をレジスタMARに転送し、PCをインクリメントするという2つのマイクロ操作からなる。PCにインクリメント後の新しい値が設定されるのは、実行タイミング0と実行タイミング1の境界のクロック信号立上り時点であり、それまではPCの値に変化はない。したがって以下のように記述しても、MARPCに設定される値は上の記述の場合と同じである。
	0: PC <- inc, MAR <- PC

さらに、他のレジスタを使わずにレジスタWAとレジスタXの内容を入れ替えることもできる。これは、以下のように記述すればよい(実行タイミング5において行う場合)。

	5: WA <- X, X <- WA


■命令定義例

命令'LD AC, (arg)'を例に、命令定義の実際を示す。この命令は、命令定義ファイル中では以下のように定義される。
LD AC, (arg)
	nbyte 3
	opcode 10010010
	0: MAR <- PC, PC <- inc
	1: MDR <- mem
	2: IR <- MDR
	3: MAR <- PC, PC <- inc
	4: MDR <- mem, MAR <- PC, PC <- inc
	5: MDRH <- MDR, MDR <- mem
	6: MAR <- MDRW
	7: MDR <- mem
	8: AC <- MDR
これは、1バイトの命令コードと2バイトのオペランドからなる3バイト長の命令であり、オペランドをメモリアドレスとして用いてメモリからデータを読み出し、ACに保存する。

現在のPCの値が125(10進数)であり、メモリの内容が図4(a)のようになっているとき、命令定義にしたがってプロセッサが動作するときのレジスタの値の変化の様子を図4(b)に示す。

クロックPCMARMDRHMDRMDRWIRAC
0125
1126125
2 146
3 146
4127126
5128127 $01$XX01
6 $01$B7$01B7
7 439
8 115
0(次命令) 115
(a) (b)
図4. 命令実行の様子. (a)メモリ内容, (b)命令実行中のレジスタ値

オペランドはメモリアドレス126, 127に格納されており、命令実行クロック4で最初の1バイト(8ビット), 命令実行クロック5で次の1バイト(8ビット)を順に読み出す。この命令では、オペランドは16ビットのメモリアドレスとして利用するので、これら2バイトを連結して16ビットのオペランドデータ(メモリアドレス)とする必要がある。

2つのバイトデータを連結して16ビットデータとするにはMDRWを利用する。すなわち、オペランドの最初の1バイト(8ビット)をMDRHに転送し、オペランドの次の1バイト(8ビット)がMDRに入った時点でMDRWが16ビットのオペランドとなる。
上の例ではオペランドの2つのバイトデータの値は$01, $B7(いずれも16進数)であり、これらを連結した16ビットの値は$01B7(16進数)となる。$01がMDRHに、$B7がMDRに入ったときMDRWが16ビットの$01B7となる。

MDRWの16ビット値をMARに転送してメモリアドレスとして使用してメモリからデータを読み出し、最後にACに転送する。