MIX (プログラミング)
MIX とは、ドナルド・クヌースが著書 The Art of Computer Programming (TAoCP) で使った仮想計算機である。1960年代に生まれた MIX は、今後の TAoCP の版では MMIX という新たな(仮想の)コンピュータアーキテクチャで置換される予定である。MIX と MMIX のソフトウェア実装(MIXware および MMIXware)はクヌースが開発しており、自由に利用可能となっている。クヌースの MIX/MMIX エミュレータからの派生版も存在する。GNU MDK がその一例で、フリーソフトウェアとして幅広いプラットフォーム上で動作する。
TAoCP 本文中の記述によれば、MIX は「世界初の多機種複合型コンピュータ[1]」で、型番は MIX という綴りをローマ数字として解釈したものと同じ 1009 であると設定されている。この数は「MIX によく似ていて MIX を簡単にシミュレートできる実在のコンピュータ 16 種」の型番から取って平均した(mixした)ものである[2]とあるが、具体的にその機種[3]を検討してみるとかなり恣意的であり、このラインナップについてはこじつけと見てよいであろう。
アーキテクチャ
[編集]MIX は二進法と十進法のハイブリッド型コンピュータである。二進法でプログラムする場合、各バイトは6ビットで構成される(値は 0 から 63 まで)。十進法では、1バイトは2桁の十進数字で構成される(値は 0 から 99 まで)。5バイトと符号で1ワードが構成される。MIX 用プログラムの多くは二進法でも十進法でも動作する(1バイトに63より大きい値を格納しようとしない限り)。
1ワードで表せる範囲は、二進モードでは −1,073,741,823 から 1,073,741,823 まで(これらの値を含む)、十進モードでは −9,999,999,999 から 9,999,999,999 まで(これらの値を含む)である。符号-仮数表現であるため、MIX では “−0” と “+0” が表現上区別される。これは、現代のコンピュータの多くが固定長整数の表現に採用している2の補数表現とは大きく異なる点である(浮動小数点数の表現には現代のコンピュータでもよく使われている)。
レジスタ
[編集]MIX ではレジスタは9本存在する。
- rA: アキュムレータ(1ワード、すなわち5バイトと符号)
- rX: 拡張レジスタ(1ワード)
- rI1, rI2, rI3, rI4, rI5, rI6: インデックスレジスタ(2バイトと符号)
- rJ: ジャンプアドレスレジスタ(2バイト。常に正なので符号なし)
バイトは最低でも6ビットで構成される。命令ではバイト単位にレジスタ上の操作位置を指定でき、(first:last) の形式で表される(命令の修飾部)。0番目のフィールドは1ビットの符号を指す。
MIX では、直前の操作によってオーバフローが発生したかどうかを記録するフィールドと、比較結果を3種類の値(大きい、等しい、小さい)で示すフィールドも持っている。以下の図では、各レジスタをフィールドに分割して表示している。
|
|
|
|
|
|
|
|
|
|
|
メモリと入出力
[編集]MIXマシンには、4000ワードのストレージ(メモリ)があり、アドレスは 0 から 3999 までである。以下のような各種入出力機器も含まれる。
- 磁気テープ装置(デバイス番号 0 から 7)
- 磁気ディスクまたは磁気ドラム装置(デバイス番号 8 から 15)
- カードリーダ(デバイス番号 16)
- カードパンチ(デバイス番号 17)
- ラインプリンタ(デバイス番号 18)
- タイプライタ端末(デバイス番号 19)
- 紙テープ装置(デバイス番号 20)
命令
[編集]各命令はメモリ上の1ワードに格納され、4つの部分から構成される。メモリアドレス部(2バイトと符号ビット)、インデックス指定部(1バイト、rIx レジスタの番号を指定)、修飾部(1バイト)、命令コード(1バイト)である。修飾部はアドレス指定されたワード内のフィールド位置の指定に使われる。全ての命令コードにはニーモニックが対応している。
MIX では自己書き換えコードをよく使う。特にサブルーチンからの自動復帰機能(リターンスタック)が命令セット上考慮されていないので、その場合も自己書き換えコードを使う。自己書き換えの際には、命令の中の修飾部を駆使して、命令ワード内の特定の部分だけを書き換える(例えば、アドレス部だけを書き換えるなどする)。
MIXプログラムを書く場合、MIXAL というアセンブリ言語を使う。
LDA ADDR,i (0:5) | rA := memory[ADDR + rIi]; |
---|---|
LDX ADDR,i (0:5) | rX := memory[ADDR + rIi]; |
LD? ADDR,i (0:5) | rI? := memory[ADDR + rIi]; |
LDAN ADDR,i (0:5) | rA := - memory[ADDR + rIi]; |
LDXN ADDR,i (0:5) | rX := - memory[ADDR + rIi]; |
LD?N ADDR,i (0:5) | rI? := - memory[ADDR + rIi]; |
STA ADDR,i (0:5) | memory[ADDR + rIi] := rA; |
STX ADDR,i (0:5) | memory[ADDR + rIi] := rX; |
ST? ADDR,i (0:5) | memory[ADDR + rIi] := rI?; |
STJ ADDR,i (0:5) | memory[ADDR + rIi] := rJ; |
STZ ADDR,i (0:5) | memory[ADDR + rIi] := 0; |
ADD ADDR,i (0:5) | rA := rA + memory[ADDR + rIi]; |
SUB ADDR,i (0:5) | rA := rA - memory[ADDR + rIi]; |
MUL ADDR,i (0:5) | (rA,rX) := rA * memory[ADDR + rIi]; |
DIV ADDR,i (0:5) | rA := int( (rA,rX) / memory[ADDR + rIi]); rX := (rA,rX) mod memory[ADDR + rIi]; |
ENTA ADDR,i | rA := ADDR + rIi; |
ENTX ADDR,i | rX := ADDR + rIi; |
ENT? ADDR,i | rI? := ADDR + rIi; |
ENNA ADDR,i | rA := - ADDR - rIi; |
ENNX ADDR,i | rX := - ADDR - rIi; |
ENN? ADDR,i | rI? := - ADDR - rIi; |
INCA ADDR,i | rA := rA + ADDR + rIi; |
INCX ADDR,i | rX := rX + ADDR + rIi; |
INC? ADDR,i | rI? := ADDR + rIi; |
DECA ADDR,i | rA := rA - ADDR - rIi; |
DECX ADDR,i | rX := rX -ADDR - rIi; |
DEC? ADDR,i | rI? := rI? - ADDR - rIi; |
CMPA ADDR,i (0:5) | rA を memory[ADDR + rIi] と比較; |
CMPX ADDR,i (0:5) | rX を memory[ADDR + rIi] と比較; |
CMP? ADDR,i (0:5) | rI? を memory[rIi + ADDR] と比較; |
JMP ADDR,i | goto ADDR + rIi; |
JSJ ADDR,i | rJ := 次の命令のアドレス; goto ADDR + rIi; |
JOV ADDR,i | if (overflow) then overflow := false; goto ADDR + rIi; |
JNOV ADDR,i | if (no overflow) then goto ADDR + rIi; else overflow := false; |
JL, JE, JG ADDR,i JGE, JNE, JLE ADDR,i |
if (less, equal, greater) then goto ADDR + rIi; if (no less, unequal, no greater) then goto ADDR + rIi; |
JAN, JAZ, JAP ADDR,i JANN, JANZ, JANP ADDR,i |
if (a<0 / a==0 / a>0) then goto ADDR + rIi; if (a>=0 / a!=0 / a<=0) then goto ADDR + rIi; |
JXN, JXZ, JXP ADDR,i JXNN, JXNZ, JXNP ADDR,i |
if (x<0 / x==0 / x>0) then goto ADDR + rIi; if (x>=0 / x!=0 / x<=0) then goto ADDR + rIi; |
J?N, J?Z, J?P ADDR,i J?NN, J?NZ, J?NP ADDR,i |
if (rI?<0 / rI?==0 / rI?>0) then goto ADDR + rIi; if (rI?>=0 / rI?!=0 / rI?<=0) then goto ADDR + rIi; |
MOVE ADDR,i(F) | for (n=1; n<=F; n++, rI1++) memory[ADDR + rIi + n] := memory[rI1]; |
SLA, SRA ADDR,i SLAX, SRAX ADDR,i SLC, SRC ADDR,i |
a を左(L)/右(R)に ADDR+rIi バイトだけシフト ax を左(L)/右(R)に ADDR+rIi バイトだけシフト ax を左(L)/右(R)に ADDR+rIi バイトだけローテート |
NOP | 何もしない; |
HLT | 実行停止; |
IN ADDR,i(F) | 入力装置 F から1ブロック読み取り memory[ADDR + rIi] を始点として格納; |
OUT ADDR,i(F) | 出力装置 F に memory[ADDR + rIi] を始点として1ブロックを書き込み; |
IOC ADDR,i(F) | 入出力装置 F に制御命令を送る; |
JRED ADDR,i(F) | if (入出力装置 F はレディ状態) then goto ADDR + rIi; |
JBUS ADDR,i(F) | if (入出力装置 F はビジー状態) then goto ADDR + rIi; |
NUM | rA := (rA,rX) にある文字列を数値化した値; |
CHAR | (rA,rX) := rA の内容を数値として文字列(文字コード)で表したもの; |
なお、シフト命令を見てもわかるとおり、MIXではバイトの内部構造が影響するような命令を持たない(例えばビット演算)。このため、二進法でも十進法でも同じプログラムが動作するのである。
注
[編集]- ^ パソコンが「なんでもできる箱」になった現代からは想像するのは難しいが、1960年代以前のコンピュータは「科学技術計算用」「事務処理用」と、全く別々に設計されていた。1964年のSystem/360が、現代に繋がる「汎用」という設計コンセプトの始祖であり、本書の執筆当初はまだそのようなコンピュータは「最新の」機種であった。
- ^ 現代ではベンチマークプログラムにとって代わられたが、以前は「ギブソンミックス」等と称して、「実際のプログラムで実行される命令に近い割合」で命令実行速度をミックスした値が性能の指標として使われていた。
- ^ System/360、IBM 650、IBM 709、IBM 7070、U3(?)、SS80(en:UNIVAC Solid State 80)、UNIVAC 1107(en:UNIVAC 1100/2200 series)、en:CDC 1604、en:Bendix G-20、B220(バロースのDatatron 220か)、en:Philco Transac S-2000(日本ではあまり有名ではないが、商業的に成功した初のトランジスタ機として重要とされている)、SDS 920(en:SDS 9 Series)、601(?、Minivac 601かRCA 601か)、en:Honeywell 800、PDP-4、II(en:UNIVAC II か?)
外部リンク
[編集]- MMIX 2009: A RISC Computer for the Third Millennium クヌースの公式MIXページ
- MMIX News クヌースの公式MIXニュース
- MMIXware: A RISC Computer for the Third Millennium クヌースの公式MIX本
- Open Directory: Computers: Programming: Languages: Assembly: MIX-MMIX MIX-MMIX/MIXAL-MMIXAL リンク集