MIX (プログラミング)

MIX とは、ドナルド・クヌースが著書 The Art of Computer Programming (TAoCP) で使った仮想計算機である。1960年代に生まれた MIX は、今後の TAoCP の版では MMIX という新たな（仮想の）コンピュータアーキテクチャで置換される予定である。MIX と MMIX のソフトウェア実装（MIXware および MMIXware）はクヌースが開発しており、自由に利用可能となっている。クヌースの MIX/MMIX エミュレータからの派生版も存在する。GNU MDK がその一例で、自由ソフトウェアとして幅広いプラットフォーム上で動作する。

TAoCP 本文中の記述によれば、MIX は「世界初の多機種複合型コンピュータ」で、型番は MIX という綴りをローマ数字として解釈したものと同じ 1009 であると設定されている。この数は「MIX によく似ていて MIX を簡単にシミュレートできる実在のコンピュータ 16 種」の型番から取って平均した（mixした）ものであるとあるが、具体的にその機種を検討してみるとかなり恣意的であり、このラインナップについてはこじつけと見てよいであろう。

 アーキテクチャ 
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種類の値（大きい、等しい、小さい）で示すフィールドも持っている。以下の図では、各レジスタをフィールドに分割して表示している。

 ± A1 A2 A3 A4 A5
 ± X1 X2 X3 X4 X5
 + J4 J5
 OV?
 <=>?
 
 ± I1.4 I1.5
 ± I2.4 I2.5
 ± I3.4 I3.5
 ± I4.4 I4.5
 ± I5.4 I5.5
 ± I6.4 I6.5
 

 メモリと入出力 
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,iJGE, 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,iJANN, 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,iJXNN, 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,iJ?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,iSLAX, SRAX ADDR,iSLC, 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ではバイトの内部構造が影響するような命令を持たない（例えばビット演算）。このため、二進法でも十進法でも同じプログラムが動作するのである。