この記事は、
計算機の基本原理を味わおう25 - アセンブリコード合体!の続きです。前回は、ちょっと長いアセンブリコードを書く方法について解説しました。今回は、入れ子構造アセンブリコードについて解説します。
前回の最後に予告した通り、Equalオブジェクトを実装してみましょう。いくつもの方法がありますが、こんな具合の実装が長さ的に良いかと思います。
using System;
using System.Collections.Generic;
namespace MiniBitMachine.V3
{
//eql(等価性判定)命令
public sealed class Equal : IfCode
{
//対象アドレスとCPUの情報を元に、インスタンスを生成する。
public Equal( byte targetAddress, Cpu cpu )
: this( targetAddress, cpu, true )
{
}
//対象アドレスとCPUの情報を元に、インスタンスを生成する。
public Equal( byte targetAddress, Cpu cpu, bool addLast )
: base( "eql", targetAddress, cpu )
{
this.CreateInstruction( cpu, addLast );
}
//比較フラグ設定に関するコードを取得する。
protected override IEnumerable<CompareMove> GetCompareCodes( )
{
CompareMove[ ] result = new CompareMove[ 1 ];
result[ 0 ] = new CompareMove(
this.Info.CompareFlagAddress,
this.TargetAddress,
"実行フラグの場所に対象データを移動。" );
return result;
}
//比較対象が真の場合に実行するコードを取得する。
protected override IEnumerable<CompareMove> GetTrueCodes( )
{
CompareMove[ ] result = new CompareMove[ 1 ];
result[ 0 ] = new CompareMove(
this.TargetAddress,
this.Info.OneAddress,
"対象を1に設定。" );
return result;
}
//比較対象が偽の場合に実行するコードを取得する。
protected override IEnumerable<CompareMove> GetFalseCodes( )
{
CompareMove[ ] result = new CompareMove[ 1 ];
result[ 0 ] = new CompareMove(
this.TargetAddress,
this.Info.ZeroAddress,
"対象を0に設定。" );
return result;
}
//アドレスを表すインスタンスを取得する。
protected override string GetAddressString( )
{
System.Text.StringBuilder result =
new System.Text.StringBuilder();
result.Append( "対象アドレス:" );
result.Append( this.TargetAddress );
result.Append( Environment.NewLine );
return result.ToString();
}
//値を表すインスタンスを取得する。
protected override string GetValueString( )
{
System.Text.StringBuilder result =
new System.Text.StringBuilder();
result.Append( "対象値:" );
result.Append( this.TargetValue );
result.Append( Environment.NewLine );
return result.ToString();
}
//オブジェクトが持つ各種値を保存する。
internal override void SaveData( )
{
this.SaveValue = this.TargetValue;
}
//※リファクタリングにより名前を改名した
//オブジェクトが保持している値と、現在値を比べ、
//妥当な値ならばtrueを返す。
internal override bool ValidData( )
{
if ( this.SaveValue == this.TargetValue ) return true;
return false;
}
}
}
オブジェクト指向プログラミングをすると、非常に簡単なのが味わえます。でも、オブジェクト指向プログラミングの神髄はここからです。BitEqualは入れ子構造になっており、ちょっと複雑ですが、オブジェクトにしたら超簡単です。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MiniBitMachine.V3
{
//eqlb命令
public class EqualBit : IfCode
{
//比較対象のアドレス。
private byte _leftAddress;
public byte LeftAddress
{
get { return _leftAddress; }
}
//対象の値( 左の値:X )
public bool LeftValue
{
get
{
return this.Cpu.ReadMemoryData( this.LeftAddress );
}
}
private bool _saveLeftValue;
//対象アドレスとCPUの情報を元に、インスタンスを生成する。
public EqualBit( byte targetAddress, byte targetAddress1, Cpu cpu )
: this( targetAddress, targetAddress1, cpu, true )
{
}
//対象アドレスとCPUの情報を元に、インスタンスを生成する。
public EqualBit(
byte leftAddress, byte rightAddress, Cpu cpu, bool addLast )
: base( "eqlb", rightAddress, cpu )
{
this._leftAddress = leftAddress;
this.CreateInstruction( cpu, addLast );
this._if.ReLocation();
}
//比較フラグ設定に関するコードを取得する。
protected override IEnumerable<CompareMove> GetCompareCodes( )
{
CompareMove[ ] result = new CompareMove[ 1 ];
result[ 0 ] = new CompareMove(
this.Info.CompareFlagAddress,
this.TargetAddress,
"実行フラグの場所に対象データを移動。" );
return result;
}
//比較対象が真の場合に実行するコードを取得する。
protected override IEnumerable<CompareMove> GetTrueCodes( )
{
Equal result = new Equal( this.LeftAddress, this.Cpu, false );
return result.Compares;
}
//比較対象が偽の場合に実行するコードを取得する。
protected override IEnumerable<CompareMove> GetFalseCodes( )
{
Not result = new Not( this.LeftAddress, this.Cpu, true );
return result.Compares;
}
//アドレスを表すインスタンスを取得する。
protected override string GetAddressString( )
{
System.Text.StringBuilder result =
new System.Text.StringBuilder();
result.Append( "対象アドレスX:" );
result.Append( this.TargetAddress );
result.Append( Environment.NewLine );
result.Append( "対象アドレスY:" );
result.Append( this.LeftAddress );
result.Append( Environment.NewLine );
return result.ToString();
}
//値を表すインスタンスを取得する。
protected override string GetValueString( )
{
System.Text.StringBuilder result =
new System.Text.StringBuilder();
result.Append( "X:" );
result.Append( this.LeftValue );
result.Append( Environment.NewLine );
result.Append( "Y:" );
result.Append( this.TargetValue );
result.Append( Environment.NewLine );
return result.ToString();
}
//オブジェクトが持つ各種値を保存する。
internal override void SaveData( )
{
this._saveLeftValue = this.LeftValue;
this.SaveValue = this.TargetValue;
}
//オブジェクトが保持している値と、現在値を比べ、
//妥当な値ならばtrueを返す。
internal override bool ValidData( )
{
if ( this.SaveValue )
return this._saveLeftValue == this.LeftValue;
else
return this._saveLeftValue != this.LeftValue;
}
}
}
オブジェクトを再利用すれば、ほとんどないのと同じぐらい簡単です。入れ子になったアセンブリコードはちょっと書きにくいですが、オブジェクトだとそんな細部に煩わせられることはありません。最後にこのオブジェクトを使うだけです。
using System;
using System.Diagnostics;
using System.Linq;
using MiniBitMachine;
using MiniBitMachine.V3;
using MiniBitMachine.V3.Utility;
class Sample
{
//命令をテストする。
private static ExecutionResult Execute(
IfCode code,
Cpu cpu,
byte targetAddress,
bool targetValue )
{
cpu.Reset();
cpu.WriteMemoryData( targetAddress, targetValue );
code.SaveData();
Console.WriteLine( code.Name + "命令を実行します・・・" );
Console.WriteLine( "---------- 実行前 ---------- " );
Console.WriteLine( code.ToString( "v" ) );
ExecutionResult result = cpu.AllRun();
Console.WriteLine( "---------- 実行後 ---------- " );
Console.WriteLine( code.ToString( "v" ) );
Console.WriteLine( "---------- 状態 ---------- " );
Console.WriteLine( result );
Debug.Assert( code.ValidData() );
return result;
}
//テストプログラムの開始部分
private static void TestMain(
Cpu cpu,
IfCode code,
byte targetAddress )
{
code.SetBinary();
Console.WriteLine(
"********** " + code.Name + "命令の内容 **********" );
Console.WriteLine( Cpu.GetEnvironment() );
Console.Write( cpu );
Console.Write( code.ToString( "a" ) );
Console.WriteLine( );
Console.Write( code.ToString( "d" ) );
Console.WriteLine( );
ExecutionResult result;
result = Execute( code, cpu, targetAddress, false );
result += Execute( code, cpu, targetAddress, true );
Console.WriteLine( "★★★★★ 総計 ★★★★★" );
Console.WriteLine( result );
}
//eqlb命令のテストを行う。
private static void BitEqualTest( Memory m )
{
byte ds = 0;
byte cs = 1;
Cpu cpu = new Cpu( ds, cs, m );
byte targetAddress = 3;
byte targetAddress1 = 4;
EqualBit eql = new EqualBit(
targetAddress,
targetAddress1,
cpu );
cpu.WriteMemoryData( targetAddress1, false );
TestMain( cpu, eql, targetAddress );
cpu.WriteMemoryData( targetAddress1, true );
TestMain( cpu, eql, targetAddress );
Console.WriteLine();
}
static void Main()
{
Memory m = new Memory();
BitEqualTest( m ); //この命令は長すぎて正常に実行できない
Console.ReadLine();
}
}
今回は楽勝ですね・・・と、言いたいところですが、最小計算機の仕様により無限ループします。いったいなぜなんでしょうか?!謎をはらみつつ次回へ続く。
テーマ : プログラミング
ジャンル : コンピュータ