よーし今回は早速メモリをどう実装するか考えるぞ。
インドリィちゃん「その前に8ビットレジスタのテスト。」
インドリ「えぇ〜盛り上がってたのに。間違っていないよ。」
インドリィちゃん「テ・ス・ト」
public void RegisterTo8BitTest( ) {
//テスト用変数を準備
byte val;
ControlProcessingUnit target = new ControlProcessingUnit( );
Random dom = new Random( ( int ) DateTime.Now.Ticks );
Type type = target.GetType( );
//リフレクション用変数を準備
string[ ] names = new string[ ] { "AH", "AL", "BH", "BL",
"CH", "CL", "DH", "DL"};
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.SetProperty | BindingFlags.GetProperty |
BindingFlags.DeclaredOnly | BindingFlags.Instance;
foreach ( string name in names ) {
//テストするレジスタ用プロパティを取得
PropertyInfo reg16 = type.GetProperty(
name.Substring( 0, 1 ) + 'X', flags );
Assert.IsNotNull( reg16, "E" + name + "レジスタが足りないぃ〜。" );
PropertyInfo reg8 = type.GetProperty( name, flags );
//値の取得&設定テスト
Assert.IsNotNull( reg8, name + "レジスタが足りないぃ〜。" );
for ( int i = 0; i < 100; i++ ) {
//共有フィールドを汚しておく
reg16.SetValue( target,
( ushort ) dom.Next( 0, short.MaxValue ), null );
//8ビットレジスタのテスト
val = ( byte ) dom.Next( byte.MinValue, byte.MaxValue );
reg8.SetValue( target, val, null );
Assert.AreEqual( val, reg8.GetValue( target, null ),
name + "に「" + val.ToString( ) +
"」を入れたら引っかかった。by ドリィ" );
}
//上下限チェック
val = byte.MinValue;
reg8.SetValue( target, val, null );
Assert.AreEqual( val, reg8.GetValue( target, null ),
name + "の上限チェック失敗。by ドリィ" );
val = byte.MaxValue;
reg8.SetValue( target, val, null );
Assert.AreEqual( val, reg8.GetValue( target, null ),
name + "下限チェック失敗。by ドリィ" );
}
}
インドリ「う〜なんか難しそう・・・あれ?よく見ると内容は16ビットの時と変わらないピヨ。多分大丈夫だ。」
インドリィちゃん「ふふ、それはどうかしら?」
インドリ「(やけに自信ありそうだな)よしテスト実行」
インドリ「あれ?!!!テスト失敗したぞ。何で?何で?・・・えーとAHが間違っているらしい。」
インドリィちゃん「ふふ、やっぱりね。」
インドリ「くぅ〜なんか悔しいぃ〜。」
インドリは長時間調べてようやく間違いを発見した。
ALレジスタはAXレジスタの先頭8ビットを返すが、
インドリは16ビットの時と同じロジックを使いまわししたので間違っていたのだ。
その間違いに気付いたインドリは数十分をかけてようやくAHレジスタの処理を修正した。
public byte AH {
get {
byte val = ( byte ) ( ( this.eax & 0x0000FF00 ) >> 8 );
return val;
}
set {
uint tmp = this.eax & 0xFFFF00FF;
ushort hi = (ushort)(value << 8);
this.value = tmp + hi;
}
}
インドリ「ふぅー要約修正したピヨ。でもこれで大丈夫。」
インドリィちゃん「ふふふふ、コピペしている貴方がテストを通過できるかしら?」
インドリ「あれぇ?また違うレジスタで間違った。」
インドリィちゃん「同じロジックを共通化しないから。ふふふふ」
インドリ「あっそうか?」
インドリは大きな過ちを犯していることにようやく気付いた。
プログラムはコピペ(コピーと貼り付け)使いのインドリがおかした間違いを防ぐために、なるべく共通部分は纏めなくてはならないのだ。
その間違いに気付いたインドリは、
レジスタ構造体を作って、
CPUオブジェクトの各レジスタ用プロパティが使用するフィールドを、
uintからRegister構造体へ変えた。
ドリィちゃんの突っ込みを受けながらインドリが一生懸命書いたRegister構造体をみんなも見てみよう。
public struct Register
{
private uint value;
public uint ValueU32 {
get { return this.value; }
set { this.value = value; }
}
public ushort ValueU16 {
get {
ushort val = ( ushort ) ( this.value & 0x0000FFFF );
return val;
}
set {
uint tmp = this.value & 0xFFFF0000;
this.value = tmp + value;
}
}
public byte ValueHighByte {
get {
//16ビットの中の先頭8ビットを取り出す
byte val = ( byte ) ( ( this.value & 0x0000FF00 ) >> 8 );
return val;
}
set {
//16ビットの中の先頭8ビットをリセットしてから足す
uint tmp = this.value & 0xFFFF00FF;
ushort hi = ( ushort ) ( value << 8 );
this.value = tmp + hi;
}
}
public byte ValueLowByte {
get {
byte val = ( byte ) ( this.value & 0x000000FF );
return val;
}
set {
uint tmp = this.value & 0xFFFFFF00;
this.value = tmp + value;
}
}
}
CPUオブジェクトのプロパティは次のように変更した。
private Register eax;
public uint EAX {
get { return this.eax.ValueU32; }
set { this.eax.ValueU32 = value; }
}
public ushort AX {
get { return this.eax.ValueU16; }
set { this.eax.ValueU16 = value; }
}
public byte AH {
get { return this.eax.ValueHighByte; }
set { this.eax.ValueHighByte = value; }
}
public byte AL {
get { return this.eax.ValueLowByte; }
set { this.eax.ValueLowByte = value; }
}
こうしておけばつまらないミスも防げるし、将来64ビットレジスタを作成する際
既存のコードをあまり修正しなくて済む。インドリの間違いは酷いけど、
人間は誰しも完璧には作れないからこうやってリファクタリングをするのが大事だよ。
メモリの実装を紹介出来ないのは残念だけど今回はおしまい。
インドリィちゃん「ふふふふ、リファクタリング♪リファクタリング♪」