DIV命令実装と実行
これでしばらくDIV命令の実装を行えます。これから筆者のDIV命令の実装例を掲載しますが、読者の方も「DIV命令の説明」の項をよく読んで実装してみてください。
'DIV命令の実装 Private Sub Div(ByVal ope As OpeCode) Select Case ope.BitCount Case 8 '値を先に用意 Dim value As UShort If ope.IsHi Then value = ope.Value.ValueHighByte Else value = ope.Value.ValueLowByte End If '剰余を算出 Dim remainder As UShort = Me.AX Mod value '商を算出 Dim tmp As UShort = Me.AX - remainder Dim quotient As UShort = tmp / value '結果をレジスタに反映する Me.AH = CByte(remainder) Me.AL = CByte(quotient) Case 32 '値を先に用意 Dim value As UInteger = ope.Value.ValueU32 value1 = value '剰余を算出 Dim remainder As UInteger = Me.EAX Mod value '商を算出 Dim tmp As UInteger = Me.AX - remainder Dim quotient As UInteger = tmp / value '結果をレジスタに反映する Me.EDX = CByte(remainder) Me.EAX = CByte(quotient) End Select End Sub '解析済みの命令を実行します。 Public Sub ExecuteCommand() Dim cmd As OpeCode While cmds.Count <> 0 cmd = cmds.Dequeue() Select Case cmd.Name '命令を選択して実行 Case CommandName.Add Add(cmd) Case CommandName.Subtract Subtract(cmd) Case CommandName.Inc Inc(cmd) Case CommandName.Dec Dec(cmd) Case CommandName.Mov Mov(cmd) Case CommandName.Mul Mul(cmd) Case CommandName.Div Div(cmd) End Select End While End Sub
ではコードの説明をします。DIV命令の実装ポイントは、「商と剰余を別のレジスタに保存する」点と「計算順序」です。その点さえ気をつければ、今までの命令よりも実装は容易だと思います。
計算順序は剰余を計算してから商を計算します。その理由は端数処理が簡単になるからです。あらかじめ剰余を計算しておき、その値を引いておくと必ず割り切れますので、端数をどうするのかを気にする必要がなくなります。このようにしておかないと、端数を四捨五入するのか、切り捨てるのか……などのルールを決めなくてはなりません。そのような処理は、CPUではなくアプリケーション側でするべきことなので、端数処理をしないですむように考慮して実装しました。
以上でIntelCpuオブジェクト視点の修正は終わりです。後はオブジェクトを利用側の視点で修正を行っていきます。
バイナリ取得機能の修正
ここまでの説明で、IntelCpu
オブジェクトの内部ではDIV命令が実行できるようになりました。しかしそれでは、IntelCpu
オブジェクトを利用する側がDIV命令を扱えませんので、GetBinary
メソッドを修正しなければなりません。まずは実装例を掲載し、すぐ後で説明を行います。
'指定された命令と対象レジスタから指定するべきバイナリを導出する Public Shared Function GetBinary(ByVal cmd As CommandName, ByVal reg As RegisterName) As Byte Dim result As Byte = 0 'オペコードマップの行と列の値を決定 Select Case cmd Case CommandName.Mul, CommandName.Div result = 15 << 4 If reg >= RegisterName.EAX And reg <= RegisterName.EDX Then result += 7 ElseIf reg >= RegisterName.AH And reg <= RegisterName.DL Then result += 6 Else Throw New ArgumentException("命令" & cmd.ToString() & "は" & _ reg.ToString() & "レジスタをサポートしておりません。") End If End Select '結果を返す Return result End Function
Case
部分でMUL命令とDIV命令に同じバイナリを返しています。理由はDIV命令の概要の項で述べたように、MUL命令とDIV命令では同じバイナリ値だからです。もちろんこれでは困りますので、使用側がModR/Mのバイナリを操作して両命令を分けて指定するようにします。その実装方法について事項で述べます。
命令の使い分け
命令を使い分けるにはCmdExeButton_Click
を修正する必要があります。このメソッドは次の掲載例のように修正します。
'指定された機械語を実行する Private Sub CmdExeButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CmdExeButton.Click Dim binary(1) As Byte '指定された命令を実行する If Me.GetCmmandAndRegisterBinay(binary) = False Then Return End If '命令長を取得 Dim length As Integer = IntelCpu.GetCommandLength(Me.CmdCombo.SelectedIndex) '命令長が1の場合は値に関するバイナリは必要ない If length > 1 Then If Me.CmdCombo.SelectedIndex = CommandName.Mul Or Me.CmdCombo.SelectedIndex = CommandName.Div Then 'ModR/Mのバイナリを取得する binary(1) = IntelCpu.GetModRMBinary(Me.CmdCombo.SelectedIndex, _ CType(Me.RegisterCombo.SelectedIndex, RegisterName)) Me.ExecuteCommand(binary) Else If Me.GetValueBinary(binary) = True Then Me.ExecuteCommand(binary) End If End If Else Me.ExecuteCommand(binary) End If End Sub
ポイントは
If Me.CmdCombo.SelectedIndex = CommandName.Mul Or Me.CmdCombo.SelectedIndex = CommandName.Div Then 'ModR/Mのバイナリを取得する binary(1) = IntelCpu.GetModRMBinary(Me.CmdCombo.SelectedIndex, _ CType(Me.RegisterCombo.SelectedIndex, RegisterName)) Me.ExecuteCommand(binary)
の部分です。今までの作業によりIntelCpu
オブジェクトのGetModRMBinary
メソッドでDIV命令に対処済みなので、呼ぶだけでDIV命令のバイナリを得られます。
後はCmdCombo
コントロールの最後にDIVを追加するだけです。これでDIV命令の実行のテストができます。