データゼネラルNova
すべてのオプションを利用した算術命令の例は次のとおり:
ADDZR# 0,2,SNC
これは次のようにデコードされている。キャリービットをクリアし、 AC2(アキュムレータ2)の内容をAC0 に追加し、結果を1ビット右に循環シフトし、結果をテストしてキャリービットが設定されているかどうかを確認し、設定されている場合は次の命令をスキップする。 テストを実行した後、結果を破棄する。 実際には、これは2つの数値を加算し、結果が奇数か偶数かをテストする。
Nova命令セットには、メモリの内容をアキュムレータに転送する命令とその逆の命令のペア、2つの制御転送命令、およびメモリ・ロケーションの内容をテストする2つの命令が含まれている。 すべてのメモリ参照命令には、8ビットのアドレス・フィールドと、メモリ・アドレッシングのモードを指定する2ビットのフィールドが含まれていた。 4つのモードは次のとおりである:
Mode 0 - 絶対アドレッシング。 命令のアドレス・フィールドの内容は左側がゼロで埋められ、ターゲット・アドレスとして使用されます。
Mode 1 - 相対アドレッシング。 命令のアドレス・フィールドの内容は、左に符号拡張され、プログラムカウンタの現在値(命令が実行されるまでに次の命令を指します)に加算する。 その結果がターゲット・アドレスとして使用される。
Mode 2 - インデックス・アドレッシング。 命令のアドレス・フィールドの内容が、左に符号拡張され、アキュムレータ2の現在値に加算される。その結果がターゲット・アドレスとして使用される。
Mode 3 - インデックス・アドレッシング。 命令のアドレス・フィールドの内容が、左に符号拡張され、アキュムレータ3の現在値に加算される。 その結果がターゲット・アドレスとして使用される。
明らかに、Mode 0は、8ビットのアドレス・フィールドがあるため、最初の256メモリ語だけをアドレス指定できた。 メモリのこの部分は「ページ・ゼロ」と呼ばれていた。 ページ・ゼロのメモリ・ワードは、利用可能な数が少ないため、Novaのアセンブリ言語プログラマにとって貴重なものと考えられていた。ページ・ゼロの場所だけが、インデックスレジスタとして使用するためにアキュムレータ2や3を縛る必要のあるインデックスアドレスに頼ることなく、プログラムのどこからでもアドレスを指定することができた。アセンブリ言語では、".ZREL" ディレクティブにより、アセンブラはそれに続く命令とデータ語をページ・ゼロに配置し、".NREL" ディレクティブにより、それに続く命令とデータ語を「通常の」メモリに配置した。後のNovaモデルでは、アドレス・フィールドを拡張した命令が追加され、この問題を克服した(性能的にはペナルティがある)。
アセンブラは、Mode 1の相対オフセットを自動的に計算するが、ソースに明示的に書き込むことも可能であった。 メモリ参照命令が .NREL 空間のメモリアドレスを参照しているがモード指定子がない場合、Mode 1が想定され、アセンブラは現在の命令と参照されている位置との間のオフセットを計算し、これを命令のアドレス・フィールドに配置した(結果として得られる値が8ビットフィールドに収まる場合)。
2つのロードおよびストア命令は次のとおりである:
LDA - メモリ位置の内容を指定されたアキュムレータにロードする
STA - 指定されたアキュムレータの内容をメモリ位置に格納する
これらの命令には両方とも「間接(indirect)」ビットが含まれていた。 このビットがセットされている場合(アセンブリ言語ではオペコードに '@'を追加することで行われる)、ターゲット・アドレスの内容はメモリ・アドレスそのものであると仮定され、ロードまたはストアを行うためにそのアドレスが参照される。
2つの制御転送命令は次のとおりである:
JMP - 指定されたメモリ位置に制御を移す
JSR("jump subroutine")- JMP命令と同じであるが、ジャンプする前にリターン・アドレス(JSR命令に続く命令)を追加でアキュムレータ3にロードする
ロードおよびストア命令と同様に、ジャンプ命令には間接ビットが含まれており、同様にアセンブリでは '@' 文字で指定されている。 間接ジャンプの場合、プロセッサはジャンプ先の内容を取得し、その値をジャンプ先のメモリアドレスとして使用する。 ただし、ロードおよびストア命令とは異なり、間接アドレスに最上位ビットがセットされている場合は、さらに1サイクルの間接ジャンプが実行される。 Nova 3より前のNovaシリーズプロセッサでは、間接アドレスの回数に制限はなかった。自分自身を参照する間接アドレスは、命令が完了することなく、無限に間接アドレッシングループが発生する。 (この状態では、フロントパネルのSTOPスイッチを押しても何もしないので、ユーザにとっては不安になるかもしれなかった。ループを解除するには、機械をリセットする必要があった。)
2つのメモリテスト命令は次のとおりである:
ISZ - メモリ位置をインクリメントし、結果がゼロの場合は次の命令をスキップする
DSZ - メモリ位置をデクリメントし、結果がゼロの場合は次の命令をスキップする
ロードおよびストア命令の場合と同様に、1レベルの間接アドレス指定を実行する間接ビットがあった。 これらの命令は、磁気コアメモリを搭載したNovaでは、メモリ基板自体の内で命令が実行されるという点で奇妙なものだった。 当時のメモリ基板には、磁気コアメモリに内在する破壊的な読み出し問題を解決するための「ライトバック」回路が搭載されていた。 しかし、ライトバック機構には、ミニ演算ユニットも含まれており、これはプロセッサがいくつかの目的で使用していた。 ISZ命令とDSZ命令では、読み取られたメモリ位置からライトバックが行われるまでの間にインクリメントまたはデクリメントが行われ、CPUは単に結果がゼロか非ゼロかの通知を待つだけであった。 これらの命令は、アキュムレータを拘束することなくメモリ位置をループカウンタとして使用できるため便利であったが、同等の算術命令よりも処理速度が遅くなった。
メモリ参照命令の例:
LDA 1,COUNT
COUNTというラベルのメモリ・ロケーションの内容をアキュムレータ1に転送する。 COUNTが .NREL 空間にあると仮定すると、この命令は次のようになる: LDA 1,1,(COUNT-(.+1))
ここで、'.' はLDA命令の位置を表す。
JSR@ 0,17
ページ・ゼロ空間のロケーション17の内容で指定されたメモリアドレスに間接的にジャンプし、リターンアドレスをアキュムレータ3に格納する。 これは、初期のNovaモデルでRDOSシステムコールを行うための標準的な方法で、アセンブリ言語のニーモニック ".SYSTM" はこれに変換される。
JMP 0,3
アキュムレータ3にアドレスが格納されているメモリ位置にジャンプする。 JSR命令はリターンアドレスをアキュムレーター3に残しているので、これは関数やサブルーチン呼び出しから戻るための一般的な方法であった。
STA 0,3,-1
アキュムレータ0の内容を、アキュムレータ3に含まれるアドレスより1つ少ない位置に格納する。
DSZ COUNT
COUNTというラベルの場所の値をデクリメントし、結果がゼロの場合は次の命令をスキップする。上記の場合と同様に、COUNTが .NREL 空間にあると仮定すると以下のようになる: DSZ 1,(COUNT-(.+1))
Novaは、I/Oデバイスとのインターフェースのためのチャネル化モデルを実装した。 このモデルでは、各I/Oデバイスは、"Busy" および "Done" と呼ばれる2つのフラグと、A, B, Cと呼ばれる3つのデータと制御レジスタを実装することが期待されていた。 I/O命令は、レジスタを読み書きし、"start", "clear", "pulse" と呼ばれる3つの信号のうちの1つをデバイスに送信するために利用可能である。一般的に start 信号を送ると、A/B/Cレジスタに値をロードすることで設定されたI/O動作が開始される。clear 信号はI/O動作を停止し、発生した割り込みをクリアする。pulse 信号は、ディスクドライブのシーク操作など、複雑なサブシステムの付帯操作を開始するために使用された。ポーリングされたデバイスは通常、デバイスとAレジスタの間で直接データを移動した。 DMAデバイスは一般的に、メモリアドレスを指定するためにAレジスタを使用し、転送するワード数を指定するためにBレジスタを使用し、制御フラグをセットするためにCレジスタを使用していた。 チャネル63はCPU自体を参照し、さまざまな特殊機能に使用された。
各I/O命令には、6ビットのチャネル番号フィールド、どのレジスタを読み書きするかを指定する4ビット、どの信号を送信するかを指定する2ビットのフィールドが含まれた。 アセンブリ言語では、信号はオペコードに文字を追加することによって指定された。'S' は start、'C' は clear、'P' は pluse 、信号がない場合は何も指定しない。 オペコードは次のとおりである:
DIA - デバイスのAレジスタの内容を指定されたアキュムレータに移動
DOA - 指定されたチャネルのデバイスのAレジスタに指定されたアキュムレータの内容を送信
DIB - デバイスのBレジスタの内容を指定されたアキュムレータに移動
DOB - 指定されたチャネルのデバイスのBレジスタに指定されたアキュムレータの内容を送信
DIC - デバイスのCレジスタの内容を指定されたアキュムレータに移動
DOC - 指定されたチャネルのデバイスのCレジスタに指定されたアキュムレータの内容を送信
NIO - "no I/O" の誤記。 この命令は、レジスタ転送を行わずにデバイスに信号を送信するために使用された。
さらに、デバイスの状態をテストするための4つの命令が用意されていた:
SKPBN - デバイスのBusyフラグが設定されている場合、次の命令をスキップ
SKPBZ - デバイスのBusyフラグがクリアされている場合、次の命令をスキップ
SKPDN - デバイスのDoneフラグが設定されている場合、次の命令をスキップ
SKPDZ - デバイスのDoneフラグがクリアされている場合、次の命令をスキップ
デバイスを起動すると、Busyフラグが設定される。 要求された操作が完了すると、従来はデバイスは Busyフラグをクリアして Doneフラグを設定していたが、ほとんどのデバイスでは、割り込み要求メカニズムが Doneフラグに接続されていたため、Doneフラグを設定すると割り込みが発生する(割り込みが有効でデバイスがマスクされていない場合)。
これらの命令は、さまざまなCPU制御およびステータス機能を実行した。 これらの命令はすべて、実際にはCPUの自己参照I/Oチャネルであるチャネル63のI/O命令の略記ニーモニックであった。
INTA - 割り込みアクノリッジ(確認)。 割り込みデバイスのチャネル番号を指定されたアキュムレータに転送
INTDS - すべての割り込みを無効化
INTEN - すべての割り込みを有効化
IORST - I/Oリセット。 I/Oバス上でリセット信号を送信し、すべてのI/Oを停止し、割り込みを無効化し、保留中のすべての割り込みをクリア
MSKO - マスクアウト。 指定されたアキュムレータの内容を使用して割り込みマスクを設定。 マスクがどのように解釈されるかは、各I/Oデバイスの実装に依存する。 一部のデバイスはマスクできなかった。
READS - フロントパネルの16個のデータスイッチの内容を指定されたアキュムレータに転送
HALT - CPUを停止。 一度停止したCPUはフロントパネルで手動で介入することによってのみ再起動させることができた。
ハードウェアの観点から見ると、割り込み機構は比較的単純であったが、現在のCPUアーキテクチャに比べると柔軟性に欠けるものであった。 バックプレーンは、1本の割り込み要求ラインをサポートしており、割り込み要求が可能なデバイスはすべてこのラインに接続されていた。 デバイスが割り込みを要求する必要がある場合は、デバイスはこのラインを立ち上げた。 CPUは、現在の命令を完了するとすぐに割り込みを実行する。 上記のように、デバイスが割り込みを要求した場合は"done"のI/Oフラグを立てることになっており、CPUがそのデバイスのチャネルでI/Oクリア命令を実行すると、デバイスは割り込み要求をクリアすることになっていた。
CPUは、オペレーティングシステムが割り込みサービスルーチンのアドレスをメモリアドレス1に格納することを期待していた。 デバイスが割り込みを行うと、CPUはアドレス1を介して間接的にジャンプし、戻りアドレスをメモリアドレス0に配置して、それ以上の割り込みを無効にした。 次に、割り込みハンドラはINTA命令を実行して、割り込みデバイスのチャネル番号を検出する。 これはバックプレーン上の「アクノリッジ」信号を立ち上げることで動作する。 アクノリッジ信号は、バックプレーン上でデイジーチェーン形式で配線されており、バス上の各基板をループしている。 割り込みを要求するデバイスは、アクノリッジ信号がバスへ伝播するのをブロックして、2つ以上のデバイスが同時に保留中の割り込みを持っていた場合、最初のデバイスだけがアクノリッジ信号を受け取ることができるようにする必要がある。 そのデバイスは、そのチャネル番号をバス上のデータラインに設定することで応答する。 つまり、同時割り込み要求があった場合は、カードケージ内のCPUに物理的に最も近いデバイスが優先されることになる。
割り込みが処理され、サービスルーチンがデバイスにI/Oクリアを送信した後、割り込みを有効にし、メモリアドレス0を介して間接ジャンプして戻ることで、通常の処理を再開した。 リターンジャンプの直前に保留中の割り込みが発生しないように(リターンアドレスが上書きされてしまう)、INTEN命令には1命令サイクルの遅延があった。 これを実行すると、JMP@0命令であるはずの次の命令が実行されるまで割り込みが有効にならなかった。
次に、オペレーティングシステムの割り込みサービスルーチンは、通常、受信したチャネル番号を使用してインデックス・ジャンプを実行し、そのデバイスの特定の割り込み処理ルーチンにジャンプする。 特にCPUの電源障害検出回路のように、INTA命令に反応しないデバイスもあった。 INTAがゼロの結果を返した場合、割り込みサービスルーチンは、どのデバイスが割り込みを行ったかを確認するためにSKPDZ / SKPDN命令を使用してINTAに応答しないデバイスをすべてポーリングしなければならなかった。
オペレーティングシステムは、MSKO命令を使用して割り込みマスクを設定することで、割り込みの順序をある程度管理することができる。 これは、ある特定の時間にどのデバイスが割り込みを許可されているかをオペレーティングシステムが判断できるようにするためのものである。 この命令が発行されると、バックプレーン上のすべてのデバイスに16ビットの割り込みマスクが送信された。 マスクが実際に何を意味するかを決定するのはデバイス次第であった。慣例により、マスクアウトされたデバイスは割り込みラインを立ち上げてはいけないとされていたが、CPUはこれを強制する手段を持っていなかった。 マスク可能なデバイスの多くは、基板上のジャンパーでマスクビットを選択できるようになっていた。 マスクを完全に無視するデバイスもあった。
磁気コアメモリ(電源がなくても内容を保持する)を備えたシステムでは、電源障害からの回復が可能であった。 コンピュータの主電源の喪失を検出すると、CPU内の停電検出回路が割り込みを発行し、その時点から電源装置のコンデンサが充電されなくなってCPUへの電力供給が停止するまでにCPUには少し時間があった。 これは、IORST命令を発行して進行中のI/Oを停止し、4つのアキュムレータの内容とキャリービットをメモリに保存するのに十分な時間であった。 電源が回復したときに、CPUのフロントパネルのキースイッチがLOCK位置にあれば、CPUは起動してメモリアドレス2を経由して間接ジャンプを行う。 これは、アキュムレータとキャリービットを再ロードし、その後、通常の処理を再開するオペレーティングシステムのサービスルーチンのアドレスであると予想されていた。 電源障害によって中断されたI/O操作をどのようにして再開するかは、サービスルーチンにかかっていた。
当時の慣習として、ほとんどのNovaモデルには、CPU機能を制御したり監視するためのフロントパネルコンソールが装備されていた。 Nova 3より前のモデルでは、上記のNova 840パネル写真のような標準的なフロントパネルレイアウトが採用されていた。 このレイアウトには、キー付き電源スイッチ、2列のアドレスランプとデータ表示ランプ、1列のデータ入力スイッチ、および押すとさまざまなCPU機能を作動させるファンクションスイッチが含まれていた。 アドレスランプは常にプログラムカウンタの現在値をバイナリで表示していた。 データ表示ランプは、その時点でどのCPU機能が作動しているかによってさまざまな値を表示していた。 一番左のデータランプの左側には、キャリービットの現在値を表示するランプが追加されていた。 ほとんどのモデルでは、ランプは白熱電球で、パネルボードにはんだ付けされていた。切れたランプを交換することは、データゼネラルのフィールドサービスエンジニアにとって悩みの種であった。
各データスイッチは16ビット値の1ビットの値を制御し、データゼネラルの慣習に従って、左から右へ0〜15の番号が付けられていた。 データスイッチは、さまざまな機能のためのCPUに入力を提供し、READSアセンブリ言語命令を使用して実行中のプログラムから読み出すこともできる。 パネルの乱雑さを減らしてコスト削減のために、ファンクションスイッチは双方向のモーメンタリスイッチとして実装された。 ファンクションスイッチのレバーを上げると、パネル上のスイッチの上に記載されている名前の機能が作動し、レバーを押し下げると、スイッチの下に表示されている名前の機能が作動する。 スイッチレバーを離すと中立位置に戻った。
Nova 840の写真を参照すると、左から最初の4つのスイッチが4つのアキュムレータのEXAMINEとDEPOSIT機能を実行する。 これらのうちの1つでEXAMINEを押すと、アキュムレータの現在値がデータランプに二進数で表示された。 DEPOSITを押すと、データスイッチに現在設定された二進数値がアキュムレータに転送された。
右に進むと、次のスイッチはRESET/STOPスイッチであった。 STOPを押すと、CPUは現在の命令の完了後に停止する。 RESETを押すと、CPUは直ちに停止し、CPU内部レジスタをいくつかクリアし、接続されているすべてのデバイスにI/Oリセット信号を送信する。 その右側のスイッチがSTART/CONTINUEスイッチである。 CONTINUEを押すと、CPUは現在プログラムカウンタが指し示している命令の実行を再開する。 STARTを押すと、現在データスイッチ1〜15に設定されている値がプログラムカウンタに転送され、そこから実行が開始された。
次の2つのスイッチは、フロントパネルからのメモリの読み出しと書き込みを行う。 EXAMINEを押すと、データスイッチ1〜15に設定されている値がプログラムカウンタに転送され、対応するメモリに値が取り込まれ、その値がデータランプに表示される。 EXAMINE NEXTを押すと、プログラムカウンタの値がインクリメントされ、そのメモリ位置の検査操作が行われ、ユーザーは一連のメモリ位置をステップ実行できる。 DEPOSITを押すと、データスイッチに含まれている値がプログラムカウンタが指すメモリ位置に書き込まれる。 DEPOSIT NEXTを押すと、最初にプログラムカウンタがインクリメントされ、次にポイントされたメモリ位置に書き込まれた。
INST STEP機能により、CPUは現在のプログラムカウンタの位置で1つの命令を実行した後、停止した。 プログラムカウンタは命令実行の一部としてインクリメントされるため、これにより、ユーザはプログラムをシングルステップで実行することができた。 MEMORY STEPは誤記で、CPUは1クロックサイクルを実行して停止した。 これはユーザにとってはほとんど意味がなく、通常はフィールドサービス担当者が診断のためにのみ使用した。
PROGRAM LOADは、通常Novaの起動に使用されるメカニズムであった。 このスイッチがトリガーされると、32語のブートROMがメモリの最初の32語にマッピングされ、プログラムカウンタを0に設定して、CPUを起動した。 ブートROMには、選択されたI/Oデバイスから256語(512バイト)のコードをメモリに読み出し、次に読み出したコードに制御を移すコードが含まれていた。 データスイッチ8〜15は、ブートROMにどのI/Oチャネルからブートするかを指示するために使用された。 スイッチ0がオフの場合、ブートROMはデバイスをポーリングされたデバイス(たとえば、紙テープリーダー)であるとみなし、512バイトが読み込まれるまでポーリングされた入力ループを実行した。 スイッチ0がオンの場合、ブートROMはデバイスがDMA対応デバイスであると仮定し、DMAデータ転送を開始した。 ブートROMは、転送を開始する前にデバイスを配置するのに十分な賢さを持っていなかった。 これは、クラッシュ後の再起動時に問題となった。ブートデバイスがディスクドライブの場合、そのヘッドがランダムなシリンダーに残されていた可能性があった。 ブートシーケンスを動作させるには、RDOSが第1レベルのブートブロックを書き込んだシリンダ0に再配置する必要があった。 従来は、ドライブのロードシーケンスを循環させることで行われていたが、待ち時間(ドライブのモデルによっては最大5分)に不満を感じていたユーザは、フロントパネルからドライブの「再調整」I/Oコードを入力し、それをCPUにシングルステップで実行させる方法を学んだ。これは、経験豊富なユーザが数秒で完了した操作である。
電源スイッチは、OFF、ON、LOCKと書かれた3方向キースイッチであった。 OFFポジションでは、CPUからすべての電源が遮断されていた。 キーをONにすると、CPUに電源が供給された。 ただし、現在のCPUとは異なり、電源投入時にCPUが自動的に起動するわけではなく、ユーザは、PROGRAM LOADまたはその他の方法を使用してCPUを起動し、ブートシーケンスを開始する必要があった。 スイッチをLOCKにすると、フロントパネルのファンクションスイッチが無効になるため、スイッチをLOCKにしてキーを外すことで、CPUを改ざんされにくい状態にすることができた。また、磁気コアメモリを搭載しているシステムでは、LOCK状態にすることで自動停電復旧電機能が有効になった。 キーは OFFまたはLOCKポジションで取り外すことができた。
Nova 1200は、コアメモリアクセス命令(LDAおよびSTA)を2.55マイクロ秒(μs)で実行した。 リード・オンリー・メモリーを使用することで0.4μsを節約した。 アキュムレータ命令(ADD、SUB、COM、NEGなど)は1.55μs、MUL 2.55μs、DIV 3.75μs、ISZ 3.15-4.5μsであった。 後期のEclipse MV/6000では、LDAとSTAは0.44μs、ADDなどは0.33μs、MUL 2.2μs、DIV 3.19μs、ISZ 1.32μs、FAD 5.17μs、FMMD 11.66μsであった。
これは、Novaアセンブリ言語による最小限のプログラミング例である。 RDOSで実行するように設計されており、コンソールに "Hello, world." という文字列を出力する。
Novaの基本モデルには、価格競争力を維持するためにハードウェアの乗算/除算機能が内蔵されていなかった。 次のルーチンは、2つの16ビット語を乗算して 16ビット語の結果を生成する(オーバーフローは無視される)。 これは、ALU 命令の shift および test(スキップ)を組み合わせて使用していることを示している。 このルーチンが <tt>jsr</tt> によって呼び出された場合、AC3は差出人住所を保持していることに注意すること。 これはリターン命令 <tt>jmp 0,3</tt> によって使用される。 アキュムレータをクリアする慣用的な方法は <tt>sub 0,0</tt> である。 他の単一命令は、有用な定数の特定のセット(たとえば、-2、-1、または+1)をロードするように配置することができる。
次のルーチンは、16桁の2進数としてAC1の値をRDOSコンソールに出力する。 これは、Nova命令セットのさらなる奇抜さを明らかにしている。 たとえば、任意の「即時」値("immediate value")をアキュムレータにロードする命令はない(ただし、メモリ参照命令はそのような値をエンコードして実効アドレスを形成する)。 アキュムレータは一般に、初期化されたメモリ位置(例: <tt>n16</tt> )からロードしなければならない。 PDP-11などの他の最新のマシンや、事実上のすべての最新アーキテクチャでは、即時ロードが可能であるが、 ARMなどの多くのマシンでは、即時ロードできる値の範囲が制限されている。
RDOSの <tt>.systm</tt> 呼び出しマクロは <tt>jsr を</tt>実装しているため、AC3は <tt>.pchar</tt> 関数の戻りアドレスで上書きされる。 したがって、この関数の呼び出し元の戻りアドレスを保持するための一時的な場所が必要になる。 再帰的なルーチンや再入可能なルーチンの場合は、スタック、利用可能な場合はハードウェア、利用できない場合はソフトウェアを代わりに使用しなければならない。 リターン命令は <tt>jmp @ retrn</tt> となり、Novaの間接アドレッシングモードを利用してリターンPCをロードする。
末尾の定数定義では、2つのアセンブラ機能を示している。アセンブラの基数はデフォルトで8進数 ( <tt>20</tt> = 10進数の16)で、文字定数は <tt>"0"</tt> などのようにエンコードすることができる 。
モントリオールのカナダ放送協会では、1980年代後半までチャンネル再生の自動化にNova 1200を使用していた。 その後、改装されたNova 4ユニットに置き換えられ、1990年代半ばまで使用されていた。
フェアチャイルド9440 - Nova 2と3の1チップ実装
National Semiconductor IMP-16 - オリジナルのNovaの5チップp-mos実装
ナショナルセミコンダクターPACEとナショナルセミコンダクターPACE - 単純化された命令セットを備えた1チップ実装