Burroughs B6x00-7x00 命令セットには、Burroughs B6500、[ 1 ] B7500 およびそれ以降のBurroughs Large Systemsの有効な操作のセットが含まれています。これには、現在の (2006 年時点) Unisys Clearpath/MCP システムも含まれます。B5000、B5500、B5700、B8500 などの他の Burroughs Large Systems の命令は含まれていません。これらの独自のマシンは、独特の設計と命令セットを持っています。データの各ワードは型に関連付けられており、そのワードに対する操作の効果は型によって異なる場合があります。さらに、これらのマシンはスタック[ a ]ベースであるため、ユーザーがアドレス指定できるレジスタはありません。
これらのシステムで使用されている独自のアーキテクチャから予想されるように、これらのシステムには興味深い命令セットがあります。プログラムは8 ビットのシラブルで構成され、名前呼び出し、値呼び出し、または演算子を形成します。演算子の長さは 1 ~ 12 シラブルです。演算子は200 未満で、すべて 8 ビットのシラブルに収まります。強力な文字列スキャン、転送、および編集演算子を無視すると、基本セットは約 120 個の演算子だけです。MVST や HALT など、オペレーティングシステム用に予約されている演算子を除くと、ユーザーレベルのプログラムで一般的に使用される演算子のセットは 100 未満です。名前呼び出しと値呼び出しのシラブルにはアドレスのペアが含まれます。演算子のシラブルはアドレスを使用しないか、スタック上の制御ワードと記述子を使用します。
プログラマがアドレス指定できるレジスタがないため、他のアーキテクチャで必要とされるレジスタ操作のほとんどは不要であり、レジスタペア間の操作を実行するためのバリアントも不要です。これは、すべての操作がスタックの最上位に適用されるためです。また、演算子はゼロアドレスであり、コード ストリームにレジスタやメモリ位置のアドレスを含める必要がないため、コード ファイルは非常にコンパクトになります。コード密度の一部は、重要なオペランド情報を他の場所、つまり各データ ワードの「タグ」またはポインタ テーブルに移動したことによるものです。多くの演算子は、タグによって指定される操作対象のデータの種類に応じて、汎用的または多態的です。汎用オペコードは必要なオペコード ビットが少なくて済みますが、ハードウェアはインタープリタに近くなり、一般的なケースをパイプライン処理する機会が少なくなります。
例えば、この命令セットには ADD 演算子が 1 つしかありません。整数加算か浮動小数点加算かを判断するために、オペランドをフェッチする必要がありました。一般的なアーキテクチャでは、データ型ごとに複数の演算子が必要で、例えば、整数、浮動小数点、倍精度、長整数のデータ型には add.i、add.f、add.d、add.l があります。このアーキテクチャでは、単精度と倍精度の数値のみが区別されます。整数は単に指数がゼロの実数です。オペランドの 1 つまたは両方にタグ 2 が付いている場合、倍精度加算が実行されます。それ以外の場合は、タグ 0 は単精度を示します。したがって、タグ自体は演算子 .i、.f、.d、および .l 拡張機能に相当します。これはまた、コードとデータが一致しないことがないことを意味します。
スタック上のデータの処理において重要な演算子は、値呼び出し(VALC)と名前呼び出し(NAMC)の2つです。これらは2ビット演算子で、00がVALC、01がNAMCです。音節の次の6ビットと次の音節を連結することで、アドレスペアが生成されます。したがって、VALCは音節値0000~3FFFを、NAMCは音節値4000~7FFFをカバーします。
VALC はもう一つの多態性演算子です。データワードにヒットした場合、そのワードがスタックの最上位にロードされます。IRW にヒットした場合は、データワードが見つかるまで、IRW が連鎖的に続く可能性があります。PCW が見つかった場合は、値を計算する関数が実行され、その関数が戻るまで VALC は完了しません。
NAMCは、アドレスペアをIRWとしてスタックの最上位にロードするだけです(タグは自動的に1に設定されます)。
静的分岐(BRUN、BRFL、BRTR)はオフセットに2つの追加音節を使用しました。そのため、算術演算は1音節、アドレッシング演算(NAMCとVALC)は2音節、分岐は3音節、ロングリテラル(LT48)は5音節を占有しました。結果として、各演算が4バイトを占有する従来のRISCアーキテクチャよりもコード密度がはるかに高く(エントロピーが優れていました)、コード密度の向上は命令キャッシュミスの減少、ひいては大規模コード実行時のパフォーマンス向上につながりました。
以下の演算子の説明では、AとBはスタックの最上位2つのレジスタであることを覚えておいてください。倍精度拡張はXレジスタとYレジスタによって提供されるため、最上位2つの倍精度オペランドはAXとBYで表されます。(ほとんどの場合、AXとBYはAとBだけで暗黙的に示されます。)
B6500では、1ワードは48ビットのデータと3ビットのタグで構成されます。データビットは0~47ビット、タグは48~50ビットです。48ビット目は読み取り専用ビットであるため、奇数個のタグは、ユーザーレベルプログラムで書き込みができない制御ワードを示します。コードワードにはタグ3が割り当てられます。以下に、タグとその機能の一覧を示します。
これらのマシンの現行バージョンであるUnisys ClearPathは、タグをさらに拡張し、4ビットタグを実現しました。4ビットタグを規定したマイクロコードレベルは、ガンマレベルと呼ばれていました。
偶数タグのワードは、ユーザープログラムによってユーザー状態として変更可能なユーザーデータです。奇数タグのワードはハードウェアによって直接生成および使用され、プログラムの実行状態を表します。これらのワードは特定の命令またはハードウェアによって生成および消費されるため、ハードウェアの実装によってワードの正確なフォーマットが変わる可能性がありますが、システムワードのフォーマットが変わっても、同じコードストリームは同じ結果を生成するため、ユーザープログラムを再コンパイルする必要はありません。
タグ1ワードは、スタック上のデータアドレスを表します。通常のIRWは、現在のスタック上のデータに対応するアドレスペアを単純に格納します。SIRWは、アドレスにスタック番号を含めることで、任意のスタック上のデータを参照します。
タグ5の単語は記述子であり、次のセクションでより詳しく説明します。タグ5の単語は、スタック外のデータアドレスを表します。
タグ7は、プロシージャのエントリポイントを示すプログラム制御ワードです。オペレータがPCWに到達すると、プロシージャが実行されます。ENTRオペレータは、プロシージャ(値を返さないルーチン)を明示的に実行します。関数(値を返すルーチン)は、VALCなどのオペレータによって暗黙的に実行されます。グローバルルーチンは、D[1]環境のコードセグメント辞書に格納されているPCWを指すSIRWとして、D[2]環境に格納されます。D[1]環境は、このコードを共有するすべてのプロセスから参照できるため、現在のスタックには格納されません。したがって、コードは再入可能で共有されます。
タグ3はコードワード自体を表し、スタック上には出現しません。タグ3は、スタック制御ワードであるMSCW、RCW、TOSCWにも使用されます。

スタックハードウェアの最適化とは、Dレジスタ(または「表示」レジスタ)を提供することです。これらは、呼び出された各スタックフレームの開始位置を指すレジスタです。これらのレジスタは、プロシージャの開始と終了時に自動的に更新され、ソフトウェアからはアクセスできません。Dレジスタは32個あり、これが字句のネストレベルを32レベルに制限する理由です。
語彙レベル5 (D[5]) から語彙レベル2 (D[2]) のグローバル変数にアクセスする方法を考えてみましょう。変数が語彙レベル2の基底から6ワード離れているとします。つまり、アドレスペア(2, 6)で表されます。Dレジスタがない場合、D[5]フレームの基底にある制御ワードを参照する必要があります。この制御ワードは、D[4]環境を含むフレームを指しています。次に、この環境の基底にある制御ワードを参照してD[3]環境を見つけ、必要な語彙レベルまで全てのリンクをたどるまでこの手順を繰り返します。これは、この地点に到達するために呼び出されたプロシージャを戻るパスとは異なります。(アーキテクチャはデータスタックとコールスタックを同じ構造で保持しますが、制御ワードを使用して両者を区別します。)
ご覧のとおり、変数にアクセスするだけでも、これは非常に非効率的です。Dレジスタを使用すると、D[2]レジスタはレキシカルレベル2環境のベースを指し、変数のアドレスを生成するために必要なのは、スタックフレームベースからのオフセットをDレジスタ内のフレームベースアドレスに加えることだけです。(効率的なリンクリスト検索演算子LLLUがあり、上記のようにスタックを検索できますが、Dレジスタ方式の方が高速です。)Dレジスタを使用すると、外部環境およびグローバル環境のエンティティへのアクセスは、ローカル変数へのアクセスと同じくらい効率的です。
Dタグデータ — アドレスペア、コメント 登録する
| 0 | n | (4, 1) 整数n (プロシージャではなく、ブロックへの入室時に宣言される) |-----------------------| | D[4]==>3 | MSCW | (4, 0) D[3]へのリンクを含むマークスタック制御ワード。 |========================| | 0 | r2 | (3, 5) 実数r2 |-----------------------| | 0 | r1 | (3, 4) 実数r1 |-----------------------| | 1 | p2 | (3, 3) (2,6)におけるg へのSIRW参照 |-----------------------| | 0 | p1 | (3, 2) fの値からの パラメータp1 |-----------------------| | 3 | RCW | (3, 1) リターン制御ワード |-----------------------| | D[3]==>3 | MSCW | (3, 0) D[2]へのリンクを含むマークスタック制御ワード。 |========================| | 1 | a | (2, 7) 配列a ======>[10ワードのメモリブロック] |-----------------------| | 0 | g | (2, 6) 実数g |-----------------------| | 0 | f | (2, 5) 実数f |-----------------------| | 0 | k | (2, 4) 整数k |-----------------------| | 0 | j | (2, 3) 整数j |-----------------------| | 0 | i | (2, 2) 整数i |-----------------------| | 3 | RCW | (2, 1) リターン制御ワード |-----------------------| | D[2]==>3 | MSCW | (2, 0) 前のスタックフレームへのリンクを含むマークスタック制御ワード。 |========================| — スタックの底
プロシージャ p をコルーチンまたはプロセス命令として呼び出した場合、D[3] 環境は別の D[3] ベースのスタックになります。これは、ALGOL プログラム コードで暗示されているように、非同期プロセスが D[2] 環境にアクセスできることを意味します。さらに一歩進んで、まったく異なるプログラムが別のプログラムのコードを呼び出し、自身のプロセス スタックの上に別のプロセスの D[2] 環境を指す D[3] スタック フレームを作成することもできます。コードの実行環境のアドレス空間全体が瞬時に変化し、自身のプロセス スタック上の D[2] 環境は直接アドレス指定できなくなり、代わりに別のプロセス スタックの D[2] 環境が直接アドレス指定できるようになります。これがライブラリ呼び出しの実装方法です。このようなクロス スタック呼び出しでは、呼び出し元のコードと呼び出されるコードは、異なるソース言語で記述され、異なるコンパイラでコンパイルされたプログラムから生成されることさえあります。
D[1]環境とD[0]環境は、現在のプロセスのスタックには存在しません。D[1]環境はコードセグメント辞書であり、同じコードを実行するすべてのプロセスで共有されます。D[0]環境は、オペレーティングシステムによってエクスポートされるエンティティを表します。
スタックフレームは、実際にはプロセススタック内に存在する必要すらありません。この機能は、初期の頃はファイルI/Oの最適化に利用され、I/O操作中にFIB(ファイル情報ブロック)がD[1]のアドレスにある表示レジスタにリンクされていました。1990年代初頭には、この機能は言語機能としてSTRUCTURE BLOCKSとして実装され、ライブラリ技術と組み合わされてCONNECTION BLOCKSとしても利用されました。データ構造を表示レジスタのアドレス範囲にリンクする機能は、オブジェクト指向を実装するものでした。つまり、B6500は、オブジェクト指向という用語が使われるずっと前から、ある種のオブジェクト指向を利用していたのです。
他のシステムでは、コンパイラは同様の方法でシンボルテーブルを構築するかもしれませんが、最終的にはストレージ要件が調整され、マシンコードは16ビット、32ビット、あるいは64ビットのフラットなメモリ アドレスを使用するように記述されます。これらのアドレスには何でも格納できるため、誤ったアドレスに書き込むとあらゆるものが破損する可能性があります。代わりに、2部構成のアドレス方式がハードウェアによって実装されました。各字句レベルでは、変数はレベルのスタックのベースからオフセットされた位置に配置され、通常は1ワードを占めます。倍精度または複素数の変数は2ワードを占めます。配列はこの領域には格納されず、配列の1ワードの記述子のみが格納されます。したがって、各字句レベルでの総ストレージ要件は大きくなく、数十、数百、極端な場合でも数千程度であり、32ビット以上を必要とするようなカウントではありません。そして実際、これはオペランドをスタックにロードするVALC命令(値呼び出し)の形式に反映されています。このオペコードは 2 ビット長で、バイトの残りのビットは次のバイトと連結され、14 ビットのアドレス指定フィールドが作成されます。実行されるコードは、例えば 6 のような何らかのレキシカル レベルにあります。これは、レキシカル レベル 0 から 6 のみが有効であることを意味し、目的のレキシカル レベルを指定するには 3 ビットのみが必要です。したがって、VALC 操作のアドレス部分は、その目的のために 3 ビットだけを予約し、残りはそのレベルおよびそれより低いレベルのエンティティを参照するために使用できます。深くネストされたプロシージャ (つまり、高いレキシカル レベル) では、エンティティを識別するために使用できるビットが少なくなります。レベル 16 以上では、レベル 0 から 31 の選択を指定するために 5 ビットが必要となり、残りの 9 ビットで任意のレキシカル レベルの最初の 512 個のエンティティのみを識別できます。これは、32 ビットのアドレス空間でエンティティをリテラルメモリ アドレスでアドレス指定するよりもはるかにコンパクトです。さらに、データをロードしたのはVALCオペコードのみであり、ADD、MULTなどのオペコードはアドレッシングを行わず、スタックの最上位要素のみを操作していた。
さらに重要なのは、この方式によって、フラットアドレッシングを採用するシステムで発生する可能性のある多くのエラーが、マシンコードレベルでもそもそも発生し得ないほど深刻なものになったことです。タスクは自身のアドレスを生成する手段を持たないため、他のタスクが使用しているメモリを破損させることはできませんでした。指定されたDレジスタからのオフセットは、ハードウェアによってスタックフレームの境界と照合され、不正な値は捕捉されました。同様に、タスク内では、配列記述子に配列の境界に関する情報が含まれており、インデックス操作はすべてハードウェアによってチェックされました。言い換えれば、各配列は独自のアドレス空間を形成しました。いずれにせよ、すべてのメモリワードにタグを付けることで、第2レベルの保護が提供されました。誤った値の割り当ては、データを保持する場所にのみ行われ、ポインタや配列記述子などを保持する場所には行われず、ましてやマシンコードを保持する場所には行われませんでした。
—続いてNAMCでPCWをロードし、必要に応じてパラメータをプッシュし、その後ENTR)
— Aレジスタで指定されたメモリアドレスに、Bレジスタに値を格納する。 —スタックから値を削除する。
Load命令は、間接アドレスに引っかかったり、さらに悪いことに、名前呼び出しサンクルーチンへの偽装された呼び出しに遭遇したりする可能性がある。
必要に応じて、住所の連鎖を追跡してください。
これらは通常、ソース文字列内に特定の文字が検出されるまで文字列転送に使用されました。これらの演算子はすべて、記述子の境界によって制限されているため、バッファオーバーフローから保護されています。
これらは、コンパイラ作成に役立つ文字列のスキャンに使用されました。これらの演算子はすべて、記述子の境界によって制限されているため、バッファオーバーフローから保護されています。
これらは、特にビジネスアプリケーションにおいて、高度な文字列操作を行うための特殊な演算子であった。