ModR/Mのバイナリを扱う方法
MUL命令は幸いほとんどが固定値なので、ModR/Mの操作方法は簡単です。MUL命令はModが11、レジスタ/オペコードが100なので、後はR/Mだけを導出すればよいのです。R/Mにはソースオペランドとなるレジスタを表す値を設定します。
このレジスタの値は必要なビット毎に分かれています。まず8ビット(バイト)のレジスタは、AL・CL・DL・BL・AH・CH・DH・BHの順に0~7までの値が割り振られています。次に32ビット(ダブルワード)のレジスタはEAX・ECX・EDX・EBXの順に0~3までの値が割り振られています。
この記事では、16ビットのレジスタは32ビットCPUではMUL命令で使用できないので省略しています。また、32ビットのレジスタについても全てのレジスタを書いているわけではありません。注意してください。
以上を踏まえて、レジスタの名前から数値を返すメソッドと、その逆に数値からレジスタ名を割り出すメソッドを実装します。
'指定した8ビットレジスタに対応する値を返す Public Shared Function GetRegisterByteModRMValue(ByVal reg As RegisterName) As Byte Select Case reg Case RegisterName.AL Return 0 Case RegisterName.CL Return 1 Case RegisterName.DL Return 2 Case RegisterName.BL Return 3 Case RegisterName.AH Return 4 Case RegisterName.CH Return 5 Case RegisterName.DH Return 6 Case RegisterName.BH Return 7 Case Else Throw New ArgumentOutOfRangeException("このメソッドは" & reg & "には対応していません。") End Select End Function '指定した32ビットレジスタに対応する値を返す Public Shared Function GetRegister32ModRMValue(ByVal reg As RegisterName) As UInteger Select Case reg Case RegisterName.EAX Return 0 Case RegisterName.ECX Return 1 Case RegisterName.EDX Return 2 Case RegisterName.EBX Return 3 Case Else Throw New ArgumentOutOfRangeException("このメソッドは" & reg & "には対応していません。") End Select End Function 'ModR/Mで指定された1バイトレジスタの名称を判別する Private Shared Function GetRegister8Name(ByVal value As Byte) As RegisterName Select Case value Case 0 Return RegisterName.AL Case 1 Return RegisterName.CL Case 2 Return RegisterName.DL Case 3 Return RegisterName.BL Case 4 Return RegisterName.AH Case 5 Return RegisterName.CH Case 6 Return RegisterName.DH Case 7 Return RegisterName.BH Case Else Throw New ArgumentOutOfRangeException("必ず0以上7以下の値を指定して下さい。") End Select End Function 'ModR/Mで指定されたダブルワードレジスタの名称を判別する Private Shared Function GetRegister32Name(ByVal value As Byte) As RegisterName Select Case value Case 0 Return RegisterName.EAX Case 1 Return RegisterName.ECX Case 2 Return RegisterName.EDX Case 3 Return RegisterName.EBX Case Else Throw New ArgumentOutOfRangeException("必ず0以上3以下の値を指定して下さい。") End Select End Function
これらメソッドの実装内容は説明不要だと思いますので省略します。これでR/Mの設定を行えるようになりましたので、ModR/Mのバイナリを導出するメソッドを実装できます。これから筆者の実装例を提示しますが、皆さんも一度自分で実装してみてください。
'ModR/Mのバイナリを取得する Public Shared Function GetModRMBinary(ByVal cmd As CommandName, ByVal reg As RegisterName) As Byte Dim result As ModRM = New ModRM(0) result.ModValue = 3 Select Case cmd Case CommandName.Mul result.RegOrOpeValue = 4 Dim bit As Byte = GetRegisterBit(reg) If bit = 8 Then result.RMValue = GetRegisterByteModRMValue(reg) ElseIf bit = 32 Then result.RMValue = GetRegister32ModRMValue(reg) Else Throw New ArgumentOutOfRangeException("MULでは16ビットのレジスタを指定できません。") End If Case Else Throw New ArgumentOutOfRangeException(cmd & "はサポートしていません。") End Select Return result.Binary End Function '指定したレジスタが対応しているビット数を返す。 Public Shared Function GetRegisterBit(ByVal reg As RegisterName) As Byte If reg >= RegisterName.EAX And reg <= RegisterName.EDX Then Return 32 ElseIf reg >= RegisterName.AX And reg <= RegisterName.DX Then Return 16 Else Return 8 End If End Function
ではプログラムの説明をします。今回はメモリを扱わないのでModValue
(Mod部)は3で固定、MUL命令は先ほど説明したようにレジスタ/オペコード部が100で固定なのでRegOrOpeValue
プロパティに4を設定しています。後は指定されたレジスタのビット数をGetRegisterBit
メソッドで取得し、先ほど実装したレジスタに対応する数値を返すメソッドを使って、RMValue
(R/M)プロパティに値を設定するだけです。これで基本的なパーツが実装できましたので、次項から順を追って実装していきます。