RegisterValueChangedEventArgsクラスの定義
イベント駆動型プログラミングでは、何の情報が発生するのかを考えることから始めます。今回の目的は「レジスタの変更を自動的に知りたい」ですから、レジスタの値が変更される時、何の情報が必要になるかを考えます。
筆者が必要だと考えたのは、値が変更されたレジスタの「種類」「名前」「ビット数」「変更後の数値」の4個でした。これらの情報を扱えるようにRegisterValueChangedEventArgsクラスを定義しました。
コードは次のとおりです。
Public Class RegisterValueChangedEventArgs Inherits EventArgs '更新を容易にするための情報 Private m_gruop As RegisterGruop Public Property Gruop() As RegisterGruop Get Return Me.m_gruop End Get Set(ByVal value As RegisterGruop) Me.m_gruop = value End Set End Property 'どのレジスタの値が変更されたのか表します Private m_name As RegisterName Public Property Name() Get Return m_name End Get Set(ByVal value) m_name = value End Set End Property '現在どのビット数の値を扱っているのかわかった方がいいです Private m_bit As Byte Public Property BitCount() As Byte Get Return m_bit End Get Set(ByVal value As Byte) m_bit = value End Set End Property 'レジスタが入れ子状態になっているので必要です Private m_val As Register Public Property Value() As Register Get Return m_val End Get Set(ByVal value As Register) m_val = value End Set End Property '値が変更されたレジスタの名前、レジスタのビット数、 '値を指定してインスタンスを生成します。 Sub New(ByVal group As RegisterGruop, ByVal target As RegisterName, _ ByVal bitCount As Byte, ByVal value As Register) Me.m_gruop = group Me.m_name = target Me.m_bit = bitCount Me.m_val = value End Sub End Class 'レジスタを表す列挙体 Public Enum RegisterName EAX EBX ECX EDX AX BX CX DX AH AL BH BL CH CL DH DL End Enum 'レジスタの種類 Public Enum RegisterGruop A B C D End Enum
RegisterValueChangedEventArgsの定義を解説します。GruopプロパティとBitCountプロパティについては実際に使っているところを見た方が早いので解説を後にし、RegisterNameプロパティとValueプロパティを解説します。
RegisterNameプロパティがなぜString型でないのか疑問に思う方は多いでしょう。ですが実のところ、この方がミスが少なくて効率がいいのです。それはミスタイプが減らせるからです。文字列ではEAXをXXAなどと打ち間違ってもコンパイルできてしまいます。一方、列挙体ではエラーが発生するので、間違ったことがすぐ分かり大変便利です。それに加えて、開発環境のコード補完機能を利用している人は入力の効率がよくなる上にミスもなくなります。このような場面では、列挙体は便利なので大いに活用しましょう。
ValueプロパティについてもUInteger、UShort、Byteなどの値型ではなくてレジスタ構造体であることが気になると思います。なぜこのように実装したのかと言うとレジスタが入れ子状態になっているからです。前回のテストドライバを実行して、1つのレジスタの値を変更したら複数の値が更新されたはずです。ですから、単純な値型では実現できないのでレジスタ構造体で値を指定するように実装しました。
これでRegisterValueChangedEventArgsクラスの定義を終えたので実際に使います。実装する場所はIntelCpuクラス内です。
イベント発動
まずはIntelCpuクラスにイベントを定義します。
'イベント Public Event RegisterValueChanged(ByVal sender As Object, _ ByVal e As RegisterValueChangedEventArgs) 'レジスタの値が変更されたことを通知する Private Sub OnRegisterValueChanged(ByVal gruop As RegisterGruop, _ ByVal name As RegisterName, _ ByVal bitCount As Byte, ByVal value As Register) Dim e As RegisterValueChangedEventArgs = _ New RegisterValueChangedEventArgs(gruop, name, bitCount, value) RaiseEvent RegisterValueChanged(Me, e) End Sub
これでレジスタの値が変更されたことを外部に通知できます。
次にイベント発生場所を決めて、そこでOnRegisterValueChangedメソッドを使用します。
Private m_eax As Register Public Property EAX() As UInteger Get Return Me.m_eax.ValueU32 End Get Set(ByVal value As UInteger) Me.m_eax.ValueU32 = value Me.OnRegisterValueChanged(RegisterGruop.A, _ RegisterName.EAX, 32, Me.m_eax) End Set End Property Public Property AX() As UShort Get Return Me.m_eax.ValueU16 End Get Set(ByVal value As UShort) Me.m_eax.ValueU16 = value Me.OnRegisterValueChanged(RegisterGruop.A, _ RegisterName.AX, 16, Me.m_eax) End Set End Property Public Property AH() As Byte Get Return Me.m_eax.ValueHighByte End Get Set(ByVal value As Byte) Me.m_eax.ValueHighByte = value Me.OnRegisterValueChanged(RegisterGruop.A, _ RegisterName.AH, 8, Me.m_eax) End Set End Property Public Property AL() As Byte Get Return Me.m_eax.ValueLowByte End Get Set(ByVal value As Byte) Me.m_eax.ValueLowByte = value Me.OnRegisterValueChanged(RegisterGruop.A, _ RegisterName.AL, 8, Me.m_eax) End Set End Property
このコードを参考に、すべての他のレジスタ関連のプロパティを実装してください(具体的な実装例は添付のサンプルコードを参照のこと)。これでIntelCpuクラスを使用しているオブジェクトに通知をすることができます。後は受け取る側の準備をするだけです。
