KUE-CHIP2という教育用マイコンのエミュレータを作った話 (2)


早稲田大学 Advent Calendar 2016」10日目の記事です。

諸事情によりカレンダーを2日分も占領する形になってしまい申し訳ありません。
(一つの記事にすると長すぎる、また書きたいことが多すぎて書ききれなかった事が原因です。)
本稿執筆時点でカレンダーにはまだ空きが残っているので、記事のネタをお持ちの方は検討してみてください。

本稿ではKUE-CHIP2のアーキテクチャの詳細と、前回簡単にしかできなかったKUE-CHIP2エミュレータ&アセンブラ環境「KEMU-IDE」についてもう少し詳しく解説させていただく予定です。

前回: KUE-CHIP2という教育用マイコンのエミュレータを作った話 (1)

KUE-CHIP2 アーキテクチャ

前回、KUE-CHIP2を搭載した、学生実験でも使われる「KUE-CHIP2 教育用ボード」について詳しく紹介しました。教育用ボードで使用できる機能は、メモリやCPU内部のレジスタ値を検査したり設定したりすることや、プログラムの実行開始などの司令を送ることだったのですが、それらの機能の殆どは1辺3cm程度のKUE-CHIP2のCPUパッケージの中、さらにいえば1辺約5mmのダイの上に実装されているのです。(製造プロセスは1.2μmです。)KUE-CHIP2の中心的な機能以外にも様々な機能が詰め込まれている結果、CPU内の構造は極めて複雑に成っている上、ピン数も84ピンとこの規模のCPUにしては多い方だと思います。(特に様々なバス、レジスタを外部から参照できる機能が複雑化の原因だと思われます。)よって、この記事では機械語及びそれに対応する主要な機能のみを解説します。もっと詳しく知りたい方は、「KUE-CHIP 2設計ドキュメント (神原弘之他, 1993/1/16, 京都高度技術研究所, v1.10)」に詳しく載っている他、それ以外の参考文献については(無料で読めるもの、読めないものがあります)この記事の末尾に記載しておきますので参照してください。

KUE-CHIP2の全体像

まず、下の図を見てください。

※出典: KUE-CHIP2教育用ボード リファレンスマニュアル(京都高度技術研究所, 1993/6/25 v1.11)

図中の点線で囲まれた部分が、CPUのダイに実装された部分です。(図中のExternal Memory, IBUF, OBUF等は基盤(KUE-CHIP2教育用ボード)上に直接実装されています。)また、点線で示された経路(矢印)は、KUE-CHIP2教育用ボードからCPUの内部のレジスタ値などを確認するためのものなので説明では省略します。また、この図はあくまでアーキテクチャを説明するための概念図であり、実際の実装の様子とはかなり異なっています(例えば、ACC, IX, ALUは同じモジュール内に実装されています)。

命令の実行過程

KUE-CHIP2が起動すると、ACC, IX, IR, PCという各種レジスタが0に初期化され、プログラムの実行が開始されます。一つの命令はP0-P4で示される3個から5個のフェーズで構成されており、そのうちP0とP1については共通で以下のような処理になります。

P0 P1
(PC)->MAR, PC++ (DBi)->IR

P0では、PC(Program Counter)レジスタの値がMARに呼び出され、PCレジスタの値がインクリメントされます。(Sel. と書かれたセレクタが切り替わります。) その後PCがインクリメントされます。Inter.Mem. はMARが指す8ビット(b0-b7)のアドレス(正確には、それに加えてコントローラが指示するb8)の値を出力しているので、MARが書き換わることによって、DBiの値が書き換わります。

P1で、DBiの値がIR (Interruption Register)に読み込まれます。IRに入力された値はコントローラの命令デコーダ(IDC)によって解釈され、それを元に各モジュールに対して指示が与えられます。

よって、P2以降は各命令によって動作が異なっています。

KUE-CHIP2の命令解説

以下では、KUE-CHIP2で使用できる命令のビット列、アセンブリ言語上での記述、動作について述べます。

HLT / NOP

命令 アセンブリ言語 ビット列 動作(P2)
HLT HLT 00001xxx 0101xxxx 1->halt
NOP NOP 00000xxx 何もしない

xはドントケア(感知しない)

HLTはプログラムの最後に記述する命令で、SS (Start/Stop)モードで実行中のプログラムを停止します(haltという信号線を1にします)。この命令を最後に記述して置かなければ、プログラムの実行が止まらずCPUが暴走してしまうので、かならず入れるようにしましょう。(KEMU-IDEでは, プログラムが暴走する危険がある場合はエラーとなります。)
HLTのビット列で、2つめのもの(0101xxxx)は、後述するST命令の無効命令(第二オペランドにIXまたはACCを指定した状態)に相当します。
クロックジェネレータがhalt信号を受けると、CPUの動作を停止するとともに出力OPを0にします。

NOPは何もしない命令です。時間稼ぎをしたい場合、また手動で命令をKUE-CHIP2のメモリに書き込む際、後で命令を入力するための場所を空けておきたい場合等に使用します。

OUT / IN

命令 アセンブリ言語 ビット列 P2 P3
OUT OUT 00010xxx ACC->DBo 1->OBUF_WE
IN IN 00011xxx DBi->ACC 1->IBUF_FLG_CLR

OUT, IN命令は外部KUE-CHIP2と通信するために用いられます。
通信を行うには、送信側KUE-CHIP2のJP1ポートと受信側KUE-CHIP2のJP2ポートをケーブルで接続する必要が有ります。こうすることで、送信側のOBUF, OBUF_FLAGと受信側のIBUF, IBUF_FLAGを物理的に接続します。

OUT命令では、ACCレジスタの値を送信します。まずACCの値がDBoに出力され、次にOBUF_WEに1を送出します。基板(KUE-CHIP2教育用ボード)がOBUF_WEを受信すると、DBoの内容をOBUFに書き込むと同時に、OBUF_FLAGをセットします。P2とP3に処理を分割しているのは、P2で外部バッファにデータを送信した直後はバッファの状態が不安定であり、これが安定するまでOBUFへの書き込みを待つためです。

IN命令では、ACCレジスタに受信した値を格納します。まずP2でIBUF_REをす出し、DBiの値がACCに書き込まれ、その後P3でIBUF_FLG_CLRが送出されます。基板はIBUF_REを受信すると、IBUFの値(送信側のOBUFと物理的に接続されている)とDBiが接続されるように3-state bufferを制御します。その後IBUF_FLG_CLRを受信すると、IBUF_FLAGに0をセット(すなわちリセット)します。IBUF_FLAGは送信側のOBUF_FLAGと物理的に接続されているので、その結果OBUF_FLAGもリセットされることになります。

なお、複数のデータをOUTで出力する場合、次のデータを送信する前に受信側がデータを取り込むのを待つ必要が有ります。またINでデータを読み込む場合も同様にデータの送信を待つ必要が有ります。このため、OUT/IN命令は後述するBNI/BNO命令と組み合わせて使用されます。具体的には、送信側のユーザーメモリの100h-115h番地の値を受信側の同じ番地に書き込むプログラムは以下のようになります。

* 送信側
LD IX, 00h
LOOP:
LD ACC, (IX+00h)
OUT
OUT_LOOP:
BNO OUT_LOOP
ADD IX, 01h
CMP IX, 16h
BN LOOP
HLT END

* 受信側
LD IX, 00h
LOOP:
BNI LOOP
ST ACC, (IX+00h)
ADD IX, 01h
CMP IX, 16h
BN LOOP
HLT
END

RCF / SCF

命令 アセンブリ言語 ビット列 動作(P2)
RCF RCF 00100xxx 0->CF
SCF SCF 00101xxx 1->CF

CF(Carry Flag)の値をセットします。
この命令は、複数桁の計算時にADC命令とともに使用されます。具体的には、ユーザーメモリの100h-103hと104h-107h番地にリトルエンディアンでかk脳された値を足し合わせて108h-112h番地に書き込むプログラムは以下のようになります。

LD IX, 00h
RCF
LOOP:
LD ACC, (IX+00h)
ADC ACC, (IX+04h)
ST ACC, (IX+08h)
ADD IX, 01h
CMP IX, 04h
BN LOOP
BNC NOT_CARRY
LD ACC, 01h
ST ACC, (112h)
HLT
NOT_CARRY:
LD ACC, 00h
ST ACC, (112h)
HLT
END

Bcc : ブランチ命令

命令 ビット列(1バイト目) ビット列(2バイト目) P2 P3
Bcc 0011 cc addr (PC)->MAR, PC++ STATUS CHECK, (Mem)->PC
アセンブリ言語 cc 意味 条件
BA 0000 Always true
BVF 1000 on oVerFlow VF==1
BNZ 0001 on Not Zero ZF==0
BZ 1001 on Zero ZF==1
BZP 0010 on Zero or Positive NF==0
BN 1010 on Negative NF==1
BP 0011 on Positive (NF or ZF) == 0
BZN 1011 on Zero or Negative (NF or ZF) == 1
BNI 0100 on No Input IBUF_FLAG == 0
BNO 1100 on No Output OBUF_FLAG == 1
BNC 0101 on Not Carry CF == 0
BC 1101 on Carry CF == 1
BGE 0110 on Greater than or Equal (VF xor NF) == 0
BLT 1110 on Less Than (VF xor NF) == 1
BGT 0111 on Greater Than ((VF xor NF) or ZF) == 0
BLE 1111 On Less than or Equal ((VF xor NF) or ZF) == 1

Branch命令は, FLAGの値等に応じて実行を分岐(ジャンプ)する命令です。P2はP0と同様で、P3で条件を評価してその結果によってメモリの値(DBiに出力されている)をPCに格納します。

FLAGは8ビットの値で、上位4ビットは常に0, 下位4ビットについては重みの低い側から順にZF(Zero Flag), NF(Negative Flag), VF(oVerflow Flag), CF(Carry Flag)に割り当てられています。これらのフラグは以下のような命令の実行時にセットされ、それ以外では以前の値をそれぞれ独立に保持しています。(CPUがリセットされると0に初期化されます。)

フラグ 変更する命令
CF SCF, RCF, Ssm, SBC, ADC
VF Ssm, 演算命令
NF Ssm, 演算命令
ZF Ssm, 演算命令

尚、VFは、SRA, SRL, SLL, RRA, RRL, RLL, EOR, OR, AND命令では常に0となります。(それ以外では、計算前後の符号ビットが変化した時に1となります。)

NFは、計算結果の符号ビットの値が1のときにセットされ、ZFは計算結果のすべてのビットが0のときにセットされます。

アセンブリ命令でBcc命令を使う場合、直接アドレスを指定することも出来ますが、通常はラベルとともに使用されます。また、後述するCMP命令と同時に使用されることが多いです。

CMP ACC, 00h
BA TARGET
TARGET:

Ssm / Rsm (シフト/ロテート命令)

命令 1バイト目 P2 P3
Ssm 0100a0sm Shift a, Update TCF Update flags
Rsm 0100a1sm Rotate a, Update TCF Update flags
a レジスタ
0 ACC
1 IX
sm ビット列 意味
RA 00 Right Arithmetically
LA 01 Left Arithmetically
RL 10 Right Logically
LL 11 Left Logically

シフト/ロテート命令では、aで示されたレジスタ(ACC or IX)をシフトまたはロテートします。


※出典: KUE-CHIP2教育用ボード リファレンスマニュアル(京都高度技術研究所, 1993/6/25 v1.11)
SLAとSLLの違いは、VFフラグのセットのされ方の違いです。

P2でシフトまたはロテートが行われ、その後キャリー(桁溢れ)が発生したかどうかがTCF(Temporary Carry Flag)と呼ばれる内部バッファに書き込まれます。P3ではTCFの値およびaの値によって各FLAGの値が格納されます。

LD / ST命令

LD命令は、レジスタ(ACC, IX), 即値、プログラムメモリ、データメモリの値をレジスタ(ACC, IX)に読み込みます。

ST命令は、レジスタ(ACC, IX)の値をプログラムメモリ、データメモリに書き込みます。

フェーズ数が命令のオペランドによって異なり、また動作も複雑なため、いくつか例示するだけで勘弁してください。

LD ACC, IX # IXの値をACCに書き込む
LD ACC, 01h # ACC に 1(16進数)を書き込む
LD ACC, [00h] # ACCにプログラムメモリの0番地の値を読み込む
LD ACC, (00h) # ACCにデータメモリの0番地の値を読み込む
LD ACC, (100h) # 上と同様
LD ACC, [IX+00h] # プログラムメモリのIX番地をACCに読み込む
LD ACC, (IX+02h) # データメモリのIX+02番地をACCに読み込む
LD IX, (ACC+00h) # このようなことはできません!
LD ACC, (IX) # このような書き方も出来ません! (IX+00h と書けば良い)
ST ACC, (IX+00h) # ACCの値をデータメモリのIX番地に書き込む
ST ACC, IX # 第2オペランドにレジスタは指定できません!
ST ACC, 01h # 第2オペランドに即値は指定できません!

演算命令

KUE-CHIP2では以下のような演算を使用できます。

命令 意味
ADD ADD
ADC ADd with Carry
SUB SUBtract
SBC SuBtract with Carry
CMP CoMPare
AND AND
OR OR
EOR Exclusive OR

掛け算、割り算命令はありません。LD命令と同様に、2つのオペランドを指定できる他、第二オペランドとしてメモリアドレス等も指定できます。演算結果はCMP以外では第一オペランドに格納されます。

アセンブラの機能

KUE-CHIP2の命令には含まれていないのですが、「KEMU-IDE」上で使用できる機能です。

コメント

*, #で始まる部分はコメントとして扱われ、プログラム上では無視されます。

END

プログラム上にENDを記述すると、コンパイルをそこで停止します。

ラベル

番地または定数に名前を付けます。例えば以下のように命令の直前にラベルを記述すると、その命令がコンパイルされた後の番地位置がラベルにセットされ、ブランチ命令のオペランドなどに使用できます。

LABEL: NOP
* ...
BA LABEL

* 以下のように複数行に書いても良い
LABEL:
NOP

また、以下のようにすることで定数をラベルとして扱えます。

DATA: EQU 02h
LD ACC, DATA # ACCに02hを代入

最後に

結局、今回制作した「KEMU-IDE」の説明はかけませんでしたので、後日別記事として公開したいと思います。

参考文献

KUE-CHIP2についてより深く知りたい、という方の便利のために、関連文献を挙げておきます。

雑誌など

2番目以降の文献は、実際にKUE-CHIP2の設計に携われた方によるものです。

  1. 船附誠弘 , 中谷嵩之 , 山崎勝弘 , 小柳滋 : ” 教育用マイクロプロセッサの設計と FPGA ボード上での検証 ”, 第 4 回情報科学技術フォーラム , C-001 (2005)
  2. 越智裕之 , 澤田宏 , 岡田和久 , 上嶋明 , 神原弘之 , 濱口清治 , 安浦寛人 : ” 計算機工学・集積回路工学教育用 マイクロプロセッサ KUE-CHIP2”, 計算機アーキテクチャ , 96-14 (1992)
  3. 安浦寛人他 : ” ぼくらのマイクロプロセッサ開発記 1”, Bit( 共立出版 ), 25-10 (1993)
  4. 安浦寛人他 : ” ぼくらのマイクロプロセッサ開発記 2”, Bit( 共立出版 ), 25-11 (1993)
  5. 安浦寛人他 : ” ぼくらのマイクロプロセッサ開発記 3”, Bit( 共立出版 ), 25-12 (1993)
  6. 安浦寛人他 : ” ぼくらのマイクロプロセッサ開発記 4”, Bit( 共立出版 ), 26-1 (1994)
  7. 安浦寛人他 : ” ぼくらのマイクロプロセッサ開発記 完 ”, Bit( 共立出版 ), 26-2 (1994)

公式マニュアル

  • 京都高度技術研究所 , KUE-CHIP2 設計ドキュメント Version 1.10, 1993年1月6日
  • 京都高度技術研究所 , KUE-CHIP2 教育用ボード リファレンスマニュアル Version 1.11, 1993年6月25日

その他、Web上にもある程度情報は載っているので検索してみてください。