目次次へ番外編 Chap.2 命令語インタプリタをつくる(2)

(2010/12/26 本文中のVisual Basicプログラムは、VB5.0のものです。VB.NETとは一部異なる部分があります。)

前回は、ラダーに必要なリレー、スイッチ等の部品の代わりに、命令語インタプリタを使うということで命令語の仕様を決定しました。
今回は、この命令語インタプリタをVisual Basicで実際に作ってみます。

2.1 命令語インタプリタの仕様
次の仕様の命令語インタプリタを作ることにします。

(1)命令語の仕様
Chap.1の表1−1 命令語インタプリタ命令セット(最小仕様)の通りです。
ただし、できるだけ処理を簡単にするため、オペコード(命令)とオペランド(I/O番号)は2行に分けて記述します。

例1

LD

AND

OUT
80

また、プログラムを見やすくするため、コメント行を定義します。
行の先頭2文字が”/*”という文字列になっているとき、命令語インタプリタはこの行をコメントとみなし無視します

例2

/* この1行はコメント行です */
LD

AND

OUT
80

空白行も命令語インタプリタは無視します。

(2)I/O番号
16進数で0からFFの256点のI/O番号とします。各I/Oは1ビットのデータを記憶できます。

(3)命令語プログラムファイル
命令語プログラムは、テキストファイルです。命令語インタプリタ自身に編集機能はありませんので、Windowsのメモ帳などのテキストエディタで作成することにします。

2.2 機能
命令語インタプリタの機能についてみてみましょう。外観は図2.1の通りです。

図2.1 命令語インタプリタの外観


ここで、2つの表示領域がありますが、左側には現在のI/Oデータを表示し、右側には命令語プログラムリストを表示します。
ボタンは、4つあり、各ボタンをクリックしたときの動作は表2.1の通りとなります。

表2.1 各ボタンをクリックしたときの動作
ボタン名 機能
ファイル読み込み 命令語プログラムのファイル名を入力するウィンドが開きます。
ファイル名を入力すると、プログラムをインタプリタ内部に読み込みます。
スキャン実行 命令語プログラムを繰り返し実行します。
スキャン停止 命令語プログラムの実行を停止します。
データ入力 I/O番号と書込データを入力するウィンドウが開きます。
I/O番号と書込データを入力すると、指定したI/O番号のメモリにデータを書き込みます。


2.3 プログラムの作成
では、Visual Basicでこのプログラムを作成してみましょう。

(1)コントロールの配置
まず、新規にプロジェクトを作成し、フォームに図2.2のようにコントロールを配置します。
表示領域には、リストボックスを使っています。また、スキャン実行のタイミングを得るためにタイマコントロールを使っています。

図2.2 コントロールの配置


(2)変数の定義
次に、このフォームにコードを記述します。
まず、表2.2のように変数を定義します。これらの変数は、フォーム内のどのプロシージャ・ファンクションからも見えるようにします。
具体的には、プログラムの先頭で、Dim Cmd As Stringというようにして、各変数を定義してゆきます。

表2.2 変数一覧

(3)各ボタンのイベント処理の記述
次に、各ボタンをクリックしたときの処理を記述します。
表2.3に処理内容を示します。この表を見てわかるように、スキャン実行、スキャン停止、データ入力では変数にデータをセットするだけで、実際の処理はタイマイベントでおこないます。

表2.3 各ボタンのイベント処理
イベント 処理
「ファイル読み込み」をクリック 命令語ファイル名を入力するウィンドウを開き、このウィンドウで指定された命令語ファイルを読み込み、1行ずつ配列変数Prgに代入し、最終ポインタの次をLastPrgに代入します。
「スキャン実行」をクリック 変数RunFlgにTrueを代入します。
「スキャン停止」をクリック 変数RunFlgにFalseを代入します。
「データ入力」をクリック I/O番号と書込データを入力するウィンドウを開き、入力された文字列を変数Cmdに代入します。


(4)タイマイベント処理の記述
命令語インタプリタの主要部分は、このタイマイベント処理です。
このタイマイベントの発生時間間隔は100msで、これはフォームロード時にフォームロードイベントで値を設定します。
タイマイベントが発生するたびに、次の処理1〜処理3を行ないます。

処理1
変数Cmdをみて、もし文字列データがあれば、これをI/O番号と書き込みデータと見做し、指定されたI/O番号のメモリに指定されたデータを書き込みます。

処理2
変数RunFlgをみて、もしTrueであれば、配列Prgを順に読み出し、命令語を解析し、命令語の動作仕様に基づき実行します。

処理3
I/Oメモリの内容を表示領域に表示します。

以上が、命令語インタプリタの処理の概要です。コメントを除けば、プログラムは100行以内でできるはずです。


2.4 プログラムリスト

参考のために、プログラムリストを掲載します。

---------- リストここから ----------
Option Explicit

'変数定義

Dim Prg(1023) As String '命令語プログラム
Dim LastPrg As Integer '命令語プログラム最終ポインタ+1
Dim RunFlg As Boolean 'スキャン実行許可フラグ
Dim m(&HFF) As Boolean 'I/Oメモリ
Dim Reg As Integer '導通状態レジスタ
Dim Cmd As String 'I/Oメモリの変更指示(文字列)
Dim FileName As String '命令語プログラムファイル名

'名称:フォームロード処理
'機能:起動時の初期設定


Private Sub Form_Load()
frmMain.Caption = "番外編 - 命令語インタプリタ V1.0"
lstMon.Font = "MS ゴシック"
lstPrg.Font = "MS ゴシック"
Timer1.Interval = 100 '100ms毎にタイマイベント発生
End Sub

'名称:「ファイル読み込み」ボタン処理
'機能:命令語プログラムを読み込み、配列に格納する


Private Sub cmdFile_Click()
LastPrg = 0
Open InputBox("命令語ファイル名を入力してください。") For Input As #1
lstPrg.Clear
Do While Not EOF(1)
Line Input #1, Prg(LastPrg)
lstPrg.AddItem Prg(LastPrg)
LastPrg = LastPrg + 1
Loop
Close #1
End Sub

'名称:「スキャン実行」ボタン処理
'機能:スキャン実行許可フラグをONする(実際のスキャン実行は、タイマ処理でおこなう)


Private Sub cmdScanRun_Click()
RunFlg = True 'スキャン実行許可フラグをON
End Sub

'名称:「スキャン停止」ボタン処理
'機能:スキャン実行許可フラグをOFFする


Private Sub cmdScanStop_Click()
RunFlg = False 'スキャン実行許可フラグをOFF
End Sub

'名称:「データ入力」ボタン処理
'機能:入力ボックスを開き、I/Oメモリの変更指示(文字列)を受け取る(実際の変更は、タイマ処理でおこなう)


Private Sub cmdInput_Click()
Cmd = InputBox("「アドレス2桁+空白1文字+データ1桁」の形式で、半角4文字で入力してください。(例 2E 1)", "データ入力", "")
End Sub

'名称:タイマ処理
'機能:I/Oメモリ変更、命令語プログラムを1スキャン実行、I/O表示をおこなう


Private Sub Timer1_Timer()
Dim i As Integer
Dim j As Integer
'指示されたI/Oメモリを変更する
If Cmd <> "" Then '変更指示があるとき
If Right(Cmd, 1) = "1" Then '1のとき
m(Val("&H" + Left(Cmd, 2))) = True 'ONに変更
Else '1以外のとき
m(Val("&H" + Left(Cmd, 2))) = False 'OFFに変更
End If
End If
Cmd = ""
If RunFlg = True Then 'スキャン実行許可フラグがTrueのとき
'命令語プログラムを1スキャン実行する
For i = 0 To LastPrg - 1
Select Case Prg(i)
Case "LD" 'LD命令のとき
i = i + 1
Reg = m(Val("&H" + Prg(i)))
Case "LDNOT" 'LDNOT命令のとき
i = i + 1
Reg = Not (m(Val("&H" + Prg(i))))
Case "AND" 'AND命令のとき
i = i + 1
Reg = Reg And m(Val("&H" + Prg(i)))
Case "ANDNOT" 'ANDNOT命令のとき
i = i + 1
Reg = Reg And (Not (m(Val("&H" + Prg(i)))))
Case "OR" 'OR命令のとき
i = i + 1
Reg = Reg Or m(Val("&H" + Prg(i)))
Case "ORNOT" 'ORNOT命令のとき
i = i + 1
Reg = Reg Or (Not (m(Val("&H" + Prg(i)))))
Case "OUT" 'OUT命令のとき
i = i + 1
m(Val("&H" + Prg(i))) = Reg
Case "END" 'END命令のとき
Exit For
Case Else '上記以外のとき
If Left(Prg(i), 2) <> "/*" Then End 'コメント以外のとき、終了
End Select
Next
End If
'I/Oメモリを画面に表示する
lstMon.Clear
For i = 0 To &HF8 Step 16
lstMon.AddItem Right("0" & Hex(i), 2) & "-" & Right("0" & Hex(i + &HF), 2) & " " _
& OnOff(m(i + 0)) & OnOff(m(i + 1)) & OnOff(m(i + 2)) & OnOff(m(i + 3)) & " " _
& OnOff(m(i + 4)) & OnOff(m(i + 5)) & OnOff(m(i + 6)) & OnOff(m(i + 7)) & " " _
& OnOff(m(i + 8)) & OnOff(m(i + 9)) & OnOff(m(i + 10)) & OnOff(m(i + 11)) & " " _
& OnOff(m(i + 12)) & OnOff(m(i + 13)) & OnOff(m(i + 14)) & OnOff(m(i + 15))
Next
End Sub

'名称:boolean−文字列変換
'機能:booleanを0または1の文字列に変換する


Private Function OnOff(x As Boolean) As String
If x = True Then 'Trueのとき
OnOff = "1" '1を返す
Else 'Falseのとき
OnOff = "0" '0を返す
End If
End Function
---------- リストここまで ----------


2.5 インタプリタを実行してみる
できあがった命令語インタプリタで次のプログラムを実行してみましょう。
このプログラムは、1スキャン毎にデータが反転します。アドレスF0が反転動作しているのが見えたらOKです。

/* 1スキャン反転クロック(F0) */
LDNOT
F0
OUT
F0
END

次回はこの命令語インタプリタを使って、命令語プログラムを実行してみることにしましょう。

目次次へ