計算機の基本原理を味わおう29 - 命令形式を見直しモジュール化に備える
現在、最小計算機にコードセグメントを変更する術がありません。では、どうすればいいのでしょうか?答えは「命令で対応する」です。CPUは命令に従って動作するものですから、命令で対応するのが自然の流れです。ただし、命令といっても、新命令は必要ありません。最小計算機には比較移動命令しかありませんが、それだけで十分です。命令を増やすと、最小の名に反しますので、比較移動命令の形式を検討し直すことにします。
比較移動命令は、特別な数値を用意することにより、影響を与える対象を特定していました。同様に、コードセグメントにも特別な数値を対応付けします。コードセグメントの数値を決めるにあたって、仕様を簡潔にするために、他の数値についても対応付けをやり直します。
特別な数値と対応されているものは、定数の0、定数の1、CPU停止の指示、比較フラグ、プログラムカウンタ、コードセグメントです。各対象と数値の対応を書きます。
・定数の0:0 ・定数の1:1 ・終了フラグ:255 ・比較フラグ:254 ・プログラムカウンタ:253 ・コードセグメント:252
定数以外は255から数値割り振りをしている理由は、今後増える可能性があり、2とか3とかの小さな数値を指定できるように維持したいからです。もし、2、3、4、・・・という具合に数値を対応付けすると、よく使う小さな数値が使用できなくなります。255から割り振ることにしました。
対応付けを変えると、3つのオブジェクトを変更しなくてはなりません。とはいえ、あまり大きな変更ではありません。ちょっと修正するだけです。
public class Cpu
{
//実行環境に関する情報を取得する。
public static ExecuteEnvironment GetEnvironment( )
{
ExecuteEnvironment result =
new ExecuteEnvironment() {
ZeroAddress = 0,
OneAddress = 1,
LastAddress = 255,
CompareFlagAddress = 254,
ProgramCounterAddress = 253,
CodeSegmentAddress = 252
};
return result;
}
}
//命令を解析するオブジェクト。
public class Decoder
{
//命令をデコードする。
public CompareMove Decode( IEnumerable<bool> binary )
{
CompareMove result = new CompareMove( binary );
bool imFlag = false;
result.Destination = this.GetOperand(
result.Destination, ref result, true, ref imFlag );
result.Source = this.GetOperand(
result.Source, ref result, false, ref imFlag );
return result;
}
//オペランドを取得する。
private IOperand GetOperand(
IOperand ope,
ref CompareMove target,
bool setFlag,
ref bool isImmediate )
{
IOperand result = ope;
if ( isImmediate ) {
return ope;
}
if ( ope.Value ==
this._exeInfo.ZeroAddress ) {
result = new Immediate( 0 );
} else if ( ope.Value ==
this._exeInfo.OneAddress ) {
result = new Immediate( 1 );
} else if ( ope.Value ==
this._exeInfo.LastAddress ) {
result = this._exeInfo.IsStop;
} else if ( ope.Value ==
this._exeInfo.CompareFlagAddress ) {
result = this._exeInfo.CompareFlag;
target.CompareFlag = true;
return result;
} else if ( ope.Value ==
this._exeInfo.ProgramCounterAddress ) {
result = this._addresser.ProgramCounter;
isImmediate = true;
} else if ( ope.Value ==
this._exeInfo.CodeSegmentAddress ) {
result = this._addresser.CodeSegment;
isImmediate = true;
} else {
var ms = new MemorySnapShot(
this._addresser,
this._exeInfo.ReferenceMemory,
ope.Value );
result = ms;
}
if ( setFlag ) {
target.CompareFlag =
this._exeInfo.GetCompareFlag();
}
return result;
}
}
修正部分だけ掲載しました。オペランドを設定する部分は、ついでにリファクタリングしました。このプログラムを参考に、貴方のプログラムを修正してください。プログラムを修正したらテストです。正しく動作するかテストしましょう。
class Sample
{
static void Main()
{
ExecuteEnvironment ex = Cpu.GetEnvironment();
ProgramCounterUpdateTest( ex );
CodeSegmentUpdateTest( ex );
CpuStopTest( ex );
Console.WriteLine( "テスト終了" );
Console.ReadLine();
}
//テストを実行。
static Cpu Execute( ExecuteEnvironment ex,
CompareMove target )
{
//CPUを用意
Cpu cpu = new Cpu( 0, 1, new Memory() );
//次の命令を実行すための準備用命令
var flagSet = new CompareMove();
flagSet.Destination =
new Immediate( ex.CompareFlagAddress );
flagSet.Source =
new Immediate( ex.OneAddress );
//メモリにこれから実行する命令を書きこむ
var opes = new CompareMove[ ] {
flagSet, target };
cpu.CodeWrite( opes );
//命令を実行
cpu.Run();
cpu.Run();
return cpu;
}
//プログラムカウンタを更新できるかテスト。
static void ProgramCounterUpdateTest(
ExecuteEnvironment ex )
{
//プログラムカウンタ更新命令
byte value = 10;
var target = new CompareMove();
target.Destination =
new Immediate( ex.ProgramCounterAddress );
target.Source =
new Immediate( value);
//実行&結果チェック
Cpu cpu = Execute( ex, target );
if ( cpu.Addresser.ProgramCounter.Value != value ) {
Console.WriteLine( "テスト失敗!:PCの値{0}",
cpu.Addresser.ProgramCounter.Value );
}
}
//コードセグメントを更新できるかテスト。
static void CodeSegmentUpdateTest(
ExecuteEnvironment ex)
{
//コードセグメント更新命令
byte value = 12;
var target = new CompareMove();
target.Destination =
new Immediate( ex.CodeSegmentAddress );
target.Source =
new Immediate( value );
//実行&結果チェック
Cpu cpu = Execute( ex, target );
if ( cpu.Addresser.CodeSegment.Value != value ) {
Console.WriteLine( "テスト失敗!:CSの値{0}",
cpu.Addresser.CodeSegment.Value );
}
}
//CPUをストップできるかテスト。
static void CpuStopTest(
ExecuteEnvironment ex )
{
//CPU停止命令
var target = new CompareMove();
target.Destination =
new Immediate( ex.LastAddress );
target.Source =
new Immediate( ex.OneAddress );
//実行&結果チェック
Cpu cpu = Execute( ex, target );
if ( !cpu.IsStop ) {
Console.WriteLine(
"テスト失敗!CPUが止まりません・・・" );
}
}
}
このテストをパスすればひとまずOKです。これでモジュールが作れる!といいたいところですが、まだ足りない要素があります。それは何でしょうか?次回を読むまでに考えてみてください。続く...