LKit-16

LKit-16はパナファコムがL-16Aマイクロコンピュータシリーズの販売拡張用に1977年3月に発売したトレーニングキットです。正確にはLearning KITと呼ぶべきでしょうか。これを省略してLKitとなっているわけなんですが。
CPUにMN1610、インターバルタイマ・クロックジェネレータにMN1640、コンソールの制御用にMN1630を1個使用しています。オプションでMN1630を2個まで増設できて、各種I/Oを接続できます。

LKit-16

特徴は、L-16Aというコンピュータアーキテクチャ自身にもありましたが、このように多数のキースイッチを備えて、1行アセンブラでプログラムを入力できたことにあります。オーディオカセットテープインターフェースも標準装備です。
あ、この全体写真のLKit-16は3年以上にわたって私の遊び道具だったために、そうとう改造してありますんでよろしく。

LED表示とファンクションキーの拡大を示します。

LKit-16 LEDs and function keys

STOPキーは実行中のユーザプログラムに割り込んで中断するためのもの。
RUNは実行指令でFUNCはオーディオカセットテープとのロードやストアの他ユーザプログラムの起動用など。CANCLはメモリへの命令書き込みなんかをキャンセルするためのキーで、SET ADRSはDATA部のLEDに表示している値をADDRESS部へ転送するキー(DATAにはその番地の内容が表示される)。
ASMBLは以下に説明するアセンブラ入力による機械語入力。STOREはDATA部に表示されている値をADDRESS部に表示された番地のメモりに書き込むコマンドで、DISPは次のアドレスの内容を表示するコマンドになっています。

LKit-16 keyboard

LKit-16の最大の特長が、この多数のキーを使ったアセンブリ言語入力です。
キーにはいくつかの文字が印刷されたラベルが付けられています。たとえば左上隅のキーには(wってのは後でアルファベット入力するために書き込んであるので無視してください)左上にLというアセンブリ命令、右下にR0というレジスタ名が書かれています。一番上の列はすべて命令とレジスタ名が並んだキーになっています。次の2番目の段のキーには左上にMVやMVBのようにアセンブリ命令が同じように書き込まれていますが、下の方にはhhとか*とかhh(X0)などと書き込まれています。これはメモリアドレッシング指定を表していて、hhなら16進数2桁で直接表す0ページダイレクト、*ならプログラムカウンタ相対、hh(X0)ならインデックスレジスタX0を用いたインデックス修飾アドレッシングという意味になります。
そこで、ST   R2, 12(X0)という命令をメモリに書き込みたいとしたら、まず1行2列目のSTキーを押して、次に1行3列目のR2キーを押し、次にアドレッシングモード指定のhh(X0)を押し、1と2のキーを順に押してから最後にASMBLキーを叩きます。するとLEDのDATA部に対応する機械語が組み立てられているという仕組みになっています。ラベルの処理やアドレス計算は行ってくれません。しかし、アセンブリ言語でコーディングしたリストのアドレス計算だけ自分の手で行えば、機械語を16進数に変換する手間だけは省略できます。MC6800や8080のような可変長の機械語を持つCPUでは役に立たないと思いますが、L-16Aはすべて1語命令で、アセンブリ言語コーディングの1行が1語にそのまま対応しますから、アドレス計算というのは行数を(コメントなどを除いて)数え上げることを意味します。機械語の内容やサイズを考えずにアドレス計算できるから、この1行アセンブリ言語入力が役立つわけです。
もちろん、命令が33種類しかない(LKit-16のアセンブラだと別名を用いて36種類の命令表記)ために、この程度のキーで入力できることもありますけど。
なお、命令体系についてはMN1610の項に詳細を示してあります。

メモリはアドレス0000 - 00FFと1000 - 10FFが標準のRWM領域、0100 - 04FFがモニタプログラムのROMの領域です。RWMは1100 - 12FFまで拡張でき、ROMも2708相当品で0500 - 08FFまで拡張できます。RWMは2111相当品を使用します。(ICソケットと)メモリICだけを用意すれば、RWMを最大2 KByteまで拡張できるのは当時のシングルボードコンピュータとしては異例の大容量でした(1 Word 16 bitであることに注意)。

0ページメモリのうち、アドレス0000 - 003Fの範囲の部分はモニタプログラムが使用します。この部分のメモリマップをもとに、モニタ動作を見ていくことにしましょう。メモリマップはこのようになっています。
番地  内容
0000  OPSW0
0001
0002  OPSW1
0003
0004  OPSW2
0005
0006  OPSW3
0007
0008  UNPSW
0009
000A  ブレークアドレス
000B  ブレークカウンタ
000C  テープストア開始アドレス
000D  テープストア終了アドレス
000E  キー入力データ数
000F  タイマカウンタ
0010  レジスタセーブエリア R0
0011  同 R1
0012  同 R2
0013  同 R3
0014  同 R4
0015  同 SP
0016  同 STR
0017  同 IC
0018  キー入力フラッグ
0019  データ表示バッファ
001A  アドレス表示バッファ
001B  K1 : D4 (K1は上位バイトでD4が下位バイト。以下同じ)
001C  K2 : D3    K1 - K8はキー入力バッファ
001D  K3 : D2    D4 - A1はディスプレイバッファ
001E  K4 : D1
001F  K5 : A4
0020  K6 : A3
0021  K7 : A2
0022  K8 : A1
0023  JUMP1    テープロードルーチン開始アドレス
0024  JUMP2    テープストアルーチン開始アドレス
0025  JUMP3    以下、ユーザプログラム開始番地格納エリア
0026  JUMP4
0027  JUMP5
0028  JUMP6
0029  JUMP7
002A  JUMP8
002B  Level0 SPセーブエリア
002C  ユーザタイマ処理開始番地
002D  LMONI2アドレス定数
002E  Level0スタックエリア
002F  同上
0030  SEGWTアドレス定数
0031  CMR保存エリア
0032  ERRORアドレス定数
0033  KSCANアドレス定数
0034  DSCANアドレス定数
0035  未使用
0036  テープロード・ストア作業領域
0037  同上
0038  同上
0039  ユーザ用スタックエリア
003A  同上
003B  同上
003C  同上
003D  Level1用スタックエリア
003E  同上
003F  同上
アドレス0000からのOPSW領域はL-16Aの割り込み機構に根差した予約領域です。アドレス0008, 0009のUNSPW領域は、Level2割り込みをユーザが使用する場合に、その割り込みサービスルーチンのPSWを登録するための場所です。LKit-16では、本来のNPSW2領域はROMの中に割り当てられていますから、ユーザが自由に書き換えるわけにはいきません。モニタの一部として、Level2割り込みを受け付けると、このUNPSWからPSWをロードしてユーザの割り込みサービスルーチンに制御を渡すようなコードが含まれていて、ユーザにLevel2割り込みを開放しています。なお、Level0割り込みはモニタが占有していて、Level1割り込みのうち、インターバルタイマ割り込みだけはユーザが使用できるようになっています(後述)。
ブレークアドレスとブレークカウンタはユーザプログラムのデバッグに使用します。ここを操作せずにAUTO/STEPスイッチをSTEP側に切り替えてプログラムを実行すると、1命令ずつのステップ実行になります。この領域にアドレスと実行回数をセットしてからRUNさせれば、指定された回数だけ指定されたアドレスの命令を実行した直後に停止してモニタ操作が可能になります。
テープストア開始アドレスとテープストア終了アドレスは、オーディオカセットテープへのメモリ内容の保存に使用します。テープへのストアとロードはモニタプログラム本体とは結合の薄い独立性の高いプログラムになっていて、FUNCキーで呼び出せますが、その際のパラメータ指定に使います。ユーザプログラムで使用可能なRWMのアドレスを指定しなくてはなりません。このアドレスはテープにも格納されるため、ロード時には不要です。
キー入力データ数はキー入力バッファに格納されているキーコード数で、モニタプログラムの作業用データです。
アドレス000Fのタイマカウンタは、モニタプログラム内のインターバルタイマ割り込みルーチンで管理されています。モニタプログラム内のインターバル割り込みルーチンでは、アドレス002Cのユーザタイマ処理開始番地に0以外の値が書き込まれていれば、そのアドレスをサブルーチンとして呼び出します。ユーザタイマ処理ルーチンでは、RET命令で復帰しなくてはなりません。その後、モニタ内で割り込みの後処理を行います。ユーザタイマ処理開始番地が0になっている場合、モニタプログラムのインターバルタイマ割り込みルーチンではタイマカウンタをインクリメントしてサービスを終了します。ユーザプログラムはタイマカウンタの内容を調べることによって割り込みの有無を検査したり時間経過を知ることができます。どちらにしろ、インターバルタイマのスタートやストップは直接ユーザプログラムが行います。
レジスタセーブエリアはユーザプログラム実行時のレジスタの内容を保存してある領域で、RUNコマンド実行時にCPU内部のレジスタに転送されます。また、STOPキーやステップ動作などでユーザプログラムが中断された場合、ここにレジスタの内容が書き込まれます。モニタプログラム使用時には、このアドレスを指定すればレジスタの内容を調べることができますが、それだけでなくデータキーのレジスタ名が書かれているキーを押してからDISPコマンドキーを押せば、一発で指定したレジスタの内容を表示することができます。
アドレス0018のキー入力フラッグはキーボード読み取り時にチャタリング除去をするためのフラグやキー入力バッファのポインタ値が入っている作業領域で、モニタプログラムが使用します。
データ表示バッファとアドレス表示バッファは、データLEDとアドレスLEDに表示される数値が格納されます。モニタプログラムのSEGWTルーチンで、この内容をLEDのセグメントデータに変換してディスプレイバッファに格納します。
アドレス0023から002Aのユーザプログラム開始番地格納エリアはFUNCキーで呼び出されるルーチンのアドレスが格納されている領域です。データスイッチの最上段のキーを押してからFUNCキーを押すと、対応したユーザプログラムが起動されます。JUMP1にはテープロードルーチンの開始アドレスが、JUMP2にはテープストアルーチンの開始アドレスが、デフォルトで格納されていますから、L FUNCと押せばテープからのロードが、ST FUNCでテープへのストアができます。モニタプログラムを起動してから高速テープロード・ストアルーチンをロードして、このアドレスを書き換えてしまうと、それまでと同じ操作でも高速テープロード・ストアルーチンを使用することができたりして便利でした。また、それ以外の場所に16進数電卓のようなプログラムをロードしたアドレスを登録しておけば、プログラムの作成時に簡単に16進数計算を利用できるようになったりします。
アドレス002B以降はモニタや割り込みの作業領域とかスタックが多くなっていますが、特に重要なのはいくつかのアドレス定数です。L-16Aのアーキテクチャでは、近傍のサブルーチンを呼び出す以外はどこかにサブルーチンのエントリアドレスをアドレス定数として格納して、そこへの間接参照でサブルーチンを呼び出すのが一般的です。ゼロページ領域にアドレス定数を確保しておけば、全メモリ空間のどこからでもそのサブルーチンを間接参照できますから、オペレーティングシステムのシステムコールに使うのに適しているわけです。その他にも、長いプログラムの各所から繰り返し呼び出されるルーチンのアドレス定数をゼロページのどこかに格納しておけば、プログラムを短く高速にできます。
さて、問題のアドレス定数はLMONI2, SEGWT, ERROR, KSCAN, DSCANの五つがあります。このうち、モニタプログラムを短くするために使われているのがERRORアドレス定数で、エラー表示を行うためのサブルーチンアドレスが入っています。エラー表示はモニタプログラム内の各所から呼び出されますから、効果的なのです。残りの4定数は、ユーザプログラムが使用するためのアドレス定数です。
LMONI2アドレス定数はユーザプログラムからモニタプログラムへ復帰する際のエントリアドレスです。従って、ユーザプログラムの終了時にはB (X'002D')でモニタに復帰できます。
SEGWTアドレス定数はデータ表示バッファとアドレス表示バッファの内容を対応するセグメントデータに変換してディスプレイバッファに書き込みます。ディスプレイバッファのD4がデータの最上位デジットに、A1がアドレスの最下位デジットに対応します。また、セグメントデータのビット8がセグメントaに、ビット14がセグメントgに対応し、LSBのビット15が小数点に対応します。対応するビットが1だと、そのセグメントを点灯させるという意味になります。BAL (X'0030')だけではセグメントデータへの変換だけで、表示は一切行われないので注意してください。すべてのレジスタの内容が破壊されます。
実際に表示を行うためのサブルーチンアドレスがDSCANアドレス定数として定義されています。このサブルーチンを呼び出すと、1回だけLEDをダイナミックスキャンします。表示データはディスプレイバッファのものが使用されるため、このサブルーチンを呼び出す前にディスプレイバッファ内の任意のビットをセットすることで、好きなパターンを表示させることができます。このサブルーチン呼び出しによって、各桁のLEDが約0.9 msずつ光り、サブルーチン全体では約8 ms消費します。あくまで1回だけのダイナミックスキャンですから、きちんと表示させるためには繰り返しこのルーチンを呼び出します。R0以外のレジスタの内容は破壊されます。
KSCANアドレス定数はキーボードをスキャンするサブルーチンアドレスです。R0のビット13 - 15とR1のビット12 - 14のそれぞれ3 bitにキーのスキャンコードを入れて返ってきます。R1が0の場合にはキー入力はありません。これだけわかれば、具体的なスキャンコードは実験で調べられるでしょう。キー入力がない場合は約0.5 msの時間を消費します。X0とX1の内容は保存されます。
以上で、公開されているモニタプログラム内のサブルーチンエントリはすべてです。
これで、最低限のキー入力とLED表示のプログラムを書くことができるでしょう。あとはインターバルタイマの起動と停止の手順があれば、当時のLKit-16本体だけで実行できるゲームプログラミングなんかが可能かな。
インターバルタイマの操作はアセンブリ言語のソースコードで示すことにします。LKit-16ではタイマ割り込みをモニタが部分的にサポートしているため、裸のL-16Aのプログラミングとは少々異なります。

TINTVEC    EQU     X'002C'
*
TCMD1MS    EQU     X'81'          ; インターバルタイマへのコマンド
TCMD10MS   EQU     X'83'
TCMDSTOP   EQU     X'00'
*
ICMR       EQU     X'0C'
TOBR       EQU     X'0F'
*
TINTADDR   DC      A(TINTROUTINE)  ; 割り込みルーチンのアドレス定数
*
TIMERSTART EQU     *
           L       R0, TINTADDR    ; ベクタの設定
           ST      R0, TINTVEC
           MVI     R0, TCMD1MS     ; インターバルタイマの操作
           WT      R0, ICMR
           MVI     R0, TCONST
           WT      R0, TOBR
           RET
*
TIMERSTOP  EQU     *
           MVI     R0, TCMDSTOP
           WT      R0, ICMR
           RET

TIMERSTARTサブルーチンを呼び出せば、インターバルタイマ割り込みが周期的に発生して、TINTROUTINEが呼び出されます。このプログラムだと、TCONST×1 msの周期で割り込みがかかります。TCMD1MSの代わりにTCMD10MSを使用すると、TCONST×10 msの周期になります。TCONSTは8 bitの定数ですが、0は256と解釈されます。従って、1 msから2.56 sまでの周期の割り込みを発生させることができます。
なお、インターバルタイマ割り込みを使用すると、LKit-16のモニタでステップ実行などができなくなりますから、個別に各モジュールのデバッグを充分に行ってから、インターバルタイマを使用するようにした方がよいでしょう。
割り込みルーチンでは、使用するレジスタ類を退避する以外は、ほぼ普通のサブルーチンと同じようにプログラミングできます。メインプログラムのスタック領域が小さい場合は割り込みルーチン内で専用のスタック領域を使用した方が安全かもしれませんが、最初からメインプログラムのスタック領域を広げた方が効率的だと思います。RET命令でモニタプログラムに復帰します。

以上で、個別のハードウェアをプログラミングする以外の、ソフトウェア側からのインターフェースの説明は充分だと思います。あとはサブチャネルバスを用いたI/Oのプログラミングなんですが、それは個々のハードウェアに依存するので、ここでは扱わないことにしましょう。

Return to IC Collection