テストドライバ側の変更
まずはMoveDataButton_Click
メソッドの内容を三分割して、他の機械語命令を指定しやすくします。
'指定された命令と対象レジスタから必要なバイナリ決定しその値を返す Private Function GetCmmandAndRegisterBinay(ByRef binary() As Byte) As Boolean Try binary(0) = IntelCpu.GetBinary(CmdCombo.SelectedIndex, RegisterCombo.SelectedIndex) Catch ae As ArgumentException MessageBox.Show(ae.Message, _ "エラー", _ MessageBoxButtons.OK, MessageBoxIcon.Error) Return False End Try Return True End Function 'レジスタのビット数に応じてコピーする値を用意する Private Function GetValueBinary(ByRef binary() As Byte) As Boolean Dim regName As RegisterName = RegisterCombo.SelectedIndex 'レジスタのビット数に応じてコピーする値を用意する If regName < RegisterName.AH Then ReDim Preserve binary(4) Dim value As UInteger 'エラーチェック If regName < RegisterName.AX Then If UInteger.TryParse(ValueText.Text, value) = False Then MessageBox.Show("0~" & UInteger.MaxValue.ToString() & "までの数値を入力してください。", _ "入力エラー", _ MessageBoxButtons.OK, _ MessageBoxIcon.Error) ValueText.Focus() Return False End If Else If UShort.TryParse(ValueText.Text, value) = False Then MessageBox.Show("0~" & UShort.MaxValue.ToString() & "までの数値を入力してください。", _ "入力エラー", _ MessageBoxButtons.OK, _ MessageBoxIcon.Error) ValueText.Focus() Return False End If End If '値を分解して保存 Dim values As Byte() = IntelCpu.DivisionValue(value) values = IntelCpu.GetLittleEndianValue(values) For i As Integer = 0 To 3 binary(i + 1) = values(i) Next Else '1バイト(8ビット)レジスタの場合 Dim value As Byte 'エラーチェック If Byte.TryParse(ValueText.Text, value) = True Then binary(1) = value Else MessageBox.Show("0~" & Byte.MaxValue.ToString() & "までの数値を入力してください。", _ "入力エラー", _ MessageBoxButtons.OK, _ MessageBoxIcon.Error) ValueText.Focus() Return False End If End If Return True End Function '指定された命令を実行する Private Sub ExecuteCommand(ByVal binary() As Byte) Me.cpu.ReadBinary(binary) Me.cpu.AnalyzeBinary() Me.cpu.ExecuteCommand() End Sub
処理そのものは前回とほとんど変わりませんので説明は割愛します。
次に、画面構成そのものを下記の図のように変えます。
各コントロールのプロパティ値は、本稿に添付してあるサンプルプログラムを参考にしてください。
ここからテストドライバの本格的な改造が始まります。以前はMOVE命令専用だったコマンドボタンの名前を「CmdExeButton」に変え、そのクリック命令を変更します。コードは次のとおりです。
'指定された機械語を実行する 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 If Me.GetValueBinary(binary) = True Then Me.ExecuteCommand(binary) '情報欄を更新する Select Case CmdCombo.SelectedIndex Case CommandName.Mov If RegisterCombo.SelectedIndex = RegisterName.EAX Then ResultLabel.Text = "値が" & EaxValueLabel.Text & "に初期化されました。" EaxValueLabel.Tag = EaxValueLabel.Text AlValueLabel.Tag = Me.before.ValueLowByte.ToString() ElseIf RegisterCombo.SelectedIndex = RegisterName.AL Then ResultLabel.Text = "値が" & AlValueLabel.Text & "に初期化されました。" AlValueLabel.Tag = AlValueLabel.Text End If Case CommandName.Add If RegisterCombo.SelectedIndex = RegisterName.EAX Then ResultLabel.Text = EaxValueLabel.Tag.ToString() & _ " + " & _ UInteger.Parse(ValueText.Text).ToString("N", Me.format) & _ " = " & EaxValueLabel.Text EaxValueLabel.Tag = EaxValueLabel.Text AlValueLabel.Tag = Me.before.ValueLowByte.ToString() ElseIf RegisterCombo.SelectedIndex = RegisterName.AL Then ResultLabel.Text = AlValueLabel.Tag.ToString() & _ " + " & _ UInteger.Parse(ValueText.Text).ToString() & _ " = " & AlValueLabel.Text AlValueLabel.Tag = AlValueLabel.Text End If End Select End If End Sub
コードの量は多いのですが、簡単なことしかしていません。目的は機械語を実行することですから、GetCmmandAndRegisterBinay
メソッドとGetValueBinary
メソッドの2つを、例外が起こっていないか確認しながらExecuteCommand
メソッドを実行しているだけです。
あとは、レジスタだけでは計算結果が分かりにくいので「情報欄」を追加し、そこに
「以前の値」+「指定した値」=計算結果
を表示しているだけです。ただし、以前の値を保存する方法が少し難しいので説明しておきます。以前の値を保持する場所は「対応するラベルのTag
プロパティ」です。まずは初期値から保存しています。
Private Sub TestForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 'レジスタの初期値を保存 EaxValueLabel.Tag = EaxValueLabel.Text AlValueLabel.Tag = Me.before.ValueLowByte.ToString() End Sub
さらに、各命令でも保存しています。
'指定された機械語を実行する Private Sub CmdExeButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CmdExeButton.Click '情報欄を更新する Select Case CmdCombo.SelectedIndex Case CommandName.Mov If RegisterCombo.SelectedIndex = RegisterName.EAX Then ResultLabel.Text = "値が" & EaxValueLabel.Text & "に初期化されました。" EaxValueLabel.Tag = EaxValueLabel.Text AlValueLabel.Tag = Me.before.ValueLowByte.ToString() ElseIf RegisterCombo.SelectedIndex = RegisterName.AL Then ResultLabel.Text = "値が" & AlValueLabel.Text & "に初期化されました。" AlValueLabel.Tag = AlValueLabel.Text '←この部分 End If End Sub
これだけでは不十分なので、レジスタの値が変更されたタイミングでも保存しています。
Private Sub RegisterValueChanged(ByVal sender As Object, ByVal e As RegisterValueChangedEventArgs) '以前の値を保持する変数へ最新値を保存 Me.before = New Register(e.Value.ValueU32) End Sub
この処理はレジスタの最新値がCmdExeButton_Click
メソッド実行後は「過去の値」になるのことを利用しています。このように保存しておけば、今度CmdExeButton_Click
メソッド内の処理で適切なラベルのTag
プロパティで以前の値として保存できます。
これでテストドライバの変更作業も終わりです。実際に動かしてCPUの動きを目で確認してください。例えば、ALレジスタの値が255を超えた場合どうなるのかを試してください。その際、ADD命令は「ALレジスタとEAXレジスタしか指定できない」ことに注意してください。
まとめ
いかがでしょうか? 筆者としては機械語を扱っているという感覚がどんどん強まっています。読者の方々にも同じ感覚を味わってもらえれば幸いです。
今回は、仮想CPUであるという利点を強調するために、情報を加工してわかりやすく表示する方法を示しました。これは現実のCPUには出来ない芸当です。仮想としての利点は色々ありますが、情報が柔軟に加工出来るのが利点です。その利点を利用して、CPUを理解する一助になればと願っています。
次回はSUB命令を実装する予定です。おたのしみに。