
リトルマンコンピュータ(LMC)は、1965年にスチュアート・マドニック博士によって作成されたコンピュータの教育用モデルです。 [ 1 ] LMCは、現代のコンピュータの基本的な機能をすべて備えたシンプルなフォン・ノイマン型アーキテクチャのコンピュータをモデル化しているため、一般的に学生の教育に使用されます。マシンコード(ただし、バイナリではなく10進数)またはアセンブリコードでプログラムできます。 [ 2 ] [ 3 ] [ 4 ]
LMCモデルは、小さな人間が閉じた郵便室に閉じ込められているという概念に基づいています(このシナリオではコンピュータに相当します)。部屋の一方の端には、0から99までの番号が付けられた100個のメールボックス(メモリ)があり、それぞれに3桁の命令またはデータ(000から999まで)を格納できます。さらに、もう一方の端にはINBOXとOUTBOXというラベルの付いた2つのメールボックスがあり、データの受信と出力に使用されます。部屋の中央には、加算と減算という単純な2機能の計算機であるアキュムレータと、プログラムカウンタと呼ばれるリセット可能なカウンタを備えた作業領域があります。プログラムカウンタには、小さな人間が次に実行する命令のアドレスが保持されます。このプログラムカウンタは通常、各命令の実行後に1ずつ増加し、小さな人間がプログラムを順次実行できるようにします。分岐命令を使用すると、反復(ループ)や条件付きプログラミング構造をプログラムに組み込むことができます。後者は、特定の条件(通常はアキュムレータに格納されている値がゼロまたは正の値である場合)が満たされた場合に、プログラムカウンタを非連続メモリアドレスに設定することによって実現されます。
フォン・ノイマン・アーキテクチャの規定によれば、任意のメールボックス(固有のメモリ位置を示す)には、命令またはデータのいずれかを格納できます。そのため、プログラムカウンタがデータを含むメモリアドレスに到達しないように注意する必要があります。そうしないと、リトルマンはそれを命令として扱おうとします。この特性を利用して、コードとして解釈されることを意図した命令をメールボックスに書き込み、自己書き換えコードを作成できます。LMCを使用するには、ユーザーはデータをメールボックスにロードし、メモリアドレス0に格納されている命令から実行を開始するようにリトルマンに指示します。プログラムカウンタをゼロにリセットすると、プログラムは実質的に再起動されますが、場合によっては異なる状態になります。
プログラムを実行するために、この小さなキャラクターは次の手順を実行します。
LMCはバイナリプロセッサの実際の動作を反映していますが、バイナリや16進数での作業に慣れていない学生にとって複雑さを最小限に抑えるために、10進数の単純さが選択されました。
LMCシミュレータの中には、3桁の数値命令を直接使用するものと、3文字のニーモニックコードとラベルを使用するものがあります。いずれの場合も、理解を容易にするため、命令セットは意図的に非常に限定されています(通常は約10個の命令)。LMCがニーモニックコードとラベルを使用する場合、プログラムのアセンブル時にこれらは3桁の数値命令に変換されます。
下の表は、典型的な数値命令セットと、それに対応するニーモニックコードを示しています。
このプログラム(命令901から命令000)は、数値コードのみを使用して記述されています。このプログラムは2つの数値を入力として受け取り、その差を出力します。実行はメールボックス00から始まり、メールボックス07で終了することに注意してください。数値命令コードを使用してLMCをプログラミングする際の欠点については、以下で説明します。
アセンブリ言語は、数値命令コードの代わりにニーモニックとラベルを使用する低レベルプログラミング言語です。LMCでは限られたニーモニックしか使用しませんが、各命令にニーモニックを使用することの利便性は、以下に示す同じプログラムのアセンブリ言語から明らかです。プログラマは、一連の匿名数値コードを記憶する必要がなくなり、より覚えやすいニーモニックコードを使用してプログラミングできるようになります。ニーモニックがメモリ アドレスを含む命令(分岐命令またはデータのロード/セーブ)である場合は、メモリ アドレスに名前を付けるためにラベルが使用されます。
INP STAファースト INP STAセカンド LDAファースト サブ秒 外 HLT 初デート 2回目のデート
ラベルがない場合、プログラマはメールボックス(メモリ)アドレスを手動で計算する必要があります。数値コードの例では、最後の HLT 命令の前に新しい命令を挿入する場合、その HLT 命令はアドレス 07 からアドレス 08 に移動します (アドレスのラベル付けはアドレス位置 00 から始まります)。ユーザーが最初の入力として 600 を入力したとします。命令 308 は、この値がアドレス位置 08 に格納され、000 (HLT) 命令を上書きすることを意味します。600 は「メールボックス アドレス 00 に分岐」を意味するため、プログラムは停止する代わりに無限ループに陥ります。
この問題を解決するために、ほとんどのアセンブリ言語(LMCを含む)はニーモニックとラベルを組み合わせて使用しています。ラベルとは、命令やデータが格納されているメモリアドレスを指定するため、または命令内でそのアドレスを参照するために使用される単語のことです。
プログラムが組み立てられるとき:
ニーモニックとラベルを使用するアセンブリ言語の例では、最後の HLT 命令の前に新しい命令が挿入された場合、FIRST というラベルの付いたアドレス位置はメモリ位置 08 ではなく 09 になり、プログラムがアセンブルされる際に STA FIRST 命令は 308 (STA 08) ではなく 309 (STA 09) に変換されます。
したがって、ラベルは以下の目的で使用されます。
以下のプログラムは、ユーザーからの入力を受け取り、ゼロまでカウントダウンします。
INP OUT // 出力の初期化 LOOP BRZ QUIT // このメモリ アドレスを LOOP とラベル付けします。アキュムレータの値が 0 の場合、ラベル付けされたメモリ アドレスにジャンプします。 // やめる SUB ONE // アキュムレータからアドレス ONE に格納されている値を減算します 外 BRA LOOP // LOOP とラベル付けされたメモリ アドレスに (無条件に) ジャンプする QUIT HLT // このメモリ アドレスを QUIT とラベル付けする ONE DAT 1 // このメモリ アドレスに値 1 を格納し、ONE というラベルを付けます (変数宣言)
以下のプログラムは、ユーザー入力を受け取り、それを二乗し、結果を出力して、この処理を繰り返します。ゼロを入力するとプログラムが終了します。 (注:LMCの3桁の数値制限のため、999を超える値を入力すると動作が未定義になります。)
START LDA ZERO // 複数プログラム実行のための初期化 STA結果 STAカウント INP // ユーザーが入力した情報 BRZ END // 入力が 0 の場合、プログラム END へ分岐する STA VALUE // 入力値をVALUEとして保存する LOOP LDA RESULT // RESULT をロードする 値の追加 // ユーザーが入力した値(VALUE)をRESULTに追加します STA RESULT // 新しいRESULTを保存する LDA COUNT // COUNTをロードします 1を加算 // カウントに1を加算します STA COUNT // 新しいCOUNTを保存する SUB VALUE // COUNTからユーザーが入力したVALUEを減算します BRZ ENDLOOP // ゼロの場合(VALUEがRESULTにVALUE回加算された場合)、ENDLOOPに分岐 BRA LOOP // ループに分岐して、VALUE を RESULT に追加し続けます ENDLOOP LDA RESULT // 結果をロード 出力 // 結果を出力する BRA START // STARTに分岐して初期化し、別の入力値を取得します END HLT // HALT - ゼロが入力されたので終了です! 結果データ // 計算結果(デフォルト値は0) COUNT DAT // カウンター(デフォルトは0) ONE DAT 1 // 定数、値は1 VALUE DAT // ユーザーが入力した値。二乗する値(デフォルト値は0) ZERO DAT // 定数、値は0(デフォルト値は0)
注:DAT文の後にデータがない場合、デフォルト値0がメモリアドレスに格納されます。
上記の例では、[BRZ ENDLOOP] は未定義の動作に依存しています。COUNT-VALUE が負の値になる場合、ACCUMULATOR の値が未定義となり、BRZ が分岐するかしないかが不確定になります (ACCUMULATOR がゼロになるか、またはオーバーフローする可能性があります)。コードを仕様に準拠させるには、以下を置き換えてください。
... LDA COUNT // COUNTをロードします 1を加算 // カウントに1を加算します STA COUNT // 新しいCOUNTを保存する SUB VALUE // COUNTからユーザーが入力したVALUEを減算します BRZ ENDLOOP // ゼロの場合(VALUEがRESULTにVALUE回加算された場合)、ENDLOOPに分岐 ...
次のバージョンでは、COUNT-VALUEではなくVALUE-COUNTを評価することで、アキュムレータがアンダーフローしないようにします。
... LDA COUNT // COUNTをロードします 1を加算 // カウントに1を加算します STA COUNT // 新しいCOUNTを保存する LDA VALUE // VALUE をロードします SUB COUNT // ユーザーが入力した値からCOUNTを減算します BRZ ENDLOOP // ゼロの場合(VALUEがRESULTにVALUE回加算された場合)、ENDLOOPに分岐 ...
もう一つの例は、自身のマシンコードを出力するクワインである(文字を出力できないため、ソースコードを出力することは不可能である)。
LOAD LDA 0 // 位置 0 をアキュムレータにロードします。この行は、次の行をアキュムレータにロードするために、ループごとに変更されます。 OUT // アキュムレータの値を出力します。アキュムレータの値は、先ほど読み込まれた行の値になります。 SUB ONE // アキュムレータの値から 1 を減算します。これは、次のステップで BRZ を実行して、プログラムの最後の行にいるかどうかを確認するためです。 BRZ ONE // 前の減算でアキュムレータが0になった場合(つまり、アキュムレータに001の値があった場合)、位置ONEに分岐する LDA LOAD // LOAD 位置をアキュムレータにロードします。これは、この位置のアドレス桁をインクリメントするための準備です。 ADD ONE // LOAD 行の位置桁をインクリメントします。アキュムレータに現在格納されている値は、命令として読み取られた場合、最後にロードされた行と比較して、次の行をアキュムレータにロードします。 STA LOAD // 新しくインクリメントされたLOAD行をLOAD位置に格納する ブラロード // ループの先頭に戻る ONE DAT 1 // 変数ONE。命令として読み込まれると、HLT/COBとして解釈され、プログラムが終了します。
このクワインは自己書き換えコードを使用して動作します。各イテレーションで位置0が1ずつインクリメントされ、その行のコードが出力されます。出力されるコードが1になると、1の位置に分岐します。1の位置の値はオペコードが0なので、HALT/COB命令として解釈されます。
C++
using namespace std; int mainO [ {
浮動小数点数、長さ、幅、面積。