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ではバイトの内部構造が影響するような命令を持たない︵例えばビット演算︶。このため、二進法でも十進法でも同じプログラムが動作するのである。